:: :: :: :::: /app/hall/hoon :: :: :: :: :: :: ::TODO document gate samples fully. :: ::TODO for using moons as tmp identities for friends: stories may want to keep :: lists of moons (or just ships in general?) that we define as "standalone" :: so that the "convert to true identity" doesn't happen for them. :: /- hall-sur=hall :: structures /+ hall-lib=hall, hall-legacy :: libraries /= seed /~ !>(.) /= filter-gram /^ $-({telegram:hall-sur bowl:gall} telegram:hall-sur) /| /: /%/filter /!noun/ /~ |=({t/telegram:hall-sur bowl:gall} t) == /= custom-rules /^ (map knot $-({ship ?($r %w) bowl:gall} ?)) /: /%/rules /_ /!noun/ :: :::: =, hall-sur =, hall-lib => :: # :: # %arch :: # :: data structures :: |% :: # %state :: state data structures ++ state :: application state $: stories/(map name story) :: conversations outbox/(map serial tracking) :: sent messages log/(map name @ud) :: logged to clay nicks/(map ship nick) :: local nicknames binds/(jug char audience) :: circle glyph lookup public/(set circle) :: publicly member of rir/wire :: current rumor wire == :: ++ story :: wire content $: count/@ud :: (lent grams) grams/(list telegram) :: all messages known/(map serial @ud) :: messages heard sourced/(map circle (list @ud)) :: heard from sequence/(map circle @ud) :: last-heard p circle locals/group :: local status remotes/(map circle group) :: remote status shape/config :: configuration mirrors/(map circle config) :: remote config peers/(jar ship query) :: subscribers inherited/_| :: from parent? == :: :: # %deltas :: changes to state ++ delta :: $% :: public state :: {$public add/? cir/circle} :: show/hide membership :: messaging state :: {$out cir/circle out/(list thought)} :: send msgs to circle $: $done :: set delivery state cir/circle :: ses/(list serial) :: res/delivery :: == :: :: shared ui state :: {$glyph diff-glyph} :: un/bound glyph {$nick diff-nick} :: changed nickname :: story state :: {$story nom/name det/delta-story} :: change to story :: side-effects :: {$init ~} :: initialize {$observe who/ship} :: watch burden bearer $: $present :: send %present cmd hos/ship :: nos/(set name) :: dif/diff-status :: == :: == :: ++ delta-story :: story delta $% {$inherited ihr/?} :: inherited flag {$follow sub/? srs/(set source)} :: un/subscribe {$sequent cir/circle num/@ud} :: update last-heard {$gram src/circle gam/telegram} :: new/changed msgs {$sourced src/circle num/@ud} :: new heard-from diff-story :: both in & outward == :: :: # %out :: outgoing data ++ move (pair bone card) :: all actions ++ lime :: diff fruit $% {$hall-prize prize} :: {$hall-rumor rumor} :: == :: ++ pear :: poke fruit $% {$hall-command command} :: {$hall-action action} ::TODO see ++gentle-quit == :: ++ card :: general card $% {$diff lime} :: {$info wire ship term nori:clay} :: {$peer wire dock path} :: {$poke wire dock pear} :: {$pull wire dock ~} :: {$quit ~} :: == :: ++ weir :: parsed wire $% {$repeat cir/circle ses/(list serial)} :: messaging wire {$circle nom/name src/source} :: subscription wire == :: -- :: :: # :: # %work :: # :: functional cores and arms. :: ~% %hall-door ..^^^is ~ |_ {bol/bowl:gall $1 state} :: :: # %transition :: prep transition :: ++ prep :: adapts state. :: => |% ++ states $%({$1 s/state} {$0 s/state-0}) :: ++ state-0 (cork state |=(a/state a(stories (~(run by stories.a) story-0)))) ++ story-0 %+ cork story |= a/story %= a shape *config-0 mirrors (~(run by mirrors.a) config-0) peers (~(run by peers.a) |=(a/(list query) (turn a query-0))) == ++ query-0 $% $: $circle nom/name wer/(unit circle) wat/(set circle-data) ran/range-0 == query == ++ config-0 {src/(set source-0) cap/cord tag/tags fit/filter con/control} ++ source-0 {cir/circle ran/range-0} ++ range-0 %- unit $: hed/place-0 tal/(unit place-0) == ++ place-0 $% {$da @da} {$ud @ud} {$sd @sd} == -- =| mos/(list move) |= old/(unit states) ^- (quip move _..prep) ?~ old %- pre-bake ta-done:ta-init:ta ?- -.u.old $1 [mos ..prep(+<+ u.old)] :: $0 =- $(old `[%1 s.u.old(stories -)]) |^ %- ~(run by stories.s.u.old) |= soy/story-0 ^- story %= soy shape (prep-config shape.soy) mirrors (~(run by mirrors.soy) prep-config) peers %- ~(run by peers.soy) |= a/(list query-0) ^- (list query) (murn a prep-query) == :: ++ prep-config |= cof/config-0 ^- config =. src.cof %- ~(gas in *(set source)) (murn ~(tap in src.cof) prep-source) :* src.cof cap.cof tag.cof fit.cof con.cof 0 == :: ++ prep-source |= src/source-0 ^- (unit source) =+ nan=(prep-range ran.src) ?~ nan ~& [%forgetting-source src] ~ `src(ran u.nan) :: ++ prep-query |= que/query-0 ^- (unit query) ?. ?=($circle -.que) `que =+ nan=(prep-range ran.que) ?~ nan ~& [%forgetting-query que] ~ `que(ran u.nan) :: ++ prep-range |= ran/range-0 ^- (unit range) ?~ ran `ran :: ranges with a relative end aren't stored because they end :: immediately, so if we find one we can safely discard it. ?: ?=({$~ {$sd @sd}} tal.u.ran) ~ :: we replace relative range starts with the current date. :: this is practically correct. ?: ?=({$sd @sd} hed.u.ran) `ran(hed.u [%da now.bol]) `ran -- == :: :: # %engines :: main cores. :: ++ ta :: thinker core, used for processing pokes into deltas. :: ~/ %hall-ta |_ :: deltas: deltas created by core operations. :: deltas/(list delta) :: # %resolve :: ++ ta-done :: resolve core :: :: produces the moves stored in ++ta's moves. :: they are produced in reverse order because :: ++ta-emil and ++ta-emit add them to the head of :: the {moves}. :: :: we don't produce any new state, because ++ta :: doesn't make any changes to it itself. :: ^- (list delta) (flop deltas) :: :: # :: # %emitters :: # :: arms that create outward changes. :: ++ ta-delta :: adds a delta to the head of {deltas}. :: ~/ %hall-ta-delta |= det/delta %_(+> deltas [det deltas]) :: ++ ta-deltas :: adds multiple deltas to the head of {deltas}. :: :: flops to stay consistent with ++ta-delta. :: ~/ %hall-ta-deltas |= des/(list delta) %_(+> deltas (welp (flop des) deltas)) :: ++ ta-speak :: sends {sep} as an %app message to the user's inbox. :: ~/ %hall-ta-speak |= sep/speech %+ ta-action %phrase :- [[our.bol %inbox] ~ ~] [%app dap.bol sep]~ :: ++ ta-grieve :: sends a stack trace to the user's inbox. :: ~/ %hall-ta-grieve |= {msg/tape fal/tang} %^ ta-speak %fat [%name 'stack trace' %tank fal] [%lin | (crip msg)] :: ++ ta-note :: sends {msg} to the user's inbox. :: ~/ %hall-ta-note |= msg/tape (ta-speak %lin | (crip msg)) :: ++ ta-evil :: tracing printf and crash. :: ~/ %hall-ta-evil |= msg/cord ~| [%hall-ta-evil msg] !! :: :: # :: # %data :: # :: utility functions for data retrieval. :: ++ ta-know :: story monad :: :: produces a gill that takes a gate. if the story :: {nom} exists, calls the gate with a story core. :: if it doesn't, does nothing. :: ~/ %hall-ta-know |= nom/name |: fun=^|(|:(so ta)) ^+ +>+> =+ pur=(~(get by stories) nom) ?~ pur %- ta-evil (crip "no story '{(trip nom)}'") (fun ~(. so nom ~ u.pur)) :: :: # :: # %interaction-events :: # :: arms that apply events we received. :: ++ ta-init :: initialize app :: :: populate state on first boot. creates our default mailbox and journal. :: :: create default circles. => %+ roll ^- (list {security name cord}) :~ [%mailbox %inbox 'default home'] [%journal %public 'visible activity'] [%mailbox %i 'dm invites'] == |: [[typ=*security nom=*name des=*cord] ta] (ta-action [%create nom des typ]) %- ta-deltas :: if needed, subscribe to our parent's /burden. =+ sen=(above [our now our]:bol) ?: ?| !=(%czar (clan:title sen)) =(sen our.bol) =(%pawn (clan:title our.bol)) == ~ [%init ~]~ :: ++ ta-apply :: applies the command sent by {src}. :: ~/ %hall-ta-apply |= {src/ship cod/command} ^+ +> ?- -.cod :: %publish commands prompt us (as a circle host) :: to verify and distribute messages. $publish (ta-think | src +.cod) :: %present commands are used to ask us to set :: someone's status in the indicated stories. $present (ta-present src +.cod) :: %bearing commands are used by our children to :: let us know they're bearing our /burden. we :: need to watch them to allow changes to go up. $bearing (ta-observe src) ::TODO isn't this redundant with ta-subscribe? == :: ++ ta-present :: update a status :: :: sets status for the indicated stories, :: but only if they have write permission there. :: ~/ %hall-ta-present |= {who/ship nos/(set name) dif/diff-status} ^+ +> =+ nol=~(tap in nos) |- ?~ nol +>.^$ =. +>.^$ ?. (~(has by stories) i.nol) +>.^$ =+ soy=(~(got by stories) i.nol) so-done:(~(so-present so i.nol ~ soy) who dif) $(nol t.nol) :: ++ ta-action :: performs action sent by a client. :: ~/ %hall-ta-action |= act/action ^+ +> =< work :: # :: # %actions :: # :: action processing core :: :: ++work calls the appropriate action processing :: arm. most use ++affect to retrieve the affected :: story, crashing if it doesn't exist. ~% %hall-ta-action-inner + ~ |% :: # %utility ++ work :: perform action ^+ ..ta-action ?- -.act :: circle configuration $create (action-create +.act) $design (action-design +.act) $source (action-source +.act) $depict (action-depict +.act) $filter (action-filter +.act) $permit (action-permit +.act) $delete (action-delete +.act) $usage (action-usage +.act) $read (action-read +.act) $newdm (action-newdm +.act) :: messaging $convey (action-convey +.act) $phrase (action-phrase +.act) :: personal metadata $notify (action-notify +.act) $naming (action-naming +.act) :: changing shared ui $glyph (action-glyph +.act) $nick (action-nick +.act) :: misc changes $public (action-public +.act) == :: ++ affect :: delta to story :: :: store a delta about a story. if the story :: does not exist, crash. :: ~/ %hall-affect |= {nom/name det/delta-story} ?: (~(has by stories) nom) (impact nom det) (ta-evil (crip "no story {(trip nom)}")) :: ++ impact :: delta for story :: :: Store a delta about a story. :: ~/ %hall-impact |= {nom/name det/delta-story} (ta-delta %story nom det) :: ++ present :: send status update :: ~/ %hall-present |= {aud/audience dif/diff-status} ^+ ..ta-action =/ cic ^- (jug ship name) %- ~(rep in aud) |= {c/circle m/(jug ship name)} (~(put ju m) hos.c nom.c) =? ..ta-action (~(has by cic) our.bol) =+ nos=~(tap in (~(get ju cic) our.bol)) (ta-present our.bol (~(get ju cic) our.bol) dif) =. cic (~(del by cic) our.bol) %- ta-deltas %- ~(rep by cic) |= {{h/ship s/(set name)} l/(list delta)} :_ l [%present h s dif] :: :: # %circle-configuration ++ action-create :: creates a story with the specified parameters. :: ~/ %hall-action-create |= {nom/name des/cord typ/security} ^+ ..ta-action ?. (~(has in stories) nom) %^ impact nom %new :* [[[our.bol nom] ~] ~ ~] des ~ *filter :- typ ?. ?=(?($village $journal) typ) ~ [our.bol ~ ~] 0 == (ta-evil (crip "{(trip nom)}: already exists")) :: ++ action-design :: creates a story with the specified config. :: ~/ %hall-action-design |= {nom/name cof/config} ?. (~(has in stories) nom) (impact nom %new cof) (ta-evil (crip "{(trip nom)}: already exists")) :: ++ action-delete :: delete + announce :: :: delete story {nom}, optionally announcing the :: event with message {mes}. :: ~/ %hall-action-delete |= {nom/name mes/(unit cord)} ^+ ..ta-action =? ..ta-action ?=(^ mes) %+ action-phrase [[our.bol nom] ~ ~] [%lin | u.mes]~ (affect nom %remove ~) :: ++ action-depict :: change description of story {nom} to {des}. :: ~/ %hall-depict |= {nom/name cap/cord} (affect nom %config [our.bol nom] %caption cap) :: ++ action-filter :: change message rules :: :: replaces the story's current filter with the :: specified one. :: ~/ %hall-action-filter |= {nom/name fit/filter} (affect nom %config [our.bol nom] %filter fit) :: ++ action-permit :: invite to/banish from story {nom} all {sis}. :: ~/ %hall-action-permit |= {nom/name inv/? sis/(set ship)} =+ soy=(~(get by stories) nom) ?~ soy (ta-evil (crip "no story {(trip nom)}")) so-done:(~(so-permit so nom ~ u.soy) inv sis) :: ++ action-source :: add/remove {pos} as sources for story {nom}. :: ~/ %hall-action-source |= {nom/name sub/? srs/(set source)} =+ soy=(~(get by stories) nom) ?~ soy (ta-evil (crip "no story {(trip nom)}")) so-done:(~(so-sources so nom ~ u.soy) sub srs) :: ++ action-usage :: add or remove usage tags. :: ~/ %hall-action-usage |= {nom/name add/? tas/tags} =+ soy=(~(get by stories) nom) ?~ soy (ta-evil (crip "no story {(trip nom)}")) so-done:(~(so-usage so nom ~ u.soy) add tas) :: ++ action-read :: set the read message number :: ~/ %hall-action-read |= {nom/name red/@ud} =+ soy=(~(get by stories) nom) ?~ soy (ta-evil (crip "no story {(trip nom)}")) so-done:(~(so-read so nom ~ u.soy) red) :: ++ action-newdm :: copy all behavior of create, permit, and source in that order :: ~/ %hall-action-newdm |= sis/(set ship) :: generate circle name from sis as a dot seperated list of ship names :: in alphabetical order :: =/ nom/name %^ rsh 3 1 %+ roll %+ sort %+ turn ~(tap in (~(put in sis) our.bol)) |= p/ship ^- cord (scot %p p) aor |= {p/cord nam/name} ^- @tas (crip "{(trip `@t`nam)}.{(slag 1 (trip p))}") :: if we've already created this circle, no-op :: ?: (~(has in ~(key by stories)) nom) (ta-deltas ~) :: check if we already have an invite to this dm group :: or if we are creating it :: =/ inv=(list telegram) %+ skim grams:(~(got by stories) %i) |= g=telegram ^- ? ?. ?=({$inv *} sep.g) %.n ?. =(nom nom.cir.sep.g) %.n ?. (~(has in sis) aut.g) %.n %.y :: =. inv %+ sort inv |= {a/telegram b/telegram} (lte wen.a wen.b) :: create our side of the dm and source it to our inbox :: =/ dels/(list delta) :~ :* %story %inbox %follow & [[[our.bol nom] ~] ~ ~] == :* %story nom %new [[[our.bol nom] ~] ~ ~] 'dm' ~ *filter [%village (~(put in sis) our.bol)] 0 == == :: if we did initiate the dm, send out invites :: ?: ?=(~ inv) =. ..ta-action (ta-deltas dels) %- action-convey ^- (list thought) %+ turn ~(tap in (~(del in sis) our.bol)) |= a=ship ^- thought :* uid=(shaf a eny.bol) aud=(sy [a %i] ~) wen=now.bol sep=[%inv & [our.bol nom]] == :: if we did not initiate the dm, source to the initiators copy :: =. dels :_ dels :* %story nom %follow & [[[aut.i.inv nom] ~] ~ ~] == (ta-deltas dels) :: :: # %messaging ++ action-convey :: post exact :: :: sends the messages provided in the action. :: ~/ %hall-action-convey |= tos/(list thought) (ta-think & our.bol tos) :: ++ action-phrase :: post easy :: :: sends the message contents provided in the :: action generating a serial and setting a :: timestamp. :: ~/ %hall-action-phrase |= {aud/audience ses/(list speech)} ^+ ..ta-action =- (ta-think & our.bol tos) |- ^- tos/(list thought) ?~ ses ~ =^ sir eny.bol ~(uniq hall-lib bol) :_ $(ses t.ses) [sir aud [now.bol i.ses]] :: :: # %personal-metadata :: ++ action-notify :: our presence update :: :: notify the audience of our new presence state, :: or tell them to remove us if {pes} is ~. :: ~/ %hall-action-notify |= {aud/audience pes/(unit presence)} ^+ ..ta-action ?~ pes (present aud %remove ~) (present aud %presence u.pes) :: ++ action-naming :: our name update :: ~/ %hall-action-naming |= {aud/audience man/human} ^+ ..ta-action (present aud %human %full man) :: :: # %changing-shared-ui ++ action-nick :: new identity :: :: assigns a new local identity ("nickname") to the :: target ship. :: ~/ %hall-action-nick |= {who/ship nic/nick} ^+ ..ta-action ?. =((~(get by nicks) who) `nic) ..ta-action :: no change (ta-delta %nick who nic) :: ++ action-glyph :: un/bind glyph {lif} to audience {aud}. :: ~/ %hall-action-glyph |= {lif/char aud/audience bin/?} (ta-delta %glyph bin lif aud) :: ++ action-public :: show/hide membership :: :: add or remove a circle from the public membership list. :: ~/ %hall-action-public |= {add/? cir/circle} (ta-delta %public add cir) -- :: :: # :: # %subscription-events :: # :: arms that react to subscription events. :: ++ ta-observe :: watch burden bearer :: :: subscribe to a child who is bearing our burden. ::TODO everyone should be able to bear if they so desire. :: ~/ %hall-ta-observe |= who/ship ^+ +> ?. =(our.bol (above our.bol now.bol who)) ~&([%not-our-bearer who] +>) (ta-delta %observe who) :: ++ ta-subscribe :: listen to :: :: reaction to incoming subscriptions. :: ~/ %hall-ta-subscribe |= {her/ship qer/query} ^+ +> ?+ -.qer +> $burden (ta-observe her) $circle %+ ta-delta %story [nom.qer %peer & her qer] == :: ++ ta-greet :: subscription success :: :: store a started subscription as source. :: ~/ %hall-ta-greet |= {nom/name src/source} %- (ta-know nom) |= sor/_so =< so-done (so-greet:sor src) :: ++ ta-leave :: subscription failed :: :: removes {src} from story {nom}'s sources. :: ~/ %hall-ta-leave |= {nom/name src/source} %- (ta-know nom) |= sor/_so =< so-done (so-leave:sor src) :: ++ ta-take :: apply prize :: :: for a %burden prize, bear the burden in a new :: or existing story. :: for a %circle prize, use ++so to accept it. :: for a %report prize, silently ignore. :: ~/ %hall-ta-take |= {wir/wire piz/prize} ^+ +> ?+ -.piz ~&([%ignoring-prize -.piz] +>) $report +> :: $burden =+ sos=~(tap by sos.piz) |- ^+ ..ta-take ?~ sos ..ta-take =. ..ta-take =+ (fall (~(get by stories) p.i.sos) *story) => (~(so-bear so p.i.sos ~ -) q.i.sos) =. acs (flop acs) |- ^+ ..ta-take ?~ acs ..ta-take =. ..ta-take (ta-action i.acs) $(acs t.acs) $(sos t.sos) ::TODO runtime error ::%+ roll ~(tap by sos.piz) ::|= {{n/name b/burden} _..ta-take} ::=+ (fall (~(get by stories) n) *story) ::so-done:(~(so-bear so n ~ -) b) :: $circle =+ wer=(etch wir) ?> ?=($circle -.wer) %- (ta-know nom.wer) |= sor/_so =< so-done (so-take:sor cir.src.wer +.piz) == :: ++ ta-hear :: apply rumor :: :: apply changes from a rumor to our state. :: for %burden, authoratively apply the story :: diff. if it's a new one, bear it. :: for %circle, apply the story diff normally. :: ~/ %hall-ta-hear |= {wir/wire rum/rumor} ^+ +> ?+ -.rum ~&([%ignoring-rumor -.rum] +>) :: $burden ?+ -.rum.rum %- (ta-know nom.rum) |= sor/_so =< so-done (so-hear:sor & [our.bol nom.rum] rum.rum) :: $new =? +> !(~(has by stories) nom.rum) (ta-delta %story +.rum) => =+ (fall (~(get by stories) nom.rum) *story) %- ~(so-bear so nom.rum ~ -) [~ [cof.rum.rum ~] [~ ~]] =. acs (flop acs) |- ^+ +>+ ?~ acs +>+ =. +>+ (ta-action i.acs) $(acs t.acs) ::TODO runtime error ::=< so-done ::%- ~(so-bear so nom.rum ~ (fall (~(get by stories) nom.rum) *story)) ::[~ [cof.rum.rum ~] [~ ~]] == :: $circle =+ wer=(etch wir) ?> ?=($circle -.wer) %- (ta-know nom.wer) |= sor/_so =< so-done (so-hear:sor | cir.src.wer rum.rum) == :: ++ ta-repeat :: message delivered :: :: message got delivered. if an error was returned :: mark the message as rejected. if not, received. :: ~/ %hall-ta-repeat |= {who/circle ses/(list serial) fal/(unit tang)} ^+ +> ?~ fal (ta-delta %done who ses %accepted) =. +> (ta-delta %done who ses %rejected) =- (ta-grieve - u.fal) %+ weld "{(scow %ud (lent ses))} message(s) " "rejected by {(scow %p hos.who)}/{(trip nom.who)}" :: ++ ta-resub :: subscription dropped :: :: when a subscription gets dropped by gall, we :: resubscribe. :: ~/ %hall-ta-resub |= {nom/name src/source} ^+ +> %- (ta-know nom) |= sor/_so =< so-done (so-resub:sor src) :: :: # :: # %messaging :: # :: arms for sending and processing messages. :: ++ ta-think :: publish or review :: :: consumes each thought. :: ~/ %hall-ta-think |= {pub/? aut/ship tos/(list thought)} ^+ +> ?~ tos +> $(tos t.tos, +> (ta-consume pub aut i.tos)) :: ++ ta-consume :: to each audience :: :: conducts thought {tot} to each circle in its audience. :: ~/ %hall-ta-consume |= {pub/? aut/ship tot/thought} =+ aud=~(tap in aud.tot) |- ^+ +>.^$ ?~ aud +>.^$ $(aud t.aud, +>.^$ (ta-conduct pub aut i.aud tot)) :: ++ ta-conduct :: thought to circle :: :: either publishes or records a thought. :: ~/ %hall-ta-conduct |= {pub/? aut/ship cir/circle tot/thought} ^+ +> ?: pub ?. (team:title our.bol aut) (ta-note "strange author {(scow %p aut)}") =. aut our.bol ?: =(aut hos.cir) ?: (~(has by stories) nom.cir) (ta-record nom.cir hos.cir tot) ::TODO avenue for abuse? (ta-note "have no story {(scow %tas nom.cir)}") (ta-transmit cir tot) ?. =(our.bol hos.cir) +> (ta-record nom.cir aut tot) :: ++ ta-record :: add to story :: :: add or update telegram {gam} in story {nom}. :: ~/ %hall-ta-record |= {nom/name gam/telegram} %- (ta-know nom) |= sor/_so =< so-done (so-learn:sor [our.bol nom] gam) :: ++ ta-transmit :: sends thought {tot} to {cir}. :: ~/ %hall-ta-transmit |= {cir/circle tot/thought} ^+ +> (ta-delta %out cir tot ~) :: :: # :: # %stories :: # :: arms for modifying stories. :: ++ so :: story core, used for doing work on a story. :: ~/ %hall-so |_ :: nom: story name in {stories}. :: acs: hall actions issued due to changes. :: story is faceless to ease data access. :: $: nom/name acs/(list action) story == :: :: # %resolve ++ so-done :: apply actions generated by story operations. ::TODO maybe produce list of actions, apply in ++ta :: ^+ +> =. acs (flop acs) |- ^+ +>+ ?~ acs +>+ =. +>+ (ta-action i.acs) $(acs t.acs) :: :: # :: # %emitters :: # :: arms that create outward changes. :: ++ so-act :: stores a hall action. :: ~/ %hall-so-act |= act/action ^+ +> +>(acs [act acs]) :: ++ so-note :: sends {msg} as an %app message to the user's inbox. :: ~/ %hall-so-note |= msg/cord ^+ +> %+ so-act %phrase :- [[our.bol %inbox] ~ ~] [%app dap.bol %lin | msg]~ :: ++ so-delta :: store delta in ++ta core. :: ~/ %hall-so-delta |= det/delta ^+ +> +>(deltas [det deltas]) :: ++ so-deltas :: store multiple deltas in ++ta core. :: ~/ %hall-so-deltas |= des/(list delta) %_(+> deltas (welp (flop des) deltas)) :: ++ so-delta-our :: adds a delta about this story. :: ~/ %hall-so-delta-our |= det/delta-story ^+ +> (so-delta %story nom det) :: ++ so-deltas-our :: adds multiple deltas about this story. :: ~/ %hall-so-deltas-our |= des/(list delta-story) ^+ +> %- so-deltas %+ turn des |= d/delta-story [%story nom d] :: :: # :: # %data :: # :: utility functions for data retrieval. :: ++ so-cir [our.bol nom] :: us as circle :: :: # :: # %interaction-events :: # :: arms that apply events we received. :: ++ so-take :: apply the prize as if it were rumors. :: ~/ %hall-so-take |= {src/circle nes/(list envelope) cos/lobby pes/crowd} ^+ +> =. +>.$ (so-hear | src %config src %full loc.cos) =. +>.$ =+ los=~(tap by loc.pes) |- ?~ los +>.^$ =. +>.^$ (so-hear | src %status src p.i.los %full q.i.los) $(los t.los) ::TODO ideally you'd just do this, but that runtime errors... ::%- ~(rep in loc.pes) ::|= {{w/ship s/status} _+>.$} ::(so-hear | src %status src w %full s) (so-unpack src nes) :: ++ so-hear :: apply changes from a rumor to this story. :: ~/ %hall-so-hear |= {bur/? src/circle rum/rumor-story} ::TODO tall-form gate comments like this for everything? ::|= $: :: bur: whether the story is inherited :: :: src: story to change :: :: rum: change to this story :: :: :: bur/? :: src/circle :: rum/rumor-story :: == ^+ +> ?- -.rum $bear (so-bear bur.rum) $peer (so-delta-our rum) $gram (so-open src nev.rum) $remove ::TODO should also remove from {remotes}? (so-delta-our %config src %remove ~) :: $new ?: =(src so-cir) (so-config-full ~ cof.rum) $(rum [%config src %full cof.rum]) :: $config :: we only subscribe to remotes' configs. ?. =(src cir.rum) ~! %unexpected-remote-config-from-remote !! =/ old/(unit config) ?: =(cir.rum so-cir) `shape (~(get by mirrors) cir.rum) :: ignore if it won't result in change. ?. ?| &(?=($remove -.dif.rum) ?=(^ old)) ?=(~ old) !=(u.old (change-config u.old dif.rum)) == +>.$ :: full changes to us need to get split up. ?: &(=(cir.rum so-cir) ?=($full -.dif.rum)) (so-config-full `shape cof.dif.rum) (so-delta-our rum) :: $status :: we only subscribe to remotes' locals. ?. |(=(src cir.rum) =(src so-cir)) ~! %unexpected-remote-status-from-remote !! =/ old/(unit status) ?: =(cir.rum so-cir) (~(get by locals) who.rum) =- (~(get by -) who.rum) (fall (~(get by remotes) cir.rum) *group) :: ignore if it won't result in change. ?. ?| &(?=($remove -.dif.rum) ?=(^ old)) ?=(~ old) !=(u.old (change-status u.old dif.rum)) == +>.$ (so-delta-our rum) == :: ++ so-bear :: accept burden :: :: add what was pushed down from above to our :: state. in case of conflict, existing data is :: overwritten. :: ::NOTE we don't use ++roll here because of urbit/arvo#447. :: ~/ %hall-so-bear |= {gaz/(list telegram) cos/lobby pes/crowd} ^+ +> =* self +> :: :: local config =. self (so-config-full `shape loc.cos) :: :: remote config =. self =+ rem=~(tap by rem.cos) |- ^+ self ?~ rem self =* wer p.i.rem =* cof q.i.rem :: only make a delta if it actually changed. =? self !=(`cof (~(get by mirrors) wer)) (so-delta-our %config wer %full cof) $(rem t.rem) :: :: local status =. self =+ sas=~(tap by loc.pes) |- ^+ self ?~ sas self =* who p.i.sas =* sat q.i.sas :: only make a delta if it actually changed. =? deltas !=(`sat (~(get by locals) who)) :_ deltas :^ %story nom %status [[our.bol nom] who %full sat] $(sas t.sas) :: :: remote status =. self =+ rem=~(tap by rem.pes) |- ^+ self ?~ rem self =* wer p.i.rem =* gou q.i.rem :: only make deltas if it actually changed. =? deltas !=(`gou (~(get by remotes) wer)) %+ welp deltas =+ gop=~(tap by gou) =+ hav=(fall (~(get by remotes) wer) *group) =| l/(list delta) |- ^+ l ?~ gop l =* who p.i.gop =* sat q.i.gop :: only make a delta if it actually changed. =? l !=(`sat (~(get by hav) who)) [[%story nom %status wer who %full sat] l] $(gop t.gop) $(rem t.rem) :: :: telegrams =. self %_ self deltas %+ welp deltas %- flop ^- (list delta) %+ murn gaz |= t/telegram ^- (unit delta) :: in audience, replace above with us. ::TODO this really should be done by the sender. =. aud.t =+ dem=[(above [our now our]:bol) nom] ?. (~(has in aud.t) dem) aud.t =+ (~(del in aud.t) dem) (~(put in -) so-cir) =+ num=(~(get by known) uid.t) ?: &(?=(^ num) =(t (snag u.num grams))) ~ ::TODO this really should have sent us the message :: src as well but that's not an easy fix. `[%story nom %gram [(above [our now our]:bol) nom] t] == :: inherited flag %_(self deltas [[%story nom %inherited &] deltas]) ::TODO runtime error ::(so-delta-our %inherited &) :: :: # :: # %changes :: # :: arms that make miscellaneous changes to this story. :: ++ so-present :: accept status diff ~/ %hall-so-present |= {who/ship dif/diff-status} ^+ +> :: only have presence if you have write permission. ?. |((so-admire who) ?=($remove -.dif)) +> :: ignore if it won't result in change, :: or if it sets an impersonating handle. ?. ?: ?=($remove -.dif) (~(has by locals) who) ?| !(~(has by locals) who) :: =+ old=(~(got by locals) who) =+ new=(change-status - dif) ?& !=(old new) :: ?= ~ (rush (fall han.man.new '') ;~(pfix sig fed:ag)) ::TODO calling with %+ gives syntax error == == +> (so-delta-our %status so-cir who dif) :: ++ so-config-full :: split full config :: :: split a %full config delta up into multiple :: smaller ones, for easier application. :: ~/ %hall-so-config-full |= {old/(unit config) cof/config} ^+ +> ~? &(?=(^ old) !=(src.u.old src.cof)) %maybe-missing-src-changes %- so-deltas =- %+ turn - |= d/diff-config [%story nom [%config so-cir d]] ^- (list diff-config) ::TODO figure out how to deal with src changes here. :: %follow will probably behave a bit iffy in some cases. ?~ old :: if we have no previous config, all diffs apply. :~ [%caption cap.cof] [%usage & tag.cof] [%filter fit.cof] [%secure sec.con.cof] [%permit & sis.con.cof] [%read red.cof] == =- (murn - same) ^- (list (unit diff-config)) =* col u.old :: if we have previous config, figure out the changes. :~ ?: =(cap.col cap.cof) ~ `[%caption cap.cof] :: =+ gon=(~(dif in tag.col) tag.cof) ?~ gon ~ `[%usage | gon] :: =+ new=(~(dif in tag.cof) tag.col) ?~ new ~ `[%usage & new] :: ?: =(fit.col fit.cof) ~ `[%filter fit.cof] :: ?: =(sec.con.col sec.con.cof) ~ `[%secure sec.con.cof] :: =+ gon=(~(dif in sis.con.col) sis.con.cof) ?~ gon ~ `[%permit | gon] :: =+ new=(~(dif in sis.con.cof) sis.con.col) ?~ new ~ `[%permit & new] == :: ++ so-sources :: change source :: :: adds or removes {srs} from our sources, :: skipping over ones we already (don't) have. :: ~/ %hall-so-sources |= {add/? srs/(set source)} ^+ +> =/ sus/(set source) %. src.shape ?:(add ~(dif in srs) ~(int in srs)) :: we only make a delta for removals here, :: because we don't need to wait for ++reap when :: pulling subscriptions. =? +>.$ !add =+ sos=~(tap in sus) |- ^+ +>.^$ ?~ sos +>.^$ =. +>.^$ (so-delta-our %config so-cir %source | i.sos) $(sos t.sos) ::TODO ideally below, but unexplained runtime error at `so-delta-our` ::%+ roll ~(tap in sus) ::|= {src/source _+>.$} ::^+ +>.^$ ::(so-delta-our %config so-cir %source | src) ?~ sus +>.$ (so-delta-our %follow add sus) :: ++ so-depict :: change description :: :: modifies our caption. :: ~/ %hall-so-depict |= cap/cord ^+ +> ?: =(cap cap.shape) +> (so-delta-our %config so-cir %caption cap) :: ++ so-usage :: add or remove usage tags. :: ~/ %hall-so-usage |= {add/? tas/tags} ^+ +> =/ sas/tags %. tag.shape ?:(add ~(dif in tas) ~(int in tas)) ?~ sas +>.$ (so-delta-our %config so-cir %usage add sas) :: ++ so-read :: set the read message number in circle config ~/ %hall-so-read |= {red/@ud} ^+ +> ?: =(red red.shape) +> (so-delta-our %config so-cir %read red) :: ++ so-filter :: change message rules :: :: modifies our filter. :: ~/ %hall-so-filter |= fit/filter ^+ +> ?: =(fit fit.shape) +> (so-delta-our %config so-cir %filter fit) :: ++ so-delete :: delete story :: :: deletes this story. removes it from {stories} :: and unsubscribes from all src. :: (so-delta-our %remove ~) :: :: # :: # %subscriptions :: # :: arms for starting and ending subscriptions :: ++ so-greet :: subscription started :: :: store a started subscription as source. :: ~/ %hall-so-greet |= src/source ^+ +> ?: (~(has in src.shape) src) +> (so-delta-our %config so-cir %source & src) :: ++ so-leave :: subscription ended :: :: delete {src} from our sources. :: ~/ %hall-so-leave |= src/source ^+ +> ?. (~(has in src.shape) src) +> (so-delta-our %config so-cir %source | src) :: ++ so-resub :: subscription revived :: :: re-subscribe to a dropped subscription. :: if it was already active, we continue where :: we left off. :: ~/ %hall-so-resub |= src/source ^+ +> =+ seq=(~(get by sequence) cir.src) =/ ner/range ?~ seq ran.src =- `[[%ud u.seq] -] ?~ ran.src ~ tal.u.ran.src :: if our subscription changes or ends, remove :: the original source. =? +>.$ !=(ner ran.src) (so-delta-our %config so-cir %source | src) :: if we're past the range, don't resubscribe. ?: ?& ?=(^ ran.src) ?=(^ tal.u.ran.src) :: ?- -.u.tal.u.ran.src $sd & $da (gte now.bol +.u.tal.u.ran.src) $ud ?& ?=(^ seq) (gte u.seq +.u.tal.u.ran.src) == == == +>.$ (so-delta-our %follow & [[cir.src -] ~ ~]) :: ++ so-first-grams :: beginning of stream :: :: find all grams that fall within the range. :: ~/ %hall-so-first-grams |= ran/range ^- (list telegram) =+ [num=0 gaz=grams zeg=*(list telegram)] :: fill in empty ranges to select all grams, :: and calculate absolutes for relative places. =. ran ?~ ran `[[%ud 0] `[%ud count]] =* hed hed.u.ran =? hed ?=($sd -.hed) [%ud (sub count (min count (abs:si +.hed)))] ?~ tal.u.ran `[hed `[%ud count]] =* tal u.tal.u.ran =? tal ?=($sd -.tal) [%ud (sub count (min count (abs:si +.tal)))] ran :: never fails, but compiler needs it. ?> &(?=(^ ran) ?=(^ tal.u.ran)) =* hed hed.u.ran =* tal u.tal.u.ran %- flop |- ^- (list telegram) ?~ gaz zeg ?: ?- -.u.tal.u.ran :: after the end $ud (lth +.u.tal.u.ran num) $da (lth +.u.tal.u.ran wen.i.gaz) $sd !! :: caught above == :: if past the range, we're done searching. zeg ?: ?- -.hed.u.ran :: before the start $ud (lth num +.hed.u.ran) $da (lth wen.i.gaz +.hed.u.ran) $sd !! :: caught above == :: if before the range, continue onward. $(num +(num), gaz t.gaz) :: if in the range, add this gram and continue. $(num +(num), gaz t.gaz, zeg [i.gaz zeg]) :: ++ so-in-range :: place in range? :: :: produces two booleans: whether we're :: currently in the range, and whether the range :: has passed. ::TODO to deal with changed messages, we'll want :: to be able to pass in a num. :: ~/ %hall-so-in-range |= ran/range ^- {in/? done/?} ?~ ran [& |] =/ min ?- -.hed.u.ran $sd & :: relative is always in. $ud (gth count +.hed.u.ran) $da (gth now.bol +.hed.u.ran) == ?~ tal.u.ran [min |] =- [&(min -) !-] ?- -.u.tal.u.ran $sd | :: relative is always done. $ud (gte +(+.u.tal.u.ran) count) $da (gte +.u.tal.u.ran now.bol) == :: :: # :: # %messaging :: # :: arms for adding to this story's messages. :: ++ so-sane :: sanitize %lin speech according to our settings. :: ~/ %hall-so-sane |= sep/speech ^- speech ?+ -.sep sep ?($ire $fat $app) sep(sep $(sep sep.sep)) :: $lin =- sep(msg -) %- crip %- tufa %+ turn (tuba (trip msg.sep)) |= a/@c :: always replace control characters. ?: |((lth a 32) =(a `@c`127)) `@`'?' :: if desired, remove uppercasing. ?: ?& !cas.fit.shape (gte a 'A') (lte a 'Z') == (add a 32) :: if desired, replace non-ascii characters. ?: ?& !utf.fit.shape (gth a 127) == `@`'?' a == :: ++ so-unpack :: process envelopes :: :: learn telegrams from list of envelopes and :: update the sequence of the source if needed. :: ~/ %hall-so-unpack |= {src/circle nes/(list envelope)} ^+ +> =. +> (so-lesson src (turn nes tail)) =/ num %+ roll nes |= {nev/envelope max/@ud} ?:((gth num.nev max) num.nev max) ?. (gth num (fall (~(get by sequence) src) 0)) +>.$ (so-delta-our %sequent src num) :: ++ so-open :: process envelope :: :: learn telegram from envelope and update the :: sequence of the source if needed. :: ~/ %hall-so-open |= {src/circle nev/envelope} ^+ +> =. +> (so-learn src gam.nev) ?. (gth num.nev (fall (~(get by sequence) src) 0)) +> (so-delta-our %sequent src num.nev) :: ++ so-lesson :: learn all telegrams in a list. :: ~/ %hall-so-lesson |= {src/circle gaz/(list telegram)} ^+ +> ?~ gaz +> $(gaz t.gaz, +> (so-learn src i.gaz)) :: ++ so-learn :: save/update message :: :: store an incoming telegram, updating if it :: already exists. :: ~/ %hall-so-learn |= {src/circle gam/telegram} ^+ +> :: check for write permissions. ::TODO we want to !! instead of silently failing, :: so that ++coup-repeat of the caller gets :: an error. but the caller may not be the :: author. if we check for that to be true, :: can we guarantee it's not an older message :: getting resent? does that matter? think. ?. (so-admire aut.gam) +> :: clean up the message to conform to our rules. =. sep.gam (so-sane sep.gam) :: =. gam (filter-gram gam bol) :: if we already have it, ignore. =+ old=(~(get by known) uid.gam) ?. &(?=(^ old) =(gam (snag u.old grams))) (so-delta-our %gram src gam) =+ sed=(~(get by sourced) src) ?: |(?=(~ sed) ?=(~ (find [u.old]~ u.sed))) (so-delta-our %sourced src u.old) +>.$ :: :: # :: # %permissions :: # :: arms relating to story permissions. :: ++ so-permit :: invite/banish :: :: update config to dis/allow ships permission. :: ~/ %hall-so-permit |= {inv/? sis/(set ship)} ^+ +> :: wyt: whitelist? :: add: add to list? =/ wyt/? ?=(?($village $journal) sec.con.shape) =/ add/? =(inv wyt) =/ sus/(set ship) %. sis.con.shape ?:(add ~(dif in sis) ~(int in sis)) ?~ sus +>.$ :: if banished, remove their presences. =? +>.$ !inv %- so-deltas-our %+ turn ~(tap in `(set ship)`sus) |= s/ship :+ %status so-cir [s %remove ~] (so-delta-our %config so-cir %permit [add sus]) :: ++ so-admire :: accept from :: :: checks {her} write permissions. :: ~/ %hall-so-admire |= her/ship ^- ? ?- sec.con.shape $channel !(~(has in sis.con.shape) her) :: blacklist $village (~(has in sis.con.shape) her) :: whitelist $journal (~(has in sis.con.shape) her) :: author whitelist $mailbox !(~(has in sis.con.shape) her) :: author blacklist $custom :: custom rule =/ rul/$-({ship ?($r $w) bowl:gall} ?) (fall (~(get by custom-rules) nom) |=(* |)) (rul her %w bol) == :: ++ so-visible :: checks {her} read permissions. :: ~/ %hall-so-visible |= her/ship ^- ? ?- sec.con.shape $channel !(~(has in sis.con.shape) her) :: blacklist $village (~(has in sis.con.shape) her) :: whitelist $journal & :: all $mailbox (team:title our.bol her) :: our team $custom :: custom rule =/ rul/$-({ship ?($r $w) bowl:gall} ?) (fall (~(get by custom-rules) nom) |=(* |)) (rul her %r bol) == -- -- :: ++ da :: delta application :: :: core for doing things, mostly applying deltas to :: application state, but also dealing with events :: that aren't pokes. :: where appropriate, creates moves. those get :: produced when finalizing with ++da-done. :: ~/ %hall-da |_ :: moves: moves created by core operations. :: moves/(list move) :: # %resolve :: ++ da-done :: resolve core :: :: produces the moves stored in ++da's moves. :: they are produced in reverse order because :: ++da-emil and ++da-emit add them to the head of :: the {moves}. :: ^- (quip move _+>) [(flop moves) +>] :: :: # :: # %emitters :: # :: arms that create outward changes. :: ++ da-emil :: emit move list :: :: adds multiple moves to the head of {moves}. :: flops to stay consistent with ++ta-emit. :: ~/ %hall-da-emil |= mol/(list move) %_(+> moves (welp (flop mol) moves)) :: ++ da-emit :: emit a move :: :: adds a move to the head of {moves}. :: ~/ %hall-da-emit |= mov/move %_(+> moves [mov moves]) :: ++ da-present :: send %present cmd :: ~/ %hall-da-present |= {hos/ship nos/(set name) dif/diff-status} ^+ +> %- da-emit :* ost.bol %poke /present [hos dap.bol] [%hall-command %present nos dif] == :: :: # :: # %change-application :: # :: arms that change the application state. :: ++ da-change :: apply delta :: :: modifies application state according to the :: change specified in {dif}. :: ~/ %hall-da-change |= det/delta ^+ +> ?- -.det $public (da-change-public +.det) $out (da-change-out +.det) $done (da-change-done +.det) $glyph (da-change-glyph +.det) $nick (da-change-nick +.det) $story (da-change-story +.det) $init da-init $observe (da-observe +.det) $present (da-present +.det) == :: ++ da-init :: startup side-effects :: :: apply %init delta, querying the /burden of the :: ship above us. :: (da-emit (wire-to-peer /burden)) :: ++ da-observe :: watch burden bearer :: :: apply %observe delta, querying the /report of :: {who} below us. :: ~/ %hall-da-observe |= who/ship (da-emit (wire-to-peer /report/(scot %p who))) :: ++ da-change-public :: show/hide membership :: :: add/remove a circle to/from the public :: membership list. :: ~/ %hall-da-change-public |= {add/? cir/circle} ^+ +> =- +>.$(public -) ?: add (~(put in public) cir) (~(del in public) cir) :: ++ da-change-out :: outgoing messages :: :: apply an %out delta, sending a message. :: ~/ %hall-da-change-out |= {cir/circle out/(list thought)} ^+ +> =+ ses=(turn out head) =. outbox :: for every serial, add %pending state. %+ roll ses |= {s/serial o/_outbox} =? o ?=(~ o) outbox =+ t=(fall (~(get by o) s) *tracking) %+ ~(put by o) s (~(put by t) cir %pending) %+ da-emit ost.bol :* %poke /repeat/(scot %p hos.cir)/[nom.cir]/(scot %ud (jam ses)) [hos.cir dap.bol] [%hall-command %publish out] == :: ++ da-change-done :: delivered messages :: :: apply a %done delta, setting new delivery state :: for messages. :: ~/ %hall-da-change-done |= {cir/circle ses/(list serial) res/delivery} ^+ +> %_ +> outbox :: for every serial, set new delivery state. %- ~(gas by outbox) %+ turn ses |= s/serial :- s %+ ~(put by (~(got by outbox) s)) cir res == :: ++ da-change-glyph :: un/bound glyph :: :: apply a %glyph delta, un/binding a glyph to/from :: an audience. :: ~/ %hall-da-change-glyph |= {bin/? gyf/char aud/audience} ^+ +> ?: bin %_ +> binds (~(put ju binds) gyf aud) == =/ ole/(list audience) ?. =(aud ~) [aud ~] ~(tap in (~(get ju binds) gyf)) |- ^+ +>.^$ ?~ ole +>.^$ %_ $ binds (~(del ju binds) gyf i.ole) ole t.ole == :: ++ da-change-nick :: changed nickname :: :: apply a %nick delta, setting a nickname for a :: ship. :: ~/ %hall-da-change-nick |= {who/ship nic/nick} ^+ +> +>(nicks (change-nicks nicks who nic)) :: :: # :: # %stories :: # :: arms for modifying stories. :: ++ da-change-story :: apply circle delta :: :: apply a %story delta, redirecting the delta :: itself to ++sa-change. :: in case of a new or deleted story, specialized :: arms are called. :: ~/ %hall-da-change-story |= {nom/name det/delta-story} ^+ +> ?+ -.det =< sa-done %. det =+ (fall (~(get by stories) nom) *story) ~(sa-change sa nom -) :: $new (da-create nom +.det) $bear ~&(%unexpected-unsplit-bear +>) $remove (da-delete nom) == :: ++ da-create :: configure story :: :: creates story {nom} with config {con}. :: ~/ %hall-da-create |= {nom/name cof/config} ^+ +> =< sa-done %- ~(sa-change sa nom *story) [%config [our.bol nom] %full cof] :: ++ da-delete :: delete story :: :: calls the story core to delete story {nom}. :: ~/ %hall-da-delete |= nom/name ^+ +> =. +> %- da-emil ~(sa-delete sa nom (~(got by stories) nom)) +>(stories (~(del by stories) nom)) :: ++ sa :: story delta core :: :: story core, used for doing work on a story. :: ~/ %hall-sa |_ :: nom: story name in {stories}. :: story is faceless to ease data access. :: $: nom/name story == :: # %resolve :: ++ sa-done :: apply changes :: :: put changed story back into the map. :: +>(stories (~(put by stories) nom +<+)) :: :: # :: # %emitters :: # :: arms that create outward changes. :: ++ sa-emil :: emit move list :: :: adds multiple moves to the head of {moves}. :: flops to stay consistent with ++ta-emit. :: ~/ %hall-sa-emil |= mol/(list move) %_(+> moves (welp (flop mol) moves)) :: ++ sa-emit :: emit a move :: :: adds a move to the head of {moves}. :: ~/ %hall-sa-emit |= mov/move %_(+> moves [mov moves]) :: ++ sa-sauce :: cards to moves. :: ~/ %hall-sa-sauce |= {ost/bone cub/(list card)} ^- (list move) (flop (turn cub |=(a/card [ost a]))) :: :: # :: # %data :: # :: utility functions for data retrieval. :: ++ sa-cir [our.bol nom] :: :: # :: # %delta-application :: # :: arms for applying deltas. :: ++ sa-delete :: deletion of story :: :: apply a %remove story delta, unsubscribing :: this story from all its active sources. :: %+ weld (sa-abjure src.shape) (sa-eject ~(key by peers)) :: ++ sa-change :: apply circle delta :: :: figure out whether to apply a %story delta to :: local or remote data. :: ~/ %hall-sa-change |= det/delta-story ^+ +> %. det ?: ?& ?=(?($config $status) -.det) !=(cir.det sa-cir) == sa-change-remote sa-change-local :: ++ sa-change-local :: apply our delta :: :: apply a %story delta to local data. :: ~/ %hall-sa-change-local |= det/delta-story ^+ +> ?+ -.det ~&([%unexpected-delta-local -.det] !!) :: $inherited +>(inherited ihr.det) :: $peer ?: add.det +>(peers (~(add ja peers) who.det qer.det)) =+ qes=(~(get ja peers) who.det) =. qes =+ res=(find ~[qer.det] qes) ?~ res qes (oust [u.res 1] qes) ?~ qes +>.$(peers (~(del by peers) who.det)) +>.$(peers (~(put in peers) who.det qes)) :: $follow (sa-emil (sa-follow-effects sub.det srs.det)) :: $sequent +>(sequence (~(put by sequence) cir.det num.det)) :: $gram (sa-change-gram +.det) :: $sourced (sa-add-gram-source +.det) :: $config =. +> %- sa-emil (sa-config-effects shape dif.det) +>(shape (change-config shape dif.det)) :: $status %_ +> locals ?: ?=($remove -.dif.det) (~(del by locals) who.det) %+ ~(put by locals) who.det %+ change-status (fall (~(get by locals) who.det) *status) dif.det == == :: ++ sa-add-gram-source :: remember message source :: :: if it's not already known, make note of the :: fact that message {num} was heard from {src}. :: ~/ %hall-sa-add-gram-source |= {src/circle num/@ud} ^+ +> =- +>.$(sourced -) =+ sed=(fall (~(get by sourced) src) ~) ?^ (find ~[num] sed) sourced (~(put by sourced) src [num sed]) :: ++ sa-change-gram :: save/update message :: :: apply a %gram delta, either appending or :: updating a message. :: ~/ %hall-sa-change-gram |= {src/circle gam/telegram} ^+ +> ::TODO move "known" logic up into ++so? that way, :: we can attach message numbers to changes. =+ old=(~(get by known) uid.gam) ?~ old :: new message %. [src count] %_ sa-add-gram-source grams (welp grams [gam ~]) count +(count) known (~(put by known) uid.gam count) == :: changed message %. [src u.old] %_ sa-add-gram-source grams %+ welp (scag u.old grams) [gam (slag +(u.old) grams)] == :: ++ sa-change-remote :: apply remote's delta :: :: apply a story diff to remote data. :: ~/ %hall-sa-change-remote |= det/delta-story ^+ +> ?+ -.det ~&([%unexpected-delta-remote -.det] !!) :: $config ?: ?=($remove -.dif.det) +>(mirrors (~(del by mirrors) cir.det)) =/ new/config %+ change-config (fall (~(get by mirrors) cir.det) *config) dif.det +>.$(mirrors (~(put by mirrors) cir.det new)) :: $status %_ +>.$ remotes %+ ~(put by remotes) cir.det =+ ole=(fall (~(get by remotes) cir.det) *group) ?: ?=($remove -.dif.det) (~(del by ole) who.det) =+ old=(fall (~(get by ole) who.det) *status) (~(put by ole) who.det (change-status old dif.det)) == == :: ++ sa-config-effects :: apply side-effects for a %config delta. :: ~/ %hall-sa-change-effects |= {old/config dif/diff-config} ^- (list move) ?+ -.dif ~ $permit (sa-permit-effects sec.con.old sis.con.old +.dif) ::NOTE when doing a lone %secure, calculate the :: necessary %permit deltas alongside it. == :: ++ sa-follow-effects :: un/subscribe :: :: apply side-effects for a %follow delta, :: un/subscribing this story to/from {cos}. :: ~/ %hall-sa-follow-effects |= {sub/? srs/(set source)} ^- (list move) %. srs ?:(sub sa-acquire sa-abjure) :: ++ sa-permit-effects :: notify permitted :: :: apply side-effects for a %permit delta, :: kicking the subscriptions of {sis} if they :: are being banished. :: ~/ %hall-sa-permit-effects |= {sec/security old/(set ship) add/? sis/(set ship)} ^- (list move) =/ wyt ?=(?($village $journal) sec) =/ inv =(wyt add) ?: inv ~ =/ sus/(set ship) %. sis.con.shape ?:(add ~(dif in sis) ~(int in sis)) (sa-eject sus) :: :: # :: # %subscriptions :: # :: arms for starting and ending subscriptions :: ++ sa-acquire :: subscribes this story to each circle. :: ~/ %hall-sa-aquire |= srs/(set source) =- (murn - same) %+ turn ~(tap in srs) |= {cir/circle ran/range} ^- (unit move) ?: =(cir sa-cir) ~ :: ignore self-subs =+ wat=~[%grams %config-l %group-l] `(wire-to-peer (circle-wire nom wat cir ran)) :: ++ sa-abjure :: unsubscribes this story from each circle. :: ~/ %hall-sa-abjure |= srs/(set source) ^- (list move) %+ turn ~(tap in srs) |= {cir/circle ran/range} ^- move =/ wir %^ circle-wire nom ~[%grams %config-l %group-l] [cir ran] [ost.bol %pull wir [hos.cir dap.bol] ~] :: ++ sa-eject :: removes ships {sis} from {followers}. :: ~/ %hall-sa-eject |= sis/(set ship) ^- (list move) %- zing %+ turn ~(tap in sup.bol) |= {b/bone s/ship p/path} ^- (list move) ?. ?& (~(has in sis) s) ?=({$circle @tas *} p) =(i.t.p nom) == ~ (gentle-quit b s (path-to-query p)) :: ++ sa-unearth :: ships' bones :: :: find the bones in {sup.bol} that belong to :: a ship in {sis}. :: ~/ %hall-sa-unearth |= sis/(set ship) ^- (set bone) %- ~(rep in sup.bol) |= {{b/bone s/ship p/path} c/(set bone)} ?. ?& (~(has in sis) s) ?=({$circle @tas *} p) =(i.t.p nom) == c (~(put in c) b) -- -- :: :: :: # :: # %wire-utility :: # :: ++ circle-wire :: /circle peer wire :: :: constructs a /circle %peer path for subscribing :: {nom} to a source. :: ~/ %hall-circle-wire |= {nom/name wat/(list circle-data) source} ^- wire ;: weld /circle/[nom]/(scot %p hos.cir)/[nom.cir] (sort wat gth) :: consistence (range-to-path ran) == :: ++ wire-to-peer :: peer move from wire :: :: builds the peer move associated with the wire. :: ~/ %hall-wire-to-peer |= wir/wire ^- move =+ tar=(wire-to-target wir) [ost.bol %peer wir [p.tar dap.bol] q.tar] :: ++ wire-to-target :: ship+path from wire :: :: parses {wir} to obtain the target ship and the :: query path. :: ~/ %hall-wire-to-target |= wir/wire ^- (pair ship path) ?+ wir ~&(wir !!) {$circle @ @ *} :- (slav %p i.t.t.wir) (welp /circle t.t.t.wir) :: {$burden *} :- (above [our now our]:bol) /burden/(scot %p our.bol) :: {$report @ *} :- (slav %p i.t.wir) /report == :: ++ etch :: parse wire :: :: parses {wir} to obtain either %circle with story :: and circle or %repeat with message number, source :: ship, story and serials. :: ~/ %hall-etch |= wir/wire ^- weir ?+ wir !! {$circle @ @ @ *} :: $circle, us, host, target :^ %circle i.t.wir [(slav %p i.t.t.wir) i.t.t.t.wir] (path-to-range t.t.t.t.wir) :: {$repeat @ @ @ ~} :+ %repeat [(slav %p i.t.wir) i.t.t.wir] ((list serial) (cue (slav %ud i.t.t.t.wir))) == :: ++ etch-circle :: parse /circle wire :: :: parses a /circle wire, call a gate with the :: result. :: ~/ %hall-etch-circle |= $: wir/wire $= fun $- {nom/name src/source} {(list move) _.} == =+ wer=(etch wir) ?>(?=($circle -.wer) (fun nom.wer src.wer)) :: ++ etch-repeat :: parses a /repeat wire, call gate with the result. :: ~/ %hall-etch-repeat |= $: wir/wire $= fun $- {cir/circle ses/(list serial)} {(list move) _.} == =+ wer=(etch wir) ?>(?=($repeat -.wer) (fun cir.wer ses.wer)) :: ++ gentle-quit :: quit other, pull us :: :: we want to gently pull our own subscriptions, :: rather than quitting them, so that we may :: differentiate between a gall/ames quit and a :: foreign quit. but since wex.bol isn't filled, :: we'll have to just guess at what the correct wire :: wire is. this is truly terrible, but will have to :: do for now. ::TODO get rid of this once gall improves. :: it needs to tell us the difference between :: an app-caused quit and a queue-caused one. :: (aka connected/disconnected/rejected state) :: ~/ %hall-gentle-quit |= {bon/bone who/ship qer/query} ^- (list move) ?. ?=($circle -.qer) ~ ?. =(who our.bol) [bon %quit ~]~ %- zing %+ turn ~(tap in ~(key by stories)) |= n/name ^- (list move) :~ :^ ost.bol %poke / :+ [our.bol dap.bol] %hall-action :^ %source n | [[[our.bol nom.qer] ran.qer] ~ ~] :: :^ ost.bol %pull %^ circle-wire n ~(tap in wat.qer) [[our.bol nom.qer] ran.qer] [[our.bol dap.bol] ~] == :: :: # :: # %new-events :: # ++ bake :: apply state delta :: :: applies a change to the application state, :: producing side-effects. :: ~/ %hall-bake |= det/delta ^- (quip move _+>) da-done:(da-change:da det) :: ++ pre-bake :: apply more deltas :: ~/ %hall-pre-bake |= des/(list delta) ^- (quip move _+>) =| moz/(list move) |- ^- (quip move _+>.^$) ?~ des [moz +>.^$] =^ mos +>.^$ (bake i.des) $(moz :(welp moz mos (affection i.des)), des t.des) ::TODO ideally you'd just do this, but that runtime errors on "bake"... ::%+ roll des ::|= {d/delta m/(list move) _+>.$} ::=^ mos +>.^$ (bake d) ::[:(welp m mos (affection d)) +>.^$] :: ++ peek ~/ %hall-peek |= pax/path ?> ?=({$x *} pax) :: others unsupported. ^- (unit (unit (pair mark prize))) =+ piz=(look (path-to-query t.pax)) ?~ piz ~ ?~ u.piz [~ ~] ``[%hall-prize u.u.piz] :: ++ look :: query on state :: :: find the result (if any) for a given query. :: ~/ %hall-look |= qer/query ^- (unit (unit prize)) ?- -.qer $client ``[%client binds nicks] :: $circles =- ``[%circles -] %- ~(gas in *(set name)) %+ murn ~(tap by stories) |= {n/name s/story} ^- (unit name) ?:((~(so-visible so:ta n ~ s) who.qer) `n ~) :: $public ``[%public public] :: $burden :+ ~ ~ :- %burden %- ~(gas in *(map name burden)) %+ murn ~(tap by stories) |= {n/name s/story} ^- (unit (pair name burden)) :: only auto-federate channels for now. ?. ?=($channel sec.con.shape.s) ~ :+ ~ n :: share no more than the last 100, for performance reasons. :+ ?: (lte count.s 100) grams.s (slag (sub count.s 100) grams.s) [shape.s mirrors.s] [locals.s remotes.s] :: $report ::TODO gall note: need to be able to subscirbe to just changes... or just :: data etc. ``[%report ~] :: $peers =+ soy=(~(get by stories) nom.qer) ?~ soy ~ ``[%peers peers.u.soy] :: $circle ::REVIEW should we send precs & config to out of range subs? =+ soy=(~(get by stories) nom.qer) ?~ soy ~ :+ ~ ~ :- %circle :+ ?. (~(has in wat.qer) %grams) ~ %+ turn =- (~(so-first-grams so:ta nom.qer ~ -) ran.qer) ::TODO this can be done more efficiently. ?~ wer.qer u.soy =- u.soy(grams -, count (lent -)) ?. (~(has by sourced.u.soy) u.wer.qer) ~ %+ turn %- flop (~(got by sourced.u.soy) u.wer.qer) |= n/@ud (snag n grams.u.soy) (cury gram-to-envelope nom.qer) :- shape.u.soy ?. (~(has in wat.qer) %config-r) ~ mirrors.u.soy :- locals.u.soy ?. (~(has in wat.qer) %group-r) ~ remotes.u.soy == :: ++ dedicate :: rumor-story to theirs :: :: modify a %story diff to make it about their ship :: instead of ours. :: ~/ %hall-dedicate |= {who/ship nom/name det/delta-story} ^- rumor-story ?+ -.det det :: :: internal-only changes. $follow !! $inherited !! $sequent !! $sourced !! :: $gram :+ %gram ?. =(src.det [our.bol nom]) src.det [who nom] %+ gram-to-envelope nom %_ gam.det aud %- ~(run in aud.gam.det) |= c/circle ?. =(c [our.bol nom]) c [who nom] == :: $config ?. =(cir.det [our.bol nom]) det det(cir [who nom]) :: $status ?. =(cir.det [our.bol nom]) det det(cir [who nom]) == :: ++ gram-to-envelope :: wrap gram with nr :: :: deduce the initial msg number from a telegram :: for a given story. assumes both story and :: telegram are known. :: ~/ %hall-gram-to-envelope |= {nom/name gam/telegram} ^- envelope :_ gam %. uid.gam ~(got by known:(~(got by stories) nom)) :: ++ circle-feel-story :: ~/ %hall-circle-feel-story |= $: wer/(unit circle) wat/(set circle-data) nom/name det/delta-story == ^- ? ?& ?~ wer & ?+ -.det & $gram =(src.det u.wer) $config =(cir.det u.wer) $status =(cir.det u.wer) == :: ?: =(wat ~) & %- ~(has in wat) ?+ -.det %hasnot $gram %grams $new %config-l $remove %config-l $config ?: =(cir.det [our.bol nom]) %config-l %config-r $status ?: =(cir.det [our.bol nom]) %group-l %group-r == == :: ++ feel :: delta to rumor :: :: if the given delta changes the result of the given :: query, produce the relevant rumor. :: ~/ %hall-feel |= {qer/query det/delta} ^- (unit rumor) ?- -.qer $client :: changes to shared ui state apply. ?+ -.det ~ $glyph `[%client det] $nick `[%client det] == :: $circles ::NOTE this is another case where having access to :: the pre-delta state would be nice to have. ?. ?=($story -.det) ~ =; add/(unit ?) ?~ add ~ `[%circles u.add nom.det] ::REVIEW this could be considered leaky, since it :: doesn't check if {who} ever knew of {nom}, :: but does that matter? can't really check.. :: if the story got deleted, remove it from the circles listing. ?: ?=($remove -.det.det) `| =+ soy=(~(got by stories) nom.det) :: if the story got created, or something about the read permissions set :: for the subscriber changed, update the circles listing. =; dif/? ?. dif ~ :: if the story just got created, don't send a remove rumor, because it :: never showed up in the first place. =- ?:(&(?=($new -.det.det) !-) ~ `-) ?| (team:title our.bol who.qer) (~(so-visible so:ta nom.det ~ soy) who.qer) == ?| ?=($new -.det.det) :: ?& ?=($config -.det.det) ?=($permit -.dif.det.det) ?=(?($channel $village) sec.con.shape.soy) (~(has in sis.dif.det.det) who.qer) == == :: $public ?. ?=($public -.det) ~ `det :: $burden ?. ?=($story -.det) ~ ?: &(=(who.qer src.bol) =(rir /report/(scot %p src.bol))) ~ ?: ?=(?($follow $inherited $sequent $sourced) -.det.det) ~ :: only burden channels for now. ?. (~(has by stories) nom.det) ~ ?. =(%channel sec.con.shape:(~(got by stories) nom.det)) ~ `[%burden nom.det (dedicate who.qer nom.det det.det)] :: $report :: only send changes we didn't get from above. ?: =(src.bol (above [our now our]:bol)) ~ :: only send story reports about grams and status. ?. ?=($story -.det) ~ ?. ?=(?($gram $status) -.det.det) ~ =+ soy=(~(got by stories) nom.det) :: and only if the story is inherited. ?. inherited.soy ~ :: only burden channels for now. ?. =(%channel sec.con.shape.soy) ~ `[%burden nom.det (dedicate (above [our now our]:bol) nom.det det.det)] :: $peers ?. ?=($story -.det) ~ ?. =(nom.qer nom.det) ~ ?. ?=($peer -.det.det) ~ `[%peers +.det.det] :: $circle ?. ?=($story -.det) ~ ?. =(nom.qer nom.det) ~ ?. %- circle-feel-story [wer.qer wat.qer nom.det det.det] ~ ?. ?| ?=($remove -.det.det) :: =< in %. ran.qer =+ soy=(~(got by stories) nom.qer) ~(so-in-range so:ta nom.qer ~ soy) == ~ ?. ?=(?($gram $new $config $status $remove) -.det.det) ~ :+ ~ %circle ?+ det.det det.det {$gram *} :+ %gram src.det.det (gram-to-envelope nom.det gam.det.det) == == :: ++ affection :: rumors to interested :: :: for a given delta, send rumors to all queries it :: affects. :: ~/ %hall-affection |= det/delta ^- (list move) :: cache results for paths. =| res/(map path (list move)) %- zing %+ turn ~(tap by sup.bol) |= {b/bone s/ship p/path} ^- (list move) =+ mur=(~(get by res) p) ?^ mur u.mur =- =. res (~(put by res) p -) - =+ qer=(path-to-query p) %+ welp =+ rum=(feel qer det) ?~ rum ~ [b %diff %hall-rumor u.rum]~ ?. ?=($circle -.qer) ~ :: kill the subscription if we forgot the story. ?. (~(has by stories) nom.qer) (gentle-quit b s qer) :: kill the subscription if it's past its range. =- ?:(done:- (gentle-quit b s qer) ~) %. ran.qer =- ~(so-in-range so:ta nom.qer ~ -) (~(got by stories) nom.qer) :: ++ path-to-query :: path, coins, query :: :: parse a path into a (list coin), then parse that :: into a query structure. :: ~/ %hall-path-to-query |= pax/path ?. ?=({$circle @tas *} pax) (coins-to-query (path-to-coins pax)) =/ qer/query [%circle i.t.pax ~ ~ ~] ?> ?=($circle -.qer) :: for type system. =+ pax=t.t.pax =+ ^- {qer/query pax/path} ?. ?=({@ @ *} pax) [qer pax] =+ hos=(slaw %p i.pax) ?~ hos [qer pax] :_ t.t.pax qer(wer `[u.hos i.t.pax]) ?> ?=($circle -.qer) |- ^+ qer ?~ pax qer ::TODO can probably do this a bit better... ?+ i.pax qer(ran (path-to-range pax)) :: circle-data %_ $ pax t.pax wat.qer (~(put in wat.qer) i.pax) == $group %_ $ pax t.pax wat.qer %- ~(uni in wat.qer) ^+ wat.qer =/ dat=(list circle-data) [%group-l %group-r ~] (sy dat) == $config %_ $ pax t.pax wat.qer %- ~(uni in wat.qer) ^+ wat.qer =/ dat=(list circle-data) [%config-l %config-r ~] (sy dat) == == :: ++ path-to-coins :: path to coin list :: :: parse a path into a list of coins. :: ~/ %hall-path-to-coins |= pax/path ^- (list coin) %+ turn `path`pax |= a/@ta (need (slay a)) :: ++ coins-to-query :: coin list to query :: :: parse a list of coins into a query structure. :: ^- $-((list coin) query) => depa |^ %- af :~ [%client ul] [%circles (at /[%p])] [%public ul] [%burden (at /[%p])] [%report ul] == ++ term (do %tas) ++ rang (mu (al plac (mu (un plac)))) ++ plac (or %da %ud) -- :: ++ leak :: visible to :: :: determine if the given query is visible to the :: ship. :: ~/ %hall-leak |= {who/ship qer/query} ^- ? ?- -.qer $client (team:title our.bol who) $circles =(who who.qer) $public & $burden ?& =(who who.qer) =(our.bol (above our.bol now.bol who)) == $peers =(who our.bol) ::TODO or so-visible? $report =(who (above [our now our]:bol)) :: $circle ?. (~(has by stories) nom.qer) | %. who ~(so-visible so:ta nom.qer ~ (~(got by stories) nom.qer)) == :: :: # :: # %poke-events :: # :: ++ poke-hall-command :: accept command :: :: incoming hall command. process it and update logs. :: ~/ %hall-poke-hall-command |= cod/command ^- (quip move _+>) =^ mos +>.$ %- pre-bake ta-done:(ta-apply:ta src.bol cod) =^ mow +>.$ log-all-to-file [(welp mos mow) +>.$] :: ++ poke-hall-action :: accept action :: :: incoming hall action. process it. :: ~/ %hall-poke-hall-action |= act/action ^- (quip move _+>) ?. (team:title our.bol src.bol) %- pre-bake =< ta-done %- ta-note:ta "hall-action stranger {(scow %p src.bol)}" =^ mos +>.$ %- pre-bake ta-done:(ta-action:ta act) =^ mow +>.$ log-all-to-file [(welp mos mow) +>.$] :: :: # :: # %subscription-events :: # :: ++ diff-hall-prize :: accept prize :: :: accept a query result. :: ~/ %hall-diff-hall-prize |= {wir/wire piz/prize} ^- (quip move _+>) =^ mos +>.$ %- pre-bake => (ta-take:ta wir piz) (flop deltas) ::TODO ideally this, but runtime error for %burden prize ::%- pre-bake ::ta-done:(ta-take:ta wir piz) =^ mow +>.$ log-all-to-file [(welp mos mow) +>.$] :: ++ diff-hall-rumor :: accept rumor :: :: accept a query result change. :: ~/ %hall-diff-hall-rumor |= {wir/wire rum/rumor} ^- (quip move _+>) ::NOTE to keep us from echoing changes back to their :: sender, we want to know (in ++feel) if a delta :: was caused by a rumor from a /report. :: if gall worked as advertised, we'd use ost.bol :: and wex.bol to find out, but wex is never set, :: so we just keep track of the "current rumor :: wire" instead. =. rir wir =^ mos +>.$ %- pre-bake => (ta-hear:ta wir rum) (flop deltas) ::TODO runtime error for %burden rumors. ::ta-done:(ta-hear:ta wir rum) =^ mow +>.$ log-all-to-file [(welp mos mow) +>.$] :: ++ peer :: accept subscription :: :: incoming subscription on {pax}. :: ~/ %hall-peer |= pax/path ^- (quip move _+>) ?: ?=([%sole *] pax) ~&(%hall-no-sole !!) =+ qer=(path-to-query pax) ?. (leak src.bol qer) ~&(%peer-invisible !!) =^ mos +>.$ %- pre-bake ta-done:(ta-subscribe:ta src.bol qer) :_ +>.$ =+ piz=(look qer) ?~ piz ~&([%query-unavailable pax] mos) ?~ u.piz ~&([%query-invalid pax] mos) :_ mos [ost.bol %diff %hall-prize u.u.piz] :: ++ pull :: unsubscribes. :: ~/ %hall-pull |= pax/path ^- (quip move _+>) [~ +>] :: ++ pull-circle :: someone ends a /circle subscription. :: ~/ %hall-pull-circle |= pax/path ^- (quip move _+>) %- pre-bake =+ qer=(path-to-query %circle pax) ?> ?=($circle -.qer) ?. (~(has by stories) nom.qer) ~ [%story nom.qer %peer | src.bol qer]~ :: ++ reap :: subscription n/ack :: :: update state to reflect subscription success :: ~/ %hall-reap |= {wir/wire fal/(unit tang)} ^- (quip move _+>) %- pre-bake %+ welp ?. ?=({$circle *} wir) ~ =+ wer=(etch wir) ?> ?=($circle -.wer) =< ta-done %. [nom.wer src.wer] ?~ fal ta-greet:ta ta-leave:ta ?~ fal ~ =< ta-done =- (ta-grieve:ta - u.fal) =+ (wire-to-target wir) %+ weld "failed (re)subscribe to {(scow %p p)} on " %+ roll q |= {a/@ta b/tape} :(weld b "/" (trip a)) :: ++ quit :: dropped subscription :: :: gall dropped out subscription. resubscribe. :: ~/ %hall-quit |= wir/wire ^- (quip move _+>) [[(wire-to-peer wir) ~] +>] :: ++ quit-circle :: dropped circle sub :: :: gall dropped our subscription. resubscribe. :: ~/ %hall-quit-circle |= wir/wire ^- (quip move _+>) %+ etch-circle [%circle wir] |= {nom/name src/source} %- pre-bake ta-done:(ta-resub:ta nom src) :: ++ coup-repeat :: message n/ack :: :: ack from ++ta-transmit. mark the message as :: received or rejected. :: ~/ %hall-coup-repeat |= {wir/wire fal/(unit tang)} ^- (quip move _+>) %+ etch-repeat [%repeat wir] |= {cir/circle ses/(list serial)} %- pre-bake ta-done:(ta-repeat:ta cir ses fal) :: :: # :: # %logging :: # :: ++ poke-hall-save :: save as log :: :: stores the telegrams of story {nom} in a log file, :: to be re-loaded by ++poke-hall-load. ::TODO maybe update to also store sourced list. :: ~/ %hall-poke-hall-save |= nom/name ^- (quip move _+>) =/ paf/path /(scot %p our.bol)/home/(scot %da now.bol)/hall/[nom]/hall-telegrams =+ grams:(~(got by stories) nom) :_ +>.$ :_ ~ :* ost.bol %info /jamfile our.bol (foal:space:userlib paf [%hall-telegrams !>(-)]) == :: ++ poke-load-legacy :: loads legacy messages into the story {nom}. :: ~/ %hall-poke-hall-legacy |= nom/name ^- (quip move _+>) =/ jams/json .^ json %cx /(scot %p our.bol)/home/(scot %da now.bol)/hall/legacy-telegrams/json == =+ grams=(from-json:hall-legacy jams) ~& [%loaded (lent grams)] %- pre-bake %+ turn (flop grams) |= t/telegram [%story nom %gram [our.bol nom] t] :: ++ poke-hall-load :: load from log :: :: loads the telegrams of story {nom} into our state, :: as saved in ++poke-hall-save. :: ~/ %hall-poke-hall-load |= nom/name ^- (quip move _+>) =/ grams .^ (list telegram) %cx /(scot %p our.bol)/home/(scot %da now.bol)/hall/[nom]/hall-telegrams == %- pre-bake %+ turn grams |= t/telegram [%story nom %gram [our.bol nom] t] :: ++ poke-hall-log :: starts logging story {nom}'s messages. :: ~/ %hall-poke-hall-log |= nom/name ^- (quip move _+>) :- [(log-to-file nom) ~] %= +>.$ log %+ ~(put by log) nom count:(~(got by stories) nom) == :: ++ poke-hall-unlog :: stops logging story {nom}'s messages. :: ~/ %hall-poke-hall-unlog |= nom/name ^- (quip move _+>) :- ~ +>.$(log (~(del by log) nom)) :: ++ log-all-to-file :: update stories logs :: :: for every story we're logging, (over)writes all :: their grams to log files if new ones have arrived. :: ^- (quip move _.) :_ %_ . log %- ~(urn by log) |= {nom/name len/@ud} count:(~(got by stories) nom) == %+ murn ~(tap by log) |= {nom/name len/@ud} ^- (unit move) ?: (gte len count:(~(got by stories) nom)) ~ `(log-to-file nom) :: ++ log-to-file :: logs all grams of story {nom} to a file. :: ~/ %hall-log-all-to-file |= nom/name ^- move =+ ^- paf/path =+ day=(year %*(. (yore now.bol) +.t +:*tarp)) %+ en-beam:format [our.bol %home da+now.bol] /hall-telegrams/(scot %da day)/[nom]/hall =+ grams:(~(got by stories) nom) :* ost.bol %info /jamfile our.bol (foal:space:userlib paf [%hall-telegrams !>(-)]) == :: ::TODO for debug purposes. remove eventually. :: users beware, here be dragons. ++ poke-noun ~/ %hall-poke-noun |= a/* ^- (quip move _+>) ?: ?=([%kill ship] a) :_ +> %- ~(rep by sup.bol) |= [[b=bone c=(pair ship path)] out=(list move)] ?: =(+.a p.c) [[b %quit ~] out] out ?> ?=(@t a) ?: =(a 'check') ~& 'verifying message reference integrity...' =- ~&(- [~ +>.$]) %- ~(urn by stories) |= {n/name s/story} =+ %- ~(rep by known.s) |= {{u/serial a/@ud} k/@ud m/@ud} :- ?:((gth a k) a k) ?: =(u uid:(snag a grams.s)) m ~? (lth m 3) :* [%fake a u] [%prev uid:(snag (dec a) grams.s)] [%real uid:(snag a grams.s)] [%next uid:(snag +(a) grams.s)] == +(m) :^ count=count.s lent=(lent grams.s) known=k mismatch=m ?: =(a 'check subs') ~& 'here are all incoming non-circle subs' ~& ^- (list (pair ship path)) %+ murn ~(tap by sup.bol) |= {b/bone s/ship p/path} ^- (unit (pair ship path)) ?: ?=({$circle *} p) ~ `[s p] [~ +>] ?: =(a 'rebuild') ~& 'rebuilding message references...' =- [~ +>.$(stories -)] %- ~(urn by stories) |= {nom/name soy/story} =+ %+ roll grams.soy |= {t/telegram c/@ud k/(map serial @ud) s/(map circle (list @ud))} :+ +(c) (~(put by k) uid.t c) =/ src/circle ?: (~(has by aud.t) [our.bol nom]) [our.bol nom] ?~ aud.t ~&(%strange-aud [our.bol %inbox]) n.aud.t %+ ~(put by s) src [c (fall (~(get by s) src) ~)] soy(count c, known k, sourced s) ?: =(a 'refederate') ~& 'refederating. may take a while...' :_ +> =+ bov=(above [our now our]:bol) ?: =(bov our.bol) ~ :~ [ost.bol %pull /burden [bov dap.bol] ~] (wire-to-peer /burden) == ?: =(a 'incoming') ~& 'incoming subscriptions (ignoring circle subs):' ~& %+ skip ~(tap by sup.bol) |= {bone (pair ship path)} &(?=({$circle *} q) !?=({$circle $inbox *} q)) [~ +>] ?: =(a 'sources') ~& 'sources per story:' ~& %- ~(urn by stories) |= {n/name s/story} [n src.shape.s] [~ +>] ?: =(`0 (find "re-listen " (trip a))) ~& 're-listening' :_ +> :_ ~ (wire-to-peer /report/(crip (slag 10 (trip a)))) :: :: ?: =(a 'kill-all-subs') :_ +> %- ~(rep by sup.bol) |= [[b=bone (pair ship path)] out=(list move)] [[b %quit ~] out] :: ?: =(a 'kill-our-subs') :_ +> %- ~(rep by sup.bol) |= [[b=bone c=(pair ship path)] out=(list move)] ?: =(our.bol p.c) [[b %quit ~] out] out :: :: [~ +>] --