From 7b531c1b1ade70b1aa84fbec4429b9677ab8a495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?th=E1=BB=8Bnh?= Date: Fri, 15 Aug 2025 12:21:19 +0700 Subject: [PATCH 1/3] Notify users about credits reach --- backend/database/redis_db.py | 15 ++++++++ backend/routers/transcribe.py | 13 +++++++ backend/utils/llm/notifications.py | 58 ++++++++++++++++++++++++++++++ backend/utils/notifications.py | 39 +++++++++++++++++++- 4 files changed, 124 insertions(+), 1 deletion(-) diff --git a/backend/database/redis_db.py b/backend/database/redis_db.py index 0030ad3714e..4d5cb06db11 100644 --- a/backend/database/redis_db.py +++ b/backend/database/redis_db.py @@ -552,3 +552,18 @@ def get_migration_status(uid: str) -> dict: def clear_migration_status(uid: str): key = f"migration_status:{uid}" r.delete(key) + + +# ****************************************************** +# ************** CREDIT LIMIT NOTIFICATIONS ************ +# ****************************************************** + + +def set_credit_limit_notification_sent(uid: str, ttl: int = 60 * 60 * 6): + """Cache that credit limit notification was sent to user (6 hours TTL by default)""" + r.set(f'users:{uid}:credit_limit_notification_sent', '1', ex=ttl) + + +def has_credit_limit_notification_been_sent(uid: str) -> bool: + """Check if credit limit notification was already sent to user recently""" + return r.exists(f'users:{uid}:credit_limit_notification_sent') diff --git a/backend/routers/transcribe.py b/backend/routers/transcribe.py index 49c7dd50651..42358dc171f 100644 --- a/backend/routers/transcribe.py +++ b/backend/routers/transcribe.py @@ -61,6 +61,7 @@ from utils.other import endpoints as auth from utils.other.storage import get_profile_audio_if_exists +from utils.notifications import send_credit_limit_notification router = APIRouter() @@ -89,6 +90,12 @@ async def _listen( return if not has_transcription_credits(uid): + # Send credit limit notification (with Redis caching to prevent spam) + try: + await send_credit_limit_notification(uid) + except Exception as e: + print(f"Error sending credit limit notification: {e}") + await websocket.close(code=4002, reason="Usage limit exceeded") return @@ -147,6 +154,12 @@ async def _record_usage_periodically(): last_usage_record_timestamp = current_time if not has_transcription_credits(uid): + # Send credit limit notification (with Redis caching to prevent spam) + try: + await send_credit_limit_notification(uid) + except Exception as e: + print(f"Error sending credit limit notification: {e}") + nonlocal websocket_close_code websocket_close_code = 4002 websocket_active = False diff --git a/backend/utils/llm/notifications.py b/backend/utils/llm/notifications.py index 334ea616059..724702b95a3 100644 --- a/backend/utils/llm/notifications.py +++ b/backend/utils/llm/notifications.py @@ -69,3 +69,61 @@ async def generate_notification_message(uid: str, name: str, plan_type: str = "b # Improved fallback messages with more personality return ("omi", f"Hey {name}! 👋 Thanks for being part of the Omi family! ✨") + + +async def generate_credit_limit_notification(uid: str, name: str) -> Tuple[str, str]: + """ + Generate a personalized notification when user hits transcription credit limits. + """ + # Get relevant memories for context + memories = await get_relevant_memories(uid, limit=50) + memory_context = "" + if memories: + memory_summaries = [m.get('content', '') for m in memories] # Use all memories for context + memory_context = f"\nRecent conversations include: {', '.join(memory_summaries[:100])}..." + + system_prompt = """You're Omi, and you need to gently let a user know they've hit their transcription limits while encouraging them to upgrade to unlimited. + + Your Style: + - Warm and understanding, not pushy + - Show genuine care for their journey with you + - Make the upgrade feel like a natural next step + - Reference their usage to show value + - Keep it conversational and friendly + - No emojis (express yourself in words!) + - Under 150 characters total + + Key Points to Include: + - They've been actively using transcription (show appreciation) + - Unlimited plan removes all limits + - Can check usage/plans in app or search 'omi unlimited subs' in marketplace + - Make it feel like you're helping them, not selling to them + """ + + user_prompt = f"""Create a credit limit notification for {name} who has reached their transcription limits. + + Context: + - User's name: {name} + - They've been actively transcribing conversations + - Need to encourage unlimited plan subscription{memory_context} + + The message should: + - Acknowledge their active usage positively + - Suggest checking plans in the app or searching 'omi unlimited subs' in marketplace + - Feel helpful, not sales-y + - Be warm and personal to {name} + + Return only the notification body text.""" + + try: + body = await llm_medium.apredict(system_prompt + "\n" + user_prompt) + return "omi", body.strip() + + except Exception as e: + print(f"Error generating credit limit notification: {e}") + + # Fallback message + return ( + "omi", + f"Hey {name}! You've been actively using transcription - that's awesome! You've hit your limit, but unlimited plans remove all restrictions. Check your usage in the app or search 'omi unlimited subs' in the marketplace!", + ) diff --git a/backend/utils/notifications.py b/backend/utils/notifications.py index faef8c0e953..dd89290b391 100644 --- a/backend/utils/notifications.py +++ b/backend/utils/notifications.py @@ -2,7 +2,8 @@ import math from firebase_admin import messaging, auth import database.notifications as notification_db -from .llm.notifications import generate_notification_message +from database.redis_db import set_credit_limit_notification_sent, has_credit_limit_notification_been_sent +from .llm.notifications import generate_notification_message, generate_credit_limit_notification def send_notification(token: str, title: str, body: str, data: dict = None): @@ -49,6 +50,42 @@ async def send_subscription_paid_personalized_notification(user_id: str, data: d send_notification(token, "omi", body, data) +async def send_credit_limit_notification(user_id: str): + """Send a personalized credit limit notification if not sent recently""" + # Check if notification was sent recently (within 6 hours) + if has_credit_limit_notification_been_sent(user_id): + print(f"Credit limit notification already sent recently for user {user_id}") + return + + # Get user's notification token + token = notification_db.get_token_only(user_id) + if not token: + print(f"No notification token found for user {user_id}") + return + + # Get user name from Firebase Auth + try: + user = auth.get_user(user_id) + name = user.display_name + if not name and user.email: + name = user.email.split('@')[0].capitalize() + if not name: + name = "there" + except Exception as e: + print(f"Error getting user info from Firebase Auth: {e}") + name = "there" + + # Generate personalized credit limit message + title, body = await generate_credit_limit_notification(user_id, name) + + # Send notification + send_notification(token, title, body) + + # Cache that notification was sent (6 hours TTL) + set_credit_limit_notification_sent(user_id) + print(f"Credit limit notification sent to user {user_id}") + + async def send_bulk_notification(user_tokens: list, title: str, body: str): try: batch_size = 500 From b72123e6b3fbe5a49b319171348d98ea7d701c42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?th=E1=BB=8Bnh?= Date: Fri, 15 Aug 2025 12:21:38 +0700 Subject: [PATCH 2/3] Fix charts invalid maxY --- app/lib/pages/settings/usage_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/pages/settings/usage_page.dart b/app/lib/pages/settings/usage_page.dart index 224b8222586..563aadb978e 100644 --- a/app/lib/pages/settings/usage_page.dart +++ b/app/lib/pages/settings/usage_page.dart @@ -1614,7 +1614,7 @@ class _UsagePageState extends State with TickerProviderStateMixin { sideTitles: SideTitles( showTitles: true, reservedSize: 40, - interval: maxY > 1 ? (maxY / 4).roundToDouble() : 0.25, + interval: maxY > 1 ? ((maxY / 4).roundToDouble() > 0 ? (maxY / 4).roundToDouble() : 1.0) : 0.25, getTitlesWidget: (value, meta) { if (value == meta.max) return const SizedBox(); return SideTitleWidget( From ebb0dd0e0c42b07c9aa8784d3aeb066fbd6c2cbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?th=E1=BB=8Bnh?= Date: Fri, 15 Aug 2025 12:25:39 +0700 Subject: [PATCH 3/3] use 24h instead of 6h for the next credits limit notification --- backend/database/redis_db.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/database/redis_db.py b/backend/database/redis_db.py index 4d5cb06db11..69f8c197728 100644 --- a/backend/database/redis_db.py +++ b/backend/database/redis_db.py @@ -559,8 +559,8 @@ def clear_migration_status(uid: str): # ****************************************************** -def set_credit_limit_notification_sent(uid: str, ttl: int = 60 * 60 * 6): - """Cache that credit limit notification was sent to user (6 hours TTL by default)""" +def set_credit_limit_notification_sent(uid: str, ttl: int = 60 * 60 * 24): + """Cache that credit limit notification was sent to user (24 hours TTL by default)""" r.set(f'users:{uid}:credit_limit_notification_sent', '1', ex=ttl)