;; canon: bot.git src/bot/fleet/bootstrap.cljs (ported from src/fleet/bootstrap.ts)
;;
;; Fleet bootstrap: befriend the configured sibling bots. Both sides send a
;; request and both auto-accept, so the pairwise mesh converges as soon as two
;; bots are up together (or via their mailbox drops when they never overlap).
;; Idempotent by construction — the friend store only upgrades state — with a
;; gentle retry for requests lost to a mailbox 429 on a cold start.
(ns __NAME__.fleet.bootstrap
(:require ["ardegazu-peer-kit" :refer (parseFriendLink)]))
(def ^:private RETRY-MS (* 10 60000))
(def ^:private RESEND-MS (* 60 60000))
(defn parse-siblings
"Parse + verify the configured fragments (skips self and junk, with a log).
Resolves to a js array of #js {:pub :x :xs :name}."
[^js fragments my-pub log]
(let [out #js []]
(-> (.reduce fragments
(fn [p frag]
(.then ^js p
(fn [_]
(-> (parseFriendLink (.replace ^js frag #"^https://[^#]*#" "#"))
(.then (fn [^js f]
(cond
(not (some? f))
(log (str "fleet: unparseable sibling link ("
(.slice ^js frag 0 24) "…)"))
(identical? (.-pub f) my-pub) nil
:else (.push out f))))))))
(js/Promise.resolve nil))
(.then (fn [_] out)))))
(defn start-fleet-bootstrap
"Start the befriending loop; returns a stop function."
[^js social ^js siblings log]
(if (zero? (.-length siblings))
(fn [] nil)
(let [last-sent (js/Map.)
tick (fn []
(.forEach siblings
(fn [^js s]
(let [^js f (.get (.-store social) (.-pub s))]
(when-not (and (some? f) (identical? (.-state f) "friend"))
(let [last (let [v (.get last-sent (.-pub s))] (if (some? v) v 0))]
(when-not (< (- (js/Date.now) last) RESEND-MS)
(.set last-sent (.-pub s) (js/Date.now))
(-> (.sendFriendRequest (.-agent social)
#js {:pub (.-pub s) :x (.-x s)
:xs (.-xs s) :name (.-name s)}
"fleet")
(.then (fn [_] (log (str "fleet: friend request → " (.-name s)))))
(.catch (fn [^js err]
(log (str "fleet: request to " (.-name s)
" failed: " (.-message err)))))))))))))]
(tick)
(let [timer (js/setInterval tick RETRY-MS)]
(when (some? (.-unref ^js timer)) (.unref ^js timer))
(fn [] (js/clearInterval timer))))))