|  | @@ -1,358 +1,358 @@
 | 
	
		
			
				|  |  | -import TurndownService, { Options } from "turndown";
 | 
	
		
			
				|  |  | -import RSSParser from "rss-parser";
 | 
	
		
			
				|  |  | -import interval from "interval-promise";
 | 
	
		
			
				|  |  | -import { client, forumClient } from "../client";
 | 
	
		
			
				|  |  | -import sha1 from "sha1";
 | 
	
		
			
				|  |  | -import { ICommand } from "./command";
 | 
	
		
			
				|  |  | -import { TextChannel, Message, ReactionCollector, MessageReaction, Collector, User, Channel } from "discord.js";
 | 
	
		
			
				|  |  | -import { Dict } from "../util";
 | 
	
		
			
				|  |  | -import { getRepository, Not, IsNull } from "typeorm";
 | 
	
		
			
				|  |  | -import { PostedForumNewsItem } from "../entity/PostedForumsNewsItem";
 | 
	
		
			
				|  |  | -import { KnownChannel } from "../entity/KnownChannel";
 | 
	
		
			
				|  |  | -import { PostVerifyMessage } from "../entity/PostVerifyMessage";
 | 
	
		
			
				|  |  | -import bbobHTML from '@bbob/html'
 | 
	
		
			
				|  |  | -import presetHTML5 from '@bbob/preset-html5'
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -const PREVIEW_CHAR_LIMIT = 300;
 | 
	
		
			
				|  |  | -const NEWS_POST_VERIFY_CHANNEL = "newsPostVerify";
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -let verifyChannelId: string = null;
 | 
	
		
			
				|  |  | -const reactionCollectors: Dict<ReactionCollector> = {};
 | 
	
		
			
				|  |  | -const verifyMessageIdToPost: Dict<PostedForumNewsItem> = {};
 | 
	
		
			
				|  |  | -const NEWS_FEED_CHANNEL = "newsFeed";
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -const turndown = new TurndownService();
 | 
	
		
			
				|  |  | -turndown.addRule("image", {
 | 
	
		
			
				|  |  | -    filter: "img",
 | 
	
		
			
				|  |  | -    replacement: () => ""
 | 
	
		
			
				|  |  | -});
 | 
	
		
			
				|  |  | -turndown.addRule("link", {
 | 
	
		
			
				|  |  | -    filter: (node: HTMLElement, opts: Options) => node.nodeName === "A" && node.getAttribute("href") != null,
 | 
	
		
			
				|  |  | -    replacement: (content: string, node: HTMLElement) => node.getAttribute("href")
 | 
	
		
			
				|  |  | -});
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -const parser = new RSSParser();
 | 
	
		
			
				|  |  | -const RSS_UPDATE_INTERVAL_MIN = 5;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -function getThreadId(url: string) {
 | 
	
		
			
				|  |  | -    let result = url.substring(url.lastIndexOf(".") + 1);
 | 
	
		
			
				|  |  | -    if (result.endsWith("/"))
 | 
	
		
			
				|  |  | -        result = result.substring(0, result.length - 1);
 | 
	
		
			
				|  |  | -    return result;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -const NEWS_FORUM_ID = 49;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -const FEEDS = [
 | 
	
		
			
				|  |  | -    {
 | 
	
		
			
				|  |  | -        url: "http://custommaid3d2.com/index.php?forums/news.49/index.rss",
 | 
	
		
			
				|  |  | -        contentElement: "content:encoded"
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -function bbCodeToMarkdown(bbCode: string) {
 | 
	
		
			
				|  |  | -    return turndown.turndown(bbobHTML(bbCode, presetHTML5())).replace(/( {2}\n|\n\n){2,}/gm, "\n");
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -async function checkFeeds() {
 | 
	
		
			
				|  |  | -    console.log(`Checking feeds on ${new Date().toISOString()}`);
 | 
	
		
			
				|  |  | -    let forumsNewsRepo = getRepository(PostedForumNewsItem);
 | 
	
		
			
				|  |  | -    let postVerifyMessageRepo = getRepository(PostVerifyMessage);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    let forumThreads = await forumClient.getForumThreads(NEWS_FORUM_ID);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    for (let thread of forumThreads.threads) {
 | 
	
		
			
				|  |  | -        let firstPost = await forumClient.getPost(thread.first_post_id);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let contents = bbCodeToMarkdown(firstPost.message);
 | 
	
		
			
				|  |  | -        let itemObj = forumsNewsRepo.create({
 | 
	
		
			
				|  |  | -            id: thread.thread_id.toString(),
 | 
	
		
			
				|  |  | -            hash: sha1(firstPost.message),
 | 
	
		
			
				|  |  | -            verifyMessage: postVerifyMessageRepo.create({
 | 
	
		
			
				|  |  | -                author: thread.username,
 | 
	
		
			
				|  |  | -                link: `https://custommaid3d2.com/index.php?threads/${thread.thread_id}/`,
 | 
	
		
			
				|  |  | -                title: thread.title,
 | 
	
		
			
				|  |  | -                text: `${contents.substr(0, Math.min(contents.length, PREVIEW_CHAR_LIMIT))}...`,
 | 
	
		
			
				|  |  | -                isNew: true
 | 
	
		
			
				|  |  | -            })
 | 
	
		
			
				|  |  | -        });
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let postItem = await forumsNewsRepo.findOne({
 | 
	
		
			
				|  |  | -            where: { id: itemObj.id },
 | 
	
		
			
				|  |  | -            relations: ["verifyMessage"]
 | 
	
		
			
				|  |  | -        });
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if (postItem) {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if(process.env.INGORE_CHANGED_NEWS === "TRUE") {
 | 
	
		
			
				|  |  | -                await forumsNewsRepo.update({
 | 
	
		
			
				|  |  | -                    id: postItem.id
 | 
	
		
			
				|  |  | -                }, {
 | 
	
		
			
				|  |  | -                    hash: itemObj.hash
 | 
	
		
			
				|  |  | -                });
 | 
	
		
			
				|  |  | -                continue;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Add message ID to mark for edit
 | 
	
		
			
				|  |  | -            if (postItem.hash != itemObj.hash) {
 | 
	
		
			
				|  |  | -                let newHash = itemObj.hash;
 | 
	
		
			
				|  |  | -                if (!postItem.verifyMessage)
 | 
	
		
			
				|  |  | -                    postItem.verifyMessage = itemObj.verifyMessage;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                itemObj = postItem;
 | 
	
		
			
				|  |  | -                itemObj.verifyMessage.isNew = false;
 | 
	
		
			
				|  |  | -                itemObj.hash = newHash;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            else
 | 
	
		
			
				|  |  | -                continue;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if (!shouldVerify())
 | 
	
		
			
				|  |  | -            await sendNews(itemObj);
 | 
	
		
			
				|  |  | -        else
 | 
	
		
			
				|  |  | -            await addVerifyMessage(itemObj);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // for(let feedEntry of FEEDS) {
 | 
	
		
			
				|  |  | -    //     let feed = await parser.parseURL(feedEntry.url);
 | 
	
		
			
				|  |  | -    //     if(feed.items.length == 0)
 | 
	
		
			
				|  |  | -    //         continue;
 | 
	
		
			
				|  |  | -    //     let printableItems = feed.items.sort((a : any, b: any) => a.isoDate.localeCompare(b.isoDate));
 | 
	
		
			
				|  |  | -    //     if(printableItems.length > 0) {
 | 
	
		
			
				|  |  | -    //         for(let item of printableItems) {
 | 
	
		
			
				|  |  | -    //             let itemID = getThreadId(item.guid);
 | 
	
		
			
				|  |  | -    //             let contents = null;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    //             try {
 | 
	
		
			
				|  |  | -    //                 let res = await request(item.link, {resolveWithFullResponse: true}) as Response;
 | 
	
		
			
				|  |  | -    //                 if(res.statusCode != 200) {
 | 
	
		
			
				|  |  | -    //                     console.log(`Post ${itemID} could not be loaded because request returned status ${res.statusCode}`);
 | 
	
		
			
				|  |  | -    //                     continue;
 | 
	
		
			
				|  |  | -    //                 }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    //                 let rootNode = html.parse(res.body, {
 | 
	
		
			
				|  |  | -    //                     pre: true,
 | 
	
		
			
				|  |  | -    //                     script: false,
 | 
	
		
			
				|  |  | -    //                     style: false
 | 
	
		
			
				|  |  | -    //                 });
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    //                 if(!(rootNode instanceof html.HTMLElement))
 | 
	
		
			
				|  |  | -    //                     continue;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    //                 let opDiv = rootNode.querySelector("div.bbWrapper");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    //                 if (!opDiv) {
 | 
	
		
			
				|  |  | -    //                     console.log(`No posts found for ${itemID}!`);
 | 
	
		
			
				|  |  | -    //                     continue;
 | 
	
		
			
				|  |  | -    //                 }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    //                 contents = markdownify(opDiv.outerHTML, item.link);
 | 
	
		
			
				|  |  | -    //             } catch(err){
 | 
	
		
			
				|  |  | -    //                 console.log(`Failed to get html for item ${itemID} because ${err}`);
 | 
	
		
			
				|  |  | -    //                 continue;
 | 
	
		
			
				|  |  | -    //             }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    //         }
 | 
	
		
			
				|  |  | -    //     }
 | 
	
		
			
				|  |  | -    // }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -async function initPendingReactors() {
 | 
	
		
			
				|  |  | -    let verifyChannel = client.channels.get(verifyChannelId);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    let repo = getRepository(PostedForumNewsItem);
 | 
	
		
			
				|  |  | -    let verifyMessageRepo = getRepository(PostVerifyMessage);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    let pendingVerifyMessages = await repo.find({
 | 
	
		
			
				|  |  | -        where: { verifyMessage: Not(IsNull()) },
 | 
	
		
			
				|  |  | -        select: ["id"],
 | 
	
		
			
				|  |  | -        relations: ["verifyMessage"]
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    for (let msg of pendingVerifyMessages) {
 | 
	
		
			
				|  |  | -        let m = await tryFetchMessage(verifyChannel, msg.verifyMessage.messageId);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if (!m) {
 | 
	
		
			
				|  |  | -            await verifyMessageRepo.delete(msg.verifyMessage);
 | 
	
		
			
				|  |  | -            await repo.update({ id: m.id }, { verifyMessage: null })
 | 
	
		
			
				|  |  | -            continue;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let collector = m.createReactionCollector(isVerifyReaction, { maxEmojis: 1 });
 | 
	
		
			
				|  |  | -        collector.on("collect", collectReaction);
 | 
	
		
			
				|  |  | -        reactionCollectors[m.id] = collector;
 | 
	
		
			
				|  |  | -        verifyMessageIdToPost[m.id] = msg;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -async function addVerifyMessage(item: PostedForumNewsItem) {
 | 
	
		
			
				|  |  | -    let verifyChannel = client.channels.get(verifyChannelId) as TextChannel;
 | 
	
		
			
				|  |  | -    let verifyMessageRepo = getRepository(PostVerifyMessage);
 | 
	
		
			
				|  |  | -    let forumsNewsRepo = getRepository(PostedForumNewsItem);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if (item.verifyMessage.messageId) {
 | 
	
		
			
				|  |  | -        let oldMessage = await tryFetchMessage(verifyChannel, item.verifyMessage.messageId);
 | 
	
		
			
				|  |  | -        if (oldMessage)
 | 
	
		
			
				|  |  | -            await oldMessage.delete();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    let newMessage = await verifyChannel.send(toVerifyString(item.id, item.verifyMessage)) as Message;
 | 
	
		
			
				|  |  | -    item.verifyMessage.messageId = newMessage.id;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    await newMessage.react("✅");
 | 
	
		
			
				|  |  | -    await newMessage.react("❌");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    let collector = newMessage.createReactionCollector(isVerifyReaction, { maxEmojis: 1 });
 | 
	
		
			
				|  |  | -    collector.on("collect", collectReaction)
 | 
	
		
			
				|  |  | -    reactionCollectors[newMessage.id] = collector;
 | 
	
		
			
				|  |  | -    verifyMessageIdToPost[newMessage.id] = item;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    item.verifyMessage = await verifyMessageRepo.save(item.verifyMessage);
 | 
	
		
			
				|  |  | -    await forumsNewsRepo.save(item);
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -async function collectReaction(reaction: MessageReaction, collector: Collector<string, MessageReaction>) {
 | 
	
		
			
				|  |  | -    let verifyMessageRepo = getRepository(PostVerifyMessage);
 | 
	
		
			
				|  |  | -    let postRepo = getRepository(PostedForumNewsItem);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    let m = reaction.message;
 | 
	
		
			
				|  |  | -    collector.stop();
 | 
	
		
			
				|  |  | -    delete reactionCollectors[m.id];
 | 
	
		
			
				|  |  | -    let post = verifyMessageIdToPost[m.id];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    await postRepo.update({ id: post.id }, { verifyMessage: null });
 | 
	
		
			
				|  |  | -    await verifyMessageRepo.delete({ id: post.verifyMessage.id });
 | 
	
		
			
				|  |  | -    await reaction.message.delete();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if (reaction.emoji.name == "✅")
 | 
	
		
			
				|  |  | -        sendNews(post);
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -async function sendNews(item: PostedForumNewsItem) {
 | 
	
		
			
				|  |  | -    let channelRepo = getRepository(KnownChannel);
 | 
	
		
			
				|  |  | -    let newsPostRepo = getRepository(PostedForumNewsItem);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    let outChannel = await channelRepo.findOne({
 | 
	
		
			
				|  |  | -        where: { channelType: NEWS_FEED_CHANNEL }
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    let sentMessage = await postNewsItem(outChannel.channelId, item);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    item.postedMessageId = sentMessage.id;
 | 
	
		
			
				|  |  | -    item.verifyMessage = null;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    await newsPostRepo.save(item);
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -function isVerifyReaction(reaction: MessageReaction, user: User) {
 | 
	
		
			
				|  |  | -    return (reaction.emoji.name == "✅" || reaction.emoji.name == "❌") && !user.bot;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -async function tryFetchMessage(channel: Channel, messageId: string) {
 | 
	
		
			
				|  |  | -    try {
 | 
	
		
			
				|  |  | -        if (!(channel instanceof TextChannel))
 | 
	
		
			
				|  |  | -            return null;
 | 
	
		
			
				|  |  | -        return await channel.fetchMessage(messageId);
 | 
	
		
			
				|  |  | -    } catch (error) {
 | 
	
		
			
				|  |  | -        return null;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -function shouldVerify() {
 | 
	
		
			
				|  |  | -    return verifyChannelId != null;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -async function postNewsItem(channel: string, item: PostedForumNewsItem): Promise<Message | null> {
 | 
	
		
			
				|  |  | -    let newsMessage = toNewsString(item.verifyMessage);
 | 
	
		
			
				|  |  | -    let ch = client.channels.get(channel);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if (!(ch instanceof TextChannel))
 | 
	
		
			
				|  |  | -        return null;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if (item.postedMessageId) {
 | 
	
		
			
				|  |  | -        let message = await tryFetchMessage(ch, item.postedMessageId);
 | 
	
		
			
				|  |  | -        if (message)
 | 
	
		
			
				|  |  | -            return await message.edit(newsMessage);
 | 
	
		
			
				|  |  | -        else
 | 
	
		
			
				|  |  | -            return await ch.send(newsMessage) as Message;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    else
 | 
	
		
			
				|  |  | -        return await ch.send(newsMessage) as Message;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -function markdownify(htmStr: string, link: string) {
 | 
	
		
			
				|  |  | -    return turndown.turndown(htmStr).replace(/( {2}\n|\n\n){2,}/gm, "\n").replace(link, "");
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -function toNewsString(item: PostVerifyMessage) {
 | 
	
		
			
				|  |  | -    return `**${item.title}**
 | 
	
		
			
				|  |  | -Posted by ${item.author}
 | 
	
		
			
				|  |  | -${item.link}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -${item.text}`;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -function toVerifyString(postId: string, item: PostVerifyMessage) {
 | 
	
		
			
				|  |  | -    return `[${item.isNew ? "🆕 ADD" : "✏️ EDIT"}]
 | 
	
		
			
				|  |  | -Post ID: **${postId}**
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | -${toNewsString(item)}
 | 
	
		
			
				|  |  | -            
 | 
	
		
			
				|  |  | -React with ✅ (approve) or ❌ (deny).`;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -export default {
 | 
	
		
			
				|  |  | -    commands: [
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            pattern: /^edit (\d+)\s+((.*[\n\r]*)+)$/i,
 | 
	
		
			
				|  |  | -            action: async (msg, s, match) => {
 | 
	
		
			
				|  |  | -                if (msg.channel.id != verifyChannelId)
 | 
	
		
			
				|  |  | -                    return;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                let id = match[1];
 | 
	
		
			
				|  |  | -                let newContents = match[2].trim();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                let repo = getRepository(PostedForumNewsItem);
 | 
	
		
			
				|  |  | -                let verifyRepo = getRepository(PostVerifyMessage);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                let post = await repo.findOne({
 | 
	
		
			
				|  |  | -                    where: { id: id },
 | 
	
		
			
				|  |  | -                    relations: ["verifyMessage"]
 | 
	
		
			
				|  |  | -                });
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                if (!post || !post.verifyMessage) {
 | 
	
		
			
				|  |  | -                    msg.channel.send(`${msg.author.toString()} No unapproved news items with id ${id}!`);
 | 
	
		
			
				|  |  | -                    return;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                let editMsg = await tryFetchMessage(client.channels.get(verifyChannelId), post.verifyMessage.messageId);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                if (!editMsg) {
 | 
	
		
			
				|  |  | -                    msg.channel.send(`${msg.author.toString()} No verify message found for ${id}! This is a bug: report to horse.`);
 | 
	
		
			
				|  |  | -                    return;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                post.verifyMessage.text = newContents;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                await verifyRepo.save(post.verifyMessage);
 | 
	
		
			
				|  |  | -                await editMsg.edit(toVerifyString(post.id, post.verifyMessage));
 | 
	
		
			
				|  |  | -                await msg.delete();
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -    ],
 | 
	
		
			
				|  |  | -    onStart: async () => {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let repo = getRepository(KnownChannel);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let verifyChannel = await repo.findOne({
 | 
	
		
			
				|  |  | -            channelType: NEWS_POST_VERIFY_CHANNEL
 | 
	
		
			
				|  |  | -        });
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if (verifyChannel)
 | 
	
		
			
				|  |  | -            verifyChannelId = verifyChannel.channelId;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        await initPendingReactors();
 | 
	
		
			
				|  |  | -        interval(checkFeeds, RSS_UPDATE_INTERVAL_MIN * 60 * 1000);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +import TurndownService, { Options } from "turndown";
 | 
	
		
			
				|  |  | +import RSSParser from "rss-parser";
 | 
	
		
			
				|  |  | +import interval from "interval-promise";
 | 
	
		
			
				|  |  | +import { client, forumClient } from "../client";
 | 
	
		
			
				|  |  | +import sha1 from "sha1";
 | 
	
		
			
				|  |  | +import { ICommand } from "./command";
 | 
	
		
			
				|  |  | +import { TextChannel, Message, ReactionCollector, MessageReaction, Collector, User, Channel } from "discord.js";
 | 
	
		
			
				|  |  | +import { Dict } from "../util";
 | 
	
		
			
				|  |  | +import { getRepository, Not, IsNull } from "typeorm";
 | 
	
		
			
				|  |  | +import { PostedForumNewsItem } from "../entity/PostedForumsNewsItem";
 | 
	
		
			
				|  |  | +import { KnownChannel } from "../entity/KnownChannel";
 | 
	
		
			
				|  |  | +import { PostVerifyMessage } from "../entity/PostVerifyMessage";
 | 
	
		
			
				|  |  | +import bbobHTML from '@bbob/html'
 | 
	
		
			
				|  |  | +import presetHTML5 from '@bbob/preset-html5'
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const PREVIEW_CHAR_LIMIT = 300;
 | 
	
		
			
				|  |  | +const NEWS_POST_VERIFY_CHANNEL = "newsPostVerify";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +let verifyChannelId: string = null;
 | 
	
		
			
				|  |  | +const reactionCollectors: Dict<ReactionCollector> = {};
 | 
	
		
			
				|  |  | +const verifyMessageIdToPost: Dict<PostedForumNewsItem> = {};
 | 
	
		
			
				|  |  | +const NEWS_FEED_CHANNEL = "newsFeed";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const turndown = new TurndownService();
 | 
	
		
			
				|  |  | +turndown.addRule("image", {
 | 
	
		
			
				|  |  | +    filter: "img",
 | 
	
		
			
				|  |  | +    replacement: () => ""
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +turndown.addRule("link", {
 | 
	
		
			
				|  |  | +    filter: (node: HTMLElement, opts: Options) => node.nodeName === "A" && node.getAttribute("href") != null,
 | 
	
		
			
				|  |  | +    replacement: (content: string, node: HTMLElement) => node.getAttribute("href")
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const parser = new RSSParser();
 | 
	
		
			
				|  |  | +const RSS_UPDATE_INTERVAL_MIN = 5;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function getThreadId(url: string) {
 | 
	
		
			
				|  |  | +    let result = url.substring(url.lastIndexOf(".") + 1);
 | 
	
		
			
				|  |  | +    if (result.endsWith("/"))
 | 
	
		
			
				|  |  | +        result = result.substring(0, result.length - 1);
 | 
	
		
			
				|  |  | +    return result;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const NEWS_FORUM_ID = 49;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const FEEDS = [
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        url: "http://custommaid3d2.com/index.php?forums/news.49/index.rss",
 | 
	
		
			
				|  |  | +        contentElement: "content:encoded"
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function bbCodeToMarkdown(bbCode: string) {
 | 
	
		
			
				|  |  | +    return turndown.turndown(bbobHTML(bbCode, presetHTML5())).replace(/( {2}\n|\n\n){2,}/gm, "\n");
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function checkFeeds() {
 | 
	
		
			
				|  |  | +    console.log(`Checking feeds on ${new Date().toISOString()}`);
 | 
	
		
			
				|  |  | +    let forumsNewsRepo = getRepository(PostedForumNewsItem);
 | 
	
		
			
				|  |  | +    let postVerifyMessageRepo = getRepository(PostVerifyMessage);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let forumThreads = await forumClient.getForumThreads(NEWS_FORUM_ID);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (let thread of forumThreads.threads) {
 | 
	
		
			
				|  |  | +        let firstPost = await forumClient.getPost(thread.first_post_id);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        let contents = bbCodeToMarkdown(firstPost.message);
 | 
	
		
			
				|  |  | +        let itemObj = forumsNewsRepo.create({
 | 
	
		
			
				|  |  | +            id: thread.thread_id.toString(),
 | 
	
		
			
				|  |  | +            hash: sha1(firstPost.message),
 | 
	
		
			
				|  |  | +            verifyMessage: postVerifyMessageRepo.create({
 | 
	
		
			
				|  |  | +                author: thread.username,
 | 
	
		
			
				|  |  | +                link: `https://custommaid3d2.com/index.php?threads/${thread.thread_id}/`,
 | 
	
		
			
				|  |  | +                title: thread.title,
 | 
	
		
			
				|  |  | +                text: `${contents.substr(0, Math.min(contents.length, PREVIEW_CHAR_LIMIT))}...`,
 | 
	
		
			
				|  |  | +                isNew: true
 | 
	
		
			
				|  |  | +            })
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        let postItem = await forumsNewsRepo.findOne({
 | 
	
		
			
				|  |  | +            where: { id: itemObj.id },
 | 
	
		
			
				|  |  | +            relations: ["verifyMessage"]
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (postItem) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if(process.env.INGORE_CHANGED_NEWS === "TRUE") {
 | 
	
		
			
				|  |  | +                await forumsNewsRepo.update({
 | 
	
		
			
				|  |  | +                    id: postItem.id
 | 
	
		
			
				|  |  | +                }, {
 | 
	
		
			
				|  |  | +                    hash: itemObj.hash
 | 
	
		
			
				|  |  | +                });
 | 
	
		
			
				|  |  | +                continue;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // Add message ID to mark for edit
 | 
	
		
			
				|  |  | +            if (postItem.hash != itemObj.hash) {
 | 
	
		
			
				|  |  | +                let newHash = itemObj.hash;
 | 
	
		
			
				|  |  | +                if (!postItem.verifyMessage)
 | 
	
		
			
				|  |  | +                    postItem.verifyMessage = itemObj.verifyMessage;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                itemObj = postItem;
 | 
	
		
			
				|  |  | +                itemObj.verifyMessage.isNew = false;
 | 
	
		
			
				|  |  | +                itemObj.hash = newHash;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else
 | 
	
		
			
				|  |  | +                continue;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (!shouldVerify())
 | 
	
		
			
				|  |  | +            await sendNews(itemObj);
 | 
	
		
			
				|  |  | +        else
 | 
	
		
			
				|  |  | +            await addVerifyMessage(itemObj);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // for(let feedEntry of FEEDS) {
 | 
	
		
			
				|  |  | +    //     let feed = await parser.parseURL(feedEntry.url);
 | 
	
		
			
				|  |  | +    //     if(feed.items.length == 0)
 | 
	
		
			
				|  |  | +    //         continue;
 | 
	
		
			
				|  |  | +    //     let printableItems = feed.items.sort((a : any, b: any) => a.isoDate.localeCompare(b.isoDate));
 | 
	
		
			
				|  |  | +    //     if(printableItems.length > 0) {
 | 
	
		
			
				|  |  | +    //         for(let item of printableItems) {
 | 
	
		
			
				|  |  | +    //             let itemID = getThreadId(item.guid);
 | 
	
		
			
				|  |  | +    //             let contents = null;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    //             try {
 | 
	
		
			
				|  |  | +    //                 let res = await request(item.link, {resolveWithFullResponse: true}) as Response;
 | 
	
		
			
				|  |  | +    //                 if(res.statusCode != 200) {
 | 
	
		
			
				|  |  | +    //                     console.log(`Post ${itemID} could not be loaded because request returned status ${res.statusCode}`);
 | 
	
		
			
				|  |  | +    //                     continue;
 | 
	
		
			
				|  |  | +    //                 }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    //                 let rootNode = html.parse(res.body, {
 | 
	
		
			
				|  |  | +    //                     pre: true,
 | 
	
		
			
				|  |  | +    //                     script: false,
 | 
	
		
			
				|  |  | +    //                     style: false
 | 
	
		
			
				|  |  | +    //                 });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    //                 if(!(rootNode instanceof html.HTMLElement))
 | 
	
		
			
				|  |  | +    //                     continue;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    //                 let opDiv = rootNode.querySelector("div.bbWrapper");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    //                 if (!opDiv) {
 | 
	
		
			
				|  |  | +    //                     console.log(`No posts found for ${itemID}!`);
 | 
	
		
			
				|  |  | +    //                     continue;
 | 
	
		
			
				|  |  | +    //                 }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    //                 contents = markdownify(opDiv.outerHTML, item.link);
 | 
	
		
			
				|  |  | +    //             } catch(err){
 | 
	
		
			
				|  |  | +    //                 console.log(`Failed to get html for item ${itemID} because ${err}`);
 | 
	
		
			
				|  |  | +    //                 continue;
 | 
	
		
			
				|  |  | +    //             }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    //         }
 | 
	
		
			
				|  |  | +    //     }
 | 
	
		
			
				|  |  | +    // }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function initPendingReactors() {
 | 
	
		
			
				|  |  | +    let verifyChannel = client.channels.get(verifyChannelId);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let repo = getRepository(PostedForumNewsItem);
 | 
	
		
			
				|  |  | +    let verifyMessageRepo = getRepository(PostVerifyMessage);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let pendingVerifyMessages = await repo.find({
 | 
	
		
			
				|  |  | +        where: { verifyMessage: Not(IsNull()) },
 | 
	
		
			
				|  |  | +        select: ["id"],
 | 
	
		
			
				|  |  | +        relations: ["verifyMessage"]
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (let msg of pendingVerifyMessages) {
 | 
	
		
			
				|  |  | +        let m = await tryFetchMessage(verifyChannel, msg.verifyMessage.messageId);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (!m) {
 | 
	
		
			
				|  |  | +            await repo.update({ id: msg.id }, { verifyMessage: null });
 | 
	
		
			
				|  |  | +            await verifyMessageRepo.delete(msg.verifyMessage);
 | 
	
		
			
				|  |  | +            continue;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        let collector = m.createReactionCollector(isVerifyReaction, { maxEmojis: 1 });
 | 
	
		
			
				|  |  | +        collector.on("collect", collectReaction);
 | 
	
		
			
				|  |  | +        reactionCollectors[m.id] = collector;
 | 
	
		
			
				|  |  | +        verifyMessageIdToPost[m.id] = msg;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function addVerifyMessage(item: PostedForumNewsItem) {
 | 
	
		
			
				|  |  | +    let verifyChannel = client.channels.get(verifyChannelId) as TextChannel;
 | 
	
		
			
				|  |  | +    let verifyMessageRepo = getRepository(PostVerifyMessage);
 | 
	
		
			
				|  |  | +    let forumsNewsRepo = getRepository(PostedForumNewsItem);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (item.verifyMessage.messageId) {
 | 
	
		
			
				|  |  | +        let oldMessage = await tryFetchMessage(verifyChannel, item.verifyMessage.messageId);
 | 
	
		
			
				|  |  | +        if (oldMessage)
 | 
	
		
			
				|  |  | +            await oldMessage.delete();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let newMessage = await verifyChannel.send(toVerifyString(item.id, item.verifyMessage)) as Message;
 | 
	
		
			
				|  |  | +    item.verifyMessage.messageId = newMessage.id;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    await newMessage.react("✅");
 | 
	
		
			
				|  |  | +    await newMessage.react("❌");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let collector = newMessage.createReactionCollector(isVerifyReaction, { maxEmojis: 1 });
 | 
	
		
			
				|  |  | +    collector.on("collect", collectReaction)
 | 
	
		
			
				|  |  | +    reactionCollectors[newMessage.id] = collector;
 | 
	
		
			
				|  |  | +    verifyMessageIdToPost[newMessage.id] = item;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    item.verifyMessage = await verifyMessageRepo.save(item.verifyMessage);
 | 
	
		
			
				|  |  | +    await forumsNewsRepo.save(item);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function collectReaction(reaction: MessageReaction, collector: Collector<string, MessageReaction>) {
 | 
	
		
			
				|  |  | +    let verifyMessageRepo = getRepository(PostVerifyMessage);
 | 
	
		
			
				|  |  | +    let postRepo = getRepository(PostedForumNewsItem);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let m = reaction.message;
 | 
	
		
			
				|  |  | +    collector.stop();
 | 
	
		
			
				|  |  | +    delete reactionCollectors[m.id];
 | 
	
		
			
				|  |  | +    let post = verifyMessageIdToPost[m.id];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    await postRepo.update({ id: post.id }, { verifyMessage: null });
 | 
	
		
			
				|  |  | +    await verifyMessageRepo.delete({ id: post.verifyMessage.id });
 | 
	
		
			
				|  |  | +    await reaction.message.delete();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (reaction.emoji.name == "✅")
 | 
	
		
			
				|  |  | +        sendNews(post);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function sendNews(item: PostedForumNewsItem) {
 | 
	
		
			
				|  |  | +    let channelRepo = getRepository(KnownChannel);
 | 
	
		
			
				|  |  | +    let newsPostRepo = getRepository(PostedForumNewsItem);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let outChannel = await channelRepo.findOne({
 | 
	
		
			
				|  |  | +        where: { channelType: NEWS_FEED_CHANNEL }
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let sentMessage = await postNewsItem(outChannel.channelId, item);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    item.postedMessageId = sentMessage.id;
 | 
	
		
			
				|  |  | +    item.verifyMessage = null;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    await newsPostRepo.save(item);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function isVerifyReaction(reaction: MessageReaction, user: User) {
 | 
	
		
			
				|  |  | +    return (reaction.emoji.name == "✅" || reaction.emoji.name == "❌") && !user.bot;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function tryFetchMessage(channel: Channel, messageId: string) {
 | 
	
		
			
				|  |  | +    try {
 | 
	
		
			
				|  |  | +        if (!(channel instanceof TextChannel))
 | 
	
		
			
				|  |  | +            return null;
 | 
	
		
			
				|  |  | +        return await channel.fetchMessage(messageId);
 | 
	
		
			
				|  |  | +    } catch (error) {
 | 
	
		
			
				|  |  | +        return null;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function shouldVerify() {
 | 
	
		
			
				|  |  | +    return verifyChannelId != null;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function postNewsItem(channel: string, item: PostedForumNewsItem): Promise<Message | null> {
 | 
	
		
			
				|  |  | +    let newsMessage = toNewsString(item.verifyMessage);
 | 
	
		
			
				|  |  | +    let ch = client.channels.get(channel);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (!(ch instanceof TextChannel))
 | 
	
		
			
				|  |  | +        return null;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (item.postedMessageId) {
 | 
	
		
			
				|  |  | +        let message = await tryFetchMessage(ch, item.postedMessageId);
 | 
	
		
			
				|  |  | +        if (message)
 | 
	
		
			
				|  |  | +            return await message.edit(newsMessage);
 | 
	
		
			
				|  |  | +        else
 | 
	
		
			
				|  |  | +            return await ch.send(newsMessage) as Message;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    else
 | 
	
		
			
				|  |  | +        return await ch.send(newsMessage) as Message;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function markdownify(htmStr: string, link: string) {
 | 
	
		
			
				|  |  | +    return turndown.turndown(htmStr).replace(/( {2}\n|\n\n){2,}/gm, "\n").replace(link, "");
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function toNewsString(item: PostVerifyMessage) {
 | 
	
		
			
				|  |  | +    return `**${item.title}**
 | 
	
		
			
				|  |  | +Posted by ${item.author}
 | 
	
		
			
				|  |  | +${item.link}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +${item.text}`;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function toVerifyString(postId: string, item: PostVerifyMessage) {
 | 
	
		
			
				|  |  | +    return `[${item.isNew ? "🆕 ADD" : "✏️ EDIT"}]
 | 
	
		
			
				|  |  | +Post ID: **${postId}**
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +${toNewsString(item)}
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +React with ✅ (approve) or ❌ (deny).`;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +export default {
 | 
	
		
			
				|  |  | +    commands: [
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            pattern: /^edit (\d+)\s+((.*[\n\r]*)+)$/i,
 | 
	
		
			
				|  |  | +            action: async (msg, s, match) => {
 | 
	
		
			
				|  |  | +                if (msg.channel.id != verifyChannelId)
 | 
	
		
			
				|  |  | +                    return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                let id = match[1];
 | 
	
		
			
				|  |  | +                let newContents = match[2].trim();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                let repo = getRepository(PostedForumNewsItem);
 | 
	
		
			
				|  |  | +                let verifyRepo = getRepository(PostVerifyMessage);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                let post = await repo.findOne({
 | 
	
		
			
				|  |  | +                    where: { id: id },
 | 
	
		
			
				|  |  | +                    relations: ["verifyMessage"]
 | 
	
		
			
				|  |  | +                });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                if (!post || !post.verifyMessage) {
 | 
	
		
			
				|  |  | +                    msg.channel.send(`${msg.author.toString()} No unapproved news items with id ${id}!`);
 | 
	
		
			
				|  |  | +                    return;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                let editMsg = await tryFetchMessage(client.channels.get(verifyChannelId), post.verifyMessage.messageId);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                if (!editMsg) {
 | 
	
		
			
				|  |  | +                    msg.channel.send(`${msg.author.toString()} No verify message found for ${id}! This is a bug: report to horse.`);
 | 
	
		
			
				|  |  | +                    return;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                post.verifyMessage.text = newContents;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                await verifyRepo.save(post.verifyMessage);
 | 
	
		
			
				|  |  | +                await editMsg.edit(toVerifyString(post.id, post.verifyMessage));
 | 
	
		
			
				|  |  | +                await msg.delete();
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    ],
 | 
	
		
			
				|  |  | +    onStart: async () => {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        let repo = getRepository(KnownChannel);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        let verifyChannel = await repo.findOne({
 | 
	
		
			
				|  |  | +            channelType: NEWS_POST_VERIFY_CHANNEL
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (verifyChannel)
 | 
	
		
			
				|  |  | +            verifyChannelId = verifyChannel.channelId;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        await initPendingReactors();
 | 
	
		
			
				|  |  | +        interval(checkFeeds, RSS_UPDATE_INTERVAL_MIN * 60 * 1000);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  } as ICommand;
 |