Socket
Socket
Sign inDemoInstall

fb-chat-api-buttons

Package Overview
Dependencies
2
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.5 to 1.0.6

build/models/index.d.ts

3

build/index.d.ts
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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc