Skip to main content

Oracle Digital Assistant: Hooking up your chatbot to twitter.

In my previous post I described how to use node-red to interact with twitter. That post was based on some initial research which was eventually used as a basis for the post that you are reading now. For one of our projects I had to setup an Oracle Chatbot on twitter. Now as you might know, Oracle Digital Assistant supports many channels, such as web, facebook, android and iOS, but twitter is not supported. The way to expose a chatbot to a 'not supported' channel is to use a channel of the webhook type.

In this post I describe how I created a working prototype that exposes an Oracle Digital Assistant to Twitter.
The things you need for this are the following:
  1. Twitter application which is needed to use the Twitter API's to interact with twitter
  2. A nodejs application where you code the functionality
  3. A Digital Assistant instance to hold the Bot
  4. Installation of ngrok to test from your local machine

Create a twitter application

If you have an approved twitter developer account (To apply for a developer account, go to http://developer.twitter.com ) you should be able to create a new app in the 'my apps page' (https://developer.twitter.com/en/apps).  Once created, you are provided with the API keys and access tokens that you need to use in the app that you are building, in this case the a nodejs application that we are going to use to listen for tweets, send tweets to our chatbot and to tweet the replies from our chatbot.
With these keys in place, we are ready to go to the next step.

Create a nodejs application to implement the functionality

As a basis for the implementation I used Marcelo Jabali solution for an SMS chatbot described here: https://marcelojabali.blogspot.com/2017/12/using-sms-channels-with-oracle.html
Based on this code, we need to add just a couple of changes to use twitter instead of SMS.
Lets see the twitter specific code:
First of all we need get the node Twitter package from npm, as this is what will be used to interact with twitter. Navigate to the root folder of your code and install the twitter package:

With that in place we can use that package to read from twitter and post to twitter.
The first step is to make sure that we can call the API's. This is where the previously created API keys and tokens are needed.

Next it is time to write out the javascript code that reads from twitter and sends the tweets I need to the Digital Assistant, so it can come up with a reply.

I created a function that reads the twitter stream. Note that I am not using webhooks from twitter, but simply read the twitter stream. I use the tweet ID as identifier (which in fact it is) and make sure that any reply from the bot is sent as a reply to that specific tweet (using 'in_reply_to_status_id').
This actually means that as soon as the node server is started, it starts processing the stream.

In the code below you see that it uses two 'metadata' properties; the channelUrl and the channelSecretKey. Those values are actually provided by Digital Assistant when we configure the webhook channel (see below: "Create a webhook channel for our Digital Assistant")


For every tweet that is found (tweets containing keyword 'thishashtag') the messageToBotWithProperties function from the wehookUtil package is invoked. I use this package 'as is' from the previously mentioned SMS sample. The channelURL and the channelSecretKey are values provided by Digital Assistant Cloud when we configure the webhook channel at Digital Assistant Cloud.

Then I created a function that can be called by the Digital Assistants webhook channel to sends its replies. These are then forwarded to a function sendMessage() which actually replies to the tweet with whatever answer that chatbot gave us.



Setup ngrok

You can download and run ngrok on your machine and provide it the port of a network service, usually a web server. All details on how to do the installation and setup can be found here: https://ngrok.com/download

It connects to the ngrok cloud service which accepts traffic on a public address and relays that traffic through to the ngrok process running on your machine and then on to the local address you specified.
Our nodeserver that runs our twitter channel runs on port 3000. Thus when we go to the commandline and enter ngrok 3000, we will be provided with an ngrok URL.
This URL can be used to access the webhook on our local node server from Digital Assistant.

Create a webhook channel for our Digital Assistant

To use the Oracle Digital Assistant with our nodeJS program we need to set up a generic Webhook channel configuration. On our Bot Settings page, navigate to Channels and click the ‘+ Channel’ to start the configuration of the webhook. Provide a name, description, select Webhook as the Channel Type. For platform we will use version 1.0 for now. Finally provide the Outgoing Webhook URI (this is where the Bot will make HTTP requests to our Node.js server) and make sure to enable the Channel. As our node server is running locally, the outgoing webhook URI is pointing to the ngrok URL 
After finishing the configuration, click Create. The Webhook URL for our bot is generated and also you get a Secret Key for encrypting messages. These two values will be needed later when we start to configure our node code.



Now copy the Secret Key and the webhook URL and put them in our node code, so we can actually use the webhook channel:

The final result

If you enter a tweet like "I want to order some pizza' containing also our magic word, the pizzabot will be initialised, and ask you your age. From there you can continue the conversation all the way until your pizza is on its way, just as can be seen in the conversation below:

Note about this implementation

This functionality described in this post is a working prototype. However this is not the way we are using this in production. I know I did not disclose all details of the described implementation, nor will I disclose the details of our final implementation, but I will give you some hints. You should be able to figure this out. When streaming the twitter data, you will get all tweets that are posted on twitter, which in our case is much more then we actually need. That is why for production we implemented some extra restrictions on how many tweets we want to search for (using Twitter search instead of Twitter stream), how many tweets are processed (using the count parameter of the twitter search API) and how often the search is executed (using a custom scheduler that calls out to the tweet webhook). One more tweak was that instead of having to use the #thishashtag in all tweets that we want to process, we changed the implementation in such way that we are able to intercept only those tweets that contain a the keywords that where defined specifically for our use-case.

Resources

For this implementation I looked at the SMS example by Marcelo Jabali which is posted here: https://marcelojabali.blogspot.com/2017/12/using-sms-channels-with-oracle.html
Also the twitter API documentation was very helpful: https://developer.twitter.com/en/docs/basics/getting-started
And finally the documentation of the node twitter package: https://www.npmjs.com/package/twitter

Comments

Popular posts from this blog

ADF 12.1.3 : Implementing Default Table Filter Values

In one of my projects I ran into a requirement where the end user needs to be presented with default values in the table filters. This sounds like it is a common requirement, which is easy to implement. However it proved to be not so common, as it is not in the documentation nor are there any Blogpost to be found that talk about this feature. In this blogpost I describe how to implement this.

The Use Case Explained
Users of the application would typically enter today's date in a table filter in order to get all data that is valid for today. They do this each and every time. In order to facilitate them I want to have the table filter pre-filled with today's date (at the moment of writing July 31st 2015).


So whenever the page is displayed, it should display 'today' in the table filter and execute the query accordingly. The problem is to get the value in the filter without the user typing it. Lets first take a look at how the ADF Search and Filters are implemented by the f…

ADF 12.2.x : Conditional Showing Message Instead of List of Values Popup

Today I had to fix an issue in one of our ADF applications. For those of you working with ADF (as I do too occasionally) this might be valuable, so I decided to share this. The application is based on an old Oracle Forms applications and I had to implement the following functionality:

FRM-41830 : List of Values contains no entries.

In other words (more ADF like) : If you are about to render a List of Values and that List of Values contains no rows, just show a message instead of showing the List of Values.

In this post I will describe how I was able to implement this although it turned out to be a challenge.
Below are the steps that I took. Note that the Application can be downloaded from GIT.

1) I use a UI pattern where we use readonly table and do edits in a popup. That means that any List of Values will add an additional popup to the UI.



2) The List of Values that I use in this sample only shows entries where the minimum salary is less then the employees current salary. Not that …

ADF 11g Quicky 3 : Adding Error, Info and Warning messages

How can we add a message programatically ? Last week I got this question for the second time in a months time. I decided to write a short blogpost on how this works.

Adding messages is very easy, you just need to know how it works.
You can add a message to your faces context by creating a new FacesMessage. Set the severity (ERROR, WARNING, INFO or FATAL ), set the message text, and if nessecary a message detail. The fragment below shows the code for an ERROR message.

1: public void setMessagesErr(ActionEvent actionEvent) {
2: String msg = "This is a message";
3: AdfFacesContext adfFacesContext = null;
4: adfFacesContext = AdfFacesContext.getCurrentInstance();
5: FacesContext ctx = FacesContext.getCurrentInstance();
6: FacesMessage fm =
7: new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, "");
8: ctx.addMessage(null, fm);
9: }


I created a simple page with a couple of buttons to show the result of setting the message. When the butto…