Новости ChatGPT

n8n — масштабируем получение SMS и уведомлений с нескольких (десятков) SIM-карт одновременно

TL;DR Автор в прошлой статье настроил Telegram-чат, куда несколько смартфонов скидывают пуши с помощью MacroDroid и/или Tasker. Проблема в том, что смартфоны брали на себя слишком много работы. Что, если они будут тонкими клиентами, которые шлют сырые данные на сервер, где уже происходит вся обработка и рассылка? Автор делится workflow и конфигурацией для n8n, которые позволяют это реализовать в режиме "Быстрого старта".

Дисклеймеры

Общий дисклеймерО личности автораОтказ от ответственностиОб использовании ChatGPT

Аннотация

В статье рассматривается вопрос более гибкого и масштабируемого решения получения уведомлений с нескольких SIM-карт - за счёт некоего центрального сервера, в данном случае n8n. За счёт этого у администратора n8n-сервера появляется возможность полностью динамически настраивать всю бизнес-логику: от обработки поступающих данных и форматирования до рассылки уведомлений в Telegram (и не только). Рассказывается также об ��стории создания проекта, способах создания конфигурации для него и настройке не только сервера, но и конечных устройств. В данный момент автор использует данный сетап. Рассмотрены вопросы, проблемы и процесс создания всего конвейера - от MacroDroid/Tasker до n8n.

Введение

Предположим, что вы смогли-таки реализовать сетап с MacroDroid/Tasker из прошлой статьи. Или вы просто столкнулись с необходимостью масштабировать получение SMS и уведомлений с нескольких десятков SIM-карт одновременно. И, вдобавок, нужно пересылать эти уведомления в Telegram, причём не только себе. Вдобавок, периодически. Вдобавок, срочно, желательно в ту же секунду. Вручную подобным мазохизмом заниматься не хочется, да и не получится. А вот автоматизировать - можно, да и, положа руку на сердце - придётся.

В качестве затравки может прочитать предыдущую статью, где описаны подробные критерии выбора смартфонов и принцип работы с MacroDroid/Tasker. Здесь основной упор будет выполнен на централизацию существующего решения.

Почему n8n?

Рассматривал следующее:

  1. Написать код на TypeScript (NestJS), упаковать и запустить на сервере, который будет принимать HTTP-запросы от смартфонов и обрабатывать их;

  2. Индустриальный стек мониторинга и алертинга: Prometheus + AlertManager + Grafana + какой-нибудь экспортер SMS/уведомлений. Всё это дело у меня уже имеется, осталось только написать экспортеры или (что более вероятно) HTTP-запросы для PushGateway или VictoriaLogs или Vector;

  3. Третий вариант подкрался неожиданно - ребята раздавали экземпляры n8n бесплатно. Ну, что ж, а раз дело дошло до бесплатного, то почему бы и не попробовать?

n8n - это low-code платформа для автоматизации рабочих процессов. Она позволяет создавать сложные интеграции и автоматизации с минимальным количеством кода, используя визуальный интерфейс. В моём случае это было именно то, что нужно: быстрое создание рабочих процессов для обработки входящих данных и отправки уведомлений.

Что ж, пишем воркфлоу на нём, благо целый парк SIM-карт и телефонов у меня уже имеется.

А как получилось настроить смартфоны?

Кратко: на каждый смартфон поставил MacroDroid или Tasker с плагином AutoNotification, настроил группу с Telegram-ботом, созданным в @BotFather.

Какой конфиг придумал?

Концепция

Принцип работы следующий, подсмотренный у ядра XRay:

Есть единый конфиг, полностью и почти что в императивном стиле описывающий, что принимать, как обрабатывать, в каком виде рендерить и куда слать. Входящий webhook принимает HTTP-запросы от смартфонов (MacroDroid/Tasker), затем просто происходит цепочка маппингов и темплейтингов, заигрывания с сырыми данными и по конвейеру выдающему последней ноде список адресов и payload, куда и как слать запросы.

Будет несколько сущностей:

  • Recipients - получатели уведомлений (например, Telegram userId или chatId). Содержит метаданные о получателе, на��ример, разрешённые deviceId (чтобы не слали посторонние смартфоны), предпочтительный язык, дополнительные плейсхолдеры и т.д. Также содержит секреты для API (например, Telegram Bot token) и, самое главное - объект rules. С помощью него можно очень гибко настраивать, какие именно уведомления и при каких условиях этот получатель хочет нужные данные. Например, только SMS с OTP-кодами, только от определённых номеров, только с определёнными ключевыми словами и т.д. Recipients связывает Templates и Endpoints;

  • Templates - шаблоны сообщений, которые будут рендериться с помощью плейсхолдеров. Например, шаблон для SMS с OTP-кодом может выглядеть так: "Получено SMS от {data.fromPhoneNumber}: {data.body}. Код: {data.otpCodes[0]}". Шаблоны могут быть разными для разных типов уведомлений (SMS, push-уведомления и т.д.);

  • Endpoints - конечные точки, куда будут отправляться уведомления. Например, для Telegram это будет URL вида https://api.telegram.org/bot{recipient.botToken}/sendMessage, с параметрами chat_id={recipient.chatId} и text={%text%}. Можно также добавить поддержку других платформ, например, Slack, Email и т.д.;

