refactor(*): lots of shit

This commit is contained in:
EvolutionX
2022-08-04 09:49:14 +05:30
parent 1c095680ea
commit 1c865b5d48
22 changed files with 2854 additions and 271 deletions

3
.prettierrc Normal file
View File

@@ -0,0 +1,3 @@
{
"useTabs": true
}

2272
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,8 +5,8 @@
"main": "dist/src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "tsc && node .",
"compile": "tsc"
"start": "tsup && node .",
"compile": "tsup"
},
"keywords": [
"typescript",
@@ -18,10 +18,13 @@
"@sern/handler": "^1.1.9-beta",
"discord.js": "^14.0.3",
"dotenv": "^16.0.1",
"string-similarity": "^4.0.4",
"trie-search": "^1.3.6"
},
"devDependencies": {
"@types/node": "^17.0.25",
"@types/string-similarity": "^4.0.0",
"tsup": "^6.2.1",
"typescript": "^4.6.3"
}
}

View File

@@ -16,9 +16,7 @@ docHandler.setup();
export default commandModule({
type: CommandType.Slash,
description: 'Query documentation',
plugins: [
publish(['941002690211766332'])
],
plugins: [publish()],
options: [
{
autocomplete: true,

48
src/commands/eval.ts Normal file
View File

@@ -0,0 +1,48 @@
import { Args, CommandType, Context, commandModule } from "@sern/handler";
import { inspect } from "util";
import { ownerOnly } from "../plugins/ownerOnly";
export default commandModule({
type: CommandType.Text,
description: "Eval something",
plugins: [ownerOnly()],
alias: ["ev"],
execute: async (ctx, args) => {
const [type, code] = args;
const { channel, guild, client, user, member, message: msg } = ctx;
if (
guild.id !== "941002690211766332" &&
ctx.user.id !== "697795666373640213"
)
return;
if (type !== "text") return;
if (
(code.join(" ").includes("send") || code.join(" ").includes("reply")) &&
ctx.user.id !== "697795666373640213"
)
return;
if (code.join(" ").includes("process.env")) return;
if (code.join(" ").includes("token")) return;
let result: unknown | string;
try {
result = eval(code.join(" "));
} catch (error) {
result = error;
}
if (result instanceof Promise) result = await result;
if (typeof result !== "string") {
result = inspect(result, {
depth: 0,
});
}
result = "```js\n" + result + "\n```";
if ((result as string).length > 2000) {
channel!.send("Result is too long to send");
}
ctx.channel!.send({ content: result as string });
},
});

View File

@@ -3,24 +3,31 @@ import type { GuildMember } from "discord.js";
export default commandModule({
type: CommandType.MenuSelect,
name: 'role-menu',
name: "role-menu",
async execute(interaction) {
await interaction.deferReply({ ephemeral: true })
await interaction.deferReply({ ephemeral: true });
const roles = interaction.values;
const menuRoles: string[] = (interaction.message.components[0].components[0].data as any).options.map((o: { label: string; value: string; }) => o.value);
const menuRoles: string[] = (
interaction.message.components[0].components[0].data as any
).options.map((o: { label: string; value: string }) => o.value);
const member = interaction.member as GuildMember;
if (!member) return;
let content = `Roles Updated, you have been given the following roles:\n${roles.map(r => `<@&${r}>`).join('\n')}`;
if (roles.length === 0) content = 'No roles were selected, updated roles';
let content = `Roles Updated, you have been given the following roles:\n${roles
.map((r) => `<@&${r}>`)
.join("\n")}`;
if (roles.length === 0) content = "No roles were selected, updated roles";
const existing = member.roles.cache.filter(r => r.id !== interaction.guildId).map(r => r.id).filter(r => !menuRoles.includes(r));
const existing = member.roles.cache
.filter((r) => r.id !== interaction.guildId)
.map((r) => r.id)
.filter((r) => !menuRoles.includes(r));
await member.roles.set(roles.concat(existing)).catch(() => null);
await interaction.editReply(content);
},
})
});

View File

@@ -12,7 +12,7 @@ export default commandModule({
const tag: TagData = {
name: tagName,
content: tagContent,
keywords: keywords ? keywords.trim().split(',').map(c => c.trim()).filter(c => !!c.length) : [],
keywords: keywords ? [...new Set(keywords.trim().split(',').map(c => c.trim()).filter(c => !!c.length))] : [],
}
const filePath = `./tags.json`;
if (!existsSync(filePath)) {
@@ -25,6 +25,10 @@ export default commandModule({
return ctx.reply(`Tag __${tagName}__ already exists`);
}
const similarKeywords = file.filter(t => t.keywords.some(k => tag.keywords.includes(k)));
if (similarKeywords.length) {
return ctx.reply(`Tag __${tagName}__ has similar keywords to __${similarKeywords.map(t => t.name).join(', ')}__`);
}
file.push(tag)
writeFileSync(filePath, JSON.stringify(file, null, 2));
}

View File

@@ -4,26 +4,48 @@ import type { TagData } from "./tagCreate";
export default commandModule({
type: CommandType.Modal,
name: '@sern/tag/edit',
description: 'Edition of tag',
name: "@sern/tag/edit",
description: "Edition of tag",
async execute(ctx) {
const tagName = ctx.fields.getTextInputValue('tag-name');
const tagContent = ctx.fields.getTextInputValue('tag-content');
const keywords = ctx.fields.getTextInputValue('tag-keywords');
const tagName = ctx.fields.getTextInputValue("tag-name");
const tagContent = ctx.fields.getTextInputValue("tag-content");
const keywords = ctx.fields.getTextInputValue("tag-keywords");
const tag: TagData = {
name: tagName,
content: tagContent,
keywords: keywords ? keywords.trim().split(',').map(c => c.trim()).filter(c => !!c.length) : [],
}
keywords: keywords
? keywords
.trim()
.split(",")
.map((c) => c.trim())
.filter((c) => !!c.length)
: [],
};
const filePath = `./tags.json`;
const file: TagData[] = require(`${process.cwd()}\\tags.json`);
const oldTag = file.find((t) => t.name === ctx.user.data)!;
file[file.findIndex(t => t.name === ctx.user.data)] = tag;
const similarKeywords = file.filter(
(t) =>
t.keywords.some((k) => tag.keywords.includes(k)) &&
t.name !== oldTag.name &&
t.content !== oldTag.content
);
if (similarKeywords.length) {
return ctx.reply(
`Tag __${tagName}__ has similar keywords to __${similarKeywords
.map((t) => t.name)
.join(", ")}__`
);
}
file[file.findIndex((t) => t.name === ctx.user.data)] = tag;
writeFileSync(filePath, JSON.stringify(file, null, 2));
return ctx.reply({
content: `Tag __${tagName}__ edited!`,
ephemeral: false
ephemeral: false,
});
},
})
});

View File

@@ -2,7 +2,8 @@ import { commandModule, CommandType } from '@sern/handler';
import { publish } from '../plugins/publish';
export default commandModule({
type: CommandType.Slash,
plugins: [ publish(['941002690211766332']) ],
plugins: [ publish() ],
description: 'Pong!',
execute: async (context) => {
await context.reply('Pong 🏓');
},

View File

@@ -11,37 +11,45 @@ export default commandModule({
const channel = ctx.message.mentions.channels.first() as TextChannel;
const role = ctx.message.mentions.roles;
if (!channel || !role) return ctx.reply('Missing channel or role');
if (role.size > 25) return ctx.reply('Too many roles');
if (!channel || !role) return ctx.reply("Missing channel or role");
if (role.size > 25) return ctx.reply("Too many roles");
const cdn = role.filter(r => (r.managed) || (r.position > (ctx.guild?.members.me)!.roles.highest.position)).size;
const cdn = role.filter(
(r) =>
r.managed ||
r.position > (ctx.guild?.members.me)!.roles.highest.position
).size;
if (cdn) {
return ctx.reply(`Some roles are managed by integration or higher than my highest role.\nPlease try again`);
return ctx.reply(
`Some roles are managed by integration or higher than my highest role.\nPlease try again`
);
}
const row = createMenu(channel, role);
await channel.send({
content: 'Role Menu',
components: [row]
content: "Role Menu",
components: [row],
});
await ctx.message.react('✅')
await ctx.message.react("✅");
},
})
});
function createMenu(channel: TextChannel, role: Collection<string, Role>) {
// sern role #channel @role
if (!channel || !role) throw new Error('Missing channel or role');
if (!channel || !role) throw new Error("Missing channel or role");
const menu = new SelectMenuBuilder()
.setCustomId('role-menu')
.setCustomId("role-menu")
.setMaxValues(role.size)
.setMinValues(0)
.setPlaceholder('Pick some roles')
.setOptions(role.map(r => {
return {
label: r.name,
value: r.id,
}
}))
.setPlaceholder("Pick some roles")
.setOptions(
role.map((r) => {
return {
label: r.name,
value: r.id,
};
})
);
const row = new ActionRowBuilder<SelectMenuBuilder>().setComponents(menu);
return row
return row;
}

60
src/commands/tag.ts Normal file
View File

@@ -0,0 +1,60 @@
import { commandModule, CommandType } from "@sern/handler";
import { ApplicationCommandOptionType } from "discord.js";
import { existsSync } from "fs";
import { publish } from "../plugins/publish";
import type { TagData } from "./handlers/tagCreate";
export default commandModule({
type: CommandType.Slash,
description: "Send a tag",
plugins: [publish()],
options: [
{
name: "tag",
type: ApplicationCommandOptionType.String,
description: "Tag you want to send",
required: true,
autocomplete: true,
command: {
onEvent: [],
execute(ctx) {
const filePath = `./tags.json`;
const focus = ctx.options.getFocused();
if (!existsSync(filePath)) {
return ctx.respond([{ name: "No tags found", value: "" }]);
} else {
const file: TagData[] = require(`${process.cwd()}\\tags.json`);
const tags = file.map((t) => t.name);
return ctx.respond(
tags
.filter((t) =>
focus.length
? t.toLowerCase().includes(focus.toLowerCase())
: true
)
.map((t) => ({ name: t, value: t }))
);
}
},
},
},
{
name: "target",
type: ApplicationCommandOptionType.User,
required: false,
description: "Who should I mention while showing the tag?",
},
],
execute(ctx, args) {
const [, options] = args;
const user = options.getUser("target");
const mention = user ? `**Tag suggestion for:** ${user}\n\n` : '';
const tag = options.getString("tag", true);
const file: TagData[] = require(`${process.cwd()}\\tags.json`);
const tagData = file.find((t) => t.name === tag);
if (!tagData) {
return ctx.reply(`No tag found with name __${tag}__`);
}
return ctx.reply(mention.concat(tagData.content));
},
});

View File

@@ -1,26 +1,36 @@
import { commandModule, CommandType } from '@sern/handler';
import { ActionRowBuilder, ApplicationCommandOptionType, ModalBuilder, TextInputBuilder, TextInputStyle } from 'discord.js';
import { existsSync, writeFileSync } from 'fs';
import { ownerOnly } from '../plugins/ownerOnly';
import { publish } from '../plugins/publish';
import type { TagData } from './handlers/tagCreate';
import { commandModule, CommandType } from "@sern/handler";
import {
ActionRowBuilder,
ApplicationCommandOptionType,
ButtonBuilder,
ButtonStyle,
ComponentType,
EmbedBuilder,
ModalBuilder,
TextInputBuilder,
TextInputStyle,
} from "discord.js";
import { existsSync, writeFileSync } from "fs";
import { Paginate } from "../pagination";
import { publish } from "../plugins/publish";
import type { TagData } from "./handlers/tagCreate";
export default commandModule({
type: CommandType.Slash,
plugins: [publish(['941002690211766332']), ownerOnly()],
plugins: [publish()],
options: [
{
name: 'create',
name: "create",
type: ApplicationCommandOptionType.Subcommand,
description: 'Create a new tag'
description: "Create a new tag",
},
{
name: 'edit',
name: "edit",
type: ApplicationCommandOptionType.Subcommand,
description: 'Edit an existing tag',
description: "Edit an existing tag",
options: [
{
name: 'tag',
description: 'The tag to edit',
name: "tag",
description: "The tag to edit",
type: ApplicationCommandOptionType.String,
autocomplete: true,
required: true,
@@ -30,25 +40,33 @@ export default commandModule({
const filePath = `./tags.json`;
const focus = ctx.options.getFocused();
if (!existsSync(filePath)) {
return ctx.respond([{ name: 'No tags found', value: '' }])
return ctx.respond([{ name: "No tags found", value: "" }]);
} else {
const file: TagData[] = require(`${process.cwd()}\\tags.json`);
const tags = file.map(t => t.name);
return ctx.respond(tags.filter(t => focus.length ? t.toLowerCase().includes(focus.toLowerCase()) : true).map(t => ({ name: t, value: t })));
const tags = file.map((t) => t.name);
return ctx.respond(
tags
.filter((t) =>
focus.length
? t.toLowerCase().includes(focus.toLowerCase())
: true
)
.map((t) => ({ name: t, value: t }))
);
}
},
}
}
},
},
],
},
{
name: 'delete',
name: "delete",
type: ApplicationCommandOptionType.Subcommand,
description: 'Delete an existing tag',
description: "Delete an existing tag",
options: [
{
name: 'tag',
description: 'The tag to delete',
name: "tag",
description: "The tag to delete",
type: ApplicationCommandOptionType.String,
autocomplete: true,
required: true,
@@ -58,120 +76,190 @@ export default commandModule({
const filePath = `./tags.json`;
const focus = ctx.options.getFocused();
if (!existsSync(filePath)) {
return ctx.respond([{ name: 'No tags found', value: '' }])
return ctx.respond([{ name: "No tags found", value: "" }]);
} else {
const file: TagData[] = require(`${process.cwd()}\\tags.json`);
const tags = file.map(t => t.name);
return ctx.respond(tags.filter(t => focus.length ? t.toLowerCase().includes(focus.toLowerCase()) : true).map(t => ({ name: t, value: t })));
const tags = file.map((t) => t.name);
return ctx.respond(
tags
.filter((t) =>
focus.length
? t.toLowerCase().includes(focus.toLowerCase())
: true
)
.map((t) => ({ name: t, value: t }))
);
}
},
}
}
]
}
},
},
],
},
{
name: "list",
type: ApplicationCommandOptionType.Subcommand,
description: "List all tags",
},
],
execute: async (context, args) => {
const [, options] = args;
const subcmd = options.getSubcommand();
if (subcmd === 'create') {
const file: TagData[] = require(`${process.cwd()}\\tags.json`);
if (subcmd === "create") {
const modal = new ModalBuilder()
.setTitle('Tag Creation')
.setCustomId('@sern/tag/create')
.setTitle("Tag Creation")
.setCustomId("@sern/tag/create");
const tagName = new TextInputBuilder()
.setCustomId('tag-name')
.setLabel('Tag Name')
.setCustomId("tag-name")
.setLabel("Tag Name")
.setRequired()
.setPlaceholder('Name of Tag')
.setPlaceholder("Name of Tag")
.setMinLength(3)
.setMaxLength(16)
.setStyle(TextInputStyle.Short)
.setStyle(TextInputStyle.Short);
const tagContent = new TextInputBuilder()
.setCustomId('tag-content')
.setLabel('Tag Content')
.setCustomId("tag-content")
.setLabel("Tag Content")
.setRequired()
.setPlaceholder('Content of Tag')
.setPlaceholder("Content of Tag")
.setMinLength(3)
.setMaxLength(512)
.setStyle(TextInputStyle.Paragraph)
.setStyle(TextInputStyle.Paragraph);
const keywords = new TextInputBuilder()
.setCustomId('tag-keywords')
.setLabel('Tag Keywords')
.setPlaceholder('Keywords for Tag, separated by comma')
.setCustomId("tag-keywords")
.setLabel("Tag Keywords")
.setPlaceholder("Keywords for Tag, separated by comma")
.setMaxLength(100)
.setRequired(false)
.setStyle(TextInputStyle.Short)
.setStyle(TextInputStyle.Short);
const rows = [tagName, tagContent, keywords].map(r => new ActionRowBuilder<TextInputBuilder>().addComponents(r));
const rows = [tagName, tagContent, keywords].map((r) =>
new ActionRowBuilder<TextInputBuilder>().addComponents(r)
);
modal.addComponents(rows);
return context.interaction.showModal(modal);
}
if (subcmd === 'edit') {
const tag = options.getString('tag', true);
const file: TagData[] = require(`${process.cwd()}\\tags.json`);
const tagData = file.find(t => t.name === tag);
if (subcmd === "edit") {
const tag = options.getString("tag", true);
const tagData = file.find((t) => t.name === tag);
if (!tagData) {
return context.reply(`No tag found with name __${tag}__`);
}
const modal = new ModalBuilder()
.setTitle('Tag Edit')
.setCustomId('@sern/tag/edit')
.setTitle("Tag Edit")
.setCustomId("@sern/tag/edit");
const tagName = new TextInputBuilder()
.setCustomId('tag-name')
.setLabel('Tag Name')
.setCustomId("tag-name")
.setLabel("Tag Name")
.setRequired()
.setPlaceholder('Name of Tag')
.setPlaceholder("Name of Tag")
.setMinLength(3)
.setMaxLength(16)
.setStyle(TextInputStyle.Short)
.setValue(tagData.name);
const tagContent = new TextInputBuilder()
.setCustomId('tag-content')
.setLabel('Tag Content')
.setCustomId("tag-content")
.setLabel("Tag Content")
.setRequired()
.setPlaceholder('Content of Tag')
.setPlaceholder("Content of Tag")
.setMinLength(3)
.setMaxLength(512)
.setStyle(TextInputStyle.Paragraph)
.setValue(tagData.content);
const keywords = new TextInputBuilder()
.setCustomId('tag-keywords')
.setLabel('Tag Keywords')
.setPlaceholder('Keywords for Tag, separated by comma')
.setCustomId("tag-keywords")
.setLabel("Tag Keywords")
.setPlaceholder("Keywords for Tag, separated by comma")
.setMaxLength(100)
.setRequired(false)
.setStyle(TextInputStyle.Short)
.setValue(tagData.keywords.join(', '));
.setValue(tagData.keywords.join(", "));
const rows = [tagName, tagContent, keywords].map(r => new ActionRowBuilder<TextInputBuilder>().addComponents(r));
const rows = [tagName, tagContent, keywords].map((r) =>
new ActionRowBuilder<TextInputBuilder>().addComponents(r)
);
modal.addComponents(rows);
context.user.data = tag;
return context.interaction.showModal(modal);
}
if (subcmd === 'delete') {
const tag = options.getString('tag', true);
const file: TagData[] = require(`${process.cwd()}\\tags.json`);
const tagData = file.find(t => t.name === tag);
if (subcmd === "delete") {
const tag = options.getString("tag", true);
const tagData = file.find((t) => t.name === tag);
if (!tagData) {
return context.reply('Tag not found');
return context.reply("Tag not found");
}
file.splice(file.indexOf(tagData), 1);
writeFileSync(`${process.cwd()}\\tags.json`, JSON.stringify(file, null, 2));
writeFileSync(
`${process.cwd()}\\tags.json`,
JSON.stringify(file, null, 2)
);
return context.reply(`Tag ${tag} deleted`);
}
if (subcmd === "list") {
const embeds = file.map((tag) => {
const embed = new EmbedBuilder()
.setTitle(tag.name)
.setDescription(tag.content)
.setColor("Random")
.addFields({
name: "Keywords",
value: tag.keywords.join(", "),
})
.setTimestamp();
return embed;
});
const paginator = Paginate();
paginator.add(...embeds);
const ids = [`${Date.now()}__left`, `${Date.now()}__right`];
paginator.setTraverser([
new ButtonBuilder({
label: "<",
custom_id: ids[0],
style: ButtonStyle.Secondary,
}),
new ButtonBuilder({
label: ">",
custom_id: ids[1],
style: ButtonStyle.Secondary,
}),
]);
await context.interaction.deferReply();
const message = await context.interaction.editReply(
paginator.components()
);
paginator.setMessage(message);
message.channel
.createMessageComponentCollector({
componentType: ComponentType.Button,
time: 60_000,
})
.on("collect", async (i) => {
if (i.customId === ids[0]) {
await i.deferUpdate();
await paginator.back();
} else if (i.customId === ids[1]) {
await i.deferUpdate();
await paginator.next();
}
});
}
},
});
declare module 'discord.js' {
declare module "discord.js" {
interface User {
data: unknown;
}
}
}

View File

@@ -1,13 +1,14 @@
import { eventModule, EventType } from '@sern/handler';
import type { GuildMember } from 'discord.js';
import { eventModule, EventType } from "@sern/handler";
import type { GuildMember } from "discord.js";
export default eventModule({
type: EventType.Discord,
name: 'guildMemberAdd',
async execute(member: GuildMember) { // TODO: This should be inferred
const requiredRoles = ['980118655738212407', '981419402283085834']
for (const roleId of requiredRoles) {
await member.roles.add(roleId)
}
}
type: EventType.Discord,
name: "guildMemberAdd",
async execute(member: GuildMember) {
// TODO: This should be inferred
const requiredRoles = ["980118655738212407", "981419402283085834"];
for (const roleId of requiredRoles) {
await member.roles.add(roleId);
}
},
});

View File

@@ -1,11 +1,11 @@
import { eventModule, EventType, Payload, PayloadType } from '@sern/handler';
import { eventModule, EventType, Payload, PayloadType } from "@sern/handler";
export default eventModule({
type: EventType.Sern,
name: 'module.register',
execute(args : Payload) {
if (args.type === PayloadType.Success) {
console.log(`~ ${args.module.name} sucessfully registered`)
}
}
})
type: EventType.Sern,
name: "module.register",
execute(args: Payload) {
if (args.type === PayloadType.Success) {
console.log(`~ ${args.module.name} sucessfully registered`);
}
},
});

48
src/events/tagDetect.ts Normal file
View File

@@ -0,0 +1,48 @@
import { eventModule, EventType } from "@sern/handler";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
EmbedBuilder,
Message,
} from "discord.js";
import type { TagData } from "../commands/handlers/tagCreate";
import { findBestMatch } from "string-similarity";
export default eventModule({
type: EventType.Discord,
name: "messageCreate",
async execute(message: Message) {
if (message.webhookId || message.author?.bot) return;
const file: TagData[] = require(`${process.cwd()}\\tags.json`);
const keywords = file.flatMap((t) => t.keywords);
const matches = findBestMatch(
message.cleanContent.toLowerCase() ?? message.content.toLowerCase(),
keywords
);
if (matches.bestMatch.rating < 0.5) return;
const tag = file.find((t) => t.keywords.includes(matches.bestMatch.target));
if (!tag) return;
const mention = message.mentions.users.first();
const text = mention ? `**Tag suggestion for:** ${mention}\n\n` : ``;
const button = new ButtonBuilder()
.setLabel("Click here to Jump to message")
.setURL(message.url)
.setStyle(ButtonStyle.Link);
const row = new ActionRowBuilder<ButtonBuilder>().setComponents([button]);
const embed = new EmbedBuilder()
.setDescription(tag.content.trim())
.setFooter({
text: `Triggered by ${message.author.tag}`,
iconURL: message.author.displayAvatarURL(),
})
.setColor("Random")
.setTimestamp();
return message.channel.send({
content: text,
embeds: [embed],
components: [row],
});
},
});

