2024-01-03 02:05:31 +00:00
// GridTracker Copyright © 2024 GridTracker.org
2020-11-03 18:02:55 +00:00
// All rights reserved.
// See LICENSE for more information.
2023-02-17 00:35:44 +00:00
GT . gtEngineInterval = null ;
GT . chatRecvFunctions = {
2020-10-31 14:01:03 +00:00
uuid : gtChatSetUUID ,
list : gtChatNewList ,
info : gtChatUpdateCall ,
drop : gtChatRemoveCall ,
mesg : gtChatMessage ,
2023-01-22 23:55:51 +00:00
o : gtSpotMessage ,
ba : bandActivityReply
2020-08-19 16:55:41 +00:00
} ;
var ChatState = Object ( ) ;
2020-10-31 14:01:03 +00:00
ChatState . none = - 1 ;
ChatState . idle = 0 ;
ChatState . connect = 1 ;
ChatState . connecting = 2 ;
ChatState . connected = 3 ;
2023-02-12 00:59:46 +00:00
ChatState . waitUUID = 7 ;
2020-10-31 14:01:03 +00:00
ChatState . status = 4 ;
ChatState . closed = 5 ;
ChatState . error = 6 ;
2020-08-19 16:55:41 +00:00
2023-02-17 00:35:44 +00:00
GT . gtStateToFunction = {
2020-10-31 14:01:03 +00:00
"-1" : gtSetIdle ,
0 : gtCanConnect ,
1 : gtConnectChat ,
2 : gtConnecting ,
3 : gtChatSendUUID ,
4 : gtStatusCheck ,
5 : gtInError ,
2023-02-12 00:59:46 +00:00
6 : gtClosedSocket ,
7 : gtWaitUUID
2020-08-19 16:55:41 +00:00
} ;
2023-02-17 00:35:44 +00:00
GT . gtChatSocket = null ;
GT . gtFlagPins = Object ( ) ;
GT . gtMessages = Object ( ) ;
GT . gtUnread = Object ( ) ;
GT . gtCallsigns = Object ( ) ;
GT . gtSentAwayToCid = Object ( ) ;
2020-08-19 16:55:41 +00:00
2023-02-17 00:35:44 +00:00
GT . gtState = ChatState . none ;
GT . gtStatusCount = 0 ;
GT . gtStatusTime = 500 ;
GT . gtMaxChatMessages = 100 ;
GT . gtNeedUsersList = true ;
GT . gtUuidValid = false ;
2020-08-19 16:55:41 +00:00
2023-02-17 00:35:44 +00:00
GT . gtLiveStatusUpdate = false ;
GT . oamsBandActivityData = null ;
2020-08-19 16:55:41 +00:00
var myChatId = 0 ;
var myRoom = 0 ;
2022-12-01 01:36:14 +00:00
2023-02-17 00:35:44 +00:00
GT . gtCurrentMessageCount = 0 ;
2020-08-19 16:55:41 +00:00
2020-12-14 02:10:27 +00:00
function gtConnectChat ( )
{
2023-02-17 00:35:44 +00:00
if ( GT . gtChatSocket != null )
2020-12-14 02:10:27 +00:00
{
2020-10-31 14:01:03 +00:00
// we should start over
2023-02-17 00:35:44 +00:00
GT . gtState = ChatState . error ;
2020-10-31 14:01:03 +00:00
return ;
}
2021-01-02 21:24:12 +00:00
var rnd = parseInt ( Math . random ( ) * 10 ) + 18360 ;
2020-12-14 02:10:27 +00:00
try
{
2023-02-17 00:35:44 +00:00
GT . gtState = ChatState . connecting ;
GT . gtChatSocket = new WebSocket ( "ws://oams.space:" + rnd ) ;
2020-12-14 02:10:27 +00:00
}
catch ( e )
{
2023-02-17 00:35:44 +00:00
GT . gtState = ChatState . error ;
2020-10-31 14:01:03 +00:00
return ;
}
2023-02-17 00:35:44 +00:00
GT . gtChatSocket . onopen = function ( )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtState = ChatState . connected ;
2020-10-31 14:01:03 +00:00
} ;
2023-02-17 00:35:44 +00:00
GT . gtChatSocket . onmessage = function ( evt )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
if ( GT . appSettings . gtShareEnable == true )
2020-12-14 02:10:27 +00:00
{
2023-01-22 00:48:15 +00:00
let jsmesg = false ;
2020-12-14 02:10:27 +00:00
try
{
2020-10-31 14:01:03 +00:00
jsmesg = JSON . parse ( evt . data ) ;
2020-12-14 02:10:27 +00:00
}
catch ( err )
{
2020-10-31 14:01:03 +00:00
// bad message, dumping client
2023-02-17 00:35:44 +00:00
GT . gtState = ChatState . error ;
2020-10-31 14:01:03 +00:00
return ;
}
2022-12-31 21:36:41 +00:00
if ( ! ( "type" in jsmesg ) )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtState = ChatState . error ;
2020-10-31 14:01:03 +00:00
return ;
}
2023-02-17 00:35:44 +00:00
if ( jsmesg . type in GT . chatRecvFunctions )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
GT . chatRecvFunctions [ jsmesg . type ] ( jsmesg ) ;
2020-12-14 02:10:27 +00:00
}
else
{
2022-08-28 01:20:49 +00:00
// Not fatal!
2022-08-28 01:23:02 +00:00
console . log ( "Unknown oams message '" + jsmesg . type + "' ignoring" ) ;
2020-10-31 14:01:03 +00:00
}
}
} ;
2020-08-19 16:55:41 +00:00
2023-02-17 00:35:44 +00:00
GT . gtChatSocket . onerror = function ( )
2020-12-14 02:10:27 +00:00
{
2023-02-23 00:51:31 +00:00
this . close ( ) ;
GT . gtChatSocket = null ;
2023-02-17 00:35:44 +00:00
GT . gtState = ChatState . error ;
2020-10-31 14:01:03 +00:00
} ;
2020-08-19 16:55:41 +00:00
2023-02-17 00:35:44 +00:00
GT . gtChatSocket . onclose = function ( )
2020-12-14 02:10:27 +00:00
{
2023-02-23 00:51:31 +00:00
GT . gtChatSocket = null ;
2023-02-17 00:35:44 +00:00
GT . gtState = ChatState . closed ;
2020-10-31 14:01:03 +00:00
} ;
2020-08-19 16:55:41 +00:00
}
2020-10-31 14:01:03 +00:00
function gtConnecting ( ) { }
2020-08-19 16:55:41 +00:00
2020-12-14 02:10:27 +00:00
function gtInError ( )
{
2020-10-31 14:01:03 +00:00
closeGtSocket ( ) ;
2020-08-19 16:55:41 +00:00
}
2020-12-14 02:10:27 +00:00
function gtChatSendClose ( )
{
2020-10-31 14:01:03 +00:00
msg = Object ( ) ;
msg . type = "close" ;
2023-02-17 00:35:44 +00:00
msg . uuid = GT . appSettings . chatUUID ;
2020-08-19 16:55:41 +00:00
2020-10-31 14:01:03 +00:00
sendGtJson ( JSON . stringify ( msg ) ) ;
}
2020-12-14 02:10:27 +00:00
function closeGtSocket ( )
{
2023-02-17 00:35:44 +00:00
if ( GT . gtChatSocket != null )
2020-12-14 02:10:27 +00:00
{
2020-10-31 14:01:03 +00:00
gtChatSendClose ( ) ;
2023-02-23 00:51:31 +00:00
GT . gtChatSocket . close ( ) ;
GT . gtChatSocket = null ;
GT . gtState = ChatState . none ;
2020-12-14 02:10:27 +00:00
}
2023-02-17 00:35:44 +00:00
else GT . gtState = ChatState . none ;
2020-10-31 14:01:03 +00:00
}
2020-08-19 16:55:41 +00:00
2020-12-14 02:10:27 +00:00
function gtClosedSocket ( )
{
2023-02-23 00:51:31 +00:00
if ( GT . gtChatSocket != null )
{
GT . gtChatSocket . close ( ) ;
GT . gtChatSocket = null ;
}
2023-02-17 00:35:44 +00:00
GT . gtState = ChatState . none ;
2020-08-19 16:55:41 +00:00
}
2022-11-27 22:00:19 +00:00
// Connect 15 seconds after startup
2023-02-17 00:35:44 +00:00
GT . lastConnectAttempt = parseInt ( Date . now ( ) / 1000 ) - 15 ;
2022-11-27 21:51:21 +00:00
2020-12-14 02:10:27 +00:00
function gtCanConnect ( )
{
2023-02-17 00:35:44 +00:00
GT . lastConnectAttempt = timeNowSec ( ) ;
GT . gtState = ChatState . connect ;
2020-10-31 14:01:03 +00:00
}
2020-08-19 16:55:41 +00:00
2020-12-14 02:10:27 +00:00
function gtSetIdle ( )
{
2023-02-17 00:35:44 +00:00
if ( timeNowSec ( ) - GT . lastConnectAttempt >= 30 )
2022-11-27 21:51:21 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtStatusCount = 0 ;
GT . gtNeedUsersList = true ;
GT . gtState = ChatState . idle ;
GT . lastGtStatus = "" ;
2022-11-27 21:51:21 +00:00
}
2023-02-17 00:35:44 +00:00
GT . gtUuidValid = false ;
2020-10-31 14:01:03 +00:00
}
2020-12-14 02:10:27 +00:00
function gtStatusCheck ( )
{
2023-02-17 00:35:44 +00:00
if ( GT . gtStatusCount > 0 )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtStatusCount -- ;
2020-10-31 14:01:03 +00:00
}
2023-02-17 00:35:44 +00:00
if ( GT . gtStatusCount == 0 || GT . gtLiveStatusUpdate == true )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
if ( GT . gtLiveStatusUpdate == true )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtLiveStatusUpdate = false ;
2020-12-14 02:10:27 +00:00
}
else
{
2023-02-17 00:35:44 +00:00
GT . lastGtStatus = "" ;
GT . gtStatusCount = GT . gtStatusTime ;
2020-10-31 14:01:03 +00:00
}
gtChatSendStatus ( ) ;
}
2023-02-17 00:35:44 +00:00
if ( GT . gtNeedUsersList == true )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtNeedUsersList = false ;
2020-10-31 14:01:03 +00:00
gtChatGetList ( ) ;
}
}
2022-11-27 22:23:34 +00:00
function sendGtJson ( json , isUUIDrequest = false )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
if ( GT . appSettings . gtShareEnable == true && GT . gtChatSocket != null )
2020-12-14 02:10:27 +00:00
{
2023-02-25 21:41:32 +00:00
if ( GT . gtChatSocket . readyState == WebSocket . OPEN && ( isUUIDrequest || GT . gtUuidValid ) )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtChatSocket . send ( json ) ;
2020-12-14 02:10:27 +00:00
}
else
{
2023-02-25 21:41:32 +00:00
if ( GT . gtChatSocket . readyState == WebSocket . CLOSED )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtState = ChatState . closed ;
2020-10-31 14:01:03 +00:00
}
}
2020-12-14 02:10:27 +00:00
}
2020-09-28 19:28:09 +00:00
}
2020-08-19 16:55:41 +00:00
2023-02-17 00:35:44 +00:00
GT . lastGtStatus = "" ;
2020-10-31 14:01:03 +00:00
2020-12-14 02:10:27 +00:00
function gtChatSendStatus ( )
{
2020-10-31 14:01:03 +00:00
var msg = Object ( ) ;
msg . type = "status" ;
2023-02-17 00:35:44 +00:00
msg . uuid = GT . appSettings . chatUUID ;
2020-10-31 14:01:03 +00:00
2023-02-17 18:33:06 +00:00
msg . call = GT . appSettings . myCall ;
msg . grid = GT . appSettings . myRawGrid ;
msg . freq = GT . appSettings . myRawFreq ;
msg . mode = GT . appSettings . myMode ;
msg . band = GT . appSettings . myBand ;
2021-01-02 21:24:12 +00:00
msg . src = "GT" ;
2023-02-17 00:35:44 +00:00
msg . canmsg = GT . appSettings . gtMsgEnable ;
msg . o = GT . appSettings . gtSpotEnable == true ? 1 : 0 ;
2020-10-31 14:01:03 +00:00
msg = JSON . stringify ( msg ) ;
2023-02-17 00:35:44 +00:00
if ( msg != GT . lastGtStatus )
2020-12-14 02:10:27 +00:00
{
2020-10-31 14:01:03 +00:00
sendGtJson ( msg ) ;
2023-02-17 00:35:44 +00:00
GT . lastGtStatus = msg ;
2020-10-31 14:01:03 +00:00
}
}
2022-10-03 20:19:16 +00:00
function gtChatSendSpots ( spotsObject , detailsObject )
2020-12-14 02:10:27 +00:00
{
2023-01-22 23:55:51 +00:00
let msg = Object ( ) ;
2020-10-31 14:01:03 +00:00
msg . type = "o" ;
2023-02-17 00:35:44 +00:00
msg . uuid = GT . appSettings . chatUUID ;
2020-10-31 14:01:03 +00:00
msg . o = spotsObject ;
2022-10-03 20:19:16 +00:00
msg . d = detailsObject ;
2023-01-22 23:55:51 +00:00
sendGtJson ( JSON . stringify ( msg ) ) ;
}
function gtChatSendDecodes ( instancesObject )
{
let msg = Object ( ) ;
msg . type = "d" ;
2023-02-17 00:35:44 +00:00
msg . uuid = GT . appSettings . chatUUID ;
2023-01-22 23:55:51 +00:00
msg . i = instancesObject ;
sendGtJson ( JSON . stringify ( msg ) ) ;
}
function oamsBandActivityCheck ( )
{
2023-02-17 18:33:06 +00:00
if ( GT . appSettings . oamsBandActivity == true && GT . appSettings . myGrid . length >= 4 )
2023-01-22 23:55:51 +00:00
{
2023-02-17 18:33:06 +00:00
let grid = GT . appSettings . myGrid . substring ( 0 , 4 ) . toUpperCase ( ) ;
2023-02-17 00:35:44 +00:00
if ( GT . appSettings . oamsBandActivityNeighbors == true )
2023-01-22 23:55:51 +00:00
{
gtChatSendBandActivityRequest ( squareToNeighbors ( grid ) ) ;
}
else
{
gtChatSendBandActivityRequest ( [ grid ] ) ;
}
}
}
function gtChatSendBandActivityRequest ( gridArray )
{
msg = Object ( ) ;
msg . type = "ba" ;
2023-02-17 00:35:44 +00:00
msg . uuid = GT . appSettings . chatUUID ;
2023-01-22 23:55:51 +00:00
msg . ga = gridArray ;
sendGtJson ( JSON . stringify ( msg ) ) ;
}
function bandActivityReply ( jsmesg )
{
2023-02-17 00:35:44 +00:00
GT . oamsBandActivityData = jsmesg . r ;
2023-01-22 23:55:51 +00:00
renderBandActivity ( ) ;
2020-10-31 14:01:03 +00:00
}
2020-12-14 02:10:27 +00:00
function gtChatRemoveCall ( jsmesg )
{
2020-10-31 14:01:03 +00:00
var id = jsmesg . id ;
2022-12-01 23:49:05 +00:00
var cid = jsmesg . cid ;
2023-02-17 00:35:44 +00:00
if ( cid in GT . gtFlagPins )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
if ( id in GT . gtFlagPins [ cid ] . ids )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
delete GT . gtFlagPins [ cid ] . ids [ id ] ;
2022-12-01 23:49:05 +00:00
}
2022-12-04 01:07:35 +00:00
2023-02-17 00:35:44 +00:00
if ( Object . keys ( GT . gtFlagPins [ cid ] . ids ) . length == 0 )
2022-12-01 23:49:05 +00:00
{
2023-02-17 00:35:44 +00:00
delete GT . gtCallsigns [ GT . gtFlagPins [ cid ] . call ] [ cid ] ;
2022-12-04 01:07:35 +00:00
2023-02-17 00:35:44 +00:00
if ( GT . gtFlagPins [ cid ] . pin != null )
2022-12-01 23:49:05 +00:00
{
// remove pin from map here
2023-02-17 00:35:44 +00:00
if ( GT . layerSources . gtflags . hasFeature ( GT . gtFlagPins [ cid ] . pin ) )
{ GT . layerSources . gtflags . removeFeature ( GT . gtFlagPins [ cid ] . pin ) ; }
delete GT . gtFlagPins [ cid ] . pin ;
GT . gtFlagPins [ cid ] . pin = null ;
2022-12-01 23:49:05 +00:00
}
2023-02-17 00:35:44 +00:00
GT . gtFlagPins [ cid ] . live = false ;
2022-12-01 23:49:05 +00:00
notifyNoChat ( cid ) ;
2023-02-17 00:35:44 +00:00
if ( ! ( cid in GT . gtMessages ) )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
if ( Object . keys ( GT . gtCallsigns [ GT . gtFlagPins [ cid ] . call ] ) . length == 0 )
2022-12-04 01:07:35 +00:00
{
2023-02-17 00:35:44 +00:00
delete GT . gtCallsigns [ GT . gtFlagPins [ cid ] . call ] ;
2022-12-04 01:07:35 +00:00
}
2023-02-17 00:35:44 +00:00
delete GT . gtFlagPins [ cid ] ;
2020-10-31 14:01:03 +00:00
}
2022-12-01 23:49:05 +00:00
updateChatWindow ( cid ) ;
2020-10-31 14:01:03 +00:00
}
}
}
2020-12-14 02:10:27 +00:00
function gtChatUpdateCall ( jsmesg )
{
2020-10-31 14:01:03 +00:00
var id = jsmesg . id ;
var cid = jsmesg . cid ;
2023-02-17 00:35:44 +00:00
if ( cid in GT . gtFlagPins )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtFlagPins [ cid ] . ids [ id ] = true ;
2020-10-31 14:01:03 +00:00
// Did they move grid location?
2023-02-17 00:35:44 +00:00
if ( jsmesg . grid != GT . gtFlagPins [ cid ] . grid && GT . gtFlagPins [ cid ] . pin != null )
2020-12-14 02:10:27 +00:00
{
2020-10-31 14:01:03 +00:00
// remove pin from map here
2023-02-17 00:35:44 +00:00
if ( GT . layerSources . gtflags . hasFeature ( GT . gtFlagPins [ cid ] . pin ) )
{ GT . layerSources . gtflags . removeFeature ( GT . gtFlagPins [ cid ] . pin ) ; }
delete GT . gtFlagPins [ cid ] . pin ;
GT . gtFlagPins [ cid ] . pin = null ;
2020-10-31 14:01:03 +00:00
}
2022-12-04 01:07:35 +00:00
// Changed callsign?
2023-02-17 00:35:44 +00:00
if ( GT . gtFlagPins [ cid ] . call != jsmesg . call )
2022-12-04 01:07:35 +00:00
{
2023-02-17 00:35:44 +00:00
delete GT . gtCallsigns [ GT . gtFlagPins [ cid ] . call ] [ cid ] ;
2022-12-04 01:07:35 +00:00
}
2020-12-14 02:10:27 +00:00
}
else
{
2023-02-17 00:35:44 +00:00
GT . gtFlagPins [ cid ] = Object ( ) ;
GT . gtFlagPins [ cid ] . pin = null ;
GT . gtFlagPins [ cid ] . ids = Object ( ) ;
GT . gtFlagPins [ cid ] . ids [ id ] = true ;
}
GT . gtFlagPins [ cid ] . cid = jsmesg . cid ;
GT . gtFlagPins [ cid ] . call = jsmesg . call ;
GT . gtFlagPins [ cid ] . fCall = formatCallsign ( jsmesg . call ) ;
GT . gtFlagPins [ cid ] . grid = jsmesg . grid ;
GT . gtFlagPins [ cid ] . freq = jsmesg . freq ;
GT . gtFlagPins [ cid ] . band = jsmesg . band ;
GT . gtFlagPins [ cid ] . mode = jsmesg . mode ;
GT . gtFlagPins [ cid ] . src = jsmesg . src ;
GT . gtFlagPins [ cid ] . canmsg = jsmesg . canmsg ;
GT . gtFlagPins [ cid ] . o = jsmesg . o ;
GT . gtFlagPins [ cid ] . dxcc = callsignToDxcc ( jsmesg . call ) ;
GT . gtFlagPins [ cid ] . live = true ;
2020-10-31 14:01:03 +00:00
// Make a pin here
2023-02-17 00:35:44 +00:00
if ( GT . gtFlagPins [ cid ] . pin == null )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
makeGtPin ( GT . gtFlagPins [ cid ] ) ;
if ( GT . gtFlagPins [ cid ] . pin != null )
2022-09-19 02:06:49 +00:00
{
2023-02-17 00:35:44 +00:00
GT . layerSources . gtflags . addFeature ( GT . gtFlagPins [ cid ] . pin ) ;
2022-09-19 02:06:49 +00:00
}
2020-10-31 14:01:03 +00:00
}
2022-12-04 01:07:35 +00:00
2023-02-17 00:35:44 +00:00
if ( ! ( GT . gtFlagPins [ cid ] . call in GT . gtCallsigns ) )
2022-12-04 01:07:35 +00:00
{
// Can happen when a user changes callsign
2023-02-17 00:35:44 +00:00
GT . gtCallsigns [ GT . gtFlagPins [ cid ] . call ] = { } ;
2022-12-04 01:07:35 +00:00
}
2023-02-17 00:35:44 +00:00
GT . gtCallsigns [ GT . gtFlagPins [ cid ] . call ] [ cid ] = true ;
2022-12-04 01:07:35 +00:00
2022-11-27 23:55:03 +00:00
updateChatWindow ( cid ) ;
2020-10-31 14:01:03 +00:00
}
2020-12-14 02:10:27 +00:00
function gtChatGetList ( )
{
2020-10-31 14:01:03 +00:00
msg = Object ( ) ;
msg . type = "list" ;
2023-02-17 00:35:44 +00:00
msg . uuid = GT . appSettings . chatUUID ;
2020-10-31 14:01:03 +00:00
sendGtJson ( JSON . stringify ( msg ) ) ;
}
2020-12-14 02:10:27 +00:00
function redrawPins ( )
{
2020-10-31 14:01:03 +00:00
clearGtFlags ( ) ;
2023-02-17 00:35:44 +00:00
for ( cid in GT . gtFlagPins )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
if ( GT . gtFlagPins [ cid ] . pin != null )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
delete GT . gtFlagPins [ cid ] . pin ;
GT . gtFlagPins [ cid ] . pin = null ;
2020-10-31 14:01:03 +00:00
}
2023-02-17 00:35:44 +00:00
makeGtPin ( GT . gtFlagPins [ cid ] ) ;
2020-10-31 14:01:03 +00:00
2023-02-17 00:35:44 +00:00
if ( GT . gtFlagPins [ cid ] . pin != null )
2022-11-27 21:51:21 +00:00
{
2023-02-17 00:35:44 +00:00
GT . layerSources . gtflags . addFeature ( GT . gtFlagPins [ cid ] . pin ) ;
2022-11-27 21:51:21 +00:00
}
2020-10-31 14:01:03 +00:00
}
}
2020-12-14 02:10:27 +00:00
function makeGtPin ( obj )
{
try
{
if ( obj . pin )
{
2023-02-17 00:35:44 +00:00
if ( GT . layerSources . gtflags . hasFeature ( obj . pin ) )
2022-11-27 21:51:21 +00:00
{
2023-02-17 00:35:44 +00:00
GT . layerSources . gtflags . removeFeature ( obj . pin ) ;
2022-11-27 21:51:21 +00:00
}
2020-10-31 14:01:03 +00:00
delete obj . pin ;
obj . pin = null ;
}
2022-09-19 02:06:49 +00:00
if ( obj . src != "GT" ) return ;
2020-10-31 14:01:03 +00:00
if ( typeof obj . grid == "undefined" || obj . grid == null ) return ;
2020-09-28 19:28:09 +00:00
2020-10-31 14:01:03 +00:00
if ( obj . grid . length != 4 && obj . grid . length != 6 ) return ;
2020-08-19 16:55:41 +00:00
2022-11-06 23:07:55 +00:00
if ( validateGridFromString ( obj . grid ) == false ) return ;
2020-09-28 19:28:09 +00:00
2023-01-14 03:59:58 +00:00
if ( ! validateMapBandAndMode ( obj . band , obj . mode ) )
2022-11-27 21:51:21 +00:00
{
return ;
}
2020-09-28 19:28:09 +00:00
2022-11-27 21:51:21 +00:00
var LL = squareToCenter ( obj . grid ) ;
2023-11-13 02:13:28 +00:00
obj . pin = iconFeature ( ol . proj . fromLonLat ( [ LL . o , LL . a ] ) , GT . gtFlagIcon , 100 , "gtFlag" ) ;
2020-10-31 14:01:03 +00:00
obj . pin . key = obj . cid ;
obj . pin . isGtFlag = true ;
obj . pin . size = 1 ;
2020-12-14 02:10:27 +00:00
}
catch ( e ) { }
2020-08-19 16:55:41 +00:00
}
2020-12-14 02:10:27 +00:00
function gtChatNewList ( jsmesg )
{
2020-10-31 14:01:03 +00:00
clearGtFlags ( ) ;
2022-12-01 03:40:21 +00:00
// starting clean if we're getting a new chat list
2023-02-17 00:35:44 +00:00
GT . gtFlagPins = Object ( )
GT . gtMessages = Object ( ) ;
GT . gtUnread = Object ( ) ;
GT . gtCallsigns = Object ( ) ;
GT . gtSentAwayToCid = Object ( ) ;
2020-10-31 14:01:03 +00:00
2020-12-14 02:10:27 +00:00
for ( var key in jsmesg . data . calls )
{
2020-10-31 14:01:03 +00:00
var cid = jsmesg . data . cid [ key ] ;
var id = jsmesg . data . id [ key ] ;
2020-12-14 02:10:27 +00:00
if ( id != myChatId )
{
2023-02-17 00:35:44 +00:00
if ( cid in GT . gtFlagPins )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtFlagPins [ cid ] . ids [ id ] = true ;
2020-12-14 02:10:27 +00:00
}
else
{
2023-02-17 00:35:44 +00:00
GT . gtFlagPins [ cid ] = Object ( ) ;
GT . gtFlagPins [ cid ] . ids = Object ( ) ;
GT . gtFlagPins [ cid ] . ids [ id ] = true ;
GT . gtFlagPins [ cid ] . pin = null ;
2020-10-31 14:01:03 +00:00
}
2023-02-17 00:35:44 +00:00
GT . gtFlagPins [ cid ] . call = jsmesg . data . calls [ key ] ;
GT . gtFlagPins [ cid ] . fCall = formatCallsign ( GT . gtFlagPins [ cid ] . call ) ;
GT . gtFlagPins [ cid ] . grid = jsmesg . data . grid [ key ] ;
GT . gtFlagPins [ cid ] . freq = jsmesg . data . freq [ key ] ;
GT . gtFlagPins [ cid ] . band = jsmesg . data . band [ key ] ;
GT . gtFlagPins [ cid ] . mode = jsmesg . data . mode [ key ] ;
GT . gtFlagPins [ cid ] . src = jsmesg . data . src [ key ] ;
GT . gtFlagPins [ cid ] . cid = cid ;
GT . gtFlagPins [ cid ] . canmsg = jsmesg . data . canmsg [ key ] ;
GT . gtFlagPins [ cid ] . o = jsmesg . data . o [ key ] ;
GT . gtFlagPins [ cid ] . dxcc = callsignToDxcc ( GT . gtFlagPins [ cid ] . call ) ;
GT . gtFlagPins [ cid ] . live = true ;
if ( ! ( GT . gtFlagPins [ cid ] . call in GT . gtCallsigns ) )
2022-12-04 01:07:35 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtCallsigns [ GT . gtFlagPins [ cid ] . call ] = Object ( ) ;
2022-12-04 01:07:35 +00:00
}
2023-02-17 00:35:44 +00:00
GT . gtCallsigns [ GT . gtFlagPins [ cid ] . call ] [ cid ] = true ;
2022-09-19 02:06:49 +00:00
2023-02-17 00:35:44 +00:00
makeGtPin ( GT . gtFlagPins [ cid ] ) ;
2022-09-19 02:06:49 +00:00
2023-02-17 00:35:44 +00:00
if ( GT . gtFlagPins [ cid ] . pin != null )
2022-09-19 02:06:49 +00:00
{
2023-02-17 00:35:44 +00:00
GT . layerSources . gtflags . addFeature ( GT . gtFlagPins [ cid ] . pin ) ;
2022-09-19 02:06:49 +00:00
}
2020-10-31 14:01:03 +00:00
}
}
2020-08-19 16:55:41 +00:00
2020-10-31 14:01:03 +00:00
updateChatWindow ( ) ;
2023-01-22 23:55:51 +00:00
oamsBandActivityCheck ( ) ;
2020-08-19 16:55:41 +00:00
}
2020-12-14 02:10:27 +00:00
function appendToHistory ( cid , jsmesg )
{
2023-02-17 00:35:44 +00:00
if ( ! ( cid in GT . gtMessages ) )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtMessages [ cid ] = Object ( ) ;
GT . gtMessages [ cid ] . history = Array ( ) ;
2020-10-31 14:01:03 +00:00
}
2020-08-19 16:55:41 +00:00
2023-02-17 00:35:44 +00:00
GT . gtMessages [ cid ] . history . push ( jsmesg ) ;
while ( GT . gtMessages [ cid ] . history . length > GT . gtMaxChatMessages )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtMessages [ cid ] . history . shift ( ) ;
2020-10-31 14:01:03 +00:00
}
}
2020-12-14 02:10:27 +00:00
function htmlEntities ( str )
{
2020-10-31 14:01:03 +00:00
return String ( str )
. replace ( /&/g , "&" )
. replace ( /</g , "<" )
. replace ( />/g , ">" )
. replace ( /"/g , """ ) ;
}
2023-08-31 03:06:10 +00:00
function sendSimplePushMessage ( jsmesg )
2023-08-24 15:45:33 +00:00
{
const url = "https://api.simplepush.io/send" ;
2023-10-14 20:42:44 +00:00
let data = {
key : GT . msgSettings . msgSimplepushApiKey ,
title : "GT Chat - " + formatCallsign ( GT . appSettings . myCall ) ,
msg : formatCallsign ( jsmesg . call ) + ": " + jsmesg . msg
} ;
2023-08-24 15:45:33 +00:00
getPostBuffer (
url ,
null , // callback,
null ,
"https" ,
443 ,
data ,
2023-10-14 20:42:44 +00:00
5000
2023-08-24 15:45:33 +00:00
) ;
}
2023-10-09 19:48:21 +00:00
function sendPushOverMessage ( jsmesg , test = false )
2023-08-24 15:45:33 +00:00
{
const url = "https://api.pushover.net/1/messages.json" ;
let data = {
2023-10-09 19:48:21 +00:00
user : GT . msgSettings . msgPushoverUserKey ,
2023-08-24 15:45:33 +00:00
token : GT . msgSettings . msgPushoverToken ,
title :
2023-10-14 20:42:44 +00:00
"GT Chat - " + formatCallsign ( GT . appSettings . myCall ) ,
message : formatCallsign ( jsmesg . call ) + ": " + jsmesg . msg
2023-08-24 15:45:33 +00:00
} ;
getPostBuffer (
url ,
2023-10-09 19:48:21 +00:00
PushoverReply , // callback,
test ,
2023-08-24 15:45:33 +00:00
"https" ,
443 ,
data ,
2023-10-14 20:42:44 +00:00
5000 // timeoutMs,
2023-08-24 15:45:33 +00:00
) ;
}
2023-10-09 19:48:21 +00:00
function PushoverReply ( data , isTest )
{
if ( isTest )
{
var result = "Unknown Error" ;
var color = "#F00" ;
var responseJson = JSON . parse ( data ) ;
if ( typeof responseJson != "undefined" && typeof responseJson . status != "undefined" )
{
if ( responseJson . status == 1 )
{
// {"status":1,"request":"1d5ace84-c2e4-4b19-a051-5ac4c9671170"}
color = "#FFF" ;
result = "Passed!" ;
}
else if ( responseJson . status == 0 )
{
color = "#FF0" ;
if ( typeof responseJson . user != "undefined" )
{
// {"user":"invalid","errors":["user identifier is not a valid user, group, or subscribed user key, see https://pushover.net/api#identifiers"],"status":0,"request":"3ef45ac6-38d6-47db-b7aa-7731e8ac0fcb"}
result = "User Key Invalid" ;
}
else if ( typeof responseJson . token != "undefined" )
{
// {"token":"invalid","errors":["application token is invalid, see https://pushover.net/api"],"status":0,"request":"10629fe4-1e37-4c0f-a123-4585a3fb2aea"}
result = "API Token Invalid" ;
}
else
{
result = "Unknown Response" ;
}
}
else
{
result = "Unknown Status" ;
}
}
pushOverTestResultsDiv . innerHTML = result ;
pushOverTestResultsDiv . style . color = color ;
}
}
2020-12-14 02:10:27 +00:00
function gtChatMessage ( jsmesg )
{
2023-02-17 00:35:44 +00:00
if ( GT . appSettings . gtMsgEnable == true )
2020-12-14 02:10:27 +00:00
{
2020-10-31 14:01:03 +00:00
var cid = jsmesg . cid ;
jsmesg . when = Date . now ( ) ;
2020-12-14 02:10:27 +00:00
try
{
2020-12-14 01:30:10 +00:00
jsmesg . msg = new Buffer . from ( jsmesg . msg , "base64" ) . toString ( "utf8" ) ; // eslint-disable-line new-cap
2020-10-31 14:01:03 +00:00
jsmesg . msg = htmlEntities ( jsmesg . msg ) ;
2020-12-14 02:10:27 +00:00
}
catch ( e )
{
2023-08-24 15:45:33 +00:00
jsmesg . msg = "Corrupt message received" ;
2020-10-31 14:01:03 +00:00
}
2020-08-19 16:55:41 +00:00
2020-12-14 02:10:27 +00:00
if ( jsmesg . call != null && jsmesg . call != "" && jsmesg . call != "NOCALL" )
{
2020-10-31 14:01:03 +00:00
appendToHistory ( cid , jsmesg ) ;
2023-02-17 00:35:44 +00:00
GT . gtUnread [ cid ] = true ;
GT . gtCurrentMessageCount ++ ;
2020-08-19 16:55:41 +00:00
2023-10-09 19:48:21 +00:00
if ( newChatMessage ( cid , jsmesg ) == false )
2023-08-24 15:45:33 +00:00
{
2023-10-09 19:48:21 +00:00
// Only notify if you're not in active chat with them.
if ( GT . msgSettings . msgSimplepush && GT . msgSettings . msgSimplepushChat && GT . msgSettings . msgSimplepushApiKey != null )
{
sendSimplePushMessage ( jsmesg ) ;
}
if ( GT . msgSettings . msgPushover && GT . msgSettings . msgPushoverChat && GT . msgSettings . msgPushoverUserKey != null &&
GT . msgSettings . msgPushoverToken != null )
{
sendPushOverMessage ( jsmesg ) ;
}
2023-08-24 15:45:33 +00:00
2023-10-09 19:48:21 +00:00
alertChatMessage ( ) ;
}
2020-09-22 16:51:16 +00:00
2023-02-17 00:35:44 +00:00
if ( GT . msgSettings . msgAwaySelect == 1 && ! ( cid in GT . gtSentAwayToCid ) )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
GT . gtSentAwayToCid [ cid ] = true ;
2020-10-31 14:01:03 +00:00
gtSendMessage (
2023-02-17 00:35:44 +00:00
"Away message [ " + GT . msgSettings . msgAwayText + " ]" ,
2020-10-31 14:01:03 +00:00
cid
) ;
}
}
}
2020-08-19 16:55:41 +00:00
}
2020-09-22 16:51:16 +00:00
2020-12-14 02:10:27 +00:00
function gtSendMessage ( message , who )
{
2020-10-31 14:01:03 +00:00
msg = Object ( ) ;
msg . type = "mesg" ;
2023-02-17 00:35:44 +00:00
msg . uuid = GT . appSettings . chatUUID ;
2020-10-31 14:01:03 +00:00
msg . cid = who ;
2020-12-14 01:30:10 +00:00
msg . msg = new Buffer . from ( message ) . toString ( "base64" ) ; // eslint-disable-line new-cap
2020-10-31 14:01:03 +00:00
sendGtJson ( JSON . stringify ( msg ) ) ;
msg . msg = htmlEntities ( message ) ;
msg . id = 0 ;
msg . when = Date . now ( ) ;
appendToHistory ( who , msg ) ;
2020-08-19 16:55:41 +00:00
}
2020-12-14 02:10:27 +00:00
function gtChatSendUUID ( )
{
2020-10-31 14:01:03 +00:00
var msg = Object ( ) ;
msg . type = "uuid" ;
2023-02-17 00:35:44 +00:00
if ( GT . appSettings . chatUUID != "" )
2023-02-10 02:22:35 +00:00
{
2023-02-17 00:35:44 +00:00
msg . uuid = GT . appSettings . chatUUID ;
2023-02-10 02:22:35 +00:00
}
else
{
msg . uuid = null ;
}
2023-02-17 18:33:06 +00:00
msg . call = GT . appSettings . myCall ;
2021-06-06 20:42:06 +00:00
msg . ver = gtShortVersion ;
2020-10-31 14:01:03 +00:00
2022-11-27 22:23:34 +00:00
sendGtJson ( JSON . stringify ( msg ) , true ) ;
2023-02-17 00:35:44 +00:00
GT . gtState = ChatState . waitUUID ;
2023-02-12 00:59:46 +00:00
}
function gtWaitUUID ( )
{
console . log ( "waiting for UUID from OAMS" ) ;
2020-08-19 16:55:41 +00:00
}
2020-12-14 02:10:27 +00:00
function gtChatSetUUID ( jsmesg )
{
2023-02-17 00:35:44 +00:00
GT . appSettings . chatUUID = jsmesg . uuid ;
2020-10-31 14:01:03 +00:00
myChatId = jsmesg . id ;
2023-02-17 00:35:44 +00:00
GT . gtUuidValid = true ;
2020-10-31 14:01:03 +00:00
gtChatSendStatus ( ) ;
2023-02-17 00:35:44 +00:00
GT . gtLiveStatusUpdate = false ;
GT . gtStatusCount = GT . gtStatusTime ;
GT . gtState = ChatState . status ;
2020-08-19 16:55:41 +00:00
}
2023-02-17 00:35:44 +00:00
GT . getEngineWasRunning = false ;
2022-11-28 01:31:12 +00:00
2020-12-14 02:10:27 +00:00
function gtChatStateMachine ( )
{
2023-02-17 00:35:44 +00:00
if ( GT . appSettings . gtShareEnable == true && GT . mapSettings . offlineMode == false )
2020-12-14 02:10:27 +00:00
{
2020-10-31 14:01:03 +00:00
var now = timeNowSec ( ) ;
2023-02-17 00:35:44 +00:00
GT . gtStateToFunction [ GT . gtState ] ( ) ;
2020-10-31 14:01:03 +00:00
2023-02-17 00:35:44 +00:00
if ( Object . keys ( GT . gtUnread ) . length > 0 && now % 2 == 0 )
2020-12-14 02:10:27 +00:00
{
2020-10-31 14:01:03 +00:00
msgImg . style . webkitFilter = "invert(1)" ;
2020-12-14 02:10:27 +00:00
}
else msgImg . style . webkitFilter = "" ;
2020-10-31 14:01:03 +00:00
2023-02-17 00:35:44 +00:00
if ( GT . msgSettings . msgFrequencySelect > 0 && Object . keys ( GT . gtUnread ) . length > 0 )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
if ( now - GT . lastChatMsgAlert > GT . msgSettings . msgFrequencySelect * 60 )
2020-12-14 02:10:27 +00:00
{
2020-10-31 14:01:03 +00:00
alertChatMessage ( ) ;
}
2020-08-19 16:55:41 +00:00
}
2023-02-17 00:35:44 +00:00
GT . getEngineWasRunning = true ;
2020-12-14 02:10:27 +00:00
}
else
{
2023-02-17 00:35:44 +00:00
if ( GT . getEngineWasRunning == true )
2022-11-28 01:31:12 +00:00
{
2023-02-17 00:35:44 +00:00
GT . getEngineWasRunning = false ;
2022-11-28 01:31:12 +00:00
closeGtSocket ( ) ;
2023-02-17 00:35:44 +00:00
GT . lastGtStatus = "" ;
2022-11-28 01:31:12 +00:00
}
2020-10-31 14:01:03 +00:00
}
}
2020-12-14 02:10:27 +00:00
function gtSpotMessage ( jsmesg )
{
2023-02-17 00:35:44 +00:00
if ( jsmesg . cid in GT . gtFlagPins )
2022-10-03 20:19:16 +00:00
{
let frequency , band , mode ;
if ( jsmesg . ex != null )
{
frequency = Number ( jsmesg . ex [ 0 ] ) ;
2023-02-17 00:35:44 +00:00
band = formatBand ( Number ( frequency / 1000000 ) ) ;
2022-10-03 20:19:16 +00:00
mode = String ( jsmesg . ex [ 1 ] ) ;
}
else
{
2023-02-17 00:35:44 +00:00
frequency = GT . gtFlagPins [ jsmesg . cid ] . freq ;
band = GT . gtFlagPins [ jsmesg . cid ] . band ;
mode = GT . gtFlagPins [ jsmesg . cid ] . mode ;
2022-10-03 20:19:16 +00:00
}
addNewOAMSSpot ( jsmesg . cid , jsmesg . db , frequency , band , mode ) ;
}
2020-10-31 14:01:03 +00:00
}
2020-12-14 02:10:27 +00:00
function gtChatSystemInit ( )
{
2023-02-17 00:35:44 +00:00
GT . gtEngineInterval = nodeTimers . setInterval ( gtChatStateMachine , 1000 ) ;
2020-10-31 14:01:03 +00:00
}
2020-12-14 02:10:27 +00:00
function showGtFlags ( )
{
2023-02-17 00:35:44 +00:00
if ( GT . appSettings . gtFlagImgSrc > 0 )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
if ( GT . mapSettings . offlineMode == false )
2020-12-14 02:10:27 +00:00
{
2020-10-31 14:01:03 +00:00
redrawPins ( ) ;
2023-02-17 00:35:44 +00:00
GT . layerVectors . gtflags . setVisible ( true ) ;
2020-12-14 02:10:27 +00:00
}
else
{
2023-02-17 00:35:44 +00:00
GT . layerVectors . gtflags . setVisible ( false ) ;
2020-08-19 16:55:41 +00:00
}
2020-12-14 02:10:27 +00:00
}
2023-02-17 00:35:44 +00:00
else GT . layerVectors . gtflags . setVisible ( false ) ;
2020-08-19 16:55:41 +00:00
}
2020-12-14 02:10:27 +00:00
function clearGtFlags ( )
{
2023-02-17 00:35:44 +00:00
GT . layerSources . gtflags . clear ( ) ;
2020-08-19 16:55:41 +00:00
}
2020-12-14 02:10:27 +00:00
function toggleGtMap ( )
{
2023-02-17 00:35:44 +00:00
GT . appSettings . gtFlagImgSrc += 1 ;
GT . appSettings . gtFlagImgSrc %= 2 ;
gtFlagImg . src = GT . gtFlagImageArray [ GT . appSettings . gtFlagImgSrc ] ;
if ( GT . spotView > 0 && GT . receptionSettings . mergeSpots == false ) return ;
if ( GT . appSettings . gtFlagImgSrc > 0 )
2020-12-14 02:10:27 +00:00
{
2020-10-31 14:01:03 +00:00
redrawPins ( ) ;
2023-02-17 00:35:44 +00:00
GT . layerVectors . gtflags . setVisible ( true ) ;
2020-12-14 02:10:27 +00:00
}
else
{
2023-02-17 00:35:44 +00:00
GT . layerVectors . gtflags . setVisible ( false ) ;
2020-10-31 14:01:03 +00:00
}
}
2020-12-14 02:10:27 +00:00
function notifyNoChat ( id )
{
2023-02-17 00:35:44 +00:00
if ( GT . chatWindowHandle != null )
2020-12-14 02:10:27 +00:00
{
try
{
2023-02-17 00:35:44 +00:00
GT . chatWindowHandle . window . notifyNoChat ( id ) ;
2020-12-14 02:10:27 +00:00
}
catch ( e ) { }
2020-10-31 14:01:03 +00:00
}
}
2022-11-27 23:55:03 +00:00
function updateChatWindow ( id = null )
2020-12-14 02:10:27 +00:00
{
2023-02-17 00:35:44 +00:00
if ( GT . chatWindowHandle != null )
2020-12-14 02:10:27 +00:00
{
try
{
2022-11-27 23:55:03 +00:00
if ( id )
{
2023-02-17 00:35:44 +00:00
GT . chatWindowHandle . window . updateCallsign ( id ) ;
2022-11-27 23:55:03 +00:00
}
else
{
2023-02-17 00:35:44 +00:00
GT . chatWindowHandle . window . updateEverything ( ) ;
2022-11-27 23:55:03 +00:00
}
2020-12-14 02:10:27 +00:00
}
catch ( e ) { }
2020-10-31 14:01:03 +00:00
}
}
2020-12-14 02:10:27 +00:00
function newChatMessage ( id , jsmesg )
{
2020-10-31 14:01:03 +00:00
var hasFocus = false ;
2023-02-17 00:35:44 +00:00
if ( GT . msgSettings . msgActionSelect == 1 ) showMessaging ( ) ;
2020-08-19 16:55:41 +00:00
2023-02-17 00:35:44 +00:00
if ( GT . chatWindowHandle != null )
2020-12-14 02:10:27 +00:00
{
try
{
2023-02-17 00:35:44 +00:00
hasFocus = GT . chatWindowHandle . window . newChatMessage ( id , jsmesg ) ;
GT . chatWindowHandle . window . messagesRedraw ( ) ;
2020-12-14 02:10:27 +00:00
}
catch ( e ) { }
2020-10-31 14:01:03 +00:00
}
return hasFocus ;
2020-08-19 16:55:41 +00:00
}
2023-02-17 00:35:44 +00:00
GT . lastChatMsgAlert = 0 ;
2020-10-31 14:01:03 +00:00
2020-12-14 02:10:27 +00:00
function alertChatMessage ( )
{
2023-02-17 00:35:44 +00:00
if ( GT . msgSettings . msgAlertSelect == 1 )
2020-12-14 02:10:27 +00:00
{
2020-12-14 01:30:10 +00:00
// Text to speech
2023-02-17 00:35:44 +00:00
speakAlertString ( GT . msgSettings . msgAlertWord ) ;
2020-12-14 01:30:10 +00:00
}
2023-02-17 00:35:44 +00:00
if ( GT . msgSettings . msgAlertSelect == 2 )
2020-12-14 02:10:27 +00:00
{
2020-12-14 01:30:10 +00:00
// Audible
2023-02-17 00:35:44 +00:00
playAlertMediaFile ( GT . msgSettings . msgAlertMedia ) ;
2020-10-31 14:01:03 +00:00
}
2023-02-17 00:35:44 +00:00
GT . lastChatMsgAlert = timeNowSec ( ) ;
2023-08-28 20:52:43 +00:00
}