/** * KLINIKA FORMY CRM SYSTEM v3.3 - KOMPLETNY KOD * * CHANGELOG v3.3: * - Dodana kontrola limitów emaili (90 dziennie) * - Automatyczne odkładanie emaili gdy limit przekroczony * - Liczniki emaili z resetowaniem codziennym * - Zmieniona częstotliwość triggerów na co 2 godziny * - Dodany nocny processor dla zaległych emaili * - Monitoring wykorzystania limitów * - POPRAWIONE: Dodane wszystkie brakujące funkcje * - POPRAWIONE: Walidacja danych wejściowych * - POPRAWIONE: Obsługa wszystkich typów emaili w kolejce */// ==================== KONFIGURACJA GŁÓWNA ==================== const CONFIG = { SPREADSHEET_ID: '1Q52bzhfuYQUFteOcj4RQ4Vi5oU4sIS1OE-xDMjXQINA', EBOOK_SHEET_NAME: 'DARMOWY EBOOK', PROGRAM_SHEET_NAME: 'ZGŁOSZENIE - Z EBOOKA', EMAIL_SENDER: 'owsiany1995@gmail.com', // GetResponse GETRESPONSE_API_KEY: '6vszd9996ta5xrzvzju0jqvfwukauzp2', GETRESPONSE_CAMPAIGN_ID: 'GS3qW', // Limity emaili EMAIL_LIMITS: { DAILY_LIMIT: 90, // Bezpieczny limit dzienny HOURLY_LIMIT: 20, // Limit godzinowy BATCH_SIZE: 5 // Ile emaili przetwarzać na raz }, // Kolumny arkusza COLUMNS: { EBOOK_GETRESPONSE: 11, // K PROGRAM_GETRESPONSE: 22, // V EMAIL_1_STATUS: 12, // L EMAIL_2_STATUS: 13, // M EMAIL_3_STATUS: 14, // N EMAIL_4_STATUS: 15, // O PURCHASE_STATUS: 18, // R KONSULTACJA_POTWIERDZENIE: 23, // W KONSULTACJA_24H: 24, // X KONSULTACJA_2H: 25, // Y KONSULTACJA_15MIN: 26, // Z KONSULTACJA_10MIN_PO: 27, // AA KONSULTACJA_24H_PO: 28, // AB KONSULTACJA_3DNI_PO: 29, // AC CALENDAR_EVENT_ID: 30, // AD ANKIETA_STATUS: 31 // AE }, // Opóźnienia emaili (w milisekundach) DELAYS: { EMAIL_2: 5 * 60 * 60 * 1000, // 5 godzin EMAIL_3: 24 * 60 * 60 * 1000, // 24 godziny EMAIL_4: 48 * 60 * 60 * 1000, // 48 godzin KONSULTACJA_10MIN: 10 * 60 * 1000, // 10 minut KONSULTACJA_24H: 24 * 60 * 60 * 1000, KONSULTACJA_3DNI: 72 * 60 * 60 * 1000, KONSULTACJA_7DNI: 168 * 60 * 60 * 1000 }, // URLs URLS: { LANDING_PAGE_2: 'https://akf.klinika-formy.pl/wyslane/', CONFIRMATION_PAGE: 'https://akf.klinika-formy.pl/zgloszenie-przeslane/', OPINIONS: 'https://klinika-formy.pl/opinie/', FACEBOOK_GROUP: 'https://www.facebook.com/groups/klinikaformy', LOGO: 'https://akf.klinika-formy.pl/wp-content/uploads/2025/05/Bez-tytulu-1080-x-432-px-1.png', KAMIL_PHOTO: 'https://akf.klinika-formy.pl/wp-content/uploads/2025/07/@klinika.formy-1.png', PLATFORM_VIDEO: 'https://www.youtube.com/watch?v=KHkeiYJE6gg', GENERAL_PLAN_DOC: 'https://docs.google.com/document/d/1TS7NAsi-w_tsj4LyR0Szdjy1YyrZ6Aim3C9yCjttGAs/edit?usp=sharing', ANKIETA_FORM: 'https://akf.klinika-formy.pl/badanie/', PAYMENT_1350: 'https://buy.stripe.com/5kA4gz3m381Z0106oo', PAYMENT_3RATY: 'https://tubapay.pl/s/MPVlaYMD', PAYMENT_6RAT: 'https://tubapay.pl/s/vHNGc9CV', DOWNLOAD_PAGE: 'https://klinika-formy.pl/pobierz/' }, // System Settings QUEUE_PROCESSING_INTERVAL: 120, // minuty (co 2 godziny) MAX_QUEUE_AGE_DAYS: 30, SYSTEM_VERSION: '3.3' };// ==================== SYSTEM KONTROLI LIMITÓW ==================== function checkEmailQuota() { const properties = PropertiesService.getScriptProperties(); const today = new Date().toDateString(); const currentHour = new Date().getHours(); // Sprawdź dzienny limit const dailyCountKey = 'email_count_' + today; const dailyCount = parseInt(properties.getProperty(dailyCountKey) || '0'); if (dailyCount >= CONFIG.EMAIL_LIMITS.DAILY_LIMIT) { Logger.log('[Quota] Przekroczono dzienny limit emaili: ' + dailyCount); return false; } // Sprawdź godzinowy limit const hourlyCountKey = 'email_count_' + today + '_' + currentHour; const hourlyCount = parseInt(properties.getProperty(hourlyCountKey) || '0'); if (hourlyCount >= CONFIG.EMAIL_LIMITS.HOURLY_LIMIT) { Logger.log('[Quota] Przekroczono godzinowy limit emaili: ' + hourlyCount); return false; } return true; }function incrementEmailCounter() { const properties = PropertiesService.getScriptProperties(); const today = new Date().toDateString(); const currentHour = new Date().getHours(); // Zwiększ dzienny licznik const dailyCountKey = 'email_count_' + today; const dailyCount = parseInt(properties.getProperty(dailyCountKey) || '0'); properties.setProperty(dailyCountKey, String(dailyCount + 1)); // Zwiększ godzinowy licznik const hourlyCountKey = 'email_count_' + today + '_' + currentHour; const hourlyCount = parseInt(properties.getProperty(hourlyCountKey) || '0'); properties.setProperty(hourlyCountKey, String(hourlyCount + 1)); Logger.log(`[Quota] Emaile wysłane dziś: ${dailyCount + 1}/${CONFIG.EMAIL_LIMITS.DAILY_LIMIT}, w tej godzinie: ${hourlyCount + 1}/${CONFIG.EMAIL_LIMITS.HOURLY_LIMIT}`); }function getEmailQuotaStatus() { const properties = PropertiesService.getScriptProperties(); const today = new Date().toDateString(); const currentHour = new Date().getHours(); const dailyCount = parseInt(properties.getProperty('email_count_' + today) || '0'); const hourlyCount = parseInt(properties.getProperty('email_count_' + today + '_' + currentHour) || '0'); return { daily: { used: dailyCount, limit: CONFIG.EMAIL_LIMITS.DAILY_LIMIT, remaining: CONFIG.EMAIL_LIMITS.DAILY_LIMIT - dailyCount, percentage: Math.round((dailyCount / CONFIG.EMAIL_LIMITS.DAILY_LIMIT) * 100) }, hourly: { used: hourlyCount, limit: CONFIG.EMAIL_LIMITS.HOURLY_LIMIT, remaining: CONFIG.EMAIL_LIMITS.HOURLY_LIMIT - hourlyCount } }; }function checkEmailLimits() { const status = getEmailQuotaStatus(); Logger.log(`[Limits] Status: ${status.daily.used}/${status.daily.limit} emaili dziennie (${status.daily.percentage}%), ${status.hourly.used}/${status.hourly.limit} w tej godzinie`); if (status.daily.used >= CONFIG.EMAIL_LIMITS.DAILY_LIMIT) { Logger.log('[Limits] UWAGA: Limit dzienny wyczerpany!'); } else if (status.daily.used >= 75) { Logger.log('[Limits] UWAGA: Zbliżamy się do limitu dziennego!'); } return status; }// ==================== GŁÓWNY HANDLER POST ==================== function doPost(e) { const lock = LockService.getScriptLock(); try { lock.waitLock(10000); const data = e.parameter; // Walidacja danych wejściowych const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!data.email || !emailRegex.test(data.email)) { throw new Error('Nieprawidłowy adres email'); } if (!data.formType) { throw new Error('Brak typu formularza'); } const sheetName = data.formType === 'program' ? CONFIG.PROGRAM_SHEET_NAME : CONFIG.EBOOK_SHEET_NAME; const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const sheet = spreadsheet.getSheetByName(sheetName); if (!sheet) { throw new Error(`Arkusz nie istnieje: ${sheetName}`); } const timestamp = new Date(); let rowData = []; if (sheetName === CONFIG.EBOOK_SHEET_NAME) { rowData = processEbookForm(data, timestamp); } else { rowData = processProgramForm(data, timestamp); } sheet.appendRow(rowData); Logger.log(`[doPost] Zapisano formularz: ${data.email} w ${sheetName}`); return ContentService .createTextOutput(JSON.stringify({ 'result': 'success', 'message': `Zapisano w: ${sheetName}`, 'timestamp': timestamp.toISOString() })) .setMimeType(ContentService.MimeType.JSON); } catch (error) { Logger.log('[doPost] Błąd: ' + error.toString()); return ContentService .createTextOutput(JSON.stringify({ 'result': 'error', 'message': error.toString() })) .setMimeType(ContentService.MimeType.JSON); } finally { lock.releaseLock(); } }// ==================== SYSTEM KOLEJKOWY ==================== class QueueManager { constructor() { this.properties = PropertiesService.getScriptProperties(); } addToQueue(type, data, delayMillis = 0) { const queueId = Utilities.getUuid(); const sendTime = new Date().getTime() + delayMillis; const queueItem = { id: queueId, type: type, sendTime: sendTime, data: data, createdAt: new Date().getTime(), attempts: 0 }; this.properties.setProperty(`queue_${queueId}`, JSON.stringify(queueItem)); Logger.log(`[Queue] Dodano do kolejki: ${type} dla ${data.email} o ${new Date(sendTime)}`); return queueId; } processQueue() { const now = new Date().getTime(); const allProperties = this.properties.getProperties(); const stats = { processed: 0, skipped: 0, errors: 0, total: 0, quotaExceeded: 0 }; let emailsSentThisBatch = 0; for (const key in allProperties) { if (!key.startsWith('queue_')) continue; stats.total++; // Ogranicz batch processing if (emailsSentThisBatch >= CONFIG.EMAIL_LIMITS.BATCH_SIZE) { Logger.log('[Queue] Osiągnięto limit batch (' + CONFIG.EMAIL_LIMITS.BATCH_SIZE + '), pozostałe zostaną przetworzone później'); break; } try { const queueItem = JSON.parse(allProperties[key]); // Sprawdź czy czas wysłać if (queueItem.sendTime <= now) { // Przetwórz element const result = this.processQueueItem(queueItem); if (result.success) { // Usuń z kolejki this.properties.deleteProperty(key); stats.processed++; emailsSentThisBatch++; Logger.log(`[Queue] Przetworzono: ${queueItem.type} dla ${queueItem.data.email}`); } else if (result.skip) { // Pomiń i usuń this.properties.deleteProperty(key); stats.skipped++; Logger.log(`[Queue] Pominięto: ${queueItem.type} - ${result.reason}`); } else if (result.retry) { // Nie usuwaj, zostaw na później if (result.newSendTime) { queueItem.sendTime = result.newSendTime; this.properties.setProperty(key, JSON.stringify(queueItem)); } stats.quotaExceeded++; Logger.log(`[Queue] Odłożono na później: ${queueItem.type}`); } else { // Błąd - spróbuj ponownie później queueItem.attempts++; if (queueItem.attempts > 3) { this.properties.deleteProperty(key); stats.errors++; Logger.log(`[Queue] Błąd (usunięto po 3 próbach): ${queueItem.type}`); } else { queueItem.sendTime = now + 60 * 60 * 1000; // Spróbuj za godzinę this.properties.setProperty(key, JSON.stringify(queueItem)); } } } } catch (error) { Logger.log(`[Queue] Błąd przetwarzania ${key}: ${error.toString()}`); stats.errors++; // Usuń uszkodzony element this.properties.deleteProperty(key); } } if (stats.processed > 0 || stats.errors > 0 || stats.quotaExceeded > 0) { Logger.log(`[Queue] Statystyki: Przetworzono=${stats.processed}, Pominięto=${stats.skipped}, Błędy=${stats.errors}, Przekroczono limit=${stats.quotaExceeded}, Pozostało=${stats.total - stats.processed - stats.skipped - stats.errors}`); } return stats; } processQueueItem(queueItem) { const { type, data } = queueItem; try { // Sprawdź limit PRZED wysłaniem if (!checkEmailQuota()) { Logger.log('[Queue] Przekroczono limit emaili - odkładam na jutro o 2:00'); // Przesuń na jutro o 2 w nocy const tomorrow2am = new Date(); tomorrow2am.setDate(tomorrow2am.getDate() + 1); tomorrow2am.setHours(2, 0, 0, 0); return { retry: true, newSendTime: tomorrow2am.getTime() }; } // Sprawdź czy klient nie kupił (dla emaili konsultacyjnych) if (type.includes('consultation') && this.checkPurchaseStatus(data.email)) { return { skip: true, reason: 'Klient kupił' }; } // Sprawdź czy email nie był już wysłany if (this.wasEmailAlreadySent(type, data.email)) { return { skip: true, reason: 'Email już wysłany' }; } // Przetwórz według typu switch(type) { // Emaile ebook case 'ebook_email_1': wyslijEmail1(data.imie, data.nazwisko, data.email); incrementEmailCounter(); this.updateEmailStatus(data.email, CONFIG.COLUMNS.EMAIL_1_STATUS, 'Wysłano'); break; case 'ebook_email_2': wyslijEmail2Function(data.imie, data.nazwisko, data.email); incrementEmailCounter(); this.updateEmailStatus(data.email, CONFIG.COLUMNS.EMAIL_2_STATUS, 'Wysłano'); break; case 'ebook_email_3': wyslijEmail3Function(data.imie, data.nazwisko, data.email); incrementEmailCounter(); this.updateEmailStatus(data.email, CONFIG.COLUMNS.EMAIL_3_STATUS, 'Wysłano'); break; case 'ebook_email_4': wyslijEmail4Function(data.imie, data.nazwisko, data.email); incrementEmailCounter(); this.updateEmailStatus(data.email, CONFIG.COLUMNS.EMAIL_4_STATUS, 'Wysłano'); break; // Email potwierdzający konsultację case 'consultation_confirmation_email': wyslijEmailPotwierdzajacyKonsultacje(data.imie, data.nazwisko, data.email); incrementEmailCounter(); break; // Emaile konsultacyjne case 'consultation_confirmation': case 'consultation_24h_before': case 'consultation_2h_before': case 'consultation_15min_before': case 'consultation_10min_after': case 'consultation_24h_after': case 'consultation_3days_after': case 'consultation_7days_after': this.sendConsultationEmail(type, data); incrementEmailCounter(); break; default: Logger.log(`[Queue] Nieznany typ: ${type}`); return { success: false }; } return { success: true }; } catch (error) { // Jeśli błąd to limit Google - odłóż na później if (error.toString().includes('zbyt wiele razy')) { Logger.log('[Queue] Limit Google - odkładam na jutro o 2:00'); const tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setHours(2, 0, 0, 0); return { retry: true, newSendTime: tomorrow.getTime() }; } Logger.log(`[Queue] Błąd przetwarzania ${type}: ${error.toString()}`); return { success: false, error: error.toString() }; } } wasEmailAlreadySent(type, email) { const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const sheet = spreadsheet.getSheetByName(CONFIG.EBOOK_SHEET_NAME); const data = sheet.getDataRange().getValues(); // Mapowanie typów na kolumny statusu const statusColumns = { 'ebook_email_1': CONFIG.COLUMNS.EMAIL_1_STATUS, 'ebook_email_2': CONFIG.COLUMNS.EMAIL_2_STATUS, 'ebook_email_3': CONFIG.COLUMNS.EMAIL_3_STATUS, 'ebook_email_4': CONFIG.COLUMNS.EMAIL_4_STATUS }; const column = statusColumns[type]; if (!column) return false; for (let i = 1; i < data.length; i++) { if (data[i][3] === email) { const status = data[i][column - 1]; return status && status.toString().includes('Wysłano'); } } return false; } sendConsultationEmail(type, data) { const template = new ConsultationEmailTemplates(); let emailContent; let columnToUpdate; // Mapowanie typów na metody i kolumny const mapping = { 'consultation_confirmation': { method: 'getConfirmationEmail', column: CONFIG.COLUMNS.KONSULTACJA_POTWIERDZENIE }, 'consultation_24h_before': { method: 'get24hBeforeEmail', column: CONFIG.COLUMNS.KONSULTACJA_24H }, 'consultation_2h_before': { method: 'get2hBeforeEmail', column: CONFIG.COLUMNS.KONSULTACJA_2H }, 'consultation_15min_before': { method: 'get15minBeforeEmail', column: CONFIG.COLUMNS.KONSULTACJA_15MIN }, 'consultation_10min_after': { method: 'get10minAfterEmail', column: CONFIG.COLUMNS.KONSULTACJA_10MIN_PO }, 'consultation_24h_after': { method: 'get24hAfterEmail', column: CONFIG.COLUMNS.KONSULTACJA_24H_PO }, 'consultation_3days_after': { method: 'get3daysAfterEmail', column: CONFIG.COLUMNS.KONSULTACJA_3DNI_PO }, 'consultation_7days_after': { method: 'get7daysAfterEmail', column: CONFIG.COLUMNS.ANKIETA_STATUS } }; const config = mapping[type]; if (!config) { throw new Error(`Nieznany typ emaila konsultacyjnego: ${type}`); } emailContent = template[config.method](data); GmailApp.sendEmail(data.email, emailContent.subject, emailContent.text, { htmlBody: emailContent.html, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); // Aktualizuj status w arkuszu jeśli mamy rowNumber if (data.rowNumber) { this.updateConsultationStatus(data.rowNumber, config.column, `Wysłano: ${new Date()}`); } Logger.log(`[Queue] Wysłano ${type} do ${data.email}`); } checkPurchaseStatus(email) { const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const sheet = spreadsheet.getSheetByName(CONFIG.PROGRAM_SHEET_NAME); const data = sheet.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][3] === email) { return data[i][CONFIG.COLUMNS.PURCHASE_STATUS - 1] === 'KUPIŁ'; } } return false; } updateEmailStatus(email, column, status) { const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const sheet = spreadsheet.getSheetByName(CONFIG.EBOOK_SHEET_NAME); const data = sheet.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][3] === email) { sheet.getRange(i + 1, column).setValue(status); return; } } } updateConsultationStatus(rowNumber, column, status) { const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const sheet = spreadsheet.getSheetByName(CONFIG.PROGRAM_SHEET_NAME); sheet.getRange(rowNumber, column).setValue(status); } cleanup() { const now = new Date().getTime(); const maxAge = CONFIG.MAX_QUEUE_AGE_DAYS * 24 * 60 * 60 * 1000; const allProperties = this.properties.getProperties(); let cleaned = 0; for (const key in allProperties) { if (!key.startsWith('queue_')) continue; try { const queueItem = JSON.parse(allProperties[key]); // Usuń stare elementy if (queueItem.createdAt < (now - maxAge)) { this.properties.deleteProperty(key); cleaned++; } } catch (error) { // Usuń uszkodzone elementy this.properties.deleteProperty(key); cleaned++; } } // Wyczyść stare liczniki emaili const today = new Date().toDateString(); for (const key in allProperties) { if (key.startsWith('email_count_') && !key.includes(today)) { this.properties.deleteProperty(key); cleaned++; } } if (cleaned > 0) { Logger.log(`[Queue] Wyczyszczono ${cleaned} starych elementów`); } return cleaned; } getStatus() { const allProperties = this.properties.getProperties(); const now = new Date().getTime(); const status = { total: 0, pending: 0, overdue: 0, byType: {} }; for (const key in allProperties) { if (!key.startsWith('queue_')) continue; try { const queueItem = JSON.parse(allProperties[key]); status.total++; if (queueItem.sendTime > now) { status.pending++; } else { status.overdue++; } status.byType[queueItem.type] = (status.byType[queueItem.type] || 0) + 1; } catch (error) { // Ignoruj uszkodzone } } return status; } }// ==================== MODUŁ INTEGRACJI KALENDARZA ==================== class CalendarIntegration { constructor() { this.calendar = CalendarApp.getDefaultCalendar(); this.spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); this.sheet = this.spreadsheet.getSheetByName(CONFIG.PROGRAM_SHEET_NAME); this.queue = new QueueManager(); } scanForNewConsultations() { const now = new Date(); const endDate = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); Logger.log(`[Calendar] Skanowanie wydarzeń od ${now} do ${endDate}`); const events = this.calendar.getEvents(now, endDate); const stats = { new: 0, updated: 0, skipped: 0 }; events.forEach(event => { if (this.isConsultationEvent(event)) { const result = this.processConsultationEvent(event); stats[result]++; } }); Logger.log(`[Calendar] Wyniki skanowania: Nowe=${stats.new}, Zaktualizowane=${stats.updated}, Pominięte=${stats.skipped}`); return stats; } isConsultationEvent(event) { const title = event.getTitle().toLowerCase(); const keywords = ['konsultacja', 'spotkanie', 'meet', 'zmiana', 'rozmowa', 'call']; return keywords.some(keyword => title.includes(keyword)); } processConsultationEvent(event) { const attendees = event.getGuestList(); for (let attendee of attendees) { const email = attendee.getEmail(); if (email === CONFIG.EMAIL_SENDER) continue; const clientRow = this.findClientByEmail(email); if (!clientRow) { Logger.log(`[Calendar] Klient nieznaleziony w arkuszu: ${email}`); return 'skipped'; } const purchaseStatus = this.sheet.getRange(clientRow, CONFIG.COLUMNS.PURCHASE_STATUS).getValue(); if (purchaseStatus === 'KUPIŁ') { Logger.log(`[Calendar] Klient już kupił: ${email}`); this.sendWelcomeEmail(email, clientRow); return 'skipped'; } const existingEventId = this.sheet.getRange(clientRow, CONFIG.COLUMNS.CALENDAR_EVENT_ID).getValue(); if (existingEventId === event.getId()) { return 'skipped'; } this.sheet.getRange(clientRow, CONFIG.COLUMNS.CALENDAR_EVENT_ID).setValue(event.getId()); this.scheduleConsultationEmails(email, event, clientRow); return existingEventId ? 'updated' : 'new'; } return 'skipped'; } findClientByEmail(email) { const data = this.sheet.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][3] === email) { return i + 1; } } return null; } scheduleConsultationEmails(email, event, rowNumber) { const eventTime = event.getStartTime(); const clientData = this.sheet.getRange(rowNumber, 1, 1, 16).getValues()[0]; const emailData = { email: email, imie: clientData[1], nazwisko: clientData[2], eventTime: eventTime.getTime(), eventId: event.getId(), meetLink: this.extractMeetLink(event), rowNumber: rowNumber }; // Wyślij natychmiast potwierdzenie if (checkEmailQuota()) { this.sendConfirmationEmail(emailData); incrementEmailCounter(); this.sheet.getRange(rowNumber, CONFIG.COLUMNS.KONSULTACJA_POTWIERDZENIE).setValue('Wysłano: ' + new Date()); } else { // Dodaj do kolejki na później this.queue.addToQueue('consultation_confirmation', emailData, 2 * 60 * 60 * 1000); Logger.log(`[Calendar] Potwierdzenie dla ${email} dodane do kolejki (limit)`); } // Dodaj pozostałe emaile do kolejki const eventTimeMs = eventTime.getTime(); const now = new Date().getTime(); // Lista emaili do zaplanowania const emailSchedule = [ { type: 'consultation_24h_before', delay: eventTimeMs - now - (24 * 60 * 60 * 1000) }, { type: 'consultation_2h_before', delay: eventTimeMs - now - (2 * 60 * 60 * 1000) }, { type: 'consultation_15min_before', delay: eventTimeMs - now - (15 * 60 * 1000) }, { type: 'consultation_10min_after', delay: eventTimeMs - now + CONFIG.DELAYS.KONSULTACJA_10MIN }, { type: 'consultation_24h_after', delay: eventTimeMs - now + CONFIG.DELAYS.KONSULTACJA_24H }, { type: 'consultation_3days_after', delay: eventTimeMs - now + CONFIG.DELAYS.KONSULTACJA_3DNI }, { type: 'consultation_7days_after', delay: eventTimeMs - now + CONFIG.DELAYS.KONSULTACJA_7DNI } ]; // Dodaj do kolejki tylko te, które są w przyszłości emailSchedule.forEach(schedule => { if (schedule.delay > 0) { this.queue.addToQueue(schedule.type, emailData, schedule.delay); } }); Logger.log(`[Calendar] Zaplanowano ${emailSchedule.filter(s => s.delay > 0).length} emaili dla ${email}`); } extractMeetLink(event) { try { const description = event.getDescription() || ''; if (!description) return ''; const meetRegex = /https:\/\/meet\.google\.com\/[a-z-]+/i; const match = description.match(meetRegex); return match ? match[0] : ''; } catch (error) { Logger.log('[Calendar] Błąd wyciągania linku: ' + error.toString()); return ''; } } sendConfirmationEmail(emailData) { const template = new ConsultationEmailTemplates(); const { subject, html, text } = template.getConfirmationEmail(emailData); GmailApp.sendEmail(emailData.email, subject, text, { htmlBody: html, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); } sendWelcomeEmail(email, rowNumber) { if (!checkEmailQuota()) { Logger.log('[Calendar] Limit emaili - pomijam welcome email'); return; } const clientData = this.sheet.getRange(rowNumber, 1, 1, 16).getValues()[0]; const template = new ConsultationEmailTemplates(); const { subject, html, text } = template.getWelcomeEmail({ email: email, imie: clientData[1], nazwisko: clientData[2] }); GmailApp.sendEmail(email, subject, text, { htmlBody: html, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); incrementEmailCounter(); } }// ==================== GŁÓWNY PROCESSOR ==================== function mainProcessor() { const startTime = new Date(); Logger.log(`[Main] ====== START PRZETWARZANIA ${startTime} ======`); try { // Sprawdź status limitów const quotaStatus = checkEmailLimits(); Logger.log(`[Main] Limit emaili: ${quotaStatus.daily.used}/${quotaStatus.daily.limit} (pozostało: ${quotaStatus.daily.remaining})`); // 1. Skanuj kalendarz NAJPIERW (jeśli mamy zapas limitów) let calendarStats = { new: 0, updated: 0, skipped: 0 }; if (quotaStatus.daily.remaining > 10) { const calendar = new CalendarIntegration(); calendarStats = calendar.scanForNewConsultations(); } else { Logger.log('[Main] Pomijam skanowanie kalendarza - mało limitów'); } // 2. Przetwórz kolejkę RAZ (włącznie z nowymi elementami z kalendarza) const queue = new QueueManager(); const queueStats = queue.processQueue(); const endTime = new Date(); const duration = (endTime - startTime) / 1000; Logger.log(`[Main] ====== KONIEC PRZETWARZANIA ======`); Logger.log(`[Main] Czas wykonania: ${duration}s`); Logger.log(`[Main] Kolejka: Przetworzono=${queueStats.processed}, Błędy=${queueStats.errors}`); Logger.log(`[Main] Kalendarz: Nowe=${calendarStats.new}, Zaktualizowane=${calendarStats.updated}`); // Sprawdź końcowy status limitów const finalQuotaStatus = checkEmailLimits(); Logger.log(`[Main] Końcowy limit: ${finalQuotaStatus.daily.used}/${finalQuotaStatus.daily.limit}`); return { duration: duration, queue: queueStats, calendar: calendarStats, quota: finalQuotaStatus }; } catch (error) { Logger.log(`[Main] BŁĄD KRYTYCZNY: ${error.toString()}`); throw error; } }// ==================== NOCNY PROCESSOR ==================== function nightProcessor() { Logger.log('[Night] ====== NOCNE PRZETWARZANIE ======'); const startTime = new Date(); try { const queue = new QueueManager(); let totalProcessed = 0; const maxEmails = 30; // Bezpieczny limit nocny // Przetwarzaj w małych porcjach while (totalProcessed < maxEmails && checkEmailQuota()) { const result = queue.processQueue(); totalProcessed += result.processed; if (result.processed === 0) { Logger.log('[Night] Brak więcej emaili do przetworzenia'); break; } // Krótka przerwa między partiami Utilities.sleep(2000); } const duration = (new Date() - startTime) / 1000; Logger.log(`[Night] Przetworzono ${totalProcessed} emaili w ${duration}s`); return { processed: totalProcessed, duration: duration }; } catch (error) { Logger.log(`[Night] Błąd: ${error.toString()}`); throw error; } }// ==================== FUNKCJE GETRESPONSE ==================== function addContactToGetResponse(imie, email) { try { const url = 'https://api.getresponse.com/v3/contacts'; if (!email || !email.includes('@')) { return "Błąd: Nieprawidłowy format adresu email"; } const payload = { 'email': email, 'name': imie, 'campaign': { 'campaignId': CONFIG.GETRESPONSE_CAMPAIGN_ID } }; Logger.log('[GetResponse] Wysyłanie: ' + email); const options = { 'method': 'post', 'contentType': 'application/json', 'headers': { 'X-Auth-Token': 'api-key ' + CONFIG.GETRESPONSE_API_KEY }, 'payload': JSON.stringify(payload), 'muteHttpExceptions': true }; const response = UrlFetchApp.fetch(url, options); const responseCode = response.getResponseCode(); if (responseCode >= 200 && responseCode < 300) { return "Dodano do GetResponse"; } else if (responseCode === 409) { return "Już istnieje w GetResponse"; } else { return "Błąd GetResponse: " + responseCode; } } catch (error) { Logger.log('[GetResponse] Błąd: ' + error.toString()); return "Błąd: " + error.toString(); } }// ==================== FUNKCJA STOPKI ==================== function createEmailFooter() { return `
Kamil Owsianik

