mij-discord 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE +21 -0
  6. data/README.md +35 -0
  7. data/Rakefile +10 -0
  8. data/bin/console +7 -0
  9. data/bin/setup +6 -0
  10. data/lib/mij-discord.rb +56 -0
  11. data/lib/mij-discord/bot.rb +579 -0
  12. data/lib/mij-discord/cache.rb +298 -0
  13. data/lib/mij-discord/core/api.rb +228 -0
  14. data/lib/mij-discord/core/api/channel.rb +416 -0
  15. data/lib/mij-discord/core/api/invite.rb +43 -0
  16. data/lib/mij-discord/core/api/server.rb +465 -0
  17. data/lib/mij-discord/core/api/user.rb +144 -0
  18. data/lib/mij-discord/core/errors.rb +106 -0
  19. data/lib/mij-discord/core/gateway.rb +505 -0
  20. data/lib/mij-discord/data.rb +65 -0
  21. data/lib/mij-discord/data/application.rb +38 -0
  22. data/lib/mij-discord/data/channel.rb +404 -0
  23. data/lib/mij-discord/data/embed.rb +115 -0
  24. data/lib/mij-discord/data/emoji.rb +62 -0
  25. data/lib/mij-discord/data/invite.rb +87 -0
  26. data/lib/mij-discord/data/member.rb +174 -0
  27. data/lib/mij-discord/data/message.rb +206 -0
  28. data/lib/mij-discord/data/permissions.rb +121 -0
  29. data/lib/mij-discord/data/role.rb +99 -0
  30. data/lib/mij-discord/data/server.rb +359 -0
  31. data/lib/mij-discord/data/user.rb +173 -0
  32. data/lib/mij-discord/data/voice.rb +68 -0
  33. data/lib/mij-discord/events.rb +133 -0
  34. data/lib/mij-discord/events/basic.rb +80 -0
  35. data/lib/mij-discord/events/channel.rb +50 -0
  36. data/lib/mij-discord/events/member.rb +66 -0
  37. data/lib/mij-discord/events/message.rb +150 -0
  38. data/lib/mij-discord/events/server.rb +102 -0
  39. data/lib/mij-discord/logger.rb +20 -0
  40. data/lib/mij-discord/version.rb +5 -0
  41. data/mij-discord.gemspec +31 -0
  42. metadata +154 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f1bb367b3fa105f6f685bf536ffc5ed364c2bf1a
4
+ data.tar.gz: 69d324ed5ec538deec69e778e725053240bc6faf
5
+ SHA512:
6
+ metadata.gz: cfae532e6dbad1759046637cc4049a8b2d000ac40c5d4ffc376455c5c000536d78f6d4168d288f8e80eaadfef01dbaacec391debbdd7cbeb4988f4751c3f7c75
7
+ data.tar.gz: 9bc6a5388a4f3d5961c1682b011043e3940fc5cc6d4a9a12d6e9f37c51cda80ea2589de5b67754bf642904c77bbf505099d1a7b9b34941c2c0dd23e2c417ae7b
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /.idea/
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ before_install: gem install bundler -v 1.15.3
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in MijDiscord.gemspec
6
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017-2018 Mijyuoon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,35 @@
1
+ # MijDiscord
2
+
3
+ My Discord bot library that's partially based on Discordrb. Was made because I found Discordrb to be not satisfactory for my purposes.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'mij-discord'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```
22
+ $ gem install mij-discord
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ TODO: Write usage instructions here
28
+
29
+ ## Contributing
30
+
31
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Mijyuoon/MijDiscord.
32
+
33
+ ## License
34
+
35
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'mij-discord'
5
+
6
+ require 'irb'
7
+ IRB.start(__FILE__)
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require 'logger'
5
+ require 'base64'
6
+ require 'thread'
7
+ require 'json'
8
+ require 'time'
9
+ require 'socket'
10
+ require 'openssl'
11
+ require 'zlib'
12
+ require 'websocket-client-simple'
13
+ require 'rest-client'
14
+
15
+ require 'mij-discord/version'
16
+ require 'mij-discord/logger'
17
+ require 'mij-discord/cache'
18
+ require 'mij-discord/events'
19
+
20
+ require 'mij-discord/core/gateway'
21
+ require 'mij-discord/core/errors'
22
+ require 'mij-discord/core/api'
23
+ require 'mij-discord/core/api/channel'
24
+ require 'mij-discord/core/api/invite'
25
+ require 'mij-discord/core/api/server'
26
+ require 'mij-discord/core/api/user'
27
+
28
+ require 'mij-discord/data'
29
+ require 'mij-discord/data/invite'
30
+ require 'mij-discord/data/voice'
31
+ require 'mij-discord/data/permissions'
32
+ require 'mij-discord/data/application'
33
+ require 'mij-discord/data/user'
34
+ require 'mij-discord/data/member'
35
+ require 'mij-discord/data/role'
36
+ require 'mij-discord/data/channel'
37
+ require 'mij-discord/data/server'
38
+ require 'mij-discord/data/embed'
39
+ require 'mij-discord/data/emoji'
40
+ require 'mij-discord/data/message'
41
+
42
+ require 'mij-discord/events/basic'
43
+ require 'mij-discord/events/server'
44
+ require 'mij-discord/events/member'
45
+ require 'mij-discord/events/channel'
46
+ require 'mij-discord/events/message'
47
+
48
+ require 'mij-discord/bot'
49
+
50
+ class Integer
51
+ alias_method :to_id, :itself
52
+ end
53
+
54
+ class String
55
+ alias_method :to_id, :to_i
56
+ end
@@ -0,0 +1,579 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MijDiscord
4
+ class Bot
5
+ EVENTS = {
6
+ ready: MijDiscord::Events::Ready,
7
+ heartbeat: MijDiscord::Events::Heartbeat,
8
+ connect: MijDiscord::Events::Connect,
9
+ disconnect: MijDiscord::Events::Disconnect,
10
+ exception: MijDiscord::Events::Exception,
11
+
12
+ update_user: MijDiscord::Events::UpdateUser,
13
+ create_server: MijDiscord::Events::CreateServer,
14
+ update_server: MijDiscord::Events::UpdateServer,
15
+ delete_server: MijDiscord::Events::DeleteServer,
16
+ update_emoji: MijDiscord::Events::UpdateEmoji,
17
+ ban_user: MijDiscord::Events::BanUser,
18
+ unban_user: MijDiscord::Events::UnbanUser,
19
+
20
+ create_role: MijDiscord::Events::CreateRole,
21
+ update_role: MijDiscord::Events::UpdateRole,
22
+ delete_role: MijDiscord::Events::DeleteRole,
23
+ create_member: MijDiscord::Events::CreateMember,
24
+ update_member: MijDiscord::Events::UpdateMember,
25
+ delete_member: MijDiscord::Events::DeleteMember,
26
+
27
+ create_channel: MijDiscord::Events::CreateChannel,
28
+ update_channel: MijDiscord::Events::UpdateChannel,
29
+ delete_channel: MijDiscord::Events::DeleteChannel,
30
+ add_recipient: MijDiscord::Events::AddRecipient,
31
+ remove_recipient: MijDiscord::Events::RemoveRecipient,
32
+
33
+ create_message: MijDiscord::Events::CreateMessage,
34
+ channel_message: MijDiscord::Events::ChannelMessage,
35
+ private_message: MijDiscord::Events::PrivateMessage,
36
+ edit_message: MijDiscord::Events::EditMessage,
37
+ delete_message: MijDiscord::Events::DeleteMessage,
38
+ add_reaction: MijDiscord::Events::AddReaction,
39
+ remove_reaction: MijDiscord::Events::RemoveReaction,
40
+ toggle_reaction: MijDiscord::Events::ToggleReaction,
41
+ clear_reactions: MijDiscord::Events::ClearReactions,
42
+ start_typing: MijDiscord::Events::StartTyping,
43
+
44
+ update_presence: MijDiscord::Events::UpdatePresence,
45
+ update_playing: MijDiscord::Events::UpdatePlaying,
46
+ update_voice_state: MijDiscord::Events::UpdateVoiceState,
47
+ }.freeze
48
+
49
+ UNAVAILABLE_SERVER_TIMEOUT = 10
50
+
51
+ USER_STATUS = {
52
+ online: :online,
53
+ idle: :idle,
54
+ away: :idle,
55
+ dnd: :dnd,
56
+ busy: :dnd,
57
+ invisible: :invisible,
58
+ hidden: :invisible,
59
+ offline: :offline
60
+ }.freeze
61
+
62
+ attr_reader :name
63
+
64
+ attr_reader :type
65
+
66
+ attr_reader :client_id
67
+
68
+ attr_reader :token
69
+
70
+ attr_reader :shard_key
71
+
72
+ attr_reader :profile
73
+
74
+ attr_reader :gateway
75
+
76
+ attr_reader :cache
77
+
78
+ def initialize(client_id:, token:, type: :bot, name: nil,
79
+ shard_id: nil, num_shards: nil, ignore_bots: false, ignore_self: true)
80
+ @client_id, @type, @name = client_id.to_id, type, name || ''
81
+
82
+ @token = case @type
83
+ when :bot then "Bot #{token}"
84
+ when :user then "#{token}"
85
+ else raise ArgumentError, 'Invalid bot type'
86
+ end
87
+
88
+ @cache = MijDiscord::Cache::BotCache.new(self)
89
+
90
+ @shard_key = [shard_id, num_shards] if num_shards
91
+ @gateway = MijDiscord::Core::Gateway.new(self, @token, @shard_key)
92
+
93
+ @ignore_bots, @ignore_self, @ignored_ids = ignore_bots, ignore_self, Set.new
94
+
95
+ @unavailable_servers = 0
96
+
97
+ @event_dispatchers = {}
98
+ end
99
+
100
+ def connect(async = true)
101
+ @gateway.run_async
102
+ @gateway.sync unless async
103
+ nil
104
+ end
105
+
106
+ def sync
107
+ @gateway.sync
108
+ nil
109
+ end
110
+
111
+ def disconnect(no_sync = false)
112
+ @gateway.stop(no_sync)
113
+ nil
114
+ end
115
+
116
+ alias_method :shutdown, :disconnect
117
+
118
+ def connected?
119
+ @gateway.open?
120
+ end
121
+
122
+ def servers
123
+ gateway_check
124
+ @cache.list_servers
125
+ end
126
+
127
+ def server(id)
128
+ gateway_check
129
+ @cache.get_server(id)
130
+ end
131
+
132
+ def channels
133
+ gateway_check
134
+ @cache.list_channels
135
+ end
136
+
137
+ def channel(id, server = nil)
138
+ gateway_check
139
+ @cache.get_channel(id, server)
140
+ end
141
+
142
+ def pm_channel(id)
143
+ gateway_check
144
+ @cache.get_pm_channel(id)
145
+ end
146
+
147
+ alias_method :dm_channel, :pm_channel
148
+
149
+ def users
150
+ gateway_check
151
+ @cache.list_users
152
+ end
153
+
154
+ def user(id)
155
+ gateway_check
156
+ @cache.get_user(id)
157
+ end
158
+
159
+ def members(server_id)
160
+ gateway_check
161
+ server(server_id)&.members
162
+ end
163
+
164
+ def member(server_id, id)
165
+ gateway_check
166
+ server(server_id)&.member(id)
167
+ end
168
+
169
+ def roles(server_id)
170
+ gateway_check
171
+ server(server_id)&.roles
172
+ end
173
+
174
+ def role(server_id, id)
175
+ gateway_check
176
+ server(server_id)&.role(id)
177
+ end
178
+
179
+ def emojis(server_id)
180
+ gateway_check
181
+ server(server_id)&.emojis
182
+ end
183
+
184
+ def emoji(server_id, id)
185
+ gateway_check
186
+ server(server_id)&.emoji(id)
187
+ end
188
+
189
+ def application
190
+ raise RuntimeError, 'Cannot get OAuth application for non-bot user' if @type != :bot
191
+
192
+ response = MijDiscord::Core::API.oauth_application(@token)
193
+ MijDiscord::Data::Application.new(JSON.parse(response), self)
194
+ end
195
+
196
+ def invite(invite)
197
+ code = parse_invite_code(invite)
198
+ response = MijDiscord::Core::API::Invite.resolve(token, code)
199
+ MijDiscord::Data::Invite.new(JSON.parse(response), self)
200
+ end
201
+
202
+ def accept_invite(invite)
203
+ code = parse_invite_code(invite)
204
+ API::Invite.accept(token, code)
205
+ nil
206
+ end
207
+
208
+ def make_invite_url(server: nil, permissions: nil)
209
+ url = "https://discordapp.com/oauth2/authorize?scope=bot&client_id=#{@client_id}".dup
210
+ url << "&permissions=#{permissions.to_i}" if permissions.respond_to?(:to_i)
211
+ url << "&guild_id=#{server.to_id}" if server.respond_to?(:to_id)
212
+ url
213
+ end
214
+
215
+ def create_server(name, region = 'eu-central')
216
+ response = API::Server.create(@token, name, region)
217
+ id = JSON.parse(response)['id'].to_i
218
+
219
+ loop do
220
+ server = @cache.get_server(id, local: true)
221
+ return server if server
222
+
223
+ sleep(0.1)
224
+ end
225
+ end
226
+
227
+ def parse_invite_code(invite)
228
+ case invite
229
+ when %r[(\w+)] then $1
230
+ when %r[(?:https?://)?discord\.gg/(\w+)]i then $1
231
+ when %r[https?://discordapp\.com/invite/(\w+)]i then $1
232
+ when MijDiscord::Data::Invite then invite.code
233
+ else raise ArgumentError, 'Invalid invite format'
234
+ end
235
+ end
236
+
237
+ def parse_mention(mention, server_id = nil)
238
+ gateway_check
239
+
240
+ case mention
241
+ when /<@!?(\d+)>/
242
+ server_id ? member(server_id, $1) : user($1)
243
+ when /<@&(\d+)>/
244
+ role = role(server_id, $1)
245
+ return role if role
246
+
247
+ servers.each do |sv|
248
+ role = sv.role($1)
249
+ return role if role
250
+ end
251
+ when /<:\w+:(\d+)>/
252
+ emoji = emoji(server_id, $1)
253
+ return emoji if emoji
254
+
255
+ servers.each do |sv|
256
+ emoji = sv.emoji($1)
257
+ return emoji if emoji
258
+ end
259
+ end
260
+ end
261
+
262
+ def add_event(type, key = nil, **filter, &block)
263
+ raise ArgumentError, "Invalid event type: #{type}" unless EVENTS[type]
264
+
265
+ event = (@event_dispatchers[type] ||= MijDiscord::Events::EventDispatcher.new(EVENTS[type], self))
266
+ event.add_callback(key, filter, &block)
267
+ end
268
+
269
+ def remove_event(type, key)
270
+ raise ArgumentError, "Invalid event type: #{type}" unless EVENTS[type]
271
+
272
+ @event_dispatchers[type]&.remove_callback(key)
273
+ nil
274
+ end
275
+
276
+ def events(type)
277
+ raise ArgumentError, "Invalid event type: #{type}" unless EVENTS[type]
278
+
279
+ @event_dispatchers[type]&.callbacks || []
280
+ end
281
+
282
+ def ignore_user(user)
283
+ @ignored_ids << user.to_id
284
+ nil
285
+ end
286
+
287
+ def unignore_user(user)
288
+ @ignored_ids.delete(user.to_id)
289
+ nil
290
+ end
291
+
292
+ def ignored_user?(user)
293
+ @ignore_self && user.to_id == @client_id || @ignored_ids.include?(user.to_id)
294
+ end
295
+
296
+ def change_status(status: nil, game: nil, url: nil)
297
+ gateway_check
298
+
299
+ status = status.nil? ? @profile.status : USER_STATUS[status]
300
+ raise ArgumentError, "Status '#{status}' is not valid" unless status
301
+
302
+ game_obj = case game
303
+ when false
304
+ nil
305
+ when nil
306
+ {'name' => @profile.game, 'url' => @profile.stream_url, 'type' => @profile.stream_type}
307
+ else
308
+ {'name' => game, 'url' => url, 'type' => url ? 1 : 0}
309
+ end
310
+
311
+ game_obj&.reject! {|_,v| v.nil? }
312
+ game_obj = nil if game_obj&.empty?
313
+
314
+ @gateway.send_status_update(status, nil, game_obj, false)
315
+ @profile.update_presence('status' => status, 'game' => game_obj)
316
+ nil
317
+ end
318
+
319
+ def handle_heartbeat
320
+ trigger_event(:heartbeat, self)
321
+ end
322
+
323
+ def handle_exception(type, exception, payload = nil)
324
+ return if type == :event && payload&.is_a?(MijDiscord::Events::Exception)
325
+
326
+ trigger_event(:exception, self, type, exception, payload)
327
+ end
328
+
329
+ def handle_dispatch(type, data)
330
+ MijDiscord::LOGGER.debug('Dispatch') { "<#{type} #{data.inspect}>" }
331
+
332
+ if @unavailable_servers > 0 && Time.now > @unavailable_servers_timeout
333
+ MijDiscord::LOGGER.warn('Dispatch') { "Proceeding with #{@unavailable_servers} servers still unavailable" }
334
+
335
+ @unavailable_servers = 0
336
+ notify_ready
337
+ end
338
+
339
+ case type
340
+ when :CONNECT
341
+ trigger_event(:connect, self)
342
+
343
+ when :DISCONNECT
344
+ trigger_event(:disconnect, self)
345
+
346
+ when :READY
347
+ @cache.reset
348
+
349
+ @profile = MijDiscord::Data::Profile.new(data['user'], self)
350
+ @profile.update_presence('status' => :online)
351
+
352
+ @unavailable_servers = 0
353
+ @unavailable_servers_timeout = Time.now + UNAVAILABLE_SERVER_TIMEOUT
354
+
355
+ data['guilds'].each do |sv|
356
+ if sv['unavailable'].eql?(true)
357
+ @unavailable_servers += 1
358
+ else
359
+ @cache.put_server(sv)
360
+ end
361
+ end
362
+
363
+ data['private_channels'].each do |ch|
364
+ @cache.put_channel(ch, nil)
365
+ end
366
+
367
+ notify_ready if @unavailable_servers.zero?
368
+
369
+ when :GUILD_MEMBERS_CHUNK
370
+ server = @cache.get_server(data['guild_id'])
371
+ server.update_members_chunk(data['members'])
372
+
373
+ when :GUILD_CREATE
374
+ server = @cache.put_server(data)
375
+
376
+ if data['unavailable'].eql?(false)
377
+ @unavailable_servers -= 1
378
+ @unavailable_servers_timeout = Time.now + UNAVAILABLE_SERVER_TIMEOUT
379
+
380
+ notify_ready if @unavailable_servers.zero?
381
+ return
382
+ end
383
+
384
+ trigger_event(:create_server, self, server)
385
+
386
+ when :GUILD_UPDATE
387
+ server = @cache.put_server(data, update: true)
388
+ trigger_event(:update_server, self, server)
389
+
390
+ when :GUILD_DELETE
391
+ server = @cache.remove_server(data['id'])
392
+
393
+ if data['unavailable'].eql?(true)
394
+ MijDiscord::LOGGER.warn('Dispatch') { "Server <#{server.id}> died due to outage" }
395
+ return
396
+ end
397
+
398
+ trigger_event(:delete_server, self, server)
399
+
400
+ when :CHANNEL_CREATE
401
+ channel = @cache.put_channel(data, nil)
402
+ trigger_event(:create_channel, self, channel)
403
+
404
+ when :CHANNEL_UPDATE
405
+ channel = @cache.put_channel(data, nil, update: true)
406
+ trigger_event(:update_channel, self, channel)
407
+
408
+ when :CHANNEL_DELETE
409
+ channel = @cache.remove_channel(data['id'])
410
+ trigger_event(:delete_channel, self, channel)
411
+
412
+ when :CHANNEL_RECIPIENT_ADD
413
+ channel = @cache.get_channel(data['channel_id'], nil)
414
+ recipient = channel.update_recipient(add: data['user'])
415
+ trigger_event(:add_recipient, self, channel, recipient)
416
+
417
+ when :CHANNEL_RECIPIENT_REMOVE
418
+ channel = @cache.get_channel(data['channel_id'], nil)
419
+ recipient = channel.update_recipient(remove: data['user'])
420
+ trigger_event(:remove_recipient, self, channel, recipient)
421
+
422
+ when :GUILD_MEMBER_ADD
423
+ server = @cache.get_server(data['guild_id'])
424
+ member = server.cache.put_member(data)
425
+ trigger_event(:create_member, self, member, server)
426
+
427
+ when :GUILD_MEMBER_UPDATE
428
+ server = @cache.get_server(data['guild_id'])
429
+ member = server.cache.put_member(data, update: true)
430
+ trigger_event(:update_member, self, member, server)
431
+
432
+ when :GUILD_MEMBER_REMOVE
433
+ server = @cache.get_server(data['guild_id'])
434
+ member = server.cache.remove_member(data['user']['id'])
435
+ trigger_event(:delete_member, self, member, server)
436
+
437
+ when :GUILD_ROLE_CREATE
438
+ server = @cache.get_server(data['guild_id'])
439
+ role = server.cache.put_role(data['role'])
440
+ trigger_event(:create_role, self, server, role)
441
+
442
+ when :GUILD_ROLE_UPDATE
443
+ server = @cache.get_server(data['guild_id'])
444
+ role = server.cache.put_role(data['role'], update: true)
445
+ trigger_event(:update_role, self, server, role)
446
+
447
+ when :GUILD_ROLE_DELETE
448
+ server = @cache.get_server(data['guild_id'])
449
+ role = server.cache.remove_role(data['role']['id'])
450
+ trigger_event(:delete_role, self, server, role)
451
+
452
+ when :GUILD_EMOJIS_UPDATE
453
+ server = @cache.get_server(data['guild_id'])
454
+ old_emojis = server.emojis
455
+ server.update_emojis(data)
456
+ trigger_event(:update_emoji, self, server, old_emojis, server.emojis)
457
+
458
+ when :GUILD_BAN_ADD
459
+ server = @cache.get_server(data['guild_id'])
460
+ user = @cache.get_user(data['user']['id'])
461
+ trigger_event(:ban_user, self, server, user)
462
+
463
+ when :GUILD_BAN_REMOVE
464
+ server = @cache.get_server(data['guild_id'])
465
+ user = @cache.get_user(data['user']['id'])
466
+ trigger_event(:unban_user, self, server, user)
467
+
468
+ when :MESSAGE_CREATE
469
+ return if ignored_user?(data['author']['id'])
470
+ return if @ignore_bots && data['author']['bot']
471
+
472
+ channel = @cache.get_channel(data['channel_id'], nil)
473
+ message = channel.cache.put_message(data)
474
+ trigger_event(:create_message, self, message)
475
+
476
+ if message.channel.private?
477
+ trigger_event(:private_message, self, message)
478
+ else
479
+ trigger_event(:channel_message, self, message)
480
+ end
481
+
482
+ when :MESSAGE_ACK
483
+ # Do nothing with message acknowledgement
484
+
485
+ when :MESSAGE_UPDATE
486
+ author = data['author']
487
+ return if author.nil?
488
+
489
+ return if ignored_user?(author['id'])
490
+ return if @ignore_bots && author['bot']
491
+
492
+ channel = @cache.get_channel(data['channel_id'], nil)
493
+ message = channel.cache.put_message(data, update: true)
494
+ trigger_event(:edit_message, self, message)
495
+
496
+ when :MESSAGE_DELETE
497
+ channel = @cache.get_channel(data['channel_id'], nil)
498
+ channel.cache.remove_message(data['id'])
499
+
500
+ trigger_event(:delete_message, self, data)
501
+
502
+ when :MESSAGE_DELETE_BULK
503
+ messages = data['ids'].map {|x| {'id' => x, 'channel_id' => data['channel_id']} }
504
+ messages.each {|x| trigger_event(:delete_message, self, x) }
505
+
506
+ when :MESSAGE_REACTION_ADD
507
+ # Should add full use ignore support?
508
+ return if ignored_user?(data['user_id'])
509
+
510
+ trigger_event(:add_reaction, self, data)
511
+ trigger_event(:toggle_reaction, self, data)
512
+
513
+ when :MESSAGE_REACTION_REMOVE
514
+ # Should add full use ignore support?
515
+ return if ignored_user?(data['user_id'])
516
+
517
+ trigger_event(:remove_reaction, self, data)
518
+ trigger_event(:toggle_reaction, self, data)
519
+
520
+ when :MESSAGE_REACTION_REMOVE_ALL
521
+ trigger_event(:clear_reactions, self, data)
522
+
523
+ when :TYPING_START
524
+ begin
525
+ trigger_event(:start_typing, self, data)
526
+ rescue MijDiscord::Core::Errors::NoPermission
527
+ # Ignoring the channel we can't access
528
+ # Why is this even sent? :S
529
+ end
530
+
531
+ when :USER_UPDATE
532
+ user = @cache.put_user(data, update: true)
533
+ trigger_event(:update_user, self, user)
534
+
535
+ when :PRESENCE_UPDATE
536
+ return unless data['guild_id']
537
+
538
+ server = @cache.get_server(data['guild_id'])
539
+ member = server.cache.put_member(data, update: true)
540
+
541
+ old_game = member.game
542
+ member.update_presence(data)
543
+
544
+ if old_game != member.game
545
+ trigger_event(:update_playing, self, data)
546
+ else
547
+ trigger_event(:update_presence, self, data)
548
+ end
549
+
550
+ when :VOICE_STATE_UPDATE
551
+ server = @cache.get_server(data['guild_id'])
552
+ state = server.update_voice_state(data)
553
+ trigger_event(:update_voice_state, self, state)
554
+
555
+ else
556
+ MijDiscord::LOGGER.warn('Dispatch') { "Unhandled gateway event type: #{type}" }
557
+ end
558
+ rescue => exc
559
+ MijDiscord::LOGGER.error('Dispatch') { 'An error occurred in dispatch handler' }
560
+ MijDiscord::LOGGER.error('Dispatch') { exc }
561
+ end
562
+
563
+ private
564
+
565
+ def gateway_check
566
+ raise RuntimeError, 'A gateway connection is required for this action' unless connected?
567
+ end
568
+
569
+ def notify_ready
570
+ @gateway.notify_ready
571
+
572
+ trigger_event(:ready, self)
573
+ end
574
+
575
+ def trigger_event(name, *args)
576
+ @event_dispatchers[name]&.trigger(args)
577
+ end
578
+ end
579
+ end