@atproto/ozone 0.1.176 → 0.2.0
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 +13 -0
- package/dist/api/chat/getConvo.d.ts +4 -0
- package/dist/api/chat/getConvo.d.ts.map +1 -0
- package/dist/api/chat/getConvo.js +18 -0
- package/dist/api/chat/getConvo.js.map +1 -0
- package/dist/api/chat/getConvoMembers.d.ts +4 -0
- package/dist/api/chat/getConvoMembers.d.ts.map +1 -0
- package/dist/api/chat/getConvoMembers.js +18 -0
- package/dist/api/chat/getConvoMembers.js.map +1 -0
- package/dist/api/chat/index.d.ts.map +1 -1
- package/dist/api/chat/index.js +4 -0
- package/dist/api/chat/index.js.map +1 -1
- package/dist/daemon/event-reverser.d.ts.map +1 -1
- package/dist/daemon/event-reverser.js +1 -0
- package/dist/daemon/event-reverser.js.map +1 -1
- package/dist/db/migrations/20260513T202941104Z-add-subject-convo-id.d.ts +4 -0
- package/dist/db/migrations/20260513T202941104Z-add-subject-convo-id.d.ts.map +1 -0
- package/dist/db/migrations/20260513T202941104Z-add-subject-convo-id.js +107 -0
- package/dist/db/migrations/20260513T202941104Z-add-subject-convo-id.js.map +1 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/db/migrations/index.d.ts.map +1 -1
- package/dist/db/migrations/index.js +1 -0
- package/dist/db/migrations/index.js.map +1 -1
- package/dist/db/schema/expiring_tag.d.ts +1 -0
- package/dist/db/schema/expiring_tag.d.ts.map +1 -1
- package/dist/db/schema/expiring_tag.js.map +1 -1
- package/dist/db/schema/moderation_event.d.ts +2 -1
- package/dist/db/schema/moderation_event.d.ts.map +1 -1
- package/dist/db/schema/moderation_event.js.map +1 -1
- package/dist/db/schema/moderation_subject_status.d.ts +1 -0
- package/dist/db/schema/moderation_subject_status.d.ts.map +1 -1
- package/dist/db/schema/moderation_subject_status.js.map +1 -1
- package/dist/db/schema/report.d.ts +1 -0
- package/dist/db/schema/report.d.ts.map +1 -1
- package/dist/db/schema/report.js.map +1 -1
- package/dist/lexicon/index.d.ts +19 -2
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +32 -2
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +1364 -132
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +721 -44
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts +4 -0
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.js.map +1 -1
- package/dist/lexicon/types/app/bsky/embed/external.d.ts +48 -2
- package/dist/lexicon/types/app/bsky/embed/external.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/embed/external.js +21 -0
- package/dist/lexicon/types/app/bsky/embed/external.js.map +1 -1
- package/dist/lexicon/types/app/bsky/embed/getEmbedExternalView.d.ts +31 -0
- package/dist/lexicon/types/app/bsky/embed/getEmbedExternalView.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/embed/getEmbedExternalView.js +5 -0
- package/dist/lexicon/types/app/bsky/embed/getEmbedExternalView.js.map +1 -0
- package/dist/lexicon/types/chat/bsky/actor/getStatus.d.ts +24 -0
- package/dist/lexicon/types/chat/bsky/actor/getStatus.d.ts.map +1 -0
- package/dist/lexicon/types/chat/bsky/{group/getJoinLinkPreview.js → actor/getStatus.js} +2 -2
- package/dist/lexicon/types/chat/bsky/actor/getStatus.js.map +1 -0
- package/dist/lexicon/types/chat/bsky/convo/defs.d.ts +39 -7
- package/dist/lexicon/types/chat/bsky/convo/defs.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/defs.js +21 -0
- package/dist/lexicon/types/chat/bsky/convo/defs.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/getConvoForMembers.d.ts +1 -1
- package/dist/lexicon/types/chat/bsky/convo/getConvoForMembers.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/getConvoForMembers.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/getLog.d.ts +1 -1
- package/dist/lexicon/types/chat/bsky/convo/getLog.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/getLog.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/listConvoRequests.d.ts +1 -1
- package/dist/lexicon/types/chat/bsky/convo/listConvoRequests.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/listConvoRequests.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/embed/joinLink.d.ts +19 -0
- package/dist/lexicon/types/chat/bsky/embed/joinLink.d.ts.map +1 -0
- package/dist/lexicon/types/chat/bsky/embed/joinLink.js +19 -0
- package/dist/lexicon/types/chat/bsky/embed/joinLink.js.map +1 -0
- package/dist/lexicon/types/chat/bsky/group/addMembers.d.ts +1 -1
- package/dist/lexicon/types/chat/bsky/group/addMembers.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/group/addMembers.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/group/createGroup.d.ts +1 -1
- package/dist/lexicon/types/chat/bsky/group/createGroup.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/group/createGroup.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/group/defs.d.ts +23 -0
- package/dist/lexicon/types/chat/bsky/group/defs.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/group/defs.js +14 -0
- package/dist/lexicon/types/chat/bsky/group/defs.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/group/{getJoinLinkPreview.d.ts → getJoinLinkPreviews.d.ts} +3 -4
- package/dist/lexicon/types/chat/bsky/group/getJoinLinkPreviews.d.ts.map +1 -0
- package/dist/lexicon/types/chat/bsky/group/getJoinLinkPreviews.js +5 -0
- package/dist/lexicon/types/chat/bsky/group/getJoinLinkPreviews.js.map +1 -0
- package/dist/lexicon/types/chat/bsky/group/updateJoinRequestsRead.d.ts +24 -0
- package/dist/lexicon/types/chat/bsky/group/updateJoinRequestsRead.d.ts.map +1 -0
- package/dist/lexicon/types/chat/bsky/group/updateJoinRequestsRead.js +5 -0
- package/dist/lexicon/types/chat/bsky/group/updateJoinRequestsRead.js.map +1 -0
- package/dist/lexicon/types/chat/bsky/group/withdrawJoinRequest.d.ts +24 -0
- package/dist/lexicon/types/chat/bsky/group/withdrawJoinRequest.d.ts.map +1 -0
- package/dist/lexicon/types/chat/bsky/group/withdrawJoinRequest.js +5 -0
- package/dist/lexicon/types/chat/bsky/group/withdrawJoinRequest.js.map +1 -0
- package/dist/lexicon/types/chat/bsky/moderation/defs.d.ts +42 -0
- package/dist/lexicon/types/chat/bsky/moderation/defs.d.ts.map +1 -0
- package/dist/lexicon/types/chat/bsky/moderation/defs.js +26 -0
- package/dist/lexicon/types/chat/bsky/moderation/defs.js.map +1 -0
- package/dist/lexicon/types/chat/bsky/moderation/getConvo.d.ts +23 -0
- package/dist/lexicon/types/chat/bsky/moderation/getConvo.d.ts.map +1 -0
- package/dist/lexicon/types/chat/bsky/moderation/getConvo.js +5 -0
- package/dist/lexicon/types/chat/bsky/moderation/getConvo.js.map +1 -0
- package/dist/lexicon/types/chat/bsky/moderation/getConvoMembers.d.ts +26 -0
- package/dist/lexicon/types/chat/bsky/moderation/getConvoMembers.d.ts.map +1 -0
- package/dist/lexicon/types/chat/bsky/moderation/getConvoMembers.js +5 -0
- package/dist/lexicon/types/chat/bsky/moderation/getConvoMembers.js.map +1 -0
- package/dist/lexicon/types/chat/bsky/moderation/subscribeModEvents.d.ts +13 -1
- package/dist/lexicon/types/chat/bsky/moderation/subscribeModEvents.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/moderation/subscribeModEvents.js +7 -0
- package/dist/lexicon/types/chat/bsky/moderation/subscribeModEvents.js.map +1 -1
- package/dist/lexicon/types/com/atproto/server/getServiceAuth.d.ts +1 -1
- package/dist/lexicon/types/com/atproto/server/getServiceAuth.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/server/getServiceAuth.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +10 -3
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.js +7 -0
- package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/queryEvents.d.ts +2 -2
- package/dist/lexicon/types/tools/ozone/moderation/queryEvents.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/queryEvents.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts +2 -2
- package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.js.map +1 -1
- package/dist/mod-service/expiring-tags.d.ts +3 -0
- package/dist/mod-service/expiring-tags.d.ts.map +1 -1
- package/dist/mod-service/expiring-tags.js +5 -2
- package/dist/mod-service/expiring-tags.js.map +1 -1
- 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 +48 -10
- package/dist/mod-service/index.js.map +1 -1
- package/dist/mod-service/status.d.ts +7 -1
- package/dist/mod-service/status.d.ts.map +1 -1
- package/dist/mod-service/status.js +18 -3
- package/dist/mod-service/status.js.map +1 -1
- package/dist/mod-service/subject.d.ts +38 -2
- package/dist/mod-service/subject.d.ts.map +1 -1
- package/dist/mod-service/subject.js +67 -0
- package/dist/mod-service/subject.js.map +1 -1
- package/dist/mod-service/views.d.ts +2 -1
- package/dist/mod-service/views.d.ts.map +1 -1
- package/dist/mod-service/views.js +24 -22
- package/dist/mod-service/views.js.map +1 -1
- package/dist/queue/service.d.ts.map +1 -1
- package/dist/queue/service.js +7 -3
- package/dist/queue/service.js.map +1 -1
- package/dist/safelink/service.d.ts +1 -1
- package/package.json +5 -5
- package/src/api/chat/getConvo.ts +23 -0
- package/src/api/chat/getConvoMembers.ts +23 -0
- package/src/api/chat/index.ts +4 -0
- package/src/daemon/event-reverser.ts +1 -0
- package/src/db/migrations/20260513T202941104Z-add-subject-convo-id.ts +114 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/db/schema/expiring_tag.ts +1 -0
- package/src/db/schema/moderation_event.ts +2 -0
- package/src/db/schema/moderation_subject_status.ts +4 -0
- package/src/db/schema/report.ts +2 -1
- package/src/mod-service/expiring-tags.ts +8 -2
- package/src/mod-service/index.ts +52 -16
- package/src/mod-service/status.ts +24 -2
- package/src/mod-service/subject.ts +79 -1
- package/src/mod-service/views.ts +28 -23
- package/src/queue/service.ts +8 -4
- package/tests/__snapshots__/verification.test.ts.snap +2 -0
- package/tests/expiring-tags.test.ts +1 -0
- package/tests/moderation-events.test.ts +108 -1
- package/tests/moderation-status-tags.test.ts +23 -0
- package/tests/moderation-statuses.test.ts +82 -0
- package/tests/moderation.test.ts +73 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/lexicon/types/chat/bsky/group/getJoinLinkPreview.d.ts.map +0 -1
- package/dist/lexicon/types/chat/bsky/group/getJoinLinkPreview.js.map +0 -1
package/src/mod-service/views.ts
CHANGED
|
@@ -51,7 +51,11 @@ import { Un$Typed, asPredicate } from '../lexicon/util.js'
|
|
|
51
51
|
import { dbLogger, httpLogger } from '../logger.js'
|
|
52
52
|
import { ParsedLabelers } from '../util.js'
|
|
53
53
|
import { moderationSubjectStatusQueryBuilder } from './status.js'
|
|
54
|
-
import {
|
|
54
|
+
import {
|
|
55
|
+
ModSubject,
|
|
56
|
+
subjectFromEventRow,
|
|
57
|
+
subjectFromStatusRow,
|
|
58
|
+
} from './subject.js'
|
|
55
59
|
import {
|
|
56
60
|
ModerationEventRowWithHandle,
|
|
57
61
|
ModerationSubjectStatusRowWithHandle,
|
|
@@ -297,14 +301,8 @@ export class ModerationViews {
|
|
|
297
301
|
async eventDetail(
|
|
298
302
|
result: ModerationEventRowWithHandle,
|
|
299
303
|
): Promise<ModEventViewDetail> {
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
? result.subjectDid
|
|
303
|
-
: result.subjectUri
|
|
304
|
-
if (!subjectId) {
|
|
305
|
-
throw new Error(`Bad subject: ${result.id}`)
|
|
306
|
-
}
|
|
307
|
-
const subject = await this.subject(subjectId)
|
|
304
|
+
const modSubject = subjectFromEventRow(result)
|
|
305
|
+
const subject = await this.subject(modSubject)
|
|
308
306
|
const eventView = this.formatEvent(result)
|
|
309
307
|
const allBlobs = 'value' in subject ? findBlobRefs(subject.value) : []
|
|
310
308
|
const subjectBlobs = await this.blob(
|
|
@@ -555,10 +553,16 @@ export class ModerationViews {
|
|
|
555
553
|
}
|
|
556
554
|
// Partial view for subjects
|
|
557
555
|
|
|
558
|
-
async subject(subject:
|
|
559
|
-
if (subject.
|
|
560
|
-
|
|
561
|
-
|
|
556
|
+
async subject(subject: ModSubject): Promise<SubjectView> {
|
|
557
|
+
if (subject.isConvo()) {
|
|
558
|
+
return {
|
|
559
|
+
$type: 'tools.ozone.moderation.defs#convoView',
|
|
560
|
+
did: subject.did,
|
|
561
|
+
convoId: subject.convoId,
|
|
562
|
+
}
|
|
563
|
+
} else if (subject.isRepo()) {
|
|
564
|
+
const repos = await this.repos([subject.did])
|
|
565
|
+
const repo = repos.get(subject.did)
|
|
562
566
|
if (repo) {
|
|
563
567
|
return {
|
|
564
568
|
...repo,
|
|
@@ -567,24 +571,24 @@ export class ModerationViews {
|
|
|
567
571
|
} else {
|
|
568
572
|
return {
|
|
569
573
|
$type: 'tools.ozone.moderation.defs#repoViewNotFound',
|
|
570
|
-
did: subject,
|
|
574
|
+
did: subject.did,
|
|
571
575
|
}
|
|
572
576
|
}
|
|
573
|
-
} else {
|
|
574
|
-
const
|
|
575
|
-
const
|
|
577
|
+
} else if (subject.isRecord()) {
|
|
578
|
+
const uri = subject.uri
|
|
579
|
+
const records = await this.records([{ uri }])
|
|
580
|
+
const record = records.get(uri)
|
|
576
581
|
if (record) {
|
|
577
582
|
return {
|
|
578
583
|
...record,
|
|
579
584
|
$type: 'tools.ozone.moderation.defs#recordView',
|
|
580
585
|
}
|
|
581
|
-
} else {
|
|
582
|
-
return {
|
|
583
|
-
$type: 'tools.ozone.moderation.defs#recordViewNotFound',
|
|
584
|
-
uri: subject,
|
|
585
|
-
}
|
|
586
586
|
}
|
|
587
587
|
}
|
|
588
|
+
return {
|
|
589
|
+
$type: 'tools.ozone.moderation.defs#repoViewNotFound',
|
|
590
|
+
did: subject.did,
|
|
591
|
+
}
|
|
588
592
|
}
|
|
589
593
|
|
|
590
594
|
// Partial view for blobs
|
|
@@ -691,7 +695,8 @@ export class ModerationViews {
|
|
|
691
695
|
'moderation_subject_status.recordPath',
|
|
692
696
|
'=',
|
|
693
697
|
sub.recordPath ?? '',
|
|
694
|
-
)
|
|
698
|
+
)
|
|
699
|
+
.where('moderation_subject_status.convoId', '=', ''),
|
|
695
700
|
)
|
|
696
701
|
}
|
|
697
702
|
return qb
|
package/src/queue/service.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { viewQueueStats } from '../report/views.js'
|
|
|
13
13
|
const MOD_EVENT_REPORT_ACTION = 'tools.ozone.moderation.defs#modEventReport'
|
|
14
14
|
const REASON_OTHER = 'com.atproto.moderation.defs#reasonOther'
|
|
15
15
|
|
|
16
|
-
type SubjectType = 'account' | 'record' | 'message'
|
|
16
|
+
type SubjectType = 'account' | 'record' | 'message' | 'conversation'
|
|
17
17
|
|
|
18
18
|
type ResolvedAssignment = {
|
|
19
19
|
queueId: number
|
|
@@ -478,6 +478,7 @@ export class QueueService {
|
|
|
478
478
|
'subjectDid',
|
|
479
479
|
'subjectUri',
|
|
480
480
|
'subjectMessageId',
|
|
481
|
+
'subjectConvoId',
|
|
481
482
|
'meta',
|
|
482
483
|
'createdAt',
|
|
483
484
|
])
|
|
@@ -503,9 +504,11 @@ export class QueueService {
|
|
|
503
504
|
const rows = events.map((event) => {
|
|
504
505
|
const subjectType: SubjectType = event.subjectMessageId
|
|
505
506
|
? 'message'
|
|
506
|
-
: event.
|
|
507
|
-
? '
|
|
508
|
-
:
|
|
507
|
+
: event.subjectConvoId
|
|
508
|
+
? 'conversation'
|
|
509
|
+
: event.subjectUri
|
|
510
|
+
? 'record'
|
|
511
|
+
: 'account'
|
|
509
512
|
|
|
510
513
|
let collection: string | null = null
|
|
511
514
|
let recordPath = ''
|
|
@@ -545,6 +548,7 @@ export class QueueService {
|
|
|
545
548
|
did: event.subjectDid,
|
|
546
549
|
recordPath,
|
|
547
550
|
subjectMessageId: event.subjectMessageId,
|
|
551
|
+
subjectConvoId: event.subjectConvoId,
|
|
548
552
|
createdAt: now,
|
|
549
553
|
updatedAt: now,
|
|
550
554
|
}
|
|
@@ -122,6 +122,8 @@ exports[`verification list returns paginated list of verifications 1`] = `
|
|
|
122
122
|
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
123
123
|
"isValid": true,
|
|
124
124
|
"issuer": "user(0)",
|
|
125
|
+
"issuerDisplayName": "ali",
|
|
126
|
+
"issuerHandle": "alice.test",
|
|
125
127
|
"uri": "record(0)",
|
|
126
128
|
},
|
|
127
129
|
],
|
|
@@ -207,8 +207,25 @@ describe('moderation-events', () => {
|
|
|
207
207
|
}),
|
|
208
208
|
])
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
// Verify all spam events have the correct report type
|
|
211
211
|
expect(spamEvents.events.length).toEqual(6)
|
|
212
|
+
spamEvents.events.forEach((event) => {
|
|
213
|
+
expect(event.event.$type).toEqual(
|
|
214
|
+
'tools.ozone.moderation.defs#modEventReport',
|
|
215
|
+
)
|
|
216
|
+
expect((event.event as any).reportType).toEqual(REASONSPAM)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
// Verify all misleading events have one of the correct report types
|
|
220
|
+
expect(misleadingEvents.events.length).toEqual(2)
|
|
221
|
+
misleadingEvents.events.forEach((event) => {
|
|
222
|
+
expect(event.event.$type).toEqual(
|
|
223
|
+
'tools.ozone.moderation.defs#modEventReport',
|
|
224
|
+
)
|
|
225
|
+
expect([REASONMISLEADING, REASONAPPEAL]).toContain(
|
|
226
|
+
(event.event as any).reportType,
|
|
227
|
+
)
|
|
228
|
+
})
|
|
212
229
|
})
|
|
213
230
|
|
|
214
231
|
it('returns events matching keyword in comment', async () => {
|
|
@@ -501,6 +518,96 @@ describe('moderation-events', () => {
|
|
|
501
518
|
expect(event.createdBy).toEqual(network.ozone.moderatorAccnt.did)
|
|
502
519
|
})
|
|
503
520
|
})
|
|
521
|
+
|
|
522
|
+
it('queries events by conversation', async () => {
|
|
523
|
+
const convoId1 = 'conversation-123'
|
|
524
|
+
const convoId2 = 'conversation-456'
|
|
525
|
+
|
|
526
|
+
// create reports
|
|
527
|
+
await sc.createReport({
|
|
528
|
+
reasonType: REASONSPAM,
|
|
529
|
+
reason: 'spam in convo 1',
|
|
530
|
+
subject: {
|
|
531
|
+
$type: 'chat.bsky.convo.defs#convoRef',
|
|
532
|
+
did: sc.dids.carol,
|
|
533
|
+
convoId: convoId1,
|
|
534
|
+
},
|
|
535
|
+
reportedBy: sc.dids.alice,
|
|
536
|
+
})
|
|
537
|
+
await sc.createReport({
|
|
538
|
+
reasonType: REASONMISLEADING,
|
|
539
|
+
reason: 'misleading in convo 1',
|
|
540
|
+
subject: {
|
|
541
|
+
$type: 'chat.bsky.convo.defs#convoRef',
|
|
542
|
+
did: sc.dids.carol,
|
|
543
|
+
convoId: convoId1,
|
|
544
|
+
},
|
|
545
|
+
reportedBy: sc.dids.bob,
|
|
546
|
+
})
|
|
547
|
+
await sc.createReport({
|
|
548
|
+
reasonType: REASONSPAM,
|
|
549
|
+
reason: 'spam in convo 2',
|
|
550
|
+
subject: {
|
|
551
|
+
$type: 'chat.bsky.convo.defs#convoRef',
|
|
552
|
+
did: sc.dids.carol,
|
|
553
|
+
convoId: convoId2,
|
|
554
|
+
},
|
|
555
|
+
reportedBy: sc.dids.alice,
|
|
556
|
+
})
|
|
557
|
+
|
|
558
|
+
// Query events (filter by report type since auto-tagging creates extra events)
|
|
559
|
+
const convo1Events = await modClient.queryEvents({
|
|
560
|
+
subject: `at://${sc.dids.carol}/chat.bsky.convo/${convoId1}`,
|
|
561
|
+
includeAllUserRecords: false,
|
|
562
|
+
types: ['tools.ozone.moderation.defs#modEventReport'],
|
|
563
|
+
})
|
|
564
|
+
const convo2Events = await modClient.queryEvents({
|
|
565
|
+
subject: `at://${sc.dids.carol}/chat.bsky.convo/${convoId2}`,
|
|
566
|
+
includeAllUserRecords: false,
|
|
567
|
+
types: ['tools.ozone.moderation.defs#modEventReport'],
|
|
568
|
+
})
|
|
569
|
+
|
|
570
|
+
// Verify conversation 1 events are correct
|
|
571
|
+
expect(convo1Events.events.length).toBeGreaterThan(0)
|
|
572
|
+
convo1Events.events.forEach((e) => {
|
|
573
|
+
// All events should be conversation refs
|
|
574
|
+
expect(e.subject.$type).toEqual('chat.bsky.convo.defs#convoRef')
|
|
575
|
+
// All events should be for conversation 1
|
|
576
|
+
const subject = e.subject as any
|
|
577
|
+
expect(subject.convoId).toEqual(convoId1)
|
|
578
|
+
expect(subject.did).toEqual(sc.dids.carol)
|
|
579
|
+
// All events should be reports
|
|
580
|
+
expect(e.event.$type).toEqual(
|
|
581
|
+
'tools.ozone.moderation.defs#modEventReport',
|
|
582
|
+
)
|
|
583
|
+
})
|
|
584
|
+
// Verify we got both reports we created
|
|
585
|
+
const convo1Comments = convo1Events.events.map(
|
|
586
|
+
(e) => (e.event as any).comment,
|
|
587
|
+
)
|
|
588
|
+
expect(convo1Comments).toContain('spam in convo 1')
|
|
589
|
+
expect(convo1Comments).toContain('misleading in convo 1')
|
|
590
|
+
|
|
591
|
+
// Verify conversation 2 events are correct
|
|
592
|
+
expect(convo2Events.events.length).toBeGreaterThan(0)
|
|
593
|
+
convo2Events.events.forEach((e) => {
|
|
594
|
+
// All events should be conversation refs
|
|
595
|
+
expect(e.subject.$type).toEqual('chat.bsky.convo.defs#convoRef')
|
|
596
|
+
// All events should be for conversation 2
|
|
597
|
+
const subject = e.subject as any
|
|
598
|
+
expect(subject.convoId).toEqual(convoId2)
|
|
599
|
+
expect(subject.did).toEqual(sc.dids.carol)
|
|
600
|
+
// All events should be reports
|
|
601
|
+
expect(e.event.$type).toEqual(
|
|
602
|
+
'tools.ozone.moderation.defs#modEventReport',
|
|
603
|
+
)
|
|
604
|
+
})
|
|
605
|
+
// Verify we got the report we created
|
|
606
|
+
const convo2Comments = convo2Events.events.map(
|
|
607
|
+
(e) => (e.event as any).comment,
|
|
608
|
+
)
|
|
609
|
+
expect(convo2Comments).toContain('spam in convo 2')
|
|
610
|
+
})
|
|
504
611
|
})
|
|
505
612
|
|
|
506
613
|
describe('get event', () => {
|
|
@@ -113,5 +113,28 @@ describe('moderation-status-tags', () => {
|
|
|
113
113
|
expect(englishOrJapaneseDids).toContain(sc.dids.alice)
|
|
114
114
|
expect(englishOrJapaneseDids).toContain(sc.dids.bob)
|
|
115
115
|
})
|
|
116
|
+
|
|
117
|
+
it('adds tag to conversation', async () => {
|
|
118
|
+
const subject = {
|
|
119
|
+
$type: 'chat.bsky.convo.defs#convoRef',
|
|
120
|
+
did: sc.dids.alice,
|
|
121
|
+
convoId: '123',
|
|
122
|
+
}
|
|
123
|
+
await modClient.emitEvent({
|
|
124
|
+
subject: subject,
|
|
125
|
+
event: {
|
|
126
|
+
$type: 'tools.ozone.moderation.defs#modEventTag',
|
|
127
|
+
add: ['interaction-churn'],
|
|
128
|
+
remove: [],
|
|
129
|
+
},
|
|
130
|
+
})
|
|
131
|
+
const status = await network.ozone.ctx.db.db
|
|
132
|
+
.selectFrom('moderation_subject_status')
|
|
133
|
+
.selectAll()
|
|
134
|
+
.where('did', '=', subject.did)
|
|
135
|
+
.where('convoId', '=', subject.convoId)
|
|
136
|
+
.executeTakeFirstOrThrow()
|
|
137
|
+
expect(status.tags).toContain('interaction-churn')
|
|
138
|
+
})
|
|
116
139
|
})
|
|
117
140
|
})
|
|
@@ -276,6 +276,88 @@ describe('moderation-statuses', () => {
|
|
|
276
276
|
"only bob's account statuses are returned, no events have a URI even though the subjectType is record",
|
|
277
277
|
)
|
|
278
278
|
})
|
|
279
|
+
|
|
280
|
+
it('returns statuses for conversations', async () => {
|
|
281
|
+
const convoId1 = 'test-convo-123'
|
|
282
|
+
const convoId2 = 'test-convo-456'
|
|
283
|
+
|
|
284
|
+
// Create reports for conversation 1
|
|
285
|
+
await sc.createReport({
|
|
286
|
+
reasonType: REASONSPAM,
|
|
287
|
+
reason: 'spam in convo 1',
|
|
288
|
+
subject: {
|
|
289
|
+
$type: 'chat.bsky.convo.defs#convoRef',
|
|
290
|
+
did: sc.dids.carol,
|
|
291
|
+
convoId: convoId1,
|
|
292
|
+
},
|
|
293
|
+
reportedBy: sc.dids.alice,
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
// Create another report for conversation 1
|
|
297
|
+
await sc.createReport({
|
|
298
|
+
reasonType: REASONMISLEADING,
|
|
299
|
+
reason: 'misleading in convo 1',
|
|
300
|
+
subject: {
|
|
301
|
+
$type: 'chat.bsky.convo.defs#convoRef',
|
|
302
|
+
did: sc.dids.carol,
|
|
303
|
+
convoId: convoId1,
|
|
304
|
+
},
|
|
305
|
+
reportedBy: sc.dids.bob,
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
// Create report for conversation 2
|
|
309
|
+
await sc.createReport({
|
|
310
|
+
reasonType: REASONSPAM,
|
|
311
|
+
reason: 'spam in convo 2',
|
|
312
|
+
subject: {
|
|
313
|
+
$type: 'chat.bsky.convo.defs#convoRef',
|
|
314
|
+
did: sc.dids.carol,
|
|
315
|
+
convoId: convoId2,
|
|
316
|
+
},
|
|
317
|
+
reportedBy: sc.dids.alice,
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
// Query statuses for conversation 1 using AT URI format
|
|
321
|
+
const convo1Statuses = await modClient.queryStatuses({
|
|
322
|
+
subject: `at://${sc.dids.carol}/chat.bsky.convo/${convoId1}`,
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
// Query statuses for conversation 2
|
|
326
|
+
const convo2Statuses = await modClient.queryStatuses({
|
|
327
|
+
subject: `at://${sc.dids.carol}/chat.bsky.convo/${convoId2}`,
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
// Query all conversation statuses for carol
|
|
331
|
+
const allCarolConvoStatuses = await modClient.queryStatuses({
|
|
332
|
+
subject: sc.dids.carol,
|
|
333
|
+
includeAllUserRecords: true,
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
// Verify conversation 1 has exactly 1 status (multiple reports create one status)
|
|
337
|
+
expect(convo1Statuses.subjectStatuses.length).toEqual(1)
|
|
338
|
+
expect(convo1Statuses.subjectStatuses[0].subject.$type).toEqual(
|
|
339
|
+
'chat.bsky.convo.defs#convoRef',
|
|
340
|
+
)
|
|
341
|
+
expect(convo1Statuses.subjectStatuses[0].reviewState).toEqual(REVIEWOPEN)
|
|
342
|
+
|
|
343
|
+
// Verify conversation 2 has exactly 1 status
|
|
344
|
+
expect(convo2Statuses.subjectStatuses.length).toEqual(1)
|
|
345
|
+
expect(convo2Statuses.subjectStatuses[0].subject.$type).toEqual(
|
|
346
|
+
'chat.bsky.convo.defs#convoRef',
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
// Verify statuses are properly isolated by conversation
|
|
350
|
+
const convo1Subject = convo1Statuses.subjectStatuses[0].subject as any
|
|
351
|
+
const convo2Subject = convo2Statuses.subjectStatuses[0].subject as any
|
|
352
|
+
expect(convo1Subject.convoId).toEqual(convoId1)
|
|
353
|
+
expect(convo2Subject.convoId).toEqual(convoId2)
|
|
354
|
+
|
|
355
|
+
// Verify includeAllUserRecords includes conversations
|
|
356
|
+
const convoStatuses = allCarolConvoStatuses.subjectStatuses.filter(
|
|
357
|
+
(s) => s.subject.$type === 'chat.bsky.convo.defs#convoRef',
|
|
358
|
+
)
|
|
359
|
+
expect(convoStatuses.length).toBeGreaterThanOrEqual(2)
|
|
360
|
+
})
|
|
279
361
|
})
|
|
280
362
|
|
|
281
363
|
describe('reviewState changes', () => {
|
package/tests/moderation.test.ts
CHANGED
|
@@ -197,6 +197,53 @@ describe('moderation', () => {
|
|
|
197
197
|
),
|
|
198
198
|
).toBe(true)
|
|
199
199
|
})
|
|
200
|
+
|
|
201
|
+
it('creates reports of convo', async () => {
|
|
202
|
+
const convoId1 = 'convoId1'
|
|
203
|
+
const convoId2 = 'convoId2'
|
|
204
|
+
const reportA = await sc.createReport({
|
|
205
|
+
reportedBy: sc.dids.alice,
|
|
206
|
+
reasonType: REASONSPAM,
|
|
207
|
+
subject: {
|
|
208
|
+
$type: 'chat.bsky.convo.defs#convoRef',
|
|
209
|
+
did: sc.dids.carol,
|
|
210
|
+
convoId: convoId1,
|
|
211
|
+
},
|
|
212
|
+
})
|
|
213
|
+
const reportB = await sc.createReport({
|
|
214
|
+
reportedBy: sc.dids.carol,
|
|
215
|
+
reasonType: REASONOTHER,
|
|
216
|
+
reason: 'defamation',
|
|
217
|
+
subject: {
|
|
218
|
+
$type: 'chat.bsky.convo.defs#convoRef',
|
|
219
|
+
did: sc.dids.carol,
|
|
220
|
+
convoId: convoId2,
|
|
221
|
+
},
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
// Verify reportA
|
|
225
|
+
expect(reportA.subject.$type).toBe('chat.bsky.convo.defs#convoRef')
|
|
226
|
+
expect(ChatBskyConvoDefs.isConvoRef(reportA.subject)).toBe(true)
|
|
227
|
+
if (ChatBskyConvoDefs.isConvoRef(reportA.subject)) {
|
|
228
|
+
expect(reportA.subject.convoId).toBe(convoId1)
|
|
229
|
+
expect(reportA.subject.did).toBe(sc.dids.carol)
|
|
230
|
+
}
|
|
231
|
+
expect(reportA.reasonType).toBe(REASONSPAM)
|
|
232
|
+
expect(reportA.reportedBy).toBe(sc.dids.alice)
|
|
233
|
+
expect(reportA.id).toBeGreaterThan(0)
|
|
234
|
+
|
|
235
|
+
// Verify reportB
|
|
236
|
+
expect(reportB.subject.$type).toBe('chat.bsky.convo.defs#convoRef')
|
|
237
|
+
expect(ChatBskyConvoDefs.isConvoRef(reportB.subject)).toBe(true)
|
|
238
|
+
if (ChatBskyConvoDefs.isConvoRef(reportB.subject)) {
|
|
239
|
+
expect(reportB.subject.convoId).toBe(convoId2)
|
|
240
|
+
expect(reportB.subject.did).toBe(sc.dids.carol)
|
|
241
|
+
}
|
|
242
|
+
expect(reportB.reasonType).toBe(REASONOTHER)
|
|
243
|
+
expect(reportB.reason).toBe('defamation')
|
|
244
|
+
expect(reportB.reportedBy).toBe(sc.dids.carol)
|
|
245
|
+
expect(reportB.id).toBeGreaterThan(reportA.id)
|
|
246
|
+
})
|
|
200
247
|
})
|
|
201
248
|
|
|
202
249
|
describe('actioning', () => {
|
|
@@ -729,6 +776,32 @@ describe('moderation', () => {
|
|
|
729
776
|
})
|
|
730
777
|
})
|
|
731
778
|
|
|
779
|
+
it('allows conversation escalate', async () => {
|
|
780
|
+
const subject = {
|
|
781
|
+
$type: 'chat.bsky.convo.defs#convoRef',
|
|
782
|
+
did: sc.dids.bob,
|
|
783
|
+
convoId: '123',
|
|
784
|
+
}
|
|
785
|
+
await modClient.emitEvent({
|
|
786
|
+
event: {
|
|
787
|
+
$type: 'tools.ozone.moderation.defs#modEventEscalate',
|
|
788
|
+
comment: 'Y',
|
|
789
|
+
},
|
|
790
|
+
subject,
|
|
791
|
+
createdBy: 'did:example:admin',
|
|
792
|
+
})
|
|
793
|
+
|
|
794
|
+
const status = await network.ozone.ctx.db.db
|
|
795
|
+
.selectFrom('moderation_subject_status')
|
|
796
|
+
.selectAll()
|
|
797
|
+
.where('did', '=', subject.did)
|
|
798
|
+
.where('recordPath', '=', '')
|
|
799
|
+
.where('convoId', '=', subject.convoId)
|
|
800
|
+
.executeTakeFirst()
|
|
801
|
+
|
|
802
|
+
expect(status?.reviewState).toEqual(REVIEWESCALATED)
|
|
803
|
+
})
|
|
804
|
+
|
|
732
805
|
async function emitLabelEvent(
|
|
733
806
|
opts: Partial<ToolsOzoneModerationEmitEvent.InputSchema> & {
|
|
734
807
|
subject: ToolsOzoneModerationEmitEvent.InputSchema['subject']
|