From c559e7e3dee65b51f1d9dec8f4188db79e835e2d Mon Sep 17 00:00:00 2001 From: Foysal Ahamed Date: Wed, 18 Dec 2024 22:57:27 +0000 Subject: [PATCH] :sparkles: Add subject tags with report type (#3260) * :sparkles: Add subject tags with report type * :white_check_mark: Update snapshots * :white_check_mark: Update snpshot for pds admin * :white_check_mark: Update ozone snapshot * :white_check_mark: Update ozone snapshot * :broom: Cleanup --- .../ozone/src/api/moderation/emitEvent.ts | 7 +++- packages/ozone/src/api/report/createReport.ts | 3 +- packages/ozone/src/tag-service/index.ts | 21 +++++++--- packages/ozone/src/tag-service/util.ts | 5 +++ .../__snapshots__/get-record.test.ts.snap | 4 ++ .../__snapshots__/get-records.test.ts.snap | 2 + .../tests/__snapshots__/get-repo.test.ts.snap | 2 + .../__snapshots__/get-repos.test.ts.snap | 2 + .../moderation-events.test.ts.snap | 10 +++-- .../moderation-statuses.test.ts.snap | 6 +++ .../__snapshots__/moderation.test.ts.snap | 8 ++-- packages/ozone/tests/content-tagger.test.ts | 29 ++++++++++++++ packages/ozone/tests/report-muting.test.ts | 2 +- .../proxied/__snapshots__/admin.test.ts.snap | 39 +++++++++++++++---- 14 files changed, 118 insertions(+), 22 deletions(-) create mode 100644 packages/ozone/src/tag-service/util.ts diff --git a/packages/ozone/src/api/moderation/emitEvent.ts b/packages/ozone/src/api/moderation/emitEvent.ts index de9de62435a..7bf337e005b 100644 --- a/packages/ozone/src/api/moderation/emitEvent.ts +++ b/packages/ozone/src/api/moderation/emitEvent.ts @@ -7,6 +7,7 @@ import { isModEventEmail, isModEventLabel, isModEventMuteReporter, + isModEventReport, isModEventReverseTakedown, isModEventTag, isModEventTakedown, @@ -21,6 +22,7 @@ import { ModeratorOutput, AdminTokenOutput } from '../../auth-verifier' import { SettingService } from '../../setting/service' import { ProtectedTagSettingKey } from '../../setting/constants' import { ProtectedTagSetting } from '../../setting/types' +import { getTagForReport } from '../../tag-service/util' const handleModerationEvent = async ({ ctx, @@ -160,7 +162,10 @@ const handleModerationEvent = async ({ ctx.cfg.service.did, moderationTxn, ) - await tagService.evaluateForSubject() + const initialTags = isModEventReport(event) + ? [getTagForReport(event.reportType)] + : undefined + await tagService.evaluateForSubject(initialTags) if (subject.isRepo()) { if (isTakedownEvent) { diff --git a/packages/ozone/src/api/report/createReport.ts b/packages/ozone/src/api/report/createReport.ts index dc76b22763a..80f8eedfea2 100644 --- a/packages/ozone/src/api/report/createReport.ts +++ b/packages/ozone/src/api/report/createReport.ts @@ -5,6 +5,7 @@ import { subjectFromInput } from '../../mod-service/subject' import { REASONAPPEAL } from '../../lexicon/types/com/atproto/moderation/defs' import { ForbiddenError } from '@atproto/xrpc-server' import { TagService } from '../../tag-service' +import { getTagForReport } from '../../tag-service/util' export default function (server: Server, ctx: AppContext) { server.com.atproto.moderation.createReport({ @@ -37,7 +38,7 @@ export default function (server: Server, ctx: AppContext) { ctx.cfg.service.did, moderationTxn, ) - await tagService.evaluateForSubject() + await tagService.evaluateForSubject([getTagForReport(reasonType)]) return reportEvent }) diff --git a/packages/ozone/src/tag-service/index.ts b/packages/ozone/src/tag-service/index.ts index def3f6193f7..90dc83524d7 100644 --- a/packages/ozone/src/tag-service/index.ts +++ b/packages/ozone/src/tag-service/index.ts @@ -22,15 +22,18 @@ export class TagService { ] } - async evaluateForSubject() { + // Allow the caller to seed the initial tags + async evaluateForSubject(initialTags?: Iterable) { try { - const tags: string[] = [] + const tags = new Set(initialTags) await Promise.all( this.taggers.map(async (tagger) => { try { const newTags = await tagger.getTags() - if (newTags.length) tags.push(...newTags) + for (const newTag of newTags) { + tags.add(newTag) + } } catch (e) { // Don't let one tagger error stop the rest from running log.error( @@ -41,11 +44,19 @@ export class TagService { }), ) - if (tags.length > 0) { + // Ensure that before inserting new tags, we discard any tag that may + // have been evaluated to be added but is already present in the subject + if (this.subjectStatus?.tags?.length) { + for (const tag of this.subjectStatus.tags) { + tags.delete(tag) + } + } + + if (tags.size) { await this.moderationService.logEvent({ event: { $type: 'tools.ozone.moderation.defs#modEventTag', - add: tags, + add: [...tags], remove: [], }, subject: this.subject, diff --git a/packages/ozone/src/tag-service/util.ts b/packages/ozone/src/tag-service/util.ts new file mode 100644 index 00000000000..c710b2f0dc6 --- /dev/null +++ b/packages/ozone/src/tag-service/util.ts @@ -0,0 +1,5 @@ +import { ReasonType } from '../lexicon/types/com/atproto/moderation/defs' + +export const getTagForReport = (reasonType: ReasonType) => { + return `report:${reasonType.replace('com.atproto.moderation.defs#reason', '').toLowerCase()}` +} diff --git a/packages/ozone/tests/__snapshots__/get-record.test.ts.snap b/packages/ozone/tests/__snapshots__/get-record.test.ts.snap index b577606360f..d24b9d76106 100644 --- a/packages/ozone/tests/__snapshots__/get-record.test.ts.snap +++ b/packages/ozone/tests/__snapshots__/get-record.test.ts.snap @@ -46,7 +46,9 @@ Object { "subjectBlobCids": Array [], "subjectRepoHandle": "alice.test", "tags": Array [ + "report:spam", "lang:en", + "report:other", ], "takendown": true, "updatedAt": "1970-01-01T00:00:00.000Z", @@ -150,7 +152,9 @@ Object { "subjectBlobCids": Array [], "subjectRepoHandle": "alice.test", "tags": Array [ + "report:spam", "lang:en", + "report:other", ], "takendown": true, "updatedAt": "1970-01-01T00:00:00.000Z", diff --git a/packages/ozone/tests/__snapshots__/get-records.test.ts.snap b/packages/ozone/tests/__snapshots__/get-records.test.ts.snap index e2ff81c28e0..38d0efdebca 100644 --- a/packages/ozone/tests/__snapshots__/get-records.test.ts.snap +++ b/packages/ozone/tests/__snapshots__/get-records.test.ts.snap @@ -49,7 +49,9 @@ Object { "subjectBlobCids": Array [], "subjectRepoHandle": "alice.test", "tags": Array [ + "report:spam", "lang:en", + "report:other", ], "takendown": true, "updatedAt": "1970-01-01T00:00:00.000Z", diff --git a/packages/ozone/tests/__snapshots__/get-repo.test.ts.snap b/packages/ozone/tests/__snapshots__/get-repo.test.ts.snap index 9c3aada6ca8..397a817ec0b 100644 --- a/packages/ozone/tests/__snapshots__/get-repo.test.ts.snap +++ b/packages/ozone/tests/__snapshots__/get-repo.test.ts.snap @@ -40,6 +40,8 @@ Object { "subjectRepoHandle": "alice.test", "tags": Array [ "lang:und", + "report:spam", + "report:other", ], "takendown": true, "updatedAt": "1970-01-01T00:00:00.000Z", diff --git a/packages/ozone/tests/__snapshots__/get-repos.test.ts.snap b/packages/ozone/tests/__snapshots__/get-repos.test.ts.snap index f1a4cf4b14f..d0db4fcf9de 100644 --- a/packages/ozone/tests/__snapshots__/get-repos.test.ts.snap +++ b/packages/ozone/tests/__snapshots__/get-repos.test.ts.snap @@ -43,6 +43,8 @@ Object { "subjectRepoHandle": "alice.test", "tags": Array [ "lang:und", + "report:spam", + "report:other", ], "takendown": true, "updatedAt": "1970-01-01T00:00:00.000Z", diff --git a/packages/ozone/tests/__snapshots__/moderation-events.test.ts.snap b/packages/ozone/tests/__snapshots__/moderation-events.test.ts.snap index 0e2d414f2df..8e4b5a62c84 100644 --- a/packages/ozone/tests/__snapshots__/moderation-events.test.ts.snap +++ b/packages/ozone/tests/__snapshots__/moderation-events.test.ts.snap @@ -35,7 +35,9 @@ Object { "subjectBlobCids": Array [], "subjectRepoHandle": "alice.test", "tags": Array [ + "report:misleading", "lang:und", + "report:spam", ], "takendown": false, "updatedAt": "1970-01-01T00:00:00.000Z", @@ -101,6 +103,7 @@ Array [ "event": Object { "$type": "tools.ozone.moderation.defs#modEventTag", "add": Array [ + "report:spam", "lang:en", "lang:i", ], @@ -163,6 +166,7 @@ Array [ "event": Object { "$type": "tools.ozone.moderation.defs#modEventTag", "add": Array [ + "report:spam", "lang:en", ], "remove": Array [], @@ -210,7 +214,7 @@ Array [ "isReporterMuted": false, "reportType": "com.atproto.moderation.defs#reasonSpam", }, - "id": 16, + "id": 17, "subject": Object { "$type": "com.atproto.admin.defs#repoRef", "did": "user(0)", @@ -251,7 +255,7 @@ Array [ "isReporterMuted": false, "reportType": "com.atproto.moderation.defs#reasonSpam", }, - "id": 16, + "id": 17, "subject": Object { "$type": "com.atproto.admin.defs#repoRef", "did": "user(0)", @@ -292,7 +296,7 @@ Array [ "isReporterMuted": false, "reportType": "com.atproto.moderation.defs#reasonSpam", }, - "id": 16, + "id": 17, "subject": Object { "$type": "com.atproto.admin.defs#repoRef", "did": "user(0)", diff --git a/packages/ozone/tests/__snapshots__/moderation-statuses.test.ts.snap b/packages/ozone/tests/__snapshots__/moderation-statuses.test.ts.snap index 330a3f7a45f..92b4646c120 100644 --- a/packages/ozone/tests/__snapshots__/moderation-statuses.test.ts.snap +++ b/packages/ozone/tests/__snapshots__/moderation-statuses.test.ts.snap @@ -19,6 +19,7 @@ Array [ "subjectBlobCids": Array [], "subjectRepoHandle": "bob.test", "tags": Array [ + "report:spam", "lang:en", "lang:i", ], @@ -41,6 +42,7 @@ Array [ "subjectBlobCids": Array [], "subjectRepoHandle": "bob.test", "tags": Array [ + "report:spam", "lang:en", "lang:i", ], @@ -69,6 +71,7 @@ Array [ "subjectBlobCids": Array [], "subjectRepoHandle": "bob.test", "tags": Array [ + "report:spam", "lang:en", "lang:i", ], @@ -91,6 +94,7 @@ Array [ "subjectBlobCids": Array [], "subjectRepoHandle": "bob.test", "tags": Array [ + "report:spam", "lang:en", "lang:i", ], @@ -114,6 +118,7 @@ Array [ "subjectBlobCids": Array [], "subjectRepoHandle": "alice.test", "tags": Array [ + "report:spam", "lang:ha", ], "takendown": false, @@ -135,6 +140,7 @@ Array [ "subjectBlobCids": Array [], "subjectRepoHandle": "alice.test", "tags": Array [ + "report:misleading", "lang:und", ], "takendown": false, diff --git a/packages/ozone/tests/__snapshots__/moderation.test.ts.snap b/packages/ozone/tests/__snapshots__/moderation.test.ts.snap index 6ec7fd78c10..b8a33e00224 100644 --- a/packages/ozone/tests/__snapshots__/moderation.test.ts.snap +++ b/packages/ozone/tests/__snapshots__/moderation.test.ts.snap @@ -4,7 +4,7 @@ exports[`moderation reporting creates reports of a DM chat. 1`] = ` Array [ Object { "createdAt": "1970-01-01T00:00:00.000Z", - "id": 12, + "id": 14, "reasonType": "com.atproto.moderation.defs#reasonSpam", "reportedBy": "user(0)", "subject": Object { @@ -16,7 +16,7 @@ Array [ }, Object { "createdAt": "1970-01-01T00:00:00.000Z", - "id": 14, + "id": 16, "reason": "defamation", "reasonType": "com.atproto.moderation.defs#reasonOther", "reportedBy": "user(1)", @@ -34,7 +34,7 @@ exports[`moderation reporting creates reports of a record. 1`] = ` Array [ Object { "createdAt": "1970-01-01T00:00:00.000Z", - "id": 5, + "id": 7, "reasonType": "com.atproto.moderation.defs#reasonSpam", "reportedBy": "user(0)", "subject": Object { @@ -45,7 +45,7 @@ Array [ }, Object { "createdAt": "1970-01-01T00:00:00.000Z", - "id": 7, + "id": 9, "reason": "defamation", "reasonType": "com.atproto.moderation.defs#reasonOther", "reportedBy": "user(1)", diff --git a/packages/ozone/tests/content-tagger.test.ts b/packages/ozone/tests/content-tagger.test.ts index 1bb21e2f05d..9b5d375afd1 100644 --- a/packages/ozone/tests/content-tagger.test.ts +++ b/packages/ozone/tests/content-tagger.test.ts @@ -5,6 +5,7 @@ import { basicSeed, } from '@atproto/dev-env' import { REASONSPAM } from '../src/lexicon/types/com/atproto/moderation/defs' +import { REASONMISLEADING } from '../dist/lexicon/types/com/atproto/moderation/defs' describe('moderation subject content tagging', () => { let network: TestNetwork @@ -138,4 +139,32 @@ describe('moderation subject content tagging', () => { expect(imagePostStatus.tags).toContain('embed:image') }) }) + + describe('report tagger', () => { + it('Adds report reason tag', async () => { + await Promise.all([ + sc.createReport({ + reasonType: REASONSPAM, + subject: { + $type: 'com.atproto.admin.defs#repoRef', + did: sc.dids.carol, + }, + reportedBy: sc.dids.alice, + }), + sc.createReport({ + reasonType: REASONMISLEADING, + subject: { + $type: 'com.atproto.admin.defs#repoRef', + did: sc.dids.carol, + }, + reportedBy: sc.dids.alice, + }), + ]) + + const accountStatus = await getStatus(sc.dids.carol) + + expect(accountStatus.tags).toContain('report:spam') + expect(accountStatus.tags).toContain('report:misleading') + }) + }) }) diff --git a/packages/ozone/tests/report-muting.test.ts b/packages/ozone/tests/report-muting.test.ts index 5a2189ba2df..1cf195ce101 100644 --- a/packages/ozone/tests/report-muting.test.ts +++ b/packages/ozone/tests/report-muting.test.ts @@ -69,7 +69,7 @@ describe('report-muting', () => { }) // Verify that a subject status was not created for bob's post since the reporter was muted - await assertSubjectStatus(bobsPostSubject.uri, undefined) + await assertSubjectStatus(bobsPostSubject.uri, REVIEWNONE) // Verify, however, that the event was logged await modClient.queryEvents({ subject: bobsPostSubject.uri, diff --git a/packages/pds/tests/proxied/__snapshots__/admin.test.ts.snap b/packages/pds/tests/proxied/__snapshots__/admin.test.ts.snap index 55dd7be8a98..45f8c6daa02 100644 --- a/packages/pds/tests/proxied/__snapshots__/admin.test.ts.snap +++ b/packages/pds/tests/proxied/__snapshots__/admin.test.ts.snap @@ -35,7 +35,7 @@ Array [ "event": Object { "$type": "tools.ozone.moderation.defs#modEventAcknowledge", }, - "id": 6, + "id": 7, "subject": Object { "$type": "com.atproto.admin.defs#repoRef", "did": "user(0)", @@ -46,6 +46,25 @@ Array [ Object { "createdAt": "1970-01-01T00:00:00.000Z", "createdBy": "user(2)", + "creatorHandle": "mod-authority.test", + "event": Object { + "$type": "tools.ozone.moderation.defs#modEventTag", + "add": Array [ + "report:other", + ], + "remove": Array [], + }, + "id": 4, + "subject": Object { + "$type": "com.atproto.admin.defs#repoRef", + "did": "user(0)", + }, + "subjectBlobCids": Array [], + "subjectHandle": "bob.test", + }, + Object { + "createdAt": "1970-01-01T00:00:00.000Z", + "createdBy": "user(3)", "creatorHandle": "carol.test", "event": Object { "$type": "tools.ozone.moderation.defs#modEventReport", @@ -63,11 +82,12 @@ Array [ }, Object { "createdAt": "1970-01-01T00:00:00.000Z", - "createdBy": "user(3)", + "createdBy": "user(2)", "creatorHandle": "mod-authority.test", "event": Object { "$type": "tools.ozone.moderation.defs#modEventTag", "add": Array [ + "report:spam", "lang:en", "lang:i", ], @@ -108,6 +128,7 @@ Object { "event": Object { "$type": "tools.ozone.moderation.defs#modEventTag", "add": Array [ + "report:spam", "lang:en", "lang:i", ], @@ -138,8 +159,10 @@ Object { "subjectBlobCids": Array [], "subjectRepoHandle": "bob.test", "tags": Array [ + "report:spam", "lang:en", "lang:i", + "report:other", ], "takendown": false, "updatedAt": "1970-01-01T00:00:00.000Z", @@ -180,7 +203,7 @@ Array [ ], "remove": Array [], }, - "id": 5, + "id": 6, "subject": Object { "$type": "com.atproto.repo.strongRef", "cid": "cids(0)", @@ -196,7 +219,7 @@ Array [ "event": Object { "$type": "tools.ozone.moderation.defs#modEventAcknowledge", }, - "id": 4, + "id": 5, "subject": Object { "$type": "com.atproto.repo.strongRef", "cid": "cids(0)", @@ -222,7 +245,7 @@ Object { "$type": "tools.ozone.moderation.defs#recordHosting", "status": "unknown", }, - "id": 4, + "id": 5, "lastReviewedAt": "1970-01-01T00:00:00.000Z", "lastReviewedBy": "user(1)", "reviewState": "tools.ozone.moderation.defs#reviewClosed", @@ -295,8 +318,10 @@ Object { "subjectBlobCids": Array [], "subjectRepoHandle": "bob.test", "tags": Array [ + "report:spam", "lang:en", "lang:i", + "report:other", ], "takendown": false, "updatedAt": "1970-01-01T00:00:00.000Z", @@ -401,7 +426,7 @@ Object { "event": Object { "$type": "tools.ozone.moderation.defs#modEventAcknowledge", }, - "id": 4, + "id": 5, "subject": Object { "$type": "com.atproto.repo.strongRef", "cid": "cids(0)", @@ -418,7 +443,7 @@ Object { "event": Object { "$type": "tools.ozone.moderation.defs#modEventAcknowledge", }, - "id": 6, + "id": 7, "subject": Object { "$type": "com.atproto.admin.defs#repoRef", "did": "user(0)",