@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,7 +1,6 @@
|
|
|
1
1
|
import assert from 'assert'
|
|
2
2
|
import AtpAgent from '@atproto/api'
|
|
3
3
|
import { TestNetwork, SeedClient } from '@atproto/dev-env'
|
|
4
|
-
import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/defs'
|
|
5
4
|
import { forSnapshot, getOriginator, paginateAll } from '../_util'
|
|
6
5
|
import basicSeed from '../seeds/basic'
|
|
7
6
|
import { FeedAlgorithm } from '../../src/api/app/bsky/util/feed'
|
|
@@ -182,11 +181,11 @@ describe('timeline views', () => {
|
|
|
182
181
|
})
|
|
183
182
|
|
|
184
183
|
it('blocks posts, reposts, replies by actor takedown', async () => {
|
|
185
|
-
|
|
184
|
+
await Promise.all(
|
|
186
185
|
[bob, carol].map((did) =>
|
|
187
|
-
agent.api.com.atproto.admin.
|
|
186
|
+
agent.api.com.atproto.admin.emitModerationEvent(
|
|
188
187
|
{
|
|
189
|
-
|
|
188
|
+
event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
|
|
190
189
|
subject: {
|
|
191
190
|
$type: 'com.atproto.admin.defs#repoRef',
|
|
192
191
|
did,
|
|
@@ -211,10 +210,14 @@ describe('timeline views', () => {
|
|
|
211
210
|
|
|
212
211
|
// Cleanup
|
|
213
212
|
await Promise.all(
|
|
214
|
-
|
|
215
|
-
agent.api.com.atproto.admin.
|
|
213
|
+
[bob, carol].map((did) =>
|
|
214
|
+
agent.api.com.atproto.admin.emitModerationEvent(
|
|
216
215
|
{
|
|
217
|
-
|
|
216
|
+
event: { $type: 'com.atproto.admin.defs#modEventReverseTakedown' },
|
|
217
|
+
subject: {
|
|
218
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
219
|
+
did,
|
|
220
|
+
},
|
|
218
221
|
createdBy: 'did:example:admin',
|
|
219
222
|
reason: 'Y',
|
|
220
223
|
},
|
|
@@ -230,11 +233,11 @@ describe('timeline views', () => {
|
|
|
230
233
|
it('blocks posts, reposts, replies by record takedown.', async () => {
|
|
231
234
|
const postRef1 = sc.posts[dan][1].ref // Repost
|
|
232
235
|
const postRef2 = sc.replies[bob][0].ref // Post and reply parent
|
|
233
|
-
|
|
236
|
+
await Promise.all(
|
|
234
237
|
[postRef1, postRef2].map((postRef) =>
|
|
235
|
-
agent.api.com.atproto.admin.
|
|
238
|
+
agent.api.com.atproto.admin.emitModerationEvent(
|
|
236
239
|
{
|
|
237
|
-
|
|
240
|
+
event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
|
|
238
241
|
subject: {
|
|
239
242
|
$type: 'com.atproto.repo.strongRef',
|
|
240
243
|
uri: postRef.uriStr,
|
|
@@ -260,10 +263,15 @@ describe('timeline views', () => {
|
|
|
260
263
|
|
|
261
264
|
// Cleanup
|
|
262
265
|
await Promise.all(
|
|
263
|
-
|
|
264
|
-
agent.api.com.atproto.admin.
|
|
266
|
+
[postRef1, postRef2].map((postRef) =>
|
|
267
|
+
agent.api.com.atproto.admin.emitModerationEvent(
|
|
265
268
|
{
|
|
266
|
-
|
|
269
|
+
event: { $type: 'com.atproto.admin.defs#modEventReverseTakedown' },
|
|
270
|
+
subject: {
|
|
271
|
+
$type: 'com.atproto.repo.strongRef',
|
|
272
|
+
uri: postRef.uriStr,
|
|
273
|
+
cid: postRef.cidStr,
|
|
274
|
+
},
|
|
267
275
|
createdBy: 'did:example:admin',
|
|
268
276
|
reason: 'Y',
|
|
269
277
|
},
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { AlgoHandler } from './types';
|
|
2
|
-
import { GenericKeyset } from '../db/pagination';
|
|
3
|
-
declare const handler: AlgoHandler;
|
|
4
|
-
export default handler;
|
|
5
|
-
declare type Result = {
|
|
6
|
-
score: number;
|
|
7
|
-
cid: string;
|
|
8
|
-
};
|
|
9
|
-
declare type LabeledResult = {
|
|
10
|
-
primary: number;
|
|
11
|
-
secondary: string;
|
|
12
|
-
};
|
|
13
|
-
export declare class ScoreKeyset extends GenericKeyset<Result, LabeledResult> {
|
|
14
|
-
labelResult(result: Result): {
|
|
15
|
-
primary: number;
|
|
16
|
-
secondary: string;
|
|
17
|
-
};
|
|
18
|
-
labeledResultToCursor(labeled: LabeledResult): {
|
|
19
|
-
primary: string;
|
|
20
|
-
secondary: string;
|
|
21
|
-
};
|
|
22
|
-
cursorToLabeledResult(cursor: {
|
|
23
|
-
primary: string;
|
|
24
|
-
secondary: string;
|
|
25
|
-
}): {
|
|
26
|
-
primary: number;
|
|
27
|
-
secondary: string;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { AlgoHandler } from './types';
|
|
2
|
-
import { GenericKeyset } from '../db/pagination';
|
|
3
|
-
declare const handler: AlgoHandler;
|
|
4
|
-
export default handler;
|
|
5
|
-
declare type Result = {
|
|
6
|
-
score: number;
|
|
7
|
-
cid: string;
|
|
8
|
-
};
|
|
9
|
-
declare type LabeledResult = {
|
|
10
|
-
primary: number;
|
|
11
|
-
secondary: string;
|
|
12
|
-
};
|
|
13
|
-
export declare class ScoreKeyset extends GenericKeyset<Result, LabeledResult> {
|
|
14
|
-
labelResult(result: Result): {
|
|
15
|
-
primary: number;
|
|
16
|
-
secondary: string;
|
|
17
|
-
};
|
|
18
|
-
labeledResultToCursor(labeled: LabeledResult): {
|
|
19
|
-
primary: string;
|
|
20
|
-
secondary: string;
|
|
21
|
-
};
|
|
22
|
-
cursorToLabeledResult(cursor: {
|
|
23
|
-
primary: string;
|
|
24
|
-
secondary: string;
|
|
25
|
-
}): {
|
|
26
|
-
primary: number;
|
|
27
|
-
secondary: string;
|
|
28
|
-
};
|
|
29
|
-
}
|
package/dist/label-cache.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { PrimaryDatabase } from './db';
|
|
2
|
-
import { Label } from './db/tables/label';
|
|
3
|
-
export declare class LabelCache {
|
|
4
|
-
db: PrimaryDatabase;
|
|
5
|
-
bySubject: Record<string, Label[]>;
|
|
6
|
-
latestLabel: string;
|
|
7
|
-
refreshes: number;
|
|
8
|
-
destroyed: boolean;
|
|
9
|
-
constructor(db: PrimaryDatabase);
|
|
10
|
-
start(): void;
|
|
11
|
-
fullRefresh(): Promise<void>;
|
|
12
|
-
partialRefresh(): Promise<void>;
|
|
13
|
-
poll(): Promise<void>;
|
|
14
|
-
processLabels(labels: Label[]): void;
|
|
15
|
-
wipeCache(): void;
|
|
16
|
-
stop(): void;
|
|
17
|
-
forSubject(subject: string, includeNeg?: boolean): Label[];
|
|
18
|
-
forSubjects(subjects: string[], includeNeg?: boolean): Label[];
|
|
19
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Server } from '../../../../lexicon'
|
|
2
|
-
import AppContext from '../../../../context'
|
|
3
|
-
import { addAccountInfoToRepoView, getPdsAccountInfo } from './util'
|
|
4
|
-
import {
|
|
5
|
-
isRecordView,
|
|
6
|
-
isRepoView,
|
|
7
|
-
} from '../../../../lexicon/types/com/atproto/admin/defs'
|
|
8
|
-
|
|
9
|
-
export default function (server: Server, ctx: AppContext) {
|
|
10
|
-
server.com.atproto.admin.getModerationAction({
|
|
11
|
-
auth: ctx.roleVerifier,
|
|
12
|
-
handler: async ({ params, auth }) => {
|
|
13
|
-
const { id } = params
|
|
14
|
-
const db = ctx.db.getPrimary()
|
|
15
|
-
const moderationService = ctx.services.moderation(db)
|
|
16
|
-
const result = await moderationService.getActionOrThrow(id)
|
|
17
|
-
|
|
18
|
-
const [action, accountInfo] = await Promise.all([
|
|
19
|
-
moderationService.views.actionDetail(result),
|
|
20
|
-
getPdsAccountInfo(ctx, result.subjectDid),
|
|
21
|
-
])
|
|
22
|
-
|
|
23
|
-
// add in pds account info if available
|
|
24
|
-
if (isRepoView(action.subject)) {
|
|
25
|
-
action.subject = addAccountInfoToRepoView(
|
|
26
|
-
action.subject,
|
|
27
|
-
accountInfo,
|
|
28
|
-
auth.credentials.moderator,
|
|
29
|
-
)
|
|
30
|
-
} else if (isRecordView(action.subject)) {
|
|
31
|
-
action.subject.repo = addAccountInfoToRepoView(
|
|
32
|
-
action.subject.repo,
|
|
33
|
-
accountInfo,
|
|
34
|
-
auth.credentials.moderator,
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
encoding: 'application/json',
|
|
40
|
-
body: action,
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
})
|
|
44
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Server } from '../../../../lexicon'
|
|
2
|
-
import AppContext from '../../../../context'
|
|
3
|
-
import {
|
|
4
|
-
isRecordView,
|
|
5
|
-
isRepoView,
|
|
6
|
-
} from '../../../../lexicon/types/com/atproto/admin/defs'
|
|
7
|
-
import { addAccountInfoToRepoView, getPdsAccountInfo } from './util'
|
|
8
|
-
|
|
9
|
-
export default function (server: Server, ctx: AppContext) {
|
|
10
|
-
server.com.atproto.admin.getModerationReport({
|
|
11
|
-
auth: ctx.roleVerifier,
|
|
12
|
-
handler: async ({ params, auth }) => {
|
|
13
|
-
const { id } = params
|
|
14
|
-
const db = ctx.db.getPrimary()
|
|
15
|
-
const moderationService = ctx.services.moderation(db)
|
|
16
|
-
const result = await moderationService.getReportOrThrow(id)
|
|
17
|
-
const [report, accountInfo] = await Promise.all([
|
|
18
|
-
moderationService.views.reportDetail(result),
|
|
19
|
-
getPdsAccountInfo(ctx, result.subjectDid),
|
|
20
|
-
])
|
|
21
|
-
|
|
22
|
-
// add in pds account info if available
|
|
23
|
-
if (isRepoView(report.subject)) {
|
|
24
|
-
report.subject = addAccountInfoToRepoView(
|
|
25
|
-
report.subject,
|
|
26
|
-
accountInfo,
|
|
27
|
-
auth.credentials.moderator,
|
|
28
|
-
)
|
|
29
|
-
} else if (isRecordView(report.subject)) {
|
|
30
|
-
report.subject.repo = addAccountInfoToRepoView(
|
|
31
|
-
report.subject.repo,
|
|
32
|
-
accountInfo,
|
|
33
|
-
auth.credentials.moderator,
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
encoding: 'application/json',
|
|
39
|
-
body: report,
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
})
|
|
43
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Server } from '../../../../lexicon'
|
|
2
|
-
import AppContext from '../../../../context'
|
|
3
|
-
|
|
4
|
-
export default function (server: Server, ctx: AppContext) {
|
|
5
|
-
server.com.atproto.admin.resolveModerationReports({
|
|
6
|
-
auth: ctx.roleVerifier,
|
|
7
|
-
handler: async ({ input }) => {
|
|
8
|
-
const db = ctx.db.getPrimary()
|
|
9
|
-
const moderationService = ctx.services.moderation(db)
|
|
10
|
-
const { actionId, reportIds, createdBy } = input.body
|
|
11
|
-
|
|
12
|
-
const moderationAction = await db.transaction(async (dbTxn) => {
|
|
13
|
-
const moderationTxn = ctx.services.moderation(dbTxn)
|
|
14
|
-
await moderationTxn.resolveReports({ reportIds, actionId, createdBy })
|
|
15
|
-
return await moderationTxn.getActionOrThrow(actionId)
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
encoding: 'application/json',
|
|
20
|
-
body: await moderationService.views.action(moderationAction),
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
})
|
|
24
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AuthRequiredError,
|
|
3
|
-
InvalidRequestError,
|
|
4
|
-
UpstreamFailureError,
|
|
5
|
-
} from '@atproto/xrpc-server'
|
|
6
|
-
import {
|
|
7
|
-
ACKNOWLEDGE,
|
|
8
|
-
ESCALATE,
|
|
9
|
-
TAKEDOWN,
|
|
10
|
-
} from '../../../../lexicon/types/com/atproto/admin/defs'
|
|
11
|
-
import { Server } from '../../../../lexicon'
|
|
12
|
-
import AppContext from '../../../../context'
|
|
13
|
-
import { retryHttp } from '../../../../util/retry'
|
|
14
|
-
|
|
15
|
-
export default function (server: Server, ctx: AppContext) {
|
|
16
|
-
server.com.atproto.admin.reverseModerationAction({
|
|
17
|
-
auth: ctx.roleVerifier,
|
|
18
|
-
handler: async ({ input, auth }) => {
|
|
19
|
-
const access = auth.credentials
|
|
20
|
-
const db = ctx.db.getPrimary()
|
|
21
|
-
const moderationService = ctx.services.moderation(db)
|
|
22
|
-
const { id, createdBy, reason } = input.body
|
|
23
|
-
|
|
24
|
-
const { result, restored } = await db.transaction(async (dbTxn) => {
|
|
25
|
-
const moderationTxn = ctx.services.moderation(dbTxn)
|
|
26
|
-
const labelTxn = ctx.services.label(dbTxn)
|
|
27
|
-
const now = new Date()
|
|
28
|
-
|
|
29
|
-
const existing = await moderationTxn.getAction(id)
|
|
30
|
-
if (!existing) {
|
|
31
|
-
throw new InvalidRequestError('Moderation action does not exist')
|
|
32
|
-
}
|
|
33
|
-
if (existing.reversedAt !== null) {
|
|
34
|
-
throw new InvalidRequestError(
|
|
35
|
-
'Moderation action has already been reversed',
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// apply access rules
|
|
40
|
-
|
|
41
|
-
// if less than moderator access then can only reverse ack and escalation actions
|
|
42
|
-
if (
|
|
43
|
-
!access.moderator &&
|
|
44
|
-
![ACKNOWLEDGE, ESCALATE].includes(existing.action)
|
|
45
|
-
) {
|
|
46
|
-
throw new AuthRequiredError(
|
|
47
|
-
'Must be a full moderator to reverse this type of action',
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
// if less than moderator access then cannot reverse takedown on an account
|
|
51
|
-
if (
|
|
52
|
-
!access.moderator &&
|
|
53
|
-
existing.action === TAKEDOWN &&
|
|
54
|
-
existing.subjectType === 'com.atproto.admin.defs#repoRef'
|
|
55
|
-
) {
|
|
56
|
-
throw new AuthRequiredError(
|
|
57
|
-
'Must be a full moderator to reverse an account takedown',
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const { result, restored } = await moderationTxn.revertAction({
|
|
62
|
-
id,
|
|
63
|
-
createdAt: now,
|
|
64
|
-
createdBy,
|
|
65
|
-
reason,
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
// invert creates & negates
|
|
69
|
-
const { createLabelVals, negateLabelVals } = result
|
|
70
|
-
const negate =
|
|
71
|
-
createLabelVals && createLabelVals.length > 0
|
|
72
|
-
? createLabelVals.split(' ')
|
|
73
|
-
: undefined
|
|
74
|
-
const create =
|
|
75
|
-
negateLabelVals && negateLabelVals.length > 0
|
|
76
|
-
? negateLabelVals.split(' ')
|
|
77
|
-
: undefined
|
|
78
|
-
await labelTxn.formatAndCreate(
|
|
79
|
-
ctx.cfg.labelerDid,
|
|
80
|
-
result.subjectUri ?? result.subjectDid,
|
|
81
|
-
result.subjectCid,
|
|
82
|
-
{ create, negate },
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
return { result, restored }
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
if (restored && ctx.moderationPushAgent) {
|
|
89
|
-
const agent = ctx.moderationPushAgent
|
|
90
|
-
const { subjects } = restored
|
|
91
|
-
const results = await Promise.allSettled(
|
|
92
|
-
subjects.map((subject) =>
|
|
93
|
-
retryHttp(() =>
|
|
94
|
-
agent.api.com.atproto.admin.updateSubjectStatus({
|
|
95
|
-
subject,
|
|
96
|
-
takedown: {
|
|
97
|
-
applied: false,
|
|
98
|
-
},
|
|
99
|
-
}),
|
|
100
|
-
),
|
|
101
|
-
),
|
|
102
|
-
)
|
|
103
|
-
const hadFailure = results.some((r) => r.status === 'rejected')
|
|
104
|
-
if (hadFailure) {
|
|
105
|
-
throw new UpstreamFailureError('failed to revert action on PDS')
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
encoding: 'application/json',
|
|
111
|
-
body: await moderationService.views.action(result),
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
})
|
|
115
|
-
}
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import { CID } from 'multiformats/cid'
|
|
2
|
-
import { AtUri } from '@atproto/syntax'
|
|
3
|
-
import {
|
|
4
|
-
AuthRequiredError,
|
|
5
|
-
InvalidRequestError,
|
|
6
|
-
UpstreamFailureError,
|
|
7
|
-
} from '@atproto/xrpc-server'
|
|
8
|
-
import { Server } from '../../../../lexicon'
|
|
9
|
-
import AppContext from '../../../../context'
|
|
10
|
-
import {
|
|
11
|
-
ACKNOWLEDGE,
|
|
12
|
-
ESCALATE,
|
|
13
|
-
TAKEDOWN,
|
|
14
|
-
} from '../../../../lexicon/types/com/atproto/admin/defs'
|
|
15
|
-
import { getSubject, getAction } from '../moderation/util'
|
|
16
|
-
import { TakedownSubjects } from '../../../../services/moderation'
|
|
17
|
-
import { retryHttp } from '../../../../util/retry'
|
|
18
|
-
|
|
19
|
-
export default function (server: Server, ctx: AppContext) {
|
|
20
|
-
server.com.atproto.admin.takeModerationAction({
|
|
21
|
-
auth: ctx.roleVerifier,
|
|
22
|
-
handler: async ({ input, auth }) => {
|
|
23
|
-
const access = auth.credentials
|
|
24
|
-
const db = ctx.db.getPrimary()
|
|
25
|
-
const moderationService = ctx.services.moderation(db)
|
|
26
|
-
const {
|
|
27
|
-
action,
|
|
28
|
-
subject,
|
|
29
|
-
reason,
|
|
30
|
-
createdBy,
|
|
31
|
-
createLabelVals,
|
|
32
|
-
negateLabelVals,
|
|
33
|
-
subjectBlobCids,
|
|
34
|
-
durationInHours,
|
|
35
|
-
} = input.body
|
|
36
|
-
|
|
37
|
-
// apply access rules
|
|
38
|
-
|
|
39
|
-
// if less than admin access then can not takedown an account
|
|
40
|
-
if (!access.moderator && action === TAKEDOWN && 'did' in subject) {
|
|
41
|
-
throw new AuthRequiredError(
|
|
42
|
-
'Must be a full moderator to perform an account takedown',
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
// if less than moderator access then can only take ack and escalation actions
|
|
46
|
-
if (!access.moderator && ![ACKNOWLEDGE, ESCALATE].includes(action)) {
|
|
47
|
-
throw new AuthRequiredError(
|
|
48
|
-
'Must be a full moderator to take this type of action',
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
// if less than moderator access then can not apply labels
|
|
52
|
-
if (
|
|
53
|
-
!access.moderator &&
|
|
54
|
-
(createLabelVals?.length || negateLabelVals?.length)
|
|
55
|
-
) {
|
|
56
|
-
throw new AuthRequiredError('Must be a full moderator to label content')
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
validateLabels([...(createLabelVals ?? []), ...(negateLabelVals ?? [])])
|
|
60
|
-
|
|
61
|
-
const { result, takenDown } = await db.transaction(async (dbTxn) => {
|
|
62
|
-
const moderationTxn = ctx.services.moderation(dbTxn)
|
|
63
|
-
const labelTxn = ctx.services.label(dbTxn)
|
|
64
|
-
|
|
65
|
-
const result = await moderationTxn.logAction({
|
|
66
|
-
action: getAction(action),
|
|
67
|
-
subject: getSubject(subject),
|
|
68
|
-
subjectBlobCids: subjectBlobCids?.map((cid) => CID.parse(cid)) ?? [],
|
|
69
|
-
createLabelVals,
|
|
70
|
-
negateLabelVals,
|
|
71
|
-
createdBy,
|
|
72
|
-
reason,
|
|
73
|
-
durationInHours,
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
let takenDown: TakedownSubjects | undefined
|
|
77
|
-
|
|
78
|
-
if (
|
|
79
|
-
result.action === TAKEDOWN &&
|
|
80
|
-
result.subjectType === 'com.atproto.admin.defs#repoRef' &&
|
|
81
|
-
result.subjectDid
|
|
82
|
-
) {
|
|
83
|
-
// No credentials to revoke on appview
|
|
84
|
-
takenDown = await moderationTxn.takedownRepo({
|
|
85
|
-
takedownId: result.id,
|
|
86
|
-
did: result.subjectDid,
|
|
87
|
-
})
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (
|
|
91
|
-
result.action === TAKEDOWN &&
|
|
92
|
-
result.subjectType === 'com.atproto.repo.strongRef' &&
|
|
93
|
-
result.subjectUri &&
|
|
94
|
-
result.subjectCid
|
|
95
|
-
) {
|
|
96
|
-
takenDown = await moderationTxn.takedownRecord({
|
|
97
|
-
takedownId: result.id,
|
|
98
|
-
uri: new AtUri(result.subjectUri),
|
|
99
|
-
cid: CID.parse(result.subjectCid),
|
|
100
|
-
blobCids: subjectBlobCids?.map((cid) => CID.parse(cid)) ?? [],
|
|
101
|
-
})
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
await labelTxn.formatAndCreate(
|
|
105
|
-
ctx.cfg.labelerDid,
|
|
106
|
-
result.subjectUri ?? result.subjectDid,
|
|
107
|
-
result.subjectCid,
|
|
108
|
-
{ create: createLabelVals, negate: negateLabelVals },
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
return { result, takenDown }
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
if (takenDown && ctx.moderationPushAgent) {
|
|
115
|
-
const agent = ctx.moderationPushAgent
|
|
116
|
-
const { did, subjects } = takenDown
|
|
117
|
-
if (did && subjects.length > 0) {
|
|
118
|
-
const results = await Promise.allSettled(
|
|
119
|
-
subjects.map((subject) =>
|
|
120
|
-
retryHttp(() =>
|
|
121
|
-
agent.api.com.atproto.admin.updateSubjectStatus({
|
|
122
|
-
subject,
|
|
123
|
-
takedown: {
|
|
124
|
-
applied: true,
|
|
125
|
-
ref: result.id.toString(),
|
|
126
|
-
},
|
|
127
|
-
}),
|
|
128
|
-
),
|
|
129
|
-
),
|
|
130
|
-
)
|
|
131
|
-
const hadFailure = results.some((r) => r.status === 'rejected')
|
|
132
|
-
if (hadFailure) {
|
|
133
|
-
throw new UpstreamFailureError('failed to apply action on PDS')
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return {
|
|
139
|
-
encoding: 'application/json',
|
|
140
|
-
body: await moderationService.views.action(result),
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
})
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const validateLabels = (labels: string[]) => {
|
|
147
|
-
for (const label of labels) {
|
|
148
|
-
for (const char of badChars) {
|
|
149
|
-
if (label.includes(char)) {
|
|
150
|
-
throw new InvalidRequestError(`Invalid label: ${label}`)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const badChars = [' ', ',', ';', `'`, `"`]
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
2
|
-
import { QueryParams as SkeletonParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton'
|
|
3
|
-
import { AlgoHandler, AlgoResponse } from './types'
|
|
4
|
-
import { GenericKeyset, paginate } from '../db/pagination'
|
|
5
|
-
import AppContext from '../context'
|
|
6
|
-
|
|
7
|
-
const handler: AlgoHandler = async (
|
|
8
|
-
ctx: AppContext,
|
|
9
|
-
params: SkeletonParams,
|
|
10
|
-
viewer: string,
|
|
11
|
-
): Promise<AlgoResponse> => {
|
|
12
|
-
const { limit, cursor } = params
|
|
13
|
-
const db = ctx.db.getReplica('feed')
|
|
14
|
-
const feedService = ctx.services.feed(db)
|
|
15
|
-
|
|
16
|
-
const { ref } = db.db.dynamic
|
|
17
|
-
|
|
18
|
-
// candidates are ranked within a materialized view by like count, depreciated over time.
|
|
19
|
-
|
|
20
|
-
let builder = feedService
|
|
21
|
-
.selectPostQb()
|
|
22
|
-
.innerJoin('algo_whats_hot_view as candidate', 'candidate.uri', 'post.uri')
|
|
23
|
-
.where((qb) =>
|
|
24
|
-
qb
|
|
25
|
-
.where('post.creator', '=', viewer)
|
|
26
|
-
.orWhereExists((inner) =>
|
|
27
|
-
inner
|
|
28
|
-
.selectFrom('follow')
|
|
29
|
-
.where('follow.creator', '=', viewer)
|
|
30
|
-
.whereRef('follow.subjectDid', '=', 'post.creator'),
|
|
31
|
-
),
|
|
32
|
-
)
|
|
33
|
-
.select('candidate.score')
|
|
34
|
-
.select('candidate.cid')
|
|
35
|
-
|
|
36
|
-
const keyset = new ScoreKeyset(ref('candidate.score'), ref('candidate.cid'))
|
|
37
|
-
builder = paginate(builder, { limit, cursor, keyset })
|
|
38
|
-
|
|
39
|
-
const feedItems = await builder.execute()
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
feedItems,
|
|
43
|
-
cursor: keyset.packFromResult(feedItems),
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export default handler
|
|
48
|
-
|
|
49
|
-
type Result = { score: number; cid: string }
|
|
50
|
-
type LabeledResult = { primary: number; secondary: string }
|
|
51
|
-
export class ScoreKeyset extends GenericKeyset<Result, LabeledResult> {
|
|
52
|
-
labelResult(result: Result) {
|
|
53
|
-
return {
|
|
54
|
-
primary: result.score,
|
|
55
|
-
secondary: result.cid,
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
labeledResultToCursor(labeled: LabeledResult) {
|
|
59
|
-
return {
|
|
60
|
-
primary: Math.round(labeled.primary).toString(),
|
|
61
|
-
secondary: labeled.secondary,
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
cursorToLabeledResult(cursor: { primary: string; secondary: string }) {
|
|
65
|
-
const score = parseInt(cursor.primary, 10)
|
|
66
|
-
if (isNaN(score)) {
|
|
67
|
-
throw new InvalidRequestError('Malformed cursor')
|
|
68
|
-
}
|
|
69
|
-
return {
|
|
70
|
-
primary: score,
|
|
71
|
-
secondary: cursor.secondary,
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|