mirror of https://github.com/docusealco/docuseal
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
87 lines
2.8 KiB
87 lines
2.8 KiB
# frozen_string_literal: true
|
|
|
|
module Sms
|
|
module Providers
|
|
# Thin wrapper around the SignalWire Compatibility API Messages endpoint.
|
|
# Twilio-shaped on the wire (same Basic Auth, same form-encoded body, same
|
|
# 201-with-error_code JSON), with two differences from Twilio:
|
|
# - path is /api/laml/2010-04-01/Accounts/<id>/Messages (no .json suffix)
|
|
# - host comes from a per-account "Space URL" (e.g. acme.signalwire.com)
|
|
#
|
|
# Docs: https://signalwire.com/docs/compatibility-api/rest/messages/create-message
|
|
class Signalwire
|
|
TIMEOUT_SECONDS = 15
|
|
|
|
def self.configured?(config)
|
|
config['signalwire_space_url'].to_s.present? &&
|
|
config['signalwire_project_id'].to_s.present? &&
|
|
config['signalwire_api_token'].to_s.present? &&
|
|
config['signalwire_from'].to_s.present?
|
|
end
|
|
|
|
def initialize(config)
|
|
@host = normalize_space_url(config['signalwire_space_url'])
|
|
@project_id = config['signalwire_project_id'].to_s.strip
|
|
@token = config['signalwire_api_token'].to_s.strip
|
|
@from = format_e164(config['signalwire_from'])
|
|
end
|
|
|
|
def deliver(to:, text:, webhook: nil)
|
|
form = {
|
|
'From' => @from,
|
|
'To' => format_e164(to),
|
|
'Body' => text.to_s
|
|
}
|
|
form['StatusCallback'] = webhook if webhook.present?
|
|
|
|
response = http_post(form)
|
|
parse_response!(response, form)
|
|
end
|
|
|
|
private
|
|
|
|
def normalize_space_url(raw)
|
|
raw.to_s.strip.sub(%r{\Ahttps?://}, '').sub(%r{/\z}, '')
|
|
end
|
|
|
|
def format_e164(raw)
|
|
"+#{Sms.normalize_phone(raw)}"
|
|
end
|
|
|
|
def http_post(form)
|
|
uri = URI("https://#{@host}/api/laml/2010-04-01/Accounts/#{@project_id}/Messages")
|
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
http.use_ssl = true
|
|
http.read_timeout = TIMEOUT_SECONDS
|
|
http.open_timeout = TIMEOUT_SECONDS
|
|
|
|
request = Net::HTTP::Post.new(uri.request_uri)
|
|
request.basic_auth(@project_id, @token)
|
|
request['Accept'] = 'application/json'
|
|
request.set_form_data(form)
|
|
|
|
http.request(request)
|
|
end
|
|
|
|
def parse_response!(response, form)
|
|
body = begin
|
|
response.body.to_s.empty? ? {} : JSON.parse(response.body)
|
|
rescue JSON::ParserError
|
|
{ 'raw' => response.body.to_s }
|
|
end
|
|
|
|
if response.is_a?(Net::HTTPSuccess) && body['error_code'].nil?
|
|
return body
|
|
end
|
|
|
|
code = body['code'] || body['error_code']
|
|
message = body['message'] || body['error_message'] || body['raw'] || "HTTP #{response.code}"
|
|
detail = code ? "#{code} #{message}" : message
|
|
raise Sms::ProviderError,
|
|
"SignalWire rejected request (HTTP #{response.code}): #{detail}. " \
|
|
"Request: From=#{form['From']} To=#{form['To']}."
|
|
end
|
|
end
|
|
end
|
|
end
|