Дві команди · Чотири авантюристи · Літо 2026

Швейцарія & Італія

28 червня — 11 липня · Варшава → Альпи → Доломіти → Варшава

14
Днів
4
Учасники
5
Країн
0
% Пройдено
14 000
zł залишок
🟣 Команда 1
👩 Даша 👨 Віталік
0
витрачено zł
0
на особу
7 000
залишок
🔴 Команда 2
👨 Денис 👩 Інна
0
витрачено zł
0
на особу
7 000
залишок
ПРОГРЕС МАРШРУТУ
🇵🇱 🇩🇪 🇨🇭 🇨🇭 🇮🇹 🏁
🤖 Telegram: — не налаштовано
📅 Щоденний план
💰 Бюджет
Загальний бюджет
14 000
на 4 учасників
Бюджет на команду
7 000
на 2 особи
Бюджет на особу
3 500
з 4 учасників
Витрачено (загалом)
0
0% бюджету
🟣 Команда 1 · Даша & Віталік
0 zł
0 zł / особу
🔴 Команда 2 · Денис & Інна
0 zł
0 zł / особу
➕ Додати витрату
ХТО ПЛАТИТЬ:
🟣 Команда 1
🔴 Команда 2
👥 Загальні (÷2)
Загальні витрати діляться порівно між командами — кожна платить 50%
🧾 Журнал витрат
🟣 Команда 1
🔴 Команда 2
👥 Загальні
Витрат ще не додано
⚖️ Фінальний розрахунок
Хто кому і скільки винен?
Розрахунок автоматично оновлюється коли ви додаєте витрати
🟣 Команда 1 (Даша & Віталік)
0 zł
Власних: 0 · Загальних: 0
🔴 Команда 2 (Денис & Інна)
0 zł
Власних: 0 · Загальних: 0
РЕЗУЛЬТАТ
= 0 zł
Додайте витрати щоб побачити результат
👤 Розбивка по людях
Додайте витрати...
📊 Витрати за категоріями
Додайте витрати...
🗺️ Карта маршруту
ПОЛЬЩА АВСТРІЯ ШВЕЙЦАРІЯ ІТАЛІЯ ЧЕХІЯ Варшава 🏠 Нюрнберг 🇩🇪 Аппенцель 🇨🇭 Інтерлакен 🇨🇭 Лойкербад 🇨🇭 Ортізеї 🇮🇹 Чехія 🇨🇿 🥾🏔️ 🧗🏆 ЛЕГЕНДА Маршрут туди Маршрут назад 🇨🇭 Швейцарія 🇮🇹 Доломіти
Швейцарія Доломіти 🥾 Хайк🏆 Must-do
🎒 Підготовка
🥾 Спорядження
→ Треккінгові черевики (must!)
→ Палки (особливо Daubenhorn)
→ Рюкзак 20-30л + дощовик
→ Фляга 2л+
→ Сонцезахист SPF50+
→ Вітрівка
→ Аптечка + бінт
🚗 Машина
→ 🇨🇭 Він'єтка 40 CHF
→ 🇦🇹 Він'єтка 9.60 EUR
→ 🇨🇿 Він'єтка 230 CZK
→ Запасне колесо / ремкомплект
→ Mapy.cz офлайн
→ USB зарядники × 4
💳 Гроші
→ Revolut / Wise (без комісій)
→ CHF готівка ~100-150/день
→ Postomaten ATM Швейцарії
→ Tre Cime паркінг ~30 EUR
→ Дольміті Суперскі пас
🏔️ Доломіти tips
→ Tre Cime виїзд о 6:00 (!)
→ Lago di Braies до 8:00
→ Cinque Torri: канатка або 45 хв
→ Seceda: рання канатка
→ Завжди план Б
✅ Чеклист
📱 Telegram інтеграція
🤖 Налаштування
— не налаштовано
💬 Команди бота
💰 Додавання витрат
/add 150 їжаЗагальна витрата (÷2 між командами)
/d1 200 готельВитрата тільки Команди 1 (Даша & Віталік)
/d2 180 кафеВитрата тільки Команди 2 (Денис & Інна)
/паливо 320Швидка витрата на паливо (загальна)
📊 Статистика
/statПоточний стан бюджету обох команд
/settleРозрахунок — хто кому винен
/dayЩо на сьогодні по маршруту
/progressПрогрес маршруту у %
✅ Маршрут
/done Tre CimeПозначити етап пройденим
/nextНаступний крок маршруту
/mapПосилання на Google Maps
📨 Автоматичні нотифікації
Щодня о 7:00 — план дня, погода, нагадування
При позначенні пройдено — прогрес + баланс команд
💰При додаванні витрати — оновлений баланс
⚖️В кінці поїздки — повний фінальний розрахунок
📋 Google Apps Script — повний код бота
const BOT_TOKEN = 'ВАШ_TOKEN'; const CHAT_ID = 'ВАШ_CHAT_ID'; const SHEET_ID = 'ID_GOOGLE_SHEETS'; // Файл → Поділитись → Скопіювати ID // ─── ЩОДЕННІ НОТИФІКАЦІЇ ─── const TRIP_DAYS = { '2026-06-28': { emoji:'🚗', msg:'Виїзд з Варшави! Ціль — Нюрнберг (~10 год)', tip:'Заправся в Польщі 98-м — дешевше!' }, '2026-06-30': { emoji:'🥾', msg:'Хайк Schäfler–Saxer Lücke–Säntis!', tip:'Раннє виходження — туман розходиться до 9:00' }, '2026-07-04': { emoji:'🧗', msg:'ДЕНЬ Х! Gemmi Pass + Daubenhorn via ferrata!', tip:'Перевір спорядження ввечері. Удачі!' }, '2026-07-08': { emoji:'🏆', msg:'TRE CIME DI LAVAREDO! Найкращий хайк!', tip:'⚠️ ВИЇЖДЖАЙТЕ О 6:00! Паркінг повний до 9:00' }, '2026-07-11': { emoji:'🏠', msg:'Повертаємось до Варшави! Яка була поїздка?!' }, }; function sendDailyNotification() { const today = Utilities.formatDate(new Date(), 'Europe/Warsaw', 'yyyy-MM-dd'); const day = TRIP_DAYS[today]; if (!day) return; const ss = SpreadsheetApp.openById(SHEET_ID); const stats = getStats(ss); const text = [ `${day.emoji} *Швейцарія & Італія 2026 · ${today}*`, ``, day.msg, day.tip ? `\n💡 _${day.tip}_` : '', ``, `💰 *Бюджет:*`, `🟣 Команда 1: ${stats.t1} zł витрачено`, `🔴 Команда 2: ${stats.t2} zł витрачено`, `📊 Загалом: ${stats.total} zł з 14 000 zł`, ].join('\n'); sendMsg(text); } // ─── ОБРОБНИК КОМАНД ─── function doPost(e) { const update = JSON.parse(e.postData.contents); const msg = update.message; if (!msg || !msg.text) return; const text = msg.text.trim(); const ss = SpreadsheetApp.openById(SHEET_ID); // /add 150 їжа — загальна витрата if (text.match(/^\/add\s+(\d+)\s*(.*)/i)) { const [, amt, desc] = text.match(/^\/add\s+(\d+)\s*(.*)/i); const who = msg.from.first_name; addExpense(ss, 'ЗАГАЛЬНА', desc || 'витрата', Number(amt), who); const half = Math.round(amt/2); sendMsg(`✅ Додано: *${desc || 'витрата'}* — ${amt} zł\n🟣 К1: −${half} zł 🔴 К2: −${half} zł\nДодав: ${who}`); return; } // /d1 200 готель — тільки Команда 1 if (text.match(/^\/d1\s+(\d+)\s*(.*)/i)) { const [, amt, desc] = text.match(/^\/d1\s+(\d+)\s*(.*)/i); addExpense(ss, 'КОМАНДА_1', desc || 'витрата', Number(amt), msg.from.first_name); sendMsg(`✅ 🟣 Команда 1: *${desc || 'витрата'}* — ${amt} zł`); return; } // /d2 200 кафе — тільки Команда 2 if (text.match(/^\/d2\s+(\d+)\s*(.*)/i)) { const [, amt, desc] = text.match(/^\/d2\s+(\d+)\s*(.*)/i); addExpense(ss, 'КОМАНДА_2', desc || 'витрата', Number(amt), msg.from.first_name); sendMsg(`✅ 🔴 Команда 2: *${desc || 'витрата'}* — ${amt} zł`); return; } // /stat — поточний стан if (text === '/stat') { const s = getStats(ss); const bar = '█'.repeat(Math.round(s.pct/10)) + '░'.repeat(10-Math.round(s.pct/10)); sendMsg([ `📊 *Бюджет поїздки*`, ``, `🟣 Команда 1 (Даша & Віталік)`, ` Витрачено: ${s.t1} zł | На особу: ${Math.round(s.t1/2)} zł`, ``, `🔴 Команда 2 (Денис & Інна)`, ` Витрачено: ${s.t2} zł | На особу: ${Math.round(s.t2/2)} zł`, ``, `💰 Загалом: ${s.total} / 14000 zł (${s.pct}%)`, `${bar}`, `💚 Залишок: ${14000 - s.total} zł`, ].join('\n')); return; } // /settle — розрахунок if (text === '/settle') { const s = getStats(ss); const diff = s.t1 - s.t2; const absDiff = Math.abs(diff); const who = diff > 0 ? 'Команда 2 (Денис & Інна)' : 'Команда 1 (Даша & Віталік)'; const owes = diff > 0 ? 'Команда 1' : 'Команда 2'; sendMsg([ `⚖️ *Фінальний розрахунок*`, ``, `🟣 Команда 1: ${s.t1} zł`, `🔴 Команда 2: ${s.t2} zł`, ``, absDiff < 10 ? `✅ Рахунок рівний! Нікому нічого не винні 🎉` : `💸 *${owes} повинна сплатити ${who}: ${absDiff} zł*`, ``, `Тобто: ${Math.round(absDiff/2)} zł з кожного учасника`, ].join('\n')); return; } } // ─── ДОПОМІЖНІ ФУНКЦІЇ ─── function addExpense(ss, team, desc, amount, who) { const sheet = ss.getSheetByName('Витрати') || ss.insertSheet('Витрати'); const date = Utilities.formatDate(new Date(), 'Europe/Warsaw', 'dd.MM.yyyy'); sheet.appendRow([date, team, desc, amount, who]); } function getStats(ss) { const sheet = ss.getSheetByName('Витрати'); if (!sheet) return { t1:0, t2:0, total:0, pct:0 }; const rows = sheet.getDataRange().getValues().slice(1); let t1=0, t2=0, common=0; rows.forEach(r => { const team = String(r[1]).toUpperCase(); const amt = Number(r[3]) || 0; if (team === 'КОМАНДА_1') t1 += amt; else if (team === 'КОМАНДА_2') t2 += amt; else { common += amt; t1 += amt/2; t2 += amt/2; } }); const total = t1 + t2; return { t1: Math.round(t1), t2: Math.round(t2), total: Math.round(total), pct: Math.round(total/140) }; } function sendMsg(text) { UrlFetchApp.fetch(`https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`, { method:'post', contentType:'application/json', payload: JSON.stringify({ chat_id: CHAT_ID, text, parse_mode:'Markdown' }) }); } // Встановити вебхук (запусти один раз вручну): function setWebhook() { const url = `https://script.google.com/macros/s/ВАШ_SCRIPT_ID/exec`; UrlFetchApp.fetch( `https://api.telegram.org/bot${BOT_TOKEN}/setWebhook?url=${url}` ); }