First iteration!
This commit is contained in:
2
src/.env.example
Normal file
2
src/.env.example
Normal file
@ -0,0 +1,2 @@
|
||||
# Tokens
|
||||
DISCORD_TOKEN=
|
46
src/commands/ping.ts
Normal file
46
src/commands/ping.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { ApplyOptions } from '@sapphire/decorators';
|
||||
import { Command } from '@sapphire/framework';
|
||||
import { Message } from 'discord.js';
|
||||
|
||||
@ApplyOptions<Command.Options>({
|
||||
description: 'Pong!'
|
||||
})
|
||||
export class UserCommand extends Command {
|
||||
// Register Chat Input and Context Menu command
|
||||
public override registerApplicationCommands(registry: Command.Registry) {
|
||||
// Register Chat Input command
|
||||
registry.registerChatInputCommand({
|
||||
name: this.name,
|
||||
description: this.description
|
||||
});
|
||||
}
|
||||
|
||||
// Message command
|
||||
public async messageRun(message: Message) {
|
||||
return this.sendPing(message);
|
||||
}
|
||||
|
||||
// Chat Input (slash) command
|
||||
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
||||
return this.sendPing(interaction);
|
||||
}
|
||||
|
||||
private async sendPing(interactionOrMessage: Message | Command.ChatInputCommandInteraction | Command.ContextMenuCommandInteraction) {
|
||||
const pingMessage =
|
||||
interactionOrMessage instanceof Message
|
||||
? await interactionOrMessage.channel.send({ content: 'Ping?' })
|
||||
: await interactionOrMessage.reply({ content: 'Ping?', fetchReply: true });
|
||||
|
||||
const content = `Pong! Bot Latency ${Math.round(this.container.client.ws.ping)}ms. API Latency ${
|
||||
pingMessage.createdTimestamp - interactionOrMessage.createdTimestamp
|
||||
}ms.`;
|
||||
|
||||
if (interactionOrMessage instanceof Message) {
|
||||
return pingMessage.edit({ content });
|
||||
}
|
||||
|
||||
return interactionOrMessage.editReply({
|
||||
content: content
|
||||
});
|
||||
}
|
||||
}
|
41
src/commands/wryna.ts
Normal file
41
src/commands/wryna.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { ApplyOptions } from '@sapphire/decorators';
|
||||
import { Args, Command } from '@sapphire/framework';
|
||||
import { Message } from 'discord.js';
|
||||
|
||||
@ApplyOptions<Command.Options>({
|
||||
description: 'This command was your nickname in highschool!',
|
||||
options: ['nickname']
|
||||
})
|
||||
export class UserCommand extends Command {
|
||||
// Register Chat Input and Context Menu command
|
||||
public override registerApplicationCommands(registry: Command.Registry) {
|
||||
registry.registerChatInputCommand((builder) =>
|
||||
builder //
|
||||
.setName(this.name)
|
||||
.setDescription(this.description)
|
||||
.addStringOption((option) => option.setName('nickname').setDescription('Your nickname in highschool.').setRequired(true))
|
||||
);
|
||||
}
|
||||
|
||||
// Message command
|
||||
public async messageRun(message: Message, args: Args) {
|
||||
return this.sendPing(message, args.getOption('nickname') || 'NOTHING');
|
||||
}
|
||||
|
||||
// Chat Input (slash) command
|
||||
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
||||
return this.sendPing(interaction, interaction.options.getString('nickname') || 'NOTHING');
|
||||
}
|
||||
|
||||
private async sendPing(
|
||||
interactionOrMessage: Message | Command.ChatInputCommandInteraction | Command.ContextMenuCommandInteraction,
|
||||
nickname: string
|
||||
) {
|
||||
interactionOrMessage instanceof Message
|
||||
? await interactionOrMessage.channel.send({ content: `${nickname} was ${interactionOrMessage.author.tag}'s nickname in highschool!` })
|
||||
: await interactionOrMessage.reply({
|
||||
content: `${nickname} was ${interactionOrMessage.user.tag}'s nickname in highschool!`,
|
||||
fetchReply: true
|
||||
});
|
||||
}
|
||||
}
|
36
src/index.ts
Normal file
36
src/index.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import './lib/setup';
|
||||
import { LogLevel, SapphireClient } from '@sapphire/framework';
|
||||
import { ActivityType, GatewayIntentBits } from 'discord.js';
|
||||
|
||||
const client = new SapphireClient({
|
||||
defaultPrefix: '!',
|
||||
presence: {
|
||||
status: 'online',
|
||||
activities: [
|
||||
{
|
||||
name: "I'm just here for the vibes my dudes...",
|
||||
type: ActivityType.Playing
|
||||
}
|
||||
]
|
||||
},
|
||||
caseInsensitiveCommands: true,
|
||||
logger: {
|
||||
level: LogLevel.Debug
|
||||
},
|
||||
intents: [GatewayIntentBits.DirectMessages, GatewayIntentBits.GuildMessages, GatewayIntentBits.Guilds, GatewayIntentBits.MessageContent],
|
||||
loadMessageCommandListeners: true
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
try {
|
||||
client.logger.info('Logging in');
|
||||
await client.login();
|
||||
client.logger.info('logged in');
|
||||
} catch (error) {
|
||||
client.logger.fatal(error);
|
||||
client.destroy();
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
main();
|
4
src/lib/constants.ts
Normal file
4
src/lib/constants.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { join } from 'path';
|
||||
|
||||
export const rootDir = join(__dirname, '..', '..');
|
||||
export const srcDir = join(rootDir, 'src');
|
18
src/lib/setup.ts
Normal file
18
src/lib/setup.ts
Normal file
@ -0,0 +1,18 @@
|
||||
// Unless explicitly defined, set NODE_ENV as development:
|
||||
process.env.NODE_ENV ??= 'development';
|
||||
|
||||
import { ApplicationCommandRegistries, RegisterBehavior } from '@sapphire/framework';
|
||||
import '@sapphire/plugin-logger/register';
|
||||
import { setup } from '@skyra/env-utilities';
|
||||
import * as colorette from 'colorette';
|
||||
import { join } from 'node:path';
|
||||
import { srcDir } from './constants';
|
||||
|
||||
// Set default behavior to bulk overwrite
|
||||
ApplicationCommandRegistries.setDefaultBehaviorWhenNotIdentical(RegisterBehavior.BulkOverwrite);
|
||||
|
||||
// Read env var
|
||||
setup({ path: join(srcDir, '.env') });
|
||||
|
||||
// Enable colorette
|
||||
colorette.createColors({ useColor: true });
|
47
src/lib/utils.ts
Normal file
47
src/lib/utils.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import {
|
||||
container,
|
||||
type ChatInputCommandSuccessPayload,
|
||||
type Command,
|
||||
type ContextMenuCommandSuccessPayload,
|
||||
type MessageCommandSuccessPayload
|
||||
} from '@sapphire/framework';
|
||||
import { cyan } from 'colorette';
|
||||
import type { APIUser, Guild, User } from 'discord.js';
|
||||
|
||||
export function logSuccessCommand(payload: ContextMenuCommandSuccessPayload | ChatInputCommandSuccessPayload | MessageCommandSuccessPayload): void {
|
||||
let successLoggerData: ReturnType<typeof getSuccessLoggerData>;
|
||||
|
||||
if ('interaction' in payload) {
|
||||
successLoggerData = getSuccessLoggerData(payload.interaction.guild, payload.interaction.user, payload.command);
|
||||
} else {
|
||||
successLoggerData = getSuccessLoggerData(payload.message.guild, payload.message.author, payload.command);
|
||||
}
|
||||
|
||||
container.logger.debug(`${successLoggerData.shard} - ${successLoggerData.commandName} ${successLoggerData.author} ${successLoggerData.sentAt}`);
|
||||
}
|
||||
|
||||
export function getSuccessLoggerData(guild: Guild | null, user: User, command: Command) {
|
||||
const shard = getShardInfo(guild?.shardId ?? 0);
|
||||
const commandName = getCommandInfo(command);
|
||||
const author = getAuthorInfo(user);
|
||||
const sentAt = getGuildInfo(guild);
|
||||
|
||||
return { shard, commandName, author, sentAt };
|
||||
}
|
||||
|
||||
function getShardInfo(id: number) {
|
||||
return `[${cyan(id.toString())}]`;
|
||||
}
|
||||
|
||||
function getCommandInfo(command: Command) {
|
||||
return cyan(command.name);
|
||||
}
|
||||
|
||||
function getAuthorInfo(author: User | APIUser) {
|
||||
return `${author.username}[${cyan(author.id)}]`;
|
||||
}
|
||||
|
||||
function getGuildInfo(guild: Guild | null) {
|
||||
if (guild === null) return 'Direct Messages';
|
||||
return `${guild.name}[${cyan(guild.id)}]`;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
import type { ChatInputCommandDeniedPayload, Events } from '@sapphire/framework';
|
||||
import { Listener, UserError } from '@sapphire/framework';
|
||||
|
||||
export class UserEvent extends Listener<typeof Events.ChatInputCommandDenied> {
|
||||
public async run({ context, message: content }: UserError, { interaction }: ChatInputCommandDeniedPayload) {
|
||||
// `context: { silent: true }` should make UserError silent:
|
||||
// Use cases for this are for example permissions error when running the `eval` command.
|
||||
if (Reflect.get(Object(context), 'silent')) return;
|
||||
|
||||
if (interaction.deferred || interaction.replied) {
|
||||
return interaction.editReply({
|
||||
content,
|
||||
allowedMentions: { users: [interaction.user.id], roles: [] }
|
||||
});
|
||||
}
|
||||
|
||||
return interaction.reply({
|
||||
content,
|
||||
allowedMentions: { users: [interaction.user.id], roles: [] },
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import { Listener, LogLevel, type ChatInputCommandSuccessPayload } from '@sapphire/framework';
|
||||
import type { Logger } from '@sapphire/plugin-logger';
|
||||
import { logSuccessCommand } from '../../../lib/utils';
|
||||
|
||||
export class UserListener extends Listener {
|
||||
public run(payload: ChatInputCommandSuccessPayload) {
|
||||
logSuccessCommand(payload);
|
||||
}
|
||||
|
||||
public onLoad() {
|
||||
this.enabled = (this.container.logger as Logger).level <= LogLevel.Debug;
|
||||
return super.onLoad();
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
import type { ContextMenuCommandDeniedPayload, Events } from '@sapphire/framework';
|
||||
import { Listener, UserError } from '@sapphire/framework';
|
||||
|
||||
export class UserEvent extends Listener<typeof Events.ContextMenuCommandDenied> {
|
||||
public async run({ context, message: content }: UserError, { interaction }: ContextMenuCommandDeniedPayload) {
|
||||
// `context: { silent: true }` should make UserError silent:
|
||||
// Use cases for this are for example permissions error when running the `eval` command.
|
||||
if (Reflect.get(Object(context), 'silent')) return;
|
||||
|
||||
if (interaction.deferred || interaction.replied) {
|
||||
return interaction.editReply({
|
||||
content,
|
||||
allowedMentions: { users: [interaction.user.id], roles: [] }
|
||||
});
|
||||
}
|
||||
|
||||
return interaction.reply({
|
||||
content,
|
||||
allowedMentions: { users: [interaction.user.id], roles: [] },
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import { Listener, LogLevel, type ContextMenuCommandSuccessPayload } from '@sapphire/framework';
|
||||
import type { Logger } from '@sapphire/plugin-logger';
|
||||
import { logSuccessCommand } from '../../../lib/utils';
|
||||
|
||||
export class UserListener extends Listener {
|
||||
public run(payload: ContextMenuCommandSuccessPayload) {
|
||||
logSuccessCommand(payload);
|
||||
}
|
||||
|
||||
public onLoad() {
|
||||
this.enabled = (this.container.logger as Logger).level <= LogLevel.Debug;
|
||||
return super.onLoad();
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
import type { Events, MessageCommandDeniedPayload } from '@sapphire/framework';
|
||||
import { Listener, type UserError } from '@sapphire/framework';
|
||||
|
||||
export class UserEvent extends Listener<typeof Events.MessageCommandDenied> {
|
||||
public async run({ context, message: content }: UserError, { message }: MessageCommandDeniedPayload) {
|
||||
// `context: { silent: true }` should make UserError silent:
|
||||
// Use cases for this are for example permissions error when running the `eval` command.
|
||||
if (Reflect.get(Object(context), 'silent')) return;
|
||||
|
||||
return message.reply({ content, allowedMentions: { users: [message.author.id], roles: [] } });
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import type { MessageCommandSuccessPayload } from '@sapphire/framework';
|
||||
import { Listener, LogLevel } from '@sapphire/framework';
|
||||
import type { Logger } from '@sapphire/plugin-logger';
|
||||
import { logSuccessCommand } from '../../../lib/utils';
|
||||
|
||||
export class UserEvent extends Listener {
|
||||
public run(payload: MessageCommandSuccessPayload) {
|
||||
logSuccessCommand(payload);
|
||||
}
|
||||
|
||||
public onLoad() {
|
||||
this.enabled = (this.container.logger as Logger).level <= LogLevel.Debug;
|
||||
return super.onLoad();
|
||||
}
|
||||
}
|
10
src/listeners/mentionPrefixOnly.ts
Normal file
10
src/listeners/mentionPrefixOnly.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import type { Events } from '@sapphire/framework';
|
||||
import { Listener } from '@sapphire/framework';
|
||||
import type { Message } from 'discord.js';
|
||||
|
||||
export class UserEvent extends Listener<typeof Events.MentionPrefixOnly> {
|
||||
public async run(message: Message) {
|
||||
const prefix = this.container.client.options.defaultPrefix;
|
||||
return message.channel.send(prefix ? `My prefix in this guild is: \`${prefix}\`` : 'Cannot find any Prefix for Message Commands.');
|
||||
}
|
||||
}
|
50
src/listeners/ready.ts
Normal file
50
src/listeners/ready.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { ApplyOptions } from '@sapphire/decorators';
|
||||
import { Listener, Store } from '@sapphire/framework';
|
||||
import { blue, gray, green, magenta, magentaBright, white, yellow } from 'colorette';
|
||||
|
||||
const dev = process.env.NODE_ENV !== 'production';
|
||||
|
||||
@ApplyOptions<Listener.Options>({ once: true })
|
||||
export class UserEvent extends Listener {
|
||||
private readonly style = dev ? yellow : blue;
|
||||
|
||||
public run() {
|
||||
this.printBanner();
|
||||
this.printStoreDebugInformation();
|
||||
}
|
||||
|
||||
private printBanner() {
|
||||
const success = green('+');
|
||||
|
||||
const llc = dev ? magentaBright : white;
|
||||
const blc = dev ? magenta : blue;
|
||||
|
||||
const line01 = llc('');
|
||||
const line02 = llc('');
|
||||
const line03 = llc('');
|
||||
|
||||
// Offset Pad
|
||||
const pad = ' '.repeat(7);
|
||||
|
||||
console.log(
|
||||
String.raw`
|
||||
${line01} ${pad}${blc('1.0.0')}
|
||||
${line02} ${pad}[${success}] Gateway
|
||||
${line03}${dev ? ` ${pad}${blc('<')}${llc('/')}${blc('>')} ${llc('DEVELOPMENT MODE')}` : ''}
|
||||
`.trim()
|
||||
);
|
||||
}
|
||||
|
||||
private printStoreDebugInformation() {
|
||||
const { client, logger } = this.container;
|
||||
const stores = [...client.stores.values()];
|
||||
const last = stores.pop()!;
|
||||
|
||||
for (const store of stores) logger.info(this.styleStore(store, false));
|
||||
logger.info(this.styleStore(last, true));
|
||||
}
|
||||
|
||||
private styleStore(store: Store<any>, last: boolean) {
|
||||
return gray(`${last ? '└─' : '├─'} Loaded ${this.style(store.size.toString().padEnd(3, ' '))} ${store.name}.`);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user