Skip to content

mathiasconradt/nextcloud-notes-alexa-skill

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 

Repository files navigation

Nextcloud Notes Alexa Skill

aws-lambda-function-code.png

This skill has three intents:

  • about: gives an introduction to the skill / Nextcloud Notes.
  • read notes: reads out all of your notes in Nextcloud.
  • take a note: take a new note and stores it in Nextcloud.

The way to let Alexa read all notes, you would ask:

** "Alexa, ask Nextcloud to read my notes" **

If you want to take a new note, you would ask:

** "Alexa, ask Nextcloud to take a note: {your note here}" **

Note that Alexa is not (yet) actually intended to be used for free-text / transcription like this (according to the Alexa evangelists that were at the workshop that I joined). So, not all words might be recognized 100% correctly.

The following should be noted:

  • This skill is just a quick & dirty code result of an Amazon Skills workshop I participated in, because I was curious about the status, possibilities and limitations of Alexa. It is not intended to be used in production.

  • in this example, you put your Nextcloud password hard-coded (though encoded as basic auth header) into the Lamda function. I use https://www.blitter.se/utils/basic-authentication-header-generator/ for that. It's not the recommended way. In a real production skill / service, you should use "account linking" together with OAuth.

  • the Nextcloud endpoint / url is currently hard-coded as well.

Backend: Lambda function

Setup

  • create an AWS account https://aws.amazon.com
  • go to AWS Management Console at https://console.aws.amazon.com/console/home
  • select a location, i.e. Ireland (EU), but any (most) other will do - just make sure that Amazon Skills Set is available there, because it's not available in all regions yet.
  • search for Lambda in the AWS management console and select it
  • click the 'Create function' button in the upper right
  • filter blueprints and select alexa-skill-kit-sdk-factskill

aws-services-lambda.png

aws-lambda-functions.png

aws-lambda-blueprints.png

