In the last two tutorials, we have worked on two public services. In this tutorial, we will create a personal device: LinkedIn. It allows users to link their own LinkedIn accounts by OAuth and publish posts on LinkedIn.
Go to the LinkedIn Developers Page and click on the big "Create App" button. Then fill in the required information about your app and submit by clicking on "Create app". You've just created a LinkedIn app!
On your app page, click on the "Auth" tab and take note of the Client ID and Client Secret. We will need these later!
On the same "Auth" tab, under OAuth 2.0 Settings, add the following URL
https://thingengine.stanford.edu/devices/oauth2/callback/<your-name>.linkedin
where <your-name>.linkedin
is the name of your Almond device.
Go to the Device Creation Page, fill in the following basic information about the device:
<your-name>.linkedin
(This must be unique and the same as what you have in step 1)My LinkedIn
(This should also be unique so that it's easy to find!)LinkedIn Account in Almond
Social Network
index.js
with the following code (remember to replace <your-name>.linkedin
with your device's ID)"use strict";
const Tp = require('thingpedia');
const PROFILE_URL = 'https://api.linkedin.com/v2/me';
const SHARE_URL = 'https://api.linkedin.com/v2/ugcPosts';
module.exports = class LinkedinDevice extends Tp.BaseDevice {
/*
runOAuth2 specifies how the authentication works in detail
*/
static get runOAuth2() {
return Tp.Helpers.OAuth2({
scope: ["r_emailaddress","r_liteprofile","w_member_social"],
authorize: 'https://www.linkedin.com/oauth/v2/authorization',
get_access_token: 'https://www.linkedin.com/oauth/v2/accessToken',
set_state: true,
callback(engine, accessToken, refreshToken) {
const auth = 'Bearer ' + accessToken;
return Tp.Helpers.Http.get(PROFILE_URL,
{ auth: auth,
accept: 'application/json' }).then((response) => {
const parsed = JSON.parse(response);
return engine.devices.loadOneDevice({ kind: '<your-name>.linkedin',
accessToken: accessToken,
refreshToken: refreshToken,
userId: parsed.id,
userName: parsed.formattedName
}, true);
});
}
});
}
/*
A user might have multiple accounts for LinkedIn.
Thus we need to have an unique ID to identify different instances of the class.
And optionally, we give each instance a different name and description, so
that users can easily tell which account a device instance associates with
in their device list.
*/
constructor(engine, state) {
super(engine, state);
// This is the actual device name that will appear on your user's Almond
this.uniqueId = 'com.linkedin-' + this.state.userId;
this.name = "LinkedIn Account of %s".format(this.state.userName);
this.description = "This is your LinkedIn account";
}
/*
An action function called "share"
The "do_" prefix indicates this is an action, not a query
This allows Almond to help your user share a post on LinkedIn!
*/
do_share({ content }) {
/*
Send an HTTP POST request to SHARE_URL with the data we want to post.
Options include the auth information, the format of the data, and the expected output type
*/
return Tp.Helpers.Http.post(SHARE_URL, JSON.stringify({
"author": "urn:li:person:" + this.state.userId,
"lifecycleState": "PUBLISHED",
"specificContent": {
"com.linkedin.ugc.ShareContent": {
"shareCommentary": {
"text": content
},
"shareMediaCategory": "NONE"
}
},
"visibility": {
"com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
}
}), {
useOAuth2: this,
dataContentType: 'application/json',
accept: 'application/json'
});
}
};
References: Node.js HTTP APIs, Promise, Array.prototype.map().
Click on manifest.tt
on the left panel.
Copy the following code to the editor and replace <your-name>.linkedin
with the
actual device ID. Also, replace <your-client-id>
and <your-client-secret>
with the Client ID and Client Secret from your LinkedIn app's Auth page.
class @<your-name>.linkedin {
// tell the system this device uses customized js code
import loader from @org.thingpedia.v2();
// tell the system this device uses OAuth2
import config from @org.thingpedia.config.oauth2(client_id=<your-client-id>, client_secret=<your-client-secret>);
/*
The function to post on LinkedIn.
Example commands: "post on LinkedIn"
*/
action share(in req status: String #_[prompt="What do you want to post?"])
#_[confirmation="share $status on your LinkedIn"]
#[doc="share a comment and a link "];
}
Click on dataset.tt
on the left panel.
Copy the following code to the editor and replace <your-name>.linkedin
with the
actual device ID:
dataset @<your-name>.linkedin {
action (p_status :String) := @<your-name>.linkedin.share(status=p_status)
#_[utterances=["share $p_status on linkedin","post $p_status on linkedin"]];
action := @<your-name>.linkedin.share()
#_[utterances=["update my linkedin","post something on my linkedin"]];
}
Click the Create
button at the top left corner to submit the device.
Congratulation! You made a LinkedIn device for Thingpedia.
Go to Thingpedia page and search for your device name to see your device.
Go to My Almond. Click on Configure new skill under "Enabled Skills" and then on your device's name. You should be immediately directed to the LinkedIn OAuth page.
After you log in to LinkedIn and grant permission, you will be redirected to your Almond page, which now includes the LinkedIn skill!
Similar to previous tutorials,
please wait for a couple minutes until the banner disappears.
Then try commands such as get my LinkedIn profile
, update my LinkedIn
.
Note that at this point, the natural language support is very limited.
If you want to train the full model, click on the Start training
button at the bottom
of the details page of your device to start a new training job. The training will take up to 12 hours.