const SPREADSHEET_URL = 'ПОСИЛАННЯ НА ВАШУ ТАБЛИЦЮ' — сюди буде надходити звіт
const KPI_COST_PER_CONVERSION = 1000 — вище цього числа буде вважатись відхиленням та формуватись у звіт. Можна оновлювати періодично за потреби на актуальну CPA
const DATE_RANGE = 'LAST_30_DAYS' — залишати, як є або прописати в такому форматі потрібний діапазон '2024-04-01,2024-04-30'
Як створити телеграм бота, знайти CHAT ID, BOT TOKEN, розповідав ТУТ
function main() {
const SPREADSHEET_URL = 'ПОСИЛАННЯ НА ВАШУ ТАБЛИЦЮ'; // замініть на свою таблицю
const KPI_COST_PER_CONVERSION = 1000; // вартість конверсії в грн,прописати число
const DATE_RANGE = 'LAST_30_DAYS'; // або '2024-04-01,2024-04-30'
const TELEGRAM_BOT_TOKEN = 'ВАШ ТЕЛЕГРАМ ТОКЕН';
const TELEGRAM_CHAT_ID = 'ВАШ ТЕЛЕГРАМ ЧАТ ID';
const currencyCode = AdsApp.currentAccount().getCurrencyCode();
const spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
analyzeKeywords(spreadsheet, KPI_COST_PER_CONVERSION, DATE_RANGE, currencyCode);
analyzeAds(spreadsheet, KPI_COST_PER_CONVERSION, DATE_RANGE, currencyCode);
analyzeDevices(spreadsheet, KPI_COST_PER_CONVERSION, DATE_RANGE, currencyCode);
analyzeTime(spreadsheet, KPI_COST_PER_CONVERSION, DATE_RANGE, currencyCode);
sendTelegramMessage(SPREADSHEET_URL, TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID);
function clearOrCreateSheet(spreadsheet, name, withTimeDevice = false) {
let sheet = spreadsheet.getSheetByName(name);
if (!sheet) {
sheet = spreadsheet.insertSheet(name);
} else {
sheet.clear({contentsOnly: true});
}
const headers = ['Тип', 'Назва/ID', 'Кампанія', 'Конверсії', 'Ціна/Конверсію', 'Витрати', 'Валюта', 'CTR (%)', 'Конверсія (%)', 'Кліки', 'Середня ціна кліка', 'Покази'];
if (withTimeDevice) {
headers.push('День', 'Година');
}
sheet.appendRow(headers);
return sheet;
}
function formatPercent(value) {
return (value * 100).toFixed(2).replace('.', ',') + '%';
}
function appendFormattedRow(sheet, data, numericColumns) {
const rowIndex = sheet.getLastRow() + 1;
const cleanedData = data.map((val, i) => {
if (numericColumns.includes(i + 1)) {
const cleanedVal = val.toString().replace(/\\s/g, '').replace(',', '.');
const num = parseFloat(cleanedVal);
return isFinite(num) && num >= 0 ? parseFloat(num.toFixed(2).replace('.', ',')) : '0,00';
}
return val;
});
sheet.appendRow(cleanedData);
}
function analyzeKeywords(spreadsheet, kpi, dateRange, currencyCode) {
const sheet = clearOrCreateSheet(spreadsheet, 'Ключові слова');
const report = AdsApp.keywords()
.withCondition("Status = ENABLED")
.withCondition("Impressions > 0")
.withCondition("Clicks > 0")
.forDateRange(dateRange)
.get();
while (report.hasNext()) {
const kw = report.next();
const stats = kw.getStatsFor(dateRange);
const cost = stats.getCost();
const conversions = Math.ceil(stats.getConversions()); // Округлюємо в більшу сторону
const clicks = stats.getClicks();
const impressions = stats.getImpressions();
const costPerConv = conversions > 0 ? cost / conversions : null;
const avgCpc = clicks > 0 ? cost / clicks : 0;
if (conversions === 0 || (costPerConv && costPerConv > kpi)) {
appendFormattedRow(sheet, [
'Ключове слово',
kw.getText(),
kw.getCampaign().getName(),
conversions,
costPerConv ? costPerConv.toFixed(2).replace('.', ',') : 'Немає',
cost.toFixed(2).replace('.', ','),
currencyCode,
formatPercent(stats.getCtr()),
clicks > 0 ? formatPercent(conversions / clicks) : '0%',
clicks,
avgCpc.toFixed(2).replace('.', ','),
impressions
], [4, 5, 6, 10, 12]);
}
}
}
function analyzeAds(spreadsheet, kpi, dateRange, currencyCode) {
const sheet = clearOrCreateSheet(spreadsheet, 'Оголошення');
const report = AdsApp.ads()
.withCondition("Status = ENABLED")
.withCondition("Impressions > 0")
.withCondition("Clicks > 0")
.forDateRange(dateRange)
.get();
while (report.hasNext()) {
const ad = report.next();
const stats = ad.getStatsFor(dateRange);
const cost = stats.getCost();
const conversions = Math.ceil(stats.getConversions()); // Округлюємо в більшу сторону
const clicks = stats.getClicks();
const impressions = stats.getImpressions();
const costPerConv = conversions > 0 ? cost / conversions : null;
const avgCpc = clicks > 0 ? cost / clicks : 0;
if (conversions === 0 || (costPerConv && costPerConv > kpi)) {
appendFormattedRow(sheet, [
'Оголошення',
ad.getId(),
ad.getCampaign().getName(),
conversions,
costPerConv ? costPerConv.toFixed(2).replace('.', ',') : 'Немає',
cost.toFixed(2).replace('.', ','),
currencyCode,
formatPercent(stats.getCtr()),
clicks > 0 ? formatPercent(conversions / clicks) : '0%',
clicks,
avgCpc.toFixed(2).replace('.', ','),
impressions
], [4, 5, 6, 10, 12]);
}
}
}
function analyzeDevices(spreadsheet, kpi, dateRange, currencyCode) {
const sheet = clearOrCreateSheet(spreadsheet, 'Пристрої');
const report = AdsApp.report(
`SELECT Device, CampaignName, Conversions, Cost, Clicks, Impressions
FROM CAMPAIGN_PERFORMANCE_REPORT
WHERE Impressions > 0 AND Clicks > 0
DURING ${dateRange}`
);
const rows = report.rows();
while (rows.hasNext()) {
const row = rows.next();
const conv = Math.ceil(parseFloat(row['Conversions'])); // Округлюємо в більшу сторону
const cost = parseFloat(row['Cost']);
const clicks = parseFloat(row['Clicks']);
const impressions = parseFloat(row['Impressions']);
const costPerConv = conv > 0 ? cost / conv : null;
const avgCpc = clicks > 0 ? cost / clicks : 0;
if (conv === 0 || (costPerConv && costPerConv > kpi)) {
appendFormattedRow(sheet, [
'Пристрій',
row['Device'],
row['CampaignName'],
conv,
costPerConv ? costPerConv.toFixed(2).replace('.', ',') : 'Немає',
cost.toFixed(2).replace('.', ','),
currencyCode,
impressions > 0 ? formatPercent(clicks / impressions) : '0%',
clicks > 0 ? formatPercent(conv / clicks) : '0%',
clicks,
avgCpc.toFixed(2).replace('.', ','),
impressions
], [4, 5, 6, 10, 12]);
}
}
}
function analyzeTime(spreadsheet, kpi, dateRange, currencyCode) {
const sheet = clearOrCreateSheet(spreadsheet, 'Час/День', true);
const report = AdsApp.report(
`SELECT CampaignName, DayOfWeek, HourOfDay, Conversions, Cost, Clicks, Impressions
FROM CAMPAIGN_PERFORMANCE_REPORT
WHERE Impressions > 0 AND Clicks > 0
DURING ${dateRange}`
);
const rows = report.rows();
while (rows.hasNext()) {
const row = rows.next();
const conv = Math.ceil(parseFloat(row['Conversions'])); // Округлюємо в більшу сторону
const cost = parseFloat(row['Cost']);
const clicks = parseFloat(row['Clicks']);
const impressions = parseFloat(row['Impressions']);
const costPerConv = conv > 0 ? cost / conv : null;
const avgCpc = clicks > 0 ? cost / clicks : 0;
if (conv === 0 || (costPerConv && costPerConv > kpi)) {
appendFormattedRow(sheet, [
'Година/День',
'',
row['CampaignName'],
conv,
costPerConv ? costPerConv.toFixed(2).replace('.', ',') : 'Немає',
cost.toFixed(2).replace('.', ','),
currencyCode,
impressions > 0 ? formatPercent(clicks / impressions) : '0%',
clicks > 0 ? formatPercent(conv / clicks) : '0%',
clicks,
avgCpc.toFixed(2).replace('.', ','),
impressions,
row['DayOfWeek'],
row['HourOfDay']
], [4, 5, 6, 10, 12]);
}
}
}
function sendTelegramMessage(sheetUrl, botToken, chatId) {
const message = encodeURIComponent(`✅ Ваш звіт моніторингу CPA Google Ads готовий:\\n${sheetUrl}`);
const url = `https://api.telegram.org/bot${botToken}/sendMessage?chat_id=${chatId}&text=${message}`;
UrlFetchApp.fetch(url);
}
}