I´m developing a chatbot using Twilio Studio and at some point, I need to collect the data inputs from the clients that talk to my bot, and then post it into an airtable base.
I don´t have much coding skills, so my guide was this Dabble Lab tutorial: https://www.youtube.com/watch?v=xjt9YhNFrno
However, the Twilio function proposed in the video isn´t working. Here is the code:
exports.handler = function(context, event, callback) {
let member = {
name : event.name,
email : event.email,
date : Date.now()
};
var Airtable = require('airtable');
var base = new Airtable({apiKey: context.AIRTABLE_API_KEY}).base('appISrkMnNdL65Lzj');
base('Members').create(member, function(err, record) {
if (err) { console.error(err); return; }
console.log(record.getId());
callback(null, member);
});
};
When I try to make a POST request via Postman, this is what happens in my Twilio Console
And this is the capture of Postman response
As I´m using Twilio Studio Flow for developing the chatbot, I guess I could use the HTTP Request Widget but I really don´t know how to configure it.
The columns in my base are: Id - name - email - date
Any idea how can I solve this?
There are a couple of good Twilio/Airtable blogs recently posted. It looks like you are getting an error back but your error condition isn't calling the callback and thus the timeout.
Could it be some issue is with the Airtable field types not liking the type of the data you are posting, in particular the date.
Maybe you need something like this instead?
const today = new Date();
const date = `${(today.getMonth()+1)}/${today.getDate()}/${today.getFullYear()}`;
Writing to Airtable from your Twilio app
Using Airtable as a database for your Twilio app
Related
Good day everyone,
For context purpose, I am trying to monitor a Youtube Channel where, whenever they post a new video, I will get a notification and process the entry.
What I have done:-
a) setup the callback url to receive and reply the hub challenge.
b) https://pubsubhubbub.appspot.com/subscribe
Subscribe to this site as suggested by Youtube where the topic URL is
https://www.youtube.com/feeds/videos.xml?channel_id=UClrEYreVkBee7ZQYei_6Jqg (atom-feed)
and the callback URL is to a Google Cloud functions url.
c) Reply and verified the hub challenge sent by (b).
The questions I have are:
I havent receive any response from pubsubhubbub since the subscription but the Youtube channel already has updated videos since then. Have I missed some steps here or is this not the expectation to have?
I expect to only receive a notification and not the XML feed body from pubsubhubbub and upon notification, I should then process and fetch the XML feed from the channel.
OR
I expect to receive the XML feed body / latest entry from pubsubhubbub.
The reason I am asking this is I see there is a latest entry and not the full feed in the pubsubhubbub site.
Thanks in advance if there are anybody who could help answer these questions.
import express from 'express';
import dotenv from "dotenv";
dotenv.config();
const YouTubeNotifier = require('youtube-notification');
const app = express();
const port = 6050;
const baseUrl = "https://evil-wasp-10.loca.lt"
let channelId = process.env.CHANNEL_ID;
export const notifier = new YouTubeNotifier({
hubCallback: `${baseUrl}/youtube/notifications`,
});
app.use("/youtube/notifications", notifier.listener());
app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`)
})
notifier.subscribe(channelId);
notifier.on('subscribe', (data: any) => {
console.log('Subscribed');
console.log(data);
});
notifier.on('notified', (data: any) => {
console.log('New Video');
console.log(data);
});
We have a studio flow called "Google LA" that's triggered via Rest API. This flow has a Send and Wait for Reply so we hook this flow to "When a message comes in" so it will follow the rest of the flow when customer rates the service 1 to 5 stars. Now, within the Send and Wait for Reply, we want the customer's reply be forwarded to our main business phone number for tracking/recording purposes and so we can address their issues for rating us 1 to 3 stars. Here's our setup:
This is what we want:
Edited for philnash suggestion:
I created a function in Twilio with this code:
exports.handler = function(context, event, callback) {
const accountSid = context.ACCOUNT_SID;
const authToken = context.AUTH_TOKEN;
const client = require('twilio')(accountSid, authToken);
client.messages
.create({
body: widgets.negative1_3.inbound.Body,
from: '+12132779513',
to: '+12133885256'
})
.then(message => console.log(message.sid));
};
However, it did not send anything or the customer response. I renamed the negative1-3 widget to negative1_3 and published the studio flow.
I tried changing the body: 'Hello' to make sure that my function works, and yes. I received the 'Hello' sms to my verified caller ID phone number after it reaches the first_question -> check_response -> negative1_3.
Twilio developer evangelist here.
You don't necessarily need to forward the message here. You can make an API call to your own service with all the data you need from the message, so you can store and react to the information that way.
To do so you will want to add either an HTTP Request widget or a Run Function widget after the Send and Wait For Reply widget. Within those widgets, you can access the reply from the Send And Wait For Reply widget using liquid tags. You can see how to call on the variables in the docs for the Send and Wait For Reply widget. In the case of your widget, you should be able to get the body of the reply by referring to:
widgets.negative1-3.inbound.Body
(Although I am not sure how the name "negative1-3" will work, so you might try widgets["negative1-3"] instead, or rename the widget with underscores.)
Using the body of the inbound message, as well as the from number, you can send the data to your own application with the HTTP request widget or with a Run Function widget.
Edit
Your function can only access parameters that you set in the function widget config. You can then access those parameters in the event object. You also need to return once the message is sent successfully using the callback function. One other tip, you don't need to instantiate your own client, you can get it from the context. Like so:
exports.handler = function(context, event, callback) {
const client = context.getTwilioClient();
client.messages
.create({
body: event.Body,
from: '+12132779513',
to: '+12133885256'
})
.then(message => {
console.log(message.sid);
callback(null, "OK");
})
.catch(error => callback(error));
};
My company uses Twilio Flex as our phone system and I was recently tasked with setting up a feature that will let us edit a TwiML voice message that plays before our normal voice message. This TwiML message will be changed through a Twilio bot that I've published in our Microsoft Teams.
The reason for this is so that our support desk can add a short message in the lines of "We're currently experiencing issues with X" before our normal "Welcome to [Company] support" message.
If TwiML's can be edited using HTTP POST/PUT or Twilio's API this should be a trivial matter, but so far I've not been able to figure out how.
I couldn't find any reference to this in the API doc, so I decided that HTTP POST would be the way to go. Using this as a start off point, I'm able to retrieve my TwiML using HTTP GET:
https://support.twilio.com/hc/en-us/articles/223132187--Not-Authorized-error-when-trying-to-view-TwiML-Bin-URL
const axios = require('axios');
const crypto = require('crypto');
const accountSidFlex = process.env.accountSidFlex;
const authTokenFlex = process.env.authTokenFlex;
var URL = 'https://handler.twilio.com/twiml/EHXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' + '?AccountSid=' + accountSidFlex
var twilioSig = crypto.createHmac('sha1', authTokenFlex).update(new Buffer(URL, 'utf-8')).digest('Base64')
var config = {
headers:{
'X-TWILIO-SIGNATURE': twilioSig
}
}
axios.get(
URL,config
).catch(error => console.log(error))
.then(response => {
console.log(response.data)
})
response.data shows the TwiML's current XML content.
My attempts at a POST only gives the same output as the GET, while PUT gives 405 Method Not Allowed.
var URL = 'https://handler.twilio.com/twiml/EHXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' + '?AccountSid=' + accountSidFlex
var twilioSig = crypto.createHmac('sha1', authTokenFlex).update(new Buffer(URL, 'utf-8')).digest('Base64')
var config = {
headers:{
'X-TWILIO-SIGNATURE': twilioSig,
'Content-Type': 'text/xml'
}
}
var xml =
'<?xml version="1.0" encoding="UTF-8"?>\
<Response><Play digits="www"/>\
<Say voice="alice">"We are currently experiencing X related issues". </Say>\
</Response>';
axios.post(
URL,xml,config
)
.catch(error => console.log(error))
.then(response => {
console.log(response.data)
})
Ideally I'd like to be able to change a specific TwiML using either HTTP methods or the Twilio-API, so that we can use it in out Studio Flow. We'd just keep it silent until we need to add something to it and revert back to silent once the issues have passed.
Any help would be appreciated!
You cannot currently change the contents of TwiML Bins, Studio Flows, or Twilio Functions programatically. I believe the key functionality you are looking for is a way to dynamically update the messaging (Say/Play Widget) in a Studio flow based on some condition.
One way is to use a Function Widget to retrieve a Twilio Sync document for the message, returning the message as JSON and have the Say/Play widget play that message. You can find the Twilio Sync REST API examples for Add, Modify, and Retrieve in the associated document.
You can retrieve the parsed response using variable syntax detailed here, https://www.twilio.com/docs/studio/widget-library#run-function.
I see that Dialogflow has fulfiment and webhook installations to allow for further dynamic and logistic control over the bot responses. I'm trying to peg a database on top of the webhook, but the channel I'm using is Twilio Text Messaging, and I'm having a little trouble with connecting the two. When I do activate fulfillment, the twilio bot does not read it. Any way to solve this?
I already created a few webhooks using Flask, and integrated it through fulfillment briefly using ngrok, but the bot is responding via the text responses I set for it. Its for google assistance, and facebook messenger, but not with the Twilio integration.
I also tried using inlineJS to see if that held any difference to specifically define Twilio as the messaging outlet to use, however it did not peak success.
const functions = require('firebase-functions');
const {dialogflow} = require('actions-on-google');
const GOODLOCATION = 'location.good'
const NEARLOCATION = 'location.near'
const CHEAPLOCATION = 'location.cheap'
const WELCOME_INTENT = 'Default Welcome Intent'
const FALLBACK_INTENT = 'Default Fallback Intent'
const CRAVINGCULTUREINTENT = 'CravingCulture'
const CRAVINGITEM = 'CravingItem'
const app = dialogflow()
/*Supported Platforms*/
const PLATFORMS = {
UNSPECIFIED: 'PLATFORM_UNSPECIFIED',
FACEBOOK: 'FACEBOOK',
SLACK: 'SLACK',
TELEGRAM: 'TELEGRAM',
KIK: 'KIK',
SKYPE: 'SKYPE',
LINE: 'LINE',
VIBER: 'VIBER',
ACTIONS_ON_GOOGLE: 'ACTIONS_ON_GOOGLE',
TWILIO: 'TWILIO'
};
// Platforms that support Rich messaging
const SUPPORTED_RICH_MESSAGE_PLATFORMS = [
PLATFORMS.FACEBOOK,
PLATFORMS.SLACK,
PLATFORMS.TELEGRAM,
PLATFORMS.KIK,
PLATFORMS.SKYPE,
PLATFORMS.LINE,
PLATFORMS.VIBER,
PLATFORMS.ACTIONS_ON_GOOGLE,
PLATFROM.TWILIO
];
app.intent(WELCOME_INTENT, (conv)=> {
if(agent.requestSource === agent.TWILIO){
conv.ask('This is working, Congratulations!')
}
else{
conv.ask("Could not be served")
}
});
app.intent(FALLBACK_INTENT, (conv)=> {
conv.ask("I am unaware of that phrase, could you repeat that?")
});
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app)
I want the output to be any thing that the user insert through the twilio that this bot will respond accordingly to what is passed in.
A little late, perhaps - hopefully this issue isn't still blocking/plaguing you.
I'm thinking you were intending:
if(agent.requestSource === PLATFORMS.TWILIO){
not:
if(agent.requestSource === agent.TWILIO){
However, also the value of agent.requestSource is actually lowercase "twilio".
I've been stalking around here and have gotten most of my answers as I make my way through this new tool, but I'm now stuck and need some direct advice.
The Gather function in Studio is not PCI compliant, so I have to shift my call to a Function and return the parsed data--I finally figured out how to do that one--however, I've found that I cannot call the web service housed within the single function and had to send the with event.Digits to another function to make the web service call to my token provider. This works, however it has led to a strange result: my token is read back as TTS and then the call is hung up. I have no TTS action in play. Below are my sets of code:
Initial function called from Studio:
const got = require('got');
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
twiml.gather({
input: 'dtmf',
finishOnKey: '#',
timeout: 10,
action: 'paymenttest',
method: 'GET'
}).say('Enter CC');
console.log(twiml);
callback(null, twiml);
};
This successfully calls my function with the digits entered:
const got = require('got');
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.MessagingResponse();
const url ='my payment gateway' + event.Digits + '&EXPDATE=1220&CARDTYPE=VI';
got.get(url, {
headers: {
'content-Type': 'application/x-www-form-urlencoded'
}
}).then(function(response) {
// Check the response and ask your second question here
event.callback(null, response.body);
}).catch(function(error) {
// Boo, there was an error.
callback(error)
});
};
This successfully returns the token....but as mentioned prior...it's read back out to me instead of getting included in the data returned back to Studio.
Twilio developer evangelist here.
Right now Studio is not well setup for using TwiML from a Twilio Function and then continuing the flow. In your case when you return the token from your second Function Twilio is dealing with it as if you just returned text to a regular TwiML webhook. When this happens Twilio defaults to assuming you meant <Say> and reads out the text.
While the team work on redirecting calls back into Studio flows there is a workaround.
Instead of returning the token in the second Function, return some TwiML that includes a <Redirect> to your Studio flow's webhook URL with ?FlowEvent=audioComplete appended. You will also need to add a dummy Say/Play widget after your Function widget (it becomes the next part in the flow that can trigger an "audio complete" message, so exists to collect that and send on to the next widget).
The only thing that we haven't handled in thie workaround is sending the token to the flow. I don't believe we can do this via this redirect workaround, so instead I'd recommend storing the token in your own database or something like a Twilio Sync object. This way you can use it outside of Studio however you like. If you need it within the Studio flow then you can create one more Function that returns the token as JSON, and that will be stored within the flow variables then.
If you would prefer to use <Pay>, as this would be a lot easier, I also recommend requesting the pay connector you need.
I think philnash answer here got old, even when it still works.
Right now, you should have to call the first function using TwiML Redirect node.
In the second function, you should have to add a redirect to the webhook of the flow adding ?FlowEvent=return&foo=bar (where foo=bar should be changed by the info you really want to return).