const TOKEN = 'токен_бота';
const SHEET_ID = 'ід_таблиці';
const USERS_DB = {
314639039: "Сергій",
2105359407: "Леся",
5188583639: "Семен"
};
function doPost(e) {
try {
const data = JSON.parse(e.postData.contents);
if (!data.message || !data.message.text) return HtmlService.createHtmlOutput("OK");
const chatId = data.message.chat.id;
const text = data.message.text.trim().toLowerCase();
let userLabel = USERS_DB[chatId];
if (!userLabel) {
const rawUrl = 'https://api.telegram.org/bot'+TOKEN+'/sendMessage';
const cleanUrl = rawUrl.replace(/\s+/g, '');
UrlFetchApp.fetch(cleanUrl, {
method: "post",
payload: JSON.stringify({ chat_id: chatId, text: "🔒 Доступ обмежено. Цей бот є приватним сімейним інструментом розробника." }),
contentType: "application/json",
muteHttpExceptions: true
});
return HtmlService.createHtmlOutput("OK");
}
const sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName("Витрати");
// ==========================================
// 1. ОБРОБКА КОМАНД СТАТИСТИКИ
// ==========================================
if (text === "📅 за день" || text === "🗓️ за місяць" || text === "📊 разом" || text === "за день" || text === "за місяць" || text === "разом") {
const stats = calculateStats(sheet, userLabel);
let responseMessage = "";
if (text.includes("за день")) {
responseMessage = "📅 **За сьогодні :** " + stats.userDay.toFixed(0) + " грн";
} else if (text.includes("за місяць")) {
responseMessage = "🗓️ **За місяць :** " + stats.userMonth.toFixed(0) + " грн";
} else if (text.includes("разом")) {
responseMessage = "📊 **Спільний сімейний звіт:**\n" +
"• Ви за сьогодні: " + stats.userDay.toFixed(0) + " грн\n" +
"• Ви за місяць: " + stats.userMonth.toFixed(0) + " грн\n" +
"• **ЗАГАЛОМ СЬОГОДНІ**: " + stats.totalDay.toFixed(0) + " грн\n" +
"• **ЗАГАЛОМ ЗА МІСЯЦЬ**: " + stats.totalMonth.toFixed(0) + " грн";
}
sendTextWithKeyboard(chatId, responseMessage);
return HtmlService.createHtmlOutput("OK");
}
// ==========================================
// 2. ОБРОБКА КОМАНДИ ВИВЕДЕННЯ ОСТАННІХ 5 ВИТРАТ
// ==========================================
if (text === "🔍 останні 5" || text === "останні 5") {
const rows = sheet.getDataRange().getValues();
let lastTransactions = [];
for (let i = rows.length - 1; i >= 1; i--) {
if (rows[i] && rows[i][4].toString() === userLabel) {
const rowDateRaw = rows[i][0];
let formattedDate = rowDateRaw.toString();
if (rowDateRaw instanceof Date) {
formattedDate = rowDateRaw.toLocaleString("uk-UA", {timeZone: "Europe/Kyiv"});
}
const dateOnly = formattedDate.split(",")[0];
const amount = parseFloat(rows[i][1]).toFixed(0);
const category = rows[i][2];
const description = rows[i][5] || "-";
lastTransactions.push(dateOnly + " — **" + amount + " грн**\n└ _" + category + "_ (" + description + ")");
if (lastTransactions.length === 5) break;
}
}
let responseMessage = "🔍 **Ваші останні 5 витрат:**\n\n";
if (lastTransactions.length === 0) {
responseMessage += "У таблиці ще немає жодного вашого запису.";
} else {
responseMessage += lastTransactions.join("\n\n");
}
sendTextWithKeyboard(chatId, responseMessage);
return HtmlService.createHtmlOutput("OK");
}
// ==========================================
// 3. ОБРОБКА КОМАНДИ ВИДАЛЕННЯ ОСТАННЬОГО ОСОБИСТОГО ЗАПИСУ
// ==========================================
if (text === "🗑️ видалити останній запис" || text === "видалити останній запис") {
const rows = sheet.getDataRange().getValues();
let rowToDelete = -1;
for (let i = rows.length - 1; i >= 1; i--) {
if (rows[i] && rows[i][4].toString() === userLabel) {
rowToDelete = i + 1;
break;
}
}
if (rowToDelete === -1) {
sendTextWithKeyboard(chatId, "❌ У таблиці не знайдено жодного вашого запису для видалення!");
return HtmlService.createHtmlOutput("OK");
}
const targetRowData = sheet.getRange(rowToDelete, 1, 1, 4).getValues();
const deletedAmount = parseFloat(targetRowData[0][1]);
const deletedCategory = targetRowData[0][2];
sheet.deleteRow(rowToDelete);
const stats = calculateStats(sheet, userLabel);
const responseMessage = "🗑️ **Успішно видалено ваш останній запис:** " + deletedAmount.toFixed(0) + " грн (" + deletedCategory + ")\n\n" +
"📊 **Оновлений звіт для контролю:**\n" +
"• Ви за сьогодні: " + stats.userDay.toFixed(0) + " грн\n" +
"• Разом сьогодні: " + stats.totalDay.toFixed(0) + " грн";
sendTextWithKeyboard(chatId, responseMessage);
return HtmlService.createHtmlOutput("OK");
}
// ==========================================
// 4. ОБРОБКА ВВЕДЕННЯ ВИТРАТ (ВИПРАВЛЕНО ПАРСИНГ)
// ==========================================
const parts = data.message.text.trim().split(/\s+/);
const amount = parseFloat(parts[0]);
if (isNaN(amount)) {
sendTextWithKeyboard(chatId, "❌ Невірний формат.\n\n• Щоб записати витрату, продиктуйте: [Сума] [Категорія] [Опис]\n• Або натисніть кнопку статистики нижче 👇");
return HtmlService.createHtmlOutput("OK");
}
// ВИПРАВЛЕННЯ: беремо саме елементи масиву як чистий текст
const category = parts[1] || "Інше";
const description = parts.slice(2).join(" ") || "-";
const date = new Date().toLocaleString("uk-UA", {timeZone: "Europe/Kyiv"});
sheet.appendRow([date, amount, category, "Телеграм", userLabel, description]);
sendTextWithKeyboard(chatId, "✅ **Записано (" + userLabel + "):** " + amount.toFixed(0) + " грн в категорію \"" + category + "\"");
} catch (error) {
try {
const dataError = JSON.parse(e.postData.contents);
if (dataError.message) {
const rawUrl = 'https://api.telegram.org/bot'+TOKEN+'/sendMessage';
const cleanUrlErr = rawUrl.replace(/\s+/g, '');
UrlFetchApp.fetch(cleanUrlErr, {
method: "post",
payload: JSON.stringify({ chat_id: dataError.message.chat.id, text: "⚠️ Помилка: " + error.toString() }),
contentType: "application/json",
muteHttpExceptions: true
});
}
} catch (e) {}
}
return HtmlService.createHtmlOutput("OK");
}
function calculateStats(sheet, currentUser) {
const rows = sheet.getDataRange().getValues();
const result = { userDay: 0, userMonth: 0, totalDay: 0, totalMonth: 0 };
if (rows.length <= 1) return result;
const now = new Date();
const todayStr = now.toLocaleDateString("uk-UA", {timeZone: "Europe/Kyiv"});
const currentMonth = now.getMonth();
const currentYear = now.getFullYear();
for (let i = 1; i < rows.length; i++) {
let rowDate = rows[i][0];
const amount = parseFloat(rows[i][1]);
const rowUser = rows[i][4] ? rows[i][4].toString() : "";
if (isNaN(amount) || !rowDate) continue;
let parsedDate;
if (rowDate instanceof Date) {
parsedDate = rowDate;
} else {
const cleanStr = rowDate.toString().split(",")[0].trim();
const parts = cleanStr.split(".");
if (parts.length === 3) {
parsedDate = new Date(parseInt(parts[2], 10), parseInt(parts[1], 10) - 1, parseInt(parts[0], 10));
}
}
if (parsedDate) {
const rowDateStr = parsedDate.toLocaleDateString("uk-UA", {timeZone: "Europe/Kyiv"});
const isToday = (rowDateStr === todayStr);
const isCurrentMonth = (parsedDate.getMonth() === currentMonth && parsedDate.getFullYear() === currentYear);
if (isToday) result.totalDay += amount;
if (isCurrentMonth) result.totalMonth += amount;
if (rowUser === currentUser) {
if (isToday) result.userDay += amount;
if (isCurrentMonth) result.userMonth += amount;
}
}
}
return result;
}
function sendTextWithKeyboard(chatId, text) {
const rawUrl = 'https://api.telegram.org/bot'+TOKEN+'/sendMessage';
const cleanUrl = rawUrl.replace(/\s+/g, '');
const keyboard = {
keyboard: [
[{ text: "📅 За день" }, { text: "🗓️ За місяць" }],
[{ text: "📊 Разом" }, { text: "🔍 Останні 5" }],
[{ text: "🗑️ Видалити останній запис" }]
],
resize_keyboard: true,
one_time_keyboard: false
};
const payload = {
chat_id: chatId,
text: text,
parse_mode: "Markdown",
reply_markup: JSON.stringify(keyboard)
};
UrlFetchApp.fetch(cleanUrl, {
method: "post",
payload: JSON.stringify(payload),
contentType: "application/json",
muteHttpExceptions: true
});
}
function runManualTest() {
const mockEvent = {
postData: {
contents: JSON.stringify({
message: {
chat: { id: 2105339407 },
text: "150 кава сільпо"
}
})
}
};
doPost(mockEvent);
}
3. Створюємо нового Телеграм бота через супербота @BotFather командою /newbot . Копіюємо токен цього бота в наш скрипт замість токен_бота.
4. Взнаєм свій ідентифікатор та ідентифікатори членів родини через бота @userinfo3bot і прописуємо у масив USERS_DB . Також підставте ід будь-кого з родини у функцію runManualTest для ручного тестування роботи бота.
5. Зберігаємо скрипт бота і активуємо через "Ввести в дію" - "Нове введення в дію". Обов'язково вказуємо щоб бот виконувався від вашого імені (ваш e-mail) і доступ для всіх.
6. Проходимо авторизацію (натисніть Advanced -> Go to Untitled project (unsafe) -> Allow).
7. Скопіюйте URL-адресу вебзастосунку, яка з'явиться (вона закінчується на /exec).
8. Викличте в браузері https://api.telegram.org/botВАШ_ТОКЕН_БОТА/setWebhook?url=ВАША_АДРЕСА_СКРИПТА (замінивши токен і адресу на вже вам відомі) - ви маєте отримати json повідомлення що застосунок прив'язався до бота.
9. Поверніться в скрипт і запустіть вручну функцію runManualTest - у вашій таблиці має появитися перший запис.
Тепер або набирайте витрату вручну або наговорюйте через мікрофончик клавіатури у форматі: Сума Категорія Опис. Сума в цілих гривнях, категорія - одним словом, опис може складатись з декількох слів.
Комментариев нет:
Отправить комментарий