Add initial login view and debug panel with tview
This commit is contained in:
		
							
								
								
									
										12
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| .idea/ | ||||
| @@ -1,2 +1,2 @@ | ||||
| # gomuks | ||||
| A Go terminal matrix client. | ||||
| A terminal Matrix client written in Go. | ||||
|   | ||||
							
								
								
									
										70
									
								
								config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								config.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										56
									
								
								debug.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								debug.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||
| } | ||||
							
								
								
									
										14
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| ) | ||||
							
								
								
									
										113
									
								
								gomuks.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								gomuks.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||
| } | ||||
							
								
								
									
										118
									
								
								matrix.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								matrix.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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") | ||||
| } | ||||
							
								
								
									
										102
									
								
								session.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								session.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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() | ||||
| } | ||||
		Reference in New Issue
	
	Block a user