mailkite 0.5.0 → 0.8.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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mailkite.rb +154 -6
  3. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16ccb6cd1fe7bcc80237e945bd42ea0f5f0f69d6fc618b955d8b0e93f70c04ef
4
- data.tar.gz: 198dd419d07e5dda9fb2023ec653b600098f63f6837f11344db3e156d28c37e3
3
+ metadata.gz: ea16a5e20d5b4548d304b4c3ddd9a11d8ab600c10084b97128d409f92e99e816
4
+ data.tar.gz: b05b9ccaca0842f125cbe58841ba68b8ef57477feb159eb49498d522e4c1c04e
5
5
  SHA512:
6
- metadata.gz: d61fd1fe911b68d90fb8359eb62fca139a8858d8dd3752d4930420e6ea2fdb6ed59c9264936a3582359ae407d5320946621c7ddcca151e691aeb24a6bf8977a5
7
- data.tar.gz: 88ff488ba25e4ffccf1fdc4c26b5bcaf38b2a7bf7fb3d795a0f8165c08f53e6166bcb01420ec826c370895b163b83cd427ae7e984c1b97f4953eece037499d69
6
+ metadata.gz: 322718f1d3f37a5861fb5d9436dba95de176bdff6a560645b695371ca70029d5ca2dd320a5379b1b7513cf767bc9143b04c46ec668761667c7262707f91a3522
7
+ data.tar.gz: '03179b18866ef5ffa56291fef71046e5d3d869f48af530f2ab4d5169ecdd80b55a6d00bd947ad7dc870d66f2656dfd56e7bd86f6e28c749f3cc2c0a79a1f1275'
data/lib/mailkite.rb CHANGED
@@ -15,7 +15,7 @@ require "base64"
15
15
  require "cgi"
16
16
 
17
17
  module Mailkite
18
- VERSION = "0.5.0"
18
+ VERSION = "0.8.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
@@ -147,6 +147,7 @@ module Mailkite
147
147
  "GET" => Net::HTTP::Get,
148
148
  "POST" => Net::HTTP::Post,
149
149
  "PUT" => Net::HTTP::Put,
150
+ "PATCH" => Net::HTTP::Patch,
150
151
  "DELETE" => Net::HTTP::Delete,
151
152
  }.freeze
152
153
 
@@ -165,6 +166,27 @@ module Mailkite
165
166
  req.body = JSON.generate(body)
166
167
  end
167
168
 
169
+ perform(req, uri)
170
+ end
171
+
172
+ # Raw-binary request used by uploadAttachment for `bytes`/`path` uploads:
173
+ # the body is the file bytes themselves (not JSON, not multipart) and the
174
+ # Content-Type names the file's media type. Same auth + response parsing.
175
+ def request_binary(bytes, filename:, content_type:, retention_days: nil)
176
+ query = { "filename" => filename }
177
+ query["retentionDays"] = retention_days unless retention_days.nil?
178
+ qs = query.map { |k, v| "#{CGI.escape(k)}=#{CGI.escape(v.to_s)}" }.join("&")
179
+ uri = URI("#{@base_url}/v1/attachments?#{qs}")
180
+ req = Net::HTTP::Post.new(uri)
181
+ req["Authorization"] = "Bearer #{@api_key}"
182
+ req["Content-Type"] = content_type
183
+ req.body = bytes
184
+ perform(req, uri)
185
+ end
186
+
187
+ # Send a prepared Net::HTTP request and parse the JSON response (shared by
188
+ # `request` and `request_binary`).
189
+ def perform(req, uri)
168
190
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) }
169
191
  text = res.body
170
192
  data = text && !text.empty? ? JSON.parse(text) : nil
@@ -184,12 +206,73 @@ module Mailkite
184
206
  request("POST", "/v1/send", message)
185
207
  end
186
208
 
187
- # Upload a file (base64 `content`) and get back a secure, time-limited URL to
188
- # reference as a send() attachment ({ filename, url }) or link inline —
189
- # instead of base64-inlining large files on every send. `file` keys:
190
- # filename, content (base64), plus optional contentType and retentionDays.
209
+ # Extension MIME map for raw-binary attachment uploads (default
210
+ # application/octet-stream when an extension is unknown).
211
+ ATTACHMENT_MIME = {
212
+ "pdf" => "application/pdf",
213
+ "png" => "image/png",
214
+ "jpg" => "image/jpeg",
215
+ "jpeg" => "image/jpeg",
216
+ "gif" => "image/gif",
217
+ "webp" => "image/webp",
218
+ "svg" => "image/svg+xml",
219
+ "csv" => "text/csv",
220
+ "txt" => "text/plain",
221
+ "html" => "text/html",
222
+ "json" => "application/json",
223
+ "zip" => "application/zip",
224
+ "doc" => "application/msword",
225
+ "docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
226
+ "xls" => "application/vnd.ms-excel",
227
+ "xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
228
+ "ics" => "text/calendar",
229
+ "ical" => "text/calendar",
230
+ }.freeze
231
+
232
+ # Upload a file and get back a secure, time-limited URL to reference as a
233
+ # send() attachment ({ filename, url }) or link inline — instead of
234
+ # base64-inlining large files on every send. Provide the file ONE of four
235
+ # ways via the `file` hash (priority order):
236
+ # 1. url — MailKite fetches & re-hosts the remote file
237
+ # 2. bytes — raw binary string, uploaded directly
238
+ # 3. path — local file read off disk, then uploaded as bytes
239
+ # 4. content — base64 string (the original behavior)
240
+ # Plus optional filename, contentType, and retentionDays.
191
241
  def uploadAttachment(file)
