Alert Simulation: Scheduled Telegram Alerts
Alert simulation for testing a monitoring pipeline — generate randomized-severity alerts on a cron schedule and deliver them to a Telegram chat through the Bot API, exercising the pipeline without a real incident.
A critical alert that never reaches the on-call phone, a dashboard tile that miscounts what arrived, an escalation that never fires on an unacknowledged warning — each of these bugs waits for a real incident to surface, and surfaces at the cost of one. Firing realistic alerts on a schedule surfaces them on demand instead, exercising every piece downstream of the trigger: the chat, the paging rotation, the dashboard count, the escalation path.
The generator below fires exactly those alerts — randomized severity, service, and metric value, on a cron schedule — and delivers each over HTTP to a live Telegram chat through the Bot API. A miniature alerting pipeline in a few files, hitting a real notification channel instead of a mock.
Alert simulation and the pipeline it exercises
A detection rule that matches, a threshold that trips, or a health check that fails all produce the same next step: an alert, structured no differently from any other event Eventum generates — a severity, a service, a message, a timestamp. Testing that a rule fires correctly says nothing about what happens after — the notification routing, on-call paging, dashboard counting, and escalation the opening paragraph lists, everything downstream of the alert itself. A real incident exercises that whole chain eventually — at whatever severity and service it happens to produce.
Firing realistic alerts on a schedule exercises the same chain on demand, at whatever severity mix and service spread a test needs, instead of waiting on a rule to match or an incident to happen. Telegram is one concrete chat to land these in — nothing below depends on Telegram past the request body and the endpoint URL, and the same pipeline reaches Slack, Discord, or PagerDuty just as directly.
What you'll build
The generator uses:
- cron input — fires every 2 minutes.
- all picking mode — renders the one alert template on every tick, using conditional Jinja2 logic to vary message format and value range by severity.
- HTTP output — posts each rendered alert to the Telegram Bot API as its own request. See Send test data to an API endpoint for the delivery mechanics behind it.
- Secrets — bot token stored securely in the keyring.
- Parameters — chat ID passed through
startup.yml.
Prerequisites
- Eventum installed
- A Telegram bot token (create one via @BotFather)
- The chat ID of the target chat (send a message to your bot, then query
https://api.telegram.org/bot<TOKEN>/getUpdatesto find it)
Project structure
Build it
Create the project directory
mkdir -p eventum/generators/alerts/templates
cd eventumWrite the alert template
The template produces a JSON body for the Telegram sendMessage API. It uses module.rand to pick a random severity and service, then builds a Markdown-formatted message with an emoji indicator.
{% set severity = module.rand.weighted_choice(["info", "warning", "critical", "resolved"], [0.4, 0.35, 0.2, 0.05]) %}
{% set service = module.rand.choice(["api-gateway", "auth-service", "db-primary", "cache-redis", "worker-queue", "nginx-lb"]) %}
{% set icons = {"info": "ℹ️", "warning": "⚠️", "critical": "🔴", "resolved": "✅"} %}
{% set metric = module.rand.choice(["CPU usage", "memory usage", "request latency", "error rate", "disk I/O", "connection pool"]) %}
{% if severity == "critical" %}
{% set value = module.rand.number.integer(90, 100) %}
{% elif severity == "warning" %}
{% set value = module.rand.number.integer(70, 89) %}
{% else %}
{% set value = module.rand.number.integer(10, 69) %}
{% endif %}
{
"chat_id": {{ params.chat_id }},
"parse_mode": "Markdown",
"text": "{{ icons[severity] }} *{{ severity | upper }}* | `{{ service }}`\n\n{{ metric }}: *{{ value }}%*\nHost: `{{ module.faker.locale.en.hostname() }}`\nTime: {{ timestamp.strftime('%H:%M:%S UTC') }}"
}The message looks like this in Telegram:
🔴 CRITICAL |
nginx-lbconnection pool: 95% Host:
desktop-00.miller.infoTime: 13:34:09 UTC
Configure the generator
The cron expression */2 * * * * 0 fires at second 0 of every 2nd minute — seconds are the last field, after the weekday, so the trailing 0 is the second and the leading */2 the minute. The HTTP output posts to the Telegram API endpoint, with success_code: 200 matching what sendMessage actually returns on success — the http output's own default of 201 assumes an endpoint that treats each POST as creating a new resource, which Telegram's does not.
input:
- cron:
expression: "*/2 * * * * 0"
count: 1
event:
template:
mode: all
params:
chat_id: ${params.chat_id}
templates:
- alert:
template: templates/alert.jinja
output:
- stdout: {}
- http:
url: "https://api.telegram.org/bot${secrets.telegram_token}/sendMessage"
method: POST
success_code: 200
headers:
Content-Type: "application/json"
formatter:
format: plainThe plain formatter passes the template output as-is — the template already produces a valid JSON request body. Swapping in the http output's own default of json-batch here would collect every alert a batch accumulates into a single JSON array and post that array as one request, which Telegram's sendMessage endpoint does not accept; see Send test data to an API endpoint for the full per-event versus per-batch tradeoff behind that choice. The stdout output is included for local debugging; remove it in production.
Configure the application
server:
host: "0.0.0.0"
port: 9474
path:
startup: /home/user/eventum/startup.yml
generators_dir: /home/user/eventum/generators
logs: /home/user/eventum/logs
keyring_cryptfile: /home/user/eventum/cryptfile.cfgAll path.* values must be absolute paths. Adjust to match your actual project location.
The startup file passes the chat ID as a parameter:
- id: alerts
path: alerts/generator.yml
params:
chat_id: "123456789"Replace 123456789 with your actual Telegram chat ID.
Store the bot token
The bot token is sensitive — store it in the keyring:
eventum-keyring set telegram_tokenEnter your bot token when prompted (e.g., 110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw).
Run it
eventum run -c eventum.ymlEvery 2 minutes, an alert message appears in your Telegram chat. The terminal shows the same JSON body on stdout:
The alert below was captured by pointing this generator's output at stdout alone and swapping the cron input for a bounded static input, so it rendered immediately in sample mode (--live-mode false) instead of waiting on the real 2-minute cadence. The deployed config above, run with eventum run, produces the identical JSON shape at that real cadence, delivered to Telegram instead of the terminal.
{
"chat_id": 123456789,
"parse_mode": "Markdown",
"text": "⚠️ *WARNING* | `api-gateway`\n\nconnection pool: *81%*\nHost: `srv-41.cole-williams.com`\nTime: 13:34:09 UTC"
}Going further
- Any HTTP-reachable destination — as the intro notes, the request body and the URL are the only Telegram-specific parts. Point the same
httpoutput at a Slack Incoming Webhook, a Discord Webhook, or a PagerDuty Events API integration instead, reshaping the template's JSON to match the destination's expected payload. See Send test data to an API endpoint for the delivery mechanics this generalizes. - Escalation schedule — use multiple inputs with different cron expressions: check every 2 minutes during business hours, every 10 minutes overnight.
- Incident sequences — switch to FSM mode to model alert → acknowledged → investigating → resolved workflows.
- Silence window — use the cron
start/endfields to suppress alerts during maintenance windows.
What's next
cron reference
Cron expressions, date ranges, and extended syntax.
HTTP output
URL, headers, auth, and TLS options for HTTP delivery.
Secrets
Encrypting credentials with the keyring.
FAQ
Related
- The Test Sigma rules with synthetic attack telemetry tutorial for generating the detection match an alert like this one would normally be triggered by
- The Logs vs metrics vs events lesson for treating an alert as a structured event like any other, not a special case
- The Send test data to an API endpoint lesson for the
success_code, formatter, and batching mechanics behind the HTTP delivery this scenario relies on - The Secrets reference for the keyring and
${secrets.*}substitution storing the bot token - The Scenarios track for the wider picture of applied synthetic data
- Ready-made generators in the Eventum Hub