Mark rooms needing T5DE in the room list (t5de_t5), for the client's T5 badge
CI / build (push) Successful in 30s
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:
1 parent
08a2ed94a3
commit
d379d848c1
2 files changed
+102
-29
No files matched your search
+91
-23
@@ -11,6 +11,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -763,16 +764,14 @@ func (r *Rooms) others(n int64) []xmlrpc.Value {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// addToRoomList adds hosted rooms to the client's room list (rooms_list_paginated.php: one array per field): all
|
// addToRoomList changes the client's room list (rooms_list_paginated.php: one array per field): hosted rooms are
|
||||||
// of them to the first page without a search, and those the client was in this run to recently visited rooms.
|
// 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) {
|
func (r *Rooms) addToRoomList(x *proxy.Exchange) {
|
||||||
q := x.URL.Query()
|
q := x.URL.Query()
|
||||||
if p := q.Get("page"); p != "" && p != "1" && p != "0" {
|
firstPage := q.Get("page") == "" || q.Get("page") == "1" || q.Get("page") == "0"
|
||||||
return
|
addHosted := firstPage && q.Get("keywords") == "" && q.Get("search") == ""
|
||||||
}
|
|
||||||
if q.Get("keywords") != "" || q.Get("search") != "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
recent := q.Get("recent") != ""
|
recent := q.Get("recent") != ""
|
||||||
x.ModifyResponse = func(resp *http.Response, body []byte) ([]byte, error) {
|
x.ModifyResponse = func(resp *http.Response, body []byte) ([]byte, error) {
|
||||||
var list map[string]json.RawMessage
|
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)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
var out struct {
|
if addHosted {
|
||||||
Rooms []Room `json:"rooms"`
|
for _, rm := range r.listedRooms(ctx, recent) {
|
||||||
}
|
|
||||||
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] {
|
|
||||||
appendRoom(list, rm)
|
appendRoom(list, rm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
r.markT5(ctx, list)
|
||||||
return json.Marshal(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
|
// 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).
|
// kind).
|
||||||
func appendRoom(list map[string]json.RawMessage, rm Room) {
|
func appendRoom(list map[string]json.RawMessage, rm Room) {
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ func (h *fakeHub) Call(ctx context.Context, method, path string, body, out any)
|
|||||||
return json.Unmarshal([]byte(`{"room": {"n": 3, "name": "Lounge <b>", "ownerImvuId": 88, "ownerName": "Erin",
|
return json.Unmarshal([]byte(`{"room": {"n": 3, "name": "Lounge <b>", "ownerImvuId": 88, "ownerName": "Erin",
|
||||||
"roomPid": 50168116, "rating": "general", "maxUsers": 10, "occupancy": 2},
|
"roomPid": 50168116, "rating": "general", "maxUsers": 10, "occupancy": 2},
|
||||||
"scene": {"contents": [[1, 47477426, "37207-46"], [2, "coatcoat22", "37-11"], [3, "notherenn2", "37-12"]], "hash": "h1"}}`), out)
|
"scene": {"contents": [[1, 47477426, "37207-46"], [2, "coatcoat22", "37-11"], [3, "notherenn2", "37-12"]], "hash": "h1"}}`), out)
|
||||||
|
case method == "GET" && strings.HasPrefix(path, "/api/v1/decorations?instances="):
|
||||||
|
return json.Unmarshal([]byte(`{"rooms": [{"instance": "1-644", "contents": [[1, "coatcoat22", "x"]]}, {"instance": "2-17", "contents": []}]}`), out)
|
||||||
case method == "GET" && path == "/api/v1/hosted-rooms":
|
case method == "GET" && path == "/api/v1/hosted-rooms":
|
||||||
return json.Unmarshal([]byte(`{"rooms": [{"n": 3, "name": "Lounge", "ownerImvuId": 88, "ownerName": "Erin", "maxUsers": 10, "occupancy": 2}]}`), out)
|
return json.Unmarshal([]byte(`{"rooms": [{"n": 3, "name": "Lounge", "ownerImvuId": 88, "ownerName": "Erin", "maxUsers": 10, "occupancy": 2}]}`), out)
|
||||||
case method == "PUT" && path == "/api/v1/hosted-rooms/3/scene":
|
case method == "PUT" && path == "/api/v1/hosted-rooms/3/scene":
|
||||||
@@ -241,22 +243,25 @@ func TestRoomList(t *testing.T) {
|
|||||||
req, _ := http.NewRequest("GET", u.String(), nil)
|
req, _ := http.NewRequest("GET", u.String(), nil)
|
||||||
x := &proxy.Exchange{URL: u, Request: req}
|
x := &proxy.Exchange{URL: u, Request: req}
|
||||||
r.Intercept(x)
|
r.Intercept(x)
|
||||||
body := []byte(`{"customers_room_id": ["644"], "customers_id": ["1"], "num_participants": [4], "name": ["A"], "participants": [[]], "is_ap": ["0"], "odd": [{"x": 1}], "number_of_rooms": 1}`)
|
body := []byte(`{"customers_room_id": ["644", "17"], "customers_id": ["1", "2"], "num_participants": [4, 1], "name": ["A", "B"], "participants": [[], []], "is_ap": ["0", "0"], "odd": [{"x": 1}, {}], "number_of_rooms": 2}`)
|
||||||
out, err := x.ModifyResponse(&http.Response{StatusCode: 200}, body)
|
out, err := x.ModifyResponse(&http.Response{StatusCode: 200}, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
var l map[string]any
|
var l map[string]any
|
||||||
json.Unmarshal(out, &l)
|
json.Unmarshal(out, &l)
|
||||||
if fmt.Sprint(l["customers_room_id"]) != "[644 2100000003]" || fmt.Sprint(l["customers_id"]) != "[1 88]" ||
|
if fmt.Sprint(l["customers_room_id"]) != "[644 17 2100000003]" || fmt.Sprint(l["customers_id"]) != "[1 2 88]" ||
|
||||||
fmt.Sprint(l["num_participants"]) != "[4 2]" || fmt.Sprint(l["odd"]) != "[map[x:1] map[]]" || l["number_of_rooms"] != float64(1) {
|
fmt.Sprint(l["num_participants"]) != "[4 1 2]" || fmt.Sprint(l["odd"]) != "[map[x:1] map[] map[]]" || l["number_of_rooms"] != float64(2) ||
|
||||||
|
fmt.Sprint(l["t5de_t5"]) != "[1 0 1]" { // decorated, plain, hosted
|
||||||
t.Fatalf("list %s", out)
|
t.Fatalf("list %s", out)
|
||||||
}
|
}
|
||||||
// Other pages and searches are left alone.
|
// Other pages get no hosted rooms, but their decorated rooms are marked.
|
||||||
u, _ = url.Parse("https://client-dynamic.imvu.com/api/rooms/rooms_list_paginated.php?page=2")
|
u, _ = url.Parse("https://client-dynamic.imvu.com/api/rooms/rooms_list_paginated.php?page=2")
|
||||||
x = &proxy.Exchange{URL: u, Request: req}
|
x = &proxy.Exchange{URL: u, Request: req}
|
||||||
r.Intercept(x)
|
r.Intercept(x)
|
||||||
if x.ModifyResponse != nil {
|
out, _ = x.ModifyResponse(&http.Response{StatusCode: 200}, body)
|
||||||
t.Fatal("changed page 2")
|
json.Unmarshal(out, &l)
|
||||||
|
if fmt.Sprint(l["customers_room_id"]) != "[644 17]" || fmt.Sprint(l["t5de_t5"]) != "[1 0]" {
|
||||||
|
t.Fatalf("page 2: %s", out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user