discorb 0.13.4 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.github/workflows/codeql-analysis.yml +70 -0
  4. data/.github/workflows/lint-push.yml +18 -0
  5. data/.github/workflows/lint.yml +16 -0
  6. data/.rubocop.yml +70 -0
  7. data/Changelog.md +12 -0
  8. data/Gemfile +7 -3
  9. data/Rakefile +22 -22
  10. data/discorb.gemspec +1 -0
  11. data/examples/commands/bookmarker.rb +2 -1
  12. data/examples/commands/hello.rb +1 -0
  13. data/examples/commands/inspect.rb +3 -2
  14. data/examples/components/authorization_button.rb +2 -1
  15. data/examples/components/select_menu.rb +2 -1
  16. data/examples/extension/main.rb +1 -0
  17. data/examples/extension/message_expander.rb +1 -0
  18. data/examples/simple/eval.rb +3 -2
  19. data/examples/simple/ping_pong.rb +1 -0
  20. data/examples/simple/rolepanel.rb +1 -0
  21. data/examples/simple/wait_for_message.rb +4 -3
  22. data/exe/discorb +8 -7
  23. data/lib/discorb/allowed_mentions.rb +64 -0
  24. data/lib/discorb/app_command/command.rb +274 -0
  25. data/lib/discorb/app_command/handler.rb +168 -0
  26. data/lib/discorb/app_command.rb +2 -426
  27. data/lib/discorb/asset.rb +2 -0
  28. data/lib/discorb/audit_logs.rb +3 -3
  29. data/lib/discorb/channel.rb +19 -4
  30. data/lib/discorb/client.rb +30 -27
  31. data/lib/discorb/common.rb +4 -26
  32. data/lib/discorb/components/button.rb +106 -0
  33. data/lib/discorb/components/select_menu.rb +157 -0
  34. data/lib/discorb/components/text_input.rb +96 -0
  35. data/lib/discorb/components.rb +11 -276
  36. data/lib/discorb/dictionary.rb +3 -0
  37. data/lib/discorb/embed.rb +2 -2
  38. data/lib/discorb/emoji.rb +19 -3
  39. data/lib/discorb/emoji_table.rb +1 -1
  40. data/lib/discorb/error.rb +4 -6
  41. data/lib/discorb/event.rb +9 -7
  42. data/lib/discorb/exe/about.rb +1 -0
  43. data/lib/discorb/exe/irb.rb +4 -3
  44. data/lib/discorb/exe/new.rb +6 -7
  45. data/lib/discorb/exe/run.rb +2 -1
  46. data/lib/discorb/exe/setup.rb +8 -5
  47. data/lib/discorb/exe/show.rb +1 -0
  48. data/lib/discorb/extend.rb +19 -14
  49. data/lib/discorb/extension.rb +5 -1
  50. data/lib/discorb/gateway.rb +28 -30
  51. data/lib/discorb/guild.rb +11 -13
  52. data/lib/discorb/guild_template.rb +2 -2
  53. data/lib/discorb/http.rb +15 -17
  54. data/lib/discorb/integration.rb +1 -1
  55. data/lib/discorb/intents.rb +1 -1
  56. data/lib/discorb/interaction/autocomplete.rb +4 -3
  57. data/lib/discorb/interaction/command.rb +34 -9
  58. data/lib/discorb/interaction/components.rb +5 -2
  59. data/lib/discorb/interaction/modal.rb +33 -0
  60. data/lib/discorb/interaction/response.rb +33 -4
  61. data/lib/discorb/interaction/root.rb +1 -0
  62. data/lib/discorb/interaction.rb +2 -1
  63. data/lib/discorb/log.rb +1 -1
  64. data/lib/discorb/member.rb +1 -3
  65. data/lib/discorb/message.rb +26 -277
  66. data/lib/discorb/message_meta.rb +205 -0
  67. data/lib/discorb/modules.rb +1 -1
  68. data/lib/discorb/permission.rb +2 -2
  69. data/lib/discorb/presence.rb +4 -1
  70. data/lib/discorb/rate_limit.rb +2 -4
  71. data/lib/discorb/user.rb +1 -1
  72. data/lib/discorb/utils/colored_puts.rb +1 -0
  73. data/lib/discorb/voice_state.rb +3 -0
  74. data/lib/discorb/webhook.rb +1 -1
  75. data/lib/discorb.rb +1 -0
  76. data/template-replace/scripts/arrow.rb +1 -0
  77. data/template-replace/scripts/favicon.rb +1 -0
  78. data/template-replace/scripts/index.rb +2 -1
  79. data/template-replace/scripts/locale_ja.rb +5 -4
  80. data/template-replace/scripts/sidebar.rb +1 -0
  81. data/template-replace/scripts/version.rb +7 -10
  82. data/template-replace/scripts/yard_replace.rb +5 -4
  83. metadata +16 -2
