discorb 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +56 -0
  3. data/.yardopts +6 -0
  4. data/Changelog.md +5 -0
  5. data/Gemfile +23 -0
  6. data/Gemfile.lock +70 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +53 -0
  9. data/Rakefile +46 -0
  10. data/bin/console +15 -0
  11. data/bin/setup +8 -0
  12. data/discorb.gemspec +37 -0
  13. data/docs/Examples.md +26 -0
  14. data/docs/events.md +480 -0
  15. data/docs/voice_events.md +283 -0
  16. data/examples/components/authorization_button.rb +43 -0
  17. data/examples/components/select_menu.rb +61 -0
  18. data/examples/extension/main.rb +12 -0
  19. data/examples/extension/message_expander.rb +41 -0
  20. data/examples/simple/eval.rb +32 -0
  21. data/examples/simple/ping_pong.rb +16 -0
  22. data/examples/simple/rolepanel.rb +65 -0
  23. data/examples/simple/wait_for_message.rb +30 -0
  24. data/lib/discorb/application.rb +157 -0
  25. data/lib/discorb/asset.rb +57 -0
  26. data/lib/discorb/audit_logs.rb +323 -0
  27. data/lib/discorb/channel.rb +1101 -0
  28. data/lib/discorb/client.rb +363 -0
  29. data/lib/discorb/color.rb +173 -0
  30. data/lib/discorb/common.rb +123 -0
  31. data/lib/discorb/components.rb +290 -0
  32. data/lib/discorb/dictionary.rb +119 -0
  33. data/lib/discorb/embed.rb +345 -0
  34. data/lib/discorb/emoji.rb +218 -0
  35. data/lib/discorb/emoji_table.rb +3799 -0
  36. data/lib/discorb/error.rb +98 -0
  37. data/lib/discorb/event.rb +35 -0
  38. data/lib/discorb/extend.rb +18 -0
  39. data/lib/discorb/extension.rb +54 -0
  40. data/lib/discorb/file.rb +69 -0
  41. data/lib/discorb/flag.rb +109 -0
  42. data/lib/discorb/gateway.rb +967 -0
  43. data/lib/discorb/gateway_requests.rb +47 -0
  44. data/lib/discorb/guild.rb +1244 -0
  45. data/lib/discorb/guild_template.rb +211 -0
  46. data/lib/discorb/image.rb +43 -0
  47. data/lib/discorb/integration.rb +111 -0
  48. data/lib/discorb/intents.rb +137 -0
  49. data/lib/discorb/interaction.rb +333 -0
  50. data/lib/discorb/internet.rb +285 -0
  51. data/lib/discorb/invite.rb +145 -0
  52. data/lib/discorb/log.rb +70 -0
  53. data/lib/discorb/member.rb +232 -0
  54. data/lib/discorb/message.rb +583 -0
  55. data/lib/discorb/modules.rb +138 -0
  56. data/lib/discorb/permission.rb +270 -0
  57. data/lib/discorb/presence.rb +308 -0
  58. data/lib/discorb/reaction.rb +48 -0
  59. data/lib/discorb/role.rb +189 -0
  60. data/lib/discorb/sticker.rb +157 -0
  61. data/lib/discorb/user.rb +163 -0
  62. data/lib/discorb/utils.rb +16 -0
  63. data/lib/discorb/voice_state.rb +251 -0
  64. data/lib/discorb/webhook.rb +420 -0
  65. data/lib/discorb.rb +51 -0
  66. metadata +120 -0
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ #
5
+ # Error class for Discorb.
6
+ # @abstract
7
+ #
8
+ class DiscorbError < StandardError
9
+ private
10
+
11
+ def enumerate_errors(hash)
12
+ res = {}
13
+ _recr_items([], hash, res)
14
+ res
15
+ end
16
+
17
+ def _recr_items(key, item, res)
18
+ case item
19
+ when Array
20
+ item.each_with_index do |v, i|
21
+ _recr_items (key + [i]), v, res
22
+ end
23
+ when Hash
24
+ item.each do |k, v|
25
+ _recr_items (key + [k]), v, res
26
+ end
27
+ else
28
+ res[key.join(".").gsub("_errors.", "")] = item
29
+ end
30
+ end
31
+ end
32
+
33
+ #
34
+ # Represents a HTTP error.
35
+ #
36
+ class HTTPError < DiscorbError
37
+ # @return [String] the HTTP response code.
38
+ attr_reader :code
39
+ # @return [Net::HTTPResponse] the HTTP response.
40
+ attr_reader :response
41
+
42
+ # @!visibility private
43
+ def initialize(resp, data)
44
+ @code = data[:code]
45
+ @response = resp
46
+ super(data[:message])
47
+ end
48
+ end
49
+
50
+ #
51
+ # Represents a 400 error.
52
+ #
53
+ class BadRequestError < HTTPError
54
+ # @!visibility private
55
+ def initialize(resp, data)
56
+ @code = data[:code]
57
+ @response = resp
58
+ DiscorbError.instance_method(:initialize).bind(self).call(
59
+ [data[:message], "\n", enumerate_errors(data[:errors]).map do |ek, ev|
60
+ "#{ek}=>#{ev}"
61
+ end.join("\n")].join("\n")
62
+ )
63
+ end
64
+ end
65
+
66
+ #
67
+ # Represents a 403 error.
68
+ #
69
+ class ForbiddenError < HTTPError
70
+ end
71
+
72
+ #
73
+ # Represents a 404 error.
74
+ #
75
+ class NotFoundError < HTTPError
76
+ end
77
+
78
+ #
79
+ # Represents a error in client-side.
80
+ #
81
+ class ClientError < DiscorbError
82
+ end
83
+
84
+ #
85
+ # Represents a timeout error.
86
+ #
87
+ class TimeoutError < DiscorbError
88
+ end
89
+
90
+ #
91
+ # Represents a warning.
92
+ #
93
+ class NotSupportedWarning < DiscorbError
94
+ def initialize(message)
95
+ super("#{message} is not supported yet.")
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ #
5
+ # Represents a event.
6
+ # This class shouldn't be instantiated directly.
7
+ # Use {Client#on} instead.
8
+ #
9
+ class Event
10
+ # @return [Proc] the block to be called.
11
+ attr_reader :block
12
+ # @return [Symbol] the event id.
13
+ attr_reader :id
14
+ # @return [Hash] the event discriminator.
15
+ attr_reader :discriminator
16
+ # @return [Boolean] whether the event is once or not.
17
+ attr_reader :once
18
+ alias once? once
19
+
20
+ def initialize(block, id, discriminator)
21
+ @block = block
22
+ @id = id
23
+ @once = discriminator.fetch(:once, false)
24
+ @discriminator = discriminator
25
+ @rescue = nil
26
+ end
27
+
28
+ #
29
+ # Calls the block associated with the event.
30
+ #
31
+ def call(...)
32
+ @block.call(...)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Time
4
+ #
5
+ # Format a time object to a Discord formatted string.
6
+ #
7
+ # @param ["f", "F", "d", "D", "t", "T", "R"] type The format to use.
8
+ #
9
+ # @return [String] The formatted time.
10
+ #
11
+ def to_df(type = nil)
12
+ if type.nil?
13
+ "<t:#{to_i}>"
14
+ else
15
+ "<t:#{to_i}:#{type}>"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ #
5
+ # Module to make extension.
6
+ # extend this module to make your own extension.
7
+ # @see file:docs/extension.md
8
+ # @abstract
9
+ #
10
+ module Extension
11
+ @events = {}
12
+ @client = nil
13
+
14
+ #
15
+ # Define a new event.
16
+ #
17
+ # @param [Symbol] event_name The name of the event.
18
+ # @param [Symbol] id The id of the event. Used to delete the event.
19
+ # @param [Hash] discriminator Other discriminators.
20
+ # @param [Proc] block The block to execute when the event is triggered.
21
+ #
22
+ # @return [Discorb::Event] The event.
23
+ #
24
+ def event(event_name, id: nil, **discriminator, &block)
25
+ raise ArgumentError, "Event name must be a symbol" unless event_name.is_a?(Symbol)
26
+ raise ArgumentError, "block must be a Proc" unless block.is_a?(Proc)
27
+
28
+ @events = {} if @events.nil?
29
+ @events[event_name] ||= []
30
+ discriminator[:extension] = Extension
31
+ @events[event_name] << Discorb::Event.new(block, id, discriminator)
32
+ end
33
+
34
+ #
35
+ # Define a new once event.
36
+ #
37
+ # @param [Symbol] event_name The name of the event.
38
+ # @param [Symbol] id The id of the event. Used to delete the event.
39
+ # @param [Hash] discriminator Other discriminators.
40
+ # @param [Proc] block The block to execute when the event is triggered.
41
+ #
42
+ # @return [Discorb::Event] The event.
43
+ #
44
+ def once_event(event_name, id: nil, **discriminator, &block)
45
+ event(event_name, id: id, once: true, **discriminator, &block)
46
+ end
47
+
48
+ # @return [Hash{Symbol => Array<Discorb::Event>}] The events of the extension.
49
+ attr_reader :events
50
+
51
+ # @!visibility private
52
+ attr_accessor :client
53
+ end
54
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mime/types"
4
+
5
+ module Discorb
6
+ #
7
+ # Represents a attachment file.
8
+ #
9
+ class Attachment < DiscordModel
10
+ # @return [#read] The file content.
11
+ attr_reader :io
12
+ # @return [Discorb::Snowflake] The attachment id.
13
+ attr_reader :id
14
+ # @return [String] The attachment filename.
15
+ attr_reader :filename
16
+ # @return [String] The attachment content type.
17
+ attr_reader :content_type
18
+ # @return [Integer] The attachment size in bytes.
19
+ attr_reader :size
20
+ # @return [String] The attachment url.
21
+ attr_reader :url
22
+ # @return [String] The attachment proxy url.
23
+ attr_reader :proxy_url
24
+ # @return [Integer] The image height.
25
+ # @return [nil] If the attachment is not an image.
26
+ attr_reader :height
27
+ # @return [Integer] The image width.
28
+ # @return [nil] If the attachment is not an image.
29
+ attr_reader :width
30
+
31
+ # @!attribute [r] image?
32
+ # @return [Boolean] whether the file is an image.
33
+
34
+ # @!visibility private
35
+ def initialize(data)
36
+ @id = Snowflake.new(data[:id])
37
+ @filename = data[:filename]
38
+ @content_type = data[:content_type]
39
+ @size = data[:size]
40
+ @url = data[:url]
41
+ @proxy_url = data[:proxy_url]
42
+ @height = data[:height]
43
+ @width = data[:width]
44
+ end
45
+
46
+ def image?
47
+ @content_type.start_with? "image/"
48
+ end
49
+ end
50
+
51
+ #
52
+ # Represents a file to send as an attachment.
53
+ #
54
+ class File
55
+ # @return [#read] The IO of the file.
56
+ attr_accessor :io
57
+ # @return [String] The filename of the file. If not set, path or object_id of the IO is used.
58
+ attr_accessor :filename
59
+ # @return [String] The content type of the file. If not set, it is guessed from the filename.
60
+ attr_accessor :content_type
61
+
62
+ def initialize(io, filename = nil, content_type: nil)
63
+ @io = io
64
+ @filename = filename || (io.respond_to?(:path) ? io.path : io.object_id)
65
+ @content_type = content_type || MIME::Types.type_for(@filename)[0].to_s
66
+ @content_type = "application/octet-stream" if @content_type == ""
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ #
5
+ # Represents a flag.
6
+ # @abstract
7
+ #
8
+ class Flag
9
+ # @return [Hash{Symbol => Boolean}] the values of the flag.
10
+ attr_reader :values
11
+ # @return [Integer] the value of the flag.
12
+ attr_reader :value
13
+
14
+ @bits = {}
15
+
16
+ # Initialize the flag.
17
+ # @note This is usually called by the subclass.
18
+ #
19
+ # @param [Integer] value The value of the flag.
20
+ def initialize(value)
21
+ @value = value
22
+ @values = {}
23
+ self.class.bits.each_with_index do |(bn, bv), _i|
24
+ @values[bn] = value & (1 << bv) != 0
25
+ end
26
+ end
27
+
28
+ def method_missing(name, args = nil)
29
+ if @values.key?(name.to_s.delete_suffix("?").to_sym)
30
+ @values[name.to_s.delete_suffix("?").to_sym]
31
+ else
32
+ super
33
+ end
34
+ end
35
+
36
+ def respond_to_missing?(sym, include_private)
37
+ @values.key?(name.to_s.delete_suffix("?").to_sym) ? true : super
38
+ end
39
+
40
+ #
41
+ # Union of two flags.
42
+ #
43
+ # @param [Discorb::Flag] other The other flag.
44
+ #
45
+ # @return [Discorb::Flag] The union of the two flags.
46
+ #
47
+ def |(other)
48
+ self.class.new(@value | other.value)
49
+ end
50
+
51
+ alias + |
52
+
53
+ #
54
+ # Subtraction of two flags.
55
+ #
56
+ # @param [Discorb::Flag] other The other flag.
57
+ #
58
+ # @return [Discorb::Flag] The subtraction of the two flags.
59
+ #
60
+ def -(other)
61
+ self.class.new(@value & (@value ^ other.value))
62
+ end
63
+
64
+ #
65
+ # Intersection of two flags.
66
+ #
67
+ # @param [Discorb::Flag] other The other flag.
68
+ #
69
+ # @return [Discorb::Flag] The intersection of the two flags.
70
+ #
71
+ def &(other)
72
+ self.class.new(@value & other.value)
73
+ end
74
+
75
+ #
76
+ # XOR of two flags.
77
+ #
78
+ # @param [Discorb::Flag] other The other flag.
79
+ #
80
+ # @return [Discorb::Flag] The XOR of the two flags.
81
+ #
82
+ def ^(other)
83
+ self.class.new(@value ^ other.value)
84
+ end
85
+
86
+ #
87
+ # Negation of the flag.
88
+ #
89
+ # @return [Discorb::Flag] The negation of the flag.
90
+ #
91
+ def ~@
92
+ self.class.new(~@value)
93
+ end
94
+
95
+ class << self
96
+ # @return [Hash{Integer => Symbol}] the bits of the flag.
97
+ attr_reader :bits
98
+
99
+ #
100
+ # Max value of the flag.
101
+ #
102
+ # @return [Integer] the max value of the flag.
103
+ #
104
+ def max_value
105
+ 2 ** @bits.values.max - 1
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,967 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ #
5
+ # A module to handle gateway.
6
+ # This module is internal use only.
7
+ #
8
+ module GatewayHandler
9
+ #
10
+ # Represents an event.
11
+ #
12
+ class GatewayEvent
13
+ # @!visibility private
14
+ def initialize(data)
15
+ @data = data
16
+ end
17
+ end
18
+
19
+ #
20
+ # Represents a reaction event.
21
+ #
22
+ class ReactionEvent < GatewayEvent
23
+ # @return [Hash] The raw data of the event.
24
+ attr_reader :data
25
+ # @return [Discorb::Snowflake] The ID of the user who reacted.
26
+ attr_reader :user_id
27
+ alias member_id user_id
28
+ # @return [Discorb::Snowflake] The ID of the channel the message was sent in.
29
+ attr_reader :channel_id
30
+ # @return [Discorb::Snowflake] The ID of the message.
31
+ attr_reader :message_id
32
+ # @return [Discorb::Snowflake] The ID of the guild the message was sent in.
33
+ attr_reader :guild_id
34
+ # @macro client_cache
35
+ # @return [Discorb::User] The user who reacted.
36
+ attr_reader :user
37
+ # @macro client_cache
38
+ # @return [Discorb::Channel] The channel the message was sent in.
39
+ attr_reader :channel
40
+ # @macro client_cache
41
+ # @return [Discorb::Guild] The guild the message was sent in.
42
+ attr_reader :guild
43
+ # @macro client_cache
44
+ # @return [Discorb::Message] The message the reaction was sent in.
45
+ attr_reader :message
46
+ # @macro client_cache
47
+ # @return [Discorb::Member] The member who reacted.
48
+ attr_reader :member
49
+ # @return [Discorb::UnicodeEmoji, Discorb::PartialEmoji] The emoji that was reacted with.
50
+ attr_reader :emoji
51
+
52
+ # @!visibility private
53
+ def initialize(client, data)
54
+ @client = client
55
+ @data = data
56
+ if data.key?(:user_id)
57
+ @user_id = Snowflake.new(data[:user_id])
58
+ else
59
+ @member_data = data[:member]
60
+ end
61
+ @channel_id = Snowflake.new(data[:channel_id])
62
+ @message_id = Snowflake.new(data[:message_id])
63
+ @guild_id = Snowflake.new(data[:guild_id])
64
+ @guild = client.guilds[data[:guild_id]]
65
+ @channel = client.channels[data[:channel_id]] unless @guild.nil?
66
+
67
+ @user = client.users[data[:user_id]] if data.key?(:user_id)
68
+
69
+ unless @guild.nil?
70
+ @member = if data.key?(:member)
71
+ @guild.members[data[:member][:user][:id]] || Member.new(@client, @guild_id, data[:member][:user], data[:member])
72
+ else
73
+ @guild.members[data[:user_id]]
74
+ end
75
+ end
76
+
77
+ @message = client.messages[data[:message_id]]
78
+ @emoji = data[:emoji][:id].nil? ? UnicodeEmoji.new(data[:emoji][:name]) : PartialEmoji.new(data[:emoji])
79
+ end
80
+
81
+ # Fetch the message.
82
+ # If message is cached, it will be returned.
83
+ # @macro async
84
+ # @macro http
85
+ #
86
+ # @param [Boolean] force Whether to force fetching the message.
87
+ #
88
+ # @return [Discorb::Message] The message.
89
+ def fetch_message(force: false)
90
+ Async do |_task|
91
+ next @message if !force && @message
92
+
93
+ @message = @channel.fetch_message(@message_id).wait
94
+ end
95
+ end
96
+ end
97
+
98
+ #
99
+ # Represents a `MESSAGE_REACTION_REMOVE_ALL` event.
100
+ #
101
+ class ReactionRemoveAllEvent < GatewayEvent
102
+ # @return [Discorb::Snowflake] The ID of the channel the message was sent in.
103
+ attr_reader :channel_id
104
+ # @return [Discorb::Snowflake] The ID of the message.
105
+ attr_reader :message_id
106
+ # @return [Discorb::Snowflake] The ID of the guild the message was sent in.
107
+ attr_reader :guild_id
108
+ # @macro client_cache
109
+ # @return [Discorb::Channel] The channel the message was sent in.
110
+ attr_reader :channel
111
+ # @macro client_cache
112
+ # @return [Discorb::Guild] The guild the message was sent in.
113
+ attr_reader :guild
114
+ # @macro client_cache
115
+ # @return [Discorb::Message] The message the reaction was sent in.
116
+ attr_reader :message
117
+
118
+ # @!visibility private
119
+ def initialize(client, data)
120
+ @client = client
121
+ @data = data
122
+ @guild_id = Snowflake.new(data[:guild_id])
123
+ @channel_id = Snowflake.new(data[:channel_id])
124
+ @message_id = Snowflake.new(data[:message_id])
125
+ @guild = client.guilds[data[:guild_id]]
126
+ @channel = client.channels[data[:channel_id]]
127
+ @message = client.messages[data[:message_id]]
128
+ end
129
+
130
+ # Fetch the message.
131
+ # If message is cached, it will be returned.
132
+ # @macro async
133
+ # @macro http
134
+ #
135
+ # @param [Boolean] force Whether to force fetching the message.
136
+ #
137
+ # @return [Discorb::Message] The message.
138
+ def fetch_message(force: false)
139
+ Async do |_task|
140
+ next @message if !force && @message
141
+
142
+ @message = @channel.fetch_message(@message_id).wait
143
+ end
144
+ end
145
+ end
146
+
147
+ #
148
+ # Represents a `MESSAGE_REACTION_REMOVE_EMOJI` event.
149
+ #
150
+ class ReactionRemoveEmojiEvent < GatewayEvent
151
+ # @return [Discorb::Snowflake] The ID of the channel the message was sent in.
152
+ attr_reader :channel_id
153
+ # @return [Discorb::Snowflake] The ID of the message.
154
+ attr_reader :message_id
155
+ # @return [Discorb::Snowflake] The ID of the guild the message was sent in.
156
+ attr_reader :guild_id
157
+ # @macro client_cache
158
+ # @return [Discorb::Channel] The channel the message was sent in.
159
+ attr_reader :channel
160
+ # @macro client_cache
161
+ # @return [Discorb::Guild] The guild the message was sent in.
162
+ attr_reader :guild
163
+ # @macro client_cache
164
+ # @return [Discorb::Message] The message the reaction was sent in.
165
+ attr_reader :message
166
+ # @return [Discorb::UnicodeEmoji, Discorb::PartialEmoji] The emoji that was reacted with.
167
+ attr_reader :emoji
168
+
169
+ # @!visibility private
170
+ def initialize(client, data)
171
+ @client = client
172
+ @data = data
173
+ @guild_id = Snowflake.new(data[:guild_id])
174
+ @channel_id = Snowflake.new(data[:channel_id])
175
+ @message_id = Snowflake.new(data[:message_id])
176
+ @guild = client.guilds[data[:guild_id]]
177
+ @channel = client.channels[data[:channel_id]]
178
+ @message = client.messages[data[:message_id]]
179
+ @emoji = data[:emoji][:id].nil? ? DiscordEmoji.new(data[:emoji][:name]) : PartialEmoji.new(data[:emoji])
180
+ end
181
+
182
+ # Fetch the message.
183
+ # If message is cached, it will be returned.
184
+ # @macro async
185
+ # @macro http
186
+ #
187
+ # @param [Boolean] force Whether to force fetching the message.
188
+ #
189
+ # @return [Discorb::Message] The message.
190
+ def fetch_message(force: false)
191
+ Async do |_task|
192
+ next @message if !force && @message
193
+
194
+ @message = @channel.fetch_message(@message_id).wait
195
+ end
196
+ end
197
+ end
198
+
199
+ #
200
+ # Represents a `MESSAGE_UPDATE` event.
201
+ #
202
+
203
+ class MessageUpdateEvent < GatewayEvent
204
+ # @return [Discorb::Message] The message before update.
205
+ attr_reader :before
206
+ # @return [Discorb::Message] The message after update.
207
+ attr_reader :after
208
+ # @return [Discorb::Snowflake] The ID of the message.
209
+ attr_reader :id
210
+ # @return [Discorb::Snowflake] The ID of the channel the message was sent in.
211
+ attr_reader :channel_id
212
+ # @return [Discorb::Snowflake] The ID of the guild the message was sent in.
213
+ attr_reader :guild_id
214
+ # @return [String] The new content of the message.
215
+ attr_reader :content
216
+ # @return [Time] The time the message was edited.
217
+ attr_reader :timestamp
218
+ # @return [Boolean] Whether the message pings @everyone.
219
+ attr_reader :mention_everyone
220
+ # @macro client_cache
221
+ # @return [Array<Discorb::Role>] The roles mentioned in the message.
222
+ attr_reader :mention_roles
223
+ # @return [Array<Discorb::Attachment>] The attachments in the message.
224
+ attr_reader :attachments
225
+ # @return [Array<Discorb::Embed>] The embeds in the message.
226
+ attr_reader :embeds
227
+
228
+ # @!attribute [r] channel
229
+ # @macro client_cache
230
+ # @return [Discorb::Channel] The channel the message was sent in.
231
+ # @!attribute [r] guild
232
+ # @macro client_cache
233
+ # @return [Discorb::Guild] The guild the message was sent in.
234
+
235
+ def initialize(client, data, before, after)
236
+ @client = client
237
+ @data = data
238
+ @before = before
239
+ @after = after
240
+ @id = Snowflake.new(data[:id])
241
+ @channel_id = Snowflake.new(data[:channel_id])
242
+ @guild_id = Snowflake.new(data[:guild_id]) if data.key?(:guild_id)
243
+ @content = data[:content]
244
+ @timestamp = Time.iso8601(data[:edited_timestamp])
245
+ @mention_everyone = data[:mention_everyone]
246
+ @mention_roles = data[:mention_roles].map { |r| guild.roles[r] } if data.key?(:mention_roles)
247
+ @attachments = data[:attachments].map { |a| Attachment.new(a) } if data.key?(:attachments)
248
+ @embeds = data[:embeds] ? data[:embeds].map { |e| Embed.new(data: e) } : [] if data.key?(:embeds)
249
+ end
250
+
251
+ def channel
252
+ @client.channels[@channel_id]
253
+ end
254
+
255
+ def guild
256
+ @client.guilds[@guild_id]
257
+ end
258
+
259
+ # Fetch the message.
260
+ # @macro async
261
+ # @macro http
262
+ #
263
+ # @return [Discorb::Message] The message.
264
+ def fetch_message
265
+ Async do
266
+ channel.fetch_message(@id).wait
267
+ end
268
+ end
269
+ end
270
+
271
+ #
272
+ # Represents a message but it has only ID.
273
+ #
274
+ class UnknownDeleteBulkMessage < GatewayEvent
275
+ # @return [Discorb::Snowflake] The ID of the message.
276
+ attr_reader :id
277
+
278
+ # @!attribute [r] channel
279
+ # @macro client_cache
280
+ # @return [Discorb::Channel] The channel the message was sent in.
281
+ # @!attribute [r] guild
282
+ # @macro client_cache
283
+ # @return [Discorb::Guild] The guild the message was sent in.
284
+
285
+ # @!visibility private
286
+ def initialize(client, id, data)
287
+ @client = client
288
+ @id = id
289
+ @data = data
290
+ @channel_id = Snowflake.new(data[:channel_id])
291
+ @guild_id = Snowflake.new(data[:guild_id]) if data.key?(:guild_id)
292
+ end
293
+
294
+ def channel
295
+ @client.channels[@channel_id]
296
+ end
297
+
298
+ def guild
299
+ @client.guilds[@guild_id]
300
+ end
301
+ end
302
+
303
+ #
304
+ # Represents a `INVITE_DELETE` event.
305
+ #
306
+ class InviteDeleteEvent < GatewayEvent
307
+ # @return [String] The invite code.
308
+ attr_reader :code
309
+
310
+ # @!attribute [r] channel
311
+ # @macro client_cache
312
+ # @return [Discorb::Channel] The channel the message was sent in.
313
+ # @!attribute [r] guild
314
+ # @macro client_cache
315
+ # @return [Discorb::Guild] The guild the message was sent in.
316
+
317
+ # @!visibility private
318
+ def initialize(client, data)
319
+ @client = client
320
+ @data = data
321
+ @channel_id = Snowflake.new(data[:channel])
322
+ @guild_id = Snowflake.new(data[:guild_id])
323
+ @code = data[:code]
324
+ end
325
+
326
+ def channel
327
+ @client.channels[@channel_id]
328
+ end
329
+
330
+ def guild
331
+ @client.guilds[@guild_id]
332
+ end
333
+ end
334
+
335
+ class GuildIntegrationsUpdateEvent < GatewayEvent
336
+ def initialize(client, data)
337
+ @client = client
338
+ @data = data
339
+ @guild_id = Snowflake.new(data[:guild_id])
340
+ end
341
+
342
+ def guild
343
+ @client.guilds[@guild_id]
344
+ end
345
+ end
346
+
347
+ #
348
+ # Represents a `TYPING_START` event.
349
+ #
350
+ class TypingStartEvent < GatewayEvent
351
+ # @return [Discorb::Snowflake] The ID of the channel the user is typing in.
352
+ attr_reader :user_id
353
+
354
+ # @!attribute [r] channel
355
+ # @macro client_cache
356
+ # @return [Discorb::Channel] The channel the user is typing in.
357
+ # @!attribute [r] guild
358
+ # @macro client_cache
359
+ # @return [Discorb::Guild] The guild the user is typing in.
360
+ # @!attribute [r] user
361
+ # @macro client_cache
362
+ # @return [Discorb::User] The user that is typing.
363
+
364
+ # @!visibility private
365
+ def initialize(client, data)
366
+ @client = client
367
+ @data = data
368
+ @channel_id = Snowflake.new(data[:channel_id])
369
+ @guild_id = Snowflake.new(data[:guild_id]) if data.key?(:guild_id)
370
+ @user_id = Snowflake.new(data[:user_id])
371
+ @timestamp = Time.at(data[:timestamp])
372
+ @member = guild.members[@user_id] || Member.new(@client, @guild_id, @client.users[@user_id].instance_variable_get(:@data), data[:member]) if guild
373
+ end
374
+
375
+ def user
376
+ @client.users[@user_id]
377
+ end
378
+
379
+ def channel
380
+ @client.channels[@channel_id]
381
+ end
382
+
383
+ def guild
384
+ @client.guilds[@guild_id]
385
+ end
386
+ end
387
+
388
+ #
389
+ # Represents a message pin event.
390
+ #
391
+ class MessagePinEvent < GatewayEvent
392
+ # @return [Discorb::Message] The message that was pinned.
393
+ attr_reader :message
394
+ # @return [:pinned, :unpinned] The type of event.
395
+ attr_reader :type
396
+
397
+ # @!attribute [r] pinned?
398
+ # @return [Boolean] Whether the message was pinned.
399
+ # @!attribute [r] unpinned?
400
+ # @return [Boolean] Whether the message was unpinned.
401
+
402
+ def initialize(client, data, message)
403
+ @client = client
404
+ @data = data
405
+ @message = message
406
+ @type = if message.nil?
407
+ :unknown
408
+ elsif @message.pinned?
409
+ :pinned
410
+ else
411
+ :unpinned
412
+ end
413
+ end
414
+
415
+ def pinned?
416
+ @type == :pinned
417
+ end
418
+
419
+ def unpinned?
420
+ @type = :unpinned
421
+ end
422
+ end
423
+
424
+ #
425
+ # Represents a `WEBHOOKS_UPDATE` event.
426
+ #
427
+ class WebhooksUpdateEvent < GatewayEvent
428
+ # @!attribute [r] channel
429
+ # @macro client_cache
430
+ # @return [Discorb::Channel] The channel where the webhook was updated.
431
+ # @!attribute [r] guild
432
+ # @macro client_cache
433
+ # @return [Discorb::Guild] The guild where the webhook was updated.
434
+
435
+ # @!visibility private
436
+ def initialize(client, data)
437
+ @client = client
438
+ @data = data
439
+ @guild_id = Snowflake.new(data[:guild_id])
440
+ @channel_id = Snowflake.new(data[:channel_id])
441
+ end
442
+
443
+ def guild
444
+ @client.guilds[@guild_id]
445
+ end
446
+
447
+ def channel
448
+ @client.channels[@channel_id]
449
+ end
450
+ end
451
+
452
+ private
453
+
454
+ def connect_gateway(first)
455
+ @log.info "Connecting to gateway."
456
+ Async do |_task|
457
+ @internet = Internet.new(self) if first
458
+ @first = first
459
+ _, gateway_response = @internet.get("/gateway").wait
460
+ gateway_url = gateway_response[:url]
461
+ endpoint = Async::HTTP::Endpoint.parse("#{gateway_url}?v=9&encoding=json",
462
+ alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
463
+ begin
464
+ Async::WebSocket::Client.connect(endpoint, headers: [["User-Agent", Discorb::USER_AGENT]]) do |connection|
465
+ @connection = connection
466
+ while (message = @connection.read)
467
+ handle_gateway(message)
468
+ end
469
+ end
470
+ rescue Protocol::WebSocket::ClosedError => e
471
+ case e.message
472
+ when "Authentication failed."
473
+ @tasks.map(&:stop)
474
+ raise ClientError.new("Authentication failed."), cause: nil
475
+ when "Discord WebSocket requesting client reconnect."
476
+ @log.info "Discord WebSocket requesting client reconnect"
477
+ @tasks.map(&:stop)
478
+ connect_gateway(false)
479
+ end
480
+ rescue EOFError, Async::Wrapper::Cancelled
481
+ connect_gateway(false)
482
+ end
483
+ end
484
+ end
485
+
486
+ def send_gateway(opcode, **value)
487
+ @connection.write({ op: opcode, d: value })
488
+ @connection.flush
489
+ @log.debug "Sent message with opcode #{opcode}: #{value.to_json.gsub(@token, "[Token]")}"
490
+ end
491
+
492
+ def handle_gateway(payload)
493
+ Async do |task|
494
+ data = payload[:d]
495
+ @last_s = payload[:s] if payload[:s]
496
+ @log.debug "Received message with opcode #{payload[:op]} from gateway: #{data}"
497
+ case payload[:op]
498
+ when 10
499
+ @heartbeat_interval = data[:heartbeat_interval]
500
+ @tasks << handle_heartbeat(@heartbeat_interval)
501
+ if @first
502
+ payload = {
503
+ token: @token,
504
+ intents: @intents.value,
505
+ compress: false,
506
+ properties: { "$os" => RUBY_PLATFORM, "$browser" => "discorb", "$device" => "discorb" },
507
+ }
508
+ payload[:presence] = @identify_presence if @identify_presence
509
+ send_gateway(2, **payload)
510
+ else
511
+ payload = {
512
+ token: @token,
513
+ session_id: @session_id,
514
+ seq: @last_s,
515
+ }
516
+ send_gateway(6, **payload)
517
+ end
518
+ when 9
519
+ @log.warn "Received opcode 9, closed connection"
520
+ @tasks.map(&:stop)
521
+ if data
522
+ @log.info "Connection is resumable, reconnecting"
523
+ @connection.close
524
+ connect_gateway(false)
525
+ else
526
+ @log.info "Connection is not resumable, reconnecting with opcode 2"
527
+ task.sleep(2)
528
+ @connection.close
529
+ connect_gateway(true)
530
+ end
531
+ when 0
532
+ handle_event(payload[:t], data)
533
+ end
534
+ end
535
+ end
536
+
537
+ def handle_heartbeat(interval)
538
+ Async do |task|
539
+ task.sleep((interval / 1000.0 - 1) * rand)
540
+ loop do
541
+ @connection.write({ op: 1, d: @last_s })
542
+ @connection.flush
543
+ @log.debug "Sent opcode 1."
544
+ @log.debug "Waiting for heartbeat."
545
+ sleep(interval / 1000.0 - 1)
546
+ end
547
+ end
548
+ end
549
+
550
+ def handle_event(event_name, data)
551
+ return @log.debug "Client isn't ready; event #{event_name} wasn't handled" if @wait_until_ready && !@ready && !%w[READY GUILD_CREATE].include?(event_name)
552
+
553
+ dispatch(:event_receive, event_name, data)
554
+ @log.debug "Handling event #{event_name}"
555
+ case event_name
556
+ when "READY"
557
+ @api_version = data[:v]
558
+ @session_id = data[:session_id]
559
+ @user = ClientUser.new(self, data[:user])
560
+ @uncached_guilds = data[:guilds].map { |g| g[:id] }
561
+ when "GUILD_CREATE"
562
+ if @uncached_guilds.include?(data[:id])
563
+ Guild.new(self, data, true)
564
+ @uncached_guilds.delete(data[:id])
565
+ if @uncached_guilds == []
566
+ @ready = true
567
+ dispatch(:ready)
568
+ @log.info("Guilds were cached")
569
+ end
570
+ elsif @guilds.has?(data[:id])
571
+ @guilds[data[:id]].send(:_set_data, data)
572
+ dispatch(:guild_available, guild)
573
+ else
574
+ guild = Guild.new(self, data, true)
575
+ dispatch(:guild_join, guild)
576
+ end
577
+ dispatch(:guild_create, @guilds[data[:id]])
578
+ when "MESSAGE_CREATE"
579
+ message = Message.new(self, data)
580
+ dispatch(:message, message)
581
+ when "GUILD_UPDATE"
582
+ if @guilds.has?(data[:id])
583
+ current = @guilds[data[:id]]
584
+ before = Guild.new(self, current.instance_variable_get(:@data).merge(no_cache: true), false)
585
+ current.send(:_set_data, data, false)
586
+ dispatch(:guild_update, before, current)
587
+ else
588
+ @log.warn "Unknown guild id #{data[:id]}, ignoring"
589
+ end
590
+ when "GUILD_DELETE"
591
+ return @log.warn "Unknown guild id #{data[:id]}, ignoring" unless (guild = @guilds.delete(data[:id]))
592
+
593
+ dispatch(:guild_delete, guild)
594
+ if guild.has?(:unavailable)
595
+ dispatch(:guild_destroy, guild)
596
+ else
597
+ dispatch(:guild_leave, guild)
598
+ end
599
+ when "GUILD_ROLE_CREATE"
600
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
601
+
602
+ nr = Role.new(@client, guild, data[:role])
603
+ guild.roles[data[:role][:id]] = nr
604
+ dispatch(:role_create, nr)
605
+ when "GUILD_ROLE_UPDATE"
606
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
607
+ return @log.warn "Unknown role id #{data[:role][:id]}, ignoring" unless guild.roles.has?(data[:role][:id])
608
+
609
+ current = guild.roles[data[:role][:id]]
610
+ before = Role.new(@client, guild, current.instance_variable_get(:@data).update({ no_cache: true }))
611
+ current.send(:_set_data, data[:role])
612
+ dispatch(:role_update, before, current)
613
+ when "GUILD_ROLE_DELETE"
614
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
615
+ return @log.warn "Unknown role id #{data[:role_id]}, ignoring" unless (role = guild.roles.delete(data[:role_id]))
616
+
617
+ dispatch(:role_delete, role)
618
+ when "CHANNEL_CREATE"
619
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
620
+
621
+ nc = Channel.make_channel(self, data)
622
+ guild.channels[data[:id]] = nc
623
+
624
+ dispatch(:channel_create, nc)
625
+ when "CHANNEL_UPDATE"
626
+ return @log.warn "Unknown channel id #{data[:id]}, ignoring" unless (current = @channels[data[:id]])
627
+
628
+ before = Channel.make_channel(self, current.instance_variable_get(:@data), no_cache: true)
629
+ current.send(:_set_data, data)
630
+ dispatch(:channel_update, before, current)
631
+ when "CHANNEL_DELETE"
632
+ return @log.warn "Unknown channel id #{data[:id]}, ignoring" unless (channel = @channels.delete(data[:id]))
633
+
634
+ @guilds[data[:guild_id]]&.channels&.delete(data[:id])
635
+ dispatch(:channel_delete, channel)
636
+ when "CHANNEL_PINS_UPDATE"
637
+ nil # do in MESSAGE_UPDATE
638
+ when "THREAD_CREATE"
639
+ thread = Channel.make_channel(self, data)
640
+
641
+ dispatch(:thread_create, thread)
642
+ if data.key?(:member)
643
+ dispatch(:thread_join, thread)
644
+ else
645
+ dispatch(:thread_new, thread)
646
+ end
647
+ when "THREAD_UPDATE"
648
+ return @log.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels[data[:id]])
649
+
650
+ before = Channel.make_channel(self, thread.instance_variable_get(:@data), no_cache: true)
651
+ thread.send(:_set_data, data)
652
+ dispatch(:thread_update, before, thread)
653
+ when "THREAD_DELETE"
654
+ return @log.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels.delete(data[:id]))
655
+
656
+ @guilds[data[:guild_id]]&.channels&.delete(data[:id])
657
+ dispatch(:thread_delete, thread)
658
+ when "THREAD_LIST_SYNC"
659
+ data[:threads].each do |raw_thread|
660
+ thread = Channel.make_channel(self, raw_thread.merge({ member: raw_thread[:members].find { |m| m[:id] == raw_thread[:id] } }))
661
+ @channels[thread.id] = thread
662
+ end
663
+ when "THREAD_MEMBER_UPDATE"
664
+ return @log.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels[data[:id]])
665
+
666
+ if (member = thread.members[data[:id]])
667
+ old = ThreadChannel::Member.new(self, member.instance_variable_get(:@data))
668
+ member._set_data(data)
669
+ else
670
+ old = nil
671
+ member = ThreadChannel::Member.new(self, data)
672
+ thread.members[data[:user_id]] = member
673
+ end
674
+ dispatch(:thread_member_update, thread, old, member)
675
+ when "THREAD_MEMBERS_UPDATE"
676
+ return @log.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels[data[:id]])
677
+
678
+ thread.instance_variable_set(:@member_count, data[:member_count])
679
+ members = []
680
+ (data[:added_members] || []).each do |raw_member|
681
+ member = ThreadChannel::Member.new(self, raw_member)
682
+ thread.members[member.id] = member
683
+ members << member
684
+ end
685
+ removed_members = []
686
+ (data[:removed_member_ids] || []).each do |id|
687
+ removed_members << thread.members.delete(id)
688
+ end
689
+ dispatch(:thread_members_update, thread, members, removed_members)
690
+ when "STAGE_INSTANCE_CREATE"
691
+ instance = StageInstance.new(self, data)
692
+ dispatch(:stage_instance_create, instance)
693
+ when "STAGE_INSTANCE_UPDATE"
694
+ return @log.warn "Unknown channel id #{data[:channel_id]} , ignoring" unless (channel = @channels[data[:channel_id]])
695
+ return @log.warn "Unknown stage instance id #{data[:id]}, ignoring" unless (instance = channel.stage_instances[data[:id]])
696
+
697
+ old = StageInstance.new(self, instance.instance_variable_get(:@data), no_cache: true)
698
+ current.send(:_set_data, data)
699
+ dispatch(:stage_instance_update, old, current)
700
+ when "STAGE_INSTANCE_DELETE"
701
+ return @log.warn "Unknown channel id #{data[:channel_id]} , ignoring" unless (channel = @channels[data[:channel_id]])
702
+ return @log.warn "Unknown stage instance id #{data[:id]}, ignoring" unless (instance = channel.stage_instances.delete(data[:id]))
703
+
704
+ dispatch(:stage_instance_delete, instance)
705
+ when "GUILD_MEMBER_ADD"
706
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
707
+
708
+ nm = Member.new(self, data[:guild_id], data[:user].update({ no_cache: true }), data)
709
+ guild.members[nm.id] = nm
710
+ dispatch(:member_add, nm)
711
+ when "GUILD_MEMBER_UPDATE"
712
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
713
+ return @log.warn "Unknown member id #{data[:id]}, ignoring" unless (nm = guild.members[data[:id]])
714
+
715
+ old = Member.new(self, data[:guild_id], data[:user], data.update({ no_cache: true }))
716
+ nm.send(:_set_data, data[:user], data)
717
+ dispatch(:member_update, old, nm)
718
+ when "GUILD_MEMBER_REMOVE"
719
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
720
+ return @log.warn "Unknown member id #{data[:id]}, ignoring" unless (member = guild.members.delete(data[:id]))
721
+
722
+ dispatch(:member_remove, member)
723
+ when "GUILD_BAN_ADD"
724
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
725
+
726
+ user = if @users.has? data[:user][:id]
727
+ @users[data[:user][:id]]
728
+ else
729
+ User.new(self, data[:user].update({ no_cache: true }))
730
+ end
731
+
732
+ dispatch(:guild_ban_add, guild, user)
733
+ when "GUILD_BAN_REMOVE"
734
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
735
+
736
+ user = if @users.has? data[:user][:id]
737
+ @users[data[:user][:id]]
738
+ else
739
+ User.new(self, data[:user].update({ no_cache: true }))
740
+ end
741
+
742
+ dispatch(:guild_ban_remove, guild, user)
743
+ when "GUILD_EMOJIS_UPDATE"
744
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
745
+
746
+ before_emojis = guild.emojis.values.map(&:id).to_set
747
+ data[:emojis].each do |emoji|
748
+ guild.emojis[emoji[:id]] = CustomEmoji.new(self, guild, emoji)
749
+ end
750
+ deleted_emojis = before_emojis - guild.emojis.values.map(&:id).to_set
751
+ deleted_emojis.each do |emoji|
752
+ guild.emojis.delete(emoji)
753
+ end
754
+ when "GUILD_INTEGRATIONS_UPDATE"
755
+ # dispatch(:guild_integrations_update, GuildIntegrationsUpdateEvent.new(self, data))
756
+ # Currently not implemented
757
+ when "INTEGRATION_CREATE"
758
+ dispatch(:integration_create, Integration.new(self, data, data[:guild_id]))
759
+ when "INTEGRATION_UPDATE"
760
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
761
+ return @log.warn "Unknown integration id #{data[:id]}, ignoring" unless (integration = guild.integrations[data[:id]])
762
+
763
+ before = Integration.new(self, integration.instance_variable_get(:@data), data[:guild_id], no_cache: true)
764
+ integration.send(:_set_data, data)
765
+ dispatch(:integration_update, before, integration)
766
+ when "INTEGRATION_DELETE"
767
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
768
+ return @log.warn "Unknown integration id #{data[:id]}, ignoring" unless (integration = guild.integrations.delete(data[:id]))
769
+
770
+ dispatch(:integration_delete, integration)
771
+ when "WEBHOOKS_UPDATE"
772
+ dispatch(:webhooks_update, WebhooksUpdateEvent.new(self, data))
773
+ when "INVITE_CREATE"
774
+ dispatch(:invite_create, Invite.new(self, data, true))
775
+ when "INVITE_DELETE"
776
+ dispatch(:invite_delete, InviteDeleteEvent.new(self, data))
777
+ when "VOICE_STATE_UPDATE"
778
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
779
+
780
+ current = guild.voice_states[data[:user_id]]
781
+ if current.nil?
782
+ old = nil
783
+ current = VoiceState.new(self, data)
784
+ guild.voice_states[data[:user_id]] = current
785
+ else
786
+ old = VoiceState.new(self, current.instance_variable_get(:@data))
787
+ current.send(:_set_data, data)
788
+ end
789
+ dispatch(:voice_state_update, old, current)
790
+ if old&.channel != current&.channel
791
+ dispatch(:voice_channel_update, old, current)
792
+ case [old&.channel, current&.channel]
793
+ in [nil, _]
794
+ dispatch(:voice_channel_connect, current)
795
+ in [_, nil]
796
+ dispatch(:voice_channel_disconnect, old)
797
+ in _
798
+ dispatch(:voice_channel_move, old, current)
799
+ end
800
+ end
801
+ if old&.mute? != current&.mute?
802
+ dispatch(:voice_mute_update, old, current)
803
+ case [old&.mute?, current&.mute?]
804
+ when [false, true]
805
+ dispatch(:voice_mute_enable, current)
806
+ when [true, false]
807
+ dispatch(:voice_mute_disable, old)
808
+ end
809
+ end
810
+ if old&.deaf? != current&.deaf?
811
+ dispatch(:voice_deaf_update, old, current)
812
+ case [old&.deaf?, current&.deaf?]
813
+ when [false, true]
814
+ dispatch(:voice_deaf_enable, current)
815
+ when [true, false]
816
+ dispatch(:voice_deaf_disable, old)
817
+ end
818
+ end
819
+ if old&.self_mute? != current&.self_mute?
820
+ dispatch(:voice_self_mute_update, old, current)
821
+ case [old&.self_mute?, current&.self_mute?]
822
+ when [false, true]
823
+ dispatch(:voice_self_mute_enable, current)
824
+ when [true, false]
825
+ dispatch(:voice_self_mute_disable, old)
826
+ end
827
+ end
828
+ if old&.self_deaf? != current&.self_deaf?
829
+ dispatch(:voice_self_deaf_update, old, current)
830
+ case [old&.self_deaf?, current&.self_deaf?]
831
+ when [false, true]
832
+ dispatch(:voice_self_deaf_enable, current)
833
+ when [true, false]
834
+ dispatch(:voice_self_deaf_disable, old)
835
+ end
836
+ end
837
+ if old&.server_mute? != current&.server_mute?
838
+ dispatch(:voice_server_mute_update, old, current)
839
+ case [old&.server_mute?, current&.server_mute?]
840
+ when [false, true]
841
+ dispatch(:voice_server_mute_enable, current)
842
+ when [true, false]
843
+ dispatch(:voice_server_mute_disable, old)
844
+ end
845
+ end
846
+ if old&.server_deaf? != current&.server_deaf?
847
+ dispatch(:voice_server_deaf_update, old, current)
848
+ case [old&.server_deaf?, current&.server_deaf?]
849
+ when [false, true]
850
+ dispatch(:voice_server_deaf_enable, current)
851
+ when [true, false]
852
+ dispatch(:voice_server_deaf_disable, old)
853
+ end
854
+ end
855
+ if old&.video? != current&.video?
856
+ dispatch(:voice_video_update, old, current)
857
+ case [old&.video?, current&.video?]
858
+ when [false, true]
859
+ dispatch(:voice_video_start, current)
860
+ when [true, false]
861
+ dispatch(:voice_video_end, old)
862
+ end
863
+ end
864
+ if old&.stream? != current&.stream?
865
+ dispatch(:voice_stream_update, old, current)
866
+ case [old&.stream?, current&.stream?]
867
+ when [false, true]
868
+ dispatch(:voice_stream_start, current)
869
+ when [true, false]
870
+ dispatch(:voice_stream_end, old)
871
+ end
872
+ end
873
+ when "PRESENCE_UPDATE"
874
+ return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
875
+
876
+ guild.presences[data[:user][:id]] = Presence.new(self, data)
877
+ when "MESSAGE_UPDATE"
878
+ if (message = @messages[data[:id]])
879
+ before = Message.new(self, message.instance_variable_get(:@data), no_cache: true)
880
+ message.send(:_set_data, message.instance_variable_get(:@data).merge(data))
881
+ else
882
+ @log.info "Uncached message ID #{data[:id]}, ignoring"
883
+ before = nil
884
+ message = nil
885
+ end
886
+ if data[:edited_timestamp].nil?
887
+ if message.nil?
888
+ nil
889
+ elsif message.pinned?
890
+ message.instance_variable_set(:@pinned, false)
891
+ else
892
+ message.instance_variable_set(:@pinned, true)
893
+ end
894
+ dispatch(:message_pin_update, MessagePinEvent.new(self, data, message))
895
+ else
896
+ dispatch(:message_update, MessageUpdateEvent.new(self, data, before, current))
897
+ end
898
+ when "MESSAGE_DELETE"
899
+ message.instance_variable_set(:@deleted, true) if (message = @messages[data[:id]])
900
+
901
+ dispatch(:message_delete_id, Snowflake.new(data[:id]), channels[data[:channel_id]], data[:guild_id] && guilds[data[:guild_id]])
902
+ dispatch(:message_delete, message, channels[data[:channel_id]], data[:guild_id] && guilds[data[:guild_id]])
903
+ when "MESSAGE_DELETE_BULK"
904
+ messages = []
905
+ data[:ids].each do |id|
906
+ if (message = @messages[id])
907
+ message.instance_variable_set(:@deleted, true)
908
+ messages.push(message)
909
+ else
910
+ messages.push(UnknownDeleteBulkMessage.new(self, id))
911
+ end
912
+ end
913
+ dispatch(:message_delete_bulk, messages)
914
+ when "MESSAGE_REACTION_ADD"
915
+ if (target_message = @messages[data[:message_id]])
916
+ if (target_reaction = target_message.reactions.find do |r|
917
+ r.emoji.is_a?(UnicodeEmoji) ? r.emoji.value == data[:emoji][:name] : r.emoji.id == data[:emoji][:id]
918
+ end)
919
+ target_reaction.set_instance_variable(:@count, target_reaction.count + 1)
920
+ else
921
+ target_message.reactions << Reaction.new(
922
+ self,
923
+ {
924
+ count: 1,
925
+ me: @user.id == data[:user_id],
926
+ emoji: data[:emoji],
927
+ }
928
+ )
929
+ end
930
+ end
931
+ dispatch(:reaction_add, ReactionEvent.new(self, data))
932
+ when "MESSAGE_REACTION_REMOVE"
933
+ if (target_message = @messages[data[:message_id]]) &&
934
+ (target_reaction = target_message.reactions.find do |r|
935
+ data[:emoji][:id].nil? ? r.emoji.name == data[:emoji][:name] : r.emoji.id == data[:emoji][:id]
936
+ end)
937
+ target_reaction.set_instance_variable(:@count, target_reaction.count - 1)
938
+ target_message.reactions.delete(target_reaction) if target_reaction.count.zero?
939
+ end
940
+ dispatch(:reaction_remove, ReactionEvent.new(self, data))
941
+ when "MESSAGE_REACTION_REMOVE_ALL"
942
+ if (target_message = @messages[data[:message_id]])
943
+ target_message.reactions = []
944
+ end
945
+ dispatch(:reaction_remove_all, ReactionRemoveAllEvent.new(self, data))
946
+ when "MESSAGE_REACTION_REMOVE_EMOJI"
947
+ if (target_message = @messages[data[:message_id]]) &&
948
+ (target_reaction = target_message.reactions.find { |r| data[:emoji][:id].nil? ? r.name == data[:emoji][:name] : r.id == data[:emoji][:id] })
949
+ target_message.reactions.delete(target_reaction)
950
+ end
951
+ dispatch(:reaction_remove_emoji, ReactionRemoveEmojiEvent.new(data))
952
+ when "TYPING_START"
953
+ dispatch(:typing_start, TypingStartEvent.new(self, data))
954
+ when "INTERACTION_CREATE"
955
+ interaction = Interaction.make_interaction(self, data)
956
+ dispatch(:integration_create, interaction)
957
+
958
+ dispatch(interaction.class.event_name, interaction)
959
+ when "RESUMED"
960
+ @log.info("Successfully resumed connection")
961
+ dispatch(:resumed)
962
+ else
963
+ @log.warn "Unknown event: #{event_name}\n#{data.inspect}"
964
+ end
965
+ end
966
+ end
967
+ end