Sending unique codes via SMS automation - twilio

I have a guest wifi that required the user to have a voucher code in order to access the wifi network.
What am trying to achieve is for the user to send a sms message to my twilio phone number.
I would for the user to be somehow validated. once validated a sms message would be sent to the user with a unique voucher code.
Each voucher code would be unique and i would need a way to upload them so that twilio can grab a new code each time and send to the user.
I would really appreciate if someone would be willing to help me on this.
I have already created a sms automation using twilio studio. It does everything that i want it to do except for the validation part and i am not sure how to send a unique code each time the flow is triggered.
Thanks!

Okay, you didn't mention the framework or language you are using to communicate with the Twilio API. but I will explain the principle.
first, I will recommend that you have a data table where you store the codes you send and the user who received it. ( if you are working with a database you can set the code field to unique, so you can be sure about the duplication )
then before going to send the code to Twilio API so it can forward it in an SMS to the user you should validate it by checking if the newly generated code is already used or not.
this is an example using the nodejs and the mongoose ORM ( Mongo DB database ):
const mongoose = require( "mongoose" );
const Schema = mongoose.Schema;
const uniqueValidator = require( "mongoose-unique-validator" );
const CodeSchema = new Schema( {
owner: { type: Schema.Types.ObjectId, ref: "User" },
code: { type: Number, index: true, unique: true }
} ,{ timestamps: true } );
CodeSchema.plugin( uniqueValidator, { message: "is already taken." } );
CodeSchema.pre( "validate", function( next ){
if( !this.code ) {
this.code = Math.floor( Math.random() * Math.pow( 36, 6 ) | 0 );
}
next();
} );
module.exports = mongoose.model( "Code", CodeSchema ) || mongoose.models.Code;
then when you create the schema this pre-validate function will execute and fill the code field with a unique code.

Related

Custom activity feed notifications sent from daemon(nodejs app), not getting the data assigned to subEntityId in teams mobile client

I'm using activityFeedNotification graph api to send push notification to the users of our teams tab app from backend using nodejs. The notification is sending successfully in both teams desktop and mobile client but we're not getting the data assigned to subEntityId in mobile client(In desktop client and browser we're getting it).
We are encoding the data(object) and assigning it to the subEntityId in teams context in our nodejs application. Then in teams client, we get that data from teams context using microsoft teams sdk and redirect user to the respective page in our application based on whatever data we get in subEntityId
In desktop, deeplinking is working perfectly but in android client, we're not getting any data in subEntityId. It is just opening the homepage of our tab app but I need to redirect user to specific page based whatever data is assigned to subEntityId.
Below I've provided how we're encoding the data and assigning it to subEntityId.
Server Side:
const context = encodeURIComponent(
JSON.stringify({
"subEntityId": {
"type": "PROGRAM_PROFILE",
"program_id": "12345",
uid: uuidv4(),
}
})
);
const body = {
topic: {
source: 'text',
value: notificationTopic,
webUrl: `https://teams.microsoft.com/l/entity/${TEAMS_APP_ID}/index?context=${context}`,
},
activityType: 'commonNotification',
previewText: {
content: notificationSubtitle,
},
templateParameters: [
{
name: 'title',
value: notificationTitle,
},
],
};
const url = `https://graph.microsoft.com/v1.0/users/${userId}/teamwork/sendActivityNotification`;
await axios.post(url, body));
Client Side:
const context = await app.getContext();
console.log(context?.page?.subPageId); // getting undefined
Any kind of help is appreciated!
From the documentation:
{page.subPageId}: The developer-defined unique ID for the subpage this content points defined when generating a deep link for a specific item within the page. (Known as {subEntityId} prior to TeamsJS v.2.0.0).
You are using subEntityId on the backend but accessing subPageId on the client side.

Twilio + Airtable / Collecting data from a chatbot

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

Save a user's pushSubscription info in Rails database?

