Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat: implement dwc command #74

Merged
merged 2 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions apps/barry/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,10 @@ enum CaseType {
Mute @map("MUTE")
Kick @map("KICK")
Ban @map("BAN")
DWC
Unmute @map("UNMUTE")
Unban @map("UNBAN")
UnDWC @map("UNDWC")
}

model Case {
Expand Down Expand Up @@ -212,3 +214,12 @@ model TempBan {
@@id([guildID, userID])
@@map("temp_bans")
}

model DWCScheduledBan {
guildID String @map("guild_id")
userID String @map("user_id")
createdAt DateTime @map("created_at") @default(now())

@@id([guildID, userID])
@@map("dwc_scheduled_bans")
}
3 changes: 3 additions & 0 deletions apps/barry/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ export default {
emotes: {
// Moderation
ban: new Emoji("ban", "1149399269531459616"),
dwc: new Emoji("dwc", "1151425609487106101"),
kick: new Emoji("kick", "1149399337412071484"),
mute: new Emoji("mute", "1149399536381477006"),
note: new Emoji("note", "1149399622398255188"),
unban: new Emoji("unban", "1149399596372607127"),
undwc: new Emoji("undwc", "1151870611203821628"),
unmute: new Emoji("unmute", "1149399568446914670"),
warn: new Emoji("warn", "1149399652504977508"),

Expand All @@ -76,5 +78,6 @@ export default {
previous: new Emoji("previous", "1124406936188768357")
},
defaultColor: 0xFFC331,
defaultDWCColor: 0xFFFF58,
embedColor: 0x2B2D31
};
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,38 @@ export class ProfileRepository {
});
}

/**
* Retrieves the profile record with the flaggable messages for the specified user.
*
* @param guildID The ID of the guild.
* @param userID The ID of the user.
* @param maxDays The amount of days to get profiles for.
* @returns The profile record with messages, or null if not found.
*/
async getWithFlaggableMessages(
guildID: string,
userID: string,
maxDays: number = 14
): Promise<ProfileWithMessages | null> {
const milliseconds = maxDays * 86400000;
const timestamp = BigInt(Date.now() - milliseconds - 1420070400000);
const minimumID = String(timestamp << 22n);

return this.#prisma.profile.findUnique({
include: {
messages: {
where: {
guildID: guildID,
messageID: {
gte: minimumID
}
}
}
},
where: { userID }
});
}

/**
* Retrieves the profile record with its messages for the specified user.
*
Expand Down
64 changes: 64 additions & 0 deletions apps/barry/src/modules/marketplace/dependencies/profiles/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
type APIEmbed,
type APIUser,
ButtonStyle,
ComponentType
Expand All @@ -11,7 +12,10 @@ import {
ProfileRepository,
ProfilesSettingsRepository
} from "./database.js";

import { DiscordAPIError } from "@discordjs/rest";
import { Module } from "@barry/core";
import { getDWCEmbed } from "../../utils.js";
import { getProfileContent } from "./editor/functions/content.js";
import { loadEvents } from "../../../../utils/loadFolder.js";

Expand Down Expand Up @@ -69,6 +73,18 @@ export default class ProfilesModule extends Module<Application> {
this.profilesSettings = new ProfilesSettingsRepository(client.prisma);
}

/**
* Flags all requests for the specified user.
*
* @param guildID The ID of the guild.
* @param channelID The ID of the channel.
* @param user The user to flag the requests of.
* @param reason The reason to flag the user.
*/
async flagUser(guildID: string, channelID: string, user: APIUser, reason: string): Promise<void> {
return this.#resetProfiles(guildID, channelID, user, 14, [getDWCEmbed(reason)]);
}

/**
* Checks if the guild has enabled this module.
*
Expand Down Expand Up @@ -148,4 +164,52 @@ export default class ProfilesModule extends Module<Application> {
}
}
}

/**
* Removes the flag from all profiles for the specified user.
*
* @param guildID The ID of the guild.
* @param channelID The ID of the channel.
* @param user The user to remove the flag of.
*/
async unflagUser(guildID: string, channelID: string, user: APIUser): Promise<void> {
return this.#resetProfiles(guildID, channelID, user, 21);
}

/**
* Resets flagged profiles for a user in a specific guild's channel.
*
* @param guildID The ID of the guild.
* @param channelID The ID of the channel.
* @param user The user for whom the profiles are being reset.
* @param maxDays The maximum number of days ago a request can be to be reset.
* @param embeds Optional array of embed objects to include in the updated messages.
*/
async #resetProfiles(
guildID: string,
channelID: string,
user: APIUser,
maxDays: number = 14,
embeds: APIEmbed[] = []
): Promise<void> {
const profile = await this.profiles.getWithFlaggableMessages(guildID, user.id, maxDays);
if (profile !== null) {
const content = getProfileContent(user, profile);
if (embeds.length > 0) {
content.embeds?.push(...embeds);
}

for (const message of profile.messages) {
try {
await this.client.api.channels.editMessage(channelID, message.messageID, content);
} catch (error: unknown) {
if (error instanceof DiscordAPIError && error.code === 10008) {
continue;
}

this.client.logger.error(error);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ export interface RequestWithAttachments extends Request {
attachments: RequestAttachment[];
}

/**
* Represents a request with messages.
*/
export interface RequestWithMessages extends Request {
/**
* The messages for the request.
*/
messages: RequestMessage[];
}

/**
* Repository class for managing requests.
*/
Expand Down Expand Up @@ -123,6 +133,49 @@ export class RequestRepository {
});
}

/**
* Retrieves the requests that can be flagged for the specified user.
*
* @param guildID The ID of the guild.
* @param userID The ID of the user.
* @param maxDays The amount of days to get requests for.
* @returns The flaggable request records.
*/
async getFlaggableByUser(
guildID: string,
userID: string,
maxDays: number = 14
): Promise<Array<RequestWithAttachments & RequestWithMessages>> {
const milliseconds = maxDays * 86400000;
const timestamp = BigInt(Date.now() - milliseconds - 1420070400000);
const minimumID = String(timestamp << 22n);

return this.#prisma.request.findMany({
include: {
attachments: true,
messages: {
where: {
guildID: guildID,
messageID: {
gte: minimumID
}
}
}
},
where: {
messages: {
some: {
guildID: guildID,
messageID: {
gte: minimumID
}
}
},
userID: userID
}
});
}

