Ver Fonte

Add endpoints to list groups and open group portals

Tulir Asokan há 3 anos atrás
pai
commit
8e5442300c
2 ficheiros alterados com 85 adições e 5 exclusões
  1. 64 4
      provisioning.go
  2. 21 1
      user.go

+ 64 - 4
provisioning.go

@@ -58,7 +58,9 @@ func (prov *ProvisioningAPI) Init() {
 	r.HandleFunc("/reconnect", prov.Reconnect).Methods(http.MethodPost)
 	r.HandleFunc("/sync/appstate/{name}", prov.SyncAppState).Methods(http.MethodPost)
 	r.HandleFunc("/contacts", prov.ListContacts).Methods(http.MethodGet)
+	r.HandleFunc("/groups", prov.ListGroups).Methods(http.MethodGet)
 	r.HandleFunc("/pm/{number}", prov.StartPM).Methods(http.MethodPost)
+	r.HandleFunc("/open/{groupID}", prov.OpenGroup).Methods(http.MethodPost)
 	prov.bridge.AS.Router.HandleFunc("/_matrix/app/com.beeper.asmux/ping", prov.BridgeStatePing).Methods(http.MethodPost)
 	prov.bridge.AS.Router.HandleFunc("/_matrix/app/com.beeper.bridge_state", prov.BridgeStatePing).Methods(http.MethodPost)
 
@@ -237,6 +239,23 @@ func (prov *ProvisioningAPI) ListContacts(w http.ResponseWriter, r *http.Request
 	}
 }
 
+func (prov *ProvisioningAPI) ListGroups(w http.ResponseWriter, r *http.Request) {
+	if user := r.Context().Value("user").(*User); user.Session == nil {
+		jsonResponse(w, http.StatusBadRequest, Error{
+			Error:   "User is not logged into WhatsApp",
+			ErrCode: "no session",
+		})
+	} else if groups, err := user.getCachedGroupList(); err != nil {
+		prov.log.Errorfln("Failed to fetch %s's groups: %v", user.MXID, err)
+		jsonResponse(w, http.StatusInternalServerError, Error{
+			Error:   "Internal server error while fetching group list",
+			ErrCode: "failed to get groups",
+		})
+	} else {
+		jsonResponse(w, http.StatusOK, groups)
+	}
+}
+
 type OtherUserInfo struct {
 	MXID   id.UserID     `json:"mxid"`
 	JID    types.JID     `json:"jid"`
@@ -245,9 +264,10 @@ type OtherUserInfo struct {
 }
 
 type PortalInfo struct {
-	RoomID      id.RoomID     `json:"room_id"`
-	OtherUser   OtherUserInfo `json:"other_user"`
-	JustCreated bool          `json:"just_created"`
+	RoomID      id.RoomID        `json:"room_id"`
+	OtherUser   *OtherUserInfo   `json:"other_user,omitempty"`
+	GroupInfo   *types.GroupInfo `json:"group_info,omitempty"`
+	JustCreated bool             `json:"just_created"`
 }
 
 func (prov *ProvisioningAPI) StartPM(w http.ResponseWriter, r *http.Request) {
@@ -287,7 +307,7 @@ func (prov *ProvisioningAPI) StartPM(w http.ResponseWriter, r *http.Request) {
 		}
 		jsonResponse(w, status, PortalInfo{
 			RoomID: portal.MXID,
-			OtherUser: OtherUserInfo{
+			OtherUser: &OtherUserInfo{
 				JID:    puppet.JID,
 				MXID:   puppet.MXID,
 				Name:   puppet.Displayname,
@@ -298,6 +318,46 @@ func (prov *ProvisioningAPI) StartPM(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
+func (prov *ProvisioningAPI) OpenGroup(w http.ResponseWriter, r *http.Request) {
+	groupID, _ := mux.Vars(r)["groupID"]
+	if user := r.Context().Value("user").(*User); !user.IsLoggedIn() {
+		jsonResponse(w, http.StatusBadRequest, Error{
+			Error:   "User is not logged into WhatsApp",
+			ErrCode: "no session",
+		})
+	} else if jid, err := types.ParseJID(groupID); err != nil || jid.Server != types.GroupServer || (!strings.ContainsRune(jid.User, '-') && len(jid.User) < 15) {
+		jsonResponse(w, http.StatusBadRequest, Error{
+			Error:   "Invalid group ID",
+			ErrCode: "invalid group id",
+		})
+	} else if info, err := user.Client.GetGroupInfo(jid); err != nil {
+		// TODO return better responses for different errors (like ErrGroupNotFound and ErrNotInGroup)
+		jsonResponse(w, http.StatusInternalServerError, Error{
+			Error:   fmt.Sprintf("Failed to get group info: %v", err),
+			ErrCode: "error getting group info",
+		})
+	} else {
+		prov.log.Debugln("Importing", jid, "for", user.MXID)
+		portal := user.GetPortalByJID(info.JID)
+		status := http.StatusOK
+		if len(portal.MXID) == 0 {
+			err = portal.CreateMatrixRoom(user, info, true)
+			if err != nil {
+				jsonResponse(w, http.StatusInternalServerError, Error{
+					Error: fmt.Sprintf("Failed to create portal: %v", err),
+				})
+				return
+			}
+			status = http.StatusCreated
+		}
+		jsonResponse(w, status, PortalInfo{
+			RoomID:      portal.MXID,
+			GroupInfo:   info,
+			JustCreated: status == http.StatusCreated,
+		})
+	}
+}
+
 func (prov *ProvisioningAPI) Ping(w http.ResponseWriter, r *http.Request) {
 	user := r.Context().Value("user").(*User)
 	wa := map[string]interface{}{

+ 21 - 1
user.go

@@ -1,5 +1,5 @@
 // mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
-// Copyright (C) 2021 Tulir Asokan
+// Copyright (C) 2022 Tulir Asokan
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Affero General Public License as published by
@@ -70,6 +70,10 @@ type User struct {
 
 	spaceMembershipChecked  bool
 	lastPhoneOfflineWarning time.Time
+
+	groupListCache     []*types.GroupInfo
+	groupListCacheLock sync.Mutex
+	groupListCacheTime time.Time
 }
 
 func (bridge *Bridge) getUserByMXID(userID id.UserID, onlyIfExists bool) *User {
@@ -629,8 +633,10 @@ func (user *User) HandleEvent(event interface{}) {
 	case *events.PushName:
 		go user.syncPuppet(v.JID, "push name event")
 	case *events.GroupInfo:
+		user.groupListCache = nil
 		go user.handleGroupUpdate(v)
 	case *events.JoinedGroup:
+		user.groupListCache = nil
 		go user.handleGroupCreate(v)
 	case *events.Picture:
 		go user.handlePictureUpdate(v)
@@ -1080,3 +1086,17 @@ func (user *User) StartPM(jid types.JID, reason string) (*Portal, *Puppet, bool,
 	err := portal.CreateMatrixRoom(user, nil, false)
 	return portal, puppet, true, err
 }
+
+const groupListCacheMaxAge = 24 * time.Hour
+
+func (user *User) getCachedGroupList() ([]*types.GroupInfo, error) {
+	user.groupListCacheLock.Lock()
+	defer user.groupListCacheLock.Unlock()
+	if user.groupListCache != nil && user.groupListCacheTime.Add(groupListCacheMaxAge).After(time.Now()) {
+		return user.groupListCache, nil
+	}
+	var err error
+	user.groupListCache, err = user.Client.GetJoinedGroups()
+	user.groupListCacheTime = time.Now()
+	return user.groupListCache, err
+}