Skip to content

Instantly share code, notes, and snippets.

@sanchezcarlosjr
Last active July 20, 2020 21:27
Show Gist options
  • Save sanchezcarlosjr/2387ce3fa902848a4f436c23e1f67130 to your computer and use it in GitHub Desktop.
Save sanchezcarlosjr/2387ce3fa902848a4f436c23e1f67130 to your computer and use it in GitHub Desktop.
How to create a bot on Whatsapp?
import * as dialogflow from '@google-cloud/dialogflow';
import { uuid } from 'uuidv4';
import { NaturalProcessorLanguage } from '../domain/NaturalProcessorLanguage';
import * as protos from '@google-cloud/dialogflow/build/protos/protos';
export class Dialogflow implements NaturalProcessorLanguage {
private responses:
[protos.google.cloud.dialogflow.v2.IDetectIntentResponse,
protos.google.cloud.dialogflow.v2.IDetectIntentRequest | undefined, {} | undefined];
async detectIntent(text: string, sessionId: string = uuid()): Promise<string> {
if (text.length > 250) {
return new Promise((resolve) => resolve('Be shorter in your request'));
}
const sessionClient = new dialogflow.SessionsClient();
const sessionPath = sessionClient.projectAgentSessionPath('project-id ', sessionId);
this.responses = await sessionClient.detectIntent({
session: sessionPath,
queryInput: {
text: {
text,
languageCode: 'en-US'
},
},
});
return this.responses[0].queryResult.fulfillmentText;
}
getMedia(): string {
if (this.responses[0].queryResult.fulfillmentText === 'Listen to how I am!') {
return 'https://firebasestorage.googleapis.com/v0/b/arsus-production.appspot.com/o/assets%2FWhatsApp%20Ptt%202020-07-17%20at%205.14.18%20PM.ogg?alt=media&token=b0de767c-e6b5-49d8-b916-275244ff3ed6';
}
return '';
}
}
export interface NaturalProcessorLanguage {
detectIntent(text: string, sessionId?: string): Promise<string>;
getMedia(): string;
}
{
"name": "arsus-server",
"scripts": {
"build": "tsc",
"start": "npm run shell",
"test": "npm run test:unit",
"test:unit": "mocha -r ts-node/register tests/**/*.spec.ts --timeout 100000",
"test:features": "cucumber-js -p default",
"deploy": "firebase deploy --only functions"
},
"main": "lib/index.js",
"dependencies": {
"@google-cloud/dialogflow": "^2.0.0",
"@google-cloud/vision": "^2.1.0",
"@sendgrid/mail": "^7.2.0",
"actions-on-google": "^2.12.0",
"dialogflow-fulfillment": "^0.6.1",
"express": "^4.17.1",
"firebase": "^7.15.1",
"firebase-admin": "^8.12.1",
"firebase-functions": "^3.7.0",
"twilio": "^3.48.0",
"uuidv4": "^6.1.1"
},
"devDependencies": {
"@firebase/firestore-types": "^1.11.0",
"@types/actions-on-google": "^2.0.1",
"@types/chai": "^4.2.11",
"@types/cucumber": "^6.0.1",
"@types/express": "^4.17.6",
"@types/faker": "^4.1.12",
"@types/jest": "^26.0.0",
"@types/mocha": "^7.0.2",
"@types/node": "^14.0.13",
"@types/request-promise": "^4.1.46",
"@types/sinon": "^9.0.4",
"@types/stripe": "^7.13.23",
"@types/supertest": "^2.0.9",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"cucumber": "^6.0.5",
"faker": "^4.1.0",
"firebase-functions-test": "^0.2.1",
"mocha": "^8.0.1",
"sinon": "^9.0.2",
"supertest": "^4.0.2",
"ts-jest": "^26.1.0",
"ts-node": "^8.10.2",
"tslint": "^6.1.2",
"typescript": "^3.9.5"
},
"engines": {
"node": "10"
},
"private": true
}
import { https } from 'firebase-functions';
import { Response } from 'express';
import { Dialogflow } from '../../../../contexts/email/infraestructure/Dialogflow';
import * as functions from 'firebase-functions';
import { WhatsappCreator } from '../../../../contexts/blog/bot/application/WhatsappCreator';
export interface BodyTwilio {
SmsMessageSid: string;
NumMedia: string;
SmsSid: string;
SmsStatus: string;
Body: string;
To: string;
NumSegments: string;
MessageSid: string;
AccountSid: string;
From: string;
ApiVersion: string;
}
export const SMS = functions
.runWith({
timeoutSeconds: 20,
memory: '2GB'
})
.https.onRequest(async (request: https.Request, response: Response) => {
const body: BodyTwilio = request.body;
const whatsappCreator = new WhatsappCreator(new Dialogflow());
const xml: string = await whatsappCreator.create(body.Body, body.From);
response.setHeader('Content-Type', 'text/xml');
return response.send(xml);
});
{
"compilerOptions": {
"module": "commonjs",
"noImplicitReturns": true,
"noUnusedLocals": true,
"outDir": "lib",
"sourceMap": true,
"strict": true,
"target": "es2015",
"strictNullChecks": false,
"skipLibCheck": true
},
"lib": ["esnext.asynciterable", "es2015", "dom"],
"compileOnSave": true,
"include": ["src/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
{
"extends": ["tslint:recommended", "tslint-config-prettier"],
"rules": {
"array-type": false,
"arrow-parens": false,
"deprecation": {
"severity": "warn"
},
"import-blacklist": [true, "rxjs/Rx"],
"max-line-length": [true, 120],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"public-static-field",
"protected-static-field",
"private-static-field",
"public-instance-field",
"protected-instance-field",
"private-instance-field",
"constructor",
"public-static-method",
"protected-static-method",
"private-static-method",
"public-instance-method",
"protected-instance-method",
"private-instance-method"
]
}
],
"interface-name": false,
"max-classes-per-file": false,
"no-consecutive-blank-lines": false,
"no-console": [true, "debug", "time", "timeEnd", "trace"],
"no-duplicate-variable": [true, "check-parameters"],
"no-empty": false,
"no-inferrable-types": [true, "ignore-params"],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-unnecessary-initializer": true,
"object-literal-sort-keys": false,
"quotemark": [true, "single"],
"typedef": [true, "parameter", "property-declaration"],
"variable-name": false,
"no-var-requires": false,
"object-literal-key-quotes": false,
"ordered-imports": false,
"trailing-comma": false,
"no-conflicting-lifecycle": true,
"no-output-native": true,
"directive-selector": [true, "attribute", "app", "camelCase"],
"component-selector": [true, "element", "page", "app", "kebab-case"],
"template-banana-in-box": true,
"template-no-negated-async": true,
"no-output-on-prefix": true,
"no-inputs-metadata-property": true,
"no-outputs-metadata-property": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-output-rename": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true
}
}
import { expect } from 'chai';
import * as mocha from 'mocha';
import { WhatsappCreator } from '../../../src/contexts/blog/bot/application/WhatsappCreator';
import sinon = require('sinon');
mocha.describe('Whatsapp', () => {
it.only('should return a string', async () => {
const dialogflow = sinon.createStubInstance(Dialogflow);
const body = 'ABC';
dialogflow.detectIntent.returns(new Promise((resolve) => resolve(body)));
const whatsapp = new WhatsappCreator(dialogflow);
const response = await whatsapp.create('a');
const xml = `<?xml version="1.0" encoding="UTF-8"?><Response><Message><Body>${body}</Body></Message></Response>`;
expect(response).to.equal(xml);
});
});
import { NaturalProcessorLanguage } from '../../../email/domain/NaturalProcessorLanguage';
const MessagingResponse = require('twilio').twiml.MessagingResponse;
export class WhatsappCreator {
private twiml = new MessagingResponse();
private message = this.twiml.message();
constructor(private naturalProcessorLanguage: NaturalProcessorLanguage) {
}
async create(text: string, sessionId: string): Promise<string> {
const fulfillmentText = await this.naturalProcessorLanguage.detectIntent(text, sessionId);
this.message.body(fulfillmentText);
this.setMedia(this.naturalProcessorLanguage.getMedia());
return this.twiml.toString();
}
private setMedia(media: string) {
if (media) {
this.message.media(media);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment