diff --git a/example/lib/bot.ex b/example/lib/bot.ex index 23ccf5a..5de9ed4 100644 --- a/example/lib/bot.ex +++ b/example/lib/bot.ex @@ -1,7 +1,11 @@ defmodule Example.Bot do use TelegramEx, name: :example_bot, - routers: [Example.Routers.Commands, Example.Routers.Admin, Example.Routers.Survey] + routers: [ + Example.Routers.Commands, + Example.Routers.Admin, + Example.Routers.Survey + ] # ── /start ───────────────────────────────────────────────────────── # Reply keyboard with all available commands @@ -11,9 +15,9 @@ defmodule Example.Bot do ["/html", "/keyboard", "/reply_kb"], ["/photo", "/document", "/sticker"], ["/video", "/voice", "/location"], - ["/contact", "/silent", "/admin"], + ["/contact", "/silent", "/reply_to"], ["/survey", "/poll", "/quiz"], - ["/command_demo", "/echo hello"] + ["/admin", "/command_demo", "/echo hello"] ] ctx @@ -36,6 +40,7 @@ defmodule Example.Bot do /markdown — Markdown formatted message /html — HTML formatted message /silent — message without notification + /reply_to — reply to the command message Keyboards /keyboard — inline keyboard with callbacks @@ -219,6 +224,20 @@ defmodule Example.Bot do |> Message.send(chat["id"]) end + # ── /reply_to ────────────────────────────────────────────────────── + def handle_message(%{text: "/reply_to", chat: chat, message_id: message_id}, ctx) do + ctx + |> Message.text("This message replies to your /reply_to command.") + |> Message.reply_to(message_id) + |> Message.send(chat["id"]) + end + + def handle_message(%{chat: chat, reply: %{text: text}}, ctx) do + ctx + |> Message.text("You replied to message with text:\n#{text}") + |> Message.send(chat["id"]) + end + # ── /admin ───────────────────────────────────────────────────────── # FSM transition → :admin state (handled in Admin router) def handle_message(%{text: "/admin", chat: chat}, ctx) do diff --git a/lib/builders/builder.ex b/lib/builders/builder.ex index d6ca7ce..a2b4e37 100644 --- a/lib/builders/builder.ex +++ b/lib/builders/builder.ex @@ -51,4 +51,30 @@ defmodule TelegramEx.Builder do |> Map.put(key, file) |> then(&Map.put(ctx, :payload, &1)) end + + defmacro __using__(_opts) do + quote do + alias TelegramEx.{API, Builder, Effect} + + @type input :: map() | Effect.t() + + @doc """ + Sends the message without notification sound. + """ + @spec silent(input()) :: Effect.t() + def silent(input) do + Builder.put_payload(input, :disable_notification, true) + end + + @doc """ + Selects message to reply to. + """ + @spec reply_to(input(), String.t()) :: Effect.t() + def reply_to(input, message_id) do + Builder.put_payload(input, :reply_parameters, %{message_id: message_id}) + end + + defoverridable silent: 1, reply_to: 2 + end + end end diff --git a/lib/builders/contact.ex b/lib/builders/contact.ex index 7b56f58..4538a4e 100644 --- a/lib/builders/contact.ex +++ b/lib/builders/contact.ex @@ -6,11 +6,7 @@ defmodule TelegramEx.Builder.Contact do [Messages and Media](messages-and-media.md). """ - alias TelegramEx.API - alias TelegramEx.Builder - alias TelegramEx.Effect - - @type input :: map() | Effect.t() + use TelegramEx.Builder @doc """ Sets contact information with first name and phone number. @@ -33,14 +29,6 @@ defmodule TelegramEx.Builder.Contact do |> Builder.put_payload(:last_name, last_name) end - @doc """ - Sends the contact without notification sound. - """ - @spec silent(input()) :: Effect.t() - def silent(input) do - Builder.put_payload(input, :disable_notification, true) - end - @doc """ Sends the contact to the specified chat. """ diff --git a/lib/builders/document.ex b/lib/builders/document.ex index aae22b7..1423f8e 100644 --- a/lib/builders/document.ex +++ b/lib/builders/document.ex @@ -6,11 +6,7 @@ defmodule TelegramEx.Builder.Document do [Messages and Media](messages-and-media.md). """ - alias TelegramEx.API - alias TelegramEx.Builder - alias TelegramEx.Effect - - @type input :: map() | Effect.t() + use TelegramEx.Builder @doc """ Sets the document from a URL. @@ -96,25 +92,6 @@ defmodule TelegramEx.Builder.Document do |> Builder.put_payload(:parse_mode, parse_mode) end - @doc """ - Sends the document without notification sound. - - Accepts a handler context or an existing `TelegramEx.Effect` and returns an - effect with `:disable_notification` stored in the payload. - - ## Parameters - - - `input` - Context map or effect - - ## Returns - - `TelegramEx.Effect` with `:disable_notification` stored in the payload. - """ - @spec silent(input()) :: Effect.t() - def silent(input) do - Builder.put_payload(input, :disable_notification, true) - end - @doc """ Sends the document to the specified chat. diff --git a/lib/builders/location.ex b/lib/builders/location.ex index 1f7710a..8324a4b 100644 --- a/lib/builders/location.ex +++ b/lib/builders/location.ex @@ -6,11 +6,7 @@ defmodule TelegramEx.Builder.Location do [Messages and Media](messages-and-media.md). """ - alias TelegramEx.API - alias TelegramEx.Builder - alias TelegramEx.Effect - - @type input :: map() | Effect.t() + use TelegramEx.Builder @doc """ Sets the geographic coordinates. @@ -22,14 +18,6 @@ defmodule TelegramEx.Builder.Location do |> Builder.put_payload(:longitude, lng) end - @doc """ - Sends the location without notification sound. - """ - @spec silent(input()) :: Effect.t() - def silent(input) do - Builder.put_payload(input, :disable_notification, true) - end - @doc """ Sends the location to the specified chat. """ diff --git a/lib/builders/message.ex b/lib/builders/message.ex index 649223a..725360a 100644 --- a/lib/builders/message.ex +++ b/lib/builders/message.ex @@ -6,11 +6,7 @@ defmodule TelegramEx.Builder.Message do `sendMessage` request. See [Messages and Media](messages-and-media.md). """ - alias TelegramEx.API - alias TelegramEx.Builder - alias TelegramEx.Effect - - @type input :: map() | Effect.t() + use TelegramEx.Builder @doc """ Sets the text content of the message. @@ -60,14 +56,6 @@ defmodule TelegramEx.Builder.Message do Builder.put_payload(input, :reply_markup, %{remove_keyboard: true}) end - @doc """ - Sends the message without notification sound. - """ - @spec silent(input()) :: Effect.t() - def silent(input) do - Builder.put_payload(input, :disable_notification, true) - end - @doc """ Answers a callback query from an inline keyboard button. diff --git a/lib/builders/photo.ex b/lib/builders/photo.ex index d3db0ac..9354e78 100644 --- a/lib/builders/photo.ex +++ b/lib/builders/photo.ex @@ -6,11 +6,7 @@ defmodule TelegramEx.Builder.Photo do [Messages and Media](messages-and-media.md). """ - alias TelegramEx.API - alias TelegramEx.Builder - alias TelegramEx.Effect - - @type input :: map() | Effect.t() + use TelegramEx.Builder @doc """ Sets the photo from a URL. @@ -48,14 +44,6 @@ defmodule TelegramEx.Builder.Photo do |> Builder.put_payload(:parse_mode, parse_mode) end - @doc """ - Sends the photo without notification sound. - """ - @spec silent(input()) :: Effect.t() - def silent(input) do - Builder.put_payload(input, :disable_notification, true) - end - @doc """ Sends the photo to the specified chat. """ diff --git a/lib/builders/poll.ex b/lib/builders/poll.ex index 7ba90a3..d00e0c9 100644 --- a/lib/builders/poll.ex +++ b/lib/builders/poll.ex @@ -6,11 +6,7 @@ defmodule TelegramEx.Builder.Poll do [Messages and Media](messages-and-media.md). """ - alias TelegramEx.API - alias TelegramEx.Builder - alias TelegramEx.Effect - - @type input :: map() | Effect.t() + use TelegramEx.Builder @spec poll(input(), String.t(), list(String.t())) :: Effect.t() def poll(input, question, options) do @@ -61,11 +57,6 @@ defmodule TelegramEx.Builder.Poll do Builder.put_payload(input, :close_date, timestamp) end - @spec silent(input()) :: Effect.t() - def silent(input) do - Builder.put_payload(input, :disable_notification, true) - end - @spec send(input(), integer()) :: Effect.t() def send(input, id) do input diff --git a/lib/builders/sticker.ex b/lib/builders/sticker.ex index 26f2ca0..7a8080c 100644 --- a/lib/builders/sticker.ex +++ b/lib/builders/sticker.ex @@ -6,11 +6,7 @@ defmodule TelegramEx.Builder.Sticker do [Messages and Media](messages-and-media.md). """ - alias TelegramEx.API - alias TelegramEx.Builder - alias TelegramEx.Effect - - @type input :: map() | Effect.t() + use TelegramEx.Builder @doc """ Sets the sticker by Telegram file ID. @@ -38,14 +34,6 @@ defmodule TelegramEx.Builder.Sticker do Builder.put_file_payload(input, :sticker, path) end - @doc """ - Sends the sticker without notification sound. - """ - @spec silent(input()) :: Effect.t() - def silent(input) do - Builder.put_payload(input, :disable_notification, true) - end - @doc """ Sends the sticker to the specified chat. """ diff --git a/lib/builders/video.ex b/lib/builders/video.ex index ec32c90..b000074 100644 --- a/lib/builders/video.ex +++ b/lib/builders/video.ex @@ -6,11 +6,7 @@ defmodule TelegramEx.Builder.Video do sends. See [Messages and Media](messages-and-media.md). """ - alias TelegramEx.API - alias TelegramEx.Builder - alias TelegramEx.Effect - - @type input :: map() | Effect.t() + use TelegramEx.Builder @doc """ Sets the video by Telegram file ID. @@ -64,14 +60,6 @@ defmodule TelegramEx.Builder.Video do Builder.put_payload(input, :cover, url) end - @doc """ - Sends the video without notification sound. - """ - @spec silent(input()) :: Effect.t() - def silent(input) do - Builder.put_payload(input, :disable_notification, true) - end - @doc """ Sends the video to the specified chat. """ diff --git a/lib/builders/voice.ex b/lib/builders/voice.ex index 0cb64c7..5e90c41 100644 --- a/lib/builders/voice.ex +++ b/lib/builders/voice.ex @@ -6,11 +6,7 @@ defmodule TelegramEx.Builder.Voice do duration, and silent sends. See [Messages and Media](messages-and-media.md). """ - alias TelegramEx.API - alias TelegramEx.Builder - alias TelegramEx.Effect - - @type input :: map() | Effect.t() + use TelegramEx.Builder @doc """ Sets the voice message by Telegram file ID. @@ -64,14 +60,6 @@ defmodule TelegramEx.Builder.Voice do Builder.put_payload(input, :duration, seconds) end - @doc """ - Sends the voice message without notification sound. - """ - @spec silent(input()) :: Effect.t() - def silent(input) do - Builder.put_payload(input, :disable_notification, true) - end - @doc """ Sends the voice message to the specified chat. """ diff --git a/lib/types/message.ex b/lib/types/message.ex index 8794a5c..f5467be 100644 --- a/lib/types/message.ex +++ b/lib/types/message.ex @@ -42,7 +42,7 @@ defmodule TelegramEx.Types.Message do audio: map() | nil, caption: String.t() | nil, message_thread_id: integer() | nil, - reply_to_message: t() | nil + reply: t() | nil } defstruct [ @@ -59,7 +59,7 @@ defmodule TelegramEx.Types.Message do :voice, :caption, :message_thread_id, - :reply_to_message + :reply ] @doc """ @@ -83,7 +83,7 @@ defmodule TelegramEx.Types.Message do video: map["video"], voice: map["voice"], caption: map["caption"], - reply_to_message: Message.from_map(map["reply_to_message"]) + reply: Message.from_map(map["reply_to_message"]) } end end diff --git a/test/builders/builder_test.exs b/test/builders/builder_test.exs new file mode 100644 index 0000000..f772088 --- /dev/null +++ b/test/builders/builder_test.exs @@ -0,0 +1,19 @@ +defmodule Builder do + use TelegramEx.Builder +end + +defmodule TelegramEx.BuilderTest do + use ExUnit.Case + + test "silent/1 adds disable_notification to payload" do + effect = Builder.silent(%{}) + + assert effect.ctx.payload == %{disable_notification: true} + end + + test "reply_to/2 adds reply parameters to payload" do + effect = Builder.reply_to(%{}, 123) + + assert effect.ctx.payload == %{reply_parameters: %{message_id: 123}} + end +end