Files
postmoogle/vendor/gitlab.com/etke.cc/linkpearl/linkpearl.go
2022-11-16 23:00:58 +02:00

186 lines
4.4 KiB
Go

// Package linkpearl represents the library itself
package linkpearl
import (
"database/sql"
lru "github.com/hashicorp/golang-lru/v2"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/crypto"
"maunium.net/go/mautrix/event"
"gitlab.com/etke.cc/linkpearl/config"
"gitlab.com/etke.cc/linkpearl/store"
)
const (
// DefaultMaxRetries for operations like autojoin
DefaultMaxRetries = 10
// DefaultAccountDataCache size
DefaultAccountDataCache = 1000
)
// Linkpearl object
type Linkpearl struct {
db *sql.DB
acc *lru.Cache[string, map[string]string]
acr *Crypter
aclr map[string]string
log config.Logger
api *mautrix.Client
olm *crypto.OlmMachine
store *store.Store
joinPermit func(*event.Event) bool
autoleave bool
maxretries int
}
type ReqPresence struct {
Presence event.Presence `json:"presence"`
StatusMsg string `json:"status_msg,omitempty"`
}
func setDefaults(cfg *config.Config) {
if cfg.AccountDataLogReplace == nil {
cfg.AccountDataLogReplace = make(map[string]string)
}
if cfg.MaxRetries == 0 {
cfg.MaxRetries = DefaultMaxRetries
}
if cfg.AccountDataCache == 0 {
cfg.AccountDataCache = DefaultAccountDataCache
}
if cfg.JoinPermit == nil {
// By default, we approve all join requests
cfg.JoinPermit = func(*event.Event) bool { return true }
}
}
func initCrypter(secret string) (*Crypter, error) {
if secret == "" {
return nil, nil
}
return NewCrypter(secret)
}
// New linkpearl
func New(cfg *config.Config) (*Linkpearl, error) {
setDefaults(cfg)
api, err := mautrix.NewClient(cfg.Homeserver, "", "")
if err != nil {
return nil, err
}
api.Logger = cfg.APILogger
acc, _ := lru.New[string, map[string]string](cfg.AccountDataCache) //nolint:errcheck // addressed in setDefaults()
acr, err := initCrypter(cfg.AccountDataSecret)
if err != nil {
return nil, err
}
lp := &Linkpearl{
db: cfg.DB,
acc: acc,
acr: acr,
aclr: cfg.AccountDataLogReplace,
api: api,
log: cfg.LPLogger,
joinPermit: cfg.JoinPermit,
autoleave: cfg.AutoLeave,
maxretries: cfg.MaxRetries,
}
storer := store.New(cfg.DB, cfg.Dialect, cfg.StoreLogger)
if err = storer.CreateTables(); err != nil {
return nil, err
}
lp.store = storer
lp.api.Store = storer
if err = lp.login(cfg.Login, cfg.Password); err != nil {
return nil, err
}
if !cfg.NoEncryption {
if err = lp.store.WithCrypto(lp.api.UserID, lp.api.DeviceID, cfg.StoreLogger); err != nil {
return nil, err
}
lp.olm = crypto.NewOlmMachine(lp.api, cfg.CryptoLogger, lp.store, lp.store)
if err = lp.olm.Load(); err != nil {
return nil, err
}
}
return lp, nil
}
// GetClient returns underlying API client
func (l *Linkpearl) GetClient() *mautrix.Client {
return l.api
}
// GetDB returns underlying DB object
func (l *Linkpearl) GetDB() *sql.DB {
return l.db
}
// GetStore returns underlying persistent store object, compatible with crypto.Store, crypto.StateStore and mautrix.Storer
func (l *Linkpearl) GetStore() *store.Store {
return l.store
}
// GetMachine returns underlying OLM machine
func (l *Linkpearl) GetMachine() *crypto.OlmMachine {
return l.olm
}
// GetAccountDataCrypter returns crypter used for account data (if any)
func (l *Linkpearl) GetAccountDataCrypter() *Crypter {
return l.acr
}
// SetPresence (own). See https://spec.matrix.org/v1.3/client-server-api/#put_matrixclientv3presenceuseridstatus
func (l *Linkpearl) SetPresence(presence event.Presence, message string) error {
req := ReqPresence{Presence: presence, StatusMsg: message}
u := l.GetClient().BuildClientURL("v3", "presence", l.GetClient().UserID, "status")
_, err := l.GetClient().MakeRequest("PUT", u, req, nil)
return err
}
// SetJoinPermit sets the the join permit callback function
func (l *Linkpearl) SetJoinPermit(value func(*event.Event) bool) {
l.joinPermit = value
}
// Start performs matrix /sync
func (l *Linkpearl) Start(optionalStatusMsg ...string) error {
l.initSync()
var statusMsg string
if len(optionalStatusMsg) > 0 {
statusMsg = optionalStatusMsg[0]
}
err := l.SetPresence(event.PresenceOnline, statusMsg)
if err != nil {
l.log.Error("cannot set presence: %v", err)
}
defer l.Stop()
l.log.Info("client has been started")
return l.api.Sync()
}
// Stop the client
func (l *Linkpearl) Stop() {
l.log.Debug("stopping the client")
err := l.api.SetPresence(event.PresenceOffline)
if err != nil {
l.log.Error("cannot set presence: %v", err)
}
l.api.StopSync()
l.log.Info("client has been stopped")
}