192
- request("POST", "/v1/attachments", file)
242
+ url = file["url"] || file[:url]
243
+ bytes = file["bytes"] || file[:bytes]
244
+ path = file["path"] || file[:path]
245
+ content = file["content"] || file[:content]
246
+ filename = file["filename"] || file[:filename]
247
+ content_type = file["contentType"] || file[:contentType]
248
+ retention_days = file["retentionDays"] || file[:retentionDays]
249
+
250
+ if url
251
+ body = { "url" => url }
252
+ body["filename"] = filename unless filename.nil?
253
+ body["contentType"] = content_type unless content_type.nil?
254
+ body["retentionDays"] = retention_days unless retention_days.nil?
255
+ return request("POST", "/v1/attachments", body)
256
+ end
257
+
258
+ if bytes || path
259
+ if path
260
+ bytes = File.binread(path)
261
+ filename ||= File.basename(path)
262
+ content_type ||= ATTACHMENT_MIME[File.extname(path).downcase.delete_prefix(".")]
263
+ end
264
+ content_type ||= "application/octet-stream"
265
+ return request_binary(bytes, filename: filename, content_type: content_type, retention_days: retention_days)
266
+ end
267
+
268
+ if content
269
+ body = { "content" => content, "filename" => filename }
270
+ body["contentType"] = content_type unless content_type.nil?
271
+ body["retentionDays"] = retention_days unless retention_days.nil?
272
+ return request("POST", "/v1/attachments", body)
273
+ end
274
+
275
+ raise ArgumentError, "uploadAttachment requires one of: path, bytes, url, or content"
193
276
  end
194
277
 
195
278
  # Run an inbound message through an AI agent. message keys: text (required),
@@ -284,6 +367,71 @@ module Mailkite
284
367
  request("POST", "/api/deliveries/#{id}/retry")
285
368
  end
286
369
 
370
+ # --- Lists ----------------------------------------------------------
371
+ def listLists
372
+ request("GET", "/api/lists")
373
+ end
374
+
375
+ def createList(body)
376
+ request("POST", "/api/lists", body)
377
+ end
378
+
379
+ def getList(id)
380
+ request("GET", "/api/lists/#{id}")
381
+ end
382
+
383
+ def updateList(id, body)
384
+ request("PATCH", "/api/lists/#{id}", body)
385
+ end
386
+
387
+ def deleteList(id)
388
+ request("DELETE", "/api/lists/#{id}")
389
+ end
390
+
391
+ def listListContacts(id)
392
+ request("GET", "/api/lists/#{id}/contacts")
393
+ end
394
+
395
+ def addListContacts(id, body)
396
+ request("POST", "/api/lists/#{id}/contacts", body)
397
+ end
398
+
399
+ def removeListContact(id, contactId)
400
+ request("DELETE", "/api/lists/#{id}/contacts/#{contactId}")
401
+ end
402
+
403
+ # --- Broadcasts -----------------------------------------------------
404
+ def listBroadcasts
405
+ request("GET", "/api/broadcasts")
406
+ end
407
+
408
+ def createBroadcast(body)
409
+ request("POST", "/api/broadcasts", body)
410
+ end
411
+
412
+ def getBroadcast(id)
413
+ request("GET", "/api/broadcasts/#{id}")
414
+ end
415
+
416
+ def updateBroadcast(id, body)
417
+ request("PATCH", "/api/broadcasts/#{id}", body)
418
+ end
419
+
420
+ def deleteBroadcast(id)
421
+ request("DELETE", "/api/broadcasts/#{id}")
422
+ end
423
+
424
+ def sendBroadcast(id, body)
425
+ request("POST", "/api/broadcasts/#{id}/send", body)
426
+ end
427
+
428
+ # --- Docs -----------------------------------------------------------
429
+ # Semantic search over the MailKite docs. PUBLIC — no auth required.
430
+ # Returns { "query" => ..., "matches" => [...] }.
431
+ def semanticSearch(query)
432
+ request("GET", "/v1/docs/search?query=#{CGI.escape(query)}")
433
+ end
434
+
287
435
  # --- Webhooks -------------------------------------------------------
288
436
  # Instance wrapper around Mailkite.verify_webhook, so you can verify on an
289
437
  # existing client. No network call; no API key required.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mailkite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - MailKite
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-25 00:00:00.000000000 Z
11
+ date: 2026-06-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Send and manage email over your own authenticated domain with MailKite.
14
14
  email:
@@ -21,7 +21,7 @@ homepage: https://mailkite.dev/docs/libraries
21
21
  licenses:
22
22
  - MIT
23
23
  metadata:
24
- source_code_uri: https://github.com/mailkite/mailkite
24
+ source_code_uri: https://github.com/mailkite/mailkite-ruby
25
25
  post_install_message:
26
26
  rdoc_options: []
27
27
  require_paths: