@atproto/ozone 0.1.166 → 0.1.168

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.
Files changed (85) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/api/moderation/emitEvent.d.ts.map +1 -1
  3. package/dist/api/moderation/emitEvent.js +8 -0
  4. package/dist/api/moderation/emitEvent.js.map +1 -1
  5. package/dist/api/report/createReport.d.ts.map +1 -1
  6. package/dist/api/report/createReport.js +2 -4
  7. package/dist/api/report/createReport.js.map +1 -1
  8. package/dist/api/util.d.ts +1 -1
  9. package/dist/api/util.d.ts.map +1 -1
  10. package/dist/api/util.js +1 -0
  11. package/dist/api/util.js.map +1 -1
  12. package/dist/db/schema/moderation_event.d.ts +1 -1
  13. package/dist/db/schema/moderation_event.d.ts.map +1 -1
  14. package/dist/db/schema/moderation_event.js.map +1 -1
  15. package/dist/lexicon/lexicons.d.ts +42 -18
  16. package/dist/lexicon/lexicons.d.ts.map +1 -1
  17. package/dist/lexicon/lexicons.js +22 -10
  18. package/dist/lexicon/lexicons.js.map +1 -1
  19. package/dist/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.d.ts +3 -3
  20. package/dist/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.d.ts.map +1 -1
  21. package/dist/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.js.map +1 -1
  22. package/dist/lexicon/types/com/atproto/label/defs.d.ts +1 -1
  23. package/dist/lexicon/types/com/atproto/label/defs.d.ts.map +1 -1
  24. package/dist/lexicon/types/com/atproto/label/defs.js.map +1 -1
  25. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +10 -2
  26. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
  27. package/dist/lexicon/types/tools/ozone/moderation/defs.js +9 -0
  28. package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
  29. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts +1 -1
  30. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts.map +1 -1
  31. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.js.map +1 -1
  32. package/dist/mod-service/index.d.ts +3 -2
  33. package/dist/mod-service/index.d.ts.map +1 -1
  34. package/dist/mod-service/index.js +11 -0
  35. package/dist/mod-service/index.js.map +1 -1
  36. package/dist/mod-service/status.d.ts.map +1 -1
  37. package/dist/mod-service/status.js +7 -0
  38. package/dist/mod-service/status.js.map +1 -1
  39. package/package.json +7 -7
  40. package/src/api/moderation/emitEvent.ts +17 -1
  41. package/src/api/report/createReport.ts +2 -4
  42. package/src/api/util.ts +1 -0
  43. package/src/db/schema/moderation_event.ts +1 -0
  44. package/src/lexicon/lexicons.ts +24 -11
  45. package/src/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.ts +3 -3
  46. package/src/lexicon/types/com/atproto/label/defs.ts +2 -5
  47. package/src/lexicon/types/tools/ozone/moderation/defs.ts +19 -0
  48. package/src/lexicon/types/tools/ozone/moderation/emitEvent.ts +1 -0
  49. package/src/mod-service/index.ts +14 -0
  50. package/src/mod-service/status.ts +8 -0
  51. package/tests/3p-labeler.test.ts +2 -2
  52. package/tests/__snapshots__/get-starter-pack.test.ts.snap +9 -9
  53. package/tests/__snapshots__/get-subjects.test.ts.snap +2 -2
  54. package/tests/__snapshots__/team.test.ts.snap +4 -4
  55. package/tests/__snapshots__/verification-listener.test.ts.snap +2 -2
  56. package/tests/__snapshots__/verification.test.ts.snap +3 -3
  57. package/tests/_util.ts +6 -4
  58. package/tests/account-strikes.test.ts +1 -1
  59. package/tests/age-assurance.test.ts +134 -7
  60. package/tests/communication-templates.test.ts +1 -1
  61. package/tests/expiring-label.test.ts +1 -1
  62. package/tests/get-account-timeline.test.ts +1 -1
  63. package/tests/get-config.test.ts +1 -1
  64. package/tests/get-lists.test.ts +2 -2
  65. package/tests/get-profiles.test.ts +1 -1
  66. package/tests/get-record.test.ts +1 -1
  67. package/tests/get-records.test.ts +1 -1
  68. package/tests/get-repo.test.ts +2 -2
  69. package/tests/get-repos.test.ts +2 -2
  70. package/tests/get-starter-pack.test.ts +2 -2
  71. package/tests/get-subjects.test.ts +1 -1
  72. package/tests/moderation.test.ts +3 -3
  73. package/tests/query-labels.test.ts +1 -1
  74. package/tests/repo-search.test.ts +1 -1
  75. package/tests/report-reason.test.ts +1 -1
  76. package/tests/safelink.test.ts +3 -3
  77. package/tests/scheduled-action-processor.test.ts +1 -1
  78. package/tests/scheduled-action.test.ts +3 -3
  79. package/tests/sets.test.ts +1 -1
  80. package/tests/settings.test.ts +1 -1
  81. package/tests/strike-expiry-processor.test.ts +1 -1
  82. package/tests/takedown.test.ts +2 -2
  83. package/tests/team.test.ts +2 -2
  84. package/tests/verification-listener.test.ts +1 -1
  85. package/tests/verification.test.ts +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/mod-service/status.ts"],"names":[],"mappings":"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","sourcesContent":["// This may require better organization but for now, just dumping functions here containing DB queries for moderation status\n\nimport { HOUR } from '@atproto/common'\nimport { AtUri } from '@atproto/syntax'\nimport { isAppealReport } from '../api/util'\nimport { Database } from '../db'\nimport { DatabaseSchema } from '../db/schema'\nimport { jsonb } from '../db/types'\nimport {\n REVIEWCLOSED,\n REVIEWESCALATED,\n REVIEWNONE,\n REVIEWOPEN,\n} from '../lexicon/types/tools/ozone/moderation/defs'\nimport { ModerationEventRow, ModerationSubjectStatusRow } from './types'\n\nconst getSubjectStatusForModerationEvent = ({\n currentStatus,\n action,\n createdBy,\n createdAt,\n durationInHours,\n}: {\n currentStatus?: ModerationSubjectStatusRow\n action: string\n createdBy: string\n createdAt: string\n durationInHours: number | null\n}): Partial<ModerationSubjectStatusRow> => {\n const defaultReviewState = currentStatus\n ? currentStatus.reviewState\n : REVIEWNONE\n\n switch (action) {\n case 'tools.ozone.moderation.defs#modEventAcknowledge':\n return {\n lastReviewedBy: createdBy,\n reviewState: REVIEWCLOSED,\n lastReviewedAt: createdAt,\n }\n case 'tools.ozone.moderation.defs#modEventReport':\n return {\n reviewState: REVIEWOPEN,\n lastReportedAt: createdAt,\n }\n case 'tools.ozone.moderation.defs#modEventEscalate':\n return {\n lastReviewedBy: createdBy,\n reviewState: REVIEWESCALATED,\n lastReviewedAt: createdAt,\n }\n case 'tools.ozone.moderation.defs#modEventReverseTakedown':\n return {\n lastReviewedBy: createdBy,\n reviewState: REVIEWCLOSED,\n takendown: false,\n suspendUntil: null,\n lastReviewedAt: createdAt,\n }\n case 'tools.ozone.moderation.defs#modEventUnmuteReporter':\n return {\n lastReviewedBy: createdBy,\n muteReportingUntil: null,\n // It's not likely to receive an unmute event that does not already have a status row\n // but if it does happen, default to unnecessary\n reviewState: defaultReviewState,\n lastReviewedAt: createdAt,\n }\n case 'tools.ozone.moderation.defs#modEventUnmute':\n return {\n lastReviewedBy: createdBy,\n muteUntil: null,\n // It's not likely to receive an unmute event that does not already have a status row\n // but if it does happen, default to unnecessary\n reviewState: defaultReviewState,\n lastReviewedAt: createdAt,\n }\n case 'tools.ozone.moderation.defs#modEventTakedown':\n return {\n // If we are doing a takedown, safe to move the item out of appealed state\n ...(currentStatus?.appealed ? { appealed: false } : {}),\n takendown: true,\n lastReviewedBy: createdBy,\n reviewState: REVIEWCLOSED,\n lastReviewedAt: createdAt,\n suspendUntil: durationInHours\n ? new Date(Date.now() + durationInHours * HOUR).toISOString()\n : null,\n }\n case 'tools.ozone.moderation.defs#modEventMuteReporter':\n return {\n lastReviewedBy: createdBy,\n lastReviewedAt: createdAt,\n // By default, mute for 24hrs\n muteReportingUntil: new Date(\n Date.now() + (durationInHours || 24) * HOUR,\n ).toISOString(),\n // It's not likely to receive a mute event on a subject that does not already have a status row\n // but if it does happen, default to unnecessary\n reviewState: defaultReviewState,\n }\n case 'tools.ozone.moderation.defs#modEventMute':\n return {\n lastReviewedBy: createdBy,\n lastReviewedAt: createdAt,\n // By default, mute for 24hrs\n muteUntil: new Date(\n Date.now() + (durationInHours || 24) * HOUR,\n ).toISOString(),\n // It's not likely to receive a mute event on a subject that does not already have a status row\n // but if it does happen, default to unnecessary\n reviewState: defaultReviewState,\n }\n case 'tools.ozone.moderation.defs#modEventComment':\n return {\n lastReviewedBy: createdBy,\n lastReviewedAt: createdAt,\n reviewState: defaultReviewState,\n }\n case 'tools.ozone.moderation.defs#modEventTag':\n return { tags: [], reviewState: defaultReviewState }\n case 'tools.ozone.moderation.defs#modEventResolveAppeal':\n return {\n appealed: false,\n }\n case 'tools.ozone.moderation.defs#ageAssuranceEvent':\n case 'tools.ozone.moderation.defs#ageAssuranceOverrideEvent':\n return {\n reviewState: defaultReviewState,\n }\n default:\n return {}\n }\n}\n\nconst hostingEvents = [\n 'tools.ozone.moderation.defs#accountEvent',\n 'tools.ozone.moderation.defs#identityEvent',\n 'tools.ozone.moderation.defs#recordEvent',\n]\n\nconst getSubjectStatusForRecordEvent = ({\n event,\n currentStatus,\n}: {\n event: ModerationEventRow\n currentStatus?: ModerationSubjectStatusRow\n}): Partial<ModerationSubjectStatusRow> => {\n const timestamp =\n typeof event.meta?.timestamp === 'string'\n ? event.meta?.timestamp\n : event.createdAt\n\n if (event.action === 'tools.ozone.moderation.defs#recordEvent') {\n if (event.meta?.op === 'delete') {\n return {\n hostingStatus: 'deleted',\n hostingDeletedAt: timestamp,\n }\n } else if (event.meta?.op === 'update') {\n return {\n hostingStatus: 'active',\n hostingUpdatedAt: timestamp,\n }\n }\n return {}\n }\n\n if (event.action === 'tools.ozone.moderation.defs#accountEvent') {\n const status: Partial<ModerationSubjectStatusRow> = {\n hostingUpdatedAt: timestamp,\n }\n\n if (event.meta?.status) {\n status.hostingStatus = `${event.meta?.status}`\n }\n\n if (event.meta?.status === 'deleted') {\n status.hostingDeletedAt = timestamp\n } else if (event.meta?.status === 'deactivated') {\n status.hostingDeactivatedAt = timestamp\n } else {\n // When deactivated accounts are re-activated, we receive the event with just the active flag set to true\n // so we want to make sure that the hostingStatus is not set to an outdated value\n if (\n currentStatus?.hostingStatus === 'deactivated' &&\n event.meta?.active\n ) {\n status.hostingStatus = 'active'\n status.hostingReactivatedAt = timestamp\n }\n }\n\n return status\n }\n\n if (event.action === 'tools.ozone.moderation.defs#identityEvent') {\n const status: Partial<ModerationSubjectStatusRow> = {\n hostingUpdatedAt: timestamp,\n }\n\n if (event.meta?.tombstone) {\n status.hostingStatus = 'tombstoned'\n status.hostingDeletedAt = timestamp\n }\n\n return status\n }\n\n return {}\n}\n\nexport const moderationSubjectStatusQueryBuilder = (db: DatabaseSchema) => {\n // @NOTE: Using select() instead of selectAll() below because the materialized\n // views might be incomplete, and we don't want the null `did` columns to\n // interfere with the (never null) `did` column from the\n // `moderation_subject_status` table in the results\n return db\n .selectFrom('moderation_subject_status')\n .selectAll('moderation_subject_status')\n .leftJoin('account_events_stats', (join) =>\n join.onRef(\n 'moderation_subject_status.did',\n '=',\n 'account_events_stats.subjectDid',\n ),\n )\n .select([\n 'account_events_stats.takedownCount',\n 'account_events_stats.suspendCount',\n 'account_events_stats.escalateCount',\n 'account_events_stats.reportCount',\n 'account_events_stats.appealCount',\n ])\n .leftJoin('account_record_events_stats', (join) =>\n join.onRef(\n 'moderation_subject_status.did',\n '=',\n 'account_record_events_stats.subjectDid',\n ),\n )\n .select([\n 'account_record_events_stats.totalReports',\n 'account_record_events_stats.reportedCount',\n 'account_record_events_stats.escalatedCount',\n 'account_record_events_stats.appealedCount',\n ])\n .leftJoin('account_record_status_stats', (join) =>\n join.onRef(\n 'moderation_subject_status.did',\n '=',\n 'account_record_status_stats.did',\n ),\n )\n .select([\n 'account_record_status_stats.subjectCount',\n 'account_record_status_stats.pendingCount',\n 'account_record_status_stats.processedCount',\n 'account_record_status_stats.takendownCount',\n ])\n .leftJoin('account_strike', (join) =>\n join.onRef('moderation_subject_status.did', '=', 'account_strike.did'),\n )\n .select([\n 'account_strike.activeStrikeCount as strikeCount',\n 'account_strike.totalStrikeCount',\n 'account_strike.firstStrikeAt',\n 'account_strike.lastStrikeAt',\n ])\n}\n\n// Based on a given moderation action event, this function will update the moderation status of the subject\n// If there's no existing status, it will create one\n// If the action event does not affect the status, it will do nothing\nexport const adjustModerationSubjectStatus = async (\n db: Database,\n moderationEvent: ModerationEventRow,\n blobCids?: string[],\n): Promise<ModerationSubjectStatusRow | null> => {\n const {\n action,\n subjectDid,\n subjectUri,\n subjectCid,\n createdBy,\n meta,\n addedTags,\n removedTags,\n comment,\n createdAt,\n } = moderationEvent\n\n // If subjectUri exists, it's not a repoRef so pass along the uri to get identifier back\n const identifier = getStatusIdentifierFromSubject(subjectUri || subjectDid)\n\n db.assertTransaction()\n\n const now = new Date().toISOString()\n const currentStatus = await db.db\n .selectFrom('moderation_subject_status')\n .where('did', '=', identifier.did)\n .where('recordPath', '=', identifier.recordPath)\n // Make sure we respect other updates that may be happening at the same time\n .forUpdate()\n .selectAll()\n .executeTakeFirst()\n\n if (hostingEvents.includes(action)) {\n const newStatus = getSubjectStatusForRecordEvent({\n event: moderationEvent,\n currentStatus,\n })\n if (!Object.keys(newStatus).length) {\n return currentStatus || null\n }\n\n const status = await db.db\n .insertInto('moderation_subject_status')\n .values({\n ...identifier,\n ...newStatus,\n // newStatus doesn't contain a reviewState or takendown so in case this is a new entry\n // we need to set a default values so that the insert doesn't fail\n reviewState: currentStatus ? currentStatus.reviewState : REVIEWNONE,\n // @TODO: should we try to update this based on status property of account event?\n // For now we're the only one emitting takedowns so i don't think it makes too much of a difference\n takendown: currentStatus ? currentStatus.takendown : false,\n ageAssuranceState: currentStatus\n ? currentStatus.ageAssuranceState\n : 'unknown',\n createdAt: now,\n updatedAt: now,\n })\n .onConflict((oc) =>\n oc.constraint('moderation_status_unique_idx').doUpdateSet({\n ...newStatus,\n updatedAt: now,\n }),\n )\n .returningAll()\n .executeTakeFirst()\n\n return status || null\n }\n\n // If reporting is muted for this reporter, we don't want to update the subject status\n if (meta?.isReporterMuted) {\n return currentStatus || null\n }\n\n const isAppealEvent =\n action === 'tools.ozone.moderation.defs#modEventReport' &&\n meta?.reportType &&\n isAppealReport(`${meta.reportType}`)\n\n const subjectStatus = getSubjectStatusForModerationEvent({\n currentStatus,\n action,\n createdBy,\n createdAt,\n durationInHours: moderationEvent.durationInHours,\n })\n\n if (\n currentStatus?.reviewState === REVIEWESCALATED &&\n subjectStatus.reviewState !== REVIEWCLOSED\n ) {\n // If the current status is escalated only allow incoming events to move the state to\n // reviewClosed because escalated subjects should never move to any other state\n subjectStatus.reviewState = REVIEWESCALATED\n }\n\n if (currentStatus && subjectStatus.reviewState === REVIEWNONE) {\n // reviewNone is ONLY allowed when there is no current status\n // If there is a current status, it should not be allowed to move back to reviewNone\n subjectStatus.reviewState = currentStatus.reviewState\n }\n\n // Set these because we don't want to override them if they're already set\n const defaultData = {\n comment: null,\n // Defaulting reviewState to open for any event may not be the desired behavior.\n // For instance, if a subject never had any event and we just want to leave a comment to keep an eye on it\n // that shouldn't mean we want to review the subject\n reviewState: REVIEWNONE,\n recordCid: subjectCid || null,\n ageAssuranceState: currentStatus?.ageAssuranceState || 'unknown',\n }\n const newStatus = {\n ...defaultData,\n ...subjectStatus,\n }\n\n if (\n action === 'tools.ozone.moderation.defs#modEventPriorityScore' &&\n typeof meta?.priorityScore === 'number'\n ) {\n newStatus.priorityScore = meta?.priorityScore\n subjectStatus.priorityScore = meta?.priorityScore\n }\n\n if (\n action === 'tools.ozone.moderation.defs#modEventReverseTakedown' &&\n !subjectStatus.takendown\n ) {\n newStatus.takendown = false\n subjectStatus.takendown = false\n }\n\n if (isAppealEvent) {\n newStatus.appealed = true\n subjectStatus.appealed = true\n newStatus.lastAppealedAt = createdAt\n subjectStatus.lastAppealedAt = createdAt\n // Set reviewState to escalated when appeal events are emitted\n subjectStatus.reviewState = REVIEWESCALATED\n newStatus.reviewState = REVIEWESCALATED\n }\n\n if (\n action === 'tools.ozone.moderation.defs#modEventResolveAppeal' &&\n subjectStatus.appealed\n ) {\n newStatus.appealed = false\n subjectStatus.appealed = false\n }\n\n if (\n action === 'tools.ozone.moderation.defs#modEventComment' &&\n meta?.sticky\n ) {\n newStatus.comment = comment\n subjectStatus.comment = comment\n }\n\n if (action === 'tools.ozone.moderation.defs#modEventTag') {\n let tags = currentStatus?.tags || []\n if (addedTags?.length) {\n tags = tags.concat(addedTags)\n }\n if (removedTags?.length) {\n tags = tags.filter((tag) => !removedTags.includes(tag))\n }\n newStatus.tags = jsonb([...new Set(tags)]) as unknown as string[]\n subjectStatus.tags = newStatus.tags\n }\n\n if (action === 'tools.ozone.moderation.defs#ageAssuranceEvent') {\n // Only when the last update was made by an admin AND state was set to reset user event can override final state\n if (\n currentStatus?.ageAssuranceUpdatedBy !== 'admin' ||\n currentStatus?.ageAssuranceState === 'reset'\n ) {\n if (typeof meta?.status === 'string') {\n newStatus.ageAssuranceState = meta.status\n subjectStatus.ageAssuranceState = meta.status\n newStatus.ageAssuranceUpdatedBy = 'user'\n subjectStatus.ageAssuranceUpdatedBy = 'user'\n }\n }\n }\n\n if (action === 'tools.ozone.moderation.defs#ageAssuranceOverrideEvent') {\n if (typeof meta?.status === 'string') {\n newStatus.ageAssuranceState = meta.status\n subjectStatus.ageAssuranceState = meta.status\n newStatus.ageAssuranceUpdatedBy = 'admin'\n subjectStatus.ageAssuranceUpdatedBy = 'admin'\n }\n }\n\n if (blobCids?.length) {\n const newBlobCids = jsonb(\n blobCids,\n ) as unknown as ModerationSubjectStatusRow['blobCids']\n newStatus.blobCids = newBlobCids\n subjectStatus.blobCids = newBlobCids\n }\n\n const insertQuery = db.db\n .insertInto('moderation_subject_status')\n .values({\n ...identifier,\n ...newStatus,\n createdAt: now,\n updatedAt: now,\n } as ModerationSubjectStatusRow)\n .onConflict((oc) =>\n oc.constraint('moderation_status_unique_idx').doUpdateSet({\n ...subjectStatus,\n updatedAt: now,\n }),\n )\n\n const status = await insertQuery.returningAll().executeTakeFirst()\n return status || null\n}\n\nexport const getStatusIdentifierFromSubject = (\n subject: string | AtUri,\n): { did: string; recordPath: string } => {\n const isSubjectString = typeof subject === 'string'\n if (isSubjectString && subject.startsWith('did:')) {\n return {\n did: subject,\n recordPath: '',\n }\n }\n\n if (isSubjectString && !subject.startsWith('at://')) {\n throw new Error('Subject is neither a did nor an at-uri')\n }\n\n const uri = isSubjectString ? new AtUri(subject) : subject\n return {\n did: uri.host,\n recordPath: `${uri.collection}/${uri.rkey}`,\n }\n}\n"]}
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/mod-service/status.ts"],"names":[],"mappings":"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","sourcesContent":["// This may require better organization but for now, just dumping functions here containing DB queries for moderation status\n\nimport { HOUR } from '@atproto/common'\nimport { AtUri } from '@atproto/syntax'\nimport { isAppealReport } from '../api/util'\nimport { Database } from '../db'\nimport { DatabaseSchema } from '../db/schema'\nimport { jsonb } from '../db/types'\nimport {\n REVIEWCLOSED,\n REVIEWESCALATED,\n REVIEWNONE,\n REVIEWOPEN,\n} from '../lexicon/types/tools/ozone/moderation/defs'\nimport { ModerationEventRow, ModerationSubjectStatusRow } from './types'\n\nconst getSubjectStatusForModerationEvent = ({\n currentStatus,\n action,\n createdBy,\n createdAt,\n durationInHours,\n}: {\n currentStatus?: ModerationSubjectStatusRow\n action: string\n createdBy: string\n createdAt: string\n durationInHours: number | null\n}): Partial<ModerationSubjectStatusRow> => {\n const defaultReviewState = currentStatus\n ? currentStatus.reviewState\n : REVIEWNONE\n\n switch (action) {\n case 'tools.ozone.moderation.defs#modEventAcknowledge':\n return {\n lastReviewedBy: createdBy,\n reviewState: REVIEWCLOSED,\n lastReviewedAt: createdAt,\n }\n case 'tools.ozone.moderation.defs#modEventReport':\n return {\n reviewState: REVIEWOPEN,\n lastReportedAt: createdAt,\n }\n case 'tools.ozone.moderation.defs#modEventEscalate':\n return {\n lastReviewedBy: createdBy,\n reviewState: REVIEWESCALATED,\n lastReviewedAt: createdAt,\n }\n case 'tools.ozone.moderation.defs#modEventReverseTakedown':\n return {\n lastReviewedBy: createdBy,\n reviewState: REVIEWCLOSED,\n takendown: false,\n suspendUntil: null,\n lastReviewedAt: createdAt,\n }\n case 'tools.ozone.moderation.defs#modEventUnmuteReporter':\n return {\n lastReviewedBy: createdBy,\n muteReportingUntil: null,\n // It's not likely to receive an unmute event that does not already have a status row\n // but if it does happen, default to unnecessary\n reviewState: defaultReviewState,\n lastReviewedAt: createdAt,\n }\n case 'tools.ozone.moderation.defs#modEventUnmute':\n return {\n lastReviewedBy: createdBy,\n muteUntil: null,\n // It's not likely to receive an unmute event that does not already have a status row\n // but if it does happen, default to unnecessary\n reviewState: defaultReviewState,\n lastReviewedAt: createdAt,\n }\n case 'tools.ozone.moderation.defs#modEventTakedown':\n return {\n // If we are doing a takedown, safe to move the item out of appealed state\n ...(currentStatus?.appealed ? { appealed: false } : {}),\n takendown: true,\n lastReviewedBy: createdBy,\n reviewState: REVIEWCLOSED,\n lastReviewedAt: createdAt,\n suspendUntil: durationInHours\n ? new Date(Date.now() + durationInHours * HOUR).toISOString()\n : null,\n }\n case 'tools.ozone.moderation.defs#modEventMuteReporter':\n return {\n lastReviewedBy: createdBy,\n lastReviewedAt: createdAt,\n // By default, mute for 24hrs\n muteReportingUntil: new Date(\n Date.now() + (durationInHours || 24) * HOUR,\n ).toISOString(),\n // It's not likely to receive a mute event on a subject that does not already have a status row\n // but if it does happen, default to unnecessary\n reviewState: defaultReviewState,\n }\n case 'tools.ozone.moderation.defs#modEventMute':\n return {\n lastReviewedBy: createdBy,\n lastReviewedAt: createdAt,\n // By default, mute for 24hrs\n muteUntil: new Date(\n Date.now() + (durationInHours || 24) * HOUR,\n ).toISOString(),\n // It's not likely to receive a mute event on a subject that does not already have a status row\n // but if it does happen, default to unnecessary\n reviewState: defaultReviewState,\n }\n case 'tools.ozone.moderation.defs#modEventComment':\n return {\n lastReviewedBy: createdBy,\n lastReviewedAt: createdAt,\n reviewState: defaultReviewState,\n }\n case 'tools.ozone.moderation.defs#modEventTag':\n return { tags: [], reviewState: defaultReviewState }\n case 'tools.ozone.moderation.defs#modEventResolveAppeal':\n return {\n appealed: false,\n }\n case 'tools.ozone.moderation.defs#ageAssuranceEvent':\n case 'tools.ozone.moderation.defs#ageAssuranceOverrideEvent':\n case 'tools.ozone.moderation.defs#ageAssurancePurgeEvent':\n return {\n reviewState: defaultReviewState,\n }\n default:\n return {}\n }\n}\n\nconst hostingEvents = [\n 'tools.ozone.moderation.defs#accountEvent',\n 'tools.ozone.moderation.defs#identityEvent',\n 'tools.ozone.moderation.defs#recordEvent',\n]\n\nconst getSubjectStatusForRecordEvent = ({\n event,\n currentStatus,\n}: {\n event: ModerationEventRow\n currentStatus?: ModerationSubjectStatusRow\n}): Partial<ModerationSubjectStatusRow> => {\n const timestamp =\n typeof event.meta?.timestamp === 'string'\n ? event.meta?.timestamp\n : event.createdAt\n\n if (event.action === 'tools.ozone.moderation.defs#recordEvent') {\n if (event.meta?.op === 'delete') {\n return {\n hostingStatus: 'deleted',\n hostingDeletedAt: timestamp,\n }\n } else if (event.meta?.op === 'update') {\n return {\n hostingStatus: 'active',\n hostingUpdatedAt: timestamp,\n }\n }\n return {}\n }\n\n if (event.action === 'tools.ozone.moderation.defs#accountEvent') {\n const status: Partial<ModerationSubjectStatusRow> = {\n hostingUpdatedAt: timestamp,\n }\n\n if (event.meta?.status) {\n status.hostingStatus = `${event.meta?.status}`\n }\n\n if (event.meta?.status === 'deleted') {\n status.hostingDeletedAt = timestamp\n } else if (event.meta?.status === 'deactivated') {\n status.hostingDeactivatedAt = timestamp\n } else {\n // When deactivated accounts are re-activated, we receive the event with just the active flag set to true\n // so we want to make sure that the hostingStatus is not set to an outdated value\n if (\n currentStatus?.hostingStatus === 'deactivated' &&\n event.meta?.active\n ) {\n status.hostingStatus = 'active'\n status.hostingReactivatedAt = timestamp\n }\n }\n\n return status\n }\n\n if (event.action === 'tools.ozone.moderation.defs#identityEvent') {\n const status: Partial<ModerationSubjectStatusRow> = {\n hostingUpdatedAt: timestamp,\n }\n\n if (event.meta?.tombstone) {\n status.hostingStatus = 'tombstoned'\n status.hostingDeletedAt = timestamp\n }\n\n return status\n }\n\n return {}\n}\n\nexport const moderationSubjectStatusQueryBuilder = (db: DatabaseSchema) => {\n // @NOTE: Using select() instead of selectAll() below because the materialized\n // views might be incomplete, and we don't want the null `did` columns to\n // interfere with the (never null) `did` column from the\n // `moderation_subject_status` table in the results\n return db\n .selectFrom('moderation_subject_status')\n .selectAll('moderation_subject_status')\n .leftJoin('account_events_stats', (join) =>\n join.onRef(\n 'moderation_subject_status.did',\n '=',\n 'account_events_stats.subjectDid',\n ),\n )\n .select([\n 'account_events_stats.takedownCount',\n 'account_events_stats.suspendCount',\n 'account_events_stats.escalateCount',\n 'account_events_stats.reportCount',\n 'account_events_stats.appealCount',\n ])\n .leftJoin('account_record_events_stats', (join) =>\n join.onRef(\n 'moderation_subject_status.did',\n '=',\n 'account_record_events_stats.subjectDid',\n ),\n )\n .select([\n 'account_record_events_stats.totalReports',\n 'account_record_events_stats.reportedCount',\n 'account_record_events_stats.escalatedCount',\n 'account_record_events_stats.appealedCount',\n ])\n .leftJoin('account_record_status_stats', (join) =>\n join.onRef(\n 'moderation_subject_status.did',\n '=',\n 'account_record_status_stats.did',\n ),\n )\n .select([\n 'account_record_status_stats.subjectCount',\n 'account_record_status_stats.pendingCount',\n 'account_record_status_stats.processedCount',\n 'account_record_status_stats.takendownCount',\n ])\n .leftJoin('account_strike', (join) =>\n join.onRef('moderation_subject_status.did', '=', 'account_strike.did'),\n )\n .select([\n 'account_strike.activeStrikeCount as strikeCount',\n 'account_strike.totalStrikeCount',\n 'account_strike.firstStrikeAt',\n 'account_strike.lastStrikeAt',\n ])\n}\n\n// Based on a given moderation action event, this function will update the moderation status of the subject\n// If there's no existing status, it will create one\n// If the action event does not affect the status, it will do nothing\nexport const adjustModerationSubjectStatus = async (\n db: Database,\n moderationEvent: ModerationEventRow,\n blobCids?: string[],\n): Promise<ModerationSubjectStatusRow | null> => {\n const {\n action,\n subjectDid,\n subjectUri,\n subjectCid,\n createdBy,\n meta,\n addedTags,\n removedTags,\n comment,\n createdAt,\n } = moderationEvent\n\n // If subjectUri exists, it's not a repoRef so pass along the uri to get identifier back\n const identifier = getStatusIdentifierFromSubject(subjectUri || subjectDid)\n\n db.assertTransaction()\n\n const now = new Date().toISOString()\n const currentStatus = await db.db\n .selectFrom('moderation_subject_status')\n .where('did', '=', identifier.did)\n .where('recordPath', '=', identifier.recordPath)\n // Make sure we respect other updates that may be happening at the same time\n .forUpdate()\n .selectAll()\n .executeTakeFirst()\n\n if (hostingEvents.includes(action)) {\n const newStatus = getSubjectStatusForRecordEvent({\n event: moderationEvent,\n currentStatus,\n })\n if (!Object.keys(newStatus).length) {\n return currentStatus || null\n }\n\n const status = await db.db\n .insertInto('moderation_subject_status')\n .values({\n ...identifier,\n ...newStatus,\n // newStatus doesn't contain a reviewState or takendown so in case this is a new entry\n // we need to set a default values so that the insert doesn't fail\n reviewState: currentStatus ? currentStatus.reviewState : REVIEWNONE,\n // @TODO: should we try to update this based on status property of account event?\n // For now we're the only one emitting takedowns so i don't think it makes too much of a difference\n takendown: currentStatus ? currentStatus.takendown : false,\n ageAssuranceState: currentStatus\n ? currentStatus.ageAssuranceState\n : 'unknown',\n createdAt: now,\n updatedAt: now,\n })\n .onConflict((oc) =>\n oc.constraint('moderation_status_unique_idx').doUpdateSet({\n ...newStatus,\n updatedAt: now,\n }),\n )\n .returningAll()\n .executeTakeFirst()\n\n return status || null\n }\n\n // If reporting is muted for this reporter, we don't want to update the subject status\n if (meta?.isReporterMuted) {\n return currentStatus || null\n }\n\n const isAppealEvent =\n action === 'tools.ozone.moderation.defs#modEventReport' &&\n meta?.reportType &&\n isAppealReport(`${meta.reportType}`)\n\n const subjectStatus = getSubjectStatusForModerationEvent({\n currentStatus,\n action,\n createdBy,\n createdAt,\n durationInHours: moderationEvent.durationInHours,\n })\n\n if (\n currentStatus?.reviewState === REVIEWESCALATED &&\n subjectStatus.reviewState !== REVIEWCLOSED\n ) {\n // If the current status is escalated only allow incoming events to move the state to\n // reviewClosed because escalated subjects should never move to any other state\n subjectStatus.reviewState = REVIEWESCALATED\n }\n\n if (currentStatus && subjectStatus.reviewState === REVIEWNONE) {\n // reviewNone is ONLY allowed when there is no current status\n // If there is a current status, it should not be allowed to move back to reviewNone\n subjectStatus.reviewState = currentStatus.reviewState\n }\n\n // Set these because we don't want to override them if they're already set\n const defaultData = {\n comment: null,\n // Defaulting reviewState to open for any event may not be the desired behavior.\n // For instance, if a subject never had any event and we just want to leave a comment to keep an eye on it\n // that shouldn't mean we want to review the subject\n reviewState: REVIEWNONE,\n recordCid: subjectCid || null,\n ageAssuranceState: currentStatus?.ageAssuranceState || 'unknown',\n }\n const newStatus = {\n ...defaultData,\n ...subjectStatus,\n }\n\n if (\n action === 'tools.ozone.moderation.defs#modEventPriorityScore' &&\n typeof meta?.priorityScore === 'number'\n ) {\n newStatus.priorityScore = meta?.priorityScore\n subjectStatus.priorityScore = meta?.priorityScore\n }\n\n if (\n action === 'tools.ozone.moderation.defs#modEventReverseTakedown' &&\n !subjectStatus.takendown\n ) {\n newStatus.takendown = false\n subjectStatus.takendown = false\n }\n\n if (isAppealEvent) {\n newStatus.appealed = true\n subjectStatus.appealed = true\n newStatus.lastAppealedAt = createdAt\n subjectStatus.lastAppealedAt = createdAt\n // Set reviewState to escalated when appeal events are emitted\n subjectStatus.reviewState = REVIEWESCALATED\n newStatus.reviewState = REVIEWESCALATED\n }\n\n if (\n action === 'tools.ozone.moderation.defs#modEventResolveAppeal' &&\n subjectStatus.appealed\n ) {\n newStatus.appealed = false\n subjectStatus.appealed = false\n }\n\n if (\n action === 'tools.ozone.moderation.defs#modEventComment' &&\n meta?.sticky\n ) {\n newStatus.comment = comment\n subjectStatus.comment = comment\n }\n\n if (action === 'tools.ozone.moderation.defs#modEventTag') {\n let tags = currentStatus?.tags || []\n if (addedTags?.length) {\n tags = tags.concat(addedTags)\n }\n if (removedTags?.length) {\n tags = tags.filter((tag) => !removedTags.includes(tag))\n }\n newStatus.tags = jsonb([...new Set(tags)]) as unknown as string[]\n subjectStatus.tags = newStatus.tags\n }\n\n if (action === 'tools.ozone.moderation.defs#ageAssuranceEvent') {\n // Only when the last update was made by an admin AND state was set to reset user event can override final state\n if (\n currentStatus?.ageAssuranceUpdatedBy !== 'admin' ||\n currentStatus?.ageAssuranceState === 'reset'\n ) {\n if (typeof meta?.status === 'string') {\n newStatus.ageAssuranceState = meta.status\n subjectStatus.ageAssuranceState = meta.status\n newStatus.ageAssuranceUpdatedBy = 'user'\n subjectStatus.ageAssuranceUpdatedBy = 'user'\n }\n }\n }\n\n if (action === 'tools.ozone.moderation.defs#ageAssuranceOverrideEvent') {\n if (typeof meta?.status === 'string') {\n newStatus.ageAssuranceState = meta.status\n subjectStatus.ageAssuranceState = meta.status\n newStatus.ageAssuranceUpdatedBy = 'admin'\n subjectStatus.ageAssuranceUpdatedBy = 'admin'\n }\n }\n\n if (action === 'tools.ozone.moderation.defs#ageAssurancePurgeEvent') {\n newStatus.ageAssuranceState = 'unknown'\n subjectStatus.ageAssuranceState = 'unknown'\n newStatus.ageAssuranceUpdatedBy = null\n subjectStatus.ageAssuranceUpdatedBy = null\n }\n\n if (blobCids?.length) {\n const newBlobCids = jsonb(\n blobCids,\n ) as unknown as ModerationSubjectStatusRow['blobCids']\n newStatus.blobCids = newBlobCids\n subjectStatus.blobCids = newBlobCids\n }\n\n const insertQuery = db.db\n .insertInto('moderation_subject_status')\n .values({\n ...identifier,\n ...newStatus,\n createdAt: now,\n updatedAt: now,\n } as ModerationSubjectStatusRow)\n .onConflict((oc) =>\n oc.constraint('moderation_status_unique_idx').doUpdateSet({\n ...subjectStatus,\n updatedAt: now,\n }),\n )\n\n const status = await insertQuery.returningAll().executeTakeFirst()\n return status || null\n}\n\nexport const getStatusIdentifierFromSubject = (\n subject: string | AtUri,\n): { did: string; recordPath: string } => {\n const isSubjectString = typeof subject === 'string'\n if (isSubjectString && subject.startsWith('did:')) {\n return {\n did: subject,\n recordPath: '',\n }\n }\n\n if (isSubjectString && !subject.startsWith('at://')) {\n throw new Error('Subject is neither a did nor an at-uri')\n }\n\n const uri = isSubjectString ? new AtUri(subject) : subject\n return {\n did: uri.host,\n recordPath: `${uri.collection}/${uri.rkey}`,\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/ozone",
3
- "version": "0.1.166",
3
+ "version": "0.1.168",
4
4
  "license": "MIT",
