mautrix-discord/bridge/bridge.go
2022-05-21 00:13:33 +03:00

203 lines
4.1 KiB
Go

package bridge
import (
"errors"
"fmt"
"sync"
"time"
log "maunium.net/go/maulogger/v2"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/appservice"
"maunium.net/go/mautrix/id"
"go.mau.fi/mautrix-discord/config"
"go.mau.fi/mautrix-discord/database"
"go.mau.fi/mautrix-discord/version"
)
const (
reconnectDelay = 10 * time.Second
)
type Bridge struct {
Config *config.Config
log log.Logger
as *appservice.AppService
db *database.Database
eventProcessor *appservice.EventProcessor
matrixHandler *matrixHandler
bot *appservice.IntentAPI
provisioning *ProvisioningAPI
usersByMXID map[id.UserID]*User
usersByID map[string]*User
usersLock sync.Mutex
managementRooms map[id.RoomID]*User
managementRoomsLock sync.Mutex
portalsByMXID map[id.RoomID]*Portal
portalsByID map[database.PortalKey]*Portal
portalsLock sync.Mutex
puppets map[string]*Puppet
puppetsByCustomMXID map[id.UserID]*Puppet
puppetsLock sync.Mutex
StateStore *database.SQLStateStore
crypto Crypto
}
func New(cfg *config.Config) (*Bridge, error) {
// Create the logger.
logger, err := cfg.CreateLogger()
if err != nil {
return nil, err
}
logger.Infoln("Initializing version", version.String)
// Create and initialize the app service.
appservice, err := cfg.CreateAppService()
if err != nil {
return nil, err
}
appservice.Log = log.Sub("matrix")
appservice.Init()
// Create the bot.
bot := appservice.BotIntent()
// Setup the database.
db, err := cfg.CreateDatabase(logger)
if err != nil {
return nil, err
}
// Create the state store
logger.Debugln("Initializing state store")
stateStore := database.NewSQLStateStore(db)
appservice.StateStore = stateStore
// Create the bridge.
bridge := &Bridge{
as: appservice,
db: db,
bot: bot,
Config: cfg,
log: logger,
usersByMXID: make(map[id.UserID]*User),
usersByID: make(map[string]*User),
managementRooms: make(map[id.RoomID]*User),
portalsByMXID: make(map[id.RoomID]*Portal),
portalsByID: make(map[database.PortalKey]*Portal),
puppets: make(map[string]*Puppet),
puppetsByCustomMXID: make(map[id.UserID]*Puppet),
StateStore: stateStore,
}
bridge.crypto = NewCryptoHelper(bridge)
if cfg.Appservice.Provisioning.Enabled() {
bridge.provisioning = newProvisioningAPI(bridge)
}
// Setup the event processors
bridge.setupEvents()
return bridge, nil
}
func (b *Bridge) connect() error {
b.log.Debugln("Checking connection to homeserver")
for {
resp, err := b.bot.Whoami()
if err != nil {
if errors.Is(err, mautrix.MUnknownToken) {
b.log.Fatalln("Access token invalid. Is the registration installed in your homeserver correctly?")
return fmt.Errorf("invalid access token")
}
b.log.Errorfln("Failed to connect to homeserver : %v", err)
b.log.Errorfln("reconnecting in %s", reconnectDelay)
time.Sleep(reconnectDelay)
} else if resp.UserID != b.bot.UserID {
b.log.Fatalln("Unexpected user ID in whoami call: got %s, expected %s", resp.UserID, b.bot.UserID)
return fmt.Errorf("expected user id %q but got %q", b.bot.UserID, resp.UserID)
} else {
break
}
}
b.log.Debugln("Connected to homeserver")
return nil
}
func (b *Bridge) Start() error {
b.log.Infoln("Bridge started")
if err := b.connect(); err != nil {
return err
}
if b.crypto != nil {
if err := b.crypto.Init(); err != nil {
b.log.Fatalln("Error initializing end-to-bridge encryption:", err)
return err
}
}
b.log.Debugln("Starting application service HTTP server")
go b.as.Start()
b.log.Debugln("Starting event processor")
go b.eventProcessor.Start()
go b.updateBotProfile()
if b.crypto != nil {
go b.crypto.Start()
}
go b.startUsers()
// Finally tell the appservice we're ready
b.as.Ready = true
return nil
}
func (b *Bridge) Stop() {
if b.crypto != nil {
b.crypto.Stop()
}
b.as.Stop()
b.eventProcessor.Stop()
for _, user := range b.usersByMXID {
if user.Session == nil {
continue
}
b.log.Debugln("Disconnecting", user.MXID)
user.Session.Close()
}
b.log.Infoln("Bridge stopped")
}