This commit is contained in:
yhqe committed 2026-03-15 03:45:16 -05:00
1 parent a27bfb63e8
commit 670512944c
15 files changed
+33568 -43

No files matched your search

View File
Whitespace-only changes.
+348
View File
@@ -0,0 +1,348 @@
'use strict';
const { time } = require("discord.js");
const path = require('path');
const fs = require('fs');
const logger = require('../../utils/logger');
async function fetch_avatar(id) {
const res = await fetch(`https://thumbnails.roblox.com/v1/users/avatar?userIds=${id}&size=420x420&format=Png&isCircular=false`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' }
});
const data = await res.json();
logger.debug(`[fetch_avatar] ${JSON.stringify(data)}`);
if (data.data && data.data[0] && data.data[0].imageUrl) {
logger.debug(`[fetch_avatar] fetched avatar for user id ${id}: ${data.data[0].imageUrl}`);
return data.data[0].imageUrl;
} else {
return null;
}
}
async function fetch_item_thumb(id) {
const res = await fetch(`https://thumbnails.roblox.com/v1/assets?assetIds=${id}&returnPolicy=PlaceHolder&size=420x420&format=Png&isCircular=false`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' }
});
const data = await res.json();
logger.debug(`[fetch_item_thumb] ${JSON.stringify(data)}`);
if (data.data && data.data[0] && data.data[0].imageUrl) {
logger.debug(`[fetch_item_thumb] fetched item thumbnail for item id ${id}: ${data.data[0].imageUrl}`);
return data.data[0].imageUrl;
} else {
return null;
}
}
function to_unix_timestamp(dateString) {
const unixSeconds = Math.floor(new Date(dateString).getTime() / 1000);
logger.debug(unixSeconds);
return unixSeconds;
}
module.exports = {
name: 'rbx',
description: `various commands related to roblox`,
aliases: ['roblox'],
usage: 'rbx user <username>\n.rbx item <item id>\n.rbx group <group id>\n.rbx game <game id>',
async execute(client, message) {
var embed = {};
const args = message.content.split(' ').slice(1);
if (args.length < 2) {
return message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: '<:no:1481811955592466492> please provide a subcommand and an argument!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
}
const subcommand = args[0].toLowerCase();
const argument = args.slice(1).join(' ');
let sent = await message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
title: '',
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: '<a:loading_icon:1366587619890302998> fetching...',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
let latency = Date.now() - message.createdTimestamp;
let apiLatency = Math.round(client.ws.ping);
if (subcommand === 'user') {
try {
const res = await fetch(`https://users.roblox.com/v1/usernames/users`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ usernames: [argument], excludeBannedUsers: false })
});
const data = await res.json();
const extra_res = await fetch(`https://users.roblox.com/v1/users/${data.data[0].id}`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' }
});
const extra_data = await extra_res.json();
const r_res = await fetch(`https://api.rolimons.com/players/v1/playerinfo/${data.data[0].id}`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' }
});
const r_data = await r_res.json();
const ra_res = await fetch(`https://api.rolimons.com/players/v1/playerassets/${data.data[0].id}`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' }
});
const ra_data = await ra_res.json();
logger.debug(`[rbx] fetched user data (roblox, standard) for "${argument}": ${JSON.stringify(data)}`);
logger.debug(`[rbx] fetched user data (roblox, extra) for "${argument}": ${JSON.stringify(extra_data)}`);
logger.debug(`[rbx] fetched user data (rolimons) for "${argument}": ${JSON.stringify(r_data)}`);
logger.debug(`[rbx] fetched user data (rolimons, assets) for "${argument}": ${JSON.stringify(ra_data)}`);
if (!data.data || !data.data[0]) {
embed = {
color: 0x8aa2b3,
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: '<:no:1481811955592466492> could not find a roblox user with that username!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}
} else {
const user = data.data[0];
const avatarUrl = await fetch_avatar(user.id);
const hasCheckmark = user.hasVerifiedBadge;
const hasPremium = r_data.premium;
const isOnline = ra_data.isOnline;
const viewInvRes = await fetch(`https://inventory.roblox.com/v1/users/${user.id}/can-view-inventory`);
const isInvPublic = (await viewInvRes.json()).canView;
embed = {
color: 0x8aa2b3,
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: `**[${user.displayName}](https://www.roblox.com/users/${user.id}/profile)** [(${user.name})](https://www.rolimons.com/player/${user.id}) ${isOnline ? "<:online:1482657396362055826>" : "<:offline:1482657564650115073>"} ${hasPremium ? "<:premium:1482660040358760602>" : ""} ${hasCheckmark ? "<:Verified:1376786225486364823>" : ""}\n-# *${extra_data.description || "no description"}*`,
fields: [
{ name: 'id', value: String(user.id), inline: true },
{ name: 'rap', value: String(r_data.rap), inline: true },
{ name: 'value', value: String(r_data.value), inline: true },
{ name: 'inventory', value: `${isInvPublic ? 'public' : 'private'}`, inline: true },
{ name: 'terminated', value: String(r_data.terminated), inline: true },
{ name: 'date created', value: `<t:${to_unix_timestamp(String(extra_data.created))}:f>`, inline: true },
{ name: 'last online', value: `${isOnline ? "now" : "<t:" + r_data.last_online + ":f>"}`, inline: true },
],
thumbnail: {
url: avatarUrl,
height: 420,
width: 420,
},
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
};
}
} catch (err) {
logger.error(`[rbx] failed to fetch user data for "${argument}":`, err);
embed = {
color: 0x8aa2b3,
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: `<:no:1481811955592466492> could not fetch data for user "${argument}"!`,
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}
}
} else if (subcommand === 'item') {
try {
const res = await fetch(`https://economy.roblox.com/v2/assets/${argument}/details`);
const p_res = await fetch(`https://thumbnails.roblox.com/v1/ assets?assetIds=${argument}&returnPolicy=PlaceHolder&size=420x420&format=Png&isCircular=false`)
const resale_res = await fetch(`https://economy.roblox.com/v1/assets/${argument}/resale-data`);
const item = await res.json();
logger.debug(`[rbx] fetched item data (roblox) for "${argument}": ${JSON.stringify(item)}`);
const resale_data = await resale_res.json();
logger.debug(`[rbx] fetched resale data for "${argument}": ${JSON.stringify(resale_data)}`);
let itemsJson;
try {
const itemsPath = path.resolve(process.cwd(), 'items.json');
const itemsRaw = fs.readFileSync(itemsPath, 'utf8');
itemsJson = JSON.parse(itemsRaw);
if (itemsJson.items && itemsJson.items[argument]) {
logger.debug(`[rbx] found item ${argument} in items.json: ${JSON.stringify(itemsJson.items[argument])}`);
}
} catch (e) {
logger.warn(`[rbx] could not read items.json: ${e.message}`);
}
let item_e = itemsJson.items[argument];
var hasAcronym = false;
if (item_e[1] = "") {
hasAcronym = false;
} else {
hasAcronym = true;
}
const item_thumb = await fetch_item_thumb(item.AssetId);
const is_offsale = item.IsForSale;
embed = {
color: 0x8aa2b3,
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: `[${item.Name}](https://roblox.com/catalog/${argument}) ${hasAcronym ? item_e[1] : ""}\n-# *${item.Description}*`,
fields: [
{ name: "id", value: `${item.AssetId}`, inline: true },
{ name: "price", value: `${is_offsale ? "offsale" : item.PriceInRobux}`, inline: true },
{ name: "limited", value: `${item.IsLimited}`, inline: true },
{ name: "rap", value: `${item.IsLimited ? resale_data.recentAveragePrice : "0"}`, inline: true },
{ name: "creator", value: `${item.Creator.Name}`, inline: true },
],
thumbnail: {
url: item_thumb,
height: 420,
width: 420
},
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
};
} catch (err) {
logger.error(`[rbx] failed to fetch item data for "${argument}":`, err);
embed = {
color: 0x8aa2b3,
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: `<:no:1481811955592466492> could not fetch data for item "${argument}"!`,
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}
}
} else if (subcommand === 'group') {
try {
const res = await fetch(`https://groups.roblox.com/v1/groups/${argument}`);
const p_res = await fetch(`https://thumbnails.roblox.com/v1/groups/icons?groupIds=${argument}&size=420x420&format=Png&isCircular=false`);
if (!res.ok) throw new Error();
if (!p_res.ok) throw new Error();
const group = await res.json();
const p_json = await p_res.json();
logger.debug(`${JSON.stringify(p_json)}`);
const thumb = (p_json && p_json.data && p_json.data[0]) ? p_json.data[0].imageUrl : null;
embed = {
color: 0x8aa2b3,
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: `**[${group.name}](https://roblox.com/communities/${argument})**\n-# *${group.description || "no description"}*`,
thumbnail: {
url: thumb,
height: 420,
width: 420
},
fields: [
{ name: "members", value: `${group.memberCount}`, inline: true },
{ name: "owner", value: `${group.owner.username}`, inline: true },
{ name: "shout", value: `${group.shout}`, inline: true },
{ name: "open", value: `${group.publicEntryAllowed}`, inline: true },
{ name: "verified", value: `${group.hasVerifiedBadge}`, inline: true },
],
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
};
} catch (err) {
logger.error(`[rbx] failed to fetch group data for "${argument}":`, err);
embed.title = 'Error';
embed.description = 'Failed to fetch group data.';
}
} else if (subcommand === 'game') {
try {
const res = await fetch(`https://games.roblox.com/v1/games?universeIds=${argument}`);
if (!res.ok) throw new Error();
const data = await res.json();
if (!data.data || !data.data[0]) {
embed.title = 'Game Not Found';
embed.description = `Could not find a Roblox game with the universe ID **${argument}**.`;
} else {
const game = data.data[0];
embed.title = `Roblox Game: ${game.name}`;
embed.description = game.description || 'No description.';
embed.thumbnail.url = `https://www.roblox.com/asset-thumbnail/image?assetId=${game.rootPlaceId}&width=420&height=420&format=png`;
embed.fields = [
{ name: 'Creator', value: game.creator.name, inline: true },
{ name: 'Playing', value: game.playing.toLocaleString(), inline: true },
{ name: 'Visits', value: game.visits.toLocaleString(), inline: true }
];
}
} catch (err) {
embed.title = 'Error';
embed.description = 'Failed to fetch game data.';
}
}/* else {
embed.title = 'invalid subcommand';
embed.description = 'Valid subcommands: `user`, `item`, `group`, `game`';
}*/
await sent.edit({ content: null, embeds: [embed] });
},
};
+20 -19
View File
@@ -9,6 +9,7 @@ module.exports = {
usage: 'help [command]',
execute(client, message, args) {
var embed;
const prefix = process.env.PREFIX || '!';
// Show info for a specific command
@@ -20,24 +21,24 @@ module.exports = {
return message.reply(`no command found for \`${name}\`.`);
}
const embed = new EmbedBuilder()
.setColor(0x1da1f2)
.setTitle(`${prefix}${command.name}`)
.setDescription(command.description || 'no description');
if (command.aliases?.length) {
embed.addFields({
name: 'aliases',
value: command.aliases.map((a) => `\`${prefix}${a}\``).join(', '),
});
}
if (command.usage) {
embed.addFields({
name: 'usage',
value: `\`${prefix}${command.usage}\``,
});
}
embed = {
color: 0x8aa2b3,
title: '',
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: `**${prefix}${command.name}**\n-# ${command.description || 'no description'}`,
fields: [
{ name: 'aliases', value: command.aliases.map((a) => `\`${prefix}${a}\``).join(', ') },
{ name: 'usage', value: `\`${prefix}${command.usage}\`` }
],
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
};
return message.reply({ embeds: [embed] });
}
@@ -52,7 +53,7 @@ module.exports = {
commandList.push(`\`${prefix}${command.name}\` — ${command.description || 'no description'}`);
}
const embed = {
embed = {
color: 0x8aa2b3,
title: '',
author: {
File renamed without changes.
File renamed without changes.
@@ -50,7 +50,7 @@ module.exports = {
}
// add the role to a json file for the guild and save it to be used when new members join (if the role is already in the database, update it to the new role)
const dbPath = path.join(__dirname, '..', '..', 'db.json');
const dbPath = path.join(__dirname, '..', '..', '..', 'db.json');
let db = {};
if (fs.existsSync(dbPath)) {
File renamed without changes.
File renamed without changes.
+19 -15
View File
@@ -10,20 +10,24 @@ module.exports = (client, member) => {
const db = JSON.parse(fs.readFileSync(dbPath, 'utf8'));
const roleId = db.autoroles ? db.autoroles[member.guild.id] : null;
if (!roleId) {
logger.info(`member "${member.user.tag}" joined guild "${member.guild.name}", but no autorole is set up.`);
return;
logger.info(`member "${member.user.tag}" has joined guild "${member.guild.name}"`);
// autorole
{
if (!roleId) {
logger.info(`no autorole is set up in "${member.guild.name}"`);
return;
}
const role = member.guild.roles.cache.get(roleId);
if (!role) {
logger.error(`role with id "${roleId}" not found in guild "${member.guild.name}"`);
return;
}
member.roles.add(role)
.then(() => logger.info(`assigned role "${role.name}" to user "${member.user.tag}"!`))
.catch(error => logger.error(`failed to assign role "${role.name}" to user "${member.user.tag}": ${error}`));
}
const role = member.guild.roles.cache.get(roleId);
if (!role) {
logger.error(`role with id "${roleId}" not found in guild "${member.guild.name}".`);
return;
}
logger.info("member has joined the server! assigning member role...");
member.roles.add(role)
.then(() => logger.info(`assigned role "${role.name}" to user "${member.user.tag}"!`))
.catch(error => logger.error(`failed to assign role "${role.name}" to user "${member.user.tag}": ${error}`));
};
+10 -2
View File
@@ -2,7 +2,7 @@
const fs = require('fs');
const path = require('path');
const { Events } = require('discord.js');
const { ActivityType, Events } = require('discord.js');
const logger = require('../utils/logger');
module.exports = {
@@ -20,9 +20,17 @@ module.exports = {
const variables = Object.keys(db);
logger.info(`[db] loaded variables from db.json: ${variables.length > 0 ? variables.join(', ') : 'none'}`);
// cache rolimons item list
fetch('https://api.rolimons.com/items/v2/itemdetails')
.then(res => res.ok ? res.json() : Promise.reject(new Error('Failed to fetch rolimons item list')))
.then(data => {
fs.writeFileSync(path.join(process.cwd(), 'items.json'), JSON.stringify(data, null, 2));
})
.catch(err => logger.error('[ready] error fetching rolimons item list:', err));
logger.info(`[ready] logged in as ${client.user.tag} (${client.user.id})`);
logger.info(`[ready] serving ${client.guilds.cache.size} guild(s)`);
client.user.setActivity(`${client.guilds.cache.size} servers`, { type: 'WATCHING' });
client.user.setActivity(`watching over ${client.guilds.cache.size} servers`, { type: ActivityType.Watching });
},
};
+17 -3
View File
@@ -12,9 +12,23 @@ const logger = require('../utils/logger');
*/
function loadCommands(client) {
const commandsPath = path.join(__dirname, '..', 'commands');
const commandFiles = fs
.readdirSync(commandsPath)
.filter((file) => file.endsWith('.js'));
// Recursively get all .js files in commandsPath
function getAllCommandFiles(dir) {
let results = [];
const list = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of list) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
results = results.concat(getAllCommandFiles(fullPath));
} else if (entry.isFile() && entry.name.endsWith('.js')) {
results.push(path.relative(commandsPath, fullPath));
}
}
return results;
}
const commandFiles = getAllCommandFiles(commandsPath);
let loaded = 0;
+8 -1
View File
@@ -18,7 +18,7 @@ const logger = createLogger({
format.ms(),
format.errors({ stack: true }),
format.splat(),
format.json()
format.json({ space: 2 })
),
transports: [
new transports.Console({
@@ -41,6 +41,13 @@ const logger = createLogger({
new transports.File({
filename: path.join(logs_dir, 'combined.log'),
format: format.combine(
format.timestamp(),
format.ms(),
format.errors({ stack: true }),
format.splat(),
format.json({ space: 2 })
),
}),
new transports.File({