Skip to content
This repository was archived by the owner on Aug 25, 2023. It is now read-only.

Commit 01eac59

Browse files
committed
Forms!
1 parent eb044f3 commit 01eac59

File tree

6 files changed

+150
-9
lines changed

6 files changed

+150
-9
lines changed

docs/objects/Bot.rst

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,41 @@ This object represents a Telegram bot.
237237
:param string filter: Must be one of the following: ``private``, ``group``, ``supergroup``, ``channel`` or ``all``. Can also be a function that accepts one argument (a :doc:`Chat` object without functions) and returns a boolean value
238238
:param function callback: A function to which is provided a single argument, a promise that resolves to a :doc:`Chat` object.
239239

240-
Iterates a function through all of chats stored in the profiles storage.
240+
Iterates a function through all of chats stored in the profiles storage.
241+
242+
.. js:function:: form(user_id, object, callback)
243+
244+
:param string user_id: Can also be a :doc:`Chat` or a :doc:`User` object. **It must be a private chat!**
245+
:param object object: The form object (see below).
246+
:param function callback: A function that accept one argument
247+
248+
Creates a form for a user. The ``object`` argument must follow this format:
249+
250+
.. code-block:: javascript
251+
252+
{name: {
253+
message: {
254+
text: 'Please enter your name',
255+
options: {}
256+
},
257+
regex: /([A-Z]){1,20}/,
258+
error: {
259+
text: 'Your name must be between 1 and 20 letters long and must contain only letters',
260+
options: {}
261+
}
262+
},
263+
surname: {
264+
message: {
265+
text: 'Please enter your surname',
266+
options: {}
267+
},
268+
regex: /([A-Z]){1,20}/,
269+
error: {
270+
text: 'Your surname must be between 1 and 20 letters long and must contain only letters',
271+
options: {}
272+
}
273+
}
274+
275+
.. js:function:: removeForm(user_id)
276+
277+
:param string user_id: Can also be a :doc:`Chat` or a :doc:`User` object. **It must be a private chat!**

docs/objects/CallbackQuery.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ This object represents a Telegram callback query.
1515

1616
Creates a new CallbackQuery object.
1717

18-
.. js:function:: answerCallbackQuery(text, alert)
18+
.. js:function:: answer(text, alert)
1919

2020
:param string text:
2121
:param boolean alert: Whether the user should be shown an alert

docs/quickstart.rst

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ chat) as an argument.
7171

7272
.. warning::
7373

74-
A ``message`` event will be emitted even if a command or a service message event is triggered! Make sure
74+
A ``message`` event will be emitted even if a service message event is triggered! Make sure
7575
to not produce unintended behaviors
7676

7777
********
@@ -323,4 +323,42 @@ Nodeogram provides a simple way for you and your bot to keep track of the users
323323
its profile management interface. If a bot is instantiated with ``profiles_path`` in its options, it will start saving
324324
basic information about any user and chat he will become aware of.
325325

326-
The ``broadcast()`` function will enable you to easily access that data and interact with your users.
326+
The ``broadcast()`` function will enable you to easily access that data and interact with your users.
327+
328+
=====
329+
Forms
330+
=====
331+
332+
Nodeogram enables you to ask your users questions and receive responses in an ordinated fashion. Creating a form for a user
333+
will make the bot ignore any event for his chat until he answers every question. Once every question is answered, answers
334+
are returned to you via a callback. Nodeogram also makes sure that the answers provided are acceptable for you bot, checking
335+
if they match a regular expression (regex).
336+
337+
.. code-block:: javascript
338+
339+
bot.form(message.from, {
340+
name: {
341+
message: {
342+
text: 'Please enter your name',
343+
options: {}
344+
},
345+
regex: /([A-Z]){1,20}/,
346+
error: {
347+
text: 'Your name must be between 1 and 20 letters long and must contain only letters',
348+
options: {}
349+
}
350+
},
351+
surname: {
352+
message: {
353+
text: 'Please enter your surname',
354+
options: {}
355+
},
356+
regex: /([A-Z]){1,20}/,
357+
error: {
358+
text: 'Your surname must be between 1 and 20 letters long and must contain only letters',
359+
options: {}
360+
}
361+
}
362+
}, (result) => {
363+
message.from.sendMessage(`Your name is ${result.name} ${result.surname}`)
364+
})

examples/redditlivebot/app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
const nodeogram = require('nodeogram'),
33
WebSocketClient = require('websocket').client,
44
config = require('./config.json'),
5-
bot = new nodeogram.Bot(config.token, {profiles_path: __dirname + '/profiles.json', enableHelp: false}),
5+
bot = new nodeogram.Bot(config.token, {profiles_path: __dirname + '/profiles.json', enableHelp: true}),
66
request = require('superagent-promise')(require('superagent'), Promise);
77

88
var threads = {}, // thread_id: [user_id, ...]

