@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.
Files changed (236) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/api/com/atproto/moderation/util.d.ts +4 -3
  3. package/dist/cache/read-through.d.ts +30 -0
  4. package/dist/config.d.ts +18 -0
  5. package/dist/context.d.ts +21 -6
  6. package/dist/daemon/config.d.ts +15 -0
  7. package/dist/daemon/context.d.ts +15 -0
  8. package/dist/daemon/index.d.ts +23 -0
  9. package/dist/daemon/logger.d.ts +3 -0
  10. package/dist/daemon/notifications.d.ts +18 -0
  11. package/dist/daemon/services.d.ts +11 -0
  12. package/dist/db/database-schema.d.ts +1 -2
  13. package/dist/db/index.js +41 -1
  14. package/dist/db/index.js.map +3 -3
  15. package/dist/db/migrations/20231003T202833377Z-create-moderation-subject-status.d.ts +3 -0
  16. package/dist/db/migrations/20231205T000257238Z-remove-did-cache.d.ts +3 -0
  17. package/dist/db/migrations/index.d.ts +2 -0
  18. package/dist/db/pagination.d.ts +2 -1
  19. package/dist/db/{periodic-moderation-action-reversal.d.ts → periodic-moderation-event-reversal.d.ts} +3 -5
  20. package/dist/db/tables/moderation.d.ts +24 -34
  21. package/dist/did-cache.d.ts +10 -7
  22. package/dist/feed-gen/types.d.ts +1 -1
  23. package/dist/index.d.ts +6 -1
  24. package/dist/index.js +4370 -2758
  25. package/dist/index.js.map +3 -3
  26. package/dist/indexer/context.d.ts +2 -0
  27. package/dist/indexer/index.d.ts +1 -0
  28. package/dist/lexicon/index.d.ts +23 -18
  29. package/dist/lexicon/lexicons.d.ts +561 -412
  30. package/dist/lexicon/types/app/bsky/feed/defs.d.ts +1 -7
  31. package/dist/lexicon/types/app/bsky/graph/defs.d.ts +1 -0
  32. package/dist/lexicon/types/com/atproto/admin/defs.d.ts +114 -48
  33. package/dist/lexicon/types/com/atproto/admin/{resolveModerationReports.d.ts → deleteAccount.d.ts} +2 -13
  34. package/dist/lexicon/types/com/atproto/admin/{takeModerationAction.d.ts → emitModerationEvent.d.ts} +5 -6
  35. package/dist/lexicon/types/com/atproto/admin/{getModerationAction.d.ts → getModerationEvent.d.ts} +1 -1
  36. package/dist/lexicon/types/com/atproto/admin/{getModerationActions.d.ts → queryModerationEvents.d.ts} +5 -1
  37. package/dist/lexicon/types/com/atproto/admin/{getModerationReports.d.ts → queryModerationStatuses.d.ts} +12 -6
  38. package/dist/lexicon/types/com/atproto/admin/sendEmail.d.ts +1 -0
  39. package/dist/lexicon/types/com/atproto/{admin/getModerationReport.d.ts → temp/importRepo.d.ts} +10 -7
  40. package/dist/lexicon/types/com/atproto/temp/pushBlob.d.ts +25 -0
  41. package/dist/lexicon/types/com/atproto/{admin/reverseModerationAction.d.ts → temp/transferAccount.d.ts} +11 -5
  42. package/dist/logger.d.ts +1 -0
  43. package/dist/migrate-moderation-data.d.ts +1 -0
  44. package/dist/redis.d.ts +10 -1
  45. package/dist/services/actor/index.d.ts +18 -4
  46. package/dist/services/actor/views.d.ts +6 -8
  47. package/dist/services/feed/index.d.ts +7 -4
  48. package/dist/services/feed/util.d.ts +9 -1
  49. package/dist/services/feed/views.d.ts +11 -21
  50. package/dist/services/graph/index.d.ts +5 -29
  51. package/dist/services/graph/types.d.ts +1 -0
  52. package/dist/services/index.d.ts +3 -7
  53. package/dist/services/label/index.d.ts +10 -4
  54. package/dist/services/moderation/index.d.ts +134 -72
  55. package/dist/services/moderation/pagination.d.ts +36 -0
  56. package/dist/services/moderation/status.d.ts +13 -0
  57. package/dist/services/moderation/types.d.ts +35 -0
  58. package/dist/services/moderation/views.d.ts +18 -14
  59. package/dist/services/types.d.ts +3 -0
  60. package/dist/services/util/notification.d.ts +5 -0
  61. package/dist/services/util/post.d.ts +6 -6
  62. package/dist/util/debug.d.ts +1 -1
  63. package/dist/util/retry.d.ts +1 -6
  64. package/package.json +11 -11
  65. package/src/api/app/bsky/feed/getActorFeeds.ts +2 -1
  66. package/src/api/app/bsky/feed/getActorLikes.ts +1 -3
  67. package/src/api/app/bsky/feed/getAuthorFeed.ts +1 -3
  68. package/src/api/app/bsky/feed/getFeed.ts +9 -9
  69. package/src/api/app/bsky/feed/getFeedGenerator.ts +3 -0
  70. package/src/api/app/bsky/feed/getFeedGenerators.ts +2 -1
  71. package/src/api/app/bsky/feed/getListFeed.ts +1 -3
  72. package/src/api/app/bsky/feed/getPostThread.ts +15 -54
  73. package/src/api/app/bsky/feed/getPosts.ts +21 -18
  74. package/src/api/app/bsky/feed/getSuggestedFeeds.ts +2 -1
  75. package/src/api/app/bsky/feed/getTimeline.ts +1 -3
  76. package/src/api/app/bsky/feed/searchPosts.ts +20 -17
  77. package/src/api/app/bsky/graph/getList.ts +6 -3
  78. package/src/api/app/bsky/graph/getListBlocks.ts +3 -2
  79. package/src/api/app/bsky/graph/getListMutes.ts +2 -1
  80. package/src/api/app/bsky/graph/getLists.ts +2 -1
  81. package/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts +3 -1
  82. package/src/api/blob-resolver.ts +6 -11
  83. package/src/api/com/atproto/admin/emitModerationEvent.ts +220 -0
  84. package/src/api/com/atproto/admin/{getModerationActions.ts → getModerationEvent.ts} +5 -11
  85. package/src/api/com/atproto/admin/getRecord.ts +1 -0
  86. package/src/api/com/atproto/admin/{getModerationReports.ts → queryModerationEvents.ts} +13 -16
  87. package/src/api/com/atproto/admin/queryModerationStatuses.ts +55 -0
  88. package/src/api/com/atproto/moderation/createReport.ts +9 -7
  89. package/src/api/com/atproto/moderation/util.ts +38 -20
  90. package/src/api/index.ts +8 -14
  91. package/src/auth.ts +29 -21
  92. package/src/auto-moderator/index.ts +26 -19
  93. package/src/cache/read-through.ts +151 -0
  94. package/src/config.ts +90 -1
  95. package/src/context.ts +11 -7
  96. package/src/daemon/config.ts +60 -0
  97. package/src/daemon/context.ts +27 -0
  98. package/src/daemon/index.ts +78 -0
  99. package/src/daemon/logger.ts +6 -0
  100. package/src/daemon/notifications.ts +54 -0
  101. package/src/daemon/services.ts +22 -0
  102. package/src/db/database-schema.ts +0 -2
  103. package/src/db/migrations/20231003T202833377Z-create-moderation-subject-status.ts +123 -0
  104. package/src/db/migrations/20231205T000257238Z-remove-did-cache.ts +14 -0
  105. package/src/db/migrations/index.ts +2 -0
  106. package/src/db/pagination.ts +26 -3
  107. package/src/db/{periodic-moderation-action-reversal.ts → periodic-moderation-event-reversal.ts} +50 -46
  108. package/src/db/tables/moderation.ts +35 -52
  109. package/src/did-cache.ts +33 -56
  110. package/src/feed-gen/bsky-team.ts +1 -1
  111. package/src/feed-gen/hot-classic.ts +1 -1
  112. package/src/feed-gen/index.ts +0 -4
  113. package/src/feed-gen/mutuals.ts +6 -2
  114. package/src/feed-gen/types.ts +1 -1
  115. package/src/index.ts +57 -17
  116. package/src/indexer/context.ts +5 -0
  117. package/src/indexer/index.ts +10 -7
  118. package/src/lexicon/index.ts +80 -67
  119. package/src/lexicon/lexicons.ts +698 -507
  120. package/src/lexicon/types/app/bsky/feed/defs.ts +1 -18
  121. package/src/lexicon/types/app/bsky/graph/defs.ts +1 -0
  122. package/src/lexicon/types/com/atproto/admin/defs.ts +276 -84
  123. package/src/lexicon/types/com/atproto/admin/{resolveModerationReports.ts → deleteAccount.ts} +2 -13
  124. package/src/lexicon/types/com/atproto/admin/{takeModerationAction.ts → emitModerationEvent.ts} +13 -11
  125. package/src/lexicon/types/com/atproto/admin/{getModerationReport.ts → getModerationEvent.ts} +1 -1
  126. package/src/lexicon/types/com/atproto/admin/{getModerationActions.ts → queryModerationEvents.ts} +8 -1
  127. package/src/lexicon/types/com/atproto/admin/{getModerationReports.ts → queryModerationStatuses.ts} +21 -14
  128. package/src/lexicon/types/com/atproto/admin/sendEmail.ts +1 -0
  129. package/src/lexicon/types/com/atproto/{admin/getModerationAction.ts → temp/importRepo.ts} +11 -7
  130. package/src/lexicon/types/com/atproto/temp/pushBlob.ts +39 -0
  131. package/src/lexicon/types/com/atproto/{admin/reverseModerationAction.ts → temp/transferAccount.ts} +18 -5
  132. package/src/logger.ts +2 -0
  133. package/src/migrate-moderation-data.ts +414 -0
  134. package/src/redis.ts +43 -3
  135. package/src/services/actor/index.ts +55 -7
  136. package/src/services/actor/views.ts +18 -21
  137. package/src/services/feed/index.ts +52 -19
  138. package/src/services/feed/util.ts +47 -19
  139. package/src/services/feed/views.ts +87 -13
  140. package/src/services/graph/index.ts +21 -3
  141. package/src/services/graph/types.ts +1 -0
  142. package/src/services/index.ts +14 -14
  143. package/src/services/indexing/index.ts +7 -10
  144. package/src/services/indexing/plugins/block.ts +2 -3
  145. package/src/services/indexing/plugins/feed-generator.ts +2 -3
  146. package/src/services/indexing/plugins/follow.ts +2 -3
  147. package/src/services/indexing/plugins/like.ts +2 -3
  148. package/src/services/indexing/plugins/list-block.ts +2 -3
  149. package/src/services/indexing/plugins/list-item.ts +2 -3
  150. package/src/services/indexing/plugins/list.ts +2 -3
  151. package/src/services/indexing/plugins/post.ts +16 -4
  152. package/src/services/indexing/plugins/repost.ts +2 -3
  153. package/src/services/indexing/plugins/thread-gate.ts +2 -3
  154. package/src/services/label/index.ts +68 -25
  155. package/src/services/moderation/index.ts +380 -395
  156. package/src/services/moderation/pagination.ts +96 -0
  157. package/src/services/moderation/status.ts +241 -0
  158. package/src/services/moderation/types.ts +49 -0
  159. package/src/services/moderation/views.ts +278 -329
  160. package/src/services/types.ts +4 -0
  161. package/src/services/util/notification.ts +70 -0
  162. package/src/util/debug.ts +2 -2
  163. package/src/util/retry.ts +1 -44
  164. package/tests/__snapshots__/feed-generation.test.ts.snap +322 -6
  165. package/tests/__snapshots__/indexing.test.ts.snap +0 -6
  166. package/tests/admin/__snapshots__/get-record.test.ts.snap +30 -132
  167. package/tests/admin/__snapshots__/get-repo.test.ts.snap +14 -60
  168. package/tests/admin/__snapshots__/moderation-events.test.ts.snap +146 -0
  169. package/tests/admin/__snapshots__/moderation-statuses.test.ts.snap +64 -0
  170. package/tests/admin/__snapshots__/moderation.test.ts.snap +0 -125
  171. package/tests/admin/get-record.test.ts +5 -9
  172. package/tests/admin/get-repo.test.ts +10 -12
  173. package/tests/admin/moderation-events.test.ts +221 -0
  174. package/tests/admin/moderation-statuses.test.ts +145 -0
  175. package/tests/admin/moderation.test.ts +512 -860
  176. package/tests/admin/repo-search.test.ts +3 -3
  177. package/tests/algos/hot-classic.test.ts +1 -2
  178. package/tests/auth.test.ts +1 -1
  179. package/tests/auto-moderator/fuzzy-matcher.test.ts +2 -1
  180. package/tests/auto-moderator/labeler.test.ts +19 -20
  181. package/tests/auto-moderator/takedowns.test.ts +61 -28
  182. package/tests/blob-resolver.test.ts +4 -2
  183. package/tests/daemon.test.ts +191 -0
  184. package/tests/did-cache.test.ts +20 -5
  185. package/tests/feed-generation.test.ts +57 -9
  186. package/tests/handle-invalidation.test.ts +1 -5
  187. package/tests/indexing.test.ts +20 -13
  188. package/tests/redis-cache.test.ts +231 -0
  189. package/tests/seeds/basic.ts +3 -0
  190. package/tests/subscription/repo.test.ts +4 -7
  191. package/tests/views/__snapshots__/block-lists.test.ts.snap +3 -9
  192. package/tests/views/__snapshots__/blocks.test.ts.snap +0 -9
  193. package/tests/views/__snapshots__/mute-lists.test.ts.snap +5 -5
  194. package/tests/views/__snapshots__/mutes.test.ts.snap +0 -3
  195. package/tests/views/__snapshots__/thread.test.ts.snap +0 -30
  196. package/tests/views/actor-search.test.ts +2 -3
  197. package/tests/views/author-feed.test.ts +42 -36
  198. package/tests/views/follows.test.ts +40 -35
  199. package/tests/views/list-feed.test.ts +17 -9
  200. package/tests/views/notifications.test.ts +13 -9
  201. package/tests/views/profile.test.ts +20 -19
  202. package/tests/views/thread.test.ts +117 -94
  203. package/tests/views/threadgating.test.ts +89 -19
  204. package/tests/views/timeline.test.ts +21 -13
  205. package/dist/api/com/atproto/admin/resolveModerationReports.d.ts +0 -3
  206. package/dist/api/com/atproto/admin/reverseModerationAction.d.ts +0 -3
  207. package/dist/api/com/atproto/admin/takeModerationAction.d.ts +0 -3
  208. package/dist/db/tables/did-cache.d.ts +0 -10
  209. package/dist/feed-gen/best-of-follows.d.ts +0 -29
  210. package/dist/feed-gen/whats-hot.d.ts +0 -29
  211. package/dist/feed-gen/with-friends.d.ts +0 -3
  212. package/dist/label-cache.d.ts +0 -19
  213. package/src/api/com/atproto/admin/getModerationAction.ts +0 -44
  214. package/src/api/com/atproto/admin/getModerationReport.ts +0 -43
  215. package/src/api/com/atproto/admin/resolveModerationReports.ts +0 -24
  216. package/src/api/com/atproto/admin/reverseModerationAction.ts +0 -115
  217. package/src/api/com/atproto/admin/takeModerationAction.ts +0 -156
  218. package/src/db/tables/did-cache.ts +0 -13
  219. package/src/feed-gen/best-of-follows.ts +0 -74
  220. package/src/feed-gen/whats-hot.ts +0 -101
  221. package/src/feed-gen/with-friends.ts +0 -39
  222. package/src/label-cache.ts +0 -90
  223. package/tests/admin/__snapshots__/get-moderation-action.test.ts.snap +0 -172
  224. package/tests/admin/__snapshots__/get-moderation-actions.test.ts.snap +0 -178
  225. package/tests/admin/__snapshots__/get-moderation-report.test.ts.snap +0 -177
  226. package/tests/admin/__snapshots__/get-moderation-reports.test.ts.snap +0 -307
  227. package/tests/admin/get-moderation-action.test.ts +0 -100
  228. package/tests/admin/get-moderation-actions.test.ts +0 -164
  229. package/tests/admin/get-moderation-report.test.ts +0 -100
  230. package/tests/admin/get-moderation-reports.test.ts +0 -332
  231. package/tests/algos/whats-hot.test.ts +0 -118
  232. package/tests/algos/with-friends.test.ts +0 -145
  233. /package/dist/api/com/atproto/admin/{getModerationAction.d.ts → emitModerationEvent.d.ts} +0 -0
  234. /package/dist/api/com/atproto/admin/{getModerationActions.d.ts → getModerationEvent.d.ts} +0 -0
  235. /package/dist/api/com/atproto/admin/{getModerationReport.d.ts → queryModerationEvents.d.ts} +0 -0
  236. /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
