blob: 5d1c20e1db375ffb0484444e2e636e7fddf07fd8 [file] [log] [blame]
Marc Kupietz2f17a762025-12-06 11:45:49 +01001const crypto = require('crypto');
2
3const nc_talk_url = process.env.NC_TALK_URL || 'https://cloud.ids-mannheim.de';
4const nc_talk_conversation = process.env.NC_TALK_CONVERSATION;
5const nc_talk_secret = process.env.NC_TALK_SECRET;
6
7// Function to send message to Nextcloud Talk
8async function sendToNextcloudTalk(message, silent = false, screenshotPath = null) {
9 if (!nc_talk_conversation || !nc_talk_secret) {
10 return;
11 }
12
13 try {
14 const axios = require('axios');
15 const fs = require('fs');
16 const sharp = require('sharp');
17
18 let fullMessage = message;
19 const MAX_MESSAGE_LENGTH = 32000; // Nextcloud Talk message size limit
20
Marc Kupietz92e78fc2026-06-14 20:38:50 +020021 // If a screenshot path is provided, try to embed it. The data URI is
22 // base64 (~33% overhead), so a PNG quickly blows past the message size
23 // limit. Use JPEG (much smaller for UI screenshots) and step the width
24 // down until the embedded message fits, keeping the largest that does.
Marc Kupietz2f17a762025-12-06 11:45:49 +010025 if (screenshotPath && fs.existsSync(screenshotPath)) {
26 try {
Marc Kupietz92e78fc2026-06-14 20:38:50 +020027 const candidateWidths = [800, 640, 520, 420, 320, 240];
28 let embedded = null;
29 let lastSize = 0;
30
31 for (const width of candidateWidths) {
32 const resizedBuffer = await sharp(screenshotPath)
33 .resize(width, null, { // maintain aspect ratio
34 withoutEnlargement: true,
35 fit: 'inside'
36 })
37 .jpeg({ quality: 72, mozjpeg: true })
38 .toBuffer();
39
40 const dataUri = `data:image/jpeg;base64,${resizedBuffer.toString('base64')}`;
41 const messageWithImage = `${message}\n\n![Screenshot](${dataUri})`;
42 lastSize = messageWithImage.length;
43
44 if (messageWithImage.length <= MAX_MESSAGE_LENGTH) {
45 embedded = messageWithImage;
46 console.log(`Screenshot embedded at width ${width}px (message size: ${messageWithImage.length} chars)`);
47 break;
48 }
49 }
50
51 if (embedded) {
52 fullMessage = embedded;
Marc Kupietz2f17a762025-12-06 11:45:49 +010053 } else {
Marc Kupietz92e78fc2026-06-14 20:38:50 +020054 console.log(`Screenshot too large to embed even at smallest size (${lastSize} chars), sending text-only notification`);
55 fullMessage = `${message}\n\n_Screenshot available locally but too large to embed_`;
Marc Kupietz2f17a762025-12-06 11:45:49 +010056 }
57 } catch (imageError) {
58 console.error('Failed to process screenshot for Nextcloud Talk:', imageError.message);
59 fullMessage = `${message}\n\n_Screenshot available locally but could not be processed_`;
60 }
61 }
62
63 // Generate random header and signature
64 const randomHeader = crypto.randomBytes(32).toString('hex');
65 const messageToSign = randomHeader + fullMessage;
66 const signature = crypto.createHmac('sha256', nc_talk_secret)
67 .update(messageToSign)
68 .digest('hex');
69
70 // Send the message
71 await axios.post(
72 `${nc_talk_url}/ocs/v2.php/apps/spreed/api/v1/bot/${nc_talk_conversation}/message`,
73 {
74 message: fullMessage,
75 silent: silent
76 },
77 {
78 headers: {
79 'Content-Type': 'application/json',
80 'Accept': 'application/json',
81 'OCS-APIRequest': 'true',
82 'X-Nextcloud-Talk-Bot-Random': randomHeader,
83 'X-Nextcloud-Talk-Bot-Signature': signature
84 }
85 }
86 );
87 console.log('Message sent to Nextcloud Talk successfully');
88 } catch (error) {
89 console.error('Failed to send message to Nextcloud Talk:', error.message);
90 }
91}
92
93function ifConditionIt(title, condition, test) {
94 if (typeof it !== 'function') {
95 const { it } = require('mocha');
96 return condition ? it(title, test) : it.skip(title + " (skipped)", test)
97 }
98 return condition ? it(title, test) : it.skip(title + " (skipped)", test)
99}
100
101module.exports = {
102 sendToNextcloudTalk,
103 ifConditionIt,
104 nc_talk_conversation,
105 nc_talk_secret
106};