xernerx 0.1.120-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,151 @@
1
+ /** @format */
2
+
3
+ import * as Discord from 'discord.js';
4
+ import { z } from 'zod';
5
+
6
+ import { start } from '../util/prompts.js';
7
+ import { XernerxStats } from '../types/interfaces.js';
8
+ import { CommandHandler } from '../handlers/CommandHandler.js';
9
+ import { XernerxMessageCommand } from '../base/XernerxMessageCommand.js';
10
+ import { XernerxSlashCommand } from '../main.js';
11
+ import { XernerxContextCommand } from '../base/XernerxContextCommand.js';
12
+ import { XernerxEvent } from '../base/XernerxEvent.js';
13
+ import { XernerxInhibitor } from '../base/XernerxInhibitor.js';
14
+ import XernerxLog from '../tools/XernerxLog.js';
15
+ import sharpyy from 'sharpyy';
16
+ import { ClientUtil } from '../util/ClientUtil.js';
17
+
18
+ export class XernerxClient<T = {}> extends Discord.Client {
19
+ public declare readonly settings;
20
+ public declare readonly stats: XernerxStats;
21
+ public declare readonly modules: {
22
+ commandHandler: CommandHandler;
23
+ options: {
24
+ message?: { global?: boolean };
25
+ slash?: { global?: boolean };
26
+ context?: { global?: boolean };
27
+ };
28
+ };
29
+ public declare readonly util: ClientUtil;
30
+ public declare readonly commands;
31
+ public declare readonly events;
32
+ public declare readonly inhibitors;
33
+
34
+ constructor(DiscordOptions: any, XernerxOptions: any, config: T) {
35
+ super(DiscordOptions);
36
+
37
+ this.settings = z
38
+ .object({
39
+ // required settings
40
+ token: z.string(),
41
+
42
+ // optional settings
43
+ global: z.boolean().default(false),
44
+ owners: z.array(z.string()).default([]),
45
+ guilds: z.array(z.string()).default([]),
46
+
47
+ // dev settings
48
+ debug: z.boolean().default(false),
49
+ })
50
+ .parse({ ...XernerxOptions, ...config });
51
+
52
+ this.stats = {
53
+ guilds: null,
54
+ users: null,
55
+ shardId: null,
56
+ shardCount: null,
57
+ shards: null,
58
+ voteCount: null,
59
+ votes: null,
60
+ };
61
+
62
+ this.util = new ClientUtil(this);
63
+
64
+ start(this);
65
+
66
+ this.modules = {
67
+ commandHandler: new CommandHandler(this),
68
+
69
+ options: {},
70
+ };
71
+
72
+ this.commands = {
73
+ message: new Discord.Collection() as Discord.Collection<string, XernerxMessageCommand>,
74
+ slash: new Discord.Collection() as Discord.Collection<string, XernerxSlashCommand>,
75
+ context: new Discord.Collection() as Discord.Collection<string, XernerxContextCommand>,
76
+ stats: {
77
+ slash: {
78
+ local: 0,
79
+ global: 0,
80
+ },
81
+ message: {
82
+ local: 0,
83
+ global: 0,
84
+ },
85
+ context: {
86
+ local: 0,
87
+ global: 0,
88
+ },
89
+ },
90
+ };
91
+
92
+ this.events = new Discord.Collection() as Discord.Collection<string, XernerxEvent>;
93
+ this.inhibitors = new Discord.Collection() as Discord.Collection<string, XernerxInhibitor>;
94
+
95
+ this.connect();
96
+ }
97
+
98
+ private async connect() {
99
+ // slash cmds
100
+ this.deploy();
101
+
102
+ // login
103
+ this.login(this.settings.token);
104
+ }
105
+
106
+ private async deploy() {
107
+ this.once('ready', async (client) => {
108
+ const commands: { local: Array<XernerxSlashCommand | XernerxContextCommand>; global: Array<XernerxSlashCommand | XernerxContextCommand> } = { local: [], global: [] };
109
+
110
+ if (this.settings.global) {
111
+ if (!this.modules.options.slash?.global) commands.global.push(...(this.commands.slash as any));
112
+ else {
113
+ commands.global.push(...(this.commands.slash.filter((c) => c.global) as any));
114
+
115
+ commands.local.push(...(this.commands.slash.filter((c) => !c.global) as any));
116
+ }
117
+
118
+ if (!this.modules.options.context?.global) commands.global.push(...(this.commands.context as any));
119
+ else {
120
+ commands.global.push(...(this.commands.context.filter((c) => c.global) as any));
121
+
122
+ commands.local.push(...(this.commands.context.filter((c) => !c.global) as any));
123
+ }
124
+ } else commands.local.push(...(this.commands.context as any), ...(this.commands.slash as any));
125
+
126
+ for (const id of this.settings.guilds) {
127
+ const guild = await this.guilds.fetch(id);
128
+
129
+ if (!commands.local.length) break;
130
+
131
+ if (this.settings.debug) XernerxLog.debug(`Attempting to deploy commands to guild: ${sharpyy(guild.name, 'txBlue')}`);
132
+
133
+ this.rest.put(Discord.Routes.applicationGuildCommands(client.user.id, id), { body: (commands.local as any).map(([id, command]: any) => command.toJSON()) });
134
+
135
+ await XernerxLog.box(
136
+ (commands.local as any).map(([id]: [string]) => sharpyy(id, 'txYellow')).join(', '),
137
+ 'red',
138
+ `Deployed ${sharpyy('local', 'txRed', 'bold')} commands in ${sharpyy(guild.name, 'txBlue')}`
139
+ );
140
+ }
141
+
142
+ if (this.settings.global) {
143
+ if (this.settings.debug) XernerxLog.debug(`Attempting to deploy commands globally`);
144
+
145
+ this.rest.put(Discord.Routes.applicationCommands(client.user.id), { body: [] });
146
+
147
+ await XernerxLog.box((commands.global as any).map(([id]: [string]) => sharpyy(id, 'txYellow')).join(', '), 'green', `Deployed ${sharpyy('global', 'txGreen', 'bold')} commands`);
148
+ }
149
+ });
150
+ }
151
+ }
@@ -0,0 +1,31 @@
1
+ /** @format */
2
+
3
+ import { XernerxClient } from '../client/XernerxClient.js';
4
+ import { XernerxContextCommandHandlerOptions, XernerxMessageCommandHandlerOptions, XernerxSlashCommandHandlerOptions } from '../types/interfaces.js';
5
+ import { Handler } from './Handler.js';
6
+ import path from 'path';
7
+
8
+ export class CommandHandler extends Handler {
9
+ constructor(client: XernerxClient) {
10
+ super(client);
11
+ }
12
+
13
+ protected async loadSlashCommands(options: XernerxSlashCommandHandlerOptions) {
14
+ if (!this.client.modules.options.slash) this.client.modules.options.slash = {};
15
+ this.client.modules.options.slash.global = options.global ?? this.client.settings.global;
16
+
17
+ for (const file of await this.readdir(options.directory)) {
18
+ const command = await this.load(path.resolve(`${options.directory}/${file}`));
19
+
20
+ if (!command) continue;
21
+
22
+ if (command.filetype == 'XernerxMessageCommand' || command.filetype == 'XernerxSlashCommand' || command.filetype == 'XernerxContextCommand') {
23
+ if (!this.client.modules.options.slash.global) this.client.commands.stats.slash.local++;
24
+ else if (!command.global) this.client.commands.stats.slash.local++;
25
+ else this.client.commands.stats.slash.global++;
26
+ }
27
+ }
28
+ }
29
+ protected async loadMessageCommands(options: XernerxMessageCommandHandlerOptions) {}
30
+ protected async loadContextCommands(options: XernerxContextCommandHandlerOptions) {}
31
+ }
@@ -0,0 +1,57 @@
1
+ /** @format */
2
+ // import * as inquirer from '@inquirer/prompts';
3
+ // import { link } from 'sharpyy';
4
+
5
+ import { XernerxClient } from '../client/XernerxClient.js';
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+
9
+ import { XernerxFile } from '../types/types.js';
10
+ import XernerxLog from '../tools/XernerxLog.js';
11
+ import { link } from 'sharpyy';
12
+
13
+ export class Handler {
14
+ public declare readonly client: XernerxClient;
15
+
16
+ constructor(client: XernerxClient) {
17
+ this.client = client;
18
+ }
19
+
20
+ protected async readdir(directory: string) {
21
+ const dirpath = path.resolve(directory);
22
+
23
+ if (this.client.settings.debug) XernerxLog.debug(`Attempting to read directory ${link(directory, new URL(dirpath))}`);
24
+
25
+ try {
26
+ const files = fs.readdirSync(dirpath);
27
+
28
+ return files.filter((file) => file.endsWith('.js') || file.endsWith('.mjs') || file.endsWith('.cjs'));
29
+ } catch (error) {
30
+ XernerxLog.error(`Error reading directory ${link(directory, new URL(dirpath))}:`, error as Error);
31
+ return [];
32
+ }
33
+ }
34
+
35
+ protected async load(filepath: string) {
36
+ if (this.client.settings.debug) XernerxLog.debug(`Attempting to load ${link(filepath, new URL(filepath))}`);
37
+
38
+ try {
39
+ const file = ((await import(`file://${filepath}`))?.default || (await import(`file://${filepath}`))) as XernerxFile;
40
+
41
+ const openFile = new (file as any)() as XernerxFile;
42
+
43
+ const [collection, group] = openFile.collection.split('.');
44
+
45
+ group
46
+ ? this.client[group as 'commands'][collection as 'message' | 'slash' | 'context'].set(openFile._id, openFile as never)
47
+ : this.client[collection as 'events' | 'inhibitors'].set(openFile._id, openFile as never);
48
+
49
+ if (this.client.settings.debug) XernerxLog.debug(`Loaded ${link(openFile._id, new URL(filepath))}`);
50
+
51
+ return openFile as XernerxFile;
52
+ } catch (error) {
53
+ XernerxLog.error(`Error loading ${link(filepath, new URL(filepath))}:`, error as Error);
54
+ return null;
55
+ }
56
+ }
57
+ }
package/src/main.ts ADDED
@@ -0,0 +1,5 @@
1
+ /** @format */
2
+
3
+ export * from './client/XernerxClient.js';
4
+ export * from './base/XernerxSlashCommand.js';
5
+ export * from './tools/XernerxLog.js';
@@ -0,0 +1,58 @@
1
+ /** @format */
2
+
3
+ import sharpyy, { link } from 'sharpyy';
4
+
5
+ export default new (class XernerxLog {
6
+ public declare readonly baseText;
7
+
8
+ constructor() {
9
+ this.baseText = `${sharpyy('XERNERX', 'txRainbow', 'bold')} | ${new Date()
10
+ .toLocaleTimeString()
11
+ .split(/ /gim)
12
+ .map((str) =>
13
+ str
14
+ .split(':')
15
+ .map((str) => (isNaN(Number(str)) ? str : sharpyy(str, 'txCyan')))
16
+ .join(':')
17
+ )
18
+ .join(' ')} | ${sharpyy(String(Math.round(process.memoryUsage().rss / 10000) / 100), 'txCyan')}mb |`;
19
+ }
20
+
21
+ info(message: string) {
22
+ return console.info(`✔️ |`, this.baseText, message);
23
+ }
24
+
25
+ async error(message: string, error?: Error) {
26
+ const { default: boxen } = await import('boxen');
27
+
28
+ const errorMessage = boxen(error?.stack as string, { padding: 1, margin: 1, borderStyle: 'round', borderColor: 'red' });
29
+
30
+ if (error) return console.error(`❗ |`, this.baseText, message, errorMessage);
31
+ else return console.error(`❗ |`, this.baseText, message);
32
+ }
33
+
34
+ warn(message: string, url?: URL) {
35
+ console.warn(`⚠️ |`, this.baseText, message, url ? `Read more here ${link('here', url)}` : '');
36
+ }
37
+
38
+ debug(message: string) {
39
+ return console.debug(`🐛 |`, this.baseText, message);
40
+ }
41
+
42
+ async box(message: string, color?: string, title?: string) {
43
+ const { default: boxen } = await import('boxen');
44
+
45
+ const time = new Date()
46
+ .toLocaleTimeString()
47
+ .split(/ /gim)
48
+ .map((str) =>
49
+ str
50
+ .split(':')
51
+ .map((str) => (isNaN(Number(str)) ? str : sharpyy(str, 'txCyan')))
52
+ .join(':')
53
+ )
54
+ .join(' ');
55
+
56
+ return console.info(boxen(message, { padding: 1, margin: 1, borderStyle: 'round', borderColor: color, title: `${time} - ${sharpyy('XERNERX', 'txRainbow', 'bold')} - ${title || ''}` }));
57
+ }
58
+ })();
@@ -0,0 +1,25 @@
1
+ /** @format */
2
+
3
+ export interface XernerxStats {
4
+ guilds: null | number;
5
+ users: null | number;
6
+ shardId: null | number;
7
+ shardCount: null | number;
8
+ shards: null | number;
9
+ voteCount: null | number;
10
+ votes: null | number;
11
+ }
12
+
13
+ export interface XernerxSlashCommandHandlerOptions {
14
+ directory: string;
15
+ global: boolean;
16
+ }
17
+
18
+ export interface XernerxMessageCommandHandlerOptions {
19
+ directory: string;
20
+ global: boolean;
21
+ }
22
+ export interface XernerxContextCommandHandlerOptions {
23
+ directory: string;
24
+ global: boolean;
25
+ }
@@ -0,0 +1,29 @@
1
+ /** @format */
2
+
3
+ import { XernerxContextCommand } from '../base/XernerxContextCommand.js';
4
+ import { XernerxEvent } from '../base/XernerxEvent.js';
5
+ import { XernerxInhibitor } from '../base/XernerxInhibitor.js';
6
+ import { XernerxMessageCommand } from '../base/XernerxMessageCommand.js';
7
+ import { XernerxSlashCommand } from '../base/XernerxSlashCommand.js';
8
+
9
+ export type XernerxFileType = 'XernerxSlashCommand' | 'XernerxMessageCommand' | 'XernerxContextCommand' | 'XernerxEvent' | 'XernerxInhibitor';
10
+
11
+ export type XernerxFile<T = XernerxFileType> = T extends 'XernerxSlashCommand'
12
+ ? XernerxSlashCommand
13
+ : T extends 'XernerxMessageCommand'
14
+ ? XernerxMessageCommand
15
+ : T extends 'XernerxContextCommand'
16
+ ? XernerxContextCommand
17
+ : T extends 'XernerxEvent'
18
+ ? XernerxEvent
19
+ : T extends 'XernerxInhibitor'
20
+ ? XernerxInhibitor
21
+ : XernerxSlashCommand | XernerxMessageCommand | XernerxSlashCommand | XernerxEvent | XernerxInhibitor;
22
+
23
+ export type XernerxCommand<T = XernerxFileType> = T extends 'XernerxSlashCommand'
24
+ ? XernerxSlashCommand
25
+ : T extends 'XernerxMessageCommand'
26
+ ? XernerxMessageCommand
27
+ : T extends 'XernerxContextCommand'
28
+ ? XernerxContextCommand
29
+ : XernerxSlashCommand | XernerxMessageCommand | XernerxContextCommand;
@@ -0,0 +1,29 @@
1
+ /** @format */
2
+
3
+ import { XernerxClient } from '../main.js';
4
+ import { Util } from './Util.js';
5
+
6
+ export class ClientUtil extends Util {
7
+ constructor(client: XernerxClient) {
8
+ super(client);
9
+ }
10
+
11
+ public uptime(timestamp?: number | null) {
12
+ if (!timestamp) timestamp = this.client.uptime || 0;
13
+ let totalSeconds = timestamp / 1000;
14
+ let years = Math.floor(totalSeconds / 31536000);
15
+ totalSeconds %= 31536000;
16
+ let days = Math.floor(totalSeconds / 86400);
17
+ totalSeconds %= 86400;
18
+ let hours = Math.floor(totalSeconds / 3600);
19
+ totalSeconds %= 3600;
20
+ let minutes = Math.floor(totalSeconds / 60);
21
+ let seconds = Math.floor(totalSeconds % 60);
22
+
23
+ if (years >= 1) return `${years}y ${days}d ${hours}h ${minutes}m`;
24
+ else if (days >= 1) return `${days}d ${hours}h ${minutes}m`;
25
+ else if (hours >= 1) return `${hours}h ${minutes}m`;
26
+ else if (minutes >= 1) return `${minutes}m ${seconds}s`;
27
+ else return `${seconds}s`;
28
+ }
29
+ }
@@ -0,0 +1,11 @@
1
+ /** @format */
2
+
3
+ import { XernerxClient } from '../main.js';
4
+
5
+ export class Util {
6
+ public declare readonly client: XernerxClient;
7
+
8
+ constructor(client: XernerxClient) {
9
+ this.client = client;
10
+ }
11
+ }
@@ -0,0 +1,120 @@
1
+ /** @format */
2
+
3
+ import sharpyy from 'sharpyy';
4
+ import { Color } from 'ora';
5
+
6
+ import { XernerxClient } from '../client/XernerxClient.js';
7
+ import XernerxLog from '../tools/XernerxLog.js';
8
+
9
+ let init = false;
10
+
11
+ export async function start(client: XernerxClient) {
12
+ if (init) return;
13
+
14
+ if (client.settings.debug) XernerxLog.info('Debug mode enabled, preventing console clear and logging all handlings...');
15
+ else console.clear();
16
+
17
+ const { default: ora, spinners } = await import('ora');
18
+ const { default: boxen } = await import('boxen');
19
+
20
+ console.info(
21
+ boxen(sharpyy('XERNERX', 'txRainbow', 'bold', 'underlines'), {
22
+ padding: 1,
23
+ margin: 1,
24
+ borderStyle: 'round',
25
+ height: 3,
26
+ align: 'center',
27
+ borderColor: 'magenta',
28
+ fullscreen: (width, height) => [width - 5, height],
29
+ })
30
+ );
31
+
32
+ if (!client.options.intents.has('Guilds'))
33
+ XernerxLog.warn('Your bot is signed in without the Guilds intent, this may cause issues with the bot.', new URL('https://discord.com/developers/docs/topics/gateway#gateway-intents'));
34
+
35
+ const spinner = ora();
36
+
37
+ spinner.spinner = spinners.dots12;
38
+
39
+ spinner.start();
40
+
41
+ let index = 0;
42
+
43
+ setInterval(async () => {
44
+ // Update Discord stats
45
+ calculateStats(client);
46
+
47
+ // Update commandline
48
+
49
+ const time = new Date()
50
+ .toLocaleTimeString()
51
+ .split(/ /gim)
52
+ .map((str) =>
53
+ str
54
+ .split(':')
55
+ .map((str) => (isNaN(Number(str)) ? str : sharpyy(str, 'txCyan')))
56
+ .join(':')
57
+ )
58
+ .join(' '),
59
+ ram = `${sharpyy(String(Math.round(process.memoryUsage().rss / 10000) / 100), 'txCyan')}mb`;
60
+
61
+ const colors = ['red', 'green', 'yellow', 'blue', 'magenta', 'cyan'];
62
+
63
+ const baseText = `${sharpyy('XERNERX', 'txRainbow', 'bold')} | ${time} | ${ram} | `;
64
+
65
+ if (client.user) {
66
+ spinner.color = colors[index] as Color;
67
+
68
+ const counts = {
69
+ local: client.commands.stats.slash.local + client.commands.stats.message.local + client.commands.stats.context.local,
70
+ global: client.commands.stats.slash.global + client.commands.stats.message.global + client.commands.stats.context.global,
71
+ };
72
+
73
+ spinner.text = boxen(
74
+ Object.entries({
75
+ 'Name': sharpyy(client.user.tag, 'txBlue'),
76
+ 'Status': `${sharpyy('Online', 'txGreen')} | ${client.settings.global ? sharpyy('Global', 'txGreen') : sharpyy('Local', 'txRed')}`,
77
+ 'Uptime': client.util
78
+ .uptime(client.uptime)
79
+ .split('')
80
+ .map((c) => (isNaN(Number(c)) ? c : sharpyy(c, 'txCyan')))
81
+ .join(''),
82
+ 'Commands': client.settings.global ? `${counts.local ? `local: ${counts.local}` : ''}|${counts.global ? `global: ${counts.global}` : ''}` : counts.local + counts.global,
83
+ 'RAM Usage': ram,
84
+ 'Guilds': (await Promise.all(client.settings.guilds.map(async (id) => sharpyy((await client.guilds.fetch(id))?.name || id, 'txBlue')))).join(', '),
85
+ 'Owners': (await Promise.all(client.settings.owners.map(async (id) => sharpyy((await client.users.fetch(id))?.username || id, 'txBlue')))).join(', '),
86
+ 'GuildCount': sharpyy(String(client.stats.guilds), 'txCyan'),
87
+ 'UserCount': sharpyy(String(client.stats.users), 'txCyan'),
88
+ 'ShardCount': sharpyy(String(client.stats.shardCount), 'txCyan'),
89
+ })
90
+ .map(([key, value]) => `${key}: ${value}`)
91
+ .join('\n'),
92
+ {
93
+ padding: 1,
94
+ margin: 1,
95
+ borderStyle: 'round',
96
+ borderColor: 'magenta',
97
+ title: time + ' - ' + sharpyy('XERNERX', 'txRainbow', 'bold') + '\x1B[35m',
98
+ height: 15,
99
+ fullscreen: (width, height) => [width - 5, height],
100
+ }
101
+ );
102
+ } else {
103
+ spinner.color = 'green';
104
+
105
+ spinner.text = baseText + `Connecting to Discord${index == 0 ? '.' : index == 1 ? '..' : index == 2 ? '...' : ''}`;
106
+ }
107
+
108
+ index++;
109
+
110
+ if (index == colors.length - 1) index = 0;
111
+ }, 250);
112
+
113
+ init = true;
114
+ }
115
+
116
+ function calculateStats(client: XernerxClient) {
117
+ client.stats.guilds = client.guilds.cache.size;
118
+ client.stats.users = client.users.cache.size;
119
+ client.stats.shardCount = client.options.shardCount || null;
120
+ }