send notification mails for chat messages

This commit is contained in:
Ulf Gebhardt 2025-04-02 18:15:38 +02:00
parent 6581c018c3
commit b884d05a88
Signed by: ulfgebhardt
GPG Key ID: DA6B843E748679C9
4 changed files with 150 additions and 26 deletions

View File

@ -71,6 +71,19 @@ export const resetPasswordTemplate = ({ email, variables: { nonce, name } }) =>
}
}
export const chatMessageTemplate = ({ email, variables: { name } }) => {
const subject = 'Neue Chatnachricht | New chat message'
const actionUrl = new URL('/chat', CONFIG.CLIENT_URI)
const renderParams = { ...defaultParams, englishHint, actionUrl, name, subject }
return {
from,
to: email,
subject,
html: mustache.render(templates.layout, renderParams, { content: templates.chatMessage }),
}
}
export const wrongAccountTemplate = ({ email, _variables = {} }) => {
const subject = 'Falsche Mailadresse? | Wrong E-mail?'
const actionUrl = new URL('/password-reset/request', CONFIG.CLIENT_URI)

View File

@ -0,0 +1,105 @@
<!-- Email Body German : BEGIN -->
<table class="email-german" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="{{{ welcomeImageUrl }}}"
width="300" height="" alt="Welcome image" border="0"
style="width: 100%; max-width: 300px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block; padding: 20px;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hallo {{ name }}!</h1>
<p style="margin: 0;">Du hast eine neue Chatnachricht erhalten. Klicke hier, um sie zu lesen:</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Chat anzeigen</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text + Button : END -->
</table>
<!-- Email Body German : END -->
<!-- Email Body English : BEGIN -->
<table class="email-english" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="{{{ welcomeImageUrl }}}"
width="300" height="" alt="Welcome image" border="0"
style="width: 100%; max-width: 300px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block; padding: 20px;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hello {{ name }}!</h1>
<p style="margin: 0;">You have received a new chat message. Click here to read:</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">show Chat</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text + Button : END -->
</table>
<!-- Email Body English : END -->

View File

@ -7,5 +7,6 @@ export const signup = readFile('./signup.html')
export const passwordReset = readFile('./resetPassword.html')
export const wrongAccount = readFile('./wrongAccount.html')
export const emailVerification = readFile('./emailVerification.html')
export const chatMessage = readFile('./chatMessage.html')
export const layout = readFile('./layout.html')

View File

@ -2,7 +2,7 @@ import { pubsub, NOTIFICATION_ADDED } from '../../server'
import extractMentionedUsers from './mentions/extractMentionedUsers'
import { validateNotifyUsers } from '../validation/validationMiddleware'
import { sendMail } from '../helpers/email/sendMail'
import { notificationTemplate } from '../helpers/email/templateBuilder'
import { chatMessageTemplate, notificationTemplate } from '../helpers/email/templateBuilder'
const queryNotificationEmails = async (context, notificationUserIds) => {
if (!(notificationUserIds && notificationUserIds.length)) return []
@ -311,44 +311,49 @@ const handleCreateMessage = async (resolve, root, args, context, resolveInfo) =>
// Find Recipient
const session = context.driver.session()
const messageRecipient = await session.readTransaction(async (transaction) => {
const messageRecipient = session.readTransaction(async (transaction) => {
const messageRecipientCypher = `
MATCH (currentUser:User { id: $currentUserId })-[:CHATS_IN]->(room:Room { id: $roomId })
MATCH (room)<-[:CHATS_IN]-(recipientUser:User)
MATCH (room)<-[:CHATS_IN]-(recipientUser:User)-[:PRIMARY_EMAIL]->(emailAddress:EmailAddress)
WHERE NOT recipientUser.id = $currentUserId
RETURN recipientUser
RETURN recipientUser, emailAddress {.email}
`
const messageRecipientTxResponse = await transaction.run(messageRecipientCypher, {
const txResponse = await transaction.run(messageRecipientCypher, {
currentUserId,
roomId,
})
const [recipientUser] = await messageRecipientTxResponse.records.map((record) =>
record.get('recipientUser'),
)
return recipientUser
return {
user: await txResponse.records.map((record) => record.get('recipientUser'))[0],
email: await txResponse.records.map((record) => record.get('emailAddress'))[0].email,
}
})
session.close()
try {
const { user, email } = await messageRecipient
// Is Recipient online
const lastActive = new Date(user.properties.lastActiveAt).getTime()
const awaySince = new Date(user.properties.awaySince).getTime()
const now = new Date().getTime()
const status = user.properties.lastOnlineStatus
// online & last active less than 1.5min -> no action
if (status === 'online' && now - lastActive < 90000) {
return resolve(root, args, context, resolveInfo)
}
// away for less then 3min -> no action
if (status === 'away' && now - awaySince < 180000) {
return resolve(root, args, context, resolveInfo)
}
sendMail(chatMessageTemplate({ email, variables: { name: user.properties.name } }))
// Is Recipient online
const lastActive = new Date(messageRecipient.properties.lastActiveAt).getTime()
const awaySince = new Date(messageRecipient.properties.awaySince).getTime()
const now = new Date().getTime()
const status = messageRecipient.properties.lastOnlineStatus
// online & last active less than 1.5min -> no action
if (status === 'online' && now - lastActive < 90000) {
return resolve(root, args, context, resolveInfo)
} catch (error) {
throw new Error(error)
} finally {
session.close()
}
// away for less then 3min -> no action
if (status === 'away' && now - awaySince < 180000) {
return resolve(root, args, context, resolveInfo)
}
// TODO
console.log('do notify via email', messageRecipient)
return resolve(root, args, context, resolveInfo)
}
export default {