Building 3rd party service webhook for Whatsapp.gblib.
Signed-off-by: Rodrigo Rodriguez <me@rodrigorodriguez.com>
This commit is contained in:
parent
ba85db06dd
commit
1d0dc4cf25
5 changed files with 145 additions and 112 deletions
43
README.md
43
README.md
|
@ -1,9 +1,9 @@
|
|||

|
||||

|
||||
|
||||
Welcome to General Bots Community Edition
|
||||
Welcome to General Bot Community Edition
|
||||
----------------
|
||||
|
||||
General Bots is a package based chat bot server focused in convention
|
||||
General Bot is a package based chat bot server focused in convention
|
||||
over configuration and code-less approaches, which brings software packages
|
||||
and application server concepts to help parallel bot development.
|
||||
|
||||
|
@ -31,7 +31,7 @@ How To
|
|||
|
||||
### Run the server locally
|
||||
|
||||
1. Install [Node.js](https://www.npmjs.com/get-npm) the current generation General Bots code execution platform;
|
||||
1. Install [Node.js](https://www.npmjs.com/get-npm) the current generation General Bot code execution platform;
|
||||
2. Open a **Terminal** on Linux and Mac or a **Command Prompt** window on Windows;npm
|
||||
3. Type `npm install -g botserver` and press *ENTER*;
|
||||
4. Type `gbot` to run the server core.
|
||||
|
@ -39,7 +39,7 @@ How To
|
|||
Notes:
|
||||
|
||||
* [*nodejs.install* Chocolatey Package](https://chocolatey.org/packages/nodejs.install) is also available.
|
||||
* The zip source code of general bots is also available for [Download](https://codeload.github.com/pragmatismo-io/BotServer/zip/master);
|
||||
* The zip source code of General Bot is also available for [Download](https://codeload.github.com/pragmatismo-io/BotServer/zip/master);
|
||||
|
||||
### Configure the server to deploy specific directory
|
||||
|
||||
|
@ -56,19 +56,21 @@ Note:
|
|||
1. [Optional] Install [Chocolatey](https://chocolatey.org/install), a Windows Package Manager;
|
||||
2. Install [git](`https://git-scm.com/`), a Software Configuration Management (SCM).;
|
||||
3. Install [Node.js](npmjs.com/get-npm), a [Runtime system](https://en.wikipedia.org/wiki/Runtime_system).
|
||||
(https://www.npmjs.com/get-npm);
|
||||
(https://www.npmjs.com/get-npm) (suggested: LTS 8.x.x);
|
||||
4. Install [Visual Studio Code](https://chocolatey.org/packages/nodejs.install), Brackets or Atom as an editor of your choice;
|
||||
5. [Fork](https://en.wikipedia.org/wiki/Fork_(software_development)) by visiting https://github.com/pragmatismo-io/BotServer/fork
|
||||
6. Clone the just forked repository by running `git clone <your-forked-repository-url>/BotServer.git` ;
|
||||
7. Run `npm install` on Command Prompt or PowerShell on the General Bots source-code folder;
|
||||
8. Enter './deploy/default.gbui' folder;
|
||||
9. Run `npm install` folled by `npm run build` (To build default Bot UI);
|
||||
10. Enter the On the downloaded folder (../..);
|
||||
11. Run the bot server by `npm start`.
|
||||
7. Run `npm install -g typescript`;
|
||||
8. Run `npm install` on Command Prompt or PowerShell on the General Bot source-code folder;
|
||||
9. Enter './deploy/default.gbui' folder;
|
||||
10. Run `npm install` folled by `npm run build` (To build default Bot UI);
|
||||
11. Enter the On the downloaded folder (../..);
|
||||
12. Compile the bot server by `tsc`.
|
||||
13. Run the bot server by `npm start`.
|
||||
|
||||
Note:
|
||||
|
||||
* Whenever you are ready to turn your open-source bot ideas in form of .gbapp (source-code) and artifacts like .gbkb, .gbtheme, .gbot or the .gbai full package read [CONTRIBUTING.md](https://github.com/pragmatismo-io/BotServer/blob/master/CONTRIBUTING.md) about performing Pull Requests (PR) and creating other public custom packages repositories of your own personal or organization General Bots Community Edition powered packages.
|
||||
* Whenever you are ready to turn your open-source bot ideas in form of .gbapp (source-code) and artifacts like .gbkb, .gbtheme, .gbot or the .gbai full package read [CONTRIBUTING.md](https://github.com/pragmatismo-io/BotServer/blob/master/CONTRIBUTING.md) about performing Pull Requests (PR) and creating other public custom packages repositories of your own personal or organization General Bot Community Edition powered packages.
|
||||
|
||||
### Just copy the source code to your machine
|
||||
|
||||
|
@ -81,7 +83,7 @@ The subjects.json file contains all information related to the subject tree and
|
|||
### Creating a new Theme folder (.gbtheme folder)
|
||||
|
||||
A theme is composed of some CSS files and images. That set of files can change
|
||||
everything in the General Bots UI. Use them extensively before going to change
|
||||
everything in the General Bot UI. Use them extensively before going to change
|
||||
the UI application itself (HTML & JS).
|
||||
|
||||
Package Types
|
||||
|
@ -97,10 +99,10 @@ directory.
|
|||
The artificial intelligence extensions in form of pluggable apps. Dialogs,
|
||||
Services and all model related to data. A set of interactions, use cases,
|
||||
integrations in form of conversationals dialogs.
|
||||
The .gbapp adds the General Bots base library (botlib) for building Node.js TypeScript Apps packages.
|
||||
The .gbapp adds the General Bot base library (botlib) for building Node.js TypeScript Apps packages.
|
||||
|
||||
|
||||
Four components builds up a General Bots App:
|
||||
Four components builds up a General Bot App:
|
||||
|
||||
* dialogs
|
||||
* models
|
||||
|
@ -122,7 +124,6 @@ Models builds the foundation of data relationships in form of entities.
|
|||
|
||||
Services are a façade for bot back-end logic and other custom processing.
|
||||
|
||||
|
||||
#### Tests
|
||||
|
||||
Tests try to automate code execution validation before crashing in production.
|
||||
|
@ -152,7 +153,7 @@ Reference
|
|||
|
||||
### GeneralBots admin commands
|
||||
|
||||
General Bots can be controlled by the same chat window people talk to, so
|
||||
General Bot can be controlled by the same chat window people talk to, so
|
||||
here is a list of admin commands related to deploying .gb* files.
|
||||
|
||||
| Command | Description |
|
||||
|
@ -167,17 +168,17 @@ here is a list of admin commands related to deploying .gb* files.
|
|||
* Rodrigo Rodriguez (me@rodrigorodriguez.com) - Coding, Docs & Architecture.
|
||||
* David Lerner (david.lerner@hotmail.com) - UI, UX & Theming
|
||||
* Eduardo Romeiro (eromeirosp@outlook.com) - Content & UX
|
||||
|
||||
* Jorge Ramos (jramos@pobox.com) - Coding, Docs & Architecture.
|
||||
|
||||
Powered by Microsoft [BOT Framework](https://dev.botframework.com/) and [Azure](http://www.azure.com).
|
||||
|
||||
General Bots Code Name is [Guaribas](https://en.wikipedia.org/wiki/Guaribas), the name of a city in Brasil, state of Piaui.
|
||||
General Bot Code Name is [Guaribas](https://en.wikipedia.org/wiki/Guaribas), the name of a city in Brasil, state of Piaui.
|
||||
[Roberto Mangabeira Unger](http://www.robertounger.com/en/): "No one should have to do work that can be done by a machine".
|
||||
|
||||
|
||||
## License & Warranty
|
||||
|
||||
General Bots Copyright (c) Pragmatismo.io. All rights reserved.
|
||||
General Bot Copyright (c) Pragmatismo.io. All rights reserved.
|
||||
Licensed under the AGPL-3.0.
|
||||
|
||||
According to our dual licensing model, this program can be used either
|
||||
|
@ -193,7 +194,7 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
"General Bots" is a registered trademark of Pragmatismo.io.
|
||||
"General Bot" is a registered trademark of Pragmatismo.io.
|
||||
The licensing of the program under the AGPLv3 does not imply a
|
||||
trademark license. Therefore any rights, title and interest in
|
||||
our trademarks remain entirely with us.
|
||||
|
|
|
@ -95,6 +95,8 @@ export class GuaribasInstance extends Model<GuaribasInstance> implements IGBInst
|
|||
|
||||
@Column whatsappServiceKey: string;
|
||||
|
||||
@Column whatsappServiceNumber: string;
|
||||
|
||||
@Column spellcheckerKey: string;
|
||||
|
||||
@Column theme: string;
|
||||
|
|
|
@ -42,6 +42,7 @@ import { WhatsappDirectLine } from "./services/WhatsappDirectLine";
|
|||
|
||||
|
||||
export class GBWhatsappPackage implements IGBPackage {
|
||||
|
||||
sysPackages: IGBPackage[] = null;
|
||||
channel: WhatsappDirectLine;
|
||||
|
||||
|
@ -53,7 +54,8 @@ export class GBWhatsappPackage implements IGBPackage {
|
|||
}
|
||||
|
||||
loadBot(min: GBMinInstance): void {
|
||||
this.channel = new WhatsappDirectLine(min.instance.whatsappBotKey);
|
||||
this.channel = new WhatsappDirectLine(min.botId, min.instance.whatsappBotKey, min.instance.whatsappServiceKey,
|
||||
min.instance.whatsappServiceNumber);
|
||||
}
|
||||
|
||||
unloadBot(min: GBMinInstance): void {
|
||||
|
|
|
@ -47,18 +47,23 @@ import { GBServiceCallback, GBService, IGBInstance } from "botlib";
|
|||
export class WhatsappDirectLine extends GBService {
|
||||
|
||||
pollInterval = 1000;
|
||||
directLineSecret = '';
|
||||
directLineClientName = 'DirectLineClient';
|
||||
directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
|
||||
directLineClient: any;
|
||||
whatsappServiceKey: string;
|
||||
whatsappServiceNumber: string;
|
||||
botId: string;
|
||||
|
||||
constructor(botId, directLineSecret, whatsappServiceKey, whatsappServiceNumber) {
|
||||
|
||||
constructor(directLineSecret) {
|
||||
super();
|
||||
|
||||
this.directLineSecret = directLineSecret;
|
||||
this.botId = botId;
|
||||
this.whatsappServiceKey = whatsappServiceKey;
|
||||
this.whatsappServiceNumber = whatsappServiceNumber;
|
||||
|
||||
|
||||
// TODO: Migrate to Swagger 3.
|
||||
let directLineClient = rp(this.directLineSpecUrl)
|
||||
this.directLineClient = rp(this.directLineSpecUrl)
|
||||
.then(function (spec) {
|
||||
return new Swagger({
|
||||
spec: JSON.parse(spec.trim()),
|
||||
|
@ -67,147 +72,173 @@ export class WhatsappDirectLine extends GBService {
|
|||
})
|
||||
.then(function (client) {
|
||||
client.clientAuthorizations.add('AuthorizationBotConnector',
|
||||
new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' + directLineSecret, 'header'));
|
||||
new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' +
|
||||
directLineSecret, 'header'));
|
||||
return client;
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error('Error initializing DirectLine client', err);
|
||||
logger.error('Error initializing DirectLine client', err);
|
||||
});
|
||||
|
||||
// TODO: Remove *this* issue.
|
||||
let _this = this;
|
||||
directLineClient.then(function (client) {
|
||||
client.Conversations.Conversations_StartConversation()
|
||||
.then(function (response) {
|
||||
return response.obj.conversationId;
|
||||
})
|
||||
.then(function (conversationId) {
|
||||
_this.sendMessagesFromConsole(client, conversationId);
|
||||
_this.pollMessages(client, conversationId);
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error('Error starting conversation', err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
received (req,res){
|
||||
logger.info(`Whatsapp called. Event: ${req.body.event}, muid: ${req.body.muid}, cuid: ${req.body.cuid}`);
|
||||
res.end();
|
||||
}
|
||||
received(req, res) {
|
||||
|
||||
sendMessagesFromConsole(client, conversationId) {
|
||||
let _this = this;
|
||||
var stdin = process.openStdin();
|
||||
process.stdout.write('Command> ');
|
||||
stdin.addListener('data', function (e) {
|
||||
var input = e.toString().trim();
|
||||
if (input) {
|
||||
// exit
|
||||
if (input.toLowerCase() === 'exit') {
|
||||
return process.exit();
|
||||
}
|
||||
logger.info(`GBWhatsapp: Hook called. Event: ${req.body.event},
|
||||
muid: ${req.body.muid}, contact: ${req.body.contact.name},
|
||||
(${req.body.contact.uid}, ${req.body.contact.type})`);
|
||||
|
||||
client.Conversations.Conversations_PostActivity(
|
||||
{
|
||||
conversationId: conversationId,
|
||||
activity: {
|
||||
textFormat: 'plain',
|
||||
text: input,
|
||||
type: 'message',
|
||||
from: {
|
||||
id: _this.directLineClientName,
|
||||
name: _this.directLineClientName
|
||||
}
|
||||
}
|
||||
}).catch(function (err) {
|
||||
console.error('Error sending message:', err);
|
||||
let conversationId = null; // req.body.cuid;
|
||||
let text = req.body.message.body.text;
|
||||
let from = req.body.contact.uid;
|
||||
let fromName = req.body.contact.name;
|
||||
|
||||
this.directLineClient.then((client) => {
|
||||
|
||||
if (conversationId == null) {
|
||||
|
||||
logger.info(`GBWhatsapp: Starting new conversation on Bot.`);
|
||||
client.Conversations.Conversations_StartConversation()
|
||||
.then((response) => {
|
||||
return response.obj.conversationId;
|
||||
})
|
||||
.then((conversationId) => {
|
||||
|
||||
this.inputMessage(client, conversationId, text,
|
||||
from, fromName);
|
||||
|
||||
this.pollMessages(client, conversationId,from, fromName);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Error starting conversation', err);
|
||||
});
|
||||
|
||||
process.stdout.write('Command> ');
|
||||
} else {
|
||||
this.inputMessage(client, conversationId, text,
|
||||
from, fromName);
|
||||
|
||||
this.pollMessages(client, conversationId, from, fromName);
|
||||
}
|
||||
res.end();
|
||||
});
|
||||
}
|
||||
|
||||
/** TBD: Poll Messages from conversation using DirectLine client */
|
||||
pollMessages(client, conversationId) {
|
||||
let _this = this;
|
||||
console.log('Starting polling message for conversationId: ' + conversationId);
|
||||
|
||||
inputMessage(client, conversationId, text, from, fromName) {
|
||||
|
||||
client.Conversations.Conversations_PostActivity(
|
||||
{
|
||||
conversationId: conversationId,
|
||||
activity: {
|
||||
textFormat: 'plain',
|
||||
text: text,
|
||||
type: 'message',
|
||||
from: {
|
||||
id: from,
|
||||
name: fromName
|
||||
},
|
||||
replyToId: from
|
||||
}
|
||||
}).catch(function (err) {
|
||||
logger.error(`GBWhatsapp: Error receiving message: ${err}.`);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
pollMessages(client, conversationId,from, fromName){
|
||||
|
||||
logger.info(`GBWhatsapp: Starting polling message for conversationId:
|
||||
${conversationId}.`);
|
||||
|
||||
var watermark = null;
|
||||
setInterval(function () {
|
||||
client.Conversations.Conversations_GetActivities({ conversationId: conversationId, watermark: watermark })
|
||||
.then(function (response) {
|
||||
watermark = response.obj.watermark; // use watermark so subsequent requests skip old messages
|
||||
setInterval(() => {
|
||||
client.Conversations.Conversations_GetActivities({
|
||||
conversationId:
|
||||
conversationId, watermark: watermark
|
||||
})
|
||||
.then((response) => {
|
||||
watermark = response.obj.watermark;
|
||||
return response.obj.activities;
|
||||
})
|
||||
.then(_this.printMessages, _this.directLineClientName);
|
||||
.then((activities) => {
|
||||
this.printMessages(activities, from, fromName);
|
||||
});
|
||||
}, this.pollInterval);
|
||||
}
|
||||
|
||||
printMessages(activities, directLineClientName) {
|
||||
printMessages(activities,from, fromName) {
|
||||
|
||||
if (activities && activities.length) {
|
||||
// ignore own messages
|
||||
activities = activities.filter(function (m) { return m.from.id !== directLineClientName });
|
||||
|
||||
// Ignore own messages.
|
||||
|
||||
activities = activities.filter((m) => { return m.from.id === this.botId });
|
||||
|
||||
if (activities.length) {
|
||||
|
||||
// print other messages
|
||||
activities.forEach(activity => {
|
||||
console.log(activity.text);
|
||||
});
|
||||
// Print other messages.
|
||||
|
||||
process.stdout.write('Command> ');
|
||||
activities.forEach(activity => {
|
||||
this.printMessage(activity, from, fromName);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printMessage(activity) {
|
||||
printMessage(activity, from, fromName) {
|
||||
|
||||
let output: string;
|
||||
|
||||
if (activity.text) {
|
||||
console.log(activity.text);
|
||||
logger.info(`GBWhatsapp: MSG: ${activity.text}`);
|
||||
output = activity.text;
|
||||
}
|
||||
|
||||
if (activity.attachments) {
|
||||
activity.attachments.forEach(function (attachment) {
|
||||
switch (attachment.contentType) {
|
||||
case "application/vnd.microsoft.card.hero":
|
||||
this.renderHeroCard(attachment);
|
||||
output += this.renderHeroCard(attachment);
|
||||
break;
|
||||
|
||||
case "image/png":
|
||||
console.log('Opening the requested image ' + attachment.contentUrl);
|
||||
open(attachment.contentUrl);
|
||||
logger.info('Opening the requested image ' + attachment.contentUrl);
|
||||
output += `\n${attachment.contentUrl}`;
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.sendToDevice(from, fromName, output);
|
||||
}
|
||||
|
||||
renderHeroCard(attachment) {
|
||||
var width = 70;
|
||||
var contentLine = function (content) {
|
||||
let output: string;
|
||||
let width = 70;
|
||||
|
||||
let contentLine = function (content) {
|
||||
return ' '.repeat((width - content.length) / 2) +
|
||||
content +
|
||||
' '.repeat((width - content.length) / 2);
|
||||
}
|
||||
|
||||
console.log('/' + '*'.repeat(width + 1));
|
||||
console.log('*' + contentLine(attachment.content.title) + '*');
|
||||
console.log('*' + ' '.repeat(width) + '*');
|
||||
console.log('*' + contentLine(attachment.content.text) + '*');
|
||||
console.log('*'.repeat(width + 1) + '/');
|
||||
output += '/' + '*'.repeat(width + 1);
|
||||
output += '*' + contentLine(attachment.content.title) + '*';
|
||||
output += '*' + ' '.repeat(width) + '*';
|
||||
output += '*' + contentLine(attachment.content.text) + '*';
|
||||
output += '*'.repeat(width + 1) + '/';
|
||||
}
|
||||
|
||||
|
||||
async sendToDevice(senderID, msg) {
|
||||
async sendToDevice(to, toName, msg) {
|
||||
var options = {
|
||||
method: 'POST',
|
||||
url: 'https://www.waboxapp.com/api/send/chat',
|
||||
qs:
|
||||
{
|
||||
token: '',
|
||||
uid: '55****388**',
|
||||
to: senderID,
|
||||
token: this.whatsappServiceKey,
|
||||
uid: this.whatsappServiceNumber,
|
||||
to: to,
|
||||
custom_uid: 'GBZAP' + (new Date()).toISOString,
|
||||
text: msg
|
||||
},
|
||||
|
@ -218,8 +249,5 @@ export class WhatsappDirectLine extends GBService {
|
|||
};
|
||||
|
||||
const result = await request.get(options);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -32,7 +32,7 @@
|
|||
"async": "^1.5.2",
|
||||
"body-parser": "^1.18.2",
|
||||
"botbuilder": "^3.14.0",
|
||||
"botlib": "0.0.16",
|
||||
"botlib": "0.0.18",
|
||||
"chai": "^4.1.2",
|
||||
"chokidar": "^2.0.2",
|
||||
"csv-parse": "^2.4.0",
|
||||
|
|
Loading…
Add table
Reference in a new issue