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,1614 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OnyxCord
|
|
4
|
+
# Basic attributes a server should have
|
|
5
|
+
module ServerAttributes
|
|
6
|
+
# @return [String] this server's name.
|
|
7
|
+
attr_reader :name
|
|
8
|
+
|
|
9
|
+
# @return [String] the hexadecimal ID used to identify this server's icon.
|
|
10
|
+
attr_reader :icon_id
|
|
11
|
+
|
|
12
|
+
# Utility method to get a server's icon URL.
|
|
13
|
+
# @param format [String] The URL will default to `webp`. You can otherwise specify one of `jpg`
|
|
14
|
+
# or `png` to override this.
|
|
15
|
+
# @return [String, nil] The URL to the server's icon, or `nil` if the server hasn't set an icon.
|
|
16
|
+
def icon_url(format: 'webp')
|
|
17
|
+
API.icon_url(@id, @icon_id, format) if @icon_id
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# An isolated collection of channels and member's on Discord.
|
|
22
|
+
class Server
|
|
23
|
+
include IDObject
|
|
24
|
+
include ServerAttributes
|
|
25
|
+
|
|
26
|
+
# @return [String] the ID of the region the server is on (e.g. `amsterdam`).
|
|
27
|
+
attr_reader :region_id
|
|
28
|
+
|
|
29
|
+
# @return [Array<Channel>] an array of all the channels (text and voice) on this server.
|
|
30
|
+
attr_reader :channels
|
|
31
|
+
|
|
32
|
+
# @return [Hash<Integer => Emoji>] a hash of all the emoji available on this server.
|
|
33
|
+
attr_reader :emoji
|
|
34
|
+
alias_method :emojis, :emoji
|
|
35
|
+
|
|
36
|
+
# @return [true, false] whether or not this server is large (members > 100). If it is,
|
|
37
|
+
# it means the members list may be inaccurate for a couple seconds after starting up the bot.
|
|
38
|
+
attr_reader :large
|
|
39
|
+
alias_method :large?, :large
|
|
40
|
+
|
|
41
|
+
# @return [Array<Symbol>] the features of the server (eg. "INVITE_SPLASH")
|
|
42
|
+
attr_reader :features
|
|
43
|
+
|
|
44
|
+
# @return [Integer] the absolute number of members on this server, offline or not.
|
|
45
|
+
attr_reader :member_count
|
|
46
|
+
|
|
47
|
+
# @return [Integer] the amount of time after which a voice user gets moved into the AFK channel, in seconds.
|
|
48
|
+
attr_reader :afk_timeout
|
|
49
|
+
|
|
50
|
+
# @return [Hash<Integer => VoiceState>] the hash (user ID => voice state) of voice states of members on this server
|
|
51
|
+
attr_reader :voice_states
|
|
52
|
+
|
|
53
|
+
# @return [Integer] the server's amount of Nitro boosters, 0 if no one has boosted.
|
|
54
|
+
attr_reader :booster_count
|
|
55
|
+
|
|
56
|
+
# @return [Integer] the server's Nitro boost level, 0 if no level.
|
|
57
|
+
attr_reader :boost_level
|
|
58
|
+
|
|
59
|
+
# @return [String] the preferred locale of the server. Used in server discovery and notices from Discord.
|
|
60
|
+
attr_reader :locale
|
|
61
|
+
|
|
62
|
+
# @return [String, nil] the description of the server. Shown in server discovery and external embeds.
|
|
63
|
+
attr_reader :description
|
|
64
|
+
|
|
65
|
+
# @return [String, nil] the hash of the server's banner image or GIF.
|
|
66
|
+
attr_reader :banner_id
|
|
67
|
+
|
|
68
|
+
# @return [String, nil] the hash of the server's invite splash image.
|
|
69
|
+
attr_reader :splash_id
|
|
70
|
+
alias_method :splash_hash, :splash_id
|
|
71
|
+
|
|
72
|
+
# @return [Integer] the maximum number of members that can join the server.
|
|
73
|
+
attr_reader :max_member_count
|
|
74
|
+
|
|
75
|
+
# @return [String, nil] the code of the server's custom vanity invite link.
|
|
76
|
+
attr_reader :vanity_invite_code
|
|
77
|
+
|
|
78
|
+
# @return [Integer, nil] the maximum number of members that can concurrently be online in the server.
|
|
79
|
+
# Always set to `nil` except for the largest of servers.
|
|
80
|
+
attr_reader :max_presence_count
|
|
81
|
+
|
|
82
|
+
# @return [String, nil] the hash of the server's discovery splash image.
|
|
83
|
+
attr_reader :discovery_splash_id
|
|
84
|
+
|
|
85
|
+
# @return [Integer] the flags for the server's designated system channel.
|
|
86
|
+
attr_reader :system_channel_flags
|
|
87
|
+
|
|
88
|
+
# @return [Integer] the maximum number of members that can concurrently watch a stream in a video channel.
|
|
89
|
+
attr_reader :max_video_channel_members
|
|
90
|
+
|
|
91
|
+
# @return [Integer] the maximum number of members that can concurrently watch a stream in a stage channel.
|
|
92
|
+
attr_reader :max_stage_video_channel_members
|
|
93
|
+
|
|
94
|
+
# @return [true, false] whether or not the server has the boost progress bar enabled.
|
|
95
|
+
attr_reader :boost_progress_bar
|
|
96
|
+
alias_method :boost_progress_bar?, :boost_progress_bar
|
|
97
|
+
|
|
98
|
+
# @return [Time, nil] the time at when the last raid was detected on the server.
|
|
99
|
+
attr_reader :raid_detected_at
|
|
100
|
+
|
|
101
|
+
# @return [Time, nil] the time at when DM spam was last detected on the server.
|
|
102
|
+
attr_reader :dm_spam_detected_at
|
|
103
|
+
|
|
104
|
+
# @return [Time, nil] the time at when invites will be re-enabled on the server.
|
|
105
|
+
attr_reader :invites_disabled_until
|
|
106
|
+
|
|
107
|
+
# @return [Time, nil] the time at when non-friend direct messages will be re-enabled on the server.
|
|
108
|
+
attr_reader :dms_disabled_until
|
|
109
|
+
|
|
110
|
+
# @!visibility private
|
|
111
|
+
def initialize(data, bot)
|
|
112
|
+
@bot = bot
|
|
113
|
+
@id = data['id'].to_i
|
|
114
|
+
@members = {}
|
|
115
|
+
@voice_states = {}
|
|
116
|
+
@emoji = {}
|
|
117
|
+
@channels = []
|
|
118
|
+
@channels_by_id = {}
|
|
119
|
+
@scheduled_events = {}
|
|
120
|
+
|
|
121
|
+
update_data(data)
|
|
122
|
+
|
|
123
|
+
# Whether this server's members have been chunked (resolved using op 8 and GUILD_MEMBERS_CHUNK) yet
|
|
124
|
+
@chunked = false
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# @return [Member] The server owner.
|
|
128
|
+
def owner
|
|
129
|
+
member(@owner_id)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# The default channel is the text channel on this server with the highest position
|
|
133
|
+
# that the bot has Read Messages permission on.
|
|
134
|
+
# @param send_messages [true, false] whether to additionally consider if the bot has Send Messages permission
|
|
135
|
+
# @return [Channel, nil] The default channel on this server, or `nil` if there are no channels that the bot can read.
|
|
136
|
+
def default_channel(send_messages = false)
|
|
137
|
+
bot_member = member(@bot.profile)
|
|
138
|
+
text_channels.sort_by { |e| [e.position, e.id] }.find do |e|
|
|
139
|
+
if send_messages
|
|
140
|
+
bot_member.can_read_messages?(e) && bot_member.can_send_messages?(e)
|
|
141
|
+
else
|
|
142
|
+
bot_member.can_read_messages?(e)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
alias_method :general_channel, :default_channel
|
|
148
|
+
|
|
149
|
+
# @return [Role] The @everyone role on this server
|
|
150
|
+
def everyone_role
|
|
151
|
+
@roles[@id]
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# @return [Array<Role>] an array of all the roles available on this server.
|
|
155
|
+
def roles
|
|
156
|
+
@roles.values
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Gets a role on this server based on its ID.
|
|
160
|
+
# @param id [String, Integer] The role ID to look for.
|
|
161
|
+
# @return [Role, nil] The role identified by the ID, or `nil` if it couldn't be found.
|
|
162
|
+
def role(id)
|
|
163
|
+
@roles[id.resolve_id]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Get a mapping of role IDs to the amount of members who have the role.
|
|
167
|
+
# @example Print out the name of the roles in a server followed by the role's member count.
|
|
168
|
+
# server = bot.server(81384788765712384)
|
|
169
|
+
#
|
|
170
|
+
# server.role_member_counts.each do |id, count|
|
|
171
|
+
# puts("Name: #{server.role(id).name}, Count: #{count}")
|
|
172
|
+
# end
|
|
173
|
+
# @return [Hash<Integer => Integer>] A hash mapping role IDs to their respective member counts.
|
|
174
|
+
def role_member_counts
|
|
175
|
+
response = JSON.parse(API::Server.role_member_counts(@bot.token, @id))
|
|
176
|
+
response.transform_keys!(&:to_i)
|
|
177
|
+
response.tap { |hash| hash[@id] = @member_count }
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Gets a member on this server based on user ID
|
|
181
|
+
# @param id [Integer] The user ID to look for
|
|
182
|
+
# @param request [true, false] Whether the member should be requested from Discord if it's not cached
|
|
183
|
+
def member(id, request = true)
|
|
184
|
+
id = id.resolve_id
|
|
185
|
+
return @members[id] if member_cached?(id)
|
|
186
|
+
return nil unless request
|
|
187
|
+
|
|
188
|
+
member = @bot.member(self, id)
|
|
189
|
+
@members[id] = member unless member.nil?
|
|
190
|
+
rescue StandardError
|
|
191
|
+
nil
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# @return [Array<Member>] an array of all the members on this server.
|
|
195
|
+
# @raise [RuntimeError] if the bot was not started with the :server_member intent
|
|
196
|
+
def members
|
|
197
|
+
return @members.values if @chunked
|
|
198
|
+
|
|
199
|
+
@bot.debug("Members for server #{@id} not chunked yet - initiating")
|
|
200
|
+
|
|
201
|
+
# If the SERVER_MEMBERS intent flag isn't set, the gateway won't respond when we ask for members.
|
|
202
|
+
raise 'The :server_members intent is required to get server members' if @bot.gateway.intents.nobits?(INTENTS[:server_members])
|
|
203
|
+
|
|
204
|
+
@bot.request_chunks(@id)
|
|
205
|
+
sleep 0.05 until @chunked
|
|
206
|
+
@members.values
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
alias_method :users, :members
|
|
210
|
+
|
|
211
|
+
# @return [Array<Member>] an array of all the bot members on this server.
|
|
212
|
+
def bot_members
|
|
213
|
+
members.select(&:bot_account?)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# @return [Array<Member>] an array of all the non bot members on this server.
|
|
217
|
+
def non_bot_members
|
|
218
|
+
members.reject(&:bot_account?)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# @return [Member] the bot's own `Member` on this server
|
|
222
|
+
def bot
|
|
223
|
+
member(@bot.profile)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# @return [Array<Integration>] an array of the integrations in this server.
|
|
227
|
+
# @note If the server has more than 50 integrations, they cannot be accessed.
|
|
228
|
+
def integrations
|
|
229
|
+
integration = JSON.parse(API::Server.integrations(@bot.token, @id))
|
|
230
|
+
integration.map { |element| Integration.new(element, @bot, self) }
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# @param action [Symbol] The action to only include.
|
|
234
|
+
# @param user [User, String, Integer] The user, or their ID, to filter entries to.
|
|
235
|
+
# @param limit [Integer] The amount of entries to limit it to.
|
|
236
|
+
# @param before [Entry, String, Integer] The entry, or its ID, to use to not include all entries after it.
|
|
237
|
+
# @return [AuditLogs] The server's audit logs.
|
|
238
|
+
def audit_logs(action: nil, user: nil, limit: 50, before: nil)
|
|
239
|
+
raise 'Invalid audit log action!' if action && AuditLogs::ACTIONS.key(action).nil?
|
|
240
|
+
|
|
241
|
+
action = AuditLogs::ACTIONS.key(action)
|
|
242
|
+
user = user.resolve_id if user
|
|
243
|
+
before = before.resolve_id if before
|
|
244
|
+
AuditLogs.new(self, @bot, JSON.parse(API::Server.audit_logs(@bot.token, @id, limit, user, action, before)))
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# @note For internal use only
|
|
248
|
+
# @!visibility private
|
|
249
|
+
def cache_widget_data(data = nil)
|
|
250
|
+
data ||= if bot.permission?(:manage_server)
|
|
251
|
+
JSON.parse(API::Server.widget(@bot.token, @id))
|
|
252
|
+
else
|
|
253
|
+
return update_data(nil)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
@widget_enabled = data['enabled']
|
|
257
|
+
@widget_channel_id = data['channel_id']
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# @return [true, false] whether or not the server has widget enabled
|
|
261
|
+
def widget_enabled?
|
|
262
|
+
cache_widget_data if @widget_enabled.nil?
|
|
263
|
+
@widget_enabled
|
|
264
|
+
end
|
|
265
|
+
alias_method :widget?, :widget_enabled?
|
|
266
|
+
alias_method :embed_enabled, :widget_enabled?
|
|
267
|
+
alias_method :embed?, :widget_enabled?
|
|
268
|
+
|
|
269
|
+
# @return [Channel, nil] the channel the server widget will make an invite for.
|
|
270
|
+
def widget_channel
|
|
271
|
+
cache_widget_data if @widget_enabled.nil?
|
|
272
|
+
@bot.channel(@widget_channel_id) if @widget_channel_id
|
|
273
|
+
end
|
|
274
|
+
alias_method :embed_channel, :widget_channel
|
|
275
|
+
|
|
276
|
+
# Sets whether this server's widget is enabled
|
|
277
|
+
# @param value [true, false]
|
|
278
|
+
# @deprecated Please migrate to using {#modify} with the `widget_enabled:` parameter.
|
|
279
|
+
def widget_enabled=(value)
|
|
280
|
+
modify_widget(value, widget_channel)
|
|
281
|
+
end
|
|
282
|
+
alias_method :embed_enabled=, :widget_enabled=
|
|
283
|
+
|
|
284
|
+
# Sets whether this server's widget is enabled
|
|
285
|
+
# @param value [true, false]
|
|
286
|
+
# @param reason [String, nil] the reason to be shown in the audit log for this action
|
|
287
|
+
# @deprecated Please migrate to using {#modify} with the `widget_enabled:` parameter.
|
|
288
|
+
def set_widget_enabled(value, reason = nil)
|
|
289
|
+
modify_widget(value, widget_channel, reason)
|
|
290
|
+
end
|
|
291
|
+
alias_method :set_embed_enabled, :set_widget_enabled
|
|
292
|
+
|
|
293
|
+
# Changes the channel on the server's widget
|
|
294
|
+
# @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the widget
|
|
295
|
+
# @deprecated Please migrate to using {#modify} with the `widget_channel:` parameter.
|
|
296
|
+
def widget_channel=(channel)
|
|
297
|
+
modify_widget(widget?, channel)
|
|
298
|
+
end
|
|
299
|
+
alias_method :embed_channel=, :widget_channel=
|
|
300
|
+
|
|
301
|
+
# Changes the channel on the server's widget
|
|
302
|
+
# @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the widget
|
|
303
|
+
# @param reason [String, nil] the reason to be shown in the audit log for this action
|
|
304
|
+
# @deprecated Please migrate to using {#modify} with the `widget_channel:` parameter.
|
|
305
|
+
def set_widget_channel(channel, reason = nil)
|
|
306
|
+
modify_widget(widget?, channel, reason)
|
|
307
|
+
end
|
|
308
|
+
alias_method :set_embed_channel, :set_widget_channel
|
|
309
|
+
|
|
310
|
+
# Changes the channel on the server's widget, and sets whether it is enabled.
|
|
311
|
+
# @param enabled [true, false] whether the widget is enabled
|
|
312
|
+
# @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the widget
|
|
313
|
+
# @param reason [String, nil] the reason to be shown in the audit log for this action
|
|
314
|
+
# @deprecated Please migrate to using {#modify} with the `widget_enabled:` and `widget_channel:` parameters.
|
|
315
|
+
def modify_widget(enabled, channel, reason = nil)
|
|
316
|
+
cache_widget_data if @widget_enabled.nil?
|
|
317
|
+
channel_id = channel ? channel.resolve_id : @widget_channel_id
|
|
318
|
+
cache_widget_data(JSON.parse(API::Server.modify_widget(@bot.token, @id, enabled, channel_id, reason)))
|
|
319
|
+
end
|
|
320
|
+
alias_method :modify_embed, :modify_widget
|
|
321
|
+
|
|
322
|
+
# @param include_idle [true, false] Whether to count idle members as online.
|
|
323
|
+
# @param include_bots [true, false] Whether to include bot accounts in the count.
|
|
324
|
+
# @return [Array<Member>] an array of online members on this server.
|
|
325
|
+
def online_members(include_idle: false, include_bots: true)
|
|
326
|
+
@members.values.select do |e|
|
|
327
|
+
((include_idle ? e.idle? : false) || e.online?) && (include_bots ? true : !e.bot_account?)
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
alias_method :online_users, :online_members
|
|
332
|
+
|
|
333
|
+
# Adds a member to this guild that has granted this bot's application an OAuth2 access token
|
|
334
|
+
# with the `guilds.join` scope.
|
|
335
|
+
# For more information about Discord's OAuth2 implementation, see: https://discord.com/developers/docs/topics/oauth2
|
|
336
|
+
# @note Your bot must be present in this server, and have permission to create instant invites for this to work.
|
|
337
|
+
# @param user [User, String, Integer] the user, or ID of the user to add to this server
|
|
338
|
+
# @param access_token [String] the OAuth2 Bearer token that has been granted the `guilds.join` scope
|
|
339
|
+
# @param nick [String] the nickname to give this member upon joining
|
|
340
|
+
# @param roles [Role, Array<Role, String, Integer>] the role (or roles) to give this member upon joining
|
|
341
|
+
# @param deaf [true, false] whether this member will be server deafened upon joining
|
|
342
|
+
# @param mute [true, false] whether this member will be server muted upon joining
|
|
343
|
+
# @return [Member, nil] the created member, or `nil` if the user is already a member of this server.
|
|
344
|
+
def add_member_using_token(user, access_token, nick: nil, roles: [], deaf: false, mute: false)
|
|
345
|
+
user_id = user.resolve_id
|
|
346
|
+
roles = roles.is_a?(Array) ? roles.map(&:resolve_id) : [roles.resolve_id]
|
|
347
|
+
response = API::Server.add_member(@bot.token, @id, user_id, access_token, nick, roles, deaf, mute)
|
|
348
|
+
return nil if response.empty?
|
|
349
|
+
|
|
350
|
+
add_member Member.new(JSON.parse(response), self, @bot)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Returns the amount of members that are candidates for pruning
|
|
354
|
+
# @param days [Integer] the number of days to consider for inactivity
|
|
355
|
+
# @return [Integer] number of members to be removed
|
|
356
|
+
# @raise [ArgumentError] if days is not between 1 and 30 (inclusive)
|
|
357
|
+
def prune_count(days)
|
|
358
|
+
raise ArgumentError, 'Days must be between 1 and 30' unless days.between?(1, 30)
|
|
359
|
+
|
|
360
|
+
response = JSON.parse API::Server.prune_count(@bot.token, @id, days)
|
|
361
|
+
response['pruned']
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# Prunes (kicks) an amount of members for inactivity
|
|
365
|
+
# @param days [Integer] the number of days to consider for inactivity (between 1 and 30)
|
|
366
|
+
# @param reason [String] The reason the for the prune.
|
|
367
|
+
# @return [Integer] the number of members removed at the end of the operation
|
|
368
|
+
# @raise [ArgumentError] if days is not between 1 and 30 (inclusive)
|
|
369
|
+
def begin_prune(days, reason = nil)
|
|
370
|
+
raise ArgumentError, 'Days must be between 1 and 30' unless days.between?(1, 30)
|
|
371
|
+
|
|
372
|
+
response = JSON.parse API::Server.begin_prune(@bot.token, @id, days, reason)
|
|
373
|
+
response['pruned']
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
alias_method :prune, :begin_prune
|
|
377
|
+
|
|
378
|
+
# @return [Array<Channel>] an array of text channels on this server
|
|
379
|
+
def text_channels
|
|
380
|
+
@channels.select(&:text?)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# @return [Array<Channel>] an array of voice channels on this server
|
|
384
|
+
def voice_channels
|
|
385
|
+
@channels.select(&:voice?)
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# @return [Array<Channel>] an array of category channels on this server
|
|
389
|
+
def categories
|
|
390
|
+
@channels.select(&:category?)
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# @return [Array<Channel>] an array of channels on this server that are not in a category
|
|
394
|
+
def orphan_channels
|
|
395
|
+
@channels.reject { |c| c.parent || c.category? }
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# @return [ServerPreview] the preview of this server shown in the discovery page.
|
|
399
|
+
def preview
|
|
400
|
+
@bot.server_preview(@id)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# @return [String, nil] the widget URL to the server that displays the amount of online members in a
|
|
404
|
+
# stylish way. `nil` if the widget is not enabled.
|
|
405
|
+
def widget_url
|
|
406
|
+
update_data if @widget_enabled.nil?
|
|
407
|
+
|
|
408
|
+
API.widget_url(@id) if @widget_enabled
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# @param style [Symbol] The style the picture should have. Possible styles are:
|
|
412
|
+
# * `:banner1` creates a rectangular image with the server name, member count and icon, a "Powered by Discord" message on the bottom and an arrow on the right.
|
|
413
|
+
# * `:banner2` creates a less tall rectangular image that has the same information as `banner1`, but the Discord logo on the right - together with the arrow and separated by a diagonal separator.
|
|
414
|
+
# * `:banner3` creates an image similar in size to `banner1`, but it has the arrow in the bottom part, next to the Discord logo and with a "Chat now" text.
|
|
415
|
+
# * `:banner4` creates a tall, almost square, image that prominently features the Discord logo at the top and has a "Join my server" in a pill-style button on the bottom. The information about the server is in the same format as the other three `banner` styles.
|
|
416
|
+
# * `:shield` creates a very small, long rectangle, of the style you'd find at the top of GitHub `README.md` files. It features a small version of the Discord logo at the left and the member count at the right.
|
|
417
|
+
# @return [String, nil] the widget banner URL to the server that displays the amount of online members,
|
|
418
|
+
# server icon and server name in a stylish way. `nil` if the widget is not enabled.
|
|
419
|
+
def widget_banner_url(style)
|
|
420
|
+
update_data if @widget_enabled.nil?
|
|
421
|
+
|
|
422
|
+
API.widget_url(@id, style) if @widget_enabled
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# Utility method to get a server's splash URL.
|
|
426
|
+
# @param format [String] The URL will default to `webp`. You can otherwise specify one of `jpg` or `png` to
|
|
427
|
+
# override this.
|
|
428
|
+
# @return [String, nil] The URL to the server's splash image, or `nil` if the server doesn't have a splash image.
|
|
429
|
+
def splash_url(format: 'webp')
|
|
430
|
+
API.splash_url(@id, @splash_id, format) if @splash_id
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
# Utility method to get a server's banner URL.
|
|
434
|
+
# @param format [String] The URL will default to `webp`. You can otherwise specify one of `jpg` or `png` to
|
|
435
|
+
# override this.
|
|
436
|
+
# @return [String, nil] The URL to the server's banner image, or `nil` if the server doesn't have a banner image.
|
|
437
|
+
def banner_url(format: 'webp')
|
|
438
|
+
API.banner_url(@id, @banner_id, format) if @banner_id
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Utility method to get a server's discovery splash URL.
|
|
442
|
+
# @param format [String] The URL will default to `webp`. You can otherwise specify one of `jpg` or `png` to override this.
|
|
443
|
+
# @return [String, nil] The URL to the server's discovery splash image, or `nil` if the server doesn't have a discovery splash image.
|
|
444
|
+
def discovery_splash_url(format: 'webp')
|
|
445
|
+
API.discovery_splash_url(@id, @discovery_splash_id, format) if @discovery_splash_id
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# @return [String] a URL that a user can use to navigate to this server in the client
|
|
449
|
+
def link
|
|
450
|
+
"https://discord.com/channels/#{@id}"
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
alias_method :jump_link, :link
|
|
454
|
+
|
|
455
|
+
# Search the messages that have been sent in this server.
|
|
456
|
+
# @example Search for 200 messages from a user that contain an attachment.
|
|
457
|
+
# options = {
|
|
458
|
+
# limit: 200,
|
|
459
|
+
# contains: :file,
|
|
460
|
+
# authors: 171764626755813376
|
|
461
|
+
# }
|
|
462
|
+
#
|
|
463
|
+
# results = server.search_messages(**options)
|
|
464
|
+
# @example Search for all of the messages in a channel that mentions someone.
|
|
465
|
+
# options = {
|
|
466
|
+
# limit: nil,
|
|
467
|
+
# mentions: 171764626755813376,
|
|
468
|
+
# channels: 381891448884428801
|
|
469
|
+
# }
|
|
470
|
+
#
|
|
471
|
+
# results = server.search_messages(**options)
|
|
472
|
+
# @example Search for 105 messages that contain specific embed types, sorted by oldest to newest.
|
|
473
|
+
# options = {
|
|
474
|
+
# limit: 105,
|
|
475
|
+
# embed_types: %i[article image],
|
|
476
|
+
# sort_order: :ascending
|
|
477
|
+
# }
|
|
478
|
+
#
|
|
479
|
+
# results = server.search_messages(**options)
|
|
480
|
+
# @example Search for 30 messages sent between two dates that contain the word “time” and an @everyone ping.
|
|
481
|
+
# options = {
|
|
482
|
+
# limit: 30,
|
|
483
|
+
# content: 'time',
|
|
484
|
+
# mentions_everyone: true,
|
|
485
|
+
# after: Time.parse("December 16th, 2020"),
|
|
486
|
+
# before: Time.parse("December 25th, 2020")
|
|
487
|
+
# }
|
|
488
|
+
#
|
|
489
|
+
# results = server.search_messages(**options)
|
|
490
|
+
# @example Search for 500 messages that reply to a specific message, contain a Ruby file, and were sent by a bot account.
|
|
491
|
+
# options = {
|
|
492
|
+
# limit: 500,
|
|
493
|
+
# author_types: :bot,
|
|
494
|
+
# file_extensions: '.rb',
|
|
495
|
+
# reply_messages: 1454184993923268660
|
|
496
|
+
# }
|
|
497
|
+
#
|
|
498
|
+
# results = server.search_messages(**options)
|
|
499
|
+
# @param limit [Integer, nil] The maximum number of messages to return, or `nil` to fetch all of the messages that match the search query.
|
|
500
|
+
# @param offset [Integer, nil] The number of messages between 0-9975 to offset the search query by.
|
|
501
|
+
# @param before [Time, #resolve_id, nil] Get messages sent before this timestamp.
|
|
502
|
+
# @param after [Time, #resolve_id, nil] Get messages sent after this timestamp.
|
|
503
|
+
# @param content [String, #to_s, nil] Get messages with matching message content.
|
|
504
|
+
# @param slop [Integer, nil] The amount of variation allowed between the placement of words when matching against message content; between 0-100.
|
|
505
|
+
# @param channels [Array<Channel, Integer, String>, Channel, Integer, String, nil] Get messages that were sent in these channels.
|
|
506
|
+
# @param authors [Array<#resolve_id>, #resolve_id, nil] Get messages that were created by these authors.
|
|
507
|
+
# @param author_types [Array<String, Symbol>, String, Symbol, nil] Get messages that were created by these author types: `user`, `bot`, or `webhook`.
|
|
508
|
+
# @param mentions [Array<#resolve_id>, #resolve_id, nil] Get messages that mention these users or members.
|
|
509
|
+
# @param role_mentions [Array<Role, Integer, String>, Role, Integer, String, nil] Get messages that mention these roles.
|
|
510
|
+
# @param mentions_everyone [true, false, nil] Get messages that mention the @everyone role.
|
|
511
|
+
# @param reply_users [Array<#resolve_id>, #resolve_id, nil] Get messages that replied to these users or members.
|
|
512
|
+
# @param reply_messages [Array<Message, Integer, String>, Message, Integer, String, nil] Get messages that replied to these messages.
|
|
513
|
+
# @param pinned [true, false, nil] Get messages that are pinned.
|
|
514
|
+
# @param contains [Array<String, Symbol>, String, Symbol, nil] Get messages that contain specific fields, e.g. `file`, `poll`, `sound`, etc.
|
|
515
|
+
# @param embed_types [Array<String, Symbol>, String, Symbol, nil] Get messages that contain matching embed types.
|
|
516
|
+
# @param embed_providers [Array<String, Symbol>, String, Symbol, nil] Get messages that contain embeds from specific providers.
|
|
517
|
+
# @param link_hosts [Array<String, Symbol>, String, Symbol, nil] Get messages that contain matching link hostnames, e.g. `discord.com`.
|
|
518
|
+
# @param file_names [Array<String, Symbol, Attachment>, String, Symbol, Attachment, nil] Get messages that contain matching attachment filenames.
|
|
519
|
+
# @param file_extensions [Array<String, Symbol>, String, Symbol, nil] Get messages that contain matching attachment file extensions, e.g. `.rb`, `.mp3`, etc.
|
|
520
|
+
# @param include_nsfw [true, false, nil] Whether or not to include messages that have been sent in NSFW channels.
|
|
521
|
+
# @param sort_by [Symbol, String, nil] Whether to sort the returned messages by their `:creation_time`, or `:relevance` to the search query.
|
|
522
|
+
# @param sort_order [Symbol, string, nil] Whether to order the returned messages in `:descending`, or `:ascending` order. Not respected when sorting by `:relevance`.
|
|
523
|
+
# @raise [OnyxCord::Errors::NoPermission] This may occur when the application has not enabled the `MESSAGE_CONTENT` privileged intent on the Discord Developer Portal.
|
|
524
|
+
# @note Messages with GIFs sent before February 24th, 2026 may not be returned under the `gif` embed type when using the `embed_types:` parameter.
|
|
525
|
+
# @note Messages fetched via this method will not contain reactions. This means that {Message#reactions} will **always** return an empty array, even if the message has reactions.
|
|
526
|
+
# @return [SearchedMessages] the results of the search query.
|
|
527
|
+
def search_messages(
|
|
528
|
+
limit: 25, offset: nil, before: nil, after: nil, content: nil, slop: 2, channels: nil, authors: nil, author_types: nil,
|
|
529
|
+
mentions: nil, role_mentions: nil, mentions_everyone: nil, reply_users: nil, reply_messages: nil, pinned: nil, contains: nil,
|
|
530
|
+
embed_types: nil, embed_providers: nil, link_hosts: nil, file_names: nil, file_extensions: nil, include_nsfw: true, sort_by: nil,
|
|
531
|
+
sort_order: :descending
|
|
532
|
+
)
|
|
533
|
+
sort_order = case sort_order&.to_sym
|
|
534
|
+
when nil, :desc, :descending, :newest_first
|
|
535
|
+
:desc
|
|
536
|
+
when :asc, :ascending, :oldest_first
|
|
537
|
+
:asc
|
|
538
|
+
else
|
|
539
|
+
raise ArgumentError, "Invalid value for the 'sort_order' parameter"
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
sort_by = case sort_by&.to_sym
|
|
543
|
+
when nil, :timestamp, :creation_time
|
|
544
|
+
:timestamp
|
|
545
|
+
when :relevance, :match_score
|
|
546
|
+
:relevance
|
|
547
|
+
else
|
|
548
|
+
raise ArgumentError, "Invalid value for the 'sort_by' parameter"
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
options = {
|
|
552
|
+
limit: limit && limit <= 25 ? limit : 25,
|
|
553
|
+
max_id: before.is_a?(Time) ? IDObject.synthesise(before) : before&.resolve_id,
|
|
554
|
+
min_id: after.is_a?(Time) ? IDObject.synthesise(after) : after&.resolve_id,
|
|
555
|
+
offset: offset || 0,
|
|
556
|
+
slop: slop,
|
|
557
|
+
content: content&.to_s,
|
|
558
|
+
channel_id: channels ? Array(channels).map(&:resolve_id) : channels,
|
|
559
|
+
author_type: author_types ? Array(author_types) : author_types,
|
|
560
|
+
author_id: authors ? Array(authors).map(&:resolve_id) : authors,
|
|
561
|
+
mentions: mentions ? Array(mentions).map(&:resolve_id) : mentions,
|
|
562
|
+
mentions_role_id: role_mentions ? Array(role_mentions).map(&:resolve_id) : role_mentions,
|
|
563
|
+
mention_everyone: mentions_everyone,
|
|
564
|
+
replied_to_user_id: reply_users ? Array(reply_users).map(&:resolve_id) : reply_users,
|
|
565
|
+
replied_to_message_id: reply_messages ? Array(reply_messages).map(&:resolve_id) : reply_messages,
|
|
566
|
+
pinned: pinned,
|
|
567
|
+
has: contains ? Array(contains) : contains,
|
|
568
|
+
embed_type: embed_types ? Array(embed_types) : embed_types,
|
|
569
|
+
embed_provider: embed_providers ? Array(embed_providers) : embed_providers,
|
|
570
|
+
link_hostname: link_hosts ? Array(link_hosts) : link_hosts,
|
|
571
|
+
attachment_filename: (Array(file_names).map { |file| file.is_a?(Attachment) ? file.filename : file } if file_names),
|
|
572
|
+
attachment_extension: file_extensions ? Array(file_extensions).map { |type| type.to_s.delete_prefix('.') } : file_extensions,
|
|
573
|
+
sort_by: sort_by,
|
|
574
|
+
sort_order: sort_order,
|
|
575
|
+
include_nsfw: include_nsfw
|
|
576
|
+
}.compact
|
|
577
|
+
|
|
578
|
+
raise ArgumentError, "The 'role_mentions' parameter cannot contain the everyone role" if options[:mentions_role_id]&.any?(@id)
|
|
579
|
+
|
|
580
|
+
# Only store the total message count from the first request.
|
|
581
|
+
total = nil
|
|
582
|
+
|
|
583
|
+
get_messages = lambda do |query|
|
|
584
|
+
data = JSON.parse(API::Server.search_messages(@bot.token, @id, **options, **query.compact))
|
|
585
|
+
total ||= data['total_results']
|
|
586
|
+
|
|
587
|
+
data['threads']&.each do |thread|
|
|
588
|
+
thread['member'] = data['members']&.find { |member| thread['id'] == member['id'] }
|
|
589
|
+
|
|
590
|
+
@bot.ensure_channel(thread, self)
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
data['messages'].collect { |nested_messages| Message.new(nested_messages[0], @bot) }
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
paginator = Paginator.new(limit, :down) do |page|
|
|
597
|
+
if sort_by == :relevance
|
|
598
|
+
if (count = (paginator.amount_fetched + options[:offset])) > 9975
|
|
599
|
+
[]
|
|
600
|
+
else
|
|
601
|
+
get_messages.call(offset: count)
|
|
602
|
+
end
|
|
603
|
+
elsif sort_order == :desc
|
|
604
|
+
get_messages.call(max_id: page&.last&.id, offset: page ? 0 : nil)
|
|
605
|
+
else
|
|
606
|
+
get_messages.call(min_id: page&.last&.id, offset: page ? 0 : nil)
|
|
607
|
+
end
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
SearchedMessages.new(paginator.to_a, total, @bot)
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
# Adds a role to the role cache
|
|
614
|
+
# @note For internal use only
|
|
615
|
+
# @!visibility private
|
|
616
|
+
def add_role(role)
|
|
617
|
+
@roles[role.id] = role
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
# Removes a role from the role cache
|
|
621
|
+
# @note For internal use only
|
|
622
|
+
# @!visibility private
|
|
623
|
+
def delete_role(role_id)
|
|
624
|
+
@roles.delete(role_id.resolve_id)
|
|
625
|
+
@members.each_value do |member|
|
|
626
|
+
new_roles = member.roles.reject { |r| r.id == role_id }
|
|
627
|
+
member.update_roles(new_roles)
|
|
628
|
+
end
|
|
629
|
+
@channels.each do |channel|
|
|
630
|
+
overwrites = channel.permission_overwrites.reject { |id, _| id == role_id }
|
|
631
|
+
channel.update_overwrites(overwrites)
|
|
632
|
+
end
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
# Updates the positions of all roles on the server
|
|
636
|
+
# @note For internal use only
|
|
637
|
+
# @!visibility private
|
|
638
|
+
def update_role_positions(role_positions, reason: nil)
|
|
639
|
+
response = JSON.parse(API::Server.update_role_positions(@bot.token, @id, role_positions, reason))
|
|
640
|
+
response.each { |data| role(data['id'].to_i)&.update_data(data) }
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
# Adds a member to the member cache.
|
|
644
|
+
# @note For internal use only
|
|
645
|
+
# @!visibility private
|
|
646
|
+
def add_member(member)
|
|
647
|
+
@member_count += 1
|
|
648
|
+
@members[member.id] = member
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
# Removes a member from the member cache.
|
|
652
|
+
# @note For internal use only
|
|
653
|
+
# @!visibility private
|
|
654
|
+
def delete_member(user_id)
|
|
655
|
+
@members.delete(user_id)
|
|
656
|
+
@member_count -= 1 unless @member_count <= 0
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
# Checks whether a member is cached
|
|
660
|
+
# @note For internal use only
|
|
661
|
+
# @!visibility private
|
|
662
|
+
def member_cached?(user_id)
|
|
663
|
+
@members.include?(user_id)
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
# Adds a member to the cache
|
|
667
|
+
# @note For internal use only
|
|
668
|
+
# @!visibility private
|
|
669
|
+
def cache_member(member)
|
|
670
|
+
@members[member.id] = member
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
# Adds a scheduled event to the cache
|
|
674
|
+
# @note For internal use only
|
|
675
|
+
# @!visibility private
|
|
676
|
+
def cache_scheduled_event(event)
|
|
677
|
+
@scheduled_events[event.id] = event
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
# Removes a scheduled event from the cache.
|
|
681
|
+
# @note For internal use only
|
|
682
|
+
# @!visibility private
|
|
683
|
+
def delete_scheduled_event(event)
|
|
684
|
+
@scheduled_events.delete(event.resolve_id)
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
# Updates a member's voice state
|
|
688
|
+
# @note For internal use only
|
|
689
|
+
# @!visibility private
|
|
690
|
+
def update_voice_state(data)
|
|
691
|
+
user_id = data['user_id'].to_i
|
|
692
|
+
|
|
693
|
+
if data['channel_id']
|
|
694
|
+
unless @voice_states[user_id]
|
|
695
|
+
# Create a new voice state for the user
|
|
696
|
+
@voice_states[user_id] = VoiceState.new(user_id)
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
# Update the existing voice state (or the one we just created)
|
|
700
|
+
channel = @channels_by_id[data['channel_id'].to_i]
|
|
701
|
+
@voice_states[user_id].update(
|
|
702
|
+
channel,
|
|
703
|
+
data['mute'],
|
|
704
|
+
data['deaf'],
|
|
705
|
+
data['self_mute'],
|
|
706
|
+
data['self_deaf']
|
|
707
|
+
)
|
|
708
|
+
else
|
|
709
|
+
# The user is not in a voice channel anymore, so delete its voice state
|
|
710
|
+
@voice_states.delete(user_id)
|
|
711
|
+
end
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
# Creates a channel on this server with the given name.
|
|
715
|
+
# @note If parent is provided, permission overwrites have the follow behavior:
|
|
716
|
+
#
|
|
717
|
+
# 1. If overwrites is null, the new channel inherits the parent's permissions.
|
|
718
|
+
# 2. If overwrites is [], the new channel inherits the parent's permissions.
|
|
719
|
+
# 3. If you supply one or more overwrites, the channel will be created with those permissions and ignore the parents.
|
|
720
|
+
#
|
|
721
|
+
# @param name [String] Name of the channel to create
|
|
722
|
+
# @param type [Integer, Symbol] Type of channel to create (0: text, 2: voice, 4: category, 5: news, 6: store)
|
|
723
|
+
# @param topic [String] the topic of this channel, if it will be a text channel
|
|
724
|
+
# @param bitrate [Integer] the bitrate of this channel, if it will be a voice channel
|
|
725
|
+
# @param user_limit [Integer] the user limit of this channel, if it will be a voice channel
|
|
726
|
+
# @param permission_overwrites [Array<Hash>, Array<Overwrite>] permission overwrites for this channel
|
|
727
|
+
# @param parent [Channel, String, Integer] parent category, or its ID, for this channel to be created in.
|
|
728
|
+
# @param nsfw [true, false] whether this channel should be created as nsfw
|
|
729
|
+
# @param rate_limit_per_user [Integer] how many seconds users need to wait in between messages.
|
|
730
|
+
# @param reason [String] The reason the for the creation of this channel.
|
|
731
|
+
# @return [Channel] the created channel.
|
|
732
|
+
# @raise [ArgumentError] if type is not 0 (text), 2 (voice), 4 (category), 5 (news), or 6 (store)
|
|
733
|
+
def create_channel(name, type = 0, topic: nil, bitrate: nil, user_limit: nil, permission_overwrites: nil, parent: nil, nsfw: false, rate_limit_per_user: nil, position: nil, reason: nil)
|
|
734
|
+
type = Channel::TYPES[type] if type.is_a?(Symbol)
|
|
735
|
+
raise ArgumentError, 'Channel type must be either 0 (text), 2 (voice), 4 (category), news (5), or store (6)!' unless [0, 2, 4, 5, 6].include?(type)
|
|
736
|
+
|
|
737
|
+
permission_overwrites.map! { |e| e.is_a?(Overwrite) ? e.to_hash : e } if permission_overwrites.is_a?(Array)
|
|
738
|
+
parent_id = parent.respond_to?(:resolve_id) ? parent.resolve_id : nil
|
|
739
|
+
response = API::Server.create_channel(@bot.token, @id, name, type, topic, bitrate, user_limit, permission_overwrites, parent_id, nsfw, rate_limit_per_user, position, reason)
|
|
740
|
+
Channel.new(JSON.parse(response), @bot)
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
# Creates a role on this server which can then be modified. It will be initialized
|
|
744
|
+
# with the regular role defaults the client uses unless specified, i.e. name is "new role",
|
|
745
|
+
# permissions are the default, colour is the default etc.
|
|
746
|
+
# @param name [String] Name of the role to create.
|
|
747
|
+
# @param colour [Integer, ColourRGB, #combined] The primary colour of the role to create.
|
|
748
|
+
# @param hoist [true, false] whether members of this role should be displayed seperately in the members list.
|
|
749
|
+
# @param mentionable [true, false] whether this role can mentioned by anyone in the server.
|
|
750
|
+
# @param permissions [Integer, Array<Symbol>, Permissions, #bits] The permissions to write to the new role.
|
|
751
|
+
# @param icon [String, #read, nil] The base64 encoded image data, or a file like object that responds to #read.
|
|
752
|
+
# @param unicode_emoji [String, nil] The unicode emoji of the role to create, or nil.
|
|
753
|
+
# @param display_icon [String, File, #read, nil] The icon to display for the role. Overrides the **icon** and **unicode_emoji** parameters if passed.
|
|
754
|
+
# @param reason [String] The reason the for the creation of this role.
|
|
755
|
+
# @param secondary_colour [Integer, ColourRGB, nil] The secondary colour of the role to create.
|
|
756
|
+
# @param tertiary_colour [Integer, ColourRGB, nil] The tertiary colour of the role to create.
|
|
757
|
+
# @return [Role] the created role.
|
|
758
|
+
def create_role(name: 'new role', colour: 0, hoist: false, mentionable: false, permissions: 104_324_161, secondary_colour: nil, tertiary_colour: nil, icon: nil, unicode_emoji: nil, display_icon: nil, reason: nil)
|
|
759
|
+
colour = colour.respond_to?(:combined) ? colour.combined : colour
|
|
760
|
+
|
|
761
|
+
permissions = if permissions.is_a?(Array)
|
|
762
|
+
Permissions.bits(permissions)
|
|
763
|
+
elsif permissions.respond_to?(:bits)
|
|
764
|
+
permissions.bits
|
|
765
|
+
else
|
|
766
|
+
permissions
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
icon = icon.respond_to?(:read) ? OnyxCord.encode64(icon) : icon
|
|
770
|
+
|
|
771
|
+
colours = {
|
|
772
|
+
primary_color: colour&.to_i,
|
|
773
|
+
tertiary_color: tertiary_colour&.to_i,
|
|
774
|
+
secondary_color: secondary_colour&.to_i
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if display_icon.is_a?(String)
|
|
778
|
+
unicode_emoji = display_icon
|
|
779
|
+
elsif display_icon.respond_to?(:read)
|
|
780
|
+
icon = OnyxCord.encode64(display_icon)
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
response = API::Server.create_role(@bot.token, @id, name, nil, hoist, mentionable, permissions&.to_s, reason, colours, icon, unicode_emoji)
|
|
784
|
+
|
|
785
|
+
role = Role.new(JSON.parse(response), @bot, self)
|
|
786
|
+
@roles[role.id] = role
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
# Adds a new custom emoji on this server.
|
|
790
|
+
# @param name [String] The name of emoji to create.
|
|
791
|
+
# @param image [String, #read] A base64 encoded string with the image data, or an object that responds to `#read`, such as `File`.
|
|
792
|
+
# @param roles [Array<Role, String, Integer>] An array of roles, or role IDs to be whitelisted for this emoji.
|
|
793
|
+
# @param reason [String] The reason the for the creation of this emoji.
|
|
794
|
+
# @return [Emoji] The emoji that has been added.
|
|
795
|
+
def add_emoji(name, image, roles = [], reason: nil)
|
|
796
|
+
image = image.respond_to?(:read) ? OnyxCord.encode64(image) : image
|
|
797
|
+
|
|
798
|
+
data = JSON.parse(API::Server.add_emoji(@bot.token, @id, image, name, roles.map(&:resolve_id), reason))
|
|
799
|
+
new_emoji = Emoji.new(data, @bot, self)
|
|
800
|
+
@emoji[new_emoji.id] = new_emoji
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
# Delete a custom emoji on this server
|
|
804
|
+
# @param emoji [Emoji, String, Integer] The emoji or emoji ID to be deleted.
|
|
805
|
+
# @param reason [String] The reason the for the deletion of this emoji.
|
|
806
|
+
def delete_emoji(emoji, reason: nil)
|
|
807
|
+
API::Server.delete_emoji(@bot.token, @id, emoji.resolve_id, reason)
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
# Changes the name and/or role whitelist of an emoji on this server.
|
|
811
|
+
# @param emoji [Emoji, String, Integer] The emoji or emoji ID to edit.
|
|
812
|
+
# @param name [String] The new name for the emoji.
|
|
813
|
+
# @param roles [Array<Role, String, Integer>] A new array of roles, or role IDs, to whitelist.
|
|
814
|
+
# @param reason [String] The reason for the editing of this emoji.
|
|
815
|
+
# @return [Emoji] The edited emoji.
|
|
816
|
+
def edit_emoji(emoji, name: nil, roles: nil, reason: nil)
|
|
817
|
+
emoji = @emoji[emoji.resolve_id]
|
|
818
|
+
data = JSON.parse(API::Server.edit_emoji(@bot.token, @id, emoji.resolve_id, name || emoji.name, (roles || emoji.roles).map(&:resolve_id), reason))
|
|
819
|
+
new_emoji = Emoji.new(data, @bot, self)
|
|
820
|
+
@emoji[new_emoji.id] = new_emoji
|
|
821
|
+
end
|
|
822
|
+
|
|
823
|
+
# The amount of emoji the server can have, based on its current Nitro Boost Level.
|
|
824
|
+
# @return [Integer] the max amount of emoji
|
|
825
|
+
def max_emoji
|
|
826
|
+
case @boost_level
|
|
827
|
+
when 1
|
|
828
|
+
100
|
|
829
|
+
when 2
|
|
830
|
+
150
|
|
831
|
+
when 3
|
|
832
|
+
250
|
|
833
|
+
else
|
|
834
|
+
50
|
|
835
|
+
end
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
# Searches a server for members that matches a username or a nickname.
|
|
839
|
+
# @param name [String] The username or nickname to search for.
|
|
840
|
+
# @param limit [Integer] The maximum number of members between 1-1000 to return. Returns 1 member by default.
|
|
841
|
+
# @return [Array<Member>, nil] An array of member objects that match the given parameters, or nil for no members.
|
|
842
|
+
def search_members(name:, limit: nil)
|
|
843
|
+
response = JSON.parse(API::Server.search_guild_members(@bot.token, @id, name, limit))
|
|
844
|
+
return nil if response.empty?
|
|
845
|
+
|
|
846
|
+
response.map { |mem| Member.new(mem, self, @bot) }
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
# Retrieve banned users from this server.
|
|
850
|
+
# @param limit [Integer] Number of users to return (up to maximum 1000, default 1000).
|
|
851
|
+
# @param before_id [Integer] Consider only users before given user id.
|
|
852
|
+
# @param after_id [Integer] Consider only users after given user id.
|
|
853
|
+
# @return [Array<ServerBan>] a list of banned users on this server and the reason they were banned.
|
|
854
|
+
def bans(limit: nil, before_id: nil, after_id: nil)
|
|
855
|
+
response = JSON.parse(API::Server.bans(@bot.token, @id, limit, before_id, after_id))
|
|
856
|
+
response.map do |e|
|
|
857
|
+
ServerBan.new(self, @bot.ensure_user(e['user']), e['reason'])
|
|
858
|
+
end
|
|
859
|
+
end
|
|
860
|
+
|
|
861
|
+
# Get the users who have been banned from the server.
|
|
862
|
+
# @param limit [Integer, nil] The max number of bans to return, or `nil` for no limit.
|
|
863
|
+
# @param after [User, Member, Time, Integer, String, nil] Get bans after this user ID.
|
|
864
|
+
# @param before [User, Member, Time, Integer, String, nil] Get bans before this user ID.
|
|
865
|
+
# @return [Array<ServerBan>] The users who have been banned from the server.
|
|
866
|
+
# @note When using the `before` parameter, bans will be sorted in descending order by user ID
|
|
867
|
+
# (newest users first), and in ascending order by user ID (oldest users first) otherwise.
|
|
868
|
+
def bans!(limit: 1000, before: nil, after: nil)
|
|
869
|
+
raise ArgumentError, "'before' and 'after' are mutually exclusive" if before && after
|
|
870
|
+
|
|
871
|
+
f_limit = limit && limit <= 1000 ? limit : 1000
|
|
872
|
+
f_after = after.is_a?(Time) ? IDObject.synthesize(after) : after&.resolve_id
|
|
873
|
+
f_before = before.is_a?(Time) ? IDObject.synthesize(before) : before&.resolve_id
|
|
874
|
+
|
|
875
|
+
get_bans = lambda do |before: nil, after: nil|
|
|
876
|
+
data = API::Server.bans(@bot.token, @id, f_limit, before&.id || f_before, after&.id || f_after)
|
|
877
|
+
JSON.parse(data).map { |ban| ServerBan.new(self, @bot.ensure_user(ban['user']), ban['reason']) }
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
paginator = Paginator.new(limit, before ? :up : :down) do |page|
|
|
881
|
+
if before
|
|
882
|
+
get_bans.call(before: page&.first&.user)
|
|
883
|
+
else
|
|
884
|
+
get_bans.call(after: page&.last&.user)
|
|
885
|
+
end
|
|
886
|
+
end
|
|
887
|
+
|
|
888
|
+
paginator.to_a
|
|
889
|
+
end
|
|
890
|
+
|
|
891
|
+
# Bans a user from this server.
|
|
892
|
+
# @param user [User, String, Integer] The user to ban.
|
|
893
|
+
# @param message_days [Integer] How many days worth of messages sent by the user should be deleted. This is deprecated and will be removed in 4.0.
|
|
894
|
+
# @param message_seconds [Integer] How many seconds of messages sent by the user should be deleted.
|
|
895
|
+
# @param reason [String] The reason the user is being banned.
|
|
896
|
+
def ban(user, message_days = 0, message_seconds: nil, reason: nil)
|
|
897
|
+
delete_messages = if message_days != 0 && message_days
|
|
898
|
+
message_days * 86_400
|
|
899
|
+
else
|
|
900
|
+
message_seconds || 0
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
API::Server.ban_user!(@bot.token, @id, user.resolve_id, delete_messages, reason)
|
|
904
|
+
end
|
|
905
|
+
|
|
906
|
+
# Unbans a previously banned user from this server.
|
|
907
|
+
# @param user [User, String, Integer] The user to unban.
|
|
908
|
+
# @param reason [String] The reason the user is being unbanned.
|
|
909
|
+
def unban(user, reason = nil)
|
|
910
|
+
API::Server.unban_user(@bot.token, @id, user.resolve_id, reason)
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
# Ban up to 200 users from this server in one go.
|
|
914
|
+
# @param users [Array<User, String, Integer>] Array of up to 200 users to ban.
|
|
915
|
+
# @param message_seconds [Integer] How many seconds of messages sent by the users should be deleted.
|
|
916
|
+
# @param reason [String] The reason these users are being banned.
|
|
917
|
+
# @return [BulkBan]
|
|
918
|
+
def bulk_ban(users:, message_seconds: 0, reason: nil)
|
|
919
|
+
raise ArgumentError, 'Can only ban between 1 and 200 users!' unless users.size.between?(1, 200)
|
|
920
|
+
|
|
921
|
+
return ban(users.first, 0, message_seconds: message_seconds, reason: reason) if users.size == 1
|
|
922
|
+
|
|
923
|
+
response = API::Server.bulk_ban(@bot.token, @id, users.map(&:resolve_id), message_seconds, reason)
|
|
924
|
+
BulkBan.new(JSON.parse(response), self, reason)
|
|
925
|
+
end
|
|
926
|
+
|
|
927
|
+
# Kicks a user from this server.
|
|
928
|
+
# @param user [User, String, Integer] The user to kick.
|
|
929
|
+
# @param reason [String] The reason the user is being kicked.
|
|
930
|
+
def kick(user, reason = nil)
|
|
931
|
+
API::Server.remove_member(@bot.token, @id, user.resolve_id, reason)
|
|
932
|
+
end
|
|
933
|
+
|
|
934
|
+
# Forcibly moves a user into a different voice channel.
|
|
935
|
+
# Only works if the bot has the permission needed and if the user is already connected to some voice channel on this server.
|
|
936
|
+
# @param user [User, String, Integer] The user to move.
|
|
937
|
+
# @param channel [Channel, String, Integer, nil] The voice channel to move into. (If nil, the user is disconnected from the voice channel)
|
|
938
|
+
def move(user, channel)
|
|
939
|
+
API::Server.update_member(@bot.token, @id, user.resolve_id, channel_id: channel&.resolve_id)
|
|
940
|
+
end
|
|
941
|
+
|
|
942
|
+
# Leave the server.
|
|
943
|
+
def leave
|
|
944
|
+
API::User.leave_server(@bot.token, @id)
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
# Sets the server's name.
|
|
948
|
+
# @param name [String] The new server name.
|
|
949
|
+
def name=(name)
|
|
950
|
+
modify(name: name)
|
|
951
|
+
end
|
|
952
|
+
|
|
953
|
+
# @return [Array<VoiceRegion>] collection of available voice regions to this guild
|
|
954
|
+
def available_voice_regions
|
|
955
|
+
return @available_voice_regions if @available_voice_regions
|
|
956
|
+
|
|
957
|
+
@available_voice_regions = {}
|
|
958
|
+
|
|
959
|
+
data = JSON.parse API::Server.regions(@bot.token, @id)
|
|
960
|
+
@available_voice_regions = data.map { |e| VoiceRegion.new e }
|
|
961
|
+
end
|
|
962
|
+
|
|
963
|
+
# @return [VoiceRegion, nil] voice region data for this server's region
|
|
964
|
+
# @note This may return `nil` if this server's voice region is deprecated.
|
|
965
|
+
def region
|
|
966
|
+
available_voice_regions.find { |e| e.id == @region_id }
|
|
967
|
+
end
|
|
968
|
+
|
|
969
|
+
# Moves the server to another region. This will cause a voice interruption of at most a second.
|
|
970
|
+
# @param region [String] The new region the server should be in.
|
|
971
|
+
def region=(region)
|
|
972
|
+
update_data(JSON.parse(API::Server.update!(@bot.token, @id, region: region.to_s)))
|
|
973
|
+
end
|
|
974
|
+
|
|
975
|
+
# Sets the server's icon.
|
|
976
|
+
# @param icon [String, #read, nil] The new icon, in base64-encoded JPG format.
|
|
977
|
+
def icon=(icon)
|
|
978
|
+
modify(icon: icon)
|
|
979
|
+
end
|
|
980
|
+
|
|
981
|
+
# Sets the server's AFK channel.
|
|
982
|
+
# @param afk_channel [Channel, nil] The new AFK channel, or `nil` if there should be none set.
|
|
983
|
+
def afk_channel=(afk_channel)
|
|
984
|
+
modify(afk_channel: afk_channel)
|
|
985
|
+
end
|
|
986
|
+
|
|
987
|
+
# Sets the server's system channel.
|
|
988
|
+
# @param system_channel [Channel, String, Integer, nil] The new system channel, or `nil` should it be disabled.
|
|
989
|
+
def system_channel=(system_channel)
|
|
990
|
+
modify(system_channel: system_channel)
|
|
991
|
+
end
|
|
992
|
+
|
|
993
|
+
# Sets the amount of time after which a user gets moved into the AFK channel.
|
|
994
|
+
# @param afk_timeout [Integer] The AFK timeout, in seconds.
|
|
995
|
+
def afk_timeout=(afk_timeout)
|
|
996
|
+
modify(afk_timeout: afk_timeout)
|
|
997
|
+
end
|
|
998
|
+
|
|
999
|
+
# A map of possible server verification levels to symbol names
|
|
1000
|
+
VERIFICATION_LEVELS = {
|
|
1001
|
+
none: 0,
|
|
1002
|
+
low: 1,
|
|
1003
|
+
medium: 2,
|
|
1004
|
+
high: 3,
|
|
1005
|
+
very_high: 4
|
|
1006
|
+
}.freeze
|
|
1007
|
+
|
|
1008
|
+
# @return [Symbol] The verification level of the server (:none = none, :low = 'Must have a verified email on their Discord account', :medium = 'Has to be registered with Discord for at least 5 minutes', :high = 'Has to be a member of this server for at least 10 minutes', :very_high = 'Must have a verified phone on their Discord account').
|
|
1009
|
+
def verification_level
|
|
1010
|
+
VERIFICATION_LEVELS.key(@verification_level)
|
|
1011
|
+
end
|
|
1012
|
+
|
|
1013
|
+
# Sets the verification level of the server
|
|
1014
|
+
# @param level [Integer, Symbol] The verification level from 0-4 or Symbol (see {VERIFICATION_LEVELS})
|
|
1015
|
+
def verification_level=(level)
|
|
1016
|
+
modify(verification_level: level)
|
|
1017
|
+
end
|
|
1018
|
+
|
|
1019
|
+
# A map of possible message notification levels to symbol names
|
|
1020
|
+
NOTIFICATION_LEVELS = {
|
|
1021
|
+
all_messages: 0,
|
|
1022
|
+
only_mentions: 1
|
|
1023
|
+
}.freeze
|
|
1024
|
+
|
|
1025
|
+
# @return [Symbol] The default message notifications settings of the server (:all_messages = 'All messages', :only_mentions = 'Only @mentions').
|
|
1026
|
+
def default_message_notifications
|
|
1027
|
+
NOTIFICATION_LEVELS.key(@default_message_notifications)
|
|
1028
|
+
end
|
|
1029
|
+
|
|
1030
|
+
# Sets the default message notification level
|
|
1031
|
+
# @param notification_level [Integer, Symbol] The default message notification 0-1 or Symbol (see {NOTIFICATION_LEVELS})
|
|
1032
|
+
def default_message_notifications=(notification_level)
|
|
1033
|
+
modify(notification_level: notification_level)
|
|
1034
|
+
end
|
|
1035
|
+
|
|
1036
|
+
alias_method :notification_level=, :default_message_notifications=
|
|
1037
|
+
|
|
1038
|
+
# A map of possible content filter levels to symbol names
|
|
1039
|
+
FILTER_LEVELS = {
|
|
1040
|
+
disabled: 0,
|
|
1041
|
+
members_without_roles: 1,
|
|
1042
|
+
all_members: 2
|
|
1043
|
+
}.freeze
|
|
1044
|
+
|
|
1045
|
+
# @return [Symbol] The explicit content filter level of the server (:disabled = 'Don't scan any messages.', :members_without_roles = 'Scan messages for members without a role.', :all_members = 'Scan messages sent by all members.').
|
|
1046
|
+
def explicit_content_filter
|
|
1047
|
+
FILTER_LEVELS.key(@explicit_content_filter)
|
|
1048
|
+
end
|
|
1049
|
+
|
|
1050
|
+
alias_method :content_filter_level, :explicit_content_filter
|
|
1051
|
+
|
|
1052
|
+
# Sets the server content filter.
|
|
1053
|
+
# @param filter_level [Integer, Symbol] The content filter from 0-2 or Symbol (see {FILTER_LEVELS})
|
|
1054
|
+
def explicit_content_filter=(filter_level)
|
|
1055
|
+
modify(explicit_content_filter: filter_level)
|
|
1056
|
+
end
|
|
1057
|
+
|
|
1058
|
+
# A map of possible multi-factor authentication levels to symbol names
|
|
1059
|
+
MFA_LEVELS = {
|
|
1060
|
+
none: 0,
|
|
1061
|
+
elevated: 1
|
|
1062
|
+
}.freeze
|
|
1063
|
+
|
|
1064
|
+
# @return [Symbol] The multi-factor authentication level of the server (:none = 'no MFA/2FA requirement for moderation actions', :elevated = 'MFA/2FA is required for moderation actions')
|
|
1065
|
+
def mfa_level
|
|
1066
|
+
MFA_LEVELS.key @mfa_level
|
|
1067
|
+
end
|
|
1068
|
+
|
|
1069
|
+
# A map of possible NSFW levels to symbol names
|
|
1070
|
+
NSFW_LEVELS = {
|
|
1071
|
+
default: 0,
|
|
1072
|
+
explicit: 1,
|
|
1073
|
+
safe: 2,
|
|
1074
|
+
age_restricted: 3
|
|
1075
|
+
}.freeze
|
|
1076
|
+
|
|
1077
|
+
# @return [Symbol] The NSFW level of the server (:default = 'no NSFW level has been set', :explicit = 'the server may contain explicit content', :safe = 'the server does not contain NSFW content', :age_restricted = 'server membership is restricted to adults')
|
|
1078
|
+
def nsfw_level
|
|
1079
|
+
NSFW_LEVELS.key @nsfw_level
|
|
1080
|
+
end
|
|
1081
|
+
|
|
1082
|
+
# @return [true, false] whether this server has any emoji or not.
|
|
1083
|
+
def any_emoji?
|
|
1084
|
+
@emoji.any?
|
|
1085
|
+
end
|
|
1086
|
+
|
|
1087
|
+
alias_method :has_emoji?, :any_emoji?
|
|
1088
|
+
alias_method :emoji?, :any_emoji?
|
|
1089
|
+
|
|
1090
|
+
# Create an invite link using the server's vanity code.
|
|
1091
|
+
# @return [String, nil] The server's vanity invite URL, or `nil` if the server does not have a vanity invite code.
|
|
1092
|
+
def vanity_invite_url
|
|
1093
|
+
return unless @vanity_invite_code
|
|
1094
|
+
|
|
1095
|
+
"https://discord.gg/#{@vanity_invite_code}"
|
|
1096
|
+
end
|
|
1097
|
+
|
|
1098
|
+
alias_method :vanity_invite_link, :vanity_invite_url
|
|
1099
|
+
|
|
1100
|
+
# Check if the auto-moderation system has detected a raid.
|
|
1101
|
+
# @return [true, false] Whether or not Discord's anti-spam system has detected a raid in the server.
|
|
1102
|
+
def raid_detected?
|
|
1103
|
+
!@raid_detected_at.nil?
|
|
1104
|
+
end
|
|
1105
|
+
|
|
1106
|
+
# Check if the auto-moderation system has detected DM spam.
|
|
1107
|
+
# @return [true, false] Whether or not Discord's anti-spam system has detected dm-spam in the server.
|
|
1108
|
+
def dm_spam_detected?
|
|
1109
|
+
!@dm_spam_detected_at.nil?
|
|
1110
|
+
end
|
|
1111
|
+
|
|
1112
|
+
# Check if the server has disabled non-friend DMs.
|
|
1113
|
+
# @return [true, false] Whether or not the server has stopped member's who aren't friends from DMing each other.
|
|
1114
|
+
def dms_disabled?
|
|
1115
|
+
!@dms_disabled_until.nil? && @dms_disabled_until > Time.now
|
|
1116
|
+
end
|
|
1117
|
+
|
|
1118
|
+
# Check if the server has paused invites.
|
|
1119
|
+
# @return [true, false] Whether or not the server has stopped new members from joining, either via incident actions
|
|
1120
|
+
# or the `:invites_disabled` feature.
|
|
1121
|
+
def invites_disabled?
|
|
1122
|
+
(!@invites_disabled_until.nil? && @invites_disabled_until > Time.now) || @features.include?(:invites_disabled)
|
|
1123
|
+
end
|
|
1124
|
+
|
|
1125
|
+
# Requests a list of Webhooks on the server.
|
|
1126
|
+
# @return [Array<Webhook>] webhooks on the server.
|
|
1127
|
+
def webhooks
|
|
1128
|
+
webhooks = JSON.parse(API::Server.webhooks(@bot.token, @id))
|
|
1129
|
+
webhooks.map { |webhook| Webhook.new(webhook, @bot) }
|
|
1130
|
+
end
|
|
1131
|
+
|
|
1132
|
+
# Requests a list of Invites to the server.
|
|
1133
|
+
# @return [Array<Invite>] invites to the server.
|
|
1134
|
+
def invites
|
|
1135
|
+
invites = JSON.parse(API::Server.invites(@bot.token, @id))
|
|
1136
|
+
invites.map { |invite| Invite.new(invite, @bot) }
|
|
1137
|
+
end
|
|
1138
|
+
|
|
1139
|
+
# Get the scheduled events on the server.
|
|
1140
|
+
# @param bypass_cache [true, false] Whether the cached scheduled events
|
|
1141
|
+
# should be ignored and re-fetched via an HTTP request.
|
|
1142
|
+
# @return [Array<ScheduledEvent>] The scheduled events on the server.
|
|
1143
|
+
def scheduled_events(bypass_cache: false)
|
|
1144
|
+
process_scheduled_events(JSON.parse(API::Server.list_scheduled_events(@bot.token, @id, with_user_count: true))) if bypass_cache
|
|
1145
|
+
|
|
1146
|
+
@scheduled_events.values
|
|
1147
|
+
end
|
|
1148
|
+
|
|
1149
|
+
# Get a specific scheduled event on the server.
|
|
1150
|
+
# @param scheduled_event_id [Integer, String, ScheduledEvent] The scheduled event to get.
|
|
1151
|
+
# @param request [true, false] Whether to request the event from discord if it isn't cached.
|
|
1152
|
+
# @return [ScheduledEvent, nil] The scheduled event for the ID, or `nil` if it couldn't be found.
|
|
1153
|
+
def scheduled_event(scheduled_event_id, request: true)
|
|
1154
|
+
id = scheduled_event_id.resolve_id
|
|
1155
|
+
return @scheduled_events[id] if @scheduled_events[id]
|
|
1156
|
+
return nil unless request
|
|
1157
|
+
|
|
1158
|
+
event = JSON.parse(API::Server.get_scheduled_event(@bot.token, @id, id, with_user_count: true))
|
|
1159
|
+
scheduled_event = ScheduledEvent.new(event, self, @bot)
|
|
1160
|
+
@scheduled_events[scheduled_event.id] = scheduled_event
|
|
1161
|
+
rescue StandardError
|
|
1162
|
+
nil
|
|
1163
|
+
end
|
|
1164
|
+
|
|
1165
|
+
# Create a scheduled event on this server.
|
|
1166
|
+
# @param name [String] The 1-100 character name of the scheduled event to create.
|
|
1167
|
+
# @param start_time [Time] The start time of the scheduled event to create.
|
|
1168
|
+
# @param entity_type [Integer, Symbol] The entity type of the scheduled event to create.
|
|
1169
|
+
# @param end_time [Time, nil] The end time of the scheduled event to create.
|
|
1170
|
+
# @param channel [Integer, Channel, String, nil] The channel where the scheduled event will take place.
|
|
1171
|
+
# @param location [String, nil] The external location of the scheduled event to create.
|
|
1172
|
+
# @param description [String, nil] The 1-100 character description of the scheduled event to create.
|
|
1173
|
+
# @param cover [File, #read, nil] The cover image of the scheduled event to create.
|
|
1174
|
+
# @param recurrence_rule [#to_h, nil] The recurrence rule of the scheduled event to create.
|
|
1175
|
+
# @param reason [String, nil] The audit log reason for creating the scheduled event.
|
|
1176
|
+
# @yieldparam builder [ScheduledEvent::RecurrenceRule::Builder] An optional reccurence rule builder.
|
|
1177
|
+
# @return [ScheduledEvent] the scheduled event that was created.
|
|
1178
|
+
def create_scheduled_event(name:, start_time:, entity_type:, end_time: nil, channel: nil, location: nil, description: nil, cover: nil, recurrence_rule: nil, reason: nil)
|
|
1179
|
+
yield((builder = ScheduledEvent::RecurrenceRule::Builder.new)) if block_given?
|
|
1180
|
+
|
|
1181
|
+
options = {
|
|
1182
|
+
name: name,
|
|
1183
|
+
privacy_level: 2,
|
|
1184
|
+
scheduled_start_time: start_time&.iso8601,
|
|
1185
|
+
entity_type: ScheduledEvent::ENTITY_TYPES[entity_type] || entity_type,
|
|
1186
|
+
channel_id: channel&.resolve_id,
|
|
1187
|
+
entity_metadata: location ? { location: location } : nil,
|
|
1188
|
+
scheduled_end_time: end_time&.iso8601,
|
|
1189
|
+
description: description,
|
|
1190
|
+
image: cover.respond_to?(:read) ? OnyxCord.encode64(cover) : cover,
|
|
1191
|
+
recurrence_rule: block_given? ? builder.to_h : recurrence_rule&.to_h
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
event = JSON.parse(API::Server.create_scheduled_event(@bot.token, @id, **options, reason: reason))
|
|
1195
|
+
scheduled_event = ScheduledEvent.new(event, self, @bot)
|
|
1196
|
+
@scheduled_events[scheduled_event.id] = scheduled_event
|
|
1197
|
+
end
|
|
1198
|
+
|
|
1199
|
+
# Processes a GUILD_MEMBERS_CHUNK packet, specifically the members field
|
|
1200
|
+
# @note For internal use only
|
|
1201
|
+
# @!visibility private
|
|
1202
|
+
def process_chunk(members, chunk_index, chunk_count)
|
|
1203
|
+
process_members(members)
|
|
1204
|
+
LOGGER.debug("Processed chunk #{chunk_index + 1}/#{chunk_count} server #{@id} - index #{chunk_index} - length #{members.length}")
|
|
1205
|
+
|
|
1206
|
+
return if chunk_index + 1 < chunk_count
|
|
1207
|
+
|
|
1208
|
+
LOGGER.debug("Finished chunking server #{@id}")
|
|
1209
|
+
|
|
1210
|
+
# Reset everything to normal
|
|
1211
|
+
@chunked = true
|
|
1212
|
+
end
|
|
1213
|
+
|
|
1214
|
+
# Get the AFK channel of the server.
|
|
1215
|
+
# @return [Channel, nil] the AFK voice channel of this server, or `nil` if none is set.
|
|
1216
|
+
def afk_channel
|
|
1217
|
+
@bot.channel(@afk_channel_id) if @afk_channel_id
|
|
1218
|
+
end
|
|
1219
|
+
|
|
1220
|
+
# Get the rules channel of the server.
|
|
1221
|
+
# @return [Channel, nil] The channel where community servers can display rules or guidelines, or `nil` if none is set.
|
|
1222
|
+
def rules_channel
|
|
1223
|
+
@bot.channel(@rules_channel_id) if @rules_channel_id
|
|
1224
|
+
end
|
|
1225
|
+
|
|
1226
|
+
# Get the system channel of the server.
|
|
1227
|
+
# @return [Channel, nil] The system channel (used for automatic welcome messages) of a server, or `nil` if none is set.
|
|
1228
|
+
def system_channel
|
|
1229
|
+
@bot.channel(@system_channel_id) if @system_channel_id
|
|
1230
|
+
end
|
|
1231
|
+
|
|
1232
|
+
# Get the safety alerts channel of the server.
|
|
1233
|
+
# @return [Channel, nil] The channel where Community servers receive safety alerts from Discord, or `nil` if none is set.
|
|
1234
|
+
def safety_alerts_channel
|
|
1235
|
+
@bot.channel(@safety_alerts_channel_id) if @safety_alerts_channel_id
|
|
1236
|
+
end
|
|
1237
|
+
|
|
1238
|
+
# Get the public updates channel of the server.
|
|
1239
|
+
# @return [Channel, nil] The channel where Community servers receive public updates from Discord, or `nil` if none is set.
|
|
1240
|
+
def public_updates_channel
|
|
1241
|
+
@bot.channel(@public_updates_channel_id) if @public_updates_channel_id
|
|
1242
|
+
end
|
|
1243
|
+
|
|
1244
|
+
# Modify the properties of the server.
|
|
1245
|
+
# @param name [String] The new 2-32 character name of the server.
|
|
1246
|
+
# @param verification_level [Symbol, Integer, nil] The new verification level of the server.
|
|
1247
|
+
# @param notification_level [Symbol, Integer, nil] The new default message notification level of the server.
|
|
1248
|
+
# @param explicit_content_filter [Symbol, Integer, nil] The new explicit content filter level of the server.
|
|
1249
|
+
# @param afk_channel [Channel, Integer, String, nil] The new AFK voice channel members should be automatically moved to.
|
|
1250
|
+
# @param afk_timeout [Integer] The new AFK timeout in seconds. Can be set to one of `60`, `300`, `900`, `1800`, or `3600`.
|
|
1251
|
+
# @param icon [#read, File, nil] The new icon of the server. Should be a file-like object that responds to `#read`.
|
|
1252
|
+
# @param splash [#read, File, nil] The new invite splash of the server. Should be a file-like object that responds to `#read`.
|
|
1253
|
+
# @param discovery_splash [#read, File, nil] The new discovery splash of the server. Should be a file-like object that responds to `#read`.
|
|
1254
|
+
# @param banner [#read, File, nil] The new banner of the server. Should be a file-like object that responds to `#read`.
|
|
1255
|
+
# @param system_channel [Channel, Integer, String, nil] The new channel where system messages should be sent.
|
|
1256
|
+
# @param system_channel_flags [Integer] The new system channel flags to set for the server's system channel expressed as a bitfield.
|
|
1257
|
+
# @param rules_channel [Channel, Integer, String, nil] The new channel where the server displays its rules or guidelines.
|
|
1258
|
+
# @param public_updates_channel [Channel, Integer, String, nil] The new channel where public updates should be sent.
|
|
1259
|
+
# @param locale [String, Symbol, nil] The new preferred locale of the server; primarily for community servers.
|
|
1260
|
+
# @param features [Array<String, Symbol>] The new features to set for the server.
|
|
1261
|
+
# @param description [String, nil] The new description of the server.
|
|
1262
|
+
# @param boost_progress_bar [true, false] Whether or not the server boosting progress bar should be visible.
|
|
1263
|
+
# @param safety_alerts_channel [Channel, Integer, String, nil] The new channel where safety alerts should be sent.
|
|
1264
|
+
# @param widget_enabled [true, false, nil] Whether or not the server's widget should be enabled.
|
|
1265
|
+
# @param widget_channel [Channel, Integer, String, nil] The new invite channel for the server's widget.
|
|
1266
|
+
# @param dms_disabled_until [Time, nil] The time at when non-friend direct messages will be enabled again.
|
|
1267
|
+
# @param invites_disabled_until [Time, nil] The time at when invites will no longer be disabled.
|
|
1268
|
+
# @param reason [String, nil] The reason to show in the server's audit log for modifying the server.
|
|
1269
|
+
# @return [nil]
|
|
1270
|
+
def modify(
|
|
1271
|
+
name: :undef, verification_level: :undef, notification_level: :undef, explicit_content_filter: :undef,
|
|
1272
|
+
afk_channel: :undef, afk_timeout: :undef, icon: :undef, splash: :undef, discovery_splash: :undef, banner: :undef,
|
|
1273
|
+
system_channel: :undef, system_channel_flags: :undef, rules_channel: :undef, public_updates_channel: :undef,
|
|
1274
|
+
locale: :undef, features: :undef, description: :undef, boost_progress_bar: :undef, safety_alerts_channel: :undef,
|
|
1275
|
+
widget_enabled: :undef, widget_channel: :undef, dms_disabled_until: :undef, invites_disabled_until: :undef,
|
|
1276
|
+
reason: nil
|
|
1277
|
+
)
|
|
1278
|
+
data = {
|
|
1279
|
+
name: name,
|
|
1280
|
+
verification_level: VERIFICATION_LEVELS[verification_level] || verification_level,
|
|
1281
|
+
default_message_notifications: NOTIFICATION_LEVELS[notification_level] || notification_level,
|
|
1282
|
+
explicit_content_filter: FILTER_LEVELS[explicit_content_filter] || explicit_content_filter,
|
|
1283
|
+
afk_channel_id: afk_channel == :undef ? afk_channel : afk_channel&.resolve_id,
|
|
1284
|
+
afk_timeout: afk_timeout,
|
|
1285
|
+
icon: icon.respond_to?(:read) ? OnyxCord.encode64(icon) : icon,
|
|
1286
|
+
splash: splash.respond_to?(:read) ? OnyxCord.encode64(splash) : splash,
|
|
1287
|
+
discovery_splash: discovery_splash.respond_to?(:read) ? OnyxCord.encode64(discovery_splash) : discovery_splash,
|
|
1288
|
+
banner: banner.respond_to?(:read) ? OnyxCord.encode64(banner) : banner,
|
|
1289
|
+
system_channel_id: system_channel == :undef ? system_channel : system_channel&.resolve_id,
|
|
1290
|
+
system_channel_flags: system_channel_flags,
|
|
1291
|
+
rules_channel_id: rules_channel == :undef ? rules_channel : rules_channel&.resolve_id,
|
|
1292
|
+
public_updates_channel_id: public_updates_channel == :undef ? public_updates_channel : public_updates_channel&.resolve_id,
|
|
1293
|
+
preferred_locale: locale,
|
|
1294
|
+
features: features == :undef ? features : features.map(&:upcase),
|
|
1295
|
+
description: description,
|
|
1296
|
+
premium_progress_bar_enabled: boost_progress_bar,
|
|
1297
|
+
safety_alerts_channel_id: safety_alerts_channel == :undef ? safety_alerts_channel : safety_alerts_channel&.resolve_id
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
if widget_enabled != :undef || widget_channel != :undef
|
|
1301
|
+
widget_data = {
|
|
1302
|
+
enabled: widget_enabled,
|
|
1303
|
+
channel_id: widget_channel == :undef ? widget_channel : widget_channel&.resolve_id
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
cache_widget_data(JSON.parse(API::Server.update_widget(@bot.token, @id, **widget_data, reason: reason)))
|
|
1307
|
+
end
|
|
1308
|
+
|
|
1309
|
+
if invites_disabled_until != :undef || dms_disabled_until != :undef
|
|
1310
|
+
incidents_data = {
|
|
1311
|
+
dms_disabled_until: dms_disabled_until == :undef ? @dms_disabled_until&.iso8601 : dms_disabled_until&.iso8601,
|
|
1312
|
+
invites_disabled_until: invites_disabled_until == :undef ? @invites_disabled_until&.iso8601 : invites_disabled_until&.iso8601
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
process_incident_actions(JSON.parse(API::Server.update_incident_actions(@bot.token, @id, **incidents_data, reason: reason)))
|
|
1316
|
+
end
|
|
1317
|
+
|
|
1318
|
+
return unless data.any? { |_, value| value != :undef }
|
|
1319
|
+
|
|
1320
|
+
update_data(JSON.parse(API::Server.update!(@bot.token, @id, **data, reason: reason)))
|
|
1321
|
+
nil
|
|
1322
|
+
end
|
|
1323
|
+
|
|
1324
|
+
# Updates the cached data with new data
|
|
1325
|
+
# @note For internal use only
|
|
1326
|
+
# @!visibility private
|
|
1327
|
+
def update_data(new_data = nil)
|
|
1328
|
+
new_data ||= JSON.parse(API::Server.resolve(@bot.token, @id))
|
|
1329
|
+
@name = new_data['name']
|
|
1330
|
+
@icon_id = new_data['icon']
|
|
1331
|
+
@splash_id = new_data['splash']
|
|
1332
|
+
@discovery_splash_id = new_data['discovery_splash']
|
|
1333
|
+
@owner_id = new_data['owner_id'].to_i
|
|
1334
|
+
@region_id = new_data['region'] if new_data.key?('region')
|
|
1335
|
+
|
|
1336
|
+
@afk_timeout = new_data['afk_timeout']
|
|
1337
|
+
@afk_channel_id = new_data['afk_channel_id']&.to_i
|
|
1338
|
+
|
|
1339
|
+
@widget_enabled = new_data['widget_enabled'] if new_data.key?('widget_enabled')
|
|
1340
|
+
@widget_channel_id = new_data['widget_channel_id'] if new_data.key?('widget_channel_id')
|
|
1341
|
+
|
|
1342
|
+
@system_channel_flags = new_data['system_channel_flags']
|
|
1343
|
+
@system_channel_id = new_data['system_channel_id']&.to_i
|
|
1344
|
+
|
|
1345
|
+
@rules_channel_id = new_data['rules_channel_id']&.to_i
|
|
1346
|
+
@public_updates_channel_id = new_data['public_updates_channel_id']&.to_i
|
|
1347
|
+
@safety_alerts_channel_id = new_data['safety_alerts_channel_id']&.to_i
|
|
1348
|
+
|
|
1349
|
+
@mfa_level = new_data['mfa_level']
|
|
1350
|
+
@nsfw_level = new_data['nsfw_level']
|
|
1351
|
+
@verification_level = new_data['verification_level']
|
|
1352
|
+
@explicit_content_filter = new_data['explicit_content_filter']
|
|
1353
|
+
@default_message_notifications = new_data['default_message_notifications']
|
|
1354
|
+
|
|
1355
|
+
@features = new_data['features']&.map { |feature| feature.downcase.to_sym } || @features || []
|
|
1356
|
+
@max_presence_count = new_data['max_presences'] if new_data.key?('max_presences')
|
|
1357
|
+
@max_member_count = new_data['max_members'] if new_data.key?('max_members')
|
|
1358
|
+
@large = new_data.key?('large') ? new_data['large'] : (@large || false)
|
|
1359
|
+
@member_count = new_data['member_count'] || new_data['approximate_member_count'] || @member_count || 0
|
|
1360
|
+
|
|
1361
|
+
@vanity_url_code = new_data['vanity_url_code']
|
|
1362
|
+
@description = new_data['description']
|
|
1363
|
+
@banner_id = new_data['banner']
|
|
1364
|
+
@boost_level = new_data['premium_tier']
|
|
1365
|
+
@booster_count = new_data['premium_subscription_count'] || @booster_count || 0
|
|
1366
|
+
@locale = new_data['preferred_locale']
|
|
1367
|
+
|
|
1368
|
+
@max_video_channel_members = new_data['max_video_channel_users'] || @max_video_channel_members
|
|
1369
|
+
@max_stage_video_channel_members = new_data['max_stage_video_channel_users'] || @max_stage_video_channel_members
|
|
1370
|
+
@boost_progress_bar = new_data['premium_progress_bar_enabled']
|
|
1371
|
+
|
|
1372
|
+
process_channels(new_data['channels']) if new_data['channels']
|
|
1373
|
+
process_roles(new_data['roles']) if new_data['roles']
|
|
1374
|
+
process_emoji(new_data['emojis']) if new_data['emojis']
|
|
1375
|
+
process_members(new_data['members']) if new_data['members']
|
|
1376
|
+
process_presences(new_data['presences']) if new_data['presences']
|
|
1377
|
+
process_voice_states(new_data['voice_states']) if new_data['voice_states']
|
|
1378
|
+
process_active_threads(new_data['threads']) if new_data['threads']
|
|
1379
|
+
process_incident_actions(new_data['incidents_data']) if new_data.key?('incidents_data')
|
|
1380
|
+
process_scheduled_events(new_data['guild_scheduled_events']) if new_data['guild_scheduled_events']
|
|
1381
|
+
end
|
|
1382
|
+
|
|
1383
|
+
# Adds a channel to this server's cache
|
|
1384
|
+
# @note For internal use only
|
|
1385
|
+
# @!visibility private
|
|
1386
|
+
def add_channel(channel)
|
|
1387
|
+
@channels << channel
|
|
1388
|
+
@channels_by_id[channel.id] = channel
|
|
1389
|
+
end
|
|
1390
|
+
|
|
1391
|
+
# Deletes a channel from this server's cache
|
|
1392
|
+
# @note For internal use only
|
|
1393
|
+
# @!visibility private
|
|
1394
|
+
def delete_channel(id)
|
|
1395
|
+
@channels.reject! { |e| e.id == id }
|
|
1396
|
+
@channels_by_id.delete(id)
|
|
1397
|
+
end
|
|
1398
|
+
|
|
1399
|
+
# Updates the cached emoji data with new data
|
|
1400
|
+
# @note For internal use only
|
|
1401
|
+
# @!visibility private
|
|
1402
|
+
def update_emoji_data(new_data)
|
|
1403
|
+
@emoji = {}
|
|
1404
|
+
process_emoji(new_data['emojis'])
|
|
1405
|
+
end
|
|
1406
|
+
|
|
1407
|
+
# Updates the threads for this server's cache
|
|
1408
|
+
# @note For internal use only
|
|
1409
|
+
# @!visibility private
|
|
1410
|
+
def clear_threads(ids = nil)
|
|
1411
|
+
if ids.nil?
|
|
1412
|
+
@channels.reject!(&:thread?)
|
|
1413
|
+
@channels_by_id.delete_if { |_, channel| channel.thread? }
|
|
1414
|
+
else
|
|
1415
|
+
@channels.reject! { |channel| channel.thread? && ids.any?(channel.parent&.id) }
|
|
1416
|
+
@channels_by_id.delete_if { |_, channel| channel.thread? && ids.any?(channel.parent&.id) }
|
|
1417
|
+
end
|
|
1418
|
+
end
|
|
1419
|
+
|
|
1420
|
+
# The inspect method is overwritten to give more useful output
|
|
1421
|
+
def inspect
|
|
1422
|
+
"<Server name=#{@name} id=#{@id} large=#{@large} region=#{@region} owner=#{@owner} afk_channel_id=#{@afk_channel_id} system_channel_id=#{@system_channel_id} afk_timeout=#{@afk_timeout}>"
|
|
1423
|
+
end
|
|
1424
|
+
|
|
1425
|
+
private
|
|
1426
|
+
|
|
1427
|
+
def process_roles(roles)
|
|
1428
|
+
# Create roles
|
|
1429
|
+
@roles = {}
|
|
1430
|
+
|
|
1431
|
+
return unless roles
|
|
1432
|
+
|
|
1433
|
+
roles.each do |element|
|
|
1434
|
+
role = Role.new(element, @bot, self)
|
|
1435
|
+
@roles[role.id] = role
|
|
1436
|
+
end
|
|
1437
|
+
end
|
|
1438
|
+
|
|
1439
|
+
def process_emoji(emoji)
|
|
1440
|
+
return if emoji.empty?
|
|
1441
|
+
|
|
1442
|
+
emoji.each do |element|
|
|
1443
|
+
new_emoji = Emoji.new(element, @bot, self)
|
|
1444
|
+
@emoji[new_emoji.id] = new_emoji
|
|
1445
|
+
end
|
|
1446
|
+
end
|
|
1447
|
+
|
|
1448
|
+
def process_members(members)
|
|
1449
|
+
return unless members
|
|
1450
|
+
|
|
1451
|
+
members.each do |element|
|
|
1452
|
+
member = Member.new(element, self, @bot)
|
|
1453
|
+
@members[member.id] = member
|
|
1454
|
+
end
|
|
1455
|
+
end
|
|
1456
|
+
|
|
1457
|
+
def process_presences(presences)
|
|
1458
|
+
# Update user statuses with presence info
|
|
1459
|
+
return unless presences
|
|
1460
|
+
|
|
1461
|
+
presences.each do |element|
|
|
1462
|
+
next unless element['user']
|
|
1463
|
+
|
|
1464
|
+
user_id = element['user']['id'].to_i
|
|
1465
|
+
user = @members[user_id]
|
|
1466
|
+
if user
|
|
1467
|
+
user.update_presence(element)
|
|
1468
|
+
else
|
|
1469
|
+
LOGGER.warn "Rogue presence update! #{element['user']['id']} on #{@id}"
|
|
1470
|
+
end
|
|
1471
|
+
end
|
|
1472
|
+
end
|
|
1473
|
+
|
|
1474
|
+
def process_channels(channels)
|
|
1475
|
+
@channels = []
|
|
1476
|
+
@channels_by_id = {}
|
|
1477
|
+
|
|
1478
|
+
return unless channels
|
|
1479
|
+
|
|
1480
|
+
channels.each do |element|
|
|
1481
|
+
channel = @bot.ensure_channel(element, self)
|
|
1482
|
+
@channels << channel
|
|
1483
|
+
@channels_by_id[channel.id] = channel
|
|
1484
|
+
end
|
|
1485
|
+
end
|
|
1486
|
+
|
|
1487
|
+
def process_voice_states(voice_states)
|
|
1488
|
+
return unless voice_states
|
|
1489
|
+
|
|
1490
|
+
voice_states.each do |element|
|
|
1491
|
+
update_voice_state(element)
|
|
1492
|
+
end
|
|
1493
|
+
end
|
|
1494
|
+
|
|
1495
|
+
def process_active_threads(threads)
|
|
1496
|
+
@channels ||= []
|
|
1497
|
+
@channels_by_id ||= {}
|
|
1498
|
+
|
|
1499
|
+
return unless threads
|
|
1500
|
+
|
|
1501
|
+
threads.each do |element|
|
|
1502
|
+
thread = @bot.ensure_channel(element, self)
|
|
1503
|
+
@channels << thread
|
|
1504
|
+
@channels_by_id[thread.id] = thread
|
|
1505
|
+
end
|
|
1506
|
+
end
|
|
1507
|
+
|
|
1508
|
+
def process_incident_actions(incidents)
|
|
1509
|
+
incidents ||= {}
|
|
1510
|
+
@raid_detected_at = incidents['raid_detected_at'] ? Time.parse(incidents['raid_detected_at']) : nil
|
|
1511
|
+
@dms_disabled_until = incidents['dms_disabled_until'] ? Time.parse(incidents['dms_disabled_until']) : nil
|
|
1512
|
+
@dm_spam_detected_at = incidents['dm_spam_detected_at'] ? Time.parse(incidents['dm_spam_detected_at']) : nil
|
|
1513
|
+
@invites_disabled_until = incidents['invites_disabled_until'] ? Time.parse(incidents['invites_disabled_until']) : nil
|
|
1514
|
+
end
|
|
1515
|
+
|
|
1516
|
+
def process_scheduled_events(events)
|
|
1517
|
+
@scheduled_events = {}
|
|
1518
|
+
|
|
1519
|
+
return unless events
|
|
1520
|
+
|
|
1521
|
+
events.each do |element|
|
|
1522
|
+
event = ScheduledEvent.new(element, self, @bot)
|
|
1523
|
+
@scheduled_events[event.resolve_id] = event
|
|
1524
|
+
end
|
|
1525
|
+
end
|
|
1526
|
+
end
|
|
1527
|
+
|
|
1528
|
+
# A ban entry on a server.
|
|
1529
|
+
class ServerBan
|
|
1530
|
+
# @return [String, nil] the reason the user was banned, if provided
|
|
1531
|
+
attr_reader :reason
|
|
1532
|
+
|
|
1533
|
+
# @return [User] the user that was banned
|
|
1534
|
+
attr_reader :user
|
|
1535
|
+
|
|
1536
|
+
# @return [Server] the server this ban belongs to
|
|
1537
|
+
attr_reader :server
|
|
1538
|
+
|
|
1539
|
+
# @!visibility private
|
|
1540
|
+
def initialize(server, user, reason)
|
|
1541
|
+
@server = server
|
|
1542
|
+
@user = user
|
|
1543
|
+
@reason = reason
|
|
1544
|
+
end
|
|
1545
|
+
|
|
1546
|
+
# Removes this ban on the associated user in the server
|
|
1547
|
+
# @param reason [String] the reason for removing the ban
|
|
1548
|
+
def remove(reason = nil)
|
|
1549
|
+
@server.unban(user, reason)
|
|
1550
|
+
end
|
|
1551
|
+
|
|
1552
|
+
alias_method :unban, :remove
|
|
1553
|
+
alias_method :lift, :remove
|
|
1554
|
+
end
|
|
1555
|
+
|
|
1556
|
+
# A bulk ban entry on a server.
|
|
1557
|
+
class BulkBan
|
|
1558
|
+
# @return [Server] The server this bulk ban belongs to.
|
|
1559
|
+
attr_reader :server
|
|
1560
|
+
|
|
1561
|
+
# @return [String, nil] The reason these users were banned.
|
|
1562
|
+
attr_reader :reason
|
|
1563
|
+
|
|
1564
|
+
# @return [Array<Integer>] Array of user IDs that were banned.
|
|
1565
|
+
attr_reader :banned_users
|
|
1566
|
+
|
|
1567
|
+
# @return [Array<Integer>] Array of user IDs that couldn't be banned.
|
|
1568
|
+
attr_reader :failed_users
|
|
1569
|
+
|
|
1570
|
+
# @!visibility private
|
|
1571
|
+
def initialize(data, server, reason)
|
|
1572
|
+
@server = server
|
|
1573
|
+
@reason = reason
|
|
1574
|
+
@banned_users = data['banned_users']&.map(&:resolve_id) || []
|
|
1575
|
+
@failed_users = data['failed_users']&.map(&:resolve_id) || []
|
|
1576
|
+
end
|
|
1577
|
+
end
|
|
1578
|
+
|
|
1579
|
+
# A set of messages collected from a search query.
|
|
1580
|
+
class SearchedMessages
|
|
1581
|
+
include Enumerable
|
|
1582
|
+
|
|
1583
|
+
# @return [Array<Message>] the messages that matched the search query.
|
|
1584
|
+
attr_reader :messages
|
|
1585
|
+
|
|
1586
|
+
# @return [Integer] the total number of messages that matched the search query.
|
|
1587
|
+
attr_reader :total_results
|
|
1588
|
+
|
|
1589
|
+
# @!visibility private
|
|
1590
|
+
def initialize(messages, total, bot)
|
|
1591
|
+
@bot = bot
|
|
1592
|
+
@messages = messages
|
|
1593
|
+
@total_results = total
|
|
1594
|
+
end
|
|
1595
|
+
|
|
1596
|
+
# Get a single message that matched the search query by its index.
|
|
1597
|
+
# @param index [Integer] The index of the message to get from the array.
|
|
1598
|
+
# @return [Message] the message that was found at the specified index.
|
|
1599
|
+
def [](index)
|
|
1600
|
+
@messages[index]
|
|
1601
|
+
end
|
|
1602
|
+
|
|
1603
|
+
# Iterate over each message that matched the search query.
|
|
1604
|
+
# @return [Array<Message>, Enumerable] The array that was iterated over.
|
|
1605
|
+
def each(...)
|
|
1606
|
+
@messages.each(...)
|
|
1607
|
+
end
|
|
1608
|
+
|
|
1609
|
+
# @!visibility private
|
|
1610
|
+
def inspect
|
|
1611
|
+
"<SearchedMessages messages=[#{'...' if @messages.any?}] total_results=#{@total_results}>"
|
|
1612
|
+
end
|
|
1613
|
+
end
|
|
1614
|
+
end
|