Compare commits

...
10 Commits
22 changed files with 2056 additions and 34284 deletions

No files matched your search

+146 -142
View File
@@ -1,142 +1,146 @@
# Logs # Logs
logs logs
*.log *.log
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
lerna-debug.log* lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html) *.bat
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Diagnostic reports (https://nodejs.org/api/report.html)
# Runtime data report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
pids
*.pid # Runtime data
*.seed pids
*.pid.lock *.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover *.pid.lock
lib-cov
# Directory for instrumented libs generated by jscoverage/JSCover
# Coverage directory used by tools like istanbul lib-cov
coverage
*.lcov # Coverage directory used by tools like istanbul
coverage
# nyc test coverage *.lcov
.nyc_output
# nyc test coverage
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .nyc_output
.grunt
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
# Bower dependency directory (https://bower.io/) .grunt
bower_components
# Bower dependency directory (https://bower.io/)
# node-waf configuration bower_components
.lock-wscript
# node-waf configuration
# Compiled binary addons (https://nodejs.org/api/addons.html) .lock-wscript
build/Release
# Compiled binary addons (https://nodejs.org/api/addons.html)
# Dependency directories build/Release
node_modules/
jspm_packages/ # Dependency directories
node_modules/
# Snowpack dependency directory (https://snowpack.dev/) jspm_packages/
web_modules/
# Snowpack dependency directory (https://snowpack.dev/)
# TypeScript cache web_modules/
*.tsbuildinfo
# TypeScript cache
# Optional npm cache directory *.tsbuildinfo
.npm
# Optional npm cache directory
# Optional eslint cache .npm
.eslintcache
# Optional eslint cache
# Optional stylelint cache .eslintcache
.stylelintcache
# Optional stylelint cache
# Optional REPL history .stylelintcache
.node_repl_history
# Optional REPL history
# Output of 'npm pack' .node_repl_history
*.tgz
# Output of 'npm pack'
# Yarn Integrity file *.tgz
.yarn-integrity
# Yarn Integrity file
# database .yarn-integrity
db.json
# database
# dotenv environment variable files db.json
.env
.env.* # dotenv environment variable files
!.env.example .env
.env.*
# parcel-bundler cache (https://parceljs.org/) !.env.example
.cache
.parcel-cache # parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output .parcel-cache
.next
out # Next.js build output
.next
# Nuxt.js build / generate output out
.nuxt
dist # Nuxt.js build / generate output
.nuxt
# Gatsby files dist
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js # Gatsby files
# https://nextjs.org/blog/next-9-1#public-directory-support .cache/
# public # Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# vuepress build output # public
.vuepress/dist
# vuepress build output
# vuepress v2.x temp and cache directory .vuepress/dist
.temp
.cache # vuepress v2.x temp and cache directory
.temp
# Sveltekit cache directory .cache
.svelte-kit/
# Sveltekit cache directory
# vitepress build output .svelte-kit/
**/.vitepress/dist
# vitepress build output
# vitepress cache directory **/.vitepress/dist
**/.vitepress/cache
# vitepress cache directory
# Docusaurus cache and generated files **/.vitepress/cache
.docusaurus
# Docusaurus cache and generated files
# Serverless directories .docusaurus
.serverless/
# Serverless directories
# FuseBox cache .serverless/
.fusebox/
# FuseBox cache
# DynamoDB Local files .fusebox/
.dynamodb/
# DynamoDB Local files
# Firebase cache directory .dynamodb/
.firebase/
# Firebase cache directory
# TernJS port file .firebase/
.tern-port
# TernJS port file
# Stores VSCode versions used for testing VSCode extensions .tern-port
.vscode-test
# Stores VSCode versions used for testing VSCode extensions
# yarn v3 .vscode-test
.pnp.*
.yarn/* # yarn v3
!.yarn/patches .pnp.*
!.yarn/plugins .yarn/*
!.yarn/releases !.yarn/patches
!.yarn/sdks !.yarn/plugins
!.yarn/versions !.yarn/releases
!.yarn/sdks
# Vite logs files !.yarn/versions
vite.config.js.timestamp-*
vite.config.ts.timestamp-* # Vite logs files
vite.config.js.timestamp-*
items.json
db.json
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

+1 -2
View File
@@ -1,7 +1,6 @@
{ {
"autoroles": { "autoroles": {
"1133441692196950101": "1192595134026616894", "1133441692196950101": "1192595134026616894"
"1487585267777933455": "1487585267777933459"
}, },
"autobans": { "autobans": {
"1133441692196950101": "1264634623950848158" "1133441692196950101": "1264634623950848158"
-33208
View File
File diff suppressed because it is too large. Load diff
+384 -423
View File
@@ -1,423 +1,384 @@
'use strict'; 'use strict';
const { time } = require("discord.js"); const { time } = require("discord.js");
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
const logger = require('../../utils/logger'); const logger = require('../../utils/logger');
const env = process.env;
async function fetch_avatar(id) {
const res = await fetch(`https://thumbnails.roproxy.com/v1/users/avatar?userIds=${id}&size=420x420&format=Png&isCircular=false`, { async function fetch_avatar(id) {
method: 'GET', const res = await fetch(`https://thumbnails.roproxy.com/v1/users/avatar?userIds=${id}&size=420x420&format=Png&isCircular=false`, {
headers: { 'Content-Type': 'application/json' } method: 'GET',
}); headers: { 'Content-Type': 'application/json' }
});
const data = await res.json();
logger.debug(`[fetch_avatar] ${JSON.stringify(data)}`); const data = await res.json();
if (data.data && data.data[0] && data.data[0].imageUrl) { logger.debug(`[fetch_avatar] ${JSON.stringify(data)}`);
logger.debug(`[fetch_avatar] fetched avatar for user id ${id}: ${data.data[0].imageUrl}`); if (data.data && data.data[0] && data.data[0].imageUrl) {
return data.data[0].imageUrl; logger.debug(`[fetch_avatar] fetched avatar for user id ${id}: ${data.data[0].imageUrl}`);
} else { return data.data[0].imageUrl;
return null; } else {
} return null;
} }
}
async function fetch_item_thumb(id) {
const res = await fetch(`https://thumbnails.roproxy.com/v1/assets?assetIds=${id}&returnPolicy=PlaceHolder&size=420x420&format=Png&isCircular=false`, { async function fetch_item_thumb(id) {
method: 'GET', const res = await fetch(`https://thumbnails.roproxy.com/v1/assets?assetIds=${id}&returnPolicy=PlaceHolder&size=420x420&format=Png&isCircular=false`, {
headers: { 'Content-Type': 'application/json' } method: 'GET',
}); headers: { 'Content-Type': 'application/json' }
});
const data = await res.json();
logger.debug(`[fetch_item_thumb] ${JSON.stringify(data)}`); const data = await res.json();
if (data.data && data.data[0] && data.data[0].imageUrl) { logger.debug(`[fetch_item_thumb] ${JSON.stringify(data)}`);
logger.debug(`[fetch_item_thumb] fetched item thumbnail for item id ${id}: ${data.data[0].imageUrl}`); if (data.data && data.data[0] && data.data[0].imageUrl) {
return data.data[0].imageUrl; logger.debug(`[fetch_item_thumb] fetched item thumbnail for item id ${id}: ${data.data[0].imageUrl}`);
} else { return data.data[0].imageUrl;
return null; } else {
} return null;
} }
}
function to_unix_timestamp(dateString) {
const unixSeconds = Math.floor(new Date(dateString).getTime() / 1000); function to_unix_timestamp(dateString) {
logger.debug(unixSeconds); const unixSeconds = Math.floor(new Date(dateString).getTime() / 1000);
return unixSeconds; logger.debug(unixSeconds);
} return unixSeconds;
}
module.exports = {
name: 'rbx', module.exports = {
description: `various commands related to roblox`, name: 'rbx',
aliases: ['roblox'], description: `various commands related to roblox`,
usage: 'rbx user <username>\n.rbx item <item id>\n.rbx group <group id>\n.rbx game <game id>\n.rbx avatar <username>', 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 = {}; async execute(client, message) {
const args = message.content.split(' ').slice(1); var embed = {};
const args = message.content.split(' ').slice(1);
if (args.length < 2) {
return message.reply({ if (args.length < 2) {
content: null, embeds: [{ return message.reply({
color: 0x8aa2b3, content: null, embeds: [{
author: { color: 0x8aa2b3,
name: message.author.username, author: {
icon_url: message.author.displayAvatarURL(), name: message.author.username,
}, icon_url: message.author.displayAvatarURL(),
description: '<:no:1481811955592466492> please provide a subcommand and an argument!', },
footer: { description: '<:no:1481811955592466492> please provide a subcommand and an argument!',
text: `twitter @ ${message.guild.name}`, footer: {
icon_url: client.user.displayAvatarURL(), text: `twitter @ ${message.guild.name}`,
}, icon_url: client.user.displayAvatarURL(),
timestamp: new Date(), },
}] timestamp: new Date(),
}); }]
} });
}
const subcommand = args[0].toLowerCase();
const argument = args.slice(1).join(' '); const subcommand = args[0].toLowerCase();
const argument = args.slice(1).join(' ');
let sent = await message.reply({
content: null, embeds: [{ let sent = await message.reply({
color: 0x8aa2b3, content: null, embeds: [{
title: '', color: 0x8aa2b3,
author: { title: '',
name: message.author.username, author: {
icon_url: message.author.displayAvatarURL(), name: message.author.username,
}, icon_url: message.author.displayAvatarURL(),
description: '<a:loading_icon:1366587619890302998> fetching...', },
footer: { description: '<a:loading_icon:1366587619890302998> fetching...',
text: `twitter @ ${message.guild.name}`, footer: {
icon_url: client.user.displayAvatarURL(), text: `twitter @ ${message.guild.name}`,
}, icon_url: client.user.displayAvatarURL(),
timestamp: new Date(), },
}] timestamp: new Date(),
}); }]
});
let latency = Date.now() - message.createdTimestamp;
let apiLatency = Math.round(client.ws.ping); const res = await fetch(`https://users.roblox.com/v1/usernames/users`, {
method: 'POST',
if (subcommand === 'user' || subcommand === 'u') { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.ROBLOSECURITY}` },
try { body: JSON.stringify({ usernames: [argument], excludeBannedUsers: false })
const res = await fetch(`https://users.roproxy.com/v1/usernames/users`, { });
method: 'POST', const data = await res.json();
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.ROBLOSECURITY}` }, logger.debug(`[rbx] fetched user data (roblox, standard) for "${argument}": ${JSON.stringify(data)}`);
body: JSON.stringify({ usernames: [argument], excludeBannedUsers: false })
}); if (!data.data || !data.data[0]) {
const data = await res.json(); throw new Error(`user ${argument} not found...`);
logger.debug(`[rbx] fetched user data (roblox, standard) for "${argument}": ${JSON.stringify(data)}`); }
const extra_res = await fetch(`https://users.roblox.com/v1/users/${data.data[0].id}`, {
if (!data.data || !data.data[0]) { method: 'GET',
throw new Error('User not found'); headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.ROBLOSECURITY}` }
} });
const extra_res = await fetch(`https://users.roproxy.com/v1/users/${data.data[0].id}`, { const extra_data = await extra_res.json();
method: 'GET', logger.debug(`[rbx] fetched user data (roblox, extra) for "${argument}": ${JSON.stringify(extra_data)}`);
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.ROBLOSECURITY}` }
}); const r_res = await fetch(`https://api.rolimons.com/players/v1/playerinfo/${data.data[0].id}`, {
const extra_data = await extra_res.json(); method: 'GET',
logger.debug(`[rbx] fetched user data (roblox, extra) for "${argument}": ${JSON.stringify(extra_data)}`); headers: { 'Content-Type': 'application/json' }
});
const r_res = await fetch(`https://api.rolimons.com/players/v1/playerinfo/${data.data[0].id}`, { let r_data = {};
method: 'GET', if (r_res.ok) {
headers: { 'Content-Type': 'application/json' } r_data = await r_res.json();
}); } else {
let r_data = {}; logger.warn(`[rbx] Failed to fetch rolimons playerinfo for id ${data.data[0].id}: HTTP ${r_res.status}`);
if (r_res.ok) { r_data = {};
r_data = await r_res.json(); }
} else { logger.debug(`[rbx] fetched user data (rolimons) for "${argument}": ${JSON.stringify(r_data)}`);
logger.warn(`[rbx] Failed to fetch rolimons playerinfo for id ${data.data[0].id}: HTTP ${r_res.status}`);
r_data = {}; const ra_res = await fetch(`https://api.rolimons.com/players/v1/playerassets/${data.data[0].id}`, {
} method: 'GET',
logger.debug(`[rbx] fetched user data (rolimons) for "${argument}": ${JSON.stringify(r_data)}`); headers: { 'Content-Type': 'application/json' }
});
const ra_res = await fetch(`https://api.rolimons.com/players/v1/playerassets/${data.data[0].id}`, { const ra_data = await ra_res.json();
method: 'GET', logger.debug(`[rbx] fetched user data (rolimons, assets) for "${argument}": ${JSON.stringify(ra_data)}`);
headers: { 'Content-Type': 'application/json' }
}); if (subcommand === 'user' || subcommand === 'u') {
const ra_data = await ra_res.json(); try {
logger.debug(`[rbx] fetched user data (rolimons, assets) for "${argument}": ${JSON.stringify(ra_data)}`); if (!data.data || !data.data[0]) {
embed = {
if (!data.data || !data.data[0]) { color: 0x8aa2b3,
embed = { author: {
color: 0x8aa2b3, name: message.author.username,
author: { icon_url: message.author.displayAvatarURL(),
name: message.author.username, },
icon_url: message.author.displayAvatarURL(), description: '<:no:1481811955592466492> could not find a roblox user with that username!',
}, footer: {
description: '<:no:1481811955592466492> could not find a roblox user with that username!', text: `twitter @ ${message.guild.name}`,
footer: { icon_url: client.user.displayAvatarURL(),
text: `twitter @ ${message.guild.name}`, },
icon_url: client.user.displayAvatarURL(), timestamp: new Date(),
}, }
timestamp: new Date(), } else {
} const user = data.data[0];
} else { const avatarUrl = await fetch_avatar(user.id);
const user = data.data[0];
const avatarUrl = await fetch_avatar(user.id); const hasCheckmark = user.hasVerifiedBadge;
const hasPremium = r_data.premium;
const hasCheckmark = user.hasVerifiedBadge; const isOnline = ra_data.isOnline;
const hasPremium = r_data.premium;
const isOnline = ra_data.isOnline; const viewInvRes = await fetch(`https://inventory.roproxy.com/v1/users/${user.id}/can-view-inventory`);
const isInvPublic = (await viewInvRes.json()).canView;
const viewInvRes = await fetch(`https://inventory.roproxy.com/v1/users/${user.id}/can-view-inventory`);
const isInvPublic = (await viewInvRes.json()).canView; const formatted_desc = (extra_data.description || "no description")
.split('\n')
const formatted_desc = (extra_data.description || "no description") .map(line => line.trim() ? `-# *${line}*` : '')
.split('\n') .join('\n');
.map(line => line.trim() ? `-# *${line}*` : '')
.join('\n'); embed = {
color: 0x8aa2b3,
embed = { author: {
color: 0x8aa2b3, name: message.author.username,
author: { icon_url: message.author.displayAvatarURL(),
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${formatted_desc}`,
}, fields: [
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${formatted_desc}`, { name: 'id', value: String(extra_data.id), inline: true },
fields: [ { name: 'rap', value: String(r_data.rap), inline: true },
{ name: 'id', value: String(extra_data.id), inline: true }, { name: 'value', value: String(r_data.value), inline: true },
{ name: 'rap', value: String(r_data.rap), inline: true }, { name: 'inventory', value: `${isInvPublic ? 'public' : 'private'}`, inline: true },
{ name: 'value', value: String(r_data.value), inline: true }, { name: 'terminated', value: String(r_data.terminated), inline: true },
{ name: 'inventory', value: `${isInvPublic ? 'public' : 'private'}`, inline: true }, { name: 'date created', value: `<t:${to_unix_timestamp(String(extra_data.created))}:f>`, inline: true },
{ name: 'terminated', value: String(r_data.terminated), inline: true }, { name: 'last online', value: `${isOnline ? "now" : "<t:" + r_data.last_online + ":f>"}`, 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,
thumbnail: { height: 420,
url: avatarUrl, width: 420,
height: 420, },
width: 420, footer: {
}, text: `twitter @ ${message.guild.name}`,
footer: { icon_url: client.user.displayAvatarURL(),
text: `twitter @ ${message.guild.name}`, },
icon_url: client.user.displayAvatarURL(), timestamp: new Date(),
}, };
timestamp: new Date(), }
}; } catch (err) {
} logger.error(`[rbx] failed to fetch user data for "${argument}":`, err);
} catch (err) { embed = {
logger.error(`[rbx] failed to fetch user data for "${argument}":`, err); color: 0x8aa2b3,
embed = { author: {
color: 0x8aa2b3, name: message.author.username,
author: { icon_url: message.author.displayAvatarURL(),
name: message.author.username, },
icon_url: message.author.displayAvatarURL(), description: `<:no:1481811955592466492> could not fetch data for user "${argument}"!`,
}, footer: {
description: `<:no:1481811955592466492> could not fetch data for user "${argument}"!`, text: `twitter @ ${message.guild.name}`,
footer: { icon_url: client.user.displayAvatarURL(),
text: `twitter @ ${message.guild.name}`, },
icon_url: client.user.displayAvatarURL(), timestamp: new Date(),
}, }
timestamp: new Date(), }
} } else if (subcommand === 'item') {
} try {
} else if (subcommand === 'avatar') { const res = await fetch(`https://economy.roproxy.com/v2/assets/${argument}/details`, {
try { method: 'GET',
const res = await fetch(`https://thumbnails.roproxy.com/v1/users/avatar?userIds=${id}&size=420x420&format=Png&isCircular=false`, { headers: {
method: 'GET', 'Content-Type': 'application/json',
headers: { 'Content-Type': 'application/json' } 'Cookie': `.ROBLOSECURITY=${process.env.ROBLOSECURITY}`
}); }
});
const data = await res.json(); const p_res = await fetch(`https://thumbnails.roproxy.com/v1/assets?assetIds=${argument}&returnPolicy=PlaceHolder&size=420x420&format=Png&isCircular=false`, {
embed = { method: 'GET',
color: 0x8aa2b3, headers: {
author: { 'Content-Type': 'application/json',
name: message.author.username, 'Cookie': `.ROBLOSECURITY=${process.env.ROBLOSECURITY}`
icon_url: message.author.displayAvatarURL(), }
}, });
description: `${}`, const resale_res = await fetch(`https://economy.roproxy.com/v1/assets/${argument}/resale-data`, {
footer: { method: 'GET',
text: `twitter @ ${message.guild.name}`, headers: {
icon_url: client.user.displayAvatarURL(), 'Content-Type': 'application/json',
}, 'Cookie': `.ROBLOSECURITY=${process.env.ROBLOSECURITY}`
timestamp: new Date(), }
}; });
} catch (err) {
logger.error(`[rbx] failed to fetch avatar for user id "${argument}":`, err); const item = await res.json();
embed = { logger.debug(`[rbx] fetched item data (roblox) for "${argument}": ${JSON.stringify(item)}`);
color: 0x8aa2b3,
author: { const resale_data = await resale_res.json();
name: message.author.username, logger.debug(`[rbx] fetched resale data for "${argument}": ${JSON.stringify(resale_data)}`);
icon_url: message.author.displayAvatarURL(),
}, let itemsJson;
description: `<:no:1481811955592466492> could not fetch avatar data for user "${argument}"!`, try {
footer: { const itemsPath = path.resolve(process.cwd(), 'items.json');
text: `twitter @ ${message.guild.name}`, const itemsRaw = fs.readFileSync(itemsPath, 'utf8');
icon_url: client.user.displayAvatarURL(), itemsJson = JSON.parse(itemsRaw);
}, if (itemsJson.items && itemsJson.items[argument]) {
timestamp: new Date(), logger.debug(`[rbx] found item ${argument} in items.json: ${JSON.stringify(itemsJson.items[argument])}`);
}; }
} } catch (e) {
} else if (subcommand === 'item') { logger.warn(`[rbx] could not read items.json: ${e.message}`);
try { }
const res = await fetch(`https://economy.roproxy.com/v2/assets/${argument}/details`, {
method: 'GET', let item_e = itemsJson.items[argument];
headers: {
'Content-Type': 'application/json', var hasAcronym = false;
'Cookie': `.ROBLOSECURITY=${process.env.ROBLOSECURITY}` if (item_e[1] = "") {
} hasAcronym = false;
}); } else {
const p_res = await fetch(`https://thumbnails.roproxy.com/v1/assets?assetIds=${argument}&returnPolicy=PlaceHolder&size=420x420&format=Png&isCircular=false`, { hasAcronym = true;
method: 'GET', }
headers: {
'Content-Type': 'application/json', const item_thumb = await fetch_item_thumb(item.AssetId);
'Cookie': `.ROBLOSECURITY=${process.env.ROBLOSECURITY}` const is_offsale = item.IsForSale;
}
}); const is_limited = item.IsLimited;
const resale_res = await fetch(`https://economy.roproxy.com/v1/assets/${argument}/resale-data`, { const is_limited_unique = item.IsLimitedUnique;
method: 'GET',
headers: { const is_limited_at_all = is_limited || is_limited_unique;
'Content-Type': 'application/json',
'Cookie': `.ROBLOSECURITY=${process.env.ROBLOSECURITY}` const limited_string = is_limited ? (is_limited_unique ? "true (unique)" : "true") : "false";
}
}); embed = {
color: 0x8aa2b3,
const item = await res.json(); author: {
logger.debug(`[rbx] fetched item data (roblox) for "${argument}": ${JSON.stringify(item)}`); name: message.author.username,
icon_url: message.author.displayAvatarURL(),
const resale_data = await resale_res.json(); },
logger.debug(`[rbx] fetched resale data for "${argument}": ${JSON.stringify(resale_data)}`); description: `[${item.Name}](https://roblox.com/catalog/${argument}) ${hasAcronym ? item_e[1] : ""}\n-# *${item.Description}*`,
fields: [
let itemsJson; { name: "id", value: `${item.AssetId}`, inline: true },
try { { name: "price", value: `${is_offsale ? "offsale" : item.PriceInRobux}`, inline: true },
const itemsPath = path.resolve(process.cwd(), 'items.json'); { name: "limited", value: `${limited_string}`, inline: true },
const itemsRaw = fs.readFileSync(itemsPath, 'utf8'); { name: "rap", value: `${is_limited_at_all ? resale_data.recentAveragePrice : "0"}`, inline: true },
itemsJson = JSON.parse(itemsRaw); { name: "creator", value: `${item.Creator.Name}`, inline: true },
if (itemsJson.items && itemsJson.items[argument]) { ],
logger.debug(`[rbx] found item ${argument} in items.json: ${JSON.stringify(itemsJson.items[argument])}`); thumbnail: {
} url: item_thumb,
} catch (e) { height: 420,
logger.warn(`[rbx] could not read items.json: ${e.message}`); width: 420
} },
footer: {
let item_e = itemsJson.items[argument]; text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
var hasAcronym = false; },
if (item_e[1] = "") { timestamp: new Date(),
hasAcronym = false; };
} else { } catch (err) {
hasAcronym = true; logger.error(`[rbx] failed to fetch item data for "${argument}":`, err);
} embed = {
color: 0x8aa2b3,
const item_thumb = await fetch_item_thumb(item.AssetId); author: {
const is_offsale = item.IsForSale; name: message.author.username,
icon_url: message.author.displayAvatarURL(),
const is_limited = item.IsLimited; },
const is_limited_unique = item.IsLimitedUnique; description: `<:no:1481811955592466492> could not fetch data for item "${argument}"!`,
footer: {
const is_limited_at_all = is_limited || is_limited_unique; text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
const limited_string = is_limited ? (is_limited_unique ? "true (unique)" : "true") : "false"; },
timestamp: new Date(),
embed = { }
color: 0x8aa2b3, }
author: { } else if (subcommand === 'group') {
name: message.author.username, try {
icon_url: message.author.displayAvatarURL(), const res = await fetch(`https://groups.roproxy.com/v1/groups/${argument}`);
}, const p_res = await fetch(`https://thumbnails.roproxy.com/v1/groups/icons?groupIds=${argument}&size=420x420&format=Png&isCircular=false`);
description: `[${item.Name}](https://roblox.com/catalog/${argument}) ${hasAcronym ? item_e[1] : ""}\n-# *${item.Description}*`,
fields: [ if (!res.ok) throw new Error();
{ name: "id", value: `${item.AssetId}`, inline: true }, if (!p_res.ok) throw new Error();
{ name: "price", value: `${is_offsale ? "offsale" : item.PriceInRobux}`, inline: true },
{ name: "limited", value: `${limited_string}`, inline: true }, const group = await res.json();
{ name: "rap", value: `${is_limited_at_all ? resale_data.recentAveragePrice : "0"}`, inline: true }, const p_json = await p_res.json();
{ name: "creator", value: `${item.Creator.Name}`, inline: true }, logger.debug(`${JSON.stringify(p_json)}`);
], const thumb = (p_json && p_json.data && p_json.data[0]) ? p_json.data[0].imageUrl : null;
thumbnail: {
url: item_thumb, embed = {
height: 420, color: 0x8aa2b3,
width: 420 author: {
}, name: message.author.username,
footer: { icon_url: message.author.displayAvatarURL(),
text: `twitter @ ${message.guild.name}`, },
icon_url: client.user.displayAvatarURL(), description: `**[${group.name}](https://roblox.com/communities/${argument})**\n-# *${group.description || "no description"}*`,
}, thumbnail: {
timestamp: new Date(), url: thumb,
}; height: 420,
} catch (err) { width: 420
logger.error(`[rbx] failed to fetch item data for "${argument}":`, err); },
embed = { fields: [
color: 0x8aa2b3, { name: "members", value: `${group.memberCount}`, inline: true },
author: { { name: "owner", value: `${group.owner.username}`, inline: true },
name: message.author.username, { name: "shout", value: `${group.shout}`, inline: true },
icon_url: message.author.displayAvatarURL(), { name: "open", value: `${group.publicEntryAllowed}`, inline: true },
}, { name: "verified", value: `${group.hasVerifiedBadge}`, inline: true },
description: `<:no:1481811955592466492> could not fetch data for item "${argument}"!`, ],
footer: { footer: {
text: `twitter @ ${message.guild.name}`, text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(), icon_url: client.user.displayAvatarURL(),
}, },
timestamp: new Date(), timestamp: new Date(),
} };
} } catch (err) {
} else if (subcommand === 'group') { logger.error(`[rbx] failed to fetch group data for "${argument}":`, err);
try { embed.title = 'Error';
const res = await fetch(`https://groups.roproxy.com/v1/groups/${argument}`); embed.description = 'Failed to fetch group data.';
const p_res = await fetch(`https://thumbnails.roproxy.com/v1/groups/icons?groupIds=${argument}&size=420x420&format=Png&isCircular=false`); }
} else if (subcommand === 'game') {
if (!res.ok) throw new Error(); try {
if (!p_res.ok) throw new Error(); const res = await fetch(`https://games.roproxy.com/v1/games?universeIds=${argument}`);
if (!res.ok) throw new Error();
const group = await res.json(); const data = await res.json();
const p_json = await p_res.json(); if (!data.data || !data.data[0]) {
logger.debug(`${JSON.stringify(p_json)}`); embed.title = 'Game Not Found';
const thumb = (p_json && p_json.data && p_json.data[0]) ? p_json.data[0].imageUrl : null; embed.description = `Could not find a Roblox game with the universe ID **${argument}**.`;
} else {
embed = { const game = data.data[0];
color: 0x8aa2b3, embed.title = `Roblox Game: ${game.name}`;
author: { embed.description = game.description || 'No description.';
name: message.author.username, embed.thumbnail.url = `https://www.roproxy.com/asset-thumbnail/image?assetId=${game.rootPlaceId}&width=420&height=420&format=png`;
icon_url: message.author.displayAvatarURL(), embed.fields = [
}, { name: 'Creator', value: game.creator.name, inline: true },
description: `**[${group.name}](https://roblox.com/communities/${argument})**\n-# *${group.description || "no description"}*`, { name: 'Playing', value: game.playing.toLocaleString(), inline: true },
thumbnail: { { name: 'Visits', value: game.visits.toLocaleString(), inline: true }
url: thumb, ];
height: 420, }
width: 420 } catch (err) {
}, embed.title = 'Error';
fields: [ embed.description = 'Failed to fetch game data.';
{ name: "members", value: `${group.memberCount}`, inline: true }, }
{ name: "owner", value: `${group.owner.username}`, inline: true }, }/* else {
{ name: "shout", value: `${group.shout}`, inline: true }, embed.title = 'invalid subcommand';
{ name: "open", value: `${group.publicEntryAllowed}`, inline: true }, embed.description = 'Valid subcommands: `user`, `item`, `group`, `game`';
{ name: "verified", value: `${group.hasVerifiedBadge}`, inline: true }, }*/
],
footer: { await sent.edit({ content: null, embeds: [embed] });
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.roproxy.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.roproxy.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] });
},
};
+84 -76
View File
@@ -1,76 +1,84 @@
const { EmbedBuilder } = require('discord.js'); const { EmbedBuilder } = require('discord.js');
const db = require('../../db.json');
'use strict';
'use strict';
module.exports = {
name: 'help', const get_guild_prefix = (guildId) => {
description: 'lists all available commands or shows info about a specific one', return db.prefixes?.[guildId] || null;
aliases: ['h', 'commands'], };
usage: 'help [command]',
module.exports = {
execute(client, message, args) { name: 'help',
var embed; description: 'lists all available commands or shows info about a specific one',
const prefix = process.env.PREFIX || '!'; aliases: ['h', 'commands'],
usage: 'help [command]',
// Show info for a specific command
if (args.length > 0) { execute(client, message, args) {
const name = args[0].toLowerCase(); var embed;
const command = client.commands.get(name);
let prefix = process.env.PREFIX;
if (!command) { if (message.guild) {
return message.reply(`no command found for \`${name}\`.`); const guildPrefix = get_guild_prefix(message.guild.id);
} if (guildPrefix) prefix = guildPrefix;
}
embed = {
color: 0x8aa2b3, if (args.length > 0) {
title: '', const name = args[0].toLowerCase();
author: { const command = client.commands.get(name);
name: message.author.username,
icon_url: message.author.displayAvatarURL(), if (!command) {
}, return message.reply(`no command found for \`${name}\`.`);
description: `**${prefix}${command.name}**\n-# ${command.description || 'no description'}`, }
fields: [
{ name: 'aliases', value: command.aliases.map((a) => `\`${prefix}${a}\``).join(', ') }, embed = {
{ name: 'usage', value: `\`${prefix}${command.usage}\`` } color: 0x8aa2b3,
], title: '',
footer: { author: {
text: `twitter @ ${message.guild.name}`, name: message.author.username,
icon_url: client.user.displayAvatarURL(), icon_url: message.author.displayAvatarURL(),
}, },
timestamp: new Date(), description: `**${prefix}${command.name}**\n-# ${command.description || 'no description'}`,
}; fields: [
{ name: 'aliases', value: command.aliases.map((a) => `\`${prefix}${a}\``).join(', ') },
return message.reply({ embeds: [embed] }); { name: 'usage', value: `\`${prefix}${command.usage}\`` }
} ],
footer: {
// Build a deduplicated command list (ignore aliases pointing to the same command) text: `twitter @ ${message.guild.name}`,
const seen = new Set(); icon_url: client.user.displayAvatarURL(),
const commandList = []; },
timestamp: new Date(),
for (const command of client.commands.values()) { };
if (seen.has(command.name)) continue;
seen.add(command.name); return message.reply({ embeds: [embed] });
commandList.push(`\`${prefix}${command.name}\` — ${command.description || 'no description'}`); }
}
const seen = new Set();
embed = { const commandList = [];
color: 0x8aa2b3,
title: '', for (const command of client.commands.values()) {
author: { if (seen.has(command.name)) continue;
name: message.author.username, seen.add(command.name);
icon_url: message.author.displayAvatarURL(), commandList.push(`\`${prefix}${command.name}\` — ${command.description || 'no description'}`);
}, }
description: `-# use ${prefix}help <command> for more details`,
fields: [ embed = {
{ name: 'commands', value: commandList.join('\n') }, color: 0x8aa2b3,
], title: '',
footer: { author: {
text: `twitter @ ${message.guild.name}`, name: message.author.username,
icon_url: client.user.displayAvatarURL(), icon_url: message.author.displayAvatarURL(),
}, },
timestamp: new Date(), description: `-# use ${prefix}help <command> for more details`,
}; fields: [
{ name: 'commands', value: commandList.join('\n') },
return message.reply({ embeds: [embed] }); ],
}, footer: {
}; text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
};
return message.reply({ embeds: [embed] });
},
};
+101 -101
View File
@@ -1,102 +1,102 @@
'use strict'; 'use strict';
const { time } = require("discord.js"); const { time } = require("discord.js");
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
module.exports = { module.exports = {
name: 'autoban', name: 'autoban',
description: `automatically ban a user if they join the server`, description: `automatically ban a user if they join the server`,
aliases: ['ab', 'banish'], aliases: ['ab', 'banish'],
usage: 'ban <user> [reason] [duration]', usage: 'ban <user> [reason] [duration]',
async execute(client, message, args) { async execute(client, message, args) {
if (!message.member.permissions.has('BanMembers')) { if (!message.member.permissions.has('BanMembers')) {
return message.reply({ return message.reply({
content: null, embeds: [{ content: null, embeds: [{
color: 0x8aa2b3, color: 0x8aa2b3,
author: { author: {
name: message.author.username, name: message.author.username,
icon_url: message.author.displayAvatarURL(), icon_url: message.author.displayAvatarURL(),
}, },
description: '<:no:1481811955592466492> you do not have permission to use this command!', description: '<:no:1481811955592466492> you do not have permission to use this command!',
footer: { footer: {
text: `twitter @ ${message.guild.name}`, text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(), icon_url: client.user.displayAvatarURL(),
}, },
timestamp: new Date(), timestamp: new Date(),
}] }]
}); });
} }
const user = message.mentions.users.first() || await client.users.fetch(args[0]).catch(() => null); const user = message.mentions.users.first() || await client.users.fetch(args[0]).catch(() => null);
if (!user) { if (!user) {
return message.reply({ return message.reply({
content: null, embeds: [{ content: null, embeds: [{
color: 0x8aa2b3, color: 0x8aa2b3,
author: { author: {
name: message.author.username, name: message.author.username,
icon_url: message.author.displayAvatarURL(), icon_url: message.author.displayAvatarURL(),
}, },
description: '<:no:1481811955592466492> please specify a user to autoban!', description: '<:no:1481811955592466492> please specify a user to autoban!',
footer: { footer: {
text: `twitter @ ${message.guild.name}`, text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(), icon_url: client.user.displayAvatarURL(),
}, },
timestamp: new Date(), timestamp: new Date(),
}] }]
}); });
} }
const reason = args.slice(1).join(' ') || 'autobanned (twitter)'; const reason = args.slice(1).join(' ') || 'autobanned (twitter)';
const dbPath = path.join(__dirname, '..', '..', '..', 'db.json'); const dbPath = path.join(__dirname, '..', '..', '..', 'db.json');
let db = {}; let db = {};
if (fs.existsSync(dbPath)) { if (fs.existsSync(dbPath)) {
db = JSON.parse(fs.readFileSync(dbPath, 'utf8')); db = JSON.parse(fs.readFileSync(dbPath, 'utf8'));
} }
if (!db.autobans) { if (!db.autobans) {
db.autobans = {}; db.autobans = {};
} }
db.autobans[message.guild.id] = user.id; db.autobans[message.guild.id] = user.id;
try { try {
await fs.writeFileSync(dbPath, JSON.stringify(db, null, 2)); await fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
message.reply({ message.reply({
content: null, embeds: [{ content: null, embeds: [{
color: 0x8aa2b3, color: 0x8aa2b3,
author: { author: {
name: message.author.username, name: message.author.username,
icon_url: message.author.displayAvatarURL(), icon_url: message.author.displayAvatarURL(),
}, },
description: `<:check:1481811973447356496> ${user.username} has been added to the autoban list!`, description: `<:check:1481811973447356496> ${user.username} has been added to the autoban list!`,
footer: { footer: {
text: `twitter @ ${message.guild.name}`, text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(), icon_url: client.user.displayAvatarURL(),
}, },
timestamp: new Date(), timestamp: new Date(),
}] }]
}); });
} catch (error) { } catch (error) {
console.error(error); console.error(error);
message.reply({ message.reply({
content: null, embeds: [{ content: null, embeds: [{
color: 0x8aa2b3, color: 0x8aa2b3,
author: { author: {
name: message.author.username, name: message.author.username,
icon_url: message.author.displayAvatarURL(), icon_url: message.author.displayAvatarURL(),
}, },
description: '<:no:1481811955592466492> an error occurred while trying to add the user to the autoban list!', description: '<:no:1481811955592466492> an error occurred while trying to add the user to the autoban list!',
footer: { footer: {
text: `twitter @ ${message.guild.name}`, text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(), icon_url: client.user.displayAvatarURL(),
}, },
timestamp: new Date(), timestamp: new Date(),
}] }]
}); });
} }
}, },
}; };
+86 -87
View File
@@ -1,88 +1,87 @@
'use strict'; 'use strict';
const { time } = require("discord.js"); const { time } = require("discord.js");
module.exports = { module.exports = {
name: 'ban', name: 'ban',
description: `ban a user from the current server`, description: `ban a user from the current server`,
aliases: ['b'], aliases: ['banish'],
usage: 'ban <user> [reason] [duration]', usage: 'ban <user> [reason] [duration]',
async execute(client, message, args) { async execute(client, message, args) {
if (!message.member.permissions.has('BanMembers')) { if (!message.member.permissions.has('BanMembers')) {
return message.reply({ return message.reply({
content: null, embeds: [{ content: null, embeds: [{
color: 0x8aa2b3, color: 0x8aa2b3,
author: { author: {
name: message.author.username, name: message.author.username,
icon_url: message.author.displayAvatarURL(), icon_url: message.author.displayAvatarURL(),
}, },
description: '<:no:1481811955592466492> you do not have permission to use this command!', description: '<:no:1481811955592466492> you do not have permission to use this command!',
footer: { footer: {
text: `twitter @ ${message.guild.name}`, text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(), icon_url: client.user.displayAvatarURL(),
}, },
timestamp: new Date(), timestamp: new Date(),
}] }]
}); });
} }
const user = message.mentions.users.first() || client.users.cache.get(args[0]); const user = message.mentions.users.first() || client.users.cache.get(args[0]);
if (!user) { if (!user) {
return message.reply({ return message.reply({
content: null, embeds: [{ content: null, embeds: [{
color: 0x8aa2b3, color: 0x8aa2b3,
author: { author: {
name: message.author.username, name: message.author.username,
icon_url: message.author.displayAvatarURL(), icon_url: message.author.displayAvatarURL(),
}, },
description: '<:no:1481811955592466492> please specify a user to ban!', description: '<:no:1481811955592466492> please specify a user to ban!',
footer: { footer: {
text: `twitter @ ${message.guild.name}`, text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(), icon_url: client.user.displayAvatarURL(),
}, },
timestamp: new Date(), timestamp: new Date(),
}] }]
}); });
} }
const reason = args.slice(1).join(' ') || 'no reason provided (twitter)'; const reason = args.slice(1).join(' ') || 'no reason provided (twitter)';
const duration = args[2] ? parseInt(args[2]) : null;
try {
try { await message.guild.members.ban(user, { reason });
await message.guild.members.ban(user, { reason }); message.reply({
message.reply({ content: null, embeds: [{
content: null, embeds: [{ color: 0x8aa2b3,
color: 0x8aa2b3, author: {
author: { name: message.author.username,
name: message.author.username, icon_url: message.author.displayAvatarURL(),
icon_url: message.author.displayAvatarURL(), },
}, description: `<:check:1481811973447356496> ${user.username} has been banned!`,
description: `<:check:1481811973447356496> ${user.username} has been banned!`, footer: {
footer: { text: `twitter @ ${message.guild.name}`,
text: `twitter @ ${message.guild.name}`, icon_url: client.user.displayAvatarURL(),
icon_url: client.user.displayAvatarURL(), },
}, timestamp: new Date(),
timestamp: new Date(), }]
}] });
}); } catch (error) {
} catch (error) { console.error(error);
console.error(error); message.reply({
message.reply({ content: null, embeds: [{
content: null, embeds: [{ color: 0x8aa2b3,
color: 0x8aa2b3, author: {
author: { name: message.author.username,
name: message.author.username, icon_url: message.author.displayAvatarURL(),
icon_url: message.author.displayAvatarURL(), },
}, description: '<:no:1481811955592466492> an error occurred while trying to ban the user!',
description: '<:no:1481811955592466492> an error occurred while trying to ban the user!', footer: {
footer: { text: `twitter @ ${message.guild.name}`,
text: `twitter @ ${message.guild.name}`, icon_url: client.user.displayAvatarURL(),
icon_url: client.user.displayAvatarURL(), },
}, timestamp: new Date(),
timestamp: new Date(), }]
}] });
}); }
} },
},
}; };
+46
View File
@@ -0,0 +1,46 @@
'use strict';
const { time } = require("discord.js");
module.exports = {
name: 'lockdown',
description: 'lockdown the the current channel ',
aliases: ['ld', 'lock'],
usage: 'lockdown',
async execute(client, message) {
var embed;
if (!message.member?.permissions?.has('ManageChannels')) {
await message.reply({
embeds: [{
color: 0x8aa2b3,
title: '',
description: '<:no:1481811955592466492> you do not have permissions to run this command!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
return;
}
const channel = message.channel;
await channel.permissionOverwrites.edit(message.guild.roles.everyone, { SendMessages: false });
await channel.permissionOverwrites.edit(message.guild.roles.cache.find(r => r.permissions.has('Administrator')), { SendMessages: true });
embed = {
color: 0x8aa2b3,
title: '',
description: `<:check:1481811973447356496> ${channel} is now locked!`,
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
};
await message.reply({ content: null, embeds: [embed] });
},
};
+64
View File
@@ -0,0 +1,64 @@
'use strict';
const { time } = require("discord.js");
module.exports = {
name: 'role',
description: `give or take a role from a user`,
aliases: ['r'],
usage: 'role <user> (-)<role>',
async execute(client, message) {
var embed = {
color: 0x8aa2b3,
title: '',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}
if (!message.member.permissions.has('Administrator')) {
embed.description = '<:no:1481811955592466492> you do not have permissions to run this command!';
await message.reply({ content: null, embeds: [embed] });
return;
}
if (!message.mentions.members.size) {
embed.description = '<:no:1481811955592466492> please specify a user to give or take a role from!';
await message.reply({ content: null, embeds: [embed] });
return;
}
const member = message.mentions.members.first();
const roleName = message.content.split(' ').slice(2).join(' ').replace(/^-/, '');
const role = message.guild.roles.cache.find(r => r.name === roleName);
if (!role) {
embed.description = '<:no:1481811955592466492> role not found!';
await message.reply({ content: null, embeds: [embed] });
return;
}
if (message.content.split(' ')[2].startsWith('-')) {
if (!member.roles.cache.has(role.id)) {
embed.description = `<:no:1481811955592466492> ${member} does not have the ${role} role!`;
await message.reply({ content: null, embeds: [embed] });
return;
}
await member.roles.remove(role);
embed.description = `<:check:1481811973447356496> ${role} role has been taken from ${member}!`;
} else {
if (member.roles.cache.has(role.id)) {
embed.description = `<:no:1481811955592466492> ${member} already has the ${role} role!`;
await message.reply({ content: null, embeds: [embed] });
return;
}
await member.roles.add(role);
embed.description = `<:check:1481811973447356496> ${role} role has been given to ${member}!`;
}
await message.reply({ content: null, embeds: [embed] });
},
};
+115
View File
@@ -0,0 +1,115 @@
'use strict';
const { PermissionFlagsBits } = require("discord.js");
module.exports = {
name: 'tempban',
description: `temporarily ban a user from the current server`,
aliases: ['tban'],
usage: 'tempban <user> <duration> [reason]',
async execute(client, message, args) {
if (!message.member.permissions.has('BanMembers')) {
return message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: '<:no:1481811955592466492> you do not have permission to use this command!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
}
const user = message.mentions.users.first() || client.users.cache.get(args[0]);
if (!user) {
return message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: '<:no:1481811955592466492> please specify a user to ban!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
}
const durationStr = args[1];
const durationMap = { s: 1000, m: 60000, h: 3600000, d: 86400000, w: 604800000 };
const match = durationStr?.match(/^(\d+)([smhdw])$/);
if (!match || !durationMap[match[2]]) {
return message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: '<:no:1481811955592466492> please specify a valid duration (e.g., 1s, 1m, 1d, 1w)!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
}
const duration = parseInt(match[1]) * durationMap[match[2]];
const reason = args.slice(2).join(' ') || 'no reason provided (twitter)';
try {
await message.guild.members.ban(user, { reason: `${reason} (twitter, moderator: ${message.author.username})` });
setTimeout(() => {
message.guild.bans.remove(user, 'temporary ban expired (twitter)');
}, duration);
message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: `<:check:1481811973447356496> ${user.username} has been temporarily banned for ${durationStr}!`,
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
} catch (error) {
console.error(error);
message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: '<:no:1481811955592466492> an error occurred while trying to ban the user!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
}
},
};
+116
View File
@@ -0,0 +1,116 @@
'use strict';
const { time } = require("discord.js");
module.exports = {
name: 'timeout',
description: `timeout a user`,
aliases: ['mute'],
usage: 'timeout <user> <duration>',
async execute(client, message, args) {
var embed;
if (!message.member.permissions.has('ModerateMembers')) {
await message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
title: '',
description: '<:no:1481811955592466492> you do not have permissions to run this command!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
return;
}
if (!message.mentions.members.size) {
await message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
title: '',
description: '<:no:1481811955592466492> please specify a user to timeout!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
return;
}
const member = message.mentions.members.first();
const durationStr = args[1];
const durationMap = { s: 1000, m: 60000, h: 3600000, d: 86400000, w: 604800000 };
const match = durationStr?.match(/^(\d+)([smhdw])$/);
if (!match || !durationMap[match[2]]) {
return message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: '<:no:1481811955592466492> please specify a valid duration (e.g., 1s, 1m, 1d, 1w)!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
}
const duration = parseInt(match[1]) * durationMap[match[2]];
if (!duration) {
await message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
title: '',
description: '<:no:1481811955592466492> please specify a valid duration for the timeout!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
return;
}
try {
await member.timeout(duration);
embed = {
color: 0x8aa2b3,
title: '',
description: `<:check:1481811973447356496> ${member} has been timed out for ${durationStr}!`,
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
};
} catch (error) {
console.error(error);
embed = {
color: 0x8aa2b3,
title: '',
description: '<:no:1481811955592466492> an error occurred while trying to timeout the user!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
};
}
await message.reply({ content: null, embeds: [embed] });
},
};
+50
View File
@@ -0,0 +1,50 @@
'use strict';
const { time } = require("discord.js");
module.exports = {
name: 'unlockdown',
description: 'unlock the current channel ',
aliases: ['ud', 'unlock'],
usage: 'unlockdown',
async execute(client, message) {
var embed;
if (!message.member?.permissions?.has('ManageChannels')) {
await message.reply({
embeds: [{
color: 0x8aa2b3,
title: '',
description: '<:no:1481811955592466492> you do not have permissions to run this command!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
return;
}
const channel = message.channel;
await channel.permissionOverwrites.edit(
message.guild.roles.everyone,
{ SendMessages: null }
);
embed = {
color: 0x8aa2b3,
title: '',
description: `<:check:1481811973447356496> ${channel} is now unlocked!`,
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
};
await message.reply({ content: null, embeds: [embed] });
},
};
+92
View File
@@ -0,0 +1,92 @@
'use strict';
const { time } = require("discord.js");
module.exports = {
name: 'untimeout',
description: `untimeout a user`,
aliases: ['unmute'],
usage: 'untimeout <user>',
async execute(client, message, args) {
var embed;
if (!message.member.permissions.has('ModerateMembers')) {
await message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
title: '',
description: '<:no:1481811955592466492> you do not have permissions to run this command!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
return;
}
if (!message.mentions.members.size || !message.mentions.members.first().communicationDisabledUntil) {
await message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
title: '',
description: '<:no:1481811955592466492> that user is not timed out!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
return;
}
if (!message.mentions.members.size) {
await message.reply({
content: null, embeds: [{
color: 0x8aa2b3,
title: '',
description: '<:no:1481811955592466492> please specify a user to timeout!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
}]
});
return;
}
const member = message.mentions.members.first();
try {
await member.timeout(null);
embed = {
color: 0x8aa2b3,
title: '',
description: `<:check:1481811973447356496> ${member} has been untimed out!`,
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
};
} catch (error) {
console.error(error);
embed = {
color: 0x8aa2b3,
title: '',
description: '<:no:1481811955592466492> an error occurred while trying to untimeout the user!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
};
}
await message.reply({ content: null, embeds: [embed] });
},
};
+370
View File
@@ -0,0 +1,370 @@
'use strict';
const fs = require('fs');
const fsp = require('fs/promises');
const os = require('os');
const path = require('path');
const { spawn } = require('child_process');
const { pipeline } = require('stream/promises');
const { Readable } = require('stream');
const logger = require('../../utils/logger');
function isMp3Attachment(attachment) {
const name = (attachment.name || '').toLowerCase();
const contentType = (attachment.contentType || '').toLowerCase();
return name.endsWith('.mp3') || contentType.startsWith('audio/');
}
function isImageAttachment(attachment) {
const name = (attachment.name || '').toLowerCase();
const contentType = (attachment.contentType || '').toLowerCase();
return (
name.endsWith('.png') ||
name.endsWith('.jpg') ||
name.endsWith('.jpeg') ||
name.endsWith('.webp') ||
(contentType.startsWith('image/') && contentType !== 'image/gif')
);
}
function isGifAttachment(attachment) {
const name = (attachment.name || '').toLowerCase();
const contentType = (attachment.contentType || '').toLowerCase();
return name.endsWith('.gif') || contentType === 'image/gif';
}
function isVideoAttachment(attachment) {
const name = (attachment.name || '').toLowerCase();
const contentType = (attachment.contentType || '').toLowerCase();
return (
name.endsWith('.mp4') ||
name.endsWith('.mov') ||
name.endsWith('.mkv') ||
name.endsWith('.webm') ||
name.endsWith('.m4v') ||
contentType.startsWith('video/')
);
}
function runProcess(command, args) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, { stdio: ['ignore', 'ignore', 'pipe'] });
let stderr = '';
child.stderr.on('data', (chunk) => {
stderr += chunk.toString();
});
child.on('error', reject);
child.on('close', (code) => {
if (code === 0) {
resolve();
return;
}
reject(new Error(stderr || `${command} exited with code ${code}`));
});
});
}
function isRetryableFetchError(err) {
const code = err?.code || err?.cause?.code;
const name = err?.name;
const message = String(err?.message || '');
return (
code === 'UND_ERR_SOCKET' ||
code === 'ECONNRESET' ||
code === 'EPIPE' ||
code === 'ETIMEDOUT' ||
name === 'SocketError' ||
name === 'FetchError' ||
/other side closed/i.test(message)
);
}
async function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function downloadAttachment(attachment, filePath) {
const attempts = 3;
for (let attempt = 1; attempt <= attempts; attempt += 1) {
try {
const response = await fetch(attachment.url);
if (!response.ok || !response.body) {
throw new Error(`failed to download ${attachment.name}: HTTP ${response.status}`);
}
await pipeline(Readable.fromWeb(response.body), fs.createWriteStream(filePath));
return;
} catch (err) {
await fsp.rm(filePath, { force: true }).catch(() => null);
if (attempt < attempts && isRetryableFetchError(err)) {
logger.warn(
`[snippet] attachment download failed, retrying (${attempt}/${attempts}) for ${attachment.name || 'unknown'}: ${err.message || err}`,
);
await sleep(250 * attempt);
continue;
}
throw new Error(`failed to download ${attachment.name}: ${err.message || err}`);
}
}
}
async function getDuration(filePath) {
const probe = spawn('ffprobe', [
'-v', 'error',
'-show_entries', 'format=duration',
'-of', 'default=noprint_wrappers=1:nokey=1',
filePath,
], { stdio: ['ignore', 'pipe', 'pipe'] });
let stdout = '';
let stderr = '';
probe.stdout.on('data', (chunk) => {
stdout += chunk.toString();
});
probe.stderr.on('data', (chunk) => {
stderr += chunk.toString();
});
const exitCode = await new Promise((resolve, reject) => {
probe.on('error', reject);
probe.on('close', resolve);
});
if (exitCode !== 0) {
throw new Error(stderr || 'failed to read media duration');
}
const duration = Number.parseFloat(stdout.trim());
if (!Number.isFinite(duration) || duration <= 0) {
throw new Error('invalid media duration');
}
return duration;
}
function buildEmbed(client, message, description, fields = []) {
const embed = {
color: 0x8aa2b3,
title: '',
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description,
footer: {
text: `twitter @ ${message.guild?.name || 'dms'}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
};
if (fields.length > 0) {
embed.fields = fields;
}
return embed;
}
function getErrorMessage(err) {
if (err instanceof Error) {
return err.stack || err.message;
}
if (typeof err === 'string') {
return err;
}
return 'unknown error';
}
const logoPath = path.resolve(__dirname, '../../../assets/logo.png');
function buildVideoFilter() {
return [
'[0:v]scale=trunc(iw/2)*2:trunc(ih/2)*2[base]',
'[2:v]format=rgba,colorchannelmixer=aa=0.5,scale=trunc(iw*0.12/2)*2:-1,rotate=(PI/2)*t:ow=rotw(iw):oh=roth(ih):c=none[logo]',
'[base][logo]overlay=x=W-w-16:y=H-h-16:shortest=1[v]',
].join(';');
}
module.exports = {
name: 'snippet',
description: 'create a snippet with an image/video and an mp3',
usage: 'snippet <seconds> [mp3 attachment] [image or video attachment]',
async execute(client, message, args = []) {
const requestedLength = Number.parseInt(args[0], 10);
if (!Number.isInteger(requestedLength) || requestedLength <= 0) {
return message.reply({
content: null,
embeds: [buildEmbed(
client,
message,
'<:no:1481811955592466492> provide a clip length in seconds, then attach one mp3 and either one image or one video file.',
)],
});
}
const attachments = [...message.attachments.values()];
const mp3Attachment = attachments.find(isMp3Attachment);
const imageAttachment = attachments.find(isImageAttachment);
const gifAttachment = attachments.find(isGifAttachment);
const videoAttachment = attachments.find(isVideoAttachment);
const visualAttachment = imageAttachment || gifAttachment || videoAttachment;
const hasMultipleVisualTypes = [imageAttachment, gifAttachment, videoAttachment].filter(Boolean).length > 1;
if (!mp3Attachment || !visualAttachment || hasMultipleVisualTypes) {
return message.reply({
content: null,
embeds: [buildEmbed(
client,
message,
'<:no:1481811955592466492> attach one mp3 file and either one image, one gif, or one video file, but not more than one visual file.',
)],
});
}
const sent = await message.reply({
content: null,
embeds: [buildEmbed(
client,
message,
'<a:loading_icon:1366587619890302998> building your snippet...',
)],
});
const tempDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'twitter-snippet-'));
const audioPath = path.join(tempDir, mp3Attachment.name || 'input.mp3');
const visualPath = path.join(tempDir, visualAttachment.name || (imageAttachment ? 'input.png' : gifAttachment ? 'input.gif' : 'input.mp4'));
const outputPath = path.join(tempDir, 'snippet.mp4');
try {
await Promise.all([
downloadAttachment(mp3Attachment, audioPath),
downloadAttachment(visualAttachment, visualPath),
]);
const audioDuration = await getDuration(audioPath);
const isStillImageSource = Boolean(imageAttachment && imageAttachment === visualAttachment);
const isGifSource = Boolean(gifAttachment && gifAttachment === visualAttachment);
const isLoopedVisualSource = isStillImageSource || isGifSource;
const visualDuration = isLoopedVisualSource ? Infinity : await getDuration(visualPath);
if (requestedLength > audioDuration || requestedLength > visualDuration) {
throw new Error('requested length is longer than one of the attached files');
}
const clipLength = requestedLength;
if (isLoopedVisualSource) {
const audioStart = audioDuration === clipLength
? 0
: Math.random() * (audioDuration - clipLength);
await runProcess('ffmpeg', [
'-y',
...(isStillImageSource ? ['-loop', '1'] : ['-stream_loop', '-1']),
'-i', visualPath,
'-ss', audioStart.toFixed(3),
'-i', audioPath,
'-loop', '1',
'-i', logoPath,
'-t', clipLength.toFixed(3),
'-filter_complex', buildVideoFilter(),
'-map', '[v]',
'-map', '1:a:0',
'-c:v', 'libx264',
'-preset', 'veryfast',
'-crf', '20',
'-c:a', 'aac',
'-b:a', '192k',
'-pix_fmt', 'yuv420p',
'-movflags', '+faststart',
outputPath,
]);
} else {
const videoStart = visualDuration === clipLength
? 0
: Math.random() * (visualDuration - clipLength);
const audioStart = audioDuration === clipLength
? 0
: Math.random() * (audioDuration - clipLength);
await runProcess('ffmpeg', [
'-y',
'-ss', videoStart.toFixed(3),
'-i', visualPath,
'-ss', audioStart.toFixed(3),
'-i', audioPath,
'-loop', '1',
'-i', logoPath,
'-t', clipLength.toFixed(3),
'-filter_complex', buildVideoFilter(),
'-map', '[v]',
'-map', '1:a:0',
'-c:v', 'libx264',
'-preset', 'veryfast',
'-crf', '20',
'-c:a', 'aac',
'-b:a', '192k',
'-pix_fmt', 'yuv420p',
'-movflags', '+faststart',
outputPath,
]);
}
const finalEmbed = buildEmbed(
client,
message,
'<:check:1481811973447356496> clip finished successfully.',
[
{ name: 'audio source', value: `**${mp3Attachment.name || 'unknown'}**`, inline: true },
{ name: 'visual source', value: `**${visualAttachment.name || 'unknown'}**`, inline: true },
{ name: 'clip length', value: `**${clipLength.toFixed(2)}s**`, inline: true },
],
);
await sent.edit({
content: null,
embeds: [finalEmbed],
files: [{ attachment: outputPath, name: 'snippet.mp4' }],
});
} catch (err) {
const errorMessage = getErrorMessage(err);
logger.error('[snippet] failed to build clip', err instanceof Error ? err : new Error(String(err)));
await sent.edit({
content: null,
embeds: [buildEmbed(
client,
message,
'<:no:1481811955592466492> failed to build the clip.',
[
{ name: 'reason', value: `**${errorMessage.slice(0, 1000)}**` },
],
)],
}).catch((editErr) => {
logger.error('[snippet] failed to edit error reply', editErr instanceof Error ? editErr : new Error(String(editErr)));
});
} finally {
await fsp.rm(tempDir, { recursive: true, force: true }).catch(() => null);
}
},
};
+1
View File
@@ -61,6 +61,7 @@ module.exports = {
}; };
} }
await sent.edit({ content: null, embeds: [embed] }); await sent.edit({ content: null, embeds: [embed] });
}, },
}; };
+58 -43
View File
@@ -1,43 +1,58 @@
'use strict'; 'use strict';
const { time } = require("discord.js"); const { time } = require("discord.js");
module.exports = { module.exports = {
name: 'ping', name: 'ping',
description: `test the bot's latency to discord's servers and api`, description: `test the bot's latency to discord's servers and api`,
aliases: ['pong'], aliases: ['pong'],
usage: 'ping', usage: 'ping',
async execute(client, message) { async execute(client, message) {
const sent = await message.reply('pinging'); const sent = await message.reply({
const latency = sent.createdTimestamp - message.createdTimestamp; content: null, embeds: [{
const apiLatency = Math.round(client.ws.ping); color: 0x8aa2b3,
title: '',
const embed = { author: {
color: 0x8aa2b3, name: message.author.username,
title: '', icon_url: message.author.displayAvatarURL(),
author: { },
name: message.author.username, description: '<a:loading_icon:1366587619890302998> pinging...',
icon_url: message.author.displayAvatarURL(), footer: {
}, text: `twitter @ ${message.guild.name}`,
description: 'fuck you!!!! 🖕', icon_url: client.user.displayAvatarURL(),
fields: [ },
{ name: '# of cmds loaded', value: `**${client.commands.size}**`, inline: true }, timestamp: new Date(),
{ name: 'bot latency', value: `**${latency}ms**`, inline: true }, }]
{ name: 'api latency', value: `**${apiLatency}ms**`, inline: true }, });
], const latency = sent.createdTimestamp - message.createdTimestamp;
footer: { const apiLatency = Math.round(client.ws.ping);
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(), const embed = {
}, color: 0x8aa2b3,
timestamp: new Date(), title: '',
thumbnail: { author: {
url: client.user.displayAvatarURL(), name: message.author.username,
height: 1024, icon_url: message.author.displayAvatarURL(),
width: 1024, },
}, description: 'fuck you!!!! 🖕',
}; fields: [
{ name: '# of cmds loaded', value: `**${client.commands.size}**`, inline: true },
await sent.edit({ content: null, embeds: [embed] }); { name: 'bot latency', value: `**${latency}ms**`, inline: true },
}, { name: 'api latency', value: `**${apiLatency}ms**`, inline: true },
}; ],
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date(),
thumbnail: {
url: client.user.displayAvatarURL(),
height: 1024,
width: 1024,
},
};
await sent.edit({ content: null, embeds: [embed] });
},
};
+137
View File
@@ -0,0 +1,137 @@
'use strict';
const { time } = require("discord.js");
const fs = require('node:fs');
const path = require('node:path');
const DB_PATH = path.join(__dirname, '../../../db.json');
const readDb = () => {
try {
return JSON.parse(fs.readFileSync(DB_PATH, 'utf8'));
} catch {
return {};
}
};
const writeDb = (db) => {
fs.writeFileSync(DB_PATH, JSON.stringify(db, null, 2), 'utf8');
};
module.exports = {
name: 'prefix',
description: 'change the server command prefix',
aliases: ['setprefix'],
usage: 'prefix <new prefix>',
execute: async (client, message, args = []) => {
var embed;
if (!message.guild) {
await message.reply({
embeds: [{
color: 0x8aa2b3,
title: '',
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: '<:no:1481811955592466492> this command can only be used in a server!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date()
}]
});
return;
}
if (!message.member?.permissions?.has('ManageGuild')) {
await message.reply({
embeds: [{
color: 0x8aa2b3,
title: '',
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: '<:no:1481811955592466492> you do not have permissions to run this command!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date()
}]
});
return;
}
let newPrefix = args[0];
if (!newPrefix) {
const parts = message.content.trim().split(/\s+/);
newPrefix = parts[1];
}
if (!newPrefix) {
await message.reply({
embeds: [{
color: 0x8aa2b3,
title: '',
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: '<:no:1481811955592466492> you must provide a new prefix!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date()
}]
});
return;
}
if (newPrefix.length > 5) {
await message.reply({
embeds: [{
color: 0x8aa2b3,
title: '',
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: '<:no:1481811955592466492> prefix must be 5 characters or less!',
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date()
}]
});
return;
}
const db = readDb();
db.prefixes = db.prefixes || {};
db.prefixes[message.guild.id] = newPrefix;
writeDb(db);
await message.reply({
embeds: [{
color: 0x8aa2b3,
title: '',
author: {
name: message.author.username,
icon_url: message.author.displayAvatarURL(),
},
description: `<:check:1481811973447356496> prefix for **${message.guild.name}** updated to \`${newPrefix}\`!`,
footer: {
text: `twitter @ ${message.guild.name}`,
icon_url: client.user.displayAvatarURL(),
},
timestamp: new Date()
}]
});
}
};
void time;
+49 -49
View File
@@ -1,50 +1,50 @@
'use strict'; 'use strict';
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const { Events } = require('discord.js'); const { Events } = require('discord.js');
const logger = require('../utils/logger'); const logger = require('../utils/logger');
module.exports = (client, member) => { module.exports = (client, member) => {
const dbPath = path.join(__dirname, '..', '..', 'db.json'); const dbPath = path.join(__dirname, '..', '..', 'db.json');
const db = JSON.parse(fs.readFileSync(dbPath, 'utf8')); const db = JSON.parse(fs.readFileSync(dbPath, 'utf8'));
const roleId = db.autoroles ? db.autoroles[member.guild.id] : null; const roleId = db.autoroles ? db.autoroles[member.guild.id] : null;
logger.info(`member "${member.user.tag}" has joined guild "${member.guild.name}"`); logger.info(`member "${member.user.tag}" has joined guild "${member.guild.name}"`);
// autorole // autorole
{ {
if (!roleId) { if (!roleId) {
logger.info(`no autorole is set up in "${member.guild.name}"`); logger.info(`no autorole is set up in "${member.guild.name}"`);
return; return;
} }
const role = member.guild.roles.cache.get(roleId); const role = member.guild.roles.cache.get(roleId);
if (!role) { if (!role) {
logger.error(`role with id "${roleId}" not found in guild "${member.guild.name}"`); logger.error(`role with id "${roleId}" not found in guild "${member.guild.name}"`);
return; return;
} }
member.roles.add(role) member.roles.add(role)
.then(() => logger.info(`assigned role "${role.name}" to user "${member.user.tag}"!`)) .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}`)); .catch(error => logger.error(`failed to assign role "${role.name}" to user "${member.user.tag}": ${error}`));
} }
// autoban // autoban
{ {
const autoban = db.autobans ? db.autobans[member.guild.id] : null; const autoban = db.autobans ? db.autobans[member.guild.id] : null;
if (!autoban) { if (!autoban) {
logger.info(`no autoban is set up for ${member.user.tag} in "${member.guild.name}"`); logger.info(`no autoban is set up for ${member.user.tag} in "${member.guild.name}"`);
return; return;
} }
if (member.user.tag === autoban) { if (member.user.tag === autoban) {
member.ban({ reason: 'autobanned (twitter)' }) member.ban({ reason: 'autobanned (twitter)' })
.then(() => logger.info(`autobanned user "${member.user.tag}" from guild "${member.guild.name}"`)) .then(() => logger.info(`autobanned user "${member.user.tag}" from guild "${member.guild.name}"`))
.catch(error => logger.error(`failed to autoban user "${member.user.tag}" from guild "${member.guild.name}": ${error}`)); .catch(error => logger.error(`failed to autoban user "${member.user.tag}" from guild "${member.guild.name}": ${error}`));
} else { } else {
logger.info(`user "${member.user.tag}" is not on the autoban list for "${member.guild.name}"`); logger.info(`user "${member.user.tag}" is not on the autoban list for "${member.guild.name}"`);
} }
} }
}; };
+61 -52
View File
@@ -1,52 +1,61 @@
'use strict'; 'use strict';
const { Events } = require('discord.js'); const { Events } = require('discord.js');
const logger = require('../utils/logger'); const logger = require('../utils/logger');
const db = require('../../db.json'); // adjust path as needed
module.exports = {
name: Events.MessageCreate, const get_guild_prefix = (guildId) => {
return db.prefixes?.[guildId] || null;
async execute(client, message) { };
// Ignore bots and messages without the prefix
if (message.author.bot) return; module.exports = {
name: Events.MessageCreate,
const prefix = process.env.PREFIX || '!';
if (!message.content.startsWith(prefix)) return; async execute(client, message) {
if (message.author.bot) return;
// Parse command name and arguments
const args = message.content.slice(prefix.length).trim().split(/\s+/); let prefix = process.env.PREFIX || '!';
const commandName = args.shift().toLowerCase();
if (message.guild) {
const command = client.commands.get(commandName); const guildPrefix = get_guild_prefix(message.guild.id);
if (!command) return; if (guildPrefix) prefix = guildPrefix;
}
logger.info(
`[command] "${commandName}" used by ${message.author.tag} (${message.author.id})` + if (!message.content.startsWith(prefix)) return;
` in ${message.guild ? `#${message.channel.name} (${message.guild.name})` : 'dms'}`,
); const args = message.content.slice(prefix.length).trim().split(/\s+/);
const commandName = args.shift().toLowerCase();
try {
await command.execute(client, message, args); const command = client.commands.get(commandName);
} catch (err) { if (!command) return;
logger.error(`[command] error executing "${commandName}":`, err);
await message logger.info(
.reply({ `[command] "${commandName}" used by ${message.author.tag} (${message.author.id})` +
content: null, embeds: [{ ` in ${message.guild ? `#${message.channel.name} (${message.guild.name})` : 'dms'}`,
color: 0x8aa2b3, );
title: '',
author: { try {
name: message.author.username, await command.execute(client, message, args);
icon_url: message.author.displayAvatarURL(), } catch (err) {
}, logger.error(`[command] error executing "${commandName}":`, err);
description: '<:no:1481811955592466492> an error occurred while executing that command!', await message
footer: { .reply({
text: `${message.author.username} @ twitter`, content: null, embeds: [{
icon_url: message.author.displayAvatarURL(), color: 0x8aa2b3,
}, title: '',
timestamp: new Date(), author: {
}] name: message.author.username,
}) icon_url: message.author.displayAvatarURL(),
.catch(() => null); },
} description: '<:no:1481811955592466492> an error occurred while executing that command!',
}, footer: {
}; text: `${message.author.username} @ twitter`,
icon_url: message.author.displayAvatarURL(),
},
timestamp: new Date(),
}]
})
.catch(() => null);
}
},
};
+35 -36
View File
@@ -1,36 +1,35 @@
'use strict'; 'use strict';
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const { ActivityType, Events } = require('discord.js'); const { ActivityType, Events } = require('discord.js');
const logger = require('../utils/logger'); const logger = require('../utils/logger');
module.exports = { module.exports = {
name: Events.ClientReady, name: Events.ClientReady,
once: true, once: true,
execute(client) { execute(client) {
const db_path = path.join(process.cwd(), 'db.json'); const db_path = path.join(process.cwd(), 'db.json');
if (!fs.existsSync(db_path)) { if (!fs.existsSync(db_path)) {
fs.writeFileSync(db_path, JSON.stringify({}, null, 2)); fs.writeFileSync(db_path, JSON.stringify({}, null, 2));
logger.info('[db] created db.json'); logger.info('[db] created db.json');
} }
const db = JSON.parse(fs.readFileSync(db_path, 'utf8')); const db = JSON.parse(fs.readFileSync(db_path, 'utf8'));
const variables = Object.keys(db); const variables = Object.keys(db);
logger.info(`[db] loaded variables from db.json: ${variables.length > 0 ? variables.join(', ') : 'none'}`); 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')
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(res => res.ok ? res.json() : Promise.reject(new Error('Failed to fetch rolimons item list'))) .then(data => {
.then(data => { fs.writeFileSync(path.join(process.cwd(), 'items.json'), JSON.stringify(data, null, 2));
fs.writeFileSync(path.join(process.cwd(), 'items.json'), JSON.stringify(data, null, 2)); })
}) .catch(err => logger.error('[ready] error fetching rolimons item list:', err));
.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] logged in as ${client.user.tag} (${client.user.id})`); logger.info(`[ready] serving ${client.guilds.cache.size} guild(s)`);
logger.info(`[ready] serving ${client.guilds.cache.size} guild(s)`);
client.user.setActivity(`watching over ${client.guilds.cache.size} servers`, { type: ActivityType.Watching });
client.user.setActivity(`watching over ${client.guilds.cache.size} servers`, { type: ActivityType.Watching }); },
}, };
};
+60 -65
View File
@@ -1,65 +1,60 @@
'use strict'; 'use strict';
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const logger = require('../utils/logger'); const logger = require('../utils/logger');
/** /**
* Loads all command files from src/commands and registers them on the client. * @param {import('discord.js').Client} client
* Each command file must export: { name, description, aliases?, usage?, execute } */
* function loadCommands(client) {
* @param {import('discord.js').Client} client const commandsPath = path.join(__dirname, '..', 'commands');
*/
function loadCommands(client) { function getAllCommandFiles(dir) {
const commandsPath = path.join(__dirname, '..', 'commands'); let results = [];
const list = fs.readdirSync(dir, { withFileTypes: true });
// Recursively get all .js files in commandsPath for (const entry of list) {
function getAllCommandFiles(dir) { const fullPath = path.join(dir, entry.name);
let results = []; if (entry.isDirectory()) {
const list = fs.readdirSync(dir, { withFileTypes: true }); results = results.concat(getAllCommandFiles(fullPath));
for (const entry of list) { } else if (entry.isFile() && entry.name.endsWith('.js')) {
const fullPath = path.join(dir, entry.name); results.push(path.relative(commandsPath, fullPath));
if (entry.isDirectory()) { }
results = results.concat(getAllCommandFiles(fullPath)); }
} else if (entry.isFile() && entry.name.endsWith('.js')) { return results;
results.push(path.relative(commandsPath, fullPath)); }
}
} const commandFiles = getAllCommandFiles(commandsPath);
return results;
} let loaded = 0;
const commandFiles = getAllCommandFiles(commandsPath); for (const file of commandFiles) {
try {
let loaded = 0; const command = require(path.join(commandsPath, file));
for (const file of commandFiles) { if (!command.name || typeof command.execute !== 'function') {
try { logger.warn(
const command = require(path.join(commandsPath, file)); `[commandhandler] skipping ${file}: missing "name" or "execute"`,
);
if (!command.name || typeof command.execute !== 'function') { continue;
logger.warn( }
`[commandhandler] skipping ${file}: missing "name" or "execute"`,
); client.commands.set(command.name.toLowerCase(), command);
continue;
} if (Array.isArray(command.aliases)) {
for (const alias of command.aliases) {
client.commands.set(command.name.toLowerCase(), command); client.commands.set(alias.toLowerCase(), command);
}
// Register aliases if provided }
if (Array.isArray(command.aliases)) {
for (const alias of command.aliases) { logger.debug(`[commandhandler] loaded command: ${command.name}`);
client.commands.set(alias.toLowerCase(), command); loaded++;
} } catch (err) {
} logger.error(`[commandhandler] failed to load ${file}:`, err);
}
logger.debug(`[commandhandler] loaded command: ${command.name}`); }
loaded++;
} catch (err) { logger.info(`[commandhandler] ${loaded} command(s) loaded`);
logger.error(`[commandhandler] failed to load ${file}:`, err); }
}
} module.exports = { loadCommands };
logger.info(`[commandhandler] ${loaded} command(s) loaded`);
}
module.exports = { loadCommands };