Explorar o código

Implement joining groups and checking invite links

Tulir Asokan %!s(int64=3) %!d(string=hai) anos
pai
achega
1e5d5c1a3e
Modificáronse 5 ficheiros con 73 adicións e 48 borrados
  1. 34 27
      commands.go
  2. 1 1
      go.mod
  3. 2 2
      go.sum
  4. 21 17
      portal.go
  5. 15 1
      user.go

+ 34 - 27
commands.go

@@ -130,7 +130,7 @@ func (handler *CommandHandler) CommandMux(ce *CommandEvent) {
 		handler.CommandLogout(ce)
 	case "toggle":
 		handler.CommandToggle(ce)
-	case "set-relay", "unset-relay", "login-matrix", "sync", "list", "open", "pm", "invite-link", "join", "create":
+	case "set-relay", "unset-relay", "login-matrix", "sync", "list", "open", "pm", "invite-link", "check-invite", "join", "create":
 		if !ce.User.HasSession() {
 			ce.Reply("You are not logged in. Use the `login` command to log into WhatsApp.")
 			return
@@ -154,6 +154,8 @@ func (handler *CommandHandler) CommandMux(ce *CommandEvent) {
 			handler.CommandPM(ce)
 		case "invite-link":
 			handler.CommandInviteLink(ce)
+		case "check-invite":
+			handler.CommandCheckInvite(ce)
 		case "join":
 			handler.CommandJoin(ce)
 		case "create":
@@ -223,25 +225,44 @@ func (handler *CommandHandler) CommandVersion(ce *CommandEvent) {
 	ce.Reply(fmt.Sprintf("[%s](%s) %s (%s)", Name, URL, linkifiedVersion, BuildTime))
 }
 
-const cmdInviteLinkHelp = `invite-link - Get an invite link to the current group chat.`
+const cmdInviteLinkHelp = `invite-link [--reset] - Get an invite link to the current group chat, optionally regenerating the link and revoking the old link.`
 
 func (handler *CommandHandler) CommandInviteLink(ce *CommandEvent) {
+	reset := len(ce.Args) > 0 && strings.ToLower(ce.Args[0]) == "--reset"
 	if ce.Portal == nil {
 		ce.Reply("Not a portal room")
 	} else if ce.Portal.IsPrivateChat() {
 		ce.Reply("Can't get invite link to private chat")
 	} else if ce.Portal.IsBroadcastList() {
 		ce.Reply("Can't get invite link to broadcast list")
-	} else if link, err := ce.User.Client.GetGroupInviteLink(ce.Portal.Key.JID); err != nil {
+	} else if link, err := ce.User.Client.GetGroupInviteLink(ce.Portal.Key.JID, reset); err != nil {
 		ce.Reply("Failed to get invite link: %v", err)
 	} else {
 		ce.Reply(link)
 	}
 }
 
-const cmdJoinHelp = `join <invite link> - Join a group chat with an invite link.`
+const cmdCheckInviteHelp = `check-invite <invite link> - Resolve an invite link and check which group it points at.`
 const inviteLinkPrefix = "https://chat.whatsapp.com/"
 
+func (handler *CommandHandler) CommandCheckInvite(ce *CommandEvent) {
+	if len(ce.Args) == 0 {
+		ce.Reply("**Usage:** `join <invite link>`")
+		return
+	} else if len(ce.Args[0]) <= len(inviteLinkPrefix) || ce.Args[0][:len(inviteLinkPrefix)] != inviteLinkPrefix {
+		ce.Reply("That doesn't look like a WhatsApp invite link")
+		return
+	}
+	group, err := ce.User.Client.GetGroupInfoFromLink(ce.Args[0])
+	if err != nil {
+		ce.Reply("Failed to get group info: %v", err)
+		return
+	}
+	ce.Reply("That invite link points at %s (`%s`)", group.Name, group.JID)
+}
+
+const cmdJoinHelp = `join <invite link> - Join a group chat with an invite link.`
+
 func (handler *CommandHandler) CommandJoin(ce *CommandEvent) {
 	if len(ce.Args) == 0 {
 		ce.Reply("**Usage:** `join <invite link>`")
@@ -251,28 +272,13 @@ func (handler *CommandHandler) CommandJoin(ce *CommandEvent) {
 		return
 	}
 
-	ce.Reply("Not yet implemented")
-	// TODO reimplement
-	//jid, err := ce.User.Conn.GroupAcceptInviteCode(ce.Args[0][len(inviteLinkPrefix):])
-	//if err != nil {
-	//	ce.Reply("Failed to join group: %v", err)
-	//	return
-	//}
-	//
-	//handler.log.Debugln("%s successfully joined group %s", ce.User.MXID, jid)
-	//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: [%s](https://matrix.to/#/%s)", portal.Name, portal.Name, portal.MXID)
-	//} else {
-	//	err = portal.CreateMatrixRoom(ce.User)
-	//	if err != nil {
-	//		ce.Reply("Failed to create portal room: %v", err)
-	//		return
-	//	}
-	//
-	//	ce.Reply("Successfully joined group \"%s\" and created portal room: [%s](https://matrix.to/#/%s)", portal.Name, portal.Name, portal.MXID)
-	//}
+	jid, err := ce.User.Client.JoinGroupViaLink(ce.Args[0])
+	if err != nil {
+		ce.Reply("Failed to join group: %v", err)
+		return
+	}
+	handler.log.Debugln("%s successfully joined group %s", ce.User.MXID, jid)
+	ce.Reply("Successfully joined group `%s`, the portal should be created momentarily", jid)
 }
 
 const cmdCreateHelp = `create - Create a group chat.`
@@ -625,6 +631,7 @@ func (handler *CommandHandler) CommandHelp(ce *CommandEvent) {
 		cmdPrefix + cmdOpenHelp,
 		cmdPrefix + cmdPMHelp,
 		cmdPrefix + cmdInviteLinkHelp,
+		cmdPrefix + cmdCheckInviteHelp,
 		cmdPrefix + cmdJoinHelp,
 		cmdPrefix + cmdCreateHelp,
 		cmdPrefix + cmdSetPowerLevelHelp,
@@ -873,7 +880,7 @@ func (handler *CommandHandler) CommandPM(ce *CommandEvent) {
 			return
 		}
 	}
-	err = portal.CreateMatrixRoom(user)
+	err = portal.CreateMatrixRoom(user, nil)
 	if err != nil {
 		ce.Reply("Failed to create portal room: %v", err)
 		return

+ 1 - 1
go.mod

@@ -8,7 +8,7 @@ require (
 	github.com/mattn/go-sqlite3 v1.14.9
 	github.com/prometheus/client_golang v1.11.0
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
-	go.mau.fi/whatsmeow v0.0.0-20211031131127-03a69c3e343b
+	go.mau.fi/whatsmeow v0.0.0-20211031175440-39cd01efeed7
 	golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
 	google.golang.org/protobuf v1.27.1
 	gopkg.in/yaml.v2 v2.4.0

+ 2 - 2
go.sum

@@ -139,8 +139,8 @@ github.com/tidwall/sjson v1.2.3 h1:5+deguEhHSEjmuICXZ21uSSsXotWMA0orU783+Z7Cp8=
 github.com/tidwall/sjson v1.2.3/go.mod h1:5WdjKx3AQMvCJ4RG6/2UYT7dLrGvJUV1x4jdTAyGvZs=
 go.mau.fi/libsignal v0.0.0-20211024113310-f9fc6a1855f2 h1:xpQTMgJGGaF+c8jV/LA/FVXAPJxZbSAGeflOc+Ly6uQ=
 go.mau.fi/libsignal v0.0.0-20211024113310-f9fc6a1855f2/go.mod h1:3XlVlwOfp8f9Wri+C1D4ORqgUsN4ZvunJOoPjQMBhos=
-go.mau.fi/whatsmeow v0.0.0-20211031131127-03a69c3e343b h1:GvVBHYS4iBduhXtsPsnqJtrt8BP1OqYp6OdZqYtt/xY=
-go.mau.fi/whatsmeow v0.0.0-20211031131127-03a69c3e343b/go.mod h1:ODEmmqeUn9eBDQHFc1S902YA3YFLtmaBujYRRFl53jI=
+go.mau.fi/whatsmeow v0.0.0-20211031175440-39cd01efeed7 h1:AxqjTj5ejuTUGrpG21Ot/dIjY946OjveZM08SACeDhw=
+go.mau.fi/whatsmeow v0.0.0-20211031175440-39cd01efeed7/go.mod h1:ODEmmqeUn9eBDQHFc1S902YA3YFLtmaBujYRRFl53jI=
 golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=

+ 21 - 17
portal.go

@@ -201,7 +201,7 @@ func (portal *Portal) handleMessageLoop() {
 				continue
 			}
 			portal.log.Debugln("Creating Matrix room from incoming message")
-			err := portal.CreateMatrixRoom(msg.source)
+			err := portal.CreateMatrixRoom(msg.source, nil)
 			if err != nil {
 				portal.log.Errorln("Failed to create portal room:", err)
 				continue
@@ -533,7 +533,7 @@ func (portal *Portal) SyncParticipants(source *User, metadata *types.GroupInfo)
 		}
 
 		expectedLevel := 0
-		if participant.JID == metadata.OwnerJID {
+		if participant.JID == metadata.OwnerJID || participant.IsSuperAdmin {
 			expectedLevel = 95
 		} else if participant.IsAdmin {
 			expectedLevel = 50
@@ -737,11 +737,11 @@ func (portal *Portal) ensureUserInvited(user *User) (ok bool) {
 	return
 }
 
-func (portal *Portal) Sync(user *User) bool {
+func (portal *Portal) Sync(user *User, groupInfo *types.GroupInfo) bool {
 	portal.log.Infoln("Syncing portal for", user.MXID)
 
 	if len(portal.MXID) == 0 {
-		err := portal.CreateMatrixRoom(user)
+		err := portal.CreateMatrixRoom(user, groupInfo)
 		if err != nil {
 			portal.log.Errorln("Failed to create portal room:", err)
 			return false
@@ -1197,7 +1197,7 @@ var BackfillDummyStateEvent = event.Type{Type: "fi.mau.dummy.blank_backfill_stat
 var BackfillEndDummyEvent = event.Type{Type: "fi.mau.dummy.backfill_end", Class: event.MessageEventType}
 var ForwardBackfillDummyEvent = event.Type{Type: "fi.mau.dummy.pre_forward_backfill", Class: event.MessageEventType}
 
-func (portal *Portal) CreateMatrixRoom(user *User) error {
+func (portal *Portal) CreateMatrixRoom(user *User, groupInfo *types.GroupInfo) error {
 	portal.roomCreateLock.Lock()
 	defer portal.roomCreateLock.Unlock()
 	if len(portal.MXID) > 0 {
@@ -1211,7 +1211,6 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
 
 	portal.log.Infoln("Creating Matrix room. Info source:", user.MXID)
 
-	var metadata *types.GroupInfo
 	//var broadcastMetadata *types.BroadcastListInfo
 	if portal.IsPrivateChat() {
 		puppet := portal.bridge.GetPuppetByJID(portal.Key.JID)
@@ -1249,11 +1248,16 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
 		portal.log.Debugln("Broadcast list is not yet supported, not creating room after all")
 		return fmt.Errorf("broadcast list bridging is currently not supported")
 	} else {
-		var err error
-		metadata, err = user.Client.GetGroupInfo(portal.Key.JID)
-		if err == nil {
-			portal.Name = metadata.Name
-			portal.Topic = metadata.Topic
+		if groupInfo == nil {
+			var err error
+			groupInfo, err = user.Client.GetGroupInfo(portal.Key.JID)
+			if err != nil {
+				portal.log.Warnfln("Failed to get group info through %s: %v", user.JID, err)
+			}
+		}
+		if groupInfo != nil {
+			portal.Name = groupInfo.Name
+			portal.Topic = groupInfo.Topic
 		}
 		portal.UpdateAvatar(user, types.EmptyJID, false)
 	}
@@ -1325,13 +1329,13 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
 	portal.ensureUserInvited(user)
 	user.syncChatDoublePuppetDetails(portal, true)
 
-	if metadata != nil {
-		portal.SyncParticipants(user, metadata)
-		if metadata.IsAnnounce {
-			portal.RestrictMessageSending(metadata.IsAnnounce)
+	if groupInfo != nil {
+		portal.SyncParticipants(user, groupInfo)
+		if groupInfo.IsAnnounce {
+			portal.RestrictMessageSending(groupInfo.IsAnnounce)
 		}
-		if metadata.IsLocked {
-			portal.RestrictMetadataChanges(metadata.IsLocked)
+		if groupInfo.IsLocked {
+			portal.RestrictMetadataChanges(groupInfo.IsLocked)
 		}
 	}
 	//if broadcastMetadata != nil {

+ 15 - 1
user.go

@@ -376,7 +376,7 @@ func (user *User) handleHistorySync(evt *waProto.HistorySync) {
 				continue
 			}
 			user.log.Debugln("Creating portal for", portal.Key.JID, "as part of history sync handling")
-			err = portal.CreateMatrixRoom(user)
+			err = portal.CreateMatrixRoom(user, nil)
 			if err != nil {
 				user.log.Warnfln("Failed to create room for %s during backfill: %v", portal.Key.JID, err)
 				continue
@@ -444,6 +444,8 @@ func (user *User) HandleEvent(event interface{}) {
 		go user.syncPuppet(v.JID)
 	case *events.GroupInfo:
 		go user.handleGroupUpdate(v)
+	case *events.JoinedGroup:
+		go user.handleGroupCreate(v)
 	case *events.Picture:
 		go user.handlePictureUpdate(v)
 	case *events.Receipt:
@@ -754,6 +756,18 @@ func (user *User) markSelfReadFull(portal *Portal) {
 	}
 }
 
+func (user *User) handleGroupCreate(evt *events.JoinedGroup) {
+	portal := user.GetPortalByJID(evt.JID)
+	if len(portal.MXID) == 0 {
+		err := portal.CreateMatrixRoom(user, &evt.GroupInfo)
+		if err != nil {
+			user.log.Errorln("Failed to create Matrix room after join notification: %v", err)
+		}
+	} else {
+		portal.Sync(user, &evt.GroupInfo)
+	}
+}
+
 func (user *User) handleGroupUpdate(evt *events.GroupInfo) {
 	portal := user.GetPortalByJID(evt.JID)
 	if portal == nil || len(portal.MXID) == 0 {