Собственно, всё. Этот конфиг задаёт всё поведение системы от начала и до конца. Но он будет не самым простым, и вам придётся его создать самостоятельно, пока я не написал веб-конструктов для него.

Проектирование модели

Как истинный ООПшник (кстати, что такое ООП?), решил я начать с проектирования. В моём случае я описал модельки полезной нагрузки (payload) для входящих HTTP-запросов от смартфонов:

Например, SMS-сообщение
export interface SmsReceivedDataModel {
/** Discriminator. */
type: "sms_received";

/** Sender address/number (may be alphanumeric in some regions). */
fromName?: string;
fromNameInContacts?: string;
fromPhoneNumber?: string;

/** Message body text. */
body: string;

/** ISO 8601 timestamp when SMS was received (device time). */
at?: string;

/** SIM slot index if known (1/2). */
simSlot?: number;

/** Subscription/carrier name if available. */
carrier?: string;

/** Message center address if captured. */
serviceCenter?: string;

/** True if message looks like OTP (if your automation tags it). */
isOtp?: boolean;

/** Extracted OTP code(s) if automation parses it. */
otpCodes?: string[];

/** Optional thread id / conversation id from SMS provider. */
threadId?: string;

/** Optional app/package that captured the SMS (some automations rely on notifications). */
capturedByPackage?: string;

/** Optional keyword tags computed by automation app. */
tags?: string[];
}
Объяснить код с
Конфиг для n8n workflow
/**
* Core configuration for an n8n workflow that:
* - receives "data" from an automation app via webhook
* - selects recipients & templates
* - renders messages (templating)
* - sends to external HTTP endpoints (e.g., Telegram Bot API)
*
* All top-level entities are objects (maps) keyed by id to match your preference
* (objects over arrays) and enable stable lookups in n8n.
*/
export interface ConfigModel {
/** Schema version for migrations and backward compatibility. */
version: string;
tokens: Record<string, string>[];

/** Optional human label for the configuration instance. */
name?: string;

/**
* A map of recipients by `id`.
*
* Key MUST match `Recipient.id`.
*/
recipients: Record<RecipientId, RecipientModel>;

/**
* A map of endpoints by `id`.
*
* Endpoints may reference `{recipient.*}` placeholders.
*/
endpoints: Record<EndpointId, EndpointModel>;

/**
* A map of templates by `id`.
*
* Templates may reference placeholders from data/recipient/meta.
*/
templates: Record<TemplateId, TemplateModel>;

/**
* Optional device registry. If omitted, "device checks" can rely solely on deviceId
* strings embedded in incoming payloads and recipients.allowedDeviceIds.
*/
devices?: Record<DeviceId, DeviceDefinition>;

/**
* Optional global defaults applied by n8n when building outbound requests.
* Per-endpoint settings override these defaults.
*/
defaults?: {
/** Default timeout in milliseconds for outbound HTTP calls. */
httpTimeoutMs?: number;

/** Default headers applied to outbound HTTP calls unless overridden. */
headers?: Record<string, string>;

/**
* If true, the system should reject any placeholder that cannot be resolved
* (fail-fast). If false, unresolved placeholders may be left as-is or replaced
* with empty string depending on runtime policy.
*/
strictTemplating?: boolean;
};
}


/**
* Core configuration for an n8n workflow that:
* - receives "data" from an automation app via webhook
* - selects recipients & templates
* - renders messages (templating)
* - sends to external HTTP endpoints (e.g., Telegram Bot API)
*
* All top-level entities are objects (maps) keyed by id to match your preference
* (objects over arrays) and enable stable lookups in n8n.
*/
export interface ConfigModel {
/** Schema version for migrations and backward compatibility. */
version: string;
tokens: Record<string, string>[];

/** Optional human label for the configuration instance. */
name?: string;

/**
* A map of recipients by `id`.
*
* Key MUST match `Recipient.id`.
*/
recipients: Record<RecipientId, RecipientModel>;

/**
* A map of endpoints by `id`.
*
* Endpoints may reference `{recipient.*}` placeholders.
*/
endpoints: Record<EndpointId, EndpointModel>;

/**
* A map of templates by `id`.
*
* Templates may reference placeholders from data/recipient/meta.
*/
templates: Record<TemplateId, TemplateModel>;

/**
* Optional device registry. If omitted, "device checks" can rely solely on deviceId
* strings embedded in incoming payloads and recipients.allowedDeviceIds.
*/
devices?: Record<DeviceId, DeviceDefinition>;

/**
* Optional global defaults applied by n8n when building outbound requests.
* Per-endpoint settings override these defaults.
*/
defaults?: {
/** Default timeout in milliseconds for outbound HTTP calls. */
httpTimeoutMs?: number;

/** Default headers applied to outbound HTTP calls unless overridden. */
headers?: Record<string, string>;

/**
* If true, the system should reject any placeholder that cannot be resolved
* (fail-fast). If false, unresolved placeholders may be left as-is or replaced
* with empty string depending on runtime policy.
*/
strictTemplating?: boolean;
};
}

/* ----------------------------- IDs / Aliases ----------------------------- */

export type RecipientId = string;
export type EndpointId = string;
export type TemplateId = string;
export type DeviceId = string;

/* -------------------------------- Recipient ------------------------------ */

/**
* A "recipient" is a business-logic target that receives templated messages via one
* or more endpoints. Example: a Telegram user/channel/chat ID plus extra vars.
*/
export interface RecipientModel {
/** Optional display name for UI or logs. */
name?: string;
disabled?: boolean;

/**
* Recipient-scoped variables for templating, e.g.:
* { "tgChatId": "123456", "lang": "ru", "timezone": "Asia/Bangkok" }
*
* Use `{recipient.vars.tgChatId}` (or `{recipient.vars.lang}`) in templates/endpoints.
*/
vars: Record<string, string | number | boolean | null>;

/**
* Which incoming devices are allowed to trigger messages to this recipient.
* If omitted or empty, all devices are allowed (unless your runtime enforces otherwise).
*/
allowedDeviceIds?: DeviceId[];

/**
* Which endpoint(s) the recipient is eligible to send through.
* If omitted or empty, runtime may allow all endpoints or require explicit mapping
* depending on your policy.
*/
endpointIds?: EndpointId[];

/**
* Which template(s) may be used for this recipient.
* If omitted or empty, runtime may allow all templates or require explicit mapping
* depending on your policy.
*/
templateIds?: TemplateId[];

/**
* Optional filtering by data types for this recipient.
* If omitted, all data types are allowed.
*/
allowedDataTypes?: DataType[];

/**
* Optional per-recipient rendering preferences.
* Useful for future UI and consistent formatting.
*/
rendering?: {
/** Language preference for template selection, if you implement i18n. */
language?: string;

/** Timezone for formatting timestamps. */
timezone?: string;

/** If true, escape HTML (Telegram HTML mode) or perform endpoint-specific escaping. */
escapeMode?: "none" | "telegram_html" | "telegram_markdownv2";
};

/**
* Optional "routing rules" to select specific template(s) and/or endpoint(s)
* based on incoming data. This enables business logic in config instead of n8n code.
*/
rules: RecipientRuleModel[];
}

/**
* A rule evaluated in order; first match wins (typical policy).
*/
export interface RecipientRuleModel {
/** Unique id of the rule (for UI, debugging, and test referencing). */
id: string;

/** Optional human description. */
description?: string;
type: DataType;

/** Match conditions; all specified conditions must match (AND). */
when: FieldPredicate[];

/**
* Actions to apply when the rule matches.
* These override or constrain the recipient's default templateIds/endpointIds.
*/
then: {
/** Use these templates (in order). */
templateIds?: TemplateId[];

/** Send via these endpoints (in order). */
endpointIds?: EndpointId[];
};
}

/**
* A simple field predicate for config-driven routing.
*/
export interface FieldPredicate {
/** Path in the runtime object (you define actual resolver). */
path: string;

/** Comparison operator. */
operator:
| "equals"
| "not_equals"
| "includes"
| "not_includes"
| "starts_with"
| "ends_with"
| "matches_regex"
| "gt"
| "gte"
| "lt"
| "lte"
| "exists";

/** Comparison value; omitted for `exists`. */
value?: string | number | boolean | null;
}

/* -------------------------------- Endpoint -------------------------------- */

