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