Twilio - Play music during long process - coldfusion

Twilio newbie here.
I have a Twilio voice application that collects a bunch of data (international topup sales) - and there is a point where the actual process of purchasing the topup takes place.
This process can last anywhere from 10 to 30 seconds, where most of them are about 15 seconds. Sounds to me like I need to use the Twilio <ENQUEUE> tag (https://www.twilio.com/docs/voice/twiml/enqueue), but it does not work.
I am simply calling it like this (happens to be ColdFusion):
<Enqueue
waitUrl="processtopup.cfm"
method="POST"
action="topupdone.cfm">processTopup</Enqueue>
Within the processtopup.cfm file is the <PLAY> tag (which won't work because that is the page that takes more than 15 seconds.
Sorry - but I'm just confused on ho this should work. Thanks in advance!

Here is a possible solution. I've tested this and it works.
The main idea is to play some message/music in a loop until ColdFusion does the job, then, when ColdFusion is done, instruct the call to execute a different Twilio XML by making a POST request to Twilio's API call resource.
When a call comes in, and Twilio hits your endpoint, capture the call id, it will be used to switch the call to a different XML. The call id it's passed as FORM.CALLSID or URL.CALLSID depending on your webhook configuration at Twilio.
The call id looks something like CAdecbfa7e8e2a9d09336abcd57044cf74.
Pass the call id trough your flow (as url parameter should be fine) so it reaches processtopup.cfm.
Move the long running code from processtopup.cfm to let's say
processtopup-action.cfm
Code in processtopup.cfm should now return immediately XML for playing loop (or you can play some .mp3), I'm showing with a message:
<cfoutput><?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say loop="0">Please wait while we process your request...</Say>
</Response>
</cfoutput>
<cfhttp
url="http://www.yourwebsite.com/processtopup-action.cfm?callsid=#FORM.CALLSID#"
method="get"
timeout="1" />
The code for processtopup-action.cfm
<!--- // place your long running code here --->
<cfset accountSid = '{your account sid}' />
<cfset authToken = '{your auth token}' />
<cfhttp
url="https://api.twilio.com/2010-04-01/Accounts/#variables.accountSid#/Calls/#URL.CALLSID#.json"
method="POST"
username="#variables.accountSid#"
password="#variables.authToken#"
result="http_result">
<cfhttpparam
name="Url"
value="http://www.yourwebsite.com/finish.cfm"
type="formfield" />
</cfhttp>
Code for finish.cfm
<cfoutput><?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>This is the data you're looking for.</Say>
<Say>Thank you. Goodbye!</Say>
<Hangup />
</Response>
</cfoutput>
Of course, you can pass other parameters as you need.
Again, the main idea is that processtopup-action.cfm, after executing your long running code, makes a POST request to Twilio's API and instructs the call to switch to execute new TwiML located at http://www.yourwebsite.com/finish.cfm
Docs:
Call Redirection via the Twilio REST API
(https://www.twilio.com/blog/2009/09/call-queueing-putting-callers-on-hold-calll-redirect-new-url-new-feature.html)
Modifying Live Calls
(https://www.twilio.com/docs/voice/modify-live-calls)

Related

Enctype issue when I try to upload a file with a Coldfusion webservice

Can't get to work a Coldfusion webservice that uploads a file in an external server.
My application runs in "Server A" and the file repository (external server) is in "Server B"
The template (inicio.cfm) which contains the form with the <cfinput type="file"> to select the client's file to be uploaded, is stored in "Server A". This template performs more tasks than just show the upload form. It performs calculations, queries, etc. outside the form's code block. Also, the action page of this form is the template itself (because of my application's needed tasks).
The first code line of my form definition is (inside inicio.cfm):
<cfform method="post" name="AltaConvocatoria" enctype="multipart/form-data">
Which demonstrate that I'm using the right enctype definition.
In the other hand, stored in "Server B" is my Coldfusion component or webservice (alta_ga.cfc) which only task is to upload the file selected by user in "inicio.cfm" form and rename it.
Here's alta_ga.cfc code:
<cfcomponent>
<cffunction access="remote" returntype="void" name="cargaAnuncio">
<cfargument name="destinoAnuncio" required="yes" type="string">
<cfargument name="PrefijoNvoNombre" required="yes" type="string">
<cffile action="upload"
fileField="str_ArchivoAnuncio"
destination="#destinoAnuncio#"
nameconflict="Overwrite">
<cfset NvoNomAnuncio = #PrefijoNvoNombre# & #Right(cffile.ClientFile, 5)#>
<cfset viejoNombre1 = #destinoAnuncio# & #cffile.ClientFile#>
<cffile
action = "rename"
destination = "#NvoNomAnuncio#"
source = "#viejoNombre1#">
</cffunction>
</cfcomponent>
For that pupose, I invoke the webservice from the form's action code block in inicio.cfm with this:
<cfinvoke webservice="http://192.168.208.128/podi/mgmt/alta_ga.cfc?wsdl" method="cargaAnuncio" >
<cfinvokeargument name="destinoAnuncio" value="#form.destinoAnuncio#" />
<cfinvokeargument name="PrefijoNvoNombre" value="#form.PrefijoNvoNombre#" />
</cfinvoke>
When I try to load a file using my form's template inicio.cfm I get this message:
Cannot perform web service invocation cargaAnuncio.
The fault returned when invoking the web service operation is:
'' podi.mgmt.PodiMgmtAlta_gaCfcCFCInvocationExceptionException: coldfusion.tagext.io.FileUtils$CFFileNonMultipartException : Invalid content type: application/soap+xml; charset=UTF-8; action="urn:cargaAnuncio".The files upload action requires forms to use enctype="multipart/form-data".]
All the arguments and variables that I'm using are correct, because I tested the webservice as a local component (stored in Server A and uploading the file in the same server) and worked fine. Here's the code of the succesful test (invoked as a component instead of a webservice):
<cfinvoke component="alta_ga" method="cargaAnuncio" destinoAnuncio="#form.destinoAnuncio#" PrefijoNvoNombre="#form.PrefijoNvoNombre#">
¿What could be wrong?
There's a lack of documentation about this functionality. Adobe's user guide doesn't explain this functionality in depht. Ben Forta's books... same. Or I couldn't find the information.
Thanks in advance.
When a form is posted to a CFML server, the posted file is saved in a temporary directory before any of your code runs. All <cffile action="upload"> does is to copy a file from that temporary directory to the location you want it to be. Your remote server ServerB has no idea about any file posted on ServerA, so <cffile action="upload"> will not help you.
The action is misleading. It's not upload-ing anything. It's just copying from a predetermined temp directory. The web server handles the uploading before the CF server is even involved.
You will likely need to <cffile action="upload"> on ServerA to a specific place, and then it needs to post that file to your web service on ServerB. Then ServerB should be able to use <cffile action="upload"> to transfer it from the upload temp directory to wherever you need it to be. That said I have never tried this when posting to a web service.
Alternatively you could just post the file directly to ServerB in the first place, to save needing ServerA to be an intermediary. This might not be possible, of course.

Twilio + Django - Voice message

Here's my requirement:
I have a text ="Thank you for your response". I want to initiate a call with a user, and when the user picks up the call, i want the text message to be turned into a voice message.
Here's what i have so far:
I am initiating a twilio call:
twilioClient.calls.create(to=user_number, from_=twilio_number, \
url="https://testserver.com/call/")
In urls.py, i have /call/ that points to my django view module
In view, the module looks like:
def testTwilioCall(request):
return HttpResponse(open('/path/call.xml').read(), content_type='application/xml')
-call.xml looks like:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say voice="woman">Thank you for your response.</Say>
<Record maxLength="20" />
</Response>
Here's whats happening:
User gets a call, and the call says - "Sorry application error".
I am not sure what is going wrong. When i replace the url parameter with https://demo.twilio.com/welcome/voice/, it works.
I also double checked that the xml is being displayed fine from the browser.
Not sure where i am going wrong with rendering the xml.
PS: testserver is just a placeholder.

DocuSign API with ColdFusion

UPDATED WITH WORKING SOLUTION
I'm trying to integrate the DocuSign API using ColdFusion and keep running into an issue. I am able to authenticate successfully and retrieve the base URL. However, when trying to create the envelope with a single recipient and document, I get an error stating that the "boundary terminator" is not found in the request (when it clearly is).
I know this questions is similar to another post here but that was from a while ago and was never completely answered.
I first read in the PDF document:
<cffile action="READBINARY" file="Agreement.pdf" variable="docData">
<cfset docData = BinaryEncode(docData,"Base64")>
I then create the envelope definition in XML using the element to include the encoded binary PDF data:
<cfset envelope = "<envelopeDefinition xmlns=""http://www.docusign.com/restapi"">
<status>Sent</status>
<emailSubject>eSignature request</emailSubject>
<emailBlurb>Please sign the document</emailBlurb>
<recipients>
<signers>
<signer>
<recipientId>1</recipientId>
<name>eCS Buyer</name>
<email>XXX#XXXX.com</email>
<tabs>
<signHereTabs>
<signHere>
<documentId>1</documentId>
<pageNumber>3</pageNumber>
<xPosition>10</xPosition>
<yPosition>100</yPosition>
</signHere>
</signHereTabs>
</tabs>
</signer>
</signers>
</recipients>
<documents>
<document>
<name>Agreement.pdf</name>
<documentId>1</documentId>
<fileExtension>pdf</fileExtension>
<documentBase64>#docData#</documentBase64>
</document>
</documents>
</envelopeDefinition>">
Lastly, I POST the information:
<cfhttp url="#baseURL#/envelopes" method="POST" resolveurl="Yes" throwonerror="No">
<cfhttpparam name="X-DocuSign-Authentication" type="HEADER" value="<DocuSignCredentials><Username>#userName#</Username><Password>#password#</Password><IntegratorKey>#integratorKey#</IntegratorKey></DocuSignCredentials>">
<cfhttpparam name="Content-Type" type="HEADER" value="application/xml">
<cfhttpparam name="Accept" type="HEADER" value="application/xml">
<cfhttpparam name="Content-Length" type="HEADER" value="#Len(envelope)#">
<cfhttpparam name="request_body" type="BODY" value="#envelope#">
</cfhttp>
I tried changing the TYPE attribute for the request body to XML and FORMFIELD but is still doesn't work. I even tried changing the envelope to JSON format to no avail.
The error that is generated is:
<errorCode>INVALID_REQUEST_BODY</errorCode><message>The request body is missing or improperly formatted. The XML request does not match the expected format. </message></errorDetails>
I have been struggling with this for weeks. Any guidance would be GREATLY appreciated.
Most likely an issue with extra CRLF (i.e., extra line breaks). See the answer in this other forum post for an example of what the full request structure should look like: Docusign : Unable to create envelope from document in restapi v2.
UPDATE #1
Looks like your XML request body is missing information about the Document. Try adding a documents element as a child element of envelopeDefinition (i.e., as a peer element to recipients):
<envelopeDefinition xmlns=""http://www.docusign.com/restapi"">
...
<documents>
<document>
<name>Agreement.pdf</name>
<documentId>1</documentId>
</document>
</documents>
...
</envelopeDefinition>
Also, make sure the document bytes that your sending in the subsequent part of the request are not encoded.
UPDATE #2
I don't know much about ColdFusion, but this line of your code makes it look like you're (base64) encoding the byte stream that you're including in the Request:
<cfset docData = BinaryEncode(docData,"Base64")>
This could be causing your latest issue, as I don't believe DocuSign will accept an encoded byte stream when it's included in the manner you're currently utilizing.
If you must base64-encode the byte stream, you could add a documentBase64 property under the document element to contain the base64-encoded byte stream. (See page 104 of the DocuSign REST API Guide -- http://www.docusign.com/sites/default/files/REST_API_Guide_v2.pdf.) If you utilized this approach, your request would no longer need to be a 'multipart' request, since the document bytes would be included in the XML portion of the Request Body (not in a subsequent/separate part). Here's an example of what the Request would look like, if the base64-encoded byte stream was included within the documentBase64 element:
POST /restapi/v2/accounts/ACCOUNT_NUMBER/envelopes HTTP/1.1
Host: demo.docusign.net
X-DocuSign-Authentication: {"Username":"SENDER_EMAIL_ADDRESS","Password":"PASSWORD","IntegratorKey":"INT_KEY"}
Content-Type: application/xml
<envelopeDefinition xmlns="http://www.docusign.com/restapi">
<accountId>ACCOUNT_ID</accountId>
<status>sent</status>
<emailSubject>eSignature request</emailSubject>
<emailBlurb>Please sign the document</emailBlurb>
<recipients>
<signers>
<signer>
<email>johnsEmail#outlook.com</email>
<name>John Doe</name>
<recipientId>1</recipientId>
<routingOrder>1</routingOrder>
<tabs>
<signHereTabs>
<signHere>
<documentId>1</documentId>
<pageNumber>1</pageNumber>
<xPosition>10</xPosition>
<yPosition>100</yPosition>
</signHere>
</signHereTabs>
</tabs>
</signer>
</signers>
</recipients>
<documents>
<document>
<name>Agreement.pdf</name>
<documentId>1</documentId>
<fileExtension>pdf</fileExtension>
<documentBase64>BASE64-ENCODED-BYTE-STREAM</documentBase64>
</document>
</documents>
</envelopeDefinition>
Since the document bytes are included within the XML portion of the request, the request no longer needs to be multi-part -- simply pass the XML Request body as I've shown above, and that's it.
The error is most likely due to the extra CRLF you have after the document bytes and before your closing boundary --MYBOUNDARY--. You have this:
#docData#
--MYBOUNDARY--
Try changing that to this without that extra CRLF:
#docData#
--MYBOUNDARY--
For reference you can check out the DocuSign API Walkthrough for sending a signature request on a document. Each of the different language versions shows you how the outgoing request body should be formatted.

Docusign and ColdFusion integration

I am using the following code to call the docusign api. I got the information from this link.
<soap:Body>
<ns:CreateEnvelopeFromTemplates>
<ns:TemplateReferences>
<ns:TemplateReference>
<ns:TemplateLocation>Server</ns:TemplateLocation>
<ns:Template>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</ns:Template>
<ns:RoleAssignments>
<ns:RoleAssignment>
<ns:RoleName>Company</ns:RoleName>
<ns:RecipientID>1</ns:RecipientID>
</ns:RoleAssignment>
</ns:RoleAssignments>
</ns:TemplateReference>
</ns:TemplateReferences>
<ns:Recipients>
<ns:Recipient>
<ns:ID>1</ns:ID>
<ns:UserName>Fred Flintstone</ns:UserName>
<ns:Email>fred.flintstone#...</ns:Email>
<ns:Type>Signer</ns:Type>
<ns:RoleName>Caveman</ns:RoleName>
<ns:RoutingOrder>1</ns:RoutingOrder>
</ns:Recipient>
</ns:Recipients>
<ns:EnvelopeInformation>
<ns:AccountId>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</ns:AccountId>
<ns:EmailBlurb>This Envelope was sent through the DocuSign API using ColdFusion</ns:EmailBlurb>
<ns:Subject>DocuSign it! using ColdFusion</ns:Subject>
</ns:EnvelopeInformation>
<ns:ActivateEnvelope>true</ns:ActivateEnvelope>
</ns:CreateEnvelopeFromTemplates>
</soap:Body>
I am unable to understand what the RoleAssignment tag under Template is used for. I have gone through the documentation here but did not understand it.
I think this is the only reason I am not getting a response. I commented this portion in my code but I am getting the following output after changing all of the credentials.
An error occurred!
struct
Charset [empty string]
ErrorDetail Unknown host: demo.docusign.net
Filecontent Connection Failure
Header [empty string]
Mimetype Unable to determine MIME type of file.
Responseheader
struct [empty]
Statuscode Connection Failure. Status code unavailable.
Text YES
Can anyone please help me out with this?
I don't think the RoleAssignment has anything to do with that particular error. You need to combine the SOAP Header and the SOAP Body from their example. The SOAP Body should be contained with the SOAP Header block.
Notice that their example for the SOAP Header contains the <soap:Body> block. Then their example for the SOAP Body is also surrounded by the <soap:Body> block. They should be combined like this for your <cfhttp> call:
<cfset UserName = "[XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX]XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX" />
<cfset Password = "SuperSecret" />
<cfscript>
strNonce = ToBase64(createUUID());
</cfscript>
<!--- create the request body XML block --->
<cfsavecontent variable="request_xml">
<cfoutput>
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:ns="http://www.docusign.net/API/3.0">
<soap:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-1" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>#UserName#</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0##PasswordText">#Password#</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0##Base64Binary">#strNonce#</wsse:Nonce>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<ns:CreateEnvelopeFromTemplates>
<ns:TemplateReferences>
<ns:TemplateReference>
<ns:TemplateLocation>Server</ns:TemplateLocation>
<ns:Template>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</ns:Template>
<ns:RoleAssignments>
<ns:RoleAssignment>
<ns:RoleName>Company</ns:RoleName>
<ns:RecipientID>1</ns:RecipientID>
</ns:RoleAssignment>
</ns:RoleAssignments>
</ns:TemplateReference>
</ns:TemplateReferences>
<ns:Recipients>
<ns:Recipient>
<ns:ID>1</ns:ID>
<ns:UserName>Fred Flintstone</ns:UserName>
<ns:Email>fred.flintstone#...</ns:Email>
<ns:Type>Signer</ns:Type>
<ns:RoleName>Caveman</ns:RoleName>
<ns:RoutingOrder>1</ns:RoutingOrder>
</ns:Recipient>
</ns:Recipients>
<ns:EnvelopeInformation>
<ns:AccountId>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</ns:AccountId>
<ns:EmailBlurb>This Envelope was sent through the DocuSign API using ColdFusion</ns:EmailBlurb>
<ns:Subject>DocuSign it! using ColdFusion</ns:Subject>
</ns:EnvelopeInformation>
<ns:ActivateEnvelope>true</ns:ActivateEnvelope>
</ns:CreateEnvelopeFromTemplates>
</soap:Body>
</soap:Envelope>
</cfoutput>
</cfsavecontent>
(This example was taken directly from their documentation. I only combined the two blocks.)
Then send that request (from their example):
<!--- send the request to the DocuSign service --->
<cfhttp url="https://demo.docusign.net/api/3.0/api.asmx" method="post" result="http_response">
<!--- make sure you set this correctly for the call you are making --->
<cfhttpparam type="header" name="SOAPAction" value="http://www.docusign.net/API/3.0/CreateEnvelopeFromTemplates" />
<cfhttpparam type="header" name="accept-encoding" value="no-compression" />
<cfhttpparam type="xml" value="#trim(request_xml)#" />
</cfhttp>
And finally handle the response (from their example):
<!--- if we received a successful response --->
<cfif find("200", http_response.statusCode)>
<cfscript>
response_xml = xmlParse(http_response.fileContent);
// use XPath to get the top level element you want from the SOAP response
result_node = xmlSearch(response_xml, "//*[local-name() = 'CreateEnvelopeFromTemplatesResult']")[1];
// use dot notation to navigate the XML structure
envelope_id = result_node.EnvelopeID.xmlText;
</cfscript>
<cfoutput>
EnvelopeID: #envelope_id#
</cfoutput>
<cfelse>
<cfoutput>
An error occurred!
</cfoutput>
<cfdump var="#http_response#">
</cfif>
If this is a POST request (submitting data), it looks as though the webservice is not recognising the submission of the actual SOAP XML file.
Are you sending the XML as an XML type as shown in the documentation?
<cfhttpparam type="xml" value="#trim(request_xml)#" />
The RoleAssignment tags are used for assigning your recipient to a particular template role. When you send an envelope that uses a template, you are referencing a template that you have created through the DocuSign console (demo.docusign.net). When you created the template, you had to specify at least one template role for the template, which contains a role name, recipient name, and recipient email. The value you use for the role here needs to match the role name you provide in the api call. So if you called your role "Signer1" for instance, you need to have
<ns:RoleName>Signer1</ns:RoleName>
If you named the role "Company" the keep like you have it.

CFhttp to Scrape Image

A contractor has provided us with survey data for a set of stores. The data contains the store numbers, thumbnail images and large images. The data is accessed through the contractor's secured website. In order to build a report for the data, I am trying to scrape the store numbers and images from the site instead of manually downloading each image.
I have not used CFhttp for secured sites, but have had a little success so far with:
<cfhttp
method="post"
url="http://www.website.com/impart/client_login.php"
throwonerror="Yes"
redirect = "yes"
resolveUrl = "yes">
<cfhttpparam name="user" value="myUsername" type="formfield">
<cfhttpparam name="pass" value="myPassword" type="formfield">
<cfhttpparam name="submit" value="Login" type="formfield">
How do I proceed from getting passed the authentication to the page that contains the image to download?
I think that CFHTTP may not be the best choice for this. I am good at BASH, so I would tend towards scripting it with curl, but maybe some product on this page would be easier http://www.timedicer.co.uk/web-scraping ?
What does the dump of cfhttp scope look like? Specifically, what is the status code?
If you get a status code of 200, you'll need to maintain the session as you grab each image. See the following:
http://www.bennadel.com/blog/725-Maintaining-Sessions-Across-Multiple-ColdFusion-CFHttp-Requests.htm
http://www.bennadel.com/projects/cfhttp-session.htm
See this question for saving images via CFHTTP:
Convert an image from CFHTTP filecontent to binary data with Coldfusion