diff options
| author | Christine Dodrill <me@christine.website> | 2018-09-23 14:26:02 -0700 |
|---|---|---|
| committer | Christine Dodrill <me@christine.website> | 2018-09-23 14:26:02 -0700 |
| commit | fe760ddbdc36008cb1747fcfb742072e5240cdd6 (patch) | |
| tree | 7b6f765ac0ceb7c1078d3219746bf681f02d1c64 | |
| parent | 73344db07bd55329f87a4176802443edb7c464ae (diff) | |
| download | x-fe760ddbdc36008cb1747fcfb742072e5240cdd6.tar.xz x-fe760ddbdc36008cb1747fcfb742072e5240cdd6.zip | |
x/discord: start on ilo Kesi
| -rw-r--r-- | discord/ilo-kesi/context.go | 51 | ||||
| -rw-r--r-- | discord/ilo-kesi/main.go | 84 | ||||
| -rw-r--r-- | discord/ilo-kesi/toki_pona.go | 56 | ||||
| -rw-r--r-- | discord/ilo-kesi/toki_pona_test.go | 12 | ||||
| -rw-r--r-- | discord/ilo-kesi/within.go | 9 | ||||
| -rw-r--r-- | web/switchcounter/switchc.go | 106 |
6 files changed, 318 insertions, 0 deletions
diff --git a/discord/ilo-kesi/context.go b/discord/ilo-kesi/context.go new file mode 100644 index 0000000..069bd8b --- /dev/null +++ b/discord/ilo-kesi/context.go @@ -0,0 +1,51 @@ +package main + +import ( + "errors" + "strings" +) + +const ( + actionFront = "lawa,insa" +) + +var ( + ErrUnknownAction = errors.New("ijo-kesi: unknown action") +) + +type Request struct { + Address []*part + Action string + Subject *string // if null, user is asking for the info + Punct string +} + +func parseRequest(inp Sentence) (*Request, error) { + var result Request + + for _, part := range inp { + switch part.Part { + case partAddress: + result.Address = part.Parts + case partSubject: + if len(part.Tokens) == 1 && part.Tokens[0] != "seme" { + sub := strings.Join(part.Tokens, ",") + result.Subject = &sub + } + case partObjectMarker: + act := strings.Join(part.Tokens, ",") + + switch act { + case actionFront: + default: + return nil, ErrUnknownAction + } + + result.Action = act + case partPunctuation: + result.Punct = part.Tokens[0] + } + } + + return &result, nil +} diff --git a/discord/ilo-kesi/main.go b/discord/ilo-kesi/main.go new file mode 100644 index 0000000..1bc12bc --- /dev/null +++ b/discord/ilo-kesi/main.go @@ -0,0 +1,84 @@ +package main + +import ( + "context" + "fmt" + "log" + "net/http" + "time" + + "github.com/Xe/x/web/switchcounter" + "github.com/joeshaw/envdecode" + _ "github.com/joho/godotenv/autoload" + "github.com/peterh/liner" +) + +// lipuSona is the configuration. +type lipuSona struct { + //DiscordToken string `env:"DISCORD_TOKEN,required"` // lipu pi lukin ala + TokiPonaTokenizerAPIURL string `env:"TOKI_PONA_TOKENIZER_API_URL,default=https://us-central1-golden-cove-408.cloudfunctions.net/function-1"` + SwitchCounterWebhook string `env:"SWITCH_COUNTER_WEBHOOK,required"` + IloNimi []string `env:"IJO_NIMI,default=ke;si"` +} + +func main() { + var cfg lipuSona + err := envdecode.StrictDecode(&cfg) + if err != nil { + log.Fatal(err) + } + + //pretty.Println(cfg) + + sw := switchcounter.NewHTTPClient(http.DefaultClient, cfg.SwitchCounterWebhook) + + line := liner.NewLiner() + defer line.Close() + + line.SetCtrlCAborts(true) + + for { + if inp, err := line.Prompt("|: "); err == nil { + if inp == "" { + return + } + + line.AppendHistory(inp) + + parts, err := TokenizeTokiPona(cfg.TokiPonaTokenizerAPIURL, inp) + if err != nil { + log.Printf("Can't parse: %v", err) + } + + for _, sent := range parts { + req, err := parseRequest(sent) + if err != nil { + log.Printf("error: %v", err) + continue + } + + switch req.Action { + case actionFront: + if req.Subject == nil { + st, err := sw.Status(context.Background()) + if err != nil { + log.Printf("status error: %v", err) + continue + } + + fmt.Printf("ilo Kesi\\ jan %s li lawa insa.\n", withinToToki[st.Front]) + + log.Printf("Started at: %s (%s ago)", st.StartedAt, time.Since(st.StartedAt)) + continue + } + + log.Printf("setting front not implemented yet :(") + } + } + } else if err == liner.ErrPromptAborted { + log.Print("Aborted") + } else { + log.Print("Error reading line: ", err) + } + } +} diff --git a/discord/ilo-kesi/toki_pona.go b/discord/ilo-kesi/toki_pona.go new file mode 100644 index 0000000..9f027d2 --- /dev/null +++ b/discord/ilo-kesi/toki_pona.go @@ -0,0 +1,56 @@ +package main + +import ( + "bytes" + "encoding/json" + "net/http" +) + +type part struct { + Part string `json:"part"` + Sep *string `json:"sep"` + Tokens []string `json:"tokens"` + Parts []*part `json:"parts"` +} + +const ( + partAddress = `address` + partSubject = `subject` + partObjectMarker = `objectMarker` + partPrepPhrase = `prepPhrase` + partInterjection = `interjection` + partCartouche = `cartouche` + partPunctuation = `punctuation` + + punctPeriod = `period` + punctQuestion = `question` + punctExclamation = `exclamation` +) + +// A sentence is a series of sentence parts. +type Sentence []part + +// TokenizeTokiPona returns a series of toki pona tokens. +func TokenizeTokiPona(aurl, text string) ([]Sentence, error) { + buf := bytes.NewBuffer([]byte(text)) + req, err := http.NewRequest(http.MethodPost, aurl, buf) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", "text/plain") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var result []Sentence + err = json.NewDecoder(resp.Body).Decode(&result) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/discord/ilo-kesi/toki_pona_test.go b/discord/ilo-kesi/toki_pona_test.go new file mode 100644 index 0000000..619a57f --- /dev/null +++ b/discord/ilo-kesi/toki_pona_test.go @@ -0,0 +1,12 @@ +package main + +import ( + "testing" +) + +func TestTokenizeTokiPona(t *testing.T) { + _, err := TokenizeTokiPona("https://us-central1-golden-cove-408.cloudfunctions.net/function-1", "mi olin e sina.") + if err != nil { + t.Fatal(err) + } +} diff --git a/discord/ilo-kesi/within.go b/discord/ilo-kesi/within.go new file mode 100644 index 0000000..048a2de --- /dev/null +++ b/discord/ilo-kesi/within.go @@ -0,0 +1,9 @@ +package main + +var withinToToki = map[string]string{ + "Cadey": "Kesi", + "Nicole": "Liso", + "Jessie": "Lesi", + "Sephie": "Sesi", + "Ashe": "Ase", +} diff --git a/web/switchcounter/switchc.go b/web/switchcounter/switchc.go new file mode 100644 index 0000000..d4bfbd3 --- /dev/null +++ b/web/switchcounter/switchc.go @@ -0,0 +1,106 @@ +// Package switchcounter is a simple interface to the https://www.switchcounter.science/ API. +package switchcounter + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "time" +) + +type arg struct { + Command string `json:"command"` // always "switch" + MemberName string `json:"member_name,omitempty"` +} + +// API is the switchcounter API as an abstract interface. +type API interface { + // Status returns the front of the system for this API client. + Status(ctx context.Context) (Status, error) + + // Switch changes who is in front. + Switch(ctx context.Context, front string) (Status, error) +} + +// Status is the API response. +type Status struct { + Front string `json:"member_name"` + StartedAt time.Time `json:"started_at"` +} + +type httpClient struct { + hc *http.Client + url string // webhook url +} + +func (hc httpClient) makeRequestWith(ctx context.Context, body interface{}) (*Status, error) { + env := struct { + Webhook interface{} `json:"webhook"` + }{ + Webhook: body, + } + data, err := json.Marshal(env) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", hc.url, bytes.NewBuffer(data)) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + req.Header.Add("Content-Type", "application/json") + + resp, err := hc.hc.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + log.Printf("body: %s", string(data)) + + return nil, fmt.Errorf("http response code %d", resp.StatusCode) + } + + var result Status + err = json.NewDecoder(resp.Body).Decode(&result) + if err != nil { + return nil, err + } + + return &result, nil +} + +func (hc httpClient) Status(ctx context.Context) (Status, error) { + result, err := hc.makeRequestWith(ctx, arg{Command: "switch"}) + if err != nil { + return Status{}, err + } + return *result, nil +} + +func (hc httpClient) Switch(ctx context.Context, front string) (Status, error) { + result, err := hc.makeRequestWith(ctx, arg{Command: "switch", MemberName: front}) + if err != nil { + return Status{}, err + } + return *result, nil +} + +// NewHTTPClient creates a new instance of API over HTTP. +func NewHTTPClient(hc *http.Client, webhookURL string) API { + return httpClient{ + hc: hc, + url: webhookURL, + } +} |