Do zobaczenia

Założyciel Kliniki Formy
Tel: +48 733 705 478
@: kontakt@klinika-formy.pl
WWW: klinika-formy.pl
`; }// ==================== FUNKCJE EMAILI EBOOK ====================function wyslijEmail1(imie, nazwisko, email) { // Sprawdź limit if (!checkEmailQuota()) { Logger.log('[Email] Przekroczono limit - email 1 zostanie wysłany później'); const queue = new QueueManager(); const tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setHours(2, 0, 0, 0); queue.addToQueue('ebook_email_1', { imie, nazwisko, email }, tomorrow.getTime() - new Date().getTime()); return "Zakolejkowano na jutro (limit)"; } try { const pelneImie = imie + ' ' + nazwisko; const temat = 'MATERIAŁ WIDEO + 3 BONUSY - Twoja droga do wymarzonej sylwetki zaczyna się TUTAJ!'; const htmlBody = `
Klinika Formy
Witaj ${pelneImie}!

GRATULACJE! Właśnie wykonałeś pierwszy, najważniejszy krok w kierunku swojej wymarzonej sylwetki!

UWAGA! Twoje materiały są już gotowe:

Za chwilę odkryjesz moją sprawdzoną metodę, dzięki której:

  • ✓ Schudłem 29 kg bez efektu jo-jo (i utrzymuję wagę od 2 lat!)
  • ✓ Pomogłem 347+ osobom osiągnąć ich wymarzoną sylwetkę
  • ✓ Stworzysz nawyki na całe życie, nie tylko chwilową dietę
WAŻNE: Materiały są dostępne tylko przez 72 godziny!
Po tym czasie link wygaśnie.

Kliknij poniżej i obejrzyj 4-minutowe wideo, które zmieni Twoje podejście do odchudzania:

OBEJRZYJ WIDEO TERAZ (4 MIN)

TWOJE BONUSY (wartość 497 zł - dziś ZA DARMO):

  • E-book "ZMIANA" - Mój najnowszy poradnik (wartość 147 zł)
  • Darmowa dieta startowa - 7-dniowy jadłospis (wartość 97 zł)
  • Dostęp do grupy wsparcia na FB - 2000+ osób (wartość 67 zł/mies.)
  • BONUS SPECJALNY: Bezpłatna konsultacja online (wartość 250 zł) - tylko dla pierwszych 10 osób!

"W 3 miesiące schudłam 18 kg! Metoda Kamila to nie kolejna dieta cud, ale realny plan, który da się stosować na co dzień. Najlepsze? Zero efektu jo-jo od roku!"

- Katarzyna M., 34 lata

PS. Pamiętaj - na konsultacji przygotuję dla Ciebie DARMOWY 12-tygodniowy plan działania dostosowany do Twoich potrzeb. Pokażę Ci go jeszcze ZANIM zdecydujesz się na współpracę!

TAK, CHCĘ OBEJRZEĆ WIDEO I OTRZYMAĆ BONUSY

Nie przegap tej szansy - to może być dzień, który zmieni Twoje życie!

${createEmailFooter()}
`; const plainTextBody = `Witaj ${pelneImie}! GRATULACJE! Oto link do wideo i Twoich bonusów. Obejrzyj koniecznie: ${CONFIG.URLS.LANDING_PAGE_2}`; GmailApp.sendEmail(email, temat, plainTextBody, { htmlBody: htmlBody, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); incrementEmailCounter(); Logger.log('[Email] Wysłano email 1 do: ' + email); return "Wysłano pomyślnie"; } catch (error) { // Jeśli błąd to limit Google if (error.toString().includes('zbyt wiele razy')) { Logger.log('[Email] Limit Google - odkładam email 1 na później'); const queue = new QueueManager(); const tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setHours(2, 0, 0, 0); queue.addToQueue('ebook_email_1', { imie, nazwisko, email }, tomorrow.getTime() - new Date().getTime()); return "Zakolejkowano (limit Google)"; } throw new Error('Błąd wysyłania emaila 1: ' + error.toString()); } }function wyslijEmail2Function(imie, nazwisko, email) { try { const temat = imie + ', udało Ci się obejrzeć wideo? (ważna informacja o konsultacji)'; const htmlBody = `
Klinika Formy

Cześć ${imie}!

Minęło już 5 godzin odkąd zgłosiłeś się po materiały...

Wiem, że życie bywa zabiegane, ale nie chcę, żebyś przegapił swoją szansę!

Czy udało Ci się już obejrzeć moje 4-minutowe wideo?

Jeśli jeszcze nie, to koniecznie zrób to teraz, ponieważ:

BEZPŁATNA KONSULTACJA - Tylko dla Ciebie!

Co otrzymasz na konsultacji?

  • 12-tygodniowy indywidualny plan działania (wartość 250 zł)
  • Analizę Twoich największych przeszkód w odchudzaniu
  • Konkretne rozwiązania dopasowane do Twojego trybu życia
  • Plan treningowy i żywieniowy "szyty na miarę"

Wszystko POKAŻĘ Ci jeszcze PRZED podjęciem decyzji o współpracy!

UWAGA: Zostało tylko 7 miejsc na bezpłatne konsultacje w tym tygodniu!
3 osoby właśnie rezerwują swoje terminy...
Normalny koszt takiej konsultacji: 250 zł
Dla Ciebie dziś: GRATIS
Ale tylko jeśli wypełnisz formularz w ciągu 48h!

OBEJRZYJ WIDEO I ZAREZERWUJ KONSULTACJĘ

✓ Jak przygotować się do konsultacji?

  1. Obejrzyj wideo do końca - poznasz moją metodę
  2. Wypełnij formularz pod wideo - to zajmie max 3 minuty
  3. Pobierz aplikację Google Meet - spotkamy się online
  4. Przygotuj pytania - odpowiem na wszystkie Twoje wątpliwości

Kamerka NIE jest wymagana! Wystarczy, że będziesz mnie słyszeć i widzieć mój ekran.

PS. Wiesz co jest najgorsze? 80% osób, które odkładają decyzję "na później", nigdy nie wraca do tematu. Nie pozwól, aby Twoja szansa na zmianę przeszła Ci koło nosa!

NIE ODKŁADAJ - DZIAŁAJ TERAZ!

"Najlepszy moment na zmianę był wczoraj. Drugi najlepszy jest TERAZ."

${createEmailFooter()}
`; const plainTextBody = `Cześć ${imie}! Czy udało Ci się obejrzeć wideo? Mam dla Ciebie bezpłatną konsultację (wartość 250 zł). Zobacz: ${CONFIG.URLS.LANDING_PAGE_2}`; GmailApp.sendEmail(email, temat, plainTextBody, { htmlBody: htmlBody, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); Logger.log('[Email] Wysłano email 2 do: ' + email); return "Wysłano pomyślnie"; } catch (error) { Logger.log('[Email] Błąd wysyłania emaila 2: ' + error.toString()); return "Błąd: " + error.toString(); } }function wyslijEmail3Function(imie, nazwisko, email) { try { const temat = imie + ', zobacz co osiągnęli inni! (+ Twoja konsultacja czeka)'; const htmlBody = `
Klinika Formy

