// GridTracker Copyright © 2023 GridTracker.org // All rights reserved. // See LICENSE for more information. var g_gtEngineInterval = null; var g_chatRecvFunctions = { uuid: gtChatSetUUID, list: gtChatNewList, info: gtChatUpdateCall, drop: gtChatRemoveCall, mesg: gtChatMessage, o: gtSpotMessage, ba: bandActivityReply }; var ChatState = Object(); ChatState.none = -1; ChatState.idle = 0; ChatState.connect = 1; ChatState.connecting = 2; ChatState.connected = 3; ChatState.status = 4; ChatState.closed = 5; ChatState.error = 6; var g_gtStateToFunction = { "-1": gtSetIdle, 0: gtCanConnect, 1: gtConnectChat, 2: gtConnecting, 3: gtChatSendUUID, 4: gtStatusCheck, 5: gtInError, 6: gtClosedSocket }; var g_gtChatSocket = null; var g_gtFlagPins = Object(); var g_gtMessages = Object(); var g_gtUnread = Object(); var g_gtCallsigns = Object(); var g_gtSentAwayToCid = Object(); var g_gtState = ChatState.none; var g_gtStatusCount = 0; var g_gtStatusTime = 500; var g_gtMaxChatMessages = 100; var g_gtNeedUsersList = true; var g_gtUuidValid = false; var g_gtLiveStatusUpdate = false; var g_oamsBandActivityData = null; var myChatId = 0; var myRoom = 0; var g_gtCurrentMessageCount = 0; function gtConnectChat() { if (g_gtChatSocket != null) { // we should start over g_gtState = ChatState.error; return; } var rnd = parseInt(Math.random() * 10) + 18360; try { g_gtState = ChatState.connecting; g_gtChatSocket = new WebSocket("ws://oams.space:" + rnd); } catch (e) { g_gtState = ChatState.error; return; } g_gtChatSocket.onopen = function () { g_gtState = ChatState.connected; }; g_gtChatSocket.onmessage = function (evt) { if (g_appSettings.gtShareEnable == true) { let jsmesg = false; try { jsmesg = JSON.parse(evt.data); } catch (err) { // bad message, dumping client g_gtState = ChatState.error; return; } if (!("type" in jsmesg)) { g_gtState = ChatState.error; return; } if (jsmesg.type in g_chatRecvFunctions) { g_chatRecvFunctions[jsmesg.type](jsmesg); } else { // Not fatal! console.log("Unknown oams message '" + jsmesg.type + "' ignoring"); } } }; g_gtChatSocket.onerror = function () { g_gtState = ChatState.error; }; g_gtChatSocket.onclose = function () { g_gtState = ChatState.closed; }; } function gtConnecting() {} function gtInError() { closeGtSocket(); } function gtChatSendClose() { msg = Object(); msg.type = "close"; msg.uuid = g_appSettings.chatUUID; sendGtJson(JSON.stringify(msg)); } function closeGtSocket() { if (g_gtChatSocket != null) { gtChatSendClose(); if (g_gtChatSocket.readyState != WebSocket.CLOSED) g_gtChatSocket.close(); if (g_gtChatSocket.readyState === WebSocket.CLOSED) { g_gtChatSocket = null; g_gtState = ChatState.none; } } else g_gtState = ChatState.none; } function gtClosedSocket() { g_gtChatSocket = null; g_gtState = ChatState.none; } // Connect 15 seconds after startup var g_lastConnectAttempt = parseInt(Date.now() / 1000) - 15; function gtCanConnect() { g_lastConnectAttempt = timeNowSec(); g_gtState = ChatState.connect; } function gtSetIdle() { if (timeNowSec() - g_lastConnectAttempt >= 30) { g_gtStatusCount = 0; g_gtNeedUsersList = true; g_gtState = ChatState.idle; g_lastGtStatus = ""; } g_gtUuidValid = false; } function gtStatusCheck() { if (g_gtStatusCount > 0) { g_gtStatusCount--; } if (g_gtStatusCount == 0 || g_gtLiveStatusUpdate == true) { if (g_gtLiveStatusUpdate == true) { g_gtLiveStatusUpdate = false; } else { g_lastGtStatus = ""; g_gtStatusCount = g_gtStatusTime; } gtChatSendStatus(); } if (g_gtNeedUsersList == true) { g_gtNeedUsersList = false; gtChatGetList(); } } function sendGtJson(json, isUUIDrequest = false) { if (g_appSettings.gtShareEnable == true && g_gtChatSocket != null) { if (g_gtChatSocket.readyState === WebSocket.OPEN && (isUUIDrequest || g_gtUuidValid)) { g_gtChatSocket.send(json); } else { if (g_gtChatSocket.readyState === WebSocket.CLOSED) { g_gtState = ChatState.closed; } } } } var g_lastGtStatus = ""; function gtChatSendStatus() { var msg = Object(); msg.type = "status"; msg.uuid = g_appSettings.chatUUID; msg.call = myDEcall; msg.grid = myRawGrid; msg.freq = myRawFreq; msg.mode = myMode; msg.band = myBand; msg.src = "GT"; msg.canmsg = g_appSettings.gtMsgEnable; msg.o = g_appSettings.gtSpotEnable == true ? 1 : 0; msg = JSON.stringify(msg); if (msg != g_lastGtStatus) { sendGtJson(msg); g_lastGtStatus = msg; } } function gtChatSendSpots(spotsObject, detailsObject) { let msg = Object(); msg.type = "o"; msg.uuid = g_appSettings.chatUUID; msg.o = spotsObject; msg.d = detailsObject; sendGtJson(JSON.stringify(msg)); } function gtChatSendDecodes(instancesObject) { let msg = Object(); msg.type = "d"; msg.uuid = g_appSettings.chatUUID; msg.i = instancesObject; sendGtJson(JSON.stringify(msg)); } function oamsBandActivityCheck() { if (g_appSettings.oamsBandActivity == true && myDEGrid.length >= 4) { let grid = myDEGrid.substring(0, 4).toUpperCase(); if (g_appSettings.oamsBandActivityNeighbors == true) { gtChatSendBandActivityRequest(squareToNeighbors(grid)); } else { gtChatSendBandActivityRequest([grid]); } } } function gtChatSendBandActivityRequest(gridArray) { msg = Object(); msg.type = "ba"; msg.uuid = g_appSettings.chatUUID; msg.ga = gridArray; sendGtJson(JSON.stringify(msg)); } function bandActivityReply(jsmesg) { g_oamsBandActivityData = jsmesg.r; renderBandActivity(); } function gtChatRemoveCall(jsmesg) { var id = jsmesg.id; var cid = jsmesg.cid; if (cid in g_gtFlagPins) { if (id in g_gtFlagPins[cid].ids) { delete g_gtFlagPins[cid].ids[id]; } else { console.log("drop: No such id in g_gtFlagPins.ids:"); console.log(jsmesg); console.log(g_gtFlagPins[cid].ids); } if (Object.keys(g_gtFlagPins[cid].ids).length == 0) { delete g_gtCallsigns[g_gtFlagPins[cid].call][cid]; if (g_gtFlagPins[cid].pin != null) { // remove pin from map here if (g_layerSources.gtflags.hasFeature(g_gtFlagPins[cid].pin)) { g_layerSources.gtflags.removeFeature(g_gtFlagPins[cid].pin); } delete g_gtFlagPins[cid].pin; g_gtFlagPins[cid].pin = null; } g_gtFlagPins[cid].live = false; notifyNoChat(cid); if (!(cid in g_gtMessages)) { if (Object.keys(g_gtCallsigns[g_gtFlagPins[cid].call]).length == 0) { delete g_gtCallsigns[g_gtFlagPins[cid].call]; } delete g_gtFlagPins[cid]; } updateChatWindow(cid); } } } function gtChatUpdateCall(jsmesg) { var id = jsmesg.id; var cid = jsmesg.cid; if (cid in g_gtFlagPins) { g_gtFlagPins[cid].ids[id] = true; // Did they move grid location? if (jsmesg.grid != g_gtFlagPins[cid].grid && g_gtFlagPins[cid].pin != null) { // remove pin from map here if (g_layerSources.gtflags.hasFeature(g_gtFlagPins[cid].pin)) { g_layerSources.gtflags.removeFeature(g_gtFlagPins[cid].pin); } delete g_gtFlagPins[cid].pin; g_gtFlagPins[cid].pin = null; } // Changed callsign? if (g_gtFlagPins[cid].call != jsmesg.call) { delete g_gtCallsigns[g_gtFlagPins[cid].call][cid]; } } else { g_gtFlagPins[cid] = Object(); g_gtFlagPins[cid].pin = null; g_gtFlagPins[cid].ids = Object(); g_gtFlagPins[cid].ids[id] = true; } g_gtFlagPins[cid].cid = jsmesg.cid; g_gtFlagPins[cid].call = jsmesg.call; g_gtFlagPins[cid].fCall = jsmesg.call.formatCallsign(); g_gtFlagPins[cid].grid = jsmesg.grid; g_gtFlagPins[cid].freq = jsmesg.freq; g_gtFlagPins[cid].band = jsmesg.band; g_gtFlagPins[cid].mode = jsmesg.mode; g_gtFlagPins[cid].src = jsmesg.src; g_gtFlagPins[cid].canmsg = jsmesg.canmsg; g_gtFlagPins[cid].o = jsmesg.o; g_gtFlagPins[cid].dxcc = callsignToDxcc(jsmesg.call); g_gtFlagPins[cid].live = true; // Make a pin here if (g_gtFlagPins[cid].pin == null) { makeGtPin(g_gtFlagPins[cid]); if (g_gtFlagPins[cid].pin != null) { g_layerSources.gtflags.addFeature(g_gtFlagPins[cid].pin); } } if (!(g_gtFlagPins[cid].call in g_gtCallsigns)) { // Can happen when a user changes callsign g_gtCallsigns[g_gtFlagPins[cid].call] = {}; } g_gtCallsigns[g_gtFlagPins[cid].call][cid] = true; updateChatWindow(cid); } function gtChatGetList() { msg = Object(); msg.type = "list"; msg.uuid = g_appSettings.chatUUID; sendGtJson(JSON.stringify(msg)); } function redrawPins() { clearGtFlags(); for (cid in g_gtFlagPins) { if (g_gtFlagPins[cid].pin != null) { delete g_gtFlagPins[cid].pin; g_gtFlagPins[cid].pin = null; } makeGtPin(g_gtFlagPins[cid]); if (g_gtFlagPins[cid].pin != null) { g_layerSources.gtflags.addFeature(g_gtFlagPins[cid].pin); } } } function makeGtPin(obj) { try { if (obj.pin) { if (g_layerSources.gtflags.hasFeature(obj.pin)) { g_layerSources.gtflags.removeFeature(obj.pin); } delete obj.pin; obj.pin = null; } if (obj.src != "GT") return; if (typeof obj.grid == "undefined" || obj.grid == null) return; if (obj.grid.length != 4 && obj.grid.length != 6) return; if (validateGridFromString(obj.grid) == false) return; if (!validateMapBandAndMode(obj.band, obj.mode)) { return; } var LL = squareToCenter(obj.grid); obj.pin = iconFeature(ol.proj.fromLonLat([LL.o, LL.a]), g_gtFlagIcon, 100); obj.pin.key = obj.cid; obj.pin.isGtFlag = true; obj.pin.size = 1; } catch (e) {} } function gtChatNewList(jsmesg) { clearGtFlags(); // starting clean if we're getting a new chat list g_gtFlagPins = Object() g_gtMessages = Object(); g_gtUnread = Object(); g_gtCallsigns = Object(); g_gtSentAwayToCid = Object(); for (var key in jsmesg.data.calls) { var cid = jsmesg.data.cid[key]; var id = jsmesg.data.id[key]; if (id != myChatId) { if (cid in g_gtFlagPins) { g_gtFlagPins[cid].ids[id] = true; } else { g_gtFlagPins[cid] = Object(); g_gtFlagPins[cid].ids = Object(); g_gtFlagPins[cid].ids[id] = true; g_gtFlagPins[cid].pin = null; } g_gtFlagPins[cid].call = jsmesg.data.calls[key]; g_gtFlagPins[cid].fCall = g_gtFlagPins[cid].call.formatCallsign(); g_gtFlagPins[cid].grid = jsmesg.data.grid[key]; g_gtFlagPins[cid].freq = jsmesg.data.freq[key]; g_gtFlagPins[cid].band = jsmesg.data.band[key]; g_gtFlagPins[cid].mode = jsmesg.data.mode[key]; g_gtFlagPins[cid].src = jsmesg.data.src[key]; g_gtFlagPins[cid].cid = cid; g_gtFlagPins[cid].canmsg = jsmesg.data.canmsg[key]; g_gtFlagPins[cid].o = jsmesg.data.o[key]; g_gtFlagPins[cid].dxcc = callsignToDxcc(g_gtFlagPins[cid].call); g_gtFlagPins[cid].live = true; if (!(g_gtFlagPins[cid].call in g_gtCallsigns)) { g_gtCallsigns[g_gtFlagPins[cid].call] = Object(); } g_gtCallsigns[g_gtFlagPins[cid].call][cid] = true; makeGtPin(g_gtFlagPins[cid]); if (g_gtFlagPins[cid].pin != null) { g_layerSources.gtflags.addFeature(g_gtFlagPins[cid].pin); } } } updateChatWindow(); oamsBandActivityCheck(); } function appendToHistory(cid, jsmesg) { if (!(cid in g_gtMessages)) { g_gtMessages[cid] = Object(); g_gtMessages[cid].history = Array(); } g_gtMessages[cid].history.push(jsmesg); while (g_gtMessages[cid].history.length > g_gtMaxChatMessages) { g_gtMessages[cid].history.shift(); } } function htmlEntities(str) { return String(str) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function gtChatMessage(jsmesg) { if (g_appSettings.gtMsgEnable == true) { var cid = jsmesg.cid; jsmesg.when = Date.now(); try { jsmesg.msg = new Buffer.from(jsmesg.msg, "base64").toString("utf8"); // eslint-disable-line new-cap jsmesg.msg = htmlEntities(jsmesg.msg); } catch (e) { jsmesg.msg = "Corrupt message recieved"; } if (jsmesg.call != null && jsmesg.call != "" && jsmesg.call != "NOCALL") { appendToHistory(cid, jsmesg); g_gtUnread[cid] = true; g_gtCurrentMessageCount++; if (newChatMessage(cid, jsmesg) == false) alertChatMessage(); if (g_msgSettings.msgAwaySelect == 1 && !(cid in g_gtSentAwayToCid)) { g_gtSentAwayToCid[cid] = true; gtSendMessage( "Away message [ " + g_msgSettings.msgAwayText + " ]", cid ); } } } } function gtSendMessage(message, who) { msg = Object(); msg.type = "mesg"; msg.uuid = g_appSettings.chatUUID; msg.cid = who; msg.msg = new Buffer.from(message).toString("base64"); // eslint-disable-line new-cap sendGtJson(JSON.stringify(msg)); msg.msg = htmlEntities(message); msg.id = 0; msg.when = Date.now(); appendToHistory(who, msg); } function gtChatSendUUID() { var msg = Object(); msg.type = "uuid"; if (g_appSettings.chatUUID != "") msg.uuid = g_appSettings.chatUUID; msg.call = myDEcall; msg.ver = gtShortVersion; sendGtJson(JSON.stringify(msg), true); } function gtChatSetUUID(jsmesg) { g_appSettings.chatUUID = jsmesg.uuid; myChatId = jsmesg.id; g_gtUuidValid = true; gtChatSendStatus(); g_gtLiveStatusUpdate = false; g_gtStatusCount = g_gtStatusTime; g_gtState = ChatState.status; } var g_getEngineWasRunning = false; function gtChatStateMachine() { if (g_appSettings.gtShareEnable == true && g_mapSettings.offlineMode == false) { var now = timeNowSec(); g_gtStateToFunction[g_gtState](); if (Object.keys(g_gtUnread).length > 0 && now % 2 == 0) { msgImg.style.webkitFilter = "invert(1)"; } else msgImg.style.webkitFilter = ""; if (g_msgSettings.msgFrequencySelect > 0 && Object.keys(g_gtUnread).length > 0) { if (now - g_lastChatMsgAlert > g_msgSettings.msgFrequencySelect * 60) { alertChatMessage(); } } g_getEngineWasRunning = true; } else { if (g_getEngineWasRunning == true) { g_getEngineWasRunning = false; closeGtSocket(); g_lastGtStatus = ""; } } } function gtSpotMessage(jsmesg) { if (jsmesg.cid in g_gtFlagPins) { let frequency, band, mode; if (jsmesg.ex != null) { frequency = Number(jsmesg.ex[0]); band = Number(frequency / 1000000).formatBand(); mode = String(jsmesg.ex[1]); } else { frequency = g_gtFlagPins[jsmesg.cid].freq; band = g_gtFlagPins[jsmesg.cid].band; mode = g_gtFlagPins[jsmesg.cid].mode; } addNewOAMSSpot(jsmesg.cid, jsmesg.db, frequency, band, mode); } } function gtChatSystemInit() { g_gtEngineInterval = nodeTimers.setInterval(gtChatStateMachine, 1000); } function showGtFlags() { if (g_appSettings.gtFlagImgSrc > 0) { if (g_mapSettings.offlineMode == false) { redrawPins(); g_layerVectors.gtflags.setVisible(true); } else { g_layerVectors.gtflags.setVisible(false); } } else g_layerVectors.gtflags.setVisible(false); } function clearGtFlags() { g_layerSources.gtflags.clear(); } function toggleGtMap() { g_appSettings.gtFlagImgSrc += 1; g_appSettings.gtFlagImgSrc %= 2; gtFlagImg.src = g_gtFlagImageArray[g_appSettings.gtFlagImgSrc]; if (g_spotView > 0 && g_receptionSettings.mergeSpots == false) return; if (g_appSettings.gtFlagImgSrc > 0) { redrawPins(); g_layerVectors.gtflags.setVisible(true); } else { g_layerVectors.gtflags.setVisible(false); } } function notifyNoChat(id) { if (g_chatWindowHandle != null) { try { g_chatWindowHandle.window.notifyNoChat(id); } catch (e) {} } } function updateChatWindow(id = null) { if (g_chatWindowHandle != null) { try { if (id) { g_chatWindowHandle.window.updateCallsign(id); } else { g_chatWindowHandle.window.updateEverything(); } } catch (e) {} } } function newChatMessage(id, jsmesg) { var hasFocus = false; if (g_msgSettings.msgActionSelect == 1) showMessaging(); if (g_chatWindowHandle != null) { try { hasFocus = g_chatWindowHandle.window.newChatMessage(id, jsmesg); g_chatWindowHandle.window.messagesRedraw(); } catch (e) {} } return hasFocus; } var g_lastChatMsgAlert = 0; function alertChatMessage() { if (g_msgSettings.msgAlertSelect == 1) { // Text to speech speakAlertString(g_msgSettings.msgAlertWord); } if (g_msgSettings.msgAlertSelect == 2) { // Audible playAlertMediaFile(g_msgSettings.msgAlertMedia); } g_lastChatMsgAlert = timeNowSec(); }