-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add fanciness: onCommand #8
Comments
TelegramBot#onText() is a primitive way to match text messages, primitive enough that I do not propose any approach that builds on top of it. It is meant to beginners and simple use cases. We are trying to build a more comprehensive command handler. This means we shall be depending on the I suggest for an approach that does not rely on numerous regular expressions in the matching. We only one regexp to match and extract the following components of a text message:
The prefixes to be used will be determined during construction of the instance, as an option e.g. Also, we would need to determine the bot's name (with TelegramBot#getMe()). Before the operation completes, no polling/webhooking (:laugh:) should be happening. This should avoid the case where we can not process text messages, since the method has failed. We do not want to lose any updates, whatsoever! When the operation successfully completes, polling/webhooking should be initiated! A regexp can be constructed using const prefixes = ["/", "!"].join("");
const bot_name = "my_bot"; // retrieved earlier!
const regexp_string = `^[${prefixes}](\w+)(@${bot_name})?(\s.+)?$`;
const regexp = new RegExp(regexp_string); resulting in a regexp, like Using this single regexp, we are able to match the right commands. We are assured it will only match with the correct prefixes. It will match commands without the bot's name. But if a command includes the bot's name, if it is the correct one, it will be matched. Therefore, internally, we will use a preferable data structure to reserve mapping between commands and callbacks (registered with Tgfancy#onCommand()). This data structure can be chosen to provide better search costs, for example, using binary search, etc. Such a mapping can be visualized like so: const mapping = {
"help": cb1,
"ping": cb2,
// ... more ...
}; That is the general idea. Let's work from there! |
Thanks for response @GochoMugo. I don't see any advantage in not building on top of About no fetching updates before
Hashmap sounds like a perfect match here. do we use object or I agree with other stuff proposed by you, maybe I think I'd implement some things slightly differently, but those are details, we'll see when we'll have some code. |
|
How I see onCommand
We implement 2 functions,
onCommand
andonCommands
, one in term of other.onCommand(command: String, callback: Function)
onCommands(commands: [String], callback: Function)
Now I saw
_.castArray
, using it we could haveonCommand
accept both string and array of strings, what do you think?I think they should be synchronized with
onText
and should respectonlyFirstMatch
; all of my proposals futfill that.I see 3 possible implementations:
1. For eachonCommands
call, we create regex and useonText
to register it.Pros:+ requires no changes to n-t-b-a
Cons:- At least one regex per
onCommands
call; linear searchingonText
handler. We pass a regex which matches everything that looks like a command, something like:Then we use command => callback mapping (object or Map) to get callback we need in amortized constant time.
Problem with it, is that it cannot be currently implemented to work correctly with
onlyFirstMatch
. I think that the right thing to do when there is no callback for command, is to proceed to nextonText
callback, and there is currently no way to do that. When regex matches, no further callbacks are executed, there is no way for a function to tell "yes, it did match, but that doesn't mean I can handle it".We'd just have special constant, that when returned from
onText
callback would mean just thatIt shouldn't be too hard to implement tho, I thought about it before, and I have another use-case, we'd just need to change L112-114 in telegram.js to something like
Pros:
+
O(1)
+ should be quite simple to implement actuallyevery proposal is+ my personal favouritenot sure anymoreCons:
- requires adding a feature to n-t-b-a
RegExp
(ie. implementingexec
method) which would do all the logic which I talked in point 2 inside itself, we'd pass noop as callback. Theexec
method of the object would just return something falsey when there is no callback registered for a command, which is is interpeted as no match, and causes nextonText
RegExp
-like to be checked.Pros:
+
O(1)
+ should be quite simple to implement actuallyevery proposal is+puts almost all of the logic into single object (?)
+ requires no changes to n-t-b-a
Cons:
- hack?
4. Mix of 2 and 3, we have RegExp-like object, which returns function to call and the match object, we call the function with the match object as argument from the
onText
callback.+
O(1) \+puts almost all of the logic into single object (?)
+ requires no changes to n-t-b-a
- hack?
Getting username
That's the problem with writing this.
We can of course require the username in
options
, but... that probably wouldn't feel very well, when we can fetch it fromgetMe
and add 0 configuration.The problem is, it's an async command, and thus, because we set up getting updates immiedetly, in the constructor, and we may end up getting messages to process before we know the username, before we're prepared to handle them.
Solutions?
Provide methods to start listening for updates later
Make(I don't like this one anymore)processUpdate
async (that'd change it's signature) -- store a promise in the bot object, initially it'd bePromise.resolve()
, makeprocessUpdate
schedule processing new updates when the promise is resolved. When callingonCommand
for the first time, we'd then dopromise = Promise.join(this.getMe, promise)
.Put code to start listening in separate method,
listen
(or some other name) and have the constructor call it by default, but provide option to opt-out from calling it in the c-tor, and maybe option toonCommand
to call it whengetMe
resolvesJust ignore the username and handle all commands untilgetMe
resolvesJust ignore the username and handle all commands untilgetMe
resolvesThat method requires changes to n-t-b-a, and it's hard to explain in words, so here's pseudocode:
Do you have other ideas?
Which approaches should we take?
The text was updated successfully, but these errors were encountered: