CI / build (push) Successful in 34s
- The inventory's Recently Added puts T5 products added to the library in the last 30 days first. - The shop's T5 listing follows its sort menu: Best Match ranks by name, Newest by upload date (oldest first ascending); ratings and prices (T5 products have neither) sort by newest. - Outfit cards: outfit_products.php gets the outfit's community products added (not for sale), find_bundle.php is asked about IMVU's products only, and removing one in the card (remove_pids) keeps it from IMVU and forgets it in the outfit. Co-Authored-By: Claude Opus 5.5 <[email protected]>
79 lines
2.3 KiB
Go
79 lines
2.3 KiB
Go
package products
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"sort"
|
|
"strconv"
|
|
"time"
|
|
|
|
"git.t5de.dev/t5de/t5de-proxy/internal/hub"
|
|
"git.t5de.dev/t5de/t5de-proxy/internal/proxy"
|
|
)
|
|
|
|
// The inventory's Recently Added (api/recently_added_inventory.php?type=clothing|furni|scenes) is a list of product
|
|
// IDs from IMVU; T5 products the person added to their library lately go first, newest first.
|
|
|
|
// recentFor is how long a T5 product counts as recently added.
|
|
const recentFor = 30 * 24 * time.Hour
|
|
|
|
// IMVU's categories for furniture and rooms (the inventory's FURNI_CATEGORIES and ROOM_CATEGORIES, and rooms' 107).
|
|
var (
|
|
furniCategories = map[int64]bool{1027: true, 110: true}
|
|
roomCategories = map[int64]bool{107: true, 109: true, 366: true, 626: true, 2386: true}
|
|
)
|
|
|
|
// inventoryType is which Recently Added list a product belongs in: clothing (anything worn), furni or scenes.
|
|
func inventoryType(categories []hub.Category) string {
|
|
for _, c := range categories {
|
|
if furniCategories[c.ID] {
|
|
return "furni"
|
|
}
|
|
}
|
|
for _, c := range categories {
|
|
if roomCategories[c.ID] {
|
|
return "scenes"
|
|
}
|
|
}
|
|
return "clothing"
|
|
}
|
|
|
|
func (p *Products) interceptRecent(x *proxy.Exchange) error {
|
|
if p.Library == nil {
|
|
return nil
|
|
}
|
|
kind := x.URL.Query().Get("type")
|
|
x.ModifyResponse = func(resp *http.Response, body []byte) ([]byte, error) {
|
|
var ids []json.RawMessage
|
|
if resp.StatusCode != http.StatusOK || json.Unmarshal(body, &ids) != nil {
|
|
return body, nil
|
|
}
|
|
ours := p.recentLocals(kind, time.Now())
|
|
if len(ours) == 0 {
|
|
return body, nil
|
|
}
|
|
out := make([]json.RawMessage, 0, len(ours)+len(ids))
|
|
for _, local := range ours {
|
|
out = append(out, json.RawMessage(strconv.FormatInt(local, 10)))
|
|
}
|
|
return json.Marshal(append(out, ids...))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// recentLocals is the local IDs of T5 products of that kind added to the library within recentFor, newest first.
|
|
func (p *Products) recentLocals(kind string, now time.Time) []int64 {
|
|
lib := p.Library()
|
|
sort.SliceStable(lib, func(i, j int) bool { return lib[i].AddedAt.After(lib[j].AddedAt) })
|
|
var out []int64
|
|
for _, e := range lib {
|
|
if now.Sub(e.AddedAt) > recentFor || inventoryType(e.Product.Categories) != kind {
|
|
continue
|
|
}
|
|
if s := p.byProductID(e.Product.ID); s != nil && p.inHubDir(e.Product.ID) {
|
|
out = append(out, s.local)
|
|
}
|
|
}
|
|
return out
|
|
}
|