Skip to content
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

Conversations/dialogues #329

Closed
sinnrrr opened this issue Oct 28, 2020 · 13 comments
Closed

Conversations/dialogues #329

sinnrrr opened this issue Oct 28, 2020 · 13 comments

Comments

@sinnrrr
Copy link

sinnrrr commented Oct 28, 2020

Is there any ability to handle user input?
For example, request user to enter his name and handle it.

@demget
Copy link
Collaborator

demget commented Oct 28, 2020

Check out and try OnText event

@sinnrrr
Copy link
Author

sinnrrr commented Oct 29, 2020

OnText event fires every time user textes to bot. My task is just to ask two questions one-by-one and get for them answers. There is Rust telegram framework, where this is realized: https://github.com/teloxide/teloxide#dialogues-management

@demget
Copy link
Collaborator

demget commented Oct 29, 2020

You have to handle the input on your own. As you have the state of the user, you know in your OnText handler what is the bot waiting for and what kind of data it needs to store. The small example is here: #247 (comment)

However, we also planned to integrate similar to teloxide's approach in handling input, briefly described in #288. It is expected to appear in v3.

@sinnrrr
Copy link
Author

sinnrrr commented Oct 29, 2020

cool, thanks a lot!
telebot is so simple and powerful, that I prefer it more then any other telegram bot framework, keep it up!

@sinnrrr sinnrrr closed this as completed Oct 29, 2020
@and3rson
Copy link

and3rson commented Jun 21, 2021

@demget What if the data expected from user is mixed, e. g. photos, texts & inline callbacks?

Example:

Bot: Send me a photo.
User: <sends a photo>
Bot: Enter the description for the photo.
User: <types text and sends it>
Bot: Is everything correct? <YES> <NO>
User: Clicks <YES>
Bot: Thanks!

I feel like this could be done with some catch-all handler but then the entire application would be a single handler function with a huge batch of IF-s.

Any ideas if it would be possible to have some sort of "sub-handlers"? E. g. reroute the message within the handler into one of few child handlers based on the data received & the state of the user. This could be achieved by allowing handlers to be structs that contain CanHandle & Handle properties.

@sinnrrr
Copy link
Author

sinnrrr commented Jun 21, 2021

@demget What if the data expected from user is mixed, e. g. photos, texts & inline callbacks?

Example:

Bot: Send me a photo.
User: <sends a photo>
Bot: Enter the description for the photo.
User: <types text and sends it>
Bot: Is everything correct? <YES> <NO>
User: Clicks <YES>
Bot: Thanks!

idk if the dialogue system is done already, but back in my time, I used database to store current dialogue level (you can use whatever you want: file, redis, etc.), and, depending on that level, responded with different responses

explanation:
User: click register inline button
Server: sets the dialogue level to 1
Server: if dialogue level is 1, than respond with date time picker
User: selects date
Server: sets dialogue level to 2
Server: responds with name question
User: respond with the name
Server: checks if the dialogue have another levels (or here you can think of custom logic) and finishes dialogue with some response

you can also do some sort of middlewares yourself to validate input and other fancy stuff :)

but check for a dialogue system in docs, because the repo creator said it should be done until v3 been released

@and3rson
Copy link

and3rson commented Jun 22, 2021

@sinnrrr @demget Actually a lot of this would be achievable if there was an overloaded version of Handle like this:

type Handler struct {
	CanHandle func(m *tb.Message) bool
	Handle    func(m *tb.Message)
}

This way, it would be possible to build any sort of complexity like this:

// Handler that waits for a photo, than asks for a description, that asks the user to confirm submission.
// "step" can be stored in a database.
bot.Handle(Handler{
	CanHandle: func(m *tb.Message) bool {
		switch step {
		case "upload_photo":
			return m.Photo != nil
		case "enter_description":
			return m.Text != ""
		case "confirm":
			return m.InlineID != ""
		default:
			return false // We're done (or not yet started) here, go to the next handler
		}
	},
	Handle: func(m *tb.Message) {
		switch step {
		case "upload_photo":
			processPhoto(m) // Store photo, send "Enter photo description:" message to user
			step = "enter_description"
		case "enter_description":
			processDescription(m) // Store description, send "Are you sure you want to submit this photo? [Save]/[Cancel]" buttons to user
			step = "confirm"
		case "confirm":
			processConfirm(m) // Send "Thank you!" message to user
			step = ""
		}
	},
})

