@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,430 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QueueService = void 0;
|
|
4
|
+
exports.findMatchingQueue = findMatchingQueue;
|
|
5
|
+
const kysely_1 = require("kysely");
|
|
6
|
+
const syntax_1 = require("@atproto/syntax");
|
|
7
|
+
const xrpc_server_1 = require("@atproto/xrpc-server");
|
|
8
|
+
const pagination_1 = require("../db/pagination");
|
|
9
|
+
const types_1 = require("../db/types");
|
|
10
|
+
const handle_report_update_1 = require("../report/handle-report-update");
|
|
11
|
+
const stats_1 = require("../report/stats");
|
|
12
|
+
const views_1 = require("../report/views");
|
|
13
|
+
const MOD_EVENT_REPORT_ACTION = 'tools.ozone.moderation.defs#modEventReport';
|
|
14
|
+
const REASON_OTHER = 'com.atproto.moderation.defs#reasonOther';
|
|
15
|
+
function resolveAssignment(subjectType, collection, reportType, queues, now) {
|
|
16
|
+
const matched = findMatchingQueue(queues, subjectType, collection, reportType);
|
|
17
|
+
if (matched)
|
|
18
|
+
return { queueId: matched.id, queuedAt: now, status: 'queued' };
|
|
19
|
+
return { queueId: -1, queuedAt: null, status: 'open' };
|
|
20
|
+
}
|
|
21
|
+
class QueueService {
|
|
22
|
+
constructor(db) {
|
|
23
|
+
Object.defineProperty(this, "db", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: db
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
static creator() {
|
|
31
|
+
return (db) => new QueueService(db);
|
|
32
|
+
}
|
|
33
|
+
async checkConflict({ subjectTypes, collection, reportTypes, excludeId, }) {
|
|
34
|
+
// It's not ideal to load all rows and perform in memory checks in case we end up with a LOT of queues
|
|
35
|
+
// but we are not foreseeing a lot of queue rows so this should be fine for the
|
|
36
|
+
let qb = this.db.db
|
|
37
|
+
.selectFrom('report_queue')
|
|
38
|
+
.selectAll()
|
|
39
|
+
.where('deletedAt', 'is', null);
|
|
40
|
+
if (excludeId !== undefined) {
|
|
41
|
+
qb = qb.where('id', '!=', excludeId);
|
|
42
|
+
}
|
|
43
|
+
const existingQueues = await qb.execute();
|
|
44
|
+
for (const existing of existingQueues) {
|
|
45
|
+
const subjectTypesOverlap = subjectTypes.some((st) => existing.subjectTypes.includes(st));
|
|
46
|
+
const collectionMatch = (collection ?? null) === existing.collection;
|
|
47
|
+
const reportTypesOverlap = reportTypes.some((rt) => existing.reportTypes.includes(rt));
|
|
48
|
+
if (subjectTypesOverlap && collectionMatch && reportTypesOverlap) {
|
|
49
|
+
throw new xrpc_server_1.InvalidRequestError(`Queue configuration conflicts with existing queue: ${existing.name}`, 'ConflictingQueue');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async create({ name, subjectTypes, collection, reportTypes, description, createdBy, }) {
|
|
54
|
+
const now = new Date().toISOString();
|
|
55
|
+
return await this.db.db
|
|
56
|
+
.insertInto('report_queue')
|
|
57
|
+
.values({
|
|
58
|
+
name,
|
|
59
|
+
subjectTypes: (0, types_1.jsonb)(subjectTypes),
|
|
60
|
+
collection: collection ?? null,
|
|
61
|
+
reportTypes: (0, types_1.jsonb)(reportTypes),
|
|
62
|
+
description: description ?? null,
|
|
63
|
+
createdBy,
|
|
64
|
+
enabled: true,
|
|
65
|
+
createdAt: now,
|
|
66
|
+
updatedAt: now,
|
|
67
|
+
})
|
|
68
|
+
.returningAll()
|
|
69
|
+
.executeTakeFirstOrThrow();
|
|
70
|
+
}
|
|
71
|
+
async getById(id) {
|
|
72
|
+
return await this.db.db
|
|
73
|
+
.selectFrom('report_queue')
|
|
74
|
+
.selectAll()
|
|
75
|
+
.where('id', '=', id)
|
|
76
|
+
.where('deletedAt', 'is', null)
|
|
77
|
+
.executeTakeFirst();
|
|
78
|
+
}
|
|
79
|
+
async getViewsByIds(ids) {
|
|
80
|
+
if (!ids.length)
|
|
81
|
+
return new Map();
|
|
82
|
+
const rows = await this.db.db
|
|
83
|
+
.selectFrom('report_queue')
|
|
84
|
+
.selectAll()
|
|
85
|
+
.where('id', 'in', ids)
|
|
86
|
+
.execute();
|
|
87
|
+
return new Map(rows.map((r) => [r.id, this.view(r)]));
|
|
88
|
+
}
|
|
89
|
+
async update(id, updates) {
|
|
90
|
+
const now = new Date().toISOString();
|
|
91
|
+
return await this.db.db
|
|
92
|
+
.updateTable('report_queue')
|
|
93
|
+
.set({ ...updates, updatedAt: now })
|
|
94
|
+
.where('id', '=', id)
|
|
95
|
+
.returningAll()
|
|
96
|
+
.executeTakeFirstOrThrow();
|
|
97
|
+
}
|
|
98
|
+
async delete(id) {
|
|
99
|
+
const now = new Date().toISOString();
|
|
100
|
+
await this.db.db
|
|
101
|
+
.updateTable('report_queue')
|
|
102
|
+
.set({ deletedAt: now })
|
|
103
|
+
.where('id', '=', id)
|
|
104
|
+
.execute();
|
|
105
|
+
}
|
|
106
|
+
async migrateReports(fromQueueId, toQueueId) {
|
|
107
|
+
const now = new Date().toISOString();
|
|
108
|
+
const results = await this.db.db
|
|
109
|
+
.updateTable('report')
|
|
110
|
+
.set({
|
|
111
|
+
queueId: toQueueId ?? -1,
|
|
112
|
+
queuedAt: toQueueId ? now : null,
|
|
113
|
+
updatedAt: now,
|
|
114
|
+
})
|
|
115
|
+
.where('queueId', '=', fromQueueId)
|
|
116
|
+
.where('status', '!=', 'closed')
|
|
117
|
+
.execute();
|
|
118
|
+
return results.reduce((sum, r) => sum + Number(r.numUpdatedRows), 0);
|
|
119
|
+
}
|
|
120
|
+
async list({ limit, cursor, enabled, subjectType, collection, reportTypes, }) {
|
|
121
|
+
const { ref } = this.db.db.dynamic;
|
|
122
|
+
let qb = this.db.db
|
|
123
|
+
.selectFrom('report_queue')
|
|
124
|
+
.selectAll()
|
|
125
|
+
.where('deletedAt', 'is', null);
|
|
126
|
+
if (enabled !== undefined) {
|
|
127
|
+
qb = qb.where('enabled', '=', enabled);
|
|
128
|
+
}
|
|
129
|
+
if (subjectType !== undefined) {
|
|
130
|
+
qb = qb.where((0, kysely_1.sql) `"subjectTypes" @> ${(0, types_1.jsonb)([subjectType])}`);
|
|
131
|
+
}
|
|
132
|
+
if (collection !== undefined) {
|
|
133
|
+
qb = qb.where('collection', '=', collection);
|
|
134
|
+
}
|
|
135
|
+
if (reportTypes && reportTypes.length > 0) {
|
|
136
|
+
const conditions = reportTypes.map((t) => (0, kysely_1.sql) `"reportTypes" @> ${(0, types_1.jsonb)([t])}`);
|
|
137
|
+
qb = qb.where((0, kysely_1.sql) `(${kysely_1.sql.join(conditions, (0, kysely_1.sql) ` OR `)})`);
|
|
138
|
+
}
|
|
139
|
+
const keyset = new pagination_1.TimeIdKeyset(ref('createdAt'), ref('id'));
|
|
140
|
+
const paginatedBuilder = (0, pagination_1.paginate)(qb, {
|
|
141
|
+
limit,
|
|
142
|
+
cursor,
|
|
143
|
+
keyset,
|
|
144
|
+
direction: 'asc',
|
|
145
|
+
tryIndex: true,
|
|
146
|
+
});
|
|
147
|
+
const queues = await paginatedBuilder.execute();
|
|
148
|
+
return {
|
|
149
|
+
queues,
|
|
150
|
+
cursor: keyset.packFromResult(queues),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
view(queue) {
|
|
154
|
+
return {
|
|
155
|
+
id: queue.id,
|
|
156
|
+
name: queue.name,
|
|
157
|
+
subjectTypes: queue.subjectTypes,
|
|
158
|
+
collection: queue.collection ?? undefined,
|
|
159
|
+
reportTypes: queue.reportTypes,
|
|
160
|
+
description: queue.description ?? undefined,
|
|
161
|
+
createdBy: queue.createdBy,
|
|
162
|
+
createdAt: queue.createdAt,
|
|
163
|
+
updatedAt: queue.updatedAt,
|
|
164
|
+
enabled: queue.enabled,
|
|
165
|
+
deletedAt: queue.deletedAt ?? undefined,
|
|
166
|
+
stats: {
|
|
167
|
+
pendingCount: 0,
|
|
168
|
+
actionedCount: 0,
|
|
169
|
+
escalatedCount: 0,
|
|
170
|
+
inboundCount: 0,
|
|
171
|
+
actionRate: 0,
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
async viewsWithStats(queues) {
|
|
176
|
+
const statsService = new stats_1.ReportStatsService(this.db);
|
|
177
|
+
const queueIds = queues.map((q) => q.id);
|
|
178
|
+
const statsMap = await statsService.getLiveStatsForQueues(queueIds);
|
|
179
|
+
return queues.map((queue) => {
|
|
180
|
+
const view = this.view(queue);
|
|
181
|
+
view.stats = (0, views_1.viewQueueStats)(statsMap.get(queue.id));
|
|
182
|
+
return view;
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Re-route a range of existing reports against the current queue config.
|
|
187
|
+
* Used by the manual `tools.ozone.queue.routeReports` endpoint to pick up
|
|
188
|
+
* reports after queues are created or modified. New reports are inserted
|
|
189
|
+
* by the daemon via `insertReportsFromEvents`, not here.
|
|
190
|
+
*/
|
|
191
|
+
async assignReportBatch(params, opts) {
|
|
192
|
+
const { queues } = await this.list({ limit: 1000, enabled: true });
|
|
193
|
+
if (!queues.length) {
|
|
194
|
+
return { processed: 0, assigned: 0, unmatched: 0, maxId: 0 };
|
|
195
|
+
}
|
|
196
|
+
let query = this.db.db
|
|
197
|
+
.selectFrom('report as r')
|
|
198
|
+
.select([
|
|
199
|
+
'r.id',
|
|
200
|
+
'r.status',
|
|
201
|
+
'r.reportType',
|
|
202
|
+
'r.recordPath',
|
|
203
|
+
'r.subjectMessageId',
|
|
204
|
+
])
|
|
205
|
+
.where('r.status', '!=', 'closed')
|
|
206
|
+
.where('r.id', '>=', params.start)
|
|
207
|
+
.where('r.id', '<=', params.end)
|
|
208
|
+
.orderBy('r.id', 'asc')
|
|
209
|
+
.limit(params.limit);
|
|
210
|
+
if (opts?.includeUnmatched) {
|
|
211
|
+
query = query.where((qb) => {
|
|
212
|
+
return qb.orWhere('r.queueId', 'is', null).orWhere('r.queueId', '=', -1);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
query = query.where('r.queueId', 'is', null);
|
|
217
|
+
}
|
|
218
|
+
const reports = await query.execute();
|
|
219
|
+
if (!reports.length) {
|
|
220
|
+
return { processed: 0, assigned: 0, unmatched: 0, maxId: 0 };
|
|
221
|
+
}
|
|
222
|
+
const now = new Date().toISOString();
|
|
223
|
+
const matchedByQueue = new Map();
|
|
224
|
+
const unmatchedIds = [];
|
|
225
|
+
let maxReportId = 0;
|
|
226
|
+
for (const report of reports) {
|
|
227
|
+
const subjectType = report.subjectMessageId
|
|
228
|
+
? 'message'
|
|
229
|
+
: report.recordPath
|
|
230
|
+
? 'record'
|
|
231
|
+
: 'account';
|
|
232
|
+
// recordPath is 'collection/rkey' for records, '' for accounts
|
|
233
|
+
const slashIdx = report.recordPath.indexOf('/');
|
|
234
|
+
const collection = slashIdx > 0 ? report.recordPath.slice(0, slashIdx) : null;
|
|
235
|
+
const assignment = resolveAssignment(subjectType, collection, report.reportType, queues, now);
|
|
236
|
+
if (assignment.queueId !== -1) {
|
|
237
|
+
// Existing-row UPDATE path uses handleReportUpdate so that already
|
|
238
|
+
// escalated/closed/etc. reports keep their status — only open → queued
|
|
239
|
+
// transitions emit a status change and an activity row.
|
|
240
|
+
const result = (0, handle_report_update_1.handleReportUpdate)(report.status, { type: 'queue' });
|
|
241
|
+
const group = matchedByQueue.get(assignment.queueId) ?? [];
|
|
242
|
+
group.push({
|
|
243
|
+
id: report.id,
|
|
244
|
+
queueId: assignment.queueId,
|
|
245
|
+
nextStatus: result.nextStatus,
|
|
246
|
+
activity: result.activity,
|
|
247
|
+
});
|
|
248
|
+
matchedByQueue.set(assignment.queueId, group);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
unmatchedIds.push(report.id);
|
|
252
|
+
}
|
|
253
|
+
if (report.id > maxReportId)
|
|
254
|
+
maxReportId = report.id;
|
|
255
|
+
}
|
|
256
|
+
// Bulk UPDATE matched reports — split by whether status should change.
|
|
257
|
+
// handleReportUpdate returns nextStatus only for open → queued;
|
|
258
|
+
// other statuses keep their current status but still get routed.
|
|
259
|
+
for (const [queueId, group] of matchedByQueue) {
|
|
260
|
+
const withTransition = group
|
|
261
|
+
.filter((r) => r.nextStatus !== null)
|
|
262
|
+
.map((r) => r.id);
|
|
263
|
+
const withoutTransition = group
|
|
264
|
+
.filter((r) => r.nextStatus === null)
|
|
265
|
+
.map((r) => r.id);
|
|
266
|
+
if (withTransition.length) {
|
|
267
|
+
await this.db.db
|
|
268
|
+
.updateTable('report')
|
|
269
|
+
.set({ queueId, queuedAt: now, status: 'queued', updatedAt: now })
|
|
270
|
+
.where('id', 'in', withTransition)
|
|
271
|
+
.execute();
|
|
272
|
+
}
|
|
273
|
+
if (withoutTransition.length) {
|
|
274
|
+
await this.db.db
|
|
275
|
+
.updateTable('report')
|
|
276
|
+
.set({ queueId, queuedAt: now, updatedAt: now })
|
|
277
|
+
.where('id', 'in', withoutTransition)
|
|
278
|
+
.execute();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// Bulk UPDATE unmatched reports — status stays unchanged
|
|
282
|
+
if (unmatchedIds.length) {
|
|
283
|
+
await this.db.db
|
|
284
|
+
.updateTable('report')
|
|
285
|
+
.set({ queueId: -1, queuedAt: null, updatedAt: now })
|
|
286
|
+
.where('id', 'in', unmatchedIds)
|
|
287
|
+
.execute();
|
|
288
|
+
}
|
|
289
|
+
// Bulk INSERT activities for matched reports that changed status.
|
|
290
|
+
if (opts?.serviceDid) {
|
|
291
|
+
const withActivities = [...matchedByQueue.values()]
|
|
292
|
+
.flat()
|
|
293
|
+
.filter((r) => r.activity !== null);
|
|
294
|
+
if (withActivities.length) {
|
|
295
|
+
await this.db.db
|
|
296
|
+
.insertInto('report_activity')
|
|
297
|
+
.values(withActivities.map((r) => ({
|
|
298
|
+
reportId: r.id,
|
|
299
|
+
activityType: r.activity.activityType,
|
|
300
|
+
previousStatus: r.activity.previousStatus,
|
|
301
|
+
internalNote: null,
|
|
302
|
+
publicNote: null,
|
|
303
|
+
meta: null,
|
|
304
|
+
isAutomated: true,
|
|
305
|
+
createdBy: opts.serviceDid,
|
|
306
|
+
createdAt: now,
|
|
307
|
+
})))
|
|
308
|
+
.execute();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const assigned = [...matchedByQueue.values()].reduce((sum, g) => sum + g.length, 0);
|
|
312
|
+
return {
|
|
313
|
+
processed: reports.length,
|
|
314
|
+
assigned,
|
|
315
|
+
unmatched: unmatchedIds.length,
|
|
316
|
+
maxId: maxReportId,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Read newly-created modEventReport rows from `moderation_event` and
|
|
321
|
+
* insert corresponding `report` rows with `queueId` already resolved.
|
|
322
|
+
* Used by the queue-router daemon. Idempotent via `ON CONFLICT (eventId)
|
|
323
|
+
* DO NOTHING` — safe to re-run on the same range.
|
|
324
|
+
*
|
|
325
|
+
* Even when no queues are configured, report rows are still inserted with
|
|
326
|
+
* `queueId = -1` so the invariant "every modEventReport has a `report` row"
|
|
327
|
+
* holds.
|
|
328
|
+
*/
|
|
329
|
+
async insertReportsFromEvents(params) {
|
|
330
|
+
const { queues } = await this.list({ limit: 1000, enabled: true });
|
|
331
|
+
let query = this.db.db
|
|
332
|
+
.selectFrom('moderation_event')
|
|
333
|
+
.select([
|
|
334
|
+
'id',
|
|
335
|
+
'subjectDid',
|
|
336
|
+
'subjectUri',
|
|
337
|
+
'subjectMessageId',
|
|
338
|
+
'meta',
|
|
339
|
+
'createdAt',
|
|
340
|
+
])
|
|
341
|
+
.where('action', '=', MOD_EVENT_REPORT_ACTION)
|
|
342
|
+
.orderBy('id', 'asc')
|
|
343
|
+
.limit(params.limit);
|
|
344
|
+
if (params.cursor !== null) {
|
|
345
|
+
query = query.where('id', '>', params.cursor);
|
|
346
|
+
}
|
|
347
|
+
const events = await query.execute();
|
|
348
|
+
if (!events.length) {
|
|
349
|
+
return { processed: 0, assigned: 0, unmatched: 0, maxEventId: 0 };
|
|
350
|
+
}
|
|
351
|
+
const now = new Date().toISOString();
|
|
352
|
+
let maxEventId = 0;
|
|
353
|
+
let assigned = 0;
|
|
354
|
+
let unmatched = 0;
|
|
355
|
+
const rows = events.map((event) => {
|
|
356
|
+
const subjectType = event.subjectMessageId
|
|
357
|
+
? 'message'
|
|
358
|
+
: event.subjectUri
|
|
359
|
+
? 'record'
|
|
360
|
+
: 'account';
|
|
361
|
+
let collection = null;
|
|
362
|
+
let recordPath = '';
|
|
363
|
+
if (event.subjectUri) {
|
|
364
|
+
const uri = new syntax_1.AtUri(event.subjectUri);
|
|
365
|
+
collection = uri.collection;
|
|
366
|
+
recordPath = `${uri.collection}/${uri.rkey}`;
|
|
367
|
+
}
|
|
368
|
+
const reportType = event.meta?.reportType ?? REASON_OTHER;
|
|
369
|
+
const assignment = resolveAssignment(subjectType, collection, reportType, queues, now);
|
|
370
|
+
if (assignment.queueId === -1)
|
|
371
|
+
unmatched++;
|
|
372
|
+
else
|
|
373
|
+
assigned++;
|
|
374
|
+
if (event.id > maxEventId)
|
|
375
|
+
maxEventId = event.id;
|
|
376
|
+
const isMuted = !!event.meta?.isReporterMuted || !!event.meta?.isSubjectMuted;
|
|
377
|
+
return {
|
|
378
|
+
eventId: event.id,
|
|
379
|
+
queueId: assignment.queueId,
|
|
380
|
+
queuedAt: assignment.queuedAt,
|
|
381
|
+
actionEventIds: null,
|
|
382
|
+
actionNote: null,
|
|
383
|
+
isMuted,
|
|
384
|
+
status: assignment.status,
|
|
385
|
+
reportType,
|
|
386
|
+
did: event.subjectDid,
|
|
387
|
+
recordPath,
|
|
388
|
+
subjectMessageId: event.subjectMessageId,
|
|
389
|
+
createdAt: now,
|
|
390
|
+
updatedAt: now,
|
|
391
|
+
};
|
|
392
|
+
});
|
|
393
|
+
// ON CONFLICT (eventId) DO NOTHING covers any race where a report row
|
|
394
|
+
// already exists for the event (e.g. transitional code paths or retries
|
|
395
|
+
// after a crash mid-batch).
|
|
396
|
+
await this.db.db
|
|
397
|
+
.insertInto('report')
|
|
398
|
+
.values(rows)
|
|
399
|
+
.onConflict((oc) => oc.column('eventId').doNothing())
|
|
400
|
+
.execute();
|
|
401
|
+
// Activity rows are intentionally not emitted: a freshly-inserted report
|
|
402
|
+
// has no prior state to "transition" from. Activity rows record state
|
|
403
|
+
// changes, and being born already-queued is not a state change. This
|
|
404
|
+
// matches `handleReportUpdate`'s design where activity is only emitted
|
|
405
|
+
// on real transitions.
|
|
406
|
+
return {
|
|
407
|
+
processed: events.length,
|
|
408
|
+
assigned,
|
|
409
|
+
unmatched,
|
|
410
|
+
maxEventId,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
exports.QueueService = QueueService;
|
|
415
|
+
function findMatchingQueue(queues, subjectType, collection, reportType) {
|
|
416
|
+
if (!reportType)
|
|
417
|
+
return null;
|
|
418
|
+
for (const queue of queues) {
|
|
419
|
+
const subjectTypeMatch = queue.subjectTypes.includes(subjectType);
|
|
420
|
+
const collectionMatch = subjectType === 'record' && queue.collection !== null
|
|
421
|
+
? (collection ?? null) === queue.collection
|
|
422
|
+
: true;
|
|
423
|
+
const reportTypeMatch = queue.reportTypes.includes(reportType);
|
|
424
|
+
if (subjectTypeMatch && collectionMatch && reportTypeMatch) {
|
|
425
|
+
return queue;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/queue/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable, sql } from 'kysely'\nimport { ToolsOzoneQueueDefs } from '@atproto/api'\nimport { AtUri } from '@atproto/syntax'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport { Database } from '../db'\nimport { TimeIdKeyset, paginate } from '../db/pagination'\nimport { ReportQueue } from '../db/schema/report_queue'\nimport { jsonb } from '../db/types'\nimport { handleReportUpdate } from '../report/handle-report-update'\nimport { ReportStatsService } from '../report/stats'\nimport { viewQueueStats } from '../report/views'\n\nconst MOD_EVENT_REPORT_ACTION = 'tools.ozone.moderation.defs#modEventReport'\nconst REASON_OTHER = 'com.atproto.moderation.defs#reasonOther'\n\ntype SubjectType = 'account' | 'record' | 'message'\n\ntype ResolvedAssignment = {\n queueId: number\n queuedAt: string | null\n status: 'queued' | 'open'\n}\n\nfunction resolveAssignment(\n subjectType: SubjectType,\n collection: string | null,\n reportType: string,\n queues: Selectable<ReportQueue>[],\n now: string,\n): ResolvedAssignment {\n const matched = findMatchingQueue(queues, subjectType, collection, reportType)\n if (matched) return { queueId: matched.id, queuedAt: now, status: 'queued' }\n return { queueId: -1, queuedAt: null, status: 'open' }\n}\n\nexport type QueueServiceCreator = (db: Database) => QueueService\n\nexport class QueueService {\n constructor(public db: Database) {}\n\n static creator() {\n return (db: Database) => new QueueService(db)\n }\n\n async checkConflict({\n subjectTypes,\n collection,\n reportTypes,\n excludeId,\n }: {\n subjectTypes: string[]\n collection?: string | null\n reportTypes: string[]\n excludeId?: number\n }): Promise<void> {\n // It's not ideal to load all rows and perform in memory checks in case we end up with a LOT of queues\n // but we are not foreseeing a lot of queue rows so this should be fine for the\n let qb = this.db.db\n .selectFrom('report_queue')\n .selectAll()\n .where('deletedAt', 'is', null)\n\n if (excludeId !== undefined) {\n qb = qb.where('id', '!=', excludeId)\n }\n\n const existingQueues = await qb.execute()\n\n for (const existing of existingQueues) {\n const subjectTypesOverlap = subjectTypes.some((st) =>\n existing.subjectTypes.includes(st),\n )\n const collectionMatch = (collection ?? null) === existing.collection\n const reportTypesOverlap = reportTypes.some((rt) =>\n existing.reportTypes.includes(rt),\n )\n\n if (subjectTypesOverlap && collectionMatch && reportTypesOverlap) {\n throw new InvalidRequestError(\n `Queue configuration conflicts with existing queue: ${existing.name}`,\n 'ConflictingQueue',\n )\n }\n }\n }\n\n async create({\n name,\n subjectTypes,\n collection,\n reportTypes,\n description,\n createdBy,\n }: {\n name: string\n subjectTypes: string[]\n collection?: string | null\n reportTypes: string[]\n description?: string | null\n createdBy: string\n }): Promise<Selectable<ReportQueue>> {\n const now = new Date().toISOString()\n return await this.db.db\n .insertInto('report_queue')\n .values({\n name,\n subjectTypes: jsonb(subjectTypes),\n collection: collection ?? null,\n reportTypes: jsonb(reportTypes),\n description: description ?? null,\n createdBy,\n enabled: true,\n createdAt: now,\n updatedAt: now,\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n }\n\n async getById(id: number): Promise<Selectable<ReportQueue> | undefined> {\n return await this.db.db\n .selectFrom('report_queue')\n .selectAll()\n .where('id', '=', id)\n .where('deletedAt', 'is', null)\n .executeTakeFirst()\n }\n\n async getViewsByIds(\n ids: number[],\n ): Promise<Map<number, ToolsOzoneQueueDefs.QueueView>> {\n if (!ids.length) return new Map()\n const rows = await this.db.db\n .selectFrom('report_queue')\n .selectAll()\n .where('id', 'in', ids)\n .execute()\n return new Map(rows.map((r) => [r.id, this.view(r)]))\n }\n\n async update(\n id: number,\n updates: { name?: string; enabled?: boolean; description?: string },\n ): Promise<Selectable<ReportQueue>> {\n const now = new Date().toISOString()\n return await this.db.db\n .updateTable('report_queue')\n .set({ ...updates, updatedAt: now })\n .where('id', '=', id)\n .returningAll()\n .executeTakeFirstOrThrow()\n }\n\n async delete(id: number): Promise<void> {\n const now = new Date().toISOString()\n await this.db.db\n .updateTable('report_queue')\n .set({ deletedAt: now })\n .where('id', '=', id)\n .execute()\n }\n\n async migrateReports(\n fromQueueId: number,\n toQueueId?: number,\n ): Promise<number> {\n const now = new Date().toISOString()\n const results = await this.db.db\n .updateTable('report')\n .set({\n queueId: toQueueId ?? -1,\n queuedAt: toQueueId ? now : null,\n updatedAt: now,\n })\n .where('queueId', '=', fromQueueId)\n .where('status', '!=', 'closed')\n .execute()\n return results.reduce((sum, r) => sum + Number(r.numUpdatedRows), 0)\n }\n\n async list({\n limit,\n cursor,\n enabled,\n subjectType,\n collection,\n reportTypes,\n }: {\n limit: number\n cursor?: string\n enabled?: boolean\n subjectType?: string\n collection?: string\n reportTypes?: string[]\n }): Promise<{ queues: Selectable<ReportQueue>[]; cursor?: string }> {\n const { ref } = this.db.db.dynamic\n let qb = this.db.db\n .selectFrom('report_queue')\n .selectAll()\n .where('deletedAt', 'is', null)\n\n if (enabled !== undefined) {\n qb = qb.where('enabled', '=', enabled)\n }\n\n if (subjectType !== undefined) {\n qb = qb.where(sql`\"subjectTypes\" @> ${jsonb([subjectType])}`)\n }\n\n if (collection !== undefined) {\n qb = qb.where('collection', '=', collection)\n }\n\n if (reportTypes && reportTypes.length > 0) {\n const conditions = reportTypes.map(\n (t) => sql`\"reportTypes\" @> ${jsonb([t])}`,\n )\n qb = qb.where(sql`(${sql.join(conditions, sql` OR `)})`)\n }\n\n const keyset = new TimeIdKeyset(ref('createdAt'), ref('id'))\n const paginatedBuilder = paginate(qb, {\n limit,\n cursor,\n keyset,\n direction: 'asc',\n tryIndex: true,\n })\n\n const queues = await paginatedBuilder.execute()\n\n return {\n queues,\n cursor: keyset.packFromResult(queues),\n }\n }\n\n view(queue: Selectable<ReportQueue>): ToolsOzoneQueueDefs.QueueView {\n return {\n id: queue.id,\n name: queue.name,\n subjectTypes: queue.subjectTypes,\n collection: queue.collection ?? undefined,\n reportTypes: queue.reportTypes,\n description: queue.description ?? undefined,\n createdBy: queue.createdBy,\n createdAt: queue.createdAt,\n updatedAt: queue.updatedAt,\n enabled: queue.enabled,\n deletedAt: queue.deletedAt ?? undefined,\n stats: {\n pendingCount: 0,\n actionedCount: 0,\n escalatedCount: 0,\n inboundCount: 0,\n actionRate: 0,\n },\n }\n }\n\n async viewsWithStats(\n queues: Selectable<ReportQueue>[],\n ): Promise<ToolsOzoneQueueDefs.QueueView[]> {\n const statsService = new ReportStatsService(this.db)\n const queueIds = queues.map((q) => q.id)\n const statsMap = await statsService.getLiveStatsForQueues(queueIds)\n\n return queues.map((queue) => {\n const view = this.view(queue)\n view.stats = viewQueueStats(statsMap.get(queue.id))\n return view\n })\n }\n\n /**\n * Re-route a range of existing reports against the current queue config.\n * Used by the manual `tools.ozone.queue.routeReports` endpoint to pick up\n * reports after queues are created or modified. New reports are inserted\n * by the daemon via `insertReportsFromEvents`, not here.\n */\n async assignReportBatch(\n params: { start: number; end: number; limit: number },\n opts?: { includeUnmatched?: boolean; serviceDid?: string },\n ): Promise<{\n processed: number\n assigned: number\n unmatched: number\n maxId: number\n }> {\n const { queues } = await this.list({ limit: 1000, enabled: true })\n\n if (!queues.length) {\n return { processed: 0, assigned: 0, unmatched: 0, maxId: 0 }\n }\n\n let query = this.db.db\n .selectFrom('report as r')\n .select([\n 'r.id',\n 'r.status',\n 'r.reportType',\n 'r.recordPath',\n 'r.subjectMessageId',\n ])\n .where('r.status', '!=', 'closed')\n .where('r.id', '>=', params.start)\n .where('r.id', '<=', params.end)\n .orderBy('r.id', 'asc')\n .limit(params.limit)\n\n if (opts?.includeUnmatched) {\n query = query.where((qb) => {\n return qb.orWhere('r.queueId', 'is', null).orWhere('r.queueId', '=', -1)\n })\n } else {\n query = query.where('r.queueId', 'is', null)\n }\n\n const reports = await query.execute()\n\n if (!reports.length) {\n return { processed: 0, assigned: 0, unmatched: 0, maxId: 0 }\n }\n\n const now = new Date().toISOString()\n\n // Resolve each report's destination in memory — no DB calls in this loop\n type MatchedEntry = {\n id: number\n queueId: number\n nextStatus: string | null\n activity: { activityType: string; previousStatus: string } | null\n }\n\n const matchedByQueue = new Map<number, MatchedEntry[]>()\n const unmatchedIds: number[] = []\n let maxReportId = 0\n\n for (const report of reports) {\n const subjectType: SubjectType = report.subjectMessageId\n ? 'message'\n : report.recordPath\n ? 'record'\n : 'account'\n\n // recordPath is 'collection/rkey' for records, '' for accounts\n const slashIdx = report.recordPath.indexOf('/')\n const collection =\n slashIdx > 0 ? report.recordPath.slice(0, slashIdx) : null\n\n const assignment = resolveAssignment(\n subjectType,\n collection,\n report.reportType,\n queues,\n now,\n )\n\n if (assignment.queueId !== -1) {\n // Existing-row UPDATE path uses handleReportUpdate so that already\n // escalated/closed/etc. reports keep their status — only open → queued\n // transitions emit a status change and an activity row.\n const result = handleReportUpdate(report.status, { type: 'queue' })\n const group = matchedByQueue.get(assignment.queueId) ?? []\n group.push({\n id: report.id,\n queueId: assignment.queueId,\n nextStatus: result.nextStatus,\n activity: result.activity,\n })\n matchedByQueue.set(assignment.queueId, group)\n } else {\n unmatchedIds.push(report.id)\n }\n\n if (report.id > maxReportId) maxReportId = report.id\n }\n\n // Bulk UPDATE matched reports — split by whether status should change.\n // handleReportUpdate returns nextStatus only for open → queued;\n // other statuses keep their current status but still get routed.\n for (const [queueId, group] of matchedByQueue) {\n const withTransition = group\n .filter((r) => r.nextStatus !== null)\n .map((r) => r.id)\n const withoutTransition = group\n .filter((r) => r.nextStatus === null)\n .map((r) => r.id)\n\n if (withTransition.length) {\n await this.db.db\n .updateTable('report')\n .set({ queueId, queuedAt: now, status: 'queued', updatedAt: now })\n .where('id', 'in', withTransition)\n .execute()\n }\n if (withoutTransition.length) {\n await this.db.db\n .updateTable('report')\n .set({ queueId, queuedAt: now, updatedAt: now })\n .where('id', 'in', withoutTransition)\n .execute()\n }\n }\n\n // Bulk UPDATE unmatched reports — status stays unchanged\n if (unmatchedIds.length) {\n await this.db.db\n .updateTable('report')\n .set({ queueId: -1, queuedAt: null, updatedAt: now })\n .where('id', 'in', unmatchedIds)\n .execute()\n }\n\n // Bulk INSERT activities for matched reports that changed status.\n if (opts?.serviceDid) {\n const withActivities = [...matchedByQueue.values()]\n .flat()\n .filter((r) => r.activity !== null)\n if (withActivities.length) {\n await this.db.db\n .insertInto('report_activity')\n .values(\n withActivities.map((r) => ({\n reportId: r.id,\n activityType: r.activity!.activityType,\n previousStatus: r.activity!.previousStatus,\n internalNote: null,\n publicNote: null,\n meta: null,\n isAutomated: true,\n createdBy: opts.serviceDid!,\n createdAt: now,\n })),\n )\n .execute()\n }\n }\n\n const assigned = [...matchedByQueue.values()].reduce(\n (sum, g) => sum + g.length,\n 0,\n )\n\n return {\n processed: reports.length,\n assigned,\n unmatched: unmatchedIds.length,\n maxId: maxReportId,\n }\n }\n\n /**\n * Read newly-created modEventReport rows from `moderation_event` and\n * insert corresponding `report` rows with `queueId` already resolved.\n * Used by the queue-router daemon. Idempotent via `ON CONFLICT (eventId)\n * DO NOTHING` — safe to re-run on the same range.\n *\n * Even when no queues are configured, report rows are still inserted with\n * `queueId = -1` so the invariant \"every modEventReport has a `report` row\"\n * holds.\n */\n async insertReportsFromEvents(params: {\n cursor: number | null\n limit: number\n }): Promise<{\n processed: number\n assigned: number\n unmatched: number\n maxEventId: number\n }> {\n const { queues } = await this.list({ limit: 1000, enabled: true })\n\n let query = this.db.db\n .selectFrom('moderation_event')\n .select([\n 'id',\n 'subjectDid',\n 'subjectUri',\n 'subjectMessageId',\n 'meta',\n 'createdAt',\n ])\n .where('action', '=', MOD_EVENT_REPORT_ACTION)\n .orderBy('id', 'asc')\n .limit(params.limit)\n\n if (params.cursor !== null) {\n query = query.where('id', '>', params.cursor)\n }\n\n const events = await query.execute()\n\n if (!events.length) {\n return { processed: 0, assigned: 0, unmatched: 0, maxEventId: 0 }\n }\n\n const now = new Date().toISOString()\n let maxEventId = 0\n let assigned = 0\n let unmatched = 0\n\n const rows = events.map((event) => {\n const subjectType: SubjectType = event.subjectMessageId\n ? 'message'\n : event.subjectUri\n ? 'record'\n : 'account'\n\n let collection: string | null = null\n let recordPath = ''\n if (event.subjectUri) {\n const uri = new AtUri(event.subjectUri)\n collection = uri.collection\n recordPath = `${uri.collection}/${uri.rkey}`\n }\n\n const reportType =\n (event.meta?.reportType as string | undefined) ?? REASON_OTHER\n\n const assignment = resolveAssignment(\n subjectType,\n collection,\n reportType,\n queues,\n now,\n )\n\n if (assignment.queueId === -1) unmatched++\n else assigned++\n if (event.id > maxEventId) maxEventId = event.id\n\n const isMuted =\n !!event.meta?.isReporterMuted || !!event.meta?.isSubjectMuted\n\n return {\n eventId: event.id,\n queueId: assignment.queueId,\n queuedAt: assignment.queuedAt,\n actionEventIds: null,\n actionNote: null,\n isMuted,\n status: assignment.status,\n reportType,\n did: event.subjectDid,\n recordPath,\n subjectMessageId: event.subjectMessageId,\n createdAt: now,\n updatedAt: now,\n }\n })\n\n // ON CONFLICT (eventId) DO NOTHING covers any race where a report row\n // already exists for the event (e.g. transitional code paths or retries\n // after a crash mid-batch).\n await this.db.db\n .insertInto('report')\n .values(rows)\n .onConflict((oc) => oc.column('eventId').doNothing())\n .execute()\n\n // Activity rows are intentionally not emitted: a freshly-inserted report\n // has no prior state to \"transition\" from. Activity rows record state\n // changes, and being born already-queued is not a state change. This\n // matches `handleReportUpdate`'s design where activity is only emitted\n // on real transitions.\n\n return {\n processed: events.length,\n assigned,\n unmatched,\n maxEventId,\n }\n }\n}\n\nexport function findMatchingQueue(\n queues: Selectable<ReportQueue>[],\n subjectType: string,\n collection: string | null,\n reportType: string | undefined,\n): Selectable<ReportQueue> | null {\n if (!reportType) return null\n\n for (const queue of queues) {\n const subjectTypeMatch = queue.subjectTypes.includes(subjectType)\n const collectionMatch =\n subjectType === 'record' && queue.collection !== null\n ? (collection ?? null) === queue.collection\n : true\n const reportTypeMatch = queue.reportTypes.includes(reportType)\n\n if (subjectTypeMatch && collectionMatch && reportTypeMatch) {\n return queue\n }\n }\n\n return null\n}\n"]}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Database } from '../db';
|
|
2
|
+
import { Member } from '../lexicon/types/tools/ozone/team/defs';
|
|
3
|
+
export type ActivityType = 'queueActivity' | 'assignmentActivity' | 'escalationActivity' | 'closeActivity' | 'reopenActivity' | 'noteActivity';
|
|
4
|
+
export type CreateActivityParams = {
|
|
5
|
+
reportId: number;
|
|
6
|
+
activityType: ActivityType;
|
|
7
|
+
internalNote?: string;
|
|
8
|
+
publicNote?: string;
|
|
9
|
+
meta?: Record<string, unknown>;
|
|
10
|
+
/** Set true for activities created by automated processes (e.g. queue router). */
|
|
11
|
+
isAutomated?: boolean;
|
|
12
|
+
createdBy: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function createReportActivity(db: Database, params: CreateActivityParams): Promise<import("kysely").Selectable<import("../db/schema/report_activity").ReportActivity>>;
|
|
15
|
+
export type BulkActivityInsert = {
|
|
16
|
+
reportId: number;
|
|
17
|
+
activityType: string;
|
|
18
|
+
previousStatus: string | null;
|
|
19
|
+
internalNote?: string;
|
|
20
|
+
publicNote?: string;
|
|
21
|
+
meta?: unknown;
|
|
22
|
+
isAutomated: boolean;
|
|
23
|
+
createdBy: string;
|
|
24
|
+
createdAt: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Insert multiple activity rows in a single query. No validation — caller is
|
|
28
|
+
* responsible for correctness and for being inside an appropriate transaction.
|
|
29
|
+
*/
|
|
30
|
+
export declare function bulkInsertReportActivities(db: Database, activities: BulkActivityInsert[]): Promise<void>;
|
|
31
|
+
export type ListActivitiesParams = {
|
|
32
|
+
reportId: number;
|
|
33
|
+
limit?: number;
|
|
34
|
+
cursor?: string;
|
|
35
|
+
};
|
|
36
|
+
export declare function listReportActivities(db: Database, params: ListActivitiesParams): Promise<{
|
|
37
|
+
activities: {
|
|
38
|
+
id: number;
|
|
39
|
+
meta: unknown;
|
|
40
|
+
createdAt: string;
|
|
41
|
+
createdBy: string;
|
|
42
|
+
reportId: number;
|
|
43
|
+
internalNote: string | null;
|
|
44
|
+
publicNote: string | null;
|
|
45
|
+
isAutomated: boolean;
|
|
46
|
+
previousStatus: string | null;
|
|
47
|
+
activityType: string;
|
|
48
|
+
}[];
|
|
49
|
+
cursor: string | undefined;
|
|
50
|
+
}>;
|
|
51
|
+
export declare function formatActivityView(activity: {
|
|
52
|
+
id: number;
|
|
53
|
+
reportId: number;
|
|
54
|
+
activityType: string;
|
|
55
|
+
previousStatus: string | null;
|
|
56
|
+
internalNote: string | null;
|
|
57
|
+
publicNote: string | null;
|
|
58
|
+
meta: unknown;
|
|
59
|
+
isAutomated: boolean;
|
|
60
|
+
createdBy: string;
|
|
61
|
+
createdAt: string;
|
|
62
|
+
}, memberViews?: Map<string, Member>): {
|
|
63
|
+
id: number;
|
|
64
|
+
reportId: number;
|
|
65
|
+
activity: {
|
|
66
|
+
[k: string]: unknown;
|
|
67
|
+
$type: string;
|
|
68
|
+
};
|
|
69
|
+
internalNote: string | undefined;
|
|
70
|
+
publicNote: string | undefined;
|
|
71
|
+
meta: Record<string, unknown>;
|
|
72
|
+
isAutomated: boolean;
|
|
73
|
+
createdBy: string;
|
|
74
|
+
moderator: Member | undefined;
|
|
75
|
+
createdAt: string;
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=activity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../../src/report/activity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,wCAAwC,CAAA;AAO/D,MAAM,MAAM,YAAY,GACpB,eAAe,GACf,oBAAoB,GACpB,oBAAoB,GACpB,eAAe,GACf,gBAAgB,GAChB,cAAc,CAAA;AAElB,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,YAAY,CAAA;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,kFAAkF;IAClF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,oBAAoB,+FAkF7B;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,EAAE,OAAO,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,QAAQ,EACZ,UAAU,EAAE,kBAAkB,EAAE,iBAmBjC;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,oBAAoB;;;;;;;;;;;;;;GA6B7B;AAaD,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE;IACR,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,WAAW,EAAE,OAAO,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB,EACD,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;;;;;eArBvB,MAAM;;;;;;;;;EAsCjB"}
|