add bbb and nhl

master
Steve Ayerhart 2021-04-04 11:10:32 -04:00
parent 5bafa0257b
commit 89f81d7b89
No known key found for this signature in database
GPG Key ID: 5C815FDF3A00B8BA
10 changed files with 571 additions and 0 deletions

1
README.txt Normal file
View File

@ -0,0 +1 @@
The 'work' directory is for your own personal vocabularies.

1
bonerbonerboner/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/bbb.db

View File

@ -0,0 +1,29 @@
! Copyright (C) 2020 .
! See http://factorcode.org/license.txt for BSD license.
USING: kernel http.server http.server.responses http.server.dispatchers furnace.actions html.forms accessors namespaces io.servers io.sockets.secure.debug ;
USING: bonerbonerboner.services ;
IN: bonerbonerboner
TUPLE: bbb < dispatcher ;
: <theme-action> ( -- action )
<page-action> [ "bbb" "theme" set-value ] >>init
{ bbb "templates/themes" } >>template ;
: <heartbeat-response> ( -- response )
"bonerbonerboner" <text-content> ;
: <bbb> ( -- responder )
bbb new-dispatcher
<heartbeat-response> "heartbeat" add-responder ;
! <theme-action> "theme" add-responder ;
SYMBOL: current-test-server
: run-test-bbb ( -- )
<bbb> main-responder set-global
8080 httpd current-test-server set ;

View File

