onyxcord 2.0.0 → 2.0.6
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 +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +55 -0
- data/README.md +64 -1
- data/lib/onyxcord/api.rb +58 -55
- data/lib/onyxcord/application_commands/command.rb +102 -0
- data/lib/onyxcord/application_commands/context.rb +99 -0
- data/lib/onyxcord/application_commands/option.rb +80 -0
- data/lib/onyxcord/application_commands/registry.rb +50 -0
- data/lib/onyxcord/application_commands.rb +11 -0
- data/lib/onyxcord/async/runtime.rb +30 -0
- data/lib/onyxcord/bot.rb +47 -15
- data/lib/onyxcord/data/interaction.rb +4 -0
- data/lib/onyxcord/event_executor.rb +56 -6
- data/lib/onyxcord/gateway.rb +15 -15
- data/lib/onyxcord/http.rb +115 -0
- data/lib/onyxcord/json.rb +49 -0
- data/lib/onyxcord/rate_limiter/async_rest.rb +149 -0
- data/lib/onyxcord/version.rb +1 -1
- data/lib/onyxcord/webhooks/version.rb +1 -1
- data/lib/onyxcord/websocket.rb +2 -15
- metadata +10 -4
- data/.devcontainer/Dockerfile +0 -13
- data/.devcontainer/devcontainer.json +0 -29
- data/.devcontainer/postcreate.sh +0 -4
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OnyxCord
|
|
4
|
+
module ApplicationCommands
|
|
5
|
+
class Registry
|
|
6
|
+
attr_reader :bot, :commands
|
|
7
|
+
|
|
8
|
+
def initialize(bot)
|
|
9
|
+
@bot = bot
|
|
10
|
+
@commands = {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def slash(name, description:, **attributes, &block)
|
|
14
|
+
register(Command.chat_input(name, description: description, **attributes, &block))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def user(name, **attributes, &block)
|
|
18
|
+
register(Command.user(name, **attributes, &block))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def message(name, **attributes, &block)
|
|
22
|
+
register(Command.message(name, **attributes, &block))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def register(command)
|
|
26
|
+
@commands[command.name] = command
|
|
27
|
+
wire_handler(command)
|
|
28
|
+
command
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def sync!(server_id: nil, delete_unknown: false)
|
|
32
|
+
payload = @commands.values.map(&:to_h)
|
|
33
|
+
|
|
34
|
+
if server_id
|
|
35
|
+
@bot.bulk_overwrite_guild_application_commands(server_id, payload)
|
|
36
|
+
else
|
|
37
|
+
@bot.bulk_overwrite_global_application_commands(payload)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def wire_handler(command)
|
|
44
|
+
@bot.application_command(command.name) do |event|
|
|
45
|
+
command.call(Context.new(event, command))
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'onyxcord/application_commands/option'
|
|
4
|
+
require 'onyxcord/application_commands/command'
|
|
5
|
+
require 'onyxcord/application_commands/context'
|
|
6
|
+
require 'onyxcord/application_commands/registry'
|
|
7
|
+
|
|
8
|
+
module OnyxCord
|
|
9
|
+
module ApplicationCommands
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'async'
|
|
4
|
+
|
|
5
|
+
module OnyxCord
|
|
6
|
+
module AsyncRuntime
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def run(&block)
|
|
10
|
+
current = Async::Task.current?
|
|
11
|
+
return yield current if current
|
|
12
|
+
|
|
13
|
+
Async(&block).wait
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def async(&block)
|
|
17
|
+
current = Async::Task.current?
|
|
18
|
+
return current.async(&block) if current
|
|
19
|
+
|
|
20
|
+
Async(&block)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def sleep(duration)
|
|
24
|
+
task = Async::Task.current?
|
|
25
|
+
return task.sleep(duration) if task.respond_to?(:sleep)
|
|
26
|
+
|
|
27
|
+
Kernel.sleep(duration)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/onyxcord/bot.rb
CHANGED
|
@@ -33,6 +33,7 @@ require 'onyxcord/api/invite'
|
|
|
33
33
|
require 'onyxcord/api/interaction'
|
|
34
34
|
require 'onyxcord/api/application'
|
|
35
35
|
|
|
36
|
+
require 'onyxcord/async/runtime'
|
|
36
37
|
require 'onyxcord/errors'
|
|
37
38
|
require 'onyxcord/message_components'
|
|
38
39
|
require 'onyxcord/data'
|
|
@@ -42,6 +43,7 @@ require 'onyxcord/websocket'
|
|
|
42
43
|
require 'onyxcord/cache'
|
|
43
44
|
require 'onyxcord/gateway'
|
|
44
45
|
|
|
46
|
+
require 'onyxcord/application_commands'
|
|
45
47
|
require 'onyxcord/voice/voice_bot'
|
|
46
48
|
|
|
47
49
|
module OnyxCord
|
|
@@ -309,28 +311,27 @@ module OnyxCord
|
|
|
309
311
|
# this. If you need a way to safely run code after the bot is fully
|
|
310
312
|
# connected, use a {#ready} event handler instead.
|
|
311
313
|
def run(background = false)
|
|
312
|
-
|
|
313
|
-
|
|
314
|
+
if background
|
|
315
|
+
@run_task = OnyxCord::AsyncRuntime.async { run_forever }
|
|
316
|
+
return @run_task
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
OnyxCord::AsyncRuntime.run { run_forever }
|
|
320
|
+
end
|
|
314
321
|
|
|
315
|
-
|
|
316
|
-
@gateway.
|
|
322
|
+
def run_forever
|
|
323
|
+
@gateway.run
|
|
317
324
|
end
|
|
318
325
|
|
|
319
|
-
# Joins the bot's connection thread with the current thread.
|
|
320
|
-
# This blocks execution until the websocket stops, which should only happen
|
|
321
|
-
# manually triggered. or due to an error. This is necessary to have a
|
|
322
|
-
# continuously running bot.
|
|
323
326
|
def join
|
|
324
|
-
@
|
|
327
|
+
@run_task&.wait
|
|
325
328
|
end
|
|
326
329
|
alias_method :sync, :join
|
|
327
330
|
|
|
328
|
-
# Stops the bot gracefully, disconnecting the websocket without immediately killing the thread. This means that
|
|
329
|
-
# Discord is immediately aware of the closed connection and makes the bot appear offline instantly.
|
|
330
|
-
# @note This method no longer takes an argument as of 3.4.0
|
|
331
331
|
def stop(_no_sync = nil)
|
|
332
332
|
@gateway.stop
|
|
333
333
|
@event_executor.shutdown
|
|
334
|
+
@run_task&.stop if @run_task.respond_to?(:stop)
|
|
334
335
|
nil
|
|
335
336
|
end
|
|
336
337
|
|
|
@@ -411,7 +412,7 @@ module OnyxCord
|
|
|
411
412
|
|
|
412
413
|
debug('Voice channel init packet sent! Now waiting.')
|
|
413
414
|
|
|
414
|
-
sleep(0.05) until @voices[server_id]
|
|
415
|
+
OnyxCord::AsyncRuntime.sleep(0.05) until @voices[server_id]
|
|
415
416
|
debug('Voice connect succeeded!')
|
|
416
417
|
@voices[server_id]
|
|
417
418
|
end
|
|
@@ -477,9 +478,9 @@ module OnyxCord
|
|
|
477
478
|
# @param enforce_nonce [true, false] Whether the nonce should be enforced and used for message de-duplication.
|
|
478
479
|
# @param poll [Hash, Poll::Builder, Poll, nil] The poll that should be attached to this message.
|
|
479
480
|
def send_temporary_message(channel, content, timeout, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0, nonce = nil, enforce_nonce = false, poll = nil)
|
|
480
|
-
|
|
481
|
+
OnyxCord::AsyncRuntime.async do
|
|
481
482
|
message = send_message(channel, content, tts, embeds, attachments, allowed_mentions, message_reference, components, flags, nonce, enforce_nonce, poll)
|
|
482
|
-
sleep(timeout)
|
|
483
|
+
OnyxCord::AsyncRuntime.sleep(timeout)
|
|
483
484
|
message.delete
|
|
484
485
|
end
|
|
485
486
|
|
|
@@ -864,6 +865,37 @@ module OnyxCord
|
|
|
864
865
|
ApplicationCommand.new(JSON.parse(resp), self, server_id)
|
|
865
866
|
end
|
|
866
867
|
|
|
868
|
+
# @return [OnyxCord::ApplicationCommands::Registry]
|
|
869
|
+
def commands
|
|
870
|
+
@modern_command_registry ||= OnyxCord::ApplicationCommands::Registry.new(self)
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
def slash(name, description: nil, **attributes, &block)
|
|
874
|
+
commands.slash(name, description: description, **attributes, &block)
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
def user_command(name, **attributes, &block)
|
|
878
|
+
commands.user(name, **attributes, &block)
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
def message_command(name, **attributes, &block)
|
|
882
|
+
commands.message(name, **attributes, &block)
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
def sync_application_commands!(server_id: nil, delete_unknown: false)
|
|
886
|
+
commands.sync!(server_id: server_id, delete_unknown: delete_unknown)
|
|
887
|
+
end
|
|
888
|
+
|
|
889
|
+
def bulk_overwrite_global_application_commands(commands)
|
|
890
|
+
response = API::Application.bulk_overwrite_global_commands(@token, profile.id, commands)
|
|
891
|
+
JSON.parse(response).map { |data| ApplicationCommand.new(data, self) }
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
def bulk_overwrite_guild_application_commands(server_id, commands)
|
|
895
|
+
response = API::Application.bulk_overwrite_guild_commands(@token, profile.id, server_id.resolve_id, commands)
|
|
896
|
+
JSON.parse(response).map { |data| ApplicationCommand.new(data, self, server_id) }
|
|
897
|
+
end
|
|
898
|
+
|
|
867
899
|
# @yieldparam [OptionBuilder]
|
|
868
900
|
# @yieldparam [PermissionBuilder]
|
|
869
901
|
# @example
|
|
@@ -299,6 +299,10 @@ module OnyxCord
|
|
|
299
299
|
Interactions::Message.new(JSON.parse(resp), @bot, self)
|
|
300
300
|
end
|
|
301
301
|
|
|
302
|
+
alias edit_original edit_response
|
|
303
|
+
alias delete_original delete_response
|
|
304
|
+
alias followup send_message
|
|
305
|
+
|
|
302
306
|
# @param message [String, Integer, InteractionMessage, Message] The message created by this interaction to be edited.
|
|
303
307
|
# @param content [String] The message content.
|
|
304
308
|
# @param embeds [Array<Hash, Webhooks::Embed>] The embeds for the message.
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'async'
|
|
3
|
+
require 'onyxcord/async/runtime'
|
|
4
4
|
|
|
5
5
|
module OnyxCord
|
|
6
|
-
# Event execution strategies used by bot dispatch.
|
|
7
6
|
module EventExecutor
|
|
8
7
|
STOP = Object.new.freeze
|
|
9
8
|
|
|
10
|
-
# Deterministic executor useful for tests, benchmarks, and tiny bots.
|
|
11
9
|
class Inline
|
|
12
10
|
def post
|
|
13
11
|
yield
|
|
@@ -20,8 +18,6 @@ module OnyxCord
|
|
|
20
18
|
end
|
|
21
19
|
end
|
|
22
20
|
|
|
23
|
-
# Async-based worker pool for event handlers.
|
|
24
|
-
# Uses Async tasks (fibers) instead of threads for lightweight concurrency.
|
|
25
21
|
class Pool
|
|
26
22
|
attr_reader :queue
|
|
27
23
|
|
|
@@ -47,7 +43,6 @@ module OnyxCord
|
|
|
47
43
|
@queue.size
|
|
48
44
|
end
|
|
49
45
|
|
|
50
|
-
# Compatibility with code that checks worker threads.
|
|
51
46
|
def threads
|
|
52
47
|
@workers
|
|
53
48
|
end
|
|
@@ -87,6 +82,59 @@ module OnyxCord
|
|
|
87
82
|
end
|
|
88
83
|
end
|
|
89
84
|
|
|
85
|
+
class AsyncPool
|
|
86
|
+
attr_reader :queue
|
|
87
|
+
|
|
88
|
+
def initialize(size:, queue_size: nil)
|
|
89
|
+
raise ArgumentError, 'Pool size must be greater than zero' unless size.positive?
|
|
90
|
+
|
|
91
|
+
@size = size
|
|
92
|
+
@queue = ::Async::Queue.new
|
|
93
|
+
@closed = false
|
|
94
|
+
@workers = []
|
|
95
|
+
start_workers
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def post(&block)
|
|
99
|
+
raise ArgumentError, 'EventExecutor::AsyncPool#post requires a block' unless block
|
|
100
|
+
raise 'Event executor has been shut down' if @closed
|
|
101
|
+
|
|
102
|
+
@queue.enqueue(block)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def queue_size
|
|
106
|
+
@queue.size
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def shutdown
|
|
110
|
+
return if @closed
|
|
111
|
+
|
|
112
|
+
@closed = true
|
|
113
|
+
@size.times { @queue.enqueue(STOP) }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def threads
|
|
117
|
+
[]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
def start_workers
|
|
123
|
+
@workers = Array.new(@size) do
|
|
124
|
+
OnyxCord::AsyncRuntime.async do
|
|
125
|
+
loop do
|
|
126
|
+
job = @queue.dequeue
|
|
127
|
+
break if job.equal?(STOP)
|
|
128
|
+
|
|
129
|
+
job.call
|
|
130
|
+
rescue StandardError => e
|
|
131
|
+
OnyxCord::LOGGER.log_exception(e) if defined?(OnyxCord::LOGGER)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
90
138
|
module_function
|
|
91
139
|
|
|
92
140
|
def build(type, workers:, queue_size: nil)
|
|
@@ -95,6 +143,8 @@ module OnyxCord
|
|
|
95
143
|
Inline.new
|
|
96
144
|
when :pool
|
|
97
145
|
Pool.new(size: workers, queue_size: queue_size)
|
|
146
|
+
when :async_pool
|
|
147
|
+
AsyncPool.new(size: workers, queue_size: queue_size)
|
|
98
148
|
else
|
|
99
149
|
raise ArgumentError, "Unknown event executor: #{type.inspect}"
|
|
100
150
|
end
|
data/lib/onyxcord/gateway.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require 'async'
|
|
4
4
|
require 'async/http/endpoint'
|
|
5
5
|
require 'async/websocket/client'
|
|
6
|
+
require 'onyxcord/async/runtime'
|
|
6
7
|
require 'onyxcord/rate_limiter/gateway'
|
|
7
8
|
|
|
8
9
|
module OnyxCord
|
|
@@ -86,25 +87,24 @@ module OnyxCord
|
|
|
86
87
|
|
|
87
88
|
# Connect to the gateway server inside an Async reactor.
|
|
88
89
|
def run_async
|
|
89
|
-
@
|
|
90
|
-
Thread.current[:onyxcord_name] = 'gateway'
|
|
91
|
-
Async do |task|
|
|
92
|
-
@reactor_task = task
|
|
93
|
-
connect_loop
|
|
94
|
-
LOGGER.warn('The gateway loop exited!')
|
|
95
|
-
end
|
|
96
|
-
end
|
|
90
|
+
@task = OnyxCord::AsyncRuntime.async { run }
|
|
97
91
|
|
|
98
|
-
LOGGER.debug('Gateway
|
|
92
|
+
LOGGER.debug('Gateway task created! Waiting for confirmation...')
|
|
99
93
|
loop do
|
|
100
|
-
sleep(0.5)
|
|
94
|
+
OnyxCord::AsyncRuntime.sleep(0.5)
|
|
101
95
|
break if @ws_success
|
|
102
96
|
break if @should_reconnect == false
|
|
103
97
|
end
|
|
104
98
|
end
|
|
105
99
|
|
|
100
|
+
def run
|
|
101
|
+
@reactor_task = Async::Task.current
|
|
102
|
+
connect_loop
|
|
103
|
+
LOGGER.warn('The gateway loop exited!')
|
|
104
|
+
end
|
|
105
|
+
|
|
106
106
|
def sync
|
|
107
|
-
@
|
|
107
|
+
@task&.wait
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
def open?
|
|
@@ -118,7 +118,7 @@ module OnyxCord
|
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
def kill
|
|
121
|
-
@
|
|
121
|
+
@task&.stop
|
|
122
122
|
end
|
|
123
123
|
|
|
124
124
|
def notify_ready
|
|
@@ -224,7 +224,7 @@ module OnyxCord
|
|
|
224
224
|
@heartbeat_task = @reactor_task&.async do
|
|
225
225
|
loop do
|
|
226
226
|
if (@session && !@session.suspended?) || !@session
|
|
227
|
-
sleep
|
|
227
|
+
OnyxCord::AsyncRuntime.sleep(@heartbeat_interval)
|
|
228
228
|
if !@closed && @connection
|
|
229
229
|
@bot.raise_heartbeat_event
|
|
230
230
|
heartbeat
|
|
@@ -232,7 +232,7 @@ module OnyxCord
|
|
|
232
232
|
LOGGER.debug('Tried to heartbeat without connection — skipping.')
|
|
233
233
|
end
|
|
234
234
|
else
|
|
235
|
-
sleep
|
|
235
|
+
OnyxCord::AsyncRuntime.sleep(1)
|
|
236
236
|
end
|
|
237
237
|
rescue StandardError => e
|
|
238
238
|
LOGGER.error('Error while heartbeating!')
|
|
@@ -260,7 +260,7 @@ module OnyxCord
|
|
|
260
260
|
|
|
261
261
|
def wait_for_reconnect
|
|
262
262
|
LOGGER.debug("Reconnecting in #{@falloff} seconds.")
|
|
263
|
-
sleep
|
|
263
|
+
OnyxCord::AsyncRuntime.sleep(@falloff)
|
|
264
264
|
@falloff *= 1.5
|
|
265
265
|
@falloff = 115 + (rand * 10) if @falloff > 120
|
|
266
266
|
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'httpx'
|
|
4
|
+
require 'onyxcord/json'
|
|
5
|
+
|
|
6
|
+
module OnyxCord
|
|
7
|
+
# Modern HTTP adapter wrapping HTTPX with persistent connections, automatic
|
|
8
|
+
# retries on 502, and a response interface compatible with the rest of OnyxCord.
|
|
9
|
+
module HTTP
|
|
10
|
+
# Lightweight response wrapper so call-sites that relied on RestClient's
|
|
11
|
+
# `.body`, `.headers`, `.code` interface keep working unchanged.
|
|
12
|
+
class Response
|
|
13
|
+
# @return [String] the response body
|
|
14
|
+
attr_reader :body
|
|
15
|
+
|
|
16
|
+
# @return [Integer] the HTTP status code
|
|
17
|
+
attr_reader :code
|
|
18
|
+
|
|
19
|
+
# @return [Hash] the response headers (symbol keys, underscored)
|
|
20
|
+
attr_reader :headers
|
|
21
|
+
|
|
22
|
+
def initialize(httpx_response)
|
|
23
|
+
@raw = httpx_response
|
|
24
|
+
@body = httpx_response.body.to_s
|
|
25
|
+
@code = httpx_response.status
|
|
26
|
+
@headers = normalize_headers(httpx_response.headers)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_s
|
|
30
|
+
@body
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# RestClient compatibility — some code calls `response` directly as a
|
|
34
|
+
# string (implicit to_s).
|
|
35
|
+
alias_method :to_str, :to_s
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def normalize_headers(headers)
|
|
40
|
+
headers.to_h.transform_keys do |key|
|
|
41
|
+
key.to_s.tr('-', '_').downcase.to_sym
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
module_function
|
|
47
|
+
|
|
48
|
+
# The shared HTTPX session with persistent connections.
|
|
49
|
+
def session
|
|
50
|
+
@session ||= HTTPX.plugin(:persistent)
|
|
51
|
+
.plugin(:follow_redirects)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Reset the HTTP session (useful for tests).
|
|
55
|
+
def reset!
|
|
56
|
+
@session = nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Perform a raw HTTP request and return a {Response}.
|
|
60
|
+
# @param type [Symbol] HTTP method (:get, :post, :put, :patch, :delete)
|
|
61
|
+
# @param url [String] The full URL.
|
|
62
|
+
# @param body [String, Hash, nil] The request body.
|
|
63
|
+
# @param headers [Hash] Request headers.
|
|
64
|
+
# @return [Response]
|
|
65
|
+
def request(type, url, body = nil, **headers)
|
|
66
|
+
http = session.with(headers: headers)
|
|
67
|
+
|
|
68
|
+
raw = case type
|
|
69
|
+
when :get
|
|
70
|
+
http.get(url)
|
|
71
|
+
when :post
|
|
72
|
+
if body.is_a?(Hash) && body.any? { |_, v| v.respond_to?(:read) || v.respond_to?(:path) }
|
|
73
|
+
# Multipart upload
|
|
74
|
+
http.plugin(:multipart).post(url, form: body)
|
|
75
|
+
else
|
|
76
|
+
http.post(url, body: body)
|
|
77
|
+
end
|
|
78
|
+
when :put
|
|
79
|
+
http.put(url, body: body)
|
|
80
|
+
when :patch
|
|
81
|
+
http.patch(url, body: body)
|
|
82
|
+
when :delete
|
|
83
|
+
http.delete(url)
|
|
84
|
+
else
|
|
85
|
+
raise ArgumentError, "Unknown HTTP method: #{type}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# HTTPX returns an error response object on network failures
|
|
89
|
+
raise raw.error if raw.is_a?(HTTPX::ErrorResponse)
|
|
90
|
+
|
|
91
|
+
Response.new(raw)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Convenience wrappers
|
|
95
|
+
def get(url, **headers)
|
|
96
|
+
request(:get, url, nil, **headers)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def post(url, body = nil, **headers)
|
|
100
|
+
request(:post, url, body, **headers)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def put(url, body = nil, **headers)
|
|
104
|
+
request(:put, url, body, **headers)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def patch(url, body = nil, **headers)
|
|
108
|
+
request(:patch, url, body, **headers)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def delete(url, **headers)
|
|
112
|
+
request(:delete, url, nil, **headers)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'oj'
|
|
4
|
+
|
|
5
|
+
# Configure Oj in compat mode so that all stdlib JSON.parse / .to_json calls
|
|
6
|
+
# are transparently accelerated. This is loaded early by the main onyxcord.rb
|
|
7
|
+
# entry point so every module benefits automatically.
|
|
8
|
+
module OnyxCord
|
|
9
|
+
# Fast Oj-backed JSON wrapper providing compatibility with stdlib JSON methods.
|
|
10
|
+
module JSON
|
|
11
|
+
Oj.default_options = { mode: :compat, symbol_keys: false }
|
|
12
|
+
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
# Fast JSON decode using Oj via stdlib JSON compatibility.
|
|
16
|
+
# @param data [String] The JSON string to decode.
|
|
17
|
+
# @return [Hash, Array, String, Numeric, nil]
|
|
18
|
+
def decode(data, *args)
|
|
19
|
+
::JSON.parse(data, *args)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Fast JSON encode using Oj via stdlib JSON compatibility.
|
|
23
|
+
# @param data [Object] The object to encode as JSON.
|
|
24
|
+
# @return [String]
|
|
25
|
+
def encode(data, *args)
|
|
26
|
+
::JSON.generate(data, *args)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Alias parse to decode for standard library compatibility
|
|
30
|
+
def parse(data, *args)
|
|
31
|
+
::JSON.parse(data, *args)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Alias generate to encode for standard library compatibility
|
|
35
|
+
def generate(data, *args)
|
|
36
|
+
::JSON.generate(data, *args)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Alias load to decode for Oj/JSON compatibility
|
|
40
|
+
def load(data, *args)
|
|
41
|
+
::JSON.parse(data, *args)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Alias dump to encode for Oj/JSON compatibility
|
|
45
|
+
def dump(data, *args)
|
|
46
|
+
::JSON.generate(data, *args)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|