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,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OnyxCord
|
|
4
|
+
# Mixin for objects that have IDs
|
|
5
|
+
module IDObject
|
|
6
|
+
# @return [Integer] the ID which uniquely identifies this object across Discord.
|
|
7
|
+
attr_reader :id
|
|
8
|
+
alias_method :resolve_id, :id
|
|
9
|
+
alias_method :hash, :id
|
|
10
|
+
|
|
11
|
+
# ID based comparison
|
|
12
|
+
def ==(other)
|
|
13
|
+
OnyxCord.id_compare?(@id, other)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
alias_method :eql?, :==
|
|
17
|
+
|
|
18
|
+
# Estimates the time this object was generated on based on the beginning of the ID. This is fairly accurate but
|
|
19
|
+
# shouldn't be relied on as Discord might change its algorithm at any time
|
|
20
|
+
# @return [Time] when this object was created at
|
|
21
|
+
def creation_time
|
|
22
|
+
# Milliseconds
|
|
23
|
+
ms = (@id >> 22) + DISCORD_EPOCH
|
|
24
|
+
Time.at(ms / 1000.0)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Creates an artificial snowflake at the given point in time. Useful for comparing against.
|
|
28
|
+
# @param time [Time] The time the snowflake should represent.
|
|
29
|
+
# @return [Integer] a snowflake with the timestamp data as the given time
|
|
30
|
+
def self.synthesise(time)
|
|
31
|
+
ms = (time.to_f * 1000).to_i
|
|
32
|
+
(ms - DISCORD_EPOCH) << 22
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class << self
|
|
36
|
+
alias_method :synthesize, :synthesise
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'onyxcord/data'
|
|
4
|
+
|
|
5
|
+
module OnyxCord::Light
|
|
6
|
+
# Represents the bot account used for the light bot, but without any methods to change anything.
|
|
7
|
+
class LightProfile
|
|
8
|
+
include OnyxCord::IDObject
|
|
9
|
+
include OnyxCord::UserAttributes
|
|
10
|
+
|
|
11
|
+
# @!visibility private
|
|
12
|
+
def initialize(data, bot)
|
|
13
|
+
@bot = bot
|
|
14
|
+
|
|
15
|
+
@username = data['username']
|
|
16
|
+
@id = data['id'].to_i
|
|
17
|
+
@discriminator = data['discriminator']
|
|
18
|
+
@avatar_id = data['avatar']
|
|
19
|
+
|
|
20
|
+
@bot_account = false
|
|
21
|
+
@bot_account = true if data['bot']
|
|
22
|
+
|
|
23
|
+
@verified = data['verified']
|
|
24
|
+
|
|
25
|
+
@email = data['email']
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# A server that only has an icon, a name, and an ID associated with it, like for example an integration's server.
|
|
30
|
+
class UltraLightServer
|
|
31
|
+
include OnyxCord::IDObject
|
|
32
|
+
include OnyxCord::ServerAttributes
|
|
33
|
+
|
|
34
|
+
# @!visibility private
|
|
35
|
+
def initialize(data, bot)
|
|
36
|
+
@bot = bot
|
|
37
|
+
|
|
38
|
+
@id = data['id'].to_i
|
|
39
|
+
|
|
40
|
+
@name = data['name']
|
|
41
|
+
@icon_id = data['icon']
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Represents a light server which only has a fraction of the properties of any other server.
|
|
46
|
+
class LightServer < UltraLightServer
|
|
47
|
+
# @return [true, false] whether or not the LightBot this server belongs to is the owner of the server.
|
|
48
|
+
attr_reader :bot_is_owner
|
|
49
|
+
alias_method :bot_is_owner?, :bot_is_owner
|
|
50
|
+
|
|
51
|
+
# @return [OnyxCord::Permissions] the permissions the LightBot has on this server
|
|
52
|
+
attr_reader :bot_permissions
|
|
53
|
+
|
|
54
|
+
# @!visibility private
|
|
55
|
+
def initialize(data, bot)
|
|
56
|
+
super(data, bot)
|
|
57
|
+
|
|
58
|
+
@bot_is_owner = data['owner']
|
|
59
|
+
@bot_permissions = OnyxCord::Permissions.new(data['permissions'])
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'onyxcord/data'
|
|
4
|
+
require 'onyxcord/light/data'
|
|
5
|
+
|
|
6
|
+
module OnyxCord::Light
|
|
7
|
+
# A connection of your Discord account to a particular other service (currently, Twitch and YouTube)
|
|
8
|
+
class Connection
|
|
9
|
+
# @return [Symbol] what type of connection this is (either :twitch or :youtube currently)
|
|
10
|
+
attr_reader :type
|
|
11
|
+
|
|
12
|
+
# @return [true, false] whether this connection is revoked
|
|
13
|
+
attr_reader :revoked
|
|
14
|
+
alias_method :revoked?, :revoked
|
|
15
|
+
|
|
16
|
+
# @return [String] the name of the connected account
|
|
17
|
+
attr_reader :name
|
|
18
|
+
|
|
19
|
+
# @return [String] the ID of the connected account
|
|
20
|
+
attr_reader :id
|
|
21
|
+
|
|
22
|
+
# @return [Array<Integration>] the integrations associated with this connection
|
|
23
|
+
attr_reader :integrations
|
|
24
|
+
|
|
25
|
+
# @!visibility private
|
|
26
|
+
def initialize(data, bot)
|
|
27
|
+
@bot = bot
|
|
28
|
+
|
|
29
|
+
@revoked = data['revoked']
|
|
30
|
+
@type = data['type'].to_sym
|
|
31
|
+
@name = data['name']
|
|
32
|
+
@id = data['id']
|
|
33
|
+
|
|
34
|
+
@integrations = data['integrations'].map { |e| Integration.new(e, self, bot) }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# An integration of a connection into a particular server, for example being a member of a subscriber-only Twitch
|
|
39
|
+
# server.
|
|
40
|
+
class Integration
|
|
41
|
+
include OnyxCord::IDObject
|
|
42
|
+
|
|
43
|
+
# @return [UltraLightServer] the server associated with this integration
|
|
44
|
+
attr_reader :server
|
|
45
|
+
|
|
46
|
+
# @note The connection returned by this method will have no integrations itself, as Discord doesn't provide that
|
|
47
|
+
# data. Also, it will always be considered not revoked.
|
|
48
|
+
# @return [Connection] the server's underlying connection (for a Twitch subscriber-only server, it would be the
|
|
49
|
+
# Twitch account connection of the server owner).
|
|
50
|
+
attr_reader :server_connection
|
|
51
|
+
|
|
52
|
+
# @return [Connection] the connection integrated with the server (i.e. your connection)
|
|
53
|
+
attr_reader :integrated_connection
|
|
54
|
+
|
|
55
|
+
# @!visibility private
|
|
56
|
+
def initialize(data, integrated, bot)
|
|
57
|
+
@bot = bot
|
|
58
|
+
@integrated_connection = integrated
|
|
59
|
+
|
|
60
|
+
@server = UltraLightServer.new(data['guild'], bot)
|
|
61
|
+
|
|
62
|
+
# Restructure the given data so we can reuse the Connection initializer
|
|
63
|
+
restructured = {}
|
|
64
|
+
|
|
65
|
+
restructured['type'] = data['type']
|
|
66
|
+
restructured['id'] = data['account']['id']
|
|
67
|
+
restructured['name'] = data['account']['name']
|
|
68
|
+
restructured['integrations'] = []
|
|
69
|
+
|
|
70
|
+
@server_connection = Connection.new(restructured, bot)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'onyxcord/api'
|
|
4
|
+
require 'onyxcord/api/invite'
|
|
5
|
+
require 'onyxcord/api/user'
|
|
6
|
+
require 'onyxcord/light/data'
|
|
7
|
+
require 'onyxcord/light/integrations'
|
|
8
|
+
|
|
9
|
+
# This module contains classes to allow connections to bots without a connection to the gateway socket, i.e. bots
|
|
10
|
+
# that only use the REST part of the API.
|
|
11
|
+
module OnyxCord::Light
|
|
12
|
+
# A bot that only uses the REST part of the API. Hierarchically unrelated to the regular {OnyxCord::Bot}. Useful to
|
|
13
|
+
# make applications integrated to Discord over OAuth, for example.
|
|
14
|
+
class LightBot
|
|
15
|
+
# Create a new LightBot. This does no networking yet, all networking is done by the methods on this class.
|
|
16
|
+
# @param token [String] The token that should be used to authenticate to Discord. Can be an OAuth token or a regular
|
|
17
|
+
# user account token.
|
|
18
|
+
def initialize(token)
|
|
19
|
+
if token.respond_to? :token
|
|
20
|
+
# Parse AccessTokens from the OAuth2 gem
|
|
21
|
+
token = token.token
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
unless token.include? '.'
|
|
25
|
+
# Discord user/bot tokens always contain two dots, so if there's none we can assume it's an OAuth token.
|
|
26
|
+
token = "Bearer #{token}" # OAuth tokens have to be prefixed with 'Bearer' for Discord to be able to use them
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
@token = token
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [LightProfile] the details of the user this bot is connected to.
|
|
33
|
+
def profile
|
|
34
|
+
response = OnyxCord::API::User.profile(@token)
|
|
35
|
+
LightProfile.new(JSON.parse(response), self)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [Array<LightServer>] the servers this bot is connected to.
|
|
39
|
+
def servers
|
|
40
|
+
response = OnyxCord::API::User.servers(@token)
|
|
41
|
+
JSON.parse(response).map { |e| LightServer.new(e, self) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Joins a server using an instant invite.
|
|
45
|
+
# @param code [String] The code part of the invite (for example 0cDvIgU2voWn4BaD if the invite URL is
|
|
46
|
+
# https://discord.gg/0cDvIgU2voWn4BaD)
|
|
47
|
+
def join(code)
|
|
48
|
+
OnyxCord::API::Invite.accept(@token, code)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Gets the connections associated with this account.
|
|
52
|
+
# @return [Array<Connection>] this account's connections.
|
|
53
|
+
def connections
|
|
54
|
+
response = OnyxCord::API::User.connections(@token)
|
|
55
|
+
JSON.parse(response).map { |e| Connection.new(e, self) }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OnyxCord
|
|
4
|
+
# The format log timestamps should be in, in strftime format
|
|
5
|
+
LOG_TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S.%L'
|
|
6
|
+
|
|
7
|
+
# Logs debug messages
|
|
8
|
+
class Logger
|
|
9
|
+
# @return [true, false] whether this logger is in extra-fancy mode!
|
|
10
|
+
attr_writer :fancy
|
|
11
|
+
|
|
12
|
+
# @return [String, nil] The bot token to be redacted or nil if it shouldn't.
|
|
13
|
+
attr_writer :token
|
|
14
|
+
|
|
15
|
+
# @return [Array<IO>, Array<#puts & #flush>] the streams the logger should write to.
|
|
16
|
+
attr_accessor :streams
|
|
17
|
+
|
|
18
|
+
# Creates a new logger.
|
|
19
|
+
# @param fancy [true, false] Whether this logger uses fancy mode (ANSI escape codes to make the output colourful)
|
|
20
|
+
# @param streams [Array<IO>, Array<#puts & #flush>] the streams the logger should write to.
|
|
21
|
+
def initialize(fancy = false, streams = [$stdout])
|
|
22
|
+
@fancy = fancy
|
|
23
|
+
self.mode = :normal
|
|
24
|
+
|
|
25
|
+
@streams = streams
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# The modes this logger can have. This is probably useless unless you want to write your own Logger
|
|
29
|
+
MODES = {
|
|
30
|
+
debug: { long: 'DEBUG', short: 'D', format_code: '' },
|
|
31
|
+
good: { long: 'GOOD', short: '✓', format_code: "\u001B[32m" }, # green
|
|
32
|
+
info: { long: 'INFO', short: 'i', format_code: '' },
|
|
33
|
+
warn: { long: 'WARN', short: '!', format_code: "\u001B[33m" }, # yellow
|
|
34
|
+
error: { long: 'ERROR', short: '✗', format_code: "\u001B[31m" }, # red
|
|
35
|
+
out: { long: 'OUT', short: '→', format_code: "\u001B[36m" }, # cyan
|
|
36
|
+
in: { long: 'IN', short: '←', format_code: "\u001B[35m" }, # purple
|
|
37
|
+
ratelimit: { long: 'RATELIMIT', short: 'R', format_code: "\u001B[41m" } # red background
|
|
38
|
+
}.freeze
|
|
39
|
+
|
|
40
|
+
# The ANSI format code that resets formatting
|
|
41
|
+
FORMAT_RESET = "\u001B[0m"
|
|
42
|
+
|
|
43
|
+
# The ANSI format code that makes something bold
|
|
44
|
+
FORMAT_BOLD = "\u001B[1m"
|
|
45
|
+
|
|
46
|
+
MODES.each do |mode, hash|
|
|
47
|
+
define_method(mode) do |message|
|
|
48
|
+
write(message.to_s, hash) if @enabled_modes.include? mode
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Sets the logging mode to :debug
|
|
53
|
+
# @param value [true, false] Whether debug mode should be on. If it is off the mode will be set to :normal.
|
|
54
|
+
def debug=(value)
|
|
55
|
+
self.mode = value ? :debug : :normal
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Sets the logging mode
|
|
59
|
+
# Possible modes are:
|
|
60
|
+
# * :debug logs everything
|
|
61
|
+
# * :verbose logs everything except for debug messages
|
|
62
|
+
# * :normal logs useful information, warnings and errors
|
|
63
|
+
# * :quiet only logs warnings and errors
|
|
64
|
+
# * :silent logs nothing
|
|
65
|
+
# @param value [Symbol] What logging mode to use
|
|
66
|
+
def mode=(value)
|
|
67
|
+
case value
|
|
68
|
+
when :debug
|
|
69
|
+
@enabled_modes = %i[debug good info warn error out in ratelimit]
|
|
70
|
+
when :verbose
|
|
71
|
+
@enabled_modes = %i[good info warn error out in ratelimit]
|
|
72
|
+
when :normal
|
|
73
|
+
@enabled_modes = %i[info warn error ratelimit]
|
|
74
|
+
when :quiet
|
|
75
|
+
@enabled_modes = %i[warn error]
|
|
76
|
+
when :silent
|
|
77
|
+
@enabled_modes = %i[]
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Logs an exception to the console.
|
|
82
|
+
# @param e [Exception] The exception to log.
|
|
83
|
+
def log_exception(e)
|
|
84
|
+
error("Exception: #{e.inspect}")
|
|
85
|
+
e.backtrace.each { |line| error(line) }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def write(message, mode)
|
|
91
|
+
thread_name = Thread.current[:onyxcord_name]
|
|
92
|
+
timestamp = Time.now.strftime(LOG_TIMESTAMP_FORMAT)
|
|
93
|
+
|
|
94
|
+
# Redact token if set
|
|
95
|
+
log = if @token && @token != ''
|
|
96
|
+
message.to_s.gsub(@token, 'REDACTED_TOKEN')
|
|
97
|
+
else
|
|
98
|
+
message.to_s
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
@streams.each do |stream|
|
|
102
|
+
if @fancy && !stream.is_a?(File)
|
|
103
|
+
fancy_write(stream, log, mode, thread_name, timestamp)
|
|
104
|
+
else
|
|
105
|
+
simple_write(stream, log, mode, thread_name, timestamp)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def fancy_write(stream, message, mode, thread_name, timestamp)
|
|
111
|
+
stream.puts "#{timestamp} #{FORMAT_BOLD}#{thread_name.ljust(16)}#{FORMAT_RESET} #{mode[:format_code]}#{mode[:short]}#{FORMAT_RESET} #{message}"
|
|
112
|
+
stream.flush
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def simple_write(stream, message, mode, thread_name, timestamp)
|
|
116
|
+
stream.puts "[#{mode[:long]} : #{thread_name} @ #{timestamp}] #{message}"
|
|
117
|
+
stream.flush
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OnyxCord
|
|
4
|
+
# Helpers for Discord message component payloads.
|
|
5
|
+
module MessageComponents
|
|
6
|
+
# Discord message flag for the Components V2 layout system.
|
|
7
|
+
IS_COMPONENTS_V2 = 1 << 15
|
|
8
|
+
|
|
9
|
+
# Component types that only exist in the Components V2 message system.
|
|
10
|
+
V2_COMPONENT_TYPES = [9, 10, 11, 12, 13, 14, 17].freeze
|
|
11
|
+
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
def payload(components)
|
|
15
|
+
case components
|
|
16
|
+
when nil
|
|
17
|
+
[]
|
|
18
|
+
when Array
|
|
19
|
+
components.map { |component| component.respond_to?(:to_h) ? component.to_h : component }
|
|
20
|
+
when Hash
|
|
21
|
+
[components]
|
|
22
|
+
else
|
|
23
|
+
if components.respond_to?(:to_a)
|
|
24
|
+
components.to_a
|
|
25
|
+
elsif components.respond_to?(:to_h)
|
|
26
|
+
[components.to_h]
|
|
27
|
+
else
|
|
28
|
+
Array(components)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def components_v2?(components)
|
|
34
|
+
payload(components).any? { |component| component_v2?(component) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def apply_v2_flag(flags, components, force: false)
|
|
38
|
+
return flags unless force || components_v2?(components)
|
|
39
|
+
|
|
40
|
+
flag_value(flags) | IS_COMPONENTS_V2
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def component_v2?(component)
|
|
44
|
+
return false if component.nil?
|
|
45
|
+
|
|
46
|
+
component = component.to_h if component.respond_to?(:to_h)
|
|
47
|
+
return false unless component.is_a?(Hash)
|
|
48
|
+
|
|
49
|
+
type = component[:type] || component['type']
|
|
50
|
+
return true if V2_COMPONENT_TYPES.include?(type)
|
|
51
|
+
|
|
52
|
+
children = component[:components] || component['components']
|
|
53
|
+
return true if children && components_v2?(children)
|
|
54
|
+
|
|
55
|
+
accessory = component[:accessory] || component['accessory']
|
|
56
|
+
component_v2?(accessory)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def flag_value(flags)
|
|
60
|
+
case flags
|
|
61
|
+
when nil, :undef
|
|
62
|
+
0
|
|
63
|
+
when Array
|
|
64
|
+
flags.map { |flag| flag.respond_to?(:to_i) ? flag.to_i : 0 }.reduce(0, &:|)
|
|
65
|
+
else
|
|
66
|
+
flags.respond_to?(:to_i) ? flags.to_i : 0
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OnyxCord
|
|
4
|
+
# Utility class for wrapping paginated endpoints. It is [Enumerable](https://ruby-doc.org/core-2.5.1/Enumerable.html),
|
|
5
|
+
# similar to an `Array`, so most of the same methods can be used to filter the results of the request
|
|
6
|
+
# that it wraps. If you simply want an array of all of the results, `#to_a` can be called.
|
|
7
|
+
class Paginator
|
|
8
|
+
include Enumerable
|
|
9
|
+
|
|
10
|
+
# @return [Integer] the total amount of elements that have been fetched so far.
|
|
11
|
+
attr_reader :amount_fetched
|
|
12
|
+
|
|
13
|
+
# Creates a new {Paginator}
|
|
14
|
+
# @param limit [Integer] the maximum number of items to request before stopping
|
|
15
|
+
# @param direction [:up, :down] the order in which results are returned in
|
|
16
|
+
# @yield [Array, nil] the last page of results, or nil if this is the first iteration.
|
|
17
|
+
# This should be used to request the next page of results.
|
|
18
|
+
# @yieldreturn [Array] the next page of results
|
|
19
|
+
def initialize(limit, direction, &block)
|
|
20
|
+
@amount_fetched = 0
|
|
21
|
+
@limit = limit
|
|
22
|
+
@direction = direction
|
|
23
|
+
@block = block
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Yields every item produced by the wrapped request, until it returns
|
|
27
|
+
# no more results or the configured `limit` is reached.
|
|
28
|
+
def each
|
|
29
|
+
last_page = nil
|
|
30
|
+
until limit_exceeded?
|
|
31
|
+
page = @block.call(last_page)
|
|
32
|
+
return if page.empty?
|
|
33
|
+
|
|
34
|
+
enumerator = case @direction
|
|
35
|
+
when :down
|
|
36
|
+
page.each
|
|
37
|
+
when :up
|
|
38
|
+
page.reverse_each
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
enumerator.each do |item|
|
|
42
|
+
yield item
|
|
43
|
+
@amount_fetched += 1
|
|
44
|
+
break if limit_exceeded?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
last_page = page
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
# Whether the paginator limit has been exceeded
|
|
54
|
+
def limit_exceeded?
|
|
55
|
+
return false if @limit.nil?
|
|
56
|
+
|
|
57
|
+
@amount_fetched >= @limit
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|