- const actionResults = await Promise.all(
184
+ await Promise.all(
186
185
  [bob, carol].map((did) =>
187
- agent.api.com.atproto.admin.takeModerationAction(
186
+ agent.api.com.atproto.admin.emitModerationEvent(
188
187
  {
189
- action: TAKEDOWN,
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
- actionResults.map((result) =>
215
- agent.api.com.atproto.admin.reverseModerationAction(
213
+ [bob, carol].map((did) =>
214
+ agent.api.com.atproto.admin.emitModerationEvent(
216
215
  {
217
- id: result.data.id,
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
- const actionResults = await Promise.all(
236
+ await Promise.all(
234
237
  [postRef1, postRef2].map((postRef) =>
235
- agent.api.com.atproto.admin.takeModerationAction(
238
+ agent.api.com.atproto.admin.emitModerationEvent(
236
239
  {
237
- action: TAKEDOWN,
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
- actionResults.map((result) =>
264
- agent.api.com.atproto.admin.reverseModerationAction(
266
+ [postRef1, postRef2].map((postRef) =>
267
+ agent.api.com.atproto.admin.emitModerationEvent(
265
268
  {
266
- id: result.data.id,
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,3 +0,0 @@
1
- import { Server } from '../../../../lexicon';
2
- import AppContext from '../../../../context';
3
- export default function (server: Server, ctx: AppContext): void;
@@ -1,3 +0,0 @@
1
- import { Server } from '../../../../lexicon';
2
- import AppContext from '../../../../context';
3
- export default function (server: Server, ctx: AppContext): void;
@@ -1,3 +0,0 @@
1
- import { Server } from '../../../../lexicon';
2
- import AppContext from '../../../../context';
3
- export default function (server: Server, ctx: AppContext): void;
@@ -1,10 +0,0 @@
1
- import { DidDocument } from '@atproto/identity';
2
- export interface DidCache {
3
- did: string;
4
- doc: DidDocument;
5
- updatedAt: number;
6
- }
7
- export declare const tableName = "did_cache";
8
- export declare type PartialDB = {
9
- [tableName]: DidCache;
10
- };
@@ -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
- }
@@ -1,3 +0,0 @@
1
- import { AlgoHandler } from './types';
2
- declare const handler: AlgoHandler;
3
- export default handler;
@@ -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,13 +0,0 @@
1
- import { DidDocument } from '@atproto/identity'
2
-
3
- export interface DidCache {
4
- did: string
5
- doc: DidDocument
6
- updatedAt: number
7
- }
8
-
9
- export const tableName = 'did_cache'
10
-
11
- export type PartialDB = {
12
- [tableName]: DidCache
13
- }
@@ -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
- }