@@ -122,7 +122,7 @@ module Discorb
122
122
  # @return [Discorb::User] The user associated with the integration.
123
123
 
124
124
  # @private
125
- def initialize(client, data)
125
+ def initialize(_client, data)
126
126
  @id = Snowflake.new(data[:id])
127
127
  @guild_id = data[:guild_id]
128
128
  @user_id = data[:application_id]
@@ -261,7 +261,6 @@ module Discorb
261
261
  #
262
262
  # Represents a `MESSAGE_UPDATE` event.
263
263
  #
264
-
265
264
  class MessageUpdateEvent < GatewayEvent
266
265
  # @return [Discorb::Message] The message before update.
267
266
  attr_reader :before
@@ -393,6 +392,9 @@ module Discorb
393
392
  end
394
393
  end
395
394
 
395
+ #
396
+ # Represents a `GUILD_INTEGRATIONS_UPDATE` event.
397
+ #
396
398
  class GuildIntegrationsUpdateEvent < GatewayEvent
397
399
  def initialize(client, data)
398
400
  @client = client
@@ -528,20 +530,16 @@ module Discorb
528
530
  module Handler
529
531
  private
530
532
 
531
- def connect_gateway(reconnect, force_close: false)
533
+ def connect_gateway(reconnect)
532
534
  if reconnect
533
535
  @log.info "Reconnecting to gateway..."
534
536
  else
535
537
  @log.info "Connecting to gateway..."
536
538
  end
537
539
  Async do
538
- if @connection
540
+ if @connection && !@connection.closed?
539
541
  Async do
540
- if force_close
541
- @connection.force_close
542
- else
543
- @connection.close
544
- end
542
+ @connection.close
545
543
  end
546
544
  end
547
545
  @http = HTTP.new(self)
@@ -578,7 +576,8 @@ module Discorb
578
576
  Errno::ECONNRESET,
579
577
  IOError => e
580
578
  @log.error "Gateway connection closed accidentally: #{e.class}: #{e.message}"
581
- connect_gateway(true, force_close: true)
579
+ @connection.force_close
580
+ connect_gateway(true)
582
581
  else # should never happen
583
582
  connect_gateway(true)
584
583
  end
@@ -593,13 +592,13 @@ module Discorb
593
592
  when 4014
594
593
  raise ClientError.new("Disallowed intents were specified"), cause: nil
595
594
  when 4002, 4003, 4005, 4007
596
- raise ClientError.new(<<~EOS), cause: e
597
- Disconnected from gateway, probably due to library issues.
598
- #{e.message}
595
+ raise ClientError.new(<<~ERROR), cause: e
596
+ Disconnected from gateway, probably due to library issues.
597
+ #{e.message}
599
598
 
600
- Please report this to the library issue tracker.
601
- https://github.com/discorb-lib/discorb/issues
602
- EOS
599
+ Please report this to the library issue tracker.
600
+ https://github.com/discorb-lib/discorb/issues
601
+ ERROR
603
602
  when 1001
604
603
  @log.info "Gateway closed with code 1001, reconnecting."
605
604
  connect_gateway(true)
@@ -608,8 +607,9 @@ module Discorb
608
607
  @log.debug "#{e.message}"
609
608
  connect_gateway(false)
610
609
  end
611
- rescue => e
610
+ rescue StandardError => e
612
611
  @log.error "Discord WebSocket error: #{e.full_message}"
