node-red-contrib-knx-ultimate 6.3.25 → 6.3.26
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/CHANGELOG.md +9 -1
- package/examples/KNX AI - Conversational Control with Confirmation.json +4 -2
- package/examples/KNX AI - Summary Anomalies and Ask.json +4 -0
- package/examples/KNX AI - Telegrambot Direct Chat.json +7 -5
- package/nodes/knxUltimateAI.html +310 -124
- package/nodes/knxUltimateAI.js +1742 -264
- package/nodes/locales/de/knxUltimateAI.html +33 -7
- package/nodes/locales/de/knxUltimateAI.json +34 -15
- package/nodes/locales/en/knxUltimateAI.html +33 -7
- package/nodes/locales/en/knxUltimateAI.json +34 -15
- package/nodes/locales/es/knxUltimateAI.html +33 -7
- package/nodes/locales/es/knxUltimateAI.json +34 -15
- package/nodes/locales/fr/knxUltimateAI.html +33 -7
- package/nodes/locales/fr/knxUltimateAI.json +34 -15
- package/nodes/locales/it/knxUltimateAI.html +33 -7
- package/nodes/locales/it/knxUltimateAI.json +34 -15
- package/nodes/locales/zh-CN/knxUltimateAI.html +33 -7
- package/nodes/locales/zh-CN/knxUltimateAI.json +34 -15
- package/nodes/plugins/knxUltimateAI-vue/assets/app.css +1 -1
- package/nodes/plugins/knxUltimateAI-vue/assets/app.js +4 -4
- package/nodes/utils/knxAiTelegramVoice.js +475 -0
- package/nodes/utils/knxAiWebAccess.js +631 -0
- package/package.json +1 -1
- package/resources/KNXAIChatAdapterMappings.js +96 -2
|
@@ -10,8 +10,7 @@ const telegram = msg.payload;
|
|
|
10
10
|
if (!telegram || typeof telegram !== 'object') return;
|
|
11
11
|
|
|
12
12
|
const chatId = telegram.chatId;
|
|
13
|
-
|
|
14
|
-
if (chatId === undefined || chatId === null || content === '') return;
|
|
13
|
+
if (chatId === undefined || chatId === null) return;
|
|
15
14
|
|
|
16
15
|
msg.sessionId = String(chatId);
|
|
17
16
|
msg.language =
|
|
@@ -19,6 +18,45 @@ msg.language =
|
|
|
19
18
|
msg.language ||
|
|
20
19
|
'';
|
|
21
20
|
|
|
21
|
+
if (telegram.type === 'voice') {
|
|
22
|
+
const original = msg.originalMessage && typeof msg.originalMessage === 'object'
|
|
23
|
+
? msg.originalMessage
|
|
24
|
+
: {};
|
|
25
|
+
const voice = original.voice && typeof original.voice === 'object'
|
|
26
|
+
? original.voice
|
|
27
|
+
: original.message && original.message.voice && typeof original.message.voice === 'object'
|
|
28
|
+
? original.message.voice
|
|
29
|
+
: {};
|
|
30
|
+
const fileId = String(telegram.content || voice.file_id || '').trim();
|
|
31
|
+
const weblink = String(telegram.weblink || msg.weblink || '').trim();
|
|
32
|
+
const botDetails = msg.telegramBot && typeof msg.telegramBot === 'object' ? msg.telegramBot : {};
|
|
33
|
+
const botApiBase = String(botDetails.baseApiUrl || botDetails.baseapiurl || botDetails.baseApiURL || '').trim();
|
|
34
|
+
let allowedOrigin = 'https://api.telegram.org';
|
|
35
|
+
if (botApiBase) {
|
|
36
|
+
try { allowedOrigin = new URL(botApiBase).origin; } catch (error) { /* use Telegram default */ }
|
|
37
|
+
}
|
|
38
|
+
if (!fileId && !weblink) return;
|
|
39
|
+
msg.topic = 'ask';
|
|
40
|
+
msg.knxAi = Object.assign({}, msg.knxAi, {
|
|
41
|
+
sessionId: String(chatId),
|
|
42
|
+
voiceInput: {
|
|
43
|
+
source: 'telegram',
|
|
44
|
+
originalType: 'voice',
|
|
45
|
+
fileId: fileId,
|
|
46
|
+
weblink: weblink,
|
|
47
|
+
allowedOrigin: allowedOrigin,
|
|
48
|
+
mediaType: String(voice.mime_type || telegram.contentType || telegram.mimeType || 'audio/ogg'),
|
|
49
|
+
filename: String(voice.file_name || telegram.filename || 'telegram-voice.ogg'),
|
|
50
|
+
durationSeconds: Math.max(0, Number(voice.duration || telegram.duration) || 0),
|
|
51
|
+
fileSize: Math.max(0, Number(voice.file_size || telegram.fileSize) || 0)
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return msg;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const content = typeof telegram.content === 'string' ? telegram.content.trim() : '';
|
|
58
|
+
if (content === '') return;
|
|
59
|
+
|
|
22
60
|
if (telegram.type === 'callback_query' && (content === 'confirm' || content === 'cancel')) {
|
|
23
61
|
msg.topic = content;
|
|
24
62
|
msg.knxAi = Object.assign({}, msg.knxAi, {
|
|
@@ -70,6 +108,52 @@ if (image && Buffer.isBuffer(image.data)) {
|
|
|
70
108
|
return msg;
|
|
71
109
|
}
|
|
72
110
|
|
|
111
|
+
const audio = msg.knxAi && msg.knxAi.audio;
|
|
112
|
+
if (audio && Buffer.isBuffer(audio.data)) {
|
|
113
|
+
const voiceLanguage = String(msg.knxAi.language || (source && source.language) || '').trim().toLowerCase().split(/[-_]/)[0];
|
|
114
|
+
const disclosureLabels = {
|
|
115
|
+
de: 'KI-generierte Stimme',
|
|
116
|
+
en: 'AI-generated voice',
|
|
117
|
+
es: 'Voz generada por IA',
|
|
118
|
+
fr: 'Voix générée par l’IA',
|
|
119
|
+
it: 'Voce generata dall’IA',
|
|
120
|
+
zh: 'AI 生成的语音'
|
|
121
|
+
};
|
|
122
|
+
const disclosure = disclosureLabels[voiceLanguage] || disclosureLabels.en;
|
|
123
|
+
const voiceOptions = { caption: [disclosure, content].filter(Boolean).join('\\n').slice(0, 1024) };
|
|
124
|
+
const confirmation = msg.knxAi && msg.knxAi.confirmationRequest;
|
|
125
|
+
if (confirmation && confirmation.required === true && Array.isArray(confirmation.actions)) {
|
|
126
|
+
voiceOptions.reply_markup = JSON.stringify({
|
|
127
|
+
keyboard: [confirmation.actions.map(function (action) {
|
|
128
|
+
return { text: action.label };
|
|
129
|
+
})],
|
|
130
|
+
resize_keyboard: true,
|
|
131
|
+
one_time_keyboard: true
|
|
132
|
+
});
|
|
133
|
+
} else if (msg.knxAi && Array.isArray(msg.knxAi.suggestions) && msg.knxAi.suggestions.length) {
|
|
134
|
+
voiceOptions.reply_markup = JSON.stringify({
|
|
135
|
+
keyboard: msg.knxAi.suggestions.slice(0, 3).map(function (suggestion) {
|
|
136
|
+
return [{ text: Array.from(String(suggestion.text || suggestion.label || '').trim()).slice(0, 64).join('') }];
|
|
137
|
+
}).filter(function (row) { return row[0].text; }),
|
|
138
|
+
resize_keyboard: true,
|
|
139
|
+
one_time_keyboard: true
|
|
140
|
+
});
|
|
141
|
+
} else if (/^knx_confirmation_/.test(String(msg.knxAi.type || '')) || /^knx_routine_/.test(String(msg.knxAi.type || ''))) {
|
|
142
|
+
voiceOptions.reply_markup = JSON.stringify({ remove_keyboard: true });
|
|
143
|
+
}
|
|
144
|
+
msg.payload = {
|
|
145
|
+
chatId: chatId,
|
|
146
|
+
type: 'voice',
|
|
147
|
+
content: audio.data,
|
|
148
|
+
options: voiceOptions,
|
|
149
|
+
fileOptions: {
|
|
150
|
+
filename: audio.filename || 'knx-ai-reply.ogg',
|
|
151
|
+
contentType: audio.mediaType || 'audio/ogg'
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
return msg;
|
|
155
|
+
}
|
|
156
|
+
|
|
73
157
|
const telegramPayload = {
|
|
74
158
|
chatId: chatId,
|
|
75
159
|
type: 'message',
|
|
@@ -86,6 +170,16 @@ if (confirmation && confirmation.required === true && Array.isArray(confirmation
|
|
|
86
170
|
one_time_keyboard: true
|
|
87
171
|
})
|
|
88
172
|
};
|
|
173
|
+
} else if (msg.knxAi && Array.isArray(msg.knxAi.suggestions) && msg.knxAi.suggestions.length) {
|
|
174
|
+
telegramPayload.options = {
|
|
175
|
+
reply_markup: JSON.stringify({
|
|
176
|
+
keyboard: msg.knxAi.suggestions.slice(0, 3).map(function (suggestion) {
|
|
177
|
+
return [{ text: Array.from(String(suggestion.text || suggestion.label || '').trim()).slice(0, 64).join('') }];
|
|
178
|
+
}).filter(function (row) { return row[0].text; }),
|
|
179
|
+
resize_keyboard: true,
|
|
180
|
+
one_time_keyboard: true
|
|
181
|
+
})
|
|
182
|
+
};
|
|
89
183
|
} else if (msg.knxAi && (/^knx_confirmation_/.test(String(msg.knxAi.type || '')) || /^knx_routine_/.test(String(msg.knxAi.type || '')))) {
|
|
90
184
|
telegramPayload.options = {
|
|
91
185
|
reply_markup: JSON.stringify({ remove_keyboard: true })
|