{"id":37678823,"url":"https://github.com/torrua/keyboa","last_synced_at":"2026-01-16T12:21:34.700Z","repository":{"id":46569899,"uuid":"237219324","full_name":"torrua/keyboa","owner":"torrua","description":"Keyboa is a project designed to simplify Telegram keyboards creation.","archived":false,"fork":false,"pushed_at":"2025-10-01T20:52:41.000Z","size":111,"stargazers_count":51,"open_issues_count":3,"forks_count":7,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-11-01T01:02:13.605Z","etag":null,"topics":["bot","generate","hacktoberfest","inline","keyboard","lists","python","telegram"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/torrua.png","metadata":{"files":{"readme":"README-es.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-01-30T13:30:59.000Z","updated_at":"2025-10-01T20:52:40.000Z","dependencies_parsed_at":"2025-11-17T05:04:19.782Z","dependency_job_id":null,"html_url":"https://github.com/torrua/keyboa","commit_stats":{"total_commits":108,"total_committers":7,"mean_commits":"15.428571428571429","dds":0.2129629629629629,"last_synced_commit":"7a5b7bc44bee883eeb2c5de26c577c0d43bb971e"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/torrua/keyboa","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/torrua%2Fkeyboa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/torrua%2Fkeyboa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/torrua%2Fkeyboa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/torrua%2Fkeyboa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/torrua","download_url":"https://codeload.github.com/torrua/keyboa/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/torrua%2Fkeyboa/sbom","scorecard":{"id":895098,"data":{"date":"2025-08-11","repo":{"name":"github.com/torrua/keyboa","commit":"481e42228abd30638c00198a64cb1bab7a8b37a1"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.5,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":3,"reason":"Found 8/22 approved changesets -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/codeql-analysis.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/torrua/keyboa/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/torrua/keyboa/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/torrua/keyboa/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:54: update your workflow using https://app.stepsecurity.io/secureworkflow/torrua/keyboa/codeql-analysis.yml/master?enable=pin","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Vulnerabilities","score":9,"reason":"1 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2024-48 / GHSA-fj7x-q9j7-g6q6"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":7,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 0 commits out of 22 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-24T13:23:16.453Z","repository_id":46569899,"created_at":"2025-08-24T13:23:16.453Z","updated_at":"2025-08-24T13:23:16.453Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28478570,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T11:59:17.896Z","status":"ssl_error","status_checked_at":"2026-01-16T11:55:55.838Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bot","generate","hacktoberfest","inline","keyboard","lists","python","telegram"],"created_at":"2026-01-16T12:21:34.611Z","updated_at":"2026-01-16T12:21:34.677Z","avatar_url":"https://github.com/torrua.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Keyboa\n[![Download Keyboa](https://img.shields.io/pypi/v/keyboa.svg)](https://pypi.python.org/pypi/keyboa)\n![PyPI - Downloads](https://img.shields.io/pypi/dm/keyboa?color=yellow\u0026logo=pypi\u0026logoColor=yellow)\n[![Build Status](https://travis-ci.com/torrua/keyboa.svg?branch=master)](https://travis-ci.com/torrua/keyboa)\n![CodeQL](https://github.com/torrua/keyboa/workflows/CodeQL/badge.svg?branch=master)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/torrua/keyboa/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/torrua/keyboa/?branch=master)\n[![codecov](https://codecov.io/gh/torrua/keyboa/branch/master/graph/badge.svg?token=H4MO1Y3DDY)](https://codecov.io/gh/torrua/keyboa)\n[![GitHub license](https://img.shields.io/github/license/torrua/keyboa)](https://github.com/torrua/keyboa/blob/master/LICENSE)\n\nEste es un generador de teclado en línea simple pero flexible que funciona como un complemento del paquete PyTelegramBotAPI. Con **keyboa** puedes:\n- crear botones rápidamente con callbacks complejos,\n- crear teclados directamente desde listas,\n- combinar fácilmente varios teclados en uno,\n- muchas otras cosas interesantes ...\n\n\n\u003e 📖 Esta guía se aplica a la versión 3 del teclado o superiores.\n\u003e Si está utilizando la versión 2 del teclado o una inferior, por favor utilice [The guide for version 2](README_for_v2.md).\n\n\u003e 📌 **NOTICIA IMPORTANTE**: \n\u003e \n\u003e La versión 3 no es compatible con la versión 2.\n\u003e Si decide actualizar de la versión 2 a la 3, tenga en cuenta que deberá ajustar su código.\n\n## Instalación\nKeyboa es compatible con Python 3.7 y superior. Puede instalar este paquete con pip como de costumbre:\n```sh\n$ pip install keyboa\n```\nDespués de eso, importamos:\n```python\nfrom keyboa import Keyboa\n```\n\n## Inicio Rápido\n### Teclado básico\nEl teclado para telegram más sencillo, se puede crear así:\n```python\nmenu = [\"spam\", \"eggs\", \"ham\"]\nkeyboard = Keyboa(items=menu)\nbot.send_message(chat_id=chat_id, text=text, reply_markup=keyboard())\n```\n![keyboard from list of str](https://telegra.ph/file/d9280b11ed11ec13e6f56.png)\n\n### Teclado estructurado simple\nSi necesita crear un teclado con una estructura predefinida, haga lo siguiente:\n```python\nmenu = [[\"spam\", \"eggs\"], [\"ham\", \"bread\"], \"spam\"]\nkeyboard = Keyboa(items=menu)\nbot.send_message(chat_id=chat_id, text=text, reply_markup=keyboard())\n```\n![keyboard from list of str](https://telegra.ph/file/2eb6752324fa196cae4ac.png)\n\nEste es un buen comienzo, pero echemos un vistazo con mayor detalle a cómo funciona y qué características adicionales podemos usar.\n\n## Cómo funciona\n\nLa clase ```Keyboa``` proporciona dos opciones para crear teclados compatilbes con pyTelegramBotAPI a través del tipo ```InlineKeyboardMarkup``` : método ```slice()``` y la propiedad ```keyboard```.\nUtilice a continuación,la descripción de la [Keyboa class](#keyboa-class). Como referencia para comprender los matices y las limitaciones del módulo o observe los siguientes ejemplos.\n\n## Crear teclados\nLa forma más sencilla de crear un teclado es iniciar el objeto Keyboa con una lista de elementos y obtener la propiedad ```keyboard```.\n```python\nmenu = [\"spam\", \"eggs\", \"ham\"]\nkeyboard = Keyboa(items=menu)\nbot.send_message(chat_id=chat_id, text=text, reply_markup=keyboard())\n```\n![keyboard from list of str](https://telegra.ph/file/d9280b11ed11ec13e6f56.png)\n\nPor defecto, cada elemento de la lista se convierte en una fila separada, pero es fácil de cambiar al combinar los elementos en grupos.\n```python\nmenu = [[\"spam\", \"eggs\"], [\"ham\", \"bread\"], \"spam\"]\nkeyboard = Keyboa(items=menu)\nbot.send_message(chat_id=chat_id, text=text, reply_markup=keyboard())\n```\n![keyboard from list of str](https://telegra.ph/file/2eb6752324fa196cae4ac.png)\n\nPuedes ver que, los botones del teclado están ordenados en función a cómo los agrupamos en la lista. \n\nFíjese en que el último \"spam\" se ha convertido en una fila separada, aunque no lo hemos incluido en una lista separada.\n\nY, por supuesto, usted puede crear estructuras más complejas si lo desea, por ejemplo:\n```python\nmenu = [[\"spam\", \"eggs\", \"ham\"], [\"ham\", \"eggs\"], [\"spam\", ] [\"sausages\", \"spam\"], [\"eggs\", \"spam\", \"spam\"]]\nkeyboard = Keyboa(items=menu)\nbot.send_message(chat_id=chat_id, text=text, reply_markup=keyboard())\n```\n![keyboard from list of str](https://telegra.ph/file/faff37512c626845c5524.png)\n\n\u003eDebido a las limitaciones de la API Telegram, puede agregar **hasta 8 botones por fila** y **hasta 100 para todo el teclado**.\n\nProfundicemos. Suponga que tiene una lista de 24 elementos, y le gustaría dividirla en filas de 6 botones cada una. Debería realizar lo siguiente:\n```python\nnumbers = list(range(1, 25))\nkeyboard = Keyboa(items=numbers, items_in_row=6)\nbot.send_message(chat_id=chat_id, text=text, reply_markup=keyboard())\n```\n![keyboard with 6 items_in_row](https://telegra.ph/file/2122cb9f50938b39b4439.png)\n\n💡 Puede crear fácilmente 3, 4 o incluso 8 botones sseguidos, cambiando únicamente el parámetro ```items_in_row```.\n\nAhora intentaremos usar más atributos para ver cómo afectarán el resultado:\n```python\nkeyboa = Keyboa(items=list(range(0, 48)), alignment=True)\nkeyboard = keyboa(slice(5, 37))\nbot.send_message(chat_id=chat_id, text=text, reply_markup=keyboard)\n```\n![keyboard slice with alignment](https://telegra.ph/file/cc41513058a2b3d9f83ba.png)\n\nComo puede ver, este teclado consta de un segmento ```[5:37]```. Además, aunque no especificamos el atributo ```items_in_row```, la función dividió la lista en filas iguales, debido al atributo ```alignment```.\n\n## Crear Botones\n💡 Por lo general, no es necesario crear botones separados, ya que estos se crearán automáticamente a partir de sus datos de origen cuando se crea el teclado.\nPero en caso de necesitarlo, se puede hacer de la siguiente manera.\nImport ```Button``` class ([detailed description](#button-class)), create button object from various data types, such as ```str```, ```int```, ```tuple```, ```dict``` and call ```button``` property to get ```InlineKeyboardButton```:\n```python\nfrom keyboa import Button\nspam = Button(button_data=\"spam\").button\n```\n\n#### botón de ```str``` o ```int```\n```python\nspam = Button(button_data=\"spam\").button\n```\n```sh\n{'text': 'spam', 'callback_data': 'spam'}\n```\n\n#### botón de ```tuple```\n```python\nspam = Button(button_data=(\"spam\", \"eggs\"), front_marker=\"ham_\", back_marker=\"_spam\").button\n```\n```sh\n{'text': 'spam', 'callback_data': 'ham_eggs_spam'}\n```\n💡 Observe que, en este ejemplo también usamos ```front_marker``` y ```back_marker``` para añadir algunos datos al callback_data del botón.\n\n#### botón de ```dict``` sin la tecla \"text\" \n```python\nspam = Button(button_data={\"spam\": \"ham_eggs_spam\"}).button\n```\n```sh\n{'text': 'spam', 'callback_data': 'ham_eggs_spam'}\n```\n\n#### botón de ```dict``` con la tecla \"text\" \n```python\nspam = Button(button_data={\"text\": \"spam\", \"url\": \"https://ya.ru/\", \"callback_data\": \"eggs\"}).button\n```\n```sh\n{\"text\": \"spam\", \"url\": \"https://ya.ru/\", \"callback_data\": \"eggs\"}\n```\n\n## Combinar Teclados\nA veces es necesario combinar varios bloques de teclado en uno. ¡El método de la clase Keyboa ```combine()``` hace precisamente eso!\n\nEste método tiene un solo parámetro de entrada - ```keyboards```. Debe ser una secuencia de objetos ```InlineKeyboardMarkup```. También podría presentarse como un único ```InlineKeyboardMarkup```.\n\nAsí es como funciona:\n```python\ncontrols = [[\"⏹️\", \"⏪️\", \"⏏️\", \"⏩️\", \"▶️\"], ]\ntracks = list(range(1, 13))\n\nkeyboard_controls = Keyboa(items=controls).keyboard\nkeyboard_tracks = Keyboa(items=tracks, items_in_row=4).keyboard\n\nkeyboard = Keyboa.combine(keyboards=(keyboard_tracks, keyboard_controls))\nbot.send_message(chat_id=user_id, text=text_tracks, reply_markup=keyboard)\n```\n![keyboard combo](https://telegra.ph/file/342c06d783faeb786f242.png)\n\nComo puede ver, fusionamos los dos teclados en uno.\n\n## Callbacks complejos\nAlgunas nociones sobre cómo crear callbacks complejos para botones. \n\nA menudo es necesario leer y pasar por las opciones de callback que el usuario ha seleccionado secuencialmente. Por ejemplo, determinar la dirección: ciudad, calle, casa, número de apartamento.\n\nSupongamos que ofrecemos al usuario varias ciudades para elegir. Crear un teclado simple:\n```python\nkb_cities = Keyboa(\n    items=[\"Moscow\", \"London\", \"Tokyo\", ],\n    front_marker=\"\u0026city=\",\n    back_marker=\"$\"\n)\nbot.send_message(chat_id=user_id, text=\"Select your city:\", reply_markup=kb_cities())\n```\n![keyboard cities](https://telegra.ph/file/dcd011c72e43aefd8d00d.png)\n\nAl hacerlo, obtenemos lo siguiente dentro del teclado:\n```sh\n{'inline_keyboard': [\n    [{'text': 'Moscow', 'callback_data': '\u0026city=Moscow$'}],\n    [{'text': 'London', 'callback_data': '\u0026city=London$'}],\n    [{'text': 'Tokyo', 'callback_data': '\u0026city=Tokyo$'}]\n]}\n```\nSuponga que un usuario selecciona ```London```. Nos gustaría recordar este dato y dejarle elegir entre varias calles:\n```python\nreceived_callback = call.data  # \"\u0026city=London$\"\nstreets = [\"Baker Street\", \"Oxford Street\", \"Abbey Road\", ]\nkb_streets = Keyboa(\n    items=streets, \n    front_marker=\"\u0026street=\",\n    back_marker=received_callback  # añadimos datos existentes al final\n)\nbot.send_message(chat_id=user_id, text=\"Select your street:\", reply_markup=kb_streets())\n```\n![keyboard streets](https://telegra.ph/file/cf06e3bc0adece894535d.png)\n\n```sh\n{'inline_keyboard': [\n    [{\n        'text': 'Baker Street',\n        'callback_data': '\u0026street=Baker Street\u0026city=London$'}],\n    [{\n        'text': 'Oxford Street',\n        'callback_data': '\u0026street=Oxford Street\u0026city=London$'}],\n    [{\n        'text': 'Abbey Road',\n        'callback_data': '\u0026street=Abbey Road\u0026city=London$'}]\n]}\n```\n💡 Observe que usamos un ```front_marker``` para especificar el tipo de los elementos actuales, y un ```back_marker``` para adjuntar información existente.\n\nComo puede ver, la variante seleccionada por el usuario en el paso anterior también ha sido guardada.\nSi el usuario selecciona una calle, por ejemplo, ```Baker Street```, recibiremos el ```call.data``` como ```'\u0026street=Baker Street\u0026city=London$'```. Por supuesto podemos parsearlo fácilmente.\n\nFinalmente, deje que seleccione un apartamento:\n```python\nreceived_callback = call.data  # '\u0026street=Baker Street\u0026city=London$'\napartments = [\"221a\", \"221b\", \"221c\", ]\nkb_apartments = Keyboa(\n    items=apartments, \n    front_marker=\"\u0026apartments=\", \n    back_marker=received_callback  # añadimos datos existentes al final\n)\nbot.send_message(chat_id=user_id, text=\"Select your apartments:\", reply_markup=kb_apartments())\n```\n![keyboard streets](https://telegra.ph/file/0eec50498f2a68955c81c.png)\n\n```sh\n{'inline_keyboard': [[\n        {'text': '221a',\n        'callback_data': '\u0026apartments=221a\u0026street=Baker Street\u0026city=London$'}, \n        {'text': '221b', \n        'callback_data': '\u0026apartments=221b\u0026street=Baker Street\u0026city=London$'},\n        {'text': '221c', \n        'callback_data': '\u0026apartments=221c\u0026street=Baker Street\u0026city=London$'}\n    ]]\n}\n```\nY si el usuario selecciona el botón ```221b```, asumiremos que ¡🕵🏻‍♂️ Mr. Sherlock Holmes también usa nuestro bot!\n\n## Detalles\n### Clase Keyboa\nAtributo | Tipo | Descripcción\n--------- | ---- | -----------\n```items``` | BlockItems | _Obligatorio_. Lista de elementos para el teclado. El número total no debe ser superior a 100 debido a la limitación de la API de Telegram Bot.\n```items_in_row``` | Integer | _Opcional_. El número de botones en una fila de teclado. Deber ser **de 1 a 8** debido a la limitación de la API de Telegram Bot.\u003cbr\u003eEl valor por defecto es ```None```, lo que significa que de manera predeterminada la estructura del teclado depende de la agrupación de los elementos  ```items```.\n```copy_text_to_callback``` | Boolean | Si es ```True```, y ```button_data``` es un ```str``` o un ```int```, la función copiará el texto del botón al callback data (y agregará otros marcadores en caso de que existan).\u003cbr\u003eEl valor por defecto es ```True```.\n```front_marker``` | CallbackDataMarker | _Opcional_. Parte frontal del callback data, que es común para todos los botones.\n```back_marker``` | CallbackDataMarker | _Opcional_. Parte posterior del callback data, que es común para todos los botones.\n```alignment``` | Boolean or Iterable | Si es ```True```, intentará dividir todos los elementos en **filas iguales en un rango de 3 a 5**.\u003cbr\u003eSi es ```Iterable``` (con cualquier ```int``` en un rango de 1 a 8), intentará encontrar un divisor adecuado entre ellos.\u003cbr\u003e\u003cbr\u003eEl atributo habilitado reemplaza la acción del atributo ```items_in_row```, pero si no se puede encontrar un divisor adecuado, la función usará si se proporciona el valor ```items_in_row```.\u003cbr\u003e\u003cbr\u003eEl valor por defecto es ```None```.\n```alignment_reverse``` | Boolean | Si es ```True```, intentará encontrar el divisor empezando por el final de la variable ```auto_alignment``` (si está definida) o desde el rango predeterminado.\u003cbr\u003e\u003cbr\u003eEl atributo habilitado solo funciona si ```auto_alignment``` está habilitado.\u003cbr\u003e\u003cbr\u003eEl valor por defecto es ```None```.\n\n```python\n# secuencia sin estructura de objetos InlineButtonData\nFlatSequence = List[InlineButtonData]\n\n# secuencia estructurada de objetos InlineButtonData\nStructuredSequence = List[Union[FlatSequence, InlineButtonData]]\n\n# tipo unificado que le permite utilizar cualquier tipo de dato disponible para el teclado\nBlockItems = Union[StructuredSequence, InlineButtonData]\n```\n\n### Clase button\nAtributo | Tipo | Descripcción\n--------- | ---- | -----------\n```button_data``` | InlineButtonData | Un objeto a partir del cual se creará el botón.\u003cbr\u003e_Consulte la explicación detallada a continuación._\n```front_marker``` | CallbackDataMarker | _Opcional_. Un objeto que se agregará al lado **izquierdo** del callback.\n```back_marker``` | CallbackDataMarker | _Opcional_. Un objeto que se agregará al lado **derecho** del callback.\n```copy_text_to_callback``` | Boolean | Si es ```True```, y ```button_data``` es un ```str``` o un ```int```, la función copiará el texto del botón al callback data (y agregará otros marcadores en caso de que existan).\u003cbr\u003eEl valor por defecto es ```False```.\n\nTodos los tipos aceptables se combinan en el tipo ```InlineButtonData```:\n```python\nInlineButtonData = Union[str, int, tuple, dict, InlineKeyboardButton]\n```\nTambién hay un tipo ```CallbackDataMarker``` para los callback data:\n```python\nCallbackDataMarker = Optional[Union[str, int]]\n```\n\nPara el objeto ```button_data```  --\n* Si es un ```str``` o un ```int```, se usará para texto (y callback, si ```copy_text_to_callback``` no está deshabilitado).\n* Si es un ```tuple```, el elemento cero [0] será el texto, y el primero [1] será el callback. \n* Si es un ```dict```, hay dos opciones:\n   * Si **no hay clave \"text\"** en el diccionario y solo existe una clave, la clave será el texto, y el valor será el callback.\u003cbr\u003eEn este caso, ¡no se realiza ninguna verificación del contenido del diccionario!\n  * Si **hay clave \"text\"**, la función pasa todo el diccionario a ```InlineKeyboardButton```, donde las claves del diccionario representan los parámetros del objeto y los valores del diccionario en consencuencia representan los valores de los parámetros.\nEn todos los demás casos se generará un ```ValueError```.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftorrua%2Fkeyboa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftorrua%2Fkeyboa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftorrua%2Fkeyboa/lists"}