业务描述:
需要监听aws的存储中的最新消息,发送新的消息推送到指定tg的频道。
主要流程:
1.上传消息到s3存储桶(不做具体描述)
2.通过aws的lambda监听s3存储桶的最新消息(txt文件)
3.将txt文件内容处理后推送到tg频道中
具体流程:
一、准备工作
1.创建bot
2.在频道中添加bot作为管理员
3.获取bot的token和频道的channel id
二、监听s3消息并推送到指定的tg频道中
1.创建函数
2.上传代码到lambda中
注:建议使用zip上传
代码源中必须包含package和node_modules,需要项目的完整环境
注: 代码如下,可以根据自己的业务调整。我的业务tg频道的channel id是从txt中解析获取。
注: 需要注意parse_mode的选择
注: 在lambda中发送完消息之后是无法获取状态的,也就是代码中response是没法获取状态的,不管成功失败。这也就导致了会存在消息丢失的情况
javascript
const TelegramBot = require('node-telegram-bot-api');
const AWS = require('aws-sdk');
const s3 = new AWS.S3();
const TELEGRAM_BOT_TOKEN = '你的tg bot token'; // Telegram Bot Token
const TARGET_BUCKET_NAME = '你需要监听的存储桶的名称'; // 监听的目标存储桶名称
// Initialize the Telegram bot
const bot = new TelegramBot(TELEGRAM_BOT_TOKEN);
// AWS Lambda Handler
exports.handler = async (event, context) => {
const functionName = context.functionName; // 获取 Lambda 函数的名称
// tg-bot-test:测试环境 tg-bot:生产
const [TEXT_NAME, MEDIA_NAME] = functionName === 'tg-bot-test' ? ['text-output-test', 'media-test'] : ['text-output', 'media'];
try {
const currentTime = new Date();
for (const record of event.Records) {
const bucket = record.s3.bucket.name; // 存储桶名称
const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' ')); // 对象键
const eventName = record.eventName;
// 仅处理指定存储桶的事件(新增)
if (bucket === TARGET_BUCKET_NAME && eventName.startsWith('ObjectCreated:Put')) {
console.log(`New file uploaded: ${key} to bucket: ${bucket}`);
// 获取对象的元数据
const metadata = await getObjectMetadata(bucket, key);
const creationTime = metadata.LastModified; // 获取创建时间
const timeDiffInSeconds = (currentTime - creationTime) / 1000; // 计算时间差(秒)
console.log(`File creation time: ${creationTime}, Time difference: ${timeDiffInSeconds} seconds`);
// 若创建时间超过 60 秒,则不再继续执行
if (timeDiffInSeconds > 60) {
console.log(`File ${key} creation time exceeds 60 seconds, stopping execution...`);
return; // 结束 Lambda 函数的执行
}
// 检查文件是否在指定的文件夹中
if (key.startsWith(`${TEXT_NAME}/`)) {
// 从 S3 获取文本文件内容
const textContent = await getFileContentFromS3(bucket, key);
console.log(`Updated file: ${key}`); // 打印更新文件的名称
console.log(`textContent: ${textContent}`);
// 获取第三行内容并转换为数字
let numberValue = 0;
const lines = textContent.split('\n');
let captionContent = "";
let channelId = "";
if (lines.length >= 3) {
channelId = lines[0].trim(); // 获取发送到的频道的id
console.log("channelId:", channelId);
const thirdLine = lines[2].trim(); // 获取第三行并去除多余空格
numberValue = parseFloat(thirdLine); // 转换为数字
console.log(`Third line as number: ${numberValue}`); // 打印数字值
captionContent = lines.slice(3).join('\n').trim(); // 从第三行之后的所有内容
} else {
console.error('The file does not contain enough lines.');
return;
}
// 提取文件名(去除文件夹和后缀)
const fileName = key.split('/').pop().split('.').slice(0, -1).join('.');
console.log(`File name without folder and extension: ${fileName}`); // 打印文件名
// 生成所有图片的名称
let allImage = [];
for (let index = 0; index < numberValue; index++) {
allImage.push(`${fileName}.img${index}.jpg`);
}
console.log(`All images: ${allImage}`);
// 收集图片的 URL
const imageUrls = allImage.map(image => `https://${bucket}.s3.us-east-1.amazonaws.com/${MEDIA_NAME}/${image}`);
// 发送所有图片作为一条消息
await sendPhotosToTelegram(imageUrls, captionContent, channelId);
}
}
}
} catch (error) {
console.error("error message:", error);
}
};
const getObjectMetadata = async (bucket, key) => {
const params = {
Bucket: bucket,
Key: key
};
const metadata = await s3.headObject(params).promise();
return metadata; // 返回对象的元数据
};
const getFileContentFromS3 = async (bucket, key) => {
const params = {
Bucket: bucket,
Key: key
};
const data = await s3.getObject(params).promise();
return data.Body.toString('utf-8'); // 返回文件内容,假设是文本文件
};
const sendPhotosToTelegram = async (imageUrls, captionContent, channelId) => {
const media = imageUrls.map((url) => ({
type: 'photo',
media: url,
}));
// 如果有需要,可以为第一张图片添加 caption
if (captionContent) {
media[0].caption = captionContent;
media[0].parse_mode = 'Markdown'; //注意此处的选择,Markdown是支持多图和超链接文本的,但是MarkdownV2是不支持超链接文本的,而且也不支持特殊字符
}
try {
console.log("request==================start");
const response = await bot.sendMediaGroup(`@${channelId}`, media);
console.log("request==================end");
console.log('Response from Telegram:', response); // 打印 Telegram 的响应(lambda没有效果)
return response;
} catch (error) {
console.error('Error sending photos to Telegram:', error.response ? error.response.data : error.message);
throw error;
}
};
其他
1.在没有解决消息丢失的情况下建议不要使用lambda推送重要消息
2.可以使用mq来完成消息的监听和发送,这样response也可以监听到状态,也不会存在消息丢失情况,即使丢失也可以通过状态控制。