3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-05-11 20:55:01 +00:00

Merge branch 'master' of github.com:ZeppelinBot/Zeppelin into feat/application-commands

This commit is contained in:
Lily Bergonzat 2024-04-15 12:39:01 +02:00
commit 0be54912c4
164 changed files with 22402 additions and 25686 deletions

9123
backend/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -4,31 +4,31 @@
"description": "",
"private": true,
"scripts": {
"watch": "cross-env NODE_ENV=development tsc-watch --onSuccess \"node start-dev.js\"",
"watch-yaml-parse-test": "cross-env NODE_ENV=development tsc-watch --onSuccess \"node dist/backend/src/yamlParseTest.js\"",
"build": "rimraf dist && tsc",
"start-bot-dev": "cross-env NODE_ENV=development node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9229 dist/backend/src/index.js",
"start-bot-dev-debug": "NODE_ENV=development DEBUG=true clinic heapprofiler --collect-only --dest .clinic-bot -- node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9229 dist/backend/src/index.js",
"start-bot-prod": "cross-env NODE_ENV=production node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --enable-source-maps --stack-trace-limit=30 dist/backend/src/index.js",
"start-bot-prod-debug": "NODE_ENV=production DEBUG=true clinic heapprofiler --collect-only --dest .clinic-bot -- node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --enable-source-maps --stack-trace-limit=30 dist/backend/src/index.js",
"watch-bot": "cross-env NODE_ENV=development tsc-watch --onSuccess \"npm run start-bot-dev\"",
"start-api-dev": "cross-env NODE_ENV=development node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9239 dist/backend/src/api/index.js",
"start-api-dev-debug": "NODE_ENV=development DEBUG=true clinic heapprofiler --collect-only --dest .clinic-api -- node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9239 dist/backend/src/api/index.js",
"start-api-prod": "cross-env NODE_ENV=production node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --enable-source-maps --stack-trace-limit=30 dist/backend/src/api/index.js",
"start-api-prod-debug": "NODE_ENV=production DEBUG=true clinic heapprofiler --collect-only --dest .clinic-api -- node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --enable-source-maps --stack-trace-limit=30 dist/backend/src/api/index.js",
"watch-api": "cross-env NODE_ENV=development tsc-watch --onSuccess \"npm run start-api-dev\"",
"typeorm": "node -r ./register-tsconfig-paths.js ./node_modules/typeorm/cli.js",
"migrate": "npm run typeorm -- migration:run -d dist/backend/src/data/dataSource.js",
"migrate-prod": "cross-env NODE_ENV=production npm run migrate",
"migrate-dev": "cross-env NODE_ENV=development npm run build && npm run migrate",
"migrate-rollback": "npm run typeorm -- migration:revert -d dist/backend/src/data/dataSource.js",
"migrate-rollback-prod": "cross-env NODE_ENV=production npm run migrate",
"migrate-rollback-dev": "cross-env NODE_ENV=development npm run build && npm run migrate",
"validate-active-configs": "cross-env NODE_ENV=development node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --enable-source-maps dist/backend/src/validateActiveConfigs.js > ../config-errors.txt",
"export-config-json-schema": "cross-env NODE_ENV=development node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --enable-source-maps dist/backend/src/exportSchemas.js > ../config-schema.json",
"watch": "tsc-watch --build --onSuccess \"node start-dev.js\"",
"watch-yaml-parse-test": "tsc-watch --build --onSuccess \"node dist/yamlParseTest.js\"",
"build": "tsc --build",
"start-bot-dev": "node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9229 dist/index.js",
"start-bot-dev-debug": "DEBUG=true clinic heapprofiler --collect-only --dest .clinic-bot -- node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9229 dist/index.js",
"start-bot-prod": "node --enable-source-maps --stack-trace-limit=30 dist/index.js",
"start-bot-prod-debug": "DEBUG=true clinic heapprofiler --collect-only --dest .clinic-bot -- node --enable-source-maps --stack-trace-limit=30 dist/index.js",
"watch-bot": "tsc-watch --build --onSuccess \"npm run start-bot-dev\"",
"start-api-dev": "node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9239 dist/api/index.js",
"start-api-dev-debug": "DEBUG=true clinic heapprofiler --collect-only --dest .clinic-api -- node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9239 dist/api/index.js",
"start-api-prod": "node --enable-source-maps --stack-trace-limit=30 dist/api/index.js",
"start-api-prod-debug": "clinic heapprofiler --collect-only --dest .clinic-api -- node --enable-source-maps --stack-trace-limit=30 dist/api/index.js",
"watch-api": "tsc-watch --build --onSuccess \"npm run start-api-dev\"",
"typeorm": "node ../node_modules/typeorm/cli.js",
"migrate": "npm run typeorm -- migration:run -d dist/data/dataSource.js",
"migrate-prod": "npm run migrate",
"migrate-dev": "npm run build && npm run migrate",
"migrate-rollback": "npm run typeorm -- migration:revert -d dist/data/dataSource.js",
"migrate-rollback-prod": "npm run migrate-rollback",
"migrate-rollback-dev": "npm run build && npm run migrate-rollback",
"validate-active-configs": "node --enable-source-maps dist/validateActiveConfigs.js > ../config-errors.txt",
"export-config-json-schema": "node --enable-source-maps dist/exportSchemas.js > ../config-schema.json",
"test": "npm run build && npm run run-tests",
"run-tests": "ava",
"test-watch": "tsc-watch --onSuccess \"npx ava\""
"test-watch": "tsc-watch --build --onSuccess \"npx ava\""
},
"dependencies": {
"@silvia-odwyer/photon-node": "^0.3.1",
@ -45,7 +45,7 @@
"fp-ts": "^2.0.1",
"humanize-duration": "^3.15.0",
"js-yaml": "^3.13.1",
"knub": "^32.0.0-next.16",
"knub": "^32.0.0-next.21",
"knub-command-manager": "^9.1.0",
"last-commit-log": "^2.1.0",
"lodash.chunk": "^4.2.0",
@ -56,7 +56,7 @@
"lodash.pick": "^4.4.0",
"moment-timezone": "^0.5.21",
"multer": "^1.4.5-lts.1",
"mysql": "^2.16.0",
"mysql2": "^3.9.3",
"parse-color": "^1.0.0",
"passport": "^0.6.0",
"passport-custom": "^1.0.5",
@ -102,7 +102,7 @@
},
"ava": {
"files": [
"dist/backend/src/**/*.test.js"
"dist/**/*.test.js"
],
"require": [
"./register-tsconfig-paths.js"

View file

@ -3,15 +3,15 @@ import moment from "moment-timezone";
import { GuildArchives } from "../data/GuildArchives";
import { notFound } from "./responses";
export function initArchives(app: express.Express) {
export function initArchives(router: express.Router) {
const archives = new GuildArchives(null);
// Legacy redirect
app.get("/spam-logs/:id", (req: Request, res: Response) => {
router.get("/spam-logs/:id", (req: Request, res: Response) => {
res.redirect("/archives/" + req.params.id);
});
app.get("/archives/:id", async (req: Request, res: Response) => {
router.get("/archives/:id", async (req: Request, res: Response) => {
const archive = await archives.find(req.params.id);
if (!archive) return notFound(res);

View file

@ -51,8 +51,8 @@ function simpleDiscordAPIRequest(bearerToken, path): Promise<any> {
});
}
export function initAuth(app: express.Express) {
app.use(passport.initialize());
export function initAuth(router: express.Router) {
router.use(passport.initialize());
passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((user, done) => done(null, user as IPassportApiUser));
@ -110,8 +110,8 @@ export function initAuth(app: express.Express) {
),
);
app.get("/auth/login", passport.authenticate("oauth2"));
app.get(
router.get("/auth/login", passport.authenticate("oauth2"));
router.get(
"/auth/oauth-callback",
passport.authenticate("oauth2", { failureRedirect: "/", session: false }),
(req: Request, res: Response) => {
@ -122,7 +122,7 @@ export function initAuth(app: express.Express) {
}
},
);
app.post("/auth/validate-key", async (req: Request, res: Response) => {
router.post("/auth/validate-key", async (req: Request, res: Response) => {
const key = req.body.key;
if (!key) {
return res.status(400).json({ error: "No key supplied" });
@ -135,14 +135,14 @@ export function initAuth(app: express.Express) {
res.json({ valid: true, userId });
});
app.post("/auth/logout", ...apiTokenAuthHandlers(), async (req: Request, res: Response) => {
router.post("/auth/logout", ...apiTokenAuthHandlers(), async (req: Request, res: Response) => {
await apiLogins.expireApiKey(req.user!.apiKey);
return ok(res);
});
// API route to refresh the given API token's expiry time
// The actual refreshing happens in the api-token passport strategy above, so we just return 200 OK here
app.post("/auth/refresh", ...apiTokenAuthHandlers(), (req, res) => {
router.post("/auth/refresh", ...apiTokenAuthHandlers(), (req, res) => {
return ok(res);
});
}

View file

@ -1,6 +1,7 @@
import express from "express";
import z from "zod";
import { guildPlugins } from "../plugins/availablePlugins";
import { guildPluginInfo } from "../plugins/pluginInfo";
import { indentLines } from "../utils";
import { notFound } from "./responses";
@ -96,30 +97,31 @@ function formatZodConfigSchema(schema: z.ZodTypeAny) {
return "unknown";
}
export function initDocs(app: express.Express) {
const docsPlugins = guildPlugins.filter((plugin) => plugin.showInDocs);
export function initDocs(router: express.Router) {
const docsPluginNames = Object.keys(guildPluginInfo).filter((k) => guildPluginInfo[k].showInDocs);
app.get("/docs/plugins", (req: express.Request, res: express.Response) => {
router.get("/docs/plugins", (req: express.Request, res: express.Response) => {
res.json(
docsPlugins.map((plugin) => {
const thinInfo = plugin.info ? { prettyName: plugin.info.prettyName, legacy: plugin.info.legacy ?? false } : {};
docsPluginNames.map((pluginName) => {
const info = guildPluginInfo[pluginName];
const thinInfo = info ? { prettyName: info.prettyName, legacy: info.legacy ?? false } : {};
return {
name: plugin.name,
name: pluginName,
info: thinInfo,
};
}),
);
});
app.get("/docs/plugins/:pluginName", (req: express.Request, res: express.Response) => {
// prettier-ignore
const plugin = docsPlugins.find(_plugin => _plugin.name === req.params.pluginName);
if (!plugin) {
router.get("/docs/plugins/:pluginName", (req: express.Request, res: express.Response) => {
const name = req.params.pluginName;
const baseInfo = guildPluginInfo[name];
if (!baseInfo) {
return notFound(res);
}
const name = plugin.name;
const info = { ...(plugin.info || {}) };
const plugin = guildPlugins.find((p) => p.name === name)!;
const info = { ...baseInfo };
delete info.configSchema;
const messageCommands = (plugin.messageCommands || []).map((cmd) => ({
@ -132,7 +134,7 @@ export function initDocs(app: express.Express) {
}));
const defaultOptions = plugin.defaultOptions || {};
const configSchema = plugin.info?.configSchema && formatZodConfigSchema(plugin.info.configSchema);
const configSchema = info.configSchema && formatZodConfigSchema(info.configSchema);
res.json({
name,

View file

@ -1,4 +1,4 @@
import { ApiPermissions } from "@shared/apiPermissions";
import { ApiPermissions } from "@zeppelinbot/shared";
import express, { Request, Response } from "express";
import { YAMLException } from "js-yaml";
import moment from "moment-timezone";

View file

@ -1,4 +1,4 @@
import { ApiPermissions } from "@shared/apiPermissions";
import { ApiPermissions } from "@zeppelinbot/shared";
import express, { Request, Response } from "express";
import moment from "moment-timezone";
import { z } from "zod";

View file

@ -3,12 +3,12 @@ import { apiTokenAuthHandlers } from "../auth";
import { initGuildsImportExportAPI } from "./importExport";
import { initGuildsMiscAPI } from "./misc";
export function initGuildsAPI(app: express.Express) {
export function initGuildsAPI(router: express.Router) {
const guildRouter = express.Router();
guildRouter.use(...apiTokenAuthHandlers());
initGuildsMiscAPI(guildRouter);
initGuildsImportExportAPI(guildRouter);
app.use("/guilds", guildRouter);
router.use("/guilds", guildRouter);
}

View file

@ -1,4 +1,4 @@
import { ApiPermissions } from "@shared/apiPermissions";
import { ApiPermissions } from "@zeppelinbot/shared";
import express, { Request, Response } from "express";
import { YAMLException } from "js-yaml";
import moment from "moment-timezone";

View file

@ -1,4 +1,4 @@
import { ApiPermissions, hasPermission, permissionArrToSet } from "@shared/apiPermissions";
import { ApiPermissions, hasPermission, permissionArrToSet } from "@zeppelinbot/shared";
import { Request, Response } from "express";
import { ApiPermissionAssignments } from "../data/ApiPermissionAssignments";
import { isStaff } from "../staff";

View file

@ -10,6 +10,8 @@ import { initGuildsAPI } from "./guilds/index";
import { clientError, error, notFound } from "./responses";
import { startBackgroundTasks } from "./tasks";
const apiPathPrefix = env.API_PATH_PREFIX || (env.NODE_ENV === "development" ? "/api" : "");
const app = express();
app.use(
@ -24,16 +26,20 @@ app.use(
);
app.use(multer().none());
const rootRouter = express.Router();
initAuth(app);
initGuildsAPI(app);
initArchives(app);
initDocs(app);
// Default route
app.get("/", (req, res) => {
rootRouter.get("/", (req, res) => {
res.json({ status: "cookies", with: "milk" });
});
app.use(apiPathPrefix, rootRouter);
// Error response
// eslint-disable-next-line @typescript-eslint/no-unused-vars
app.use((err, req, res, next) => {
@ -51,7 +57,7 @@ app.use((req, res, next) => {
return notFound(res);
});
const port = env.API_PORT;
const port = 3001;
app.listen(port, "0.0.0.0", () => console.log(`API server listening on port ${port}`)); // tslint:disable-line
startBackgroundTasks();

View file

@ -1,11 +1,10 @@
import { BaseConfig, ConfigValidationError, PluginConfigManager } from "knub";
import { BaseConfig, ConfigValidationError, GuildPluginBlueprint, PluginConfigManager } from "knub";
import { ZodError } from "zod";
import { ZeppelinPlugin } from "./plugins/ZeppelinPlugin";
import { guildPlugins } from "./plugins/availablePlugins";
import { zZeppelinGuildConfig } from "./types";
import { formatZodIssue } from "./utils/formatZodIssue";
const pluginNameToPlugin = new Map<string, ZeppelinPlugin>();
const pluginNameToPlugin = new Map<string, GuildPluginBlueprint<any, any>>();
for (const plugin of guildPlugins) {
pluginNameToPlugin.set(plugin.name, plugin);
}

View file

@ -1,4 +1,4 @@
import { ApiPermissions } from "@shared/apiPermissions";
import { ApiPermissions } from "@zeppelinbot/shared";
import { Repository } from "typeorm";
import { ApiAuditLog } from "./ApiAuditLog";
import { BaseRepository } from "./BaseRepository";

View file

@ -6,16 +6,16 @@ import { backendDir } from "../paths";
moment.tz.setDefault("UTC");
const entities = path.relative(process.cwd(), path.resolve(backendDir, "dist/backend/src/data/entities/*.js"));
const migrations = path.relative(process.cwd(), path.resolve(backendDir, "dist/backend/src/migrations/*.js"));
const entities = path.relative(process.cwd(), path.resolve(backendDir, "dist/data/entities/*.js"));
const migrations = path.relative(process.cwd(), path.resolve(backendDir, "dist/migrations/*.js"));
export const dataSource = new DataSource({
type: "mysql",
host: env.DB_HOST,
port: env.DB_PORT,
username: env.DB_USER,
password: env.DB_PASSWORD,
database: env.DB_DATABASE,
host: env.DB_HOST || "mysql",
port: env.DB_PORT || 3306,
username: env.DB_USER || "zeppelin",
password: env.DB_PASSWORD || env.DEVELOPMENT_MYSQL_PASSWORD,
database: env.DB_DATABASE || "zeppelin",
charset: "utf8mb4",
supportBigNumbers: true,
bigNumberStrings: true,

View file

@ -13,7 +13,6 @@ const envType = z.object({
DASHBOARD_URL: z.string().url(),
API_URL: z.string().url(),
API_PORT: z.preprocess((v) => Number(v), z.number().min(1).max(65535)).default(3000),
STAFF: z
.preprocess(
@ -39,22 +38,22 @@ const envType = z.object({
PHISHERMAN_API_KEY: z.string().optional(),
DOCKER_DEV_MYSQL_PASSWORD: z.string().optional(), // Included here for the DB_PASSWORD default in development
DOCKER_PROD_MYSQL_PASSWORD: z.string().optional(), // Included here for the DB_PASSWORD default in production
DB_HOST: z.string().optional(),
DB_PORT: z.preprocess((v) => Number(v), z.number()).optional(),
DB_USER: z.string().optional(),
DB_PASSWORD: z.string().optional(),
DB_DATABASE: z.string().optional(),
DB_HOST: z.string().optional().default("mysql"),
DB_PORT: z
.preprocess((v) => Number(v), z.number())
.optional()
.default(3306),
DB_USER: z.string().optional().default("zeppelin"),
DB_PASSWORD: z.string().optional(), // Default is set to DOCKER_MYSQL_PASSWORD further below
DB_DATABASE: z.string().optional().default("zeppelin"),
DEVELOPMENT_MYSQL_PASSWORD: z.string().optional(),
API_PATH_PREFIX: z.string().optional(),
DEBUG: z
.string()
.optional()
.transform((str) => str === "true"),
NODE_ENV: z.string().default("development"),
});
let toValidate = { ...process.env };
@ -65,11 +64,3 @@ if (fs.existsSync(envPath)) {
}
export const env = envType.parse(toValidate);
if (!env.DB_PASSWORD) {
if (process.env.NODE_ENV === "production" && env.DOCKER_PROD_MYSQL_PASSWORD) {
env.DB_PASSWORD = env.DOCKER_PROD_MYSQL_PASSWORD;
} else if (env.DOCKER_DEV_MYSQL_PASSWORD) {
env.DB_PASSWORD = env.DOCKER_DEV_MYSQL_PASSWORD;
}
}

View file

@ -1,13 +1,12 @@
import { z } from "zod";
import zodToJsonSchema from "zod-to-json-schema";
import { guildPlugins } from "./plugins/availablePlugins";
import { guildPluginInfo } from "./plugins/pluginInfo";
import { zZeppelinGuildConfig } from "./types";
const pluginSchemaMap = guildPlugins.reduce((map, plugin) => {
if (!plugin.info) {
return map;
const pluginSchemaMap = Object.entries(guildPluginInfo).reduce((map, [pluginName, pluginInfo]) => {
if (pluginInfo.configSchema) {
map[pluginName] = pluginInfo.configSchema;
}
map[plugin.name] = plugin.info.configSchema;
return map;
}, {});

View file

@ -164,7 +164,7 @@ if (process.env.NODE_ENV === "production") {
}
// Verify required Node.js version
const REQUIRED_NODE_VERSION = "14.0.0";
const REQUIRED_NODE_VERSION = "16.9.0";
const requiredParts = REQUIRED_NODE_VERSION.split(".").map((v) => parseInt(v, 10));
const actualVersionParts = process.versions.node.split(".").map((v) => parseInt(v, 10));
for (const [i, part] of actualVersionParts.entries()) {
@ -463,7 +463,7 @@ connect().then(async () => {
logger.info("Cleaning up before exit...");
// Force exit after 10sec
setTimeout(() => process.exit(code), 10 * SECONDS);
await bot.stop();
await bot.destroy();
await dataSource.destroy();
logger.info("Done! Exiting now.");
process.exit(code);

View file

@ -10,11 +10,13 @@ export class CreateSlowmodeTables1544877081073 implements MigrationInterface {
name: "guild_id",
type: "bigint",
unsigned: true,
isPrimary: true,
},
{
name: "channel_id",
type: "bigint",
unsigned: true,
isPrimary: true,
},
{
name: "slowmode_seconds",
@ -25,7 +27,6 @@ export class CreateSlowmodeTables1544877081073 implements MigrationInterface {
indices: [],
}),
);
await queryRunner.createPrimaryKey("slowmode_channels", ["guild_id", "channel_id"]);
await queryRunner.createTable(
new Table({
@ -35,16 +36,19 @@ export class CreateSlowmodeTables1544877081073 implements MigrationInterface {
name: "guild_id",
type: "bigint",
unsigned: true,
isPrimary: true,
},
{
name: "channel_id",
type: "bigint",
unsigned: true,
isPrimary: true,
},
{
name: "user_id",
type: "bigint",
unsigned: true,
isPrimary: true,
},
{
name: "expires_at",
@ -58,7 +62,6 @@ export class CreateSlowmodeTables1544877081073 implements MigrationInterface {
],
}),
);
await queryRunner.createPrimaryKey("slowmode_users", ["guild_id", "channel_id", "user_id"]);
}
public async down(queryRunner: QueryRunner): Promise<any> {

View file

@ -61,11 +61,13 @@ export class CreateStarboardTable1544887946307 implements MigrationInterface {
name: "starboard_id",
type: "int",
unsigned: true,
isPrimary: true,
},
{
name: "message_id",
type: "bigint",
unsigned: true,
isPrimary: true,
},
{
name: "starboard_message_id",
@ -75,7 +77,6 @@ export class CreateStarboardTable1544887946307 implements MigrationInterface {
],
}),
);
await queryRunner.createPrimaryKey("starboard_messages", ["starboard_id", "message_id"]);
}
public async down(queryRunner: QueryRunner): Promise<any> {

View file

@ -10,11 +10,13 @@ export class CreateAutoReactionsTable1547290549908 implements MigrationInterface
name: "guild_id",
type: "bigint",
unsigned: true,
isPrimary: true,
},
{
name: "channel_id",
type: "bigint",
unsigned: true,
isPrimary: true,
},
{
name: "reactions",
@ -23,7 +25,6 @@ export class CreateAutoReactionsTable1547290549908 implements MigrationInterface
],
}),
);
await queryRunner.createPrimaryKey("auto_reactions", ["guild_id", "channel_id"]);
}
public async down(queryRunner: QueryRunner): Promise<any> {

View file

@ -9,10 +9,12 @@ export class CreateDashboardUsersTable1558804449510 implements MigrationInterfac
{
name: "guild_id",
type: "bigint",
isPrimary: true,
},
{
name: "user_id",
type: "bigint",
isPrimary: true,
},
{
name: "username",
@ -28,7 +30,6 @@ export class CreateDashboardUsersTable1558804449510 implements MigrationInterfac
}),
);
await queryRunner.createPrimaryKey("dashboard_users", ["guild_id", "user_id"]);
await queryRunner.createIndex(
"dashboard_users",
new TableIndex({

View file

@ -2,14 +2,10 @@ import { MigrationInterface, QueryRunner, TableColumn, TableIndex } from "typeor
export class AddTypeAndPermissionsToApiPermissions1573158035867 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
try {
await queryRunner.dropPrimaryKey("api_permissions");
} catch {} // eslint-disable-line no-empty
const table = (await queryRunner.getTable("api_permissions"))!;
if (table.indices.length) {
await queryRunner.dropIndex("api_permissions", table.indices[0]);
}
// We can't use a TableIndex object in dropIndex directly as the table name is included in the generated index name
// and the table name has changed since the original index was created
const originalIndexName = queryRunner.connection.namingStrategy.indexName("dashboard_users", ["user_id"]);
await queryRunner.dropIndex("api_permissions", originalIndexName);
await queryRunner.addColumn(
"api_permissions",
@ -22,7 +18,11 @@ export class AddTypeAndPermissionsToApiPermissions1573158035867 implements Migra
await queryRunner.renameColumn("api_permissions", "user_id", "target_id");
await queryRunner.createPrimaryKey("api_permissions", ["guild_id", "type", "target_id"]);
await queryRunner.query(`
ALTER TABLE api_permissions
DROP PRIMARY KEY,
ADD PRIMARY KEY(\`guild_id\`, \`type\`, \`target_id\`);
`);
await queryRunner.dropColumn("api_permissions", "role");
@ -49,7 +49,12 @@ export class AddTypeAndPermissionsToApiPermissions1573158035867 implements Migra
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropIndex("api_permissions", "IDX_e06d750f13e6a4b4d3d6b847a9");
await queryRunner.dropIndex(
"api_permissions",
new TableIndex({
columnNames: ["type", "target_id"],
}),
);
await queryRunner.dropColumn("api_permissions", "permissions");
@ -62,7 +67,11 @@ export class AddTypeAndPermissionsToApiPermissions1573158035867 implements Migra
}),
);
await queryRunner.dropPrimaryKey("api_permissions");
await queryRunner.query(`
ALTER TABLE api_permissions
DROP PRIMARY KEY,
ADD PRIMARY KEY(\`guild_id\`, \`type\`);
`);
await queryRunner.renameColumn("api_permissions", "target_id", "user_id");
@ -74,7 +83,5 @@ export class AddTypeAndPermissionsToApiPermissions1573158035867 implements Migra
columnNames: ["user_id"],
}),
);
await queryRunner.createPrimaryKey("api_permissions", ["guild_id", "user_id"]);
}
}

View file

@ -2,36 +2,46 @@ import { MigrationInterface, QueryRunner, Table, TableColumn } from "typeorm";
export class MoveStarboardsToConfig1573248462469 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
// Create the new column for the channels id
const chanid_column = new TableColumn({
name: "starboard_channel_id",
type: "bigint",
unsigned: true,
});
await queryRunner.addColumn("starboard_messages", chanid_column);
// Create a new column for the channel's id
await queryRunner.addColumn(
"starboard_messages",
new TableColumn({
name: "starboard_channel_id",
type: "bigint",
unsigned: true,
}),
);
// Since we are removing the guild_id with the starboards table, we might want it here
const guid_column = new TableColumn({
name: "guild_id",
type: "bigint",
unsigned: true,
});
await queryRunner.addColumn("starboard_messages", guid_column);
await queryRunner.addColumn(
"starboard_messages",
new TableColumn({
name: "guild_id",
type: "bigint",
unsigned: true,
}),
);
// Migrate the old starboard_id to the new starboard_channel_id
await queryRunner.query(`
UPDATE starboard_messages AS sm
JOIN starboards AS sb
ON sm.starboard_id = sb.id
SET sm.starboard_channel_id = sb.channel_id, sm.guild_id = sb.guild_id;
`);
UPDATE starboard_messages AS sm
JOIN starboards AS sb
ON sm.starboard_id = sb.id
SET sm.starboard_channel_id = sb.channel_id, sm.guild_id = sb.guild_id;
`);
// Drop the starboard_id column as it is now obsolete
await queryRunner.dropColumn("starboard_messages", "starboard_id");
// Set new Primary Key
await queryRunner.dropPrimaryKey("starboard_messages");
await queryRunner.createPrimaryKey("starboard_messages", ["starboard_message_id"]);
// Finally, drop the starboards channel as it is now obsolete
await queryRunner.query(`
ALTER TABLE starboard_messages
DROP PRIMARY KEY,
ADD PRIMARY KEY(\`starboard_message_id\`);
`);
// Drop the starboard_id column as it is now obsolete
// We can't use queyrRunner.dropColumn() here because TypeORM helpfully thinks that
// starboard_id is still part of the primary key and tries to drop the PK first
await queryRunner.query("ALTER TABLE starboard_messages DROP COLUMN starboard_id");
// Finally, drop the starboards table as it is now obsolete
await queryRunner.dropTable("starboards", true);
}
@ -39,15 +49,20 @@ export class MoveStarboardsToConfig1573248462469 implements MigrationInterface {
await queryRunner.dropColumn("starboard_messages", "starboard_channel_id");
await queryRunner.dropColumn("starboard_messages", "guild_id");
const sbId = new TableColumn({
name: "starboard_id",
type: "int",
unsigned: true,
});
await queryRunner.addColumn("starboard_messages", sbId);
await queryRunner.addColumn(
"starboard_messages",
new TableColumn({
name: "starboard_id",
type: "int",
unsigned: true,
}),
);
await queryRunner.dropPrimaryKey("starboard_messages");
await queryRunner.createPrimaryKey("starboard_messages", ["starboard_id", "message_id"]);
await queryRunner.query(`
ALTER TABLE starboard_messages
DROP PRIMARY KEY,
ADD PRIMARY KEY(\`starboard_id\`, \`message_id\`);
`);
await queryRunner.createTable(
new Table({

View file

@ -12,7 +12,7 @@ import {
TextBasedChannel,
User,
} from "discord.js";
import { AnyPluginData, CommandContext, ExtendedMatchParams, GuildPluginData, helpers } from "knub";
import { AnyPluginData, BasePluginData, CommandContext, ExtendedMatchParams, GuildPluginData, helpers } from "knub";
import { isStaff } from "./staff";
import { TZeppelinKnub } from "./types";
import { Tail } from "./utils/typeUtils";
@ -135,3 +135,16 @@ export function mapToPublicFn<T extends AnyFn>(inputFn: T) {
};
};
}
type FnWithPluginData<TPluginData> = (pluginData: TPluginData, ...args: any[]) => any;
export function makePublicFn<TPluginData extends BasePluginData<any>, T extends FnWithPluginData<TPluginData>>(
pluginData: TPluginData,
fn: T,
) {
return (...args: Tail<Parameters<T>>): ReturnType<T> => {
return fn(pluginData, ...args);
};
}
// ???

View file

@ -1,9 +1,8 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { GuildLogs } from "../../data/GuildLogs";
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { AutoDeletePluginType, zAutoDeleteConfig } from "./types";
import { onMessageCreate } from "./util/onMessageCreate";
import { onMessageDelete } from "./util/onMessageDelete";
@ -16,15 +15,8 @@ const defaultOptions: PluginOptions<AutoDeletePluginType> = {
},
};
export const AutoDeletePlugin = zeppelinGuildPlugin<AutoDeletePluginType>()({
export const AutoDeletePlugin = guildPlugin<AutoDeletePluginType>()({
name: "auto_delete",
showInDocs: true,
info: {
prettyName: "Auto-delete",
description: "Allows Zeppelin to auto-delete messages from a channel after a delay",
configurationGuide: "Maximum deletion delay is currently 5 minutes",
configSchema: zAutoDeleteConfig,
},
dependencies: () => [TimeAndDatePlugin, LogsPlugin],
configParser: (input) => zAutoDeleteConfig.parse(input),

View file

@ -0,0 +1,8 @@
import { ZeppelinPluginInfo } from "../../types";
export const autoDeletePluginInfo: ZeppelinPluginInfo = {
showInDocs: true,
prettyName: "Auto-delete",
description: "Allows Zeppelin to auto-delete messages from a channel after a delay",
configurationGuide: "Maximum deletion delay is currently 5 minutes",
};

View file

@ -1,9 +1,7 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { GuildAutoReactions } from "../../data/GuildAutoReactions";
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
import { trimPluginDescription } from "../../utils";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { DisableAutoReactionsCmd } from "./commands/DisableAutoReactionsCmd";
import { NewAutoReactionsCmd } from "./commands/NewAutoReactionsCmd";
import { AddReactionsEvt } from "./events/AddReactionsEvt";
@ -23,16 +21,8 @@ const defaultOptions: PluginOptions<AutoReactionsPluginType> = {
],
};
export const AutoReactionsPlugin = zeppelinGuildPlugin<AutoReactionsPluginType>()({
export const AutoReactionsPlugin = guildPlugin<AutoReactionsPluginType>()({
name: "auto_reactions",
showInDocs: true,
info: {
prettyName: "Auto-reactions",
description: trimPluginDescription(`
Allows setting up automatic reactions to all new messages on a channel
`),
configSchema: zAutoReactionsConfig,
},
// prettier-ignore
dependencies: () => [

View file

@ -0,0 +1,10 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
export const autoReactionsInfo: ZeppelinPluginInfo = {
showInDocs: true,
prettyName: "Auto-reactions",
description: trimPluginDescription(`
Allows setting up automatic reactions to all new messages on a channel
`),
};

View file

@ -1,4 +1,4 @@
import { CooldownManager } from "knub";
import { CooldownManager, guildPlugin } from "knub";
import { Queue } from "../../Queue";
import { GuildAntiraidLevels } from "../../data/GuildAntiraidLevels";
import { GuildArchives } from "../../data/GuildArchives";
@ -15,7 +15,6 @@ import { ModActionsPlugin } from "../ModActions/ModActionsPlugin";
import { MutesPlugin } from "../Mutes/MutesPlugin";
import { PhishermanPlugin } from "../Phisherman/PhishermanPlugin";
import { RoleManagerPlugin } from "../RoleManager/RoleManagerPlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { AntiraidClearCmd } from "./commands/AntiraidClearCmd";
import { SetAntiraidCmd } from "./commands/SetAntiraidCmd";
import { ViewAntiraidCmd } from "./commands/ViewAntiraidCmd";
@ -32,7 +31,6 @@ import {
import { clearOldRecentNicknameChanges } from "./functions/clearOldNicknameChanges";
import { clearOldRecentActions } from "./functions/clearOldRecentActions";
import { clearOldRecentSpam } from "./functions/clearOldRecentSpam";
import { pluginInfo } from "./info";
import { AutomodPluginType, zAutomodConfig } from "./types";
const defaultOptions = {
@ -58,10 +56,8 @@ const defaultOptions = {
],
};
export const AutomodPlugin = zeppelinGuildPlugin<AutomodPluginType>()({
export const AutomodPlugin = guildPlugin<AutomodPluginType>()({
name: "automod",
showInDocs: true,
info: pluginInfo,
// prettier-ignore
dependencies: () => [

View file

@ -1,12 +1,12 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
import { ZeppelinGuildPluginBlueprint } from "../ZeppelinPluginBlueprint";
import { zAutomodConfig } from "./types";
export const pluginInfo: ZeppelinGuildPluginBlueprint["info"] = {
export const automodPluginInfo: ZeppelinPluginInfo = {
showInDocs: true,
prettyName: "Automod",
description: trimPluginDescription(`
Allows specifying automated actions in response to triggers. Example use cases include word filtering and spam prevention.
`),
`),
configurationGuide: trimPluginDescription(`
The automod plugin is very customizable. For a full list of available triggers, actions, and their options, see Config schema at the bottom of this page.
@ -99,6 +99,5 @@ export const pluginInfo: ZeppelinGuildPluginBlueprint["info"] = {
Bad custom status on user <@!{user.id}>:
{matchSummary}
~~~
`),
configSchema: zAutomodConfig,
`),
};

View file

@ -1,10 +1,10 @@
import { Snowflake, TextChannel } from "discord.js";
import { globalPlugin } from "knub";
import { AllowedGuilds } from "../../data/AllowedGuilds";
import { ApiPermissionAssignments } from "../../data/ApiPermissionAssignments";
import { Configs } from "../../data/Configs";
import { GuildArchives } from "../../data/GuildArchives";
import { CommonPlugin } from "../Common/CommonPlugin";
import { zeppelinGlobalPlugin } from "../ZeppelinPluginBlueprint";
import { getActiveReload, resetActiveReload } from "./activeReload";
import { AddDashboardUserCmd } from "./commands/AddDashboardUserCmd";
import { AddServerFromInviteCmd } from "./commands/AddServerFromInviteCmd";
@ -35,7 +35,7 @@ const defaultOptions = {
},
};
export const BotControlPlugin = zeppelinGlobalPlugin<BotControlPluginType>()({
export const BotControlPlugin = globalPlugin<BotControlPluginType>()({
name: "bot_control",
configParser: (input) => zBotControlConfig.parse(input),
defaultOptions,

View file

@ -1,4 +1,4 @@
import { ApiPermissions } from "@shared/apiPermissions";
import { ApiPermissions } from "@zeppelinbot/shared";
import { commandTypeHelpers as ct } from "../../../commandTypes";
import { isStaffPreFilter } from "../../../pluginUtils";
import { renderUsername } from "../../../utils";

View file

@ -1,4 +1,4 @@
import { ApiPermissions } from "@shared/apiPermissions";
import { ApiPermissions } from "@zeppelinbot/shared";
import moment from "moment-timezone";
import { commandTypeHelpers as ct } from "../../../commandTypes";
import { DBDateFormat, isGuildInvite, resolveInvite } from "../../../utils";

View file

@ -1,4 +1,4 @@
import { ApiPermissions } from "@shared/apiPermissions";
import { ApiPermissions } from "@zeppelinbot/shared";
import moment from "moment-timezone";
import { commandTypeHelpers as ct } from "../../../commandTypes";
import { isStaffPreFilter } from "../../../pluginUtils";

View file

@ -1,13 +1,10 @@
import { CaseTypes } from "../../data/CaseTypes";
import { Case } from "../../data/entities/Case";
import { guildPlugin } from "knub";
import { GuildArchives } from "../../data/GuildArchives";
import { GuildCases } from "../../data/GuildCases";
import { GuildLogs } from "../../data/GuildLogs";
import { mapToPublicFn } from "../../pluginUtils";
import { trimPluginDescription } from "../../utils";
import { makePublicFn } from "../../pluginUtils";
import { InternalPosterPlugin } from "../InternalPoster/InternalPosterPlugin";
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { createCase } from "./functions/createCase";
import { createCaseNote } from "./functions/createCaseNote";
import { getCaseEmbed } from "./functions/getCaseEmbed";
@ -16,7 +13,7 @@ import { getCaseTypeAmountForUserId } from "./functions/getCaseTypeAmountForUser
import { getRecentCasesByMod } from "./functions/getRecentCasesByMod";
import { getTotalCasesByMod } from "./functions/getTotalCasesByMod";
import { postCaseToCaseLogChannel } from "./functions/postToCaseLogChannel";
import { CaseArgs, CaseNoteArgs, CasesPluginType, zCasesConfig } from "./types";
import { CasesPluginType, zCasesConfig } from "./types";
// The `any` cast here is to prevent TypeScript from locking up from the circular dependency
function getLogsPlugin(): Promise<any> {
@ -34,51 +31,24 @@ const defaultOptions = {
},
};
export const CasesPlugin = zeppelinGuildPlugin<CasesPluginType>()({
export const CasesPlugin = guildPlugin<CasesPluginType>()({
name: "cases",
showInDocs: true,
info: {
prettyName: "Cases",
description: trimPluginDescription(`
This plugin contains basic configuration for cases created by other plugins
`),
configSchema: zCasesConfig,
},
dependencies: async () => [TimeAndDatePlugin, InternalPosterPlugin, (await getLogsPlugin()).LogsPlugin],
configParser: (input) => zCasesConfig.parse(input),
defaultOptions,
public: {
createCase(pluginData) {
return (args: CaseArgs) => {
return createCase(pluginData, args);
};
},
createCaseNote(pluginData) {
return (args: CaseNoteArgs) => {
return createCaseNote(pluginData, args);
};
},
postCaseToCaseLogChannel(pluginData) {
return (caseOrCaseId: Case | number) => {
return postCaseToCaseLogChannel(pluginData, caseOrCaseId);
};
},
getCaseTypeAmountForUserId(pluginData) {
return (userID: string, type: CaseTypes) => {
return getCaseTypeAmountForUserId(pluginData, userID, type);
};
},
getTotalCasesByMod: mapToPublicFn(getTotalCasesByMod),
getRecentCasesByMod: mapToPublicFn(getRecentCasesByMod),
getCaseEmbed: mapToPublicFn(getCaseEmbed),
getCaseSummary: mapToPublicFn(getCaseSummary),
public(pluginData) {
return {
createCase: makePublicFn(pluginData, createCase),
createCaseNote: makePublicFn(pluginData, createCaseNote),
postCaseToCaseLogChannel: makePublicFn(pluginData, postCaseToCaseLogChannel),
getCaseTypeAmountForUserId: makePublicFn(pluginData, getCaseTypeAmountForUserId),
getTotalCasesByMod: makePublicFn(pluginData, getTotalCasesByMod),
getRecentCasesByMod: makePublicFn(pluginData, getRecentCasesByMod),
getCaseEmbed: makePublicFn(pluginData, getCaseEmbed),
getCaseSummary: makePublicFn(pluginData, getCaseSummary),
};
},
afterLoad(pluginData) {

View file

@ -0,0 +1,10 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
export const casesPluginInfo: ZeppelinPluginInfo = {
showInDocs: true,
prettyName: "Cases",
description: trimPluginDescription(`
This plugin contains basic configuration for cases created by other plugins
`),
};

View file

@ -1,10 +1,8 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { GuildLogs } from "../../data/GuildLogs";
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners";
import { trimPluginDescription } from "../../utils";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { CensorPluginType, zCensorConfig } from "./types";
import { onMessageCreate } from "./util/onMessageCreate";
import { onMessageUpdate } from "./util/onMessageUpdate";
@ -43,18 +41,8 @@ const defaultOptions: PluginOptions<CensorPluginType> = {
],
};
export const CensorPlugin = zeppelinGuildPlugin<CensorPluginType>()({
export const CensorPlugin = guildPlugin<CensorPluginType>()({
name: "censor",
showInDocs: true,
info: {
prettyName: "Censor",
description: trimPluginDescription(`
Censor words, tokens, links, regex, etc.
For more advanced filtering, check out the Automod plugin!
`),
legacy: true,
configSchema: zCensorConfig,
},
dependencies: () => [LogsPlugin],
configParser: (input) => zCensorConfig.parse(input),

View file

@ -0,0 +1,12 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
export const censorPluginInfo: ZeppelinPluginInfo = {
showInDocs: true,
legacy: true,
prettyName: "Censor",
description: trimPluginDescription(`
Censor words, tokens, links, regex, etc.
For more advanced filtering, check out the Automod plugin!
`),
};

View file

@ -1,12 +1,11 @@
import { guildPlugin } from "knub";
import z from "zod";
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { ArchiveChannelCmd } from "./commands/ArchiveChannelCmd";
import { ChannelArchiverPluginType } from "./types";
export const ChannelArchiverPlugin = zeppelinGuildPlugin<ChannelArchiverPluginType>()({
export const ChannelArchiverPlugin = guildPlugin<ChannelArchiverPluginType>()({
name: "channel_archiver",
showInDocs: false,
dependencies: () => [TimeAndDatePlugin],
configParser: (input) => z.strictObject({}).parse(input),

View file

@ -1,8 +1,6 @@
import { CooldownManager } from "knub";
import { CooldownManager, guildPlugin } from "knub";
import { GuildLogs } from "../../data/GuildLogs";
import { trimPluginDescription } from "../../utils";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { VoiceStateUpdateEvt } from "./events/VoiceStateUpdateEvt";
import { CompanionChannelsPluginType, zCompanionChannelsConfig } from "./types";
@ -12,18 +10,8 @@ const defaultOptions = {
},
};
export const CompanionChannelsPlugin = zeppelinGuildPlugin<CompanionChannelsPluginType>()({
export const CompanionChannelsPlugin = guildPlugin<CompanionChannelsPluginType>()({
name: "companion_channels",
showInDocs: true,
info: {
prettyName: "Companion channels",
description: trimPluginDescription(`
Set up 'companion channels' between text and voice channels.
Once set up, any time a user joins one of the specified voice channels,
they'll get channel permissions applied to them for the text channels.
`),
configSchema: zCompanionChannelsConfig,
},
dependencies: () => [LogsPlugin],
configParser: (input) => zCompanionChannelsConfig.parse(input),

View file

@ -0,0 +1,12 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
export const companionChannelsPluginInfo: ZeppelinPluginInfo = {
showInDocs: true,
prettyName: "Companion channels",
description: trimPluginDescription(`
Set up 'companion channels' between text and voice channels.
Once set up, any time a user joins one of the specified voice channels,
they'll get channel permissions applied to them for the text channels.
`),
};

View file

@ -1,12 +1,10 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { GuildCases } from "../../data/GuildCases";
import { trimPluginDescription } from "../../utils";
import { CasesPlugin } from "../Cases/CasesPlugin";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { ModActionsPlugin } from "../ModActions/ModActionsPlugin";
import { MutesPlugin } from "../Mutes/MutesPlugin";
import { UtilityPlugin } from "../Utility/UtilityPlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { BanCmd } from "./commands/BanUserCtxCmd";
import { CleanCmd } from "./commands/CleanMessageCtxCmd";
import { ModMenuCmd } from "./commands/ModMenuUserCtxCmd";
@ -33,16 +31,8 @@ const defaultOptions: PluginOptions<ContextMenuPluginType> = {
],
};
export const ContextMenuPlugin = zeppelinGuildPlugin<ContextMenuPluginType>()({
export const ContextMenuPlugin = guildPlugin<ContextMenuPluginType>()({
name: "context_menu",
showInDocs: true,
info: {
prettyName: "Context Menus",
description: trimPluginDescription(`
This plugin provides command shortcuts via context menus
`),
configSchema: zContextMenusConfig,
},
dependencies: () => [CasesPlugin, MutesPlugin, ModActionsPlugin, LogsPlugin, UtilityPlugin],
configParser: (input) => zContextMenusConfig.parse(input),

View file

@ -0,0 +1,12 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
import { zContextMenusConfig } from "./types";
export const contextMenuPluginInfo: ZeppelinPluginInfo = {
showInDocs: false,
prettyName: "Context menu",
description: trimPluginDescription(`
This plugin provides command shortcuts via context menus
`),
configSchema: zContextMenusConfig,
};

View file

@ -1,10 +1,9 @@
import { EventEmitter } from "events";
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { GuildCounters } from "../../data/GuildCounters";
import { CounterTrigger, parseCounterConditionString } from "../../data/entities/CounterTrigger";
import { mapToPublicFn } from "../../pluginUtils";
import { makePublicFn } from "../../pluginUtils";
import { MINUTES, convertDelayStringToMS, values } from "../../utils";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { AddCounterCmd } from "./commands/AddCounterCmd";
import { CountersListCmd } from "./commands/CountersListCmd";
import { ResetAllCounterValuesCmd } from "./commands/ResetAllCounterValuesCmd";
@ -56,35 +55,23 @@ const defaultOptions: PluginOptions<CountersPluginType> = {
* A single trigger can only trigger once per user/channel/in general, depending on how specific the counter is (e.g. a per-user trigger can only trigger once per user).
* After being triggered, a trigger is "reset" if the counter value no longer matches the trigger (e.g. drops to 100 or below in the above example). After this, that trigger can be triggered again.
*/
export const CountersPlugin = zeppelinGuildPlugin<CountersPluginType>()({
export const CountersPlugin = guildPlugin<CountersPluginType>()({
name: "counters",
showInDocs: true,
info: {
prettyName: "Counters",
description:
"Keep track of per-user, per-channel, or global numbers and trigger specific actions based on this number",
configurationGuide: "See <a href='/docs/setup-guides/counters'>Counters setup guide</a>",
configSchema: zCountersConfig,
},
defaultOptions,
// TODO: Separate input and output types
configParser: (input) => zCountersConfig.parse(input),
public: {
counterExists: mapToPublicFn(counterExists),
// Change a counter's value by a relative amount, e.g. +5
changeCounterValue: mapToPublicFn(changeCounterValue),
// Set a counter's value to an absolute value
setCounterValue: mapToPublicFn(setCounterValue),
getPrettyNameForCounter: mapToPublicFn(getPrettyNameForCounter),
getPrettyNameForCounterTrigger: mapToPublicFn(getPrettyNameForCounterTrigger),
onCounterEvent: mapToPublicFn(onCounterEvent),
offCounterEvent: mapToPublicFn(offCounterEvent),
public(pluginData) {
return {
counterExists: makePublicFn(pluginData, counterExists),
changeCounterValue: makePublicFn(pluginData, changeCounterValue),
setCounterValue: makePublicFn(pluginData, setCounterValue),
getPrettyNameForCounter: makePublicFn(pluginData, getPrettyNameForCounter),
getPrettyNameForCounterTrigger: makePublicFn(pluginData, getPrettyNameForCounterTrigger),
onCounterEvent: makePublicFn(pluginData, onCounterEvent),
offCounterEvent: makePublicFn(pluginData, offCounterEvent),
};
},
// prettier-ignore

View file

@ -0,0 +1,11 @@
import { ZeppelinPluginInfo } from "../../types";
import { zCountersConfig } from "./types";
export const countersPluginInfo: ZeppelinPluginInfo = {
prettyName: "Counters",
showInDocs: true,
description:
"Keep track of per-user, per-channel, or global numbers and trigger specific actions based on this number",
configurationGuide: "See <a href='/docs/setup-guides/counters'>Counters setup guide</a>",
configSchema: zCountersConfig,
};

View file

@ -1,5 +1,5 @@
import { GuildChannel, GuildMember, User } from "discord.js";
import { guildPluginMessageCommand, parseSignature } from "knub";
import { guildPlugin, guildPluginMessageCommand, parseSignature } from "knub";
import { TSignature } from "knub-command-manager";
import { commandTypes } from "../../commandTypes";
import { TemplateSafeValueContainer, createTypedTemplateSafeValueContainer } from "../../templateFormatter";
@ -12,7 +12,6 @@ import {
userToTemplateSafeUser,
} from "../../utils/templateSafeObjects";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { runEvent } from "./functions/runEvent";
import { CustomEventsPluginType, zCustomEventsConfig } from "./types";
@ -22,9 +21,8 @@ const defaultOptions = {
},
};
export const CustomEventsPlugin = zeppelinGuildPlugin<CustomEventsPluginType>()({
export const CustomEventsPlugin = guildPlugin<CustomEventsPluginType>()({
name: "custom_events",
showInDocs: false,
dependencies: () => [LogsPlugin],
configParser: (input) => zCustomEventsConfig.parse(input),

View file

@ -0,0 +1,6 @@
import { ZeppelinPluginInfo } from "../../types";
export const customEventsPluginInfo: ZeppelinPluginInfo = {
prettyName: "Custom events",
showInDocs: false,
};

View file

@ -1,10 +1,9 @@
import { Guild } from "discord.js";
import { BasePluginType, GlobalPluginData, globalPluginEventListener } from "knub";
import { BasePluginType, GlobalPluginData, globalPlugin, globalPluginEventListener } from "knub";
import z from "zod";
import { AllowedGuilds } from "../../data/AllowedGuilds";
import { Configs } from "../../data/Configs";
import { env } from "../../env";
import { zeppelinGlobalPlugin } from "../ZeppelinPluginBlueprint";
interface GuildAccessMonitorPluginType extends BasePluginType {
state: {
@ -23,7 +22,7 @@ async function checkGuild(pluginData: GlobalPluginData<GuildAccessMonitorPluginT
/**
* Global plugin to monitor if Zeppelin is invited to a non-whitelisted server, and leave it
*/
export const GuildAccessMonitorPlugin = zeppelinGlobalPlugin<GuildAccessMonitorPluginType>()({
export const GuildAccessMonitorPlugin = globalPlugin<GuildAccessMonitorPluginType>()({
name: "guild_access_monitor",
configParser: (input) => z.strictObject({}).parse(input),

View file

@ -1,12 +1,11 @@
import { globalPlugin } from "knub";
import z from "zod";
import { Configs } from "../../data/Configs";
import { zeppelinGlobalPlugin } from "../ZeppelinPluginBlueprint";
import { reloadChangedGuilds } from "./functions/reloadChangedGuilds";
import { GuildConfigReloaderPluginType } from "./types";
export const GuildConfigReloaderPlugin = zeppelinGlobalPlugin<GuildConfigReloaderPluginType>()({
export const GuildConfigReloaderPlugin = globalPlugin<GuildConfigReloaderPluginType>()({
name: "guild_config_reloader",
showInDocs: false,
configParser: (input) => z.strictObject({}).parse(input),

View file

@ -0,0 +1,6 @@
import { ZeppelinPluginInfo } from "../../types";
export const guildConfigReloaderPluginInfo: ZeppelinPluginInfo = {
prettyName: "Guild config reloader",
showInDocs: false,
};

View file

@ -1,15 +1,13 @@
import { Guild } from "discord.js";
import { guildPluginEventListener } from "knub";
import { guildPlugin, guildPluginEventListener } from "knub";
import z from "zod";
import { AllowedGuilds } from "../../data/AllowedGuilds";
import { ApiPermissionAssignments } from "../../data/ApiPermissionAssignments";
import { MINUTES } from "../../utils";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { GuildInfoSaverPluginType } from "./types";
export const GuildInfoSaverPlugin = zeppelinGuildPlugin<GuildInfoSaverPluginType>()({
export const GuildInfoSaverPlugin = guildPlugin<GuildInfoSaverPluginType>()({
name: "guild_info_saver",
showInDocs: false,
configParser: (input) => z.strictObject({}).parse(input),

View file

@ -0,0 +1,6 @@
import { ZeppelinPluginInfo } from "../../types";
export const guildInfoSaverPluginInfo: ZeppelinPluginInfo = {
prettyName: "Guild info saver",
showInDocs: false,
};

View file

@ -1,8 +1,8 @@
import { guildPlugin } from "knub";
import z from "zod";
import { GuildMemberCache } from "../../data/GuildMemberCache";
import { mapToPublicFn } from "../../pluginUtils";
import { makePublicFn } from "../../pluginUtils";
import { SECONDS } from "../../utils";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { cancelDeletionOnMemberJoin } from "./events/cancelDeletionOnMemberJoin";
import { removeMemberCacheOnMemberLeave } from "./events/removeMemberCacheOnMemberLeave";
import { updateMemberCacheOnMemberUpdate } from "./events/updateMemberCacheOnMemberUpdate";
@ -14,9 +14,8 @@ import { GuildMemberCachePluginType } from "./types";
const PENDING_SAVE_INTERVAL = 30 * SECONDS;
export const GuildMemberCachePlugin = zeppelinGuildPlugin<GuildMemberCachePluginType>()({
export const GuildMemberCachePlugin = guildPlugin<GuildMemberCachePluginType>()({
name: "guild_member_cache",
showInDocs: false,
configParser: (input) => z.strictObject({}).parse(input),
@ -29,8 +28,10 @@ export const GuildMemberCachePlugin = zeppelinGuildPlugin<GuildMemberCachePlugin
cancelDeletionOnMemberJoin,
],
public: {
getCachedMemberData: mapToPublicFn(getCachedMemberData),
public(pluginData) {
return {
getCachedMemberData: makePublicFn(pluginData, getCachedMemberData),
};
},
beforeLoad(pluginData) {

View file

@ -0,0 +1,6 @@
import { ZeppelinPluginInfo } from "../../types";
export const guildMemberCachePluginInfo: ZeppelinPluginInfo = {
prettyName: "Guild member cache",
showInDocs: false,
};

View file

@ -1,9 +1,8 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import z from "zod";
import { Queue } from "../../Queue";
import { Webhooks } from "../../data/Webhooks";
import { mapToPublicFn } from "../../pluginUtils";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { makePublicFn } from "../../pluginUtils";
import { editMessage } from "./functions/editMessage";
import { sendMessage } from "./functions/sendMessage";
import { InternalPosterPluginType } from "./types";
@ -13,17 +12,17 @@ const defaultOptions: PluginOptions<InternalPosterPluginType> = {
overrides: [],
};
export const InternalPosterPlugin = zeppelinGuildPlugin<InternalPosterPluginType>()({
export const InternalPosterPlugin = guildPlugin<InternalPosterPluginType>()({
name: "internal_poster",
showInDocs: false,
configParser: (input) => z.strictObject({}).parse(input),
defaultOptions,
// prettier-ignore
public: {
sendMessage: mapToPublicFn(sendMessage),
editMessage: mapToPublicFn(editMessage),
public(pluginData) {
return {
sendMessage: makePublicFn(pluginData, sendMessage),
editMessage: makePublicFn(pluginData, editMessage),
};
},
async beforeLoad(pluginData) {

View file

@ -0,0 +1,6 @@
import { ZeppelinPluginInfo } from "../../types";
export const internalPosterPluginInfo: ZeppelinPluginInfo = {
prettyName: "Internal poster",
showInDocs: false,
};

View file

@ -1,8 +1,6 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { onGuildEvent } from "../../data/GuildEvents";
import { GuildVCAlerts } from "../../data/GuildVCAlerts";
import { trimPluginDescription } from "../../utils";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { FollowCmd } from "./commands/FollowCmd";
import { DeleteFollowCmd, ListFollowCmd } from "./commands/ListFollowCmd";
import { WhereCmd } from "./commands/WhereCmd";
@ -28,18 +26,8 @@ const defaultOptions: PluginOptions<LocateUserPluginType> = {
],
};
export const LocateUserPlugin = zeppelinGuildPlugin<LocateUserPluginType>()({
export const LocateUserPlugin = guildPlugin<LocateUserPluginType>()({
name: "locate_user",
showInDocs: true,
info: {
prettyName: "Locate user",
description: trimPluginDescription(`
This plugin allows users with access to the commands the following:
* Instantly receive an invite to the voice channel of a user
* Be notified as soon as a user switches or joins a voice channel
`),
configSchema: zLocateUserConfig,
},
configParser: (input) => zLocateUserConfig.parse(input),
defaultOptions,

View file

@ -0,0 +1,14 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
import { zLocateUserConfig } from "./types";
export const locateUserPluginInfo: ZeppelinPluginInfo = {
prettyName: "Locate user",
description: trimPluginDescription(`
This plugin allows users with access to the commands the following:
* Instantly receive an invite to the voice channel of a user
* Be notified as soon as a user switches or joins a voice channel
`),
configSchema: zLocateUserConfig,
showInDocs: true,
};

View file

@ -1,4 +1,4 @@
import { CooldownManager, PluginOptions } from "knub";
import { CooldownManager, PluginOptions, guildPlugin } from "knub";
import DefaultLogMessages from "../../data/DefaultLogMessages.json";
import { GuildArchives } from "../../data/GuildArchives";
import { GuildCases } from "../../data/GuildCases";
@ -6,11 +6,10 @@ import { GuildLogs } from "../../data/GuildLogs";
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
import { LogType } from "../../data/LogType";
import { logger } from "../../logger";
import { mapToPublicFn } from "../../pluginUtils";
import { makePublicFn } from "../../pluginUtils";
import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners";
import { TypedTemplateSafeValueContainer, createTypedTemplateSafeValueContainer } from "../../templateFormatter";
import { createTypedTemplateSafeValueContainer } from "../../templateFormatter";
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { LogsChannelCreateEvt, LogsChannelDeleteEvt, LogsChannelUpdateEvt } from "./events/LogsChannelModifyEvts";
import {
LogsEmojiCreateEvt,
@ -31,7 +30,7 @@ import {
import { LogsThreadCreateEvt, LogsThreadDeleteEvt, LogsThreadUpdateEvt } from "./events/LogsThreadModifyEvts";
import { LogsGuildMemberUpdateEvt } from "./events/LogsUserUpdateEvts";
import { LogsVoiceStateUpdateEvt } from "./events/LogsVoiceChannelEvts";
import { FORMAT_NO_TIMESTAMP, ILogTypeData, LogsPluginType, TLogChannel, zLogsConfig } from "./types";
import { FORMAT_NO_TIMESTAMP, LogsPluginType, zLogsConfig } from "./types";
import { getLogMessage } from "./util/getLogMessage";
import { log } from "./util/log";
import { onMessageDelete } from "./util/onMessageDelete";
@ -140,13 +139,8 @@ const defaultOptions: PluginOptions<LogsPluginType> = {
],
};
export const LogsPlugin = zeppelinGuildPlugin<LogsPluginType>()({
export const LogsPlugin = guildPlugin<LogsPluginType>()({
name: "logs",
showInDocs: true,
info: {
prettyName: "Logs",
configSchema: zLogsConfig,
},
dependencies: async () => [TimeAndDatePlugin, InternalPosterPlugin, (await getCasesPlugin()).CasesPlugin],
configParser: (input) => zLogsConfig.parse(input),
@ -178,86 +172,79 @@ export const LogsPlugin = zeppelinGuildPlugin<LogsPluginType>()({
LogsGuildMemberRoleChangeEvt,
],
public: {
getLogMessage: (pluginData) => {
return <TLogType extends keyof ILogTypeData>(
type: TLogType,
data: TypedTemplateSafeValueContainer<ILogTypeData[TLogType]>,
opts?: Pick<TLogChannel, "format" | "timestamp_format" | "include_embed_timestamp">,
) => {
return getLogMessage(pluginData, type, data, opts);
};
},
logAutomodAction: mapToPublicFn(logAutomodAction),
logBotAlert: mapToPublicFn(logBotAlert),
logCaseCreate: mapToPublicFn(logCaseCreate),
logCaseDelete: mapToPublicFn(logCaseDelete),
logCaseUpdate: mapToPublicFn(logCaseUpdate),
logCensor: mapToPublicFn(logCensor),
logChannelCreate: mapToPublicFn(logChannelCreate),
logChannelDelete: mapToPublicFn(logChannelDelete),
logChannelUpdate: mapToPublicFn(logChannelUpdate),
logClean: mapToPublicFn(logClean),
logEmojiCreate: mapToPublicFn(logEmojiCreate),
logEmojiDelete: mapToPublicFn(logEmojiDelete),
logEmojiUpdate: mapToPublicFn(logEmojiUpdate),
logMassBan: mapToPublicFn(logMassBan),
logMassMute: mapToPublicFn(logMassMute),
logMassUnban: mapToPublicFn(logMassUnban),
logMemberBan: mapToPublicFn(logMemberBan),
logMemberForceban: mapToPublicFn(logMemberForceban),
logMemberJoin: mapToPublicFn(logMemberJoin),
logMemberJoinWithPriorRecords: mapToPublicFn(logMemberJoinWithPriorRecords),
logMemberKick: mapToPublicFn(logMemberKick),
logMemberLeave: mapToPublicFn(logMemberLeave),
logMemberMute: mapToPublicFn(logMemberMute),
logMemberMuteExpired: mapToPublicFn(logMemberMuteExpired),
logMemberMuteRejoin: mapToPublicFn(logMemberMuteRejoin),
logMemberNickChange: mapToPublicFn(logMemberNickChange),
logMemberNote: mapToPublicFn(logMemberNote),
logMemberRestore: mapToPublicFn(logMemberRestore),
logMemberRoleAdd: mapToPublicFn(logMemberRoleAdd),
logMemberRoleChanges: mapToPublicFn(logMemberRoleChanges),
logMemberRoleRemove: mapToPublicFn(logMemberRoleRemove),
logMemberTimedBan: mapToPublicFn(logMemberTimedBan),
logMemberTimedMute: mapToPublicFn(logMemberTimedMute),
logMemberTimedUnban: mapToPublicFn(logMemberTimedUnban),
logMemberTimedUnmute: mapToPublicFn(logMemberTimedUnmute),
logMemberUnban: mapToPublicFn(logMemberUnban),
logMemberUnmute: mapToPublicFn(logMemberUnmute),
logMemberWarn: mapToPublicFn(logMemberWarn),
logMessageDelete: mapToPublicFn(logMessageDelete),
logMessageDeleteAuto: mapToPublicFn(logMessageDeleteAuto),
logMessageDeleteBare: mapToPublicFn(logMessageDeleteBare),
logMessageDeleteBulk: mapToPublicFn(logMessageDeleteBulk),
logMessageEdit: mapToPublicFn(logMessageEdit),
logMessageSpamDetected: mapToPublicFn(logMessageSpamDetected),
logOtherSpamDetected: mapToPublicFn(logOtherSpamDetected),
logPostedScheduledMessage: mapToPublicFn(logPostedScheduledMessage),
logRepeatedMessage: mapToPublicFn(logRepeatedMessage),
logRoleCreate: mapToPublicFn(logRoleCreate),
logRoleDelete: mapToPublicFn(logRoleDelete),
logRoleUpdate: mapToPublicFn(logRoleUpdate),
logScheduledMessage: mapToPublicFn(logScheduledMessage),
logScheduledRepeatedMessage: mapToPublicFn(logScheduledRepeatedMessage),
logSetAntiraidAuto: mapToPublicFn(logSetAntiraidAuto),
logSetAntiraidUser: mapToPublicFn(logSetAntiraidUser),
logStageInstanceCreate: mapToPublicFn(logStageInstanceCreate),
logStageInstanceDelete: mapToPublicFn(logStageInstanceDelete),
logStageInstanceUpdate: mapToPublicFn(logStageInstanceUpdate),
logStickerCreate: mapToPublicFn(logStickerCreate),
logStickerDelete: mapToPublicFn(logStickerDelete),
logStickerUpdate: mapToPublicFn(logStickerUpdate),
logThreadCreate: mapToPublicFn(logThreadCreate),
logThreadDelete: mapToPublicFn(logThreadDelete),
logThreadUpdate: mapToPublicFn(logThreadUpdate),
logVoiceChannelForceDisconnect: mapToPublicFn(logVoiceChannelForceDisconnect),
logVoiceChannelForceMove: mapToPublicFn(logVoiceChannelForceMove),
logVoiceChannelJoin: mapToPublicFn(logVoiceChannelJoin),
logVoiceChannelLeave: mapToPublicFn(logVoiceChannelLeave),
logVoiceChannelMove: mapToPublicFn(logVoiceChannelMove),
logDmFailed: mapToPublicFn(logDmFailed),
public(pluginData) {
return {
getLogMessage: makePublicFn(pluginData, getLogMessage),
logAutomodAction: makePublicFn(pluginData, logAutomodAction),
logBotAlert: makePublicFn(pluginData, logBotAlert),
logCaseCreate: makePublicFn(pluginData, logCaseCreate),
logCaseDelete: makePublicFn(pluginData, logCaseDelete),
logCaseUpdate: makePublicFn(pluginData, logCaseUpdate),
logCensor: makePublicFn(pluginData, logCensor),
logChannelCreate: makePublicFn(pluginData, logChannelCreate),
logChannelDelete: makePublicFn(pluginData, logChannelDelete),
logChannelUpdate: makePublicFn(pluginData, logChannelUpdate),
logClean: makePublicFn(pluginData, logClean),
logEmojiCreate: makePublicFn(pluginData, logEmojiCreate),
logEmojiDelete: makePublicFn(pluginData, logEmojiDelete),
logEmojiUpdate: makePublicFn(pluginData, logEmojiUpdate),
logMassBan: makePublicFn(pluginData, logMassBan),
logMassMute: makePublicFn(pluginData, logMassMute),
logMassUnban: makePublicFn(pluginData, logMassUnban),
logMemberBan: makePublicFn(pluginData, logMemberBan),
logMemberForceban: makePublicFn(pluginData, logMemberForceban),
logMemberJoin: makePublicFn(pluginData, logMemberJoin),
logMemberJoinWithPriorRecords: makePublicFn(pluginData, logMemberJoinWithPriorRecords),
logMemberKick: makePublicFn(pluginData, logMemberKick),
logMemberLeave: makePublicFn(pluginData, logMemberLeave),
logMemberMute: makePublicFn(pluginData, logMemberMute),
logMemberMuteExpired: makePublicFn(pluginData, logMemberMuteExpired),
logMemberMuteRejoin: makePublicFn(pluginData, logMemberMuteRejoin),
logMemberNickChange: makePublicFn(pluginData, logMemberNickChange),
logMemberNote: makePublicFn(pluginData, logMemberNote),
logMemberRestore: makePublicFn(pluginData, logMemberRestore),
logMemberRoleAdd: makePublicFn(pluginData, logMemberRoleAdd),
logMemberRoleChanges: makePublicFn(pluginData, logMemberRoleChanges),
logMemberRoleRemove: makePublicFn(pluginData, logMemberRoleRemove),
logMemberTimedBan: makePublicFn(pluginData, logMemberTimedBan),
logMemberTimedMute: makePublicFn(pluginData, logMemberTimedMute),
logMemberTimedUnban: makePublicFn(pluginData, logMemberTimedUnban),
logMemberTimedUnmute: makePublicFn(pluginData, logMemberTimedUnmute),
logMemberUnban: makePublicFn(pluginData, logMemberUnban),
logMemberUnmute: makePublicFn(pluginData, logMemberUnmute),
logMemberWarn: makePublicFn(pluginData, logMemberWarn),
logMessageDelete: makePublicFn(pluginData, logMessageDelete),
logMessageDeleteAuto: makePublicFn(pluginData, logMessageDeleteAuto),
logMessageDeleteBare: makePublicFn(pluginData, logMessageDeleteBare),
logMessageDeleteBulk: makePublicFn(pluginData, logMessageDeleteBulk),
logMessageEdit: makePublicFn(pluginData, logMessageEdit),
logMessageSpamDetected: makePublicFn(pluginData, logMessageSpamDetected),
logOtherSpamDetected: makePublicFn(pluginData, logOtherSpamDetected),
logPostedScheduledMessage: makePublicFn(pluginData, logPostedScheduledMessage),
logRepeatedMessage: makePublicFn(pluginData, logRepeatedMessage),
logRoleCreate: makePublicFn(pluginData, logRoleCreate),
logRoleDelete: makePublicFn(pluginData, logRoleDelete),
logRoleUpdate: makePublicFn(pluginData, logRoleUpdate),
logScheduledMessage: makePublicFn(pluginData, logScheduledMessage),
logScheduledRepeatedMessage: makePublicFn(pluginData, logScheduledRepeatedMessage),
logSetAntiraidAuto: makePublicFn(pluginData, logSetAntiraidAuto),
logSetAntiraidUser: makePublicFn(pluginData, logSetAntiraidUser),
logStageInstanceCreate: makePublicFn(pluginData, logStageInstanceCreate),
logStageInstanceDelete: makePublicFn(pluginData, logStageInstanceDelete),
logStageInstanceUpdate: makePublicFn(pluginData, logStageInstanceUpdate),
logStickerCreate: makePublicFn(pluginData, logStickerCreate),
logStickerDelete: makePublicFn(pluginData, logStickerDelete),
logStickerUpdate: makePublicFn(pluginData, logStickerUpdate),
logThreadCreate: makePublicFn(pluginData, logThreadCreate),
logThreadDelete: makePublicFn(pluginData, logThreadDelete),
logThreadUpdate: makePublicFn(pluginData, logThreadUpdate),
logVoiceChannelForceDisconnect: makePublicFn(pluginData, logVoiceChannelForceDisconnect),
logVoiceChannelForceMove: makePublicFn(pluginData, logVoiceChannelForceMove),
logVoiceChannelJoin: makePublicFn(pluginData, logVoiceChannelJoin),
logVoiceChannelLeave: makePublicFn(pluginData, logVoiceChannelLeave),
logVoiceChannelMove: makePublicFn(pluginData, logVoiceChannelMove),
logDmFailed: makePublicFn(pluginData, logDmFailed),
};
},
beforeLoad(pluginData) {

View file

@ -0,0 +1,8 @@
import { ZeppelinPluginInfo } from "../../types";
import { zLogsConfig } from "./types";
export const logsPluginInfo: ZeppelinPluginInfo = {
prettyName: "Logs",
configSchema: zLogsConfig,
showInDocs: true,
};

View file

@ -1,6 +1,5 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { SaveMessagesToDBCmd } from "./commands/SaveMessagesToDB";
import { SavePinsToDBCmd } from "./commands/SavePinsToDB";
import { MessageCreateEvt, MessageDeleteBulkEvt, MessageDeleteEvt, MessageUpdateEvt } from "./events/SaveMessagesEvts";
@ -20,9 +19,8 @@ const defaultOptions: PluginOptions<MessageSaverPluginType> = {
],
};
export const MessageSaverPlugin = zeppelinGuildPlugin<MessageSaverPluginType>()({
export const MessageSaverPlugin = guildPlugin<MessageSaverPluginType>()({
name: "message_saver",
showInDocs: false,
configParser: (input) => zMessageSaverConfig.parse(input),
defaultOptions,

View file

@ -0,0 +1,6 @@
import { ZeppelinPluginInfo } from "../../types";
export const messageSaverPluginInfo: ZeppelinPluginInfo = {
prettyName: "Message saver",
showInDocs: false,
};

View file

@ -1,18 +1,18 @@
import { GuildMember, Message, Snowflake } from "discord.js";
import { Message } from "discord.js";
import { EventEmitter } from "events";
import { guildPlugin } from "knub";
import { Queue } from "../../Queue";
import { GuildCases } from "../../data/GuildCases";
import { onGuildEvent } from "../../data/GuildEvents";
import { GuildLogs } from "../../data/GuildLogs";
import { GuildMutes } from "../../data/GuildMutes";
import { GuildTempbans } from "../../data/GuildTempbans";
import { mapToPublicFn } from "../../pluginUtils";
import { MINUTES, trimPluginDescription } from "../../utils";
import { makePublicFn, mapToPublicFn } from "../../pluginUtils";
import { MINUTES } from "../../utils";
import { CasesPlugin } from "../Cases/CasesPlugin";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { MutesPlugin } from "../Mutes/MutesPlugin";
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { AddCaseMsgCmd } from "./commands/addcase/AddCaseMsgCmd";
import { AddCaseSlashCmd } from "./commands/addcase/AddCaseSlashCmd";
import { BanMsgCmd } from "./commands/ban/BanMsgCmd";
@ -71,15 +71,7 @@ import { offModActionsEvent } from "./functions/offModActionsEvent";
import { onModActionsEvent } from "./functions/onModActionsEvent";
import { updateCase } from "./functions/updateCase";
import { warnMember } from "./functions/warnMember";
import {
AttachmentLinkReactionType,
BanOptions,
KickOptions,
ModActionsPluginType,
WarnOptions,
modActionsSlashGroup,
zModActionsConfig,
} from "./types";
import { AttachmentLinkReactionType, ModActionsPluginType, modActionsSlashGroup, zModActionsConfig } from "./types";
const defaultOptions = {
config: {
@ -147,16 +139,8 @@ const defaultOptions = {
],
};
export const ModActionsPlugin = zeppelinGuildPlugin<ModActionsPluginType>()({
export const ModActionsPlugin = guildPlugin<ModActionsPluginType>()({
name: "mod_actions",
showInDocs: true,
info: {
prettyName: "Mod actions",
description: trimPluginDescription(`
This plugin contains the 'typical' mod actions such as warning, muting, kicking, banning, etc.
`),
configSchema: zModActionsConfig,
},
dependencies: () => [TimeAndDatePlugin, CasesPlugin, MutesPlugin, LogsPlugin],
configParser: (input) => zModActionsConfig.parse(input),
@ -218,66 +202,21 @@ export const ModActionsPlugin = zeppelinGuildPlugin<ModActionsPluginType>()({
DeleteCaseMsgCmd,
],
public: {
warnMember(pluginData) {
return (member: GuildMember, reason: string, reasonWithAttachments: string, warnOptions?: WarnOptions) => {
return warnMember(pluginData, member, reason, reasonWithAttachments, warnOptions);
};
},
kickMember(pluginData) {
return (member: GuildMember, reason: string, reasonWithAttachments: string, kickOptions?: KickOptions) => {
kickMember(pluginData, member, reason, reasonWithAttachments, kickOptions);
};
},
banUserId(pluginData) {
return (
userId: string,
reason?: string,
reasonWithAttachments?: string,
banOptions?: BanOptions,
banTime?: number,
) => {
return banUserId(pluginData, userId, reason, reasonWithAttachments, banOptions, banTime);
};
},
updateCase(pluginData) {
return (msg: Message, caseNumber: number | null, note: string) => {
updateCase(pluginData, msg, msg.author, caseNumber ?? undefined, note, [...msg.attachments.values()]);
};
},
hasNotePermission(pluginData) {
return (member: GuildMember, channelId: Snowflake) => {
return hasNotePermission(pluginData, member, channelId);
};
},
hasWarnPermission(pluginData) {
return (member: GuildMember, channelId: Snowflake) => {
return hasWarnPermission(pluginData, member, channelId);
};
},
hasMutePermission(pluginData) {
return (member: GuildMember, channelId: Snowflake) => {
return hasMutePermission(pluginData, member, channelId);
};
},
hasBanPermission(pluginData) {
return (member: GuildMember, channelId: Snowflake) => {
return hasBanPermission(pluginData, member, channelId);
};
},
on: mapToPublicFn(onModActionsEvent),
off: mapToPublicFn(offModActionsEvent),
getEventEmitter(pluginData) {
return () => pluginData.state.events;
},
public(pluginData) {
return {
warnMember: makePublicFn(pluginData, warnMember),
kickMember: makePublicFn(pluginData, kickMember),
banUserId: makePublicFn(pluginData, banUserId),
updateCase: (msg: Message, caseNumber: number | null, note: string) =>
updateCase(pluginData, msg, msg.author, caseNumber ?? undefined, note, [...msg.attachments.values()]),
hasNotePermission: makePublicFn(pluginData, hasNotePermission),
hasWarnPermission: makePublicFn(pluginData, hasWarnPermission),
hasMutePermission: makePublicFn(pluginData, hasMutePermission),
hasBanPermission: makePublicFn(pluginData, hasBanPermission),
on: mapToPublicFn(onModActionsEvent),
off: mapToPublicFn(offModActionsEvent),
getEventEmitter: () => pluginData.state.events,
};
},
beforeLoad(pluginData) {

View file

@ -0,0 +1,12 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
import { zModActionsConfig } from "./types";
export const modActionsPluginInfo: ZeppelinPluginInfo = {
prettyName: "Mod actions",
showInDocs: true,
description: trimPluginDescription(`
This plugin contains the 'typical' mod actions such as warning, muting, kicking, banning, etc.
`),
configSchema: zModActionsConfig,
};

View file

@ -1,15 +1,15 @@
import { GuildMember, Snowflake } from "discord.js";
import { EventEmitter } from "events";
import { guildPlugin } from "knub";
import { GuildArchives } from "../../data/GuildArchives";
import { GuildCases } from "../../data/GuildCases";
import { onGuildEvent } from "../../data/GuildEvents";
import { GuildLogs } from "../../data/GuildLogs";
import { GuildMutes } from "../../data/GuildMutes";
import { mapToPublicFn } from "../../pluginUtils";
import { makePublicFn } from "../../pluginUtils";
import { CasesPlugin } from "../Cases/CasesPlugin";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { RoleManagerPlugin } from "../RoleManager/RoleManagerPlugin.js";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { ClearBannedMutesCmd } from "./commands/ClearBannedMutesCmd";
import { ClearMutesCmd } from "./commands/ClearMutesCmd";
import { ClearMutesWithoutRoleCmd } from "./commands/ClearMutesWithoutRoleCmd";
@ -61,13 +61,8 @@ const defaultOptions = {
],
};
export const MutesPlugin = zeppelinGuildPlugin<MutesPluginType>()({
export const MutesPlugin = guildPlugin<MutesPluginType>()({
name: "mutes",
showInDocs: true,
info: {
prettyName: "Mutes",
configSchema: zMutesConfig,
},
dependencies: () => [CasesPlugin, LogsPlugin, RoleManagerPlugin],
configParser: (input) => zMutesConfig.parse(input),
@ -89,21 +84,18 @@ export const MutesPlugin = zeppelinGuildPlugin<MutesPluginType>()({
RegisterManualTimeoutsEvt,
],
public: {
muteUser: mapToPublicFn(muteUser),
unmuteUser: mapToPublicFn(unmuteUser),
hasMutedRole(pluginData) {
return (member: GuildMember) => {
public(pluginData) {
return {
muteUser: makePublicFn(pluginData, muteUser),
unmuteUser: makePublicFn(pluginData, unmuteUser),
hasMutedRole: (member: GuildMember) => {
const muteRole = pluginData.config.get().mute_role;
return muteRole ? member.roles.cache.has(muteRole as Snowflake) : false;
};
},
on: mapToPublicFn(onMutesEvent),
off: mapToPublicFn(offMutesEvent),
getEventEmitter(pluginData) {
return () => pluginData.state.events;
},
},
on: makePublicFn(pluginData, onMutesEvent),
off: makePublicFn(pluginData, offMutesEvent),
getEventEmitter: () => pluginData.state.events,
};
},
beforeLoad(pluginData) {

View file

@ -0,0 +1,8 @@
import { ZeppelinPluginInfo } from "../../types";
import { zMutesConfig } from "./types";
export const mutesPluginInfo: ZeppelinPluginInfo = {
prettyName: "Mutes",
showInDocs: true,
configSchema: zMutesConfig,
};

View file

@ -1,8 +1,7 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { Queue } from "../../Queue";
import { GuildNicknameHistory } from "../../data/GuildNicknameHistory";
import { UsernameHistory } from "../../data/UsernameHistory";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { NamesCmd } from "./commands/NamesCmd";
import { NameHistoryPluginType, zNameHistoryConfig } from "./types";
@ -20,9 +19,8 @@ const defaultOptions: PluginOptions<NameHistoryPluginType> = {
],
};
export const NameHistoryPlugin = zeppelinGuildPlugin<NameHistoryPluginType>()({
export const NameHistoryPlugin = guildPlugin<NameHistoryPluginType>()({
name: "name_history",
showInDocs: false,
configParser: (input) => zNameHistoryConfig.parse(input),
defaultOptions,

View file

@ -0,0 +1,8 @@
import { ZeppelinPluginInfo } from "../../types";
import { zNameHistoryConfig } from "./types";
export const nameHistoryPluginInfo: ZeppelinPluginInfo = {
prettyName: "Name history",
showInDocs: false,
configSchema: zNameHistoryConfig,
};

View file

@ -1,10 +1,8 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { GuildLogs } from "../../data/GuildLogs";
import { GuildPersistedData } from "../../data/GuildPersistedData";
import { trimPluginDescription } from "../../utils";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { RoleManagerPlugin } from "../RoleManager/RoleManagerPlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { LoadDataEvt } from "./events/LoadDataEvt";
import { StoreDataEvt } from "./events/StoreDataEvt";
import { PersistPluginType, zPersistConfig } from "./types";
@ -17,17 +15,8 @@ const defaultOptions: PluginOptions<PersistPluginType> = {
},
};
export const PersistPlugin = zeppelinGuildPlugin<PersistPluginType>()({
export const PersistPlugin = guildPlugin<PersistPluginType>()({
name: "persist",
showInDocs: true,
info: {
prettyName: "Persist",
description: trimPluginDescription(`
Re-apply roles or nicknames for users when they rejoin the server.
Mute roles are re-applied automatically, this plugin is not required for that.
`),
configSchema: zPersistConfig,
},
dependencies: () => [LogsPlugin, RoleManagerPlugin],
configParser: (input) => zPersistConfig.parse(input),

View file

@ -0,0 +1,13 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
import { zPersistConfig } from "./types";
export const persistPluginInfo: ZeppelinPluginInfo = {
prettyName: "Persist",
description: trimPluginDescription(`
Re-apply roles or nicknames for users when they rejoin the server.
Mute roles are re-applied automatically, this plugin is not required for that.
`),
configSchema: zPersistConfig,
showInDocs: true,
};

View file

@ -1,9 +1,7 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { hasPhishermanMasterAPIKey, phishermanApiKeyIsValid } from "../../data/Phisherman";
import { mapToPublicFn } from "../../pluginUtils";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { makePublicFn } from "../../pluginUtils";
import { getDomainInfo } from "./functions/getDomainInfo";
import { pluginInfo } from "./info";
import { PhishermanPluginType, zPhishermanConfig } from "./types";
const defaultOptions: PluginOptions<PhishermanPluginType> = {
@ -13,17 +11,16 @@ const defaultOptions: PluginOptions<PhishermanPluginType> = {
overrides: [],
};
export const PhishermanPlugin = zeppelinGuildPlugin<PhishermanPluginType>()({
export const PhishermanPlugin = guildPlugin<PhishermanPluginType>()({
name: "phisherman",
showInDocs: true,
info: pluginInfo,
configParser: (input) => zPhishermanConfig.parse(input),
defaultOptions,
// prettier-ignore
public: {
getDomainInfo: mapToPublicFn(getDomainInfo),
public(pluginData) {
return {
getDomainInfo: makePublicFn(pluginData, getDomainInfo),
};
},
async beforeLoad(pluginData) {

View file

@ -1,8 +1,8 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
import { ZeppelinGuildPluginBlueprint } from "../ZeppelinPluginBlueprint";
import { zPhishermanConfig } from "./types";
export const pluginInfo: ZeppelinGuildPluginBlueprint["info"] = {
export const phishermanPluginInfo: ZeppelinPluginInfo = {
prettyName: "Phisherman",
description: trimPluginDescription(`
Match scam/phishing links using the Phisherman API. See https://phisherman.gg/ for more details!
@ -40,4 +40,5 @@ export const pluginInfo: ZeppelinGuildPluginBlueprint["info"] = {
~~~
`),
configSchema: zPhishermanConfig,
showInDocs: true,
};

View file

@ -1,6 +1,5 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { GuildPingableRoles } from "../../data/GuildPingableRoles";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { PingableRoleDisableCmd } from "./commands/PingableRoleDisableCmd";
import { PingableRoleEnableCmd } from "./commands/PingableRoleEnableCmd";
import { PingableRolesPluginType, zPingableRolesConfig } from "./types";
@ -19,13 +18,8 @@ const defaultOptions: PluginOptions<PingableRolesPluginType> = {
],
};
export const PingableRolesPlugin = zeppelinGuildPlugin<PingableRolesPluginType>()({
export const PingableRolesPlugin = guildPlugin<PingableRolesPluginType>()({
name: "pingable_roles",
showInDocs: true,
info: {
prettyName: "Pingable roles",
configSchema: zPingableRolesConfig,
},
configParser: (input) => zPingableRolesConfig.parse(input),
defaultOptions,

View file

@ -0,0 +1,8 @@
import { ZeppelinPluginInfo } from "../../types";
import { zPingableRolesConfig } from "./types";
export const pingableRolesPluginInfo: ZeppelinPluginInfo = {
prettyName: "Pingable roles",
configSchema: zPingableRolesConfig,
showInDocs: true,
};

View file

@ -1,11 +1,10 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { onGuildEvent } from "../../data/GuildEvents";
import { GuildLogs } from "../../data/GuildLogs";
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
import { GuildScheduledPosts } from "../../data/GuildScheduledPosts";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { EditCmd } from "./commands/EditCmd";
import { EditEmbedCmd } from "./commands/EditEmbedCmd";
import { PostCmd } from "./commands/PostCmd";
@ -30,13 +29,8 @@ const defaultOptions: PluginOptions<PostPluginType> = {
],
};
export const PostPlugin = zeppelinGuildPlugin<PostPluginType>()({
export const PostPlugin = guildPlugin<PostPluginType>()({
name: "post",
showInDocs: true,
info: {
prettyName: "Post",
configSchema: zPostConfig,
},
dependencies: () => [TimeAndDatePlugin, LogsPlugin],
configParser: (input) => zPostConfig.parse(input),

View file

@ -0,0 +1,8 @@
import { ZeppelinPluginInfo } from "../../types";
import { zPostConfig } from "./types";
export const postPluginInfo: ZeppelinPluginInfo = {
prettyName: "Post",
configSchema: zPostConfig,
showInDocs: true,
};

View file

@ -1,9 +1,8 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { Queue } from "../../Queue";
import { GuildReactionRoles } from "../../data/GuildReactionRoles";
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { ClearReactionRolesCmd } from "./commands/ClearReactionRolesCmd";
import { InitReactionRolesCmd } from "./commands/InitReactionRolesCmd";
import { RefreshReactionRolesCmd } from "./commands/RefreshReactionRolesCmd";
@ -33,14 +32,8 @@ const defaultOptions: PluginOptions<ReactionRolesPluginType> = {
],
};
export const ReactionRolesPlugin = zeppelinGuildPlugin<ReactionRolesPluginType>()({
export const ReactionRolesPlugin = guildPlugin<ReactionRolesPluginType>()({
name: "reaction_roles",
showInDocs: true,
info: {
prettyName: "Reaction roles",
legacy: "Consider using the [Role buttons](/docs/plugins/role_buttons) plugin instead.",
configSchema: zReactionRolesConfig,
},
dependencies: () => [LogsPlugin],
configParser: (input) => zReactionRolesConfig.parse(input),

View file

@ -0,0 +1,10 @@
import { ZeppelinPluginInfo } from "../../types";
import { zReactionRolesConfig } from "./types";
export const reactionRolesPluginInfo: ZeppelinPluginInfo = {
prettyName: "Reaction roles",
description: "Consider using the [Role buttons](https://zeppelin.gg/docs/plugins/role_buttons) plugin instead.",
legacy: true,
configSchema: zReactionRolesConfig,
showInDocs: true,
};

View file

@ -1,8 +1,7 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { onGuildEvent } from "../../data/GuildEvents";
import { GuildReminders } from "../../data/GuildReminders";
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { RemindCmd } from "./commands/RemindCmd";
import { RemindersCmd } from "./commands/RemindersCmd";
import { RemindersDeleteCmd } from "./commands/RemindersDeleteCmd";
@ -23,13 +22,8 @@ const defaultOptions: PluginOptions<RemindersPluginType> = {
],
};
export const RemindersPlugin = zeppelinGuildPlugin<RemindersPluginType>()({
export const RemindersPlugin = guildPlugin<RemindersPluginType>()({
name: "reminders",
showInDocs: true,
info: {
prettyName: "Reminders",
configSchema: zRemindersConfig,
},
dependencies: () => [TimeAndDatePlugin],
configParser: (input) => zRemindersConfig.parse(input),

View file

@ -0,0 +1,8 @@
import { ZeppelinPluginInfo } from "../../types";
import { zRemindersConfig } from "./types";
export const remindersPluginInfo: ZeppelinPluginInfo = {
prettyName: "Reminders",
configSchema: zRemindersConfig,
showInDocs: true,
};

View file

@ -1,17 +1,14 @@
import { guildPlugin } from "knub";
import { GuildRoleButtons } from "../../data/GuildRoleButtons";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { RoleManagerPlugin } from "../RoleManager/RoleManagerPlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { resetButtonsCmd } from "./commands/resetButtons";
import { onButtonInteraction } from "./events/buttonInteraction";
import { applyAllRoleButtons } from "./functions/applyAllRoleButtons";
import { pluginInfo } from "./info";
import { RoleButtonsPluginType, zRoleButtonsConfig } from "./types";
export const RoleButtonsPlugin = zeppelinGuildPlugin<RoleButtonsPluginType>()({
export const RoleButtonsPlugin = guildPlugin<RoleButtonsPluginType>()({
name: "role_buttons",
info: pluginInfo,
showInDocs: true,
defaultOptions: {
config: {

View file

@ -1,8 +1,9 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
import { ZeppelinGuildPluginBlueprint } from "../ZeppelinPluginBlueprint";
import { zRoleButtonsConfig } from "./types";
export const pluginInfo: ZeppelinGuildPluginBlueprint["info"] = {
export const roleButtonsPluginInfo: ZeppelinPluginInfo = {
showInDocs: true,
prettyName: "Role buttons",
description: trimPluginDescription(`
Allow users to pick roles by clicking on buttons

View file

@ -1,7 +1,7 @@
import { guildPlugin } from "knub";
import { GuildRoleQueue } from "../../data/GuildRoleQueue";
import { mapToPublicFn } from "../../pluginUtils";
import { makePublicFn } from "../../pluginUtils";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { addPriorityRole } from "./functions/addPriorityRole";
import { addRole } from "./functions/addRole";
import { removePriorityRole } from "./functions/removePriorityRole";
@ -9,18 +9,19 @@ import { removeRole } from "./functions/removeRole";
import { runRoleAssignmentLoop } from "./functions/runRoleAssignmentLoop";
import { RoleManagerPluginType, zRoleManagerConfig } from "./types";
export const RoleManagerPlugin = zeppelinGuildPlugin<RoleManagerPluginType>()({
export const RoleManagerPlugin = guildPlugin<RoleManagerPluginType>()({
name: "role_manager",
showInDocs: false,
dependencies: () => [LogsPlugin],
configParser: (input) => zRoleManagerConfig.parse(input),
public: {
addRole: mapToPublicFn(addRole),
removeRole: mapToPublicFn(removeRole),
addPriorityRole: mapToPublicFn(addPriorityRole),
removePriorityRole: mapToPublicFn(removePriorityRole),
public(pluginData) {
return {
addRole: makePublicFn(pluginData, addRole),
removeRole: makePublicFn(pluginData, removeRole),
addPriorityRole: makePublicFn(pluginData, addPriorityRole),
removePriorityRole: makePublicFn(pluginData, removePriorityRole),
};
},
beforeLoad(pluginData) {

View file

@ -0,0 +1,6 @@
import { ZeppelinPluginInfo } from "../../types";
export const roleManagerPluginInfo: ZeppelinPluginInfo = {
prettyName: "Role manager",
showInDocs: false,
};

View file

@ -1,9 +1,7 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { GuildLogs } from "../../data/GuildLogs";
import { trimPluginDescription } from "../../utils";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { RoleManagerPlugin } from "../RoleManager/RoleManagerPlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { AddRoleCmd } from "./commands/AddRoleCmd";
import { MassAddRoleCmd } from "./commands/MassAddRoleCmd";
import { MassRemoveRoleCmd } from "./commands/MassRemoveRoleCmd";
@ -32,16 +30,8 @@ const defaultOptions: PluginOptions<RolesPluginType> = {
],
};
export const RolesPlugin = zeppelinGuildPlugin<RolesPluginType>()({
export const RolesPlugin = guildPlugin<RolesPluginType>()({
name: "roles",
showInDocs: true,
info: {
prettyName: "Roles",
description: trimPluginDescription(`
Enables authorised users to add and remove whitelisted roles with a command.
`),
configSchema: zRolesConfig,
},
dependencies: () => [LogsPlugin, RoleManagerPlugin],
configParser: (input) => zRolesConfig.parse(input),

View file

@ -0,0 +1,12 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
import { zRolesConfig } from "./types";
export const rolesPluginInfo: ZeppelinPluginInfo = {
showInDocs: true,
prettyName: "Roles",
description: trimPluginDescription(`
Enables authorised users to add and remove whitelisted roles with a command.
`),
configSchema: zRolesConfig,
};

View file

@ -1,6 +1,4 @@
import { CooldownManager, PluginOptions } from "knub";
import { trimPluginDescription } from "../../utils";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { CooldownManager, PluginOptions, guildPlugin } from "knub";
import { RoleAddCmd } from "./commands/RoleAddCmd";
import { RoleHelpCmd } from "./commands/RoleHelpCmd";
import { RoleRemoveCmd } from "./commands/RoleRemoveCmd";
@ -13,60 +11,8 @@ const defaultOptions: PluginOptions<SelfGrantableRolesPluginType> = {
},
};
export const SelfGrantableRolesPlugin = zeppelinGuildPlugin<SelfGrantableRolesPluginType>()({
export const SelfGrantableRolesPlugin = guildPlugin<SelfGrantableRolesPluginType>()({
name: "self_grantable_roles",
showInDocs: true,
info: {
prettyName: "Self-grantable roles",
description: trimPluginDescription(`
Allows users to grant themselves roles via a command
`),
configurationGuide: trimPluginDescription(`
### Basic configuration
In this example, users can add themselves platform roles on the channel 473087035574321152 by using the
\`!role\` command. For example, \`!role pc ps4\` to add both the "pc" and "ps4" roles as specified below.
~~~yml
self_grantable_roles:
config:
entries:
basic:
roles:
"543184300250759188": ["pc", "computer"]
"534710505915547658": ["ps4", "ps", "playstation"]
"473085927053590538": ["xbox", "xb1", "xb"]
overrides:
- channel: "473087035574321152"
config:
entries:
basic:
can_use: true
~~~
### Maximum number of roles
This is identical to the basic example above, but users can only choose 1 role.
~~~yml
self_grantable_roles:
config:
entries:
basic:
roles:
"543184300250759188": ["pc", "computer"]
"534710505915547658": ["ps4", "ps", "playstation"]
"473085927053590538": ["xbox", "xb1", "xb"]
max_roles: 1
overrides:
- channel: "473087035574321152"
config:
entries:
basic:
can_use: true
~~~
`),
configSchema: zSelfGrantableRolesConfig,
},
configParser: (input) => zSelfGrantableRolesConfig.parse(input),
defaultOptions,

View file

@ -0,0 +1,55 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
import { zSelfGrantableRolesConfig } from "./types";
export const selfGrantableRolesPluginInfo: ZeppelinPluginInfo = {
showInDocs: true,
prettyName: "Self-grantable roles",
description: trimPluginDescription(`
Allows users to grant themselves roles via a command
`),
configurationGuide: trimPluginDescription(`
### Basic configuration
In this example, users can add themselves platform roles on the channel 473087035574321152 by using the
\`!role\` command. For example, \`!role pc ps4\` to add both the "pc" and "ps4" roles as specified below.
~~~yml
self_grantable_roles:
config:
entries:
basic:
roles:
"543184300250759188": ["pc", "computer"]
"534710505915547658": ["ps4", "ps", "playstation"]
"473085927053590538": ["xbox", "xb1", "xb"]
overrides:
- channel: "473087035574321152"
config:
entries:
basic:
can_use: true
~~~
### Maximum number of roles
This is identical to the basic example above, but users can only choose 1 role.
~~~yml
self_grantable_roles:
config:
entries:
basic:
roles:
"543184300250759188": ["pc", "computer"]
"534710505915547658": ["ps4", "ps", "playstation"]
"473085927053590538": ["xbox", "xb1", "xb"]
max_roles: 1
overrides:
- channel: "473087035574321152"
config:
entries:
basic:
can_use: true
~~~
`),
configSchema: zSelfGrantableRolesConfig,
};

View file

@ -1,10 +1,9 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { GuildLogs } from "../../data/GuildLogs";
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
import { GuildSlowmodes } from "../../data/GuildSlowmodes";
import { SECONDS } from "../../utils";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { SlowmodeClearCmd } from "./commands/SlowmodeClearCmd";
import { SlowmodeDisableCmd } from "./commands/SlowmodeDisableCmd";
import { SlowmodeGetCmd } from "./commands/SlowmodeGetCmd";
@ -35,13 +34,8 @@ const defaultOptions: PluginOptions<SlowmodePluginType> = {
],
};
export const SlowmodePlugin = zeppelinGuildPlugin<SlowmodePluginType>()({
export const SlowmodePlugin = guildPlugin<SlowmodePluginType>()({
name: "slowmode",
showInDocs: true,
info: {
prettyName: "Slowmode",
configSchema: zSlowmodeConfig,
},
// prettier-ignore
dependencies: () => [

View file

@ -0,0 +1,8 @@
import { ZeppelinPluginInfo } from "../../types";
import { zSlowmodeConfig } from "./types";
export const slowmodePluginInfo: ZeppelinPluginInfo = {
showInDocs: true,
prettyName: "Slowmode",
configSchema: zSlowmodeConfig,
};

View file

@ -1,11 +1,9 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { GuildArchives } from "../../data/GuildArchives";
import { GuildLogs } from "../../data/GuildLogs";
import { GuildMutes } from "../../data/GuildMutes";
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
import { trimPluginDescription } from "../../utils";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { SpamVoiceStateUpdateEvt } from "./events/SpamVoiceEvt";
import { SpamPluginType, zSpamConfig } from "./types";
import { clearOldRecentActions } from "./util/clearOldRecentActions";
@ -42,18 +40,8 @@ const defaultOptions: PluginOptions<SpamPluginType> = {
],
};
export const SpamPlugin = zeppelinGuildPlugin<SpamPluginType>()({
export const SpamPlugin = guildPlugin<SpamPluginType>()({
name: "spam",
showInDocs: true,
info: {
prettyName: "Spam protection",
description: trimPluginDescription(`
Basic spam detection and auto-muting.
For more advanced spam filtering, check out the Automod plugin!
`),
legacy: true,
configSchema: zSpamConfig,
},
dependencies: () => [LogsPlugin],
configParser: (input) => zSpamConfig.parse(input),

View file

@ -0,0 +1,14 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
import { zSpamConfig } from "./types";
export const spamPluginInfo: ZeppelinPluginInfo = {
showInDocs: true,
prettyName: "Spam protection",
description: trimPluginDescription(`
Basic spam detection and auto-muting.
For more advanced spam filtering, check out the Automod plugin!
`),
legacy: true,
configSchema: zSpamConfig,
};

View file

@ -1,9 +1,7 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
import { GuildStarboardMessages } from "../../data/GuildStarboardMessages";
import { GuildStarboardReactions } from "../../data/GuildStarboardReactions";
import { trimPluginDescription } from "../../utils";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { MigratePinsCmd } from "./commands/MigratePinsCmd";
import { StarboardReactionAddEvt } from "./events/StarboardReactionAddEvt";
import { StarboardReactionRemoveAllEvt, StarboardReactionRemoveEvt } from "./events/StarboardReactionRemoveEvts";
@ -26,101 +24,8 @@ const defaultOptions: PluginOptions<StarboardPluginType> = {
],
};
export const StarboardPlugin = zeppelinGuildPlugin<StarboardPluginType>()({
export const StarboardPlugin = guildPlugin<StarboardPluginType>()({
name: "starboard",
showInDocs: true,
info: {
prettyName: "Starboard",
description: trimPluginDescription(`
This plugin allows you to set up starboards on your server. Starboards are like user voted pins where messages with enough reactions get immortalized on a "starboard" channel.
`),
configurationGuide: trimPluginDescription(`
### Note on emojis
To specify emoji in the config, you need to use the emoji's "raw form".
To obtain this, post the emoji with a backslash in front of it.
- Example with a default emoji: ":star:" => "⭐"
- Example with a custom emoji: ":mrvnSmile:" => "<:mrvnSmile:543000534102310933>"
### Basic starboard
Any message on the server that gets 5 star reactions will be posted into the starboard channel (604342689038729226).
~~~yml
starboard:
config:
boards:
basic:
channel_id: "604342689038729226"
stars_required: 5
~~~
### Basic starboard with custom color
Any message on the server that gets 5 star reactions will be posted into the starboard channel (604342689038729226), with the given color (0x87CEEB).
~~~yml
starboard:
config:
boards:
basic:
channel_id: "604342689038729226"
stars_required: 5
color: 0x87CEEB
~~~
### Custom star emoji
This is identical to the basic starboard above, but accepts two emoji: the regular star and a custom :mrvnSmile: emoji
~~~yml
starboard:
config:
boards:
basic:
channel_id: "604342689038729226"
star_emoji: ["⭐", "<:mrvnSmile:543000534102310933>"]
stars_required: 5
~~~
### Limit starboard to a specific channel
This is identical to the basic starboard above, but only works from a specific channel (473087035574321152).
~~~yml
starboard:
config:
boards:
basic:
enabled: false # The starboard starts disabled and is then enabled in a channel override below
channel_id: "604342689038729226"
stars_required: 5
overrides:
- channel: "473087035574321152"
config:
boards:
basic:
enabled: true
~~~
### Limit starboard to a specific level (and above)
This is identical to the basic starboard above, but only works for a specific level (>=50).
~~~yml
starboard:
config:
boards:
levelonly:
enabled: false # The starboard starts disabled and is then enabled in a level override below
channel_id: "604342689038729226"
stars_required: 1
overrides:
- level: ">=50"
config:
boards:
levelonly:
enabled: true
~~~
`),
configSchema: zStarboardConfig,
},
configParser: (input) => zStarboardConfig.parse(input),
defaultOptions,

View file

@ -0,0 +1,96 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
import { zStarboardConfig } from "./types";
export const starboardPluginInfo: ZeppelinPluginInfo = {
showInDocs: true,
prettyName: "Starboard",
description: trimPluginDescription(`
This plugin allows you to set up starboards on your server. Starboards are like user voted pins where messages with enough reactions get immortalized on a "starboard" channel.
`),
configurationGuide: trimPluginDescription(`
### Note on emojis
To specify emoji in the config, you need to use the emoji's "raw form".
To obtain this, post the emoji with a backslash in front of it.
- Example with a default emoji: ":star:" => "⭐"
- Example with a custom emoji: ":mrvnSmile:" => "<:mrvnSmile:543000534102310933>"
### Basic starboard
Any message on the server that gets 5 star reactions will be posted into the starboard channel (604342689038729226).
~~~yml
starboard:
config:
boards:
basic:
channel_id: "604342689038729226"
stars_required: 5
~~~
### Basic starboard with custom color
Any message on the server that gets 5 star reactions will be posted into the starboard channel (604342689038729226), with the given color (0x87CEEB).
~~~yml
starboard:
config:
boards:
basic:
channel_id: "604342689038729226"
stars_required: 5
color: 0x87CEEB
~~~
### Custom star emoji
This is identical to the basic starboard above, but accepts two emoji: the regular star and a custom :mrvnSmile: emoji
~~~yml
starboard:
config:
boards:
basic:
channel_id: "604342689038729226"
star_emoji: ["⭐", "<:mrvnSmile:543000534102310933>"]
stars_required: 5
~~~
### Limit starboard to a specific channel
This is identical to the basic starboard above, but only works from a specific channel (473087035574321152).
~~~yml
starboard:
config:
boards:
basic:
enabled: false # The starboard starts disabled and is then enabled in a channel override below
channel_id: "604342689038729226"
stars_required: 5
overrides:
- channel: "473087035574321152"
config:
boards:
basic:
enabled: true
~~~
### Limit starboard to a specific level (and above)
This is identical to the basic starboard above, but only works for a specific level (>=50).
~~~yml
starboard:
config:
boards:
levelonly:
enabled: false # The starboard starts disabled and is then enabled in a level override below
channel_id: "604342689038729226"
stars_required: 1
overrides:
- level: ">=50"
config:
boards:
levelonly:
enabled: true
~~~
`),
configSchema: zStarboardConfig,
};

View file

@ -1,23 +1,20 @@
import { Snowflake } from "discord.js";
import humanizeDuration from "humanize-duration";
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import moment from "moment-timezone";
import { GuildArchives } from "../../data/GuildArchives";
import { GuildLogs } from "../../data/GuildLogs";
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
import { GuildTags } from "../../data/GuildTags";
import { mapToPublicFn } from "../../pluginUtils";
import { convertDelayStringToMS, trimPluginDescription } from "../../utils";
import { makePublicFn } from "../../pluginUtils";
import { convertDelayStringToMS } from "../../utils";
import { LogsPlugin } from "../Logs/LogsPlugin";
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { TagCreateCmd } from "./commands/TagCreateCmd";
import { TagDeleteCmd } from "./commands/TagDeleteCmd";
import { TagEvalCmd } from "./commands/TagEvalCmd";
import { TagListCmd } from "./commands/TagListCmd";
import { TagSourceCmd } from "./commands/TagSourceCmd";
import { generateTemplateMarkdown } from "./docs";
import { TemplateFunctions } from "./templateFunctions";
import { TagsPluginType, zTagsConfig } from "./types";
import { findTagByName } from "./util/findTagByName";
import { onMessageCreate } from "./util/onMessageCreate";
@ -54,24 +51,8 @@ const defaultOptions: PluginOptions<TagsPluginType> = {
],
};
export const TagsPlugin = zeppelinGuildPlugin<TagsPluginType>()({
export const TagsPlugin = guildPlugin<TagsPluginType>()({
name: "tags",
showInDocs: true,
info: {
prettyName: "Tags",
description: "Tags are a way to store and reuse information.",
configurationGuide: trimPluginDescription(`
### Template Functions
You can use template functions in your tags. These functions are called when the tag is rendered.
You can use these functions to render dynamic content, or to access information from the message and/or user calling the tag.
You use them by adding a \`{}\` on your tag.
Here are the functions you can use in your tags:
${generateTemplateMarkdown(TemplateFunctions)}
`),
configSchema: zTagsConfig,
},
dependencies: () => [LogsPlugin],
defaultOptions,
@ -90,9 +71,11 @@ export const TagsPlugin = zeppelinGuildPlugin<TagsPluginType>()({
onMessageDelete,
],
public: {
renderTagBody: mapToPublicFn(renderTagBody),
findTagByName: mapToPublicFn(findTagByName),
public(pluginData) {
return {
renderTagBody: makePublicFn(pluginData, renderTagBody),
findTagByName: makePublicFn(pluginData, findTagByName),
};
},
configParser: (input) => zTagsConfig.parse(input),

View file

@ -1,17 +0,0 @@
import { trimPluginDescription } from "../../utils";
import { TemplateFunction } from "./types";
export function generateTemplateMarkdown(definitions: TemplateFunction[]): string {
return definitions
.map((def) => {
const usage = def.signature ?? `(${def.arguments.join(", ")})`;
const examples = def.examples?.map((ex) => `> \`{${ex}}\``).join("\n") ?? null;
return trimPluginDescription(`
## ${def.name}
**${def.description}**\n
__Usage__: \`{${def.name}${usage}}\`\n
${examples ? `__Examples__:\n${examples}` : ""}\n\n
`);
})
.join("\n\n");
}

View file

@ -0,0 +1,36 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
import { TemplateFunctions } from "./templateFunctions";
import { TemplateFunction, zTagsConfig } from "./types";
export const tagsPluginInfo: ZeppelinPluginInfo = {
showInDocs: true,
prettyName: "Tags",
description: "Tags are a way to store and reuse information.",
configurationGuide: trimPluginDescription(`
### Template Functions
You can use template functions in your tags. These functions are called when the tag is rendered.
You can use these functions to render dynamic content, or to access information from the message and/or user calling the tag.
You use them by adding a \`{}\` on your tag.
Here are the functions you can use in your tags:
${generateTemplateMarkdown(TemplateFunctions)}
`),
configSchema: zTagsConfig,
};
function generateTemplateMarkdown(definitions: TemplateFunction[]): string {
return definitions
.map((def) => {
const usage = def.signature ?? `(${def.arguments.join(", ")})`;
const examples = def.examples?.map((ex) => `> \`{${ex}}\``).join("\n") ?? null;
return trimPluginDescription(`
## ${def.name}
**${def.description}**\n
__Usage__: \`{${def.name}${usage}}\`\n
${examples ? `__Examples__:\n${examples}` : ""}\n\n
`);
})
.join("\n\n");
}

View file

@ -1,8 +1,6 @@
import { PluginOptions } from "knub";
import { PluginOptions, guildPlugin } from "knub";
import { GuildMemberTimezones } from "../../data/GuildMemberTimezones";
import { mapToPublicFn } from "../../pluginUtils";
import { trimPluginDescription } from "../../utils";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { makePublicFn } from "../../pluginUtils";
import { ResetTimezoneCmd } from "./commands/ResetTimezoneCmd";
import { SetTimezoneCmd } from "./commands/SetTimezoneCmd";
import { ViewTimezoneCmd } from "./commands/ViewTimezoneCmd";
@ -31,16 +29,8 @@ const defaultOptions: PluginOptions<TimeAndDatePluginType> = {
],
};
export const TimeAndDatePlugin = zeppelinGuildPlugin<TimeAndDatePluginType>()({
export const TimeAndDatePlugin = guildPlugin<TimeAndDatePluginType>()({
name: "time_and_date",
showInDocs: true,
info: {
prettyName: "Time and date",
description: trimPluginDescription(`
Allows controlling the displayed time/date formats and timezones
`),
configSchema: zTimeAndDateConfig,
},
configParser: (input) => zTimeAndDateConfig.parse(input),
defaultOptions,
@ -52,12 +42,14 @@ export const TimeAndDatePlugin = zeppelinGuildPlugin<TimeAndDatePluginType>()({
ViewTimezoneCmd,
],
public: {
getGuildTz: mapToPublicFn(getGuildTz),
inGuildTz: mapToPublicFn(inGuildTz),
getMemberTz: mapToPublicFn(getMemberTz),
inMemberTz: mapToPublicFn(inMemberTz),
getDateFormat: mapToPublicFn(getDateFormat),
public(pluginData) {
return {
getGuildTz: makePublicFn(pluginData, getGuildTz),
inGuildTz: makePublicFn(pluginData, inGuildTz),
getMemberTz: makePublicFn(pluginData, getMemberTz),
inMemberTz: makePublicFn(pluginData, inMemberTz),
getDateFormat: makePublicFn(pluginData, getDateFormat),
};
},
beforeLoad(pluginData) {

View file

@ -0,0 +1,12 @@
import { ZeppelinPluginInfo } from "../../types";
import { trimPluginDescription } from "../../utils";
import { zTimeAndDateConfig } from "./types";
export const timeAndDatePluginInfo: ZeppelinPluginInfo = {
showInDocs: true,
prettyName: "Time and date",
description: trimPluginDescription(`
Allows controlling the displayed time/date formats and timezones
`),
configSchema: zTimeAndDateConfig,
};

View file

@ -1,12 +1,11 @@
import { guildPlugin } from "knub";
import { Queue } from "../../Queue";
import { UsernameHistory } from "../../data/UsernameHistory";
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
import { MessageCreateUpdateUsernameEvt, VoiceChannelJoinUpdateUsernameEvt } from "./events/UpdateUsernameEvts";
import { UsernameSaverPluginType, zUsernameSaverConfig } from "./types";
export const UsernameSaverPlugin = zeppelinGuildPlugin<UsernameSaverPluginType>()({
export const UsernameSaverPlugin = guildPlugin<UsernameSaverPluginType>()({
name: "username_saver",
showInDocs: false,
configParser: (input) => zUsernameSaverConfig.parse(input),

Some files were not shown because too many files have changed in this diff Show more