onyxcord 2.1.0 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/{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/{message_payload.rb → internal/message_payload.rb} +3 -3
- 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/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 +18 -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 +10 -10
- data/lib/onyxcord/models.rb +57 -0
- data/lib/onyxcord/{api.rb → rest/client.rb} +23 -22
- 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} +7 -25
- 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 +22 -22
- data/lib/onyxcord/{allowed_mentions.rb → utils/allowed_mentions.rb} +1 -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 -42
- metadata +207 -92
- 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 -257
- 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 -703
- 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 -1151
- data/lib/onyxcord/data.rb +0 -57
- data/lib/onyxcord/event_executor.rb +0 -153
- data/lib/onyxcord/gateway.rb +0 -505
- data/lib/onyxcord/http.rb +0 -186
- data/lib/onyxcord/json.rb +0 -49
- data/lib/onyxcord/rate_limiter/async_rest.rb +0 -150
- 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/{errors.rb → core/errors.rb} +0 -0
- /data/lib/onyxcord/{upload.rb → internal/upload.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
data/lib/onyxcord/http.rb
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'httpx'
|
|
4
|
-
require 'net/http'
|
|
5
|
-
require 'onyxcord/json'
|
|
6
|
-
require 'onyxcord/upload'
|
|
7
|
-
require 'securerandom'
|
|
8
|
-
require 'uri'
|
|
9
|
-
|
|
10
|
-
module OnyxCord
|
|
11
|
-
# Modern HTTP adapter wrapping HTTPX with persistent connections, automatic
|
|
12
|
-
# retries on 502, and a response interface compatible with the rest of OnyxCord.
|
|
13
|
-
module HTTP
|
|
14
|
-
# Lightweight response wrapper so call-sites that relied on RestClient's
|
|
15
|
-
# `.body`, `.headers`, `.code` interface keep working unchanged.
|
|
16
|
-
class Response
|
|
17
|
-
# @return [String] the response body
|
|
18
|
-
attr_reader :body
|
|
19
|
-
|
|
20
|
-
# @return [Integer] the HTTP status code
|
|
21
|
-
attr_reader :code
|
|
22
|
-
|
|
23
|
-
# @return [Hash] the response headers (symbol keys, underscored)
|
|
24
|
-
attr_reader :headers
|
|
25
|
-
|
|
26
|
-
def initialize(httpx_response)
|
|
27
|
-
@raw = httpx_response
|
|
28
|
-
@body = httpx_response.body.to_s
|
|
29
|
-
@code = httpx_response.respond_to?(:status) ? httpx_response.status : httpx_response.code.to_i
|
|
30
|
-
response_headers = httpx_response.respond_to?(:headers) ? httpx_response.headers : httpx_response.to_hash
|
|
31
|
-
@headers = normalize_headers(response_headers)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def to_s
|
|
35
|
-
@body
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# RestClient compatibility — some code calls `response` directly as a
|
|
39
|
-
# string (implicit to_s).
|
|
40
|
-
alias_method :to_str, :to_s
|
|
41
|
-
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
def normalize_headers(headers)
|
|
45
|
-
headers.to_h.transform_keys do |key|
|
|
46
|
-
key.to_s.tr('-', '_').downcase.to_sym
|
|
47
|
-
end.transform_values do |value|
|
|
48
|
-
value.is_a?(Array) && value.size == 1 ? value.first : value
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
module_function
|
|
54
|
-
|
|
55
|
-
# The shared HTTPX session with persistent connections for the current thread.
|
|
56
|
-
def session
|
|
57
|
-
Thread.current[:onyxcord_http_session] ||= HTTPX.plugin(:persistent)
|
|
58
|
-
.plugin(:follow_redirects)
|
|
59
|
-
.with(
|
|
60
|
-
fallback_protocol: 'http/1.1',
|
|
61
|
-
ssl: { alpn_protocols: ['http/1.1'] }
|
|
62
|
-
)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Reset the HTTP session (useful for tests).
|
|
66
|
-
def reset!
|
|
67
|
-
Thread.current[:onyxcord_http_session] = nil
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Perform a raw HTTP request and return a {Response}.
|
|
71
|
-
# @param type [Symbol] HTTP method (:get, :post, :put, :patch, :delete)
|
|
72
|
-
# @param url [String] The full URL.
|
|
73
|
-
# @param body [String, Hash, nil] The request body.
|
|
74
|
-
# @param headers [Hash] Request headers.
|
|
75
|
-
# @return [Response]
|
|
76
|
-
def request(type, url, body = nil, **headers)
|
|
77
|
-
http = session.with(headers: headers)
|
|
78
|
-
|
|
79
|
-
raw = case type
|
|
80
|
-
when :get
|
|
81
|
-
http.get(url)
|
|
82
|
-
when :post
|
|
83
|
-
if multipart?(body)
|
|
84
|
-
# Multipart upload
|
|
85
|
-
post_multipart(url, body, headers)
|
|
86
|
-
else
|
|
87
|
-
http.post(url, body: body)
|
|
88
|
-
end
|
|
89
|
-
when :put
|
|
90
|
-
http.put(url, body: body)
|
|
91
|
-
when :patch
|
|
92
|
-
http.patch(url, body: body)
|
|
93
|
-
when :delete
|
|
94
|
-
http.delete(url)
|
|
95
|
-
else
|
|
96
|
-
raise ArgumentError, "Unknown HTTP method: #{type}"
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# HTTPX returns an error response object on network failures
|
|
100
|
-
raise raw.error if raw.is_a?(HTTPX::ErrorResponse)
|
|
101
|
-
|
|
102
|
-
Response.new(raw)
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def post_multipart(url, body, headers)
|
|
106
|
-
uri = URI(url)
|
|
107
|
-
boundary = "----OnyxCord#{SecureRandom.hex(12)}"
|
|
108
|
-
request = Net::HTTP::Post.new(uri)
|
|
109
|
-
headers.each { |key, value| request[key.to_s] = value }
|
|
110
|
-
request['Content-Type'] = "multipart/form-data; boundary=#{boundary}"
|
|
111
|
-
request.body = multipart_body(body, boundary)
|
|
112
|
-
|
|
113
|
-
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
114
|
-
http.request(request)
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def multipart_body(body, boundary)
|
|
119
|
-
output = String.new.b
|
|
120
|
-
|
|
121
|
-
multipart_parts(body).each do |part|
|
|
122
|
-
key = part[:name]
|
|
123
|
-
value = part[:value]
|
|
124
|
-
output << "--#{boundary}\r\n"
|
|
125
|
-
if part[:filename]
|
|
126
|
-
value.rewind if value.respond_to?(:rewind)
|
|
127
|
-
filename = part[:filename]
|
|
128
|
-
output << "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{filename}\"\r\n"
|
|
129
|
-
output << "Content-Type: #{part[:content_type] || multipart_content_type(filename)}\r\n\r\n"
|
|
130
|
-
output << value.read.to_s
|
|
131
|
-
else
|
|
132
|
-
output << "Content-Disposition: form-data; name=\"#{key}\"\r\n"
|
|
133
|
-
output << "\r\n"
|
|
134
|
-
output << value.to_s
|
|
135
|
-
end
|
|
136
|
-
output << "\r\n"
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
output << "--#{boundary}--\r\n"
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def multipart_content_type(filename)
|
|
143
|
-
File.extname(filename).casecmp('.txt').zero? ? 'text/plain' : 'application/octet-stream'
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
def multipart?(body)
|
|
147
|
-
return true if body.is_a?(Array) && body.all? { |part| part.is_a?(Hash) && part[:name] && part.key?(:value) }
|
|
148
|
-
|
|
149
|
-
body.is_a?(Hash) && body.any? { |_, value| value.respond_to?(:read) || value.respond_to?(:path) }
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def multipart_parts(body)
|
|
153
|
-
return body if body.is_a?(Array)
|
|
154
|
-
|
|
155
|
-
body.map do |key, value|
|
|
156
|
-
if value.respond_to?(:read) || value.respond_to?(:path)
|
|
157
|
-
upload = OnyxCord::Upload.wrap(value)
|
|
158
|
-
{ name: key, value: upload, filename: upload.filename, content_type: upload.content_type }
|
|
159
|
-
else
|
|
160
|
-
{ name: key, value: value }
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# Convenience wrappers
|
|
166
|
-
def get(url, **headers)
|
|
167
|
-
request(:get, url, nil, **headers)
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def post(url, body = nil, **headers)
|
|
171
|
-
request(:post, url, body, **headers)
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def put(url, body = nil, **headers)
|
|
175
|
-
request(:put, url, body, **headers)
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def patch(url, body = nil, **headers)
|
|
179
|
-
request(:patch, url, body, **headers)
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def delete(url, **headers)
|
|
183
|
-
request(:delete, url, nil, **headers)
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
end
|
data/lib/onyxcord/json.rb
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
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
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'json'
|
|
4
|
-
require 'onyxcord/async/runtime'
|
|
5
|
-
|
|
6
|
-
module OnyxCord
|
|
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 { OnyxCord::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
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module OnyxCord
|
|
4
|
-
module RateLimiter
|
|
5
|
-
# Sliding-window limiter for Gateway sends.
|
|
6
|
-
class Gateway
|
|
7
|
-
DEFAULT_LIMIT = 120
|
|
8
|
-
DEFAULT_INTERVAL = 60
|
|
9
|
-
|
|
10
|
-
def initialize(limit: DEFAULT_LIMIT, interval: DEFAULT_INTERVAL, clock: -> { Time.now }, sleeper: ->(duration) { sleep(duration) })
|
|
11
|
-
@limit = limit
|
|
12
|
-
@interval = interval
|
|
13
|
-
@clock = clock
|
|
14
|
-
@sleeper = sleeper
|
|
15
|
-
@sent_at = []
|
|
16
|
-
@mutex = Mutex.new
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def wait
|
|
20
|
-
@mutex.synchronize do
|
|
21
|
-
now = @clock.call
|
|
22
|
-
prune(now)
|
|
23
|
-
|
|
24
|
-
if @sent_at.length >= @limit
|
|
25
|
-
sleep_for = @interval - (now - @sent_at.first)
|
|
26
|
-
@sleeper.call(sleep_for) if sleep_for.positive?
|
|
27
|
-
now = @clock.call
|
|
28
|
-
prune(now)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
@sent_at << now
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def prune(now)
|
|
38
|
-
@sent_at.shift while @sent_at.any? && (now - @sent_at.first) >= @interval
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'onyxcord/json'
|
|
4
|
-
|
|
5
|
-
module OnyxCord
|
|
6
|
-
module RateLimiter
|
|
7
|
-
# Discord REST rate limiter keyed by route/major parameter and remapped to
|
|
8
|
-
# X-RateLimit-Bucket whenever Discord returns a concrete bucket id.
|
|
9
|
-
class Rest
|
|
10
|
-
DEFAULT_ENTRY_TTL = 3600
|
|
11
|
-
DEFAULT_PRUNE_INTERVAL = 100
|
|
12
|
-
|
|
13
|
-
def initialize(clock: -> { Time.now }, entry_ttl: DEFAULT_ENTRY_TTL, prune_interval: DEFAULT_PRUNE_INTERVAL)
|
|
14
|
-
@route_buckets = {}
|
|
15
|
-
@bucket_mutexes = {}
|
|
16
|
-
@bucket_last_used = {}
|
|
17
|
-
@global_mutex = Mutex.new
|
|
18
|
-
@clock = clock
|
|
19
|
-
@entry_ttl = entry_ttl
|
|
20
|
-
@prune_interval = prune_interval
|
|
21
|
-
@requests_since_prune = 0
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def before_request(route, major_parameter)
|
|
25
|
-
mutex_wait(mutex_for(route, major_parameter))
|
|
26
|
-
mutex_wait(@global_mutex) if @global_mutex.locked?
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def record_response(route, major_parameter, headers)
|
|
30
|
-
headers = normalize_headers(headers)
|
|
31
|
-
bucket = headers[:x_ratelimit_bucket]
|
|
32
|
-
key = route_key(route, major_parameter)
|
|
33
|
-
touch(key)
|
|
34
|
-
|
|
35
|
-
if bucket
|
|
36
|
-
bucket = bucket_key(bucket, major_parameter)
|
|
37
|
-
@route_buckets[key] = bucket
|
|
38
|
-
touch(bucket)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
return unless headers[:x_ratelimit_remaining] == '0'
|
|
42
|
-
|
|
43
|
-
wait_seconds = headers[:x_ratelimit_reset_after].to_f
|
|
44
|
-
return unless wait_seconds.positive?
|
|
45
|
-
|
|
46
|
-
sync_wait(wait_seconds, mutex_for(route, major_parameter))
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def handle_rate_limit(route, major_parameter, response)
|
|
50
|
-
headers = normalize_headers(response.headers)
|
|
51
|
-
mutex = headers[:x_ratelimit_global] == 'true' || headers[:x_ratelimit_scope] == 'global' ? @global_mutex : mutex_for(route, major_parameter)
|
|
52
|
-
wait_seconds = retry_after(response, headers)
|
|
53
|
-
|
|
54
|
-
sync_wait(wait_seconds, mutex) if wait_seconds.positive?
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def stats
|
|
58
|
-
{
|
|
59
|
-
route_buckets: @route_buckets.size,
|
|
60
|
-
bucket_mutexes: @bucket_mutexes.size,
|
|
61
|
-
tracked_keys: @bucket_last_used.size
|
|
62
|
-
}
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def prune!
|
|
66
|
-
return 0 unless @entry_ttl
|
|
67
|
-
|
|
68
|
-
cutoff = @clock.call - @entry_ttl
|
|
69
|
-
stale_keys = @bucket_last_used.select { |_, last_used| last_used < cutoff }.keys
|
|
70
|
-
|
|
71
|
-
stale_keys.each do |key|
|
|
72
|
-
@bucket_mutexes.delete(key)
|
|
73
|
-
@bucket_last_used.delete(key)
|
|
74
|
-
@route_buckets.delete(key)
|
|
75
|
-
@route_buckets.delete_if { |_, bucket_key| bucket_key == key }
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
@requests_since_prune = 0
|
|
79
|
-
stale_keys.length
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
private
|
|
83
|
-
|
|
84
|
-
def mutex_for(route, major_parameter)
|
|
85
|
-
key = resolved_key(route, major_parameter)
|
|
86
|
-
touch(key)
|
|
87
|
-
@bucket_mutexes[key] ||= Mutex.new
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def resolved_key(route, major_parameter)
|
|
91
|
-
@route_buckets[route_key(route, major_parameter)] || route_key(route, major_parameter)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def route_key(route, major_parameter)
|
|
95
|
-
[route, major_parameter].freeze
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def bucket_key(bucket, major_parameter)
|
|
99
|
-
[:bucket, bucket, major_parameter].freeze
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def retry_after(response, headers)
|
|
103
|
-
body = response.respond_to?(:body) ? response.body : response.to_s
|
|
104
|
-
if body && !body.empty?
|
|
105
|
-
data = JSON.parse(body)
|
|
106
|
-
return data['retry_after'].to_f if data['retry_after']
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
(headers[:retry_after] || 0).to_f
|
|
110
|
-
rescue JSON::ParserError
|
|
111
|
-
(headers[:retry_after] || 0).to_f
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def normalize_headers(headers)
|
|
115
|
-
headers.each_with_object({}) do |(key, value), memo|
|
|
116
|
-
memo[key.to_s.tr('-', '_').downcase.to_sym] = value.to_s
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def touch(key)
|
|
121
|
-
@bucket_last_used[key] = @clock.call
|
|
122
|
-
prune_if_needed
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def prune_if_needed
|
|
126
|
-
return unless @prune_interval
|
|
127
|
-
|
|
128
|
-
@requests_since_prune += 1
|
|
129
|
-
prune! if @requests_since_prune >= @prune_interval
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def sync_wait(time, mutex)
|
|
133
|
-
mutex.synchronize { sleep time }
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def mutex_wait(mutex)
|
|
137
|
-
mutex.lock
|
|
138
|
-
mutex.unlock
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
end
|
data/lib/onyxcord/version.rb
DELETED
data/lib/onyxcord/websocket.rb
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'onyxcord/async/runtime'
|
|
4
|
-
require 'async/http/endpoint'
|
|
5
|
-
require 'async/http/protocol/http11'
|
|
6
|
-
require 'async/websocket/client'
|
|
7
|
-
|
|
8
|
-
module OnyxCord
|
|
9
|
-
class WebSocket
|
|
10
|
-
attr_reader :connected
|
|
11
|
-
|
|
12
|
-
alias_method :connected?, :connected
|
|
13
|
-
|
|
14
|
-
def initialize(host, open_handler, message_handler, error_handler, close_handler)
|
|
15
|
-
@host = host
|
|
16
|
-
@open_handler = open_handler
|
|
17
|
-
@message_handler = message_handler
|
|
18
|
-
@error_handler = error_handler
|
|
19
|
-
@close_handler = close_handler
|
|
20
|
-
|
|
21
|
-
@connection = nil
|
|
22
|
-
@connected = false
|
|
23
|
-
|
|
24
|
-
connect
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def send(data)
|
|
28
|
-
return unless @connection
|
|
29
|
-
|
|
30
|
-
data = data.to_json if data.is_a?(Hash)
|
|
31
|
-
@connection.write(Protocol::WebSocket::TextMessage.generate(data))
|
|
32
|
-
@connection.flush
|
|
33
|
-
rescue StandardError => e
|
|
34
|
-
@error_handler&.call(e)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def close
|
|
38
|
-
@connected = false
|
|
39
|
-
@connection&.close
|
|
40
|
-
rescue StandardError
|
|
41
|
-
# Ignore errors on close
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
private
|
|
45
|
-
|
|
46
|
-
def connect
|
|
47
|
-
endpoint = websocket_endpoint(@host)
|
|
48
|
-
|
|
49
|
-
@task = OnyxCord::AsyncRuntime.async do
|
|
50
|
-
Async::WebSocket::Client.connect(endpoint, extensions: nil) do |connection|
|
|
51
|
-
@connection = connection
|
|
52
|
-
@connected = true
|
|
53
|
-
@open_handler&.call
|
|
54
|
-
|
|
55
|
-
while (message = connection.read)
|
|
56
|
-
@message_handler&.call(message.to_str)
|
|
57
|
-
end
|
|
58
|
-
rescue StandardError => e
|
|
59
|
-
@error_handler&.call(e)
|
|
60
|
-
ensure
|
|
61
|
-
@connected = false
|
|
62
|
-
@close_handler&.call(nil)
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
rescue StandardError => e
|
|
66
|
-
@error_handler&.call(e)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def websocket_endpoint(url)
|
|
70
|
-
Async::HTTP::Endpoint.parse(
|
|
71
|
-
url,
|
|
72
|
-
protocol: Async::HTTP::Protocol::HTTP11,
|
|
73
|
-
alpn_protocols: ['http/1.1']
|
|
74
|
-
)
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|