notifications big

This commit is contained in:
alma 2026-01-11 22:42:00 +01:00
parent af1635b7da
commit 95fc180d3c
2 changed files with 199 additions and 5 deletions

View File

@ -315,9 +315,203 @@ export class RocketChatAdapter implements NotificationAdapter {
}
async getNotifications(userId: string, page = 1, limit = 20): Promise<Notification[]> {
// For now, return empty array - we can implement this later if needed
// The notification count is what matters for the badge
return [];
logger.debug('[ROCKETCHAT_ADAPTER] getNotifications called', { userId, page, limit });
try {
const email = await this.getUserEmail();
if (!email) {
logger.error('[ROCKETCHAT_ADAPTER] Could not get user email');
return [];
}
const rocketChatUserId = await this.getRocketChatUserId(email);
if (!rocketChatUserId) {
logger.debug('[ROCKETCHAT_ADAPTER] User not found in RocketChat');
return [];
}
const userToken = await this.getUserToken(rocketChatUserId);
if (!userToken) {
logger.error('[ROCKETCHAT_ADAPTER] Could not get user token');
return [];
}
const userHeaders = {
'X-Auth-Token': userToken.authToken,
'X-User-Id': userToken.userId,
'Content-Type': 'application/json'
};
// Get user's subscriptions with unread messages
const subscriptionsResponse = await fetch(`${this.baseUrl}/api/v1/subscriptions.get`, {
method: 'GET',
headers: userHeaders
});
if (!subscriptionsResponse.ok) {
logger.error('[ROCKETCHAT_ADAPTER] Failed to get subscriptions', {
status: subscriptionsResponse.status,
});
return [];
}
const subscriptionsData = await subscriptionsResponse.json();
if (!subscriptionsData.success || !Array.isArray(subscriptionsData.update)) {
logger.error('[ROCKETCHAT_ADAPTER] Invalid subscriptions response');
return [];
}
// Filter subscriptions with unread messages
const userSubscriptions = subscriptionsData.update.filter((sub: any) => {
return (sub.unread > 0 || sub.alert) && ['d', 'c', 'p'].includes(sub.t);
});
// Get user info for comparison
const usersResponse = await fetch(`${this.baseUrl}/api/v1/users.list`, {
method: 'GET',
headers: {
'X-Auth-Token': process.env.ROCKET_CHAT_TOKEN!,
'X-User-Id': process.env.ROCKET_CHAT_USER_ID!,
'Content-Type': 'application/json'
}
});
let currentUser: any = null;
if (usersResponse.ok) {
const usersData = await usersResponse.json();
if (usersData.success && Array.isArray(usersData.users)) {
const username = email.split('@')[0];
currentUser = usersData.users.find((u: any) =>
u.username === username || u.emails?.some((e: any) => e.address === email)
);
}
}
const notifications: Notification[] = [];
// Fetch messages for each subscription with unread messages
for (const subscription of userSubscriptions) {
try {
// Determine the correct endpoint based on room type
let endpoint;
switch (subscription.t) {
case 'c':
endpoint = 'channels.messages';
break;
case 'p':
endpoint = 'groups.messages';
break;
case 'd':
endpoint = 'im.messages';
break;
default:
continue;
}
const queryParams = new URLSearchParams({
roomId: subscription.rid,
count: String(Math.max(subscription.unread, 1)) // Fetch at least 1 message
});
const messagesResponse = await fetch(
`${this.baseUrl}/api/v1/${endpoint}?${queryParams}`, {
method: 'GET',
headers: userHeaders
}
);
if (!messagesResponse.ok) {
logger.error('[ROCKETCHAT_ADAPTER] Failed to get messages for room', {
roomName: subscription.name,
status: messagesResponse.status,
});
continue;
}
const messageData = await messagesResponse.json();
if (messageData.success && messageData.messages?.length > 0) {
// Get the latest unread message (skip own messages)
const unreadMessages = messageData.messages.filter((msg: any) => {
// Skip messages sent by current user
if (currentUser && msg.u?._id === currentUser._id) {
return false;
}
// Skip system messages
if (msg.t || !msg.msg) {
return false;
}
return true;
});
if (unreadMessages.length > 0) {
const latestMessage = unreadMessages[0];
const messageUser = latestMessage.u || {};
const roomName = subscription.fname || subscription.name || subscription.name;
// Determine room type for link
let roomTypePath = 'channel';
if (subscription.t === 'd') roomTypePath = 'direct';
else if (subscription.t === 'p') roomTypePath = 'group';
const notification: Notification = {
id: `rocketchat-${latestMessage._id}`,
source: 'rocketchat',
sourceId: latestMessage._id,
type: 'message',
title: subscription.t === 'd'
? `Message de ${messageUser.name || messageUser.username || 'Utilisateur'}`
: `${messageUser.name || messageUser.username || 'Utilisateur'} dans ${roomName}`,
message: latestMessage.msg || '',
link: `${this.baseUrl}/${roomTypePath}/${subscription.name}`,
isRead: false, // All messages here are unread
timestamp: new Date(latestMessage.ts),
priority: subscription.alert ? 'high' : 'normal',
user: {
id: messageUser._id || '',
name: messageUser.name || messageUser.username || 'Utilisateur',
},
metadata: {
roomId: subscription.rid,
roomName: roomName,
roomType: subscription.t,
unreadCount: subscription.unread,
}
};
notifications.push(notification);
}
}
} catch (error) {
logger.error('[ROCKETCHAT_ADAPTER] Error fetching messages for room', {
roomName: subscription.name,
error: error instanceof Error ? error.message : String(error),
});
continue;
}
}
// Sort by timestamp (newest first) and apply pagination
notifications.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
const startIndex = (page - 1) * limit;
const endIndex = startIndex + limit;
const paginatedNotifications = notifications.slice(startIndex, endIndex);
logger.debug('[ROCKETCHAT_ADAPTER] getNotifications result', {
total: notifications.length,
returned: paginatedNotifications.length,
page,
limit,
});
return paginatedNotifications;
} catch (error) {
logger.error('[ROCKETCHAT_ADAPTER] Error getting notifications', {
error: error instanceof Error ? error.message : String(error),
});
return [];
}
}
async markAsRead(userId: string, notificationId: string): Promise<boolean> {

View File

@ -1,8 +1,8 @@
export interface Notification {
id: string;
source: 'leantime' | 'nextcloud' | 'gitea' | 'dolibarr' | 'moodle';
source: 'leantime' | 'rocketchat' | 'email' | 'nextcloud' | 'gitea' | 'dolibarr' | 'moodle';
sourceId: string; // Original ID from the source system
type: string; // Type of notification (e.g., 'task', 'mention', 'comment')
type: string; // Type of notification (e.g., 'task', 'mention', 'comment', 'message')
title: string;
message: string;
link?: string; // Link to view the item in the source system