discordrb 1.3.4 → 1.3.5
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 +4 -4
- data/.rubocop.yml +44 -0
- data/lib/discordrb.rb +4 -3
- data/lib/discordrb/api.rb +4 -3
- data/lib/discordrb/bot.rb +102 -90
- data/lib/discordrb/commands/{command-bot.rb → command_bot.rb} +32 -37
- data/lib/discordrb/commands/events.rb +1 -0
- data/lib/discordrb/commands/parser.rb +15 -14
- data/lib/discordrb/data.rb +49 -50
- data/lib/discordrb/events/{channel-create.rb → channel_create.rb} +50 -48
- data/lib/discordrb/events/{channel-delete.rb → channel_delete.rb} +5 -3
- data/lib/discordrb/events/{channel-update.rb → channel_update.rb} +7 -4
- data/lib/discordrb/events/generic.rb +11 -7
- data/lib/discordrb/events/{guild-member-update.rb → guild_member_update.rb} +8 -6
- data/lib/discordrb/events/{guild-role-create.rb → guild_role_create.rb} +7 -5
- data/lib/discordrb/events/{guild-role-delete.rb → guild_role_delete.rb} +4 -2
- data/lib/discordrb/events/{guild-role-update.rb → guild_role_update.rb} +7 -5
- data/lib/discordrb/events/message.rb +15 -11
- data/lib/discordrb/events/presence.rb +5 -3
- data/lib/discordrb/events/typing.rb +8 -6
- data/lib/discordrb/events/{voice-state-update.rb → voice_state_update.rb} +14 -13
- data/lib/discordrb/exceptions.rb +4 -2
- data/lib/discordrb/games.rb +4 -307
- data/lib/discordrb/games_list.rb +308 -0
- data/lib/discordrb/permissions.rb +10 -9
- data/lib/discordrb/version.rb +2 -1
- metadata +13 -11
@@ -6,6 +6,7 @@ require 'discordrb/commands/events'
|
|
6
6
|
# Specialized bot to run commands
|
7
7
|
|
8
8
|
module Discordrb::Commands
|
9
|
+
# Bot that supports commands and command chains
|
9
10
|
class CommandBot < Discordrb::Bot
|
10
11
|
attr_reader :attributes, :prefix
|
11
12
|
|
@@ -51,34 +52,29 @@ module Discordrb::Commands
|
|
51
52
|
users: {}
|
52
53
|
}
|
53
54
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
return unless @attributes[:help_command]
|
56
|
+
command(@attributes[:help_command], max_args: 1, description: 'Shows a list of all the commands available or displays help for a specific command.', usage: 'help [command name]') do |event, command_name|
|
57
|
+
if command_name
|
58
|
+
command = @commands[command_name.to_sym]
|
59
|
+
return "The command `#{command_name}` does not exist!" unless command
|
60
|
+
desc = command.attributes[:description] || '*No description available*'
|
61
|
+
usage = command.attributes[:usage]
|
62
|
+
result = "**`#{command_name}`**: #{desc}"
|
63
|
+
result << "\nUsage: `#{usage}`" if usage
|
64
|
+
else
|
65
|
+
available_commands = @commands.values.reject { |c| !c.attributes[:help_available] }
|
66
|
+
case available_commands.length
|
67
|
+
when 0..5
|
68
|
+
available_commands.reduce "**List of commands:**\n" do |memo, c|
|
69
|
+
memo + "**`#{c.name}`**: #{c.attributes[:description] || '*No description available*'}\n"
|
60
70
|
end
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
71
|
+
when 5..50
|
72
|
+
(available_commands.reduce "**List of commands:**\n" do |memo, c|
|
73
|
+
memo + "`#{c.name}`, "
|
74
|
+
end)[0..-3]
|
65
75
|
else
|
66
|
-
available_commands
|
67
|
-
|
68
|
-
when 0..5
|
69
|
-
available_commands.reduce "**List of commands:**\n" do |memo, command|
|
70
|
-
memo + "**`#{command.name}`**: #{command.attributes[:description] || '*No description available*'}\n"
|
71
|
-
end
|
72
|
-
when 5..50
|
73
|
-
(available_commands.reduce "**List of commands:**\n" do |memo, command|
|
74
|
-
memo + "`#{command.name}`, "
|
75
|
-
end)[0..-3]
|
76
|
-
else
|
77
|
-
event.user.pm (available_commands.reduce "**List of commands:**\n" do |memo, command|
|
78
|
-
memo + "`#{command.name}`, "
|
79
|
-
end)[0..-3]
|
80
|
-
"Sending list in PM!"
|
81
|
-
end
|
76
|
+
event.user.pm(available_commands.reduce("**List of commands:**\n") { |a, e| a + "`#{e.name}`, " })[0..-3]
|
77
|
+
'Sending list in PM!'
|
82
78
|
end
|
83
79
|
end
|
84
80
|
end
|
@@ -114,19 +110,18 @@ module Discordrb::Commands
|
|
114
110
|
message = Discordrb::Message.new(data, self)
|
115
111
|
event = CommandEvent.new(message, self)
|
116
112
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
if chain.strip.empty?
|
121
|
-
debug("Chain is empty")
|
122
|
-
return
|
123
|
-
end
|
113
|
+
return unless message.content.start_with? @prefix
|
114
|
+
chain = message.content[@prefix.length..-1]
|
124
115
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
event.respond result if result
|
116
|
+
if chain.strip.empty?
|
117
|
+
debug('Chain is empty')
|
118
|
+
return
|
129
119
|
end
|
120
|
+
|
121
|
+
debug("Parsing command chain #{chain}")
|
122
|
+
result = (@attributes[:advanced_functionality]) ? CommandChain.new(chain, self).execute(event) : simple_execute(chain, event)
|
123
|
+
result = event.saved_message + (result || '')
|
124
|
+
event.respond result if result
|
130
125
|
end
|
131
126
|
|
132
127
|
def set_user_permission(id, level)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'discordrb/events/message'
|
2
2
|
|
3
3
|
module Discordrb::Commands
|
4
|
+
# Extension of MessageEvent for commands that contains the command called, makes the bot readable and adds a message to be saved
|
4
5
|
class CommandEvent < Discordrb::Events::MessageEvent
|
5
6
|
attr_reader :bot, :saved_message
|
6
7
|
attr_accessor :command
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module Discordrb::Commands
|
2
|
+
# Command that can be called in a chain
|
2
3
|
class Command
|
3
4
|
attr_reader :attributes, :name
|
4
5
|
|
@@ -51,6 +52,7 @@ module Discordrb::Commands
|
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
55
|
+
# Command chain, may have multiple commands, nested and commands
|
54
56
|
class CommandChain
|
55
57
|
def initialize(chain, bot, subchain = false)
|
56
58
|
@attributes = bot.attributes
|
@@ -60,7 +62,8 @@ module Discordrb::Commands
|
|
60
62
|
end
|
61
63
|
|
62
64
|
def execute_bare(event)
|
63
|
-
b_start
|
65
|
+
b_start = -1
|
66
|
+
b_level = 0
|
64
67
|
result = ''
|
65
68
|
quoted = false
|
66
69
|
hacky_delim, hacky_space, hacky_prev = [0xe001, 0xe002, 0xe003].pack('U*').chars
|
@@ -100,14 +103,12 @@ module Discordrb::Commands
|
|
100
103
|
|
101
104
|
result << char if b_level <= 0
|
102
105
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
end
|
110
|
-
end
|
106
|
+
next unless char == @attributes[:sub_chain_end] && !quoted
|
107
|
+
b_level -= 1
|
108
|
+
next unless b_level == 0
|
109
|
+
nested = @chain[b_start + 1..index - 1]
|
110
|
+
subchain = CommandChain.new(nested, @bot, true)
|
111
|
+
result << subchain.execute(event)
|
111
112
|
end
|
112
113
|
|
113
114
|
event.respond("Your subchains are mismatched! Make sure you don't have any extra #{@attributes[:sub_chain_start]}'s or #{@attributes[:sub_chain_end]}'s") unless b_level == 0
|
@@ -135,8 +136,8 @@ module Discordrb::Commands
|
|
135
136
|
command.gsub! hacky_delim, @attributes[:chain_delimiter]
|
136
137
|
|
137
138
|
first_space = command.index ' '
|
138
|
-
command_name = first_space ? command[0..first_space-1] : command
|
139
|
-
arguments = first_space ? command[first_space+1..-1] : ''
|
139
|
+
command_name = first_space ? command[0..first_space - 1] : command
|
140
|
+
arguments = first_space ? command[first_space + 1..-1] : ''
|
140
141
|
|
141
142
|
# Append a previous sign if none is present
|
142
143
|
arguments << @attributes[:previous] unless arguments.include? @attributes[:previous]
|
@@ -161,7 +162,7 @@ module Discordrb::Commands
|
|
161
162
|
|
162
163
|
def execute(event)
|
163
164
|
old_chain = @chain
|
164
|
-
@bot.debug
|
165
|
+
@bot.debug 'Executing bare chain'
|
165
166
|
result = execute_bare(event)
|
166
167
|
|
167
168
|
@chain_args ||= []
|
@@ -179,7 +180,7 @@ module Discordrb::Commands
|
|
179
180
|
end
|
180
181
|
|
181
182
|
result = new_result
|
182
|
-
|
183
|
+
# TODO: more chain arguments
|
183
184
|
end
|
184
185
|
end
|
185
186
|
|
@@ -201,7 +202,7 @@ module Discordrb::Commands
|
|
201
202
|
arg.split ' '
|
202
203
|
end
|
203
204
|
|
204
|
-
chain = chain[chain_args_index+1..-1]
|
205
|
+
chain = chain[chain_args_index + 1..-1]
|
205
206
|
end
|
206
207
|
|
207
208
|
[chain_args, chain]
|
data/lib/discordrb/data.rb
CHANGED
@@ -6,6 +6,7 @@ require 'discordrb/api'
|
|
6
6
|
require 'discordrb/games'
|
7
7
|
|
8
8
|
module Discordrb
|
9
|
+
# User on Discord, including internal data like discriminators
|
9
10
|
class User
|
10
11
|
attr_reader :username, :id, :discriminator, :avatar
|
11
12
|
|
@@ -76,11 +77,11 @@ module Discordrb
|
|
76
77
|
# Determine if the user has permission to do an action
|
77
78
|
# action is a permission from Permissions::Flags.
|
78
79
|
# channel is the channel in which the action takes place (not applicable for server-wide actions).
|
79
|
-
def
|
80
|
+
def permission?(action, server, channel = nil)
|
80
81
|
# For each role, check if
|
81
82
|
# (1) the channel explicitly allows or permits an action for the role and
|
82
83
|
# (2) if the user is allowed to do the action if the channel doesn't specify
|
83
|
-
return false
|
84
|
+
return false unless @roles[server.id]
|
84
85
|
|
85
86
|
@roles[server.id].reduce(false) do |can_act, role|
|
86
87
|
channel_allow = nil
|
@@ -91,28 +92,29 @@ module Discordrb
|
|
91
92
|
channel_allow = true
|
92
93
|
elsif deny.instance_variable_get("@#{action}")
|
93
94
|
channel_allow = false
|
94
|
-
# else
|
95
|
-
# If the channel has nothing to say on the matter, we can defer to the role itself
|
96
95
|
end
|
96
|
+
# If the channel has nothing to say on the matter, we can defer to the role itself
|
97
97
|
end
|
98
98
|
if channel_allow == false
|
99
|
-
can_act =
|
99
|
+
can_act = false
|
100
100
|
elsif channel_allow == true
|
101
101
|
can_act = true
|
102
102
|
else # channel_allow == nil
|
103
103
|
can_act = role.permissions.instance_variable_get("@#{action}") || can_act
|
104
104
|
end
|
105
|
+
can_act
|
105
106
|
end
|
106
107
|
end
|
107
108
|
|
108
109
|
# Define methods for querying permissions
|
109
110
|
Discordrb::Permissions::Flags.each_value do |flag|
|
110
111
|
define_method "can_#{flag}?" do |server, channel = nil|
|
111
|
-
|
112
|
+
permission? flag, server, channel
|
112
113
|
end
|
113
114
|
end
|
114
115
|
end
|
115
116
|
|
117
|
+
# A Discord role that contains permissions and applies to certain users
|
116
118
|
class Role
|
117
119
|
attr_reader :permissions
|
118
120
|
attr_reader :name
|
@@ -121,6 +123,8 @@ module Discordrb
|
|
121
123
|
attr_reader :color
|
122
124
|
|
123
125
|
def initialize(data, bot, server = nil)
|
126
|
+
@bot = bot
|
127
|
+
@server = server
|
124
128
|
@permissions = Permissions.new(data['permissions'])
|
125
129
|
@name = data['name']
|
126
130
|
@id = data['id'].to_i
|
@@ -136,6 +140,7 @@ module Discordrb
|
|
136
140
|
end
|
137
141
|
end
|
138
142
|
|
143
|
+
# A Discord channel, including data like the topic
|
139
144
|
class Channel
|
140
145
|
attr_reader :name, :server, :type, :id, :is_private, :recipient, :topic, :position
|
141
146
|
|
@@ -144,7 +149,7 @@ module Discordrb
|
|
144
149
|
def initialize(data, bot, server = nil)
|
145
150
|
@bot = bot
|
146
151
|
|
147
|
-
#data is a sometimes a Hash and othertimes an array of Hashes, you only want the last one if it's an array
|
152
|
+
# data is a sometimes a Hash and othertimes an array of Hashes, you only want the last one if it's an array
|
148
153
|
data = data[-1] if data.is_a?(Array)
|
149
154
|
|
150
155
|
@id = data['id'].to_i
|
@@ -159,20 +164,19 @@ module Discordrb
|
|
159
164
|
else
|
160
165
|
@name = data['name']
|
161
166
|
@server = bot.server(data['guild_id'].to_i)
|
162
|
-
@server
|
167
|
+
@server ||= server
|
163
168
|
end
|
164
169
|
|
165
170
|
# Populate permission overwrites
|
166
171
|
@permission_overwrites = {}
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
end
|
172
|
+
return unless data['permission_overwrites']
|
173
|
+
data['permission_overwrites'].each do |element|
|
174
|
+
role_id = element['id'].to_i
|
175
|
+
deny = Permissions.new(element['deny'])
|
176
|
+
allow = Permissions.new(element['allow'])
|
177
|
+
@permission_overwrites[role_id] = OpenStruct.new
|
178
|
+
@permission_overwrites[role_id].deny = deny
|
179
|
+
@permission_overwrites[role_id].allow = allow
|
176
180
|
end
|
177
181
|
end
|
178
182
|
|
@@ -214,12 +218,10 @@ module Discordrb
|
|
214
218
|
# List of users currently in a channel
|
215
219
|
def users
|
216
220
|
if @type == 'text'
|
217
|
-
@server.members.select {|u| u.status != :offline }
|
221
|
+
@server.members.select { |u| u.status != :offline }
|
218
222
|
else
|
219
223
|
@server.members.select do |user|
|
220
|
-
if user.voice_channel
|
221
|
-
user.voice_channel.id == @id
|
222
|
-
end
|
224
|
+
user.voice_channel.id == @id if user.voice_channel
|
223
225
|
end
|
224
226
|
end
|
225
227
|
end
|
@@ -238,6 +240,7 @@ module Discordrb
|
|
238
240
|
end
|
239
241
|
end
|
240
242
|
|
243
|
+
# A message on Discord that was sent to a text channel
|
241
244
|
class Message
|
242
245
|
attr_reader :content, :author, :channel, :timestamp, :id, :mentions
|
243
246
|
alias_method :user, :author
|
@@ -271,6 +274,7 @@ module Discordrb
|
|
271
274
|
end
|
272
275
|
end
|
273
276
|
|
277
|
+
# A server on Discord
|
274
278
|
class Server
|
275
279
|
attr_reader :region, :name, :owner_id, :id, :members
|
276
280
|
|
@@ -304,8 +308,8 @@ module Discordrb
|
|
304
308
|
@members << user
|
305
309
|
members_by_id[user.id] = user
|
306
310
|
user_roles = []
|
307
|
-
element['roles'].each do |
|
308
|
-
role_id =
|
311
|
+
element['roles'].each do |e|
|
312
|
+
role_id = e.to_i
|
309
313
|
user_roles << roles_by_id[role_id]
|
310
314
|
end
|
311
315
|
user.update_roles(self, user_roles)
|
@@ -314,13 +318,12 @@ module Discordrb
|
|
314
318
|
# Update user statuses with presence info
|
315
319
|
if data['presences']
|
316
320
|
data['presences'].each do |element|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
end
|
321
|
+
next unless element['user']
|
322
|
+
user_id = element['user']['id'].to_i
|
323
|
+
user = members_by_id[user_id]
|
324
|
+
if user
|
325
|
+
user.status = element['status'].to_sym
|
326
|
+
user.game = Discordrb::Games.find_game(element['game_id'])
|
324
327
|
end
|
325
328
|
end
|
326
329
|
end
|
@@ -336,23 +339,18 @@ module Discordrb
|
|
336
339
|
end
|
337
340
|
end
|
338
341
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
channel = channels_by_id[channel_id]
|
352
|
-
end
|
353
|
-
user.move(channel)
|
354
|
-
end
|
355
|
-
end
|
342
|
+
return unless data['voice_states']
|
343
|
+
data['voice_states'].each do |element|
|
344
|
+
user_id = element['user_id'].to_i
|
345
|
+
user = members_by_id[user_id]
|
346
|
+
next unless user
|
347
|
+
user.server_mute = element['mute']
|
348
|
+
user.server_deaf = element['deaf']
|
349
|
+
user.self_mute = element['self_mute']
|
350
|
+
user.self_mute = element['self_mute']
|
351
|
+
channel_id = element['channel_id']
|
352
|
+
channel = channel_id ? channels_by_id[channel_id] : nil
|
353
|
+
user.move(channel)
|
356
354
|
end
|
357
355
|
end
|
358
356
|
|
@@ -361,13 +359,13 @@ module Discordrb
|
|
361
359
|
end
|
362
360
|
|
363
361
|
def delete_role(role_id)
|
364
|
-
@roles.reject! {|r| r.id == role_id}
|
362
|
+
@roles.reject! { |r| r.id == role_id }
|
365
363
|
@members.each do |user|
|
366
|
-
new_roles = user.roles.reject {|r| r.id == role_id}
|
364
|
+
new_roles = user.roles.reject { |r| r.id == role_id }
|
367
365
|
user.update_roles(self, new_roles)
|
368
366
|
end
|
369
367
|
@channels.each do |channel|
|
370
|
-
overwrites = channel.permission_overwrites.reject {|id,
|
368
|
+
overwrites = channel.permission_overwrites.reject { |id, _| id == role_id }
|
371
369
|
channel.update_overwrites(overwrites)
|
372
370
|
end
|
373
371
|
end
|
@@ -378,6 +376,7 @@ module Discordrb
|
|
378
376
|
end
|
379
377
|
end
|
380
378
|
|
379
|
+
# A colour (red, green and blue values). Used for role colours
|
381
380
|
class ColorRGB
|
382
381
|
attr_reader :red, :green, :blue
|
383
382
|
|
@@ -1,48 +1,50 @@
|
|
1
|
-
require 'discordrb/events/generic'
|
2
|
-
require 'discordrb/data'
|
3
|
-
|
4
|
-
module Discordrb::Events
|
5
|
-
|
6
|
-
|
7
|
-
attr_reader :
|
8
|
-
attr_reader :
|
9
|
-
attr_reader :
|
10
|
-
attr_reader :
|
11
|
-
attr_reader :
|
12
|
-
attr_reader :
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@
|
19
|
-
@
|
20
|
-
@
|
21
|
-
@
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
return
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
a == e
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
a == e
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
1
|
+
require 'discordrb/events/generic'
|
2
|
+
require 'discordrb/data'
|
3
|
+
|
4
|
+
module Discordrb::Events
|
5
|
+
# Raised when a channel is created
|
6
|
+
class ChannelCreateEvent
|
7
|
+
attr_reader :type
|
8
|
+
attr_reader :topic
|
9
|
+
attr_reader :position
|
10
|
+
attr_reader :name
|
11
|
+
attr_reader :is_private
|
12
|
+
attr_reader :id
|
13
|
+
attr_reader :server
|
14
|
+
|
15
|
+
def initialize(data, bot)
|
16
|
+
@type = data['type']
|
17
|
+
@topic = data['topic']
|
18
|
+
@position = data['position']
|
19
|
+
@name = data['name']
|
20
|
+
@is_private = data['is_private']
|
21
|
+
@id = data['id']
|
22
|
+
@server = bot.server(data['guild_id'].to_i)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Event handler for ChannelCreateEvent
|
27
|
+
class ChannelCreateEventHandler < EventHandler
|
28
|
+
def matches?(event)
|
29
|
+
# Check for the proper event type
|
30
|
+
return false unless event.is_a? ChannelCreateEvent
|
31
|
+
|
32
|
+
[
|
33
|
+
matches_all(@attributes[:type], event.type) do |a, e|
|
34
|
+
if a.is_a? String
|
35
|
+
a == e.name
|
36
|
+
else
|
37
|
+
a == e
|
38
|
+
end
|
39
|
+
end,
|
40
|
+
matches_all(@attributes[:name], event.name) do |a, e|
|
41
|
+
if a.is_a? String
|
42
|
+
a == e.to_s
|
43
|
+
else
|
44
|
+
a == e
|
45
|
+
end
|
46
|
+
end
|
47
|
+
].reduce(true, &:&)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|