Mark rooms needing T5DE in the room list (t5de_t5), for the client's T5 badge
CI / build (push) Successful in 30s

Hosted rooms, and IMVU rooms the hub has decorations for (asked 20 at a time), on every page; hosted
rooms are added to recent rooms from the hub's recent list when it has one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
dhkatzandClaude Opus 5.5 committed 2026-10-30 05:58:12 -07:00
1 parent 08a2ed94a3
commit d379d848c1
2 files changed
+102 -29

No files matched your search

+91 -23
View File
@@ -11,6 +11,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"regexp"
"sort"
"strconv"
@@ -763,16 +764,14 @@ func (r *Rooms) others(n int64) []xmlrpc.Value {
return out
}
// addToRoomList adds hosted rooms to the client's room list (rooms_list_paginated.php: one array per field): all
// of them to the first page without a search, and those the client was in this run to recently visited rooms.
// addToRoomList changes the client's room list (rooms_list_paginated.php: one array per field): hosted rooms are
// added to the first page without a search (all of them; to recently visited rooms, the person's recent ones), and
// every room gets t5de_t5: 1 for rooms needing T5DE to be seen as meant (hosted, or with T5 decorations), for the
// client's T5 badge (its t5de-rooms patch).
func (r *Rooms) addToRoomList(x *proxy.Exchange) {
q := x.URL.Query()
if p := q.Get("page"); p != "" && p != "1" && p != "0" {
return
}
if q.Get("keywords") != "" || q.Get("search") != "" {
return
}
firstPage := q.Get("page") == "" || q.Get("page") == "1" || q.Get("page") == "0"
addHosted := firstPage && q.Get("keywords") == "" && q.Get("search") == ""
recent := q.Get("recent") != ""
x.ModifyResponse = func(resp *http.Response, body []byte) ([]byte, error) {
var list map[string]json.RawMessage
@@ -781,28 +780,97 @@ func (r *Rooms) addToRoomList(x *proxy.Exchange) {
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var out struct {
Rooms []Room `json:"rooms"`
}
if err := r.Hub.Call(ctx, "GET", "/api/v1/hosted-rooms", nil, &out); err != nil || len(out.Rooms) == 0 {
return body, nil
}
sort.SliceStable(out.Rooms, func(i, j int) bool { return out.Rooms[i].Occupancy > out.Rooms[j].Occupancy })
r.mu.Lock()
visited := map[int64]bool{}
for n := range r.owners {
visited[n] = true
}
r.mu.Unlock()
for _, rm := range out.Rooms {
if !recent || visited[rm.N] {
if addHosted {
for _, rm := range r.listedRooms(ctx, recent) {
appendRoom(list, rm)
}
}
r.markT5(ctx, list)
return json.Marshal(list)
}
}
// listedRooms is the hosted rooms for the list: the person's recent ones (or, if the hub can't say, those visited
// this run), or all of them, busiest first.
func (r *Rooms) listedRooms(ctx context.Context, recent bool) []Room {
var out struct {
Rooms []Room `json:"rooms"`
}
if recent {
if err := r.Hub.Call(ctx, "GET", "/api/v1/hosted-rooms?recent=1", nil, &out); err == nil {
return out.Rooms
}
}
if err := r.Hub.Call(ctx, "GET", "/api/v1/hosted-rooms", nil, &out); err != nil {
return nil
}
sort.SliceStable(out.Rooms, func(i, j int) bool { return out.Rooms[i].Occupancy > out.Rooms[j].Occupancy })
if !recent {
return out.Rooms
}
r.mu.Lock()
defer r.mu.Unlock()
var visited []Room
for _, rm := range out.Rooms {
if _, ok := r.owners[rm.N]; ok {
visited = append(visited, rm)
}
}
return visited
}
// markT5 sets the list's t5de_t5 column: hosted rooms, and IMVU rooms the hub has decorations for.
func (r *Rooms) markT5(ctx context.Context, list map[string]json.RawMessage) {
var owners, numbers []json.RawMessage
if json.Unmarshal(list["customers_id"], &owners) != nil || json.Unmarshal(list["customers_room_id"], &numbers) != nil || len(owners) != len(numbers) {
return
}
marks := make([]int, len(owners))
var ask []string
index := map[string][]int{}
for i := range owners {
instance := rawText(owners[i]) + "-" + rawText(numbers[i])
if _, ok := RoomNumber(instance); ok {
marks[i] = 1
continue
}
if index[instance] == nil {
ask = append(ask, instance)
}
index[instance] = append(index[instance], i)
}
for len(ask) > 0 {
batch := ask[:min(len(ask), 20)]
ask = ask[len(batch):]
var out struct {
Rooms []struct {
Instance string `json:"instance"`
Contents []json.RawMessage `json:"contents"`
} `json:"rooms"`
}
if err := r.Hub.Call(ctx, "GET", "/api/v1/decorations?instances="+url.QueryEscape(strings.Join(batch, ",")), nil, &out); err != nil {
break
}
for _, d := range out.Rooms {
if len(d.Contents) > 0 {
for _, i := range index[d.Instance] {
marks[i] = 1
}
}
}
}
list["t5de_t5"], _ = json.Marshal(marks)
}
// rawText is a JSON string's or number's text.
func rawText(raw json.RawMessage) string {
var s string
if json.Unmarshal(raw, &s) == nil {
return s
}
return strings.TrimSpace(string(raw))
}
// appendRoom adds a room to each of the list's arrays (fields it doesn't know get an empty value of the array's
// kind).
func appendRoom(list map[string]json.RawMessage, rm Room) {