Ejemplos
Ejemplos listos para copiar en curl, TypeScript y Python. Novedades, mensajes, tarjetas con botones, comandos, webhooks, publicar, grupos y texto en vivo.
Todos los ejemplos de esta página se corrieron contra Huwi con un bot real. Para los de TypeScript y Python necesitas huwi.ts o huwi.py, que están en Primeros pasos. Antes de correrlos:
export HUWI_KEY="hwk_tu_clave" # la clave de tu bot
export HUWI_URL="https://huwi.app" # opcional: es el valor por defecto- TypeScript: Node 22.6 o más con
node --experimental-strip-types archivo.ts, o contsx. - Python: Python 3 con
pip install requests.
Quién soy (bot.me)
curl -s "https://huwi.app/api/trpc/bot.me" \
-H "x-api-key: $HUWI_KEY"Pedir novedades y confirmar (bot.updates)
bot.updates espera hasta 25 segundos. Para confirmar, vuelve a pedir con offset igual al id de la última novedad más uno: lo anterior se da por recibido.
# Pedido largo: espera hasta 25 s a que haya novedades.
curl -s -G "https://huwi.app/api/trpc/bot.updates" \
-H "x-api-key: $HUWI_KEY" \
--data-urlencode 'input={"json":{"timeout":25000}}'
# Confirmar: vuelve a pedir con offset = id de la última novedad + 1.
curl -s -G "https://huwi.app/api/trpc/bot.updates" \
-H "x-api-key: $HUWI_KEY" \
--data-urlencode 'input={"json":{"offset":"107652223732637697","timeout":25000}}'El offset es el id más uno
Si pasas el id de la última novedad tal cual, Huwi te la entrega otra vez. Los ids son números de 18 dígitos: en
JavaScript sácales el más uno con BigInt (no con Number).
Mandar un mensaje de texto
curl -s "https://huwi.app/api/trpc/message.send" \
-H "x-api-key: $HUWI_KEY" \
-H "content-type: application/json" \
-d '{"json":{"chatId":"107652219945181184","clientId":"saludo-1","text":"Hola desde curl"}}'El clientId hace el envío idempotente: si tu programa reintenta con el mismo clientId, Huwi devuelve el mismo mensaje en lugar de mandar otro. Usa un id que salga de la novedad (eco-${update.id}) cuando contestes algo.
Una tarjeta con botones y el toque
Manda la tarjeta con components. El toque llega como novedad action; contesta con un mensaje y, si quieres, bot.answerAction.
curl -s "https://huwi.app/api/trpc/message.send" \
-H "x-api-key: $HUWI_KEY" -H "content-type: application/json" \
-d '{"json":{"chatId":"107652219945181184","clientId":"pedido-1042","components":{"v":1,"body":[
{"type":"card","title":"Pedido #1042","subtitle":"En camino"},
{"type":"actions","actions":[{"id":"track","label":"Rastrear","kind":"callback"}]}
]}}}'Los componentes están en la galería.
Comandos y /start
Guarda los comandos una vez y atiende /start (con su carga útil) y /help.
curl -s "https://huwi.app/api/trpc/bot.setCommands" \
-H "x-api-key: $HUWI_KEY" -H "content-type: application/json" \
-d '{"json":{"commands":[{"command":"help","description":"Qué puedo hacer"},{"command":"pedido","description":"Ver el estado de mi pedido"}]}}'Recibir por webhook
Poner el webhook y un receptor que verifica Huwi-Signature. Mira Webhooks para el detalle del algoritmo.
# Poner el webhook. Devuelve {"secret":"whsec_..."}: se ve una sola vez, guárdalo.
curl -s "https://huwi.app/api/trpc/bot.setWebhook" \
-H "x-api-key: $HUWI_KEY" -H "content-type: application/json" \
-d '{"json":{"url":"https://mi-bot.example.com/huwi"}}'
# Ver si está puesto y desde cuándo falla.
curl -s "https://huwi.app/api/trpc/bot.webhookInfo" -H "x-api-key: $HUWI_KEY"
# Quitarlo y volver a bot.updates.
curl -s -X POST "https://huwi.app/api/trpc/bot.deleteWebhook" -H "x-api-key: $HUWI_KEY"Publicar
post.create publica en la cuenta del bot (necesita el permiso post). Una foto se sube primero: upload.create te da dónde, la subes con PUT y usas la url que devolvió. Hay un tope de 50 publicaciones por día.
# Publicación de solo texto.
curl -s "https://huwi.app/api/trpc/post.create" \
-H "x-api-key: $HUWI_KEY" -H "content-type: application/json" \
-d '{"json":{"media":{"type":"text","background":"none"},"caption":"Hola desde un bot #huwibots"}}'Un video se sube igual, con contentType: "video/mp4", y media lleva url, posterUrl, width, height y durationSec; prepáralo como lo hace el celular (H.264, audio AAC e índice al principio).
En un grupo
import { mutate, query } from "./huwi.ts";
// En un grupo, las novedades traen el `chatId` del grupo. Con "solo menciones, comandos y respuestas",
// el bot recibe únicamente lo que le habla a él; con "todo", todos los mensajes.
const me = await query("bot.me");
async function handle(update: any) {
if (update.type !== "message") return;
const { text, command } = update.payload as { text: string; command?: string };
const mentioned = text.toLowerCase().includes(`@${me.username}`);
if (command === "help" || mentioned) {
// En un grupo el límite es de 20 mensajes por minuto, sin turnos.
await mutate("message.send", { chatId: update.chatId, clientId: `g-${update.id}`, text: "Aquí estoy. Escribe /help para ver qué hago." });
}
}
let offset: string | undefined;
while (true) {
const { updates } = await query("bot.updates", { offset, timeout: 25000 });
for (const update of updates) {
await handle(update);
offset = String(BigInt(update.id) + 1n);
}
}Texto que se va escribiendo
Para una respuesta que llega de a poco (por ejemplo, la de un modelo), manda un mensaje y edítalo a medida que llega el texto.
import { mutate } from "./huwi.ts";
// Texto que se va escribiendo (por ejemplo, la respuesta de un modelo): se manda un mensaje y se edita.
async function* model(): AsyncGenerator<string> {
for (const word of "Claro, te cuento cómo va tu pedido: sale hoy y llega en la tarde.".split(" ")) {
await new Promise((resolve) => setTimeout(resolve, 150));
yield `${word} `;
}
}
const chatId = process.env.CHAT_ID!;
const message = await mutate("message.send", { chatId, clientId: crypto.randomUUID(), text: "…" });
let text = "";
let last = 0;
for await (const piece of model()) {
text += piece;
// Edita como mucho cada ~700 ms: cada edición es un pedido más y llega a la persona en vivo.
if (Date.now() - last > 700) {
await mutate("message.edit", { messageId: message.id, text });
last = Date.now();
}
}
await mutate("message.edit", { messageId: message.id, text: text.trim() }); // la versión finalbot.startStream, appendStream y endStream
Existen en la API para escribir un mensaje en vivo, pero hoy, con la base de datos real, bot.startStream responde
BAD_REQUEST con use-message. Usa message.send y message.edit, como arriba.
Manejar errores
Cuando algo falla, la API responde con un código HTTP y un cuerpo así:
{ "error": { "json": { "message": "botRate", "code": -32029, "data": { "code": "TOO_MANY_REQUESTS", "httpStatus": 429 } } } }Lee data.code y message (las tablas están en Límites y errores). Ante un TOO_MANY_REQUESTS, espera un poco y reintenta con el mismo clientId. Los ejemplos de huwi.ts y huwi.py lanzan un HuwiError con code y status:
import { HuwiError, mutate } from "./huwi.ts";
try {
await mutate("message.send", { chatId, clientId: "x1", text: "Hola" });
} catch (error) {
if (error instanceof HuwiError && error.code === "TOO_MANY_REQUESTS") {
await new Promise((resolve) => setTimeout(resolve, 3000)); // espera y vuelve a intentar
} else {
throw error;
}
}