discordrb 1.5.4 → 1.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0e493737a94960682725acf9687de8219edac74a
4
- data.tar.gz: ea16d4b131834bbc0a6c040e6632a0296d5c1b26
3
+ metadata.gz: 8f012d4df7d5f6beac41402044ddc9f597aef8b7
4
+ data.tar.gz: 1b75879a95b4be99a0faef8617e1b019c3b7bb81
5
5
  SHA512:
6
- metadata.gz: 369259ee5a1536ab978068f4d726d1c2257ab16f1174ec4939e9b67e3268263e8bf20e1ed804745851d0e52cb5bed69e40c42d9e13282334b8006b19d2388b74
7
- data.tar.gz: 8e8c09b5f222675d62f8b2126cf1e97168d0c1b0dc0bf384f4162ddd1f90d79c33d54ac13c26498436488b831872def2f2b3aa554bc9aa950a3c1ea450da1d20
6
+ metadata.gz: eb0e782b340e2fa53f2b93618594332f2b8c0025cad5756072f9792786f0f79c9768e0938cd934f2c289ec36edd8025c6c6e90e15dbe98a36e20eb82d09a5bf3
7
+ data.tar.gz: 4d75ececf380101a0f7dd9b3e8f748c658446f0b54d5a5f19fbadc4aa6edc7c9671174723ea792d56527b60c21c4543c0ef69fc822630a930331d4fb63c0e5b4
@@ -37,3 +37,14 @@ Style/ConstantName:
37
37
  # Allow 'Pokémon-style' exception handling
38
38
  Lint/RescueException:
39
39
  Enabled: false
40
+
41
+ AllCops:
42
+ TargetRubyVersion: 2.1
43
+
44
+ # Apparently setting the target version to 2.1 is not enough to prevent this cop...
45
+ Style/FrozenStringLiteralComment:
46
+ Enabled: false
47
+
48
+ # http://stackoverflow.com/questions/4763121/should-i-use-alias-or-alias-method
49
+ Style/Alias:
50
+ Enabled: false
data/.yardopts CHANGED
@@ -1 +1 @@
1
- --markup markdown
1
+ --markup markdown --hide-tag todo
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.6.0
4
+
5
+ * The inline documentation using YARD was greatly improved and is now mostly usable, at least for the data classes and voice classes. It's still not complete enough to be released on GitHub, but you can build it yourself using [YARD](http://yardoc.org/).
6
+ * It's now possible to encrypt sent voice data using an optional parameter in `voice_connect`. The encryption uses RbNaCl's [SecretBox](https://github.com/cryptosphere/rbnacl/wiki/Secret-Key-Encryption#algorithm-details) and is enabled by default.
7
+ * The [new library comparison](https://discordapi.com/unofficial/comparison.html) is now fully supported, barring voice receive and multi-send: (#39)
8
+ * `bot.invite` will create an `Invite` object from a code containing information about it.
9
+ * `server.move(user, channel)` will move a user to a different voice channel.
10
+ * The events `bot.message_edit` and `bot.message_delete` are now available for message editing and deletion. Note that Discord doesn't provide the content of edited/deleted messages, so you'll have to implement message caching yourself if you really need it.
11
+ * The events `bot.user_ban` and `bot.user_unban` are now available for users getting banned/unbanned from servers.
12
+ * A bot's name can now be sent using `bot.name=`. This data will be sent to Discord with the user-agent and it might be used for cool statistics in the future.
13
+ * Discord server ownership transfer is now implemented using the writer `server.owner=`.
14
+ * `CommandBot`s can now have command aliases by simply using an array of symbols as the command name.
15
+ * A utility method `server.default_channel` was implemented that returns the default text channel of a server, usually called #general. (An alias `general_channel` is available too.)
16
+ * Tokens will no longer appear in debug output, so you're safe sending output logs to other people.
17
+ * A reader `server.owner` that returns the server's owner as a `User` was added. Previously, users had to manually get the `User` object using `bot.user`.
18
+ * Most methods that accept IDs or data objects now also accept `Integer`s or `String`s containing the IDs now. This is implemented by adding a method `resolve_id` to all objects that could potentially contain an ID. (Note that this change is not complete yet and I might have missed some methods.)
19
+ * The writer `server.afk_channel_id=` is now deprecated as its functionality is now covered by `server.afk_channel=`.
20
+ * A new reader `user.avatar_url` was added that returns the full image URL to a user's avatar.
21
+ * To avoid confusion with `avatar_url`, the reader `user.avatar` was renamed to `avatar_id`. (`user.avatar` still exists but is now deprecated.)
22
+ * Symbols are now used instead of strings as hash keys in all methods that send JSON data to somewhere. This might improve performance slightly.
23
+
24
+ ### Bugfixes
25
+ * Fixed the reader `server.afk_channel_id` not containing a value sometimes.
26
+ * An issue was fixed where attempting to create a `Server` object from a stub server that didn't contain any role data would cause an exception.
27
+ * The `Invite` `server` property will now be initialized directly from the invite data instead of the channel the invite is to, to prevent it being `nil` when the invite channel was stubbed.
28
+ * The `inviter` of an `Invite` will now be `nil` instead of causing an exception when it doesn't exist in the invite data.
29
+
3
30
  ## 1.5.4
4
31
  * The `opus-ruby` and `levenshtein` dependencies are now optional - if you don't need them, it won't crash immediately (only when you try to use voice / `find` with a threshold > 0, respectively)
5
32
 
data/README.md CHANGED
@@ -49,6 +49,10 @@ You're missing the OpenSSL libraries that EventMachine, a dependency of discordr
49
49
  gem uninstall eventmachine
50
50
  gem install eventmachine -- --with-ssl-dir=C:/OpenSSL-Win32
51
51
 
52
+ **If you're having trouble getting voice playback to work**:
53
+
54
+ Look here: https://github.com/meew0/discordrb/wiki/Voice-sending#troubleshooting
55
+
52
56
  ## Usage
53
57
 
54
58
  You can make a simple bot like this:
@@ -24,9 +24,11 @@ Gem::Specification.new do |spec|
24
24
  spec.add_dependency 'activesupport'
25
25
  spec.add_dependency 'opus-ruby'
26
26
  spec.add_dependency 'websocket-client-simple'
27
+ spec.add_dependency 'rbnacl-libsodium'
27
28
 
28
29
  spec.required_ruby_version = '>= 2.1.0'
29
30
 
30
31
  spec.add_development_dependency 'bundler', '~> 1.10'
31
32
  spec.add_development_dependency 'rake', '~> 10.0'
33
+ spec.add_development_dependency 'yard', '~> 0.8.7.6'
32
34
  end
@@ -7,5 +7,22 @@ require 'discordrb/logger'
7
7
  module Discordrb
8
8
  Thread.current[:discordrb_name] = 'main'
9
9
 
10
+ # The default debug logger used by discordrb.
10
11
  LOGGER = Logger.new
11
12
  end
13
+
14
+ # In discordrb, Integer and {String} are monkey-patched to allow for easy resolution of IDs
15
+ class Integer
16
+ # @return [Integer] The Discord ID represented by this integer, i. e. the integer itself
17
+ def resolve_id
18
+ self
19
+ end
20
+ end
21
+
22
+ # In discordrb, {Integer} and String are monkey-patched to allow for easy resolution of IDs
23
+ class String
24
+ # @return [Integer] The Discord ID represented by this string, i. e. the string converted to an integer
25
+ def resolve_id
26
+ to_i
27
+ end
28
+ end
@@ -4,26 +4,25 @@ require 'json'
4
4
  # List of methods representing endpoints in Discord's API
5
5
  module Discordrb::API
6
6
  # The base URL of the Discord REST API.
7
- APIBASE = 'https://discordapp.com/api'
7
+ APIBASE = 'https://discordapp.com/api'.freeze
8
8
 
9
9
  module_function
10
10
 
11
- # Generate a user agent identifying this requester as discordrb.
12
- def user_agent
13
- libraries = [
14
- # rest-client
15
- "rest-client/#{RestClient::VERSION}",
16
-
17
- # ruby
18
- "#{RUBY_ENGINE}/#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}",
11
+ def bot_name
12
+ @bot_name
13
+ end
19
14
 
20
- # discordrb
21
- "discordrb/#{Discordrb::VERSION}"
22
- ]
15
+ def bot_name=(value)
16
+ @bot_name = value
17
+ end
23
18
 
24
- # Required by Discord devs
19
+ # Generate a user agent identifying this requester as discordrb.
20
+ def user_agent
21
+ # This particular string is required by the Discord devs.
25
22
  required = "DiscordBot (https://github.com/meew0/discordrb, v#{Discordrb::VERSION})"
26
- "#{libraries.join(' ')} #{required}"
23
+ @bot_name ||= ''
24
+
25
+ "rest-client/#{RestClient::VERSION} #{RUBY_ENGINE}/#{RUBY_VERSION}p#{RUBY_PATCHLEVEL} discordrb/#{Discordrb::VERSION} #{required} #{@bot_name}"
27
26
  end
28
27
 
29
28
  def raw_request(type, attributes)
@@ -47,6 +46,11 @@ module Discordrb::API
47
46
  response
48
47
  end
49
48
 
49
+ # Make an avatar URL from the user and avatar IDs
50
+ def avatar_url(user_id, avatar_id)
51
+ "#{APIBASE}/users/#{user_id}/avatars/#{avatar_id}.jpg"
52
+ end
53
+
50
54
  # Ban a user from a server and delete their messages from the last message_days days
51
55
  def ban_user(token, server_id, user_id, message_days)
52
56
  request(
@@ -74,6 +78,17 @@ module Discordrb::API
74
78
  )
75
79
  end
76
80
 
81
+ # Move a user to a different voice channel
82
+ def move_user(token, server_id, user_id, channel_id)
83
+ request(
84
+ :patch,
85
+ "#{APIBASE}/guilds/#{server_id}/members/#{user_id}",
86
+ { channel_id: channel_id }.to_json,
87
+ Authorization: token,
88
+ content_type: :json
89
+ )
90
+ end
91
+
77
92
  # Get a server's banned users
78
93
  def bans(token, server_id)
79
94
  request(
@@ -96,10 +111,10 @@ module Discordrb::API
96
111
  # Logout from the server
97
112
  def logout(token)
98
113
  request(
99
- :post,
100
- "#{APIBASE}/auth/logout",
101
- nil,
102
- Authorization: token
114
+ :post,
115
+ "#{APIBASE}/auth/logout",
116
+ nil,
117
+ Authorization: token
103
118
  )
104
119
  end
105
120
 
@@ -108,7 +123,7 @@ module Discordrb::API
108
123
  request(
109
124
  :post,
110
125
  "#{APIBASE}/guilds",
111
- { 'name' => name, 'region' => region.to_s }.to_json,
126
+ { name: name, region: region.to_s }.to_json,
112
127
  Authorization: token,
113
128
  content_type: :json
114
129
  )
@@ -119,7 +134,18 @@ module Discordrb::API
119
134
  request(
120
135
  :patch,
121
136
  "#{APIBASE}/guilds/#{server_id}",
122
- { 'name' => name, 'region' => region, 'icon' => icon, 'afk_channel_id' => afk_channel_id, 'afk_timeout' => afk_timeout }.to_json,
137
+ { name: name, region: region, icon: icon, afk_channel_id: afk_channel_id, afk_timeout: afk_timeout }.to_json,
138
+ Authorization: token,
139
+ content_type: :json
140
+ )
141
+ end
142
+
143
+ # Transfer server ownership
144
+ def transfer_ownership(token, server_id, user_id)
145
+ request(
146
+ :patch,
147
+ "#{APIBASE}/guilds/#{server_id}",
148
+ { owner_id: user_id }.to_json,
123
149
  Authorization: token,
124
150
  content_type: :json
125
151
  )
@@ -150,7 +176,7 @@ module Discordrb::API
150
176
  request(
151
177
  :post,
152
178
  "#{APIBASE}/guilds/#{server_id}/channels",
153
- { 'name' => name, 'type' => type }.to_json,
179
+ { name: name, type: type }.to_json,
154
180
  Authorization: token,
155
181
  content_type: :json
156
182
  )
@@ -161,7 +187,7 @@ module Discordrb::API
161
187
  request(
162
188
  :patch,
163
189
  "#{APIBASE}/channels/#{channel_id}",
164
- { 'name' => name, 'position' => position, 'topic' => topic }.to_json,
190
+ { name: name, position: position, topic: topic }.to_json,
165
191
  Authorization: token,
166
192
  content_type: :json
167
193
  )
@@ -200,7 +226,7 @@ module Discordrb::API
200
226
  request(
201
227
  :post,
202
228
  "#{APIBASE}/users/#{bot_user_id}/channels",
203
- { 'recipient_id' => user_id }.to_json,
229
+ { recipient_id: user_id }.to_json,
204
230
  Authorization: token,
205
231
  content_type: :json
206
232
  )
@@ -211,7 +237,7 @@ module Discordrb::API
211
237
  request(
212
238
  :post,
213
239
  "#{APIBASE}/channels/#{channel_id}/invites",
214
- { 'max_age' => max_age, 'max_uses' => max_uses, 'temporary' => temporary, 'xkcdpass' => xkcd }.to_json,
240
+ { max_age: max_age, max_uses: max_uses, temporary: temporary, xkcdpass: xkcd }.to_json,
215
241
  Authorization: token,
216
242
  content_type: :json
217
243
  )
@@ -231,7 +257,7 @@ module Discordrb::API
231
257
  request(
232
258
  :post,
233
259
  "#{APIBASE}/channels/#{channel_id}/messages",
234
- { 'content' => message, 'mentions' => mentions, tts => tts }.to_json,
260
+ { content: message, mentions: mentions, tts => tts }.to_json,
235
261
  Authorization: token,
236
262
  content_type: :json
237
263
  )
@@ -251,7 +277,7 @@ module Discordrb::API
251
277
  request(
252
278
  :patch,
253
279
  "#{APIBASE}/channels/#{channel_id}/messages/#{message_id}",
254
- { 'content' => message, 'mentions' => mentions }.to_json,
280
+ { content: message, mentions: mentions }.to_json,
255
281
  Authorization: token,
256
282
  content_type: :json
257
283
  )
@@ -297,7 +323,7 @@ module Discordrb::API
297
323
  request(
298
324
  :patch,
299
325
  "#{APIBASE}/guilds/#{server_id}/roles/#{role_id}",
300
- { 'color' => colour, 'name' => name, 'hoist' => hoist, 'permissions' => packed_permissions }.to_json,
326
+ { color: colour, name: name, hoist: hoist, permissions: packed_permissions }.to_json,
301
327
  Authorization: token,
302
328
  content_type: :json
303
329
  )
@@ -317,7 +343,7 @@ module Discordrb::API
317
343
  request(
318
344
  :patch,
319
345
  "#{APIBASE}/guilds/#{server_id}/members/#{user_id}",
320
- { 'roles' => roles }.to_json,
346
+ { roles: roles }.to_json,
321
347
  Authorization: token,
322
348
  content_type: :json
323
349
  )
@@ -328,7 +354,7 @@ module Discordrb::API
328
354
  request(
329
355
  :put,
330
356
  "#{APIBASE}/channels/#{channel_id}/permissions/#{user_id}",
331
- { 'type' => 'member', 'id' => user_id, 'allow' => allow, 'deny' => deny }.to_json,
357
+ { type: 'member', id: user_id, allow: allow, deny: deny }.to_json,
332
358
  Authorization: token,
333
359
  content_type: :json
334
360
  )
@@ -339,7 +365,7 @@ module Discordrb::API
339
365
  request(
340
366
  :put,
341
367
  "#{APIBASE}/channels/#{channel_id}/permissions/#{role_id}",
342
- { 'type' => 'role', 'id' => role_id, 'allow' => allow, 'deny' => deny }.to_json,
368
+ { type: 'role', id: role_id, allow: allow, deny: deny }.to_json,
343
369
  Authorization: token,
344
370
  content_type: :json
345
371
  )
@@ -378,7 +404,7 @@ module Discordrb::API
378
404
  request(
379
405
  :patch,
380
406
  "#{APIBASE}/users/@me",
381
- { 'avatar' => avatar, 'email' => email, 'new_password' => new_password, 'password' => password, 'username' => new_username }.to_json,
407
+ { avatar: avatar, email: email, new_password: new_password, password: password, username: new_username }.to_json,
382
408
  Authorization: token,
383
409
  content_type: :json
384
410
  )
@@ -7,7 +7,7 @@ module Discordrb
7
7
  # If no block is present, the await will also be deleted. This is an easy way to make
8
8
  # temporary events that are only temporary under certain conditions.
9
9
  #
10
- # Besides the given block, an {AwaitEvent} will also be executed with the key and
10
+ # Besides the given block, an {Discordrb::Events::AwaitEvent} will also be executed with the key and
11
11
  # the type of the await that was triggered. It's possible to register multiple events
12
12
  # that trigger on the same await.
13
13
  class Await
@@ -40,6 +40,7 @@ module Discordrb
40
40
  dummy_handler = @bot.handler_class(@type).new(@attributes, @bot)
41
41
  return [nil, nil] unless dummy_handler.matches?(event)
42
42
 
43
+ should_delete = nil
43
44
  should_delete = true if (@block && @block.call(event) != false) || !@block
44
45
 
45
46
  [@key, should_delete]
@@ -16,6 +16,7 @@ require 'discordrb/events/guild_role_delete'
16
16
  require 'discordrb/events/guild_role_update'
17
17
  require 'discordrb/events/guilds'
18
18
  require 'discordrb/events/await'
19
+ require 'discordrb/events/bans'
19
20
 
20
21
  require 'discordrb/api'
21
22
  require 'discordrb/exceptions'
@@ -35,11 +36,6 @@ module Discordrb
35
36
  # @return [User] The bot user.
36
37
  attr_reader :bot_user
37
38
 
38
- # The Discord API token received when logging in. Useful to explicitly call
39
- # {API} methods.
40
- # @return [String] The API token.
41
- attr_reader :token
42
-
43
39
  # The list of users the bot shares a server with.
44
40
  # @return [Array<User>] The users.
45
41
  attr_reader :users
@@ -63,6 +59,10 @@ module Discordrb
63
59
  # Whether or not the bot should parse its own messages. Off by default.
64
60
  attr_accessor :should_parse_self
65
61
 
62
+ # The bot's name which discordrb sends to Discord when making any request, so Discord can identify bots with the
63
+ # same codebase. Not required but I recommend setting it anyway.
64
+ attr_accessor :name
65
+
66
66
  # Makes a new bot with the given email and password. It will be ready to be added event handlers to and can eventually be run with {#run}.
67
67
  # @param email [String] The email for your (or the bot's) Discord account.
68
68
  # @param password [String] The valid password that should be used to log in to the account.
@@ -80,6 +80,8 @@ module Discordrb
80
80
  @email = email
81
81
  @password = password
82
82
 
83
+ @name = ''
84
+
83
85
  debug('Creating token cache')
84
86
  @token_cache = Discordrb::TokenCache.new
85
87
  debug('Token cache created successfully')
@@ -96,6 +98,14 @@ module Discordrb
96
98
  @current_thread = 0
97
99
  end
98
100
 
101
+ # The Discord API token received when logging in. Useful to explicitly call
102
+ # {API} methods.
103
+ # @return [String] The API token.
104
+ def token
105
+ API.bot_name = @name
106
+ @token
107
+ end
108
+
99
109
  # Runs the bot, which logs into Discord and connects the WebSocket. This prevents all further execution unless it is executed with `async` = `:async`.
100
110
  # @param async [Symbol] If it is `:async`, then the bot will allow further execution.
101
111
  # It doesn't necessarily have to be that, anything truthy will work,
@@ -153,10 +163,11 @@ module Discordrb
153
163
  # @param id [Integer] The channel ID for which to search for.
154
164
  # @return [Channel] The channel identified by the ID.
155
165
  def channel(id)
166
+ id = id.resolve_id
156
167
  debug("Obtaining data for channel with id #{id}")
157
168
  return @channels[id] if @channels[id]
158
169
 
159
- response = API.channel(@token, id)
170
+ response = API.channel(token, id)
160
171
  channel = Channel.new(JSON.parse(response), self)
161
172
  @channels[id] = channel
162
173
  end
@@ -167,10 +178,11 @@ module Discordrb
167
178
  # @param id [Integer] The user ID to generate a private channel for.
168
179
  # @return [Channel] A private channel for that user.
169
180
  def private_channel(id)
181
+ id = id.resolve_id
170
182
  debug("Creating private channel with user id #{id}")
171
183
  return @private_channels[id] if @private_channels[id]
172
184
 
173
- response = API.create_private(@token, @bot_user.id, id)
185
+ response = API.create_private(token, @bot_user.id, id)
174
186
  channel = Channel.new(JSON.parse(response), self)
175
187
  @private_channels[id] = channel
176
188
  end
@@ -186,29 +198,44 @@ module Discordrb
186
198
  # @return [String] Only the code for the invite.
187
199
  def resolve_invite_code(invite)
188
200
  invite = invite.code if invite.is_a? Discordrb::Invite
189
- invite = invite[invite.rindex('/') + 1..-1] if invite.start_with?('http') || invite.start_with?('discord.gg')
201
+ invite = invite[invite.rindex('/') + 1..-1] if invite.start_with?('http', 'discord.gg')
190
202
  invite
191
203
  end
192
204
 
205
+ # Gets information about an invite.
206
+ # @param invite [String, Invite] The invite to join. For possible formats see {#resolve_invite_code}.
207
+ # @return [Invite] The invite with information about the given invite URL.
208
+ def invite(invite)
209
+ code = resolve_invite_code(invite)
210
+ Invite.new(JSON.parse(API.resolve_invite(token, code)), self)
211
+ end
212
+
193
213
  # Makes the bot join an invite to a server.
194
214
  # @param invite [String, Invite] The invite to join. For possible formats see {#resolve_invite_code}.
195
215
  def join(invite)
196
- invite = resolve_invite_code(invite)
197
- resolved = JSON.parse(API.resolve_invite(@token, invite))['code']
198
- API.join_server(@token, resolved)
216
+ resolved = invite(invite)['code']
217
+ API.join_server(token, resolved)
199
218
  end
200
219
 
201
220
  attr_reader :voice
202
221
 
203
- def voice_connect(chan)
222
+ # Connects to a voice channel, initializes network connections and returns the {Voice::VoiceBot} over which audio
223
+ # data can then be sent. After connecting, the bot can also be accessed using {#voice}.
224
+ # @param chan [Channel] The voice channel to connect to.
225
+ # @param encrypted [true, false] Whether voice communication should be encrypted using RbNaCl's SecretBox
226
+ # (uses an XSalsa20 stream cipher for encryption and Poly1305 for authentication)
227
+ # @return [Voice::VoiceBot] the initialized bot over which audio data can then be sent.
228
+ def voice_connect(chan, encrypted = true)
204
229
  if @voice
205
230
  debug('Voice bot exists already! Destroying it')
206
231
  @voice.destroy
207
232
  @voice = nil
208
233
  end
209
234
 
210
- chan = channel(chan) if chan.is_a? Integer
235
+ chan = channel(chan.resolve_id)
211
236
  @voice_channel = chan
237
+ @should_encrypt_voice = encrypted
238
+
212
239
  debug("Got voice channel: #{@voice_channel}")
213
240
 
214
241
  data = {
@@ -236,7 +263,7 @@ module Discordrb
236
263
  # @param code [String, Invite] The invite to revoke. For possible formats see {#resolve_invite_code}.
237
264
  def delete_invite(code)
238
265
  invite = resolve_invite_code(code)
239
- API.delete_invite(@token, invite)
266
+ API.delete_invite(token, invite)
240
267
  end
241
268
 
242
269
  # Gets a user by its ID.
@@ -244,6 +271,7 @@ module Discordrb
244
271
  # @param id [Integer] The user ID that should be resolved.
245
272
  # @return [User, nil] The user identified by the ID, or `nil` if it couldn't be found.
246
273
  def user(id)
274
+ id = id.resolve_id
247
275
  @users[id]
248
276
  end
249
277
 
@@ -252,6 +280,7 @@ module Discordrb
252
280
  # @param id [Integer] The server ID that should be resolved.
253
281
  # @return [Server, nil] The server identified by the ID, or `nil` if it couldn't be found.
254
282
  def server(id)
283
+ id = id.resolve_id
255
284
  @servers[id]
256
285
  end
257
286
 
@@ -334,7 +363,7 @@ module Discordrb
334
363
  def send_message(channel_id, content)
335
364
  debug("Sending message to #{channel_id} with content '#{content}'")
336
365
 
337
- response = API.send_message(@token, channel_id, content)
366
+ response = API.send_message(token, channel_id, content)
338
367
  Message.new(JSON.parse(response), self)
339
368
  end
340
369
 
@@ -343,7 +372,8 @@ module Discordrb
343
372
  # @param channel_id [Integer] The ID that identifies the channel to send something to.
344
373
  # @param file [File] The file that should be sent.
345
374
  def send_file(channel_id, file)
346
- API.send_file(@token, channel_id, file)
375
+ response = API.send_file(token, channel_id, file)
376
+ Message.new(JSON.parse(response), self)
347
377
  end
348
378
 
349
379
  # Add an await the bot should listen to. For information on awaits, see {Await}.
@@ -374,7 +404,7 @@ module Discordrb
374
404
  # * `:sydney`
375
405
  # @return [Server] The server that was created.
376
406
  def create_server(name, region = :london)
377
- response = API.create_server(@token, name, region)
407
+ response = API.create_server(token, name, region)
378
408
  id = JSON.parse(response)['id'].to_i
379
409
  sleep 0.1 until @servers[id]
380
410
  server = @servers[id]
@@ -387,7 +417,7 @@ module Discordrb
387
417
  # @return [User] The user identified by the mention, or `nil` if none exists.
388
418
  def parse_mention(mention)
389
419
  # Mention format: <@id>
390
- return nil unless /\<@(?<id>\d+)\>?/ =~ mention
420
+ return nil unless /<@(?<id>\d+)>?/ =~ mention
391
421
  user(id.to_i)
392
422
  end
393
423
 
@@ -398,10 +428,10 @@ module Discordrb
398
428
  @game = name
399
429
 
400
430
  data = {
401
- 'op' => 3,
402
- 'd' => {
403
- 'idle_since' => nil,
404
- 'game' => name ? { 'name' => name } : nil
431
+ op: 3,
432
+ d: {
433
+ idle_since: nil,
434
+ game: name ? { name: name } : nil
405
435
  }
406
436
  }
407
437
 
@@ -453,6 +483,14 @@ module Discordrb
453
483
  register_event(TypingEvent, attributes, block)
454
484
  end
455
485
 
486
+ def message_edit(attributes = {}, &block)
487
+ register_event(MessageEditEvent, attributes, block)
488
+ end
489
+
490
+ def message_delete(attributes = {}, &block)
491
+ register_event(MessageDeleteEvent, attributes, block)
492
+ end
493
+
456
494
  def presence(attributes = {}, &block)
457
495
  register_event(PresenceEvent, attributes, block)
458
496
  end
@@ -514,6 +552,14 @@ module Discordrb
514
552
  register_event(GuildMemberDeleteEvent, attributes, block)
515
553
  end
516
554
 
555
+ def user_ban(attributes = {}, &block)
556
+ register_event(UserBanEvent, attributes, block)
557
+ end
558
+
559
+ def user_unban(attributes = {}, &block)
560
+ register_event(UserUnbanEvent, attributes, block)
561
+ end
562
+
517
563
  def server_create(attributes = {}, &block)
518
564
  register_event(GuildCreateEvent, attributes, block)
519
565
  end
@@ -623,11 +669,7 @@ module Discordrb
623
669
  end
624
670
  end
625
671
  user.status = status
626
- if data['game']
627
- user.game = data['game']['name']
628
- else
629
- user.game = nil
630
- end
672
+ user.game = data['game'] ? data['game']['name'] : nil
631
673
  user
632
674
  end
633
675
 
@@ -664,8 +706,7 @@ module Discordrb
664
706
  channel = @voice_channel
665
707
 
666
708
  debug('Got data, now creating the bot.')
667
- @voice = Discordrb::Voice::VoiceBot.new(channel, self, token, @session_id, endpoint)
668
- @voice
709
+ @voice = Discordrb::Voice::VoiceBot.new(channel, self, token, @session_id, endpoint, @should_encrypt_voice)
669
710
  end
670
711
 
671
712
  # Internal handler for CHANNEL_CREATE
@@ -798,6 +839,18 @@ module Discordrb
798
839
  # Internal handler for TYPING_START
799
840
  def start_typing(data); end
800
841
 
842
+ # Internal handler for MESSAGE_UPDATE
843
+ def update_message(data); end
844
+
845
+ # Internal handler for MESSAGE_DELETE
846
+ def delete_message(data); end
847
+
848
+ # Internal handler for GUILD_BAN_ADD
849
+ def add_user_ban(data); end
850
+
851
+ # Internal handler for GUILD_BAN_REMOVE
852
+ def remove_user_ban(data); end
853
+
801
854
  ## ####### ###### #### ## ##
802
855
  ## ## ## ## ## ## ### ##
803
856
  ## ## ## ## ## #### ##
@@ -813,7 +866,7 @@ module Discordrb
813
866
  # First, attempt to get the token from the cache
814
867
  token = @token_cache.token(@email, @password)
815
868
  if token
816
- debug("Token successfully obtained from cache: #{token}")
869
+ debug('Token successfully obtained from cache!')
817
870
  return token
818
871
  end
819
872
 
@@ -825,7 +878,7 @@ module Discordrb
825
878
  login_response_object = JSON.parse(login_response)
826
879
  fail InvalidAuthenticationException unless login_response_object['token']
827
880
 
828
- debug("Received token: #{login_response_object['token']}")
881
+ debug('Received token from Discord!')
829
882
 
830
883
  # Cache the token
831
884
  @token_cache.store_token(@email, @password, login_response_object['token'])
@@ -851,7 +904,7 @@ module Discordrb
851
904
 
852
905
  def find_gateway
853
906
  # Get updated websocket_hub
854
- response = API.gateway(@token)
907
+ response = API.gateway(token)
855
908
  JSON.parse(response)['url']
856
909
  end
857
910
 
@@ -942,6 +995,16 @@ module Discordrb
942
995
  event = PrivateMessageEvent.new(message, self)
943
996
  raise_event(event)
944
997
  end
998
+ when 'MESSAGE_UPDATE'
999
+ update_message(data)
1000
+
1001
+ event = MessageEditEvent.new(data, self)
1002
+ raise_event(event)
1003
+ when 'MESSAGE_DELETE'
1004
+ delete_message(data)
1005
+
1006
+ event = MessageDeleteEvent.new(data, self)
1007
+ raise_event(event)
945
1008
  when 'TYPING_START'
946
1009
  start_typing(data)
947
1010
 
@@ -952,11 +1015,11 @@ module Discordrb
952
1015
  played_before = user(data['user']['id'].to_i).game
953
1016
  update_presence(data)
954
1017
 
955
- if now_playing != played_before
956
- event = PlayingEvent.new(data, self)
957
- else
958
- event = PresenceEvent.new(data, self)
959
- end
1018
+ event = if now_playing != played_before
1019
+ PlayingEvent.new(data, self)
1020
+ else
1021
+ PresenceEvent.new(data, self)
1022
+ end
960
1023
 
961
1024
  raise_event(event)
962
1025
  when 'VOICE_STATE_UPDATE'
@@ -998,6 +1061,16 @@ module Discordrb
998
1061
 
999
1062
  event = GuildMemberDeleteEvent.new(data, self)
1000
1063
  raise_event(event)
1064
+ when 'GUILD_BAN_ADD'
1065
+ add_user_ban(data)
1066
+
1067
+ event = UserBanEvent.new(data, self)
1068
+ raise_event(event)
1069
+ when 'GUILD_BAN_REMOVE'
1070
+ remove_user_ban(data)
1071
+
1072
+ event = UserUnbanEvent.new(data, self)
1073
+ raise_event(event)
1001
1074
  when 'GUILD_ROLE_UPDATE'
1002
1075
  update_guild_role(data)
1003
1076
 
@@ -1028,6 +1101,9 @@ module Discordrb
1028
1101
 
1029
1102
  event = GuildDeleteEvent.new(data, self)
1030
1103
  raise_event(event)
1104
+ else
1105
+ # another event that we don't support yet
1106
+ debug "Event #{packet['t']} has been received but is unsupported, ignoring"
1031
1107
  end
1032
1108
  rescue Exception => e
1033
1109
  log_exception(e)
@@ -1044,16 +1120,16 @@ module Discordrb
1044
1120
  def websocket_open(_)
1045
1121
  # Send the initial packet
1046
1122
  packet = {
1047
- 'op' => 2, # Packet identifier
1048
- 'd' => { # Packet data
1049
- 'v' => 2, # Another identifier
1050
- 'token' => @token,
1051
- 'properties' => { # I'm unsure what these values are for exactly, but they don't appear to impact bot functionality in any way.
1052
- '$os' => "#{RUBY_PLATFORM}",
1053
- '$browser' => 'discordrb',
1054
- '$device' => 'discordrb',
1055
- '$referrer' => '',
1056
- '$referring_domain' => ''
1123
+ op: 2, # Packet identifier
1124
+ d: { # Packet data
1125
+ v: 2, # Another identifier
1126
+ token: @token,
1127
+ properties: { # I'm unsure what these values are for exactly, but they don't appear to impact bot functionality in any way.
1128
+ '$os': RUBY_PLATFORM.to_s,
1129
+ '$browser': 'discordrb',
1130
+ '$device': 'discordrb',
1131
+ '$referrer': '',
1132
+ '$referring_domain': ''
1057
1133
  }
1058
1134
  }
1059
1135
  }
@@ -1111,8 +1187,8 @@ module Discordrb
1111
1187
  millis = Time.now.strftime('%s%L').to_i
1112
1188
  debug("Sending heartbeat at #{millis}")
1113
1189
  data = {
1114
- 'op' => 1,
1115
- 'd' => millis
1190
+ op: 1,
1191
+ d: millis
1116
1192
  }
1117
1193
 
1118
1194
  @ws.send(data.to_json)