summaryrefslogtreecommitdiff
path: root/desk/app/boke.hoon
diff options
context:
space:
mode:
Diffstat (limited to 'desk/app/boke.hoon')
-rw-r--r--desk/app/boke.hoon1145
1 files changed, 1145 insertions, 0 deletions
diff --git a/desk/app/boke.hoon b/desk/app/boke.hoon
new file mode 100644
index 0000000..5b3231a
--- /dev/null
+++ b/desk/app/boke.hoon
@@ -0,0 +1,1145 @@
+/- *boke, tp=trill-post, c=tlon-channels, ch=tlon-chat, cnt=contact, pols=polls
+/+ dbug, js=json, plib=trill-utils, kaji, lib=boke, ui=trill-ui, mm=mmast, sr=sortug, const=constants, wall
+/= nav /web/layout/nav
+/= router /web/router
+|%
+++ page-size 10
++$ read-post
+$: title=@t
+ =path
+ contents=content-list:tp
+ date=@da
+ tags=(set @t)
+ id=@ud
+==
++$ read-comment
+$: author=@t
+ date=@da
+ contents=content-list:tp
+ pa=@t
+ parent=@da
+ post=@da
+ title=@t
+==
++$ read-boardpost
+$: author=@t
+ op=@t
+ thread=@da
+ date=@da
+ title=@t
+ content=content-list:tp
+ id=@ud
+ tid=@ud
+==
++$ versioned-state
+$% state-0
+==
++$ card card:agent:gall
+--
+%- agent:dbug
+=| state-0
+=* state -
+^- agent:gall
+=<
+|_ =bowl:gall
++* this .
+ hd ~(. +> bowl)
+ kaj (init:kaji bowl _state kaji-req)
+::
+++ on-fail |~(* `this)
+++ on-leave |~(* `this)
+++ on-save !>(state)
+++ on-init
+^- (quip card _this)
+=. state init-state:hd
+:_ this init-cards:hd
+
+++ on-load |= old=vase
+:_ this(state !<(versioned-state old)) ~
+++ on-watch
+|= =(pole knot)
+~& >> on-watch=pole
+ ?+ pole !!
+ [%ui ~] `this
+ [%http-response id=@ ~] `this
+ [%tv type=@t author=@t ~] `this
+ [%chat host=@t name=@t ~] `this
+ [%sse tab=@t ~] `this
+ ==
+++ on-poke
+|= [=mark =vase]
+?: (~(has in banned-ships:const) src.bowl) `this
+|^
+?+ mark `this
+%handle-http-request serve
+%noun (on-poke-noun !<(* vase))
+%kaji run:handle-ui
+==
+ ++ handle-ui
+ =/ fd !<(form-data:kaji vase)
+ =/ i input.fd
+ =/ tab-id (~(got by input.fd) 'tab')
+ =/ ui-card ~(. ui:kaji tab-id)
+ =/ origin (~(got by input.fd) 'origin')
+ |%
+ ++ run
+ ?+ action.fd `this
+ :: polls
+ %vote-poll
+ =/ soption (~(got by input.fd) 'option')
+ =/ uoption (dec:kaji soption ,[p=@p time=@da index=@ud])
+ ?~ uoption (send-error 'format error')
+ =/ =pid:tp [p.u.uoption time.u.uoption]
+ =/ index index.u.uoption
+ =. polls (vote-poll:hd pid index)
+ :_ this
+ %- route:kaj [route:router [%kaji tab-id %redi (~(got by input.fd) 'origin')] state]
+ :: radio
+ %add-radio-chat
+ =/ input (~(got by input.fd) 'input')
+ ?: .=('' input) (send-error 'no input')
+ =/ stype (~(got by input.fd) 'type')
+ ?: .=(stype 'urb')
+ =/ sowner (~(got by input.fd) 'owner')
+ =/ sip (slaw %p sowner)
+ ?~ sip ~& "error-parsing-radio-owner" `this
+ (radio-chat u.sip input)
+ ?: .=(stype 'our')
+ =/ station (~(got by input.fd) 'name')
+ (bstv-chat station input)
+ `this
+
+ %change-radio
+ =/ jon=json
+ [%s 'lol']
+
+ :_ this :_ ~
+ %+ ui-fact:kaji tab-id :_ ~
+ [%custom *manx jon]
+ %add-chat
+ :_ this :_ ~
+ =/ input (~(get by input.fd) 'input')
+ ?~ input (alert:ui-card "No input, please report to site@spandrell.ch or ~docteg-mothep")
+ =/ parsed (text-to-content:hd u.input)
+ ?~ parsed (alert:ui-card "Text parsing failed, please report to site@spandrel.ch or ~docteg-mothep")
+ (send-message:hd u.parsed)
+ %add-thread
+ =/ is-blog ?= ^ (~(get by input.fd) 'is-blog')
+ =/ is-edit ?= ^ (~(get by input.fd) 'editing')
+ =/ try-title ?: is-blog validate-blog validate-thread
+ ?: ?=(%err -.try-title) (send-error +.try-title)
+ =/ [title=@t tags=(set @t)] +.try-title
+ =/ try-post (post-or-edit tags)
+ ?: ?=(%err -.try-post)
+ (send-error +.try-post)
+ (go-save-thread title +.try-post is-blog is-edit)
+ %add-reply
+ =/ is-blog ?= ^ (~(get by input.fd) 'is-blog')
+ =/ parents (dec-i:kaji i 'parents' ,[pid:tp pid:tp])
+ ?~ parents (send-error 'parent cue error, please report to ~docteg-mothep or site@spandrell.ch')
+ =/ try-post (post-or-edit ~)
+ ?: ?=(%err -.try-post)
+ (send-error +.try-post)
+ (go-save-reply +.try-post -.u.parents +.u.parents is-blog)
+
+ %del-thread
+ =/ is-blog ?= %~ (~(get by input.fd) 'is-blog')
+ =/ pid (dec-i:kaji input.fd 'pid' pid:tp)
+ ?~ pid (send-error 'no id to delete')
+ =/ ted (get:torm:tp threads u.pid)
+ ?~ ted (send-error 'thread-not-found')
+ =. feed +:(del:gorm:tp feed u.pid)
+ =. threads +:(del:torm:tp threads u.pid)
+ =/ last-reply ?~ replies.u.ted u.pid i.replies.u.ted
+ =. active-threads
+ +:(del:porm:tp active-threads last-reply)
+ =. feed +:(del:gorm:tp feed u.pid)
+ =. paths (~(del by paths) path.u.ted)
+ =. tags (del-in-tags:hd tags.u.ted u.pid)
+ :_ this
+ ?: is-blog
+ %- route:kaj [route:router [%kaji tab-id %redi '/'] state]
+ %- route:kaj [route:router [%kaji tab-id %redi '/board'] state]
+
+ %del-reply
+ =/ pid (dec-i:kaji input.fd 'pid' pid:tp)
+ ?~ pid (send-error 'no id to delete')
+ =/ p (get:gorm:tp feed u.pid)
+ ?~ p (send-error 'post-not-found')
+ ?~ parent.u.p (send-error 'comment parent not set')
+ =. feed +:(del:gorm:tp feed u.pid)
+ =/ par (get:gorm:tp feed u.parent.u.p)
+ =. feed ?~ par feed
+ =/ nc (~(del in children.u.par) u.pid)
+ =/ np u.par(children nc)
+ (put:gorm:tp feed u.parent.u.p np)
+ :: branch whether it's the last reply or not as it affects the active-threads
+ =/ ted (get:torm:tp threads thread.u.p)
+ ?~ ted (send-error 'thread-not-found')
+ =/ nr %+ skip replies.u.ted |= p=pid:tp .=(u.pid p)
+ ?~ replies.u.ted (send-error 'weird')
+ =/ last i.replies.u.ted
+ =/ is-last=? .=(last u.pid)
+ ?. is-last
+ =/ nted u.ted(replies nr)
+ =. threads (put:torm:tp threads thread.u.p nted)
+ :_ this
+ %- route:kaj [route:router [%kaji tab-id %redi (~(got by input.fd) 'origin')] state]
+ ::
+ =/ nr t.replies.u.ted
+ =/ nted u.ted(replies nr)
+ =/ penultimo ?~ nr thread.u.p i.nr
+ =. threads (put:torm:tp threads thread.u.p nted)
+ =. active-threads +:(del:porm:tp active-threads u.pid)
+ =. active-threads (put:porm:tp active-threads penultimo thread.u.p)
+ :_ this
+ %- route:kaj [route:router [%kaji tab-id %redi (~(got by input.fd) 'origin')] state]
+ ==
+ ++ validate-blog
+ =/ stitle (~(get by input.fd) 'title')
+ ?~ stitle :- %err 'Please enter a title for the thread'
+ ?: .=('' u.stitle) :- %err 'Please enter a title for the thread'
+ =/ stags (~(get by input.fd) 'tags')
+ ?~ stags :- %err 'No tags, please report to ~docteg-mothep or site@spandrell.ch'
+ =/ ntags (parse-tags:ui u.stags)
+ ?~ ntags :- %err 'Error parsing tags, please report'
+ =/ ttags (~(put in u.ntags) 'blog')
+ =. ttags (~(del in ttags) '')
+ [%ok u.stitle ttags]
+
+ ++ validate-thread
+ =/ stitle (~(get by input.fd) 'title')
+ ?~ stitle :- %err 'Please enter a title for the thread'
+ ?: .=('' u.stitle) :- %err 'Please enter a title for the thread'
+ =/ sboard (~(get by input.fd) 'board')
+ ?~ sboard :- %err 'No board, please report'
+ =/ stags (~(get by input.fd) 'tags')
+ ?~ stags :- %err 'No tags, please report to ~docteg-mothep or site@spandrell.ch'
+ =/ ntags (parse-tags:ui u.stags)
+ ?~ ntags :- %err 'Error parsing tags, please report'
+ =/ board (~(get by categories:const) u.sboard)
+ ?~ board :- %err 'No category, please report'
+ =/ ttags (~(put in u.ntags) u.sboard)
+ =. ttags (~(del in ttags) '')
+ [%ok u.stitle ttags]
+
+ ++ post-or-edit
+ |= ntags=(set @t)
+ =/ stext (~(get by input.fd) 'text')
+ =/ upoll find-poll
+ =/ cl (build-content stext upoll)
+ ?: ?=(%err -.cl) cl
+ =/ sedit (~(get by input.fd) 'editing')
+ ?~ sedit :- %ok
+ :: TODO title!
+ (build-post:lib +.cl ntags [src.bowl now.bowl])
+
+ =/ edit-pid (dec:kaji u.sedit pid:tp)
+ ?~ edit-pid :- %err 'Edit cue error, please report to ~docteg-mothep or site@spandrell.ch'
+ =/ pedit (get:gorm:tp feed u.edit-pid)
+ ?~ pedit :- %err 'Post to edit not found, please report to ~docteg-mothep or site@spandrell.ch'
+ :- %ok (edit-post:lib u.pedit +.cl ntags now.bowl)
+ ::
+ ++ build-content
+ |= [text=(unit @t) poll=(unit poll:pols)] ^- (maybe:sr content-list:tp)
+ ?~ text
+ ?~ poll [%err 'no content!']
+ :: only poll
+ =/ cl=content-list:tp (insert-poll:hd ~ u.poll) [%ok cl]
+ ?: .=(u.text '') ?~ poll [%err 'no content!']
+ =/ cl=content-list:tp (insert-poll:hd ~ u.poll) [%ok cl]
+ :: have text
+ =/ contents (tokenize:ui u.text)
+ ?~ contents :- %err 'Parsing error, please report to ~docteg-mothep or site@spandrell.ch'
+ ?~ poll [%ok contents]
+ =/ cl=content-list:tp
+ (insert-poll:hd contents u.poll) [%ok cl]
+
+ ++ send-error
+ |= [text=@t]
+ :_ this :_ ~
+ =/ =manx ;span:"{(trip text)}"
+ %+ ui-fact:kaji tab-id
+
+ =/ div (~(get by input.fd) 'error-div')
+ :_ ~
+ ?~ div
+ [%alert manx 1.000]
+ [%swap manx u.div .y]
+ ::
+ ++ go-save-thread
+ |= [title=@t p=post:tp is-blog=? is-edit=?]
+ =/ s (save-thread:hd title p is-blog is-edit)
+ ?: ?=(%err -.s) (send-error +.s)
+ =. state +.s
+ =/ upoll find-poll
+ =. polls ?~ upoll polls (save-poll:hd u.upoll)
+ :_ this
+ %- route:kaj [route:router [%kaji tab-id %board-redi p] state]
+ :: TODO notify mentions
+ :: would need an agent for remote hark tho
+ ++ go-save-reply
+ |= [p=post:tp thread=pid:tp parent=pid:tp is-blog=?]
+ =. p (set-parents:lib p thread parent)
+ =/ s (save-reply:hd p thread parent is-blog)
+ ?: ?=(%err -.s) (send-error +.s)
+ =. state +.s
+ =/ upoll find-poll
+ =. polls ?~ upoll polls (save-poll:hd u.upoll)
+ :_ this
+ ?: is-blog
+ %- route:kaj [route:router [%kaji tab-id %blog-redi p] state]
+ %- route:kaj [route:router [%kaji tab-id %board-redi p] state]
+ :: TODO notify ships of their replies
+ ::
+ ++ bstv-chat
+ |= [name=@t input=@t]
+ =/ tower (~(get by here.tv) name)
+ ?~ tower `this
+ =/ contents (tokenize:ui input)
+ =/ post (build-post:lib contents ~ [src.bowl now.bowl])
+ =/ station (~(get by schedule.u.tower) started.current.u.tower)
+ ?~ station `this
+ =. chat.u.station [post chat.u.station]
+ =. schedule.u.tower (~(put by schedule.u.tower) started.current.u.tower u.station)
+ =. here.tv (~(put by here.tv) name u.tower)
+ :_ this
+ %- route:kaj [route:router [%kaji tab-id %tv-chat name post] state]
+ ++ radio-chat
+ |= [p=@p t=@t]
+ =/ tower (~(get by urbit.tv) p)
+ ?~ tower `this
+ `this
+
+ ::
+ ++ find-poll ^- (unit poll:pols)
+ :: =/ wal ~(. wall src.bowl)
+ :: ?~ (subscription-type:wal now.bowl) ~
+ =/ stitle (~(get by input.fd) 'poll-title')
+ ?~ stitle ~
+ ?: .=('' u.stitle) ~
+ =/ opts=(list @t)
+ =| acc=(list @t) =| i=@ud
+ |-
+ =/ key %- crip "poll-opt-{(scow %ud i)}"
+ =/ opt (~(get by input.fd) key)
+ ?~ opt (flop acc)
+ =/ nacc [u.opt acc]
+ $(i +(i), acc nacc)
+ ?~ opts ~
+ =/ smin-bet (~(get by input.fd) 'poll-min-bet')
+ ?~ smin-bet ~
+ :: =/ min-bet ~
+ :: TODO parse this
+ =/ sexpiry (~(get by input.fd) 'poll-expiry')
+ :: ?~ sexpiry ~
+ =/ expiry ?~ sexpiry (add now.bowl ~d1)
+ =/ parsed (rush u.sexpiry html-datetime:parsing:sr)
+ ?~ parsed ~& "some weird html fuckery" (add now.bowl ~d1)
+ u.parsed
+
+ =/ multiple=?
+ =/ smultiple (~(get by input.fd) 'poll-multiple')
+ :: actually this doesn't get sent at all if unchecked?
+ ?~ smultiple .n ?: .=(u.smultiple 'on') .y .n
+
+ %- some %- new-poll:pols :* [src.bowl now.bowl] u.stitle expiry opts multiple ==
+
+ --
+
+
+
+ ::
+ ++ on-poke-noun
+ |= a=*
+ ?: ?=([%data-dump *] a) (sync-dump +.a)
+ ?: ?=(%gib a) give-batdev
+ ?> .=(src.bowl our.bowl)
+ :: ?: ?=(%sync a) poke-tasdev
+ ?: ?=(%tv-fix a) tv-fix
+ ?: ?=(%backup a) :_ this :_ ~ dump-state:hd
+ ?: ?=(%restore a) :- ~ this(state restore:hd)
+ ?: ?=(%sync-last a) :- ~ this(state read-last:hd)
+ ?: ?=(%kaji a) print-kaji
+ ?: ?=(%kick a) kick-kaji
+ :: loading blog data
+ ?: ?=(%rblog a) %- wrap load-blog:hd
+ ?: ?=(%rblog2 a) %- wrap load-blog-comments:hd
+ :: loading tianming data
+ ?: ?=(%rboard a) %- wrap load-tianming
+ ?: ?=(%rboard2 a) %- wrap load-tianming-replies
+ :: ?: ?=(%count a) log
+ ?: ?=(%check a) check
+ ?: ?=(%threads a) check-threads
+ ?: ?=(%athreads a) check-athreads
+ ?: ?=(%tags a) check-tags
+ ?: ?=([%tag tag=@t] a) (check-tag tag.a)
+ :: ?: ?=(%chcom a) check-comment
+ :: ?: ?=(%sample a) sample
+ ?: ?=(%pats a) pats
+ ?: ?=(%feed a) check-feed
+ ?: ?=(%radio a) get-radio
+ ?: ?=(%tw a) try-tw
+ ?: ?=([%check-thread *] a) (check-thread +.a)
+ :: fixes
+ ?: ?=([%fix-thread *] a) (fix-thread +.a)
+ ?: ?=([%dedup *] a) (dedup +.a)
+ ?: ?=([%scrub-ded *] a) (scrub +.a)
+ ?: ?=([%fix-active *] a) (fix-active +.a)
+
+ ?: ?=(%sqlite a) dump-to-sqlite
+ ?: ?=(%licki a) init-lick
+ ?: ?=(%slick a) stop-lick
+ ?: ?=([%lick *] a) (send-lick +.a)
+ `this
+ ::
+ ++ init-lick
+ ~& "init lick"
+ :_ this :_ ~
+ =/ note=note-arvo [%l %spin /'licker.sock']
+ [%pass /lick/init %arvo note]
+ ++ stop-lick
+ :_ this :_ ~
+ =/ note=note-arvo [%l %shut /'licker.sock']
+ [%pass /lick/init %arvo note]
+ ++ send-lick
+ |= a=*
+ :_ this :_ ~
+ =/ note=note-arvo [%l %spit /'licker.sock' %noun a]
+ [%pass /lick/init %arvo note]
+
+ :: ++ insert-sql
+ :: """
+ :: INSERT INTO posts(ts,author_id, title, content, snippet, url)
+ :: VALUES({tsn}, {author-idn}, {title}, {content}, {snippet}, {url});
+ :: """
+ :: ++ insert-sql2
+ :: """
+ :: INSERT INTO comments(ts,user_id, content, approved, comment_type, post_id, parent_id)
+ :: VALUES({tsn}, {user-idn}, {content}, {approved-n}, {type}, {post-idn}, {parent-idn});
+ :: """
+ :: ++ insert-sql3
+ :: """
+ :: INSERT INTO tags(tag)
+ :: VALUES({tsn}, {user-idn}, {content}, {approved-n}, {type}, {post-idn}, {parent-idn});
+ :: """
+ ++ dump-to-sqlite
+ :: posts
+ =/ count 0
+ :: =/ l (tap:gorm:tp feed)
+ :: =/ res
+ :: |-
+ :: ?~ l ~
+ :: ?: (gte count 20) ~
+ :: =/ entry i.l
+ :: =/ poast val.entry
+ :: =/ is-blog (~(has in tags.poast) 'blog')
+ :: ?: is-blog
+ :: ~& >> poast=poast
+ :: =. count +(count)
+ :: $(l t.l)
+ :: $(l t.l)
+ :: threads
+ =/ l (tap:torm:tp threads)
+ =/ res
+ |-
+ ?~ l ~
+ ?: (gte count 20) ~
+ =/ entry i.l
+ =/ ted val.entry
+ =/ is-blog (~(has in tags.ted) 'blog')
+ ?: is-blog
+ ~& >> ted=ted
+ =. count +(count)
+ $(l t.l)
+ $(l t.l)
+ :: TODO prov
+ :: =/ tv-chat
+ :: =/ tvs (~(got by here.tv) 'spandrell-tv')
+ :: =/ sta (~(got by schedule.tvs) started.current.tvs)
+ :: chat.sta
+ :: =/ data=dump-type [(tap:gorm:tp feed) (tap:torm:tp threads) active-threads paths tags tv-chat]
+ :: =/ vase !>((jam data))
+ :: =/ =soba:clay :_ ~ [/data/site-dump/(scot %da now.bowl)/jam %ins %noun vase]
+ :: =/ =nori:clay [%& soba]
+ :: =/ =task:clay [%info %blog nori]
+ :: init
+ :: =/ note=note-arvo [%l %spit /'licker.sock' %noun 'ligma']
+
+ :: [%pass /lick/spit %arvo note]
+ `this
+ ::
+ ++ sync-dump
+ |= a=*
+ ~& dump-received=src.bowl
+ ?> .=(our.bowl ~batdev-docteg-mothep)
+ =/ data=dump-type (dump-type a)
+ =. feed (gas:gorm:tp *gfeed:tp feed.data)
+ =. threads (gas:torm:tp *threadsf threads.data)
+ =. active-threads at.data
+ =. paths paths.data
+ =. tags tt.data
+ `this
+ ++ give-batdev
+ ~& sync-req-received=src.bowl
+ ?> .=(our.bowl ~tasdev-docteg-mothep)
+ ?> .=(src.bowl ~batdev-docteg-mothep)
+ ~& sync-req-received=src.bowl
+ =/ tv-chat
+ =/ tvs (~(got by here.tv) 'spandrell-tv')
+ =/ sta (~(got by schedule.tvs) started.current.tvs)
+ chat.sta
+ ~& "tv"
+ =/ data=dump-type [(tap:gorm:tp feed) (tap:torm:tp threads) active-threads paths tags tv-chat]
+ :_ this :_ ~
+ [%pass /gib %agent [~batdev-docteg-mothep %boke] %poke %noun !>([%data-dump data])]
+ ++ poke-tasdev
+ ~& "poking tasdev"
+ :_ this :_ ~
+ [%pass /gib %agent [~tasdev-docteg-mothep %boke] %poke %noun !>(%gib)]
+ ++ tv-fix
+ =/ muh (~(got by here.tv) 'Spandrell TV')
+ =. here.tv (~(del by here.tv) 'Spandrell TV')
+ =. here.tv (~(put by here.tv) 'spandrell-tv' muh)
+ `this
+ ++ fix-thread |= a=*
+ =/ =pid:tp ;; pid:tp a
+ =/ thread (got:torm:tp threads pid)
+ =/ post (got:gorm:tp feed pid)
+ =/ fn=full-node:tp (node-to-full:plib post feed)
+ =/ flat (flatten-fn:plib fn)
+ =/ pids %+ turn flat |= p=post:tp [author.p id.p]
+ =/ ordered %+ sort pids |= [a=pid:tp b=pid:tp] (gth id.a id.b)
+ =. replies.thread ordered
+ =. threads (put:torm:tp threads pid thread)
+ `this
+ ++ fix-active |= a=*
+ =/ =pid:tp ;; pid:tp a
+ =. active-threads +:(del:porm:tp active-threads pid)
+ `this
+ ++ check-thread |= a=*
+ =/ =pid:tp ;; pid:tp a
+ =/ ted (get:torm:tp threads pid)
+ ~& >> ted=ted
+ `this
+ ++ scrub |= a=*
+ =/ =pid:tp ;; pid:tp a
+ =/ ted (got:torm:tp threads pid)
+ ~& >> ted=ted
+ =. replies.ted
+ =/ l replies.ted
+ =| nl=(list pid:tp)
+ |-
+ ?~ l (flop nl)
+ =/ muh i.l
+ =/ pst (get:gorm:tp feed muh)
+ ?~ pst $(l t.l)
+ $(l t.l, nl [muh nl])
+ =. threads (put:torm:tp threads pid ted)
+ `this
+ ++ dedup |= a=*
+ =/ =pid:tp ;; pid:tp a
+ =/ ted (got:torm:tp threads pid)
+ ~& >> ted=ted
+ =. replies.ted
+ =/ l replies.ted
+ =| nl=(list pid:tp)
+ |-
+ ?~ l (flop nl)
+ =/ muh i.l
+ =/ pidset (silt t.l)
+ ?: (~(has in pidset) muh) $(l t.l)
+ $(l t.l, nl [muh nl])
+ ~& new=replies.ted
+ `this
+++ try-tw
+ :_ this :_ ~
+ [%pass /tw-test %arvo %k %fard q.byk.bowl %tw %noun !>([%user 'spandrell4'])]
+ ++ get-radio
+ :_ this :_ ~ watch-radio-card:hd
+ ++ print-kaji
+ =/ a %+ turn ~(tap by sup.bowl)
+ |= [=duct [=ship =path]]
+ ~& > duct
+ ~& >>> ship
+ ~& >> path
+ duct
+ `this
+ ++ wrap
+ |= s=_state =. state s `this
+ ++ kick-kaji
+ :: =/ cards %+ turn ~(tap by sup.bowl)
+
+ :: |= [=duct [=ship =path]]
+ :: ~& > duct
+ :: ~& >>> ship
+ :: ~& >> path
+ =/ paths=(list path) ~[/ui /sse/(scot %p our.bowl)]
+ =/ cards :_ ~ [%give %kick paths ~]
+ ~& kicking=cards
+ [cards this]
+ ++ check-feed
+ ~& %+ roll (tap:gorm:tp feed) |= [[=pid:tp =post:tp] a=(list pid:tp)]
+ ?. (~(has in tags.post) 'blog') a [pid a]
+ `this
+ ++ pats
+ ~& > paths
+ `this
+ ++ check-tags
+ ~& tags=tags
+ `this
+ ++ check-tag
+ |= t=@t
+ :: ~& (~(get by tags) t)
+ =/ pids (~(get by tags) t)
+ ?~ pids ~& "not found" `this
+ =/ a %+ turn (tap:porm:tp u.pids) |= [=pid:tp p=pid:tp]
+ =/ ted (get:torm:tp threads pid)
+ ?~ ted ~& ted-not-found=pid ~
+ ~& > thread=[title.u.ted]
+ ~
+ `this
+ ++ check
+ =/ last (pry:gorm:tp feed)
+ ~& > last=last
+ `this
+ ++ check-threads
+ =/ t %+ turn (scag 10 (tap:torm:tp threads)) |= [pid:tp thread:tp]
+ ~& > thread=[title path (lent replies)] ~
+ `this
+ ++ check-athreads
+ =/ t %+ turn (scag 2 (tap:torm:tp threads)) |= [pid:tp thread:tp]
+ ~& > thread=[title (lent replies)] ~
+ `this
+ ++ check-comment
+ :: =/ last (pry:orm:tp blog)
+ :: ?~ last `this
+ :: ?~ parent.val.u.last `this
+ :: =/ parent (get:orm:tp blog u.parent.val.u.last)
+ :: ~& > last=last
+ :: ~& >> parent=parent
+ `this
+ ++ sample
+ :: ~& >> %+ scag 3 %+ skim (tap:orm:tp blog)
+ :: |= [=id:tp =post:tp] ?=(%~ parent.post)
+ `this
+ ++ log
+ :: ~& %- lent (tap:orm:tp blog)
+ `this
+ ++ load-tianming
+ =/ j .^(json %cx /(scot %p our.bowl)/blog/(scot %da now.bowl)/data/threads/json)
+ =/ jjj (boardpost:de:js j)
+ |-
+ ?~ jjj state
+ =/ a=read-boardpost i.jjj
+ =/ author ?: .=((cass (trip author.a)) "spandrell") ~docteg-mothep
+ (add (bex 64) author.a)
+ =/ pid [author date.a]
+ =/ c (put:corm:tp *content-map:tp date.a content.a)
+ =| p=post:tp
+ =. p %= p
+ id date.a
+ author -.pid
+ thread pid
+ parent ~
+ contents c
+ tags (silt `(list @t)`~['oldtianming'])
+ ==
+ =/ s (save-thread:hd title.a p .n .n)
+ =. state ?: ?=(%err -.s) state +.s
+ $(jjj t.jjj)
+
+ ++ load-tianming-replies
+ =/ j .^(json %cx /(scot %p our.bowl)/boke/(scot %da now.bowl)/data/replies/json)
+ =/ jjj (boardpost:de:js j)
+ |-
+ ?~ jjj state
+ =/ a=read-boardpost i.jjj
+ =/ author ?: .=((cass (trip author.a)) "spandrell") ~docteg-mothep
+ (add (bex 64) author.a)
+ =/ pid [author date.a]
+ =/ thread-author ?: .=((cass (trip op.a)) "spandrell") ~docteg-mothep
+ (add (bex 64) op.a)
+ =/ thread-pid [thread-author thread.a]
+ =/ c (put:corm:tp *content-map:tp date.a content.a)
+ =| p=post:tp
+ =. p %= p
+ id date.a
+ author author
+ thread thread-pid
+ parent `thread-pid
+ contents c
+ ==
+ =. state
+ =/ s (save-reply:hd p thread-pid thread-pid .y)
+ ?: ?=(%err -.s) state +.s
+ $(jjj t.jjj)
+ ::
+
+ :: =/ one (put:gorm:tp feed pid p)
+ :: =. feed ?~ parent-post one
+ :: =/ parent-pid [author.u.parent-post parent.a]
+ :: =/ full-parent (get:gorm:tp feed parent-pid)
+ :: ?~ full-parent one
+ :: =/ oc children.u.full-parent
+ :: =/ nc (~(put in oc) pid)
+ :: =/ np u.full-parent(children nc)
+ :: (put:gorm:tp one parent-pid np)
+ :: =. tags =/ curr (~(get by tags) 'blog-comment')
+ :: =/ nl ?~ curr ~[pid] (snoc u.curr pid)
+ :: (~(put by tags) 'blog-comment' nl)
+ :: =/ l (tap:torm:tp threads)
+ :: |-
+ :: ?~ l `this
+ :: =/ t=thread:tp +.i.l
+ :: ?~ replies.t $(l t.l)
+ :: =/ del (del:torm:tp active-threads -.i.l)
+ :: =. active-threads ?~ -.del threads
+ :: (put:torm:tp +.del -.replies.t t)
+ :: $(l t.l)
+
+
+ ++ serve
+ ^- (quip card _this)
+ =/ order !<(eyre-order:kaji vase)
+ =/ address address.req.order
+ ~& >> address=address
+ ~& > req=url.request.req.order
+ ?: (~(has in banned-ips:const) address) `this
+ :: ~& >>> malicious-request-alert=req.order `this
+ :_ this
+ %- route:kaj
+ [route:router [%eyre order] state]
+--
+++ on-peek
+|= =(pole knot)
+?+ pole ~
+ [%x %manx %nav sip=@t ~]
+ =/ sip (slav %p sip.pole)
+ =. bowl bowl(src sip)
+ :- ~ :- ~ :- %noun !>
+ =/ nv nav(bowl bowl) ext:nv
+ [%x %chads sip=@t ~]
+ :- ~ :- ~ :- %noun !>
+ =/ sip (slav %p sip.pole)
+ =/ wal ~(. wall sip)
+ ?~ (subscription-type:wal now.bowl) .n .y
+==
+:: ~& on-peek=pole
+:: =/ blog (~(got by feeds) %blog)
+:: ?+ pole ~
+:: [%x %index ~]
+:: =/ posts=(list full-node:tp)
+:: %+ scag page-size
+:: %- flop
+:: %+ roll (tap:orm:tp blog)
+:: |= [i=[=id:tp =post:tp] a=(list full-node:tp)]
+:: ?~ parent.post.i [(node-to-full:plib post.i blog) a] a
+:: ``[%noun !>(posts)]
+:: [%x %post rest=*]
+:: ~& > rest=rest.pole
+:: =/ p (~(get by paths) rest.pole)
+:: ?~ p ~
+:: =/ poast (got:orm:tp blog id.u.p)
+:: =/ fn (node-to-full:plib poast blog)
+:: ``[%noun !>(fn)]
+:: :: [%x %search query=@ comments=@]
+:: :: =/ with-comments .=(comments '1')
+:: :: ~
+:: ==
+++ on-agent
+|= [=wire =sign:agent:gall]
+|^
+ ?: ?=(%kick -.sign) resub
+ ?: ?=([%radio ~] wire) handle-radio
+ ?. ?=(%fact -.sign) `this
+ ?: ?=(%dm p.cage.sign) (handle-dm !<(memo:c q.cage.sign))
+ ?. ?=(%channel-response p.cage.sign) `this
+ =/ res !<(r-channels:c q.cage.sign)
+ ?. ?=(%post +<.res) `this
+ =/ =flag:tlonc ->.res
+ :_ this
+ =/ r r-post.r-channel.res
+ ?- -.r
+ %set ?~ post.r ~ (route:kaj [route:router [%kaji '' %chat-msg flag +<.u.post.r] state])
+ %reply ~ :: reply-count.reply-meta.r id-reply.r
+ %reacts ~ :: reacts.r :: map
+ %essay ~ :: wtf
+ ==
+ ++ handle-dm
+ |= m=memo:c
+ :: validate TOTP and set cookie if so
+ `this
+ ++ handle-radio
+ ?. ?=(%fact -.sign) `this
+ =/ e !<(radio-event q.cage.sign)
+ ?. ?=(%response -.e) `this
+ =. urbit.tv minitowers.e `this
+
+ ++ resub
+ :_ this
+ ?+ wire ~
+ [%radio ~] :~(watch-radio-card:hd)
+ [%chat %updates ~] :~(watch-chat-card:hd)
+ ==
+
+
+--
+++ on-arvo
+ |= [=(pole knot) =sign-arvo]
+ |^
+ ?: ?=([%lick %soak *] sign-arvo)
+ ?+ [mark noun]:sign-arvo `this
+ [%connect ~] ((slog 'socket connected' ~) `this)
+ [%disconnected ~] ((slog 'socket disconnected' ~) `this)
+ [%error *] ((slog leaf+"socket error {(trip ;;(@t noun.sign-arvo))}" ~) `this)
+ [%noun *] ((slog leaf+"socket noun {(trip ;;(@t noun.sign-arvo))}" ~) `this)
+ ==
+ ::
+ ?: ?=([%arow *] +.sign-arvo) (handle-thread +>.sign-arvo)
+ ?. ?=([%behn %wake *] sign-arvo) `this
+ ?. ?=([%backup ~] pole) `this
+ :_ this :_ ~ dump-state:hd
+ ::
+ ++ handle-thread
+ |= s=(avow:khan cage)
+ ?: ?=(%| -.s) ~& "thread failed" `this
+ =/ devase !<(* +>.s)
+ :: ?: ?=(%& -.p.sign-arvo) `this
+ `this
+ --
+--
+|_ =bowl:gall
+:: saving to state
++$ save-res
+$% [%err err=@t]
+ [%ok p=_state]
+==
+++ save-thread
+|= [title=@t p=post:tp is-blog=? is-edit=?] ^- save-res
+ =/ post-pid [author.p id.p]
+ =. feed (put:gorm:tp feed post-pid p)
+ =/ pat ?: is-blog
+ (make-blogpost-path:lib id.p title)
+ (make-board-path:lib paths title tags.p)
+ =/ t ?. is-edit *thread:tp (got:torm:tp threads post-pid)
+ =/ snip-size ?: is-blog 500 200
+ =/ snip (abbreviate-post:plib contents.p snip-size)
+ =. t t(title title, path pat, pid post-pid, tags tags.p, snip snip) :: TODO remove titles from posts
+ =. threads (put:torm:tp threads post-pid t)
+ =. active-threads (put:porm:tp active-threads post-pid post-pid)
+ =. tags (save-tags tags.p post-pid)
+ =. paths (~(put by paths) pat post-pid)
+ :- %ok state
+++ save-reply
+|= [p=post:tp thread=pid:tp parent=pid:tp blog=?] ^- save-res
+ =. p (set-parents:lib p thread parent)
+ =/ post-pid [author.p id.p]
+ =/ pop (get:torm:tp threads thread)
+ ?~ pop [%err 'thread op not found please report to ~docteg-mothep or site@spandrell.ch']
+ =/ ppar (get:gorm:tp feed parent)
+ ?~ ppar [%err 'parent not found, please report to ~docteg-mothep or site@spandrell.ch']
+ =. feed (put:gorm:tp feed post-pid p)
+ =/ npar u.ppar(children (~(put in children.u.ppar) post-pid))
+ =. feed (put:gorm:tp feed parent npar)
+ =/ has-reply (~(has in (silt replies.u.pop)) post-pid)
+ =/ nr ?: has-reply replies.u.pop [post-pid replies.u.pop]
+ =/ npop u.pop(replies nr)
+ =. threads (put:torm:tp threads thread npop)
+ =/ replies replies.u.pop
+ =/ last-reply ?~ replies thread i.replies
+ =/ delt (del:porm:tp active-threads last-reply)
+ =/ mmm ?~ -.delt ~& >>> thread-not-found-under-last-reply=post-pid ~ ~
+ =. active-threads +:delt
+ =. active-threads (put:porm:tp active-threads post-pid thread)
+ :- %ok state
+::
+++ save-tags
+|= [ntags=(set @t) =pid:tp] ^- tags-table
+ =/ tag-list ~(tap in ntags)
+ |- ^- tags-table
+ ?~ tag-list tags
+ =/ curr (~(get by tags) i.tag-list)
+ =/ npids ?~ curr
+ (put:porm:tp *pidmap pid pid)
+ (put:porm:tp u.curr pid pid)
+ =/ nmap (~(put by tags) i.tag-list npids)
+ $(tag-list t.tag-list, tags nmap)
+
+++ del-in-tags
+|= [ntags=(set @t) =pid:tp] ^+ tags
+ =/ tag-list ~(tap in ntags)
+ |- ^+ tags
+ ?~ tag-list tags
+ =/ curr (~(get by tags) i.tag-list)
+ =/ npids ?~ curr ~ +:(del:porm:tp u.curr pid)
+ =/ nmap (~(put by tags) i.tag-list npids)
+ $(tag-list t.tag-list, tags nmap)
+
+:: polls
+
+++ save-poll
+ |= p=poll:pols ^+ polls
+ (~(put by polls) [author.p time.p] p)
+
+++ insert-poll
+ |= [c=content-list:tp =poll:pols] ^+ c
+ =/ ref=block:tp :- %ref :+ %polls src.bowl
+ /(scot %p author.poll)/(scot %da time.poll)
+ (snoc c ref)
+
+++ vote-poll
+ |= [=pid:tp option=@ud]
+ =/ upoll (~(get by polls) pid)
+ ?~ upoll polls
+ =/ =poll:pols (add-vote u.upoll option)
+ (~(put by polls) pid poll)
+++ add-vote
+ |= [=poll:pols option=@ud] ^+ poll
+ ?: ?=(%exc -.votes.poll)
+ =. p.votes.poll
+ (~(put by p.votes.poll) src.bowl option)
+ poll
+ ::
+ =. p.votes.poll
+ =/ curr (~(get by p.votes.poll) option)
+ =/ nmap=(map @p (set @ud))
+ ?~ curr
+ %- malt ~[[src.bowl (silt ~[option])]]
+ ::
+ =/ my-votes=(unit (set @ud)) (~(get by u.curr) src.bowl)
+ ?~ my-votes
+ (~(put by u.curr) src.bowl `(set @ud)`(silt ~[option]))
+ (~(put by u.curr) src.bowl `(set @ud)`(~(put in u.my-votes) option))
+
+ (~(put by p.votes.poll) option nmap)
+ poll
+ ::
+
+::
+++ dump-state ^- card
+ :: TODO prov
+ =/ tv-chat
+ =/ tvs (~(got by here.tv) 'spandrell-tv')
+ =/ sta (~(got by schedule.tvs) started.current.tvs)
+ chat.sta
+ =/ data=dump-type [(tap:gorm:tp feed) (tap:torm:tp threads) active-threads paths tags tv-chat]
+ =/ vase !>((jam data))
+ =/ =soba:clay :_ ~ [/data/site-dump/(scot %da now.bowl)/jam %ins %noun vase]
+ =/ =nori:clay [%& soba]
+ =/ =task:clay [%info %blog nori]
+ =/ note=note-arvo [%c task]
+ [%pass /dump/[dap.bowl] %arvo note]
++$ dump-type
+ $: feed=(list feed-pair)
+ threads=(list thread-pair)
+ at=pidmap
+ paths=pathmap
+ tt=tags-table
+ tv-chat=(list post:tp)
+ ==
+
++$ feed-pair [pid:tp post:tp]
++$ thread-pair [pid:tp thread:tp]
+++ read-last ^+ state
+ ~& > "restoring"
+ =/ bp /(scot %p our.bowl)/blog/(scot %da now.bowl)
+ =/ f .^((list path) %ct (weld bp /data/site-dump))
+ ?~ f state
+ =/ filename (rear f)
+ ~& [f filename]
+ =/ j .^(@ %cx (weld bp filename))
+ ~& read-file=(end [0 100] j)
+ =/ cued (cue j)
+ =/ data ;;(dump-type cued)
+ ~& >>> lengths=[(lent feed.data) (lent threads.data)]
+ =/ tvs (~(got by here.tv) 'spandrell-tv')
+ =/ sta (~(got by schedule.tvs) started.current.tvs)
+ =. chat.sta tv-chat.data
+ =. schedule.tvs (~(put by schedule.tvs) started.current.tvs sta)
+ =. here.tv (~(put by here.tv) 'spandrell-tv' tvs)
+ %= state
+ feed (gas:gorm:tp *gfeed:tp feed.data)
+ threads (gas:torm:tp *threadsf threads.data)
+ active-threads at.data
+ paths paths.data
+ tags tt.data
+ ==
+++ restore ^+ state
+ ~& > "restoring"
+ =/ j .^(@ %cx /(scot %p our.bowl)/blog/(scot %da now.bowl)/data/site-dump/jam)
+ ~& read-file=(end [0 100] j)
+ =/ cued (cue j)
+ =/ data ;;(dump-type cued)
+ ~& >>> lengths=[(lent feed.data) (lent threads.data)]
+ =/ tvs (~(got by here.tv) 'spandrell-tv')
+ =/ sta (~(got by schedule.tvs) started.current.tvs)
+ =. chat.sta tv-chat.data
+ =. schedule.tvs (~(put by schedule.tvs) started.current.tvs sta)
+ =. here.tv (~(put by here.tv) 'spandrell-tv' tvs)
+ %= state
+ feed (gas:gorm:tp *gfeed:tp feed.data)
+ threads (gas:torm:tp *threadsf threads.data)
+ active-threads at.data
+ paths paths.data
+ tags tt.data
+ ==
+:: Cards
+++ watch-radio-card ^- card
+[%pass /radio %agent [~docteg-mothep %tower] %watch /towers]
+++ watch-chat-card ^- card
+[%pass /chat/updates %agent [our.bowl %channels] %watch /chat/(scot %p our.bowl)/chat]
+++ watch-dms-card ^- card
+[%pass /dm-root %agent [our.bowl %chat] %watch /]
+++ watch-cards ^- (list card)
+:~ watch-radio-card
+ watch-chat-card
+==
+++ root-path-card ^- card
+ [%pass /mmm %arvo %e %connect [~ /] dap.bowl]
+++ init-cards ^- (list card)
+:~ watch-radio-card
+ watch-chat-card
+ watch-dms-card
+ root-path-card
+ schedule-backup-card
+==
+++ schedule-backup-card ^- card
+ [%pass /backup %arvo %b %wait (add now.bowl ~h6)]
+
+++ init-state ^+ state
+=. tv init-tv
+:: =. state load-blog
+:: =. state load-blog-comments
+state
+++ init-tv ^+ tv
+=/ starting=@da now.bowl
+=/ sta=tv-station :*
+ 'Dune Month!'
+ 'https://hydrogen.finnem.net/hls/live.m3u8'
+ starting
+ ~
+ ~
+ chat-welcome-post^~
+ ~
+==
+=/ schedule %- malt :_ ~ [starting sta]
+=/ =bstv ['Spandrell TV' ~docteg-mothep ['Dune Month!' starting] schedule]
+=. here.tv (~(put by here.tv) 'spandrell-tv' bstv)
+tv
+
+++ chat-welcome-post ^- post:tp
+=/ contents (tokenize:ui 'Welcome to Spandrell TV! Make yourself cozy')
+(build-post:lib contents ~ [~docteg-mothep now.bowl])
+++ load-blog
+ =/ j .^(json %cx /(scot %p our.bowl)/blog/(scot %da now.bowl)/data/posts/json)
+ =/ jjj (poast:de:js j)
+ |-
+ ?~ jjj state
+ =/ a=read-post i.jjj
+ =/ pid [~docteg-mothep date.a]
+ =/ c (put:corm:tp *content-map:tp date.a contents.a)
+ =/ tgs (~(put in tags.a) 'blog')
+ =| p=post:tp
+ =. p %= p
+ id date.a
+ author -.pid
+ thread pid
+ contents c
+ tags tgs
+ ==
+ =/ s (save-thread title.a p .y .n)
+ =. state ?: ?=(%err -.s) state +.s
+ $(jjj t.jjj)
+
+++ load-blog-comments
+ =/ j .^(json %cx /(scot %p our.bowl)/boke/(scot %da now.bowl)/data/comments/json)
+ =/ jj %+ sort (comment:de:js j) |= [a=read-comment b=read-comment]
+ (lth date.a date.b)
+ |-
+ ?~ jj state
+ =/ a=read-comment i.jj
+ =/ c (put:corm:tp *content-map:tp date.a contents.a)
+ =/ author ?: .=((cass (trip author.a)) "spandrell") ~docteg-mothep
+ (add (bex 64) author.a)
+ =/ pa ?: .=((cass (trip pa.a)) "spandrell") ~docteg-mothep
+ (add (bex 64) pa.a)
+ =/ pid [author date.a]
+ =/ ppid [pa parent.a]
+ =/ parent-post (get:gorm:tp feed ppid)
+ =/ parent=(unit pid:tp) ?~ parent-post ~& > no-parent=[author.a date.a pa.a parent.a title.a] ~
+ `ppid
+ =/ op [~docteg-mothep post.a]
+ =| p=post:tp
+ =. p %= p
+ id date.a
+ author `@p`author
+ contents c
+ tags (sy ~['blog-comment'])
+ ==
+ =. state ?~ parent state
+ =/ s (save-reply p op u.parent .y)
+ ?: ?=(%err -.s) state +.s
+ $(jj t.jj)
+
+
+++ hi bowl
+:: cards
+:: ++ send-dm
+:: |= who=@p
+:: =/ input %- crip "Show me what you got"
+:: =/ parsed (text-to-content input)
+:: ~& parsed=parsed
+:: ?~ parsed (alert-card:kaj "Text parsing failed")
+:: =/ content=story:c
+:: :_ ~ :- %inline :_ ~ u.parsed
+:: =/ id [our.bowl now.bowl]
+:: =/ =memo:c [content our.bowl now.bowl]
+:: =/ delta [%add memo ~ ~]
+:: =/ diff [id delta]
+:: =/ a [who diff]
+:: :* %pass
+:: /send
+:: %agent
+:: [our.bowl %chat]
+:: %poke
+:: %chat-dm-action
+:: !> a
+:: ==
+
+++ send-message
+|= input=inline:c
+=/ content=story:c
+:_ ~ :- %inline :_ ~ input
+=/ =memo:c [content src.bowl now.bowl]
+=/ =kind-data:c [%chat ~]
+=/ =essay:c [memo kind-data]
+=/ cp=c-post:c [%add essay]
+=/ cc=c-channel:c [%post cp]
+=/ =nest:c [%chat our.bowl %chat]
+=/ a=c-channels:c [%channel nest cc]
+:* %pass
+ /send
+ %agent
+ [our.bowl %channels]
+ %poke
+ %channel-action
+ !> a
+==
+ ++ text-to-content
+ %+ curr rush
+ |^ ;~ pose
+ (cook |=(=@t [%link t t]) turl)
+ text
+ ==
+ ++ turl
+ =- (sear - text)
+ |= t=cord
+ ^- (unit cord)
+ ?~((rush t aurf:de-purl:html) ~ `t)
+ :: +text: text message body
+ ::
+ ++ text
+ (cook crip (plus next))
+ --
+--