revoltrb 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be934ade9a4122fe6ea640bf5aafe2930eb1a11ee2a8f4340d282b3fa99f163c
4
- data.tar.gz: 6e384401d88c604fa0bfa05b1a840697c0382a288683ac27db23d98e425b4a25
3
+ metadata.gz: 9a423864396d60b962eebc129b294b03f79e812d9b95eb2511f7c4bb2f07cefc
4
+ data.tar.gz: 142aba7edfce9961c36ff0ab2eaa70eced3e1a55ec64e2f2ae32db345fc30347
5
5
  SHA512:
6
- metadata.gz: e36bad7e4adec3333f2341b6d1e85aadc1cbab691552d69a1f9e777a7df5722b2fc048e1e98be0e361a4a1447d2597e65f3cc8888d7afb9fb589e9dfa4f79b27
7
- data.tar.gz: db5fa947e54394afb19495145a3b3beb8ef50bf6bc17c584fc8369659a281603c48f522fe7a7c0ea4dcf56c7a1f1a35185d5a593ffaea5586951862a19a40535
6
+ metadata.gz: 1c9fb2983e8cba19aa607218619f5b1c7fc8bc712bba8a3030fb6e460060d60ed89a0493c35329668148542156f0591a5d2bbdf6ed0e1d1f5cd799b547293972
7
+ data.tar.gz: 8bf120f58d994b0a9617844acbf0b86e0491b85965a228e50e6430ba5402c80ef1c0f22585e9d22558711f4b92255942ca0bca89db9411da924d7523cb4a43f7
data/README.md CHANGED
@@ -1,18 +1,18 @@
1
1
  # revoltrb
2
2
 
3
- Revoltrb is a Ruby package (a.k.a. Gem) that allows you to make Revolt.chat bots using the Ruby programming language.
4
-
5
- This package (a.k.a. Gem) is not officially endorsed by Revolt.chat amd this is not an official Revolt.chat product.
3
+ Revoltrb is a Ruby package (a.k.a. Gem) that allows you to make Revolt.chat bots using the Ruby programming language. This package (a.k.a. Gem) is not officially endorsed by Revolt.chat amd this is not an official Revolt.chat product.
6
4
 
7
5
  You need Ruby 3.0 or newer in order to use this package (Ruby 3.2 or newer is recommended)
8
6
 
7
+ > [!NOTE]
8
+ > This package (a.k.a. Gem) is in a early alpha state so expect things to be buggy and/or broken.
9
+
9
10
  ## ToDo
10
11
 
11
12
  This list contains a list of things that I know is broken and gotta fix. Contributing will be super helpful.
12
13
 
13
- - Fix reactions support
14
14
  - Fix obtaining server information
15
- - Fix not being able to have an embed color
15
+ - Fix reactions support
16
16
 
17
17
  ## Setup
18
18
 
