1) Estructura de la hoja
Crea una hoja de cálculo llamada Gastos con una pestaña Movimientos (fila 1 = encabezados):
AFecha (yyyy-mm-dd HH:mm:ss)BUsuario (Telegram @username o chat id)CMontoDCategoríaENota
2) BotFather: crea tu bot
- En Telegram, abre @BotFather → envía /newbot.
- Elige nombre y usuario, copia el TOKEN (guárdalo).
- Opcional: en /setprivacy pon DISABLED (para recibir todos los mensajes del chat).
3) Apps Script: Web App + código
- En tu Google Sheet: Extensiones → Apps Script.
- Cambia el nombre del proyecto a telegram-gastos.
- Pega este código en Code.gs y ajusta las constantes
SHEET_ID,SHEET_NAMEyALLOWED_USERS:
/** CONFIG **/
const TELEGRAM_TOKEN = 'TU_TOKEN_DE_BOTFATHER';
const SHEET_ID = 'TU_ID_DE_SPREADSHEET';
const SHEET_NAME = 'Movimientos';
const TIMEZONE = 'America/Mexico_City';
/** Usuarios permitidos (username sin @ o chat_id numérico). Deja [] para permitir a cualquiera. **/
const ALLOWED_USERS = ['tu_usuario']; // p.ej. ['diegoalejandre', 123456789]
/** Punto de entrada del Webhook (POST desde Telegram) **/
function doPost(e) {
try {
const update = JSON.parse(e.postData.contents);
if (!update.message) return ContentService.createTextOutput('ok');
const msg = update.message;
const chat = msg.chat;
const text = (msg.text || '').trim();
const uname = (msg.from.username || String(chat.id)).toString();
if (!isAllowed(uname)) {
sendMessage(chat.id, '⛔ No estás autorizado para usar este bot.');
return ContentService.createTextOutput('forbidden');
}
if (text.startsWith('/start')) {
sendMessage(chat.id, '¡Hola! Usa:\n/gasto <monto> <categoria> [nota opcional]\nEj.: /gasto 250 comida taquitos');
return ContentService.createTextOutput('ok');
}
if (text.startsWith('/gasto')) {
const { ok, error, data } = parseGasto(text);
if (!ok) {
sendMessage(chat.id, 'Formato inválido. Ej.: /gasto 120 gasolina nota opcional');
return ContentService.createTextOutput('bad');
}
const saved = appendRow(uname, data.monto, data.categoria, data.nota);
sendMessage(chat.id, saved ? '💾 Registrado ✅' : '⚠️ No se pudo guardar.');
return ContentService.createTextOutput('ok');
}
sendMessage(chat.id, 'Comando no reconocido. Usa /gasto o /start.');
return ContentService.createTextOutput('ok');
} catch (err) {
console.error(err);
return ContentService.createTextOutput('error');
}
}
/** Helpers **/
function isAllowed(uname) {
if (!ALLOWED_USERS.length) return true;
return ALLOWED_USERS.map(String).includes(String(uname));
}
function parseGasto(text) {
// /gasto 250 comida tacos al pastor
const parts = text.split(/\s+/).slice(1); // quita "/gasto"
const monto = Number(parts[0]?.replace(',', '.'));
const categoria = parts[1];
const nota = parts.slice(2).join(' ') || '';
if (!monto || !categoria) return { ok:false, error:'Formato', data:null };
return { ok:true, data:{ monto, categoria, nota } };
}
function appendRow(uname, monto, categoria, nota) {
const ss = SpreadsheetApp.openById(SHEET_ID);
const sh = ss.getSheetByName(SHEET_NAME);
if (!sh) throw new Error('No existe la pestaña ' + SHEET_NAME);
const now = new Date();
const fecha = Utilities.formatDate(now, TIMEZONE, 'yyyy-MM-dd HH:mm:ss');
sh.appendRow([fecha, '@' + uname, monto, categoria, nota]);
return true;
}
function sendMessage(chatId, text) {
const url = 'https://api.telegram.org/bot' + TELEGRAM_TOKEN + '/sendMessage';
const payload = { chat_id: chatId, text: text };
const params = { method:'post', contentType:'application/json', payload: JSON.stringify(payload), muteHttpExceptions:true };
const res = UrlFetchApp.fetch(url, params);
return res.getResponseCode() === 200;
}
/** Utilidad para probar localmente el parser */
function _testParser() {
console.log(parseGasto('/gasto 199.90 super verdura y leche'));
}
Publica como Web App: Deploy → New deployment → tipo Web app → Execute as: Me → Who has access: Anyone → Deploy. Copia la URL del Web App.
4) Conecta el Webhook
- Abre en el navegador (reemplazando TOKEN y URL codificada):
https://api.telegram.org/botTOKEN/setWebhook?url=URL_WEB_APP - Debe responder {"ok":true,...}. Listo, Telegram enviará los mensajes al Web App.
- Para ver errores, revisa Apps Script → Executions (logs).
5) Comandos y ejemplos
/start— ayuda rápida y formato./gasto 250 comida taquitos— guarda: 250 | comida | taquitos./gasto 120 gasolina— sin nota.
6) Seguridad, límites y log
- Permisos: limita a tus usuarios en
ALLOWED_USERS(username sin @ o chat id). - Rotación de token: si sospechas exposición, en BotFather → /revoke.
- Cuotas: Apps Script tiene límites diarios; para uso personal/PyME alcanza de sobra.
- Auditoría: agrega otra pestaña “Log” para guardar también el raw update si lo requieres.
¿Prefieres que lo implemente y te lo deje con dashboard?
Configuro el bot, tu Sheet con pivotes y un dashboard simple para ver gastos por categoría/mes.