mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-05-25 02:25:01 +00:00
Merge 02759f2e87
into 016330366e
This commit is contained in:
commit
b0053c6849
16 changed files with 359 additions and 47 deletions
|
@ -30,6 +30,7 @@ const migrationsDir = path.relative(process.cwd(), path.resolve(backendRoot, "sr
|
|||
module.exports = {
|
||||
type: "mysql",
|
||||
host: process.env.DB_HOST,
|
||||
port: 13306,
|
||||
username: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_DATABASE,
|
||||
|
|
22
backend/package-lock.json
generated
22
backend/package-lock.json
generated
|
@ -13,7 +13,7 @@
|
|||
"cors": "^2.8.5",
|
||||
"cross-env": "^5.2.0",
|
||||
"deep-diff": "^1.0.2",
|
||||
"discord-api-types": "^0.31.0",
|
||||
"discord-api-types": "^0.31.2",
|
||||
"discord.js": "^13.6.0",
|
||||
"dotenv": "^4.0.0",
|
||||
"emoji-regex": "^8.0.0",
|
||||
|
@ -1693,8 +1693,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/discord-api-types": {
|
||||
"version": "0.31.0",
|
||||
"license": "MIT"
|
||||
"version": "0.31.2",
|
||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.31.2.tgz",
|
||||
"integrity": "sha512-gpzXTvFVg7AjKVVJFH0oJGC0q0tO34iJGSHZNz9u3aqLxlD6LfxEs9wWVVikJqn9gra940oUTaPFizCkRDcEiA=="
|
||||
},
|
||||
"node_modules/discord.js": {
|
||||
"version": "13.6.0",
|
||||
|
@ -3018,8 +3019,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.24.0",
|
||||
"license": "MIT",
|
||||
"version": "2.29.3",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
|
||||
"integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
|
@ -6346,7 +6348,9 @@
|
|||
}
|
||||
},
|
||||
"discord-api-types": {
|
||||
"version": "0.31.0"
|
||||
"version": "0.31.2",
|
||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.31.2.tgz",
|
||||
"integrity": "sha512-gpzXTvFVg7AjKVVJFH0oJGC0q0tO34iJGSHZNz9u3aqLxlD6LfxEs9wWVVikJqn9gra940oUTaPFizCkRDcEiA=="
|
||||
},
|
||||
"discord.js": {
|
||||
"version": "13.6.0",
|
||||
|
@ -6425,7 +6429,7 @@
|
|||
},
|
||||
"erlpack": {
|
||||
"version": "git+ssh://git@github.com/discord/erlpack.git#3b793a333dd3f6a140b9168ea91e9fa9660753ce",
|
||||
"from": "erlpack@github:discord/erlpack.git",
|
||||
"from": "erlpack@github:discord/erlpack",
|
||||
"requires": {
|
||||
"bindings": "^1.5.0",
|
||||
"nan": "^2.15.0"
|
||||
|
@ -7175,7 +7179,9 @@
|
|||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.24.0"
|
||||
"version": "2.29.3",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
|
||||
"integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw=="
|
||||
},
|
||||
"moment-timezone": {
|
||||
"version": "0.5.27",
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"cors": "^2.8.5",
|
||||
"cross-env": "^5.2.0",
|
||||
"deep-diff": "^1.0.2",
|
||||
"discord-api-types": "^0.31.0",
|
||||
"discord-api-types": "^0.31.2",
|
||||
"discord.js": "^13.6.0",
|
||||
"dotenv": "^4.0.0",
|
||||
"emoji-regex": "^8.0.0",
|
||||
|
|
66
backend/src/data/GuildTagAliases.ts
Normal file
66
backend/src/data/GuildTagAliases.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
import { getRepository, Repository } from "typeorm";
|
||||
import { BaseGuildRepository } from "./BaseGuildRepository";
|
||||
import { TagAlias } from "./entities/TagAlias";
|
||||
|
||||
export class GuildTagAliases extends BaseGuildRepository {
|
||||
private tagAliases: Repository<TagAlias>;
|
||||
|
||||
constructor(guildId) {
|
||||
super(guildId);
|
||||
this.tagAliases = getRepository(TagAlias);
|
||||
}
|
||||
|
||||
async all(): Promise<TagAlias[]> {
|
||||
return this.tagAliases.find({
|
||||
where: {
|
||||
guild_id: this.guildId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async find(alias): Promise<TagAlias | undefined> {
|
||||
return this.tagAliases.findOne({
|
||||
where: {
|
||||
guild_id: this.guildId,
|
||||
alias,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async findAllWithTag(tag): Promise<TagAlias[] | undefined> {
|
||||
const all = await this.all();
|
||||
const aliases = all.filter((a) => a.tag === tag);
|
||||
return aliases.length > 0 ? aliases : undefined;
|
||||
}
|
||||
|
||||
async createOrUpdate(alias, tag, userId) {
|
||||
const existingTagAlias = await this.find(alias);
|
||||
if (existingTagAlias) {
|
||||
await this.tagAliases
|
||||
.createQueryBuilder()
|
||||
.update()
|
||||
.set({
|
||||
tag,
|
||||
user_id: userId,
|
||||
created_at: () => "NOW()",
|
||||
})
|
||||
.where("guild_id = :guildId", { guildId: this.guildId })
|
||||
.andWhere("alias = :alias", { alias })
|
||||
.execute();
|
||||
} else {
|
||||
await this.tagAliases.insert({
|
||||
guild_id: this.guildId,
|
||||
user_id: userId,
|
||||
alias,
|
||||
tag,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async delete(alias) {
|
||||
await this.tagAliases.delete({
|
||||
guild_id: this.guildId,
|
||||
alias,
|
||||
});
|
||||
}
|
||||
}
|
20
backend/src/data/entities/TagAlias.ts
Normal file
20
backend/src/data/entities/TagAlias.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { Column, Entity, PrimaryColumn } from "typeorm";
|
||||
|
||||
@Entity("tag_aliases")
|
||||
export class TagAlias {
|
||||
@Column()
|
||||
@PrimaryColumn()
|
||||
guild_id: string;
|
||||
|
||||
@Column()
|
||||
@PrimaryColumn()
|
||||
alias: string;
|
||||
|
||||
@Column()
|
||||
@PrimaryColumn()
|
||||
tag: string;
|
||||
|
||||
@Column() user_id: string;
|
||||
|
||||
@Column() created_at: string;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
import { MigrationInterface, QueryRunner, Table } from "typeorm";
|
||||
|
||||
export class CreateTagAliasesTable1650721595278 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
name: "tag_aliases",
|
||||
columns: [
|
||||
{
|
||||
name: "guild_id",
|
||||
type: "bigint",
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
name: "alias",
|
||||
type: "varchar",
|
||||
length: "255",
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
name: "tag",
|
||||
type: "varchar",
|
||||
length: "255",
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
name: "user_id",
|
||||
type: "bigint",
|
||||
},
|
||||
{
|
||||
name: "created_at",
|
||||
type: "datetime",
|
||||
default: "now()",
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
queryRunner.dropTable("tag_aliases");
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import { GuildArchives } from "../../data/GuildArchives";
|
|||
import { GuildLogs } from "../../data/GuildLogs";
|
||||
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
||||
import { GuildTags } from "../../data/GuildTags";
|
||||
import { GuildTagAliases } from "src/data/GuildTagAliases";
|
||||
import { mapToPublicFn } from "../../pluginUtils";
|
||||
import { convertDelayStringToMS, trimPluginDescription } from "../../utils";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
|
@ -68,7 +69,7 @@ export const TagsPlugin = zeppelinGuildPlugin<TagsPluginType>()({
|
|||
You use them by adding a \`{}\` on your tag.
|
||||
|
||||
Here are the functions you can use in your tags:
|
||||
|
||||
|
||||
${generateTemplateMarkdown(TemplateFunctions)}
|
||||
`),
|
||||
},
|
||||
|
@ -123,6 +124,7 @@ export const TagsPlugin = zeppelinGuildPlugin<TagsPluginType>()({
|
|||
|
||||
state.archives = GuildArchives.getGuildInstance(guild.id);
|
||||
state.tags = GuildTags.getGuildInstance(guild.id);
|
||||
state.tagAliases = GuildTagAliases.getGuildInstance(guild.id);
|
||||
state.savedMessages = GuildSavedMessages.getGuildInstance(guild.id);
|
||||
state.logs = new GuildLogs(guild.id);
|
||||
|
||||
|
|
|
@ -8,11 +8,24 @@ export const TagCreateCmd = tagsCmd({
|
|||
permission: "can_create",
|
||||
|
||||
signature: {
|
||||
alias: ct.bool({ option: true, shortcut: "a", isSwitch: true }),
|
||||
tag: ct.string(),
|
||||
body: ct.string({ catchAll: true }),
|
||||
},
|
||||
|
||||
async run({ message: msg, args, pluginData }) {
|
||||
const prefix = pluginData.config.get().prefix;
|
||||
|
||||
if (args.alias) {
|
||||
const existingTag = await pluginData.state.tagAliases.find(args.body);
|
||||
if (existingTag) {
|
||||
sendErrorMessage(pluginData, msg.channel, `You cannot create an alias of an alias`);
|
||||
return;
|
||||
}
|
||||
await pluginData.state.tagAliases.createOrUpdate(args.tag, args.body, msg.author.id);
|
||||
sendSuccessMessage(pluginData, msg.channel, `Alias set! Use it with: \`${prefix}${args.tag}\``);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
parseTemplate(args.body);
|
||||
} catch (e) {
|
||||
|
@ -26,7 +39,6 @@ export const TagCreateCmd = tagsCmd({
|
|||
|
||||
await pluginData.state.tags.createOrUpdate(args.tag, args.body, msg.author.id);
|
||||
|
||||
const prefix = pluginData.config.get().prefix;
|
||||
sendSuccessMessage(pluginData, msg.channel, `Tag set! Use it with: \`${prefix}${args.tag}\``);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -11,13 +11,25 @@ export const TagDeleteCmd = tagsCmd({
|
|||
},
|
||||
|
||||
async run({ message: msg, args, pluginData }) {
|
||||
const alias = await pluginData.state.tagAliases.find(args.tag);
|
||||
const tag = await pluginData.state.tags.find(args.tag);
|
||||
if (!tag) {
|
||||
|
||||
if (!tag && !alias) {
|
||||
sendErrorMessage(pluginData, msg.channel, "No tag with that name");
|
||||
return;
|
||||
}
|
||||
|
||||
await pluginData.state.tags.delete(args.tag);
|
||||
sendSuccessMessage(pluginData, msg.channel, "Tag deleted!");
|
||||
if (tag) {
|
||||
const aliasesOfTag = await pluginData.state.tagAliases.findAllWithTag(tag?.tag);
|
||||
if (aliasesOfTag) {
|
||||
// tslint:disable-next-line:no-shadowed-variable
|
||||
aliasesOfTag.forEach((alias) => pluginData.state.tagAliases.delete(alias.alias));
|
||||
}
|
||||
await pluginData.state.tags.delete(args.tag);
|
||||
} else {
|
||||
await pluginData.state.tagAliases.delete(alias?.alias);
|
||||
}
|
||||
|
||||
sendSuccessMessage(pluginData, msg.channel, `${tag ? "Tag" : "Alias"} deleted!`);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import { sendErrorMessage } from "src/pluginUtils";
|
||||
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { createChunkedMessage } from "../../../utils";
|
||||
import { tagsCmd } from "../types";
|
||||
|
||||
|
@ -5,16 +8,61 @@ export const TagListCmd = tagsCmd({
|
|||
trigger: ["tag list", "tags", "taglist"],
|
||||
permission: "can_list",
|
||||
|
||||
async run({ message: msg, pluginData }) {
|
||||
signature: {
|
||||
noaliases: ct.bool({ option: true, isSwitch: true, shortcut: "na" }),
|
||||
aliasesonly: ct.bool({ option: true, isSwitch: true, shortcut: "ao" }),
|
||||
tag: ct.string({ option: true }),
|
||||
},
|
||||
|
||||
async run({ message: msg, args, pluginData }) {
|
||||
const prefix = (await pluginData.config.getForMessage(msg)).prefix;
|
||||
const tags = await pluginData.state.tags.all();
|
||||
if (tags.length === 0) {
|
||||
msg.channel.send(`No tags created yet! Use \`tag create\` command to create one.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const prefix = (await pluginData.config.getForMessage(msg)).prefix;
|
||||
const tagNames = tags.map((tag) => tag.tag).sort();
|
||||
const allAliases = await pluginData.state.tagAliases.all();
|
||||
|
||||
createChunkedMessage(msg.channel, `Available tags (use with ${prefix}tag): \`\`\`${tagNames.join(", ")}\`\`\``);
|
||||
if (args.aliasesonly) {
|
||||
let aliasesArr: string[] = [];
|
||||
if (args.tag) {
|
||||
const tag = await pluginData.state.tags.find(args.tag);
|
||||
if (!tag) {
|
||||
sendErrorMessage(pluginData, msg.channel, `Tag \`${args.tag}\` doesn't exist.`);
|
||||
return;
|
||||
}
|
||||
const aliasesForTag = await pluginData.state.tagAliases.findAllWithTag(args.tag);
|
||||
if (!aliasesForTag) {
|
||||
sendErrorMessage(pluginData, msg.channel, `No aliases for tag \`${args.tag}\`.`);
|
||||
return;
|
||||
}
|
||||
aliasesArr = aliasesForTag.map((a) => a.alias);
|
||||
createChunkedMessage(
|
||||
msg.channel,
|
||||
`Available aliases for tag \`${args.tag}\` (use with \`${prefix}alias\`: \`\`\`${aliasesArr.join(
|
||||
", ",
|
||||
)}\`\`\``,
|
||||
);
|
||||
return;
|
||||
}
|
||||
aliasesArr = allAliases.map((a) => a.alias);
|
||||
createChunkedMessage(
|
||||
msg.channel,
|
||||
`Available aliases (use with \`${prefix}alias\`: \`\`\`${aliasesArr.join(", ")}\`\`\``,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const tagNames = tags.map((tag) => tag.tag).sort();
|
||||
const tagAliasesNames = allAliases.map((alias) => alias.alias).sort();
|
||||
const tagAndAliasesNames = tagNames
|
||||
.join(", ")
|
||||
.concat(args.noaliases ? "" : tagAliasesNames.length > 0 ? `, ${tagAliasesNames.join(", ")}` : "");
|
||||
|
||||
createChunkedMessage(
|
||||
msg.channel,
|
||||
`Available tags (use with ${prefix}tag/alias): \`\`\`${tagAndAliasesNames}\`\`\``,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -14,24 +14,42 @@ export const TagSourceCmd = tagsCmd({
|
|||
},
|
||||
|
||||
async run({ message: msg, args, pluginData }) {
|
||||
const alias = await pluginData.state.tagAliases.find(args.tag);
|
||||
const aliasedTag = await pluginData.state.tags.find(alias?.tag ?? null);
|
||||
const tag = (await pluginData.state.tags.find(args.tag)) || aliasedTag;
|
||||
|
||||
if (args.delete) {
|
||||
const actualTag = await pluginData.state.tags.find(args.tag);
|
||||
if (!actualTag) {
|
||||
|
||||
if (!actualTag && !aliasedTag) {
|
||||
sendErrorMessage(pluginData, msg.channel, "No tag with that name");
|
||||
return;
|
||||
}
|
||||
|
||||
await pluginData.state.tags.delete(args.tag);
|
||||
sendSuccessMessage(pluginData, msg.channel, "Tag deleted!");
|
||||
if (actualTag) {
|
||||
const aliasesOfTag = await pluginData.state.tagAliases.findAllWithTag(actualTag?.tag);
|
||||
if (aliasesOfTag) {
|
||||
// tslint:disable-next-line:no-shadowed-variable
|
||||
aliasesOfTag.forEach((alias) => pluginData.state.tagAliases.delete(alias.alias));
|
||||
}
|
||||
await pluginData.state.tags.delete(args.tag);
|
||||
} else {
|
||||
await pluginData.state.tagAliases.delete(alias?.alias);
|
||||
}
|
||||
|
||||
sendSuccessMessage(pluginData, msg.channel, `${actualTag ? "Tag" : "Alias"} deleted!`);
|
||||
return;
|
||||
}
|
||||
|
||||
const tag = await pluginData.state.tags.find(args.tag);
|
||||
if (!tag) {
|
||||
if (!tag && !aliasedTag) {
|
||||
sendErrorMessage(pluginData, msg.channel, "No tag with that name");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tag?.body) {
|
||||
return;
|
||||
}
|
||||
|
||||
const archiveId = await pluginData.state.archives.create(tag.body, moment.utc().add(10, "minutes"));
|
||||
const url = pluginData.state.archives.getUrl(getBaseUrl(pluginData), archiveId);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import { GuildArchives } from "../../data/GuildArchives";
|
|||
import { GuildLogs } from "../../data/GuildLogs";
|
||||
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
||||
import { GuildTags } from "../../data/GuildTags";
|
||||
import { GuildTagAliases } from "../../data/GuildTagAliases";
|
||||
import { tEmbed, tNullable } from "../../utils";
|
||||
|
||||
export const Tag = t.union([t.string, tEmbed]);
|
||||
|
@ -50,6 +51,7 @@ export interface TagsPluginType extends BasePluginType {
|
|||
state: {
|
||||
archives: GuildArchives;
|
||||
tags: GuildTags;
|
||||
tagAliases: GuildTagAliases;
|
||||
savedMessages: GuildSavedMessages;
|
||||
logs: GuildLogs;
|
||||
|
||||
|
|
|
@ -20,8 +20,18 @@ export async function findTagByName(
|
|||
return config.categories[categoryName]?.tags[tagName] ?? null;
|
||||
}
|
||||
|
||||
let tag: string | null;
|
||||
|
||||
// Dynamic tag
|
||||
// Format: "tag"
|
||||
const dynamicTag = await pluginData.state.tags.find(name);
|
||||
return dynamicTag?.body ?? null;
|
||||
tag = dynamicTag?.body ?? null;
|
||||
|
||||
// Aliased tag
|
||||
// Format: "alias"
|
||||
const aliasedTagName = await pluginData.state.tagAliases.find(name);
|
||||
const aliasedTag = await pluginData.state.tags.find(aliasedTagName?.tag);
|
||||
tag ? (tag = tag) : (tag = aliasedTag?.body ?? null);
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
|
30
backend/src/plugins/Tags/util/fuzzySearch.ts
Normal file
30
backend/src/plugins/Tags/util/fuzzySearch.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
// https://rosettacode.org/wiki/Levenshtein_distance#JavaScript
|
||||
|
||||
function levenshtein(a: string, b: string): number {
|
||||
let t: number[] = [];
|
||||
let u: number[] = [];
|
||||
const m = a.length;
|
||||
const n = b.length;
|
||||
if (!m) {
|
||||
return n;
|
||||
}
|
||||
if (!n) {
|
||||
return m;
|
||||
}
|
||||
for (let j = 0; j <= n; j++) {
|
||||
t[j] = j;
|
||||
}
|
||||
for (let i = 1; i <= m; i++) {
|
||||
let j: number;
|
||||
// tslint:disable-next-line:ban-comma-operator
|
||||
for (u = [i], j = 1; j <= n; j++) {
|
||||
u[j] = a[i - 1] === b[j - 1] ? t[j - 1] : Math.min(t[j - 1], t[j], u[j - 1]) + 1;
|
||||
}
|
||||
t = u;
|
||||
}
|
||||
return u[n];
|
||||
}
|
||||
|
||||
export function distance(str: string, t: string): number {
|
||||
return levenshtein(str, t);
|
||||
}
|
|
@ -1,9 +1,12 @@
|
|||
import { GuildMember } from "discord.js";
|
||||
import { GuildMember, Snowflake, TextChannel } from "discord.js";
|
||||
import escapeStringRegexp from "escape-string-regexp";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { ExtendedMatchParams } from "knub/dist/config/PluginConfigManager";
|
||||
|
||||
import { StrictMessageContent } from "../../../utils";
|
||||
import { TagsPluginType, TTagCategory } from "../types";
|
||||
|
||||
import { distance } from "./fuzzySearch";
|
||||
import { renderTagFromString } from "./renderTagFromString";
|
||||
|
||||
interface BaseResult {
|
||||
|
@ -44,7 +47,8 @@ export async function matchAndRenderTagFromString(
|
|||
|
||||
const withoutPrefix = str.slice(prefix.length);
|
||||
|
||||
for (const [tagName, tagBody] of Object.entries(category.tags)) {
|
||||
// tslint:disable-next-line:no-shadowed-variable
|
||||
for (const [tagName, _tagBody] of Object.entries(category.tags)) {
|
||||
const regex = new RegExp(`^${escapeStringRegexp(tagName)}(?:\\s|$)`);
|
||||
if (regex.test(withoutPrefix)) {
|
||||
const renderedContent = await renderTagFromString(
|
||||
|
@ -70,43 +74,79 @@ export async function matchAndRenderTagFromString(
|
|||
}
|
||||
}
|
||||
|
||||
// Dynamic tags
|
||||
// Dynamic + Aliased tags
|
||||
if (config.can_use !== true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const dynamicTagPrefix = config.prefix;
|
||||
if (!str.startsWith(dynamicTagPrefix)) {
|
||||
const tagPrefix = config.prefix;
|
||||
if (!str.startsWith(tagPrefix)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const dynamicTagNameMatch = str.slice(dynamicTagPrefix.length).match(/^\S+/);
|
||||
if (dynamicTagNameMatch === null) {
|
||||
const tagNameMatch = str.slice(tagPrefix.length).match(/^\S+/);
|
||||
if (tagNameMatch == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const dynamicTagName = dynamicTagNameMatch[0];
|
||||
const dynamicTag = await pluginData.state.tags.find(dynamicTagName);
|
||||
if (!dynamicTag) {
|
||||
const tagName = tagNameMatch[0];
|
||||
|
||||
const aliasName = await pluginData.state.tagAliases.find(tagName);
|
||||
const aliasedTag = await pluginData.state.tags.find(aliasName?.tag);
|
||||
const dynamicTag = await pluginData.state.tags.find(tagName);
|
||||
|
||||
if (!aliasedTag && !dynamicTag) {
|
||||
// fuzzy search the list of aliases and tags to see if there's a match and
|
||||
// inform the user
|
||||
const tags = await pluginData.state.tags.all();
|
||||
const aliases = await pluginData.state.tagAliases.all();
|
||||
let lowest: [number, [string]] = [999999, [""]];
|
||||
tags.forEach((tag) => {
|
||||
const tagname = tag?.tag;
|
||||
const dist = distance(tagname, tagName);
|
||||
if (dist < lowest[0]) {
|
||||
lowest = [dist, [`**${tagname}**`]];
|
||||
} else if (dist === lowest[0]) {
|
||||
lowest[1].push(`**${tagname}**`);
|
||||
}
|
||||
});
|
||||
aliases.forEach((alias) => {
|
||||
const aliasname = alias?.alias;
|
||||
const dist = distance(aliasname, tagName);
|
||||
if (dist < lowest[0]) {
|
||||
lowest = [dist, [`**${aliasname}**`]];
|
||||
} else if (dist === lowest[0]) {
|
||||
lowest[1].push(`**${aliasname}**`);
|
||||
}
|
||||
});
|
||||
if (lowest[0] > 6) return null;
|
||||
const content: StrictMessageContent = {
|
||||
content: `Did you mean:\n${lowest[1].join("\n")}`,
|
||||
};
|
||||
|
||||
return {
|
||||
renderedContent: content,
|
||||
tagName: "",
|
||||
category: null,
|
||||
categoryName: null,
|
||||
};
|
||||
}
|
||||
|
||||
const tagBody = aliasedTag?.body ?? dynamicTag?.body;
|
||||
|
||||
if (!tagBody) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const renderedDynamicTagContent = await renderTagFromString(
|
||||
pluginData,
|
||||
str,
|
||||
dynamicTagPrefix,
|
||||
dynamicTagName,
|
||||
dynamicTag.body,
|
||||
member,
|
||||
);
|
||||
const renderedTagContent = await renderTagFromString(pluginData, str, tagPrefix, tagName, tagBody, member);
|
||||
|
||||
if (renderedDynamicTagContent == null) {
|
||||
if (renderedTagContent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
renderedContent: renderedDynamicTagContent,
|
||||
tagName: dynamicTagName,
|
||||
renderedContent: renderedTagContent,
|
||||
tagName,
|
||||
categoryName: null,
|
||||
category: null,
|
||||
};
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import { Snowflake, TextChannel } from "discord.js";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { erisAllowedMentionsToDjsMentionOptions } from "src/utils/erisAllowedMentionsToDjsMentionOptions";
|
||||
|
||||
import { SavedMessage } from "../../../data/entities/SavedMessage";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { convertDelayStringToMS, resolveMember, tStrictMessageContent } from "../../../utils";
|
||||
import { messageIsEmpty } from "../../../utils/messageIsEmpty";
|
||||
import { validate } from "../../../validatorUtils";
|
||||
import { TagsPluginType } from "../types";
|
||||
import { matchAndRenderTagFromString } from "./matchAndRenderTagFromString";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { TagsPluginType } from "../types";
|
||||
|
||||
import { matchAndRenderTagFromString } from "./matchAndRenderTagFromString";
|
||||
|
||||
export async function onMessageCreate(pluginData: GuildPluginData<TagsPluginType>, msg: SavedMessage) {
|
||||
if (msg.is_bot) return;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue