@atproto/ozone 0.1.172 → 0.1.174
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +40 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/moderation/emitEvent.d.ts.map +1 -1
- package/dist/api/moderation/emitEvent.js +31 -0
- package/dist/api/moderation/emitEvent.js.map +1 -1
- package/dist/api/queue/assignModerator.d.ts +4 -0
- package/dist/api/queue/assignModerator.d.ts.map +1 -0
- package/dist/api/queue/assignModerator.js +28 -0
- package/dist/api/queue/assignModerator.js.map +1 -0
- package/dist/api/queue/createQueue.d.ts +4 -0
- package/dist/api/queue/createQueue.d.ts.map +1 -0
- package/dist/api/queue/createQueue.js +44 -0
- package/dist/api/queue/createQueue.js.map +1 -0
- package/dist/api/queue/deleteQueue.d.ts +4 -0
- package/dist/api/queue/deleteQueue.d.ts.map +1 -0
- package/dist/api/queue/deleteQueue.js +40 -0
- package/dist/api/queue/deleteQueue.js.map +1 -0
- package/dist/api/queue/getAssignments.d.ts +4 -0
- package/dist/api/queue/getAssignments.d.ts.map +1 -0
- package/dist/api/queue/getAssignments.js +19 -0
- package/dist/api/queue/getAssignments.js.map +1 -0
- package/dist/api/queue/listQueues.d.ts +4 -0
- package/dist/api/queue/listQueues.d.ts.map +1 -0
- package/dist/api/queue/listQueues.js +29 -0
- package/dist/api/queue/listQueues.js.map +1 -0
- package/dist/api/queue/routeReports.d.ts +4 -0
- package/dist/api/queue/routeReports.d.ts.map +1 -0
- package/dist/api/queue/routeReports.js +33 -0
- package/dist/api/queue/routeReports.js.map +1 -0
- package/dist/api/queue/unassignModerator.d.ts +4 -0
- package/dist/api/queue/unassignModerator.d.ts.map +1 -0
- package/dist/api/queue/unassignModerator.js +24 -0
- package/dist/api/queue/unassignModerator.js.map +1 -0
- package/dist/api/queue/updateQueue.d.ts +4 -0
- package/dist/api/queue/updateQueue.d.ts.map +1 -0
- package/dist/api/queue/updateQueue.js +39 -0
- package/dist/api/queue/updateQueue.js.map +1 -0
- package/dist/api/report/assignModerator.d.ts +4 -0
- package/dist/api/report/assignModerator.d.ts.map +1 -0
- package/dist/api/report/assignModerator.js +33 -0
- package/dist/api/report/assignModerator.js.map +1 -0
- package/dist/api/report/createActivity.d.ts +4 -0
- package/dist/api/report/createActivity.d.ts.map +1 -0
- package/dist/api/report/createActivity.js +44 -0
- package/dist/api/report/createActivity.js.map +1 -0
- package/dist/api/report/getAssignments.d.ts +4 -0
- package/dist/api/report/getAssignments.d.ts.map +1 -0
- package/dist/api/report/getAssignments.js +19 -0
- package/dist/api/report/getAssignments.js.map +1 -0
- package/dist/api/report/getHistoricalStats.d.ts +4 -0
- package/dist/api/report/getHistoricalStats.d.ts.map +1 -0
- package/dist/api/report/getHistoricalStats.js +32 -0
- package/dist/api/report/getHistoricalStats.js.map +1 -0
- package/dist/api/report/getLatestReport.d.ts +4 -0
- package/dist/api/report/getLatestReport.d.ts.map +1 -0
- package/dist/api/report/getLatestReport.js +31 -0
- package/dist/api/report/getLatestReport.js.map +1 -0
- package/dist/api/report/getLiveStats.d.ts +4 -0
- package/dist/api/report/getLiveStats.d.ts.map +1 -0
- package/dist/api/report/getLiveStats.js +25 -0
- package/dist/api/report/getLiveStats.js.map +1 -0
- package/dist/api/report/getReport.d.ts +4 -0
- package/dist/api/report/getReport.d.ts.map +1 -0
- package/dist/api/report/getReport.js +35 -0
- package/dist/api/report/getReport.js.map +1 -0
- package/dist/api/report/listActivities.d.ts +4 -0
- package/dist/api/report/listActivities.d.ts.map +1 -0
- package/dist/api/report/listActivities.js +25 -0
- package/dist/api/report/listActivities.js.map +1 -0
- package/dist/api/report/queryReports.d.ts +4 -0
- package/dist/api/report/queryReports.d.ts.map +1 -0
- package/dist/api/report/queryReports.js +29 -0
- package/dist/api/report/queryReports.js.map +1 -0
- package/dist/api/report/reassignQueue.d.ts +4 -0
- package/dist/api/report/reassignQueue.d.ts.map +1 -0
- package/dist/api/report/reassignQueue.js +45 -0
- package/dist/api/report/reassignQueue.js.map +1 -0
- package/dist/api/report/refreshStats.d.ts +4 -0
- package/dist/api/report/refreshStats.d.ts.map +1 -0
- package/dist/api/report/refreshStats.js +26 -0
- package/dist/api/report/refreshStats.js.map +1 -0
- package/dist/api/report/unassignModerator.d.ts +4 -0
- package/dist/api/report/unassignModerator.d.ts.map +1 -0
- package/dist/api/report/unassignModerator.js +21 -0
- package/dist/api/report/unassignModerator.js.map +1 -0
- package/dist/api/util.d.ts +2 -0
- package/dist/api/util.d.ts.map +1 -1
- package/dist/api/util.js +9 -1
- package/dist/api/util.js.map +1 -1
- package/dist/assignment/index.d.ts +89 -0
- package/dist/assignment/index.d.ts.map +1 -0
- package/dist/assignment/index.js +537 -0
- package/dist/assignment/index.js.map +1 -0
- package/dist/config/config.d.ts +14 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +9 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/env.d.ts +3 -0
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +3 -0
- package/dist/config/env.js.map +1 -1
- package/dist/context.d.ts +9 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +31 -10
- package/dist/context.js.map +1 -1
- package/dist/daemon/context.d.ts +6 -0
- package/dist/daemon/context.d.ts.map +1 -1
- package/dist/daemon/context.js +28 -4
- package/dist/daemon/context.js.map +1 -1
- package/dist/daemon/event-reverser.d.ts +1 -0
- package/dist/daemon/event-reverser.d.ts.map +1 -1
- package/dist/daemon/event-reverser.js +42 -1
- package/dist/daemon/event-reverser.js.map +1 -1
- package/dist/daemon/job-cursor.d.ts +5 -0
- package/dist/daemon/job-cursor.d.ts.map +1 -0
- package/dist/daemon/job-cursor.js +28 -0
- package/dist/daemon/job-cursor.js.map +1 -0
- package/dist/daemon/queue-router.d.ts +17 -0
- package/dist/daemon/queue-router.d.ts.map +1 -0
- package/dist/daemon/queue-router.js +114 -0
- package/dist/daemon/queue-router.js.map +1 -0
- package/dist/daemon/stats-computer.d.ts +51 -0
- package/dist/daemon/stats-computer.d.ts.map +1 -0
- package/dist/daemon/stats-computer.js +117 -0
- package/dist/daemon/stats-computer.js.map +1 -0
- package/dist/daemon/strike-expiry-processor.d.ts.map +1 -1
- package/dist/daemon/strike-expiry-processor.js +4 -19
- package/dist/daemon/strike-expiry-processor.js.map +1 -1
- package/dist/db/migrations/20260219T164523000Z-create-report-table.d.ts +4 -0
- package/dist/db/migrations/20260219T164523000Z-create-report-table.d.ts.map +1 -0
- package/dist/db/migrations/20260219T164523000Z-create-report-table.js +126 -0
- package/dist/db/migrations/20260219T164523000Z-create-report-table.js.map +1 -0
- package/dist/db/migrations/20260219T165302248Z-moderator-assignment.d.ts +4 -0
- package/dist/db/migrations/20260219T165302248Z-moderator-assignment.d.ts.map +1 -0
- package/dist/db/migrations/20260219T165302248Z-moderator-assignment.js +35 -0
- package/dist/db/migrations/20260219T165302248Z-moderator-assignment.js.map +1 -0
- package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.d.ts +4 -0
- package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.d.ts.map +1 -0
- package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.js +36 -0
- package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.js.map +1 -0
- package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.d.ts +4 -0
- package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.d.ts.map +1 -0
- package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.js +39 -0
- package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.js.map +1 -0
- package/dist/db/migrations/20260318T152058935Z-add-report-stat.d.ts +4 -0
- package/dist/db/migrations/20260318T152058935Z-add-report-stat.d.ts.map +1 -0
- package/dist/db/migrations/20260318T152058935Z-add-report-stat.js +34 -0
- package/dist/db/migrations/20260318T152058935Z-add-report-stat.js.map +1 -0
- package/dist/db/migrations/20260428T000000000Z-add-expiring-tag-table.d.ts +4 -0
- package/dist/db/migrations/20260428T000000000Z-add-expiring-tag-table.d.ts.map +1 -0
- package/dist/db/migrations/20260428T000000000Z-add-expiring-tag-table.js +32 -0
- package/dist/db/migrations/20260428T000000000Z-add-expiring-tag-table.js.map +1 -0
- package/dist/db/migrations/index.d.ts +6 -0
- package/dist/db/migrations/index.d.ts.map +1 -1
- package/dist/db/migrations/index.js +7 -1
- package/dist/db/migrations/index.js.map +1 -1
- package/dist/db/pagination.d.ts +31 -0
- package/dist/db/pagination.d.ts.map +1 -1
- package/dist/db/pagination.js +74 -1
- package/dist/db/pagination.js.map +1 -1
- package/dist/db/schema/expiring_tag.d.ts +15 -0
- package/dist/db/schema/expiring_tag.d.ts.map +1 -0
- package/dist/db/schema/expiring_tag.js +5 -0
- package/dist/db/schema/expiring_tag.js.map +1 -0
- package/dist/db/schema/index.d.ts +7 -1
- package/dist/db/schema/index.d.ts.map +1 -1
- package/dist/db/schema/index.js.map +1 -1
- package/dist/db/schema/moderator_assignment.d.ts +14 -0
- package/dist/db/schema/moderator_assignment.d.ts.map +1 -0
- package/dist/db/schema/moderator_assignment.js +5 -0
- package/dist/db/schema/moderator_assignment.js.map +1 -0
- package/dist/db/schema/report.d.ts +25 -0
- package/dist/db/schema/report.d.ts.map +1 -0
- package/dist/db/schema/report.js +5 -0
- package/dist/db/schema/report.js.map +1 -0
- package/dist/db/schema/report_activity.d.ts +18 -0
- package/dist/db/schema/report_activity.d.ts.map +1 -0
- package/dist/db/schema/report_activity.js +5 -0
- package/dist/db/schema/report_activity.js.map +1 -0
- package/dist/db/schema/report_queue.d.ts +19 -0
- package/dist/db/schema/report_queue.d.ts.map +1 -0
- package/dist/db/schema/report_queue.js +5 -0
- package/dist/db/schema/report_queue.js.map +1 -0
- package/dist/db/schema/report_stat.d.ts +20 -0
- package/dist/db/schema/report_stat.d.ts.map +1 -0
- package/dist/db/schema/report_stat.js +5 -0
- package/dist/db/schema/report_stat.js.map +1 -0
- package/dist/lexicon/index.d.ts +50 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +120 -2
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +11255 -7885
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +1900 -120
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/embed/external.d.ts +2 -0
- package/dist/lexicon/types/app/bsky/embed/external.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/embed/external.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/actor/defs.d.ts +8 -2
- package/dist/lexicon/types/chat/bsky/actor/defs.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/actor/defs.js +9 -0
- package/dist/lexicon/types/chat/bsky/actor/defs.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/defs.d.ts +37 -10
- package/dist/lexicon/types/chat/bsky/convo/defs.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/defs.js +9 -0
- package/dist/lexicon/types/chat/bsky/convo/defs.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/getMessages.d.ts +3 -0
- package/dist/lexicon/types/chat/bsky/convo/getMessages.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/getMessages.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +2 -0
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts +19 -0
- package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/emitEvent.js +9 -0
- package/dist/lexicon/types/tools/ozone/moderation/emitEvent.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/queue/assignModerator.d.ts +27 -0
- package/dist/lexicon/types/tools/ozone/queue/assignModerator.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/assignModerator.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/assignModerator.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/createQueue.d.ts +35 -0
- package/dist/lexicon/types/tools/ozone/queue/createQueue.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/createQueue.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/createQueue.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/defs.d.ts +62 -0
- package/dist/lexicon/types/tools/ozone/queue/defs.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/defs.js +34 -0
- package/dist/lexicon/types/tools/ozone/queue/defs.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/deleteQueue.d.ts +29 -0
- package/dist/lexicon/types/tools/ozone/queue/deleteQueue.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/deleteQueue.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/deleteQueue.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/getAssignments.d.ts +30 -0
- package/dist/lexicon/types/tools/ozone/queue/getAssignments.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/getAssignments.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/getAssignments.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/listQueues.d.ts +32 -0
- package/dist/lexicon/types/tools/ozone/queue/listQueues.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/listQueues.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/listQueues.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/routeReports.d.ts +31 -0
- package/dist/lexicon/types/tools/ozone/queue/routeReports.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/routeReports.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/routeReports.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/unassignModerator.d.ts +18 -0
- package/dist/lexicon/types/tools/ozone/queue/unassignModerator.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/unassignModerator.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/unassignModerator.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/updateQueue.d.ts +32 -0
- package/dist/lexicon/types/tools/ozone/queue/updateQueue.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/updateQueue.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/updateQueue.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/assignModerator.d.ts +31 -0
- package/dist/lexicon/types/tools/ozone/report/assignModerator.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/assignModerator.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/assignModerator.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/createActivity.d.ts +37 -0
- package/dist/lexicon/types/tools/ozone/report/createActivity.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/createActivity.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/createActivity.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/defs.d.ts +185 -0
- package/dist/lexicon/types/tools/ozone/report/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/report/defs.js +108 -0
- package/dist/lexicon/types/tools/ozone/report/defs.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/report/getAssignments.d.ts +30 -0
- package/dist/lexicon/types/tools/ozone/report/getAssignments.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getAssignments.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getAssignments.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.d.ts +36 -0
- package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getLatestReport.d.ts +21 -0
- package/dist/lexicon/types/tools/ozone/report/getLatestReport.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getLatestReport.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getLatestReport.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getLiveStats.d.ts +27 -0
- package/dist/lexicon/types/tools/ozone/report/getLiveStats.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getLiveStats.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getLiveStats.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getReport.d.ts +22 -0
- package/dist/lexicon/types/tools/ozone/report/getReport.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getReport.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getReport.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/listActivities.d.ts +26 -0
- package/dist/lexicon/types/tools/ozone/report/listActivities.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/listActivities.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/listActivities.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/queryReports.d.ts +48 -0
- package/dist/lexicon/types/tools/ozone/report/queryReports.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/queryReports.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/queryReports.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/reassignQueue.d.ts +31 -0
- package/dist/lexicon/types/tools/ozone/report/reassignQueue.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/reassignQueue.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/reassignQueue.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/refreshStats.d.ts +28 -0
- package/dist/lexicon/types/tools/ozone/report/refreshStats.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/refreshStats.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/refreshStats.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/unassignModerator.d.ts +25 -0
- package/dist/lexicon/types/tools/ozone/report/unassignModerator.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/unassignModerator.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/unassignModerator.js.map +1 -0
- package/dist/mod-service/expiring-tags.d.ts +27 -0
- package/dist/mod-service/expiring-tags.d.ts.map +1 -0
- package/dist/mod-service/expiring-tags.js +62 -0
- package/dist/mod-service/expiring-tags.js.map +1 -0
- package/dist/mod-service/index.d.ts +3 -1
- package/dist/mod-service/index.d.ts.map +1 -1
- package/dist/mod-service/index.js +61 -2
- package/dist/mod-service/index.js.map +1 -1
- package/dist/mod-service/report.d.ts +64 -0
- package/dist/mod-service/report.d.ts.map +1 -0
- package/dist/mod-service/report.js +282 -0
- package/dist/mod-service/report.js.map +1 -0
- package/dist/mod-service/status.d.ts +24 -0
- package/dist/mod-service/status.d.ts.map +1 -1
- package/dist/queue/service.d.ts +86 -0
- package/dist/queue/service.d.ts.map +1 -0
- package/dist/queue/service.js +430 -0
- package/dist/queue/service.js.map +1 -0
- package/dist/report/activity.d.ts +77 -0
- package/dist/report/activity.d.ts.map +1 -0
- package/dist/report/activity.js +141 -0
- package/dist/report/activity.js.map +1 -0
- package/dist/report/handle-report-update.d.ts +47 -0
- package/dist/report/handle-report-update.d.ts.map +1 -0
- package/dist/report/handle-report-update.js +178 -0
- package/dist/report/handle-report-update.js.map +1 -0
- package/dist/report/reassign.d.ts +10 -0
- package/dist/report/reassign.d.ts.map +1 -0
- package/dist/report/reassign.js +75 -0
- package/dist/report/reassign.js.map +1 -0
- package/dist/report/stats.d.ts +105 -0
- package/dist/report/stats.d.ts.map +1 -0
- package/dist/report/stats.js +619 -0
- package/dist/report/stats.js.map +1 -0
- package/dist/report/views.d.ts +111 -0
- package/dist/report/views.d.ts.map +1 -0
- package/dist/report/views.js +156 -0
- package/dist/report/views.js.map +1 -0
- package/dist/team/index.d.ts +1 -0
- package/dist/team/index.d.ts.map +1 -1
- package/dist/team/index.js +11 -0
- package/dist/team/index.js.map +1 -1
- package/package.json +3 -3
- package/src/api/index.ts +40 -0
- package/src/api/moderation/emitEvent.ts +38 -0
- package/src/api/queue/assignModerator.ts +31 -0
- package/src/api/queue/createQueue.ts +62 -0
- package/src/api/queue/deleteQueue.ts +56 -0
- package/src/api/queue/getAssignments.ts +19 -0
- package/src/api/queue/listQueues.ts +39 -0
- package/src/api/queue/routeReports.ts +44 -0
- package/src/api/queue/unassignModerator.ts +26 -0
- package/src/api/queue/updateQueue.ts +54 -0
- package/src/api/report/assignModerator.ts +36 -0
- package/src/api/report/createActivity.ts +57 -0
- package/src/api/report/getAssignments.ts +20 -0
- package/src/api/report/getHistoricalStats.ts +41 -0
- package/src/api/report/getLatestReport.ts +44 -0
- package/src/api/report/getLiveStats.ts +26 -0
- package/src/api/report/getReport.ts +55 -0
- package/src/api/report/listActivities.ts +34 -0
- package/src/api/report/queryReports.ts +44 -0
- package/src/api/report/reassignQueue.ts +68 -0
- package/src/api/report/refreshStats.ts +27 -0
- package/src/api/report/unassignModerator.ts +21 -0
- package/src/api/util.ts +12 -0
- package/src/assignment/index.ts +731 -0
- package/src/config/config.ts +27 -0
- package/src/config/env.ts +8 -0
- package/src/context.ts +31 -0
- package/src/daemon/context.ts +34 -0
- package/src/daemon/event-reverser.ts +50 -1
- package/src/daemon/job-cursor.ts +33 -0
- package/src/daemon/queue-router.ts +101 -0
- package/src/daemon/stats-computer.ts +101 -0
- package/src/daemon/strike-expiry-processor.ts +4 -20
- package/src/db/migrations/20260219T164523000Z-create-report-table.ts +155 -0
- package/src/db/migrations/20260219T165302248Z-moderator-assignment.ts +42 -0
- package/src/db/migrations/20260225T000000000Z-add-report-queue-table.ts +41 -0
- package/src/db/migrations/20260313T000000000Z-add-report-activity-table.ts +48 -0
- package/src/db/migrations/20260318T152058935Z-add-report-stat.ts +35 -0
- package/src/db/migrations/20260428T000000000Z-add-expiring-tag-table.ts +32 -0
- package/src/db/migrations/index.ts +6 -0
- package/src/db/pagination.ts +85 -0
- package/src/db/schema/expiring_tag.ts +17 -0
- package/src/db/schema/index.ts +13 -1
- package/src/db/schema/moderator_assignment.ts +16 -0
- package/src/db/schema/report.ts +27 -0
- package/src/db/schema/report_activity.ts +22 -0
- package/src/db/schema/report_queue.ts +21 -0
- package/src/db/schema/report_stat.ts +27 -0
- package/src/lexicon/index.ts +280 -0
- package/src/lexicon/lexicons.ts +2083 -214
- package/src/lexicon/types/app/bsky/embed/external.ts +2 -0
- package/src/lexicon/types/chat/bsky/actor/defs.ts +17 -1
- package/src/lexicon/types/chat/bsky/convo/defs.ts +50 -10
- package/src/lexicon/types/chat/bsky/convo/getMessages.ts +3 -0
- package/src/lexicon/types/tools/ozone/moderation/defs.ts +2 -0
- package/src/lexicon/types/tools/ozone/moderation/emitEvent.ts +24 -0
- package/src/lexicon/types/tools/ozone/queue/assignModerator.ts +46 -0
- package/src/lexicon/types/tools/ozone/queue/createQueue.ts +54 -0
- package/src/lexicon/types/tools/ozone/queue/defs.ts +99 -0
- package/src/lexicon/types/tools/ozone/queue/deleteQueue.ts +48 -0
- package/src/lexicon/types/tools/ozone/queue/getAssignments.ts +48 -0
- package/src/lexicon/types/tools/ozone/queue/listQueues.ts +50 -0
- package/src/lexicon/types/tools/ozone/queue/routeReports.ts +50 -0
- package/src/lexicon/types/tools/ozone/queue/unassignModerator.ts +37 -0
- package/src/lexicon/types/tools/ozone/queue/updateQueue.ts +51 -0
- package/src/lexicon/types/tools/ozone/report/assignModerator.ts +50 -0
- package/src/lexicon/types/tools/ozone/report/createActivity.ts +60 -0
- package/src/lexicon/types/tools/ozone/report/defs.ts +327 -0
- package/src/lexicon/types/tools/ozone/report/getAssignments.ts +48 -0
- package/src/lexicon/types/tools/ozone/report/getHistoricalStats.ts +54 -0
- package/src/lexicon/types/tools/ozone/report/getLatestReport.ts +39 -0
- package/src/lexicon/types/tools/ozone/report/getLiveStats.ts +45 -0
- package/src/lexicon/types/tools/ozone/report/getReport.ts +38 -0
- package/src/lexicon/types/tools/ozone/report/listActivities.ts +44 -0
- package/src/lexicon/types/tools/ozone/report/queryReports.ts +72 -0
- package/src/lexicon/types/tools/ozone/report/reassignQueue.ts +55 -0
- package/src/lexicon/types/tools/ozone/report/refreshStats.ts +46 -0
- package/src/lexicon/types/tools/ozone/report/unassignModerator.ts +44 -0
- package/src/mod-service/expiring-tags.ts +98 -0
- package/src/mod-service/index.ts +71 -3
- package/src/mod-service/report.ts +408 -0
- package/src/queue/service.ts +599 -0
- package/src/report/activity.ts +234 -0
- package/src/report/handle-report-update.ts +209 -0
- package/src/report/reassign.ts +109 -0
- package/src/report/stats.ts +850 -0
- package/src/report/views.ts +241 -0
- package/src/team/index.ts +11 -0
- package/tests/expiring-tags.test.ts +231 -0
- package/tests/get-report.test.ts +136 -0
- package/tests/query-reports.test.ts +608 -0
- package/tests/queue-assignment.test.ts +428 -0
- package/tests/queue-router.test.ts +306 -0
- package/tests/queues.test.ts +690 -0
- package/tests/report-action.test.ts +308 -0
- package/tests/report-activity.test.ts +567 -0
- package/tests/report-assignment.test.ts +517 -0
- package/tests/report-reassign-queue.test.ts +340 -0
- package/tests/report-routing.test.ts +245 -0
- package/tests/report-stats.test.ts +545 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/src/config/config.ts
CHANGED
|
@@ -90,6 +90,15 @@ export const envToCfg = (env: OzoneEnvironment): OzoneConfig => {
|
|
|
90
90
|
}
|
|
91
91
|
: null
|
|
92
92
|
|
|
93
|
+
const assignmentsCfg: OzoneConfig['assignments'] = {
|
|
94
|
+
queueDurationMs: env.assignmentQueueDurationMs ?? 5 * MINUTE,
|
|
95
|
+
reportDurationMs: env.assignmentReportDurationMs ?? 5 * MINUTE,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const statsCfg: OzoneConfig['stats'] = {
|
|
99
|
+
computerIntervalMinutes: env.statsComputerIntervalMinutes ?? 15,
|
|
100
|
+
}
|
|
101
|
+
|
|
93
102
|
return {
|
|
94
103
|
service: serviceCfg,
|
|
95
104
|
db: dbCfg,
|
|
@@ -101,6 +110,8 @@ export const envToCfg = (env: OzoneEnvironment): OzoneConfig => {
|
|
|
101
110
|
blobDivert: blobDivertServiceCfg,
|
|
102
111
|
access: accessCfg,
|
|
103
112
|
verifier: verifierCfg,
|
|
113
|
+
assignments: assignmentsCfg,
|
|
114
|
+
stats: statsCfg,
|
|
104
115
|
jetstreamUrl: env.jetstreamUrl,
|
|
105
116
|
}
|
|
106
117
|
}
|
|
@@ -115,10 +126,21 @@ export type OzoneConfig = {
|
|
|
115
126
|
identity: IdentityConfig
|
|
116
127
|
blobDivert: BlobDivertConfig | null
|
|
117
128
|
access: AccessConfig
|
|
129
|
+
assignments: AssignmentsConfig
|
|
130
|
+
stats: StatsConfig
|
|
118
131
|
jetstreamUrl?: string
|
|
119
132
|
verifier: VerifierConfig | null
|
|
120
133
|
}
|
|
121
134
|
|
|
135
|
+
export type StatsConfig = {
|
|
136
|
+
/**
|
|
137
|
+
* Minutes between stats computer cycles.
|
|
138
|
+
* Defaults to 15. Minimum is 1.
|
|
139
|
+
* Set to -1 to disable the stats computer.
|
|
140
|
+
*/
|
|
141
|
+
computerIntervalMinutes: number
|
|
142
|
+
}
|
|
143
|
+
|
|
122
144
|
export type ServiceConfig = {
|
|
123
145
|
port: number
|
|
124
146
|
publicUrl: string
|
|
@@ -182,3 +204,8 @@ export type VerifierConfig = {
|
|
|
182
204
|
jetstreamUrl?: string
|
|
183
205
|
issuersToIndex?: string[]
|
|
184
206
|
}
|
|
207
|
+
|
|
208
|
+
export type AssignmentsConfig = {
|
|
209
|
+
queueDurationMs: number
|
|
210
|
+
reportDurationMs: number
|
|
211
|
+
}
|
package/src/config/env.ts
CHANGED
|
@@ -43,6 +43,11 @@ export const readEnv = (): OzoneEnvironment => {
|
|
|
43
43
|
verifierPassword: envStr('OZONE_VERIFIER_PASSWORD'),
|
|
44
44
|
verifierIssuersToIndex: envList('OZONE_VERIFIER_ISSUERS_TO_INDEX'),
|
|
45
45
|
jetstreamUrl: envStr('OZONE_JETSTREAM_URL'),
|
|
46
|
+
assignmentQueueDurationMs: envInt('OZONE_ASSIGNMENT_QUEUE_DURATION_MS'),
|
|
47
|
+
assignmentReportDurationMs: envInt('OZONE_ASSIGNMENT_REPORT_DURATION_MS'),
|
|
48
|
+
statsComputerIntervalMinutes: envInt(
|
|
49
|
+
'OZONE_STATS_COMPUTER_INTERVAL_MINUTES',
|
|
50
|
+
),
|
|
46
51
|
}
|
|
47
52
|
}
|
|
48
53
|
|
|
@@ -84,4 +89,7 @@ export type OzoneEnvironment = {
|
|
|
84
89
|
verifierPassword?: string
|
|
85
90
|
verifierIssuersToIndex?: string[]
|
|
86
91
|
jetstreamUrl?: string
|
|
92
|
+
assignmentQueueDurationMs?: number
|
|
93
|
+
assignmentReportDurationMs?: number
|
|
94
|
+
statsComputerIntervalMinutes?: number
|
|
87
95
|
}
|
package/src/context.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { AtpAgent } from '@atproto/api'
|
|
|
5
5
|
import { Keypair, Secp256k1Keypair } from '@atproto/crypto'
|
|
6
6
|
import { DidCache, IdResolver, MemoryCache } from '@atproto/identity'
|
|
7
7
|
import { createServiceAuthHeaders } from '@atproto/xrpc-server'
|
|
8
|
+
import { AssignmentService } from './assignment'
|
|
8
9
|
import { AuthVerifier } from './auth-verifier'
|
|
9
10
|
import { BackgroundQueue } from './background'
|
|
10
11
|
import {
|
|
@@ -22,6 +23,8 @@ import {
|
|
|
22
23
|
ModerationServiceProfileCreator,
|
|
23
24
|
} from './mod-service/profile'
|
|
24
25
|
import { StrikeService, StrikeServiceCreator } from './mod-service/strike'
|
|
26
|
+
import { QueueService, QueueServiceCreator } from './queue/service'
|
|
27
|
+
import { ReportStatsService, ReportStatsServiceCreator } from './report/stats'
|
|
25
28
|
import {
|
|
26
29
|
SafelinkRuleService,
|
|
27
30
|
SafelinkRuleServiceCreator,
|
|
@@ -58,6 +61,8 @@ export type AppContextOptions = {
|
|
|
58
61
|
communicationTemplateService: CommunicationTemplateServiceCreator
|
|
59
62
|
safelinkRuleService: SafelinkRuleServiceCreator
|
|
60
63
|
scheduledActionService: ScheduledActionServiceCreator
|
|
64
|
+
queueService: QueueServiceCreator
|
|
65
|
+
reportStatsService: ReportStatsServiceCreator
|
|
61
66
|
setService: SetServiceCreator
|
|
62
67
|
settingService: SettingServiceCreator
|
|
63
68
|
strikeService: StrikeServiceCreator
|
|
@@ -73,6 +78,7 @@ export type AppContextOptions = {
|
|
|
73
78
|
imgInvalidator?: ImageInvalidator
|
|
74
79
|
backgroundQueue: BackgroundQueue
|
|
75
80
|
sequencer: Sequencer
|
|
81
|
+
assignmentService: AssignmentService
|
|
76
82
|
authVerifier: AuthVerifier
|
|
77
83
|
verificationService: VerificationServiceCreator
|
|
78
84
|
verificationIssuer: VerificationIssuerCreator
|
|
@@ -143,6 +149,8 @@ export class AppContext {
|
|
|
143
149
|
cfg.appview.did,
|
|
144
150
|
createAuthHeaders,
|
|
145
151
|
)
|
|
152
|
+
const queueService = QueueService.creator()
|
|
153
|
+
const reportStatsService = ReportStatsService.creator()
|
|
146
154
|
const setService = SetService.creator()
|
|
147
155
|
const settingService = SettingService.creator()
|
|
148
156
|
const strikeService = StrikeService.creator()
|
|
@@ -164,6 +172,14 @@ export class AppContext {
|
|
|
164
172
|
strikeService,
|
|
165
173
|
overrides?.imgInvalidator,
|
|
166
174
|
)
|
|
175
|
+
const assignmentService = AssignmentService.creator(
|
|
176
|
+
{
|
|
177
|
+
queueDurationMs: cfg.assignments.queueDurationMs,
|
|
178
|
+
reportDurationMs: cfg.assignments.reportDurationMs,
|
|
179
|
+
},
|
|
180
|
+
queueService,
|
|
181
|
+
teamService,
|
|
182
|
+
)(db)
|
|
167
183
|
|
|
168
184
|
const sequencer = new Sequencer(modService(db))
|
|
169
185
|
|
|
@@ -183,6 +199,8 @@ export class AppContext {
|
|
|
183
199
|
safelinkRuleService,
|
|
184
200
|
scheduledActionService,
|
|
185
201
|
teamService,
|
|
202
|
+
queueService,
|
|
203
|
+
reportStatsService,
|
|
186
204
|
setService,
|
|
187
205
|
settingService,
|
|
188
206
|
strikeService,
|
|
@@ -195,6 +213,7 @@ export class AppContext {
|
|
|
195
213
|
idResolver,
|
|
196
214
|
backgroundQueue,
|
|
197
215
|
sequencer,
|
|
216
|
+
assignmentService,
|
|
198
217
|
authVerifier,
|
|
199
218
|
blobDiverter,
|
|
200
219
|
verificationService,
|
|
@@ -245,6 +264,14 @@ export class AppContext {
|
|
|
245
264
|
return this.opts.teamService
|
|
246
265
|
}
|
|
247
266
|
|
|
267
|
+
get queueService(): QueueServiceCreator {
|
|
268
|
+
return this.opts.queueService
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
get reportStatsService(): ReportStatsServiceCreator {
|
|
272
|
+
return this.opts.reportStatsService
|
|
273
|
+
}
|
|
274
|
+
|
|
248
275
|
get setService(): SetServiceCreator {
|
|
249
276
|
return this.opts.setService
|
|
250
277
|
}
|
|
@@ -309,6 +336,10 @@ export class AppContext {
|
|
|
309
336
|
return this.opts.sequencer
|
|
310
337
|
}
|
|
311
338
|
|
|
339
|
+
get assignmentService(): AssignmentService {
|
|
340
|
+
return this.opts.assignmentService
|
|
341
|
+
}
|
|
342
|
+
|
|
312
343
|
get authVerifier(): AuthVerifier {
|
|
313
344
|
return this.opts.authVerifier
|
|
314
345
|
}
|
package/src/daemon/context.ts
CHANGED
|
@@ -8,6 +8,8 @@ import { OzoneConfig, OzoneSecrets } from '../config'
|
|
|
8
8
|
import { Database } from '../db'
|
|
9
9
|
import { ModerationService } from '../mod-service'
|
|
10
10
|
import { StrikeService } from '../mod-service/strike'
|
|
11
|
+
import { QueueService } from '../queue/service'
|
|
12
|
+
import { ReportStatsService } from '../report/stats'
|
|
11
13
|
import { ScheduledActionService } from '../scheduled-action/service'
|
|
12
14
|
import { SettingService } from '../setting/service'
|
|
13
15
|
import { TeamService } from '../team'
|
|
@@ -15,7 +17,9 @@ import { getSigningKeyId } from '../util'
|
|
|
15
17
|
import { EventPusher } from './event-pusher'
|
|
16
18
|
import { EventReverser } from './event-reverser'
|
|
17
19
|
import { MaterializedViewRefresher } from './materialized-view-refresher'
|
|
20
|
+
import { QueueRouter } from './queue-router'
|
|
18
21
|
import { ScheduledActionProcessor } from './scheduled-action-processor'
|
|
22
|
+
import { StatsComputer } from './stats-computer'
|
|
19
23
|
import { StrikeExpiryProcessor } from './strike-expiry-processor'
|
|
20
24
|
import { TeamProfileSynchronizer } from './team-profile-synchronizer'
|
|
21
25
|
import { VerificationListener } from './verification-listener'
|
|
@@ -31,7 +35,9 @@ export type DaemonContextOptions = {
|
|
|
31
35
|
teamProfileSynchronizer: TeamProfileSynchronizer
|
|
32
36
|
scheduledActionProcessor: ScheduledActionProcessor
|
|
33
37
|
strikeExpiryProcessor: StrikeExpiryProcessor
|
|
38
|
+
queueRouter: QueueRouter
|
|
34
39
|
verificationListener?: VerificationListener
|
|
40
|
+
statsComputer?: StatsComputer
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
export class DaemonContext {
|
|
@@ -111,6 +117,16 @@ export class DaemonContext {
|
|
|
111
117
|
|
|
112
118
|
const strikeExpiryProcessor = new StrikeExpiryProcessor(db, strikeService)
|
|
113
119
|
|
|
120
|
+
const queueService = QueueService.creator()
|
|
121
|
+
const queueRouter = new QueueRouter(db, queueService)
|
|
122
|
+
|
|
123
|
+
const reportStatsService = ReportStatsService.creator()
|
|
124
|
+
const statsComputer = new StatsComputer(
|
|
125
|
+
db,
|
|
126
|
+
reportStatsService,
|
|
127
|
+
cfg.stats.computerIntervalMinutes,
|
|
128
|
+
)
|
|
129
|
+
|
|
114
130
|
// Only spawn the listener if verifier config exists and a jetstream URL is provided
|
|
115
131
|
const verificationListener =
|
|
116
132
|
cfg.verifier && cfg.jetstreamUrl
|
|
@@ -132,7 +148,9 @@ export class DaemonContext {
|
|
|
132
148
|
teamProfileSynchronizer,
|
|
133
149
|
scheduledActionProcessor,
|
|
134
150
|
strikeExpiryProcessor,
|
|
151
|
+
queueRouter,
|
|
135
152
|
verificationListener,
|
|
153
|
+
statsComputer,
|
|
136
154
|
...(overrides ?? {}),
|
|
137
155
|
})
|
|
138
156
|
}
|
|
@@ -173,10 +191,18 @@ export class DaemonContext {
|
|
|
173
191
|
return this.opts.strikeExpiryProcessor
|
|
174
192
|
}
|
|
175
193
|
|
|
194
|
+
get queueRouter(): QueueRouter {
|
|
195
|
+
return this.opts.queueRouter
|
|
196
|
+
}
|
|
197
|
+
|
|
176
198
|
get verificationListener(): VerificationListener | undefined {
|
|
177
199
|
return this.opts.verificationListener
|
|
178
200
|
}
|
|
179
201
|
|
|
202
|
+
get statsComputer(): StatsComputer | undefined {
|
|
203
|
+
return this.opts.statsComputer
|
|
204
|
+
}
|
|
205
|
+
|
|
180
206
|
async start() {
|
|
181
207
|
this.eventPusher.start()
|
|
182
208
|
this.eventReverser.start()
|
|
@@ -184,12 +210,18 @@ export class DaemonContext {
|
|
|
184
210
|
this.teamProfileSynchronizer.start()
|
|
185
211
|
this.scheduledActionProcessor.start()
|
|
186
212
|
this.strikeExpiryProcessor.start()
|
|
213
|
+
this.queueRouter.start()
|
|
187
214
|
this.verificationListener?.start()
|
|
215
|
+
this.statsComputer?.start()
|
|
188
216
|
}
|
|
189
217
|
|
|
190
218
|
async processAll() {
|
|
191
219
|
// Sequential because the materialized view values depend on the events.
|
|
192
220
|
await this.eventPusher.processAll()
|
|
221
|
+
// Drain pending modEventReport rows into the report table so test code
|
|
222
|
+
// that calls processAll() after creating reports sees the report rows
|
|
223
|
+
// immediately (in production this happens via the 1-min poll).
|
|
224
|
+
await this.queueRouter.routeReports()
|
|
193
225
|
await this.materializedViewRefresher.run()
|
|
194
226
|
await this.teamProfileSynchronizer.run()
|
|
195
227
|
}
|
|
@@ -203,7 +235,9 @@ export class DaemonContext {
|
|
|
203
235
|
this.teamProfileSynchronizer.destroy(),
|
|
204
236
|
this.scheduledActionProcessor.destroy(),
|
|
205
237
|
this.strikeExpiryProcessor.destroy(),
|
|
238
|
+
this.queueRouter.destroy(),
|
|
206
239
|
this.verificationListener?.stop(),
|
|
240
|
+
this.statsComputer?.destroy(),
|
|
207
241
|
])
|
|
208
242
|
} finally {
|
|
209
243
|
await this.backgroundQueue.destroy()
|
|
@@ -2,6 +2,11 @@ import { MINUTE } from '@atproto/common'
|
|
|
2
2
|
import { Database } from '../db'
|
|
3
3
|
import { dbLogger } from '../logger'
|
|
4
4
|
import { ModerationServiceCreator, ReversalSubject } from '../mod-service'
|
|
5
|
+
import {
|
|
6
|
+
deleteExpiringTagsByIds,
|
|
7
|
+
getExpiredTags,
|
|
8
|
+
} from '../mod-service/expiring-tags'
|
|
9
|
+
import { subjectFromStatusRow } from '../mod-service/subject'
|
|
5
10
|
|
|
6
11
|
export class EventReverser {
|
|
7
12
|
destroyed = false
|
|
@@ -62,7 +67,51 @@ export class EventReverser {
|
|
|
62
67
|
|
|
63
68
|
// We shouldn't have too many actions due for reversal at any given time, so running in parallel is probably fine
|
|
64
69
|
// Internally, each reversal runs within its own transaction
|
|
65
|
-
await Promise.all(
|
|
70
|
+
await Promise.all([
|
|
71
|
+
...subjectsDueForReversal.map(this.revertState.bind(this)),
|
|
72
|
+
this.findAndRevertExpiredTags(),
|
|
73
|
+
])
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async findAndRevertExpiredTags() {
|
|
77
|
+
const groups = await getExpiredTags(this.db)
|
|
78
|
+
if (!groups.length) return
|
|
79
|
+
|
|
80
|
+
for (const group of groups) {
|
|
81
|
+
await this.db.transaction(async (dbTxn) => {
|
|
82
|
+
// Check which tags are still present on the subject
|
|
83
|
+
const status = await dbTxn.db
|
|
84
|
+
.selectFrom('moderation_subject_status')
|
|
85
|
+
.where('did', '=', group.did)
|
|
86
|
+
.where('recordPath', '=', group.recordPath)
|
|
87
|
+
.selectAll()
|
|
88
|
+
.executeTakeFirst()
|
|
89
|
+
|
|
90
|
+
const currentTags: string[] = status?.tags ?? []
|
|
91
|
+
const tagsToRemove = group.tags.filter((t) => currentTags.includes(t))
|
|
92
|
+
|
|
93
|
+
// Delete the expiring_tag rows regardless
|
|
94
|
+
await deleteExpiringTagsByIds(dbTxn, group.ids)
|
|
95
|
+
|
|
96
|
+
// Only emit removal event if there are tags still present to remove
|
|
97
|
+
if (tagsToRemove.length > 0 && status) {
|
|
98
|
+
const subject = subjectFromStatusRow(status)
|
|
99
|
+
const moderationTxn = this.modService(dbTxn)
|
|
100
|
+
await moderationTxn.logEvent({
|
|
101
|
+
event: {
|
|
102
|
+
$type: 'tools.ozone.moderation.defs#modEventTag',
|
|
103
|
+
add: [],
|
|
104
|
+
remove: tagsToRemove,
|
|
105
|
+
comment:
|
|
106
|
+
'[SCHEDULED_REVERSAL] Reverting temporary tags as originally scheduled',
|
|
107
|
+
},
|
|
108
|
+
createdBy: group.createdBy,
|
|
109
|
+
subject,
|
|
110
|
+
createdAt: new Date(),
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
}
|
|
66
115
|
}
|
|
67
116
|
}
|
|
68
117
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Database } from '../db'
|
|
2
|
+
|
|
3
|
+
export async function initJobCursor(db: Database, job: string): Promise<void> {
|
|
4
|
+
await db.db
|
|
5
|
+
.insertInto('job_cursor')
|
|
6
|
+
.values({ job, cursor: null })
|
|
7
|
+
.onConflict((oc) => oc.doNothing())
|
|
8
|
+
.execute()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function getJobCursor(
|
|
12
|
+
db: Database,
|
|
13
|
+
job: string,
|
|
14
|
+
): Promise<string | null> {
|
|
15
|
+
const entry = await db.db
|
|
16
|
+
.selectFrom('job_cursor')
|
|
17
|
+
.select('cursor')
|
|
18
|
+
.where('job', '=', job)
|
|
19
|
+
.executeTakeFirst()
|
|
20
|
+
return entry?.cursor ?? null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function updateJobCursor(
|
|
24
|
+
db: Database,
|
|
25
|
+
job: string,
|
|
26
|
+
cursor: string,
|
|
27
|
+
): Promise<void> {
|
|
28
|
+
await db.db
|
|
29
|
+
.updateTable('job_cursor')
|
|
30
|
+
.set({ cursor })
|
|
31
|
+
.where('job', '=', job)
|
|
32
|
+
.execute()
|
|
33
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { MINUTE } from '@atproto/common'
|
|
2
|
+
import { Database } from '../db'
|
|
3
|
+
import { dbLogger } from '../logger'
|
|
4
|
+
import { QueueServiceCreator } from '../queue/service'
|
|
5
|
+
import { initJobCursor } from './job-cursor'
|
|
6
|
+
|
|
7
|
+
const JOB_NAME = 'queue_router'
|
|
8
|
+
const BATCH_SIZE = 100
|
|
9
|
+
|
|
10
|
+
export class QueueRouter {
|
|
11
|
+
destroyed = false
|
|
12
|
+
processingPromise: Promise<void> = Promise.resolve()
|
|
13
|
+
timer?: NodeJS.Timeout
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
private db: Database,
|
|
17
|
+
private queueServiceCreator: QueueServiceCreator,
|
|
18
|
+
) {}
|
|
19
|
+
|
|
20
|
+
start() {
|
|
21
|
+
this.initializeCursor().then(() => this.poll())
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
poll() {
|
|
25
|
+
if (this.destroyed) return
|
|
26
|
+
this.processingPromise = this.routeReports()
|
|
27
|
+
.catch((err) => dbLogger.error({ err }, 'queue routing errored'))
|
|
28
|
+
.finally(() => {
|
|
29
|
+
this.timer = setTimeout(() => this.poll(), getInterval())
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async destroy() {
|
|
34
|
+
this.destroyed = true
|
|
35
|
+
if (this.timer) {
|
|
36
|
+
clearTimeout(this.timer)
|
|
37
|
+
this.timer = undefined
|
|
38
|
+
}
|
|
39
|
+
await this.processingPromise
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async initializeCursor() {
|
|
43
|
+
await initJobCursor(this.db, JOB_NAME)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async getCursor(): Promise<number | null> {
|
|
47
|
+
const row = await this.db.db
|
|
48
|
+
.selectFrom('job_cursor')
|
|
49
|
+
.select('cursor')
|
|
50
|
+
.where('job', '=', JOB_NAME)
|
|
51
|
+
.executeTakeFirst()
|
|
52
|
+
return row?.cursor ? parseInt(row.cursor, 10) : null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async routeReports() {
|
|
56
|
+
await this.db.transaction(async (txn) => {
|
|
57
|
+
// Acquire row lock on the job_cursor row. A second daemon instance
|
|
58
|
+
// hitting this same query blocks here until the first transaction
|
|
59
|
+
// commits, then reads the now-advanced cursor and processes the next
|
|
60
|
+
// range. The lock is held for the whole batch (~50–200ms).
|
|
61
|
+
const row = await txn.db
|
|
62
|
+
.selectFrom('job_cursor')
|
|
63
|
+
.selectAll()
|
|
64
|
+
.where('job', '=', JOB_NAME)
|
|
65
|
+
.forUpdate()
|
|
66
|
+
.executeTakeFirst()
|
|
67
|
+
if (!row) return
|
|
68
|
+
const cursor = row.cursor ? parseInt(row.cursor, 10) : null
|
|
69
|
+
|
|
70
|
+
const queueService = this.queueServiceCreator(txn)
|
|
71
|
+
const result = await queueService.insertReportsFromEvents({
|
|
72
|
+
cursor,
|
|
73
|
+
limit: BATCH_SIZE,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
if (result.processed === 0) {
|
|
77
|
+
dbLogger.info('no new report events to route')
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
await txn.db
|
|
82
|
+
.updateTable('job_cursor')
|
|
83
|
+
.set({ cursor: String(result.maxEventId) })
|
|
84
|
+
.where('job', '=', JOB_NAME)
|
|
85
|
+
.execute()
|
|
86
|
+
|
|
87
|
+
dbLogger.info(
|
|
88
|
+
{
|
|
89
|
+
processed: result.processed,
|
|
90
|
+
assigned: result.assigned,
|
|
91
|
+
unmatched: result.unmatched,
|
|
92
|
+
maxEventId: result.maxEventId,
|
|
93
|
+
},
|
|
94
|
+
'queue routing completed',
|
|
95
|
+
)
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Poll every 1 minute
|
|
101
|
+
const getInterval = (): number => 1 * MINUTE
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { sql } from 'kysely'
|
|
2
|
+
import { MINUTE } from '@atproto/common'
|
|
3
|
+
import { Database } from '../db'
|
|
4
|
+
import { dbLogger } from '../logger'
|
|
5
|
+
import { ReportStatsServiceCreator } from '../report/stats'
|
|
6
|
+
|
|
7
|
+
// Stable lock ID for pg_try_advisory_lock across all instances
|
|
8
|
+
const ADVISORY_LOCK_ID = 7_239_401
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Background daemon that materializes report statistics on an interval (default is 15 minutes).
|
|
12
|
+
*
|
|
13
|
+
* Each cycle computes calendar-day snapshots: today's stats are recomputed (in-progress day),
|
|
14
|
+
* and yesterday's snapshot is finalized if it wasn't already. Historical snapshots (completed
|
|
15
|
+
* days) are write-once and never recomputed unless explicitly refreshed via the API.
|
|
16
|
+
*
|
|
17
|
+
* Query profile per cycle (assuming ~10K reports/day, 10 queues, 20 moderators, 9 type groups):
|
|
18
|
+
* - 7 batched GROUP BY queries against the report table for today's date window
|
|
19
|
+
* (+ 7 more for yesterday if finalization is needed).
|
|
20
|
+
* Day-window queries scan ~10K rows. Pending-count queries use partial indexes
|
|
21
|
+
* (WHERE status != 'closed') so only scan open reports, not the full table.
|
|
22
|
+
* Expected: ~10-50ms per query, ~100-350ms total report-table time.
|
|
23
|
+
* - ~40 lightweight reads against report_stat for freshness checks (small indexed table).
|
|
24
|
+
* - ~40 lightweight writes to report_stat for upserts.
|
|
25
|
+
*
|
|
26
|
+
* Locking: Uses pg_try_advisory_lock to ensure only one instance materializes at a time
|
|
27
|
+
* when running multiple containers. Advisory locks are cooperative, session-level locks —
|
|
28
|
+
* they do NOT block any table reads, writes, row locks, or transactions from other sessions.
|
|
29
|
+
* Normal application queries (report creation, moderation actions, API reads) are completely
|
|
30
|
+
* unaffected. If another instance already holds the lock, this instance skips the cycle
|
|
31
|
+
* immediately without blocking.
|
|
32
|
+
*/
|
|
33
|
+
export class StatsComputer {
|
|
34
|
+
destroyed = false
|
|
35
|
+
processingPromise: Promise<void> = Promise.resolve()
|
|
36
|
+
timer?: NodeJS.Timeout
|
|
37
|
+
|
|
38
|
+
constructor(
|
|
39
|
+
private db: Database,
|
|
40
|
+
private reportStatsServiceCreator: ReportStatsServiceCreator,
|
|
41
|
+
/**
|
|
42
|
+
* Minutes between stats computer cycles.
|
|
43
|
+
* Defaults to 15. Minimum is 1.
|
|
44
|
+
* Set to -1 to disable the stats computer.
|
|
45
|
+
*/
|
|
46
|
+
private intervalMinutes: number,
|
|
47
|
+
) {}
|
|
48
|
+
|
|
49
|
+
get disabled() {
|
|
50
|
+
return this.intervalMinutes < 1
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
start() {
|
|
54
|
+
this.poll()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
poll() {
|
|
58
|
+
if (this.destroyed || this.disabled) return
|
|
59
|
+
this.processingPromise = this.materializeStats()
|
|
60
|
+
.catch((err) => dbLogger.error({ err }, 'stats materialization errored'))
|
|
61
|
+
.finally(() => {
|
|
62
|
+
this.timer = setTimeout(
|
|
63
|
+
() => this.poll(),
|
|
64
|
+
this.intervalMinutes * MINUTE,
|
|
65
|
+
)
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async materializeStats() {
|
|
70
|
+
const lockResult = await sql<{
|
|
71
|
+
locked: boolean
|
|
72
|
+
}>`SELECT pg_try_advisory_lock(${ADVISORY_LOCK_ID}) as locked`.execute(
|
|
73
|
+
this.db.db,
|
|
74
|
+
)
|
|
75
|
+
const acquired = lockResult.rows[0]?.locked === true
|
|
76
|
+
if (!acquired) {
|
|
77
|
+
dbLogger.info(
|
|
78
|
+
'stats materialization skipped, another instance holds lock',
|
|
79
|
+
)
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const statsService = this.reportStatsServiceCreator(this.db)
|
|
85
|
+
await statsService.materializeAll()
|
|
86
|
+
} finally {
|
|
87
|
+
await sql`SELECT pg_advisory_unlock(${ADVISORY_LOCK_ID})`.execute(
|
|
88
|
+
this.db.db,
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async destroy() {
|
|
94
|
+
this.destroyed = true
|
|
95
|
+
if (this.timer) {
|
|
96
|
+
clearTimeout(this.timer)
|
|
97
|
+
this.timer = undefined
|
|
98
|
+
}
|
|
99
|
+
await this.processingPromise
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -2,6 +2,7 @@ import { HOUR } from '@atproto/common'
|
|
|
2
2
|
import { Database } from '../db'
|
|
3
3
|
import { dbLogger } from '../logger'
|
|
4
4
|
import { StrikeServiceCreator } from '../mod-service/strike'
|
|
5
|
+
import { getJobCursor, initJobCursor, updateJobCursor } from './job-cursor'
|
|
5
6
|
|
|
6
7
|
const JOB_NAME = 'strike_expiry'
|
|
7
8
|
|
|
@@ -40,32 +41,15 @@ export class StrikeExpiryProcessor {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
async initializeCursor() {
|
|
43
|
-
await this.db
|
|
44
|
-
.insertInto('job_cursor')
|
|
45
|
-
.values({
|
|
46
|
-
job: JOB_NAME,
|
|
47
|
-
cursor: null,
|
|
48
|
-
})
|
|
49
|
-
.onConflict((oc) => oc.doNothing())
|
|
50
|
-
.execute()
|
|
44
|
+
await initJobCursor(this.db, JOB_NAME)
|
|
51
45
|
}
|
|
52
46
|
|
|
53
47
|
async getCursor(): Promise<string | null> {
|
|
54
|
-
|
|
55
|
-
.selectFrom('job_cursor')
|
|
56
|
-
.select('cursor')
|
|
57
|
-
.where('job', '=', JOB_NAME)
|
|
58
|
-
.executeTakeFirst()
|
|
59
|
-
|
|
60
|
-
return entry?.cursor || null
|
|
48
|
+
return getJobCursor(this.db, JOB_NAME)
|
|
61
49
|
}
|
|
62
50
|
|
|
63
51
|
async updateCursor(cursor: string): Promise<void> {
|
|
64
|
-
await this.db
|
|
65
|
-
.updateTable('job_cursor')
|
|
66
|
-
.set({ cursor })
|
|
67
|
-
.where('job', '=', JOB_NAME)
|
|
68
|
-
.execute()
|
|
52
|
+
await updateJobCursor(this.db, JOB_NAME, cursor)
|
|
69
53
|
}
|
|
70
54
|
|
|
71
55
|
async processExpiredStrikes() {
|