From 86942a34a2aa086dee76c9e4e0b6942848e1b979 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Mon, 17 Feb 2020 16:28:32 +0100 Subject: [PATCH] Fix XMPP && transmit nicknames from bridges to matrix --- appservice/account.go | 23 ++++++-- appservice/matrix.go | 13 +++++ appservice/server.go | 7 ++- connector/connector.go | 3 +- connector/irc/irc.go | 12 ++++- connector/xmpp/xmpp.go | 120 +++++++++++++++++++++++------------------ main.go | 2 + 7 files changed, 119 insertions(+), 61 deletions(-) diff --git a/appservice/account.go b/appservice/account.go index ae9716f..a20751f 100644 --- a/appservice/account.go +++ b/appservice/account.go @@ -89,7 +89,17 @@ func (a *Account) Left(roomId RoomID) { } func (a *Account) UserInfoUpdated(user UserID, info *UserInfo) { - // TODO + mx_user_id, err := dbGetMxUser(a.Protocol, user) + if err != nil { + return + } + + if info.DisplayName != "" { + mxProfileDisplayname(mx_user_id, fmt.Sprintf("%s (%s)", info.DisplayName, a.Protocol)) + } + if info.Avatar != nil { + // TODO + } } func (a *Account) RoomInfoUpdated(roomId RoomID, author UserID, info *RoomInfo) { @@ -98,7 +108,7 @@ func (a *Account) RoomInfoUpdated(roomId RoomID, author UserID, info *RoomInfo) return } - as_mxid := fmt.Sprintf("@%s:%s", registration.SenderLocalpart, config.MatrixDomain) + as_mxid := ezbrMxId() if len(author) > 0 { mx_user_id, err := dbGetMxUser(a.Protocol, author) if err == nil { @@ -109,7 +119,14 @@ func (a *Account) RoomInfoUpdated(roomId RoomID, author UserID, info *RoomInfo) if info.Topic != "" { mxRoomTopicAs(mx_room_id, info.Topic, as_mxid) } - // TODO + + if info.Name != "" { + mxRoomNameAs(mx_room_id, info.Name, as_mxid) + } + + if info.Picture != nil { + // TODO + } } func (a *Account) Event(event *Event) { diff --git a/appservice/matrix.go b/appservice/matrix.go index 75bcaa1..2c4562e 100644 --- a/appservice/matrix.go +++ b/appservice/matrix.go @@ -13,6 +13,12 @@ import ( . "git.deuxfleurs.fr/Deuxfleurs/easybridge/mxlib" ) +func ezbrMxId() string { + return fmt.Sprintf("@%s:%s", registration.SenderLocalpart, config.MatrixDomain) +} + +// ---- + var httpClient *http.Client func init() { @@ -234,6 +240,13 @@ func mxPutStateAs(room string, event_type string, key string, content map[string return err } +func mxRoomNameAs(room string, name string, as_user string) error { + content := map[string]interface{} { + "name": name, + } + return mxPutStateAs(room, "m.room.name", "", content, as_user) +} + func mxRoomTopicAs(room string, topic string, as_user string) error { content := map[string]interface{} { "topic": topic, diff --git a/appservice/server.go b/appservice/server.go index 2690e58..b67e813 100644 --- a/appservice/server.go +++ b/appservice/server.go @@ -34,12 +34,11 @@ func Start(r *mxlib.Registration, c *Config) (chan error, error) { return nil, err } - svc_mxid := fmt.Sprintf("@%s:%s", registration.SenderLocalpart, config.MatrixDomain) err = mxRegisterUser(registration.SenderLocalpart) if mxe, ok := err.(*mxlib.MxError); !ok || mxe.ErrCode != "M_USER_IN_USE" { return nil, err } - err = mxProfileDisplayname(svc_mxid, "Easybridge") + err = mxProfileDisplayname(ezbrMxId(), "Easybridge") if err != nil { return nil, err } @@ -123,6 +122,8 @@ func handleTxnEvent(e *mxlib.Event) { ev.Author = acct.Conn.User() ev.Room = room.RoomID acct.Conn.Send(ev) + } else { + log.Debugf("Could not find room account for %s %s %s", e.Sender, room.Protocol, room.RoomID) } } } else if e.Type == "m.room.member" { @@ -141,6 +142,8 @@ func handleTxnEvent(e *mxlib.Event) { if acct != nil { acct.Conn.Leave(room.RoomID) // TODO: manage autojoin list, remove this room + } else { + log.Debugf("Could not find room account for %s %s %s", e.Sender, room.Protocol, room.RoomID) } } } diff --git a/connector/connector.go b/connector/connector.go index 0afcd1c..9ff5d49 100644 --- a/connector/connector.go +++ b/connector/connector.go @@ -113,8 +113,7 @@ type Event struct { } type UserInfo struct { - Nickname string - Status string + DisplayName string Avatar MediaObject } diff --git a/connector/irc/irc.go b/connector/irc/irc.go index b673498..a57a2e2 100644 --- a/connector/irc/irc.go +++ b/connector/irc/irc.go @@ -263,12 +263,16 @@ func (irc *IRC) ircJoin(c *girc.Client, e girc.Event) { if e.Source.Name == irc.nick { irc.handler.Joined(room) } else { + user := UserID(e.Source.Name + "@" + irc.server) ev := &Event{ Type: EVENT_JOIN, - Author: UserID(e.Source.Name + "@" + irc.server), + Author: user, Room: room, } irc.handler.Event(ev) + irc.handler.UserInfoUpdated(user, &UserInfo{ + DisplayName: e.Source.Name, + }) } } @@ -277,12 +281,16 @@ func (irc *IRC) ircPart(c *girc.Client, e girc.Event) { if e.Source.Name == irc.nick { irc.handler.Left(room) } else { + user := UserID(e.Source.Name + "@" + irc.server) ev := &Event{ Type: EVENT_LEAVE, - Author: UserID(e.Source.Name + "@" + irc.server), + Author: user, Room: room, } irc.handler.Event(ev) + irc.handler.UserInfoUpdated(user, &UserInfo{ + DisplayName: e.Source.Name, + }) } } diff --git a/connector/xmpp/xmpp.go b/connector/xmpp/xmpp.go index 7516ebc..e50bb58 100644 --- a/connector/xmpp/xmpp.go +++ b/connector/xmpp/xmpp.go @@ -181,74 +181,73 @@ func (xm *XMPP) handleXMPP() error { switch v := m.(type) { case gxmpp.Chat: - log.Printf("== Receiving %#v\n", v) + remote_sp := strings.Split(v.Remote, "/") + // Skip self-sent events + if v.Remote == xm.jid || (v.Type == "groupchat" && len(remote_sp) == 2 && remote_sp[1] == xm.nickname) { + continue + } + + // If empty text, make sure we joined the room + // We would do this at every incoming message if it were not so costly if v.Text == "" && v.Type == "groupchat" { - // Empty message when we joined group chat - if !strings.Contains(v.Remote, "/") { - xm.handler.Joined(RoomID(v.Remote)) + xm.handler.Joined(RoomID(remote_sp[0])) + } + + // Handle subject change in group chats + if v.Subject != "" && v.Type == "groupchat" { + author := UserID("") + if len(remote_sp) == 2 { + author = UserID(remote_sp[1] + "@" + remote_sp[0]) } - if v.Subject != "" { - author := UserID("") - remote_sp := strings.Split(v.Remote, "/") - if len(remote_sp) == 2 { - author = UserID(remote_sp[1] + "@" + remote_sp[0]) - } - xm.handler.RoomInfoUpdated(RoomID(v.Remote), author, &RoomInfo{ - Topic: v.Subject, - }) - } - continue + xm.handler.RoomInfoUpdated(RoomID(remote_sp[0]), author, &RoomInfo{ + Topic: v.Subject, + }) } - if v.Text == "" || v.Remote == xm.jid { - continue - } - - event := &Event{ - Type: EVENT_MESSAGE, - Text: v.Text, - } - log.Printf("Remote: %s\n", v.Remote) - - if strings.HasPrefix(event.Text, "/me ") { - event.Type = EVENT_ACTION - event.Text = strings.Replace(event.Text, "/me ", "", 1) - } - - if v.Type == "chat" { - remote_jid := strings.Split(v.Remote, "/")[0] - event.Author = UserID(remote_jid) - xm.handler.Event(event) - } - if v.Type == "groupchat" { - remote := strings.Split(v.Remote, "/") - if len(remote) != 2 { - log.Printf("Invalid remote: %s\n", v.Remote) - continue - } - event.Room = RoomID(remote[0]) - event.Author = UserID(remote[1] + "@" + remote[0]) - - if strings.Contains(v.Text, "has set the subject to:") { - // TODO - continue + // Handle text message + if v.Text != "" { + event := &Event{ + Type: EVENT_MESSAGE, + Text: v.Text, } - xm.handler.Event(event) + if strings.HasPrefix(event.Text, "/me ") { + event.Type = EVENT_ACTION + event.Text = strings.Replace(event.Text, "/me ", "", 1) + } + + if v.Type == "chat" { + event.Author = UserID(remote_sp[0]) + xm.handler.Event(event) + } + if v.Type == "groupchat" && len(remote_sp) == 2 { + event.Room = RoomID(remote_sp[0]) + event.Author = UserID(remote_sp[1] + "@" + remote_sp[0]) + xm.handler.Event(event) + } } case gxmpp.Presence: remote := strings.Split(v.From, "/") - if muc, ok := xm.isMUC[remote[0]]; ok && muc && len(remote) == 2 { + if ismuc, ok := xm.isMUC[remote[0]]; ok && ismuc { + // skip presence with no user and self-presence + if len(remote) < 2 || remote[1] == xm.nickname { + continue + } + + user := UserID(remote[1] + "@" + remote[0]) event := &Event{ Type: EVENT_JOIN, Room: RoomID(remote[0]), - Author: UserID(remote[1] + "@" + remote[0]), + Author: user, } if v.Type == "unavailable" { event.Type = EVENT_LEAVE } xm.handler.Event(event) + xm.handler.UserInfoUpdated(user, &UserInfo{ + DisplayName: remote[1], + }) } // Do nothing. } @@ -264,8 +263,24 @@ func (xm *XMPP) SetUserInfo(info *UserInfo) error { } func (xm *XMPP) SetRoomInfo(roomId RoomID, info *RoomInfo) error { - // TODO - return fmt.Errorf("Not implemented") + if info.Topic != "" { + xm.conn.Send(gxmpp.Chat{ + Type: "groupchat", + Remote: string(roomId), + Subject: info.Topic, + }) + } + + if info.Picture != nil { + // TODO + return fmt.Errorf("Room picture change not implemented on xmpp") + } + + if info.Name != "" && info.Name != string(roomId) { + // TODO + return fmt.Errorf("Room name change not implemented on xmpp") + } + return nil } func (xm *XMPP) Join(roomId RoomID) error { @@ -286,6 +301,7 @@ func (xm *XMPP) Leave(roomId RoomID) { } func (xm *XMPP) Send(event *Event) error { + fmt.Printf("xm *XMPP Send %#v\n", event) if len(event.Recipient) > 0 { xm.conn.Send(gxmpp.Chat{ Type: "chat", diff --git a/main.go b/main.go index 105d798..14ec229 100644 --- a/main.go +++ b/main.go @@ -185,6 +185,8 @@ func main() { conn = &irc.IRC{} case "xmpp": conn = &xmpp.XMPP{} + default: + log.Fatalf("Invalid protocol %s", params.Protocol) } account := &appservice.Account{ MatrixUser: fmt.Sprintf("@%s:%s", user, config.MatrixDomain),