@atproto/ozone 0.1.173 → 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 +9 -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/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/index.d.ts +5 -0
- package/dist/db/migrations/index.d.ts.map +1 -1
- package/dist/db/migrations/index.js +6 -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/index.d.ts +6 -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 +10535 -7389
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +1789 -122
- 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/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/index.d.ts +3 -1
- package/dist/mod-service/index.d.ts.map +1 -1
- package/dist/mod-service/index.js +39 -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 +20 -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/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/index.ts +5 -0
- package/src/db/pagination.ts +85 -0
- package/src/db/schema/index.ts +10 -0
- 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 +1910 -160
- package/src/lexicon/types/app/bsky/embed/external.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/index.ts +45 -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/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
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED CODE - DO NOT MODIFY
|
|
3
|
+
*/
|
|
4
|
+
import { type ValidationResult, BlobRef } from '@atproto/lexicon'
|
|
5
|
+
import { CID } from 'multiformats/cid'
|
|
6
|
+
import { validate as _validate } from '../../../../lexicons'
|
|
7
|
+
import {
|
|
8
|
+
type $Typed,
|
|
9
|
+
is$typed as _is$typed,
|
|
10
|
+
type OmitKey,
|
|
11
|
+
} from '../../../../util'
|
|
12
|
+
import type * as ToolsOzoneReportDefs from './defs.js'
|
|
13
|
+
|
|
14
|
+
const is$typed = _is$typed,
|
|
15
|
+
validate = _validate
|
|
16
|
+
const id = 'tools.ozone.report.reassignQueue'
|
|
17
|
+
|
|
18
|
+
export type QueryParams = {}
|
|
19
|
+
|
|
20
|
+
export interface InputSchema {
|
|
21
|
+
/** ID of the report to reassign */
|
|
22
|
+
reportId: number
|
|
23
|
+
/** Target queue ID. Use -1 to unassign from any queue. */
|
|
24
|
+
queueId: number
|
|
25
|
+
/** Optional moderator-only note recorded on the resulting queueActivity as internalNote. */
|
|
26
|
+
comment?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface OutputSchema {
|
|
30
|
+
report: ToolsOzoneReportDefs.ReportView
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface HandlerInput {
|
|
34
|
+
encoding: 'application/json'
|
|
35
|
+
body: InputSchema
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface HandlerSuccess {
|
|
39
|
+
encoding: 'application/json'
|
|
40
|
+
body: OutputSchema
|
|
41
|
+
headers?: { [key: string]: string }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface HandlerError {
|
|
45
|
+
status: number
|
|
46
|
+
message?: string
|
|
47
|
+
error?:
|
|
48
|
+
| 'ReportNotFound'
|
|
49
|
+
| 'ReportClosed'
|
|
50
|
+
| 'AlreadyInTargetQueue'
|
|
51
|
+
| 'QueueNotFound'
|
|
52
|
+
| 'QueueDisabled'
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type HandlerOutput = HandlerError | HandlerSuccess
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED CODE - DO NOT MODIFY
|
|
3
|
+
*/
|
|
4
|
+
import { type ValidationResult, BlobRef } from '@atproto/lexicon'
|
|
5
|
+
import { CID } from 'multiformats/cid'
|
|
6
|
+
import { validate as _validate } from '../../../../lexicons'
|
|
7
|
+
import {
|
|
8
|
+
type $Typed,
|
|
9
|
+
is$typed as _is$typed,
|
|
10
|
+
type OmitKey,
|
|
11
|
+
} from '../../../../util'
|
|
12
|
+
|
|
13
|
+
const is$typed = _is$typed,
|
|
14
|
+
validate = _validate
|
|
15
|
+
const id = 'tools.ozone.report.refreshStats'
|
|
16
|
+
|
|
17
|
+
export type QueryParams = {}
|
|
18
|
+
|
|
19
|
+
export interface InputSchema {
|
|
20
|
+
/** Start date for recomputation, inclusive (YYYY-MM-DD). */
|
|
21
|
+
startDate: string
|
|
22
|
+
/** End date for recomputation, inclusive (YYYY-MM-DD). */
|
|
23
|
+
endDate: string
|
|
24
|
+
/** Optional list of queue IDs to recompute. Omit to recompute all groups. */
|
|
25
|
+
queueIds?: number[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface OutputSchema {}
|
|
29
|
+
|
|
30
|
+
export interface HandlerInput {
|
|
31
|
+
encoding: 'application/json'
|
|
32
|
+
body: InputSchema
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface HandlerSuccess {
|
|
36
|
+
encoding: 'application/json'
|
|
37
|
+
body: OutputSchema
|
|
38
|
+
headers?: { [key: string]: string }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface HandlerError {
|
|
42
|
+
status: number
|
|
43
|
+
message?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type HandlerOutput = HandlerError | HandlerSuccess
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED CODE - DO NOT MODIFY
|
|
3
|
+
*/
|
|
4
|
+
import { type ValidationResult, BlobRef } from '@atproto/lexicon'
|
|
5
|
+
import { CID } from 'multiformats/cid'
|
|
6
|
+
import { validate as _validate } from '../../../../lexicons'
|
|
7
|
+
import {
|
|
8
|
+
type $Typed,
|
|
9
|
+
is$typed as _is$typed,
|
|
10
|
+
type OmitKey,
|
|
11
|
+
} from '../../../../util'
|
|
12
|
+
import type * as ToolsOzoneReportDefs from './defs.js'
|
|
13
|
+
|
|
14
|
+
const is$typed = _is$typed,
|
|
15
|
+
validate = _validate
|
|
16
|
+
const id = 'tools.ozone.report.unassignModerator'
|
|
17
|
+
|
|
18
|
+
export type QueryParams = {}
|
|
19
|
+
|
|
20
|
+
export interface InputSchema {
|
|
21
|
+
/** The ID of the report to unassign. */
|
|
22
|
+
reportId: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type OutputSchema = ToolsOzoneReportDefs.AssignmentView
|
|
26
|
+
|
|
27
|
+
export interface HandlerInput {
|
|
28
|
+
encoding: 'application/json'
|
|
29
|
+
body: InputSchema
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface HandlerSuccess {
|
|
33
|
+
encoding: 'application/json'
|
|
34
|
+
body: OutputSchema
|
|
35
|
+
headers?: { [key: string]: string }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface HandlerError {
|
|
39
|
+
status: number
|
|
40
|
+
message?: string
|
|
41
|
+
error?: 'InvalidAssignment'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type HandlerOutput = HandlerError | HandlerSuccess
|
package/src/mod-service/index.ts
CHANGED
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
import {
|
|
63
63
|
ModEventType,
|
|
64
64
|
ModerationEventRow,
|
|
65
|
+
ModerationEventRowWithHandle,
|
|
65
66
|
ModerationSubjectStatusRow,
|
|
66
67
|
ModerationSubjectStatusRowWithHandle,
|
|
67
68
|
ReporterStats,
|
|
@@ -376,6 +377,29 @@ export class ModerationService {
|
|
|
376
377
|
return { cursor: keyset.packFromResult(result), events: resultWithHandles }
|
|
377
378
|
}
|
|
378
379
|
|
|
380
|
+
async getEventsByIds(ids: number[]): Promise<ModerationEventRowWithHandle[]> {
|
|
381
|
+
if (!ids.length) return []
|
|
382
|
+
|
|
383
|
+
const result = await this.db.db
|
|
384
|
+
.selectFrom('moderation_event')
|
|
385
|
+
.selectAll()
|
|
386
|
+
.where('id', 'in', ids)
|
|
387
|
+
.execute()
|
|
388
|
+
|
|
389
|
+
if (!result.length) return []
|
|
390
|
+
|
|
391
|
+
const infos = await this.views.getAccoutInfosByDid([
|
|
392
|
+
...result.map((row) => row.subjectDid),
|
|
393
|
+
...result.map((row) => row.createdBy),
|
|
394
|
+
])
|
|
395
|
+
|
|
396
|
+
return result.map((r) => ({
|
|
397
|
+
...r,
|
|
398
|
+
creatorHandle: infos.get(r.createdBy)?.handle,
|
|
399
|
+
subjectHandle: infos.get(r.subjectDid)?.handle,
|
|
400
|
+
}))
|
|
401
|
+
}
|
|
402
|
+
|
|
379
403
|
async getReport(id: number): Promise<ModerationEventRow | undefined> {
|
|
380
404
|
return await this.db.db
|
|
381
405
|
.selectFrom('moderation_event')
|
|
@@ -591,6 +615,13 @@ export class ModerationService {
|
|
|
591
615
|
if (isReportingMuted) {
|
|
592
616
|
meta.isReporterMuted = true
|
|
593
617
|
}
|
|
618
|
+
// Also capture whether the subject was muted at event-creation time, so
|
|
619
|
+
// the queue-router daemon can populate report.isMuted later without
|
|
620
|
+
// racing against subsequent mute/unmute changes.
|
|
621
|
+
const isSubjectMuted = await this.isSubjectMuted(subject.did)
|
|
622
|
+
if (isSubjectMuted) {
|
|
623
|
+
meta.isSubjectMuted = true
|
|
624
|
+
}
|
|
594
625
|
}
|
|
595
626
|
|
|
596
627
|
const subjectInfo = subject.info()
|
|
@@ -1035,7 +1066,7 @@ export class ModerationService {
|
|
|
1035
1066
|
modTool,
|
|
1036
1067
|
} = info
|
|
1037
1068
|
|
|
1038
|
-
|
|
1069
|
+
return await this.logEvent({
|
|
1039
1070
|
event: {
|
|
1040
1071
|
$type: 'tools.ozone.moderation.defs#modEventReport',
|
|
1041
1072
|
reportType: reasonType,
|
|
@@ -1046,8 +1077,6 @@ export class ModerationService {
|
|
|
1046
1077
|
createdAt,
|
|
1047
1078
|
modTool,
|
|
1048
1079
|
})
|
|
1049
|
-
|
|
1050
|
-
return result
|
|
1051
1080
|
}
|
|
1052
1081
|
|
|
1053
1082
|
async getSubjectStatuses({
|
|
@@ -1425,6 +1454,19 @@ export class ModerationService {
|
|
|
1425
1454
|
return !!result
|
|
1426
1455
|
}
|
|
1427
1456
|
|
|
1457
|
+
// Check if a subject (the account being reported) has an active mute
|
|
1458
|
+
async isSubjectMuted(did: string) {
|
|
1459
|
+
const result = await this.db.db
|
|
1460
|
+
.selectFrom('moderation_subject_status')
|
|
1461
|
+
.where('did', '=', did)
|
|
1462
|
+
.where('recordPath', '=', '')
|
|
1463
|
+
.where('muteUntil', '>', new Date().toISOString())
|
|
1464
|
+
.select(sql`true`.as('status'))
|
|
1465
|
+
.executeTakeFirst()
|
|
1466
|
+
|
|
1467
|
+
return !!result
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1428
1470
|
async formatAndCreateLabels(
|
|
1429
1471
|
uri: string,
|
|
1430
1472
|
cid: string | null,
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
import { sql } from 'kysely'
|
|
2
|
+
import { AtUri } from '@atproto/syntax'
|
|
3
|
+
import { Database } from '../db'
|
|
4
|
+
import { Report } from '../db/schema/report'
|
|
5
|
+
import { QueryParams } from '../lexicon/types/tools/ozone/report/queryReports'
|
|
6
|
+
import {
|
|
7
|
+
AlreadyInTargetState,
|
|
8
|
+
InvalidStateTransition,
|
|
9
|
+
handleReportUpdate,
|
|
10
|
+
} from '../report/handle-report-update'
|
|
11
|
+
|
|
12
|
+
export type ReportWithEvent = Omit<Report, 'id'> & {
|
|
13
|
+
id: number
|
|
14
|
+
subjectDid: string
|
|
15
|
+
subjectUri: string | null
|
|
16
|
+
subjectCid: string | null
|
|
17
|
+
reportedBy: string
|
|
18
|
+
comment: string | null
|
|
19
|
+
meta: Record<string, string | boolean | number> | null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type QueryReportsResult = {
|
|
23
|
+
reports: ReportWithEvent[]
|
|
24
|
+
cursor: string | undefined
|
|
25
|
+
}
|
|
26
|
+
function reportQuery(db: Database) {
|
|
27
|
+
return db.db
|
|
28
|
+
.selectFrom('report as r')
|
|
29
|
+
.innerJoin('moderation_event as me', 'me.id', 'r.eventId')
|
|
30
|
+
.where('me.action', '=', 'tools.ozone.moderation.defs#modEventReport')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function queryReports(
|
|
34
|
+
db: Database,
|
|
35
|
+
params: QueryParams,
|
|
36
|
+
): Promise<QueryReportsResult> {
|
|
37
|
+
let builder = reportQuery(db)
|
|
38
|
+
|
|
39
|
+
if (params.queueId !== undefined) {
|
|
40
|
+
builder = builder.where('r.queueId', '=', params.queueId)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
builder = builder.where('r.status', '=', params.status)
|
|
44
|
+
|
|
45
|
+
if (params.subject) {
|
|
46
|
+
const isRecord = params.subject.startsWith('at://')
|
|
47
|
+
if (isRecord) {
|
|
48
|
+
const uri = new AtUri(params.subject)
|
|
49
|
+
builder = builder
|
|
50
|
+
.where('r.did', '=', uri.host)
|
|
51
|
+
.where('r.recordPath', '=', `${uri.collection}/${uri.rkey}`)
|
|
52
|
+
} else {
|
|
53
|
+
builder = builder
|
|
54
|
+
.where('r.did', '=', params.subject)
|
|
55
|
+
.where('r.recordPath', '=', '')
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (params.did) {
|
|
60
|
+
builder = builder.where('r.did', '=', params.did)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (params.subjectType) {
|
|
64
|
+
const normalizedType = params.subjectType as 'account' | 'record'
|
|
65
|
+
if (normalizedType === 'account') {
|
|
66
|
+
builder = builder.where('r.recordPath', '=', '')
|
|
67
|
+
} else if (normalizedType === 'record') {
|
|
68
|
+
builder = builder.where('r.recordPath', '!=', '')
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (params.collections?.length) {
|
|
73
|
+
// Filter by collection prefix on recordPath (uses text_pattern_ops index)
|
|
74
|
+
const collectionConditions = params.collections.map(
|
|
75
|
+
(collection) => sql`r."recordPath" LIKE ${`${collection}/%`}`,
|
|
76
|
+
)
|
|
77
|
+
builder = builder.where(sql`(${sql.join(collectionConditions, sql` OR `)})`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (params.reportTypes?.length) {
|
|
81
|
+
builder = builder.where('r.reportType', 'in', params.reportTypes)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (params.isMuted !== undefined) {
|
|
85
|
+
builder = builder.where('r.isMuted', '=', params.isMuted)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (params.reportedAfter) {
|
|
89
|
+
builder = builder.where('r.createdAt', '>', params.reportedAfter)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (params.reportedBefore) {
|
|
93
|
+
builder = builder.where('r.createdAt', '<', params.reportedBefore)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (params.assignedTo) {
|
|
97
|
+
builder = builder.where('r.assignedTo', '=', params.assignedTo)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const sortField = params.sortField ?? 'createdAt'
|
|
101
|
+
const sortDirection = params.sortDirection ?? 'desc'
|
|
102
|
+
|
|
103
|
+
builder = builder
|
|
104
|
+
.orderBy(
|
|
105
|
+
sortField === 'updatedAt' ? 'r.updatedAt' : 'r.createdAt',
|
|
106
|
+
sortDirection,
|
|
107
|
+
)
|
|
108
|
+
.orderBy('r.id', 'desc')
|
|
109
|
+
|
|
110
|
+
const limit = params.limit ?? 50
|
|
111
|
+
if (params.cursor) {
|
|
112
|
+
const [sortValue, id] = params.cursor.split('::')
|
|
113
|
+
const sortCol = sortField === 'updatedAt' ? 'r.updatedAt' : 'r.createdAt'
|
|
114
|
+
if (sortDirection === 'desc') {
|
|
115
|
+
builder = builder.where(sql`(
|
|
116
|
+
${sql.ref(sortCol)} < ${sortValue}
|
|
117
|
+
OR (${sql.ref(sortCol)} = ${sortValue} AND r.id < ${Number(id)})
|
|
118
|
+
)`)
|
|
119
|
+
} else {
|
|
120
|
+
builder = builder.where(sql`(
|
|
121
|
+
${sql.ref(sortCol)} > ${sortValue}
|
|
122
|
+
OR (${sql.ref(sortCol)} = ${sortValue} AND r.id > ${Number(id)})
|
|
123
|
+
)`)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const finalQuery = builder
|
|
128
|
+
.selectAll('r')
|
|
129
|
+
.select([
|
|
130
|
+
'me.subjectDid',
|
|
131
|
+
'me.subjectUri',
|
|
132
|
+
'me.subjectCid',
|
|
133
|
+
'me.createdBy as reportedBy',
|
|
134
|
+
'me.comment',
|
|
135
|
+
'me.meta',
|
|
136
|
+
])
|
|
137
|
+
.limit(limit + 1)
|
|
138
|
+
|
|
139
|
+
const reports = await finalQuery.execute()
|
|
140
|
+
|
|
141
|
+
let cursor: string | undefined
|
|
142
|
+
const hasMore = reports.length > limit
|
|
143
|
+
if (hasMore) {
|
|
144
|
+
const last = reports[limit - 1]
|
|
145
|
+
const sortValue =
|
|
146
|
+
sortField === 'updatedAt' ? last.updatedAt : last.createdAt
|
|
147
|
+
cursor = `${sortValue}::${last.id}`
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const reportsToReturn = hasMore ? reports.slice(0, limit) : reports
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
reports: reportsToReturn,
|
|
154
|
+
cursor,
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function getReportById(
|
|
159
|
+
db: Database,
|
|
160
|
+
id: number,
|
|
161
|
+
): Promise<ReportWithEvent | undefined> {
|
|
162
|
+
return reportQuery(db)
|
|
163
|
+
.where('r.id', '=', id)
|
|
164
|
+
.selectAll('r')
|
|
165
|
+
.select([
|
|
166
|
+
'me.subjectDid',
|
|
167
|
+
'me.subjectUri',
|
|
168
|
+
'me.subjectCid',
|
|
169
|
+
'me.createdBy as reportedBy',
|
|
170
|
+
'me.comment',
|
|
171
|
+
'me.meta',
|
|
172
|
+
])
|
|
173
|
+
.executeTakeFirst()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export async function getLatestReport(
|
|
177
|
+
db: Database,
|
|
178
|
+
): Promise<ReportWithEvent | undefined> {
|
|
179
|
+
return reportQuery(db)
|
|
180
|
+
.selectAll('r')
|
|
181
|
+
.select([
|
|
182
|
+
'me.subjectDid',
|
|
183
|
+
'me.subjectUri',
|
|
184
|
+
'me.subjectCid',
|
|
185
|
+
'me.createdBy as reportedBy',
|
|
186
|
+
'me.comment',
|
|
187
|
+
'me.meta',
|
|
188
|
+
])
|
|
189
|
+
.orderBy('r.id', 'desc')
|
|
190
|
+
.limit(1)
|
|
191
|
+
.executeTakeFirst()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export type FindReportsForSubjectParams = {
|
|
195
|
+
subjectDid: string
|
|
196
|
+
subjectUri?: string | null
|
|
197
|
+
reportIds?: number[]
|
|
198
|
+
reportTypes?: string[]
|
|
199
|
+
targetAll?: boolean
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export type ReportResult = {
|
|
203
|
+
id: number
|
|
204
|
+
eventId: number
|
|
205
|
+
queueId: number | null
|
|
206
|
+
queuedAt: string | null
|
|
207
|
+
actionEventIds: number[] | null
|
|
208
|
+
actionNote: string | null
|
|
209
|
+
isMuted: boolean
|
|
210
|
+
status: string
|
|
211
|
+
createdAt: string
|
|
212
|
+
updatedAt: string
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export async function findReportsForSubject(
|
|
216
|
+
db: Database,
|
|
217
|
+
params: FindReportsForSubjectParams,
|
|
218
|
+
): Promise<ReportResult[]> {
|
|
219
|
+
let builder = reportQuery(db).where('r.did', '=', params.subjectDid)
|
|
220
|
+
|
|
221
|
+
// Filter by subject URI (if provided, match exactly; if null/undefined, match repo-level)
|
|
222
|
+
if (params.subjectUri) {
|
|
223
|
+
const uri = new AtUri(params.subjectUri)
|
|
224
|
+
builder = builder.where(
|
|
225
|
+
'r.recordPath',
|
|
226
|
+
'=',
|
|
227
|
+
`${uri.collection}/${uri.rkey}`,
|
|
228
|
+
)
|
|
229
|
+
} else {
|
|
230
|
+
builder = builder.where('r.recordPath', '=', '')
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (params.targetAll) {
|
|
234
|
+
// Target all open/escalated reports on the subject
|
|
235
|
+
builder = builder.where('r.status', 'not in', ['closed'])
|
|
236
|
+
} else if (params.reportIds?.length) {
|
|
237
|
+
// Target specific report IDs — still enforce state transition rules
|
|
238
|
+
builder = builder
|
|
239
|
+
.where('r.id', 'in', params.reportIds)
|
|
240
|
+
.where('r.status', 'not in', ['closed'])
|
|
241
|
+
} else if (params.reportTypes?.length) {
|
|
242
|
+
// Target reports matching specific report types
|
|
243
|
+
builder = builder
|
|
244
|
+
.where('r.reportType', 'in', params.reportTypes)
|
|
245
|
+
.where('r.status', 'not in', ['closed'])
|
|
246
|
+
} else {
|
|
247
|
+
// No targeting criteria provided
|
|
248
|
+
return []
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const reports = await builder.selectAll('r').execute()
|
|
252
|
+
|
|
253
|
+
return reports
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export type ProcessReportActionParams = {
|
|
257
|
+
db: Database
|
|
258
|
+
reportAction: {
|
|
259
|
+
ids?: number[]
|
|
260
|
+
types?: string[]
|
|
261
|
+
all?: boolean
|
|
262
|
+
note?: string
|
|
263
|
+
}
|
|
264
|
+
subjectDid: string
|
|
265
|
+
subjectUri: string | null
|
|
266
|
+
eventId: number
|
|
267
|
+
eventType: string
|
|
268
|
+
createdBy: string
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Validates and processes a report action by:
|
|
273
|
+
* 1. Finding matching reports based on targeting criteria
|
|
274
|
+
* 2. Validating that specified report IDs exist and belong to the subject
|
|
275
|
+
* 3. Bulk-updating reports with the action event ID, note, and status
|
|
276
|
+
* 4. Bulk-inserting a report_activity row for each updated report
|
|
277
|
+
*
|
|
278
|
+
* @throws InvalidRequestError if validation fails
|
|
279
|
+
*/
|
|
280
|
+
export async function processReportAction(
|
|
281
|
+
params: ProcessReportActionParams,
|
|
282
|
+
): Promise<number> {
|
|
283
|
+
const {
|
|
284
|
+
db,
|
|
285
|
+
reportAction,
|
|
286
|
+
subjectDid,
|
|
287
|
+
subjectUri,
|
|
288
|
+
eventId,
|
|
289
|
+
eventType,
|
|
290
|
+
createdBy,
|
|
291
|
+
} = params
|
|
292
|
+
|
|
293
|
+
// Find reports matching the criteria
|
|
294
|
+
const matchingReports = await findReportsForSubject(db, {
|
|
295
|
+
subjectDid,
|
|
296
|
+
subjectUri,
|
|
297
|
+
reportIds: reportAction.ids,
|
|
298
|
+
reportTypes: reportAction.types,
|
|
299
|
+
targetAll: reportAction.all,
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
// Validate that reports were found for ids and types
|
|
303
|
+
if (matchingReports.length === 0) {
|
|
304
|
+
if (reportAction.ids?.length) {
|
|
305
|
+
throw new Error(
|
|
306
|
+
'No matching reports found for the specified report IDs on this subject',
|
|
307
|
+
)
|
|
308
|
+
} else if (reportAction.types?.length) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
'No matching reports found for the specified report types on this subject',
|
|
311
|
+
)
|
|
312
|
+
}
|
|
313
|
+
// For 'all', it's okay if no reports exist
|
|
314
|
+
return 0
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Validate that all specified report IDs were found
|
|
318
|
+
if (reportAction.ids?.length) {
|
|
319
|
+
const foundIds = new Set(matchingReports.map((r) => r.id))
|
|
320
|
+
const requestedIds = new Set(reportAction.ids)
|
|
321
|
+
const missingIds = [...requestedIds].filter((id) => !foundIds.has(id))
|
|
322
|
+
|
|
323
|
+
if (missingIds.length > 0) {
|
|
324
|
+
throw new Error(
|
|
325
|
+
`Report IDs ${missingIds.join(', ')} do not exist, are already closed, or do not belong to this subject`,
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Determine per-report transitions via the pure state machine.
|
|
331
|
+
// Skip reports whose current status doesn't allow the transition.
|
|
332
|
+
const validUpdates: {
|
|
333
|
+
id: number
|
|
334
|
+
nextStatus: string
|
|
335
|
+
activityType: string
|
|
336
|
+
previousStatus: string
|
|
337
|
+
}[] = []
|
|
338
|
+
|
|
339
|
+
for (const report of matchingReports) {
|
|
340
|
+
try {
|
|
341
|
+
const result = handleReportUpdate(report.status, {
|
|
342
|
+
type: 'event',
|
|
343
|
+
eventType,
|
|
344
|
+
})
|
|
345
|
+
if (result.nextStatus && result.activity) {
|
|
346
|
+
validUpdates.push({
|
|
347
|
+
id: report.id,
|
|
348
|
+
nextStatus: result.nextStatus,
|
|
349
|
+
activityType: result.activity.activityType,
|
|
350
|
+
previousStatus: result.activity.previousStatus,
|
|
351
|
+
})
|
|
352
|
+
}
|
|
353
|
+
} catch (err) {
|
|
354
|
+
if (
|
|
355
|
+
err instanceof AlreadyInTargetState ||
|
|
356
|
+
err instanceof InvalidStateTransition
|
|
357
|
+
) {
|
|
358
|
+
// Skip reports that can't transition — silent per design
|
|
359
|
+
continue
|
|
360
|
+
}
|
|
361
|
+
throw err
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (!validUpdates.length) {
|
|
366
|
+
return 0
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const now = new Date().toISOString()
|
|
370
|
+
const updateIds = validUpdates.map((u) => u.id)
|
|
371
|
+
|
|
372
|
+
// Bulk UPDATE reports that passed validation
|
|
373
|
+
// All valid reports share the same target status since they come from the
|
|
374
|
+
// same event type, so a single UPDATE is sufficient.
|
|
375
|
+
const status = validUpdates[0].nextStatus
|
|
376
|
+
const closedAt = status === 'closed' ? now : null
|
|
377
|
+
await db.db
|
|
378
|
+
.updateTable('report')
|
|
379
|
+
.set({
|
|
380
|
+
actionEventIds: sql`COALESCE("actionEventIds", '[]'::jsonb) || ${JSON.stringify(eventId)}::jsonb`,
|
|
381
|
+
actionNote: reportAction.note ?? null,
|
|
382
|
+
status,
|
|
383
|
+
updatedAt: now,
|
|
384
|
+
closedAt,
|
|
385
|
+
})
|
|
386
|
+
.where('id', 'in', updateIds)
|
|
387
|
+
.execute()
|
|
388
|
+
|
|
389
|
+
// Bulk INSERT one activity per updated report
|
|
390
|
+
await db.db
|
|
391
|
+
.insertInto('report_activity')
|
|
392
|
+
.values(
|
|
393
|
+
validUpdates.map((u) => ({
|
|
394
|
+
reportId: u.id,
|
|
395
|
+
activityType: u.activityType,
|
|
396
|
+
previousStatus: u.previousStatus,
|
|
397
|
+
internalNote: null,
|
|
398
|
+
publicNote: reportAction.note ?? null,
|
|
399
|
+
meta: null,
|
|
400
|
+
isAutomated: false,
|
|
401
|
+
createdBy,
|
|
402
|
+
createdAt: now,
|
|
403
|
+
})),
|
|
404
|
+
)
|
|
405
|
+
.execute()
|
|
406
|
+
|
|
407
|
+
return validUpdates.length
|
|
408
|
+
}
|