@messenger-box/platform-server 10.0.3-alpha.7 → 10.0.3-alpha.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/containers/containers.js +4 -1
- package/lib/containers/containers.js.map +1 -1
- package/lib/containers/context-services-from-container.d.ts +1 -1
- package/lib/containers/context-services-from-container.js +1 -1
- package/lib/containers/context-services-from-container.js.map +1 -1
- package/lib/graphql/resolvers/channel-member.d.ts +3 -2
- package/lib/graphql/resolvers/channel-member.js +30 -5
- package/lib/graphql/resolvers/channel-member.js.map +1 -1
- package/lib/graphql/resolvers/channel.d.ts +3 -2
- package/lib/graphql/resolvers/channel.js +279 -53
- package/lib/graphql/resolvers/channel.js.map +1 -1
- package/lib/graphql/resolvers/extended-token-account.d.ts +3 -2
- package/lib/graphql/resolvers/extended-token-account.js +90 -23
- package/lib/graphql/resolvers/extended-token-account.js.map +1 -1
- package/lib/graphql/resolvers/index.d.ts +1 -1
- package/lib/graphql/resolvers/post-thread.d.ts +1 -1
- package/lib/graphql/resolvers/post-thread.js +294 -132
- package/lib/graphql/resolvers/post-thread.js.map +1 -1
- package/lib/graphql/resolvers/post.d.ts +2 -3
- package/lib/graphql/resolvers/post.js +696 -234
- package/lib/graphql/resolvers/post.js.map +1 -1
- package/lib/graphql/resolvers/reaction.d.ts +3 -2
- package/lib/graphql/resolvers/reaction.js +96 -14
- package/lib/graphql/resolvers/reaction.js.map +1 -1
- package/lib/graphql/schema/channel-member.graphql +110 -21
- package/lib/graphql/schema/channel-member.graphql.js +1 -1
- package/lib/graphql/schema/channel.graphql +337 -38
- package/lib/graphql/schema/channel.graphql.js +1 -1
- package/lib/graphql/schema/post-thread.graphql +167 -21
- package/lib/graphql/schema/post-thread.graphql.js +1 -1
- package/lib/graphql/schema/post.graphql +284 -40
- package/lib/graphql/schema/post.graphql.js +1 -1
- package/lib/graphql/schema/reaction.graphql +71 -13
- package/lib/graphql/schema/reaction.graphql.js +1 -1
- package/lib/graphql/schema/services.graphql +2 -0
- package/lib/graphql/schema/users.graphql +76 -13
- package/lib/graphql/schema/users.graphql.js +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/interfaces/index.d.ts +0 -1
- package/lib/interfaces/services.d.ts +1 -1
- package/lib/migrations/dbMigrations/AddPostsConfigurationsMigration.d.ts +17 -0
- package/lib/migrations/dbMigrations/AddPostsConfigurationsMigration.js +44 -0
- package/lib/migrations/dbMigrations/AddPostsConfigurationsMigration.js.map +1 -0
- package/lib/migrations/dbMigrations/index.d.ts +1 -0
- package/lib/migrations/index.d.ts +1 -0
- package/lib/migrations/mail-template-migration.js +1 -1
- package/lib/migrations/message-notification-template-migration.d.ts +1 -1
- package/lib/migrations/message-notification-template-migration.js +1 -1
- package/lib/plugins/channel-moleculer-service.d.ts +21 -1
- package/lib/plugins/channel-moleculer-service.js +417 -115
- package/lib/plugins/channel-moleculer-service.js.map +1 -1
- package/lib/plugins/extended-token-account-moleculer-service.d.ts +25 -1
- package/lib/plugins/extended-token-account-moleculer-service.js +348 -22
- package/lib/plugins/extended-token-account-moleculer-service.js.map +1 -1
- package/lib/plugins/messenger-notification-moleculer-service.d.ts +27 -3
- package/lib/plugins/messenger-notification-moleculer-service.js +404 -58
- package/lib/plugins/messenger-notification-moleculer-service.js.map +1 -1
- package/lib/plugins/post-moleculer-service.d.ts +85 -21
- package/lib/plugins/post-moleculer-service.js +986 -256
- package/lib/plugins/post-moleculer-service.js.map +1 -1
- package/lib/plugins/post-thread-moleculer-service.d.ts +33 -1
- package/lib/plugins/post-thread-moleculer-service.js +326 -8
- package/lib/plugins/post-thread-moleculer-service.js.map +1 -1
- package/lib/plugins/reaction-moleculer-service.js +1 -1
- package/lib/plugins/reaction-moleculer-service.js.map +1 -1
- package/lib/preferences/settings/post-settings.d.ts +2 -0
- package/lib/preferences/settings/post-settings.js +47 -9
- package/lib/preferences/settings/post-settings.js.map +1 -1
- package/lib/services/channel-service.d.ts +179 -33
- package/lib/services/channel-service.js +821 -274
- package/lib/services/channel-service.js.map +1 -1
- package/lib/services/extended-token-account-service.d.ts +130 -14
- package/lib/services/extended-token-account-service.js +462 -52
- package/lib/services/extended-token-account-service.js.map +1 -1
- package/lib/services/index.d.ts +1 -0
- package/lib/services/messenger-notification-service.d.ts +106 -13
- package/lib/services/messenger-notification-service.js +824 -442
- package/lib/services/messenger-notification-service.js.map +1 -1
- package/lib/services/post-service.d.ts +182 -16
- package/lib/services/post-service.js +731 -115
- package/lib/services/post-service.js.map +1 -1
- package/lib/services/post-thread-service.d.ts +114 -5
- package/lib/services/post-thread-service.js +400 -13
- package/lib/services/post-thread-service.js.map +1 -1
- package/lib/services/proxy-services/channel-microservice.d.ts +5 -3
- package/lib/services/proxy-services/channel-microservice.js +19 -10
- package/lib/services/proxy-services/channel-microservice.js.map +1 -1
- package/lib/services/proxy-services/messenger-notification-microservice.d.ts +128 -8
- package/lib/services/proxy-services/messenger-notification-microservice.js +324 -29
- package/lib/services/proxy-services/messenger-notification-microservice.js.map +1 -1
- package/lib/services/proxy-services/post-microservice.d.ts +186 -12
- package/lib/services/proxy-services/post-microservice.js +543 -54
- package/lib/services/proxy-services/post-microservice.js.map +1 -1
- package/lib/services/proxy-services/post-thread-microservice.d.ts +134 -3
- package/lib/services/proxy-services/post-thread-microservice.js +388 -6
- package/lib/services/proxy-services/post-thread-microservice.js.map +1 -1
- package/lib/services/proxy-services/reaction-microservice.d.ts +161 -3
- package/lib/services/proxy-services/reaction-microservice.js +474 -2
- package/lib/services/proxy-services/reaction-microservice.js.map +1 -1
- package/lib/services/reaction-service.d.ts +124 -4
- package/lib/services/reaction-service.js +415 -3
- package/lib/services/reaction-service.js.map +1 -1
- package/lib/services/redis-cache-manager.d.ts +18 -0
- package/lib/services/redis-cache-manager.js +83 -0
- package/lib/services/redis-cache-manager.js.map +1 -0
- package/lib/store/models/account-token-store.d.ts +1 -1
- package/lib/store/models/account-token-store.js.map +1 -1
- package/lib/store/models/channel.d.ts +2 -3
- package/lib/store/models/channel.js +181 -72
- package/lib/store/models/channel.js.map +1 -1
- package/lib/store/models/post-thread.d.ts +3 -3
- package/lib/store/models/post-thread.js +96 -14
- package/lib/store/models/post-thread.js.map +1 -1
- package/lib/store/models/post.d.ts +2 -3
- package/lib/store/models/post.js +143 -23
- package/lib/store/models/post.js.map +1 -1
- package/lib/store/models/reaction.d.ts +2 -3
- package/lib/store/models/reaction.js +67 -8
- package/lib/store/models/reaction.js.map +1 -1
- package/lib/store/repositories/__tests__/__fixtures__/team-repository.d.ts +3 -3
- package/lib/store/repositories/channel-repository.d.ts +6 -6
- package/lib/store/repositories/channel-repository.js +5 -2
- package/lib/store/repositories/channel-repository.js.map +1 -1
- package/lib/store/repositories/post-repository.d.ts +6 -6
- package/lib/store/repositories/post-repository.js +5 -2
- package/lib/store/repositories/post-repository.js.map +1 -1
- package/lib/store/repositories/post-thread-repository.d.ts +6 -6
- package/lib/store/repositories/post-thread-repository.js +5 -2
- package/lib/store/repositories/post-thread-repository.js.map +1 -1
- package/lib/store/repositories/reaction-repository.d.ts +6 -6
- package/lib/store/repositories/reaction-repository.js +5 -2
- package/lib/store/repositories/reaction-repository.js.map +1 -1
- package/lib/templates/constants/SERVER_TYPES.ts.template +0 -3
- package/lib/templates/repositories/ChannelRepository.ts.template +3 -3
- package/lib/templates/repositories/PostRepository.ts.template +3 -3
- package/lib/templates/repositories/PostThreadRepository.ts.template +3 -3
- package/lib/templates/repositories/ReactionRepository.ts.template +3 -4
- package/lib/templates/services/ChannelService.ts.template +280 -39
- package/lib/templates/services/ExtendedTokenAccountService.ts.template +104 -9
- package/lib/templates/services/MessengerNotificationService.ts.template +94 -19
- package/lib/templates/services/PostService.ts.template +184 -20
- package/lib/templates/services/PostThreadService.ts.template +151 -6
- package/lib/templates/services/ReactionService.ts.template +129 -3
- package/lib/templates/services/RedisCacheManager.ts.template +22 -0
- package/package.json +6 -5
- package/lib/interfaces/context.d.ts +0 -14
- package/lib/store/models/common-options.js +0 -20
- package/lib/store/models/common-options.js.map +0 -1
|
@@ -1,76 +1,471 @@
|
|
|
1
|
-
import {__decorate,__param,__metadata}from'tslib';import {omit}from'lodash-es';import {sub,compareAsc}from'date-fns';import fetch from'node-fetch';import {ServiceBroker}from'moleculer';import {injectable,inject}from'inversify';import {SERVER_TYPES,MoleculerServiceName,MailServiceAction,MailTemplateId,SmsServiceActions,AccountServiceAction,PostTypeEnum,RoomType}from'common';import {CommonType}from'@common-stack/core';import'@cdm-logger/core';import {config}from'../config/env-config.js'
|
|
1
|
+
import {__decorate,__param,__metadata}from'tslib';import {omit}from'lodash-es';import {sub,compareAsc}from'date-fns';import fetch from'node-fetch';import {ServiceBroker}from'moleculer';import {injectable,inject}from'inversify';import {SERVER_TYPES,MoleculerServiceName,MailServiceAction,MailTemplateId,SmsServiceActions,AccountServiceAction,PostTypeEnum,RoomType}from'common/server';import {CommonType}from'@common-stack/core';import'@cdm-logger/core';import {DisposableCollection}from'@adminide-stack/core';import {config}from'../config/env-config.js';/**
|
|
2
|
+
* Messenger Notification Service Implementation
|
|
3
|
+
*
|
|
4
|
+
* This service handles comprehensive notification management within the messenger platform,
|
|
5
|
+
* providing operations for sending various types of notifications including email, SMS,
|
|
6
|
+
* push notifications, and Expo notifications across different messaging contexts.
|
|
7
|
+
*
|
|
8
|
+
* Key capabilities:
|
|
9
|
+
* - Multi-channel notification delivery (email, SMS, push, Expo)
|
|
10
|
+
* - Unread message notification scheduling and management
|
|
11
|
+
* - User preference-based notification filtering
|
|
12
|
+
* - Support service notification handling
|
|
13
|
+
* - Alert message notification broadcasting
|
|
14
|
+
* - Thread-based notification management
|
|
15
|
+
* - Real-time push notification integration
|
|
16
|
+
* - Template-based notification customization
|
|
17
|
+
* - Channel and post-based notification routing
|
|
18
|
+
* - User aggregation and preference management
|
|
19
|
+
*
|
|
20
|
+
* The service integrates with multiple external services through Moleculer broker
|
|
21
|
+
* and provides comprehensive error handling and logging for notification workflows.
|
|
22
|
+
*/
|
|
23
|
+
let MessengerNotificationService = class MessengerNotificationService {
|
|
2
24
|
channelRepository;
|
|
3
25
|
postRepository;
|
|
4
26
|
postThreadRepository;
|
|
5
27
|
countryRepository;
|
|
6
28
|
broker;
|
|
7
|
-
|
|
29
|
+
toDispose = new DisposableCollection();
|
|
8
30
|
users = {};
|
|
9
31
|
userPreferences = {};
|
|
10
32
|
supportServiceenvConfig = config?.SUPPORT_SERVICE_SETTINGS ?? null;
|
|
33
|
+
logger;
|
|
11
34
|
constructor(channelRepository, postRepository, postThreadRepository, countryRepository, broker, logger) {
|
|
12
35
|
this.channelRepository = channelRepository;
|
|
13
36
|
this.postRepository = postRepository;
|
|
14
37
|
this.postThreadRepository = postThreadRepository;
|
|
15
38
|
this.countryRepository = countryRepository;
|
|
16
39
|
this.broker = broker;
|
|
17
|
-
this.logger = logger
|
|
40
|
+
this.logger = logger?.child({
|
|
41
|
+
className: 'MessengerNotificationService'
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Disposes of resources used by the service
|
|
46
|
+
*/
|
|
47
|
+
dispose() {
|
|
48
|
+
this.toDispose.dispose();
|
|
18
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Sets the support service configuration
|
|
52
|
+
*
|
|
53
|
+
* @description Updates the support service notification configuration for customized notifications
|
|
54
|
+
*
|
|
55
|
+
* @param {ISupportServiceConfig} config - Support service configuration
|
|
56
|
+
* @returns {Promise<void | Error>} - Void promise or error
|
|
57
|
+
*/
|
|
58
|
+
// @ts-ignore - Type compatibility issue between interface and implementation
|
|
19
59
|
async setSupportServiceConfig(config) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
60
|
+
try {
|
|
61
|
+
if (!config) {
|
|
62
|
+
return new Error('Support service configuration is required');
|
|
63
|
+
}
|
|
64
|
+
this.supportServiceenvConfig = {
|
|
65
|
+
...this.supportServiceenvConfig,
|
|
66
|
+
...config
|
|
67
|
+
};
|
|
68
|
+
this.logger.debug('Support service configuration updated', {
|
|
69
|
+
hasEmail: !!config.email,
|
|
70
|
+
hasTemplateId: !!config.templateId,
|
|
71
|
+
hasVariables: !!config.variables
|
|
72
|
+
});
|
|
73
|
+
} catch (error) {
|
|
74
|
+
this.logger.error('Error setting support service config: %o', error);
|
|
75
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while setting support service config');
|
|
76
|
+
}
|
|
24
77
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Sends notification for unread messages within a specified time frame
|
|
80
|
+
*
|
|
81
|
+
* @description Aggregates unread messages across channels and sends notifications
|
|
82
|
+
* to users based on their preferences within the specified duration
|
|
83
|
+
*
|
|
84
|
+
* @param {NotificationDurationUnitEnum} unit - Time unit for the duration
|
|
85
|
+
* @param {number} value - Number of time units to look back
|
|
86
|
+
* @returns {Promise<boolean | Error>} - Success status or error
|
|
87
|
+
*/
|
|
88
|
+
// @ts-ignore - Type compatibility issue between interface and implementation
|
|
89
|
+
async sendNotificationOfUnreadMessages(unit, value) {
|
|
90
|
+
try {
|
|
91
|
+
if (!unit || typeof value !== 'number' || value <= 0) {
|
|
92
|
+
return new Error('Valid time unit and positive value are required');
|
|
93
|
+
}
|
|
94
|
+
this.logger.debug('Starting unread message notification process', {
|
|
95
|
+
unit,
|
|
96
|
+
value
|
|
97
|
+
});
|
|
98
|
+
const channels = await this.getLatestMessageInAllChannels(unit, value);
|
|
99
|
+
if (!channels || channels.length === 0) {
|
|
100
|
+
this.logger.debug('No channels with unread messages found');
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
const membersWithUnreadMessagesInChannels = this.groupByMember(channels);
|
|
104
|
+
const notificationPromises = Object.entries(membersWithUnreadMessagesInChannels).map(entry => this.sendNotificationToUser(entry));
|
|
105
|
+
await Promise.all(notificationPromises);
|
|
106
|
+
this.logger.debug('Unread message notifications sent successfully', {
|
|
107
|
+
channelCount: channels.length,
|
|
108
|
+
userCount: Object.keys(membersWithUnreadMessagesInChannels).length
|
|
109
|
+
});
|
|
110
|
+
return true;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
this.logger.error('Error sending unread message notifications: %o', error);
|
|
113
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while sending unread message notifications');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Sends Expo push notifications
|
|
118
|
+
*
|
|
119
|
+
* @description Sends push notifications through the Expo notification service
|
|
120
|
+
*
|
|
121
|
+
* @param {IExpoNotificationData} data - Notification data
|
|
122
|
+
* @returns {Promise<IExpoNotification | Error>} - Notification result or error
|
|
123
|
+
*/
|
|
124
|
+
async sendPushNotificationsExpo(data) {
|
|
125
|
+
try {
|
|
126
|
+
if (!data) {
|
|
127
|
+
return new Error('Notification data is required');
|
|
128
|
+
}
|
|
129
|
+
if (!data.to || Array.isArray(data.to) && data.to.length === 0) {
|
|
130
|
+
return new Error('Notification recipients are required');
|
|
131
|
+
}
|
|
132
|
+
this.logger.debug('Sending Expo push notification', {
|
|
133
|
+
recipients: Array.isArray(data.to) ? data.to.length : 1,
|
|
134
|
+
hasTitle: !!data.title,
|
|
135
|
+
hasBody: !!data.body
|
|
136
|
+
});
|
|
137
|
+
const response = await fetch('https://exp.host/--/api/v2/push/send/', {
|
|
138
|
+
method: 'POST',
|
|
139
|
+
headers: {
|
|
140
|
+
Accept: 'application/json',
|
|
141
|
+
'Accept-Encoding': 'gzip, deflate',
|
|
142
|
+
'Content-Type': 'application/json'
|
|
143
|
+
},
|
|
144
|
+
body: JSON.stringify(data)
|
|
145
|
+
});
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
return new Error(`Expo notification service returned ${response.status}: ${response.statusText}`);
|
|
148
|
+
}
|
|
149
|
+
const result = await response.json();
|
|
150
|
+
this.logger.debug('Expo push notification sent successfully', {
|
|
151
|
+
status: result.status,
|
|
152
|
+
id: result.id
|
|
153
|
+
});
|
|
154
|
+
return result;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
this.logger.error('Error sending Expo push notification: %o', error);
|
|
157
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while sending Expo notification');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Sends Expo notification when a post is created
|
|
162
|
+
*
|
|
163
|
+
* @description Handles push notification logic for new posts with thread and channel support
|
|
164
|
+
*
|
|
165
|
+
* @param {IPost} post - The post that triggered the notification
|
|
166
|
+
* @param {IExpoNotificationBodyData} notificationData - Optional notification data override
|
|
167
|
+
* @returns {Promise<boolean | Error>} - Success status or error
|
|
168
|
+
*/
|
|
169
|
+
// @ts-ignore - Type compatibility issue between interface and implementation
|
|
170
|
+
async sendExpoNotificationOnPost(post, notificationData) {
|
|
171
|
+
try {
|
|
172
|
+
if (!post) {
|
|
173
|
+
return new Error('Post data is required');
|
|
174
|
+
}
|
|
175
|
+
this.logger.debug('Processing post notification', {
|
|
176
|
+
postId: post.id,
|
|
177
|
+
channelId: post.channel,
|
|
178
|
+
hasNotificationData: !!notificationData
|
|
179
|
+
});
|
|
180
|
+
const currentPost = {
|
|
181
|
+
...post
|
|
182
|
+
};
|
|
183
|
+
const notificationParams = notificationData && notificationData?.url ? notificationData : currentPost?.props?.notificationParams;
|
|
184
|
+
if (!notificationParams) {
|
|
185
|
+
this.logger.debug('No notification parameters found, skipping notification');
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
// Get thread participants if thread-based notification
|
|
189
|
+
const postThread = notificationParams?.thread?.id ? await this.getThreadWithParticipants(notificationParams?.thread?.id?.toString()) : null;
|
|
190
|
+
// Get channel members
|
|
191
|
+
const channel = await this.getChannelWithMembers(currentPost?.channel?.toString());
|
|
192
|
+
if (!channel) {
|
|
193
|
+
return new Error('Channel not found for post notification');
|
|
194
|
+
}
|
|
195
|
+
// Get sender information
|
|
196
|
+
const userId = notificationParams?.senderId?.toString() || currentPost?.editedBy?.toString();
|
|
197
|
+
const user = await this.getUser(userId);
|
|
198
|
+
if (!user) {
|
|
199
|
+
return new Error('User not found for post notification');
|
|
200
|
+
}
|
|
201
|
+
const {
|
|
202
|
+
givenName,
|
|
203
|
+
familyName,
|
|
204
|
+
username
|
|
205
|
+
} = user;
|
|
206
|
+
const fullName = givenName ? `${givenName} ${familyName}` : username || 'Message';
|
|
207
|
+
const notificationParamsOtherData = notificationParams?.other ?? {};
|
|
208
|
+
const title = notificationParams?.title || fullName;
|
|
209
|
+
const body = notificationParams?.body || (currentPost?.message === '' ? 'Sent a file' : currentPost?.message || 'New message');
|
|
210
|
+
const sound = notificationParamsOtherData?.sound ?? 'default';
|
|
211
|
+
// Collect notification tokens
|
|
212
|
+
let tokens = [];
|
|
213
|
+
if (postThread) {
|
|
214
|
+
tokens = this.extractNotificationTokens(postThread.participants?.filter(p => p?.user?.id !== userId) || [], 'participants');
|
|
215
|
+
} else {
|
|
216
|
+
tokens = this.extractNotificationTokens(channel.members?.filter(member => member?.user?.id !== userId) || [], 'members');
|
|
217
|
+
}
|
|
218
|
+
if (tokens.length === 0) {
|
|
219
|
+
this.logger.debug('No notification tokens found for recipients');
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
// Send the notification
|
|
223
|
+
const notificationResult = await this.sendPushNotificationsExpo({
|
|
224
|
+
to: tokens,
|
|
225
|
+
data: notificationParams,
|
|
226
|
+
title,
|
|
227
|
+
body,
|
|
228
|
+
sound
|
|
229
|
+
});
|
|
230
|
+
if (notificationResult instanceof Error) {
|
|
231
|
+
return notificationResult;
|
|
232
|
+
}
|
|
233
|
+
this.logger.debug('Post notification sent successfully', {
|
|
234
|
+
postId: post.id,
|
|
235
|
+
recipientCount: tokens.length
|
|
236
|
+
});
|
|
237
|
+
return true;
|
|
238
|
+
} catch (error) {
|
|
239
|
+
this.logger.error('Error sending post notification: %o', error);
|
|
240
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while sending post notification');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Sends general notifications for a post
|
|
245
|
+
*
|
|
246
|
+
* @description Handles email and SMS notifications for posts with template support
|
|
247
|
+
*
|
|
248
|
+
* @param {IPost} post - The post that triggered the notification
|
|
249
|
+
* @returns {Promise<boolean | Error>} - Success status or error
|
|
250
|
+
*/
|
|
251
|
+
// @ts-ignore - Type compatibility issue between interface and implementation
|
|
252
|
+
async sendNotificationOnPost(post) {
|
|
253
|
+
try {
|
|
254
|
+
if (!post) {
|
|
255
|
+
return new Error('Post data is required');
|
|
256
|
+
}
|
|
257
|
+
this.logger.debug('Processing post notification', {
|
|
258
|
+
postId: post.id
|
|
259
|
+
});
|
|
260
|
+
const currentPost = {
|
|
261
|
+
...post
|
|
262
|
+
};
|
|
263
|
+
const template = currentPost?.props?.template ?? null;
|
|
264
|
+
// Extract template configuration
|
|
265
|
+
const guestTemplateId = template?.guest?.id ?? null;
|
|
266
|
+
const guestTemplateVariables = this.extractTemplateVariables(template?.guest?.variables);
|
|
267
|
+
const hostTemplateId = template?.host?.id ?? null;
|
|
268
|
+
const hostTemplateVariables = this.extractTemplateVariables(template?.host?.variables);
|
|
269
|
+
// Get guest user information
|
|
270
|
+
const guestId = guestTemplateVariables?.userId?.toString() || currentPost?.editedBy?.toString();
|
|
271
|
+
const user = await this.getUser(guestId);
|
|
272
|
+
if (!user) {
|
|
273
|
+
return new Error('Guest user not found');
|
|
274
|
+
}
|
|
275
|
+
const userPreferences = await this.getGlobalNotificationSettings(user.id);
|
|
276
|
+
const {
|
|
277
|
+
email,
|
|
278
|
+
sms,
|
|
279
|
+
browser
|
|
280
|
+
} = userPreferences?.global?.notification?.offlineMessages || {};
|
|
281
|
+
// Get channel and host information
|
|
282
|
+
const channel = await this.getChannelWithMembers(currentPost?.channel?.toString());
|
|
283
|
+
if (!channel) {
|
|
284
|
+
return new Error('Channel not found');
|
|
285
|
+
}
|
|
286
|
+
const channelOwner = this.findChannelOwner(channel);
|
|
287
|
+
const hostId = hostTemplateVariables?.userId?.toString() || channelOwner;
|
|
288
|
+
const owner = hostId ? await this.getUser(hostId) : null;
|
|
289
|
+
// Prepare notification content
|
|
290
|
+
const {
|
|
291
|
+
guestSubject,
|
|
292
|
+
hostSubject,
|
|
293
|
+
guestText,
|
|
294
|
+
hostText
|
|
295
|
+
} = this.prepareNotificationContent(currentPost, user, owner, guestTemplateVariables, hostTemplateVariables);
|
|
296
|
+
// Send notifications based on preferences
|
|
297
|
+
const notificationPromises = [];
|
|
298
|
+
if (email) {
|
|
299
|
+
// Send to guest
|
|
300
|
+
notificationPromises.push(this.sendMailOnPost(user, currentPost, channel, guestSubject, guestText, guestTemplateId, guestTemplateVariables));
|
|
301
|
+
// Send to host if exists
|
|
302
|
+
if (owner) {
|
|
303
|
+
notificationPromises.push(this.sendMailOnPost(owner, currentPost, channel, hostSubject, hostText, hostTemplateId, hostTemplateVariables));
|
|
35
304
|
}
|
|
36
305
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
creator: true,
|
|
42
|
-
members: {
|
|
43
|
-
$filter: {
|
|
44
|
-
input: '$members',
|
|
45
|
-
as: 'item',
|
|
46
|
-
cond: {
|
|
47
|
-
$and: [{
|
|
48
|
-
$lte: ['$$item.lastViewedAt', '$lastPostAt']
|
|
49
|
-
}]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
306
|
+
if (sms) {
|
|
307
|
+
notificationPromises.push(this.sendSmsOnPost(user, currentPost, guestText));
|
|
308
|
+
if (owner) {
|
|
309
|
+
notificationPromises.push(this.sendSmsOnPost(owner, currentPost, hostText));
|
|
52
310
|
}
|
|
53
311
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
},
|
|
62
|
-
createdAt: {
|
|
63
|
-
$in: channels.map(({
|
|
64
|
-
lastPostAt
|
|
65
|
-
}) => lastPostAt)
|
|
312
|
+
await Promise.all(notificationPromises);
|
|
313
|
+
this.logger.debug('Post notifications sent successfully', {
|
|
314
|
+
postId: post.id,
|
|
315
|
+
notificationTypes: {
|
|
316
|
+
email,
|
|
317
|
+
sms,
|
|
318
|
+
browser
|
|
66
319
|
}
|
|
320
|
+
});
|
|
321
|
+
return true;
|
|
322
|
+
} catch (error) {
|
|
323
|
+
this.logger.error('Error sending post notification: %o', error);
|
|
324
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while sending post notification');
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Sends notifications for alert messages within a time frame
|
|
329
|
+
*
|
|
330
|
+
* @description Processes and sends notifications for alert-type messages
|
|
331
|
+
*
|
|
332
|
+
* @param {NotificationDurationUnitEnum} unit - Time unit for the duration
|
|
333
|
+
* @param {number} value - Number of time units to look back
|
|
334
|
+
* @returns {Promise<boolean | Error>} - Success status or error
|
|
335
|
+
*/
|
|
336
|
+
// @ts-ignore - Type compatibility issue between interface and implementation
|
|
337
|
+
async sendNotificationOfAlertMessages(unit, value) {
|
|
338
|
+
try {
|
|
339
|
+
if (!unit || typeof value !== 'number' || value <= 0) {
|
|
340
|
+
return new Error('Valid time unit and positive value are required');
|
|
67
341
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
342
|
+
this.logger.debug('Processing alert message notifications', {
|
|
343
|
+
unit,
|
|
344
|
+
value
|
|
345
|
+
});
|
|
346
|
+
const posts = await this.getLatestAlertMessage(unit, value);
|
|
347
|
+
if (!posts || posts.length === 0) {
|
|
348
|
+
this.logger.debug('No alert messages found');
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
const notificationPromises = posts.map(post => this.sendNotificationOnPost(post));
|
|
352
|
+
const results = await Promise.all(notificationPromises);
|
|
353
|
+
// Check if any notifications failed
|
|
354
|
+
const failures = results.filter(result => result instanceof Error);
|
|
355
|
+
if (failures.length > 0) {
|
|
356
|
+
this.logger.warn(`${failures.length} alert notifications failed out of ${posts.length}`);
|
|
357
|
+
}
|
|
358
|
+
this.logger.debug('Alert message notifications processed', {
|
|
359
|
+
totalPosts: posts.length,
|
|
360
|
+
failures: failures.length
|
|
361
|
+
});
|
|
362
|
+
return true;
|
|
363
|
+
} catch (error) {
|
|
364
|
+
this.logger.error('Error sending alert message notifications: %o', error);
|
|
365
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while sending alert notifications');
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Sends notifications for unread service messages
|
|
370
|
+
*
|
|
371
|
+
* @description Handles notifications for support service channels
|
|
372
|
+
*
|
|
373
|
+
* @param {NotificationDurationUnitEnum} unit - Time unit for the duration
|
|
374
|
+
* @param {number} value - Number of time units to look back
|
|
375
|
+
* @returns {Promise<boolean | Error>} - Success status or error
|
|
376
|
+
*/
|
|
377
|
+
// @ts-ignore - Type compatibility issue between interface and implementation
|
|
378
|
+
async sendNotificationOfUnreadServiceMessages(unit, value) {
|
|
379
|
+
try {
|
|
380
|
+
if (!unit || typeof value !== 'number' || value <= 0) {
|
|
381
|
+
return new Error('Valid time unit and positive value are required');
|
|
382
|
+
}
|
|
383
|
+
this.logger.debug('Processing service message notifications', {
|
|
384
|
+
unit,
|
|
385
|
+
value
|
|
386
|
+
});
|
|
387
|
+
const threads = await this.getServiceThreads(unit, value);
|
|
388
|
+
if (!threads || threads.length === 0) {
|
|
389
|
+
this.logger.debug('No service threads found');
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
const posts = await Promise.all(threads.map(thread => this.getServicePosts(thread)));
|
|
393
|
+
const validPosts = posts.filter(post => post !== null);
|
|
394
|
+
if (validPosts.length === 0) {
|
|
395
|
+
this.logger.debug('No valid service posts found');
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
const notificationPromises = validPosts.map(post => this.sendServiceNotificationOnPost(post));
|
|
399
|
+
const results = await Promise.all(notificationPromises);
|
|
400
|
+
const failures = results.filter(result => result instanceof Error);
|
|
401
|
+
if (failures.length > 0) {
|
|
402
|
+
this.logger.warn(`${failures.length} service notifications failed out of ${validPosts.length}`);
|
|
403
|
+
}
|
|
404
|
+
this.logger.debug('Service message notifications processed', {
|
|
405
|
+
totalThreads: threads.length,
|
|
406
|
+
validPosts: validPosts.length,
|
|
407
|
+
failures: failures.length
|
|
408
|
+
});
|
|
409
|
+
return true;
|
|
410
|
+
} catch (error) {
|
|
411
|
+
this.logger.error('Error sending service message notifications: %o', error);
|
|
412
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while sending service notifications');
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// Private helper methods
|
|
416
|
+
async getLatestMessageInAllChannels(unit, value) {
|
|
417
|
+
try {
|
|
418
|
+
const today = new Date();
|
|
419
|
+
const lastDay = sub(today, {
|
|
420
|
+
[unit]: value
|
|
421
|
+
});
|
|
422
|
+
const channels = await this.channelRepository.model.aggregate([{
|
|
423
|
+
$match: {
|
|
424
|
+
lastPostAt: {
|
|
425
|
+
$gte: lastDay,
|
|
426
|
+
$lte: today
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}, {
|
|
430
|
+
$project: {
|
|
431
|
+
_id: true,
|
|
432
|
+
lastPostAt: true,
|
|
433
|
+
creator: true,
|
|
434
|
+
members: {
|
|
435
|
+
$filter: {
|
|
436
|
+
input: '$members',
|
|
437
|
+
as: 'item',
|
|
438
|
+
cond: {
|
|
439
|
+
$and: [{
|
|
440
|
+
$lte: ['$$item.lastViewedAt', '$lastPostAt']
|
|
441
|
+
}]
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}]);
|
|
447
|
+
const posts = await this.postRepository.getAll({
|
|
448
|
+
criteria: {
|
|
449
|
+
channel: {
|
|
450
|
+
$in: channels.map(({
|
|
451
|
+
_id
|
|
452
|
+
}) => _id)
|
|
453
|
+
},
|
|
454
|
+
createdAt: {
|
|
455
|
+
$in: channels.map(({
|
|
456
|
+
lastPostAt
|
|
457
|
+
}) => lastPostAt)
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
return channels.map(channel => ({
|
|
462
|
+
...channel,
|
|
463
|
+
post: posts.find(post => post.channel.toString() === channel._id.toString())
|
|
464
|
+
}));
|
|
465
|
+
} catch (error) {
|
|
466
|
+
this.logger.error('Error getting latest messages in channels: %o', error);
|
|
467
|
+
return [];
|
|
468
|
+
}
|
|
74
469
|
}
|
|
75
470
|
groupByMember(channels) {
|
|
76
471
|
return channels.reduce((acc, curr) => {
|
|
@@ -88,447 +483,434 @@ import {__decorate,__param,__metadata}from'tslib';import {omit}from'lodash-es';i
|
|
|
88
483
|
}) => compareAsc(channelMessage.lastPostAt, lastViewedAt), []);
|
|
89
484
|
}
|
|
90
485
|
async sendMail(user, channels) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
} = this.users[user];
|
|
100
|
-
await this.broker.call(`${MoleculerServiceName.MailService}.${MailServiceAction.Send}`, {
|
|
101
|
-
request: {
|
|
102
|
-
topic: 'Unread Messages Notification',
|
|
103
|
-
to: notificationEmail || email,
|
|
104
|
-
templateId: MailTemplateId.MessageNotificationServiceId,
|
|
105
|
-
from: config.MAIL_SEND_DEFAULT_EMAIL,
|
|
106
|
-
subject: `You have Unread Messages in ${channels.length} chats`,
|
|
107
|
-
variables: {
|
|
108
|
-
text: `Hey ${username},\nYou have Unread Messages in ${channels.length} chats`,
|
|
109
|
-
user,
|
|
110
|
-
channel: channels
|
|
111
|
-
}
|
|
486
|
+
try {
|
|
487
|
+
this.logger.debug('Sending email notification', {
|
|
488
|
+
userId: user,
|
|
489
|
+
channelCount: channels.length
|
|
490
|
+
});
|
|
491
|
+
const userAccount = this.users[user];
|
|
492
|
+
if (!userAccount) {
|
|
493
|
+
throw new Error(`User account not found for user ${user}`);
|
|
112
494
|
}
|
|
113
|
-
|
|
495
|
+
const {
|
|
496
|
+
notificationEmail,
|
|
497
|
+
email,
|
|
498
|
+
username
|
|
499
|
+
} = userAccount;
|
|
500
|
+
await this.broker.call(`${MoleculerServiceName.MailService}.${MailServiceAction.Send}`, {
|
|
501
|
+
request: {
|
|
502
|
+
topic: 'Unread Messages Notification',
|
|
503
|
+
to: notificationEmail || email,
|
|
504
|
+
templateId: MailTemplateId.MessageNotificationServiceId,
|
|
505
|
+
from: config.MAIL_SEND_DEFAULT_EMAIL,
|
|
506
|
+
subject: `You have Unread Messages in ${channels.length} chats`,
|
|
507
|
+
variables: {
|
|
508
|
+
text: `Hey ${username},\nYou have Unread Messages in ${channels.length} chats`,
|
|
509
|
+
user,
|
|
510
|
+
channel: channels
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
} catch (error) {
|
|
515
|
+
this.logger.error('Error sending email notification: %o', error);
|
|
516
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while sending email');
|
|
517
|
+
}
|
|
114
518
|
}
|
|
115
519
|
async sendText(user, channels) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// #Todo Get User Full Name and Phone Number
|
|
121
|
-
const {
|
|
122
|
-
username,
|
|
123
|
-
phoneNumber
|
|
124
|
-
} = this.users[user];
|
|
125
|
-
const [number] = phoneNumber;
|
|
126
|
-
if (!number) {
|
|
127
|
-
this.logger.debug('Outgoing request to SendSMS Skipped, reason No phoneNumber found', {
|
|
128
|
-
user: this.users[user],
|
|
129
|
-
channels
|
|
520
|
+
try {
|
|
521
|
+
this.logger.debug('Sending SMS notification', {
|
|
522
|
+
userId: user,
|
|
523
|
+
channelCount: channels.length
|
|
130
524
|
});
|
|
525
|
+
const userAccount = this.users[user];
|
|
526
|
+
if (!userAccount) {
|
|
527
|
+
throw new Error(`User account not found for user ${user}`);
|
|
528
|
+
}
|
|
529
|
+
const {
|
|
530
|
+
username,
|
|
531
|
+
phoneNumber
|
|
532
|
+
} = userAccount;
|
|
533
|
+
const [number] = phoneNumber || [];
|
|
534
|
+
if (!number) {
|
|
535
|
+
this.logger.debug('SMS notification skipped - no phone number', {
|
|
536
|
+
userId: user
|
|
537
|
+
});
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
await this.broker.call(`${MoleculerServiceName.SmsService}.${SmsServiceActions.Send}`, {
|
|
541
|
+
to: number,
|
|
542
|
+
body: `Hey ${username},\nYou have Unread Messages in ${channels.length} chats`
|
|
543
|
+
});
|
|
544
|
+
} catch (error) {
|
|
545
|
+
this.logger.error('Error sending SMS notification: %o', error);
|
|
546
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while sending SMS');
|
|
131
547
|
}
|
|
132
|
-
await this.broker.call(`${MoleculerServiceName.SmsService}.${SmsServiceActions.Send}`, {
|
|
133
|
-
to: number,
|
|
134
|
-
body: `Hey ${username},\nYou have Unread Messages in ${channels.length} chats`
|
|
135
|
-
});
|
|
136
548
|
}
|
|
137
549
|
async sendPushNotifications(user, channels) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
channels
|
|
550
|
+
this.logger.debug('Push notification placeholder called', {
|
|
551
|
+
userId: user,
|
|
552
|
+
channelCount: channels.length
|
|
142
553
|
});
|
|
143
|
-
|
|
554
|
+
// Placeholder for future push notification implementation
|
|
144
555
|
}
|
|
145
|
-
getGlobalNotificationSettings(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
556
|
+
async getGlobalNotificationSettings(userId) {
|
|
557
|
+
try {
|
|
558
|
+
// Placeholder for preference service integration
|
|
559
|
+
return {
|
|
560
|
+
global: {
|
|
561
|
+
notification: {
|
|
562
|
+
offlineMessages: {
|
|
563
|
+
sms: true,
|
|
564
|
+
email: true,
|
|
565
|
+
browser: true
|
|
566
|
+
}
|
|
154
567
|
}
|
|
155
568
|
}
|
|
156
|
-
}
|
|
157
|
-
})
|
|
569
|
+
};
|
|
570
|
+
} catch (error) {
|
|
571
|
+
this.logger.error('Error getting notification settings: %o', error);
|
|
572
|
+
return {
|
|
573
|
+
global: {
|
|
574
|
+
notification: {
|
|
575
|
+
offlineMessages: {
|
|
576
|
+
sms: false,
|
|
577
|
+
email: false,
|
|
578
|
+
browser: false
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
}
|
|
158
584
|
}
|
|
159
585
|
async getUser(id) {
|
|
160
|
-
|
|
161
|
-
id
|
|
162
|
-
|
|
586
|
+
try {
|
|
587
|
+
if (!id) {
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
590
|
+
return await this.broker.call(`${MoleculerServiceName.AccountUser}.${AccountServiceAction.FindAccountById}`, {
|
|
591
|
+
id
|
|
592
|
+
});
|
|
593
|
+
} catch (error) {
|
|
594
|
+
this.logger.error('Error getting user: %o', error);
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
163
597
|
}
|
|
164
598
|
async fetchUsersForMembers(users) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
599
|
+
try {
|
|
600
|
+
const missingUsers = users.filter(user => !Object.keys(this.users).includes(user));
|
|
601
|
+
await Promise.all(missingUsers.map(async user => {
|
|
602
|
+
const userAccount = await this.getUser(user);
|
|
603
|
+
if (userAccount) {
|
|
604
|
+
this.users[user] = userAccount;
|
|
605
|
+
this.userPreferences[user] = await this.getGlobalNotificationSettings(user);
|
|
606
|
+
}
|
|
607
|
+
}));
|
|
608
|
+
} catch (error) {
|
|
609
|
+
this.logger.error('Error fetching users: %o', error);
|
|
610
|
+
}
|
|
169
611
|
}
|
|
170
612
|
async sendNotificationToUser([user, channels]) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
613
|
+
try {
|
|
614
|
+
await this.fetchUsersForMembers([user, ...channels.map(({
|
|
615
|
+
post
|
|
616
|
+
}) => post.editedBy.toString())]);
|
|
617
|
+
const userPrefs = this.userPreferences[user];
|
|
618
|
+
const {
|
|
619
|
+
email,
|
|
620
|
+
sms,
|
|
621
|
+
browser
|
|
622
|
+
} = userPrefs?.global?.notification?.offlineMessages || {};
|
|
623
|
+
const filteredChannels = channels.filter(({
|
|
624
|
+
post
|
|
625
|
+
}) => user !== post.editedBy.toString());
|
|
626
|
+
if (filteredChannels.length === 0) {
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
const notificationPromises = [];
|
|
630
|
+
if (email) {
|
|
631
|
+
notificationPromises.push(this.sendMail(user, filteredChannels));
|
|
632
|
+
}
|
|
633
|
+
if (sms) {
|
|
634
|
+
notificationPromises.push(this.sendText(user, filteredChannels));
|
|
635
|
+
}
|
|
636
|
+
if (browser) {
|
|
637
|
+
notificationPromises.push(this.sendPushNotifications(user, filteredChannels));
|
|
638
|
+
}
|
|
639
|
+
await Promise.all(notificationPromises);
|
|
640
|
+
} catch (error) {
|
|
641
|
+
this.logger.error('Error sending notification to user: %o', error);
|
|
642
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while sending user notification');
|
|
193
643
|
}
|
|
194
644
|
}
|
|
195
|
-
async sendNotificationOfUnreadMessages(unit, value) {
|
|
196
|
-
const channels = await this.getLatestMessageInAllChannels(unit, value);
|
|
197
|
-
const membersWithUnreadMessagesInChannels = this.groupByMember(channels);
|
|
198
|
-
await Promise.all(Object.entries(membersWithUnreadMessagesInChannels).map(this.sendNotificationToUser.bind(this)));
|
|
199
|
-
}
|
|
200
|
-
async sendPushNotificationsExpo(data) {
|
|
201
|
-
this.logger.debug('Outgoing request to ExpoPushNotification');
|
|
202
|
-
const response = await fetch('https://exp.host/--/api/v2/push/send/', {
|
|
203
|
-
method: 'POST',
|
|
204
|
-
headers: {
|
|
205
|
-
Accept: 'application/json',
|
|
206
|
-
'Accept-Encoding': 'gzip, deflate',
|
|
207
|
-
'Content-Type': 'application/json'
|
|
208
|
-
},
|
|
209
|
-
body: JSON.stringify(data)
|
|
210
|
-
});
|
|
211
|
-
const result = await response.json();
|
|
212
|
-
this.logger.debug('ExpoPushNotification response', result);
|
|
213
|
-
return result;
|
|
214
|
-
}
|
|
215
645
|
async getThreadWithParticipants(id) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
646
|
+
try {
|
|
647
|
+
const thread = await this.postThreadRepository.model.findOne({
|
|
648
|
+
_id: id
|
|
649
|
+
}).lean();
|
|
650
|
+
if (!thread) {
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
const participants = await Promise.all((thread.participants || []).map(async m => ({
|
|
654
|
+
...m,
|
|
655
|
+
user: await this.getUser(m?.user?.toString())
|
|
656
|
+
})));
|
|
657
|
+
return {
|
|
658
|
+
...thread,
|
|
659
|
+
participants
|
|
660
|
+
};
|
|
661
|
+
} catch (error) {
|
|
662
|
+
this.logger.error('Error getting thread with participants: %o', error);
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
230
665
|
}
|
|
231
666
|
async getChannelWithMembers(id) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
667
|
+
try {
|
|
668
|
+
const channel = await this.channelRepository.model.findOne({
|
|
669
|
+
_id: id
|
|
670
|
+
}).lean();
|
|
671
|
+
if (!channel) {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
const members = await Promise.all((channel.members || []).map(async m => ({
|
|
675
|
+
...m,
|
|
676
|
+
user: await this.getUser(m?.user?.toString())
|
|
677
|
+
})));
|
|
678
|
+
return {
|
|
679
|
+
...channel,
|
|
680
|
+
members
|
|
681
|
+
};
|
|
682
|
+
} catch (error) {
|
|
683
|
+
this.logger.error('Error getting channel with members: %o', error);
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
246
686
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const postThread = notificationParams?.thread?.id ? await this.getThreadWithParticipants(notificationParams?.thread?.id?.toString()) : null;
|
|
254
|
-
const channel = await this.getChannelWithMembers(currentPost?.channel?.toString());
|
|
255
|
-
const userId = notificationParams?.senderId?.toString() || currentPost?.editedBy?.toString();
|
|
256
|
-
const user = await this.getUser(userId);
|
|
257
|
-
const {
|
|
258
|
-
givenName,
|
|
259
|
-
familyName,
|
|
260
|
-
username
|
|
261
|
-
} = user;
|
|
262
|
-
const fullName = givenName ? givenName + ' ' + familyName : '';
|
|
263
|
-
const notificationParamsOtherData = notificationParams?.other ?? null;
|
|
264
|
-
const title = notificationParams && notificationParams?.title ? notificationParams?.title : fullName ? fullName : 'Message';
|
|
265
|
-
const body = notificationParams && notificationParams?.body ? notificationParams?.body : currentPost?.message == '' ? 'Send a file' : currentPost?.message;
|
|
266
|
-
const sound = notificationParamsOtherData?.sound ?? 'default';
|
|
267
|
-
let tokens;
|
|
268
|
-
if (postThread) {
|
|
269
|
-
tokens = postThread?.participants?.filter(p => p?.user?.id != userId)?.map(m => m?.user?.tokens?.filter(t => t?.type == 'EXPO_NOTIFICATION_TOKEN')?.map(et => et?.token) ?? [])?.flat(1)?.filter(t => t)?.filter((value, index, array) => array.indexOf(value) === index) ?? [];
|
|
270
|
-
} else {
|
|
271
|
-
tokens = channel?.members?.filter(member => member?.user?.id != userId)?.map(m => m?.user?.tokens?.filter(t => t?.type == 'EXPO_NOTIFICATION_TOKEN')?.map(et => et?.token) ?? [])?.flat(1)?.filter(t => t)?.filter((value, index, array) => array.indexOf(value) === index) ?? [];
|
|
687
|
+
extractNotificationTokens(entities, entityType) {
|
|
688
|
+
try {
|
|
689
|
+
return entities.map(entity => entity?.user?.tokens?.filter(t => t?.type === 'EXPO_NOTIFICATION_TOKEN')?.map(et => et?.token) || []).flat().filter(token => token).filter((value, index, array) => array.indexOf(value) === index);
|
|
690
|
+
} catch (error) {
|
|
691
|
+
this.logger.error(`Error extracting notification tokens from ${entityType}: %o`, error);
|
|
692
|
+
return [];
|
|
272
693
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
694
|
+
}
|
|
695
|
+
extractTemplateVariables(variables) {
|
|
696
|
+
try {
|
|
697
|
+
return variables && typeof variables === 'object' && variables.constructor === Object ? {
|
|
698
|
+
...variables
|
|
699
|
+
} : null;
|
|
700
|
+
} catch (error) {
|
|
701
|
+
this.logger.error('Error extracting template variables: %o', error);
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
findChannelOwner(channel) {
|
|
706
|
+
try {
|
|
707
|
+
return channel?.members?.filter(m => m?.roles === 'OWNER')?.map(u => u?.user?.toString())?.[0] || null;
|
|
708
|
+
} catch (error) {
|
|
709
|
+
this.logger.error('Error finding channel owner: %o', error);
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
prepareNotificationContent(post, user, owner, guestVariables, hostVariables) {
|
|
714
|
+
try {
|
|
715
|
+
// Prepare subject and content based on post properties
|
|
716
|
+
const postPropsAttachmentKeys = post?.props?.attachment ? Object.keys(post?.props?.attachment) : null;
|
|
717
|
+
const attachmentRefKey = postPropsAttachmentKeys?.find(key => key?.toLowerCase() === post?.props?.ref?.toLowerCase());
|
|
718
|
+
const refType = attachmentRefKey ? post?.props?.attachment[attachmentRefKey]?.type?.toLowerCase() : ' Notification';
|
|
719
|
+
const subject = post?.props?.ref ? `${post.props.ref.charAt(0).toUpperCase() + post.props.ref.slice(1)} ${refType.charAt(0).toUpperCase() + refType.slice(1)}` : 'You have Unread Messages in 1 chats';
|
|
720
|
+
const userName = user.givenName && user.familyName ? `${user.givenName} ${user.familyName}` : user.username || 'User';
|
|
721
|
+
const ownerName = owner?.givenName && owner?.familyName ? `${owner.givenName} ${owner.familyName}` : owner?.username || 'Owner';
|
|
722
|
+
return {
|
|
723
|
+
guestSubject: guestVariables?.subject || subject,
|
|
724
|
+
hostSubject: hostVariables?.subject || subject,
|
|
725
|
+
guestText: guestVariables?.bodyText || `Hi ${userName}, ${post?.message}`,
|
|
726
|
+
hostText: hostVariables?.bodyText || `Hi ${ownerName}, ${post?.message}`
|
|
288
727
|
};
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
728
|
+
} catch (error) {
|
|
729
|
+
this.logger.error('Error preparing notification content: %o', error);
|
|
730
|
+
return {
|
|
731
|
+
guestSubject: 'Notification',
|
|
732
|
+
hostSubject: 'Notification',
|
|
733
|
+
guestText: 'You have a new message',
|
|
734
|
+
hostText: 'You have a new message'
|
|
735
|
+
};
|
|
736
|
+
}
|
|
292
737
|
}
|
|
293
738
|
async sendMailOnPost(user, post, channel, subject, text, templateId = null, variables = null, recipientMail = null) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
739
|
+
try {
|
|
740
|
+
this.logger.debug('Sending mail notification for post', {
|
|
741
|
+
userId: user.id,
|
|
742
|
+
postId: post.id,
|
|
743
|
+
hasTemplate: !!templateId
|
|
744
|
+
});
|
|
745
|
+
const {
|
|
746
|
+
notificationEmail,
|
|
747
|
+
email
|
|
748
|
+
} = user;
|
|
749
|
+
await this.broker.call(`${MoleculerServiceName.MailService}.${MailServiceAction.Send}`, {
|
|
750
|
+
request: {
|
|
751
|
+
topic: subject,
|
|
752
|
+
to: recipientMail || notificationEmail || email,
|
|
753
|
+
templateId: templateId || MailTemplateId.MessageNotificationServiceId,
|
|
754
|
+
namespace: config.NAMESPACE,
|
|
755
|
+
from: config.MAIL_SEND_DEFAULT_EMAIL,
|
|
756
|
+
subject,
|
|
757
|
+
variables: variables || {
|
|
758
|
+
text,
|
|
759
|
+
user,
|
|
760
|
+
post,
|
|
761
|
+
channel
|
|
762
|
+
}
|
|
316
763
|
}
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
async sendSmsOnPost(user, post, text) {
|
|
321
|
-
this.logger.debug('Outgoing request to SendSMS', {
|
|
322
|
-
user,
|
|
323
|
-
post
|
|
324
|
-
});
|
|
325
|
-
const {
|
|
326
|
-
username,
|
|
327
|
-
givenName,
|
|
328
|
-
familyName,
|
|
329
|
-
phoneNumber
|
|
330
|
-
} = user;
|
|
331
|
-
const [number] = phoneNumber;
|
|
332
|
-
const {
|
|
333
|
-
countryCode,
|
|
334
|
-
phoneNumber: mobile
|
|
335
|
-
} = number;
|
|
336
|
-
const countryData = await this.countryRepository.model.find({
|
|
337
|
-
_id: countryCode
|
|
338
|
-
});
|
|
339
|
-
const [country] = countryData;
|
|
340
|
-
const {
|
|
341
|
-
phone_code
|
|
342
|
-
} = country;
|
|
343
|
-
const mobileWithCountryCode = phone_code + mobile;
|
|
344
|
-
if (!mobileWithCountryCode) {
|
|
345
|
-
this.logger.debug('Outgoing request to SendSMS Skipped, reason No phoneNumber found', {
|
|
346
|
-
user: user,
|
|
347
|
-
post
|
|
348
764
|
});
|
|
765
|
+
} catch (error) {
|
|
766
|
+
this.logger.error('Error sending mail for post: %o', error);
|
|
767
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while sending mail');
|
|
349
768
|
}
|
|
350
|
-
await this.broker.call(`${MoleculerServiceName.SmsService}.${SmsServiceActions.Send}`, {
|
|
351
|
-
to: mobileWithCountryCode,
|
|
352
|
-
body: text
|
|
353
|
-
});
|
|
354
769
|
}
|
|
355
|
-
async
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const {
|
|
371
|
-
givenName,
|
|
372
|
-
familyName,
|
|
373
|
-
username
|
|
374
|
-
} = user;
|
|
375
|
-
const userPreferences = await this.getGlobalNotificationSettings(user);
|
|
376
|
-
const {
|
|
377
|
-
email,
|
|
378
|
-
sms,
|
|
379
|
-
browser
|
|
380
|
-
} = userPreferences?.global?.notification?.offlineMessages || {};
|
|
381
|
-
const channel = await this.channelRepository.model.findOne({
|
|
382
|
-
_id: currentPost?.channel
|
|
383
|
-
});
|
|
384
|
-
const channelOwner = channel?.members?.filter(m => m?.roles == 'OWNER')?.map(u => u?.user?.toString())?.[0] ?? null;
|
|
385
|
-
const hostId = hostTemlateVarables?.userId?.toString() || channelOwner;
|
|
386
|
-
const owner = hostId ? await this.getUser(hostId) : null;
|
|
387
|
-
const {
|
|
388
|
-
givenName: ownerGivenName,
|
|
389
|
-
familyName: ownerFamilyName,
|
|
390
|
-
username: ownerUserName
|
|
391
|
-
} = owner;
|
|
392
|
-
const postPropsAttachmentKeys = currentPost?.props?.attachment ? Object.keys(currentPost?.props?.attachment) : null;
|
|
393
|
-
const attachmentRefKey = postPropsAttachmentKeys ? Array.isArray(postPropsAttachmentKeys) ? postPropsAttachmentKeys.filter(v => v == currentPost?.props?.ref.toLowerCase())[0] : Object.keys(currentPost?.props?.attachment).find(key => key?.toLowerCase() === currentPost?.props?.ref?.toLowerCase()) : null;
|
|
394
|
-
const refType = attachmentRefKey ? currentPost?.props?.attachment[attachmentRefKey]?.type?.toLowerCase() : ' Notification';
|
|
395
|
-
currentPost?.props?.ref?.toLowerCase()?.charAt(0)?.toUpperCase() + currentPost?.props?.ref?.toLowerCase()?.slice(1);
|
|
396
|
-
refType?.toLowerCase()?.charAt(0)?.toUpperCase() + refType?.toLowerCase()?.slice(1);
|
|
397
|
-
const subject = currentPost?.props?.ref ? `${currentPost.props.ref.toLowerCase().charAt(0).toUpperCase() + currentPost.props.ref.toLowerCase().slice(1)} ${refType.toLowerCase().charAt(0).toUpperCase() + refType.toLowerCase().slice(1)}` : 'You have Unread Messages in 1 chats';
|
|
398
|
-
const guestSubject = guestTemlateVarables?.subject || subject;
|
|
399
|
-
const hostSubject = hostTemlateVarables?.subject || subject;
|
|
400
|
-
const userName = givenName + ' ' + familyName || username;
|
|
401
|
-
const userText = `Hi ${userName}, ${currentPost?.message}`;
|
|
402
|
-
const ownerName = ownerGivenName + ' ' + ownerFamilyName || ownerUserName;
|
|
403
|
-
const ownerText = `Hi ${ownerName}, ${currentPost?.message}`;
|
|
404
|
-
const guestText = guestTemlateVarables?.bodyText || userText;
|
|
405
|
-
const hostText = hostTemlateVarables?.bodyText || ownerText;
|
|
406
|
-
if (email) {
|
|
407
|
-
if (guestTemlateVarables && !guestTemlateVarables.hasOwnProperty('text')) guestTemlateVarables.text = guestText;
|
|
408
|
-
if (guestTemlateVarables && !guestTemlateVarables.hasOwnProperty('user')) guestTemlateVarables.user = user;
|
|
409
|
-
if (guestTemlateVarables && !guestTemlateVarables.hasOwnProperty('post')) guestTemlateVarables.post = currentPost;
|
|
410
|
-
if (guestTemlateVarables && !guestTemlateVarables.hasOwnProperty('channel')) guestTemlateVarables.channel = channel;
|
|
411
|
-
await this.sendMailOnPost(user, currentPost, channel, guestSubject, guestText, guestTemplateId, guestTemlateVarables);
|
|
412
|
-
if (owner) {
|
|
413
|
-
if (hostTemlateVarables && !hostTemlateVarables.hasOwnProperty('text')) hostTemlateVarables.text = hostText;
|
|
414
|
-
if (hostTemlateVarables && !hostTemlateVarables.hasOwnProperty('user')) hostTemlateVarables.user = owner;
|
|
415
|
-
if (hostTemlateVarables && !hostTemlateVarables.hasOwnProperty('post')) hostTemlateVarables.post = currentPost;
|
|
416
|
-
if (hostTemlateVarables && !hostTemlateVarables.hasOwnProperty('channel')) hostTemlateVarables.channel = channel;
|
|
417
|
-
await this.sendMailOnPost(owner, currentPost, channel, hostSubject, hostText, hostTemplateId, hostTemlateVarables);
|
|
770
|
+
async sendSmsOnPost(user, post, text) {
|
|
771
|
+
try {
|
|
772
|
+
this.logger.debug('Sending SMS notification for post', {
|
|
773
|
+
userId: user.id,
|
|
774
|
+
postId: post.id
|
|
775
|
+
});
|
|
776
|
+
const {
|
|
777
|
+
phoneNumber
|
|
778
|
+
} = user;
|
|
779
|
+
const [number] = phoneNumber || [];
|
|
780
|
+
if (!number) {
|
|
781
|
+
this.logger.debug('SMS notification skipped - no phone number', {
|
|
782
|
+
userId: user.id
|
|
783
|
+
});
|
|
784
|
+
return;
|
|
418
785
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
786
|
+
const {
|
|
787
|
+
countryCode,
|
|
788
|
+
phoneNumber: mobile
|
|
789
|
+
} = number;
|
|
790
|
+
const countryData = await this.countryRepository.model.find({
|
|
791
|
+
_id: countryCode
|
|
792
|
+
});
|
|
793
|
+
const [country] = countryData || [];
|
|
794
|
+
if (!country) {
|
|
795
|
+
throw new Error('Country data not found for phone number');
|
|
424
796
|
}
|
|
797
|
+
const {
|
|
798
|
+
phone_code
|
|
799
|
+
} = country;
|
|
800
|
+
const mobileWithCountryCode = phone_code + mobile;
|
|
801
|
+
await this.broker.call(`${MoleculerServiceName.SmsService}.${SmsServiceActions.Send}`, {
|
|
802
|
+
to: mobileWithCountryCode,
|
|
803
|
+
body: text
|
|
804
|
+
});
|
|
805
|
+
} catch (error) {
|
|
806
|
+
this.logger.error('Error sending SMS for post: %o', error);
|
|
807
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while sending SMS');
|
|
425
808
|
}
|
|
426
|
-
return true;
|
|
427
809
|
}
|
|
428
810
|
async getLatestAlertMessage(unit, value) {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
811
|
+
try {
|
|
812
|
+
const today = new Date();
|
|
813
|
+
const lastDay = sub(today, {
|
|
814
|
+
[unit]: value
|
|
815
|
+
});
|
|
816
|
+
const filteredPosts = await this.postRepository.model.aggregate([{
|
|
817
|
+
$match: {
|
|
818
|
+
type: PostTypeEnum.Alert,
|
|
819
|
+
updatedAt: {
|
|
820
|
+
$gte: lastDay,
|
|
821
|
+
$lte: today
|
|
822
|
+
}
|
|
439
823
|
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
return
|
|
448
|
-
}
|
|
449
|
-
return posts;
|
|
450
|
-
}
|
|
451
|
-
async sendNotificationOfAlertMessages(unit, value) {
|
|
452
|
-
const posts = await this.getLatestAlertMessage(unit, value);
|
|
453
|
-
if (posts && posts?.length > 0) await Promise.all(posts.map(this.sendNotificationOnPost.bind(this)));
|
|
824
|
+
}]);
|
|
825
|
+
return filteredPosts?.map(p => ({
|
|
826
|
+
...p,
|
|
827
|
+
editedBy: p?.editedBy?.toString()
|
|
828
|
+
})) || [];
|
|
829
|
+
} catch (error) {
|
|
830
|
+
this.logger.error('Error getting latest alert messages: %o', error);
|
|
831
|
+
return [];
|
|
832
|
+
}
|
|
454
833
|
}
|
|
455
834
|
async sendServiceNotificationOnPost(post) {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
835
|
+
try {
|
|
836
|
+
const currentPost = {
|
|
837
|
+
...post
|
|
838
|
+
};
|
|
839
|
+
const channel = await this.getChannelWithMembers(currentPost?.channel?.toString());
|
|
840
|
+
if (!channel) {
|
|
841
|
+
return new Error('Channel not found for service notification');
|
|
842
|
+
}
|
|
843
|
+
const hostId = this.findChannelOwner(channel);
|
|
844
|
+
const userId = hostId && currentPost?.editedBy?.toString() !== hostId ? hostId : null;
|
|
845
|
+
if (!userId || !this.supportServiceenvConfig?.email) {
|
|
846
|
+
this.logger.debug('Service notification skipped - no recipient or config');
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
466
849
|
const postUserId = currentPost?.editedBy?.toString();
|
|
467
850
|
const postUser = await this.getUser(postUserId);
|
|
468
|
-
const {
|
|
469
|
-
givenName: postUserGivenName,
|
|
470
|
-
familyName: postUserFamilyname,
|
|
471
|
-
username: postUserUsername
|
|
472
|
-
} = postUser;
|
|
473
|
-
const postUserFullName = postUserGivenName + ' ' + postUserFamilyname || postUserUsername;
|
|
474
851
|
const user = await this.getUser(userId);
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
email
|
|
481
|
-
} = user;
|
|
852
|
+
if (!postUser || !user) {
|
|
853
|
+
return new Error('Required users not found for service notification');
|
|
854
|
+
}
|
|
855
|
+
const postUserFullName = postUser.givenName && postUser.familyName ? `${postUser.givenName} ${postUser.familyName}` : postUser.username || 'User';
|
|
856
|
+
const userName = user.givenName && user.familyName ? `${user.givenName} ${user.familyName}` : user.username || 'User';
|
|
482
857
|
const subject = 'You have Unread Messages in support service chat';
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
858
|
+
const userText = `Hi, you have unread message in support service chat sent by ${postUserFullName} - \n ${currentPost?.message}.`;
|
|
859
|
+
await this.sendMailOnPost(user, currentPost, channel, subject, userText, this.supportServiceenvConfig?.templateId || null, this.supportServiceenvConfig?.variables || null, this.supportServiceenvConfig?.email);
|
|
860
|
+
} catch (error) {
|
|
861
|
+
this.logger.error('Error sending service notification: %o', error);
|
|
862
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while sending service notification');
|
|
486
863
|
}
|
|
487
864
|
}
|
|
488
865
|
async getServiceThreads(unit, value) {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}, {
|
|
498
|
-
$project: {
|
|
499
|
-
_id: {
|
|
500
|
-
$toString: '$_id'
|
|
866
|
+
try {
|
|
867
|
+
const today = new Date();
|
|
868
|
+
const lastDay = sub(today, {
|
|
869
|
+
[unit]: value
|
|
870
|
+
});
|
|
871
|
+
const channels = await this.channelRepository.model.aggregate([{
|
|
872
|
+
$match: {
|
|
873
|
+
type: RoomType.Service
|
|
501
874
|
}
|
|
502
|
-
}
|
|
503
|
-
}]);
|
|
504
|
-
const channelIds = channels?.map(c => c?._id?.toString())?.filter(id => id);
|
|
505
|
-
const postThreads = await this.postThreadRepository.model.find({
|
|
506
|
-
lastReplyAt: {
|
|
507
|
-
$gte: lastDay,
|
|
508
|
-
$lte: today
|
|
509
|
-
},
|
|
510
|
-
channel: {
|
|
511
|
-
$in: channelIds
|
|
512
|
-
}
|
|
513
|
-
});
|
|
514
|
-
return postThreads;
|
|
515
|
-
}
|
|
516
|
-
async getServicePosts(postThread) {
|
|
517
|
-
const post = await this.postRepository.model.findOne({
|
|
518
|
-
$or: [{
|
|
519
|
-
parentId: postThread?.post?.toString()
|
|
520
875
|
}, {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
876
|
+
$project: {
|
|
877
|
+
_id: {
|
|
878
|
+
$toString: '$_id'
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}]);
|
|
882
|
+
const channelIds = channels?.map(c => c?._id?.toString())?.filter(id => id) || [];
|
|
883
|
+
const postThreads = await this.postThreadRepository.model.find({
|
|
884
|
+
lastReplyAt: {
|
|
885
|
+
$gte: lastDay,
|
|
886
|
+
$lte: today
|
|
887
|
+
},
|
|
888
|
+
channel: {
|
|
889
|
+
$in: channelIds
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
return postThreads || [];
|
|
893
|
+
} catch (error) {
|
|
894
|
+
this.logger.error('Error getting service threads: %o', error);
|
|
895
|
+
return [];
|
|
896
|
+
}
|
|
527
897
|
}
|
|
528
|
-
async
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
898
|
+
async getServicePosts(postThread) {
|
|
899
|
+
try {
|
|
900
|
+
const post = await this.postRepository.model.findOne({
|
|
901
|
+
$or: [{
|
|
902
|
+
parentId: postThread?.post?.toString()
|
|
903
|
+
}, {
|
|
904
|
+
_id: postThread?.post?.toString()
|
|
905
|
+
}]
|
|
906
|
+
}).lean().sort({
|
|
907
|
+
updatedAt: -1
|
|
908
|
+
});
|
|
909
|
+
return post || null;
|
|
910
|
+
} catch (error) {
|
|
911
|
+
this.logger.error('Error getting service posts: %o', error);
|
|
912
|
+
return null;
|
|
913
|
+
}
|
|
532
914
|
}
|
|
533
915
|
};
|
|
534
916
|
MessengerNotificationService = __decorate([injectable(), __param(0, inject(SERVER_TYPES.ChannelRepository)), __param(1, inject(SERVER_TYPES.PostRepository)), __param(2, inject(SERVER_TYPES.PostThreadRepository)), __param(3, inject(SERVER_TYPES.CountryRepository)), __param(4, inject(CommonType.MOLECULER_BROKER)), __param(5, inject('Logger')), __metadata("design:paramtypes", [Object, Object, Object, Object, ServiceBroker, Object])], MessengerNotificationService);export{MessengerNotificationService};//# sourceMappingURL=messenger-notification-service.js.map
|