Browse Source

Implement news verification

ghorsington 5 years ago
parent
commit
5c6cce09e1
4 changed files with 153 additions and 9 deletions
  1. 148 7
      commands/rss_checker.js
  2. 3 1
      db.js
  3. 1 1
      main.js
  4. 1 0
      package.json

+ 148 - 7
commands/rss_checker.js

@@ -3,6 +3,12 @@ const RSSParser = require("rss-parser");
 const db = require("../db.js");
 const interval = require("interval-promise");
 const client = require("../client.js");
+const sha1 = require("sha1");
+
+const verifyChannelID = db.get("newsPostVerifyChannel").value();
+
+const reactionCollectors = {};
+const verifyMessageIdToPostId = {};
 
 const turndown = new TurndownService();
 turndown.addRule("image", {
@@ -27,7 +33,6 @@ function getThreadId(url) {
 async function checkFeeds() {
     console.log(`Checking feeds on ${new Date().toISOString()}`);
     let feeds = db.get("rssFeeds").value();
-    let outlets = db.get("feedOutputs").value();
     let oldNews = db.get("postedNewsGuids");
     for(let feedEntry of feeds) {
         let feed = await parser.parseURL(feedEntry.url);
@@ -37,12 +42,37 @@ async function checkFeeds() {
         if(printableItems.length > 0) {
             printableItems.forEach(item => {
                 let itemID = getThreadId(item.guid);
-                if(oldNews.has(itemID).value())
-                    return;
-                outlets.forEach(ch => {
-                    client.channels.get(ch).send(`**${item.title}**\nPosted by ${item.creator}\n${item.link}\n\n${turndown.turndown(item[feedEntry.contentElement]).replace(/( {2}\n|\n\n){2,}/gm, "\n").replace(item.link, "")}`);
-                });
-                oldNews.set(itemID, true).write();
+
+                let contents = item[feedEntry.contentElement];
+                let itemObj = {
+                    id: itemID,
+                    title: item.title,
+                    link: item.link,
+                    creator: item.creator,
+                    contents: contents,
+                    hash: sha1(contents),
+                    messageId: null,
+                    verifyMessageId: null
+                };
+
+                if(oldNews.has(itemObj.id).value()){
+                    let data = oldNews.get(itemObj.id).value();
+                    // Old type, don't care
+                    if(data === true)
+                        return;
+
+                    // Add message ID to mark for edit
+                    if(data.hash != itemObj.hash)
+                        itemObj.messageId = data.messageId;
+                    else 
+                        return;
+                }
+
+                if(!shouldVerify())
+                    sendNews(itemObj);
+                else
+                    addVerifyMessage(itemObj);
+
             });
             let lastUpdateDate = printableItems[printableItems.length - 1].isoDate;
             console.log(`Setting last update marker on ${feedEntry.url} to ${lastUpdateDate}`);
@@ -51,7 +81,118 @@ async function checkFeeds() {
     }
 }
 
+function initPendingReactors() {
+    let verifyChannel = client.channels.get(verifyChannelID);
+    db.get("newsCache").forOwn(async i => {
+        let m = await verifyChannel.fetchMessage(i.verifyMessageId);
+        let collector = m.createReactionCollector(isVerifyReaction, { maxEmojis: 1 });
+        collector.on("collect", collectReaction)
+        reactionCollectors[m.id] = collector;
+        verifyMessageIdToPostId[m.id] = i.id;
+    }).value();
+}
+
+async function addVerifyMessage(item) {
+    let verifyChannel = client.channels.get(verifyChannelID);
+    let cache = db.get("newsCache");
+    let postedNews = db.get("postedNewsGuids");
+    let process = "🆕 ADD";
+
+    if(postedNews.has(item.id).value())
+        process = "✏️ EDIT";
+
+    if(cache.has(item.id).value()) {
+        let oldItem = cache.get(item.id).value();
+        let oldMessage = await verifyChannel.fetchMessage(oldItem.verifyMessageId);
+        if(oldMessage)
+            await oldMessage.delete();
+    }
+
+    let newMessage = await verifyChannel.send(`[${process}]
+Post ID: **${item.id}**
+
+${newsToString(item)}
+        
+React with ✅ (approve) or ❌ (deny).`
+);
+
+    
+    await newMessage.react("✅");
+    await newMessage.react("❌");
+
+    var collector = newMessage.createReactionCollector(isVerifyReaction, { maxEmojis: 1 });
+    collector.on("collect", collectReaction)
+    reactionCollectors[newMessage.id] = collector;
+    verifyMessageIdToPostId[newMessage.id] = item.id;
+    item.verifyMessageId = newMessage.id;
+    cache.set(item.id, item).write();
+}
+
+function collectReaction(reaction, collector) {
+    let cache = db.get("newsCache");
+    let oldNews = db.get("postedNewsGuids");
+    let m = reaction.message;
+    collector.stop();
+    delete reactionCollectors[m.id];
+    let postId = verifyMessageIdToPostId[m.id];
+    let item = cache.get(postId).value();
+    cache.unset(postId).write();
+    m.delete();
+
+    if(reaction.emoji.name == "✅")
+        sendNews(item);
+    else
+        oldNews.set(item.id, {
+            hash: item.hash,
+            messageId: null
+        }).write();
+}
+
+async function sendNews(item) {
+    let outChannel = db.get("feedOutputChannel").value();
+    let oldNews = db.get("postedNewsGuids");
+
+    let sentMessage = await postNewsItem(outChannel, item);
+    oldNews.set(item.id, {
+                    hash: item.hash,
+                    messageId: sentMessage.id
+                }).write();
+}
+
+function isVerifyReaction(reaction, user) {
+    return (reaction.emoji.name == "✅" || reaction.emoji.name == "❌") && !user.bot;
+}
+
+function shouldVerify() {
+    return verifyChannelID != "";
+}
+
+async function postNewsItem(channel, item) {
+    let newsText = newsToString(item);
+
+    let ch = client.channels.get(channel);
+
+    if(item.messageId) {
+        let message = await ch.fetchMessage(item.messageId);
+        if(message)
+            return await message.edit(newsText);
+        else 
+            return await ch.send(newsText);
+    } 
+    else 
+        return await ch.send(newsText);
+}
+
+function newsToString(item) {
+    return `**${item.title}**
+Posted by ${item.creator}
+${item.link}
+
+${turndown.turndown(item.contents).replace(/( {2}\n|\n\n){2,}/gm, "\n").replace(item.link, "")}`;
+}
+
 const onStart = () => {
+    initPendingReactors();
     interval(checkFeeds, RSS_UPDATE_INTERVAL_MIN * 60 * 1000);
 };
 

+ 3 - 1
db.js

@@ -71,8 +71,10 @@ db.defaults({
             contentElement: "content:encoded"
         }
     ],
+    newsCache: {},
     postedNewsGuids: {},
-    feedOutputs: ["493337841724555276"],
+    newsPostVerifyChannel: "",
+    feedOutputChannel: "493337841724555276",
     messageReactions: {},
     faceEditChannels: {
         "459622760839118848": 1.0,

+ 1 - 1
main.js

@@ -82,7 +82,7 @@ client.on("message", m => {
 });
 
 client.on("messageReactionAdd", (r, u) => {
-    if (Math.random() <= REACT_PROBABILITY && !r.me) {
+    if (Math.random() <= REACT_PROBABILITY && !u.bot) {
         console.log(`Reacting to message ${r.message.id} because user ${u.tag} reacted to it`);
         r.message.react(r.emoji);
     }

+ 1 - 0
package.json

@@ -29,6 +29,7 @@
     "request": "^2.88.0",
     "request-promise-native": "^1.0.5",
     "rss-parser": "^3.4.3",
+    "sha1": "^1.1.1",
     "turndown": "^5.0.1",
     "uws": "^99.0.0"
   },