Created
June 14, 2021 22:06
-
-
Save Snawoot/849abe612fd953b65ecfc02a9944f299 to your computer and use it in GitHub Desktop.
Скрипт Google App Script для получения уведомлений о новых сообщений в темах на рутрекере (rutracker.org) на почту
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> <= <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