Implement joining groups and checking invite links

This commit is contained in:
Tulir Asokan 2021-10-31 19:59:23 +02:00
parent 1f91eacc10
commit 1e5d5c1a3e
5 changed files with 73 additions and 48 deletions

View file

@ -130,7 +130,7 @@ func (handler *CommandHandler) CommandMux(ce *CommandEvent) {
handler.CommandLogout(ce) handler.CommandLogout(ce)
case "toggle": case "toggle":
handler.CommandToggle(ce) 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() { if !ce.User.HasSession() {
ce.Reply("You are not logged in. Use the `login` command to log into WhatsApp.") ce.Reply("You are not logged in. Use the `login` command to log into WhatsApp.")
return return
@ -154,6 +154,8 @@ func (handler *CommandHandler) CommandMux(ce *CommandEvent) {
handler.CommandPM(ce) handler.CommandPM(ce)
case "invite-link": case "invite-link":
handler.CommandInviteLink(ce) handler.CommandInviteLink(ce)
case "check-invite":
handler.CommandCheckInvite(ce)
case "join": case "join":
handler.CommandJoin(ce) handler.CommandJoin(ce)
case "create": case "create":
@ -223,25 +225,44 @@ func (handler *CommandHandler) CommandVersion(ce *CommandEvent) {
ce.Reply(fmt.Sprintf("[%s](%s) %s (%s)", Name, URL, linkifiedVersion, BuildTime)) 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) { func (handler *CommandHandler) CommandInviteLink(ce *CommandEvent) {
reset := len(ce.Args) > 0 && strings.ToLower(ce.Args[0]) == "--reset"
if ce.Portal == nil { if ce.Portal == nil {
ce.Reply("Not a portal room") ce.Reply("Not a portal room")
} else if ce.Portal.IsPrivateChat() { } else if ce.Portal.IsPrivateChat() {
ce.Reply("Can't get invite link to private chat") ce.Reply("Can't get invite link to private chat")
} else if ce.Portal.IsBroadcastList() { } else if ce.Portal.IsBroadcastList() {
ce.Reply("Can't get invite link to broadcast list") 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) ce.Reply("Failed to get invite link: %v", err)
} else { } else {
ce.Reply(link) 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/" 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) { func (handler *CommandHandler) CommandJoin(ce *CommandEvent) {
if len(ce.Args) == 0 { if len(ce.Args) == 0 {
ce.Reply("**Usage:** `join <invite link>`") ce.Reply("**Usage:** `join <invite link>`")
@ -251,28 +272,13 @@ func (handler *CommandHandler) CommandJoin(ce *CommandEvent) {
return return
} }
ce.Reply("Not yet implemented") jid, err := ce.User.Client.JoinGroupViaLink(ce.Args[0])
// TODO reimplement if err != nil {
//jid, err := ce.User.Conn.GroupAcceptInviteCode(ce.Args[0][len(inviteLinkPrefix):]) ce.Reply("Failed to join group: %v", err)
//if err != nil { return
// 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)
//
//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)
//}
} }
const cmdCreateHelp = `create - Create a group chat.` const cmdCreateHelp = `create - Create a group chat.`
@ -625,6 +631,7 @@ func (handler *CommandHandler) CommandHelp(ce *CommandEvent) {
cmdPrefix + cmdOpenHelp, cmdPrefix + cmdOpenHelp,
cmdPrefix + cmdPMHelp, cmdPrefix + cmdPMHelp,
cmdPrefix + cmdInviteLinkHelp, cmdPrefix + cmdInviteLinkHelp,
cmdPrefix + cmdCheckInviteHelp,
cmdPrefix + cmdJoinHelp, cmdPrefix + cmdJoinHelp,
cmdPrefix + cmdCreateHelp, cmdPrefix + cmdCreateHelp,
cmdPrefix + cmdSetPowerLevelHelp, cmdPrefix + cmdSetPowerLevelHelp,
@ -873,7 +880,7 @@ func (handler *CommandHandler) CommandPM(ce *CommandEvent) {
return return
} }
} }
err = portal.CreateMatrixRoom(user) err = portal.CreateMatrixRoom(user, nil)
if err != nil { if err != nil {
ce.Reply("Failed to create portal room: %v", err) ce.Reply("Failed to create portal room: %v", err)
return return

2
go.mod
View file

@ -8,7 +8,7 @@ require (
github.com/mattn/go-sqlite3 v1.14.9 github.com/mattn/go-sqlite3 v1.14.9
github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_golang v1.11.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e 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 golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
google.golang.org/protobuf v1.27.1 google.golang.org/protobuf v1.27.1
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0

4
go.sum
View file

@ -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= 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 h1:xpQTMgJGGaF+c8jV/LA/FVXAPJxZbSAGeflOc+Ly6uQ=
go.mau.fi/libsignal v0.0.0-20211024113310-f9fc6a1855f2/go.mod h1:3XlVlwOfp8f9Wri+C1D4ORqgUsN4ZvunJOoPjQMBhos= 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-20211031175440-39cd01efeed7 h1:AxqjTj5ejuTUGrpG21Ot/dIjY946OjveZM08SACeDhw=
go.mau.fi/whatsmeow v0.0.0-20211031131127-03a69c3e343b/go.mod h1:ODEmmqeUn9eBDQHFc1S902YA3YFLtmaBujYRRFl53jI= 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-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-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=

View file

@ -201,7 +201,7 @@ func (portal *Portal) handleMessageLoop() {
continue continue
} }
portal.log.Debugln("Creating Matrix room from incoming message") portal.log.Debugln("Creating Matrix room from incoming message")
err := portal.CreateMatrixRoom(msg.source) err := portal.CreateMatrixRoom(msg.source, nil)
if err != nil { if err != nil {
portal.log.Errorln("Failed to create portal room:", err) portal.log.Errorln("Failed to create portal room:", err)
continue continue
@ -533,7 +533,7 @@ func (portal *Portal) SyncParticipants(source *User, metadata *types.GroupInfo)
} }
expectedLevel := 0 expectedLevel := 0
if participant.JID == metadata.OwnerJID { if participant.JID == metadata.OwnerJID || participant.IsSuperAdmin {
expectedLevel = 95 expectedLevel = 95
} else if participant.IsAdmin { } else if participant.IsAdmin {
expectedLevel = 50 expectedLevel = 50
@ -737,11 +737,11 @@ func (portal *Portal) ensureUserInvited(user *User) (ok bool) {
return 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) portal.log.Infoln("Syncing portal for", user.MXID)
if len(portal.MXID) == 0 { if len(portal.MXID) == 0 {
err := portal.CreateMatrixRoom(user) err := portal.CreateMatrixRoom(user, groupInfo)
if err != nil { if err != nil {
portal.log.Errorln("Failed to create portal room:", err) portal.log.Errorln("Failed to create portal room:", err)
return false 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 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} 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() portal.roomCreateLock.Lock()
defer portal.roomCreateLock.Unlock() defer portal.roomCreateLock.Unlock()
if len(portal.MXID) > 0 { 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) portal.log.Infoln("Creating Matrix room. Info source:", user.MXID)
var metadata *types.GroupInfo
//var broadcastMetadata *types.BroadcastListInfo //var broadcastMetadata *types.BroadcastListInfo
if portal.IsPrivateChat() { if portal.IsPrivateChat() {
puppet := portal.bridge.GetPuppetByJID(portal.Key.JID) 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") portal.log.Debugln("Broadcast list is not yet supported, not creating room after all")
return fmt.Errorf("broadcast list bridging is currently not supported") return fmt.Errorf("broadcast list bridging is currently not supported")
} else { } else {
var err error if groupInfo == nil {
metadata, err = user.Client.GetGroupInfo(portal.Key.JID) var err error
if err == nil { groupInfo, err = user.Client.GetGroupInfo(portal.Key.JID)
portal.Name = metadata.Name if err != nil {
portal.Topic = metadata.Topic 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) portal.UpdateAvatar(user, types.EmptyJID, false)
} }
@ -1325,13 +1329,13 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
portal.ensureUserInvited(user) portal.ensureUserInvited(user)
user.syncChatDoublePuppetDetails(portal, true) user.syncChatDoublePuppetDetails(portal, true)
if metadata != nil { if groupInfo != nil {
portal.SyncParticipants(user, metadata) portal.SyncParticipants(user, groupInfo)
if metadata.IsAnnounce { if groupInfo.IsAnnounce {
portal.RestrictMessageSending(metadata.IsAnnounce) portal.RestrictMessageSending(groupInfo.IsAnnounce)
} }
if metadata.IsLocked { if groupInfo.IsLocked {
portal.RestrictMetadataChanges(metadata.IsLocked) portal.RestrictMetadataChanges(groupInfo.IsLocked)
} }
} }
//if broadcastMetadata != nil { //if broadcastMetadata != nil {

16
user.go
View file

@ -376,7 +376,7 @@ func (user *User) handleHistorySync(evt *waProto.HistorySync) {
continue continue
} }
user.log.Debugln("Creating portal for", portal.Key.JID, "as part of history sync handling") 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 { if err != nil {
user.log.Warnfln("Failed to create room for %s during backfill: %v", portal.Key.JID, err) user.log.Warnfln("Failed to create room for %s during backfill: %v", portal.Key.JID, err)
continue continue
@ -444,6 +444,8 @@ func (user *User) HandleEvent(event interface{}) {
go user.syncPuppet(v.JID) go user.syncPuppet(v.JID)
case *events.GroupInfo: case *events.GroupInfo:
go user.handleGroupUpdate(v) go user.handleGroupUpdate(v)
case *events.JoinedGroup:
go user.handleGroupCreate(v)
case *events.Picture: case *events.Picture:
go user.handlePictureUpdate(v) go user.handlePictureUpdate(v)
case *events.Receipt: 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) { func (user *User) handleGroupUpdate(evt *events.GroupInfo) {
portal := user.GetPortalByJID(evt.JID) portal := user.GetPortalByJID(evt.JID)
if portal == nil || len(portal.MXID) == 0 { if portal == nil || len(portal.MXID) == 0 {