-
Notifications
You must be signed in to change notification settings - Fork 0
feat: grant user special permissions #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds an interactive private /grant command and grant-management UI, integrates whitelisting into the auto-moderation stack, extends TgLogger with grant logging/menus/types, extracts a shared zod transformer, and bumps tooling deps and a TODO entry. Changes
Sequence Diagram(s)sequenceDiagram
participant User as Telegram User
participant Bot as Bot (/grant)
participant Backend as Backend API
participant Logger as TgLogger
participant Chat as Telegram Chat
User->>Bot: /grant `@target` (open UI)
Note over Bot: select start, time, duration, confirm
Bot->>Backend: POST /grants (userId, adderId, reason, since, until)
Backend-->>Bot: Grant created
Bot->>Logger: Log CREATE grant
Logger->>Chat: Post grant message / forward original
Chat-->>User: Grant confirmation + management actions
sequenceDiagram
participant Sender as Message Sender
participant Middleware as Moderation Middleware
participant Backend as Backend API
participant Logger as TgLogger
participant Chat as Telegram Chat
Sender->>Middleware: Send message in group
Middleware->>Backend: Check whitelist (role/admin/grants)
Backend-->>Middleware: Whitelist status
alt Whitelisted (creator/admin/grant)
Middleware->>Logger: Log grant usage
Middleware->>Chat: Skip enforcement or only log
else Not whitelisted
Middleware->>Chat: Mute/delete and/or reply
end
🚥 Pre-merge checks | ✅ 1 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds a grant system that allows Direttivo members to temporarily whitelist users from the auto-moderation stack. Users with active grants can send messages that would normally be blocked (e.g., promotional links, content flagged by AI moderation) without being muted or having their messages deleted.
Changes:
- Added
/grantcommand with an interactive menu for creating time-bound permission grants - Integrated grant checking into the auto-moderation whitelist system alongside role-based permissions
- Added grant activity logging (CREATE, USAGE, INTERRUPT) with interactive menus in the TG logger
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/types.ts | Exported shared numberOrString Zod transformer for command argument parsing |
| src/modules/tg-logger/types.ts | Added GrantLog discriminated union type for grant action logging |
| src/modules/tg-logger/index.ts | Implemented grants() logging method and fixed formatting issue in moderation action logs |
| src/modules/tg-logger/grants.ts | Created interactive menus for grant message handling and grant interruption |
| src/modules/index.ts | Registered grants topic (402) for TG logger |
| src/middlewares/auto-moderation-stack/index.ts | Added whitelist checking with grant support to link and harmful content handlers |
| src/commands/role.ts | Refactored to use shared numberOrString helper |
| src/commands/index.ts | Registered new grants command module |
| src/commands/grants.ts | Implemented /grant command with interactive date/time/duration selection |
| src/commands/banall.ts | Refactored to use shared numberOrString helper and fixed description |
| pnpm-lock.yaml | Updated @polinetwork/backend (0.12.0 → 0.13.1) and @biomejs/biome (2.2.4 → 2.3.11) |
| package.json | Updated dependencies and added --unsafe flag to biome check:fix script |
| biome.jsonc | Updated schema reference to match new biome version |
| TODO.md | Marked "do not delete Direttivo's allowed messages" as completed |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
src/middlewares/auto-moderation-stack/index.ts:241
- The
nonLatinHandlerdoes not check forctx.whitelistedbefore muting and deleting messages, unlike thelinkHandlerandharmfulContentHandler. This means that users with grants will still be moderated for non-latin character content, which is inconsistent with the feature's purpose of allowing whitelisted users to bypass auto-moderation. Consider adding a check similar to the other handlers to skip moderation for whitelisted users or log the grant usage if appropriate.
*/
private async nonLatinHandler(ctx: Filter<ModerationContext<C>, "message:text" | "message:caption">) {
const text = ctx.message.caption ?? ctx.message.text
const match = text.match(NON_LATIN.REGEX)
// 1. there are non latin characters
// 2. there are more than LENGTH_THR non-latin characters
// 3. the percentage of non-latin characters after the LENGTH_THR is more than PERCENTAGE_THR
// that should catch messages respecting this inequality: 0.2y + 8 < x ≤ y
// with x = number of non-latin characters, y = total length of the message
// longer messages can have more non-latin characters, but less in percentage
if (match && (match.length - NON_LATIN.LENGTH_THR) / text.length > NON_LATIN.PERCENTAGE_THR) {
// just delete the message and mute the user for 10 minutes
await mute({
ctx,
message: ctx.message,
target: ctx.from,
reason: "Message contains non-latin characters",
duration: duration.zod.parse(NON_LATIN.MUTE_DURATION),
from: ctx.me,
})
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/middlewares/auto-moderation-stack/index.ts (1)
220-241:nonLatinHandlerdoes not respect whitelist status.Unlike
linkHandlerandharmfulContentHandler, this handler doesn't checkctx.whitelistedand will mute granted users for non-Latin content. This appears inconsistent with the PR objective to allow whitelisted users to bypass auto-moderation.🐛 Suggested fix to respect whitelist
private async nonLatinHandler(ctx: Filter<ModerationContext<C>, "message:text" | "message:caption">) { + if (ctx.whitelisted) return const text = ctx.message.caption ?? ctx.message.text const match = text.match(NON_LATIN.REGEX)Alternatively, if granted users should still be logged for this action, follow the same pattern as
linkHandler.
🤖 Fix all issues with AI agents
In `@package.json`:
- Around line 15-16: The "check:fix" npm script currently applies unsafe fixes;
update the scripts so "check:fix" runs only safe autofixes (remove the --unsafe
flag) and add a new "check:fix:unsafe" script that runs "biome check --write
--unsafe" for deliberate unsafe autofix workflows; modify the package.json
"scripts" entries for "check:fix" and add "check:fix:unsafe" accordingly so safe
fixes are the default and unsafe fixes are explicit.
In `@src/commands/grants.ts`:
- Around line 107-111: The changeDuration handler (and other handlers in this
file that call Telegram methods) fires async operations like ctx.editMessageText
and ctx.menu.nav without awaiting them, which can produce unhandled rejections
if the message was edited/deleted; update changeDuration to await
ctx.editMessageText(...) and await any async ctx.menu.* or Telegram API calls,
and wrap these awaits in a try/catch to swallow/log errors; apply the same
change to the other similar blocks in this file where ctx.editMessageText,
ctx.deleteMessage, ctx.reply or ctx.menu.nav are invoked so all Telegram calls
are awaited and errors handled.
🧹 Nitpick comments (2)
src/commands/grants.ts (1)
127-127: Resolve the timezone TODO before release.Scheduling correctness depends on bot/user timezone alignment; happy to help implement this.
src/middlewares/auto-moderation-stack/index.ts (1)
93-105: Consider adding error handling for backend API calls.If the backend API is unavailable, these calls will throw and could prevent moderation from running entirely. Consider wrapping in try-catch and defaulting to
null(not whitelisted) on failure to ensure moderation remains active even during backend outages.♻️ Suggested defensive approach
private async isWhitelisted(ctx: ModerationContext<C>): Promise<WhitelistType | null> { const { status } = await ctx.getAuthor() if (status === "creator") return { role: "creator" } if (status === "administrator") return { role: "admin" } - const isAdmin = await api.tg.permissions.checkGroup.query({ userId: ctx.from.id, groupId: ctx.chatId }) - if (isAdmin) return { role: "admin" } - - const grant = await api.tg.grants.checkUser.query({ userId: ctx.from.id }) - if (grant.isGranted) return { role: "user" } + try { + const isAdmin = await api.tg.permissions.checkGroup.query({ userId: ctx.from.id, groupId: ctx.chatId }) + if (isAdmin) return { role: "admin" } + + const grant = await api.tg.grants.checkUser.query({ userId: ctx.from.id }) + if (grant.isGranted) return { role: "user" } + } catch { + // Backend unavailable - fail closed (not whitelisted) to maintain moderation + } return null }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/middlewares/auto-moderation-stack/index.ts (1)
221-241: Whitelist bypass is missing for non‑Latin moderation.
Whitelisted users can still be muted here, which conflicts with the new bypass semantics used in other handlers.🛠 Proposed fix
private async nonLatinHandler(ctx: Filter<ModerationContext<C>, "message:text" | "message:caption">) { + if (ctx.whitelisted) return const text = ctx.message.caption ?? ctx.message.text
🤖 Fix all issues with AI agents
In `@src/middlewares/auto-moderation-stack/index.ts`:
- Around line 94-105: The isWhitelisted method currently calls
api.tg.permissions.checkGroup.query and api.tg.grants.checkUser.query without
handling thrown errors, which can cause the middleware to fail closed; update
isWhitelisted (and keep ctx.getAuthor handling) to wrap the remote checks
(api.tg.permissions.checkGroup.query and api.tg.grants.checkUser.query) in
try/catch blocks and on any exception log or swallow the error and return null
(i.e. treat as not whitelisted) so moderation still runs; ensure the method
still returns { role: "creator" } or { role: "admin" } for ctx.getAuthor results
and { role: "user" } only when grant.isGranted is true, otherwise return null on
errors.
🧹 Nitpick comments (1)
src/middlewares/auto-moderation-stack/index.ts (1)
166-202: Confirm whitelist scope for harmful-content moderation.
Whitelisted users currently bypass mutes for harmful content (only logging USAGE). If grants are meant only to allow blocked links, consider still enforcing harmful-content actions or adding a scoped grant model.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 13 out of 14 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 13 out of 14 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
src/middlewares/auto-moderation-stack/index.ts:250
- The nonLatinHandler does not check for whitelisted users before applying moderation actions. This is inconsistent with the other handlers (linkHandler, harmfulContentHandler, multichatSpamHandler) which all check
ctx.whitelistedbefore taking action. Whitelisted users (admins and users with grants) should be allowed to bypass this check as well.
private async nonLatinHandler(ctx: Filter<ModerationContext<C>, "message:text" | "message:caption">) {
const text = ctx.message.caption ?? ctx.message.text
const match = text.match(NON_LATIN.REGEX)
// 1. there are non latin characters
// 2. there are more than LENGTH_THR non-latin characters
// 3. the percentage of non-latin characters after the LENGTH_THR is more than PERCENTAGE_THR
// that should catch messages respecting this inequality: 0.2y + 8 < x ≤ y
// with x = number of non-latin characters, y = total length of the message
// longer messages can have more non-latin characters, but less in percentage
if (match && (match.length - NON_LATIN.LENGTH_THR) / text.length > NON_LATIN.PERCENTAGE_THR) {
// just delete the message and mute the user for 10 minutes
await mute({
ctx,
message: ctx.message,
target: ctx.from,
reason: "Message contains non-latin characters",
duration: duration.zod.parse(NON_LATIN.MUTE_DURATION),
from: ctx.me,
})
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/middlewares/auto-moderation-stack/index.ts (1)
230-250: Whitelist is ignored in non‑Latin handler.Whitelisted users (admins/granted) can still be muted here, which breaks the “skip if whitelisted” policy used elsewhere. Consider short‑circuiting (or logging grant usage) like the other handlers.
🛠️ Suggested fix
private async nonLatinHandler(ctx: Filter<ModerationContext<C>, "message:text" | "message:caption">) { + if (ctx.whitelisted) return const text = ctx.message.caption ?? ctx.message.text const match = text.match(NON_LATIN.REGEX)
🤖 Fix all issues with AI agents
In `@src/commands/grants.ts`:
- Around line 163-168: The "Now" menu item captures convNow at creation which
can become stale and also fails to await changeStartTime; update the handler on
the menu created via conversation.menu("grants-start-time", ...) .text(...) so
it recomputes the current time inside an async handler (call await
conversation.now() or new Date() there), derive hours/minutes from that fresh
time, and await the call to changeStartTime(ctx, hours, minutes) so the selected
"Now" always uses the current time and the async operation is properly awaited.
- Around line 213-223: The grant creation API call (api.tg.grants.create.mutate)
inside the .text("✅ Confirm", async (ctx) => { ... }) handler is non‑idempotent
and must be executed inside conversation.external to avoid duplicate grants on
replay; update the handler to call await conversation.external(async () => {
return await api.tg.grants.create.mutate({...}).catch(...) }) so the external
call is isolated from conversation replay/resume logic and returns the same {
success, error } shape to the surrounding code.
In `@src/middlewares/auto-moderation-stack/index.ts`:
- Around line 59-68: The filter callback dereferences ctx.from.id which can be
undefined for channel/anonymous posts; update the callback in the filter used
here to first check for a missing ctx.from (e.g., if (!ctx.from) return true)
before comparing ctx.from.id to ctx.me.id, then proceed to call
this.isWhitelisted and set ctx.whitelisted as before; ensure you reference the
same filter callback, ctx.from, ctx.me, isWhitelisted and ctx.whitelisted
symbols so the short‑circuit prevents a crash.
♻️ Duplicate comments (1)
src/commands/grants.ts (1)
107-111: Await menu-triggered Telegram API calls to avoid unhandled rejections.Several handlers call
ctx.editMessageText/res.deleteMessagewithoutawait. If the message is already edited/deleted, these reject and can surface as unhandled rejections. Please await and handle failures; apply the same pattern across menu callbacks.🔧 Example fix (apply similarly elsewhere)
async function changeDuration(ctx: ConversationMenuContext<ConversationContext<"private">>, durationStr: string) { grantDuration = duration.zod.parse(durationStr) - ctx.editMessageText(baseMsg(), { reply_markup: ctx.msg?.reply_markup }) - ctx.menu.nav("grants-main") + await ctx.editMessageText(baseMsg(), { reply_markup: ctx.msg?.reply_markup }).catch(() => {}) + await ctx.menu.nav("grants-main") }Also applies to: 113-119, 122-131, 145-155, 161-162, 188-191
Enables Direttivo to grant user permissions to send not-allowed messages, in general to be whitelisted from the auto-moderation stack.
This can be used when Direttivo approves a promotional message to be sent across our groups, that might contain not-allowed links (e.g. forms.gle) that would cause the bot to delete the message.
We created a
/grantcommand to use thebackendgrants create endpoint and we use thetgLoggerto log CREATE, USAGE and INTERRUPT grant actions.More customization grant creation will be possible in the WIP admin dashboard