// ==UserScript==
// @name AnimeBytes Forum Tag Notifications
// @author sabs (like "sobs"), [email protected]
// @namespace sabs
// @version 22
// @description Find out when someone tags you
// @match https://animebytes.tv/*
// @homepageURL https://animebytes.tv/forums.php?action=viewthread&threadid=27261
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_log
// @grant GM_registerMenuCommand
// @icon data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABb2lDQ1BpY2MAACiRdZE7SwNBFIW/JEp8YqFFEIsUUSwiiIJop7FIEyTECEZtNutuIiTrspsgwVawsQhYiDa+Cv+BtoKtgiAogoiNf8BXI7LeMUJEdJbZ+3FmzmXmDPgTBb3oNgxC0So5qXgsPJuZCwcfaSaEnyBjmu7aE8lkgn/H2zU+Va8GVK//9/05WhcNVwdfk/CIbjsl4XHhxErJVrwh3KXntUXhPeGoIwcUPld6tsYPinM1flHspFOT4Fc9w7kfnP3Bet4pCvcLR4qFsv59HnWTNsOamZbaLbMHlxRxYoTJUmaJAiUGpFqS2d++wS/fFMvi0eVvU8ERR468eKOilqWrIdUU3ZCvQEXl/jtP1xweqnVvi0Hjvec990JwEz6qnve+73kfBxC4g1Or7l+WnEZfRa/WtcgudKzB8Vldy27ByTqEbm3N0b6kgEy/acLTEbRnoPMSWuZrWX2vc3gD6VV5ogvY3oE+2d+x8AnhZWf8PQBFKQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAbRJREFUOMudU00oRFEUPucOZjGpec+MorCzGIvZWtsgOz8rU8oCmWEjslAiJUkx4ydlyQpblFKWFsqkLCwpI8x7VmJ49zj3+enNNaQ5m9v97rnfOee730XQImvGG5Aw5veVJHNvTj2Bsw2I9wB4hKW4Ydyl0t584d1YRmIWJaWZ4CDwsJghdOYIIExEESKZoJxzyjnrFJksyyNQQDY4uMtJ44BizXhMHbunBGFvASbzcU6fdfOw/0XiEli32Qle2hHAYWj+6wICTn9gelCTnckuuDmWMVwL9HbJ7H5EODHt1UZvql05FKVXOcpjdHtxRUwCo9wBH/Llz4nSei0lmmmvxHwCuvRxWKsBodqBf0TQWt1BxC19FMFMdd9booq/aXBP66JOdSA9g7VQaKz8t+uEMpAPUECw0tdewJJP/b82ILFZhwQSHOQ1KeWMZQy16Yl2MN7DFTp+DOVal92nVM07YMGY/FAivxCRqtxeUJVPC6eUVaGIcJ1oVFWMqM9SNAFeTOXM6lArolgubN3vdl90L2Ch7ywk9JJ6UqAa1uGZAM+50hmhb8m0k1eWEd9kXTqVg98BpA+2IT/y+kMAAAAASUVORK5CYII=
// ==/UserScript==
(function() {
'use strict';
unsafeWindow.abng = {
alerts: JSON.parse(GM_getValue("alerts", "{}")),
preference: GM_getValue("preference", "tag"),
hidden: GM_getValue("hidden", false)
}
function getUsername() {
return document.querySelector(".username").text
}
var bookmarks, header, text, a, linkbox, container
GM_registerMenuCommand("Check for Mentions", () => {
refresh()
})
function getUnreadAlerts() {
return Object.values(unsafeWindow.abng.alerts).filter(a => !a.seen)
}
function extractPostsFromSearchPage(response) {
var container = document.implementation.createHTMLDocument().documentElement;
container.innerHTML = response.responseText;
var fid, pid, date, clean;
var ret = {};
Array.from(container.querySelector(".colhead").parentNode.children).slice(1).forEach(row => {
fid = row.children[0].children[0].href.split("=").slice(-1)[0];
pid = row.children[1].children[0].href.split("t").slice(-1)[0];
date = Date.parse(row.children[2].children[0].title.slice(0,-4))
clean = new Date(date).toDateString()
ret["" + fid + "-" + pid] = {
row: row.outerHTML,
date: date,
fpid: "" + fid + "-" + pid,
clean: clean,
seen: false,
}
})
return ret
}
function hourlyCheck() {
let last_check = new Date(GM_getValue("last_check", 0))
var t1 = last_check.getTime();
var t2 = new Date().getTime();
let hours = parseInt((t2-t1)/(3600*1000));
if (hours >= 1) {
refresh()
} else {
buildAlertsArea()
buildAlertDisplay()
}
}
hourlyCheck()
function refresh() {
buildAlertDisplay(true)
getPosts((ret) => {
reloadAlerts(ret)
buildAlertsArea()
buildAlertDisplay()
GM_setValue("last_check", new Date().toString())
})
}
function reloadAlerts(alerts) {
let old_alerts = JSON.parse(GM_getValue("alerts", "{}"))
unsafeWindow.abng.alerts = {...alerts, ...old_alerts}
GM_setValue("alerts", JSON.stringify(unsafeWindow.abng.alerts))
}
function createLinkboxLink(text, callback) {
a = document.createElement("a")
a.addEventListener("click", callback, false)
a.style = "cursor: pointer;"
a.appendChild(document.createTextNode(text))
return a
}
function markAlertRead(id) {
unsafeWindow.abng.alerts = JSON.parse(GM_getValue("alerts", "{}"))
unsafeWindow.abng.alerts[id].seen = true
GM_setValue("alerts", JSON.stringify(unsafeWindow.abng.alerts))
}
function changeAlertOption(value) {
unsafeWindow.abng.preference = value
GM_setValue("preference", unsafeWindow.abng.preference)
}
function buildAlertDisplay(checking = false) {
let alerts = getUnreadAlerts()
let a = document.querySelector("#my_mentions")
if (a !== null) {
a.remove()
}
let light,dark,text
a = createLinkboxLink(text, () => {
let div = document.querySelector("#not_content")
div.style.display = div.style.display === "none" ? "block" : "none";
})
if (checking) {
a.text="checking..."
dark="#f38080"
light="#ed106a"
} else if (alerts.length === 0) {
if (unsafeWindow.abng.hidden) {
dark="#f3808000"
} else {
dark="#f38080"
}
light="#ed106a"
a.text="no mentions"
} else {
let s = alerts.length === 1 ? "" : "s"
a.text = "" + alerts.length + " mention" + s
dark="#ed106a"
light="#f380af"
a.style.fontWeight="bold"
}
a.id = "my_mentions"
a.style.color=dark
a.addEventListener("mouseenter", () => {
a.style.color=light
}, false)
a.addEventListener("mouseleave", () => {
a.style.color=dark
}, false)
let username_menu = document.querySelector("#username_menu")
a.style.float = "left"
a.style.lineHeight = "20px"
a.style.verticalAlign = "middle"
a.style.padding = "0 5px"
username_menu.parentNode.insertBefore(a, username_menu)
}
function createCheckbox(id, checked, label_text, listener) {
let checkbox;
checkbox = document.createElement("input")
checkbox.type = "checkbox";
checkbox.name = "settings";
checkbox.style = "display:inline-block;"
checkbox.id = id;
checkbox.checked=checked;
checkbox.addEventListener('change', () => {
listener(checkbox)
})
var label = document.createElement('label')
label.htmlFor = id;
label.style = "display:block;text-align:left"
label.appendChild(checkbox)
label.appendChild(document.createTextNode(" " + label_text));
return label
}
function createRadiobox(id, checked, label_text, listener) {
let checkbox;
checkbox = document.createElement("input")
checkbox.type = "radio";
checkbox.name = "settings";
checkbox.style = "display:inline-block;"
checkbox.id = id;
checkbox.checked=checked;
checkbox.addEventListener('change', () => {
listener(checkbox)
})
var label = document.createElement('label')
label.htmlFor = id;
label.style = "display:block;text-align:left"
label.appendChild(checkbox)
label.appendChild(document.createTextNode(" " + label_text));
return label
}
function buildAlertsArea() {
let ret = {}
let data = []
let div, table, body, row, text, td, options
let should_display = "none"
div = document.querySelector("#not_content")
if (div !== null) {
should_display = div.style.display
div.remove()
}
div = document.createElement("div")
div.id = "not_content"
if (getUnreadAlerts().length !== 0) {
div.innerHTML='<div><table cellpadding="6" cellspacing="1" border="0" class="border" width="100%"><tbody><tr class="colhead"><td>Forum</td><td>Topic</td><td>Time</td><td>Mark Read</td><tr></tbody></table>'
table = div.querySelector("table")
var idx = 0;
Object.values(unsafeWindow.abng.alerts).sort((a,b) => a.date - b.date).forEach(alert => {
if (alert.seen) {
return
}
row = table.insertRow(1)
row.innerHTML = alert.row
row.classList.add(idx++ % 2 == 0 ? "rowa" : "rowb")
row.children[2].innerHTML = alert.clean
td = document.createElement("td")
td.width="12%"
td.appendChild(createLinkboxLink("[ x ]", () => {
markAlertRead(alert.fpid)
buildAlertsArea()
buildAlertDisplay()
}))
row.appendChild(td)
})
} else {
div.append("You don't have any new mentions!")
div.append(document.createElement("br"))
div.append("If you would like to see your previous mentions again, ", createLinkboxLink("click here to bring them all back.", ()=>{
if (confirm("You will receive all your previous mentions again.")) {
unsafeWindow.abng.alerts = JSON.parse(GM_getValue("alerts", "{}"))
unsafeWindow.abng.alerts = {}
GM_setValue("alerts", JSON.stringify(unsafeWindow.abng.alerts))
refresh()
}
}))
}
options = document.createElement("div")
options.append(createRadiobox("any", unsafeWindow.abng.preference === "any", "Notify me whenever a post contains my username", (checkbox)=>{
if (checkbox.checked) {
changeAlertOption("any")
refresh()
}
}))
options.append(createRadiobox("tag", unsafeWindow.abng.preference === "tag", "Only notify me when someone [user] tags me", (checkbox)=>{
if (checkbox.checked) {
changeAlertOption("tag")
refresh()
}
}))
options.append(createCheckbox("hidden", unsafeWindow.abng.hidden, "Hide the counter if I have zero mentions (it's still clickable)", (checkbox)=>{
unsafeWindow.abng.hidden = checkbox.checked
GM_setValue("hidden", unsafeWindow.abng.hidden)
refresh()
}))
options.style.lineHeight = "22px"
options.style.margin = "10px auto 20px"
options.style.textAlign = "center"
div.append(options)
div.style.display = should_display
document.querySelector("#content").prepend(div)
}
function getPosts(callback) {
let query;
if (unsafeWindow.abng.preference === "tag") {
query = "%5D" + getUsername() + "%5B"
} else {
query = getUsername()
}
GM_xmlhttpRequest({
method: "GET",
url: "https://animebytes.tv/forums.php?action=search&type=body&search=" + query,
onload: (r) => {
callback(extractPostsFromSearchPage(r))
}
})
}
})();