data/lib/revoltrb/bot.rb CHANGED
@@ -1,488 +1,538 @@
1
- # lib/revoltrb/bot.rb
2
-
3
- require 'json'
4
- require 'net/http'
5
- require 'websocket-client-simple' # This is required for WebSocket communication
6
- require 'thread'
7
- require 'time'
8
- require_relative 'debuglogger'
9
- require_relative 'request_queue'
10
-
11
- module Revoltrb
12
- class RevoltBot
13
- attr_reader :token, :user_id, :bot_name, :servers, :prefix, :bot_owner_id, :bot_discriminator, :bot_discoverable, :bot_creation_date
14
- attr_accessor :websocket_url, :api_url, :cdn_url
15
- # Initializes the bot with the provided token, API endpoints, and configuration.
16
- def initialize(token, api_url: 'https://api.revolt.chat', websocket_url: 'wss://app.revolt.chat/events', cdn_url: 'https://cdn.revoltusercontent.com', prefix: nil, debuglogs: false, selfbot: false)
17
- @token = token
18
- @api_url = api_url
19
- @websocket_url = websocket_url
20
- @cdn_url = cdn_url
21
-
22
- @user_id = nil
23
- @bot_name = nil
24
- @servers = {}
25
- @commands = {}
26
- @message_handlers = []
27
-
28
- @websocket = nil
29
- @websocket_thread = nil
30
- @heartbeat_interval = 30 # Default heartbeat interval in seconds
31
- @last_heartbeat_sent = Time.now.to_i
32
- @running = false
33
- @ready_event_received = false
34
- @logger = Revoltrb::DebugLogger.new(debuglogs)
35
- @request_queue = Revoltrb::RequestQueue.new(500)
36
-
37
- @prefix = "!"
38
- @prefix = prefix if prefix
39
- @selfbot = selfbot
40
- @logger.debug "RevoltBot initialized. API: #{@api_url}, WS: #{@websocket_url}, CDN: #{@cdn_url}, Prefix: #{@prefix}"
41
- end
42
-
43
- def get_botinfo
44
- {
45
- 'bot_id' => @user_id,
46
- 'bot_name' => @bot_name,
47
- 'bot_discriminator' => @bot_discriminator,
48
- 'bot_ownerid' => @bot_owner_id,
49
- 'bot_discoverable' => @bot_discoverable,
50
- 'bot_creationdate' => @bot_creation_date,
51
- 'bot_prefix' => @prefix,
52
- 'bot_token' => @token
53
- }
54
- end
55
-
56
- # Log into the Revolt.chat bot
57
- def login
58
- @logger.debug "BOT: Bot attempting to start...."
59
- # Step 1: Fetch initial bot user details via REST API using /users/@me with X-Bot-Token | As confirmed, /users/@me returns the bot's user object directly.
60
- uri = URI("#{@api_url}/users/@me")
61
- req = Net::HTTP::Get.new(uri)
62
- _add_auth_header(req)
63
- res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
64
- http.request(req)
65
- end
66
-
67
- if res.is_a?(Net::HTTPSuccess)
68
- bot_user_data = JSON.parse(res.body)
69
- @logger.debug "Response Body (successful login attempt from /users/@me): #{res.body}"
70
- @user_id = bot_user_data&.[]('_id')
71
- @bot_name = bot_user_data&.[]('username')
72
- @bot_discriminator = bot_user_data&.[]('discriminator')
73
- bot_specific_info = bot_user_data&.[]('bot')
74
- @bot_owner_id = bot_specific_info&.[]('owner')
75
- @bot_discoverable = nil
76
- @bot_creation_date = Time.at(bot_user_data&.[]('created_at').to_i / 1000) rescue nil
77
- if @user_id.nil? || @bot_name.nil?
78
- @logger.debug "Error: Essential properties (_id, username) missing or nil in API response from /users/@me."
79
- @logger.debug "Please inspect the 'Response Body' above for unexpected format."
80
- return false
81
- end
82
-
83
- @logger.debug "Successfully identified as #{@bot_name} (ID: #{@user_id}) via REST API."
84
- @logger.debug "Bot owner ID after parsing: #{@bot_owner_id}"
85
- @logger.debug "Owner ID: #{@bot_owner_id}, Discoverable: #{@bot_discoverable.inspect}, Created: #{@bot_creation_date}"
86
- else
87
- @logger.debug "Initial REST API call failed: #{res.message} (Code: #{res.code})"
88
- @logger.debug "Response Body: #{res.body}"
89
- @logger.debug "Please check your bot token. Cannot proceed with WebSocket connection."
90
- return false
91
- end
92
- # Step 2: Connect to the WebSocket
93
- @running = true
94
- connect_websocket
95
- @request_queue.start_processing
96
- true
97
- end
98
-
99
- def connect_websocket
100
- if @websocket && @websocket.open? && @running
101
- @logger.debug "WebSocket already open and running."
102
- return
103
- end
104
-
105
- @logger.debug "Connecting to WebSocket: #{@websocket_url}"
106
- ws_url_with_token = "#{@websocket_url}?token=#{@token}"
107
- bot_instance = self
108
- thread_logger = @logger
109
-
110
- @websocket_thread = Thread.new do
111
- begin
112
- @websocket = WebSocket::Client::Simple.connect ws_url_with_token
113
- @websocket.on :open do
114
- thread_logger.debug "WebSocket connection opened!"
115
- end
116
- @websocket.on :message do |msg|
117
- bot_instance.handle_websocket_message(msg.data)
118
- end
119
- @websocket.on :close do |e|
120
- close_code = e&.code || 'N/A'
121
- close_reason = e&.reason || 'No reason provided'
122
- thread_logger.debug "WebSocket closed: #{close_code} - #{close_reason}."
123
- bot_instance.instance_variable_set(:@websocket, nil)
124
- if bot_instance.instance_variable_get(:@running)
125
- thread_logger.debug "Attempting to reconnect in 5 seconds..."
126
- sleep 5
127
- bot_instance.connect_websocket
128
- else
129
- thread_logger.debug "BOT: Bot has stopped and will not try to reconnect" # Use local thread_logger
130
- end
131
- end
132
- @websocket.on :error do |e|
133
- error_message = e&.message || 'Unknown error'
134
- thread_logger.debug "WebSocket error: #{error_message}"
135
- @websocket.close if @websocket&.open?
136
- end
137
-
138
- while bot_instance.instance_variable_get(:@running)
139
- if Time.now.to_i - @last_heartbeat_sent > @heartbeat_interval
140
- bot_instance.send_heartbeat
141
- end
142
- sleep 1
143
- end
144
- thread_logger.debug "WebSocket thread loop finished."
145
- rescue => e
146
- thread_logger.debug "WebSocket thread unhandled exception: #{e.message}"
147
- thread_logger.debug e.backtrace.join("\n")
148
- bot_instance.instance_variable_set(:@websocket, nil)
149
- if bot_instance.instance_variable_get(:@running)
150
- thread_logger.debug "Attempting to reconnect in 5 seconds due to unhandled error..."
151
- sleep 5
152
- bot_instance.connect_websocket
153
- else
154
- thread_logger.debug "Bot is stopped, not attempting to reconnect after unhandled error."
155
- end
156
- end
157
- end
158
- sleep 1
159
- end
160
-
161
- def send_heartbeat
162
- if @websocket && @websocket.open?
163
- payload = { type: 'Ping', data: Time.now.to_i }
164
- @websocket.send(payload.to_json)
165
- @last_heartbeat_sent = Time.now.to_i
166
- end
167
- rescue OpenSSL::SSL::SSLError => e
168
- @logger.debug "AN ERROR HAS OCCURED: Error sending heartbeat (SSL): #{e.message}"
169
- @websocket&.close
170
- rescue => e
171
- @logger.debug "AN ERROR HAS OCCURED: Error sending heartbeat: #{e.message}"
172
- @websocket&.close
173
- end
174
-
175
- def handle_websocket_message(raw_data)
176
- begin
177
- event = JSON.parse(raw_data)
178
- event_type = event['type']
179
- case event_type
180
- when 'Ready'
181
- @logger.debug "Received 'Ready' event. Populating initial data..."
182
- if event['servers']
183
- event['servers'].each do |server_data|
184
- @servers[server_data['_id']] = {
185
- 'name' => server_data['name'],
186
- 'id' => server_data['_id']
187
- }
188
- @logger.debug "Stored server ID from Ready event: #{server_data['_id'].inspect}" # New debug
189
- end
190
- @logger.debug "Loaded #{event['servers'].count} real servers from 'Ready' event."
191
- else
192
- @logger.debug "'Ready' event received but no 'servers' array found."
193
- end
194
- @ready_event_received = true
195
- @logger.debug "@ready_event_received set to true."
196
- when 'Message'
197
- unless event['author'] == @user_id
198
- process_message(event)
199
- end
200
- when 'Authenticated'
201
- @logger.debug "Successfully authenticated with WebSocket."
202
- when 'Pong'
203
- # @logger.debug "Received Pong response."
204
- when 'Error'
205
- @logger.debug "AN ERROR HAS OCCURED: Revolt API Error received via WebSocket: #{event['error']}"
206
- else
207
- # @logger.debug "AN ERROR HAS OCCURED: Unhandled WebSocket event type: #{event_type}"
208
- end
209
- rescue JSON::ParserError => e
210
- @logger.debug "Failed to parse WebSocket message as JSON: #{e.message}"
211
- @logger.debug "Raw message: #{raw_data}"
212
- rescue => e
213
- @logger.debug "Error processing WebSocket message: #{e.message}"
214
- @logger.debug e.backtrace.join("\n")
215
- end
216
- end
217
-
218
- def on_message(&block)
219
- @message_handlers << block
220
- end
221
- def command(command_name, required_permissions: [], nsfw_channel_required: false, &block)
222
- cmd_key = command_name.to_s.downcase
223
- @commands[cmd_key] = {
224
- 'block' => block,
225
- 'permissions' => required_permissions,
226
- 'nsfw_cmd' => nsfw_channel_required
227
- }
228
- @logger.debug "Command '#{command_name}' registered with permissions: #{required_permissions.inspect}, NSFW required: #{nsfw_channel_required}."
229
- end
230
-
231
- def check_permissions(message, required_permissions, nsfw_channel_required)
232
- @logger.debug "check_permissions called with required_permissions: #{required_permissions.inspect}, NSFW required: #{nsfw_channel_required}."
233
- @logger.debug "message['channel'] value: #{message['channel'].inspect}"
234
-
235
- server_id_from_message = message['member']&.[]('_id')&.[]('server')
236
- @logger.debug "message['member']['_id']['server'] value: #{server_id_from_message.inspect}"
237
-
238
- if required_permissions.empty? && !nsfw_channel_required
239
- @logger.debug "Permission Check: No specific permissions or NSFW requirement. Allowing command."
240
- return true
241
- end
242
- user_id = message['author']
243
-
244
- @logger.debug "\n--- PERMISSION DEBUG START ---"
245
- @logger.debug "User ID from message (user_id): '#{user_id}' | Bot Owner ID stored (@bot_owner_id): '#{@bot_owner_id}'"
246
- @logger.debug "Are they equal? (user_id == @bot_owner_id): #{user_id == @bot_owner_id}"
247
- # @logger.debug "User ID char codes: #{user_id.each_char.map(&:ord).join(', ')}"
248
- # @logger.debug "Bot Owner ID char codes: #{@bot_owner_id.each_char.map(&:ord).join(', ')}"
249
- @logger.debug "--- PERMISSION DEBUG END ---\n"
250
- # --- BotOwner Check ---
251
- if required_permissions.include?('BotOwner')
252
- if user_id == @bot_owner_id
253
- @logger.debug "Permission Check: User is the bot owner. Allowing command."
254
- return true
255
- else
256
- @logger.debug "Permission Check: User is NOT the bot owner. Denying command."
257
- return false
258
- end
259
- end
260
- # --- NSFW Channel Check ---
261
- channel_id = message['channel']
262
- if server_id_from_message.nil?
263
- if nsfw_channel_required
264
- @logger.debug "Permission Check: Command requires NSFW channel, but message is in DM. Denying command."
265
- return false
266
- end
267
- else
268
- channel_details = get_channel_details(channel_id)
269
- if channel_details.nil?
270
- @logger.debug "Permission Check: Command requires NSFW channel but could not retrieve channel details. Denying command to be safe."
271
- return false
272
- end
273
- is_channel_nsfw = channel_details['nsfw'] || false
274
- if nsfw_channel_required && !is_channel_nsfw
275
- @logger.debug "Permission Check: Command requires NSFW channel, but current channel is NOT NSFW marked. Denying."
276
- return false
277
- elsif !nsfw_channel_required && is_channel_nsfw
278
- @logger.debug "Permission Check: Command does not require NSFW channel, but is in NSFW marked channel. Allowing."
279
- end
280
- end
281
-
282
- if !required_permissions.empty?
283
- @logger.debug "User #{user_id} is not the bot owner. Requires permissions: #{required_permissions.inspect}. (Full permission check for non-owner, server-specific permissions not implemented)."
284
- return false
285
- end
286
-
287
- @logger.debug "Permission Check: All checks passed. Allowing command."
288
- true
289
- end
290
-
291
- def process_message(message)
292
- unless @ready_event_received
293
- @logger.debug "AN ERROR HAS OCCURED: Bot is not ready for commands"
294
- return
295
- end
296
-
297
- unless @selfbot
298
- return if message['author'] == @user_id
299
- end
300
-
301
- content = message['content']&.strip
302
- return if content.nil? || content.empty?
303
- @logger.debug "Full Message object received in process_message: #{message.inspect}"
304
-
305
- @commands.each do |cmd_name, cmd_data|
306
- command_full_string = "#{@prefix}#{cmd_name}"
307
- if content.downcase.start_with?(command_full_string.downcase)
308
- args_string = content[command_full_string.length..]&.strip
309
- args = args_string.to_s.split(/\s+/)
310
- args = [] if args == ['']
311
- # --- Permission Check (Pass both permissions and nsfw_channel_required to check_permissions) ---
312
- if check_permissions(message, cmd_data['permissions'], cmd_data['nsfw_cmd'])
313
- @logger.debug "Executing command: '#{cmd_name}' with args: #{args.inspect}"
314
- cmd_data['block'].call(message, args)
315
- else
316
- channel_id = message['channel']
317
- if cmd_data['nsfw_cmd'] && message['member']
318
- channel_details = get_channel_details(channel_id)
319
- if channel_details && !channel_details['nsfw']
320
- self.send_message(channel_id, text: "⛔ A NSFW marked channel is required to use the following command: `#{cmd_name}`")
321
- else
322
- self.send_message(channel_id, text: "⛔ You don't have permission to use the `#{cmd_name}` command.")
323
- end
324
- else
325
- self.send_message(channel_id, text: "⛔ You don't have permission to use the `#{cmd_name}` command.")
326
- end
327
- end
328
- return
329
- end
330
- end
331
-
332
- @message_handlers.each do |handler|
333
- @logger.debug "Calling general message handler for: '#{content}'"
334
- handler.call(message)
335
- end
336
- end
337
-
338
- def send_message(channel_id, text: nil, embeds: nil, masquerade_name: nil, masquerade_avatar_url: nil)
339
- if text.nil? && (embeds.nil? || embeds.empty?)
340
- @logger.debug "AN ERROR HAS OCCURED: Cannot send empty message or embeds."
341
- return
342
- end
343
-
344
- payload = {}
345
- payload[:content] = text if text
346
-
347
- if embeds && !embeds.empty?
348
- filtered_embeds = embeds.map do |embed|
349
- supported_keys = ['title', 'description', 'color', 'url', 'image']
350
- embed.select { |k, v| supported_keys.include?(k.to_s) }
351
- end
352
- payload[:embeds] = filtered_embeds
353
- end
354
- if masquerade_name || masquerade_avatar_url
355
- payload[:masquerade] = {}
356
- payload[:masquerade][:name] = masquerade_name if masquerade_name
357
- payload[:masquerade][:avatar] = masquerade_avatar_url if masquerade_avatar_url
358
- end
359
-
360
- @request_queue.enqueue do
361
- @logger.debug "Attempting to send message to channel '#{channel_id}' via queue..."
362
- uri = URI("#{@api_url}/channels/#{channel_id}/messages")
363
- req = Net::HTTP::Post.new(uri)
364
- _add_auth_header(req)
365
- req['Content-Type'] = 'application/json'
366
- req.body = payload.to_json
367
-
368
- res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
369
- http.request(req)
370
- end
371
-
372
- if res.is_a?(Net::HTTPSuccess)
373
- @logger.debug "Message sent successfully!"
374
- else
375
- @logger.debug "AN ERROR HAS OCCURED: Failed to send message: #{res.message} (Code: #{res.code})"
376
- @logger.debug "Response Body: #{res.body}"
377
- end
378
- end
379
- rescue => e
380
- @logger.debug "AN ERROR HAS OCCURED: Error enqueuing message: #{e.message}"
381
- @logger.debug e.backtrace.join("\n")
382
- end
383
-
384
-
385
- def add_reaction(channel_id, message_id, emoji_id)
386
- @request_queue.enqueue do
387
- @logger.debug "Attempting to add reaction '#{emoji_id}' to message '#{message_id}' in channel '#{channel_id}' via queue."
388
- uri = URI("#{@api_url}/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji_id}")
389
- req = Net::HTTP::Put.new(uri) # Reactions use PUT
390
- req['x-bot-token'] = @token
391
- res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
392
- http.request(req)
393
- end
394
- if res.is_a?(Net::HTTPSuccess) || res.code == '204'
395
- @logger.debug "Reaction has been added successfully!"
396
- else
397
- @logger.debug "AN ERROR HAS OCCURED: Failed to add reaction: #{res.message} (Code: #{res.code})"
398
- @logger.debug "Response Body: #{res.body}"
399
- end
400
- end
401
- rescue => e
402
- @logger.debug "AN ERROR HAS OCCURED: Error enqueuing add reaction: #{e.message}"
403
- @logger.debug e.backtrace.join("\n")
404
- end
405
-
406
- def remove_reaction(channel_id, message_id, emoji_id, user_id: nil)
407
- target_user_id = user_id || @user_id # Default to bot's own ID
408
- @request_queue.enqueue do
409
- @logger.debug "Attempting to remove reaction '#{emoji_id}' from message '#{message_id}' by user '#{target_user_id}' in channel '#{channel_id}' via queue."
410
- uri = URI("#{@api_url}/channels/#{channel_id}/messages/#{message_id}/reactions/#{emoji_id}?user_id=#{target_user_id}")
411
- req = Net::HTTP::Delete.new(uri)
412
- req['x-bot-token'] = @token
413
- res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
414
- http.request(req)
415
- end
416
- if res.is_a?(Net::HTTPSuccess) || res.code == '204'
417
- @logger.debug "Reaction has been removed successfully!"
418
- else
419
- @logger.debug "AN ERROR HAS OCCURED: Failed to remove reaction: #{res.message} (Code: #{res.code})"
420
- @logger.debug "Response Body: #{res.body}"
421
- end
422
- end
423
- rescue => e
424
- @logger.debug "AN ERROR HAS OCCURED: Error enqueuing remove reaction: #{e.message}"
425
- @logger.debug e.backtrace.join("\n")
426
- end
427
-
428
- def get_channel_details(channel_id)
429
- @logger.debug "Fetching channel details for ID: #{channel_id} (direct API call for permission check)."
430
- uri = URI("#{@api_url}/channels/#{channel_id}")
431
- req = Net::HTTP::Get.new(uri)
432
- req['x-bot-token'] = @token
433
-
434
- res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
435
- http.request(req)
436
- end
437
-
438
- if res.is_a?(Net::HTTPSuccess)
439
- JSON.parse(res.body)
440
- else
441
- @logger.debug "AN ERROR HAS OCCURED: Failed to fetch channel details for #{channel_id}: #{res.message} (Code: #{res.code})"
442
- nil
443
- end
444
- rescue => e
445
- @logger.debug "AN ERROR HAS OCCURED: Error fetching channel details: #{e.message}"
446
- nil
447
- end
448
-
449
- def get_server_info(server_id)
450
- @logger.debug "get_server_info called with server_id: '#{server_id}' (Type: #{server_id.class}, Length: #{server_id.length})"
451
- @logger.debug "Available server IDs in cache (@servers.keys): #{@servers.keys.inspect}"
452
- found_server = @servers[server_id]
453
- @logger.debug "Result of @servers[server_id]: #{found_server.inspect}"
454
- found_server
455
- end
456
- def get_server_name(server_id)
457
- @servers[server_id]&.[]('name')
458
- end
459
-
460
- def stop
461
- @logger.debug "Stopping bot..."
462
- @running = false
463
- if @websocket_thread && @websocket_thread.alive?
464
- unless @websocket_thread.join(5)
465
- @logger.debug "WebSocket thread did not terminate gracefully, forcing kill."
466
- @websocket_thread.kill
467
- end
468
- @logger.debug "WebSocket thread terminated."
469
- end
470
- if @websocket && @websocket.open?
471
- @websocket.close
472
- @logger.debug "WebSocket closed."
473
- end
474
- @request_queue.stop_processing
475
- @logger.debug "Bot stopped."
476
- end
477
-
478
- private
479
-
480
- def _add_auth_header(request)
481
- if @selfbot
482
- request['x-session-token'] = @token
483
- else
484
- request['x-bot-token'] = @token
485
- end
486
- end
487
- end
1
+ # lib/revoltrb/bot.rb
2
+
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'websocket-client-simple' # This is required for WebSocket communication
6
+ require 'thread'
7
+ require 'time'
8
+ require_relative 'debuglogger'
9
+ require_relative 'request_queue'
10
+
11
+ module Revoltrb
12
+ class RevoltBot
13
+ EMOJI_MAP = {
14
+ ':grinning:' => '😀',
15
+ ':heart:' => '❤️',
16
+ ':joy:' => '😂',
17
+ ':unamused:' => '😒',
18
+ ':sunglasses:' => '😎',
19
+ ':thinking:' => '🤔',
20
+ ':clap:' => '👏',
21
+ ':thumbsup:' => '👍',
22
+ ':thumbsdown:' => '👎',
23
+ ':point_up:' => '☝️',
24
+ ':+1:' => '👍',
25
+ ':-1:' => '👎'
26
+ }.freeze
27
+
28
+ attr_reader :token, :user_id, :bot_name, :servers, :prefix, :bot_owner_id, :bot_discriminator, :bot_discoverable, :bot_creation_date
29
+ attr_accessor :websocket_url, :api_url, :cdn_url
30
+ # Initializes the bot with the provided token, API endpoints, and configuration.
31
+ def initialize(token, api_url: 'https://api.revolt.chat', websocket_url: 'wss://app.revolt.chat/events', cdn_url: 'https://cdn.revoltusercontent.com', prefix: nil, debuglogs: false, selfbot: false)
32
+ @token = token
33
+ @api_url = api_url
34
+ @websocket_url = websocket_url
35
+ @cdn_url = cdn_url
36
+
37
+ @user_id = nil
38
+ @bot_name = nil
39
+ @servers = {}
40
+ @commands = {}
41
+ @message_handlers = []
42
+
43
+ @websocket = nil
44
+ @websocket_thread = nil
45
+ @heartbeat_interval = 30 # Default heartbeat interval in seconds
46
+ @last_heartbeat_sent = Time.now.to_i
47
+ @running = false
48
+ @ready_event_received = false
49
+ @logger = Revoltrb::DebugLogger.new(debuglogs)
50
+ @request_queue = Revoltrb::RequestQueue.new(500)
51
+
52
+ @prefix = "!"
53
+ @prefix = prefix if prefix
54
+ @selfbot = selfbot
55
+ @logger.debug "RevoltBot initialized. API: #{@api_url}, WS: #{@websocket_url}, CDN: #{@cdn_url}, Prefix: #{@prefix}"
56
+ end
57
+
58
+ def get_botinfo
59
+ {
60
+ 'bot_id' => @user_id,
61
+ 'bot_name' => @bot_name,
62
+ 'bot_discriminator' => @bot_discriminator,
63
+ 'bot_ownerid' => @bot_owner_id,
64
+ 'bot_creationdate' => @bot_creation_date,
65
+ 'bot_flags' => @bot_flags,
66
+ 'bot_discoverable' => @bot_discoverable,
67
+ 'bot_public' => @bot_public,
68
+ 'bot_analytics' => @bot_analytics,
69
+ 'bot_prefix' => @prefix,
70
+ 'bot_token' => @token
71
+ }
72
+ end
73
+
74
+ # Log into the Revolt.chat bot
75
+ def login
76
+ @logger.debug "BOT: Bot attempting to start...."
77
+ # Step 1: Fetch initial bot user details via REST API using /users/@me with X-Bot-Token | As confirmed, /users/@me returns the bot's user object directly.
78
+ uri = URI("#{@api_url}/users/@me")
79
+ req = Net::HTTP::Get.new(uri)
80
+ _add_auth_header(req)
81
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
82
+ http.request(req)
83
+ end
84
+
85
+ if res.is_a?(Net::HTTPSuccess)
86
+ bot_user_data = JSON.parse(res.body)
87
+ @logger.debug "Response Body (successful login attempt from /users/@me): #{res.body}"
88
+ @user_id = bot_user_data&.[]('_id')
89
+ @bot_name = bot_user_data&.[]('username')
90
+ @bot_discriminator = bot_user_data&.[]('discriminator')
91
+ bot_specific_info = bot_user_data&.[]('bot')
92
+ @bot_owner_id = bot_specific_info&.[]('owner')
93
+ @bot_flags = bot_specific_info&.[]('flags')
94
+ @bot_discoverable = nil
95
+ @bot_public = nil
96
+ @bot_analytics = nil
97
+ @bot_creation_date = Time.at(bot_user_data&.[]('created_at').to_i / 1000) rescue nil
98
+ if @user_id.nil? || @bot_name.nil?
99
+ @logger.debug "Error: Essential properties (_id, username) missing or nil in API response from /users/@me."
100
+ @logger.debug "Please inspect the 'Response Body' above for unexpected format."
101
+ return false
102
+ end
103
+
104
+ @logger.debug "Successfully identified as #{@bot_name} (ID: #{@user_id}) via REST API."
105
+ @logger.debug "Bot owner ID after parsing: #{@bot_owner_id}"
106
+ @logger.debug "Owner ID: #{@bot_owner_id}, Discoverable: #{@bot_discoverable.inspect}, Created: #{@bot_creation_date}"
107
+ else
108
+ @logger.debug "Initial REST API call failed: #{res.message} (Code: #{res.code})"
109
+ @logger.debug "Response Body: #{res.body}"
110
+ @logger.debug "Please check your bot token. Cannot proceed with WebSocket connection."
111
+ return false
112
+ end
113
+ # Step 2: Connect to the WebSocket
114
+ @running = true
115
+ connect_websocket
116
+ @request_queue.start_processing
117
+ true
118
+ end
119
+
120
+ def connect_websocket
121
+ if @websocket && @websocket.open? && @running
122
+ @logger.debug "WebSocket already open and running."
123
+ return
124
+ end
125
+
126
+ @logger.debug "Connecting to WebSocket: #{@websocket_url}"
127
+ ws_url_with_token = "#{@websocket_url}?token=#{@token}"
128
+ bot_instance = self
129
+ thread_logger = @logger
130
+
131
+ @websocket_thread = Thread.new do
132
+ begin
133
+ @websocket = WebSocket::Client::Simple.connect ws_url_with_token
134
+ @websocket.on :open do
135
+ thread_logger.debug "WebSocket connection opened!"
136
+ end
137
+ @websocket.on :message do |msg|
138
+ bot_instance.handle_websocket_message(msg.data)
139
+ end
140
+ @websocket.on :close do |e|
141
+ close_code = e&.code || 'N/A'
142
+ close_reason = e&.reason || 'No reason provided'
143
+ thread_logger.debug "WebSocket closed: #{close_code} - #{close_reason}."
144
+ bot_instance.instance_variable_set(:@websocket, nil)
145
+ if bot_instance.instance_variable_get(:@running)
146
+ thread_logger.debug "Attempting to reconnect in 5 seconds..."
147
+ sleep 5
148
+ bot_instance.connect_websocket
149
+ else
150
+ thread_logger.debug "BOT: Bot has stopped and will not try to reconnect" # Use local thread_logger
151
+ end
152
+ end
153
+ @websocket.on :error do |e|
154
+ error_message = e&.message || 'Unknown error'
155
+ thread_logger.debug "WebSocket error: #{error_message}"
156
+ @websocket.close if @websocket&.open?
157
+ end
158
+
159
+ while bot_instance.instance_variable_get(:@running)
160
+ if Time.now.to_i - @last_heartbeat_sent > @heartbeat_interval
161
+ bot_instance.send_heartbeat
162
+ end
163
+ sleep 1
164
+ end
165
+ thread_logger.debug "WebSocket thread loop finished."
166
+ rescue => e
167
+ thread_logger.debug "WebSocket thread unhandled exception: #{e.message}"
168
+ thread_logger.debug e.backtrace.join("\n")
169
+ bot_instance.instance_variable_set(:@websocket, nil)
170
+ if bot_instance.instance_variable_get(:@running)
171
+ thread_logger.debug "Attempting to reconnect in 5 seconds due to unhandled error..."
172
+ sleep 5
173
+ bot_instance.connect_websocket
174
+ else
175
+ thread_logger.debug "Bot is stopped, not attempting to reconnect after unhandled error."
176
+ end
177
+ end
178
+ end
179
+ sleep 1
180
+ end
181
+
182
+ def send_heartbeat
183
+ if @websocket && @websocket.open?
184
+ payload = { type: 'Ping', data: Time.now.to_i }
185
+ @websocket.send(payload.to_json)
186
+ @last_heartbeat_sent = Time.now.to_i
187
+ end
188
+ rescue OpenSSL::SSL::SSLError => e
189
+ @logger.debug "AN ERROR HAS OCCURED: Error sending heartbeat (SSL): #{e.message}"
190
+ @websocket&.close
191
+ rescue => e
192
+ @logger.debug "AN ERROR HAS OCCURED: Error sending heartbeat: #{e.message}"
193
+ @websocket&.close
194
+ end
195
+
196
+ def handle_websocket_message(raw_data)
197
+ begin
198
+ event = JSON.parse(raw_data)
199
+ event_type = event['type']
200
+ case event_type
201
+ when 'Ready'
202
+ @logger.debug "Received 'Ready' event. Populating initial data..."
203
+ if event['servers']
204
+ event['servers'].each do |server_data|
205
+ @servers[server_data['_id']] = {
206
+ 'name' => server_data['name'],
207
+ 'id' => server_data['_id']
208
+ }
209
+ @logger.debug "Stored server ID from Ready event: #{server_data['_id'].inspect}" # New debug
210
+ end
211
+ @logger.debug "Loaded #{event['servers'].count} real servers from 'Ready' event."
212
+ else
213
+ @logger.debug "'Ready' event received but no 'servers' array found."
214
+ end
215
+ @ready_event_received = true
216
+ @logger.debug "@ready_event_received set to true."
217
+ when 'Message'
218
+ unless event['author'] == @user_id
219
+ process_message(event)
220
+ end
221
+ when 'Authenticated'
222
+ @logger.debug "Successfully authenticated with WebSocket."
223
+ when 'Pong'
224
+ # @logger.debug "Received Pong response."
225
+ when 'Error'
226
+ @logger.debug "AN ERROR HAS OCCURED: Revolt API Error received via WebSocket: #{event['error']}"
227
+ else
228
+ # @logger.debug "AN ERROR HAS OCCURED: Unhandled WebSocket event type: #{event_type}"
229
+ end
230
+ rescue JSON::ParserError => e
231
+ @logger.debug "Failed to parse WebSocket message as JSON: #{e.message}"
232
+ @logger.debug "Raw message: #{raw_data}"
233
+ rescue => e
234
+ @logger.debug "Error processing WebSocket message: #{e.message}"
235
+ @logger.debug e.backtrace.join("\n")
236
+ end
237
+ end
238
+
239
+ def on_message(&block)
240
+ @message_handlers << block
241
+ end
242
+ def command(command_name, required_permissions: [], nsfw_channel_required: false, &block)
243
+ cmd_key = command_name.to_s.downcase
244
+ @commands[cmd_key] = {
245
+ 'block' => block,
246
+ 'permissions' => required_permissions,
247
+ 'nsfw_cmd' => nsfw_channel_required
248
+ }
249
+ @logger.debug "Command '#{command_name}' registered with permissions: #{required_permissions.inspect}, NSFW required: #{nsfw_channel_required}."
250
+ end
251
+
252
+ def check_permissions(message, required_permissions, nsfw_channel_required)
253
+ @logger.debug "check_permissions called with required_permissions: #{required_permissions.inspect}, NSFW required: #{nsfw_channel_required}."
254
+ @logger.debug "message['channel'] value: #{message['channel'].inspect}"
255
+
256
+ server_id_from_message = message['member']&.[]('_id')&.[]('server')
257
+ @logger.debug "message['member']['_id']['server'] value: #{server_id_from_message.inspect}"
258
+
259
+ if required_permissions.empty? && !nsfw_channel_required
260
+ @logger.debug "Permission Check: No specific permissions or NSFW requirement. Allowing command."
261
+ return true
262
+ end
263
+ user_id = message['author']
264
+
265
+ @logger.debug "\n--- PERMISSION DEBUG START ---"
266
+ @logger.debug "User ID from message (user_id): '#{user_id}' | Bot Owner ID stored (@bot_owner_id): '#{@bot_owner_id}'"
267
+ @logger.debug "Are they equal? (user_id == @bot_owner_id): #{user_id == @bot_owner_id}"
268
+ # @logger.debug "User ID char codes: #{user_id.each_char.map(&:ord).join(', ')}"
269
+ # @logger.debug "Bot Owner ID char codes: #{@bot_owner_id.each_char.map(&:ord).join(', ')}"
270
+ @logger.debug "--- PERMISSION DEBUG END ---\n"
271
+ # --- BotOwner Check ---
272
+ if required_permissions.include?('BotOwner')
273
+ if user_id == @bot_owner_id
274
+ @logger.debug "Permission Check: User is the bot owner. Allowing command."
275
+ return true
276
+ else
277
+ @logger.debug "Permission Check: User is NOT the bot owner. Denying command."
278
+ return false
279
+ end
280
+ end
281
+ # --- NSFW Channel Check ---
282
+ channel_id = message['channel']
283
+ if server_id_from_message.nil?
284
+ if nsfw_channel_required
285
+ @logger.debug "Permission Check: Command requires NSFW channel, but message is in DM. Denying command."
286
+ return false
287
+ end
288
+ else
289
+ channel_details = get_channel_details(channel_id)
290
+ if channel_details.nil?
291
+ @logger.debug "Permission Check: Command requires NSFW channel but could not retrieve channel details. Denying command to be safe."
292
+ return false
293
+ end
294
+ is_channel_nsfw = channel_details['nsfw'] || false
295
+ if nsfw_channel_required && !is_channel_nsfw
296
+ @logger.debug "Permission Check: Command requires NSFW channel, but current channel is NOT NSFW marked. Denying."
297
+ return false
298
+ elsif !nsfw_channel_required && is_channel_nsfw
299
+ @logger.debug "Permission Check: Command does not require NSFW channel, but is in NSFW marked channel. Allowing."
300
+ end
301
+ end
302
+
303
+ if !required_permissions.empty?
304
+ @logger.debug "User #{user_id} is not the bot owner. Requires permissions: #{required_permissions.inspect}. (Full permission check for non-owner, server-specific permissions not implemented)."
305
+ return false
306
+ end
307
+ @logger.debug "Permission Check: All checks passed. Allowing command."
308
+ true
309
+ end
310
+
311
+ def process_message(message)
312
+ unless @ready_event_received
313
+ @logger.debug "AN ERROR HAS OCCURED: Bot is not ready for commands"
314
+ return
315
+ end
316
+
317
+ unless @selfbot
318
+ return if message['author'] == @user_id
319
+ end
320
+
321
+ content = message['content']&.strip
322
+ return if content.nil? || content.empty?
323
+ @logger.debug "Full Message object received in process_message: #{message.inspect}"
324
+
325
+ @commands.each do |cmd_name, cmd_data|
326
+ command_full_string = "#{@prefix}#{cmd_name}"
327
+ if content.downcase.start_with?(command_full_string.downcase)
328
+ args_string = content[command_full_string.length..]&.strip
329
+ args = args_string.to_s.split(/\s+/)
330
+ args = [] if args == ['']
331
+ # --- Permission Check (Pass both permissions and nsfw_channel_required to check_permissions) ---
332
+ if check_permissions(message, cmd_data['permissions'], cmd_data['nsfw_cmd'])
333
+ @logger.debug "Executing command: '#{cmd_name}' with args: #{args.inspect}"
334
+ cmd_data['block'].call(message, args)
335
+ else
336
+ channel_id = message['channel']
337
+ if cmd_data['nsfw_cmd'] && message['member']
338
+ channel_details = get_channel_details(channel_id)
339
+ if channel_details && !channel_details['nsfw']
340
+ self.send_message(channel_id, text: " A NSFW marked channel is required to use the following command: `#{cmd_name}`")
341
+ else
342
+ self.send_message(channel_id, text: "⛔ You don't have permission to use the `#{cmd_name}` command.")
343
+ end
344
+ else
345
+ self.send_message(channel_id, text: "⛔ You don't have permission to use the `#{cmd_name}` command.")
346
+ end
347
+ end
348
+ return
349
+ end
350
+ end
351
+
352
+ @message_handlers.each do |handler|
353
+ @logger.debug "Calling general message handler for: '#{content}'"
354
+ handler.call(message)
355
+ end
356
+ end
357
+
358
+ def send_message(channel_id, text: nil, embeds: nil, masquerade_name: nil, masquerade_avatar_url: nil)
359
+ if text.nil? && (embeds.nil? || embeds.empty?)
360
+ @logger.debug "AN ERROR HAS OCCURED: Cannot send empty message or embeds."
361
+ return
362
+ end
363
+
364
+ payload = {}
365
+ payload[:content] = text if text
366
+
367
+ if embeds && !embeds.empty?
368
+ filtered_embeds = embeds.map do |embed|
369
+ supported_keys = ['title', 'description', 'colour', 'url', 'icon_url', 'media']
370
+ embed.select { |k, v| supported_keys.include?(k.to_s) }
371
+ end
372
+ payload[:embeds] = filtered_embeds
373
+ end
374
+ if masquerade_name || masquerade_avatar_url
375
+ payload[:masquerade] = {}
376
+ payload[:masquerade][:name] = masquerade_name if masquerade_name
377
+ payload[:masquerade][:avatar] = masquerade_avatar_url if masquerade_avatar_url
378
+ end
379
+
380
+ @request_queue.enqueue do
381
+ @logger.debug "Attempting to send message to channel '#{channel_id}' via queue..."
382
+ uri = URI("#{@api_url}/channels/#{channel_id}/messages")
383
+ req = Net::HTTP::Post.new(uri)
384
+ _add_auth_header(req)
385
+ req['Content-Type'] = 'application/json'
386
+ req.body = payload.to_json
387
+
388
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
389
+ http.request(req)
390
+ end
391
+
392
+ if res.is_a?(Net::HTTPSuccess)
393
+ @logger.debug "Message sent successfully!"
394
+ else
395
+ @logger.debug "AN ERROR HAS OCCURED: Failed to send message: #{res.message} (Code: #{res.code})"
396
+ @logger.debug "Response Body: #{res.body}"
397
+ end
398
+ end
399
+ rescue => e
400
+ @logger.debug "AN ERROR HAS OCCURED: Error enqueuing message: #{e.message}"
401
+ @logger.debug e.backtrace.join("\n")
402
+ end
403
+
404
+ def find_unicode_emoji(shortcode)
405
+ EMOJI_MAP[shortcode]
406
+ end
407
+
408
+ def add_reaction(channel_id, message_id, emoji_id)
409
+ if emoji_id.start_with?(':') && emoji_id.end_with?(':')
410
+ @logger.debug "AN ERROR HAS OCCURED: Cannot add reaction with shortcode '#{emoji_id}'. Please use the actual Unicode emoji or a custom emoji ID."
411
+ return
412
+ end
413
+
414
+ @request_queue.enqueue do
415
+ @logger.debug "Attempting to add reaction '#{emoji_id}' to message '#{message_id}' in channel '#{channel_id}' via queue."
416
+ encoded_emoji_id = CGI.escape(emoji_id)
417
+
418
+ uri = URI("#{@api_url}/channels/#{channel_id}/messages/#{message_id}/reactions/#{encoded_emoji_id}")
419
+ req = Net::HTTP::Put.new(uri)
420
+ _add_auth_header(req)
421
+
422
+ res = nil
423
+ begin
424
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https', read_timeout: 10, open_timeout: 10) do |http|
425
+ http.request(req)
426
+ end
427
+ rescue Net::ReadTimeout => e
428
+ @logger.debug "AN ERROR HAS OCCURED: The network request timed out while waiting for a response after 10 seconds. Error: #{e.message}"
429
+ return
430
+ rescue Net::OpenTimeout => e
431
+ @logger.debug "AN ERROR HAS OCCURED: The network request timed out while trying to open a connection after 10 seconds. Error: #{e.message}"
432
+ return
433
+ rescue => e
434
+ @logger.debug "AN ERROR HAS OCCURED: An unexpected error occurred during the network request. Error: #{e.message}"
435
+ return
436
+ ensure
437
+ @logger.debug "Network request to add reaction finished."
438
+ end
439
+
440
+ @logger.debug "Received response with code: #{res.code}"
441
+ if res.is_a?(Net::HTTPSuccess) || res.code == '204'
442
+ @logger.debug "Reaction added successfully!"
443
+ else
444
+ @logger.debug "AN ERROR HAS OCCURED: Failed to add reaction: #{res.message} (Code: #{res.code})"
445
+ @logger.debug "Response Body: #{res.body}"
446
+ if res.code == '403'
447
+ @logger.debug "HINT: The bot may be missing the 'AddReactions' permission in this channel or server."
448
+ end
449
+ end
450
+ end
451
+ end
452
+
453
+ def remove_reaction(channel_id, message_id, emoji_id, user_id: nil)
454
+ target_user_id = user_id || @user_id
455
+ @request_queue.enqueue do
456
+ @logger.debug "Attempting to remove reaction '#{emoji_id}' from message '#{message_id}' by user '#{target_user_id}' in channel '#{channel_id}' via queue."
457
+
458
+ encoded_emoji_id = CGI.escape(emoji_id)
459
+
460
+ uri = URI("#{@api_url}/channels/#{channel_id}/messages/#{message_id}/reactions/#{encoded_emoji_id}?user_id=#{target_user_id}")
461
+ req = Net::HTTP::Delete.new(uri)
462
+ _add_auth_header(req)
463
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
464
+ http.request(req)
465
+ end
466
+ if res.is_a?(Net::HTTPSuccess) || res.code == '204'
467
+ @logger.debug "Reaction removed successfully!"
468
+ else
469
+ @logger.debug "AN ERROR HAS OCCURED: Failed to remove reaction: #{res.message} (Code: #{res.code})"
470
+ @logger.debug "Response Body: #{res.body}"
471
+ end
472
+ end
473
+ rescue => e
474
+ @logger.debug "AN ERROR HAS OCCURED: Error enqueuing remove reaction: #{e.message}"
475
+ @logger.debug e.backtrace.join("\n")
476
+ end
477
+
478
+ def get_channel_details(channel_id)
479
+ @logger.debug "Fetching channel details for ID: #{channel_id} (direct API call for permission check)."
480
+ uri = URI("#{@api_url}/channels/#{channel_id}")
481
+ req = Net::HTTP::Get.new(uri)
482
+ req['x-bot-token'] = @token
483
+
484
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
485
+ http.request(req)
486
+ end
487
+
488
+ if res.is_a?(Net::HTTPSuccess)
489
+ JSON.parse(res.body)
490
+ else
491
+ @logger.debug "AN ERROR HAS OCCURED: Failed to fetch channel details for #{channel_id}: #{res.message} (Code: #{res.code})"
492
+ nil
493
+ end
494
+ rescue => e
495
+ @logger.debug "AN ERROR HAS OCCURED: Error fetching channel details: #{e.message}"
496
+ nil
497
+ end
498
+
499
+ def get_server_info(server_id)
500
+ @logger.debug "get_server_info called with server_id: '#{server_id}' (Type: #{server_id.class}, Length: #{server_id.length})"
501
+ @logger.debug "Available server IDs in cache (@servers.keys): #{@servers.keys.inspect}"
502
+ found_server = @servers[server_id]
503
+ @logger.debug "Result of @servers[server_id]: #{found_server.inspect}"
504
+ found_server
505
+ end
506
+ def get_server_name(server_id)
507
+ @servers[server_id]&.[]('name')
508
+ end
509
+
510
+ def stop
511
+ @logger.debug "Stopping bot..."
512
+ @running = false
513
+ if @websocket_thread && @websocket_thread.alive?
514
+ unless @websocket_thread.join(5)
515
+ @logger.debug "WebSocket thread did not terminate gracefully, forcing kill."
516
+ @websocket_thread.kill
517
+ end
518
+ @logger.debug "WebSocket thread terminated."
519
+ end
520
+ if @websocket && @websocket.open?
521
+ @websocket.close
522
+ @logger.debug "WebSocket closed."
523
+ end
524
+ @request_queue.stop_processing
525
+ @logger.debug "Bot stopped."
526
+ end
527
+
528
+ private
529
+
530
+ def _add_auth_header(request)
531
+ if @selfbot
532
+ request['x-session-token'] = @token
533
+ else
534
+ request['x-bot-token'] = @token
535
+ end
536
+ end
537
+ end
488
538
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Revoltrb
4
4
  # The current version of revoltrb
5
- VERSION = "0.0.1"
5
+ VERSION = "0.0.2"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: revoltrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roxanne Studios
@@ -13,30 +13,30 @@ dependencies:
13
13
  name: json
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - ">="
16
+ - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '0'
18
+ version: 2.13.2
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - ">="
23
+ - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '0'
25
+ version: 2.13.2
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: net-http
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
- - - ">="
30
+ - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '0'
32
+ version: 0.6.0
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - ">="
37
+ - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '0'
39
+ version: 0.6.0
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: thread
42
42
  requirement: !ruby/object:Gem::Requirement