Manage T5DE rooms from the client; the AP+ badge on avatar cards
CI / build (push) Successful in 28s
CI / build (push) Successful in 28s
Chat Rooms > Manage (the t5de-room-manage patch) lists the person's T5DE rooms (room_management_info.php?t5de=1) and saves them from IMVU's room dialog (room_management.php: create with t5de=1, edit and delete naming a hosted room) as hub calls: the room, its moderators by name, and a picture from the person's IMVU gallery, uploaded to the hub. The hub's refusals show in the dialog (hub.Error keeps their message). Avatar cards get show_ap_plus again, which IMVU stopped sending, from the person's public IMVU profile (is_ap_plus). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
6aac769246
commit
8daf91ede3
7 files changed
+608
-2
No files matched your search
+46
-1
@@ -8,13 +8,16 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -491,10 +494,52 @@ func (c *Client) Call(ctx context.Context, method, path string, body, out any) e
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, _ := io.ReadAll(io.LimitReader(resp.Body, 4<<20))
|
||||
return answer(resp, data, out)
|
||||
}
|
||||
|
||||
// Upload sends a file to the hub as multipart form data (one field), as Call does JSON.
|
||||
func (c *Client) Upload(ctx context.Context, method, path, field, filename, contentType string, file []byte, out any) error {
|
||||
var buf bytes.Buffer
|
||||
w := multipart.NewWriter(&buf)
|
||||
h := textproto.MIMEHeader{}
|
||||
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name=%q; filename=%q`, field, filename))
|
||||
h.Set("Content-Type", contentType)
|
||||
part, err := w.CreatePart(h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
part.Write(file)
|
||||
w.Close()
|
||||
req, err := http.NewRequestWithContext(ctx, method, c.saved.URL+path, &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+c.saved.Token)
|
||||
req.Header.Set("User-Agent", UserAgent)
|
||||
req.Header.Set("Content-Type", w.FormDataContentType())
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, _ := io.ReadAll(io.LimitReader(resp.Body, 4<<20))
|
||||
return answer(resp, data, out)
|
||||
}
|
||||
|
||||
// Error is the hub refusing a call: its status, error code and message (shown to people as is).
|
||||
type Error struct {
|
||||
Status int
|
||||
Code string
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e *Error) Error() string { return fmt.Sprintf("the hub answered %d %s", e.Status, e.Code) }
|
||||
|
||||
func answer(resp *http.Response, data []byte, out any) error {
|
||||
if resp.StatusCode/100 != 2 {
|
||||
var e struct{ Error, Message string }
|
||||
json.Unmarshal(data, &e)
|
||||
return fmt.Errorf("the hub answered %d %s", resp.StatusCode, e.Error)
|
||||
return &Error{Status: resp.StatusCode, Code: e.Error, Message: e.Message}
|
||||
}
|
||||
if out == nil {
|
||||
return nil
|
||||
|
||||
Reference in new issue
Block a user