onyxcord 2.0.13 → 3.2.0
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/lib/onyxcord/application_commands/command.rb +4 -97
- data/lib/onyxcord/application_commands/context.rb +4 -94
- data/lib/onyxcord/application_commands/option.rb +4 -75
- data/lib/onyxcord/application_commands/registry.rb +4 -45
- data/lib/onyxcord/application_commands.rb +8 -5
- data/lib/onyxcord/bot.rb +24 -1759
- data/lib/onyxcord/{cache.rb → cache/manager.rb} +16 -14
- data/lib/onyxcord/cache/stores/gateway.rb +272 -0
- data/lib/onyxcord/commands/bot/channels.rb +48 -0
- data/lib/onyxcord/commands/bot/execution.rb +174 -0
- data/lib/onyxcord/commands/bot/message_dispatch.rb +58 -0
- data/lib/onyxcord/commands/bot/permissions.rb +72 -0
- data/lib/onyxcord/commands/bot.rb +209 -0
- data/lib/onyxcord/commands/container.rb +21 -2
- data/lib/onyxcord/commands/parser.rb +30 -1
- data/lib/onyxcord/container.rb +16 -960
- data/lib/onyxcord/core/bot/application_commands.rb +210 -0
- data/lib/onyxcord/core/bot/awaits.rb +112 -0
- data/lib/onyxcord/core/bot/invites.rb +42 -0
- data/lib/onyxcord/core/bot/messaging.rb +161 -0
- data/lib/onyxcord/core/bot/oauth.rb +27 -0
- data/lib/onyxcord/core/bot/presence.rb +103 -0
- data/lib/onyxcord/core/bot/runtime.rb +183 -0
- data/lib/onyxcord/core/bot/voice.rb +69 -0
- data/lib/onyxcord/{errors.rb → core/errors.rb} +18 -3
- data/lib/onyxcord/{logger.rb → core/logger.rb} +5 -0
- data/lib/onyxcord/core/version.rb +5 -0
- data/lib/onyxcord/events/await/base.rb +48 -0
- data/lib/onyxcord/events/await.rb +1 -46
- data/lib/onyxcord/events/ban/base.rb +54 -0
- data/lib/onyxcord/events/ban/remove.rb +11 -0
- data/lib/onyxcord/events/bans.rb +2 -58
- data/lib/onyxcord/events/channels/base.rb +91 -0
- data/lib/onyxcord/events/channels/delete.rb +73 -0
- data/lib/onyxcord/events/channels/pins.rb +44 -0
- data/lib/onyxcord/events/channels/recipients.rb +77 -0
- data/lib/onyxcord/events/channels.rb +4 -239
- data/lib/onyxcord/events/guilds/audit_logs.rb +79 -0
- data/lib/onyxcord/events/guilds/base.rb +97 -0
- data/lib/onyxcord/events/guilds/emoji.rb +147 -0
- data/lib/onyxcord/events/guilds.rb +3 -267
- data/lib/onyxcord/events/handlers/channels.rb +133 -0
- data/lib/onyxcord/events/handlers/core.rb +146 -0
- data/lib/onyxcord/events/handlers/guilds.rb +375 -0
- data/lib/onyxcord/events/handlers/interactions.rb +238 -0
- data/lib/onyxcord/events/handlers/lifetime.rb +71 -0
- data/lib/onyxcord/events/handlers/messages.rb +218 -0
- data/lib/onyxcord/events/handlers/presence.rb +56 -0
- data/lib/onyxcord/events/handlers/raw.rb +39 -0
- data/lib/onyxcord/events/handlers/reactions.rb +98 -0
- data/lib/onyxcord/events/handlers/scheduled_events.rb +243 -0
- data/lib/onyxcord/events/handlers/voice.rb +46 -0
- data/lib/onyxcord/events/integration/base.rb +45 -0
- data/lib/onyxcord/events/integration/create.rb +11 -0
- data/lib/onyxcord/events/integration/delete.rb +48 -0
- data/lib/onyxcord/events/integration/update.rb +11 -0
- data/lib/onyxcord/events/integrations.rb +4 -98
- data/lib/onyxcord/events/interactions/application_commands.rb +185 -0
- data/lib/onyxcord/events/interactions/autocomplete.rb +62 -0
- data/lib/onyxcord/events/interactions/base.rb +175 -0
- data/lib/onyxcord/events/interactions/components.rb +221 -0
- data/lib/onyxcord/events/interactions/permissions.rb +53 -0
- data/lib/onyxcord/events/interactions.rb +5 -622
- data/lib/onyxcord/events/invite/create.rb +79 -0
- data/lib/onyxcord/events/invite/delete.rb +55 -0
- data/lib/onyxcord/events/invites.rb +2 -125
- data/lib/onyxcord/events/lifetime/base.rb +31 -0
- data/lib/onyxcord/events/lifetime.rb +1 -29
- data/lib/onyxcord/events/member/add.rb +12 -0
- data/lib/onyxcord/events/member/base.rb +67 -0
- data/lib/onyxcord/events/member/delete.rb +21 -0
- data/lib/onyxcord/events/member/update.rb +25 -0
- data/lib/onyxcord/events/members.rb +4 -108
- data/lib/onyxcord/events/message/base.rb +295 -0
- data/lib/onyxcord/events/message/ids.rb +71 -0
- data/lib/onyxcord/events/message/mentions.rb +55 -0
- data/lib/onyxcord/events/message/updates.rb +27 -0
- data/lib/onyxcord/events/message.rb +4 -397
- data/lib/onyxcord/events/poll/add.rb +11 -0
- data/lib/onyxcord/events/poll/base.rb +106 -0
- data/lib/onyxcord/events/poll/remove.rb +11 -0
- data/lib/onyxcord/events/polls.rb +3 -116
- data/lib/onyxcord/events/presence/playing.rb +77 -0
- data/lib/onyxcord/events/presence/status.rb +60 -0
- data/lib/onyxcord/events/presence.rb +2 -129
- data/lib/onyxcord/events/raw/base.rb +74 -0
- data/lib/onyxcord/events/raw.rb +1 -72
- data/lib/onyxcord/events/reactions/base.rb +143 -0
- data/lib/onyxcord/events/reactions/clear.rb +99 -0
- data/lib/onyxcord/events/reactions.rb +2 -216
- data/lib/onyxcord/events/role/create.rb +46 -0
- data/lib/onyxcord/events/role/delete.rb +40 -0
- data/lib/onyxcord/events/role/update.rb +11 -0
- data/lib/onyxcord/events/roles.rb +3 -85
- data/lib/onyxcord/events/scheduled_events/events.rb +112 -0
- data/lib/onyxcord/events/scheduled_events/users.rb +91 -0
- data/lib/onyxcord/events/scheduled_events.rb +2 -169
- data/lib/onyxcord/events/thread/base.rb +49 -0
- data/lib/onyxcord/events/thread/members.rb +44 -0
- data/lib/onyxcord/events/thread/update.rb +18 -0
- data/lib/onyxcord/events/threads.rb +3 -98
- data/lib/onyxcord/events/typing/base.rb +73 -0
- data/lib/onyxcord/events/typing.rb +1 -71
- data/lib/onyxcord/events/voice/server_update.rb +48 -0
- data/lib/onyxcord/events/voice/state_update.rb +137 -0
- data/lib/onyxcord/events/voice_server_update.rb +2 -46
- data/lib/onyxcord/events/voice_state_update.rb +2 -135
- data/lib/onyxcord/events/webhook/update.rb +65 -0
- data/lib/onyxcord/events/webhooks.rb +1 -63
- data/lib/onyxcord/gateway/client.rb +460 -0
- data/lib/onyxcord/interactions/command.rb +120 -0
- data/lib/onyxcord/interactions/context.rb +109 -0
- data/lib/onyxcord/interactions/internal/application_command.rb +203 -0
- data/lib/onyxcord/interactions/internal/message.rb +152 -0
- data/lib/onyxcord/interactions/internal/metadata.rb +109 -0
- data/lib/onyxcord/interactions/internal/option_builder.rb +230 -0
- data/lib/onyxcord/interactions/internal/permission_builder.rb +90 -0
- data/lib/onyxcord/interactions/option.rb +88 -0
- data/lib/onyxcord/interactions/registry.rb +50 -0
- data/lib/onyxcord/interactions.rb +16 -0
- data/lib/onyxcord/internal/async_runtime.rb +32 -0
- data/lib/onyxcord/internal/event_bus.rb +591 -0
- data/lib/onyxcord/internal/event_executor.rb +155 -0
- data/lib/onyxcord/internal/gateway/opcodes.rb +22 -0
- data/lib/onyxcord/internal/gateway/session.rb +45 -0
- data/lib/onyxcord/internal/http.rb +189 -0
- data/lib/onyxcord/internal/json.rb +51 -0
- data/lib/onyxcord/internal/message_payload.rb +78 -0
- data/lib/onyxcord/internal/rate_limiter/async_rest.rb +151 -0
- data/lib/onyxcord/internal/rate_limiter/gateway.rb +44 -0
- data/lib/onyxcord/internal/rate_limiter/rest.rb +144 -0
- data/lib/onyxcord/internal/upload.rb +39 -0
- data/lib/onyxcord/internal/websocket.rb +79 -0
- data/lib/onyxcord/light/data.rb +1 -1
- data/lib/onyxcord/light/integrations.rb +1 -1
- data/lib/onyxcord/light/light_bot.rb +7 -7
- data/lib/onyxcord/{data → models}/activity.rb +2 -2
- data/lib/onyxcord/{data → models}/application.rb +3 -3
- data/lib/onyxcord/{data → models}/avatar_decoration.rb +1 -1
- data/lib/onyxcord/{data → models}/channel.rb +23 -23
- data/lib/onyxcord/{data → models}/collectibles.rb +1 -1
- data/lib/onyxcord/{data → models}/emoji.rb +1 -1
- data/lib/onyxcord/models/interaction.rb +450 -0
- data/lib/onyxcord/{data → models}/invite.rb +1 -1
- data/lib/onyxcord/{data → models}/member.rb +11 -11
- data/lib/onyxcord/{data → models}/message.rb +22 -14
- data/lib/onyxcord/{data → models}/poll.rb +2 -2
- data/lib/onyxcord/{data → models}/primary_server.rb +1 -1
- data/lib/onyxcord/{data → models}/profile.rb +4 -4
- data/lib/onyxcord/{data → models}/role.rb +6 -12
- data/lib/onyxcord/{data → models}/scheduled_event.rb +5 -5
- data/lib/onyxcord/{data → models}/server.rb +41 -41
- data/lib/onyxcord/{data → models}/server_preview.rb +2 -2
- data/lib/onyxcord/{data → models}/team.rb +1 -1
- data/lib/onyxcord/{data → models}/user.rb +5 -5
- data/lib/onyxcord/{data → models}/webhook.rb +13 -11
- data/lib/onyxcord/models.rb +57 -0
- data/lib/onyxcord/{api.rb → rest/client.rb} +83 -27
- data/lib/onyxcord/{api → rest/routes}/application.rb +79 -57
- data/lib/onyxcord/rest/routes/channel/base.rb +73 -0
- data/lib/onyxcord/rest/routes/channel/groups.rb +78 -0
- data/lib/onyxcord/rest/routes/channel/messages.rb +130 -0
- data/lib/onyxcord/rest/routes/channel/permissions.rb +73 -0
- data/lib/onyxcord/rest/routes/channel/pins.rb +45 -0
- data/lib/onyxcord/rest/routes/channel/polls.rb +32 -0
- data/lib/onyxcord/rest/routes/channel/reactions.rb +86 -0
- data/lib/onyxcord/rest/routes/channel/threads.rb +176 -0
- data/lib/onyxcord/rest/routes/channel/webhooks.rb +47 -0
- data/lib/onyxcord/rest/routes/channel.rb +12 -0
- data/lib/onyxcord/{api/interaction.rb → rest/routes/interaction/base.rb} +10 -33
- data/lib/onyxcord/rest/routes/interaction/response.rb +24 -0
- data/lib/onyxcord/rest/routes/interaction.rb +4 -0
- data/lib/onyxcord/{api → rest/routes}/invite.rb +7 -7
- data/lib/onyxcord/rest/routes/server/admin.rb +174 -0
- data/lib/onyxcord/rest/routes/server/assets.rb +25 -0
- data/lib/onyxcord/rest/routes/server/bans.rb +67 -0
- data/lib/onyxcord/rest/routes/server/base.rb +77 -0
- data/lib/onyxcord/rest/routes/server/channels.rb +58 -0
- data/lib/onyxcord/rest/routes/server/emoji.rb +48 -0
- data/lib/onyxcord/rest/routes/server/members.rb +109 -0
- data/lib/onyxcord/rest/routes/server/messages.rb +19 -0
- data/lib/onyxcord/rest/routes/server/roles.rb +156 -0
- data/lib/onyxcord/rest/routes/server/scheduled_events.rb +86 -0
- data/lib/onyxcord/rest/routes/server.rb +12 -0
- data/lib/onyxcord/{api → rest/routes}/user.rb +23 -23
- data/lib/onyxcord/{api → rest/routes}/webhook.rb +30 -32
- data/lib/onyxcord/{allowed_mentions.rb → utils/allowed_mentions.rb} +9 -1
- data/lib/onyxcord/utils/timestamp.rb +69 -0
- data/lib/onyxcord/voice/{voice_bot.rb → client.rb} +4 -4
- data/lib/onyxcord/voice/encoder.rb +1 -1
- data/lib/onyxcord/voice/network/udp.rb +135 -0
- data/lib/onyxcord/voice/network/websocket.rb +225 -0
- data/lib/onyxcord/voice/network.rb +5 -357
- data/lib/onyxcord/webhooks/builder.rb +1 -1
- data/lib/onyxcord/webhooks/client.rb +13 -13
- data/lib/onyxcord/webhooks/modal/group_builder.rb +55 -0
- data/lib/onyxcord/webhooks/modal/label_builder.rb +183 -0
- data/lib/onyxcord/webhooks/modal.rb +2 -239
- data/lib/onyxcord/webhooks/version.rb +3 -3
- data/lib/onyxcord/webhooks/view/container_builder.rb +80 -0
- data/lib/onyxcord/webhooks/view/file_builder.rb +24 -0
- data/lib/onyxcord/webhooks/view/media_gallery_builder.rb +50 -0
- data/lib/onyxcord/webhooks/view/row_builder.rb +120 -0
- data/lib/onyxcord/webhooks/view/section_builder.rb +61 -0
- data/lib/onyxcord/webhooks/view/select_menu_builder.rb +77 -0
- data/lib/onyxcord/webhooks/view/separator_builder.rb +22 -0
- data/lib/onyxcord/webhooks/view/text_display_builder.rb +20 -0
- data/lib/onyxcord/webhooks/view.rb +9 -425
- data/lib/onyxcord.rb +106 -40
- metadata +208 -91
- data/.github/CONTRIBUTING.md +0 -13
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -24
- data/.github/pull_request_template.md +0 -37
- data/.github/workflows/ci.yml +0 -78
- data/.github/workflows/codeql.yml +0 -65
- data/.github/workflows/deploy.yml +0 -54
- data/.github/workflows/release.yml +0 -51
- data/.gitignore +0 -17
- data/.markdownlint.json +0 -4
- data/.overcommit.yml +0 -7
- data/.rspec +0 -2
- data/.rubocop.yml +0 -129
- data/.yardopts +0 -1
- data/CHANGELOG.md +0 -202
- data/Gemfile +0 -6
- data/LICENSE.txt +0 -21
- data/README.md +0 -448
- data/Rakefile +0 -13
- data/lib/onyxcord/api/channel.rb +0 -705
- data/lib/onyxcord/api/server.rb +0 -775
- data/lib/onyxcord/async/runtime.rb +0 -30
- data/lib/onyxcord/commands/command_bot.rb +0 -505
- data/lib/onyxcord/data/interaction.rb +0 -1143
- data/lib/onyxcord/data.rb +0 -57
- data/lib/onyxcord/event_executor.rb +0 -153
- data/lib/onyxcord/gateway.rb +0 -500
- data/lib/onyxcord/http.rb +0 -115
- data/lib/onyxcord/json.rb +0 -49
- data/lib/onyxcord/rate_limiter/async_rest.rb +0 -149
- data/lib/onyxcord/rate_limiter/gateway.rb +0 -42
- data/lib/onyxcord/rate_limiter/rest.rb +0 -142
- data/lib/onyxcord/version.rb +0 -7
- data/lib/onyxcord/websocket.rb +0 -77
- data/onyxcord.gemspec +0 -63
- /data/lib/onyxcord/{configuration.rb → core/configuration.rb} +0 -0
- /data/lib/onyxcord/{data → models}/attachment.rb +0 -0
- /data/lib/onyxcord/{data → models}/audit_logs.rb +0 -0
- /data/lib/onyxcord/{data → models}/call.rb +0 -0
- /data/lib/onyxcord/{data → models}/channel_tag.rb +0 -0
- /data/lib/onyxcord/{data → models}/component.rb +0 -0
- /data/lib/onyxcord/{data → models}/embed.rb +0 -0
- /data/lib/onyxcord/{data → models}/install_params.rb +0 -0
- /data/lib/onyxcord/{data → models}/integration.rb +0 -0
- /data/lib/onyxcord/{data → models}/message_activity.rb +0 -0
- /data/lib/onyxcord/{data → models}/overwrite.rb +0 -0
- /data/lib/onyxcord/{data → models}/reaction.rb +0 -0
- /data/lib/onyxcord/{data → models}/recipient.rb +0 -0
- /data/lib/onyxcord/{data → models}/role_connection_data.rb +0 -0
- /data/lib/onyxcord/{data → models}/role_subscription.rb +0 -0
- /data/lib/onyxcord/{data → models}/snapshot.rb +0 -0
- /data/lib/onyxcord/{data → models}/timestamp.rb +0 -0
- /data/lib/onyxcord/{data → models}/voice_region.rb +0 -0
- /data/lib/onyxcord/{data → models}/voice_state.rb +0 -0
- /data/lib/onyxcord/{colour_rgb.rb → utils/colour_rgb.rb} +0 -0
- /data/lib/onyxcord/{id_object.rb → utils/id_object.rb} +0 -0
- /data/lib/onyxcord/{message_components.rb → utils/message_components.rb} +0 -0
- /data/lib/onyxcord/{paginator.rb → utils/paginator.rb} +0 -0
- /data/lib/onyxcord/{permissions.rb → utils/permissions.rb} +0 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'async_runtime'
|
|
4
|
+
|
|
5
|
+
module OnyxCord
|
|
6
|
+
module Internal
|
|
7
|
+
module EventExecutor
|
|
8
|
+
STOP = Object.new.freeze
|
|
9
|
+
|
|
10
|
+
class Inline
|
|
11
|
+
def post
|
|
12
|
+
yield
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def shutdown; end
|
|
16
|
+
|
|
17
|
+
def threads
|
|
18
|
+
[]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Pool
|
|
23
|
+
attr_reader :queue
|
|
24
|
+
|
|
25
|
+
def initialize(size:, queue_size: nil)
|
|
26
|
+
raise ArgumentError, 'Pool size must be greater than zero' unless size.positive?
|
|
27
|
+
|
|
28
|
+
@size = size
|
|
29
|
+
@queue = queue_size ? SizedQueue.new(queue_size) : Queue.new
|
|
30
|
+
@closed = false
|
|
31
|
+
@workers = []
|
|
32
|
+
|
|
33
|
+
start_workers
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def post(&block)
|
|
37
|
+
raise ArgumentError, 'EventExecutor::Pool#post requires a block' unless block
|
|
38
|
+
raise 'Event executor has been shut down' if @closed
|
|
39
|
+
|
|
40
|
+
@queue << block
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def queue_size
|
|
44
|
+
@queue.size
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def threads
|
|
48
|
+
@workers
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def shutdown
|
|
52
|
+
return if @closed
|
|
53
|
+
|
|
54
|
+
@closed = true
|
|
55
|
+
@size.times { @queue << STOP }
|
|
56
|
+
@workers.each do |w|
|
|
57
|
+
w.join unless w == Thread.current
|
|
58
|
+
rescue StandardError
|
|
59
|
+
nil
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def start_workers
|
|
66
|
+
@workers = Array.new(@size) do |index|
|
|
67
|
+
Thread.new do
|
|
68
|
+
Thread.current[:onyxcord_name] = "event-worker-#{index + 1}"
|
|
69
|
+
worker_loop
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def worker_loop
|
|
75
|
+
loop do
|
|
76
|
+
job = @queue.pop
|
|
77
|
+
break if job.equal?(STOP)
|
|
78
|
+
|
|
79
|
+
job.call
|
|
80
|
+
rescue StandardError => e
|
|
81
|
+
OnyxCord::LOGGER.log_exception(e) if defined?(OnyxCord::LOGGER)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class AsyncPool
|
|
87
|
+
attr_reader :queue
|
|
88
|
+
|
|
89
|
+
def initialize(size:, queue_size: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
90
|
+
raise ArgumentError, 'Pool size must be greater than zero' unless size.positive?
|
|
91
|
+
|
|
92
|
+
@size = size
|
|
93
|
+
@queue = ::Async::Queue.new
|
|
94
|
+
@closed = false
|
|
95
|
+
@workers = []
|
|
96
|
+
start_workers
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def post(&block)
|
|
100
|
+
raise ArgumentError, 'EventExecutor::AsyncPool#post requires a block' unless block
|
|
101
|
+
raise 'Event executor has been shut down' if @closed
|
|
102
|
+
|
|
103
|
+
@queue.enqueue(block)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def queue_size
|
|
107
|
+
@queue.size
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def shutdown
|
|
111
|
+
return if @closed
|
|
112
|
+
|
|
113
|
+
@closed = true
|
|
114
|
+
@size.times { @queue.enqueue(STOP) }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def threads
|
|
118
|
+
[]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
def start_workers
|
|
124
|
+
@workers = Array.new(@size) do
|
|
125
|
+
AsyncRuntime.async do
|
|
126
|
+
loop do
|
|
127
|
+
job = @queue.dequeue
|
|
128
|
+
break if job.equal?(STOP)
|
|
129
|
+
|
|
130
|
+
job.call
|
|
131
|
+
rescue StandardError => e
|
|
132
|
+
OnyxCord::LOGGER.log_exception(e) if defined?(OnyxCord::LOGGER)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
module_function
|
|
140
|
+
|
|
141
|
+
def build(type, workers:, queue_size: nil)
|
|
142
|
+
case type
|
|
143
|
+
when :inline
|
|
144
|
+
Inline.new
|
|
145
|
+
when :pool
|
|
146
|
+
Pool.new(size: workers, queue_size: queue_size)
|
|
147
|
+
when :async_pool
|
|
148
|
+
AsyncPool.new(size: workers, queue_size: queue_size)
|
|
149
|
+
else
|
|
150
|
+
raise ArgumentError, "Unknown event executor: #{type.inspect}"
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OnyxCord
|
|
4
|
+
module Internal
|
|
5
|
+
module Gateway
|
|
6
|
+
module Opcodes
|
|
7
|
+
DISPATCH = 0
|
|
8
|
+
HEARTBEAT = 1
|
|
9
|
+
IDENTIFY = 2
|
|
10
|
+
PRESENCE = 3
|
|
11
|
+
VOICE_STATE = 4
|
|
12
|
+
VOICE_PING = 5
|
|
13
|
+
RESUME = 6
|
|
14
|
+
RECONNECT = 7
|
|
15
|
+
REQUEST_MEMBERS = 8
|
|
16
|
+
INVALIDATE_SESSION = 9
|
|
17
|
+
HELLO = 10
|
|
18
|
+
HEARTBEAT_ACK = 11
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OnyxCord
|
|
4
|
+
module Internal
|
|
5
|
+
module Gateway
|
|
6
|
+
class Session
|
|
7
|
+
attr_reader :session_id, :resume_gateway_url
|
|
8
|
+
attr_accessor :sequence
|
|
9
|
+
|
|
10
|
+
def initialize(session_id, resume_gateway_url)
|
|
11
|
+
@session_id = session_id
|
|
12
|
+
@sequence = 0
|
|
13
|
+
@suspended = false
|
|
14
|
+
@invalid = false
|
|
15
|
+
@resume_gateway_url = resume_gateway_url
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def suspend
|
|
19
|
+
@suspended = true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def suspended?
|
|
23
|
+
@suspended
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def resume
|
|
27
|
+
@suspended = false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def invalidate
|
|
31
|
+
@invalid = true
|
|
32
|
+
@resume_gateway_url = nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def invalid?
|
|
36
|
+
@invalid
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def should_resume?
|
|
40
|
+
suspended? && !invalid?
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'httpx'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require_relative 'json'
|
|
6
|
+
require_relative 'upload'
|
|
7
|
+
require 'securerandom'
|
|
8
|
+
require 'uri'
|
|
9
|
+
|
|
10
|
+
module OnyxCord
|
|
11
|
+
module Internal
|
|
12
|
+
# Modern HTTP adapter wrapping HTTPX with persistent connections, automatic
|
|
13
|
+
# retries on 502, and a response interface compatible with the rest of OnyxCord.
|
|
14
|
+
module HTTP
|
|
15
|
+
# Lightweight response wrapper so call-sites that relied on RestClient's
|
|
16
|
+
# `.body`, `.headers`, `.code` interface keep working unchanged.
|
|
17
|
+
class Response
|
|
18
|
+
# @return [String] the response body
|
|
19
|
+
attr_reader :body
|
|
20
|
+
|
|
21
|
+
# @return [Integer] the HTTP status code
|
|
22
|
+
attr_reader :code
|
|
23
|
+
|
|
24
|
+
# @return [Hash] the response headers (symbol keys, underscored)
|
|
25
|
+
attr_reader :headers
|
|
26
|
+
|
|
27
|
+
def initialize(httpx_response)
|
|
28
|
+
@raw = httpx_response
|
|
29
|
+
@body = httpx_response.body.to_s
|
|
30
|
+
@code = httpx_response.respond_to?(:status) ? httpx_response.status : httpx_response.code.to_i
|
|
31
|
+
response_headers = httpx_response.respond_to?(:headers) ? httpx_response.headers : httpx_response.to_hash
|
|
32
|
+
@headers = normalize_headers(response_headers)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_s
|
|
36
|
+
@body
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# RestClient compatibility — some code calls `response` directly as a
|
|
40
|
+
# string (implicit to_s).
|
|
41
|
+
alias_method :to_str, :to_s
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def normalize_headers(headers)
|
|
46
|
+
transformed = headers.to_h.transform_keys do |key|
|
|
47
|
+
key.to_s.tr('-', '_').downcase.to_sym
|
|
48
|
+
end
|
|
49
|
+
transformed.transform_values do |value|
|
|
50
|
+
value.is_a?(Array) && value.size == 1 ? value.first : value
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
module_function
|
|
56
|
+
|
|
57
|
+
# The shared HTTPX session with persistent connections for the current thread.
|
|
58
|
+
def session
|
|
59
|
+
Thread.current[:onyxcord_http_session] ||= HTTPX.plugin(:persistent)
|
|
60
|
+
.plugin(:follow_redirects)
|
|
61
|
+
.with(
|
|
62
|
+
fallback_protocol: 'http/1.1',
|
|
63
|
+
ssl: { alpn_protocols: ['http/1.1'] }
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Reset the HTTP session (useful for tests).
|
|
68
|
+
def reset!
|
|
69
|
+
Thread.current[:onyxcord_http_session] = nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Perform a raw HTTP request and return a {Response}.
|
|
73
|
+
# @param type [Symbol] HTTP method (:get, :post, :put, :patch, :delete)
|
|
74
|
+
# @param url [String] The full URL.
|
|
75
|
+
# @param body [String, Hash, nil] The request body.
|
|
76
|
+
# @param headers [Hash] Request headers.
|
|
77
|
+
# @return [Response]
|
|
78
|
+
def request(type, url, body = nil, **headers)
|
|
79
|
+
http = session.with(headers: headers)
|
|
80
|
+
|
|
81
|
+
raw = case type
|
|
82
|
+
when :get
|
|
83
|
+
http.get(url)
|
|
84
|
+
when :post
|
|
85
|
+
if multipart?(body)
|
|
86
|
+
# Multipart upload
|
|
87
|
+
post_multipart(url, body, headers)
|
|
88
|
+
else
|
|
89
|
+
http.post(url, body: body)
|
|
90
|
+
end
|
|
91
|
+
when :put
|
|
92
|
+
http.put(url, body: body)
|
|
93
|
+
when :patch
|
|
94
|
+
http.patch(url, body: body)
|
|
95
|
+
when :delete
|
|
96
|
+
http.delete(url)
|
|
97
|
+
else
|
|
98
|
+
raise ArgumentError, "Unknown HTTP method: #{type}"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# HTTPX returns an error response object on network failures
|
|
102
|
+
raise raw.error if raw.is_a?(HTTPX::ErrorResponse)
|
|
103
|
+
|
|
104
|
+
Response.new(raw)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def post_multipart(url, body, headers)
|
|
108
|
+
uri = URI(url)
|
|
109
|
+
boundary = "----OnyxCord#{SecureRandom.hex(12)}"
|
|
110
|
+
request = Net::HTTP::Post.new(uri)
|
|
111
|
+
headers.each { |key, value| request[key.to_s] = value }
|
|
112
|
+
request['Content-Type'] = "multipart/form-data; boundary=#{boundary}"
|
|
113
|
+
request.body = multipart_body(body, boundary)
|
|
114
|
+
|
|
115
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
116
|
+
http.request(request)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def multipart_body(body, boundary)
|
|
121
|
+
output = (+'').b
|
|
122
|
+
|
|
123
|
+
multipart_parts(body).each do |part|
|
|
124
|
+
key = part[:name]
|
|
125
|
+
value = part[:value]
|
|
126
|
+
output << "--#{boundary}\r\n"
|
|
127
|
+
if part[:filename]
|
|
128
|
+
value.rewind if value.respond_to?(:rewind)
|
|
129
|
+
filename = part[:filename]
|
|
130
|
+
output << "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{filename}\"\r\n"
|
|
131
|
+
output << "Content-Type: #{part[:content_type] || multipart_content_type(filename)}\r\n\r\n"
|
|
132
|
+
output << value.read.to_s
|
|
133
|
+
else
|
|
134
|
+
output << "Content-Disposition: form-data; name=\"#{key}\"\r\n"
|
|
135
|
+
output << "\r\n"
|
|
136
|
+
output << value.to_s
|
|
137
|
+
end
|
|
138
|
+
output << "\r\n"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
output << "--#{boundary}--\r\n"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def multipart_content_type(filename)
|
|
145
|
+
File.extname(filename).casecmp('.txt').zero? ? 'text/plain' : 'application/octet-stream'
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def multipart?(body)
|
|
149
|
+
return true if body.is_a?(Array) && body.all? { |part| part.is_a?(Hash) && part[:name] && part.key?(:value) }
|
|
150
|
+
|
|
151
|
+
body.is_a?(Hash) && body.any? { |_, value| value.respond_to?(:read) || value.respond_to?(:path) }
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def multipart_parts(body)
|
|
155
|
+
return body if body.is_a?(Array)
|
|
156
|
+
|
|
157
|
+
body.map do |key, value|
|
|
158
|
+
if value.respond_to?(:read) || value.respond_to?(:path)
|
|
159
|
+
upload = Upload.wrap(value)
|
|
160
|
+
{ name: key, value: upload, filename: upload.filename, content_type: upload.content_type }
|
|
161
|
+
else
|
|
162
|
+
{ name: key, value: value }
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Convenience wrappers
|
|
168
|
+
def get(url, **headers)
|
|
169
|
+
request(:get, url, nil, **headers)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def post(url, body = nil, **headers)
|
|
173
|
+
request(:post, url, body, **headers)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def put(url, body = nil, **headers)
|
|
177
|
+
request(:put, url, body, **headers)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def patch(url, body = nil, **headers)
|
|
181
|
+
request(:patch, url, body, **headers)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def delete(url, **headers)
|
|
185
|
+
request(:delete, url, nil, **headers)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
module Internal
|
|
10
|
+
# Fast Oj-backed JSON wrapper providing compatibility with stdlib JSON methods.
|
|
11
|
+
module JSON
|
|
12
|
+
Oj.default_options = { mode: :compat, symbol_keys: false }
|
|
13
|
+
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
# Fast JSON decode using Oj via stdlib JSON compatibility.
|
|
17
|
+
# @param data [String] The JSON string to decode.
|
|
18
|
+
# @return [Hash, Array, String, Numeric, nil]
|
|
19
|
+
def decode(data, *args)
|
|
20
|
+
::JSON.parse(data, *args)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Fast JSON encode using Oj via stdlib JSON compatibility.
|
|
24
|
+
# @param data [Object] The object to encode as JSON.
|
|
25
|
+
# @return [String]
|
|
26
|
+
def encode(data, *args)
|
|
27
|
+
::JSON.generate(data, *args)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Alias parse to decode for standard library compatibility
|
|
31
|
+
def parse(data, *args)
|
|
32
|
+
::JSON.parse(data, *args)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Alias generate to encode for standard library compatibility
|
|
36
|
+
def generate(data, *args)
|
|
37
|
+
::JSON.generate(data, *args)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Alias load to decode for Oj/JSON compatibility
|
|
41
|
+
def load(data, *args)
|
|
42
|
+
::JSON.parse(data, *args)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Alias dump to encode for Oj/JSON compatibility
|
|
46
|
+
def dump(data, *args)
|
|
47
|
+
::JSON.generate(data, *args)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../utils/message_components'
|
|
4
|
+
require_relative 'upload'
|
|
5
|
+
|
|
6
|
+
module OnyxCord
|
|
7
|
+
module MessagePayload
|
|
8
|
+
MAX_ATTACHMENTS = 10
|
|
9
|
+
MAX_EMBEDS = 10
|
|
10
|
+
KEEP = :keep
|
|
11
|
+
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
def attachment_payload(attachments)
|
|
15
|
+
uploads(attachments).map.with_index { |upload, index| { id: index, filename: upload.filename } }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def multipart_body(body, attachments)
|
|
19
|
+
parts = uploads(attachments).map.with_index do |upload, index|
|
|
20
|
+
{
|
|
21
|
+
name: "files[#{index}]",
|
|
22
|
+
value: upload,
|
|
23
|
+
filename: upload.filename,
|
|
24
|
+
content_type: upload.content_type || 'application/octet-stream'
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
parts << { name: 'payload_json', value: body.to_json }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def validate!(content: nil, embeds: nil, components: nil, flags: nil, attachments: nil, poll: nil)
|
|
32
|
+
validate_limit!('attachments', attachments, MAX_ATTACHMENTS)
|
|
33
|
+
validate_limit!('embeds', embeds, MAX_EMBEDS)
|
|
34
|
+
validate_components_v2!(content, embeds, components, flags, poll)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def edit_body(content, embeds)
|
|
38
|
+
content_keep = content == KEEP
|
|
39
|
+
embeds_keep = embeds == KEEP
|
|
40
|
+
content_given = !content_keep && !content.nil?
|
|
41
|
+
embeds_given = !embeds_keep && !embeds.nil?
|
|
42
|
+
body = {}
|
|
43
|
+
body[:content] = content if !content_keep && (content_given || embeds_given)
|
|
44
|
+
body[:embeds] = embeds_given ? embeds : [] if !embeds_keep && (embeds_given || content_given)
|
|
45
|
+
body
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def uploads(attachments)
|
|
49
|
+
Array(attachments).map { |attachment| OnyxCord::Upload.wrap(attachment) }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def validate_limit!(name, values, max)
|
|
53
|
+
return if values.nil? || Array(values).length <= max
|
|
54
|
+
|
|
55
|
+
raise ArgumentError, "#{name} cannot exceed #{max} elements"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def validate_components_v2!(content, embeds, components, flags, poll)
|
|
59
|
+
return unless components_v2?(components, flags)
|
|
60
|
+
return unless present?(content) || present?(embeds) || present?(poll)
|
|
61
|
+
|
|
62
|
+
raise ArgumentError, 'Components V2 messages cannot include content, embeds, or poll'
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def components_v2?(components, flags)
|
|
66
|
+
OnyxCord::MessageComponents.components_v2?(components) ||
|
|
67
|
+
OnyxCord::MessageComponents.flag_value(flags).anybits?(OnyxCord::MessageComponents::IS_COMPONENTS_V2)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def present?(value)
|
|
71
|
+
return false if value.nil?
|
|
72
|
+
return false if value == KEEP
|
|
73
|
+
return !value.empty? if value.respond_to?(:empty?)
|
|
74
|
+
|
|
75
|
+
true
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../async_runtime'
|
|
4
|
+
|
|
5
|
+
module OnyxCord
|
|
6
|
+
module Internal
|
|
7
|
+
module RateLimiter
|
|
8
|
+
class AsyncRest
|
|
9
|
+
DEFAULT_ENTRY_TTL = 3600
|
|
10
|
+
DEFAULT_PRUNE_INTERVAL = 100
|
|
11
|
+
|
|
12
|
+
def initialize(clock: -> { Time.now }, entry_ttl: DEFAULT_ENTRY_TTL, prune_interval: DEFAULT_PRUNE_INTERVAL)
|
|
13
|
+
@route_buckets = {}
|
|
14
|
+
@bucket_locks = {}
|
|
15
|
+
@bucket_last_used = {}
|
|
16
|
+
@global_lock = Mutex.new
|
|
17
|
+
@clock = clock
|
|
18
|
+
@entry_ttl = entry_ttl
|
|
19
|
+
@prune_interval = prune_interval
|
|
20
|
+
@requests_since_prune = 0
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def before_request(route, major_parameter)
|
|
24
|
+
wait_for(@global_lock)
|
|
25
|
+
wait_for(mutex_for(route, major_parameter))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def record_response(route, major_parameter, headers)
|
|
29
|
+
headers = normalize_headers(headers)
|
|
30
|
+
bucket = headers[:x_ratelimit_bucket]
|
|
31
|
+
key = route_key(route, major_parameter)
|
|
32
|
+
touch(key)
|
|
33
|
+
|
|
34
|
+
if bucket
|
|
35
|
+
bucket = bucket_key(bucket, major_parameter)
|
|
36
|
+
@route_buckets[key] = bucket
|
|
37
|
+
touch(bucket)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
return unless headers[:x_ratelimit_remaining] == '0'
|
|
41
|
+
|
|
42
|
+
wait_seconds = headers[:x_ratelimit_reset_after].to_f
|
|
43
|
+
return unless wait_seconds.positive?
|
|
44
|
+
|
|
45
|
+
async_wait(wait_seconds, mutex_for(route, major_parameter))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def handle_rate_limit(route, major_parameter, response)
|
|
49
|
+
headers = normalize_headers(response.headers)
|
|
50
|
+
wait_seconds = retry_after(response, headers)
|
|
51
|
+
|
|
52
|
+
return unless wait_seconds.positive?
|
|
53
|
+
|
|
54
|
+
if headers[:x_ratelimit_global] == 'true' || headers[:x_ratelimit_scope] == 'global'
|
|
55
|
+
global_wait(wait_seconds)
|
|
56
|
+
else
|
|
57
|
+
async_wait(wait_seconds, mutex_for(route, major_parameter))
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def stats
|
|
62
|
+
{
|
|
63
|
+
route_buckets: @route_buckets.size,
|
|
64
|
+
bucket_locks: @bucket_locks.size,
|
|
65
|
+
tracked_keys: @bucket_last_used.size
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def prune!
|
|
70
|
+
return 0 unless @entry_ttl
|
|
71
|
+
|
|
72
|
+
cutoff = @clock.call - @entry_ttl
|
|
73
|
+
stale_keys = @bucket_last_used.select { |_, last_used| last_used < cutoff }.keys
|
|
74
|
+
|
|
75
|
+
stale_keys.each do |key|
|
|
76
|
+
@bucket_locks.delete(key)
|
|
77
|
+
@bucket_last_used.delete(key)
|
|
78
|
+
@route_buckets.delete(key)
|
|
79
|
+
@route_buckets.delete_if { |_, bucket_key| bucket_key == key }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
@requests_since_prune = 0
|
|
83
|
+
stale_keys.length
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def mutex_for(route, major_parameter)
|
|
89
|
+
key = resolved_key(route, major_parameter)
|
|
90
|
+
touch(key)
|
|
91
|
+
@bucket_locks[key] ||= Mutex.new
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def resolved_key(route, major_parameter)
|
|
95
|
+
@route_buckets[route_key(route, major_parameter)] || route_key(route, major_parameter)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def route_key(route, major_parameter)
|
|
99
|
+
[route, major_parameter].freeze
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def bucket_key(bucket, major_parameter)
|
|
103
|
+
[:bucket, bucket, major_parameter].freeze
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def retry_after(response, headers)
|
|
107
|
+
body = response.respond_to?(:body) ? response.body : response.to_s
|
|
108
|
+
if body && !body.empty?
|
|
109
|
+
data = JSON.parse(body)
|
|
110
|
+
return data['retry_after'].to_f if data['retry_after']
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
(headers[:retry_after] || 0).to_f
|
|
114
|
+
rescue JSON::ParserError
|
|
115
|
+
(headers[:retry_after] || 0).to_f
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def normalize_headers(headers)
|
|
119
|
+
headers.each_with_object({}) do |(key, value), memo|
|
|
120
|
+
memo[key.to_s.tr('-', '_').downcase.to_sym] = value.to_s
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def touch(key)
|
|
125
|
+
@bucket_last_used[key] = @clock.call
|
|
126
|
+
prune_if_needed
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def prune_if_needed
|
|
130
|
+
return unless @prune_interval
|
|
131
|
+
|
|
132
|
+
@requests_since_prune += 1
|
|
133
|
+
prune! if @requests_since_prune >= @prune_interval
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def wait_for(mutex)
|
|
137
|
+
mutex.lock
|
|
138
|
+
mutex.unlock
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def async_wait(time, mutex)
|
|
142
|
+
mutex.synchronize { AsyncRuntime.sleep(time) }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def global_wait(time)
|
|
146
|
+
async_wait(time, @global_lock)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|