discordrb 1.1.3 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of discordrb might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +9 -9
- data/.travis.yml +4 -4
- data/Gemfile +4 -4
- data/LICENSE.txt +21 -21
- data/README.md +78 -78
- data/Rakefile +4 -4
- data/bin/console +14 -14
- data/bin/setup +7 -7
- data/discordrb.gemspec +28 -28
- data/examples/ping.rb +11 -11
- data/examples/pm.rb +11 -11
- data/lib/discordrb.rb +6 -6
- data/lib/discordrb/bot.rb +525 -342
- data/lib/discordrb/data.rb +391 -140
- data/lib/discordrb/endpoints/endpoints.rb +16 -16
- data/lib/discordrb/events/channel-create.rb +48 -0
- data/lib/discordrb/events/channel-delete.rb +48 -0
- data/lib/discordrb/events/channel-update.rb +49 -0
- data/lib/discordrb/events/generic.rb +59 -59
- data/lib/discordrb/events/guild-member-update.rb +40 -0
- data/lib/discordrb/events/guild-role-create.rb +34 -0
- data/lib/discordrb/events/guild-role-delete.rb +35 -0
- data/lib/discordrb/events/guild-role-update.rb +34 -0
- data/lib/discordrb/events/lifetime.rb +9 -9
- data/lib/discordrb/events/message.rb +59 -59
- data/lib/discordrb/events/presence.rb +40 -40
- data/lib/discordrb/events/typing.rb +45 -45
- data/lib/discordrb/events/voice-state-update.rb +89 -0
- data/lib/discordrb/exceptions.rb +10 -10
- data/lib/discordrb/version.rb +3 -3
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 002c1c37653e0e75bbe66c5727030a39f5eae9d8
|
4
|
+
data.tar.gz: e703d694a84d3fe9b738403aee0517fa250ae549
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e43742e8e297e34d20892936267e1e60464945ee020b6c69c4b6d6f8277fb7256b32e3050a5af3696f0926f168e1e889e2dc9b0a7517d4ba04c62f38f1f1b24c
|
7
|
+
data.tar.gz: ce006992dd5bf162e19c32f0bca74980982023954ee448ec7b04916d153bf7bade1d5f6b182a108bb05e298160d3f8b73f25f84d7cc71dff28cdb88a7300813e
|
data/.gitignore
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
/.bundle/
|
2
|
-
/.yardoc
|
3
|
-
/Gemfile.lock
|
4
|
-
/_yardoc/
|
5
|
-
/coverage/
|
6
|
-
/doc/
|
7
|
-
/pkg/
|
8
|
-
/spec/reports/
|
9
|
-
/tmp/
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/Gemfile.lock
|
4
|
+
/_yardoc/
|
5
|
+
/coverage/
|
6
|
+
/doc/
|
7
|
+
/pkg/
|
8
|
+
/spec/reports/
|
9
|
+
/tmp/
|
data/.travis.yml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
language: ruby
|
2
|
-
rvm:
|
3
|
-
- 2.2.2
|
4
|
-
before_install: gem install bundler -v 1.10.6
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.2.2
|
4
|
+
before_install: gem install bundler -v 1.10.6
|
data/Gemfile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
# Specify your gem's dependencies in discordrb.gemspec
|
4
|
-
gemspec
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in discordrb.gemspec
|
4
|
+
gemspec
|
data/LICENSE.txt
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
The MIT License (MIT)
|
2
|
-
|
3
|
-
Copyright (c) 2015 meew0
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
7
|
-
in the Software without restriction, including without limitation the rights
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
10
|
-
furnished to do so, subject to the following conditions:
|
11
|
-
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
13
|
-
all copies or substantial portions of the Software.
|
14
|
-
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
-
THE SOFTWARE.
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 meew0
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,78 +1,78 @@
|
|
1
|
-
[![Build Status](https://travis-ci.org/meew0/discordrb.svg?branch=master)](https://travis-ci.org/meew0/discordrb)
|
2
|
-
|
3
|
-
# discordrb
|
4
|
-
|
5
|
-
An implementation of the [Discord](https://discordapp.com/) API using Ruby.
|
6
|
-
|
7
|
-
## Installation
|
8
|
-
|
9
|
-
### Linux
|
10
|
-
|
11
|
-
On Linux, it should be as simple as running:
|
12
|
-
|
13
|
-
$ gem install discordrb
|
14
|
-
|
15
|
-
### Windows
|
16
|
-
|
17
|
-
On Windows, to install discordrb, run this in a shell:
|
18
|
-
|
19
|
-
$ gem install discordrb
|
20
|
-
|
21
|
-
Run the [ping example](https://github.com/meew0/discordrb/blob/master/examples/ping.rb) to verify that the installation works:
|
22
|
-
|
23
|
-
$ ruby ping.rb
|
24
|
-
|
25
|
-
#### Troubleshooting
|
26
|
-
|
27
|
-
**If you get an error like this when installing the gem**:
|
28
|
-
|
29
|
-
ERROR: Error installing discordrb:
|
30
|
-
The 'websocket-driver' native gem requires installed build tools.
|
31
|
-
|
32
|
-
You're missing the development kit required to build native extensions. Follow [these instructions](https://github.com/oneclick/rubyinstaller/wiki/Development-Kit#installation-instructions) and reinstall discordrb:
|
33
|
-
|
34
|
-
$ gem uninstall discordrb
|
35
|
-
$ gem install discordrb
|
36
|
-
|
37
|
-
**If you get an error like this when running the example**:
|
38
|
-
|
39
|
-
terminate called after throwing an instance of 'std::runtime_error'
|
40
|
-
what(): Encryption not available on this event-machine
|
41
|
-
|
42
|
-
You're missing the OpenSSL libraries that EventMachine, a dependency of discordrb, needs to be built with to use encrypted connections (which Discord requires). Download the OpenSSL libraries from [here](http://slproweb.com/download/Win32OpenSSL-1_0_2d.exe), install them to their default location and reinstall EventMachine using these libraries:
|
43
|
-
|
44
|
-
$ gem uninstall eventmachine
|
45
|
-
$ gem install eventmachine -- --with-ssl-dir=C:/OpenSSL-Win32
|
46
|
-
|
47
|
-
## Usage
|
48
|
-
|
49
|
-
You can make a simple bot like this:
|
50
|
-
|
51
|
-
```ruby
|
52
|
-
require 'discordrb'
|
53
|
-
|
54
|
-
bot = Discordrb::Bot.new "email@example.com", "hunter2"
|
55
|
-
|
56
|
-
bot.message(with_text: "Ping!") do |event|
|
57
|
-
event.respond "Pong!"
|
58
|
-
end
|
59
|
-
|
60
|
-
bot.run
|
61
|
-
```
|
62
|
-
|
63
|
-
This bot responds to every "Ping!" with a "Pong!".
|
64
|
-
|
65
|
-
## Development
|
66
|
-
|
67
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake false` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
68
|
-
|
69
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
70
|
-
|
71
|
-
## Contributing
|
72
|
-
|
73
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/meew0/discordrb.
|
74
|
-
|
75
|
-
|
76
|
-
## License
|
77
|
-
|
78
|
-
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
1
|
+
[![Build Status](https://travis-ci.org/meew0/discordrb.svg?branch=master)](https://travis-ci.org/meew0/discordrb)
|
2
|
+
|
3
|
+
# discordrb
|
4
|
+
|
5
|
+
An implementation of the [Discord](https://discordapp.com/) API using Ruby.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
### Linux
|
10
|
+
|
11
|
+
On Linux, it should be as simple as running:
|
12
|
+
|
13
|
+
$ gem install discordrb
|
14
|
+
|
15
|
+
### Windows
|
16
|
+
|
17
|
+
On Windows, to install discordrb, run this in a shell:
|
18
|
+
|
19
|
+
$ gem install discordrb
|
20
|
+
|
21
|
+
Run the [ping example](https://github.com/meew0/discordrb/blob/master/examples/ping.rb) to verify that the installation works (make sure to replace the username and password in there with your own or your bots'!):
|
22
|
+
|
23
|
+
$ ruby ping.rb
|
24
|
+
|
25
|
+
#### Troubleshooting
|
26
|
+
|
27
|
+
**If you get an error like this when installing the gem**:
|
28
|
+
|
29
|
+
ERROR: Error installing discordrb:
|
30
|
+
The 'websocket-driver' native gem requires installed build tools.
|
31
|
+
|
32
|
+
You're missing the development kit required to build native extensions. Follow [these instructions](https://github.com/oneclick/rubyinstaller/wiki/Development-Kit#installation-instructions) and reinstall discordrb:
|
33
|
+
|
34
|
+
$ gem uninstall discordrb
|
35
|
+
$ gem install discordrb
|
36
|
+
|
37
|
+
**If you get an error like this when running the example**:
|
38
|
+
|
39
|
+
terminate called after throwing an instance of 'std::runtime_error'
|
40
|
+
what(): Encryption not available on this event-machine
|
41
|
+
|
42
|
+
You're missing the OpenSSL libraries that EventMachine, a dependency of discordrb, needs to be built with to use encrypted connections (which Discord requires). Download the OpenSSL libraries from [here](http://slproweb.com/download/Win32OpenSSL-1_0_2d.exe), install them to their default location and reinstall EventMachine using these libraries:
|
43
|
+
|
44
|
+
$ gem uninstall eventmachine
|
45
|
+
$ gem install eventmachine -- --with-ssl-dir=C:/OpenSSL-Win32
|
46
|
+
|
47
|
+
## Usage
|
48
|
+
|
49
|
+
You can make a simple bot like this:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
require 'discordrb'
|
53
|
+
|
54
|
+
bot = Discordrb::Bot.new "email@example.com", "hunter2"
|
55
|
+
|
56
|
+
bot.message(with_text: "Ping!") do |event|
|
57
|
+
event.respond "Pong!"
|
58
|
+
end
|
59
|
+
|
60
|
+
bot.run
|
61
|
+
```
|
62
|
+
|
63
|
+
This bot responds to every "Ping!" with a "Pong!".
|
64
|
+
|
65
|
+
## Development
|
66
|
+
|
67
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake false` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
68
|
+
|
69
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
70
|
+
|
71
|
+
## Contributing
|
72
|
+
|
73
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/meew0/discordrb.
|
74
|
+
|
75
|
+
|
76
|
+
## License
|
77
|
+
|
78
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require "bundler/gem_tasks"
|
2
|
-
|
3
|
-
# Make "build" the default task
|
4
|
-
task :default => :build
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
# Make "build" the default task
|
4
|
+
task :default => :build
|
data/bin/console
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "discordrb"
|
5
|
-
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "discordrb"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
#!/bin/bash
|
2
|
-
set -euo pipefail
|
3
|
-
IFS=$'\n\t'
|
4
|
-
|
5
|
-
bundle install
|
6
|
-
|
7
|
-
# Do any other automated setup that you need to do here
|
1
|
+
#!/bin/bash
|
2
|
+
set -euo pipefail
|
3
|
+
IFS=$'\n\t'
|
4
|
+
|
5
|
+
bundle install
|
6
|
+
|
7
|
+
# Do any other automated setup that you need to do here
|
data/discordrb.gemspec
CHANGED
@@ -1,28 +1,28 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'discordrb/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "discordrb"
|
8
|
-
spec.version = Discordrb::VERSION
|
9
|
-
spec.authors = ["meew0"]
|
10
|
-
spec.email = [""]
|
11
|
-
|
12
|
-
spec.summary = "Discord API for Ruby"
|
13
|
-
spec.description = "A Ruby implementation of the Discord (https://discordapp.com) API."
|
14
|
-
spec.homepage = "https://github.com/meew0/discordrb"
|
15
|
-
spec.license = "MIT"
|
16
|
-
|
17
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
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 "faye-websocket"
|
23
|
-
spec.add_dependency "rest-client"
|
24
|
-
spec.add_dependency "activesupport"
|
25
|
-
|
26
|
-
spec.add_development_dependency "bundler", "~> 1.10"
|
27
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
-
end
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'discordrb/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "discordrb"
|
8
|
+
spec.version = Discordrb::VERSION
|
9
|
+
spec.authors = ["meew0"]
|
10
|
+
spec.email = [""]
|
11
|
+
|
12
|
+
spec.summary = "Discord API for Ruby"
|
13
|
+
spec.description = "A Ruby implementation of the Discord (https://discordapp.com) API."
|
14
|
+
spec.homepage = "https://github.com/meew0/discordrb"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
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 "faye-websocket"
|
23
|
+
spec.add_dependency "rest-client"
|
24
|
+
spec.add_dependency "activesupport"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
end
|
data/examples/ping.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
# This simple bot responds to every "Ping!" message with a "Pong!"
|
2
|
-
|
3
|
-
require 'discordrb'
|
4
|
-
|
5
|
-
bot = Discordrb::Bot.new "email@example.com", "hunter2"
|
6
|
-
|
7
|
-
bot.message(with_text: "Ping!") do |event|
|
8
|
-
event.respond "Pong!"
|
9
|
-
end
|
10
|
-
|
11
|
-
bot.run
|
1
|
+
# This simple bot responds to every "Ping!" message with a "Pong!"
|
2
|
+
|
3
|
+
require 'discordrb'
|
4
|
+
|
5
|
+
bot = Discordrb::Bot.new "email@example.com", "hunter2"
|
6
|
+
|
7
|
+
bot.message(with_text: "Ping!") do |event|
|
8
|
+
event.respond "Pong!"
|
9
|
+
end
|
10
|
+
|
11
|
+
bot.run
|
data/examples/pm.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
# This bot shows off PM functionality by sending a PM every time the bot is mentioned.
|
2
|
-
|
3
|
-
require 'discordrb'
|
4
|
-
|
5
|
-
bot = Discordrb::Bot.new "email@example.com", "hunter2"
|
6
|
-
|
7
|
-
bot.mention do |event|
|
8
|
-
event.user.pm("You have mentioned me!")
|
9
|
-
end
|
10
|
-
|
11
|
-
bot.run
|
1
|
+
# This bot shows off PM functionality by sending a PM every time the bot is mentioned.
|
2
|
+
|
3
|
+
require 'discordrb'
|
4
|
+
|
5
|
+
bot = Discordrb::Bot.new "email@example.com", "hunter2"
|
6
|
+
|
7
|
+
bot.mention do |event|
|
8
|
+
event.user.pm("You have mentioned me!")
|
9
|
+
end
|
10
|
+
|
11
|
+
bot.run
|
data/lib/discordrb.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
require "discordrb/version"
|
2
|
-
require "discordrb/bot"
|
3
|
-
|
4
|
-
module Discordrb
|
5
|
-
# Your code goes here...
|
6
|
-
end
|
1
|
+
require "discordrb/version"
|
2
|
+
require "discordrb/bot"
|
3
|
+
|
4
|
+
module Discordrb
|
5
|
+
# Your code goes here...
|
6
|
+
end
|
data/lib/discordrb/bot.rb
CHANGED
@@ -1,342 +1,525 @@
|
|
1
|
-
require 'rest-client'
|
2
|
-
require 'faye/websocket'
|
3
|
-
require 'eventmachine'
|
4
|
-
|
5
|
-
require 'discordrb/endpoints/endpoints'
|
6
|
-
|
7
|
-
require 'discordrb/events/message'
|
8
|
-
require 'discordrb/events/typing'
|
9
|
-
require 'discordrb/events/lifetime'
|
10
|
-
require 'discordrb/events/presence'
|
11
|
-
|
12
|
-
require 'discordrb/
|
13
|
-
require 'discordrb/
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
@
|
37
|
-
|
38
|
-
@
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
data
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
@
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
|
104
|
-
def
|
105
|
-
|
106
|
-
end
|
107
|
-
|
108
|
-
def
|
109
|
-
register_event(
|
110
|
-
end
|
111
|
-
|
112
|
-
def
|
113
|
-
register_event(
|
114
|
-
end
|
115
|
-
|
116
|
-
def
|
117
|
-
register_event(
|
118
|
-
end
|
119
|
-
|
120
|
-
def
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
#
|
135
|
-
#
|
136
|
-
def
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
def
|
153
|
-
|
154
|
-
end
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
1
|
+
require 'rest-client'
|
2
|
+
require 'faye/websocket'
|
3
|
+
require 'eventmachine'
|
4
|
+
|
5
|
+
require 'discordrb/endpoints/endpoints'
|
6
|
+
|
7
|
+
require 'discordrb/events/message'
|
8
|
+
require 'discordrb/events/typing'
|
9
|
+
require 'discordrb/events/lifetime'
|
10
|
+
require 'discordrb/events/presence'
|
11
|
+
require 'discordrb/events/voice-state-update'
|
12
|
+
require 'discordrb/events/channel-create'
|
13
|
+
require 'discordrb/events/channel-update'
|
14
|
+
require 'discordrb/events/channel-delete'
|
15
|
+
require 'discordrb/events/guild-member-update'
|
16
|
+
require 'discordrb/events/guild-role-create'
|
17
|
+
require 'discordrb/events/guild-role-delete'
|
18
|
+
require 'discordrb/events/guild-role-update'
|
19
|
+
|
20
|
+
require 'discordrb/exceptions'
|
21
|
+
require 'discordrb/data'
|
22
|
+
|
23
|
+
module Discordrb
|
24
|
+
class Bot
|
25
|
+
include Discordrb::Events
|
26
|
+
def initialize(email, password, debug = false)
|
27
|
+
# Make sure people replace the login details in the example files...
|
28
|
+
if email.end_with? "example.com"
|
29
|
+
puts "You have to replace the login details in the example files with your own!"
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
|
33
|
+
@debug = debug
|
34
|
+
|
35
|
+
@email = email
|
36
|
+
@password = password
|
37
|
+
|
38
|
+
@token = login
|
39
|
+
|
40
|
+
@event_handlers = {}
|
41
|
+
|
42
|
+
@channels = {}
|
43
|
+
@users = {}
|
44
|
+
end
|
45
|
+
|
46
|
+
def run
|
47
|
+
# Handle heartbeats
|
48
|
+
@heartbeat_interval = 1
|
49
|
+
@heartbeat_active = false
|
50
|
+
@heartbeat_thread = Thread.new do
|
51
|
+
while true do
|
52
|
+
sleep @heartbeat_interval
|
53
|
+
send_heartbeat if @heartbeat_active
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
while true do
|
58
|
+
websocket_connect
|
59
|
+
debug("Disconnected! Attempting to reconnect in 5 seconds.")
|
60
|
+
sleep 5
|
61
|
+
@token = login
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def channel(id)
|
66
|
+
debug("Obtaining data for channel with id #{id}")
|
67
|
+
return @channels[id] if @channels[id]
|
68
|
+
|
69
|
+
response = RestClient.get Discordrb::Endpoints::CHANNELS + "/#{id}", {:Authorization => @token}
|
70
|
+
channel = Channel.new(JSON.parse(response), self)
|
71
|
+
@channels[id] = channel
|
72
|
+
end
|
73
|
+
|
74
|
+
def private_channel(id)
|
75
|
+
debug("Creating private channel with user id #{id}")
|
76
|
+
return @private_channels[id] if @private_channels[id]
|
77
|
+
|
78
|
+
data = {
|
79
|
+
'recipient_id' => id
|
80
|
+
}
|
81
|
+
|
82
|
+
response = RestClient.post Discordrb::Endpoints::USERS + "/#{@bot_user.id}/channels", data.to_json, {:Authorization => @token, :content_type => :json}
|
83
|
+
channel = Channel.new(JSON.parse(response), self)
|
84
|
+
@private_channels[id] = channel
|
85
|
+
end
|
86
|
+
|
87
|
+
def user(id)
|
88
|
+
@users[id]
|
89
|
+
end
|
90
|
+
|
91
|
+
def server(id)
|
92
|
+
@servers[id]
|
93
|
+
end
|
94
|
+
|
95
|
+
def send_message(channel_id, content)
|
96
|
+
debug("Sending message to #{channel_id} with content '#{content}'")
|
97
|
+
data = {
|
98
|
+
'content' => content.to_s,
|
99
|
+
'mentions' => []
|
100
|
+
}
|
101
|
+
RestClient.post Discordrb::Endpoints::CHANNELS + "/#{channel_id}/messages", data.to_json, {:Authorization => @token, :content_type => :json}
|
102
|
+
end
|
103
|
+
|
104
|
+
def debug=(debug)
|
105
|
+
@debug = debug
|
106
|
+
end
|
107
|
+
|
108
|
+
def message(attributes = {}, &block)
|
109
|
+
register_event(MessageEvent, attributes, block)
|
110
|
+
end
|
111
|
+
|
112
|
+
def ready(attributes = {}, &block)
|
113
|
+
register_event(ReadyEvent, attributes, block)
|
114
|
+
end
|
115
|
+
|
116
|
+
def disconnected(attributes = {}, &block)
|
117
|
+
register_event(DisconnectEvent, attributes, block)
|
118
|
+
end
|
119
|
+
|
120
|
+
def typing(attributes = {}, &block)
|
121
|
+
register_event(TypingEvent, attributes, block)
|
122
|
+
end
|
123
|
+
|
124
|
+
def presence(attributes = {}, &block)
|
125
|
+
register_event(PresenceEvent, attributes, block)
|
126
|
+
end
|
127
|
+
|
128
|
+
def mention(attributes = {}, &block)
|
129
|
+
register_event(MentionEvent, attributes, block)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Handle channel creation
|
133
|
+
# Attributes:
|
134
|
+
# * type: Channel type ('text' or 'voice')
|
135
|
+
# * name: Channel name
|
136
|
+
def channel_create(attributes = {}, &block)
|
137
|
+
register_event(ChannelCreateEvent, attributes, block)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Handle channel update
|
141
|
+
# Attributes:
|
142
|
+
# * type: Channel type ('text' or 'voice')
|
143
|
+
# * name: Channel name
|
144
|
+
def channel_update(attributes = {}, &block)
|
145
|
+
register_event(ChannelUpdateEvent, attributes, block)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Handle channel deletion
|
149
|
+
# Attributes:
|
150
|
+
# * type: Channel type ('text' or 'voice')
|
151
|
+
# * name: Channel name
|
152
|
+
def channel_delete(attributes = {}, &block)
|
153
|
+
register_event(ChannelDeleteEvent, attributes, block)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Handle a change to a voice state.
|
157
|
+
# This includes joining a voice channel or changing mute or deaf state.
|
158
|
+
# Attributes:
|
159
|
+
# * from: User whose voice state changed
|
160
|
+
# * mute: server mute status
|
161
|
+
# * deaf: server deaf status
|
162
|
+
# * self_mute: self mute status
|
163
|
+
# * self_deaf: self deaf status
|
164
|
+
# * channel: channel the user joined
|
165
|
+
def voice_state_update(attributes = {}, &block)
|
166
|
+
register_event(VoiceStateUpdateEvent, attributes, block)
|
167
|
+
end
|
168
|
+
|
169
|
+
def remove_handler(handler)
|
170
|
+
clazz = event_class(handler.class)
|
171
|
+
@event_handlers[clazz].delete(handler)
|
172
|
+
end
|
173
|
+
|
174
|
+
def add_handler(handler)
|
175
|
+
clazz = event_class(handler.class)
|
176
|
+
@event_handlers[clazz] << handler
|
177
|
+
end
|
178
|
+
|
179
|
+
alias_method :<<, :add_handler
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
# Internal handler for PRESENCE_UPDATE
|
184
|
+
def update_presence(data)
|
185
|
+
user_id = data['user']['id'].to_i
|
186
|
+
server_id = data['guild_id'].to_i
|
187
|
+
server = @servers[server_id]
|
188
|
+
return if !server
|
189
|
+
|
190
|
+
user = @users[user_id]
|
191
|
+
if !user
|
192
|
+
user = User.new(data['user'], self)
|
193
|
+
@users[user_id] = user
|
194
|
+
end
|
195
|
+
|
196
|
+
status = data['status'].to_sym
|
197
|
+
if status != :offline
|
198
|
+
if !(server.members.find {|u| u.id == user.id })
|
199
|
+
server.members << user
|
200
|
+
end
|
201
|
+
end
|
202
|
+
user.status = status
|
203
|
+
user.game_id = data['game_id']
|
204
|
+
end
|
205
|
+
|
206
|
+
# Internal handler for VOICE_STATUS_UPDATE
|
207
|
+
def update_voice_state(data)
|
208
|
+
user_id = data['user_id'].to_i
|
209
|
+
server_id = data['guild_id'].to_i
|
210
|
+
server = @servers[server_id]
|
211
|
+
return if !server
|
212
|
+
|
213
|
+
user = @users[user_id]
|
214
|
+
user.server_mute = data['mute']
|
215
|
+
user.server_deaf = data['deaf']
|
216
|
+
user.self_mute = data['self_mute']
|
217
|
+
user.self_deaf = data['self_deaf']
|
218
|
+
|
219
|
+
channel_id = data['channel_id']
|
220
|
+
channel = nil
|
221
|
+
if channel_id
|
222
|
+
channel = @channels[channel_id.to_i]
|
223
|
+
end
|
224
|
+
user.move(channel)
|
225
|
+
end
|
226
|
+
|
227
|
+
# Internal handler for CHANNEL_CREATE
|
228
|
+
def create_channel(data)
|
229
|
+
channel = Channel.new(data, self)
|
230
|
+
server = channel.server
|
231
|
+
server.channels << channel
|
232
|
+
@channels[channel.id] = channel
|
233
|
+
end
|
234
|
+
|
235
|
+
# Internal handler for CHANNEL_UPDATE
|
236
|
+
def update_channel(data)
|
237
|
+
channel = Channel.new(data, self)
|
238
|
+
server = channel.server
|
239
|
+
old_channel = @channels[channel.id]
|
240
|
+
return if !old_channel
|
241
|
+
old_channel.update_from(channel)
|
242
|
+
end
|
243
|
+
|
244
|
+
# Internal handler for CHANNEL_DELETE
|
245
|
+
def delete_channel(data)
|
246
|
+
channel = Channel.new(data, self)
|
247
|
+
server = channel.server
|
248
|
+
@channels[channel.id] = nil
|
249
|
+
server.channels.reject! {|c| c.id == channel.id}
|
250
|
+
end
|
251
|
+
|
252
|
+
# Internal handler for GUILD_MEMBER_UPDATE
|
253
|
+
def update_guild_member(data)
|
254
|
+
user_data = data['user']
|
255
|
+
server_id = data['guild_id'].to_i
|
256
|
+
roles = []
|
257
|
+
data['roles'].each do |element|
|
258
|
+
role_id = element.to_i
|
259
|
+
roles << @servers[server_id].roles.find {|r| r.id == role_id}
|
260
|
+
end
|
261
|
+
user_id = user_data['id'].to_i
|
262
|
+
user = @users[user_id]
|
263
|
+
user.update_roles(roles)
|
264
|
+
end
|
265
|
+
|
266
|
+
# Internal handler for GUILD_ROLE_UPDATE
|
267
|
+
def update_guild_role(data)
|
268
|
+
role_data = data['role']
|
269
|
+
server_id = data['guild_id'].to_i
|
270
|
+
server = @servers[server_id]
|
271
|
+
new_role = Role.new(role_data, self, server)
|
272
|
+
role_id = role_data['id'].to_i
|
273
|
+
old_role = server.roles.find {|r| r.id == role_id}
|
274
|
+
old_role.update_from(new_role)
|
275
|
+
end
|
276
|
+
|
277
|
+
# Internal handler for GUILD_ROLE_CREATE
|
278
|
+
def create_guild_role(data)
|
279
|
+
role_data = data['role']
|
280
|
+
server_id = data['guild_id'].to_i
|
281
|
+
server = @servers[server_id]
|
282
|
+
new_role = Role.new(role_data, self, server)
|
283
|
+
server.add_role(new_role)
|
284
|
+
end
|
285
|
+
|
286
|
+
# Internal handler for GUILD_ROLE_DELETE
|
287
|
+
def delete_guild_role(data)
|
288
|
+
role_data = data['role']
|
289
|
+
role_id = role_data['id'].to_i
|
290
|
+
server_id = data['guild_id'].to_i
|
291
|
+
server = @servers[server_id]
|
292
|
+
server.delete_role(role_id)
|
293
|
+
end
|
294
|
+
|
295
|
+
def debug(message)
|
296
|
+
puts "[DEBUG @ #{Time.now.to_s}] #{message}" if @debug
|
297
|
+
end
|
298
|
+
|
299
|
+
def login
|
300
|
+
debug("Logging in")
|
301
|
+
login_attempts = login_attempts || 0
|
302
|
+
|
303
|
+
# Login
|
304
|
+
login_response = RestClient.post Discordrb::Endpoints::LOGIN, :email => @email, :password => @password
|
305
|
+
raise HTTPStatusException.new(login_response.code) if login_response.code >= 400
|
306
|
+
|
307
|
+
# Parse response
|
308
|
+
login_response_object = JSON.parse(login_response)
|
309
|
+
raise InvalidAuthenticationException unless login_response_object['token']
|
310
|
+
|
311
|
+
debug("Received token: #{login_response_object['token']}")
|
312
|
+
login_response_object['token']
|
313
|
+
rescue Exception => e
|
314
|
+
response_code = login_response.nil? ? 0 : login_response.code ######## mackmm145
|
315
|
+
if login_attempts < 100 && (e.inspect.include?("No such host is known.") || response_code == 523)
|
316
|
+
debug("Login failed! Reattempting in 5 seconds. #{100 - login_attempts} attempts remaining.")
|
317
|
+
debug("Error was: #{e.inspect}")
|
318
|
+
sleep 5
|
319
|
+
login_attempts += 1
|
320
|
+
retry
|
321
|
+
else
|
322
|
+
debug("Login failed permanently after #{login_attempts + 1} attempts")
|
323
|
+
|
324
|
+
# Apparently we get a 400 if the password or username is incorrect. In that case, tell the user
|
325
|
+
debug("Are you sure you're using the correct username and password?") if e.class == RestClient::BadRequest
|
326
|
+
raise $!
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
def get_gateway
|
331
|
+
# Get updated websocket_hub
|
332
|
+
response = RestClient.get Discordrb::Endpoints::GATEWAY, :authorization => @token
|
333
|
+
JSON.parse(response)["url"]
|
334
|
+
end
|
335
|
+
|
336
|
+
def websocket_connect
|
337
|
+
debug("Attempting to get gateway URL...")
|
338
|
+
websocket_hub = get_gateway
|
339
|
+
debug("Success! Gateway URL is #{websocket_hub}.")
|
340
|
+
debug("Now running bot")
|
341
|
+
|
342
|
+
EM.run {
|
343
|
+
@ws = Faye::WebSocket::Client.new(websocket_hub)
|
344
|
+
|
345
|
+
@ws.on :open do |event|; websocket_open(event); end
|
346
|
+
@ws.on :message do |event|; websocket_message(event); end
|
347
|
+
@ws.on :error do |event|; debug(event.message); end
|
348
|
+
@ws.on :close do |event|; websocket_close(event); @ws = nil; end
|
349
|
+
}
|
350
|
+
end
|
351
|
+
|
352
|
+
def websocket_message(event)
|
353
|
+
begin
|
354
|
+
debug("Received packet #{event.data}")
|
355
|
+
|
356
|
+
# Parse packet
|
357
|
+
packet = JSON.parse(event.data)
|
358
|
+
|
359
|
+
raise "Invalid Packet" unless packet['op'] == 0 # TODO
|
360
|
+
|
361
|
+
data = packet['d']
|
362
|
+
case packet['t']
|
363
|
+
when "READY"
|
364
|
+
# Activate the heartbeats
|
365
|
+
@heartbeat_interval = data['heartbeat_interval'].to_f / 1000.0
|
366
|
+
@heartbeat_active = true
|
367
|
+
debug("Desired heartbeat_interval: #{@heartbeat_interval}")
|
368
|
+
|
369
|
+
bot_user_id = data['user']['id'].to_i
|
370
|
+
|
371
|
+
# Initialize servers
|
372
|
+
@servers = {}
|
373
|
+
data['guilds'].each do |element|
|
374
|
+
server = Server.new(element, self)
|
375
|
+
@servers[server.id] = server
|
376
|
+
|
377
|
+
# Initialize users
|
378
|
+
server.members.each do |element|
|
379
|
+
@users[element.id] = element
|
380
|
+
end
|
381
|
+
|
382
|
+
# Save the bot user
|
383
|
+
@bot_user = @users[bot_user_id]
|
384
|
+
end
|
385
|
+
|
386
|
+
# Add private channels
|
387
|
+
@private_channels = {}
|
388
|
+
data['private_channels'].each do |element|
|
389
|
+
channel = Channel.new(element, self)
|
390
|
+
@channels[channel.id] = channel
|
391
|
+
@private_channels[channel.recipient.id] = channel
|
392
|
+
end
|
393
|
+
|
394
|
+
# Make sure to raise the event
|
395
|
+
raise_event(ReadyEvent.new)
|
396
|
+
when "MESSAGE_CREATE"
|
397
|
+
message = Message.new(data, self)
|
398
|
+
event = MessageEvent.new(message, self)
|
399
|
+
raise_event(event)
|
400
|
+
|
401
|
+
if message.mentions.any? { |user| user.id == @bot_user.id }
|
402
|
+
event = MentionEvent.new(message, self)
|
403
|
+
raise_event(event)
|
404
|
+
end
|
405
|
+
when "TYPING_START"
|
406
|
+
event = TypingEvent.new(data, self)
|
407
|
+
raise_event(event)
|
408
|
+
when "PRESENCE_UPDATE"
|
409
|
+
update_presence(data)
|
410
|
+
event = PresenceEvent.new(data, self)
|
411
|
+
raise_event(event)
|
412
|
+
when "VOICE_STATE_UPDATE"
|
413
|
+
update_voice_state(data)
|
414
|
+
event = VoiceStateUpdateEvent.new(data, self)
|
415
|
+
raise_event(event)
|
416
|
+
when "CHANNEL_CREATE"
|
417
|
+
create_channel(data)
|
418
|
+
event = ChannelCreateEvent.new(data, self)
|
419
|
+
raise_event(event)
|
420
|
+
when "CHANNEL_UPDATE"
|
421
|
+
update_channel(data)
|
422
|
+
event = ChannelUpdateEvent.new(data, self)
|
423
|
+
raise_event(event)
|
424
|
+
when "CHANNEL_DELETE"
|
425
|
+
delete_channel(data)
|
426
|
+
event = ChannelDeleteEvent.new(data, self)
|
427
|
+
raise_event(event)
|
428
|
+
when "GUILD_MEMBER_UPDATE"
|
429
|
+
update_guild_member(data)
|
430
|
+
event = GuildMemberUpdateEvent.new(data, self)
|
431
|
+
raise_event(event)
|
432
|
+
when "GUILD_ROLE_UPDATE"
|
433
|
+
update_guild_role(data)
|
434
|
+
event = GuildRoleUpdateEvent.new(data, self)
|
435
|
+
raise_event(event)
|
436
|
+
when "GUILD_ROLE_CREATE"
|
437
|
+
create_guild_role(data)
|
438
|
+
event = GuildRoleCreateEvent.new(data, self)
|
439
|
+
raise_event(event)
|
440
|
+
when "GUILD_ROLE_DELETE"
|
441
|
+
delete_guild_role(data)
|
442
|
+
event = GuildRoleDeleteEvent.new(data, self)
|
443
|
+
raise_event(event)
|
444
|
+
end
|
445
|
+
rescue Exception => e
|
446
|
+
debug("Exception: #{e.inspect}")
|
447
|
+
e.backtrace.each {|line| debug(line) }
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
def websocket_close(event)
|
452
|
+
debug("Disconnected from WebSocket!")
|
453
|
+
debug(" (Reason: #{event.reason})")
|
454
|
+
debug(" (Code: #{event.code})")
|
455
|
+
raise_event(DisconnectEvent.new)
|
456
|
+
EM.stop
|
457
|
+
end
|
458
|
+
|
459
|
+
def websocket_open(event)
|
460
|
+
# Send the initial packet
|
461
|
+
packet = {
|
462
|
+
"op" => 2, # Packet identifier
|
463
|
+
"d" => { # Packet data
|
464
|
+
"v" => 2, # Another identifier
|
465
|
+
"token" => @token,
|
466
|
+
"properties" => { # I'm unsure what these values are for exactly, but they don't appear to impact bot functionality in any way.
|
467
|
+
"$os" => "Linux",
|
468
|
+
"$browser" => "",
|
469
|
+
"$device" => "discordrb",
|
470
|
+
"$referrer" => "",
|
471
|
+
"$referring_domain" => ""
|
472
|
+
}
|
473
|
+
}
|
474
|
+
}
|
475
|
+
|
476
|
+
@ws.send(packet.to_json)
|
477
|
+
end
|
478
|
+
|
479
|
+
def raise_event(event)
|
480
|
+
debug("Raised a #{event.class}")
|
481
|
+
handlers = @event_handlers[event.class]
|
482
|
+
(handlers || []).each do |handler|
|
483
|
+
handler.match(event)
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
def register_event(clazz, attributes, block)
|
488
|
+
handler = handler_class(clazz).new(attributes, block)
|
489
|
+
|
490
|
+
@event_handlers[clazz] ||= []
|
491
|
+
@event_handlers[clazz] << handler
|
492
|
+
|
493
|
+
# Return the handler so it can be removed later
|
494
|
+
handler
|
495
|
+
end
|
496
|
+
|
497
|
+
def send_heartbeat
|
498
|
+
millis = Time.now.strftime("%s%L").to_i
|
499
|
+
debug("Sending heartbeat at #{millis}")
|
500
|
+
data = {
|
501
|
+
'op' => 1,
|
502
|
+
'd' => millis
|
503
|
+
}
|
504
|
+
|
505
|
+
@ws.send(data.to_json)
|
506
|
+
end
|
507
|
+
|
508
|
+
def class_from_string(str)
|
509
|
+
str.split('::').inject(Object) do |mod, class_name|
|
510
|
+
mod.const_get(class_name)
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
def event_class(handler_class)
|
515
|
+
class_name = handler_class.to_s
|
516
|
+
return nil unless class_name.end_with? "Handler"
|
517
|
+
|
518
|
+
class_from_string(class_name[0..-8])
|
519
|
+
end
|
520
|
+
|
521
|
+
def handler_class(event_class)
|
522
|
+
class_from_string(event_class.to_s + "Handler")
|
523
|
+
end
|
524
|
+
end
|
525
|
+
end
|