-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathredirect-patroller.js
More file actions
executable file
·291 lines (268 loc) · 9.23 KB
/
redirect-patroller.js
File metadata and controls
executable file
·291 lines (268 loc) · 9.23 KB
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#!/usr/bin/env node
// Dependencies.
const argv = require('minimist')(process.argv.slice(2));
const MWBot = require('mwbot');
const mysql = require('mysql');
const util = require('util');
const credentials = require('./redirect_credentials'); // Load credentials from config.
const apiUrl = 'https://en.wikipedia.org/w/api.php';
const database = 'enwiki_p';
const reportPage = 'User:DannyS712 bot III/Redirects.json';
const editSummary = 'Redirects to patrol (bot)';
/**
* Log a message to stdout prepended with a timestamp.
* @param {String} message
*/
function log(message) {
const datestamp = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
console.log(`${datestamp}: ${message}`);
}
/**
* Connect to the replicas.
* @returns {Connection} A new MySQL connection.
*/
function getReplicaConnection() {
log('Establishing connection to the replicas (redirect patroller)');
const connection = mysql.createConnection({
host: credentials.db_host,
port: credentials.db_port,
user: credentials.db_user,
password: credentials.db_password,
database: credentials.db_database
});
connection.connect();
return connection;
}
/**
* Query the replicas to possible redirects for patrolling
* @returns {Array} Result of query.
*/
async function getRecentRedirects() {
const connection = getReplicaConnection();
log('Running query to fetch unpatrolled redirects');
const sql = `
SELECT
page_id AS 'pageid',
page_title AS 'title',
ptrpt_value AS 'target',
actor_name AS 'creator'
FROM
${database}.page
JOIN ${database}.pagetriage_page ON page_id = ptrp_page_id
JOIN ${database}.pagetriage_page_tags ON ptrp_page_id = ptrpt_page_id
JOIN ${database}.revision ON page_latest = rev_id
JOIN ${database}.actor ON rev_actor = actor_id
WHERE
ptrp_reviewed = 0
AND ptrpt_tag_id = 9
AND page_namespace = 0
AND page_is_redirect = 1`;
// Make database query synchronous.
const fn = util.promisify(connection.query).bind(connection);
return await fn(sql);
}
/**
* Retrieve bot instance
* @returns MWBot
*/
async function getBot() {
// Login to the bot.
log(`Logging in to bot account`);
const bot = new MWBot({apiUrl});
await bot.loginGetEditToken({
apiUrl,
username: credentials.username,
password: credentials.password
});
return bot;
}
/**
* Update the report with the given content.
* @param {String} content
* @param {MWBot} bot
* @returns {Promise<void>}
*/
async function updateReport(content, bot) {
// Edit the page.
log(`Writing to [[${reportPage}]]`);
await bot.edit(reportPage, content, editSummary).catch(err => {
const error = err.response && err.response.error ? err.response.error.code : 'Unknown';
log(`Failed to write to page: ${error}`);
});
}
/**
* Get a list of users to automatically patrol
* @param {MWBot} bot
* @returns {Array} users to patrol
*/
async function getPatrollableUsers( bot ) {
log('Fetching users to patrol');
const result = await bot.request( {
action: 'query',
prop: 'revisions',
titles: 'Wikipedia:New pages patrol/Redirect autopatrol list',
rvslots: '*',
rvprop: 'content'
} );
//console.log( result );
const pagecontent = result.query.pages['62534307'].revisions[0].slots.main['*'];
//console.log( pagecontent );
let listContent = '';
if ( pagecontent.indexOf('<!-- DannyS712 bot III: autopatrol list start -->') !== -1 ) {
listContent = pagecontent.substring(
pagecontent.indexOf('<!-- DannyS712 bot III: autopatrol list start -->') + 50,
pagecontent.indexOf('<!-- DannyS712 bot III: autopatrol list end -->') - 1
);
} else {
listContent = pagecontent.substring(
pagecontent.indexOf('<!-- DannyS712 bot III: whitelist start -->') + 44,
pagecontent.indexOf('<!-- DannyS712 bot III: whitelist end -->') - 1
)
}
const users = listContent.split('\n').map(u => u.replace(/^\* {{user2\|(.*?)}}/, '$1'));
console.log( users );
return users;
}
/**
* Patrol the redirects
* @param {int} pageid
* @param {MWBot} bot
* @returns {Promise<void>}
*/
async function patrolRedirect( pageid, bot ) {
// Patrol the redirect.
log(`Patrolling ${pageid}`);
await bot.request( {
action: 'pagetriageaction',
pageid: pageid,
reviewed: 1,
token: bot.editToken
} ).then( response => {
console.log( response );
} ).catch(err => {
const error = err.response && err.response.error ? err.response.error.code : 'Unknown';
log(`Failed to patrol page: ${error}`);
});
}
/**
* Patrol the redirects
* @param {Array} redirects to patrol
* @param {MWBot} bot
* @returns {bool} true
*/
async function patrolRedirects( redirects, bot ) {
console.log( redirects );
for (var lll = 0; lll < redirects.length; lll++) {
await patrolRedirect( redirects[lll].pageid, bot );
}
return true;
}
/**
* Filter redirects to only include those that can be patrolled
* @param {Array} redirects all redirects
* @param {Array} usersToPatrol users to patrol automatically
* @param {bool} logAll whether everything should be logged
* @returns {Array} redirects that can be patrolled
*/
async function getPatrollableRedirects( redirects, usersToPatrol, logAll ) {
var patrollable = [];
var title, target, user, reason;
for ( var iii = 0; iii < redirects.length; iii++ ) {
title = redirects[iii].title.toString().replace( /_/g, ' ');
target = redirects[iii].target.toString().replace( /REDIRECT\s*/i, '' );
user = redirects[iii].creator.toString();
reason = shouldPatrol( title, target, user, usersToPatrol );
if ( reason !== false ) {
patrollable.push( {
pageid: parseInt( redirects[iii].pageid ),
title: title,
target: target,
user: user,
reason: reason,
} );
if ( logAll ) {
log( title + ' -> ' + target + ' created by ' + user + ' - true' );
}
} else if ( logAll ) {
log( title + ' -> ' + target + ' created by ' + user + ' - false' );
}
}
return patrollable;
}
/**
* Determine if a specific redirect should be patrolled
* @param {String} title redirect title
* @param {String} target redirect target
* @param {String} user redirect creator
* @param {Array} usersToPatrol users to automatically patrol
* @returns {String|false} Reason for patrolling, or false for not patrolling
*/
function shouldPatrol( title, target, user, usersToPatrol ) {
if (checkAutopatrol( user, usersToPatrol )) return 'Autopatrol redirect';
if (target === title.replace( / \(disambiguation\)/i, '')) return 'Rule A';
if (comparePages( target, title )) return 'Rule B';
if (comparePages( target + 's', title ) ) return 'Rule C';
if (comparePages( target + 'es', title ) ) return 'Rule C';
if (comparePages( target.replace( /[’'‘ʻ]/g, '\'' ), title.replace( /[’'‘ʻ]/g, '\'' ) ) ) return 'Rule D';
if (comparePages( target, title.replace( /(\w*), (\w*)/, '$2 $1' ) ) ) return 'Rule E';
if (comparePages( target, 'List of ' + title ) ) return 'Rule F';
if (comparePages( target.replace( /[ -]/g, '' ), title.replace( /[ -]/g, '' ) ) ) return 'Rule G';
if (comparePages( target.replace( / vs?\.? /g, 'v.' ), title.replace( / vs?\.? /g, 'v.' ) ) ) return 'Rule H';
if (comparePages( target.replace( /^The /, '' ), title.replace( /^The /g, '' ) ) ) return 'Rule I';
if (comparePages( target.replace( /[-‒–—―]/g, '-' ), title.replace( /[-‒–—―]/g, '-' ) ) ) return 'Rule J';
if (comparePages( target.replace( /SC/g, 'S.C.' ), title.replace( /SC/g, 'S.C.' ) ) ) return 'K';
if (comparePages( target.replace( /FC/g, 'F.C.' ), title.replace( /FC/g, 'F.C.' ) ) ) return 'K';
if (comparePages( target.replace( /UK/g, 'U.K.' ), title.replace( /UK/g, 'U.K.' ) ) ) return 'K';
if (comparePages( target.replace( /&/g, 'and' ), title.replace( /&/g, 'and' ) ) ) return 'K';
if (comparePages( target.replace( /USA/g, 'U.S.A.' ), title.replace( /USA/g, 'U.S.A.' ) ) ) return 'K';
if (comparePages( target.replace( /US/g, 'U.S.' ), title.replace( /US/g, 'U.S.' ) ) ) return 'K';
return false;
}
/**
* Determine if a redirect was created by an "autopatrolled" user
* @param {String} user redirect creator
* @param {Array} usersToPatrol users to automatically patrol
* @returns {bool} if the redirect should be patrolled based on creator
*/
function checkAutopatrol( user, usersToPatrol ) {
if ( usersToPatrol.indexOf( user ) > -1 ) {
log( `Autopatrolling redirect created by ${user}`)
return true;
}
return false;
}
/**
* Compare a redirect's title and target
* @param {String} target
* @param {String} title
* @returns {bool} if the redirect should be patrolled
*/
function comparePages( target, title ) {
var comparison = target.localeCompare( title, 'en', {sensitivity: 'base'} );
if (comparison === 0) return true;
return false;
}
/**
* Entry point for the bot task.
* @returns {Promise<void>}
*/
async function main() {
const results = await getRecentRedirects();
const bot = await getBot();
const usersToPatrol = await getPatrollableUsers( bot );
//console.log( results );
const patrollable = await getPatrollableRedirects( results, usersToPatrol, argv.log );
console.log( 'patrollable', patrollable );
const patrollableAsString = await JSON.stringify( patrollable );
console.log( 'as string:', patrollableAsString );
await updateReport( patrollableAsString, bot );
if (argv.dry) {
// Dry mode.
console.log(patrollableAsString);
} else {
await patrolRedirects(patrollable, bot);
}
log('Task complete!');
process.exit();
}
main().catch(console.error);