Handle WhatsApp kicks and Matrix invites

This commit is contained in:
Tulir Asokan 2020-06-25 23:58:35 +03:00
parent fed6756ae5
commit 326293303d
5 changed files with 91 additions and 12 deletions

View file

@ -235,7 +235,7 @@ func (handler *CommandHandler) CommandJoin(ce *CommandEvent) {
portal := handler.bridge.GetPortalByJID(database.GroupPortalKey(jid))
if len(portal.MXID) > 0 {
portal.Sync(ce.User, whatsapp.Contact{Jid: portal.Key.JID})
ce.Reply("Successfully joined group \"%s\" and synced portal room", portal.Name)
ce.Reply("Successfully joined group \"%s\" and synced portal room: [%s](https://matrix.to/#/%s)", portal.Name, portal.Name, portal.MXID)
} else {
err = portal.CreateMatrixRoom(ce.User)
if err != nil {
@ -243,7 +243,7 @@ func (handler *CommandHandler) CommandJoin(ce *CommandEvent) {
return
}
ce.Reply("Successfully joined group \"%s\" and created portal room", portal.Name)
ce.Reply("Successfully joined group \"%s\" and created portal room: [%s](https://matrix.to/#/%s)", portal.Name, portal.Name, portal.MXID)
}
}

View file

@ -155,8 +155,10 @@ func (mx *MatrixHandler) HandleMembership(evt *event.Event) {
return
}
isSelf := id.UserID(evt.GetStateKey()) == evt.Sender
if content.Membership == event.MembershipLeave {
if id.UserID(evt.GetStateKey()) == evt.Sender {
if isSelf {
if evt.Unsigned.PrevContent != nil {
_ = evt.Unsigned.PrevContent.ParseRaw(evt.Type)
prevContent, ok := evt.Unsigned.PrevContent.Parsed.(*event.MemberEventContent)
@ -169,6 +171,8 @@ func (mx *MatrixHandler) HandleMembership(evt *event.Event) {
} else {
portal.HandleMatrixKick(user, evt)
}
} else if content.Membership == event.MembershipInvite && !isSelf {
portal.HandleMatrixInvite(user, evt)
}
}

View file

@ -1281,6 +1281,53 @@ func (portal *Portal) encryptFile(data []byte, mimeType string) ([]byte, string,
return file.Encrypt(data), "application/octet-stream", file
}
func (portal *Portal) tryKickUser(userID id.UserID, intent *appservice.IntentAPI) error {
_, err := intent.KickUser(portal.MXID, &mautrix.ReqKickUser{UserID: userID})
if err != nil {
httpErr, ok := err.(mautrix.HTTPError)
if ok && httpErr.RespError != nil && httpErr.RespError.ErrCode == "M_FORBIDDEN" {
_, err = portal.MainIntent().KickUser(portal.MXID, &mautrix.ReqKickUser{UserID: userID})
}
}
return err
}
func (portal *Portal) removeUser(isSameUser bool, kicker *appservice.IntentAPI, target id.UserID, targetIntent *appservice.IntentAPI) {
if !isSameUser || targetIntent == nil {
err := portal.tryKickUser(target, kicker)
if err != nil {
portal.log.Warnfln("Failed to kick %s from %s: %v", target, portal.MXID, err)
if targetIntent != nil {
_, _ = targetIntent.LeaveRoom(portal.MXID)
}
}
} else {
_, err := targetIntent.LeaveRoom(portal.MXID)
if err != nil {
portal.log.Warnfln("Failed to leave portal as %s: %v", target, err)
_, _ = portal.MainIntent().KickUser(portal.MXID, &mautrix.ReqKickUser{UserID: target})
}
}
}
func (portal *Portal) HandleWhatsAppKick(senderJID string, jids []string) {
sender := portal.bridge.GetPuppetByJID(senderJID)
senderIntent := sender.IntentFor(portal)
for _, jid := range jids {
puppet := portal.bridge.GetPuppetByJID(jid)
portal.removeUser(puppet.JID == sender.JID, senderIntent, puppet.MXID, puppet.DefaultIntent())
user := portal.bridge.GetUserByJID(jid)
if user != nil {
var customIntent *appservice.IntentAPI
if puppet.CustomMXID == user.MXID {
customIntent = puppet.CustomIntent()
}
portal.removeUser(puppet.JID == sender.JID, senderIntent, user.MXID, customIntent)
}
}
}
type base struct {
download func() ([]byte, error)
info whatsapp.MessageInfo
@ -2005,6 +2052,7 @@ func (portal *Portal) HandleMatrixLeave(sender *User) {
portal.Cleanup(false)
return
} else {
// TODO should we somehow deduplicate this call if this leave was sent by the bridge?
resp, err := sender.Conn.LeaveGroup(portal.Key.JID)
if err != nil {
portal.log.Errorfln("Failed to leave group as %s: %v", sender.MXID, err)
@ -2015,8 +2063,8 @@ func (portal *Portal) HandleMatrixLeave(sender *User) {
}
}
func (portal *Portal) HandleMatrixKick(sender *User, event *event.Event) {
puppet := portal.bridge.GetPuppetByMXID(id.UserID(event.GetStateKey()))
func (portal *Portal) HandleMatrixKick(sender *User, evt *event.Event) {
puppet := portal.bridge.GetPuppetByMXID(id.UserID(evt.GetStateKey()))
if puppet != nil {
resp, err := sender.Conn.RemoveMember(portal.Key.JID, []string{puppet.JID})
if err != nil {
@ -2026,3 +2074,15 @@ func (portal *Portal) HandleMatrixKick(sender *User, event *event.Event) {
portal.log.Infoln("Kick %s response: %s", puppet.JID, <-resp)
}
}
func (portal *Portal) HandleMatrixInvite(sender *User, evt *event.Event) {
puppet := portal.bridge.GetPuppetByMXID(id.UserID(evt.GetStateKey()))
if puppet != nil {
resp, err := sender.Conn.AddMember(portal.Key.JID, []string{puppet.JID})
if err != nil {
portal.log.Errorfln("Failed to add %s to group as %s: %v", puppet.JID, sender.MXID, err)
return
}
portal.log.Infoln("Add %s response: %s", puppet.JID, <-resp)
}
}

18
user.go
View file

@ -861,6 +861,14 @@ func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) {
portal := user.GetPortalByJID(cmd.JID)
if len(portal.MXID) == 0 {
if cmd.Data.Action == whatsappExt.ChatActionIntroduce && cmd.Data.SenderJID != "unknown" {
go func() {
err := portal.CreateMatrixRoom(user)
if err != nil {
user.log.Errorln("Failed to create portal room after receiving join event:", err)
}
}()
}
return
}
@ -872,13 +880,19 @@ func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) {
case whatsappExt.ChatActionRemoveTopic:
go portal.UpdateTopic("", cmd.Data.SenderJID, true)
case whatsappExt.ChatActionPromote:
go portal.ChangeAdminStatus(cmd.Data.PermissionChange.JIDs, true)
go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, true)
case whatsappExt.ChatActionDemote:
go portal.ChangeAdminStatus(cmd.Data.PermissionChange.JIDs, false)
go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, false)
case whatsappExt.ChatActionAnnounce:
go portal.RestrictMessageSending(cmd.Data.Announce)
case whatsappExt.ChatActionRestrict:
go portal.RestrictMetadataChanges(cmd.Data.Restrict)
case whatsappExt.ChatActionRemove:
go portal.HandleWhatsAppKick(cmd.Data.SenderJID, cmd.Data.UserChange.JIDs)
case whatsappExt.ChatActionIntroduce:
if cmd.Data.SenderJID != "unknown" {
go portal.Sync(user, whatsapp.Contact{Jid: portal.Key.JID})
}
}
}

View file

@ -46,6 +46,7 @@ const (
ChatActionPromote ChatActionType = "promote"
ChatActionDemote ChatActionType = "demote"
ChatActionIntroduce ChatActionType = "introduce"
ChatActionRemove ChatActionType = "remove"
)
type ChatUpdateData struct {
@ -80,7 +81,7 @@ type ChatUpdateData struct {
Announce bool
PermissionChange struct {
UserChange struct {
JIDs []string `json:"participants"`
}
}
@ -127,8 +128,8 @@ func (cud *ChatUpdateData) UnmarshalJSON(data []byte) error {
unmarshalTo = &cud.Restrict
case ChatActionAnnounce:
unmarshalTo = &cud.Announce
case ChatActionPromote, ChatActionDemote:
unmarshalTo = &cud.PermissionChange
case ChatActionPromote, ChatActionDemote, ChatActionRemove:
unmarshalTo = &cud.UserChange
default:
return nil
}
@ -137,8 +138,8 @@ func (cud *ChatUpdateData) UnmarshalJSON(data []byte) error {
return err
}
cud.NameChange.SetBy = strings.Replace(cud.NameChange.SetBy, OldUserSuffix, NewUserSuffix, 1)
for index, jid := range cud.PermissionChange.JIDs {
cud.PermissionChange.JIDs[index] = strings.Replace(jid, OldUserSuffix, NewUserSuffix, 1)
for index, jid := range cud.UserChange.JIDs {
cud.UserChange.JIDs[index] = strings.Replace(jid, OldUserSuffix, NewUserSuffix, 1)
}
for index, jid := range cud.Introduce.SuperAdmins {
cud.Introduce.SuperAdmins[index] = strings.Replace(jid, OldUserSuffix, NewUserSuffix, 1)