Bots de Koto en cualquier lenguaje
Una API HTTP sencilla. Envías HTTP + JSON normal; la pasarela de Koto mantiene la identidad E2E del bot y su bandeja de entrada, descifra los mensajes entrantes y convierte tus llamadas en operaciones cifradas de Koto. Tú no tocas la criptografía.
Resumen
La API sigue un patrón familiar: el método es la ruta, el token va en una cabecera Authorization y la respuesta es un envoltorio JSON { ok, result }. No se requiere ningún SDK: solo necesitas hacer peticiones HTTP. El formato de las llamadas es compatible con las bibliotecas cliente existentes de la Telegram Bot API*, por lo que a menudo basta con redirigir su URL base a la pasarela de Koto.
El flujo es sencillo: crea un bot en @BotKoto y obtén un token → llama a los métodos pasando el token en una cabecera Authorization → recibe actualizaciones por long-poll getUpdates o un webhook.
* Telegram es una marca comercial de Telegram Messenger LLP. Koto se desarrolla de forma independiente, no está afiliado ni respaldado por Telegram; el nombre se utiliza únicamente para describir la compatibilidad.
1. Crear un bot
Abre @BotKoto en cualquier cliente de Koto, envía /newbot, elige un nombre y @username. Recibirás un token como kbot_0a1b2c…. Mantenlo en secreto: controla el bot.
2. Llamar a la API
El método es la ruta completa; el token va en una cabecera Authorization: Bearer (nunca en la URL, para que no se filtre en los registros):
# the method is the whole path; the token rides in a header
{GATEWAY}/{method}
Authorization: Bearer {TOKEN}
# example
curl -H "Authorization: Bearer kbot_0a1b…" \
https://api.koto.run/getMeCada respuesta es un envoltorio JSON:
{ "ok": true, "result": … }
{ "ok": false, "error": "reason" }Una comprobación rápida de que el bot está activo:
curl -H "Authorization: Bearer kbot_0a1b…" \
https://api.koto.run/getMe
# {"ok":true,"result":{"id":"bot-…","username":"mybot","is_bot":true}}Enviar un mensaje:
curl -X POST https://api.koto.run/sendMessage \
-H "Authorization: Bearer kbot_…" \
-H 'content-type: application/json' \
-d '{"chat_id":"KOTO-…","text":"Hello from Koto!"}'3. Recibir actualizaciones
Dos formas mutuamente excluyentes:
Long-poll getUpdates — extrae actualizaciones y confírmalas con un offset creciente. offset = último update_id + 1 confirma todo lo anterior. getUpdates mantiene la conexión hasta timeout segundos (25 por defecto).
Webhook setWebhook — la propia pasarela hace POST de cada actualización a tu URL. Mientras haya un webhook configurado, getUpdates devuelve 409.
Ejemplo: bot de eco
Un bot completo en bash puro: solo HTTP + JSON, sin SDK de Koto y sin criptografía. Long-poll getUpdates y respuesta mediante sendMessage:
#!/usr/bin/env bash
# A full bot over plain HTTP — no SDK, no crypto. The token rides in an
# Authorization: Bearer header, never the URL.
# Run: BOT_TOKEN=kbot_xxx ./echo.sh
API="http://127.0.0.1:8090"
AUTH=(-H "Authorization: Bearer $BOT_TOKEN")
offset=0
while true; do
resp=$(curl -s "${AUTH[@]}" "$API/getUpdates?offset=$offset&timeout=20")
for upd in $(echo "$resp" | jq -c '.result[]?'); do
offset=$(( $(jq '.update_id' <<<"$upd") + 1 ))
chat=$(jq -r '.message.chat.id // ""' <<<"$upd")
text=$(jq -r '.message.text // ""' <<<"$upd")
[ -n "$chat" ] && [ -n "$text" ] && \
curl -s -X POST "${AUTH[@]}" "$API/sendMessage" -H 'content-type: application/json' \
-d "$(jq -nc --arg c "$chat" --arg t "Echo: $text" '{chat_id:$c,text:$t}')"
done
doneMétodos
chat_id es lo que llegó en message.chat.id de la actualización. Los identificadores de mensaje son cadenas opacas. Un envío correcto devuelve { "message_id": "…" }, una operación void devuelve true.
Lectura
| Método | HTTP | Parámetros | Resultado |
|---|---|---|---|
getMe | GET | — | { id, username, is_bot } |
getUpdates | GET | ?offset= &timeout= &limit= | matriz de Update |
getChat | GET | ?chat_id= | { id, type, members } |
Envío y acciones
| Método | Cuerpo | Notas |
|---|---|---|
sendMessage | { chat_id, text, reply_markup?, reply_to_message_id? } | texto, teclado en línea opcional y respuesta citada |
sendPhoto | { chat_id, photo, mime, w, h, caption? } | photo — base64 de los bytes de la imagen |
sendDocument | { chat_id, document, file_name, mime, size } | document — base64 del archivo |
editMessageText | { chat_id, message_id, text } | editar el texto |
editMessageReplyMarkup | { chat_id, message_id, reply_markup } | un markup vacío quita el teclado |
deleteMessage | { chat_id, message_id } | eliminar |
forwardMessage | { chat_id, text, from? } | from — la etiqueta «reenviado de» |
sendPoll | { chat_id, question, options[], is_anonymous? } | encuesta |
pinChatMessage | { chat_id, message_id } | fijar |
unpinChatMessage | { chat_id } | desfijar |
setMessageReaction | { chat_id, message_id, emoji, add? } | add por defecto es true |
sendChatAction | { chat_id, action? } | muestra «escribiendo…» |
answerCallbackQuery | { callback_query_id } | ack no-op (Koto no tiene spinner en el botón) |
Menú de comandos (la lista «/»)
| Método | Cuerpo | Notas |
|---|---|---|
setMyCommands | { commands: [{ command, description }] } | lista pública de comandos |
getMyCommands | — | lista actual |
deleteMyCommands | — | vaciar |
Webhooks
| Método | Cuerpo | Notas |
|---|---|---|
setWebhook | { url, secret_token? } | la pasarela hace POST de las actualizaciones a url; secret_token se devuelve como X-Bot-Webhook-Secret |
deleteWebhook | — | vuelve a getUpdates |
getWebhookInfo | — | { url, pending_update_count, … } |
El objeto Update
getUpdates (y el cuerpo del POST del webhook) devuelven un Update. Se rellena exactamente un campo de datos por actualización:
{
"update_id": 42,
"message": { … }, // new incoming message
"edited_message": { … }, // a message's text was edited
"callback_query": { … }, // inline button press
"poll_answer": { … }, // poll vote
"message_reaction": { … } // reaction toggled
}Message
{
"message_id": "…",
"date": 1733836800,
"chat": { "id": "…" },
"from": { "id": "KOTO-…" },
"text": "hello",
"caption": "…", // for a photo/document
"photo": { "data": "<base64>", "mime": "image/jpeg", "w": 1280, "h": 720 },
"document": { "data": "<base64>", "file_name": "…", "mime": "…", "size": 1234 },
"reply_to_message": { … }, // if this is a quoted reply
"forward_from": "Alice", // if forwarded
"reply_markup": [[ … ]] // attached keyboard
}callback_query: { id, from, chat, message_id, data } — pasa chat.id a sendMessage para responder a la pulsación.
Mini aplicaciones
Una mini aplicación es una página web que el bot abre dentro de un chat de Koto. El usuario pulsa el botón de la aplicación (▦) junto al campo de entrada, la página se carga en un entorno aislado y puede comunicarse con el bot. La URL debe ser HTTPS.
Indicar la URL de la mini aplicación
A través de un cliente de Koto (bot → Mini App → pegar la URL) o a través del registro con el token de gestión del bot:
PUT /v1/bots/<token>/webapp {"url": "https://example.com/app"}El puente KotoWebApp
<script src="koto-webapp.js"></script>
<script>
KotoWebApp.ready(); // theme + platform from the host
KotoWebApp.MainButton.setText("Done").show();
KotoWebApp.MainButton.onClick(function () {
KotoWebApp.sendData(JSON.stringify({ ok: true })); // → sent to the bot
});
</script>| Miembro | Qué hace |
|---|---|
ready() | informar al host de que la página se ha cargado; rellena themeParams/platform |
sendData(string) | enviar una cadena al bot, luego la aplicación se cierra |
close() / expand() | cerrar / expandir la hoja a toda la altura |
openLink(url) | abrir una URL en el navegador del usuario |
themeParams | colores del host (también como variables CSS --koto-*) |
MainButton | .setText .show .hide .enable .disable .onClick |
onEvent(name, cb) | eventos: ready, themeChanged, mainButtonClicked |
sendData(s) entrega s al bot como un mensaje entrante normal (el bot lo ve a través de getUpdates/webhook). Envía JSON compacto y analízalo en el bot.
Límites y particularidades
- • Límite de tasa: por bot, 30 peticiones/seg por defecto →
429si se supera. - • Multimedia — base64 en línea: envía los bytes directamente en
photo/document, sin carga en dos pasos nifile_id. - • Los identificadores son Koto ID (
KOTO-…para usuarios,bot-…para bots), no numéricos. - • End-to-end: los mensajes se cifran en tránsito, el relé ciego nunca ve el texto plano. La pasarela descifra en nombre del bot, por lo que el operador del bot (y la pasarela que lo aloja) ven lo que se escribe al bot. Los chats con un bot no son privados frente al propio bot. Esto no afecta a las conversaciones usuario↔usuario.
Estado de funciones
| Función | Estado |
|---|---|
| Texto, foto, documento, encuestas | ✅ implementado |
| Edit / delete / pin / reacciones / forward | ✅ implementado |
| Teclados en línea + pulsaciones (callback_query) | ✅ implementado |
| Menú de comandos (setMyCommands) | ✅ implementado |
| getUpdates long-poll y webhooks | ✅ implementado |
| Respuestas citadas | ✅ implementado |
| Bots en grupos/canales (>2 participantes) | ⚠️ los métodos funcionan en un grupo donde el bot ya está; añadirlo a un grupo a través de la Bot API aún no está conectado |
| Modo en línea (@bot … en cualquier chat) | ❌ aún no compatible |
| Pagos en el bot | ❌ aún no compatible |
Alojamiento
La mayoría de los bots no necesitan alojamiento: trabajas a través de la pasarela gestionada de Koto — basta con tu token kbot_… y la URL base pública.
¿Necesitas control total sobre las claves? La pasarela y un único conector (mantienes las claves del bot tú mismo, la misma API) se pueden desplegar por tu cuenta. Las opciones de despliegue y las variables de entorno se describen en el README del repositorio koto-bot — aquí, en la referencia pública de la API, no los duplicamos.