rockneurotiko/ex_gram

sending two messages in one command

TwistingTwists opened this issue · 12 comments

Aim : when user sends /sum 4+5 bot should be able to reply with two messages

  1. answer to the present question (9)
  2. send a string "do you want this bot to solve more additions? "

--> bot waits for another /sum 50+60 like command.

What I tried?

def handle({:command, :sum, %{chat: %{id: chat_id}, text: text} = msg}, context) do

    # takes "4+5" as `text` and returns 9 (currently, only addition!)
    result =
      text
      |> String.split("+")
      |> Enum.map(fn x -> String.to_integer(x) end)
      |> Enum.sum()

    answer(context, Integer.to_string(result))
    # answer(context, "Would you like to solve more questions?")
    # handle({:command, :start, msg}, context)
  end

approaches that do not work

See the last two lines. both of the below approaches skip sending original answer (9)

  1. after replying with answer(context, Integer.to_string(result)) I tried to add another answer = did not work.
  2. Tried to call handle function which has :start message = did not work either!

suggestion for alternatives to send two or more replies on one command of user.

waiting...

The context works like a Plug connection or a Tesla context, you need to keep all the data in the context and return it at the end if you want it to be executed.

In your example:

def handle({:command, :sum, %{chat: %{id: chat_id}, text: text} = msg}, context) do

    # takes "4+5" as `text` and returns 9 (currently, only addition!)
    result =
      text
      |> String.split("+")
      |> Enum.map(fn x -> String.to_integer(x) end)
      |> Enum.sum()

    context
    |> answer(Integer.to_string(result))
    |> answer("Would you like to solve more questions?")
  end

This will send two messages in order, and then when the user answer you can continue with the flow.

In order to ensure that the bot "understand" the message, you would need to force the answer, or use a keyboard, or something similar, that's up to you and how you want the bot to behave.

Oh nice! I will read more about Plugs and Tesla Adapters. Looks idiomatic Elixir!

bot "understand" the message, you would need to force the answer, or use a keyboard, or something similar, that's up to you and how you want the bot to behave.

did not get the bot 'understand' the message part.

 context
    |> answer(Integer.to_string(result))
    |> answer("Would you like to solve more questions?")

I cannot chain ExGram.send_poll to above because send_poll does not take context as argument.

If I want to send 10 polls once user sends /startquiz with 20 seconds delay after each query, how can I chain it into the context?

The responses that works with context are limited, and are made to make it easier the most common workflows. The current responses implemented can be found here, but they are: answer, answer_callback, answer_inline_query, edit and delete. All the other kind of responses, including send_poll has to be made manually calling to ExGram

So, to send 10 polls with 20 seconds delay, you will have to do it manually. There are much better ways, but a really simple one can be:

# Spawn to create them asynchronously and don't block ExGram messages processing
defp send_polls(polls), do: spawn(fn -> do_send_polls(polls) end)

defp do_send_polls([]), do: :ok 
defp do_send_polls([poll | polls]) do
  apply(ExGram, :send_poll, poll)
  Process.sleep(20_000)
  do_send_polls(polls)
end


# Usage:
# polls = [
#   [chat_id, "Question", options, type: "quiz", correct_option_id: 0]
#   [chat_id, "Question 2", options, type: "quiz", correct_option_id: 0]
# ]
# send_polls(polls)

As I said, this is a really simple way and there are better solutions which you can explore, but that's up to you and how you want to do it.

When I add

# Spawn to create them asynchronously and don't block ExGram messages processing
defp send_polls(polls), do: spawn(fn -> do_send_polls(polls) end)

defp do_send_polls([]), do: :ok 
defp do_send_polls([poll | polls]) do
  apply(ExGram, :send_poll, poll)
  Process.sleep(20_000)
  do_send_polls(polls)
end

to my bot.ex file (at top) and

def handle({:command, :startyquiz, %{chat: %{id: chat_id}}}, context) do
    polls = [
      [chat_id, "Question", ["Option1", "Option2"], type: "quiz", correct_option_id: 0],
      [chat_id, "Question 2", ["Option1", "Option2"], type: "quiz", correct_option_id: 0]
    ]

    send_polls(polls)
  end

I get following error message

08:38:49.158 [error] Process #PID<0.263.0> raised an exception
** (UndefinedFunctionError) function ExGram.send_poll/5 is undefined or private
    (ex_gram 0.23.0) ExGram.send_poll(417851214, "Question", ["Option1", "Option2"], {:type, "quiz"}, {:correct_option_id, 0})
    (rbi_bot 0.1.0) lib/rbi_bot/bot.ex:22: RbiBot.Bot.do_send_polls/1

The last two options need to be part of a keyword list, try with this:

polls = [
      [chat_id, "Question", ["Option1", "Option2"], [type: "quiz", correct_option_id: 0]],
      [chat_id, "Question 2", ["Option1", "Option2"], [type: "quiz", correct_option_id: 0]]
    ]

The important part are that the type and correct_option_id are part of their own list, list like if you were calling the method manually

Sorry, but how I can send text message and after send document?
Only by:

ExGram.send_document(...)
context |> answer(...)

?

I do not want to use caption in document.

@prtngn it wasn't possible with the current code, but I created a branch that adds a new Response to be able to do it.

You can test it like this:

  • Set ex_gram to the branch in mix.exs
{:ex_gram, github: "rockneurotiko/ex_gram", branch: "add_response_answer_document"}
  • Use the new answer_document DSL
context
|> answer_document({:file, "/path/to/your/file"})
|> answer("Message")

The answer_document works with any file (document_id, {:file, path} and {:file, content, file_name}) and you can add all the options that send_document has

|> answer_document(document_id, caption: "Caption", disable_notification: true)

Try the new feature and let me know if it's what you was looking form 😄

send_document

Oh, good )

It's worked only with file path ) I try to send document by id:

    text = "some text"

    context
    |> answer_document("BQACAgIAAxkDAAObY2DK9So_5PWUbSjKWdF7d5csNDkAApglAAKrGwhLkTaEhI8EBroqBA")
    |> answer(text)

And get only message with text.

@prtngn maybe the document id is wrong, expired or not valid, because I just tried that with a new generated document id and it worked correctly.

@prtngn maybe the document id is wrong, expired or not valid, because I just tried that with a new generated document id and it worked correctly.

hm, very strange, but today is worked )

I merged the PR and released the version 0.29 with the changes