-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathVotingFraudDetector.user.js
108 lines (89 loc) · 3.42 KB
/
VotingFraudDetector.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// ==UserScript==
// @name Voting fraud detector
// @homepage https://github.com/kamil-tekiela/userscripts
// @version 1.0
// @description Adds a button unders user card to check for serial voting in the user's reputation history
// @author Dharman
// @match *://stackoverflow.com/questions/*
// @grant GM_xmlhttpRequest
// ==/UserScript==
(function($) {
'use strict';
initButton();
const API_KEY = 'gS)WzUg0j7Q5ZVEBB5Onkw((';
async function checkUser() {
var repHistory = [];
var $this = $(this);
var userId = $('.user-details a', $this.closest('.user-info')).attr('href').match("^/users/(\\d+)/")[1];
$this.addClass('is-loading').off();
// Call API endpoint
let page = 1;
while(page <= 10) {
var response = await getHistory(userId, page);
repHistory = (repHistory || []).concat(response.items);
if(!response.has_more){
break;
}
if(response.backoff) {
console.log('Backoff: ' + response.backoff);
await sleep(response.backoff);
}
// Don't check more than 6 months
let lastEl = repHistory[repHistory.length - 1];
if(new Date(lastEl.creation_date * 1000) < subMonths(new Date(), 6)) {
break;
}
page++;
}
let lastVote = null;
let secondLastVote = null;
let occurences = [];
let total = 0;
for (const historyItem of repHistory){
// only upvotes
if(historyItem.reputation_history_type === "post_upvoted"){
// If within 2 minutes of last vote
if(lastVote && lastVote - historyItem.creation_date < 120) {
let dateUTC = new Date(historyItem.creation_date * 1000)
let arrKey = dateUTC.getUTCFullYear() +'-'+ (dateUTC.getUTCMonth()+1) +'-'+ dateUTC.getUTCDate();
(occurences[arrKey] = occurences[arrKey] || []).push(historyItem);
total++;
}
lastVote = secondLastVote;
secondLastVote = historyItem.creation_date;
}
}
$this.removeClass('is-loading');
if(Object.keys(occurences).length > 1 && total>5){
$this.text('Fraud!').addClass('s-btn__danger');
} else {
$this.text('Ok');
}
console.log(occurences);
}
function getHistory (userId, page=1) {
return new Promise(resolve => {
GM_xmlhttpRequest({
method: 'GET',
url: 'https://api.stackexchange.com/2.2/users/'+ userId +'/reputation-history?page='+page+'&pagesize=100&site=stackoverflow&key=' + API_KEY,
onload: function(data) {
resolve(JSON.parse(data.responseText));
}
});
});
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function subMonths(date, months) {
date.setMonth(date.getMonth() - months);
return date;
}
async function initButton(){
$('.user-info').filter(function( index ) {
return $(".user-details a", this ).length === 1;
}).append(
$('<button/>', {'class': 's-btn s-btn__filled'}).text('check').on('click', checkUser)
);
}
})(jQuery);