fb-chat-api-buttons
Advanced tools
Comparing version 1.0.5 to 1.0.6
export * from './interfaces'; | ||
export * from './models/chat-buttons'; | ||
export * from './utils'; | ||
export * from './models'; |
@@ -6,3 +6,2 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__export(require("./models/chat-buttons")); | ||
__export(require("./utils")); | ||
__export(require("./models")); |
@@ -1,15 +0,9 @@ | ||
export interface IButtonBase { | ||
export interface IButton { | ||
id?: string; | ||
metadata?: any; | ||
onClick?: IButtonCallback; | ||
} | ||
export declare type IButtonCallback = (btn?: IButton | IRichButton, threadId?: string) => void; | ||
export interface IButton extends IButtonBase { | ||
text: string; | ||
color?: string; | ||
} | ||
export interface IRichButton extends IButtonBase { | ||
title: string; | ||
description?: string; | ||
image?: string; | ||
onClick?: IButtonCallback; | ||
} | ||
export declare type IButtonCallback = (btn?: IButton, threadId?: string) => void; |
import { Application } from 'express'; | ||
/** | ||
* @param app An express application | ||
* @param path Callback route path. Default /callback | ||
* @param endpoint Public callback endpoint e.g https://www.example.com/callback | ||
* @param api Facebook chat api | ||
*/ | ||
export interface IOptions { | ||
app: Application; | ||
path: string; | ||
path?: string; | ||
endpoint: string; | ||
api?: any; | ||
} |
@@ -1,12 +0,24 @@ | ||
import { IOptions, IButton, IRichButton } from '../interfaces'; | ||
import { NextFunction, Request, Response } from 'express'; | ||
import { IOptions, IButton } from '../interfaces'; | ||
export declare class ChatButtons { | ||
options: IOptions; | ||
private callbacks; | ||
private font; | ||
/** | ||
* Inits chat buttons. | ||
* @param options | ||
*/ | ||
constructor(options: IOptions); | ||
setApi(api: any): void; | ||
send(btn: IButton | IRichButton, threadId: string): void; | ||
/** | ||
* Sends a button. | ||
*/ | ||
send(btn: IButton, threadId: string): void; | ||
/** | ||
* Stores button callback in memory, so it can be triggered later. | ||
*/ | ||
private attachCallback; | ||
private middleware; | ||
private renderButton; | ||
/** | ||
* A middleware for express server. | ||
*/ | ||
middleware: (req: Request, res: Response, next: NextFunction) => void; | ||
} |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const jimp_1 = __importDefault(require("jimp")); | ||
const utils_1 = require("../utils"); | ||
const views_1 = require("../views"); | ||
const hash_1 = require("../utils/hash"); | ||
class ChatButtons { | ||
/** | ||
* Inits chat buttons. | ||
* @param options | ||
*/ | ||
constructor(options) { | ||
this.options = options; | ||
this.callbacks = {}; | ||
this.middleware = (req, res, next) => __awaiter(this, void 0, void 0, function* () { | ||
const beingPrefetched = req.headers['user-agent'].includes('facebook'); | ||
/** | ||
* A middleware for express server. | ||
*/ | ||
this.middleware = (req, res, next) => { | ||
const { data, threadId } = req.query; | ||
const beingPrefetched = utils_1.isBeingPrefetched(req); | ||
// Decode data and parse it to JSON | ||
const decoded = decodeURIComponent(data); | ||
const btn = JSON.parse(decoded); | ||
const isRichBtn = btn.title != null; | ||
if (isRichBtn) { | ||
const richBtn = btn; | ||
if (beingPrefetched) { | ||
res.send(views_1.prefetchView(richBtn)); | ||
} | ||
else { | ||
this.callbacks[btn.id](richBtn, threadId); | ||
res.send(views_1.clickView(threadId)); | ||
} | ||
// If link is being prefetched, then | ||
// render a html page with meta tags | ||
// e.g. <meta property="og:title" value="Button title" /> | ||
if (beingPrefetched) { | ||
res.send(views_1.prefetchView(btn)); | ||
} | ||
// If link isn't being prefetched, then it counts as a click, so | ||
// it will trigger callback and render a html page with js code that closes tab. | ||
else { | ||
const simpleBtn = btn; | ||
if (beingPrefetched) { | ||
const buffer = yield this.renderButton(simpleBtn.text, simpleBtn.color); | ||
res.set('Content-Type', jimp_1.default.MIME_JPEG); | ||
res.send(buffer); | ||
} | ||
else { | ||
this.callbacks[btn.id](simpleBtn, threadId); | ||
res.send(views_1.clickView(threadId)); | ||
} | ||
res.send(views_1.clickView(threadId)); | ||
const callback = this.callbacks[btn.id]; | ||
if (callback != null) | ||
callback(threadId); | ||
} | ||
next(); | ||
}); | ||
}; | ||
this.options.path = this.options.path || '/callback'; | ||
// Apply the middleware. | ||
options.app.use(options.path, this.middleware); | ||
jimp_1.default.loadFont(jimp_1.default.FONT_SANS_16_BLACK, (err, font) => { | ||
this.font = font; | ||
}); | ||
} | ||
@@ -60,9 +45,19 @@ setApi(api) { | ||
} | ||
/** | ||
* Sends a button. | ||
*/ | ||
send(btn, threadId) { | ||
btn.id = btn.id || hash_1.generateHash(8); | ||
const { endpoint, api } = this.options; | ||
// If button hasn't got an id, then set a random one. | ||
btn.id = btn.id || utils_1.generateHash(8); | ||
// Generate a callback url. | ||
const url = utils_1.generateUrl(endpoint, btn, threadId); | ||
// Store button callback in memory. | ||
this.attachCallback(btn); | ||
// Send button. | ||
api.sendMessage({ url }, threadId); | ||
} | ||
/** | ||
* Stores button callback in memory, so it can be triggered later. | ||
*/ | ||
attachCallback(btn) { | ||
@@ -74,19 +69,3 @@ const { id } = btn; | ||
} | ||
renderButton(text, background = '#03A9F4') { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const fontWidth = jimp_1.default.measureText(this.font, text); | ||
const fontHeight = jimp_1.default.measureTextHeight(this.font, text, 256); | ||
const width = fontWidth + 48; | ||
const height = fontHeight + 48; | ||
const img = yield new jimp_1.default(width, height, background); | ||
yield img.print(this.font, 0, 0, { | ||
text, | ||
alignmentX: jimp_1.default.HORIZONTAL_ALIGN_CENTER, | ||
alignmentY: jimp_1.default.VERTICAL_ALIGN_MIDDLE, | ||
}, width, height); | ||
const buffer = yield img.getBufferAsync(jimp_1.default.MIME_JPEG); | ||
return buffer; | ||
}); | ||
} | ||
} | ||
exports.ChatButtons = ChatButtons; |
@@ -0,1 +1,6 @@ | ||
/** | ||
* Generates a random string in given length. | ||
* @param length Range | ||
* @param possible Possible characters | ||
*/ | ||
export declare const generateHash: (length: number, possible?: string) => string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Generates a random string in given length. | ||
* @param length Range | ||
* @param possible Possible characters | ||
*/ | ||
exports.generateHash = (length, possible = 'abcdefghijklmnopqrstuvwxyz0123456789') => { | ||
@@ -4,0 +9,0 @@ let id = ''; |
export * from './url'; | ||
export * from './hash'; | ||
export * from './meta-tags'; | ||
export * from './prefetch'; |
@@ -7,1 +7,4 @@ "use strict"; | ||
__export(require("./url")); | ||
__export(require("./hash")); | ||
__export(require("./meta-tags")); | ||
__export(require("./prefetch")); |
interface IMetaTags { | ||
[key: string]: any; | ||
} | ||
/** | ||
* Generates a html code with meta tags. | ||
* @param list Meta tags | ||
*/ | ||
export declare const getMetaTags: (list: IMetaTags) => string; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Generates a html code with meta tags. | ||
* @param list Meta tags | ||
*/ | ||
exports.getMetaTags = (list) => { | ||
@@ -4,0 +8,0 @@ let html = ''; |
@@ -1,2 +0,8 @@ | ||
import { IButton, IRichButton } from '../interfaces'; | ||
export declare const generateUrl: (endpoint: string, btn: IButton | IRichButton, threadId: string) => string; | ||
import { IButton } from '../interfaces'; | ||
/** | ||
* Generates a callback url. | ||
* @param endpoint Public callback endpoint | ||
* @param btn Button | ||
* @param threadId Thread id | ||
*/ | ||
export declare const generateUrl: (endpoint: string, btn: IButton, threadId: string) => string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const hash_1 = require("./hash"); | ||
/** | ||
* Generates a callback url. | ||
* @param endpoint Public callback endpoint | ||
* @param btn Button | ||
* @param threadId Thread id | ||
*/ | ||
exports.generateUrl = (endpoint, btn, threadId) => { | ||
// Use a random string in callback url, because | ||
// facebook doesn't prefetch the same urls that were prefetched in the past. | ||
const hash = hash_1.generateHash(24); | ||
@@ -6,0 +14,0 @@ const data = encodeURIComponent(JSON.stringify(btn)); |
@@ -1,2 +0,2 @@ | ||
import { IRichButton } from '../interfaces'; | ||
export declare const prefetchView: ({ title, description, image }: IRichButton) => string; | ||
import { IButton } from '../interfaces'; | ||
export declare const prefetchView: ({ title, description, image }: IButton) => string; |
{ | ||
"name": "fb-chat-api-buttons", | ||
"version": "1.0.5", | ||
"version": "1.0.6", | ||
"description": "🤖 Provides slightly better UX for your facebook chat bot by adding interactive buttons.", | ||
@@ -35,3 +35,2 @@ "keywords": [ | ||
"@types/node": "10.12.18", | ||
"jimp": "0.6.0", | ||
"typescript": "3.2.4" | ||
@@ -38,0 +37,0 @@ }, |
@@ -1,3 +0,16 @@ | ||
# 🤖 Facebook Chat API buttons | ||
<div align="center"> | ||
<h1>Chat Buttons</h1> | ||
Provides slightly better UX for your facebook chat bot by adding interactive buttons. | ||
[![Travis](https://img.shields.io/travis/xNerhu/fb-chat-api-buttons.svg?style=flat-square)](https://travis-ci.org/xNerhu/fb-chat-api-buttons.svg) | ||
Provide slightly better UX for your facebook chat bot by adding interactive buttons. | ||
</div> | ||
## Installing | ||
To install chat buttons run in terminal: | ||
```bash | ||
$ npm install fb-chat-api-buttons | ||
``` | ||
## Documentation | ||
Soon... |
export * from './interfaces'; | ||
export * from './models/chat-buttons'; | ||
export * from './utils'; | ||
export * from './models'; |
@@ -1,21 +0,10 @@ | ||
export interface IButtonBase { | ||
export interface IButton { | ||
id?: string; | ||
metadata?: any; | ||
onClick?: IButtonCallback; | ||
} | ||
export type IButtonCallback = ( | ||
btn?: IButton | IRichButton, | ||
threadId?: string, | ||
) => void; | ||
export interface IButton extends IButtonBase { | ||
text: string; | ||
color?: string; | ||
} | ||
export interface IRichButton extends IButtonBase { | ||
title: string; | ||
description?: string; | ||
image?: string; | ||
onClick?: IButtonCallback; | ||
} | ||
export type IButtonCallback = (btn?: IButton, threadId?: string) => void; |
import { Application } from 'express'; | ||
/** | ||
* @param app An express application | ||
* @param path Callback route path. Default /callback | ||
* @param endpoint Public callback endpoint e.g https://www.example.com/callback | ||
* @param api Facebook chat api | ||
*/ | ||
export interface IOptions { | ||
app: Application; | ||
path: string; | ||
path?: string; | ||
endpoint: string; | ||
api?: any; | ||
} |
import { NextFunction, Request, Response } from 'express'; | ||
import Jimp from 'jimp'; | ||
import { IOptions, IButton, IRichButton, IButtonCallback } from '../interfaces'; | ||
import { generateUrl } from '../utils'; | ||
import { IOptions, IButton, IButtonCallback } from '../interfaces'; | ||
import { generateHash, generateUrl, isBeingPrefetched } from '../utils'; | ||
import { prefetchView, clickView } from '../views'; | ||
import { generateHash } from '../utils/hash'; | ||
@@ -16,10 +14,10 @@ interface ICallbacks { | ||
private font; | ||
/** | ||
* Inits chat buttons. | ||
* @param options | ||
*/ | ||
constructor(public options: IOptions) { | ||
this.options.path = this.options.path || '/callback'; | ||
// Apply the middleware. | ||
options.app.use(options.path, this.middleware); | ||
Jimp.loadFont(Jimp.FONT_SANS_16_BLACK, (err, font) => { | ||
this.font = font; | ||
}); | ||
} | ||
@@ -31,13 +29,21 @@ | ||
public send(btn: IButton | IRichButton, threadId: string) { | ||
/** | ||
* Sends a button. | ||
*/ | ||
public send(btn: IButton, threadId: string) { | ||
const { endpoint, api } = this.options; | ||
// If button hasn't got an id, then set a random one. | ||
btn.id = btn.id || generateHash(8); | ||
const { endpoint, api } = this.options; | ||
// Generate a callback url. | ||
const url = generateUrl(endpoint, btn, threadId); | ||
// Store button callback in memory. | ||
this.attachCallback(btn); | ||
// Send button. | ||
api.sendMessage({ url }, threadId); | ||
} | ||
private attachCallback(btn: IButton | IRichButton) { | ||
/** | ||
* Stores button callback in memory, so it can be triggered later. | ||
*/ | ||
private attachCallback(btn: IButton) { | ||
const { id } = btn; | ||
@@ -49,62 +55,26 @@ if (this.callbacks[id] == null) { | ||
private middleware = async ( | ||
req: Request, | ||
res: Response, | ||
next: NextFunction, | ||
) => { | ||
const beingPrefetched = req.headers['user-agent'].includes('facebook'); | ||
/** | ||
* A middleware for express server. | ||
*/ | ||
public middleware = (req: Request, res: Response, next: NextFunction) => { | ||
const { data, threadId } = req.query; | ||
const beingPrefetched = isBeingPrefetched(req); | ||
// Decode data and parse it to JSON | ||
const decoded = decodeURIComponent(data); | ||
const btn: IButton | IRichButton = JSON.parse(decoded); | ||
const isRichBtn = (btn as IRichButton).title != null; | ||
if (isRichBtn) { | ||
const richBtn = btn as IRichButton; | ||
if (beingPrefetched) { | ||
res.send(prefetchView(richBtn)); | ||
} else { | ||
this.callbacks[btn.id](richBtn, threadId); | ||
res.send(clickView(threadId)); | ||
} | ||
} else { | ||
const simpleBtn = btn as IButton; | ||
if (beingPrefetched) { | ||
const buffer = await this.renderButton(simpleBtn.text, simpleBtn.color); | ||
res.set('Content-Type', Jimp.MIME_JPEG); | ||
res.send(buffer); | ||
} else { | ||
this.callbacks[btn.id](simpleBtn, threadId); | ||
res.send(clickView(threadId)); | ||
} | ||
const btn = JSON.parse(decoded) as IButton; | ||
// If link is being prefetched, then | ||
// render a html page with meta tags | ||
// e.g. <meta property="og:title" value="Button title" /> | ||
if (beingPrefetched) { | ||
res.send(prefetchView(btn)); | ||
} | ||
// If link isn't being prefetched, then it counts as a click, so | ||
// it will trigger callback and render a html page with js code that closes tab. | ||
else { | ||
res.send(clickView(threadId)); | ||
const callback = this.callbacks[btn.id]; | ||
if (callback != null) callback(threadId); | ||
} | ||
next(); | ||
}; | ||
private async renderButton(text: string, background = '#03A9F4') { | ||
const fontWidth = Jimp.measureText(this.font, text); | ||
const fontHeight = Jimp.measureTextHeight(this.font, text, 256); | ||
const width = fontWidth + 48; | ||
const height = fontHeight + 48; | ||
const img = await new Jimp(width, height, background); | ||
await img.print( | ||
this.font, | ||
0, | ||
0, | ||
{ | ||
text, | ||
alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER, | ||
alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE, | ||
}, | ||
width, | ||
height, | ||
); | ||
const buffer = await img.getBufferAsync(Jimp.MIME_JPEG); | ||
return buffer; | ||
} | ||
} |
@@ -0,1 +1,6 @@ | ||
/** | ||
* Generates a random string in given length. | ||
* @param length Range | ||
* @param possible Possible characters | ||
*/ | ||
export const generateHash = ( | ||
@@ -2,0 +7,0 @@ length: number, |
export * from './url'; | ||
export * from './hash'; | ||
export * from './meta-tags'; | ||
export * from './prefetch'; |
@@ -5,2 +5,6 @@ interface IMetaTags { | ||
/** | ||
* Generates a html code with meta tags. | ||
* @param list Meta tags | ||
*/ | ||
export const getMetaTags = (list: IMetaTags) => { | ||
@@ -7,0 +11,0 @@ let html = ''; |
@@ -1,9 +0,17 @@ | ||
import { IButton, IRichButton } from '../interfaces'; | ||
import { IButton } from '../interfaces'; | ||
import { generateHash } from './hash'; | ||
/** | ||
* Generates a callback url. | ||
* @param endpoint Public callback endpoint | ||
* @param btn Button | ||
* @param threadId Thread id | ||
*/ | ||
export const generateUrl = ( | ||
endpoint: string, | ||
btn: IButton | IRichButton, | ||
btn: IButton, | ||
threadId: string, | ||
) => { | ||
// Use a random string in callback url, because | ||
// facebook doesn't prefetch the same urls that were prefetched in the past. | ||
const hash = generateHash(24); | ||
@@ -10,0 +18,0 @@ const data = encodeURIComponent(JSON.stringify(btn)); |
@@ -1,5 +0,5 @@ | ||
import { IRichButton } from '../interfaces'; | ||
import { IButton } from '../interfaces'; | ||
import { getMetaTags } from '../utils/meta-tags'; | ||
export const prefetchView = ({ title, description, image }: IRichButton) => ` | ||
export const prefetchView = ({ title, description, image }: IButton) => ` | ||
<!DOCTYPE html> | ||
@@ -6,0 +6,0 @@ <html> |
Sorry, the diff of this file is not supported yet
2
54
539
17
21625
- Removedjimp@0.6.0
- Removed@babel/polyfill@7.12.1(transitive)
- Removed@jimp/bmp@0.6.8(transitive)
- Removed@jimp/core@0.6.8(transitive)
- Removed@jimp/custom@0.6.8(transitive)
- Removed@jimp/gif@0.6.8(transitive)
- Removed@jimp/jpeg@0.6.8(transitive)
- Removed@jimp/plugin-blit@0.6.8(transitive)
- Removed@jimp/plugin-blur@0.6.8(transitive)
- Removed@jimp/plugin-color@0.6.8(transitive)
- Removed@jimp/plugin-contain@0.6.8(transitive)
- Removed@jimp/plugin-cover@0.6.8(transitive)
- Removed@jimp/plugin-crop@0.6.8(transitive)
- Removed@jimp/plugin-displace@0.6.8(transitive)
- Removed@jimp/plugin-dither@0.6.8(transitive)
- Removed@jimp/plugin-flip@0.6.8(transitive)
- Removed@jimp/plugin-gaussian@0.6.8(transitive)
- Removed@jimp/plugin-invert@0.6.8(transitive)
- Removed@jimp/plugin-mask@0.6.8(transitive)
- Removed@jimp/plugin-normalize@0.6.8(transitive)
- Removed@jimp/plugin-print@0.6.8(transitive)
- Removed@jimp/plugin-resize@0.6.8(transitive)
- Removed@jimp/plugin-rotate@0.6.8(transitive)
- Removed@jimp/plugin-scale@0.6.8(transitive)
- Removed@jimp/plugins@0.6.8(transitive)
- Removed@jimp/png@0.6.8(transitive)
- Removed@jimp/tiff@0.6.8(transitive)
- Removed@jimp/types@0.6.8(transitive)
- Removed@jimp/utils@0.6.8(transitive)
- Removedany-base@1.1.0(transitive)
- Removedbase64-js@1.5.1(transitive)
- Removedbmp-js@0.1.0(transitive)
- Removedbuffer@5.7.1(transitive)
- Removedbuffer-equal@0.0.1(transitive)
- Removedcore-js@2.6.12(transitive)
- Removeddom-walk@0.1.2(transitive)
- Removedexif-parser@0.1.12(transitive)
- Removedfile-type@9.0.0(transitive)
- Removedglobal@4.4.0(transitive)
- Removedieee754@1.2.1(transitive)
- Removedis-function@1.0.2(transitive)
- Removedjimp@0.6.0(transitive)
- Removedjpeg-js@0.3.7(transitive)
- Removedload-bmfont@1.4.1(transitive)
- Removedmime@1.6.0(transitive)
- Removedmin-document@2.19.0(transitive)
- Removedminimist@0.0.8(transitive)
- Removedmkdirp@0.5.1(transitive)
- Removedomggif@1.0.10(transitive)
- Removedpako@1.0.11(transitive)
- Removedparse-bmfont-ascii@1.0.6(transitive)
- Removedparse-bmfont-binary@1.0.6(transitive)
- Removedparse-bmfont-xml@1.1.6(transitive)
- Removedparse-headers@2.0.5(transitive)
- Removedphin@2.9.3(transitive)
- Removedpixelmatch@4.0.2(transitive)
- Removedpngjs@3.4.0(transitive)
- Removedprocess@0.11.10(transitive)
- Removedregenerator-runtime@0.13.11(transitive)
- Removedsax@1.4.1(transitive)
- Removedtimm@1.7.1(transitive)
- Removedtinycolor2@1.6.0(transitive)
- Removedutif@2.0.1(transitive)
- Removedxhr@2.6.0(transitive)
- Removedxml-parse-from-string@1.0.1(transitive)
- Removedxml2js@0.5.0(transitive)
- Removedxmlbuilder@11.0.1(transitive)
- Removedxtend@4.0.2(transitive)