mirror of
https://github.com/sern-handler/sern-community
synced 2026-06-06 01:16:57 +00:00
refactor(*): lots of shit
This commit is contained in:
3
.prettierrc
Normal file
3
.prettierrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"useTabs": true
|
||||
}
|
||||
2272
package-lock.json
generated
2272
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
48
src/commands/eval.ts
Normal 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 });
|
||||
},
|
||||
});
|
||||
@@ -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);
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
@@ -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 🏓');
|
||||
},
|
||||
|
||||
@@ -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
60
src/commands/tag.ts
Normal 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));
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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
48
src/events/tagDetect.ts
Normal 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],
|
||||
});
|
||||
},
|
||||
});
|
||||
32
src/index.ts
32
src/index.ts
@@ -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}`);
|
||||
});
|
||||
|
||||
@@ -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]
|
||||
),
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
26
tags.json
26
tags.json
@@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -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
19
tsup.config.ts
Normal 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",
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user