mailkite 0.4.0 → 0.6.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/mailkite.rb +121 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 79f557c742e7377043c6fbbfc62c788bde380e6c75f647a354c4182346275a4d
|
|
4
|
+
data.tar.gz: be02421caccae364a81433fd239d043e19aeb546817c0acbf3c9727850894785
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 94b4ec4ac99c756f5887d9f5526ac931df163675e0dcd7a460a9703847b253a5c2852d744f227704f847791bb183f6a1226178ee0714b26eb6fc4249d68a3e17
|
|
7
|
+
data.tar.gz: 501cce3bfee6edd4123a24f337325668e2a4009523814b5d3989fa197a4d4fc2185587dfd3bdf694f2ceed3ba0330e14ceeeac54f351f4f8e30c91b4310c7a97
|
data/lib/mailkite.rb
CHANGED
|
@@ -15,7 +15,7 @@ require "base64"
|
|
|
15
15
|
require "cgi"
|
|
16
16
|
|
|
17
17
|
module Mailkite
|
|
18
|
-
VERSION = "0.
|
|
18
|
+
VERSION = "0.6.0"
|
|
19
19
|
DEFAULT_BASE_URL = "https://api.mailkite.dev"
|
|
20
20
|
# Reject webhook events older than this (ms) to block replays. Pass 0 to disable.
|
|
21
21
|
DEFAULT_TOLERANCE_MS = 5 * 60 * 1000
|
|
@@ -61,6 +61,24 @@ module Mailkite
|
|
|
61
61
|
'{"status":"ok"}'
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
# Control-mode reply telling MailKite to mark the message as spam.
|
|
65
|
+
# Returns the exact string `{"status":"spam"}`.
|
|
66
|
+
def self.reply_spam
|
|
67
|
+
'{"status":"spam"}'
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Control-mode reply telling MailKite to drop (discard) the message.
|
|
71
|
+
# Returns the exact string `{"status":"drop"}`.
|
|
72
|
+
def self.reply_drop
|
|
73
|
+
'{"status":"drop"}'
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Control-mode reply telling MailKite to block the sender.
|
|
77
|
+
# Returns the exact string `{"status":"ok","actions":[{"type":"block-sender"}]}`.
|
|
78
|
+
def self.reply_block_sender
|
|
79
|
+
'{"status":"ok","actions":[{"type":"block-sender"}]}'
|
|
80
|
+
end
|
|
81
|
+
|
|
64
82
|
# Encrypt a UTF-8 string to a customer RSA public key (SPKI/PEM), producing the
|
|
65
83
|
# MailKite at-rest envelope as a compact JSON string. Hybrid scheme: a fresh
|
|
66
84
|
# AES-256-GCM content key encrypts the data, then the raw key is wrapped with
|
|
@@ -147,6 +165,27 @@ module Mailkite
|
|
|
147
165
|
req.body = JSON.generate(body)
|
|
148
166
|
end
|
|
149
167
|
|
|
168
|
+
perform(req, uri)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Raw-binary request used by uploadAttachment for `bytes`/`path` uploads:
|
|
172
|
+
# the body is the file bytes themselves (not JSON, not multipart) and the
|
|
173
|
+
# Content-Type names the file's media type. Same auth + response parsing.
|
|
174
|
+
def request_binary(bytes, filename:, content_type:, retention_days: nil)
|
|
175
|
+
query = { "filename" => filename }
|
|
176
|
+
query["retentionDays"] = retention_days unless retention_days.nil?
|
|
177
|
+
qs = query.map { |k, v| "#{CGI.escape(k)}=#{CGI.escape(v.to_s)}" }.join("&")
|
|
178
|
+
uri = URI("#{@base_url}/v1/attachments?#{qs}")
|
|
179
|
+
req = Net::HTTP::Post.new(uri)
|
|
180
|
+
req["Authorization"] = "Bearer #{@api_key}"
|
|
181
|
+
req["Content-Type"] = content_type
|
|
182
|
+
req.body = bytes
|
|
183
|
+
perform(req, uri)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Send a prepared Net::HTTP request and parse the JSON response (shared by
|
|
187
|
+
# `request` and `request_binary`).
|
|
188
|
+
def perform(req, uri)
|
|
150
189
|
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) }
|
|
151
190
|
text = res.body
|
|
152
191
|
data = text && !text.empty? ? JSON.parse(text) : nil
|
|
@@ -166,6 +205,75 @@ module Mailkite
|
|
|
166
205
|
request("POST", "/v1/send", message)
|
|
167
206
|
end
|
|
168
207
|
|
|
208
|
+
# Extension → MIME map for raw-binary attachment uploads (default
|
|
209
|
+
# application/octet-stream when an extension is unknown).
|
|
210
|
+
ATTACHMENT_MIME = {
|
|
211
|
+
"pdf" => "application/pdf",
|
|
212
|
+
"png" => "image/png",
|
|
213
|
+
"jpg" => "image/jpeg",
|
|
214
|
+
"jpeg" => "image/jpeg",
|
|
215
|
+
"gif" => "image/gif",
|
|
216
|
+
"webp" => "image/webp",
|
|
217
|
+
"svg" => "image/svg+xml",
|
|
218
|
+
"csv" => "text/csv",
|
|
219
|
+
"txt" => "text/plain",
|
|
220
|
+
"html" => "text/html",
|
|
221
|
+
"json" => "application/json",
|
|
222
|
+
"zip" => "application/zip",
|
|
223
|
+
"doc" => "application/msword",
|
|
224
|
+
"docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
225
|
+
"xls" => "application/vnd.ms-excel",
|
|
226
|
+
"xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
227
|
+
"ics" => "text/calendar",
|
|
228
|
+
"ical" => "text/calendar",
|
|
229
|
+
}.freeze
|
|
230
|
+
|
|
231
|
+
# Upload a file and get back a secure, time-limited URL to reference as a
|
|
232
|
+
# send() attachment ({ filename, url }) or link inline — instead of
|
|
233
|
+
# base64-inlining large files on every send. Provide the file ONE of four
|
|
234
|
+
# ways via the `file` hash (priority order):
|
|
235
|
+
# 1. url — MailKite fetches & re-hosts the remote file
|
|
236
|
+
# 2. bytes — raw binary string, uploaded directly
|
|
237
|
+
# 3. path — local file read off disk, then uploaded as bytes
|
|
238
|
+
# 4. content — base64 string (the original behavior)
|
|
239
|
+
# Plus optional filename, contentType, and retentionDays.
|
|
240
|
+
def uploadAttachment(file)
|
|
241
|
+
url = file["url"] || file[:url]
|
|
242
|
+
bytes = file["bytes"] || file[:bytes]
|
|
243
|
+
path = file["path"] || file[:path]
|
|
244
|
+
content = file["content"] || file[:content]
|
|
245
|
+
filename = file["filename"] || file[:filename]
|
|
246
|
+
content_type = file["contentType"] || file[:contentType]
|
|
247
|
+
retention_days = file["retentionDays"] || file[:retentionDays]
|
|
248
|
+
|
|
249
|
+
if url
|
|
250
|
+
body = { "url" => url }
|
|
251
|
+
body["filename"] = filename unless filename.nil?
|
|
252
|
+
body["contentType"] = content_type unless content_type.nil?
|
|
253
|
+
body["retentionDays"] = retention_days unless retention_days.nil?
|
|
254
|
+
return request("POST", "/v1/attachments", body)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
if bytes || path
|
|
258
|
+
if path
|
|
259
|
+
bytes = File.binread(path)
|
|
260
|
+
filename ||= File.basename(path)
|
|
261
|
+
content_type ||= ATTACHMENT_MIME[File.extname(path).downcase.delete_prefix(".")]
|
|
262
|
+
end
|
|
263
|
+
content_type ||= "application/octet-stream"
|
|
264
|
+
return request_binary(bytes, filename: filename, content_type: content_type, retention_days: retention_days)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
if content
|
|
268
|
+
body = { "content" => content, "filename" => filename }
|
|
269
|
+
body["contentType"] = content_type unless content_type.nil?
|
|
270
|
+
body["retentionDays"] = retention_days unless retention_days.nil?
|
|
271
|
+
return request("POST", "/v1/attachments", body)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
raise ArgumentError, "uploadAttachment requires one of: path, bytes, url, or content"
|
|
275
|
+
end
|
|
276
|
+
|
|
169
277
|
# Run an inbound message through an AI agent. message keys: text (required),
|
|
170
278
|
# plus optional subject/from/html/routeId/address/model.
|
|
171
279
|
def agent(message)
|
|
@@ -270,6 +378,18 @@ module Mailkite
|
|
|
270
378
|
Mailkite.reply_ok
|
|
271
379
|
end
|
|
272
380
|
|
|
381
|
+
def reply_spam
|
|
382
|
+
Mailkite.reply_spam
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def reply_drop
|
|
386
|
+
Mailkite.reply_drop
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def reply_block_sender
|
|
390
|
+
Mailkite.reply_block_sender
|
|
391
|
+
end
|
|
392
|
+
|
|
273
393
|
def encrypt(plaintext, public_key)
|
|
274
394
|
Mailkite.encrypt(plaintext, public_key)
|
|
275
395
|
end
|