feat: add admin command with stats subcommand and integrate dsxjs dependency

This commit is contained in:
grngxd 2025-08-03 22:13:02 +01:00
parent 9583267f64
commit 7089d8ad28
7 changed files with 90 additions and 7 deletions

67
cmd/admin.ts Normal file
View file

@ -0,0 +1,67 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
import { db } from "../db/db";
import { createEmbed } from "../lib/embed";
import { formatSize } from "../lib/files";
import { StereoFile } from "../types/files";
export const data = new SlashCommandBuilder()
.setName("admin")
.setDescription("admin commands")
.addSubcommand(subcommand =>
subcommand
.setName("stats")
.setDescription("view misc stats")
);
export const execute = async (interaction: ChatInputCommandInteraction): Promise<void> => {
const subcommand = interaction.options.getSubcommand();
if (subcommand === "stats") {
const files = await db.all<StereoFile[]>`SELECT * FROM files`;
const totalSize = files.reduce((a, b) => a + b.size, 0);
const totalFiles = files.length;
const mimes: string[] = files.map(file => file.mime);
const sortedMimes = mimes.sort((a, b) =>
mimes.filter(v => v === a).length - mimes.filter(v => v === b).length
);
const mostCommonMime = sortedMimes.pop() || "unknown";
const mostCommonType = ("." + mostCommonMime.split("/")[1]) || "unknown";
const mostCommonCount = mimes.filter(v => v === mostCommonMime).length;
const uniqueUsers = new Set(files.map(file => file.owner)).size;
const uploaderCounts: Record<string, number> = {};
files.forEach(file => {
uploaderCounts[file.owner] = (uploaderCounts[file.owner] || 0) + 1;
});
const topUploader = (Object.entries(uploaderCounts).sort((a, b) => b[1] - a[1])[0]) || ["none", 0];
const oldestFile = files.reduce((a, b) => new Date(a.created_at) < new Date(b.created_at) ? a : b);
const newestFile = files.reduce((a, b) => new Date(a.created_at) > new Date(b.created_at) ? a : b);
const oldestDate = new Date(oldestFile.created_at);
const newestDate = new Date(newestFile.created_at);
const daysSpan = Math.max(1, Math.ceil((newestDate.getTime() - oldestDate.getTime()) / (1000 * 60 * 60 * 24)));
const filesPerDay = (totalFiles / daysSpan).toFixed(2);
const embed = createEmbed(interaction.user)
.setTitle("stats")
.addFields([
{ name: "total files", value: `${totalFiles}` },
{ name: "total size", value: `${formatSize(totalSize)}` },
{ name: "average size", value: `${formatSize(totalSize / totalFiles)}` },
{ name: "most common type", value: `${mostCommonType} (${mostCommonCount} files)` },
{ name: "files per day", value: `${Math.round(Number(filesPerDay) * 100) / 100} files/day` },
{ name: "total users", value: `${uniqueUsers}` },
{ name: "top uploader", value: `<@${topUploader[0]}> (${topUploader[1]} files)` }
].map(field => ({ ...field, inline: true })))
.setDescription("here's the global stats for stereo:")
await interaction.reply({ embeds: [embed] });
}
};
export default [data, execute] as const;

9
cmd/example.ts Normal file
View file

@ -0,0 +1,9 @@
import { CacheType, CommandInteraction, Events, Interaction, SlashCommandBuilder } from "discord.js";
export const data = new SlashCommandBuilder()
export const execute = async (interaction: CommandInteraction): Promise<void> => {}
export const on: {
[event in Events]?: (interaction: Interaction<CacheType>) => Promise<void>;
} = {}
export default [data, execute, on] as const;

View file

@ -1,3 +1,4 @@
import admin from "./admin";
import files from "./files";
import register from "./register";
import user from "./user";
@ -5,5 +6,6 @@ import user from "./user";
export default [
user,
register,
files
files,
admin
]

View file

@ -42,12 +42,12 @@ export const execute = async (interaction: ChatInputCommandInteraction) => {
const totalSize = files.reduce((a, b) => a + b.size, 0);
const embed = createEmbed(user)
.setDescription("Here's your overview:")
.addFields(
{ name: "Uploads", value: `${files.length} files`, inline: true },
{ name: "Uploaded", value: `${formatSize(totalSize)} / 15 GB (${(totalSize / (15 * 1024 * 1024 * 1024)).toFixed(2)}%)`, inline: true },
{ name: "Plan", value: `Free`, inline: true }
)
.setDescription("here's your account info:")
.addFields([
{ name: "uploads", value: `${files.length} files` },
{ name: "uploaded", value: `${formatSize(totalSize)} / 15 GB (${(totalSize / (15 * 1024 * 1024 * 1024)).toFixed(2)}%)` },
{ name: "plan", value: `free` }
].map(field => ({ ...field, inline: true })))
await interaction.reply({ embeds: [embed] });
}