Advertisement
Guest User

Untitled

a guest
Nov 20th, 2017
116
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Natty Reporter
  3. // @namespace    https://github.com/Tunaki/stackoverflow-userscripts
  4. // @version      0.22
  5. // @description  Adds a Natty link below answers that sends a report for the bot in SOBotics. Intended to be used to give feedback on reports (true positive / false positive / needs edit) or report NAA/VLQ-flaggable answers.
  6. // @author       Tunaki
  7. // @include      /^https?:\/\/(www\.)?stackoverflow\.com\/.*/
  8. // @grant        GM_xmlhttpRequest
  9. // @require      http://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js
  10. // @downloadURL  https://github.com/SOBotics/Userscripts/blob/master/Natty/NattyReporter.user.js
  11. // ==/UserScript==
  12.  
  13. var room = 111347;
  14. GM.xmlHttpRequest = GM_xmlhttpRequest;
  15.  
  16. function sendChatMessage(msg, answerId) {
  17.   GM.xmlHttpRequest({
  18.     method: 'GET',
  19.     url: 'http://chat.stackoverflow.com/rooms/' + room,
  20.     onload: function (response) {
  21.       var fkey = response.responseText.match(/hidden" value="([\dabcdef]{32})/)[1];
  22.       GM.xmlHttpRequest({
  23.         method: 'POST',
  24.         url: 'http://chat.stackoverflow.com/chats/' + room + '/messages/new',
  25.         headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  26.         data: 'text=' + encodeURIComponent(msg) + '&fkey=' + fkey,
  27.         onload: function (r) {
  28.           $('[data-answerid="' + answerId + '"] a.report-natty-link').addClass('natty-reported').html('Reported to Natty!');
  29.         }
  30.       });
  31.     }
  32.   });
  33. }
  34.  
  35. function sendSentinelAndChat(answerId, feedback) {
  36.   var link = 'http://stackoverflow.com/a/' + answerId;
  37.   GM.xmlHttpRequest({
  38.     method: 'GET',
  39.     url: 'http://samserver.bhargavrao.com:8000/napi/api/feedback/' + answerId,
  40.     onload: function (samserverResponse) {
  41.       if (samserverResponse.status !== 200) {
  42.         alert('Error while reporting: status ' + samserverResponse.status);
  43.         return;
  44.       }
  45.       var samserverJson = JSON.parse(samserverResponse.responseText);
  46.       if (samserverJson.items[0] != null) {
  47.         sendChatMessage('@Natty feedback ' + link + ' ' + feedback, answerId);
  48.       } else if (feedback === 'tp') {
  49.         sendChatMessage('@Natty report ' + link, answerId);
  50.       }
  51.     },
  52.     onerror: function (samserverResponse) {
  53.       alert('Error while reporting: ' + samserverResponse.responseText);
  54.     }
  55.   });
  56. }
  57.  
  58. function sendRequest(event) {
  59.   var messageJSON;
  60.   try {
  61.     messageJSON = JSON.parse(event.data);
  62.   } catch (zError) { }
  63.   if (!messageJSON) return;
  64.   if (messageJSON[0] == 'postHrefReportNatty') {
  65.       $.get('//api.stackexchange.com/2.2/posts/'+messageJSON[1]+'?site=stackoverflow&key=qhq7Mdy8)4lSXLCjrzQFaQ((&filter=!3tz1WbZYQxC_IUm7Z', function(aRes) {
  66.       // post is deleted, just report it (it can only be an answer since VLQ-flaggable question are only from review, thus not deleted), otherwise, check that it is really an answer and then its date
  67.       if (aRes.items.length === 0) {
  68.         sendSentinelAndChat(messageJSON[1], messageJSON[2]);
  69.       } else if (aRes.items[0]['post_type'] === 'answer') {
  70.         var answerDate = aRes.items[0]['creation_date'];
  71.         var currentDate = Date.now() / 1000;
  72.         // only do something when answer was less than 30 days ago, after which Natty reports age away
  73.         if (Math.round((currentDate - answerDate) / (24 * 60 * 60)) <= 30) {
  74.           $.get('//api.stackexchange.com/2.2/answers/'+messageJSON[1]+'/questions?site=stackoverflow&key=qhq7Mdy8)4lSXLCjrzQFaQ((&filter=!)8aBxR_Gih*BsCr', function(qRes) {
  75.             var questionDate = qRes.items[0]['creation_date'];
  76.             // only do something when answer was posted at least 30 days after the question
  77.             if (Math.round((answerDate - questionDate) / (24 * 60 * 60)) >= 30) {
  78.               sendSentinelAndChat(messageJSON[1], messageJSON[2]);
  79.             } else {
  80.                 $('[data-answerid="' + messageJSON[1] + '"] a.report-natty-link').addClass('natty-reported').html('Not a late answer.');
  81.             }
  82.           });
  83.         } else {
  84.             $('[data-answerid="' + messageJSON[1] + '"] a.report-natty-link').addClass('natty-reported').html('Answer too old.');
  85.         }
  86.       }
  87.     });
  88.   }
  89. };
  90.  
  91. window.addEventListener('message', sendRequest, false);
  92.  
  93. const ScriptToInject = function() {
  94.   function addXHRListener(callback) {
  95.     let open = XMLHttpRequest.prototype.open;
  96.     XMLHttpRequest.prototype.open = function() {
  97.       this.addEventListener('load', callback.bind(null, this), false);
  98.       open.apply(this, arguments);
  99.     };
  100.   };
  101.  
  102.   function reportToNatty(e) {
  103.     e.preventDefault();
  104.     var $this = $(this);
  105.     if ($this.closest('a.natty-reported').length > 0) return false;
  106.     var postId = $this.closest('div.post-menu').find('a.short-link').attr('id').split('-')[2];
  107.     var feedback = $this.text();
  108.     window.postMessage(JSON.stringify(['postHrefReportNatty', postId, feedback]), "*");
  109.   }
  110.    
  111.   function shortcutClicked(e) {
  112.    
  113.     var comments = {
  114.       'link-only':
  115.         'A link to a solution is welcome, but please ensure your answer is useful without it: ' +
  116.         '[add context around the link](//meta.stackexchange.com/a/8259) so your fellow users will ' +
  117.         'have some idea what it is and why it’s there, then quote the most relevant part of the ' +
  118.         'page you\'re linking to in case the target page is unavailable. ' +
  119.         '[Answers that are little more than a link may be deleted.](//stackoverflow.com/help/deleted-answers)',
  120.       'naa <50':
  121.         'This does not provide an answer to the question. You can [search for similar questions](//stackoverflow.com/search), ' +
  122.         'or refer to the related and linked questions on the right-hand side of the page to find an answer. ' +
  123.         'If you have a related but different question, [ask a new question](//stackoverflow.com/questions/ask), ' +
  124.         'and include a link to this one to help provide context. ' +
  125.         'See: [Ask questions, get answers, no distractions](//stackoverflow.com/tour)',
  126.       'naa >50':
  127.         'This post doesn\'t look like an attempt to answer this question. Every post here is expected to be ' +
  128.         'an explicit attempt to *answer* this question; if you have a critique or need a clarification of ' +
  129.         'the question or another answer, you can [post a comment](//stackoverflow.com/help/privileges/comment) ' +
  130.         '(like this one) directly below it. Please remove this answer and create either a comment or a new question. ' +
  131.         'See: [Ask questions, get answers, no distractions](//stackoverflow.com/tour)',
  132.       'thanks <15':
  133.         'Please don\'t add _"thanks"_ as answers. They don\'t actually provide an answer to the question, ' +
  134.         'and can be perceived as noise by its future visitors. Once you [earn](http://meta.stackoverflow.com/q/146472) ' +
  135.         'enough [reputation](http://stackoverflow.com/help/whats-reputation), you will gain privileges to ' +
  136.         '[upvote answers](http://stackoverflow.com/help/privileges/vote-up) you like. This way future visitors of the question ' +
  137.         'will see a higher vote count on that answer, and the answerer will also be rewarded with reputation points. ' +
  138.         'See [Why is voting important](http://stackoverflow.com/help/why-vote).',
  139.       'thanks >15':
  140.         'Please don\'t add _"thanks"_ as answers. They don\'t actually provide an answer to the question, ' +
  141.         'and can be perceived as noise by its future visitors. ' +
  142.         'Instead, [upvote answers](http://stackoverflow.com/help/privileges/vote-up) you like. This way future visitors of the question ' +
  143.         'will see a higher vote count on that answer, and the answerer will also be rewarded with reputation points. ' +
  144.         'See [Why is voting important](http://stackoverflow.com/help/why-vote).',
  145.       'me too':
  146.         'Please don\'t add *"Me too"* as answers. It doesn\'t actually provide an answer to the question. ' +
  147.         'If you have a different but related question, then [ask](//$SITEURL$/questions/ask) it ' +
  148.         '(reference this one if it will help provide context). If you\'re interested in this specific question, ' +
  149.         'you can [upvote](//stackoverflow.com/help/privileges/vote-up) it, leave a [comment](//stackoverflow.com/help/privileges/comment), ' +
  150.         'or start a [bounty](//stackoverflow.com/help/privileges/set-bounties) ' +
  151.         'once you have enough [reputation](//stackoverflow.com/help/whats-reputation).',
  152.       'lib':
  153.         'Please don\'t just post some tool or library as an answer. At least demonstrate [how it solves the problem](http://meta.stackoverflow.com/a/251605) in the answer itself.'
  154.     };
  155.      
  156.     e.preventDefault();
  157.     var postID = $(this).closest('div.post-menu').find('a.short-link').attr('id').split('-')[2];
  158.     var whichFeedback = $(this).text();
  159.    
  160.     //flag the post (and report to Natty)
  161.     if (whichFeedback == 'link-only' || whichFeedback == 'lib') {
  162.       $.post('//stackoverflow.com/flags/posts/' + postID + '/add/PostLowQuality', {'fkey': StackExchange.options.user.fkey, 'otherText': ''},
  163.         function (response) {
  164.           if (!response['Success']) {
  165.             alert('Post could not be flagged VLQ');
  166.           }
  167.         });
  168.     } else {
  169.       $.post('//stackoverflow.com/flags/posts/' + postID + '/add/AnswerNotAnAnswer', {'fkey': StackExchange.options.user.fkey, 'otherText': ''});
  170.     }
  171.  
  172.     //add a comment
  173.     $.get('//api.stackexchange.com/2.2/answers/'+postID+'?site=stackoverflow&key=qhq7Mdy8)4lSXLCjrzQFaQ((', function(aRes) {
  174.       if (aRes.items.length === 0) {
  175.         // Post deleted, nothing to do
  176.         return;
  177.       }
  178.       if (aRes.items[0]['user_type'] == 'does_not_exist') {
  179.         // User deleted, no comment needed
  180.         return;
  181.       }
  182.       if (whichFeedback == 'naa') {
  183.         // Pick the correct comment to post
  184.         if (aRes.items[0]['owner']['reputation'] < 50) {
  185.           whichFeedback = 'naa <50';
  186.         } else {
  187.           whichFeedback = 'naa >50';
  188.         }
  189.       }
  190.       if (whichFeedback == 'thanks') {
  191.         if (aRes.items[0]['owner']['reputation'] < 15) {
  192.           whichFeedback = 'thanks <15';
  193.         } else {
  194.           whichFeedback = 'thanks >15';
  195.         }
  196.       }
  197.       var comment = comments[whichFeedback];
  198.       $.post('//stackoverflow.com/posts/' + postID + '/comments', {'fkey': StackExchange.options.user.fkey, 'comment': comment},
  199.         function(data, textStatus, jqXHR) {
  200.           var commentUI = StackExchange.comments.uiForPost($('#comments-' + postID));
  201.           commentUI.addShow(true, false);
  202.           commentUI.showComments(data, null, false, true);
  203.           $(document).trigger('comment', postID);
  204.         });
  205.     });
  206.   }
  207.  
  208.   function handleAnswers(postId) {
  209.     var $posts;
  210.     if(!postId) {
  211.       $posts = $('.answer .post-menu');
  212.     } else {
  213.       $posts = $('[data-answerid="' + postId + '"] .post-menu');
  214.     }
  215.     $posts.each(function() {
  216.       var $this = $(this);
  217.       $this.append($('<span>').attr('class', 'lsep').html('|'));
  218.       var $dropdown = $('<dl>').css({ 'margin': '0', 'z-index': '1', 'position': 'absolute', 'white-space': 'nowrap', 'background': '#FFF', 'padding': '2px', 'border': '1px solid #9fa6ad', 'box-shadow': '0 2px 4px rgba(36,39,41,0.3)', 'cursor': 'default' }).hide();
  219.       $.each(['tp', 'fp', 'ne'], function(i, val) { $dropdown.append($('<dd>').append($('<a>').css({ 'display': 'block', 'margin-top': '3px', 'width': 'auto' }).click(reportToNatty).text(val))); });
  220.       $dropdown.append($('<hr>').css({'margin-bottom': '6.5px'}));
  221.       $.each(['link-only', 'naa', 'lib', 'thanks'], function(i, val) { $dropdown.append($('<dd>').append($('<a>').css({ 'display': 'block', 'margin-top': '3px', 'width': 'auto' }).click(shortcutClicked).text(val))); });
  222.       $this.append($('<a>').attr('class', 'report-natty-link').html('Natty').hover(function() { $dropdown.toggle(); }).append($dropdown));
  223.     });
  224.   };
  225.  
  226.   addXHRListener(function(xhr) {
  227.     if (/ajax-load-realtime/.test(xhr.responseURL)) {
  228.       let matches = /answer" data-answerid="(\d+)/.exec(xhr.responseText);
  229.       if (matches !== null) {
  230.         handleAnswers(matches[1]);
  231.       }
  232.     }
  233.   });
  234.  
  235.   //Flags
  236.   addXHRListener(function(xhr) {
  237.     let matches = /flags\/posts\/(\d+)\/add\/(AnswerNotAnAnswer|PostLowQuality)/.exec(xhr.responseURL);
  238.     if (matches !== null && xhr.status === 200) {
  239.       window.postMessage(JSON.stringify(['postHrefReportNatty', matches[1], 'tp']), "*");
  240.     }
  241.   });
  242.  
  243.   //LQPRQ
  244.   addXHRListener(function(xhr) {
  245.     let matches = /(\d+)\/recommend-delete/.exec(xhr.responseURL);
  246.     if (matches !== null && xhr.status === 200) {
  247.       window.postMessage(JSON.stringify(['postHrefReportNatty', matches[1], 'tp']), "*");
  248.     }
  249.   });
  250.  
  251.   $(document).ready(function() {
  252.     handleAnswers();
  253.   });
  254. };
  255.  
  256. const ScriptToInjectNode = document.createElement('script');
  257. document.body.appendChild(ScriptToInjectNode);
  258.  
  259. const ScriptToInjectContent = document.createTextNode('(' + ScriptToInject.toString() + ')()');
  260. ScriptToInjectNode.appendChild(ScriptToInjectContent);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement