-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathuncategorize-afc.js
More file actions
executable file
·141 lines (124 loc) · 4.19 KB
/
uncategorize-afc.js
File metadata and controls
executable file
·141 lines (124 loc) · 4.19 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
#!/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('./credentials'); // Load credentials from config.
const apiUrl = 'https://en.wikipedia.org/w/api.php';
const database = 'enwiki_p';
const editSummary = 'Task 3: Disable the categories on this page while it is still a draft, per [[WP:DRAFTNOCAT]]/[[WP:USERNOCAT]]';
/**
* 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 (AfC)');
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 get the categorized drafts.
* @returns {Array} Result of query.
*/
async function getCategorizedDrafts() {
const connection = getReplicaConnection();
log('Running query to fetch categorized afc submissions');
const sql = `
SELECT page.page_id AS ID, page.page_namespace AS ns, page.page_title AS title FROM categorylinks
JOIN page ON page_id = cl_from
WHERE cl_to = 'AfC_submissions_with_categories'
AND page_namespace in (2, 118)
AND page_is_redirect = 0`;
// Make database query synchronous.
const fn = util.promisify(connection.query).bind(connection);
return await fn(sql);
}
/**
* Remove the categories of a page
* @param {MWBot} bot
* @param {Object} row
* @param {Bool} dryRun
* @returns {Promise<void>|void}
*/
async function uncategorizePage( bot, row, dryRun ) {
const ns = parseInt( row.ns );
const nsTitlePrefix = ( ns === 2 ) ? 'User:' : 'Draft:';
const title = nsTitlePrefix + row.title;
const pageID = parseInt( row.ID );
const queryResult = await bot.read( title, true ); // true = don't follow redirects
const content = queryResult.query.pages[ pageID ].revisions[ 0 ][ '*' ];
// Ensure that draft categories, and categories in {{Draft categories}}, don't match
// Supported redirects: {{Draftcat}}, {{Draft cats}}, and {{Draft Categories}}
var newContent = content;
let toPutBackAll = [];
while ( true ) {
const ignoredCategories = newContent.match(/{{Draft(?: categories| cats|cat)\|[^{}]+}}/i);
const toPutBack = ( ignoredCategories && ignoredCategories[0] ) || '';
if (toPutBack === '') {
break;
}
toPutBackAll.push( toPutBack );
newContent = newContent.replace(/{{Draft(?: categories| cats|cat)\|[^{}]+}}/i, 'PUTTHEIGNOREDCATEGORIESBACKHERE');
}
newContent = newContent.replace( /(((\[\[Category:(?!(?:Draft|.*?drafts\]\]))[^\]]+\]\])\n?)+)/gi, '{{Draft categories|\n$1}}\n' );
while ( toPutBackAll.length !== 0 ) {
newContent = newContent.replace( 'PUTTHEIGNOREDCATEGORIESBACKHERE', toPutBackAll.shift() );
}
if ( dryRun ) {
console.log( title, content, newContent );
return;
} else {
return await bot.update( title, newContent, editSummary, { minor: true } );
}
}
/**
* Create and return a bot instance
* @returns {MWBot}
*/
async function botLogin() {
// 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;
}
/**
* Entry point for the bot task.
* @returns {Promise<void>}
*/
async function main() {
const bot = await botLogin();
const drafts = await getCategorizedDrafts();
var dry;
if ( argv.dry ) {
dry = true;
} else {
dry = false
}
for ( var iii = 0; iii < drafts.length; iii++ ) {
await uncategorizePage( bot, drafts[iii], dry )
}
log('Task complete!');
process.exit();
}
main().catch(console.error);