I have followed this tutorial: https://dzone.com/articles/how-to-add-real-web-push-notifications-to-your-web to enable push notifications on my rails app. I am using the webpush gem to send the notifications.
So far, all I have managed to do is get the browser to ask for permission to send notifications, and when I try to call the method send_web_push_notification (shown below) line 2 is throwing up an error.
I think it is because I am not saving the user's pushSubscription info to the database, but I don't know how to do this. In the tutorial, there is this line at the end: 'We use a database JSON field called web_push_subscription to save the pushSubscription info on our users.'
Would someone be able to show me how to do this?
Any help would be appreciated. Thanks.
send_web_push_notification method:
def send_web_push_notification(user_id)
subscription = User.find(user_id).web_push_subscription
message = {
title: "You have a message!",
body: "This is the message body",
tag: "new-message"
}
unless subscription.nil?
Webpush.payload_send(
message: JSON.generate(message),
endpoint: subscription["endpoint"],
p256dh: subscription["keys"]["p256dh"],
auth: subscription["keys"]["auth"],
ttl: 15,
vapid: {
subject: 'mailto:admin#example.com',
public_key: Rails.application.config.webpush_keys[:public_key],
private_key: Rails.application.config.webpush_keys[:private_key]
}
)
end
end
serviceworker.js.erb:
function showNotification(event) {
return new Promise(resolve => {
const { body, title, tag } = JSON.parse(event.data.text());
self.registration
.getNotifications({ tag })
.then(existingNotifications => { // close? ignore? })
.then(() => {
const icon = `/path/to/icon`;
return self.registration
.showNotification(title, { body, tag, icon })
})
.then(resolve)
})
}
self.addEventListener("push", event => {
event.waitUntil(
showNotification(event)
);
}
});
self.addEventListener("notificationclick", event => {
event.waitUntil(clients.openWindow("/"));
});
application.js:
const permission = Notification.requestPermission();
if (permission !== 'granted') {
// no notifications
}else{
// yay notifications
}
function subscribeToPushNotifications(registration) {
return registration.pushManager
.subscribe({
userVisibleOnly: true,
applicationServerKey: window.vapidPublicKey
})
.then(pushSubscription => {
console.log(
"Received PushSubscription:",
JSON.stringify(pushSubscription)
);
return pushSubscription;
});
}
If you look closely codes you will notice that you have to create a Json field in database to save subscription. If there is subscription available than push notification will be sent. Actually there many more scenarios it is not necessary the you want to save one browser for user notification, if you plan multiple browser than you have to create separate table, but if you want to add one browser for push notification, than you can add this information in user table too. Create new migration to update your user table and add following column
t.json "web_push_subscription"
Run migration, Now you have Json column if you notice code clearly following are information you require in your user database, you will save this information when user subscribe for push notification
user. web_push_subscription[:endpoint] = what_ever_value_received
user.web_push_subscription[:auth] = what_ever_value_received
Unfortunately it is just idea as I have not implement it, but I should check JSON.stringify(pushSubscription) object recived, and there are chances all data would be in this response which you received you may need to save it as it is to your subscription.
You also need to save permission, that user really allowed you to send notification, if yes than one field in user as boolean notification = true, so you can check if user allow you to send notification, than you can send, otherwise don't send. You should also have way to remove these keys for specific user when they unsubscribe notifications.
You basically need to update a model, which is backend, but you do not want the user to go through all that process. This is where ajax comes in handy. I am not very comfortable with ajax but it is one of the best things provided by JS.
With the code in ajax function, you will hit the controller update action with the changed attribute and the update will change the model as necessary and update it. then your html will change accordingly without page refresh.
TLDR: I think you are looking for this.

How to use DialogFlow fulfilment and webhooks for Twilio?

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".

Service worker remote/foreign installation [duplicate]

I have two subdomains: https://abc.xxxx.com and https://xyz.xxxx.com. So my questions:
1). is it possible to register a service worker for
https://xyz.xxxx.com from https://abc.xxxx.com ? if yes then how?
2). if http://abc.xxxx.com (http insecure) then anyway to register
a service worker for https://xyz.xxxx.com from http://abc.xxxx.com like in iframe or something....
This is a real situation, I am facing for my multiple subdomain. Any help appreciated. Thanks in advance.
Here are some general answers that I think should address the various points you raise in your question:
Each registered service worker has an associated scope, which dictates the set of web pages that the service worker can control. The scope of a service worker is a URL, and that URL must have the same origin as the page that registers the service worker, and must be either a URL that corresponds to the same path level as the page or a path that's one or more levels down. The default scope corresponds to the same path level as location of the service worker script. Because of this restriction, it's not possible to call navigator.serviceWorker.register(...) from a page on one (sub-)domain and end up with a service worker that controls pages on another (sub-)domain.
There are restrictions in place to prevent you from throwing an https: <iframe> on an http: page and using that to register a service worker. See DOMException when registering service worker inside an https iframe
Though I don't know that it's directly related to your question, explicitly calling fetch() for an http: resource within your service worker code will result in a failure in current versions of Chrome, since mixed-content fetch()s are not allowed within a service worker. I don't know if things are 100% settled on that front, and this open bug is still relevant.
If you have pages that live on both abc.123.com and xyz.123.com and you want both sets of pages to be controlled by a service worker, then you need to have two separate service worker registrations. Each registration needs to be for a copy of your service worker JS file that's hosted on the respective domain corresponding to the top-level page, and all pages and service worker scripts need to be accessed via https:.
That being said, you can kick off a service worker registration for a different domain by including a cross-domain <iframe> on a page, but both the host page and the <iframe> need to be served via https:. The normal service worker scoping restrictions apply, so if, for example, you want to register a service worker for the other domain that will cover the entire https://other-domain.com/ scope, you need to make sure that the location of the service worker script being registered is at the top-level, e.g. https://other-domain.com/service-worker.js, not at https://other-domain.com/path/to/service-worker.js. This is the approach used by, for example, the AMP project via the <amp-install-serviceworker> element.
Service Worker scripts must be hosted at the same origin (Protocol + Domain name + Port). Each sub-domain is considered a different origin, So, you will need to register a service worker for each one. Each of these workers will have its own cache and scope.
Try use Ngnix proxy_pass. This work for me.
My bad, I misunderstood a bit. Well, here's the code
if('serviceWorker' in navigator){
if(window.location.pathname != '/'){
//register with API
if(!navigator.serviceWorker.controller) navigator.serviceWorker.register('/service-worker', { scope: '/' });
//once registration is complete
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration){
//get subscription
serviceWorkerRegistration.pushManager.getSubscription().then(function(subscription){
//enable the user to alter the subscription
//jquery selector for enabling whatever you use to subscribe.removeAttr("disabled");
//set it to allready subscribed if it is so
if(subscription){
//code for showing the user that they're allready subscribed
}
});
});
}
}else{
console.warn('Service workers aren\'t supported in this browser.');
}
then here's the event -ish for your subscribe / unsubscribe
// subscribe or unsubscribe to the ServiceWorker
$(document.body).on('change', /*selector*/, function(){
//new state is checked so we subscribe
if($(this).prop('checked')){
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration){
serviceWorkerRegistration.pushManager.subscribe()
.then(function(subscription){
// The subscription was successful
console.log('subscription successful'); //subscription.subscriptionId
//save in DB - this is important because
$.post($('#basePath').val() + 'settings/ajax-SW-sub/', {id:subscription.subscriptionId}, function(data){
//console.log(data);
}, 'json');
}).catch(function(e) {
if (Notification.permission === 'denied') {
// The user denied the notification permission which
// means we failed to subscribe and the user will need
// to manually change the notification permission to
// subscribe to push messages
console.warn('Permission for Notifications was denied');
} else {
// A problem occurred with the subscription; common reasons
// include network errors, and lacking gcm_sender_id and/or
// gcm_user_visible_only in the manifest.
console.error('Unable to subscribe to push.', e);
}
});
});//*/
//new state us unchecked so we unsubscribe
}else{
$('.js-enable-sub-test').parent().removeClass('checked');
//get subscription
navigator.serviceWorker.ready.then(function(reg) {
reg.pushManager.getSubscription().then(function(subscription) {
//unregister in db
$.post($('#basePath').val() + 'settings/ajax-SW-unsub/', {id:subscription.subscriptionId}, function(data){
//console.log(data);
}, 'json');
//remove subscription from google servers
subscription.unsubscribe().then(function(successful) {
// You've successfully unsubscribed
console.log('unsubscribe successful');
}).catch(function(e) {
// Unsubscription failed
console.log('unsubscribe failed', e);
})
})
});//*/
}
});
after that you need to register an account on the google developer console and register a project for something like *.xxxx.com . Then you need to get a proper manifest json with gcm_sender_id and gcm_user_visible_only
You need to create a key for both server and browser applications, there's more info on that on this page.
https://developers.google.com/web/updates/2015/03/push-notificatons-on-the-open-web?hl=en
The one for browser applications goes in your manifest json.
Then to send out push notifications you'll be using something like this:
function addSWmessage($args){
$output = false;
if(!isset($args['expiration']) || $args['expiration'] == ''){
$args['expiration'] = date('Y-m-d H:i:s', strtotime('+7 days', time()));
}
$sql = sprintf("INSERT INTO `serviceworker_messages` SET title = '%s', body = '%s', imageurl = '%s', linkurl = '%s', hash = '%s', expiration = '%s'",
parent::escape_string($args['title']),
parent::escape_string($args['text']),
parent::escape_string($args['imageurl']),
parent::escape_string($args['linkurl']),
parent::escape_string(md5(uniqid('******************', true))),
parent::escape_string($args['expiration']));
if($id = parent::insert($sql)){
$output = $id;
}
return $output;
}
function pushSWmessage($args){
//$args['messageid'] $args['userids'][]
foreach($args['userids'] as $val){
$sql = sprintf("SELECT messages_mobile, messages FROM `users_serviceworker_hash` WHERE users_id = '%s'",
parent::escape_string($val));
if($row = parent::queryOne($sql)){
$m1 = json_decode($row['messages'], true);
$m1[] = $args['messageid'];
$m2 = json_decode($row['messages_mobile'], true);
$m2[] = $args['messageid'];
$sql = sprintf("UPDATE `users_serviceworker_hash` SET messages = '%s', messages_mobile = '%s' WHERE users_id = '%s'",
parent::escape_string(json_encode($m1)),
parent::escape_string(json_encode($m2)),
parent::escape_string($val['users_id']));
parent::insert($sql);
}
}
$sql = sprintf("SELECT subscriptionID, users_id FROM `users_serviceworker_subscriptions`");
if($rows = parent::query($sql)){
foreach($rows as $val){
if(in_array($val['users_id'], $args['userids'])){
$registrationIds[] = $val['subscriptionID'];
}
}
if(isset($registrationIds) && !empty($registrationIds)){
// prep the bundle
$msg = array
(
'message' => '!',
'title' => '!',
'subtitle' => '!',
'tickerText' => '!',
'vibrate' => 1,
'sound' => 1,
'largeIcon' => '!',
'smallIcon' => '!'
);
$headers = array
(
'Authorization: key='.SW_API_ACCESS_KEY,
'Content-Type: application/json'
);
$fields = array
(
'registration_ids' => $registrationIds,
'data' => $msg
);
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL, 'https://android.googleapis.com/gcm/send');
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch,CURLOPT_POSTFIELDS, json_encode($fields));
curl_exec($ch);
curl_close($ch);
}
}
}
And no, I don't know what issue you've been having but this works for me with multiple sub domains. :)

Resources