/**
* Defines how to send an HTTP request.
* URLs, headers, query, and body may include `{recipient.*}` placeholders.
*
* NOTE: Actual auth secrets should ideally NOT be embedded directly here unless
* encrypted or stored in n8n credentials; model allows it for completeness.
*/
export interface EndpointModel {
/** Optional display name for UI or logs. */
name?: string;

/** HTTP method. */
method: HttpMethod;

/**
* Fully qualified URL including path.
* May contain templated placeholders from recipient vars, e.g.:
* "https://api.telegram.org/bot{recipient.vars.botToken}/sendMessage"
*/
url: string;

/**
* Optional query string parameters.
* Values may include placeholders.
*/
searchParams?: Record<string, string>;

/**
* Optional headers.
* Values may include placeholders.
*/
headers?: Record<string, string>;

/**
* Optional request body settings.
* If not provided, runtime can send no body.
*/
body?: EndpointBody;

/**
* Timeout in milliseconds for this endpoint call.
* Overrides config.defaults.httpTimeoutMs.
*/
timeoutMs?: number;

/**
* Optional retry policy for transient failures.
*/
retry?: {
/** Maximum attempts including the first try. */
maxAttempts: number;
/** Backoff strategy. */
backoff: "fixed" | "exponential";
/** Base delay in milliseconds. */
baseDelayMs: number;
/** Only retry on these HTTP status codes, if specified. */
retryOnStatusCodes?: number[];
};

/**
* Optional expectation/validation on response for success determination.
* Useful for endpoints like Telegram that always return JSON with ok=true/false.
*/
successCriteria?: {
/** Treat these status codes as success. If omitted, use 200-299. */
statusCodes?: number[];
/**
* A JSON path to a boolean "success" field.
* Example for Telegram: "ok"
*/
jsonBooleanPath?: string;
};
}

export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";

export interface EndpointBody {
/**
* Body format.
* - json: typical REST payload
* - form_urlencoded: Telegram often supports application/x-www-form-urlencoded
* - raw: string payload (e.g., pre-rendered JSON or text)
*/
type: "json" | "form_urlencoded" | "raw";

/**
* Body content.
* - For json/form_urlencoded: object where leaf values may contain placeholders
* - For raw: a string which may contain placeholders
*/
content: Record<string, unknown> | string;
}

/* -------------------------------- Templates ------------------------------- */

/**
* A template is a named message payload with placeholders.
* You can keep it generic, or add endpoint-specific "rendering targets" later.
*/
export interface TemplateModel {
/** Optional human name for UI/logs. */
name?: string;

/**
* Main template string. Placeholders use `{...}` and are resolved at runtime.
*
* Recommended placeholder namespaces:
* - {data.*} incoming data payload fields
* - {recipient.*} recipient fields/vars
* - {meta.*} envelope info (deviceId, receivedAt, etc.)
*/
text: string;

/**
* Optional endpoint-specific formatting metadata.
* Example: Telegram sendMessage parse_mode.
*/
format?: {
/** Controls how the receiving endpoint should parse markup. */
parseMode?: "plain" | "telegram_html" | "telegram_markdownv2";

/** If true, strip unknown placeholders (otherwise fail or keep). */
ignoreUnknownPlaceholders?: boolean;
};

/**
* Optional additional fields that can be used as payload parts
* (e.g., title, subject, caption), depending on endpoint mapping.
*/
fields?: Record<string, string>;
}

/* --------------------------------- Devices -------------------------------- */

/**
* Optional device registry entry to enrich logs and enable per-device gating.
*/
export interface DeviceDefinition {
/** Optional friendly name. */
name?: string;

/** Optional platform description. */
platform?: "android" | "ios" | "other";

/** Optional additional metadata. */
vars?: Record<string, string | number | boolean | null>;
}

Объяснить код с

Да, вышло заморочно-таки, особенно со второй моделькой. Да, писалось нейронкой, но затем на 60% отредактировано мной.

Первая версия workflow для n8n

Первая версия писалась очень долго. Решил я тогда однозначно всё-таки вкатиться в вайб-кодинг, причём по уму: настроить проект в VS Code, nx monorepo (ибо будет несколько проектов), copilot-instructions.md/AGENTS.md, MCP, Skills и т. д.

Кто не шарит за вайб-код, MCP - это типа как API внешних инструментов для ИИ-агентов на основе нейросетей. В данном случае оно поможет нейронке правильно создавать и редактировать workflow у n8n. Для MCP с n8n есть классный проект n8n-mcp, его и подключаем для Windows через npm:

npm i n8n-mcp -g
Объяснить код с

В mcp.json прописываем (не забудьте подставить свои значения):

{
"mcp": {
"servers": {
"n8n-mcp": {
"command": "npx",
"args": ["n8n-mcp"],
"env": {
"N8N_API_URL": "http://localhost:5678",
"N8N_API_KEY": "your-api-key"
}
}
}
}
}
Объяснить код с

После этого можно создавать и редактировать workflow в n8n с помощью нейронки. Грузанул её контекстом, моделями, привёл аналогии, простые примеры, чего я хочу. Промпт бы потянул на тонкую книжку!

Но, к сожалению, нейросети, даже хвалёные GPT-5.2 и Gemini 3 Pro, создавали лишь что-то типа такого:

Не, ну так можно было и на моём любимом TypeScript написать. Вся бизнес-логика в одной ноде, ей Б-гу! Отхохотавшись вволю, решил писать сам по-старинке, без вайб-кодинга, но местами с помощью нейронки.

