discordrb 3.1.1 → 3.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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7243dd69686cf52feecca4bfe214758c7c3341c5
4
- data.tar.gz: 85afc41eb200b3229e71c885d849c89a5ff31a03
3
+ metadata.gz: 337ea47519caa85873b29fa6a7ac2a77157cbdb1
4
+ data.tar.gz: 3e605cffe66fa9e5604cc4fd0832f00de8648700
5
5
  SHA512:
6
- metadata.gz: 18911b18d0f2fb58539211affc6c735c5a63656cc35764249a0fb12e57a764ba2c5ff521c17a3f60006d6e5a685300114b6837c30697a43d79959988d11d9814
7
- data.tar.gz: 398fc6c56a8781e4a6d7cacfe9677d20c5bc9b78f92a9f45fc382d47a39bb8359d440452e0316f480d65a445059d655a5fdc33e625097b87f418d907dc3c87b2
6
+ metadata.gz: 938fcccefe4d46b647355c3500ca04bce24718aa01f60fabecdffec22801c17f9e3629629615682cd9ee2e070ad92858bcbac00cf76ce821fd88bab310baa7f5
7
+ data.tar.gz: c68a60e989731a168002f87bda571acdff25b8bda65cc2fea32f14fb05ed7056008b9c05428e1d9b844d3eeb1f049947d029c784aa7bce6a7678ceb9f2a6237a
@@ -0,0 +1,16 @@
1
+ ---
2
+ engines:
3
+ duplication:
4
+ enabled: true
5
+ config:
6
+ languages:
7
+ - ruby
8
+ fixme:
9
+ enabled: true
10
+ rubocop:
11
+ enabled: true
12
+ ratings:
13
+ paths:
14
+ - "**.rb"
15
+ exclude_paths:
16
+ - spec/
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /doc/
7
7
  /pkg/
8
8
  /spec/reports/
9
+ /profiles/
9
10
  /tmp/
10
11
  /.idea/
11
12
  .DS_Store
@@ -5,6 +5,9 @@ Metrics/AbcSize:
5
5
  Metrics/BlockNesting:
6
6
  Enabled: false
7
7
 
8
+ Metrics/BlockLength:
9
+ Enabled: false
10
+
8
11
  Metrics/ClassLength:
9
12
  Enabled: false
10
13
 
@@ -38,6 +41,12 @@ Style/ConstantName:
38
41
  Lint/RescueException:
39
42
  Enabled: false
40
43
 
44
+ # Prefer |m, e| for the `reduce` block arguments
45
+ Style/SingleLineBlockParams:
46
+ Methods:
47
+ - reduce: [m, e]
48
+ - inject: [m, e]
49
+
41
50
  AllCops:
42
51
  TargetRubyVersion: 2.1
43
52
 
@@ -1,7 +1,8 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.2
3
+ - 2.1.9
4
4
  before_install: gem install bundler -v 1.10.6
5
5
  script:
6
6
  - bundle exec rspec spec
7
+ - bundle exec codeclimate-test-reporter
7
8
  - bundle exec rubocop -c .rubocop.yml
@@ -1,5 +1,54 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.2.0
4
+
5
+ - Various parts of gateway error handling were improved, leading to significant stability improvements:
6
+ - A long standing bug was fixed that prevented resumes in most cases, which caused unnecessary reconnections.
7
+ - The error handler that handles problems with sending the raw data over TCP now catches errors more broadly.
8
+ - Heartbeat ACKs (opcode 11) are now checked, which allows the client to detect zombie connections early on. If this causes problems for you you can disable it using `bot.gateway.check_heartbeat_acks = false`.
9
+ - Received heartbeats are now properly handled again
10
+ - Added a client for webhooks, implemented as a separate gem `discordrb-webhooks`. This allows the creation of applications that only use webhooks without the overhead provided by the rest of discordrb. The gem is added as a dependency by normal discordrb so you don't need to install it separately if you're already using that.
11
+ - Adding, updating or deleting custom emoji is now supported ([#285](https://github.com/meew0/discordrb/pull/285), thanks @Daniel-Worrall)
12
+ - Rich embeds can now be sent alongside messages, for example using the `embed` parameter in `send_message`, or with the new method `Channel#send_embed`
13
+ - `advanced_functionality` bots now support escaping using backslashes ([#293](https://github.com/meew0/discordrb/issues/293) / [#304](https://github.com/meew0/discordrb/pull/304), thanks @LikeLakers2)
14
+ - Added type checking and conversion for commands ([#298](https://github.com/meew0/discordrb/pull/298), thanks @ohtaavi)
15
+ - Bulk deleting messages now checks for message age (see also [hammerandchisel/discord-api-docs#208](https://github.com/hammerandchisel/discord-api-docs/issues/208)). By default, it will ignore messages that are too old to be bulk deleted, but there is also a `strict` mode setting now that raises an exception in such a case.
16
+ - Reactions can now be viewed for existing messages ([#262](https://github.com/meew0/discordrb/pull/262), thanks @z64), added to messages ([#266](https://github.com/meew0/discordrb/pull/266), thanks @z64), and listened for using gateway events as well as internal handlers ([#300](https://github.com/meew0/discordrb/issues/300)).
17
+ - Game types and stream URLs are now cached ([#297](https://github.com/meew0/discordrb/issues/297))
18
+ - The default non-streaming game was changed to be `0` instead of `nil` ([#277](https://github.com/meew0/discordrb/pull/277), thanks @zeyla)
19
+ - A method `Channel#delete_message` was added to support deleting single messages by ID without prior resolution.
20
+ - Permission overwrites can now be deleted from channels ([#268](https://github.com/meew0/discordrb/pull/268), thanks @greenbigfrog)
21
+ - There is now a utility method `IDObject.synthesise` that creates snowflakes with specific timestamps out of thin air.
22
+ - Typing events are now respondable, so you can call `#respond` on them for example ([#270](https://github.com/meew0/discordrb/pull/270), thanks @VxJasonxV)
23
+ - Message authors can now be `User` objects if a `Member` object could not be found or created ([#290](https://github.com/meew0/discordrb/issues/290))
24
+ - Added two new events, `unknown` ([#288](https://github.com/meew0/discordrb/issues/288)) and `raw`, that are raised for unknown dispatches and all dispatches, respectively.
25
+ - Bots can now be set to fully ignore other bots ([#257](https://github.com/meew0/discordrb/pull/257), thanks @greenbigfrog)
26
+ - Voice state update events now have an `old_channel` property/attribute that denotes the previous channel the user was in in case of joining/moving/leaving.
27
+ - The default help command no longer shows commands the user can't use ([#275](https://github.com/meew0/discordrb/pull/275), thanks @FormalHellhound)
28
+ - Updated the command example to no longer include user-specific stuff ([#260](https://github.com/meew0/discordrb/issues/260))
29
+ - `Server#role` now resolves IDs, so they can be passed as strings if necessary.
30
+
31
+ ### Bugfixes
32
+
33
+ - Fixed bots' shard settings being ignored in certain cases
34
+ - Parsing role mentions using `Bot#parse_mention` works properly now.
35
+ - Fixed some specific REST methods that were broken by the API module refactor ([#302](https://github.com/meew0/discordrb/pull/302), thanks @LikeLakers2)
36
+ - Cached channel data is now updated properly on change ([#272](https://github.com/meew0/discordrb/issues/272))
37
+ - Users' avatars are now updated properly on change ([#265](https://github.com/meew0/discordrb/pull/265), thanks @Roughsketch)
38
+ - Fixed voice state tracking for newly created channels ([#292](https://github.com/meew0/discordrb/issues/292))
39
+ - Fixed event attribute handling for PlayingEvent ([#303](https://github.com/meew0/discordrb/pull/303), thanks @sven-strothoff)
40
+ - Getting specific emoji by ID no longer fails to resolve non-cached emoji ([#283](https://github.com/meew0/discordrb/pull/283), thanks @greenbigfrog)
41
+ - Voice state update events no longer fail to be raised for users leaving channels, if the event handler had a channel attribute set ([#301](https://github.com/meew0/discordrb/issues/301))
42
+ - Bots that don't define any events should work properly again
43
+ - Fixed error handling for messages over the character limit ([#276](https://github.com/meew0/discordrb/issues/276))
44
+ - Fixed some specific log messages not being called properly ([#263](https://github.com/meew0/discordrb/pull/263), thanks @Roughsketch)
45
+ - Fixed some edge case bugs in the default help command:
46
+ - In the case of too many commands to be sent in the channel, it no longer replies with "Sending help in PM!" when called from PM
47
+ - It no longer fails completely if called from PM if there are any commands that require server-specific checks ([#308](https://github.com/meew0/discordrb/issues/308))
48
+ - Fixed a slight formatting mistake
49
+ - Quoted command arguments in `advanced_functionality` are no longer split by newline
50
+ - Fixed a specific edge case in command chain handling where handling commands with the same name as the chain delimiter was broken
51
+
3
52
  ## 3.1.1
4
53
 
5
54
  *Bugfix-only release.*
@@ -0,0 +1,13 @@
1
+ # Contributing guidelines
2
+
3
+ Any contributions are very much appreciated! This project is relatively relaxed when it comes to guidelines, however
4
+ there are still some things that would be nice to have considered.
5
+
6
+ For bug reports, please try to include a code sample if at all appropriate for
7
+ the issue, so we can reproduce it on our own machines.
8
+
9
+ For PRs, please make sure that you tested your code before every push; it's a little annoying to keep having to get back
10
+ to a PR because of small avoidable oversights. (Huge bonus points if you're adding specs for your code! This project
11
+ has very few specs in places where it should have more so every added spec is very much appreciated.)
12
+
13
+ If you have any questions at all, don't be afraid to ask in the [discordrb channel on Discord](https://discord.gg/0SBTUU1wZTWfFQL2).
data/Gemfile CHANGED
@@ -1,4 +1,9 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ # Test coverage reporting for CodeClimate
4
+ gem 'codeclimate-test-reporter', group: :test, require: nil
5
+ gem 'simplecov', group: :test
6
+
3
7
  # Specify your gem's dependencies in discordrb.gemspec
4
- gemspec
8
+ gemspec name: 'discordrb'
9
+ gemspec name: 'discordrb-webhooks', development_group: 'webhooks'
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
  [![Gem](https://img.shields.io/gem/dt/discordrb.svg)](https://rubygems.org/gems/discordrb)
3
3
  [![Build Status](https://travis-ci.org/meew0/discordrb.svg?branch=master)](https://travis-ci.org/meew0/discordrb)
4
4
  [![Inline docs](http://inch-ci.org/github/meew0/discordrb.svg?branch=master&style=shields)](http://inch-ci.org/github/meew0/discordrb)
5
+ [![Code Climate](https://codeclimate.com/github/meew0/discordrb/badges/gpa.svg)](https://codeclimate.com/github/meew0/discordrb)
6
+ [![Test Coverage](https://codeclimate.com/github/meew0/discordrb/badges/coverage.svg)](https://codeclimate.com/github/meew0/discordrb/coverage)
5
7
  [![Join Discord](https://img.shields.io/badge/discord-join-7289DA.svg)](https://discord.gg/0SBTUU1wZTWfFQL2)
6
8
  # discordrb
7
9
 
data/Rakefile CHANGED
@@ -1,4 +1,15 @@
1
- require 'bundler/gem_tasks'
1
+ require 'bundler/gem_helper'
2
+
3
+ namespace :main do
4
+ Bundler::GemHelper.install_tasks(name: 'discordrb')
5
+ end
6
+
7
+ namespace :webhooks do
8
+ Bundler::GemHelper.install_tasks(name: 'discordrb-webhooks')
9
+ end
10
+
11
+ task build: [:'main:build', :'webhooks:build']
12
+ task release: [:'main:release', :'webhooks:release']
2
13
 
3
14
  # Make "build" the default task
4
15
  task default: :build
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'discordrb/webhooks/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'discordrb-webhooks'
8
+ spec.version = Discordrb::Webhooks::VERSION
9
+ spec.authors = ['meew0']
10
+ spec.email = ['']
11
+
12
+ spec.summary = 'Webhook client for discordrb'
13
+ spec.description = "A client for Discord's webhooks to fit alongside [discordrb](https://rubygems.org/gems/discordrb)."
14
+ spec.homepage = 'https://github.com/meew0/discordrb'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z lib/discordrb/webhooks/`.split("\x0") + ['lib/discordrb/webhooks.rb']
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'rest-client'
23
+
24
+ spec.required_ruby_version = '>= 2.1.0'
25
+ end
@@ -14,16 +14,17 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = 'https://github.com/meew0/discordrb'
15
15
  spec.license = 'MIT'
16
16
 
17
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|examples)/}) }
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|examples|lib/discordrb/webhooks)/}) }
18
18
  spec.bindir = 'exe'
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ['lib']
21
21
 
22
22
  spec.add_dependency 'rest-client'
23
- spec.add_dependency 'activesupport'
24
23
  spec.add_dependency 'opus-ruby'
25
24
  spec.add_dependency 'websocket-client-simple', '>= 0.3.0'
26
- spec.add_dependency 'rbnacl'
25
+ spec.add_dependency 'rbnacl', '~> 3.4.0' # 24: update
26
+
27
+ spec.add_dependency 'discordrb-webhooks'
27
28
 
28
29
  spec.required_ruby_version = '>= 2.1.0'
29
30
 
@@ -31,5 +32,6 @@ Gem::Specification.new do |spec|
31
32
  spec.add_development_dependency 'rake', '~> 10.0'
32
33
  spec.add_development_dependency 'yard', '~> 0.8.7.6'
33
34
  spec.add_development_dependency 'rspec', '~> 3.4.0'
34
- spec.add_development_dependency 'rubocop', '0.42.0'
35
+ spec.add_development_dependency 'rspec-prof', '~> 0.0.7'
36
+ spec.add_development_dependency 'rubocop', '0.45.0'
35
37
  end
@@ -137,6 +137,11 @@ module Discordrb::API
137
137
  "#{api_base}/guilds/#{server_id}/widget.png?style=#{style}"
138
138
  end
139
139
 
140
+ # Make a splash URL from server and splash IDs
141
+ def splash_url(server_id, splash_id)
142
+ "https://cdn.discordapp.com/splashes/#{server_id}/#{splash_id}.jpg"
143
+ end
144
+
140
145
  # Make an emoji icon URL from emoji ID
141
146
  def emoji_icon_url(emoji_id)
142
147
  "https://cdn.discordapp.com/emojis/#{emoji_id}.png"
@@ -66,18 +66,20 @@ module Discordrb::API::Channel
66
66
 
67
67
  # Send a message to a channel
68
68
  # https://discordapp.com/developers/docs/resources/channel#create-message
69
- def create_message(token, channel_id, message, mentions = [], tts = false, _server_id = nil) # send message
69
+ def create_message(token, channel_id, message, mentions = [], tts = false, embed = nil) # send message
70
70
  Discordrb::API.request(
71
71
  :channels_cid_messages_mid,
72
72
  channel_id,
73
73
  :post,
74
74
  "#{Discordrb::API.api_base}/channels/#{channel_id}/messages",
75
- { content: message, mentions: mentions, tts: tts }.to_json,
75
+ { content: message, mentions: mentions, tts: tts, embed: embed }.to_json,
76
76
  Authorization: token,
77
77
  content_type: :json
78
78
  )
79
- rescue RestClient::InternalServerError
80
- raise Discordrb::Errors::MessageTooLong, "Message over the character limit (#{message.length} > 2000)"
79
+ rescue RestClient::BadRequest => e
80
+ parsed = JSON.parse(e.response.body)
81
+ raise Discordrb::Errors::MessageTooLong, "Message over the character limit (#{message.length} > 2000)" if parsed['content'] && parsed['content'].is_a?(Array) && parsed['content'].first == 'Must be 2000 or less characters long.'
82
+ raise
81
83
  end
82
84
 
83
85
  # Send a file as a message to a channel
@@ -95,13 +97,13 @@ module Discordrb::API::Channel
95
97
 
96
98
  # Edit a message
97
99
  # https://discordapp.com/developers/docs/resources/channel#edit-message
98
- def edit_message(token, channel_id, message_id, message, mentions = [])
100
+ def edit_message(token, channel_id, message_id, message, mentions = [], embed = nil)
99
101
  Discordrb::API.request(
100
102
  :channels_cid_messages_mid,
101
103
  channel_id,
102
104
  :patch,
103
105
  "#{Discordrb::API.api_base}/channels/#{channel_id}/messages/#{message_id}",
104
- { content: message, mentions: mentions }.to_json,
106
+ { content: message, mentions: mentions, embed: embed }.to_json,
105
107
  Authorization: token,
106
108
  content_type: :json
107
109
  )
@@ -133,6 +135,72 @@ module Discordrb::API::Channel
133
135
  )
134
136
  end
135
137
 
138
+ # Create a reaction on a message using this client
139
+ # https://discordapp.com/developers/docs/resources/channel#create-reaction
140
+ def create_reaction(token, channel_id, message_id, emoji)
141
+ emoji = URI.encode(emoji) unless emoji.ascii_only?
142
+ Discordrb::API.request(
143
+ :channels_cid_messages_mid_reactions_emoji_me,
144
+ channel_id,
145
+ :put,
146
+ "#{Discordrb::API.api_base}/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}/@me",
147
+ nil,
148
+ Authorization: token,
149
+ content_type: :json
150
+ )
151
+ end
152
+
153
+ # Delete this client's own reaction on a message
154
+ # https://discordapp.com/developers/docs/resources/channel#delete-own-reaction
155
+ def delete_own_reaction(token, channel_id, message_id, emoji)
156
+ emoji = URI.encode(emoji) unless emoji.ascii_only?
157
+ Discordrb::API.request(
158
+ :channels_cid_messages_mid_reactions_emoji_me,
159
+ channel_id,
160
+ :delete,
161
+ "#{Discordrb::API.api_base}/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}/@me",
162
+ Authorization: token
163
+ )
164
+ end
165
+
166
+ # Delete another client's reaction on a message
167
+ # https://discordapp.com/developers/docs/resources/channel#delete-user-reaction
168
+ def delete_user_reaction(token, channel_id, message_id, emoji, user_id)
169
+ emoji = URI.encode(emoji) unless emoji.ascii_only?
170
+ Discordrb::API.request(
171
+ :channels_cid_messages_mid_reactions_emoji_uid,
172
+ channel_id,
173
+ :delete,
174
+ "#{Discordrb::API.api_base}/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}/#{user_id}",
175
+ Authorization: token
176
+ )
177
+ end
178
+
179
+ # Get a list of clients who reacted with a specific reaction on a message
180
+ # https://discordapp.com/developers/docs/resources/channel#get-reactions
181
+ def get_reactions(token, channel_id, message_id, emoji)
182
+ emoji = URI.encode(emoji) unless emoji.ascii_only?
183
+ Discordrb::API.request(
184
+ :channels_cid_messages_mid_reactions_emoji,
185
+ channel_id,
186
+ :get,
187
+ "#{Discordrb::API.api_base}/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji}",
188
+ Authorization: token
189
+ )
190
+ end
191
+
192
+ # Deletes all reactions on a message from all clients
193
+ # https://discordapp.com/developers/docs/resources/channel#delete-all-reactions
194
+ def delete_all_reactions(token, channel_id, message_id)
195
+ Discordrb::API.request(
196
+ :channels_cid_messages_mid_reactions,
197
+ channel_id,
198
+ :delete,
199
+ "#{Discordrb::API.api_base}/channels/#{channel_id}/messages/#{message_id}/reactions",
200
+ Authorization: token
201
+ )
202
+ end
203
+
136
204
  # Update a channels permission for a role or member
137
205
  # https://discordapp.com/developers/docs/resources/channel#edit-channel-permissions
138
206
  def update_permission(token, channel_id, overwrite_id, allow, deny, type)
@@ -354,4 +354,41 @@ module Discordrb::API::Server
354
354
  Authorization: token
355
355
  )
356
356
  end
357
+
358
+ # Adds a custom emoji
359
+ def add_emoji(token, server_id, image, name)
360
+ Discordrb::API.request(
361
+ :guilds_sid_emojis,
362
+ server_id,
363
+ :post,
364
+ "#{Discordrb::API.api_base}/guilds/#{server_id}/emojis",
365
+ { image: image, name: name }.to_json,
366
+ Authorization: token,
367
+ content_type: :json
368
+ )
369
+ end
370
+
371
+ # Changes an emoji name
372
+ def edit_emoji(token, server_id, emoji_id, name)
373
+ Discordrb::API.request(
374
+ :guilds_sid_emojis_eid,
375
+ server_id,
376
+ :patch,
377
+ "#{Discordrb::API.api_base}/guilds/#{server_id}/emojis/#{emoji_id}",
378
+ { name: name }.to_json,
379
+ Authorization: token,
380
+ content_type: :json
381
+ )
382
+ end
383
+
384
+ # Deletes a custom emoji
385
+ def delete_emoji(token, server_id, emoji_id)
386
+ Discordrb::API.request(
387
+ :guilds_sid_emojis_eid,
388
+ server_id,
389
+ :delete,
390
+ "#{Discordrb::API.api_base}/guilds/#{server_id}/emojis/#{emoji_id}",
391
+ Authorization: token
392
+ )
393
+ end
357
394
  end
@@ -15,6 +15,8 @@ require 'discordrb/events/roles'
15
15
  require 'discordrb/events/guilds'
16
16
  require 'discordrb/events/await'
17
17
  require 'discordrb/events/bans'
18
+ require 'discordrb/events/raw'
19
+ require 'discordrb/events/reactions'
18
20
 
19
21
  require 'discordrb/api'
20
22
  require 'discordrb/api/channel'
@@ -91,11 +93,12 @@ module Discordrb
91
93
  # @param num_shards [Integer] The total number of shards that should be running. See
92
94
  # https://github.com/hammerandchisel/discord-api-docs/issues/17 for how to do sharding.
93
95
  # @param redact_token [true, false] Whether the bot should redact the token in logs. Default is true.
96
+ # @param ignore_bots [true, false] Whether the bot should ignore bot accounts or not. Default is false.
94
97
  def initialize(
95
98
  log_mode: :normal,
96
- token: nil, client_id: nil, application_id: nil,
99
+ token: nil, client_id: nil,
97
100
  type: nil, name: '', fancy_log: false, suppress_ready: false, parse_self: false,
98
- shard_id: nil, num_shards: nil, redact_token: true
101
+ shard_id: nil, num_shards: nil, redact_token: true, ignore_bots: false
99
102
  )
100
103
 
101
104
  LOGGER.mode = if log_mode.is_a? TrueClass # Specifically check for `true` because people might not have updated yet
@@ -108,10 +111,6 @@ module Discordrb
108
111
 
109
112
  @should_parse_self = parse_self
110
113
 
111
- if application_id
112
- raise ArgumentError, 'Starting with discordrb 3.0.0, the application_id parameter has been renamed to client_id! Make sure to change this in your bot. This check will be removed in 3.1.0.'
113
- end
114
-
115
114
  @client_id = client_id
116
115
 
117
116
  @type = type || :bot
@@ -123,7 +122,7 @@ module Discordrb
123
122
  @prevent_ready = suppress_ready
124
123
 
125
124
  @token = process_token(@type, token)
126
- @gateway = Gateway.new(self, @token)
125
+ @gateway = Gateway.new(self, @token, @shard_key)
127
126
 
128
127
  init_cache
129
128
 
@@ -131,6 +130,7 @@ module Discordrb
131
130
  @should_connect_to_voice = {}
132
131
 
133
132
  @ignored_ids = Set.new
133
+ @ignore_bots = ignore_bots
134
134
 
135
135
  @event_threads = []
136
136
  @current_thread = 0
@@ -162,7 +162,8 @@ module Discordrb
162
162
  def emoji(id = nil)
163
163
  gateway_check
164
164
  if id
165
- emoji = @emoji.find { |sth| sth.id == id }
165
+ emoji
166
+ @emoji.find { |sth| sth.id == id }
166
167
  else
167
168
  emoji = {}
168
169
  @servers.each do |_, server|
@@ -170,7 +171,7 @@ module Discordrb
170
171
  emoji[element.name] = GlobalEmoji.new(element, self)
171
172
  end
172
173
  end
173
- emoji.values
174
+ @emoji = emoji.values
174
175
  end
175
176
  end
176
177
 
@@ -265,7 +266,7 @@ module Discordrb
265
266
  # @param permission_bits [Integer, String] Permission bits that should be appended to invite url.
266
267
  # @return [String] the OAuth invite URL.
267
268
  def invite_url(server: nil, permission_bits: nil)
268
- raise 'No application ID has been set during initialization! Add one as the `application_id` named parameter while creating your bot.' unless @client_id
269
+ raise 'No application ID has been set during initialization! Add one as the `client_id` named parameter while creating your bot.' unless @client_id
269
270
 
270
271
  server_id_str = server ? "&guild_id=#{server.id}" : ''
271
272
  permission_bits_str = permission_bits ? "&permissions=#{permission_bits}" : ''
@@ -346,13 +347,13 @@ module Discordrb
346
347
  # @param channel_id [Integer] The ID that identifies the channel to send something to.
347
348
  # @param content [String] The text that should be sent as a message. It is limited to 2000 characters (Discord imposed).
348
349
  # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
349
- # @param server_id [Integer] The ID that identifies the server to send something to.
350
+ # @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
350
351
  # @return [Message] The message that was sent.
351
- def send_message(channel_id, content, tts = false, server_id = nil)
352
+ def send_message(channel_id, content, tts = false, embed = nil)
352
353
  channel_id = channel_id.resolve_id
353
354
  debug("Sending message to #{channel_id} with content '#{content}'")
354
355
 
355
- response = API::Channel.create_message(token, channel_id, content, [], tts, server_id)
356
+ response = API::Channel.create_message(token, channel_id, content, [], tts, embed ? embed.to_hash : nil)
356
357
  Message.new(JSON.parse(response), self)
357
358
  end
358
359
 
@@ -362,10 +363,10 @@ module Discordrb
362
363
  # @param content [String] The text that should be sent as a message. It is limited to 2000 characters (Discord imposed).
363
364
  # @param timeout [Float] The amount of time in seconds after which the message sent will be deleted.
364
365
  # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
365
- # @param server_id [Integer] The ID that identifies the server to send something to.
366
- def send_temporary_message(channel_id, content, timeout, tts = false, server_id = nil)
366
+ # @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
367
+ def send_temporary_message(channel_id, content, timeout, tts = false, embed = nil)
367
368
  Thread.new do
368
- message = send_message(channel_id, content, tts, server_id)
369
+ message = send_message(channel_id, content, tts, embed)
369
370
 
370
371
  sleep(timeout)
371
372
 
@@ -440,11 +441,14 @@ module Discordrb
440
441
  if /<@!?(?<id>\d+)>?/ =~ mention
441
442
  user(id.to_i)
442
443
  elsif /<@&(?<id>\d+)>?/ =~ mention
443
- return server.role(id) if server
444
- servers.each do |element|
445
- role = element.role(id)
444
+ return server.role(id.to_i) if server
445
+ @servers.values.each do |element|
446
+ role = element.role(id.to_i)
446
447
  return role unless role.nil?
447
448
  end
449
+
450
+ # Return nil if no role is found
451
+ nil
448
452
  elsif /<:(\w+):(?<id>\d+)>?/ =~ mention
449
453
  emoji.find { |element| element.id.to_i == id.to_i }
450
454
  end
@@ -463,7 +467,7 @@ module Discordrb
463
467
  @game = game
464
468
  @status = status
465
469
  @streamurl = url
466
- type = url ? 1 : nil
470
+ type = url ? 1 : 0
467
471
 
468
472
  game_obj = game || url ? { name: game, url: url, type: type } : nil
469
473
  @gateway.send_status_update(status, since, game_obj, afk)
@@ -640,21 +644,28 @@ module Discordrb
640
644
  member.update_username(username)
641
645
  end
642
646
 
643
- member.status = data['status'].to_sym
644
- member.game = data['game'] ? data['game']['name'] : nil
647
+ member.update_presence(data)
648
+
649
+ member.avatar_id = data['user']['avatar'] if data['user']['avatar']
645
650
 
646
651
  server.cache_member(member)
647
652
  end
648
653
 
649
- # Internal handler for VOICE_STATUS_UPDATE
654
+ # Internal handler for VOICE_STATE_UPDATE
650
655
  def update_voice_state(data)
656
+ @session_id = data['session_id']
657
+
651
658
  server_id = data['guild_id'].to_i
652
659
  server = server(server_id)
653
660
  return unless server
654
661
 
662
+ user_id = data['user_id'].to_i
663
+ old_voice_state = server.voice_states[user_id]
664
+ old_channel_id = old_voice_state.voice_channel.id if old_voice_state
665
+
655
666
  server.update_voice_state(data)
656
667
 
657
- @session_id = data['session_id']
668
+ old_channel_id
658
669
  end
659
670
 
660
671
  # Internal handler for VOICE_SERVER_UPDATE
@@ -686,7 +697,7 @@ module Discordrb
686
697
 
687
698
  # Handle normal and private channels separately
688
699
  if server
689
- server.channels << channel
700
+ server.add_channel(channel)
690
701
  @channels[channel.id] = channel
691
702
  elsif channel.pm?
692
703
  @pm_channels[channel.recipient.id] = channel
@@ -711,7 +722,7 @@ module Discordrb
711
722
  # Handle normal and private channels separately
712
723
  if server
713
724
  @channels.delete(channel.id)
714
- server.channels.reject! { |c| c.id == channel.id }
725
+ server.delete_channel(channel.id)
715
726
  elsif channel.pm?
716
727
  @pm_channels.delete(channel.recipient.id)
717
728
  elsif channel.group?
@@ -813,6 +824,13 @@ module Discordrb
813
824
  server.delete_role(role_id)
814
825
  end
815
826
 
827
+ # Internal handler for GUILD_EMOJIS_UPDATE
828
+ def update_guild_emoji(data)
829
+ server_id = data['guild_id'].to_i
830
+ server = @servers[server_id]
831
+ server.update_emoji_data(data)
832
+ end
833
+
816
834
  # Internal handler for MESSAGE_CREATE
817
835
  def create_message(data); end
818
836
 
@@ -825,6 +843,15 @@ module Discordrb
825
843
  # Internal handler for MESSAGE_DELETE
826
844
  def delete_message(data); end
827
845
 
846
+ # Internal handler for MESSAGE_REACTION_ADD
847
+ def add_message_reaction(data); end
848
+
849
+ # Internal handler for MESSAGE_REACTION_REMOVE
850
+ def remove_message_reaction(data); end
851
+
852
+ # Internal handler for MESSAGE_REACTION_REMOVE_ALL
853
+ def remove_all_message_reactions(data); end
854
+
828
855
  # Internal handler for GUILD_BAN_ADD
829
856
  def add_user_ban(data); end
830
857
 
@@ -917,6 +944,11 @@ module Discordrb
917
944
  return
918
945
  end
919
946
 
947
+ if @ignore_bots && data['author']['bot']
948
+ debug("Ignored Bot account with ID #{data['author']['id']}")
949
+ return
950
+ end
951
+
920
952
  # If create_message is overwritten with a method that returns the parsed message, use that instead, so we don't
921
953
  # parse the message twice (which is just thrown away performance)
922
954
  message = create_message(data)
@@ -979,6 +1011,21 @@ module Discordrb
979
1011
  rescue Discordrb::Errors::NoPermission
980
1012
  debug 'Typing started in channel the bot has no access to, ignoring'
981
1013
  end
1014
+ when :MESSAGE_REACTION_ADD
1015
+ add_message_reaction(data)
1016
+
1017
+ event = ReactionAddEvent.new(data, self)
1018
+ raise_event(event)
1019
+ when :MESSAGE_REACTION_REMOVE
1020
+ remove_message_reaction(data)
1021
+
1022
+ event = ReactionRemoveEvent.new(data, self)
1023
+ raise_event(event)
1024
+ when :MESSAGE_REACTION_REMOVE_ALL
1025
+ remove_all_message_reactions(data)
1026
+
1027
+ event = ReactionRemoveAllEvent.new(data, self)
1028
+ raise_event(event)
982
1029
  when :PRESENCE_UPDATE
983
1030
  # Ignore friends list presences
984
1031
  return unless data['guild_id']
@@ -996,9 +1043,9 @@ module Discordrb
996
1043
 
997
1044
  raise_event(event)
998
1045
  when :VOICE_STATE_UPDATE
999
- update_voice_state(data)
1046
+ old_channel_id = update_voice_state(data)
1000
1047
 
1001
- event = VoiceStateUpdateEvent.new(data, self)
1048
+ event = VoiceStateUpdateEvent.new(data, old_channel_id, self)
1002
1049
  raise_event(event)
1003
1050
  when :VOICE_SERVER_UPDATE
1004
1051
  update_voice_server(data)
@@ -1100,9 +1147,49 @@ module Discordrb
1100
1147
 
1101
1148
  event = ServerDeleteEvent.new(data, self)
1102
1149
  raise_event(event)
1150
+ when :GUILD_EMOJIS_UPDATE
1151
+ server_id = data['guild_id'].to_i
1152
+ server = @servers[server_id]
1153
+ old_emoji_data = server.emoji.clone
1154
+ update_guild_emoji(data)
1155
+ new_emoji_data = server.emoji
1156
+
1157
+ created_ids = new_emoji_data.keys - old_emoji_data.keys
1158
+ deleted_ids = old_emoji_data.keys - new_emoji_data.keys
1159
+ updated_ids = old_emoji_data.select do |k, v|
1160
+ new_emoji_data[k] && (v.name != new_emoji_data[k].name || v.roles != new_emoji_data[k].roles)
1161
+ end.keys
1162
+
1163
+ event = ServerEmojiChangeEvent.new(server, data, self)
1164
+ raise_event(event)
1165
+
1166
+ created_ids.each do |e|
1167
+ event = ServerEmojiCreateEvent.new(server, new_emoji_data[e], self)
1168
+ raise_event(event)
1169
+ end
1170
+
1171
+ deleted_ids.each do |e|
1172
+ event = ServerEmojiDeleteEvent.new(server, old_emoji_data[e], self)
1173
+ raise_event(event)
1174
+ end
1175
+
1176
+ updated_ids.each do |e|
1177
+ event = ServerEmojiUpdateEvent.new(server, old_emoji_data[e], new_emoji_data[e], self)
1178
+ raise_event(event)
1179
+ end
1103
1180
  else
1104
1181
  # another event that we don't support yet
1105
- debug "Event #{type} has been received but is unsupported, ignoring"
1182
+ debug "Event #{type} has been received but is unsupported. Raising UnknownEvent"
1183
+
1184
+ event = UnknownEvent.new(type, data, self)
1185
+ raise_event(event)
1186
+ end
1187
+
1188
+ # The existence of this array is checked before for performance reasons, since this has to be done for *every*
1189
+ # dispatch.
1190
+ if @event_handlers && @event_handlers[RawEvent]
1191
+ event = RawEvent.new(type, data, self)
1192
+ raise_event(event)
1106
1193
  end
1107
1194
  rescue Exception => e
1108
1195
  LOGGER.error('Gateway message error!')