5
5
  "description": "Backend service for moderating the Bluesky network.",
6
6
  "keywords": [
@@ -35,15 +35,15 @@
35
35
  "uint8arrays": "3.0.0",
36
36
  "undici": "^6.14.1",
37
37
  "ws": "^8.12.0",
38
- "@atproto/api": "^0.19.2",
39
- "@atproto/common": "^0.5.14",
38
+ "@atproto/api": "^0.19.5",
39
+ "@atproto/common": "^0.5.15",
40
40
  "@atproto/crypto": "^0.4.5",
41
41
  "@atproto/identity": "^0.4.12",
42
42
  "@atproto/lexicon": "^0.6.2",
43
- "@atproto/syntax": "^0.5.0",
43
+ "@atproto/syntax": "^0.5.2",
44
44
  "@atproto/ws-client": "^0.0.4",
45
45
  "@atproto/xrpc": "^0.7.7",
46
- "@atproto/xrpc-server": "^0.10.15"
46
+ "@atproto/xrpc-server": "^0.10.18"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@did-plc/server": "^0.0.1",
@@ -55,8 +55,8 @@
55
55
  "jest": "^28.1.2",
56
56
  "ts-node": "^10.8.2",
57
57
  "typescript": "^5.6.3",
58
- "@atproto/pds": "^0.4.214",
59
- "@atproto/lex-cli": "^0.9.9"
58
+ "@atproto/lex-cli": "^0.9.9",
59
+ "@atproto/pds": "^0.4.217"
60
60
  },