612
+ @connection.force_close
613
613
  connect_gateway(false)
614
614
  end
615
615
  end
@@ -622,7 +622,7 @@ module Discorb
622
622
  end
623
623
 
624
624
  def handle_gateway(payload, reconnect)
625
- Async do |task|
625
+ Async do |_task|
626
626
  data = payload[:d]
627
627
  @last_s = payload[:s] if payload[:s]
628
628
  @log.debug "Received message with opcode #{payload[:op]} from gateway:"
@@ -648,7 +648,7 @@ module Discorb
648
648
  send_gateway(2, **payload)
649
649
  end
650
650
  when 7
651
- @log.info "Received opcode 7, reconnecting"
651
+ @log.info "Received opcode 7, stopping tasks"
652
652
  @tasks.map(&:stop)
653
653
  when 9
654
654
  @log.warn "Received opcode 9, closed connection"
@@ -673,7 +673,7 @@ module Discorb
673
673
  end
674
674
 
675
675
  def handle_heartbeat
676
- Async do |task|
676
+ Async do |_task|
677
677
  interval = @heartbeat_interval
678
678
  sleep((interval / 1000.0 - 1) * rand)
679
679
  loop do
@@ -700,9 +700,7 @@ module Discorb
700
700
  @session_id = data[:session_id]
701
701
  @user = ClientUser.new(self, data[:user])
702
702
  @uncached_guilds = data[:guilds].map { |g| g[:id] }
703
- if @uncached_guilds == [] or !@intents.guilds
704
- ready
705
- end
703
+ ready if (@uncached_guilds == []) || !@intents.guilds
706
704
  dispatch(:ready)
707
705
  @tasks << handle_heartbeat
708
706
  when "GUILD_CREATE"
@@ -905,7 +903,7 @@ module Discorb
905
903
  when "INTEGRATION_UPDATE"
906
904
  return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
907
905
 
908
- before = Integration.new(self, data, data[:guild_id])
906
+ integration = Integration.new(self, data, data[:guild_id])
909
907
  dispatch(:integration_update, integration)
910
908
  when "INTEGRATION_DELETE"
911
909
  return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
@@ -1096,7 +1094,7 @@ module Discorb
1096
1094
  dispatch(:typing_start, TypingStartEvent.new(self, data))
1097
1095
  when "INTERACTION_CREATE"
1098
1096
  interaction = Interaction.make_interaction(self, data)
1099
- dispatch(:integration_create, interaction)
1097
+ dispatch(:interaction_create, interaction)
1100
1098
 
1101
1099
  dispatch(interaction.class.event_name, interaction)
1102
1100
  when "RESUMED"
@@ -1114,15 +1112,15 @@ module Discorb
1114
1112
  old = event.dup
1115
1113
  event.send(:_set_data, data)
1116
1114
  dispatch(:scheduled_event_update, old, event)
1117
- if old.status != event.status
1115
+ if old.status == event.status
1116
+ dispatch(:scheduled_event_edit, old, event)
1117
+ else
1118
1118
  case event.status
1119
1119
  when :active
1120
1120
  dispatch(:scheduled_event_start, event)
1121
1121
  when :completed
1122
1122
  dispatch(:scheduled_event_end, event)
1123
1123
  end
1124
- else
1125
- dispatch(:scheduled_event_edit, old, event)
1126
1124
  end
1127
1125
  when "GUILD_SCHEDULED_EVENT_DELETE"
1128
1126
  @log.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
@@ -1182,12 +1180,12 @@ module Discorb
1182
1180
  def close
1183
1181
  super
1184
1182
  @closed = true
1185
- rescue
1183
+ rescue StandardError
1186
1184
  force_close
1187
1185
  end
1188
1186
 
1189
1187
  def force_close
1190
- @framer.instance_variable_get(:@stream).close
1188
+ @framer.instance_variable_get(:@stream).instance_variable_get(:@io).instance_variable_get(:@io).instance_variable_get(:@io).close
1191
1189
  @closed = true
1192
1190
  end
1193
1191
 
data/lib/discorb/guild.rb CHANGED
@@ -614,7 +614,7 @@ module Discorb
614
614
  #
