Add endpoints to list groups and open group portals

This commit is contained in:
Tulir Asokan 2022-03-01 20:25:46 +02:00
parent 4bba398cfe
commit 8e5442300c
2 changed files with 85 additions and 5 deletions

View file

@ -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{}{

22
user.go
View file

@ -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
}