commit e6c580d73a64b13098061b4c7987c263b5b8a77b
parent 528f91784d10cd7116db9199ebb68d679d6ec938
Author: MTRNord <mtrnord1@gmail.com>
Date: Sun, 26 Nov 2017 16:09:51 +0100
Use new logger, use own storage for sync, use own syncer with custom filter, try QThreads
Signed-off-by: MTRNord <mtrnord1@gmail.com>
Diffstat:
13 files changed, 397 insertions(+), 214 deletions(-)
diff --git a/main.go b/main.go
@@ -2,9 +2,9 @@ package main
import (
"fmt"
- "log"
"os"
"os/signal"
+ "path/filepath"
"runtime"
"sync"
"syscall"
@@ -12,20 +12,44 @@ import (
"github.com/Nordgedanken/Morpheus/matrix"
"github.com/Nordgedanken/Morpheus/matrix/db"
"github.com/Nordgedanken/Morpheus/ui"
- "github.com/Nordgedanken/Morpheus/util"
"github.com/dgraph-io/badger"
+ "github.com/matrix-org/dugong"
"github.com/matrix-org/gomatrix"
+ "github.com/shibukawa/configdir"
+ log "github.com/sirupsen/logrus"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/gui"
"github.com/therecipe/qt/widgets"
)
var window *widgets.QMainWindow
-var localLog *log.Logger
func main() {
runtime.GOMAXPROCS(128)
+ // Init Logs
+ configDirs := configdir.New("Nordgedanken", "Morpheus")
+
+ log.SetFormatter(&log.TextFormatter{
+ TimestampFormat: "2006-01-02 15:04:05.000000",
+ DisableColors: false,
+ DisableTimestamp: true,
+ DisableSorting: true,
+ QuoteEmptyFields: true,
+ })
+
+ log.AddHook(dugong.NewFSHook(
+ filepath.Join(filepath.ToSlash(configDirs.QueryFolders(configdir.Global)[0].Path)+"/log/", "info.log"),
+ filepath.Join(filepath.ToSlash(configDirs.QueryFolders(configdir.Global)[0].Path)+"/log/", "warn.log"),
+ filepath.Join(filepath.ToSlash(configDirs.QueryFolders(configdir.Global)[0].Path)+"/log/", "error.log"),
+ &log.TextFormatter{
+ TimestampFormat: "2006-01-02 15:04:05.000000",
+ DisableColors: true,
+ DisableTimestamp: false,
+ DisableSorting: false,
+ }, &dugong.DailyRotationSchedule{GZip: false},
+ ))
+
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
@@ -34,27 +58,17 @@ func main() {
os.Exit(1)
}()
- var file *os.File
- var err error
-
- localLog = util.Logger()
- localLog, file, err = util.StartFileLog(localLog)
- if err != nil {
- localLog.Fatalln(err)
- }
- defer file.Close()
-
UserDB, DBOpenErr := db.OpenUserDB()
if DBOpenErr != nil {
- localLog.Fatalln(DBOpenErr)
+ log.Fatalln(DBOpenErr)
}
CacheDB, DBOpenErr := db.OpenCacheDB()
if DBOpenErr != nil {
- localLog.Fatalln(DBOpenErr)
+ log.Fatalln(DBOpenErr)
}
- localLog.Println("Starting Morpheus")
+ log.Infoln("Starting Morpheus")
app := widgets.NewQApplication(len(os.Args), os.Args)
@@ -136,16 +150,16 @@ func main() {
return nil
})
if DBErr != nil {
- localLog.Fatalln("Login: ", DBErr)
+ log.Errorln("Login: ", DBErr)
}
if accessToken != "" && homeserverURL != "" && userID != "" {
var wg sync.WaitGroup
- localLog.Println("Starting Auto Login Sequenze in background")
+ log.Infoln("Starting Auto Login Sequenze in background")
results := make(chan *gomatrix.Client)
wg.Add(1)
- go matrix.DoLogin("", "", homeserverURL, userID, accessToken, localLog, results, &wg)
+ go matrix.DoLogin("", "", homeserverURL, userID, accessToken, results, &wg)
go func() {
wg.Wait() // wait for each execTask to return
@@ -157,13 +171,9 @@ func main() {
//TODO Don't switch screen on wrong login data.
MainUIStruct := ui.NewMainUIStruct(windowWidth, windowHeight, window)
MainUIStruct.SetCli(result)
- MainUILoggerInitErr := MainUIStruct.InitLogger()
- if MainUILoggerInitErr != nil {
- localLog.Fatalln(MainUILoggerInitErr)
- }
mainUIErr := MainUIStruct.NewUI()
if mainUIErr != nil {
- localLog.Fatalln("mainUI: ", mainUIErr)
+ log.Errorln("mainUI: ", mainUIErr)
return
}
MainUIStruct.GetWidget().Resize2(windowWidth, windowHeight)
@@ -172,13 +182,9 @@ func main() {
} else {
//Show loginUI
LoginUIStruct := ui.NewLoginUIStruct(windowWidth, windowHeight, window)
- LoginUIStructInitErr := LoginUIStruct.InitLogger()
- if LoginUIStructInitErr != nil {
- localLog.Fatalln(LoginUIStructInitErr)
- }
loginUIErr := LoginUIStruct.NewUI()
if loginUIErr != nil {
- localLog.Fatalln("Login Err: ", loginUIErr)
+ log.Errorln("Login Err: ", loginUIErr)
return
}
LoginUIStruct.GetWidget().Resize2(windowWidth, windowHeight)
@@ -192,19 +198,19 @@ func main() {
_ = widgets.QApplication_Exec()
defer UserDB.Close()
defer CacheDB.Close()
- localLog.Println("Stopping Morpheus")
+ log.Infoln("Stopping Morpheus")
}
func cleanup() {
- fmt.Println("cleanup")
+ log.Infoln("cleanup")
UserDB, DBOpenErr := db.OpenUserDB()
if DBOpenErr != nil {
- localLog.Fatalln(DBOpenErr)
+ log.Errorln(DBOpenErr)
}
CacheDB, DBOpenErr := db.OpenCacheDB()
if DBOpenErr != nil {
- localLog.Fatalln(DBOpenErr)
+ log.Errorln(DBOpenErr)
}
UserDB.Close()
diff --git a/matrix/db/db.go b/matrix/db/db.go
@@ -2,7 +2,6 @@ package db
import (
"errors"
- "log"
"os"
"path/filepath"
"strconv"
@@ -50,7 +49,6 @@ func OpenCacheDB() (db *badger.DB, err error) {
})
if CacheDB == nil {
- log.Println(CacheDB)
err = errors.New("missing CacheDB")
return
}
@@ -93,7 +91,6 @@ func OpenUserDB() (db *badger.DB, err error) {
})
if UserDB == nil {
- log.Println(UserDB)
err = errors.New("missing UserDB")
return
}
@@ -104,7 +101,6 @@ func OpenUserDB() (db *badger.DB, err error) {
// CacheMessageEvents writes message infos into the cache into the defined room
func CacheMessageEvents(id, sender, roomID, message string, timestamp int64) (err error) {
- log.Println("start Caching")
db, DBOpenErr := OpenCacheDB()
if DBOpenErr != nil {
err = DBOpenErr
@@ -113,7 +109,6 @@ func CacheMessageEvents(id, sender, roomID, message string, timestamp int64) (er
// Update cache
DBerr := db.Update(func(txn *badger.Txn) error {
- log.Println(id)
DBSetIDErr := txn.Set([]byte("room|"+roomID+"|messages|"+id+"|id"), []byte(id))
if DBSetIDErr != nil {
return DBSetIDErr
@@ -135,7 +130,6 @@ func CacheMessageEvents(id, sender, roomID, message string, timestamp int64) (er
})
if DBerr != nil {
- log.Println("DBERR: ", DBerr)
err = DBerr
return
}
diff --git a/matrix/db/storer.go b/matrix/db/storer.go
@@ -0,0 +1,53 @@
+package db
+
+import (
+ "github.com/dgraph-io/badger"
+)
+
+// Storer is the interface which needs to be conformed to in order to persist Go-NEB data
+type Storer interface {
+ UpdateNextBatch(userID, nextBatch string) (err error)
+ LoadNextBatch(userID string) (nextBatch string, err error)
+}
+
+type MorpheusStorage struct {
+ Database *badger.DB
+}
+
+// UpdateNextBatch updates the next_batch token for the given user.
+func (m *MorpheusStorage) UpdateNextBatch(userID, nextBatch string) (err error) {
+ DBerr := m.Database.Update(func(txn *badger.Txn) error {
+
+ DBSetNextBatchErr := txn.Set([]byte("matrix|"+userID+"|nextBatch|"), []byte(nextBatch))
+ return DBSetNextBatchErr
+ })
+ if DBerr != nil {
+ err = DBerr
+ return
+ }
+ return
+}
+
+// LoadNextBatch loads the next_batch token for the given user.
+func (m *MorpheusStorage) LoadNextBatch(userID string) (nextBatch string, err error) {
+ DBerr := m.Database.View(func(txn *badger.Txn) error {
+
+ nextBatchItem, NextBatchErr := txn.Get([]byte("matrix|" + userID + "|nextBatch|"))
+ if NextBatchErr != nil {
+ return NextBatchErr
+ }
+
+ nextBatchByte, nextBatchByteErr := nextBatchItem.Value()
+ if nextBatchByteErr != nil {
+ return nextBatchByteErr
+ }
+
+ nextBatch = string(nextBatchByte)
+ return nil
+ })
+ if DBerr != nil {
+ err = DBerr
+ return
+ }
+ return
+}
diff --git a/matrix/login.go b/matrix/login.go
@@ -1,19 +1,19 @@
package matrix
import (
- "log"
"strings"
"sync"
"github.com/Nordgedanken/Morpheus/matrix/db"
"github.com/matrix-org/gomatrix"
+ log "github.com/sirupsen/logrus"
)
//GetClient returns a Client
func GetClient(homeserverURL, userID, accessToken string) (client *gomatrix.Client, err error) {
- db, DBOpenErr := db.OpenUserDB()
+ userDB, DBOpenErr := db.OpenUserDB()
if DBOpenErr != nil {
- localLog.Fatalln(DBOpenErr)
+ log.Errorln(DBOpenErr)
}
client, ClientErr := gomatrix.NewClient(homeserverURL, userID, accessToken)
@@ -22,7 +22,7 @@ func GetClient(homeserverURL, userID, accessToken string) (client *gomatrix.Clie
return
}
- txn := db.NewTransaction(true) // Read-write txn
+ txn := userDB.NewTransaction(true) // Read-write txn
DBSetAccessTokenErr := txn.Set([]byte("user|accessToken"), []byte(client.AccessToken))
if DBSetAccessTokenErr != nil {
err = DBSetAccessTokenErr
@@ -74,13 +74,13 @@ func LoginUser(username, password string) (*gomatrix.Client, error) {
return nil, err
}
- db, DBOpenErr := db.OpenUserDB()
+ userDB, DBOpenErr := db.OpenUserDB()
if DBOpenErr != nil {
- localLog.Fatalln(DBOpenErr)
+ log.Errorln(DBOpenErr)
}
cli.SetCredentials(resp.UserID, resp.AccessToken)
- txn := db.NewTransaction(true) // Read-write txn
+ txn := userDB.NewTransaction(true) // Read-write txn
DBSetAccessTokenErr := txn.Set([]byte("user|accessToken"), []byte(resp.AccessToken))
if DBSetAccessTokenErr != nil {
return nil, DBSetAccessTokenErr
@@ -110,7 +110,7 @@ func LoginUser(username, password string) (*gomatrix.Client, error) {
}
// DoLogin generates the needed Client
-func DoLogin(username, password, homeserverURL, userID, accessToken string, localLog *log.Logger, results chan<- *gomatrix.Client, wg *sync.WaitGroup) {
+func DoLogin(username, password, homeserverURL, userID, accessToken string, results chan<- *gomatrix.Client, wg *sync.WaitGroup) {
defer wg.Done()
var cli *gomatrix.Client
if accessToken != "" && homeserverURL != "" && userID != "" {
@@ -123,14 +123,14 @@ func DoLogin(username, password, homeserverURL, userID, accessToken string, loca
cli, cliErr = GetClient("https://"+homeserverURL, userID, accessToken)
}
if cliErr != nil {
- localLog.Println(cliErr)
+ log.Errorln(cliErr)
}
cli.SetCredentials(userID, accessToken)
} else {
var err error
cli, err = LoginUser(username, password)
if err != nil {
- localLog.Println(err)
+ log.Errorln(err)
}
}
diff --git a/matrix/rooms.go b/matrix/rooms.go
@@ -10,6 +10,7 @@ import (
"github.com/Nordgedanken/Morpheus/matrix/db"
"github.com/dgraph-io/badger"
"github.com/matrix-org/gomatrix"
+ log "github.com/sirupsen/logrus"
"github.com/therecipe/qt/gui"
)
@@ -63,7 +64,7 @@ func (r *Room) GetRoomAvatar() (avatarResp *gui.QPixmap, err error) {
// Get the image Data
cacheDB, DBOpenErr := db.OpenCacheDB()
if DBOpenErr != nil {
- localLog.Fatalln(DBOpenErr)
+ log.Fatalln(DBOpenErr)
}
// Init local vars
@@ -104,14 +105,14 @@ func (r *Room) GetRoomAvatar() (avatarResp *gui.QPixmap, err error) {
}
IMGdata = data
} else {
- localLog.Println("Generating Room Avatar")
+ log.Println("Generating Room Avatar")
var GenerateImgErr error
var roomName string
if r.RoomName == "" {
r.crawlRoomName()
}
roomName = r.RoomName
- localLog.Println(roomName)
+ log.Println(roomName)
if roomName == "" {
roomName = "#"
}
@@ -170,12 +171,12 @@ func (r *Room) crawlRoomName() {
}{}
if roomNameStateEventErr := r.cli.StateEvent(r.RoomID, "m.room.name", "", &roomName); roomNameStateEventErr != nil {
- localLog.Println(roomNameStateEventErr)
+ log.Println(roomNameStateEventErr)
// Not returning as a Error NotFound is allowed
}
if roomName.Name == "" {
if roomCanoncialAliasStateEventErr := r.cli.StateEvent(r.RoomID, "m.room.canonical_alias", "", &roomCanoncialAlias); roomCanoncialAliasStateEventErr != nil {
- localLog.Println(roomCanoncialAliasStateEventErr)
+ log.Println(roomCanoncialAliasStateEventErr)
// Not returning as a Error NotFound is allowed
}
if roomCanoncialAlias.Alias == "" {
diff --git a/matrix/syncer/store.go b/matrix/syncer/store.go
@@ -0,0 +1,39 @@
+package syncer
+
+import (
+ "github.com/Nordgedanken/Morpheus/matrix/db"
+ "github.com/matrix-org/gomatrix"
+ log "github.com/sirupsen/logrus"
+)
+
+// MorpheusStore implements the gomatrix.Storer interface.
+//
+// It persists the next batch token in the database, and includes a ClientConfig for the client.
+type MorpheusStore struct {
+ gomatrix.InMemoryStore
+ CacheDatabase db.Storer
+}
+
+// SaveNextBatch saves to the database.
+func (m *MorpheusStore) SaveNextBatch(userID, nextBatch string) {
+ if err := m.CacheDatabase.UpdateNextBatch(userID, nextBatch); err != nil {
+ log.WithFields(log.Fields{
+ log.ErrorKey: err,
+ "user_id": userID,
+ "next_batch": nextBatch,
+ }).Error("Failed to persist next_batch token")
+ }
+}
+
+// LoadNextBatch loads from the database.
+func (m *MorpheusStore) LoadNextBatch(userID string) string {
+ token, err := m.CacheDatabase.LoadNextBatch(userID)
+ if err != nil {
+ log.WithFields(log.Fields{
+ log.ErrorKey: err,
+ "user_id": userID,
+ }).Error("Failed to load next_batch token")
+ return ""
+ }
+ return token
+}
diff --git a/matrix/syncer/syncer.go b/matrix/syncer/syncer.go
@@ -0,0 +1,151 @@
+package syncer
+
+import (
+ "encoding/json"
+ "fmt"
+ "runtime/debug"
+ "time"
+
+ "github.com/matrix-org/gomatrix"
+)
+
+type MorpheusSyncer struct {
+ UserID string
+ Store gomatrix.Storer
+ listeners map[string][]OnEventListener // event type to listeners array
+}
+
+// OnEventListener can be used with DefaultSyncer.OnEventType to be informed of incoming events.
+type OnEventListener func(*gomatrix.Event)
+
+// NewMorpheusSyncer returns an instantiated MorpheusSyncer
+func NewMorpheusSyncer(userID string, store gomatrix.Storer) *MorpheusSyncer {
+ return &MorpheusSyncer{
+ UserID: userID,
+ Store: store,
+ listeners: make(map[string][]OnEventListener),
+ }
+}
+
+// ProcessResponse processes the /sync response in a way suitable for bots. "Suitable for bots" means a stream of
+// unrepeating events. Returns a fatal error if a listener panics.
+func (s *MorpheusSyncer) ProcessResponse(res *gomatrix.RespSync, since string) (err error) {
+ if !s.shouldProcessResponse(res, since) {
+ return
+ }
+
+ defer func() {
+ if r := recover(); r != nil {
+ err = fmt.Errorf("ProcessResponse panicked! userID=%s since=%s panic=%s\n%s", s.UserID, since, r, debug.Stack())
+ }
+ }()
+
+ for roomID, roomData := range res.Rooms.Join {
+ room := s.getOrCreateRoom(roomID)
+ for _, event := range roomData.State.Events {
+ event.RoomID = roomID
+ room.UpdateState(&event)
+ s.notifyListeners(&event)
+ }
+ for _, event := range roomData.Timeline.Events {
+ event.RoomID = roomID
+ s.notifyListeners(&event)
+ }
+ }
+ for roomID, roomData := range res.Rooms.Invite {
+ room := s.getOrCreateRoom(roomID)
+ for _, event := range roomData.State.Events {
+ event.RoomID = roomID
+ room.UpdateState(&event)
+ s.notifyListeners(&event)
+ }
+ }
+ for roomID, roomData := range res.Rooms.Leave {
+ room := s.getOrCreateRoom(roomID)
+ for _, event := range roomData.Timeline.Events {
+ if event.StateKey != nil {
+ event.RoomID = roomID
+ room.UpdateState(&event)
+ s.notifyListeners(&event)
+ }
+ }
+ }
+ return
+}
+
+// OnEventType allows callers to be notified when there are new events for the given event type.
+// There are no duplicate checks.
+func (s *MorpheusSyncer) OnEventType(eventType string, callback OnEventListener) {
+ _, exists := s.listeners[eventType]
+ if !exists {
+ s.listeners[eventType] = []OnEventListener{}
+ }
+ s.listeners[eventType] = append(s.listeners[eventType], callback)
+}
+
+// shouldProcessResponse returns true if the response should be processed. May modify the response to remove
+// stuff that shouldn't be processed.
+func (s *MorpheusSyncer) shouldProcessResponse(resp *gomatrix.RespSync, since string) bool {
+ if since == "" {
+ return false
+ }
+ // This is a horrible hack because /sync will return the most recent messages for a room
+ // as soon as you /join it. We do NOT want to process those events in that particular room
+ // because they may have already been processed (if you toggle the bot in/out of the room).
+ //
+ // Work around this by inspecting each room's timeline and seeing if an m.room.member event for us
+ // exists and is "join" and then discard processing that room entirely if so.
+ // TODO: We probably want to process messages from after the last join event in the timeline.
+ for roomID, roomData := range resp.Rooms.Join {
+ for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- {
+ e := roomData.Timeline.Events[i]
+ if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID {
+ m := e.Content["membership"]
+ mship, ok := m.(string)
+ if !ok {
+ continue
+ }
+ if mship == "join" {
+ _, ok := resp.Rooms.Join[roomID]
+ if !ok {
+ continue
+ }
+ delete(resp.Rooms.Join, roomID) // don't re-process messages
+ delete(resp.Rooms.Invite, roomID) // don't re-process invites
+ break
+ }
+ }
+ }
+ }
+ return true
+}
+
+// getOrCreateRoom must only be called by the Sync() goroutine which calls ProcessResponse()
+func (s *MorpheusSyncer) getOrCreateRoom(roomID string) *gomatrix.Room {
+ room := s.Store.LoadRoom(roomID)
+ if room == nil { // create a new Room
+ room = gomatrix.NewRoom(roomID)
+ s.Store.SaveRoom(room)
+ }
+ return room
+}
+
+func (s *MorpheusSyncer) notifyListeners(event *gomatrix.Event) {
+ listeners, exists := s.listeners[event.Type]
+ if !exists {
+ return
+ }
+ for _, fn := range listeners {
+ fn(event)
+ }
+}
+
+// OnFailedSync always returns a 10 second wait period between failed /syncs, never a fatal error.
+func (s *MorpheusSyncer) OnFailedSync(res *gomatrix.RespSync, err error) (time.Duration, error) {
+ return 10 * time.Second, nil
+}
+
+// GetFilterJSON returns a filter with a timeline limit of 50.
+func (s *MorpheusSyncer) GetFilterJSON(userID string) json.RawMessage {
+ return json.RawMessage(`{"room":{"state":{"types":["m.room.*"]},"timeline":{"limit":20,"types":["m.room.message"]}}}`)
+}
diff --git a/matrix/user.go b/matrix/user.go
@@ -12,15 +12,14 @@ import (
"image/png"
// image/png needed to load png images
_ "image/png"
- "log"
- "os"
"strconv"
"strings"
"unicode"
"unicode/utf8"
+ log "github.com/sirupsen/logrus"
+
"github.com/Nordgedanken/Morpheus/matrix/db"
- "github.com/Nordgedanken/Morpheus/util"
"github.com/disintegration/letteravatar"
"github.com/matrix-org/gomatrix"
"github.com/therecipe/qt/gui"
@@ -39,20 +38,6 @@ import (
_ "golang.org/x/image/tiff"
)
-var localLog *log.Logger
-
-func init() {
- var file *os.File
- var err error
-
- localLog = util.Logger()
- localLog, file, err = util.StartFileLog(localLog)
- if err != nil {
- localLog.Fatalln(err)
- }
- defer file.Close()
-}
-
type circle struct {
p image.Point
r int
@@ -108,7 +93,7 @@ func GetOwnUserAvatar(cli *gomatrix.Client) (avatar *gui.QPixmap, err error) {
func GetUserAvatar(cli *gomatrix.Client, mxid string, size int) (avatarResp *gui.QPixmap, err error) {
cacheDB, DBOpenErr := db.OpenCacheDB()
if DBOpenErr != nil {
- localLog.Fatalln(DBOpenErr)
+ log.Fatalln(DBOpenErr)
}
// Init local vars
@@ -116,18 +101,20 @@ func GetUserAvatar(cli *gomatrix.Client, mxid string, size int) (avatarResp *gui
var IMGdata []byte
// Get cache
- txn := cacheDB.NewTransaction(false)
- avatarDataItem, QueryErr := txn.Get([]byte("user|" + mxid + "|avatarData" + strconv.Itoa(size) + "x" + strconv.Itoa(size)))
- if QueryErr != nil && QueryErr != badger.ErrKeyNotFound {
- err = QueryErr
- return
- }
- if QueryErr != badger.ErrKeyNotFound {
- avatarDataByte, avatarDataErr := avatarDataItem.Value()
- avatarData = avatarDataByte
- if avatarDataErr != nil {
- err = avatarDataErr
+ DBErr := cacheDB.View(func(txn *badger.Txn) error {
+ roomAvatarDataItem, QueryErr := txn.Get([]byte("user|" + mxid + "|avatarData" + strconv.Itoa(size) + "x" + strconv.Itoa(size)))
+ if QueryErr != nil && QueryErr != badger.ErrKeyNotFound {
+ return QueryErr
+ }
+ if QueryErr != badger.ErrKeyNotFound {
+ avatarDataBytes, avatarDataErr := roomAvatarDataItem.Value()
+ avatarData = avatarDataBytes
+ return avatarDataErr
}
+ return nil
+ })
+ if DBErr != nil {
+ err = DBErr
return
}
@@ -171,8 +158,10 @@ func GetUserAvatar(cli *gomatrix.Client, mxid string, size int) (avatarResp *gui
}
// Update cache
- txn := cacheDB.NewTransaction(true)
- DBSetErr := txn.Set([]byte("user|"+mxid+"|avatarData"+strconv.Itoa(size)+"x"+strconv.Itoa(size)), IMGdata)
+ DBSetErr := cacheDB.Update(func(txn *badger.Txn) error {
+ DBSetErr := txn.Set([]byte("user|"+mxid+"|avatarData"+strconv.Itoa(size)+"x"+strconv.Itoa(size)), IMGdata)
+ return DBSetErr
+ })
if DBSetErr != nil {
err = DBSetErr
return
diff --git a/ui/LoginUI.go b/ui/LoginUI.go
@@ -4,8 +4,8 @@ import (
"sync"
"github.com/Nordgedanken/Morpheus/matrix"
- "github.com/Nordgedanken/Morpheus/util"
"github.com/matrix-org/gomatrix"
+ log "github.com/sirupsen/logrus"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/widgets"
)
@@ -38,17 +38,6 @@ func (l *LoginUI) GetWidget() (widget *widgets.QWidget) {
return
}
-// InitLogger adds a new logger to the LoginUI struct
-func (l *LoginUI) InitLogger() error {
- localLog := util.Logger()
- localLog, _, err := util.StartFileLog(localLog)
- if err != nil {
- return err
- }
- l.localLog = localLog
- return nil
-}
-
// NewUI initializes a new login Screen
func (l *LoginUI) NewUI() (err error) {
widget := widgets.NewQWidget(nil, 0)
@@ -134,11 +123,11 @@ func (l *LoginUI) login() (err error) {
var wg sync.WaitGroup
if l.username != "" && l.password != "" {
- l.localLog.Println("Starting Login Sequenze in background")
+ log.Infoln("Starting Login Sequenze in background")
results := make(chan *gomatrix.Client)
wg.Add(1)
- go matrix.DoLogin(l.username, l.password, "", "", "", l.localLog, results, &wg)
+ go matrix.DoLogin(l.username, l.password, "", "", "", results, &wg)
go func() {
wg.Wait() // wait for each execTask to return
@@ -150,11 +139,6 @@ func (l *LoginUI) login() (err error) {
//TODO Don't switch screen on wrong login data.
l.cli = result
MainUIStruct := NewMainUIStructWithExistingConfig(l.config, l.window)
- MainUILoggerInitErr := MainUIStruct.InitLogger()
- if MainUILoggerInitErr != nil {
- err = MainUILoggerInitErr
- return
- }
mainUIErr := MainUIStruct.NewUI()
if mainUIErr != nil {
err = mainUIErr
@@ -164,7 +148,7 @@ func (l *LoginUI) login() (err error) {
l.window.Resize(l.widget.Size())
}
} else {
- l.localLog.Println("Username and/or password is empty. Do Nothing.")
+ log.Warningln("Username and/or password is empty. Do Nothing.")
}
return
}
diff --git a/ui/MainUI.go b/ui/MainUI.go
@@ -2,18 +2,18 @@ package ui
import (
"fmt"
- "log"
"strconv"
"strings"
"sync"
"github.com/Nordgedanken/Morpheus/matrix"
"github.com/Nordgedanken/Morpheus/matrix/db"
- "github.com/Nordgedanken/Morpheus/util"
+ "github.com/Nordgedanken/Morpheus/matrix/syncer"
"github.com/dgraph-io/badger"
"github.com/matrix-org/gomatrix"
"github.com/pkg/errors"
"github.com/rhinoman/go-commonmark"
+ log "github.com/sirupsen/logrus"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/gui"
"github.com/therecipe/qt/uitools"
@@ -55,20 +55,12 @@ func (m *MainUI) GetWidget() (widget *widgets.QWidget) {
return
}
-// InitLogger adds a new logger to the MainUI struct
-func (m *MainUI) InitLogger() error {
- localLog := util.Logger()
- localLog, _, err := util.StartFileLog(localLog)
- if err != nil {
- return err
- }
- m.localLog = localLog
- return nil
-}
-
// NewUI initializes a new Main Screen
func (m *MainUI) NewUI() (err error) {
m.widget = widgets.NewQWidget(nil, 0)
+ m.widgetThread = core.NewQThread(nil)
+ m.widget.MoveToThread(m.widgetThread)
+ m.widgetThread.Start()
var loader = uitools.NewQUiLoader(nil)
var file = core.NewQFile2(":/qml/ui/chat.ui")
@@ -78,6 +70,9 @@ func (m *MainUI) NewUI() (err error) {
file.Close()
m.messageScrollArea = widgets.NewQScrollAreaFromPointer(m.widget.FindChild("messageScroll", core.Qt__FindChildrenRecursively).Pointer())
+ m.messageScrollAreaThread = core.NewQThread(nil)
+ m.messageScrollArea.MoveToThread(m.messageScrollAreaThread)
+ m.messageScrollAreaThread.Start()
messagesScrollAreaContent := widgets.NewQWidgetFromPointer(m.widget.FindChild("messagesScrollAreaContent", core.Qt__FindChildrenRecursively).Pointer())
roomScrollArea := widgets.NewQScrollAreaFromPointer(m.widget.FindChild("roomScroll", core.Qt__FindChildrenRecursively).Pointer())
roomScrollAreaContent := widgets.NewQWidgetFromPointer(m.widget.FindChild("roomScrollAreaContent", core.Qt__FindChildrenRecursively).Pointer())
@@ -139,7 +134,7 @@ func (m *MainUI) NewUI() (err error) {
} else {
own = false
}
- NewMessageErr := m.MessageListLayout.NewMessage(messageBody, m.cli, sender, timestamp, m.messageScrollArea, own)
+ NewMessageErr := m.MessageListLayout.NewMessage(messageBody, m.cli, sender, timestamp, m.messageScrollArea, own, m)
if NewMessageErr != nil {
err = NewMessageErr
return
@@ -222,47 +217,47 @@ func (m *MainUI) sendMessage(message string) (err error) {
func (m *MainUI) logout(widget *widgets.QWidget, messageScrollArea *widgets.QScrollArea) (err error) {
//TODO register enter and show loader or so
- m.localLog.Println("Starting Logout Sequence in background")
+ log.Infoln("Starting Logout Sequence in background")
var wg sync.WaitGroup
results := make(chan bool)
wg.Add(1)
- go func(cli *gomatrix.Client, localLog *log.Logger, results chan<- bool) {
+ go func(cli *gomatrix.Client, results chan<- bool) {
defer wg.Done()
_, LogoutErr := cli.Logout()
if LogoutErr != nil {
- localLog.Println(LogoutErr)
+ log.Errorln(LogoutErr)
results <- false
}
cli.ClearCredentials()
- db, DBOpenErr := db.OpenUserDB()
+ userDB, DBOpenErr := db.OpenUserDB()
if DBOpenErr != nil {
- localLog.Fatalln(DBOpenErr)
+ log.Errorln(DBOpenErr)
}
//Flush complete DB
- txn := db.NewTransaction(true) // Read-write txn
+ txn := userDB.NewTransaction(true) // Read-write txn
QueryErr := txn.Delete([]byte(""))
if QueryErr != nil {
- localLog.Println(QueryErr)
+ log.Errorln(QueryErr)
results <- false
}
CommitErr := txn.Commit(nil)
if CommitErr != nil {
- localLog.Println(CommitErr)
+ log.Errorln(CommitErr)
results <- false
}
- DBPurgeErr := db.PurgeOlderVersions()
+ DBPurgeErr := userDB.PurgeOlderVersions()
if DBPurgeErr != nil {
- localLog.Println(DBPurgeErr)
+ log.Errorln(DBPurgeErr)
results <- false
} else {
results <- true
}
- }(m.cli, m.localLog, results)
+ }(m.cli, results)
go func() {
wg.Wait() // wait for each execTask to return
@@ -278,11 +273,6 @@ func (m *MainUI) logout(widget *widgets.QWidget, messageScrollArea *widgets.QScr
messageScrollArea.DisconnectResizeEvent()
LoginUIStruct := NewLoginUIStructWithExistingConfig(m.config, m.window)
- LoginUIStructInitErr := LoginUIStruct.InitLogger()
- if LoginUIStructInitErr != nil {
- err = LoginUIStructInitErr
- return
- }
loginUIErr := LoginUIStruct.NewUI()
if loginUIErr != nil {
err = loginUIErr
@@ -296,9 +286,23 @@ func (m *MainUI) logout(widget *widgets.QWidget, messageScrollArea *widgets.QScr
func (m *MainUI) startSync() (err error) {
//Start Syncer!
- m.syncer = m.cli.Syncer.(*gomatrix.DefaultSyncer)
- m.storage = gomatrix.NewInMemoryStore()
+ CacheDB, DBOpenErr := db.OpenCacheDB()
+ if DBOpenErr != nil {
+ log.Errorln(DBOpenErr)
+ }
+
+ m.SetCacheDB(&db.MorpheusStorage{
+ Database: CacheDB,
+ })
+ m.storage = &syncer.MorpheusStore{
+ InMemoryStore: *gomatrix.NewInMemoryStore(),
+ CacheDatabase: m.cacheDB,
+ }
+
+ m.syncer = syncer.NewMorpheusSyncer(m.cli.UserID, m.storage)
+
m.cli.Store = m.storage
+ m.cli.Syncer = m.syncer
m.syncer.Store = m.storage
m.syncer.OnEventType("m.room.message", func(ev *gomatrix.Event) {
@@ -320,7 +324,7 @@ func (m *MainUI) startSync() (err error) {
// Start Non-blocking sync
go func() {
- m.localLog.Println("Start sync")
+ log.Infoln("Start sync")
for {
if e := m.cli.Sync(); e != nil {
@@ -368,14 +372,12 @@ func (m *MainUI) loadCache() (err error) {
barAtBottom = true
}
- db, DBOpenErr := db.OpenCacheDB()
+ cacheDB, DBOpenErr := db.OpenCacheDB()
if DBOpenErr != nil {
err = DBOpenErr
}
- log.Println("load cache")
-
- DBerr := db.View(func(txn *badger.Txn) error {
+ DBerr := cacheDB.View(func(txn *badger.Txn) error {
MsgOpts := badger.DefaultIteratorOptions
MsgOpts.PrefetchSize = 10
MsgIt := txn.NewIterator(MsgOpts)
@@ -590,12 +592,12 @@ func (m *MainUI) loadCache() (err error) {
return nil
})
if DBerr != nil {
- fmt.Println("DBERR: ", DBerr)
+ log.Errorln("DBERR: ", DBerr)
err = DBerr
return
}
- if !barAtBottom {
+ if barAtBottom {
bar.SetValue(bar.Maximum())
}
diff --git a/ui/messageList.go b/ui/messageList.go
@@ -1,7 +1,6 @@
package ui
import (
- "fmt"
"time"
"github.com/Nordgedanken/Morpheus/matrix"
@@ -45,10 +44,9 @@ func NewMessageList(scrollArea *widgets.QScrollArea, messageView *widgets.QWidge
}
// NewMessage adds a new message object to the view
-func (messageViewLayout *QVBoxLayoutWithTriggerSlot) NewMessage(body string, cli *gomatrix.Client, sender string, timestamp int64, scrollArea *widgets.QScrollArea, own bool) (err error) {
+func (messageViewLayout *QVBoxLayoutWithTriggerSlot) NewMessage(body string, cli *gomatrix.Client, sender string, timestamp int64, scrollArea *widgets.QScrollArea, own bool, mainUIStruct *MainUI) (err error) {
barAtBottom := false
bar := scrollArea.VerticalScrollBar()
- fmt.Println(bar.Value())
if bar.Value() == bar.Maximum() {
barAtBottom = true
}
@@ -59,6 +57,9 @@ func (messageViewLayout *QVBoxLayoutWithTriggerSlot) NewMessage(body string, cli
}
var widget = widgets.NewQWidget(nil, 0)
+ widgetThread := core.NewQThread(nil)
+ widget.MoveToThread(widgetThread)
+ widgetThread.Start()
var loader = uitools.NewQUiLoader(nil)
var file *core.QFile
@@ -76,6 +77,9 @@ func (messageViewLayout *QVBoxLayoutWithTriggerSlot) NewMessage(body string, cli
timestampString := timestampFormat.Format("15:04:05 - Mon 2.01.2006")
messageWidget := widgets.NewQWidgetFromPointer(widget.FindChild("message", core.Qt__FindChildrenRecursively).Pointer())
+ messageWidgetThread := core.NewQThread(nil)
+ messageWidget.MoveToThread(messageWidgetThread)
+ messageWidgetThread.Start()
avatarLogo := widgets.NewQLabelFromPointer(widget.FindChild("avatar", core.Qt__FindChildrenRecursively).Pointer())
messageContent := widgets.NewQLabelFromPointer(widget.FindChild("messageContent", core.Qt__FindChildrenRecursively).Pointer())
timestampContent := widgets.NewQLabelFromPointer(widget.FindChild("timestamp", core.Qt__FindChildrenRecursively).Pointer())
@@ -94,7 +98,6 @@ func (messageViewLayout *QVBoxLayoutWithTriggerSlot) NewMessage(body string, cli
} else {
senderDisplayName = senderDisplayNameResp.DisplayName
}
- fmt.Println(senderDisplayName)
senderContent.SetText(senderDisplayName)
timestampContent.SetText(timestampString)
avatarLogo.SetPixmap(avatar)
@@ -115,8 +118,6 @@ func (messageViewLayout *QVBoxLayoutWithTriggerSlot) NewMessage(body string, cli
messageViewLayout.InsertWidget(messageViewLayout.Count()+1, messageWidget, 0, core.Qt__AlignBottom)
- fmt.Println(barAtBottom)
- fmt.Println(bar.Maximum())
if barAtBottom {
bar.SetValue(bar.Maximum())
}
diff --git a/ui/types.go b/ui/types.go
@@ -1,10 +1,11 @@
package ui
import (
- "log"
-
"github.com/Nordgedanken/Morpheus/matrix"
+ "github.com/Nordgedanken/Morpheus/matrix/db"
+ "github.com/Nordgedanken/Morpheus/matrix/syncer"
"github.com/matrix-org/gomatrix"
+ "github.com/therecipe/qt/core"
"github.com/therecipe/qt/widgets"
)
@@ -16,30 +17,40 @@ type config struct {
windowWidth int
windowHeight int
- cli *gomatrix.Client
+ matrixClient
+}
+
+type matrixClient struct {
+ databases
+ cli *gomatrix.Client
+ syncer *syncer.MorpheusSyncer
}
-// Logger holds the initialised Logger
-type logger struct {
- localLog *log.Logger
+type databases struct {
+ cacheDB db.Storer
+}
+
+// SetCurrentRoom sets the new room ID of the MainUI
+func (d *databases) SetCacheDB(db db.Storer) {
+ d.cacheDB = db
}
// MainUI holds information about the MainUI
type MainUI struct {
- logger
config
- widget *widgets.QWidget
- RoomAvatar *widgets.QLabel
- RoomTitle *widgets.QLabel
- RoomTopic *widgets.QLabel
- MainWidget *widgets.QWidget
- MessageListLayout *QVBoxLayoutWithTriggerSlot
- messageScrollArea *widgets.QScrollArea
+ widget *widgets.QWidget
+ widgetThread *core.QThread
+ RoomAvatar *widgets.QLabel
+ RoomTitle *widgets.QLabel
+ RoomTopic *widgets.QLabel
+ MainWidget *widgets.QWidget
+ MessageListLayout *QVBoxLayoutWithTriggerSlot
+ messageScrollArea *widgets.QScrollArea
+ messageScrollAreaThread *core.QThread
window *widgets.QMainWindow
- syncer *gomatrix.DefaultSyncer
- storage *gomatrix.InMemoryStore
+ storage *syncer.MorpheusStore
rooms map[string]*matrix.Room
currentRoom string
}
@@ -51,7 +62,6 @@ func (m *MainUI) SetCurrentRoom(RoomID string) {
// LoginUI holds information about the LoginUI
type LoginUI struct {
- logger
config
widget *widgets.QWidget
window *widgets.QMainWindow
diff --git a/util/logger.go b/util/logger.go
@@ -1,47 +0,0 @@
-package util
-
-import (
- "io"
- "log"
- "os"
- "path/filepath"
- "sync"
- "time"
-
- "github.com/shibukawa/configdir"
-)
-
-var logInstance *log.Logger
-var once sync.Once
-
-//Logger Exports a default Logger
-func Logger() *log.Logger {
- once.Do(func() {
- logInstance = &log.Logger{}
- })
- return logInstance
-}
-
-//StartFileLog initialises the logging function
-func StartFileLog(localLog *log.Logger) (logger *log.Logger, logFile *os.File, err error) {
- configDirs := configdir.New("Nordgedanken", "Morpheus")
- if _, StatErr := os.Stat(filepath.ToSlash(configDirs.QueryFolders(configdir.Global)[0].Path) + "/log/"); os.IsNotExist(StatErr) {
- MkdirErr := os.MkdirAll(filepath.ToSlash(configDirs.QueryFolders(configdir.Global)[0].Path)+"/log/", 0666)
- if MkdirErr != nil {
- err = MkdirErr
- return
- }
- }
- f, err := os.OpenFile(filepath.ToSlash(configDirs.QueryFolders(configdir.Global)[0].Path)+"/log/Morpheus.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
- if err != nil {
- localLog.Printf("error opening file: %v", err)
- }
-
- mw := io.MultiWriter(os.Stdout, f)
-
- localLog.SetOutput(mw)
- localLog.SetPrefix(time.Now().Format("2006-01-02 15:04:05") + " - ")
- logger = localLog
- logFile = f
- return
-}