615
615
  def fetch_members(limit: 0, after: nil)
616
616
  Async do
617
- unless limit == 0
617
+ unless limit.zero?
618
618
  _resp, data = @client.http.request(Route.new("/guilds/#{@id}/members?#{URI.encode_www_form({ after: after, limit: limit })}", "//guilds/:guild_id/members", :get)).wait
619
619
  next data[:members].map { |m| Member.new(@client, @id, m[:user], m) }
620
620
  end
@@ -625,9 +625,7 @@ module Discorb
625
625
  _resp, data = @client.http.request(Route.new("/guilds/#{@id}/members?#{URI.encode_www_form(params)}", "//guilds/:guild_id/members", :get)).wait
626
626
  ret += data.map { |m| Member.new(@client, @id, m[:user], m) }
627
627
  after = data.last[:user][:id]
628
- if data.length != 1000
629
- break
630
- end
628
+ break if data.length != 1000
631
629
  end
632
630
  ret
633
631
  end
@@ -1099,7 +1097,7 @@ module Discorb
1099
1097
  def iframe(theme: "dark", width: 350, height: 500)
1100
1098
  [
1101
1099
  %(<iframe src="https://canary.discord.com/widget?id=#{@guild_id}&theme=#{theme}" width="#{width}" height="#{height}"),
1102
- %(allowtransparency="true" frameborder="0" sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"></iframe>),
1100
+ %(allowtransparency="true" frameborder="0" sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"></iframe>)
1103
1101
  ].join
1104
1102
  end
1105
1103
  end
@@ -1152,9 +1150,9 @@ module Discorb
1152
1150
  @unavailable = false
1153
1151
  @name = data[:name]
1154
1152
  @members = Discorb::Dictionary.new
1155
- data[:members].each do |m|
1156
- Member.new(@client, @id, m[:user], m)
1157
- end if data[:members]
1153
+ data[:members]&.each do |m|
1154
+ Member.new(@client, @id, m[:user], m)
1155
+ end
1158
1156
  @splash = data[:splash] && Asset.new(self, data[:splash], path: "splashes/#{@id}")
1159
1157
  @discovery_splash = data[:discovery_splash] && Asset.new(self, data[:discovery_splash], path: "discovery-splashes/#{@id}")
1160
1158
  @owner_id = data[:owner_id]
@@ -1200,13 +1198,13 @@ module Discorb
1200
1198
  tmp_channels = data[:channels].filter { |c| !c.key?(:thread_metadata) }.map do |c|
1201
1199
  Channel.make_channel(@client, c.merge({ guild_id: @id }))
1202
1200
  end
1203
- @channels = Dictionary.new(tmp_channels.map { |c| [c.id, c] }.to_h, sort: ->(c) { c[1].position })
1204
- @voice_states = Dictionary.new(data[:voice_states].map { |v| [Snowflake.new(v[:user_id]), VoiceState.new(@client, v.merge({ guild_id: @id }))] }.to_h)
1201
+ @channels = Dictionary.new(tmp_channels.to_h { |c| [c.id, c] }, sort: ->(c) { c[1].position })
1202
+ @voice_states = Dictionary.new(data[:voice_states].to_h { |v| [Snowflake.new(v[:user_id]), VoiceState.new(@client, v.merge({ guild_id: @id }))] })
1205
1203
  @threads = data[:threads] ? data[:threads].map { |t| Channel.make_channel(@client, t) } : []
1206
- @presences = Dictionary.new(data[:presences].map { |pr| [Snowflake.new(pr[:user][:id]), Presence.new(@client, pr)] }.to_h)
1204
+ @presences = Dictionary.new(data[:presences].to_h { |pr| [Snowflake.new(pr[:user][:id]), Presence.new(@client, pr)] })
1207
1205
  @max_presences = data[:max_presences]
1208
- @stage_instances = Dictionary.new(data[:stage_instances].map { |s| [Snowflake.new(s[:id]), StageInstance.new(@client, s)] }.to_h)
1209
- @scheduled_events = Dictionary.new(data[:guild_scheduled_events].map { |s| [Snowflake.new(s[:id]), ScheduledEvent.new(@client, s)] }.to_h)
1206
+ @stage_instances = Dictionary.new(data[:stage_instances].to_h { |s| [Snowflake.new(s[:id]), StageInstance.new(@client, s)] })
1207
+ @scheduled_events = Dictionary.new(data[:guild_scheduled_events].to_h { |s| [Snowflake.new(s[:id]), ScheduledEvent.new(@client, s)] })
1210
1208
  @data.update(data)
1211
1209
  end
1212
1210
  end
@@ -187,9 +187,9 @@ module Discorb
187
187
  @nsfw = data[:nsfw]
188
188
  @rate_limit_per_user = data[:rate_limit_per_user]
189
189
  @parent_id = data[:parent_id]
190
- @permission_overwrites = data[:permission_overwrites].map do |ow|
190
+ @permission_overwrites = data[:permission_overwrites].to_h do |ow|
191
191
  [Snowflake.new(ow[:id]), PermissionOverwrite.new(ow[:allow], ow[:deny])]
192
- end.to_h
192
+ end
193
193
  @type = Discorb::Channel.descendants.find { |c| c.channel_type == data[:type] }
194
194
  end
195
195
  end
data/lib/discorb/http.rb CHANGED
@@ -32,13 +32,13 @@ module Discorb
32
32
  # @raise [Discorb::HTTPError] The request was failed.
33
33
  #
34
34
  def request(path, body = "", headers: nil, audit_log_reason: nil, **kwargs)
35
- Async do |task|
35
+ Async do |_task|
36
36
  @ratelimit_handler.wait(path)
37
- if %i[post patch put].include? path.method
38
- resp = http.send(path.method, get_path(path), get_body(body), get_headers(headers, body, audit_log_reason), **kwargs)
39
- else
40
- resp = http.send(path.method, get_path(path), get_headers(headers, body, audit_log_reason), **kwargs)
41
- end
37
+ resp = if %i[post patch put].include? path.method
38
+ http.send(path.method, get_path(path), get_body(body), get_headers(headers, body, audit_log_reason), **kwargs)
39
+ else
40
+ http.send(path.method, get_path(path), get_headers(headers, body, audit_log_reason), **kwargs)
41
+ end
42
42
  data = get_response_data(resp)
43
43
  @ratelimit_handler.save(path, resp)
44
44
  handle_response(resp, data, path, body, headers, audit_log_reason, kwargs)
@@ -61,12 +61,12 @@ module Discorb
61
61
  #
62
62
  # @raise [Discorb::HTTPError] The request was failed.
63
63
  #
64
- def multipart_request(path, body = "", files, headers: nil, audit_log_reason: nil, **kwargs)
65
- Async do |task|
64
+ def multipart_request(path, body, files, headers: nil, audit_log_reason: nil, **kwargs)
65
+ Async do |_task|
66
66
  @ratelimit_handler.wait(path)
67
67
  req = Net::HTTP.const_get(path.method.to_s.capitalize).new(get_path(path), get_headers(headers, body, audit_log_reason), **kwargs)
68
68
  data = [
69
- ["payload_json", get_body(body)],
69
+ ["payload_json", get_body(body)]
70
70
  ]
71
71
  files&.each_with_index do |file, i|
72
72
  next if file.nil?
@@ -143,15 +143,13 @@ module Discorb
143
143
  begin
144
144
  data = JSON.parse(resp.body, symbolize_names: true)
145
145
  rescue JSON::ParserError, TypeError
146
- if resp.body.nil? || resp.body.empty?
147
- data = nil
148
- else
149
- data = resp.body
150
- end
151
- end
152
- if resp["Via"].nil? && resp.code == "429" && data.is_a?(String)
153
- raise CloudFlareBanError.new(resp, @client)
146
+ data = if resp.body.nil? || resp.body.empty?
147
+ nil
148
+ else
149
+ resp.body
150
+ end
154
151
  end
152
+ raise CloudFlareBanError.new(resp, @client) if resp["Via"].nil? && resp.code == "429" && data.is_a?(String)
155
153
  data
156
154
  end
157
155
 
@@ -44,7 +44,7 @@ module Discorb
44
44
  }
45
45
 
46
46
  # @private
47
- def initialize(client, data, guild_id, no_cache: false)
47
+ def initialize(client, data, guild_id)
48
48
  @client = client
49
49
  @data = data
50
50
  @guild_id = guild_id
@@ -86,7 +86,7 @@ module Discorb
86
86
  if @raw_value.key?(name)
87
87
  @raw_value[name]
88
88
  elsif name.end_with?("=") && @raw_value.key?(name[0..-2].to_sym)
89
- raise ArgumentError, "true/false expected" unless args.is_a? TrueClass or args.is_a?(FalseClass)
89
+ raise ArgumentError, "true/false expected" unless args.is_a?(TrueClass) || args.is_a?(FalseClass)
90
90
 
91
91
  @raw_value[name[0..-2].to_sym] = args
92
92
  else
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Discorb
2
3
  #
3
4
  # Represents auto complete interaction.
@@ -17,10 +18,10 @@ module Discorb
17
18
  next
18
19
  end
19
20
 
20
- option_map = command.options.map { |k, v| [k.to_s, v[:default]] }.to_h
21
- Discorb::CommandInteraction::SlashCommand.modify_option_map(option_map, options, guild)
21
+ option_map = command.options.to_h { |k, v| [k.to_s, v[:default]] }
22
+ Discorb::CommandInteraction::SlashCommand.modify_option_map(option_map, options, guild, {}, {})
22
23
  focused_index = options.find_index { |o| o[:focused] }
23
- val = command.options.values[focused_index][:autocomplete]&.call(self, *command.options.map { |k, v| option_map[k.to_s] })
24
+ val = command.options.values.filter { |option| option[:type] != :attachment }[focused_index][:autocomplete]&.call(self, *command.options.map { |k, _v| option_map[k.to_s] })
24
25
  send_complete_result(val)
25
26
  end
26
27
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Discorb
2
3
  #
3
4
  # Represents a command interaction.
@@ -5,7 +6,8 @@ module Discorb
5
6
  class CommandInteraction < Interaction
6
7
  @interaction_type = 2
7
8
  @interaction_name = :application_command
8
- include Interaction::SourceResponse
9
+ include Interaction::SourceResponder
10
+ include Interaction::ModalResponder
9
11
 
10
12
  #
11
13
  # Represents a slash command interaction.
@@ -25,10 +27,10 @@ module Discorb
25
27
  return
26
28
  end
27
29
 
28
- option_map = command.options.map { |k, v| [k.to_s, v[:default]] }.to_h
29
- SlashCommand.modify_option_map(option_map, options, guild)
30
+ option_map = command.options.to_h { |k, v| [k.to_s, v[:default]] }
31
+ SlashCommand.modify_option_map(option_map, options, guild, @members, @attachments)
30
32
 
31
- command.block.call(self, *command.options.map { |k, v| option_map[k.to_s] })
33
+ command.block.call(self, *command.options.map { |k, _v| option_map[k.to_s] })
32
34
  end
33
35
 
34
36
  class << self
@@ -55,13 +57,13 @@ module Discorb
55
57
  options = data[:options]
56
58
  end
57
59
 
58
- return name, options
60
+ [name, options]
59
61
  end
60
62
 
61
63
  # @private
62
- def modify_option_map(option_map, options, guild)
64
+ def modify_option_map(option_map, options, guild, members, attachments)
63
65
  options ||= []
64
- options.each_with_index do |option|
66
+ options.each do |option|
65
67
  val = case option[:type]
66
68
  when 3, 4, 5, 10
67
69
  option[:value]
@@ -72,7 +74,9 @@ module Discorb
72
74
  when 8
73
75
  guild.roles[option[:value]] || guild.fetch_roles.wait.find { |role| role.id == option[:value] }
74
76
  when 9
75
- guild.members[option[:value]] || guild.roles[option[:value]] || guild.fetch_member(option[:value]).wait || guild.fetch_roles.wait.find { |role| role.id == option[:value] }
77
+ members[option[:value]] || guild.members[option[:value]] || guild.roles[option[:value]] || guild.fetch_member(option[:value]).wait || guild.fetch_roles.wait.find { |role| role.id == option[:value] }
78
+ when 11
79
+ attachments[option[:value]]
76
80
  end
77
81
  option_map[option[:name]] = val
78
82
  end
@@ -92,6 +96,7 @@ module Discorb
92
96
  private
93
97
 
94
98
  def _set_data(data)
99
+ super
95
100
  @target = guild.members[data[:target_id]] || Discorb::Member.new(@client, @guild_id, data[:resolved][:users][data[:target_id].to_sym], data[:resolved][:members][data[:target_id].to_sym])
96
101
  @client.commands.find { |c| c.name == data[:name] && c.type_raw == 2 }.block.call(self, @target)
97
102
  end
@@ -109,7 +114,8 @@ module Discorb
109
114
  private
110
115
 
111
116
  def _set_data(data)
112
- @target = Message.new(@client, data[:resolved][:messages][data[:target_id].to_sym].merge(guild_id: @guild_id.to_s))
117
+ super
118
+ @target = @messages[data[:target_id]]
113
119
  @client.commands.find { |c| c.name == data[:name] && c.type_raw == 3 }.block.call(self, @target)
114
120
  end
115
121
  end
@@ -117,7 +123,26 @@ module Discorb
117
123
  private
118
124
 
119
125
  def _set_data(data)
126
+ super
120
127
  @name = data[:name]
128
+ @messages, @attachments, @members = {}, {}, {}
129
+
130
+ if data[:resolved]
131
+ data[:resolved][:users]&.each do |id, user|
132
+ @client.users[id] = Discorb::User.new(@client, user)
133
+ end
134
+ data[:resolved][:members]&.each do |id, member|
135
+ @members[id] = Discorb::Member.new(
136
+ @client, @guild_id, data[:resolved][:users][id], member
137
+ )
138
+ end
139
+ data[:resolved][:messages]&.to_h do |id, _message|
140
+ @messages[id.to_i] = Message.new(@client, data[:resolved][:messages][data[:target_id].to_sym].merge(guild_id: @guild_id.to_s)).merge(guild_id: @guild_id.to_s)
141
+ end
142
+ data[:resolved][:attachments]&.to_h do |id, attachment|
143
+ @attachments[id.to_s] = Attachment.new(attachment)
144
+ end
145
+ end
121
146
  end
122
147
 
123
148
  class << self
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Discorb
2
3
 
3
4
  #
@@ -5,8 +6,10 @@ module Discorb
5
6
  # @abstract
6
7
  #
7
8
  class MessageComponentInteraction < Interaction
8
- include Interaction::SourceResponse
9
- include Interaction::UpdateResponse
9
+ include Interaction::SourceResponder
10
+ include Interaction::UpdateResponder
11
+ include Interaction::ModalResponder
12
+
10
13
  # @return [String] The content of the response.
11
14
  attr_reader :custom_id
12
15
  # @return [Discorb::Message] The target message.
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+
5
+ #
6
+ # Represents a modal interaction.
7
+ #
8
+ class ModalInteraction < Interaction
9
+ include Interaction::SourceResponder
10
+
11
+ @interaction_type = 5
12
+ @interaction_name = :modal_submit
13
+ @event_name = :modal_submit
14
+
15
+ # @return [String] The custom id of the modal.
16
+ attr_reader :custom_id
17
+ # @return [{String => String}] The contents of the modal.
18
+ attr_reader :contents
19
+
20
+ private
21
+
22
+ def _set_data(data)
23
+ @custom_id = data[:custom_id]
24
+ @contents = data[:components].to_h do |component|
25
+ [component[:components][0][:custom_id], component[:components][0][:value]]
26
+ end
27
+ end
28
+
29
+ class << self
30
+ alias make_interaction new
31
+ end
32
+ end
33
+ end
@@ -1,9 +1,13 @@
1
+ # frozen_string_literal: true
1
2
  module Discorb
3
+ #
4
+ # Represents an interaction of Discord.
5
+ #
2
6
  class Interaction
3
7
  #
4
8
  # A module for response with source.
5
9
  #
6
- module SourceResponse
10
+ module SourceResponder
7
11
  #
8
12
  # Response with `DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE`(`5`).
9
13
  #
@@ -38,14 +42,14 @@ module Discorb
38
42
  # @param [Array<Discorb::Component>, Array<Array<Discorb::Component>>] components The components to send.
39
43
  # @param [Boolean] ephemeral Whether to make the response ephemeral.
40
44
  #
41
- # @return [Discorb::Interaction::SourceResponse::CallbackMessage, Discorb::Webhook::Message] The callback message.
45
+ # @return [Discorb::Interaction::SourceResponder::CallbackMessage, Discorb::Webhook::Message] The callback message.
42
46
  #
43
47
  def post(content = nil, tts: false, embed: nil, embeds: nil, allowed_mentions: nil, components: nil, ephemeral: false)
44
48
  Async do
45
49
  payload = {}
46
50
  payload[:content] = content if content
47
51
  payload[:tts] = tts
48
- payload[:embeds] = (embeds || [embed])&.map { |e| e&.to_hash }.filter { _1 }
52
+ payload[:embeds] = (embeds || [embed]).map { |e| e&.to_hash }.filter { _1 }
49
53
  payload[:allowed_mentions] = allowed_mentions&.to_hash(@client.allowed_mentions) || @client.allowed_mentions.to_hash
50
54
  payload[:components] = Component.to_payload(components) if components
51
55
  payload[:flags] = (ephemeral ? 1 << 6 : 0)
@@ -66,6 +70,9 @@ module Discorb
66
70
  end
67
71
  end
68
72
 
73
+ #
74
+ # Represents of a callback message of interaction.
75
+ #
69
76
  class CallbackMessage
70
77
  # @private
71
78
  def initialize(client, data, application_id, token)
@@ -127,7 +134,7 @@ module Discorb
127
134
  #
128
135
  # A module for response with update.
129
136
  #
130
- module UpdateResponse
137
+ module UpdateResponder
131
138
  #
132
139
  # Response with `DEFERRED_UPDATE_MESSAGE`(`6`).
133
140
  # @async
@@ -181,6 +188,28 @@ module Discorb
181
188
  end
182
189
  end
183
190
 
191
+ #
192
+ # A module for response with modal.
193
+ #
194
+ module ModalResponder
195
+ #
196
+ # Response with `MODAL`(`9`).
197
+ #
198
+ # @param [String] title The title of the modal.
199
+ # @param [String] custom_id The custom id of the modal.
200
+ # @param [Array<Discorb::TextInput>] components The text inputs to send.
201
+ #
202
+ # @return [Async::Task<void>] The task.
203
+ #
204
+ def show_modal(title, custom_id, components)
205
+ payload = { title: title, custom_id: custom_id, components: Component.to_payload(components) }
206
+ @client.http.request(
207
+ Route.new("/interactions/#{@id}/#{@token}/callback", "//interactions/:interaction_id/:token/callback", :post),
208
+ { type: 9, data: payload }
209
+ ).wait
210
+ end
211
+ end
212
+
184
213
  private
185
214
 
186
215
  def _set_data(*)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Discorb
2
3
  #
3
4
  # Represents a user interaction with the bot.
@@ -1,3 +1,4 @@
1
- %w[root response command components autocomplete].each do |file|
1
+ # frozen_string_literal: true
2
+ %w[root response command components autocomplete modal].each do |file|
2
3
  require_relative "interaction/#{file}.rb"
3
4
  end
data/lib/discorb/log.rb CHANGED
@@ -64,7 +64,7 @@ module Discorb
64
64
 
65
65
  def write_output(name, color, message, fallback)
66
66
  unless @out
67
- fallback.puts(message) if fallback
67
+ fallback&.puts(message)
68
68
 
69
69
  return
70
70
  end
@@ -105,9 +105,7 @@ module Discorb
105
105
  end
106
106
 
107
107
  def permissions
108
- if owner?
109
- return Permission.new((1 << 38) - 1)
110
- end
108
+ return Permission.new((1 << 38) - 1) if owner?
111
109
  roles.map(&:permissions).sum(Permission.new(0))
112
110
  end
113
111