diff --git a/.DS_Store b/.DS_Store index 5f26a37..8799b8d 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/src/server/router/aitools_router.js b/src/server/router/aitools_router.js index 0184020..e3f2a3b 100755 --- a/src/server/router/aitools_router.js +++ b/src/server/router/aitools_router.js @@ -21,14 +21,13 @@ const globalTables = require('../tools/globalTables'); const { ObjectId } = require('mongodb'); -const OpenAI = require("openai"); +const OpenAI = require('openai'); const { PassThrough } = require('stream'); +const { spawn } = require('child_process'); router.post('/getlist', authenticate_noerror, async function (req, res, next) { - - try { let idapp = req.body.idapp; @@ -36,26 +35,23 @@ router.post('/getlist', authenticate_noerror, async function (req, res, next) { return res.send({ code: server_constants.RIS_CODE_OK, - queryAIList + queryAIList, }); - } catch (e) { console.error('/getlist', e); } return null; - }); - async function getDeepSeekResponse(prompt, options) { try { const deepseek = new OpenAI({ baseURL: 'https://api.deepseek.com/v1', apiKey: process.env.DS_API_KEY, defaultHeaders: { - 'Content-Type': 'application/json' - } + 'Content-Type': 'application/json', + }, }); if (!options.withexplain) { @@ -66,10 +62,10 @@ async function getDeepSeekResponse(prompt, options) { } const completionStream = await deepseek.chat.completions.create({ - model: options.model || "deepseek-chat", + model: options.model || 'deepseek-chat', messages: [ - { role: "system", content: options?.contestsystem || "" }, - { role: "user", content: prompt } + { role: 'system', content: options?.contestsystem || '' }, + { role: 'user', content: prompt }, ], temperature: options?.temp || 0.3, max_tokens: options?.max_tokens || 1000, @@ -101,72 +97,147 @@ async function getDeepSeekResponse(prompt, options) { } router.post('/ds', authenticate, async (req, res) => { - const { prompt, options } = req.body; - const isstream = (options && options?.stream) + const isollama = options.model.startsWith('gemma3'); - if (isstream) { - // Se lo streaming è abilitato, restituiamo un flusso di dati - res.setHeader('Content-Type', 'text/event-stream'); - res.setHeader('Cache-Control', 'no-cache'); - res.setHeader('Connection', 'keep-alive'); + if (isollama) { + // Endpoint per generare una risposta dal modello Ollama + const { prompt } = req.body; + // const model = "phi:3.8b-mini-4k"; // Modello predefinito + const model = options.model; + + if (!prompt) { + return res.status(400).json({ error: 'Il prompt è richiesto.' }); + } try { - // Ottieni il flusso di dati da DeepSeek - const completionStream = await getDeepSeekResponse(prompt, options); - - for await (const chunk of completionStream) { - if (chunk.choices && chunk.choices[0]) { - const data = JSON.stringify({ choice: chunk.choices[0] }); - res.write(`data: ${data}\n\n`); - try { - const msg = chunk.choices[0].delta.content; - if (msg) { - console.log(msg); - // await tools.sleep(10000) - } - } catch (e) { - console.error('Error: ' + e.message); - } - } - } + // Chiamata all'API di Ollama + const ollama = spawn('curl', [ + '-X', + 'POST', + 'http://localhost:11434/api/generate', + '-H', + 'Content-Type: application/json', + '-d', + JSON.stringify({ + model: model, + prompt: prompt, + stream: false, // Imposta a true se vuoi una risposta in streaming + }), + ]); - res.write('data: [DONE]\n\n'); - res.end(); + let data = ''; + let error = ''; - - } catch (error) { - const errorstr = 'DeepSeek API Error:' + (error.response?.data || error.message) - console.error(errorstr); - - // In caso di errore durante lo streaming, invia un messaggio di errore - const errorData = JSON.stringify({ - code: server_constants.RIS_CODE_ERR, - error: errorstr, + ollama.stdout.on('data', (chunk) => { + data += chunk; }); - res.write(`data: ${errorData}\n\n`); - res.end(); + ollama.stderr.on('data', (chunk) => { + error += chunk; + }); + + ollama.on('close', (code) => { + if (code !== 0) { + console.error(`Errore Ollama: ${error}`); + return res.status(500).json({ error: 'Errore nella generazione del modello.' }); + } + + try { + // Ollama restituisce una risposta JSON + const response = JSON.parse(data); + const risp = response?.error ? response?.error : response?.response; + //res.json({ response: response.response }); // Invia solo la risposta al frontend + + if (response?.error) { + return res.send({ + code: server_constants.RIS_CODE_ERR, + error: risp, + }); + } else { + return res.send({ + code: server_constants.RIS_CODE_OK, + choice: risp, + }); + } + } catch (parseError) { + console.error('Errore nel parsing della risposta di Ollama:', parseError); + return res.send({ + code: server_constants.RIS_CODE_ERR, + error: 'Errore nella risposta del modello.', + }); + } + }); + } catch (err) { + console.error(err); + return res.send({ + code: server_constants.RIS_CODE_ERR, + error: 'Errore interno del server.', + }); } } else { - try { - // Se lo streaming non è abilitato, restituisci la risposta completa - const choice = await getDeepSeekResponse(prompt, options); + const isstream = options && options?.stream; - return res.send({ - code: server_constants.RIS_CODE_OK, - choice, - }); - } catch (error) { - const errorstr = 'DeepSeek API Error:' + (error.response?.data || error.message) - console.error(errorstr); + if (isstream) { + // Se lo streaming è abilitato, restituiamo un flusso di dati + res.setHeader('Content-Type', 'text/event-stream'); + res.setHeader('Cache-Control', 'no-cache'); + res.setHeader('Connection', 'keep-alive'); - // In caso di errore senza streaming, restituisci un errore standard - return res.send({ - code: server_constants.RIS_CODE_ERR, - error: errorstr, - }); + try { + // Ottieni il flusso di dati da DeepSeek + const completionStream = await getDeepSeekResponse(prompt, options); + + for await (const chunk of completionStream) { + if (chunk.choices && chunk.choices[0]) { + const data = JSON.stringify({ choice: chunk.choices[0] }); + res.write(`data: ${data}\n\n`); + try { + const msg = chunk.choices[0].delta.content; + if (msg) { + console.log(msg); + // await tools.sleep(10000) + } + } catch (e) { + console.error('Error: ' + e.message); + } + } + } + + res.write('data: [DONE]\n\n'); + res.end(); + } catch (error) { + const errorstr = 'API Error:' + (error.response?.data || error.message); + console.error(errorstr); + + // In caso di errore durante lo streaming, invia un messaggio di errore + const errorData = JSON.stringify({ + code: server_constants.RIS_CODE_ERR, + error: errorstr, + }); + res.write(`data: ${errorData}\n\n`); + res.end(); + } + } else { + try { + // Se lo streaming non è abilitato, restituisci la risposta completa + const choice = await getDeepSeekResponse(prompt, options); + + return res.send({ + code: server_constants.RIS_CODE_OK, + choice, + }); + } catch (error) { + const errorstr = 'API Error:' + (error.response?.data || error.message); + console.error(errorstr); + + // In caso di errore senza streaming, restituisci un errore standard + return res.send({ + code: server_constants.RIS_CODE_ERR, + error: errorstr, + }); + } } } }); diff --git a/src/server/router/api_router.js b/src/server/router/api_router.js index 292c23c..a7d0ea0 100644 --- a/src/server/router/api_router.js +++ b/src/server/router/api_router.js @@ -15,6 +15,8 @@ const Author = require('../models/author'); const tools = require('../tools/general'); +const axios = require('axios'); + router.post('/test-lungo', authenticate, (req, res) => { const timeout = req.body.timeout; @@ -453,4 +455,19 @@ router.post('/search-books', authenticate, async (req, res) => { } }); +router.post('/chatbot', authenticate, async (req, res) => { + try { + const response = await axios.post('http://localhost:5005/webhooks/rest/webhook', { + sender: 'user', + message: req.body.payload.message, + }); + + res.json(response.data); + } catch (error) { + console.error(error); + res.status(500).send('Errore nella comunicazione con Rasa'); + } +}); + + module.exports = router; diff --git a/src/server/telegram/telegrambot.js b/src/server/telegram/telegrambot.js index e47ab3e..5d76379 100755 --- a/src/server/telegram/telegrambot.js +++ b/src/server/telegram/telegrambot.js @@ -11,6 +11,9 @@ const appTelegramDest = [tools.FREEPLANET, tools.FREEPLANET]; const appTeleg_BotOnGroup = [tools.IDAPP_BOTONGROUP]; +const path = require('path'); +const fs = require('fs'); + //PIPPO const printf = require('util').format; @@ -31,8 +34,6 @@ const server_constants = require('../tools/server_constants'); // const {ListaIngresso} = require('../models/listaingresso'); const { MsgTemplate } = require('../models/msg_template'); -const globalTables = require('../tools/globalTables'); - const i18n = require('i18n'); let url = process.env.URL || 'https://'; @@ -1386,6 +1387,8 @@ const MyTelegramBot = { sendMsgFromSite: async function (idapp, user, params) { try { + const globalTables = require('../tools/globalTables'); + let ris = { numrec: 0, nummsgsent: 0, @@ -3810,34 +3813,69 @@ class Telegram { } } - // Soluzione 3: Implementazione completa con gestione errori e tentativi multipli async sendImageToTelegram(chatId, imageUrl, opt) { try { - // Prima prova: invia direttamente l'URL + // Primo tentativo: invia direttamente l'URL try { return await this.bot.sendPhoto(chatId, imageUrl, opt); } catch (error) { console.log('Primo tentativo fallito, provo a convertire...'); } - // Seconda prova: scarica e converti in JPG - const response = await axios.get(imageUrl, { responseType: 'arraybuffer' }); - const imageBuffer = Buffer.from(response.data); - - const jpgBuffer = await sharp(imageBuffer).jpeg({ quality: 90 }).toBuffer(); + console.log('imageUrl', imageUrl); + // Secondo tentativo: scarica e converte con sharp try { - return await this.bot.sendPhoto(chatId, jpgBuffer, opt); - } catch (error) { - console.log('Secondo tentativo fallito, provo come documento...'); + const response = await axios.get(imageUrl, { responseType: 'arraybuffer' }); + const contentType = response.headers['content-type']; + const imageBuffer = Buffer.from(response.data); + + if (!contentType.startsWith('image/')) { + console.warn('Non è un formato immagine:', contentType); + return await this.bot.sendDocument(chatId, imageBuffer, opt); + } + + try { + const jpgBuffer = await sharp(imageBuffer).jpeg({ quality: 90 }).toBuffer(); + return await this.bot.sendPhoto(chatId, jpgBuffer, opt); + } catch (err) { + console.warn('Sharp conversion failed:', err.message); + return await this.bot.sendDocument(chatId, imageBuffer, opt); + } + } catch (err) { + console.warn("Errore nel download dell'immagine, provo con file locale..."); } - // Ultima prova: invia come documento - return await this.bot.sendDocument(chatId, imageBuffer, opt); - } catch (error) { - console.error('Tutti i tentativi di invio sono falliti:', error); + // Terzo tentativo: accedi al file localmente + try { + if (!imageUrl.includes('/upload/')) { + throw new Error('URL non contiene "/upload/", impossibile determinare il path locale.'); + } + + let dirmain = ''; + + if (!tools.sulServer()) { + dirmain = server_constants.DIR_PUBLIC_LOCALE; + } + + const relativePath = imageUrl.split('/upload/')[1]; // es. "profile/surya1977/mygoods/1_module.jpg" + const mydir = tools.getdirByIdApp(opt.idapp) + dirmain + server_constants.DIR_UPLOAD; + const localPath = path.join(mydir, relativePath); + + console.log('Cerco il file localmente:', localPath); + + const localBuffer = fs.readFileSync(localPath); + return await this.bot.sendDocument(chatId, localBuffer, opt); + } catch (err) { + console.error('Lettura file locale fallita:', err.message); + } + + // Se tutto fallisce + console.error('Tutti i tentativi di invio sono falliti'); + return false; + } catch (error) { + console.error('Errore generale:', error); return false; - //throw error; } } @@ -3919,7 +3957,7 @@ class Telegram { } opt.img = tools.fixUrl(opt.img, opt.idapp); console.log('opt.img', opt.img); - risSendPhoto = await this.sendImageToTelegram(id, opt.img, { caption: text, ...form }).catch((e) => { + risSendPhoto = await this.sendImageToTelegram(id, opt.img, { caption: text, ...form, ...opt }).catch((e) => { let blocked = false; if (e.message.indexOf('Forbidden') > 0 || e.message.indexOf('chat not found') > 0) { blocked = true; @@ -4362,7 +4400,7 @@ if (true) { // RISPOSTE ALLE CALLBACK (Bottoni) - let testo_notifica_di_risposta = '' + let testo_notifica_di_risposta = ''; try { const myclTelegram = getclTelegBytoken(bot.token); @@ -4612,8 +4650,7 @@ if (true) { } // CALLBACK DI RISPOSTA AL BOT - bot.answerCallbackQuery(callbackQuery.id, { text: testo_notifica_di_risposta } ) ; - + bot.answerCallbackQuery(callbackQuery.id, { text: testo_notifica_di_risposta }); } catch (e) { console.error('Error BOT callback_query', e); } diff --git a/src/server/tools/general.js b/src/server/tools/general.js index f0f942a..ce36981 100755 --- a/src/server/tools/general.js +++ b/src/server/tools/general.js @@ -5777,8 +5777,7 @@ module.exports = { try { // let dir = this.getdirByIdApp(idapp) + dirmain + '/' + this.getDirUpload(); - let dir = this.getdirByIdApp(idapp); - '/' + this.getDirUpload() + shared_consts.getDirectoryImgByTable(table, username) + dirmain; + let dir = this.getdirByIdApp(idapp) + '/' + this.getDirUpload() + shared_consts.getDirectoryImgByTable(table, username) + dirmain; img = dir + img; @@ -5790,7 +5789,7 @@ module.exports = { if (!this.sulServer()) { // Finta Immagine - img = 'https://riso.app/upload/profile/SoniaVioletFlame/myskills/1000133092.jpg'; + // img = 'https://riso.app/upload/profile/SoniaVioletFlame/myskills/1000133092.jpg'; } return img; @@ -5835,15 +5834,15 @@ module.exports = { }, fixUrl(myurl, idapp) { - if (!myurl.startsWith('http')) { - return this.getHostByIdApp(idapp) + myurl; - } + //if (!myurl.startsWith('http')) { + // return this.getHostByIdApp(idapp) + myurl; + //} if (myurl.startsWith('http://127.0.0.1')) { //myurl = myurl.replace('http://127.0.0.1:8084/', 'https://riso.app/') // Se è in locale allora metti una foto finta... - myurl = - 'https://images.unsplash.com/photo-1464047736614-af63643285bf?q=80&w=2874&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'; + // myurl = + // 'https://images.unsplash.com/photo-1464047736614-af63643285bf?q=80&w=2874&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'; //myurl = myurl.replace('http://127.0.0.1', 'http://localhost') }