This is specific to the Opentok API. I'm quite new to javascript and struggling to get this all to work nicely.
Here's a link to the docs if you need it:
https://tokbox.com/developer/guides/
My error is:
signal error (500): Unable to send signal - you are not connected to the session.
My html file looks like this:
var apiKey = 45317102;
var sessionID = "#{#session.session_id}";
if (OT.checkSystemRequirements() == 1) {
var session = OT.initSession(apiKey, sessionID);
} else {
// the client does not support WebRTC
console.log('does not support WebRTC');
}
var token = "#{#token.token}";
var publisher;
var publisherOptions = {
width: 600,
height: 400,
name: 'Publisher',
// publishAudio: true,
// publishVideo: false
}
var subscriberOptions = {
width: 300,
height: 200,
name: 'Subscriber'
}
// for publisher
session.connect(token, function(error) {
if (error) {
console.log(error.message);
} else {
session.publish('myPublisherDiv', publisherOptions);
console.log('Publishing a stream.');
}
});
// for subscribers
session.on({
streamCreated: function(event) {
session.subscribe(event.stream, 'subscribersDiv', subscriberOptions);
console.log("New stream in the session: " + event.stream.streamId);
}
});
// check if the user is a moderator
if (session.capabilities.forceDisconnect == 1) {
console.log('you can moderate.')
} else {
console.log('you cant moderate');
}
session.signal(
{
data:"hello"
},
function(error) {
if (error) {
console.log("signal error ("
+ error.code
+ "): " + error.message);
} else {
console.log("signal sent.");
}
}
);
and my backend (ruby / rails) controller looks like this:
def create
session = OPENTOK.create_session media_mode: :routed
session_id = session.session_id
#session = Session.new(session_id: session_id)
if #session.save
redirect_to #session
else
render 'new'
end
end
def show
#session = Session.find(params[:id])
unless params[:id].blank?
s = Session.find(params[:id])
token = OPENTOK.generate_token s.session_id
#token = Token.create(session_id: s.id, token: token, active: true)
#token.save
end
end
now I'm not able to make the person who creates the session a moderator (ability to remove others) and it seems like I'm in-fact even unable to connect to the session. Even though when I run it I'm able to have 2 people in the same session at the same time.
Does anybody know what's happening (or more likely what I'm missing from my code)?
Related
Anytime I call the session.disconnect() method to remove clients from the session, I get this warning: "OpenTok:Publisher:warn Received connectivity event: "Cancel" without "Attempt"
and this error: "OpenTok:Subscriber:error Invalid state transition: Event 'disconnect' not possible in state 'disconnected'"
Could someone please explain to me what that error means? Thanks in advance.
// Initialize the session
var session = OT.initSession(data['apikey'], data['session_id']);
console.log(session);
// Initialize the publisher for the recipient
var publisherProperties = {insertMode: "append", width: '100%', height: '100%'};
var publisher = OT.initPublisher('publisher', publisherProperties, function (error) {
if (error) {
console.log(`Couldn't initialize the publisher: ${error}`);
} else {
console.log("Receiver publisher initialized.");
}
});
$('#session-modal').modal("show");
// Detect when new streams are created and subscribe to them.
session.on("streamCreated", function (event) {
console.log("New stream in the session");
var subscriberProperties = {insertMode: 'append', width: '100%', height: '100%'};
var subscriber = session.subscribe(event.stream, 'subscriber', subscriberProperties, function(error) {
if (error) {
console.log(`Couldn't subscribe to the stream: ${error}`);
} else {
console.log("Receiver subscribed to the sender's stream");
}
});
});
//When a stream you publish leaves a session, the Publisher object dispatches a streamDestroyed event:
publisher.on("streamDestroyed", function (event) {
console.log("The publisher stopped streaming. Reason: "
+ event.reason);
});
//When a stream, other than your own, leaves a session, the Session object dispatches a streamDestroyed event:
session.on("streamDestroyed", function (event) {
console.log("Stream stopped. Reason: " + event.reason);
session.disconnect();
console.log("called session.disconnect().");
});
session.on({
connectionCreated: function (event) {
connectionCount++;
if (event.connection.connectionId != session.connection.connectionId) {
console.log(`Another client connected. ${connectionCount} total.`);
}
},
connectionDestroyed: function connectionDestroyedHandler(event) {
connectionCount--;
console.log(`A client disconnected. ${connectionCount} total.`);
}
});
// Connect to the session
// If the connection is successful, publish an audio-video stream.
session.connect(data['token'], function(error) {
if (error) {
console.log("Error connecting to the session:", error.name, error.message);
} else {
console.log("Connected to the session.");
session.publish(publisher, function(error) {
if (error) {
console.log(`couldn't publish to the session: ${error}`);
} else {
console.log("The receiver is publishing a stream");
}
});
}
});
// Stop the publisher from streaming to the session if the user dismiss the modal
const stopSession = document.getElementById('stop-session');
stopSession.addEventListener("click", (event) => {
event.preventDefault();
session.disconnect();
});
I see this is kind of old, but wanted to share my solution to avoid this error. I'm not sure what the error means, but I call publisher.destroy() before calling session.disconnect() to avoid the error.
openTokPublisher.destroy();
openTokSession.disconnect();
I highly doubt you can disconnect the client with JavaScript. Here what I did.
// Connect to the session
session.connect(token, function connectCallback(error) {
// Get the connectionId
connectionId = session.connection.connectionId;
and use one of their SDK on the backend
https://tokbox.com/developer/sdks/server/
// Disconnect session
function disconnectSession() { // eslint-disable-line no-unused-vars
if (sessionId && connectionId) {
$.ajax({
url: '/OpenTok/DisconnectSession',
type: 'POST',
data: 'sessionId=' + sessionId + '&connectionId=' + connectionId,
});
}
}
I'm trying to enable sound for my Firebase push notifications and I'm not sure if there is code in the App Delegate which I need to implement, or if the code in my index.js is wrong.
I thought there was something I needed to import in AppDelegate related to sound, but all the guides I've found for implementing push notifications only have the basic code where [options] contains the only thing related to the notification's sound.
index.js Code:
var notification = {
notification: {
title: conversation.conversationName,
body: user.username + ': ' + message.text,
sound: 'default'
},
topic: topic
}
App Delegate Code: Function called in didFinishLaunchingWithOptions.
import UIKit
import Firebase
import UserNotifications
private func attemptRegisterForNotifications(application: UIApplication) {
Messaging.messaging().delegate = self
UNUserNotificationCenter.current().delegate = self
let options: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
if settings.authorizationStatus == .authorized {
// Push notifications already authorized, do nothing
print ("push notifications authorized")
} else if settings.authorizationStatus == .notDetermined {
// User hasn't specified notification status
UNUserNotificationCenter.current().requestAuthorization(options: options, completionHandler: { (granted, error) in
if let error = error {
print ("Failed to request authorization:", error)
return
}
guard granted else {return}
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
})
} else if settings.authorizationStatus == .denied {
// User has denied notifications
UNUserNotificationCenter.current().requestAuthorization(options: options, completionHandler: { (granted, error) in
if let error = error {
print ("Failed to request authorization:", error)
return
}
let alertController = UIAlertController(title: "Enable Push Notifications", message: "Enable push notifications for optimal chat experience", preferredStyle: .alert)
let settingsAction = UIAlertAction(title: "Settings", style: .default) { (_) -> Void in
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
})
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alertController.addAction(cancelAction)
alertController.addAction(settingsAction)
alertController.preferredAction = settingsAction
DispatchQueue.main.async {
self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
}
})
}
}
}
Relevant Info about Environment
Rails Version: 5.2
Devise Authentication
Code
Warden hooks for setting cookies
# config/initializers/warden_hooks.rb
Warden::Manager.after_set_user do |user,auth,opts|
scope = opts[:scope]
auth.cookies.signed["#{scope}.id"] = user.id
auth.cookies.signed["#{scope}.expires_at"] = 30.minutes.from_now
end
Warden::Manager.before_logout do |user, auth, opts|
scope = opts[:scope]
auth.cookies.signed["#{scope}.id"] = nil
auth.cookies.signed["#{scope}.expires_at"] = nil
end
Connection parent class for authenticating. This works as it should and the connection is logged.
# application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
logger.add_tags 'ActionCable', current_user.email
end
protected
USER_CLASSES = ['admin', 'agent', 'tech', 'client']
def find_verified_user
user_class = USER_CLASSES.find { |klass| cookies.signed["#{klass}.id"] }
user_id = cookies.signed["#{user_class}.id"]
verified_user = User.find_by(id: user_id)
if verified_user && cookies.signed["#{user_class}.expires_at"] > Time.now
verified_user
else
reject_unauthorized_connection
end
end
end
end
Client side code for setting up connection and sending messages.
// channels/conversations.js
$(document).on('turbolinks:load', function() {
var messages = $('#messages');
var projectId = messages.data('project-id');
var button = $('#new_message input[type=submit]');
var subscription = { channel: "ConversationsChannel", id: projectId };
App.conversation = App.cable.subscriptions.create(subscription, {
connected: function() {
console.log('Upgraded to websocket connection.');
},
disconnected: function() {
console.log('Websocket not connected.');
},
received: function(data) {
messages.append(data['message']);
},
sendMessage: function(message, project_id) {
return this.perform('send_message', {
message: message,
project_id: projectId
});
}
});
$('#message_body').on('keyup', function(e) {
var message = e.target.value;
if ($.trim(message).length > 0) {
button.prop('disabled', false);
} else {
button.prop('disabled', true);
}
});
$('#new_message').submit(function(e) {
e.preventDefault();
var textarea = $('#message_body');
App.conversation.sendMessage(textarea.val(), projectId);
textarea.val('');
button.prop('disabled', true);
});
});
Problem
The connection is registered on the server
but the state of the ActionCable.Connection on the client is never set to open, which causes the Connection.send method to fail because this.isOpen() returns false (so the client is never subscribed to the channel) - ActionCable source
If I put a break point in chrome inside send and pass the data to the webSocket.send method the subscription is successful.
I have to repeat that again if I want to send a message, as the state of the connection is still not set to open even after successfully establishing the subscription.
What I've Already Tried
Authenticating everyone
Returning the current_user in Connection#connect
Removing all code from the Connection class so no behavior is overridden
I have simple nodejs app with sockets and I've faced an error where I can't find any solution. So I'm emiting from app to client and nothing happens there. Or client can't receive it - I don't know, because I can't check if it was successfully emited to client. This is the error I got when I tried to debug callback of emit:
Error: Callbacks are not supported when broadcasting
This my app code:
http.listen(6060, function () {
console.log("Listening on *: 6060");
});
io.set('authorization', function (handshakeData, accept) {
var domain = handshakeData.headers.referer.replace('http://', '').replace('https://', '').split(/[/?#]/)[0];
if ('***' == domain) {
accept(null, true);
} else {
return accept('You must be logged in to take an action in this site!', false);
}
});
io.use(function (sock, next) {
var handshakeData = sock.request;
var userToken = handshakeData._query.key;
if (typeof userToken !== null && userToken !== 0 && userToken !== '0' && userToken.length > 0) {
connection.query('***',
[xssfilter.filter(validator.escape(userToken))],
function (error, data) {
if (error) {
debug('Cant receive user data from database by token');
next(new Error('Failed to parse user data! Please login!'));
} else {
// load data to this user.
_updateUsers(xssfilter.filter(validator.escape(userToken)), 'add', data[0], sock.id);
_loadPreData();
next(null, true);
}
});
} else {
debug('Cant receive user token');
next(new Error('Failed to parse user data! Please login!'));
}
sock.on("disconnect", function () {
_updateUsers(false, 'remove', false, sock.id);
});
});
// we need to show people online count
io.emit('online-count', {
count: Object.keys(connectedUsers).length
});
And the function used above:
function _updateUsers(userToken, action, userData, sockedID) {
switch (action) {
case 'add':
connectedUsers[sockedID] = {...};
io.emit('online-count', io.emit('online-count', {
count: Object.keys(connectedUsers).length
}););
break;
case 'remove':
delete connectedUsers[sockedID];
io.emit('online-count', io.emit('online-count', {
count: Object.keys(connectedUsers).length
}););
break;
}
}
so after emiting online-count I should accept it on the client side as I'm doing it:
var socket;
socket = io(globalData.socketConn, {query: "key=" + globalData.userData.token});
socket.on('connect', function (data) {
console.log('Client side successfully connected with APP.');
});
socket.on('error', function (err) {
error('danger', 'top', err);
});
socket.on('online-count', function (data) {
console.log('Got online count: ' + data.count);
$('#online_count').html(data.count);
});
but the problem is with this online-count.. Nothing happens and it seems that it's not was even sent from node app. Any suggestions?
The problem was with my logic - I was sending online count only if new user were connecting/disconnecting. Problem were solved by adding function to repeat itself every few seconds and send online count to client side.
I am trying to write the Lambda function for an Amazon Alexa skill in NodeJS. Alexa is that cylindrical speaker that responds to your voice, and a "skill" is basically a voice app for it. This function takes the JSON input from the Alexa device and creates a response, then sends the new JSON back to the device for output.
This code is supposed to pull the Bitcoin to USD exchange rate from the BTC-e JSON, extract the 'avg' value, and output it to the Alexa device.
I haven't done any coding in a long time, so please forgive any stupid errors. I took the example code and tried to modify it for my purposes, but I get this error when executing in AWS:
{
"errorMessage": "Unexpected identifier",
"errorType": "SyntaxError",
"stackTrace": [
"Module._compile (module.js:439:25)",
"Object.Module._extensions..js (module.js:474:10)",
"Module.load (module.js:356:32)",
"Function.Module._load (module.js:312:12)",
"Module.require (module.js:364:17)",
"require (module.js:380:17)"
]
}
My code is here. I have a feeling the problem is somewhere in lines 84-106 because that's where I did most of my work.
Thanks for any assistance!
The code below should work, just un-comment the todo I added. You had a couple of missing braces and I switched to a different http request lib that is included with AWS lambda by default. I also change the flow to make it simpler for me to test, so now it doesn't re-prompt it just tells you the latest bit coin price.
var https = require('https');
// Route the incoming request based on type (LaunchRequest, IntentRequest,
// etc.) The JSON body of the request is provided in the event parameter.
exports.handler = function (event, context) {
try {
console.log("event.session.application.applicationId=" + event.session.application.applicationId);
/**
* Uncomment this if statement and populate with your skill's application ID to
* prevent someone else from configuring a skill that sends requests to this function.
*/
// TODO add this back in for your app
// if (event.session.application.applicationId !== "amzn1.echo-sdk-ams.app.") {
// context.fail("Invalid Application ID");
// }
if (event.session.new) {
onSessionStarted({requestId: event.request.requestId}, event.session);
}
if (event.request.type === "LaunchRequest") {
onLaunch(event.request,
event.session,
function callback(sessionAttributes, speechletResponse) {
context.succeed(buildResponse(sessionAttributes, speechletResponse));
});
} else if (event.request.type === "IntentRequest") {
onIntent(event.request,
event.session,
function callback(sessionAttributes, speechletResponse) {
context.succeed(buildResponse(sessionAttributes, speechletResponse));
});
} else if (event.request.type === "SessionEndedRequest") {
onSessionEnded(event.request, event.session);
context.succeed();
}
} catch (e) {
context.fail("Exception: " + e);
}
};
/**
* Called when the session starts.
*/
function onSessionStarted(sessionStartedRequest, session) {
console.log("onSessionStarted requestId=" + sessionStartedRequest.requestId
+ ", sessionId=" + session.sessionId);
}
/**
* Called when the user launches the skill without specifying what they want.
*/
function onLaunch(launchRequest, session, callback) {
console.log("onLaunch requestId=" + launchRequest.requestId
+ ", sessionId=" + session.sessionId);
// Dispatch to your skill's launch.
getWelcomeResponse(callback);
}
/**
* Called when the user specifies an intent for this skill.
*/
function onIntent(intentRequest, session, callback) {
console.log("onIntent requestId=" + intentRequest.requestId
+ ", sessionId=" + session.sessionId);
getWelcomeResponse(callback);
}
/**
* Called when the user ends the session.
* Is not called when the skill returns shouldEndSession=true.
*/
function onSessionEnded(sessionEndedRequest, session) {
console.log("onSessionEnded requestId=" + sessionEndedRequest.requestId
+ ", sessionId=" + session.sessionId);
// Add cleanup logic here
}
// --------------- Functions that control the skill's behavior -----------------------
function getWelcomeResponse(callback) {
// If we wanted to initialize the session to have some attributes we could add those here.
var sessionAttributes = {};
var cardTitle = "Bitcoin Price";
var speechOutput = "Welcome to the Bitcoin Price skill, "
var repromptText = ''
var shouldEndSession = true;
var options = {
host: 'btc-e.com',
port: 443,
path: '/api/2/btc_usd/ticker',
method: 'GET'
};
var req = https.request(options, function(res) {
var body = '';
console.log('Status:', res.statusCode);
console.log('Headers:', JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
console.log('Successfully processed HTTPS response');
body = JSON.parse(body);
console.log(body);
console.log('last price = ' + body.ticker.last);
speechOutput += "The last price for bitcoin was : " + body.ticker.last;
callback(sessionAttributes,
buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
});
});
req.end();
}
// --------------- Helpers that build all of the responses -----------------------
function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
return {
outputSpeech: {
type: "PlainText",
text: output
},
card: {
type: "Simple",
title: "SessionSpeechlet - " + title,
content: "SessionSpeechlet - " + output
},
reprompt: {
outputSpeech: {
type: "PlainText",
text: repromptText
}
},
shouldEndSession: shouldEndSession
}
}
function buildResponse(sessionAttributes, speechletResponse) {
return {
version: "1.0",
sessionAttributes: sessionAttributes,
response: speechletResponse
}
}