/opt/canhelp/apps/api/src/lib
NameSizeModeActions
activity.ts13240644editdlrm
apple-client-secret.ts40730644editdlrm
balance.ts35060644editdlrm
daily-limits.ts42820644editdlrm
email.ts527080644editdlrm
fomo.ts113660644editdlrm
google-oauth.ts105260644editdlrm
helper-capability.ts42070644editdlrm
messaging-rules.ts31230644editdlrm
nameUtils.ts7090644editdlrm
notif.ts189290644editdlrm
plan-history.ts10200644editdlrm
push.ts63860644editdlrm
referral.ts61100644editdlrm
telegram.ts62070644editdlrm
turnstile.ts7410644editdlrm
Edit: /opt/canhelp/apps/api/src/lib/messaging-rules.ts (3123B)
import { eq } from 'drizzle-orm' import { db } from '../db.js' import { users, plans } from '@canhelp/db' export interface MessagingCheckResult { allowed: boolean reason?: string code?: string senderTier?: string recipientTier?: string } /** * Check if sender can message recipient based on their plan tiers. * * Rules: * - Free can only message users on paid plans (Pro+) * - Pro can message everyone in their direction * - Ultimate can message everyone * - canContactAll overrides everything * * @param senderId - the user initiating the message * @param recipientId - the target user */ export async function canSendMessage( senderId: string, recipientId: string, ): Promise { // Load both users with their plans in parallel const [[sender], [recipient]] = await Promise.all([ db .select({ id: users.id, planId: users.planId, role: users.role }) .from(users) .where(eq(users.id, senderId)), db .select({ id: users.id, planId: users.planId, role: users.role }) .from(users) .where(eq(users.id, recipientId)), ]) if (!sender || !recipient) { return { allowed: false, reason: 'User not found', code: 'USER_NOT_FOUND' } } // Admins can message anyone if (sender.role === 'admin') { return { allowed: true } } // Load plans in parallel const [senderPlan, recipientPlan] = await Promise.all([ sender.planId ? db.select().from(plans).where(eq(plans.id, sender.planId)).then((r) => r[0] ?? null) : Promise.resolve(null), recipient.planId ? db.select().from(plans).where(eq(plans.id, recipient.planId)).then((r) => r[0] ?? null) : Promise.resolve(null), ]) const senderTier = senderPlan?.tier ?? 'free' const recipientTier = recipientPlan?.tier ?? 'free' // canContactAll = Ultimate, can message everyone if (senderPlan?.canContactAll) { return { allowed: true, senderTier, recipientTier } } // Check based on recipient's tier if (recipientTier === 'free') { if (senderPlan?.canContactFreePlan) { return { allowed: true, senderTier, recipientTier } } return { allowed: false, reason: 'Your plan does not allow messaging users on the Free plan', code: 'PLAN_CONTACT_RESTRICTED', senderTier, recipientTier, } } if (recipientTier === 'pro') { if (senderPlan?.canContactProPlan) { return { allowed: true, senderTier, recipientTier } } return { allowed: false, reason: 'Your plan does not allow messaging users on the Pro plan', code: 'PLAN_CONTACT_RESTRICTED', senderTier, recipientTier, } } // recipientTier === 'ultimate' — Pro plans always have canContactProPlan=true, // and Ultimate has canContactAll=true — so anyone with canContactProPlan can reach Ultimate if (senderPlan?.canContactProPlan) { return { allowed: true, senderTier, recipientTier } } return { allowed: false, reason: 'Your plan does not allow messaging this user', code: 'PLAN_CONTACT_RESTRICTED', senderTier, recipientTier, } }