mirror of
https://github.com/Fishwaldo/huginn.git
synced 2025-03-15 19:31:26 +00:00
An agent for creating events from text messages received from Twilio
don't need securerandom don't assume ENV['DOMAIN'] exists update TwilioReceiveTextAgent to use new receive_web_request method signature
This commit is contained in:
parent
4140fa62f9
commit
29d4691781
2 changed files with 199 additions and 0 deletions
100
app/models/agents/twilio_receive_text_agent.rb
Normal file
100
app/models/agents/twilio_receive_text_agent.rb
Normal file
|
@ -0,0 +1,100 @@
|
|||
module Agents
|
||||
class TwilioReceiveTextAgent < Agent
|
||||
cannot_be_scheduled!
|
||||
cannot_receive_events!
|
||||
|
||||
gem_dependency_check { defined?(Twilio) }
|
||||
|
||||
description do <<-MD
|
||||
The Twilio Receive Text Agent receives text messages from Twilio and emits them as events.
|
||||
|
||||
#{'## Include `twilio-ruby` in your Gemfile to use this Agent!' if dependencies_missing?}
|
||||
|
||||
In order to create events with this agent, configure Twilio to send POST requests to:
|
||||
|
||||
```
|
||||
#{post_url}
|
||||
```
|
||||
|
||||
#{'The placeholder symbols above will be replaced by their values once the agent is saved.' unless id}
|
||||
|
||||
Options:
|
||||
|
||||
* `server_url` must be set to the URL of your
|
||||
Huginn installation (probably "https://#{ENV['DOMAIN']}"), which must be web-accessible. Be sure to set http/https correctly.
|
||||
|
||||
* `account_sid` and `auth_token` are your Twilio account credentials. `auth_token` must be the primary auth token for your Twilio accout.
|
||||
|
||||
* If `reply_text` is set, it's contents will be sent back as a confirmation text.
|
||||
|
||||
* `expected_receive_period_in_days` - How often you expect to receive events this way. Used to determine if the agent is working.
|
||||
MD
|
||||
end
|
||||
|
||||
def default_options
|
||||
{
|
||||
'account_sid' => 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
||||
'auth_token' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
||||
'server_url' => "https://#{ENV['DOMAIN'].presence || example.com}",
|
||||
'reply_text' => '',
|
||||
"expected_receive_period_in_days" => 1
|
||||
}
|
||||
end
|
||||
|
||||
def validate_options
|
||||
unless options['account_sid'].present? && options['auth_token'].present? && options['server_url'].present? && options['expected_receive_period_in_days'].present?
|
||||
errors.add(:base, 'account_sid, auth_token, server_url, and expected_receive_period_in_days are all required')
|
||||
end
|
||||
end
|
||||
|
||||
def working?
|
||||
event_created_within?(interpolated['expected_receive_period_in_days']) && !recent_error_logs?
|
||||
end
|
||||
|
||||
def post_url
|
||||
if interpolated['server_url'].present?
|
||||
"#{interpolated['server_url']}/users/#{user.id}/web_requests/#{id || ':id'}/sms-endpoint"
|
||||
else
|
||||
"https://#{ENV['DOMAIN']}/users/#{user.id}/web_requests/#{id || ':id'}/sms-endpoint"
|
||||
end
|
||||
end
|
||||
|
||||
def receive_web_request(request)
|
||||
params = request.params.except(:action, :controller, :agent_id, :user_id, :format)
|
||||
method = request.method_symbol.to_s
|
||||
headers = request.headers
|
||||
|
||||
# check the last url param: 'secret'
|
||||
secret = params.delete('secret')
|
||||
return ["Not Authorized", 401] unless secret == "sms-endpoint"
|
||||
|
||||
signature = headers['HTTP_X_TWILIO_SIGNATURE']
|
||||
|
||||
# validate from twilio
|
||||
@validator ||= Twilio::Util::RequestValidator.new interpolated['auth_token']
|
||||
if !@validator.validate(post_url, params, signature)
|
||||
error("Twilio Signature Failed to Validate\n\n"+
|
||||
"URL: #{post_url}\n\n"+
|
||||
"POST params: #{params.inspect}\n\n"+
|
||||
"Signature: #{signature}"
|
||||
)
|
||||
return ["Not authorized", 401]
|
||||
end
|
||||
|
||||
if create_event(payload: params)
|
||||
response = Twilio::TwiML::Response.new do |r|
|
||||
if interpolated['reply_text'].present?
|
||||
r.Message interpolated['reply_text']
|
||||
end
|
||||
end
|
||||
return [response.text, 201, "text/xml"]
|
||||
else
|
||||
return ["Bad request", 400]
|
||||
end
|
||||
end
|
||||
|
||||
# def client
|
||||
# @client ||= Twilio::REST::Client.new interpolated['account_sid'], interpolated['auth_token']
|
||||
# end
|
||||
end
|
||||
end
|
99
spec/models/agents/twilio_receive_text_agent_spec.rb
Normal file
99
spec/models/agents/twilio_receive_text_agent_spec.rb
Normal file
|
@ -0,0 +1,99 @@
|
|||
require 'rails_helper'
|
||||
|
||||
# Twilio Params
|
||||
# https://www.twilio.com/docs/api/twiml/sms/twilio_request
|
||||
# url: https://b924379f.ngrok.io/users/1/web_requests/7/sms-endpoint
|
||||
# params: {"ToCountry"=>"US", "ToState"=>"NY", "SmsMessageSid"=>"SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "NumMedia"=>"0", "ToCity"=>"NEW YORK", "FromZip"=>"48342", "SmsSid"=>"SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "FromState"=>"MI", "SmsStatus"=>"received", "FromCity"=>"PONTIAC", "Body"=>"Lol", "FromCountry"=>"US", "To"=>"+1347555555", "ToZip"=>"10016", "NumSegments"=>"1", "MessageSid"=>"SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "AccountSid"=>"ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "From"=>"+12485551111", "ApiVersion"=>"2010-04-01"}
|
||||
# signature: K29NMD9+v5/QLzbdGZW/DRGyxNU=
|
||||
|
||||
describe Agents::TwilioReceiveTextAgent do
|
||||
before do
|
||||
stub.any_instance_of(Twilio::Util::RequestValidator).validate { true }
|
||||
end
|
||||
|
||||
let(:payload) {
|
||||
{
|
||||
"ToCountry"=>"US",
|
||||
"ToState"=>"NY",
|
||||
"SmsMessageSid"=>"SMxxxxxxxxxxxxxxxx",
|
||||
"NumMedia"=>"0",
|
||||
"ToCity"=>"NEW YORK",
|
||||
"FromZip"=>"48342",
|
||||
"SmsSid"=>"SMxxxxxxxxxxxxxxxx",
|
||||
"FromState"=>"MI",
|
||||
"SmsStatus"=>"received",
|
||||
"FromCity"=>"PONTIAC",
|
||||
"Body"=>"Hy ",
|
||||
"FromCountry"=>"US",
|
||||
"To"=>"+1347555555",
|
||||
"ToZip"=>"10016",
|
||||
"NumSegments"=>"1",
|
||||
"MessageSid"=>"SMxxxxxxxxxxxxxxxx",
|
||||
"AccountSid"=>"ACxxxxxxxxxxxxxxxx",
|
||||
"From"=>"+12485551111",
|
||||
"ApiVersion"=>"2010-04-01"}
|
||||
}
|
||||
|
||||
describe 'receive_twilio_text_message' do
|
||||
before do
|
||||
@agent = Agents::TwilioReceiveTextAgent.new(
|
||||
:name => 'twilioreceive',
|
||||
:options => { :account_sid => 'x',
|
||||
:auth_token => 'x',
|
||||
:server_url => 'http://example.com',
|
||||
:expected_receive_period_in_days => 1
|
||||
}
|
||||
)
|
||||
@agent.user = users(:bob)
|
||||
@agent.save!
|
||||
end
|
||||
|
||||
it 'should create event upon receiving request' do
|
||||
|
||||
request = ActionDispatch::Request.new({
|
||||
'action_dispatch.request.request_parameters' => payload.merge({"secret" => "sms-endpoint"}),
|
||||
'REQUEST_METHOD' => "POST",
|
||||
'HTTP_ACCEPT' => 'application/xml',
|
||||
'HTTP_X_TWILIO_SIGNATURE' => "HpS7PBa1Agvt4OtO+wZp75IuQa0="
|
||||
})
|
||||
|
||||
out = nil
|
||||
expect {
|
||||
out = @agent.receive_web_request(request)
|
||||
}.to change { Event.count }.by(1)
|
||||
expect(out).to eq(["<?xml version=\"1.0\" encoding=\"UTF-8\"?><Response></Response>", 201, "text/xml"])
|
||||
expect(Event.last.payload).to eq(payload)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'receive_twilio_text_message and send a response' do
|
||||
before do
|
||||
@agent = Agents::TwilioReceiveTextAgent.new(
|
||||
:name => 'twilioreceive',
|
||||
:options => { :account_sid => 'x',
|
||||
:auth_token => 'x',
|
||||
:server_url => 'http://example.com',
|
||||
:reply_text => "thanks!",
|
||||
:expected_receive_period_in_days => 1
|
||||
}
|
||||
)
|
||||
@agent.user = users(:bob)
|
||||
@agent.save!
|
||||
end
|
||||
|
||||
it 'should create event and send back TwiML Message if reply_text is set' do
|
||||
out = nil
|
||||
request = ActionDispatch::Request.new({
|
||||
'action_dispatch.request.request_parameters' => payload.merge({"secret" => "sms-endpoint"}),
|
||||
'REQUEST_METHOD' => "POST",
|
||||
'HTTP_ACCEPT' => 'application/xml',
|
||||
'HTTP_X_TWILIO_SIGNATURE' => "HpS7PBa1Agvt4OtO+wZp75IuQa0="
|
||||
})
|
||||
expect {
|
||||
out = @agent.receive_web_request(request)
|
||||
}.to change { Event.count }.by(1)
|
||||
expect(out).to eq(["<?xml version=\"1.0\" encoding=\"UTF-8\"?><Response><Message>thanks!</Message></Response>", 201, "text/xml"])
|
||||
expect(Event.last.payload).to eq(payload)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue