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 +4 -4
- data/.rubocop.yml +11 -0
- data/.yardopts +1 -1
- data/CHANGELOG.md +27 -0
- data/README.md +4 -0
- data/discordrb.gemspec +2 -0
- data/lib/discordrb.rb +17 -0
- data/lib/discordrb/api.rb +57 -31
- data/lib/discordrb/await.rb +2 -1
- data/lib/discordrb/bot.rb +125 -49
- data/lib/discordrb/commands/command_bot.rb +7 -2
- data/lib/discordrb/commands/parser.rb +2 -0
- data/lib/discordrb/data.rb +471 -112
- data/lib/discordrb/events/bans.rb +54 -0
- data/lib/discordrb/events/channel_create.rb +10 -10
- data/lib/discordrb/events/channel_delete.rb +10 -10
- data/lib/discordrb/events/channel_update.rb +10 -10
- data/lib/discordrb/events/guild_role_create.rb +5 -5
- data/lib/discordrb/events/guild_role_delete.rb +5 -5
- data/lib/discordrb/events/guild_role_update.rb +5 -5
- data/lib/discordrb/events/guilds.rb +7 -8
- data/lib/discordrb/events/members.rb +5 -5
- data/lib/discordrb/events/message.rb +48 -0
- data/lib/discordrb/events/presence.rb +25 -29
- data/lib/discordrb/events/typing.rb +7 -7
- data/lib/discordrb/events/voice_state_update.rb +34 -34
- data/lib/discordrb/permissions.rb +1 -1
- data/lib/discordrb/token_cache.rb +2 -3
- data/lib/discordrb/version.rb +2 -1
- data/lib/discordrb/voice/encoder.rb +32 -2
- data/lib/discordrb/voice/network.rb +87 -11
- data/lib/discordrb/voice/voice_bot.rb +77 -20
- metadata +33 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f012d4df7d5f6beac41402044ddc9f597aef8b7
|
4
|
+
data.tar.gz: 1b75879a95b4be99a0faef8617e1b019c3b7bb81
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb0e782b340e2fa53f2b93618594332f2b8c0025cad5756072f9792786f0f79c9768e0938cd934f2c289ec36edd8025c6c6e90e15dbe98a36e20eb82d09a5bf3
|
7
|
+
data.tar.gz: 4d75ececf380101a0f7dd9b3e8f748c658446f0b54d5a5f19fbadc4aa6edc7c9671174723ea792d56527b60c21c4543c0ef69fc822630a930331d4fb63c0e5b4
|
data/.rubocop.yml
CHANGED
@@ -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
|
data/CHANGELOG.md
CHANGED
@@ -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:
|
data/discordrb.gemspec
CHANGED
@@ -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
|
data/lib/discordrb.rb
CHANGED
@@ -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
|
data/lib/discordrb/api.rb
CHANGED
@@ -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
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
15
|
+
def bot_name=(value)
|
16
|
+
@bot_name = value
|
17
|
+
end
|
23
18
|
|
24
|
-
|
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
|
-
|
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
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
{
|
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
|
-
{
|
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
|
-
{
|
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
|
-
{
|
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
|
-
{
|
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
|
-
{
|
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
|
-
{
|
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
|
-
{
|
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
|
-
{
|
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
|
-
{
|
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
|
-
{
|
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
|
-
{
|
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
|
-
{
|
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
|
)
|
data/lib/discordrb/await.rb
CHANGED
@@ -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]
|
data/lib/discordrb/bot.rb
CHANGED
@@ -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(
|
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(
|
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'
|
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
|
-
|
197
|
-
|
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
|
-
|
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)
|
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(
|
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(
|
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(
|
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(
|
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
|
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
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
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
|
-
|
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(
|
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(
|
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(
|
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
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
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
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
'$os'
|
1053
|
-
'$browser'
|
1054
|
-
'$device'
|
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
|
-
|
1115
|
-
|
1190
|
+
op: 1,
|
1191
|
+
d: millis
|
1116
1192
|
}
|
1117
1193
|
|
1118
1194
|
@ws.send(data.to_json)
|