-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
219 lines (193 loc) · 6.58 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
const BootBot = require('bootbot');
const config = require('config');
const api = require('./lib/coinranking-api.js');
const constants = require('./config/constants');
const port = process.env.PORT || config.get('PORT');
const bot = new BootBot({
accessToken: config.get('ACCESS_TOKEN'),
verifyToken: config.get('VERIFY_TOKEN'),
appSecret: config.get('APP_SECRET')
});
/*
React to greeting by user
*/
bot.hear(constants.GREETINGS, (payload, chat) => {
introduction(chat)
});
/*
React to icebreaker response
*/
bot.on('postback:ICEBREAKER', (payload, chat) => {
introduction(chat)
});
// 'Surprise me!' button click handler
bot.on('postback:RANDOM_FACT', (payload, chat) => {
chat.conversation((conversation) => {
sayFact(chat, conversation)
});
});
function introduction(chat) {
chat.say('Greetings!');
setTimeout(() => {
showMenu(chat)
}, 250)
}
bot.hear(['menu'], (payload, chat) => {
showMenu(chat)
});
// 'Who are you?' button click handler
bot.on('postback:ABOUT', (payload, chat) => {
chat.say('I am CryptoBot - I have access to Coinranking API, so I know a lot of crypto coins. I can tell them their actual price or some basic info abou them. I also know some random facts, be sure to check them out!');
setTimeout(() => {
chat.conversation((conversation) => {
endConversation(chat, conversation)
});
}, 250)
});
// 'Surprise me!' button click handler
bot.on('postback:RANDOM_FACT', (payload, chat) => {
chat.conversation((conversation) => {
sayFact(chat, conversation)
});
});
// 'Tell me about...' button click handler
bot.on('postback:TOPICS', (payload, chat) => {
api.getCoinsIndex().then((data) => {
chat.say(`I can tell you something about ${data.coins.length} different coins! I know their actual price, description, and I can even show you their logo!`);
setTimeout(() => {
chat.say({
text: 'What would you like to know?',
buttons: [
{type: 'postback', title: 'Price of a coin', payload: 'PRICE'},
{type: 'postback', title: 'Description of a coin', payload: 'DESCRIPTION'}
]
});
}, 250)
})
});
// 'Description of a coin' button click handler
bot.on('postback:DESCRIPTION', (payload, chat) => {
chat.conversation((conversation) => {
getCoinNameOrSymbol(chat, conversation, coinDescription)
});
});
// 'Price of a coin' button click handler
bot.on('postback:PRICE', (payload, chat) => {
chat.conversation((conversation) => {
getCoinNameOrSymbol(chat, conversation, coinPrice)
});
});
function coinPrice(chat, conversation, coinNameOrCode) {
api.getCoinsIndex().then((data) => {
let coinData = findCoinData(data, coinNameOrCode);
if (coinData) {
api.getCoinPrice(coinData.uuid).then((coinPrice) => {
chat.say(`Actual price of ${coinData.name} is ${coinPrice} USD`);
setTimeout(() => {
endConversation(chat, conversation)
}, 500);
})
} else {
handleCoinNotFound(chat, conversation, getCoinNameOrSymbol, coinPrice)
}
}
)
}
/*
Get coin name or symbol from user and handle it to callback function for processing
*/
function getCoinNameOrSymbol(chat, conversation, callback) {
conversation.ask(`What is the name or symbol of the coin?`, (payload, conversation) => {
callback(chat, conversation, payload.message.text);
});
}
/*
Reply with random fact and later call function to ask if another one is wanted
*/
function sayFact(chat, conversation) {
chat.say(constants.RANDOM_FACTS[Math.floor(Math.random() * constants.RANDOM_FACTS.length)]);
setTimeout(() => {
askIfUserWantsAnotherFact(chat, conversation)
}, 2000)
}
/*
Ask user if he would like to hear another fact
*/
function askIfUserWantsAnotherFact(chat, conversation) {
conversation.ask({
text: "Would you like to hear next one?",
quickReplies: ["Yes", "No"],
options: {typing: true}
}, (payload, conversation) => {
if (payload.message.text === "Yes") {
sayFact(chat, conversation)
} else {
endConversation(chat, conversation)
}
});
}
function handleCoinNotFound(chat, conversation, retryCallback, callback2) {
conversation.ask({
text: "I don't know this coin. Try another?",
quickReplies: ["Yes", "No"],
options: {typing: true}
}, (payload, conversation) => {
if (payload.message.text === "Yes") {
retryCallback(chat, conversation, callback2)
} else {
endConversation(chat, conversation)
}
})
}
function findCoinData(data, coinNameOrCode) {
let downcasedCoin = coinNameOrCode.toLowerCase();
return data.coins.find(coin => coin.code.toLowerCase() === downcasedCoin || coin.name.toLowerCase() === downcasedCoin);
}
function coinDescription(chat, conversation, coinNameOrCode) {
api.getCoinsIndex().then((data) => {
let downcasedCoinNameOrCode = coinNameOrCode.toLowerCase();
let coinData = data.coins.find(coin => coin.code.toLowerCase() === downcasedCoinNameOrCode || coin.name.toLowerCase() === downcasedCoinNameOrCode);
if (coinData) {
api.getCoinDescription(coinData.uuid).then((coinDescription) => {
// Original description has HTML tags, we don't want them in chat - just remove them.
let description = coinDescription.replace(/(<([^>]+)>)/gi, "");
// Description is often quite big and breaches chat limitation to 2000 chars in message. So the solution is to send
// each paragraph (delimited by newline) separately. It is not error proof, as there can be paragraph bigger than the limit, lets
// hope there actually isn't any.
description.split("\n").forEach(function (paragraph, index) {
// Avoid mixed order of messages by sending them with 250ms delay
setTimeout(() => {
chat.say(paragraph)
}, 250 + index * 250)
})
})
} else {
handleCoinNotFound(chat, conversation, getCoinNameOrSymbol, coinDescription)
}
}
)
}
/*
Gracefully end conversation
*/
function endConversation(chat, conversation) {
chat.say("If you need anything else, just say 'menu', I will try to help you.");
conversation.end();
}
/*
Show main actions menu
*/
function showMenu(chat) {
chat.say({
text: 'What can I do for you?',
buttons: [
{type: 'postback', title: 'Who are you?', payload: 'ABOUT'},
{type: 'postback', title: 'Tell me about...', payload: 'TOPICS'},
{type: 'postback', title: 'Surprise me!', payload: 'RANDOM_FACT'}
]
});
}
// Set greeting text - shown at first user interaction with the bot.
bot.setGreetingText('Welcome!');
// Start the bot!
bot.start(port);