FreeDATA/gui_vue/src/js/chatHandler.js

677 lines
18 KiB
JavaScript
Raw Normal View History

2023-09-12 15:52:16 +00:00
const path = require("path");
const fs = require("fs");
2023-09-20 04:46:37 +00:00
const { v4: uuidv4 } = require("uuid");
2023-09-12 15:52:16 +00:00
// pinia store setup
2023-10-03 13:15:17 +00:00
import { setActivePinia } from "pinia";
import pinia from "../store/index";
2023-09-12 15:52:16 +00:00
setActivePinia(pinia);
2023-10-03 13:15:17 +00:00
import { useChatStore } from "../store/chatStore.js";
2023-09-12 15:52:16 +00:00
const chat = useChatStore(pinia);
2023-10-03 13:15:17 +00:00
import { sendMessage } from "./sock.js";
2023-09-20 04:46:37 +00:00
//const FD = require("./src/js/freedata.js");
import {btoa_FD} from "./freedata.js"
2023-09-20 04:46:37 +00:00
// split character
const split_char = "0;1;";
2023-09-12 15:52:16 +00:00
// ---- MessageDB
try {
var PouchDB = require("pouchdb");
} catch (err) {
console.log(err);
/*
This is a fix for raspberryPi where we get an error when loading pouchdb because of
leveldown package isnt running on ARM devices.
pouchdb-browser does not depend on leveldb and seems to be working.
*/
console.log("using pouchdb-browser fallback");
var PouchDB = require("pouchdb-browser");
}
PouchDB.plugin(require("pouchdb-find"));
//PouchDB.plugin(require('pouchdb-replication'));
PouchDB.plugin(require("pouchdb-upsert"));
// https://stackoverflow.com/a/26227660
if(typeof process.env["APPDATA"] !== "undefined"){
var appDataFolder = process.env["APPDATA"]
console.log(appDataFolder)
} else {
switch (process.platform) {
case "darwin":
var appDataFolder = process.env["HOME"] + "/Library/Application Support";
console.log(appDataFolder)
break;
case "linux":
var appDataFolder = process.env["HOME"] + "/.config";
console.log(appDataFolder)
break;
case "linux2":
var appDataFolder = "undefined";
break;
case "windows":
var appDataFolder = "undefined";
break;
default:
var appDataFolder = "undefined";
break;
}
}
2023-09-12 15:52:16 +00:00
var configFolder = path.join(appDataFolder, "FreeDATA");
var chatDB = path.join(configFolder, "chatDB");
var db = new PouchDB(chatDB);
/* -------- CREATE DATABASE INDEXES */
createChatIndex();
// create callsign set for storing unique callsigns
2023-10-03 13:15:17 +00:00
chat.callsign_list = new Set();
2023-09-12 15:52:16 +00:00
2023-09-27 14:55:57 +00:00
// function for creating a new broadcast
2023-10-03 13:15:17 +00:00
export function newBroadcast(broadcastChannel, chatmessage) {
var mode = "";
var frames = "";
var data = "";
if (typeof chatFile !== "undefined") {
var file = chatFile;
var filetype = chatFileType;
var filename = chatFileName;
} else {
var file = "";
var filetype = "text";
var filename = "";
}
var file_checksum = ""; //crc32(file).toString(16).toUpperCase();
var checksum = "";
var message_type = "broadcast_transmit";
var command = "";
var timestamp = Math.floor(Date.now() / 1000);
var uuid = uuidv4();
// TODO: Not sure what this uuid part is needed for ...
let uuidlast = uuid.lastIndexOf("-");
uuidlast += 1;
if (uuidlast > 0) {
uuid = uuid.substring(uuidlast);
}
// slice uuid for reducing overhead
uuid = uuid.slice(-4);
var data_with_attachment =
timestamp +
split_char +
chatmessage +
split_char +
filename +
split_char +
filetype +
split_char +
file;
var tnc_command = "broadcast";
sendMessage(dxcallsign, data_with_attachment, checksum, uuid, tnc_command);
let newChatObj = new Object();
newChatObj.command = "msg";
newChatObj.hmac_signed = false;
newChatObj.percent = 0;
newChatObj.bytesperminute;
newChatObj.is_new = false;
newChatObj._id = uuid;
newChatObj.timestamp = timestamp;
newChatObj.dxcallsign = dxcallsign;
newChatObj.dxgrid = "null";
newChatObj.msg = chatmessage;
newChatObj.checksum = file_checksum;
newChatObj.type = message_type;
newChatObj.status = "transmitting";
newChatObj.attempt = 1;
newChatObj.uuid = uuid;
newChatObj._attachments = {
[filename]: {
content_type: filetype,
data: btoa_FD(file),
2023-10-03 13:15:17 +00:00
},
};
2023-09-27 14:55:57 +00:00
2023-10-03 13:15:17 +00:00
addObjToDatabase(newChatObj);
2023-09-27 14:55:57 +00:00
}
// function for creating a new message
2023-10-03 13:15:17 +00:00
export function newMessage(
dxcallsign,
chatmessage,
chatFile,
chatFileName,
chatFileSize,
chatFileType,
) {
var mode = "";
var frames = "";
var data = "";
if (typeof chatFile !== "undefined") {
var file = chatFile;
var filetype = chatFileType;
var filename = chatFileName;
} else {
var file = "";
var filetype = "text";
var filename = "";
}
var file_checksum = ""; //crc32(file).toString(16).toUpperCase();
var checksum = "";
var message_type = "transmit";
var command = "";
var timestamp = Math.floor(Date.now() / 1000);
var uuid = uuidv4();
// TODO: Not sure what this uuid part is needed for ...
let uuidlast = uuid.lastIndexOf("-");
uuidlast += 1;
if (uuidlast > 0) {
uuid = uuid.substring(uuidlast);
}
// slice uuid for reducing overhead
uuid = uuid.slice(-8);
var data_with_attachment =
timestamp +
split_char +
chatmessage +
split_char +
filename +
split_char +
filetype +
split_char +
file;
var tnc_command = "msg";
sendMessage(dxcallsign, data_with_attachment, checksum, uuid, tnc_command);
let newChatObj = new Object();
newChatObj.command = "msg";
newChatObj.hmac_signed = false;
newChatObj.percent = 0;
newChatObj.bytesperminute;
newChatObj.is_new = false;
newChatObj._id = uuid;
newChatObj.timestamp = timestamp;
newChatObj.dxcallsign = dxcallsign;
newChatObj.dxgrid = "null";
newChatObj.msg = chatmessage;
newChatObj.checksum = file_checksum;
newChatObj.type = message_type;
newChatObj.status = "transmitting";
newChatObj.attempt = 1;
newChatObj.uuid = uuid;
newChatObj._attachments = {
[filename]: {
content_type: filetype,
data: btoa_FD(file),
2023-10-03 13:15:17 +00:00
},
};
2023-09-20 04:46:37 +00:00
2023-10-03 13:15:17 +00:00
addObjToDatabase(newChatObj);
2023-09-20 04:46:37 +00:00
}
2023-09-30 19:57:23 +00:00
// function for creating a list, accessible by callsign
2023-10-03 13:15:17 +00:00
function sortChatList() {
// Create an empty object to store the reordered data dynamically
var reorderedData = {};
var jsonObjects = chat.unsorted_chat_list;
// Iterate through the list of JSON objects and reorder them dynamically
jsonObjects.forEach((obj) => {
var dxcallsign = obj.dxcallsign;
if (dxcallsign) {
if (!reorderedData[dxcallsign]) {
reorderedData[dxcallsign] = [];
}
reorderedData[dxcallsign].push(obj);
}
});
//console.log(reorderedData["DJ2LS-0"])
return reorderedData;
}
2023-09-12 15:52:16 +00:00
2023-09-22 21:21:44 +00:00
//repeat a message
2023-10-03 13:15:17 +00:00
export function repeatMessageTransmission(id) {
console.log(id);
2023-09-22 21:21:44 +00:00
}
// delete a message from databse and gui
2023-10-03 13:15:17 +00:00
export function deleteMessageFromDB(id) {
console.log("deleting: " + id);
db.get(id).then(function (doc) {
db.remove(doc);
});
// overwrote unsorted chat list by filtering if not ID
chat.unsorted_chat_list = chat.unsorted_chat_list.filter(
(entry) => entry.uuid !== id,
);
// and finally generate our sorted chat list, which is the key store for chat gui rendering
// the removed entry should be removed now from gui
chat.sorted_chat_list = sortChatList();
2023-09-22 21:21:44 +00:00
}
2023-09-12 15:52:16 +00:00
// function to update transmission status
2023-10-03 13:15:17 +00:00
export function updateTransmissionStatus(obj) {
// update database entries
databaseUpsert(obj.uuid, "percent", obj.percent);
databaseUpsert(obj.uuid, "bytesperminute", obj.bytesperminute);
databaseUpsert(obj.uuid, "status", obj.status);
// update screen rendering / messages
updateUnsortedChatListEntry(obj.uuid, "percent", obj.percent);
updateUnsortedChatListEntry(obj.uuid, "bytesperminute", obj.bytesperminute);
updateUnsortedChatListEntry(obj.uuid, "status", obj.status);
}
2023-10-03 13:15:17 +00:00
export function updateUnsortedChatListEntry(uuid, object, value) {
for (const entry of chat.unsorted_chat_list) {
if (entry.uuid === uuid) {
entry[object] = value;
console.log("Entry updated:", entry[object]);
chat.sorted_chat_list = sortChatList();
return entry;
}
2023-10-03 13:15:17 +00:00
}
2023-10-03 13:15:17 +00:00
console.log("Entry not updated:", object);
return null; // Return null if not found
}
2023-10-03 13:15:17 +00:00
export function databaseUpsert(id, object, value) {
db.upsert(id, function (doc) {
if (!doc[object]) {
doc[object] = value;
2023-10-03 13:15:17 +00:00
}
doc[object] = value;
return doc;
})
.then(function (res) {
// success, res is {rev: '1-xxx', updated: true, id: 'myDocId'}
2023-10-03 13:15:17 +00:00
console.log(res);
})
.catch(function (err) {
// error
2023-10-03 13:15:17 +00:00
console.log(err);
});
}
// function for fetching all messages from chat / updating chat
2023-09-12 15:52:16 +00:00
export async function updateAllChat() {
//Ensure we create an index before running db.find
//We can't rely on the default index existing before we get here...... :'(
await db
.createIndex({
index: {
fields: [{ dxcallsign: "asc" }, { timestamp: "asc" }],
},
})
.then(async function (result) {
// handle result
await db
.find({
selector: {
$and: [
{ dxcallsign: { $exists: true } },
{ timestamp: { $exists: true } },
2023-10-10 20:21:12 +00:00
//{ $or: chat.chat_filter },
2023-09-12 15:52:16 +00:00
],
},
sort: [{ dxcallsign: "asc" }, { timestamp: "asc" }],
})
.then(async function (result) {
for (var item of result.docs) {
const dxcallsign = item.dxcallsign;
// Check if dxcallsign already exists as a property in the result object
if (!chat.sorted_beacon_list[dxcallsign]) {
// If not, initialize it with an empty array for snr values
chat.sorted_beacon_list[dxcallsign] = {
dxcallsign,
snr: [],
timestamp: [],
};
chat.callsign_list.add(dxcallsign);
}
2023-10-03 13:15:17 +00:00
if (item.type === "beacon") {
2023-10-10 20:28:35 +00:00
//console.log(item);
2023-10-01 11:24:54 +00:00
2023-10-03 13:15:17 +00:00
// TODO: sort beacon list .... maybe a part for a separate function
const jsonData = [item];
2023-10-03 13:15:17 +00:00
// Process each JSON item step by step
jsonData.forEach((jsonitem) => {
const { snr, timestamp } = item;
// Push the snr value to the corresponding dxcallsign's snr array
chat.sorted_beacon_list[dxcallsign].snr.push(snr);
chat.sorted_beacon_list[dxcallsign].timestamp.push(timestamp);
});
2023-09-30 19:57:23 +00:00
} else {
2023-10-03 13:15:17 +00:00
chat.unsorted_chat_list.push(item);
2023-09-30 19:57:23 +00:00
}
2023-09-12 15:52:16 +00:00
}
2023-10-03 13:15:17 +00:00
chat.sorted_chat_list = sortChatList();
2023-09-12 15:52:16 +00:00
/*
if (typeof result !== "undefined") {
for (const item of result.docs) {
//await otherwise history will not be in chronological order
await db
.get(item._id, {
attachments: true,
})
.then(function (item_with_attachments) {
update_chat(item_with_attachments);
});
}
}
*/
})
.catch(function (err) {
console.log(err);
});
})
.catch(function (err) {
console.log(err);
});
}
2023-10-03 13:15:17 +00:00
function addObjToDatabase(newobj) {
console.log(newobj);
/*
2023-09-20 04:46:37 +00:00
db.upsert(newobj._id, function (doc) {
if (!doc._id) {
console.log("upsert")
console.log(doc)
doc = newobj
} else {
console.log("new...")
*/
2023-10-03 13:15:17 +00:00
db.post(newobj)
.then(function (response) {
// handle response
console.log("new database entry");
console.log(response);
})
.catch(function (err) {
console.log(err);
});
2023-09-20 04:46:37 +00:00
2023-10-03 13:15:17 +00:00
console.log(newobj);
if (newobj.command === "msg") {
chat.unsorted_chat_list.push(newobj);
chat.sorted_chat_list = sortChatList();
}
2023-09-20 04:46:37 +00:00
2023-10-03 13:15:17 +00:00
/*
2023-09-20 04:46:37 +00:00
// upsert footer ...
}
return doc;
})
*/
}
2023-09-12 15:52:16 +00:00
function createChatIndex() {
db.createIndex({
index: {
fields: [
"timestamp",
"uuid",
"dxcallsign",
"dxgrid",
"msg",
"checksum",
"type",
"command",
"status",
"percent",
"attempt",
"hmac_signed",
"bytesperminute",
"_attachments",
2023-09-20 04:46:37 +00:00
"is_new",
2023-09-12 15:52:16 +00:00
],
},
})
.then(function (result) {
// handle result
console.log(result);
})
.catch(function (err) {
console.log(err);
});
}
2023-09-14 19:45:07 +00:00
2023-10-03 13:15:17 +00:00
export function deleteChatByCallsign(callsign) {
chat.callsign_list.delete(callsign);
delete chat.unsorted_chat_list.callsign;
delete chat.sorted_chat_list.callsign;
2023-09-14 19:45:07 +00:00
2023-10-03 13:15:17 +00:00
deleteFromDatabaseByCallsign(callsign);
2023-09-14 19:45:07 +00:00
}
2023-10-03 13:15:17 +00:00
function deleteFromDatabaseByCallsign(callsign) {
db.find({
selector: {
dxcallsign: callsign,
},
})
.then(function (result) {
// handle result
if (typeof result !== "undefined") {
result.docs.forEach(function (item) {
console.log(item);
db.get(item._id)
.then(function (doc) {
db.remove(doc)
2023-09-14 19:45:07 +00:00
.then(function (doc) {
2023-10-03 13:15:17 +00:00
updateAllChat(true);
return true;
2023-09-14 19:45:07 +00:00
})
.catch(function (err) {
console.log(err);
});
2023-10-03 13:15:17 +00:00
})
.catch(function (err) {
console.log(err);
2023-09-14 19:45:07 +00:00
});
});
2023-10-03 13:15:17 +00:00
}
})
.catch(function (err) {
console.log(err);
});
2023-09-20 04:46:37 +00:00
}
// function for handling a received beacon
2023-10-03 13:15:17 +00:00
export function newBeaconReceived(obj) {
/*
{
"freedata": "tnc-message",
"beacon": "received",
"uuid": "12741312-3dbb-4a53-b0cc-100f6c930ab8",
"timestamp": 1696076869,
"dxcallsign": "DJ2LS-0",
"dxgrid": "JN48CS",
"snr": "-2.8",
"mycallsign": "DJ2LS-0"
}
*/
2023-10-03 13:15:17 +00:00
let newChatObj = new Object();
newChatObj.command = "beacon";
newChatObj._id = obj["uuid"];
newChatObj.uuid = obj["uuid"];
newChatObj.timestamp = obj["timestamp"];
newChatObj.dxcallsign = obj["dxcallsign"];
newChatObj.dxgrid = obj["dxgrid"];
newChatObj.type = "beacon";
newChatObj.status = obj["beacon"];
newChatObj.snr = obj["snr"];
addObjToDatabase(newChatObj);
console.log(obj);
const jsonData = [obj];
const dxcallsign = obj.dxcallsign;
// Process each JSON item step by step
jsonData.forEach((item) => {
const { snr, timestamp } = obj;
// Check if dxcallsign already exists as a property in the result object
if (!chat.sorted_beacon_list[dxcallsign]) {
// If not, initialize it with an empty array for snr values
chat.sorted_beacon_list[dxcallsign] = {
dxcallsign,
snr: [],
timestamp: [],
};
}
2023-10-01 11:24:54 +00:00
2023-10-03 13:15:17 +00:00
// Push the snr value to the corresponding dxcallsign's snr array
chat.sorted_beacon_list[dxcallsign].snr.push(snr);
chat.sorted_beacon_list[dxcallsign].timestamp.push(timestamp);
});
}
// function for handling a received message
2023-10-03 13:15:17 +00:00
export function newMessageReceived(message, protocol) {
/*
2023-09-27 14:55:57 +00:00
PROTOCOL
{
"freedata": "tnc-message",
"arq": "transmission",
"status": "received",
"uuid": "58d64f7d-be8c-4578-879b-3b6cb3b60ddf",
"percent": 100,
"bytesperminute": 536,
"compression": 0.5714285714285714,
"timestamp": 1695203863,
"finished": 0,
"mycallsign": "DJ2LS-0",
"dxcallsign": "DJ2LS-0",
"dxgrid": "------",
"data": "bTA7MTttc2cwOzE7MDsxOzA3ZTIwOzE7MTY5NTIwMzgzMzA7MTt0ZXN0MDsxOzA7MTtwbGFpbi90ZXh0MDsxOw==",
"irs": "True",
"hmac_signed": "False"
}
2023-09-27 14:55:57 +00:00
MESSAGE; decoded from "data"
[
0 - protocol type message - "m",
1 - type - "msg",
2 - checksum "",
3 - uuid - "07e2",
4 - timestamp - "1695203833",
5 - message - "test",
6 - file name - "",
7 - mime - "plain/text",
8 - file - ""
]
2023-09-27 14:55:57 +00:00
*/
2023-10-03 13:15:17 +00:00
console.log(protocol);
let newChatObj = new Object();
newChatObj.command = "msg";
newChatObj.hmac_signed = protocol["hmac_signed"];
newChatObj.percent = 100;
newChatObj.bytesperminute = protocol["bytesperminute"];
newChatObj.is_new = true;
newChatObj._id = message[3];
newChatObj.timestamp = message[4];
newChatObj.dxcallsign = protocol["dxcallsign"];
newChatObj.dxgrid = protocol["dxgrid"];
newChatObj.msg = message[5];
newChatObj.checksum = message[2];
//newChatObj.type = message[1];
newChatObj.type = protocol["status"];
2023-10-03 13:15:17 +00:00
newChatObj.status = protocol["status"];
newChatObj.attempt = 1;
newChatObj.uuid = message[3];
newChatObj._attachments = {
[message[6]]: {
content_type: message[7],
data: btoa_FD(message[8]),
2023-10-03 13:15:17 +00:00
},
};
2023-10-03 13:15:17 +00:00
// some tweaks for broadcasts
if (protocol.fec == "broadcast") {
newChatObj.broadcast_sender = protocol["dxcallsign"];
newChatObj.type = "broadcast_received";
}
2023-10-03 13:15:17 +00:00
addObjToDatabase(newChatObj);
}
2023-10-03 13:15:17 +00:00
export function setStateFailed() {
state.arq_seconds_until_finish = 0;
state.arq_seconds_until_timeout = 180;
state.arq_seconds_until_timeout_percent = 100;
2023-10-02 18:50:24 +00:00
}
2023-10-03 13:15:17 +00:00
export function setStateSuccess() {
state.arq_seconds_until_finish = 0;
state.arq_seconds_until_timeout = 180;
state.arq_seconds_until_timeout_percent = 100;
2023-10-02 18:50:24 +00:00
}
2023-09-20 04:46:37 +00:00
// CRC CHECKSUMS
// https://stackoverflow.com/a/50579690
// crc32 calculation
//console.log(crc32('abc'));
//var crc32=function(r){for(var a,o=[],c=0;c<256;c++){a=c;for(var f=0;f<8;f++)a=1&a?3988292384^a>>>1:a>>>1;o[c]=a}for(var n=-1,t=0;t<r.length;t++)n=n>>>8^o[255&(n^r.charCodeAt(t))];return(-1^n)>>>0};
//console.log(crc32('abc').toString(16).toUpperCase()); // hex
var makeCRCTable = function () {
var c;
var crcTable = [];
for (var n = 0; n < 256; n++) {
c = n;
for (var k = 0; k < 8; k++) {
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
}
crcTable[n] = c;
}
return crcTable;
};
var crc32 = function (str) {
var crcTable = window.crcTable || (window.crcTable = makeCRCTable());
var crc = 0 ^ -1;
for (var i = 0; i < str.length; i++) {
crc = (crc >>> 8) ^ crcTable[(crc ^ str.charCodeAt(i)) & 0xff];
}
return (crc ^ -1) >>> 0;
2023-10-02 18:50:24 +00:00
};