legacyprovisioning: implement SNC endpoints

Signed-off-by: Sumner Evans <sumner.evans@automattic.com>
This commit is contained in:
Sumner Evans
2024-09-03 09:51:58 -06:00
parent ec330c72be
commit 8925318ec4
2 changed files with 145 additions and 0 deletions

View File

@@ -21,15 +21,19 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"
"sync"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
"github.com/rs/zerolog"
"go.mau.fi/util/exhttp"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/id"
"go.mau.fi/mautrix-telegram/pkg/connector"
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
)
type response struct {
@@ -262,3 +266,141 @@ func legacyProvLogout(w http.ResponseWriter, r *http.Request) {
}
exhttp.WriteEmptyJSONResponse(w, http.StatusOK)
}
func legacyProvContacts(w http.ResponseWriter, r *http.Request) {
log := zerolog.Ctx(r.Context()).With().
Str("prov_method", "contacts").
Logger()
ctx := log.WithContext(r.Context())
var resp response
login := m.Matrix.Provisioning.GetLoginForRequest(w, r)
if login == nil {
exhttp.WriteJSONResponse(w, http.StatusNotFound, resp.WithError(mautrix.MNotFound.ErrCode, "No login found"))
return
}
api := login.Client.(bridgev2.ContactListingNetworkAPI)
contacts, err := api.GetContactList(ctx)
if err != nil {
log.Err(err).Msg("Failed to get contacts")
exhttp.WriteJSONResponse(w, http.StatusInternalServerError, resp.WithError("M_UNKNOWN", fmt.Sprintf("Failed to get contacts: %v", err)))
return
}
contactsMap := map[int64]*legacyContactInfo{}
for _, contact := range contacts {
id, err := ids.ParseUserID(contact.UserID)
if err != nil {
log.Err(err).Msg("Failed to parse user id")
exhttp.WriteJSONResponse(w, http.StatusInternalServerError, resp.WithError("M_UNKNOWN", fmt.Sprintf("Failed to parse user id: %v", err)))
return
}
if contact.UserInfo != nil {
contact.Ghost.UpdateInfo(ctx, contact.UserInfo)
}
contactsMap[id] = legacyContactInfoFromGhost(contact.Ghost)
}
exhttp.WriteJSONResponse(w, http.StatusOK, contactsMap)
}
func legacyProvResolveIdentifier(w http.ResponseWriter, r *http.Request) {
legacyResolveIdentifierOrStartChat(w, r, false)
}
func legacyProvPM(w http.ResponseWriter, r *http.Request) {
legacyResolveIdentifierOrStartChat(w, r, true)
}
type legacyResolveIdentifierResponse struct {
RoomID id.RoomID `json:"room_id,omitempty"`
JustCreated bool `json:"just_created,omitempty"`
ID int `json:"id,omitempty"`
ContactInfo *legacyContactInfo `json:"contact_info,omitempty"`
}
type legacyContactInfo struct {
Name string `json:"name,omitempty"`
Username string `json:"username,omitempty"`
Phone string `json:"phone,omitempty"`
IsBot bool `json:"is_bot,omitempty"`
AvatarURL id.ContentURIString `json:"avatar_url,omitempty"`
}
func legacyContactInfoFromGhost(ghost *bridgev2.Ghost) *legacyContactInfo {
var username, phone string
for _, id := range ghost.Identifiers {
if strings.HasPrefix(id, "telegram:") {
username = strings.TrimPrefix(id, "telegram:")
} else if strings.HasPrefix(id, "tel:") {
phone = strings.TrimPrefix(id, "tel:")
}
}
return &legacyContactInfo{
Name: ghost.Name,
Username: username,
Phone: phone,
IsBot: ghost.Metadata.(*connector.GhostMetadata).IsBot,
AvatarURL: ghost.AvatarMXC,
}
}
func legacyResolveIdentifierOrStartChat(w http.ResponseWriter, r *http.Request, create bool) {
log := zerolog.Ctx(r.Context()).With().
Str("prov_method", "resolve_identifier").
Bool("create", create).
Logger()
ctx := log.WithContext(r.Context())
var resp response
login := m.Matrix.Provisioning.GetLoginForRequest(w, r)
if login == nil {
exhttp.WriteJSONResponse(w, http.StatusNotFound, resp.WithError(mautrix.MNotFound.ErrCode, "No login found"))
return
}
api := login.Client.(bridgev2.IdentifierResolvingNetworkAPI)
identResp, err := api.ResolveIdentifier(ctx, mux.Vars(r)["identifier"], create)
if err != nil {
log.Err(err).Msg("Failed to resolve identifier")
exhttp.WriteJSONResponse(w, http.StatusInternalServerError,
resp.WithError("M_UNKNOWN", fmt.Sprintf("Failed to resolve identifier: %v", err)))
return
} else if identResp == nil {
exhttp.WriteJSONResponse(w, http.StatusNotFound,
resp.WithError(mautrix.MNotFound.ErrCode, "User not found on Telegram"))
return
}
status := http.StatusOK
var apiResp legacyResolveIdentifierResponse
if identResp.Ghost != nil {
if identResp.UserInfo != nil {
identResp.Ghost.UpdateInfo(ctx, identResp.UserInfo)
}
apiResp.ContactInfo = legacyContactInfoFromGhost(identResp.Ghost)
}
if identResp.Chat != nil {
if identResp.Chat.Portal == nil {
identResp.Chat.Portal, err = m.Bridge.GetPortalByKey(ctx, identResp.Chat.PortalKey)
if err != nil {
log.Err(err).Msg("Failed to get portal")
exhttp.WriteJSONResponse(w, http.StatusInternalServerError,
resp.WithError("M_UNKNOWN", "Failed to get portal"))
return
}
}
if create && identResp.Chat.Portal.MXID == "" {
apiResp.JustCreated = true
status = http.StatusCreated
err = identResp.Chat.Portal.CreateMatrixRoom(ctx, login, identResp.Chat.PortalInfo)
if err != nil {
log.Err(err).Msg("Failed to create portal room")
exhttp.WriteJSONResponse(w, http.StatusInternalServerError,
resp.WithError("M_UNKNOWN", "Failed to create portal room"))
return
}
}
apiResp.RoomID = identResp.Chat.Portal.MXID
}
exhttp.WriteJSONResponse(w, status, &apiResp)
}

View File

@@ -87,6 +87,9 @@ func main() {
m.Matrix.Provisioning.Router.HandleFunc("/v1/user/{userID}/login/send_code", legacyProvLoginSendCode)
m.Matrix.Provisioning.Router.HandleFunc("/v1/user/{userID}/login/send_password", legacyProvLoginSendPassword)
m.Matrix.Provisioning.Router.HandleFunc("/v1/user/{userID}/logout", legacyProvLogout)
m.Matrix.Provisioning.Router.HandleFunc("/v1/user/{userID}/contacts", legacyProvContacts)
m.Matrix.Provisioning.Router.HandleFunc("/v1/user/{userID}/resolve_identifier/{identifier}", legacyProvResolveIdentifier)
m.Matrix.Provisioning.Router.HandleFunc("/v1/user/{userID}/pm/{identifier}", legacyProvPM)
}
}
m.PostInit = func() {