Автобэкап Emotico в Google Диск и Таблицу
Настраивается один раз, около 5 минут. После этого записи сами попадают в твой Google Диск: полная копия для восстановления и таблица для чтения. Удобнее делать с компьютера.
2Назови его
Нажми на «Проект без названия» вверху и введи имя, например emotico-backup.

3Вставь код
Скопируй код кнопкой ниже. В редакторе выдели всё, что там есть (Ctrl+A), вставь код (Ctrl+V) и сохрани (Ctrl+S).
const FILE_NAME = "emotico-backup.json";
const SHEET_NAME = "Emotico - записи";
// Колонка и её ширина. Порядок блоков разбора повторяет экран записи
const COLS = [["Дата", 90], ["Время", 60], ["Эмоция", 140], ["Сила", 55], ["Дополнительные", 190],
["Событие", 200], ["Обстоятельства", 200], ["Мысли", 260], ["Тело", 190],
["Реакция", 190], ["Нужно", 190], ["Помогает", 200]];
const BLOCK_KEYS = ["event", "situation", "rumination", "body", "doing", "need", "shift"];
function doPost(e) {
const data = e && e.postData && e.postData.contents;
if (!data) return out({ ok: false, error: "empty" });
// Принимаем только то, что похоже на копию Emotico - случайный мусор в Диск не пишем
let j;
try {
j = JSON.parse(data);
if (j.v !== 1) throw new Error("not emotico");
} catch (err) {
return out({ ok: false, error: "bad payload" });
}
// Две отправки подряд не должны писать таблицу одновременно
const lock = LockService.getScriptLock();
lock.waitLock(20000);
let sheet = "ok";
try {
const files = DriveApp.getFilesByName(FILE_NAME);
if (files.hasNext()) files.next().setContent(data);
else DriveApp.createFile(FILE_NAME, data, "application/json");
// Таблица - удобство для чтения, а не копия: её сбой не должен ронять бэкап
try { writeSheet(j.entries || []); } catch (err) { sheet = "error: " + err; }
} finally {
lock.releaseLock();
}
return out({ ok: true, at: new Date().toISOString(), sheet: sheet });
}
// Таблица - зеркало записей: каждый раз переписывается целиком, поэтому правки
// и удаления в приложении доезжают сами. Настройки приложения сюда не попадают
function writeSheet(entries) {
const sh = getSheet();
const bits = (en, key) => {
const v = en.r && en.r[key];
if (!v) return "";
return (v.chips || []).concat(v.text ? [v.text] : []).join(", ");
};
const rows = entries.map(en => {
const em = en.emotions || [];
const t = new Date(en.time);
return [t, t,
em.length ? em[0].word : "только событие",
em.length ? em[0].level : "",
em.slice(1).map(x => x.word + " " + x.level + "/10").join(" + ")
].concat(BLOCK_KEYS.map(k => bits(en, k)));
});
const n = COLS.length;
sh.clearContents();
sh.getRange(1, 1, 1, n).setValues([COLS.map(c => c[0])]);
if (rows.length) {
sh.getRange(2, 1, rows.length, n).setValues(rows);
sh.getRange(2, 1, rows.length, 1).setNumberFormat("dd.mm.yyyy");
sh.getRange(2, 2, rows.length, 1).setNumberFormat("hh:mm");
sh.getRange(2, 1, rows.length, n).setWrap(true).setVerticalAlignment("top");
sh.getRange(2, 4, rows.length, 1).setHorizontalAlignment("center");
}
}
function getSheet() {
const props = PropertiesService.getScriptProperties();
const id = props.getProperty("SHEET_ID");
if (id) {
try {
if (!DriveApp.getFileById(id).isTrashed()) return SpreadsheetApp.openById(id).getSheets()[0];
} catch (err) {}
}
// Первая отправка (или таблицу удалили) - создаём и оформляем один раз
const ss = SpreadsheetApp.create(SHEET_NAME);
props.setProperty("SHEET_ID", ss.getId());
const sh = ss.getSheets()[0];
const n = COLS.length;
sh.setName("Записи");
sh.setFrozenRows(1);
sh.getRange(1, 1, 1, n).setFontWeight("bold").setBackground("#2F2A3A").setFontColor("#FFFFFF")
.setVerticalAlignment("middle");
sh.setRowHeight(1, 32);
COLS.forEach((c, i) => sh.setColumnWidth(i + 1, c[1]));
sh.getRange(2, 1, sh.getMaxRows() - 1, n).applyRowBanding(SpreadsheetApp.BandingTheme.LIGHT_GREY, false, false);
// Сила 1-10 подсвечена шкалой: динамику видно без графика
const power = sh.getRange(2, 4, sh.getMaxRows() - 1, 1);
sh.setConditionalFormatRules([SpreadsheetApp.newConditionalFormatRule()
.setGradientMinpointWithValue("#FFFFFF", SpreadsheetApp.InterpolationType.NUMBER, "1")
.setGradientMaxpointWithValue("#F2A65A", SpreadsheetApp.InterpolationType.NUMBER, "10")
.setRanges([power]).build()]);
return sh;
}
// Служебная: пересобрать таблицу из последней копии в Диске. Запускается вручную из редактора
// (кнопка «Выполнить») - например, если таблицу удалили и не хочется ждать отправки с телефона
function rebuildFromBackup() {
const files = DriveApp.getFilesByName(FILE_NAME);
if (!files.hasNext()) throw new Error("нет файла " + FILE_NAME);
const j = JSON.parse(files.next().getBlob().getDataAsString());
writeSheet(j.entries || []);
Logger.log("строк: " + (j.entries || []).length);
}
function out(obj) {
return ContentService.createTextOutput(JSON.stringify(obj))
.setMimeType(ContentService.MimeType.JSON);
}

