mirror of
https://github.com/FoxxMD/context-mod.git
synced 2026-01-13 07:37:53 -05:00
* If web client does not have credentials (or operator) then redirect login to form to fill them in * Write to config location on form completion
243 lines
10 KiB
TypeScript
243 lines
10 KiB
TypeScript
import winston from 'winston';
|
|
import 'winston-daily-rotate-file';
|
|
import dayjs from 'dayjs';
|
|
import utc from 'dayjs/plugin/utc.js';
|
|
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
|
import tz from 'dayjs/plugin/timezone';
|
|
import dduration from 'dayjs/plugin/duration.js';
|
|
import relTime from 'dayjs/plugin/relativeTime.js';
|
|
import sameafter from 'dayjs/plugin/isSameOrAfter.js';
|
|
import samebefore from 'dayjs/plugin/isSameOrBefore.js';
|
|
import weekOfYear from 'dayjs/plugin/weekOfYear.js';
|
|
import {Manager} from "./Subreddit/Manager";
|
|
import {Command, Argument} from 'commander';
|
|
|
|
import {
|
|
addOptions,
|
|
checks,
|
|
getUniversalCLIOptions,
|
|
getUniversalWebOptions,
|
|
operatorConfig
|
|
} from "./Utils/CommandConfig";
|
|
import {App} from "./App";
|
|
import apiServer from './Web/Server/server';
|
|
import clientServer from './Web/Client';
|
|
import Submission from "snoowrap/dist/objects/Submission";
|
|
import {COMMENT_URL_ID, parseLinkIdentifier, SUBMISSION_URL_ID} from "./util";
|
|
import LoggedError from "./Utils/LoggedError";
|
|
import {buildOperatorConfigWithDefaults, parseOperatorConfigFromSources} from "./ConfigBuilder";
|
|
import {getLogger, initLogger} from "./Utils/loggerFactory";
|
|
import Bot from "./Bot";
|
|
import {isScopeError} from "./Utils/Errors";
|
|
import {nanoid} from "nanoid";
|
|
|
|
dayjs.extend(utc);
|
|
dayjs.extend(dduration);
|
|
dayjs.extend(relTime);
|
|
dayjs.extend(sameafter);
|
|
dayjs.extend(samebefore);
|
|
dayjs.extend(tz);
|
|
dayjs.extend(advancedFormat);
|
|
dayjs.extend(weekOfYear);
|
|
|
|
const commentReg = parseLinkIdentifier([COMMENT_URL_ID]);
|
|
const submissionReg = parseLinkIdentifier([SUBMISSION_URL_ID]);
|
|
|
|
const preRunCmd = new Command();
|
|
preRunCmd.addOption(operatorConfig);
|
|
preRunCmd.allowUnknownOption();
|
|
|
|
const program = new Command();
|
|
|
|
(async function () {
|
|
let app: App;
|
|
// let errorReason: string | undefined;
|
|
// process.on('SIGTERM', async () => {
|
|
// if(app !== undefined) {
|
|
// await app.onTerminate(errorReason);
|
|
// }
|
|
// process.exit(errorReason === undefined ? 0 : 1);
|
|
// });
|
|
try {
|
|
|
|
let runCommand = program
|
|
.command('run')
|
|
.addArgument(new Argument('[interface]', 'Which interface to start the bot with').choices(['client', 'server', 'all']).default(undefined, 'process.env.MODE || all'))
|
|
.description('Monitor new activities from configured subreddits.')
|
|
.allowUnknownOption();
|
|
runCommand = addOptions(runCommand, getUniversalWebOptions());
|
|
runCommand.action(async (interfaceVal, opts) => {
|
|
const args = {...opts, mode: interfaceVal};
|
|
await initLogger(args)
|
|
const [opConfig, fileConfig] = await parseOperatorConfigFromSources(args);
|
|
const config = await buildOperatorConfigWithDefaults(opConfig);
|
|
const {
|
|
mode,
|
|
} = config;
|
|
try {
|
|
if(mode === 'all' || mode === 'client') {
|
|
await clientServer({...config, fileConfig});
|
|
}
|
|
if(mode === 'all' || mode === 'server') {
|
|
await apiServer({...config, fileConfig});
|
|
}
|
|
} catch (err: any) {
|
|
throw err;
|
|
}
|
|
});
|
|
|
|
let checkCommand = program
|
|
.command('check <activityIdentifier> [type] [bot]')
|
|
.allowUnknownOption()
|
|
.description('Run check(s) on a specific activity', {
|
|
activityIdentifier: 'Either a permalink URL or the ID of the Comment or Submission',
|
|
type: `No longer used`,
|
|
bot: 'Specify the bot to try with using `bot.name` (from config) -- otherwise all bots will be built before the bot to be used can be determined'
|
|
});
|
|
checkCommand = addOptions(checkCommand, getUniversalCLIOptions());
|
|
checkCommand
|
|
.addOption(checks)
|
|
.action(async (activityIdentifier, type, botVal, commandOptions = {}) => {
|
|
await initLogger(commandOptions);
|
|
const [opConfig, fileConfig] = await parseOperatorConfigFromSources(commandOptions);
|
|
const config = await buildOperatorConfigWithDefaults(opConfig);
|
|
const {checks = []} = commandOptions;
|
|
app = new App({...config, fileConfig});
|
|
|
|
let a;
|
|
const commentId = commentReg(activityIdentifier);
|
|
if (commentId !== undefined) {
|
|
if (type !== 'submission') {
|
|
// @ts-ignore
|
|
a = await app.client.getComment(commentId);
|
|
} else {
|
|
// @ts-ignore
|
|
a = await app.client.getSubmission(submissionReg(activityIdentifier) as string);
|
|
}
|
|
}
|
|
if (a === undefined) {
|
|
const submissionId = submissionReg(activityIdentifier);
|
|
if (submissionId !== undefined) {
|
|
if (type === 'comment') {
|
|
throw new Error(`Detected URL was for a submission but type was 'comment', cannot get activity`);
|
|
} else {
|
|
// @ts-ignore
|
|
a = await app.client.getSubmission(submissionId);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (a === undefined) {
|
|
// if we get this far then probably not a URL
|
|
if (type === undefined) {
|
|
throw new Error(`activityIdentifier was not a valid Reddit URL and type was not specified`);
|
|
}
|
|
if (type === 'comment') {
|
|
// @ts-ignore
|
|
a = await app.client.getComment(activityIdentifier);
|
|
} else {
|
|
// @ts-ignore
|
|
a = await app.client.getSubmission(activityIdentifier);
|
|
}
|
|
}
|
|
|
|
// @ts-ignore
|
|
const activity = await a.fetch();
|
|
const sub = await activity.subreddit.display_name;
|
|
const logger = winston.loggers.get('app');
|
|
let bots: Bot[] = [];
|
|
if(botVal !== undefined) {
|
|
const bot = app.bots.find(x => x.botName === botVal);
|
|
if(bot === undefined) {
|
|
logger.error(`No bot named "${botVal} found"`);
|
|
} else {
|
|
bots = [bot];
|
|
}
|
|
} else {
|
|
bots = app.bots;
|
|
}
|
|
for(const b of bots) {
|
|
await b.buildManagers([sub]);
|
|
if(b.subManagers.length > 0) {
|
|
const manager = b.subManagers[0];
|
|
await manager.handleActivity(activity, {
|
|
checkNames: checks,
|
|
source: 'user',
|
|
activitySource: {
|
|
id: nanoid(16),
|
|
type: 'user',
|
|
queuedAt: dayjs(),
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
let unmodCommand = program.command('unmoderated <subreddits...>')
|
|
.description('Run checks on all unmoderated activity in the modqueue', {
|
|
subreddits: 'The list of subreddits to run on. If not specified will run on all subreddits the account has moderation access to.',
|
|
bot: 'Specify the bot to try with using `bot.name` (from config) -- otherwise all bots will be built before the bot to be used can be determined'
|
|
})
|
|
.allowUnknownOption();
|
|
unmodCommand = addOptions(unmodCommand, getUniversalCLIOptions());
|
|
unmodCommand
|
|
.addOption(checks)
|
|
.action(async (subreddits = [], botVal, opts = {}) => {
|
|
await initLogger(opts);
|
|
const [opConfig, fileConfig] = await parseOperatorConfigFromSources(opts);
|
|
const config = await buildOperatorConfigWithDefaults(opConfig);
|
|
const {checks = []} = opts;
|
|
const logger = winston.loggers.get('app');
|
|
let bots: Bot[] = [];
|
|
if(botVal !== undefined) {
|
|
const bot = app.bots.find(x => x.botName === botVal);
|
|
if(bot === undefined) {
|
|
logger.error(`No bot named "${botVal} found"`);
|
|
} else {
|
|
bots = [bot];
|
|
}
|
|
} else {
|
|
bots = app.bots;
|
|
}
|
|
for(const b of bots) {
|
|
await b.buildManagers(subreddits);
|
|
for(const manager of b.subManagers) {
|
|
const activities = await manager.subreddit.getUnmoderated();
|
|
for (const a of activities.reverse()) {
|
|
manager.firehose.push({
|
|
activity: a,
|
|
options: {
|
|
checkNames: checks,
|
|
source: 'user',
|
|
activitySource: {
|
|
id: nanoid(16),
|
|
type: 'user',
|
|
queuedAt: dayjs(),
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
await program.parseAsync();
|
|
|
|
} catch (err: any) {
|
|
if (!err.logged && !(err instanceof LoggedError)) {
|
|
const logger = winston.loggers.has('app') ? winston.loggers.get('app') : winston.loggers.get('init');
|
|
logger.error(err);
|
|
}
|
|
process.kill(process.pid, 'SIGTERM');
|
|
}
|
|
}());
|
|
export {RuleSetResult} from "./Common/interfaces";
|
|
export {FormattedRuleResult} from "./Common/interfaces";
|
|
export {RuleResult} from "./Common/interfaces";
|
|
export {ResultContext} from "./Common/interfaces";
|
|
export {isRuleSetResult} from "./util";
|
|
export {UserNoteCriteria} from "./Common/Infrastructure/Filters/FilterCriteria";
|
|
export {AuthorCriteria} from "./Common/Infrastructure/Filters/FilterCriteria";
|
|
export {AuthorOptions} from "./Common/Infrastructure/Filters/FilterShapes";
|