on master help sign in
Improve type safety and simplify server.knot: convert PollEvent.eventType to DU, remove redundant isGossipOffline helper, simplify LookupPresence server selection and trustedOffline derivation
6b1d74aiko
client/App.fs+17−14
@@ -136,6 +136,7 @@ module App =
136 let private truncKey (hex: string) = hexToOb hex |> truncOb
136 let private truncKey (hex: string) = hexToOb hex |> truncOb
137
137
138 let private tryParsePubkey (input: string) =
138 let private tryParsePubkey (input: string) =
139+ let input = input.Trim()
139 (Phonemic.fromOb input |> Option.orElseWith (fun () -> Crypto.tryFromHex input))
140 (Phonemic.fromOb input |> Option.orElseWith (fun () -> Crypto.tryFromHex input))
140 |> Option.filter (fun bytes -> bytes.Length = 32)
141 |> Option.filter (fun bytes -> bytes.Length = 32)
141
142
@@ -375,14 +376,16 @@ module App =
375 asyncCmd (async {
376 asyncCmd (async {
376 try
377 try
377 let! response = poll session.Url session.Token cursor
378 let! response = poll session.Url session.Token cursor
379+ let ackIds = response.Events |> Array.map _.Id |> Array.toList
380+
378− let messages, ackIds, errors =
381+ let messages, errors =
379 Array.foldBack
382 Array.foldBack
380− (fun (evt: PollEvent) (msgs, acks, errs) ->
383+ (fun (evt: PollEvent) (msgs, errs) ->
381 match decryptEvent session.Identity.PrivKey evt.Payload with
384 match decryptEvent session.Identity.PrivKey evt.Payload with
382− | Ok msg -> (msg :: msgs, evt.Id :: acks, errs)
385+ | Ok msg -> (msg :: msgs, errs)
383− | Error err -> (msgs, acks, err :: errs))
386+ | Error err -> (msgs, err :: errs))
384 response.Events
387 response.Events
385− ([], [], [])
388+ ([], [])
386
389
387 if not ackIds.IsEmpty then
390 if not ackIds.IsEmpty then
388 do! ackMessages session.Url session.Token ackIds
391 do! ackMessages session.Url session.Token ackIds
@@ -565,15 +568,15 @@ module App =
565 .font(size = 11.)
568 .font(size = 11.)
566 | None -> ()
569 | None -> ()
567
570
568− let allMessages =
571+ if msgs.IsEmpty then
569− msgs
572+ Label("No messages yet")
570− |> List.map (fun m ->
573+ .padding(8.)
574+ .font(size = 14.)
575+ else
576+ for m in msgs do
571− if m.IsOutgoing then $"You: {m.Body}" else m.Body)
577+ Label(if m.IsOutgoing then $"You: {m.Body}" else m.Body)
572− |> String.concat "\n"
573−
574− Label(if allMessages = "" then "No messages yet" else allMessages)
575− .padding(8.)
578+ .padding(8.)
576− .font(size = 14.)
579+ .font(size = 14.)
577
580
578 HStack(spacing = 8.) {
581 HStack(spacing = 8.) {
579 Entry(model.Compose, SetCompose)
582 Entry(model.Compose, SetCompose)
client/Phonemic.fs+4−12
@@ -82,18 +82,10 @@ module Phonemic =
82 suffixes |> Array.mapi (fun i s -> s, byte i) |> Map.ofArray
82 suffixes |> Array.mapi (fun i s -> s, byte i) |> Map.ofArray
83
83
84 let toOb (bytes: byte[]) : string =
84 let toOb (bytes: byte[]) : string =
85− let sb = System.Text.StringBuilder()
85+ bytes
86−
86+ |> Array.chunkBySize 2
87− for i in 0..2 .. bytes.Length - 1 do
88− let pair = i / 2
89−
90− if pair > 0 then
91− sb.Append('-') |> ignore
92−
93− sb.Append(prefixes.[int bytes.[i]]).Append(suffixes.[int bytes.[i + 1]])
87+ |> Array.map (fun pair -> $"{prefixes.[int pair.[0]]}{suffixes.[int pair.[1]]}")
94− |> ignore
88+ |> String.concat "-"
95−
96− sb.ToString()
97
89
98 let fromOb (s: string) : byte[] option =
90 let fromOb (s: string) : byte[] option =
99 let pairs = s.Split('-', StringSplitOptions.RemoveEmptyEntries)
91 let pairs = s.Split('-', StringSplitOptions.RemoveEmptyEntries)
client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfo.fs+1−1
@@ -10,7 +10,7 @@ open System.Reflection
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
13−[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+e7abeea926015538a30f3346744ca1635404a646")>]
13+[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+32ef8d27d94c098194e967c81ad2bc33c2c7e1a4")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfoInputs.cache+1−1
@@ -1 +1 @@
1−ac592265f098d871e6f04a2ad8e3355d036bd09f4c75fa6fc8a66820739b5515
1+421534857e1be1cf4a3871eb3b32dc004774e6483af04a426ef4c607c9a30ce5
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.sourcelink.json+1−1
@@ -1 +1 @@
1−{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/e7abeea926015538a30f3346744ca1635404a646/*"}}
1+{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/32ef8d27d94c098194e967c81ad2bc33c2c7e1a4/*"}}
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/ref/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/refint/Skrepka.dll

Binary file not shown.

server.knot+19−11
@@ -48,7 +48,15 @@ federationEnabled = federationSecret != ""
48-- Types
48-- Types
49-- ============================================
49-- ============================================
50
50
51+type PollEventType = PollMessage {}
52+
53+impl ToJSON PollEventType where
54+ toJson _ = toJson "message"
55+
56+impl FromJSON PollEventType where
57+ parseJson _ = PollMessage {}
58+
51−type PollEvent = {id: MessageIdHex, eventType: Text, payload: {encryptedBlob: BlobHex, fromKey: PubkeyHex, timestamp: Timestamp, toKey: PubkeyHex}}
59+type PollEvent = {id: MessageIdHex, eventType: PollEventType, payload: {encryptedBlob: BlobHex, fromKey: PubkeyHex, timestamp: Timestamp, toKey: PubkeyHex}}
52
60
53type GossipEventType = GossipOnline {} | GossipOffline {}
61type GossipEventType = GossipOnline {} | GossipOffline {}
54
62
@@ -63,7 +71,6 @@ impl FromJSON GossipEventType where
63 if s == "online" then GossipOnline {} else GossipOffline {}
71 if s == "online" then GossipOnline {} else GossipOffline {}
64
72
65isGossipOnline = \et -> case et of GossipOnline {} -> True {}; _ -> False {}
73isGossipOnline = \et -> case et of GossipOnline {} -> True {}; _ -> False {}
66−isGossipOffline = \et -> case et of GossipOffline {} -> True {}; _ -> False {}
67
74
68type GossipEvent = {eventType: GossipEventType, pubkey: PubkeyHex, server: ServerName, timestamp: Timestamp, ttl: Int<S>}
75type GossipEvent = {eventType: GossipEventType, pubkey: PubkeyHex, server: ServerName, timestamp: Timestamp, ttl: Int<S>}
69
76
@@ -715,7 +722,7 @@ pollOnce = \token cursor -> do
715 let limitedMsgs = boundedSmallest matchingMsgs maxPollEvents (\m -> m.receivedAt)
722 let limitedMsgs = boundedSmallest matchingMsgs maxPollEvents (\m -> m.receivedAt)
716 let evts = do
723 let evts = do
717 m <- limitedMsgs
724 m <- limitedMsgs
718− yield {id: m.messageId, eventType: "message",
725+ yield {id: m.messageId, eventType: PollMessage {},
719 payload: {toKey: m.toKey, fromKey: m.fromKey,
726 payload: {toKey: m.toKey, fromKey: m.fromKey,
720 encryptedBlob: m.encryptedBlob,
727 encryptedBlob: m.encryptedBlob,
721 timestamp: m.timestamp}}
728 timestamp: m.timestamp}}
@@ -906,11 +913,12 @@ serve = \req -> case req of
906 p <- presence
913 p <- presence
907 where p.pubkey == pubkey && p.expiresAt > t
914 where p.pubkey == pubkey && p.expiresAt > t
908 yield p
915 yield p
909− let foundServer = (fold (\acc p ->
916+ let localPresent = any valid (\p -> p.server == serverHostN)
910− if acc.server == serverHostN then acc
917+ let bestRemote = (fold (\acc p ->
911− else if p.server == serverHostN then {server: serverHostN, best: p.expiresAt}
918+ if p.server != serverHostN && p.expiresAt > acc.best
912− else if p.expiresAt > acc.best then {server: p.server, best: p.expiresAt}
919+ then {server: p.server, best: p.expiresAt}
913− else acc) {server: "", best: 0} valid).server
920+ else acc) {server: "", best: 0} valid).server
921+ let foundServer = if localPresent then serverHostN else bestRemote
914 yield {pr: pr, authorized: True {}, foundServer: foundServer}
922 yield {pr: pr, authorized: True {}, foundServer: foundServer}
915 forkBroadcastOffline t result.pr
923 forkBroadcastOffline t result.pr
916 yield (respond {online: result.foundServer != "", server: result.foundServer, authorized: result.authorized})
924 yield (respond {online: result.foundServer != "", server: result.foundServer, authorized: result.authorized})
@@ -926,7 +934,7 @@ serve = \req -> case req of
926 else do
934 else do
927 let allValidEntries = validGossipEvents events t
935 let allValidEntries = validGossipEvents events t
928 let onlineEntries = do e <- allValidEntries; where isGossipOnline e.eventType; yield e
936 let onlineEntries = do e <- allValidEntries; where isGossipOnline e.eventType; yield e
929− let offlineEntries = do e <- allValidEntries; where isGossipOffline e.eventType; yield e
937+ let offlineEntries = do e <- allValidEntries; where not (isGossipOnline e.eventType); yield e
930 let filteredOnline = do
938 let filteredOnline = do
931 e <- onlineEntries
939 e <- onlineEntries
932 where none offlineEntries (\o -> o.server == fromServer && samePubkeyServer o e && o.timestamp > e.timestamp)
940 where none offlineEntries (\o -> o.server == fromServer && samePubkeyServer o e && o.timestamp > e.timestamp)
@@ -936,8 +944,8 @@ serve = \req -> case req of
936 where none onlineEntries (\o -> samePubkeyServer o e && o.timestamp > e.timestamp)
944 where none onlineEntries (\o -> samePubkeyServer o e && o.timestamp > e.timestamp)
937 yield e
945 yield e
938 let trustedOffline = do
946 let trustedOffline = do
939− e <- offlineEntries
947+ e <- allValidEntries
940− where e.server == fromServer
948+ where not (isGossipOnline e.eventType) && e.server == fromServer
941 yield e
949 yield e
942 let deduped = mergeByPubkeyServer [] filteredOnline
950 let deduped = mergeByPubkeyServer [] filteredOnline
943 gossipResult <- atomic do
951 gossipResult <- atomic do