如何用discord.js开发监控FTP日志变动并同步到Discord的机器人
基于discord.js的FTP日志同步机器人实现方案
依赖安装
需要提前安装以下npm包:
discord.js@14:Discord机器人核心开发库basic-ftp:轻量FTP服务操作库diff:文本差异对比工具,用于提取日志新增内容
核心逻辑说明
因为FTP协议没有主动推送文件变动的能力,我们采用定时轮询的方案实现变动检测:
- Discord机器人登录成功后,缓存目标推送频道的实例
- 按设定间隔向FTP服务器发起请求,拉取目标日志文件的最后修改时间、文件大小元信息
- 对比本次和上一次拉取的元信息,没有变动直接结束本次轮询
- 检测到变动后拉取最新日志内容,和本地存储的上一版日志做差异对比,只提取新增的内容段
- 将新增内容按Discord单条消息2000字符的限制做拆分,用代码块格式批量推送到指定频道,同时更新本地存储的日志内容和元信息
核心实现代码
const { Client, GatewayIntentBits } = require('discord.js'); const ftp = require('basic-ftp'); const { diffTrimmedLines } = require('diff'); // 配置项,可根据实际情况修改 const CONFIG = { DISCORD_TOKEN: '你的Discord机器人Token', TARGET_CHANNEL_ID: '目标推送频道的ID', FTP_CONFIG: { host: 'FTP服务器地址', port: 21, user: 'FTP登录用户名', password: 'FTP登录密码', logPath: '/server/running.log' // FTP上日志文件的完整路径 }, POLL_INTERVAL: 5000 // 轮询间隔,单位毫秒,此处为5秒 }; // 初始化Discord客户端 const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages] }); let targetChannel; let lastLogContent = ''; let lastFileMtime = 0; // 机器人就绪后启动轮询任务 client.on('ready', async () => { console.log(`机器人已登录:${client.user.tag}`); targetChannel = await client.channels.fetch(CONFIG.TARGET_CHANNEL_ID); setInterval(pollFtpLog, CONFIG.POLL_INTERVAL); }); // FTP日志轮询函数 async function pollFtpLog() { const ftpClient = new ftp.Client(); try { await ftpClient.access(CONFIG.FTP_CONFIG); // 获取日志文件的最后修改时间 const currentMtime = (await ftpClient.lastMod(CONFIG.FTP_CONFIG.logPath)).getTime(); // 文件未变动直接退出 if (currentMtime === lastFileMtime) return ftpClient.close(); lastFileMtime = currentMtime; // 拉取最新日志内容 const logBuffer = await ftpClient.downloadToBuffer(CONFIG.FTP_CONFIG.logPath); const newLogContent = logBuffer.toString('utf8'); // 首次拉取只存内容不推送 if (!lastLogContent) { lastLogContent = newLogContent; return ftpClient.close(); } // 提取新增日志内容 const diffResult = diffTrimmedLines(lastLogContent, newLogContent); const addedContent = diffResult.filter(item => item.added).map(item => item.value).join('\n'); if (addedContent.trim()) { // 按Discord消息长度限制拆分后推送 const msgChunks = addedContent.match(/[\s\S]{1,1900}/g) || []; for (const chunk of msgChunks) { await targetChannel.send(`\`\`\`log\n${chunk}\`\`\``); } } lastLogContent = newLogContent; } catch (err) { console.error('轮询出错:', err); } finally { ftpClient.close(); } } client.login(CONFIG.DISCORD_TOKEN);
优化建议
- 轮询间隔不要设置过短,避免给FTP服务器造成过大压力,建议最低设置为3秒以上
- 如果日志文件体积很大,可改为按字节范围拉取新增内容,对比上一次记录的文件大小,只拉取大于上一次大小的字节段,不需要全量下载
- 可自行添加日志内容过滤逻辑,去掉不需要推送的无关日志行,减少频道消息噪音
- 如果日志文件为GBK编码,可引入
iconv-lite库对拉取到的Buffer做转码,避免出现乱码 - 可添加错误重试机制,FTP连接失败或Discord消息推送失败时自动重试
内容的提问来源于stack exchange,提问作者Hammer
相关产品推荐
相关产品推荐

