discorb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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