irc.js (5669B)
1 const irc = require("irc"); 2 const fetch = require("node-fetch"); 3 const moment = require("moment-timezone"); 4 5 const { 6 admins, 7 botName, 8 channels, 9 maintainers, 10 report, 11 RQAPI, 12 server, 13 URAPI 14 } = require("./config"); 15 16 const { 17 fetchData, 18 fullUrl, 19 getFullLink, 20 getFullTemplate, 21 sayTime, 22 setthis 23 } = require("./utils"); 24 25 const { fallback, reset, short } = require("./promUrlShortener"); 26 27 const client = new irc.Client(server, botName, { 28 channels 29 }); 30 31 function pm(sender, msg) { 32 client.say(sender, "I am a bot."); 33 if (msg.startsWith(report)) 34 admins.forEach(admin => 35 client.say(admin, `Message from ${sender}: ${msg}`) 36 ); 37 if(msg != 'KILL') return; 38 if(['pizero', 'pizero|afk', 'acagastya'].indexOf(from) < 0) return; 39 process.abort(); 40 } 41 42 function err(msg) { 43 maintainers.forEach(maintainer => 44 client.say(maintainer, `Error: ${JSON.stringify(msg)}`) 45 ); 46 } 47 48 async function announceRQ(sender, channel) { 49 const data = await fetchData(RQAPI); 50 if (data.error) 51 client.say( 52 channel, 53 `Error occurred, ${sender}. Try this instead: "[[CAT:REV]]"` 54 ); 55 else { 56 const { list } = data; 57 if (!list.length) client.say(channel, `Review queue is empty, ${sender}.`); 58 else { 59 client.say( 60 channel, 61 `${list.length} articles to review, ${sender}. They are:` 62 ); 63 const titles = list.map(({ title }) => title); 64 const times = list.map(({ timestamp }) => moment().to(moment(timestamp))); 65 const urls = titles.map(fullUrl); 66 client.say(channel, "(Hold on a sec... Shortening the URLs.)"); 67 sayShortUrls(true, urls, channel, titles, times); 68 } 69 } 70 } 71 async function announceUR(sender, channel) { 72 const data = await fetchData(URAPI); 73 if (data.error) 74 client.say( 75 channel, 76 `Error occurred, ${sender}. Try this instead: "[[CAT:Under Review]]"` 77 ); 78 else { 79 const { list } = data; 80 if (!list.length) 81 client.say(channel, `No articles are under review, ${sender}.`); 82 else { 83 client.say( 84 channel, 85 `${list.length} articles are under review, ${sender}. They are:` 86 ); 87 const titles = list.map(({ title }) => title); 88 const times = list.map(({ timestamp }) => moment().to(moment(timestamp))); 89 const urls = titles.map(fullUrl); 90 client.say(channel, "(Hold on a sec... Shortening the URLs.)"); 91 sayShortUrls(true, urls, channel, titles, times); 92 } 93 } 94 } 95 96 async function sayShortUrls( 97 review = false, 98 urlList, 99 channel, 100 titles = [], 101 times = [], 102 pending = [] 103 ) { 104 const shortUrls = await Promise.all(urlList.map(short)); 105 106 shortUrls.forEach(({ url, err }, idx) => { 107 if (!err) { 108 let msg = url; 109 if (review) msg += " submitted for review"; 110 if (times.length) msg += ` *${times[idx]}*`; 111 if (titles.length) msg += ` -- ${titles[idx]}`; 112 if (pending[idx]) msg += " *under review*"; 113 client.say(channel, msg); 114 } else console.log(err); 115 }); 116 } 117 118 function groupChat(sender, channel, msg) { 119 const thanksRegex = new RegExp(`thanks,? ${botName}`, "i"); 120 if (thanksRegex.test(msg)) client.say(channel, `You are welcome, ${sender}.`); 121 if (msg.includes(`${botName} !RQ`)) announceRQ(sender, channel); 122 if (msg.includes(`${botName} !UR`)) announceUR(sender, channel); 123 if (msg.includes(`${botName} !FB`)) fallback(); 124 if (msg.includes(`${botName} !TRY`)) reset(); 125 if (msg.includes(`${botName} !SET`)) setthis(sender, channel, msg, client); 126 if (msg.includes(`${botName} !time`)) sayTime(msg, client, channel); 127 const regex1 = /\[{2}(.*?)\]{2}/g; 128 const regex2 = /\{{2}(.*?)\}{2}/g; 129 const links = msg.match(regex1); 130 const templates = msg.match(regex2); 131 if (links) { 132 const nonEmptyLink = links.filter(el => el.length > 4); 133 const fullLinks = nonEmptyLink.map(getFullLink); 134 if (fullLinks.length) sayShortUrls(false, fullLinks, channel); 135 } 136 if (!msg.endsWith("--nl") && templates) { 137 const nonEmptyTl = templates.filter(el => el.length > 4); 138 const fullLinks = nonEmptyTl.map(getFullTemplate); 139 if (fullLinks.length) sayShortUrls(false, fullLinks, channel); 140 } 141 } 142 143 client.addListener("error", err); 144 client.addListener("pm", pm); 145 client.addListener("message", groupChat); 146 147 const submittedState = { 148 announced: [] 149 }; 150 151 async function announceSubmitted() { 152 const res = await fetch(RQAPI); 153 const parsed = await res.json(); 154 155 const underReview = await fetch(URAPI); 156 const urParsed = await underReview.json(); 157 const urTitles = urParsed.query.categorymembers.map(({ title }) => title); 158 159 const titleTime = parsed.query.categorymembers.map(({ title, timestamp }) => { 160 return { timestamp, title }; 161 }); 162 const allTitles = parsed.query.categorymembers.map(({ title }) => title); 163 const pending = titleTime.filter( 164 ({ title }) => !submittedState.announced.includes(title) 165 ); 166 const pendingRev = pending.map(el => urTitles.includes(el.title)); 167 const titles = pending.map(({ title }) => title); 168 submittedState.announced = [...allTitles]; 169 const urls = titles.map(fullUrl); 170 const times = pending.map(({ timestamp }) => moment().to(moment(timestamp))); 171 172 if (urls.length) { 173 channels.forEach(channel => client.say(channel, "Review Queue:")); 174 channels.forEach(channel => 175 sayShortUrls(true, urls, channel, titles, times, pendingRev) 176 ); 177 } 178 } 179 180 const submittedPeriod = 5 * 60 * 1000; 181 const cacheClearPeriod = 6 * 60 * 60 * 1000; 182 183 setInterval(announceSubmitted, submittedPeriod); 184 setInterval(() => { 185 submittedState.announced = []; 186 }, cacheClearPeriod);