Skip to content

Commit

Permalink
✨ Optionally ack all open subjects from the author with takedown even…
Browse files Browse the repository at this point in the history
…t and get all mod subjects of a user (bluesky-social#2793)

* ✨ Add acknowledgeAllSubjectsOfAccount flag with takedown event

* 📝 better documentation of forAccount and subject params

* 🧹 Cleanup tests

* ✨ Change wording according to review

* ✅ Refactor tests

* ♻️ Rename acknowledge flag
  • Loading branch information
foysalit authored Sep 11, 2024
1 parent 98711a1 commit 62a8225
Show file tree
Hide file tree
Showing 17 changed files with 291 additions and 11 deletions.
4 changes: 4 additions & 0 deletions lexicons/tools/ozone/moderation/defs.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@
"durationInHours": {
"type": "integer",
"description": "Indicates how long the takedown should be in effect before automatically expiring."
},
"acknowledgeAccountSubjects": {
"type": "boolean",
"description": "If true, all other reports on content authored by this account will be resolved (acknowledged)."
}
}
},
Expand Down
31 changes: 25 additions & 6 deletions lexicons/tools/ozone/moderation/queryStatuses.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@
"parameters": {
"type": "params",
"properties": {
"subject": { "type": "string", "format": "uri" },
"includeAllUserRecords": {
"type": "boolean",
"description": "All subjects belonging to the account specified in the 'subject' param will be returned."
},
"subject": {
"type": "string",
"format": "uri",
"description": "The subject to get the status for."
},
"comment": {
"type": "string",
"description": "Search subjects by keyword from comments"
Expand Down Expand Up @@ -47,7 +55,10 @@
},
"ignoreSubjects": {
"type": "array",
"items": { "type": "string", "format": "uri" }
"items": {
"type": "string",
"format": "uri"
}
},
"lastReviewedBy": {
"type": "string",
Expand Down Expand Up @@ -80,13 +91,19 @@
},
"tags": {
"type": "array",
"items": { "type": "string" }
"items": {
"type": "string"
}
},
"excludeTags": {
"type": "array",
"items": { "type": "string" }
"items": {
"type": "string"
}
},
"cursor": { "type": "string" }
"cursor": {
"type": "string"
}
}
},
"output": {
Expand All @@ -95,7 +112,9 @@
"type": "object",
"required": ["subjectStatuses"],
"properties": {
"cursor": { "type": "string" },
"cursor": {
"type": "string"
},
"subjectStatuses": {
"type": "array",
"items": {
Expand Down
11 changes: 11 additions & 0 deletions packages/api/src/client/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10920,6 +10920,11 @@ export const schemaDict = {
description:
'Indicates how long the takedown should be in effect before automatically expiring.',
},
acknowledgeAccountSubjects: {
type: 'boolean',
description:
'If true, all other reports on content authored by this account will be resolved (acknowledged).',
},
},
},
modEventReverseTakedown: {
Expand Down Expand Up @@ -11731,9 +11736,15 @@ export const schemaDict = {
parameters: {
type: 'params',
properties: {
includeAllUserRecords: {
type: 'boolean',
description:
"All subjects belonging to the account specified in the 'subject' param will be returned.",
},
subject: {
type: 'string',
format: 'uri',
description: 'The subject to get the status for.',
},
comment: {
type: 'string',
Expand Down
2 changes: 2 additions & 0 deletions packages/api/src/client/types/tools/ozone/moderation/defs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ export interface ModEventTakedown {
comment?: string
/** Indicates how long the takedown should be in effect before automatically expiring. */
durationInHours?: number
/** If true, all other reports on content authored by this account will be resolved (acknowledged). */
acknowledgeAccountSubjects?: boolean
[k: string]: unknown
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import { CID } from 'multiformats/cid'
import * as ToolsOzoneModerationDefs from './defs'

export interface QueryParams {
/** All subjects belonging to the account specified in the 'subject' param will be returned. */
includeAllUserRecords?: boolean
/** The subject to get the status for. */
subject?: string
/** Search subjects by keyword from comments */
comment?: string
Expand Down
4 changes: 3 additions & 1 deletion packages/dev-env/src/moderator-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,17 @@ export class ModeratorClient {
subject: TakeActionInput['subject']
subjectBlobCids?: TakeActionInput['subjectBlobCids']
durationInHours?: number
acknowledgeAccountSubjects?: boolean
reason?: string
},
role?: ModLevel,
) {
const { durationInHours, ...rest } = opts
const { durationInHours, acknowledgeAccountSubjects, ...rest } = opts
return this.emitEvent(
{
event: {
$type: 'tools.ozone.moderation.defs#modEventTakedown',
acknowledgeAccountSubjects,
durationInHours,
},
...rest,
Expand Down
4 changes: 4 additions & 0 deletions packages/ozone/src/api/moderation/emitEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ const handleModerationEvent = async ({
}
}

if (isTakedownEvent && result.event.meta?.acknowledgeAccountSubjects) {
await moderationTxn.resolveSubjectsForAccount(subject.did, createdBy)
}

if (isLabelEvent) {
await moderationTxn.formatAndCreateLabels(
result.event.subjectUri ?? result.event.subjectDid,
Expand Down
2 changes: 2 additions & 0 deletions packages/ozone/src/api/moderation/queryStatuses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default function (server: Server, ctx: AppContext) {
auth: ctx.authVerifier.modOrAdminToken,
handler: async ({ params }) => {
const {
includeAllUserRecords,
subject,
takendown,
appealed,
Expand All @@ -30,6 +31,7 @@ export default function (server: Server, ctx: AppContext) {
const modService = ctx.modService(db)
const results = await modService.getSubjectStatuses({
reviewState: getReviewState(reviewState),
includeAllUserRecords,
subject,
takendown,
appealed,
Expand Down
11 changes: 11 additions & 0 deletions packages/ozone/src/lexicon/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10920,6 +10920,11 @@ export const schemaDict = {
description:
'Indicates how long the takedown should be in effect before automatically expiring.',
},
acknowledgeAccountSubjects: {
type: 'boolean',
description:
'If true, all other reports on content authored by this account will be resolved (acknowledged).',
},
},
},
modEventReverseTakedown: {
Expand Down Expand Up @@ -11731,9 +11736,15 @@ export const schemaDict = {
parameters: {
type: 'params',
properties: {
includeAllUserRecords: {
type: 'boolean',
description:
"All subjects belonging to the account specified in the 'subject' param will be returned.",
},
subject: {
type: 'string',
format: 'uri',
description: 'The subject to get the status for.',
},
comment: {
type: 'string',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ export interface ModEventTakedown {
comment?: string
/** Indicates how long the takedown should be in effect before automatically expiring. */
durationInHours?: number
/** If true, all other reports on content authored by this account will be resolved (acknowledged). */
acknowledgeAccountSubjects?: boolean
[k: string]: unknown
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
import * as ToolsOzoneModerationDefs from './defs'

export interface QueryParams {
/** All subjects belonging to the account specified in the 'subject' param will be returned. */
includeAllUserRecords?: boolean
/** The subject to get the status for. */
subject?: string
/** Search subjects by keyword from comments */
comment?: string
Expand Down
68 changes: 64 additions & 4 deletions packages/ozone/src/mod-service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Insertable, sql } from 'kysely'
import { CID } from 'multiformats/cid'
import { AtUri, INVALID_HANDLE } from '@atproto/syntax'
import { InvalidRequestError } from '@atproto/xrpc-server'
import { addHoursToDate } from '@atproto/common'
import { addHoursToDate, chunkArray } from '@atproto/common'
import { Keypair } from '@atproto/crypto'
import { IdResolver } from '@atproto/identity'
import { AtpAgent } from '@atproto/api'
Expand All @@ -18,6 +18,8 @@ import {
isModEventTakedown,
isModEventEmail,
isModEventTag,
REVIEWESCALATED,
REVIEWOPEN,
} from '../lexicon/types/tools/ozone/moderation/defs'
import { RepoRef, RepoBlobRef } from '../lexicon/types/com/atproto/admin/defs'
import {
Expand Down Expand Up @@ -279,6 +281,52 @@ export class ModerationService {
return await builder.execute()
}

async resolveSubjectsForAccount(did: string, createdBy: string) {
const subjectsToBeResolved = await this.db.db
.selectFrom('moderation_subject_status')
.where('did', '=', did)
.where('recordPath', '!=', '')
.where('reviewState', 'in', [REVIEWESCALATED, REVIEWOPEN])
.selectAll()
.execute()

if (subjectsToBeResolved.length === 0) {
return
}

// Process subjects in chunks of 100 since each of these will trigger multiple db queries
for (const subjects of chunkArray(subjectsToBeResolved, 100)) {
await Promise.all(
subjects.map(async (subject) => {
const eventData = {
createdBy,
subject: subjectFromStatusRow(subject),
}
// For consistency's sake, when acknowledging appealed subjects, we should first resolve the appeal
if (subject.appealed) {
await this.logEvent({
event: {
$type: 'tools.ozone.moderation.defs#modEventResolveAppeal',
comment:
'[AUTO_RESOLVE_FOR_TAKENDOWN_ACCOUNT]: Automatically resolving all appealed content for a takendown account',
},
...eventData,
})
}

await this.logEvent({
event: {
$type: 'tools.ozone.moderation.defs#modEventAcknowledge',
comment:
'[AUTO_RESOLVE_FOR_TAKENDOWN_ACCOUNT]: Automatically resolving all reported content for a takendown account',
},
...eventData,
})
}),
)
}
}

async logEvent(info: {
event: ModEventType
subject: ModSubject
Expand Down Expand Up @@ -320,6 +368,10 @@ export class ModerationService {
}
}

if (isModEventTakedown(event) && event.acknowledgeAccountSubjects) {
meta.acknowledgeAccountSubjects = true
}

// Keep trace of reports that came in while the reporter was in muted stated
if (isModEventReport(event)) {
const isReportingMuted = await this.isReportingMutedForSubject(createdBy)
Expand Down Expand Up @@ -677,6 +729,7 @@ export class ModerationService {
}

async getSubjectStatuses({
includeAllUserRecords,
cursor,
limit = 50,
takendown,
Expand All @@ -696,6 +749,7 @@ export class ModerationService {
tags,
excludeTags,
}: {
includeAllUserRecords?: boolean
cursor?: string
limit?: number
takendown?: boolean
Expand All @@ -720,13 +774,19 @@ export class ModerationService {

if (subject) {
const subjectInfo = getStatusIdentifierFromSubject(subject)
builder = builder
.where('moderation_subject_status.did', '=', subjectInfo.did)
.where((qb) =>
builder = builder.where(
'moderation_subject_status.did',
'=',
subjectInfo.did,
)

if (!includeAllUserRecords) {
builder = builder.where((qb) =>
subjectInfo.recordPath
? qb.where('recordPath', '=', subjectInfo.recordPath)
: qb.where('recordPath', '=', ''),
)
}
}

if (ignoreSubjects?.length) {
Expand Down
10 changes: 10 additions & 0 deletions packages/ozone/src/mod-service/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,16 @@ export class ModerationViews {
}
}

if (
event.action === 'tools.ozone.moderation.defs#modEventTakedown' &&
event.meta?.acknowledgeAccountSubjects
) {
eventView.event = {
...eventView.event,
acknowledgeAccountSubjects: event.meta?.acknowledgeAccountSubjects,
}
}

if (event.action === 'tools.ozone.moderation.defs#modEventLabel') {
eventView.event = {
...eventView.event,
Expand Down
Loading

0 comments on commit 62a8225

Please sign in to comment.