如何利用AWS监听存储桶并上传到tg bot

业务描述:

需要监听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也可以监听到状态,也不会存在消息丢失情况,即使丢失也可以通过状态控制。

相关推荐
Stitch .1 天前
AWS开源 Agent 框架 Strands Agents 速成班(实验手册)
jupyter·云计算·aws·亚马逊·vpc·智能体·mcp
WongKyunban1 天前
AWS服务分类
大数据·云计算·aws
可观测性用观测云2 天前
AWS VPC Transit Gateway 可观测最佳实践
aws
潘多编程2 天前
AWS上部署Spring Boot应用的完整指南
spring boot·云计算·aws
潘多编程2 天前
构建企业级Web应用:AWS全栈架构深度解析
前端·架构·aws
昂亢伏特卜3 天前
使用AWS免费EC2自建RustDesk远程桌面连接服务
云计算·aws
观测云4 天前
AWS VPC NAT 网关可观测最佳实践
云计算·aws
AWS官方合作商5 天前
Amazon RDS for MySQL成本优化:RDS缓存降本实战
数据库·mysql·aws
AWS官方合作商5 天前
AWS IAM:安全访问管理的核心指南
网络安全·云计算·aws
Linux运维技术栈5 天前
多云场景实战:华为手机 QR 码绑定与 AWS云服务器终端登录全解
aws·微软云·or