aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristine Dodrill <me@christine.website>2018-10-04 22:48:42 -0700
committerChristine Dodrill <me@christine.website>2018-10-04 22:49:08 -0700
commit9c9ae00ab8bd7c2a99590daf42fe9df475131929 (patch)
tree44b08805061c0a1b21fa605b3e48544542aa3a85
parent9fd916604c371b41111c14e65d5534d4dbc30ff8 (diff)
downloadx-9c9ae00ab8bd7c2a99590daf42fe9df475131929.tar.xz
x-9c9ae00ab8bd7c2a99590daf42fe9df475131929.zip
add bot to translate toki pona to english
-rw-r--r--mastodon/sona-pi-toki-pona/.gitignore1
-rw-r--r--mastodon/sona-pi-toki-pona/app/Procfile1
-rw-r--r--mastodon/sona-pi-toki-pona/main.go108
-rw-r--r--web/tokipana/tokipana.go44
4 files changed, 154 insertions, 0 deletions
diff --git a/mastodon/sona-pi-toki-pona/.gitignore b/mastodon/sona-pi-toki-pona/.gitignore
new file mode 100644
index 0000000..cca091f
--- /dev/null
+++ b/mastodon/sona-pi-toki-pona/.gitignore
@@ -0,0 +1 @@
+sona-pi-toki-pona \ No newline at end of file
diff --git a/mastodon/sona-pi-toki-pona/app/Procfile b/mastodon/sona-pi-toki-pona/app/Procfile
new file mode 100644
index 0000000..03dd662
--- /dev/null
+++ b/mastodon/sona-pi-toki-pona/app/Procfile
@@ -0,0 +1 @@
+worker: /app/bin/sona-pi-toki-pona
diff --git a/mastodon/sona-pi-toki-pona/main.go b/mastodon/sona-pi-toki-pona/main.go
new file mode 100644
index 0000000..346fe4c
--- /dev/null
+++ b/mastodon/sona-pi-toki-pona/main.go
@@ -0,0 +1,108 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+
+ madon "github.com/McKael/madon/v2"
+ "github.com/Xe/ln"
+ "github.com/Xe/ln/opname"
+ "github.com/Xe/x/web/tokipana"
+ "github.com/jaytaylor/html2text"
+ "github.com/joeshaw/envdecode"
+ _ "github.com/joho/godotenv/autoload"
+)
+
+type lipuSona struct {
+ AppID string `env:"APP_ID,required"`
+ AppSecret string `env:"APP_SECRET,required"`
+ Token string `env:"TOKEN,required"`
+ Instance string `env:"INSTANCE,required"`
+}
+
+func main() {
+ ctx := opname.With(context.Background(), "main")
+ var lipu lipuSona
+ err := envdecode.StrictDecode(&lipu)
+ if err != nil {
+ ln.FatalErr(ctx, err)
+ }
+
+ c, err := madon.RestoreApp("sona-pi-toki-pona:", lipu.Instance, lipu.AppID, lipu.AppSecret, &madon.UserToken{AccessToken: lipu.Token})
+ if err != nil {
+ ln.FatalErr(opname.With(ctx, "restore-app"), err)
+ }
+
+ for {
+ evChan := make(chan madon.StreamEvent, 10)
+ stop := make(chan bool)
+ done := make(chan bool)
+ ctx = opname.With(ctx, "hashtag-stream")
+
+ err = c.StreamListener("hashtag", "tokipona", evChan, stop, done)
+ if err != nil {
+ ln.FatalErr(ctx, err)
+ }
+
+ for {
+ select {
+ case _, _ = <-done:
+ goto redo
+ case ev := <-evChan:
+ switch ev.Event {
+ case "error":
+ ln.Error(opname.With(ctx, "event-parse"), err)
+ case "update":
+ s := ev.Data.(madon.Status)
+ ctx = opname.With(ctx, "update")
+ ctx = ln.WithF(ctx, ln.F{
+ "originating_status_id": s.ID,
+ "originating_status_url": s.URL,
+ })
+
+ text, err := html2text.FromString(s.Content, html2text.Options{PrettyTables: true})
+ if err != nil {
+ ln.Error(ctx, err)
+ continue
+ }
+
+ req := tokipana.Translate(text)
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ ln.Error(ctx, err)
+ continue
+ }
+ err = tokipana.Validate(resp)
+ if err != nil {
+ ln.Error(ctx, err)
+ continue
+ }
+ data, err := ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ if err != nil {
+ ln.Error(ctx, err)
+ continue
+ }
+
+ st, err := c.PostStatus(madon.PostStatusParams{
+ Text: fmt.Sprintf(translationTemplate, string(data)),
+ InReplyTo: s.ID,
+ Visibility: "public",
+ })
+ if err != nil {
+ ln.Error(ctx, err)
+ continue
+ }
+
+ ln.Log(ctx, ln.Info("posted translation of toki pona text"), ln.F{"status_id": st.ID, "status_url": st.URL})
+ }
+ }
+ }
+ }
+
+redo:
+}
+
+const translationTemplate = `Translated into English: %s`
diff --git a/web/tokipana/tokipana.go b/web/tokipana/tokipana.go
new file mode 100644
index 0000000..2daa54b
--- /dev/null
+++ b/web/tokipana/tokipana.go
@@ -0,0 +1,44 @@
+// Package tokipana wraps http://inamidst.com/services/tokipana.
+package tokipana
+
+import (
+ "errors"
+ "net/http"
+ "net/url"
+)
+
+// The API URL.
+const APIURL = `http://inamidst.com/services/tokipana`
+
+// Errors
+var (
+ ErrInvalidRequest = errors.New("tokipana: invalid request")
+)
+
+// Validate checks if a response from the API is valid or not.
+func Validate(resp *http.Response) error {
+ if resp.StatusCode == http.StatusOK {
+ return nil
+ }
+
+ if resp.StatusCode%100 != 2 {
+ return ErrInvalidRequest
+ }
+
+ return nil
+}
+
+// Translate returns a request to translate the given toki pona text into english.
+func Translate(text string) *http.Request {
+ u, _ := url.Parse(APIURL)
+ q := u.Query()
+ q.Set("text", text)
+ u.RawQuery = q.Encode()
+
+ req, err := http.NewRequest(http.MethodGet, u.String(), nil)
+ if err != nil {
+ panic(err)
+ }
+
+ return req
+}