@ -0,0 +1,53 @@
! Copyright (C) 2020 .
! See http://factorcode.org/license.txt for BSD license.
USING: kernel sequences http http.server.dispatchers accessors vocabs.parser furnace.actions regexp db.types db.tuples math urls strings calendar ;
USING: bonerbonerboner ;
IN: bonerbonerboner.services.link-logger
! TODO: index url and updated date
! TODO: add slack integration
TUPLE: link id url created-by updated-by repost-count date-created date-updated ;
link "links"
{
{ "id" "id" +db-assigned-id+ }
{ "url" "url" URL }
{ "repost-count" "repost_count" INTEGER }
{ "created-by" "created_by" TEXT }
{ "updated-by" "updated_by" TEXT }
{ "date-created" "date_created" TIMESTAMP }
{ "date-updated" "date_updated" TIMESTAMP }
} define-persistent
: ensure-link-logger ( -- )
[ link ensure-table ] with-bbb-db ;
: <link> ( url who -- link )
[ f ] 2dip dup 0 now dup link boa ;
: <repost> ( link who -- link )
[ dup repost-count>> 1 + >>repost-count ] dip >>updated-by now >>date-updated ;
: add-link ( url who -- )
[ <link> insert-tuple ] with-bbb-db ;
: update-repost ( link who -- )
[ <repost> update-tuple ] with-bbb-db ;
: repost? ( url -- link/f )
link new swap >>url [ select-tuple ] with-bbb-db ;
: url-regex ( -- regexp )
R/ http[s]?:\/\/(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+/i ;
: strip-urls ( str -- seq )
url-regex all-matching-subseqs ;
: check-repost ( url who -- )
[ dup repost? ] dip swap [ update-repost ] [ add-link ] if ;
! : check-links ( str -- )
! strip-urls [ check-repost ] each ;

View File

@ -0,0 +1,83 @@
! Copyright (C) 2020 .
! See http://factorcode.org/license.txt for BSD license.
USING: kernel math math.ranges math.order math.parser math.functions arrays sequences sequences.extras threads calendar io timers accessors random formatting combinators combinators.random http.server.responses ;
USING: prettyprint http furnace.actions http.server.dispatchers ;
USING: bonerbonerboner.services.slack ;
IN: bonerbonerboner.services.mississippis
CONSTANT: max-mississippi-count 30
CONSTANT: min-mississippi-count 1
: random-stupid-word ( -- something-stupid )
{ "balls" "fart" "SIEEEEEEEEEEGE" "hey" "HONESTLY" "fuckface" "it's too late-TOOOOO LATE" }
random ;
: random-m-word ( -- m-word )
{ "Minnesota" "Montana" "Marsupial" "Minneapolis" }
random ;
: random-no-word ( -- nope )
{ "No can doobie" "Can't do that" "I don't think so, kev" "nope" "Can't, learn the rules" }
random ;
: nonstandard-mississippi-duration ( -- duration )
.25 5 uniform-random-float seconds ;
: standard-mississippi-duration ( -- duration )
1 seconds ;
: nonstandard-mississippi-wait ( -- )
nonstandard-mississippi-duration sleep ;
: valid-mississippi-count? ( n -- ? )
min-mississippi-count max-mississippi-count between? ;
: announce ( str -- )
. flush ;
! say-slack ;
: announce-sip ( sip -- )
"%d Mississippi" sprintf announce ;
: announce-fake-terminal ( terminal -- )
number>string random-m-word 2array " " join announce ;
: announce-random-stupid-word ( -- )
random-stupid-word announce ;
: announce-random-sip ( -- )
1000 random announce-sip ;
: announce-go! ( -- )
"GO!" announce ;
: announce-standard-mississippis ( terminal -- )
[1,b] [ standard-mississippi-duration sleep announce-sip ] each ;
:: announce-nonstandard-mississippi ( terminal sip -- )
{
{ .3 [ ] }
{ .1 [ announce-random-sip ] }
{ .075 [ announce-random-stupid-word ] }
{ .025 [ terminal announce-fake-terminal ] }
{ .5 [ sip announce-sip ] }
} casep ;
: announce-nonstandard-mississippis ( terminal -- )
[
1 - [ dup <array> ] [ [1,b] ] bi
[ nonstandard-mississippi-wait announce-nonstandard-mississippi ] 2each
]
[ announce-sip ] bi ;
: announce-mississippis ( terminal standard? -- )
[ announce-standard-mississippis ] [ announce-nonstandard-mississippis ] if ;
: mississippi-go! ( standard? terminal -- )
dup valid-mississippi-count?
[ announce-go! swap announce-mississippis ]
[ 2drop random-no-word announce ] if ;
: <heartbeat-response> ( -- response )
"mississippis" <text-content> ;

View File

@ -0,0 +1,61 @@
! Copyright (C) 2020 .
! See http://factorcode.org/license.txt for BSD license.
USING: kernel regexp sequences http.server http.server.responses db db.types db.tuples unicode formatting assocs ;
USING: bonerbonerboner bonerbonerboner.services.slack ;
IN: bonerbonerboner.services.platzisms
TUPLE: platzism id quote ;
platzism "platzisms"
{
{ "id" "id" +db-assigned-id+ }
{ "quote" "quote" TEXT }
} define-persistent
: ensure-platzisms ( -- )
[ platzism ensure-table ] with-bbb-db ;
: <platzism> ( str -- platzism )
[ f ] dip platzism boa ;
: add-platzism ( str -- )
[ <platzism> insert-tuple ] with-bbb-db ;
: platzism-exists? ( str -- ? )
[ >lower "SELECT id FROM platzisms WHERE lower(quote) = '%s'" sprintf sql-query ] with-bbb-db empty? not ;
: is-platz? ( str -- ? )
R/ ^platz\?$/i matches? ;
: is-platzism? ( str -- ? )
R/ ^steve platz is currently .+/i matches? ;
: random-platzism ( -- platzism )
"SELECT quote from platzisms ORDER BY RANDOM() LIMIT 1" [ sql-query ] with-bbb-db
dup empty? [ drop "I'm not sure what Platz is up to." ] [ first first ] if ;
: repost-callout ( -- )
"We already know Platz is doing that" say-slack ;
: confirm-platzism ( -- )
"noted" say-slack ;
: share-platzism ( -- )
random-platzism say-slack ;
: log/confirm-platzism ( str -- )
dup platzism-exists?
[ drop repost-callout ]
[
add-platzism
confirm-platzism
] if ;
: check-platz ( event -- )
"text" of
[ is-platz? [ share-platzism ] when ]
[ is-platzism? [ log/confirm-platzism ] when* ] bi ;
[ check-platz ] add-slack-handler

View File

@ -0,0 +1,14 @@
! Copyright (C) 2020 .
! See http://factorcode.org/license.txt for BSD license.
USING: kernel fry db db.sqlite environment math.parser ;
IN: bonerbonerboner.services
CONSTANT: bbb-default-port 8069
: with-bbb-db ( quot -- )
'[ "bbb.db" <sqlite-db> _ with-db ] call ; inline
: bbb-api-port ( -- port )
"BBB_API_PORT" os-env [ string>number ] [ bbb-default-port ] if* ;

View File

@ -0,0 +1,64 @@
! Copyright (C) 2020 .
! See http://factorcode.org/license.txt for BSD license.
USING: io.sockets.secure kernel http http.server http.server.dispatchers accessors furnace.actions namespaces http.server.responses io.servers io.sockets.secure.debug json.reader assocs combinators sequences arrays threads locals formatting json.writer urls http.client hashtables ;
USING: prettyprint ;
IN: bonerbonerboner.services.slack
SYMBOLS: slack-callbacks current-slack-webhook-url current-slack-authorization-token ;
TUPLE: slack < dispatcher ;
: add-slack-handler ( quot: ( event -- ) -- )
slack-callbacks get append slack-callbacks set ;
: <bad-callback-response> ( -- response )
"400" "unrecognized event type" <trivial-response> ;
: <heartbeat-response> ( -- response )
"slack" <text-content> ;
: <ok-response> ( -- response )
"200" "OK" <trivial-response> ;
: handle-challenge ( json -- response )
"challenge" of <text-content> ;
:: handle-slack-event ( slack-event -- response )
"bot_id" slack-event key? "subtype" slack-event key? or
[
slack-callbacks get length slack-event <array>
slack-callbacks get [ curry "Slack Callback" spawn drop ] 2each
] unless
<ok-response> ;
: <slack-event-action> ( -- action )
<action>
[
request get post-data>> data>> json> dup
"type" of
{
{ "url_verification" [ handle-challenge ] }
{ "event_callback" [ handle-slack-event ] }
[ drop <bad-callback-response> ]
} case
] >>submit ;
: <heartbeat-action> ( -- action )
<action> [ <heartbeat-response> ] >>display ;
: slack-post-message ( payload -- )
>json
current-slack-webhook-url get >url
<post-request>
http-request 2drop ;
: say-slack ( str -- )
"text" associate
slack-post-message ;
: <slack> ( -- responder )
slack new-dispatcher
<slack-event-action> "slack-events" add-responder
<heartbeat-action> "heartbeat" add-responder ;

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0" >
<html class="@theme" >
<head>
<meta name="viewport" content="width=device-width, initial-sale=1"/>
</head>
<body/>
</html>
</t:chloe>

256
nhl/nhl.factor Normal file
View File

@ -0,0 +1,256 @@
! Copyright (C) 2020 .
! See http://factorcode.org/license.txt for BSD license.
USING: kernel math math.parser strings calendar alien.syntax sequences urls http.client formatting json.reader assocs combinators splitting classes.maybe ;
USING: prettyprint ;
IN: nhl
TUPLE: conference
{ id integer }
{ name string }
{ short-name string }
{ abbreviation string } ;
TUPLE: timezone
{ id string }
{ offset integer }
{ tz string } ;
TUPLE: venue
{ name: string }
{ city: string }
{ timezone timezone } ;
TUPLE: division
{ id integer }
{ name string }
! { name-short string }
{ abbreviation string }
{ conference maybe{ conference } } ;
TUPLE: franchise
{ id integer }
{ team-name string }
{ first-season-id integer }
{ location-name string }
{ most-recent-team-id integer } ;
TUPLE: team
{ id integer }
{ name string }
{ team-name string }
{ location-name string }
{ short-name string }
{ official-site-url url }
{ abbreviation string }
! { first-year timestamp }
{ first-year integer }
{ division division }
{ franchise franchise }
{ venue venue } ;
TUPLE: game-time
{ period integer }
{ period-sec integer } ;
! { period-sec timestamp } ;
TUPLE: location
{ x integer }
{ y integer } ;
TUPLE: player-position
{ code string }
{ name string }
{ type string }
{ abbreviation string } ;
TUPLE: player
{ id integer }
{ full-name string }
{ first-name string }
{ last-name string }
{ primary-number string }
{ current-team string }
{ position player-position }
{ height string }
{ weight integer }
{ shoots/catches string }
{ alternate-captain? boolean }
{ captain? boolean }
{ rookie? boolean }
{ nationality string }
{ birth-date timestamp }
{ birth-city string }
{ birth-state/prvince maybe{ string } }
{ birth-country string } ;
TUPLE: status
{ abstract-state string }
{ coded-state string }
{ detailed-state string }
{ code string }
{ start-time-tbd boolean } ;
TUPLE: league-record
{ wins integer }
{ losses integer }
{ ot integer }
{ type string } ;
TUPLE: team-info
{ scores integer }
{ attempts integer }
{ goals integer }
{ shots-on-goal integer }
{ rink-side string }
{ goalie-pulled? boolean }
{ num-skaters integer }
{ power-play? boolean }
{ league-record league-record }
{ score integer }
{ team team } ;
TUPLE: teams
{ }
TUPLE: game
{ id integer }
{ link team }
{ type team }
{ season string }
{ date timestamp }
{ status status }
{ teams teams }
{ linescore linescore }
{ venue venue }
{ content content }
{ series-summary series-summary } ;
TUPLE: event
{ game-id integer }
{ id integer }
{ type string }
{ sub-type string }
{ time game-time }
{ location location }
{ team team }
{ by sequence }
{ on player } ;
TUPLE: date
{ date string }
{ total-items integer }
{ total-events integer }
{ total-games integer }
{ total-matches integer }
{ games sequence }
{ events sequence }
{ matches sequence } ;
TUPLE: schedule
{ copyright string }
{ total-items integer }
{ total-events integer }
{ total-games integer }
{ total-matches integer }
{ wait integer }
{ dates sequence } ;
CONSTANT: nhl-base-url "https://statsapi.web.nhl.com/api/v1/"
! TODO: CACHING
: >entity-url ( entity id -- url )
dup string? [ string>number ] when
"%s/%d" sprintf nhl-base-url prepend >url ;
: entity-get ( id entity -- json )
swap over [
>entity-url http-get nip json>
] dip of first ;
: entities-get ( entity -- seq )
dup [
nhl-base-url prepend http-get nip json>
] dip of ;
: parse-conference ( json -- conference )
"CONF" . dup .
{
[ "id" of ] [ "name" of ] [ "shortName" of ] [ "abbreviation" of ]
} cleave
conference boa ;
: conference-get ( id -- conference ) "conferences" entity-get parse-conference ;
: conferences-get ( -- conferences ) "conferences" entities-get [ parse-conference ] map ;
: parse-division ( json -- division )
{
[ "id" of ] [ "name" of ] [ "abbreviation" of ]
! [ "conference" of ] ! "id" of conference-get ]
} cleave
f
division boa ;
: parse-franchise ( json -- franchise )
{
[ "franchiseId" of ] [ "teamName" of ] [ "firstSeasonId" of ]
[ "locationName" of ] [ "mostRecentTeamId" of ]
} cleave
franchise boa ;
: parse-position ( json -- player-position )
{
[ "code" of ] [ "name" of ] [ "type" of ] [ "abbreviation" of ]
} cleave player-position boa ;
: parse-player ( json -- player )
{
[ "id" of ]
[ "fullName" of ]
[ "firstName" of ]
[ "lastName" of ]
[ "primaryNumber" of ]
[ "currentTeam" of "name" of ]
[ "primaryPosition" of parse-position ]
[ "height" of ]
[ "weight" of ]
[ "shootsCatches" of ]
[ "alternateCaptain" of ]
[ "captain" of ]
[ "rookie" of ]
[ "nationality" of ]
[ "birthDate" of "-" split [ string>number ] map [ first ] [ second ] [ third ] tri <date> ]
[ "birthCity" of ]
[ "birthStateProvince" of ]
[ "birthCountry" of ]
} cleave player boa ;
: division-get ( id -- division )
"divisions" entity-get parse-division ;
: divisions-get ( -- divisions )
"divisions" entities-get [ parse-division ] map ;
: franchise-get ( id -- division )
"franchises" entity-get parse-franchise ;
: franchises-get ( -- divisions )
"franchises" entities-get [ parse-franchise ] map ;
: player-get ( id -- player ) "people" entity-get parse-player ;
: players-get ( ids -- players ) [ player-get ] map ;
: parse-timezone ( json -- timezone )
[ "id" of ] [ "offset" of ] [ "tz" of ] tri timezone boa ;
: parse-venue ( json -- venue )
[ "name" of ] [ "city" of ] [ "timeZone" of parse-timezone ] tri venue boa ;
: parse-team ( json -- team )
{
[ "id" of ] [ "name" of ] [ "teamName" of ] [ "locationName" of ]
[ "shortName" of ] [ "officialSiteUrl" of >url ] [ "abbreviation" of ]
[ "firstYearOfPlay" of string>number ] [ "division" of "id" of division-get ]
[ "franchiseId" of franchise-get ] [ "venue" of parse-venue ]
} cleave
team boa ;
: team-get ( id -- team ) "teams" entity-get parse-team ;
: teams-get ( -- teams ) "teams" entities-get [ parse-team ] map ;