feat(dolist): send mail having attachments
This commit is contained in:
parent
b0b7114c3b
commit
61cd9aa8c7
2 changed files with 112 additions and 37 deletions
|
@ -1,3 +1,5 @@
|
||||||
|
require "support/jsv"
|
||||||
|
|
||||||
class Dolist::API
|
class Dolist::API
|
||||||
CONTACT_URL = "https://apiv9.dolist.net/v1/contacts/read?AccountID=%{account_id}"
|
CONTACT_URL = "https://apiv9.dolist.net/v1/contacts/read?AccountID=%{account_id}"
|
||||||
EMAIL_LOGS_URL = "https://apiv9.dolist.net/v1/statistics/email/sendings/transactional/search?AccountID=%{account_id}"
|
EMAIL_LOGS_URL = "https://apiv9.dolist.net/v1/statistics/email/sendings/transactional/search?AccountID=%{account_id}"
|
||||||
|
@ -6,6 +8,7 @@ class Dolist::API
|
||||||
EMAIL_MESSAGES_ADRESSES_REPLIES = "https://apiv9.dolist.net/v1/email/messages/addresses/replies?AccountID=%{account_id}"
|
EMAIL_MESSAGES_ADRESSES_REPLIES = "https://apiv9.dolist.net/v1/email/messages/addresses/replies?AccountID=%{account_id}"
|
||||||
EMAIL_MESSAGES_ADRESSES_PACKSENDERS = "https://apiv9.dolist.net/v1/email/messages/addresses/packsenders?AccountID=%{account_id}"
|
EMAIL_MESSAGES_ADRESSES_PACKSENDERS = "https://apiv9.dolist.net/v1/email/messages/addresses/packsenders?AccountID=%{account_id}"
|
||||||
EMAIL_SENDING_TRANSACTIONAL = "https://apiv9.dolist.net/v1/email/sendings/transactional?AccountID=%{account_id}"
|
EMAIL_SENDING_TRANSACTIONAL = "https://apiv9.dolist.net/v1/email/sendings/transactional?AccountID=%{account_id}"
|
||||||
|
EMAIL_SENDING_TRANSACTIONAL_ATTACHMENT = "https://apiv9.dolist.net/v1/email/sendings/transactional/attachment?AccountID=%{account_id}"
|
||||||
EMAIL_SENDING_TRANSACTIONAL_SEARCH = "https://apiv9.dolist.net/v1/email/sendings/transactional/search?AccountID=%{account_id}"
|
EMAIL_SENDING_TRANSACTIONAL_SEARCH = "https://apiv9.dolist.net/v1/email/sendings/transactional/search?AccountID=%{account_id}"
|
||||||
|
|
||||||
class_attribute :limit_remaining, :limit_reset_at
|
class_attribute :limit_remaining, :limit_reset_at
|
||||||
|
@ -34,39 +37,62 @@ class Dolist::API
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_email(mail)
|
def send_email(mail)
|
||||||
url = format(EMAIL_SENDING_TRANSACTIONAL, account_id: account_id)
|
if mail.attachments.any? { !_1.inline? }
|
||||||
|
return send_email_with_attachment(mail)
|
||||||
|
end
|
||||||
|
|
||||||
body = {
|
body = { "TransactionalSending": prepare_mail_body(mail) }
|
||||||
"TransactionalSending": {
|
|
||||||
"Type": "TransactionalService",
|
|
||||||
"Contact": {
|
|
||||||
"FieldList": [
|
|
||||||
{
|
|
||||||
"ID": EMAIL_KEY,
|
|
||||||
"Value": mail.to.first
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Message": {
|
|
||||||
"Name": mail['X-Dolist-Message-Name'].value,
|
|
||||||
"Subject": mail.subject,
|
|
||||||
"SenderID": sender_id,
|
|
||||||
"ForceHttp": true,
|
|
||||||
"Format": "html",
|
|
||||||
"DisableOpenTracking": true,
|
|
||||||
"IsTrackingValidated": true
|
|
||||||
},
|
|
||||||
"MessageContent": {
|
|
||||||
"SourceCode": mail_source_code(mail),
|
|
||||||
"EncodingType": "UTF8",
|
|
||||||
"EnableTrackingDetection": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
url = format_url(EMAIL_SENDING_TRANSACTIONAL)
|
||||||
post(url, body.to_json)
|
post(url, body.to_json)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def send_email_with_attachment(mail)
|
||||||
|
uri = URI(format_url(EMAIL_SENDING_TRANSACTIONAL_ATTACHMENT))
|
||||||
|
|
||||||
|
request = Net::HTTP::Post.new(uri)
|
||||||
|
|
||||||
|
default_headers.each do |key, value|
|
||||||
|
next if key.to_s == "Content-Type"
|
||||||
|
request[key] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
boundary = "---011000010111000001101001" # any random string not present in the body
|
||||||
|
request.content_type = "multipart/form-data; boundary=#{boundary}"
|
||||||
|
|
||||||
|
body = "--#{boundary}\r\n"
|
||||||
|
|
||||||
|
base64_files(mail.attachments).each do |file|
|
||||||
|
body << "Content-Disposition: form-data; name=\"#{file.field_name}\"; filename=\"#{file.filename}\"\r\n"
|
||||||
|
body << "Content-Type: #{file.mime_type}\r\n"
|
||||||
|
body << "\r\n"
|
||||||
|
body << file.content
|
||||||
|
body << "\r\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
body << "\r\n--#{boundary}\r\n"
|
||||||
|
body << "Content-Disposition: form-data; name=\"TransactionalSending\"\r\n"
|
||||||
|
body << "Content-Type: text/plain; charset=utf-8\r\n"
|
||||||
|
body << "\r\n"
|
||||||
|
body << prepare_mail_body(mail).to_jsv
|
||||||
|
|
||||||
|
body << "\r\n--#{boundary}--\r\n"
|
||||||
|
body << "\r\n"
|
||||||
|
|
||||||
|
request.body = body
|
||||||
|
|
||||||
|
http = Net::HTTP.new(uri.host, uri.port)
|
||||||
|
http.use_ssl = true
|
||||||
|
|
||||||
|
response = http.request(request)
|
||||||
|
|
||||||
|
if response.body.empty?
|
||||||
|
fail "Dolist API returned an empty response"
|
||||||
|
else
|
||||||
|
JSON.parse(response.body)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def sent_mails(email_address)
|
def sent_mails(email_address)
|
||||||
contact_id = fetch_contact_id(email_address)
|
contact_id = fetch_contact_id(email_address)
|
||||||
if contact_id.nil?
|
if contact_id.nil?
|
||||||
|
@ -83,23 +109,25 @@ class Dolist::API
|
||||||
end
|
end
|
||||||
|
|
||||||
def senders
|
def senders
|
||||||
url = format(EMAIL_MESSAGES_ADRESSES_PACKSENDERS, account_id: account_id)
|
get format_url(EMAIL_MESSAGES_ADRESSES_PACKSENDERS)
|
||||||
get(url)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def replies
|
def replies
|
||||||
url = format(EMAIL_MESSAGES_ADRESSES_REPLIES, account_id: account_id)
|
get format_url(EMAIL_MESSAGES_ADRESSES_REPLIES)
|
||||||
get(url)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def format_url(base)
|
||||||
|
format(base, account_id: account_id)
|
||||||
|
end
|
||||||
|
|
||||||
def sender_id
|
def sender_id
|
||||||
senders.dig("ItemList", 0, "Sender", "ID")
|
@sender_id ||= senders.dig("ItemList", 0, "Sender", "ID")
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(url)
|
def get(url)
|
||||||
response = Typhoeus.get(url, headers:).tap do
|
response = Typhoeus.get(url, headers: default_headers).tap do
|
||||||
self.class.save_rate_limit_headers(_1.headers)
|
self.class.save_rate_limit_headers(_1.headers)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -107,14 +135,18 @@ class Dolist::API
|
||||||
end
|
end
|
||||||
|
|
||||||
def post(url, body)
|
def post(url, body)
|
||||||
response = Typhoeus.post(url, body:, headers:).tap do
|
response = Typhoeus.post(url, body:, headers: default_headers).tap do
|
||||||
self.class.save_rate_limit_headers(_1.headers)
|
self.class.save_rate_limit_headers(_1.headers)
|
||||||
end
|
end
|
||||||
|
|
||||||
JSON.parse(response.response_body)
|
if response.response_body.empty?
|
||||||
|
fail "Empty response from Dolist API"
|
||||||
|
else
|
||||||
|
JSON.parse(response.response_body)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def headers
|
def default_headers
|
||||||
{
|
{
|
||||||
"Content-Type": 'application/json',
|
"Content-Type": 'application/json',
|
||||||
"Accept": 'application/json',
|
"Accept": 'application/json',
|
||||||
|
@ -150,6 +182,34 @@ class Dolist::API
|
||||||
post(url, body)["ItemList"]
|
post(url, body)["ItemList"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def prepare_mail_body(mail)
|
||||||
|
{
|
||||||
|
"Type": "TransactionalService",
|
||||||
|
"Contact": {
|
||||||
|
"FieldList": [
|
||||||
|
{
|
||||||
|
"ID": EMAIL_KEY,
|
||||||
|
"Value": mail.to.first
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Message": {
|
||||||
|
"Name": mail['X-Dolist-Message-Name'].value,
|
||||||
|
"Subject": mail.subject,
|
||||||
|
"SenderID": sender_id,
|
||||||
|
"ForceHttp": true,
|
||||||
|
"Format": "html",
|
||||||
|
"DisableOpenTracking": true,
|
||||||
|
"IsTrackingValidated": true
|
||||||
|
},
|
||||||
|
"MessageContent": {
|
||||||
|
"SourceCode": mail_source_code(mail),
|
||||||
|
"EncodingType": "UTF8",
|
||||||
|
"EnableTrackingDetection": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def to_sent_mail(email_address, contact_id, dolist_message)
|
def to_sent_mail(email_address, contact_id, dolist_message)
|
||||||
SentMail.new(
|
SentMail.new(
|
||||||
from: ENV['DOLIST_NO_REPLY_EMAIL'],
|
from: ENV['DOLIST_NO_REPLY_EMAIL'],
|
||||||
|
@ -180,4 +240,16 @@ class Dolist::API
|
||||||
mail.html_part.body.decoded
|
mail.html_part.body.decoded
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def base64_files(attachments)
|
||||||
|
attachments.map do |attachment|
|
||||||
|
raise ArgumentError, "Dolist API does not support non PDF attachments. Given #{attachment.filename} which has mime_type=#{attachment.mime_type}" unless attachment.mime_type == "application/pdf"
|
||||||
|
|
||||||
|
field_name = File.basename(attachment.filename, File.extname(attachment.filename))
|
||||||
|
attachment_content = attachment.body.decoded
|
||||||
|
attachment_base64 = Base64.strict_encode64(attachment_content)
|
||||||
|
|
||||||
|
Dolist::Base64File.new(field_name:, filename: attachment.filename, mime_type: attachment.mime_type, content: attachment_base64)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
3
app/lib/dolist/base64_file.rb
Normal file
3
app/lib/dolist/base64_file.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module Dolist
|
||||||
|
Base64File = Struct.new(:field_name, :filename, :mime_type, :content, keyword_init: true)
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue