diff --git a/.gitignore b/.gitignore index 2c4a144..7776844 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ config.yaml discord logs/ +mautrix-discord.db registration.yaml diff --git a/bridge/bridge.go b/bridge/bridge.go index b7c9911..46d01a1 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -10,6 +10,7 @@ import ( "maunium.net/go/mautrix/appservice" "gitlab.com/beeper/discord/config" + "gitlab.com/beeper/discord/database" "gitlab.com/beeper/discord/version" ) @@ -23,6 +24,7 @@ type Bridge struct { log log.Logger as *appservice.AppService + db *database.Database eventProcessor *appservice.EventProcessor matrixHandler *matrixHandler bot *appservice.IntentAPI @@ -49,9 +51,16 @@ func New(cfg *config.Config) (*Bridge, error) { // Create the bot. bot := appservice.BotIntent() + // Setup the database. + db, err := cfg.CreateDatabase(logger) + if err != nil { + return nil, err + } + // Create the bridge. bridge := &Bridge{ as: appservice, + db: db, bot: bot, config: cfg, log: logger, diff --git a/bridge/matrix.go b/bridge/matrix.go index b52f609..607544f 100644 --- a/bridge/matrix.go +++ b/bridge/matrix.go @@ -2,8 +2,10 @@ package bridge import ( "maunium.net/go/maulogger/v2" + "maunium.net/go/mautrix" "maunium.net/go/mautrix/appservice" "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" ) type matrixHandler struct { @@ -25,7 +27,7 @@ func (b *Bridge) setupEvents() { b.eventProcessor.On(event.StateMember, b.matrixHandler.handleMembership) } -func (mh *MatrixHandler) join(evt *event.Event, intent *appservice.IntentAPI) *mautrix.RespJoinedMembers { +func (mh *matrixHandler) join(evt *event.Event, intent *appservice.IntentAPI) *mautrix.RespJoinedMembers { resp, err := intent.JoinRoomByID(evt.RoomID) if err != nil { mh.log.Debugfln("Failed to join room %s as %s with invite from %s: %v", evt.RoomID, intent.UserID, evt.Sender, err) @@ -74,7 +76,7 @@ func (mh *matrixHandler) handleMembership(evt *event.Event) { } // Grab the content of the event. - content := evt.Content.AsMessage() + content := evt.Content.AsMember() // TODO: handle invites from ourselfs? diff --git a/config/appservice.go b/config/appservice.go index 209f20e..9e2bc56 100644 --- a/config/appservice.go +++ b/config/appservice.go @@ -13,6 +13,8 @@ type appservice struct { Bot bot `yaml:"bot"` + Database database `yaml:"database"` + ASToken string `yaml:"as_token"` HSToken string `yaml:"hs_token"` } @@ -34,6 +36,10 @@ func (a *appservice) validate() error { a.Port = 29350 } + if err := a.Database.validate(); err != nil { + return err + } + if err := a.Bot.validate(); err != nil { return err } diff --git a/config/database.go b/config/database.go new file mode 100644 index 0000000..7fb569d --- /dev/null +++ b/config/database.go @@ -0,0 +1,58 @@ +package config + +import ( + log "maunium.net/go/maulogger/v2" + + db "gitlab.com/beeper/discord/database" +) + +type database struct { + Type string `yaml:"type"` + URI string `yaml:"uri"` + + MaxOpenConns int `yaml:"max_open_conns"` + MaxIdleConns int `yaml:"max_idle_conns"` +} + +func (d *database) validate() error { + if d.Type == "" { + d.Type = "sqlite3" + } + + if d.URI == "" { + d.URI = "mautrix-discord.db" + } + + if d.MaxOpenConns == 0 { + d.MaxOpenConns = 20 + } + + if d.MaxIdleConns == 0 { + d.MaxIdleConns = 2 + } + + return nil +} + +func (d *database) UnmarshalYAML(unmarshal func(interface{}) error) error { + type rawDatabase database + + raw := rawDatabase{} + if err := unmarshal(&raw); err != nil { + return err + } + + *d = database(raw) + + return d.validate() +} + +func (c *Config) CreateDatabase(baseLog log.Logger) (*db.Database, error) { + return db.New( + c.Appservice.Database.Type, + c.Appservice.Database.URI, + c.Appservice.Database.MaxOpenConns, + c.Appservice.Database.MaxIdleConns, + baseLog, + ) +} diff --git a/database/database.go b/database/database.go new file mode 100644 index 0000000..7af8509 --- /dev/null +++ b/database/database.go @@ -0,0 +1,46 @@ +package database + +import ( + "database/sql" + + _ "github.com/lib/pq" + _ "github.com/mattn/go-sqlite3" + + log "maunium.net/go/maulogger/v2" + + "gitlab.com/beeper/discord/database/migrations" +) + +type Database struct { + *sql.DB + log log.Logger + dialect string +} + +func New(dbType, uri string, maxOpenConns, maxIdleConns int, baseLog log.Logger) (*Database, error) { + conn, err := sql.Open(dbType, uri) + if err != nil { + return nil, err + } + + if dbType == "sqlite3" { + conn.Exec("PRAGMA foreign_keys = ON") + } + + conn.SetMaxOpenConns(maxOpenConns) + conn.SetMaxIdleConns(maxIdleConns) + + dbLog := baseLog.Sub("Database") + + if err := migrations.Run(conn, dbLog); err != nil { + return nil, err + } + + db := &Database{ + DB: conn, + log: dbLog, + dialect: dbType, + } + + return db, nil +} diff --git a/database/migrations/01-initial.sql b/database/migrations/01-initial.sql new file mode 100644 index 0000000..05304f8 --- /dev/null +++ b/database/migrations/01-initial.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS portal ( + did text, + receiver text, + mxid text UNIQUE, + + name text NOT NULL, + topic text NOT NULL, + avatar text NOT NULL, + + PRIMARY KEY (did, receiver) +); diff --git a/database/migrations/migrations.go b/database/migrations/migrations.go new file mode 100644 index 0000000..ab72c17 --- /dev/null +++ b/database/migrations/migrations.go @@ -0,0 +1,54 @@ +package migrations + +import ( + "database/sql" + "embed" + + "github.com/lopezator/migrator" + log "maunium.net/go/maulogger/v2" +) + +//go:embed *.sql +var migrations embed.FS + +func migrationFromFile(filename string) *migrator.Migration { + return &migrator.Migration{ + Name: filename, + Func: func(tx *sql.Tx) error { + data, err := migrations.ReadFile(filename) + if err != nil { + return err + } + + if _, err := tx.Exec(string(data)); err != nil { + return err + } + + return nil + }, + } +} + +func Run(db *sql.DB, baseLog log.Logger) error { + subLogger := baseLog.Sub("Migrations") + logger := migrator.LoggerFunc(func(msg string, args ...interface{}) { + subLogger.Infof(msg, args...) + }) + + m, err := migrator.New( + migrator.TableName("version"), + migrator.WithLogger(logger), + migrator.Migrations( + migrationFromFile("01-initial.sql"), + ), + ) + if err != nil { + return err + } + + if err := m.Migrate(db); err != nil { + return err + } + + return nil +} diff --git a/go.mod b/go.mod index 3180b16..8556e79 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,11 @@ go 1.17 require ( github.com/alecthomas/kong v0.2.18 + github.com/lib/pq v1.9.0 + github.com/lopezator/migrator v0.3.0 + github.com/mattn/go-sqlite3 v1.14.6 gopkg.in/yaml.v2 v2.4.0 + maunium.net/go/maulogger/v2 v2.3.1 maunium.net/go/mautrix v0.9.27 ) @@ -15,5 +19,4 @@ require ( github.com/pkg/errors v0.9.1 // indirect golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d // indirect - maunium.net/go/maulogger/v2 v2.3.1 // indirect ) diff --git a/go.sum b/go.sum index 8f4d10e..688693a 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= @@ -25,7 +27,12 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO 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/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lopezator/migrator v0.3.0 h1:VW/rR+J8NYwPdkBxjrFdjwejpgvP59LbmANJxXuNbuk= +github.com/lopezator/migrator v0.3.0/go.mod h1:bpVAVPkWSvTw8ya2Pk7E/KiNAyDWNImgivQY79o8/8I= +github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= 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= @@ -48,6 +55,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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 h1:1aflnvSoWWLI2k/dMUAl5lvU1YO4Mb4hz0gh+1rjcxU= @@ -63,6 +71,8 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn 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= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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= @@ -74,7 +84,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -maunium.net/go/maulogger/v2 v2.2.4 h1:oV2GDeM4fx1uRysdpDC0FcrPg+thFicSd9XzPcYMbVY= maunium.net/go/maulogger/v2 v2.2.4/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A= maunium.net/go/maulogger/v2 v2.3.1 h1:fwBYJne0pHvJrrIPHK+TAPfyxxbBEz46oVGez2x0ODE= maunium.net/go/maulogger/v2 v2.3.1/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=