- Iniziato a scrivere la CHATBOT...
This commit is contained in:
@@ -21,14 +21,13 @@ const globalTables = require('../tools/globalTables');
|
|||||||
|
|
||||||
const { ObjectId } = require('mongodb');
|
const { ObjectId } = require('mongodb');
|
||||||
|
|
||||||
const OpenAI = require("openai");
|
const OpenAI = require('openai');
|
||||||
|
|
||||||
const { PassThrough } = require('stream');
|
const { PassThrough } = require('stream');
|
||||||
|
|
||||||
|
const { spawn } = require('child_process');
|
||||||
|
|
||||||
router.post('/getlist', authenticate_noerror, async function (req, res, next) {
|
router.post('/getlist', authenticate_noerror, async function (req, res, next) {
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let idapp = req.body.idapp;
|
let idapp = req.body.idapp;
|
||||||
|
|
||||||
@@ -36,26 +35,23 @@ router.post('/getlist', authenticate_noerror, async function (req, res, next) {
|
|||||||
|
|
||||||
return res.send({
|
return res.send({
|
||||||
code: server_constants.RIS_CODE_OK,
|
code: server_constants.RIS_CODE_OK,
|
||||||
queryAIList
|
queryAIList,
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('/getlist', e);
|
console.error('/getlist', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
async function getDeepSeekResponse(prompt, options) {
|
async function getDeepSeekResponse(prompt, options) {
|
||||||
try {
|
try {
|
||||||
const deepseek = new OpenAI({
|
const deepseek = new OpenAI({
|
||||||
baseURL: 'https://api.deepseek.com/v1',
|
baseURL: 'https://api.deepseek.com/v1',
|
||||||
apiKey: process.env.DS_API_KEY,
|
apiKey: process.env.DS_API_KEY,
|
||||||
defaultHeaders: {
|
defaultHeaders: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!options.withexplain) {
|
if (!options.withexplain) {
|
||||||
@@ -66,10 +62,10 @@ async function getDeepSeekResponse(prompt, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const completionStream = await deepseek.chat.completions.create({
|
const completionStream = await deepseek.chat.completions.create({
|
||||||
model: options.model || "deepseek-chat",
|
model: options.model || 'deepseek-chat',
|
||||||
messages: [
|
messages: [
|
||||||
{ role: "system", content: options?.contestsystem || "" },
|
{ role: 'system', content: options?.contestsystem || '' },
|
||||||
{ role: "user", content: prompt }
|
{ role: 'user', content: prompt },
|
||||||
],
|
],
|
||||||
temperature: options?.temp || 0.3,
|
temperature: options?.temp || 0.3,
|
||||||
max_tokens: options?.max_tokens || 1000,
|
max_tokens: options?.max_tokens || 1000,
|
||||||
@@ -101,72 +97,147 @@ async function getDeepSeekResponse(prompt, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
router.post('/ds', authenticate, async (req, res) => {
|
router.post('/ds', authenticate, async (req, res) => {
|
||||||
|
|
||||||
const { prompt, options } = req.body;
|
const { prompt, options } = req.body;
|
||||||
|
|
||||||
const isstream = (options && options?.stream)
|
const isollama = options.model.startsWith('gemma3');
|
||||||
|
|
||||||
if (isstream) {
|
if (isollama) {
|
||||||
// Se lo streaming è abilitato, restituiamo un flusso di dati
|
// Endpoint per generare una risposta dal modello Ollama
|
||||||
res.setHeader('Content-Type', 'text/event-stream');
|
const { prompt } = req.body;
|
||||||
res.setHeader('Cache-Control', 'no-cache');
|
// const model = "phi:3.8b-mini-4k"; // Modello predefinito
|
||||||
res.setHeader('Connection', 'keep-alive');
|
const model = options.model;
|
||||||
|
|
||||||
|
if (!prompt) {
|
||||||
|
return res.status(400).json({ error: 'Il prompt è richiesto.' });
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Ottieni il flusso di dati da DeepSeek
|
// Chiamata all'API di Ollama
|
||||||
const completionStream = await getDeepSeekResponse(prompt, options);
|
const ollama = spawn('curl', [
|
||||||
|
'-X',
|
||||||
for await (const chunk of completionStream) {
|
'POST',
|
||||||
if (chunk.choices && chunk.choices[0]) {
|
'http://localhost:11434/api/generate',
|
||||||
const data = JSON.stringify({ choice: chunk.choices[0] });
|
'-H',
|
||||||
res.write(`data: ${data}\n\n`);
|
'Content-Type: application/json',
|
||||||
try {
|
'-d',
|
||||||
const msg = chunk.choices[0].delta.content;
|
JSON.stringify({
|
||||||
if (msg) {
|
model: model,
|
||||||
console.log(msg);
|
prompt: prompt,
|
||||||
// await tools.sleep(10000)
|
stream: false, // Imposta a true se vuoi una risposta in streaming
|
||||||
}
|
}),
|
||||||
} catch (e) {
|
]);
|
||||||
console.error('Error: ' + e.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.write('data: [DONE]\n\n');
|
let data = '';
|
||||||
res.end();
|
let error = '';
|
||||||
|
|
||||||
|
ollama.stdout.on('data', (chunk) => {
|
||||||
} catch (error) {
|
data += chunk;
|
||||||
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,
|
|
||||||
});
|
});
|
||||||
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 {
|
} else {
|
||||||
try {
|
const isstream = options && options?.stream;
|
||||||
// Se lo streaming non è abilitato, restituisci la risposta completa
|
|
||||||
const choice = await getDeepSeekResponse(prompt, options);
|
|
||||||
|
|
||||||
return res.send({
|
if (isstream) {
|
||||||
code: server_constants.RIS_CODE_OK,
|
// Se lo streaming è abilitato, restituiamo un flusso di dati
|
||||||
choice,
|
res.setHeader('Content-Type', 'text/event-stream');
|
||||||
});
|
res.setHeader('Cache-Control', 'no-cache');
|
||||||
} catch (error) {
|
res.setHeader('Connection', 'keep-alive');
|
||||||
const errorstr = 'DeepSeek API Error:' + (error.response?.data || error.message)
|
|
||||||
console.error(errorstr);
|
|
||||||
|
|
||||||
// In caso di errore senza streaming, restituisci un errore standard
|
try {
|
||||||
return res.send({
|
// Ottieni il flusso di dati da DeepSeek
|
||||||
code: server_constants.RIS_CODE_ERR,
|
const completionStream = await getDeepSeekResponse(prompt, options);
|
||||||
error: errorstr,
|
|
||||||
});
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ const Author = require('../models/author');
|
|||||||
|
|
||||||
const tools = require('../tools/general');
|
const tools = require('../tools/general');
|
||||||
|
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
router.post('/test-lungo', authenticate, (req, res) => {
|
router.post('/test-lungo', authenticate, (req, res) => {
|
||||||
const timeout = req.body.timeout;
|
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;
|
module.exports = router;
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ const appTelegramDest = [tools.FREEPLANET, tools.FREEPLANET];
|
|||||||
|
|
||||||
const appTeleg_BotOnGroup = [tools.IDAPP_BOTONGROUP];
|
const appTeleg_BotOnGroup = [tools.IDAPP_BOTONGROUP];
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
//PIPPO
|
//PIPPO
|
||||||
|
|
||||||
const printf = require('util').format;
|
const printf = require('util').format;
|
||||||
@@ -31,8 +34,6 @@ const server_constants = require('../tools/server_constants');
|
|||||||
// const {ListaIngresso} = require('../models/listaingresso');
|
// const {ListaIngresso} = require('../models/listaingresso');
|
||||||
const { MsgTemplate } = require('../models/msg_template');
|
const { MsgTemplate } = require('../models/msg_template');
|
||||||
|
|
||||||
const globalTables = require('../tools/globalTables');
|
|
||||||
|
|
||||||
const i18n = require('i18n');
|
const i18n = require('i18n');
|
||||||
|
|
||||||
let url = process.env.URL || 'https://<PUBLIC-URL>';
|
let url = process.env.URL || 'https://<PUBLIC-URL>';
|
||||||
@@ -1386,6 +1387,8 @@ const MyTelegramBot = {
|
|||||||
|
|
||||||
sendMsgFromSite: async function (idapp, user, params) {
|
sendMsgFromSite: async function (idapp, user, params) {
|
||||||
try {
|
try {
|
||||||
|
const globalTables = require('../tools/globalTables');
|
||||||
|
|
||||||
let ris = {
|
let ris = {
|
||||||
numrec: 0,
|
numrec: 0,
|
||||||
nummsgsent: 0,
|
nummsgsent: 0,
|
||||||
@@ -3810,34 +3813,69 @@ class Telegram {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Soluzione 3: Implementazione completa con gestione errori e tentativi multipli
|
|
||||||
async sendImageToTelegram(chatId, imageUrl, opt) {
|
async sendImageToTelegram(chatId, imageUrl, opt) {
|
||||||
try {
|
try {
|
||||||
// Prima prova: invia direttamente l'URL
|
// Primo tentativo: invia direttamente l'URL
|
||||||
try {
|
try {
|
||||||
return await this.bot.sendPhoto(chatId, imageUrl, opt);
|
return await this.bot.sendPhoto(chatId, imageUrl, opt);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('Primo tentativo fallito, provo a convertire...');
|
console.log('Primo tentativo fallito, provo a convertire...');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seconda prova: scarica e converti in JPG
|
console.log('imageUrl', imageUrl);
|
||||||
const response = await axios.get(imageUrl, { responseType: 'arraybuffer' });
|
|
||||||
const imageBuffer = Buffer.from(response.data);
|
|
||||||
|
|
||||||
const jpgBuffer = await sharp(imageBuffer).jpeg({ quality: 90 }).toBuffer();
|
|
||||||
|
|
||||||
|
// Secondo tentativo: scarica e converte con sharp
|
||||||
try {
|
try {
|
||||||
return await this.bot.sendPhoto(chatId, jpgBuffer, opt);
|
const response = await axios.get(imageUrl, { responseType: 'arraybuffer' });
|
||||||
} catch (error) {
|
const contentType = response.headers['content-type'];
|
||||||
console.log('Secondo tentativo fallito, provo come documento...');
|
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
|
// Terzo tentativo: accedi al file localmente
|
||||||
return await this.bot.sendDocument(chatId, imageBuffer, opt);
|
try {
|
||||||
} catch (error) {
|
if (!imageUrl.includes('/upload/')) {
|
||||||
console.error('Tutti i tentativi di invio sono falliti:', error);
|
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;
|
return false;
|
||||||
//throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3919,7 +3957,7 @@ class Telegram {
|
|||||||
}
|
}
|
||||||
opt.img = tools.fixUrl(opt.img, opt.idapp);
|
opt.img = tools.fixUrl(opt.img, opt.idapp);
|
||||||
console.log('opt.img', opt.img);
|
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;
|
let blocked = false;
|
||||||
if (e.message.indexOf('Forbidden') > 0 || e.message.indexOf('chat not found') > 0) {
|
if (e.message.indexOf('Forbidden') > 0 || e.message.indexOf('chat not found') > 0) {
|
||||||
blocked = true;
|
blocked = true;
|
||||||
@@ -4362,7 +4400,7 @@ if (true) {
|
|||||||
|
|
||||||
// RISPOSTE ALLE CALLBACK (Bottoni)
|
// RISPOSTE ALLE CALLBACK (Bottoni)
|
||||||
|
|
||||||
let testo_notifica_di_risposta = ''
|
let testo_notifica_di_risposta = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const myclTelegram = getclTelegBytoken(bot.token);
|
const myclTelegram = getclTelegBytoken(bot.token);
|
||||||
@@ -4612,8 +4650,7 @@ if (true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CALLBACK DI RISPOSTA AL BOT
|
// 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) {
|
} catch (e) {
|
||||||
console.error('Error BOT callback_query', e);
|
console.error('Error BOT callback_query', e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5777,8 +5777,7 @@ module.exports = {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// let dir = this.getdirByIdApp(idapp) + dirmain + '/' + this.getDirUpload();
|
// let dir = this.getdirByIdApp(idapp) + dirmain + '/' + this.getDirUpload();
|
||||||
let dir = this.getdirByIdApp(idapp);
|
let dir = this.getdirByIdApp(idapp) + '/' + this.getDirUpload() + shared_consts.getDirectoryImgByTable(table, username) + dirmain;
|
||||||
'/' + this.getDirUpload() + shared_consts.getDirectoryImgByTable(table, username) + dirmain;
|
|
||||||
|
|
||||||
img = dir + img;
|
img = dir + img;
|
||||||
|
|
||||||
@@ -5790,7 +5789,7 @@ module.exports = {
|
|||||||
|
|
||||||
if (!this.sulServer()) {
|
if (!this.sulServer()) {
|
||||||
// Finta Immagine
|
// Finta Immagine
|
||||||
img = 'https://riso.app/upload/profile/SoniaVioletFlame/myskills/1000133092.jpg';
|
// img = 'https://riso.app/upload/profile/SoniaVioletFlame/myskills/1000133092.jpg';
|
||||||
}
|
}
|
||||||
|
|
||||||
return img;
|
return img;
|
||||||
@@ -5835,15 +5834,15 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
fixUrl(myurl, idapp) {
|
fixUrl(myurl, idapp) {
|
||||||
if (!myurl.startsWith('http')) {
|
//if (!myurl.startsWith('http')) {
|
||||||
return this.getHostByIdApp(idapp) + myurl;
|
// return this.getHostByIdApp(idapp) + myurl;
|
||||||
}
|
//}
|
||||||
if (myurl.startsWith('http://127.0.0.1')) {
|
if (myurl.startsWith('http://127.0.0.1')) {
|
||||||
//myurl = myurl.replace('http://127.0.0.1:8084/', 'https://riso.app/')
|
//myurl = myurl.replace('http://127.0.0.1:8084/', 'https://riso.app/')
|
||||||
|
|
||||||
// Se è in locale allora metti una foto finta...
|
// Se è in locale allora metti una foto finta...
|
||||||
myurl =
|
// 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';
|
// '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')
|
//myurl = myurl.replace('http://127.0.0.1', 'http://localhost')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user