From 51c00f2a75110b446f2d13d4678b8074936fa703 Mon Sep 17 00:00:00 2001 From: messypy Date: Tue, 28 Jul 2026 00:37:56 +0900 Subject: [PATCH] Fallback history export to VRChat logs --- cmd/vrc_osc_gui/history_view_windows.go | 67 +++++++++++++++++++++++-- cmd/vrc_osc_gui/window_windows.go | 2 +- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/cmd/vrc_osc_gui/history_view_windows.go b/cmd/vrc_osc_gui/history_view_windows.go index 2869f60..5b9e008 100644 --- a/cmd/vrc_osc_gui/history_view_windows.go +++ b/cmd/vrc_osc_gui/history_view_windows.go @@ -342,6 +342,45 @@ func historyEventsFromSnapshot() []historyEventRecord { return out } +func historyEventsWithFallback() []historyEventRecord { + events := historyEventsFromSnapshot() + if len(events) > 0 { + return events + } + users, ok := currentUsersFromVRChatLog() + if !ok || len(users) == 0 { + return nil + } + out := make([]historyEventRecord, 0, len(users)) + for _, user := range users { + name := strings.TrimSpace(user.Name) + if name == "" { + continue + } + if !user.LastJoin.IsZero() { + out = append(out, historyEventRecord{ + At: user.LastJoin, + Kind: "join", + Name: name, + }) + } + if !user.Present && !user.LastLeave.IsZero() { + out = append(out, historyEventRecord{ + At: user.LastLeave, + Kind: "leave", + Name: name, + }) + } + } + sort.SliceStable(out, func(i, j int) bool { + if out[i].At.Equal(out[j].At) { + return strings.ToLower(out[i].Name) < strings.ToLower(out[j].Name) + } + return out[i].At.Before(out[j].At) + }) + return out +} + func decodeHistoryEventsSnapshot(b []byte) []historyEventRecord { type snapshot struct { UpdatedAt time.Time `json:"updated_at"` @@ -408,7 +447,7 @@ func historyVisitsForDay(app *guiApp, day time.Time) []historyDetailRow { if !historyInQueryRange(day, q) { return nil } - events := historyEventsFromSnapshot() + events := historyEventsWithFallback() visits := historyVisitSource() return historyVisitsForDayFromData(app, day, visits, events, q) } @@ -502,7 +541,7 @@ func historyCalendarCellsForMonth(app *guiApp) []historyCalendarCell { monthStart = time.Now() } monthStart = time.Date(monthStart.Year(), monthStart.Month(), 1, 0, 0, 0, 0, monthStart.Location()) - events := historyEventsFromSnapshot() + events := historyEventsWithFallback() visits := historyVisitSource() return historyCalendarCellsForMonthFromData(app, monthStart, visits, events, q) } @@ -591,7 +630,7 @@ func exportHistoryDay(app *guiApp, day time.Time, selected map[string]bool) (str } func exportHistoryDayFromRequest(req historyExportRequest) (string, error) { - events := historyEventsFromSnapshot() + events := historyEventsWithFallback() visits := historyVisitSource() rows := append([]historyDetailRow(nil), req.Rows...) if len(rows) == 0 { @@ -795,7 +834,7 @@ func appendExportGuestSnapshot(b *strings.Builder, day time.Time, q historyQuery if b == nil { return } - guests := readGuestSnapshot() + guests := guestSnapshotForExport() dayStart, dayEnd := historyDayBounds(day) type guestLine struct { name string @@ -848,6 +887,24 @@ func appendExportGuestSnapshot(b *strings.Builder, day time.Time, q historyQuery } } +func guestSnapshotForExport() []userState { + guests := readGuestSnapshot() + if len(guests) > 0 { + out := make([]userState, 0, len(guests)) + for _, guest := range guests { + out = append(out, userState{ + Name: guest.Name, + Present: guest.Present, + LastJoin: guest.LastJoin, + LastLeave: guest.LastLeave, + }) + } + return out + } + users, _ := currentUsersFromJoinLeave() + return users +} + func cloneHistorySelection(selected map[string]bool) map[string]bool { if len(selected) == 0 { return nil @@ -887,7 +944,7 @@ func refreshHistoryCache(app *guiApp, force bool) bool { if !force && app.historyCacheLoaded && !app.historyReloadPending { return false } - events := historyEventsFromSnapshot() + events := historyEventsWithFallback() visits := historyVisitSource() q := historyQueryFromApp(app) app.historyRows = historyRows() diff --git a/cmd/vrc_osc_gui/window_windows.go b/cmd/vrc_osc_gui/window_windows.go index e706728..d257555 100644 --- a/cmd/vrc_osc_gui/window_windows.go +++ b/cmd/vrc_osc_gui/window_windows.go @@ -2946,7 +2946,7 @@ func eventRows(users []userState) []guiEventRow { } func joinLeaveRowsForCurrentWorld(app *guiApp) []guiEventRow { - events := historyEventsFromSnapshot() + events := historyEventsWithFallback() if len(events) == 0 { users, _ := currentUsersFromJoinLeave() return eventRows(users)