discorb 0.19.0 → 0.20.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.
- checksums.yaml +4 -4
- data/.github/workflows/build_version.yml +2 -2
- data/.rubocop.yml +12 -75
- data/Changelog.md +10 -0
- data/Rakefile +482 -454
- data/lib/discorb/allowed_mentions.rb +68 -72
- data/lib/discorb/app_command/command.rb +466 -398
- data/lib/discorb/app_command/common.rb +65 -25
- data/lib/discorb/app_command/handler.rb +304 -266
- data/lib/discorb/app_command.rb +5 -5
- data/lib/discorb/application.rb +198 -197
- data/lib/discorb/asset.rb +101 -101
- data/lib/discorb/attachment.rb +134 -119
- data/lib/discorb/audit_logs.rb +412 -385
- data/lib/discorb/automod.rb +279 -269
- data/lib/discorb/channel/base.rb +107 -108
- data/lib/discorb/channel/category.rb +32 -32
- data/lib/discorb/channel/container.rb +44 -44
- data/lib/discorb/channel/dm.rb +26 -28
- data/lib/discorb/channel/guild.rb +311 -246
- data/lib/discorb/channel/stage.rb +156 -140
- data/lib/discorb/channel/text.rb +430 -336
- data/lib/discorb/channel/thread.rb +374 -325
- data/lib/discorb/channel/voice.rb +85 -79
- data/lib/discorb/channel.rb +5 -5
- data/lib/discorb/client.rb +635 -621
- data/lib/discorb/color.rb +178 -182
- data/lib/discorb/common.rb +168 -164
- data/lib/discorb/components/button.rb +107 -106
- data/lib/discorb/components/select_menu.rb +157 -145
- data/lib/discorb/components/text_input.rb +103 -106
- data/lib/discorb/components.rb +68 -66
- data/lib/discorb/dictionary.rb +135 -135
- data/lib/discorb/embed.rb +404 -398
- data/lib/discorb/emoji.rb +309 -302
- data/lib/discorb/emoji_table.rb +16099 -8857
- data/lib/discorb/error.rb +131 -131
- data/lib/discorb/event.rb +360 -314
- data/lib/discorb/event_handler.rb +39 -39
- data/lib/discorb/exe/about.rb +17 -17
- data/lib/discorb/exe/irb.rb +72 -67
- data/lib/discorb/exe/new.rb +323 -315
- data/lib/discorb/exe/run.rb +69 -68
- data/lib/discorb/exe/setup.rb +57 -55
- data/lib/discorb/exe/show.rb +12 -12
- data/lib/discorb/extend.rb +25 -45
- data/lib/discorb/extension.rb +89 -83
- data/lib/discorb/flag.rb +126 -128
- data/lib/discorb/gateway.rb +984 -804
- data/lib/discorb/gateway_events.rb +670 -638
- data/lib/discorb/gateway_requests.rb +45 -48
- data/lib/discorb/guild.rb +2115 -1626
- data/lib/discorb/guild_template.rb +280 -241
- data/lib/discorb/http.rb +247 -232
- data/lib/discorb/image.rb +42 -42
- data/lib/discorb/integration.rb +169 -161
- data/lib/discorb/intents.rb +161 -163
- data/lib/discorb/interaction/autocomplete.rb +76 -62
- data/lib/discorb/interaction/command.rb +279 -224
- data/lib/discorb/interaction/components.rb +114 -104
- data/lib/discorb/interaction/modal.rb +36 -32
- data/lib/discorb/interaction/response.rb +379 -336
- data/lib/discorb/interaction/root.rb +271 -257
- data/lib/discorb/interaction.rb +5 -5
- data/lib/discorb/invite.rb +154 -153
- data/lib/discorb/member.rb +344 -311
- data/lib/discorb/message.rb +615 -544
- data/lib/discorb/message_meta.rb +197 -186
- data/lib/discorb/modules.rb +371 -290
- data/lib/discorb/permission.rb +305 -291
- data/lib/discorb/presence.rb +352 -346
- data/lib/discorb/rate_limit.rb +81 -76
- data/lib/discorb/reaction.rb +55 -54
- data/lib/discorb/role.rb +272 -240
- data/lib/discorb/shard.rb +76 -74
- data/lib/discorb/sticker.rb +193 -171
- data/lib/discorb/user.rb +205 -188
- data/lib/discorb/utils/colored_puts.rb +16 -16
- data/lib/discorb/utils.rb +12 -16
- data/lib/discorb/voice_state.rb +305 -281
- data/lib/discorb/webhook.rb +537 -507
- data/lib/discorb.rb +62 -56
- data/sig/discorb/application.rbs +2 -0
- data/sig/discorb/automod.rbs +10 -1
- data/sig/discorb/guild.rbs +2 -0
- data/sig/discorb/message.rbs +2 -0
- data/sig/discorb/user.rbs +22 -20
- metadata +2 -2
data/lib/discorb/client.rb
CHANGED
@@ -1,621 +1,635 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "json"
|
4
|
-
require "logger"
|
5
|
-
|
6
|
-
require "async"
|
7
|
-
require "async/websocket/client"
|
8
|
-
require_relative "./utils/colored_puts"
|
9
|
-
|
10
|
-
module Discorb
|
11
|
-
#
|
12
|
-
# Class for connecting to the Discord server.
|
13
|
-
#
|
14
|
-
class Client
|
15
|
-
# @return [Discorb::Intents] The intents that the client is currently using.
|
16
|
-
attr_accessor :intents
|
17
|
-
# @return [Discorb::Application] The application that the client is using.
|
18
|
-
# @return [nil] If never fetched application by {#fetch_application}.
|
19
|
-
attr_reader :application
|
20
|
-
# @return [Discorb::HTTP] The http client.
|
21
|
-
attr_reader :http
|
22
|
-
# @return [Integer] The heartbeat interval.
|
23
|
-
attr_reader :heartbeat_interval
|
24
|
-
# @return [Integer] The API version of the Discord gateway.
|
25
|
-
# @return [nil] If not connected to the gateway.
|
26
|
-
attr_reader :api_version
|
27
|
-
# @return [String] The token of the client.
|
28
|
-
attr_reader :token
|
29
|
-
# @return [Discorb::AllowedMentions] The allowed mentions that the client is using.
|
30
|
-
attr_reader :allowed_mentions
|
31
|
-
# @return [Discorb::ClientUser] The client user.
|
32
|
-
attr_reader :user
|
33
|
-
# @return [Discorb::Dictionary{Discorb::Snowflake => Discorb::Guild}] A dictionary of guilds.
|
34
|
-
attr_reader :guilds
|
35
|
-
# @return [Discorb::Dictionary{Discorb::Snowflake => Discorb::User}] A dictionary of users.
|
36
|
-
attr_reader :users
|
37
|
-
# @return [Discorb::Dictionary{Discorb::Snowflake => Discorb::Channel}] A dictionary of channels.
|
38
|
-
attr_reader :channels
|
39
|
-
# @return [Discorb::Dictionary{Discorb::Snowflake => Discorb::Emoji}] A dictionary of emojis.
|
40
|
-
attr_reader :emojis
|
41
|
-
# @return [Discorb::Dictionary{Discorb::Snowflake => Discorb::Message}] A dictionary of messages.
|
42
|
-
attr_reader :messages
|
43
|
-
# @return [Array<Discorb::ApplicationCommand::Command>] The commands that the client is using.
|
44
|
-
attr_reader :commands
|
45
|
-
# @return [Float] The ping of the client.
|
46
|
-
# @note This will be calculated from heartbeat and heartbeat_ack.
|
47
|
-
# @return [nil] If not connected to the gateway.
|
48
|
-
attr_reader :ping
|
49
|
-
# @return [:initialized, :running, :closed] The status of the client.
|
50
|
-
attr_reader :status
|
51
|
-
# @return [Hash{String => Discorb::Extension}] The loaded extensions.
|
52
|
-
attr_reader :extensions
|
53
|
-
# @return [Hash{Integer => Discorb::Shard}] The shards of the client.
|
54
|
-
attr_reader :shards
|
55
|
-
# @private
|
56
|
-
# @return [Hash{Discorb::Snowflake => Discorb::ApplicationCommand::Command}] The commands on the top level.
|
57
|
-
attr_reader :callable_commands
|
58
|
-
# @private
|
59
|
-
# @return [{String => Thread::Mutex}] A hash of mutexes.
|
60
|
-
attr_reader :mutex
|
61
|
-
|
62
|
-
# @!attribute [r] session_id
|
63
|
-
# @return [String] The session ID of the client or current shard.
|
64
|
-
# @return [nil] If not connected to the gateway.
|
65
|
-
# @!attribute [r] shard
|
66
|
-
# @return [Discorb::Shard] The current shard. This is implemented with Thread variables.
|
67
|
-
# @return [nil] If client has no shard.
|
68
|
-
# @!attribute [r] shard_id
|
69
|
-
# @return [Integer] The current shard ID. This is implemented with Thread variables.
|
70
|
-
# @return [nil] If client has no shard.
|
71
|
-
# @!attribute [r] logger
|
72
|
-
# @return [Logger] The logger.
|
73
|
-
|
74
|
-
#
|
75
|
-
# Initializes a new client.
|
76
|
-
#
|
77
|
-
# @param [Discorb::AllowedMentions] allowed_mentions The allowed mentions that the client is using.
|
78
|
-
# @param [Discorb::Intents] intents The intents that the client is currently using.
|
79
|
-
# @param [Integer] message_caches The number of messages to cache.
|
80
|
-
# @param [Logger] logger The IO object to use for logging.
|
81
|
-
# @param [:debug, :info, :warn, :error, :critical] log_level The log level.
|
82
|
-
# @param [Boolean] wait_until_ready Whether to delay event dispatch until ready.
|
83
|
-
# @param [Boolean] fetch_member Whether to fetch member on ready. This may slow down the client. Default to `false`.
|
84
|
-
# @param [String] title
|
85
|
-
# The title of the process. `false` to default of ruby, `nil` to `discorb: User#0000`. Default to `nil`.
|
86
|
-
#
|
87
|
-
def initialize(
|
88
|
-
allowed_mentions: nil,
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
@
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
@
|
103
|
-
|
104
|
-
@
|
105
|
-
@
|
106
|
-
@
|
107
|
-
@
|
108
|
-
@
|
109
|
-
@
|
110
|
-
@
|
111
|
-
@
|
112
|
-
@
|
113
|
-
@
|
114
|
-
@
|
115
|
-
@
|
116
|
-
@
|
117
|
-
@
|
118
|
-
@
|
119
|
-
@
|
120
|
-
@
|
121
|
-
@
|
122
|
-
@
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
#
|
129
|
-
#
|
130
|
-
# @
|
131
|
-
#
|
132
|
-
# @param [
|
133
|
-
# @param [
|
134
|
-
#
|
135
|
-
# @
|
136
|
-
#
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
@events[event_name]
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
# @
|
151
|
-
#
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
#
|
170
|
-
#
|
171
|
-
# @
|
172
|
-
#
|
173
|
-
#
|
174
|
-
# @
|
175
|
-
#
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
#
|
230
|
-
#
|
231
|
-
# @
|
232
|
-
#
|
233
|
-
# @
|
234
|
-
#
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
#
|
250
|
-
#
|
251
|
-
#
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
#
|
277
|
-
#
|
278
|
-
#
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
#
|
319
|
-
#
|
320
|
-
# @
|
321
|
-
#
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
_resp, data =
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
#
|
358
|
-
#
|
359
|
-
# @
|
360
|
-
#
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
@
|
441
|
-
end
|
442
|
-
@
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
#
|
474
|
-
#
|
475
|
-
#
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
if
|
571
|
-
|
572
|
-
else
|
573
|
-
@
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
@
|
596
|
-
end
|
597
|
-
end
|
598
|
-
|
599
|
-
def
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
end
|
606
|
-
|
607
|
-
def
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "logger"
|
5
|
+
|
6
|
+
require "async"
|
7
|
+
require "async/websocket/client"
|
8
|
+
require_relative "./utils/colored_puts"
|
9
|
+
|
10
|
+
module Discorb
|
11
|
+
#
|
12
|
+
# Class for connecting to the Discord server.
|
13
|
+
#
|
14
|
+
class Client
|
15
|
+
# @return [Discorb::Intents] The intents that the client is currently using.
|
16
|
+
attr_accessor :intents
|
17
|
+
# @return [Discorb::Application] The application that the client is using.
|
18
|
+
# @return [nil] If never fetched application by {#fetch_application}.
|
19
|
+
attr_reader :application
|
20
|
+
# @return [Discorb::HTTP] The http client.
|
21
|
+
attr_reader :http
|
22
|
+
# @return [Integer] The heartbeat interval.
|
23
|
+
attr_reader :heartbeat_interval
|
24
|
+
# @return [Integer] The API version of the Discord gateway.
|
25
|
+
# @return [nil] If not connected to the gateway.
|
26
|
+
attr_reader :api_version
|
27
|
+
# @return [String] The token of the client.
|
28
|
+
attr_reader :token
|
29
|
+
# @return [Discorb::AllowedMentions] The allowed mentions that the client is using.
|
30
|
+
attr_reader :allowed_mentions
|
31
|
+
# @return [Discorb::ClientUser] The client user.
|
32
|
+
attr_reader :user
|
33
|
+
# @return [Discorb::Dictionary{Discorb::Snowflake => Discorb::Guild}] A dictionary of guilds.
|
34
|
+
attr_reader :guilds
|
35
|
+
# @return [Discorb::Dictionary{Discorb::Snowflake => Discorb::User}] A dictionary of users.
|
36
|
+
attr_reader :users
|
37
|
+
# @return [Discorb::Dictionary{Discorb::Snowflake => Discorb::Channel}] A dictionary of channels.
|
38
|
+
attr_reader :channels
|
39
|
+
# @return [Discorb::Dictionary{Discorb::Snowflake => Discorb::Emoji}] A dictionary of emojis.
|
40
|
+
attr_reader :emojis
|
41
|
+
# @return [Discorb::Dictionary{Discorb::Snowflake => Discorb::Message}] A dictionary of messages.
|
42
|
+
attr_reader :messages
|
43
|
+
# @return [Array<Discorb::ApplicationCommand::Command>] The commands that the client is using.
|
44
|
+
attr_reader :commands
|
45
|
+
# @return [Float] The ping of the client.
|
46
|
+
# @note This will be calculated from heartbeat and heartbeat_ack.
|
47
|
+
# @return [nil] If not connected to the gateway.
|
48
|
+
attr_reader :ping
|
49
|
+
# @return [:initialized, :running, :closed] The status of the client.
|
50
|
+
attr_reader :status
|
51
|
+
# @return [Hash{String => Discorb::Extension}] The loaded extensions.
|
52
|
+
attr_reader :extensions
|
53
|
+
# @return [Hash{Integer => Discorb::Shard}] The shards of the client.
|
54
|
+
attr_reader :shards
|
55
|
+
# @private
|
56
|
+
# @return [Hash{Discorb::Snowflake => Discorb::ApplicationCommand::Command}] The commands on the top level.
|
57
|
+
attr_reader :callable_commands
|
58
|
+
# @private
|
59
|
+
# @return [{String => Thread::Mutex}] A hash of mutexes.
|
60
|
+
attr_reader :mutex
|
61
|
+
|
62
|
+
# @!attribute [r] session_id
|
63
|
+
# @return [String] The session ID of the client or current shard.
|
64
|
+
# @return [nil] If not connected to the gateway.
|
65
|
+
# @!attribute [r] shard
|
66
|
+
# @return [Discorb::Shard] The current shard. This is implemented with Thread variables.
|
67
|
+
# @return [nil] If client has no shard.
|
68
|
+
# @!attribute [r] shard_id
|
69
|
+
# @return [Integer] The current shard ID. This is implemented with Thread variables.
|
70
|
+
# @return [nil] If client has no shard.
|
71
|
+
# @!attribute [r] logger
|
72
|
+
# @return [Logger] The logger.
|
73
|
+
|
74
|
+
#
|
75
|
+
# Initializes a new client.
|
76
|
+
#
|
77
|
+
# @param [Discorb::AllowedMentions] allowed_mentions The allowed mentions that the client is using.
|
78
|
+
# @param [Discorb::Intents] intents The intents that the client is currently using.
|
79
|
+
# @param [Integer] message_caches The number of messages to cache.
|
80
|
+
# @param [Logger] logger The IO object to use for logging.
|
81
|
+
# @param [:debug, :info, :warn, :error, :critical] log_level The log level.
|
82
|
+
# @param [Boolean] wait_until_ready Whether to delay event dispatch until ready.
|
83
|
+
# @param [Boolean] fetch_member Whether to fetch member on ready. This may slow down the client. Default to `false`.
|
84
|
+
# @param [String] title
|
85
|
+
# The title of the process. `false` to default of ruby, `nil` to `discorb: User#0000`. Default to `nil`.
|
86
|
+
#
|
87
|
+
def initialize(
|
88
|
+
allowed_mentions: nil,
|
89
|
+
intents: nil,
|
90
|
+
message_caches: 1000,
|
91
|
+
logger: nil,
|
92
|
+
wait_until_ready: true,
|
93
|
+
fetch_member: false,
|
94
|
+
title: nil
|
95
|
+
)
|
96
|
+
@allowed_mentions =
|
97
|
+
allowed_mentions ||
|
98
|
+
AllowedMentions.new(everyone: true, roles: true, users: true)
|
99
|
+
@intents = (intents or Intents.default)
|
100
|
+
@events = {}
|
101
|
+
@api_version = nil
|
102
|
+
@logger =
|
103
|
+
logger || Logger.new($stdout, progname: "discorb", level: Logger::ERROR)
|
104
|
+
@user = nil
|
105
|
+
@users = Discorb::Dictionary.new
|
106
|
+
@channels = Discorb::Dictionary.new
|
107
|
+
@guilds = Discorb::Dictionary.new(sort: ->(k) { k[0].to_i })
|
108
|
+
@emojis = Discorb::Dictionary.new
|
109
|
+
@messages = Discorb::Dictionary.new(limit: message_caches)
|
110
|
+
@application = nil
|
111
|
+
@last_s = nil
|
112
|
+
@identify_presence = nil
|
113
|
+
@wait_until_ready = wait_until_ready
|
114
|
+
@ready = false
|
115
|
+
@tasks = []
|
116
|
+
@conditions = {}
|
117
|
+
@commands = []
|
118
|
+
@callable_commands = []
|
119
|
+
@status = :initialized
|
120
|
+
@fetch_member = fetch_member
|
121
|
+
@title = title
|
122
|
+
@extensions = {}
|
123
|
+
@mutex = {}
|
124
|
+
@shards = {}
|
125
|
+
set_default_events
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Registers an event handler.
|
130
|
+
# @see file:docs/Events.md Events Documentation
|
131
|
+
#
|
132
|
+
# @param [Symbol] event_name The name of the event.
|
133
|
+
# @param [Symbol] id Custom ID of the event.
|
134
|
+
# @param [Hash] metadata The metadata of the event.
|
135
|
+
# @param [Proc] block The block to execute when the event is triggered.
|
136
|
+
#
|
137
|
+
# @return [Discorb::EventHandler] The event.
|
138
|
+
#
|
139
|
+
def on(event_name, id: nil, **metadata, &block)
|
140
|
+
ne = EventHandler.new(block, id, metadata)
|
141
|
+
@events[event_name] ||= []
|
142
|
+
@events[event_name].delete_if { |e| e.metadata[:override] }
|
143
|
+
@events[event_name] << ne
|
144
|
+
ne
|
145
|
+
end
|
146
|
+
|
147
|
+
#
|
148
|
+
# Almost same as {#on}, but only triggers the event once.
|
149
|
+
#
|
150
|
+
# @param (see #on)
|
151
|
+
#
|
152
|
+
# @return [Discorb::EventHandler] The event.
|
153
|
+
#
|
154
|
+
def once(event_name, id: nil, **metadata, &block)
|
155
|
+
metadata[:once] = true
|
156
|
+
on(event_name, id: id, **metadata, &block)
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# Remove event by ID.
|
161
|
+
#
|
162
|
+
# @param [Symbol] event_name The name of the event.
|
163
|
+
# @param [Symbol] id The ID of the event.
|
164
|
+
#
|
165
|
+
def remove_event(event_name, id)
|
166
|
+
@events[event_name].delete_if { |e| e.id == id }
|
167
|
+
end
|
168
|
+
|
169
|
+
#
|
170
|
+
# Dispatch an event.
|
171
|
+
# @async
|
172
|
+
#
|
173
|
+
# @param [Symbol] event_name The name of the event.
|
174
|
+
# @param [Object] args The arguments to pass to the event.
|
175
|
+
#
|
176
|
+
# @return [Async::Task<void>] The task.
|
177
|
+
#
|
178
|
+
def dispatch(event_name, *args)
|
179
|
+
Async do
|
180
|
+
if (conditions = @conditions[event_name])
|
181
|
+
ids = Set[*conditions.map(&:first).map(&:object_id)]
|
182
|
+
conditions.delete_if do |condition|
|
183
|
+
next unless ids.include?(condition.first.object_id)
|
184
|
+
|
185
|
+
check_result = condition[1].nil? || condition[1].call(*args)
|
186
|
+
if check_result
|
187
|
+
condition.first.signal(args)
|
188
|
+
true
|
189
|
+
else
|
190
|
+
false
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
events = @events[event_name].dup || []
|
195
|
+
if respond_to?("on_#{event_name}")
|
196
|
+
event_method = method("on_#{event_name}")
|
197
|
+
class << event_method
|
198
|
+
def id
|
199
|
+
"method"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
events << event_method
|
203
|
+
end
|
204
|
+
if events.nil?
|
205
|
+
logger.debug "Event #{event_name} doesn't have any proc, skipping"
|
206
|
+
next
|
207
|
+
end
|
208
|
+
logger.debug "Dispatching event #{event_name}"
|
209
|
+
events.each do |block|
|
210
|
+
Async do
|
211
|
+
Async(annotation: "Discorb event: #{event_name}") do |_task|
|
212
|
+
if block.is_a?(Discorb::EventHandler) && block.metadata[:once]
|
213
|
+
@events[event_name].delete(block)
|
214
|
+
end
|
215
|
+
block.call(*args)
|
216
|
+
logger.debug "Dispatched proc with ID #{block.id.inspect}"
|
217
|
+
rescue StandardError, ScriptError => e
|
218
|
+
if event_name == :error
|
219
|
+
raise e
|
220
|
+
else
|
221
|
+
dispatch(:error, event_name, args, e)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
#
|
230
|
+
# Fetch user from ID.
|
231
|
+
# @async
|
232
|
+
#
|
233
|
+
# @param [#to_s] id <description>
|
234
|
+
#
|
235
|
+
# @return [Async::Task<Discorb::User>] The user.
|
236
|
+
#
|
237
|
+
# @raise [Discorb::NotFoundError] If the user doesn't exist.
|
238
|
+
#
|
239
|
+
def fetch_user(id)
|
240
|
+
Async do
|
241
|
+
_resp, data =
|
242
|
+
@http.request(
|
243
|
+
Route.new("/users/#{id}", "//users/:user_id", :get)
|
244
|
+
).wait
|
245
|
+
User.new(self, data)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
#
|
250
|
+
# Fetch channel from ID.
|
251
|
+
# @async
|
252
|
+
#
|
253
|
+
# @param [#to_s] id The ID of the channel.
|
254
|
+
#
|
255
|
+
# @return [Async::Task<Discorb::Channel>] The channel.
|
256
|
+
#
|
257
|
+
# @raise [Discorb::NotFoundError] If the channel doesn't exist.
|
258
|
+
#
|
259
|
+
def fetch_channel(id)
|
260
|
+
Async do
|
261
|
+
_resp, data =
|
262
|
+
@http.request(
|
263
|
+
Route.new("/channels/#{id}", "//channels/:channel_id", :get)
|
264
|
+
).wait
|
265
|
+
Channel.make_channel(self, data)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
#
|
270
|
+
# Fetch guild from ID.
|
271
|
+
# @async
|
272
|
+
#
|
273
|
+
# @param [#to_s] id <description>
|
274
|
+
#
|
275
|
+
# @return [Async::Task<Discorb::Guild>] The guild.
|
276
|
+
#
|
277
|
+
# @raise [Discorb::NotFoundError] If the guild doesn't exist.
|
278
|
+
#
|
279
|
+
def fetch_guild(id)
|
280
|
+
Async do
|
281
|
+
_resp, data =
|
282
|
+
@http.request(
|
283
|
+
Route.new("/guilds/#{id}", "//guilds/:guild_id", :get)
|
284
|
+
).wait
|
285
|
+
Guild.new(self, data, false)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
#
|
290
|
+
# Fetch invite from code.
|
291
|
+
# @async
|
292
|
+
#
|
293
|
+
# @param [String] code The code of the invite.
|
294
|
+
# @param [Boolean] with_count Whether to include the count of the invite.
|
295
|
+
# @param [Boolean] with_expiration Whether to include the expiration of the invite.
|
296
|
+
#
|
297
|
+
# @return [Async::Task<Discorb::Invite>] The invite.
|
298
|
+
#
|
299
|
+
def fetch_invite(code, with_count: true, with_expiration: true)
|
300
|
+
Async do
|
301
|
+
_resp, data =
|
302
|
+
@http.request(
|
303
|
+
Route.new(
|
304
|
+
"/invites/#{code}?with_count=#{with_count}&with_expiration=#{with_expiration}",
|
305
|
+
"//invites/:code",
|
306
|
+
:get
|
307
|
+
)
|
308
|
+
).wait
|
309
|
+
Invite.new(self, data, false)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
#
|
314
|
+
# Fetch webhook from ID.
|
315
|
+
# If application was cached, it will be used.
|
316
|
+
# @async
|
317
|
+
#
|
318
|
+
# @param [Boolean] force Whether to force the fetch.
|
319
|
+
#
|
320
|
+
# @return [Async::Task<Discorb::Application>] The application.
|
321
|
+
#
|
322
|
+
def fetch_application(force: false)
|
323
|
+
Async do
|
324
|
+
next @application if @application && !force
|
325
|
+
|
326
|
+
_resp, data =
|
327
|
+
@http.request(
|
328
|
+
Route.new(
|
329
|
+
"/oauth2/applications/@me",
|
330
|
+
"//oauth2/applications/@me",
|
331
|
+
:get
|
332
|
+
)
|
333
|
+
).wait
|
334
|
+
@application = Application.new(self, data)
|
335
|
+
@application
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
#
|
340
|
+
# Fetch nitro sticker pack from ID.
|
341
|
+
# @async
|
342
|
+
#
|
343
|
+
# @return [Async::Task<Array<Discorb::Sticker::Pack>>] The packs.
|
344
|
+
#
|
345
|
+
def fetch_nitro_sticker_packs
|
346
|
+
Async do
|
347
|
+
_resp, data =
|
348
|
+
@http.request(
|
349
|
+
Route.new("/sticker-packs", "//sticker-packs", :get)
|
350
|
+
).wait
|
351
|
+
data[:sticker_packs].map { |pack| Sticker::Pack.new(self, pack) }
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
#
|
356
|
+
# Update presence of the client.
|
357
|
+
#
|
358
|
+
# @param [Discorb::Activity] activity The activity to update.
|
359
|
+
# @param [:online, :idle, :dnd, :invisible] status The status to update.
|
360
|
+
#
|
361
|
+
def update_presence(activity = nil, status: nil)
|
362
|
+
payload = { activities: [], status: status, since: nil, afk: nil }
|
363
|
+
payload[:activities] = [activity.to_hash] unless activity.nil?
|
364
|
+
payload[:status] = status unless status.nil?
|
365
|
+
if connection
|
366
|
+
Async { send_gateway(3, **payload) }
|
367
|
+
else
|
368
|
+
@identify_presence = payload
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
alias change_presence update_presence
|
373
|
+
|
374
|
+
#
|
375
|
+
# Method to wait for a event.
|
376
|
+
# @async
|
377
|
+
#
|
378
|
+
# @param [Symbol] event The name of the event.
|
379
|
+
# @param [Integer] timeout The timeout in seconds.
|
380
|
+
# @param [Proc] check The check to use.
|
381
|
+
#
|
382
|
+
# @return [Async::Task<Object>] The result of the event.
|
383
|
+
#
|
384
|
+
# @raise [Discorb::TimeoutError] If the event didn't occur in time.
|
385
|
+
#
|
386
|
+
def event_lock(event, timeout = nil, &check)
|
387
|
+
Async do |task|
|
388
|
+
condition = Async::Condition.new
|
389
|
+
@conditions[event] ||= []
|
390
|
+
@conditions[event] << [condition, check]
|
391
|
+
if timeout.nil?
|
392
|
+
value = condition.wait
|
393
|
+
else
|
394
|
+
timeout_task =
|
395
|
+
task.with_timeout(timeout) do
|
396
|
+
condition.wait
|
397
|
+
rescue Async::TimeoutError
|
398
|
+
@conditions[event].delete_if { |c| c.first == condition }
|
399
|
+
raise Discorb::TimeoutError,
|
400
|
+
"Timeout waiting for event #{event}",
|
401
|
+
cause: nil
|
402
|
+
end
|
403
|
+
value = timeout_task
|
404
|
+
end
|
405
|
+
value.length <= 1 ? value.first : value
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
alias await event_lock
|
410
|
+
|
411
|
+
def inspect
|
412
|
+
"#<#{self.class} user=\"#{user}\">"
|
413
|
+
end
|
414
|
+
|
415
|
+
#
|
416
|
+
# Load the extension.
|
417
|
+
#
|
418
|
+
# @param [Class, Discorb::Extension] ext The extension to load.
|
419
|
+
# @param [Object] ... The arguments to pass to the `ext#initialize`.
|
420
|
+
#
|
421
|
+
def load_extension(ext, ...)
|
422
|
+
case ext
|
423
|
+
when Class
|
424
|
+
unless ext < Discorb::Extension
|
425
|
+
raise ArgumentError, "#{ext} is not a extension"
|
426
|
+
end
|
427
|
+
|
428
|
+
ins = ext.new(self, ...)
|
429
|
+
when Discorb::Extension
|
430
|
+
ins = ext
|
431
|
+
else
|
432
|
+
raise ArgumentError, "#{ext} is not a extension"
|
433
|
+
end
|
434
|
+
|
435
|
+
@events.each_value do |event|
|
436
|
+
event.delete_if { |c| c.metadata[:extension] == ins.class.name }
|
437
|
+
end
|
438
|
+
ins.events.each do |name, events|
|
439
|
+
@events[name] ||= []
|
440
|
+
events.each { |event| @events[name] << event }
|
441
|
+
end
|
442
|
+
@commands.delete_if do |cmd|
|
443
|
+
cmd.respond_to? :extension and cmd.extension == ins.class.name
|
444
|
+
end
|
445
|
+
ins.class.commands.each do |cmd|
|
446
|
+
cmd.define_singleton_method(:extension) { ins.class.name }
|
447
|
+
cmd.replace_block(ins)
|
448
|
+
cmd.block.define_singleton_method(:self_replaced) { true }
|
449
|
+
@commands << cmd
|
450
|
+
end
|
451
|
+
|
452
|
+
cls = ins.class
|
453
|
+
cls.loaded(self, ...) if cls.respond_to? :loaded
|
454
|
+
ins.class.callable_commands.each do |cmd|
|
455
|
+
unless cmd.respond_to? :self_replaced
|
456
|
+
cmd.define_singleton_method(:extension) { ins.class.name }
|
457
|
+
cmd.replace_block(ins)
|
458
|
+
cmd.block.define_singleton_method(:self_replaced) { true }
|
459
|
+
end
|
460
|
+
@callable_commands << cmd
|
461
|
+
end
|
462
|
+
@extensions[ins.class.name] = ins
|
463
|
+
ins
|
464
|
+
end
|
465
|
+
|
466
|
+
include Discorb::Gateway::Handler
|
467
|
+
include Discorb::ApplicationCommand::Handler
|
468
|
+
|
469
|
+
#
|
470
|
+
# Starts the client.
|
471
|
+
# @note This method behavior will change by CLI.
|
472
|
+
# @see file:docs/cli.md CLI documentation
|
473
|
+
#
|
474
|
+
# @param [String, nil] token The token to use.
|
475
|
+
#
|
476
|
+
# @note If the token is nil, you should use `discorb run` with the `-e` or `--env` option.
|
477
|
+
#
|
478
|
+
def run(token = nil, shards: nil, shard_count: nil)
|
479
|
+
token ||= ENV.fetch("DISCORB_CLI_TOKEN", nil)
|
480
|
+
if token.nil?
|
481
|
+
raise ArgumentError,
|
482
|
+
"Token is not specified, and -e/--env is not specified"
|
483
|
+
end
|
484
|
+
|
485
|
+
case ENV.fetch("DISCORB_CLI_FLAG", nil)
|
486
|
+
when nil
|
487
|
+
start_client(token, shards: shards, shard_count: shard_count)
|
488
|
+
when "run"
|
489
|
+
before_run(token)
|
490
|
+
start_client(token, shards: shards, shard_count: shard_count)
|
491
|
+
when "setup"
|
492
|
+
run_setup(token)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
#
|
497
|
+
# Stops the client.
|
498
|
+
#
|
499
|
+
def close
|
500
|
+
@shards.any? ? @shards.each_value(&:close) : @connection.send_close
|
501
|
+
@tasks.each(&:stop)
|
502
|
+
@status = :closed
|
503
|
+
end
|
504
|
+
|
505
|
+
def session_id
|
506
|
+
shard ? shard.session_id : @session_id
|
507
|
+
end
|
508
|
+
|
509
|
+
def logger
|
510
|
+
shard&.logger || @logger
|
511
|
+
end
|
512
|
+
|
513
|
+
def shard
|
514
|
+
Thread.current.thread_variable_get("shard")
|
515
|
+
end
|
516
|
+
|
517
|
+
def shard_id
|
518
|
+
Thread.current.thread_variable_get("shard_id")
|
519
|
+
end
|
520
|
+
|
521
|
+
private
|
522
|
+
|
523
|
+
def before_run(token)
|
524
|
+
require "json"
|
525
|
+
options =
|
526
|
+
JSON.parse(ENV.fetch("DISCORB_CLI_OPTIONS", nil), symbolize_names: true)
|
527
|
+
setup_commands(token) if options[:setup]
|
528
|
+
end
|
529
|
+
|
530
|
+
def run_setup(token)
|
531
|
+
# @type var guild_ids: Array[String] | false
|
532
|
+
guild_ids = false
|
533
|
+
if guilds = ENV.fetch("DISCORB_SETUP_GUILDS", nil)
|
534
|
+
guild_ids = guilds.split(",")
|
535
|
+
end
|
536
|
+
guild_ids = false if guild_ids == ["global"]
|
537
|
+
setup_commands(token, guild_ids: guild_ids).wait
|
538
|
+
clear_commands(
|
539
|
+
token,
|
540
|
+
ENV.fetch("DISCORB_SETUP_CLEAR_GUILDS", "").split(",")
|
541
|
+
)
|
542
|
+
if ENV.fetch("DISCORB_SETUP_SCRIPT", nil) == "true"
|
543
|
+
@events[:setup]&.each(&:call)
|
544
|
+
on_setup if respond_to? :on_setup
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
def set_status(status, shard)
|
549
|
+
if shard.nil?
|
550
|
+
@status = status
|
551
|
+
else
|
552
|
+
@shards[shard].status = status
|
553
|
+
end
|
554
|
+
end
|
555
|
+
|
556
|
+
def connection
|
557
|
+
shard_id ? @shards[shard_id].connection : @connection
|
558
|
+
end
|
559
|
+
|
560
|
+
def connection=(value)
|
561
|
+
if shard_id
|
562
|
+
@shards[shard_id].connection = value
|
563
|
+
else
|
564
|
+
@connection = value
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
def session_id=(value)
|
569
|
+
sid = shard_id
|
570
|
+
if sid
|
571
|
+
@shards[sid].session_id = value
|
572
|
+
else
|
573
|
+
@session_id = value
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
def start_client(token, shards: nil, shard_count: nil)
|
578
|
+
@token = token.to_s
|
579
|
+
@shard_count = shard_count
|
580
|
+
Signal.trap(:SIGINT) do
|
581
|
+
logger.info "SIGINT received, closing..."
|
582
|
+
Signal.trap(:SIGINT, "DEFAULT")
|
583
|
+
close
|
584
|
+
end
|
585
|
+
if shards.nil?
|
586
|
+
main_loop(nil)
|
587
|
+
else
|
588
|
+
@shards =
|
589
|
+
shards.to_h.with_index do |shard, i|
|
590
|
+
[shard, Shard.new(self, shard, shard_count, i)]
|
591
|
+
end
|
592
|
+
@shards.values[..-1].each_with_index do |shard, i|
|
593
|
+
shard.next_shard = @shards.values[i + 1]
|
594
|
+
end
|
595
|
+
@shards.each_value { |s| s.thread.join }
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
def main_loop(shard)
|
600
|
+
set_status(:running, shard)
|
601
|
+
connect_gateway(false).wait
|
602
|
+
rescue StandardError
|
603
|
+
set_status(:closed, shard)
|
604
|
+
raise
|
605
|
+
end
|
606
|
+
|
607
|
+
def main_task
|
608
|
+
shard_id ? shard.main_task : @main_task
|
609
|
+
end
|
610
|
+
|
611
|
+
def main_task=(value)
|
612
|
+
if shard_id
|
613
|
+
shard.main_task = value
|
614
|
+
else
|
615
|
+
@main_task = value
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
619
|
+
def set_default_events
|
620
|
+
on :error, override: true do |event_name, _args, e|
|
621
|
+
message =
|
622
|
+
"An error occurred while dispatching #{event_name}:\n#{e.full_message}"
|
623
|
+
logger.error message
|
624
|
+
end
|
625
|
+
|
626
|
+
once :standby do
|
627
|
+
next if @title == false
|
628
|
+
|
629
|
+
title =
|
630
|
+
@title || ENV.fetch("DISCORB_CLI_TITLE", nil) || "discorb: #{@user}"
|
631
|
+
Process.setproctitle title
|
632
|
+
end
|
633
|
+
end
|
634
|
+
end
|
635
|
+
end
|