EDIT: It would be even cooler if Message itself would contain Type property that would contain exact type of the message, so that the function itself could then do clear checks for an incoming message type. Also having "event type" as first argument to bot.Handle seems very limiting. For example, int python-telegram-bot every handler stores the "filters" (I. e. which message types it can handle) in itself, thus allowing unlimited nesting. In contrast, telebot stores the event-type-to-handler globally which makes it harder to build complex nested handlers.

@sinnrrr
Copy link
Author

sinnrrr commented Jun 22, 2021

@sinnrrr @demget Actually a lot of this would be achievable if there was an overloaded version of Handle like this:

type Handler struct {
	CanHandle func(m *tb.Message) bool
	Handle    func(m *tb.Message)
}

This way, it would be possible to build any sort of complexity like this:

	bot.Handle(Handler{
		CanHandle: func(m *tb.Message) bool {
			switch step {
			case "upload_photo":
				return m.Photo != nil
			case "enter_description":
				return m.Text != ""
			case "confirm":
				return m.InlineID != ""
			default:
				return false // We're done (or not yet started) here, go to the next handler
			}
		},
		Handle: func(m *tb.Message) {
			switch step {
			case "upload_photo":
				processPhoto(m)
				step = "enter_description"
			case "enter_description":
				processDescription(m)
				step = "confirm"
			case "confirm":
				processConfirm(m)
				step = ""
			}
		},
	})

It would be even cooler if Message itself would contain Type property that would contain exact type of the message, so that the function itself could then do clear checks for an incoming message type.

you can see how I had realised this in my repo: https://github.com/sinnrrr/schoolhelper

@and3rson
Copy link

and3rson commented Jun 22, 2021

@sinnrrr Thanks for the example. It really looks good however it doesn't allow the bot to accept different types of messages on different stages/levels/whatever-you-call-them. :)

@sinnrrr
Copy link
Author

sinnrrr commented Jun 22, 2021

@sinnrrr Looks good however it doesn't allow the bot to accept different types of messages on different stages.

I'm not really sure what do you mean by "different type of messages". It's just the way you define how you handle dialogue level (switch or if construction), and therefore you can do everything you want.

By the way, you should also have a look at the link, which is in the description of the issue to the rust library for telegram bots. The dialogue system is just so elegant there.

@and3rson
Copy link

and3rson commented Jun 22, 2021

@sinnrrr What I mean is https://github.com/sinnrrr/schoolhelper/blob/master/dialogue.go#L37 - won't the "OnText" event allow the dialog to handle only text inputs?

Edit (offtop): looks like your bot really loves Ukrainian "salo" (I do, too) :D

@sinnrrr
Copy link
Author

sinnrrr commented Jun 22, 2021

@sinnrrr What I mean is https://github.com/sinnrrr/schoolhelper/blob/master/dialogue.go#L37 - won't the "OnText" event allow the dialog to handle only text inputs?

yes, I understood what did you mean

I think it should be realized using levels, but not just 0 or 1 as an index, but text1 or inline2, that way tou can add more events like onInline (idk if that exists xD) and than switchcasing.

but that is a real crap))

@demget
Copy link
Collaborator

demget commented Jun 22, 2021

@demget What if the data expected from user is mixed, e. g. photos, texts & inline callbacks?

In v3 there is a single Context for any kind of update so you can handle anything.

func onHello(c tele.Context) error {
  if c.Callback() != nil {
    return c.Edit("Hello!")
  }
  return c.Send("Hello!")
  
  // or simply:
  return c.EditOrSend("Hello!")
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants