Botban feature #16
@ -26,6 +26,11 @@
|
|||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-unused-vars": "off",
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
"@typescript-eslint/no-non-null-assertion": "off",
|
"@typescript-eslint/no-non-null-assertion": "off",
|
||||||
|
"@typescript-eslint/no-misused-promises": ["error", {
|
||||||
|
"checksVoidReturn": {
|
||||||
|
"arguments": false
|
||||||
|
}
|
||||||
|
}],
|
||||||
"accessor-pairs": "warn",
|
"accessor-pairs": "warn",
|
||||||
"array-callback-return": "warn",
|
"array-callback-return": "warn",
|
||||||
"array-bracket-newline": [
|
"array-bracket-newline": [
|
||||||
|
@ -42,7 +42,7 @@ import { InvokerWrapper, MemberWrapper, UserWrapper } from '../src/client/compon
|
|||||||
import GuildWrapper from '../src/client/components/wrappers/GuildWrapper.js';
|
import GuildWrapper from '../src/client/components/wrappers/GuildWrapper.js';
|
||||||
import DiscordClient from '../src/client/DiscordClient.js';
|
import DiscordClient from '../src/client/DiscordClient.js';
|
||||||
import { CommandOption, Inhibitor } from '../src/client/interfaces/index.js';
|
import { CommandOption, Inhibitor } from '../src/client/interfaces/index.js';
|
||||||
import { MuteType } from './Settings.js';
|
import { MuteType, TextCommandsSettings } from './Settings.js';
|
||||||
import { ClientEvents } from './Events.js';
|
import { ClientEvents } from './Events.js';
|
||||||
import { FilterResult } from './Utils.js';
|
import { FilterResult } from './Utils.js';
|
||||||
import { GuildSettingTypes } from './Guild.js';
|
import { GuildSettingTypes } from './Guild.js';
|
||||||
@ -114,6 +114,7 @@ export type ObserverOptions = {
|
|||||||
export type InhibitorOptions = {
|
export type InhibitorOptions = {
|
||||||
name?: string,
|
name?: string,
|
||||||
guild?: boolean
|
guild?: boolean
|
||||||
|
// Higher numbers come first
|
||||||
priority?: number,
|
priority?: number,
|
||||||
silent?: boolean
|
silent?: boolean
|
||||||
} & Partial<ComponentOptions>
|
} & Partial<ComponentOptions>
|
||||||
@ -502,3 +503,14 @@ export declare interface ExtendedVoiceState extends VoiceState {
|
|||||||
export declare interface ExtendedInvite extends Invite {
|
export declare interface ExtendedInvite extends Invite {
|
||||||
guildWrapper?: GuildWrapper
|
guildWrapper?: GuildWrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type EntitySettings = {
|
||||||
|
[key: string]: unknown,
|
||||||
|
textcommands: TextCommandsSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EntityData = {
|
||||||
|
id: Snowflake,
|
||||||
|
banned?: boolean,
|
||||||
|
settings?: EntitySettings
|
||||||
|
}
|
5
@types/Guild.d.ts
vendored
5
@types/Guild.d.ts
vendored
@ -48,6 +48,7 @@ import {
|
|||||||
WordWatcherSettings
|
WordWatcherSettings
|
||||||
} from './Settings.js';
|
} from './Settings.js';
|
||||||
import { ObjectId } from 'mongodb';
|
import { ObjectId } from 'mongodb';
|
||||||
|
import { EntityData, EntitySettings } from './Client.ts';
|
||||||
|
|
||||||
export type GuildSettingTypes =
|
export type GuildSettingTypes =
|
||||||
| AutomodSettings
|
| AutomodSettings
|
||||||
@ -117,7 +118,7 @@ export type GuildSettings = {
|
|||||||
invitefilter: InviteFilterSettings,
|
invitefilter: InviteFilterSettings,
|
||||||
mentionfilter: MentionFilterSettings,
|
mentionfilter: MentionFilterSettings,
|
||||||
raidprotection: RaidprotectionSettings,
|
raidprotection: RaidprotectionSettings,
|
||||||
}
|
} & EntitySettings
|
||||||
|
|
||||||
export type PermissionSet = {
|
export type PermissionSet = {
|
||||||
global: string[],
|
global: string[],
|
||||||
@ -143,7 +144,7 @@ export type GuildData = {
|
|||||||
modlogs?: boolean,
|
modlogs?: boolean,
|
||||||
settings?: boolean
|
settings?: boolean
|
||||||
}
|
}
|
||||||
}
|
} & EntityData
|
||||||
|
|
||||||
export type CallbackData = {
|
export type CallbackData = {
|
||||||
type: string,
|
type: string,
|
||||||
|
@ -17,11 +17,6 @@
|
|||||||
import { Snowflake } from 'discord.js';
|
import { Snowflake } from 'discord.js';
|
||||||
import { InfractionType, SettingAction } from './Client.js';
|
import { InfractionType, SettingAction } from './Client.js';
|
||||||
|
|
||||||
export type UserSettings = {
|
|
||||||
prefix?: string,
|
|
||||||
locale?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Setting = {
|
export type Setting = {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
|
34
@types/User.d.ts
vendored
Normal file
34
@types/User.d.ts
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Galactic - Discord moderation bot
|
||||||
|
// Copyright (C) 2024 Navy.gif
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { EntityData } from './Client.ts';
|
||||||
|
import { LocaleSettings, TextCommandsSettings } from './Settings.ts';
|
||||||
|
|
||||||
|
export type UserSettingTypes =
|
||||||
|
| LocaleSettings
|
||||||
|
| TextCommandsSettings
|
||||||
|
|
||||||
|
export type UserSettings = {
|
||||||
|
[key: string]: UserSettingTypes
|
||||||
|
textcommands: TextCommandsSettings,
|
||||||
|
locale: LocaleSettings
|
||||||
|
} & EntitySettings
|
||||||
|
|
||||||
|
export type UserData = {
|
||||||
|
[key: string]: unknown
|
||||||
|
settings?: UserSettings,
|
||||||
|
debug?: boolean
|
||||||
|
} & EntityData
|
@ -18,6 +18,8 @@ Internal/technical documentation will be in the source files adjacent to the cod
|
|||||||
- MariaDB (TBD)
|
- MariaDB (TBD)
|
||||||
- RabbitMQ (maybe, TBD)
|
- RabbitMQ (maybe, TBD)
|
||||||
|
|
||||||
|
A Docker compose file is available for convenience for setting up databases for development or personal deployment.
|
||||||
|
|
||||||
### Running
|
### Running
|
||||||
- Install the dependencies: `yarn install`
|
- Install the dependencies: `yarn install`
|
||||||
- Run the bot: `yarn start`
|
- Run the bot: `yarn start`
|
||||||
|
47
docker-compose.yml
Normal file
47
docker-compose.yml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Compose file for the bot's DB stack
|
||||||
|
# Modify as necessary
|
||||||
|
# TODO: Setup Traefik labels
|
||||||
|
|
||||||
|
services:
|
||||||
|
mongo:
|
||||||
|
image: mongo
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- mongo-data:/data/db
|
||||||
|
ports:
|
||||||
|
- 27017:27017
|
||||||
|
environment:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: root
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: I_am_ROOT
|
||||||
|
|
||||||
|
maria:
|
||||||
|
image: mariadb
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- maria-data:/var/lib/mysql
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
||||||
|
environment:
|
||||||
|
MARIADB_ROOT_PASSWORD: I_am_ROOT
|
||||||
|
|
||||||
|
# Web interfaces for interacting with the databases
|
||||||
|
adminer:
|
||||||
|
image: adminer
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
|
||||||
|
mongo-express:
|
||||||
|
image: mongo-express
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- 8081:8081
|
||||||
|
environment:
|
||||||
|
ME_CONFIG_MONGODB_ADMINUSERNAME: root
|
||||||
|
ME_CONFIG_MONGODB_ADMINPASSWORD: I_am_ROOT
|
||||||
|
ME_CONFIG_MONGODB_URL: mongodb://root:I_am_ROOT@mongo:27017/
|
||||||
|
ME_CONFIG_BASICAUTH: false
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mongo-data:
|
||||||
|
maria-data:
|
@ -235,6 +235,7 @@ class DiscordClient extends Client
|
|||||||
this.#logger.error(`Unhandled rejection:\n${err?.stack || err}`);
|
this.#logger.error(`Unhandled rejection:\n${err?.stack || err}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
process.on('message', this.#handleMessage.bind(this));
|
process.on('message', this.#handleMessage.bind(this));
|
||||||
process.on('SIGINT', () => this.logger.info('Received SIGINT'));
|
process.on('SIGINT', () => this.logger.info('Received SIGINT'));
|
||||||
process.on('SIGTERM', () => this.logger.info('Received SIGTERM'));
|
process.on('SIGTERM', () => this.logger.info('Received SIGTERM'));
|
||||||
@ -524,6 +525,7 @@ class DiscordClient extends Client
|
|||||||
return this.localeLoader.format(language, index, params, code);
|
return this.localeLoader.format(language, index, params, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Combine these
|
||||||
async getGuildWrapper (id: string)
|
async getGuildWrapper (id: string)
|
||||||
{
|
{
|
||||||
if (this.#guildWrappers.has(id))
|
if (this.#guildWrappers.has(id))
|
||||||
@ -551,6 +553,11 @@ class DiscordClient extends Client
|
|||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getGuildWrappers (ids: string[])
|
||||||
|
{
|
||||||
|
return (await Promise.all(ids.map(id => this.getGuildWrapper(id)))).filter(entry => entry !== null);
|
||||||
|
}
|
||||||
|
|
||||||
getUserWrapper(resolveable: UserResolveable, fetch?: false): UserWrapper | null;
|
getUserWrapper(resolveable: UserResolveable, fetch?: false): UserWrapper | null;
|
||||||
getUserWrapper(resolveable: UserResolveable, fetch?: true): Promise<UserWrapper | null>;
|
getUserWrapper(resolveable: UserResolveable, fetch?: true): Promise<UserWrapper | null>;
|
||||||
getUserWrapper (resolveable: UserResolveable, fetch = true)
|
getUserWrapper (resolveable: UserResolveable, fetch = true)
|
||||||
@ -582,6 +589,16 @@ class DiscordClient extends Client
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUserWrappers(resolveables: UserResolveable[], fetch?: true): Promise<UserWrapper[]>;
|
||||||
|
getUserWrappers(resolveables: UserResolveable[], fetch?: false): UserWrapper[];
|
||||||
|
getUserWrappers (resolveables: UserResolveable[], fetch = true): Promise<UserWrapper[]> | UserWrapper[]
|
||||||
|
{
|
||||||
|
if (!fetch)
|
||||||
|
return resolveables.map(r => this.getUserWrapper(r, false)).filter(e => e !== null) as UserWrapper[];
|
||||||
|
return Promise.all(resolveables.map(resolveable => this.getUserWrapper(resolveable)))
|
||||||
|
.then(entries => entries.filter(e => e !== null)) as Promise<UserWrapper[]>;
|
||||||
|
}
|
||||||
|
|
||||||
async fetchInvite (invite: InviteResolvable, opts?: ClientFetchInviteOptions)
|
async fetchInvite (invite: InviteResolvable, opts?: ClientFetchInviteOptions)
|
||||||
{
|
{
|
||||||
const code = DataResolver.resolveInviteCode(invite);
|
const code = DataResolver.resolveInviteCode(invite);
|
||||||
|
71
src/client/components/commands/developer/Botban.ts
Normal file
71
src/client/components/commands/developer/Botban.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// Galactic - Discord moderation bot
|
||||||
|
// Copyright (C) 2024 Navy.gif
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { CommandOptionType, CommandParams } from '../../../../../@types/Client.js';
|
||||||
|
import { Command } from '../../../interfaces/index.js';
|
||||||
|
import InvokerWrapper from '../../wrappers/InvokerWrapper.js';
|
||||||
|
import DiscordClient from '../../../DiscordClient.js';
|
||||||
|
|
||||||
|
class BotbanCommand extends Command
|
||||||
|
{
|
||||||
|
|
||||||
|
constructor (client: DiscordClient)
|
||||||
|
{
|
||||||
|
super(client, {
|
||||||
|
name: 'botban',
|
||||||
|
aliases: [ 'bban' ],
|
||||||
|
restricted: true,
|
||||||
|
moduleName: 'developer',
|
||||||
|
options: [
|
||||||
|
{ name: 'users', type: CommandOptionType.USERS },
|
||||||
|
{ name: 'guild', type: CommandOptionType.STRING },
|
||||||
|
{ name: 'unban', type: CommandOptionType.BOOLEAN, flag: true, defaultValue: true, valueOptional: true }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute (_invoker: InvokerWrapper, { users, guild, unban }: CommandParams)
|
||||||
|
{
|
||||||
|
let out = 'Revoked bot access from:\n';
|
||||||
|
if (users)
|
||||||
|
{
|
||||||
|
const ids = users.asUsers.map(u => u.id);
|
||||||
|
const wrappers = await this.client.getUserWrappers(ids);
|
||||||
|
out += '**Users**\n';
|
||||||
|
for (const wrapper of wrappers)
|
||||||
|
{
|
||||||
|
out += `\t${wrapper.displayName}\n`;
|
||||||
|
await wrapper.setBotBan(!unban?.asBool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (guild)
|
||||||
|
{
|
||||||
|
const ids = guild.asString.split(' ');
|
||||||
|
const wrappers = await this.client.getGuildWrappers(ids);
|
||||||
|
out += '**Servers**\n';
|
||||||
|
for (const wrapper of wrappers)
|
||||||
|
{
|
||||||
|
out += `\t${wrapper}`;
|
||||||
|
await wrapper.setBotBan(!unban?.asBool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BotbanCommand;
|
@ -28,19 +28,46 @@ class DebugCommand extends Command
|
|||||||
restricted: true,
|
restricted: true,
|
||||||
moduleName: 'developer',
|
moduleName: 'developer',
|
||||||
options: [
|
options: [
|
||||||
{ name: 'guild', required: true },
|
{
|
||||||
{ name: 'enabled', required: true, type: CommandOptionType.BOOLEAN }
|
name: [ 'enable', 'disable' ],
|
||||||
|
type: CommandOptionType.SUB_COMMAND,
|
||||||
|
options: [
|
||||||
|
{ name: 'guild', required: true },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'list',
|
||||||
|
type: CommandOptionType.SUB_COMMAND,
|
||||||
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute (_invoker: InvokerWrapper, options: CommandParams)
|
async execute (invoker: InvokerWrapper, options: CommandParams)
|
||||||
{
|
{
|
||||||
const guildId = options.guild!.asString;
|
const { subcommand } = invoker;
|
||||||
return this.enableDebug(guildId, options.enabled?.asBool);
|
if (!subcommand)
|
||||||
|
throw new Error('Missing subcommand');
|
||||||
|
|
||||||
|
if ([ 'enable', 'disable' ].includes(subcommand.name))
|
||||||
|
{
|
||||||
|
const guildId = options.guild!.asString;
|
||||||
|
return this.toggleDebug(guildId, subcommand.name === 'enable');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subcommand.name === 'list')
|
||||||
|
return this.listDebugGuilds();
|
||||||
}
|
}
|
||||||
|
|
||||||
async enableDebug (guildId: string, enabled = false)
|
async listDebugGuilds ()
|
||||||
|
{
|
||||||
|
const data = await this.client.mongodb.guilds.find({ debug: true });
|
||||||
|
const ids = data.map(entry => entry.id ?? entry.guildId);
|
||||||
|
const guilds = await this.client.getGuildWrappers(ids);
|
||||||
|
return guilds.map(guild => `**${guild.name}**: ${guild.id}`).join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleDebug (guildId: string, enabled = false)
|
||||||
{
|
{
|
||||||
let output = '';
|
let output = '';
|
||||||
if (this.client.shard)
|
if (this.client.shard)
|
||||||
|
47
src/client/components/inhibitors/Banned.ts
Normal file
47
src/client/components/inhibitors/Banned.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Galactic - Discord moderation bot
|
||||||
|
// Copyright (C) 2024 Navy.gif
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { InhibitorResponse } from '../../../../@types/Client.js';
|
||||||
|
import Util from '../../../utilities/Util.js';
|
||||||
|
import DiscordClient from '../../DiscordClient.js';
|
||||||
|
import Inhibitor from '../../interfaces/Inhibitor.js';
|
||||||
|
import InvokerWrapper from '../wrappers/InvokerWrapper.js';
|
||||||
|
|
||||||
|
class Banned extends Inhibitor
|
||||||
|
{
|
||||||
|
constructor (client: DiscordClient)
|
||||||
|
{
|
||||||
|
super(client, {
|
||||||
|
name: 'banned',
|
||||||
|
priority: 10,
|
||||||
|
silent: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute (invoker: InvokerWrapper): Promise<InhibitorResponse>
|
||||||
|
{
|
||||||
|
const user = await invoker.userWrapper();
|
||||||
|
const { guild } = invoker;
|
||||||
|
if (user && await user.botBanned())
|
||||||
|
return super._fail({ noun: Util.capitalise(invoker.format('NOUN_USER')) });
|
||||||
|
if (guild && await guild.botBanned())
|
||||||
|
return super._fail({ noun: Util.capitalise(invoker.format('NOUN_GUILD')) });
|
||||||
|
return super._succeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Banned;
|
@ -24,7 +24,7 @@ class ChannelIgnore extends Inhibitor
|
|||||||
{
|
{
|
||||||
super(client, {
|
super(client, {
|
||||||
name: 'channelIgnore',
|
name: 'channelIgnore',
|
||||||
priority: 9,
|
priority: 8,
|
||||||
guild: true,
|
guild: true,
|
||||||
silent: true
|
silent: true
|
||||||
});
|
});
|
||||||
|
@ -25,7 +25,7 @@ class ClientPermissions extends Inhibitor
|
|||||||
{
|
{
|
||||||
super(client, {
|
super(client, {
|
||||||
name: 'clientPermissions',
|
name: 'clientPermissions',
|
||||||
priority: 10,
|
priority: 9,
|
||||||
guarded: true,
|
guarded: true,
|
||||||
guild: true
|
guild: true
|
||||||
});
|
});
|
||||||
|
@ -170,8 +170,7 @@ class ModerationManager implements Initialisable, CallbackClient
|
|||||||
constructor (client: DiscordClient)
|
constructor (client: DiscordClient)
|
||||||
{
|
{
|
||||||
this.#client = client;
|
this.#client = client;
|
||||||
// this.#callbacks = new Collection();
|
this.#logger = client.createLogger(this);
|
||||||
this.#logger = client.createLogger(this); // new Logger({ name: 'ModMngr' });
|
|
||||||
this.#infractionClasses = Constant.Infractions;
|
this.#infractionClasses = Constant.Infractions;
|
||||||
this.#ready = false;
|
this.#ready = false;
|
||||||
}
|
}
|
||||||
@ -418,11 +417,6 @@ class ModerationManager implements Initialisable, CallbackClient
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {class} Infraction
|
|
||||||
* @param {User | GuildMember} target
|
|
||||||
* @param {object} info
|
|
||||||
* @return {Infraction}
|
|
||||||
* @memberof ModerationManager
|
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line max-lines-per-function
|
// eslint-disable-next-line max-lines-per-function
|
||||||
async _handleTarget (
|
async _handleTarget (
|
||||||
@ -431,7 +425,6 @@ class ModerationManager implements Initialisable, CallbackClient
|
|||||||
info: HandleTargetData
|
info: HandleTargetData
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// wrapper: guildWrapper
|
|
||||||
const { reason, force, guild } = info;
|
const { reason, force, guild } = info;
|
||||||
const { automod, modpoints } = await guild.settings();
|
const { automod, modpoints } = await guild.settings();
|
||||||
const { Type: type } = Infraction;
|
const { Type: type } = Infraction;
|
||||||
@ -609,13 +602,12 @@ class ModerationManager implements Initialisable, CallbackClient
|
|||||||
|
|
||||||
async handleCallback (_id: string, infraction: InfractionJSON)
|
async handleCallback (_id: string, infraction: InfractionJSON)
|
||||||
{
|
{
|
||||||
// const infraction = await this.#client.mongodb.infractions.findOne({ id });
|
|
||||||
if (!infraction)
|
if (!infraction)
|
||||||
return;
|
return;
|
||||||
this.#logger.debug(`Infraction callback: ${infraction.id} (${infraction.type})`);
|
this.#logger.debug(`Infraction callback: ${infraction.id} (${infraction.type})`);
|
||||||
const undoClass = Constant.Infractions[Constants.InfractionOpposites[infraction.type]];
|
const undoClass = Constant.Infractions[Constants.InfractionOpposites[infraction.type]];
|
||||||
if (!undoClass)
|
if (!undoClass)
|
||||||
return;
|
return this.#logger.error(`Missing undo class for ${infraction.type}`);
|
||||||
|
|
||||||
const guild = await this.#client.getGuildWrapper(infraction.guild!);
|
const guild = await this.#client.getGuildWrapper(infraction.guild!);
|
||||||
if (!guild)
|
if (!guild)
|
||||||
@ -646,7 +638,7 @@ class ModerationManager implements Initialisable, CallbackClient
|
|||||||
throw new Error('Missing executor');
|
throw new Error('Missing executor');
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await new undoClass(this.#client, this.#logger, {
|
const result = await new undoClass(this.#client, this.#logger, {
|
||||||
type: undoClass.Type,
|
type: undoClass.Type,
|
||||||
reason: `AUTO-${Constants.InfractionOpposites[infraction.type]} from Case ${infraction.case}`,
|
reason: `AUTO-${Constants.InfractionOpposites[infraction.type]} from Case ${infraction.case}`,
|
||||||
channel,
|
channel,
|
||||||
@ -657,6 +649,8 @@ class ModerationManager implements Initialisable, CallbackClient
|
|||||||
target,
|
target,
|
||||||
executor
|
executor
|
||||||
}).execute();
|
}).execute();
|
||||||
|
if (result.error)
|
||||||
|
this.#logger.error(result);
|
||||||
}
|
}
|
||||||
catch (err)
|
catch (err)
|
||||||
{
|
{
|
||||||
|
@ -287,7 +287,7 @@ class CommandHandler extends Observer
|
|||||||
{
|
{
|
||||||
if (!(invoker.command instanceof SettingsCommand))
|
if (!(invoker.command instanceof SettingsCommand))
|
||||||
invoker.command.error(now);
|
invoker.command.error(now);
|
||||||
this.logger.error(`\n[${invoker.type.toUpperCase()}] Command ${debugstr} errored:\nGuild: ${invoker.inGuild() ? invoker.guild?.name : 'dms'} (${invoker.guild?.id || ''})\nOptions:\n${Object.keys(options).map((key) => `[${key}: ${options[key].asString} (${options[key].rawValue})]`).join('\n')}\n${error.stack || error}`);
|
this.logger.error(`\n[${invoker.type.toUpperCase()}] Command "${debugstr}" errored:\nGuild: ${invoker.inGuild() ? invoker.guild?.name : 'dms'} (${invoker.guild?.id || ''})\nOptions:\n${Object.keys(options).map((key) => `[${key}: ${options[key].asString} (${options[key].rawValue})]`).join('\n')}\n${error.stack || error}`);
|
||||||
this._generateError(invoker, { type: 'commandHandler' });
|
this._generateError(invoker, { type: 'commandHandler' });
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
40
src/client/components/wrappers/EntityWrapper.ts
Normal file
40
src/client/components/wrappers/EntityWrapper.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Galactic - Discord moderation bot
|
||||||
|
// Copyright (C) 2024 Navy.gif
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// import { LoggerClient } from '@navy.gif/logger';
|
||||||
|
// import DiscordClient from '../../DiscordClient.js';
|
||||||
|
// import { EntityData } from '../../../../@types/Client.js';
|
||||||
|
|
||||||
|
// // TODO: Consider refactoring Guild and User wrappers to have a common ancestor
|
||||||
|
// class EntityWrapper<DataType extends EntityData>
|
||||||
|
// {
|
||||||
|
// #client: DiscordClient;
|
||||||
|
// #data!: DataType;
|
||||||
|
// #logger: LoggerClient;
|
||||||
|
// constructor (client: DiscordClient)
|
||||||
|
// {
|
||||||
|
// this.#client = client;
|
||||||
|
// this.#logger = client.createLogger(this, { name: this.constructor.name });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async banned ()
|
||||||
|
// {
|
||||||
|
// return this.#data.banned;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export default EntityWrapper;
|
@ -26,7 +26,7 @@ import {
|
|||||||
} from '../../../../@types/Guild.js';
|
} from '../../../../@types/Guild.js';
|
||||||
import DiscordClient from '../../DiscordClient.js';
|
import DiscordClient from '../../DiscordClient.js';
|
||||||
|
|
||||||
const configVersion = '3.slash.2';
|
const configVersion = '3.slash.3';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Guild,
|
Guild,
|
||||||
@ -45,6 +45,7 @@ import {
|
|||||||
import MemberWrapper from './MemberWrapper.js';
|
import MemberWrapper from './MemberWrapper.js';
|
||||||
import { FilterUtil, Util } from '../../../utilities/index.js';
|
import { FilterUtil, Util } from '../../../utilities/index.js';
|
||||||
import { LoggerClient } from '@navy.gif/logger';
|
import { LoggerClient } from '@navy.gif/logger';
|
||||||
|
import { ObjectId } from 'mongodb';
|
||||||
|
|
||||||
class GuildWrapper
|
class GuildWrapper
|
||||||
{
|
{
|
||||||
@ -104,15 +105,16 @@ class GuildWrapper
|
|||||||
{
|
{
|
||||||
if (this.#data)
|
if (this.#data)
|
||||||
return this.#data;
|
return this.#data;
|
||||||
const data = await this.#client.mongodb.guilds.findOne<GuildData>({ guildId: this.id });
|
|
||||||
|
const data = await this.#client.mongodb.guilds.findOne({ $or: [{ guildId: this.id }, { id: this.id }] });
|
||||||
if (!data)
|
if (!data)
|
||||||
{
|
{
|
||||||
this.#data = {};
|
this.#data = { id: this.id };
|
||||||
return this.#data;
|
return this.#data;
|
||||||
}
|
}
|
||||||
if (data._version === '3.slash')
|
if (data._version === '3.slash')
|
||||||
{
|
{
|
||||||
const oldSettings = data as GuildSettings;
|
const oldSettings = data as unknown as GuildSettings;
|
||||||
const keys = Object.keys(this.defaultConfig);
|
const keys = Object.keys(this.defaultConfig);
|
||||||
const settings: PartialGuildSettings = {};
|
const settings: PartialGuildSettings = {};
|
||||||
for (const key of keys)
|
for (const key of keys)
|
||||||
@ -125,6 +127,13 @@ class GuildWrapper
|
|||||||
await this.#client.mongodb.guilds.deleteOne({ guildId: this.id });
|
await this.#client.mongodb.guilds.deleteOne({ guildId: this.id });
|
||||||
await this.#client.mongodb.guilds.updateOne({ guildId: this.id }, { $set: data });
|
await this.#client.mongodb.guilds.updateOne({ guildId: this.id }, { $set: data });
|
||||||
}
|
}
|
||||||
|
if (data._version === '3.slash.2')
|
||||||
|
{
|
||||||
|
data.id = data.guildId as string;
|
||||||
|
delete data.guildId;
|
||||||
|
data._version = configVersion; // 3.slash.3
|
||||||
|
await this.#client.mongodb.guilds.updateOne({ _id: data._id as ObjectId }, { $set: data });
|
||||||
|
}
|
||||||
this.#data = data;
|
this.#data = data;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -136,10 +145,8 @@ class GuildWrapper
|
|||||||
return this.#settings;
|
return this.#settings;
|
||||||
|
|
||||||
const data = await this.fetchData();
|
const data = await this.fetchData();
|
||||||
// eslint-disable-next-line prefer-const
|
|
||||||
const {
|
const {
|
||||||
settings,
|
settings,
|
||||||
// _imported
|
|
||||||
} = data;
|
} = data;
|
||||||
const { defaultConfig } = this;
|
const { defaultConfig } = this;
|
||||||
|
|
||||||
@ -168,7 +175,7 @@ class GuildWrapper
|
|||||||
return this.#settings;
|
return this.#settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateData (data: GuildData)
|
async updateData (data: Partial<GuildData>)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -194,6 +201,22 @@ class GuildWrapper
|
|||||||
} as GuildSettings;
|
} as GuildSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if guild has been banned from using the bot
|
||||||
|
*/
|
||||||
|
async botBanned ()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setBotBan (value = true)
|
||||||
|
{
|
||||||
|
if (!this.#data)
|
||||||
|
await this.fetchData();
|
||||||
|
this.#data.banned = value;
|
||||||
|
await this.updateData({ banned: value });
|
||||||
|
}
|
||||||
|
|
||||||
async permissions ()
|
async permissions ()
|
||||||
{
|
{
|
||||||
if (this.#permissions)
|
if (this.#permissions)
|
||||||
|
@ -16,171 +16,104 @@
|
|||||||
|
|
||||||
import { ImageURLOptions, MessageCreateOptions, MessagePayload, User } from 'discord.js';
|
import { ImageURLOptions, MessageCreateOptions, MessagePayload, User } from 'discord.js';
|
||||||
import DiscordClient from '../../DiscordClient.js';
|
import DiscordClient from '../../DiscordClient.js';
|
||||||
import { UserSettings } from '../../../../@types/Settings.js';
|
|
||||||
import { LoggerClient } from '@navy.gif/logger';
|
import { LoggerClient } from '@navy.gif/logger';
|
||||||
|
import { UserData, UserSettings } from '../../../../@types/User.js';
|
||||||
|
|
||||||
class UserWrapper
|
class UserWrapper
|
||||||
{
|
{
|
||||||
#client: DiscordClient;
|
#client: DiscordClient;
|
||||||
#user: User;
|
#user: User;
|
||||||
|
|
||||||
#settings: UserSettings;
|
#data!: UserData;
|
||||||
// #points: {
|
|
||||||
// [key: string]: {
|
|
||||||
// expirations: Expiry[],
|
|
||||||
// points: number
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
#logger: LoggerClient;
|
#logger: LoggerClient;
|
||||||
|
#settings: UserSettings;
|
||||||
|
|
||||||
constructor (client: DiscordClient, user: User)
|
constructor (client: DiscordClient, user: User)
|
||||||
{
|
{
|
||||||
this.#client = client;
|
this.#client = client;
|
||||||
this.#user = user;
|
this.#user = user;
|
||||||
this.#logger = client.createLogger({ name: `User: ${user.id}` });
|
this.#logger = client.createLogger({ name: `User: ${user.id}` });
|
||||||
|
}
|
||||||
|
|
||||||
this.#settings = {};
|
async fetchData ()
|
||||||
// this.#points = {};
|
{
|
||||||
|
if (this.#data)
|
||||||
|
return this.#data;
|
||||||
|
|
||||||
|
const data = await this.#client.mongodb.users.findOne({ id: this.id });
|
||||||
|
if (!data)
|
||||||
|
{
|
||||||
|
this.#data = { id: this.id };
|
||||||
|
return this.#data;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#data = data;
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async settings (forceFetch = false)
|
async settings (forceFetch = false)
|
||||||
{
|
{
|
||||||
if (this.#settings && !forceFetch)
|
if (this.#data && !forceFetch)
|
||||||
return this.#settings;
|
return this.#data.settings;
|
||||||
|
|
||||||
|
const data = await this.fetchData();
|
||||||
|
const { settings } = data;
|
||||||
|
const { defaultConfig } = this;
|
||||||
|
|
||||||
const settings = await this.#client.mongodb.users.findOne({ userId: this.id });
|
|
||||||
if (settings)
|
if (settings)
|
||||||
this.#settings = { ...this.defaultConfig, ...settings };
|
{
|
||||||
else
|
const keys = Object.keys(settings);
|
||||||
this.#settings = { userId: this.id, ...this.defaultConfig };
|
for (const key of keys)
|
||||||
|
{
|
||||||
|
defaultConfig[key] = { ...defaultConfig[key], [key]: settings[key] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#settings = defaultConfig;
|
||||||
return this.#settings;
|
return this.#settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
// async fetchPoints (guild: GuildWrapper)
|
/**
|
||||||
// {
|
* Check whether the user is banned from using the bot
|
||||||
// let index = this.#points[guild.id];
|
*/
|
||||||
// if (index)
|
async botBanned (): Promise<boolean>
|
||||||
// return Promise.resolve(index);
|
{
|
||||||
// this.#points[guild.id] = {
|
const data = await this.fetchData();
|
||||||
// expirations: [],
|
return data?.banned ?? false;
|
||||||
// points: 0
|
|
||||||
// };
|
|
||||||
// index = this.#points[guild.id];
|
|
||||||
|
|
||||||
// const filter = {
|
|
||||||
// guild: guild.id,
|
|
||||||
// target: this.id,
|
|
||||||
// resolved: false,
|
|
||||||
// // points: { $gte: 0 },
|
|
||||||
// // $or: [{ expiration: 0 }, { expiration: { $gte: now } }]
|
|
||||||
// };
|
|
||||||
// const find = await this.#client.mongodb.infractions.find(
|
|
||||||
// filter,
|
|
||||||
// { projection: { id: 1, points: 1, expiration: 1 } }
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (find.length)
|
|
||||||
// {
|
|
||||||
// for (const { points: p, expiration, id } of find)
|
|
||||||
// {
|
|
||||||
// let points = p;
|
|
||||||
// // Imported cases may have false or null
|
|
||||||
// if (typeof points !== 'number')
|
|
||||||
// points = 0;
|
|
||||||
// if (expiration > 0)
|
|
||||||
// {
|
|
||||||
// index.expirations.push({ points, expiration, id });
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// index.points += points;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return index;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async editPoints (guild: GuildWrapper, { id, diff, expiration }: {id: string, diff: number, expiration: unknown })
|
|
||||||
// {
|
|
||||||
// const points = await this.fetchPoints(guild);
|
|
||||||
// if (expiration)
|
|
||||||
// {
|
|
||||||
// const expiry = points.expirations.find((exp) => exp.id === id) as Expiry;
|
|
||||||
// expiry.points += diff;
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// points.points += diff;
|
|
||||||
// return this.totalPoints(guild);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async editExpiration (guild: GuildWrapper, { id, expiration, points }: { id: string, expiration: number, points: number })
|
|
||||||
// {
|
|
||||||
// const index = await this.fetchPoints(guild);
|
|
||||||
// const i = index.expirations.findIndex((exp) => exp.id === id);
|
|
||||||
// if (i > -1)
|
|
||||||
// index.expirations[i].expiration = expiration;
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// index.points -= points;
|
|
||||||
// index.expirations.push({ id, points, expiration });
|
|
||||||
// }
|
|
||||||
// return this.totalPoints(guild);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async totalPoints (guild: GuildWrapper, point?: { id: string, points: number, expiration: number, timestamp: number })
|
|
||||||
// { // point = { points: x, expiration: x, timestamp: x}
|
|
||||||
// const index = await this.fetchPoints(guild);
|
|
||||||
// const now = Date.now();
|
|
||||||
|
|
||||||
// if (point)
|
|
||||||
// {
|
|
||||||
// if (point.expiration > 0)
|
|
||||||
// {
|
|
||||||
// index.expirations.push({ id: point.id, points: point.points, expiration: point.expiration + point.timestamp });
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// index.points += point.points;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let expirationPoints = index.expirations.map((e) =>
|
|
||||||
// {
|
|
||||||
// if (e.expiration >= now)
|
|
||||||
// return e.points;
|
|
||||||
// return 0;
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (expirationPoints.length === 0)
|
|
||||||
// expirationPoints = [ 0 ];
|
|
||||||
// return expirationPoints.reduce((p, v) => p + v) + index.points;
|
|
||||||
// }
|
|
||||||
|
|
||||||
async updateSettings (data: UserSettings)
|
|
||||||
{ // Update property (upsert true) - updateOne
|
|
||||||
if (!this.#settings)
|
|
||||||
await this.settings();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await this.#client.mongodb.users.updateOne(
|
|
||||||
{ guildId: this.id },
|
|
||||||
{ $set: data }
|
|
||||||
);
|
|
||||||
this.#settings = {
|
|
||||||
...this.#settings,
|
|
||||||
...data
|
|
||||||
};
|
|
||||||
this.#storageLog(`Database Update (guild:${this.id}).`);
|
|
||||||
}
|
|
||||||
catch (error)
|
|
||||||
{
|
|
||||||
this.#storageError(error as Error);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get defaultConfig ()
|
async setBotBan (value = true)
|
||||||
|
{
|
||||||
|
if (!this.#data)
|
||||||
|
await this.fetchData();
|
||||||
|
this.#data!.banned = value;
|
||||||
|
await this.updateData({ banned: value });
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateSettings (settings: Partial<UserSettings>)
|
||||||
|
{
|
||||||
|
if (!this.#data?.settings)
|
||||||
|
await this.settings();
|
||||||
|
|
||||||
|
await this.updateData({ settings: settings as UserSettings });
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateData (data: Partial<UserData>)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await this.#client.mongodb.users.updateOne({ id: this.id }, { $set: data });
|
||||||
|
this.#data = { ...this.#data, ...data } as UserData;
|
||||||
|
this.#storageLog('Data update');
|
||||||
|
}
|
||||||
|
catch (err)
|
||||||
|
{
|
||||||
|
const error = err as Error;
|
||||||
|
this.#storageError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get defaultConfig (): UserSettings
|
||||||
{
|
{
|
||||||
return JSON.parse(JSON.stringify(this.#client.defaultConfig('USER')));
|
return JSON.parse(JSON.stringify(this.#client.defaultConfig('USER')));
|
||||||
}
|
}
|
||||||
@ -242,14 +175,114 @@ class UserWrapper
|
|||||||
|
|
||||||
get prefix ()
|
get prefix ()
|
||||||
{
|
{
|
||||||
return this.#settings.prefix;
|
return this.#data?.settings.textcommands.prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
get locale ()
|
get locale ()
|
||||||
{
|
{
|
||||||
return this.#settings.locale;
|
return this.#data?.settings.locale.language;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default UserWrapper;
|
export default UserWrapper;
|
||||||
|
|
||||||
|
// async fetchPoints (guild: GuildWrapper)
|
||||||
|
// {
|
||||||
|
// let index = this.#points[guild.id];
|
||||||
|
// if (index)
|
||||||
|
// return Promise.resolve(index);
|
||||||
|
// this.#points[guild.id] = {
|
||||||
|
// expirations: [],
|
||||||
|
// points: 0
|
||||||
|
// };
|
||||||
|
// index = this.#points[guild.id];
|
||||||
|
|
||||||
|
// const filter = {
|
||||||
|
// guild: guild.id,
|
||||||
|
// target: this.id,
|
||||||
|
// resolved: false,
|
||||||
|
// // points: { $gte: 0 },
|
||||||
|
// // $or: [{ expiration: 0 }, { expiration: { $gte: now } }]
|
||||||
|
// };
|
||||||
|
// const find = await this.#client.mongodb.infractions.find(
|
||||||
|
// filter,
|
||||||
|
// { projection: { id: 1, points: 1, expiration: 1 } }
|
||||||
|
// );
|
||||||
|
|
||||||
|
// if (find.length)
|
||||||
|
// {
|
||||||
|
// for (const { points: p, expiration, id } of find)
|
||||||
|
// {
|
||||||
|
// let points = p;
|
||||||
|
// // Imported cases may have false or null
|
||||||
|
// if (typeof points !== 'number')
|
||||||
|
// points = 0;
|
||||||
|
// if (expiration > 0)
|
||||||
|
// {
|
||||||
|
// index.expirations.push({ points, expiration, id });
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// index.points += points;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return index;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async editPoints (guild: GuildWrapper, { id, diff, expiration }: {id: string, diff: number, expiration: unknown })
|
||||||
|
// {
|
||||||
|
// const points = await this.fetchPoints(guild);
|
||||||
|
// if (expiration)
|
||||||
|
// {
|
||||||
|
// const expiry = points.expirations.find((exp) => exp.id === id) as Expiry;
|
||||||
|
// expiry.points += diff;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// points.points += diff;
|
||||||
|
// return this.totalPoints(guild);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async editExpiration (guild: GuildWrapper, { id, expiration, points }: { id: string, expiration: number, points: number })
|
||||||
|
// {
|
||||||
|
// const index = await this.fetchPoints(guild);
|
||||||
|
// const i = index.expirations.findIndex((exp) => exp.id === id);
|
||||||
|
// if (i > -1)
|
||||||
|
// index.expirations[i].expiration = expiration;
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// index.points -= points;
|
||||||
|
// index.expirations.push({ id, points, expiration });
|
||||||
|
// }
|
||||||
|
// return this.totalPoints(guild);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async totalPoints (guild: GuildWrapper, point?: { id: string, points: number, expiration: number, timestamp: number })
|
||||||
|
// { // point = { points: x, expiration: x, timestamp: x}
|
||||||
|
// const index = await this.fetchPoints(guild);
|
||||||
|
// const now = Date.now();
|
||||||
|
|
||||||
|
// if (point)
|
||||||
|
// {
|
||||||
|
// if (point.expiration > 0)
|
||||||
|
// {
|
||||||
|
// index.expirations.push({ id: point.id, points: point.points, expiration: point.expiration + point.timestamp });
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// index.points += point.points;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let expirationPoints = index.expirations.map((e) =>
|
||||||
|
// {
|
||||||
|
// if (e.expiration >= now)
|
||||||
|
// return e.points;
|
||||||
|
// return 0;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (expirationPoints.length === 0)
|
||||||
|
// expirationPoints = [ 0 ];
|
||||||
|
// return expirationPoints.reduce((p, v) => p + v) + index.points;
|
||||||
|
// }
|
@ -264,13 +264,13 @@ class Infraction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.#duration)
|
// Remove the role structures as they will cause problems when serialising for database
|
||||||
await this.#client.moderation.handleTimedInfraction(this.json);
|
|
||||||
|
|
||||||
/* LMAOOOO PLEASE DONT JUDGE ME */
|
|
||||||
if (this.#data.roles)
|
if (this.#data.roles)
|
||||||
delete this.#data.roles;
|
delete this.#data.roles;
|
||||||
|
|
||||||
|
if (this.#duration)
|
||||||
|
await this.#client.moderation.handleTimedInfraction(this.json);
|
||||||
|
|
||||||
return this.save();
|
return this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
import { CallbackInfo } from '../../../../@types/CallbackManager.js';
|
import { CallbackInfo } from '../../../../@types/CallbackManager.js';
|
||||||
import { InfractionJSON } from '../../../../@types/Client.js';
|
import { InfractionJSON } from '../../../../@types/Client.js';
|
||||||
import { AttachmentData, GuildData, GuildPermissions, MessageLogEntry, RoleCacheEntry, WebhookEntry, WordWatcherEntry } from '../../../../@types/Guild.js';
|
import { AttachmentData, GuildData, GuildPermissions, MessageLogEntry, RoleCacheEntry, WebhookEntry, WordWatcherEntry } from '../../../../@types/Guild.js';
|
||||||
import { UserSettings } from '../../../../@types/Settings.js';
|
|
||||||
import { MongoDBOptions } from '../../../../@types/Storage.js';
|
import { MongoDBOptions } from '../../../../@types/Storage.js';
|
||||||
|
import { UserData } from '../../../../@types/User.js';
|
||||||
import DiscordClient from '../../DiscordClient.js';
|
import DiscordClient from '../../DiscordClient.js';
|
||||||
import { MongodbTable } from '../interfaces/index.js';
|
import { MongodbTable } from '../interfaces/index.js';
|
||||||
import Provider from '../interfaces/Provider.js';
|
import Provider from '../interfaces/Provider.js';
|
||||||
@ -142,7 +142,7 @@ class MongoDBProvider extends Provider
|
|||||||
|
|
||||||
get users ()
|
get users ()
|
||||||
{
|
{
|
||||||
return this.tables.users as MongodbTable<UserSettings>;
|
return this.tables.users as MongodbTable<UserData>;
|
||||||
}
|
}
|
||||||
|
|
||||||
get roleCache ()
|
get roleCache ()
|
||||||
|
@ -27,3 +27,6 @@ The command **{command}** can only be run by developers.
|
|||||||
|
|
||||||
[INHIBITOR_GUILDONLY_ERROR]
|
[INHIBITOR_GUILDONLY_ERROR]
|
||||||
The command **{command}** is only available in servers.
|
The command **{command}** is only available in servers.
|
||||||
|
|
||||||
|
[INHIBITOR_BANNED_ERROR]
|
||||||
|
{noun} has been banned from using the bot.
|
5
src/localization/en_gb/general/en_gb_nouns.lang
Normal file
5
src/localization/en_gb/general/en_gb_nouns.lang
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[NOUN_USER]
|
||||||
|
user
|
||||||
|
|
||||||
|
[NOUN_GUILD]
|
||||||
|
server
|
Loading…
Reference in New Issue
Block a user