chat_sdk-discord 0.2.1 → 0.3.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/chat_sdk/discord/adapter.rb +44 -70
- data/lib/chat_sdk/discord/api_client.rb +22 -48
- data/lib/chat_sdk/discord/event_parser.rb +4 -2
- data/lib/chat_sdk/discord/signature.rb +1 -2
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cd5ac190ac4b9ef0ac699a10d66ba147e5af7448cdfe271c571f3d2ba78c5ab8
|
|
4
|
+
data.tar.gz: 458821ec7c94570b34d587020318234b91505aa11a2dd81145e9f03704c986d8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1ef601e8b62acde8c447ef64dcc37516575a89108adc203a6443bd0374817b4c9809987c79e504546f904d9a44c1f6af15ee0d6b1d862eacaac292e26dab1a03
|
|
7
|
+
data.tar.gz: f4df9c079de22e5494236d3b2f19caa476ce9b94aa877a94aa3926c97afe3464b90596ae7bafaaa66be2116bfff5ea0fe3d3f16177beefd308092acb6cbddc22
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "erb"
|
|
4
|
-
|
|
5
3
|
module ChatSDK
|
|
6
4
|
module Discord
|
|
7
5
|
class Adapter < ChatSDK::Adapter::Base
|
|
@@ -19,6 +17,8 @@ module ChatSDK
|
|
|
19
17
|
|
|
20
18
|
@client = ApiClient.new(bot_token: @bot_token)
|
|
21
19
|
@renderer = EmbedRenderer.new
|
|
20
|
+
@verify_key = Ed25519::VerifyKey.new([@public_key].pack("H*")) if @public_key
|
|
21
|
+
@dm_channels = {}
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def name
|
|
@@ -30,7 +30,7 @@ module ChatSDK
|
|
|
30
30
|
body = rack_request.body.read
|
|
31
31
|
rack_request.body.rewind
|
|
32
32
|
|
|
33
|
-
unless @
|
|
33
|
+
unless @verify_key
|
|
34
34
|
raise ChatSDK::ConfigurationError, "Discord public_key required for signature verification"
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -41,7 +41,7 @@ module ChatSDK
|
|
|
41
41
|
raise ChatSDK::SignatureVerificationError, "Missing Discord signature headers"
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
Signature.verify!(@
|
|
44
|
+
Signature.verify!(@verify_key, signature, timestamp, body)
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def ack_response(rack_request)
|
|
@@ -60,10 +60,7 @@ module ChatSDK
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def parse_events(rack_request)
|
|
63
|
-
|
|
64
|
-
rack_request.body.rewind
|
|
65
|
-
|
|
66
|
-
payload = JSON.parse(body)
|
|
63
|
+
payload = read_json_body(rack_request)
|
|
67
64
|
EventParser.parse(payload)
|
|
68
65
|
rescue JSON::ParserError
|
|
69
66
|
[]
|
|
@@ -71,17 +68,7 @@ module ChatSDK
|
|
|
71
68
|
|
|
72
69
|
# Outbound
|
|
73
70
|
def post_message(channel_id:, message:, thread_id: nil)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
content = msg.text || msg.card&.fallback_text || ""
|
|
77
|
-
embeds = nil
|
|
78
|
-
components = nil
|
|
79
|
-
|
|
80
|
-
if msg.card?
|
|
81
|
-
rendered = @renderer.render(msg.card)
|
|
82
|
-
embeds = rendered["embeds"]
|
|
83
|
-
components = rendered["components"]
|
|
84
|
-
end
|
|
71
|
+
content, embeds, components = prepare_message_payload(message)
|
|
85
72
|
|
|
86
73
|
result = @client.create_message(
|
|
87
74
|
channel_id,
|
|
@@ -102,18 +89,7 @@ module ChatSDK
|
|
|
102
89
|
end
|
|
103
90
|
|
|
104
91
|
def edit_message(channel_id:, message_id:, message:)
|
|
105
|
-
|
|
106
|
-
msg = ChatSDK::PostableMessage.from(message)
|
|
107
|
-
|
|
108
|
-
content = msg.text || msg.card&.fallback_text || ""
|
|
109
|
-
embeds = nil
|
|
110
|
-
components = nil
|
|
111
|
-
|
|
112
|
-
if msg.card?
|
|
113
|
-
rendered = @renderer.render(msg.card)
|
|
114
|
-
embeds = rendered["embeds"]
|
|
115
|
-
components = rendered["components"]
|
|
116
|
-
end
|
|
92
|
+
content, embeds, components = prepare_message_payload(message)
|
|
117
93
|
|
|
118
94
|
@client.edit_message(
|
|
119
95
|
channel_id,
|
|
@@ -125,80 +101,78 @@ module ChatSDK
|
|
|
125
101
|
end
|
|
126
102
|
|
|
127
103
|
def delete_message(channel_id:, message_id:)
|
|
128
|
-
require_capability!(:delete_messages)
|
|
129
104
|
@client.delete_message(channel_id, message_id)
|
|
130
105
|
end
|
|
131
106
|
|
|
132
|
-
def post_ephemeral(channel_id:, user_id:, message:, thread_id: nil)
|
|
133
|
-
super # raises NotSupportedError
|
|
134
|
-
end
|
|
135
|
-
|
|
136
107
|
def upload_file(channel_id:, io:, filename:, thread_id: nil, comment: nil)
|
|
137
|
-
require_capability!(:file_uploads)
|
|
138
108
|
@client.upload_file(channel_id, io, filename)
|
|
139
109
|
end
|
|
140
110
|
|
|
141
111
|
def add_reaction(channel_id:, message_id:, emoji:)
|
|
142
|
-
require_capability!(:reactions)
|
|
143
112
|
@client.add_reaction(channel_id, message_id, emoji)
|
|
144
113
|
end
|
|
145
114
|
|
|
146
115
|
def remove_reaction(channel_id:, message_id:, emoji:)
|
|
147
|
-
require_capability!(:reactions)
|
|
148
116
|
@client.remove_reaction(channel_id, message_id, emoji)
|
|
149
117
|
end
|
|
150
118
|
|
|
151
119
|
def open_dm(user_id)
|
|
152
|
-
|
|
153
|
-
result = @client.create_dm(user_id)
|
|
154
|
-
result["id"]
|
|
120
|
+
@dm_channels[user_id] ||= @client.create_dm(user_id)["id"]
|
|
155
121
|
end
|
|
156
122
|
|
|
157
123
|
def fetch_messages(channel_id:, thread_id: nil, cursor: nil, limit: 50)
|
|
158
|
-
require_capability!(:message_history)
|
|
159
|
-
|
|
160
124
|
messages_data = @client.get_messages(channel_id, limit: limit, before: cursor)
|
|
161
125
|
messages_data = [] unless messages_data.is_a?(Array)
|
|
162
126
|
|
|
163
|
-
messages = messages_data.map do |
|
|
164
|
-
|
|
165
|
-
id: msg["id"],
|
|
166
|
-
text: msg["content"] || "",
|
|
167
|
-
author: ChatSDK::Author.new(
|
|
168
|
-
id: msg.dig("author", "id") || "unknown",
|
|
169
|
-
name: msg.dig("author", "username") || "unknown",
|
|
170
|
-
platform: :discord
|
|
171
|
-
),
|
|
172
|
-
thread_id: msg["id"],
|
|
173
|
-
channel_id: channel_id,
|
|
174
|
-
platform: :discord,
|
|
175
|
-
raw: msg
|
|
176
|
-
)
|
|
127
|
+
messages = messages_data.map do |data|
|
|
128
|
+
parse_discord_message(data, channel_id)
|
|
177
129
|
end
|
|
178
130
|
|
|
179
131
|
next_cursor = messages_data.any? ? messages_data.last["id"] : nil
|
|
180
132
|
[messages, next_cursor]
|
|
181
133
|
end
|
|
182
134
|
|
|
183
|
-
def open_modal(trigger_id:, modal:)
|
|
184
|
-
super # raises NotSupportedError
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
def start_typing(channel_id:, thread_id: nil)
|
|
188
|
-
super # raises NotSupportedError
|
|
189
|
-
end
|
|
190
|
-
|
|
191
135
|
def mention(user_id)
|
|
192
136
|
"<@#{user_id}>"
|
|
193
137
|
end
|
|
194
138
|
|
|
195
139
|
def render(postable_message)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
@renderer.render(msg.card)
|
|
140
|
+
if postable_message.card?
|
|
141
|
+
@renderer.render(postable_message.card)
|
|
199
142
|
else
|
|
200
|
-
|
|
143
|
+
postable_message.text
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
private
|
|
148
|
+
|
|
149
|
+
def prepare_message_payload(message)
|
|
150
|
+
msg = ChatSDK::PostableMessage.from(message)
|
|
151
|
+
content = msg.text || msg.card&.fallback_text || ""
|
|
152
|
+
embeds = nil
|
|
153
|
+
components = nil
|
|
154
|
+
if msg.card?
|
|
155
|
+
rendered = @renderer.render(msg.card)
|
|
156
|
+
embeds = rendered["embeds"]
|
|
157
|
+
components = rendered["components"]
|
|
201
158
|
end
|
|
159
|
+
[content, embeds, components]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def parse_discord_message(data, channel_id)
|
|
163
|
+
ChatSDK::Message.new(
|
|
164
|
+
id: data["id"],
|
|
165
|
+
text: data["content"] || "",
|
|
166
|
+
author: ChatSDK::Author.new(
|
|
167
|
+
id: data.dig("author", "id") || "unknown",
|
|
168
|
+
name: data.dig("author", "username") || "unknown",
|
|
169
|
+
platform: :discord
|
|
170
|
+
),
|
|
171
|
+
thread_id: data["id"],
|
|
172
|
+
channel_id: channel_id,
|
|
173
|
+
platform: :discord,
|
|
174
|
+
raw: data
|
|
175
|
+
)
|
|
202
176
|
end
|
|
203
177
|
end
|
|
204
178
|
end
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "erb"
|
|
4
|
+
|
|
3
5
|
module ChatSDK
|
|
4
6
|
module Discord
|
|
5
|
-
class ApiClient
|
|
7
|
+
class ApiClient < ChatSDK::ApiClient::Base
|
|
6
8
|
BASE_URL = "https://discord.com"
|
|
7
9
|
API_PREFIX = "/api/v10"
|
|
8
10
|
|
|
@@ -54,69 +56,41 @@ module ChatSDK
|
|
|
54
56
|
request(:get, path)
|
|
55
57
|
end
|
|
56
58
|
|
|
57
|
-
# Typing
|
|
58
|
-
def trigger_typing(channel_id)
|
|
59
|
-
request(:post, "#{API_PREFIX}/channels/#{channel_id}/typing")
|
|
60
|
-
end
|
|
61
|
-
|
|
62
59
|
# File upload
|
|
63
60
|
def upload_file(channel_id, io, filename)
|
|
64
|
-
conn = Faraday.new(url: BASE_URL) do |f|
|
|
65
|
-
f.request :multipart
|
|
66
|
-
f.response :json
|
|
67
|
-
f.adapter :net_http
|
|
68
|
-
end
|
|
69
|
-
|
|
70
61
|
payload = {
|
|
71
62
|
"file[0]" => Faraday::Multipart::FilePart.new(io, "application/octet-stream", filename)
|
|
72
63
|
}
|
|
73
64
|
|
|
74
|
-
response =
|
|
75
|
-
req.headers["Authorization"] = "Bot #{@bot_token}"
|
|
76
|
-
end
|
|
77
|
-
|
|
65
|
+
response = upload_connection.post("#{API_PREFIX}/channels/#{channel_id}/messages", payload)
|
|
78
66
|
handle_response(response)
|
|
79
67
|
end
|
|
80
68
|
|
|
81
69
|
private
|
|
82
70
|
|
|
83
|
-
def
|
|
84
|
-
|
|
85
|
-
f.request :json
|
|
86
|
-
f.response :json
|
|
87
|
-
f.adapter :net_http
|
|
88
|
-
end
|
|
71
|
+
def base_url
|
|
72
|
+
BASE_URL
|
|
89
73
|
end
|
|
90
74
|
|
|
91
|
-
def
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
req.body = body if body && method != :get
|
|
95
|
-
end
|
|
75
|
+
def adapter_name
|
|
76
|
+
:discord
|
|
77
|
+
end
|
|
96
78
|
|
|
97
|
-
|
|
79
|
+
def configure_auth(faraday)
|
|
80
|
+
faraday.headers["Authorization"] = "Bot #{@bot_token}"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def extract_success_body(response)
|
|
84
|
+
response.body
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def extract_retry_after(response)
|
|
88
|
+
body = response.body
|
|
89
|
+
body.is_a?(Hash) ? body["retry_after"]&.to_i : nil
|
|
98
90
|
end
|
|
99
91
|
|
|
100
|
-
def
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if response.status == 429
|
|
104
|
-
retry_after = response.body.is_a?(Hash) ? response.body["retry_after"]&.to_i : nil
|
|
105
|
-
raise ChatSDK::RateLimitedError.new(
|
|
106
|
-
"Discord API rate limited",
|
|
107
|
-
retry_after: retry_after,
|
|
108
|
-
status: response.status,
|
|
109
|
-
body: response.body,
|
|
110
|
-
adapter_name: :discord
|
|
111
|
-
)
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
raise ChatSDK::PlatformError.new(
|
|
115
|
-
"Discord API error: #{response.status}",
|
|
116
|
-
status: response.status,
|
|
117
|
-
body: response.body,
|
|
118
|
-
adapter_name: :discord
|
|
119
|
-
)
|
|
92
|
+
def extract_error_message(response)
|
|
93
|
+
response.status.to_s
|
|
120
94
|
end
|
|
121
95
|
end
|
|
122
96
|
end
|
|
@@ -58,7 +58,8 @@ module ChatSDK
|
|
|
58
58
|
id: user_info[:id],
|
|
59
59
|
name: user_info[:name],
|
|
60
60
|
platform: :discord,
|
|
61
|
-
bot: false
|
|
61
|
+
bot: false,
|
|
62
|
+
locale: user_info[:locale]
|
|
62
63
|
)
|
|
63
64
|
|
|
64
65
|
[ChatSDK::Events::Action.new(
|
|
@@ -77,7 +78,8 @@ module ChatSDK
|
|
|
77
78
|
user = payload.dig("member", "user") || payload["user"] || {}
|
|
78
79
|
{
|
|
79
80
|
id: user["id"] || "unknown",
|
|
80
|
-
name: user["username"] || user["id"] || "unknown"
|
|
81
|
+
name: user["username"] || user["id"] || "unknown",
|
|
82
|
+
locale: payload["locale"]
|
|
81
83
|
}
|
|
82
84
|
end
|
|
83
85
|
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
module ChatSDK
|
|
4
4
|
module Discord
|
|
5
5
|
module Signature
|
|
6
|
-
def self.verify!(
|
|
7
|
-
verify_key = Ed25519::VerifyKey.new([public_key_hex].pack("H*"))
|
|
6
|
+
def self.verify!(verify_key, signature_hex, timestamp, body)
|
|
8
7
|
signature = [signature_hex].pack("H*")
|
|
9
8
|
message = "#{timestamp}#{body}"
|
|
10
9
|
verify_key.verify(signature, message)
|
metadata
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: chat_sdk-discord
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
7
|
+
- Quentin Rousseau
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
@@ -53,7 +53,7 @@ dependencies:
|
|
|
53
53
|
version: '1.3'
|
|
54
54
|
description: Discord bot adapter for the ChatSDK framework
|
|
55
55
|
email:
|
|
56
|
-
-
|
|
56
|
+
- quentin@rootly.com
|
|
57
57
|
executables: []
|
|
58
58
|
extensions: []
|
|
59
59
|
extra_rdoc_files: []
|