From ba02cef232ab0160b7390b9e1984f9e0157e4f1c Mon Sep 17 00:00:00 2001 From: Maxi Vila Date: Tue, 26 May 2026 01:53:31 -0300 Subject: [PATCH 1/7] feat: add disablecommunity/enablecommunity admin commands with soft-disable via enabled field --- bot/modules/community/actions.ts | 11 ++- bot/modules/community/commands.ts | 121 ++++++++++++++++++++++++++++-- bot/modules/community/index.ts | 13 +++- bot/modules/community/messages.ts | 10 ++- bot/modules/orders/scenes.ts | 5 +- locales/de.yaml | 4 + locales/en.yaml | 4 + locales/es.yaml | 4 + locales/fa.yaml | 4 + locales/fr.yaml | 4 + locales/it.yaml | 4 + locales/ko.yaml | 4 + locales/pt.yaml | 4 + locales/ru.yaml | 4 + locales/uk.yaml | 4 + models/community.ts | 2 + util/communityHelper.ts | 10 ++- 17 files changed, 199 insertions(+), 13 deletions(-) diff --git a/bot/modules/community/actions.ts b/bot/modules/community/actions.ts index e88c769a..0a8859f9 100644 --- a/bot/modules/community/actions.ts +++ b/bot/modules/community/actions.ts @@ -59,7 +59,10 @@ const getVolumeNDays = async ( export const onCommunityInfo = async (ctx: MainContext) => { const commId = ctx.match?.[1]; - const community = await Community.findById(commId); + const community = await Community.findOne({ + _id: commId, + enabled: { $ne: false }, + }); if (community === null) throw new Error('community not found'); const userCount = await User.countDocuments({ default_community_id: commId }); const orderCount = await getOrdersNDays(1, commId); @@ -120,7 +123,11 @@ export const onSetCommunity = async (ctx: CommunityContext) => { export const withdrawEarnings = async (ctx: CommunityContext) => { ctx.deleteMessage(); - const community = await Community.findById(ctx.match?.[1]); + const community = await Community.findOne({ + _id: ctx.match?.[1], + enabled: { $ne: false }, + }); + if (!community) return ctx.reply(ctx.i18n.t('community_not_found')); ctx.scene.enter('ADD_EARNINGS_INVOICE_WIZARD_SCENE_ID', { community, }); diff --git a/bot/modules/community/commands.ts b/bot/modules/community/commands.ts index 78e60525..96a7f1d0 100644 --- a/bot/modules/community/commands.ts +++ b/bot/modules/community/commands.ts @@ -1,11 +1,12 @@ /* eslint-disable no-underscore-dangle */ import { logger } from '../../../logger'; import { showUserCommunitiesMessage } from './messages'; -import { Community, Order } from '../../../models'; +import { Community, Order, User } from '../../../models'; import { validateParams, validateObjectId } from '../../validations'; import { MainContext } from '../../start'; import { CommunityContext } from './communityContext'; import { Telegraf } from 'telegraf'; +import { getUserI18nContext } from '../../../util'; async function getOrderCountByCommunity(): Promise { const data = await Order.aggregate([ @@ -21,6 +22,7 @@ async function findCommunities(currency: string) { const communities = await Community.find({ currencies: currency, public: true, + enabled: { $ne: false }, }); const orderCount = await getOrderCountByCommunity(); return communities.map(comm => { @@ -49,9 +51,15 @@ export const setComm = async (ctx: MainContext) => { if (groupName[0] == '@') { // Allow find communities case insensitive const regex = new RegExp(['^', groupName, '$'].join(''), 'i'); - community = await Community.findOne({ group: regex }); + community = await Community.findOne({ + group: regex, + enabled: { $ne: false }, + }); } else if (groupName[0] == '-') { - community = await Community.findOne({ group: groupName }); + community = await Community.findOne({ + group: groupName, + enabled: { $ne: false }, + }); } if (!community) { return await ctx.reply(ctx.i18n.t('community_not_found')); @@ -70,7 +78,11 @@ export const communityAdmin = async (ctx: CommunityContext) => { try { const [group] = (await validateParams(ctx, 2, '\\<_community_\\>'))!; const creator_id = ctx.user.id; - const [community] = await Community.find({ group, creator_id }); + const [community] = await Community.find({ + group, + creator_id, + enabled: { $ne: false }, + }); if (!community) throw new Error('CommunityNotFound'); await ctx.scene.enter('COMMUNITY_ADMIN', { community }); } catch (err: any) { @@ -89,7 +101,10 @@ export const myComms = async (ctx: MainContext) => { try { const { user } = ctx; - const communities = await Community.find({ creator_id: user._id }); + const communities = await Community.find({ + creator_id: user._id, + enabled: { $ne: false }, + }); if (!communities.length) return await ctx.reply(ctx.i18n.t('you_dont_have_communities')); @@ -147,6 +162,7 @@ export const updateCommunity = async ( const community = await Community.findOne({ _id: id, creator_id: user._id, + enabled: { $ne: false }, }); if (!community) { @@ -228,6 +244,7 @@ export const deleteCommunity = async (ctx: CommunityContext) => { const community = await Community.findOne({ _id: id, creator_id: ctx.user._id, + enabled: { $ne: false }, }); if (!community) { @@ -241,6 +258,99 @@ export const deleteCommunity = async (ctx: CommunityContext) => { } }; +async function findCommunityByInput( + ctx: MainContext, + input: string, +): Promise { + if (input[0] === '@') { + const regex = new RegExp( + `^${input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, + 'i', + ); + return Community.findOne({ group: regex }); + } + if (!(await validateObjectId(ctx, input))) return null; + return Community.findOne({ _id: input }); +} + +export const disableCommunity = async (ctx: MainContext) => { + try { + const [input] = (await validateParams( + ctx, + 2, + '\\<_community id \\| @groupUsername_\\>', + ))!; + if (!input) return; + + const community = await findCommunityByInput(ctx, input); + if (!community) return ctx.reply(ctx.i18n.t('community_not_found')); + if (community.enabled === false) + return ctx.reply(ctx.i18n.t('community_already_disabled')); + + community.enabled = false; + await community.save(); + + const creator = await User.findById(community.creator_id); + if (creator) { + try { + const creatorI18n = await getUserI18nContext(creator); + await ctx.telegram.sendMessage( + creator.tg_id, + creatorI18n.t('community_disabled_by_admin', { + communityName: community.name, + }), + ); + } catch (notifyError) { + logger.error(notifyError); + } + } + + return ctx.reply(ctx.i18n.t('operation_successful')); + } catch (error) { + logger.error(error); + await ctx.reply(ctx.i18n.t('generic_error')); + } +}; + +export const enableCommunity = async (ctx: MainContext) => { + try { + const [input] = (await validateParams( + ctx, + 2, + '\\<_community id \\| @groupUsername_\\>', + ))!; + if (!input) return; + + const community = await findCommunityByInput(ctx, input); + if (!community) return ctx.reply(ctx.i18n.t('community_not_found')); + if (community.enabled !== false) + return ctx.reply(ctx.i18n.t('community_already_enabled')); + + community.enabled = true; + await community.save(); + + const creator = await User.findById(community.creator_id); + if (creator) { + try { + const creatorI18n = await getUserI18nContext(creator); + await ctx.telegram.sendMessage( + creator.tg_id, + creatorI18n.t('community_enabled_by_admin', { + communityName: community.name, + }), + ); + } catch (notifyError) { + logger.error(notifyError); + } + } + + return ctx.reply(ctx.i18n.t('operation_successful')); + } catch (error) { + logger.error(error); + await ctx.reply(ctx.i18n.t('generic_error')); + } +}; + export const changeVisibility = async (ctx: CommunityContext) => { try { ctx.deleteMessage(); @@ -251,6 +361,7 @@ export const changeVisibility = async (ctx: CommunityContext) => { const community = await Community.findOne({ _id: id, creator_id: ctx.user._id, + enabled: { $ne: false }, }); if (!community) { diff --git a/bot/modules/community/index.ts b/bot/modules/community/index.ts index e9ac6da4..f6668d8d 100644 --- a/bot/modules/community/index.ts +++ b/bot/modules/community/index.ts @@ -1,5 +1,5 @@ import { Telegraf } from 'telegraf'; -import { userMiddleware } from '../../middleware/user'; +import { userMiddleware, superAdminMiddleware } from '../../middleware/user'; import * as actions from './actions'; import * as commands from './commands'; import { @@ -65,6 +65,17 @@ export const configure = (bot: Telegraf) => { }, ); + bot.command( + 'disablecommunity', + superAdminMiddleware, + commands.disableCommunity, + ); + bot.command( + 'enablecommunity', + superAdminMiddleware, + commands.enableCommunity, + ); + bot.command('findcomms', userMiddleware, commands.findCommunity); bot.action( /^communityInfo_([0-9a-f]{24})$/, diff --git a/bot/modules/community/messages.ts b/bot/modules/community/messages.ts index 94b2c8d6..da2d3d5f 100644 --- a/bot/modules/community/messages.ts +++ b/bot/modules/community/messages.ts @@ -50,7 +50,10 @@ export const updateCommunityMessage = async (ctx: MainContext) => { try { await ctx.deleteMessage(); const id = ctx.match?.[1]; - const community = await Community.findById(id); + const community = await Community.findOne({ + _id: id, + enabled: { $ne: false }, + }); if (community == null) throw new Error('community was not found'); let text = ctx.i18n.t('community') + `: ${community.name}\n`; text += ctx.i18n.t('what_to_do'); @@ -170,7 +173,10 @@ export const earningsMessage = async (ctx: MainContext) => { if (isScheduled) return await ctx.reply(ctx.i18n.t('invoice_already_being_paid')); - const community = await Community.findById(communityId); + const community = await Community.findOne({ + _id: communityId, + enabled: { $ne: false }, + }); if (community == null) throw new Error('community was not found'); const button = community.earnings > 0 diff --git a/bot/modules/orders/scenes.ts b/bot/modules/orders/scenes.ts index 1eb6a497..d66823d5 100644 --- a/bot/modules/orders/scenes.ts +++ b/bot/modules/orders/scenes.ts @@ -154,7 +154,10 @@ const createOrderSteps = { const stateComm = ctx.wizard.state.community; const loadedComm = !stateComm && user?.default_community_id - ? await Community.findById(user.default_community_id) + ? await Community.findOne({ + _id: user.default_community_id, + enabled: { $ne: false }, + }) : null; const community = stateComm ?? loadedComm; if (loadedComm) ctx.wizard.state.community = loadedComm; diff --git a/locales/de.yaml b/locales/de.yaml index 07e691ab..d26f73a1 100644 --- a/locales/de.yaml +++ b/locales/de.yaml @@ -717,3 +717,7 @@ payment_methods_saved: "Zahlungsmethoden gespeichert ✅" custom_payment_method: "✍️ Benutzerdefinierte Zahlungsmethode" payment_methods_reset: "Zahlungsmethoden entfernt. Benutzer können jetzt beliebige Zahlungsmethoden frei eingeben." payment_methods_wizard_commands: "/reset — alle Zahlungsmethoden entfernen und Standardverhalten wiederherstellen\n/exit — ohne Speichern beenden" +community_disabled_by_admin: An administrator has disabled your community ${communityName} +community_enabled_by_admin: An administrator has re-enabled your community ${communityName} +community_already_disabled: This community is already disabled +community_already_enabled: This community is already enabled diff --git a/locales/en.yaml b/locales/en.yaml index def6f4cd..e7dbe2ff 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -722,3 +722,7 @@ payment_methods_saved: "Payment methods saved ✅" custom_payment_method: "✍️ Custom payment method" payment_methods_reset: "Payment methods removed. Users can now enter any payment method freely." payment_methods_wizard_commands: "/reset — remove all payment methods and restore default behavior\n/exit — exit without saving" +community_disabled_by_admin: An administrator has disabled your community ${communityName} +community_enabled_by_admin: An administrator has re-enabled your community ${communityName} +community_already_disabled: This community is already disabled +community_already_enabled: This community is already enabled diff --git a/locales/es.yaml b/locales/es.yaml index 3825ad63..78492c9f 100644 --- a/locales/es.yaml +++ b/locales/es.yaml @@ -719,3 +719,7 @@ payment_methods_saved: "Métodos de pago guardados ✅" custom_payment_method: "✍️ Método de pago personalizado" payment_methods_reset: "Métodos de pago eliminados. Los usuarios ahora pueden ingresar cualquier método de pago libremente." payment_methods_wizard_commands: "/reset — eliminar todos los métodos de pago y restaurar el comportamiento predeterminado\n/exit — salir sin guardar" +community_disabled_by_admin: An administrator has disabled your community ${communityName} +community_enabled_by_admin: An administrator has re-enabled your community ${communityName} +community_already_disabled: This community is already disabled +community_already_enabled: This community is already enabled diff --git a/locales/fa.yaml b/locales/fa.yaml index 463206de..392fb16f 100644 --- a/locales/fa.yaml +++ b/locales/fa.yaml @@ -816,3 +816,7 @@ payment_methods_saved: "روش‌های پرداخت ذخیره شد ✅" custom_payment_method: "✍️ روش پرداخت سفارشی" payment_methods_reset: "روش‌های پرداخت حذف شدند. کاربران اکنون می‌توانند هر روش پرداختی را آزادانه وارد کنند." payment_methods_wizard_commands: "/reset — حذف همه روش‌های پرداخت و بازگرداندن رفتار پیش‌فرض\n/exit — خروج بدون ذخیره" +community_disabled_by_admin: An administrator has disabled your community ${communityName} +community_enabled_by_admin: An administrator has re-enabled your community ${communityName} +community_already_disabled: This community is already disabled +community_already_enabled: This community is already enabled diff --git a/locales/fr.yaml b/locales/fr.yaml index e96d3e0b..33a83188 100644 --- a/locales/fr.yaml +++ b/locales/fr.yaml @@ -716,3 +716,7 @@ payment_methods_saved: "Méthodes de paiement enregistrées ✅" custom_payment_method: "✍️ Méthode de paiement personnalisée" payment_methods_reset: "Méthodes de paiement supprimées. Les utilisateurs peuvent désormais saisir n'importe quelle méthode de paiement librement." payment_methods_wizard_commands: "/reset — supprimer toutes les méthodes de paiement et restaurer le comportement par défaut\n/exit — quitter sans enregistrer" +community_disabled_by_admin: An administrator has disabled your community ${communityName} +community_enabled_by_admin: An administrator has re-enabled your community ${communityName} +community_already_disabled: This community is already disabled +community_already_enabled: This community is already enabled diff --git a/locales/it.yaml b/locales/it.yaml index 54aae511..33053d30 100644 --- a/locales/it.yaml +++ b/locales/it.yaml @@ -714,3 +714,7 @@ payment_methods_saved: "Metodi di pagamento salvati ✅" custom_payment_method: "✍️ Metodo di pagamento personalizzato" payment_methods_reset: "Metodi di pagamento rimossi. Gli utenti possono ora inserire qualsiasi metodo di pagamento liberamente." payment_methods_wizard_commands: "/reset — rimuovere tutti i metodi di pagamento e ripristinare il comportamento predefinito\n/exit — uscire senza salvare" +community_disabled_by_admin: An administrator has disabled your community ${communityName} +community_enabled_by_admin: An administrator has re-enabled your community ${communityName} +community_already_disabled: This community is already disabled +community_already_enabled: This community is already enabled diff --git a/locales/ko.yaml b/locales/ko.yaml index 8880aa1d..69f2c0de 100644 --- a/locales/ko.yaml +++ b/locales/ko.yaml @@ -714,3 +714,7 @@ payment_methods_saved: "결제 방법이 저장되었습니다 ✅" custom_payment_method: "✍️ 사용자 지정 결제 방법" payment_methods_reset: "결제 방법이 삭제되었습니다. 이제 사용자는 어떤 결제 방법이든 자유롭게 입력할 수 있습니다." payment_methods_wizard_commands: "/reset — 모든 결제 방법을 삭제하고 기본 동작을 복원합니다\n/exit — 저장하지 않고 종료" +community_disabled_by_admin: An administrator has disabled your community ${communityName} +community_enabled_by_admin: An administrator has re-enabled your community ${communityName} +community_already_disabled: This community is already disabled +community_already_enabled: This community is already enabled diff --git a/locales/pt.yaml b/locales/pt.yaml index 7cfdebc2..b1a5cd8d 100644 --- a/locales/pt.yaml +++ b/locales/pt.yaml @@ -716,3 +716,7 @@ payment_methods_saved: "Métodos de pagamento salvos ✅" custom_payment_method: "✍️ Método de pagamento personalizado" payment_methods_reset: "Métodos de pagamento removidos. Os usuários agora podem inserir qualquer método de pagamento livremente." payment_methods_wizard_commands: "/reset — remover todos os métodos de pagamento e restaurar o comportamento padrão\n/exit — sair sem salvar" +community_disabled_by_admin: An administrator has disabled your community ${communityName} +community_enabled_by_admin: An administrator has re-enabled your community ${communityName} +community_already_disabled: This community is already disabled +community_already_enabled: This community is already enabled diff --git a/locales/ru.yaml b/locales/ru.yaml index 09956b27..f3260065 100644 --- a/locales/ru.yaml +++ b/locales/ru.yaml @@ -717,3 +717,7 @@ payment_methods_saved: "Способы оплаты сохранены ✅" custom_payment_method: "✍️ Пользовательский способ оплаты" payment_methods_reset: "Способы оплаты удалены. Пользователи теперь могут свободно вводить любой способ оплаты." payment_methods_wizard_commands: "/reset — удалить все способы оплаты и восстановить поведение по умолчанию\n/exit — выйти без сохранения" +community_disabled_by_admin: An administrator has disabled your community ${communityName} +community_enabled_by_admin: An administrator has re-enabled your community ${communityName} +community_already_disabled: This community is already disabled +community_already_enabled: This community is already enabled diff --git a/locales/uk.yaml b/locales/uk.yaml index 1176d9e4..b7a690ae 100644 --- a/locales/uk.yaml +++ b/locales/uk.yaml @@ -713,3 +713,7 @@ payment_methods_saved: "Способи оплати збережено ✅" custom_payment_method: "✍️ Власний спосіб оплати" payment_methods_reset: "Способи оплати видалено. Користувачі тепер можуть вільно вводити будь-який спосіб оплати." payment_methods_wizard_commands: "/reset — видалити всі способи оплати та відновити поведінку за замовчуванням\n/exit — вийти без збереження" +community_disabled_by_admin: An administrator has disabled your community ${communityName} +community_enabled_by_admin: An administrator has re-enabled your community ${communityName} +community_already_disabled: This community is already disabled +community_already_enabled: This community is already enabled diff --git a/models/community.ts b/models/community.ts index 0434174e..71dfef2e 100644 --- a/models/community.ts +++ b/models/community.ts @@ -47,6 +47,7 @@ export interface ICommunity extends Document { solvers: Types.DocumentArray; banned_users: Types.DocumentArray; public: boolean; + enabled?: boolean; currencies: Array; payment_methods: Array; created_at: Date; @@ -78,6 +79,7 @@ const CommunitySchema = new Schema({ solvers: [usernameIdSchema], // users that are dispute solvers banned_users: [usernameIdSchema], // users that are banned from the community public: { type: Boolean, default: true }, + enabled: { type: Boolean }, currencies: { type: [String], required: true, diff --git a/util/communityHelper.ts b/util/communityHelper.ts index 76a18e4c..923c6655 100644 --- a/util/communityHelper.ts +++ b/util/communityHelper.ts @@ -27,14 +27,20 @@ export const getCommunityInfo = async ( if (chatType !== 'private' && chat?.username) { // Group chat - find community by group name const regex = new RegExp(`^@${chat.username}$`, 'i'); - community = await Community.findOne({ group: regex }); + community = await Community.findOne({ + group: regex, + enabled: { $ne: false }, + }); if (community) { communityId = community._id; } } else if (user.default_community_id) { // Private chat with default community communityId = user.default_community_id; - community = await Community.findById(communityId); + community = await Community.findOne({ + _id: communityId, + enabled: { $ne: false }, + }); // If community not found, clear the user's default if (!community) { From 7572c36d0d87ae377d9ad8e304d82981722143c5 Mon Sep 17 00:00:00 2001 From: Maxi Vila Date: Tue, 26 May 2026 02:14:19 -0300 Subject: [PATCH 2/7] i18n: translate disable/enable community admin messages to all supported languages --- locales/de.yaml | 8 ++++---- locales/es.yaml | 8 ++++---- locales/fa.yaml | 8 ++++---- locales/fr.yaml | 8 ++++---- locales/it.yaml | 8 ++++---- locales/ko.yaml | 8 ++++---- locales/pt.yaml | 8 ++++---- locales/ru.yaml | 8 ++++---- locales/uk.yaml | 8 ++++---- 9 files changed, 36 insertions(+), 36 deletions(-) diff --git a/locales/de.yaml b/locales/de.yaml index d26f73a1..b42ce6ca 100644 --- a/locales/de.yaml +++ b/locales/de.yaml @@ -717,7 +717,7 @@ payment_methods_saved: "Zahlungsmethoden gespeichert ✅" custom_payment_method: "✍️ Benutzerdefinierte Zahlungsmethode" payment_methods_reset: "Zahlungsmethoden entfernt. Benutzer können jetzt beliebige Zahlungsmethoden frei eingeben." payment_methods_wizard_commands: "/reset — alle Zahlungsmethoden entfernen und Standardverhalten wiederherstellen\n/exit — ohne Speichern beenden" -community_disabled_by_admin: An administrator has disabled your community ${communityName} -community_enabled_by_admin: An administrator has re-enabled your community ${communityName} -community_already_disabled: This community is already disabled -community_already_enabled: This community is already enabled +community_disabled_by_admin: Ein Administrator hat deine Community ${communityName} deaktiviert +community_enabled_by_admin: Ein Administrator hat deine Community ${communityName} wieder aktiviert +community_already_disabled: Diese Community ist bereits deaktiviert +community_already_enabled: Diese Community ist bereits aktiviert diff --git a/locales/es.yaml b/locales/es.yaml index 78492c9f..e54d5be9 100644 --- a/locales/es.yaml +++ b/locales/es.yaml @@ -719,7 +719,7 @@ payment_methods_saved: "Métodos de pago guardados ✅" custom_payment_method: "✍️ Método de pago personalizado" payment_methods_reset: "Métodos de pago eliminados. Los usuarios ahora pueden ingresar cualquier método de pago libremente." payment_methods_wizard_commands: "/reset — eliminar todos los métodos de pago y restaurar el comportamiento predeterminado\n/exit — salir sin guardar" -community_disabled_by_admin: An administrator has disabled your community ${communityName} -community_enabled_by_admin: An administrator has re-enabled your community ${communityName} -community_already_disabled: This community is already disabled -community_already_enabled: This community is already enabled +community_disabled_by_admin: Un administrador ha deshabilitado tu comunidad ${communityName} +community_enabled_by_admin: Un administrador ha reactivado tu comunidad ${communityName} +community_already_disabled: Esta comunidad ya está deshabilitada +community_already_enabled: Esta comunidad ya está habilitada diff --git a/locales/fa.yaml b/locales/fa.yaml index 392fb16f..4d800417 100644 --- a/locales/fa.yaml +++ b/locales/fa.yaml @@ -816,7 +816,7 @@ payment_methods_saved: "روش‌های پرداخت ذخیره شد ✅" custom_payment_method: "✍️ روش پرداخت سفارشی" payment_methods_reset: "روش‌های پرداخت حذف شدند. کاربران اکنون می‌توانند هر روش پرداختی را آزادانه وارد کنند." payment_methods_wizard_commands: "/reset — حذف همه روش‌های پرداخت و بازگرداندن رفتار پیش‌فرض\n/exit — خروج بدون ذخیره" -community_disabled_by_admin: An administrator has disabled your community ${communityName} -community_enabled_by_admin: An administrator has re-enabled your community ${communityName} -community_already_disabled: This community is already disabled -community_already_enabled: This community is already enabled +community_disabled_by_admin: یک مدیر جامعه ${communityName} شما را غیرفعال کرد +community_enabled_by_admin: یک مدیر جامعه ${communityName} شما را دوباره فعال کرد +community_already_disabled: این جامعه از قبل غیرفعال است +community_already_enabled: این جامعه از قبل فعال است diff --git a/locales/fr.yaml b/locales/fr.yaml index 33a83188..001c4dad 100644 --- a/locales/fr.yaml +++ b/locales/fr.yaml @@ -716,7 +716,7 @@ payment_methods_saved: "Méthodes de paiement enregistrées ✅" custom_payment_method: "✍️ Méthode de paiement personnalisée" payment_methods_reset: "Méthodes de paiement supprimées. Les utilisateurs peuvent désormais saisir n'importe quelle méthode de paiement librement." payment_methods_wizard_commands: "/reset — supprimer toutes les méthodes de paiement et restaurer le comportement par défaut\n/exit — quitter sans enregistrer" -community_disabled_by_admin: An administrator has disabled your community ${communityName} -community_enabled_by_admin: An administrator has re-enabled your community ${communityName} -community_already_disabled: This community is already disabled -community_already_enabled: This community is already enabled +community_disabled_by_admin: Un administrateur a désactivé votre communauté ${communityName} +community_enabled_by_admin: Un administrateur a réactivé votre communauté ${communityName} +community_already_disabled: Cette communauté est déjà désactivée +community_already_enabled: Cette communauté est déjà activée diff --git a/locales/it.yaml b/locales/it.yaml index 33053d30..6b51b9bc 100644 --- a/locales/it.yaml +++ b/locales/it.yaml @@ -714,7 +714,7 @@ payment_methods_saved: "Metodi di pagamento salvati ✅" custom_payment_method: "✍️ Metodo di pagamento personalizzato" payment_methods_reset: "Metodi di pagamento rimossi. Gli utenti possono ora inserire qualsiasi metodo di pagamento liberamente." payment_methods_wizard_commands: "/reset — rimuovere tutti i metodi di pagamento e ripristinare il comportamento predefinito\n/exit — uscire senza salvare" -community_disabled_by_admin: An administrator has disabled your community ${communityName} -community_enabled_by_admin: An administrator has re-enabled your community ${communityName} -community_already_disabled: This community is already disabled -community_already_enabled: This community is already enabled +community_disabled_by_admin: Un amministratore ha disabilitato la tua comunità ${communityName} +community_enabled_by_admin: Un amministratore ha riabilitato la tua comunità ${communityName} +community_already_disabled: Questa comunità è già disabilitata +community_already_enabled: Questa comunità è già abilitata diff --git a/locales/ko.yaml b/locales/ko.yaml index 69f2c0de..e222b876 100644 --- a/locales/ko.yaml +++ b/locales/ko.yaml @@ -714,7 +714,7 @@ payment_methods_saved: "결제 방법이 저장되었습니다 ✅" custom_payment_method: "✍️ 사용자 지정 결제 방법" payment_methods_reset: "결제 방법이 삭제되었습니다. 이제 사용자는 어떤 결제 방법이든 자유롭게 입력할 수 있습니다." payment_methods_wizard_commands: "/reset — 모든 결제 방법을 삭제하고 기본 동작을 복원합니다\n/exit — 저장하지 않고 종료" -community_disabled_by_admin: An administrator has disabled your community ${communityName} -community_enabled_by_admin: An administrator has re-enabled your community ${communityName} -community_already_disabled: This community is already disabled -community_already_enabled: This community is already enabled +community_disabled_by_admin: 관리자가 커뮤니티 ${communityName}을(를) 비활성화했습니다 +community_enabled_by_admin: 관리자가 커뮤니티 ${communityName}을(를) 다시 활성화했습니다 +community_already_disabled: 이 커뮤니티는 이미 비활성화되어 있습니다 +community_already_enabled: 이 커뮤니티는 이미 활성화되어 있습니다 diff --git a/locales/pt.yaml b/locales/pt.yaml index b1a5cd8d..daeb3290 100644 --- a/locales/pt.yaml +++ b/locales/pt.yaml @@ -716,7 +716,7 @@ payment_methods_saved: "Métodos de pagamento salvos ✅" custom_payment_method: "✍️ Método de pagamento personalizado" payment_methods_reset: "Métodos de pagamento removidos. Os usuários agora podem inserir qualquer método de pagamento livremente." payment_methods_wizard_commands: "/reset — remover todos os métodos de pagamento e restaurar o comportamento padrão\n/exit — sair sem salvar" -community_disabled_by_admin: An administrator has disabled your community ${communityName} -community_enabled_by_admin: An administrator has re-enabled your community ${communityName} -community_already_disabled: This community is already disabled -community_already_enabled: This community is already enabled +community_disabled_by_admin: Um administrador desativou sua comunidade ${communityName} +community_enabled_by_admin: Um administrador reativou sua comunidade ${communityName} +community_already_disabled: Esta comunidade já está desativada +community_already_enabled: Esta comunidade já está ativada diff --git a/locales/ru.yaml b/locales/ru.yaml index f3260065..4d7594f1 100644 --- a/locales/ru.yaml +++ b/locales/ru.yaml @@ -717,7 +717,7 @@ payment_methods_saved: "Способы оплаты сохранены ✅" custom_payment_method: "✍️ Пользовательский способ оплаты" payment_methods_reset: "Способы оплаты удалены. Пользователи теперь могут свободно вводить любой способ оплаты." payment_methods_wizard_commands: "/reset — удалить все способы оплаты и восстановить поведение по умолчанию\n/exit — выйти без сохранения" -community_disabled_by_admin: An administrator has disabled your community ${communityName} -community_enabled_by_admin: An administrator has re-enabled your community ${communityName} -community_already_disabled: This community is already disabled -community_already_enabled: This community is already enabled +community_disabled_by_admin: Администратор отключил ваше сообщество ${communityName} +community_enabled_by_admin: Администратор повторно включил ваше сообщество ${communityName} +community_already_disabled: Это сообщество уже отключено +community_already_enabled: Это сообщество уже включено diff --git a/locales/uk.yaml b/locales/uk.yaml index b7a690ae..aa616590 100644 --- a/locales/uk.yaml +++ b/locales/uk.yaml @@ -713,7 +713,7 @@ payment_methods_saved: "Способи оплати збережено ✅" custom_payment_method: "✍️ Власний спосіб оплати" payment_methods_reset: "Способи оплати видалено. Користувачі тепер можуть вільно вводити будь-який спосіб оплати." payment_methods_wizard_commands: "/reset — видалити всі способи оплати та відновити поведінку за замовчуванням\n/exit — вийти без збереження" -community_disabled_by_admin: An administrator has disabled your community ${communityName} -community_enabled_by_admin: An administrator has re-enabled your community ${communityName} -community_already_disabled: This community is already disabled -community_already_enabled: This community is already enabled +community_disabled_by_admin: Адміністратор вимкнув вашу спільноту ${communityName} +community_enabled_by_admin: Адміністратор повторно увімкнув вашу спільноту ${communityName} +community_already_disabled: Ця спільнота вже вимкнена +community_already_enabled: Ця спільнота вже увімкнена From d4c48d945a908ffd6c0f3d21810e6dad0562604c Mon Sep 17 00:00:00 2001 From: Maxi Vila Date: Tue, 26 May 2026 02:24:42 -0300 Subject: [PATCH 3/7] fix: reply with community_not_found instead of throwing on disabled/missing community --- bot/modules/community/actions.ts | 2 +- bot/modules/community/commands.ts | 12 +++++++++--- bot/modules/community/messages.ts | 6 ++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/bot/modules/community/actions.ts b/bot/modules/community/actions.ts index 0a8859f9..c0aef8b9 100644 --- a/bot/modules/community/actions.ts +++ b/bot/modules/community/actions.ts @@ -63,7 +63,7 @@ export const onCommunityInfo = async (ctx: MainContext) => { _id: commId, enabled: { $ne: false }, }); - if (community === null) throw new Error('community not found'); + if (community === null) return ctx.reply(ctx.i18n.t('community_not_found')); const userCount = await User.countDocuments({ default_community_id: commId }); const orderCount = await getOrdersNDays(1, commId); const volume = await getVolumeNDays(1, commId); diff --git a/bot/modules/community/commands.ts b/bot/modules/community/commands.ts index 96a7f1d0..eb83b986 100644 --- a/bot/modules/community/commands.ts +++ b/bot/modules/community/commands.ts @@ -267,7 +267,9 @@ async function findCommunityByInput( `^${input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'i', ); - return Community.findOne({ group: regex }); + const matches = await Community.find({ group: regex }).limit(2); + if (matches.length > 1) throw new Error('AmbiguousCommunityInput'); + return matches[0] ?? null; } if (!(await validateObjectId(ctx, input))) return null; return Community.findOne({ _id: input }); @@ -306,7 +308,9 @@ export const disableCommunity = async (ctx: MainContext) => { } return ctx.reply(ctx.i18n.t('operation_successful')); - } catch (error) { + } catch (error: any) { + if (error.message === 'AmbiguousCommunityInput') + return ctx.reply(ctx.i18n.t('ambiguous_community_input')); logger.error(error); await ctx.reply(ctx.i18n.t('generic_error')); } @@ -345,7 +349,9 @@ export const enableCommunity = async (ctx: MainContext) => { } return ctx.reply(ctx.i18n.t('operation_successful')); - } catch (error) { + } catch (error: any) { + if (error.message === 'AmbiguousCommunityInput') + return ctx.reply(ctx.i18n.t('ambiguous_community_input')); logger.error(error); await ctx.reply(ctx.i18n.t('generic_error')); } diff --git a/bot/modules/community/messages.ts b/bot/modules/community/messages.ts index da2d3d5f..90ce18da 100644 --- a/bot/modules/community/messages.ts +++ b/bot/modules/community/messages.ts @@ -54,7 +54,8 @@ export const updateCommunityMessage = async (ctx: MainContext) => { _id: id, enabled: { $ne: false }, }); - if (community == null) throw new Error('community was not found'); + if (community == null) + return await ctx.reply(ctx.i18n.t('community_not_found')); let text = ctx.i18n.t('community') + `: ${community.name}\n`; text += ctx.i18n.t('what_to_do'); const visibilityText = community.public @@ -177,7 +178,8 @@ export const earningsMessage = async (ctx: MainContext) => { _id: communityId, enabled: { $ne: false }, }); - if (community == null) throw new Error('community was not found'); + if (community == null) + return await ctx.reply(ctx.i18n.t('community_not_found')); const button = community.earnings > 0 ? { From 3abe467ba1a6d4219777490700df17a885c5336d Mon Sep 17 00:00:00 2001 From: Maxi Vila Date: Tue, 26 May 2026 08:25:25 -0300 Subject: [PATCH 4/7] fix: preserve default_community_id on disabled community and revalidate before setCommunity --- bot/modules/community/actions.ts | 5 +++++ util/communityHelper.ts | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/bot/modules/community/actions.ts b/bot/modules/community/actions.ts index c0aef8b9..6cc0b081 100644 --- a/bot/modules/community/actions.ts +++ b/bot/modules/community/actions.ts @@ -114,6 +114,11 @@ export const onCommunityInfo = async (ctx: MainContext) => { export const onSetCommunity = async (ctx: CommunityContext) => { const tgId = (ctx.update as any).callback_query.from.id; const defaultCommunityId = ctx.match?.[1]; + const community = await Community.findOne({ + _id: defaultCommunityId, + enabled: { $ne: false }, + }); + if (!community) return ctx.reply(ctx.i18n.t('community_not_found')); await User.findOneAndUpdate( { tg_id: tgId }, { default_community_id: defaultCommunityId }, diff --git a/util/communityHelper.ts b/util/communityHelper.ts index 923c6655..78c84d6d 100644 --- a/util/communityHelper.ts +++ b/util/communityHelper.ts @@ -42,10 +42,7 @@ export const getCommunityInfo = async ( enabled: { $ne: false }, }); - // If community not found, clear the user's default if (!community) { - user.default_community_id = undefined; - await user.save(); communityId = undefined; } } From eca950418035e23f995e43cc73d3089e0a800cf1 Mon Sep 17 00:00:00 2001 From: Maxi Vila Date: Tue, 26 May 2026 08:27:43 -0300 Subject: [PATCH 5/7] revert: restore default_community_id cleanup on disabled community lookup --- util/communityHelper.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/communityHelper.ts b/util/communityHelper.ts index 78c84d6d..60926e79 100644 --- a/util/communityHelper.ts +++ b/util/communityHelper.ts @@ -43,6 +43,8 @@ export const getCommunityInfo = async ( }); if (!community) { + user.default_community_id = undefined; + await user.save(); communityId = undefined; } } From 0d76aa598cd37c69dbfbc4352b46f7c043966afb Mon Sep 17 00:00:00 2001 From: Maxi Vila Date: Tue, 26 May 2026 10:33:13 -0300 Subject: [PATCH 6/7] fix: handle disabled community gracefully in enterWizard, settings and validateAdmin --- bot/modules/orders/commands.ts | 2 +- bot/modules/user/scenes/settings.ts | 8 +++++--- bot/validations.ts | 5 ++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/bot/modules/orders/commands.ts b/bot/modules/orders/commands.ts index 75a9d385..74ba87d9 100644 --- a/bot/modules/orders/commands.ts +++ b/bot/modules/orders/commands.ts @@ -197,7 +197,7 @@ async function enterWizard( const { community, isBanned } = communityInfo; if (!community) { - throw new Error('Default community not found'); + return deletedCommunityMessage(ctx); } // Check if user is banned diff --git a/bot/modules/user/scenes/settings.ts b/bot/modules/user/scenes/settings.ts index b5645873..0a49fca5 100644 --- a/bot/modules/user/scenes/settings.ts +++ b/bot/modules/user/scenes/settings.ts @@ -26,9 +26,11 @@ function make() { lightning_address: '', }; if (user.default_community_id) { - const community = await Community.findById(user.default_community_id); - if (community == null) throw new Error('community not found'); - data.community = community.group; + const community = await Community.findOne({ + _id: user.default_community_id, + enabled: { $ne: false }, + }); + if (community) data.community = community.group; } if (user.nostr_public_key) { data.npub = NostrLib.encodeNpub(user.nostr_public_key); diff --git a/bot/validations.ts b/bot/validations.ts index 7ee42bbb..045eb3f4 100644 --- a/bot/validations.ts +++ b/bot/validations.ts @@ -111,7 +111,10 @@ const validateAdmin = async (ctx: MainContext, id?: string) => { let community = null; if (user.default_community_id) - community = await Community.findOne({ _id: user.default_community_id }); + community = await Community.findOne({ + _id: user.default_community_id, + enabled: { $ne: false }, + }); const isSolver = isDisputeSolver(community, user); From da9be1bae66af5dd0a31bd40f4564b3691651054 Mon Sep 17 00:00:00 2001 From: Maxi Vila Date: Thu, 28 May 2026 12:10:23 -0300 Subject: [PATCH 7/7] i18n: add ambiguous_community_input translation key to all locales --- locales/de.yaml | 1 + locales/en.yaml | 1 + locales/es.yaml | 1 + locales/fa.yaml | 1 + locales/fr.yaml | 1 + locales/it.yaml | 1 + locales/ko.yaml | 1 + locales/pt.yaml | 1 + locales/ru.yaml | 1 + locales/uk.yaml | 1 + 10 files changed, 10 insertions(+) diff --git a/locales/de.yaml b/locales/de.yaml index b42ce6ca..872e0fc8 100644 --- a/locales/de.yaml +++ b/locales/de.yaml @@ -721,3 +721,4 @@ community_disabled_by_admin: Ein Administrator hat deine Community ${communityNa community_enabled_by_admin: Ein Administrator hat deine Community ${communityName} wieder aktiviert community_already_disabled: Diese Community ist bereits deaktiviert community_already_enabled: Diese Community ist bereits aktiviert +ambiguous_community_input: Mehrere Communities stimmen mit diesem Benutzernamen überein, bitte verwende stattdessen die Community-ID diff --git a/locales/en.yaml b/locales/en.yaml index e7dbe2ff..69ecbb61 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -726,3 +726,4 @@ community_disabled_by_admin: An administrator has disabled your community ${comm community_enabled_by_admin: An administrator has re-enabled your community ${communityName} community_already_disabled: This community is already disabled community_already_enabled: This community is already enabled +ambiguous_community_input: Multiple communities match that username, please use the community ID instead diff --git a/locales/es.yaml b/locales/es.yaml index e54d5be9..53e55d88 100644 --- a/locales/es.yaml +++ b/locales/es.yaml @@ -723,3 +723,4 @@ community_disabled_by_admin: Un administrador ha deshabilitado tu comunidad ${co community_enabled_by_admin: Un administrador ha reactivado tu comunidad ${communityName} community_already_disabled: Esta comunidad ya está deshabilitada community_already_enabled: Esta comunidad ya está habilitada +ambiguous_community_input: Varias comunidades coinciden con ese nombre de usuario, por favor usa el ID de la comunidad diff --git a/locales/fa.yaml b/locales/fa.yaml index 4d800417..b413ad09 100644 --- a/locales/fa.yaml +++ b/locales/fa.yaml @@ -820,3 +820,4 @@ community_disabled_by_admin: یک مدیر جامعه ${communityName} شما ر community_enabled_by_admin: یک مدیر جامعه ${communityName} شما را دوباره فعال کرد community_already_disabled: این جامعه از قبل غیرفعال است community_already_enabled: این جامعه از قبل فعال است +ambiguous_community_input: چندین جامعه با این نام کاربری مطابقت دارند، لطفاً از شناسه جامعه استفاده کنید diff --git a/locales/fr.yaml b/locales/fr.yaml index 001c4dad..7a3684da 100644 --- a/locales/fr.yaml +++ b/locales/fr.yaml @@ -720,3 +720,4 @@ community_disabled_by_admin: Un administrateur a désactivé votre communauté $ community_enabled_by_admin: Un administrateur a réactivé votre communauté ${communityName} community_already_disabled: Cette communauté est déjà désactivée community_already_enabled: Cette communauté est déjà activée +ambiguous_community_input: Plusieurs communautés correspondent à ce nom d'utilisateur, veuillez utiliser l'ID de la communauté à la place diff --git a/locales/it.yaml b/locales/it.yaml index 6b51b9bc..5be780da 100644 --- a/locales/it.yaml +++ b/locales/it.yaml @@ -718,3 +718,4 @@ community_disabled_by_admin: Un amministratore ha disabilitato la tua comunità community_enabled_by_admin: Un amministratore ha riabilitato la tua comunità ${communityName} community_already_disabled: Questa comunità è già disabilitata community_already_enabled: Questa comunità è già abilitata +ambiguous_community_input: Più comunità corrispondono a quel nome utente, usa invece l'ID della comunità diff --git a/locales/ko.yaml b/locales/ko.yaml index e222b876..7c6a26df 100644 --- a/locales/ko.yaml +++ b/locales/ko.yaml @@ -718,3 +718,4 @@ community_disabled_by_admin: 관리자가 커뮤니티 ${communityName}을(를) community_enabled_by_admin: 관리자가 커뮤니티 ${communityName}을(를) 다시 활성화했습니다 community_already_disabled: 이 커뮤니티는 이미 비활성화되어 있습니다 community_already_enabled: 이 커뮤니티는 이미 활성화되어 있습니다 +ambiguous_community_input: 해당 사용자 이름과 일치하는 커뮤니티가 여러 개 있습니다. 커뮤니티 ID를 사용해 주세요 diff --git a/locales/pt.yaml b/locales/pt.yaml index daeb3290..4d18128a 100644 --- a/locales/pt.yaml +++ b/locales/pt.yaml @@ -720,3 +720,4 @@ community_disabled_by_admin: Um administrador desativou sua comunidade ${communi community_enabled_by_admin: Um administrador reativou sua comunidade ${communityName} community_already_disabled: Esta comunidade já está desativada community_already_enabled: Esta comunidade já está ativada +ambiguous_community_input: Várias comunidades correspondem a esse nome de usuário, por favor use o ID da comunidade diff --git a/locales/ru.yaml b/locales/ru.yaml index 4d7594f1..2ec78d04 100644 --- a/locales/ru.yaml +++ b/locales/ru.yaml @@ -721,3 +721,4 @@ community_disabled_by_admin: Администратор отключил ваш community_enabled_by_admin: Администратор повторно включил ваше сообщество ${communityName} community_already_disabled: Это сообщество уже отключено community_already_enabled: Это сообщество уже включено +ambiguous_community_input: Несколько сообществ соответствуют этому имени пользователя, пожалуйста, используйте ID сообщества diff --git a/locales/uk.yaml b/locales/uk.yaml index aa616590..d527edbe 100644 --- a/locales/uk.yaml +++ b/locales/uk.yaml @@ -717,3 +717,4 @@ community_disabled_by_admin: Адміністратор вимкнув вашу community_enabled_by_admin: Адміністратор повторно увімкнув вашу спільноту ${communityName} community_already_disabled: Ця спільнота вже вимкнена community_already_enabled: Ця спільнота вже увімкнена +ambiguous_community_input: Кілька спільнот відповідають цьому імені користувача, будь ласка, використовуйте ID спільноти