diff --git a/package.json b/package.json index 3e0fe08..ccb6bb3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "messenger-chat", "author": "Amine Ben hammou", - "version": "1.0.0-beta.1", + "version": "1.0.0-beta.2", "license": "MIT", "description": "A library to create chat bots for Messenger", "module": "dist/messenger-chat.esm.js", diff --git a/src/bot/handle.ts b/src/bot/handle.ts index 632dc0d..4c88671 100644 --- a/src/bot/handle.ts +++ b/src/bot/handle.ts @@ -1,4 +1,5 @@ import {EventEntry, WebhookEvent} from '../events/types' +import {Reply} from '../messages/types' import {BotConfig} from './types' export async function handle(config: BotConfig, event: WebhookEvent) { @@ -11,19 +12,34 @@ export async function handle(config: BotConfig, event: WebhookEvent) { async function handleEntry(config: BotConfig, entry: EventEntry) { const messaging = entry.messaging[0] const {recipient, sender} = messaging - const {accessToken, storage, send} = config + const {storage, initialContext} = config const contextKey = `${sender.id}-${recipient.id}` - const context = (await storage.get(contextKey)) || config.initialContext + const context = (await storage.get(contextKey)) || initialContext const setContext = (value: any) => storage.set(contextKey, value) - let response = config.handle({...messaging, context, setContext}) + const getUserFields = () => loadUserFields(config, sender) + let response = config.handle({...messaging, context, setContext, getUserFields}) if (response instanceof Promise) { response = await response } if (response) { - await send(accessToken, { + await sendReply(config, { messaging_type: 'RESPONSE', recipient: sender, message: response, }) } } + +async function sendReply({axiosInstance, accessToken}: BotConfig, reply: Reply) { + return axiosInstance.post( + `https://graph.facebook.com/v11.0/me/messages?access_token=${accessToken}`, + reply + ) +} + +async function loadUserFields({axiosInstance, accessToken}: BotConfig, sender: {id: string}) { + const res = await axiosInstance.get( + `https://graph.facebook.com/v11.0/${sender.id}?access_token=${accessToken}&fields=id,first_name,last_name,profile_pic` + ) + return res.data +} diff --git a/src/bot/index.ts b/src/bot/index.ts index 15618ce..a26d76f 100644 --- a/src/bot/index.ts +++ b/src/bot/index.ts @@ -14,11 +14,7 @@ export function bot(config: Partial): Bot { config = { initialContext: {}, storage: memoryStorage(), - send: (token, data) => - axiosInstance.post( - `https://graph.facebook.com/v11.0/me/messages?access_token=${token}`, - data - ), + axiosInstance, ...config, } diff --git a/src/bot/types.ts b/src/bot/types.ts index c9eb803..bcd2ed2 100644 --- a/src/bot/types.ts +++ b/src/bot/types.ts @@ -1,3 +1,4 @@ +import {AxiosInstance} from 'axios' import {Message} from '../messages/types' import {ContextStorage} from '../storages/types' import {WebhookEvent, MessagingItem} from '../events/types' @@ -8,7 +9,7 @@ export type BotConfig = { initialContext: any storage: ContextStorage handle: (entry: ChatEntry) => Message | undefined | Promise - send: (accessToken: string, data: any) => Promise + axiosInstance: AxiosInstance } export interface Bot { @@ -25,4 +26,12 @@ export type VerificationQuery = { export type ChatEntry = MessagingItem & { context: any setContext: (value: Record) => void + getUserFields: () => Promise +} + +export type UserFields = { + id: string + first_name: string + last_name: string + profile_pic: string } diff --git a/test/bot.test.ts b/test/bot.test.ts index a3b93f7..eb843ba 100644 --- a/test/bot.test.ts +++ b/test/bot.test.ts @@ -1,5 +1,6 @@ import {BotConfig, ChatEntry} from '../src/types' import {bot, event, text, textReply} from '../src' +import {AxiosInstance} from 'axios' describe('bot', () => { const evt = event() @@ -14,17 +15,18 @@ describe('bot', () => { }) test(`simple echo bot`, async () => { - const send = jest.fn() + const axiosMock = ({post: jest.fn()} as any) as AxiosInstance + const sendUrl = `https://graph.facebook.com/v11.0/me/messages?access_token=fake-access-token` const handle = ({message}: ChatEntry) => { if (message) { return text(`You said: "${message.text}"`) } return text(`Send me a message and I will send it back to you!`) } - const app = makeBot({send, handle}) + const app = makeBot({axiosInstance: axiosMock, handle}) await app.handle(getStarted) - expect(send).toBeCalledWith('fake-access-token', { + expect(axiosMock.post).toBeCalledWith(sendUrl, { messaging_type: 'RESPONSE', recipient: {id: 'bob'}, message: { @@ -34,7 +36,7 @@ describe('bot', () => { }) await app.handle(evt.text('Yo').get()) - expect(send).toBeCalledWith('fake-access-token', { + expect(axiosMock.post).toBeCalledWith(sendUrl, { messaging_type: 'RESPONSE', recipient: {id: 'bob'}, message: {text: 'You said: "Yo"', quick_replies: undefined}, @@ -42,7 +44,8 @@ describe('bot', () => { }) test(`calculator bot`, async () => { - const send = jest.fn() + const axiosMock = ({post: jest.fn()} as any) as AxiosInstance + const sendUrl = `https://graph.facebook.com/v11.0/me/messages?access_token=fake-access-token` const initialContext = {step: 'start'} const handle = async ({context, setContext, message}: ChatEntry) => { if (context.step === 'start') { @@ -81,10 +84,10 @@ describe('bot', () => { } return undefined } - const app = makeBot({send, handle, initialContext}) + const app = makeBot({axiosInstance: axiosMock, handle, initialContext}) await app.handle(getStarted) - expect(send).toBeCalledWith('fake-access-token', { + expect(axiosMock.post).toBeCalledWith(sendUrl, { messaging_type: 'RESPONSE', recipient: {id: 'bob'}, message: { @@ -99,35 +102,35 @@ describe('bot', () => { }) await app.handle(evt.quickReply('+').get()) - expect(send).toBeCalledWith('fake-access-token', { + expect(axiosMock.post).toBeCalledWith(sendUrl, { messaging_type: 'RESPONSE', recipient: {id: 'bob'}, message: {text: `Please enter the first argument for operation '+'`}, }) await app.handle(evt.text('bla bla').get()) - expect(send).toBeCalledWith('fake-access-token', { + expect(axiosMock.post).toBeCalledWith(sendUrl, { messaging_type: 'RESPONSE', recipient: {id: 'bob'}, message: {text: `Your input is incorrect, try again`}, }) await app.handle(evt.text('11').get()) - expect(send).toBeCalledWith('fake-access-token', { + expect(axiosMock.post).toBeCalledWith(sendUrl, { messaging_type: 'RESPONSE', recipient: {id: 'bob'}, message: {text: `Please enter the second argument for operation '+'`}, }) await app.handle(evt.text('4').get()) - expect(send).toBeCalledWith('fake-access-token', { + expect(axiosMock.post).toBeCalledWith(sendUrl, { messaging_type: 'RESPONSE', recipient: {id: 'bob'}, message: {text: `The result is 15`}, }) await app.handle(evt.text('Thanks!').get()) - expect(send).toBeCalledWith('fake-access-token', { + expect(axiosMock.post).toBeCalledWith(sendUrl, { messaging_type: 'RESPONSE', recipient: {id: 'bob'}, message: {