I'm working on a project now within Twilio, using Twilio Functions, where I'm trying to set up SMS messaging so that if we receive an incoming keyword, we respond with a specific message, including a URL. The plan is to have multiple incoming keywords, with different responses so if someone sends an SMS to one of our numbers, depending on that key word, we respond with a basic message and a URL. I'm trying to figure out the best way to handle this within Twilio Functions.
I have this working for a single incoming keyword/response, as seen below.
if (incomingMessage.includes('testpark')) {
twiml.message('StartMyParking:\n\nTo start your parking, please click this link: https://blahblah.com');
} else if (incomingMessage.includes('bye')) {
twiml.message('Goodbye!');
} else {
twiml.message('Please check your zone/code and try again.');
}
While that works, I want to add in more incoming words, along with responses, such as an incoming message of 'testpark2' and a response of 'StartMyParking:\n\nTo start your parking, please click this link: https://blahblah2.com'.
Then I would want to include another one with 'testpark3' and a response of 'StartMyParking:\n\nTo start your parking, please click this link: https://blahblah3.com' and so on, all within the same script.
Can someone help me understand how to achieve this?
There are a lot of ways to achieve your desired outcome, but here's the most straightforward to begin with.
Instead of creating an else if statement for every possible keyword, you could define the keyword/response pairs up front using a JavaScript Map.
The keys of the Map will be your keywords, the values of the Map will be your responses:
const keywordResponseMap = new Map([
['testpark2', 'StartMyParking:\n\nTo start your parking, please click this link: https://blahblah2.com'],
['testpark3', 'StartMyParking:\n\nTo start your parking, please click this link: https://blahblah3.com'],
['testpark', 'StartMyParking:\n\nTo start your parking, please click this link: https://blahblah.com'],
]);
const keywords = Array.from(keywordResponseMap.keys());
let keyword;
if (incomingMessage.includes('bye')) {
twiml.message('Goodbye!');
}
else if (keyword = keywords.find(k => incomingMessage.includes(k))) {
const response = keywordResponseMap.get(keyword);
twiml.message(response);
} else {
twiml.message('Please check your zone/code and try again.');
}
Also note that I'm putting the bye case up front because it is more performant than looking for the keywords in the incomingMessage, thus you avoid unnecessarily doing that processing when a user says bye.
You can use find to search for any keyword that is in the incomingMessage, then you can use the keyword that you found to retrieve the response from the map.
If your response will always be the same except for the URL, you could further optimize this by only storing the URL in the map and using string interpolation like this:
const keywordUrlMap = new Map([
['testpark2', 'https://blahblah2.com'],
['testpark3', 'https://blahblah3.com'],
['testpark', 'https://blahblah.com'],
]);
const keywords = Array.from(keywordUrlMap.keys());
let keyword;
if (incomingMessage.includes('bye')) {
twiml.message('Goodbye!');
}
else if (keyword = keywords.find(k => incomingMessage.includes(k))) {
const url = keywordUrlMap.get(keyword);
twiml.message(`StartMyParking:\n\nTo start your parking, please click this link: ${url}`);
} else {
twiml.message('Please check your zone/code and try again.');
}
It is also important to note that I'm putting testpark last in the map because testpark matches to testpark2 and testpark3. If you'd put it first, it would always resolve to testpark even with a user submits testpark2 or similar values.
Also, I'm using the Map type because it guarantees the order in which the keys are returned, which is again important for the previous point.
When you have a lot more keywords and responses, you may have to start looking at a solution to store them externally like a database, and query the database by keyword to resolve the response.
Good luck, we can't wait to see what you build!
Related
I have been working on this problem for a couple of days now, and feel like I'm extremely close to solving the issue. I need to cycle through an object and send a text message to each record in the object before moving on to the next line of code. The code to send works when isolated locally. But I'm having trouble getting it to work in Twilio Functions. All my code appears to work, but the text messages are not sent.
for await (const contact of allItems) {
client.messages
.create({
body: "Hey " + contact.name + "!" + " " + message,
messagingServiceSid: messaging_service,
to: contact.key
});
};
// End Send Message to Each Contact
I've attached the portion of code I believe I'm having issues with.
What I want to do, is for this piece of code to run completely before moving on to the next few lines and invoking a callback.
Any idea how I could do something like this?
Twilio developer evangelist here.
The issue here is that you are not awaiting the actual result of the asynchronous call. Locally, that doesn't matter, but within Twilio Functions once you call the callback function all asynchronous calls that have started are terminated.
To create a loop that makes asynchronous requests and waits for them all to complete you will want to use Promise.all in conjunction with map. In your example, it should look like this:
function SendMessages(allItems, message, client, messagingService) {
const apiRequests = allItems.map((contact) => {
let usersName = contact.name;
return client.messages.create({
body: `Hey ${usersName}! ${message}`,
messagingServiceSid: messagingService,
to: contact.key,
});
})
return Promise.all(apiRequests);
}
In this function we map over the list contacts returning the promise that is created when we try to send a message. The variable apiRequests becomes an array of promises that represent the requests to the API. We can then use this array of promises in Promise.all. Promise.all will only resolve once all the promises it is passed have resolved (or if a promise rejects).
I recommend you read my answer to your previous question as that brings up some further issues that you might have with this.
I'm using this way to get the username data from Instagram:
https://api.instagram.com/v1/users/search?q=[USERNAME]&client_id=[CLIENT ID]
It works fine, but has a flaw - the username search actually gets ALL usernames starting with the string you set.
Why/How is that happening ?
There is a limit of 52 username results, so is there a way to increase it, because if you search "asdasd" (which is an existing account!) you would get probably a million accounts ?
Is there a work-around, because I want to search for the exact username ?
So it seems there is only this API for this functionality. You can simply use this workaround: make your request as you are doing it right now, then you can filter out the single item you need. You can iterate through the list of users, and only keep the one, where the username is exactly the same as you have specified.
SOLUTION:
Put the username in quotations like this (username: asdasdasd):
https://api.instagram.com/v1/users/search?q="asdasdasd"&client_id=[CLIENT ID]
Which results in (the results that interests you is highlighted):
- if you don't use the quotations there's a big chance that the desired username won't appear in the results!
If there are more than one results use this code the iterate through the response data to find your one and get it's ID, full_name etc.
function getUserID() {
//send request for the user info on click
$('.btn-user-request').click(function(){
var searchTerm = $('.input-user-request').val();
if(searchTerm == ''){
$('.user-id-value').html('Enter a username!');
}
else{
$.ajax({
url: "https://api.instagram.com/v1/users/search?q=\"" + searchTerm + "\"&client_id=5fc90c90b885487485125d6df440fefd",
dataType: 'jsonp'
}).done(function(data) {
if(data.data[0] == []){
$('.user-id-value').html('No username found!');
}
else{
for(i=0;i<data.data.length;i++){
var userInformation = data.data[i];
if(userInformation.username == searchTerm){
$('.user-id-value').html(userInformation.id);
break;
}
}
}
});
}
});
//simulate the button click, on click of the Enter key
$(".input-user-request").on('keydown', function(){
if(event.keyCode == 13){
$(".btn-user-request").click();
}
});
}
See JSFiddle
(for some reason the code doesn't format properly)
I would suggest to try username "jack".
In fact none of suggested methods work for this.
I tried with max_id, min_id, it does not work either.
So it is exact flaw in API, and there is no such documentation on Instagram API to help to solve this.
So finding exact user id by it's username is quite a problem for popular names.
The only one solution for today I found is parsing user's Instagram HTML page and get it's id from there. This is very stupid, but this is only one solution which works in all cases right now :(
It's simple:
https://www.instagram.com/{username}/?__a=1
we are developing a mobile (iOS/Android) application through which the user can send an automatic Twilio phone call (using Parse Cloud Code) to a number of her/his choice. Is it possible to dynamically set the content of the phone call from the client (like one can do with SMS’s by passing a string as a variable)? From the examples in the Parse/Twilio documentation it seems that this is not the case and one can only set in advance different texts at different URL’s and choose between them. Is it so?
Twilio developer evangelist here.
You can dynamically set phone call content from a single URL by adding URL parameters to the URL you set for the phone call. So, if, for example, you created a call like so:
client.makeCall({
to: NUMBER,
from: YOUR_TWILIO_NUMBER,
url: 'http://example.com/call/?name=Phil'
}, function(err, responseData) {
// call is made
});
Then you can use the name parameter in the callback to alter the response. Here's an example route in express:
app.post('/call', function(req, res) {
responseText = "Hello " + req.query.name;
res.send("<Response><Say>" + responseText + "</Say></Response>");
});
I hope this helps, let me know if you have any other questions.
I am trying to access a tweet conversation history using TweetSharp.
The requirement is like, if I use id of one tweet item it should
return me the whole thread that followed before that tweet item.
But could not find such method exposed through TwittertService, where
I can pass the current Tweet _id and get the conversation details.
I followed the following approach to get the collection (list) ie,
List<TwitterStatus> list = new List<TwitterStatus>();
private void GetReplied(TwitterStatus twitter, TwitterResponse twitterResponse)
{
if (twitter.InReplyToStatusId != null)
{
long statusID = (long)twitter.InReplyToStatusId;
this.ts.GetTweet(statusID, (twitterRecursive,
twitterResponseRecursive) =>
{
list.Add(twitterRecursive);
if (twitter.InReplyToStatusId != null)
{
this.GetReplied(twitterRecursive,
twitterResponseRecursive);
}
});
}
else
{
Debug.WriteLine(list.Count);
foreach (TwitterStatus status in list)
{
Debug.WriteLine(status.Text);
}
}
}
this.ts.GetTweet(<tweet Id>, twitterResponse) =>
{
list.Add(twitter);
this.GetReplied(twitter, twitterResponse);
});
Just wanted to have your advice, on that. Do we have any such method
with TweetSharp or any alternative approach can be implemented?
Really appreciate your help.
Yep, that's how you'll have to do it: start with a tweet and walk its in_reply_to_status_id chain.
One thing I do, although it may not be applicable to you, is check a local cache of tweets before fetching from Twitter to see if I already fetched the tweet being replied to.
As for libraries that make this easier, I don't know of any. Regardless of whether they expose a more streamlined method, under the covers they'll still have to perform the process that you implemented in your code.
To see how another library approached this task, take a look at this Twitterizer sample.
I want to prefix URLs which match my patterns. When I open a new tab in Firefox and enter a matching URL the page should not be loaded normally, the URL should first be modified and then loading the page should start.
Is it possible to modify an URL through a Mozilla Firefox Addon before the page starts loading?
Browsing the HTTPS Everywhere add-on suggests the following steps:
Register an observer for the "http-on-modify-request" observer topic with nsIObserverService
Proceed if the subject of your observer notification is an instance of nsIHttpChannel and subject.URI.spec (the URL) matches your criteria
Create a new nsIStandardURL
Create a new nsIHttpChannel
Replace the old channel with the new. The code for doing this in HTTPS Everywhere is quite dense and probably much more than you need. I'd suggest starting with chrome/content/IOUtils.js.
Note that you should register a single "http-on-modify-request" observer for your entire application, which means you should put it in an XPCOM component (see HTTPS Everywhere for an example).
The following articles do not solve your problem directly, but they do contain a lot of sample code that you might find helpful:
https://developer.mozilla.org/en/Setting_HTTP_request_headers
https://developer.mozilla.org/en/XUL_School/Intercepting_Page_Loads
Thanks to Iwburk, I have been able to do this.
We can do this my overriding the nsiHttpChannel with a new one, doing this is slightly complicated but luckily the add-on https-everywhere implements this to force a https connection.
https-everywhere's source code is available here
Most of the code needed for this is in the files
IO Util.js
ChannelReplacement.js
We can work with the above files alone provided we have the basic variables like Cc,Ci set up and the function xpcom_generateQI defined.
var httpRequestObserver =
{
observe: function(subject, topic, data) {
if (topic == "http-on-modify-request") {
var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
var requestURL = subject.URI.spec;
if(isToBeReplaced(requestURL)) {
var newURL = getURL(requestURL);
ChannelReplacement.runWhenPending(subject, function() {
var cr = new ChannelReplacement(subject, ch);
cr.replace(true,null);
cr.open();
});
}
}
},
get observerService() {
return Components.classes["#mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
},
register: function() {
this.observerService.addObserver(this, "http-on-modify-request", false);
},
unregister: function() {
this.observerService.removeObserver(this, "http-on-modify-request");
}
};
httpRequestObserver.register();
The code will replace the request not redirect.
While I have tested the above code well enough, I am not sure about its implementation. As far I can make out, it copies all the attributes of the requested channel and sets them to the channel to be overridden. After which somehow the output requested by original request is supplied using the new channel.
P.S. I had seen a SO post in which this approach was suggested.
You could listen for the page load event or maybe the DOMContentLoaded event instead. Or you can make an nsIURIContentListener but that's probably more complicated.
Is it possible to modify an URL through a Mozilla Firefox Addon before the page starts loading?
YES it is possible.
Use page-mod of the Addon-SDK by setting contentScriptWhen: "start"
Then after completely preventing the document from getting parsed you can either
fetch a different document from the same domain and inject it in the page.
after some document.URL processing do a location.replace() call
Here is an example of doing 1. https://stackoverflow.com/a/36097573/6085033