summaryrefslogtreecommitdiff
path: root/desk/sur
diff options
context:
space:
mode:
Diffstat (limited to 'desk/sur')
-rw-r--r--desk/sur/boke.hoon113
-rw-r--r--desk/sur/contact.hoon51
-rw-r--r--desk/sur/polls.hoon57
-rw-r--r--desk/sur/spider.hoon27
-rw-r--r--desk/sur/tlon-chat.hoon1268
-rw-r--r--desk/sur/tlon/channels.hoon464
-rw-r--r--desk/sur/tlon/cite.hoon57
-rw-r--r--desk/sur/tlon/groups.hoon383
-rw-r--r--desk/sur/trill/gate.hoon26
-rw-r--r--desk/sur/trill/post.hoon115
-rw-r--r--desk/sur/twatter/json.hoon105
-rw-r--r--desk/sur/twatter/test.json1
12 files changed, 2667 insertions, 0 deletions
diff --git a/desk/sur/boke.hoon b/desk/sur/boke.hoon
new file mode 100644
index 0000000..6af312d
--- /dev/null
+++ b/desk/sur/boke.hoon
@@ -0,0 +1,113 @@
+/- tp=trill-post, tlonc=tlon-channels, polls
+|%
++$ action
+$% [%add p=post:tp]
+ [%del p=id:tp]
+ :: approve comments?
+ [%add-board name=@t]
+ [%del-board name=@t]
+==
++$ kaji-req
+$% [%board-redi =post:tp]
+ [%blog-redi =post:tp]
+ [%redi url=@t]
+ [%tv-chat p=@t q=post:tp]
+ [%radio-chat p=@p q=*]
+ [%chat-msg p=flag:tlonc q=memo:tlonc]
+==
++$ state state-0
++$ tags-table (map @t pidmap)
++$ pathmap (map path pid:tp)
++$ threadsf ((mop pid:tp thread:tp) ggth:tp)
++$ pidmap ((mop pid:tp pid:tp) ggth:tp)
++$ external ((mop esrc epost) extc)
+++ erm ((on id:tp post:tp) gth)
++$ esrc
+$: time=@da
+ id=@t :: whatever
+==
++$ epost
+ $: =pid:tp
+ url=@t
+ service=@t
+ json=@t
+==
+++ extc |= [a=esrc b=esrc] (gth time.a time.b)
+:: in active threads the pid is the pid of the last reply
++$ state-0
+$: %0
+ :: all data here. flat structure. fk'it
+ feed=gfeed:tp
+ threads=threadsf
+ active-threads=pidmap :: same as above but ordered by last reply
+ paths=pathmap
+ tags=tags-table
+ tv=tv-table
+ polls=polls-table
+ notifications=hark-table
+ external=(map @t external) :: twitter etc, tagged or named
+==
++$ tv-table
+ $: urbit=(map @p urbit-radio)
+ here=(map @t bstv)
+ ==
++$ hark-table (map @p t-hark)
++$ t-hark
+ $: last-online=@da
+ tracking=(map pid:tp post:tp)
+ ==
++$ polls-table (map pid:tp poll:polls)
++$ bstv
+ $: name=@t
+ owner=@p
+ current=[playing=@t started=@da] :: should point to the schedule
+ schedule=(map @da tv-station)
+ ==
++$ tv-station
+ $: description=cord
+ src=cord :: source file
+ started=@da
+ ended=(unit @da)
+ viewers=(map @p (list presence))
+ chat=(list post:tp)
+ perms=*
+ ==
++$ presence [came=@da left=@da]
++$ twr $%([%our name=@t owner=@p tv=tv-station] [%urb name=@t owner=@p p=urbit-radio])
++$ urbit-radio
+ $:
+ description=cord
+ location=ship
+ time=@da
+ viewers=@ud
+ ==
++$ radio-event
+ $% [%put p=urbit-radio]
+ [%remove =ship]
+ [%request ~]
+ [%response minitowers=(map ship urbit-radio)]
+ ==
+
++$ admin
+$: banned=(set address:eyre)
+ settings=(map @p setting)
+==
++$ setting
+$: thing=*
+==
+
+:: fetching stuff, shouldn't be here really but whatever
++$ section $?(%blog %comments %chat %threads %replies)
++$ chat-page [p=scan:tlonc older=(unit @da) newer=(unit @da) count=@ud]
++$ search-res $%([%chat chat-page] [%trill search-page])
++$ search-page
+ $: res=(list post-snip)
+ newer=cursor:tp
+ older=cursor:tp
+ ==
++$ post-snip
+ $: fn=full-node:tp
+ snip=@t
+ ==
+
+--
diff --git a/desk/sur/contact.hoon b/desk/sur/contact.hoon
new file mode 100644
index 0000000..973f954
--- /dev/null
+++ b/desk/sur/contact.hoon
@@ -0,0 +1,51 @@
+/+ sr=sortug
+|%
+++ get-contacts
+|= =bowl:gall
+=/ io ~(. io:sr bowl)
+(scry:io %whom /1/contacts/mars whoms)
+:: fuck it
++$ res
+$: p=@p
+ contact=(unit contact)
+ whom=(unit whom)
+==
+:: %contacts
++$ foreign [for=$@(~ prof) sag=*]
++$ prof [wen=@da con=$@(~ contact)]
++$ rolodex (map ship foreign)
++$ contact
+$: nickname=@t
+ bio=@t
+ status=@t
+ color=@ux
+ avatar=(unit @t)
+ cover=(unit @t)
+ groups=(set (pair ship term))
+==
+:: %whom
++$ whoms (map (each @p @t) whom)
++$ whom
+$: info=(map @tas info-field)
+ profile=(unit profile)
+==
++$ access-level ?(%public %mutual)
+::
++$ profile
+ $: info=(map @tas [value=info-field access=access-level])
+ fields=(map @tas field-def)
+ ==
++$ field-def [name=@t type=field-type-tag]
+::
++$ field-type-tag ?(%text %date %look %tint %coll)
+::
++$ info-field
+ $% [%text @t]
+ [%date @da]
+ [%look @t] :: link to image
+ [%tint @ux] :: color (0-255)
+ [%coll coll] :: collection of items on Urbit (groups, apps, wikis, etc)
+ ==
+::
++$ coll (set [=ship slug=@ta])
+--
diff --git a/desk/sur/polls.hoon b/desk/sur/polls.hoon
new file mode 100644
index 0000000..93085c2
--- /dev/null
+++ b/desk/sur/polls.hoon
@@ -0,0 +1,57 @@
+/- gate=trill-gate
+|%
++$ pid [ship=@p id=@da]
++$ id @da
++$ expiry @da
++$ text @t
++$ poll
+$: author=ship
+ =time
+ =expiry
+ =text
+ options=(list @t)
+ =votes
+ read=lock:gate
+ write=lock:gate
+==
++$ votes
+$% excl
+ incl
+==
++$ excl [%exc p=(map ship @ud)]
++$ incl [%inc p=(map @ud (map @p (set @ud)))]
+:: some helpers
+
+++ new-poll
+|= [=pid =text =expiry options=(list @t) inc=?]
+=/ votes ?. inc *excl *incl
+^- poll
+:* author=-.pid
+ time=+.pid
+ expiry
+ text
+ options
+ votes
+ read=*lock:gate
+ write=*lock:gate
+==
+:: comms
++$ ui-action
+$% [%propose text=@t =expiry options=(list @t) exc=?]
+ [%cancel =id]
+ [%change-expiry =id =expiry]
+ [%vote =pid option=@ comment=@t]
+ [%cancel-vote =pid]
+==
++$ update
+$% [%new-poll =poll]
+ [%ded-poll p=pid]
+ [%old-poll p=poll q=upd]
+==
++$ upd
+$? [%new-vote p=@ud]
+ [%expiry-changed q=expiry]
+ [%vote-changed p=@p old=@ new=@]
+ [%vote-canceled p=@p q=@]
+==
+--
diff --git a/desk/sur/spider.hoon b/desk/sur/spider.hoon
new file mode 100644
index 0000000..7c21268
--- /dev/null
+++ b/desk/sur/spider.hoon
@@ -0,0 +1,27 @@
+/+ libstrand=strand
+=, strand=strand:libstrand
+|%
++$ thread $-(vase shed:khan)
++$ input [=tid =cage]
++$ tid tid:strand
++$ bowl bowl:strand
++$ http-error
+ $? %bad-request :: 400
+ %forbidden :: 403
+ %nonexistent :: 404
+ %offline :: 504
+ ==
++$ start-args
+ $: parent=(unit tid)
+ use=(unit tid)
+ =beak
+ file=term
+ =vase
+ ==
++$ inline-args
+ $: parent=(unit tid)
+ use=(unit tid)
+ =beak
+ =shed:khan
+ ==
+--
diff --git a/desk/sur/tlon-chat.hoon b/desk/sur/tlon-chat.hoon
new file mode 100644
index 0000000..dd82833
--- /dev/null
+++ b/desk/sur/tlon-chat.hoon
@@ -0,0 +1,1268 @@
+|%
+++ meta
+|%
++$ data
+ $: title=cord
+ description=cord
+ image=cord
+ cover=cord
+ ==
++$ diff
+ $% [%title =cord]
+ [%description =cord]
+ [%image =cord]
+ [%cover =cord]
+ ==
+--
+++ d
+|%
+:: $flag: identifier for a diary channel
++$ flag (pair ship term)
+:: $feel: either an emoji identifier like :diff or a URL for custom
++$ feel @ta
+:: $view: the persisted display format for a diary
++$ view ?(%grid %list)
+:: $sort: the persisted sort type for a diary
++$ sort ?(%alpha %time)
+:: $shelf: my ship's diaries
++$ shelf (map flag diary)
+:: $said: used for references
++$ said (pair flag outline)
+:: $plan: index into diary state
+:: p: Note being referred to
+:: q: Quip being referred to, if any
+::
++$ plan
+ (pair time (unit time))
+::
+:: $diary: written longform communication
+::
+:: net: an indicator of whether I'm a host or subscriber
+:: log: the history of all modifications
+:: perm: holds the diary's permissions
+:: view: what format to display
+:: sort: how to order posts
+:: notes: the actual contents of the diary
+:: remark: what is the last thing we've read
+:: banter: comments organized by post
+::
++$ diary
+ $: =net
+ =log
+ =perm
+ =view
+ =sort
+ =notes
+ =remark
+ ==
+::
+:: $notes: a set of time ordered diary posts
+::
+++ notes
+ =< rock
+ |%
+ +$ rock
+ ((mop time note) lte)
+ ++ on
+ ((^on time note) lte)
+ +$ diff
+ (pair time delta)
+ +$ delta
+ $% [%add p=essay]
+ [%edit p=essay]
+ [%del ~]
+ [%quips p=diff:quips]
+ [%add-feel p=ship q=feel]
+ [%del-feel p=ship]
+ ==
+ --
+::
+:: $quips: a set of time ordered note comments
+::
+++ quips
+ =< rock
+ |%
+ +$ rock
+ ((mop time quip) lte)
+ ++ on
+ ((^on time quip) lte)
+ +$ diff
+ (pair time delta)
+ +$ delta
+ $% [%add p=memo]
+ [%del ~]
+ [%add-feel p=ship q=feel]
+ [%del-feel p=ship]
+ ==
+ --
+::
+:: $outline: abridged $note
+:: .quips: number of comments
+::
++$ outline
+ [quips=@ud quippers=(set ship) essay]
+::
+++ outlines
+ =< outlines
+ |%
+ +$ outlines ((mop time outline) lte)
+ ++ on ((^on time outline) lte)
+ --
+::
+:: $note: a diary post
+::
++$ note [seal essay]
+:: $quip: a post comment
+::
++$ quip [cork memo]
+::
+:: $seal: host-side data for a note
+::
++$ seal
+ $: =time
+ =quips
+ feels=(map ship feel)
+ ==
+::
+:: $cork: host-side data for a quip
+::
++$ cork
+ $: =time
+ feels=(map ship feel)
+ ==
+:: $essay: the post data itself
+::
+:: title: the name of the post
+:: image: a visual displayed as a header
+:: content: the body of the post
+:: author: the ship that wrote the post
+:: sent: the client-side time the post was made
+::
++$ essay
+ $: title=@t
+ image=@t
+ content=(list verse)
+ author=ship
+ sent=time
+ ==
++$ story (pair (list block) (list inline))
+:: $memo: the comment data itself
+::
+:: content: the body of the comment
+:: author: the ship that wrote the comment
+:: sent: the client-side time the comment was made
+::
++$ memo
+ $: content=story
+ author=ship
+ sent=time
+ ==
+:: $verse: a chunk of post content
+::
+:: blocks stand on their own. inlines come in groups and get wrapped
+:: into a paragraph
+::
++$ verse
+ $% [%block p=block]
+ [%inline p=(list inline)]
+ ==
+:: $listing: recursive type for infinitely nested <ul> or <ol>
++$ listing
+ $% [%list p=?(%ordered %unordered) q=(list listing) r=(list inline)]
+ [%item p=(list inline)]
+ ==
+:: $block: post content that sits outside of the normal text
+::
+:: %image: a visual, we record dimensions for better rendering
+:: %cite: an Urbit reference
+:: %header: a traditional HTML heading, h1-h6
+:: %listing: a traditional HTML list, ul and ol
+::
++$ block
+ $% [%image src=cord height=@ud width=@ud alt=cord]
+ [%cite =cite:c]
+ [%header p=?(%h1 %h2 %h3 %h4 %h5 %h6) q=(list inline)]
+ [%listing p=listing]
+ [%rule ~]
+ ==
+:: $inline: post content that flows within a paragraph
+::
+:: @t: plain text
+:: %italics: italic text
+:: %bold: bold text
+:: %strike: strikethrough text
+:: %inline-code: code formatting for small snippets
+:: %blockquote: blockquote surrounded content
+:: %block: link/reference to blocks
+:: %code: code formatting for large snippets
+:: %tag: tag gets special signifier
+:: %link: link to a URL with a face
+:: %break: line break
+::
++$ inline
+ $@ @t
+ $% [%italics p=(list inline)]
+ [%bold p=(list inline)]
+ [%strike p=(list inline)]
+ [%blockquote p=(list inline)]
+ [%inline-code p=cord]
+ [%ship p=ship]
+ [%block p=@ud q=cord]
+ [%code p=cord]
+ [%tag p=cord]
+ [%link p=cord q=cord]
+ [%break ~]
+ ==
+:: $log: a time ordered history of modifications to a diary
+::
++$ log
+ ((mop time diff) lte)
+++ log-on
+ ((on time diff) lte)
+::
+:: $action: the complete set of data required to modify a diary
+::
++$ action
+ (pair flag:g update)
+::
+:: $update: a representation in time of a modification to a diary
+::
++$ update
+ (pair time diff)
+::
+:: $diff: the full suite of modifications that can be made to a diary
+::
++$ diff
+ $% [%notes p=diff:notes]
+ ::
+ [%add-sects p=(set sect:g)]
+ [%del-sects p=(set sect:g)]
+ ::
+ [%create p=perm q=notes]
+ [%view p=view]
+ [%sort p=sort]
+ ::
+ ==
+::
+:: $net: an indicator of whether I'm a host or subscriber
+::
+:: %pub: am publisher/host with fresh log
+:: %sub: subscribed to the ship at saga
+::
++$ net
+ $% [%sub p=ship load=_| =saga:e]
+ [%pub ~] :: TODO: permissions?
+ ==
+::
+:: $briefs: a map of diary unread information
+::
+:: brief: the last time a diary was read, how many posts since,
+:: and the id of the last read note
+::
+++ briefs
+ =< briefs
+ |%
+ +$ briefs
+ (map flag brief)
+ +$ brief
+ [last=time count=@ud read-id=(unit time)]
+ +$ update
+ (pair flag brief)
+ --
+:: $remark: a marker representing the last note I've read
+::
++$ remark
+ [last-read=time watching=_| ~]
+::
++$ remark-action
+ (pair flag remark-diff)
+::
++$ remark-diff
+ $% [%read ~]
+ [%read-at p=time]
+ [?(%watch %unwatch) ~]
+ ==
+::
+:: $perm: represents the permissions for a diary channel and gives a
+:: pointer back to the group it belongs to.
+::
++$ perm
+ $: writers=(set sect:g)
+ group=flag:g
+ ==
+:: $leave: a flag to pass for a channel leave
+::
++$ leave flag:g
+::
+:: $create: represents a request to create a channel
+::
+:: The name will be used as part of the flag which represents the
+:: channel. $create is consumed by the diary agent first and then
+:: passed to the groups agent to register the channel with the group.
+::
+:: Write permission is stored with the specific agent in the channel,
+:: read permission is stored with the group's data.
+::
++$ create
+ $: group=flag:g
+ name=term
+ title=cord
+ description=cord
+ readers=(set sect:g)
+ writers=(set sect:g)
+ ==
+--
+++ c
+|%
++$ writ [seal memo]
+:: $id: an identifier for chat messages
++$ id (pair ship time)
+:: $feel: either an emoji identifier like :wave: or a URL for custom
++$ feel @ta
++$ said (pair flag writ)
+::
+:: $seal: the id of a chat and its meta-responses
+::
+:: id: the id of the message
+:: feels: reactions to a message
+:: replied: set of replies to a message
+::
++$ seal
+ $: =id
+ feels=(map ship feel)
+ replied=(set id)
+ ==
+::
+:: $whom: a polymorphic identifier for chats
+::
++$ whom
+ $% [%flag p=flag]
+ [%ship p=ship]
+ [%club p=id:club]
+ ==
+::
+:: $briefs: a map of chat/club/dm unread information
+::
+:: brief: the last time a message was read, how many messages since,
+:: and the id of the last read message
+::
+++ briefs
+ =< briefs
+ |%
+ +$ briefs
+ (map whom brief)
+ +$ brief
+ [last=time count=@ud read-id=(unit id)]
+ +$ update
+ (pair whom brief)
+ --
+::
++$ remark-action
+ (pair whom remark-diff)
+::
++$ remark-diff
+ $% [%read ~]
+ [%read-at p=time]
+ [?(%watch %unwatch) ~]
+ ==
+::
+:: $flag: an identifier for a $chat channel
+::
++$ flag (pair ship term)
+::
+:: $diff: represents an update to state
+::
+:: %writs: a chat message update
+:: %add-sects: add sects to writer permissions
+:: %del-sects: delete sects from writers
+:: %create: create a new chat
+::
++$ diff
+ $% [%writs p=diff:writs]
+ ::
+ [%add-sects p=(set sect:g)]
+ [%del-sects p=(set sect:g)]
+ ::
+ [%create p=perm q=pact]
+ ==
+:: $index: a map of chat message id to server received message time
+::
++$ index (map id time)
+::
+:: $pact: a double indexed map of chat messages, id -> time -> message
+::
++$ pact
+ $: wit=writs
+ dex=index
+ ==
+::
+:: $club: a direct line of communication between multiple parties
+::
+:: uses gossip to ensure all parties keep in sync
+::
+++ club
+ =< club
+ |%
+ :: $id: an identification signifier for a $club
+ ::
+ +$ id @uvH
+ :: $net: status of club
+ ::
+ +$ net ?(%archive %invited %done)
+ +$ club [=pact crew]
+ ::
+ :: $crew: a container for the metadata for the club
+ ::
+ :: team: members that have accepted an invite
+ :: hive: pending members that have been invited
+ :: met: metadata representing club
+ :: net: status
+ :: pin: should the $club be pinned to the top
+ ::
+ +$ crew
+ $: team=(set ship)
+ hive=(set ship)
+ met=data:meta
+ =net
+ pin=_|
+ ==
+ :: $rsvp: a $club invitation response
+ ::
+ +$ rsvp [=id =ship ok=?]
+ :: $create: a request to create a $club with a starting set of ships
+ ::
+ +$ create
+ [=id hive=(set ship)]
+ :: $invite: the contents to send in an invitation to someone
+ ::
+ +$ invite [=id team=(set ship) hive=(set ship) met=data:meta]
+ :: $echo: number of times diff has been echoed
+ ::
+ +$ echo @ud
+ +$ diff (pair echo delta)
+ ::
+ +$ delta
+ $% [%writ =diff:writs]
+ [%meta meta=data:meta]
+ [%team =ship ok=?]
+ [%hive by=ship for=ship add=?]
+ [%init team=(set ship) hive=(set ship) met=data:meta]
+ ==
+ ::
+ +$ action (pair id diff)
+ --
+::
+:: $writs: a set of time ordered chat messages
+::
+++ writs
+ =< writs
+ |%
+ +$ writs
+ ((mop time writ) lte)
+ ++ on
+ ((^on time writ) lte)
+ +$ diff
+ (pair id delta)
+ +$ delta
+ $% [%add p=memo]
+ [%del ~]
+ [%add-feel p=ship q=feel]
+ [%del-feel p=ship]
+ ==
+ --
+::
+:: $dm: a direct line of communication between two ships
+::
+:: net: status of dm
+:: id: a message identifier
+:: action: an update to the dm
+:: rsvp: a response to a dm invitation
+::
+++ dm
+ =< dm
+ |%
+ +$ dm
+ $: =pact
+ =remark
+ =net
+ pin=_|
+ ==
+ +$ net ?(%inviting %invited %archive %done)
+ +$ id (pair ship time)
+ +$ diff diff:writs
+ +$ action (pair ship diff)
+ +$ rsvp [=ship ok=?]
+ --
+::
+:: $log: a time ordered map of all modifications to groups
+::
++$ log
+ ((mop time diff) lte)
+++ log-on
+ ((on time diff) lte)
++$ remark
+ [last-read=time watching=_| ~]
+::
+:: $chat: a group based channel for communicating
+::
++$ chat
+ [=net =remark =log =perm =pact]
+::
+:: $notice: the contents of an automated message
+::
+:: pfix: text preceding ship name
+:: sfix: text following ship name
+::
++$ notice [pfix=@t sfix=@t]
+::
+:: $content: the contents of a message whether handwritten or automated
+::
++$ content
+ $% [%story p=story]
+ [%notice p=notice]
+ ==
+::
+:: $draft: the contents of an unsent message at a particular $whom
+::
++$ draft
+ (pair whom story)
+::
+:: $story: handwritten contents of a message
+::
+:: blocks precede inline content
+::
++$ story
+ (pair (list block) (list inline))
+::
+:: $block: content which stands on it's own outside of inline content
+::
++$ block
+ $% [%image src=cord height=@ud width=@ud alt=cord]
+ [%cite =cite]
+ ==
+::
+:: $inline: a representation of text with or without formatting
+::
+:: @t: plain text
+:: %italics: italic text
+:: %bold: bold text
+:: %strike: strikethrough text
+:: %blockquote: blockquote surrounded content
+:: %inline-code: code formatting for small snippets
+:: %ship: a mention of a ship
+:: %block: link/reference to blocks
+:: %code: code formatting for large snippets
+:: %tag: tag gets special signifier
+:: %link: link to a URL with a face
+:: %break: line break
+::
++$ inline
+ $@ @t
+ $% [%italics p=(list inline)]
+ [%bold p=(list inline)]
+ [%strike p=(list inline)]
+ [%blockquote p=(list inline)]
+ [%inline-code p=cord]
+ [%ship p=ship]
+ [%block p=@ud q=cord]
+ [%code p=cord]
+ [%tag p=cord]
+ [%link p=cord q=cord]
+ [%break ~]
+ ==
+::
+:: $memo: a chat message with metadata
+::
+:: replying: what message we're replying to
+:: author: writer of the message
+:: sent: time (from sender) when the message was sent
+:: content: body of the message
+::
++$ memo
+ $: replying=(unit id)
+ author=ship
+ sent=time
+ =content
+ ==
+::
+:: $net: an indicator of whether I'm a host or subscriber
+::
+:: %load: iniating chat join
+:: %pub: am publisher/host with fresh log
+:: %sub: subscribed to the ship
+::
++$ net
+ $% [%sub host=ship load=_| =saga:e]
+ [%pub ~]
+ ==
+::
+:: $action: the complete set of data required to edit a chat
+::
++$ action
+ (pair flag update)
+::
+:: $update: a representation in time of a modification of a chat
+::
++$ update
+ (pair time diff)
+::
+:: $logs: a time ordered map of all modifications to groups
+::
++$ logs
+ ((mop time diff) lte)
+::
+:: $perm: represents the permissions for a channel and gives a pointer
+:: back to the group it belongs to.
+::
++$ perm
+ $: writers=(set sect:g)
+ group=flag:g
+ ==
+::
+:: $leave: a flag to pass for a channel leave
+::
++$ leave flag:g
+::
+:: $create: represents a request to create a channel
+::
+:: The name will be used as part of the flag which represents the
+:: channel. $create is consumed by the chat agent first
+:: and then passed to the groups agent to register the channel with
+:: the group.
+::
+:: Write permission is stored with the specific agent in the channel,
+:: read permission is stored with the group's data.
+::
++$ create
+ $: group=flag:g
+ name=term
+ title=cord
+ description=cord
+ readers=(set sect:g)
+ writers=(set sect:g)
+ ==
+--
+++ cite
+=< cite
+|%
+++ purse
+ |= =(pole knot)
+ ^- (unit cite)
+ ?. =(~.1 -.pole) ~
+ =. pole +.pole
+ ?+ pole ~
+ [%chan agent=@ ship=@ name=@ rest=*]
+ =/ ship (slaw %p ship.pole)
+ ?~ ship ~
+ `[%chan [agent.pole u.ship name.pole] rest.pole]
+ ::
+ [%desk ship=@ name=@ rest=*]
+ =/ ship (slaw %p ship.pole)
+ ?~ ship ~
+ `[%desk [u.ship name.pole] rest.pole]
+ ::
+ [%group ship=@ name=@ ~]
+ =/ ship (slaw %p ship.pole)
+ ?~ ship ~
+ `[%group u.ship name.pole]
+ ==
+++ parse
+ |= =path
+ ^- cite
+ (need (purse path))
+::
+++ print
+ |= c=cite
+ |^ ^- path
+ :- (scot %ud 1)
+ ?- -.c
+ %chan chan/(welp (nest nest.c) wer.c)
+ %desk desk/(welp (flag flag.c) wer.c)
+ %group group/(flag flag.c)
+ %bait bait/:(welp (flag grp.c) (flag gra.c) wer.c)
+ ==
+ ++ flag
+ |= f=flag:g
+ ~[(scot %p p.f) q.f]
+ ++ nest
+ |= n=nest:g
+ [p.n (flag q.n)]
+ --
+::
++$ cite
+ $% [%chan =nest:g wer=path]
+ [%group =flag:g]
+ [%desk =flag:g wer=path]
+ [%bait grp=flag:g gra=flag:g wer=path]
+ :: scry into groups when you receive a bait for a chat that doesn't exist yet
+ :: work out what app
+ ==
+--
+++ e
+|%
+:: $saga: version synchronisation state
+:: %dex: publisher is ahead
+:: %lev: we are ahead
+:: %chi: full sync
+::
++$ saga
+ $% [%dex ver=@ud]
+ [%lev ~]
+ [%chi ~]
+ ==
+
++$ epic @ud
+::
+--
+++ h
+|%
+:: $flag: identifier for a heap channel
++$ flag (pair ship term)
+:: $feel: either an emoji identifier like :wave or a URL for custom
++$ feel @ta
+:: $view: the persisted display format for a heap
++$ view ?(%grid %list)
+:: $stash: heaps I've joined
++$ stash (map flag heap)
+:: $said: used for curio references
++$ said (pair flag curio)
+::
+:: $heap: a collection of curiosities
+::
+:: net: an indicator of whether I'm a host or subscriber
+:: log: the history of all modifications
+:: perm: holds the heap's permissions
+:: view: what format to display
+:: curios: the actual contents of the heap
+:: remark: what is the last thing we've seen/read
+::
++$ heap
+ $: =net
+ =log
+ =perm
+ =view
+ =curios
+ =remark
+ ==
+:: $curios: a set of time ordered heap items
+::
+++ curios
+ =< curios
+ |%
+ +$ curios
+ ((mop time curio) lte)
+ ++ on
+ ((^on time curio) lte)
+ +$ diff
+ (pair time delta)
+ +$ delta
+ $% [%add p=heart]
+ [%edit p=heart]
+ [%del ~]
+ [%add-feel p=ship q=feel]
+ [%del-feel p=ship]
+ ==
+ --
+:: $curio: an item in the collection or a comment about an item
+::
++$ curio [seal heart]
+::
+:: $seal: the id of a curio and its meta-responses
+::
+:: time: the id of the curio
+:: feels: reactions to a curio
+:: replied: set of replies to a curio
+::
++$ seal
+ $: =time
+ feels=(map ship feel)
+ replied=(set time)
+ ==
+:: $heart: the curio data itself
+::
+:: title: name of the curio
+:: content: body of the curio
+:: author: writer of the curio
+:: sent: the client-side time the curio was made
+:: replying: what curio we're commenting on
+::
++$ heart
+ $: title=(unit @t)
+ =content
+ author=ship
+ sent=time
+ replying=(unit time)
+ ==
+:: $content: curio content
+::
++$ content
+ (pair (list block) (list inline))
+:: $block: block-level curio content
++$ block
+ $% [%image src=cord height=@ud width=@ud alt=cord]
+ [%cite =cite]
+ ==
+:: $inline: curio content that flows within a paragraph
+::
+:: @t: plain text
+:: %italics: italic text
+:: %bold: bold text
+:: %strike: strikethrough text
+:: %inline-code: code formatting for small snippets
+:: %blockquote: blockquote surrounded content
+:: %code: code formatting for large snippets
+:: %tag: tag gets special signifier
+:: %link: link to a URL with a face
+:: %break: line break
+::
++$ inline
+ $@ @t
+ $% [%italics p=(list inline)]
+ [%bold p=(list inline)]
+ [%strike p=(list inline)]
+ [%blockquote p=(list inline)]
+ [%inline-code p=cord]
+ [%ship p=ship]
+ [%code p=cord]
+ [%block p=@ud q=cord]
+ [%tag p=cord]
+ [%link p=cord q=cord]
+ [%break ~]
+ ==
+:: $log: a time ordered history of modifications to a heap
+::
++$ log
+ ((mop time diff) lte)
+++ log-on
+ ((on time diff) lte)
+::
+:: $action: the complete set of data required to modify a heap
+::
++$ action
+ (pair flag:g update)
+::
+:: $update: a representation in time of a modification to a heap
+::
++$ update
+ (pair time diff)
+::
+:: $diff: the full suite of modifications that can be made to a heap
+::
++$ diff
+ $% [%curios p=diff:curios]
+ ::
+ [%add-sects p=(set sect:g)]
+ [%del-sects p=(set sect:g)]
+ ::
+ [%create p=perm q=curios]
+ [%view p=view]
+ ==
+:: $net: an indicator of whether I'm a host or subscriber
+::
+:: %pub: am publisher/host with fresh log
+:: %sub: subscribed to the ship
+::
++$ net
+ $% [%sub p=ship load=_| =saga:e]
+ [%pub ~]
+ ==
+::
+:: $briefs: a map of heap unread information
+::
+:: brief: the last time a heap was read, how many items since,
+:: and the id of the last seen curio
+::
+++ briefs
+ =< briefs
+ |%
+ +$ briefs
+ (map flag brief)
+ +$ brief
+ [last=time count=@ud read-id=(unit time)]
+ +$ update
+ (pair flag brief)
+ --
+:: $remark: a marker representing the last note I've read
+::
++$ remark
+ [last-read=time watching=_| ~]
+::
++$ remark-action
+ (pair flag remark-diff)
+::
++$ remark-diff
+ $% [%read ~]
+ [%read-at p=time]
+ [?(%watch %unwatch) ~]
+ ==
+:: $perm: represents the permissions for a heap channel and gives a
+:: pointer back to the group it belongs to.
+::
++$ perm
+ $: writers=(set sect:g)
+ group=flag:g
+ ==
+:: $leave: a flag to pass for a channel leave
+::
++$ leave flag:g
+::
+:: $create: represents a request to create a channel
+::
+:: The name will be used as part of the flag which represents the
+:: channel. $create is consumed by the heap agent first and then
+:: passed to the groups agent to register the channel with the group.
+::
+:: Write permission is stored with the specific agent in the channel,
+:: read permission is stored with the group's data.
+::
++$ create
+ $: group=flag:g :: TODO: unmanaged-style group chats
+ name=term
+ title=cord
+ description=cord
+ readers=(set sect:g)
+ writers=(set sect:g)
+ ==
+--
+++ g
+|%
+:: $flag: ID for a group
+::
++$ flag (pair ship term)
+::
+:: $nest: ID for a channel, {app}/{ship}/{name}
+::
++$ nest (pair dude:gall flag)
+::
+:: $sect: ID for cabal, similar to a role
+::
++$ sect term
+::
+:: $zone: channel grouping
+::
+:: includes its own metadata for display and keeps the order of
+:: channels within.
+::
+:: zone: the term that represents the ID of a zone
+:: realm: the metadata representing the zone and the order of channels
+:: delta: the set of actions that can be taken on a zone
+:: %add: create a zone
+:: %del: delete the zone
+:: %edit: modify the zone metadata
+:: %mov: reorders the zone in the group
+:: %mov-nest: reorders a channel within the zone
+::
+++ zone
+ =< zone
+ |%
+ +$ zone @tas
+ +$ realm
+ $: met=data:meta
+ ord=(list nest)
+ ==
+ +$ diff (pair zone delta)
+ +$ delta
+ $% [%add meta=data:meta]
+ [%del ~]
+ [%edit meta=data:meta]
+ [%mov idx=@ud]
+ [%mov-nest =nest idx=@ud]
+ ==
+ --
+::
+:: $fleet: group members and their associated metadata
+::
+:: vessel: a user's set of sects or roles and the time that they joined
+:: @da default represents an admin added member that has yet to join
+::
+++ fleet
+ =< fleet
+ |%
+ +$ fleet (map ship vessel)
+ +$ vessel
+ $: sects=(set sect)
+ joined=time
+ ==
+ +$ diff
+ $% [%add ~]
+ [%del ~]
+ [%add-sects sects=(set sect)]
+ [%del-sects sects=(set sect)]
+ ==
+ --
+::
+:: $channel: a medium for interaction
+::
+++ channel
+ =< channel
+ |%
+ +$ preview
+ $: =nest
+ meta=data:meta
+ group=^preview
+ ==
+ ::
+ +$ channels (map nest channel)
+ ::
+ :: $channel: a collection of metadata about a specific agent integration
+ ::
+ :: meta: title, description, image, cover
+ :: added: when the channel was created
+ :: zone: what zone or section to bucket in
+ :: join: should the channel be joined by new members
+ :: readers: what sects can see the channel, empty means anyone
+ ::
+ +$ channel
+ $: meta=data:meta
+ added=time
+ =zone
+ join=?
+ readers=(set sect)
+ ==
+ ::
+ :: $diff: represents the set of actions you can take on a channel
+ ::
+ :: add: create a channel, should be called from agent
+ :: del: delete a channel
+ :: add-sects: add sects to readers
+ :: del-sects: delete sects from readers
+ :: zone: change the zone of the channel
+ :: join: toggle default join
+ ::
+ +$ diff
+ $% [%add =channel]
+ [%del ~]
+ ::
+ [%add-sects sects=(set sect)]
+ [%del-sects sects=(set sect)]
+ ::
+ [%zone =zone]
+ ::
+ [%join join=_|]
+ ==
+ --
+::
+:: $group: collection of people and the pathways in which they interact
+::
+:: group holds all data around members, permissions, channel
+:: organization, and its own metadata to represent the group
+::
++$ group
+ $: =fleet
+ cabals=(map sect cabal)
+ zones=(map zone realm:zone)
+ zone-ord=(list zone)
+ =bloc
+ =channels:channel
+ imported=(set nest)
+ =cordon
+ secret=?
+ meta=data:meta
+ ==
+::
+:: $cabal: metadata representing a $sect or role
+::
+++ cabal
+ =< cabal
+ |%
+ ::
+ +$ cabal
+ [meta=data:meta ~]
+ ::
+ +$ diff
+ $% [%add meta=data:meta]
+ [%del ~]
+ ==
+ --
+::
+:: $cordon: group entry and visibility permissions
+::
+++ cordon
+ =< cordon
+ |%
+ ::
+ :: $open: a group with open entry, only bans are barred entry
+ ::
+ ++ open
+ |%
+ :: $ban: set of ships and ranks/classes that are not allowed entry
+ ::
+ :: bans can either be done at the individual ship level or by the
+ :: rank level (comet/moon/etc.)
+ ::
+ +$ ban [ships=(set ship) ranks=(set rank:title)]
+ +$ diff
+ $% [%add-ships p=(set ship)]
+ [%del-ships p=(set ship)]
+ ::
+ [%add-ranks p=(set rank:title)]
+ [%del-ranks p=(set rank:title)]
+ ==
+ --
+ ::
+ :: $shut: a group with closed entry, everyone barred entry
+ ::
+ :: a shut cordon means that the group is closed, but still visible.
+ :: people may request entry and either be accepted or denied or
+ :: they may be invited directly
+ ::
+ :: ask: represents those requesting entry
+ :: pending: represents those who've been invited
+ ::
+ ++ shut
+ |%
+ +$ state [pend=(set ship) ask=(set ship)]
+ +$ kind ?(%ask %pending)
+ +$ diff
+ $% [%add-ships p=kind q=(set ship)]
+ [%del-ships p=kind q=(set ship)]
+ ==
+ --
+ ::
+ :: $cordon: a set of metadata to represent the entry policy for a group
+ ::
+ :: open: a group with open entry, only bans barred entry
+ :: shut: a group with closed entry, everyone barred entry
+ :: afar: a custom entry policy defined by another agent
+ ::
+ +$ cordon
+ $% [%shut state:shut]
+ [%afar =flag =path desc=@t]
+ [%open =ban:open]
+ ==
+ ::
+ :: $diff: the actions you can take on a cordon
+ ::
+ +$ diff
+ $% [%shut p=diff:shut]
+ [%open p=diff:open]
+ [%swap p=cordon]
+ ==
+ --
+::
+:: $bloc: superuser sects
+::
+:: sects in the bloc set are allowed to make modifications to the group
+:: and its various metadata and permissions
+::
+++ bloc
+ =< bloc
+ |%
+ +$ bloc (set sect)
+ +$ diff
+ $% [%add p=(set sect)]
+ [%del p=(set sect)]
+ ==
+ --
+::
+:: $diff: the general set of changes that can be made to a group
+::
++$ diff
+ $% [%fleet p=(set ship) q=diff:fleet]
+ [%cabal p=sect q=diff:cabal]
+ [%channel p=nest q=diff:channel]
+ [%bloc p=diff:bloc]
+ [%cordon p=diff:cordon]
+ [%zone p=diff:zone]
+ [%meta p=data:meta]
+ [%secret p=?]
+ [%create p=group]
+ [%del ~]
+ ==
+::
+:: $action: the complete set of data required to edit a group
+::
++$ action
+ (pair flag update)
+::
+:: $update: a representation in time of a modification of a group
+::
++$ update
+ (pair time diff)
+::
+:: $create: a request to make a group
+::
++$ create
+ $: name=term
+ title=cord
+ description=cord
+ image=cord
+ cover=cord
+ =cordon
+ members=(jug ship sect)
+ secret=?
+ ==
+::
++$ init [=time =group]
+::
++$ groups
+ (map flag group)
++$ net-groups
+ (map flag [net group])
+::
+:: $log: a time ordered map of all modifications to groups
+::
++$ log
+ ((mop time diff) lte)
+::
+++ log-on
+ ((on time diff) lte)
+::
+:: $net: an indicator of whether I'm a host or subscriber
+::
++$ net
+ $~ [%pub ~]
+ $% [%pub p=log]
+ [%sub p=time load=_| =saga:e]
+ ==
+::
+:: $join: a join request, can elect to join all channels
+::
++$ join
+ $: =flag
+ join-all=?
+ ==
+::
+:: $knock: a request to enter a closed group
+::
++$ knock flag
+::
+:: $progress: the state of a group join
+::
++$ progress
+ ?(%knocking %adding %watching %done %error)
+::
+:: $claim: a mark for gangs to represent a join in progress
+::
++$ claim
+ $: join-all=?
+ =progress
+ ==
+::
+:: $preview: the metadata and entry policy for a group
+::
++$ preview
+ $: =flag
+ meta=data:meta
+ =cordon
+ =time
+ secret=?
+ ==
+::
++$ previews (map flag preview)
+::
+:: $invite: a marker to show you've been invited to a group
+::
++$ invite (pair flag ship)
+::
+:: $gang: view of foreign group
+::
++$ gang
+ $: cam=(unit claim)
+ pev=(unit preview)
+ vit=(unit invite)
+ ==
+::
++$ gangs (map flag gang)
+--
+-- \ No newline at end of file
diff --git a/desk/sur/tlon/channels.hoon b/desk/sur/tlon/channels.hoon
new file mode 100644
index 0000000..c222d4c
--- /dev/null
+++ b/desk/sur/tlon/channels.hoon
@@ -0,0 +1,464 @@
+:: channels: message stream structures
+::
+:: four shapes that cross client-subscriber-publisher boundaries:
+:: - actions client-to-subscriber change requests (user actions)
+:: - commands subscriber-to-publisher change requests
+:: - updates publisher-to-subscriber change notifications
+:: - responses subscriber-to-client change notifications
+::
+:: --action--> --command-->
+:: client subscriber publisher
+:: <--response-- <--update--
+::
+:: local actions _may_ become responses,
+:: remote actions become commands,
+:: commands _may_ become updates,
+:: updates _may_ become responses.
+::
+/- g=tlon-groups, c=tlon-cite
+/+ mp=mop-extensions
+|%
++| %ancients
+::
+++ mar
+ |%
+ ++ act `mark`%channel-action
+ ++ cmd `mark`%channel-command
+ ++ upd `mark`%channel-update
+ ++ log `mark`%channel-logs
+ ++ not `mark`%channel-posts
+ --
+::
++| %primitives
+::
++$ flag (pair ship term)
++$ v-channels (map nest v-channel)
+++ v-channel
+ |^ ,[global local]
+ :: $global: should be identical between ships
+ ::
+ +$ global
+ $: posts=v-posts
+ order=(rev order=arranged-posts)
+ view=(rev =view)
+ sort=(rev =sort)
+ perm=(rev =perm)
+ ==
+ :: $window: sparse set of time ranges
+ ::
+ ::TODO populate this
+ +$ window (list [from=time to=time])
+ :: .window: time range for requested posts that we haven't received
+ :: .diffs: diffs for posts in the window, to apply on receipt
+ ::
+ +$ future
+ [=window diffs=(jug id-post u-post)]
+ :: $local: local-only information
+ ::
+ +$ local
+ $: =net
+ =log
+ =remark
+ =window
+ =future
+ ==
+ --
+:: $v-post: a channel post
+::
++$ v-post [v-seal (rev essay)]
++$ id-post time
++$ v-posts ((mop id-post (unit v-post)) lte)
+++ on-v-posts ((on id-post (unit v-post)) lte)
+++ mo-v-posts ((mp id-post (unit v-post)) lte)
+:: $v-reply: a post comment
+::
++$ v-reply [v-reply-seal (rev memo)]
++$ id-reply time
++$ v-replies ((mop id-reply (unit v-reply)) lte)
+++ on-v-replies ((on id-reply (unit v-reply)) lte)
+++ mo-v-replies ((mp time (unit v-reply)) lte)
+:: $v-seal: host-side data for a post
+::
++$ v-seal $+ channel-seal
+ $: id=id-post
+ replies=v-replies
+ reacts=v-reacts
+ ==
+:: $v-reply-seal: host-side data for a reply
+::
++$ v-reply-seal
+ $: id=id-reply
+ reacts=v-reacts
+ ==
+:: $essay: top-level post, with metadata
+::
++$ essay [memo =kind-data]
+:: $reply-meta: metadata for all replies
++$ reply-meta
+ $: reply-count=@ud
+ last-repliers=(set ship)
+ last-reply=(unit time)
+ ==
+:: $kind-data: metadata for a channel type's "post"
+::
++$ kind-data
+ $% [%diary title=@t image=@t]
+ [%heap title=(unit @t)]
+ [%chat kind=$@(~ [%notice ~])]
+ ==
+:: $memo: post data proper
+::
+:: content: the body of the comment
+:: author: the ship that wrote the comment
+:: sent: the client-side time the comment was made
+::
++$ memo
+ $: content=story
+ author=ship
+ sent=time
+ ==
+:: $story: post body content
+::
++$ story (list verse)
+:: $verse: a chunk of post content
+::
+:: blocks stand on their own. inlines come in groups and get wrapped
+:: into a paragraph
+::
++$ verse
+ $% [%block p=block]
+ [%inline p=(list inline)]
+ ==
+:: $listing: recursive type for infinitely nested <ul> or <ol>
++$ listing
+ $% [%list p=?(%ordered %unordered %tasklist) q=(list listing) r=(list inline)]
+ [%item p=(list inline)]
+ ==
+:: $block: post content that sits outside of the normal text
+::
+:: %image: a visual, we record dimensions for better rendering
+:: %cite: an Urbit reference
+:: %header: a traditional HTML heading, h1-h6
+:: %listing: a traditional HTML list, ul and ol
+:: %code: a block of code
+::
++$ block $+ channel-block
+ $% [%image src=cord height=@ud width=@ud alt=cord]
+ [%cite =cite:c]
+ [%header p=?(%h1 %h2 %h3 %h4 %h5 %h6) q=(list inline)]
+ [%listing p=listing]
+ [%rule ~]
+ [%code code=cord lang=cord]
+ ==
+:: $inline: post content that flows within a paragraph
+::
+:: @t: plain text
+:: %italics: italic text
+:: %bold: bold text
+:: %strike: strikethrough text
+:: %inline-code: code formatting for small snippets
+:: %blockquote: blockquote surrounded content
+:: %block: link/reference to blocks
+:: %code: code formatting for large snippets
+:: %tag: tag gets special signifier
+:: %link: link to a URL with a face
+:: %break: line break
+::
++$ inline $+ channel-inline
+ $@ @t
+ $% [%italics p=(list inline)]
+ [%bold p=(list inline)]
+ [%strike p=(list inline)]
+ [%blockquote p=(list inline)]
+ [%inline-code p=cord]
+ [%code p=cord]
+ [%ship p=ship]
+ [%block p=@ud q=cord]
+ [%tag p=cord]
+ [%link p=cord q=cord]
+ [%task p=?(%.y %.n) q=(list inline)]
+ [%break ~]
+ ==
+::
++$ kind ?(%diary %heap %chat)
+:: $nest: identifier for a channel
++$ nest [=kind =ship name=term]
+:: $view: the persisted display format for a channel
++$ view $~(%list ?(%grid %list))
+:: $sort: the persisted sort type for a channel
++$ sort $~(%time ?(%alpha %time %arranged))
+:: $arranged-posts: an array of postIds
++$ arranged-posts (unit (list time))
+:: $hidden-posts: a set of ids for posts that are hidden
++$ hidden-posts (set id-post)
+:: $post-toggle: hide or show a particular post by id
++$ post-toggle
+ $% [%hide =id-post]
+ [%show =id-post]
+ ==
+:: $react: either an emoji identifier like :diff or a URL for custom
++$ react @ta
++$ v-reacts (map ship (rev (unit react)))
+:: $scan: search results
++$ scan (list reference)
++$ reference
+ $% [%post =post]
+ [%reply =id-post =reply]
+ ==
+:: $said: used for references
++$ said (pair nest reference)
+:: $plan: index into channel state
+:: p: Post being referred to
+:: q: Reply being referred to, if any
+::
++$ plan
+ (pair time (unit time))
+::
+:: $net: subscriber-only state
+::
++$ net [p=ship load=_|]
+::
+:: $unreads: a map of channel unread information, for clients
+:: $unread: unread data for a specific channel, for clients
+:: recency: time of most recent message
+:: count: how many posts are unread
+:: unread-id: the id of the first unread top-level post
+:: threads: for each unread thread, the id of the first unread reply
+::
++$ unreads (map nest unread)
++$ unread
+ $: recency=time
+ count=@ud
+ unread-id=(unit id-post)
+ threads=(map id-post id-reply)
+ ==
+:: $remark: markers representing unread state
+:: last-read: time at which the user last read this channel
+:: watching: unused, intended for disabling unread accumulation
+:: unread-threads: threads that contain unread messages
+::
++$ remark [recency=time last-read=time watching=_| unread-threads=(set id-post)]
+::
+:: $perm: represents the permissions for a channel and gives a
+:: pointer back to the group it belongs to.
+::
++$ perm
+ $: writers=(set sect:g)
+ group=flag:g
+ ==
+::
+:: $log: a time ordered history of modifications to a channel
+::
++$ log ((mop time u-channel) lte)
+++ log-on ((on time u-channel) lte)
+::
+:: $create-channel: represents a request to create a channel
+::
+:: $create-channel is consumed by the channel agent first and then
+:: passed to the groups agent to register the channel with the group.
+::
+:: Write permission is stored with the specific agent in the channel,
+:: read permission is stored with the group's data.
+::
++$ create-channel
+ $: =kind
+ name=term
+ group=flag:g
+ title=cord
+ description=cord
+ readers=(set sect:g)
+ writers=(set sect:g)
+ ==
+:: $outline: abridged $post
+:: .replies: number of comments
+::
++$ outline
+ [replies=@ud replyers=(set ship) essay]
+::
+++ outlines
+ =< outlines
+ |%
+ +$ outlines ((mop time outline) lte)
+ ++ on ((^on time outline) lte)
+ --
+++ rev
+ |$ [data]
+ [rev=@ud data]
+::
+++ apply-rev
+ |* [old=(rev) new=(rev)]
+ ^+ [changed=& old]
+ ?: (lth rev.old rev.new)
+ &+new
+ |+old
+::
+++ next-rev
+ |* [old=(rev) new=*]
+ ^+ [changed=& old]
+ ?: =(+.old new)
+ |+old
+ &+old(rev +(rev.old), + new)
+::
++| %actions
+::
+:: some actions happen to be the same as commands, but this can freely
+:: change
+::
+::NOTE we might want to add a action-id=uuid to this eventually, threading
+:: that through all the way, so that an $r-channels may indicate what
+:: originally caused it
++$ a-channels
+ $% [%create =create-channel]
+ [%pin pins=(list nest)]
+ [%channel =nest =a-channel]
+ [%toggle-post toggle=post-toggle]
+ ==
++$ a-channel
+ $% [%join group=flag:g]
+ [%leave ~]
+ a-remark
+ c-channel
+ ==
+::
++$ a-remark
+ $~ [%read ~]
+ $% [%read ~]
+ [%read-at =time]
+ [%watch ~]
+ [%unwatch ~]
+ ==
+::
++$ a-post c-post
++$ a-reply c-reply
+::
++| %commands
+::
++$ c-channels
+ $% [%create =create-channel]
+ [%channel =nest =c-channel]
+ ==
++$ c-channel
+ $% [%post =c-post]
+ [%view =view]
+ [%sort =sort]
+ [%order order=arranged-posts]
+ [%add-writers sects=(set sect:g)]
+ [%del-writers sects=(set sect:g)]
+ ==
+::
++$ c-post
+ $% [%add =essay]
+ [%edit id=id-post =essay]
+ [%del id=id-post]
+ [%reply id=id-post =c-reply]
+ c-react
+ ==
+::
++$ c-reply
+ $% [%add =memo]
+ [%edit id=id-reply =memo]
+ [%del id=id-reply]
+ c-react
+ ==
+::
++$ c-react
+ $% [%add-react id=@da p=ship q=react]
+ [%del-react id=@da p=ship]
+ ==
+::
++| %updates
+::
++$ update [=time =u-channel]
++$ u-channels [=nest =u-channel]
++$ u-channel
+ $% [%create =perm]
+ [%order (rev order=arranged-posts)]
+ [%view (rev =view)]
+ [%sort (rev =sort)]
+ [%perm (rev =perm)]
+ [%post id=id-post =u-post]
+ ==
+::
++$ u-post
+ $% [%set post=(unit v-post)]
+ [%reacts reacts=v-reacts]
+ [%essay (rev =essay)]
+ [%reply id=id-reply =u-reply]
+ ==
+::
++$ u-reply
+ $% [%set reply=(unit v-reply)]
+ [%reacts reacts=v-reacts]
+ ==
+::
++$ u-checkpoint global:v-channel
+::
++| %responses
+::
++$ r-channels [=nest =r-channel]
++$ r-channel
+ $% [%posts =posts]
+ [%post id=id-post =r-post]
+ [%order order=arranged-posts]
+ [%view =view]
+ [%sort =sort]
+ [%perm =perm]
+ ::
+ [%create =perm]
+ [%join group=flag:g]
+ [%leave ~]
+ a-remark
+ ==
+::
++$ r-post
+ $% [%set post=(unit post)]
+ [%reply id=id-reply =reply-meta =r-reply]
+ [%reacts =reacts]
+ [%essay =essay]
+ ==
+::
++$ r-reply
+ $% [%set reply=(unit reply)]
+ [%reacts =reacts]
+ ==
+:: versions of backend types with their revision numbers stripped,
+:: because the frontend shouldn't care to learn those.
+::
++$ channels (map nest channel)
+++ channel
+ |^ ,[global local]
+ +$ global
+ $: =posts
+ order=arranged-posts
+ =view
+ =sort
+ =perm
+ ==
+ ::
+ +$ local
+ $: =net
+ =remark
+ ==
+ --
++$ paged-posts
+ $: =posts
+ newer=(unit time)
+ older=(unit time)
+ total=@ud
+ ==
++$ posts ((mop id-post (unit post)) lte)
++$ post [seal essay]
++$ seal
+ $: id=id-post
+ =reacts
+ =replies
+ =reply-meta
+ ==
++$ reacts (map ship react)
++$ reply [reply-seal memo]
++$ replies ((mop id-reply reply) lte)
++$ reply-seal [id=id-reply parent-id=id-post =reacts]
+++ on-posts ((on id-post (unit post)) lte)
+++ on-replies ((on id-reply reply) lte)
++$ cite cite:c
+--
diff --git a/desk/sur/tlon/cite.hoon b/desk/sur/tlon/cite.hoon
new file mode 100644
index 0000000..1d92a46
--- /dev/null
+++ b/desk/sur/tlon/cite.hoon
@@ -0,0 +1,57 @@
+/- g=tlon-groups
+=< cite
+|%
+++ purse
+ |= =(pole knot)
+ ^- (unit cite)
+ ?. =(~.1 -.pole) ~
+ =. pole +.pole
+ ?+ pole ~
+ [%chan agent=@ ship=@ name=@ rest=*]
+ =/ ship (slaw %p ship.pole)
+ ?~ ship ~
+ `[%chan [agent.pole u.ship name.pole] rest.pole]
+ ::
+ [%desk ship=@ name=@ rest=*]
+ =/ ship (slaw %p ship.pole)
+ ?~ ship ~
+ `[%desk [u.ship name.pole] rest.pole]
+ ::
+ [%group ship=@ name=@ ~]
+ =/ ship (slaw %p ship.pole)
+ ?~ ship ~
+ `[%group u.ship name.pole]
+ ==
+::
+++ parse
+ |= =path
+ ^- cite
+ (need (purse path))
+::
+++ print
+ |= c=cite
+ |^ ^- path
+ :- (scot %ud 1)
+ ?- -.c
+ %chan chan/(welp (nest nest.c) wer.c)
+ %desk desk/(welp (flag flag.c) wer.c)
+ %group group/(flag flag.c)
+ %bait bait/:(welp (flag grp.c) (flag gra.c) wer.c)
+ ==
+ ++ flag
+ |= f=flag:g
+ ~[(scot %p p.f) q.f]
+ ++ nest
+ |= n=nest:g
+ [p.n (flag q.n)]
+ --
+::
++$ cite
+ $% [%chan =nest:g wer=path]
+ [%group =flag:g]
+ [%desk =flag:g wer=path]
+ [%bait grp=flag:g gra=flag:g wer=path]
+ :: scry into groups when you receive a bait for a chat that doesn't exist yet
+ :: work out what app
+ ==
+--
diff --git a/desk/sur/tlon/groups.hoon b/desk/sur/tlon/groups.hoon
new file mode 100644
index 0000000..cf03233
--- /dev/null
+++ b/desk/sur/tlon/groups.hoon
@@ -0,0 +1,383 @@
+|%
+++ epic
+|%
++$ saga
+ $~ [%lev ~]
+ $% [%dex ver=@ud]
+ [%lev ~]
+ [%chi ~]
+ ==
+
++$ epic @ud
+--
+++ meta
+|%
++$ data
+ $: title=cord
+ description=cord
+ image=cord
+ cover=cord
+ ==
++$ diff
+ $% [%title =cord]
+ [%description =cord]
+ [%image =cord]
+ [%cover =cord]
+ ==
+--
+++ e epic
+++ okay `epic:e`2
+++ mar
+ |%
+ ++ act `mark`(rap 3 %group-action '-' (scot %ud okay) ~)
+ ++ upd `mark`(rap 3 %group-update '-' (scot %ud okay) ~)
+ ++ log `mark`(rap 3 %group-log '-' (scot %ud okay) ~)
+ ++ int `mark`(rap 3 %group-init '-' (scot %ud okay) ~)
+ --
+:: $flag: ID for a group
+::
++$ flag (pair ship term)
+::
+:: $nest: ID for a channel, {app}/{ship}/{name}
+::
++$ nest (pair term flag)
+::
+:: $sect: ID for cabal, similar to a role
+::
++$ sect term
+::
+:: $zone: channel grouping
+::
+:: includes its own metadata for display and keeps the order of
+:: channels within.
+::
+:: zone: the term that represents the ID of a zone
+:: realm: the metadata representing the zone and the order of channels
+:: delta: the set of actions that can be taken on a zone
+:: %add: create a zone
+:: %del: delete the zone
+:: %edit: modify the zone metadata
+:: %mov: reorders the zone in the group
+:: %mov-nest: reorders a channel within the zone
+::
+++ zone
+ =< zone
+ |%
+ +$ zone @tas
+ +$ realm
+ $: met=data:meta
+ ord=(list nest)
+ ==
+ +$ diff (pair zone delta)
+ +$ delta
+ $% [%add meta=data:meta]
+ [%del ~]
+ [%edit meta=data:meta]
+ [%mov idx=@ud]
+ [%mov-nest =nest idx=@ud]
+ ==
+ --
+::
+:: $fleet: group members and their associated metadata
+::
+:: vessel: a user's set of sects or roles and the time that they joined
+:: @da default represents an admin added member that has yet to join
+::
+++ fleet
+ =< fleet
+ |%
+ +$ fleet (map ship vessel)
+ +$ vessel
+ $: sects=(set sect)
+ joined=time
+ ==
+ +$ diff
+ $% [%add ~]
+ [%del ~]
+ [%add-sects sects=(set sect)]
+ [%del-sects sects=(set sect)]
+ ==
+ --
+::
+:: $channel: a medium for interaction
+::
+++ channel
+ =< channel
+ |%
+ +$ preview
+ $: =nest
+ meta=data:meta
+ group=^preview
+ ==
+ ::
+ +$ channels (map nest channel)
+ ::
+ :: $channel: a collection of metadata about a specific agent integration
+ ::
+ :: meta: title, description, image, cover
+ :: added: when the channel was created
+ :: zone: what zone or section to bucket in
+ :: join: should the channel be joined by new members
+ :: readers: what sects can see the channel, empty means anyone
+ ::
+ +$ channel
+ $: meta=data:meta
+ added=time
+ =zone
+ join=?
+ readers=(set sect)
+ ==
+ ::
+ :: $diff: represents the set of actions you can take on a channel
+ ::
+ :: add: create a channel
+ :: edit: edit a channel
+ :: del: delete a channel
+ :: add-sects: add sects to readers
+ :: del-sects: delete sects from readers
+ :: zone: change the zone of the channel
+ :: join: toggle default join
+ ::
+ +$ diff
+ $% [%add =channel]
+ [%edit =channel]
+ [%del ~]
+ ::
+ [%add-sects sects=(set sect)]
+ [%del-sects sects=(set sect)]
+ ::
+ [%zone =zone]
+ ::
+ [%join join=_|]
+ ==
+ --
+::
+:: $group: collection of people and the pathways in which they interact
+::
+:: group holds all data around members, permissions, channel
+:: organization, and its own metadata to represent the group
+::
++$ group
+ $: =fleet
+ cabals=(map sect cabal)
+ zones=(map zone realm:zone)
+ zone-ord=(list zone)
+ =bloc
+ =channels:channel
+ imported=(set nest)
+ =cordon
+ secret=?
+ meta=data:meta
+ ==
+::
++$ group-ui [group saga=(unit saga:e)]
+:: $cabal: metadata representing a $sect or role
+::
+++ cabal
+ =< cabal
+ |%
+ ::
+ +$ cabal
+ [meta=data:meta ~]
+ ::
+ +$ diff
+ $% [%add meta=data:meta]
+ [%edit meta=data:meta]
+ [%del ~]
+ ==
+ --
+::
+:: $cordon: group entry and visibility permissions
+::
+++ cordon
+ =< cordon
+ |%
+ ::
+ :: $open: a group with open entry, only bans are barred entry
+ ::
+ ++ open
+ |%
+ :: $ban: set of ships and ranks/classes that are not allowed entry
+ ::
+ :: bans can either be done at the individual ship level or by the
+ :: rank level (comet/moon/etc.)
+ ::
+ +$ ban [ships=(set ship) ranks=(set rank:title)]
+ +$ diff
+ $% [%add-ships p=(set ship)]
+ [%del-ships p=(set ship)]
+ ::
+ [%add-ranks p=(set rank:title)]
+ [%del-ranks p=(set rank:title)]
+ ==
+ --
+ ::
+ :: $shut: a group with closed entry, everyone barred entry
+ ::
+ :: a shut cordon means that the group is closed, but still visible.
+ :: people may request entry and either be accepted or denied or
+ :: they may be invited directly
+ ::
+ :: ask: represents those requesting entry
+ :: pending: represents those who've been invited
+ ::
+ ++ shut
+ |%
+ +$ state [pend=(set ship) ask=(set ship)]
+ +$ kind ?(%ask %pending)
+ +$ diff
+ $% [%add-ships p=kind q=(set ship)]
+ [%del-ships p=kind q=(set ship)]
+ ==
+ --
+ ::
+ :: $cordon: a set of metadata to represent the entry policy for a group
+ ::
+ :: open: a group with open entry, only bans barred entry
+ :: shut: a group with closed entry, everyone barred entry
+ :: afar: a custom entry policy defined by another agent
+ ::
+ +$ cordon
+ $% [%shut state:shut]
+ [%afar =flag =path desc=@t]
+ [%open =ban:open]
+ ==
+ ::
+ :: $diff: the actions you can take on a cordon
+ ::
+ +$ diff
+ $% [%shut p=diff:shut]
+ [%open p=diff:open]
+ [%swap p=cordon]
+ ==
+ --
+::
+:: $bloc: superuser sects
+::
+:: sects in the bloc set are allowed to make modifications to the group
+:: and its various metadata and permissions
+::
+++ bloc
+ =< bloc
+ |%
+ +$ bloc (set sect)
+ +$ diff
+ $% [%add p=(set sect)]
+ [%del p=(set sect)]
+ ==
+ --
+::
+:: $diff: the general set of changes that can be made to a group
+::
++$ diff
+ $% [%fleet p=(set ship) q=diff:fleet]
+ [%cabal p=sect q=diff:cabal]
+ [%channel p=nest q=diff:channel]
+ [%bloc p=diff:bloc]
+ [%cordon p=diff:cordon]
+ [%zone p=diff:zone]
+ [%meta p=data:meta]
+ [%secret p=?]
+ [%create p=group]
+ [%del ~]
+ ==
+::
+:: $action: the complete set of data required to edit a group
+::
++$ action
+ (pair flag update)
+::
+:: $update: a representation in time of a modification of a group
+::
++$ update
+ (pair time diff)
+::
+:: $create: a request to make a group
+::
++$ create
+ $: name=term
+ title=cord
+ description=cord
+ image=cord
+ cover=cord
+ =cordon
+ members=(jug ship sect)
+ secret=?
+ ==
+::
++$ init [=time =group]
+::
+:: $groups-ui: map for frontend to display groups
++$ groups-ui
+ (map flag group-ui)
++$ groups
+ (map flag group)
++$ net-groups
+ (map flag [net group])
+::
+:: $log: a time ordered map of all modifications to groups
+::
++$ log
+ ((mop time diff) lte)
+::
+++ log-on
+ ((on time diff) lte)
+::
+:: $net: an indicator of whether I'm a host or subscriber
+::
++$ net
+ $~ [%pub ~]
+ $% [%pub p=log]
+ [%sub p=time load=_| =saga:e]
+ ==
+::
+:: $join: a join request, can elect to join all channels
+::
++$ join
+ $: =flag
+ join-all=?
+ ==
+::
+:: $knock: a request to enter a closed group
+::
++$ knock flag
+::
+:: $progress: the state of a group join
+::
++$ progress
+ ?(%knocking %adding %watching %done %error)
+::
+:: $claim: a mark for gangs to represent a join in progress
+::
++$ claim
+ $: join-all=?
+ =progress
+ ==
+::
+:: $preview: the metadata and entry policy for a group
+::
++$ preview
+ $: =flag
+ meta=data:meta
+ =cordon
+ =time
+ secret=?
+ ==
+::
++$ previews (map flag preview)
+::
+:: $invite: a marker to show you've been invited to a group
+::
++$ invite (pair flag ship)
+::
+:: $gang: view of foreign group
+::
++$ gang
+ $: cam=(unit claim)
+ pev=(unit preview)
+ vit=(unit invite)
+ ==
+::
++$ gangs (map flag gang)
+::
+--
diff --git a/desk/sur/trill/gate.hoon b/desk/sur/trill/gate.hoon
new file mode 100644
index 0000000..ddc0adb
--- /dev/null
+++ b/desk/sur/trill/gate.hoon
@@ -0,0 +1,26 @@
+|%
++$ gate
+$: =lock
+ begs=(set @p) :: follow requests
+ post-begs=(set post-beg) :: read requests for specific posts
+ :: TODO include whole thread?
+ mute=lock :: mute list to prevent request spamming
+ backlog=$~(50 @) :: size of backlog sent to followers by default
+==
++$ post-beg [=ship id=@da]
+
++$ lock
+$: rank=[caveats=(set rank:title) locked=_| public=?]
+ luk=[caveats=(set ship) locked=_| public=?]
+ ship=[caveats=(set ship) locked=_| public=?]
+ tags=[caveats=(set @t) locked=_| public=?]
+ custom=[fn=(unit $-(@p ?)) public=?]
+==
++$ change
+$% [%set-rank set=(set rank:title) locked=? public=?]
+ [%set-luk set=(set ship) locked=? public=?]
+ [%set-ship set=(set ship) locked=? public=?]
+ [%set-tags set=(set @t) locked=? public=?]
+ [%set-custom term] :: Handle this and set in hoon
+==
+--
diff --git a/desk/sur/trill/post.hoon b/desk/sur/trill/post.hoon
new file mode 100644
index 0000000..78c5e1a
--- /dev/null
+++ b/desk/sur/trill/post.hoon
@@ -0,0 +1,115 @@
+/- gate=trill-gate
+/+ mp=mop-extensions
+|%
++$ id @da
++$ pid [=ship =id]
+:: anon post type?
++$ tag @t
++$ gfeed ((mop pid post) ggth)
+++ ggth |=([[shipa=@p a=time] [shipb=@p b=time]] (gth a b))
+++ gorm ((on pid post) ggth)
+++ torm ((on pid thread) ggth)
+++ porm ((on pid pid) ggth)
+++ gorrm ((mp pid post) ggth)
++$ feed ((mop id post) gth)
+++ orm ((on id post) gth)
++$ full-graph ((mop pid full-node) ggth)
+++ form ((on pid full-node) ggth)
+:: instead of using this I'm just gonna jam old names
++$ perms [read=lock:gate write=lock:gate]
++$ cursor (unit id)
++$ page-req [newer=cursor older=cursor count=@ud]
++$ full-node [p=post children=$~(~ full-graph)]
++$ cpage [p=(list post) top=@ud bot=@ud]
++$ spage [p=(list post) page-req]
++$ page [p=(list full-node) page-req]
++$ pfilter $-(=post ?)
++$ tfilter $-(=thread ?)
++$ bfilter $-([=thread =post] ?) :: search filter
++$ tpage [p=(list thread) page-req]
++$ thread
+ $: =pid
+ replies=(list pid) :: key should be the head of this list
+ title=@t
+ =path
+ tags=(set tag)
+ snip=content-list
+ ==
++$ post
+ $: =id
+ title=@t
+ author=ship
+ thread=pid :: this is the key of the threads table!
+ parent=(unit pid)
+ children=(set pid)
+ contents=content-map
+ =perms
+ =engagement
+ =signature
+ tags=(set tag) ::TODO make sure it's not infinite
+ ==
+::
++$ signature [p=@uvH q=ship r=life]
++$ engagement
+ $:
+ =reacts quoted=(set signed-pid)
+ shared=(set signed-pid)
+ ==
++$ signed-pid [=signature =pid]
++$ react @t
++$ reacts (map ship signed-react)
++$ signed-react [p=react q=signature]
+
+
++$ content-map ((mop time content-list) gth)
+++ corm ((on time content-list) gth)
++$ heading $?(%h1 %h2 %h3 %h4 %h5 %h6)
++$ content-list (list block)
++$ paragraph (list inline)
+:: man tables are a rabbit hole. we'll get to it
+++ table
+|^ [%table headers=(list cell) rows=(list row)]
++$ row (list cell)
++$ cell content-list
+--
++$ clist [%list p=(list li) ordered=?]
++$ li content-list
++$ block
+ $% [%paragraph p=paragraph]
+ [%blockquote p=paragraph]
+ :: table
+ clist
+ [%heading p=cord q=heading]
+ [%media =media]
+ [%codeblock code=cord lang=cord]
+ [%eval hoon=cord]
+ [%tasklist p=(list task)]
+ ::
+ [%ref app=term =ship =path]
+ [%json origin=term content=@t]
+ ==
++$ task [p=paragraph q=?]
++$ poll-opt [option=cord votes=@]
++$ media
+ $% [%images p=(list [url=@t caption=@t])]
+ [%video p=cord]
+ [%audio p=cord]
+ ==
++$ inline
+ $% [%text p=cord]
+ [%italic p=cord]
+ [%bold p=cord]
+ [%strike p=cord]
+ [%codespan p=cord]
+ [%link href=cord show=cord]
+ [%break ~]
+ :: not strictly markdown
+ [%ship p=ship]
+ [%date p=@da]
+ [%note id=cord text=(list inline)] :: footnotes and so on
+ [%underline p=cord]
+ [%sup p=cord]
+ [%sub p=cord]
+ [%ruby p=cord q=cord]
+ ==
+--
diff --git a/desk/sur/twatter/json.hoon b/desk/sur/twatter/json.hoon
new file mode 100644
index 0000000..476508b
--- /dev/null
+++ b/desk/sur/twatter/json.hoon
@@ -0,0 +1,105 @@
+=, dejs-soft:format
+|%
+++ username
+%- ot :_ ~ :- 'data'
+%- ot :_ ~ :- 'user'
+%- ot :_ ~ :- 'result'
+%- ot
+ :~ :: :- '__typename' so
+ :: :- 'id' so
+ :- 'rest_id' so
+ :: :- 'profile_image_shape' so
+ :: :- 'is_profile-translatable' bo
+ :: :- 'is_blue_verified' bo
+ :: :- 'has_graduated_access' bo
+ :: :- 'is_profile-translatable' bo
+ :: :- 'creator_subscriptions_count' ni
+ :: :- 'legacy_extended_profile' so :: empty objects
+ :: :- 'verification_info' so
+ :: :- 'business_account' so
+ :: :- 'affiliates_highlighted_label' so
+ :: :- 'legacy' %- ot
+ :: :~ :- 'can-dm' bo
+ :: :- 'can-media-tag' bo
+ :: :- 'can-media-tag' bo
+ :: :- 'default_profile' bo
+ :: :- 'default_profile_image' bo
+ :: :- 'has_custom_timelines' bo
+ :: :- 'is_translator' bo
+ :: :- 'possibly_sensitive' bo
+ :: :- 'verified' bo
+ :: :- 'want_retweets' bo
+ :: :- 'fast_followers_count' ni
+ :: :- 'favourites_count' ni
+ :: :- 'followers_count' ni
+ :: :- 'friends_count' ni
+ :: :- 'listed_count' ni
+ :: :- 'media_count' ni
+ :: :- 'normal_followers_count' ni
+ :: :- 'statuses_count' ni
+ :: :- 'description' so
+ :: :- 'location' so
+ :: :- 'name' so
+ :: :- 'profile_banner_url' so
+ :: :- 'profile_image_url_https' so
+ :: :- 'screen_name' so
+ :: :- 'profile_interstitial_type' so :: mmm
+ :: :- 'translator_type' so
+ :: :- 'pinned_tweets_ids_str' (ar so)
+ :: :- 'withheld_in_countries' (ar so)
+ :: :- 'entities' %- ot
+ :: :~ :- 'description' %- ot
+ :: :~ :- 'urls' (ar so)
+ :: ==
+ :: ==
+ :: ==
+ ==
+++ user-tweets
+=, dejs-soft:format
+%- ot :_ ~ :- 'data'
+%- ot :_ ~ :- 'user'
+%- ot :_ ~ :- 'result'
+%- ot
+ :~ :- '__typename' so
+ :- 'timeline_v2' %- ot :_ ~
+ :- 'timeline' %- ot
+ :~ :- 'metadata' %- ot ~
+ :- 'instructions' (ar instruction)
+ ==
+ ==
+++ instruction
+|= jon=json
+ :: ?>(%o -.jon)
+ =/ type (~(got by p.jon) 'type')
+ ?: .=([%s 'TimelineClearCache'] type) ((ot ~) jon)
+ ?: .=([%s 'TimelinePinEntry'] type) ((ot timeline-pin-entry) jon)
+ ?: .=([%s 'TimelineAddEntries'] type) ((ot timeline-add-entries) jon)
+ !!
+
+:~ :- 'TimelinePinEntry' timeline-pin-entry
+ :- 'TimelineAddEntries' timeline-add-entries
+==
+
+++ timeline-pin-entry
+%- ot
+:~ :- 'type' so
+ :- 'entry' %- ot
+ :~ :- 'entryId' so
+ :- 'sortIndex' so
+ :- 'content' content
+ ==
+==
+++ timeline-add-entries
+so
+++ content
+%- ot
+:~ :- 'entryType' so
+ :- '__typename' so
+ :- 'itemContent' item-content
+ :- 'clientEventInfo' client-event-info
+==
+++ item-content
+so
+++ client-event-info
+so
+--
diff --git a/desk/sur/twatter/test.json b/desk/sur/twatter/test.json
new file mode 100644
index 0000000..ec747fa
--- /dev/null
+++ b/desk/sur/twatter/test.json
@@ -0,0 +1 @@
+null \ No newline at end of file