mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
1. Replaced all Date.now() instances with a humanized ISO8601 timestamp that is easily readable by users. This has a seamless effect on the embedded timestamping of chat mesages. Old chat files can still be edited (messages deleted), and continued. Character PNG files get updated with the new date format, and this will be reflected in github but there is no functional change to the user. 2. Added character name to the front of all newly saved/imported chat files. Old chats will retain their original filename unless re-imported. With these two changes, the new chat filenames are as such: "Aqua - 2023-3-3 @03h 08m 36s 948ms.jsonl" (I would like to make it smaller, perhaps by removing the milliseconds. Let me know if this is possible, or if it would introduce potential overwrites if users make/update files within the same second.) 3. Increased the size of the 'view past chats' popup box to max 800px so accommodate the new larger filenames.
1342 lines
49 KiB
JavaScript
1342 lines
49 KiB
JavaScript
var express = require('express');
|
||
var app = express();
|
||
var fs = require('fs');
|
||
const readline = require('readline');
|
||
const open = require('open');
|
||
|
||
var rimraf = require("rimraf");
|
||
const multer = require("multer");
|
||
const https = require('https');
|
||
//const PNG = require('pngjs').PNG;
|
||
const extract = require('png-chunks-extract');
|
||
const encode = require('png-chunks-encode');
|
||
const PNGtext = require('png-chunk-text');
|
||
|
||
const sharp = require('sharp');
|
||
sharp.cache(false);
|
||
const path = require('path');
|
||
|
||
const cookieParser = require('cookie-parser');
|
||
const crypto = require('crypto');
|
||
const ipaddr = require('ipaddr.js');
|
||
|
||
const config = require(path.join(process.cwd(), './config.conf'));
|
||
const server_port = config.port;
|
||
const whitelist = config.whitelist;
|
||
const whitelistMode = config.whitelistMode;
|
||
const autorun = config.autorun;
|
||
|
||
|
||
|
||
var Client = require('node-rest-client').Client;
|
||
var client = new Client();
|
||
|
||
var api_server = "";//"http://127.0.0.1:5000";
|
||
//var server_port = 8000;
|
||
|
||
var api_novelai = "https://api.novelai.net";
|
||
|
||
var response_get_story;
|
||
var response_generate;
|
||
var response_generate_novel;
|
||
var request_promt;
|
||
var response_promt;
|
||
var characters = {};
|
||
var character_i = 0;
|
||
var response_create;
|
||
var response_edit;
|
||
var response_dw_bg;
|
||
var response_getstatus;
|
||
var response_getstatus_novel;
|
||
var response_getlastversion;
|
||
var api_key_novel;
|
||
|
||
//RossAscends: Added function to format dates used in files and chat timestamps to a humanized format.
|
||
//Mostly I wanted this to be for file names, but couldn't figure out exactly where the filename save code was as everything seemed to be connected.
|
||
//During testing, this performs the same as previous date.now() structure.
|
||
//It also does not break old characters/chats, as the code just uses whatever timestamp exists in the chat.
|
||
//New chats made with characters will use this new formatting.
|
||
//Useable variable is (( humanizedISO8601Datetime ))
|
||
|
||
var baseDate = new Date(Date.now());
|
||
var humanYear = baseDate.getFullYear();
|
||
var humanMonth = (baseDate.getMonth()+1);
|
||
var humanDate = baseDate.getDate();
|
||
var humanHour = (baseDate.getHours() < 10? '0' : '') + baseDate.getHours();
|
||
var humanMinute = (baseDate.getMinutes() < 10? '0' : '') + baseDate.getMinutes();
|
||
var humanSecond = (baseDate.getSeconds() < 10? '0' : '') + baseDate.getSeconds();
|
||
var humanMillisecond = (baseDate.getMilliseconds() < 10? '0' : '') + baseDate.getMilliseconds();
|
||
|
||
var humanizedISO8601DateTime = (humanYear+"-"+humanMonth+"-"+humanDate+" @"+humanHour+"h "+humanMinute+"m "+humanSecond+"s "+humanMillisecond+"ms");
|
||
|
||
var is_colab = false;
|
||
var charactersPath = 'public/characters/';
|
||
var chatsPath = 'public/chats/';
|
||
if (is_colab && process.env.googledrive == 2){
|
||
charactersPath = '/content/drive/MyDrive/TavernAI/characters/';
|
||
chatsPath = '/content/drive/MyDrive/TavernAI/chats/';
|
||
}
|
||
const jsonParser = express.json({limit: '100mb'});
|
||
const urlencodedParser = express.urlencoded({extended: true, limit: '100mb'});
|
||
|
||
// CSRF Protection //
|
||
const doubleCsrf = require('csrf-csrf').doubleCsrf;
|
||
|
||
const CSRF_SECRET = crypto.randomBytes(8).toString('hex');
|
||
const COOKIES_SECRET = crypto.randomBytes(8).toString('hex');
|
||
|
||
const { invalidCsrfTokenError, generateToken, doubleCsrfProtection } = doubleCsrf({
|
||
getSecret: () => CSRF_SECRET,
|
||
cookieName: "X-CSRF-Token",
|
||
cookieOptions: {
|
||
httpOnly: true,
|
||
sameSite: "strict",
|
||
secure: false
|
||
},
|
||
size: 64,
|
||
getTokenFromRequest: (req) => req.headers["x-csrf-token"]
|
||
});
|
||
|
||
app.get("/csrf-token", (req, res) => {
|
||
res.json({
|
||
"token": generateToken(res)
|
||
});
|
||
});
|
||
|
||
app.use(cookieParser(COOKIES_SECRET));
|
||
app.use(doubleCsrfProtection);
|
||
|
||
// CORS Settings //
|
||
const cors = require('cors');
|
||
const CORS = cors({
|
||
origin: 'null',
|
||
methods: ['OPTIONS']
|
||
});
|
||
|
||
app.use(CORS);
|
||
|
||
app.use(function (req, res, next) { //Security
|
||
let clientIp = req.connection.remoteAddress;
|
||
let ip = ipaddr.parse(clientIp);
|
||
// Check if the IP address is IPv4-mapped IPv6 address
|
||
if (ip.kind() === 'ipv6' && ip.isIPv4MappedAddress()) {
|
||
const ipv4 = ip.toIPv4Address().toString();
|
||
clientIp = ipv4;
|
||
} else {
|
||
clientIp = ip;
|
||
clientIp = clientIp.toString();
|
||
}
|
||
|
||
//clientIp = req.connection.remoteAddress.split(':').pop();
|
||
if (whitelistMode === true && !whitelist.includes(clientIp)) {
|
||
console.log('Forbidden: Connection attempt from '+ clientIp+'. If you are attempting to connect, please add your IP address in whitelist or disable whitelist mode in config.conf in root of TavernAI folder.\n');
|
||
return res.status(403).send('<b>Forbidden</b>: Connection attempt from <b>'+ clientIp+'</b>. If you are attempting to connect, please add your IP address in whitelist or disable whitelist mode in config.conf in root of TavernAI folder.');
|
||
}
|
||
next();
|
||
});
|
||
|
||
app.use((req, res, next) => {
|
||
if (req.url.startsWith('/characters/') && is_colab && process.env.googledrive == 2) {
|
||
|
||
const filePath = path.join(charactersPath, decodeURIComponent(req.url.substr('/characters'.length)));
|
||
fs.access(filePath, fs.constants.R_OK, (err) => {
|
||
if (!err) {
|
||
res.sendFile(filePath);
|
||
} else {
|
||
res.send('Character not found: '+filePath);
|
||
//next();
|
||
}
|
||
});
|
||
} else {
|
||
next();
|
||
}
|
||
});
|
||
app.use(express.static(__dirname + "/public", { refresh: true }));
|
||
|
||
|
||
|
||
|
||
app.use('/backgrounds', (req, res) => {
|
||
const filePath = decodeURIComponent(path.join(process.cwd(), 'public/backgrounds', req.url.replace(/%20/g, ' ')));
|
||
fs.readFile(filePath, (err, data) => {
|
||
if (err) {
|
||
res.status(404).send('File not found');
|
||
return;
|
||
}
|
||
//res.contentType('image/jpeg');
|
||
res.send(data);
|
||
});
|
||
});
|
||
app.use('/characters', (req, res) => {
|
||
const filePath = decodeURIComponent(path.join(process.cwd(), charactersPath, req.url.replace(/%20/g, ' ')));
|
||
fs.readFile(filePath, (err, data) => {
|
||
if (err) {
|
||
res.status(404).send('File not found');
|
||
return;
|
||
}
|
||
//res.contentType('image/jpeg');
|
||
res.send(data);
|
||
});
|
||
});
|
||
app.use(multer({dest:"uploads"}).single("avatar"));
|
||
app.get("/", function(request, response){
|
||
response.sendFile(__dirname + "/public/index.html");
|
||
//response.send("<h1>Главная страница</h1>");
|
||
});
|
||
app.get("/notes/*", function(request, response){
|
||
response.sendFile(__dirname + "/public"+request.url+".html");
|
||
//response.send("<h1>Главная страница</h1>");
|
||
});
|
||
app.post("/getlastversion", jsonParser, function(request, response_getlastversion = response){
|
||
if(!request.body) return response_getlastversion.sendStatus(400);
|
||
|
||
const repo = 'TavernAI/TavernAI';
|
||
let req;
|
||
req = https.request({
|
||
hostname: 'github.com',
|
||
path: `/${repo}/releases/latest`,
|
||
method: 'HEAD'
|
||
}, (res) => {
|
||
if(res.statusCode === 302) {
|
||
const glocation = res.headers.location;
|
||
const versionStartIndex = glocation.lastIndexOf('@')+1;
|
||
const version = glocation.substring(versionStartIndex);
|
||
//console.log(version);
|
||
response_getlastversion.send({version: version});
|
||
}else{
|
||
response_getlastversion.send({version: 'error'});
|
||
}
|
||
});
|
||
|
||
req.on('error', (error) => {
|
||
console.error(error);
|
||
response_getlastversion.send({version: 'error'});
|
||
});
|
||
|
||
req.end();
|
||
|
||
});
|
||
//**************Kobold api
|
||
app.post("/generate", jsonParser, function(request, response_generate = response){
|
||
if(!request.body) return response_generate.sendStatus(400);
|
||
//console.log(request.body.prompt);
|
||
//const dataJson = JSON.parse(request.body);
|
||
request_promt = request.body.prompt;
|
||
|
||
//console.log(request.body);
|
||
var this_settings = { prompt: request_promt,
|
||
use_story:false,
|
||
use_memory:false,
|
||
use_authors_note:false,
|
||
use_world_info:false,
|
||
max_context_length: request.body.max_context_length
|
||
//temperature: request.body.temperature,
|
||
//max_length: request.body.max_length
|
||
};
|
||
|
||
if(request.body.gui_settings == false){
|
||
var sampler_order = [request.body.s1,request.body.s2,request.body.s3,request.body.s4,request.body.s5,request.body.s6,request.body.s7];
|
||
this_settings = { prompt: request_promt,
|
||
use_story:false,
|
||
use_memory:false,
|
||
use_authors_note:false,
|
||
use_world_info:false,
|
||
max_context_length: request.body.max_context_length,
|
||
max_length: request.body.max_length,
|
||
rep_pen: request.body.rep_pen,
|
||
rep_pen_range: request.body.rep_pen_range,
|
||
rep_pen_slope: request.body.rep_pen_slope,
|
||
temperature: request.body.temperature,
|
||
tfs: request.body.tfs,
|
||
top_a: request.body.top_a,
|
||
top_k: request.body.top_k,
|
||
top_p: request.body.top_p,
|
||
typical: request.body.typical,
|
||
sampler_order: sampler_order
|
||
};
|
||
}
|
||
|
||
console.log(this_settings);
|
||
var args = {
|
||
data: this_settings,
|
||
headers: { "Content-Type": "application/json" }
|
||
};
|
||
client.post(api_server+"/v1/generate",args, function (data, response) {
|
||
if(response.statusCode == 200){
|
||
console.log(data);
|
||
response_generate.send(data);
|
||
}
|
||
if(response.statusCode == 422){
|
||
console.log('Validation error');
|
||
response_generate.send({error: true});
|
||
}
|
||
if(response.statusCode == 501 || response.statusCode == 503 || response.statusCode == 507){
|
||
console.log(data);
|
||
response_generate.send({error: true});
|
||
}
|
||
}).on('error', function (err) {
|
||
console.log(err);
|
||
//console.log('something went wrong on the request', err.request.options);
|
||
response_generate.send({error: true});
|
||
});
|
||
});
|
||
app.post("/savechat", jsonParser, function(request, response){
|
||
//console.log('/savechat/ entered');
|
||
//console.log(request.data);
|
||
//console.log(request.body.bg);
|
||
//const data = request.body;
|
||
//console.log(request);
|
||
//console.log(request.body.chat);
|
||
//var bg = "body {background-image: linear-gradient(rgba(19,21,44,0.75), rgba(19,21,44,0.75)), url(../backgrounds/"+request.body.bg+");}";
|
||
var dir_name = String(request.body.avatar_url).replace('.png','');
|
||
let chat_data = request.body.chat;
|
||
let jsonlData = chat_data.map(JSON.stringify).join('\n');
|
||
fs.writeFile(chatsPath+dir_name+"/"+request.body.file_name+'.jsonl', jsonlData, 'utf8', function(err) {
|
||
if(err) {
|
||
response.send(err);
|
||
return console.log(err);
|
||
//response.send(err);
|
||
}else{
|
||
//response.redirect("/");
|
||
response.send({result: "ok"});
|
||
}
|
||
});
|
||
|
||
});
|
||
app.post("/getchat", jsonParser, function(request, response){
|
||
//console.log(request.data);
|
||
//console.log(request.body.bg);
|
||
//const data = request.body;
|
||
//console.log(request);
|
||
//console.log(request.body.chat);
|
||
//var bg = "body {background-image: linear-gradient(rgba(19,21,44,0.75), rgba(19,21,44,0.75)), url(../backgrounds/"+request.body.bg+");}";
|
||
//console.log('/getchat entered');
|
||
var dir_name = String(request.body.avatar_url).replace('.png','');
|
||
|
||
fs.stat(chatsPath+dir_name, function(err, stat) {
|
||
|
||
if(stat === undefined){ //if no chat dir for the character is found, make one with the character name
|
||
|
||
fs.mkdirSync(chatsPath+dir_name);
|
||
response.send({});
|
||
return;
|
||
}else{
|
||
|
||
if(err === null){ //if there is a dir, then read the requested file from the JSON call
|
||
|
||
fs.stat(chatsPath+dir_name+"/"+request.body.file_name+".jsonl", function(err, stat) {
|
||
|
||
if (err === null) { //if no error (the file exists), read the file
|
||
|
||
if(stat !== undefined){
|
||
fs.readFile(chatsPath+dir_name+"/"+request.body.file_name+".jsonl", 'utf8', (err, data) => {
|
||
if (err) {
|
||
console.error(err);
|
||
response.send(err);
|
||
return;
|
||
}
|
||
//console.log(data);
|
||
const lines = data.split('\n');
|
||
|
||
// Iterate through the array of strings and parse each line as JSON
|
||
const jsonData = lines.map(JSON.parse);
|
||
response.send(jsonData);
|
||
//console.log('read the requested file')
|
||
|
||
});
|
||
}
|
||
}else{
|
||
response.send({});
|
||
//return console.log(err);
|
||
return;
|
||
}
|
||
});
|
||
}else{
|
||
console.error(err);
|
||
response.send({});
|
||
return;
|
||
|
||
}
|
||
}
|
||
|
||
|
||
});
|
||
|
||
|
||
});
|
||
app.post("/getstatus", jsonParser, function(request, response_getstatus = response){
|
||
if(!request.body) return response_getstatus.sendStatus(400);
|
||
api_server = request.body.api_server;
|
||
if(api_server.indexOf('localhost') != -1){
|
||
api_server = api_server.replace('localhost','127.0.0.1');
|
||
}
|
||
var args = {
|
||
headers: { "Content-Type": "application/json" }
|
||
};
|
||
client.get(api_server+"/v1/model",args, function (data, response) {
|
||
if(response.statusCode == 200){
|
||
if(data.result != "ReadOnly"){
|
||
|
||
//response_getstatus.send(data.result);
|
||
}else{
|
||
data.result = "no_connection";
|
||
}
|
||
}else{
|
||
data.result = "no_connection";
|
||
}
|
||
response_getstatus.send(data);
|
||
//console.log(response.statusCode);
|
||
//console.log(data);
|
||
//response_getstatus.send(data);
|
||
//data.results[0].text
|
||
}).on('error', function (err) {
|
||
//console.log('');
|
||
//console.log('something went wrong on the request', err.request.options);
|
||
response_getstatus.send({result: "no_connection"});
|
||
});
|
||
});
|
||
function checkServer(){
|
||
//console.log('Check run###################################################');
|
||
api_server = 'http://127.0.0.1:5000';
|
||
var args = {
|
||
headers: { "Content-Type": "application/json" }
|
||
};
|
||
client.get(api_server+"/v1/model",args, function (data, response) {
|
||
console.log(data.result);
|
||
//console.log('###################################################');
|
||
console.log(data);
|
||
}).on('error', function (err) {
|
||
console.log(err);
|
||
//console.log('');
|
||
//console.log('something went wrong on the request', err.request.options);
|
||
//console.log('errorrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr');
|
||
});
|
||
}
|
||
|
||
//***************** Main functions
|
||
function charaFormatData(data){
|
||
var char = {"name": data.ch_name, "description": data.description, "personality": data.personality, "first_mes": data.first_mes, "avatar": 'none', "chat": humanizedISO8601DateTime, "mes_example": data.mes_example, "scenario": data.scenario, "create_date": humanizedISO8601DateTime};
|
||
return char;
|
||
}
|
||
app.post("/createcharacter", urlencodedParser, function(request, response){
|
||
|
||
if(!request.body) return response.sendStatus(400);
|
||
if (!fs.existsSync(charactersPath+request.body.ch_name+'.png')){
|
||
if(!fs.existsSync(chatsPath+request.body.ch_name) )fs.mkdirSync(chatsPath+request.body.ch_name);
|
||
|
||
let filedata = request.file;
|
||
//console.log(filedata.mimetype);
|
||
var fileType = ".png";
|
||
var img_file = "ai";
|
||
var img_path = "public/img/";
|
||
|
||
var char = charaFormatData(request.body);//{"name": request.body.ch_name, "description": request.body.description, "personality": request.body.personality, "first_mes": request.body.first_mes, "avatar": 'none', "chat": Date.now(), "last_mes": '', "mes_example": ''};
|
||
char = JSON.stringify(char);
|
||
if(!filedata){
|
||
|
||
charaWrite('./public/img/fluffy.png', char, request.body.ch_name, response);
|
||
|
||
}else{
|
||
|
||
img_path = "./uploads/";
|
||
img_file = filedata.filename
|
||
if (filedata.mimetype == "image/jpeg") fileType = ".jpeg";
|
||
if (filedata.mimetype == "image/png") fileType = ".png";
|
||
if (filedata.mimetype == "image/gif") fileType = ".gif";
|
||
if (filedata.mimetype == "image/bmp") fileType = ".bmp";
|
||
charaWrite(img_path+img_file, char, request.body.ch_name, response);
|
||
}
|
||
//console.log("The file was saved.");
|
||
|
||
}else{
|
||
response.send("Error: A character with that name already exists.");
|
||
}
|
||
//console.log(request.body);
|
||
//response.send(request.body.ch_name);
|
||
|
||
//response.redirect("https://metanit.com")
|
||
});
|
||
|
||
|
||
app.post("/editcharacter", urlencodedParser, function(request, response){
|
||
if(!request.body) return response.sendStatus(400);
|
||
let filedata = request.file;
|
||
//console.log(filedata.mimetype);
|
||
var fileType = ".png";
|
||
var img_file = "ai";
|
||
var img_path = charactersPath;
|
||
|
||
var char = charaFormatData(request.body);//{"name": request.body.ch_name, "description": request.body.description, "personality": request.body.personality, "first_mes": request.body.first_mes, "avatar": request.body.avatar_url, "chat": request.body.chat, "last_mes": request.body.last_mes, "mes_example": ''};
|
||
char.chat = request.body.chat;
|
||
char.create_date = request.body.create_date;
|
||
|
||
char = JSON.stringify(char);
|
||
let target_img = (request.body.avatar_url).replace('.png', '');
|
||
if(!filedata){
|
||
|
||
charaWrite(img_path+request.body.avatar_url, char, target_img, response, 'Character saved');
|
||
}else{
|
||
//console.log(filedata.filename);
|
||
img_path = "uploads/";
|
||
img_file = filedata.filename;
|
||
|
||
charaWrite(img_path+img_file, char, target_img, response, 'Character saved');
|
||
//response.send('Character saved');
|
||
}
|
||
});
|
||
app.post("/deletecharacter", urlencodedParser, function(request, response){
|
||
if(!request.body) return response.sendStatus(400);
|
||
rimraf(charactersPath+request.body.avatar_url, (err) => {
|
||
if(err) {
|
||
response.send(err);
|
||
return console.log(err);
|
||
}else{
|
||
//response.redirect("/");
|
||
let dir_name = request.body.avatar_url;
|
||
rimraf(chatsPath+dir_name.replace('.png',''), (err) => {
|
||
if(err) {
|
||
response.send(err);
|
||
return console.log(err);
|
||
}else{
|
||
//response.redirect("/");
|
||
|
||
response.send('ok');
|
||
}
|
||
});
|
||
}
|
||
});
|
||
});
|
||
|
||
async function charaWrite(img_url, data, target_img, response = undefined, mes = 'ok'){
|
||
try {
|
||
// Load the image in any format
|
||
sharp.cache(false);
|
||
var image = await sharp(img_url).resize(400, 600).toFormat('png').toBuffer();// old 170 234
|
||
// Convert the image to PNG format
|
||
//const pngImage = image.toFormat('png');
|
||
|
||
// Resize the image to 100x100
|
||
//const resizedImage = pngImage.resize(100, 100);
|
||
|
||
// Get the chunks
|
||
var chunks = extract(image);
|
||
var tEXtChunks = chunks.filter(chunk => chunk.create_date === 'tEXt');
|
||
|
||
// Remove all existing tEXt chunks
|
||
for (var tEXtChunk of tEXtChunks) {
|
||
chunks.splice(chunks.indexOf(tEXtChunk), 1);
|
||
}
|
||
// Add new chunks before the IEND chunk
|
||
var base64EncodedData = Buffer.from(data, 'utf8').toString('base64');
|
||
chunks.splice(-1, 0, PNGtext.encode('chara', base64EncodedData));
|
||
//chunks.splice(-1, 0, text.encode('lorem', 'ipsum'));
|
||
|
||
fs.writeFileSync(charactersPath+target_img+'.png', new Buffer.from(encode(chunks)));
|
||
if(response !== undefined) response.send(mes);
|
||
|
||
|
||
} catch (err) {
|
||
console.log(err);
|
||
if(response !== undefined) response.send(err);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
function charaRead(img_url){
|
||
sharp.cache(false);
|
||
const buffer = fs.readFileSync(img_url);
|
||
const chunks = extract(buffer);
|
||
|
||
const textChunks = chunks.filter(function (chunk) {
|
||
return chunk.name === 'tEXt';
|
||
}).map(function (chunk) {
|
||
//console.log(text.decode(chunk.data));
|
||
return PNGtext.decode(chunk.data);
|
||
});
|
||
var base64DecodedData = Buffer.from(textChunks[0].text, 'base64').toString('utf8');
|
||
return base64DecodedData;//textChunks[0].text;
|
||
//console.log(textChunks[0].keyword); // 'hello'
|
||
//console.log(textChunks[0].text); // 'world'
|
||
}
|
||
|
||
app.post("/getcharacters", jsonParser, function(request, response){
|
||
fs.readdir(charactersPath, (err, files) => {
|
||
if (err) {
|
||
console.error(err);
|
||
return;
|
||
}
|
||
|
||
const pngFiles = files.filter(file => file.endsWith('.png'));
|
||
|
||
//console.log(pngFiles);
|
||
characters = {};
|
||
var i = 0;
|
||
pngFiles.forEach(item => {
|
||
//console.log(item);
|
||
var img_data = charaRead(charactersPath+item);
|
||
try {
|
||
let jsonObject = JSON.parse(img_data);
|
||
jsonObject.avatar = item;
|
||
//console.log(jsonObject);
|
||
characters[i] = {};
|
||
characters[i] = jsonObject;
|
||
i++;
|
||
} catch (error) {
|
||
if (error instanceof SyntaxError) {
|
||
console.log("String [" + (i) + "] is not valid JSON!");
|
||
} else {
|
||
console.log("An unexpected error occurred: ", error);
|
||
}
|
||
}
|
||
});
|
||
//console.log(characters);
|
||
response.send(JSON.stringify(characters));
|
||
});
|
||
//var directories = getDirectories("public/characters");
|
||
//console.log(directories[0]);
|
||
//characters = {};
|
||
//character_i = 0;
|
||
//getCharaterFile(directories, response,0);
|
||
|
||
});
|
||
app.post("/getbackgrounds", jsonParser, function(request, response){
|
||
var images = getImages("public/backgrounds");
|
||
if(is_colab === true){
|
||
images = ['tavern.png'];
|
||
}
|
||
response.send(JSON.stringify(images));
|
||
|
||
});
|
||
app.post("/iscolab", jsonParser, function(request, response){
|
||
let send_data = false;
|
||
if(process.env.colaburl !== undefined){
|
||
send_data = String(process.env.colaburl).trim();
|
||
}
|
||
response.send({colaburl:send_data});
|
||
|
||
});
|
||
app.post("/getuseravatars", jsonParser, function(request, response){
|
||
var images = getImages("public/User Avatars");
|
||
response.send(JSON.stringify(images));
|
||
|
||
});
|
||
app.post("/setbackground", jsonParser, function(request, response){
|
||
//console.log(request.data);
|
||
//console.log(request.body.bg);
|
||
//const data = request.body;
|
||
//console.log(request);
|
||
//console.log(1);
|
||
var bg = "#bg1 {background-image: url(../backgrounds/"+request.body.bg+");}";
|
||
fs.writeFile('public/css/bg_load.css', bg, 'utf8', function(err) {
|
||
if(err) {
|
||
response.send(err);
|
||
return console.log(err);
|
||
}else{
|
||
//response.redirect("/");
|
||
response.send({result:'ok'});
|
||
}
|
||
});
|
||
|
||
});
|
||
app.post("/delbackground", jsonParser, function(request, response){
|
||
if(!request.body) return response.sendStatus(400);
|
||
rimraf('public/backgrounds/'+request.body.bg, (err) => {
|
||
if(err) {
|
||
response.send(err);
|
||
return console.log(err);
|
||
}else{
|
||
//response.redirect("/");
|
||
response.send('ok');
|
||
}
|
||
});
|
||
|
||
});
|
||
app.post("/downloadbackground", urlencodedParser, function(request, response){
|
||
response_dw_bg = response;
|
||
if(!request.body) return response.sendStatus(400);
|
||
|
||
let filedata = request.file;
|
||
//console.log(filedata.mimetype);
|
||
var fileType = ".png";
|
||
var img_file = "ai";
|
||
var img_path = "public/img/";
|
||
|
||
img_path = "uploads/";
|
||
img_file = filedata.filename;
|
||
if (filedata.mimetype == "image/jpeg") fileType = ".jpeg";
|
||
if (filedata.mimetype == "image/png") fileType = ".png";
|
||
if (filedata.mimetype == "image/gif") fileType = ".gif";
|
||
if (filedata.mimetype == "image/bmp") fileType = ".bmp";
|
||
fs.copyFile(img_path+img_file, 'public/backgrounds/'+img_file+fileType, (err) => {
|
||
if(err) {
|
||
|
||
return console.log(err);
|
||
}else{
|
||
//console.log(img_file+fileType);
|
||
response_dw_bg.send(img_file+fileType);
|
||
}
|
||
//console.log('The image was copied from temp directory.');
|
||
});
|
||
|
||
|
||
});
|
||
|
||
app.post("/savesettings", jsonParser, function(request, response){
|
||
|
||
|
||
fs.writeFile('public/settings.json', JSON.stringify(request.body), 'utf8', function(err) {
|
||
if(err) {
|
||
response.send(err);
|
||
return console.log(err);
|
||
//response.send(err);
|
||
}else{
|
||
//response.redirect("/");
|
||
response.send({result: "ok"});
|
||
}
|
||
});
|
||
|
||
});
|
||
app.post('/getsettings', jsonParser, (request, response) => { //Wintermute's code
|
||
const koboldai_settings = [];
|
||
const koboldai_setting_names = [];
|
||
const novelai_settings = [];
|
||
const novelai_setting_names = [];
|
||
const settings = fs.readFileSync('public/settings.json', 'utf8', (err, data) => {
|
||
if (err) return response.sendStatus(500);
|
||
|
||
return data;
|
||
});
|
||
//Kobold
|
||
const files = fs
|
||
.readdirSync('public/KoboldAI Settings')
|
||
.sort(
|
||
(a, b) =>
|
||
new Date(fs.statSync(`public/KoboldAI Settings/${b}`).mtime) -
|
||
new Date(fs.statSync(`public/KoboldAI Settings/${a}`).mtime)
|
||
);
|
||
|
||
files.forEach(item => {
|
||
const file = fs.readFileSync(
|
||
`public/KoboldAI Settings/${item}`,
|
||
'utf8',
|
||
(err, data) => {
|
||
if (err) return response.sendStatus(500)
|
||
|
||
return data;
|
||
}
|
||
);
|
||
koboldai_settings.push(file);
|
||
koboldai_setting_names.push(item.replace(/\.[^/.]+$/, ''));
|
||
});
|
||
|
||
//Novel
|
||
const files2 = fs
|
||
.readdirSync('public/NovelAI Settings')
|
||
.sort(
|
||
(a, b) =>
|
||
new Date(fs.statSync(`public/NovelAI Settings/${b}`).mtime) -
|
||
new Date(fs.statSync(`public/NovelAI Settings/${a}`).mtime)
|
||
);
|
||
|
||
files2.forEach(item => {
|
||
const file2 = fs.readFileSync(
|
||
`public/NovelAI Settings/${item}`,
|
||
'utf8',
|
||
(err, data) => {
|
||
if (err) return response.sendStatus(500);
|
||
|
||
return data;
|
||
}
|
||
);
|
||
|
||
novelai_settings.push(file2);
|
||
novelai_setting_names.push(item.replace(/\.[^/.]+$/, ''));
|
||
});
|
||
|
||
response.send({
|
||
settings,
|
||
koboldai_settings,
|
||
koboldai_setting_names,
|
||
novelai_settings,
|
||
novelai_setting_names
|
||
});
|
||
});
|
||
|
||
|
||
function getCharaterFile(directories,response,i){ //old need del
|
||
if(directories.length > i){
|
||
|
||
fs.stat(charactersPath+directories[i]+'/'+directories[i]+".json", function(err, stat) {
|
||
if (err == null) {
|
||
fs.readFile(charactersPath+directories[i]+'/'+directories[i]+".json", 'utf8', (err, data) => {
|
||
if (err) {
|
||
console.error(err);
|
||
return;
|
||
}
|
||
//console.log(data);
|
||
|
||
characters[character_i] = {};
|
||
characters[character_i] = data;
|
||
i++;
|
||
character_i++;
|
||
getCharaterFile(directories,response,i);
|
||
});
|
||
}else{
|
||
i++;
|
||
getCharaterFile(directories,response,i);
|
||
}
|
||
});
|
||
|
||
}else{
|
||
response.send(JSON.stringify(characters));
|
||
}
|
||
}
|
||
function getImages(path) {
|
||
return fs.readdirSync(path).sort(function (a, b) {
|
||
return new Date(fs.statSync(path + '/' + a).mtime) - new Date(fs.statSync(path + '/' + b).mtime);
|
||
}).reverse();
|
||
}
|
||
function getKoboldSettingFiles(path) {
|
||
return fs.readdirSync(path).sort(function (a, b) {
|
||
return new Date(fs.statSync(path + '/' + a).mtime) - new Date(fs.statSync(path + '/' + b).mtime);
|
||
}).reverse();
|
||
}
|
||
function getDirectories(path) {
|
||
return fs.readdirSync(path).sort(function (a, b) {
|
||
return new Date(fs.statSync(path + '/' + a).mtime) - new Date(fs.statSync(path + '/' + b).mtime);
|
||
}).reverse();
|
||
}
|
||
|
||
//***********Novel.ai API
|
||
|
||
app.post("/getstatus_novelai", jsonParser, function(request, response_getstatus_novel =response){
|
||
|
||
if(!request.body) return response_getstatus_novel.sendStatus(400);
|
||
api_key_novel = request.body.key;
|
||
var data = {};
|
||
var args = {
|
||
data: data,
|
||
|
||
headers: { "Content-Type": "application/json", "Authorization": "Bearer "+api_key_novel}
|
||
};
|
||
client.get(api_novelai+"/user/subscription",args, function (data, response) {
|
||
if(response.statusCode == 200){
|
||
//console.log(data);
|
||
response_getstatus_novel.send(data);//data);
|
||
}
|
||
if(response.statusCode == 401){
|
||
console.log('Access Token is incorrect.');
|
||
response_getstatus_novel.send({error: true});
|
||
}
|
||
if(response.statusCode == 500 || response.statusCode == 501 || response.statusCode == 501 || response.statusCode == 503 || response.statusCode == 507){
|
||
console.log(data);
|
||
response_getstatus_novel.send({error: true});
|
||
}
|
||
}).on('error', function (err) {
|
||
//console.log('');
|
||
//console.log('something went wrong on the request', err.request.options);
|
||
response_getstatus_novel.send({error: true});
|
||
});
|
||
});
|
||
|
||
|
||
|
||
app.post("/generate_novelai", jsonParser, function(request, response_generate_novel = response){
|
||
if(!request.body) return response_generate_novel.sendStatus(400);
|
||
|
||
console.log(request.body);
|
||
var data = {
|
||
"input": request.body.input,
|
||
"model": request.body.model,
|
||
"parameters": {
|
||
"use_string": request.body.use_string,
|
||
"temperature": request.body.temperature,
|
||
"max_length": request.body.max_length,
|
||
"min_length": request.body.min_length,
|
||
"tail_free_sampling": request.body.tail_free_sampling,
|
||
"repetition_penalty": request.body.repetition_penalty,
|
||
"repetition_penalty_range": request.body.repetition_penalty_range,
|
||
"repetition_penalty_frequency": request.body.repetition_penalty_frequency,
|
||
"repetition_penalty_presence": request.body.repetition_penalty_presence,
|
||
//"stop_sequences": {{187}},
|
||
//bad_words_ids = {{50256}, {0}, {1}};
|
||
//generate_until_sentence = true;
|
||
"use_cache": request.body.use_cache,
|
||
//use_string = true;
|
||
"return_full_text": request.body.return_full_text,
|
||
"prefix": request.body.prefix,
|
||
"order": request.body.order
|
||
}
|
||
};
|
||
|
||
var args = {
|
||
data: data,
|
||
|
||
headers: { "Content-Type": "application/json", "Authorization": "Bearer "+api_key_novel}
|
||
};
|
||
client.post(api_novelai+"/ai/generate",args, function (data, response) {
|
||
if(response.statusCode == 201){
|
||
console.log(data);
|
||
response_generate_novel.send(data);
|
||
}
|
||
if(response.statusCode == 400){
|
||
console.log('Validation error');
|
||
response_generate_novel.send({error: true});
|
||
}
|
||
if(response.statusCode == 401){
|
||
console.log('Access Token is incorrect');
|
||
response_generate_novel.send({error: true});
|
||
}
|
||
if(response.statusCode == 402){
|
||
console.log('An active subscription is required to access this endpoint');
|
||
response_generate_novel.send({error: true});
|
||
}
|
||
if(response.statusCode == 500 || response.statusCode == 409){
|
||
console.log(data);
|
||
response_generate_novel.send({error: true});
|
||
}
|
||
}).on('error', function (err) {
|
||
//console.log('');
|
||
//console.log('something went wrong on the request', err.request.options);
|
||
response_getstatus.send({error: true});
|
||
});
|
||
});
|
||
|
||
app.post("/getallchatsofcharacter", jsonParser, function(request, response){
|
||
if(!request.body) return response.sendStatus(400);
|
||
|
||
var char_dir = (request.body.avatar_url).replace('.png','')
|
||
fs.readdir(chatsPath+char_dir, (err, files) => {
|
||
if (err) {
|
||
console.error(err);
|
||
response.send({error: true});
|
||
return;
|
||
}
|
||
|
||
// filter for JSON files
|
||
const jsonFiles = files.filter(file => path.extname(file) === '.jsonl');
|
||
|
||
// sort the files by name
|
||
//jsonFiles.sort().reverse();
|
||
// print the sorted file names
|
||
var chatData = {};
|
||
let ii = jsonFiles.length; //this is the number of files belonging to the character
|
||
|
||
for(let i = jsonFiles.length-1; i >= 0; i--){
|
||
const file = jsonFiles[i];
|
||
|
||
const fileStream = fs.createReadStream(chatsPath+char_dir+'/'+file);
|
||
const rl = readline.createInterface({
|
||
input: fileStream,
|
||
crlfDelay: Infinity
|
||
});
|
||
|
||
let lastLine;
|
||
rl.on('line', (line) => {
|
||
lastLine = line;
|
||
});
|
||
rl.on('close', () => {
|
||
if(lastLine){
|
||
let jsonData = JSON.parse(lastLine);
|
||
if(jsonData.name !== undefined){
|
||
chatData[i] = {};
|
||
chatData[i]['file_name'] = file;
|
||
chatData[i]['mes'] = jsonData['mes'];
|
||
ii--;
|
||
if(ii === 0){
|
||
response.send(chatData);
|
||
}
|
||
}else{
|
||
return;
|
||
}
|
||
}
|
||
rl.close();
|
||
});
|
||
}
|
||
});
|
||
|
||
});
|
||
function getPngName(file){
|
||
let i = 1;
|
||
let base_name = file;
|
||
while (fs.existsSync(charactersPath+file+'.png')) {
|
||
file = base_name+i;
|
||
i++;
|
||
}
|
||
return file;
|
||
}
|
||
app.post("/importcharacter", urlencodedParser, function(request, response){
|
||
if(!request.body) return response.sendStatus(400);
|
||
|
||
let png_name = '';
|
||
let filedata = request.file;
|
||
//console.log(filedata.filename);
|
||
var format = request.body.file_type;
|
||
//console.log(format);
|
||
if(filedata){
|
||
if(format == 'json'){
|
||
fs.readFile('./uploads/'+filedata.filename, 'utf8', (err, data) => {
|
||
if (err){
|
||
console.log(err);
|
||
response.send({error:true});
|
||
}
|
||
const jsonData = JSON.parse(data);
|
||
|
||
if(jsonData.name !== undefined){
|
||
png_name = getPngName(jsonData.name);
|
||
let char = {"name": jsonData.name, "description": jsonData.description ?? '', "personality": jsonData.personality ?? '', "first_mes": jsonData.first_mes ?? '', "avatar": 'none', "chat": humanizedISO8601DateTime, "mes_example": jsonData.mes_example ?? '', "scenario": jsonData.scenario ?? '', "create_date": humanizedISO8601DateTime};
|
||
char = JSON.stringify(char);
|
||
charaWrite('./public/img/fluffy.png', char, png_name, response, {file_name: png_name});
|
||
}else if(jsonData.char_name !== undefined){//json Pygmalion notepad
|
||
png_name = getPngName(jsonData.char_name);
|
||
let char = {"name": jsonData.char_name, "description": jsonData.char_persona ?? '', "personality": '', "first_mes": jsonData.char_greeting ?? '', "avatar": 'none', "chat": humanizedISO8601DateTime, "mes_example": jsonData.example_dialogue ?? '', "scenario": jsonData.world_scenario ?? '', "create_date": humanizedISO8601DateTime};
|
||
char = JSON.stringify(char);
|
||
charaWrite('./public/img/fluffy.png', char, png_name, response, {file_name: png_name});
|
||
}else{
|
||
console.log('Incorrect character format .json');
|
||
response.send({error:true});
|
||
}
|
||
});
|
||
}else{
|
||
try{
|
||
|
||
var img_data = charaRead('./uploads/'+filedata.filename);
|
||
let jsonData = JSON.parse(img_data);
|
||
png_name = getPngName(jsonData.name);
|
||
|
||
if(jsonData.name !== undefined){
|
||
let char = {"name": jsonData.name, "description": jsonData.description ?? '', "personality": jsonData.personality ?? '', "first_mes": jsonData.first_mes ?? '', "avatar": 'none', "chat": humanizedISO8601DateTime, "mes_example": jsonData.mes_example ?? '', "scenario": jsonData.scenario ?? '', "create_date": humanizedISO8601DateTime};
|
||
char = JSON.stringify(char);
|
||
charaWrite('./uploads/'+filedata.filename, char, png_name, response, {file_name: png_name});
|
||
/*
|
||
fs.copyFile('./uploads/'+filedata.filename, charactersPath+png_name+'.png', (err) => {
|
||
if(err) {
|
||
response.send({error:true});
|
||
return console.log(err);
|
||
}else{
|
||
//console.log(img_file+fileType);
|
||
response.send({file_name: png_name});
|
||
}
|
||
//console.log('The image was copied from temp directory.');
|
||
});*/
|
||
}
|
||
}catch(err){
|
||
console.log(err);
|
||
response.send({error:true});
|
||
}
|
||
}
|
||
//charaWrite(img_path+img_file, char, request.body.ch_name, response);
|
||
}
|
||
//console.log("The file was saved.");
|
||
|
||
|
||
//console.log(request.body);
|
||
//response.send(request.body.ch_name);
|
||
|
||
//response.redirect("https://metanit.com")
|
||
});
|
||
|
||
app.post("/importchat", urlencodedParser, function(request, response){
|
||
if(!request.body) return response.sendStatus(400);
|
||
|
||
var format = request.body.file_type;
|
||
let filedata = request.file;
|
||
let avatar_url = (request.body.avatar_url).replace('.png', '');
|
||
let ch_name = request.body.character_name;
|
||
//console.log(filedata.filename);
|
||
//var format = request.body.file_type;
|
||
//console.log(format);
|
||
//console.log(1);
|
||
if(filedata){
|
||
|
||
if(format === 'json'){
|
||
fs.readFile('./uploads/'+filedata.filename, 'utf8', (err, data) => {
|
||
|
||
if (err){
|
||
console.log(err);
|
||
response.send({error:true});
|
||
}
|
||
|
||
const jsonData = JSON.parse(data);
|
||
var new_chat = [];
|
||
if(jsonData.histories !== undefined){
|
||
let i = 0;
|
||
new_chat[i] = {};
|
||
new_chat[0]['user_name'] = 'You';
|
||
new_chat[0]['character_name'] = ch_name;
|
||
new_chat[0]['create_date'] = humanizedISO8601DateTime //Date.now();
|
||
i++;
|
||
jsonData.histories.histories[0].msgs.forEach(function(item) {
|
||
new_chat[i] = {};
|
||
if(item.src.is_human == true){
|
||
new_chat[i]['name'] = 'You';
|
||
}else{
|
||
new_chat[i]['name'] = ch_name;
|
||
}
|
||
new_chat[i]['is_user'] = item.src.is_human;
|
||
new_chat[i]['is_name'] = true;
|
||
new_chat[i]['send_date'] = humanizedISO8601DateTime //Date.now();
|
||
new_chat[i]['mes'] = item.text;
|
||
i++;
|
||
});
|
||
const chatJsonlData = new_chat.map(JSON.stringify).join('\n');
|
||
fs.writeFile(chatsPath+avatar_url+'/'+ch_name+' - '+humanizedISO8601DateTime+' imported.jsonl', chatJsonlData, 'utf8', function(err) { //added ch_name and replaced Date.now() with humanizedISO8601DateTime
|
||
|
||
if(err) {
|
||
response.send(err);
|
||
return console.log(err);
|
||
//response.send(err);
|
||
}else{
|
||
//response.redirect("/");
|
||
response.send({res:true});
|
||
}
|
||
});
|
||
|
||
}else{
|
||
response.send({error:true});
|
||
return;
|
||
}
|
||
|
||
});
|
||
}
|
||
if(format === 'jsonl'){
|
||
const fileStream = fs.createReadStream('./uploads/'+filedata.filename);
|
||
const rl = readline.createInterface({
|
||
input: fileStream,
|
||
crlfDelay: Infinity
|
||
});
|
||
|
||
rl.once('line', (line) => {
|
||
let jsonData = JSON.parse(line);
|
||
|
||
if(jsonData.user_name !== undefined){
|
||
fs.copyFile('./uploads/'+filedata.filename, chatsPath+avatar_url+'/'+ch_name+' - '+humanizedISO8601DateTime+'.jsonl', (err) => { //Date.now() replaced for humanizedISO8601DateTime
|
||
if(err) {
|
||
response.send({error:true});
|
||
return console.log(err);
|
||
}else{
|
||
response.send({res:true});
|
||
return;
|
||
}
|
||
});
|
||
}else{
|
||
//response.send({error:true});
|
||
return;
|
||
}
|
||
rl.close();
|
||
});
|
||
}
|
||
|
||
}
|
||
|
||
});
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
app.listen(server_port, function() {
|
||
if(process.env.colab !== undefined){
|
||
if(process.env.colab == 2){
|
||
is_colab = true;
|
||
}
|
||
}
|
||
console.log('Launching...');
|
||
if(autorun) open('http:127.0.0.1:'+server_port);
|
||
console.log('TavernAI started: http://127.0.0.1:'+server_port);
|
||
if (fs.existsSync('public/characters/update.txt') && !is_colab) {
|
||
convertStage1();
|
||
}
|
||
|
||
});
|
||
|
||
//#####################CONVERTING IN NEW FORMAT########################
|
||
|
||
var charactersB = {};//B - Backup
|
||
var character_ib = 0;
|
||
|
||
var directoriesB = {};
|
||
|
||
|
||
function convertStage1(){
|
||
//if (!fs.existsSync('public/charactersBackup')) {
|
||
//fs.mkdirSync('public/charactersBackup');
|
||
//copyFolder('public/characters/', 'public/charactersBackup');
|
||
//}
|
||
|
||
var directories = getDirectories2("public/characters");
|
||
//console.log(directories[0]);
|
||
charactersB = {};
|
||
character_ib = 0;
|
||
var folderForDel = {};
|
||
getCharaterFile2(directories, 0);
|
||
}
|
||
function convertStage2(){
|
||
//directoriesB = JSON.parse(directoriesB);
|
||
//console.log(directoriesB);
|
||
var mes = true;
|
||
for (const key in directoriesB) {
|
||
if(mes){
|
||
console.log('***');
|
||
console.log('The update of the character format has begun...');
|
||
console.log('***');
|
||
mes = false;
|
||
}
|
||
//console.log(`${key}: ${directoriesB[key]}`);
|
||
//console.log(JSON.parse(charactersB[key]));
|
||
//console.log(directoriesB[key]);
|
||
|
||
var char = JSON.parse(charactersB[key]);
|
||
char.create_date = humanizedISO8601DateTime;
|
||
charactersB[key] = JSON.stringify(char);
|
||
var avatar = 'public/img/fluffy.png';
|
||
if(char.avatar !== 'none'){
|
||
avatar = 'public/characters/'+char.name+'/avatars/'+char.avatar;
|
||
}
|
||
|
||
charaWrite(avatar, charactersB[key], directoriesB[key]);
|
||
|
||
const files = fs.readdirSync('public/characters/'+directoriesB[key]+'/chats');
|
||
if (!fs.existsSync(chatsPath+char.name)) {
|
||
fs.mkdirSync(chatsPath+char.name);
|
||
}
|
||
files.forEach(function(file) {
|
||
// Read the contents of the file
|
||
|
||
const fileContents = fs.readFileSync('public/characters/'+directoriesB[key]+'/chats/' + file, 'utf8');
|
||
|
||
|
||
// Iterate through the array of strings and parse each line as JSON
|
||
let chat_data = JSON.parse(fileContents);
|
||
let new_chat_data = [];
|
||
let this_chat_user_name = 'You';
|
||
let is_pass_0 = false;
|
||
if(chat_data[0].indexOf('<username-holder>') !== -1){
|
||
this_chat_user_name = chat_data[0].substr('<username-holder>'.length, chat_data[0].length);
|
||
is_pass_0 = true;
|
||
}
|
||
let i = 0;
|
||
let ii = 0;
|
||
new_chat_data[i] = {user_name:'You', character_name:char.name, create_date: humanizedISO8601DateTime};
|
||
i++;
|
||
ii++;
|
||
chat_data.forEach(function(mes) {
|
||
if(!(i === 1 && is_pass_0)){
|
||
if(mes.indexOf('<username-holder>') === -1 && mes.indexOf('<username-idkey>') === -1){
|
||
new_chat_data[ii] = {};
|
||
let is_name = false;
|
||
if(mes.trim().indexOf(this_chat_user_name+':') !== 0){
|
||
if(mes.trim().indexOf(char.name+':') === 0){
|
||
mes = mes.replace(char.name+':','');
|
||
is_name = true;
|
||
}
|
||
new_chat_data[ii]['name'] = char.name;
|
||
new_chat_data[ii]['is_user'] = false;
|
||
new_chat_data[ii]['is_name'] = is_name;
|
||
new_chat_data[ii]['send_date'] = humanizedISO8601DateTime; //Date.now();
|
||
|
||
}else{
|
||
mes = mes.replace(this_chat_user_name+':','');
|
||
new_chat_data[ii]['name'] = 'You';
|
||
new_chat_data[ii]['is_user'] = true;
|
||
new_chat_data[ii]['is_name'] = true;
|
||
new_chat_data[ii]['send_date'] = humanizedISO8601DateTime //Date.now();
|
||
|
||
}
|
||
new_chat_data[ii]['mes'] = mes.trim();
|
||
ii++;
|
||
}
|
||
}
|
||
i++;
|
||
|
||
});
|
||
const jsonlData = new_chat_data.map(JSON.stringify).join('\n');
|
||
// Write the contents to the destination folder
|
||
fs.writeFileSync(chatsPath+char.name+'/' + file+'l', jsonlData);
|
||
});
|
||
//fs.rmSync('public/characters/'+directoriesB[key],{ recursive: true });
|
||
console.log(char.name+' update!');
|
||
}
|
||
//removeFolders('public/characters');
|
||
fs.unlinkSync('public/characters/update.txt');
|
||
if(mes == false){
|
||
console.log('***');
|
||
console.log('Сharacter format update completed successfully!');
|
||
console.log('***');
|
||
console.log('Now you can delete these folders, they are no longer used by TavernAI:');
|
||
}
|
||
for (const key in directoriesB) {
|
||
console.log('public/characters/'+directoriesB[key]);
|
||
}
|
||
}
|
||
function removeFolders(folder) {
|
||
const files = fs.readdirSync(folder);
|
||
files.forEach(function(file) {
|
||
const filePath = folder + '/' + file;
|
||
const stat = fs.statSync(filePath);
|
||
if (stat.isDirectory()) {
|
||
removeFolders(filePath);
|
||
fs.rmdirSync(filePath);
|
||
}
|
||
});
|
||
}
|
||
|
||
function copyFolder(src, dest) {
|
||
const files = fs.readdirSync(src);
|
||
files.forEach(function(file) {
|
||
const filePath = src + '/' + file;
|
||
const stat = fs.statSync(filePath);
|
||
if (stat.isFile()) {
|
||
fs.copyFileSync(filePath, dest + '/' + file);
|
||
} else if (stat.isDirectory()) {
|
||
fs.mkdirSync(dest + '/' + file);
|
||
copyFolder(filePath, dest + '/' + file);
|
||
}
|
||
});
|
||
}
|
||
|
||
|
||
function getDirectories2(path) {
|
||
return fs.readdirSync(path)
|
||
.filter(function (file) {
|
||
return fs.statSync(path + '/' + file).isDirectory();
|
||
})
|
||
.sort(function (a, b) {
|
||
return new Date(fs.statSync(path + '/' + a).mtime) - new Date(fs.statSync(path + '/' + b).mtime);
|
||
})
|
||
.reverse();
|
||
}
|
||
function getCharaterFile2(directories,i){
|
||
if(directories.length > i){
|
||
fs.stat('public/characters/'+directories[i]+'/'+directories[i]+".json", function(err, stat) {
|
||
if (err == null) {
|
||
fs.readFile('public/characters/'+directories[i]+'/'+directories[i]+".json", 'utf8', (err, data) => {
|
||
if (err) {
|
||
console.error(err);
|
||
return;
|
||
}
|
||
//console.log(data);
|
||
if (!fs.existsSync('public/characters/'+directories[i]+'.png')) {
|
||
charactersB[character_ib] = {};
|
||
charactersB[character_ib] = data;
|
||
directoriesB[character_ib] = directories[i];
|
||
character_ib++;
|
||
}
|
||
i++;
|
||
getCharaterFile2(directories,i);
|
||
});
|
||
}else{
|
||
i++;
|
||
getCharaterFile2(directories,i);
|
||
}
|
||
});
|
||
}else{
|
||
convertStage2();
|
||
}
|
||
}
|