From 1cb97101882eab07c2b6ba9d714e639ff4671875 Mon Sep 17 00:00:00 2001 From: watsonb8 Date: Thu, 14 Sep 2023 22:35:20 -0500 Subject: [PATCH] Successfully adding portals to groupme space --- groupmeext/subscription.go | 2 +- portal.go | 152 ++++--------------------- user.go | 224 +++++++++++++++++++++++-------------- 3 files changed, 163 insertions(+), 215 deletions(-) diff --git a/groupmeext/subscription.go b/groupmeext/subscription.go index 2b4ad61..4bcf5df 100644 --- a/groupmeext/subscription.go +++ b/groupmeext/subscription.go @@ -53,7 +53,7 @@ func NewFayeClient(logger log.Logger) *FayeClient { fc := &FayeClient{wray.NewFayeClient(groupme.PushServer)} fc.SetLogger(fayeLogger{logger.Sub("FayeClient")}) fc.AddExtension(&AuthExt{}) - fc.AddExtension(fc.FayeClient) + //fc.AddExtension(fc.FayeClient) return fc } diff --git a/portal.go b/portal.go index fa89036..bd4e10e 100644 --- a/portal.go +++ b/portal.go @@ -149,8 +149,6 @@ func (portal *Portal) SyncDM(user *User, dm *groupme.Chat) { } if len(portal.MXID) == 0 { - - // "" for overall user not related to one group puppet := portal.bridge.GetPuppetByGMID(portal.Key.GMID) meta, err := portal.bridge.StateStore.TryGetMember("", puppet.MXID) if err { @@ -190,10 +188,6 @@ func (portal *Portal) SyncDM(user *User, dm *groupme.Chat) { portal.ensureUserInvited(user) } - //if portal.IsPrivateChat() { - // return - //} - update := false update = portal.updateMetadata(user) || update @@ -710,9 +704,6 @@ func (portal *Portal) handleAttachment(intent *appservice.IntentAPI, attachment Info: &event.FileInfo{ Size: len(data), MimeType: mime, - //Width: width, - //Height: height, - //Duration: int(msg.length), }, } if content.File != nil { @@ -749,9 +740,6 @@ func (portal *Portal) handleAttachment(intent *appservice.IntentAPI, attachment Info: &event.FileInfo{ Size: len(data), MimeType: fmime, - //Width: width, - //Height: height, - //Duration: int(msg.length), }, } if content.File != nil { @@ -924,6 +912,26 @@ func (portal *Portal) createMatrixRoom(user *User) error { Content: event.Content{Parsed: bridgeInfo}, StateKey: &bridgeInfoStateKey, }} + + spaceIDStr := user.SpaceId + initialState = append(initialState, &event.Event{ + Type: event.StateSpaceParent, + StateKey: &spaceIDStr, + Content: event.Content{Parsed: &event.SpaceParentEventContent{ + Via: []string{portal.bridge.AS.HomeserverDomain}, + Canonical: true, + }}, + }) + initialState = append(initialState, &event.Event{ + Type: event.StateJoinRules, + Content: event.Content{Parsed: &event.JoinRulesEventContent{ + JoinRule: event.JoinRuleRestricted, + Allow: []event.JoinRuleAllow{{ + RoomID: id.RoomID(user.SpaceId), + Type: event.JoinRuleAllowRoomMembership, + }}, + }}, + }) if !portal.AvatarURL.IsEmpty() { initialState = append(initialState, &event.Event{ Type: event.StateRoomAvatar, @@ -977,6 +985,8 @@ func (portal *Portal) createMatrixRoom(user *User) error { portal.bridge.StateStore.SetMembership(portal.MXID, user, event.MembershipInvite) } + user.addPortalToSpace(portal) + return nil } @@ -1069,12 +1079,6 @@ func (portal *Portal) ensureMXIDInvited(mxid id.UserID) { } func (portal *Portal) ensureUserInvited(user *User) bool { - portal.userMXIDAction(user, portal.ensureMXIDInvited) - - customPuppet := portal.bridge.GetPuppetByCustomMXID(user.MXID) - if customPuppet != nil && customPuppet.CustomIntent() != nil { - _ = customPuppet.CustomIntent().EnsureJoined(portal.MXID) - } return user.ensureInvited(portal.MainIntent(), portal.MXID, portal.IsPrivateChat()) } @@ -1146,89 +1150,6 @@ func (portal *Portal) sendMessage(intent *appservice.IntentAPI, eventType event. } } -// func (portal *Portal) handleReaction(msgID groupme.ID, ppl []groupme.ID) { -// reactions := portal.bridge.DB.Reaction.GetByGMID(msgID) -// newLikes := newReactions(reactions, ppl) -// removeLikes := oldReactions(reactions, ppl) - -// var eventID id.EventID -// if len(newLikes) > 0 { -// message := portal.bridge.DB.Message.GetByGMID(portal.Key, msgID) -// if message == nil { -// portal.log.Errorln("Received reaction for unknown message", msgID) -// return -// } -// eventID = message.MXID -// } - -// for _, jid := range newLikes { -// intent := portal.getReactionIntent(jid) -// resp, err := portal.sendReaction(intent, eventID, "❤") -// if err != nil { -// portal.log.Errorln("Something wrong with sending reaction", msgID, jid, err) -// continue -// } - -// newReaction := portal.bridge.DB.Reaction.New() -// newReaction.MXID = resp.EventID -// newReaction.MessageJID = msgID -// newReaction.MessageMXID = eventID -// newReaction.PuppetJID = jid - -// newReaction.Insert() - -// } - -// for _, reaction := range removeLikes { -// if len(reaction.Puppet.JID) == 0 { -// portal.log.Warnln("Reaction user state wrong", reaction.MXID, msgID) -// continue -// } -// intent := portal.getReactionIntent(reaction.PuppetJID) -// _, err := intent.RedactEvent(portal.MXID, reaction.MXID) -// if err != nil { -// portal.log.Errorln("Something wrong with reaction redaction", reaction.MXID) -// continue -// } -// reaction.Delete() - -// } -// } - -// func oldReactions(a []*database.Reaction, b []string) (ans []*database.Reaction) { -// for _, i := range a { -// flag := false -// for _, j := range b { -// if i.PuppetJID == j { -// flag = true -// break -// } -// } -// if !flag { -// ans = append(ans, i) -// } -// } - -// return -// } - -// func newReactions(a []*database.Reaction, b []string) (ans []string) { -// for _, j := range b { -// flag := false -// for _, i := range a { -// if i.GMID == j { -// flag = true -// break -// } -// } -// if !flag { -// ans = append(ans, j) -// } -// } - -// return -// } - func (portal *Portal) sendMediaBridgeFailure(source *User, intent *appservice.IntentAPI, message groupme.Message, bridgeErr error) { portal.log.Errorfln("Failed to bridge media for %s: %v", message.UserID.String(), bridgeErr) resp, err := portal.sendMessage(intent, event.EventMessage, &event.MessageEventContent{ @@ -1290,36 +1211,12 @@ func (portal *Portal) convertMatrixMessage(sender *User, evt *event.Event) ([]*g return nil, sender } - //ts := uint64(evt.Timestamp / 1000) - //status := waProto.WebMessageInfo_ERROR - //fromMe := true - // info := &waProto.WebMessageInfo{ - // Key: &waProto.MessageKey{ - // FromMe: &fromMe, - // Id: makeMessageID(), - // RemoteJid: &portal.Key.JID, - // }, - // MessageTimestamp: &ts, - // Message: &waProto.Message{}, - // Status: &status, - // } - // info := groupme.Message{ GroupID: groupme.ID(portal.Key.String()), ConversationID: groupme.ID(portal.Key.String()), ChatID: groupme.ID(portal.Key.String()), RecipientID: groupme.ID(portal.Key.GMID), } - replyToID := content.GetReplyTo() - if len(replyToID) > 0 { - // content.RemoveReplyFallback() - // msg := portal.bridge.DB.Message.GetByMXID(replyToID) - // if msg != nil && msg.Content != nil { - // ctxInfo.StanzaId = &msg.JID - // ctxInfo.Participant = &msg.Sender - // ctxInfo.QuotedMessage = msg.Content - // } - } relaybotFormatted := false if evt.Type == event.EventSticker { @@ -1488,11 +1385,6 @@ func (portal *Portal) syncParticipants(group *groupme.Group) { } expectedLevel := 0 - // if participant.IsSuperAdmin { - // expectedLevel = 95 - // } else if participant.IsAdmin { - // expectedLevel = 50 - // } changed = levels.EnsureUserLevel(puppet.MXID, expectedLevel) || changed if user != nil { changed = levels.EnsureUserLevel(user.MXID, expectedLevel) || changed diff --git a/user.go b/user.go index 2a8438c..fadfdf9 100644 --- a/user.go +++ b/user.go @@ -56,7 +56,7 @@ type User struct { Client *groupmeext.Client ConnectionErrors int - CommunityID string + SpaceId string ChatList map[groupme.ID]*groupme.Chat GroupList map[groupme.ID]*groupme.Group @@ -90,6 +90,8 @@ type Chat struct { type ChatList []Chat +var connectWaitGroup = sync.WaitGroup{} + func (cl ChatList) Len() int { return len(cl) } @@ -152,53 +154,7 @@ func (user *User) GetIGhost() bridge.Ghost { } func (user *User) GetSpaceRoom() id.RoomID { - if !user.bridge.Config.Bridge.PersonalFilteringSpaces { - return "" - } - - if len(user.SpaceRoom) == 0 { - user.spaceCreateLock.Lock() - defer user.spaceCreateLock.Unlock() - if len(user.SpaceRoom) > 0 { - return user.SpaceRoom - } - - resp, err := user.bridge.Bot.CreateRoom(&mautrix.ReqCreateRoom{ - Visibility: "private", - Name: "GroupMe", - Topic: "Your GroupMe bridged chats", - InitialState: []*event.Event{{ - Type: event.StateRoomAvatar, - Content: event.Content{ - Parsed: &event.RoomAvatarEventContent{ - URL: user.bridge.Config.AppService.Bot.ParsedAvatar, - }, - }, - }}, - CreationContent: map[string]interface{}{ - "type": event.RoomTypeSpace, - }, - PowerLevelOverride: &event.PowerLevelsEventContent{ - Users: map[id.UserID]int{ - user.bridge.Bot.UserID: 9001, - user.MXID: 50, - }, - }, - }) - - if err != nil { - user.log.Errorln("Failed to auto-create space room:", err) - } else { - user.SpaceRoom = resp.RoomID - user.Update() - user.ensureInvited(user.bridge.Bot, user.SpaceRoom, false) - } - } else if !user.spaceMembershipChecked && !user.bridge.StateStore.IsInRoom(user.SpaceRoom, user.MXID) { - user.ensureInvited(user.bridge.Bot, user.SpaceRoom, false) - } - user.spaceMembershipChecked = true - - return user.SpaceRoom + return user.getSpaceRoom(&user.SpaceRoom, "GroupMe", "Your GroupMe bridged chats", "") } func (user *User) GetManagementRoom() id.RoomID { @@ -288,6 +244,7 @@ func (user *User) PostLogin() { } func (user *User) Connect() bool { + user.SpaceId = "GroupMe" if user.Conn != nil { return true } else if len(user.Token) == 0 { @@ -627,27 +584,19 @@ func (user *User) sendMarkdownBridgeAlert(formatString string, args ...interface } func (user *User) postConnPing() bool { - //user.log.Debugln("Making post-connection ping") - //err := user.Conn.AdminTest() - //if err != nil { - // user.log.Errorfln("Post-connection ping failed: %v. Disconnecting and then reconnecting after a second", err) - // sess, disconnectErr := user.Conn.Disconnect() - // if disconnectErr != nil { - // user.log.Warnln("Error while disconnecting after failed post-connection ping:", disconnectErr) - // } else { - // user.Session = &sess - // } - // user.bridge.Metrics.TrackDisconnection(user.MXID) - // go func() { - // time.Sleep(1 * time.Second) - // user.tryReconnect(fmt.Sprintf("Post-connection ping failed: %v", err)) - // }() - // return false - //} else { - // user.log.Debugln("Post-connection ping OK") - // return true - //} - return true + user.log.Debugln("Making post-connection ping") + if !user.Conn.Connected() { + user.log.Errorfln("Post-connection ping failed: %v. Disconnecting and then reconnecting after a second") + user.bridge.Metrics.TrackDisconnection(user.MXID) + go func() { + time.Sleep(1 * time.Second) + user.Connect() + }() + return false + } else { + user.log.Debugln("Post-connection ping OK") + return true + } } func (user *User) intPostLogin() { @@ -670,17 +619,12 @@ func (user *User) intPostLogin() { select { case <-user.chatListReceived: user.log.Debugln("Chat list receive confirmation received in PostLogin") - case <-time.After(time.Duration(1000 /**user.bridge.Config.Bridge.ChatListWait**/) * time.Second): + case <-time.After(time.Duration(10000 /**user.bridge.Config.Bridge.ChatListWait**/) * time.Second): user.log.Warnln("Timed out waiting for chat list to arrive!") user.postConnPing() return } - if !user.postConnPing() { - user.log.Debugln("Post-connection ping failed, unlocking processing of incoming messages.") - return - } - user.log.Debugln("Waiting for portal sync complete confirmation") select { case <-user.syncPortalsDone: @@ -719,17 +663,16 @@ func (user *User) syncPortals(createAll bool) { } //for _, chat := range chats { - //var inSpace, ok bool - //if inSpace, ok = existingKeys[chat.Portal.Key]; !ok || !inCommunity { - // inCommunity = user.addPortalToCommunity(chat.Portal) - // if chat.Portal.IsPrivateChat() { - // puppet := user.bridge.GetPuppetByGMID(chat.Portal.Key.GMID) - // user.ad.addPuppetToCommunity(puppet) + // if user.IsInSpace(chat.Portal.Key) { + // user.MarkInSpace(chat.Portal.Key) + // if chat.Portal.IsPrivateChat() { + // user.addPortalToSpace(chat.Portal) + // } // } + // + // portalKeys = append(portalKeys, chat.Portal.Key) //} - //portalKeys = append(portalKeys, chat.Portal.Key) - //} - //user.log.Infoln("Read chat list, updating user-portal mapping") + user.log.Infoln("Read chat list, updating user-portal mapping") err := user.SetPortalKeys(portalKeys) if err != nil { @@ -773,6 +716,47 @@ func (user *User) syncPortals(createAll bool) { } } +func (user *User) addPuppetToSpace(puppet *Puppet) bool { + bot := user.bridge.Bot + url := bot.BuildURL(mautrix.ClientURLPath{"groups", user.SpaceId, "admin", "users", "invite", puppet.MXID}) + blankReqBody := map[string]interface{}{} + _, err := bot.MakeRequest(http.MethodPut, url, &blankReqBody, nil) + if err != nil { + user.log.Warnfln("Failed to invite %s to %s: %v", puppet.MXID, user.SpaceId, err) + return false + } + reqBody := map[string]map[string]string{ + "m.visibility": { + "type": "private", + }, + } + url = bot.BuildURLWithQuery(mautrix.ClientURLPath{"groups", user.SpaceId, "self", "accept_invite"}, map[string]string{ + "user_id": puppet.MXID.String(), + }) + _, err = bot.MakeRequest(http.MethodPut, url, &reqBody, nil) + if err != nil { + user.log.Warnfln("Failed to join %s as %s: %v", user.SpaceId, puppet.MXID, err) + return false + } + user.log.Debugln("Added", puppet.MXID, "to", user.SpaceId) + return true +} + +func (user *User) addPortalToSpace(portal *Portal) bool { + if portal.MXID == "" { + return false + } + _, err := user.bridge.Bot.SendStateEvent(user.GetSpaceRoom(), event.StateSpaceChild, portal.MXID.String(), &event.SpaceChildEventContent{ + Via: []string{user.bridge.AS.HomeserverDomain}, + }) + if err != nil { + user.log.Errorln("Failed to add portal space. RoomId: ", portal.MXID.String()) + return false + } else { + return true + } +} + func (user *User) getDirectChats() map[id.UserID][]id.RoomID { res := make(map[id.UserID][]id.RoomID) privateChats := user.bridge.DB.Portal.FindPrivateChats(user.GMID) @@ -784,6 +768,78 @@ func (user *User) getDirectChats() map[id.UserID][]id.RoomID { return res } +func (user *User) getSpaceRoom(ptr *id.RoomID, name, topic string, parent id.RoomID) id.RoomID { + if len(*ptr) > 0 { + return *ptr + } + user.spaceCreateLock.Lock() + defer user.spaceCreateLock.Unlock() + if len(*ptr) > 0 { + return *ptr + } + + initialState := []*event.Event{{ + Type: event.StateRoomAvatar, + Content: event.Content{ + Parsed: &event.RoomAvatarEventContent{ + URL: user.bridge.Config.AppService.Bot.ParsedAvatar, + }, + }, + }} + + if parent != "" { + parentIDStr := parent.String() + initialState = append(initialState, &event.Event{ + Type: event.StateSpaceParent, + StateKey: &parentIDStr, + Content: event.Content{ + Parsed: &event.SpaceParentEventContent{ + Canonical: true, + Via: []string{user.bridge.AS.HomeserverDomain}, + }, + }, + }) + } + + resp, err := user.bridge.Bot.CreateRoom(&mautrix.ReqCreateRoom{ + Visibility: "private", + Name: name, + Topic: topic, + InitialState: initialState, + CreationContent: map[string]interface{}{ + "type": event.RoomTypeSpace, + }, + PowerLevelOverride: &event.PowerLevelsEventContent{ + Users: map[id.UserID]int{ + user.bridge.Bot.UserID: 9001, + user.MXID: 50, + }, + }, + }) + + if err != nil { + user.log.Errorln("Failed to auto-create space room", err) + } else { + *ptr = resp.RoomID + user.Update() + user.ensureInvited(user.bridge.Bot, resp.RoomID, true) + + //if parent != "" { + // _, err = user.bridge.Bot.SendStateEvent(parent, event.StateSpaceChild, resp.RoomID.String(), &event.SpaceChildEventContent{ + // Via: []string{user.bridge.AS.HomeserverDomain}, + // Order: " 0000", + // }) + // if err != nil { + // user.log.Error().Err(err). + // Str("created_space_id", resp.RoomID.String()). + // Str("parent_space_id", parent.String()). + // Msg("Failed to add created space room to parent space") + // } + //} + } + return *ptr +} + func (user *User) updateAvatar(gmdi groupme.ID, avatarID *string, avatarURL *id.ContentURI, avatarSet *bool, log log.Logger, intent *appservice.IntentAPI) bool { return false