urbit/pkg/arvo/app/chat-hook.hoon

687 lines
18 KiB
Plaintext
Raw Normal View History

2019-11-23 01:05:06 +03:00
:: chat-hook:
:: mirror chat data from foreign to local based on read permissions
:: allow sending chat messages to foreign paths based on write perms
::
/- *permission-store, *chat-hook, *invite-store, *metadata-store,
*permission-hook, *group-store, *permission-group-hook ::TMP for upgrade
2020-02-20 03:04:21 +03:00
/+ *chat-json, *chat-eval, default-agent, verb, dbug
2019-11-23 01:05:06 +03:00
|%
+$ card card:agent:gall
::
+$ versioned-state
$% state-0
state-1
2019-11-23 01:05:06 +03:00
==
::
+$ state-1 [%1 state-base]
+$ state-0 [%0 state-base]
+$ state-base
$: synced=(map path ship)
2019-11-23 01:05:06 +03:00
invite-created=_|
allow-history=(map path ?)
==
::
+$ poke
$% [%chat-action chat-action]
[%permission-action permission-action]
[%invite-action invite-action]
[%chat-view-action chat-view-action]
==
::
+$ fact
$% [%chat-update chat-update]
==
--
=| state-1
2019-11-23 01:05:06 +03:00
=* state -
::
%+ verb |
%- agent:dbug
2019-11-23 01:05:06 +03:00
^- agent:gall
=<
|_ bol=bowl:gall
2019-11-23 01:05:06 +03:00
+* this .
chat-core +>
cc ~(. chat-core bol)
def ~(. (default-agent this %|) bol)
2019-11-23 01:05:06 +03:00
::
++ on-init
^- (quip card _this)
:_ this(invite-created %.y)
:~ (invite-poke:cc [%create /chat])
2019-11-23 01:05:06 +03:00
[%pass /invites %agent [our.bol %invite-store] %watch /invitatory/chat]
[%pass /permissions %agent [our.bol %permission-store] %watch /updates]
==
++ on-save !>(state)
++ on-load
|= =old=vase
=/ old !<(versioned-state old-vase)
?: ?=(%1 -.old)
[~ this(state old)]
:: path structure ugprade logic
::
:_ this(state [%1 +.old])
%- zing
^- (list (list card))
%+ turn
%~ tap in
%^ scry:cc (set path)
%chat-store
/keys
|^ |= chat=path
^- (list card)
=/ newp=permission (unify-permissions chat)
=/ old-group=path [%chat chat]
=/ new-group=path [%'~' chat]
;: weld
:~ (delete-group (snoc old-group %read))
(delete-group (snoc old-group %write))
==
::
(create-group new-group who.newp)
(hookup-group new-group kind.newp)
[(record-group new-group chat)]~
==
::
++ unify-permissions
|= chat=path
^- permission
=/ read=(unit permission) (get-permission chat %read)
=/ write=(unit permission) (get-permission chat %write)
?. &(?=(^ read) ?=(^ write))
~& [%missing-permission chat read=?=(~ read) write=?=(~ write)]
[%white [(slav %p (snag 0 chat)) ~ ~]]
:: village: exclusive to writers
::
?: &(?=(%white kind.u.read) ?=(%white kind.u.write))
[%white who.u.write]
:: channel: merge blacklists
::
?: &(?=(%black kind.u.read) ?=(%black kind.u.write))
[%black (~(uni in who.u.read) who.u.write)]
:: journal: exclusive to writers
::
?: &(?=(%black kind.u.read) ?=(%white kind.u.write))
[%white who.u.write]
:: mailbox: exclusive to readers
::
?: &(?=(%white kind.u.read) ?=(%black kind.u.write))
[%white who.u.read]
~| [%weird-kinds kind.u.read kind.u.write]
!!
::
++ get-permission
|= [chat=path what=?(%read %write)]
%^ scry:cc (unit permission)
%permission-store
[%permission %chat (snoc chat what)]
::
++ make-poke
|= [app=term =mark =vase]
^- card
[%pass /on-load/[app]/[mark] %agent [our.bol app] %poke mark vase]
::
++ delete-group
|= group=path
^- card
%^ make-poke %group-store
%group-action
!> ^- group-action
[%unbundle group]
::
++ create-group
|= [group=path who=(set ship)]
^- (list card)
:~ %^ make-poke %group-store
%group-action
!> ^- group-action
[%bundle group]
::
%^ make-poke %group-store
%group-action
!> ^- group-action
[%add who group]
==
::
++ hookup-group
|= [group=path =kind]
^- (list card)
:* %^ make-poke %permission-group-hook
%permission-group-hook-action
!> ^- permission-group-hook-action
[%associate group [group^kind ~ ~]]
::
=/ =ship (slav %p (snag 1 group))
?. =(our.bol ship) ~
:_ ~
%^ make-poke %permission-hook
%permission-hook-action
!> ^- permission-hook-action
[%add-owned group group]
==
::
++ record-group
|= [group=path chat=path]
^- card
=/ =metadata
~| [%weird-chat-path chat]
%* . *metadata
title (snag 1 chat)
date-created now.bol
creator (slav %p (snag 0 chat))
==
%^ make-poke %metadata-store
%metadata-action
!> ^- metadata-action
[%add group [%chat chat] metadata]
--
2019-11-23 01:05:06 +03:00
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
=^ cards state
?+ mark (on-poke:def mark vase)
%json (poke-json:cc !<(json vase))
%chat-action (poke-chat-action:cc !<(chat-action vase))
%chat-hook-action (poke-chat-hook-action:cc !<(chat-hook-action vase))
==
[cards this]
::
++ on-watch
|= =path
^- (quip card _this)
?+ path (on-watch:def path)
[%backlog *] [(watch-backlog:cc t.path) this]
[%mailbox *] [(watch-mailbox:cc t.path) this]
2019-11-23 01:05:06 +03:00
==
::
++ on-agent
|= [=wire =sign:agent:gall]
^- (quip card _this)
?+ -.sign (on-agent:def wire sign)
%watch-ack
=^ cards state
2020-01-04 00:06:42 +03:00
(watch-ack:cc wire p.sign)
2019-11-23 01:05:06 +03:00
[cards this]
::
%kick
=^ cards state
(kick:cc wire)
[cards this]
::
%fact
?+ p.cage.sign (on-agent:def wire sign)
%chat-update
=^ cards state
(fact-chat-update:cc wire !<(chat-update q.cage.sign))
[cards this]
::
%invite-update
=^ cards state
(fact-invite-update:cc wire !<(invite-update q.cage.sign))
[cards this]
::
%permission-update
=^ cards state
(fact-permission-update:cc wire !<(permission-update q.cage.sign))
[cards this]
==
==
::
++ on-leave on-leave:def
++ on-peek on-peek:def
++ on-arvo on-arvo:def
++ on-fail on-fail:def
--
::
::
|_ bol=bowl:gall
::
++ poke-json
|= jon=json
^- (quip card _state)
(poke-chat-action (json-to-action jon))
::
++ poke-chat-action
|= act=chat-action
^- (quip card _state)
?> ?=(%message -.act)
:: local
:_ state
?: (team:title our.bol src.bol)
?. (~(has by synced) path.act)
~
=* letter letter.envelope.act
=? letter &(?=(%code -.letter) ?=(~ output.letter))
=/ =hoon (ream expression.letter)
letter(output (eval bol hoon))
2019-11-23 01:05:06 +03:00
=/ ship (~(got by synced) path.act)
=/ appl ?:(=(ship our.bol) %chat-store %chat-hook)
[%pass / %agent [ship appl] %poke %chat-action !>(act)]~
:: foreign
=/ ship (~(get by synced) path.act)
?~ ship
~
?. =(u.ship our.bol)
~
:: check if write is permitted
?. (is-permitted src.bol path.act)
2019-11-23 01:05:06 +03:00
~
=: author.envelope.act src.bol
when.envelope.act now.bol
==
[%pass / %agent [our.bol %chat-store] %poke %chat-action !>(act)]~
::
++ poke-chat-hook-action
|= act=chat-hook-action
^- (quip card _state)
?- -.act
%add-owned
?> (team:title our.bol src.bol)
=/ chat-path [%mailbox path.act]
?: (~(has by synced) path.act)
[~ state]
=: synced (~(put by synced) path.act our.bol)
allow-history (~(put by allow-history) path.act allow-history.act)
==
:_ state
%+ weld
[%pass chat-path %agent [our.bol %chat-store] %watch chat-path]~
2020-01-10 01:53:38 +03:00
(create-permission path.act security.act)
2019-11-23 01:05:06 +03:00
::
%add-synced
?> (team:title our.bol src.bol)
?: (~(has by synced) path.act) [~ state]
=. synced (~(put by synced) path.act ship.act)
?. ask-history.act
=/ chat-path [%mailbox path.act]
:_ state
[%pass chat-path %agent [ship.act %chat-hook] %watch chat-path]~
:: TODO: only ask for backlog from previous point
=/ chat-history [%backlog (weld path.act /0)]
2019-11-23 01:05:06 +03:00
:_ state
[%pass chat-history %agent [ship.act %chat-hook] %watch chat-history]~
2019-11-23 01:05:06 +03:00
::
%remove
=/ ship (~(get by synced) path.act)
?~ ship
[~ state]
?: &(=(u.ship our.bol) (team:title our.bol src.bol))
:: delete one of our.bol own paths
:_ state(synced (~(del by synced) path.act))
%- zing
:~ (pull-wire [%backlog (weld path.act /0)])
(pull-wire [%mailbox path.act])
~[(permission-poke [%delete [%chat path.act]])]
[%give %kick [%mailbox path.act]~ ~]~
2019-11-23 01:05:06 +03:00
==
?. |(=(u.ship src.bol) (team:title our.bol src.bol))
:: if neither ship = source or source = us, do nothing
[~ state]
:: delete a foreign ship's path
:- (pull-wire [%mailbox path.act])
state(synced (~(del by synced) path.act))
==
::
++ watch-mailbox
|= pax=path
^- (list card)
?> ?=(^ pax)
?> (~(has by synced) pax)
:: check if read is permitted
?> (is-permitted src.bol pax)
=/ box (chat-scry pax)
?~ box !!
[%give %fact ~ %chat-update !>([%create pax])]~
::
++ watch-backlog
2019-11-23 01:05:06 +03:00
|= pax=path
^- (list card)
?> ?=(^ pax)
=/ last (dec (lent pax))
=/ backlog-start=(unit @ud)
%+ rush
(snag last `(list @ta)`pax)
dem:ag
=/ pas `path`(oust [last 1] `(list @ta)`pax)
?> ?=([* ^] pas)
?> (~(has by synced) pas)
:: check if read is permitted
?> (is-permitted src.bol pas)
%- zing
:~ [%give %fact ~ %chat-update !>([%create pas])]~
?. ?&(?=(^ backlog-start) (~(has by allow-history) pas)) ~
(paginate-messages pas (need (chat-scry pas)) u.backlog-start)
[%give %kick [%backlog pax]~ `src.bol]~
==
2019-11-23 01:05:06 +03:00
::
++ paginate-messages
|= [=path =mailbox start=@ud]
^- (list card)
=/ cards=(list card) ~
=/ end (lent envelopes.mailbox)
?: |((gte start end) =(end 0))
cards
=. envelopes.mailbox (slag start `(list envelope)`envelopes.mailbox)
|- ^- (list card)
?~ envelopes.mailbox
cards
?: (lte end 5.000)
=. cards
%+ snoc cards
%- messages-fact
[path start (lent envelopes.mailbox) envelopes.mailbox]
$(envelopes.mailbox ~)
=. cards
%+ snoc cards
%- messages-fact
:^ path start
(add start 5.000)
(scag 5.000 `(list envelope)`envelopes.mailbox)
=: start (add start 5.000)
end (sub end 5.000)
==
$(envelopes.mailbox (slag 5.000 `(list envelope)`envelopes.mailbox))
::
++ fact-invite-update
|= [wir=wire fact=invite-update]
^- (quip card _state)
?+ -.fact
[~ state]
::
%accepted
=/ ask-history ?~((chat-scry path.invite.fact) %.y %.n)
2019-11-23 01:05:06 +03:00
:_ state
[(chat-view-poke [%join ship.invite.fact path.invite.fact ask-history])]~
==
::
++ fact-permission-update
|= [wir=wire fact=permission-update]
^- (quip card _state)
2020-02-08 02:52:55 +03:00
|^
2019-11-23 01:05:06 +03:00
:_ state
?+ -.fact ~
2019-11-23 01:05:06 +03:00
%add (handle-permissions [%add path.fact who.fact])
%remove (handle-permissions [%remove path.fact who.fact])
==
2020-02-08 02:52:55 +03:00
::
++ handle-permissions
|= [kind=?(%add %remove) pax=path who=(set ship)]
^- (list card)
%- zing
^- (list (list card))
%+ turn
(chats-of-group pax)
|= chat=path
^- (list card)
=/ owner (~(get by synced) chat)
2020-02-11 22:47:34 +03:00
?~ owner ~
?. =(u.owner our.bol) ~
2020-02-08 02:52:55 +03:00
%- zing
%+ turn ~(tap in who)
|= =ship
?: (is-permitted ship chat)
2020-02-08 02:52:55 +03:00
?: ?|(=(kind %remove) =(ship our.bol)) ~
:: if ship has just been added to the permitted group,
:: send them an invite
~[(send-invite chat ship)]
2020-02-08 02:52:55 +03:00
:: if ship is not permitted, kick their subscription
[%give %kick [%mailbox chat]~ `ship]~
2020-02-08 02:52:55 +03:00
::
++ send-invite
|= [=path =ship]
^- card
=/ =invite [our.bol %chat-hook path ship '']
=/ act=invite-action [%invite /chat (shaf %msg-uid eny.bol) invite]
[%pass / %agent [our.bol %invite-hook] %poke %invite-action !>(act)]
--
2019-11-23 01:05:06 +03:00
::
++ fact-chat-update
|= [wir=wire fact=chat-update]
^- (quip card _state)
?: (team:title our.bol src.bol)
(handle-local fact)
(handle-foreign fact)
::
++ handle-local
|= fact=chat-update
^- (quip card _state)
?+ -.fact [~ state]
2019-11-23 01:05:06 +03:00
%delete
?. (~(has by synced) path.fact)
[~ state]
:_ state(synced (~(del by synced) path.fact))
[%pass [%mailbox path.fact] %agent [our.bol %chat-store] %leave ~]~
::
%message
:_ state
[%give %fact [%mailbox path.fact]~ %chat-update !>(fact)]~
2019-11-23 01:05:06 +03:00
::
%messages
:_ state
[%give %fact [%mailbox path.fact]~ %chat-update !>(fact)]~
2019-11-23 01:05:06 +03:00
==
::
++ handle-foreign
|= fact=chat-update
^- (quip card _state)
?+ -.fact [~ state]
2019-11-23 01:05:06 +03:00
%create
:_ state
?> ?=([* ^] path.fact)
=/ shp (~(get by synced) path.fact)
?~ shp ~
?. =(src.bol u.shp) ~
[(chat-poke [%create path.fact])]~
2019-11-23 01:05:06 +03:00
::
%delete
?> ?=([* ^] path.fact)
=/ shp (~(get by synced) path.fact)
?~ shp
[~ state]
?. =(u.shp src.bol)
[~ state]
:_ state(synced (~(del by synced) path.fact))
:- (chat-poke [%delete path.fact])
[%pass [%mailbox path.fact] %agent [src.bol %chat-hook] %leave ~]~
::
%message
:_ state
?> ?=([* ^] path.fact)
=/ shp (~(get by synced) path.fact)
?~ shp ~
?. =(src.bol u.shp) ~
[(chat-poke [%message path.fact envelope.fact])]~
::
%messages
:_ state
?> ?=([* ^] path.fact)
=/ shp (~(get by synced) path.fact)
?~ shp ~
?. =(src.bol u.shp) ~
[(chat-poke [%messages path.fact envelopes.fact])]~
==
::
++ kick
|= wir=wire
^- (quip card _state)
?: =(wir /permissions)
:_ state
[%pass /permissions %agent [our.bol %permission-store] %watch /updates]~
2019-12-01 08:24:23 +03:00
::
2020-02-06 22:28:58 +03:00
?+ wir !!
[%mailbox @ *]
~& mailbox-kick+wir
?. (~(has by synced) t.wir)
:: no-op
[~ state]
~& %chat-hook-resubscribe
2019-12-01 08:24:23 +03:00
=/ =ship (~(got by synced) t.wir)
=/ mailbox=(unit mailbox) (chat-scry t.wir)
=/ chat-history
%+ welp backlog+t.wir
?~(mailbox /0 /(scot %ud (lent envelopes.u.mailbox)))
:_ state
2019-12-01 08:24:23 +03:00
[%pass chat-history %agent [ship %chat-hook] %watch chat-history]~
::
2020-02-06 22:28:58 +03:00
[%backlog @ @ *]
=/ pax `path`(oust [(dec (lent t.wir)) 1] `(list @ta)`t.wir)
?. (~(has by synced) pax) [~ state]
=/ =ship
?: =('~' i.t.wir)
(slav %p i.t.t.wir)
(slav %p i.t.wir)
=. pax ?~((chat-scry pax) wir [%mailbox pax])
:_ state
[%pass pax %agent [ship %chat-hook] %watch pax]~
==
2019-11-23 01:05:06 +03:00
::
++ watch-ack
|= [wir=wire saw=(unit tang)]
^- (quip card _state)
?~ saw
[~ state]
?> ?=(^ wir)
:_ state(synced (~(del by synced) t.wir))
%. ~
%- slog
:* leaf+"chat-hook failed subscribe on {(spud t.wir)}"
leaf+"stack trace:"
u.saw
==
::
++ chat-poke
|= act=chat-action
^- card
[%pass / %agent [our.bol %chat-store] %poke %chat-action !>(act)]
::
++ chat-view-poke
|= act=chat-view-action
^- card
[%pass / %agent [our.bol %chat-view] %poke %chat-view-action !>(act)]
::
++ permission-poke
|= act=permission-action
^- card
[%pass / %agent [our.bol %permission-store] %poke %permission-action !>(act)]
::
++ invite-poke
|= act=invite-action
^- card
[%pass / %agent [our.bol %invite-store] %poke %invite-action !>(act)]
::
++ messages-fact
|= [=path start=@ud end=@ud envelopes=(list envelope)]
^- card
[%give %fact ~ %chat-update !>([%messages path start end envelopes])]
::
++ create-permission
|= [pax=path sec=rw-security]
^- (list card)
?+ sec ~
%channel ~[(permission-poke (sec-to-perm pax %black))]
%village ~[(permission-poke (sec-to-perm pax %white))]
2019-11-23 01:05:06 +03:00
==
::
++ sec-to-perm
|= [pax=path =kind]
^- permission-action
[%create pax kind *(set ship)]
::
++ chat-scry
|= pax=path
^- (unit mailbox)
2020-02-21 00:28:56 +03:00
%^ scry (unit mailbox)
%chat-store
[%mailbox pax]
2019-11-23 01:05:06 +03:00
::
++ invite-scry
|= uid=serial
^- (unit invite)
2020-02-21 00:28:56 +03:00
%^ scry (unit invite)
%invite-store
/invite/chat/(scot %uv uid)
2019-11-23 01:05:06 +03:00
::
++ chats-of-group
|= =group-path
^- (list path)
:: if metadata-store isn't running yet, we're still in the upgrade ota phase.
:: we can't get chats from the metadata-store, but can make assumptions
:: about group path shape, and the chat that would match it.
::TODO remove me at some point.
::
?. .^(? %gu (scot %p our.bol) %metadata-store (scot %da now.bol) ~)
?: ?=([%'~' @ ^] group-path)
~& [%assuming-ported-legacy-group group-path]
[t.group-path]~
~& [%weird-group group-path]
~
%+ murn
^- (list resource)
=- ~(tap in (~(gut by -) group-path ~))
.^ (jug path resource)
%gy
(scot %p our.bol)
%metadata-store
(scot %da now.bol)
/group-indices
==
|= resource
^- (unit path)
?. =(%chat app-name) ~
`app-path
::
++ groups-of-chat
|= chat=path
^- (list group-path)
:: if metadata-store isn't running yet, we're still in the upgrade ota phase.
:: we can't get groups from the metadata-store, but can make assumptions
:: about chat path shape, and the chat that would match it.
::TODO remove me at some point.
::
?. .^(? %gu (scot %p our.bol) %metadata-store (scot %da now.bol) ~)
?: ?=([@ ^] chat)
~& [%assuming-ported-legacy-chat chat]
[%'~' chat]~
~& [%weird-chat chat]
~
=- ~(tap in (~(gut by -) [%chat chat] ~))
.^ (jug resource group-path)
%gy
(scot %p our.bol)
%metadata-store
(scot %da now.bol)
/resource-indices
==
::
::NOTE this assumes permission paths match group paths
++ is-permitted
|= [who=ship chat=path]
2019-11-23 01:05:06 +03:00
^- ?
%+ lien (groups-of-chat chat)
|= =group-path
%^ scry ?
%permission-store
[%permitted (scot %p who) group-path]
2019-11-23 01:05:06 +03:00
::
2020-02-21 00:28:56 +03:00
++ scry
|* [=mold app=term =path]
.^ mold
%gx
(scot %p our.bol)
app
(scot %da now.bol)
(snoc `^path`path %noun)
==
::
2019-11-23 01:05:06 +03:00
++ pull-wire
|= pax=path
^- (list card)
?> ?=(^ pax)
=/ shp (~(get by synced) t.pax)
?~ shp ~
?: =(u.shp our.bol)
[%pass pax %agent [our.bol %chat-store] %leave ~]~
[%pass pax %agent [u.shp %chat-hook] %leave ~]~
--