discorb 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,11 +2,11 @@ require "discorb"
2
2
 
3
3
  client = Discorb::Client.new
4
4
 
5
- client.once :ready do |_task|
5
+ client.once :ready do
6
6
  puts "Logged in as #{client.user}"
7
7
  end
8
8
 
9
- client.on :message do |_task, message|
9
+ client.on :message do |message|
10
10
  next if message.author.bot?
11
11
  next unless message.content.start_with?("eval ")
12
12
 
@@ -6,7 +6,7 @@ client.once :ready do
6
6
  puts "Logged in as #{client.user}"
7
7
  end
8
8
 
9
- client.on :message do |_task, message|
9
+ client.on :message do |message|
10
10
  next if message.author.bot?
11
11
  next unless message.content == "ping"
12
12
 
@@ -13,7 +13,7 @@ client.once :ready do
13
13
  puts "Logged in as #{client.user}"
14
14
  end
15
15
 
16
- client.on :reaction_add do |_task, event|
16
+ client.on :reaction_add do |event|
17
17
  next unless event.emoji.value.end_with?(0x0000fe0f.chr("utf-8") + 0x000020e3.chr("utf-8"))
18
18
  next if event.member.bot?
19
19
 
@@ -28,7 +28,7 @@ client.on :reaction_add do |_task, event|
28
28
  end
29
29
  end
30
30
 
31
- client.on :reaction_remove do |_task, event|
31
+ client.on :reaction_remove do |event|
32
32
  next unless event.emoji.value.end_with?(0x0000fe0f.chr("utf-8") + 0x000020e3.chr("utf-8"))
33
33
  next if event.member.bot?
34
34
 
@@ -43,7 +43,7 @@ client.on :reaction_remove do |_task, event|
43
43
  end
44
44
  end
45
45
 
46
- client.on :message do |_task, message|
46
+ client.on :message do |message|
47
47
  next unless message.content.start_with?("/rp ")
48
48
  next if message.author.bot?
49
49
 
@@ -6,7 +6,7 @@ client.once :ready do
6
6
  puts "Logged in as #{client.user}"
7
7
  end
8
8
 
9
- client.on :message do |_task, message|
9
+ client.on :message do |message|
10
10
  next if message.author.bot?
11
11
  next unless message.content == "!quiz"
12
12
 
data/lib/discorb/asset.rb CHANGED
@@ -54,4 +54,38 @@ module Discorb
54
54
  end
55
55
  end
56
56
  end
57
+
58
+ #
59
+ # Represents a default avatar.
60
+ #
61
+ class DefaultAvatar < DiscordModel
62
+
63
+ # @!attribute [r] animated?
64
+ # @return [false] For compatibility with {Asset}, always `false`.
65
+
66
+ # @!visibility private
67
+ def initialize(discriminator)
68
+ @discriminator = discriminator.to_s.rjust(4, "0")
69
+ end
70
+
71
+ def animated?
72
+ false
73
+ end
74
+
75
+ #
76
+ # Returns the URL of the avatar.
77
+ #
78
+ # @param [String] image_format The image format. This is compatible with {Asset#url}, will be ignored.
79
+ # @param [Integer] size The size of the image. This is compatible with {Asset#url}, will be ignored.
80
+ #
81
+ # @return [String] URL of the avatar.
82
+ #
83
+ def url(image_format: nil, size: 1024)
84
+ "https://cdn.discordapp.com/embed/avatars/#{@discriminator.to_i % 5}.png"
85
+ end
86
+
87
+ def inspect
88
+ "#<#{self.class} #{@discriminator}>"
89
+ end
90
+ end
57
91
  end
@@ -975,6 +975,10 @@ module Discorb
975
975
  end
976
976
  end
977
977
 
978
+ class News < ThreadChannel
979
+ @channel_type = 10
980
+ end
981
+
978
982
  class Public < ThreadChannel
979
983
  @channel_type = 11
980
984
  end
@@ -41,6 +41,12 @@ module Discorb
41
41
  attr_reader :messages
42
42
  # @return [Discorb::Logger] The logger.
43
43
  attr_reader :log
44
+ # @return [Array<Discorb::Command::Command>] The commands that the client is using.
45
+ attr_reader :commands
46
+ # @return [Float] The ping of the client.
47
+ # @note This will be calculated from heartbeat and heartbeat_ack.
48
+ # @return [nil] If not connected to the gateway.
49
+ attr_reader :ping
44
50
 
45
51
  #
46
52
  # Initializes a new client.
@@ -52,8 +58,13 @@ module Discorb
52
58
  # @param [Boolean] colorize_log Whether to colorize the log.
53
59
  # @param [:debug, :info, :warn, :error, :critical] log_level The log level.
54
60
  # @param [Boolean] wait_until_ready Whether to delay event dispatch until ready.
61
+ # @param [Boolean] overwrite_application_commands Whether to overwrite application commands on ready.
55
62
  #
56
- def initialize(allowed_mentions: nil, intents: nil, message_caches: 1000, log: nil, colorize_log: false, log_level: :info, wait_until_ready: true)
63
+ def initialize(
64
+ allowed_mentions: nil, intents: nil, message_caches: 1000,
65
+ log: nil, colorize_log: false, log_level: :info,
66
+ wait_until_ready: true, overwrite_application_commands: true
67
+ )
57
68
  @allowed_mentions = allowed_mentions || AllowedMentions.new(everyone: true, roles: true, users: true)
58
69
  @intents = (intents or Intents.default)
59
70
  @events = {}
@@ -72,6 +83,8 @@ module Discorb
72
83
  @ready = false
73
84
  @tasks = []
74
85
  @conditions = {}
86
+ @commands = []
87
+ @overwrite_application_commands = overwrite_application_commands
75
88
  end
76
89
 
77
90
  #
@@ -124,7 +137,7 @@ module Discorb
124
137
  # @param [Object] args The arguments to pass to the event.
125
138
  #
126
139
  def dispatch(event_name, *args)
127
- Async do |_task|
140
+ Async do
128
141
  if (conditions = @conditions[event_name])
129
142
  ids = Set[*conditions.map(&:first).map(&:object_id)]
130
143
  conditions.delete_if do |condition|
@@ -148,7 +161,7 @@ module Discorb
148
161
  lambda { |event_args|
149
162
  Async(annotation: "Discorb event: #{event_name}") do |task|
150
163
  @events[event_name].delete(block) if block.discriminator[:once]
151
- block.call(task, *event_args)
164
+ block.call(*event_args)
152
165
  @log.debug "Dispatched proc with ID #{block.id.inspect}"
153
166
  rescue StandardError, ScriptError => e
154
167
  message = "An error occurred while dispatching proc with ID #{block.id.inspect}\n#{e.full_message}"
@@ -293,7 +306,7 @@ module Discorb
293
306
  end
294
307
  payload[:status] = status unless status.nil?
295
308
  if @connection
296
- Async do |_task|
309
+ Async do
297
310
  send_gateway(3, **payload)
298
311
  end
299
312
  else
@@ -306,7 +319,7 @@ module Discorb
306
319
  #
307
320
  # Method to wait for a event.
308
321
  #
309
- # @param [Symbol] event_name The name of the event.
322
+ # @param [Symbol] event The name of the event.
310
323
  # @param [Integer] timeout The timeout in seconds.
311
324
  # @param [Proc] check The check to use.
312
325
  #
@@ -347,6 +360,9 @@ module Discorb
347
360
  #
348
361
  def extend(mod)
349
362
  if mod.respond_to?(:events)
363
+ @events.each_value do |event|
364
+ event.delete_if { |c| c.discriminator[:extension] == mod.name }
365
+ end
350
366
  mod.events.each do |name, events|
351
367
  @events[name] = [] if @events[name].nil?
352
368
  events.each do |event|
@@ -358,6 +374,7 @@ module Discorb
358
374
  super(mod)
359
375
  end
360
376
 
361
- include Discorb::GatewayHandler
377
+ include Discorb::Gateway::Handler
378
+ include Discorb::Command::Handler
362
379
  end
363
380
  end
@@ -0,0 +1,393 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ #
5
+ # Handles application commands.
6
+ #
7
+ module Command
8
+ #
9
+ # Module to handle commands.
10
+ #
11
+ module Handler
12
+ #
13
+ # Add new top-level command.
14
+ #
15
+ # @param [String] command_name Command name.
16
+ # @param [String] description Command description.
17
+ # @param [Hash{String => Hash{:description => String, :optional => Boolean, :type => Object}}] options Command options.
18
+ # The key is the option name, the value is a hash with the following keys:
19
+ #
20
+ # | Key | Type | Description |
21
+ # | --- | --- | --- |
22
+ # | `:description` | `String` | Description of the option. |
23
+ # | `:optional` | `Boolean` | Whether the option is optional or not. |
24
+ # | `:type` | `Object` | Type of the option. |
25
+ # | `:choice` | `Hash{String => String, Integer, Float}` | Type of the option. |
26
+ #
27
+ # @param [Array<#to_s>] guild_ids Guild IDs to restrict the command to.
28
+ # @param [Proc] block Command block.
29
+ #
30
+ # @return [Discorb::Command::Command::SlashCommand]
31
+ #
32
+ # @see file:docs/application_command.md#register-slash-command
33
+ #
34
+ def slash(command_name, description, options = {}, guild_ids: [], &block)
35
+ command = Discorb::Command::Command::SlashCommand.new(command_name, description, options, guild_ids, block, 1, "")
36
+ @commands << command
37
+ command
38
+ end
39
+
40
+ #
41
+ # Add new command with group.
42
+ #
43
+ # @param [String] command_name Command name.
44
+ # @param [String] description Command description.
45
+ # @param [Array<#to_s>] guild_ids Guild IDs to restrict the command to.
46
+ #
47
+ # @return [Discorb::Command::Command::GroupCommand] Command object.
48
+ #
49
+ # @see file:docs/slash_command.md
50
+ #
51
+ def slash_group(command_name, description, guild_ids: [])
52
+ command = Discorb::Command::Command::GroupCommand.new(command_name, description, guild_ids, nil)
53
+ @commands << command
54
+ command
55
+ end
56
+
57
+ #
58
+ # Add message context menu command.
59
+ #
60
+ # @param [String] command_name Command name.
61
+ # @param [Array<#to_s>] guild_ids Guild IDs to restrict the command to.
62
+ # @param [Proc] block Command block.
63
+ # @yield [interaction, message] Block to execute.
64
+ # @yieldparam [Discorb::CommandInteraction::UserMenuCommand] Interaction object.
65
+ # @yieldparam [Discorb::Message] user Message object.
66
+ #
67
+ # @return [Discorb::Command::Command] Command object.
68
+ #
69
+ def message_command(command_name, guild_ids: [], &block)
70
+ command = Discorb::Command::Command.new(command_name, guild_ids, block, 3)
71
+ @commands << command
72
+ command
73
+ end
74
+
75
+ #
76
+ # Add user context menu command.
77
+ #
78
+ # @param [String] command_name Command name.
79
+ # @param [Array<#to_s>] guild_ids Guild IDs to restrict the command to.
80
+ # @param [Proc] block Command block.
81
+ # @yield [interaction, user] Block to execute.
82
+ # @yieldparam [Discorb::CommandInteraction::UserMenuCommand] Interaction object.
83
+ # @yieldparam [Discorb::User] user User object.
84
+ #
85
+ # @return [Discorb::Command::Command] Command object.
86
+ #
87
+ def user_command(command_name, guild_ids: [], &block)
88
+ command = Discorb::Command::Command.new(command_name, guild_ids, block, 2)
89
+ @commands << command
90
+ command
91
+ end
92
+
93
+ #
94
+ # Setup commands.
95
+ # @note This method is called automatically if overwrite_application_commands is set to true.
96
+ # @see Client#initialize
97
+ #
98
+ # @param [String] token Bot token.
99
+ # @note `token` parameter only required if you don't run client.
100
+ #
101
+ def setup_commands(token = nil)
102
+ Async do
103
+ @token ||= token
104
+ @http = HTTP.new(self)
105
+ global_commands = @commands.select { |c| c.guild_ids.empty? }
106
+ guild_ids = Set[*@commands.map(&:guild_ids).flatten]
107
+ app_info = fetch_application.wait
108
+ http.put("/applications/#{app_info.id}/commands", global_commands.map(&:to_hash)).wait unless global_commands.empty?
109
+ guild_ids.each do |guild_id|
110
+ commands = @commands.select { |c| c.guild_ids.include?(guild_id) }
111
+ http.put("/applications/#{app_info.id}/guilds/#{guild_id}/commands", commands.map(&:to_hash)).wait
112
+ end unless guild_ids.empty?
113
+ @log.info "Successfully setup commands"
114
+ end
115
+ end
116
+ end
117
+
118
+ #
119
+ # Represents a application command.
120
+ # @abstract
121
+ #
122
+ class Command < DiscordModel
123
+ # @return [String] The name of the command.
124
+ attr_reader :name
125
+ # @return [Array<#to_s>] The guild ids that the command is enabled in.
126
+ attr_reader :guild_ids
127
+ # @return [Proc] The block of the command.
128
+ attr_reader :block
129
+ # @return [:chat_input, :user, :message] The type of the command.
130
+ attr_reader :type
131
+ # @return [Integer] The raw type of the command.
132
+ attr_reader :type_raw
133
+ # @return [Discorb::Dictionary{Discorb::Snowflake, :global => Discorb::Snowflake}] The ID mapping.
134
+ attr_reader :id_map
135
+
136
+ @types = {
137
+ 1 => :chat_input,
138
+ 2 => :user,
139
+ 3 => :message,
140
+ }.freeze
141
+
142
+ # @!visibility private
143
+ def initialize(name, guild_ids, block, type)
144
+ @name = name
145
+ @guild_ids = guild_ids.map(&:to_s)
146
+ @block = block
147
+ @raw_type = type
148
+ @type = Discorb::Command::Command.types[type]
149
+ @type_raw = type
150
+ @id_map = Discorb::Dictionary.new
151
+ end
152
+
153
+ # @!visibility private
154
+ def to_hash
155
+ {
156
+ name: @name,
157
+ default_permission: @default_permission,
158
+ type: @type_raw,
159
+ }
160
+ end
161
+
162
+ #
163
+ # Represents the slash command.
164
+ #
165
+ class SlashCommand < Command
166
+ # @return [String] The description of the command.
167
+ attr_reader :description
168
+ # @return [Hash{String => Hash}] The options of the command.
169
+ attr_reader :options
170
+
171
+ # @!visibility private
172
+ def initialize(name, description, options, guild_ids, block, type, parent)
173
+ @description = description
174
+ @name = name
175
+ @guild_ids = guild_ids.map(&:to_s)
176
+ @block = block
177
+ @type = Discorb::Command::Command.types[type]
178
+ @options = options
179
+ @id = nil
180
+ @parent = parent
181
+ @id_map = Discorb::Dictionary.new
182
+ end
183
+
184
+ #
185
+ # Returns the commands name.
186
+ #
187
+ # @return [String] The name of the command.
188
+ #
189
+ def to_s
190
+ (@parent + " " + @name).strip
191
+ end
192
+
193
+ # @!visibility private
194
+ def to_hash
195
+ options_payload = options.map do |name, value|
196
+ ret = {
197
+ type: case value[:type]
198
+ when String, :string, :str
199
+ 3
200
+ when Integer, :integer, :int
201
+ 4
202
+ when TrueClass, FalseClass, :boolean, :bool
203
+ 5
204
+ when Discorb::User, Discorb::Member, :user, :member
205
+ 6
206
+ when Discorb::Channel, :channel
207
+ 7
208
+ when Discorb::Role, :role
209
+ 8
210
+ when :mentionable
211
+ 9
212
+ when Float, :float
213
+ 10
214
+ else
215
+ raise ArgumentError, "Invalid option type: #{value[:type]}"
216
+ end,
217
+ name: name,
218
+ description: value[:description],
219
+ required: !value[:optional],
220
+ }
221
+ if value[:choices]
222
+ ret[:choices] = value[:choices].map { |t| { name: t[0], value: t[1] } }
223
+ end
224
+ ret
225
+ end
226
+ {
227
+ name: @name,
228
+ default_permission: true,
229
+ description: @description,
230
+ options: options_payload,
231
+ }
232
+ end
233
+ end
234
+
235
+ #
236
+ # Represents the command with subcommands.
237
+ #
238
+ class GroupCommand < Command
239
+ # @return [Array<Discorb::Command::Command::SlashCommand, Discorb::Command::Command::SubcommandGroup>] The subcommands of the command.
240
+ attr_reader :commands
241
+ # @return [String] The description of the command.
242
+ attr_reader :description
243
+
244
+ # @!visibility private
245
+ def initialize(name, description, guild_ids, type)
246
+ super(name, guild_ids, block, type)
247
+ @description = description
248
+ @commands = []
249
+ @id_map = Discorb::Dictionary.new
250
+ end
251
+
252
+ #
253
+ # Add new subcommand.
254
+ #
255
+ # @param (see Discorb::Command::Handler#slash)
256
+ # @return [Discorb::Command::Command::SlashCommand] The added subcommand.
257
+ #
258
+ def slash(command_name, description, options = {}, &block)
259
+ command = Discorb::Command::Command::SlashCommand.new(command_name, description, options, [], block, 1, @name)
260
+ options_payload = options.map do |name, value|
261
+ ret = {
262
+ type: case (value[:type].is_a?(Array) ? value[:type].first : value[:type])
263
+ when String, :string
264
+ 3
265
+ when Integer
266
+ 4
267
+ when TrueClass, FalseClass, :boolean
268
+ 5
269
+ when Discorb::User, Discorb::Member, :user, :member
270
+ 6
271
+ when Discorb::Channel, :channel
272
+ 7
273
+ when Discorb::Role, :role
274
+ 8
275
+ when :mentionable
276
+ 9
277
+ when Float
278
+ 10
279
+ end,
280
+ name: name,
281
+ description: value[:description],
282
+ required: !value[:optional],
283
+ }
284
+ if value[:type].is_a?(Array)
285
+ ret[:choices] = value[:type]
286
+ end
287
+
288
+ ret
289
+ end
290
+ {
291
+ name: @name,
292
+ default_permission: true,
293
+ description: @description,
294
+ options: options_payload,
295
+ }
296
+ @commands << command
297
+ command
298
+ end
299
+
300
+ #
301
+ # Add new subcommand group.
302
+ #
303
+ # @param [String] command_name Group name.
304
+ # @param [String] description Group description.
305
+ #
306
+ # @return [Discorb::Command::Command::SubcommandGroup] Command object.
307
+ #
308
+ # @see file:docs/slash_command.md
309
+ #
310
+ def group(command_name, description)
311
+ command = Discorb::Command::Command::SubcommandGroup.new(command_name, description, @name)
312
+ @commands << command
313
+ command
314
+ end
315
+
316
+ #
317
+ # Returns the command name.
318
+ #
319
+ # @return [String] The command name.
320
+ #
321
+ def to_s
322
+ @name
323
+ end
324
+
325
+ # @!visibility private
326
+ def to_hash
327
+ options_payload = @commands.map do |command|
328
+ if command.is_a?(SlashCommand)
329
+ {
330
+ name: command.name,
331
+ description: command.description,
332
+ default_permission: command.enabled,
333
+ type: 1,
334
+ options: command.to_hash[:options],
335
+ }
336
+ else
337
+ {
338
+ name: command.name,
339
+ description: command.description,
340
+ default_permission: command.enabled,
341
+ type: 2,
342
+ options: command.commands.map { |c| c.to_hash.merge(type: 1) },
343
+ }
344
+ end
345
+ end
346
+
347
+ {
348
+ name: @name,
349
+ default_permission: @enabled,
350
+ description: @description,
351
+ options: options_payload,
352
+ }
353
+ end
354
+ end
355
+
356
+ #
357
+ # Represents the subcommand group.
358
+ #
359
+ class SubcommandGroup < GroupCommand
360
+ # @return [Array<Discorb::Command::Command::SlashCommand>] The subcommands of the command.
361
+ attr_reader :commands
362
+
363
+ # @!visibility private
364
+ def initialize(name, description, enabled, parent)
365
+ super(name, description, [], enabled, 1)
366
+
367
+ @commands = []
368
+ @parent = parent
369
+ end
370
+
371
+ def to_s
372
+ @parent + " " + @name
373
+ end
374
+
375
+ #
376
+ # Add new subcommand.
377
+ # @param (see Discorb::Command::Handler#slash)
378
+ # @return [Discorb::Command::Command::SlashCommand] The added subcommand.
379
+ #
380
+ def slash(command_name, description, options = {}, enabled: true, &block)
381
+ command = Discorb::Command::Command::SlashCommand.new(command_name, description, options, [], enabled, block, 1, @parent + " " + @name)
382
+ @commands << command
383
+ command
384
+ end
385
+ end
386
+
387
+ class << self
388
+ # @!visibility private
389
+ attr_reader :types
390
+ end
391
+ end
392
+ end
393
+ end