/**
* Upserts a request record for the specified user.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { type APIUser, ButtonStyle, ComponentType } from "@discordjs/core";
import {
type APIEmbed,
type APIUser,
ButtonStyle,
ComponentType
} from "@discordjs/core";
import {
type RequestWithAttachments,
RequestMessageRepository,
Expand All @@ -8,7 +13,9 @@ import {
import type { Application } from "../../../../Application.js";
import type { RequestsSettings } from "@prisma/client";

import { DiscordAPIError } from "@discordjs/rest";
import { Module } from "@barry/core";
import { getDWCEmbed } from "../../utils.js";
import { getRequestContent } from "./editor/functions/content.js";
import { loadEvents } from "../../../../utils/loadFolder.js";

Expand Down Expand Up @@ -55,6 +62,18 @@ export default class RequestsModule extends Module<Application> {
this.requestsSettings = new RequestsSettingsRepository(client.prisma);
}

/**
* Flags all requests for the specified user.
*
* @param guildID The ID of the guild.
* @param channelID The ID of the channel.
* @param user The user to flag the requests of.
* @param reason The reason to flag the user.
*/
async flagUser(guildID: string, channelID: string, user: APIUser, reason: string): Promise<void> {
return this.#resetRequests(guildID, channelID, user, 14, [getDWCEmbed(reason)]);
}

/**
* Checks if the guild has enabled this module.
*
Expand Down Expand Up @@ -150,4 +169,52 @@ export default class RequestsModule extends Module<Application> {
}
}
}

/**
* Removes the flag from all requests for the specified user.
*
* @param guildID The ID of the guild.
* @param channelID The ID of the channel.
* @param user The user to remove the flag of.
*/
async unflagUser(guildID: string, channelID: string, user: APIUser): Promise<void> {
return this.#resetRequests(guildID, channelID, user, 21);
}

/**
* Resets flagged requests for a user in a specific guild's channel.
*
* @param guildID The ID of the guild.
* @param channelID The ID of the channel.
* @param user The user for whom the requests are being reset.
* @param maxDays The maximum number of days ago a request can be to be reset.
* @param embeds Optional array of embed objects to include in the updated messages.
*/
async #resetRequests(
guildID: string,
channelID: string,
user: APIUser,
maxDays: number = 14,
embeds: APIEmbed[] = []
): Promise<void> {
const requests = await this.requests.getFlaggableByUser(guildID, user.id, maxDays);
for (const request of requests) {
const content = getRequestContent(user, request);
if (embeds.length > 0) {
content.embeds?.push(...embeds);
}

for (const message of request.messages) {
try {
await this.client.api.channels.editMessage(channelID, message.messageID, content);
} catch (error: unknown) {
if (error instanceof DiscordAPIError && error.code === 10008) {
continue;
}

this.client.logger.error(error);
}
}
}
}
}
24 changes: 23 additions & 1 deletion apps/barry/src/modules/marketplace/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import type { ReplyableInteraction } from "@barry/core";
import { ButtonStyle, ComponentType, MessageFlags } from "@discordjs/core";
import {
type APIEmbed,
ButtonStyle,
ComponentType,
MessageFlags
} from "@discordjs/core";
import config from "../../config.js";

/**
* Represents a user that can be contacted.
Expand Down Expand Up @@ -83,3 +89,19 @@ export async function displayContact(interaction: ReplyableInteraction, contacta
flags: MessageFlags.Ephemeral
});
}

/**
* Returns the DWC embed for the user.
*
* @param reason The reason the user has been marked as DWC.
*/
export function getDWCEmbed(reason: string): APIEmbed {
return {
author: {
name: "Deal With Caution",
icon_url: config.emotes.error.imageURL
},
description: "This user has been marked as `Deal With Caution`. If you have a business relationship with this person, proceed with caution.\n\n**Reason:**\n" + reason,
color: config.embedColor
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,14 @@ export default class extends SlashCommand<ModerationModule> {
});

const settings = await this.module.moderationSettings.getOrCreate(interaction.guildID);
await this.module.createLogMessage({
case: entity,
creator: interaction.user,
duration: duration,
reason: options.reason,
user: options.user
}, settings);
if (settings.channelID !== null) {
await this.module.createLogMessage(settings.channelID, {
case: entity,
creator: interaction.user,
duration: duration,
reason: options.reason,
user: options.user
});
}
}
}
Loading