5

Send notifications to a Telegram bot for events in an AWS account

 2 years ago
source link: https://advancedweb.hu/send-notifications-to-a-telegram-bot-for-events-in-an-aws-account/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Event-driven Telegram bot

In the previous article we looked into how to get started with Telegram bot development. The bot that we implemented there was reactive: it responded to messages sent by a user, but it could not send messages later.

Related
A step-by-step guide on getting started with Telegram bot development

In this article, we’ll create a bot that sends messages when an event happens in an AWS account. This is useful as a monitoring tool, for example, a CloudWatch Alarm can trigger the bot and send a message. It can also be integrated with EventBridge, which is a powerful tool to monitor what is happening in the account. For example, the bot can send a message when an EC2 instance is stopped.

Why it’s better than SMS?

First, SMS costs money and its setup is increasingly complicated due to regulation. But the main benefit of a bot is two-way communication. Not only you can get an event that an EC2 instance is stopped, but you could also fetch its logs, or even restart it. While all these require writing code, the possibilities are there to make it more than a dumb notification service.

Recommended book
How to design, implement, and deploy GraphQL-based APIs on the AWS cloud
Book, 2022

Let’s jump in to the implementation!

Managing subscriptions

The first task is to manage subscriptions. Since multiple people can use a Telegram bot and it needs the chat IDs to send messages, we need a way for users to subscribe and unsubscribe. This also requires a database to store the current subscriptions, for which DynamoDB is a great choice.

To manage a subscription, three functions are needed, one to start, one to stop, and one to query the current status:

const stop = async (chat_id) => {
	await client.send(new DeleteItemCommand({
		TableName: subscribers_table,
		Key: {chat_id: {S: String(chat_id)}},
	}));
};
const start = async (chat_id) => {
	await client.send(new PutItemCommand({
		TableName: subscribers_table,
		Item: {
			chat_id: {S: String(chat_id)},
		},
	}));
};
const isListening = async (chat_id) => {
	const res = await client.send(new GetItemCommand({
		TableName: subscribers_table,
		Key: {chat_id: {S: String(chat_id)}},
	}));
	return res.Item !== undefined;
};

With these, the bot needs to process control messages sent to it:

if (update.message) {
	const {message: {chat: {id: chat_id}, text}} = update;
	if (text === "/start") {
		await start(chat_id);
		await sendTelegramCommand("sendMessage", {
			chat_id,
			text: "Subscribed to updates",
		});
	}
	if (text === "/stop") {
		await stop(chat_id);
		await sendTelegramCommand("sendMessage", {
			chat_id,
			text: "Updates stopped",
		});
	}
	if (text === "/status") {
		const status = await isListening(chat_id);
		await sendTelegramCommand("sendMessage", {
			chat_id,
			text: String(status),
		});
	}
}

This is enough for users to manage their subscription status:

subscribe-aeef70bad7ff52d5b633912e99e6d924417de09312b730b615cbcc8161831562.png

And under the hood, the bot keeps track of the chat IDs in the DynamoDB table:

ddb_item-51289da109b7eaf6d19aaec100b5fa74acaffee36093ff7431e67284092def4c.png

Error handling

In Telegram, users can block bots, in which case it should register an unsubscription. When that happens, Telegram sends an update via the Webhook, and the bot can stop the notifications for that chat:

if (update.my_chat_member) {
	if (update.my_chat_member.new_chat_member.status === "kicked") {
		await stop(update.my_chat_member.chat.id);
	}
}

Publishing messages

Now that we have a list of subscribed chats, let’s see how a Lambda function can send messages to all of them!

In this example we’ll process SNS messages, so there are Records in the event and a message is in the Sns.Message field in the record:

import {sendTelegramCommand} from "./telegram-control.js";
import {DynamoDBClient, paginateScan} from "@aws-sdk/client-dynamodb";

export const handler = async (event) => {
	const client = new DynamoDBClient();
	const {subscribers_table} = process.env;

	for await (const page of paginateScan({client}, {TableName: subscribers_table})) {
		// for each page of chats
		await Promise.all(page.Items.map(async ({chat_id: {S: chat_id}}) => {
			// for each subscriber
			await Promise.all(event.Records.map((async (record) => {
				// for each record
				await sendTelegramCommand("sendMessage", {
					chat_id,
					text: record.Sns.Message,
				});
			})));
		}));
	}
};

Then subscribe the Lambda function to the SNS topic:

subscriptions-f010b81af84e75ec6b388425695fc974cb025edcdb0fc2a0dac18e26da29f168.png

Testing

Now that the whole flow is set up, let’s publish a message to the SNS topic:

publish-548952dea3b05dc52d92890df3aa3409de1e72b5dec5a8002301290ffa3acb06.png

On all subscribed Telegram chats, the message is sent by the bot:

received-7950703f2e816a889b8dfc83031cfceac73441a397390a8af79381567b46539f.png

CloudWatch alarms

A natural next step for a notification service is to send alarms from CloudWatch as these track the urgent events in an account. Since they support SNS as a target for a state change, it’s easy to set up:

alarm_config-974788344ec2fa04ea6a95338fd8953415fd7adb5c21bc25db1e25d9b913f3c4.png

Then when the alarm’s state is changed, the Telegram bot sends a message:

alarm_received-9f973279d0cb3daac5aff0fd1c597c7d8901c8f43534114d308602a01e1089c7.png

Conclusion

Telegram bots can track subscribers and they can send messages when an external event happens. This makes a bot suitable to send a notification for important and urgent events.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK