@atproto/bsky 0.0.15 → 0.0.17
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 +18 -0
- package/dist/api/com/atproto/moderation/util.d.ts +4 -3
- package/dist/cache/read-through.d.ts +30 -0
- package/dist/config.d.ts +18 -0
- package/dist/context.d.ts +21 -6
- package/dist/daemon/config.d.ts +15 -0
- package/dist/daemon/context.d.ts +15 -0
- package/dist/daemon/index.d.ts +23 -0
- package/dist/daemon/logger.d.ts +3 -0
- package/dist/daemon/notifications.d.ts +18 -0
- package/dist/daemon/services.d.ts +11 -0
- package/dist/db/database-schema.d.ts +1 -2
- package/dist/db/index.js +41 -1
- package/dist/db/index.js.map +3 -3
- package/dist/db/migrations/20231003T202833377Z-create-moderation-subject-status.d.ts +3 -0
- package/dist/db/migrations/20231205T000257238Z-remove-did-cache.d.ts +3 -0
- package/dist/db/migrations/index.d.ts +2 -0
- package/dist/db/pagination.d.ts +2 -1
- package/dist/db/{periodic-moderation-action-reversal.d.ts → periodic-moderation-event-reversal.d.ts} +3 -5
- package/dist/db/tables/moderation.d.ts +24 -34
- package/dist/did-cache.d.ts +10 -7
- package/dist/feed-gen/types.d.ts +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +4370 -2758
- package/dist/index.js.map +3 -3
- package/dist/indexer/context.d.ts +2 -0
- package/dist/indexer/index.d.ts +1 -0
- package/dist/lexicon/index.d.ts +23 -18
- package/dist/lexicon/lexicons.d.ts +561 -412
- package/dist/lexicon/types/app/bsky/feed/defs.d.ts +1 -7
- package/dist/lexicon/types/app/bsky/graph/defs.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/admin/defs.d.ts +114 -48
- package/dist/lexicon/types/com/atproto/admin/{resolveModerationReports.d.ts → deleteAccount.d.ts} +2 -13
- package/dist/lexicon/types/com/atproto/admin/{takeModerationAction.d.ts → emitModerationEvent.d.ts} +5 -6
- package/dist/lexicon/types/com/atproto/admin/{getModerationAction.d.ts → getModerationEvent.d.ts} +1 -1
- package/dist/lexicon/types/com/atproto/admin/{getModerationActions.d.ts → queryModerationEvents.d.ts} +5 -1
- package/dist/lexicon/types/com/atproto/admin/{getModerationReports.d.ts → queryModerationStatuses.d.ts} +12 -6
- package/dist/lexicon/types/com/atproto/admin/sendEmail.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/{admin/getModerationReport.d.ts → temp/importRepo.d.ts} +10 -7
- package/dist/lexicon/types/com/atproto/temp/pushBlob.d.ts +25 -0
- package/dist/lexicon/types/com/atproto/{admin/reverseModerationAction.d.ts → temp/transferAccount.d.ts} +11 -5
- package/dist/logger.d.ts +1 -0
- package/dist/migrate-moderation-data.d.ts +1 -0
- package/dist/redis.d.ts +10 -1
- package/dist/services/actor/index.d.ts +18 -4
- package/dist/services/actor/views.d.ts +6 -8
- package/dist/services/feed/index.d.ts +7 -4
- package/dist/services/feed/util.d.ts +9 -1
- package/dist/services/feed/views.d.ts +11 -21
- package/dist/services/graph/index.d.ts +5 -29
- package/dist/services/graph/types.d.ts +1 -0
- package/dist/services/index.d.ts +3 -7
- package/dist/services/label/index.d.ts +10 -4
- package/dist/services/moderation/index.d.ts +134 -72
- package/dist/services/moderation/pagination.d.ts +36 -0
- package/dist/services/moderation/status.d.ts +13 -0
- package/dist/services/moderation/types.d.ts +35 -0
- package/dist/services/moderation/views.d.ts +18 -14
- package/dist/services/types.d.ts +3 -0
- package/dist/services/util/notification.d.ts +5 -0
- package/dist/services/util/post.d.ts +6 -6
- package/dist/util/debug.d.ts +1 -1
- package/dist/util/retry.d.ts +1 -6
- package/package.json +11 -11
- package/src/api/app/bsky/feed/getActorFeeds.ts +2 -1
- package/src/api/app/bsky/feed/getActorLikes.ts +1 -3
- package/src/api/app/bsky/feed/getAuthorFeed.ts +1 -3
- package/src/api/app/bsky/feed/getFeed.ts +9 -9
- package/src/api/app/bsky/feed/getFeedGenerator.ts +3 -0
- package/src/api/app/bsky/feed/getFeedGenerators.ts +2 -1
- package/src/api/app/bsky/feed/getListFeed.ts +1 -3
- package/src/api/app/bsky/feed/getPostThread.ts +15 -54
- package/src/api/app/bsky/feed/getPosts.ts +21 -18
- package/src/api/app/bsky/feed/getSuggestedFeeds.ts +2 -1
- package/src/api/app/bsky/feed/getTimeline.ts +1 -3
- package/src/api/app/bsky/feed/searchPosts.ts +20 -17
- package/src/api/app/bsky/graph/getList.ts +6 -3
- package/src/api/app/bsky/graph/getListBlocks.ts +3 -2
- package/src/api/app/bsky/graph/getListMutes.ts +2 -1
- package/src/api/app/bsky/graph/getLists.ts +2 -1
- package/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts +3 -1
- package/src/api/blob-resolver.ts +6 -11
- package/src/api/com/atproto/admin/emitModerationEvent.ts +220 -0
- package/src/api/com/atproto/admin/{getModerationActions.ts → getModerationEvent.ts} +5 -11
- package/src/api/com/atproto/admin/getRecord.ts +1 -0
- package/src/api/com/atproto/admin/{getModerationReports.ts → queryModerationEvents.ts} +13 -16
- package/src/api/com/atproto/admin/queryModerationStatuses.ts +55 -0
- package/src/api/com/atproto/moderation/createReport.ts +9 -7
- package/src/api/com/atproto/moderation/util.ts +38 -20
- package/src/api/index.ts +8 -14
- package/src/auth.ts +29 -21
- package/src/auto-moderator/index.ts +26 -19
- package/src/cache/read-through.ts +151 -0
- package/src/config.ts +90 -1
- package/src/context.ts +11 -7
- package/src/daemon/config.ts +60 -0
- package/src/daemon/context.ts +27 -0
- package/src/daemon/index.ts +78 -0
- package/src/daemon/logger.ts +6 -0
- package/src/daemon/notifications.ts +54 -0
- package/src/daemon/services.ts +22 -0
- package/src/db/database-schema.ts +0 -2
- package/src/db/migrations/20231003T202833377Z-create-moderation-subject-status.ts +123 -0
- package/src/db/migrations/20231205T000257238Z-remove-did-cache.ts +14 -0
- package/src/db/migrations/index.ts +2 -0
- package/src/db/pagination.ts +26 -3
- package/src/db/{periodic-moderation-action-reversal.ts → periodic-moderation-event-reversal.ts} +50 -46
- package/src/db/tables/moderation.ts +35 -52
- package/src/did-cache.ts +33 -56
- package/src/feed-gen/bsky-team.ts +1 -1
- package/src/feed-gen/hot-classic.ts +1 -1
- package/src/feed-gen/index.ts +0 -4
- package/src/feed-gen/mutuals.ts +6 -2
- package/src/feed-gen/types.ts +1 -1
- package/src/index.ts +57 -17
- package/src/indexer/context.ts +5 -0
- package/src/indexer/index.ts +10 -7
- package/src/lexicon/index.ts +80 -67
- package/src/lexicon/lexicons.ts +698 -507
- package/src/lexicon/types/app/bsky/feed/defs.ts +1 -18
- package/src/lexicon/types/app/bsky/graph/defs.ts +1 -0
- package/src/lexicon/types/com/atproto/admin/defs.ts +276 -84
- package/src/lexicon/types/com/atproto/admin/{resolveModerationReports.ts → deleteAccount.ts} +2 -13
- package/src/lexicon/types/com/atproto/admin/{takeModerationAction.ts → emitModerationEvent.ts} +13 -11
- package/src/lexicon/types/com/atproto/admin/{getModerationReport.ts → getModerationEvent.ts} +1 -1
- package/src/lexicon/types/com/atproto/admin/{getModerationActions.ts → queryModerationEvents.ts} +8 -1
- package/src/lexicon/types/com/atproto/admin/{getModerationReports.ts → queryModerationStatuses.ts} +21 -14
- package/src/lexicon/types/com/atproto/admin/sendEmail.ts +1 -0
- package/src/lexicon/types/com/atproto/{admin/getModerationAction.ts → temp/importRepo.ts} +11 -7
- package/src/lexicon/types/com/atproto/temp/pushBlob.ts +39 -0
- package/src/lexicon/types/com/atproto/{admin/reverseModerationAction.ts → temp/transferAccount.ts} +18 -5
- package/src/logger.ts +2 -0
- package/src/migrate-moderation-data.ts +414 -0
- package/src/redis.ts +43 -3
- package/src/services/actor/index.ts +55 -7
- package/src/services/actor/views.ts +18 -21
- package/src/services/feed/index.ts +52 -19
- package/src/services/feed/util.ts +47 -19
- package/src/services/feed/views.ts +87 -13
- package/src/services/graph/index.ts +21 -3
- package/src/services/graph/types.ts +1 -0
- package/src/services/index.ts +14 -14
- package/src/services/indexing/index.ts +7 -10
- package/src/services/indexing/plugins/block.ts +2 -3
- package/src/services/indexing/plugins/feed-generator.ts +2 -3
- package/src/services/indexing/plugins/follow.ts +2 -3
- package/src/services/indexing/plugins/like.ts +2 -3
- package/src/services/indexing/plugins/list-block.ts +2 -3
- package/src/services/indexing/plugins/list-item.ts +2 -3
- package/src/services/indexing/plugins/list.ts +2 -3
- package/src/services/indexing/plugins/post.ts +16 -4
- package/src/services/indexing/plugins/repost.ts +2 -3
- package/src/services/indexing/plugins/thread-gate.ts +2 -3
- package/src/services/label/index.ts +68 -25
- package/src/services/moderation/index.ts +380 -395
- package/src/services/moderation/pagination.ts +96 -0
- package/src/services/moderation/status.ts +241 -0
- package/src/services/moderation/types.ts +49 -0
- package/src/services/moderation/views.ts +278 -329
- package/src/services/types.ts +4 -0
- package/src/services/util/notification.ts +70 -0
- package/src/util/debug.ts +2 -2
- package/src/util/retry.ts +1 -44
- package/tests/__snapshots__/feed-generation.test.ts.snap +322 -6
- package/tests/__snapshots__/indexing.test.ts.snap +0 -6
- package/tests/admin/__snapshots__/get-record.test.ts.snap +30 -132
- package/tests/admin/__snapshots__/get-repo.test.ts.snap +14 -60
- package/tests/admin/__snapshots__/moderation-events.test.ts.snap +146 -0
- package/tests/admin/__snapshots__/moderation-statuses.test.ts.snap +64 -0
- package/tests/admin/__snapshots__/moderation.test.ts.snap +0 -125
- package/tests/admin/get-record.test.ts +5 -9
- package/tests/admin/get-repo.test.ts +10 -12
- package/tests/admin/moderation-events.test.ts +221 -0
- package/tests/admin/moderation-statuses.test.ts +145 -0
- package/tests/admin/moderation.test.ts +512 -860
- package/tests/admin/repo-search.test.ts +3 -3
- package/tests/algos/hot-classic.test.ts +1 -2
- package/tests/auth.test.ts +1 -1
- package/tests/auto-moderator/fuzzy-matcher.test.ts +2 -1
- package/tests/auto-moderator/labeler.test.ts +19 -20
- package/tests/auto-moderator/takedowns.test.ts +61 -28
- package/tests/blob-resolver.test.ts +4 -2
- package/tests/daemon.test.ts +191 -0
- package/tests/did-cache.test.ts +20 -5
- package/tests/feed-generation.test.ts +57 -9
- package/tests/handle-invalidation.test.ts +1 -5
- package/tests/indexing.test.ts +20 -13
- package/tests/redis-cache.test.ts +231 -0
- package/tests/seeds/basic.ts +3 -0
- package/tests/subscription/repo.test.ts +4 -7
- package/tests/views/__snapshots__/block-lists.test.ts.snap +3 -9
- package/tests/views/__snapshots__/blocks.test.ts.snap +0 -9
- package/tests/views/__snapshots__/mute-lists.test.ts.snap +5 -5
- package/tests/views/__snapshots__/mutes.test.ts.snap +0 -3
- package/tests/views/__snapshots__/thread.test.ts.snap +0 -30
- package/tests/views/actor-search.test.ts +2 -3
- package/tests/views/author-feed.test.ts +42 -36
- package/tests/views/follows.test.ts +40 -35
- package/tests/views/list-feed.test.ts +17 -9
- package/tests/views/notifications.test.ts +13 -9
- package/tests/views/profile.test.ts +20 -19
- package/tests/views/thread.test.ts +117 -94
- package/tests/views/threadgating.test.ts +89 -19
- package/tests/views/timeline.test.ts +21 -13
- package/dist/api/com/atproto/admin/resolveModerationReports.d.ts +0 -3
- package/dist/api/com/atproto/admin/reverseModerationAction.d.ts +0 -3
- package/dist/api/com/atproto/admin/takeModerationAction.d.ts +0 -3
- package/dist/db/tables/did-cache.d.ts +0 -10
- package/dist/feed-gen/best-of-follows.d.ts +0 -29
- package/dist/feed-gen/whats-hot.d.ts +0 -29
- package/dist/feed-gen/with-friends.d.ts +0 -3
- package/dist/label-cache.d.ts +0 -19
- package/src/api/com/atproto/admin/getModerationAction.ts +0 -44
- package/src/api/com/atproto/admin/getModerationReport.ts +0 -43
- package/src/api/com/atproto/admin/resolveModerationReports.ts +0 -24
- package/src/api/com/atproto/admin/reverseModerationAction.ts +0 -115
- package/src/api/com/atproto/admin/takeModerationAction.ts +0 -156
- package/src/db/tables/did-cache.ts +0 -13
- package/src/feed-gen/best-of-follows.ts +0 -74
- package/src/feed-gen/whats-hot.ts +0 -101
- package/src/feed-gen/with-friends.ts +0 -39
- package/src/label-cache.ts +0 -90
- package/tests/admin/__snapshots__/get-moderation-action.test.ts.snap +0 -172
- package/tests/admin/__snapshots__/get-moderation-actions.test.ts.snap +0 -178
- package/tests/admin/__snapshots__/get-moderation-report.test.ts.snap +0 -177
- package/tests/admin/__snapshots__/get-moderation-reports.test.ts.snap +0 -307
- package/tests/admin/get-moderation-action.test.ts +0 -100
- package/tests/admin/get-moderation-actions.test.ts +0 -164
- package/tests/admin/get-moderation-report.test.ts +0 -100
- package/tests/admin/get-moderation-reports.test.ts +0 -332
- package/tests/algos/whats-hot.test.ts +0 -118
- package/tests/algos/with-friends.test.ts +0 -145
- /package/dist/api/com/atproto/admin/{getModerationAction.d.ts → emitModerationEvent.d.ts} +0 -0
- /package/dist/api/com/atproto/admin/{getModerationActions.d.ts → getModerationEvent.d.ts} +0 -0
- /package/dist/api/com/atproto/admin/{getModerationReport.d.ts → queryModerationEvents.d.ts} +0 -0
- /package/dist/api/com/atproto/admin/{getModerationReports.d.ts → queryModerationStatuses.d.ts} +0 -0
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { SeedClient, TestNetwork } from '@atproto/dev-env'
|
|
2
2
|
import AtpAgent from '@atproto/api'
|
|
3
|
-
import {
|
|
4
|
-
ACKNOWLEDGE,
|
|
5
|
-
TAKEDOWN,
|
|
6
|
-
} from '@atproto/api/src/client/types/com/atproto/admin/defs'
|
|
7
3
|
import {
|
|
8
4
|
REASONOTHER,
|
|
9
5
|
REASONSPAM,
|
|
@@ -23,6 +19,7 @@ describe('admin get repo view', () => {
|
|
|
23
19
|
agent = network.pds.getClient()
|
|
24
20
|
sc = network.getSeedClient()
|
|
25
21
|
await basicSeed(sc)
|
|
22
|
+
await network.processAll()
|
|
26
23
|
})
|
|
27
24
|
|
|
28
25
|
afterAll(async () => {
|
|
@@ -30,8 +27,8 @@ describe('admin get repo view', () => {
|
|
|
30
27
|
})
|
|
31
28
|
|
|
32
29
|
beforeAll(async () => {
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
await sc.emitModerationEvent({
|
|
31
|
+
event: { $type: 'com.atproto.admin.defs#modEventAcknowledge' },
|
|
35
32
|
subject: {
|
|
36
33
|
$type: 'com.atproto.admin.defs#repoRef',
|
|
37
34
|
did: sc.dids.alice,
|
|
@@ -54,9 +51,8 @@ describe('admin get repo view', () => {
|
|
|
54
51
|
did: sc.dids.alice,
|
|
55
52
|
},
|
|
56
53
|
})
|
|
57
|
-
await sc.
|
|
58
|
-
|
|
59
|
-
action: TAKEDOWN,
|
|
54
|
+
await sc.emitModerationEvent({
|
|
55
|
+
event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
|
|
60
56
|
subject: {
|
|
61
57
|
$type: 'com.atproto.admin.defs#repoRef',
|
|
62
58
|
did: sc.dids.alice,
|
|
@@ -101,9 +97,11 @@ describe('admin get repo view', () => {
|
|
|
101
97
|
expect(beforeEmailVerification.emailConfirmedAt).toBeUndefined()
|
|
102
98
|
const timestampBeforeVerification = Date.now()
|
|
103
99
|
const bobsAccount = sc.accounts[sc.dids.bob]
|
|
104
|
-
const verificationToken =
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
const verificationToken =
|
|
101
|
+
await network.pds.ctx.accountManager.createEmailToken(
|
|
102
|
+
sc.dids.bob,
|
|
103
|
+
'confirm_email',
|
|
104
|
+
)
|
|
107
105
|
await agent.api.com.atproto.server.confirmEmail(
|
|
108
106
|
{ email: bobsAccount.email, token: verificationToken },
|
|
109
107
|
{
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { TestNetwork, SeedClient } from '@atproto/dev-env'
|
|
2
|
+
import AtpAgent, { ComAtprotoAdminDefs } from '@atproto/api'
|
|
3
|
+
import { forSnapshot } from '../_util'
|
|
4
|
+
import basicSeed from '../seeds/basic'
|
|
5
|
+
import {
|
|
6
|
+
REASONMISLEADING,
|
|
7
|
+
REASONSPAM,
|
|
8
|
+
} from '../../src/lexicon/types/com/atproto/moderation/defs'
|
|
9
|
+
|
|
10
|
+
describe('moderation-events', () => {
|
|
11
|
+
let network: TestNetwork
|
|
12
|
+
let agent: AtpAgent
|
|
13
|
+
let pdsAgent: AtpAgent
|
|
14
|
+
let sc: SeedClient
|
|
15
|
+
|
|
16
|
+
const emitModerationEvent = async (eventData) => {
|
|
17
|
+
return pdsAgent.api.com.atproto.admin.emitModerationEvent(eventData, {
|
|
18
|
+
encoding: 'application/json',
|
|
19
|
+
headers: network.bsky.adminAuthHeaders('moderator'),
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const queryModerationEvents = (eventQuery) =>
|
|
24
|
+
agent.api.com.atproto.admin.queryModerationEvents(eventQuery, {
|
|
25
|
+
headers: network.bsky.adminAuthHeaders('moderator'),
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const seedEvents = async () => {
|
|
29
|
+
const bobsAccount = {
|
|
30
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
31
|
+
did: sc.dids.bob,
|
|
32
|
+
}
|
|
33
|
+
const alicesAccount = {
|
|
34
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
35
|
+
did: sc.dids.alice,
|
|
36
|
+
}
|
|
37
|
+
const bobsPost = {
|
|
38
|
+
$type: 'com.atproto.repo.strongRef',
|
|
39
|
+
uri: sc.posts[sc.dids.bob][0].ref.uriStr,
|
|
40
|
+
cid: sc.posts[sc.dids.bob][0].ref.cidStr,
|
|
41
|
+
}
|
|
42
|
+
const alicesPost = {
|
|
43
|
+
$type: 'com.atproto.repo.strongRef',
|
|
44
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
45
|
+
cid: sc.posts[sc.dids.alice][0].ref.cidStr,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (let i = 0; i < 4; i++) {
|
|
49
|
+
await emitModerationEvent({
|
|
50
|
+
event: {
|
|
51
|
+
$type: 'com.atproto.admin.defs#modEventReport',
|
|
52
|
+
reportType: i % 2 ? REASONSPAM : REASONMISLEADING,
|
|
53
|
+
comment: 'X',
|
|
54
|
+
},
|
|
55
|
+
// Report bob's account by alice and vice versa
|
|
56
|
+
subject: i % 2 ? bobsAccount : alicesAccount,
|
|
57
|
+
createdBy: i % 2 ? sc.dids.alice : sc.dids.bob,
|
|
58
|
+
})
|
|
59
|
+
await emitModerationEvent({
|
|
60
|
+
event: {
|
|
61
|
+
$type: 'com.atproto.admin.defs#modEventReport',
|
|
62
|
+
reportType: REASONSPAM,
|
|
63
|
+
comment: 'X',
|
|
64
|
+
},
|
|
65
|
+
// Report bob's post by alice and vice versa
|
|
66
|
+
subject: i % 2 ? bobsPost : alicesPost,
|
|
67
|
+
createdBy: i % 2 ? sc.dids.alice : sc.dids.bob,
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
beforeAll(async () => {
|
|
73
|
+
network = await TestNetwork.create({
|
|
74
|
+
dbPostgresSchema: 'bsky_moderation_events',
|
|
75
|
+
})
|
|
76
|
+
agent = network.bsky.getClient()
|
|
77
|
+
pdsAgent = network.pds.getClient()
|
|
78
|
+
sc = network.getSeedClient()
|
|
79
|
+
await basicSeed(sc)
|
|
80
|
+
await network.processAll()
|
|
81
|
+
await seedEvents()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
afterAll(async () => {
|
|
85
|
+
await network.close()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe('query events', () => {
|
|
89
|
+
it('returns all events for record or repo', async () => {
|
|
90
|
+
const [bobsEvents, alicesPostEvents] = await Promise.all([
|
|
91
|
+
queryModerationEvents({
|
|
92
|
+
subject: sc.dids.bob,
|
|
93
|
+
}),
|
|
94
|
+
queryModerationEvents({
|
|
95
|
+
subject: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
96
|
+
}),
|
|
97
|
+
])
|
|
98
|
+
|
|
99
|
+
expect(forSnapshot(bobsEvents.data.events)).toMatchSnapshot()
|
|
100
|
+
expect(forSnapshot(alicesPostEvents.data.events)).toMatchSnapshot()
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('filters events by types', async () => {
|
|
104
|
+
const alicesAccount = {
|
|
105
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
106
|
+
did: sc.dids.alice,
|
|
107
|
+
}
|
|
108
|
+
await Promise.all([
|
|
109
|
+
emitModerationEvent({
|
|
110
|
+
event: {
|
|
111
|
+
$type: 'com.atproto.admin.defs#modEventComment',
|
|
112
|
+
comment: 'X',
|
|
113
|
+
},
|
|
114
|
+
subject: alicesAccount,
|
|
115
|
+
createdBy: 'did:plc:moderator',
|
|
116
|
+
}),
|
|
117
|
+
emitModerationEvent({
|
|
118
|
+
event: {
|
|
119
|
+
$type: 'com.atproto.admin.defs#modEventEscalate',
|
|
120
|
+
comment: 'X',
|
|
121
|
+
},
|
|
122
|
+
subject: alicesAccount,
|
|
123
|
+
createdBy: 'did:plc:moderator',
|
|
124
|
+
}),
|
|
125
|
+
])
|
|
126
|
+
const [allEvents, reportEvents] = await Promise.all([
|
|
127
|
+
queryModerationEvents({
|
|
128
|
+
subject: sc.dids.alice,
|
|
129
|
+
}),
|
|
130
|
+
queryModerationEvents({
|
|
131
|
+
subject: sc.dids.alice,
|
|
132
|
+
types: ['com.atproto.admin.defs#modEventReport'],
|
|
133
|
+
}),
|
|
134
|
+
])
|
|
135
|
+
|
|
136
|
+
expect(allEvents.data.events.length).toBeGreaterThan(
|
|
137
|
+
reportEvents.data.events.length,
|
|
138
|
+
)
|
|
139
|
+
expect(
|
|
140
|
+
[...new Set(reportEvents.data.events.map((e) => e.event.$type))].length,
|
|
141
|
+
).toEqual(1)
|
|
142
|
+
|
|
143
|
+
expect(
|
|
144
|
+
[...new Set(allEvents.data.events.map((e) => e.event.$type))].length,
|
|
145
|
+
).toEqual(3)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('returns events for all content by user', async () => {
|
|
149
|
+
const [forAccount, forPost] = await Promise.all([
|
|
150
|
+
queryModerationEvents({
|
|
151
|
+
subject: sc.dids.bob,
|
|
152
|
+
includeAllUserRecords: true,
|
|
153
|
+
}),
|
|
154
|
+
queryModerationEvents({
|
|
155
|
+
subject: sc.posts[sc.dids.bob][0].ref.uriStr,
|
|
156
|
+
includeAllUserRecords: true,
|
|
157
|
+
}),
|
|
158
|
+
])
|
|
159
|
+
|
|
160
|
+
expect(forAccount.data.events.length).toEqual(forPost.data.events.length)
|
|
161
|
+
// Save events are returned from both requests
|
|
162
|
+
expect(forPost.data.events.map(({ id }) => id).sort()).toEqual(
|
|
163
|
+
forAccount.data.events.map(({ id }) => id).sort(),
|
|
164
|
+
)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('returns paginated list of events with cursor', async () => {
|
|
168
|
+
const allEvents = await queryModerationEvents({
|
|
169
|
+
subject: sc.dids.bob,
|
|
170
|
+
includeAllUserRecords: true,
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
const getPaginatedEvents = async (
|
|
174
|
+
sortDirection: 'asc' | 'desc' = 'desc',
|
|
175
|
+
) => {
|
|
176
|
+
let defaultCursor: undefined | string = undefined
|
|
177
|
+
const events: ComAtprotoAdminDefs.ModEventView[] = []
|
|
178
|
+
let count = 0
|
|
179
|
+
do {
|
|
180
|
+
// get 1 event at a time and check we get all events
|
|
181
|
+
const { data } = await queryModerationEvents({
|
|
182
|
+
limit: 1,
|
|
183
|
+
subject: sc.dids.bob,
|
|
184
|
+
includeAllUserRecords: true,
|
|
185
|
+
cursor: defaultCursor,
|
|
186
|
+
sortDirection,
|
|
187
|
+
})
|
|
188
|
+
events.push(...data.events)
|
|
189
|
+
defaultCursor = data.cursor
|
|
190
|
+
count++
|
|
191
|
+
// The count is a circuit breaker to prevent infinite loop in case of failing test
|
|
192
|
+
} while (defaultCursor && count < 10)
|
|
193
|
+
|
|
194
|
+
return events
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const defaultEvents = await getPaginatedEvents()
|
|
198
|
+
const reversedEvents = await getPaginatedEvents('asc')
|
|
199
|
+
|
|
200
|
+
expect(allEvents.data.events.length).toEqual(4)
|
|
201
|
+
expect(defaultEvents.length).toEqual(allEvents.data.events.length)
|
|
202
|
+
expect(reversedEvents.length).toEqual(allEvents.data.events.length)
|
|
203
|
+
expect(reversedEvents[0].id).toEqual(defaultEvents[3].id)
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
describe('get event', () => {
|
|
208
|
+
it('gets an event by specific id', async () => {
|
|
209
|
+
const { data } = await pdsAgent.api.com.atproto.admin.getModerationEvent(
|
|
210
|
+
{
|
|
211
|
+
id: 1,
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
headers: network.bsky.adminAuthHeaders('moderator'),
|
|
215
|
+
},
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
expect(forSnapshot(data)).toMatchSnapshot()
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
})
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { TestNetwork, SeedClient } from '@atproto/dev-env'
|
|
2
|
+
import AtpAgent, {
|
|
3
|
+
ComAtprotoAdminDefs,
|
|
4
|
+
ComAtprotoAdminQueryModerationStatuses,
|
|
5
|
+
} from '@atproto/api'
|
|
6
|
+
import { forSnapshot } from '../_util'
|
|
7
|
+
import basicSeed from '../seeds/basic'
|
|
8
|
+
import {
|
|
9
|
+
REASONMISLEADING,
|
|
10
|
+
REASONSPAM,
|
|
11
|
+
} from '../../src/lexicon/types/com/atproto/moderation/defs'
|
|
12
|
+
|
|
13
|
+
describe('moderation-statuses', () => {
|
|
14
|
+
let network: TestNetwork
|
|
15
|
+
let agent: AtpAgent
|
|
16
|
+
let pdsAgent: AtpAgent
|
|
17
|
+
let sc: SeedClient
|
|
18
|
+
|
|
19
|
+
const emitModerationEvent = async (eventData) => {
|
|
20
|
+
return pdsAgent.api.com.atproto.admin.emitModerationEvent(eventData, {
|
|
21
|
+
encoding: 'application/json',
|
|
22
|
+
headers: network.bsky.adminAuthHeaders('moderator'),
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const queryModerationStatuses = (statusQuery) =>
|
|
27
|
+
agent.api.com.atproto.admin.queryModerationStatuses(statusQuery, {
|
|
28
|
+
headers: network.bsky.adminAuthHeaders('moderator'),
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const seedEvents = async () => {
|
|
32
|
+
const bobsAccount = {
|
|
33
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
34
|
+
did: sc.dids.bob,
|
|
35
|
+
}
|
|
36
|
+
const carlasAccount = {
|
|
37
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
38
|
+
did: sc.dids.alice,
|
|
39
|
+
}
|
|
40
|
+
const bobsPost = {
|
|
41
|
+
$type: 'com.atproto.repo.strongRef',
|
|
42
|
+
uri: sc.posts[sc.dids.bob][1].ref.uriStr,
|
|
43
|
+
cid: sc.posts[sc.dids.bob][1].ref.cidStr,
|
|
44
|
+
}
|
|
45
|
+
const alicesPost = {
|
|
46
|
+
$type: 'com.atproto.repo.strongRef',
|
|
47
|
+
uri: sc.posts[sc.dids.alice][1].ref.uriStr,
|
|
48
|
+
cid: sc.posts[sc.dids.alice][1].ref.cidStr,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i < 4; i++) {
|
|
52
|
+
await emitModerationEvent({
|
|
53
|
+
event: {
|
|
54
|
+
$type: 'com.atproto.admin.defs#modEventReport',
|
|
55
|
+
reportType: i % 2 ? REASONSPAM : REASONMISLEADING,
|
|
56
|
+
comment: 'X',
|
|
57
|
+
},
|
|
58
|
+
// Report bob's account by alice and vice versa
|
|
59
|
+
subject: i % 2 ? bobsAccount : carlasAccount,
|
|
60
|
+
createdBy: i % 2 ? sc.dids.alice : sc.dids.bob,
|
|
61
|
+
})
|
|
62
|
+
await emitModerationEvent({
|
|
63
|
+
event: {
|
|
64
|
+
$type: 'com.atproto.admin.defs#modEventReport',
|
|
65
|
+
reportType: REASONSPAM,
|
|
66
|
+
comment: 'X',
|
|
67
|
+
},
|
|
68
|
+
// Report bob's post by alice and vice versa
|
|
69
|
+
subject: i % 2 ? bobsPost : alicesPost,
|
|
70
|
+
createdBy: i % 2 ? sc.dids.alice : sc.dids.bob,
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
beforeAll(async () => {
|
|
76
|
+
network = await TestNetwork.create({
|
|
77
|
+
dbPostgresSchema: 'bsky_moderation_statuses',
|
|
78
|
+
})
|
|
79
|
+
agent = network.bsky.getClient()
|
|
80
|
+
pdsAgent = network.pds.getClient()
|
|
81
|
+
sc = network.getSeedClient()
|
|
82
|
+
await basicSeed(sc)
|
|
83
|
+
await network.processAll()
|
|
84
|
+
await seedEvents()
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
afterAll(async () => {
|
|
88
|
+
await network.close()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
describe('query statuses', () => {
|
|
92
|
+
it('returns statuses for subjects that received moderation events', async () => {
|
|
93
|
+
const response = await queryModerationStatuses({})
|
|
94
|
+
|
|
95
|
+
expect(forSnapshot(response.data.subjectStatuses)).toMatchSnapshot()
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('returns paginated statuses', async () => {
|
|
99
|
+
// We know there will be exactly 4 statuses in db
|
|
100
|
+
const getPaginatedStatuses = async (
|
|
101
|
+
params: ComAtprotoAdminQueryModerationStatuses.QueryParams,
|
|
102
|
+
) => {
|
|
103
|
+
let cursor: string | undefined = ''
|
|
104
|
+
const statuses: ComAtprotoAdminDefs.SubjectStatusView[] = []
|
|
105
|
+
let count = 0
|
|
106
|
+
do {
|
|
107
|
+
const results = await queryModerationStatuses({
|
|
108
|
+
limit: 1,
|
|
109
|
+
cursor,
|
|
110
|
+
...params,
|
|
111
|
+
})
|
|
112
|
+
cursor = results.data.cursor
|
|
113
|
+
statuses.push(...results.data.subjectStatuses)
|
|
114
|
+
count++
|
|
115
|
+
// The count is just a brake-check to prevent infinite loop
|
|
116
|
+
} while (cursor && count < 10)
|
|
117
|
+
|
|
118
|
+
return statuses
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const list = await getPaginatedStatuses({})
|
|
122
|
+
expect(list[0].id).toEqual(4)
|
|
123
|
+
expect(list[list.length - 1].id).toEqual(1)
|
|
124
|
+
|
|
125
|
+
await emitModerationEvent({
|
|
126
|
+
subject: list[1].subject,
|
|
127
|
+
event: {
|
|
128
|
+
$type: 'com.atproto.admin.defs#modEventAcknowledge',
|
|
129
|
+
comment: 'X',
|
|
130
|
+
},
|
|
131
|
+
createdBy: sc.dids.bob,
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
const listReviewedFirst = await getPaginatedStatuses({
|
|
135
|
+
sortDirection: 'desc',
|
|
136
|
+
sortField: 'lastReviewedAt',
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
// Verify that the item that was recently reviewed comes up first when sorted descendingly
|
|
140
|
+
// while the result set always contains same number of items regardless of sorting
|
|
141
|
+
expect(listReviewedFirst[0].id).toEqual(list[1].id)
|
|
142
|
+
expect(listReviewedFirst.length).toEqual(list.length)
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
})
|