From 7c9daffd78db7cb8f2646b0e8e9f5cc60a635c64 Mon Sep 17 00:00:00 2001 From: David Florness Date: Sat, 22 May 2021 18:12:29 -0400 Subject: [PATCH] Implement autojoin --- .gitignore | 1 + autojoin/cmd.go | 65 ++++++++++++++++++++++++ go.mod | 7 +++ go.sum | 75 ++++++++++++++++++++++++++++ main.go | 15 ++++++ shared/auth.go | 130 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 293 insertions(+) create mode 100644 .gitignore create mode 100644 autojoin/cmd.go create mode 100644 go.sum create mode 100644 main.go create mode 100644 shared/auth.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f3352b3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +axon diff --git a/autojoin/cmd.go b/autojoin/cmd.go new file mode 100644 index 0000000..1a7b513 --- /dev/null +++ b/autojoin/cmd.go @@ -0,0 +1,65 @@ +package autojoin + +import ( + "fmt" + "os" + + "github.com/rwxrob/cmdtab" + + "git.hnitbjorg.xyz/~edwargix/axon/shared" + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/event" +) + +func init() { + x := cmdtab.New("autojoin") + x.Summary = "wait for room invitations and automatically join" + + x.Method = func(args []string) error { + if len(args) != 0 { + return x.UsageError() + } + autojoin() + return nil + } +} + +func autojoin() { + data, err := shared.GetAuthInfo() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + client, err := mautrix.NewClient(data.Homeserver, data.UserID, data.AccessToken) + if err != nil { + panic(err) + } + + syncer := client.Syncer.(*mautrix.DefaultSyncer) + syncer.OnEvent(client.Store.(*mautrix.InMemoryStore).UpdateState) + syncer.OnEventType(event.StateMember, func(_ mautrix.EventSource, event *event.Event) { + // skip non-member events and member events that don't pertain + // to us + if *event.StateKey != data.UserID.String() || event.Type.Type != "m.room.member" { + return + } + content := event.Content.AsMember() + // skip non-invite messages + if content.Membership != "invite" { + return + } + fmt.Printf("Joining room %s...\n", event.RoomID) + _, err := client.JoinRoomByID(event.RoomID) + if err != nil { + panic(err) + } + fmt.Printf("Joined room %s\n", event.RoomID) + }) + + fmt.Println("Looking for rooms to join...") + err = client.Sync() + if err != nil { + panic(err) + } +} diff --git a/go.mod b/go.mod index 067417f..4b44371 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,10 @@ module git.hnitbjorg.xyz/~edwargix/axon go 1.16 + +require ( + github.com/kyoh86/xdg v1.2.0 + github.com/rwxrob/cmdtab v0.10.2 + golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a + maunium.net/go/mautrix v0.9.12 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3ec7b70 --- /dev/null +++ b/go.sum @@ -0,0 +1,75 @@ +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/kyoh86/xdg v1.2.0 h1:CERuT/ShdTDj+A2UaX3hQ3mOV369+Sj+wyn2nIRIIkI= +github.com/kyoh86/xdg v1.2.0/go.mod h1:/mg8zwu1+qe76oTFUBnyS7rJzk7LLC0VGEzJyJ19DHs= +github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rwxrob/cmdtab v0.10.2 h1:DElMFAqlJcfXTiZUSgU/c2AHAdRbxpOYQdYLvMpjF3k= +github.com/rwxrob/cmdtab v0.10.2/go.mod h1:AZ16dJkvySlkGy2PBZDcUcRdar+SLv7t1an8OOKkOag= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tidwall/gjson v1.6.8/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/sjson v1.1.5/go.mod h1:VuJzsZnTowhSxWdOgsAnb886i4AjEyTkk7tNtsL7EYE= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +maunium.net/go/maulogger/v2 v2.2.4/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A= +maunium.net/go/mautrix v0.9.12 h1:iNsKOCyK/BqN6Q/7A1aLj1B0/lMbr81LIg+Lry1Vjuo= +maunium.net/go/mautrix v0.9.12/go.mod h1:7IzKfWvpQtN+W2Lzxc0rLvIxFM3ryKX6Ys3S/ZoWbg8= diff --git a/main.go b/main.go new file mode 100644 index 0000000..9a8501c --- /dev/null +++ b/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/rwxrob/cmdtab" + _ "git.hnitbjorg.xyz/~edwargix/axon/autojoin" +) + +func main() { + x := cmdtab.New("axon", "autojoin") + x.Summary = "a set of simple Matrix CS utilities" + x.Usage = "(autojoin)" + x.Version = "v0.0.0" + + cmdtab.Execute("axon") +} diff --git a/shared/auth.go b/shared/auth.go new file mode 100644 index 0000000..2234324 --- /dev/null +++ b/shared/auth.go @@ -0,0 +1,130 @@ +package shared + +import ( + "bufio" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "syscall" + + "github.com/kyoh86/xdg" + "golang.org/x/crypto/ssh/terminal" + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/id" +) + +type AuthInfo struct { + AccessToken string `json:"access_token"` + DeviceID id.DeviceID `json:"device_id"` + Homeserver string `json:"homeserver"` + UserID id.UserID `json:"user_id"` + Username string `json:"username"` +} + +var authFname = xdg.DataHome() + "/axon/auth.json" + +func GetAuthInfo() (authInfo *AuthInfo, err error) { + if _, err = os.Stat(authFname); os.IsNotExist(err) { + fmt.Println("no cached auth data found; inquiring for credentials") + authInfo, err = inquireForAuthInfo() + if err == nil { + authInfo.save() + } + return authInfo, err + } + + jsonBytes, err := ioutil.ReadFile(authFname) + if err != nil { + return nil, fmt.Errorf("error reading auth file %s: %s", authFname, err) + } + + authInfo = &AuthInfo{} + err = json.Unmarshal(jsonBytes, authInfo) + if err != nil { + return nil, fmt.Errorf("error unmarshalling auth file: %s", err) + } + + return authInfo, nil +} + +func stripNewline(s string) string { + if s == "" { + return s + } + if s[len(s)-1] == '\n' { + return s[:len(s)-1] + } + return s +} + +func inquireForAuthInfo() (authInfo *AuthInfo, err error) { + reader := bufio.NewReader(os.Stdin) + + authInfo = &AuthInfo{} + + fmt.Print("Matrix username (@localpart:domain): ") + username, err := reader.ReadString('\n') + if err != nil { + return nil, fmt.Errorf("error reading username: %s", err) + } + username = stripNewline(username) + localPart, serverName, err := id.UserID(username).Parse() + if err != nil { + return nil, fmt.Errorf("error parsing username: %s", err) + } + authInfo.Username = localPart + + wellKnownResp, err := mautrix.DiscoverClientAPI(serverName) + if err != nil { + return nil, fmt.Errorf("error discovering homeserver: %s", err) + } + if wellKnownResp == nil { + fmt.Println("You should tell your homeserver sysadmin to setup /.well-known/matrix/client") + fmt.Print("homeserver URL: ") + homeserver, err := reader.ReadString('\n') + if err != nil { + return nil, fmt.Errorf("error reading homeserver: %s", err) + } + authInfo.Homeserver = stripNewline(homeserver) + } else { + authInfo.Homeserver = wellKnownResp.Homeserver.BaseURL + } + + fmt.Print("password: ") + rawPassword, err := terminal.ReadPassword(syscall.Stdin) + if err != nil { + return nil, fmt.Errorf("error reading password: %s", err) + } + // entering a password doesn't insert a newline + fmt.Println() + + client, err := mautrix.NewClient(authInfo.Homeserver, "", "") + if err != nil { + return nil, fmt.Errorf("error creating mautrix client: %s", err) + } + fmt.Printf("attempting login...\n") + respLogin, err := client.Login(&mautrix.ReqLogin{ + Type: "m.login.password", + Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: authInfo.Username}, + Password: string(rawPassword), + StoreCredentials: true, + }) + if err != nil { + return nil, fmt.Errorf("error logging in with given credentials: %s", err) + } + + authInfo.AccessToken = respLogin.AccessToken + authInfo.DeviceID = respLogin.DeviceID + authInfo.UserID = respLogin.UserID + + return authInfo, nil +} + +func (authInfo *AuthInfo) save() error { + jsonBytes, err := json.Marshal(*authInfo) + if err != nil { + return err + } + return ioutil.WriteFile(authFname, jsonBytes, 0600) +} -- 2.38.4