Basic Information

  • Name: nextcloudNotes
  • Role: if you don't have it yet, create a new default role, which will automatically be lambda_basic_execution
  • leave the code as is, you will edit it later (right now, it's not even editable)
  • click "create function" button
  • after saving, copy/paste and note down the ARN somewhere, i.e. in my case arn:aws:lambda:eu-west-1:992674152027:function:nextcloudNotes (adjust it to your own ARN !)

aws-lambda-function-code.png

Configuration

  • copy/paste the source code as below (or from src/nextcloud-notes-lambda-function.js) into the function source textarea

    // 1. Text strings =====================================================================================================
    //    Modify these strings and messages to change the behavior of your Lambda function
    
    const languageStrings = {
        'en': {
            'translation': {
                'WELCOME' : "Welcome to Nextcloud Notes!",
                'HELP'    : "Say read my notes to hear all your notes or say take a note to let Alexa take a new note in Nextcloud for you. ",
                'ABOUT'   : "Nextcloud Notes allows you to take notes and keep them safe and private.",
                'STOP'    : "Okay, see you next time!"
            }
        }
    };
    
    const SKILL_NAME = "Nextcloud Notes";
    
    // 2. Skill Code =======================================================================================================
    
    const Alexa = require('alexa-sdk');
    
    exports.handler = function(event, context, callback) {
        var alexa = Alexa.handler(event, context);
        alexa.resources = languageStrings;
        alexa.registerHandlers(handlers);
        alexa.execute();
    };
    
    const handlers = {
        'LaunchRequest': function () {
            var say = this.t('WELCOME') + ' ' + this.t('HELP');
            this.response.speak(say).listen(say);
            this.emit(':responseReady');
        },
    
        'AboutIntent': function () {
            this.response.speak(this.t('ABOUT'));
            this.emit(':responseReady');
        },
    
        'CreateNoteIntent': function () {
            var noteToTake = '';
            if (this.event.request.intent.slots.content.value) {
                noteToTake = this.event.request.intent.slots.content.value;
            }
            createNote(noteToTake, ( content ) => {
                var say = content;
                this.response.speak(say);
                this.emit(':responseReady');
            });
        },
    
        'GetNotesIntent': function () {
            getNotes( ( content ) => {
                var say = content;
                this.response.speak(say);
                this.emit(':responseReady');
            });
        },
    
        'AMAZON.YesIntent': function () {
            this.response.speak(this.t('ABOUT'));
            this.emit(':responseReady');
        },
        'AMAZON.NoIntent': function () {
            this.emit('AMAZON.StopIntent');
        },
        'AMAZON.HelpIntent': function () {
            this.response.speak(this.t('HELP')).listen(this.t('HELP'));
            this.emit(':responseReady');
        },
        'AMAZON.CancelIntent': function () {
            this.response.speak(this.t('STOP'));
            this.emit(':responseReady');
        },
        'AMAZON.StopIntent': function () {
            this.emit('SessionEndedRequest');
        },
        'SessionEndedRequest': function () {
            this.response.speak(this.t('STOP'));
            this.emit(':responseReady');
        }
    
    };
    
    //    END of Intent Handlers {} ========================================================================================
    // 3. Helper Function  =================================================================================================
    
    function createNote(note, callback) {
    
      var http = require('http');
      var fs = require('fs');
    
      // Build the post string from an object
      var post_data = JSON.stringify({content: note});
    
      // An object of options to indicate where to post to
      var post_options = {
          host: 'www.mynextcloud.com',
          port: '80', // or better: 443 if you use https
          path: '/index.php/apps/notes/api/v0.2/notes',
          method: 'POST',
          headers: {
              'Authorization': 'Basic YWRtaW46YWRtaW4=', // TODO adjust the Basic Auth credentials here. Note: hard-coding them here is definitely NOT best-practice though!
              'Content-Type': 'application/json',
              'Accept': 'application/json',
              'Content-Length': Buffer.byteLength(post_data)
          }
      };
    
      // Set up the request
      var post_req = http.request(post_options, function(res) {
          res.setEncoding('utf8');
          // TODO MC one of these listeners is not needed.
          res.on('data', function (chunk) {
              callback('Note creation successful.');
          });
          res.on('end', function (chunk) {
              callback('Note creation successful.');
          });
          res.on('error', function (chunk) {
              callback('Note creation failed.');
          });
      });
    
      // post the data
      post_req.write(post_data);
      post_req.end();
    }
    
    function getNotes(callback) {
        var http = require('http');
        // TODO adjust the Basic Auth credentials here. Note: hard-coding them here is definitely NOT best-practice though!
        var req = http.request("http://admin:admin@www.mynextcloud.com/index.php/apps/notes/api/v0.2/notes", res => {
            res.setEncoding('utf8');
            var returnData = "";
            res.on('data', chunk => {
                returnData = returnData + chunk;
            });
            res.on('end', () => {
                var noteObj = JSON.parse(returnData);
                var content = '';
                for (var i=0; i<noteObj.length; i++) {
                    content += 'Note ' + (i+1) + ': ' + noteObj[i].content + '.';
                }
                callback(content);
            });
        });
        req.end();
    }
  • adjust Nextcloud endpoint URL and username / password. I use https://www.blitter.se/utils/basic-authentication-header-generator/ for that.

  • click the save button

aws-lambda-function-code.png

Triggers

On the triggers tab, select Alexa:

  • Click on + Add trigger
  • click on the empty icon
  • select Alexa Skills Set and submit

You're done with the backend.

aws-lambda-trigger.png

aws-lambda-trigger-add.png

aws-lambda-trigger-add-select.png

Frontend: Skill configuration

Setup

dev-alexa.png

dev-alexa-skills.png

For the skill you are going to create, set the following configuration:

Skill Information

skill-information.png

  • Skill type: Custom
  • Select Language: English (U.S.)
  • Name: Nextcloud
  • Invocation Name: nextcloud

Interaction model

skill-interaction-model.png

Copy/paste below JSON (or from [src/nextcloud-notes-interaction-model.json](src/nextcloud-notes-interaction-model.json)) and save, then build.

```javascript
{
  "intents": [
    {
      "name": "AboutIntent",
      "samples": [
        "about"
      ],
      "slots": []
    },
    {
      "name": "AMAZON.CancelIntent",
      "samples": []
    },
    {
      "name": "AMAZON.HelpIntent",
      "samples": []
    },
    {
      "name": "AMAZON.StopIntent",
      "samples": []
    },
    {
      "name": "CreateNoteIntent",
      "samples": [
        "take a note {content}",
        "create a note {content}",
        "create a new note {content}",
        "take a new note {content}"
      ],
      "slots": [
        {
          "name": "content",
          "type": "AMAZON.Actor",
          "samples": []
        }
      ]
    },
    {
      "name": "GetNotesIntent",
      "samples": [
        "read my notes",
        "read all notes",
        "get my notes",
        "get all notes"
      ],
      "slots": []
    }
  ]
}
```
  • Leave the rest as default

Configuration

skill-configuration.png

  • Service Endpoint Type: AWS Lambda ARN

    • enter the ARN from the backend / AWS lambda function that you noted down earlier
  • Leave the rest as default:

    • Provide geographical region endpoints? no
    • Accounting linking: no

Test your skill

First, you can test your new skill in the Test section of the skill page by typing a command ("utterance"), which is what you would usually speak to Alexa. You could enter something like read my notes. You don't need to enter "Alexa, ask Nextcloud..." here, but only the intent.

skill-test.png

skill-test-2.png

A better way to test with real voice is to use EchoSim http://echosim.io/: login with your Amazon Developer Account and test, for example:

"Alexa, ask Nextcloud to read my notes."

"Alexa, ask Nextcloud to take a note: Buy milk"

About

Alexa Skill to read notes and take new notes in Nextcloud

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published