4Начни развёртывание
Справа вверху нажми Начать развертывание → Новое развертывание.

5Выбери тип «Веб-приложение»
Нажми на шестерёнку рядом с «Выберите тип» и выбери Веб-приложение.

6Настрой доступ
- Запуск от имени - «От моего имени»
- У кого есть доступ - «Все»
«Все» нужно, потому что приложение обращается к скрипту без входа в Google. Защитой служит сам адрес скрипта: его знаешь только ты. Затем нажми Начать развертывание.

7Выдай разрешения
Нажми Предоставить доступ и выбери свой аккаунт.

Google покажет предупреждение «Приложение не проверено». Это ожидаемо: скрипт твой собственный, Google его не проверял. Нажми Дополнительные настройки → Перейти на страницу emotico-backup → Разрешить.
Скрипт просит доступ к Диску и Таблицам - чтобы создать файл копии и таблицу записей.
8Скопируй URL веб-приложения
Под надписью «Веб-приложение» будет адрес вида https://script.google.com/macros/s/…/exec. Скопируй его кнопкой под ним.

9Вставь URL в Emotico
На телефоне открой Emotico → Больше → Резервное копирование. Вставь адрес в поле «Автобэкап в Google Диск» и нажми Сохранить. Статус сменится на «Последняя копия: …».

✓Что появится в Диске
- emotico-backup.json - полная копия для «Восстановить из копии». Диск хранит её прошлые версии.
- Emotico - записи - таблица: одна запись на строку, новые сверху.
| Дата | Время | Эмоция | Сила | Дополнительные | Событие | Обстоятельства | Мысли |
|---|---|---|---|---|---|---|---|
| 08.10.2026 | 13:52 | спокойствие | 5 | общение | дом | ||
| 07.10.2026 | 18:10 | раздражение | 7 | усталость 4/10 | дедлайн | работа | не успеваю |
Пример. В настоящей таблице есть ещё колонки «Тело», «Реакция», «Нужно», «Помогает».
Копия отправляется, пока приложение открыто: при запуске и после изменений, не чаще раза в минуту. Таблица каждый раз переписывается целиком, поэтому свои пометки веди на соседнем листе.
Выключить: сохрани в приложении пустое поле URL.