Witaj ${imie}!

Może się zastanawiasz: "Czy to naprawdę działa?"

Pozwól, że pokażę Ci, co osiągnęli ludzie DOKŁADNIE w Twojej sytuacji:

347+
Zadowolonych klientów
4,215 kg
Łącznie zrzuconych
0%
Efektu jo-jo

"Minus 22 kg w 4 miesiące!"

"Myślałam, że po 40-tce już nie schudnę. Kamil udowodnił mi, że się myliłam. Jego plan był prosty do wdrożenia, a efekty przeszły moje oczekiwania!"

- Anna K., 42 lata, mama 3 dzieci

"Z rozmiaru XXL do M!"

"Pracuję po 12 godzin dziennie. Myślałem, że nie dam rady. Plan Kamila był idealnie dopasowany do mojego trybu życia. Schudłem 31 kg!"

- Piotr M., 38 lat, kierowca

"Najlepsza inwestycja w siebie!"

"15 kg w 3 miesiące i czuję się jak nastolatka! Mam więcej energii niż 10 lat temu. Żałuję tylko, że nie zaczęłam wcześniej!"

- Małgorzata W., 35 lat

Zobacz wszystkie 284 opinie na naszej stronie

Twoja kolej na sukces!

Za 3 miesiące możesz być kolejną osobą, która napisze swoją historię sukcesu!

Ale tylko jeśli ZACZNIESZ DZIAŁAĆ TERAZ.

Co musisz zrobić?

  1. Obejrzyj moje 4-minutowe wideo
  2. Wypełnij krótki formularz (3 minuty)
  3. Odbierz DARMOWĄ konsultację (wartość 250 zł)
OSTATNIE 24 GODZINY na darmową konsultację!
Potem koszt: 250 zł

DOŁĄCZ DO 347+ OSÓB, KTÓRE ZMIENIŁY SWOJE ŻYCIE

Specjalna wiadomość od mojej ostatniej klientki:

"${imie}, jeśli to czytasz - NIE ZWLEKAJ! Ja też się wahałam 2 tygodnie. To były 2 stracone tygodnie, w których mogłam już działać. Dziś, 3 miesiące później, jestem lżejsza o 17 kg i żałuję tylko jednego - że nie zaczęłam od razu!"

- Dorota, rozpoczęła program 3 miesiące temu

PS. Pamiętaj - na konsultacji POKAŻĘ Ci cały 12-tygodniowy plan jeszcze ZANIM podejmiesz decyzję. Zero ryzyka, same korzyści!

ZAREZERWUJ SWOJĄ DARMOWĄ KONSULTACJĘ TERAZ

${createEmailFooter()}
`; const plainTextBody = `Witaj ${imie}! Zobacz, co osiągnęli inni - 347+ osób zmieniło swoje życie. Twoja darmowa konsultacja (wartość 250 zł) czeka: ${CONFIG.URLS.LANDING_PAGE_2}`; GmailApp.sendEmail(email, temat, plainTextBody, { htmlBody: htmlBody, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); Logger.log('[Email] Wysłano email 3 do: ' + email); return "Wysłano pomyślnie"; } catch (error) { Logger.log('[Email] Błąd wysyłania emaila 3: ' + error.toString()); return "Błąd: " + error.toString(); } }function wyslijEmail4Function(imie, nazwisko, email) { try { const temat = imie + ', Twoje BONUSY są gotowe! (e-book, dieta, grupa FB + OSTATNIA SZANSA na konsultację)'; const htmlBody = `
Klinika Formy

Cześć ${imie}!

ZOSTAŁO TYLKO 24 GODZINY!

To Twoja OSTATNIA SZANSA na darmową konsultację!

Jutro o tej porze będzie za późno...

Mam dla Ciebie świetne wieści! Przygotowałem wszystkie obiecane bonusy. Są już dostępne do pobrania!

TWOJE EKSKLUZYWNE BONUSY:

BONUS #1: E-book "ZMIANA" - Mój najnowszy poradnik

147 zł GRATIS!

Kompletny przewodnik po transformacji sylwetki. 87 stron praktycznej wiedzy!

BONUS #2: Darmowa 7-dniowa dieta startowa

97 zł GRATIS!

Gotowy jadłospis z listą zakupów i przepisami. Zacznij już jutro!

BONUS #3: Dostęp do ekskluzywnej grupy na Facebooku

67 zł/mies GRATIS!

2137+ osób na tej samej drodze. Wsparcie, motywacja, sprawdzone porady!

BONUS SPECJALNY: Indywidualna konsultacja + Plan 12-tyg

250 zł OSTATNIA SZANSA!

UWAGA: Ten bonus jest dostępny TYLKO DZIŚ! Jutro konsultacja będzie kosztować 250 zł.

Całkowita wartość Twoich bonusów:

561 zł 0 zł

Oszczędzasz ponad 500 złotych!

POBIERZ E-BOOK I DIETĘ DOŁĄCZ DO GRUPY FB

Jak się przygotować do DARMOWEJ konsultacji?

To bardzo proste - tylko 4 kroki:

  1. Pobierz Google Meet na telefon lub komputer (darmowa aplikacja)
  2. Obejrzyj moje wideo do końca (4 minuty)
  3. Wypełnij formularz pod wideo (3 minuty)
  4. Wybierz termin, który Ci pasuje

Ważne: Kamerka NIE jest wymagana! Możesz uczestniczyć anonimowo. Udostępnię Ci mój ekran i pokażę przygotowany plan.

${imie}, TO NAPRAWDĘ OSTATNI MOMENT!

Za 24 godziny stracisz szansę na:

  • Darmową konsultację wartą 250 zł
  • Indywidualny plan 12-tygodniowy
  • Moją osobistą pomoc w starcie

Nie żałuj później, że nie skorzystałeś!

OSTATNIA SZANSA - ZAREZERWUJ KONSULTACJĘ TERAZ!

Moja osobista obietnica dla Ciebie:

"${imie}, wiem, że możesz mieć wątpliwości. Dlatego daję Ci moją gwarancję: na konsultacji pokażę Ci DOKŁADNY plan działania na 12 tygodni. Zobaczysz co, kiedy i jak masz robić. Jeśli uznasz, że to nie dla Ciebie - po prostu podziękujesz i tyle. Zero presji, zero nacisków.

Ale jestem pewien, że gdy zobaczysz swój spersonalizowany plan, będziesz chciał zacząć od razu!"

- Kamil Owsianik

PS. To jest mój ostatni email w tej sprawie. Jeśli teraz nie skorzystasz, stracisz tę okazję na zawsze. Konsultacja warta 250 zł czeka na Ciebie ZA DARMO - ale tylko DZIŚ!

PPS. Pamiętaj - już ponad 347 osób zmieniło swoje życie. Następny możesz być Ty!

DOŁĄCZ DO PROGRAMU ZMIANA - ZACZNIJ DZIŚ!

${createEmailFooter()}
`; const plainTextBody = `Cześć ${imie}! Twoje bonusy są gotowe! E-book, dieta, grupa FB + OSTATNIA SZANSA na darmową konsultację (wartość 250 zł). Pobierz tutaj: ${CONFIG.URLS.DOWNLOAD_PAGE} i zarezerwuj konsultację: ${CONFIG.URLS.LANDING_PAGE_2}`; GmailApp.sendEmail(email, temat, plainTextBody, { htmlBody: htmlBody, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); Logger.log('[Email] Wysłano email 4 do: ' + email); return "Wysłano pomyślnie"; } catch (error) { Logger.log('[Email] Błąd wysyłania emaila 4: ' + error.toString()); return "Błąd: " + error.toString(); } }// ==================== BRAKUJĄCA FUNKCJA - EMAIL POTWIERDZAJĄCY KONSULTACJĘ ==================== function wyslijEmailPotwierdzajacyKonsultacje(imie, nazwisko, email) { // Sprawdź limit if (!checkEmailQuota()) { Logger.log('[Email] Przekroczono limit - potwierdzenie konsultacji zostanie wysłane później'); const queue = new QueueManager(); const tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setHours(2, 0, 0, 0); queue.addToQueue('consultation_confirmation_email', { imie, nazwisko, email }, tomorrow.getTime() - new Date().getTime()); return "Zakolejkowano na jutro (limit)"; } try { const pelneImie = imie + ' ' + nazwisko; const temat = 'Potwierdzenie zgłoszenia + INSTRUKCJE przygotowania do konsultacji'; const htmlBody = `
Klinika Formy

GRATULACJE ${pelneImie}!

Twoje zgłoszenie zostało przyjęte!

Świetna decyzja! Właśnie wykonałeś najważniejszy krok w kierunku swojej wymarzonej sylwetki.

CO DZIEJE SIĘ DALEJ?

1POTWIERDŹ ZGŁOSZENIE (WAŻNE!)

Kliknij poniżej i potwierdź swoje zgłoszenie przez WhatsApp lub email:
POTWIERDŹ ZGŁOSZENIE TUTAJ

2W ciągu 24-48h skontaktujemy się z Tobą

Zadzwonimy z numeru: +48 733 705 478
Zapisz ten numer, aby nie przegapić połączenia!

3Ustalimy dogodny termin konsultacji

Znajdziemy termin, który będzie Ci najbardziej odpowiadał.
Konsultacja trwa około 20-30 minut.

4Przygotuję Twój indywidualny plan

Na podstawie Twoich danych przygotuję spersonalizowany
12-tygodniowy plan działania, który pokażę Ci na konsultacji.

POTWIERDŹ SWOJE ZGŁOSZENIE TERAZ

JAK PRZYGOTOWAĆ SIĘ DO KONSULTACJI?

  • Pobierz aplikację Google Meet - to tam się spotkamy (link otrzymasz SMS-em)
  • Znajdź spokojne miejsce - gdzie będziesz mógł swobodnie rozmawiać
  • Przygotuj pytania - zapisz wszystko, co chcesz wiedzieć
  • Kamerka NIE jest wymagana - możesz uczestniczyć tylko głosowo
  • Weź notes - będziesz chciał zapisać ważne informacje

Nie zapomnij pobrać swoich BONUSÓW!

Jako osoba zapisana na konsultację otrzymujesz:

  • E-book "ZMIANA" (wartość 147 zł)
  • 7-dniową dietę startową (wartość 97 zł)
  • Dostęp do grupy wsparcia na FB

>> KLIKNIJ TUTAJ, ABY POBRAĆ BONUSY <<

Masz pytania? Potrzebujesz szybkiego kontaktu?

Napisz do mnie na WhatsApp:

WhatsApp: +48 733 705 478

Lub zadzwoń: +48 733 705 478

Jestem dostępny pon-pt: 9:00-18:00, sob: 10:00-14:00

WAŻNE:
Na konsultacji pokażę Ci DOKŁADNY plan na 12 tygodni.
Zobaczysz wszystko jeszcze ZANIM podejmiesz decyzję o współpracy.
To konsultacja wartości 250 zł - dla Ciebie całkowicie ZA DARMO!

Do zobaczenia na konsultacji!

Pozdrawiam serdecznie,
Kamil Owsianik
Twój trener przemian

PS. Jeśli z jakiegoś powodu będziesz musiał przełożyć termin - daj znać.
Zawsze możemy ustalić inny, dogodny dla Ciebie moment.

${createEmailFooter()}
`; const plainTextBody = `Witaj ${pelneImie}, Twoje zgłoszenie zostało przyjęte! POTWIERDŹ ZGŁOSZENIE: ${CONFIG.URLS.CONFIRMATION_PAGE} Skontaktujemy się w ciągu 24-48h z numeru +48 733 705 478. Pobierz bonusy: ${CONFIG.URLS.DOWNLOAD_PAGE}`; GmailApp.sendEmail(email, temat, plainTextBody, { htmlBody: htmlBody, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); incrementEmailCounter(); Logger.log('[Email] Wysłano potwierdzenie konsultacji do: ' + email); return "Wysłano pomyślnie"; } catch (error) { // Jeśli błąd to limit Google if (error.toString().includes('zbyt wiele razy')) { Logger.log('[Email] Limit Google - odkładam potwierdzenie na później'); const queue = new QueueManager(); const tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setHours(2, 0, 0, 0); queue.addToQueue('consultation_confirmation_email', { imie, nazwisko, email }, tomorrow.getTime() - new Date().getTime()); return "Zakolejkowano (limit Google)"; } throw new Error('Błąd wysyłania emaila konsultacji: ' + error.toString()); } } // ==================== SZABLONY EMAILI KONSULTACYJNYCH ==================== class ConsultationEmailTemplates { getConfirmationEmail(data) { const meetingDate = new Date(data.eventTime); const formattedDate = Utilities.formatDate(meetingDate, 'Europe/Warsaw', 'dd.MM.yyyy'); const formattedTime = Utilities.formatDate(meetingDate, 'Europe/Warsaw', 'HH:mm'); const subject = 'Jutro o ' + formattedTime + ' - Twoja konsultacja'; // Tworzenie linku do kalendarza Google const eventTitle = encodeURIComponent('Konsultacja - Klinika Formy'); const eventDetails = encodeURIComponent('Konsultacja online z Kamilem Owsianik\n\nLink do spotkania: ' + data.meetLink); const startTime = meetingDate.toISOString().replace(/-|:|\.\d\d\d/g, ''); const endTime = new Date(meetingDate.getTime() + 60 * 60 * 1000).toISOString().replace(/-|:|\.\d\d\d/g, ''); const calendarLink = `https://calendar.google.com/calendar/render?action=TEMPLATE&text=${eventTitle}&details=${eventDetails}&dates=${startTime}/${endTime}`; const html = `
Klinika Formy

Cześć ${data.imie}!

Świetnie! Twoja konsultacja jest potwierdzona!

Szczegóły spotkania:

Data: ${formattedDate}

Godzina: ${formattedTime}

Miejsce: Online - Google Meet

