kick/invite (in portal room)

This commit is contained in:
zhaoYangguang 2020-09-21 22:48:06 +08:00
parent 1251ac2204
commit ab5d852629
5 changed files with 249 additions and 26 deletions

View File

@ -900,7 +900,7 @@ func (handler *CommandHandler) CommandInvite(ce *CommandEvent) {
userNumbers := strings.Split(ce.Args[1], ",")
if strings.HasSuffix(conversationId, whatsappExt.NewUserSuffix) {
if strings.HasSuffix(conversationId, skypeExt.NewUserSuffix) {
ce.Reply("**Usage:** `invite <group JID> <international phone number>,...`")
return
}

135
matrix.go
View File

@ -3,6 +3,8 @@ package main
import (
"fmt"
skype "github.com/kelaresg/go-skypeapi"
"github.com/kelaresg/matrix-skype/database"
"maunium.net/go/mautrix"
"strconv"
"strings"
@ -52,6 +54,28 @@ func (mx *MatrixHandler) HandleEncryption(evt *event.Event) {
}
}
func (mx *MatrixHandler) joinAndCheckMembers(evt *event.Event, intent *appservice.IntentAPI) *mautrix.RespJoinedMembers {
resp, err := intent.JoinRoomByID(evt.RoomID)
if err != nil {
mx.log.Debugfln("Failed to join room %s as %s with invite from %s: %v", evt.RoomID, intent.UserID, evt.Sender, err)
return nil
}
members, err := intent.JoinedMembers(resp.RoomID)
if err != nil {
mx.log.Debugfln("Failed to get members in room %s after accepting invite from %s as %s: %v", resp.RoomID, evt.Sender, intent.UserID, err)
_, _ = intent.LeaveRoom(resp.RoomID)
return nil
}
if len(members.Joined) < 2 {
mx.log.Debugln("Leaving empty room", resp.RoomID, "after accepting invite from", evt.Sender, "as", intent.UserID)
_, _ = intent.LeaveRoom(resp.RoomID)
return nil
}
return members
}
func (mx *MatrixHandler) HandleBotInvite(evt *event.Event) {
intent := mx.as.BotIntent()
@ -114,6 +138,102 @@ func (mx *MatrixHandler) HandleBotInvite(evt *event.Event) {
}
}
func (mx *MatrixHandler) handlePrivatePortal(roomID id.RoomID, inviter *User, puppet *Puppet, key database.PortalKey) {
portal := mx.bridge.GetPortalByJID(key)
if len(portal.MXID) == 0 {
mx.createPrivatePortalFromInvite(roomID, inviter, puppet, portal)
return
}
err := portal.MainIntent().EnsureInvited(portal.MXID, inviter.MXID)
if err != nil {
mx.log.Warnfln("Failed to invite %s to existing private chat portal %s with %s: %v. Redirecting portal to new room...", inviter.MXID, portal.MXID, puppet.JID, err)
mx.createPrivatePortalFromInvite(roomID, inviter, puppet, portal)
return
}
intent := puppet.DefaultIntent()
_, _ = intent.SendNotice(roomID, "You already have a private chat portal with me at %s")
mx.log.Debugln("Leaving private chat room", roomID, "as", puppet.MXID, "after accepting invite from", inviter.MXID, "as we already have chat with the user")
_, _ = intent.LeaveRoom(roomID)
}
func (mx *MatrixHandler) createPrivatePortalFromInvite(roomID id.RoomID, inviter *User, puppet *Puppet, portal *Portal) {
portal.MXID = roomID
portal.Topic = "WhatsApp private chat"
_, _ = portal.MainIntent().SetRoomTopic(portal.MXID, portal.Topic)
if portal.bridge.Config.Bridge.PrivateChatPortalMeta {
portal.Name = puppet.Displayname
portal.AvatarURL = puppet.AvatarURL
portal.Avatar = puppet.Avatar
_, _ = portal.MainIntent().SetRoomName(portal.MXID, portal.Name)
_, _ = portal.MainIntent().SetRoomAvatar(portal.MXID, portal.AvatarURL)
} else {
portal.Name = ""
}
portal.log.Infoln("Created private chat portal in %s after invite from", roomID, inviter.MXID)
intent := puppet.DefaultIntent()
if mx.bridge.Config.Bridge.Encryption.Default {
_, err := intent.InviteUser(roomID, &mautrix.ReqInviteUser{UserID: mx.bridge.Bot.UserID})
if err != nil {
portal.log.Warnln("Failed to invite bridge bot to enable e2be:", err)
}
err = mx.bridge.Bot.EnsureJoined(roomID)
if err != nil {
portal.log.Warnln("Failed to join as bridge bot to enable e2be:", err)
}
_, err = intent.SendStateEvent(roomID, event.StateEncryption, "", &event.EncryptionEventContent{Algorithm: id.AlgorithmMegolmV1})
if err != nil {
portal.log.Warnln("Failed to enable e2be:", err)
}
mx.as.StateStore.SetMembership(roomID, inviter.MXID, event.MembershipJoin)
mx.as.StateStore.SetMembership(roomID, puppet.MXID, event.MembershipJoin)
mx.as.StateStore.SetMembership(roomID, mx.bridge.Bot.UserID, event.MembershipJoin)
portal.Encrypted = true
}
portal.Update()
portal.UpdateBridgeInfo()
_, _ = intent.SendNotice(roomID, "Private chat portal created")
err := portal.FillInitialHistory(inviter)
if err != nil {
portal.log.Errorln("Failed to fill history:", err)
}
inviter.addPortalToCommunity(portal)
inviter.addPuppetToCommunity(puppet)
}
func (mx *MatrixHandler) HandlePuppetInvite(evt *event.Event, inviter *User, puppet *Puppet) {
intent := puppet.DefaultIntent()
members := mx.joinAndCheckMembers(evt, intent)
if members == nil {
return
}
var hasBridgeBot, hasOtherUsers bool
for mxid, _ := range members.Joined {
if mxid == intent.UserID || mxid == inviter.MXID {
continue
} else if mxid == mx.bridge.Bot.UserID {
hasBridgeBot = true
} else {
hasOtherUsers = true
}
}
if !hasBridgeBot && !hasOtherUsers {
key := database.NewPortalKey(puppet.JID, inviter.JID)
mx.handlePrivatePortal(evt.RoomID, inviter, puppet, key)
} else if !hasBridgeBot {
mx.log.Debugln("Leaving multi-user room", evt.RoomID, "as", puppet.MXID, "after accepting invite from", evt.Sender)
_, _ = intent.SendNotice(evt.RoomID, "Please invite the bridge bot first if you want to bridge to a WhatsApp group.")
_, _ = intent.LeaveRoom(evt.RoomID)
} else {
_, _ = intent.SendNotice(evt.RoomID, "This puppet will remain inactive until this room is bridged to a WhatsApp group.")
}
}
func (mx *MatrixHandler) HandleMembership(evt *event.Event) {
if _, isPuppet := mx.bridge.ParsePuppetMXID(evt.Sender); evt.Sender == mx.bridge.Bot.UserID || isPuppet {
return
@ -126,10 +246,6 @@ func (mx *MatrixHandler) HandleMembership(evt *event.Event) {
content := evt.Content.AsMember()
if content.Membership == event.MembershipInvite && id.UserID(evt.GetStateKey()) == mx.as.BotMXID() {
mx.HandleBotInvite(evt)
}
portal := mx.bridge.GetPortalByMXID(evt.RoomID)
if portal == nil {
return
}
@ -138,6 +254,15 @@ func (mx *MatrixHandler) HandleMembership(evt *event.Event) {
return
}
portal := mx.bridge.GetPortalByMXID(evt.RoomID)
if portal == nil {
puppet := mx.bridge.GetPuppetByMXID(id.UserID(evt.GetStateKey()))
if content.Membership == event.MembershipInvite && puppet != nil {
mx.HandlePuppetInvite(evt, user, puppet)
}
return
}
isSelf := id.UserID(evt.GetStateKey()) == evt.Sender
if content.Membership == event.MembershipLeave {
if id.UserID(evt.GetStateKey()) == evt.Sender {
if evt.Unsigned.PrevContent != nil {
@ -152,6 +277,8 @@ func (mx *MatrixHandler) HandleMembership(evt *event.Event) {
} else {
portal.HandleMatrixKick(user, evt)
}
} else if content.Membership == event.MembershipInvite && !isSelf {
portal.HandleMatrixInvite(user, evt)
}
}

119
portal.go
View File

@ -1015,6 +1015,43 @@ var (
StateHalfShotBridgeInfo = event.Type{Type: "uk.half-shot.bridge", Class: event.StateEventType}
)
func (portal *Portal) getBridgeInfo() (string, BridgeInfoContent) {
bridgeInfo := BridgeInfoContent{
BridgeBot: portal.bridge.Bot.UserID,
Creator: portal.MainIntent().UserID,
Protocol: BridgeInfoSection{
ID: "skype",
DisplayName: "Skype",
AvatarURL: id.ContentURIString(portal.bridge.Config.AppService.Bot.Avatar),
ExternalURL: "https://www.skype.com/",
},
Channel: BridgeInfoSection{
ID: portal.Key.JID,
DisplayName: portal.Name,
AvatarURL: portal.AvatarURL.CUString(),
},
}
bridgeInfoStateKey := fmt.Sprintf("net.maunium.whatsapp://whatsapp/%s", portal.Key.JID)
return bridgeInfoStateKey, bridgeInfo
}
func (portal *Portal) UpdateBridgeInfo() {
if len(portal.MXID) == 0 {
portal.log.Debugln("Not updating bridge info: no Matrix room created")
return
}
portal.log.Debugln("Updating bridge info...")
stateKey, content := portal.getBridgeInfo()
_, err := portal.MainIntent().SendStateEvent(portal.MXID, StateBridgeInfo, stateKey, content)
if err != nil {
portal.log.Warnln("Failed to update m.bridge:", err)
}
_, err = portal.MainIntent().SendStateEvent(portal.MXID, StateHalfShotBridgeInfo, stateKey, content)
if err != nil {
portal.log.Warnln("Failed to update uk.half-shot.bridge:", err)
}
}
func (portal *Portal) CreateMatrixRoom(user *User) error {
portal.roomCreateLock.Lock()
defer portal.roomCreateLock.Unlock()
@ -1024,9 +1061,6 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
intent := portal.MainIntent()
if err := intent.EnsureRegistered(); err != nil {
fmt.Println()
fmt.Println("CreateMatrixRoom0: ", err)
fmt.Println()
return err
}
@ -1035,9 +1069,6 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
var metadata *skypeExt.GroupInfo
if portal.IsPrivateChat() {
fmt.Println()
fmt.Println("CreateMatrixRoom1: ")
fmt.Println()
puppet := portal.bridge.GetPuppetByJID(portal.Key.JID+skypeExt.NewUserSuffix)
if portal.bridge.Config.Bridge.PrivateChatPortalMeta {
portal.Name = puppet.Displayname
@ -1048,15 +1079,9 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
}
portal.Topic = "skype private chat"
} else if portal.IsStatusBroadcastRoom() {
fmt.Println()
fmt.Println("CreateMatrixRoom2: ")
fmt.Println()
portal.Name = "skype Status Broadcast"
portal.Topic = "skype status updates from your contacts"
} else {
fmt.Println()
fmt.Println("CreateMatrixRoom3: ")
fmt.Println()
var err error
metadata, err = user.Conn.GetGroupMetaData(portal.Key.JID)
if err == nil {
@ -1068,7 +1093,6 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
if key == user.JID {
continue
}
fmt.Println("CreateMatrixRoom3.1: ", key)
if contact, ok := user.Conn.Store.Contacts[key]; ok {
if len(portalName) > 0 {
portalName = portalName + ", " + contact.DisplayName
@ -2355,6 +2379,35 @@ func (portal *Portal) Delete() {
portal.bridge.portalsLock.Unlock()
}
func (portal *Portal) GetMatrixUsers() ([]id.UserID, error) {
members, err := portal.MainIntent().JoinedMembers(portal.MXID)
if err != nil {
return nil, errors.Wrap(err, "failed to get member list")
}
var users []id.UserID
for userID := range members.Joined {
_, isPuppet := portal.bridge.ParsePuppetMXID(userID)
if !isPuppet && userID != portal.bridge.Bot.UserID {
users = append(users, userID)
}
}
return users, nil
}
func (portal *Portal) CleanupIfEmpty() {
users, err := portal.GetMatrixUsers()
if err != nil {
portal.log.Errorfln("Failed to get Matrix user list to determine if portal needs to be cleaned up: %v", err)
return
}
if len(users) == 0 {
portal.log.Infoln("Room seems to be empty, cleaning up...")
portal.Delete()
portal.Cleanup(false)
}
}
func (portal *Portal) Cleanup(puppetsOnly bool) {
if len(portal.MXID) == 0 {
return
@ -2401,9 +2454,45 @@ func (portal *Portal) HandleMatrixLeave(sender *User) {
portal.Delete()
portal.Cleanup(false)
return
} else {
// TODO should we somehow deduplicate this call if this leave was sent by the bridge?
err := sender.Conn.HandleGroupLeave(portal.Key.JID)
if err != nil {
portal.log.Errorfln("Failed to leave group as %s: %v", sender.MXID, err)
return
}
//portal.log.Infoln("Leave response:", <-resp)
portal.CleanupIfEmpty()
}
}
func (portal *Portal) HandleMatrixKick(sender *User, event *event.Event) {
// TODO
func (portal *Portal) HandleMatrixKick(sender *User, evt *event.Event) {
number, _:= portal.bridge.ParsePuppetMXID(id.UserID(evt.GetStateKey()))
puppet := portal.bridge.GetPuppetByMXID(id.UserID(evt.GetStateKey()))
fmt.Println("HandleMatrixKick", puppet)
if puppet != nil {
number = strings.Replace(number, skypeExt.NewUserSuffix, "", 1)
err := sender.Conn.HandleGroupKick(portal.Key.JID, []string{number})
if err != nil {
portal.log.Errorfln("Failed to kick %s from group as %s: %v", puppet.JID, sender.MXID, err)
return
}
//portal.log.Infoln("Kick %s response: %s", puppet.JID, <-resp)
}
}
func (portal *Portal) HandleMatrixInvite(sender *User, evt *event.Event) {
number, _:= portal.bridge.ParsePuppetMXID(id.UserID(evt.GetStateKey()))
puppet := portal.bridge.GetPuppetByMXID(id.UserID(evt.GetStateKey()))
fmt.Println("HandleMatrixInvite", puppet)
if puppet != nil {
number = strings.Replace(number, "8:", "", 1)
number = strings.Replace(number, skypeExt.NewUserSuffix, "", 1)
err := sender.Conn.HandleGroupInvite(portal.Key.JID, []string{number})
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)
}
}

View File

@ -19,7 +19,6 @@ import (
)
func (bridge *Bridge) ParsePuppetMXID(mxid id.UserID) (types.SkypeID, bool) {
fmt.Println("ParsePuppetMXID: ", bridge.Config.Bridge.FormatUsername("(.*)"))
userIDRegex, err := regexp.Compile(fmt.Sprintf("^@%s:%s$",
bridge.Config.Bridge.FormatUsername("(.*)"),
bridge.Config.Homeserver.Domain))
@ -31,8 +30,15 @@ func (bridge *Bridge) ParsePuppetMXID(mxid id.UserID) (types.SkypeID, bool) {
if match == nil || len(match) != 2 {
return "", false
}
jid := types.SkypeID(match[1] + whatsappExt.NewUserSuffix)
realId := match[1]
cond1 := "8-live-"
cond2 := "8-"
if strings.HasPrefix(realId, cond1) {
realId = strings.Replace(realId, cond1, "8:live:", 1)
} else if strings.HasPrefix(realId, cond2){
realId = strings.Replace(realId, cond2, "8:", 1)
}
jid := types.SkypeID(realId + skypeExt.NewUserSuffix)
return jid, true
}

View File

@ -275,9 +275,10 @@ func (ext *ExtendedConn) HandleGroupShare(groupJid string) (err error, link stri
func (ext *ExtendedConn) HandleGroupKick(groupJid string, numbers[]string) (err error) {
for _, number := range numbers{
if err == nil {
err = ext.Conn.RemoveMember(groupJid, number)
if err != nil {
fmt.Printf("%s Handle kick err", err)
} else {
_ = ext.Conn.RemoveMember(groupJid, number)
}
}
return