Fatih kurt
Created November 30, 2016

Chatroulette

At home and bored? Ask Alexa to talk to a complete random stranger. Switch to someone else any time.

134
Chatroulette

Things used in this project

Hardware components

Echo Dot
Amazon Alexa Echo Dot
×2

Software apps and online services

AWS Lambda
Amazon Web Services AWS Lambda
Lambda function will be responsible from receiving events and matching users.
AWS DynamoDB
Amazon Web Services AWS DynamoDB
Since we are using Lambda instead of EC2, we will not have a live memory to keep matches. These matches and dialogs will be kept on DynamoDB

Story

Read more

Schematics

Chat Flow Diagram

Code

Chat Roulette Lambda code

JavaScript
'use strict';
const doc = require('dynamodb-doc');

const dynamo = new doc.DynamoDB();
const finish = 'finish';
/**
 * This sample demonstrates a simple skill built with the Amazon Alexa Skills Kit.
 * The Intent Schema, Custom Slots, and Sample Utterances for this skill, as well as
 * testing instructions are located at http://amzn.to/1LzFrj6
 *
 * For additional samples, visit the Alexa Skills Kit Getting Started guide at
 * http://amzn.to/1LGWsLG
 */


// --------------- 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,
    };
}

function buildResponse(sessionAttributes, speechletResponse) {
    return {
        version: '1.0',
        sessionAttributes,
        response: speechletResponse,
    };
}


// --------------- Functions that control the skill's behavior -----------------------

function getWelcomeResponse(intent, session, callback) {
    // If we wanted to initialize the session to have some attributes we could add those here.
    const sessionAttributes = {};
    const cardTitle = 'Welcome';
    const shouldEndSession = false;
    getAMatch(function(item){
        if(item !== undefined){
            var speechOutput = 'Welcome to the Chat roulette. You are now connected to a user. Say '+finish+' to exit chat. ';
            session.id = item.id;
            session.order = 2;
            speechOutput += item.chat1;
            item.chat1 = undefined;
            item.chat2 = intent.slots.chat.value;
            item.match = true;
            updateMatch(item,function(){});
            const repromptText = 'Tell something to your match! Ps. silence is boring.';
            callback(sessionAttributes,
            buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
        }
        else {
            createNewMatch(intent.slots.chat.value,function(item){
                session.id = item.id;
                session.order = 1;
                const repromptText = 'Now is a good time to check if you are matched to someone.';
            });
        }
        
        
    });
    
}


function chatSession(intent, session, callback) {
    const cardTitle = intent.slots.name;
    let repromptText = 'What are you waiting for? Tell something!';
    let sessionAttributes = {};
    let shouldEndSession = false;
    let speechOutput = '';
    var ckey = "chat"+(session.order==1?"2":"1");
    var skey = "chat"+session.order;
    getMatch(session.id,function(item){
        if(intent.slots.chat.value == finish){
            item.open = false;
            item.match = false;
            speechOutput = "Come back any time. Bye. ";
            shouldEndSession = false;
        }
        else if(item.open){
            item[skey]=(item[skey]!==undefined?intent.slots.chat.value:item[skey]+" "+ intent.slots.chat.value);
            if(item.match){
                var chat = item[ckey];
                if(chat){
                    speechOutput=chat;
                    item[ckey] = undefined;
                }
                else{
                    speechOutput = "ok.";
                }
            }
            else{
                speechOutput = "No match yet, sorry.";
                repromptText = "Wanna try check if you have a match?";
            }
        }
        else{
            speechOutput = "Your match has left. Come back any time.";
            shouldEndSession = false;
            item.match = false;
            item.open = false;
        }
        callback(sessionAttributes,
             buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
        updateMatch(item,function(){});
    });

}


// --------------- Events -----------------------

/**
 * 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(launchRequest, session, 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}`);
    const intent = intentRequest.intent;
    if(session.id){
        getWelcomeResponse(intent,session,callback);
    }
    else chatSession(intent,session,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}`);
    getMatch(session.id,function(item){
        if(item !== undefined){
            item.open = false;
            saveMatch(item);
        }
    });
}


// --------------- Main handler -----------------------

// Route the incoming request based on type (LaunchRequest, IntentRequest,
// etc.) The JSON body of the request is provided in the event parameter.
exports.handler = (event, context, callback) => {
    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.
         */
        /*
        if (event.session.application.applicationId !== 'amzn1.echo-sdk-ams.app.[unique-value-here]') {
             callback('Invalid Application ID');
        }
        */

        if (event.session.new) {
            onSessionStarted({ requestId: event.request.requestId }, event.session);
        }

        if (event.request.type === 'LaunchRequest') {
            onLaunch(event.request,
                event.session,
                (sessionAttributes, speechletResponse) => {
                    callback(null, buildResponse(sessionAttributes, speechletResponse));
                });
        } else if (event.request.type === 'IntentRequest') {
            onIntent(event.request,
                event.session,
                (sessionAttributes, speechletResponse) => {
                    callback(null, buildResponse(sessionAttributes, speechletResponse));
                });
        } else if (event.request.type === 'SessionEndedRequest') {
            onSessionEnded(event.request, event.session);
            callback();
        }
    } catch (err) {
        callback(err);
    }
};

/* 
** DynamoDB Integration
*/
function getMatch(id,callback){
    dynamo.query({
                TableName: 'MatchTable',
                KeyConditionExpression: "#id = :id",
                ExpressionAttributeNames:{
                    "#id": "id"
                },
                ExpressionAttributeValues: {
                    ":id":id
                }
            }, function(err, data) {
                if (err) {
                    console.error("Unable to query. Error:", JSON.stringify(err, null, 2));
                } else {
                    callback(data.Items[0]);
                }
            });
}
function getAMatch(callback){
    dynamo.query({
                TableName: 'MatchTable',
                KeyConditionExpression: "#open = :open and #match = :match",
                ExpressionAttributeNames:{
                    "#open": "open",
                    "#match":"match"
                },
                ExpressionAttributeValues: {
                    ":open":"true",
                    ":match":"true"
                }
            }, function(err, data) {
                if (err) {
                    console.error("Unable to query. Error:", JSON.stringify(err, null, 2));
                } else {
                    callback(data.Items[0]);
                }
            });
}
function saveMatch(obj,callback){
    console.error("{save:obj}");
    dynamo.putItem(obj, callback);
}
function updateMatch(obj,callback){
    dynamo.updateItem(obj, callback);
}
function createNewMatch(chat1, callback){
    var params = {
        TableName:"MatchTable",
        Item:{
            "chat1": chat1,
            "open": true,
            "match":false
        }
    };
    saveMatch(params,callback);
}

Credits

Fatih kurt
1 project • 0 followers
find me on https://www.linkedin.com/in/fatihkurt
Contact

Comments

Please log in or sign up to comment.