-module(telega@keyboard). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/telega/keyboard.gleam"). -export([new/1, hear/1, to_markup/1, one_time/1, persistent/1, resized/1, placeholder/2, selected/1, row/1, inline_row/1, grid/2, new_inline/1, inline_grid/2, remove/0, single/1, inline_single/1, builder/0, inline_builder/0, button/1, text/2, contact_button/1, contact/2, location_button/1, location/2, web_app_button/2, web_app/3, next_row/1, build/1, inline_button/2, inline_text/3, inline_url_button/2, inline_url/3, inline_web_app_button/2, inline_web_app/3, inline_switch_query_button/2, inline_switch_query/3, inline_copy_text_button/2, inline_copy_text/3, inline_next_row/1, inline_build/1, inline_to_markup/1, poll_button/2, users_button/8, chat_button/12, to_inline_markup/1, inline_raw_callback_button/2, inline_switch_query_current_chat_button/2, filter_inline_keyboard_query/1, new_callback_data/3, set_callback_data_delimiter/2, pack_callback/2, unpack_callback/2, string_callback_data/1, int_callback_data/1, bool_callback_data/1]). -export_type([keyboard/0, keyboard_builder/0, inline_keyboard_builder/0, inline_keyboard/0, keyboard_callback_data/1, keyboard_callback/1]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " # Keyboard Module\n" "\n" " This module provides comprehensive keyboard functionality for Telegram bots, including:\n" " - Regular reply keyboards with special buttons (contact, location, polls, etc.)\n" " - Inline keyboards with callback data, URLs, and other interactive elements\n" " - Helper functions for creating grid layouts and complex keyboard structures\n" " - Type-safe callback data handling with validation\n" " - Error handling and validation for Telegram API constraints\n" "\n" " ## Quick Start\n" "\n" " ### Basic Reply Keyboard\n" " ```gleam\n" " // Traditional array-based approach\n" " let keyboard = keyboard.new([\n" " [keyboard.button(\"Option 1\"), keyboard.button(\"Option 2\")],\n" " [keyboard.contact_button(\"📞 Share Contact\"), keyboard.location_button(\"📍 Location\")],\n" " ])\n" " |> keyboard.one_time()\n" " |> keyboard.resized()\n" "\n" " // New builder pattern approach (recommended)\n" " let keyboard = keyboard.builder()\n" " |> keyboard.text(\"Option 1\")\n" " |> keyboard.text(\"Option 2\")\n" " |> keyboard.next_row()\n" " |> keyboard.contact(\"📞 Share Contact\")\n" " |> keyboard.location(\"📍 Location\")\n" " |> keyboard.build()\n" " |> keyboard.one_time()\n" " |> keyboard.resized()\n" " ```\n" "\n" " ### Inline Keyboard with Callbacks\n" " ```gleam\n" " let callback_data = keyboard.string_callback_data(\"action\")\n" "\n" " // Traditional approach\n" " let assert Ok(button) = keyboard.inline_button(\"Click Me\",\n" " keyboard.pack_callback(callback_data, \"click\"))\n" " let keyboard = keyboard.new_inline([[\n" " button,\n" " keyboard.inline_url_button(\"Visit\", \"https://example.com\"),\n" " ]])\n" "\n" " // Builder pattern approach (recommended)\n" " let callback = keyboard.pack_callback(callback_data, \"click\")\n" " let assert Ok(keyboard) = {\n" " use builder <- result.try(keyboard.inline_builder()\n" " |> keyboard.inline_text(\"Click Me\", callback))\n" " let builder = keyboard.inline_url(builder, \"Visit\", \"https://example.com\")\n" " Ok(keyboard.inline_build(builder))\n" " }\n" "\n" " // In your handler:\n" " let assert Ok(filter) = keyboard.filter_inline_keyboard_query(keyboard)\n" " ```\n" "\n" " ### Grid Layouts\n" " ```gleam\n" " let buttons = [\n" " keyboard.button(\"1\"), keyboard.button(\"2\"),\n" " keyboard.button(\"3\"), keyboard.button(\"4\"),\n" " ]\n" " let grid_keyboard = keyboard.grid(buttons, 2) // 2x2 grid\n" " ```\n" "\n" " ## Special Button Types\n" "\n" " - **Contact buttons**: Request user's phone number\n" " - **Location buttons**: Request user's location\n" " - **Poll buttons**: Allow users to create polls\n" " - **Web App buttons**: Launch mini-apps\n" " - **User/Chat request buttons**: Request access to users or chats\n" "\n" " ## Inline Button Types\n" "\n" " - **Callback buttons**: Execute bot commands with data\n" " - **URL buttons**: Open external links\n" " - **Switch inline buttons**: Switch to inline mode\n" " - **Copy text buttons**: Copy text to clipboard\n" " - **Web app buttons**: Launch inline web applications\n" "\n" " ## Error Handling\n" "\n" " The module uses Result types for operations that can fail:\n" " - Callback data validation (max 64 bytes)\n" " - Inline button creation with validation\n" " - Filter creation for callback queries\n" "\n" " ## Builder Pattern API\n" "\n" " The keyboard module provides a fluent builder API similar to Grammy/GramIO:\n" "\n" " ### Reply Keyboard Builder\n" " ```gleam\n" " let keyboard = keyboard.builder()\n" " |> keyboard.text(\"Yes\")\n" " |> keyboard.text(\"No\")\n" " |> keyboard.next_row()\n" " |> keyboard.contact(\"📞 Contact\")\n" " |> keyboard.location(\"📍 Location\")\n" " |> keyboard.next_row()\n" " |> keyboard.web_app(\"Open App\", \"https://example.com\")\n" " |> keyboard.build()\n" " |> keyboard.one_time()\n" " ```\n" "\n" " ### Inline Keyboard Builder\n" " ```gleam\n" " let callback_data = keyboard.string_callback_data(\"menu\")\n" "\n" " let result = keyboard.inline_builder()\n" " |> keyboard.inline_text(\"Settings\", keyboard.pack_callback(callback_data, \"settings\"))\n" " |> result.try(keyboard.inline_text(_, \"About\", keyboard.pack_callback(callback_data, \"about\")))\n" " |> result.try(fn(builder) {\n" " let builder = keyboard.inline_next_row(builder)\n" " let builder = keyboard.inline_url(builder, \"Help\", \"https://help.example.com\")\n" " Ok(keyboard.inline_build(builder))\n" " })\n" " ```\n" "\n" " ## Best Practices\n" "\n" " 1. **Use builder pattern** for complex keyboards (recommended over arrays)\n" " 2. **Use typed callback data** for better type safety\n" " 3. **Validate callback data length** before creating buttons\n" " 4. **Use grid layouts** for better UX with many buttons\n" " 5. **Handle Result types** properly when using validation functions\n" " 6. **Use one-time keyboards** for single-use interactions\n" "\n" " ## Advanced Usage Patterns\n" "\n" " ### Dynamic Keyboards\n" " ```gleam\n" " // Create keyboards based on data\n" " pub fn create_user_list_keyboard(users: List(User)) -> InlineKeyboard {\n" " let user_callback = keyboard.int_callback_data(\"select_user\")\n" " let buttons = list.filter_map(users, fn(user) {\n" " let callback = keyboard.pack_callback(user_callback, user.id)\n" " case keyboard.inline_button(user.name, callback) {\n" " Ok(button) -> Some(button)\n" " Error(_) -> None\n" " }\n" " })\n" " keyboard.inline_grid(buttons, 2) // 2 columns\n" " }\n" " ```\n" "\n" " ### Pagination Pattern\n" " ```gleam\n" " pub fn create_pagination_keyboard(\n" " current_page: Int,\n" " total_pages: Int,\n" " ) -> InlineKeyboard {\n" " let page_callback = keyboard.int_callback_data(\"page\")\n" " let buttons = []\n" "\n" " // Build buttons list\n" " let buttons = case current_page > 1 {\n" " True -> {\n" " let prev_callback = keyboard.pack_callback(page_callback, current_page - 1)\n" " case keyboard.inline_button(\"← Previous\", prev_callback) {\n" " Ok(prev_btn) -> [prev_btn, ..buttons]\n" " Error(_) -> buttons\n" " }\n" " }\n" " False -> buttons\n" " }\n" "\n" " // Add page indicator\n" " let page_info = int.to_string(current_page) <> \"/\" <> int.to_string(total_pages)\n" " let info_btn = keyboard.inline_copy_text_button(page_info, page_info)\n" " let buttons = [info_btn, ..buttons]\n" "\n" " // Add next button\n" " let buttons = case current_page < total_pages {\n" " True -> {\n" " let next_callback = keyboard.pack_callback(page_callback, current_page + 1)\n" " case keyboard.inline_button(\"Next ->\", next_callback) {\n" " Ok(next_btn) -> [next_btn, ..buttons]\n" " Error(_) -> buttons\n" " }\n" " }\n" " False -> buttons\n" " }\n" "\n" " keyboard.new_inline([list.reverse(buttons)])\n" " }\n" " ```\n" "\n" " ### Error Handling Best Practices\n" " ```gleam\n" " // Always handle Result types properly\n" " case keyboard.inline_button(\"Action\", callback) {\n" " Ok(button) -> {\n" " let keyboard = keyboard.inline_single(button)\n" " reply.with_markup(ctx, \"Choose action:\", keyboard.to_inline_markup(keyboard))\n" " }\n" " Error(msg) -> {\n" " // Log the error and provide fallback\n" " logging.log(logging.Error, \"Keyboard error: \" <> msg)\n" " reply.with_text(ctx, \"Sorry, something went wrong.\")\n" " }\n" " }\n" " ```\n" "\n" " ### Performance Tips\n" " - Use `grid()` and `inline_grid()` for better organization\n" " - Keep callback data under 64 bytes (validated automatically)\n" " - Use typed callback data for better type safety\n" " - Cache callback data configurations to avoid recreation\n" " - Consider using `one_time()` keyboards for single interactions\n" ). -opaque keyboard() :: {keyboard, list(list(telega@model@types:keyboard_button())), gleam@option:option(boolean()), gleam@option:option(boolean()), gleam@option:option(boolean()), gleam@option:option(binary()), gleam@option:option(boolean())}. -opaque keyboard_builder() :: {keyboard_builder, list(list(telega@model@types:keyboard_button())), list(telega@model@types:keyboard_button())}. -opaque inline_keyboard_builder() :: {inline_keyboard_builder, list(list(telega@model@types:inline_keyboard_button())), list(telega@model@types:inline_keyboard_button())}. -opaque inline_keyboard() :: {inline_keyboard, list(list(telega@model@types:inline_keyboard_button()))}. -opaque keyboard_callback_data(AVKV) :: {keyboard_callback_data, binary(), fun((AVKV) -> binary()), fun((binary()) -> AVKV), binary()}. -type keyboard_callback(AVKW) :: {keyboard_callback, AVKW, binary(), binary(), keyboard_callback_data(AVKW)}. -file("src/telega/keyboard.gleam", 258). ?DOC( " Create a new reply keyboard from a list of button rows.\n" "\n" " ## Example\n" " ```gleam\n" " let keyboard = keyboard.new([\n" " [keyboard.button(\"Yes\"), keyboard.button(\"No\")],\n" " [keyboard.button(\"Cancel\")],\n" " ])\n" " ```\n" ). -spec new(list(list(telega@model@types:keyboard_button()))) -> keyboard(). new(Buttons) -> {keyboard, Buttons, none, none, none, none, none}. -file("src/telega/keyboard.gleam", 284). ?DOC( " Extract button texts from a keyboard for use with `telega.wait_hears()`.\n" " This is useful for listening to button presses in conversation flows.\n" "\n" " ## Example\n" " ```gleam\n" " let keyboard = keyboard.new([[keyboard.button(\"Yes\"), keyboard.button(\"No\")]])\n" " let hears = keyboard.hear(keyboard)\n" "\n" " use _, text <- telega.wait_hears(ctx:, hears:, or: None, timeout: None)\n" " case text {\n" " \"Yes\" -> // handle yes\n" " \"No\" -> // handle no\n" " _ -> // handle other\n" " }\n" " ```\n" ). -spec hear(keyboard()) -> telega@bot:hears(). hear(Keyboard) -> _pipe = erlang:element(2, Keyboard), _pipe@1 = gleam@list:flat_map( _pipe, fun(Row) -> gleam@list:map(Row, fun(Button) -> erlang:element(2, Button) end) end ), {hear_texts, _pipe@1}. -file("src/telega/keyboard.gleam", 291). ?DOC(" Build a reply markup for `Message` from a keyboard\n"). -spec to_markup(keyboard()) -> telega@model@types:send_message_reply_markup_parameters(). to_markup(Keyboard) -> {send_message_reply_reply_keyboard_markup_parameters, {reply_keyboard_markup, erlang:element(2, Keyboard), erlang:element(3, Keyboard), erlang:element(4, Keyboard), erlang:element(5, Keyboard), erlang:element(6, Keyboard), erlang:element(7, Keyboard)}}. -file("src/telega/keyboard.gleam", 303). ?DOC(" Make the keyboard one-time\n"). -spec one_time(keyboard()) -> keyboard(). one_time(Keyboard) -> {keyboard, erlang:element(2, Keyboard), erlang:element(3, Keyboard), erlang:element(4, Keyboard), {some, true}, erlang:element(6, Keyboard), erlang:element(7, Keyboard)}. -file("src/telega/keyboard.gleam", 308). ?DOC(" Make the keyboard persistent\n"). -spec persistent(keyboard()) -> keyboard(). persistent(Keyboard) -> {keyboard, erlang:element(2, Keyboard), {some, true}, erlang:element(4, Keyboard), erlang:element(5, Keyboard), erlang:element(6, Keyboard), erlang:element(7, Keyboard)}. -file("src/telega/keyboard.gleam", 313). ?DOC(" Make the keyboard resizable\n"). -spec resized(keyboard()) -> keyboard(). resized(Keyboard) -> {keyboard, erlang:element(2, Keyboard), erlang:element(3, Keyboard), {some, true}, erlang:element(5, Keyboard), erlang:element(6, Keyboard), erlang:element(7, Keyboard)}. -file("src/telega/keyboard.gleam", 318). ?DOC(" Set the placeholder for the input field\n"). -spec placeholder(keyboard(), binary()) -> keyboard(). placeholder(Keyboard, Text) -> {keyboard, erlang:element(2, Keyboard), erlang:element(3, Keyboard), erlang:element(4, Keyboard), erlang:element(5, Keyboard), {some, Text}, erlang:element(7, Keyboard)}. -file("src/telega/keyboard.gleam", 324). ?DOC( " Make the keyboard selective.\n" " Use this parameter if you want to show the keyboard to specific users only.\n" ). -spec selected(keyboard()) -> keyboard(). selected(Keyboard) -> {keyboard, erlang:element(2, Keyboard), erlang:element(3, Keyboard), erlang:element(4, Keyboard), erlang:element(5, Keyboard), erlang:element(6, Keyboard), {some, true}}. -file("src/telega/keyboard.gleam", 331). ?DOC(" Create a keyboard row from a list of buttons\n"). -spec row(list(telega@model@types:keyboard_button())) -> list(telega@model@types:keyboard_button()). row(Buttons) -> Buttons. -file("src/telega/keyboard.gleam", 336). ?DOC(" Create an inline keyboard row from a list of buttons\n"). -spec inline_row(list(telega@model@types:inline_keyboard_button())) -> list(telega@model@types:inline_keyboard_button()). inline_row(Buttons) -> Buttons. -file("src/telega/keyboard.gleam", 369). ?DOC(" Split a list into chunks of specified size\n"). -spec chunk_list(list(AVLF), integer()) -> list(list(AVLF)). chunk_list(List, Size) -> case List of [] -> []; _ -> {Chunk, Rest} = gleam@list:split(List, Size), [Chunk | chunk_list(Rest, Size)] end. -file("src/telega/keyboard.gleam", 354). ?DOC( " Create a grid keyboard with buttons arranged in rows of specified width.\n" " This is useful for creating organized layouts with many buttons.\n" "\n" " ## Example\n" " ```gleam\n" " let buttons = [\n" " keyboard.button(\"1\"), keyboard.button(\"2\"), keyboard.button(\"3\"),\n" " keyboard.button(\"4\"), keyboard.button(\"5\"), keyboard.button(\"6\"),\n" " ]\n" " let grid_keyboard = keyboard.grid(buttons, 3) // 3 columns per row\n" " // Results in: [[1, 2, 3], [4, 5, 6]]\n" " ```\n" ). -spec grid(list(telega@model@types:keyboard_button()), integer()) -> keyboard(). grid(Buttons, Columns) -> Rows = chunk_list(Buttons, Columns), new(Rows). -file("src/telega/keyboard.gleam", 724). ?DOC( " Create a new inline keyboard from a list of button rows.\n" " Inline keyboards appear directly below messages and don't replace the user's keyboard.\n" "\n" " ## Example\n" " ```gleam\n" " let callback_data = keyboard.string_callback_data(\"action\")\n" " let assert Ok(callback_btn) = keyboard.inline_button(\"Click\",\n" " keyboard.pack_callback(callback_data, \"click\"))\n" "\n" " let keyboard = keyboard.new_inline([\n" " [callback_btn, keyboard.inline_url_button(\"Visit\", \"https://example.com\")],\n" " [keyboard.inline_copy_text_button(\"Copy\", \"Hello World!\")],\n" " ])\n" " ```\n" ). -spec new_inline(list(list(telega@model@types:inline_keyboard_button()))) -> inline_keyboard(). new_inline(Buttons) -> {inline_keyboard, Buttons}. -file("src/telega/keyboard.gleam", 360). ?DOC(" Create a grid inline keyboard with buttons arranged in rows of specified width\n"). -spec inline_grid(list(telega@model@types:inline_keyboard_button()), integer()) -> inline_keyboard(). inline_grid(Buttons, Columns) -> Rows = chunk_list(Buttons, Columns), new_inline(Rows). -file("src/telega/keyboard.gleam", 380). ?DOC(" Remove keyboard markup - useful for hiding keyboards\n"). -spec remove() -> telega@model@types:send_message_reply_markup_parameters(). remove() -> {send_message_reply_remove_keyboard_markup_parameters, {reply_keyboard_remove, true, none}}. -file("src/telega/keyboard.gleam", 388). ?DOC(" Create a single-button keyboard\n"). -spec single(telega@model@types:keyboard_button()) -> keyboard(). single(Button) -> new([[Button]]). -file("src/telega/keyboard.gleam", 393). ?DOC(" Create a single-button inline keyboard\n"). -spec inline_single(telega@model@types:inline_keyboard_button()) -> inline_keyboard(). inline_single(Button) -> new_inline([[Button]]). -file("src/telega/keyboard.gleam", 411). ?DOC( " Create a new keyboard builder for method chaining.\n" " This provides a more ergonomic API for building complex keyboards.\n" "\n" " ## Example\n" " ```gleam\n" " let keyboard = keyboard.builder()\n" " |> keyboard.text(\"Option 1\")\n" " |> keyboard.text(\"Option 2\")\n" " |> keyboard.next_row()\n" " |> keyboard.text(\"Cancel\")\n" " |> keyboard.build()\n" " ```\n" ). -spec builder() -> keyboard_builder(). builder() -> {keyboard_builder, [], []}. -file("src/telega/keyboard.gleam", 428). ?DOC( " Create a new inline keyboard builder for method chaining.\n" " This provides a more ergonomic API for building complex inline keyboards.\n" "\n" " ## Example\n" " ```gleam\n" " let callback_data = keyboard.string_callback_data(\"action\")\n" " let keyboard = keyboard.inline_builder()\n" " |> keyboard.inline_text(\"Button 1\", keyboard.pack_callback(callback_data, \"btn1\"))\n" " |> keyboard.inline_text(\"Button 2\", keyboard.pack_callback(callback_data, \"btn2\"))\n" " |> keyboard.inline_next_row()\n" " |> keyboard.inline_url(\"Visit\", \"https://example.com\")\n" " |> keyboard.inline_build()\n" " ```\n" ). -spec inline_builder() -> inline_keyboard_builder(). inline_builder() -> {inline_keyboard_builder, [], []}. -file("src/telega/keyboard.gleam", 572). ?DOC(" Create a new keyboard button\n"). -spec button(binary()) -> telega@model@types:keyboard_button(). button(Text) -> {keyboard_button, Text, none, none, none, none, none, none, none, none, none}. -file("src/telega/keyboard.gleam", 433). ?DOC(" Add a text button to the current row of the keyboard builder.\n"). -spec text(keyboard_builder(), binary()) -> keyboard_builder(). text(Builder, Text) -> Button = button(Text), {keyboard_builder, erlang:element(2, Builder), [Button | erlang:element(3, Builder)]}. -file("src/telega/keyboard.gleam", 600). ?DOC( " Create a button that requests the user's contact information.\n" " When pressed, the user will be prompted to share their phone number.\n" "\n" " ## Example\n" " ```gleam\n" " let contact_btn = keyboard.contact_button(\"📞 Share Contact\")\n" " let keyboard = keyboard.single(contact_btn)\n" " ```\n" ). -spec contact_button(binary()) -> telega@model@types:keyboard_button(). contact_button(Text) -> _record = button(Text), {keyboard_button, erlang:element(2, _record), erlang:element(3, _record), erlang:element(4, _record), erlang:element(5, _record), erlang:element(6, _record), erlang:element(7, _record), {some, true}, erlang:element(9, _record), erlang:element(10, _record), erlang:element(11, _record)}. -file("src/telega/keyboard.gleam", 439). ?DOC(" Add a contact request button to the current row of the keyboard builder.\n"). -spec contact(keyboard_builder(), binary()) -> keyboard_builder(). contact(Builder, Text) -> Button = contact_button(Text), {keyboard_builder, erlang:element(2, Builder), [Button | erlang:element(3, Builder)]}. -file("src/telega/keyboard.gleam", 612). ?DOC( " Create a button that requests the user's location.\n" " When pressed, the user will be prompted to share their current location.\n" "\n" " ## Example\n" " ```gleam\n" " let location_btn = keyboard.location_button(\"📍 Share Location\")\n" " let keyboard = keyboard.single(location_btn)\n" " ```\n" ). -spec location_button(binary()) -> telega@model@types:keyboard_button(). location_button(Text) -> _record = button(Text), {keyboard_button, erlang:element(2, _record), erlang:element(3, _record), erlang:element(4, _record), erlang:element(5, _record), erlang:element(6, _record), erlang:element(7, _record), erlang:element(8, _record), {some, true}, erlang:element(10, _record), erlang:element(11, _record)}. -file("src/telega/keyboard.gleam", 445). ?DOC(" Add a location request button to the current row of the keyboard builder.\n"). -spec location(keyboard_builder(), binary()) -> keyboard_builder(). location(Builder, Text) -> Button = location_button(Text), {keyboard_builder, erlang:element(2, Builder), [Button | erlang:element(3, Builder)]}. -file("src/telega/keyboard.gleam", 588). ?DOC(" Create a new web app button\n"). -spec web_app_button(binary(), binary()) -> telega@model@types:keyboard_button(). web_app_button(Text, Url) -> _record = button(Text), {keyboard_button, erlang:element(2, _record), erlang:element(3, _record), erlang:element(4, _record), erlang:element(5, _record), erlang:element(6, _record), erlang:element(7, _record), erlang:element(8, _record), erlang:element(9, _record), erlang:element(10, _record), {some, {web_app_info, Url}}}. -file("src/telega/keyboard.gleam", 451). ?DOC(" Add a web app button to the current row of the keyboard builder.\n"). -spec web_app(keyboard_builder(), binary(), binary()) -> keyboard_builder(). web_app(Builder, Text, Url) -> Button = web_app_button(Text, Url), {keyboard_builder, erlang:element(2, Builder), [Button | erlang:element(3, Builder)]}. -file("src/telega/keyboard.gleam", 461). ?DOC(" Start a new row in the keyboard builder.\n"). -spec next_row(keyboard_builder()) -> keyboard_builder(). next_row(Builder) -> case erlang:element(3, Builder) of [] -> Builder; _ -> New_row = lists:reverse(erlang:element(3, Builder)), {keyboard_builder, [New_row | erlang:element(2, Builder)], []} end. -file("src/telega/keyboard.gleam", 472). ?DOC(" Build the final keyboard from the builder.\n"). -spec build(keyboard_builder()) -> keyboard(). build(Builder) -> Final_builder = case erlang:element(3, Builder) of [] -> Builder; _ -> next_row(Builder) end, All_rows = lists:reverse(erlang:element(2, Final_builder)), new(All_rows). -file("src/telega/keyboard.gleam", 945). ?DOC(" Validate callback data according to Telegram API limits\n"). -spec validate_callback_data(binary()) -> {ok, nil} | {error, binary()}. validate_callback_data(Data) -> Byte_length = erlang:byte_size(Data), case Byte_length > 64 of true -> {error, <<<<"Callback data must be 1-64 bytes long, got "/utf8, (erlang:integer_to_binary(Byte_length))/binary>>/binary, " bytes"/utf8>>}; false -> {ok, nil} end. -file("src/telega/keyboard.gleam", 738). ?DOC(" Create a new inline button with callback data\n"). -spec inline_button(binary(), keyboard_callback(any())) -> {ok, telega@model@types:inline_keyboard_button()} | {error, binary()}. inline_button(Text, Callback_data) -> case validate_callback_data(erlang:element(4, Callback_data)) of {ok, _} -> {ok, {inline_keyboard_button, Text, none, none, none, {some, erlang:element(4, Callback_data)}, none, none, none, none, none, none, none, none}}; {error, Msg} -> {error, Msg} end. -file("src/telega/keyboard.gleam", 482). ?DOC(" Add an inline text button with callback data to the current row.\n"). -spec inline_text(inline_keyboard_builder(), binary(), keyboard_callback(any())) -> {ok, inline_keyboard_builder()} | {error, binary()}. inline_text(Builder, Text, Callback) -> case inline_button(Text, Callback) of {ok, Button} -> {ok, {inline_keyboard_builder, erlang:element(2, Builder), [Button | erlang:element(3, Builder)]}}; {error, Msg} -> {error, Msg} end. -file("src/telega/keyboard.gleam", 788). ?DOC(" Create an inline URL button\n"). -spec inline_url_button(binary(), binary()) -> telega@model@types:inline_keyboard_button(). inline_url_button(Text, Url) -> {inline_keyboard_button, Text, none, none, {some, Url}, none, none, none, none, none, none, none, none, none}. -file("src/telega/keyboard.gleam", 500). ?DOC(" Add an inline URL button to the current row.\n"). -spec inline_url(inline_keyboard_builder(), binary(), binary()) -> inline_keyboard_builder(). inline_url(Builder, Text, Url) -> Button = inline_url_button(Text, Url), {inline_keyboard_builder, erlang:element(2, Builder), [Button | erlang:element(3, Builder)]}. -file("src/telega/keyboard.gleam", 810). ?DOC(" Create an inline web app button\n"). -spec inline_web_app_button(binary(), binary()) -> telega@model@types:inline_keyboard_button(). inline_web_app_button(Text, Url) -> {inline_keyboard_button, Text, none, none, none, none, {some, {web_app_info, Url}}, none, none, none, none, none, none, none}. -file("src/telega/keyboard.gleam", 510). ?DOC(" Add an inline web app button to the current row.\n"). -spec inline_web_app(inline_keyboard_builder(), binary(), binary()) -> inline_keyboard_builder(). inline_web_app(Builder, Text, Url) -> Button = inline_web_app_button(Text, Url), {inline_keyboard_builder, erlang:element(2, Builder), [Button | erlang:element(3, Builder)]}. -file("src/telega/keyboard.gleam", 832). ?DOC(" Create an inline switch query button\n"). -spec inline_switch_query_button(binary(), binary()) -> telega@model@types:inline_keyboard_button(). inline_switch_query_button(Text, Query) -> {inline_keyboard_button, Text, none, none, none, none, none, none, {some, Query}, none, none, none, none, none}. -file("src/telega/keyboard.gleam", 520). ?DOC(" Add an inline switch query button to the current row.\n"). -spec inline_switch_query(inline_keyboard_builder(), binary(), binary()) -> inline_keyboard_builder(). inline_switch_query(Builder, Text, Query) -> Button = inline_switch_query_button(Text, Query), {inline_keyboard_builder, erlang:element(2, Builder), [Button | erlang:element(3, Builder)]}. -file("src/telega/keyboard.gleam", 876). ?DOC(" Create an inline copy text button\n"). -spec inline_copy_text_button(binary(), binary()) -> telega@model@types:inline_keyboard_button(). inline_copy_text_button(Text, Copy_text) -> {inline_keyboard_button, Text, none, none, none, none, none, none, none, none, none, {some, {copy_text_button, Copy_text}}, none, none}. -file("src/telega/keyboard.gleam", 530). ?DOC(" Add an inline copy text button to the current row.\n"). -spec inline_copy_text(inline_keyboard_builder(), binary(), binary()) -> inline_keyboard_builder(). inline_copy_text(Builder, Text, Copy_text) -> Button = inline_copy_text_button(Text, Copy_text), {inline_keyboard_builder, erlang:element(2, Builder), [Button | erlang:element(3, Builder)]}. -file("src/telega/keyboard.gleam", 540). ?DOC(" Start a new row in the inline keyboard builder.\n"). -spec inline_next_row(inline_keyboard_builder()) -> inline_keyboard_builder(). inline_next_row(Builder) -> case erlang:element(3, Builder) of [] -> Builder; _ -> New_row = lists:reverse(erlang:element(3, Builder)), {inline_keyboard_builder, [New_row | erlang:element(2, Builder)], []} end. -file("src/telega/keyboard.gleam", 553). ?DOC(" Build the final inline keyboard from the builder.\n"). -spec inline_build(inline_keyboard_builder()) -> inline_keyboard(). inline_build(Builder) -> Final_builder = case erlang:element(3, Builder) of [] -> Builder; _ -> inline_next_row(Builder) end, All_rows = lists:reverse(erlang:element(2, Final_builder)), new_inline(All_rows). -file("src/telega/keyboard.gleam", 563). ?DOC(" Build a reply markup for `Message` from an inline keyboard\n"). -spec inline_to_markup(inline_keyboard()) -> telega@model@types:send_message_reply_markup_parameters(). inline_to_markup(Keyboard) -> {send_message_reply_inline_keyboard_markup_parameters, {inline_keyboard_markup, erlang:element(2, Keyboard)}}. -file("src/telega/keyboard.gleam", 617). ?DOC(" Create a button that requests poll creation\n"). -spec poll_button(binary(), gleam@option:option(binary())) -> telega@model@types:keyboard_button(). poll_button(Text, Poll_type) -> _record = button(Text), {keyboard_button, erlang:element(2, _record), erlang:element(3, _record), erlang:element(4, _record), erlang:element(5, _record), erlang:element(6, _record), erlang:element(7, _record), erlang:element(8, _record), erlang:element(9, _record), {some, {keyboard_button_poll_type, Poll_type}}, erlang:element(11, _record)}. -file("src/telega/keyboard.gleam", 628). ?DOC(" Create a button that requests users to be shared\n"). -spec users_button( binary(), integer(), gleam@option:option(boolean()), gleam@option:option(boolean()), gleam@option:option(integer()), gleam@option:option(boolean()), gleam@option:option(boolean()), gleam@option:option(boolean()) ) -> telega@model@types:keyboard_button(). users_button( Text, Request_id, User_is_bot, User_is_premium, Max_quantity, Request_name, Request_username, Request_photo ) -> _record = button(Text), {keyboard_button, erlang:element(2, _record), erlang:element(3, _record), erlang:element(4, _record), {some, {keyboard_button_request_users, Request_id, User_is_bot, User_is_premium, Max_quantity, Request_name, Request_username, Request_photo}}, erlang:element(6, _record), erlang:element(7, _record), erlang:element(8, _record), erlang:element(9, _record), erlang:element(10, _record), erlang:element(11, _record)}. -file("src/telega/keyboard.gleam", 653). ?DOC(" Create a button that requests a chat to be shared\n"). -spec chat_button( binary(), integer(), boolean(), gleam@option:option(boolean()), gleam@option:option(boolean()), gleam@option:option(boolean()), gleam@option:option(telega@model@types:chat_administrator_rights()), gleam@option:option(telega@model@types:chat_administrator_rights()), gleam@option:option(boolean()), gleam@option:option(boolean()), gleam@option:option(boolean()), gleam@option:option(boolean()) ) -> telega@model@types:keyboard_button(). chat_button( Text, Request_id, Chat_is_channel, Chat_is_forum, Chat_has_username, Chat_is_created, User_administrator_rights, Bot_administrator_rights, Bot_is_member, Request_title, Request_username, Request_photo ) -> _record = button(Text), {keyboard_button, erlang:element(2, _record), erlang:element(3, _record), erlang:element(4, _record), erlang:element(5, _record), {some, {keyboard_button_request_chat, Request_id, Chat_is_channel, Chat_is_forum, Chat_has_username, Chat_is_created, User_administrator_rights, Bot_administrator_rights, Bot_is_member, Request_title, Request_username, Request_photo}}, erlang:element(7, _record), erlang:element(8, _record), erlang:element(9, _record), erlang:element(10, _record), erlang:element(11, _record)}. -file("src/telega/keyboard.gleam", 729). ?DOC(" Build a reply markup for `Message` from an inline keyboard\n"). -spec to_inline_markup(inline_keyboard()) -> telega@model@types:send_message_reply_markup_parameters(). to_inline_markup(Keyboard) -> {send_message_reply_inline_keyboard_markup_parameters, {inline_keyboard_markup, erlang:element(2, Keyboard)}}. -file("src/telega/keyboard.gleam", 766). ?DOC( " Create a new inline button with raw string callback data.\n" " Unlike `inline_button`, no typed `KeyboardCallback` packing or validation\n" " is performed — the string is used as-is.\n" ). -spec inline_raw_callback_button(binary(), binary()) -> telega@model@types:inline_keyboard_button(). inline_raw_callback_button(Text, Callback_data) -> {inline_keyboard_button, Text, none, none, none, {some, Callback_data}, none, none, none, none, none, none, none, none}. -file("src/telega/keyboard.gleam", 854). ?DOC(" Create an inline switch query current chat button\n"). -spec inline_switch_query_current_chat_button(binary(), binary()) -> telega@model@types:inline_keyboard_button(). inline_switch_query_current_chat_button(Text, Query) -> {inline_keyboard_button, Text, none, none, none, none, none, none, none, {some, Query}, none, none, none, none}. -file("src/telega/keyboard.gleam", 922). ?DOC( " Create a filter for inline keyboard callback queries.\n" " This filter can be used with `telega.wait_callback_query()` to only listen\n" " for callbacks from buttons in this specific keyboard.\n" "\n" " ## Example\n" " ```gleam\n" " let callback_data = keyboard.string_callback_data(\"action\")\n" " let assert Ok(button1) = keyboard.inline_button(\"Yes\",\n" " keyboard.pack_callback(callback_data, \"yes\"))\n" " let assert Ok(button2) = keyboard.inline_button(\"No\",\n" " keyboard.pack_callback(callback_data, \"no\"))\n" "\n" " let keyboard = keyboard.new_inline([[button1, button2]])\n" " let assert Ok(filter) = keyboard.filter_inline_keyboard_query(keyboard)\n" "\n" " // In your handler:\n" " use ctx, payload, query_id <- telega.wait_callback_query(\n" " ctx:, filter:, or: None, timeout: None\n" " )\n" " let assert Ok(callback) = keyboard.unpack_callback(payload, callback_data)\n" " ```\n" "\n" " ## Errors\n" " - Returns `Error` if the keyboard has no callback buttons\n" " - Returns `Error` if the callback data creates an invalid regex pattern\n" ). -spec filter_inline_keyboard_query(inline_keyboard()) -> {ok, telega@bot:callback_query_filter()} | {error, binary()}. filter_inline_keyboard_query(Keyboard) -> Options = begin _pipe = erlang:element(2, Keyboard), _pipe@1 = gleam@list:flat_map( _pipe, fun(Row) -> gleam@list:map( Row, fun(Button) -> erlang:element(6, Button) end ) end ), _pipe@2 = gleam@option:values(_pipe@1), gleam@string:join(_pipe@2, <<"|"/utf8>>) end, case Options of <<""/utf8>> -> {error, <<"No callback data found in keyboard"/utf8>>}; _ -> case gleam@regexp:from_string( <<<<"^("/utf8, Options/binary>>/binary, ")$"/utf8>> ) of {ok, Re} -> {ok, {callback_query_filter, Re}}; {error, _} -> {error, <<"Invalid regex pattern from callback data"/utf8>>} end end. -file("src/telega/keyboard.gleam", 1013). ?DOC( " Create a new callback data configuration for inline keyboard buttons.\n" " This defines how data is serialized/deserialized when buttons are pressed.\n" "\n" " ## Parameters\n" " - `id`: Unique identifier for this callback type\n" " - `serialize`: Function to convert your data to a string\n" " - `deserialize`: Function to convert string back to your data\n" "\n" " ## Example\n" " ```gleam\n" " // For custom enum types\n" " pub type Action {\n" " Save\n" " Delete\n" " Edit\n" " }\n" "\n" " let action_callback = keyboard.new_callback_data(\n" " id: \"action\",\n" " serialize: fn(action) {\n" " case action {\n" " Save -> \"save\"\n" " Delete -> \"delete\"\n" " Edit -> \"edit\"\n" " }\n" " },\n" " deserialize: fn(str) {\n" " case str {\n" " \"save\" -> Save\n" " \"delete\" -> Delete\n" " _ -> Edit\n" " }\n" " }\n" " )\n" " ```\n" ). -spec new_callback_data( binary(), fun((AVMN) -> binary()), fun((binary()) -> AVMN) ) -> keyboard_callback_data(AVMN). new_callback_data(Id, Serialize, Deserialize) -> {keyboard_callback_data, Id, Serialize, Deserialize, <<":"/utf8>>}. -file("src/telega/keyboard.gleam", 1022). ?DOC(" Change the delimiter for the callback data, useful if you need to use `:` in the id\n"). -spec set_callback_data_delimiter(keyboard_callback_data(AVMP), binary()) -> keyboard_callback_data(AVMP). set_callback_data_delimiter(Data, Delimiter) -> {keyboard_callback_data, erlang:element(2, Data), erlang:element(3, Data), erlang:element(4, Data), Delimiter}. -file("src/telega/keyboard.gleam", 1030). ?DOC(" Pack callback data into a callback\n"). -spec pack_callback(keyboard_callback_data(AVMS), AVMS) -> keyboard_callback(AVMS). pack_callback(Callback_data, Data) -> Payload = <<<<(erlang:element(2, Callback_data))/binary, (erlang:element(5, Callback_data))/binary>>/binary, ((erlang:element(3, Callback_data))(Data))/binary>>, {keyboard_callback, Data, erlang:element(2, Callback_data), Payload, Callback_data}. -file("src/telega/keyboard.gleam", 1041). ?DOC(" Unpack payload into a callback\n"). -spec unpack_callback(binary(), keyboard_callback_data(AVMV)) -> {ok, keyboard_callback(AVMV)} | {error, nil}. unpack_callback(Payload, Callback_data) -> gleam@result:'try'( gleam@string:split_once(Payload, erlang:element(5, Callback_data)), fun(_use0) -> {Id, Data} = _use0, {ok, {keyboard_callback, (erlang:element(4, Callback_data))(Data), Id, Payload, Callback_data}} end ). -file("src/telega/keyboard.gleam", 1076). ?DOC( " Create a simple string callback data configuration.\n" " This is the most common type for simple string-based callbacks.\n" "\n" " ## Example\n" " ```gleam\n" " let callback_data = keyboard.string_callback_data(\"action\")\n" " let callback = keyboard.pack_callback(callback_data, \"delete_user\")\n" " let assert Ok(button) = keyboard.inline_button(\"Delete\", callback)\n" "\n" " // In your callback handler:\n" " let assert Ok(unpacked) = keyboard.unpack_callback(payload, callback_data)\n" " case unpacked.data {\n" " \"delete_user\" -> // handle deletion\n" " _ -> // handle other actions\n" " }\n" " ```\n" ). -spec string_callback_data(binary()) -> keyboard_callback_data(binary()). string_callback_data(Id) -> new_callback_data( Id, fun gleam@function:identity/1, fun gleam@function:identity/1 ). -file("src/telega/keyboard.gleam", 1097). ?DOC( " Create an integer callback data configuration.\n" " Useful for pagination, user IDs, or any numeric data.\n" "\n" " ## Example\n" " ```gleam\n" " let page_callback = keyboard.int_callback_data(\"page\")\n" " let next_page = keyboard.pack_callback(page_callback, 2)\n" " let assert Ok(button) = keyboard.inline_button(\"Next ->\", next_page)\n" "\n" " // In your callback handler:\n" " let assert Ok(unpacked) = keyboard.unpack_callback(payload, page_callback)\n" " let page_number = unpacked.data // Int\n" " ```\n" ). -spec int_callback_data(binary()) -> keyboard_callback_data(integer()). int_callback_data(Id) -> new_callback_data( Id, fun erlang:integer_to_binary/1, fun(Data) -> case gleam_stdlib:parse_int(Data) of {ok, I} -> I; {error, _} -> 0 end end ). -file("src/telega/keyboard.gleam", 1140). -spec deserialize_bool(binary()) -> boolean(). deserialize_bool(Data) -> Data =:= <<"true"/utf8>>. -file("src/telega/keyboard.gleam", 1133). -spec serialize_bool(boolean()) -> binary(). serialize_bool(Data) -> case Data of true -> <<"true"/utf8>>; false -> <<"false"/utf8>> end. -file("src/telega/keyboard.gleam", 1125). ?DOC( " Create a boolean callback data configuration.\n" " Useful for toggle buttons, settings, or yes/no choices.\n" "\n" " ## Example\n" " ```gleam\n" " let toggle_callback = keyboard.bool_callback_data(\"notifications\")\n" " let enable_btn = keyboard.pack_callback(toggle_callback, True)\n" " let disable_btn = keyboard.pack_callback(toggle_callback, False)\n" "\n" " let assert Ok(enable) = keyboard.inline_button(\"🔔 Enable\", enable_btn)\n" " let assert Ok(disable) = keyboard.inline_button(\"🔕 Disable\", disable_btn)\n" "\n" " // In your callback handler:\n" " let assert Ok(unpacked) = keyboard.unpack_callback(payload, toggle_callback)\n" " case unpacked.data {\n" " True -> // enable notifications\n" " False -> // disable notifications\n" " }\n" " ```\n" ). -spec bool_callback_data(binary()) -> keyboard_callback_data(boolean()). bool_callback_data(Id) -> new_callback_data(Id, fun serialize_bool/1, fun deserialize_bool/1).