I'm using Twilio with VueJs for the first time, and i'm getting an error : Twilio.Device is not a constructor
i'm following this tutorial : https://www.twilio.com/blog/make-receive-phone-calls-browser-twilio-programmable-voice-python-javascript
this is my code :
created(){
const Twilio = require("twilio");
let device;
console.log("Requesting Access Token...");
// Using a relative link to access the Voice Token function
getAPI.get("/api/contacts/token/")
.then(function (response) {
console.log("Got a token.");
console.log("Token: " + response.data.token);
// Setup Twilio.Device
device = new Twilio.Device(response.data.token, {
// Set Opus as our preferred codec. Opus generally performs better, requiring less bandwidth and
// providing better audio quality in restrained network conditions. Opus will be default in 2.0.
codecPreferences: ["opus", "pcmu"],
// Use fake DTMF tones client-side. Real tones are still sent to the other end of the call,
// but the client-side DTMF tones are fake. This prevents the local mic capturing the DTMF tone
// a second time and sending the tone twice. This will be default in 2.0.
fakeLocalDTMF: true,
// Use `enableRingingState` to enable the device to emit the `ringing`
// state. The TwiML backend also needs to have the attribute
// `answerOnBridge` also set to true in the `Dial` verb. This option
// changes the behavior of the SDK to consider a call `ringing` starting
// from the connection to the TwiML backend to when the recipient of
// the `Dial` verb answers.
enableRingingState: true,
debug: true,
});
device.on("ready", function (device) {
console.log("Twilio.Device Ready!");
});
device.on("error", function (error) {
console.log("Twilio.Device Error: " + error.message);
});
device.on("connect", function (conn) {
console.log('Successfully established call ! ');
// $('#modal-call-in-progress').modal('show')
});
device.on("disconnect", function (conn) {
console.log("Call ended.");
// $('.modal').modal('hide')
});
})
.catch(function (err) {
console.log(err);
console.log("Could not get a token from server!");
});
}
You should use Twilio client instead of Twilio to make the request.
yarn add twilio-client
// or
npm install twilio-client
Import Device from twilio client
import { Device } from 'twilio-client'; // import the library at the top instead of putting in created()
export default {
...
// same code as yours, only moved import to the top
created() {
let device;
console.log("Requesting Access Token...");
// Using a relative link to access the Voice Token function
getAPI.get("/api/contacts/token/")
.then(function (response) {
console.log("Got a token.");
console.log("Token: " + response.data.token);
// Setup Twilio.Device
device = new Twilio.Device(response.data.token, {
....
}
...
})
...
}
}
Related
I made alert service using FCM, it works fine in my local server. but after I deployed my server in ec2, trying alert function on ec2 app gives me this error :
[ient-SecureIO-1] o.a.t.websocket.pojo.PojoEndpointBase : No error handling configured for [springboot.utils.WebsocketClientEndpoint] and the following error occurred
java.lang.IllegalArgumentException: Exactly one of token, topic or condition must be specified
Reading the error message, i guess that there's no token in my server.
In notification.js, I'm trying to get token and request POST to '/register'
const firebaseModule = (function () {
async function init() {
// Your web app's Firebase configuration
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/firebase-messaging-sw.js')
.then(registration => {
var firebaseConfig = {
configuration Information
};
// Initialize Firebase
console.log("firebase Initialization");
firebase.initializeApp(firebaseConfig);
// Show Notification Dialog
const messaging = firebase.messaging();
messaging.requestPermission()
.then(function() {
console.log("Permission granted to get token");
return messaging.getToken();
})
.then(async function(token) {
console.log("Token: ", token);
await fetch('/register', { method: 'post', body: token })
messaging.onMessage(payload => {
const title = payload.notification.title
const options = {
body : payload.notification.body
}
navigator.serviceWorker.ready.then(registration => {
registration.showNotification(title, options);
})
})
})
.catch(function(err) {
console.log("Error Occured : " +err );
})
})
})
}
}
And by debugging it by console.log(), I found out that code is stopped before "if ('serviceWorker' in navigator) {"
So I need to make it proceed. But to be honest, it's been a while since i made this function, and I don't know almost anything about Javascript. I don't even know what navigator means(I googled it, but couldn't find clear answer) I'm having trouble figuring out what is wrong and how can I fix it. Can someone help me??
I'm trying to implement a basic service worker to assure that users of my simple web app have the latest code. So when I update html, js, or css files I can increment the cachename in the service worker file and prompt users to refresh, clear their cache, and get the latest code.
Until now I've relied on hacky ways to update javascript files (including a parameter in the referring URL: /javascript-file.js?v=1).
The with the service worker code below seem unpredictable: sometimes small changes to JS or CSS are reflected after I increment the cachename (code below). Sometimes the changes are reflected without incrementing the cachename, which suggests the code is ALWAYS pulling from the network (wasting resources).
How can you troubleshoot which version of files the code is using and whether the service worker is using cached or network versions? Am I not understanding the basic model for using service workers to achieve this goal?
Any help appreciated.
serv-worker.js (in root):
console.log('Start serv-worker.js');
const cacheName = '3.2121';
var urlsToCache = [
'home.html',
'home-js.js',
'web-bg.js',
'css/main.css',
'css/edit-menus.css'
];
self.addEventListener('install', event => {
console.log('Install event...', urlsToCache);
event.waitUntil(
caches.open(cacheName)
.then(function(cache) {
console.log('Opened cache', cacheName);
return cache.addAll(urlsToCache);
})
);
});
// Network first.
self.addEventListener('fetch', (event) => {
// Check the cache first
// If it's not found, send the request to the network
// event.respondWith(
// caches.match(event.request).then(function (response) {
// return response || fetch(event.request).then(function (response) {
// return response;
// });
// })
// );
event.respondWith(async function() {
try {
console.log('aPull from network...', event.request);
return await fetch(event.request);
} catch (err) {
console.log('aPull from cache...', event.request);
return caches.match(event.request);
}
}());
});
self.addEventListener('message', function (event) {
console.log('ServiceWorker cache version: ', cacheName, event);
console.log('Received msg1: ', event.data);
if (event.data.action === 'skipWaiting') {
console.log('ccClearing cache: ', cacheName);
// caches.delete('1.9rt1'); // hardcode old one
// caches.delete(cacheName); // actually removes cached versions
caches.keys().then(function(names) {
for (let name of names)
caches.delete(name);
});
self.skipWaiting();
}
});
Code in web-bg.js, which home.html references:
function servWorker(){
let newWorker;
function showUpdateBar() {
console.log('Show the update mssgg...ddddd');
$('#flexModalHeader').html('AP just got better!');
$('#flexModalMsg').html("<p>AP just got better. Learn about <a href='https://11trees.com/support/release-notes-annotate-pro-web-editor/'>what changed</a>.<br><br>Hit Continue to refresh.</p>");
$('#flexModalBtn').html("<span id='updateAPbtn'>Continue</span>");
$('#flexModal').modal('show');
}
// The click event on the pop up notification
$(document).on('click', '#updateAPbtn', function (e) {
console.log('Clicked btn to refresh...');
newWorker.postMessage({ action: 'skipWaiting' });
});
if ('serviceWorker' in navigator) {
console.log('ServiceWORKER 1234');
navigator.serviceWorker.register(baseDomain + 'serv-worker.js').then(reg => {
console.log('In serviceWorker check...', reg);
reg.addEventListener('updatefound', () => {
console.log('A wild service worker has appeared in reg.installing!');
newWorker = reg.installing;
newWorker.addEventListener('statechange', () => {
// Has network.state changed?
console.log('SSState is now: ', newWorker.state);
switch (newWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
// new update available
console.log('Detected service worker update...show update...');
showUpdateBar();
}
// No update available
break;
}
});
});
});
let refreshing;
navigator.serviceWorker.addEventListener('controllerchange', function (e) {
console.log('a1111xxxListen for controllerchange...', e);''
if (refreshing) return;
console.log('Refresh the page...');
window.location.reload();
refreshing = true;
});
} // End serviceworker registration logic
return;
} // END serv-worker
You've commented out the section for /// Check the cache first and then below that the try/catch statement again pulls from the network and falls back to the cache.
Uncomment this section of code and see if you're loading from the cache first.
// event.respondWith(
// caches.match(event.request).then(function (response) {
// return response || fetch(event.request).then(function (response) {
// return response;
// });
// })
// );
Don't forget that even if you request from the network from the service worker the browser will still use it's own internal cache to serve data. How long the data stays in the browser's cache depends on the expiration headers being sent by the server.
When using expires, it's still a fairly common solution to do something like:
index.html - expires after an hour. Has script/css tags that call out file names with ?v=x.y.z
/resources - folder that holds js and css. This folder has a very long expiration time. But that long expiration is short circuited by changing the ?v=x.y.z in index.html
I've used the above successfully in Progressive Web Apps (PWAs). But it is a little painful when debugging. The best option here is to manually clear out the cache and service worker from Dev Tools \ Application, if you're in Chrome.
I implemented Twilio voice call functionality that enables users to call support people from the browser but it works the first time only after allowed microphone permission but then next time getting below error and resetting microphone permission then voice call works.
code: 31000, message: "Cannot establish connection. Client is disconnected"
Below is code snippet on the client-side that written in angular with help of twilio client docs
import twilio from 'twilio-client';
public device: any;
this.device = new twilio.Device('<token-fetched>', {
codecPreferences: ['opus', 'pcmu'],
fakeLocalDTMF: true,
enableIceRestart: true
})
let params = {
To: '<to-number>',
Id: '<id>',
token: '<token-fetched>'
}
if (this.device) {
this.device.connect(params);
}
this.device.on('error', (error) => {
console.log("this is error",error);
})
this.device.on('disconnect',(connection) => {
console.log("connection ended", connection);
})
I had the same issue here, the problem is that I was calling var connection = Twilio.Device.connect(); before the async setup work had been completed.
Write device.connect inside of device.ready function like
Twilio.Device.ready(function(device) {
console.log('Ready');
var connection = Twilio.Device.connect();
// Do rest of twilio work ...!
}
This will fix your issue.
Firebase admin isn't writing to the database.
I am instantiating the database:
var db = admin.database();
Then setting up a reference to the table I want:
var systemsRef = db.ref("systems/");
I then have a function to check if the 'system', (an encrypted hardware id), exists.
function isSystemRegistered(id){
var isTrue;
systemsRef.once('value', function(snapshot) {
isTrue = (snapshot.hasChild(id))? true : false;
});
return isTrue;
}
Which, as of yet returns false; which is true, because it doesn't exist yet. If the system doesn't exist, it writes the data.
const sysID = getSysID();
var sys.info.name = generateUniqueSystemName();
if(isSystemRegistered(sysID){
console.log("system is already registered!");
} else {
msystemsRef.set({
sysID : sys.info.name
}, function(error){
console.log('There was an error while attempting to write to database: ' + error);
});
});
}
I've experimented, and temporarily made my prototype database fully public for a few minutes, just to be sure my rules weren't the issue... They weren't: still no bueno. No writes to the database... and no errors.
I tried a different set, just be sure:
msystemsRef.set("I'm writing data", function(error) {
if (error) {
alert("Data could not be saved." + error);
} else {
alert("Data saved successfully.");
}
});
Again, I'm using an admin account, with public rules, so I should see a now I'm writing data table, just below root. Nothing...
So I switched tactics and attempted to push to the database with the canned tutorial, with my database still fully public:
systemsRef.push({sysID : sys.info.name});
And nothing... Want am I missing?
Make sure the credentials and databaseURL are correct.
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: databaseURL
});
Check if they're matching - credential from one app and databaseURL from another existing app could produce such result.
If you are not loading credential's data from file but from somewhere else, make sure it's not modified - I had an issue with newlines in private key when putting the credential's data to shell variable.
In isSystemRegistered you're returning the synchronized value of isTrue which is undefined.
You should return the promise of the .once('value') method and in the calling method attach a then() to check if it exists.
You can also use the snapshot.exists() to check on a reference for existence.
Edit:
Suggested edit:
var systemRef = admin.database('system');
function isSystemRegistered(id) {
return systemRef.child(id).once('value')
.then(function (snap) {
return snap.exists();
});
}
function writeData(aSystem) {
var sysId = generateUniqueSystemName();
return systemRef.child(sysId)
.once('value')
.then(function (snapshot) {
if (!snapshot.exists()) {
return systemRef.child(sysId).update(aSystem);
}
// Here `sysId === snapshot.key`
return systemRef.child(sysId).set(aSystem);
})
.catch(function (err) {
console.error(err);
});
}
Running on Raspberry Pi raises some more questions.
How does it connect to the internet?
How fast is the connection?
What NodeJS version do you run?
Did this run successfully on your PC?
Same issue here. I eventually realised that the database went offline before the command was even sent to firebase. This made sense because there was no error, since it never even sent the request.
Eg. .set({blah: 123}) does not immediately transmit to the server. Instead, something is placed on the node event queue to execute. If you let the database go offline too soon, it won't process the queue.
Perhaps (like me) you're calling admin.database().goOffline(); at the end of the script? If so, you may just need to defer or delay the offline method until after the transmission.
// (TYPESCRIPT)
import * as admin from 'firebase-admin';
let app = admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "https://blahblah.firebaseio.com/",
});
admin.database().ref().push({ something: 123 }).then(() => {
console.log("push complete");
});
// delay before going offline
setTimeout(() => {
admin.database().goOffline();
process.abort();
}, 2000);
Am using FCM to handle notifications, it works fine up until when I need to update my UI from the firebase-messaging-sw.js when my web app is in the background.
My first question is: is it possible to update my web app UI in the background (When user is not focused on the web app) through a service worker
Secondly, if so, how? because I tried a couple of things and its not working, obviously am doing something wrong and when it does work, my web app is in the foreground. What am I doing wrong?
My codes are below.
my-firebase-service-sw.js
// [START initialize_firebase_in_sw]
// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase
libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/4.1.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.1.1/firebase-messaging.js');
// My Custom Service Worker Codes
var CACHE_NAME = 'assembly-v0.1.3.1';
var urlsToCache = [
'/',
'lib/vendors/bower_components/animate.css/animate.min.css',
'lib/vendors/bower_components/sweetalert/dist/sweetalert.css',
'lib/css/app_1.min.css',
'lib/css/app_2.min.css',
'lib/css/design.css'
];
var myserviceWorker;
var servicePort;
// Install Service Worker
self.addEventListener('install', function (event) {
console.log('installing...');
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function (cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
console.log('installed...');
});
// Service Worker Active
self.addEventListener('activate', function (event) {
console.log('activated!');
// here you can run cache management
var cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames.map(function (cacheName) {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request)
.then(function (response) {
// Cache hit - return response
if (response) {
return response;
}
// IMPORTANT: Clone the request. A request is a stream and
// can only be consumed once. Since we are consuming this
// once by cache and once by the browser for fetch, we need
// to clone the response.
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(
function (response) {
// Check if we received a valid response
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// IMPORTANT: Clone the response. A response is a stream
// and because we want the browser to consume the response
// as well as the cache consuming the response, we need
// to clone it so we have two streams.
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function (cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
self.addEventListener('message', function (event) {
console.log("SW Received Message: " + event.data);
// servicePort = event;
event.ports[0].postMessage("SW Replying Test Testing 4567!");
});
myserviceWorker = self;
// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
'messagingSenderId': '393093818386'
});
// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();
// [END initialize_firebase_in_sw]
// If you would like to customize notifications that are received in the
// background (Web app is closed or not in browser focus) then you should
// implement this optional method.
// [START background_handler]
messaging.setBackgroundMessageHandler(function (payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
// send to client
console.log('Sending data to notification');
try {
myserviceWorker.clients.matchAll().then(function (clients) {
clients.forEach(function (client) {
console.log('sending to client ' + client);
client.postMessage({
"msg": "401",
"dta": payload.data
});
})
});
} catch (e) {
console.log(e);
}
const notificationTitle = payload.data.title;;
const notificationOptions = {
body: payload.data.body,
icon: payload.data.icon,
click_action: "value"
};
return self.registration.showNotification(notificationTitle,
notificationOptions);
});
// [END background_handler]
In my main javascript file, which receives the payload. it receives it when the application is in the foreground. My major concern and problem is receiving payload when the application is in the background, all activities on foreground works just fine.
It is possible to update the UI even your website is opening but unfocused.
Just add enable option includeUncontrolled when you get all client list.
Example:
messaging.setBackgroundMessageHandler(function (payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
self.clients.matchAll({includeUncontrolled: true}).then(function (clients) {
console.log(clients);
//you can see your main window client in this list.
clients.forEach(function(client) {
client.postMessage('YOUR_MESSAGE_HERE');
})
})
});
In your main page, just add listener for message from service worker.
Ex:
navigator.serviceWorker.addEventListener('message', function (event) {
console.log('event listener', event);
});
See Clients.matchAll() for more details.