123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- import { isAuthorisedAsync } from "../util";
- import { ICommand } from "./command";
- import { Message } from "discord.js";
- import { getRepository } from "typeorm";
- import { Guide, GuideType, GuideKeyword } from "@db/entity/Guide";
- const makePattern = /^make (\w+)\s+name:(.+)\s*keywords:(.+)\s*contents:((.*[\n\r]*)+)$/i;
- const deletePattern = /^delete (\w+)\s+(.+)$/i;
- interface IGuide {
- name: string,
- displayName: string,
- content: string
- };
- async function matchGuide(keywords: string[]) {
- let a = await getRepository(Guide).query(
- `select guide.*
- from guide
- inner join (select gk."guideId", count("guideKeywordId") as gc
- from guide_keywords_guide_keyword as gk
- where
- gk."guideKeywordId" in (select id
- from guide_keyword
- where
- guide_keyword.keyword in (${keywords.map((v, i) => `$${i + 1}`).join(",")}))
- group by gk."guideId") as gks
- on gks."guideId" = guide.id
- order by gc desc
- limit 1`,
- keywords
- ) as Guide[];
- if(a.length == 0)
- return null;
- return a[0];
- }
- async function listGuides(msg: Message, guideType: string, message: string) {
- let repo = getRepository(Guide);
- let allGuides = await repo.createQueryBuilder("guide")
- .select(["guide.displayName"])
- .leftJoinAndSelect("guide.keywords", "keyword")
- .where("guide.type = :type", { type: guideType })
- .getMany();
- const MAX_GUIDES_PER_MSG = 30;
- let guides = `${msg.author.toString()} ${message}\n\`\`\``;
- let guideNum = 0;
- for(let guide of allGuides) {
- guides += `${guide.displayName} -- ${guide.keywords.map(k => k.keyword).join(" ")}\n`;
- guideNum++;
- if(guideNum == MAX_GUIDES_PER_MSG) {
- guides += "```";
- await msg.channel.send(guides);
- guides = "```\n";
- guideNum = 0;
- }
- }
- if(guideNum != 0) {
- guides += "```\n\nTo display the guides, ping me with one or more keywords, like `@NoctBot sybaris com`";
- await msg.channel.send(guides);
- }
- }
- export default {
- onDirectMention: async (actionsDone, msg, content) => {
- if (actionsDone)
- return false;
-
- if(msg.attachments.size > 0 || content.length == 0)
- return false;
-
- let parts = content.split(" ").map(s => s.trim()).filter(s => s.length != 0);
- let guide = await matchGuide(parts);
-
- if (guide) {
- msg.channel.send(guide.content);
- return true;
- }
- return false;
- },
- commands: [
- {
- pattern: makePattern,
- action: async (msg, s, match) => {
- if (!await isAuthorisedAsync(msg.member)) return;
- let type = match[1].toLowerCase();
- let name = match[2].trim();
- let keywords = match[3].toLowerCase().split(" ").map(s => s.trim()).filter(s => s.length != 0);
- let contents = match[4].trim();
-
- if(contents.length == 0){
- msg.channel.send(
- `${msg.author.toString()} The guide must have some content!`
- );
- return;
- }
-
- if(!Object.values(GuideType).includes(type)){
- msg.channel.send(
- `${msg.author.toString()} The type ${type} is not a valid guide type!`
- );
- return;
- }
-
- let repo = getRepository(GuideKeyword);
- let guideRepo = getRepository(Guide);
-
- let existingKeywords = await repo.find({
- where: [
- ...keywords.map(k => ({ keyword: k }))
- ]
- });
-
- let existingGuide = await matchGuide(keywords);
- let addGuide = async () => {
- let newKeywords = new Set<string>();
- let knownKeywords = new Set(existingKeywords.map(e => e.keyword));
- for(let word of keywords) {
- if(!knownKeywords.has(word))
- newKeywords.add(word);
- }
- let addedKeywords = await repo.save([...newKeywords].map(k => repo.create({
- keyword: k
- })));
- await guideRepo.save(guideRepo.create({
- content: contents,
- displayName: name,
- keywords: [...existingKeywords, ...addedKeywords],
- type: type as GuideType
- }));
- };
- if(existingGuide) {
- let guideKeywordsCount = await repo
- .createQueryBuilder("keywords")
- .leftJoinAndSelect("keywords.relatedGuides", "guide")
- .where("guide.id = :id", {id: existingGuide.id })
- .getCount();
- if(guideKeywordsCount == existingKeywords.length)
- await guideRepo.update({id: existingGuide.id}, {
- displayName: name,
- content: contents
- });
- else
- await addGuide();
- } else
- await addGuide();
-
- msg.channel.send(
- `${msg.author.toString()} Added/updated "${name}" (keywords \`${keywords.join(" ")}\`)!`
- );
- }
- },
- {
- pattern: deletePattern,
- action: async (msg, s, match) => {
- if (!await isAuthorisedAsync(msg.member)) return;
- let type = match[1];
- let keywords = match[2].toLowerCase().split(" ").map(s => s.trim()).filter(s => s.length != 0);
-
- if(!Object.values(GuideType).includes(type)){
- await msg.channel.send(
- `${msg.author.toString()} The type ${type} is not a valid guide type!`
- );
- return;
- }
- let dedupedKeywords = [...new Set(keywords)];
-
- let repo = getRepository(GuideKeyword);
- let guideRepo = getRepository(Guide);
- let existingGuide = await matchGuide(keywords);
- if(existingGuide) {
- let guideKeywordsCount = await repo
- .createQueryBuilder("keywords")
- .leftJoinAndSelect("keywords.relatedGuides", "guide")
- .where("guide.id = :id", {id: existingGuide.id })
- .getCount();
- if(guideKeywordsCount == dedupedKeywords.length) {
- await guideRepo.delete({ id: existingGuide.id });
- await msg.channel.send(`${msg.author.toString()} Removed ${type} "${keywords.join(" ")}"!`);
- return;
- }
- }
-
- await msg.channel.send(`${msg.author.toString()} No such ${type} with keywords \`${keywords.join(" ")}\`! Did you forget to specify all keywords?`);
- }
- },
- { pattern: "guides", action: async msg => await listGuides(msg, "guide", "Here are the guides I have:") },
- { pattern: "memes", action: async msg => await listGuides(msg, "meme", "Here are some random memes I have:") },
- { pattern: "misc", action: async msg => await listGuides(msg, "misc", "These are some misc stuff I can also do:") },
- ],
- documentation: {
- "make <guidetype> <NEWLINE>name: <name> <NEWLINE>keywords: <keywords> <NEWLINE>contents: <content>": {
- auth: true,
- description: "Creates a new guide of the specified type, the specified keywords and content."
- },
- "delete <guidetype> <keywords>": {
- auth: true,
- description: "Deletes a guide of the specified type."
- },
- "guides": {
- auth: false,
- description: "Lists all guides and keywords that trigger them."
- },
- "memes": {
- auth: false,
- description: "Lists all memes and keywords that trigger them."
- },
- "miscs": {
- auth: false,
- description: "Lists all additional keywords the bot reacts to."
- }
- }
- } as ICommand;
|