P. S. Надо отдать должное - всё завелось с первого раза, но смысл тогда был в n8n, если мне от него нужна была одна нода со всей бизнес-логикой? Так не пойдёт. Дальше пишем сами.

Вторая версия

Нейросети не пользовались фичами n8n, поэтому начал делить 9К строк кода на отдельные ноды, каждая из которых выполняет одну функцию:

Добавим ветвящуюся логику, поддержку GET- и POST-запросов:

Выделим внутреннюю проверку ошибок в If-ноды, будем выкидывать разные ошибки - от авторизации до валидации и маппинга.

Итоговый workflow получился таким:

И что оно умеет?

Теперь можно играться как угодно с маппингом данных, их содержимым, фильтровать входящие уведомления об СМС или пушах и отправлять как угодно в каком угодно виде. Всё сделано так, что вам здесь нужно изменить всего лишь одну ноду:

- и оно будет делать как описано.

Каждое устройство имеет свой токен-ключ, без них n8n не пропустит левые запросы. Для дебага с HTTP-клиента можно добавить токен с именем debug. Токены придумываете сами, сохраняете в конфиге n8n. Смартфоны обязаны использовать ключи в HTTP-заголовке x-n8n-device-relay.

Ключевую роль играет шаблонизация. Реализована она в виде таких строк: {data.body} или {device.id} - они позволяют ссылаться куда и как угодно. По сути, вы можете не соблюдать мои гайдлайны и практически весь конфиг переделать под свои модельки и слать с мобилок любые JSON'ки - бизнес-логика сама позаботится о правильном рендеринге значений, если вы всё сделали правильно.

Честно, не хотел изобретать такой велосипед с шаблонизацией, но я не нашёл возможности прикрутить, например, Jinja в n8n (особенно учитывая, что он не мой). Поэтому попросил нейронку написать простую шаблонизацию на jаvascript, который и использую в одной из нод. Работает вполне себе нормально - можно ссылаться на вложенные объекты, но, кажется, массивы оно не поддерживает.

Итоговый конфиг выглядит примерно так:

Пример конфига (минимальный)
{
"version": "1.0.0",
"tokens": [
{
"name": "myphone",
"key": "<THINK_OF_A_SECRET_KEY_YOURSELF>"
},
{
"name": "debug",
"key": "<THIS_TOKEN_WITH_DEBUG_NAME_BYPASSES_DEVICE_CHECK_USE_ONLY_FOR_TESTING>"
}
],
"recipients": {
"me": {
"vars": {
"botToken": "<INSERT_TELEGRAM_BOT_TOKEN>",
"tgChatId": "<INSERT_TELEGRAM_CHAT_ID>"
},
"rules": [
{
"id": "rule_sms_received",
"type": "sms_received",
"when": [
{
"path": "device.id",
"operator": "equals",
"value": "myphone"
}
],
"then": {
"endpointIds": [
"telegram_sendMessage_post"
],
"templateIds": [
"template_sms_1_ru"
]
}
},
{
"id": "rule_call_received",
"type": "call_received",
"when": [
{
"path": "device.id",
"operator": "equals",
"value": "myphone"
}
],
"then": {
"endpointIds": [
"telegram_sendMessage_post"
],
"templateIds": [
"template_call_1_ru"
]
}
},
{
"id": "rule_wifi_connected",
"type": "wifi_connected",
"when": [
{
"path": "device.id",
"operator": "equals",
"value": "myphone"
}
],
"then": {
"endpointIds": [
"telegram_sendMessage_post"
],
"templateIds": [
"template_wifi_1_ru"
]
}
},
{
"id": "rule_notification_received",
"type": "notification_received",
"when": [
{
"path": "device.id",
"operator": "equals",
"value": "myphone"
}
],
"then": {
"endpointIds": [
"telegram_sendMessage_post"
],
"templateIds": [
"template_app_1_ru"
]
}
},
{
"id": "rule_notification_received",
"type": "notification_received",
"when": [
{
"path": "device.id",
"operator": "equals",
"value": "myphone"
},
{
"path": "data.body",
"operator": "matches_regex",
"value": "(?:.+)?SOME STRING(?:.+)?"
}
],
"then": {
"endpointIds": [
"telegram_sendMessage_post"
],
"templateIds": [
"template_app_1_ru"
]
}
}
]
}
},
"endpoints": {
"telegram_sendMessage_get": {
"method": "GET",
"url": "https://api.telegram.org/bot{recipient.vars.botToken}/sendMessage",
"searchParams": {
"chat_id": "{recipient.vars.tgChatId}",
"text": "{%text%}"
}
},
"telegram_sendMessage_post": {
"method": "POST",
"url": "https://api.telegram.org/bot{recipient.vars.botToken}/sendMessage",
"body": {
"type": "json",
"content": {
"chat_id": "{recipient.vars.tgChatId}",
"text": "{%text%}"
}
}
}
},
"templates": {
"template_sms_1_ru": {
"text": "? Входящее смс от: {data.fromName} (номер телефона: {data.fromPhoneNumber})\n\n? Текст:\n{data.body}\n\nℹ️ Доп. данные:\n? Заряд: {device.batteryLevelPercent}%,\n? Имя SIM: {device.cellularCarrierName} (ID: {device.currentSimSlotIndex}),\n? Сеть Wi-Fi: {device.currentWifiSsid}"
},
"template_call_1_ru": {
"text": "? Входящий звонок от: {data.fromName} (номер телефона: {data.fromPhoneNumber})\n\nℹ️ Доп. данные:\n? Заряд: {device.batteryLevelPercent}%,\n? Имя SIM: {device.cellularCarrierName} (ID: {device.currentSimSlotIndex}),\n? Сеть Wi-Fi: {device.currentWifiSsid}"
},
"template_wifi_1_ru": {
"text": "? Сеть Wi-Fi подключена!\n\nℹ️ Доп. данные:\n? Заряд: {device.batteryLevelPercent}%"
},
"template_app_1_ru": {
"text": "? Уведомление от приложения: {data.appName} ({data.packageName})\n\n? Заголовок:\n{data.title}\n\n? Текст:\n{data.body}\n\nℹ️ Доп. данные:\n? Заряд: {device.batteryLevelPercent}%"
}
}
}
Объяснить код с

