139 lines
4.6 KiB
JavaScript
139 lines
4.6 KiB
JavaScript
// @ts-ignore
|
|
const Config = require("../config.json");
|
|
|
|
const { promisify } = require("util");
|
|
const Camo = require("camo");
|
|
const Core = require("../../discord-bot-core");
|
|
const DiscordUtil = require("../../discord-bot-core").util;
|
|
const GetUrls = require("get-urls");
|
|
const Url = require("url");
|
|
|
|
// @ts-ignore
|
|
const readFeed = url => promisify(require("rss-parser").parseURL)(url);
|
|
const resolveDns = promisify(require("dns").resolve);
|
|
|
|
module.exports = class FeedData extends Core.BaseEmbeddedData {
|
|
constructor() {
|
|
super();
|
|
|
|
this.feedID = "";
|
|
this.url = "";
|
|
this.channelID = "";
|
|
this.roleID = "";
|
|
this.cachedLinks = [];
|
|
this.maxCacheSize = 100;
|
|
|
|
// @ts-ignore
|
|
this.schema({
|
|
feedID: String,
|
|
url: String,
|
|
channelID: String,
|
|
roleID: String,
|
|
cachedLinks: [String],
|
|
maxCacheSize: Number
|
|
});
|
|
}
|
|
|
|
cache(...elements) {
|
|
const newArticles = elements
|
|
.map(el => normaliseUrlForCache(el))
|
|
.filter(el => !this._isCached(el));
|
|
|
|
Array.prototype.push.apply(this.cachedLinks, newArticles);
|
|
|
|
this.cachedLinks.splice(0, this.cachedLinks.length - this.maxCacheSize); //seeing as new links come in at the end of the array, we need to remove the old links from the beginning
|
|
|
|
return elements.length > 0;
|
|
}
|
|
|
|
updatePastPostedLinks(guild) {
|
|
const channel = guild.channels.get(this.channelID);
|
|
|
|
if (!channel)
|
|
return Promise.reject("Channel not found!");
|
|
|
|
return new Promise((resolve, reject) => {
|
|
channel.fetchMessages({ limit: 100 })
|
|
.then(messages => {
|
|
/* we want to push the links in oldest first, but discord.js returns messages newest first, so we need to reverse them
|
|
* discord.js returns a map, and maps don't have .reverse methods, hence needing to spread the elements into an array first */
|
|
[...messages.values()].reverse().forEach(m => this.cache(...GetUrls(m.content)));
|
|
resolve();
|
|
})
|
|
.catch(reject);
|
|
});
|
|
}
|
|
|
|
fetchLatest(guild) {
|
|
const dnsPromise = resolveDns(Url.parse(this.url).host).then(() => this._doFetchRSS(guild));
|
|
|
|
dnsPromise.catch(err => DiscordUtil.dateDebugError("Connection error: Can't resolve host", err.message || err));
|
|
|
|
return dnsPromise;
|
|
}
|
|
|
|
toString() {
|
|
const blacklist = ["cachedLinks", "maxCacheSize"];
|
|
return `\`\`\`JavaScript\n ${JSON.stringify(this, (k, v) => !blacklist.find(x => x === k) ? v : undefined, "\t")} \`\`\``;
|
|
}
|
|
|
|
_isCached(url) {
|
|
return this.cachedLinks.indexOf(normaliseUrlForCache(url)) > -1;
|
|
}
|
|
|
|
_doFetchRSS(guild) {
|
|
const feedPromise = readFeed(this.url).then(parsed => this._processLatestArticle(guild, parsed.feed.entries));
|
|
|
|
feedPromise.catch(err => DiscordUtil.dateDebugError([`Error reading feed ${this.url}`, err]));
|
|
|
|
return feedPromise;
|
|
}
|
|
|
|
_processLatestArticle(guild, entries) {
|
|
if (entries.length === 0 || !entries[0].link)
|
|
return false;
|
|
|
|
if (this._isCached(entries[0].link))
|
|
return false;
|
|
|
|
this.cache(entries[0].link);
|
|
|
|
const channel = guild.channels.get(this.channelID),
|
|
role = guild.roles.get(this.roleID);
|
|
|
|
channel.send((role || "") + formatPost(entries[0]))
|
|
.catch(err => DiscordUtil.dateDebugError(`Error posting in ${channel.id}: ${err.message || err}`));
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
function formatPost(article) {
|
|
let message = "";
|
|
|
|
if (article.title) message += `\n**${article.title}**`;
|
|
if (article.content) message += article.content.length > Config.charLimit ? "\nArticle content too long for a single Discord message!" : `\n${article.content}`;
|
|
if (article.link) message += `\n\n${normaliseUrlForDiscord(article.link)}`;
|
|
|
|
return message;
|
|
}
|
|
|
|
function normaliseUrlForDiscord(url) {
|
|
const parsedUrl = Url.parse(url);
|
|
if (parsedUrl.host && parsedUrl.host.includes("youtube.com"))
|
|
url = normaliseYouTubeUrl(url, parsedUrl);
|
|
|
|
return url;
|
|
}
|
|
|
|
function normaliseYouTubeUrl(origUrl, parsedUrl) {
|
|
const videoIDParam = parsedUrl.query ? parsedUrl.query.split("&").find(x => x.startsWith("v=")) : null;
|
|
if (!videoIDParam)
|
|
return origUrl;
|
|
const videoID = videoIDParam.substring(videoIDParam.indexOf("=") + 1, videoIDParam.length);
|
|
return `http://youtu.be/${videoID}`
|
|
}
|
|
|
|
function normaliseUrlForCache(url) {
|
|
return normaliseUrlForDiscord(url).replace(/^((https?:\/\/)?(www.)?)/, "");
|
|
} |