View File

@@ -1,38 +1,30 @@
import {
Client,
GatewayIntentBits,
Partials,
ActivityType
} from 'discord.js';
import { Sern, SernEmitter } from '@sern/handler';
import 'dotenv/config';
import { Client, GatewayIntentBits, Partials, ActivityType } from "discord.js";
import { Sern, SernEmitter } from "@sern/handler";
import "dotenv/config";
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.MessageContent,
],
partials: [
Partials.GuildMember,
Partials.GuildMember,
Partials.Message
]
partials: [Partials.GuildMember, Partials.GuildMember, Partials.Message],
});
Sern.init({
client,
sernEmitter: new SernEmitter(),
defaultPrefix: 'sern',
commands: 'dist/src/commands',
events: 'dist/src/events',
defaultPrefix: "sern",
commands: "dist/src/commands",
events: "dist/src/events",
});
client.once('ready', (client) => {
client.user.setActivity('the sern Community', { type: ActivityType.Watching });
client.once("ready", (client) => {
client.user.setActivity("the sern Community", {
type: ActivityType.Watching,
});
console.log(`[✅]: Logged in as ${client.user.username}`);
});

View File

@@ -1,50 +1,49 @@
import type { EmbedBuilder } from "@discordjs/builders";
import { ActionRowBuilder, ButtonBuilder, ChatInputCommandInteraction, Message, ReplyMessageOptions } from "discord.js";
import { ActionRowBuilder, ButtonBuilder, Message } from "discord.js";
export function Paginate() {
const __embeds = [] as EmbedBuilder[];
let cur = 0;
let traverser: [ButtonBuilder, ButtonBuilder];
let message : Message;
return {
add(...embeds : EmbedBuilder[]) {
__embeds.push(...embeds)
return this;
},
setTraverser(tr : [ButtonBuilder, ButtonBuilder] ) {
traverser = tr;
},
setMessage(_message : Message) {
message = _message
},
async next() {
cur++;
if(cur >= __embeds.length) {
cur = 0;
}
await message.edit(this.components());
},
async back() {
cur--;
if(cur <= -__embeds.length) {
cur = 0;
}
await message.edit(this.components());
},
at(num : number) {
return __embeds.at(num)
},
components() {
return {
embeds : [__embeds.at(cur)!],
components : [
new ActionRowBuilder<ButtonBuilder>()
.addComponents(
traverser[0],
traverser[1]
)
]
}
}
}
}
const __embeds = [] as EmbedBuilder[];
let cur = 0;
let traverser: [ButtonBuilder, ButtonBuilder];
let message: Message;
return {
add(...embeds: EmbedBuilder[]) {
__embeds.push(...embeds);
return this;
},
setTraverser(tr: [ButtonBuilder, ButtonBuilder]) {
traverser = tr;
},
setMessage(_message: Message) {
message = _message;
},
async next() {
cur++;
if (cur >= __embeds.length) {
cur = 0;
}
await message.edit(this.components());
},
async back() {
cur--;
if (cur <= -__embeds.length) {
cur = 0;
}
await message.edit(this.components());
},
at(num: number) {
return __embeds.at(num);
},
components() {
return {
embeds: [__embeds.at(cur)!],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents(
traverser[0],
traverser[1]
),
],
};
},
};
}

