diff --git a/commands.go b/commands.go index 34255ef..5b795fc 100644 --- a/commands.go +++ b/commands.go @@ -56,7 +56,7 @@ func (handler *CommandHandler) Handle(roomID types.MatrixRoomID, user *User, mes args := strings.Split(message, " ") cmd := strings.ToLower(args[0]) ce := &CommandEvent{ - Bot: handler.bridge.AS.BotIntent(), + Bot: handler.bridge.Bot, Bridge: handler.bridge, Handler: handler, RoomID: roomID, diff --git a/database/message.go b/database/message.go index d4e3a8c..733a91e 100644 --- a/database/message.go +++ b/database/message.go @@ -30,12 +30,12 @@ type MessageQuery struct { func (mq *MessageQuery) CreateTable() error { _, err := mq.db.Exec(`CREATE TABLE IF NOT EXISTS message ( - chat_jid VARCHAR(25) NOT NULL, - chat_receiver VARCHAR(25) NOT NULL, - jid VARCHAR(255) NOT NULL, + chat_jid VARCHAR(25), + chat_receiver VARCHAR(25), + jid VARCHAR(255), mxid VARCHAR(255) NOT NULL UNIQUE, - PRIMARY KEY (chat_jid, jid), + PRIMARY KEY (chat_jid, chat_receiver, jid), FOREIGN KEY (chat_jid, chat_receiver) REFERENCES portal(jid, receiver) )`) return err @@ -97,9 +97,9 @@ func (msg *Message) Scan(row Scannable) *Message { } func (msg *Message) Insert() error { - _, err := msg.db.Exec("INSERT INTO message VALUES (?, ?, ?)", msg.Chat.JID, msg.Chat.Receiver, msg.JID, msg.MXID) + _, err := msg.db.Exec("INSERT INTO message VALUES (?, ?, ?, ?)", msg.Chat.JID, msg.Chat.Receiver, msg.JID, msg.MXID) if err != nil { - msg.log.Warnfln("Failed to update %s: %v", msg.Chat, msg.JID, err) + msg.log.Warnfln("Failed to insert %s: %v", msg.Chat, msg.JID, err) } return err } diff --git a/database/portal.go b/database/portal.go index 1a8586d..31f3a58 100644 --- a/database/portal.go +++ b/database/portal.go @@ -122,13 +122,15 @@ type Portal struct { } func (portal *Portal) Scan(row Scannable) *Portal { - err := row.Scan(&portal.Key.JID, &portal.Key.Receiver, &portal.MXID, &portal.Name, &portal.Topic, &portal.Avatar) + var mxid sql.NullString + err := row.Scan(&portal.Key.JID, &portal.Key.Receiver, &mxid, &portal.Name, &portal.Topic, &portal.Avatar) if err != nil { if err != sql.ErrNoRows { portal.log.Errorln("Database scan failed:", err) } return nil } + portal.MXID = mxid.String return portal } diff --git a/database/puppet.go b/database/puppet.go index 401a5c8..1b11dcf 100644 --- a/database/puppet.go +++ b/database/puppet.go @@ -74,13 +74,16 @@ type Puppet struct { } func (puppet *Puppet) Scan(row Scannable) *Puppet { - err := row.Scan(&puppet.JID, &puppet.Displayname, &puppet.Avatar) + var displayname, avatar sql.NullString + err := row.Scan(&puppet.JID, &displayname, &avatar) if err != nil { if err != sql.ErrNoRows { puppet.log.Errorln("Database scan failed:", err) } return nil } + puppet.Displayname = displayname.String + puppet.Avatar = avatar.String return puppet } diff --git a/database/user.go b/database/user.go index b650cb3..686ce02 100644 --- a/database/user.go +++ b/database/user.go @@ -18,10 +18,12 @@ package database import ( "database/sql" + "strings" "github.com/Rhymen/go-whatsapp" log "maunium.net/go/maulogger" "maunium.net/go/mautrix-whatsapp/types" + "maunium.net/go/mautrix-whatsapp/whatsapp-ext" ) type UserQuery struct { @@ -40,8 +42,7 @@ func (uq *UserQuery) CreateTable() error { client_token VARCHAR(255), server_token VARCHAR(255), enc_key BLOB, - mac_key BLOB, - wid VARCHAR(255) + mac_key BLOB )`) return err } @@ -74,7 +75,7 @@ func (uq *UserQuery) GetByMXID(userID types.MatrixUserID) *User { } func (uq *UserQuery) GetByJID(userID types.WhatsAppID) *User { - row := uq.db.QueryRow("SELECT * FROM user WHERE jid=?", userID) + row := uq.db.QueryRow("SELECT * FROM user WHERE jid=?", stripSuffix(userID)) if row == nil { return nil } @@ -92,28 +93,42 @@ type User struct { } func (user *User) Scan(row Scannable) *User { - sess := whatsapp.Session{} - err := row.Scan(&user.MXID, &user.JID, &user.ManagementRoom, &sess.ClientId, &sess.ClientToken, &sess.ServerToken, - &sess.EncKey, &sess.MacKey, &sess.Wid) + var managementRoom, clientID, clientToken, serverToken, jid sql.NullString + var encKey, macKey []byte + err := row.Scan(&user.MXID, &jid, &managementRoom, &clientID, &clientToken, &serverToken, &encKey, &macKey) if err != nil { if err != sql.ErrNoRows { user.log.Errorln("Database scan failed:", err) } return nil } - if len(sess.ClientId) > 0 { - user.Session = &sess + if len(jid.String) > 0 && len(clientID.String) > 0 { + user.JID = jid.String + whatsappExt.NewUserSuffix + user.Session = &whatsapp.Session{ + ClientId: clientID.String, + ClientToken: clientToken.String, + ServerToken: serverToken.String, + EncKey: encKey, + MacKey: macKey, + Wid: jid.String + whatsappExt.OldUserSuffix, + } } else { user.Session = nil } return user } -func (user *User) jidPtr() *string { - if len(user.JID) > 0 { - return &user.JID +func stripSuffix(jid types.WhatsAppID) string { + if len(jid) == 0 { + return jid } - return nil + + index := strings.IndexRune(jid, '@') + if index < 0 { + return jid + } + + return jid[:index] } func (user *User) sessionUnptr() (sess whatsapp.Session) { @@ -125,16 +140,17 @@ func (user *User) sessionUnptr() (sess whatsapp.Session) { func (user *User) Insert() error { sess := user.sessionUnptr() - _, err := user.db.Exec("INSERT INTO user VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", user.MXID, user.jidPtr(), user.ManagementRoom, - sess.ClientId, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey, sess.Wid) + _, err := user.db.Exec("INSERT INTO user VALUES (?, ?, ?, ?, ?, ?, ?, ?)", user.MXID, stripSuffix(user.JID), + user.ManagementRoom, + sess.ClientId, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey) return err } func (user *User) Update() error { sess := user.sessionUnptr() - _, err := user.db.Exec("UPDATE user SET jid=?, management_room=?, client_id=?, client_token=?, server_token=?, enc_key=?, mac_key=?, wid=? WHERE mxid=?", - user.jidPtr(), user.ManagementRoom, - sess.ClientId, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey, sess.Wid, + _, err := user.db.Exec("UPDATE user SET jid=?, management_room=?, client_id=?, client_token=?, server_token=?, enc_key=?, mac_key=? WHERE mxid=?", + stripSuffix(user.JID), user.ManagementRoom, + sess.ClientId, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey, user.MXID) return err } diff --git a/formatting.go b/formatting.go index bd3eb3c..896710e 100644 --- a/formatting.go +++ b/formatting.go @@ -24,6 +24,7 @@ import ( "maunium.net/go/gomatrix" "maunium.net/go/gomatrix/format" + "maunium.net/go/mautrix-whatsapp/types" "maunium.net/go/mautrix-whatsapp/whatsapp-ext" ) @@ -104,7 +105,7 @@ func NewFormatter(bridge *Bridge) *Formatter { return formatter } -func (formatter *Formatter) getMatrixInfoByJID(jid string) (mxid, displayname string) { +func (formatter *Formatter) getMatrixInfoByJID(jid types.WhatsAppID) (mxid, displayname string) { if user := formatter.bridge.GetUserByJID(jid); user != nil { mxid = user.MXID displayname = user.MXID diff --git a/main.go b/main.go index eda5f89..5594e2c 100644 --- a/main.go +++ b/main.go @@ -165,18 +165,18 @@ func (bridge *Bridge) UpdateBotProfile() { var err error if botConfig.Avatar == "remove" { - err = bridge.AS.BotIntent().SetAvatarURL("") + err = bridge.Bot.SetAvatarURL("") } else if len(botConfig.Avatar) > 0 { - err = bridge.AS.BotIntent().SetAvatarURL(botConfig.Avatar) + err = bridge.Bot.SetAvatarURL(botConfig.Avatar) } if err != nil { bridge.Log.Warnln("Failed to update bot avatar:", err) } if botConfig.Displayname == "remove" { - err = bridge.AS.BotIntent().SetDisplayName("") + err = bridge.Bot.SetDisplayName("") } else if len(botConfig.Avatar) > 0 { - err = bridge.AS.BotIntent().SetDisplayName(botConfig.Displayname) + err = bridge.Bot.SetDisplayName(botConfig.Displayname) } if err != nil { bridge.Log.Warnln("Failed to update bot displayname:", err) diff --git a/portal.go b/portal.go index e597d1c..98457e8 100644 --- a/portal.go +++ b/portal.go @@ -126,7 +126,7 @@ func (portal *Portal) SyncParticipants(metadata *whatsappExt.GroupInfo) { puppet.Intent().EnsureJoined(portal.MXID) user := portal.bridge.GetUserByJID(participant.JID) - if !portal.bridge.AS.StateStore.IsInvited(portal.MXID, user.MXID) { + if user != nil && !portal.bridge.AS.StateStore.IsInvited(portal.MXID, user.MXID) { portal.MainIntent().InviteUser(portal.MXID, &gomatrix.ReqInviteUser{ UserID: user.MXID, }) @@ -144,7 +144,10 @@ func (portal *Portal) SyncParticipants(metadata *whatsappExt.GroupInfo) { } } if changed { - portal.MainIntent().SetPowerLevels(portal.MXID, levels) + _, err = portal.MainIntent().SetPowerLevels(portal.MXID, levels) + if err != nil { + portal.log.Errorln("Failed to change power levels:", err) + } } } @@ -258,10 +261,10 @@ func (portal *Portal) GetBasePowerLevels() *gomatrix.PowerLevels { Users: map[string]int{ portal.MainIntent().UserID: 100, }, - Events: map[gomatrix.EventType]int{ - gomatrix.StateRoomName: anyone, - gomatrix.StateRoomAvatar: anyone, - gomatrix.StateTopic: anyone, + Events: map[string]int{ + gomatrix.StateRoomName.Type: anyone, + gomatrix.StateRoomAvatar.Type: anyone, + gomatrix.StateTopic.Type: anyone, }, } } @@ -286,7 +289,10 @@ func (portal *Portal) ChangeAdminStatus(jids []string, setAdmin bool) { } } if changed { - portal.MainIntent().SetPowerLevels(portal.MXID, levels) + _, err = portal.MainIntent().SetPowerLevels(portal.MXID, levels) + if err != nil { + portal.log.Errorln("Failed to change power levels:", err) + } } } @@ -300,7 +306,10 @@ func (portal *Portal) RestrictMessageSending(restrict bool) { } else { levels.EventsDefault = 0 } - portal.MainIntent().SetPowerLevels(portal.MXID, levels) + _, err = portal.MainIntent().SetPowerLevels(portal.MXID, levels) + if err != nil { + portal.log.Errorln("Failed to change power levels:", err) + } } func (portal *Portal) RestrictMetadataChanges(restrict bool) { @@ -317,7 +326,10 @@ func (portal *Portal) RestrictMetadataChanges(restrict bool) { changed = levels.EnsureEventLevel(gomatrix.StateRoomAvatar, newLevel) || changed changed = levels.EnsureEventLevel(gomatrix.StateTopic, newLevel) || changed if changed { - portal.MainIntent().SetPowerLevels(portal.MXID, levels) + _, err = portal.MainIntent().SetPowerLevels(portal.MXID, levels) + if err != nil { + portal.log.Errorln("Failed to change power levels:", err) + } } } @@ -367,7 +379,7 @@ func (portal *Portal) MainIntent() *appservice.IntentAPI { if portal.IsPrivateChat() { return portal.bridge.GetPuppetByJID(portal.Key.JID).Intent() } - return portal.bridge.AS.BotIntent() + return portal.bridge.Bot } func (portal *Portal) IsDuplicate(id types.WhatsAppMessageID) bool { @@ -386,13 +398,13 @@ func (portal *Portal) MarkHandled(jid types.WhatsAppMessageID, mxid types.Matrix msg.Insert() } -func (portal *Portal) GetMessageIntent(info whatsapp.MessageInfo) *appservice.IntentAPI { +func (portal *Portal) GetMessageIntent(user *User, info whatsapp.MessageInfo) *appservice.IntentAPI { if info.FromMe { if portal.IsPrivateChat() { // TODO handle own messages in private chats properly return nil } - return portal.bridge.GetPuppetByJID(portal.Key.Receiver).Intent() + return portal.bridge.GetPuppetByJID(user.JID).Intent() } else if portal.IsPrivateChat() { return portal.MainIntent() } else if len(info.SenderJid) == 0 { @@ -432,7 +444,7 @@ func (portal *Portal) HandleTextMessage(source *User, message whatsapp.TextMessa return } - intent := portal.GetMessageIntent(message.Info) + intent := portal.GetMessageIntent(source, message.Info) if intent == nil { return } @@ -466,7 +478,7 @@ func (portal *Portal) HandleMediaMessage(source *User, download func() ([]byte, return } - intent := portal.GetMessageIntent(info) + intent := portal.GetMessageIntent(source, info) if intent == nil { return } diff --git a/user.go b/user.go index 2155752..0c44f5a 100644 --- a/user.go +++ b/user.go @@ -37,7 +37,6 @@ type User struct { Admin bool Whitelisted bool - jid string } func (bridge *Bridge) GetUserByMXID(userID types.MatrixUserID) *User { @@ -53,6 +52,9 @@ func (bridge *Bridge) GetUserByMXID(userID types.MatrixUserID) *User { } user = bridge.NewUser(dbUser) bridge.usersByMXID[user.MXID] = user + if len(user.JID) > 0 { + bridge.usersByJID[user.JID] = user + } if len(user.ManagementRoom) > 0 { bridge.managementRooms[user.ManagementRoom] = user } @@ -66,13 +68,12 @@ func (bridge *Bridge) GetUserByJID(userID types.WhatsAppID) *User { defer bridge.usersLock.Unlock() user, ok := bridge.usersByJID[userID] if !ok { - dbUser := bridge.DB.User.GetByMXID(userID) + dbUser := bridge.DB.User.GetByJID(userID) if dbUser == nil { - dbUser = bridge.DB.User.New() - dbUser.MXID = userID - dbUser.Insert() + return nil } user = bridge.NewUser(dbUser) + bridge.usersByMXID[user.MXID] = user bridge.usersByJID[user.JID] = user if len(user.ManagementRoom) > 0 { bridge.managementRooms[user.ManagementRoom] = user @@ -91,6 +92,9 @@ func (bridge *Bridge) GetAllUsers() []*User { if !ok { user = bridge.NewUser(dbUser) bridge.usersByMXID[user.MXID] = user + if len(user.JID) > 0 { + bridge.usersByJID[user.JID] = user + } if len(user.ManagementRoom) > 0 { bridge.managementRooms[user.ManagementRoom] = user } @@ -147,6 +151,7 @@ func (user *User) Connect(evenIfNoSession bool) bool { return false } user.Conn = whatsappExt.ExtendConn(conn) + user.Conn.SetClientName("Mautrix-WhatsApp bridge", "mx-wa") user.log.Debugln("WhatsApp connection successful") user.Conn.AddHandler(user) return user.RestoreSession()