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.
77 lines
2.5 KiB
77 lines
2.5 KiB
# frozen_string_literal: true
|
|
|
|
module Sms
|
|
module Providers
|
|
# Thin wrapper around the BulkVS messageSend API.
|
|
#
|
|
# Docs: https://portal.bulkvs.com/api/v1.0/documentation
|
|
#
|
|
# Request shape:
|
|
# POST /api/v1.0/messageSend
|
|
# Authorization: Basic <pre-encoded token from the BulkVS portal>
|
|
# Content-Type: application/json
|
|
# { "From": "<e164>", "To": ["<e164>", ...], "Message": "<text>",
|
|
# "delivery_status_webhook_url": "<optional>" }
|
|
#
|
|
# Response shape (success): JSON with at least { "Status": "...", ... }
|
|
# Response shape (error): non-2xx + JSON body with error details
|
|
class Bulkvs
|
|
ENDPOINT = 'https://portal.bulkvs.com/api/v1.0/messageSend'
|
|
TIMEOUT_SECONDS = 15
|
|
|
|
def initialize(config)
|
|
@token = config['basic_auth_token'].to_s.strip
|
|
@from = Sms.normalize_phone(config['from_number'])
|
|
@config_webhook = config['delivery_webhook_url'].to_s.strip
|
|
end
|
|
|
|
def deliver(to:, text:, webhook: nil)
|
|
body = {
|
|
'From' => @from,
|
|
'To' => Array(to).map { |n| Sms.normalize_phone(n) },
|
|
'Message' => text.to_s
|
|
}
|
|
effective_webhook = webhook.presence || @config_webhook.presence
|
|
body['delivery_status_webhook_url'] = effective_webhook if effective_webhook
|
|
|
|
response = http_post(body)
|
|
parse_response!(response, body)
|
|
end
|
|
|
|
private
|
|
|
|
def http_post(body)
|
|
uri = URI(ENDPOINT)
|
|
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['Authorization'] = "Basic #{@token}"
|
|
request['Content-Type'] = 'application/json'
|
|
request['Accept'] = 'application/json'
|
|
request.body = JSON.generate(body)
|
|
|
|
http.request(request)
|
|
end
|
|
|
|
def parse_response!(response, request_body)
|
|
body = begin
|
|
response.body.to_s.empty? ? {} : JSON.parse(response.body)
|
|
rescue JSON::ParserError
|
|
{ 'raw' => response.body.to_s }
|
|
end
|
|
|
|
return body if response.is_a?(Net::HTTPSuccess)
|
|
|
|
message = body['Description'] || body['Status'] || body['error'] ||
|
|
body['raw'] || "HTTP #{response.code}"
|
|
raise Sms::ProviderError,
|
|
"BulkVS rejected request (HTTP #{response.code}): #{message}. " \
|
|
"Request body: #{JSON.generate(request_body.except('Message'))}."
|
|
end
|
|
end
|
|
end
|
|
end
|