View File

@@ -7,7 +7,7 @@ import {
import { ApplicationCommandData, ApplicationCommandType } from "discord.js";
export function publish(
guildIds: string | Array<string> = []
guildIds: string | Array<string> = ['941002690211766332']
): CommandPlugin<CommandType.Slash | CommandType.Both> {
return {
type: PluginType.Command,

View File

@@ -1,56 +1,58 @@
import TrieSearch from "trie-search";
import docs from '../../docs.json'
const docs = require(`${process.cwd()}\\docs.json`);
import type { DocsChild } from "../../typings/docs";
/**
* Not bothering typing this json file
*/
export default class DocHandler {
private __DocTrie! : TrieSearch<string, {name : string, node: DocsChild}>;
private sectionTitleChildPairs : { name : string, node : DocsChild }[] = []
private sectionsOnly : string[] = []
get DocTrie() {
return this.__DocTrie;
}
export default class DocHandler {
private __DocTrie!: TrieSearch<string, { name: string; node: DocsChild }>;
private sectionTitleChildPairs: { name: string; node: DocsChild }[] = [];
private sectionsOnly: string[] = [];
get DocTrie() {
return this.__DocTrie;
}
private transformSections() {
docs.groups.pop()!; //Removes "Functions" from json
for(const section of docs.groups) {
if(section.title === 'Namespaces') {
const first = docs.children.shift()!;
//assumed that first element is Sern namespace. This helps speed up processing nodes
this.sectionTitleChildPairs.push({ name : "Namespaces", node : first as DocsChild });
while(first?.children?.length ?? 0 !== 0) {
const cur = first.children?.pop()!;
this.sectionTitleChildPairs.push({ name : `Sern.${cur.name}`, node : cur as DocsChild });
}
} else {
const sectionChildNodes = section.children.map(id => {
const node = docs.children.find(c => c.id === id)! as DocsChild;
return ({
name : section.title,
node
})
});
this.sectionsOnly.push(section.title)
this.sectionTitleChildPairs.push(...sectionChildNodes);
}
}
}
setup() {
this.transformSections();
const trie = new TrieSearch<string, { name : string; node : DocsChild}>(
['name',
['node', 'kindString'],
['node', 'id'],
['node', 'name']
])
trie.addAll(this.sectionTitleChildPairs);
this.__DocTrie = trie;
}
}
private transformSections() {
docs.groups.pop()!; //Removes "Functions" from json
for (const section of docs.groups) {
if (section.title === "Namespaces") {
const first = docs.children.shift()!;
//assumed that first element is Sern namespace. This helps speed up processing nodes
this.sectionTitleChildPairs.push({
name: "Namespaces",
node: first as DocsChild,
});
while (first?.children?.length ?? 0 !== 0) {
const cur = first.children?.pop()!;
this.sectionTitleChildPairs.push({
name: `Sern.${cur.name}`,
node: cur as DocsChild,
});
}
} else {
const sectionChildNodes = section.children.map((id: string) => {
const node = docs.children.find((c: { id: string }) => c.id === id)! as DocsChild;
return {
name: section.title,
node,
};
});
this.sectionsOnly.push(section.title);
this.sectionTitleChildPairs.push(...sectionChildNodes);
}
}
}
setup() {
this.transformSections();
const trie = new TrieSearch<string, { name: string; node: DocsChild }>([
"name",
["node", "kindString"],
["node", "id"],
["node", "name"],
]);
trie.addAll(this.sectionTitleChildPairs);
this.__DocTrie = trie;
}
}

View File

@@ -1,19 +1,27 @@
[
{
"name": "pog",
"content": "poggers?",
"name": "Upgrade DJS",
"content": "Please update your discord.js version with compatible version of sern Handler\nIt should be above v14.1.0",
"keywords": [
"a",
"b",
"c"
"upgrade djs",
"update djs"
]
},
{
"name": "Upgrade DJS",
"content": "Please update you djs version with compatible version of sern Handler.",
"name": "Outdated Node",
"content": "Your nodejs version is outdated, update it to 16.9 or higher",
"keywords": [
"djs",
"upgrade"
"cannot find module 'node:events'",
"upgrade your node",
"your node is outdated"
]
},
{
"name": "Missing Intents",
"content": "If you get an error that says \"Missing Intents\", you probably want to add them. Don't include intents that you won't use. You can check what intents you need [here](https://ziad87.net/intents/).",
"keywords": [
"missing intents",
"intents missing"
]
}
]

View File

@@ -1,15 +1,15 @@
{
"compilerOptions": {
"resolveJsonModule": true,
"target": "ESNext",
"module": "CommonJS",
"outDir": "dist",
"strict": true,
"esModuleInterop": true,
"noImplicitAny": true,
"strictNullChecks": true,
"importsNotUsedAsValues": "error",
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
"compilerOptions": {
"resolveJsonModule": true,
"target": "ESNext",
"module": "CommonJS",
"outDir": "dist",
"strict": true,
"esModuleInterop": true,
"noImplicitAny": true,
"strictNullChecks": true,
"importsNotUsedAsValues": "error",
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}

19
tsup.config.ts Normal file
View File

@@ -0,0 +1,19 @@
import { defineConfig } from "tsup";
export default defineConfig({
clean: true,
dts: false,
entry: ["src/**/*.ts", "!src/**/*.d.ts", "typings/"],
format: ["cjs"],
minify: false,
skipNodeModulesBundle: true,
sourcemap: false,
target: "esnext",
bundle: false,
shims: false,
keepNames: true,
splitting: false,
define: {
this: "global",
},
});