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.
@@ -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
- const content = typeof telegram.content === 'string' ? telegram.content.trim() : '';
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 })