61
61
  "scripts": {
62
62
  "codegen": "lex gen-server --yes ./src/lexicon ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/* ../../lexicons/chat/bsky/*/* ../../lexicons/tools/ozone/*/*",
@@ -1,5 +1,9 @@
1
1
  import { isModEventDivert } from '@atproto/api/dist/client/types/tools/ozone/moderation/defs'
2
- import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server'
2
+ import {
3
+ AuthRequiredError,
4
+ ForbiddenError,
5
+ InvalidRequestError,
6
+ } from '@atproto/xrpc-server'
3
7
  import { AdminTokenOutput, ModeratorOutput } from '../../auth-verifier'
4
8
  import { AppContext } from '../../context'
5
9
  import { Server } from '../../lexicon'
@@ -8,6 +12,7 @@ import {
8
12
  ModEventTag,
9
13
  isAgeAssuranceEvent,
10
14
  isAgeAssuranceOverrideEvent,
15
+ isAgeAssurancePurgeEvent,
11
16
  isModEventAcknowledge,
12
17
  isModEventEmail,
13
18
  isModEventLabel,
@@ -71,6 +76,17 @@ const handleModerationEvent = async ({
71
76
  }
72
77
  }
73
78
 
79
+ if (isAgeAssurancePurgeEvent(event)) {
80
+ if (!subject.isRepo()) {
81
+ throw new InvalidRequestError('Invalid subject type')
82
+ }
83
+ if (!auth.credentials.isModerator) {
84
+ throw new ForbiddenError(
85
+ 'Must be a moderator to purge age assurance events',
86
+ )
87
+ }
88
+ }
89
+
74
90
  if (isRevokeAccountCredentialsEvent(event)) {
75
91
  if (!subject.isRepo()) {
76
92
  throw new InvalidRequestError('Invalid subject type')
@@ -24,10 +24,8 @@ export default function (server: Server, ctx: AppContext) {
24
24
 
25
25
  const db = ctx.db
26
26
 
27
- await Promise.all([
28
- assertValidReporter(ctx.modService(db), reasonType, requester),
29
- ctx.moderationServiceProfile().validateReasonType(reasonType),
30
- ])
27
+ await ctx.moderationServiceProfile().validateReasonType(reasonType)
28
+ await assertValidReporter(ctx.modService(db), reasonType, requester)
31
29
 
32
30
  const report = await db.transaction(async (dbTxn) => {
33
31
  const moderationTxn = ctx.modService(dbTxn)
package/src/api/util.ts CHANGED
@@ -157,6 +157,7 @@ const eventTypes = new Set([
157
157
  'tools.ozone.moderation.defs#modEventPriorityScore',
158
158
  'tools.ozone.moderation.defs#ageAssuranceEvent',
159
159
  'tools.ozone.moderation.defs#ageAssuranceOverrideEvent',
160
+ 'tools.ozone.moderation.defs#ageAssurancePurgeEvent',
160
161
  'tools.ozone.moderation.defs#revokeAccountCredentialsEvent',
161
162
  'tools.ozone.moderation.defs#scheduleTakedownEvent',
162
163
  'tools.ozone.moderation.defs#cancelScheduledTakedownEvent',
@@ -25,6 +25,7 @@ export interface ModerationEvent {
25
25
  | 'tools.ozone.moderation.defs#modEventPriorityScore'
26
26
  | 'tools.ozone.moderation.defs#ageAssuranceEvent'
27
27
  | 'tools.ozone.moderation.defs#ageAssuranceOverrideEvent'
28
+ | 'tools.ozone.moderation.defs#ageAssurancePurgeEvent'
28
29
  | 'tools.ozone.moderation.defs#revokeAccountCredentialsEvent'
29
30
  subjectType:
30
31
  | 'com.atproto.admin.defs#repoRef'
@@ -6243,21 +6243,21 @@ export const schemaDict = {
6243
6243
  ref: 'lex:app.bsky.actor.defs#profileView',
6244
6244
  },
6245
6245
  },
6246
+ recIdStr: {
6247
+ type: 'string',
6248
+ description:
6249
+ 'Snowflake for this recommendation, use when submitting recommendation events.',
6250
+ },
6246
6251
  isFallback: {
6247
6252
  type: 'boolean',
6248
6253
  description:
6249
- 'If true, response has fallen-back to generic results, and is not scoped using relativeToDid',
6254
+ 'DEPRECATED, unused. Previously: if true, response has fallen-back to generic results, and is not scoped using relativeToDid',
6250
6255
  default: false,
6251
6256
  },
6252
6257
  recId: {
6253
6258
  type: 'integer',
6254
6259
  description: 'DEPRECATED: use recIdStr instead.',
6255
6260
  },
6256
- recIdStr: {
6257
- type: 'string',
6258
- description:
6259
- 'Snowflake for this recommendation, use when submitting recommendation events.',
6260
- },
6261
6261
  },
6262
6262
  },
6263
6263
  },
@@ -11842,16 +11842,13 @@ export const schemaDict = {
11842
11842
  type: 'string',
11843
11843
  knownValues: [
11844
11844
  '!hide',
11845
- '!no-promote',
11846
11845
  '!warn',
11847
11846
  '!no-unauthenticated',
11848
- 'dmca-violation',
11849
- 'doxxing',
11850
11847
  'porn',
11851
11848
  'sexual',
11852
11849
  'nudity',
11853
- 'nsfl',
11854
- 'gore',
11850
+ 'graphic-media',
11851
+ 'bot',
11855
11852
  ],
11856
11853
  },
11857
11854
  },
@@ -15949,6 +15946,7 @@ export const schemaDict = {
15949
15946
  'lex:tools.ozone.moderation.defs#modEventPriorityScore',
15950
15947
  'lex:tools.ozone.moderation.defs#ageAssuranceEvent',
15951
15948
  'lex:tools.ozone.moderation.defs#ageAssuranceOverrideEvent',
15949
+ 'lex:tools.ozone.moderation.defs#ageAssurancePurgeEvent',
15952
15950
  'lex:tools.ozone.moderation.defs#revokeAccountCredentialsEvent',
15953
15951
  'lex:tools.ozone.moderation.defs#scheduleTakedownEvent',
15954
15952
  'lex:tools.ozone.moderation.defs#cancelScheduledTakedownEvent',
@@ -16026,6 +16024,7 @@ export const schemaDict = {
16026
16024
  'lex:tools.ozone.moderation.defs#modEventPriorityScore',
16027
16025
  'lex:tools.ozone.moderation.defs#ageAssuranceEvent',
16028
16026
  'lex:tools.ozone.moderation.defs#ageAssuranceOverrideEvent',
16027
+ 'lex:tools.ozone.moderation.defs#ageAssurancePurgeEvent',
16029
16028
  'lex:tools.ozone.moderation.defs#revokeAccountCredentialsEvent',
16030
16029
  'lex:tools.ozone.moderation.defs#scheduleTakedownEvent',
16031
16030
  'lex:tools.ozone.moderation.defs#cancelScheduledTakedownEvent',
@@ -16592,6 +16591,19 @@ export const schemaDict = {
16592
16591
  },
16593
16592
  },
16594
16593
  },
16594
+ ageAssurancePurgeEvent: {
16595
+ type: 'object',
16596
+ description:
16597
+ 'Purges all age assurance events for the subject. Only works on DID subjects. Moderator-only.',
16598
+ required: ['comment'],
16599
+ properties: {
16600
+ comment: {
16601
+ type: 'string',
16602
+ minLength: 1,
16603
+ description: 'Comment describing the reason for the purge.',
16604
+ },
16605
+ },
16606
+ },
16595
16607
  revokeAccountCredentialsEvent: {
16596
16608
  type: 'object',
16597
16609
  description:
@@ -17465,6 +17477,7 @@ export const schemaDict = {
17465
17477
  'lex:tools.ozone.moderation.defs#modEventPriorityScore',
17466
17478
  'lex:tools.ozone.moderation.defs#ageAssuranceEvent',
17467
17479
  'lex:tools.ozone.moderation.defs#ageAssuranceOverrideEvent',
17480
+ 'lex:tools.ozone.moderation.defs#ageAssurancePurgeEvent',
17468
17481
  'lex:tools.ozone.moderation.defs#revokeAccountCredentialsEvent',
17469
17482
  'lex:tools.ozone.moderation.defs#scheduleTakedownEvent',
17470
17483
  'lex:tools.ozone.moderation.defs#cancelScheduledTakedownEvent',
@@ -22,12 +22,12 @@ export type InputSchema = undefined
22
22
 
23
23
  export interface OutputSchema {
24
24
  suggestions: AppBskyActorDefs.ProfileView[]
25
- /** If true, response has fallen-back to generic results, and is not scoped using relativeToDid */
25
+ /** Snowflake for this recommendation, use when submitting recommendation events. */
26
+ recIdStr?: string
27
+ /** DEPRECATED, unused. Previously: if true, response has fallen-back to generic results, and is not scoped using relativeToDid */
26
28
  isFallback?: boolean
27
29
  /** DEPRECATED: use recIdStr instead. */
28
30
  recId?: number
29
- /** Snowflake for this recommendation, use when submitting recommendation events. */
30
- recIdStr?: string
31
31
  }
32
32
 
33
33
  export type HandlerInput = void
@@ -133,14 +133,11 @@ export function validateLabelValueDefinitionStrings<V>(v: V) {
133
133
 
134
134
  export type LabelValue =
135
135
  | '!hide'
136
- | '!no-promote'
137
136
  | '!warn'
138
137
  | '!no-unauthenticated'
139
- | 'dmca-violation'
140
- | 'doxxing'
141
138
  | 'porn'
142
139
  | 'sexual'
143
140
  | 'nudity'
144
- | 'nsfl'
145
- | 'gore'
141
+ | 'graphic-media'
142
+ | 'bot'
146
143
  | (string & {})
@@ -46,6 +46,7 @@ export interface ModEventView {
46
46
  | $Typed<ModEventPriorityScore>
47
47
  | $Typed<AgeAssuranceEvent>
48
48
  | $Typed<AgeAssuranceOverrideEvent>
49
+ | $Typed<AgeAssurancePurgeEvent>
49
50
  | $Typed<RevokeAccountCredentialsEvent>
50
51
  | $Typed<ScheduleTakedownEvent>
51
52
  | $Typed<CancelScheduledTakedownEvent>
@@ -98,6 +99,7 @@ export interface ModEventViewDetail {
98
99
  | $Typed<ModEventPriorityScore>
99
100
  | $Typed<AgeAssuranceEvent>
100
101
  | $Typed<AgeAssuranceOverrideEvent>
102
+ | $Typed<AgeAssurancePurgeEvent>
101
103
  | $Typed<RevokeAccountCredentialsEvent>
102
104
  | $Typed<ScheduleTakedownEvent>
103
105
  | $Typed<CancelScheduledTakedownEvent>
@@ -503,6 +505,23 @@ export function validateAgeAssuranceOverrideEvent<V>(v: V) {
503
505
  )
504
506
  }
505
507
 
508
+ /** Purges all age assurance events for the subject. Only works on DID subjects. Moderator-only. */
509
+ export interface AgeAssurancePurgeEvent {
510
+ $type?: 'tools.ozone.moderation.defs#ageAssurancePurgeEvent'
511
+ /** Comment describing the reason for the purge. */
512
+ comment: string
513
+ }
514
+
515
+ const hashAgeAssurancePurgeEvent = 'ageAssurancePurgeEvent'
516
+
517
+ export function isAgeAssurancePurgeEvent<V>(v: V) {
518
+ return is$typed(v, id, hashAgeAssurancePurgeEvent)
519
+ }
520
+
521
+ export function validateAgeAssurancePurgeEvent<V>(v: V) {
522
+ return validate<AgeAssurancePurgeEvent & V>(v, id, hashAgeAssurancePurgeEvent)
523
+ }
524
+
506
525
  /** Account credentials revocation by moderators. Only works on DID subjects. */
507
526
  export interface RevokeAccountCredentialsEvent {
508
527
  $type?: 'tools.ozone.moderation.defs#revokeAccountCredentialsEvent'
@@ -42,6 +42,7 @@ export interface InputSchema {
42
42
  | $Typed<ToolsOzoneModerationDefs.ModEventPriorityScore>
43
43
  | $Typed<ToolsOzoneModerationDefs.AgeAssuranceEvent>
44
44
  | $Typed<ToolsOzoneModerationDefs.AgeAssuranceOverrideEvent>
45
+ | $Typed<ToolsOzoneModerationDefs.AgeAssurancePurgeEvent>
45
46
  | $Typed<ToolsOzoneModerationDefs.RevokeAccountCredentialsEvent>
46
47
  | $Typed<ToolsOzoneModerationDefs.ScheduleTakedownEvent>
47
48
  | $Typed<ToolsOzoneModerationDefs.CancelScheduledTakedownEvent>
@@ -28,6 +28,7 @@ import {
28
28
  isAccountEvent,
29
29
  isAgeAssuranceEvent,
30
30
  isAgeAssuranceOverrideEvent,
31
+ isAgeAssurancePurgeEvent,
31
32
  isIdentityEvent,
32
33
  isModEventAcknowledge,
33
34
  isModEventComment,
@@ -665,6 +666,10 @@ export class ModerationService {
665
666
  subject.blobCids,
666
667
  )
667
668
 
669
+ if (isAgeAssurancePurgeEvent(event)) {
670
+ await this.purgeAgeAssuranceEvents(subjectInfo.subjectDid)
671
+ }
672
+
668
673
  // Updates are only needed if strikeCount is numeric (in some cases even 0)
669
674
  if (modEvent.strikeCount !== null) {
670
675
  try {
@@ -681,6 +686,15 @@ export class ModerationService {
681
686
  return { event: modEvent, subjectStatus }
682
687
  }
683
688
 
689
+ async purgeAgeAssuranceEvents(subjectDid: string) {
690
+ this.db.assertTransaction()
691
+ await this.db.db
692
+ .deleteFrom('moderation_event')
693
+ .where('subjectDid', '=', subjectDid)
694
+ .where('action', '=', 'tools.ozone.moderation.defs#ageAssuranceEvent')
695
+ .execute()
696
+ }
697
+
684
698
  async getLastReversibleEventForSubject(subject: ReversalSubject) {
685
699
  // If the subject is neither suspended nor muted don't bother finding the last reversible event
686
700
  // Ideally, this should never happen because the caller of this method should only call this
@@ -125,6 +125,7 @@ const getSubjectStatusForModerationEvent = ({
125
125
  }
126
126
  case 'tools.ozone.moderation.defs#ageAssuranceEvent':
127
127
  case 'tools.ozone.moderation.defs#ageAssuranceOverrideEvent':
128
+ case 'tools.ozone.moderation.defs#ageAssurancePurgeEvent':
128
129
  return {
129
130
  reviewState: defaultReviewState,
130
131
  }
@@ -469,6 +470,13 @@ export const adjustModerationSubjectStatus = async (
469
470
  }
470
471
  }
471
472
 
473
+ if (action === 'tools.ozone.moderation.defs#ageAssurancePurgeEvent') {
474
+ newStatus.ageAssuranceState = 'unknown'
475
+ subjectStatus.ageAssuranceState = 'unknown'
476
+ newStatus.ageAssuranceUpdatedBy = null
477
+ subjectStatus.ageAssuranceUpdatedBy = null
478
+ }
479
+
472
480
  if (blobCids?.length) {
473
481
  const newBlobCids = jsonb(
474
482
  blobCids,
@@ -43,8 +43,8 @@ describe('labels from 3p labelers', () => {
43
43
  pdsDid: network.pds.ctx.cfg.service.did,
44
44
  })
45
45
 
46
- thirdPartyAgent = thirdPartyLabeler.getClient()
47
- agent = ozone.getClient()
46
+ thirdPartyAgent = thirdPartyLabeler.getAgent()
47
+ agent = ozone.getAgent()
48
48
  sc = network.getSeedClient()
49
49
  modClient = ozone.getModClient()
50
50
  thirdPartyModClient = thirdPartyLabeler.getModClient()
@@ -10,7 +10,7 @@ Object {
10
10
  "allowSubscriptions": "followers",
11
11
  },
12
12
  },
13
- "avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)@jpeg",
13
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)",
14
14
  "createdAt": "1970-01-01T00:00:00.000Z",
15
15
  "did": "user(0)",
16
16
  "displayName": "ali",
@@ -45,7 +45,7 @@ Object {
45
45
  "allowSubscriptions": "followers",
46
46
  },
47
47
  },
48
- "avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)@jpeg",
48
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)",
49
49
  "createdAt": "1970-01-01T00:00:00.000Z",
50
50
  "description": "its me!",
51
51
  "did": "user(0)",
@@ -123,7 +123,7 @@ Object {
123
123
  "allowSubscriptions": "followers",
124
124
  },
125
125
  },
126
- "avatar": "https://bsky.public.url/img/avatar/plain/user(4)/cids(1)@jpeg",
126
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(4)/cids(1)",
127
127
  "createdAt": "1970-01-01T00:00:00.000Z",
128
128
  "description": "hi im bob label_me",
129
129
  "did": "user(3)",
@@ -184,7 +184,7 @@ Object {
184
184
  "allowSubscriptions": "followers",
185
185
  },
186
186
  },
187
- "avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)@jpeg",
187
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)",
188
188
  "createdAt": "1970-01-01T00:00:00.000Z",
189
189
  "did": "user(0)",
190
190
  "displayName": "ali",
@@ -219,7 +219,7 @@ Object {
219
219
  "allowSubscriptions": "followers",
220
220
  },
221
221
  },
222
- "avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)@jpeg",
222
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)",
223
223
  "createdAt": "1970-01-01T00:00:00.000Z",
224
224
  "description": "its me!",
225
225
  "did": "user(0)",
@@ -299,7 +299,7 @@ Object {
299
299
  "allowSubscriptions": "followers",
300
300
  },
301
301
  },
302
- "avatar": "https://bsky.public.url/img/avatar/plain/user(4)/cids(1)@jpeg",
302
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(4)/cids(1)",
303
303
  "createdAt": "1970-01-01T00:00:00.000Z",
304
304
  "description": "hi im bob label_me",
305
305
  "did": "user(3)",
@@ -362,7 +362,7 @@ Object {
362
362
  "allowSubscriptions": "followers",
363
363
  },
364
364
  },
365
- "avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)@jpeg",
365
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)",
366
366
  "createdAt": "1970-01-01T00:00:00.000Z",
367
367
  "did": "user(0)",
368
368
  "displayName": "ali",
@@ -397,7 +397,7 @@ Object {
397
397
  "allowSubscriptions": "followers",
398
398
  },
399
399
  },
400
- "avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)@jpeg",
400
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)",
401
401
  "createdAt": "1970-01-01T00:00:00.000Z",
402
402
  "description": "its me!",
403
403
  "did": "user(0)",
@@ -483,7 +483,7 @@ Object {
483
483
  "allowSubscriptions": "followers",
484
484
  },
485
485
  },
486
- "avatar": "https://bsky.public.url/img/avatar/plain/user(5)/cids(1)@jpeg",
486
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(5)/cids(1)",
487
487
  "createdAt": "1970-01-01T00:00:00.000Z",
488
488
  "description": "hi im bob label_me",
489
489
  "did": "user(4)",
@@ -14,7 +14,7 @@ Array [
14
14
  "lists": 0,
15
15
  "starterPacks": 0,
16
16
  },
17
- "avatar": "https://bsky.public.url/img/avatar/plain/user(3)/cids(0)@jpeg",
17
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(3)/cids(0)",
18
18
  "createdAt": "1970-01-01T00:00:00.000Z",
19
19
  "description": "its me!",
20
20
  "did": "user(0)",
@@ -199,7 +199,7 @@ Array [
199
199
  "lists": 0,
200
200
  "starterPacks": 0,
201
201
  },
202
- "avatar": "https://bsky.public.url/img/avatar/plain/user(3)/cids(0)@jpeg",
202
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(3)/cids(0)",
203
203
  "createdAt": "1970-01-01T00:00:00.000Z",
204
204
  "description": "its me!",
205
205
  "did": "user(0)",
@@ -92,7 +92,7 @@ Array [
92
92
  "lists": 0,
93
93
  "starterPacks": 0,
94
94
  },
95
- "avatar": "https://bsky.public.url/img/avatar/plain/user(6)/cids(0)@jpeg",
95
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(6)/cids(0)",
96
96
  "createdAt": "1970-01-01T00:00:00.000Z",
97
97
  "description": "its me!",
98
98
  "did": "user(5)",
@@ -141,7 +141,7 @@ Array [
141
141
  "lists": 0,
142
142
  "starterPacks": 0,
143
143
  },
144
- "avatar": "https://bsky.public.url/img/avatar/plain/user(8)/cids(0)@jpeg",
144
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(8)/cids(0)",
145
145
  "createdAt": "1970-01-01T00:00:00.000Z",
146
146
  "description": "hi im bob label_me",
147
147
  "did": "user(7)",
@@ -273,7 +273,7 @@ Array [
273
273
  "lists": 0,
274
274
  "starterPacks": 0,
275
275
  },
276
- "avatar": "https://bsky.public.url/img/avatar/plain/user(6)/cids(0)@jpeg",
276
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(6)/cids(0)",
277
277
  "createdAt": "1970-01-01T00:00:00.000Z",
278
278
  "description": "its me!",
279
279
  "did": "user(5)",
@@ -322,7 +322,7 @@ Array [
322
322
  "lists": 0,
323
323
  "starterPacks": 0,
324
324
  },
325
- "avatar": "https://bsky.public.url/img/avatar/plain/user(8)/cids(0)@jpeg",
325
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(8)/cids(0)",
326
326
  "createdAt": "1970-01-01T00:00:00.000Z",
327
327
  "description": "hi im bob label_me",
328
328
  "did": "user(7)",