lovelyCARDINAL/WikiBots

View on GitHub
src/Suppress/revokeUser.js

Summary

Maintainability
A
0 mins
Test Coverage
import { env } from 'process';
import { Octokit } from '@octokit/core';
import { load } from 'js-yaml';
import moment from 'moment';
import { MediaWikiApi } from 'wiki-saikou';
import clientLogin from '../utils/clientLogin.js';
import config from '../utils/config.js';

const zhapi = new MediaWikiApi(config.zh.api, {
        headers: { 'user-agent': config.useragent },
    }),
    cmapi = new MediaWikiApi(config.cm.api, {
        headers: { 'user-agent': config.useragent },
    });

const octokit = new Octokit({ auth: env.GHP });

async function getRevokeList() {
    const pages = await (async () => {
        const { data } = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', {
            owner: 'moepad',
            repo: 'revoke-user',
            path: 'data',
        });
        return data
            .filter((item) => item.type === 'file')
            .map((item) => item.name);
    })();
    const dates = pages.map((date) => moment(date.replace('.yaml', ''), 'YYYY-MM-DD'));
    const maxDate = moment.max(dates);
    const newPage = pages[dates.indexOf(maxDate)];
    return await (async () => {
        const { data } = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', {
            owner: 'moepad',
            repo: 'revoke-user',
            path: `data/${newPage}`,
            mediaType: {
                format: 'raw',
            },
        });
        return load(data);
    })();
}

async function manageTags(operation) {
    const { data } = await zhapi.postWithToken('csrf', {
        action: 'managetags',
        operation,
        tag: 'RevokeUser',
        reason: '用户注销',
        ignorewarnings: true,
        tags: 'Bot',
    }, {
        retry: 50,
        noCache: true,
    });
    console.log(JSON.stringify(data));
}

async function deleteAvatar(user) {
    let retry = 0;
    while (retry < 15) {
        const { response: { data } } = await cmapi.request.post('/index.php', {
            title: 'Special:查看头像',
            'delete': 'true',
            user,
            reason: '用户注销',
        });
        if (data.includes('该用户没有头像。')) {
            console.log(`Successful deleted the avatar of ${user}`);
            break;
        }
        retry++;
        if (retry === 10) {
            console.warn(`Failed to delete the avatar of ${user}`);
        }
    }
}

async function deleteRights(user) {
    await cmapi.postWithToken('userrights', {
        action: 'userrights',
        user,
        remove: 'goodeditor|honoredmaintainer|techeditor|manually-confirmed|file-maintainer|extendedconfirmed',
        reason: '用户注销',
        tags: 'Bot',
    }, {
        retry: 50,
        noCache: true,
    }).then(({ data }) => console.log(JSON.stringify(data)));
}

async function deletePages(username) {
    const user = username.replaceAll('_', ' ');
    const { data: { query: { allpages } } } = await zhapi.post({
        list: 'allpages',
        apprefix: user,
        apnamespace: '2',
    }, {
        retry: 15,
    });
    const pagelist = allpages
        .map((page) => page.title)
        .filter((title) => title.startsWith(`User:${user}/`) || title === `User:${user}`);
    await Promise.all(pagelist.map(async (title) => {
        await zhapi.postWithToken('csrf', {
            action: 'delete',
            title,
            reason: '用户注销',
            tags: 'Bot|RevokeUser',
        }, {
            retry: 50,
            noCache: true,
        }).then(({ data }) => /cantedit|protected/.test(data?.errors?.[0]?.code) ? console.warn(`[[${title}]] is protected.`) : console.log(JSON.stringify(data)));
    }));
}

async function queryLogs(api, leaction, leuser) {
    const { data: { query: { logevents } } } = await api.post({
        list: 'logevents',
        leprop: 'ids|comment',
        leaction,
        leend: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(),
        leuser,
        lelimit: 'max',
    }, {
        retry: 15,
    });
    return logevents
        .filter(({ suppressed, comment }) => !suppressed && comment === '用户注销')
        .map(({ logid }) => logid);
}

async function hideLogs(api, ids) {
    await api.postWithToken('csrf', {
        action: 'revisiondelete',
        type: 'logging',
        ids,
        hide: 'content|user|comment',
        suppress: 'yes',
        reason: '用户注销',
        tags: 'Bot',
    }, {
        retry: 50,
        noCache: true,
    }).then(({ data }) => console.log(JSON.stringify(data)));
}

(async () => {
    console.log(`Start time: ${new Date().toISOString()}`);

    const userlist = await getRevokeList();

    await Promise.all([
        clientLogin(zhapi, config.cm.sbot.account, config.password),
        clientLogin(cmapi, config.cm.sbot.account, config.password),
    ]);

    await manageTags('activate');

    await Promise.all(userlist.map(async (user) => {
        await Promise.all([
            deleteAvatar(user),
            deleteRights(user),
            deletePages(user),
        ]);
    }));

    const [cmidlist, zhidlist] = await Promise.all([
        Promise.all([
            queryLogs(cmapi, 'avatar/delete', config.zh.sbot.account),
            queryLogs(cmapi, 'rights/rights', config.zh.sbot.account),
        ]).then((ids) => ids.flat()),
        queryLogs(zhapi, 'delete/delete', config.zh.sbot.account),
    ]);

    await Promise.all([
        cmidlist.length && hideLogs(cmapi, cmidlist),
        zhidlist.length && hideLogs(zhapi, zhidlist),
    ]);

    await manageTags('deactivate');

    console.log(`End time: ${new Date().toISOString()}`);
})();