mij-discord 1.0.10 → 1.0.11
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.
- checksums.yaml +4 -4
- data/lib/mij-discord/bot.rb +687 -677
- data/lib/mij-discord/cache.rb +8 -2
- data/lib/mij-discord/core/api.rb +3 -0
- data/lib/mij-discord/core/api/server.rb +13 -0
- data/lib/mij-discord/core/api/user.rb +12 -0
- data/lib/mij-discord/data.rb +6 -2
- data/lib/mij-discord/data/channel.rb +3 -5
- data/lib/mij-discord/data/message.rb +10 -10
- data/lib/mij-discord/data/server.rb +74 -9
- data/lib/mij-discord/data/user.rb +2 -2
- data/lib/mij-discord/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ae829acad5e6f041e7f83cca2f109e4d2b844f7
|
4
|
+
data.tar.gz: 02c64939be9627ccfb6d6e825e5dbf2f003633c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5a0cbe987ac8756d8948e4693615296c39c149843bfafe415ec0e93ba62474cf164393dfd285f5829254108dd8b7b6ef219b3237a94015d61d787390a554849
|
7
|
+
data.tar.gz: b5e2d9ae1edc18597532f9cd94391aa23928424ad92bd5b66f16abdae2ec060286cff356d26f5f124512a1b1cede86ec6fd685d5acde9bad4ec2b90f68a52075
|
data/lib/mij-discord/bot.rb
CHANGED
@@ -1,678 +1,688 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module MijDiscord
|
4
|
-
class Bot
|
5
|
-
class AuthInfo
|
6
|
-
attr_reader :id
|
7
|
-
|
8
|
-
attr_reader :token
|
9
|
-
|
10
|
-
attr_reader :type
|
11
|
-
|
12
|
-
attr_reader :name
|
13
|
-
|
14
|
-
def initialize(id, token, type, name)
|
15
|
-
@id, @type, @name = id.to_id, type, name
|
16
|
-
|
17
|
-
@token = case type
|
18
|
-
when :bot then "Bot #{token}"
|
19
|
-
when :user then "#{token}"
|
20
|
-
else raise ArgumentError, 'Invalid token type'
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def bot?
|
25
|
-
@type == :bot
|
26
|
-
end
|
27
|
-
|
28
|
-
def user?
|
29
|
-
@type == :user
|
30
|
-
end
|
31
|
-
|
32
|
-
alias_method :to_s, :token
|
33
|
-
|
34
|
-
def inspect
|
35
|
-
MijDiscord.make_inspect(self, :id, :type, :name)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
EVENTS = {
|
40
|
-
ready: MijDiscord::Events::Ready,
|
41
|
-
heartbeat: MijDiscord::Events::Heartbeat,
|
42
|
-
connect: MijDiscord::Events::Connect,
|
43
|
-
disconnect: MijDiscord::Events::Disconnect,
|
44
|
-
exception: MijDiscord::Events::Exception,
|
45
|
-
unhandled: MijDiscord::Events::Unhandled,
|
46
|
-
|
47
|
-
update_user: MijDiscord::Events::UpdateUser,
|
48
|
-
create_server: MijDiscord::Events::CreateServer,
|
49
|
-
update_server: MijDiscord::Events::UpdateServer,
|
50
|
-
delete_server: MijDiscord::Events::DeleteServer,
|
51
|
-
update_emoji: MijDiscord::Events::UpdateEmoji,
|
52
|
-
ban_user: MijDiscord::Events::BanUser,
|
53
|
-
unban_user: MijDiscord::Events::UnbanUser,
|
54
|
-
|
55
|
-
create_role: MijDiscord::Events::CreateRole,
|
56
|
-
update_role: MijDiscord::Events::UpdateRole,
|
57
|
-
delete_role: MijDiscord::Events::DeleteRole,
|
58
|
-
create_member: MijDiscord::Events::CreateMember,
|
59
|
-
update_member: MijDiscord::Events::UpdateMember,
|
60
|
-
delete_member: MijDiscord::Events::DeleteMember,
|
61
|
-
|
62
|
-
create_channel: MijDiscord::Events::CreateChannel,
|
63
|
-
update_channel: MijDiscord::Events::UpdateChannel,
|
64
|
-
delete_channel: MijDiscord::Events::DeleteChannel,
|
65
|
-
update_webhooks: MijDiscord::Events::UpdateWebhooks,
|
66
|
-
update_pins: MijDiscord::Events::UpdatePins,
|
67
|
-
add_recipient: MijDiscord::Events::AddRecipient,
|
68
|
-
remove_recipient: MijDiscord::Events::RemoveRecipient,
|
69
|
-
|
70
|
-
create_message: MijDiscord::Events::CreateMessage,
|
71
|
-
channel_message: MijDiscord::Events::ChannelMessage,
|
72
|
-
private_message: MijDiscord::Events::PrivateMessage,
|
73
|
-
edit_message: MijDiscord::Events::EditMessage,
|
74
|
-
delete_message: MijDiscord::Events::DeleteMessage,
|
75
|
-
add_reaction: MijDiscord::Events::AddReaction,
|
76
|
-
remove_reaction: MijDiscord::Events::RemoveReaction,
|
77
|
-
toggle_reaction: MijDiscord::Events::ToggleReaction,
|
78
|
-
clear_reactions: MijDiscord::Events::ClearReactions,
|
79
|
-
start_typing: MijDiscord::Events::StartTyping,
|
80
|
-
|
81
|
-
update_presence: MijDiscord::Events::UpdatePresence,
|
82
|
-
update_voice_state: MijDiscord::Events::UpdateVoiceState,
|
83
|
-
}.freeze
|
84
|
-
|
85
|
-
UNAVAILABLE_SERVER_TIMEOUT = 10
|
86
|
-
|
87
|
-
attr_reader :auth
|
88
|
-
|
89
|
-
attr_reader :shard_key
|
90
|
-
|
91
|
-
attr_reader :profile
|
92
|
-
|
93
|
-
attr_reader :gateway
|
94
|
-
|
95
|
-
attr_reader :cache
|
96
|
-
|
97
|
-
def initialize(client_id:, token:, type: :bot, name: nil,
|
98
|
-
shard_id: nil, num_shards: nil, ignore_bots: false, ignore_self: true)
|
99
|
-
@auth = AuthInfo.new(client_id, token, type, name)
|
100
|
-
|
101
|
-
@cache = MijDiscord::Cache::BotCache.new(self)
|
102
|
-
|
103
|
-
@shard_key = [shard_id, num_shards] if num_shards
|
104
|
-
@gateway = MijDiscord::Core::Gateway.new(self, @auth, @shard_key)
|
105
|
-
|
106
|
-
@ignore_bots, @ignore_self, @ignored_ids = ignore_bots, ignore_self, Set.new
|
107
|
-
|
108
|
-
@unavailable_servers = 0
|
109
|
-
|
110
|
-
@event_dispatchers = {}
|
111
|
-
end
|
112
|
-
|
113
|
-
def connect(async = true)
|
114
|
-
@gateway.run_async
|
115
|
-
@gateway.sync unless async
|
116
|
-
nil
|
117
|
-
end
|
118
|
-
|
119
|
-
def sync
|
120
|
-
@gateway.sync
|
121
|
-
nil
|
122
|
-
end
|
123
|
-
|
124
|
-
def disconnect(no_sync = false)
|
125
|
-
@gateway.stop(no_sync)
|
126
|
-
nil
|
127
|
-
end
|
128
|
-
|
129
|
-
alias_method :shutdown, :disconnect
|
130
|
-
|
131
|
-
def connected?
|
132
|
-
@gateway.open?
|
133
|
-
end
|
134
|
-
|
135
|
-
def servers
|
136
|
-
gateway_check
|
137
|
-
@cache.list_servers
|
138
|
-
end
|
139
|
-
|
140
|
-
def server(id)
|
141
|
-
gateway_check
|
142
|
-
@cache.get_server(id)
|
143
|
-
end
|
144
|
-
|
145
|
-
def channels
|
146
|
-
gateway_check
|
147
|
-
@cache.list_channels
|
148
|
-
end
|
149
|
-
|
150
|
-
def channel(id, server = nil)
|
151
|
-
gateway_check
|
152
|
-
@cache.get_channel(id, server)
|
153
|
-
end
|
154
|
-
|
155
|
-
def pm_channel(id)
|
156
|
-
gateway_check
|
157
|
-
@cache.get_pm_channel(id)
|
158
|
-
end
|
159
|
-
|
160
|
-
alias_method :dm_channel, :pm_channel
|
161
|
-
|
162
|
-
def users
|
163
|
-
gateway_check
|
164
|
-
@cache.list_users
|
165
|
-
end
|
166
|
-
|
167
|
-
def user(id)
|
168
|
-
gateway_check
|
169
|
-
@cache.get_user(id)
|
170
|
-
end
|
171
|
-
|
172
|
-
def members(server_id)
|
173
|
-
gateway_check
|
174
|
-
server(server_id)&.members
|
175
|
-
end
|
176
|
-
|
177
|
-
def member(server_id, id)
|
178
|
-
gateway_check
|
179
|
-
server(server_id)&.member(id)
|
180
|
-
end
|
181
|
-
|
182
|
-
def roles(server_id)
|
183
|
-
gateway_check
|
184
|
-
server(server_id)&.roles
|
185
|
-
end
|
186
|
-
|
187
|
-
def role(server_id, id)
|
188
|
-
gateway_check
|
189
|
-
server(server_id)&.role(id)
|
190
|
-
end
|
191
|
-
|
192
|
-
def emojis(server_id)
|
193
|
-
gateway_check
|
194
|
-
server(server_id)&.emojis
|
195
|
-
end
|
196
|
-
|
197
|
-
def emoji(server_id, id)
|
198
|
-
gateway_check
|
199
|
-
server(server_id)&.emoji(id)
|
200
|
-
end
|
201
|
-
|
202
|
-
def application
|
203
|
-
raise 'Cannot get OAuth application for non-bot user' unless @auth.bot?
|
204
|
-
|
205
|
-
response = MijDiscord::Core::API.oauth_application(@auth)
|
206
|
-
MijDiscord::Data::Application.new(JSON.parse(response), self)
|
207
|
-
end
|
208
|
-
|
209
|
-
def invite(invite)
|
210
|
-
code = parse_invite_code(invite)
|
211
|
-
response = MijDiscord::Core::API::Invite.resolve(@auth, code, true)
|
212
|
-
MijDiscord::Data::Invite.new(JSON.parse(response), self)
|
213
|
-
end
|
214
|
-
|
215
|
-
def accept_invite(invite)
|
216
|
-
code = parse_invite_code(invite)
|
217
|
-
MijDiscord::Core::API::Invite.accept(@auth, code)
|
218
|
-
nil
|
219
|
-
end
|
220
|
-
|
221
|
-
def make_invite_url(server: nil, permissions: nil)
|
222
|
-
url = "https://discordapp.com/oauth2/authorize?scope=bot&client_id=#{@auth.id}".dup
|
223
|
-
url << "&permissions=#{permissions.to_i}" if permissions.respond_to?(:to_i)
|
224
|
-
url << "&guild_id=#{server.to_id}" if server.respond_to?(:to_id)
|
225
|
-
url
|
226
|
-
end
|
227
|
-
|
228
|
-
def create_server(name, region = 'eu-central')
|
229
|
-
response = API::Server.create(@auth, name, region)
|
230
|
-
id = JSON.parse(response)['id'].to_i
|
231
|
-
|
232
|
-
loop do
|
233
|
-
server = @cache.get_server(id, local: true)
|
234
|
-
return server if server
|
235
|
-
|
236
|
-
sleep(0.1)
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
def parse_invite_code(invite)
|
241
|
-
case invite
|
242
|
-
when %r[^(?:https?://)?discord\.gg/(\w+)$]i then $1
|
243
|
-
when %r[^https?://discordapp\.com/invite/(\w+)$]i then $1
|
244
|
-
when %r[^([a-zA-Z0-9]+)$] then $1
|
245
|
-
when MijDiscord::Data::Invite then invite.code
|
246
|
-
else raise ArgumentError, 'Invalid invite format'
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
def parse_mention_id(mention, type, server_id = nil)
|
251
|
-
case type
|
252
|
-
when :user
|
253
|
-
return server_id ? member(server_id, mention) : user(mention)
|
254
|
-
|
255
|
-
when :channel
|
256
|
-
return channel(mention, server_id)
|
257
|
-
|
258
|
-
when :role
|
259
|
-
role = role(server_id, mention)
|
260
|
-
return role if role
|
261
|
-
|
262
|
-
servers.each do |sv|
|
263
|
-
role = sv.role(mention)
|
264
|
-
return role if role
|
265
|
-
end
|
266
|
-
|
267
|
-
when :emoji
|
268
|
-
emoji = emoji(server_id, mention)
|
269
|
-
return emoji if emoji
|
270
|
-
|
271
|
-
servers.each do |sv|
|
272
|
-
emoji = sv.emoji(mention)
|
273
|
-
return emoji if emoji
|
274
|
-
end
|
275
|
-
|
276
|
-
else raise TypeError, "Invalid mention type '#{type}'"
|
277
|
-
end
|
278
|
-
|
279
|
-
nil
|
280
|
-
end
|
281
|
-
|
282
|
-
def parse_mention(mention, server_id = nil, type: nil)
|
283
|
-
gateway_check
|
284
|
-
|
285
|
-
mention = mention.to_s.strip
|
286
|
-
|
287
|
-
if !type.nil? && mention =~ /^(\d+)$/
|
288
|
-
parse_mention_id($1, type, server_id)
|
289
|
-
|
290
|
-
elsif mention =~ /^<@!?(\d+)>$/
|
291
|
-
return nil if type && type != :user
|
292
|
-
parse_mention_id($1, :user, server_id)
|
293
|
-
|
294
|
-
elsif mention =~ /^<#(\d+)>$/
|
295
|
-
return nil if type && type != :channel
|
296
|
-
parse_mention_id($1, :channel, server_id)
|
297
|
-
|
298
|
-
elsif mention =~ /^<@&(\d+)>$/
|
299
|
-
return nil if type && type != :role
|
300
|
-
parse_mention_id($1, :role, server_id)
|
301
|
-
|
302
|
-
elsif mention =~ /^<(a?):(\w+):(\d+)>$/
|
303
|
-
return nil if type && type != :emoji
|
304
|
-
parse_mention_id($1, :emoji, server_id) || begin
|
305
|
-
em_data = { 'id' => $3.to_i, 'name' => $2, 'animated' => !$1.empty? }
|
306
|
-
MijDiscord::Data::Emoji.new(em_data, nil)
|
307
|
-
end
|
308
|
-
|
309
|
-
end
|
310
|
-
end
|
311
|
-
|
312
|
-
def add_event(type, key = nil, **filter, &block)
|
313
|
-
raise ArgumentError, "Invalid event type: #{type}" unless EVENTS[type]
|
314
|
-
|
315
|
-
event = (@event_dispatchers[type] ||= MijDiscord::Events::EventDispatcher.new(EVENTS[type], self))
|
316
|
-
event.add_callback(key, filter, &block)
|
317
|
-
end
|
318
|
-
|
319
|
-
def remove_event(type, key)
|
320
|
-
raise ArgumentError, "Invalid event type: #{type}" unless EVENTS[type]
|
321
|
-
|
322
|
-
@event_dispatchers[type]&.remove_callback(key)
|
323
|
-
nil
|
324
|
-
end
|
325
|
-
|
326
|
-
def events(type)
|
327
|
-
raise ArgumentError, "Invalid event type: #{type}" unless EVENTS[type]
|
328
|
-
|
329
|
-
@event_dispatchers[type]&.callbacks || []
|
330
|
-
end
|
331
|
-
|
332
|
-
def ignore_user(user)
|
333
|
-
@ignored_ids << user.to_id
|
334
|
-
nil
|
335
|
-
end
|
336
|
-
|
337
|
-
def unignore_user(user)
|
338
|
-
@ignored_ids.delete(user.to_id)
|
339
|
-
nil
|
340
|
-
end
|
341
|
-
|
342
|
-
def ignored_user?(user)
|
343
|
-
user = user.to_id
|
344
|
-
|
345
|
-
return true if @ignore_self && user == @auth.id
|
346
|
-
return true if @ignored_ids.include?(user)
|
347
|
-
|
348
|
-
if @ignore_bots && (user = @cache.get_user(user, local: true))
|
349
|
-
return true if user.bot_account?
|
350
|
-
end
|
351
|
-
|
352
|
-
false
|
353
|
-
end
|
354
|
-
|
355
|
-
def update_presence(status: nil, game: nil)
|
356
|
-
gateway_check
|
357
|
-
|
358
|
-
status = case status
|
359
|
-
when nil then @profile.status
|
360
|
-
when :online, :idle, :dnd, :online then status
|
361
|
-
else raise ArgumentError, 'Invalid status'
|
362
|
-
end
|
363
|
-
|
364
|
-
game = case game
|
365
|
-
when nil then @profile.game
|
366
|
-
when false then nil
|
367
|
-
when String, Hash
|
368
|
-
MijDiscord::Data::Game.construct(game)
|
369
|
-
when MijDiscord::Data::Game then game
|
370
|
-
else raise ArgumentError, 'Invalid game'
|
371
|
-
end&.to_hash
|
372
|
-
|
373
|
-
@gateway.send_status_update(status, nil, game, false)
|
374
|
-
@profile.update_presence('status' => status, 'game' => game)
|
375
|
-
nil
|
376
|
-
end
|
377
|
-
|
378
|
-
def handle_heartbeat
|
379
|
-
trigger_event(:heartbeat, self)
|
380
|
-
end
|
381
|
-
|
382
|
-
def handle_exception(type, exception, payload = nil)
|
383
|
-
return if type == :event && payload&.is_a?(MijDiscord::Events::Exception)
|
384
|
-
|
385
|
-
trigger_event(:exception, self, type, exception, payload)
|
386
|
-
end
|
387
|
-
|
388
|
-
def handle_dispatch(type, data)
|
389
|
-
MijDiscord::LOGGER.debug('Dispatch') { "<#{type} #{data.inspect}>" }
|
390
|
-
|
391
|
-
if @unavailable_servers > 0 && Time.now > @unavailable_servers_timeout
|
392
|
-
MijDiscord::LOGGER.warn('Dispatch') { "Proceeding with #{@unavailable_servers} servers still unavailable" }
|
393
|
-
|
394
|
-
@unavailable_servers = 0
|
395
|
-
notify_ready
|
396
|
-
end
|
397
|
-
|
398
|
-
case type
|
399
|
-
when :CONNECT
|
400
|
-
trigger_event(:connect, self)
|
401
|
-
|
402
|
-
when :DISCONNECT
|
403
|
-
trigger_event(:disconnect, self)
|
404
|
-
|
405
|
-
when :READY
|
406
|
-
@cache.reset
|
407
|
-
|
408
|
-
@profile = MijDiscord::Data::Profile.new(data['user'], self)
|
409
|
-
@profile.update_presence('status' => :online)
|
410
|
-
|
411
|
-
@unavailable_servers = 0
|
412
|
-
@unavailable_servers_timeout = Time.now + UNAVAILABLE_SERVER_TIMEOUT
|
413
|
-
|
414
|
-
data['guilds'].each do |sv|
|
415
|
-
if sv['unavailable'].eql?(true)
|
416
|
-
@unavailable_servers += 1
|
417
|
-
else
|
418
|
-
@cache.put_server(sv)
|
419
|
-
end
|
420
|
-
end
|
421
|
-
|
422
|
-
data['private_channels'].each do |ch|
|
423
|
-
@cache.put_channel(ch, nil)
|
424
|
-
end
|
425
|
-
|
426
|
-
notify_ready if @unavailable_servers.zero?
|
427
|
-
|
428
|
-
when :SESSIONS_REPLACE
|
429
|
-
# Do nothing with session replace because no idea what it does.
|
430
|
-
|
431
|
-
when :PRESENCES_REPLACE
|
432
|
-
# Do nothing with presences replace because no idea what it does.
|
433
|
-
|
434
|
-
when :GUILD_MEMBERS_CHUNK
|
435
|
-
server = @cache.get_server(data['guild_id'])
|
436
|
-
server.update_members_chunk(data['members'])
|
437
|
-
|
438
|
-
when :GUILD_CREATE
|
439
|
-
server = @cache.put_server(data)
|
440
|
-
|
441
|
-
if data['unavailable'].eql?(false)
|
442
|
-
@unavailable_servers -= 1
|
443
|
-
@unavailable_servers_timeout = Time.now + UNAVAILABLE_SERVER_TIMEOUT
|
444
|
-
|
445
|
-
notify_ready if @unavailable_servers.zero?
|
446
|
-
return
|
447
|
-
end
|
448
|
-
|
449
|
-
trigger_event(:create_server, self, server)
|
450
|
-
|
451
|
-
when :GUILD_SYNC
|
452
|
-
server = @cache.get_server(data['id'])
|
453
|
-
server.update_synced_data(data)
|
454
|
-
|
455
|
-
when :GUILD_UPDATE
|
456
|
-
server = @cache.put_server(data, update: true)
|
457
|
-
trigger_event(:update_server, self, server)
|
458
|
-
|
459
|
-
when :GUILD_DELETE
|
460
|
-
server = @cache.remove_server(data['id'])
|
461
|
-
|
462
|
-
if data['unavailable'].eql?(true)
|
463
|
-
MijDiscord::LOGGER.warn('Dispatch') { "Server <#{data['id']}> died due to outage" }
|
464
|
-
return
|
465
|
-
end
|
466
|
-
|
467
|
-
trigger_event(:delete_server, self, server)
|
468
|
-
|
469
|
-
when :CHANNEL_CREATE
|
470
|
-
channel = @cache.put_channel(data, nil)
|
471
|
-
trigger_event(:create_channel, self, channel)
|
472
|
-
|
473
|
-
when :CHANNEL_UPDATE
|
474
|
-
channel = @cache.put_channel(data, nil, update: true)
|
475
|
-
trigger_event(:update_channel, self, channel)
|
476
|
-
|
477
|
-
when :CHANNEL_DELETE
|
478
|
-
channel = @cache.remove_channel(data['id'])
|
479
|
-
trigger_event(:delete_channel, self, channel)
|
480
|
-
|
481
|
-
when :WEBHOOKS_UPDATE
|
482
|
-
channel = @cache.get_channel(data['channel_id'], nil)
|
483
|
-
trigger_event(:update_webhooks, self, channel)
|
484
|
-
|
485
|
-
when :CHANNEL_PINS_UPDATE
|
486
|
-
channel = @cache.get_channel(data['channel_id'], nil)
|
487
|
-
trigger_event(:update_pins, self, channel)
|
488
|
-
|
489
|
-
when :CHANNEL_PINS_ACK
|
490
|
-
# Do nothing with pins acknowledgement
|
491
|
-
|
492
|
-
when :CHANNEL_RECIPIENT_ADD
|
493
|
-
channel = @cache.get_channel(data['channel_id'], nil)
|
494
|
-
recipient = channel.update_recipient(add: data['user'])
|
495
|
-
trigger_event(:add_recipient, self, channel, recipient)
|
496
|
-
|
497
|
-
when :CHANNEL_RECIPIENT_REMOVE
|
498
|
-
channel = @cache.get_channel(data['channel_id'], nil)
|
499
|
-
recipient = channel.update_recipient(remove: data['user'])
|
500
|
-
trigger_event(:remove_recipient, self, channel, recipient)
|
501
|
-
|
502
|
-
when :GUILD_MEMBER_ADD
|
503
|
-
server = @cache.get_server(data['guild_id'])
|
504
|
-
member = server.update_member(data, :add)
|
505
|
-
trigger_event(:create_member, self, member, server)
|
506
|
-
|
507
|
-
when :GUILD_MEMBER_UPDATE
|
508
|
-
server = @cache.get_server(data['guild_id'])
|
509
|
-
member = server.update_member(data, :update)
|
510
|
-
trigger_event(:update_member, self, member, server)
|
511
|
-
|
512
|
-
when :GUILD_MEMBER_REMOVE
|
513
|
-
server = @cache.get_server(data['guild_id'])
|
514
|
-
member = server.update_member(data, :remove)
|
515
|
-
trigger_event(:delete_member, self, member, server)
|
516
|
-
|
517
|
-
when :GUILD_ROLE_CREATE
|
518
|
-
server = @cache.get_server(data['guild_id'])
|
519
|
-
role = server.cache.put_role(data['role'])
|
520
|
-
trigger_event(:create_role, self, server, role)
|
521
|
-
|
522
|
-
when :GUILD_ROLE_UPDATE
|
523
|
-
server = @cache.get_server(data['guild_id'])
|
524
|
-
role = server.cache.put_role(data['role'], update: true)
|
525
|
-
trigger_event(:update_role, self, server, role)
|
526
|
-
|
527
|
-
when :GUILD_ROLE_DELETE
|
528
|
-
server = @cache.get_server(data['guild_id'])
|
529
|
-
role = server.cache.remove_role(data['role_id'])
|
530
|
-
trigger_event(:delete_role, self, server, role)
|
531
|
-
|
532
|
-
when :GUILD_EMOJIS_UPDATE
|
533
|
-
server = @cache.get_server(data['guild_id'])
|
534
|
-
old_emojis = server.emojis
|
535
|
-
server.update_emojis(data)
|
536
|
-
trigger_event(:update_emoji, self, server, old_emojis, server.emojis)
|
537
|
-
|
538
|
-
when :GUILD_BAN_ADD
|
539
|
-
server = @cache.get_server(data['guild_id'])
|
540
|
-
user = @cache.get_user(data['user']['id'], local: @auth.user?)
|
541
|
-
user ||= MijDiscord::Data::User.new(data['user'], self)
|
542
|
-
trigger_event(:ban_user, self, server, user)
|
543
|
-
|
544
|
-
when :GUILD_BAN_REMOVE
|
545
|
-
server = @cache.get_server(data['guild_id'])
|
546
|
-
user = @cache.get_user(data['user']['id'], local: @auth.user?)
|
547
|
-
user ||= MijDiscord::Data::User.new(data['user'], self)
|
548
|
-
trigger_event(:unban_user, self, server, user)
|
549
|
-
|
550
|
-
when :MESSAGE_CREATE
|
551
|
-
channel = @cache.get_channel(data['channel_id'], nil)
|
552
|
-
message = channel.cache.put_message(data)
|
553
|
-
|
554
|
-
return if ignored_user?(data['author']['id'])
|
555
|
-
trigger_event(:create_message, self, message)
|
556
|
-
|
557
|
-
if message.channel.private?
|
558
|
-
trigger_event(:private_message, self, message)
|
559
|
-
else
|
560
|
-
trigger_event(:channel_message, self, message)
|
561
|
-
end
|
562
|
-
|
563
|
-
when :MESSAGE_ACK
|
564
|
-
# Do nothing with message acknowledgement
|
565
|
-
|
566
|
-
when :MESSAGE_UPDATE
|
567
|
-
author = data['author']
|
568
|
-
return if author.nil?
|
569
|
-
|
570
|
-
channel = @cache.get_channel(data['channel_id'], nil)
|
571
|
-
message = channel.cache.put_message(data, update: true)
|
572
|
-
|
573
|
-
return if ignored_user?(author['id'])
|
574
|
-
trigger_event(:edit_message, self, message)
|
575
|
-
|
576
|
-
when :MESSAGE_DELETE
|
577
|
-
channel = @cache.get_channel(data['channel_id'], nil)
|
578
|
-
channel.cache.remove_message(data['id'])
|
579
|
-
|
580
|
-
trigger_event(:delete_message, self, data)
|
581
|
-
|
582
|
-
when :MESSAGE_DELETE_BULK
|
583
|
-
messages = data['ids'].map {|x| {'id' => x, 'channel_id' => data['channel_id']} }
|
584
|
-
messages.each {|x| trigger_event(:delete_message, self, x) }
|
585
|
-
|
586
|
-
when :MESSAGE_REACTION_ADD
|
587
|
-
channel = @cache.get_channel(data['channel_id'], nil)
|
588
|
-
message = channel.cache.get_message(data['message_id'], local: true)
|
589
|
-
message.update_reaction(add: data) if message
|
590
|
-
|
591
|
-
return if ignored_user?(data['user_id'])
|
592
|
-
trigger_event(:add_reaction, self, data)
|
593
|
-
trigger_event(:toggle_reaction, self, data)
|
594
|
-
|
595
|
-
when :MESSAGE_REACTION_REMOVE
|
596
|
-
channel = @cache.get_channel(data['channel_id'], nil)
|
597
|
-
message = channel.cache.get_message(data['message_id'], local: true)
|
598
|
-
message.update_reaction(remove: data) if message
|
599
|
-
|
600
|
-
return if ignored_user?(data['user_id'])
|
601
|
-
trigger_event(:remove_reaction, self, data)
|
602
|
-
trigger_event(:toggle_reaction, self, data)
|
603
|
-
|
604
|
-
when :MESSAGE_REACTION_REMOVE_ALL
|
605
|
-
channel = @cache.get_channel(data['channel_id'], nil)
|
606
|
-
message = channel.cache.get_message(data['message_id'], local: true)
|
607
|
-
message.update_reaction(clear: true) if message
|
608
|
-
|
609
|
-
trigger_event(:clear_reactions, self, data)
|
610
|
-
|
611
|
-
when :TYPING_START
|
612
|
-
begin
|
613
|
-
return if ignored_user?(data['user_id'])
|
614
|
-
trigger_event(:start_typing, self, data)
|
615
|
-
rescue MijDiscord::Errors::Forbidden
|
616
|
-
# Ignoring the channel we can't access
|
617
|
-
# Why is this even sent? :S
|
618
|
-
end
|
619
|
-
|
620
|
-
when :USER_UPDATE
|
621
|
-
user = @cache.put_user(data, update: true)
|
622
|
-
@profile.update_data(data) if @profile == user
|
623
|
-
|
624
|
-
trigger_event(:update_user, self, user)
|
625
|
-
|
626
|
-
when :PRESENCE_UPDATE
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
user&.update_presence(data)
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
end
|
662
|
-
|
663
|
-
def
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MijDiscord
|
4
|
+
class Bot
|
5
|
+
class AuthInfo
|
6
|
+
attr_reader :id
|
7
|
+
|
8
|
+
attr_reader :token
|
9
|
+
|
10
|
+
attr_reader :type
|
11
|
+
|
12
|
+
attr_reader :name
|
13
|
+
|
14
|
+
def initialize(id, token, type, name)
|
15
|
+
@id, @type, @name = id.to_id, type, name
|
16
|
+
|
17
|
+
@token = case type
|
18
|
+
when :bot then "Bot #{token}"
|
19
|
+
when :user then "#{token}"
|
20
|
+
else raise ArgumentError, 'Invalid token type'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def bot?
|
25
|
+
@type == :bot
|
26
|
+
end
|
27
|
+
|
28
|
+
def user?
|
29
|
+
@type == :user
|
30
|
+
end
|
31
|
+
|
32
|
+
alias_method :to_s, :token
|
33
|
+
|
34
|
+
def inspect
|
35
|
+
MijDiscord.make_inspect(self, :id, :type, :name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
EVENTS = {
|
40
|
+
ready: MijDiscord::Events::Ready,
|
41
|
+
heartbeat: MijDiscord::Events::Heartbeat,
|
42
|
+
connect: MijDiscord::Events::Connect,
|
43
|
+
disconnect: MijDiscord::Events::Disconnect,
|
44
|
+
exception: MijDiscord::Events::Exception,
|
45
|
+
unhandled: MijDiscord::Events::Unhandled,
|
46
|
+
|
47
|
+
update_user: MijDiscord::Events::UpdateUser,
|
48
|
+
create_server: MijDiscord::Events::CreateServer,
|
49
|
+
update_server: MijDiscord::Events::UpdateServer,
|
50
|
+
delete_server: MijDiscord::Events::DeleteServer,
|
51
|
+
update_emoji: MijDiscord::Events::UpdateEmoji,
|
52
|
+
ban_user: MijDiscord::Events::BanUser,
|
53
|
+
unban_user: MijDiscord::Events::UnbanUser,
|
54
|
+
|
55
|
+
create_role: MijDiscord::Events::CreateRole,
|
56
|
+
update_role: MijDiscord::Events::UpdateRole,
|
57
|
+
delete_role: MijDiscord::Events::DeleteRole,
|
58
|
+
create_member: MijDiscord::Events::CreateMember,
|
59
|
+
update_member: MijDiscord::Events::UpdateMember,
|
60
|
+
delete_member: MijDiscord::Events::DeleteMember,
|
61
|
+
|
62
|
+
create_channel: MijDiscord::Events::CreateChannel,
|
63
|
+
update_channel: MijDiscord::Events::UpdateChannel,
|
64
|
+
delete_channel: MijDiscord::Events::DeleteChannel,
|
65
|
+
update_webhooks: MijDiscord::Events::UpdateWebhooks,
|
66
|
+
update_pins: MijDiscord::Events::UpdatePins,
|
67
|
+
add_recipient: MijDiscord::Events::AddRecipient,
|
68
|
+
remove_recipient: MijDiscord::Events::RemoveRecipient,
|
69
|
+
|
70
|
+
create_message: MijDiscord::Events::CreateMessage,
|
71
|
+
channel_message: MijDiscord::Events::ChannelMessage,
|
72
|
+
private_message: MijDiscord::Events::PrivateMessage,
|
73
|
+
edit_message: MijDiscord::Events::EditMessage,
|
74
|
+
delete_message: MijDiscord::Events::DeleteMessage,
|
75
|
+
add_reaction: MijDiscord::Events::AddReaction,
|
76
|
+
remove_reaction: MijDiscord::Events::RemoveReaction,
|
77
|
+
toggle_reaction: MijDiscord::Events::ToggleReaction,
|
78
|
+
clear_reactions: MijDiscord::Events::ClearReactions,
|
79
|
+
start_typing: MijDiscord::Events::StartTyping,
|
80
|
+
|
81
|
+
update_presence: MijDiscord::Events::UpdatePresence,
|
82
|
+
update_voice_state: MijDiscord::Events::UpdateVoiceState,
|
83
|
+
}.freeze
|
84
|
+
|
85
|
+
UNAVAILABLE_SERVER_TIMEOUT = 10
|
86
|
+
|
87
|
+
attr_reader :auth
|
88
|
+
|
89
|
+
attr_reader :shard_key
|
90
|
+
|
91
|
+
attr_reader :profile
|
92
|
+
|
93
|
+
attr_reader :gateway
|
94
|
+
|
95
|
+
attr_reader :cache
|
96
|
+
|
97
|
+
def initialize(client_id:, token:, type: :bot, name: nil,
|
98
|
+
shard_id: nil, num_shards: nil, ignore_bots: false, ignore_self: true)
|
99
|
+
@auth = AuthInfo.new(client_id, token, type, name)
|
100
|
+
|
101
|
+
@cache = MijDiscord::Cache::BotCache.new(self)
|
102
|
+
|
103
|
+
@shard_key = [shard_id, num_shards] if num_shards
|
104
|
+
@gateway = MijDiscord::Core::Gateway.new(self, @auth, @shard_key)
|
105
|
+
|
106
|
+
@ignore_bots, @ignore_self, @ignored_ids = ignore_bots, ignore_self, Set.new
|
107
|
+
|
108
|
+
@unavailable_servers = 0
|
109
|
+
|
110
|
+
@event_dispatchers = {}
|
111
|
+
end
|
112
|
+
|
113
|
+
def connect(async = true)
|
114
|
+
@gateway.run_async
|
115
|
+
@gateway.sync unless async
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
|
119
|
+
def sync
|
120
|
+
@gateway.sync
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
|
124
|
+
def disconnect(no_sync = false)
|
125
|
+
@gateway.stop(no_sync)
|
126
|
+
nil
|
127
|
+
end
|
128
|
+
|
129
|
+
alias_method :shutdown, :disconnect
|
130
|
+
|
131
|
+
def connected?
|
132
|
+
@gateway.open?
|
133
|
+
end
|
134
|
+
|
135
|
+
def servers
|
136
|
+
gateway_check
|
137
|
+
@cache.list_servers
|
138
|
+
end
|
139
|
+
|
140
|
+
def server(id)
|
141
|
+
gateway_check
|
142
|
+
@cache.get_server(id)
|
143
|
+
end
|
144
|
+
|
145
|
+
def channels
|
146
|
+
gateway_check
|
147
|
+
@cache.list_channels
|
148
|
+
end
|
149
|
+
|
150
|
+
def channel(id, server = nil)
|
151
|
+
gateway_check
|
152
|
+
@cache.get_channel(id, server)
|
153
|
+
end
|
154
|
+
|
155
|
+
def pm_channel(id)
|
156
|
+
gateway_check
|
157
|
+
@cache.get_pm_channel(id)
|
158
|
+
end
|
159
|
+
|
160
|
+
alias_method :dm_channel, :pm_channel
|
161
|
+
|
162
|
+
def users
|
163
|
+
gateway_check
|
164
|
+
@cache.list_users
|
165
|
+
end
|
166
|
+
|
167
|
+
def user(id)
|
168
|
+
gateway_check
|
169
|
+
@cache.get_user(id)
|
170
|
+
end
|
171
|
+
|
172
|
+
def members(server_id)
|
173
|
+
gateway_check
|
174
|
+
server(server_id)&.members
|
175
|
+
end
|
176
|
+
|
177
|
+
def member(server_id, id)
|
178
|
+
gateway_check
|
179
|
+
server(server_id)&.member(id)
|
180
|
+
end
|
181
|
+
|
182
|
+
def roles(server_id)
|
183
|
+
gateway_check
|
184
|
+
server(server_id)&.roles
|
185
|
+
end
|
186
|
+
|
187
|
+
def role(server_id, id)
|
188
|
+
gateway_check
|
189
|
+
server(server_id)&.role(id)
|
190
|
+
end
|
191
|
+
|
192
|
+
def emojis(server_id)
|
193
|
+
gateway_check
|
194
|
+
server(server_id)&.emojis
|
195
|
+
end
|
196
|
+
|
197
|
+
def emoji(server_id, id)
|
198
|
+
gateway_check
|
199
|
+
server(server_id)&.emoji(id)
|
200
|
+
end
|
201
|
+
|
202
|
+
def application
|
203
|
+
raise 'Cannot get OAuth application for non-bot user' unless @auth.bot?
|
204
|
+
|
205
|
+
response = MijDiscord::Core::API.oauth_application(@auth)
|
206
|
+
MijDiscord::Data::Application.new(JSON.parse(response), self)
|
207
|
+
end
|
208
|
+
|
209
|
+
def invite(invite)
|
210
|
+
code = parse_invite_code(invite)
|
211
|
+
response = MijDiscord::Core::API::Invite.resolve(@auth, code, true)
|
212
|
+
MijDiscord::Data::Invite.new(JSON.parse(response), self)
|
213
|
+
end
|
214
|
+
|
215
|
+
def accept_invite(invite)
|
216
|
+
code = parse_invite_code(invite)
|
217
|
+
MijDiscord::Core::API::Invite.accept(@auth, code)
|
218
|
+
nil
|
219
|
+
end
|
220
|
+
|
221
|
+
def make_invite_url(server: nil, permissions: nil)
|
222
|
+
url = "https://discordapp.com/oauth2/authorize?scope=bot&client_id=#{@auth.id}".dup
|
223
|
+
url << "&permissions=#{permissions.to_i}" if permissions.respond_to?(:to_i)
|
224
|
+
url << "&guild_id=#{server.to_id}" if server.respond_to?(:to_id)
|
225
|
+
url
|
226
|
+
end
|
227
|
+
|
228
|
+
def create_server(name, region = 'eu-central')
|
229
|
+
response = API::Server.create(@auth, name, region)
|
230
|
+
id = JSON.parse(response)['id'].to_i
|
231
|
+
|
232
|
+
loop do
|
233
|
+
server = @cache.get_server(id, local: true)
|
234
|
+
return server if server
|
235
|
+
|
236
|
+
sleep(0.1)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def parse_invite_code(invite)
|
241
|
+
case invite
|
242
|
+
when %r[^(?:https?://)?discord\.gg/(\w+)$]i then $1
|
243
|
+
when %r[^https?://discordapp\.com/invite/(\w+)$]i then $1
|
244
|
+
when %r[^([a-zA-Z0-9]+)$] then $1
|
245
|
+
when MijDiscord::Data::Invite then invite.code
|
246
|
+
else raise ArgumentError, 'Invalid invite format'
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def parse_mention_id(mention, type, server_id = nil)
|
251
|
+
case type
|
252
|
+
when :user
|
253
|
+
return server_id ? member(server_id, mention) : user(mention)
|
254
|
+
|
255
|
+
when :channel
|
256
|
+
return channel(mention, server_id)
|
257
|
+
|
258
|
+
when :role
|
259
|
+
role = role(server_id, mention)
|
260
|
+
return role if role
|
261
|
+
|
262
|
+
servers.each do |sv|
|
263
|
+
role = sv.role(mention)
|
264
|
+
return role if role
|
265
|
+
end
|
266
|
+
|
267
|
+
when :emoji
|
268
|
+
emoji = emoji(server_id, mention)
|
269
|
+
return emoji if emoji
|
270
|
+
|
271
|
+
servers.each do |sv|
|
272
|
+
emoji = sv.emoji(mention)
|
273
|
+
return emoji if emoji
|
274
|
+
end
|
275
|
+
|
276
|
+
else raise TypeError, "Invalid mention type '#{type}'"
|
277
|
+
end
|
278
|
+
|
279
|
+
nil
|
280
|
+
end
|
281
|
+
|
282
|
+
def parse_mention(mention, server_id = nil, type: nil)
|
283
|
+
gateway_check
|
284
|
+
|
285
|
+
mention = mention.to_s.strip
|
286
|
+
|
287
|
+
if !type.nil? && mention =~ /^(\d+)$/
|
288
|
+
parse_mention_id($1, type, server_id)
|
289
|
+
|
290
|
+
elsif mention =~ /^<@!?(\d+)>$/
|
291
|
+
return nil if type && type != :user
|
292
|
+
parse_mention_id($1, :user, server_id)
|
293
|
+
|
294
|
+
elsif mention =~ /^<#(\d+)>$/
|
295
|
+
return nil if type && type != :channel
|
296
|
+
parse_mention_id($1, :channel, server_id)
|
297
|
+
|
298
|
+
elsif mention =~ /^<@&(\d+)>$/
|
299
|
+
return nil if type && type != :role
|
300
|
+
parse_mention_id($1, :role, server_id)
|
301
|
+
|
302
|
+
elsif mention =~ /^<(a?):(\w+):(\d+)>$/
|
303
|
+
return nil if type && type != :emoji
|
304
|
+
parse_mention_id($1, :emoji, server_id) || begin
|
305
|
+
em_data = { 'id' => $3.to_i, 'name' => $2, 'animated' => !$1.empty? }
|
306
|
+
MijDiscord::Data::Emoji.new(em_data, nil)
|
307
|
+
end
|
308
|
+
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def add_event(type, key = nil, **filter, &block)
|
313
|
+
raise ArgumentError, "Invalid event type: #{type}" unless EVENTS[type]
|
314
|
+
|
315
|
+
event = (@event_dispatchers[type] ||= MijDiscord::Events::EventDispatcher.new(EVENTS[type], self))
|
316
|
+
event.add_callback(key, filter, &block)
|
317
|
+
end
|
318
|
+
|
319
|
+
def remove_event(type, key)
|
320
|
+
raise ArgumentError, "Invalid event type: #{type}" unless EVENTS[type]
|
321
|
+
|
322
|
+
@event_dispatchers[type]&.remove_callback(key)
|
323
|
+
nil
|
324
|
+
end
|
325
|
+
|
326
|
+
def events(type)
|
327
|
+
raise ArgumentError, "Invalid event type: #{type}" unless EVENTS[type]
|
328
|
+
|
329
|
+
@event_dispatchers[type]&.callbacks || []
|
330
|
+
end
|
331
|
+
|
332
|
+
def ignore_user(user)
|
333
|
+
@ignored_ids << user.to_id
|
334
|
+
nil
|
335
|
+
end
|
336
|
+
|
337
|
+
def unignore_user(user)
|
338
|
+
@ignored_ids.delete(user.to_id)
|
339
|
+
nil
|
340
|
+
end
|
341
|
+
|
342
|
+
def ignored_user?(user)
|
343
|
+
user = user.to_id
|
344
|
+
|
345
|
+
return true if @ignore_self && user == @auth.id
|
346
|
+
return true if @ignored_ids.include?(user)
|
347
|
+
|
348
|
+
if @ignore_bots && (user = @cache.get_user(user, local: true))
|
349
|
+
return true if user.bot_account?
|
350
|
+
end
|
351
|
+
|
352
|
+
false
|
353
|
+
end
|
354
|
+
|
355
|
+
def update_presence(status: nil, game: nil)
|
356
|
+
gateway_check
|
357
|
+
|
358
|
+
status = case status
|
359
|
+
when nil then @profile.status
|
360
|
+
when :online, :idle, :dnd, :online then status
|
361
|
+
else raise ArgumentError, 'Invalid status'
|
362
|
+
end
|
363
|
+
|
364
|
+
game = case game
|
365
|
+
when nil then @profile.game
|
366
|
+
when false then nil
|
367
|
+
when String, Hash
|
368
|
+
MijDiscord::Data::Game.construct(game)
|
369
|
+
when MijDiscord::Data::Game then game
|
370
|
+
else raise ArgumentError, 'Invalid game'
|
371
|
+
end&.to_hash
|
372
|
+
|
373
|
+
@gateway.send_status_update(status, nil, game, false)
|
374
|
+
@profile.update_presence('status' => status, 'game' => game)
|
375
|
+
nil
|
376
|
+
end
|
377
|
+
|
378
|
+
def handle_heartbeat
|
379
|
+
trigger_event(:heartbeat, self)
|
380
|
+
end
|
381
|
+
|
382
|
+
def handle_exception(type, exception, payload = nil)
|
383
|
+
return if type == :event && payload&.is_a?(MijDiscord::Events::Exception)
|
384
|
+
|
385
|
+
trigger_event(:exception, self, type, exception, payload)
|
386
|
+
end
|
387
|
+
|
388
|
+
def handle_dispatch(type, data)
|
389
|
+
MijDiscord::LOGGER.debug('Dispatch') { "<#{type} #{data.inspect}>" }
|
390
|
+
|
391
|
+
if @unavailable_servers > 0 && Time.now > @unavailable_servers_timeout
|
392
|
+
MijDiscord::LOGGER.warn('Dispatch') { "Proceeding with #{@unavailable_servers} servers still unavailable" }
|
393
|
+
|
394
|
+
@unavailable_servers = 0
|
395
|
+
notify_ready
|
396
|
+
end
|
397
|
+
|
398
|
+
case type
|
399
|
+
when :CONNECT
|
400
|
+
trigger_event(:connect, self)
|
401
|
+
|
402
|
+
when :DISCONNECT
|
403
|
+
trigger_event(:disconnect, self)
|
404
|
+
|
405
|
+
when :READY
|
406
|
+
@cache.reset
|
407
|
+
|
408
|
+
@profile = MijDiscord::Data::Profile.new(data['user'], self)
|
409
|
+
@profile.update_presence('status' => :online)
|
410
|
+
|
411
|
+
@unavailable_servers = 0
|
412
|
+
@unavailable_servers_timeout = Time.now + UNAVAILABLE_SERVER_TIMEOUT
|
413
|
+
|
414
|
+
data['guilds'].each do |sv|
|
415
|
+
if sv['unavailable'].eql?(true)
|
416
|
+
@unavailable_servers += 1
|
417
|
+
else
|
418
|
+
@cache.put_server(sv)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
data['private_channels'].each do |ch|
|
423
|
+
@cache.put_channel(ch, nil)
|
424
|
+
end
|
425
|
+
|
426
|
+
notify_ready if @unavailable_servers.zero?
|
427
|
+
|
428
|
+
when :SESSIONS_REPLACE
|
429
|
+
# Do nothing with session replace because no idea what it does.
|
430
|
+
|
431
|
+
when :PRESENCES_REPLACE
|
432
|
+
# Do nothing with presences replace because no idea what it does.
|
433
|
+
|
434
|
+
when :GUILD_MEMBERS_CHUNK
|
435
|
+
server = @cache.get_server(data['guild_id'])
|
436
|
+
server.update_members_chunk(data['members'])
|
437
|
+
|
438
|
+
when :GUILD_CREATE
|
439
|
+
server = @cache.put_server(data)
|
440
|
+
|
441
|
+
if data['unavailable'].eql?(false)
|
442
|
+
@unavailable_servers -= 1
|
443
|
+
@unavailable_servers_timeout = Time.now + UNAVAILABLE_SERVER_TIMEOUT
|
444
|
+
|
445
|
+
notify_ready if @unavailable_servers.zero?
|
446
|
+
return
|
447
|
+
end
|
448
|
+
|
449
|
+
trigger_event(:create_server, self, server)
|
450
|
+
|
451
|
+
when :GUILD_SYNC
|
452
|
+
server = @cache.get_server(data['id'])
|
453
|
+
server.update_synced_data(data)
|
454
|
+
|
455
|
+
when :GUILD_UPDATE
|
456
|
+
server = @cache.put_server(data, update: true)
|
457
|
+
trigger_event(:update_server, self, server)
|
458
|
+
|
459
|
+
when :GUILD_DELETE
|
460
|
+
server = @cache.remove_server(data['id'])
|
461
|
+
|
462
|
+
if data['unavailable'].eql?(true)
|
463
|
+
MijDiscord::LOGGER.warn('Dispatch') { "Server <#{data['id']}> died due to outage" }
|
464
|
+
return
|
465
|
+
end
|
466
|
+
|
467
|
+
trigger_event(:delete_server, self, server)
|
468
|
+
|
469
|
+
when :CHANNEL_CREATE
|
470
|
+
channel = @cache.put_channel(data, nil)
|
471
|
+
trigger_event(:create_channel, self, channel)
|
472
|
+
|
473
|
+
when :CHANNEL_UPDATE
|
474
|
+
channel = @cache.put_channel(data, nil, update: true)
|
475
|
+
trigger_event(:update_channel, self, channel)
|
476
|
+
|
477
|
+
when :CHANNEL_DELETE
|
478
|
+
channel = @cache.remove_channel(data['id'])
|
479
|
+
trigger_event(:delete_channel, self, channel)
|
480
|
+
|
481
|
+
when :WEBHOOKS_UPDATE
|
482
|
+
channel = @cache.get_channel(data['channel_id'], nil)
|
483
|
+
trigger_event(:update_webhooks, self, channel)
|
484
|
+
|
485
|
+
when :CHANNEL_PINS_UPDATE
|
486
|
+
channel = @cache.get_channel(data['channel_id'], nil)
|
487
|
+
trigger_event(:update_pins, self, channel)
|
488
|
+
|
489
|
+
when :CHANNEL_PINS_ACK
|
490
|
+
# Do nothing with pins acknowledgement
|
491
|
+
|
492
|
+
when :CHANNEL_RECIPIENT_ADD
|
493
|
+
channel = @cache.get_channel(data['channel_id'], nil)
|
494
|
+
recipient = channel.update_recipient(add: data['user'])
|
495
|
+
trigger_event(:add_recipient, self, channel, recipient)
|
496
|
+
|
497
|
+
when :CHANNEL_RECIPIENT_REMOVE
|
498
|
+
channel = @cache.get_channel(data['channel_id'], nil)
|
499
|
+
recipient = channel.update_recipient(remove: data['user'])
|
500
|
+
trigger_event(:remove_recipient, self, channel, recipient)
|
501
|
+
|
502
|
+
when :GUILD_MEMBER_ADD
|
503
|
+
server = @cache.get_server(data['guild_id'])
|
504
|
+
member = server.update_member(data, :add)
|
505
|
+
trigger_event(:create_member, self, member, server)
|
506
|
+
|
507
|
+
when :GUILD_MEMBER_UPDATE
|
508
|
+
server = @cache.get_server(data['guild_id'])
|
509
|
+
member = server.update_member(data, :update)
|
510
|
+
trigger_event(:update_member, self, member, server)
|
511
|
+
|
512
|
+
when :GUILD_MEMBER_REMOVE
|
513
|
+
server = @cache.get_server(data['guild_id'])
|
514
|
+
member = server.update_member(data, :remove)
|
515
|
+
trigger_event(:delete_member, self, member, server)
|
516
|
+
|
517
|
+
when :GUILD_ROLE_CREATE
|
518
|
+
server = @cache.get_server(data['guild_id'])
|
519
|
+
role = server.cache.put_role(data['role'])
|
520
|
+
trigger_event(:create_role, self, server, role)
|
521
|
+
|
522
|
+
when :GUILD_ROLE_UPDATE
|
523
|
+
server = @cache.get_server(data['guild_id'])
|
524
|
+
role = server.cache.put_role(data['role'], update: true)
|
525
|
+
trigger_event(:update_role, self, server, role)
|
526
|
+
|
527
|
+
when :GUILD_ROLE_DELETE
|
528
|
+
server = @cache.get_server(data['guild_id'])
|
529
|
+
role = server.cache.remove_role(data['role_id'])
|
530
|
+
trigger_event(:delete_role, self, server, role)
|
531
|
+
|
532
|
+
when :GUILD_EMOJIS_UPDATE
|
533
|
+
server = @cache.get_server(data['guild_id'])
|
534
|
+
old_emojis = server.emojis
|
535
|
+
server.update_emojis(data)
|
536
|
+
trigger_event(:update_emoji, self, server, old_emojis, server.emojis)
|
537
|
+
|
538
|
+
when :GUILD_BAN_ADD
|
539
|
+
server = @cache.get_server(data['guild_id'])
|
540
|
+
user = @cache.get_user(data['user']['id'], local: @auth.user?)
|
541
|
+
user ||= MijDiscord::Data::User.new(data['user'], self)
|
542
|
+
trigger_event(:ban_user, self, server, user)
|
543
|
+
|
544
|
+
when :GUILD_BAN_REMOVE
|
545
|
+
server = @cache.get_server(data['guild_id'])
|
546
|
+
user = @cache.get_user(data['user']['id'], local: @auth.user?)
|
547
|
+
user ||= MijDiscord::Data::User.new(data['user'], self)
|
548
|
+
trigger_event(:unban_user, self, server, user)
|
549
|
+
|
550
|
+
when :MESSAGE_CREATE
|
551
|
+
channel = @cache.get_channel(data['channel_id'], nil)
|
552
|
+
message = channel.cache.put_message(data)
|
553
|
+
|
554
|
+
return if ignored_user?(data['author']['id'])
|
555
|
+
trigger_event(:create_message, self, message)
|
556
|
+
|
557
|
+
if message.channel.private?
|
558
|
+
trigger_event(:private_message, self, message)
|
559
|
+
else
|
560
|
+
trigger_event(:channel_message, self, message)
|
561
|
+
end
|
562
|
+
|
563
|
+
when :MESSAGE_ACK
|
564
|
+
# Do nothing with message acknowledgement
|
565
|
+
|
566
|
+
when :MESSAGE_UPDATE
|
567
|
+
author = data['author']
|
568
|
+
return if author.nil?
|
569
|
+
|
570
|
+
channel = @cache.get_channel(data['channel_id'], nil)
|
571
|
+
message = channel.cache.put_message(data, update: true)
|
572
|
+
|
573
|
+
return if ignored_user?(author['id'])
|
574
|
+
trigger_event(:edit_message, self, message)
|
575
|
+
|
576
|
+
when :MESSAGE_DELETE
|
577
|
+
channel = @cache.get_channel(data['channel_id'], nil)
|
578
|
+
channel.cache.remove_message(data['id'])
|
579
|
+
|
580
|
+
trigger_event(:delete_message, self, data)
|
581
|
+
|
582
|
+
when :MESSAGE_DELETE_BULK
|
583
|
+
messages = data['ids'].map {|x| {'id' => x, 'channel_id' => data['channel_id']} }
|
584
|
+
messages.each {|x| trigger_event(:delete_message, self, x) }
|
585
|
+
|
586
|
+
when :MESSAGE_REACTION_ADD
|
587
|
+
channel = @cache.get_channel(data['channel_id'], nil)
|
588
|
+
message = channel.cache.get_message(data['message_id'], local: true)
|
589
|
+
message.update_reaction(add: data) if message
|
590
|
+
|
591
|
+
return if ignored_user?(data['user_id'])
|
592
|
+
trigger_event(:add_reaction, self, data)
|
593
|
+
trigger_event(:toggle_reaction, self, data)
|
594
|
+
|
595
|
+
when :MESSAGE_REACTION_REMOVE
|
596
|
+
channel = @cache.get_channel(data['channel_id'], nil)
|
597
|
+
message = channel.cache.get_message(data['message_id'], local: true)
|
598
|
+
message.update_reaction(remove: data) if message
|
599
|
+
|
600
|
+
return if ignored_user?(data['user_id'])
|
601
|
+
trigger_event(:remove_reaction, self, data)
|
602
|
+
trigger_event(:toggle_reaction, self, data)
|
603
|
+
|
604
|
+
when :MESSAGE_REACTION_REMOVE_ALL
|
605
|
+
channel = @cache.get_channel(data['channel_id'], nil)
|
606
|
+
message = channel.cache.get_message(data['message_id'], local: true)
|
607
|
+
message.update_reaction(clear: true) if message
|
608
|
+
|
609
|
+
trigger_event(:clear_reactions, self, data)
|
610
|
+
|
611
|
+
when :TYPING_START
|
612
|
+
begin
|
613
|
+
return if ignored_user?(data['user_id'])
|
614
|
+
trigger_event(:start_typing, self, data)
|
615
|
+
rescue MijDiscord::Errors::Forbidden
|
616
|
+
# Ignoring the channel we can't access
|
617
|
+
# Why is this even sent? :S
|
618
|
+
end
|
619
|
+
|
620
|
+
when :USER_UPDATE
|
621
|
+
user = @cache.put_user(data, update: true)
|
622
|
+
@profile.update_data(data) if @profile == user
|
623
|
+
|
624
|
+
trigger_event(:update_user, self, user)
|
625
|
+
|
626
|
+
when :PRESENCE_UPDATE
|
627
|
+
# Bullshit logic due to Discord gateway sending this in a stupid way
|
628
|
+
# TODO: Rewrite relevant parts for better handling?
|
629
|
+
if data['guild_id']
|
630
|
+
server = @cache.get_server(data['guild_id'])
|
631
|
+
user = server.cache.get_member(data['user']['id'])
|
632
|
+
|
633
|
+
user&.update_presence(data)
|
634
|
+
user&.update_data(data)
|
635
|
+
else
|
636
|
+
user = @cache.get_user(data['user']['id'])
|
637
|
+
|
638
|
+
user&.update_presence(data)
|
639
|
+
user&.update_data(data['user'])
|
640
|
+
|
641
|
+
if @profile == user
|
642
|
+
@profile.update_presence(data)
|
643
|
+
@profile.update_data(data['user'])
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
trigger_event(:update_presence, self, data)
|
648
|
+
|
649
|
+
when :VOICE_STATE_UPDATE
|
650
|
+
server = @cache.get_server(data['guild_id'])
|
651
|
+
state = server.update_voice_state(data)
|
652
|
+
trigger_event(:update_voice_state, self, state)
|
653
|
+
|
654
|
+
else
|
655
|
+
MijDiscord::LOGGER.warn('Dispatch') { "Unhandled gateway event type: #{type}" }
|
656
|
+
trigger_event(:unhandled, self, type, data)
|
657
|
+
end
|
658
|
+
rescue => exc
|
659
|
+
MijDiscord::LOGGER.error('Dispatch') { 'An error occurred in dispatch handler' }
|
660
|
+
MijDiscord::LOGGER.error('Dispatch') { exc }
|
661
|
+
end
|
662
|
+
|
663
|
+
def inspect
|
664
|
+
MijDiscord.make_inspect(self, :auth)
|
665
|
+
end
|
666
|
+
|
667
|
+
private
|
668
|
+
|
669
|
+
def gateway_check
|
670
|
+
raise 'A gateway connection is required for this action' unless connected?
|
671
|
+
end
|
672
|
+
|
673
|
+
def notify_ready
|
674
|
+
@gateway.notify_ready
|
675
|
+
|
676
|
+
trigger_event(:ready, self)
|
677
|
+
|
678
|
+
if @auth.user?
|
679
|
+
guilds = @cache.list_servers.map(&:id)
|
680
|
+
@gateway.send_request_guild_sync(guilds)
|
681
|
+
end
|
682
|
+
end
|
683
|
+
|
684
|
+
def trigger_event(name, *args)
|
685
|
+
@event_dispatchers[name]&.trigger(args)
|
686
|
+
end
|
687
|
+
end
|
678
688
|
end
|