diff options
Diffstat (limited to 'account.go')
-rw-r--r-- | account.go | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/account.go b/account.go new file mode 100644 index 0000000..8da6d44 --- /dev/null +++ b/account.go @@ -0,0 +1,331 @@ +package main + +import ( + "fmt" + "strings" + + log "github.com/sirupsen/logrus" + + . "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector" +) + +type Account struct { + MatrixUser string + AccountName string + Protocol string + Conn Connector + + JoinedRooms map[RoomID]bool +} + +var registeredAccounts = map[string]map[string]*Account{} + +func AddAccount(a *Account) { + if _, ok := registeredAccounts[a.MatrixUser]; !ok { + registeredAccounts[a.MatrixUser] = make(map[string]*Account) + } + registeredAccounts[a.MatrixUser][a.AccountName] = a + ezbrSystemSendf(a.MatrixUser, "Connecting to account %s (%s)", a.AccountName, a.Protocol) +} + +func FindAccount(mxUser string, name string) *Account { + if u, ok := registeredAccounts[mxUser]; ok { + if a, ok := u[name]; ok { + return a + } + } + return nil +} + +func FindJoinedAccount(mxUser string, protocol string, room RoomID) *Account { + if u, ok := registeredAccounts[mxUser]; ok { + for _, acct := range u { + if acct.Protocol == protocol { + if j, ok := acct.JoinedRooms[room]; ok && j { + return acct + } + } + } + } + return nil +} + +func RemoveAccount(mxUser string, name string) { + if u, ok := registeredAccounts[mxUser]; ok { + delete(u, name) + } +} + +func (a *Account) ezbrMessagef(format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + msg = fmt.Sprintf("%s: %s", a.Protocol, msg) + ezbrSystemSend(a.MatrixUser, msg) +} + +// ---- Begin event handlers ---- + +func (a *Account) Joined(roomId RoomID) { + err := a.joinedInternal(roomId) + if err != nil { + a.ezbrMessagef("Dropping Account.Joined %s: %s", roomId, err.Error()) + } +} + +func (a *Account) joinedInternal(roomId RoomID) error { + a.JoinedRooms[roomId] = true + + mx_room_id, err := dbGetMxRoom(a.Protocol, roomId) + if err != nil { + return err + } + + log.Debugf("Joined %s (%s)\n", roomId, a.MatrixUser) + + err = mx.RoomInvite(mx_room_id, a.MatrixUser) + if err != nil && strings.Contains(err.Error(), "already in the room") { + err = nil + } + return err +} + +// ---- + +func (a *Account) Left(roomId RoomID) { + err := a.leftInternal(roomId) + if err != nil { + a.ezbrMessagef("Dropping Account.Left %s: %s", roomId, err.Error()) + } +} + +func (a *Account) leftInternal(roomId RoomID) error { + delete(a.JoinedRooms, roomId) + + mx_room_id, err := dbGetMxRoom(a.Protocol, roomId) + if err != nil { + return err + } + + log.Printf("Left %s (%s)\n", roomId, a.MatrixUser) + + err = mx.RoomKick(mx_room_id, a.MatrixUser, fmt.Sprintf("got leave room event on %s", a.Protocol)) + if err != nil && strings.Contains(err.Error(), "not in the room") { + err = nil + } + return err +} + +// ---- + +func (a *Account) UserInfoUpdated(user UserID, info *UserInfo) { + err := a.userInfoUpdatedInternal(user, info) + if err != nil { + a.ezbrMessagef("Dropping Account.UserInfoUpdated %s: %s", user, err.Error()) + } +} + +func (a *Account) userInfoUpdatedInternal(user UserID, info *UserInfo) error { + mx_user_id, err := dbGetMxUser(a.Protocol, user) + if err != nil { + return err + } + + if info.DisplayName != "" { + err2 := mx.ProfileDisplayname(mx_user_id, fmt.Sprintf("%s (%s)", info.DisplayName, a.Protocol)) + if err2 != nil { + err = err2 + } + } + + if info.Avatar != nil { + cache_key := fmt.Sprintf("%s/user_avatar/%s", a.Protocol, user) + cache_val := info.Avatar.Filename() + if cache_val == "" || dbCacheTestAndSet(cache_key, cache_val) { + err2 := mx.ProfileAvatar(mx_user_id, info.Avatar) + if err2 != nil { + err = err2 + } + } + } + + return err +} + +// ---- + +func (a *Account) RoomInfoUpdated(roomId RoomID, author UserID, info *RoomInfo) { + err := a.roomInfoUpdatedInternal(roomId, author, info) + if err != nil { + a.ezbrMessagef("Dropping Account.RoomInfoUpdated %s: %s", roomId, err.Error()) + } +} + +func (a *Account) roomInfoUpdatedInternal(roomId RoomID, author UserID, info *RoomInfo) error { + mx_room_id, err := dbGetMxRoom(a.Protocol, roomId) + if err != nil { + return err + } + + as_mxid := ezbrMxId() + if len(author) > 0 { + mx_user_id, err2 := dbGetMxUser(a.Protocol, author) + if err2 == nil { + as_mxid = mx_user_id + } + } + + if info.Topic != "" { + err2 := mx.RoomTopicAs(mx_room_id, info.Topic, as_mxid) + if err2 != nil { + err = err2 + } + } + + if info.Name != "" { + name := fmt.Sprintf("%s (%s)", info.Name, a.Protocol) + err2 := mx.RoomNameAs(mx_room_id, name, as_mxid) + if err2 != nil { + err = err2 + } + } + + if info.Picture != nil { + cache_key := fmt.Sprintf("%s/room_picture/%s", a.Protocol, roomId) + cache_val := info.Picture.Filename() + if cache_val == "" || dbCacheTestAndSet(cache_key, cache_val) { + err2 := mx.RoomAvatarAs(mx_room_id, info.Picture, as_mxid) + if err2 != nil { + err = err2 + } + } + } + + return err +} + +// ---- + +func (a *Account) Event(event *Event) { + err := a.eventInternal(event) + if err != nil { + a.ezbrMessagef("Dropping Account.Event %s %s %s: %s", event.Author, event.Recipient, event.Room, err.Error()) + } +} + +func (a *Account) eventInternal(event *Event) error { + // TODO: automatically ignore events that come from one of our bridged matrix users + // TODO: deduplicate events if we have several matrix users joined the same room (hard problem) + + mx_user_id, err := dbGetMxUser(a.Protocol, event.Author) + if err != nil { + return err + } + + if event.Type == EVENT_JOIN { + log.Printf("%s join %s %s", a.Protocol, event.Author, event.Room) + mx_room_id, err := dbGetMxRoom(a.Protocol, event.Room) + if err != nil { + return err + } + + err = mx.RoomInvite(mx_room_id, mx_user_id) + if err != nil { + if strings.Contains(err.Error(), "already in the room") { + return nil + } + return err + } + + return mx.RoomJoinAs(mx_room_id, mx_user_id) + } else if event.Type == EVENT_LEAVE { + log.Printf("%s join %s %s", a.Protocol, event.Author, event.Room) + mx_room_id, err := dbGetMxRoom(a.Protocol, event.Room) + if err != nil { + return err + } + + return mx.RoomLeaveAs(mx_room_id, mx_user_id) + } else { + log.Printf("%s msg %s %s", a.Protocol, event.Author, event.Room) + mx_room_id := "" + + if len(event.Room) > 0 { + mx_room_id, err = dbGetMxRoom(a.Protocol, event.Room) + if err != nil { + return err + } + } else { + mx_room_id, err = dbGetMxPmRoom(a.Protocol, event.Author, mx_user_id, a.MatrixUser, a.AccountName) + if err != nil { + return err + } + } + + if event.Id != "" { + cache_key := fmt.Sprintf("%s/event_seen/%s/%s", + a.Protocol, mx_room_id, event.Id) + if !dbCacheTestAndSet(cache_key, "yes") { + // false: cache key was not modified, meaning we + // already saw the event + return nil + } + } + + typ := "m.text" + if event.Type == EVENT_ACTION { + typ = "m.emote" + } + + err = mx.SendMessageAs(mx_room_id, typ, event.Text, mx_user_id) + if err != nil { + return err + } + + if event.Attachments != nil { + for _, file := range event.Attachments { + mxfile, err := mx.UploadMedia(file) + if err != nil { + return err + } + content := map[string]interface{}{ + "body": mxfile.Filename(), + "filename": mxfile.Filename(), + "url": fmt.Sprintf("mxc://%s/%s", mxfile.MxcServer, mxfile.MxcMediaId), + } + if sz := mxfile.ImageSize(); sz != nil { + content["msgtype"] = "m.image" + content["info"] = map[string]interface{}{ + "mimetype": mxfile.Mimetype(), + "size": mxfile.Size(), + "w": sz.Width, + "h": sz.Height, + } + } else { + content["msgtype"] = "m.file" + content["info"] = map[string]interface{}{ + "mimetype": mxfile.Mimetype(), + "size": mxfile.Size(), + } + } + err = mx.SendAs(mx_room_id, "m.room.message", content, mx_user_id) + if err != nil { + return err + } + } + } + return nil + } +} + +// ---- + +func (a *Account) CacheGet(key string) string { + cache_key := fmt.Sprintf("%s/account/%s/%s/%s", + a.Protocol, a.MatrixUser, a.AccountName, key) + return dbCacheGet(cache_key) +} + +func (a *Account) CachePut(key string, value string) { + cache_key := fmt.Sprintf("%s/account/%s/%s/%s", + a.Protocol, a.MatrixUser, a.AccountName, key) + dbCachePut(cache_key, value) +} |