Skip to content

Add /closecommunity command so superadmins can close any community#807

Draft
Matobi98 wants to merge 5 commits into
lnp2pBot:mainfrom
Matobi98:closecommunity
Draft

Add /closecommunity command so superadmins can close any community#807
Matobi98 wants to merge 5 commits into
lnp2pBot:mainfrom
Matobi98:closecommunity

Conversation

@Matobi98
Copy link
Copy Markdown
Contributor

@Matobi98 Matobi98 commented May 18, 2026

Summary

Add a new /closecommunity command so superadmins can close any community by ID or Telegram group handle, with a confirmation step before executing the action.


Changes

New command: /closecommunity

Usage:
/closecommunity
/closecommunity @GroupUsername

  • Restricted to superadmins via superAdminMiddleware
  • Accepts either a MongoDB community ID or a @groupUsername
  • Before closing, shows a confirmation message with:
    • Community name
    • Total completed orders
    • Creator username
    • List of solvers
  • Inline buttons: Continue / Cancel
  • On confirmation: deletes the community and sends a DM to the owner notifying them of the closure

Bug fix: superAdminMiddleware with callback queries

validateSuperAdmin was reading ctx.update.message.from, which throws when the update comes from a button press (callback query context). The middleware now correctly falls back to callback_query.from when available.

Summary by CodeRabbit

  • New Features

    • Super-admins can enable or disable communities via new admin commands; creators receive notifications when their community is toggled.
    • Communities now have an enabled state (defaults to enabled) so disabled communities are treated as inactive in flows.
  • Behavior Changes

    • Several bot flows (info, earnings, order creation, settings) now ignore disabled communities and behave as if they don't exist.
  • Localization

    • Added translations for enable/disable notifications and status messages across supported languages.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

Walkthrough

Adds an enabled boolean to Community (default true), updates database lookups to ignore disabled communities, introduces superadmin /disablecommunity and /enablecommunity commands with creator notification and i18n strings, and passes Telegram callback user id into superAdmin validation.

Changes

Community enable/disable

Layer / File(s) Summary
Admin validation context
bot/middleware/user.ts
superAdminMiddleware extracts Telegram user id from callback queries and passes it to validateSuperAdmin(ctx, tgId).
Community model: enabled flag
models/community.ts
Adds enabled: boolean to ICommunity and CommunitySchema with default true.
DB lookups & UI paths
bot/modules/community/actions.ts, bot/modules/community/messages.ts, bot/modules/community/commands.ts, bot/modules/orders/scenes.ts, bot/modules/user/scenes/settings.ts
Replaces findById and similar queries with findOne({ _id, enabled: { $ne: false } }) across info, earnings/withdraw, order creation, user settings, and command validation so disabled communities are treated as not-found.
Enable/disable commands, wiring & locales
bot/modules/community/commands.ts, bot/modules/community/index.ts, locales/*
Adds disableCommunity and enableCommunity commands (shared community resolver, toggles community.enabled, saves, optionally notifies creator), wires commands behind superAdminMiddleware, and adds localized messages for enable/disable flows across supported locales.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes


Possibly related issues

  • #804: Implements community enable/disable behavior and admin commands referenced by the issue.

Possibly related PRs


Suggested reviewers

  • Catrya
  • grunch
  • Luquitasjeffrey

Poem

🐰 I hopped through code with a carrot-and-key,
Tucked enabled safe in Community tree.
Admins can toggle with a polite little ping,
Creators get notes when the bell starts to ring.
Hooray — tiny hops, big change, happy spring!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title claims to add a /closecommunity command, but the actual changeset implements /disablecommunity and /enablecommunity commands instead. The implementation disables/enables communities (toggling an enabled boolean field) rather than deleting them. Update the PR title to accurately reflect the final implementation: 'Add /disablecommunity and /enablecommunity commands for superadmins' or similar, as the actual feature differs significantly from what the title describes.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@bot/modules/community/commands.ts`:
- Around line 255-256: The code builds a RegExp from raw input and passes it to
Community.findOne({ group: regex }), allowing regex metacharacters in input to
change matching; fix by first normalizing and escaping the user-supplied group
username (remove an optional leading '@' from input, then escape all regex
metacharacters) before constructing the RegExp used as regex, so
Community.findOne still queries the group field case-insensitively but cannot be
manipulated by special characters.
- Around line 324-331: The DM to the owner (creator) via
ctx.telegram.sendMessage can throw and currently prevents sending the final
success reply; wrap the notification call in its own try/catch (or use .catch)
so failures are handled/logged without interrupting flow, e.g., call
ctx.telegram.sendMessage(creator.tg_id, ...) inside try { ... } catch (err) { /*
log err */ } and then always execute and return
ctx.reply(ctx.i18n.t('operation_successful')); ensure you reference creator,
ctx.telegram.sendMessage and ctx.reply when making the change.

