{"size": 13110, "ext": "js", "max_stars_repo_path": "dist/mod.js", "max_stars_repo_name": "pkuh6/ph-rescue", "max_stars_repo_stars_event_min_datetime": null, "max_stars_repo_stars_event_max_datetime": null, "max_issues_repo_path": "dist/mod.js", "max_issues_repo_name": "pkuh6/ph-rescue", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "dist/mod.js", "max_forks_repo_name": "pkuh6/ph-rescue", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": null, "max_issues_count": null, "max_forks_count": null, "avg_line_length": 28.4381778742, "max_line_length": 126, "alphanum_fraction": 0.4887109077}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.main = void 0;
const init_1 = require("./init");
const playwright_chromium_1 = require("playwright-chromium");
const cli_tools_1 = require("@ddu6/cli-tools");
const clit = new cli_tools_1.CLIT(__dirname, init_1.config);
let unlocking = false;
async function get(path, params = {}) {
    const result = await clit.request(`https://${init_1.config.host}/phs/${path}`, { params });
    if (typeof result === 'number') {
        if (result === 408) {
            clit.out('Timeout');
        }
        return result;
    }
    const { status, body } = result;
    if (status !== 200) {
        return status;
    }
    try {
        return JSON.parse(body);
    }
    catch (err) {
        if (err instanceof Error) {
            clit.out(err);
        }
    }
    return 500;
}
async function unlock() {
    if (unlocking) {
        return;
    }
    unlocking = true;
    try {
        const browser = await playwright_chromium_1.chromium.launch({
            proxy: init_1.config.recaptchaProxy === 'http://host' || init_1.config.recaptchaProxy.length === 0 ? undefined : {
                server: init_1.config.recaptchaProxy
            }
        });
        const context = await browser.newContext({ storageState: {
                cookies: [],
                origins: [{
                        origin: 'https://pkuhelper.pku.edu.cn',
                        localStorage: [{
                                name: 'TOKEN',
                                value: init_1.config.token
                            }]
                    }]
            } });
        const page = await context.newPage();
        await page.goto('https://pkuhelper.pku.edu.cn/hole', { timeout: init_1.config.unlockSleep * 1000 });
        await clit.sleep(init_1.config.unlockSleep);
        await browser.close();
    }
    catch (err) {
        if (err instanceof Error) {
            clit.out(err);
        }
    }
    unlocking = false;
}
function prettyTimestamp(stamp) {
    const date = new Date(stamp * 1000);
    return [
        date.getHours(),
        date.getMinutes(),
        date.getSeconds(),
    ]
        .map(val => val.toString().padStart(2, '0'))
        .join(':')
        + ' '
        + [
            date.getMonth() + 1,
            date.getDate(),
            date.getFullYear(),
        ]
            .map(val => val.toString().padStart(2, '0'))
            .join('/');
}
async function basicallyGetComments(id) {
    return await get(`cs${id}`, {
        update: '',
        token: init_1.config.token,
        password: init_1.config.password
    });
}
async function basicallyGetLocalComments(id) {
    return await get(`local/cs${id}`, {
        token: init_1.config.token,
        password: init_1.config.password
    });
}
async function basicallyGetHole(id) {
    return await get(`h${id}`, {
        update: '',
        token: init_1.config.token,
        password: init_1.config.password
    });
}
async function basicallyGetPage(p) {
    return await get(`p${p}`, {
        update: '',
        key: '',
        token: init_1.config.token,
        password: init_1.config.password
    });
}
async function basicallyGetLocalPage(p, s, e) {
    return await get(`local/p${p}`, {
        key: '',
        s,
        e,
        token: init_1.config.token,
        password: init_1.config.password,
    });
}
async function basicallyUpdateHole(localData) {
    const result = await basicallyGetHole(Number(localData.pid));
    if (result === 401) {
        return 401;
    }
    if (result === 403) {
        return 403;
    }
    if (result === 503) {
        return 503;
    }
    if (result === 404) {
        return 404;
    }
    if (typeof result === 'number') {
        return 500;
    }
    const reply = Number(result.reply);
    const deltaComments = reply - Number(localData.reply);
    const deltaLikes = Number(result.likenum) - Number(localData.likenum);
    if (deltaComments > 0
        || deltaLikes !== 0) {
        clit.log(`h${localData.pid} updated by ${deltaComments} comments and ${deltaLikes} likes`);
    }
    return await updateComments(Number(localData.pid), reply);
}
async function getLocalPage(p, s, e) {
    for (let i = 0; i < init_1.config.errLimit; i++) {
        if (unlocking) {
            await clit.sleep(init_1.config.recaptchaSleep);
            continue;
        }
        const result = await basicallyGetLocalPage(p, s, e);
        if (result === 503) {
            clit.out('503');
            await clit.sleep(init_1.config.congestionSleep);
            continue;
        }
        if (result === 500) {
            clit.out('500');
            await clit.sleep(init_1.config.errSleep);
            continue;
        }
        if (result === 401) {
            return 401;
        }
        if (result === 403) {
            return 403;
        }
        if (typeof result === 'number') {
            return 500;
        }
        return result;
    }
    return 500;
}
async function getLocalPages(s, e) {
    const data = [];
    for (let p = 1;; p++) {
        const result = await getLocalPage(p, s, e);
        if (result === 401) {
            return 401;
        }
        if (result === 403) {
            return 403;
        }
        if (result === 500) {
            return 500;
        }
        data.push(...result);
        if (result.length < 50) {
            break;
        }
    }
    return data;
}
async function basicallyUpdateComments(id, reply) {
    if (reply === 0) {
        return 200;
    }
    if (reply > 0) {
        const result = await basicallyGetLocalComments(id);
        if (result === 401) {
            return 401;
        }
        if (result === 403) {
            return 403;
        }
        if (result === 503) {
            return 503;
        }
        if (typeof result === 'number') {
            return 500;
        }
        if (result.length >= reply) {
            return 200;
        }
    }
    const result = await basicallyGetComments(id);
    if (result === 423) {
        return 423;
    }
    if (result === 401) {
        return 401;
    }
    if (result === 403) {
        return 403;
    }
    if (result === 503) {
        return 503;
    }
    if (result === 404) {
        return 404;
    }
    if (typeof result === 'number') {
        return 500;
    }
    for (const { text } of result) {
        if (typeof text === 'string' && text.startsWith('[Helper]')) {
            return 423;
        }
    }
    if (result.length > 0) {
        const cid = Math.max(...result.map(val => Number(val.cid)));
        const timestamp = Math.max(...result.map(val => Number(val.timestamp)));
        clit.log(`cs${id} updated to c${cid} which is in ${prettyTimestamp(timestamp)}`);
    }
    return 200;
}
async function updateComments(id, reply) {
    for (let i = 0; i < init_1.config.errLimit; i++) {
        if (unlocking) {
            await clit.sleep(init_1.config.recaptchaSleep);
            continue;
        }
        const result = await basicallyUpdateComments(id, reply);
        if (result === 503) {
            clit.out('503');
            await clit.sleep(init_1.config.congestionSleep);
            continue;
        }
        if (result === 500) {
            clit.out('500');
            await clit.sleep(init_1.config.errSleep);
            continue;
        }
        if (result === 423) {
            clit.out('423');
            if (init_1.config.autoUnlock) {
                await unlock();
            }
            await clit.sleep(init_1.config.recaptchaSleep);
            continue;
        }
        if (result === 401) {
            return 401;
        }
        if (result === 403) {
            return 403;
        }
        return 200;
    }
    return 500;
}
async function updateHole(localData) {
    for (let i = 0; i < init_1.config.errLimit; i++) {
        if (unlocking) {
            await clit.sleep(init_1.config.recaptchaSleep);
            continue;
        }
        const result = await basicallyUpdateHole(localData);
        if (result === 503) {
            clit.out('503');
            await clit.sleep(init_1.config.congestionSleep);
            continue;
        }
        if (result === 500) {
            clit.out('500');
            await clit.sleep(init_1.config.errSleep);
            continue;
        }
        if (result === 401) {
            return 401;
        }
        if (result === 403) {
            return 403;
        }
        clit.log(`${localData.pid} checked`);
        return 200;
    }
    return 500;
}
async function basicallyUpdatePage(p) {
    const result = await basicallyGetPage(p);
    if (result === 401) {
        return 401;
    }
    if (result === 403) {
        return 403;
    }
    if (result === 503) {
        return 503;
    }
    if (typeof result === 'number') {
        return 500;
    }
    if (result.find(val => val.text !== '为保障树洞信息安全, 请前往树洞网页版进行人机验证, pkuhelper.pku.edu.cn/hole') === undefined) {
        return 423;
    }
    return {
        maxTime: Number(result[0].timestamp),
        minTime: Number(result[result.length - 1].timestamp)
    };
}
async function updatePage(p) {
    for (let i = 0; i < init_1.config.errLimit; i++) {
        if (unlocking) {
            await clit.sleep(init_1.config.recaptchaSleep);
            continue;
        }
        const result = await basicallyUpdatePage(p);
        if (result === 503) {
            clit.out('503');
            await clit.sleep(init_1.config.congestionSleep);
            continue;
        }
        if (result === 500) {
            clit.out('500');
            await clit.sleep(init_1.config.errSleep);
            continue;
        }
        if (result === 423) {
            clit.out('423');
            if (init_1.config.autoUnlock) {
                await unlock();
            }
            await clit.sleep(init_1.config.recaptchaSleep);
            continue;
        }
        if (result === 401) {
            return 401;
        }
        if (result === 403) {
            return 403;
        }
        return result;
    }
    return 500;
}
async function updatePages(lastMaxTime, span) {
    let maxTime = lastMaxTime;
    let minTime = lastMaxTime;
    for (let p = 1; p <= 100 && minTime + span >= lastMaxTime; p++) {
        const result = await updatePage(p);
        if (result === 401) {
            return 401;
        }
        if (result === 403) {
            return 403;
        }
        if (result === 500) {
            return 500;
        }
        if (p === 1) {
            maxTime = result.maxTime;
        }
        minTime = result.minTime;
    }
    return { maxTime };
}
async function rescueHoles() {
    let maxTime = Math.floor(Date.now() / 1000) - init_1.config.restartDuration;
    while (true) {
        const result = await updatePages(maxTime, 0);
        if (result === 401) {
            clit.out('401');
            return;
        }
        if (result === 403) {
            clit.out('403');
            return;
        }
        if (result === 500) {
            clit.out(`Fail to rescue holes after ${prettyTimestamp(maxTime)}`);
        }
        else {
            clit.log(`Holes after ${prettyTimestamp(maxTime)} rescued`);
            maxTime = result.maxTime;
        }
        await clit.sleep(init_1.config.rescueInterval);
    }
}
async function updateHoles() {
    let last = Math.floor(Date.now() / 1000) - init_1.config.updateInterval - init_1.config.restartDuration;
    while (true) {
        const now = Math.floor(Date.now() / 1000);
        for (const span of init_1.config.updateSpans) {
            const e = now - span;
            const s = last - span;
            normal: {
                const result = await getLocalPages(s.toString(), e.toString());
                if (result === 401) {
                    clit.out('401');
                    return;
                }
                if (result === 403) {
                    clit.out('403');
                    return;
                }
                if (result === 500) {
                    break normal;
                }
                for (const data of result) {
                    const result = await updateHole(data);
                    if (result === 401) {
                        clit.out('401');
                        return;
                    }
                    if (result === 403) {
                        clit.out('403');
                        return;
                    }
                    if (result === 500) {
                        break normal;
                    }
                }
                clit.log(`Holes between ${prettyTimestamp(s)} and ${prettyTimestamp(e)} updated under span ${span}`);
                continue;
            }
            clit.out(`Fail to update holes between ${prettyTimestamp(s)} and ${prettyTimestamp(e)}`);
        }
        last = now;
        await clit.sleep(init_1.config.updateInterval);
    }
}
async function main() {
    await Promise.all([
        rescueHoles(),
        updateHoles()
    ]);
}
exports.main = main;
