discordrb 3.4.3 → 3.6.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 +4 -4
- data/.devcontainer/Dockerfile +13 -0
- data/.devcontainer/devcontainer.json +29 -0
- data/.devcontainer/postcreate.sh +4 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -1
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -1
- 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 +45 -0
- data/.markdownlint.json +4 -0
- data/.rubocop.yml +58 -2
- data/CHANGELOG.md +485 -225
- data/LICENSE.txt +1 -1
- data/README.md +38 -26
- data/discordrb-webhooks.gemspec +4 -1
- data/discordrb.gemspec +18 -10
- data/lib/discordrb/api/application.rb +278 -0
- data/lib/discordrb/api/channel.rb +222 -18
- data/lib/discordrb/api/interaction.rb +63 -0
- data/lib/discordrb/api/invite.rb +2 -2
- data/lib/discordrb/api/server.rb +123 -66
- data/lib/discordrb/api/user.rb +20 -5
- data/lib/discordrb/api/webhook.rb +72 -0
- data/lib/discordrb/api.rb +35 -25
- data/lib/discordrb/bot.rb +437 -66
- data/lib/discordrb/cache.rb +41 -22
- data/lib/discordrb/commands/command_bot.rb +13 -21
- data/lib/discordrb/commands/container.rb +1 -1
- data/lib/discordrb/commands/parser.rb +7 -7
- data/lib/discordrb/commands/rate_limiter.rb +1 -1
- data/lib/discordrb/container.rb +178 -3
- data/lib/discordrb/data/activity.rb +1 -1
- data/lib/discordrb/data/application.rb +1 -0
- data/lib/discordrb/data/attachment.rb +38 -3
- data/lib/discordrb/data/audit_logs.rb +3 -3
- data/lib/discordrb/data/avatar_decoration.rb +26 -0
- data/lib/discordrb/data/call.rb +22 -0
- data/lib/discordrb/data/channel.rb +299 -30
- data/lib/discordrb/data/collectibles.rb +45 -0
- data/lib/discordrb/data/component.rb +229 -0
- data/lib/discordrb/data/embed.rb +10 -3
- data/lib/discordrb/data/emoji.rb +20 -1
- data/lib/discordrb/data/integration.rb +45 -3
- data/lib/discordrb/data/interaction.rb +937 -0
- data/lib/discordrb/data/invite.rb +1 -1
- data/lib/discordrb/data/member.rb +236 -44
- data/lib/discordrb/data/message.rb +278 -51
- data/lib/discordrb/data/overwrite.rb +15 -7
- data/lib/discordrb/data/primary_server.rb +60 -0
- data/lib/discordrb/data/profile.rb +2 -7
- data/lib/discordrb/data/reaction.rb +2 -1
- data/lib/discordrb/data/recipient.rb +1 -1
- data/lib/discordrb/data/role.rb +204 -18
- data/lib/discordrb/data/server.rb +194 -118
- data/lib/discordrb/data/server_preview.rb +68 -0
- data/lib/discordrb/data/snapshot.rb +110 -0
- data/lib/discordrb/data/user.rb +132 -12
- data/lib/discordrb/data/voice_region.rb +1 -0
- data/lib/discordrb/data/webhook.rb +99 -9
- data/lib/discordrb/data.rb +9 -0
- data/lib/discordrb/errors.rb +47 -3
- data/lib/discordrb/events/await.rb +1 -1
- data/lib/discordrb/events/channels.rb +38 -1
- data/lib/discordrb/events/generic.rb +2 -0
- data/lib/discordrb/events/guilds.rb +6 -1
- data/lib/discordrb/events/interactions.rb +575 -0
- data/lib/discordrb/events/invites.rb +2 -0
- data/lib/discordrb/events/members.rb +19 -2
- data/lib/discordrb/events/message.rb +42 -8
- data/lib/discordrb/events/presence.rb +23 -14
- data/lib/discordrb/events/raw.rb +1 -0
- data/lib/discordrb/events/reactions.rb +2 -1
- data/lib/discordrb/events/roles.rb +2 -0
- data/lib/discordrb/events/threads.rb +100 -0
- data/lib/discordrb/events/typing.rb +1 -0
- data/lib/discordrb/events/voice_server_update.rb +1 -0
- data/lib/discordrb/events/voice_state_update.rb +1 -0
- data/lib/discordrb/events/webhooks.rb +1 -0
- data/lib/discordrb/gateway.rb +57 -28
- data/lib/discordrb/paginator.rb +3 -3
- data/lib/discordrb/permissions.rb +71 -35
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +2 -2
- data/lib/discordrb/voice/network.rb +18 -7
- data/lib/discordrb/voice/sodium.rb +3 -1
- data/lib/discordrb/voice/voice_bot.rb +3 -3
- data/lib/discordrb/webhooks.rb +2 -0
- data/lib/discordrb/websocket.rb +0 -10
- data/lib/discordrb.rb +54 -5
- metadata +87 -25
- data/.circleci/config.yml +0 -126
- data/.codeclimate.yml +0 -16
- data/.travis.yml +0 -32
- data/bin/travis_build_docs.sh +0 -17
|
@@ -6,37 +6,56 @@ module Discordrb
|
|
|
6
6
|
# This hash maps bit positions to logical permissions.
|
|
7
7
|
FLAGS = {
|
|
8
8
|
# Bit => Permission # Value
|
|
9
|
-
0 => :create_instant_invite,
|
|
10
|
-
1 => :kick_members,
|
|
11
|
-
2 => :ban_members,
|
|
12
|
-
3 => :administrator,
|
|
13
|
-
4 => :manage_channels,
|
|
14
|
-
5 => :manage_server,
|
|
15
|
-
6 => :add_reactions,
|
|
16
|
-
7 => :view_audit_log,
|
|
17
|
-
8 => :priority_speaker,
|
|
18
|
-
9 => :stream,
|
|
19
|
-
10 => :read_messages,
|
|
20
|
-
11 => :send_messages,
|
|
21
|
-
12 => :send_tts_messages,
|
|
22
|
-
13 => :manage_messages,
|
|
23
|
-
14 => :embed_links,
|
|
24
|
-
15 => :attach_files,
|
|
25
|
-
16 => :read_message_history,
|
|
26
|
-
17 => :mention_everyone,
|
|
27
|
-
18 => :use_external_emoji,
|
|
28
|
-
19 => :view_server_insights,
|
|
29
|
-
20 => :connect,
|
|
30
|
-
21 => :speak,
|
|
31
|
-
22 => :mute_members,
|
|
32
|
-
23 => :deafen_members,
|
|
33
|
-
24 => :move_members,
|
|
34
|
-
25 => :use_voice_activity,
|
|
35
|
-
26 => :change_nickname,
|
|
36
|
-
27 => :manage_nicknames,
|
|
37
|
-
28 => :manage_roles,
|
|
38
|
-
29 => :manage_webhooks,
|
|
39
|
-
30 => :manage_emojis
|
|
9
|
+
0 => :create_instant_invite, # 1
|
|
10
|
+
1 => :kick_members, # 2
|
|
11
|
+
2 => :ban_members, # 4
|
|
12
|
+
3 => :administrator, # 8
|
|
13
|
+
4 => :manage_channels, # 16
|
|
14
|
+
5 => :manage_server, # 32
|
|
15
|
+
6 => :add_reactions, # 64
|
|
16
|
+
7 => :view_audit_log, # 128
|
|
17
|
+
8 => :priority_speaker, # 256
|
|
18
|
+
9 => :stream, # 512
|
|
19
|
+
10 => :read_messages, # 1024
|
|
20
|
+
11 => :send_messages, # 2048
|
|
21
|
+
12 => :send_tts_messages, # 4096
|
|
22
|
+
13 => :manage_messages, # 8192
|
|
23
|
+
14 => :embed_links, # 16384
|
|
24
|
+
15 => :attach_files, # 32768
|
|
25
|
+
16 => :read_message_history, # 65536
|
|
26
|
+
17 => :mention_everyone, # 131072
|
|
27
|
+
18 => :use_external_emoji, # 262144
|
|
28
|
+
19 => :view_server_insights, # 524288
|
|
29
|
+
20 => :connect, # 1048576
|
|
30
|
+
21 => :speak, # 2097152
|
|
31
|
+
22 => :mute_members, # 4194304
|
|
32
|
+
23 => :deafen_members, # 8388608
|
|
33
|
+
24 => :move_members, # 16777216
|
|
34
|
+
25 => :use_voice_activity, # 33554432
|
|
35
|
+
26 => :change_nickname, # 67108864
|
|
36
|
+
27 => :manage_nicknames, # 134217728
|
|
37
|
+
28 => :manage_roles, # 268435456, also Manage Permissions
|
|
38
|
+
29 => :manage_webhooks, # 536870912
|
|
39
|
+
30 => :manage_emojis, # 1073741824, also Manage Stickers
|
|
40
|
+
31 => :use_slash_commands, # 2147483648
|
|
41
|
+
32 => :request_to_speak, # 4294967296
|
|
42
|
+
33 => :manage_events, # 8589934592
|
|
43
|
+
34 => :manage_threads, # 17179869184
|
|
44
|
+
35 => :use_public_threads, # 34359738368
|
|
45
|
+
36 => :use_private_threads, # 68719476736
|
|
46
|
+
37 => :use_external_stickers, # 137438953472
|
|
47
|
+
38 => :send_messages_in_threads, # 274877906944
|
|
48
|
+
39 => :use_embedded_activities, # 549755813888
|
|
49
|
+
40 => :moderate_members, # 1099511627776
|
|
50
|
+
41 => :view_monetization_analytics, # 2199023255552
|
|
51
|
+
42 => :use_soundboard, # 4398046511104
|
|
52
|
+
43 => :create_server_expressions, # 8796093022208
|
|
53
|
+
44 => :create_scheduled_events, # 17592186044416
|
|
54
|
+
45 => :use_external_sounds, # 35184372088832
|
|
55
|
+
46 => :send_voice_messages, # 70368744177664
|
|
56
|
+
49 => :send_polls, # 562949953421312
|
|
57
|
+
50 => :use_external_apps, # 1125899906842624
|
|
58
|
+
51 => :pin_messages # 2251799813685248
|
|
40
59
|
}.freeze
|
|
41
60
|
|
|
42
61
|
FLAGS.each do |position, flag|
|
|
@@ -70,7 +89,7 @@ module Discordrb
|
|
|
70
89
|
# Initialize the instance variables based on the bitset.
|
|
71
90
|
def init_vars
|
|
72
91
|
FLAGS.each do |position, flag|
|
|
73
|
-
flag_set = (
|
|
92
|
+
flag_set = (@bits >> position).allbits?(0x1)
|
|
74
93
|
instance_variable_set "@#{flag}", flag_set
|
|
75
94
|
end
|
|
76
95
|
end
|
|
@@ -101,7 +120,7 @@ module Discordrb
|
|
|
101
120
|
# permission.can_speak = true
|
|
102
121
|
# @example Create a permissions object that could allow/deny read messages, connect, and speak by an array of symbols
|
|
103
122
|
# Permissions.new [:read_messages, :connect, :speak]
|
|
104
|
-
# @param bits [Integer, Array<Symbol>] The permission bits that should be set from the beginning, or an array of permission flag symbols
|
|
123
|
+
# @param bits [String, Integer, Array<Symbol>] The permission bits that should be set from the beginning, or an array of permission flag symbols
|
|
105
124
|
# @param writer [RoleWriter] The writer that should be used to update data when a permission is set.
|
|
106
125
|
def initialize(bits = 0, writer = nil)
|
|
107
126
|
@writer = writer
|
|
@@ -109,15 +128,26 @@ module Discordrb
|
|
|
109
128
|
@bits = if bits.is_a? Array
|
|
110
129
|
self.class.bits(bits)
|
|
111
130
|
else
|
|
112
|
-
bits
|
|
131
|
+
bits.to_i
|
|
113
132
|
end
|
|
114
133
|
|
|
115
134
|
init_vars
|
|
116
135
|
end
|
|
117
136
|
|
|
137
|
+
# Return an array of permission flag symbols for this class's permissions
|
|
138
|
+
# @example Get the permissions for the bits "9"
|
|
139
|
+
# permissions = Permissions.new(9)
|
|
140
|
+
# permissions.defined_permissions #=> [:create_instant_invite, :administrator]
|
|
141
|
+
# @return [Array<Symbol>] the permissions
|
|
142
|
+
def defined_permissions
|
|
143
|
+
FLAGS.filter_map { |value, name| @bits.anybits?((1 << value)) ? name : nil }
|
|
144
|
+
end
|
|
145
|
+
|
|
118
146
|
# Comparison based on permission bits
|
|
119
147
|
def ==(other)
|
|
148
|
+
# rubocop:disable Lint/Void
|
|
120
149
|
false unless other.is_a? Discordrb::Permissions
|
|
150
|
+
# rubocop:enable Lint/Void
|
|
121
151
|
bits == other.bits
|
|
122
152
|
end
|
|
123
153
|
end
|
|
@@ -154,6 +184,12 @@ module Discordrb
|
|
|
154
184
|
# has_manage_channels = member.defined_permission?(:manage_channels)
|
|
155
185
|
# @return [true, false] whether or not this user has the permission defined.
|
|
156
186
|
def defined_permission?(action, channel = nil)
|
|
187
|
+
# For slash commands we may not have access to the server or role
|
|
188
|
+
# permissions. In this case we use the permissions given to us by the
|
|
189
|
+
# interaction. If attempting to check against a specific channel the check
|
|
190
|
+
# is skipped.
|
|
191
|
+
return @permissions.__send__(action) if @permissions && channel.nil?
|
|
192
|
+
|
|
157
193
|
# Get the permission the user's roles have
|
|
158
194
|
role_permission = defined_role_permission?(action, channel)
|
|
159
195
|
|
|
@@ -179,7 +215,7 @@ module Discordrb
|
|
|
179
215
|
private
|
|
180
216
|
|
|
181
217
|
def defined_role_permission?(action, channel)
|
|
182
|
-
roles_to_check = [@server.everyone_role] +
|
|
218
|
+
roles_to_check = [@server.everyone_role] + roles
|
|
183
219
|
|
|
184
220
|
# For each role, check if
|
|
185
221
|
# (1) the channel explicitly allows or permits an action for the role and
|
data/lib/discordrb/version.rb
CHANGED
|
@@ -102,8 +102,8 @@ module Discordrb::Voice
|
|
|
102
102
|
'-ar', '48000',
|
|
103
103
|
'-ac', '2',
|
|
104
104
|
'pipe:1',
|
|
105
|
-
filter_volume_argument
|
|
106
|
-
].concat(options.split).reject {|segment| segment.nil? || segment == '' }
|
|
105
|
+
filter_volume_argument
|
|
106
|
+
].concat(options.split).reject { |segment| segment.nil? || segment == '' }
|
|
107
107
|
end
|
|
108
108
|
|
|
109
109
|
def filter_volume_argument
|
|
@@ -42,10 +42,10 @@ module Discordrb::Voice
|
|
|
42
42
|
attr_writer :secret_key
|
|
43
43
|
|
|
44
44
|
# The UDP encryption mode
|
|
45
|
-
attr_reader :mode
|
|
45
|
+
attr_reader :mode
|
|
46
46
|
|
|
47
47
|
# @!visibility private
|
|
48
|
-
attr_writer :mode
|
|
48
|
+
attr_writer :mode
|
|
49
49
|
|
|
50
50
|
# Creates a new UDP connection. Only creates a socket as the discovery reply may come before the data is
|
|
51
51
|
# initialized.
|
|
@@ -69,9 +69,9 @@ module Discordrb::Voice
|
|
|
69
69
|
# @return [Array(String, Integer)] the IP and port received from the discovery reply.
|
|
70
70
|
def receive_discovery_reply
|
|
71
71
|
# Wait for a UDP message
|
|
72
|
-
message = @socket.recv(
|
|
73
|
-
ip = message[
|
|
74
|
-
port = message[-2
|
|
72
|
+
message = @socket.recv(74)
|
|
73
|
+
ip = message[8..-3].delete("\0")
|
|
74
|
+
port = message[-2..].unpack1('n')
|
|
75
75
|
[ip, port]
|
|
76
76
|
end
|
|
77
77
|
|
|
@@ -98,10 +98,21 @@ module Discordrb::Voice
|
|
|
98
98
|
# Sends the UDP discovery packet with the internally stored SSRC. Discord will send a reply afterwards which can
|
|
99
99
|
# be received using {#receive_discovery_reply}
|
|
100
100
|
def send_discovery
|
|
101
|
-
|
|
101
|
+
# Create empty packet
|
|
102
|
+
discovery_packet = ''
|
|
102
103
|
|
|
103
|
-
# Add
|
|
104
|
+
# Add Type request (0x1 = request, 0x2 = response)
|
|
105
|
+
discovery_packet += [0x1].pack('n')
|
|
106
|
+
|
|
107
|
+
# Add Length (excluding Type and itself = 70)
|
|
108
|
+
discovery_packet += [70].pack('n')
|
|
109
|
+
|
|
110
|
+
# Add SSRC
|
|
111
|
+
discovery_packet += [@ssrc].pack('N')
|
|
112
|
+
|
|
113
|
+
# Add 66 zeroes so the packet is 74 bytes long
|
|
104
114
|
discovery_packet += "\0" * 66
|
|
115
|
+
|
|
105
116
|
send_packet(discovery_packet)
|
|
106
117
|
end
|
|
107
118
|
|
|
@@ -22,10 +22,10 @@ module Discordrb::Voice
|
|
|
22
22
|
# {VoiceBot#adjust_offset}, and {VoiceBot#adjust_average}.
|
|
23
23
|
class VoiceBot
|
|
24
24
|
# @return [Channel] the current voice channel
|
|
25
|
-
attr_reader :channel
|
|
25
|
+
attr_reader :channel
|
|
26
26
|
|
|
27
27
|
# @!visibility private
|
|
28
|
-
attr_writer :channel
|
|
28
|
+
attr_writer :channel
|
|
29
29
|
|
|
30
30
|
# @return [Integer, nil] the amount of time the stream has been playing, or `nil` if nothing has been played yet.
|
|
31
31
|
attr_reader :stream_time
|
|
@@ -149,7 +149,7 @@ module Discordrb::Voice
|
|
|
149
149
|
|
|
150
150
|
# Sets whether or not the bot is speaking (green circle around user).
|
|
151
151
|
# @param value [true, false, Integer] whether or not the bot should be speaking, or a bitmask denoting the audio type
|
|
152
|
-
# @note https://
|
|
152
|
+
# @note https://discord.com/developers/docs/topics/voice-connections#speaking for information on the speaking bitmask
|
|
153
153
|
def speaking=(value)
|
|
154
154
|
@playing = value
|
|
155
155
|
@ws.send_speaking(value)
|
data/lib/discordrb/webhooks.rb
CHANGED
data/lib/discordrb/websocket.rb
CHANGED
|
@@ -2,16 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require 'websocket-client-simple'
|
|
4
4
|
|
|
5
|
-
# The WSCS module which we're hooking
|
|
6
|
-
# @see Websocket::Client::Simple::Client
|
|
7
|
-
module WebSocket::Client::Simple
|
|
8
|
-
# Patch to the WSCS class to allow reading the internal thread
|
|
9
|
-
class Client
|
|
10
|
-
# @return [Thread] the internal thread this client is using for the event loop.
|
|
11
|
-
attr_reader :thread
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
5
|
module Discordrb
|
|
16
6
|
# Utility wrapper class that abstracts an instance of WSCS. Useful should we decide that WSCS isn't good either -
|
|
17
7
|
# in that case we can just switch to something else
|
data/lib/discordrb.rb
CHANGED
|
@@ -10,13 +10,13 @@ module Discordrb
|
|
|
10
10
|
Thread.current[:discordrb_name] = 'main'
|
|
11
11
|
|
|
12
12
|
# The default debug logger used by discordrb.
|
|
13
|
-
LOGGER = Logger.new(ENV
|
|
13
|
+
LOGGER = Logger.new(ENV.fetch('DISCORDRB_FANCY_LOG', false))
|
|
14
14
|
|
|
15
15
|
# The Unix timestamp Discord IDs are based on
|
|
16
16
|
DISCORD_EPOCH = 1_420_070_400_000
|
|
17
17
|
|
|
18
18
|
# Used to declare what events you wish to recieve from Discord.
|
|
19
|
-
# @see https://
|
|
19
|
+
# @see https://discord.com/developers/docs/topics/gateway#gateway-intents
|
|
20
20
|
INTENTS = {
|
|
21
21
|
servers: 1 << 0,
|
|
22
22
|
server_members: 1 << 1,
|
|
@@ -35,17 +35,39 @@ module Discordrb
|
|
|
35
35
|
direct_message_typing: 1 << 14
|
|
36
36
|
}.freeze
|
|
37
37
|
|
|
38
|
-
#
|
|
38
|
+
# All available intents
|
|
39
39
|
ALL_INTENTS = INTENTS.values.reduce(&:|)
|
|
40
40
|
|
|
41
|
+
# All unprivileged intents
|
|
42
|
+
# @see https://discord.com/developers/docs/topics/gateway#privileged-intents
|
|
43
|
+
UNPRIVILEGED_INTENTS = ALL_INTENTS & ~(INTENTS[:server_members] | INTENTS[:server_presences])
|
|
44
|
+
|
|
45
|
+
# No intents
|
|
46
|
+
NO_INTENTS = 0
|
|
47
|
+
|
|
41
48
|
# Compares two objects based on IDs - either the objects' IDs are equal, or one object is equal to the other's ID.
|
|
42
|
-
def self.id_compare(one_id, other)
|
|
49
|
+
def self.id_compare?(one_id, other)
|
|
43
50
|
other.respond_to?(:resolve_id) ? (one_id.resolve_id == other.resolve_id) : (one_id == other)
|
|
44
51
|
end
|
|
45
52
|
|
|
53
|
+
# @deprecated Please use {Discordrb.id_compare?}
|
|
54
|
+
singleton_class.alias_method :id_compare, :id_compare?
|
|
55
|
+
|
|
46
56
|
# The maximum length a Discord message can have
|
|
47
57
|
CHARACTER_LIMIT = 2000
|
|
48
58
|
|
|
59
|
+
# For creating timestamps with {timestamp}
|
|
60
|
+
# @see https://discord.com/developers/docs/reference#message-formatting-timestamp-styles
|
|
61
|
+
TIMESTAMP_STYLES = {
|
|
62
|
+
short_time: 't', # 16:20
|
|
63
|
+
long_time: 'T', # 16:20:30
|
|
64
|
+
short_date: 'd', # 20/04/2021
|
|
65
|
+
long_date: 'D', # 20 April 2021
|
|
66
|
+
short_datetime: 'f', # 20 April 2021 16:20
|
|
67
|
+
long_datetime: 'F', # Tuesday, 20 April 2021 16:20
|
|
68
|
+
relative: 'R' # 2 months ago
|
|
69
|
+
}.freeze
|
|
70
|
+
|
|
49
71
|
# Splits a message into chunks of 2000 characters. Attempts to split by lines if possible.
|
|
50
72
|
# @param msg [String] The message to split.
|
|
51
73
|
# @return [Array<String>] the message split into chunks
|
|
@@ -76,7 +98,7 @@ module Discordrb
|
|
|
76
98
|
ideal_ary = ideal.length > CHARACTER_LIMIT ? ideal.split(/(.{1,#{CHARACTER_LIMIT}}\b|.{1,#{CHARACTER_LIMIT}})/o).reject(&:empty?) : [ideal]
|
|
77
99
|
|
|
78
100
|
# Slice off the ideal part and strip newlines
|
|
79
|
-
rest = msg[ideal.length
|
|
101
|
+
rest = msg[ideal.length..].strip
|
|
80
102
|
|
|
81
103
|
# If none remains, return an empty array -> we're done
|
|
82
104
|
return [] unless rest
|
|
@@ -84,6 +106,33 @@ module Discordrb
|
|
|
84
106
|
# Otherwise, call the method recursively to split the rest of the string and add it onto the ideal array
|
|
85
107
|
ideal_ary + split_message(rest)
|
|
86
108
|
end
|
|
109
|
+
|
|
110
|
+
# @param time [Time, Integer] The time to create the timestamp from, or a unix timestamp integer.
|
|
111
|
+
# @param style [Symbol, String] One of the keys from {TIMESTAMP_STYLES} or a string with the style.
|
|
112
|
+
# @return [String]
|
|
113
|
+
# @example
|
|
114
|
+
# Discordrb.timestamp(Time.now, :short_time)
|
|
115
|
+
# # => "<t:1632146954:t>"
|
|
116
|
+
def self.timestamp(time, style = nil)
|
|
117
|
+
if style.nil?
|
|
118
|
+
"<t:#{time.to_i}>"
|
|
119
|
+
else
|
|
120
|
+
"<t:#{time.to_i}:#{TIMESTAMP_STYLES[style] || style}>"
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# A utility method to base64 encode a file like object using its mime type.
|
|
125
|
+
# @param file [File, #read] A file like object that responds to #read.
|
|
126
|
+
# @return [String] The file object encoded as base64 image data.
|
|
127
|
+
def self.encode64(file)
|
|
128
|
+
path_method = %i[original_filename path local_path].find { |method| file.respond_to?(method) }
|
|
129
|
+
|
|
130
|
+
raise ArgumentError, 'File object must respond to original_filename, path, or local path.' unless path_method
|
|
131
|
+
raise ArgumentError, 'File object must respond to read.' unless file.respond_to?(:read)
|
|
132
|
+
|
|
133
|
+
mime_type = MIME::Types.type_for(file.__send__(path_method)).first&.to_s || 'image/jpeg'
|
|
134
|
+
"data:#{mime_type};base64,#{Base64.encode64(file.read).strip}"
|
|
135
|
+
end
|
|
87
136
|
end
|
|
88
137
|
|
|
89
138
|
# In discordrb, Integer and {String} are monkey-patched to allow for easy resolution of IDs
|