chat_sdk-discord 0.2.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3c068b8a4ec22b48a3e677299e12b777dd836f60de1775a91c50b914c5fc574
4
- data.tar.gz: 8201aba74defacec745d0096e46eede4dd9c5fd0efeb4fe595c2a2cd695b9913
3
+ metadata.gz: 94defe37277d975df73d08de69cd39771dd5493d3b349a0485abf2b0d6619bbc
4
+ data.tar.gz: b7b13efb6f537a75c483400fa8138d54d88c872ffad31504508f89dbef2e1f26
5
5
  SHA512:
6
- metadata.gz: a50e09acc0f29e76140e1019db6044b46864064360aaecab313fd9a3781df41ddfce5f8a21787327a4927329c32ba727d891f62758a96dcde62d8c30c9946d02
7
- data.tar.gz: af622c3cf1dbfd417fa85ea7f0313721e7580d43e365914246c6ae15f4abad0ae5aae77615a114fca29269545a68e218f71c263d2f2964247c045559b21fb53d
6
+ metadata.gz: '0196644828476e679e0722924979e0bf4644aaa22252a38fad2a6eb7ffe8c86eec8040e21a98f1fea4d7ae2989a2151983ee96fa853e7826c46dc7b8f1ee8d85'
7
+ data.tar.gz: 1bba2dbacc4f866839c27a3ca405e73b42de376cc4ba3df6694deb0d750a05a528dc6717b44b90adfc0da20eb4dafdfd5ba612030437c143c54822a1d5a19e78
@@ -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 @public_key
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!(@public_key, signature, timestamp, body)
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
- body = rack_request.body.read
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
- msg = ChatSDK::PostableMessage.from(message)
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
- require_capability!(:edit_messages)
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
- require_capability!(:direct_messages)
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 |msg|
164
- ChatSDK::Message.new(
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
- msg = ChatSDK::PostableMessage.from(postable_message)
197
- if msg.card?
198
- @renderer.render(msg.card)
140
+ if postable_message.card?
141
+ @renderer.render(postable_message.card)
199
142
  else
200
- msg.text
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 = conn.post("#{API_PREFIX}/channels/#{channel_id}/messages", payload) do |req|
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 connection
84
- @connection ||= Faraday.new(url: BASE_URL) do |f|
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 request(method, path, body = nil)
92
- response = connection.public_send(method, path) do |req|
93
- req.headers["Authorization"] = "Bot #{@bot_token}"
94
- req.body = body if body && method != :get
95
- end
75
+ def adapter_name
76
+ :discord
77
+ end
96
78
 
97
- handle_response(response)
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 handle_response(response)
101
- return response.body if response.success?
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
@@ -3,8 +3,7 @@
3
3
  module ChatSDK
4
4
  module Discord
5
5
  module Signature
6
- def self.verify!(public_key_hex, signature_hex, timestamp, body)
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.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
- - Rootly
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
- - eng@rootly.com
56
+ - quentin@rootly.com
57
57
  executables: []
58
58
  extensions: []
59
59
  extra_rdoc_files: []