lib/Bot.js

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ function Bot(token, options) {
2727
this.saveUsers = true;
2828
this.saveChats = true;
2929
this.enableHelp = true;
30+
this.forms = {}; // user_id: {form, answers}
3031
extend(this, options);
3132

3233
this.handleAPIError = (err) => {
@@ -101,8 +102,12 @@ function Bot(token, options) {
101102
return;
102103
}
103104
var message = new Message(result.message, this);
105+
var handleNext = true;
104106
this.messageHandlers.forEach((handler) => {
105-
handler(message);
107+
if (handleNext) {
108+
var res = handler(message);
109+
handleNext = res === undefined ? true : res;
110+
}
106111
});
107112
}
108113
if (result.inline_query) this.emitter.emit('inline_query', new InlineQuery(result.inline_query, this));
@@ -128,6 +133,25 @@ function Bot(token, options) {
128133
this.commands.push({command: command, description: description, callback: callback, hidden: hidden});
129134
};
130135

136+
this.form = (user_id, object, callback) => {
137+
if (user_id instanceof Chat) user_id = user_id.id;
138+
if (user_id instanceof User) user_id = user_id.id;
139+
if (!this.forms[user_id]) {
140+
this.forms[user_id] = {
141+
form: object,
142+
answers: {},
143+
callback: callback
144+
}
145+
}
146+
this.sendMessage(user_id, object[Object.keys(object)[0]].message.text, object[Object.keys(object)[0]].message.options)
147+
};
148+
149+
this.removeForm = (user_id) => {
150+
if (this.forms[user_id]) {
151+
delete this.forms[user_id]
152+
}
153+
};
154+
131155
this.getChat = (chat_id) => {
132156
return this.call('getChat', {chat_id: chat_id}).then((res) => {
133157
return new Chat(res.body.result, this)
@@ -399,14 +423,17 @@ function Bot(token, options) {
399423
};
400424

401425
this.handleCommands = (message) => {
402-
if (message.commands.length > 0) {
403-
var command = message.commands[0];
426+
var returm = true;
427+
if (message.command) {
428+
var command = message.command;
404429
this.commands.forEach((entry) => {
405430
if (("/" + entry.command) == command.command && (!command.recipient || ("@" + this.me.username) == command.recipient)) {
406431
entry.callback(command.args, message);
432+
returm = false;
407433
}
408434
});
409435
}
436+
return returm;
410437
};
411438

412439
this.handleMessage = (message) => {
@@ -442,6 +469,40 @@ function Bot(token, options) {
442469
})
443470
};
444471

472+
this.handleForms = (message) => {
473+
var id = message.from.id;
474+
if (this.forms[id] && message.chat.type == 'private') {
475+
var next = false;
476+
var wait = false;
477+
for (var key in this.forms[id].form) {
478+
if (!next) {
479+
if (this.forms[id].form.hasOwnProperty(key) && !this.forms[id].answers.hasOwnProperty(key)) {
480+
var question = this.forms[id].form[key];
481+
if (question.regex.test(message.text)) {
482+
this.forms[id].answers[key] = message.text;
483+
next = true;
484+
} else {
485+
message.reply(question.error.text, question.error.options);
486+
wait = true;
487+
break;
488+
}
489+
}
490+
} else {
491+
if (this.forms[id].form.hasOwnProperty(key)) {
492+
message.reply(this.forms[id].form[key].message.text, this.forms[id].form[key].message.options);
493+
wait = true;
494+
}
495+
break;
496+
}
497+
}
498+
if (next == true && wait == false) {
499+
this.forms[id].callback(this.forms[id].answers)
500+
delete this.forms[id];
501+
}
502+
return false;
503+
}
504+
};
505+
445506
this.broadcast = (filter, callback) => {
446507
if (!this.profiles_path) throw new Error("Profiles path is not defined");
447508
if (profiles.chats) {
@@ -460,11 +521,12 @@ function Bot(token, options) {
460521
this.init = () => {
461522
if (!this.token) throw new Error("Invalid token!");
462523
this.messageHandlers = [
524+
this.handleForms,
463525
this.handleCommands,
464526
this.handleMessage
465527
];
466528
if (this.profiles_path) {
467-
this.messageHandlers.push(this.handleProfiles);
529+
this.messageHandlers.unshift(this.handleProfiles);
468530
global.profiles = {users: {}, chats: {}};
469531
try {
470532
global.profiles = require(this.profiles_path)

lib/Message.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ function Message(object, bot) {
2121
this.text_mentions = [];
2222
this.hashtags = [];
2323
this.links = [];
24+
this.command = undefined;
2425
this.bot = bot;
2526

2627
if (this.entities) {
@@ -41,6 +42,9 @@ function Message(object, bot) {
4142
command = rawCommand.substr(0, index);
4243
}
4344
this.commands.push({command: command, recipient: recipient, args: args});
45+
if (offset === 0) {
46+
this.command = {command: command, recipient: recipient, args: args}
47+
}
4448
break;
4549
case 'mention':
4650
this.mentions.push(text);

0 commit comments

Comments
 (0)