Skip to content
Permalink
Browse files

Finite state machine

  • Loading branch information...
julioolvr committed Jun 15, 2016
1 parent 0b93835 commit ffb568314ad5f8f531828246495f0cc1852e0e2c
Showing with 99 additions and 8 deletions.
  1. +1 −0 package.json
  2. +98 −8 src/bot.js
@@ -16,6 +16,7 @@
"babel-preset-es2015": "^6.9.0",
"babel-register": "^6.9.0",
"dotenv": "^2.0.0",
"javascript-state-machine": "^2.3.5",
"node-telegram-bot-api": "^0.23.3"
},
"devDependencies": {
@@ -1,4 +1,43 @@
import TelegramBotClient from 'node-telegram-bot-api'
import StateMachine from 'javascript-state-machine'

function createFsm() {
return StateMachine.create({
initial: 'waitingstart',
final: 'final',
events: [
{ name: 'gotstart', from: 'waitingstart', to: 'waitingname' },
{ name: 'gotname', from: 'waitingname', to: 'echoing' },
{ name: 'gottext', from: 'echoing', to: 'echoing' },
{ name: 'gotstop', from: 'echoing', to: 'confirm' },
{ name: 'confirmed', from: 'confirm', to: 'final' },
{ name: 'cancelled', from: 'confirm', to: 'echoing' },
{ name: 'invalid', from: 'confirm', to: 'confirm' }
]
})
}

function eventFromStateAndMessageText(state, text) {
switch (state) {
case 'waitingstart':
return text === '/start' && 'gotstart'
break
case 'waitingname':
return 'gotname'
break
case 'echoing':
return text === '/stop' ? 'gotstop' : 'gottext'
break
case 'confirm':
if (text === 'yes') {
return 'confirmed'
} else if (text === 'no') {
return 'cancelled'
} else {
return 'invalid'
}
}
}

export default class Bot {
constructor(token) {
@@ -14,16 +53,67 @@ export default class Bot {
}

async respondTo(message) {
let text = text = message.text
let fsm = createFsm()
let lastReply = message

let name
let lastMessage

fsm.ongotstart = () => {
lastMessage = this.client.sendMessage(message.chat.id,
'Let\'s begin! What\'s your name?',
{ reply_markup: JSON.stringify({ force_reply: true }) })
}

fsm.ongotname = (event, from, to, message) => {
name = message.text
lastMessage = this.client.sendMessage(message.chat.id,
`Got it ${name}, I'll begin echoing your replies until you respond with /stop`,
{ reply_markup: JSON.stringify({ force_reply: true }) })
}

fsm.ongottext = (event, from, to, message) => {
lastMessage = this.client.sendMessage(message.chat.id,
`Echoing for ${name}: ${message.text}`,
{ reply_markup: JSON.stringify({ force_reply: true }) })
}

fsm.ongotstop = () => {
lastMessage = this.client.sendMessage(message.chat.id,
'Are you sure you want to stop? (yes/no)',
{ reply_markup: JSON.stringify({ force_reply: true }) })
}

fsm.onconfirmed = () => {
lastMessage = this.client.sendMessage(message.chat.id,
'We\'re done here, see ya!')
}

fsm.oncancelled = () => {
lastMessage = this.client.sendMessage(message.chat.id,
'Alright, going back to echoing',
{ reply_markup: JSON.stringify({ force_reply: true }) })
}

while (text !== 'stop') {
let sentMessage = await this.client.sendMessage(message.chat.id,
`echo: ${text}`,
{ reply_markup: JSON.stringify({ force_reply: true }) })
let reply = await new Promise(resolve => this.client.onReplyToMessage(sentMessage.chat.id, sentMessage.message_id, resolve))
text = reply.text
fsm.oninvalid = () => {
lastMessage = this.client.sendMessage(message.chat.id,
'Sorry, I didn\'t catch that, do you want to cancel? (yes/no)',
{ reply_markup: JSON.stringify({ force_reply: true }) })
}

this.client.sendMessage(message.chat.id, 'Stopping')
while (!fsm.isFinished()) {
let text = lastReply.text
let event = eventFromStateAndMessageText(fsm.current, text)

if (!event || fsm.cannot(event)) {
this.client.sendMessage(message.chat.id, 'I wasn\'t expecting that, try /start')
break
}

fsm[event](lastReply)

let sentMessage = await lastMessage
lastReply = await new Promise(resolve => this.client.onReplyToMessage(sentMessage.chat.id, sentMessage.message_id, resolve))
}
}
}

0 comments on commit ffb5683

Please sign in to comment.
You can’t perform that action at this time.