discorb 0.0.8 → 0.1.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.
@@ -0,0 +1,9 @@
1
+ require "discorb"
2
+
3
+ client = Discorb::Client.new
4
+
5
+ client.slash("hello", "Greet for you") do |interaction|
6
+ interaction.post("Hello!", ephemeral: true)
7
+ end
8
+
9
+ client.run(ENV["DISCORD_BOT_TOKEN"])
@@ -0,0 +1,24 @@
1
+ require "discorb"
2
+
3
+ client = Discorb::Client.new
4
+
5
+ client.once :ready do
6
+ puts "Logged in as #{client.user}"
7
+ end
8
+
9
+ client.user_command("Info", guild_ids: [857373681096327180]) do |interaction|
10
+ interaction.post(embed: Discorb::Embed.new(
11
+ "Information of #{interaction.target.to_s_user}",
12
+ fields: [
13
+ Discorb::Embed::Field.new("User", interaction.target.to_s_user),
14
+ Discorb::Embed::Field.new("ID", interaction.target.id),
15
+ Discorb::Embed::Field.new("Bot", interaction.target.bot? ? "Yes" : "No"),
16
+ Discorb::Embed::Field.new("Joined at", interaction.target.joined_at.to_df("F")),
17
+ Discorb::Embed::Field.new("Created at", interaction.target.created_at.to_df("F")),
18
+ ],
19
+ thumbnail: interaction.target.avatar&.url,
20
+
21
+ ), hide: true)
22
+ end
23
+
24
+ client.run(ENV["DISCORD_BOT_TOKEN"])
@@ -36,7 +36,7 @@ client.on :button_click do |response|
36
36
  if response.custom_id.start_with?("auth:")
37
37
  id = response.custom_id.delete_prefix("auth:")
38
38
  response.fired_by.add_role(id).wait
39
- response.post("You got your role!\nHere's your role: <@&#{id}>", hide: true)
39
+ response.post("You got your role!\nHere's your role: <@&#{id}>", ephemeral: true)
40
40
  end
41
41
  end
42
42
 
@@ -54,7 +54,7 @@ client.on :select_menu_select do |response|
54
54
  response.post(
55
55
  "**#{selected_section[0]}**\n" \
56
56
  "#{selected_section[1].strip}\n\n" \
57
- "#{WIKIPEDIA_CREDIT}", hide: true,
57
+ "#{WIKIPEDIA_CREDIT}", ephemeral: true,
58
58
  )
59
59
  end
60
60
 
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
@@ -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
  #
@@ -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