discorb 0.0.1

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.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +56 -0
  3. data/.yardopts +6 -0
  4. data/Changelog.md +5 -0
  5. data/Gemfile +23 -0
  6. data/Gemfile.lock +70 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +53 -0
  9. data/Rakefile +46 -0
  10. data/bin/console +15 -0
  11. data/bin/setup +8 -0
  12. data/discorb.gemspec +37 -0
  13. data/docs/Examples.md +26 -0
  14. data/docs/events.md +480 -0
  15. data/docs/voice_events.md +283 -0
  16. data/examples/components/authorization_button.rb +43 -0
  17. data/examples/components/select_menu.rb +61 -0
  18. data/examples/extension/main.rb +12 -0
  19. data/examples/extension/message_expander.rb +41 -0
  20. data/examples/simple/eval.rb +32 -0
  21. data/examples/simple/ping_pong.rb +16 -0
  22. data/examples/simple/rolepanel.rb +65 -0
  23. data/examples/simple/wait_for_message.rb +30 -0
  24. data/lib/discorb/application.rb +157 -0
  25. data/lib/discorb/asset.rb +57 -0
  26. data/lib/discorb/audit_logs.rb +323 -0
  27. data/lib/discorb/channel.rb +1101 -0
  28. data/lib/discorb/client.rb +363 -0
  29. data/lib/discorb/color.rb +173 -0
  30. data/lib/discorb/common.rb +123 -0
  31. data/lib/discorb/components.rb +290 -0
  32. data/lib/discorb/dictionary.rb +119 -0
  33. data/lib/discorb/embed.rb +345 -0
  34. data/lib/discorb/emoji.rb +218 -0
  35. data/lib/discorb/emoji_table.rb +3799 -0
  36. data/lib/discorb/error.rb +98 -0
  37. data/lib/discorb/event.rb +35 -0
  38. data/lib/discorb/extend.rb +18 -0
  39. data/lib/discorb/extension.rb +54 -0
  40. data/lib/discorb/file.rb +69 -0
  41. data/lib/discorb/flag.rb +109 -0
  42. data/lib/discorb/gateway.rb +967 -0
  43. data/lib/discorb/gateway_requests.rb +47 -0
  44. data/lib/discorb/guild.rb +1244 -0
  45. data/lib/discorb/guild_template.rb +211 -0
  46. data/lib/discorb/image.rb +43 -0
  47. data/lib/discorb/integration.rb +111 -0
  48. data/lib/discorb/intents.rb +137 -0
  49. data/lib/discorb/interaction.rb +333 -0
  50. data/lib/discorb/internet.rb +285 -0
  51. data/lib/discorb/invite.rb +145 -0
  52. data/lib/discorb/log.rb +70 -0
  53. data/lib/discorb/member.rb +232 -0
  54. data/lib/discorb/message.rb +583 -0
  55. data/lib/discorb/modules.rb +138 -0
  56. data/lib/discorb/permission.rb +270 -0
  57. data/lib/discorb/presence.rb +308 -0
  58. data/lib/discorb/reaction.rb +48 -0
  59. data/lib/discorb/role.rb +189 -0
  60. data/lib/discorb/sticker.rb +157 -0
  61. data/lib/discorb/user.rb +163 -0
  62. data/lib/discorb/utils.rb +16 -0
  63. data/lib/discorb/voice_state.rb +251 -0
  64. data/lib/discorb/webhook.rb +420 -0
  65. data/lib/discorb.rb +51 -0
  66. metadata +120 -0
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ #
5
+ # Represents invite of discord.
6
+ #
7
+ class Invite < DiscordModel
8
+ # @return [String] The code of invite.
9
+ attr_reader :code
10
+
11
+ # @return [:voice, :stream, :guild] The type of invite.
12
+ attr_reader :target_type
13
+
14
+ # @return [User] The user of invite.
15
+ attr_reader :target_user
16
+
17
+ # @return [Integer] The approximate number of online users of invite.
18
+ attr_reader :approximate_presence_count
19
+
20
+ # @return [Integer] The approximate number of members of invite.
21
+ attr_reader :approximate_member_count
22
+
23
+ # @return [Time] The time when invite expires.
24
+ # @return [nil] The invite never expires.
25
+ # @macro [new] nometa
26
+ # @return [nil] The invite doesn't have metadata.
27
+ attr_reader :expires_at
28
+
29
+ # @return [Integer] The number of uses of invite.
30
+ # @macro nometa
31
+ attr_reader :uses
32
+
33
+ # @return [Integer] The maximum number of uses of invite.
34
+ # @macro nometa
35
+ attr_reader :max_uses
36
+
37
+ # @return [Integer] Duration of invite in seconds.
38
+ # @macro nometa
39
+ attr_reader :max_age
40
+
41
+ # @return [Time] The time when invite was created.
42
+ # @macro nometa
43
+ attr_reader :created_at
44
+
45
+ # @!attribute [r] channel
46
+ # Channel of the invite.
47
+ #
48
+ # @return [Discorb::Channel] Channel of invite.
49
+ # @macro client_cache
50
+ #
51
+ # @!attribute [r] guild
52
+ # Guild of the invite.
53
+ #
54
+ # @return [Discorb::Guild] Guild of invite.
55
+ # @macro client_cache
56
+ #
57
+ # @!attribute [r] remain_uses
58
+ # Number of remaining uses of invite.
59
+ # @return [Integer] Number of remaining uses of invite.
60
+ #
61
+ # @!attribute [r] url
62
+ # Full url of invite.
63
+ # @return [String] Full url of invite.
64
+ #
65
+ # @!attribute [r] temporary?
66
+ # Whether the invite is temporary.
67
+ # @return [Boolean]
68
+
69
+ @target_types = {
70
+ nil => :voice,
71
+ 1 => :stream,
72
+ 2 => :guild,
73
+ }.freeze
74
+
75
+ # @!visibility private
76
+ def initialize(client, data, gateway)
77
+ @client = client
78
+ @data = data[:data]
79
+ _set_data(data, gateway)
80
+ end
81
+
82
+ def channel
83
+ @client.channels[@channel_data[:id]]
84
+ end
85
+
86
+ def guild
87
+ @client.guilds[@guild_data[:id]]
88
+ end
89
+
90
+ def url
91
+ "https://discord.gg/#{@code}"
92
+ end
93
+
94
+ def remain_uses
95
+ @max_uses && @max_uses - @uses
96
+ end
97
+
98
+ def temporary?
99
+ @temporary
100
+ end
101
+
102
+ # Delete the invite.
103
+ # @macro async
104
+ # @macro http
105
+ def delete!(reason: nil)
106
+ Async do
107
+ @client.internet.delete("/invites/#{@code}", audit_log_reason: reason)
108
+ end
109
+ end
110
+
111
+ private
112
+
113
+ def _set_data(data, gateway)
114
+ @code = data[:code]
115
+ if gateway
116
+ @channel_data = { id: data[:channel_id] }
117
+ @guild_data = { id: data[:guild_id] }
118
+ else
119
+ @guild_data = data[:guild]
120
+ @channel_data = data[:channel]
121
+ @approximate_presence_count = data[:approximate_presence_count]
122
+ @approximate_member_count = data[:approximate_member_count]
123
+ @expires_at = data[:expires_at] && Time.iso8601(data[:expires_at])
124
+ end
125
+ @inviter_data = data[:inviter]
126
+ @target_type = self.class.target_types[data[:target_type]]
127
+ @target_user = @client.users[data[:target_user][:id]] || User.new(@client, data[:target_user]) if data[:target_user]
128
+ # @target_application = nil
129
+
130
+ # @stage_instance = data[:stage_instance] && Invite::StageInstance.new(self, data[:stage_instance])
131
+ return unless data.key?(:uses)
132
+
133
+ @uses = data[:uses]
134
+ @max_uses = data[:max_uses]
135
+ @max_age = data[:max_age]
136
+ @temporary = data[:temporary]
137
+ @created_at = Time.iso8601(data[:created_at])
138
+ end
139
+
140
+ class << self
141
+ # @!visibility private
142
+ attr_reader :target_types
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ # @!visibility private
5
+ class Logger
6
+ attr_reader :out, :colorize_log
7
+
8
+ @levels = %i[debug info warn error fatal].freeze
9
+
10
+ def initialize(out, colorize_log, level)
11
+ @out = out
12
+ @level = self.class.levels.index(level)
13
+ @colorize_log = colorize_log
14
+ end
15
+
16
+ def level
17
+ @levels[@level]
18
+ end
19
+
20
+ def level=(level)
21
+ @level = @levels.index(level)
22
+ end
23
+
24
+ def debug(message)
25
+ return unless @level <= 0
26
+
27
+ write_output("DEBUG", "\e[90m", message)
28
+ end
29
+
30
+ def info(message)
31
+ return unless @level <= 1
32
+
33
+ write_output("INFO", "\e[94m", message)
34
+ end
35
+
36
+ def warn(message)
37
+ return unless @level <= 2
38
+
39
+ write_output("WARN", "\e[93m", message)
40
+ end
41
+
42
+ def error(message)
43
+ return unless @level <= 3
44
+
45
+ write_output("ERROR", "\e[31m", message)
46
+ end
47
+
48
+ def fatal(message)
49
+ return unless @level <= 4
50
+
51
+ write_output("FATAL", "\e[91m", message)
52
+ end
53
+
54
+ class << self
55
+ attr_reader :levels
56
+ end
57
+
58
+ private
59
+
60
+ def write_output(name, color, message)
61
+ return unless @out
62
+
63
+ if @colorize_log
64
+ @out.puts("[#{Time.now.iso8601}] #{color}#{name}\e[m -- #{message}")
65
+ else
66
+ @out.puts("[#{Time.now.iso8601}] #{name} -- #{message}")
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ #
5
+ # Represents a member of a guild.
6
+ #
7
+ class Member < User
8
+ # @return [Time] The time the member boosted the guild.
9
+ attr_reader :premium_since
10
+ # @return [String] The nickname of the member.
11
+ # @return [nil] If the member has no nickname.
12
+ attr_reader :nick
13
+ # @return [Time] The time the member joined the guild.
14
+ attr_reader :joined_at
15
+ # @return [Discorb::Asset] The custom avatar of the member.
16
+ # @return [nil] If the member has no custom avatar.
17
+ attr_reader :custom_avatar
18
+ # @return [Discorb::Asset] The display avatar of the member.
19
+ attr_reader :display_avatar
20
+ # @return [Boolean] Whether the member is muted.
21
+ attr_reader :mute
22
+ alias mute? mute
23
+ # @return [Boolean] Whether the member is deafened.
24
+ attr_reader :deaf
25
+ alias deaf? deaf
26
+ # @return [Boolean] Whether the member is pending (Not passed member screening).
27
+ attr_reader :pending
28
+ alias pending? pending
29
+
30
+ # @!attribute [r] name
31
+ # @return [String] The display name of the member.
32
+ # @!attribute [r] mention
33
+ # @return [String] The mention of the member.
34
+ # @!attribute [r] voice_state
35
+ # @return [Discorb::VoiceState] The voice state of the member.
36
+ # @!attribute [r] roles
37
+ # @macro client_cache
38
+ # @return [Array<Discorb::Role>] The roles of the member.
39
+ # @!attribute [r] guild
40
+ # @macro client_cache
41
+ # @return [Discorb::Guild] The guild the member is in.
42
+ # @!attribute [r] hoisted_role
43
+ # @macro client_cache
44
+ # @return [Discorb::Role] The hoisted role of the member.
45
+ # @return [nil] If the member has no hoisted role.
46
+ # @!attribute [r] hoisted?
47
+ # @return [Boolean] Whether the member has a hoisted role.
48
+ # @!attribute [r] permissions
49
+ # @return [Discorb::Permission] The permissions of the member.
50
+ # @!attribute [r] presence
51
+ # @macro client_cache
52
+ # @return [Discorb::Presence] The presence of the member.
53
+ # @!attribute [r] activity
54
+ # @macro client_cache
55
+ # @return [Discorb::Activity] The activity of the member. It's from the {#presence}.
56
+ # @!attribute [r] activities
57
+ # @macro client_cache
58
+ # @return [Array<Discorb::Activity>] The activities of the member. It's from the {#presence}.
59
+ # @!attribute [r] status
60
+ # @macro client_cache
61
+ # @return [Symbol] The status of the member. It's from the {#presence}.
62
+
63
+ # @!visibility private
64
+ def initialize(client, guild_id, user_data, member_data)
65
+ @guild_id = guild_id
66
+ @client = client
67
+ @_member_data = {}
68
+ @data = {}
69
+ _set_data(user_data, member_data)
70
+ end
71
+
72
+ #
73
+ # Format the member to `@name` style.
74
+ #
75
+ # @return [String] The formatted member.
76
+ #
77
+ def to_s
78
+ "@#{name}"
79
+ end
80
+
81
+ def name
82
+ @nick || @username
83
+ end
84
+
85
+ def mention
86
+ "<@#{@nick.nil? ? "" : "!"}#{@id}>"
87
+ end
88
+
89
+ def voice_state
90
+ guild.voice_states[@id]
91
+ end
92
+
93
+ def guild
94
+ @client.guilds[@guild_id]
95
+ end
96
+
97
+ def roles
98
+ @role_ids.map { |r| guild.roles[r] }
99
+ end
100
+
101
+ def permissions
102
+ roles.map(&:permissions).sum(Permission.new(0))
103
+ end
104
+
105
+ def hoisted_role
106
+ @hoisted_role_id && guild.roles[@hoisted_role_id]
107
+ end
108
+
109
+ def hoisted?
110
+ !@hoisted_role_id.nil?
111
+ end
112
+
113
+ def presence
114
+ guild.presences[@id]
115
+ end
116
+
117
+ def activity
118
+ presence&.activity
119
+ end
120
+
121
+ def activities
122
+ presence&.activities
123
+ end
124
+
125
+ def status
126
+ presence&.status
127
+ end
128
+
129
+ def inspect
130
+ "#<#{self.class} #{self} id=#{@id}>"
131
+ end
132
+
133
+ #
134
+ # Add a role to the member.
135
+ # @macro http
136
+ # @macro async
137
+ #
138
+ # @param [Discorb::Role] role The role to add.
139
+ # @param [String] reason The reason for the action.
140
+ #
141
+ def add_role(role, reason: nil)
142
+ Async do
143
+ @client.internet.put("/guilds/#{@guild_id}/members/#{@id}/roles/#{role.is_a?(Role) ? role.id : role}", nil, audit_log_reason: reason).wait
144
+ end
145
+ end
146
+
147
+ #
148
+ # Remove a role to the member.
149
+ # @macro http
150
+ # @macro async
151
+ #
152
+ # @param [Discorb::Role] role The role to add.
153
+ # @param [String] reason The reason for the action.
154
+ #
155
+ def remove_role(role, reason: nil)
156
+ Async do
157
+ @client.internet.delete("/guilds/#{@guild_id}/members/#{@id}/roles/#{role.is_a?(Role) ? role.id : role}", audit_log_reason: reason).wait
158
+ end
159
+ end
160
+
161
+ #
162
+ # Edit the member.
163
+ # @macro http
164
+ # @macro async
165
+ # @macro edit
166
+ #
167
+ # @param [String] nick The nickname of the member.
168
+ # @param [Discorb::Role] role The roles of the member.
169
+ # @param [Boolean] mute Whether the member is muted.
170
+ # @param [Boolean] deaf Whether the member is deafened.
171
+ # @param [Discorb::StageChannel] channel The channel the member is moved to.
172
+ # @param [String] reason The reason for the action.
173
+ #
174
+ def edit(nick: :unset, role: :unset, mute: :unset, deaf: :unset, channel: :unset, reason: nil)
175
+ Async do
176
+ payload = {}
177
+ payload[:nick] = nick if nick != :unset
178
+ payload[:roles] = role if role != :unset
179
+ payload[:mute] = mute if mute != :unset
180
+ payload[:deaf] = deaf if deaf != :unset
181
+ payload[:channel_id] = channel&.id if channel != :unset
182
+ @client.internet.patch("/guilds/#{@guild_id}/members/#{@id}", payload, audit_log_reason: reason).wait
183
+ end
184
+ end
185
+
186
+ alias modify edit
187
+
188
+ #
189
+ # Kick the member.
190
+ #
191
+ # @param [String] reason The reason for the action.
192
+ #
193
+ def kick(reason: nil)
194
+ Async do
195
+ guild.kick_member(self, reason: reason).wait
196
+ end
197
+ end
198
+
199
+ #
200
+ # Ban the member.
201
+ #
202
+ # @param [Integer] delete_message_days The number of days to delete messages.
203
+ # @param [String] reason The reason for the action.
204
+ #
205
+ # @return [Discorb::Guild::Ban] The ban.
206
+ #
207
+ def ban(delete_message_days: 0, reason: nil)
208
+ Async do
209
+ guild.ban_member(self, delete_message_days: delete_message_days, reason: reason).wait
210
+ end
211
+ end
212
+
213
+ private
214
+
215
+ def _set_data(user_data, member_data)
216
+ user_data ||= member_data[:user]
217
+ @role_ids = member_data[:roles]
218
+ @premium_since = member_data[:premium_since] && Time.iso8601(member_data[:premium_since])
219
+ @pending = member_data[:pending]
220
+ @nick = member_data[:nick]
221
+ @mute = member_data[:mute]
222
+ @joined_at = member_data[:joined_at] && Time.iso8601(member_data[:joined_at])
223
+ @hoisted_role_id = member_data[:hoisted_role]
224
+ @deaf = member_data[:deaf]
225
+ @custom_avatar = member_data[:avatar] && Asset.new(member_data[:avatar])
226
+ @display_avatar = Asset.new(self, member_data[:avatar] || user_data[:avatar])
227
+ super(user_data)
228
+ @client.guilds[@guild_id].members[@id] = self unless @guild_id.nil?
229
+ @_member_data.update(member_data)
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,583 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ #
5
+ # Represents a allowed mentions in a message.
6
+ #
7
+ class AllowedMentions
8
+ # @return [Boolean] Whether to allow @everyone or @here.
9
+ attr_accessor :everyone
10
+ # @return [Boolean, Array<Discorb::Role>] The roles to allow, or false to disable.
11
+ attr_accessor :roles
12
+ # @return [Boolean, Array<Discorb::User>] The users to allow, or false to disable.
13
+ attr_accessor :users
14
+ # @return [Boolean] Whether to ping the user that sent the message to reply.
15
+ attr_accessor :replied_user
16
+
17
+ #
18
+ # Initializes a new instance of the AllowedMentions class.
19
+ #
20
+ # @param [Boolean] everyone Whether to allow @everyone or @here.
21
+ # @param [Boolean, Array<Discorb::Role>] roles The roles to allow, or false to disable.
22
+ # @param [Boolean, Array<Discorb::User>] users The users to allow, or false to disable.
23
+ # @param [Boolean] replied_user Whether to ping the user that sent the message to reply.
24
+ #
25
+ def initialize(everyone: nil, roles: nil, users: nil, replied_user: nil)
26
+ @everyone = everyone
27
+ @roles = roles
28
+ @users = users
29
+ @replied_user = replied_user
30
+ end
31
+
32
+ # @!visibility private
33
+ def to_hash(other = nil)
34
+ payload = {
35
+ parse: %w[everyone roles users],
36
+ }
37
+ replied_user = nil_merge(@replied_user, other&.replied_user)
38
+ everyone = nil_merge(@everyone, other&.everyone)
39
+ roles = nil_merge(@roles, other&.roles)
40
+ users = nil_merge(@users, other&.users)
41
+ payload[:replied_user] = replied_user
42
+ payload[:parse].delete("everyone") if everyone == false
43
+ if (roles == false) || roles.is_a?(Array)
44
+ payload[:roles] = roles.map { |u| u.id.to_s } if roles.is_a? Array
45
+ payload[:parse].delete("roles")
46
+ end
47
+ if (users == false) || users.is_a?(Array)
48
+ payload[:users] = users.map { |u| u.id.to_s } if users.is_a? Array
49
+ payload[:parse].delete("users")
50
+ end
51
+ payload
52
+ end
53
+
54
+ def nil_merge(*args)
55
+ args.each do |a|
56
+ return a unless a.nil?
57
+ end
58
+ nil
59
+ end
60
+ end
61
+
62
+ #
63
+ # Represents a message.
64
+ #
65
+ class Message < DiscordModel
66
+ # @return [Discorb::Snowflake] The ID of the message.
67
+ attr_reader :id
68
+ # @return [Discorb::User, Discorb::Member] The user that sent the message.
69
+ attr_reader :author
70
+ # @return [String] The content of the message.
71
+ attr_reader :content
72
+ alias to_s content
73
+ # @return [Time] The time the message was created.
74
+ attr_reader :created_at
75
+ alias timestamp created_at
76
+ alias sent_at created_at
77
+ # @return [Time] The time the message was edited.
78
+ # @return [nil] If the message was not edited.
79
+ attr_reader :updated_at
80
+ alias edited_at updated_at
81
+ alias edited_timestamp updated_at
82
+ # @return [Array<Discorb::Attachment>] The attachments of the message.
83
+ attr_reader :attachments
84
+ # @return [Array<Discorb::Embed>] The embeds of the message.
85
+ attr_reader :embeds
86
+ # @return [Array<Discorb::Reaction>] The reactions of the message.
87
+ attr_reader :reactions
88
+ # @return [Discorb::Snowflake] The ID of the channel the message was sent in.
89
+ attr_reader :webhook_id
90
+ # @return [Symbol] The type of the message.
91
+ # Currently, this will be one of:
92
+ #
93
+ # * `:default`
94
+ # * `:recipient_add`
95
+ # * `:recipient_remove`
96
+ # * `:call`
97
+ # * `:channel_name_change`
98
+ # * `:channel_icon_change`
99
+ # * `:channel_pinned_message`
100
+ # * `:guild_member_join`
101
+ # * `:user_premium_guild_subscription`
102
+ # * `:user_premium_guild_subscription_tier_1`
103
+ # * `:user_premium_guild_subscription_tier_2`
104
+ # * `:user_premium_guild_subscription_tier_3`
105
+ # * `:channel_follow_add`
106
+ # * `:guild_discovery_disqualified`
107
+ # * `:guild_discovery_requalified`
108
+ # * `:guild_discovery_grace_period_initial_warning`
109
+ # * `:guild_discovery_grace_period_final_warning`
110
+ # * `:thread_created`
111
+ # * `:reply`
112
+ # * `:application_command`
113
+ # * `:thread_starter_message`
114
+ # * `:guild_invite_reminder`
115
+ attr_reader :type
116
+ # @return [Discorb::Message::Activity] The activity of the message.
117
+ attr_reader :activity
118
+ # @return [Discorb::Application] The application of the message.
119
+ attr_reader :application_id
120
+ # @return [Discorb::Message::Reference] The reference of the message.
121
+ attr_reader :message_reference
122
+ # @return [Discorb::Message::Flag] The flag of the message.
123
+ # @see Discorb::Message::Flag
124
+ attr_reader :flag
125
+ # @return [Discorb::Message::Sticker] The sticker of the message.
126
+ attr_reader :stickers
127
+ # @return [Discorb::Message::Interaction] The interaction of the message.
128
+ attr_reader :interaction
129
+ # @return [Discorb::ThreadChannel] The thread channel of the message.
130
+ attr_reader :thread
131
+ # @return [Array<Array<Discorb::Components>>] The components of the message.
132
+ attr_reader :components
133
+ # @return [Boolean] Whether the message is deleted.
134
+ attr_reader :deleted
135
+ alias deleted? deleted
136
+ # @return [Boolean] Whether the message is tts.
137
+ attr_reader :tts
138
+ alias tts? tts
139
+ # @return [Boolean] Whether the message mentions everyone.
140
+ attr_reader :mention_everyone
141
+ alias mention_everyone? mention_everyone
142
+ # @return [Boolean] Whether the message is pinned.
143
+ attr_reader :pinned
144
+ alias pinned? pinned
145
+ @message_type = {
146
+ default: 0,
147
+ recipient_add: 1,
148
+ recipient_remove: 2,
149
+ call: 3,
150
+ channel_name_change: 4,
151
+ channel_icon_change: 5,
152
+ channel_pinned_message: 6,
153
+ guild_member_join: 7,
154
+ user_premium_guild_subscription: 8,
155
+ user_premium_guild_subscription_tier_1: 9,
156
+ user_premium_guild_subscription_tier_2: 10,
157
+ user_premium_guild_subscription_tier_3: 11,
158
+ channel_follow_add: 12,
159
+ guild_discovery_disqualified: 14,
160
+ guild_discovery_requalified: 15,
161
+ guild_discovery_grace_period_initial_warning: 16,
162
+ guild_discovery_grace_period_final_warning: 17,
163
+ thread_created: 18,
164
+ reply: 19,
165
+ application_command: 20,
166
+ thread_starter_message: 21,
167
+ guild_invite_reminder: 22,
168
+ }.freeze
169
+
170
+ # @!attribute [r] channel
171
+ # @macro client_cache
172
+ # @return [Discorb::Channel] The channel the message was sent in.
173
+ # @!attribute [r] guild
174
+ # @macro client_cache
175
+ # @return [Discorb::Guild] The guild the message was sent in.
176
+ # @return [nil] If the message was not sent in a guild.
177
+ # @!attribute [r] webhook?
178
+ # @return [Boolean] Whether the message was sent by a webhook.
179
+ # @!attribute [r] edited?
180
+ # @return [Boolean] Whether the message was edited.
181
+ # @!attribute [r] jump_url
182
+ # @return [String] The URL to jump to the message.
183
+ # @!attribute [r] embed
184
+ # @return [Discorb::Embed] The embed of the message.
185
+ # @return [nil] If the message has no embed.
186
+
187
+ # @!visibility private
188
+ def initialize(client, data, no_cache: false)
189
+ @client = client
190
+ @data = {}
191
+ @no_cache = no_cache
192
+ _set_data(data)
193
+ @client.messages[@id] = self unless @no_cache
194
+ end
195
+
196
+ def channel
197
+ @dm || @client.channels[@channel_id]
198
+ end
199
+
200
+ def guild
201
+ @client.guilds[@guild_id]
202
+ end
203
+
204
+ def webhook?
205
+ @webhook_id != nil
206
+ end
207
+
208
+ def jump_url
209
+ "https://discord.com/channels/#{@guild_id || "@me"}/#{@channel_id}/#{@id}"
210
+ end
211
+
212
+ def edited?
213
+ !@updated_at.nil?
214
+ end
215
+
216
+ #
217
+ # Convert the message to reference object.
218
+ #
219
+ # @param [Boolean] fail_if_not_exists Whether to raise an error if the message does not exist.
220
+ #
221
+ # @return [Hash] The reference object.
222
+ #
223
+ def to_reference(fail_if_not_exists: true)
224
+ {
225
+ message_id: @id,
226
+ channel_id: @channel_id,
227
+ guild_id: @guild_id,
228
+ fail_if_not_exists: fail_if_not_exists,
229
+ }
230
+ end
231
+
232
+ def embed
233
+ @embeds[0]
234
+ end
235
+
236
+ # Reply to the message.
237
+ # @macro async
238
+ # @macro http
239
+ # @param (see #post)
240
+ # @return [Discorb::Message] The message.
241
+ def reply(*args, **kwargs)
242
+ Async do |_task|
243
+ channel.post(*args, reference: self, **kwargs).wait
244
+ end
245
+ end
246
+
247
+ #
248
+ # Publish the message.
249
+ # @macro async
250
+ # @macro http
251
+ #
252
+ def publish
253
+ Async do |_task|
254
+ channel.post("/channels/#{@channel_id}/messages/#{@id}/crosspost", nil).wait
255
+ end
256
+ end
257
+
258
+ #
259
+ # Add a reaction to the message.
260
+ # @macro async
261
+ # @macro http
262
+ #
263
+ # @param [Discorb::Emoji] emoji The emoji to react with.
264
+ #
265
+ def add_reaction(emoji)
266
+ Async do |_task|
267
+ @client.internet.put("/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}/@me", nil).wait
268
+ end
269
+ end
270
+
271
+ alias react_with add_reaction
272
+
273
+ #
274
+ # Remove a reaction from the message.
275
+ # @macro async
276
+ # @macro http
277
+ #
278
+ # @param [Discorb::Emoji] emoji The emoji to remove.
279
+ #
280
+ def remove_reaction(emoji)
281
+ Async do |_task|
282
+ @client.internet.delete("/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}/@me").wait
283
+ end
284
+ end
285
+
286
+ alias delete_reaction remove_reaction
287
+
288
+ #
289
+ # Remove other member's reaction from the message.
290
+ # @macro async
291
+ # @macro http
292
+ #
293
+ # @param [Discorb::Emoji] emoji The emoji to remove.
294
+ # @param [Discorb::Member] member The member to remove the reaction from.
295
+ #
296
+ def remove_reaction_of(emoji, member)
297
+ Async do |_task|
298
+ @client.internet.delete("/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}/#{member.is_a?(Member) ? member.id : member}").wait
299
+ end
300
+ end
301
+
302
+ alias delete_reaction_of remove_reaction_of
303
+
304
+ #
305
+ # Fetch reacted users of reaction.
306
+ # @macro async
307
+ # @macro http
308
+ #
309
+ # @param [Discorb::Emoji] emoji The emoji to fetch.
310
+ # @param [Integer, nil] limit The maximum number of users to fetch. `nil` for no limit.
311
+ # @param [Discorb::Snowflake, nil] after The ID of the user to start fetching from.
312
+ #
313
+ # @return [Array<Discorb::User>] The users.
314
+ #
315
+ def fetch_reacted_users(emoji, limit: nil, after: 0)
316
+ Async do |_task|
317
+ if limit.nil? || !limit.positive?
318
+ after = 0
319
+ users = []
320
+ loop do
321
+ _resp, data = @client.internet.get("/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}?limit=100&after=#{after}").wait
322
+ break if data.empty?
323
+
324
+ users += data.map { |r| guild&.members&.[](r[:id]) || @client.users[r[:id]] || User.new(@client, r) }
325
+
326
+ break if data.length < 100
327
+
328
+ after = data[-1][:id]
329
+ end
330
+ next users
331
+ else
332
+ _resp, data = @client.internet.get("/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}?limit=#{limit}&after=#{after}").wait
333
+ next data.map { |r| guild&.members&.[](r[:id]) || @client.users[r[:id]] || User.new(@client, r) }
334
+ end
335
+ end
336
+ end
337
+
338
+ #
339
+ # Pin the message.
340
+ # @macro async
341
+ # @macro http
342
+ #
343
+ # @param [String] reason The reason for pinning the message.
344
+ #
345
+ def pin(reason: nil)
346
+ Async do
347
+ channel.pin_message(self, reason: reason).wait
348
+ end
349
+ end
350
+
351
+ #
352
+ # Unpin the message.
353
+ # @macro async
354
+ # @macro http
355
+ #
356
+ def unpin
357
+ Async do
358
+ channel.unpin_message(self, reason: reason).wait
359
+ end
360
+ end
361
+
362
+ #
363
+ # Start thread from the message.
364
+ #
365
+ # @param (see Discorb::Channel#start_thread)
366
+ #
367
+ # @return [<Type>] <description>
368
+ #
369
+ def start_thread(*args, **kwargs)
370
+ Async do
371
+ channel.start_thread(*args, message: self, **kwargs).wait
372
+ end
373
+ end
374
+
375
+ alias create_thread start_thread
376
+
377
+ # Meta
378
+
379
+ def inspect
380
+ "#<#{self.class} #{@content.inspect} id=#{@id}>"
381
+ end
382
+
383
+ #
384
+ # Represents message flag.
385
+ # ## Flag fields
386
+ # |Field|Value|
387
+ # |-|-|
388
+ # |`1 << 0`|`:crossposted`|
389
+ # |`1 << 1`|`:crosspost`|
390
+ # |`1 << 2`|`:supress_embeds`|
391
+ # |`1 << 3`|`:source_message_deleted`|
392
+ # |`1 << 4`|`:urgent`|
393
+ # |`1 << 5`|`:has_thread`|
394
+ # |`1 << 6`|`:ephemeral`|
395
+ # |`1 << 7`|`:loading`|
396
+ #
397
+ class Flag < Discorb::Flag
398
+ @bits = {
399
+ crossposted: 0,
400
+ crosspost: 1,
401
+ supress_embeds: 2,
402
+ source_message_deleted: 3,
403
+ urgent: 4,
404
+ has_thread: 5,
405
+ ephemeral: 6,
406
+ loading: 7,
407
+ }.freeze
408
+ end
409
+
410
+ #
411
+ # Represents reference of message.
412
+ #
413
+ class Reference
414
+ # @return [Discorb::Snowflake] The guild ID.
415
+ attr_accessor :guild_id
416
+ # @return [Discorb::Snowflake] The channel ID.
417
+ attr_accessor :channel_id
418
+ # @return [Discorb::Snowflake] The message ID.
419
+ attr_accessor :message_id
420
+ # @return [Boolean] Whether fail the request if the message is not found.
421
+ attr_accessor :fail_if_not_exists
422
+
423
+ alias fail_if_not_exists? fail_if_not_exists
424
+
425
+ #
426
+ # Initialize a new reference.
427
+ #
428
+ # @param [Discorb::Snowflake] guild_id The guild ID.
429
+ # @param [Discorb::Snowflake] channel_id The channel ID.
430
+ # @param [Discorb::Snowflake] message_id The message ID.
431
+ # @param [Boolean] fail_if_not_exists Whether fail the request if the message is not found.
432
+ #
433
+ def initialize(guild_id, channel_id, message_id, fail_if_not_exists: true)
434
+ @guild_id = guild_id
435
+ @channel_id = channel_id
436
+ @message_id = message_id
437
+ @fail_if_not_exists = fail_if_not_exists
438
+ end
439
+
440
+ #
441
+ # Convert the reference to a hash.
442
+ #
443
+ # @return [Hash] The hash.
444
+ #
445
+ def to_hash
446
+ {
447
+ message_id: @message_id,
448
+ channel_id: @channel_id,
449
+ guild_id: @guild_id,
450
+ fail_if_not_exists: @fail_if_not_exists,
451
+ }
452
+ end
453
+
454
+ alias to_reference to_hash
455
+
456
+ #
457
+ # Initialize a new reference from a hash.
458
+ #
459
+ # @param [Hash] data The hash.
460
+ #
461
+ # @return [Discorb::Message::Reference] The reference.
462
+ # @see https://discord.com/developers/docs/resources/channel#message-reference-object
463
+ #
464
+ def self.from_hash(data)
465
+ new(data[:guild_id], data[:channel_id], data[:message_id], fail_if_not_exists: data[:fail_if_not_exists])
466
+ end
467
+ end
468
+
469
+ class Sticker
470
+ attr_reader :id, :name, :format
471
+
472
+ def initialize(data)
473
+ @id = Snowflake.new(data[:id])
474
+ @name = data[:name]
475
+ @format = Discorb::Sticker.sticker_format[data[:format]]
476
+ end
477
+ end
478
+
479
+ private
480
+
481
+ def _set_data(data)
482
+ @id = Snowflake.new(data[:id])
483
+ @channel_id = data[:channel_id]
484
+
485
+ if data[:guild_id]
486
+ @guild_id = data[:guild_id]
487
+ @dm = nil
488
+ else
489
+ @dm = Discorb::DMChannel.new(@client, data[:channel_id])
490
+ @guild_id = nil
491
+ end
492
+
493
+ if data[:author].nil? && data[:webhook_id]
494
+ @webhook_id = Snowflake.new(data[:webhook_id])
495
+ # @author = WebhookAuthor.new(data[:webhook_id])
496
+ elsif data[:guild_id].nil? || data[:guild_id].empty?
497
+ @author = @client.users[data[:author][:id]] || User.new(@client, data[:author])
498
+ else
499
+ @author = guild.members[data[:author][:id]] || Member.new(@client,
500
+ @guild_id, data[:author], data[:member])
501
+ end
502
+ @content = data[:content]
503
+ @created_at = Time.iso8601(data[:timestamp])
504
+ @updated_at = data[:edited_timestamp].nil? ? nil : Time.iso8601(data[:edited_timestamp])
505
+
506
+ @tts = data[:tts]
507
+ @mention_everyone = data[:mention_everyone]
508
+ @mention_roles = data[:mention_roles].map { |r| guild.roles[r] }
509
+ @attachments = data[:attachments].map { |a| Attachment.new(a) }
510
+ @embeds = data[:embeds] ? data[:embeds].map { |e| Embed.new(data: e) } : []
511
+ @reactions = data[:reactions] ? data[:reactions].map { |r| Reaction.new(self, r) } : []
512
+ @pinned = data[:pinned]
513
+ @type = self.class.message_type[data[:type]]
514
+ @activity = data[:activity] && Activity.new(data[:activity])
515
+ @application_id = data[:application_id]
516
+ @message_reference = data[:message_reference] && Reference.from_hash(data[:message_reference])
517
+ @flag = Flag.new(0b111 - data[:flags])
518
+ @sticker_items = data[:sticker_items] ? data[:sticker_items].map { |s| Message::Sticker.new(s) } : []
519
+ # @referenced_message = data[:referenced_message] && Message.new(@client, data[:referenced_message])
520
+ @interaction = data[:interaction] && Message::Interaction.new(@client, data[:interaction])
521
+ @thread = data[:thread] && Channel.make_channel(@client, data[:thread])
522
+ @components = data[:components].map { |c| c[:components].map { |co| Component.from_hash(co) } }
523
+ @data.update(data)
524
+ @deleted = false
525
+ end
526
+
527
+ #
528
+ # Represents a interaction of message.
529
+ #
530
+ class Interaction < DiscordModel
531
+ # @return [Discorb::Snowflake] The user ID.
532
+ attr_reader :id
533
+ # @return [String] The name of command.
534
+ # @return [nil] If the message is not a command.
535
+ attr_reader :name
536
+ # @return [Class] The type of interaction.
537
+ attr_reader :type
538
+ # @return [Discorb::User] The user.
539
+ attr_reader :user
540
+
541
+ # @!visibility private
542
+ def initialize(client, data)
543
+ @id = Snowflake.new(data[:id])
544
+ @name = data[:name]
545
+ @type = Discorb::Interaction.descendants.find { |c| c.interaction_type == data[:type] }
546
+ @user = client.users[data[:user][:id]] || User.new(client, data[:user])
547
+ end
548
+ end
549
+
550
+ #
551
+ # Represents a activity of message.
552
+ #
553
+ class Activity < DiscordModel
554
+ # @return [String] The name of activity.
555
+ attr_reader :name
556
+ # @return [Symbol] The type of activity.
557
+ attr_reader :type
558
+
559
+ @type = {
560
+ 1 => :join,
561
+ 2 => :spectate,
562
+ 3 => :listen,
563
+ 5 => :join_request,
564
+ }
565
+
566
+ # @!visibility private
567
+ def initialize(data)
568
+ @name = data[:name]
569
+ @type = self.class.type(data[:type])
570
+ end
571
+
572
+ class << self
573
+ # @!visibility private
574
+ attr_reader :type
575
+ end
576
+ end
577
+
578
+ class << self
579
+ # @!visibility private
580
+ attr_reader :message_type
581
+ end
582
+ end
583
+ end