In `@locales/en.yaml`:
- Line 731: Update the locale string key community_closed_by_admin to remove the
literal '@' before the template variable so it uses a plain ${communityName}
(since communityName is community.name and may contain spaces/special chars);
locate the community_closed_by_admin entry in locales/en.yaml and replace "An
administrator has closed your community @${communityName}" with "An
administrator has closed your community ${communityName}".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b31e5825-8461-4827-bc64-94a62b556484

📥 Commits

Reviewing files that changed from the base of the PR and between b5b498c and 696abba.

📒 Files selected for processing (13)
  • bot/middleware/user.ts
  • bot/modules/community/commands.ts
  • bot/modules/community/index.ts
  • locales/de.yaml
  • locales/en.yaml
  • locales/es.yaml
  • locales/fa.yaml
  • locales/fr.yaml
  • locales/it.yaml
  • locales/ko.yaml
  • locales/pt.yaml
  • locales/ru.yaml
  • locales/uk.yaml

Comment thread bot/modules/community/commands.ts Outdated
Comment thread bot/modules/community/commands.ts Outdated
Comment thread locales/en.yaml Outdated
Copy link
Copy Markdown
Collaborator

@Luquitasjeffrey Luquitasjeffrey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tACK

Comment thread bot/middleware/user.ts
next: () => void,
) => {
const admin = await validateSuperAdmin(ctx);
const tgId =
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary??

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh, now I understand, if the flows comes from a button click, it checks who clicked the button, LGTM

Copy link
Copy Markdown
Contributor

@mostronatorcoder mostronatorcoder Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found a real authorization regression in the new /closecommunity flow, so this cannot be approved yet. Please fix the issue below and then I can re-review.

@mostronatorcoder
Copy link
Copy Markdown
Contributor

I requested changes. Main blocker: the new /closecommunity confirmation message uses doNothingBtn for Cancel, but doNothingBtn is still wired with userMiddleware. This flow is restricted to superadmins, so a superadmin who is not represented as a normal bot user can press Cancel and get no handler at all. In other words, the cancel button can be dead for the intended actor. Please protect that callback with the same auth model as the flow that created it.

@Matobi98 Matobi98 marked this pull request as draft May 18, 2026 23:52
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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

♻️ Duplicate comments (1)
bot/modules/community/commands.ts (1)

50-56: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Escape groupName before building regex in setComm.

Raw user input in RegExp can change match behavior and select the wrong community.

Suggested fix
     if (groupName[0] == '@') {
       // Allow find communities case insensitive
-      const regex = new RegExp(['^', groupName, '$'].join(''), 'i');
+      const escapedGroupName = groupName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+      const regex = new RegExp(`^${escapedGroupName}$`, 'i');
       community = await Community.findOne({
         group: regex,
         enabled: { $ne: false },
       });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bot/modules/community/commands.ts` around lines 50 - 56, The code builds a
RegExp from raw user input (groupName) which allows regex metacharacters to
alter matching; in setComm, before creating the regex used in Community.findOne,
escape groupName (e.g., implement/consume an escapeRegExp utility and apply it
to groupName) so special characters are treated literally, then construct the
regex with '^' + escapedGroupName + '$' and the 'i' flag; update the regex
creation and use the escaped variable in the Community.findOne query.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@bot/modules/community/actions.ts`:
- Around line 126-132: The code may pass a null community into the wizard if
Community.findOne returns nothing; update the handler that calls
Community.findOne (using ctx.match?.[1]) to check the returned community before
calling ctx.scene.enter('ADD_EARNINGS_INVOICE_WIZARD_SCENE_ID', { community }),
and if community is null/undefined, bail out with a user-facing
error/notification (e.g., ctx.reply or ctx.answerCbQuery) or redirect elsewhere
instead of entering the scene; reference the Community lookup, ctx.match?.[1],
ctx.scene.enter and ADD_EARNINGS_INVOICE_WIZARD_SCENE_ID to locate where to add
the guard.

---

Duplicate comments:
In `@bot/modules/community/commands.ts`:
- Around line 50-56: The code builds a RegExp from raw user input (groupName)
which allows regex metacharacters to alter matching; in setComm, before creating
the regex used in Community.findOne, escape groupName (e.g., implement/consume
an escapeRegExp utility and apply it to groupName) so special characters are
treated literally, then construct the regex with '^' + escapedGroupName + '$'
and the 'i' flag; update the regex creation and use the escaped variable in the
Community.findOne query.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f1a251f7-caf1-4e25-bdf7-e44240309060

📥 Commits

Reviewing files that changed from the base of the PR and between 696abba and 7801963.

📒 Files selected for processing (17)
  • bot/modules/community/actions.ts
  • bot/modules/community/commands.ts
  • bot/modules/community/index.ts
  • bot/modules/community/messages.ts
  • bot/modules/orders/scenes.ts
  • bot/modules/user/scenes/settings.ts
  • locales/de.yaml
  • locales/en.yaml
  • locales/es.yaml
  • locales/fa.yaml
  • locales/fr.yaml
  • locales/it.yaml
  • locales/ko.yaml
  • locales/pt.yaml
  • locales/ru.yaml
  • locales/uk.yaml
  • models/community.ts
✅ Files skipped from review due to trivial changes (5)
  • locales/pt.yaml
  • locales/ko.yaml
  • locales/es.yaml
  • locales/fr.yaml
  • locales/uk.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • locales/ru.yaml

Comment on lines +126 to 132
const community = await Community.findOne({
_id: ctx.match?.[1],
enabled: { $ne: false },
});
ctx.scene.enter('ADD_EARNINGS_INVOICE_WIZARD_SCENE_ID', {
community,
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard null community before entering the earnings wizard.

If the community is disabled/deleted between UI render and callback click, this path can push null into wizard state and fail later.

Suggested fix
   const community = await Community.findOne({
     _id: ctx.match?.[1],
     enabled: { $ne: false },
   });
+  if (community == null) {
+    return ctx.reply(ctx.i18n.t('community_not_found'));
+  }
   ctx.scene.enter('ADD_EARNINGS_INVOICE_WIZARD_SCENE_ID', {
     community,
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bot/modules/community/actions.ts` around lines 126 - 132, The code may pass a
null community into the wizard if Community.findOne returns nothing; update the
handler that calls Community.findOne (using ctx.match?.[1]) to check the
returned community before calling
ctx.scene.enter('ADD_EARNINGS_INVOICE_WIZARD_SCENE_ID', { community }), and if
community is null/undefined, bail out with a user-facing error/notification
(e.g., ctx.reply or ctx.answerCbQuery) or redirect elsewhere instead of entering
the scene; reference the Community lookup, ctx.match?.[1], ctx.scene.enter and
ADD_EARNINGS_INVOICE_WIZARD_SCENE_ID to locate where to add the guard.

Comment thread bot/middleware/user.ts
Comment on lines +37 to +41
const tgId =
'callback_query' in ctx.update
? ctx.update.callback_query.from.id.toString()
: undefined;
const admin = await validateSuperAdmin(ctx, tgId);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this changes because now with the /disablecommuinty and /enablecommunity approach is no longer needed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants