Skip to content

Instantly share code, notes, and snippets.

@boisei0
Last active December 6, 2018 16:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save boisei0/2ea7d7145d04a1cc5864d316271d09fd to your computer and use it in GitHub Desktop.
Save boisei0/2ea7d7145d04a1cc5864d316271d09fd to your computer and use it in GitHub Desktop.
Tampermonkey userscript to show which of your posts got flagged. Requires you to be logged in on Tumblr and on the dashboard.
// ==UserScript==
// @name Tumblr flagging checker
// @namespace http://tampermonkey.net/
// @version 0.2.3
// @updateURL https://https://gist.githubusercontent.com/boisei0/2ea7d7145d04a1cc5864d316271d09fd/raw/tumblr_flagging_score_checker.js
// @description Will test posts and displays the scores for NSFW/explicit content and if they got flagged
// @author Lena (flight-of-the-felix.tumblr.com)
// @match https://www.tumblr.com/dashboard
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @connect tumblr.com
// ==/UserScript==
(function() {
'use strict';
const debug = false;
const debugFlag = -1;
let form_key;
let blogs = {};
// https://stackoverflow.com/a/47480429
const delay = ms => new Promise(res => setTimeout(res, ms));
async function doTumblrXHR(url, options={}) {
options = Object.assign(options, {url: url, timeout: 15000});
return await new Promise((resolve, reject) => {
options.onerror = options.ontimeout = reject;
options.onload = resolve;
GM_xmlhttpRequest(options);
});
}
const farmInfo = async () => {
return new Promise(resolve => {
document.getElementById('account_button').click();
return resolve(delay(3000));
}).then(() => {
form_key = document.getElementById('tumblr_form_key').getAttribute('content');
blogs = [...document.querySelectorAll('li.popover_menu_item.popover_menu_item_blog')].map(element => {return {name: element.id.split('--')[1], posts: 0}});
blogs = blogs.map(blog => {
let node = document.querySelector(`#blog-list-item--${blog.name} .blog-sub-nav-item-link .blog-sub-nav-item-data`);
if (node === null) {
return blog;
}
blog.posts = Number(node.textContent.replace(',', ''))
return blog;
});
if (debug && debugFlag === 1) {
console.log(blogs);
}
return delay(3000);
}).then(() => {
document.getElementById('account_button').click();
return delay(2000);
});
}
function viewBlog(blog_name, limit, offset) {
if (debug && debugFlag === 2) {
console.log(`DEBUG: blog: ${blog_name}; limit: ${limit}; offset: ${offset}`);
}
let url = `https://www.tumblr.com/svc/indash_blog?tumblelog_name_or_id=${blog_name}&post_id=&limit=${limit}&offset=${offset}&should_bypass_safemode=true&should_bypass_tagfiltering=true`;
return doTumblrXHR(url, {
method: 'GET',
headers: {
'X-tumblr-form-key': form_key,
'X-requested-with': 'XMLHttpRequest',
'Referer': `https://www.tumblr.com/dashboard/blog/${blog_name}`
},
responseType: 'json'
}).then(data => {
if (data.status === 429) {
throw Error('Tumblr has decided to rate limit you. Please try again in approximately 1 minute.');
}
return data.response.response.posts.map(post => {
return {
type: post.type,
is_nsfw: post.is_nsfw,
is_nsfw_based_on_score: post.is_nsfw_based_on_score,
is_flagged: post.classification === 'explicit',
nsfw_score: post.nsfw_score,
readable_nsfw_score: `${Math.round(post.nsfw_score * 100 * 10) / 10}%`,
post_url: post.post_url,
summary: post.summary,
post_id: post.id,
// No root post checks for now, post.trail is buggy and has been buggy for at least months. Better to just don't until I have a decent fix...
// is_root_post: (post.trail.length > 0) ? (post.trail[0].hasOwnProperty('is_root_item') && post.trail[0].hasOwnProperty('is_current_item')) : false,
_meta: post // Harder to reach, but who knows...
}
});
}).catch(reason => {
throw Error(`inside viewBlog; something went wrong: ${reason}`);
});
}
function indexFlags(event) {
let blog_name = event.target.getAttribute('data-blog_name');
let outputContainer = document.getElementById('tumblr_flag_output_container');
outputContainer.innerHTML = '';
let statusBar = document.getElementById('tumblr_flag_status');
statusBar.innerHTML = '';
const totalPosts = blogs.filter(blog => blog.name === blog_name)[0].posts;
const limit = 10;
let page = 0;
let maxPages = Math.floor(totalPosts / limit) + 1;
if (debug && debugFlag === 3) {
console.log(`DEBUG: Total posts: ${totalPosts}`);
console.log(`DEBUG: Total number of requests needed: ${maxPages}`);
}
do {
viewBlog(blog_name, limit, page*limit).then(posts => {
posts.forEach(post => {
if (post.is_flagged) {
let postOutputBlock = document.createElement('p');
const postLog = `[Flagged] ${post.summary}<br /> *** Post score: ${post.readable_nsfw_score}<br /> *** URL: <a href="${post.post_url}">${post.post_url}</a>`;
postOutputBlock.innerHTML = postLog;
outputContainer.appendChild(postOutputBlock);
}
});
}).catch(reason => {
statusBar.innerHTML = reason;
});
page += 1;
}
while (page < maxPages);
}
farmInfo().then(() => {
// Set base style needed
GM_addStyle("#tumblr_flag_container{background-color: #000; color: #fff} #tumblr_flag_container a {color: #fff; font-weight: bold; text-decoration: underline;} #tumblr_flag_container a:link {color: #fff} #tumblr_flag_container a:visited {color: #e9e9e9} #tumblr_flag_container a:hover {color: #e9c4d9} #tumblr_flag_status {color: #ff0000;}");
// Insert output block
let outputContainer = document.createElement('div');
outputContainer.id = 'tumblr_flag_container';
outputContainer.style.setProperty('display', 'block');
let t = document.createTextNode('View flag scores for your posts');
let header = document.createElement('h2');
header.style.setProperty('font-size', '125%');
header.appendChild(t);
t = 'Click on one of your (side)blogs on the right to view all your flagged posts. If there are none, refresh the page, as something went wrong. For a description of the score, see <a href="https://flight-of-the-felix.tumblr.com/post/180788909548/tumblr-and-flagging-an-overview">this long post I wrote earlier</a>.<br />If it fails, ask Tampermonkey if an update is available. If it is the latest version already, message me @ flight-of-the-felix. DM is open for questions.';
let description = document.createElement('p');
description.innerHTML = t;
outputContainer.appendChild(header);
outputContainer.appendChild(description);
let statusBar = document.createElement('div');
statusBar.id = 'tumblr_flag_status';
statusBar.style.setProperty('width', '100%');
statusBar.style.setProperty('height', '50px');
outputContainer.appendChild(statusBar);
let blogsList = document.createElement('div');
blogsList.classList.add('tab-bar-container');
let innerBlogsList = document.createElement('ul');
innerBlogsList.classList.add('tab_bar', 'tab-bar');
blogsList.appendChild(innerBlogsList);
blogsList.style.setProperty('display', 'block');
blogsList.style.setProperty('overflow-y', 'scroll');
blogsList.style.setProperty('height', '200px');
blogs.forEach(blog => {
let blogItem = document.createElement('li');
let blogButton = document.createElement('button');
blogButton.classList.add('tab');
blogButton.appendChild(document.createTextNode(blog.name));
blogButton.setAttribute('data-blog_name', blog.name);
blogButton.addEventListener('click', indexFlags);
blogItem.appendChild(blogButton);
innerBlogsList.appendChild(blogItem);
});
outputContainer.appendChild(blogsList);
let innerOutputContainer = document.createElement('div');
innerOutputContainer.id = 'tumblr_flag_output_container'
innerOutputContainer.style.setProperty('display', 'block');
innerOutputContainer.style.setProperty('overflow-y', 'scroll');
innerOutputContainer.style.setProperty('overflow-wrap', 'break-word');
innerOutputContainer.style.setProperty('height', '200px');
outputContainer.appendChild(innerOutputContainer);
let outputHelper = document.getElementById('posts');
outputHelper.parentNode.insertBefore(outputContainer, outputHelper);
});
})();
@boisei0
Copy link
Author

boisei0 commented Dec 6, 2018

Change log:
0.2.2: first version with actual updates possible
0.2.3: console.debug replaced by console.log to ease it for Chrome users.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment