-
Notifications
You must be signed in to change notification settings - Fork 76
Improve sample Twilio connector code #137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,7 @@ | ||
|
|
||
| configurable string accountSid = ?; | ||
| configurable string authToken = ?; | ||
| configurable string fromNumber = ?; | ||
| configurable string toNumber = ?; | ||
| configurable string statusCallbackUrl = ?; | ||
| configurable string twimlUrl = ?; | ||
| configurable string callMessage = "Hello! This is a call from WSO2 Integrator."; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,20 @@ | ||
| import ballerinax/trigger.twilio; | ||
| import ballerina/http; | ||
| import ballerina/log; | ||
|
|
||
| configurable int port = 8090; | ||
| configurable int twimlPort = 8091; | ||
|
|
||
| listener twilio:Listener twilioListener = new (port); | ||
|
|
||
| // Serves TwiML so Twilio knows what to say when the call is answered | ||
| // ngrok must forward to localhost:8091 for this endpoint | ||
| service /twiml on new http:Listener(twimlPort) { | ||
| resource function post voice() returns http:Response { | ||
| http:Response twimlResponse = new; | ||
| twimlResponse.setHeader("Content-Type", "application/xml"); | ||
| twimlResponse.setPayload(string `<?xml version="1.0" encoding="UTF-8"?><Response><Say>${callMessage}</Say></Response>`); | ||
| log:printInfo(`TwiML ${callMessage} response sent`); | ||
| return twimlResponse; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,79 +1,87 @@ | ||
| import ballerina/log; | ||
| import ballerinax/trigger.twilio; | ||
| import ballerina/log; | ||
|
|
||
| listener twilio:Listener twilioListener = new (listenOn = listenerPort); | ||
| listener twilio:Listener twilioListener = new (listenerPort); | ||
|
|
||
| service twilio:SmsStatusService on twilioListener { | ||
| remote function onAccepted(twilio:SmsStatusChangeEventWrapper event) returns error|() { | ||
| do { | ||
| } on fail error err { | ||
| // handle error | ||
| return error("unhandled error", err); | ||
| } | ||
| } | ||
|
|
||
| remote function onQueued(twilio:SmsStatusChangeEventWrapper event) returns error|() { | ||
| do { | ||
| } on fail error err { | ||
| // handle error | ||
| return error("unhandled error", err); | ||
| } | ||
| } | ||
|
|
||
| remote function onSending(twilio:SmsStatusChangeEventWrapper event) returns error|() { | ||
| do { | ||
| } on fail error err { | ||
| // handle error | ||
| return error("unhandled error", err); | ||
| } | ||
| } | ||
|
|
||
| remote function onSent(twilio:SmsStatusChangeEventWrapper event) returns error|() { | ||
| do { | ||
| } on fail error err { | ||
| // handle error | ||
| return error("unhandled error", err); | ||
| } | ||
| } | ||
|
|
||
| remote function onFailed(twilio:SmsStatusChangeEventWrapper event) returns error|() { | ||
| do { | ||
| } on fail error err { | ||
| // handle error | ||
| return error("unhandled error", err); | ||
| } | ||
| } | ||
|
|
||
| remote function onDelivered(twilio:SmsStatusChangeEventWrapper event) returns error|() { | ||
| do { | ||
| } on fail error err { | ||
| // handle error | ||
| return error("unhandled error", err); | ||
| } | ||
| } | ||
|
|
||
| remote function onUndelivered(twilio:SmsStatusChangeEventWrapper event) returns error|() { | ||
| do { | ||
| } on fail error err { | ||
| // handle error | ||
| return error("unhandled error", err); | ||
| } | ||
| } | ||
|
|
||
| remote function onReceiving(twilio:SmsStatusChangeEventWrapper event) returns error|() { | ||
| do { | ||
| } on fail error err { | ||
| // handle error | ||
| return error("unhandled error", err); | ||
| } | ||
| } | ||
|
|
||
| remote function onReceived(twilio:SmsStatusChangeEventWrapper event) returns error|() { | ||
| do { | ||
| log:printInfo(event.toJsonString()); | ||
| } on fail error err { | ||
| // handle error | ||
| return error("unhandled error", err); | ||
| } | ||
| // Call status service | ||
| service twilio:CallStatusService on twilioListener { | ||
|
|
||
| remote function onQueued(twilio:CallStatusEventWrapper event) returns error? { | ||
| log:printInfo("Call queued", callSid = event.CallSid ?: ""); | ||
| } | ||
|
|
||
| remote function onRinging(twilio:CallStatusEventWrapper event) returns error? { | ||
| log:printInfo("Call ringing", callSid = event.CallSid ?: ""); | ||
| } | ||
|
|
||
| remote function onInProgress(twilio:CallStatusEventWrapper event) returns error? { | ||
| log:printInfo("Call in progress", callSid = event.CallSid ?: ""); | ||
| } | ||
|
|
||
| remote function onCompleted(twilio:CallStatusEventWrapper event) returns error? { | ||
| log:printInfo("Call completed", | ||
| callSid = event.CallSid ?: "", | ||
| duration = event.CallDuration ?: ""); | ||
| } | ||
|
|
||
| remote function onBusy(twilio:CallStatusEventWrapper event) returns error? { | ||
| log:printInfo("Call busy", callSid = event.CallSid ?: ""); | ||
| } | ||
|
|
||
| remote function onFailed(twilio:CallStatusEventWrapper event) returns error? { | ||
| log:printInfo("Call failed", callSid = event.CallSid ?: ""); | ||
| } | ||
|
|
||
| remote function onNoAnswer(twilio:CallStatusEventWrapper event) returns error? { | ||
| log:printInfo("Call no answer", callSid = event.CallSid ?: ""); | ||
| } | ||
|
|
||
| remote function onCanceled(twilio:CallStatusEventWrapper event) returns error? { | ||
| log:printInfo("Call canceled", callSid = event.CallSid ?: ""); | ||
| } | ||
| } | ||
|
Comment on lines
+7
to
42
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check Twilio trigger connector documentation for event name mappings
rg -n "StatusCallbackEvent|onQueued|onInProgress|initiated|answered" --type=bal -C 3Repository: wso2/integration-samples Length of output: 96 🌐 Web query:
💡 Result: Valid Twilio Voice StatusCallbackEvent values (Programmable Voice / REST or TwiML) are: initiated, ringing, answered, completed. Twilio’s voice webhooks docs explicitly list these events as the supported StatusCallbackEvent values when subscribing to additional status events beyond the default completed callback.[1] How they map to Ballerina Twilio trigger service callback methods (ballerinax/trigger.twilio Call status events): - initiated -> remote function onQueued(...) [2] - ringing -> remote function onRinging(...) [2] - answered -> remote function onInProgress(...) [2] - completed -> remote function onCompleted(...) [2] Note: The Ballerina trigger uses CallStatusEventWrapper callback method names (onQueued/onRinging/onInProgress/onCompleted) rather than the exact Twilio StatusCallbackEvent strings; the mapping above reflects which wrapper callback names the trigger exposes for those same call lifecycle phases.[2][1] Citations:
Remove unused Twilio status callbacks or clarify their purpose. The service defines callbacks for 🤖 Prompt for AI Agents |
||
|
|
||
| // SMS status service | ||
| service twilio:SmsStatusService on twilioListener { | ||
|
|
||
| remote function onAccepted(twilio:SmsStatusChangeEventWrapper event) returns error? { | ||
| log:printInfo("SMS accepted", messageSid = event.MessageSid ?: ""); | ||
| } | ||
|
|
||
| remote function onQueued(twilio:SmsStatusChangeEventWrapper event) returns error? { | ||
| log:printInfo("SMS queued", messageSid = event.MessageSid ?: ""); | ||
| } | ||
|
|
||
| remote function onSending(twilio:SmsStatusChangeEventWrapper event) returns error? { | ||
| log:printInfo("SMS sending", messageSid = event.MessageSid ?: ""); | ||
| } | ||
|
|
||
| remote function onSent(twilio:SmsStatusChangeEventWrapper event) returns error? { | ||
| log:printInfo("SMS sent", messageSid = event.MessageSid ?: ""); | ||
| } | ||
|
|
||
| remote function onFailed(twilio:SmsStatusChangeEventWrapper event) returns error? { | ||
| log:printInfo("SMS failed", messageSid = event.MessageSid ?: ""); | ||
| } | ||
|
|
||
| remote function onDelivered(twilio:SmsStatusChangeEventWrapper event) returns error? { | ||
| log:printInfo("SMS delivered", messageSid = event.MessageSid ?: ""); | ||
| } | ||
|
|
||
| remote function onUndelivered(twilio:SmsStatusChangeEventWrapper event) returns error? { | ||
| log:printInfo("SMS undelivered", messageSid = event.MessageSid ?: ""); | ||
| } | ||
|
|
||
| remote function onReceiving(twilio:SmsStatusChangeEventWrapper event) returns error? { | ||
| log:printInfo("SMS receiving", messageSid = event.MessageSid ?: ""); | ||
| } | ||
|
|
||
| remote function onReceived(twilio:SmsStatusChangeEventWrapper event) returns error? { | ||
| string fromNumber = event.From ?: ""; | ||
| string msgBody = event.Body ?: ""; | ||
| log:printInfo("SMS received", | ||
| messageSid = event.MessageSid ?: "", | ||
| 'from = fromNumber, | ||
| body = msgBody); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
What is the correct TwiML XML format and Content-Type header for Twilio voice call responses with the Say verb?💡 Result:
Correct TwiML XML format (for a Voice response that uses the verb): Hello! This must be valid TwiML: the root element is and all verbs (like ) must be nested inside it.[1] Correct Content-Type header for the HTTP response containing that TwiML: Content-Type: text/xml Twilio’s docs state that when your application responds with XML, Twilio interprets it as TwiML when the response Content-Type is one of text/xml, application/xml, or text/html; text/xml is the most commonly used value.[1] For status-callback endpoints specifically, Twilio says to respond with 200 OK and Content-Type: text/xml (with an empty body, or use 204 No Content).[1] Note: If you’re returning TwiML from your webhook (not just a status callback), you still generally want XML content types; per Twilio, text/xml/application/xml/text/html are treated as the TwiML XML Instruction Set.[1]
Citations:
Use
text/xmlfor the Content-Type header instead ofapplication/xml.Twilio's documentation indicates that while
application/xmlis accepted,text/xmlis the most commonly used and recommended Content-Type for TwiML responses. The XML structure and<Say>verb formatting are correct; however, the header should be changed from"application/xml"to"text/xml"to align with Twilio best practices.Optionally, consider adding language and voice attributes to the
<Say>verb (e.g.,language="en-US"andvoice="man"), though these are not required.🤖 Prompt for AI Agents