discordrb 3.1.1 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of discordrb might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.circleci/config.yml +126 -0
- data/.codeclimate.yml +16 -0
- data/.github/CONTRIBUTING.md +13 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
- data/.github/pull_request_template.md +37 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +39 -33
- data/.travis.yml +27 -2
- data/.yardopts +1 -1
- data/CHANGELOG.md +808 -208
- data/Gemfile +4 -1
- data/LICENSE.txt +1 -1
- data/README.md +108 -53
- data/Rakefile +14 -1
- data/bin/console +1 -0
- data/bin/travis_build_docs.sh +17 -0
- data/discordrb-webhooks.gemspec +26 -0
- data/discordrb.gemspec +24 -15
- data/lib/discordrb.rb +75 -2
- data/lib/discordrb/allowed_mentions.rb +36 -0
- data/lib/discordrb/api.rb +126 -27
- data/lib/discordrb/api/channel.rb +165 -43
- data/lib/discordrb/api/invite.rb +10 -7
- data/lib/discordrb/api/server.rb +240 -61
- data/lib/discordrb/api/user.rb +26 -24
- data/lib/discordrb/api/webhook.rb +83 -0
- data/lib/discordrb/await.rb +1 -2
- data/lib/discordrb/bot.rb +417 -149
- data/lib/discordrb/cache.rb +42 -10
- data/lib/discordrb/colour_rgb.rb +43 -0
- data/lib/discordrb/commands/command_bot.rb +186 -31
- data/lib/discordrb/commands/container.rb +30 -16
- data/lib/discordrb/commands/parser.rb +102 -47
- data/lib/discordrb/commands/rate_limiter.rb +18 -17
- data/lib/discordrb/container.rb +245 -41
- data/lib/discordrb/data.rb +27 -2511
- data/lib/discordrb/data/activity.rb +264 -0
- data/lib/discordrb/data/application.rb +50 -0
- data/lib/discordrb/data/attachment.rb +56 -0
- data/lib/discordrb/data/audit_logs.rb +345 -0
- data/lib/discordrb/data/channel.rb +849 -0
- data/lib/discordrb/data/embed.rb +251 -0
- data/lib/discordrb/data/emoji.rb +82 -0
- data/lib/discordrb/data/integration.rb +83 -0
- data/lib/discordrb/data/invite.rb +137 -0
- data/lib/discordrb/data/member.rb +297 -0
- data/lib/discordrb/data/message.rb +334 -0
- data/lib/discordrb/data/overwrite.rb +102 -0
- data/lib/discordrb/data/profile.rb +91 -0
- data/lib/discordrb/data/reaction.rb +33 -0
- data/lib/discordrb/data/recipient.rb +34 -0
- data/lib/discordrb/data/role.rb +191 -0
- data/lib/discordrb/data/server.rb +1002 -0
- data/lib/discordrb/data/user.rb +204 -0
- data/lib/discordrb/data/voice_region.rb +45 -0
- data/lib/discordrb/data/voice_state.rb +41 -0
- data/lib/discordrb/data/webhook.rb +145 -0
- data/lib/discordrb/errors.rb +36 -2
- data/lib/discordrb/events/bans.rb +7 -5
- data/lib/discordrb/events/channels.rb +2 -0
- data/lib/discordrb/events/generic.rb +19 -3
- data/lib/discordrb/events/guilds.rb +129 -6
- data/lib/discordrb/events/invites.rb +125 -0
- data/lib/discordrb/events/members.rb +6 -2
- data/lib/discordrb/events/message.rb +86 -36
- data/lib/discordrb/events/presence.rb +23 -16
- data/lib/discordrb/events/raw.rb +47 -0
- data/lib/discordrb/events/reactions.rb +159 -0
- data/lib/discordrb/events/roles.rb +7 -6
- data/lib/discordrb/events/typing.rb +9 -5
- data/lib/discordrb/events/voice_server_update.rb +47 -0
- data/lib/discordrb/events/voice_state_update.rb +29 -9
- data/lib/discordrb/events/webhooks.rb +64 -0
- data/lib/discordrb/gateway.rb +219 -88
- data/lib/discordrb/id_object.rb +39 -0
- data/lib/discordrb/light.rb +1 -1
- data/lib/discordrb/light/integrations.rb +1 -1
- data/lib/discordrb/light/light_bot.rb +1 -1
- data/lib/discordrb/logger.rb +12 -11
- data/lib/discordrb/paginator.rb +57 -0
- data/lib/discordrb/permissions.rb +148 -14
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +14 -15
- data/lib/discordrb/voice/network.rb +86 -45
- data/lib/discordrb/voice/sodium.rb +96 -0
- data/lib/discordrb/voice/voice_bot.rb +52 -40
- data/lib/discordrb/webhooks.rb +12 -0
- data/lib/discordrb/websocket.rb +2 -2
- metadata +137 -34
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Discordrb
|
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
|
+
Discordrb.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
|
data/lib/discordrb/light.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'discordrb/light/light_bot'
|
4
4
|
|
5
|
-
# This module contains classes to allow connections to bots without a connection to the gateway socket, i.
|
5
|
+
# This module contains classes to allow connections to bots without a connection to the gateway socket, i.e. bots
|
6
6
|
# that only use the REST part of the API.
|
7
7
|
module Discordrb::Light
|
8
8
|
end
|
@@ -49,7 +49,7 @@ module Discordrb::Light
|
|
49
49
|
# Twitch account connection of the server owner).
|
50
50
|
attr_reader :server_connection
|
51
51
|
|
52
|
-
# @return [Connection] the connection integrated with the server (i.
|
52
|
+
# @return [Connection] the connection integrated with the server (i.e. your connection)
|
53
53
|
attr_reader :integrated_connection
|
54
54
|
|
55
55
|
# @!visibility private
|
@@ -6,7 +6,7 @@ require 'discordrb/api/user'
|
|
6
6
|
require 'discordrb/light/data'
|
7
7
|
require 'discordrb/light/integrations'
|
8
8
|
|
9
|
-
# This module contains classes to allow connections to bots without a connection to the gateway socket, i.
|
9
|
+
# This module contains classes to allow connections to bots without a connection to the gateway socket, i.e. bots
|
10
10
|
# that only use the REST part of the API.
|
11
11
|
module Discordrb::Light
|
12
12
|
# A bot that only uses the REST part of the API. Hierarchically unrelated to the regular {Discordrb::Bot}. Useful to
|
data/lib/discordrb/logger.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Discordrb
|
4
4
|
# The format log timestamps should be in, in strftime format
|
5
|
-
LOG_TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S.%L'
|
5
|
+
LOG_TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S.%L'
|
6
6
|
|
7
7
|
# Logs debug messages
|
8
8
|
class Logger
|
@@ -18,7 +18,7 @@ module Discordrb
|
|
18
18
|
# Creates a new logger.
|
19
19
|
# @param fancy [true, false] Whether this logger uses fancy mode (ANSI escape codes to make the output colourful)
|
20
20
|
# @param streams [Array<IO>, Array<#puts & #flush>] the streams the logger should write to.
|
21
|
-
def initialize(fancy = false, streams = [
|
21
|
+
def initialize(fancy = false, streams = [$stdout])
|
22
22
|
@fancy = fancy
|
23
23
|
self.mode = :normal
|
24
24
|
|
@@ -33,14 +33,15 @@ module Discordrb
|
|
33
33
|
warn: { long: 'WARN', short: '!', format_code: "\u001B[33m" }, # yellow
|
34
34
|
error: { long: 'ERROR', short: '✗', format_code: "\u001B[31m" }, # red
|
35
35
|
out: { long: 'OUT', short: '→', format_code: "\u001B[36m" }, # cyan
|
36
|
-
in: { long: 'IN', short: '←', format_code: "\u001B[35m" } # purple
|
36
|
+
in: { long: 'IN', short: '←', format_code: "\u001B[35m" }, # purple
|
37
|
+
ratelimit: { long: 'RATELIMIT', short: 'R', format_code: "\u001B[41m" } # red background
|
37
38
|
}.freeze
|
38
39
|
|
39
40
|
# The ANSI format code that resets formatting
|
40
|
-
FORMAT_RESET = "\u001B[0m"
|
41
|
+
FORMAT_RESET = "\u001B[0m"
|
41
42
|
|
42
43
|
# The ANSI format code that makes something bold
|
43
|
-
FORMAT_BOLD = "\u001B[1m"
|
44
|
+
FORMAT_BOLD = "\u001B[1m"
|
44
45
|
|
45
46
|
MODES.each do |mode, hash|
|
46
47
|
define_method(mode) do |message|
|
@@ -65,15 +66,15 @@ module Discordrb
|
|
65
66
|
def mode=(value)
|
66
67
|
case value
|
67
68
|
when :debug
|
68
|
-
@enabled_modes = [
|
69
|
+
@enabled_modes = %i[debug good info warn error out in ratelimit]
|
69
70
|
when :verbose
|
70
|
-
@enabled_modes = [
|
71
|
+
@enabled_modes = %i[good info warn error out in ratelimit]
|
71
72
|
when :normal
|
72
|
-
@enabled_modes = [
|
73
|
+
@enabled_modes = %i[info warn error ratelimit]
|
73
74
|
when :quiet
|
74
|
-
@enabled_modes = [
|
75
|
+
@enabled_modes = %i[warn error]
|
75
76
|
when :silent
|
76
|
-
@enabled_modes = []
|
77
|
+
@enabled_modes = %i[]
|
77
78
|
end
|
78
79
|
end
|
79
80
|
|
@@ -91,7 +92,7 @@ module Discordrb
|
|
91
92
|
timestamp = Time.now.strftime(LOG_TIMESTAMP_FORMAT)
|
92
93
|
|
93
94
|
# Redact token if set
|
94
|
-
log = if @token
|
95
|
+
log = if @token && @token != ''
|
95
96
|
message.to_s.gsub(@token, 'REDACTED_TOKEN')
|
96
97
|
else
|
97
98
|
message.to_s
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Discordrb
|
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
|
+
# Creates a new {Paginator}
|
11
|
+
# @param limit [Integer] the maximum number of items to request before stopping
|
12
|
+
# @param direction [:up, :down] the order in which results are returned in
|
13
|
+
# @yield [Array, nil] the last page of results, or nil if this is the first iteration.
|
14
|
+
# This should be used to request the next page of results.
|
15
|
+
# @yieldreturn [Array] the next page of results
|
16
|
+
def initialize(limit, direction, &block)
|
17
|
+
@count = 0
|
18
|
+
@limit = limit
|
19
|
+
@direction = direction
|
20
|
+
@block = block
|
21
|
+
end
|
22
|
+
|
23
|
+
# Yields every item produced by the wrapped request, until it returns
|
24
|
+
# no more results or the configured `limit` is reached.
|
25
|
+
def each
|
26
|
+
last_page = nil
|
27
|
+
until limit_check
|
28
|
+
page = @block.call(last_page)
|
29
|
+
return if page.empty?
|
30
|
+
|
31
|
+
enumerator = case @direction
|
32
|
+
when :down
|
33
|
+
page.each
|
34
|
+
when :up
|
35
|
+
page.reverse_each
|
36
|
+
end
|
37
|
+
|
38
|
+
enumerator.each do |item|
|
39
|
+
yield item
|
40
|
+
@count += 1
|
41
|
+
break if limit_check
|
42
|
+
end
|
43
|
+
|
44
|
+
last_page = page
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Whether the paginator limit has been exceeded
|
51
|
+
def limit_check
|
52
|
+
return false if @limit.nil?
|
53
|
+
|
54
|
+
@count >= @limit
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -4,8 +4,7 @@ module Discordrb
|
|
4
4
|
# List of permissions Discord uses
|
5
5
|
class Permissions
|
6
6
|
# This hash maps bit positions to logical permissions.
|
7
|
-
|
8
|
-
Flags = {
|
7
|
+
FLAGS = {
|
9
8
|
# Bit => Permission # Value
|
10
9
|
0 => :create_instant_invite, # 1
|
11
10
|
1 => :kick_members, # 2
|
@@ -13,10 +12,10 @@ module Discordrb
|
|
13
12
|
3 => :administrator, # 8
|
14
13
|
4 => :manage_channels, # 16
|
15
14
|
5 => :manage_server, # 32
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
6 => :add_reactions, # 64
|
16
|
+
7 => :view_audit_log, # 128
|
17
|
+
8 => :priority_speaker, # 256
|
18
|
+
9 => :stream, # 512
|
20
19
|
10 => :read_messages, # 1024
|
21
20
|
11 => :send_messages, # 2048
|
22
21
|
12 => :send_tts_messages, # 4096
|
@@ -26,7 +25,7 @@ module Discordrb
|
|
26
25
|
16 => :read_message_history, # 65536
|
27
26
|
17 => :mention_everyone, # 131072
|
28
27
|
18 => :use_external_emoji, # 262144
|
29
|
-
|
28
|
+
19 => :view_server_insights, # 524288
|
30
29
|
20 => :connect, # 1048576
|
31
30
|
21 => :speak, # 2097152
|
32
31
|
22 => :mute_members, # 4194304
|
@@ -35,11 +34,14 @@ module Discordrb
|
|
35
34
|
25 => :use_voice_activity, # 33554432
|
36
35
|
26 => :change_nickname, # 67108864
|
37
36
|
27 => :manage_nicknames, # 134217728
|
38
|
-
28 => :manage_roles
|
37
|
+
28 => :manage_roles, # 268435456, also Manage Permissions
|
38
|
+
29 => :manage_webhooks, # 536870912
|
39
|
+
30 => :manage_emojis # 1073741824
|
39
40
|
}.freeze
|
40
41
|
|
41
|
-
|
42
|
+
FLAGS.each do |position, flag|
|
42
43
|
attr_reader flag
|
44
|
+
|
43
45
|
define_method "can_#{flag}=" do |value|
|
44
46
|
new_bits = @bits
|
45
47
|
if value
|
@@ -47,18 +49,19 @@ module Discordrb
|
|
47
49
|
else
|
48
50
|
new_bits &= ~(1 << position)
|
49
51
|
end
|
50
|
-
@writer
|
52
|
+
@writer&.write(new_bits)
|
51
53
|
@bits = new_bits
|
52
54
|
init_vars
|
53
55
|
end
|
54
56
|
end
|
55
57
|
|
56
58
|
alias_method :can_administrate=, :can_administrator=
|
59
|
+
alias_method :administrate, :administrator
|
57
60
|
|
58
61
|
attr_reader :bits
|
59
62
|
|
60
63
|
# Set the raw bitset of this permission object
|
61
|
-
# @param bits [
|
64
|
+
# @param bits [Integer] A number whose binary representation is the desired bitset.
|
62
65
|
def bits=(bits)
|
63
66
|
@bits = bits
|
64
67
|
init_vars
|
@@ -66,20 +69,151 @@ module Discordrb
|
|
66
69
|
|
67
70
|
# Initialize the instance variables based on the bitset.
|
68
71
|
def init_vars
|
69
|
-
|
72
|
+
FLAGS.each do |position, flag|
|
70
73
|
flag_set = ((@bits >> position) & 0x1) == 1
|
71
74
|
instance_variable_set "@#{flag}", flag_set
|
72
75
|
end
|
73
76
|
end
|
74
77
|
|
78
|
+
# Return the corresponding bits for an array of permission flag symbols.
|
79
|
+
# This is a class method that can be used to calculate bits instead
|
80
|
+
# of instancing a new Permissions object.
|
81
|
+
# @example Get the bits for permissions that could allow/deny read messages, connect, and speak
|
82
|
+
# Permissions.bits [:read_messages, :connect, :speak] #=> 3146752
|
83
|
+
# @param list [Array<Symbol>]
|
84
|
+
# @return [Integer] the computed permissions integer
|
85
|
+
def self.bits(list)
|
86
|
+
value = 0
|
87
|
+
|
88
|
+
FLAGS.each do |position, flag|
|
89
|
+
value += 2**position if list.include? flag
|
90
|
+
end
|
91
|
+
|
92
|
+
value
|
93
|
+
end
|
94
|
+
|
75
95
|
# Create a new Permissions object either as a blank slate to add permissions to (for example for
|
76
96
|
# {Channel#define_overwrite}) or from existing bit data to read out.
|
77
|
-
# @
|
97
|
+
# @example Create a permissions object that could allow/deny read messages, connect, and speak by setting flags
|
98
|
+
# permission = Permissions.new
|
99
|
+
# permission.can_read_messages = true
|
100
|
+
# permission.can_connect = true
|
101
|
+
# permission.can_speak = true
|
102
|
+
# @example Create a permissions object that could allow/deny read messages, connect, and speak by an array of symbols
|
103
|
+
# 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
|
78
105
|
# @param writer [RoleWriter] The writer that should be used to update data when a permission is set.
|
79
106
|
def initialize(bits = 0, writer = nil)
|
80
107
|
@writer = writer
|
81
|
-
|
108
|
+
|
109
|
+
@bits = if bits.is_a? Array
|
110
|
+
self.class.bits(bits)
|
111
|
+
else
|
112
|
+
bits
|
113
|
+
end
|
114
|
+
|
82
115
|
init_vars
|
83
116
|
end
|
117
|
+
|
118
|
+
# Comparison based on permission bits
|
119
|
+
def ==(other)
|
120
|
+
false unless other.is_a? Discordrb::Permissions
|
121
|
+
bits == other.bits
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Mixin to calculate resulting permissions from overrides etc.
|
126
|
+
module PermissionCalculator
|
127
|
+
# Checks whether this user can do the particular action, regardless of whether it has the permission defined,
|
128
|
+
# through for example being the server owner or having the Manage Roles permission
|
129
|
+
# @param action [Symbol] The permission that should be checked. See also {Permissions::FLAGS} for a list.
|
130
|
+
# @param channel [Channel, nil] If channel overrides should be checked too, this channel specifies where the overrides should be checked.
|
131
|
+
# @example Check if the bot can send messages to a specific channel in a server.
|
132
|
+
# bot_profile = bot.profile.on(event.server)
|
133
|
+
# can_send_messages = bot_profile.permission?(:send_messages, channel)
|
134
|
+
# @return [true, false] whether or not this user has the permission.
|
135
|
+
def permission?(action, channel = nil)
|
136
|
+
# If the member is the server owner, it irrevocably has all permissions.
|
137
|
+
return true if owner?
|
138
|
+
|
139
|
+
# First, check whether the user has Manage Roles defined.
|
140
|
+
# (Coincidentally, Manage Permissions is the same permission as Manage Roles, and a
|
141
|
+
# Manage Permissions deny overwrite will override Manage Roles, so we can just check for
|
142
|
+
# Manage Roles once and call it a day.)
|
143
|
+
return true if defined_permission?(:administrator, channel)
|
144
|
+
|
145
|
+
# Otherwise, defer to defined_permission
|
146
|
+
defined_permission?(action, channel)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Checks whether this user has a particular permission defined (i.e. not implicit, through for example
|
150
|
+
# Manage Roles)
|
151
|
+
# @param action [Symbol] The permission that should be checked. See also {Permissions::FLAGS} for a list.
|
152
|
+
# @param channel [Channel, nil] If channel overrides should be checked too, this channel specifies where the overrides should be checked.
|
153
|
+
# @example Check if a member has the Manage Channels permission defined in the server.
|
154
|
+
# has_manage_channels = member.defined_permission?(:manage_channels)
|
155
|
+
# @return [true, false] whether or not this user has the permission defined.
|
156
|
+
def defined_permission?(action, channel = nil)
|
157
|
+
# Get the permission the user's roles have
|
158
|
+
role_permission = defined_role_permission?(action, channel)
|
159
|
+
|
160
|
+
# Once we have checked the role permission, we have to check the channel overrides for the
|
161
|
+
# specific user
|
162
|
+
user_specific_override = permission_overwrite(action, channel, id) # Use the ID reader as members have no ID instance variable
|
163
|
+
|
164
|
+
# Merge the two permissions - if an override is defined, it has to be allow, otherwise we only care about the role
|
165
|
+
return role_permission unless user_specific_override
|
166
|
+
|
167
|
+
user_specific_override == :allow
|
168
|
+
end
|
169
|
+
|
170
|
+
# Define methods for querying permissions
|
171
|
+
Discordrb::Permissions::FLAGS.each_value do |flag|
|
172
|
+
define_method "can_#{flag}?" do |channel = nil|
|
173
|
+
permission? flag, channel
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
alias_method :can_administrate?, :can_administrator?
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def defined_role_permission?(action, channel)
|
182
|
+
roles_to_check = [@server.everyone_role] + @roles
|
183
|
+
|
184
|
+
# For each role, check if
|
185
|
+
# (1) the channel explicitly allows or permits an action for the role and
|
186
|
+
# (2) if the user is allowed to do the action if the channel doesn't specify
|
187
|
+
roles_to_check.sort_by(&:position).reduce(false) do |can_act, role|
|
188
|
+
# Get the override defined for the role on the channel
|
189
|
+
channel_allow = permission_overwrite(action, channel, role.id)
|
190
|
+
if channel_allow
|
191
|
+
# If the channel has an override, check whether it is an allow - if yes,
|
192
|
+
# the user can act, if not, it can't
|
193
|
+
break true if channel_allow == :allow
|
194
|
+
|
195
|
+
false
|
196
|
+
else
|
197
|
+
# Otherwise defer to the role
|
198
|
+
role.permissions.instance_variable_get("@#{action}") || can_act
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def permission_overwrite(action, channel, id)
|
204
|
+
# If no overwrites are defined, or no channel is set, no overwrite will be present
|
205
|
+
return nil unless channel && channel.permission_overwrites[id]
|
206
|
+
|
207
|
+
# Otherwise, check the allow and deny objects
|
208
|
+
allow = channel.permission_overwrites[id].allow
|
209
|
+
deny = channel.permission_overwrites[id].deny
|
210
|
+
if allow.instance_variable_get("@#{action}")
|
211
|
+
:allow
|
212
|
+
elsif deny.instance_variable_get("@#{action}")
|
213
|
+
:deny
|
214
|
+
end
|
215
|
+
|
216
|
+
# If there's no variable defined, nil will implicitly be returned
|
217
|
+
end
|
84
218
|
end
|
85
219
|
end
|
data/lib/discordrb/version.rb
CHANGED
@@ -24,16 +24,14 @@ module Discordrb::Voice
|
|
24
24
|
|
25
25
|
# Create a new encoder
|
26
26
|
def initialize
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
sample_rate = 48_000
|
28
|
+
frame_size = 960
|
29
|
+
channels = 2
|
30
30
|
@filter_volume = 1
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
raise LoadError, 'Opus unavailable - voice not supported! Please install opus for voice support to work.'
|
36
|
-
end
|
32
|
+
raise LoadError, 'Opus unavailable - voice not supported! Please install opus for voice support to work.' unless OPUS_AVAILABLE
|
33
|
+
|
34
|
+
@opus = Opus::Encoder.new(sample_rate, frame_size, channels)
|
37
35
|
end
|
38
36
|
|
39
37
|
# Set the opus encoding bitrate
|
@@ -76,21 +74,21 @@ module Discordrb::Voice
|
|
76
74
|
# an audio track. For a list of supported formats, see https://ffmpeg.org/general.html#Audio-Codecs. It even accepts
|
77
75
|
# URLs, though encoding them is pretty slow - I recommend to make a stream of it and then use {#encode_io} instead.
|
78
76
|
# @param file [String] The path or URL to encode.
|
77
|
+
# @param options [String] ffmpeg options to pass after the -i flag
|
79
78
|
# @return [IO] the audio, encoded as s16le PCM
|
80
|
-
def encode_file(file)
|
81
|
-
command = "#{ffmpeg_command} -loglevel 0 -i \"#{file}\" -f s16le -ar 48000 -ac 2 #{filter_volume_argument} pipe:1"
|
79
|
+
def encode_file(file, options = '')
|
80
|
+
command = "#{ffmpeg_command} -loglevel 0 -i \"#{file}\" #{options} -f s16le -ar 48000 -ac 2 #{filter_volume_argument} pipe:1"
|
82
81
|
IO.popen(command)
|
83
82
|
end
|
84
83
|
|
85
84
|
# Encodes an arbitrary IO audio stream using ffmpeg. Accepts pretty much any media format, even videos with audio
|
86
85
|
# tracks. For a list of supported audio formats, see https://ffmpeg.org/general.html#Audio-Codecs.
|
87
86
|
# @param io [IO] The stream to encode.
|
87
|
+
# @param options [String] ffmpeg options to pass after the -i flag
|
88
88
|
# @return [IO] the audio, encoded as s16le PCM
|
89
|
-
def encode_io(io)
|
90
|
-
|
91
|
-
command
|
92
|
-
spawn(command, in: io, out: writer)
|
93
|
-
ret_io
|
89
|
+
def encode_io(io, options = '')
|
90
|
+
command = "#{ffmpeg_command} -loglevel 0 -i - #{options} -f s16le -ar 48000 -ac 2 #{filter_volume_argument} pipe:1"
|
91
|
+
IO.popen(command, in: io)
|
94
92
|
end
|
95
93
|
|
96
94
|
private
|
@@ -101,6 +99,7 @@ module Discordrb::Voice
|
|
101
99
|
|
102
100
|
def filter_volume_argument
|
103
101
|
return '' if @filter_volume == 1
|
102
|
+
|
104
103
|
@use_avconv ? "-vol #{(@filter_volume * 256).ceil}" : "-af volume=#{@filter_volume}"
|
105
104
|
end
|
106
105
|
end
|