Skip to content
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
38 changes: 32 additions & 6 deletions src/commands/moderation/ban.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,53 @@ import { fmt } from "@/utils/format"
import { ephemeral } from "@/utils/messages"
import { getTelegramId } from "@/utils/telegram-id"
import { numberOrString, type Role } from "@/utils/types"
import { getUser } from "@/utils/users"
import { getOverloadUser, getUser } from "@/utils/users"

export const ban = new CommandsCollection<Role>("Banning")
.createCommand({
trigger: "ban",
args: [{ key: "reason", optional: true, description: "Optional reason to ban the user" }],
args: [
{
key: "reasonOrUser",
optional: true,
description:
"If the message is a reply, this argument is the reason. Otherwise, it's the username or user id of the user to ban",
type: numberOrString,
},
{ key: "reason", optional: true, description: "Optional reason to ban the user" },
],
description: "Permanently ban a user from a group",
scope: "group",
reply: "required",
reply: "optional",
permissions: {
allowedRoles: ["owner", "direttivo"],
excludedRoles: ["creator"],
allowGroupAdmins: true,
},
handler: async ({ args, context, repliedTo }) => {
if (!repliedTo.from) {
logger.error("ban: no repliedTo.from field (the msg was sent in a channel)")
const userOverload = await getOverloadUser(context, repliedTo, args.reasonOrUser, args.reason)
if (userOverload.isErr()) {
await ephemeral(
context.reply(
repliedTo
? fmt(({ n }) => n`There was an error`)
: fmt(({ n }) => n`Target user not found, please try replying to their message`)
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
)
logger.error({ args, repliedTo }, `BAN: ${userOverload.error}`)
return
}

const res = await Moderation.ban(repliedTo.from, context.chat, context.from, null, [repliedTo], args.reason)
const { user, reason } = userOverload.value

const res = await Moderation.ban(
user,
context.chat,
context.from,
null,
repliedTo ? [repliedTo] : undefined,
reason
)
if (res.isErr()) await ephemeral(context.reply(res.error.fmtError))
},
})
Expand Down
38 changes: 32 additions & 6 deletions src/commands/moderation/mute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { fmt } from "@/utils/format"
import { ephemeral } from "@/utils/messages"
import { getTelegramId } from "@/utils/telegram-id"
import { numberOrString, type Role } from "@/utils/types"
import { getUser } from "@/utils/users"
import { getOverloadUser, getUser } from "@/utils/users"

export const mute = new CommandsCollection<Role>("Muting")
.createCommand({
Expand Down Expand Up @@ -47,22 +47,48 @@ export const mute = new CommandsCollection<Role>("Muting")
})
.createCommand({
trigger: "mute",
args: [{ key: "reason", optional: true, description: "Optional reason to mute the user" }],
args: [
{
key: "reasonOrUser",
optional: true,
description:
"If the message is a reply, this argument is the reason. Otherwise, it's the username or user id of the user to mute",
type: numberOrString,
},
{ key: "reason", optional: true, description: "Optional reason to mute the user" },
],
description: "Permanently mute a user from a group",
scope: "group",
reply: "required",
reply: "optional",
permissions: {
allowedRoles: ["owner", "direttivo"],
excludedRoles: ["creator"],
allowGroupAdmins: true,
},
handler: async ({ args, context, repliedTo }) => {
if (!repliedTo.from) {
logger.error("mute: no repliedTo.from field (the msg was sent in a channel)")
const userOverload = await getOverloadUser(context, repliedTo, args.reasonOrUser, args.reason)
if (userOverload.isErr()) {
await ephemeral(
context.reply(
repliedTo
? fmt(({ n }) => n`There was an error`)
: fmt(({ n }) => n`Target user not found, please try replying to their message`)
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
)
logger.error({ args, repliedTo }, `MUTE: ${userOverload.error}`)
return
}

const res = await Moderation.mute(repliedTo.from, context.chat, context.from, null, [repliedTo], args.reason)
const { user, reason } = userOverload.value

const res = await Moderation.mute(
user,
context.chat,
context.from,
null,
repliedTo ? [repliedTo] : undefined,
reason
)
if (res.isErr()) await ephemeral(context.reply(res.error.fmtError))
},
})
Expand Down
33 changes: 32 additions & 1 deletion src/utils/users.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { Context } from "grammy"
import type { User } from "grammy/types"
import type { Message, User } from "grammy/types"
import { Err, Ok, type Result } from "neverthrow"
import { logger } from "@/logger"
import { MessageUserStorage } from "@/middlewares/message-user-storage"
import { getTelegramId } from "./telegram-id"

export async function getUser<C extends Context>(userId: number, ctx: C | null): Promise<User | null> {
// TODO: check if this works correctly
Expand All @@ -26,3 +29,31 @@ export function printCtxFrom<C extends Context = Context>(ctx: C): string {
if (!ctx.from) return "<N/A>"
return printUsername(ctx.from)
}

export async function getOverloadUser<C extends Context>(
context: C,
repliedTo: Message | null,
firstArg?: string | number,
secondArg?: string
): Promise<Result<{ user: User; reason?: string }, string>> {
if (repliedTo) {
if (!repliedTo.from) {
// error
return new Err("[getOverloadUser] no repliedTo.from field (the msg was sent in a channel)")
}
return new Ok({ user: repliedTo.from, reason: [firstArg, secondArg].filter(Boolean).join(" ") })
}

if (!firstArg) return new Err("[getOverloadUser] No firstArg passed (without repliedTo)")

const userId = typeof firstArg === "number" ? firstArg : await getTelegramId(firstArg).catch(() => null)
if (!userId) return new Err("[getOverloadUser] Cannot retrieve the userId from arg or redis")

const user = await getUser(userId, context).catch(() => null)
if (!user) return new Err("[getOverloadUser] Cannot retrieve the User from chatMember or storage")

return new Ok({
user,
reason: secondArg,
})
}
Loading