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.
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 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, {
....
}
...
})
...
}
}
We currently have a VueJS application and I am looking at migrating it to Cycle.js (first major project).
I understand in Cycle.JS we have SI and SO for drivers (using adapt()); naturally a WebSocket implementation fits this as it has both read and write effects.
We use Phoenix (Elixir) as our backend using Channels for soft real-time communication. Our client-side WS library is Phoenix herehttps://www.npmjs.com/package/phoenix.
The example on Cycle.js.org is perfect if you know how to connect.
In our case, we authenticate using a REST endpoint which returns a token (JWT) which is used to initialize the WebSocket (token parameter). This token cannot simply be passed into the driver, as the driver is initialized when the Cycle.js application runs.
An example (not actual code) of what we have now (in our VueJS application):
// Code ommited for brevity
socketHandler = new vueInstance.$phoenix.Socket(FQDN, {
_token: token
});
socketHandler.onOpen(() => VueBus.$emit('SOCKET_OPEN'));
//...... Vue component (example)
VueBus.$on('SOCKET_OPEN', function () {
let chan = VueStore.socketHandler.channel('PRIV_CHANNEL', {
_token: token
});
chan.join()
.receive('ok', () => {
//... code
})
})
The above is an example, we have a Vuex store for a global state (socket etc), centralized message bus (Vue app) for communicating between components and channel setups which come from the instantiated Phoenix Socket.
Our channel setup relies on an authenticated Socket connection which needs authentication itself to join that particular channel.
The question is, is this even possible with Cycle.js?
Initialize WebSocket connection with token parameters from a REST call (JWT Token response) - we have implemented this partially
Create channels based off that socket and token (channel streams off a driver?)
Accessing multiple channel streams (I am assuming it may work like sources.HTTP.select(CATEGORY))
We have a 1: N dependency here which I am not sure is possible with drivers.
Thank you in advance,
Update# 17/12/2018
Essentially what I am trying to imitate is the following (from Cycle.js.org):
The driver takes a sink in, in order to perform write effects (sending messages on a specific channels) but also may return a source; this means there are two streams which are async? Which means that creating the socket at runtime may lead to one stream accessing the "socket" before it is instanitated; please see comments in the snippet below.
import {adapt} from '#cycle/run/lib/adapt';
function makeSockDriver(peerId) {
// This socket may be created at an unknown period
//let socket = new Sock(peerId);
let socket = undefined;
// Sending is perfect
function sockDriver(sink$) {
sink$.addListener({
next: listener => {
sink$.addListener({
next: ({ channel, data }) => {
if(channel === 'OPEN_SOCKET' && socket === null) {
token = data;
// Initialising the socket
socket = new phoenix.Socket(FQDN, { token });
socketHandler.onOpen(() => listener.next({
channel: 'SOCKET_OPEN'
}));
} else {
if(channels[channel] === undefined) {
channels[channel] = new Channel(channel, { token });
}
channels[channel].join()
.receive('ok', () => {
sendData(data);
});
}
}
});
},
error: () => {},
complete: () => {},
});
const source$ = xs.create({
start: listener => {
sock.onReceive(function (msg) {
// There is no guarantee that "socket" is defined here, as this may fire before the socket is actually created
socket.on('some_event'); // undefined
// This works however because a call has been placed back onto the browser stack which probably gives the other blocking thread chance to write to the local stack variable "socket". But this is far from ideal
setTimeout(() => socket.on('some_event'));
});
},
stop: () => {},
});
return adapt(source$);
}
return sockDriver;
}
Jan van Brügge, the soluton you provided is perfect (thank you) except I am having trouble with the response part. Please see above example.
For example, what I am trying to achieve is something like this:
// login component
return {
DOM: ...
WS: xs.of({
channel: "OPEN_CHANNEL",
data: {
_token: 'Bearer 123'
}
})
}
//////////////////////////////////////
// Some authenticated component
// Intent
const intent$ = sources.WS.select(CHANNEL_NAME).startWith(null)
// Model
const model$ = intent$.map(resp => {
if (resp.some_response !== undefined) {
return {...}; // some model
}
return resp;
})
return {
DOM: model$.map(resp => {
// Use response from websocket to create UI of some sort
})
}
first of all, yes this is possible with a driver, and my suggestion will result in a driver that feels quite like the HTTP driver.
First of all to have some rough pseudo code that where I can explain everything, I might have misunderstood parts of your question so this might be wrong.
interface WebsocketMessage {
channel: string;
data: any;
}
function makeWebSocketDriver() {
let socket = null;
let token = null;
let channels = {}
return function websocketDriver(sink$: Stream<WebsocketMessage> {
return xs.create({
start: listener => {
sink$.addListener({
next: ({ channel, data }) => {
if(channel === 'OPEN_SOCKET' && socket === null) {
token = data;
socket = new phoenix.Socket(FQDN, { token });
socketHandler.onOpen(() => listener.next({
channel: 'SOCKET_OPEN'
}));
} else {
if(channels[channel] === undefined) {
channels[channel] = new Channel(channel, { token });
}
channels[channel].join()
.receive('ok', () => {
sendData(data);
});
}
}
});
}
});
};
}
This would be the rough structure of such a driver. You see it waits for a message with the token and then opens the socket. It also keeps track of the open channels and sends/receives in those based on the category of the message. This method just requires that all channels have unique names, I am not sure how your channel protocol works in that regard or what you want in particular.
I hope this enough to get you started, if you clarify the API of the channel send/receive and the socket, I might be able to help more. You are also always welcome to ask questions in our gitter channel
I'm using the latest JavaScript API for PubNub, with presence switched on in the admin interface. I'm running two clients on a channel, initialized and subscribed like this:
pubnub = PUBNUB.init({
publish_key : 'xxx',
subscribe_key : 'xxx',
origin : 'pubsub.pubnub.com',
cipher_key : 'xxx',
ssl : 'true',
uuid : uuid
});
pubnub.subscribe({
channel : CHANNEL,
callback : function (message) {
$("#box").val($("#box").val() + message + "\r\n");
},
connect: function () { console.log("Connected"); users(); },
disconnect: function () { console.log("Disconnected"); },
reconnect: function () { console.log("Reconnected"); },
error: function () { console.log("Network Error"); },
presence: function (m) { console.log("Presence: " + m); }
});
I unsubscribe from the channel like this:
function unsubscribe() {
pubnub.unsubscribe({
channel: CHANNEL
});
console.log("Unsubscribed");
};
When clients join the channel, I see join presence actions in all clients that are subscribed. When clients timeout, I see those actions too.
When I call unsubscribe() the log line prints, and that client does not receive any more messages published on the channel, but there is no leave presence action received by other clients who are still subscribed. What am I doing wrong?
Strangely enough, when I tried to subscribe this morning I got a 400 Invalid error. When I investigated, the PubNub admin screen told me that there were no keys associated with my project.
When I had regenerated the keys I could subscribe - and the join and leave events worked as expected too. I now get the response I expect from here-now as well.
Odd, but fixed!
I am working with faye messaging system and I want to add authentication! I worked my way through the website and followed the tutorial.
On the Client I have an extension for outgoing Messages:
var droneFaye = new faye.Client("/faye", {
timeout: 120
});
var USER_TOKEN = 'ifd63cylqwsyaq9c2ptzywjujgtfpxs';
droneFaye.addExtension({
outgoing: function(message, callback) {
if (message.channel !== '/meta/subscribe')
return callback(message);
message.ext = message.ext || {};
message.ext.token = USER_TOKEN;
console.log(message);
callback(message);
}
});
On the Server:
var token = 'ifd63cylqwsyaq9c2ptzywjujgtfpxs'
var serverAuth = {
incoming: function(message, callback) {
// Let non-subscribe messages through
if (message.channel !== '/meta/subscribe')
return callback(message);
// Get subscribed channel and auth token
var msgToken = message.ext && message.ext.token;
// Find the right token for the channel
if (token !== msgToken) {
message.error = 'Invalid subscription auth token';
}
console.log(message);
callback(message);
}
};
var adapter = new faye.NodeAdapter({
mount:'/faye',
timeout:45
});
adapter.addExtension(serverAuth);
adapter.attach(httpServer);
I have a subscription on the server like this:
adapter.getClient().subscribe("/drone/move", function(cmd) {});
Alright! So when I start the server and there is NO CLIENT it already calls the extension for the subscriptions and it will output on the console:
{ channel: '/meta/subscribe',
clientId: '2isdeb0ds77zl0lh82ob1kqu29y1cajnv80',
subscription: '/drone/move',
id: '2',
error: 'Invalid subscription auth token' }
Once a Client connects to the server it will again call the extension and it will output this:
{ channel: '/meta/subscribe',
clientId: '3kechs0c7smpc05z5o7d0a8qcd301d8zi41',
subscription: '/drone/move',
id: '3',
ext: { userId: 18787, token: 'ifd63cylqwsyaq9c2ptzywjujgtfpxs' } }
So this looks good! But no other messages are going to arrive on the server even though they have the correct token and there is no error message!
Just for information. If you add a error key with a value to the message object it will not pass the message to its subscription...its supposed to be like that!..
Also when I comment out the message.error in the extension it works fine but of course there is not authentication.
So does anyone know why the server calls the extension even though there is no client and second why does faye not give the message to its subscription even though there is no error in the message object?
thx!
Question was also asked, and answered here:
This is caused by the call to adapter.getClient().subscribe().
[...] the server-side client (<adapter>.getClient()) returns a connection to the server, thus walks through the same extensions (incoming, outgoing) as the client does.
[...] Attach an outgoing extension and you'll see it being responded to the server-client.