業務描述:
?? 需要監聽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是沒法獲取狀態的,不管成功失敗。這也就導致了會存在消息丟失的情況
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(); // 獲取發送到的頻道的idconsole.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}`);// 收集圖片的 URLconst 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,}));// 如果有需要,可以為第一張圖片添加 captionif (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也可以監聽到狀態,也不會存在消息丟失情況,即使丟失也可以通過狀態控制。