Как вам?) Давайте разберём его подробнее. Надеюсь, вы зацените результат.

Тяжёлое. Как написать такой конфиг?

Начинаем строить JSON'ку! Пишите:

{

}
Объяснить код с

- всё нижеперечисленное пихаем внутрь.

version

version - версия схемы конфига. Это не ваша версия, а служебное значение. Пока что 1.0.0. Конкретно сейчас роли не играет, но в будущем может понадобиться для миграций и обратной совместимости.

"version": "1.0.0",
Объяснить код с

tokens

tokens - список токенов-ключей для авторизации устройств. Каждый токен имеет имя и ключ. Каждый смартфон должен использовать свой ключ в заголовке x-n8n-device-relay. Для дебага предусмотрен токен с именем debug, который пропускает проверку устройства.

"tokens": [
{
"name": "myphone",
"key": "<THINK_OF_A_SECRET_KEY_YOURSELF>"
},
{
"name": "debug",
"key": "<THIS_TOKEN_WITH_DEBUG_NAME_BYPASSES_DEVICE_CHECK_USE_ONLY_FOR_TESTING>"
}
],
Объяснить код с

recipients

recipients - карта получателей уведомлений. Каждый получатель имеет свои переменные, правила и настройки. В примере есть получатель с id me, который хранит в себе переменные botToken и tgChatId для отправки им сообщений в Telegram.


"recipients": {
"me": {
"disabled": true,
"vars": {
"botToken": "<INSERT_TELEGRAM_BOT_TOKEN>",
"tgChatId": "<INSERT_TELEGRAM_CHAT_ID>"
},
"allowedDeviceIds": [
"myphone"
],
"endpointIds": [
"telegram_sendMessage_post"
],
"templateIds": [
"template_sms_1_ru",
"template_call_1_ru",
"template_wifi_1_ru",
"template_app_1_ru"
],
"rules": [
{
"id": "rule_sms_received",
"type": "sms_received",
"when": [
{
"path": "device.id",
"operator": "equals",
"value": "myphone"
}
],
"then": {
"endpointIds": [
"telegram_sendMessage_post"
],
"templateIds": [
"template_sms_1_ru"
]
}
}
]
}
},
Объяснить код с

Разберём его подробнее:

  • vars - переменные получателя, которые можно использовать в шаблонах templates и конечных точках endpoints. В данном случае это токен бота и ID чата Telegram. Вы можете добавить любые другие переменные по своему усмотрению и использовать их затем в шаблонах и эндпоинтах (будут описаны нижу);

  • rules - список правил для данного получателя. Каждое правило имеет:

    • Уникальный в пределах recipient'а id;

    • Тип данных type (например, sms_received, call_received, wifi_connected, notification_received);

    • Условия when. В условиях можно указать путь к полю, оператор (equals, not_equals, includes, not_includes, starts_with, ends_with, matches_regex, gt, gte, lt, lte, exists) и значение. Особенно полезен оператор matches_regex для фильтрации уведомлений по ключевым словам, в значении (value) вы указываете регулярное выражение без косых черт //и модификаторов типа gmi;

    • Действия then. В действиях указываются идентификаторы шаблонов и конечных точек, которые будут использоваться при совпадении условия;

    Важно! Внутри правила всё в массиве when работает по логике И (AND) - все условия должны быть выполнены для срабатывания правила.

  • Опционально можно добавить т. н. "белые списки" устройств (allowedDeviceIds), шаблонов (templateIds) и конечных точек (endpointIds) на уровне получателя, чтобы ограничить доступ. Они переопределяют rules. Например, если в rules есть конечная точка telegram_sendMessage_post, но allowedEndpointIds существует и этого эндпоинта там нет, то правило не вы��олнится;

  • Иногда клиент нужен, но не сейчас. Тогда ставим disabled: true, и recipient будет отключён. Поле опционально, поэтому можно убрать его вообще.

