discordrb 1.1.3 → 1.2.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.

@@ -1,140 +1,391 @@
1
- # These classes hold relevant Discord data, such as messages or channels.
2
-
3
- module Discordrb
4
- class User
5
- attr_reader :username, :id, :discriminator, :avatar
6
-
7
- # Is the user online, offline, or away?
8
- attr_reader :status
9
-
10
- alias_method :name, :username
11
-
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 = data['avatar']
19
-
20
- @status = :offline
21
- end
22
-
23
- # Utility function to mention users in messages
24
- def mention
25
- "<@#{@id}>"
26
- end
27
-
28
- # Utility function to send a PM
29
- def pm(content = nil)
30
- if content
31
- # Recursively call pm to get the channel, then send a message to it
32
- channel = pm
33
- channel.send_message(content)
34
- else
35
- # If no message was specified, return the PM channel
36
- @bot.private_channel(@id)
37
- end
38
- end
39
- end
40
-
41
- class Channel
42
- attr_reader :name, :server, :type, :id, :is_private, :recipient, :topic
43
-
44
- def initialize(data, bot)
45
- @bot = bot
46
-
47
- #data is a sometimes a Hash and othertimes an array of Hashes, you only want the last one if it's an array
48
- data = data[-1] if data.is_a?(Array)
49
-
50
- @id = data['id'].to_i
51
- @type = data['type'] || 'text'
52
- @topic = data['topic']
53
-
54
- @is_private = data['is_private']
55
- if @is_private
56
- @recipient = User.new(data['recipient'], bot)
57
- @name = @recipient.username
58
- else
59
- @name = data['name']
60
- @server = bot.server(data['guild_id'].to_i)
61
- end
62
- end
63
-
64
- def send_message(content)
65
- @bot.send_message(@id, content)
66
- end
67
-
68
- alias_method :send, :send_message
69
- alias_method :message, :send_message
70
- end
71
-
72
- class Message
73
- attr_reader :content, :author, :channel, :timestamp, :id, :mentions
74
- alias_method :user, :author
75
- alias_method :text, :content
76
-
77
- def initialize(data, bot)
78
- @bot = bot
79
- @content = data['content']
80
- @author = User.new(data['author'], bot)
81
- @channel = bot.channel(data['channel_id'].to_i)
82
- @timestamp = Time.at(data['timestamp'].to_i)
83
- @id = data['id'].to_i
84
-
85
- @mentions = []
86
-
87
- data['mentions'].each do |element|
88
- @mentions << User.new(element, bot)
89
- end
90
- end
91
- end
92
-
93
- class Server
94
- attr_reader :region, :name, :owner_id, :id, :members
95
-
96
- # Array of channels on the server
97
- attr_reader :channels
98
-
99
- def initialize(data, bot)
100
- @bot = bot
101
- @region = data['region']
102
- @name = data['name']
103
- @owner_id = data['owner_id'].to_i
104
- @id = data['id'].to_i
105
-
106
- @members = []
107
- members_by_id = {}
108
-
109
- data['members'].each do |element|
110
- user = User.new(element['user'], bot)
111
- @members << user
112
- members_by_id[user.id] = user
113
- end
114
-
115
- # Update user statuses with presence info
116
- if data['presences']
117
- data['presences'].each do |element|
118
- if element['user']
119
- user = members_by_id[element['user']['id'].to_i]
120
- if user && element['status']
121
- # I don't want to make User#status writable, so we'll use
122
- # instance_exec to open the object and set the status
123
- user.instance_exec(element['status']) do |status|
124
- @status = status.to_sym
125
- end
126
- end
127
- end
128
- end
129
- end
130
-
131
- @channels = []
132
-
133
- if data['channels']
134
- data['channels'].each do |element|
135
- @channels << Channel.new(element, bot)
136
- end
137
- end
138
- end
139
- end
140
- end
1
+ # These classes hold relevant Discord data, such as messages or channels.
2
+
3
+ require 'ostruct'
4
+
5
+ module Discordrb
6
+ class User
7
+ attr_reader :username, :id, :discriminator, :avatar
8
+
9
+ attr_accessor :status
10
+ attr_accessor :game_id
11
+ attr_accessor :server_mute
12
+ attr_accessor :server_deaf
13
+ attr_accessor :self_mute
14
+ attr_accessor :self_deaf
15
+ attr_reader :voice_channel
16
+ attr_reader :roles
17
+
18
+ alias_method :name, :username
19
+
20
+ def initialize(data, bot, server = nil)
21
+ @bot = bot
22
+
23
+ @username = data['username']
24
+ @id = data['id'].to_i
25
+ @discriminator = data['discriminator']
26
+ @avatar = data['avatar']
27
+ @server = server
28
+ @roles = []
29
+
30
+ @status = :offline
31
+ end
32
+
33
+ # Utility function to mention users in messages
34
+ def mention
35
+ "<@#{@id}>"
36
+ end
37
+
38
+ # Utility function to send a PM
39
+ def pm(content = nil)
40
+ if content
41
+ # Recursively call pm to get the channel, then send a message to it
42
+ channel = pm
43
+ channel.send_message(content)
44
+ else
45
+ # If no message was specified, return the PM channel
46
+ @bot.private_channel(@id)
47
+ end
48
+ end
49
+
50
+ # Move a user into a voice channel
51
+ def move(to_channel)
52
+ return if to_channel && to_channel.type != 'voice'
53
+ @voice_channel = to_channel
54
+ end
55
+
56
+ # Set this user's roles
57
+ def update_roles(roles)
58
+ @roles = roles
59
+ end
60
+
61
+ # Determine if the user has permission to do an action
62
+ # action is a permission from Permissions::Flags.
63
+ # channel is the channel in which the action takes place (not applicable for server-wide actions).
64
+ def has_permission?(action, channel = nil)
65
+ # For each role, check if
66
+ # (1) the channel explicitly allows or permits an action for the role and
67
+ # (2) if the user is allowed to do the action if the channel doesn't specify
68
+ @roles.reduce(false) do |can_act, role|
69
+ channel_allow = nil
70
+ if channel && channel.permission_overwrites[role.id]
71
+ allow = channel.permission_overwrites[role.id].allow
72
+ deny = channel.permission_overwrites[role.id].deny
73
+ if allow.instance_variable_get("@#{action}")
74
+ channel_allow = true
75
+ elsif deny.instance_variable_get("@#{action}")
76
+ channel_allow = false
77
+ # else
78
+ # If the channel has nothing to say on the matter, we can defer to the role itself
79
+ end
80
+ end
81
+ if channel_allow == false
82
+ can_act = can_act || false
83
+ elsif channel_allow == true
84
+ can_act = true
85
+ else # channel_allow == nil
86
+ can_act = role.permissions.instance_variable_get("@#{action}") || can_act
87
+ end
88
+ end
89
+ end
90
+
91
+ def method_missing(method_name, *args, &block)
92
+ if /\Acan_(?<action>\w+)\?\Z/ =~ method_name
93
+ action = action.to_sym
94
+ if Permissions::Flags.has_value? action
95
+ has_permission? action, args.first
96
+ else
97
+ super
98
+ end
99
+ else
100
+ super
101
+ end
102
+ end
103
+
104
+ # Respond to can_*? methods
105
+ def respond_to?(method_name, include_private = false)
106
+ if /\Acan_(?<action>\w+)\?\Z/ =~ method_name
107
+ action = action.to_sym
108
+ if Permissions::Flags.has_value? action
109
+ true
110
+ else
111
+ super
112
+ end
113
+ else
114
+ super
115
+ end
116
+ end
117
+ end
118
+
119
+ class Role
120
+ attr_reader :permissions
121
+ attr_reader :name
122
+ attr_reader :id
123
+ attr_reader :hoist
124
+ attr_reader :color
125
+
126
+ def initialize(data, bot, server = nil)
127
+ @permissions = Permissions.new(data['permissions'])
128
+ @name = data['name']
129
+ @id = data['id'].to_i
130
+ @hoist = data['hoist']
131
+ @color = ColorRGB.new(data['color'])
132
+ end
133
+
134
+ def update_from(other)
135
+ @permissions = other.permissions
136
+ @name = other.name
137
+ @hoist = other.hoist
138
+ @color = other.color
139
+ end
140
+ end
141
+
142
+ class Permissions
143
+ # This hash maps bit positions to logical permissions.
144
+ # I'm not sure what the unlabeled bits are reserved for.
145
+ Flags = {
146
+ # Bit => Permission # Value
147
+ 0 => :create_instant_invite, # 1
148
+ 1 => :kick_members, # 2
149
+ 2 => :ban_members, # 4
150
+ 3 => :manage_roles , # 8
151
+ 4 => :manage_channels, # 16
152
+ 5 => :manage_server, # 32
153
+ #6 # 64
154
+ #7 # 128
155
+ #8 # 256
156
+ #9 # 512
157
+ 10 => :read_messages, # 1024
158
+ 11 => :send_messages, # 2048
159
+ 12 => :send_tts_messages, # 4096
160
+ 13 => :manage_messages, # 8192
161
+ 14 => :embed_links, # 16384
162
+ 15 => :attach_files, # 32768
163
+ 16 => :read_message_history, # 65536
164
+ 17 => :mention_everyone, # 131072
165
+ #18 # 262144
166
+ #19 # 524288
167
+ 20 => :connect, # 1048576
168
+ 21 => :speak, # 2097152
169
+ 22 => :mute_members, # 4194304
170
+ 23 => :deafen_members, # 8388608
171
+ 24 => :move_members, # 16777216
172
+ 25 => :use_voice_activity # 33554432
173
+ }
174
+
175
+ Flags.each_value do |flag|
176
+ attr_reader flag
177
+ end
178
+
179
+ def initialize(bits)
180
+ Flags.each do |position, flag|
181
+ flag_set = ((bits >> position) & 0x1) == 1
182
+ instance_variable_set "@#{flag}", flag_set
183
+ end
184
+ end
185
+ end
186
+
187
+ class Channel
188
+ attr_reader :name, :server, :type, :id, :is_private, :recipient, :topic
189
+
190
+ attr_reader :permission_overwrites
191
+
192
+ def initialize(data, bot, server = nil)
193
+ @bot = bot
194
+
195
+ #data is a sometimes a Hash and othertimes an array of Hashes, you only want the last one if it's an array
196
+ data = data[-1] if data.is_a?(Array)
197
+
198
+ @id = data['id'].to_i
199
+ @type = data['type'] || 'text'
200
+ @topic = data['topic']
201
+
202
+ @is_private = data['is_private']
203
+ if @is_private
204
+ @recipient = User.new(data['recipient'], bot)
205
+ @name = @recipient.username
206
+ else
207
+ @name = data['name']
208
+ @server = bot.server(data['guild_id'].to_i)
209
+ @server = server if !@server
210
+ end
211
+
212
+ # Populate permission overwrites
213
+ @permission_overwrites = {}
214
+ if data['permission_overwrites']
215
+ data['permission_overwrites'].each do |element|
216
+ role_id = element['id'].to_i
217
+ deny = Permissions.new(element['deny'])
218
+ allow = Permissions.new(element['allow'])
219
+ @permission_overwrites[role_id] = OpenStruct.new
220
+ @permission_overwrites[role_id].deny = deny
221
+ @permission_overwrites[role_id].allow = allow
222
+ end
223
+ end
224
+ end
225
+
226
+ def send_message(content)
227
+ @bot.send_message(@id, content)
228
+ end
229
+
230
+ def update_from(other)
231
+ @topic = other.topic
232
+ @name = other.name
233
+ @is_private = other.is_private
234
+ @recipient = other.recipient
235
+ @permission_overwrites = other.permission_overwrites
236
+ end
237
+
238
+ # List of users currently in a channel
239
+ def users
240
+ if @type == 'text'
241
+ @server.members.select {|u| u.status != :offline }
242
+ else
243
+ @server.members.select do |user|
244
+ if user.voice_channel
245
+ user.voice_channel.id == @id
246
+ end
247
+ end
248
+ end
249
+ end
250
+
251
+ def update_overwrites(overwrites)
252
+ @permission_overwrites = overwrites
253
+ end
254
+
255
+ alias_method :send, :send_message
256
+ alias_method :message, :send_message
257
+ end
258
+
259
+ class Message
260
+ attr_reader :content, :author, :channel, :timestamp, :id, :mentions
261
+ alias_method :user, :author
262
+ alias_method :text, :content
263
+
264
+ def initialize(data, bot)
265
+ @bot = bot
266
+ @content = data['content']
267
+ @author = User.new(data['author'], bot)
268
+ @channel = bot.channel(data['channel_id'].to_i)
269
+ @timestamp = Time.at(data['timestamp'].to_i)
270
+ @id = data['id'].to_i
271
+
272
+ @mentions = []
273
+
274
+ data['mentions'].each do |element|
275
+ @mentions << User.new(element, bot)
276
+ end
277
+ end
278
+ end
279
+
280
+ class Server
281
+ attr_reader :region, :name, :owner_id, :id, :members
282
+
283
+ # Array of channels on the server
284
+ attr_reader :channels
285
+
286
+ # Array of roles on the server
287
+ attr_reader :roles
288
+
289
+ def initialize(data, bot)
290
+ @bot = bot
291
+ @region = data['region']
292
+ @name = data['name']
293
+ @owner_id = data['owner_id'].to_i
294
+ @id = data['id'].to_i
295
+
296
+ # Create roles
297
+ @roles = []
298
+ roles_by_id = {}
299
+ data['roles'].each do |element|
300
+ role = Role.new(element, bot)
301
+ @roles << role
302
+ roles_by_id[role.id] = role
303
+ end
304
+
305
+ @members = []
306
+ members_by_id = {}
307
+
308
+ data['members'].each do |element|
309
+ user = User.new(element['user'], bot, self)
310
+ @members << user
311
+ members_by_id[user.id] = user
312
+ user_roles = []
313
+ element['roles'].each do |element|
314
+ role_id = element.to_i
315
+ user_roles << roles_by_id[role_id]
316
+ end
317
+ user.update_roles(user_roles)
318
+ end
319
+
320
+ # Update user statuses with presence info
321
+ if data['presences']
322
+ data['presences'].each do |element|
323
+ if element['user']
324
+ user_id = element['user']['id'].to_i
325
+ user = members_by_id[user_id]
326
+ if user
327
+ user.status = element['status'].to_sym
328
+ user.game_id = element['game_id']
329
+ end
330
+ end
331
+ end
332
+ end
333
+
334
+ @channels = []
335
+ channels_by_id = {}
336
+
337
+ if data['channels']
338
+ data['channels'].each do |element|
339
+ channel = Channel.new(element, bot, self)
340
+ @channels << channel
341
+ channels_by_id[channel.id] = channel
342
+ end
343
+ end
344
+
345
+ if data['voice_states']
346
+ data['voice_states'].each do |element|
347
+ user_id = element['user_id'].to_i
348
+ user = members_by_id[user_id]
349
+ if user
350
+ user.server_mute = element['mute']
351
+ user.server_deaf = element['deaf']
352
+ user.self_mute = element['self_mute']
353
+ user.self_mute = element['self_mute']
354
+ channel_id = element['channel_id']
355
+ channel = nil
356
+ if channel_id
357
+ channel = channels_by_id[channel_id]
358
+ end
359
+ user.move(channel)
360
+ end
361
+ end
362
+ end
363
+ end
364
+
365
+ def add_role(role)
366
+ @roles << role
367
+ end
368
+
369
+ def delete_role(role_id)
370
+ @roles.reject! {|r| r.id == role_id}
371
+ @members.each do |user|
372
+ new_roles = user.roles.reject {|r| r.id == role_id}
373
+ user.update_roles(new_roles)
374
+ end
375
+ @channels.each do |channel|
376
+ overwrites = channel.permission_overwrites.reject {|id, perm| id == role_id}
377
+ channel.update_overwrites(overwrites)
378
+ end
379
+ end
380
+ end
381
+
382
+ class ColorRGB
383
+ attr_reader :red, :green, :blue
384
+
385
+ def initialize(combined)
386
+ @red = (combined >> 16) & 0xFF
387
+ @green = (combined >> 8) & 0xFF
388
+ @blue = combined & 0xFF
389
+ end
390
+ end
391
+ end