aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig12
-rw-r--r--.gitignore1
-rw-r--r--README.md2
-rw-r--r--config.go70
-rw-r--r--debug.go56
-rw-r--r--go.mod14
-rw-r--r--gomuks.go113
-rw-r--r--matrix.go118
-rw-r--r--session.go102
9 files changed, 487 insertions, 1 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..21d312a
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[*]
+indent_style = tab
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.{yaml,yml}]
+indent_style = space
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9f11b75
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.idea/
diff --git a/README.md b/README.md
index ce5b4a9..be70bf4 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,2 @@
# gomuks
-A Go terminal matrix client.
+A terminal Matrix client written in Go.
diff --git a/config.go b/config.go
new file mode 100644
index 0000000..3510e4a
--- /dev/null
+++ b/config.go
@@ -0,0 +1,70 @@
+// gomuks - A terminal Matrix client written in Go.
+// Copyright (C) 2018 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "gopkg.in/yaml.v2"
+)
+
+type Config struct {
+ MXID string `yaml:"mxid"`
+ HS string `yaml:"homeserver"`
+
+ dir string `yaml:"-"`
+ Session *Session `yaml:"-"`
+}
+
+func (config *Config) Load(dir string) {
+ config.dir = dir
+ os.MkdirAll(dir, 0700)
+ configPath := filepath.Join(dir, "config.yaml")
+ data, err := ioutil.ReadFile(configPath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return
+ } else {
+ fmt.Println("Failed to read config from", configPath)
+ panic(err)
+ }
+ }
+
+ err = yaml.Unmarshal(data, &config)
+ if err != nil {
+ fmt.Println("Failed to parse config at", configPath)
+ panic(err)
+ }
+}
+
+func (config *Config) Save() {
+ data, err := yaml.Marshal(&config)
+ if err != nil {
+ debug.Print("Failed to marshal config")
+ panic(err)
+ }
+
+ path := filepath.Join(config.dir, "config.yaml")
+ err = ioutil.WriteFile(path, data, 0600)
+ if err != nil {
+ debug.Print("Failed to write config to", path)
+ panic(err)
+ }
+}
diff --git a/debug.go b/debug.go
new file mode 100644
index 0000000..2533d5c
--- /dev/null
+++ b/debug.go
@@ -0,0 +1,56 @@
+// gomuks - A terminal Matrix client written in Go.
+// Copyright (C) 2018 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "fmt"
+
+ "github.com/rivo/tview"
+)
+
+type DebugPane struct {
+ text string
+ pane *tview.TextView
+ num int
+}
+
+func (db *DebugPane) Printf(text string, args ...interface{}) {
+ db.num++
+ db.Write(fmt.Sprintf("[%d] %s\n", db.num, fmt.Sprintf(text, args...)))
+}
+
+func (db *DebugPane) Print(text ...interface{}) {
+ db.num++
+ db.Write(fmt.Sprintf("[%d] %s", db.num, fmt.Sprintln(text...)))
+}
+
+func (db *DebugPane) Write(text string) {
+ if db.pane != nil {
+ db.text += text
+ db.pane.SetText(db.text)
+ }
+}
+
+func (db *DebugPane) Wrap(main *tview.Pages) tview.Primitive {
+ db.pane = tview.NewTextView()
+ db.pane.SetBorder(true).SetTitle("Debug output")
+ db.text += "[0] Debug pane initialized\n"
+ db.pane.SetText(db.text)
+ return tview.NewGrid().SetRows(0, 20).SetColumns(0).
+ AddItem(main, 0, 0, 1, 1, 1, 1, true).
+ AddItem(db.pane, 1, 0, 1, 1, 1, 1, false)
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..4888c39
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,14 @@
+module "maunium.net/go/gomuks"
+
+require (
+ "github.com/gdamore/encoding" v0.0.0-20151215212835-b23993cbb635
+ "github.com/gdamore/tcell" v1.0.0
+ "github.com/jroimartin/gocui" v0.0.0-20170827195011-4f518eddb04b
+ "github.com/lucasb-eyer/go-colorful" v0.0.0-20170903184257-231272389856
+ "github.com/matrix-org/gomatrix" v0.0.0-20171003113848-a7fc80c8060c
+ "github.com/mattn/go-runewidth" v0.0.2
+ "github.com/nsf/termbox-go" v0.0.0-20180303152453-e2050e41c884
+ "github.com/rivo/tview" v0.0.0-20180313071706-0b69b9b58142
+ "golang.org/x/text" v0.0.0-20171214130843-f21a4dfb5e38
+ "gopkg.in/yaml.v2" v1.1.1-gopkgin-v2.1.1
+)
diff --git a/gomuks.go b/gomuks.go
new file mode 100644
index 0000000..8ffc2e9
--- /dev/null
+++ b/gomuks.go
@@ -0,0 +1,113 @@
+// gomuks - A terminal Matrix client written in Go.
+// Copyright (C) 2018 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "os"
+ "path/filepath"
+
+ "github.com/gdamore/tcell"
+ "github.com/rivo/tview"
+)
+
+var matrix = new(MatrixContainer)
+var config = new(Config)
+var debug = new(DebugPane)
+
+func main() {
+ configDir := filepath.Join(os.Getenv("HOME"), ".config/gomuks")
+ os.MkdirAll(configDir, 0700)
+ config.Load(configDir)
+
+ views := tview.NewPages()
+ InitUI(views)
+
+ main := debug.Wrap(views)
+
+ if len(config.MXID) > 0 {
+ config.LoadSession(config.MXID)
+ }
+ matrix.Init(config)
+
+ if err := tview.NewApplication().SetRoot(main, true).Run(); err != nil {
+ panic(err)
+ }
+}
+
+func InitUI(views *tview.Pages) {
+ views.AddPage("login", InitLoginUI(), true, true)
+}
+
+func Center(width, height int, p tview.Primitive) tview.Primitive {
+ return tview.NewFlex().
+ AddItem(tview.NewBox(), 0, 1, false).
+ AddItem(tview.NewFlex().
+ SetDirection(tview.FlexRow).
+ AddItem(tview.NewBox(), 0, 1, false).
+ AddItem(p, height, 1, true).
+ AddItem(tview.NewBox(), 0, 1, false), width, 1, true).
+ AddItem(tview.NewBox(), 0, 1, false)
+}
+
+type FormTextView struct {
+ *tview.TextView
+}
+
+func (ftv *FormTextView) GetLabel() string {
+ return ""
+}
+
+func (ftv *FormTextView) SetFormAttributes(label string, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) tview.FormItem {
+ return ftv
+}
+
+func (ftv *FormTextView) GetFieldWidth() int {
+ _, _, w, _ := ftv.TextView.GetRect()
+ return w
+}
+
+func (ftv *FormTextView) SetFinishedFunc(handler func(key tcell.Key)) tview.FormItem {
+ ftv.SetDoneFunc(handler)
+ return ftv
+}
+
+func login(form *tview.Form) func() {
+ return func() {
+ hs := form.GetFormItem(0).(*tview.InputField).GetText()
+ mxid := form.GetFormItem(1).(*tview.InputField).GetText()
+ password := form.GetFormItem(2).(*tview.InputField).GetText()
+ debug.Printf("%s %s %s", hs, mxid, password)
+ config.HS = hs
+ debug.Print(matrix.Init(config))
+ debug.Print(matrix.Login(mxid, password))
+ }
+}
+
+func InitLoginUI() tview.Primitive {
+ form := tview.NewForm().SetButtonsAlign(tview.AlignCenter)
+ hs := config.HS
+ if len(hs) == 0 {
+ hs = "https://matrix.org"
+ }
+ form.
+ AddInputField("Homeserver", hs, 30, nil, nil).
+ AddInputField("Username", config.MXID, 30, nil, nil).
+ AddPasswordField("Password", "", 30, '*', nil).
+ AddButton("Log in", login(form))
+ form.SetBorder(true).SetTitle("Log in to Matrix")
+ return Center(45, 13, form)
+}
diff --git a/matrix.go b/matrix.go
new file mode 100644
index 0000000..b559f17
--- /dev/null
+++ b/matrix.go
@@ -0,0 +1,118 @@
+// gomuks - A terminal Matrix client written in Go.
+// Copyright (C) 2018 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "fmt"
+
+ "github.com/matrix-org/gomatrix"
+)
+
+type MatrixContainer struct {
+ lient *gomatrix.Client
+ config *Config
+ running bool
+ stop chan bool
+}
+
+func (c *MatrixContainer) Initialized() bool {
+ return c.lient != nil
+}
+
+func (c *MatrixContainer) Init(config *Config) error {
+ c.config = config
+
+ if c.lient != nil {
+ c.lient.StopSync()
+ }
+
+ if len(c.config.HS) == 0 {
+ return fmt.Errorf("no homeserver in config")
+ }
+
+ var mxid, accessToken string
+ if c.config.Session != nil {
+ accessToken = c.config.Session.AccessToken
+ mxid = c.config.MXID
+ }
+
+ var err error
+ c.lient, err = gomatrix.NewClient(c.config.HS, mxid, accessToken)
+ if err != nil {
+ return err
+ }
+
+ c.stop = make(chan bool, 1)
+
+ if c.config.Session != nil {
+ go c.Start()
+ }
+ return nil
+}
+
+func (c *MatrixContainer) Login(user, password string) error {
+ resp, err := c.lient.Login(&gomatrix.ReqLogin{
+ Type: "m.login.password",
+ User: user,
+ Password: password,
+ })
+ if err != nil {
+ return err
+ }
+ c.lient.SetCredentials(resp.UserID, resp.AccessToken)
+ c.config.MXID = resp.UserID
+ c.config.Save()
+
+ c.config.Session = c.config.NewSession(resp.UserID)
+ c.config.Session.AccessToken = resp.AccessToken
+ c.config.Session.Save()
+
+ go c.Start()
+
+ return nil
+}
+
+func (c *MatrixContainer) Stop() {
+ c.stop <- true
+ c.lient.StopSync()
+}
+
+func (c *MatrixContainer) Start() {
+ debug.Print("Starting sync...")
+ c.running = true
+ c.lient.Store = c.config.Session
+
+ syncer := c.lient.Syncer.(*gomatrix.DefaultSyncer)
+ syncer.OnEventType("m.room.message", c.HandleMessage)
+
+ for {
+ select {
+ case <-c.stop:
+ debug.Print("Stopping sync...")
+ c.running = false
+ return
+ default:
+ if err := c.lient.Sync(); err != nil {
+ debug.Print("Sync() errored", err)
+ }
+ }
+ }
+}
+
+func (c *MatrixContainer) HandleMessage(evt *gomatrix.Event) {
+ debug.Print("Message received")
+}
diff --git a/session.go b/session.go
new file mode 100644
index 0000000..8f0e5d8
--- /dev/null
+++ b/session.go
@@ -0,0 +1,102 @@
+// gomuks - A terminal Matrix client written in Go.
+// Copyright (C) 2018 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "path/filepath"
+
+ "github.com/matrix-org/gomatrix"
+)
+
+type Session struct {
+ MXID string `json:"-"`
+ path string `json:"-"`
+ AccessToken string
+ NextBatch string
+ FilterID string
+ Rooms map[string]*gomatrix.Room
+}
+
+func (config *Config) LoadSession(mxid string) {
+ config.Session = config.NewSession(mxid)
+ config.Session.Load()
+}
+
+func (config *Config) NewSession(mxid string) *Session {
+ return &Session{
+ MXID: mxid,
+ path: filepath.Join(config.dir, mxid+".session"),
+ Rooms: make(map[string]*gomatrix.Room),
+ }
+}
+
+func (s *Session) Load() {
+ data, err := ioutil.ReadFile(s.path)
+ if err != nil {
+ debug.Print("Failed to read session from", s.path)
+ panic(err)
+ }
+
+ err = json.Unmarshal(data, s)
+ if err != nil {
+ debug.Print("Failed to parse session at", s.path)
+ panic(err)
+ }
+}
+
+func (s *Session) Save() {
+ data, err := json.Marshal(s)
+ if err != nil {
+ debug.Print("Failed to marshal session of", s.MXID)
+ panic(err)
+ }
+
+ err = ioutil.WriteFile(s.path, data, 0600)
+ if err != nil {
+ debug.Print("Failed to write session to", s.path)
+ panic(err)
+ }
+}
+
+func (s *Session) LoadFilterID(_ string) string {
+ return s.FilterID
+}
+
+func (s *Session) LoadNextBatch(_ string) string {
+ return s.NextBatch
+}
+
+func (s *Session) LoadRoom(mxid string) *gomatrix.Room {
+ return s.Rooms[mxid]
+}
+
+func (s *Session) SaveFilterID(_, filterID string) {
+ s.FilterID = filterID
+ s.Save()
+}
+
+func (s *Session) SaveNextBatch(_, nextBatch string) {
+ s.NextBatch = nextBatch
+ s.Save()
+}
+
+func (s *Session) SaveRoom(room *gomatrix.Room) {
+ s.Rooms[room.ID] = room
+ s.Save()
+}