chore: backfill voice logs and fix timezone handling#262
Conversation
Paginates a Discord channel to reimport voice join/left events from a bot (Dyno). Includes rate limit handling, duplicate detection, and progress display via Laravel Prompts.
Change now() to now()->utc() in PersistVoiceStateAction to ensure gateway voice events use UTC, matching the ETL/Dyno source timestamps.
Convert from timestamp without timezone to timestamptz, treating existing values as UTC. Enables correct timezone-aware queries.
After migrating to timestamptz, the double AT TIME ZONE cast caused a +3h shift. Use single cast to America/Sao_Paulo since PostgreSQL now handles the UTC→local conversion automatically.
Add BackfillVoiceLogsCommand to the module's service provider.
7669616 to
6fe29dd
Compare
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Repository YAML (base), Central YAML (inherited) Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
## Summary
- Adds `app.display_timezone` config key (`DISPLAY_TIMEZONE` env,
default `America/Sao_Paulo`) to centralize the user-facing timezone
- Changes `app.timezone` default from `America/Sao_Paulo` to `UTC`
- Replaces all 15 hardcoded `'America/Sao_Paulo'` occurrences across 15
files with `config('app.display_timezone')`
- Uses parameterized SQL bindings (`?`) for `AT TIME ZONE` clauses
instead of string interpolation
- Adds `AT TIME ZONE` conversion to moderation heatmap SQL that was
missing it entirely
- Converts all user-facing timestamp displays (SLA deadlines, connection
dates, schedule cards) to use the display timezone
## Modules affected
- `config/app.php` — new config key
- `bot-discord` — GreetingsEvent
- `he4rt` — heatmap highlight + schedule-card
- `panel-admin` — marketing dashboard queries (6 files) + moderation
heatmap + appeal views
- `integration-discord` — BackfillVoiceLogsCommand CLI output
- `routes/console.php` — backup:monitor schedule
- `resources/views` — connection-hub
## Test plan
- [x] Rector dry-run — no suggestions
- [x] Pint — passed
- [x] PHPStan — 0 errors
- [x] Tests — 497 passed, 3 skipped
- [x] Verified `AT TIME ZONE ?` binding works via tinker
- [ ] Verify heatmaps render correctly in the admin panel
- [ ] Verify SLA deadline times display in Brazil timezone
- [ ] Verify schedule-card shows correct local times
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Description
Centralizes the user-facing timezone by adding app.display_timezone (env
DISPLAY_TIMEZONE, default America/Sao_Paulo) and switching the
application default timezone to UTC. Replaces hard-coded
America/Sao_Paulo occurrences with config('app.display_timezone'),
parameterizes SQL AT TIME ZONE bindings, fixes a missing AT TIME ZONE in
the moderation heatmap, and converts user-facing timestamp displays to
the new display timezone.
## References
- PR: #262
- PR: #260
- PostgreSQL AT TIME ZONE docs:
https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-TIMEZONES
## Dependencies & Requirements
- New config key: app.display_timezone (env: DISPLAY_TIMEZONE, default:
America/Sao_Paulo)
- app.timezone default changed to UTC (env: APP_TIMEZONE)
- .env.example updated with APP_TIMEZONE=UTC and
DISPLAY_TIMEZONE=America/Sao_Paulo
- No new composer/npm packages added
- Database must accept parameterized AT TIME ZONE bindings (validated
via tinker)
- Manual verifications pending: admin heatmaps rendering, SLA deadline
display, schedule-card local times
## Contributor Summary
| Contributor | Lines Added | Lines Removed | Files Changed |
|---|---:|---:|---:|
| gvieira18 | 63 | 45 | 15 |
## Changes Summary
| File Path | Change Description |
|---|---|
| config/app.php | Added display_timezone key; changed default
app.timezone to UTC |
| .env.example | Added APP_TIMEZONE and DISPLAY_TIMEZONE entries |
| app-modules/bot-discord/src/Events/GreetingsEvent.php | Use
display_timezone for greeting-hour calculation |
|
app-modules/he4rt/resources/views/components/dashboard/heatmap.blade.php
| Heatmap current-time highlight uses display_timezone |
| app-modules/he4rt/resources/views/components/schedule-card.blade.php |
Normalize schedule timestamps to display_timezone |
|
app-modules/integration-discord/src/ETL/Console/BackfillVoiceLogsCommand.php
| Format CLI log timestamps with display_timezone |
|
app-modules/panel-admin/resources/views/moderation/appeal-queue/appeal-detail.blade.php
| SLA deadline displays converted to display_timezone with null fallback
|
|
app-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/ActivityPerDay.php
| Use display_timezone and bind into SQL AT TIME ZONE |
|
app-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/MessageHeatmap.php
| Parameterized timezone bindings for day/hour extraction |
|
app-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/VoiceHeatmap.php
| Parameterized timezone bindings in voice heatmap query |
|
app-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/VoicePerDay.php
| Day-extraction query updated to use display_timezone |
|
app-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/PeriodStats.php
| Compute subdivisions using display_timezone |
|
app-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/TopChannels.php
| Date range calculations use display_timezone |
| app-modules/panel-admin/src/Marketing/Pages/MeetingShowcasePage.php |
Parse meeting bounds with display_timezone and convert to UTC |
|
app-modules/panel-admin/src/Moderation/Livewire/ModerationDashboardLivewire.php
| Added AT TIME ZONE conversion and parameterized bindings for
moderation heatmap |
| resources/views/livewire/connection-hub.blade.php | "Connected at"
timestamp uses display_timezone; props formatting |
| routes/console.php | Schedule backup:monitor to run at 09:00 in
display_timezone |
|
database/migrations/2026_05_20_160249_alter_messages_sent_at_to_timestamptz.php
| Migration: convert messages.sent_at to timestamptz interpreting
existing values as UTC |
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/he4rt/heartdevs.com/pull/264?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: danielhe4rt <danielhe4rt@gmail.com>
Summary
discord:backfill-voice): paginates a Discord channel to reimport voice join/left events from Dyno bot, with rate limit handling, duplicate detection, and progress display via Laravel PromptsPersistVoiceStateActionnow usesnow()->utc()instead ofnow()so gateway voice events match ETL timestampstimestamptzmigration: convertsvoice_messages.occurred_atfromtimestamptotimestamptz(treating existing values as UTC)AT TIME ZONEcast inVoiceHeatmapandVoicePerDaythat caused a +3h shift after thetimestamptzmigrationTest plan
php artisan discord:backfill-voice <channel_id> --dry-runto verify pagination and duplicate detectionphp artisan discord:backfill-voice <channel_id> --since=2026-04-01 --until=2026-05-01to backfill April gapVoicePerDaydaily counts match raw SQL queries