function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Mercuri')
.addItem('Open Sender', 'showMercuriSidebar')
.addToUi();
}
function showMercuriSidebar() {
const html = HtmlService.createHtmlOutputFromFile('Sidebar')
.setTitle('Mercuri WhatsApp Sender');
SpreadsheetApp.getUi().showSidebar(html);
}
function getSheetHeaders() {
const sheet = SpreadsheetApp.getActiveSheet();
const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
return headers;
}
function getSheetPreview() {
const sheet = SpreadsheetApp.getActiveSheet();
const data = sheet.getDataRange().getValues();
return {
headers: data[0],
rows: data.slice(1, Math.min(data.length, 6))
};
}
function sendMappedMessages(config) {
const apiKey = PropertiesService.getScriptProperties().getProperty('MERCURI_API_KEY');
if (!apiKey) throw new Error('Missing MERCURI_API_KEY in Script Properties');
const sheet = SpreadsheetApp.getActiveSheet();
const data = sheet.getDataRange().getValues();
const headers = data[0];
const headerMap = {};
headers.forEach((h, i) => headerMap[String(h).trim()] = i);
const results = [];
const selectedRows = config.selectedRows || [];
selectedRows.forEach(sheetRowNumber => {
const rowIndex = sheetRowNumber - 1;
const row = data[rowIndex];
if (!row) return;
const recipient = getCellByHeader(row, headerMap, config.mappings.recipient);
const country = config.mappings.country ? getCellByHeader(row, headerMap, config.mappings.country) : config.defaults.country;
const phoneNumberId = config.mappings.phoneNumberId
? getCellByHeader(row, headerMap, config.mappings.phoneNumberId)
: config.defaults.phoneNumberId;
const templateId = config.mappings.templateId
? getCellByHeader(row, headerMap, config.mappings.templateId)
: config.defaults.templateId;
const parameters = (config.parameterMappings || []).map(colName => {
return getCellByHeader(row, headerMap, colName);
});
const payload = {
phoneNumberId: String(phoneNumberId || ''),
channel: 'whatsapp',
recipient: String(recipient || ''),
message: {
type: 'template',
template: {
templateId: String(templateId || ''),
parameters: parameters
}
},
saveToInbox: true
};
if (country) payload.country = String(country);
try {
validatePayload(payload);
const response = UrlFetchApp.fetch('https://api.mercuri.cx/v1/send_message', {
method: 'post',
contentType: 'application/json',
headers: {
Authorization: 'Bearer ' + apiKey
},
payload: JSON.stringify(payload),
muteHttpExceptions: true
});
const code = response.getResponseCode();
const bodyText = response.getContentText();
let body = {};
try {
body = JSON.parse(bodyText);
} catch (e) {}
results.push({
rowNumber: sheetRowNumber,
httpCode: code,
success: code >= 200 && code < 300 && body.status === 'success',
messageId: body.messageId || '',
status: body.status || 'failed',
errorCode: body.errorCode || '',
error: body.errors ? JSON.stringify(body.errors) : bodyText
});
} catch (err) {
results.push({
rowNumber: sheetRowNumber,
httpCode: '',
success: false,
messageId: '',
status: 'failed',
errorCode: 'SCRIPT_ERROR',
error: err.message
});
}
});
writeResults(results, config.resultColumns || {});
return results;
}
function getCellByHeader(row, headerMap, headerName) {
const idx = headerMap[headerName];
return idx === undefined ? '' : row[idx];
}
function validatePayload(payload) {
if (!payload.phoneNumberId) throw new Error('Missing phoneNumberId');
if (!payload.recipient) throw new Error('Missing recipient');
if (!payload.message?.template?.templateId) throw new Error('Missing templateId');
if (!Array.isArray(payload.message.template.parameters)) {
throw new Error('Template parameters must be an array');
}
}
function writeResults(results, resultColumns) {
const sheet = SpreadsheetApp.getActiveSheet();
const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
const headerMap = {};
headers.forEach((h, i) => headerMap[String(h).trim()] = i + 1);
results.forEach(r => {
const row = r.rowNumber;
if (resultColumns.status && headerMap[resultColumns.status]) {
sheet.getRange(row, headerMap[resultColumns.status]).setValue(r.status);
}
if (resultColumns.messageId && headerMap[resultColumns.messageId]) {
sheet.getRange(row, headerMap[resultColumns.messageId]).setValue(r.messageId);
}
if (resultColumns.error && headerMap[resultColumns.error]) {
sheet.getRange(row, headerMap[resultColumns.error]).setValue(r.error);
}
});
}