onyxcord 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.devcontainer/Dockerfile +13 -0
- data/.devcontainer/devcontainer.json +29 -0
- data/.devcontainer/postcreate.sh +4 -0
- data/.github/CONTRIBUTING.md +13 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
- data/.github/pull_request_template.md +37 -0
- data/.github/workflows/ci.yml +78 -0
- data/.github/workflows/codeql.yml +65 -0
- data/.github/workflows/deploy.yml +54 -0
- data/.github/workflows/release.yml +51 -0
- data/.gitignore +16 -0
- data/.markdownlint.json +4 -0
- data/.overcommit.yml +7 -0
- data/.rspec +2 -0
- data/.rubocop.yml +129 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +305 -0
- data/Rakefile +17 -0
- data/bin/console +15 -0
- data/bin/setup +7 -0
- data/lib/onyxcord/allowed_mentions.rb +43 -0
- data/lib/onyxcord/api/application.rb +316 -0
- data/lib/onyxcord/api/channel.rb +700 -0
- data/lib/onyxcord/api/interaction.rb +67 -0
- data/lib/onyxcord/api/invite.rb +44 -0
- data/lib/onyxcord/api/server.rb +775 -0
- data/lib/onyxcord/api/user.rb +158 -0
- data/lib/onyxcord/api/webhook.rb +163 -0
- data/lib/onyxcord/api.rb +335 -0
- data/lib/onyxcord/await.rb +51 -0
- data/lib/onyxcord/bot.rb +1971 -0
- data/lib/onyxcord/cache.rb +326 -0
- data/lib/onyxcord/colour_rgb.rb +43 -0
- data/lib/onyxcord/commands/command_bot.rb +511 -0
- data/lib/onyxcord/commands/container.rb +112 -0
- data/lib/onyxcord/commands/events.rb +11 -0
- data/lib/onyxcord/commands/parser.rb +327 -0
- data/lib/onyxcord/commands/rate_limiter.rb +144 -0
- data/lib/onyxcord/configuration.rb +125 -0
- data/lib/onyxcord/container.rb +988 -0
- data/lib/onyxcord/data/activity.rb +271 -0
- data/lib/onyxcord/data/application.rb +341 -0
- data/lib/onyxcord/data/attachment.rb +91 -0
- data/lib/onyxcord/data/audit_logs.rb +438 -0
- data/lib/onyxcord/data/avatar_decoration.rb +26 -0
- data/lib/onyxcord/data/call.rb +22 -0
- data/lib/onyxcord/data/channel.rb +1355 -0
- data/lib/onyxcord/data/channel_tag.rb +69 -0
- data/lib/onyxcord/data/collectibles.rb +47 -0
- data/lib/onyxcord/data/component.rb +583 -0
- data/lib/onyxcord/data/embed.rb +258 -0
- data/lib/onyxcord/data/emoji.rb +123 -0
- data/lib/onyxcord/data/install_params.rb +24 -0
- data/lib/onyxcord/data/integration.rb +144 -0
- data/lib/onyxcord/data/interaction.rb +1141 -0
- data/lib/onyxcord/data/invite.rb +137 -0
- data/lib/onyxcord/data/member.rb +528 -0
- data/lib/onyxcord/data/message.rb +612 -0
- data/lib/onyxcord/data/message_activity.rb +41 -0
- data/lib/onyxcord/data/overwrite.rb +109 -0
- data/lib/onyxcord/data/poll.rb +365 -0
- data/lib/onyxcord/data/primary_server.rb +60 -0
- data/lib/onyxcord/data/profile.rb +79 -0
- data/lib/onyxcord/data/reaction.rb +64 -0
- data/lib/onyxcord/data/recipient.rb +34 -0
- data/lib/onyxcord/data/role.rb +449 -0
- data/lib/onyxcord/data/role_connection_data.rb +69 -0
- data/lib/onyxcord/data/role_subscription.rb +41 -0
- data/lib/onyxcord/data/scheduled_event.rb +513 -0
- data/lib/onyxcord/data/server.rb +1614 -0
- data/lib/onyxcord/data/server_preview.rb +68 -0
- data/lib/onyxcord/data/snapshot.rb +112 -0
- data/lib/onyxcord/data/team.rb +98 -0
- data/lib/onyxcord/data/timestamp.rb +69 -0
- data/lib/onyxcord/data/user.rb +324 -0
- data/lib/onyxcord/data/voice_region.rb +46 -0
- data/lib/onyxcord/data/voice_state.rb +41 -0
- data/lib/onyxcord/data/webhook.rb +238 -0
- data/lib/onyxcord/data.rb +57 -0
- data/lib/onyxcord/errors.rb +246 -0
- data/lib/onyxcord/event_executor.rb +80 -0
- data/lib/onyxcord/events/await.rb +48 -0
- data/lib/onyxcord/events/bans.rb +60 -0
- data/lib/onyxcord/events/channels.rb +225 -0
- data/lib/onyxcord/events/generic.rb +129 -0
- data/lib/onyxcord/events/guilds.rb +269 -0
- data/lib/onyxcord/events/integrations.rb +100 -0
- data/lib/onyxcord/events/interactions.rb +624 -0
- data/lib/onyxcord/events/invites.rb +127 -0
- data/lib/onyxcord/events/lifetime.rb +31 -0
- data/lib/onyxcord/events/members.rb +110 -0
- data/lib/onyxcord/events/message.rb +399 -0
- data/lib/onyxcord/events/polls.rb +118 -0
- data/lib/onyxcord/events/presence.rb +131 -0
- data/lib/onyxcord/events/raw.rb +74 -0
- data/lib/onyxcord/events/reactions.rb +218 -0
- data/lib/onyxcord/events/roles.rb +87 -0
- data/lib/onyxcord/events/scheduled_events.rb +171 -0
- data/lib/onyxcord/events/threads.rb +100 -0
- data/lib/onyxcord/events/typing.rb +73 -0
- data/lib/onyxcord/events/voice_server_update.rb +48 -0
- data/lib/onyxcord/events/voice_state_update.rb +106 -0
- data/lib/onyxcord/events/webhooks.rb +65 -0
- data/lib/onyxcord/gateway.rb +890 -0
- data/lib/onyxcord/id_object.rb +39 -0
- data/lib/onyxcord/light/data.rb +62 -0
- data/lib/onyxcord/light/integrations.rb +73 -0
- data/lib/onyxcord/light/light_bot.rb +58 -0
- data/lib/onyxcord/light.rb +8 -0
- data/lib/onyxcord/logger.rb +120 -0
- data/lib/onyxcord/message_components.rb +70 -0
- data/lib/onyxcord/paginator.rb +60 -0
- data/lib/onyxcord/permissions.rb +255 -0
- data/lib/onyxcord/rate_limiter/gateway.rb +42 -0
- data/lib/onyxcord/rate_limiter/rest.rb +89 -0
- data/lib/onyxcord/version.rb +7 -0
- data/lib/onyxcord/voice/encoder.rb +115 -0
- data/lib/onyxcord/voice/network.rb +380 -0
- data/lib/onyxcord/voice/opcodes.rb +29 -0
- data/lib/onyxcord/voice/sodium.rb +157 -0
- data/lib/onyxcord/voice/timer.rb +19 -0
- data/lib/onyxcord/voice/voice_bot.rb +386 -0
- data/lib/onyxcord/webhooks.rb +14 -0
- data/lib/onyxcord/websocket.rb +62 -0
- data/lib/onyxcord.rb +180 -0
- data/onyxcord-webhooks.gemspec +30 -0
- data/onyxcord.gemspec +50 -0
- metadata +421 -0
|
@@ -0,0 +1,612 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'onyxcord/message_components'
|
|
4
|
+
|
|
5
|
+
module OnyxCord
|
|
6
|
+
# A message on Discord that was sent to a text channel
|
|
7
|
+
class Message
|
|
8
|
+
include IDObject
|
|
9
|
+
|
|
10
|
+
# Map of message flags.
|
|
11
|
+
FLAGS = {
|
|
12
|
+
crossposted: 1 << 0,
|
|
13
|
+
crosspost: 1 << 1,
|
|
14
|
+
suppress_embeds: 1 << 2,
|
|
15
|
+
source_message_deleted: 1 << 3,
|
|
16
|
+
urgent: 1 << 4,
|
|
17
|
+
thread: 1 << 5,
|
|
18
|
+
ephemeral: 1 << 6,
|
|
19
|
+
loading: 1 << 7,
|
|
20
|
+
failed_to_mention_roles: 1 << 8,
|
|
21
|
+
suppress_notifications: 1 << 12,
|
|
22
|
+
voice_message: 1 << 13,
|
|
23
|
+
snapshot: 1 << 14,
|
|
24
|
+
uikit_components: 1 << 15,
|
|
25
|
+
is_components_v2: OnyxCord::MessageComponents::IS_COMPONENTS_V2,
|
|
26
|
+
components_v2: OnyxCord::MessageComponents::IS_COMPONENTS_V2
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
# Map of message types.
|
|
30
|
+
TYPES = {
|
|
31
|
+
default: 0,
|
|
32
|
+
recipient_add: 1,
|
|
33
|
+
recipient_remove: 2,
|
|
34
|
+
call: 3,
|
|
35
|
+
channel_name_change: 4,
|
|
36
|
+
channel_icon_change: 5,
|
|
37
|
+
channel_pinned_message: 6,
|
|
38
|
+
server_member_join: 7,
|
|
39
|
+
server_boost: 8,
|
|
40
|
+
server_boost_tier_one: 9,
|
|
41
|
+
server_boost_tier_two: 10,
|
|
42
|
+
server_boost_tier_three: 11,
|
|
43
|
+
channel_follow_add: 12,
|
|
44
|
+
server_discovery_disqualified: 14,
|
|
45
|
+
server_discovery_requalified: 15,
|
|
46
|
+
server_discovery_grace_period_initial_warning: 16,
|
|
47
|
+
server_discovery_grace_period_final_warning: 17,
|
|
48
|
+
thread_created: 18,
|
|
49
|
+
reply: 19,
|
|
50
|
+
chat_input_command: 20,
|
|
51
|
+
thread_starter_message: 21,
|
|
52
|
+
server_invite_reminder: 22,
|
|
53
|
+
context_menu_command: 23,
|
|
54
|
+
automod_action: 24,
|
|
55
|
+
role_subscription_purchase: 25,
|
|
56
|
+
interaction_premium_upsell: 26,
|
|
57
|
+
stage_start: 27,
|
|
58
|
+
stage_end: 28,
|
|
59
|
+
stage_speaker: 29,
|
|
60
|
+
stage_raise_hand: 30,
|
|
61
|
+
stage_topic: 31,
|
|
62
|
+
server_application_premium_subscription: 32,
|
|
63
|
+
server_incident_alert_mode_enabled: 36,
|
|
64
|
+
server_incident_alert_mode_disabled: 37,
|
|
65
|
+
server_incident_report_raid: 38,
|
|
66
|
+
server_incident_report_false_alarm: 39,
|
|
67
|
+
purchase_notification: 44,
|
|
68
|
+
poll_result: 46,
|
|
69
|
+
changelog: 47,
|
|
70
|
+
server_join_request_accepted: 52,
|
|
71
|
+
server_join_request_rejected: 53,
|
|
72
|
+
server_join_request_withdrawn: 54,
|
|
73
|
+
report_to_mod_deleted_message: 58,
|
|
74
|
+
report_to_mod_timeout_user: 59,
|
|
75
|
+
report_to_mod_kick_user: 60,
|
|
76
|
+
report_to_mod_ban_user: 61,
|
|
77
|
+
report_to_mod_closed_report: 62,
|
|
78
|
+
server_emoji_added: 63
|
|
79
|
+
}.freeze
|
|
80
|
+
|
|
81
|
+
# @return [String] the content of this message.
|
|
82
|
+
attr_reader :content
|
|
83
|
+
alias_method :text, :content
|
|
84
|
+
alias_method :to_s, :content
|
|
85
|
+
|
|
86
|
+
# @return [Channel] the channel in which this message was sent.
|
|
87
|
+
attr_reader :channel
|
|
88
|
+
|
|
89
|
+
# @return [Time] the timestamp at which this message was edited. `nil` if the message was never edited.
|
|
90
|
+
attr_reader :edited_timestamp
|
|
91
|
+
alias_method :edit_timestamp, :edited_timestamp
|
|
92
|
+
|
|
93
|
+
# @return [Array<User>] the users that were mentioned in this message.
|
|
94
|
+
attr_reader :mentions
|
|
95
|
+
|
|
96
|
+
# @return [Array<Attachment>] the files attached to this message.
|
|
97
|
+
attr_reader :attachments
|
|
98
|
+
|
|
99
|
+
# @return [Array<Embed>] the embed objects contained in this message.
|
|
100
|
+
attr_reader :embeds
|
|
101
|
+
|
|
102
|
+
# @return [Array<Reaction>] the reaction objects contained in this message.
|
|
103
|
+
attr_reader :reactions
|
|
104
|
+
|
|
105
|
+
# @return [true, false] whether the message used Text-To-Speech (TTS) or not.
|
|
106
|
+
attr_reader :tts
|
|
107
|
+
alias_method :tts?, :tts
|
|
108
|
+
|
|
109
|
+
# @return [String] used for validating a message was sent.
|
|
110
|
+
attr_reader :nonce
|
|
111
|
+
|
|
112
|
+
# @return [true, false] whether the message was edited or not.
|
|
113
|
+
attr_reader :edited
|
|
114
|
+
alias_method :edited?, :edited
|
|
115
|
+
|
|
116
|
+
# @return [true, false] whether the message mentioned everyone or not.
|
|
117
|
+
attr_reader :mention_everyone
|
|
118
|
+
alias_method :mention_everyone?, :mention_everyone
|
|
119
|
+
alias_method :mentions_everyone?, :mention_everyone
|
|
120
|
+
|
|
121
|
+
# @return [true, false] whether the message is pinned or not.
|
|
122
|
+
attr_reader :pinned
|
|
123
|
+
alias_method :pinned?, :pinned
|
|
124
|
+
|
|
125
|
+
# @return [Integer] what the type of the message is
|
|
126
|
+
attr_reader :type
|
|
127
|
+
|
|
128
|
+
# @return [Integer, nil] the webhook ID that sent this message, or `nil` if it wasn't sent through a webhook.
|
|
129
|
+
attr_reader :webhook_id
|
|
130
|
+
|
|
131
|
+
# @return [Array<Component>] Interaction components for this message.
|
|
132
|
+
attr_reader :components
|
|
133
|
+
|
|
134
|
+
# @return [Integer] flags set on the message.
|
|
135
|
+
attr_reader :flags
|
|
136
|
+
|
|
137
|
+
# @return [Channel, nil] The thread that was started from this message, or nil.
|
|
138
|
+
attr_reader :thread
|
|
139
|
+
|
|
140
|
+
# @return [Time, nil] the time at when this message was pinned. Only present on messages fetched via {Channel#pins}.
|
|
141
|
+
attr_reader :pinned_at
|
|
142
|
+
|
|
143
|
+
# @return [Call, nil] the call in a private channel that prompted this message.
|
|
144
|
+
attr_reader :call
|
|
145
|
+
|
|
146
|
+
# @return [Array<Snapshot>] the message snapshots included in this message.
|
|
147
|
+
attr_reader :snapshots
|
|
148
|
+
|
|
149
|
+
# @return [RoleSubscriptionData, nil] the role subscription purchase or renewal that prompted this message.
|
|
150
|
+
attr_reader :role_subscription
|
|
151
|
+
|
|
152
|
+
# @return [Integer] a generally increasing integer that can be used to determine this message's position in a thread.
|
|
153
|
+
attr_reader :position
|
|
154
|
+
|
|
155
|
+
# @return [Poll, nil] the poll that was sent with this message, or `nil`.
|
|
156
|
+
attr_reader :poll
|
|
157
|
+
|
|
158
|
+
# @return [Poll::Result, nil] the finalised results for a poll that prompted this message.
|
|
159
|
+
attr_reader :poll_result
|
|
160
|
+
alias_method :poll_results, :poll_result
|
|
161
|
+
|
|
162
|
+
# @return [Integer, nil] the ID of the application associated with this message.
|
|
163
|
+
attr_reader :application_id
|
|
164
|
+
|
|
165
|
+
# @return [MessageActivity, nil] the rich-presence activity that prompted this message.
|
|
166
|
+
attr_reader :activity
|
|
167
|
+
|
|
168
|
+
# @return [Interactions::Metadata, nil] the metadata about the interaction that prompted this message.
|
|
169
|
+
attr_reader :interaction_metadata
|
|
170
|
+
|
|
171
|
+
# @!visibility private
|
|
172
|
+
def initialize(data, bot)
|
|
173
|
+
@bot = bot
|
|
174
|
+
@id = data['id'].to_i
|
|
175
|
+
@content = data['content']
|
|
176
|
+
@channel = bot.channel(data['channel_id'].to_i)
|
|
177
|
+
@pinned = data['pinned']
|
|
178
|
+
@type = data['type']
|
|
179
|
+
@tts = data['tts']
|
|
180
|
+
@nonce = data['nonce']
|
|
181
|
+
@flags = data['flags'] || 0
|
|
182
|
+
@position = data['position'] || 0
|
|
183
|
+
@mention_everyone = data['mention_everyone']
|
|
184
|
+
@webhook_id = data['webhook_id']&.to_i
|
|
185
|
+
@application_id = data['application_id']&.to_i
|
|
186
|
+
|
|
187
|
+
@edited_timestamp = Time.parse(data['edited_timestamp']) if data['edited_timestamp']
|
|
188
|
+
@edited = !@edited_timestamp.nil?
|
|
189
|
+
|
|
190
|
+
@message_reference = data['message_reference']
|
|
191
|
+
@referenced_message = Message.new(data['referenced_message'], @bot) if data['referenced_message']
|
|
192
|
+
|
|
193
|
+
if data['author']
|
|
194
|
+
if @webhook_id
|
|
195
|
+
# This is a webhook user! It would be pointless to try to resolve a member here, so we just create
|
|
196
|
+
# a User and return that instead.
|
|
197
|
+
OnyxCord::LOGGER.debug("Webhook user: #{data['author']['id']}")
|
|
198
|
+
@author = User.new(data['author'].merge({ '_webhook' => true }), @bot)
|
|
199
|
+
elsif @channel.private?
|
|
200
|
+
|
|
201
|
+
# Turn the message user into a recipient - we can't use the channel recipient
|
|
202
|
+
# directly because the bot may also send messages to the channel
|
|
203
|
+
@author = Recipient.new(@bot.user(data['author']['id'].to_i), @channel, @bot)
|
|
204
|
+
else
|
|
205
|
+
@author_id = data['author']['id'].to_i
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
@reactions = data['reactions']&.map { |reaction| Reaction.new(reaction) } || []
|
|
210
|
+
@mentions = data['mentions']&.map { |mention| @bot.ensure_user(mention) } || []
|
|
211
|
+
@mention_roles = data['mention_roles']&.map(&:to_i) || []
|
|
212
|
+
@poll_result = Poll::Result.new(data['embeds'].pop, @message_reference, @bot) if @type == 46
|
|
213
|
+
|
|
214
|
+
@attachments = data['attachments']&.map { |attachment| Attachment.new(attachment, self, @bot) } || []
|
|
215
|
+
@embeds = data['embeds']&.map { |embed| Embed.new(embed, self) } || []
|
|
216
|
+
@components = data['components']&.filter_map { |component| Components.from_data(component, @bot) } || []
|
|
217
|
+
|
|
218
|
+
@thread = @bot.ensure_channel(data['thread']) if data['thread']
|
|
219
|
+
@pinned_at = Time.parse(data['pinned_at']) if data['pinned_at']
|
|
220
|
+
@call = Call.new(data['call'], @bot) if data['call']
|
|
221
|
+
@poll = Poll.new(data['poll'], self, @bot) if data['poll']
|
|
222
|
+
|
|
223
|
+
@snapshots = data['message_snapshots']&.map { |snapshot| Snapshot.new(snapshot['message'], @bot) } || []
|
|
224
|
+
@role_subscription = RoleSubscriptionData.new(data['role_subscription_data'], self, @bot) if data['role_subscription_data']
|
|
225
|
+
@activity = MessageActivity.new(data['activity'], @bot) if data['activity']
|
|
226
|
+
@interaction_metadata = Interactions::Metadata.new(data['interaction_metadata'], self, @bot) if data['interaction_metadata']
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# @deprecated Please migrate to using {#creation_time} instead.
|
|
230
|
+
alias_method :timestamp, :creation_time
|
|
231
|
+
|
|
232
|
+
# @return [Member, User] the user that sent this message. (Will be a {Member} most of the time, it should only be a
|
|
233
|
+
# {User} for old messages when the author has left the server since then)
|
|
234
|
+
def author
|
|
235
|
+
return @author if @author
|
|
236
|
+
|
|
237
|
+
unless @channel.private?
|
|
238
|
+
@author = @channel.server.member(@author_id)
|
|
239
|
+
OnyxCord::LOGGER.debug("Member with ID #{@author_id} not cached (possibly left the server).") if @author.nil?
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
@author ||= @bot.user(@author_id)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
alias_method :user, :author
|
|
246
|
+
alias_method :writer, :author
|
|
247
|
+
|
|
248
|
+
# @return [Server, nil] the server this message was sent in. If this message was sent in a PM channel, it will be nil.
|
|
249
|
+
# @raise [OnyxCord::Errors::NoPermission] This can happen when receiving interactions for servers in which the bot is not
|
|
250
|
+
# authorized with the `bot` scope.
|
|
251
|
+
def server
|
|
252
|
+
return if @channel.private?
|
|
253
|
+
|
|
254
|
+
@server ||= @channel.server
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Get the roles that were mentioned in this message.
|
|
258
|
+
# @return [Array<Role>] the roles that were mentioned in this message.
|
|
259
|
+
# @raise [OnyxCord::Errors::NoPermission] This can happen when receiving interactions for servers in which the bot is not
|
|
260
|
+
# authorized with the `bot` scope.
|
|
261
|
+
def role_mentions
|
|
262
|
+
return [] if @channel.private? || @mention_roles.empty?
|
|
263
|
+
|
|
264
|
+
@role_mentions ||= @mention_roles.map { |id| server.role(id) }
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Replies to this message with the specified content.
|
|
268
|
+
# @deprecated Please use {#respond}.
|
|
269
|
+
# @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
|
|
270
|
+
# @return (see #respond)
|
|
271
|
+
# @see Channel#send_message
|
|
272
|
+
def reply(content)
|
|
273
|
+
@channel.send_message(content)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Responds to this message as an inline reply.
|
|
277
|
+
# @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
|
|
278
|
+
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
|
|
279
|
+
# @param embed [Hash, OnyxCord::Webhooks::Embed, nil] The rich embed to append to this message.
|
|
280
|
+
# @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
|
|
281
|
+
# @param allowed_mentions [Hash, OnyxCord::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
|
|
282
|
+
# @param mention_user [true, false] Whether the user that is being replied to should be pinged by the reply.
|
|
283
|
+
# @param components [View, Array<Hash>] Interaction components to associate with this message.
|
|
284
|
+
# @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2) and SUPPRESS_NOTIFICATIONS (1 << 12) can be set.
|
|
285
|
+
# @return (see #respond)
|
|
286
|
+
def reply!(content, tts: false, embed: nil, attachments: nil, allowed_mentions: {}, mention_user: false, components: nil, flags: 0)
|
|
287
|
+
allowed_mentions = { parse: [] } if allowed_mentions == false
|
|
288
|
+
allowed_mentions = allowed_mentions.to_hash.transform_keys(&:to_sym)
|
|
289
|
+
allowed_mentions[:replied_user] = mention_user
|
|
290
|
+
|
|
291
|
+
respond(content, tts, embed, attachments, allowed_mentions, self, components, flags)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# (see Channel#send_message)
|
|
295
|
+
def respond(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0)
|
|
296
|
+
@channel.send_message(content, tts, embed, attachments, allowed_mentions, message_reference, components, flags)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Edits this message to have the specified content instead.
|
|
300
|
+
# You can only edit your own messages.
|
|
301
|
+
# @param new_content [String] the new content the message should have.
|
|
302
|
+
# @param new_embeds [Hash, OnyxCord::Webhooks::Embed, Array<Hash>, Array<OnyxCord::Webhooks::Embed>, nil] The new embeds the message should have. If `nil` the message will be changed to have no embeds.
|
|
303
|
+
# @param new_components [View, Array<Hash>] The new components the message should have. If `nil` the message will be changed to have no components.
|
|
304
|
+
# @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2) can be edited.
|
|
305
|
+
# @return [Message] the resulting message.
|
|
306
|
+
def edit(new_content, new_embeds = nil, new_components = nil, flags = 0)
|
|
307
|
+
new_embeds = (new_embeds.instance_of?(Array) ? new_embeds.map(&:to_hash) : [new_embeds&.to_hash]).compact
|
|
308
|
+
new_components = new_components.to_a
|
|
309
|
+
flags = OnyxCord::MessageComponents.apply_v2_flag(flags, new_components)
|
|
310
|
+
|
|
311
|
+
response = API::Channel.edit_message(@bot.token, @channel.id, @id, new_content, :undef, new_embeds, new_components, flags)
|
|
312
|
+
Message.new(JSON.parse(response), @bot)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Deletes this message.
|
|
316
|
+
# @return [nil]
|
|
317
|
+
def delete(reason = nil)
|
|
318
|
+
API::Channel.delete_message(@bot.token, @channel.id, @id, reason)
|
|
319
|
+
nil
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Pins this message
|
|
323
|
+
# @return [nil]
|
|
324
|
+
def pin(reason = nil)
|
|
325
|
+
API::Channel.pin_message(@bot.token, @channel.id, @id, reason)
|
|
326
|
+
@pinned = true
|
|
327
|
+
nil
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Unpins this message
|
|
331
|
+
# @return [nil]
|
|
332
|
+
def unpin(reason = nil)
|
|
333
|
+
API::Channel.unpin_message(@bot.token, @channel.id, @id, reason)
|
|
334
|
+
@pinned = false
|
|
335
|
+
nil
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Crossposts a message in a news channel.
|
|
339
|
+
# @return [Message] the updated message object.
|
|
340
|
+
def crosspost
|
|
341
|
+
response = API::Channel.crosspost_message(@bot.token, @channel.id, @id)
|
|
342
|
+
Message.new(JSON.parse(response), @bot)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Add an {Await} for a message with the same user and channel.
|
|
346
|
+
# @see Bot#add_await
|
|
347
|
+
# @deprecated Will be changed to blocking behavior in v4.0. Use {#await!} instead.
|
|
348
|
+
def await(key, attributes = {}, &block)
|
|
349
|
+
@bot.add_await(key, OnyxCord::Events::MessageEvent, { from: @author.id, in: @channel.id }.merge(attributes), &block)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Add a blocking {Await} for a message with the same user and channel.
|
|
353
|
+
# @see Bot#add_await!
|
|
354
|
+
def await!(attributes = {}, &block)
|
|
355
|
+
@bot.add_await!(OnyxCord::Events::MessageEvent, { from: @author.id, in: @channel.id }.merge(attributes), &block)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# Add an {Await} for a reaction to be added on this message.
|
|
359
|
+
# @see Bot#add_await
|
|
360
|
+
# @deprecated Will be changed to blocking behavior in v4.0. Use {#await_reaction!} instead.
|
|
361
|
+
def await_reaction(key, attributes = {}, &block)
|
|
362
|
+
@bot.add_await(key, OnyxCord::Events::ReactionAddEvent, { message: @id }.merge(attributes), &block)
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
# Add a blocking {Await} for a reaction to be added on this message.
|
|
366
|
+
# @see Bot#add_await!
|
|
367
|
+
def await_reaction!(attributes = {}, &block)
|
|
368
|
+
@bot.add_await!(OnyxCord::Events::ReactionAddEvent, { message: @id }.merge(attributes), &block)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
# @return [true, false] whether this message was sent by the current {Bot}.
|
|
372
|
+
def from_bot?
|
|
373
|
+
(@author_id || @author&.id) == @bot.profile.id
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# @return [true, false] whether this message has been sent over a webhook.
|
|
377
|
+
def webhook?
|
|
378
|
+
!@webhook_id.nil?
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# @return [Array<Emoji>] the emotes that were used/mentioned in this message.
|
|
382
|
+
def emoji
|
|
383
|
+
return [] if @content.empty? || @content.nil?
|
|
384
|
+
|
|
385
|
+
@emoji ||= @bot.parse_mentions(@content).select { |mention| mention.is_a?(OnyxCord::Emoji) }
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
alias_method :emojis, :emoji
|
|
389
|
+
|
|
390
|
+
# Check if any emoji were used in this message.
|
|
391
|
+
# @return [true, false] whether or not any emoji were used
|
|
392
|
+
def emoji?
|
|
393
|
+
emoji.any?
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
alias_method :emojis?, :emoji?
|
|
397
|
+
|
|
398
|
+
# Check if any reactions were used in this message.
|
|
399
|
+
# @return [true, false] whether or not this message has reactions
|
|
400
|
+
def reactions?
|
|
401
|
+
!@reactions.empty?
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# Returns the reactions made by the current bot or user.
|
|
405
|
+
# @return [Array<Reaction>] the reactions
|
|
406
|
+
def my_reactions
|
|
407
|
+
@reactions.select(&:me)
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Removes embeds from the message
|
|
411
|
+
# @return [Message] the resulting message.
|
|
412
|
+
def suppress_embeds
|
|
413
|
+
flags = @flags | (1 << 2)
|
|
414
|
+
response = API::Channel.edit_message(@bot.token, @channel.id, @id, :undef, :undef, :undef, :undef, flags)
|
|
415
|
+
Message.new(JSON.parse(response), @bot)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# Check if this message mentions a specific user or role.
|
|
419
|
+
# @param target [Role, User, Member, Integer, String] The mention to match against.
|
|
420
|
+
# @return [true, false] whether or not this message mentions the target.
|
|
421
|
+
def mentions?(target)
|
|
422
|
+
mentions = (@mentions + role_mentions)
|
|
423
|
+
|
|
424
|
+
mentions << server if @mention_everyone
|
|
425
|
+
|
|
426
|
+
mentions.any?(target.resolve_id)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# Reacts to a message.
|
|
430
|
+
# @param reaction [String, #to_reaction] the unicode emoji or {Emoji}
|
|
431
|
+
# @return [nil]
|
|
432
|
+
def create_reaction(reaction)
|
|
433
|
+
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
|
434
|
+
API::Channel.create_reaction(@bot.token, @channel.id, @id, reaction)
|
|
435
|
+
nil
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
alias_method :react, :create_reaction
|
|
439
|
+
|
|
440
|
+
# Returns the list of users who reacted with a certain reaction.
|
|
441
|
+
# @param reaction [String, #to_reaction] the unicode emoji or {Emoji}
|
|
442
|
+
# @param limit [Integer] the limit of how many users to retrieve. `nil` will return all users
|
|
443
|
+
# @param type [Integer, Symbol] the type of reaction to get. See {Reaction::TYPES}
|
|
444
|
+
# @example Get all the users that reacted with a thumbs up.
|
|
445
|
+
# thumbs_up_reactions = message.reacted_with("\u{1F44D}")
|
|
446
|
+
# @return [Array<User>] the users who used this reaction
|
|
447
|
+
def reacted_with(reaction, limit: 100, type: :normal)
|
|
448
|
+
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
|
449
|
+
reaction = reaction.to_s if reaction.respond_to?(:to_s)
|
|
450
|
+
type = Reaction::TYPES[type] || type
|
|
451
|
+
|
|
452
|
+
get_reactions = proc do |fetch_limit, after_id = nil|
|
|
453
|
+
resp = API::Channel.get_reactions(@bot.token, @channel.id, @id, reaction, nil, after_id, fetch_limit, type)
|
|
454
|
+
JSON.parse(resp).map { |d| User.new(d, @bot) }
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# Can be done without pagination
|
|
458
|
+
return get_reactions.call(limit) if limit && limit <= 100
|
|
459
|
+
|
|
460
|
+
paginator = Paginator.new(limit, :down) do |last_page|
|
|
461
|
+
if last_page && last_page.count < 100
|
|
462
|
+
[]
|
|
463
|
+
else
|
|
464
|
+
get_reactions.call(100, last_page&.last&.id)
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
paginator.to_a
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# Returns a hash of all reactions to a message as keys and the users that reacted to it as values.
|
|
472
|
+
# @param limit [Integer] the limit of how many users to retrieve per distinct reaction emoji. `nil` will return all users
|
|
473
|
+
# @example Get all the users that reacted to a message for a giveaway.
|
|
474
|
+
# giveaway_participants = message.all_reaction_users
|
|
475
|
+
# @return [Hash<String => Array<User>>] A hash mapping the string representation of a
|
|
476
|
+
# reaction to an array of users.
|
|
477
|
+
def all_reaction_users(limit: 100)
|
|
478
|
+
@reactions.to_h { |reaction| [reaction.to_s, reacted_with(reaction, limit: limit)] }
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
# Deletes a reaction made by a user on this message.
|
|
482
|
+
# @param user [User, String, Integer] the user or user ID who used this reaction
|
|
483
|
+
# @param reaction [String, #to_reaction] the reaction to remove
|
|
484
|
+
def delete_reaction(user, reaction)
|
|
485
|
+
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
|
486
|
+
API::Channel.delete_user_reaction(@bot.token, @channel.id, @id, reaction, user.resolve_id)
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
# Deletes this client's reaction on this message.
|
|
490
|
+
# @param reaction [String, #to_reaction] the reaction to remove
|
|
491
|
+
def delete_own_reaction(reaction)
|
|
492
|
+
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
|
493
|
+
API::Channel.delete_own_reaction(@bot.token, @channel.id, @id, reaction)
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
# Removes all reactions from this message.
|
|
497
|
+
def delete_all_reactions
|
|
498
|
+
API::Channel.delete_all_reactions(@bot.token, @channel.id, @id)
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
# Removes all reactions for a single emoji.
|
|
502
|
+
# @param reaction [String, #to_reaction] the reaction to remove.
|
|
503
|
+
def delete_all_reactions_for_emoji(reaction)
|
|
504
|
+
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
|
505
|
+
API::Channel.delete_all_emoji_reactions(@bot.token, @channel.id, @id, reaction.to_s)
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
# The inspect method is overwritten to give more useful output
|
|
509
|
+
def inspect
|
|
510
|
+
"<Message content=\"#{@content}\" id=#{@id} timestamp=#{@timestamp} author=#{@author} channel=#{@channel}>"
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
# @return [String] a URL that a user can use to navigate to this message in the client
|
|
514
|
+
def link
|
|
515
|
+
"https://discord.com/channels/#{server&.id || '@me'}/#{@channel.id}/#{@id}"
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
alias_method :jump_link, :link
|
|
519
|
+
|
|
520
|
+
# Whether or not this message was sent in reply to another message
|
|
521
|
+
# @return [true, false]
|
|
522
|
+
def reply?
|
|
523
|
+
!@referenced_message.nil?
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
# @return [Message, nil] the Message this Message was sent in reply to.
|
|
527
|
+
def referenced_message
|
|
528
|
+
return @referenced_message if @referenced_message
|
|
529
|
+
return nil unless @message_reference
|
|
530
|
+
|
|
531
|
+
referenced_channel = @bot.channel(@message_reference['channel_id'])
|
|
532
|
+
@referenced_message = referenced_channel&.message(@message_reference['message_id'])
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
# @return [Array<Components::Button>]
|
|
536
|
+
def buttons
|
|
537
|
+
buttons = @components.flat_map do |component|
|
|
538
|
+
case component
|
|
539
|
+
when Components::Button
|
|
540
|
+
component
|
|
541
|
+
when Components::Section
|
|
542
|
+
component.accessory if component.accessory.is_a?(Components::Button)
|
|
543
|
+
when Components::ActionRow, Components::Container
|
|
544
|
+
component.buttons
|
|
545
|
+
end
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
buttons.compact
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
# to_message -> self or message
|
|
552
|
+
# @return [OnyxCord::Message]
|
|
553
|
+
def to_message
|
|
554
|
+
self
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
alias_method :message, :to_message
|
|
558
|
+
|
|
559
|
+
FLAGS.each do |name, value|
|
|
560
|
+
define_method("#{name}?") do
|
|
561
|
+
@flags.anybits?(value)
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
TYPES.each do |name, value|
|
|
566
|
+
define_method("#{name}?") do
|
|
567
|
+
@type == value
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
# Convert this message to a hash that can be used to reference this message in a forward or a reply.
|
|
572
|
+
# @param type [Integer, Symbol] The reference type to set. Can either be one of `:reply` or `:forward`.
|
|
573
|
+
# @param must_exist [true, false] Whether to raise an error if this message was deleted when sending it.
|
|
574
|
+
# @return [Hash] the message as a hash representation that can be used in a forwarded message or a reply.
|
|
575
|
+
def to_reference(type: :reply, must_exist: true)
|
|
576
|
+
type = (type == :reply ? 0 : 1) if type.is_a?(Symbol)
|
|
577
|
+
|
|
578
|
+
{ type: type, message_id: @id, channel_id: @channel.id, fail_if_not_exists: must_exist }
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
# Forward this message to another channel.
|
|
582
|
+
# @param channel [Integer, String, Channel] The target channel to forward this message to.
|
|
583
|
+
# @param must_exist [true, false] Whether to raise an error if this message was deleted when sending it.
|
|
584
|
+
# @param timeout [Float, nil] The amount of time in seconds after which the message sent will be deleted.
|
|
585
|
+
# @param flags [Integer, Symbol, Array<Integer, Symbol>] The message flags to set on the forwarded message.
|
|
586
|
+
# @param nonce [String, Integer, nil] The 25 character optional nonce that should be used when forwarding this message.
|
|
587
|
+
# @param enforce_nonce [true, false] Whether the provided nonce should be enforced and used for message de-duplication.
|
|
588
|
+
# @return [Message, nil] the message that was created from forwarding this one, or `nil` if this is a temporary message.
|
|
589
|
+
def forward(channel, must_exist: true, timeout: nil, flags: 0, nonce: nil, enforce_nonce: false)
|
|
590
|
+
reference = to_reference(type: :forward, must_exist: must_exist)
|
|
591
|
+
|
|
592
|
+
@bot.channel(channel).send_message!(reference: reference, timeout: timeout, flags: flags, nonce: nonce, enforce_nonce: enforce_nonce)
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
# Get the formatted timestamps contained in the message content.
|
|
596
|
+
# @return [Array<TimestampMarkdown>] The formatted timestamps in the message.
|
|
597
|
+
def timestamps
|
|
598
|
+
return (@timestamps || []) if @timestamps || !@content || @content.empty?
|
|
599
|
+
|
|
600
|
+
@timestamps = []
|
|
601
|
+
|
|
602
|
+
@content.scan(/<t:(-?\d{1,13})(?::(t|T|d|D|f|F|s|S|R))?>/) do |time, style|
|
|
603
|
+
# If it's not between these values, Discord won't show it, so don't bother.
|
|
604
|
+
if (time = time.to_i).between?(-8_640_000_000_000, 8_640_000_000_000)
|
|
605
|
+
@timestamps << TimestampMarkdown.new(Time.at(time), style)
|
|
606
|
+
end
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
@timestamps
|
|
610
|
+
end
|
|
611
|
+
end
|
|
612
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OnyxCord
|
|
4
|
+
# A rich-presence activity attached to a message.
|
|
5
|
+
class MessageActivity
|
|
6
|
+
# Map of activity types.
|
|
7
|
+
TYPES = {
|
|
8
|
+
join: 1,
|
|
9
|
+
spectate: 2,
|
|
10
|
+
listen: 3,
|
|
11
|
+
join_request: 5
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
# @return [Integer] the type of the activity.
|
|
15
|
+
attr_reader :type
|
|
16
|
+
|
|
17
|
+
# @return [String, nil] the party ID of the activity.
|
|
18
|
+
attr_reader :party_id
|
|
19
|
+
|
|
20
|
+
# @!visibility private
|
|
21
|
+
def initialize(data, bot)
|
|
22
|
+
@bot = bot
|
|
23
|
+
@type = data['type']
|
|
24
|
+
@party_id = data['party_id']
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @!method join?
|
|
28
|
+
# @return [true, false] whether or not the activity type is join.
|
|
29
|
+
# @!method spectate?
|
|
30
|
+
# @return [true, false] whether or not the activity type is spectate.
|
|
31
|
+
# @!method listen?
|
|
32
|
+
# @return [true, false] whether or not the activity type is listen.
|
|
33
|
+
# @!method join_request?
|
|
34
|
+
# @return [true, false] whether or not the activity type is a join request.
|
|
35
|
+
TYPES.each do |name, value|
|
|
36
|
+
define_method("#{name}?") do
|
|
37
|
+
@type == value
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|