Skip to content

Commit

Permalink
Improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
webNeat committed Jun 27, 2021
1 parent 54533cd commit db59b96
Show file tree
Hide file tree
Showing 19 changed files with 1,064 additions and 1,082 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,8 @@
"tsdx": "^0.14.1",
"tslib": "^2.3.0",
"typescript": "^4.3.2"
},
"dependencies": {
"axios": "^0.21.1"
}
}
11 changes: 0 additions & 11 deletions src/bot/addStep.ts

This file was deleted.

65 changes: 16 additions & 49 deletions src/bot/handle.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {WebhookEvent, EventEntry} from '../events'
import { MessagingItem } from '../events/types'
import {Message, MessageReply, MessageBuilder} from '../messages'
import {BotConfig, ChatState} from './types'
import {EventEntry, WebhookEvent} from '../events/types'
import {BotConfig} from './types'

export async function handle(config: BotConfig, event: WebhookEvent) {
if (event.object !== 'page') {
Expand All @@ -12,51 +10,20 @@ export async function handle(config: BotConfig, event: WebhookEvent) {

async function handleEntry(config: BotConfig, entry: EventEntry) {
const messaging = entry.messaging[0]
const sender = messaging.sender
const state = await getState(config, sender.id)
const step = await listen(config, state, messaging)
const reply = makeReply(sender, await step.send(state.context))
await Promise.all([config.send(config.accessToken, reply), setState(config, sender.id, state)])
}

async function listen(config: BotConfig, state: ChatState, messaging: MessagingItem) {
if (!state.step) {
state.step = 'start'
return config.steps['start']
}
const {sender, message, postback} = messaging as any
const step = config.steps[state.step]
const setContext = async (value: any) => {
state.context = value
return setState(config, sender.id, state)
}
const nextStep = await step.listen({message, postback, context: state.context, setContext})
if (!nextStep) {
return step
}
if (!config.steps[nextStep]) {
throw `Unknown chat step '${nextStep}'`
}
state.step = nextStep
return config.steps[nextStep]
}

async function getState(config: BotConfig, key: string): Promise<ChatState> {
const value = await config.storage.get(key)
if (value == undefined) {
return {step: '', context: {...config.initialContext}}
const {recipient, sender} = messaging
const {accessToken, storage, send} = config
const contextKey = `${sender.id}-${recipient.id}`
const context = (await storage.get(contextKey)) || config.initialContext
const setContext = (value: any) => storage.set(contextKey, value)
let response = config.handle({...messaging, context, setContext})
if (response instanceof Promise) {
response = await response
}
return JSON.parse(value)
}

async function setState(config: BotConfig, key: string, state: ChatState): Promise<void> {
await config.storage.set(key, JSON.stringify(state))
}

function makeReply(recipient: {id: string}, message: MessageBuilder<Message>): MessageReply {
return {
recipient,
messaging_type: 'RESPONSE',
message: message.get(),
if (response) {
await send(accessToken, {
messaging_type: 'RESPONSE',
recipient: sender,
message: response,
})
}
}
22 changes: 15 additions & 7 deletions src/bot/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import {send} from './send'
import axios from 'axios'
import {handle} from './handle'
import {verify} from './verify'
import {addStep} from './addStep'
import {Bot, BotConfig} from './types'
import {memoryStorage} from '../storages'

const axiosInstance = axios.create()

export function bot(config: Partial<BotConfig>): Bot {
if (!config.accessToken) throw `The accessToken is missing on the bot configuration!`
if (!config.verifyToken) throw `The verifyToken is missing on the bot configuration!`
if (!config.send) config.send = send
if (!config.storage) config.storage = memoryStorage()
if (!config.initialContext) config.initialContext = {}
if (!config.steps) config.steps = {}
if (!config.handle) throw `handle is missing on the bot configuration!`

config = {
initialContext: {},
storage: memoryStorage(),
send: (token, data) =>
axiosInstance.post(
`https://graph.facebook.com/v11.0/me/messages?access_token=${token}`,
data
),
...config,
}

return {
on: (name, step) => addStep(config as BotConfig, name, step),
verify: query => verify(config as BotConfig, query),
handle: event => handle(config as BotConfig, event),
}
Expand Down
19 changes: 0 additions & 19 deletions src/bot/send.ts

This file was deleted.

27 changes: 7 additions & 20 deletions src/bot/types.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import {WebhookEvent, PostbackEvent, MessageEvent} from '../events'
import {Message, MessageBuilder} from '../messages'
import {ContextStorage} from '../storages'
import {Message} from '../messages/types'
import {ContextStorage} from '../storages/types'
import {WebhookEvent, MessagingItem} from '../events/types'

export type BotConfig = {
accessToken: string
verifyToken: string
initialContext: any
steps: Record<string, ChatStep>
storage: ContextStorage
send: (accessToken: string, data: any) => Promise<void>
handle: (entry: ChatEntry) => Message | undefined | Promise<Message | undefined>
send: (accessToken: string, data: any) => Promise<any>
}

export interface Bot {
on(name: string, step: ChatStep): void
verify(query: VerificationQuery): string
handle(event: WebhookEvent): Promise<void>
}
Expand All @@ -23,19 +22,7 @@ export type VerificationQuery = {
'hub.verify_token': string
}

export type ChatStep = {
send: (context: any) => Promise<MessageBuilder<Message>>
listen: (entry: ChatEntry) => Promise<string | void>
}

export type ChatEntry = {
context: any
setContext: (value: Record<string, any>) => Promise<void>
message?: MessageEvent
postback?: PostbackEvent
}

export type ChatState = {
step: string
export type ChatEntry = MessagingItem & {
context: any
setContext: (value: Record<string, any>) => void
}
4 changes: 2 additions & 2 deletions src/events/EventBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ export class EventBuilder extends Builder<WebhookEvent, EventBuilderData> {
return this.message({text: content})
}

quickReply(text: string, payload: string) {
return this.message({text, quick_reply: {payload}})
quickReply(text: string, payload?: string) {
return this.message({text, quick_reply: {payload: payload || text}})
}

replyTo(id: string, text: string) {
Expand Down
2 changes: 0 additions & 2 deletions src/events/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {EventBuilder, EventBuilderData} from './EventBuilder'

export {WebhookEvent, EventEntry, PostbackEvent, MessageEvent} from './types'

export function event(data?: EventBuilderData) {
return new EventBuilder(data || {timestamp: Date.now(), items: []})
}
6 changes: 3 additions & 3 deletions src/events/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ export type EventEntry = {
messaging: [MessagingItem]
}

export type MessagingItem = PostbackOrMessageEvent & {
export type MessagingItem = {
sender: {id: string}
recipient: {id: string}
timestamp: number
message?: MessageEvent
postback?: PostbackEvent
}

type PostbackOrMessageEvent = {postback: PostbackEvent} | {message: MessageEvent}

export type PostbackEvent = {
mid: string
title: string
Expand Down
4 changes: 1 addition & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import {text, audio, file, image, video} from './messages'

export {bot} from './bot'
export {event} from './events'
export const message = {text, audio, file, image, video}
export * from './messages'
9 changes: 0 additions & 9 deletions src/messages/FileMessageBuilder.ts

This file was deleted.

31 changes: 0 additions & 31 deletions src/messages/MessageBuilder.ts

This file was deleted.

Loading

0 comments on commit db59b96

Please sign in to comment.