${data.meetLink ? `

Link do spotkania:
${data.meetLink}

` : '

Link: Otrzymasz w osobnej wiadomości

'}
DODAJ DO KALENDARZA

JAK PRZYGOTOWAĆ SIĘ DO KONSULTACJI?

  1. Pobierz Google Meet - darmowa aplikacja na telefon/komputer
  2. Przygotuj spokojne miejsce - gdzie będziesz mógł swobodnie rozmawiać
  3. Weź notes i długopis - będziesz chciał zapisać ważne informacje
  4. Przygotuj pytania - zapisz wszystko, co chcesz wiedzieć

Kamerka NIE jest wymagana! Udostępnię Ci mój ekran i pokażę przygotowany plan.

WAŻNE: Jeśli coś wypadnie i nie będziesz mógł uczestniczyć w konsultacji, koniecznie daj znać minimum 10 godzin wcześniej! Wtedy ustalimy nowy termin i nie stracisz swojej bezpłatnej szansy.

WhatsApp/Tel: +48 733 705 478

Na konsultacji pokażę Ci DOKŁADNY plan na 12 tygodni!

Do zobaczenia jutro!
Kamil Owsianik

${createEmailFooter()}
`; const text = 'Twoja konsultacja jest potwierdzona!'; return { subject, html, text }; } get24hBeforeEmail(data) { const meetingDate = new Date(data.eventTime); const formattedTime = Utilities.formatDate(meetingDate, 'Europe/Warsaw', 'HH:mm'); const subject = 'Jutro o ' + formattedTime + ' - Twoja konsultacja (przygotuj się!)'; const html = `

Hej ${data.imie}!

Jutro o ${formattedTime} mamy konsultację!

Przygotuj się na jutro:

  1. Pobierz Google Meet (jeśli jeszcze nie masz)
  2. Znajdź spokojne miejsce na rozmowę
  3. Przygotuj pytania które Cię nurtują
${data.meetLink ? `

Link do spotkania: ${data.meetLink}

` : '

Link otrzymasz w kolejnej wiadomości

'}

Pamiętaj: Jeśli nie możesz uczestniczyć, daj znać minimum 10h wcześniej!
WhatsApp: +48 733 705 478

Do zobaczenia jutro!
Kamil

${createEmailFooter()}
`; const text = 'Jutro konsultacja!'; return { subject, html, text }; } get2hBeforeEmail(data) { const meetingDate = new Date(data.eventTime); const formattedTime = Utilities.formatDate(meetingDate, 'Europe/Warsaw', 'HH:mm'); const subject = 'Za 2 godziny start! Konsultacja o ' + formattedTime; const html = `

Za 2 godziny spotykamy się online!

Cześć ${data.imie}!

Ostatnie przygotowania:

  • Sprawdź czy masz Google Meet
  • Przygotuj notes do zapisków
  • Znajdź spokojne miejsce
${data.meetLink ? `

Link: ${data.meetLink}

` : ''}

Pokażę Ci Twój spersonalizowany plan na 12 tygodni!

Do zobaczenia!
Kamil

${createEmailFooter()}
`; const text = 'Za 2 godziny konsultacja!'; return { subject, html, text }; } get15minBeforeEmail(data) { const subject = 'START za 15 minut!'; const html = `

STARTUJEMY ZA 15 MINUT!

${data.imie}, czekam na Ciebie online!

${data.meetLink ? `DOŁĄCZ DO SPOTKANIA` : '

Sprawdź link w poprzednich wiadomościach

'}

Pamiętaj - udostępnię Ci ekran i pokażę cały plan!

${createEmailFooter()}
`; const text = 'START za 15 minut!'; return { subject, html, text }; } get10minAfterEmail(data) { const subject = 'Twój plan + prezentacja platformy'; const html = `

Dziękuję za spotkanie, ${data.imie}!

Jak obiecałem, przesyłam materiały:

PREZENTACJA PLATFORMY:

OBEJRZYJ PREZENTACJĘ

TWÓJ PLAN DZIAŁANIA:

OTWÓRZ PLAN W GOOGLE DOCS

Pozdrawiam,
Kamil

${createEmailFooter()}
`; const text = 'Dziękuję za spotkanie!'; return { subject, html, text }; } get24hAfterEmail(data) { const subject = 'Twój plan + oferta specjalna (48h)'; const html = `

Cześć ${data.imie}!

Mam nadzieję, że nasza wczorajsza rozmowa dała Ci jasny obraz tego, jak możesz osiągnąć swoją wymarzoną sylwetkę.

TWÓJ PLAN JEST TUTAJ:

OTWÓRZ PLAN

OFERTA SPECJALNA - TYLKO 48 GODZIN!

Jak wspomniałem na konsultacji, przygotowałem dla Ciebie specjalną ofertę:

1350 zł (zamiast 1500 zł)

Oszczędzasz 150 zł!

Opcje płatności:

Ta oferta jest ważna tylko 48 godzin od naszej rozmowy.

Kamil

${createEmailFooter()}
`; const text = 'Twój plan gotowy!'; return { subject, html, text }; } get3daysAfterEmail(data) { const subject = 'Ostatnia szansa na ofertę'; const html = `

OSTATNIE GODZINY!

Cześć ${data.imie},

Oferta 1350 zł wygasa dziś o północy!

OSTATNIA SZANSA

1350 zł zamiast 1500 zł

Oszczędzasz 150 zł!

ZAPŁAĆ 1350 ZŁ

3 RATY | 6 RAT

Kamil

${createEmailFooter()}
`; const text = 'Ostatnia szansa!'; return { subject, html, text }; } get7daysAfterEmail(data) { const subject = 'Czy mogę zapytać?'; const html = `

Cześć ${data.imie}

Minął tydzień od naszej konsultacji.

Chciałbym poznać Twoją opinię i dowiedzieć się, co mogę poprawić.

Wypełnij krótką ankietę (2 minuty):

WYPEŁNIJ ANKIETĘ

W podziękowaniu otrzymasz:

KOD -10% na wszystkie programy

Pozdrawiam,
Kamil

${createEmailFooter()}
`; const text = 'Czy mogę zapytać?'; return { subject, html, text }; } getWelcomeEmail(data) { const subject = 'Witaj w programie!'; const html = `

WITAJ W ZESPOLE!

Cześć ${data.imie}!

Cieszę się, że jesteś z nami! Teraz zaczynamy prawdziwą pracę nad Twoją sylwetką.

CO DALEJ?

Napisz "START" na WhatsApp: +48 733 705 478

Otrzymasz dostęp do platformy i wszystkie materiały startowe.

Witaj w rodzinie Kliniki Formy!

Kamil

${createEmailFooter()}
`; const text = 'Witaj w programie!'; return { subject, html, text }; } }// ==================== FUNKCJE POMOCNICZE ==================== function zaplanujKolejneEmaile(imie, nazwisko, email, timestamp) { const queue = new QueueManager(); // Dodaj emaile 2, 3, 4 do kolejki queue.addToQueue('ebook_email_2', { imie, nazwisko, email }, CONFIG.DELAYS.EMAIL_2); queue.addToQueue('ebook_email_3', { imie, nazwisko, email }, CONFIG.DELAYS.EMAIL_3); queue.addToQueue('ebook_email_4', { imie, nazwisko, email }, CONFIG.DELAYS.EMAIL_4); Logger.log(`[Email] Zaplanowano 3 emaile dla: ${email}`); }// ==================== PRZETWARZANIE FORMULARZY ==================== function processEbookForm(data, timestamp) { const rowData = [ timestamp, data.imie || '', data.nazwisko || '', data.email || '', data.tel || '', data.cel || '' ]; let emailStatus = 'Nie wysłano'; if (data.email) { try { emailStatus = wyslijEmail1(data.imie, data.nazwisko, data.email); // Planuj kolejne TYLKO jeśli pierwszy się udał if (!emailStatus.includes('Błąd') && !emailStatus.includes('Zakolejkowano')) { zaplanujKolejneEmaile(data.imie, data.nazwisko, data.email, timestamp); } } catch (error) { Logger.log('[Form] Błąd emaila: ' + error.toString()); emailStatus = 'Błąd: ' + error.toString(); } } while (rowData.length < CONFIG.COLUMNS.EMAIL_1_STATUS - 1) { rowData.push(''); } rowData.push(emailStatus); rowData.push('Zaplanowano'); rowData.push('Zaplanowano'); rowData.push('Zaplanowano'); let getResponseStatus = 'Nie wysłano'; if (data.email) { try { getResponseStatus = addContactToGetResponse(data.imie, data.email); } catch (error) { Logger.log('[Form] GetResponse błąd: ' + error.toString()); getResponseStatus = 'Błąd: ' + error.toString(); } } while (rowData.length < CONFIG.COLUMNS.EBOOK_GETRESPONSE - 1) { rowData.push(''); } rowData[CONFIG.COLUMNS.EBOOK_GETRESPONSE - 1] = getResponseStatus; return rowData; }function processProgramForm(data, timestamp) { const rowData = [ timestamp, data.imie || '', data.nazwisko || '', data.email || '', data.tel || '', data.waga || '', data.wzrost || '', data.wiek || '', data.cel || '', data.czas_proby || '', data.samopoczucie || '', data.pilnosc || '', data.budzet || '', data.godziny_kontaktu || '', data.dlaczego || '', data.choroby || '' ]; // Kolumna 17 - Status konsultacji let emailStatus = 'Nie wysłano'; if (data.email) { try { emailStatus = wyslijEmailPotwierdzajacyKonsultacje(data.imie, data.nazwisko, data.email); } catch (error) { Logger.log('[Form] Błąd emaila: ' + error.toString()); emailStatus = 'Błąd: ' + error.toString(); } } rowData.push(emailStatus); // Kolumna 18 - PURCHASE_STATUS rowData.push(''); // Wypełnij puste kolumny do 21 while (rowData.length < 21) { rowData.push(''); } // Kolumna 22 - GetResponse let getResponseStatus = 'Nie wysłano'; if (data.email) { try { getResponseStatus = addContactToGetResponse(data.imie, data.email); } catch (error) { Logger.log('[Form] GetResponse błąd: ' + error.toString()); getResponseStatus = 'Błąd: ' + error.toString(); } } rowData.push(getResponseStatus); return rowData; }// ==================== FUNKCJE SYSTEMOWE ==================== function setupOptimizedSystem() { Logger.log('[Setup] Rozpoczynam konfigurację systemu v3.3...'); // 1. Wyczyść wszystkie triggery const triggers = ScriptApp.getProjectTriggers(); triggers.forEach(trigger => { ScriptApp.deleteTrigger(trigger); Logger.log(`[Setup] Usunięto trigger: ${trigger.getHandlerFunction()}`); }); // 2. Ustaw główny processor (co 2 godziny zamiast co godzinę) ScriptApp.newTrigger('mainProcessor') .timeBased() .everyHours(2) // ZMIANA: co 2 godziny .create(); // 3. Ustaw nocny processor ScriptApp.newTrigger('nightProcessor') .timeBased() .everyDays(1) .atHour(2) // O 2 w nocy .create(); // 4. Ustaw czyszczenie (raz dziennie) ScriptApp.newTrigger('dailyMaintenance') .timeBased() .everyDays(1) .atHour(3) .create(); Logger.log('[Setup] System v3.3 skonfigurowany z kontrolą limitów!'); // 5. Sprawdź status const status = checkSystemStatus(); return { message: 'System zoptymalizowany z kontrolą limitów', version: CONFIG.SYSTEM_VERSION, triggers: 3, status: status }; }function dailyMaintenance() { Logger.log('[Maintenance] Rozpoczynam codzienną konserwację...'); const queue = new QueueManager(); const cleaned = queue.cleanup(); // Wyczyść stare properties const properties = PropertiesService.getScriptProperties().getProperties(); let oldProperties = 0; for (const key in properties) { if (key.startsWith('trigger_') || key.startsWith('consultation_') || key.startsWith('user_')) { PropertiesService.getScriptProperties().deleteProperty(key); oldProperties++; } } // Wyczyść stare liczniki emaili (starsze niż wczoraj) const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); const yesterdayString = yesterday.toDateString(); for (const key in properties) { if (key.startsWith('email_count_') && !key.includes(new Date().toDateString()) && !key.includes(yesterdayString)) { PropertiesService.getScriptProperties().deleteProperty(key); oldProperties++; } } Logger.log(`[Maintenance] Wyczyszczono: ${cleaned} elementów kolejki, ${oldProperties} starych properties`); return { queueCleaned: cleaned, propertiesCleaned: oldProperties }; }function checkSystemStatus() { const queue = new QueueManager(); const queueStatus = queue.getStatus(); const emailQuota = getEmailQuotaStatus(); const status = { version: CONFIG.SYSTEM_VERSION, triggers: ScriptApp.getProjectTriggers().length, maxTriggers: 20, properties: Object.keys(PropertiesService.getScriptProperties().getProperties()).length, queue: queueStatus, emailQuota: emailQuota, sheets: { ebook: SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID) .getSheetByName(CONFIG.EBOOK_SHEET_NAME).getLastRow() - 1, program: SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID) .getSheetByName(CONFIG.PROGRAM_SHEET_NAME).getLastRow() - 1 }, nextEvents: getUpcomingConsultations() }; Logger.log('[Status] System v' + status.version); Logger.log('[Status] Triggery: ' + status.triggers + '/' + status.maxTriggers); Logger.log('[Status] Kolejka: ' + queueStatus.total + ' (oczekujące: ' + queueStatus.pending + ', zaległe: ' + queueStatus.overdue + ')'); Logger.log('[Status] Limit emaili: ' + emailQuota.daily.used + '/' + emailQuota.daily.limit + ' (pozostało: ' + emailQuota.daily.remaining + ')'); Logger.log('[Status] Rekordy: Ebook=' + status.sheets.ebook + ', Program=' + status.sheets.program); return status; }function getUpcomingConsultations() { const cal = CalendarApp.getDefaultCalendar(); const now = new Date(); const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000); const events = cal.getEvents(now, tomorrow); const consultations = []; events.forEach(event => { if (event.getTitle().toLowerCase().includes('konsultacja')) { consultations.push({ title: event.getTitle(), time: event.getStartTime(), guests: event.getGuestList().map(g => g.getEmail()).filter(e => e !== CONFIG.EMAIL_SENDER) }); } }); return consultations; }// ==================== FUNKCJE ADMINISTRACYJNE ==================== function manualCalendarScan() { Logger.log('[Manual] Ręczne skanowanie kalendarza...'); const calendar = new CalendarIntegration(); const results = calendar.scanForNewConsultations(); // Przetwórz kolejkę od razu const queue = new QueueManager(); const queueResults = queue.processQueue(); return { calendar: results, queue: queueResults }; }function forceProcessQueue() { Logger.log('[Manual] Wymuszenie przetworzenia kolejki...'); const queue = new QueueManager(); return queue.processQueue(); }function clearAllData() { const ui = SpreadsheetApp.getUi(); const response = ui.alert( 'UWAGA!', 'Czy na pewno chcesz wyczyścić WSZYSTKIE dane systemowe?', ui.ButtonSet.YES_NO ); if (response === ui.Button.YES) { // Wyczyść triggery const triggers = ScriptApp.getProjectTriggers(); triggers.forEach(t => ScriptApp.deleteTrigger(t)); // Wyczyść properties const properties = PropertiesService.getScriptProperties(); properties.deleteAllProperties(); Logger.log('[Clear] Wszystkie dane systemowe wyczyszczone'); return 'Wyczyszczono wszystkie dane'; } return 'Anulowano'; }function testEmailDelivery(email) { if (!email) { email = 'test@example.com'; } // Sprawdź limit przed testem if (!checkEmailQuota()) { Logger.log('[Test] Limit emaili wyczerpany - test niemożliwy'); return 'Limit emaili wyczerpany - spróbuj jutro'; } try { GmailApp.sendEmail( email, 'TEST - Klinika Formy CRM', 'To jest email testowy.', { htmlBody: '

Test Email

System działa poprawnie.

', name: "Kamil Owsianik - TEST", replyTo: CONFIG.EMAIL_SENDER } ); incrementEmailCounter(); Logger.log(`[Test] Email testowy wysłany do: ${email}`); return 'Email testowy wysłany'; } catch (error) { Logger.log(`[Test] Błąd wysyłania: ${error.toString()}`); return 'Błąd: ' + error.toString(); } }function addTestClient(imie = 'Test', nazwisko = 'Testowy', email = 'test@example.com', telefon = '+48123456789') { const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const sheet = spreadsheet.getSheetByName(CONFIG.PROGRAM_SHEET_NAME); const rowData = [ new Date(), imie, nazwisko, email, telefon, '80', // waga '180', // wzrost '35', // wiek 'Test systemu', '3 miesiące', 'Dobrze', 'Pilne', '1500', '10-18', 'Test', 'Brak' ]; sheet.appendRow(rowData); Logger.log(`[Test] Dodano klienta testowego: ${email}`); return 'Klient testowy dodany'; }// ==================== MENU INTERFACE ==================== function onOpen() { const ui = SpreadsheetApp.getUi(); ui.createMenu('Klinika Formy CRM v3.3') .addItem('📊 Status systemu', 'showSystemStatus') .addItem('⚡ Przetwórz kolejkę TERAZ', 'forceProcessQueue') .addItem('📈 Sprawdź limity emaili', 'showEmailQuotaStatus') .addSeparator() .addSubMenu(ui.createMenu('📅 Kalendarz') .addItem('Skanuj kalendarz TERAZ', 'manualCalendarScan') .addItem('Pokaż najbliższe konsultacje', 'showUpcomingConsultations')) .addSubMenu(ui.createMenu('📧 Email & GetResponse') .addItem('Test GetResponse', 'testGetResponseAPI') .addItem('Test wysyłki email', 'promptTestEmail') .addItem('Sprawdź limity emaili', 'showEmailQuotaStatus')) .addSubMenu(ui.createMenu('⚙️ Administracja') .addItem('Konfiguruj system v3.3', 'setupOptimizedSystem') .addItem('Ręczna konserwacja', 'dailyMaintenance') .addItem('Dodaj klienta testowego', 'addTestClient') .addItem('Wyczyść WSZYSTKO', 'clearAllData')) .addSeparator() .addItem('📚 Dokumentacja', 'showDocumentation') .addToUi(); }function showEmailQuotaStatus() { const status = getEmailQuotaStatus(); const ui = SpreadsheetApp.getUi(); const message = ` LIMITY EMAILI - STATUS:📊 DZIENNY LIMIT: • Wykorzystano: ${status.daily.used}/${status.daily.limit} • Pozostało: ${status.daily.remaining} • Wykorzystanie: ${status.daily.percentage}%⏰ GODZINOWY LIMIT: • Wykorzystano: ${status.hourly.used}/${status.hourly.limit} • Pozostało: ${status.hourly.remaining}${status.daily.remaining === 0 ? '⚠️ UWAGA: Limit dzienny wyczerpany! Emaile zostaną wysłane jutro.' : ''} ${status.daily.remaining < 10 && status.daily.remaining > 0 ? '⚠️ UWAGA: Zbliżasz się do limitu dziennego!' : ''} `; ui.alert('Status limitów emaili', message, ui.ButtonSet.OK); }function showSystemStatus() { const status = checkSystemStatus(); const html = HtmlService.createHtmlOutput(`

System Status - v${status.version}

Limity emaili

${status.emailQuota.daily.used}/${status.emailQuota.daily.limit}
Emaile dzisiaj
${status.emailQuota.daily.remaining}
Pozostało
${status.emailQuota.daily.percentage}%
Wykorzystanie

System

${status.triggers}/${status.maxTriggers}
Triggery
${status.queue.total}
W kolejce
${status.queue.pending}
Oczekujące
${status.queue.overdue}
Zaległe

Rekordy w arkuszach

ArkuszLiczba rekordów
Ebook${status.sheets.ebook}
Program${status.sheets.program}

Typy w kolejce

${Object.entries(status.queue.byType).map(([type, count]) => `` ).join('')}
TypLiczba
${type}${count}

Najbliższe konsultacje (24h)

${status.nextEvents.length > 0 ? ` ${status.nextEvents.map(e => `` ).join('')}
TytułCzasGoście
${e.title}${e.time}${e.guests.join(', ')}
` : '

Brak konsultacji w najbliższych 24h

' } `) .setTitle('Status Systemu') .setWidth(800) .setHeight(700); SpreadsheetApp.getUi().showModalDialog(html, 'Status Systemu CRM v3.3'); }function showUpcomingConsultations() { const consultations = getUpcomingConsultations(); const html = HtmlService.createHtmlOutput(`

Konsultacje w najbliższych 24h

${consultations.length > 0 ? ` ${consultations.map(c => `` ).join('')}
TytułCzasGoście
${c.title}${c.time.toLocaleString('pl-PL')}${c.guests.join('
')}
` : '

Brak konsultacji w najbliższych 24 godzinach

' } `) .setTitle('Najbliższe konsultacje') .setWidth(600) .setHeight(400); SpreadsheetApp.getUi().showModalDialog(html, 'Najbliższe konsultacje'); }function promptTestEmail() { const ui = SpreadsheetApp.getUi(); const response = ui.prompt( 'Test email', 'Podaj adres email do testu:', ui.ButtonSet.OK_CANCEL ); if (response.getSelectedButton() === ui.Button.OK) { const result = testEmailDelivery(response.getResponseText()); ui.alert('Wynik', result, ui.ButtonSet.OK); } } function showDocumentation() { const html = HtmlService.createHtmlOutput(`

Klinika Formy CRM v3.3 - Dokumentacja

🎉 NOWOŚCI W WERSJI 3.3:
  • Kontrola limitów emaili - automatyczne zarządzanie limitami Google (90/dzień)
  • Nocny processor - wysyłanie zaległych emaili o 2 w nocy
  • Batch processing - przetwarzanie max 5 emaili na raz
  • Monitoring limitów - wizualizacja wykorzystania
  • Automatyczne odkładanie - emaile przekraczające limit są odkładane na jutro
  • Triggery co 2 godziny zamiast co godzinę
📧 LIMITY EMAILI:
  • Dzienny limit: 90 emaili (bezpieczny margines od limitu Google 100)
  • Godzinowy limit: 20 emaili
  • Batch: 5 emaili na jedno przetworzenie
  • Reset: Automatyczny o północy
  • Zaległe: Wysyłane automatycznie o 2:00 w nocy
⚠️ WAŻNE: System automatycznie kontroluje limity. Gdy limit zostanie wyczerpany:
  • Emaile są automatycznie odkładane na jutro o 2:00
  • System informuje w logach o przekroczeniu limitu
  • Można sprawdzić status w menu "Sprawdź limity emaili"

Główne funkcje:

Automatyczne emaile (11 typów):
  • 4 emaile dla ebooka (natychmiast, 5h, 24h, 48h)
  • 7 emaili konsultacyjnych (przed i po spotkaniu)
  • Wszystkie z kontrolą limitów
Integracja z kalendarzem:
  • Skanowanie co 2 godziny
  • Automatyczne wykrywanie konsultacji
  • Pomijanie skanowania przy małej ilości limitów
System kolejkowy z kontrolą:
  • Sprawdzanie limitu przed każdym emailem
  • Automatyczne odkładanie przy przekroczeniu
  • Priorytetyzacja ważnych emaili

Jak działa kontrola limitów?

  1. Przed wysłaniem: System sprawdza czy nie przekroczono limitu
  2. Jeśli limit OK: Email jest wysyłany, licznik zwiększany
  3. Jeśli limit przekroczony: Email odkładany na jutro o 2:00
  4. Nocne przetwarzanie: O 2:00 system przetwarza zaległe emaile
  5. Reset liczników: O północy liczniki są resetowane

Triggery systemowe:

  • mainProcessor: Co 2 godziny - główne przetwarzanie
  • nightProcessor: O 2:00 - zaległe emaile
  • dailyMaintenance: O 3:00 - czyszczenie systemu

Monitoring:

  • Menu → "📊 Status systemu" - pełny przegląd
  • Menu → "📈 Sprawdź limity emaili" - szybki status limitów
  • Logi pokazują wykorzystanie po każdym emailu

Rozwiązywanie problemów:

  • Emaile nie wysyłają się: Sprawdź limity, może być przekroczony
  • Limit wyczerpany: Poczekaj do jutra lub do 2:00
  • Zaległe emaile: Zostaną wysłane automatycznie o 2:00
  • Chcesz wymusić: "Przetwórz kolejkę TERAZ" - ale limity nadal obowiązują

Kontakt wsparcia:

Email: owsiany1995@gmail.com

WhatsApp: +48 733 705 478

`) .setTitle('Dokumentacja') .setWidth(700) .setHeight(600); SpreadsheetApp.getUi().showModalDialog(html, 'Dokumentacja CRM v3.3'); }// ==================== FUNKCJE TESTOWE ==================== function testGetResponseAPI() { const url = 'https://api.getresponse.com/v3/campaigns'; const options = { 'method': 'get', 'contentType': 'application/json', 'headers': { 'X-Auth-Token': 'api-key ' + CONFIG.GETRESPONSE_API_KEY }, 'muteHttpExceptions': true }; try { const response = UrlFetchApp.fetch(url, options); Logger.log('[GetResponse Test] Kod: ' + response.getResponseCode()); if (response.getResponseCode() === 200) { const campaigns = JSON.parse(response.getContentText()); Logger.log('[GetResponse Test] Znaleziono kampanii: ' + campaigns.length); // Test dodania kontaktu const testEmail = "test_" + new Date().getTime() + "@example.com"; const testResult = addContactToGetResponse("Test", testEmail); Logger.log('[GetResponse Test] Dodanie testowego kontaktu: ' + testResult); return "GetResponse działa poprawnie"; } else { return "Błąd GetResponse: " + response.getResponseCode(); } } catch (error) { Logger.log('[GetResponse Test] Błąd: ' + error.toString()); return "Błąd testu: " + error.toString(); } }// ==================== INICJALIZACJA ==================== function init() { Logger.log('[Init] Inicjalizacja systemu CRM v3.3...'); // Sprawdź czy arkusze istnieją const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const ebookSheet = spreadsheet.getSheetByName(CONFIG.EBOOK_SHEET_NAME); const programSheet = spreadsheet.getSheetByName(CONFIG.PROGRAM_SHEET_NAME); if (!ebookSheet || !programSheet) { throw new Error('Brak wymaganych arkuszy! Sprawdź nazwy arkuszy.'); } // Ustaw system const result = setupOptimizedSystem(); Logger.log('[Init] System v3.3 z kontrolą limitów gotowy do pracy!'); Logger.log('[Init] Limity: 90 emaili/dzień, 20 emaili/godzinę, batch 5 emaili'); Logger.log('[Init] Triggery: mainProcessor (co 2h), nightProcessor (2:00), dailyMaintenance (3:00)'); return result; }// ==================== DEBUGOWANIE ==================== function debugQueue() { const queue = new QueueManager(); const status = queue.getStatus(); console.log('=== STATUS KOLEJKI ==='); console.log('Całkowita liczba: ' + status.total); console.log('Oczekujące: ' + status.pending); console.log('Zaległe: ' + status.overdue); console.log('\nPo typach:'); for (const type in status.byType) { console.log(` ${type}: ${status.byType[type]}`); } // Pokaż szczegóły pierwszych 5 elementów const properties = PropertiesService.getScriptProperties().getProperties(); let shown = 0; console.log('\n=== PIERWSZE 5 ELEMENTÓW ==='); for (const key in properties) { if (!key.startsWith('queue_')) continue; if (shown >= 5) break; const item = JSON.parse(properties[key]); const sendTime = new Date(item.sendTime); console.log(`\n${item.type}:`); console.log(` Email: ${item.data.email}`); console.log(` Czas wysyłki: ${sendTime}`); console.log(` Próby: ${item.attempts}`); shown++; } return status; }function resetEmailCounters() { const properties = PropertiesService.getScriptProperties(); const allProperties = properties.getProperties(); let removed = 0; for (const key in allProperties) { if (key.startsWith('email_count_')) { properties.deleteProperty(key); removed++; } } console.log(`Usunięto ${removed} liczników emaili`); return `Zresetowano ${removed} liczników`; }function simulateDayChange() { // Ta funkcja symuluje zmianę dnia dla testów const properties = PropertiesService.getScriptProperties(); // Ustaw wczorajszą datę dla obecnych liczników const today = new Date().toDateString(); const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); const yesterdayString = yesterday.toDateString(); const allProperties = properties.getProperties(); for (const key in allProperties) { if (key.includes(today)) { const newKey = key.replace(today, yesterdayString); properties.setProperty(newKey, allProperties[key]); properties.deleteProperty(key); } } console.log('Symulacja zmiany dnia zakończona - liczniki przeniesione na wczoraj'); return 'Dzień zmieniony'; }// ==================== MONITORING WYDAJNOŚCI ==================== function performanceReport() { const startTime = new Date(); const report = { timestamp: startTime, emailQuota: getEmailQuotaStatus(), queueStatus: new QueueManager().getStatus(), systemHealth: checkSystemHealth(), recommendations: [] }; // Analiza i rekomendacje if (report.emailQuota.daily.percentage > 80) { report.recommendations.push('Zbliżasz się do limitu dziennego emaili - rozważ przesunięcie niektórych na jutro'); } if (report.queueStatus.overdue > 10) { report.recommendations.push('Masz ' + report.queueStatus.overdue + ' zaległych emaili - uruchom forceProcessQueue()'); } if (report.systemHealth.properties > 400) { report.recommendations.push('Dużo properties - uruchom dailyMaintenance() aby wyczyścić stare'); } const duration = (new Date() - startTime) / 1000; report.checkDuration = duration; console.log('=== RAPORT WYDAJNOŚCI ==='); console.log(`Czas sprawdzenia: ${duration}s`); console.log(`\nLimity emaili: ${report.emailQuota.daily.used}/${report.emailQuota.daily.limit}`); console.log(`Kolejka: ${report.queueStatus.total} (zaległe: ${report.queueStatus.overdue})`); console.log(`\nRekomendacje:`); report.recommendations.forEach(r => console.log(' • ' + r)); return report; }function checkSystemHealth() { return { triggers: ScriptApp.getProjectTriggers().length, properties: Object.keys(PropertiesService.getScriptProperties().getProperties()).length, executionsToday: getExecutionCount(), lastRun: PropertiesService.getScriptProperties().getProperty('last_main_run') }; }function getExecutionCount() { // Przybliżona liczba wykonań dzisiaj const properties = PropertiesService.getScriptProperties(); const today = new Date().toDateString(); const count = properties.getProperty('executions_' + today) || 0; return parseInt(count); }// ==================== BACKUP I RESTORE ==================== function backupConfiguration() { const backup = { version: CONFIG.SYSTEM_VERSION, timestamp: new Date().toISOString(), config: CONFIG, properties: PropertiesService.getScriptProperties().getProperties() }; // Zapisz backup w arkuszu const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); let backupSheet = spreadsheet.getSheetByName('BACKUP_CONFIG'); if (!backupSheet) { backupSheet = spreadsheet.insertSheet('BACKUP_CONFIG'); backupSheet.appendRow(['Timestamp', 'Version', 'Backup Data']); } backupSheet.appendRow([ backup.timestamp, backup.version, JSON.stringify(backup) ]); console.log('Backup utworzony: ' + backup.timestamp); return backup; }function restoreConfiguration(backupRow) { if (!backupRow) { console.log('Podaj numer wiersza backup do przywrócenia'); return 'Błąd: brak numeru wiersza'; } const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const backupSheet = spreadsheet.getSheetByName('BACKUP_CONFIG'); if (!backupSheet) { return 'Błąd: brak arkusza BACKUP_CONFIG'; } const backupData = backupSheet.getRange(backupRow, 3).getValue(); const backup = JSON.parse(backupData); // Przywróć properties const properties = PropertiesService.getScriptProperties(); properties.setProperties(backup.properties); console.log('Przywrócono konfigurację z: ' + backup.timestamp); return 'Przywrócono backup z ' + backup.timestamp; }// ==================== KOŃCOWA WALIDACJA ==================== function validateSystem() { const errors = []; const warnings = []; const info = []; // Sprawdź arkusze try { const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const ebookSheet = spreadsheet.getSheetByName(CONFIG.EBOOK_SHEET_NAME); const programSheet = spreadsheet.getSheetByName(CONFIG.PROGRAM_SHEET_NAME); if (!ebookSheet) errors.push('Brak arkusza: ' + CONFIG.EBOOK_SHEET_NAME); if (!programSheet) errors.push('Brak arkusza: ' + CONFIG.PROGRAM_SHEET_NAME); info.push('Arkusze: OK'); } catch (e) { errors.push('Nie można otworzyć arkusza: ' + e.toString()); } // Sprawdź triggery const triggers = ScriptApp.getProjectTriggers(); if (triggers.length === 0) { warnings.push('Brak triggerów - uruchom setupOptimizedSystem()'); } else { info.push('Triggery: ' + triggers.length); } // Sprawdź limity const quota = getEmailQuotaStatus(); if (quota.daily.remaining < 10) { warnings.push('Mało limitów emaili: ' + quota.daily.remaining); } else { info.push('Limit emaili: ' + quota.daily.remaining + ' pozostało'); } // Sprawdź kolejkę const queueStatus = new QueueManager().getStatus(); if (queueStatus.overdue > 0) { warnings.push('Zaległe emaile w kolejce: ' + queueStatus.overdue); } console.log('=== WALIDACJA SYSTEMU ==='); if (errors.length > 0) { console.log('\n❌ BŁĘDY:'); errors.forEach(e => console.log(' • ' + e)); } if (warnings.length > 0) { console.log('\n⚠️ OSTRZEŻENIA:'); warnings.forEach(w => console.log(' • ' + w)); } console.log('\n✅ INFORMACJE:'); info.forEach(i => console.log(' • ' + i)); return { valid: errors.length === 0, errors: errors, warnings: warnings, info: info }; }
/** * KLINIKA FORMY CRM SYSTEM v3.2 - POPRAWIONE EMAILE BEZ EMOJI * * CHANGELOG v3.2: * - Usunięte problematyczne emoji * - Dodana stopka ze zdjęciem do wszystkich wiadomości * - Poprawione treści w emailu 1 (2 lata, 4 min wideo) * - Dodana informacja o potwierdzeniu przez WhatsApp * - Poprawiony przycisk kalendarza * - Rozbudowane emaile konsultacyjne */// ==================== KONFIGURACJA GŁÓWNA ==================== const CONFIG = { SPREADSHEET_ID: '1Q52bzhfuYQUFteOcj4RQ4Vi5oU4sIS1OE-xDMjXQINA', EBOOK_SHEET_NAME: 'DARMOWY EBOOK', PROGRAM_SHEET_NAME: 'ZGŁOSZENIE - Z EBOOKA', EMAIL_SENDER: 'owsiany1995@gmail.com', // GetResponse GETRESPONSE_API_KEY: '6vszd9996ta5xrzvzju0jqvfwukauzp2', GETRESPONSE_CAMPAIGN_ID: 'GS3qW', // Kolumny arkusza COLUMNS: { EBOOK_GETRESPONSE: 11, // K PROGRAM_GETRESPONSE: 22, // V EMAIL_1_STATUS: 12, // L EMAIL_2_STATUS: 13, // M EMAIL_3_STATUS: 14, // N EMAIL_4_STATUS: 15, // O PURCHASE_STATUS: 18, // R KONSULTACJA_POTWIERDZENIE: 23, // W KONSULTACJA_24H: 24, // X KONSULTACJA_2H: 25, // Y KONSULTACJA_15MIN: 26, // Z KONSULTACJA_10MIN_PO: 27, // AA KONSULTACJA_24H_PO: 28, // AB KONSULTACJA_3DNI_PO: 29, // AC CALENDAR_EVENT_ID: 30, // AD ANKIETA_STATUS: 31 // AE }, // Opóźnienia emaili DELAYS: { EMAIL_2: 5 * 60 * 60 * 1000, // 5 godzin EMAIL_3: 24 * 60 * 60 * 1000, // 24 godziny EMAIL_4: 48 * 60 * 60 * 1000, // 48 godzin KONSULTACJA_10MIN: 10 * 60 * 1000, // 10 minut KONSULTACJA_24H: 24 * 60 * 60 * 1000, KONSULTACJA_3DNI: 72 * 60 * 60 * 1000, KONSULTACJA_7DNI: 168 * 60 * 60 * 1000 }, // URLs URLS: { LANDING_PAGE_2: 'https://akf.klinika-formy.pl/wyslane/', CONFIRMATION_PAGE: 'https://akf.klinika-formy.pl/zgloszenie-przeslane/', OPINIONS: 'https://klinika-formy.pl/opinie/', FACEBOOK_GROUP: 'https://www.facebook.com/groups/klinikaformy', LOGO: 'https://akf.klinika-formy.pl/wp-content/uploads/2025/05/Bez-tytulu-1080-x-432-px-1.png', KAMIL_PHOTO: 'https://akf.klinika-formy.pl/wp-content/uploads/2025/07/@klinika.formy-1.png', PLATFORM_VIDEO: 'https://www.youtube.com/watch?v=KHkeiYJE6gg', GENERAL_PLAN_DOC: 'https://docs.google.com/document/d/1TS7NAsi-w_tsj4LyR0Szdjy1YyrZ6Aim3C9yCjttGAs/edit?usp=sharing', ANKIETA_FORM: 'https://akf.klinika-formy.pl/badanie/', PAYMENT_1350: 'https://buy.stripe.com/5kA4gz3m381Z0106oo', PAYMENT_3RATY: 'https://tubapay.pl/s/MPVlaYMD', PAYMENT_6RAT: 'https://tubapay.pl/s/vHNGc9CV', DOWNLOAD_PAGE: 'https://klinika-formy.pl/pobierz/' }, // System Settings QUEUE_PROCESSING_INTERVAL: 60, // minuty MAX_QUEUE_AGE_DAYS: 30, SYSTEM_VERSION: '3.2' };// ==================== GŁÓWNY HANDLER POST ==================== function doPost(e) { const lock = LockService.getScriptLock(); try { lock.waitLock(10000); const data = e.parameter; const sheetName = data.formType === 'program' ? CONFIG.PROGRAM_SHEET_NAME : CONFIG.EBOOK_SHEET_NAME; const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const sheet = spreadsheet.getSheetByName(sheetName); if (!sheet) { throw new Error(`Arkusz nie istnieje: ${sheetName}`); } const timestamp = new Date(); let rowData = []; if (sheetName === CONFIG.EBOOK_SHEET_NAME) { rowData = processEbookForm(data, timestamp); } else { rowData = processProgramForm(data, timestamp); } sheet.appendRow(rowData); Logger.log(`[doPost] Zapisano formularz: ${data.email} w ${sheetName}`); return ContentService .createTextOutput(JSON.stringify({ 'result': 'success', 'message': `Zapisano w: ${sheetName}`, 'timestamp': timestamp.toISOString() })) .setMimeType(ContentService.MimeType.JSON); } catch (error) { Logger.log('[doPost] Błąd: ' + error.toString()); return ContentService .createTextOutput(JSON.stringify({ 'result': 'error', 'message': error.toString() })) .setMimeType(ContentService.MimeType.JSON); } finally { lock.releaseLock(); } }// ==================== SYSTEM KOLEJKOWY ==================== class QueueManager { constructor() { this.properties = PropertiesService.getScriptProperties(); } addToQueue(type, data, delayMillis = 0) { const queueId = Utilities.getUuid(); const sendTime = new Date().getTime() + delayMillis; const queueItem = { id: queueId, type: type, sendTime: sendTime, data: data, createdAt: new Date().getTime(), attempts: 0 }; this.properties.setProperty(`queue_${queueId}`, JSON.stringify(queueItem)); Logger.log(`[Queue] Dodano do kolejki: ${type} dla ${data.email} o ${new Date(sendTime)}`); return queueId; } processQueue() { const now = new Date().getTime(); const allProperties = this.properties.getProperties(); const stats = { processed: 0, skipped: 0, errors: 0, total: 0 }; for (const key in allProperties) { if (!key.startsWith('queue_')) continue; stats.total++; try { const queueItem = JSON.parse(allProperties[key]); // Sprawdź czy czas wysłać if (queueItem.sendTime <= now) { // Przetwórz element const result = this.processQueueItem(queueItem); if (result.success) { // Usuń z kolejki this.properties.deleteProperty(key); stats.processed++; Logger.log(`[Queue] Przetworzono: ${queueItem.type} dla ${queueItem.data.email}`); } else if (result.skip) { // Pomiń i usuń this.properties.deleteProperty(key); stats.skipped++; Logger.log(`[Queue] Pominięto: ${queueItem.type} - ${result.reason}`); } else { // Błąd - spróbuj ponownie później queueItem.attempts++; if (queueItem.attempts > 3) { this.properties.deleteProperty(key); stats.errors++; Logger.log(`[Queue] Błąd (usunięto po 3 próbach): ${queueItem.type}`); } else { queueItem.sendTime = now + 60 * 60 * 1000; // Spróbuj za godzinę this.properties.setProperty(key, JSON.stringify(queueItem)); } } } } catch (error) { Logger.log(`[Queue] Błąd przetwarzania ${key}: ${error.toString()}`); stats.errors++; // Usuń uszkodzony element this.properties.deleteProperty(key); } } if (stats.processed > 0 || stats.errors > 0) { Logger.log(`[Queue] Statystyki: Przetworzono=${stats.processed}, Pominięto=${stats.skipped}, Błędy=${stats.errors}, Pozostało=${stats.total - stats.processed - stats.skipped - stats.errors}`); } return stats; } processQueueItem(queueItem) { const { type, data } = queueItem; try { // Sprawdź czy klient nie kupił (dla emaili konsultacyjnych) if (type.includes('consultation') && this.checkPurchaseStatus(data.email)) { return { skip: true, reason: 'Klient kupił' }; } // Przetwórz według typu switch(type) { // Emaile ebook case 'ebook_email_2': wyslijEmail2Function(data.imie, data.nazwisko, data.email); this.updateEmailStatus(data.email, CONFIG.COLUMNS.EMAIL_2_STATUS, 'Wysłano'); break; case 'ebook_email_3': wyslijEmail3Function(data.imie, data.nazwisko, data.email); this.updateEmailStatus(data.email, CONFIG.COLUMNS.EMAIL_3_STATUS, 'Wysłano'); break; case 'ebook_email_4': wyslijEmail4Function(data.imie, data.nazwisko, data.email); this.updateEmailStatus(data.email, CONFIG.COLUMNS.EMAIL_4_STATUS, 'Wysłano'); break; // Emaile konsultacyjne case 'consultation_24h_before': case 'consultation_2h_before': case 'consultation_15min_before': case 'consultation_10min_after': case 'consultation_24h_after': case 'consultation_3days_after': case 'consultation_7days_after': this.sendConsultationEmail(type, data); break; default: Logger.log(`[Queue] Nieznany typ: ${type}`); return { success: false }; } return { success: true }; } catch (error) { Logger.log(`[Queue] Błąd przetwarzania ${type}: ${error.toString()}`); return { success: false, error: error.toString() }; } } sendConsultationEmail(type, data) { const template = new ConsultationEmailTemplates(); let emailContent; let columnToUpdate; // Mapowanie typów na metody i kolumny const mapping = { 'consultation_24h_before': { method: 'get24hBeforeEmail', column: CONFIG.COLUMNS.KONSULTACJA_24H }, 'consultation_2h_before': { method: 'get2hBeforeEmail', column: CONFIG.COLUMNS.KONSULTACJA_2H }, 'consultation_15min_before': { method: 'get15minBeforeEmail', column: CONFIG.COLUMNS.KONSULTACJA_15MIN }, 'consultation_10min_after': { method: 'get10minAfterEmail', column: CONFIG.COLUMNS.KONSULTACJA_10MIN_PO }, 'consultation_24h_after': { method: 'get24hAfterEmail', column: CONFIG.COLUMNS.KONSULTACJA_24H_PO }, 'consultation_3days_after': { method: 'get3daysAfterEmail', column: CONFIG.COLUMNS.KONSULTACJA_3DNI_PO }, 'consultation_7days_after': { method: 'get7daysAfterEmail', column: CONFIG.COLUMNS.ANKIETA_STATUS } }; const config = mapping[type]; if (!config) { throw new Error(`Nieznany typ emaila konsultacyjnego: ${type}`); } emailContent = template[config.method](data); GmailApp.sendEmail(data.email, emailContent.subject, emailContent.text, { htmlBody: emailContent.html, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); // Aktualizuj status w arkuszu this.updateConsultationStatus(data.rowNumber, config.column, `Wysłano: ${new Date()}`); Logger.log(`[Queue] Wysłano ${type} do ${data.email}`); } checkPurchaseStatus(email) { const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const sheet = spreadsheet.getSheetByName(CONFIG.PROGRAM_SHEET_NAME); const data = sheet.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][3] === email) { return data[i][CONFIG.COLUMNS.PURCHASE_STATUS - 1] === 'KUPIŁ'; } } return false; } updateEmailStatus(email, column, status) { const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const sheet = spreadsheet.getSheetByName(CONFIG.EBOOK_SHEET_NAME); const data = sheet.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][3] === email) { sheet.getRange(i + 1, column).setValue(status); return; } } } updateConsultationStatus(rowNumber, column, status) { const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const sheet = spreadsheet.getSheetByName(CONFIG.PROGRAM_SHEET_NAME); sheet.getRange(rowNumber, column).setValue(status); } cleanup() { const now = new Date().getTime(); const maxAge = CONFIG.MAX_QUEUE_AGE_DAYS * 24 * 60 * 60 * 1000; const allProperties = this.properties.getProperties(); let cleaned = 0; for (const key in allProperties) { if (!key.startsWith('queue_')) continue; try { const queueItem = JSON.parse(allProperties[key]); // Usuń stare elementy if (queueItem.createdAt < (now - maxAge)) { this.properties.deleteProperty(key); cleaned++; } } catch (error) { // Usuń uszkodzone elementy this.properties.deleteProperty(key); cleaned++; } } if (cleaned > 0) { Logger.log(`[Queue] Wyczyszczono ${cleaned} starych elementów`); } return cleaned; } getStatus() { const allProperties = this.properties.getProperties(); const now = new Date().getTime(); const status = { total: 0, pending: 0, overdue: 0, byType: {} }; for (const key in allProperties) { if (!key.startsWith('queue_')) continue; try { const queueItem = JSON.parse(allProperties[key]); status.total++; if (queueItem.sendTime > now) { status.pending++; } else { status.overdue++; } status.byType[queueItem.type] = (status.byType[queueItem.type] || 0) + 1; } catch (error) { // Ignoruj uszkodzone } } return status; } }// ==================== MODUŁ INTEGRACJI KALENDARZA ==================== class CalendarIntegration { constructor() { this.calendar = CalendarApp.getDefaultCalendar(); this.spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); this.sheet = this.spreadsheet.getSheetByName(CONFIG.PROGRAM_SHEET_NAME); this.queue = new QueueManager(); } scanForNewConsultations() { const now = new Date(); const endDate = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); Logger.log(`[Calendar] Skanowanie wydarzeń od ${now} do ${endDate}`); const events = this.calendar.getEvents(now, endDate); const stats = { new: 0, updated: 0, skipped: 0 }; events.forEach(event => { if (this.isConsultationEvent(event)) { const result = this.processConsultationEvent(event); stats[result]++; } }); Logger.log(`[Calendar] Wyniki skanowania: Nowe=${stats.new}, Zaktualizowane=${stats.updated}, Pominięte=${stats.skipped}`); return stats; } isConsultationEvent(event) { const title = event.getTitle().toLowerCase(); const keywords = ['konsultacja', 'spotkanie', 'meet', 'zmiana', 'rozmowa', 'call']; return keywords.some(keyword => title.includes(keyword)); } processConsultationEvent(event) { const attendees = event.getGuestList(); for (let attendee of attendees) { const email = attendee.getEmail(); if (email === CONFIG.EMAIL_SENDER) continue; const clientRow = this.findClientByEmail(email); if (!clientRow) { Logger.log(`[Calendar] Klient nieznaleziony w arkuszu: ${email}`); return 'skipped'; } const purchaseStatus = this.sheet.getRange(clientRow, CONFIG.COLUMNS.PURCHASE_STATUS).getValue(); if (purchaseStatus === 'KUPIŁ') { Logger.log(`[Calendar] Klient już kupił: ${email}`); this.sendWelcomeEmail(email, clientRow); return 'skipped'; } const existingEventId = this.sheet.getRange(clientRow, CONFIG.COLUMNS.CALENDAR_EVENT_ID).getValue(); if (existingEventId === event.getId()) { return 'skipped'; } this.sheet.getRange(clientRow, CONFIG.COLUMNS.CALENDAR_EVENT_ID).setValue(event.getId()); this.scheduleConsultationEmails(email, event, clientRow); return existingEventId ? 'updated' : 'new'; } return 'skipped'; } findClientByEmail(email) { const data = this.sheet.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][3] === email) { return i + 1; } } return null; } scheduleConsultationEmails(email, event, rowNumber) { const eventTime = event.getStartTime(); const clientData = this.sheet.getRange(rowNumber, 1, 1, 16).getValues()[0]; const emailData = { email: email, imie: clientData[1], nazwisko: clientData[2], eventTime: eventTime.getTime(), eventId: event.getId(), meetLink: this.extractMeetLink(event), rowNumber: rowNumber }; // Wyślij natychmiast potwierdzenie this.sendConfirmationEmail(emailData); this.sheet.getRange(rowNumber, CONFIG.COLUMNS.KONSULTACJA_POTWIERDZENIE).setValue('Wysłano: ' + new Date()); // Dodaj pozostałe emaile do kolejki const eventTimeMs = eventTime.getTime(); const now = new Date().getTime(); // Lista emaili do zaplanowania const emailSchedule = [ { type: 'consultation_24h_before', delay: eventTimeMs - now - (24 * 60 * 60 * 1000) }, { type: 'consultation_2h_before', delay: eventTimeMs - now - (2 * 60 * 60 * 1000) }, { type: 'consultation_15min_before', delay: eventTimeMs - now - (15 * 60 * 1000) }, { type: 'consultation_10min_after', delay: eventTimeMs - now + CONFIG.DELAYS.KONSULTACJA_10MIN }, { type: 'consultation_24h_after', delay: eventTimeMs - now + CONFIG.DELAYS.KONSULTACJA_24H }, { type: 'consultation_3days_after', delay: eventTimeMs - now + CONFIG.DELAYS.KONSULTACJA_3DNI }, { type: 'consultation_7days_after', delay: eventTimeMs - now + CONFIG.DELAYS.KONSULTACJA_7DNI } ]; // Dodaj do kolejki tylko te, które są w przyszłości emailSchedule.forEach(schedule => { if (schedule.delay > 0) { this.queue.addToQueue(schedule.type, emailData, schedule.delay); } }); Logger.log(`[Calendar] Zaplanowano ${emailSchedule.filter(s => s.delay > 0).length} emaili dla ${email}`); } extractMeetLink(event) { const description = event.getDescription(); const meetRegex = /https:\/\/meet\.google\.com\/[a-z-]+/i; const match = description.match(meetRegex); return match ? match[0] : ''; } sendConfirmationEmail(emailData) { const template = new ConsultationEmailTemplates(); const { subject, html, text } = template.getConfirmationEmail(emailData); GmailApp.sendEmail(emailData.email, subject, text, { htmlBody: html, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); } sendWelcomeEmail(email, rowNumber) { const clientData = this.sheet.getRange(rowNumber, 1, 1, 16).getValues()[0]; const template = new ConsultationEmailTemplates(); const { subject, html, text } = template.getWelcomeEmail({ email: email, imie: clientData[1], nazwisko: clientData[2] }); GmailApp.sendEmail(email, subject, text, { htmlBody: html, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); } }// ==================== GŁÓWNY PROCESSOR ==================== function mainProcessor() { const startTime = new Date(); Logger.log(`[Main] ====== START PRZETWARZANIA ${startTime} ======`); try { // 1. Przetwórz kolejkę emaili const queue = new QueueManager(); const queueStats = queue.processQueue(); // 2. Skanuj kalendarz const calendar = new CalendarIntegration(); const calendarStats = calendar.scanForNewConsultations(); // 3. Przetwórz kolejkę ponownie (dla nowych elementów z kalendarza) const queueStats2 = queue.processQueue(); const endTime = new Date(); const duration = (endTime - startTime) / 1000; Logger.log(`[Main] ====== KONIEC PRZETWARZANIA ======`); Logger.log(`[Main] Czas wykonania: ${duration}s`); Logger.log(`[Main] Kolejka: Przetworzono=${queueStats.processed + queueStats2.processed}, Błędy=${queueStats.errors + queueStats2.errors}`); Logger.log(`[Main] Kalendarz: Nowe=${calendarStats.new}, Zaktualizowane=${calendarStats.updated}`); return { duration: duration, queue: { processed: queueStats.processed + queueStats2.processed, errors: queueStats.errors + queueStats2.errors }, calendar: calendarStats }; } catch (error) { Logger.log(`[Main] BŁĄD KRYTYCZNY: ${error.toString()}`); throw error; } }// ==================== FUNKCJE GETRESPONSE ==================== function addContactToGetResponse(imie, email) { try { const url = 'https://api.getresponse.com/v3/contacts'; if (!email || !email.includes('@')) { return "Błąd: Nieprawidłowy format adresu email"; } const payload = { 'email': email, 'name': imie, 'campaign': { 'campaignId': CONFIG.GETRESPONSE_CAMPAIGN_ID } }; Logger.log('[GetResponse] Wysyłanie: ' + email); const options = { 'method': 'post', 'contentType': 'application/json', 'headers': { 'X-Auth-Token': 'api-key ' + CONFIG.GETRESPONSE_API_KEY }, 'payload': JSON.stringify(payload), 'muteHttpExceptions': true }; const response = UrlFetchApp.fetch(url, options); const responseCode = response.getResponseCode(); if (responseCode >= 200 && responseCode < 300) { return "Dodano do GetResponse"; } else if (responseCode === 409) { return "Już istnieje w GetResponse"; } else { return "Błąd GetResponse: " + responseCode; } } catch (error) { Logger.log('[GetResponse] Błąd: ' + error.toString()); return "Błąd: " + error.toString(); } }// ==================== FUNKCJA STOPKI ==================== function createEmailFooter() { return `
Kamil Owsianik

Do zobaczenia

Założyciel Kliniki Formy
Tel: +48 733 705 478
@: kontakt@klinika-formy.pl
WWW: klinika-formy.pl
`; }// ==================== FUNKCJE EMAILI EBOOK - POPRAWIONE ====================function wyslijEmail1(imie, nazwisko, email) { try { const pelneImie = imie + ' ' + nazwisko; const temat = 'MATERIAŁ WIDEO + 3 BONUSY - Twoja droga do wymarzonej sylwetki zaczyna się TUTAJ!'; const htmlBody = `
Klinika Formy
Witaj ${pelneImie}!

GRATULACJE! Właśnie wykonałeś pierwszy, najważniejszy krok w kierunku swojej wymarzonej sylwetki!

UWAGA! Twoje materiały są już gotowe:

Za chwilę odkryjesz moją sprawdzoną metodę, dzięki której:

  • ✓ Schudłem 29 kg bez efektu jo-jo (i utrzymuję wagę od 2 lat!)
  • ✓ Pomogłem 347+ osobom osiągnąć ich wymarzoną sylwetkę (tylko przez ostatni rok)
  • ✓ Stworzysz nawyki na całe życie, nie tylko chwilową dietę
WAŻNE: Materiały są dostępne tylko przez 72 godziny!
Po tym czasie link wygaśnie.

Kliknij poniżej i obejrzyj 4-minutowe wideo, które zmieni Twoje podejście do odchudzania:

OBEJRZYJ WIDEO TERAZ (4 MIN)

TWOJE BONUSY (wartość 497 zł - dziś ZA DARMO):

  • E-book "ZMIANA" - Mój najnowszy poradnik (wartość 147 zł)
  • Darmowa dieta startowa - 7-dniowy jadłospis (wartość 97 zł)
  • Dostęp do grupy wsparcia na FB - 2000+ osób (wartość 67 zł/mies.)
  • BONUS SPECJALNY: Bezpłatna konsultacja online (wartość 250 zł) - tylko dla pierwszych 10 osób!

"W 3 miesiące schudłam 18 kg! Metoda Kamila to nie kolejna dieta cud, ale realny plan, który da się stosować na co dzień. Najlepsze? Zero efektu jo-jo od roku!"

- Katarzyna M., 34 lata

PS. Pamiętaj - na konsultacji przygotuję dla Ciebie DARMOWY 12-tygodniowy plan działania dostosowany do Twoich potrzeb. Pokażę Ci go jeszcze ZANIM zdecydujesz się na współpracę. To wartość 250 zł - dziś możesz otrzymać go ZA DARMO!

TAK, CHCĘ OBEJRZEĆ WIDEO I OTRZYMAĆ BONUSY

Nie przegap tej szansy - to może być dzień, który zmieni Twoje życie!

${createEmailFooter()}
`; const plainTextBody = `Witaj ${pelneImie}! GRATULACJE! Oto link do wideo i Twoich bonusów. Obejrzyj koniecznie: ${CONFIG.URLS.LANDING_PAGE_2}`; GmailApp.sendEmail(email, temat, plainTextBody, { htmlBody: htmlBody, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); Logger.log('[Email] Wysłano email 1 do: ' + email); return "Wysłano pomyślnie"; } catch (error) { throw new Error('Błąd wysyłania emaila 1: ' + error.toString()); } }function wyslijEmail2Function(imie, nazwisko, email) { try { const temat = imie + ', udało Ci się obejrzeć wideo? (ważna informacja o konsultacji)'; const htmlBody = `
Klinika Formy

Cześć ${imie}!

Minęło już 5 godzin odkąd zgłosiłeś się po materiały...

Wiem, że życie bywa zabiegane, ale nie chcę, żebyś przegapił swoją szansę!

Czy udało Ci się już obejrzeć moje 4-minutowe wideo?

Jeśli jeszcze nie, to koniecznie zrób to teraz, ponieważ:

BEZPŁATNA KONSULTACJA - Tylko dla Ciebie!

Co otrzymasz na konsultacji?

  • 12-tygodniowy indywidualny plan działania (wartość 250 zł)
  • Analizę Twoich największych przeszkód w odchudzaniu
  • Konkretne rozwiązania dopasowane do Twojego trybu życia
  • Plan treningowy i żywieniowy "szyty na miarę"

Wszystko POKAŻĘ Ci jeszcze PRZED podjęciem decyzji o współpracy!

UWAGA: Zostało tylko 7 miejsc na bezpłatne konsultacje w tym tygodniu!
3 osoby właśnie rezerwują swoje terminy...
Normalny koszt takiej konsultacji: 250 zł
Dla Ciebie dziś: GRATIS
Ale tylko jeśli wypełnisz formularz w ciągu 48h!

OBEJRZYJ WIDEO I ZAREZERWUJ KONSULTACJĘ

✓ Jak przygotować się do konsultacji?

  1. Obejrzyj wideo do końca - poznasz moją metodę
  2. Wypełnij formularz pod wideo - to zajmie max 3 minuty
  3. Pobierz aplikację Google Meet - spotkamy się online
  4. Przygotuj pytania - odpowiem na wszystkie Twoje wątpliwości

Kamerka NIE jest wymagana! Wystarczy, że będziesz mnie słyszeć i widzieć mój ekran.

PS. Wiesz co jest najgorsze? 80% osób, które odkładają decyzję "na później", nigdy nie wraca do tematu. Nie pozwól, aby Twoja szansa na zmianę przeszła Ci koło nosa!

NIE ODKŁADAJ - DZIAŁAJ TERAZ!

"Najlepszy moment na zmianę był wczoraj. Drugi najlepszy jest TERAZ."

${createEmailFooter()}
`; const plainTextBody = `Cześć ${imie}! Czy udało Ci się obejrzeć wideo? Mam dla Ciebie bezpłatną konsultację (wartość 250 zł). Zobacz: ${CONFIG.URLS.LANDING_PAGE_2}`; GmailApp.sendEmail(email, temat, plainTextBody, { htmlBody: htmlBody, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); Logger.log('[Email] Wysłano email 2 do: ' + email); return "Wysłano pomyślnie"; } catch (error) { Logger.log('[Email] Błąd wysyłania emaila 2: ' + error.toString()); return "Błąd: " + error.toString(); } }function wyslijEmail3Function(imie, nazwisko, email) { try { const temat = imie + ', zobacz co osiągnęli inni! (+ Twoja konsultacja czeka)'; const htmlBody = `
Klinika Formy

Witaj ${imie}!

Może się zastanawiasz: "Czy to naprawdę działa?"

Pozwól, że pokażę Ci, co osiągnęli ludzie DOKŁADNIE w Twojej sytuacji:

347+
Zadowolonych klientów
4,215 kg
Łącznie zrzuconych
0%
Efektu jo-jo

"Minus 22 kg w 4 miesiące!"

"Myślałam, że po 40-tce już nie schudnę. Kamil udowodnił mi, że się myliłam. Jego plan był prosty do wdrożenia, a efekty przeszły moje oczekiwania!"

- Anna K., 42 lata, mama 3 dzieci

"Z rozmiaru XXL do M!"

"Pracuję po 12 godzin dziennie. Myślałem, że nie dam rady. Plan Kamila był idealnie dopasowany do mojego trybu życia. Schudłem 31 kg!"

- Piotr M., 38 lat, kierowca

"Najlepsza inwestycja w siebie!"

"15 kg w 3 miesiące i czuję się jak nastolatka! Mam więcej energii niż 10 lat temu. Żałuję tylko, że nie zaczęłam wcześniej!"

- Małgorzata W., 35 lat

Zobacz wszystkie 284 opinie na naszej stronie

Twoja kolej na sukces!

Za 3 miesiące możesz być kolejną osobą, która napisze swoją historię sukcesu!

Ale tylko jeśli ZACZNIESZ DZIAŁAĆ TERAZ.

Co musisz zrobić?

  1. Obejrzyj moje 4-minutowe wideo
  2. Wypełnij krótki formularz (3 minuty)
  3. Odbierz DARMOWĄ konsultację (wartość 250 zł)
OSTATNIE 24 GODZINY na darmową konsultację!
Potem koszt: 250 zł

DOŁĄCZ DO 347+ OSÓB, KTÓRE ZMIENIŁY SWOJE ŻYCIE

Specjalna wiadomość od mojej ostatniej klientki:

"${imie}, jeśli to czytasz - NIE ZWLEKAJ! Ja też się wahałam 2 tygodnie. To były 2 stracone tygodnie, w których mogłam już działać. Dziś, 3 miesiące później, jestem lżejsza o 17 kg i żałuję tylko jednego - że nie zaczęłam od razu!"

- Dorota, rozpoczęła program 3 miesiące temu

PS. Pamiętaj - na konsultacji POKAŻĘ Ci cały 12-tygodniowy plan jeszcze ZANIM podejmiesz decyzję. Zero ryzyka, same korzyści!

ZAREZERWUJ SWOJĄ DARMOWĄ KONSULTACJĘ TERAZ

${createEmailFooter()}
`; const plainTextBody = `Witaj ${imie}! Zobacz, co osiągnęli inni - 347+ osób zmieniło swoje życie. Twoja darmowa konsultacja (wartość 250 zł) czeka: ${CONFIG.URLS.LANDING_PAGE_2}`; GmailApp.sendEmail(email, temat, plainTextBody, { htmlBody: htmlBody, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); Logger.log('[Email] Wysłano email 3 do: ' + email); return "Wysłano pomyślnie"; } catch (error) { Logger.log('[Email] Błąd wysyłania emaila 3: ' + error.toString()); return "Błąd: " + error.toString(); } }function wyslijEmail4Function(imie, nazwisko, email) { try { const temat = imie + ', Twoje BONUSY są gotowe! (e-book, dieta, grupa FB + OSTATNIA SZANSA na konsultację)'; const htmlBody = `
Klinika Formy

Cześć ${imie}!

ZOSTAŁO TYLKO 24 GODZINY!

To Twoja OSTATNIA SZANSA na darmową konsultację!

Jutro o tej porze będzie za późno...

Mam dla Ciebie świetne wieści! Przygotowałem wszystkie obiecane bonusy. Są już dostępne do pobrania!

TWOJE EKSKLUZYWNE BONUSY:

BONUS #1: E-book "ZMIANA" - Mój najnowszy poradnik

147 zł GRATIS!

Kompletny przewodnik po transformacji sylwetki. 87 stron praktycznej wiedzy!

BONUS #2: Darmowa 7-dniowa dieta startowa

97 zł GRATIS!

Gotowy jadłospis z listą zakupów i przepisami. Zacznij już jutro!

BONUS #3: Dostęp do ekskluzywnej grupy na Facebooku

67 zł/mies GRATIS!

2137+ osób na tej samej drodze. Wsparcie, motywacja, sprawdzone porady!

BONUS SPECJALNY: Indywidualna konsultacja + Plan 12-tyg

250 zł OSTATNIA SZANSA!

UWAGA: Ten bonus jest dostępny TYLKO DZIŚ! Jutro konsultacja będzie kosztować 250 zł.

Całkowita wartość Twoich bonusów:

561 zł 0 zł

Oszczędzasz ponad 500 złotych!

POBIERZ E-BOOK I DIETĘ DOŁĄCZ DO GRUPY FB

Jak się przygotować do DARMOWEJ konsultacji?

To bardzo proste - tylko 4 kroki:

  1. Pobierz Google Meet na telefon lub komputer (darmowa aplikacja)
  2. Obejrzyj moje wideo do końca (4 minuty)
  3. Wypełnij formularz pod wideo (3 minuty)
  4. Wybierz termin, który Ci pasuje

Ważne: Kamerka NIE jest wymagana! Możesz uczestniczyć anonimowo. Udostępnię Ci mój ekran i pokażę przygotowany plan.

${imie}, TO NAPRAWDĘ OSTATNI MOMENT!

Za 24 godziny stracisz szansę na:

  • Darmową konsultację wartą 250 zł
  • Indywidualny plan 12-tygodniowy
  • Moją osobistą pomoc w starcie

Nie żałuj później, że nie skorzystałeś!

OSTATNIA SZANSA - ZAREZERWUJ KONSULTACJĘ TERAZ!

Moja osobista obietnica dla Ciebie:

"${imie}, wiem, że możesz mieć wątpliwości. Dlatego daję Ci moją gwarancję: na konsultacji pokażę Ci DOKŁADNY plan działania na 12 tygodni. Zobaczysz co, kiedy i jak masz robić. Jeśli uznasz, że to nie dla Ciebie - po prostu podziękujesz i tyle. Zero presji, zero nacisków.

Ale jestem pewien, że gdy zobaczysz swój spersonalizowany plan, będziesz chciał zacząć od razu!"

- Kamil Owsianik

PS. To jest mój ostatni email w tej sprawie. Jeśli teraz nie skorzystasz, stracisz tę okazję na zawsze. Konsultacja warta 250 zł czeka na Ciebie ZA DARMO - ale tylko DZIŚ!

PPS. Pamiętaj - już ponad 347 osób zmieniło swoje życie. Następny możesz być Ty!

DOŁĄCZ DO PROGRAMU ZMIANA - ZACZNIJ DZIŚ!

${createEmailFooter()}
`; const plainTextBody = `Cześć ${imie}! Twoje bonusy są gotowe! E-book, dieta, grupa FB + OSTATNIA SZANSA na darmową konsultację (wartość 250 zł). Pobierz tutaj: ${CONFIG.URLS.DOWNLOAD_PAGE} i zarezerwuj konsultację: ${CONFIG.URLS.LANDING_PAGE_2}`; GmailApp.sendEmail(email, temat, plainTextBody, { htmlBody: htmlBody, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); Logger.log('[Email] Wysłano email 4 do: ' + email); return "Wysłano pomyślnie"; } catch (error) { Logger.log('[Email] Błąd wysyłania emaila 4: ' + error.toString()); return "Błąd: " + error.toString(); } }function wyslijEmailPotwierdzajacyKonsultacje(imie, nazwisko, email) { try { const pelneImie = imie + ' ' + nazwisko; const temat = 'Potwierdzenie zgłoszenia + INSTRUKCJE przygotowania do konsultacji'; const htmlBody = `
Klinika Formy

GRATULACJE ${pelneImie}!

Twoje zgłoszenie zostało przyjęte!

Świetna decyzja! Właśnie wykonałeś najważniejszy krok w kierunku swojej wymarzonej sylwetki.

CO DZIEJE SIĘ DALEJ?

1POTWIERDŹ ZGŁOSZENIE (WAŻNE!)

Kliknij poniżej i potwierdź swoje zgłoszenie przez WhatsApp lub email:
POTWIERDŹ ZGŁOSZENIE TUTAJ

2W ciągu 24-48h skontaktujemy się z Tobą

Zadzwonimy z numeru: +48 733 705 478
Zapisz ten numer, aby nie przegapić połączenia!

3Ustalimy dogodny termin konsultacji

Znajdziemy termin, który będzie Ci najbardziej odpowiadał.
Konsultacja trwa około 20-30 minut.

4Przygotuję Twój indywidualny plan

Na podstawie Twoich danych przygotuję spersonalizowany
12-tygodniowy plan działania, który pokażę Ci na konsultacji.

POTWIERDŹ SWOJE ZGŁOSZENIE TERAZ

JAK PRZYGOTOWAĆ SIĘ DO KONSULTACJI?

  • Pobierz aplikację Google Meet - to tam się spotkamy (link otrzymasz SMS-em)
  • Znajdź spokojne miejsce - gdzie będziesz mógł swobodnie rozmawiać
  • Przygotuj pytania - zapisz wszystko, co chcesz wiedzieć
  • Kamerka NIE jest wymagana - możesz uczestniczyć tylko głosowo
  • Weź notes - będziesz chciał zapisać ważne informacje

Nie zapomnij pobrać swoich BONUSÓW!

Jako osoba zapisana na konsultację otrzymujesz:

  • E-book "ZMIANA" (wartość 147 zł)
  • 7-dniową dietę startową (wartość 97 zł)
  • Dostęp do grupy wsparcia na FB

>> KLIKNIJ TUTAJ, ABY POBRAĆ BONUSY <<

Masz pytania? Potrzebujesz szybkiego kontaktu?

Napisz do mnie na WhatsApp:

WhatsApp: +48 733 705 478

Lub zadzwoń: +48 733 705 478

Jestem dostępny pon-pt: 9:00-18:00, sob: 10:00-14:00

WAŻNE:
Na konsultacji pokażę Ci DOKŁADNY plan na 12 tygodni.
Zobaczysz wszystko jeszcze ZANIM podejmiesz decyzję o współpracy.
To konsultacja wartości 250 zł - dla Ciebie całkowicie ZA DARMO!

Do zobaczenia na konsultacji!

Pozdrawiam serdecznie,
Kamil Owsianik
Twój trener przemian

PS. Jeśli z jakiegoś powodu będziesz musiał przełożyć termin - daj znać.
Zawsze możemy ustalić inny, dogodny dla Ciebie moment.

${createEmailFooter()}
`; const plainTextBody = `Witaj ${pelneImie}, Twoje zgłoszenie zostało przyjęte! POTWIERDŹ ZGŁOSZENIE: ${CONFIG.URLS.CONFIRMATION_PAGE} Skontaktujemy się w ciągu 24-48h z numeru +48 733 705 478. Pobierz bonusy: ${CONFIG.URLS.DOWNLOAD_PAGE}`; GmailApp.sendEmail(email, temat, plainTextBody, { htmlBody: htmlBody, name: "Kamil Owsianik - Klinika Formy", replyTo: CONFIG.EMAIL_SENDER }); Logger.log('[Email] Wysłano potwierdzenie konsultacji do: ' + email); return "Wysłano pomyślnie"; } catch (error) { throw new Error('Błąd wysyłania emaila konsultacji: ' + error.toString()); } }// ==================== SZABLONY EMAILI KONSULTACYJNYCH ==================== class ConsultationEmailTemplates { getConfirmationEmail(data) { const meetingDate = new Date(data.eventTime); const formattedDate = Utilities.formatDate(meetingDate, 'Europe/Warsaw', 'dd.MM.yyyy'); const formattedTime = Utilities.formatDate(meetingDate, 'Europe/Warsaw', 'HH:mm'); const subject = 'Jutro o ' + formattedTime + ' - Twoja konsultacja'; // Tworzenie linku do kalendarza Google const eventTitle = encodeURIComponent('Konsultacja - Klinika Formy'); const eventDetails = encodeURIComponent('Konsultacja online z Kamilem Owsianik\n\nLink do spotkania: ' + data.meetLink); const startTime = meetingDate.toISOString().replace(/-|:|\.\d\d\d/g, ''); const endTime = new Date(meetingDate.getTime() + 60 * 60 * 1000).toISOString().replace(/-|:|\.\d\d\d/g, ''); const calendarLink = `https://calendar.google.com/calendar/render?action=TEMPLATE&text=${eventTitle}&details=${eventDetails}&dates=${startTime}/${endTime}`; const html = `
Klinika Formy

Cześć ${data.imie}!

Świetnie! Twoja konsultacja jest potwierdzona!

Szczegóły spotkania:

Data: ${formattedDate}

Godzina: ${formattedTime}

Miejsce: Online - Google Meet

${data.meetLink ? `

Link do spotkania:
${data.meetLink}

` : '

Link: Otrzymasz w osobnej wiadomości

'}
DODAJ DO KALENDARZA

JAK PRZYGOTOWAĆ SIĘ DO KONSULTACJI?

  1. Pobierz Google Meet - darmowa aplikacja na telefon/komputer
  2. Przygotuj spokojne miejsce - gdzie będziesz mógł swobodnie rozmawiać
  3. Weź notes i długopis - będziesz chciał zapisać ważne informacje
  4. Przygotuj pytania - zapisz wszystko, co chcesz wiedzieć

Kamerka NIE jest wymagana! Udostępnię Ci mój ekran i pokażę przygotowany plan.

WAŻNE: Jeśli coś wypadnie i nie będziesz mógł uczestniczyć w konsultacji, koniecznie daj znać minimum 10 godzin wcześniej! Wtedy ustalimy nowy termin i nie stracisz swojej bezpłatnej szansy.

WhatsApp/Tel: +48 733 705 478

Na konsultacji pokażę Ci DOKŁADNY plan na 12 tygodni!

Do zobaczenia jutro!
Kamil Owsianik

${createEmailFooter()}
`; const text = 'Twoja konsultacja jest potwierdzona!'; return { subject, html, text }; } get24hBeforeEmail(data) { const meetingDate = new Date(data.eventTime); const formattedTime = Utilities.formatDate(meetingDate, 'Europe/Warsaw', 'HH:mm'); const subject = 'Jutro o ' + formattedTime + ' - Twoja konsultacja (przygotuj się!)'; const html = `

Hej ${data.imie}!

Jutro o ${formattedTime} mamy konsultację!

Przygotuj się na jutro:

  1. Pobierz Google Meet (jeśli jeszcze nie masz)
  2. Znajdź spokojne miejsce na rozmowę
  3. Przygotuj pytania które Cię nurtują
${data.meetLink ? `

Link do spotkania: ${data.meetLink}

` : '

Link otrzymasz w kolejnej wiadomości

'}

Pamiętaj: Jeśli nie możesz uczestniczyć, daj znać minimum 10h wcześniej!
WhatsApp: +48 733 705 478

Do zobaczenia jutro!
Kamil

${createEmailFooter()}
`; const text = 'Jutro konsultacja!'; return { subject, html, text }; } get2hBeforeEmail(data) { const meetingDate = new Date(data.eventTime); const formattedTime = Utilities.formatDate(meetingDate, 'Europe/Warsaw', 'HH:mm'); const subject = 'Za 2 godziny start! Konsultacja o ' + formattedTime; const html = `

Za 2 godziny spotykamy się online!

Cześć ${data.imie}!

Ostatnie przygotowania:

  • Sprawdź czy masz Google Meet
  • Przygotuj notes do zapisków
  • Znajdź spokojne miejsce
${data.meetLink ? `

Link: ${data.meetLink}

` : ''}

Pokażę Ci Twój spersonalizowany plan na 12 tygodni!

Do zobaczenia!
Kamil

${createEmailFooter()}
`; const text = 'Za 2 godziny konsultacja!'; return { subject, html, text }; } get15minBeforeEmail(data) { const subject = 'START za 15 minut!'; const html = `

STARTUJEMY ZA 15 MINUT!

${data.imie}, czekam na Ciebie online!

${data.meetLink ? `DOŁĄCZ DO SPOTKANIA` : '

Sprawdź link w poprzednich wiadomościach

'}

Pamiętaj - udostępnię Ci ekran i pokażę cały plan!

${createEmailFooter()}
`; const text = 'START za 15 minut!'; return { subject, html, text }; } get10minAfterEmail(data) { const subject = 'Twój plan + prezentacja platformy'; const html = `

Dziękuję za spotkanie, ${data.imie}!

Jak obiecałem, przesyłam materiały:

PREZENTACJA PLATFORMY:

OBEJRZYJ PREZENTACJĘ

TWÓJ PLAN DZIAŁANIA:

OTWÓRZ PLAN W GOOGLE DOCS

Pozdrawiam,
Kamil

${createEmailFooter()}
`; const text = 'Dziękuję za spotkanie!'; return { subject, html, text }; } get24hAfterEmail(data) { const subject = 'Twój plan + oferta specjalna (48h)'; const html = `

Cześć ${data.imie}!

Mam nadzieję, że nasza wczorajsza rozmowa dała Ci jasny obraz tego, jak możesz osiągnąć swoją wymarzoną sylwetkę.

TWÓJ PLAN JEST TUTAJ:

OTWÓRZ PLAN

OFERTA SPECJALNA - TYLKO 48 GODZIN!

Jak wspomniałem na konsultacji, przygotowałem dla Ciebie specjalną ofertę:

1350 zł (zamiast 1500 zł)

Oszczędzasz 150 zł!

Opcje płatności:

Ta oferta jest ważna tylko 48 godzin od naszej rozmowy.

Kamil

${createEmailFooter()}
`; const text = 'Twój plan gotowy!'; return { subject, html, text }; } get3daysAfterEmail(data) { const subject = 'Ostatnia szansa na ofertę'; const html = `

OSTATNIE GODZINY!

Cześć ${data.imie},

Oferta 1350 zł wygasa dziś o północy!

OSTATNIA SZANSA

1350 zł zamiast 1500 zł

Oszczędzasz 150 zł!

ZAPŁAĆ 1350 ZŁ

3 RATY | 6 RAT

Kamil

${createEmailFooter()}
`; const text = 'Ostatnia szansa!'; return { subject, html, text }; } get7daysAfterEmail(data) { const subject = 'Czy mogę zapytać?'; const html = `

Cześć ${data.imie}

Minął tydzień od naszej konsultacji.

Chciałbym poznać Twoją opinię i dowiedzieć się, co mogę poprawić.

Wypełnij krótką ankietę (2 minuty):

WYPEŁNIJ ANKIETĘ

W podziękowaniu otrzymasz:

KOD -10% na wszystkie programy

Pozdrawiam,
Kamil

${createEmailFooter()}
`; const text = 'Czy mogę zapytać?'; return { subject, html, text }; } getWelcomeEmail(data) { const subject = 'Witaj w programie!'; const html = `

WITAJ W ZESPOLE!

Cześć ${data.imie}!

Cieszę się, że jesteś z nami! Teraz zaczynamy prawdziwą pracę nad Twoją sylwetką.

CO DALEJ?

Napisz "START" na WhatsApp: +48 733 705 478

Otrzymasz dostęp do platformy i wszystkie materiały startowe.

Witaj w rodzinie Kliniki Formy!

Kamil

${createEmailFooter()}
`; const text = 'Witaj w programie!'; return { subject, html, text }; } }// ==================== FUNKCJE POMOCNICZE ==================== function zaplanujKolejneEmaile(imie, nazwisko, email, timestamp) { const queue = new QueueManager(); // Dodaj emaile 2, 3, 4 do kolejki queue.addToQueue('ebook_email_2', { imie, nazwisko, email }, CONFIG.DELAYS.EMAIL_2); queue.addToQueue('ebook_email_3', { imie, nazwisko, email }, CONFIG.DELAYS.EMAIL_3); queue.addToQueue('ebook_email_4', { imie, nazwisko, email }, CONFIG.DELAYS.EMAIL_4); Logger.log(`[Email] Zaplanowano 3 emaile dla: ${email}`); }// ==================== PRZETWARZANIE FORMULARZY ==================== function processEbookForm(data, timestamp) { const rowData = [ timestamp, data.imie || '', data.nazwisko || '', data.email || '', data.tel || '', data.cel || '' ]; let emailStatus = 'Nie wysłano'; if (data.email) { try { emailStatus = wyslijEmail1(data.imie, data.nazwisko, data.email); zaplanujKolejneEmaile(data.imie, data.nazwisko, data.email, timestamp); } catch (error) { Logger.log('[Form] Błąd emaila: ' + error.toString()); emailStatus = 'Błąd: ' + error.toString(); } } while (rowData.length < CONFIG.COLUMNS.EMAIL_1_STATUS - 1) { rowData.push(''); } rowData.push(emailStatus); rowData.push('Zaplanowano'); rowData.push('Zaplanowano'); rowData.push('Zaplanowano'); let getResponseStatus = 'Nie wysłano'; if (data.email) { try { getResponseStatus = addContactToGetResponse(data.imie, data.email); } catch (error) { Logger.log('[Form] GetResponse błąd: ' + error.toString()); getResponseStatus = 'Błąd: ' + error.toString(); } } while (rowData.length < CONFIG.COLUMNS.EBOOK_GETRESPONSE - 1) { rowData.push(''); } rowData[CONFIG.COLUMNS.EBOOK_GETRESPONSE - 1] = getResponseStatus; return rowData; }function processProgramForm(data, timestamp) { const rowData = [ timestamp, data.imie || '', data.nazwisko || '', data.email || '', data.tel || '', data.waga || '', data.wzrost || '', data.wiek || '', data.cel || '', data.czas_proby || '', data.samopoczucie || '', data.pilnosc || '', data.budzet || '', data.godziny_kontaktu || '', data.dlaczego || '', data.choroby || '' ]; let emailStatus = 'Nie wysłano'; if (data.email) { try { emailStatus = wyslijEmailPotwierdzajacyKonsultacje(data.imie, data.nazwisko, data.email); } catch (error) { Logger.log('[Form] Błąd emaila: ' + error.toString()); emailStatus = 'Błąd: ' + error.toString(); } } rowData.push(emailStatus); rowData.push(''); // PURCHASE_STATUS let getResponseStatus = 'Nie wysłano'; if (data.email) { try { getResponseStatus = addContactToGetResponse(data.imie, data.email); } catch (error) { Logger.log('[Form] GetResponse błąd: ' + error.toString()); getResponseStatus = 'Błąd: ' + error.toString(); } } while (rowData.length < CONFIG.COLUMNS.PROGRAM_GETRESPONSE - 1) { rowData.push(''); } rowData.push(getResponseStatus); return rowData; }// ==================== FUNKCJE SYSTEMOWE ==================== function setupOptimizedSystem() { Logger.log('[Setup] Rozpoczynam konfigurację systemu v3.2...'); // 1. Wyczyść wszystkie triggery const triggers = ScriptApp.getProjectTriggers(); triggers.forEach(trigger => { ScriptApp.deleteTrigger(trigger); Logger.log(`[Setup] Usunięto trigger: ${trigger.getHandlerFunction()}`); }); // 2. Ustaw główny processor (co godzinę) ScriptApp.newTrigger('mainProcessor') .timeBased() .everyHours(1) .create(); // 3. Ustaw czyszczenie (raz dziennie) ScriptApp.newTrigger('dailyMaintenance') .timeBased() .everyDays(1) .atHour(3) .create(); Logger.log('[Setup] System v3.2 skonfigurowany! Używa tylko 2 triggery.'); // 4. Sprawdź status const status = checkSystemStatus(); return { message: 'System zoptymalizowany', version: CONFIG.SYSTEM_VERSION, triggers: 2, status: status }; }function dailyMaintenance() { Logger.log('[Maintenance] Rozpoczynam codzienną konserwację...'); const queue = new QueueManager(); const cleaned = queue.cleanup(); // Wyczyść stare properties const properties = PropertiesService.getScriptProperties().getProperties(); let oldProperties = 0; for (const key in properties) { if (key.startsWith('trigger_') || key.startsWith('consultation_') || key.startsWith('user_')) { PropertiesService.getScriptProperties().deleteProperty(key); oldProperties++; } } Logger.log(`[Maintenance] Wyczyszczono: ${cleaned} elementów kolejki, ${oldProperties} starych properties`); return { queueCleaned: cleaned, propertiesCleaned: oldProperties }; }function checkSystemStatus() { const queue = new QueueManager(); const queueStatus = queue.getStatus(); const status = { version: CONFIG.SYSTEM_VERSION, triggers: ScriptApp.getProjectTriggers().length, maxTriggers: 20, properties: Object.keys(PropertiesService.getScriptProperties().getProperties()).length, queue: queueStatus, sheets: { ebook: SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID) .getSheetByName(CONFIG.EBOOK_SHEET_NAME).getLastRow() - 1, program: SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID) .getSheetByName(CONFIG.PROGRAM_SHEET_NAME).getLastRow() - 1 }, nextEvents: getUpcomingConsultations() }; Logger.log('[Status] System v' + status.version); Logger.log('[Status] Triggery: ' + status.triggers + '/' + status.maxTriggers); Logger.log('[Status] Kolejka: ' + queueStatus.total + ' (oczekujące: ' + queueStatus.pending + ', zaległe: ' + queueStatus.overdue + ')'); Logger.log('[Status] Rekordy: Ebook=' + status.sheets.ebook + ', Program=' + status.sheets.program); return status; }function getUpcomingConsultations() { const cal = CalendarApp.getDefaultCalendar(); const now = new Date(); const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000); const events = cal.getEvents(now, tomorrow); const consultations = []; events.forEach(event => { if (event.getTitle().toLowerCase().includes('konsultacja')) { consultations.push({ title: event.getTitle(), time: event.getStartTime(), guests: event.getGuestList().map(g => g.getEmail()).filter(e => e !== CONFIG.EMAIL_SENDER) }); } }); return consultations; }// ==================== FUNKCJE ADMINISTRACYJNE ==================== function manualCalendarScan() { Logger.log('[Manual] Ręczne skanowanie kalendarza...'); const calendar = new CalendarIntegration(); const results = calendar.scanForNewConsultations(); // Przetwórz kolejkę od razu const queue = new QueueManager(); const queueResults = queue.processQueue(); return { calendar: results, queue: queueResults }; }function forceProcessQueue() { Logger.log('[Manual] Wymuszenie przetworzenia kolejki...'); const queue = new QueueManager(); return queue.processQueue(); }function clearAllData() { const ui = SpreadsheetApp.getUi(); const response = ui.alert( 'UWAGA!', 'Czy na pewno chcesz wyczyścić WSZYSTKIE dane systemowe?', ui.ButtonSet.YES_NO ); if (response === ui.Button.YES) { // Wyczyść triggery const triggers = ScriptApp.getProjectTriggers(); triggers.forEach(t => ScriptApp.deleteTrigger(t)); // Wyczyść properties const properties = PropertiesService.getScriptProperties(); properties.deleteAllProperties(); Logger.log('[Clear] Wszystkie dane systemowe wyczyszczone'); return 'Wyczyszczono wszystkie dane'; } return 'Anulowano'; }function testEmailDelivery(email) { if (!email) { email = 'test@example.com'; } try { GmailApp.sendEmail( email, 'TEST - Klinika Formy CRM', 'To jest email testowy.', { htmlBody: '

Test Email

System działa poprawnie.

', name: "Kamil Owsianik - TEST", replyTo: CONFIG.EMAIL_SENDER } ); Logger.log(`[Test] Email testowy wysłany do: ${email}`); return 'Email testowy wysłany'; } catch (error) { Logger.log(`[Test] Błąd wysyłania: ${error.toString()}`); return 'Błąd: ' + error.toString(); } }function addTestClient(imie = 'Test', nazwisko = 'Testowy', email = 'test@example.com', telefon = '+48123456789') { const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const sheet = spreadsheet.getSheetByName(CONFIG.PROGRAM_SHEET_NAME); const rowData = [ new Date(), imie, nazwisko, email, telefon, '80', // waga '180', // wzrost '35', // wiek 'Test systemu', '3 miesiące', 'Dobrze', 'Pilne', '1500', '10-18', 'Test', 'Brak' ]; sheet.appendRow(rowData); Logger.log(`[Test] Dodano klienta testowego: ${email}`); return 'Klient testowy dodany'; }// ==================== MENU INTERFACE ==================== function onOpen() { const ui = SpreadsheetApp.getUi(); ui.createMenu('Klinika Formy CRM v3.2') .addItem('Status systemu', 'showSystemStatus') .addItem('Przetwórz kolejkę TERAZ', 'forceProcessQueue') .addSeparator() .addSubMenu(ui.createMenu('Kalendarz') .addItem('Skanuj kalendarz TERAZ', 'manualCalendarScan') .addItem('Pokaż najbliższe konsultacje', 'showUpcomingConsultations')) .addSubMenu(ui.createMenu('Email & GetResponse') .addItem('Test GetResponse', 'testGetResponseAPI') .addItem('Test wysyłki email', 'promptTestEmail')) .addSubMenu(ui.createMenu('Administracja') .addItem('Konfiguruj system v3.2', 'setupOptimizedSystem') .addItem('Ręczna konserwacja', 'dailyMaintenance') .addItem('Dodaj klienta testowego', 'addTestClient') .addItem('Wyczyść WSZYSTKO', 'clearAllData')) .addSeparator() .addItem('Dokumentacja', 'showDocumentation') .addToUi(); }function showSystemStatus() { const status = checkSystemStatus(); const html = HtmlService.createHtmlOutput(`

System Status - v${status.version}

${status.triggers}/${status.maxTriggers}
Triggery
${status.queue.total}
W kolejce
${status.queue.pending}
Oczekujące
${status.queue.overdue}
Zaległe

Rekordy w arkuszach

ArkuszLiczba rekordów
Ebook${status.sheets.ebook}
Program${status.sheets.program}

Typy w kolejce

${Object.entries(status.queue.byType).map(([type, count]) => `` ).join('')}
TypLiczba
${type}${count}

Najbliższe konsultacje (24h)

${status.nextEvents.length > 0 ? ` ${status.nextEvents.map(e => `` ).join('')}
TytułCzasGoście
${e.title}${e.time}${e.guests.join(', ')}
` : '

Brak konsultacji w najbliższych 24h

' } `) .setTitle('Status Systemu') .setWidth(800) .setHeight(600); SpreadsheetApp.getUi().showModalDialog(html, 'Status Systemu CRM v3.2'); }function showUpcomingConsultations() { const consultations = getUpcomingConsultations(); const html = HtmlService.createHtmlOutput(`

Konsultacje w najbliższych 24h

${consultations.length > 0 ? ` ${consultations.map(c => `` ).join('')}
TytułCzasGoście
${c.title}${c.time.toLocaleString('pl-PL')}${c.guests.join('
')}
` : '

Brak konsultacji w najbliższych 24 godzinach

' } `) .setTitle('Najbliższe konsultacje') .setWidth(600) .setHeight(400); SpreadsheetApp.getUi().showModalDialog(html, 'Najbliższe konsultacje'); }function promptTestEmail() { const ui = SpreadsheetApp.getUi(); const response = ui.prompt( 'Test email', 'Podaj adres email do testu:', ui.ButtonSet.OK_CANCEL ); if (response.getSelectedButton() === ui.Button.OK) { const result = testEmailDelivery(response.getResponseText()); ui.alert('Wynik', result, ui.ButtonSet.OK); } }function showDocumentation() { const html = HtmlService.createHtmlOutput(`

Klinika Formy CRM v3.2 - Dokumentacja

NOWOŚCI W WERSJI 3.2:
  • Usunięte problematyczne emoji z wszystkich emaili
  • Dodana stopka ze zdjęciem do wszystkich wiadomości
  • Potwierdzenie zgłoszenia przez WhatsApp/Email
  • Poprawione linki do kalendarza
  • Rozbudowane emaile konsultacyjne
  • Poprawione treści (2 lata, 4 min wideo)
WAŻNE: System używa tylko 2 triggery zamiast 20+. Wszystkie emaile działają przez system kolejkowy.

Główne funkcje:

Automatyczne emaile (11 typów):
  • 4 emaile dla ebooka (natychmiast, 5h, 24h, 48h)
  • 7 emaili konsultacyjnych (przed i po spotkaniu)
Integracja z kalendarzem:
  • Skanowanie co godzinę
  • Automatyczne wykrywanie konsultacji
  • Słowa kluczowe: konsultacja, spotkanie, meet, zmiana, rozmowa, call
System STOP:

Wpisz "KUPIŁ" w kolumnie R aby zatrzymać emaile promocyjne

Sekwencja emaili follow-up:

  1. Email 1 (natychmiast): 4-min wideo + 3 bonusy + wartość 497 zł gratis
  2. Email 2 (po 5h): Przypomnienie + instrukcje konsultacji
  3. Email 3 (po 24h): Opinie klientów + dowody społeczne
  4. Email 4 (po 48h): Ostatnia szansa + wszystkie bonusy do pobrania

Potwierdzenie zgłoszenia:

Po wypełnieniu formularza program, klient otrzymuje email z prośbą o potwierdzenie przez:

  • WhatsApp: +48 733 705 478
  • Stronę: ${CONFIG.URLS.CONFIRMATION_PAGE}

Jak dodać klienta do konsultacji?

  1. Klient musi najpierw wypełnić formularz (być w arkuszu)
  2. Dodaj wydarzenie w kalendarzu z słowem "KONSULTACJA"
  3. Dodaj email klienta jako gościa
  4. System automatycznie wyśle 7 emaili

Ważne informacje o konsultacji:

  • Jeśli klient nie może uczestniczyć, musi dać znać min. 10h wcześniej
  • Link do Google Meet jest automatycznie pobierany z kalendarza
  • Kamerka NIE jest wymagana

Płatności:

  • Stripe: 1350 zł jednorazowo
  • TubaPay: 3 raty (533 zł) lub 6 rat (291 zł)

Bonusy dla klientów:

  • E-book "ZMIANA" (wartość 147 zł)
  • 7-dniowa dieta startowa (wartość 97 zł)
  • Grupa wsparcia na FB (wartość 67 zł/mies)
  • Bezpłatna konsultacja z planem 12-tyg (wartość 250 zł)

Monitoring:

Użyj checkSystemStatus() aby sprawdzić stan systemu

Rozwiązywanie problemów:

  • Emaile nie wysyłają się: Kliknij "Przetwórz kolejkę TERAZ"
  • Konsultacja nie wykryta: Sprawdź czy klient jest w arkuszu
  • Za dużo triggerów: System automatycznie zarządza triggerami

Kontakt wsparcia:

Email: owsiany1995@gmail.com

WhatsApp: +48 733 705 478

`) .setTitle('Dokumentacja') .setWidth(700) .setHeight(600); SpreadsheetApp.getUi().showModalDialog(html, 'Dokumentacja CRM v3.2'); }// ==================== FUNKCJE TESTOWE ==================== function testGetResponseAPI() { const url = 'https://api.getresponse.com/v3/campaigns'; const options = { 'method': 'get', 'contentType': 'application/json', 'headers': { 'X-Auth-Token': 'api-key ' + CONFIG.GETRESPONSE_API_KEY }, 'muteHttpExceptions': true }; try { const response = UrlFetchApp.fetch(url, options); Logger.log('[GetResponse Test] Kod: ' + response.getResponseCode()); if (response.getResponseCode() === 200) { const campaigns = JSON.parse(response.getContentText()); Logger.log('[GetResponse Test] Znaleziono kampanii: ' + campaigns.length); // Test dodania kontaktu const testEmail = "test_" + new Date().getTime() + "@example.com"; const testResult = addContactToGetResponse("Test", testEmail); Logger.log('[GetResponse Test] Dodanie testowego kontaktu: ' + testResult); return "GetResponse działa poprawnie"; } else { return "Błąd GetResponse: " + response.getResponseCode(); } } catch (error) { Logger.log('[GetResponse Test] Błąd: ' + error.toString()); return "Błąd testu: " + error.toString(); } }// ==================== INICJALIZACJA ==================== function init() { Logger.log('[Init] Inicjalizacja systemu CRM v3.2...'); // Sprawdź czy arkusze istnieją const spreadsheet = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID); const ebookSheet = spreadsheet.getSheetByName(CONFIG.EBOOK_SHEET_NAME); const programSheet = spreadsheet.getSheetByName(CONFIG.PROGRAM_SHEET_NAME); if (!ebookSheet || !programSheet) { throw new Error('Brak wymaganych arkuszy! Sprawdź nazwy arkuszy.'); } // Ustaw system const result = setupOptimizedSystem(); Logger.log('[Init] System gotowy do pracy!'); return result; }