diff --git a/bot/modules/community/actions.ts b/bot/modules/community/actions.ts index e88c769a..e7d85aa3 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,13 @@ 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 == null) { + 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..eee1dd08 100644 --- a/bot/modules/community/commands.ts +++ b/bot/modules/community/commands.ts @@ -1,7 +1,7 @@ /* 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'; @@ -21,6 +21,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 +50,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 +77,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 +100,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 +161,7 @@ export const updateCommunity = async ( const community = await Community.findOne({ _id: id, creator_id: user._id, + enabled: { $ne: false }, }); if (!community) { @@ -228,6 +243,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 +257,142 @@ export const deleteCommunity = async (ctx: CommunityContext) => { } }; +async function findCommunityByInput( + ctx: MainContext, + input: string, +): Promise { + if (input[0] === '@') { + const escapedInput = input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const regex = new RegExp(`^${escapedInput}$`, 'i'); + return Community.findOne({ group: regex }); + } + if (!(await validateObjectId(ctx, input))) return null; + return Community.findOne({ _id: input }); +} + +function buildCommunityInfoText( + ctx: MainContext, + community: typeof Community.prototype, + creatorUsername: string, + localeKey: string, +): string { + const solversText = + community.solvers.length > 0 + ? community.solvers + .map((s: { username: string }) => `@${s.username}`) + .join(', ') + : '-'; + const groupText = community.group || '-'; + return ctx.i18n.t(localeKey, { + communityName: community.name, + group: groupText, + solvers: solversText, + creatorUsername, + }); +} + +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 === null) { + 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); + const creatorUsername = creator?.username || 'unknown'; + + if (creator) { + try { + await ctx.telegram.sendMessage( + creator.tg_id, + ctx.i18n.t('community_disabled_by_admin', { + communityName: community.name, + }), + ); + } catch (notifyError) { + logger.error(notifyError); + } + } + + return ctx.reply( + buildCommunityInfoText( + ctx, + community, + creatorUsername, + 'community_disabled_info', + ), + ); + } 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 === null) { + 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); + const creatorUsername = creator?.username || 'unknown'; + + if (creator) { + try { + await ctx.telegram.sendMessage( + creator.tg_id, + ctx.i18n.t('community_enabled_by_admin', { + communityName: community.name, + }), + ); + } catch (notifyError) { + logger.error(notifyError); + } + } + + return ctx.reply( + buildCommunityInfoText( + ctx, + community, + creatorUsername, + 'community_enabled_info', + ), + ); + } catch (error) { + logger.error(error); + await ctx.reply(ctx.i18n.t('generic_error')); + } +}; + export const changeVisibility = async (ctx: CommunityContext) => { try { ctx.deleteMessage(); @@ -251,6 +403,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/bot/modules/user/scenes/settings.ts b/bot/modules/user/scenes/settings.ts index b5645873..9fda679c 100644 --- a/bot/modules/user/scenes/settings.ts +++ b/bot/modules/user/scenes/settings.ts @@ -26,9 +26,13 @@ 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 != null) { + data.community = community.group; + } } if (user.nostr_public_key) { data.npub = NostrLib.encodeNpub(user.nostr_public_key); diff --git a/locales/de.yaml b/locales/de.yaml index 07e691ab..6d2b163a 100644 --- a/locales/de.yaml +++ b/locales/de.yaml @@ -717,3 +717,9 @@ 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_info: "Gemeinschaft: ${communityName}\nGruppe: ${group}\nLöser: ${solvers}\nErsteller: @${creatorUsername}\nDie Gemeinschaft wurde deaktiviert" +community_enabled_info: "Gemeinschaft: ${communityName}\nGruppe: ${group}\nLöser: ${solvers}\nErsteller: @${creatorUsername}\nDie Gemeinschaft wurde aktiviert" +community_disabled_by_admin: Ein Administrator hat Ihre Gemeinschaft ${communityName} deaktiviert +community_enabled_by_admin: Ein Administrator hat Ihre Gemeinschaft ${communityName} wieder aktiviert +community_already_disabled: Diese Gemeinschaft ist bereits deaktiviert +community_already_enabled: Diese Gemeinschaft ist bereits aktiv diff --git a/locales/en.yaml b/locales/en.yaml index def6f4cd..c635350a 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -722,3 +722,9 @@ 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_info: "Community: ${communityName}\nGroup: ${group}\nSolvers: ${solvers}\nCreator: @${creatorUsername}\nThe community was disabled" +community_enabled_info: "Community: ${communityName}\nGroup: ${group}\nSolvers: ${solvers}\nCreator: @${creatorUsername}\nThe community was enabled" +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..b4a48bfe 100644 --- a/locales/es.yaml +++ b/locales/es.yaml @@ -719,3 +719,9 @@ 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_info: "Comunidad: ${communityName}\nGrupo: ${group}\nSolvers: ${solvers}\nCreador: @${creatorUsername}\nLa comunidad fue deshabilitada" +community_enabled_info: "Comunidad: ${communityName}\nGrupo: ${group}\nSolvers: ${solvers}\nCreador: @${creatorUsername}\nLa comunidad fue habilitada" +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 463206de..fcf41795 100644 --- a/locales/fa.yaml +++ b/locales/fa.yaml @@ -816,3 +816,9 @@ payment_methods_saved: "روش‌های پرداخت ذخیره شد ✅" custom_payment_method: "✍️ روش پرداخت سفارشی" payment_methods_reset: "روش‌های پرداخت حذف شدند. کاربران اکنون می‌توانند هر روش پرداختی را آزادانه وارد کنند." payment_methods_wizard_commands: "/reset — حذف همه روش‌های پرداخت و بازگرداندن رفتار پیش‌فرض\n/exit — خروج بدون ذخیره" +community_disabled_info: "جامعه: ${communityName}\nگروه: ${group}\nحل‌کنندگان: ${solvers}\nسازنده: @${creatorUsername}\nجامعه غیرفعال شد" +community_enabled_info: "جامعه: ${communityName}\nگروه: ${group}\nحل‌کنندگان: ${solvers}\nسازنده: @${creatorUsername}\nجامعه فعال شد" +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 e96d3e0b..d4a90118 100644 --- a/locales/fr.yaml +++ b/locales/fr.yaml @@ -716,3 +716,9 @@ 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_info: "Communauté: ${communityName}\nGroupe: ${group}\nSolveurs: ${solvers}\nCréateur: @${creatorUsername}\nLa communauté a été désactivée" +community_enabled_info: "Communauté: ${communityName}\nGroupe: ${group}\nSolveurs: ${solvers}\nCréateur: @${creatorUsername}\nLa communauté a été activée" +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à active diff --git a/locales/it.yaml b/locales/it.yaml index 54aae511..09ba6228 100644 --- a/locales/it.yaml +++ b/locales/it.yaml @@ -714,3 +714,9 @@ 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_info: "Comunità: ${communityName}\nGruppo: ${group}\nRisolutori: ${solvers}\nCreatore: @${creatorUsername}\nLa comunità è stata disabilitata" +community_enabled_info: "Comunità: ${communityName}\nGruppo: ${group}\nRisolutori: ${solvers}\nCreatore: @${creatorUsername}\nLa comunità è stata abilitata" +community_disabled_by_admin: Un amministratore ha disabilitato la tua comunità ${communityName} +community_enabled_by_admin: Un amministratore ha riattivato la tua comunità ${communityName} +community_already_disabled: Questa comunità è già disabilitata +community_already_enabled: Questa comunità è già attiva diff --git a/locales/ko.yaml b/locales/ko.yaml index 8880aa1d..ae565508 100644 --- a/locales/ko.yaml +++ b/locales/ko.yaml @@ -714,3 +714,9 @@ payment_methods_saved: "결제 방법이 저장되었습니다 ✅" custom_payment_method: "✍️ 사용자 지정 결제 방법" payment_methods_reset: "결제 방법이 삭제되었습니다. 이제 사용자는 어떤 결제 방법이든 자유롭게 입력할 수 있습니다." payment_methods_wizard_commands: "/reset — 모든 결제 방법을 삭제하고 기본 동작을 복원합니다\n/exit — 저장하지 않고 종료" +community_disabled_info: "커뮤니티: ${communityName}\n그룹: ${group}\n해결사: ${solvers}\n생성자: @${creatorUsername}\n커뮤니티가 비활성화되었습니다" +community_enabled_info: "커뮤니티: ${communityName}\n그룹: ${group}\n해결사: ${solvers}\n생성자: @${creatorUsername}\n커뮤니티가 활성화되었습니다" +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 7cfdebc2..cd92c5e0 100644 --- a/locales/pt.yaml +++ b/locales/pt.yaml @@ -716,3 +716,9 @@ 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_info: "Comunidade: ${communityName}\nGrupo: ${group}\nSolvers: ${solvers}\nCriador: @${creatorUsername}\nA comunidade foi desabilitada" +community_enabled_info: "Comunidade: ${communityName}\nGrupo: ${group}\nSolvers: ${solvers}\nCriador: @${creatorUsername}\nA comunidade foi habilitada" +community_disabled_by_admin: Um administrador desabilitou sua comunidade ${communityName} +community_enabled_by_admin: Um administrador reativou sua comunidade ${communityName} +community_already_disabled: Esta comunidade já está desabilitada +community_already_enabled: Esta comunidade já está habilitada diff --git a/locales/ru.yaml b/locales/ru.yaml index 09956b27..738ea7e3 100644 --- a/locales/ru.yaml +++ b/locales/ru.yaml @@ -717,3 +717,9 @@ payment_methods_saved: "Способы оплаты сохранены ✅" custom_payment_method: "✍️ Пользовательский способ оплаты" payment_methods_reset: "Способы оплаты удалены. Пользователи теперь могут свободно вводить любой способ оплаты." payment_methods_wizard_commands: "/reset — удалить все способы оплаты и восстановить поведение по умолчанию\n/exit — выйти без сохранения" +community_disabled_info: "Сообщество: ${communityName}\nГруппа: ${group}\nРешатели: ${solvers}\nСоздатель: @${creatorUsername}\nСообщество было отключено" +community_enabled_info: "Сообщество: ${communityName}\nГруппа: ${group}\nРешатели: ${solvers}\nСоздатель: @${creatorUsername}\nСообщество было активировано" +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 1176d9e4..8f836874 100644 --- a/locales/uk.yaml +++ b/locales/uk.yaml @@ -713,3 +713,9 @@ payment_methods_saved: "Способи оплати збережено ✅" custom_payment_method: "✍️ Власний спосіб оплати" payment_methods_reset: "Способи оплати видалено. Користувачі тепер можуть вільно вводити будь-який спосіб оплати." payment_methods_wizard_commands: "/reset — видалити всі способи оплати та відновити поведінку за замовчуванням\n/exit — вийти без збереження" +community_disabled_info: "Спільнота: ${communityName}\nГрупа: ${group}\nВирішувачі: ${solvers}\nСтворив: @${creatorUsername}\nСпільноту було вимкнено" +community_enabled_info: "Спільнота: ${communityName}\nГрупа: ${group}\nВирішувачі: ${solvers}\nСтворив: @${creatorUsername}\nСпільноту було увімкнено" +community_disabled_by_admin: Адміністратор вимкнув вашу спільноту ${communityName} +community_enabled_by_admin: Адміністратор повторно увімкнув вашу спільноту ${communityName} +community_already_disabled: Цю спільноту вже вимкнено +community_already_enabled: Ця спільнота вже активна diff --git a/models/community.ts b/models/community.ts index 0434174e..9e1d2640 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, default: true }, currencies: { type: [String], required: true,