From 72d4a9c0f853786d76780479fb2ba8cdbd676aa2 Mon Sep 17 00:00:00 2001 From: Merul Dhiman Date: Fri, 30 Jan 2026 23:15:02 +0530 Subject: [PATCH 1/2] feat: message users when no match --- .../src/services/AIMatchingService.ts | 27 +++++++++ .../src/services/MatchNotificationService.ts | 60 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/platforms/dreamsync-api/src/services/AIMatchingService.ts b/platforms/dreamsync-api/src/services/AIMatchingService.ts index b653c9e34..0e2c8e745 100644 --- a/platforms/dreamsync-api/src/services/AIMatchingService.ts +++ b/platforms/dreamsync-api/src/services/AIMatchingService.ts @@ -131,9 +131,36 @@ export class AIMatchingService { // Process any existing matches that haven't been messaged yet await this.processUnmessagedMatches(); + + // Send no-match messages to all users with a wishlist (even empty) who did not get a match this run + await this.sendNoMatchNotifications(wishlists, matchResults); }); } + /** + * Send no-match notifications to users who have a wishlist (even if empty) but did not get a match this run. + */ + private async sendNoMatchNotifications(wishlists: Wishlist[], matchResults: MatchResult[]): Promise { + const wishlistUserIds = [...new Set(wishlists.map(w => w.userId))]; + const matchedUserIds = new Set(matchResults.flatMap(r => r.userIds)); + const noMatchUserIds = wishlistUserIds.filter(userId => !matchedUserIds.has(userId)); + + if (noMatchUserIds.length === 0) { + console.log("✅ No users to notify for no-match (all wishlist users got a match)"); + return; + } + + console.log(`📨 Sending no-match notifications to ${noMatchUserIds.length} users with a wishlist who didn't get a match`); + for (const userId of noMatchUserIds) { + try { + await this.notificationService.sendNoMatchNotification(userId); + } catch (error) { + console.error(`❌ Error sending no-match notification to user ${userId}:`, error); + } + } + console.log(`🎉 No-match notifications sent to ${noMatchUserIds.length} users`); + } + /** * Find and process matches that haven't been messaged yet */ diff --git a/platforms/dreamsync-api/src/services/MatchNotificationService.ts b/platforms/dreamsync-api/src/services/MatchNotificationService.ts index 30e9f8ba3..0155fa29a 100644 --- a/platforms/dreamsync-api/src/services/MatchNotificationService.ts +++ b/platforms/dreamsync-api/src/services/MatchNotificationService.ts @@ -684,6 +684,66 @@ Reply with the Match ID "${match.id}" to connect with the other ${otherUserIds.l } } + /** + * Send a no-match notification to a user who has a wishlist but did not get a match this run. + * Condition: they have made a wishlist (even if empty). + */ + async sendNoMatchNotification(userId: string): Promise { + try { + console.log(`📨 Sending no-match notification to user: ${userId}`); + + const dreamsyncUser = await this.findDreamSyncUser(); + if (!dreamsyncUser) { + console.error("Cannot send no-match notification: DreamSync user not found"); + return; + } + + const user = await this.userService.getUserById(userId); + if (!user) { + console.error(`Cannot send no-match notification: User ${userId} not found`); + return; + } + + const chatResult = await this.findOrCreateMutualChat(userId); + if (!chatResult.chat) { + console.error(`Cannot send no-match notification: Could not find/create chat for user ${userId}`); + return; + } + + const { chat, wasCreated } = chatResult; + if (wasCreated) { + console.log(`⏳ Chat was just created, waiting 15 seconds before sending no-match message...`); + await new Promise(resolve => setTimeout(resolve, 15000)); + console.log(`✅ 15-second delay completed for no-match message`); + } + + const displayName = user.name || user.ename || "User"; + const messageContent = `$$system-message$$ + +Dear ${displayName}, + +DreamSync tried to find matches with other users using your wishlist but no conclusive matches were found. + +we would encourage you to add more details to your wishlist and add more topics you are interested in. + +Best Wishes, +DreamSync`; + + const messageRepository = AppDataSource.getRepository(Message); + const message = messageRepository.create({ + text: messageContent, + sender: dreamsyncUser, + group: chat, + isSystemMessage: true, + }); + + await messageRepository.save(message); + console.log(`✅ No-match notification sent to user: ${userId}`); + } catch (error) { + console.error(`❌ Error sending no-match notification to user ${userId}:`, error); + } + } + /** * Process a new match and send notifications */ From edc9a88edf8e508e1f052466c4a2bc08c8632b2f Mon Sep 17 00:00:00 2001 From: Merul Dhiman Date: Fri, 30 Jan 2026 23:29:45 +0530 Subject: [PATCH 2/2] feat: message other users --- .../src/services/AIMatchingService.ts | 46 +++++++++++++++---- .../src/services/MatchNotificationService.ts | 18 ++++---- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/platforms/dreamsync-api/src/services/AIMatchingService.ts b/platforms/dreamsync-api/src/services/AIMatchingService.ts index 0e2c8e745..5075669a9 100644 --- a/platforms/dreamsync-api/src/services/AIMatchingService.ts +++ b/platforms/dreamsync-api/src/services/AIMatchingService.ts @@ -132,33 +132,59 @@ export class AIMatchingService { // Process any existing matches that haven't been messaged yet await this.processUnmessagedMatches(); - // Send no-match messages to all users with a wishlist (even empty) who did not get a match this run - await this.sendNoMatchNotifications(wishlists, matchResults); + // Send no-match messages to ALL users with any non-null wishlist (even empty) who did not get a match this run + const allWishlistUserIds = await this.getAllWishlistUserIdsForNoMatch(); + await this.sendNoMatchNotifications(allWishlistUserIds, matchResults); }); } + /** + * Get all user IDs that have at least one active wishlist (non-null record), regardless of content/empty. + * Used for no-match notifications so we include users whose wishlists may be considered "empty". + */ + private async getAllWishlistUserIdsForNoMatch(): Promise { + const rows = await this.wishlistRepository + .createQueryBuilder("wishlist") + .select("DISTINCT wishlist.userId") + .leftJoin("wishlist.user", "user") + .where("wishlist.isActive = :isActive", { isActive: true }) + .andWhere("user.isPrivate = :isPrivate", { isPrivate: false }) + .getRawMany>(); + const userIds = rows.map(r => Object.values(r)[0]).filter(Boolean) as string[]; + console.log(`📋 [no-match] Found ${userIds.length} distinct users with at least one active wishlist (including empty wishlists)`); + return userIds; + } + /** * Send no-match notifications to users who have a wishlist (even if empty) but did not get a match this run. */ - private async sendNoMatchNotifications(wishlists: Wishlist[], matchResults: MatchResult[]): Promise { - const wishlistUserIds = [...new Set(wishlists.map(w => w.userId))]; + private async sendNoMatchNotifications(allWishlistUserIds: string[], matchResults: MatchResult[]): Promise { const matchedUserIds = new Set(matchResults.flatMap(r => r.userIds)); - const noMatchUserIds = wishlistUserIds.filter(userId => !matchedUserIds.has(userId)); + const noMatchUserIds = allWishlistUserIds.filter(userId => !matchedUserIds.has(userId)); + + console.log(`📋 [no-match] Total wishlist users: ${allWishlistUserIds.length}, matched this run: ${matchedUserIds.size}, will send no-match to: ${noMatchUserIds.length}`); if (noMatchUserIds.length === 0) { - console.log("✅ No users to notify for no-match (all wishlist users got a match)"); + console.log("✅ [no-match] No users to notify (all wishlist users got a match this run)"); return; } - console.log(`📨 Sending no-match notifications to ${noMatchUserIds.length} users with a wishlist who didn't get a match`); - for (const userId of noMatchUserIds) { + console.log(`📨 [no-match] Sending no-match notifications to ${noMatchUserIds.length} users`); + let sentCount = 0; + let errorCount = 0; + for (let i = 0; i < noMatchUserIds.length; i++) { + const userId = noMatchUserIds[i]; try { + console.log(`📨 [no-match] (${i + 1}/${noMatchUserIds.length}) Sending no-match notification to user: ${userId}`); await this.notificationService.sendNoMatchNotification(userId); + sentCount++; + console.log(`✅ [no-match] Sent to user ${userId} (${sentCount}/${noMatchUserIds.length})`); } catch (error) { - console.error(`❌ Error sending no-match notification to user ${userId}:`, error); + errorCount++; + console.error(`❌ [no-match] Error sending no-match notification to user ${userId}:`, error); } } - console.log(`🎉 No-match notifications sent to ${noMatchUserIds.length} users`); + console.log(`🎉 [no-match] Completed: ${sentCount} sent, ${errorCount} errors (total no-match candidates: ${noMatchUserIds.length})`); } /** diff --git a/platforms/dreamsync-api/src/services/MatchNotificationService.ts b/platforms/dreamsync-api/src/services/MatchNotificationService.ts index 0155fa29a..4be317735 100644 --- a/platforms/dreamsync-api/src/services/MatchNotificationService.ts +++ b/platforms/dreamsync-api/src/services/MatchNotificationService.ts @@ -690,31 +690,33 @@ Reply with the Match ID "${match.id}" to connect with the other ${otherUserIds.l */ async sendNoMatchNotification(userId: string): Promise { try { - console.log(`📨 Sending no-match notification to user: ${userId}`); + console.log(`📨 [no-match] sendNoMatchNotification: starting for user ${userId}`); const dreamsyncUser = await this.findDreamSyncUser(); if (!dreamsyncUser) { - console.error("Cannot send no-match notification: DreamSync user not found"); + console.error("[no-match] Cannot send: DreamSync user not found"); return; } const user = await this.userService.getUserById(userId); if (!user) { - console.error(`Cannot send no-match notification: User ${userId} not found`); + console.error(`[no-match] Cannot send: User ${userId} not found`); return; } + console.log(`📨 [no-match] User found: ${userId} (${user.name || user.ename || "no name"})`); const chatResult = await this.findOrCreateMutualChat(userId); if (!chatResult.chat) { - console.error(`Cannot send no-match notification: Could not find/create chat for user ${userId}`); + console.error(`[no-match] Cannot send: Could not find/create chat for user ${userId}`); return; } + console.log(`📨 [no-match] Chat ready: ${chatResult.chat.id} (wasCreated: ${chatResult.wasCreated})`); const { chat, wasCreated } = chatResult; if (wasCreated) { - console.log(`⏳ Chat was just created, waiting 15 seconds before sending no-match message...`); + console.log(`⏳ [no-match] Chat was just created, waiting 15 seconds before sending...`); await new Promise(resolve => setTimeout(resolve, 15000)); - console.log(`✅ 15-second delay completed for no-match message`); + console.log(`✅ [no-match] 15-second delay completed`); } const displayName = user.name || user.ename || "User"; @@ -738,9 +740,9 @@ DreamSync`; }); await messageRepository.save(message); - console.log(`✅ No-match notification sent to user: ${userId}`); + console.log(`✅ [no-match] Message saved for user ${userId} (messageId: ${message.id})`); } catch (error) { - console.error(`❌ Error sending no-match notification to user ${userId}:`, error); + console.error(`❌ [no-match] Error sending no-match notification to user ${userId}:`, error); } }