endpoints

endpoints - карта конечных точек для отправки уведомлений. В примере есть две конечные точки для отправки HTTP-запросов (пока что по дефолту - только Telegram, но можете добавить что угодно своё). Поддерживаются методы GET и POST.

"endpoints": {
"telegram_sendMessage_get": {
"method": "GET",
"url": "https://api.telegram.org/bot{recipient.vars.botToken}/sendMessage",
"searchParams": {
"chat_id": "{recipient.vars.tgChatId}",
"text": "{%text%}"
}
},
"telegram_sendMessage_post": {
"method": "POST",
"url": "https://api.telegram.org/bot{recipient.vars.botToken}/sendMessage",
"body": {
"type": "json",
"content": {
"chat_id": "{recipient.vars.tgChatId}",
"text": "{%text%}"
}
}
}
},
Объяснить код с

Сразу можно заметить, что в URL и параметрах используются плейсхолдеры вида {recipient.vars.botToken} и {%text%}. Первый - это переменная получателя, а второй - это текст, сгенерированный из шаблона. {%text%} - специальный "магический" плейсхолдер, смотрящий в подходящий шаблон (так как движок рендеринга значений смотрит лишь в data, а в templates у него вход заказан).

templates

templates - шаблоны (no shit Sherlock!) текста. В примере есть несколько шаблонов для разных типов уведомлений (SMS, звонки, подключение к Wi-Fi, уведомления приложений). Каждый шаблон имеет текст с плейсхолдерами для динамического заполнения.

"templates": {
"template_sms_1_ru": {
"text": "? Входящее смс от: {data.fromName} (номер телефона: {data.fromPhoneNumber})\n\n? Текст:\n{data.body}\n\nℹ️ Доп. данные:\n? Заряд: {device.batteryLevelPercent}%,\n? Имя SIM: {device.cellularCarrierName} (ID: {device.currentSimSlotIndex}),\n? Сеть Wi-Fi: {device.currentWifiSsid}"
},
"template_call_1_ru": {
"text": "? Входящий звонок от: {data.fromName} (номер телефона: {data.fromPhoneNumber})\n\nℹ️ Доп. данные:\n? Заряд: {device.batteryLevelPercent}%,\n? Имя SIM: {device.cellularCarrierName} (ID: {device.currentSimSlotIndex}),\n? Сеть Wi-Fi: {device.currentWifiSsid}"
},
"template_wifi_1_ru": {
"text": "? Сеть Wi-Fi подключена!\n\nСеть Wi-Fi: {device.currentWifiSsid}\nДанные о локации:\n- {device.locationPoint}\n- {device.locationLink}\n- Ссылка на последнее известное местоположение: {device.locationLastTimestamp}\n- Скорость аппарата: {device.currentSpeed} км/ч.\n\nℹ️ Доп. данные:\n? Заряд: {device.batteryLevelPercent}%"
},
"template_app_1_ru": {
"text": "? Уведомление от приложения: {data.appName} ({data.packageName})\n\n? Заголовок:\n{data.title}\n\n? Текст:\n{data.body}\n\nℹ️ Доп. данные:\n? Заряд: {device.batteryLevelPercent}%"
}
}
Объяснить код с

Тут уже довольно просто. Можете использовать переменные, самих шаблонов делаете сколько угодно. Используете их в recipient'ах, в объекте rules.

Как только конфиг будет готов, переходим к следующему шагу.

Я приготовил n8n, сделал конфиг - но это не workflow. Где его взять?

Всё сделано до вас: копипастите этот workflow (raw) в свой n8n, Ctrl + V в окне с новым workflow.

Видите этот участок:

- два раза там кликаете по ноде Config и вставляете свою JSON'ку.

С помощью HTTP-клиента и моей коллекции Postman можете протестировать работу workflow:

  • Выбираете нужный запрос в HTTP-клиенте;

  • В environment не забудьте вбить адрес вашего n8n-сервера и ключ устройства;

  • Во вкладке с n8n наведите на Webhook и жмите Execute workflow;

  • Жмёте Send в HTTP-клиенте;

  • Видите, что workflow отработал или нет, и почему.

И, если вздумается расширить логику, вы можете свободно можете менять содержимое нод, смотреть, как маппятся данные, что на входе и на выходе. Добавьте свои конечные точки после ноды Switch.

А как ср... слать данные?

Настраиваем смартфоны с MacroDroid или Tasker:

  1. Скачать макросы для MacroDroid или профили для Tasker (для последнего не забудьте установить плагин AutoNotification);

  2. Импортировать их в MacroDroid/Tasker на каждом смартфоне:

  • У MacroDroid это реализовано через главное меню - Импорт/Экспорт макросов - Импорт макросов;

  • У Tasker это реализовано через Профили. Удерживаете палец на вкладке Профили, выбираете Импорт;

  1. В настройках каждого макроса/профиля указать в header x-n8n-device-relay ключ устройства из вашего конфига n8n;

  2. В настройках каждого макроса/профиля указать URL вашего n8n-сервера (пример: http://your-n8n-domain.com/webhook/device-relay).

Тестируйте, проверяйте результат во вкладке n8n под названием executions.

Вы можете быстро сгенерировать MacroDroid макросы для каждого устройства скриптом PowerShell, для этого:

  • Клонируем репозиторий, в терминале переходим в его папку;

  • Копируем .\scripts\device-list.example.json или .\scripts\device-list-minimal.example.json с новым именем .\scripts\device-list.json:

Copy-Item .\scripts\device-list.example.json .\scripts\device-list.json
Объяснить код с

- редактируем его, добавляя свои устройства. Токены у устройств должны совпадать с токенами в конфиге n8n;

  • Пишем:

.\scripts\generate-macrodroid-macros-per-device.ps1
Объяснить код с
  • В папке .\scripts\devices\ появятся готовые макросы для MacroDroid, которые останется только импортировать в MacroDroid на каждом устройстве.

Оно работает! Это всё?

В общем-то, да. На самом деле, в модельке конфига много какие поля описаны, просто абсолютное большинство опциональны. Это всё потому, что те же приложения-автоматизаторы имеют очень слабо пересекающиеся множества данных, и привести их к общему знаменателю, единой модели, сложно. Как мог...

В будущем надо бы на опциональный bcrypt перейти и задуматься о поддержке iOS Shortcuts и Android Automate, но это уже другая история. Я лично счастлив с MacroDroid и Tasker.

Проблемы, вопросы и их решения

Проблема: Чому не самохостинг?

Решение: Я ждал этот вопрос. Знаете, это личное и, по-моему мнению, логичное решение, которое я описывал в статье "Чему учит постоянная релокация (или её ожидание) в контексте персональной инфраструктуры". Кратко говоря: когда ты на ходу и не нашёл свой дом, то лучше избегать самохостинга максимально. n8n в облаке или чей-то чужой n8n в данном случае - это просто в использовании. Хорошо, что оно подвернулось под руку.

Проблема: Ты палишь токены в открытом виде в конфиге! Шлёшь данные со смартфонов открытым текстом! Админ это видит, админ это сливает!

Решение: Ага. "А что ты мне сделаешь, я в другмо городе" ©. Это просто вопрос доверия к администратору n8n-сервера или надежде на принцип "неуловимого Джо". Админу с рутовыми правами ничего не мешает грепать логи n8n килограммами, или веб-сервер на verbose-логи настроить, или даже напрямую лезть в БД или var/log и кушать данные там, хех. Ладно, ладно! Давайте договоримся: в будущем планируется предварительная обработка данных bcrypt'ом - например, прямо на смартфоне (вряд ли), на своём сервере или на Cloudflare Worker'е. PR welcome.

Проблема: Так-то это можно реализовать на Vector/VictoriaLogs/PushGateway + Prometheus + AlertManager, зачем городить огород с n8n?

Решение: Я бы так и сделал). Правда, по моделькам и авторизации как у них - я не знаю. Вдобавок, я прям вижу, как вспухли бы правила алертинга у Прометея, там ведь фильтровать и маппить всё это надо. n8n мне попался чисто случайно, и я решил попробовать. Так-то подобное можно реализовать на любом бэкенде, было бы желание. У меня вышло неплохо, собой доволен.

Заключение

Таким образом, с помощью этой статьи вам удастся масштабировать получение SMS и уведомлений с парка смартфонов огромного размера (IMO до сотен или даже тысяч аппаратов), используя n8n в качестве центрального сервера для обработки и рассылки уведомлений. Вы сможете настроить гибкую бизнес-логику, шаблоны сообщений и конечные точки доставки, что позволит вам эффективно управлять большим количеством устройств и получать важные уведомления в нужное время и в нужном формате. Развитие системы планируется в сторону поддержки дополнительных платформ и улучшения безопасности передачи данных.