mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-05-25 18:25:03 +00:00
feat: add tag aliases support
This commit is contained in:
parent
bc830cf301
commit
5846012a0c
10 changed files with 162 additions and 30 deletions
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;
|
||||
}
|
|
@ -22,6 +22,7 @@ import { onMessageCreate } from "./util/onMessageCreate";
|
|||
import { onMessageDelete } from "./util/onMessageDelete";
|
||||
import { renderTagBody } from "./util/renderTagBody";
|
||||
import { LogsPlugin } from "../Logs/LogsPlugin";
|
||||
import { GuildTagAliases } from "src/data/GuildTagAliases";
|
||||
|
||||
const defaultOptions: PluginOptions<TagsPluginType> = {
|
||||
config: {
|
||||
|
@ -110,6 +111,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,19 @@ 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) {
|
||||
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 +34,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!`);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@ export const TagListCmd = tagsCmd({
|
|||
|
||||
async run({ message: msg, pluginData }) {
|
||||
const tags = await pluginData.state.tags.all();
|
||||
const aliases = await pluginData.state.tagAliases.all();
|
||||
if (tags.length === 0) {
|
||||
msg.channel.send(`No tags created yet! Use \`tag create\` command to create one.`);
|
||||
return;
|
||||
|
@ -14,7 +15,11 @@ export const TagListCmd = tagsCmd({
|
|||
|
||||
const prefix = (await pluginData.config.getForMessage(msg)).prefix;
|
||||
const tagNames = tags.map((tag) => tag.tag).sort();
|
||||
const tagAliasesNames = aliases.map((alias) => alias.alias).sort();
|
||||
const tagAndAliasesNames = tagNames
|
||||
.join(", ")
|
||||
.concat(tagAliasesNames.length > 0 ? `, ${tagAliasesNames.join(", ")}` : "");
|
||||
|
||||
createChunkedMessage(msg.channel, `Available tags (use with ${prefix}tag): \`\`\`${tagNames.join(", ")}\`\`\``);
|
||||
createChunkedMessage(msg.channel, `Available tags (use with ${prefix}tag): \`\`\`${tagAndAliasesNames}\`\`\``);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -14,19 +14,23 @@ export const TagSourceCmd = 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)) || (await pluginData.state.tags.find(alias?.tag ?? null));
|
||||
|
||||
if (args.delete) {
|
||||
const actualTag = await pluginData.state.tags.find(args.tag);
|
||||
if (!actualTag) {
|
||||
const aliasedTag = await pluginData.state.tags.find(alias?.tag ?? null);
|
||||
|
||||
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!");
|
||||
actualTag ? pluginData.state.tags.delete(args.tag) : pluginData.state.tagAliases.delete(args.tag);
|
||||
sendSuccessMessage(pluginData, msg.channel, `${actualTag ? "Tag" : "Alias"} deleted!`);
|
||||
return;
|
||||
}
|
||||
|
||||
const tag = await pluginData.state.tags.find(args.tag);
|
||||
if (!tag) {
|
||||
sendErrorMessage(pluginData, msg.channel, "No tag with that name");
|
||||
return;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,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 +71,46 @@ 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(/^[a-z0-9_-]*/);
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const renderedDynamicTagContent = await renderTagFromString(
|
||||
pluginData,
|
||||
str,
|
||||
dynamicTagPrefix,
|
||||
dynamicTagName,
|
||||
dynamicTag.body,
|
||||
member,
|
||||
);
|
||||
const tagBody = aliasedTag?.body ?? dynamicTag?.body;
|
||||
|
||||
if (renderedDynamicTagContent == null) {
|
||||
if (!tagBody) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const renderedTagContent = await renderTagFromString(pluginData, str, tagPrefix, tagName, tagBody, member);
|
||||
|
||||
if (renderedTagContent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
renderedContent: renderedDynamicTagContent,
|
||||
tagName: dynamicTagName,
|
||||
renderedContent: renderedTagContent,
|
||||
tagName,
|
||||
categoryName: null,
|
||||
category: null,
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue