'use strict'; const config = require('../../config/config'); const logger = require('../../utils/logger'); const { Setting, SETTINGS_KEY } = require('./settingModel'); const { isDbChannelEnabled, isEnvChannelEnabled, toPublicMessaging } = require('../../utils/messagingChannels'); let cache = null; let cacheAt = 0; const CACHE_TTL_MS = 10000; const normalizePhoneForBypass = (raw = '') => { if (!raw) return ''; let digits = String(raw) .replace(/[۰-۹]/g, (d) => '0123456789'['۰۱۲۳۴۵۶۷۸۹'.indexOf(d)]) .replace(/[٠-٩]/g, (d) => '0123456789'['٠١٢٣٤٥٦٧٨٩'.indexOf(d)]) .replace(/\D/g, ''); if (digits.startsWith('98') && digits.length >= 12) digits = `0${digits.slice(2)}`; if (digits.length === 10 && digits.startsWith('9')) digits = `0${digits}`; if (digits.length > 11 && digits.startsWith('09')) digits = digits.slice(0, 11); return digits; }; const readDbFlags = (doc) => ({ smsEnabled: doc?.smsEnabled, emailEnabled: doc?.emailEnabled, botEnabled: doc?.botEnabled, smsBypassNumbers: Array.isArray(doc?.smsBypassNumbers) ? doc.smsBypassNumbers.map((item) => (item.toObject ? item.toObject() : item)) : [] }); const getDbMessagingFlags = async () => { const now = Date.now(); if (cache && (now - cacheAt) < CACHE_TTL_MS) return cache; const doc = await Setting.findOne({ key: SETTINGS_KEY }) .select('smsEnabled emailEnabled botEnabled smsBypassNumbers') .lean(); cache = readDbFlags(doc); cacheAt = now; return cache; }; const invalidateMessagingCache = () => { cache = null; cacheAt = 0; }; const isSmsBypassNumber = async (phoneNumber) => { if (!phoneNumber) return false; const target = normalizePhoneForBypass(phoneNumber); if (!target) return false; try { const db = await getDbMessagingFlags(); const bypassList = Array.isArray(db.smsBypassNumbers) ? db.smsBypassNumbers : []; return bypassList.some((item) => item.isActive !== false && normalizePhoneForBypass(item.phoneNumber) === target); } catch (err) { logger.warn(`[Messaging] Failed to check bypass numbers: ${err.message}`); return false; } }; const getMessagingChannelState = async (channel, recipientPhone = null) => { const envEnabled = isEnvChannelEnabled(channel, config); const bypass = channel === 'sms' && recipientPhone ? await isSmsBypassNumber(recipientPhone) : false; if (bypass) { logger.info(`[Messaging] Recipient ${recipientPhone} is in SMS bypass list. Channel bypass activated.`); return { enabled: true, envEnabled, dbEnabled: true, bypass: true, blockReason: null }; } if (!envEnabled) { return { enabled: false, envEnabled: false, dbEnabled: null, bypass: false, blockReason: 'env_disabled' }; } try { const db = await getDbMessagingFlags(); const dbEnabled = isDbChannelEnabled(channel, db); return { enabled: dbEnabled, envEnabled: true, dbEnabled, bypass: false, blockReason: dbEnabled ? null : 'dashboard_disabled' }; } catch (err) { logger.warn(`[Messaging] Failed to read settings for ${channel}, using env only: ${err.message}`); return { enabled: true, envEnabled: true, dbEnabled: null, bypass: false, blockReason: null }; } }; const isMessagingChannelEnabled = async (channel, recipientPhone = null) => { const state = await getMessagingChannelState(channel, recipientPhone); return state.enabled; }; const getPublicMessaging = (doc) => toPublicMessaging(readDbFlags(doc), config); module.exports = { getMessagingChannelState, isMessagingChannelEnabled, isSmsBypassNumber, normalizePhoneForBypass, invalidateMessagingCache, getPublicMessaging, getDbMessagingFlags };