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 +4 -4
- data/.codeclimate.yml +16 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +9 -0
- data/.travis.yml +2 -1
- data/CHANGELOG.md +49 -0
- data/CONTRIBUTING.md +13 -0
- data/Gemfile +6 -1
- data/README.md +2 -0
- data/Rakefile +12 -1
- data/discordrb-webhooks.gemspec +25 -0
- data/discordrb.gemspec +6 -4
- data/lib/discordrb/api.rb +5 -0
- data/lib/discordrb/api/channel.rb +74 -6
- data/lib/discordrb/api/server.rb +37 -0
- data/lib/discordrb/bot.rb +116 -29
- data/lib/discordrb/commands/command_bot.rb +93 -9
- data/lib/discordrb/commands/container.rb +3 -0
- data/lib/discordrb/commands/parser.rb +45 -29
- data/lib/discordrb/commands/rate_limiter.rb +7 -9
- data/lib/discordrb/container.rb +126 -26
- data/lib/discordrb/data.rb +264 -36
- data/lib/discordrb/events/generic.rb +18 -2
- data/lib/discordrb/events/guilds.rb +117 -2
- data/lib/discordrb/events/message.rb +7 -5
- data/lib/discordrb/events/presence.rb +4 -12
- data/lib/discordrb/events/raw.rb +49 -0
- data/lib/discordrb/events/reactions.rb +104 -0
- data/lib/discordrb/events/typing.rb +4 -2
- data/lib/discordrb/events/voice_state_update.rb +17 -2
- data/lib/discordrb/gateway.rb +75 -17
- data/lib/discordrb/permissions.rb +5 -3
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +2 -5
- data/lib/discordrb/voice/network.rb +2 -2
- data/lib/discordrb/voice/voice_bot.rb +5 -6
- data/lib/discordrb/webhooks.rb +12 -0
- metadata +35 -15
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'active_support/core_ext/module'
|
4
|
-
|
5
3
|
# Events used by discordrb
|
6
4
|
module Discordrb::Events
|
7
5
|
# A negated object, used to not match something in event parameters.
|
@@ -55,6 +53,24 @@ module Discordrb::Events
|
|
55
53
|
class Event
|
56
54
|
# @return [Bot] the bot used to initialize this event.
|
57
55
|
attr_reader :bot
|
56
|
+
|
57
|
+
class << self
|
58
|
+
protected
|
59
|
+
|
60
|
+
# Delegates a list of methods to a particular object. This is essentially a reimplementation of ActiveSupport's
|
61
|
+
# `#delegate`, but without the overhead provided by the rest. Used in subclasses of `Event` to delegate properties
|
62
|
+
# on events to properties on data objects.
|
63
|
+
# @param methods [Array<Symbol>] The methods to delegate.
|
64
|
+
# @param hash [Hash<Symbol => Symbol>] A hash with one `:to` key and the value the method to be delegated to.
|
65
|
+
def delegate(*methods, hash)
|
66
|
+
methods.each do |e|
|
67
|
+
define_method(e) do
|
68
|
+
object = __send__(hash[:to])
|
69
|
+
object.__send__(e)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
58
74
|
end
|
59
75
|
|
60
76
|
# Generic event handler that can be extended
|
@@ -32,7 +32,7 @@ module Discordrb::Events
|
|
32
32
|
matches_all(@attributes[:server], event.server) do |a, e|
|
33
33
|
a == if a.is_a? String
|
34
34
|
e.name
|
35
|
-
elsif a.is_a?
|
35
|
+
elsif a.is_a? Integer
|
36
36
|
e.id
|
37
37
|
else
|
38
38
|
e
|
@@ -59,7 +59,7 @@ module Discordrb::Events
|
|
59
59
|
# Server is deleted
|
60
60
|
# @see Discordrb::EventContainer#server_delete
|
61
61
|
class ServerDeleteEvent < ServerEvent
|
62
|
-
#
|
62
|
+
# Override init_server to account for the deleted server
|
63
63
|
def init_server(data, bot)
|
64
64
|
@server = Discordrb::Server.new(data, bot, false)
|
65
65
|
end
|
@@ -67,4 +67,119 @@ module Discordrb::Events
|
|
67
67
|
|
68
68
|
# Event handler for {ServerDeleteEvent}
|
69
69
|
class ServerDeleteEventHandler < ServerEventHandler; end
|
70
|
+
|
71
|
+
# Emoji is created/deleted/updated
|
72
|
+
class ServerEmojiChangeEvent < ServerEvent
|
73
|
+
# @return [Server] the server in question.
|
74
|
+
attr_reader :server
|
75
|
+
|
76
|
+
# @return [Array<Emoji>] array of emojis.
|
77
|
+
attr_reader :emoji
|
78
|
+
|
79
|
+
def initialize(server, data, bot)
|
80
|
+
@bot = bot
|
81
|
+
@server = server
|
82
|
+
process_emoji(data)
|
83
|
+
end
|
84
|
+
|
85
|
+
def process_emoji(data)
|
86
|
+
@emoji = data['emojis'].map do |e|
|
87
|
+
@server.emoji[e['id']]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Generic event helper for when an emoji is either created or deleted
|
93
|
+
class ServerEmojiCDEvent < ServerEvent
|
94
|
+
# @return [Server] the server in question.
|
95
|
+
attr_reader :server
|
96
|
+
|
97
|
+
# @return [Emoji] the emoji data.
|
98
|
+
attr_reader :emoji
|
99
|
+
|
100
|
+
def initialize(server, emoji, bot)
|
101
|
+
@bot = bot
|
102
|
+
@emoji = emoji
|
103
|
+
@server = server
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Emoji is created
|
108
|
+
class ServerEmojiCreateEvent < ServerEmojiCDEvent; end
|
109
|
+
|
110
|
+
# Emoji is deleted
|
111
|
+
class ServerEmojiDeleteEvent < ServerEmojiCDEvent; end
|
112
|
+
|
113
|
+
# Emoji is updated
|
114
|
+
class ServerEmojiUpdateEvent < ServerEvent
|
115
|
+
# @return [Server] the server in question.
|
116
|
+
attr_reader :server
|
117
|
+
|
118
|
+
# @return [Emoji, nil] the emoji data before the event.
|
119
|
+
attr_reader :old_emoji
|
120
|
+
|
121
|
+
# @return [Emoji, nil] the updated emoji data.
|
122
|
+
attr_reader :emoji
|
123
|
+
|
124
|
+
def initialize(server, old_emoji, emoji, bot)
|
125
|
+
@bot = bot
|
126
|
+
@old_emoji = old_emoji
|
127
|
+
@emoji = emoji
|
128
|
+
@server = server
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Event handler for {ServerEmojiChangeEvent}
|
133
|
+
class ServerEmojiChangeEventHandler < ServerEventHandler; end
|
134
|
+
|
135
|
+
# Generic handler for emoji create and delete
|
136
|
+
class ServerEmojiCDEventHandler < ServerEventHandler
|
137
|
+
def matches?(event)
|
138
|
+
# Check for the proper event type
|
139
|
+
return false unless event.is_a? ServerEmojiCDEvent
|
140
|
+
|
141
|
+
[
|
142
|
+
matches_all(@attributes[:server], event.server) do |a, e|
|
143
|
+
a == if a.is_a? String
|
144
|
+
e.name
|
145
|
+
elsif a.is_a? Integer
|
146
|
+
e.id
|
147
|
+
else
|
148
|
+
e
|
149
|
+
end
|
150
|
+
end,
|
151
|
+
matches_all(@attributes[:id], event.emoji.id) { |a, e| a.resolve_id == e.resolve_id },
|
152
|
+
matches_all(@attributes[:name], event.emoji.name) { |a, e| a == e }
|
153
|
+
].reduce(true, &:&)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Event handler for {ServerEmojiCreateEvent}
|
158
|
+
class ServerEmojiCreateEventHandler < ServerEmojiCDEventHandler; end
|
159
|
+
|
160
|
+
# Event handler for {ServerEmojiDeleteEvent}
|
161
|
+
class ServerEmojiDeleteEventHandler < ServerEmojiCDEventHandler; end
|
162
|
+
|
163
|
+
# Event handler for {ServerEmojiUpdateEvent}
|
164
|
+
class ServerEmojiUpdateEventHandler < EventHandler
|
165
|
+
def matches?(event)
|
166
|
+
# Check for the proper event type
|
167
|
+
return false unless event.is_a? ServerEmojiUpdateEvent
|
168
|
+
|
169
|
+
[
|
170
|
+
matches_all(@attributes[:server], event.server) do |a, e|
|
171
|
+
a == if a.is_a? String
|
172
|
+
e.name
|
173
|
+
elsif a.is_a? Integer
|
174
|
+
e.id
|
175
|
+
else
|
176
|
+
e
|
177
|
+
end
|
178
|
+
end,
|
179
|
+
matches_all(@attributes[:id], event.old_emoji.id) { |a, e| a.resolve_id == e.resolve_id },
|
180
|
+
matches_all(@attributes[:old_name], event.old_emoji.name) { |a, e| a == e },
|
181
|
+
matches_all(@attributes[:name], event.emoji.name) { |a, e| a == e }
|
182
|
+
].reduce(true, &:&)
|
183
|
+
end
|
184
|
+
end
|
70
185
|
end
|
@@ -14,14 +14,14 @@ module Discordrb::Events
|
|
14
14
|
# @param content [String] The message to send to the channel
|
15
15
|
# @return [Discordrb::Message] the message that was sent
|
16
16
|
def send_message(content)
|
17
|
-
|
17
|
+
channel.send_message(content)
|
18
18
|
end
|
19
19
|
|
20
20
|
# Sends a temporary message to the channel this message was sent in, right now.
|
21
21
|
# @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
|
22
22
|
# @param timeout [Float] The amount of time in seconds after which the message sent will be deleted.
|
23
23
|
def send_temporary_message(content, timeout)
|
24
|
-
|
24
|
+
channel.send_temporary_message(content, timeout)
|
25
25
|
end
|
26
26
|
|
27
27
|
# Adds a string to be sent after the event has finished execution. Avoids problems with rate limiting because only
|
@@ -62,6 +62,7 @@ module Discordrb::Events
|
|
62
62
|
# Event raised when a text message is sent to a channel
|
63
63
|
class MessageEvent < Event
|
64
64
|
include Respondable
|
65
|
+
|
65
66
|
# @return [Message] the message which triggered this event.
|
66
67
|
attr_reader :message
|
67
68
|
|
@@ -72,7 +73,7 @@ module Discordrb::Events
|
|
72
73
|
attr_reader :file
|
73
74
|
|
74
75
|
# @!attribute [r] author
|
75
|
-
# @return [Member] who sent this message.
|
76
|
+
# @return [Member, User] who sent this message.
|
76
77
|
# @see Message#author
|
77
78
|
# @!attribute [r] channel
|
78
79
|
# @return [Channel] the channel in which this message was sent.
|
@@ -178,7 +179,7 @@ module Discordrb::Events
|
|
178
179
|
if a.is_a? String
|
179
180
|
# Make sure to remove the "#" from channel names in case it was specified
|
180
181
|
a.delete('#') == e.name
|
181
|
-
elsif a.is_a?
|
182
|
+
elsif a.is_a? Integer
|
182
183
|
a == e.id
|
183
184
|
else
|
184
185
|
a == e
|
@@ -187,7 +188,7 @@ module Discordrb::Events
|
|
187
188
|
matches_all(@attributes[:from], event.author) do |a, e|
|
188
189
|
if a.is_a? String
|
189
190
|
a == e.name
|
190
|
-
elsif a.is_a?
|
191
|
+
elsif a.is_a? Integer
|
191
192
|
a == e.id
|
192
193
|
elsif a == :bot
|
193
194
|
e.current_bot?
|
@@ -234,6 +235,7 @@ module Discordrb::Events
|
|
234
235
|
# A subset of MessageEvent that only contains a message ID and a channel
|
235
236
|
class MessageIDEvent < Event
|
236
237
|
include Respondable
|
238
|
+
|
237
239
|
# @return [Integer] the ID associated with this event
|
238
240
|
attr_reader :id
|
239
241
|
|
@@ -34,7 +34,7 @@ module Discordrb::Events
|
|
34
34
|
matches_all(@attributes[:from], event.user) do |a, e|
|
35
35
|
a == if a.is_a? String
|
36
36
|
e.name
|
37
|
-
elsif a.is_a?
|
37
|
+
elsif a.is_a? Integer
|
38
38
|
e.id
|
39
39
|
else
|
40
40
|
e
|
@@ -89,25 +89,17 @@ module Discordrb::Events
|
|
89
89
|
matches_all(@attributes[:from], event.user) do |a, e|
|
90
90
|
a == if a.is_a? String
|
91
91
|
e.name
|
92
|
-
elsif a.is_a?
|
92
|
+
elsif a.is_a? Integer
|
93
93
|
e.id
|
94
94
|
else
|
95
95
|
e
|
96
96
|
end
|
97
97
|
end,
|
98
98
|
matches_all(@attributes[:game], event.game) do |a, e|
|
99
|
-
a ==
|
100
|
-
e.name
|
101
|
-
else
|
102
|
-
e
|
103
|
-
end
|
99
|
+
a == e
|
104
100
|
end,
|
105
101
|
matches_all(@attributes[:type], event.type) do |a, e|
|
106
|
-
a ==
|
107
|
-
e.type
|
108
|
-
else
|
109
|
-
e
|
110
|
-
end
|
102
|
+
a == e
|
111
103
|
end
|
112
104
|
].reduce(true, &:&)
|
113
105
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'discordrb/events/generic'
|
4
|
+
|
5
|
+
# Event classes and handlers
|
6
|
+
module Discordrb::Events
|
7
|
+
# Event raised when any dispatch is received
|
8
|
+
class RawEvent < Event
|
9
|
+
# @return [Symbol] the type of this dispatch.
|
10
|
+
attr_reader :type
|
11
|
+
alias_method :t, :type
|
12
|
+
|
13
|
+
# @return [Hash] the data of this dispatch.
|
14
|
+
attr_reader :data
|
15
|
+
alias_method :d, :data
|
16
|
+
|
17
|
+
def initialize(type, data, bot)
|
18
|
+
@type = type
|
19
|
+
@data = data
|
20
|
+
@bot = bot
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Event handler for {RawEvent}
|
25
|
+
class RawEventHandler < EventHandler
|
26
|
+
def matches?(event)
|
27
|
+
# Check for the proper event type
|
28
|
+
return false unless event.is_a? RawEvent
|
29
|
+
|
30
|
+
[
|
31
|
+
matches_all(@attributes[:type] || @attributes[:t], event.type) do |a, e|
|
32
|
+
if a.is_a? Regexp
|
33
|
+
# 24: update to matches?
|
34
|
+
match = a.match(e)
|
35
|
+
match ? (e == match[0]) : false
|
36
|
+
else
|
37
|
+
e.to_s.casecmp(a.to_s).zero?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
].reduce(true, &:&)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Event raised when an unknown dispatch is received
|
45
|
+
class UnknownEvent < RawEvent; end
|
46
|
+
|
47
|
+
# Event handler for {UnknownEvent}
|
48
|
+
class UnknownEventHandler < RawEventHandler; end
|
49
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'discordrb/events/generic'
|
4
|
+
require 'discordrb/data'
|
5
|
+
|
6
|
+
module Discordrb::Events
|
7
|
+
# Generic superclass for events about adding and removing reactions
|
8
|
+
class ReactionEvent
|
9
|
+
include Respondable
|
10
|
+
|
11
|
+
# @return [Emoji] the emoji that was reacted with.
|
12
|
+
attr_reader :emoji
|
13
|
+
|
14
|
+
def initialize(data, bot)
|
15
|
+
@bot = bot
|
16
|
+
|
17
|
+
@emoji = Discordrb::Emoji.new(data['emoji'], bot, nil)
|
18
|
+
@user_id = data['user_id'].to_i
|
19
|
+
@message_id = data['message_id'].to_i
|
20
|
+
@channel_id = data['channel_id'].to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [User] the user that reacted to this message.
|
24
|
+
def user
|
25
|
+
# Cache the user so we don't do requests all the time
|
26
|
+
@user ||= @bot.user(@user_id)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Message] the message that was reacted to.
|
30
|
+
def message
|
31
|
+
@message ||= channel.load_message(@message_id)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Channel] the channel that was reacted in.
|
35
|
+
def channel
|
36
|
+
@channel ||= @bot.channel(@channel_id)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Generic superclass for event handlers pertaining to adding and removing reactions
|
41
|
+
class ReactionEventHandler < EventHandler
|
42
|
+
def matches?(event)
|
43
|
+
# Check for the proper event type
|
44
|
+
return false unless event.is_a? ReactionEvent
|
45
|
+
|
46
|
+
[
|
47
|
+
matches_all(@attributes[:emoji], event.emoji) do |a, e|
|
48
|
+
if a.is_a? Integer
|
49
|
+
e.id == a
|
50
|
+
elsif a.is_a? String
|
51
|
+
e.name == a || e.name == a.delete(':') || e.id == a.resolve_id
|
52
|
+
else
|
53
|
+
e == a
|
54
|
+
end
|
55
|
+
end
|
56
|
+
].reduce(true, &:&)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Event raised when somebody reacts to a message
|
61
|
+
class ReactionAddEvent < ReactionEvent; end
|
62
|
+
|
63
|
+
# Event handler for {ReactionAddEvent}
|
64
|
+
class ReactionAddEventHandler < ReactionEventHandler; end
|
65
|
+
|
66
|
+
# Event raised when somebody removes a reaction to a message
|
67
|
+
class ReactionRemoveEvent < ReactionEvent; end
|
68
|
+
|
69
|
+
# Event handler for {ReactionRemoveEvent}
|
70
|
+
class ReactionRemoveEventHandler < ReactionEventHandler; end
|
71
|
+
|
72
|
+
# Event raised when somebody removes all reactions from a message
|
73
|
+
class ReactionRemoveAllEvent
|
74
|
+
include Respondable
|
75
|
+
|
76
|
+
def initialize(data, bot)
|
77
|
+
@bot = bot
|
78
|
+
|
79
|
+
@message_id = data['message_id'].to_i
|
80
|
+
@channel_id = data['channel_id'].to_i
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [Channel] the channel where the removal occurred.
|
84
|
+
def channel
|
85
|
+
@channel ||= @bot.channel(@channel_id)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return [Message] the message all reactions were removed from.
|
89
|
+
def message
|
90
|
+
@message ||= channel.load_message(@message_id)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Event handler for {ReactionRemoveAllEvent}
|
95
|
+
class ReactionRemoveAllEventHandler < EventHandler
|
96
|
+
def matches?(event)
|
97
|
+
# Check for the proper event type
|
98
|
+
return false unless event.is_a? ReactionRemoveAllEvent
|
99
|
+
|
100
|
+
# No attributes yet as there is no property available on the event that doesn't involve doing a resolution request
|
101
|
+
[].reduce(true, &:&)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -5,6 +5,8 @@ require 'discordrb/events/generic'
|
|
5
5
|
module Discordrb::Events
|
6
6
|
# Event raised when a user starts typing
|
7
7
|
class TypingEvent < Event
|
8
|
+
include Respondable
|
9
|
+
|
8
10
|
# @return [Channel] the channel on which a user started typing.
|
9
11
|
attr_reader :channel
|
10
12
|
|
@@ -45,7 +47,7 @@ module Discordrb::Events
|
|
45
47
|
matches_all(@attributes[:in], event.channel) do |a, e|
|
46
48
|
if a.is_a? String
|
47
49
|
a.delete('#') == e.name
|
48
|
-
elsif a.is_a?
|
50
|
+
elsif a.is_a? Integer
|
49
51
|
a == e.id
|
50
52
|
else
|
51
53
|
a == e
|
@@ -54,7 +56,7 @@ module Discordrb::Events
|
|
54
56
|
matches_all(@attributes[:from], event.user) do |a, e|
|
55
57
|
a == if a.is_a? String
|
56
58
|
e.name
|
57
|
-
elsif a.is_a?
|
59
|
+
elsif a.is_a? Integer
|
58
60
|
e.id
|
59
61
|
else
|
60
62
|
e
|
@@ -8,7 +8,10 @@ module Discordrb::Events
|
|
8
8
|
class VoiceStateUpdateEvent < Event
|
9
9
|
attr_reader :user, :token, :suppress, :session_id, :self_mute, :self_deaf, :mute, :deaf, :server, :channel
|
10
10
|
|
11
|
-
|
11
|
+
# @return [Channel, nil] the old channel this user was on, or nil if the user is newly joining voice.
|
12
|
+
attr_reader :old_channel
|
13
|
+
|
14
|
+
def initialize(data, old_channel_id, bot)
|
12
15
|
@bot = bot
|
13
16
|
|
14
17
|
@token = data['token']
|
@@ -22,6 +25,7 @@ module Discordrb::Events
|
|
22
25
|
return unless @server
|
23
26
|
|
24
27
|
@channel = bot.channel(data['channel_id'].to_i) if data['channel_id']
|
28
|
+
@old_channel = bot.channel(old_channel_id) if old_channel_id
|
25
29
|
@user = bot.user(data['user_id'].to_i)
|
26
30
|
end
|
27
31
|
end
|
@@ -71,9 +75,20 @@ module Discordrb::Events
|
|
71
75
|
end
|
72
76
|
end,
|
73
77
|
matches_all(@attributes[:channel], event.channel) do |a, e|
|
78
|
+
next unless e # Don't bother if the channel is nil
|
79
|
+
a == if a.is_a? String
|
80
|
+
e.name
|
81
|
+
elsif a.is_a? Integer
|
82
|
+
e.id
|
83
|
+
else
|
84
|
+
e
|
85
|
+
end
|
86
|
+
end,
|
87
|
+
matches_all(@attributes[:old_channel], event.old_channel) do |a, e|
|
88
|
+
next unless e # Don't bother if the channel is nil
|
74
89
|
a == if a.is_a? String
|
75
90
|
e.name
|
76
|
-
elsif a.is_a?
|
91
|
+
elsif a.is_a? Integer
|
77
92
|
e.id
|
78
93
|
else
|
79
94
|
e
|