KI-AGENT: Matrix Sync Push für eingehende Nachrichten ergänzen
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { createHash } from "node:crypto"
|
import { createHash } from "node:crypto"
|
||||||
import { FastifyInstance } from "fastify"
|
import { FastifyInstance } from "fastify"
|
||||||
import multipart from "@fastify/multipart"
|
import multipart from "@fastify/multipart"
|
||||||
import { and, eq, inArray, ne } from "drizzle-orm"
|
import { and, desc, eq, inArray, ne } from "drizzle-orm"
|
||||||
import { authProfiles, authTenantUsers, authUsers, notificationsItems, projects } from "../../db/schema"
|
import { authProfiles, authTenantUsers, authUsers, notificationsItems, projects } from "../../db/schema"
|
||||||
import { matrixService } from "../modules/matrix.service"
|
import { matrixService } from "../modules/matrix.service"
|
||||||
import { NotificationService, UserDirectory } from "../modules/notification.service"
|
import { NotificationService, UserDirectory } from "../modules/notification.service"
|
||||||
@@ -98,6 +98,30 @@ export default async function communicationRoutes(server: FastifyInstance) {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getTenantUser = async (tenantId: number, userId: string): Promise<ChatRecipient | null> => {
|
||||||
|
const [user] = await server.db
|
||||||
|
.select({
|
||||||
|
userId: authTenantUsers.user_id,
|
||||||
|
email: authUsers.email,
|
||||||
|
firstName: authProfiles.first_name,
|
||||||
|
lastName: authProfiles.last_name,
|
||||||
|
fullName: authProfiles.full_name,
|
||||||
|
})
|
||||||
|
.from(authTenantUsers)
|
||||||
|
.innerJoin(authUsers, eq(authUsers.id, authTenantUsers.user_id))
|
||||||
|
.leftJoin(authProfiles, and(
|
||||||
|
eq(authProfiles.user_id, authTenantUsers.user_id),
|
||||||
|
eq(authProfiles.tenant_id, tenantId)
|
||||||
|
))
|
||||||
|
.where(and(
|
||||||
|
eq(authTenantUsers.tenant_id, tenantId),
|
||||||
|
eq(authTenantUsers.user_id, userId)
|
||||||
|
))
|
||||||
|
.limit(1)
|
||||||
|
|
||||||
|
return user || null
|
||||||
|
}
|
||||||
|
|
||||||
const getSenderName = async (tenantId: number, senderUserId: string) => {
|
const getSenderName = async (tenantId: number, senderUserId: string) => {
|
||||||
const [sender] = await server.db
|
const [sender] = await server.db
|
||||||
.select({
|
.select({
|
||||||
@@ -195,6 +219,67 @@ export default async function communicationRoutes(server: FastifyInstance) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasChatNotificationForMessage = async (tenantId: number, userId: string, messageId: string) => {
|
||||||
|
const rows = await server.db
|
||||||
|
.select({
|
||||||
|
payload: notificationsItems.payload,
|
||||||
|
})
|
||||||
|
.from(notificationsItems)
|
||||||
|
.where(and(
|
||||||
|
eq(notificationsItems.tenantId, tenantId),
|
||||||
|
eq(notificationsItems.userId, userId),
|
||||||
|
eq(notificationsItems.eventType, "communication.message.new")
|
||||||
|
))
|
||||||
|
.orderBy(desc(notificationsItems.createdAt))
|
||||||
|
.limit(200)
|
||||||
|
|
||||||
|
return rows.some((row) => (row.payload as any)?.messageId === messageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const notifyCurrentUserAboutIncomingMatrixMessages = async (req: any, room: any, messages: any[]) => {
|
||||||
|
if (!req.user.tenant_id || !messages.length) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const currentUser = await getTenantUser(req.user.tenant_id, req.user.user_id)
|
||||||
|
if (!currentUser) return
|
||||||
|
|
||||||
|
for (const message of messages) {
|
||||||
|
if (message.own || !message.id) continue
|
||||||
|
|
||||||
|
const text = message.body || message.attachment?.fileName || "Neue Nachricht"
|
||||||
|
const mentioned = mentionedRecipientIds(text, [currentUser]).includes(currentUser.userId)
|
||||||
|
const direct = room?.type === "direct"
|
||||||
|
|
||||||
|
if (!direct && !mentioned) continue
|
||||||
|
if (await hasChatNotificationForMessage(req.user.tenant_id, req.user.user_id, message.id)) continue
|
||||||
|
|
||||||
|
const senderName = message.senderDisplayName || message.sender || "Matrix"
|
||||||
|
const preview = text.length > 160 ? `${text.slice(0, 157)}...` : text
|
||||||
|
|
||||||
|
await notifications.trigger({
|
||||||
|
tenantId: req.user.tenant_id,
|
||||||
|
userId: req.user.user_id,
|
||||||
|
eventType: "communication.message.new",
|
||||||
|
title: mentioned ? `${senderName} hat dich erwähnt` : `Neue Direktnachricht von ${senderName}`,
|
||||||
|
message: preview,
|
||||||
|
payload: {
|
||||||
|
link: `/communication/chat?room=${encodeURIComponent(room.key)}`,
|
||||||
|
roomKey: room.key,
|
||||||
|
roomName: room.name,
|
||||||
|
roomType: room.type,
|
||||||
|
messageId: message.id,
|
||||||
|
matrixSender: message.sender,
|
||||||
|
mentioned,
|
||||||
|
direct,
|
||||||
|
},
|
||||||
|
channels: ["inapp", "push"],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
req.log.error({ err }, "Eingehende Matrix-Benachrichtigung konnte nicht ausgelöst werden")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const unreadChatNotifications = async (tenantId: number, userId: string) => {
|
const unreadChatNotifications = async (tenantId: number, userId: string) => {
|
||||||
return await server.db
|
return await server.db
|
||||||
.select({
|
.select({
|
||||||
@@ -739,13 +824,19 @@ export default async function communicationRoutes(server: FastifyInstance) {
|
|||||||
server.get("/communication/matrix/rooms/:roomKey/sync", async (req, reply) => {
|
server.get("/communication/matrix/rooms/:roomKey/sync", async (req, reply) => {
|
||||||
try {
|
try {
|
||||||
const query = req.query as { since?: string; initial?: string }
|
const query = req.query as { since?: string; initial?: string }
|
||||||
return await matrix.syncTenantRoomEvents(
|
const result = await matrix.syncTenantRoomEvents(
|
||||||
req.user.user_id,
|
req.user.user_id,
|
||||||
req.user.tenant_id,
|
req.user.tenant_id,
|
||||||
roomOptionsFromRequest(req),
|
roomOptionsFromRequest(req),
|
||||||
query.since,
|
query.since,
|
||||||
query.initial === "1"
|
query.initial === "1"
|
||||||
)
|
)
|
||||||
|
if (query.since && query.initial !== "1" && result.messages?.length) {
|
||||||
|
const room = await matrix.getTenantRoomStatus(req.user.tenant_id, result.key, result.name)
|
||||||
|
await notifyCurrentUserAboutIncomingMatrixMessages(req, room, result.messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
return handleMatrixError(req, reply, err, "Matrix sync failed")
|
return handleMatrixError(req, reply, err, "Matrix sync failed")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user