Skip to content

Instantly share code, notes, and snippets.

@Snawoot
Created June 14, 2021 22:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Snawoot/849abe612fd953b65ecfc02a9944f299 to your computer and use it in GitHub Desktop.
Save Snawoot/849abe612fd953b65ecfc02a9944f299 to your computer and use it in GitHub Desktop.
Скрипт Google App Script для получения уведомлений о новых сообщений в темах на рутрекере (rutracker.org) на почту
const TRACKED_TOPICS = [
6027310,
6046402,
5887367,
5440705,
5591249,
];
const CONTENT_CHARSET = 'cp1251';
const TOPIC_URL_TMPL = 'https://rutracker.org/forum/viewtopic.php?t=%d&start=%d';
const POST_ID_RE = /^post_(\d+)$/;
const POST_URL_TMPL = 'https://rutracker.org/forum/viewtopic.php?p=%d#%d';
const scriptProperties = PropertiesService.getScriptProperties();
function parseQuery(url) {
const query = url.split("?")[1];
if (query) {
return query.split("&")
.reduce(function(o, e) {
var temp = e.split("=");
var key = temp[0].trim();
var value = temp[1].trim();
value = isNaN(value) ? value : Number(value);
o[key] = [value];
return o;
}, {});
}
return null;
}
function notifyOwner(topics) {
const email = Session.getEffectiveUser().getEmail();
const htmlBody = "<html><body>New posts found in following topics:<br>" +
topics
.map(([topicID, postID]) => Utilities.formatString('<a href="%s">%s</a> &lt;= <a href="%s">%s</a><br/>',
makeTopicURL(topicID, 0), makeTopicURL(topicID, 0), makePostURL(postID), makePostURL(postID)))
.join('\n')
+ '</body></html>'
const options = {
htmlBody: htmlBody,
};
const textBody = "New posts found in following topics:\n" +
topics
.map(([topicID, postID]) => Utilities.formatString('%s <= %s', makeTopicURL(topicID, 0), makePostURL(postID)))
.join('\n');
MailApp.sendEmail(email,
"rutracker topic watcher",
textBody,
options);
Logger.log("User %s notified!", email);
}
function makeTopicURL(topicID, startParam) {
return Utilities.formatString(TOPIC_URL_TMPL, topicID, startParam);
}
function makePostURL(postID) {
return Utilities.formatString(POST_URL_TMPL, postID, postID);
}
function fetchTopicPage(topicID, startParam) {
const url = makeTopicURL(topicID, startParam);
const resp = UrlFetchApp.fetch(url);
if (resp.getResponseCode() != 200) {
throw "bad response code from URL "+url;
}
return resp.getContentText(CONTENT_CHARSET);
}
function parseHTML(html) {
return Cheerio.load(html)
}
function getLatestPage(topicID) {
const page = fetchTopicPage(topicID, 0);
const doc = parseHTML(page);
const latestIdx = doc('a.pg').get().map(
(el) => {
const href = doc(el).attr('href')
if (href != null) {
const start = parseQuery(href)["start"];
if (isNaN(start)) {
console.log("start is null", href)
return 0;
}
return start;
} else {
return 0;
}
})
.reduce((p, v) => {
return ( p > v ? p : v );
}, 0);
console.log(`topic ${topicID} latestIdx=${latestIdx}`)
if (latestIdx > 0) {
console.log("refetch")
return parseHTML(fetchTopicPage(topicID, latestIdx));
}
return doc;
}
function latestPost(doc) {
const posts = doc('tbody')
.filter((_, el) => {
const idAttr = doc(el).attr('id');
return idAttr != null && idAttr.match(POST_ID_RE);
})
if (!posts.length) {
throw "posts not found";
}
return posts.last().attr('id').match(POST_ID_RE)[1];
}
function processTopic(topicID) {
const doc = getLatestPage(topicID);
const latest = latestPost(doc);
Logger.log(latest);
if (scriptProperties.getProperty("topic_"+topicID+"_latest_post") != latest) {
scriptProperties.setProperty("topic_"+topicID+"_latest_post", latest);
return latest;
}
return null;
}
function main() {
const errors = {};
const updated = [];
Logger.log("The rutracker topic watcher starting...");
for (const topicID of TRACKED_TOPICS) {
try {
const res = processTopic(topicID);
if (res != null) {
updated.push([topicID, res]);
}
} catch(err) {
errors[topicID] = err;
}
}
if (updated.length) {
notifyOwner(updated);
}
Logger.log("updated=%s", updated);
Logger.log("errors=%s", errors);
if (errors.length) {
throw errors;
}
Logger.log("rutracker topic watcher run finished.");
}
function trigger(e) {
Logger.log("Trigger activated: %s", JSON.stringify(e));
main();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment