154 lines
4.3 KiB
Go
154 lines
4.3 KiB
Go
package cpnotice
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// cpApplication mirrors the user-service CP outbox payload without importing user-service internals.
|
|
type cpApplication struct {
|
|
ApplicationID string
|
|
RelationType string
|
|
Status string
|
|
Requester cpUser
|
|
Target cpUser
|
|
RoomID string
|
|
RoomRegionID int64
|
|
Gift cpGift
|
|
ExpiresAtMS int64
|
|
DecidedAtMS int64
|
|
}
|
|
|
|
type cpUser struct {
|
|
UserID int64
|
|
DisplayUserID string
|
|
Username string
|
|
Avatar string
|
|
}
|
|
|
|
type cpGift struct {
|
|
GiftID string
|
|
GiftName string
|
|
GiftIconURL string
|
|
GiftAnimationURL string
|
|
GiftCount int64
|
|
GiftValue int64
|
|
BillingReceiptID string
|
|
}
|
|
|
|
func decodeObject(value string) (map[string]any, error) {
|
|
var payload map[string]any
|
|
if err := json.Unmarshal([]byte(strings.TrimSpace(value)), &payload); err != nil {
|
|
return nil, fmt.Errorf("invalid cp payload json: %w", err)
|
|
}
|
|
if payload == nil {
|
|
payload = map[string]any{}
|
|
}
|
|
return payload, nil
|
|
}
|
|
|
|
func applicationFromObject(obj map[string]any) (cpApplication, error) {
|
|
if len(obj) == 0 {
|
|
return cpApplication{}, fmt.Errorf("cp application payload is missing")
|
|
}
|
|
app := cpApplication{
|
|
ApplicationID: stringValue(obj, "application_id", "ApplicationID"),
|
|
RelationType: stringValue(obj, "relation_type", "RelationType"),
|
|
Status: stringValue(obj, "status", "Status"),
|
|
Requester: userFromObject(firstObject(obj, "requester", "Requester")),
|
|
Target: userFromObject(firstObject(obj, "target", "Target")),
|
|
RoomID: stringValue(obj, "room_id", "RoomID"),
|
|
RoomRegionID: int64Value(obj, "room_region_id", "RoomRegionID"),
|
|
Gift: giftFromObject(firstObject(obj, "gift", "Gift")),
|
|
ExpiresAtMS: int64Value(obj, "expires_at_ms", "ExpiresAtMS"),
|
|
DecidedAtMS: int64Value(obj, "decided_at_ms", "DecidedAtMS"),
|
|
}
|
|
if app.ApplicationID == "" || app.RelationType == "" || app.Requester.UserID <= 0 || app.Target.UserID <= 0 {
|
|
return cpApplication{}, fmt.Errorf("cp application payload is incomplete")
|
|
}
|
|
return app, nil
|
|
}
|
|
|
|
func userFromObject(obj map[string]any) cpUser {
|
|
return cpUser{
|
|
UserID: int64Value(obj, "user_id", "UserID"),
|
|
DisplayUserID: stringValue(obj, "display_user_id", "DisplayUserID"),
|
|
Username: stringValue(obj, "username", "Username"),
|
|
Avatar: stringValue(obj, "avatar", "Avatar"),
|
|
}
|
|
}
|
|
|
|
func giftFromObject(obj map[string]any) cpGift {
|
|
return cpGift{
|
|
GiftID: stringValue(obj, "gift_id", "GiftID"),
|
|
GiftName: stringValue(obj, "gift_name", "GiftName"),
|
|
GiftIconURL: stringValue(obj, "gift_icon_url", "GiftIconURL"),
|
|
GiftAnimationURL: stringValue(obj, "gift_animation_url", "GiftAnimationURL"),
|
|
GiftCount: int64Value(obj, "gift_count", "GiftCount"),
|
|
GiftValue: int64Value(obj, "gift_value", "GiftValue"),
|
|
BillingReceiptID: stringValue(obj, "billing_receipt_id", "BillingReceiptID"),
|
|
}
|
|
}
|
|
|
|
func firstObject(obj map[string]any, keys ...string) map[string]any {
|
|
for _, key := range keys {
|
|
if value, ok := obj[key]; ok {
|
|
if typed, ok := value.(map[string]any); ok {
|
|
return typed
|
|
}
|
|
// json.Unmarshal into map can leave nested structs only as maps; this defensive re-marshal keeps tests and
|
|
// future typed adapters from breaking the parser if they pass a struct-like object.
|
|
body, err := json.Marshal(value)
|
|
if err == nil {
|
|
var nested map[string]any
|
|
if json.Unmarshal(body, &nested) == nil {
|
|
return nested
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return map[string]any{}
|
|
}
|
|
|
|
func stringValue(obj map[string]any, keys ...string) string {
|
|
for _, key := range keys {
|
|
switch value := obj[key].(type) {
|
|
case string:
|
|
return strings.TrimSpace(value)
|
|
case json.Number:
|
|
return value.String()
|
|
case float64:
|
|
if value == float64(int64(value)) {
|
|
return strconv.FormatInt(int64(value), 10)
|
|
}
|
|
case int64:
|
|
return strconv.FormatInt(value, 10)
|
|
case int:
|
|
return strconv.Itoa(value)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func int64Value(obj map[string]any, keys ...string) int64 {
|
|
for _, key := range keys {
|
|
switch value := obj[key].(type) {
|
|
case float64:
|
|
return int64(value)
|
|
case int64:
|
|
return value
|
|
case int:
|
|
return int64(value)
|
|
case json.Number:
|
|
parsed, _ := value.Int64()
|
|
return parsed
|
|
case string:
|
|
parsed, _ := strconv.ParseInt(strings.TrimSpace(value), 10, 64)
|
|
return parsed
|
|
}
|
|
}
|
|
return 0
|
|
}
|