681 lines
18 KiB
Go
681 lines
18 KiB
Go
// backend\myfreecams_biocontext.go
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const myfreecamsBioContextURLFmt = "https://api-edge.myfreecams.com/usernameLookup/%s"
|
|
|
|
// getrennt von Chaturbate, damit die Datei unabhängig bleibt
|
|
const myfreecamsBioCacheMaxAge = 10 * time.Minute
|
|
|
|
type MyFreeCamsBioContextResp struct {
|
|
Enabled bool `json:"enabled"`
|
|
FetchedAt time.Time `json:"fetchedAt"`
|
|
LastError string `json:"lastError,omitempty"`
|
|
Model string `json:"model"`
|
|
Bio any `json:"bio,omitempty"`
|
|
|
|
RoomStatus string `json:"roomStatus,omitempty"` // public/private/offline/unknown
|
|
IsOnline bool `json:"isOnline,omitempty"`
|
|
ChatRoomURL string `json:"chatRoomUrl,omitempty"`
|
|
ImageURL string `json:"imageUrl,omitempty"`
|
|
|
|
AvatarURL string `json:"avatarUrl,omitempty"`
|
|
SnapURL string `json:"snapUrl,omitempty"`
|
|
RoomTopic string `json:"roomTopic,omitempty"`
|
|
Country string `json:"country,omitempty"`
|
|
Gender string `json:"gender,omitempty"`
|
|
}
|
|
|
|
type mfcBioLookupResp struct {
|
|
Result struct {
|
|
Success int `json:"success"`
|
|
Message string `json:"message"`
|
|
|
|
User struct {
|
|
ID int `json:"id"`
|
|
UserID int `json:"user_id"`
|
|
Username string `json:"username"`
|
|
Avatar mfcMaybeString `json:"avatar"`
|
|
VS any `json:"vs"`
|
|
|
|
Sessions []struct {
|
|
SessionID int `json:"session_id"`
|
|
VState int `json:"vstate"`
|
|
RoomCount int `json:"room_count"`
|
|
RoomTopic string `json:"room_topic"`
|
|
ServerName string `json:"server_name"`
|
|
VideoServerType string `json:"video_server_type"`
|
|
Phase string `json:"phase"`
|
|
SnapURL string `json:"snap_url"`
|
|
} `json:"sessions"`
|
|
|
|
Profile struct {
|
|
Age string `json:"age"`
|
|
Gender string `json:"gender"`
|
|
City string `json:"city"`
|
|
Country string `json:"country"`
|
|
Birthdate string `json:"birthdate"`
|
|
SexualPreference string `json:"sexual_preference"`
|
|
MaritalStatus string `json:"marital_status"`
|
|
Ethnicity string `json:"ethnicity"`
|
|
Hair string `json:"hair"`
|
|
Eyes string `json:"eyes"`
|
|
Weight string `json:"weight"`
|
|
WeightUnits string `json:"weight_units"`
|
|
Height string `json:"height"`
|
|
HeightUnits string `json:"height_units"`
|
|
BodyType string `json:"body_type"`
|
|
Smoke string `json:"smoke"`
|
|
Drink string `json:"drink"`
|
|
Drugs string `json:"drugs"`
|
|
Blurb string `json:"blurb"`
|
|
} `json:"profile"`
|
|
|
|
Tags []string `json:"tags"`
|
|
} `json:"user"`
|
|
} `json:"result"`
|
|
|
|
Err int `json:"err"`
|
|
}
|
|
|
|
func normalizeMFCBioRoomStatus(vstate int, hasSession bool) string {
|
|
if !hasSession {
|
|
return "offline"
|
|
}
|
|
|
|
// Deine Beispiele:
|
|
// vstate=0 => public
|
|
// vstate=14 => private show
|
|
if vstate == 0 {
|
|
return "public"
|
|
}
|
|
|
|
return "private"
|
|
}
|
|
|
|
func normalizeMFCBioImageURL(raw string) string {
|
|
raw = strings.TrimSpace(raw)
|
|
if raw == "" {
|
|
return ""
|
|
}
|
|
|
|
if strings.HasPrefix(raw, "//") {
|
|
raw = "https:" + raw
|
|
} else if strings.HasPrefix(raw, "/") {
|
|
raw = "https://snap.mfcimg.com" + raw
|
|
} else if !strings.HasPrefix(raw, "http://") && !strings.HasPrefix(raw, "https://") {
|
|
raw = "https://snap.mfcimg.com/" + strings.TrimLeft(raw, "/")
|
|
}
|
|
|
|
u, err := url.Parse(raw)
|
|
if err != nil || u.Scheme == "" || u.Host == "" {
|
|
return raw
|
|
}
|
|
|
|
q := u.Query()
|
|
q.Set("no-cache", strconv.FormatInt(time.Now().UnixNano(), 10))
|
|
u.RawQuery = q.Encode()
|
|
|
|
return u.String()
|
|
}
|
|
|
|
func mfcBioDigitsOnly(s string) string {
|
|
var b strings.Builder
|
|
for _, r := range strings.TrimSpace(s) {
|
|
if r >= '0' && r <= '9' {
|
|
b.WriteRune(r)
|
|
}
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func buildMFCBioSnapimgURL(serverName string, rawUserID int) string {
|
|
serverID := mfcBioDigitsOnly(serverName)
|
|
if serverID == "" || rawUserID <= 0 {
|
|
return ""
|
|
}
|
|
|
|
return normalizeMFCBioImageURL(fmt.Sprintf(
|
|
"https://snap.mfcimg.com/snapimg/%s/853x480/mfc_%d",
|
|
serverID,
|
|
rawUserID,
|
|
))
|
|
}
|
|
|
|
func selectMFCBioImageURL(data mfcBioLookupResp) (imageURL string, avatarURL string, snapURL string) {
|
|
user := data.Result.User
|
|
|
|
avatarURL = normalizeMFCBioImageURL(user.Avatar.String())
|
|
|
|
if len(user.Sessions) > 0 {
|
|
sess := user.Sessions[0]
|
|
|
|
// bevorzugt die stabile snapimg-URL mit echter User-ID
|
|
snapURL = buildMFCBioSnapimgURL(sess.ServerName, user.ID)
|
|
|
|
// fallback: API snap_url
|
|
if strings.TrimSpace(snapURL) == "" {
|
|
snapURL = normalizeMFCBioImageURL(sess.SnapURL)
|
|
}
|
|
}
|
|
|
|
imageURL = strings.TrimSpace(snapURL)
|
|
if imageURL == "" {
|
|
imageURL = strings.TrimSpace(avatarURL)
|
|
}
|
|
|
|
return imageURL, avatarURL, snapURL
|
|
}
|
|
|
|
func inferRoomStatusFromMFCBio(data mfcBioLookupResp) (
|
|
roomStatus string,
|
|
isOnline bool,
|
|
chatRoomURL string,
|
|
imageURL string,
|
|
avatarURL string,
|
|
snapURL string,
|
|
roomTopic string,
|
|
country string,
|
|
gender string,
|
|
) {
|
|
user := data.Result.User
|
|
|
|
hasSession := len(user.Sessions) > 0
|
|
vstate := 0
|
|
if hasSession {
|
|
vstate = user.Sessions[0].VState
|
|
roomTopic = strings.TrimSpace(user.Sessions[0].RoomTopic)
|
|
}
|
|
|
|
roomStatus = normalizeMFCBioRoomStatus(vstate, hasSession)
|
|
isOnline = hasSession
|
|
chatRoomURL = "https://www.myfreecams.com/#" + strings.TrimSpace(user.Username)
|
|
|
|
imageURL, avatarURL, snapURL = selectMFCBioImageURL(data)
|
|
|
|
country = strings.TrimSpace(user.Profile.Country)
|
|
gender = strings.TrimSpace(user.Profile.Gender)
|
|
|
|
return
|
|
}
|
|
|
|
func myfreecamsBioContextHandler(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, "Nur GET erlaubt", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
enabled := true
|
|
|
|
model := sanitizeModelKey(r.URL.Query().Get("model"))
|
|
if model == "" {
|
|
http.Error(w, "model fehlt", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
refresh := strings.TrimSpace(r.URL.Query().Get("refresh"))
|
|
forceRefresh := refresh == "1" || strings.EqualFold(refresh, "true")
|
|
|
|
const host = "myfreecams.com"
|
|
|
|
var (
|
|
cachedBio any
|
|
cachedAt time.Time
|
|
hasCache bool
|
|
)
|
|
|
|
store := getModelStore()
|
|
if store != nil {
|
|
raw, ts, ok, err := store.GetBioContext(host, model)
|
|
if err == nil && ok {
|
|
var tmp any
|
|
if e := json.Unmarshal([]byte(raw), &tmp); e == nil {
|
|
cachedBio = tmp
|
|
hasCache = true
|
|
if t, e2 := time.Parse(time.RFC3339Nano, strings.TrimSpace(ts)); e2 == nil {
|
|
cachedAt = t
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if hasCache && !forceRefresh && !cachedAt.IsZero() && time.Since(cachedAt) < myfreecamsBioCacheMaxAge {
|
|
roomStatus := "unknown"
|
|
isOnline := false
|
|
chatRoomURL := "https://www.myfreecams.com/#" + model
|
|
imageURL := ""
|
|
|
|
// Cache ist generisches JSON; erneut typisieren, damit Status/Bild sauber extrahiert werden.
|
|
b, _ := json.Marshal(cachedBio)
|
|
var parsed mfcBioLookupResp
|
|
if json.Unmarshal(b, &parsed) == nil {
|
|
var avatarURL, snapURL, roomTopic, country, gender string
|
|
roomStatus, isOnline, chatRoomURL, imageURL, avatarURL, snapURL, roomTopic, country, gender =
|
|
inferRoomStatusFromMFCBio(parsed)
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: enabled,
|
|
FetchedAt: cachedAt,
|
|
Model: model,
|
|
Bio: cachedBio,
|
|
RoomStatus: roomStatus,
|
|
IsOnline: isOnline,
|
|
ChatRoomURL: chatRoomURL,
|
|
ImageURL: imageURL,
|
|
AvatarURL: avatarURL,
|
|
SnapURL: snapURL,
|
|
RoomTopic: roomTopic,
|
|
Country: country,
|
|
Gender: gender,
|
|
})
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: enabled,
|
|
FetchedAt: cachedAt,
|
|
Model: model,
|
|
Bio: cachedBio,
|
|
RoomStatus: roomStatus,
|
|
IsOnline: isOnline,
|
|
ChatRoomURL: chatRoomURL,
|
|
ImageURL: imageURL,
|
|
})
|
|
return
|
|
}
|
|
|
|
if !myFreeCamsProviderAPIEnabled() {
|
|
errMsg := "MyFreeCams API ist in den Einstellungen deaktiviert"
|
|
|
|
if hasCache {
|
|
roomStatus := "unknown"
|
|
isOnline := false
|
|
chatRoomURL := "https://www.myfreecams.com/#" + model
|
|
imageURL := ""
|
|
avatarURL := ""
|
|
snapURL := ""
|
|
roomTopic := ""
|
|
country := ""
|
|
gender := ""
|
|
|
|
b, _ := json.Marshal(cachedBio)
|
|
var cachedParsed mfcBioLookupResp
|
|
if json.Unmarshal(b, &cachedParsed) == nil {
|
|
roomStatus, isOnline, chatRoomURL, imageURL, avatarURL, snapURL, roomTopic, country, gender =
|
|
inferRoomStatusFromMFCBio(cachedParsed)
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: false,
|
|
FetchedAt: cachedAt,
|
|
LastError: errMsg,
|
|
Model: model,
|
|
Bio: cachedBio,
|
|
RoomStatus: roomStatus,
|
|
IsOnline: isOnline,
|
|
ChatRoomURL: chatRoomURL,
|
|
ImageURL: imageURL,
|
|
AvatarURL: avatarURL,
|
|
SnapURL: snapURL,
|
|
RoomTopic: roomTopic,
|
|
Country: country,
|
|
Gender: gender,
|
|
})
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: false,
|
|
FetchedAt: time.Now().UTC(),
|
|
LastError: errMsg,
|
|
Model: model,
|
|
})
|
|
return
|
|
}
|
|
|
|
// Manueller Refresh darf einmal testen, ob MFC wieder frei ist.
|
|
if forceRefresh {
|
|
clearMFCLookupPause("manual biocontext refresh for " + model)
|
|
} else if err := mfcLookupPausedError(); err != nil {
|
|
errMsg := err.Error()
|
|
|
|
if hasCache {
|
|
roomStatus := "unknown"
|
|
isOnline := false
|
|
chatRoomURL := "https://www.myfreecams.com/#" + model
|
|
imageURL := ""
|
|
avatarURL := ""
|
|
snapURL := ""
|
|
roomTopic := ""
|
|
country := ""
|
|
gender := ""
|
|
|
|
b, _ := json.Marshal(cachedBio)
|
|
var cachedParsed mfcBioLookupResp
|
|
if json.Unmarshal(b, &cachedParsed) == nil {
|
|
roomStatus, isOnline, chatRoomURL, imageURL, avatarURL, snapURL, roomTopic, country, gender =
|
|
inferRoomStatusFromMFCBio(cachedParsed)
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: enabled,
|
|
FetchedAt: cachedAt,
|
|
LastError: errMsg,
|
|
Model: model,
|
|
Bio: cachedBio,
|
|
RoomStatus: roomStatus,
|
|
IsOnline: isOnline,
|
|
ChatRoomURL: chatRoomURL,
|
|
ImageURL: imageURL,
|
|
AvatarURL: avatarURL,
|
|
SnapURL: snapURL,
|
|
RoomTopic: roomTopic,
|
|
Country: country,
|
|
Gender: gender,
|
|
})
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: enabled,
|
|
FetchedAt: time.Now().UTC(),
|
|
LastError: errMsg,
|
|
Model: model,
|
|
})
|
|
return
|
|
}
|
|
|
|
if !myFreeCamsProviderAPIEnabled() {
|
|
errMsg := "MyFreeCams API ist in den Einstellungen deaktiviert"
|
|
|
|
if hasCache {
|
|
roomStatus := "unknown"
|
|
isOnline := false
|
|
chatRoomURL := "https://www.myfreecams.com/#" + model
|
|
imageURL := ""
|
|
avatarURL := ""
|
|
snapURL := ""
|
|
roomTopic := ""
|
|
country := ""
|
|
gender := ""
|
|
|
|
b, _ := json.Marshal(cachedBio)
|
|
var cachedParsed mfcBioLookupResp
|
|
if json.Unmarshal(b, &cachedParsed) == nil {
|
|
roomStatus, isOnline, chatRoomURL, imageURL, avatarURL, snapURL, roomTopic, country, gender =
|
|
inferRoomStatusFromMFCBio(cachedParsed)
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: false,
|
|
FetchedAt: cachedAt,
|
|
LastError: errMsg,
|
|
Model: model,
|
|
Bio: cachedBio,
|
|
RoomStatus: roomStatus,
|
|
IsOnline: isOnline,
|
|
ChatRoomURL: chatRoomURL,
|
|
ImageURL: imageURL,
|
|
AvatarURL: avatarURL,
|
|
SnapURL: snapURL,
|
|
RoomTopic: roomTopic,
|
|
Country: country,
|
|
Gender: gender,
|
|
})
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: false,
|
|
FetchedAt: time.Now().UTC(),
|
|
LastError: errMsg,
|
|
Model: model,
|
|
})
|
|
return
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(r.Context(), 12*time.Second)
|
|
defer cancel()
|
|
|
|
upstreamURL := fmt.Sprintf(myfreecamsBioContextURLFmt, url.PathEscape(model))
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, upstreamURL, nil)
|
|
if err != nil {
|
|
http.Error(w, "request build failed: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
|
|
req.Header.Set("Accept", "application/json, text/plain, */*")
|
|
req.Header.Set("Referer", "https://www.myfreecams.com/#"+model)
|
|
req.Header.Set("Origin", "https://www.myfreecams.com")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
if hasCache {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: enabled,
|
|
FetchedAt: cachedAt,
|
|
LastError: err.Error(),
|
|
Model: model,
|
|
Bio: cachedBio,
|
|
})
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: enabled,
|
|
FetchedAt: time.Now().UTC(),
|
|
LastError: err.Error(),
|
|
Model: model,
|
|
})
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
raw, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
|
|
|
|
snip := strings.TrimSpace(string(raw))
|
|
if len(snip) > 400 {
|
|
snip = snip[:400] + "…"
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
errMsg := fmt.Sprintf("HTTP %d (ct=%s): %s", resp.StatusCode, resp.Header.Get("Content-Type"), snip)
|
|
|
|
if hasCache {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: enabled,
|
|
FetchedAt: cachedAt,
|
|
LastError: errMsg,
|
|
Model: model,
|
|
Bio: cachedBio,
|
|
})
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: enabled,
|
|
FetchedAt: time.Now().UTC(),
|
|
LastError: errMsg,
|
|
Model: model,
|
|
})
|
|
return
|
|
}
|
|
|
|
var parsed mfcBioLookupResp
|
|
if err := json.Unmarshal(raw, &parsed); err != nil {
|
|
errMsg := fmt.Sprintf("invalid json from MFC usernameLookup (ct=%s): %v; body: %s", resp.Header.Get("Content-Type"), err, snip)
|
|
|
|
if hasCache {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: enabled,
|
|
FetchedAt: cachedAt,
|
|
LastError: errMsg,
|
|
Model: model,
|
|
Bio: cachedBio,
|
|
})
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: enabled,
|
|
FetchedAt: time.Now().UTC(),
|
|
LastError: errMsg,
|
|
Model: model,
|
|
})
|
|
return
|
|
}
|
|
|
|
if parsed.Result.Success == 0 || parsed.Result.User.ID <= 0 {
|
|
msg := strings.TrimSpace(parsed.Result.Message)
|
|
if msg == "" {
|
|
msg = "usernameLookup failed"
|
|
}
|
|
|
|
errMsg := fmt.Sprintf("mfc usernameLookup: %s", msg)
|
|
|
|
if isMFCServersBusyMessage(msg) {
|
|
pauseMFCLookups(msg, mfcLookupBusyCooldown)
|
|
}
|
|
|
|
// Wichtig: Busy-Antwort NICHT in den Store schreiben.
|
|
if hasCache {
|
|
roomStatus := "unknown"
|
|
isOnline := false
|
|
chatRoomURL := "https://www.myfreecams.com/#" + model
|
|
imageURL := ""
|
|
avatarURL := ""
|
|
snapURL := ""
|
|
roomTopic := ""
|
|
country := ""
|
|
gender := ""
|
|
|
|
b, _ := json.Marshal(cachedBio)
|
|
var cachedParsed mfcBioLookupResp
|
|
if json.Unmarshal(b, &cachedParsed) == nil {
|
|
roomStatus, isOnline, chatRoomURL, imageURL, avatarURL, snapURL, roomTopic, country, gender =
|
|
inferRoomStatusFromMFCBio(cachedParsed)
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: enabled,
|
|
FetchedAt: cachedAt,
|
|
LastError: errMsg,
|
|
Model: model,
|
|
Bio: cachedBio,
|
|
RoomStatus: roomStatus,
|
|
IsOnline: isOnline,
|
|
ChatRoomURL: chatRoomURL,
|
|
ImageURL: imageURL,
|
|
AvatarURL: avatarURL,
|
|
SnapURL: snapURL,
|
|
RoomTopic: roomTopic,
|
|
Country: country,
|
|
Gender: gender,
|
|
})
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: enabled,
|
|
FetchedAt: time.Now().UTC(),
|
|
LastError: errMsg,
|
|
Model: model,
|
|
})
|
|
return
|
|
}
|
|
|
|
var bio any
|
|
if err := json.Unmarshal(raw, &bio); err != nil {
|
|
bio = parsed
|
|
}
|
|
|
|
fetchedAt := time.Now().UTC()
|
|
|
|
roomStatus, isOnline, chatRoomURL, imageURL, avatarURL, snapURL, roomTopic, country, gender :=
|
|
inferRoomStatusFromMFCBio(parsed)
|
|
|
|
if store != nil {
|
|
_ = store.SetBioContext(host, model, string(raw), fetchedAt.Format(time.RFC3339Nano))
|
|
|
|
// Der Methodenname ist historisch, funktioniert aber mit beliebigem host.
|
|
_ = store.SetChaturbateRoomState(
|
|
host,
|
|
model,
|
|
roomStatus,
|
|
isOnline,
|
|
chatRoomURL,
|
|
imageURL,
|
|
fetchedAt,
|
|
)
|
|
|
|
if imageURL != "" {
|
|
_ = store.SetProfileImageURLOnly(host, model, imageURL, fetchedAt.Format(time.RFC3339Nano))
|
|
}
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(MyFreeCamsBioContextResp{
|
|
Enabled: enabled,
|
|
FetchedAt: fetchedAt,
|
|
LastError: "",
|
|
Model: model,
|
|
Bio: bio,
|
|
RoomStatus: roomStatus,
|
|
IsOnline: isOnline,
|
|
ChatRoomURL: chatRoomURL,
|
|
ImageURL: imageURL,
|
|
AvatarURL: avatarURL,
|
|
SnapURL: snapURL,
|
|
RoomTopic: roomTopic,
|
|
Country: country,
|
|
Gender: gender,
|
|
})
|
|
}
|