如何用discord.py开发追踪Rocket League周游玩时长的机器人?
Discord机器人追踪Rocket League游玩时长解决方案
核心功能实现步骤
1. 检测用户Rocket League游玩状态
Discord API提供用户活动状态(Activity)接口,通过监听用户状态变化或轮询活动列表,可精准检测是否开始/结束游玩Rocket League。
关键要点:
- 需在Discord开发者门户开启Presence Intent和Server Members Intent(机器人特权网关权限),否则无法获取用户活动数据。
- Rocket League的活动名称固定为
Rocket League,活动类型需匹配PLAYING(区分观看直播、听歌等其他状态)。
代码示例(discord.js):
const fs = require('fs'); const { Client, GatewayIntentBits } = require('discord.js'); const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildPresences, GatewayIntentBits.GuildMembers, GatewayIntentBits.MessageContent ] }); // 存储用户游玩数据(可替换为SQLite等数据库) const USER_DATA_PATH = './rl_playtime.json'; let userData = {}; // 初始化数据 try { userData = JSON.parse(fs.readFileSync(USER_DATA_PATH, 'utf8')); } catch (err) { fs.writeFileSync(USER_DATA_PATH, JSON.stringify(userData)); } function saveUserData() { fs.writeFileSync(USER_DATA_PATH, JSON.stringify(userData, null, 2)); } // 监听用户状态变化 client.on('presenceUpdate', (oldPresence, newPresence) => { if (!newPresence.guild) return; // 忽略非服务器用户 // 检查前后状态是否包含Rocket League游玩 const wasPlayingRL = oldPresence?.activities.some(act => act.name === 'Rocket League' && act.type === 'PLAYING' ); const isPlayingRL = newPresence.activities.some(act => act.name === 'Rocket League' && act.type === 'PLAYING' ); // 开始游玩:记录时间戳 if (!wasPlayingRL && isPlayingRL) { userData[newPresence.user.id] = userData[newPresence.user.id] || { weeklyTotal: 0 }; userData[newPresence.user.id].lastStartTime = Date.now(); saveUserData(); } // 结束游玩:计算时长并累加 else if (wasPlayingRL && !isPlayingRL) { const userRecord = userData[newPresence.user.id]; if (userRecord?.lastStartTime) { const duration = Date.now() - userRecord.lastStartTime; userRecord.weeklyTotal += duration; delete userRecord.lastStartTime; saveUserData(); } } });
2. 追踪一周游玩时长
通过定时任务每周重置数据,确保统计范围始终为当前自然周。
代码示例:
// 每周日UTC 0点重置所有用户的周时长 function resetWeeklyData() { const now = new Date(); if (now.getUTCDay() === 0 && now.getUTCHours() === 0 && now.getUTCMinutes() < 1) { Object.keys(userData).forEach(userId => { userData[userId].weeklyTotal = 0; }); saveUserData(); // 可选:发送重置通知到指定频道 const notifyChannel = client.channels.cache.get('你的通知频道ID'); notifyChannel?.send('本周Rocket League游玩时长已重置,新一周的排行开始啦!'); } } // 每小时检查一次重置条件 setInterval(resetWeeklyData, 3600000);
3. 响应指令生成排行榜
监听!leaderboard指令,从存储数据中提取用户时长并排序格式化输出。
代码示例:
client.on('messageCreate', (message) => { if (message.content !== '!leaderboard') return; // 过滤有效数据并按时长降序排序 const sortedPlayers = Object.entries(userData) .filter(([_, data]) => data.weeklyTotal > 0) .sort((a, b) => b[1].weeklyTotal - a[1].weeklyTotal); if (sortedPlayers.length === 0) { return message.reply('本周还没有人玩过Rocket League哦!'); } // 格式化毫秒为小时+分钟 const formatMs = (ms) => { const hours = Math.floor(ms / 3600000); const minutes = Math.floor((ms % 3600000) / 60000); return `${hours}小时${minutes}分钟`; }; // 生成排行文本 let leaderboard = '📊 Rocket League本周游玩时长排行 📊\n'; sortedPlayers.forEach(([userId, data], index) => { const member = message.guild.members.cache.get(userId); const username = member?.displayName || userId; leaderboard += `${index + 1}. ${username}: ${formatMs(data.weeklyTotal)}\n`; }); message.channel.send(leaderboard); }); client.login('你的机器人Token');
注意事项
- 部分用户可能开启隐私设置,禁止他人查看活动状态,此类用户无法被追踪,需在逻辑中忽略。
- 若服务器用户量较大,建议使用SQLite/MySQL替代JSON文件存储,提升数据读写效率。
- 可扩展支持时区适配,根据服务器所在时区调整周重置时间。
内容的提问来源于stack exchange,提问作者dShocc1
相关产品推荐
相关产品推荐

