This repository has been archived by the owner on Aug 25, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
afbea90
commit 8b9339a
Showing
8 changed files
with
677 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,3 +102,6 @@ dist | |
|
||
# TernJS port file | ||
.tern-port | ||
|
||
# Configuration | ||
config.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
module.exports = { | ||
name: 'matrix', | ||
description: '矩阵命令', | ||
options: [{ | ||
name: 'invite', | ||
type: 'SUB_COMMAND', | ||
description: '查看机器人的邀请链接。' | ||
},{ | ||
name: 'info', | ||
type: 'SUB_COMMAND', | ||
description: '查看机器人信息。' | ||
}], | ||
|
||
async execute(interaction) { | ||
// 根据子命令调用对应的处理程序 | ||
let sub = interaction.options[0].name; | ||
await handles[sub](interaction); | ||
} | ||
} | ||
|
||
// 处理程序 | ||
let handles = { | ||
invite(interaction) { | ||
// 引入对象 | ||
const { commonEmbed } = interaction.client.embeds; | ||
|
||
// 发送嵌入消息 | ||
let embed = commonEmbed() | ||
.setTitle('欢迎使用矩阵!') | ||
.setDescription(`[点此邀请加入服务器](https://discord.com/oauth2/authorize?client_id=${interaction.applicationID}&scope=bot+applications.commands&permissions=8)`); | ||
interaction.reply(embed); | ||
}, | ||
|
||
async info(interaction) { | ||
// 引入对象 | ||
const { user, application, embeds } = interaction.client; | ||
const { commonEmbed } = embeds; | ||
const { version } = require('../package.json'); | ||
|
||
await application.fetch(); // 获取应用信息 | ||
// 发送嵌入消息 | ||
let embed = commonEmbed() | ||
.setTitle('机器人信息') | ||
.setThumbnail(user.displayAvatarURL()) | ||
.addFields( | ||
{ name: '所有者', value: application.owner.tag, inline: true }, | ||
{ name: '版本', value: version, inline: true }, | ||
{ name: '源代码', value: 'https://github.com/Android-KitKat/Matrix', inline: false } | ||
); | ||
interaction.reply(embed); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
const Discord = require('discord.js'); | ||
const fetch = require('node-fetch'); | ||
const xml2js = require('xml2js'); | ||
|
||
module.exports = { | ||
name: 'playsteam', | ||
description: '寻找共同游玩的Steam游戏。', | ||
cooldown: 60, | ||
options: [{ | ||
name: 'player1', | ||
type: 'STRING', | ||
description: 'Steam个人资料地址', | ||
required: true | ||
},{ | ||
name: 'player2', | ||
type: 'STRING', | ||
description: 'Steam个人资料地址', | ||
required: true | ||
},{ | ||
name: 'player3', | ||
type: 'STRING', | ||
description: 'Steam个人资料地址', | ||
required: false | ||
},{ | ||
name: 'player4', | ||
type: 'STRING', | ||
description: 'Steam个人资料地址', | ||
required: false | ||
},{ | ||
name: 'player5', | ||
type: 'STRING', | ||
description: 'Steam个人资料地址', | ||
required: false | ||
},{ | ||
name: 'player6', | ||
type: 'STRING', | ||
description: 'Steam个人资料地址', | ||
required: false | ||
}], | ||
|
||
async execute(interaction) { | ||
// 解析参数 | ||
let profiles = []; | ||
for (let option of interaction.options) { | ||
profiles.push(option.value.trim()); | ||
} | ||
|
||
// 推迟回复 | ||
await interaction.defer(); | ||
|
||
// 引入嵌入消息工具类 | ||
const { commonEmbed, errorEmbed } = interaction.client.embeds; | ||
|
||
// 计算游戏的交集 | ||
let data; | ||
try { | ||
data = await getGamesData(profiles[0]); | ||
for (let i = 1; i < profiles.length; i++) { | ||
let intersect = data.intersect(await getGamesData(profiles[i])); | ||
intersect.each(game => { | ||
let oldGame = data.get(game.appID); | ||
if (game.hoursOnRecord || oldGame.hoursOnRecord) { | ||
game.hoursOnRecord = (game.hoursOnRecord || 0) + (oldGame.hoursOnRecord || 0); | ||
} | ||
}); | ||
data = intersect; | ||
} | ||
} catch (error) { | ||
if (error.code !== 'ERR_GAME_INFO' && error.name !== 'TypeError') throw error; | ||
return interaction.followUp({ embeds:[errorEmbed(error)], ephemeral: true }); | ||
} | ||
|
||
// 按游玩时间排序 | ||
data = data.sort((gameA, gameB) => { | ||
let a = gameA.hoursOnRecord || 0; | ||
let b = gameB.hoursOnRecord || 0; | ||
return -(a - b); | ||
}); | ||
|
||
// 用便于浏览的形式输出 | ||
let result = []; | ||
for (let game of data.first(15).values()) { | ||
result.push(`[${game.name}](${game.storeLink}) (${game.hoursOnRecord} 小时)`); | ||
} | ||
let embed = commonEmbed() | ||
.setTitle('共同游玩的Steam游戏') | ||
.setDescription(`将游玩时间相加后进行排序的前15个游戏。\n\n${result.join('\n')}`); | ||
await interaction.followUp(embed); | ||
} | ||
} | ||
|
||
/** | ||
* 获取游戏数据 | ||
* @param {string} profile Steam个人资料地址 | ||
* @returns {Promise<Discord.Collection<string, any>>} | ||
*/ | ||
async function getGamesData(profile) { | ||
let url = `${profile}${profile.endsWith('/') ? '' : '/'}games/?xml=1&l=schinese`; // 生成URL | ||
let res = await fetch(url); // 发送请求 | ||
let data = await xml2js.parseStringPromise(await res.text(), { explicitArray: false }); // 解析XML | ||
// 无法获取游戏数据时报错 | ||
if (!data.gamesList.games) { | ||
let error = new Error(`无法获取 [${data.gamesList.steamID}](${profile}) 的游戏信息。`); | ||
error.code = 'ERR_GAME_INFO'; | ||
throw error; | ||
} | ||
// 提取游戏数据 | ||
let coll = new Discord.Collection(); | ||
for (let game of data.gamesList.games.game) { | ||
// 格式化游玩时间 | ||
if (game.hoursOnRecord) { | ||
game.hoursOnRecord = Number(game.hoursOnRecord.replace(/,/g, '')); | ||
}; | ||
coll.set(game.appID, game); | ||
} | ||
return coll; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"token": "Bot token", | ||
"proxy": { | ||
"enable": false, | ||
"http": "http://127.0.0.1:8080", | ||
"https": "" | ||
}, | ||
"activity": "" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
const fs = require('fs'); | ||
const colors = require('colors/safe'); | ||
const Discord = require('discord.js'); | ||
const EmbedUtils = require('./utils/EmbedUtils'); | ||
const { token, proxy, activity } = require('./config.json'); | ||
|
||
// 创建客户端 | ||
const client = new Discord.Client({ | ||
intents: ['GUILD_INTEGRATIONS'], | ||
presence: { | ||
activities: [{ | ||
name: '启动中...' | ||
}] | ||
} | ||
}); | ||
|
||
// 增加自定义属性 | ||
client.commands = new Discord.Collection(); | ||
client.cooldowns = new Discord.Collection(); | ||
client.embeds = new EmbedUtils(client); | ||
client.logger = require('tracer').dailyfile({ | ||
format: '{{timestamp}} <{{title}}> {{message}}', | ||
dateformat: 'yyyy-mm-dd HH:MM:ss Z', | ||
transport: data => { | ||
let color = { | ||
//log : do nothing | ||
trace : colors.magenta, | ||
debug : colors.cyan, | ||
info : colors.green, | ||
warn : colors.yellow, | ||
error : colors.red.bold, | ||
fatal : colors.red.bold | ||
} | ||
let output = color[data.title](data.output); | ||
if (data.title == 'warn') { | ||
console.warn(output); | ||
} else if (data.level > 4) { | ||
console.error(output); | ||
} else { | ||
console.log(output); | ||
} | ||
}, | ||
root: './logs/', | ||
maxLogFiles: 30, | ||
allLogsFileName: 'Matrix' | ||
}); | ||
const { commands, cooldowns, embeds, logger } = client; | ||
const { errorEmbed } = embeds; | ||
|
||
// 创建日志文件夹 | ||
if (!fs.existsSync('./logs/')) fs.mkdirSync('./logs/'); | ||
|
||
// 加载命令 | ||
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js')); | ||
for (const file of commandFiles) { | ||
let command = require(`./commands/${file}`); | ||
commands.set(command.name, command); | ||
} | ||
|
||
// 准备就绪时 | ||
client.once('ready', () => { | ||
logger.info(`登陆到 ${client.user.tag}`); // 输出登陆信息 | ||
client.user.setPresence(activity ? { activities: [{ name: activity }] } : {}); // 设置活动状态 | ||
// 注册斜杠命令 | ||
commands.each(command => { | ||
client.application.commands.create(command); | ||
}); | ||
}); | ||
|
||
// 收到交互时 | ||
client.on('interaction', async interaction => { | ||
// 检查交互 | ||
if (!interaction.isCommand()) return; | ||
if (!commands.has(interaction.commandName)) return interaction.reply({ embeds: [errorEmbed('命令不存在!')], ephemeral: true }); | ||
|
||
// 获取相关对象 | ||
const command = commands.get(interaction.commandName); | ||
const { user, guild, guildID } = interaction; | ||
|
||
// 检查冷却时间 | ||
if (!cooldowns.has(command.name)) { | ||
cooldowns.set(command.name, new Discord.Collection()); | ||
} | ||
const now = Date.now(); | ||
const timestamps = cooldowns.get(command.name); | ||
const cooldownAmount = (command.cooldown || 3) * 1000; | ||
if (timestamps.has(user.id)) { | ||
const timeLeft = (cooldownAmount - (now - timestamps.get(user.id))) / 1000; | ||
return interaction.reply({ embeds: [errorEmbed(`\`${command.name}\` 命令正在冷却,请 ${timeLeft.toFixed(1)} 秒后再试。`)], ephemeral: true }); | ||
} | ||
|
||
// 记录冷却时间 | ||
timestamps.set(user.id, now); | ||
setTimeout(() => timestamps.delete(user.id), cooldownAmount); | ||
|
||
// 记录日志 | ||
let options = []; | ||
interaction.options.forEach(option => { | ||
options.push(`${option.name}${option.value ? `: ${option.value}` : ''}`); | ||
}); | ||
let optionsText = options.length > 0 ? ` ${options.join(' ')}` : ''; | ||
let guildText = guild ? `,位于 ${(await guild.fetch()).name}(${guildID})` : ''; | ||
logger.info(`${user.tag}(${user.id}) 使用了 /${command.name}${optionsText}${guildText}。`); | ||
|
||
// 执行命令 | ||
try { | ||
await command.execute(interaction); | ||
} catch (error) { | ||
logger.error(error); | ||
if (interaction.deferred) { | ||
interaction.followUp({ embeds: [errorEmbed('在尝试执行命令时出错!')], ephemeral: true }); | ||
} else { | ||
interaction.reply({ embeds: [errorEmbed('在尝试执行命令时出错!')], ephemeral: true }); | ||
} | ||
} | ||
}); | ||
|
||
// 设置代理 | ||
if (proxy.enable) { | ||
require('global-agent').bootstrap(); | ||
if (proxy.http) global.GLOBAL_AGENT.HTTP_PROXY = proxy.http; | ||
if (proxy.https) global.GLOBAL_AGENT_HTTPS_PROXY = proxy.https; | ||
} | ||
|
||
client.login(token); // 登陆客户端 | ||
|
||
// 程序中止时 | ||
process.on('SIGINT', async () => { | ||
client.destroy(); // 销毁客户端 | ||
logger.info('拜拜!'); // 输出退出信息 | ||
process.exit(); // 程序退出 | ||
}); |
Oops, something went wrong.