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
4 changes: 0 additions & 4 deletions src/modules/moderation/ban-all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,6 @@ export class BanAllQueue extends Module<ModuleShared> {
private flowProducer = new FlowProducer({ connection })

public async initiateBanAll(banAll: BanAll, messageId: number) {
if (banAll.outcome !== "approved") {
throw new Error("Cannot initiate ban all for a non-approved BanAll")
}

const allGroups = await api.tg.groups.getAll.query()
const chats = allGroups.map((g) => g.telegramId)
const banType = banAll.type === "BAN" ? "ban" : "unban"
Expand Down
122 changes: 10 additions & 112 deletions src/modules/tg-logger/ban-all.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import type { Context } from "grammy"
import type { User } from "grammy/types"
import { type CallbackCtx, MenuGenerator } from "@/lib/menu"
import { logger } from "@/logger"
import { fmt, fmtUser } from "@/utils/format"
import { unicodeProgressBar } from "@/utils/progress"
import { calculateOutcome, type Outcome, type Vote, type Voter } from "@/utils/vote"
import { modules } from ".."

// NOTE
// Previously this was using a voting system made in @/utils/vote.ts.
// Since banAll is a urgent moderation action to execute, we decided to remove it,
// giving priority to common sense over formality.
// If in the future we decide to reintroduce it, check the following PR
// https://github.com/PoliNetworkOrg/telegram/pull/94
// to understand how to reimplement it

export type BanAllState = {
jobCount: number
Expand Down Expand Up @@ -33,23 +36,9 @@ export type BanAll = {
target: User
reporter: User
reason?: string
outcome: Outcome
voters: Voter[]
state: BanAllState
}

const VOTE_EMOJI: Record<Vote, string> = {
inFavor: "✅",
against: "❌",
abstained: "🫥",
}

const OUTCOME_STR: Record<Outcome, string> = {
waiting: "⏳ Waiting for votes",
approved: "✅ APPROVED",
denied: "❌ DENIED",
}

export const getProgressText = (state: BanAll["state"]): string => {
if (state.jobCount === 0) return fmt(({ i }) => i`\nFetching groups...`)

Expand All @@ -76,106 +65,15 @@ export const getProgressText = (state: BanAll["state"]): string => {
*/
export const getBanAllText = (data: BanAll) =>
fmt(
({ n, b, skip, strikethrough, i }) => [
({ n, b, skip, i }) => [
data.type === "BAN" ? b`🚨 BAN ALL 🚨` : b`🕊 UN-BAN ALL 🕊`,
"",
n`${b`🎯 Target:`} ${fmtUser(data.target)} `,
n`${b`📣 Reporter:`} ${fmtUser(data.reporter)} `,
data.type === "BAN" ? n`${b`📋 Reason:`} ${data.reason ? data.reason : i`N/A`}` : undefined,
"",
b`${OUTCOME_STR[data.outcome]} `,
data.outcome === "approved" ? skip`${getProgressText(data.state)}` : undefined,
skip`${getProgressText(data.state)}`,
"",
b`Voters`,
...data.voters.map((v) =>
data.outcome !== "waiting" && !v.vote
? strikethrough`➖ ${fmtUser(v.user)} ${v.isPresident ? b`PRES` : ""} `
: n`${v.vote ? VOTE_EMOJI[v.vote] : "⏳"} ${fmtUser(v.user)} ${v.isPresident ? b`PRES` : ""} `
),
],
{ sep: "\n" }
)

async function vote<C extends Context>(
ctx: CallbackCtx<C>,
data: BanAll,
vote: Vote
): Promise<{ feedback?: string; newData?: BanAll }> {
const voterId = ctx.callbackQuery.from.id
const voter = data.voters.find((v) => v.user.id === voterId)
if (!voter)
return {
feedback: "❌ You cannot vote",
}
if (voter.vote !== undefined)
return {
feedback: "⚠️ You cannot change your vote!",
}

voter.vote = vote
const outcome = calculateOutcome(data.voters)
logger.debug({ outcome: data.outcome, voters: data.voters }, "[VOTE] new vote, calculating...")
if (outcome === null) {
logger.fatal({ banAll: data }, "ERROR WHILE VOTING FOR BAN_ALL, Outcome is null")
return {
feedback: "There was an error, check logs",
}
}
data.outcome = outcome

if (outcome === "approved") {
try {
if (ctx.msgId) await modules.get("banAll").initiateBanAll(data, ctx.msgId)
else {
logger.error(
{ callbackQuery: ctx.callbackQuery },
"Message ID is undefined, cannot initiate ban all. How did this happen?"
)
}
} catch (error) {
await modules
.get("tgLogger")
.exception({ error, type: "UNKNOWN" }, "There was an error while initializing BanAll queue, check logs")
}
}

// remove buttons if there is an outcome (not waiting)
const reply_markup = outcome === "waiting" ? ctx.msg?.reply_markup : undefined

await ctx.editMessageText(getBanAllText(data), { reply_markup }).catch(() => {
// throws if message is not modified - we don't care
})

return {
newData: data,
feedback: "✅ Thanks for voting!",
}
}

/**
* Interactive menu for handling voting.
*
* @param data - {@link BanAll} initial BanAll
*/
export const banAllMenu = MenuGenerator.getInstance<Context>().create<BanAll>("ban-all-voting", [
[
{
text: VOTE_EMOJI.inFavor,
cb: async ({ ctx, data }) => {
return await vote(ctx, data, "inFavor")
},
},
{
text: VOTE_EMOJI.abstained,
cb: async ({ ctx, data }) => {
return await vote(ctx, data, "abstained")
},
},
{
text: VOTE_EMOJI.against,
cb: async ({ ctx, data }) => {
return await vote(ctx, data, "against")
},
},
],
])
67 changes: 18 additions & 49 deletions src/modules/tg-logger/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { GrammyError, InlineKeyboard } from "grammy"
import type { Message, User } from "grammy/types"
import { api } from "@/backend"
import { Module } from "@/lib/modules"
import { logger } from "@/logger"
import { groupMessagesByChat, stripChatId } from "@/utils/chat"
import { fmt, fmtChat, fmtDate, fmtUser } from "@/utils/format"
import type { ModuleShared } from "@/utils/types"
import { after } from "@/utils/wait"
import { modules } from ".."
import type { ModerationAction, PreDeleteResult } from "../moderation/types"
import { type BanAll, banAllMenu, getBanAllText } from "./ban-all"
import { type BanAll, getBanAllText } from "./ban-all"
import { grantCreatedMenu, grantMessageMenu } from "./grants"
import { getReportText, type Report, reportMenu } from "./report"
import type * as Types from "./types"
Expand Down Expand Up @@ -161,68 +161,37 @@ export class TgLogger extends Module<ModuleShared> {
}

public async banAll(target: User, reporter: User, type: "BAN" | "UNBAN", reason?: string): Promise<string | null> {
const direttivo = await api.tg.permissions.getDirettivo.query()

switch (direttivo.error) {
case "EMPTY":
return fmt(({ n }) => n`Error: Direttivo is not set`)

case "NOT_ENOUGH_MEMBERS":
return fmt(({ n }) => n`Error: Direttivo has not enough members!`)

case "TOO_MANY_MEMBERS":
return fmt(({ n }) => n`Error: Direttivo has too many members!`)

case "INTERNAL_SERVER_ERROR":
return fmt(({ n }) => n`Error: there was an internal error while fetching members of Direttivo.`)

case null:
break
}

const voters = direttivo.members.map((m) => ({
user: m.user
? {
id: m.userId,
first_name: m.user.firstName,
last_name: m.user.lastName,
username: m.user.username,
is_bot: m.user.isBot,
language_code: m.user.langCode,
}
: { id: m.userId },
isPresident: m.isPresident,
vote: undefined,
}))

if (!voters.some((v) => v.isPresident))
return fmt(
({ n, b }) => [b`Error: No member is President!`, n`${b`Members:`} ${voters.map((v) => v.user.id).join(" ")}`],
{
sep: "\n",
}
)

const banAll: BanAll = {
type,
outcome: "waiting",
reporter: reporter,
reason,
target,
voters,
state: {
successCount: 0,
failedCount: 0,
jobCount: 0,
},
}

const menu = await banAllMenu(banAll)
await this.log(this.topics.banAll, "———————————————")
const msg = await this.log(this.topics.banAll, getBanAllText(banAll), { reply_markup: menu })
const msg = await this.log(this.topics.banAll, getBanAllText(banAll))

if (!msg?.message_id) {
logger.error("[banall] There was an error when initiating banall, no msg.msgId")
return fmt(
({ n, b }) => [
b`${type} All ERROR!`,
n`Cannot log the message in tgLogger, therefore cannot start the procedure`,
n`This should be inspected as it should not never happen`,
],
{ sep: "\n" }
)
}

await modules.get("banAll").initiateBanAll(banAll, msg.message_id)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return fmt(
({ n, b, link }) => [
b`${type} All requested!`,
b`${type} All started!`,
msg
? n`Check ${link("here", `https://t.me/c/${this.groupId}/${this.topics.banAll}/${msg.message_id}`)}`
: undefined,
Expand Down
Loading