您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Reddit as a Weapon script. Parts and idea by /u/noeatnosleep, enhanced by /u/enim, /u/creesch, /u/skeeto, and /u/djimbob. RaaW adds links for page-wide voting and reporting. It adds a 'report to /r/spam' link, an 'analyze user submission domains' link, and a 'report to /r/botwatchman' link to userpages. RaaW disables the np. domain. RaaW Adds a 'show source' button for comments. DISCLIAMER: Use this at your own risk. If the report button is misued, you could be shadowbanned.
当前为
// ==UserScript== // @name RaaW // @version 3.1.6 // @namespace RaaW // @run-at document-end // @description Reddit as a Weapon script. Parts and idea by /u/noeatnosleep, enhanced by /u/enim, /u/creesch, /u/skeeto, and /u/djimbob. RaaW adds links for page-wide voting and reporting. It adds a 'report to /r/spam' link, an 'analyze user submission domains' link, and a 'report to /r/botwatchman' link to userpages. RaaW disables the np. domain. RaaW Adds a 'show source' button for comments. DISCLIAMER: Use this at your own risk. If the report button is misued, you could be shadowbanned. // @include http://www.reddit.com/user/* // @include http://www.reddit.com/r/* // @include http://*reddit.com/* // @include https://www.reddit.com/user/* // @include https://www.reddit.com/r/* // @include https://*reddit.com/* // @require https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js // ==/UserScript== this.jQuery = jQuery.noConflict(true); // define a basic object that we can extend with our functions so we do not accidentally // override other stuff var RaaW = { // //////////////////////////////////////////////////////////////////////// // constants // //////////////////////////////////////////////////////////////////////// // some css properties for the links in the toolbox LINK_CSS: { 'color': '#000', }, // //////////////////////////////////////////////////////////////////////// // instance variables // //////////////////////////////////////////////////////////////////////// raawToolbar: false, // true if we are moderator on the current page (by checking if .moderator is present) // in <body class="..."> isModerator: false, currentPage: 'user', // //////////////////////////////////////////////////////////////////////// // various helper functions // //////////////////////////////////////////////////////////////////////// /** * Function grabs the username of the current viewed profile. * * Returns: * (string) username or undefined if not found */ _getUsername: function() { return jQuery(document).find('.pagename.selected').text(); }, _getModhash: function() { return unsafeWindow.reddit.modhash; }, // //////////////////////////////////////////////////////////////////////// // initialization // //////////////////////////////////////////////////////////////////////// /** * Initialize RaaW. Will fetch some values like current page and after * that initialize the toolbar. */ init: function() { // first gather all the information needed this._loadEnvironment(); // now add some elements we will need this._injectElements(); // add the toolbar this._generateToolbar(); // after we created everything connect it this._registerListener(); }, /** * Load environment values like current page. */ _loadEnvironment: function() { // set current page this.currentPage = document.URL.split('reddit.com')[1].split('/')[1]; // check if we are moderator this.isModerator = jQuery('body').hasClass('moderator'); }, /** * Adds/modifies needed elements to the reddit page (e.g. 'toggle source' links). */ _injectElements: function() { // add links 'toogle source' to comments var toggleSourceCodeEl = jQuery('<li><a class="raawToggleSourceCode" href="#">view source</a></li>'); jQuery('.entry .flat-list').append(toggleSourceCodeEl); //disable .np if (document.documentElement.lang === 'np') { document.documentElement.lang = 'en-us'; } // add subscriber class to body tag jQuery('body').addClass('subscriber'); // replace links on the page Array.forEach( document.links, function(a) { a.href = a.href.replace( "https://i.imgur.com", "http://imgur.com"); a.href = a.href.replace( "https://imgur.com", "http://imgur.com"); }); // set checkbox 'limit my search to /r/...' checked jQuery('form#search input[name="restrict_sr"]').prop('checked', true); // add mod only stuff if(this.isModerator === true) { this._injectSaveAsMod(); this._injectNuke(); } }, /** * Register all click listener for the RaaW toolbar links. We do not distingish if we * are on /user or something else. There should be no noticeable impact on performance * and we save some maintenance effort. */ _registerListener: function() { // we don't want js to bind 'this' to the global object. therefore we use a trick. // whenever you need a 'this' reference inside one of the functions pointing to // the RaaW object use 'that' var that = this; // register click handler for the user toolbar links jQuery('#raawReportComment').click(function(e) { that.reportAll(e); }); jQuery('#raawBotwatchmanSend').click(function(e) { that.botwatchmanSend(e); }); jQuery('#raawAnalyzeSend').click(function(e) { that.analyzeSend(e); }); jQuery('#raawReportUserToSpam').click(function(e) { that.reportUserToSpam(e); }); jQuery('#raawAdminSend').click(function(e) { that.adminSend(e); }); // register handler for the other toolbar links jQuery('#raawDownvoteComment').click(function(e) { that.voteAll(e, -1); }); jQuery('#raawUpvoteComment').click(function(e) { that.voteAll(e, 1); }); jQuery('#raawComposeNew').click(function(e) { that.composeNew(e); }); jQuery('.raawToggleSourceCode').click(function(e) { that.toggleSourceCode(e); }); }, // //////////////////////////////////////////////////////////////////////// // toolbar stuff // //////////////////////////////////////////////////////////////////////// /** * Helper function used to create an a-element. * * Parameters: * id (string) - id attribute value * href (string) - href attribute value * text (string) - elements text * * Returns: * jQuery element instance */ _generateToolbarLink: function(id, href, text) { var link = jQuery('<a id="' + id + '" href="' + href + '">' + text + '</a>'); jQuery(link).css(this.LINK_CSS); return link; }, /** * Generate the toolbar on top of the page. */ _generateToolbar: function() { // apply some styles to the header jQuery('#header').css({ 'paddingTop': '18px' }); // create the new raaw toolbar and insert into body this.raawToolbar = jQuery('<div id="raawToolbar"></div>'); jQuery('body').prepend(this.raawToolbar); // apply style to the new toolbar jQuery(this.raawToolbar).css({ 'color': '#000' , 'background-color': '#f0f0f0' , 'border-bottom': '1px solid #000' , 'font-family': 'erdana, arial, helvetica, sans-serif' , 'font-size': '90%' , 'height': '12px' , 'padding': '3px 0px 3px 6px' , 'text-transform': 'uppercase' , 'width': '100%' , 'z-index': '+999999' , 'position': 'fixed' , 'top': '0' }); // fill toolbar with content depending on parsed page var toolbarLinks = new Array(); if(this.currentPage === 'user') { toolbarLinks.push(this._generateToolbarLink('raawReportComment', '#', 'REPORT ALL')); toolbarLinks.push(this._generateToolbarLink('raawBotwatchmanSend', '#', ' | /R/BOTWATCHMAN')); toolbarLinks.push(this._generateToolbarLink('raawAnalyzeSend', '#', ' | ANALYZE')); toolbarLinks.push(this._generateToolbarLink('raawReportUserToSpam', '#', ' | /R/SPAM')); toolbarLinks.push(this._generateToolbarLink('raawAdminSend', '#', ' | ADMIN')); } else { toolbarLinks.push(this._generateToolbarLink('raawDownvoteComment', '#', 'DOWNVOTE ALL')); toolbarLinks.push(this._generateToolbarLink('raawUpvoteComment', '#', ' | UPVOTE ALL')); toolbarLinks.push(this._generateToolbarLink('raawComposeNew', '#', ' | COMPOSE')); } for(i = 0; i < toolbarLinks.length; i++) { jQuery(this.raawToolbar).append(toolbarLinks[i]); } }, // //////////////////////////////////////////////////////////////////////// // functions for user toolbar // //////////////////////////////////////////////////////////////////////// /** * Report a given item using the reddit api. * * Parameters: * fullname (string) - fullname of item to report * el (jQuery el) - element to report (just for easier coding) * timeout (int) - timeout before request */ _reportItem: function(fullname, el, timeout) { var that = this; setTimeout(function() { var data= { 'api_type': 'json' , 'thing_id': fullname , 'uh': that._getModhash() }; jQuery.post('http://www.reddit.com/api/report', data).done(function(response) { jQuery(el).hide(1000); }).error(function(response) { console.log(response); }); }, timeout); }, /** * Report all items on the page. * * Parameters: * click (jQuery click event) - the jQuery click event. */ reportAll: function(click) { click.preventDefault(); var isConfirmed = confirm("This will report all items on the page."); if (isConfirmed === true) { // load all fullname of the comments on the page var i = 0; var that = this; jQuery('div#siteTable .thing').each(function(index, el) { var fullname = jQuery(el).attr('data-fullname'); that._reportItem(fullname, el, (400 * i) + 100); i++; }); // not accurate but will do alert('All items on this page were reported.'); } else { alert('Report canceled'); } }, /** * Open a new window to submit a user to /r/botwatchman. * * Parameters: * click (jQuery click event) - the jQuery click event. */ botwatchmanSend: function(click) { click.preventDefault(); var username = this._getUsername(); window.open('http://www.reddit.com/r/botwatchman/submit?title=overview for ' + username + '&url=http://www.reddit.com/user/' + username); }, /** * Send a new message to /u/analyzereddit with subject 'analyze' and a username * as message. * * Parameters: * click (jQuery click event) - the jQuery click event. */ analyzeSend: function(click) { click.preventDefault(); var username = this._getUsername(); window.open('http://www.reddit.com/message/compose/?to=analyzereddit&subject=analyze&message=' + username); }, /** * Open a new window to report a user to /r/spam. * * Parameters: * click (jQuery click event) - the jQuery click event. */ reportUserToSpam: function(click) { click.preventDefault(); var username = this._getUsername(); window.open('http://www.reddit.com/r/spam/submit?title=overview for '+ username + '&resubmit=true&url=http://www.reddit.com/user/' + username); }, /** * Open a new window to send a new message to /r/reddit.com. */ adminSend: function(click){ click.preventDefault(); var username = this._getUsername(); window.open('http://www.reddit.com/message/compose/?to=/r/reddit.com&message=/u/'+ username); }, // //////////////////////////////////////////////////////////////////////// // functions for default toolbar // //////////////////////////////////////////////////////////////////////// /** * Makes an asynch ajax call to the reddit API after waiting for the given amount * of time. * * Parameters: * data (object) - data to send (dir, uh, id) * thing (jQuery el) - element the vote belongs to * timeout (int) - time to wait in miliseconds */ _voteCallAPI: function(data, thing, timeout) { setTimeout(function() { console.log(data); jQuery.post('/api/vote', data).done(function(response) { jQuery(thing).hide(1000); }).error(function(response) { console.log('Error voting on item!'); console.log(response); }); }, timeout); }, /** * Up- or downvote all comment on a page. * * Parameters: * event (jQuery click event) * dir (int) - 1 upvote, -1 downvote, 0 none */ voteAll: function(event, dir) { event.preventDefault(); var things = jQuery('div.sitetable div.thing'); // gather the required fullnames to call the API for(i = 0; i < things.length; i++) { var thing = things[i]; var fullname = jQuery(thing).attr('data-fullname'); if(typeof fullname !== 'undefined') { // send request to the api var data= { 'dir': dir, 'id': fullname, 'uh': this._getModhash() }; this._voteCallAPI(data, thing, 100+(i*400)); } } }, /** * Open a new window to compose a new message. * * Parameters: * click (jQuery click event) - the jQuery click event. */ composeNew: function(click) { click.preventDefault(); window.open('http://www.reddit.com/message/compose/'); }, // //////////////////////////////////////////////////////////////////////// // 'view source' related functions // //////////////////////////////////////////////////////////////////////// /** * Helper function to fetch the sourcecode of comments/links/messages using the * reddit api. * * Parameters: * url (string) - because of the diversity of the api provide a url with the needed attributes * fullname (string) - fullname needed to search if loading messages * callback (function(source)) - callback function to call when api call is done */ _fetchSourceCode: function(url, fullname, callback) { jQuery.getJSON(url).done(function(response) { // check what type of posting we're looking at (check api for more information) var postingType = response.data.children[0].kind; // unfortunately the returned json object has no unified structure so // we need a bit more logic here var source; if(postingType === 't1') { // comment source = response.data.children[0].data.body; } else if(postingType === 't3') { // link (post); will be empty for videos or similiar source = response.data.children[0].data.selftext; } else if(postingType === 't4') { // message // the current api url loads a message thread so we need to find the // desired message rawData = response.data.children[0].data; if(rawData.name === fullname) { source = rawData.body; } else { // search through replies var replies = rawData.replies.data.children; for(var i = 0; i < replies.length; i++) { var replyRaw = replies[i].data; if(replyRaw.name === fullname) { source = replyRaw.body; break; } } } } callback(source); }); }, /** * Create a textarea to display source code * * Parameters: * source (string) - source code to display * fullname (string) - fullname of link/comment/message so we can later identify if we already loaded the source * prependTo (jQuery element) - element to prepend the textarea to */ _createSourceCodeTextarea: function(source, fullname, prependTo) { // create a textarea to display source and add it to the dom var textAreaEl = jQuery('<textarea class="'+fullname+'">'+source+'</textarea>'); jQuery(textAreaEl).css({ 'display': 'block' , 'width': '90%' , 'height': '100px' }); // insert textarea jQuery(prependTo).prepend(textAreaEl); }, /** * Toggle source code. * * Parameters: * click (jQuery click event) - the jQuery click event. */ toggleSourceCode: function(click) { click.preventDefault(); // grab the clicked link element var linkEl = jQuery(click.target); // get the data-fullname attribute to provide an id to 'throw at the api' var dataFullname = jQuery(linkEl).closest('.thing').attr('data-fullname'); var isTextAreaPresent = jQuery('textarea.'+dataFullname); if(isTextAreaPresent.length == 1) { jQuery(isTextAreaPresent).toggle(); } else { // figure out the element where we're going to insert the textarea var prependTo = jQuery(linkEl).parent().parent(); // do an ajax request to fetch the data from the api // because we cannot fetch a message and a link/comment with the same api call // we need to figure out, if we are on a message site var apiURL; if(this.currentPage === 'message') { // cut off t4_ for filtering messageId = dataFullname.slice(3, dataFullname.length); apiURL = '/message/messages/.json?mid='+messageId+'&count=1'; } else { apiURL = '/api/info.json?id=' + dataFullname; } var that = this; this._fetchSourceCode(apiURL, dataFullname, function(source) { that._createSourceCodeTextarea(source, dataFullname, prependTo); }); } }, // //////////////////////////////////////////////////////////////////////// // 'save as mod' related stuff // //////////////////////////////////////////////////////////////////////// /** * Place a 'save as mod' in the comment forms. * * Parameters: * el (jQuery element) - form element to place the button in; leave out to inject into all */ _injectSaveAsMod: function(el) { var that = this; if(typeof el === 'undefined') { el = false; } // no element given -> inject into all forms var injectHere = new Array(); if(el === false) { injectHere = jQuery('.usertext-buttons'); } else { injectHere.push(jQuery(el).find('.usertext-buttons')[0]); } // inject between save and cancel for(i = 0; i < injectHere.length; i++) { // element where the buttons life in var divEl = injectHere[i]; // button to inject; register click function... var buttonEl = jQuery('<button type="button" class="raawSaveAsMod">save as mod</button>'); jQuery(buttonEl).click(function(e) { that.saveAsMod(e); }); // find save button and add save as mod after that jQuery(divEl).find('button[type="submit"]').after(buttonEl); } // remove the buttons from the edit form jQuery('div.entry .usertext-buttons button.raawSaveAsMod').remove(); // find all reply links var replyButtons = jQuery('ul.flat-list li a').filter(function(index, el) { return jQuery(el).text() === 'reply'; }); for(i = 0; i < replyButtons.length; i++) { var button = replyButtons[i]; jQuery(button).click(function(e) { setTimeout(function() { var allButtons = jQuery('button.raawSaveAsMod'); for(i = 0; i < allButtons.length; i++) { var button = allButtons[i]; jQuery(button).off('click'); jQuery(button).click(function(e) { that.saveAsMod(e); }); } }, 500); }); } }, /** * Method will prevent a comment form to submit in the first place. Will fetch the * thing id to use it for distinguishing. After that will submit the form and when the * submit is finished distinguish the comment itself. * * Parameters: * click (jQuery click event) */ saveAsMod: function(click) { click.preventDefault(); var form = jQuery(click.target).closest('form.usertext'); // get parent var hiddenInput = jQuery(form).children('input[name="thing_id"]')[0]; var parent = jQuery(hiddenInput).val(); // get comment text var textarea = jQuery(form).find('textarea')[0]; var commentText = jQuery(textarea).val(); var modhash = this._getModhash(); // post comment data = { 'api_type': 'json' , 'text': commentText , 'thing_id': parent , 'uh': modhash }; jQuery.post('/api/comment', data).done(function(response) { if(response.json.errors.length > 0) { console.log('Error while posting comment:'); console.log(response.json.errors); alert('Error while posting comment. Please check the console for more information!'); return; } // distinguish var commentData = response.json.data.things[0].data; var data = { 'id': commentData.id , 'api_type': 'json' , 'how': 'yes' , 'uh': modhash }; jQuery.post('/api/distinguish', data).done(function(response) { if(response.json.errors.length > 0) { console.log('Error while posting comment:'); console.log(response.json.errors); alert('Error while posting comment. Please check the console for more information!'); return; } location.reload(); }); }); }, // //////////////////////////////////////////////////////////////////////// // nuke functions // //////////////////////////////////////////////////////////////////////// /** * Find the a comment with the given id in a response object. * * Parameters: * commentId (string) - something like t1_abc * response (object) - response object returned by a API call * * Returns: * (object) or undefined */ _findCommentInResponse: function(commentId, response) { var searchedComment; // build a search queue to go through var search = new Array(); for(var i = 0; i < response.length; i++) { var listing = response[i].data.children; for(var n = 0; n < listing.length; n++) { var content = listing[n]; // if data is something else than a comment skip if(content.kind !== 't1') { continue; } else { // the comment is the one we search if(content.data.id === commentId) { return content; } else { // comment is not what we search but maybe one of his replies? // add replies to search queue if(content.data.replies !== '') { search = search.concat(content.data.replies.data.children); } } } } } while(search.length > 0) { var currentObj = search.pop(); // check if this is the right comment if(currentObj.data.id === commentId) { return currentObj; } // add all the replies of this comment to the search array if(currentObj.data.replies !== '') { search = search.concat(currentObj.data.replies.data.children); } } return searchedComment; }, /** * Will find all replies to the given comment. * * Parameters: * obj (object) - thing object returned by a API call * * Returns: * (Array) Maybe an empty array if no replies where found. Array holds the ids as a * string. E.g. '1234', 'a23pav', ... */ _findAllReplies: function(obj) { var replies = new Array(); // check if there are replies if(obj.data.replies === '') { return replies; } var search = new Array(); for(var i = 0; i < obj.data.replies.data.children.length; i++) { var reply = obj.data.replies.data.children[i]; replies.push(reply.data.id); if(reply.data.replies !== '') { search = search.concat(reply.data.replies.data.children); } } while(search.length > 0) { var currentObj = search.pop(); // 'more' occures if there are more than 10 entries if(currentObj.kind === 'more') { continue; } // add the id of the current reply replies.push(currentObj.data.id); // add all replies to the reply to the search if(currentObj.data.replies !== '') { search = search.concat(currentObj.data.replies.data.children); } } return replies; }, /** * Remove the given comment. * * Parameters: * commentId (string) - id of the comment to remove * timeout (int) - timeout in milliseconds */ _nukeComment: function(commentId, timeout) { var that = this; setTimeout(function() { var data = { 'id': 't1_' + commentId , 'uh': that._getModhash() } jQuery.post('/api/remove', data).done(function(response) { var el = jQuery('div.thing[data-fullname="t1_'+commentId+'"]'); jQuery(el).hide(1000, function() { jQuery(el).remove(); }); }).error(function(response) { console.log(response); }); }, timeout); }, /** * Inject the nuke button. */ _injectNuke: function() { var nukeButton = jQuery('<a href="#" class="raawNuke" title="Nuke!">[Nuke]</a>'); // add click listener var that = this; jQuery(nukeButton).click(function(e) { e.preventDefault(); var dataFullname = jQuery(this).closest('div.thing').attr('data-fullname'); var link = jQuery('#siteTable').find('div.thing').attr('data-fullname').slice(3); // cut off t3_ // get all the comments for this thread and find the right parent comment jQuery.getJSON('/comments/'+link+'.json').done(function(response) { var searchedComment = that._findCommentInResponse(dataFullname.slice(3), response); if(typeof searchedComment !== 'undefined') { // extract all children var removeThese = that._findAllReplies(searchedComment); // add the parent itself; but remove t1_ because thats how the other look to! removeThese.push(dataFullname.slice(3)); for(var i = 0; i < removeThese.length; i++) { var childsDataFullname = removeThese[i]; that._nukeComment(removeThese[i], (i * 750) + 100); } } }); }); // insert button jQuery('div.commentarea div.thing p.tagline span.userattrs').prepend(nukeButton); } }; // initialize when document loaded jQuery(document).ready(function() { RaaW.init(); });