@atproto/ozone 0.0.2

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 (444) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE.txt +7 -0
  3. package/README.md +15 -0
  4. package/babel.config.js +3 -0
  5. package/bin/migration-create.ts +38 -0
  6. package/build.js +18 -0
  7. package/dist/api/admin/emitModerationEvent.d.ts +3 -0
  8. package/dist/api/admin/getModerationEvent.d.ts +3 -0
  9. package/dist/api/admin/getRecord.d.ts +3 -0
  10. package/dist/api/admin/getRepo.d.ts +3 -0
  11. package/dist/api/admin/queryModerationEvents.d.ts +3 -0
  12. package/dist/api/admin/queryModerationStatuses.d.ts +3 -0
  13. package/dist/api/admin/searchRepos.d.ts +3 -0
  14. package/dist/api/admin/util.d.ts +5 -0
  15. package/dist/api/com/atproto/admin/emitModerationEvent.d.ts +3 -0
  16. package/dist/api/com/atproto/admin/getModerationEvent.d.ts +3 -0
  17. package/dist/api/com/atproto/admin/getRecord.d.ts +3 -0
  18. package/dist/api/com/atproto/admin/getRepo.d.ts +3 -0
  19. package/dist/api/com/atproto/admin/queryModerationEvents.d.ts +3 -0
  20. package/dist/api/com/atproto/admin/queryModerationStatuses.d.ts +3 -0
  21. package/dist/api/com/atproto/admin/searchRepos.d.ts +3 -0
  22. package/dist/api/com/atproto/admin/util.d.ts +5 -0
  23. package/dist/api/com/atproto/moderation/createReport.d.ts +3 -0
  24. package/dist/api/com/atproto/moderation/util.d.ts +4 -0
  25. package/dist/api/com/atproto/temp/fetchLabels.d.ts +3 -0
  26. package/dist/api/health.d.ts +3 -0
  27. package/dist/api/index.d.ts +5 -0
  28. package/dist/api/label/queryLabels.d.ts +3 -0
  29. package/dist/api/label/subscribeLabels.d.ts +3 -0
  30. package/dist/api/moderation/createReport.d.ts +3 -0
  31. package/dist/api/moderation/util.d.ts +4 -0
  32. package/dist/api/temp/fetchLabels.d.ts +3 -0
  33. package/dist/api/util.d.ts +2 -0
  34. package/dist/api/well-known.d.ts +3 -0
  35. package/dist/auth-verifier.d.ts +47 -0
  36. package/dist/auth.d.ts +81 -0
  37. package/dist/background.d.ts +13 -0
  38. package/dist/config/config.d.ts +30 -0
  39. package/dist/config/env.d.ts +19 -0
  40. package/dist/config/index.d.ts +3 -0
  41. package/dist/config/secrets.d.ts +8 -0
  42. package/dist/config.d.ts +42 -0
  43. package/dist/context.d.ts +140 -0
  44. package/dist/daemon/config.d.ts +23 -0
  45. package/dist/daemon/context.d.ts +25 -0
  46. package/dist/daemon/event-pusher.d.ts +48 -0
  47. package/dist/daemon/event-reverser.d.ts +16 -0
  48. package/dist/daemon/index.d.ts +13 -0
  49. package/dist/db/index.d.ts +32 -0
  50. package/dist/db/index.js +33707 -0
  51. package/dist/db/index.js.map +7 -0
  52. package/dist/db/migrations/20231219T205730722Z-init.d.ts +3 -0
  53. package/dist/db/migrations/index.d.ts +1 -0
  54. package/dist/db/migrations/provider.d.ts +11 -0
  55. package/dist/db/pagination.d.ts +66 -0
  56. package/dist/db/schema/blob_push_event.d.ts +17 -0
  57. package/dist/db/schema/index.d.ts +10 -0
  58. package/dist/db/schema/label.d.ts +12 -0
  59. package/dist/db/schema/moderation_event.d.ts +22 -0
  60. package/dist/db/schema/moderation_subject_status.d.ts +25 -0
  61. package/dist/db/schema/record_push_event.d.ts +17 -0
  62. package/dist/db/schema/repo_push_event.d.ts +15 -0
  63. package/dist/db/types.d.ts +12 -0
  64. package/dist/error.d.ts +2 -0
  65. package/dist/index.d.ts +27 -0
  66. package/dist/index.js +131818 -0
  67. package/dist/index.js.map +7 -0
  68. package/dist/lexicon/index.d.ts +372 -0
  69. package/dist/lexicon/lexicons.d.ts +7553 -0
  70. package/dist/lexicon/types/app/bsky/actor/defs.d.ts +104 -0
  71. package/dist/lexicon/types/app/bsky/actor/getPreferences.d.ts +31 -0
  72. package/dist/lexicon/types/app/bsky/actor/getProfile.d.ts +29 -0
  73. package/dist/lexicon/types/app/bsky/actor/getProfiles.d.ts +32 -0
  74. package/dist/lexicon/types/app/bsky/actor/getSuggestions.d.ts +34 -0
  75. package/dist/lexicon/types/app/bsky/actor/profile.d.ts +15 -0
  76. package/dist/lexicon/types/app/bsky/actor/putPreferences.d.ts +26 -0
  77. package/dist/lexicon/types/app/bsky/actor/searchActors.d.ts +36 -0
  78. package/dist/lexicon/types/app/bsky/actor/searchActorsTypeahead.d.ts +34 -0
  79. package/dist/lexicon/types/app/bsky/embed/external.d.ts +31 -0
  80. package/dist/lexicon/types/app/bsky/embed/images.d.ts +37 -0
  81. package/dist/lexicon/types/app/bsky/embed/record.d.ts +54 -0
  82. package/dist/lexicon/types/app/bsky/embed/recordWithMedia.d.ts +24 -0
  83. package/dist/lexicon/types/app/bsky/feed/defs.d.ts +151 -0
  84. package/dist/lexicon/types/app/bsky/feed/describeFeedGenerator.d.ts +46 -0
  85. package/dist/lexicon/types/app/bsky/feed/generator.d.ts +18 -0
  86. package/dist/lexicon/types/app/bsky/feed/getActorFeeds.d.ts +35 -0
  87. package/dist/lexicon/types/app/bsky/feed/getActorLikes.d.ts +36 -0
  88. package/dist/lexicon/types/app/bsky/feed/getAuthorFeed.d.ts +37 -0
  89. package/dist/lexicon/types/app/bsky/feed/getFeed.d.ts +36 -0
  90. package/dist/lexicon/types/app/bsky/feed/getFeedGenerator.d.ts +34 -0
  91. package/dist/lexicon/types/app/bsky/feed/getFeedGenerators.d.ts +32 -0
  92. package/dist/lexicon/types/app/bsky/feed/getFeedSkeleton.d.ts +36 -0
  93. package/dist/lexicon/types/app/bsky/feed/getLikes.d.ts +47 -0
  94. package/dist/lexicon/types/app/bsky/feed/getListFeed.d.ts +36 -0
  95. package/dist/lexicon/types/app/bsky/feed/getPostThread.d.ts +38 -0
  96. package/dist/lexicon/types/app/bsky/feed/getPosts.d.ts +32 -0
  97. package/dist/lexicon/types/app/bsky/feed/getRepostedBy.d.ts +38 -0
  98. package/dist/lexicon/types/app/bsky/feed/getSuggestedFeeds.d.ts +34 -0
  99. package/dist/lexicon/types/app/bsky/feed/getTimeline.d.ts +35 -0
  100. package/dist/lexicon/types/app/bsky/feed/like.d.ts +9 -0
  101. package/dist/lexicon/types/app/bsky/feed/post.d.ts +50 -0
  102. package/dist/lexicon/types/app/bsky/feed/repost.d.ts +9 -0
  103. package/dist/lexicon/types/app/bsky/feed/searchPosts.d.ts +37 -0
  104. package/dist/lexicon/types/app/bsky/feed/threadgate.d.ts +28 -0
  105. package/dist/lexicon/types/app/bsky/graph/block.d.ts +8 -0
  106. package/dist/lexicon/types/app/bsky/graph/defs.d.ts +47 -0
  107. package/dist/lexicon/types/app/bsky/graph/follow.d.ts +8 -0
  108. package/dist/lexicon/types/app/bsky/graph/getBlocks.d.ts +34 -0
  109. package/dist/lexicon/types/app/bsky/graph/getFollowers.d.ts +36 -0
  110. package/dist/lexicon/types/app/bsky/graph/getFollows.d.ts +36 -0
  111. package/dist/lexicon/types/app/bsky/graph/getList.d.ts +36 -0
  112. package/dist/lexicon/types/app/bsky/graph/getListBlocks.d.ts +34 -0
  113. package/dist/lexicon/types/app/bsky/graph/getListMutes.d.ts +34 -0
  114. package/dist/lexicon/types/app/bsky/graph/getLists.d.ts +35 -0
  115. package/dist/lexicon/types/app/bsky/graph/getMutes.d.ts +34 -0
  116. package/dist/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.d.ts +32 -0
  117. package/dist/lexicon/types/app/bsky/graph/list.d.ts +19 -0
  118. package/dist/lexicon/types/app/bsky/graph/listblock.d.ts +8 -0
  119. package/dist/lexicon/types/app/bsky/graph/listitem.d.ts +9 -0
  120. package/dist/lexicon/types/app/bsky/graph/muteActor.d.ts +25 -0
  121. package/dist/lexicon/types/app/bsky/graph/muteActorList.d.ts +25 -0
  122. package/dist/lexicon/types/app/bsky/graph/unmuteActor.d.ts +25 -0
  123. package/dist/lexicon/types/app/bsky/graph/unmuteActorList.d.ts +25 -0
  124. package/dist/lexicon/types/app/bsky/moderation/defs.d.ts +49 -0
  125. package/dist/lexicon/types/app/bsky/moderation/getService.d.ts +29 -0
  126. package/dist/lexicon/types/app/bsky/moderation/getServices.d.ts +32 -0
  127. package/dist/lexicon/types/app/bsky/moderation/service.d.ts +17 -0
  128. package/dist/lexicon/types/app/bsky/notification/getUnreadCount.d.ts +31 -0
  129. package/dist/lexicon/types/app/bsky/notification/listNotifications.d.ts +52 -0
  130. package/dist/lexicon/types/app/bsky/notification/registerPush.d.ts +28 -0
  131. package/dist/lexicon/types/app/bsky/notification/updateSeen.d.ts +25 -0
  132. package/dist/lexicon/types/app/bsky/richtext/facet.d.ts +36 -0
  133. package/dist/lexicon/types/app/bsky/unspecced/defs.d.ts +13 -0
  134. package/dist/lexicon/types/app/bsky/unspecced/getPopular.d.ts +35 -0
  135. package/dist/lexicon/types/app/bsky/unspecced/getPopularFeedGenerators.d.ts +35 -0
  136. package/dist/lexicon/types/app/bsky/unspecced/getTimelineSkeleton.d.ts +35 -0
  137. package/dist/lexicon/types/app/bsky/unspecced/searchActorsSkeleton.d.ts +38 -0
  138. package/dist/lexicon/types/app/bsky/unspecced/searchPostsSkeleton.d.ts +37 -0
  139. package/dist/lexicon/types/com/atproto/admin/defs.d.ts +319 -0
  140. package/dist/lexicon/types/com/atproto/admin/deleteAccount.d.ts +25 -0
  141. package/dist/lexicon/types/com/atproto/admin/disableAccountInvites.d.ts +26 -0
  142. package/dist/lexicon/types/com/atproto/admin/disableInviteCodes.d.ts +26 -0
  143. package/dist/lexicon/types/com/atproto/admin/emitModerationEvent.d.ts +45 -0
  144. package/dist/lexicon/types/com/atproto/admin/enableAccountInvites.d.ts +26 -0
  145. package/dist/lexicon/types/com/atproto/admin/getAccountInfo.d.ts +29 -0
  146. package/dist/lexicon/types/com/atproto/admin/getAccountInfos.d.ts +32 -0
  147. package/dist/lexicon/types/com/atproto/admin/getInviteCodes.d.ts +35 -0
  148. package/dist/lexicon/types/com/atproto/admin/getModerationEvent.d.ts +29 -0
  149. package/dist/lexicon/types/com/atproto/admin/getRecord.d.ts +31 -0
  150. package/dist/lexicon/types/com/atproto/admin/getRepo.d.ts +30 -0
  151. package/dist/lexicon/types/com/atproto/admin/getSubjectStatus.d.ts +39 -0
  152. package/dist/lexicon/types/com/atproto/admin/queryModerationEvents.d.ts +39 -0
  153. package/dist/lexicon/types/com/atproto/admin/queryModerationStatuses.d.ts +48 -0
  154. package/dist/lexicon/types/com/atproto/admin/searchRepos.d.ts +36 -0
  155. package/dist/lexicon/types/com/atproto/admin/sendEmail.d.ts +40 -0
  156. package/dist/lexicon/types/com/atproto/admin/updateAccountEmail.d.ts +26 -0
  157. package/dist/lexicon/types/com/atproto/admin/updateAccountHandle.d.ts +26 -0
  158. package/dist/lexicon/types/com/atproto/admin/updateSubjectStatus.d.ts +46 -0
  159. package/dist/lexicon/types/com/atproto/identity/resolveHandle.d.ts +31 -0
  160. package/dist/lexicon/types/com/atproto/identity/updateHandle.d.ts +25 -0
  161. package/dist/lexicon/types/com/atproto/label/defs.d.ts +24 -0
  162. package/dist/lexicon/types/com/atproto/label/queryLabels.d.ts +36 -0
  163. package/dist/lexicon/types/com/atproto/label/subscribeLabels.d.ts +36 -0
  164. package/dist/lexicon/types/com/atproto/moderation/createReport.d.ts +52 -0
  165. package/dist/lexicon/types/com/atproto/moderation/defs.d.ts +8 -0
  166. package/dist/lexicon/types/com/atproto/repo/applyWrites.d.ts +53 -0
  167. package/dist/lexicon/types/com/atproto/repo/createRecord.d.ts +43 -0
  168. package/dist/lexicon/types/com/atproto/repo/deleteRecord.d.ts +30 -0
  169. package/dist/lexicon/types/com/atproto/repo/describeRepo.d.ts +35 -0
  170. package/dist/lexicon/types/com/atproto/repo/getRecord.d.ts +36 -0
  171. package/dist/lexicon/types/com/atproto/repo/listRecords.d.ts +47 -0
  172. package/dist/lexicon/types/com/atproto/repo/putRecord.d.ts +44 -0
  173. package/dist/lexicon/types/com/atproto/repo/strongRef.d.ts +8 -0
  174. package/dist/lexicon/types/com/atproto/repo/uploadBlob.d.ts +36 -0
  175. package/dist/lexicon/types/com/atproto/server/confirmEmail.d.ts +27 -0
  176. package/dist/lexicon/types/com/atproto/server/createAccount.d.ts +49 -0
  177. package/dist/lexicon/types/com/atproto/server/createAppPassword.d.ts +43 -0
  178. package/dist/lexicon/types/com/atproto/server/createInviteCode.d.ts +37 -0
  179. package/dist/lexicon/types/com/atproto/server/createInviteCodes.d.ts +46 -0
  180. package/dist/lexicon/types/com/atproto/server/createSession.d.ts +44 -0
  181. package/dist/lexicon/types/com/atproto/server/defs.d.ts +20 -0
  182. package/dist/lexicon/types/com/atproto/server/deleteAccount.d.ts +28 -0
  183. package/dist/lexicon/types/com/atproto/server/deleteSession.d.ts +19 -0
  184. package/dist/lexicon/types/com/atproto/server/describeServer.d.ts +41 -0
  185. package/dist/lexicon/types/com/atproto/server/getAccountInviteCodes.d.ts +34 -0
  186. package/dist/lexicon/types/com/atproto/server/getSession.d.ts +34 -0
  187. package/dist/lexicon/types/com/atproto/server/listAppPasswords.d.ts +39 -0
  188. package/dist/lexicon/types/com/atproto/server/refreshSession.d.ts +35 -0
  189. package/dist/lexicon/types/com/atproto/server/requestAccountDelete.d.ts +19 -0
  190. package/dist/lexicon/types/com/atproto/server/requestEmailConfirmation.d.ts +19 -0
  191. package/dist/lexicon/types/com/atproto/server/requestEmailUpdate.d.ts +30 -0
  192. package/dist/lexicon/types/com/atproto/server/requestPasswordReset.d.ts +25 -0
  193. package/dist/lexicon/types/com/atproto/server/reserveSigningKey.d.ts +36 -0
  194. package/dist/lexicon/types/com/atproto/server/resetPassword.d.ts +27 -0
  195. package/dist/lexicon/types/com/atproto/server/revokeAppPassword.d.ts +25 -0
  196. package/dist/lexicon/types/com/atproto/server/updateEmail.d.ts +27 -0
  197. package/dist/lexicon/types/com/atproto/sync/getBlob.d.ts +30 -0
  198. package/dist/lexicon/types/com/atproto/sync/getBlocks.d.ts +30 -0
  199. package/dist/lexicon/types/com/atproto/sync/getCheckout.d.ts +29 -0
  200. package/dist/lexicon/types/com/atproto/sync/getHead.d.ts +32 -0
  201. package/dist/lexicon/types/com/atproto/sync/getLatestCommit.d.ts +33 -0
  202. package/dist/lexicon/types/com/atproto/sync/getRecord.d.ts +32 -0
  203. package/dist/lexicon/types/com/atproto/sync/getRepo.d.ts +30 -0
  204. package/dist/lexicon/types/com/atproto/sync/listBlobs.d.ts +35 -0
  205. package/dist/lexicon/types/com/atproto/sync/listRepos.d.ts +42 -0
  206. package/dist/lexicon/types/com/atproto/sync/notifyOfUpdate.d.ts +25 -0
  207. package/dist/lexicon/types/com/atproto/sync/requestCrawl.d.ts +25 -0
  208. package/dist/lexicon/types/com/atproto/sync/subscribeRepos.d.ts +80 -0
  209. package/dist/lexicon/types/com/atproto/temp/fetchLabels.d.ts +33 -0
  210. package/dist/lexicon/types/com/atproto/temp/importRepo.d.ts +32 -0
  211. package/dist/lexicon/types/com/atproto/temp/pushBlob.d.ts +25 -0
  212. package/dist/lexicon/types/com/atproto/temp/requestPhoneVerification.d.ts +25 -0
  213. package/dist/lexicon/types/com/atproto/temp/transferAccount.d.ts +42 -0
  214. package/dist/lexicon/util.d.ts +2 -0
  215. package/dist/logger.d.ts +4 -0
  216. package/dist/mod-service/index.d.ts +155 -0
  217. package/dist/mod-service/status.d.ts +12 -0
  218. package/dist/mod-service/subject.d.ts +59 -0
  219. package/dist/mod-service/types.d.ts +19 -0
  220. package/dist/mod-service/views.d.ts +49 -0
  221. package/dist/sequencer/index.d.ts +2 -0
  222. package/dist/sequencer/outbox.d.ts +16 -0
  223. package/dist/sequencer/sequencer.d.ts +35 -0
  224. package/dist/services/index.d.ts +7 -0
  225. package/dist/services/moderation/index.d.ts +144 -0
  226. package/dist/services/moderation/status.d.ts +12 -0
  227. package/dist/services/moderation/subject.d.ts +60 -0
  228. package/dist/services/moderation/types.d.ts +19 -0
  229. package/dist/services/moderation/views.d.ts +42 -0
  230. package/dist/services/types.d.ts +2 -0
  231. package/dist/util/date.d.ts +1 -0
  232. package/dist/util/debug.d.ts +1 -0
  233. package/dist/util/retry.d.ts +3 -0
  234. package/dist/util.d.ts +3 -0
  235. package/jest.config.js +6 -0
  236. package/package.json +67 -0
  237. package/src/api/admin/emitModerationEvent.ts +130 -0
  238. package/src/api/admin/getModerationEvent.ts +19 -0
  239. package/src/api/admin/getRecord.ts +34 -0
  240. package/src/api/admin/getRepo.ts +31 -0
  241. package/src/api/admin/queryModerationEvents.ts +40 -0
  242. package/src/api/admin/queryModerationStatuses.ts +57 -0
  243. package/src/api/admin/searchRepos.ts +42 -0
  244. package/src/api/admin/util.ts +54 -0
  245. package/src/api/health.ts +20 -0
  246. package/src/api/index.ts +28 -0
  247. package/src/api/moderation/createReport.ts +41 -0
  248. package/src/api/moderation/util.ts +65 -0
  249. package/src/api/temp/fetchLabels.ts +29 -0
  250. package/src/api/well-known.ts +35 -0
  251. package/src/auth.ts +147 -0
  252. package/src/background.ts +35 -0
  253. package/src/config/config.ts +84 -0
  254. package/src/config/env.ts +41 -0
  255. package/src/config/index.ts +3 -0
  256. package/src/config/secrets.ts +23 -0
  257. package/src/context.ts +180 -0
  258. package/src/daemon/context.ts +90 -0
  259. package/src/daemon/event-pusher.ts +296 -0
  260. package/src/daemon/event-reverser.ts +74 -0
  261. package/src/daemon/index.ts +33 -0
  262. package/src/db/index.ts +197 -0
  263. package/src/db/migrations/20231219T205730722Z-init.ts +164 -0
  264. package/src/db/migrations/index.ts +5 -0
  265. package/src/db/migrations/provider.ts +25 -0
  266. package/src/db/pagination.ts +216 -0
  267. package/src/db/schema/blob_push_event.ts +21 -0
  268. package/src/db/schema/index.ts +18 -0
  269. package/src/db/schema/label.ts +12 -0
  270. package/src/db/schema/moderation_event.ts +35 -0
  271. package/src/db/schema/moderation_subject_status.ts +32 -0
  272. package/src/db/schema/record_push_event.ts +21 -0
  273. package/src/db/schema/repo_push_event.ts +19 -0
  274. package/src/db/types.ts +15 -0
  275. package/src/error.ts +12 -0
  276. package/src/index.ts +102 -0
  277. package/src/lexicon/index.ts +1626 -0
  278. package/src/lexicon/lexicons.ts +8050 -0
  279. package/src/lexicon/types/app/bsky/actor/defs.ts +235 -0
  280. package/src/lexicon/types/app/bsky/actor/getPreferences.ts +44 -0
  281. package/src/lexicon/types/app/bsky/actor/getProfile.ts +41 -0
  282. package/src/lexicon/types/app/bsky/actor/getProfiles.ts +46 -0
  283. package/src/lexicon/types/app/bsky/actor/getSuggestions.ts +48 -0
  284. package/src/lexicon/types/app/bsky/actor/profile.ts +32 -0
  285. package/src/lexicon/types/app/bsky/actor/putPreferences.ts +39 -0
  286. package/src/lexicon/types/app/bsky/actor/searchActors.ts +52 -0
  287. package/src/lexicon/types/app/bsky/actor/searchActorsTypeahead.ts +50 -0
  288. package/src/lexicon/types/app/bsky/embed/external.ts +82 -0
  289. package/src/lexicon/types/app/bsky/embed/images.ts +96 -0
  290. package/src/lexicon/types/app/bsky/embed/record.ts +120 -0
  291. package/src/lexicon/types/app/bsky/embed/recordWithMedia.ts +53 -0
  292. package/src/lexicon/types/app/bsky/feed/defs.ts +308 -0
  293. package/src/lexicon/types/app/bsky/feed/describeFeedGenerator.ts +80 -0
  294. package/src/lexicon/types/app/bsky/feed/generator.ts +35 -0
  295. package/src/lexicon/types/app/bsky/feed/getActorFeeds.ts +49 -0
  296. package/src/lexicon/types/app/bsky/feed/getActorLikes.ts +50 -0
  297. package/src/lexicon/types/app/bsky/feed/getAuthorFeed.ts +56 -0
  298. package/src/lexicon/types/app/bsky/feed/getFeed.ts +50 -0
  299. package/src/lexicon/types/app/bsky/feed/getFeedGenerator.ts +48 -0
  300. package/src/lexicon/types/app/bsky/feed/getFeedGenerators.ts +46 -0
  301. package/src/lexicon/types/app/bsky/feed/getFeedSkeleton.ts +50 -0
  302. package/src/lexicon/types/app/bsky/feed/getLikes.ts +69 -0
  303. package/src/lexicon/types/app/bsky/feed/getListFeed.ts +50 -0
  304. package/src/lexicon/types/app/bsky/feed/getPostThread.ts +53 -0
  305. package/src/lexicon/types/app/bsky/feed/getPosts.ts +46 -0
  306. package/src/lexicon/types/app/bsky/feed/getRepostedBy.ts +52 -0
  307. package/src/lexicon/types/app/bsky/feed/getSuggestedFeeds.ts +48 -0
  308. package/src/lexicon/types/app/bsky/feed/getTimeline.ts +49 -0
  309. package/src/lexicon/types/app/bsky/feed/like.ts +26 -0
  310. package/src/lexicon/types/app/bsky/feed/post.ts +102 -0
  311. package/src/lexicon/types/app/bsky/feed/repost.ts +27 -0
  312. package/src/lexicon/types/app/bsky/feed/searchPosts.ts +54 -0
  313. package/src/lexicon/types/app/bsky/feed/threadgate.ts +84 -0
  314. package/src/lexicon/types/app/bsky/graph/block.ts +26 -0
  315. package/src/lexicon/types/app/bsky/graph/defs.ts +104 -0
  316. package/src/lexicon/types/app/bsky/graph/follow.ts +26 -0
  317. package/src/lexicon/types/app/bsky/graph/getBlocks.ts +48 -0
  318. package/src/lexicon/types/app/bsky/graph/getFollowers.ts +50 -0
  319. package/src/lexicon/types/app/bsky/graph/getFollows.ts +50 -0
  320. package/src/lexicon/types/app/bsky/graph/getList.ts +50 -0
  321. package/src/lexicon/types/app/bsky/graph/getListBlocks.ts +48 -0
  322. package/src/lexicon/types/app/bsky/graph/getListMutes.ts +48 -0
  323. package/src/lexicon/types/app/bsky/graph/getLists.ts +49 -0
  324. package/src/lexicon/types/app/bsky/graph/getMutes.ts +48 -0
  325. package/src/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.ts +46 -0
  326. package/src/lexicon/types/app/bsky/graph/list.ts +36 -0
  327. package/src/lexicon/types/app/bsky/graph/listblock.ts +26 -0
  328. package/src/lexicon/types/app/bsky/graph/listitem.ts +27 -0
  329. package/src/lexicon/types/app/bsky/graph/muteActor.ts +38 -0
  330. package/src/lexicon/types/app/bsky/graph/muteActorList.ts +38 -0
  331. package/src/lexicon/types/app/bsky/graph/unmuteActor.ts +38 -0
  332. package/src/lexicon/types/app/bsky/graph/unmuteActorList.ts +38 -0
  333. package/src/lexicon/types/app/bsky/notification/getUnreadCount.ts +45 -0
  334. package/src/lexicon/types/app/bsky/notification/listNotifications.ts +87 -0
  335. package/src/lexicon/types/app/bsky/notification/registerPush.ts +41 -0
  336. package/src/lexicon/types/app/bsky/notification/updateSeen.ts +38 -0
  337. package/src/lexicon/types/app/bsky/richtext/facet.ts +97 -0
  338. package/src/lexicon/types/app/bsky/unspecced/defs.ts +41 -0
  339. package/src/lexicon/types/app/bsky/unspecced/getPopularFeedGenerators.ts +49 -0
  340. package/src/lexicon/types/app/bsky/unspecced/getTimelineSkeleton.ts +49 -0
  341. package/src/lexicon/types/app/bsky/unspecced/searchActorsSkeleton.ts +56 -0
  342. package/src/lexicon/types/app/bsky/unspecced/searchPostsSkeleton.ts +54 -0
  343. package/src/lexicon/types/com/atproto/admin/defs.ts +719 -0
  344. package/src/lexicon/types/com/atproto/admin/deleteAccount.ts +38 -0
  345. package/src/lexicon/types/com/atproto/admin/disableAccountInvites.ts +40 -0
  346. package/src/lexicon/types/com/atproto/admin/disableInviteCodes.ts +39 -0
  347. package/src/lexicon/types/com/atproto/admin/emitModerationEvent.ts +66 -0
  348. package/src/lexicon/types/com/atproto/admin/enableAccountInvites.ts +40 -0
  349. package/src/lexicon/types/com/atproto/admin/getAccountInfo.ts +41 -0
  350. package/src/lexicon/types/com/atproto/admin/getAccountInfos.ts +46 -0
  351. package/src/lexicon/types/com/atproto/admin/getInviteCodes.ts +49 -0
  352. package/src/lexicon/types/com/atproto/admin/getModerationEvent.ts +41 -0
  353. package/src/lexicon/types/com/atproto/admin/getRecord.ts +43 -0
  354. package/src/lexicon/types/com/atproto/admin/getRepo.ts +42 -0
  355. package/src/lexicon/types/com/atproto/admin/getSubjectStatus.ts +54 -0
  356. package/src/lexicon/types/com/atproto/admin/queryModerationEvents.ts +56 -0
  357. package/src/lexicon/types/com/atproto/admin/queryModerationStatuses.ts +72 -0
  358. package/src/lexicon/types/com/atproto/admin/searchRepos.ts +51 -0
  359. package/src/lexicon/types/com/atproto/admin/sendEmail.ts +54 -0
  360. package/src/lexicon/types/com/atproto/admin/updateAccountEmail.ts +40 -0
  361. package/src/lexicon/types/com/atproto/admin/updateAccountHandle.ts +39 -0
  362. package/src/lexicon/types/com/atproto/admin/updateSubjectStatus.ts +61 -0
  363. package/src/lexicon/types/com/atproto/identity/resolveHandle.ts +46 -0
  364. package/src/lexicon/types/com/atproto/identity/updateHandle.ts +38 -0
  365. package/src/lexicon/types/com/atproto/label/defs.ts +73 -0
  366. package/src/lexicon/types/com/atproto/label/queryLabels.ts +52 -0
  367. package/src/lexicon/types/com/atproto/label/subscribeLabels.ts +67 -0
  368. package/src/lexicon/types/com/atproto/moderation/createReport.ts +65 -0
  369. package/src/lexicon/types/com/atproto/moderation/defs.ts +32 -0
  370. package/src/lexicon/types/com/atproto/repo/applyWrites.ts +103 -0
  371. package/src/lexicon/types/com/atproto/repo/createRecord.ts +62 -0
  372. package/src/lexicon/types/com/atproto/repo/deleteRecord.ts +48 -0
  373. package/src/lexicon/types/com/atproto/repo/describeRepo.ts +50 -0
  374. package/src/lexicon/types/com/atproto/repo/getRecord.ts +54 -0
  375. package/src/lexicon/types/com/atproto/repo/listRecords.ts +77 -0
  376. package/src/lexicon/types/com/atproto/repo/putRecord.ts +64 -0
  377. package/src/lexicon/types/com/atproto/repo/strongRef.ts +26 -0
  378. package/src/lexicon/types/com/atproto/repo/uploadBlob.ts +47 -0
  379. package/src/lexicon/types/com/atproto/server/confirmEmail.ts +40 -0
  380. package/src/lexicon/types/com/atproto/server/createAccount.ts +69 -0
  381. package/src/lexicon/types/com/atproto/server/createAppPassword.ts +69 -0
  382. package/src/lexicon/types/com/atproto/server/createInviteCode.ts +50 -0
  383. package/src/lexicon/types/com/atproto/server/createInviteCodes.ts +72 -0
  384. package/src/lexicon/types/com/atproto/server/createSession.ts +58 -0
  385. package/src/lexicon/types/com/atproto/server/defs.ts +48 -0
  386. package/src/lexicon/types/com/atproto/server/deleteAccount.ts +41 -0
  387. package/src/lexicon/types/com/atproto/server/deleteSession.ts +31 -0
  388. package/src/lexicon/types/com/atproto/server/describeServer.ts +64 -0
  389. package/src/lexicon/types/com/atproto/server/getAccountInviteCodes.ts +48 -0
  390. package/src/lexicon/types/com/atproto/server/getSession.ts +47 -0
  391. package/src/lexicon/types/com/atproto/server/listAppPasswords.ts +62 -0
  392. package/src/lexicon/types/com/atproto/server/refreshSession.ts +48 -0
  393. package/src/lexicon/types/com/atproto/server/requestAccountDelete.ts +31 -0
  394. package/src/lexicon/types/com/atproto/server/requestEmailConfirmation.ts +31 -0
  395. package/src/lexicon/types/com/atproto/server/requestEmailUpdate.ts +43 -0
  396. package/src/lexicon/types/com/atproto/server/requestPasswordReset.ts +38 -0
  397. package/src/lexicon/types/com/atproto/server/reserveSigningKey.ts +51 -0
  398. package/src/lexicon/types/com/atproto/server/resetPassword.ts +40 -0
  399. package/src/lexicon/types/com/atproto/server/revokeAppPassword.ts +38 -0
  400. package/src/lexicon/types/com/atproto/server/updateEmail.ts +41 -0
  401. package/src/lexicon/types/com/atproto/sync/getBlob.ts +43 -0
  402. package/src/lexicon/types/com/atproto/sync/getBlocks.ts +42 -0
  403. package/src/lexicon/types/com/atproto/sync/getCheckout.ts +41 -0
  404. package/src/lexicon/types/com/atproto/sync/getHead.ts +47 -0
  405. package/src/lexicon/types/com/atproto/sync/getLatestCommit.ts +48 -0
  406. package/src/lexicon/types/com/atproto/sync/getRecord.ts +45 -0
  407. package/src/lexicon/types/com/atproto/sync/getRepo.ts +43 -0
  408. package/src/lexicon/types/com/atproto/sync/listBlobs.ts +51 -0
  409. package/src/lexicon/types/com/atproto/sync/listRepos.ts +66 -0
  410. package/src/lexicon/types/com/atproto/sync/notifyOfUpdate.ts +39 -0
  411. package/src/lexicon/types/com/atproto/sync/requestCrawl.ts +39 -0
  412. package/src/lexicon/types/com/atproto/sync/subscribeRepos.ts +161 -0
  413. package/src/lexicon/types/com/atproto/temp/fetchLabels.ts +47 -0
  414. package/src/lexicon/types/com/atproto/temp/importRepo.ts +45 -0
  415. package/src/lexicon/types/com/atproto/temp/pushBlob.ts +39 -0
  416. package/src/lexicon/types/com/atproto/temp/requestPhoneVerification.ts +38 -0
  417. package/src/lexicon/types/com/atproto/temp/transferAccount.ts +62 -0
  418. package/src/lexicon/util.ts +13 -0
  419. package/src/logger.ts +19 -0
  420. package/src/mod-service/index.ts +758 -0
  421. package/src/mod-service/status.ts +264 -0
  422. package/src/mod-service/subject.ts +136 -0
  423. package/src/mod-service/types.ts +32 -0
  424. package/src/mod-service/views.ts +531 -0
  425. package/src/util.ts +26 -0
  426. package/test.env +2 -0
  427. package/test.log +0 -0
  428. package/tests/__snapshots__/get-record.test.ts.snap +173 -0
  429. package/tests/__snapshots__/get-repo.test.ts.snap +57 -0
  430. package/tests/__snapshots__/moderation-events.test.ts.snap +168 -0
  431. package/tests/__snapshots__/moderation-statuses.test.ts.snap +64 -0
  432. package/tests/__snapshots__/moderation.test.ts.snap +55 -0
  433. package/tests/_util.ts +192 -0
  434. package/tests/db.test.ts +184 -0
  435. package/tests/get-record.test.ts +110 -0
  436. package/tests/get-repo.test.ts +131 -0
  437. package/tests/moderation-appeals.test.ts +269 -0
  438. package/tests/moderation-events.test.ts +220 -0
  439. package/tests/moderation-statuses.test.ts +144 -0
  440. package/tests/moderation.test.ts +961 -0
  441. package/tests/repo-search.test.ts +123 -0
  442. package/tests/server.test.ts +76 -0
  443. package/tsconfig.build.json +4 -0
  444. package/tsconfig.json +21 -0
@@ -0,0 +1,961 @@
1
+ import {
2
+ TestNetwork,
3
+ ImageRef,
4
+ RecordRef,
5
+ SeedClient,
6
+ basicSeed,
7
+ } from '@atproto/dev-env'
8
+ import AtpAgent, {
9
+ ComAtprotoAdminEmitModerationEvent,
10
+ ComAtprotoAdminQueryModerationStatuses,
11
+ ComAtprotoModerationCreateReport,
12
+ } from '@atproto/api'
13
+ import { AtUri } from '@atproto/syntax'
14
+ import { forSnapshot } from './_util'
15
+ import {
16
+ REASONMISLEADING,
17
+ REASONOTHER,
18
+ REASONSPAM,
19
+ } from '../src/lexicon/types/com/atproto/moderation/defs'
20
+ import {
21
+ ModEventLabel,
22
+ ModEventTakedown,
23
+ REVIEWCLOSED,
24
+ REVIEWESCALATED,
25
+ } from '../src/lexicon/types/com/atproto/admin/defs'
26
+ import { EventReverser } from '../src'
27
+ import { TestOzone } from '@atproto/dev-env/src/ozone'
28
+
29
+ type BaseCreateReportParams =
30
+ | { account: string }
31
+ | { content: { uri: string; cid: string } }
32
+ type CreateReportParams = BaseCreateReportParams & {
33
+ author: string
34
+ } & Omit<ComAtprotoModerationCreateReport.InputSchema, 'subject'>
35
+
36
+ type TakedownParams = BaseCreateReportParams &
37
+ Omit<ComAtprotoAdminEmitModerationEvent.InputSchema, 'subject'>
38
+
39
+ describe('moderation', () => {
40
+ let network: TestNetwork
41
+ let ozone: TestOzone
42
+ let agent: AtpAgent
43
+ let pdsAgent: AtpAgent
44
+ let sc: SeedClient
45
+
46
+ const createReport = async (params: CreateReportParams) => {
47
+ const { author, ...rest } = params
48
+ return agent.api.com.atproto.moderation.createReport(
49
+ {
50
+ // Set default type to spam
51
+ reasonType: REASONSPAM,
52
+ ...rest,
53
+ subject:
54
+ 'account' in params
55
+ ? {
56
+ $type: 'com.atproto.admin.defs#repoRef',
57
+ did: params.account,
58
+ }
59
+ : {
60
+ $type: 'com.atproto.repo.strongRef',
61
+ uri: params.content.uri,
62
+ cid: params.content.cid,
63
+ },
64
+ },
65
+ {
66
+ headers: await network.serviceHeaders(
67
+ author,
68
+ network.ozone.ctx.cfg.service.did,
69
+ ),
70
+ encoding: 'application/json',
71
+ },
72
+ )
73
+ }
74
+
75
+ const performTakedown = async ({
76
+ durationInHours,
77
+ ...rest
78
+ }: TakedownParams & Pick<ModEventTakedown, 'durationInHours'>) =>
79
+ agent.api.com.atproto.admin.emitModerationEvent(
80
+ {
81
+ event: {
82
+ $type: 'com.atproto.admin.defs#modEventTakedown',
83
+ durationInHours,
84
+ },
85
+ subject:
86
+ 'account' in rest
87
+ ? {
88
+ $type: 'com.atproto.admin.defs#repoRef',
89
+ did: rest.account,
90
+ }
91
+ : {
92
+ $type: 'com.atproto.repo.strongRef',
93
+ uri: rest.content.uri,
94
+ cid: rest.content.cid,
95
+ },
96
+ createdBy: 'did:example:admin',
97
+ ...rest,
98
+ },
99
+ {
100
+ encoding: 'application/json',
101
+ headers: ozone.adminAuthHeaders(),
102
+ },
103
+ )
104
+
105
+ const performReverseTakedown = async (params: TakedownParams) =>
106
+ agent.api.com.atproto.admin.emitModerationEvent(
107
+ {
108
+ event: {
109
+ $type: 'com.atproto.admin.defs#modEventReverseTakedown',
110
+ },
111
+ subject:
112
+ 'account' in params
113
+ ? {
114
+ $type: 'com.atproto.admin.defs#repoRef',
115
+ did: params.account,
116
+ }
117
+ : {
118
+ $type: 'com.atproto.repo.strongRef',
119
+ uri: params.content.uri,
120
+ cid: params.content.cid,
121
+ },
122
+ createdBy: 'did:example:admin',
123
+ ...params,
124
+ },
125
+ {
126
+ encoding: 'application/json',
127
+ headers: ozone.adminAuthHeaders(),
128
+ },
129
+ )
130
+
131
+ const getStatuses = async (
132
+ params: ComAtprotoAdminQueryModerationStatuses.QueryParams,
133
+ ) => {
134
+ const { data } = await agent.api.com.atproto.admin.queryModerationStatuses(
135
+ params,
136
+ { headers: ozone.adminAuthHeaders() },
137
+ )
138
+
139
+ return data
140
+ }
141
+
142
+ beforeAll(async () => {
143
+ network = await TestNetwork.create({
144
+ dbPostgresSchema: 'ozone_moderation',
145
+ })
146
+ ozone = network.ozone
147
+ agent = network.ozone.getClient()
148
+ pdsAgent = network.pds.getClient()
149
+ sc = network.getSeedClient()
150
+ await basicSeed(sc)
151
+ await network.processAll()
152
+ })
153
+
154
+ afterAll(async () => {
155
+ await network.close()
156
+ })
157
+
158
+ describe('reporting', () => {
159
+ it('creates reports of a repo.', async () => {
160
+ const { data: reportA } = await createReport({
161
+ reasonType: REASONSPAM,
162
+ account: sc.dids.bob,
163
+ author: sc.dids.alice,
164
+ })
165
+ const { data: reportB } = await createReport({
166
+ reasonType: REASONOTHER,
167
+ reason: 'impersonation',
168
+ account: sc.dids.bob,
169
+ author: sc.dids.carol,
170
+ })
171
+ expect(forSnapshot([reportA, reportB])).toMatchSnapshot()
172
+ })
173
+
174
+ it("allows reporting a repo that doesn't exist.", async () => {
175
+ const promise = createReport({
176
+ reasonType: REASONSPAM,
177
+ account: 'did:plc:unknown',
178
+ author: sc.dids.alice,
179
+ })
180
+ await expect(promise).resolves.toBeDefined()
181
+ })
182
+
183
+ it('creates reports of a record.', async () => {
184
+ const postA = sc.posts[sc.dids.bob][0].ref
185
+ const postB = sc.posts[sc.dids.bob][1].ref
186
+ const { data: reportA } = await createReport({
187
+ author: sc.dids.alice,
188
+ reasonType: REASONSPAM,
189
+ content: {
190
+ $type: 'com.atproto.repo.strongRef',
191
+ uri: postA.uriStr,
192
+ cid: postA.cidStr,
193
+ },
194
+ })
195
+ const { data: reportB } = await createReport({
196
+ reasonType: REASONOTHER,
197
+ reason: 'defamation',
198
+ content: {
199
+ $type: 'com.atproto.repo.strongRef',
200
+ uri: postB.uriStr,
201
+ cid: postB.cidStr,
202
+ },
203
+ author: sc.dids.carol,
204
+ })
205
+ expect(forSnapshot([reportA, reportB])).toMatchSnapshot()
206
+ })
207
+
208
+ it("allows reporting a record that doesn't exist.", async () => {
209
+ const postA = sc.posts[sc.dids.bob][0].ref
210
+ const postB = sc.posts[sc.dids.bob][1].ref
211
+ const postUriBad = new AtUri(postA.uriStr)
212
+ postUriBad.rkey = 'badrkey'
213
+
214
+ const promiseA = createReport({
215
+ reasonType: REASONSPAM,
216
+ content: {
217
+ $type: 'com.atproto.repo.strongRef',
218
+ uri: postUriBad.toString(),
219
+ cid: postA.cidStr,
220
+ },
221
+ author: sc.dids.alice,
222
+ })
223
+ await expect(promiseA).resolves.toBeDefined()
224
+
225
+ const promiseB = createReport({
226
+ reasonType: REASONOTHER,
227
+ reason: 'defamation',
228
+ content: {
229
+ $type: 'com.atproto.repo.strongRef',
230
+ uri: postB.uri.toString(),
231
+ cid: postA.cidStr, // bad cid
232
+ },
233
+ author: sc.dids.carol,
234
+ })
235
+ await expect(promiseB).resolves.toBeDefined()
236
+ })
237
+ })
238
+
239
+ describe('actioning', () => {
240
+ it('resolves reports on repos and records.', async () => {
241
+ const post = sc.posts[sc.dids.bob][1].ref
242
+
243
+ await Promise.all([
244
+ createReport({
245
+ reasonType: REASONSPAM,
246
+ account: sc.dids.bob,
247
+ author: sc.dids.alice,
248
+ }),
249
+ createReport({
250
+ reasonType: REASONOTHER,
251
+ reason: 'defamation',
252
+ content: {
253
+ uri: post.uri.toString(),
254
+ cid: post.cid.toString(),
255
+ },
256
+ author: sc.dids.carol,
257
+ }),
258
+ ])
259
+
260
+ await performTakedown({
261
+ account: sc.dids.bob,
262
+ })
263
+
264
+ const moderationStatusOnBobsAccount = await getStatuses({
265
+ subject: sc.dids.bob,
266
+ })
267
+
268
+ // Validate that subject status is set to review closed and takendown flag is on
269
+ expect(moderationStatusOnBobsAccount.subjectStatuses[0]).toMatchObject({
270
+ reviewState: REVIEWCLOSED,
271
+ takendown: true,
272
+ subject: {
273
+ $type: 'com.atproto.admin.defs#repoRef',
274
+ did: sc.dids.bob,
275
+ },
276
+ })
277
+
278
+ // Cleanup
279
+ await performReverseTakedown({
280
+ account: sc.dids.bob,
281
+ })
282
+ })
283
+
284
+ it('supports escalating a subject', async () => {
285
+ const alicesPostRef = sc.posts[sc.dids.alice][0].ref
286
+ const alicesPostSubject = {
287
+ $type: 'com.atproto.repo.strongRef',
288
+ uri: alicesPostRef.uri.toString(),
289
+ cid: alicesPostRef.cid.toString(),
290
+ }
291
+ await agent.api.com.atproto.admin.emitModerationEvent(
292
+ {
293
+ event: {
294
+ $type: 'com.atproto.admin.defs#modEventEscalate',
295
+ comment: 'Y',
296
+ },
297
+ subject: alicesPostSubject,
298
+ createdBy: 'did:example:admin',
299
+ },
300
+ {
301
+ encoding: 'application/json',
302
+ headers: ozone.adminAuthHeaders('triage'),
303
+ },
304
+ )
305
+
306
+ const alicesPostStatus = await getStatuses({
307
+ subject: alicesPostRef.uri.toString(),
308
+ })
309
+
310
+ expect(alicesPostStatus.subjectStatuses[0]).toMatchObject({
311
+ reviewState: REVIEWESCALATED,
312
+ takendown: false,
313
+ subject: alicesPostSubject,
314
+ })
315
+ })
316
+
317
+ it('adds persistent comment on subject through comment event', async () => {
318
+ const alicesPostRef = sc.posts[sc.dids.alice][0].ref
319
+ const alicesPostSubject = {
320
+ $type: 'com.atproto.repo.strongRef',
321
+ uri: alicesPostRef.uri.toString(),
322
+ cid: alicesPostRef.cid.toString(),
323
+ }
324
+ await agent.api.com.atproto.admin.emitModerationEvent(
325
+ {
326
+ event: {
327
+ $type: 'com.atproto.admin.defs#modEventComment',
328
+ sticky: true,
329
+ comment: 'This is a persistent note',
330
+ },
331
+ subject: alicesPostSubject,
332
+ createdBy: 'did:example:admin',
333
+ },
334
+ {
335
+ encoding: 'application/json',
336
+ headers: ozone.adminAuthHeaders('triage'),
337
+ },
338
+ )
339
+
340
+ const alicesPostStatus = await getStatuses({
341
+ subject: alicesPostRef.uri.toString(),
342
+ })
343
+
344
+ expect(alicesPostStatus.subjectStatuses[0].comment).toEqual(
345
+ 'This is a persistent note',
346
+ )
347
+ })
348
+
349
+ it('reverses status when revert event is triggered.', async () => {
350
+ const alicesPostRef = sc.posts[sc.dids.alice][0].ref
351
+ const emitModEvent = async (
352
+ event: ComAtprotoAdminEmitModerationEvent.InputSchema['event'],
353
+ overwrites: Partial<ComAtprotoAdminEmitModerationEvent.InputSchema> = {},
354
+ ) => {
355
+ const baseAction = {
356
+ subject: {
357
+ $type: 'com.atproto.repo.strongRef',
358
+ uri: alicesPostRef.uriStr,
359
+ cid: alicesPostRef.cidStr,
360
+ },
361
+ createdBy: 'did:example:admin',
362
+ }
363
+ return agent.api.com.atproto.admin.emitModerationEvent(
364
+ {
365
+ event,
366
+ ...baseAction,
367
+ ...overwrites,
368
+ },
369
+ {
370
+ encoding: 'application/json',
371
+ headers: ozone.adminAuthHeaders(),
372
+ },
373
+ )
374
+ }
375
+ // Validate that subject status is marked as escalated
376
+ await emitModEvent({
377
+ $type: 'com.atproto.admin.defs#modEventReport',
378
+ reportType: REASONSPAM,
379
+ })
380
+ await emitModEvent({
381
+ $type: 'com.atproto.admin.defs#modEventReport',
382
+ reportType: REASONMISLEADING,
383
+ })
384
+ await emitModEvent({
385
+ $type: 'com.atproto.admin.defs#modEventEscalate',
386
+ })
387
+ const alicesPostStatusAfterEscalation = await getStatuses({
388
+ subject: alicesPostRef.uriStr,
389
+ })
390
+ expect(
391
+ alicesPostStatusAfterEscalation.subjectStatuses[0].reviewState,
392
+ ).toEqual(REVIEWESCALATED)
393
+
394
+ // Validate that subject status is marked as takendown
395
+
396
+ await emitModEvent({
397
+ $type: 'com.atproto.admin.defs#modEventLabel',
398
+ createLabelVals: ['nsfw'],
399
+ negateLabelVals: [],
400
+ })
401
+ await emitModEvent({
402
+ $type: 'com.atproto.admin.defs#modEventTakedown',
403
+ })
404
+
405
+ const alicesPostStatusAfterTakedown = await getStatuses({
406
+ subject: alicesPostRef.uriStr,
407
+ })
408
+ expect(alicesPostStatusAfterTakedown.subjectStatuses[0]).toMatchObject({
409
+ reviewState: REVIEWCLOSED,
410
+ takendown: true,
411
+ })
412
+
413
+ await emitModEvent({
414
+ $type: 'com.atproto.admin.defs#modEventReverseTakedown',
415
+ })
416
+ const alicesPostStatusAfterRevert = await getStatuses({
417
+ subject: alicesPostRef.uriStr,
418
+ })
419
+ // Validate that after reverting, the status of the subject is reverted to the last status changing event
420
+ expect(alicesPostStatusAfterRevert.subjectStatuses[0]).toMatchObject({
421
+ reviewState: REVIEWCLOSED,
422
+ takendown: false,
423
+ })
424
+ // Validate that after reverting, the last review date of the subject
425
+ // DOES NOT update to the the last status changing event
426
+ expect(
427
+ new Date(
428
+ alicesPostStatusAfterEscalation.subjectStatuses[0]
429
+ .lastReviewedAt as string,
430
+ ) <
431
+ new Date(
432
+ alicesPostStatusAfterRevert.subjectStatuses[0]
433
+ .lastReviewedAt as string,
434
+ ),
435
+ ).toBeTruthy()
436
+ })
437
+
438
+ it('negates an existing label.', async () => {
439
+ const { ctx } = ozone
440
+ const post = sc.posts[sc.dids.bob][0].ref
441
+ const bobsPostSubject = {
442
+ $type: 'com.atproto.repo.strongRef',
443
+ uri: post.uriStr,
444
+ cid: post.cidStr,
445
+ }
446
+ const modService = ctx.modService(ctx.db)
447
+ await modService.formatAndCreateLabels(
448
+ ctx.cfg.service.did,
449
+ post.uriStr,
450
+ post.cidStr,
451
+ { create: ['kittens'] },
452
+ )
453
+ await emitLabelEvent({
454
+ negateLabelVals: ['kittens'],
455
+ createLabelVals: [],
456
+ subject: bobsPostSubject,
457
+ })
458
+ await expect(getRecordLabels(post.uriStr)).resolves.toEqual([])
459
+
460
+ await emitLabelEvent({
461
+ createLabelVals: ['kittens'],
462
+ negateLabelVals: [],
463
+ subject: bobsPostSubject,
464
+ })
465
+ await expect(getRecordLabels(post.uriStr)).resolves.toEqual(['kittens'])
466
+ // Cleanup
467
+ await modService.formatAndCreateLabels(
468
+ ctx.cfg.service.did,
469
+ post.uriStr,
470
+ post.cidStr,
471
+ { negate: ['kittens'] },
472
+ )
473
+ })
474
+
475
+ it('no-ops when negating an already-negated label and reverses.', async () => {
476
+ const { ctx } = ozone
477
+ const post = sc.posts[sc.dids.bob][0].ref
478
+ const modService = ctx.modService(ctx.db)
479
+ await emitLabelEvent({
480
+ negateLabelVals: ['bears'],
481
+ createLabelVals: [],
482
+ subject: {
483
+ $type: 'com.atproto.repo.strongRef',
484
+ uri: post.uriStr,
485
+ cid: post.cidStr,
486
+ },
487
+ })
488
+ await expect(getRecordLabels(post.uriStr)).resolves.toEqual([])
489
+ await emitLabelEvent({
490
+ createLabelVals: ['bears'],
491
+ negateLabelVals: [],
492
+ subject: {
493
+ $type: 'com.atproto.repo.strongRef',
494
+ uri: post.uriStr,
495
+ cid: post.cidStr,
496
+ },
497
+ })
498
+ await expect(getRecordLabels(post.uriStr)).resolves.toEqual(['bears'])
499
+ // Cleanup
500
+ await modService.formatAndCreateLabels(
501
+ ctx.cfg.service.did,
502
+ post.uriStr,
503
+ post.cidStr,
504
+ { negate: ['bears'] },
505
+ )
506
+ })
507
+
508
+ it('creates non-existing labels and reverses.', async () => {
509
+ const post = sc.posts[sc.dids.bob][0].ref
510
+ await emitLabelEvent({
511
+ createLabelVals: ['puppies', 'doggies'],
512
+ negateLabelVals: [],
513
+ subject: {
514
+ $type: 'com.atproto.repo.strongRef',
515
+ uri: post.uriStr,
516
+ cid: post.cidStr,
517
+ },
518
+ })
519
+ await expect(getRecordLabels(post.uriStr)).resolves.toEqual([
520
+ 'puppies',
521
+ 'doggies',
522
+ ])
523
+ await emitLabelEvent({
524
+ negateLabelVals: ['puppies', 'doggies'],
525
+ createLabelVals: [],
526
+ subject: {
527
+ $type: 'com.atproto.repo.strongRef',
528
+ uri: post.uriStr,
529
+ cid: post.cidStr,
530
+ },
531
+ })
532
+ await expect(getRecordLabels(post.uriStr)).resolves.toEqual([])
533
+ })
534
+
535
+ it('creates labels on a repo and reverses.', async () => {
536
+ await emitLabelEvent({
537
+ createLabelVals: ['puppies', 'doggies'],
538
+ negateLabelVals: [],
539
+ subject: {
540
+ $type: 'com.atproto.admin.defs#repoRef',
541
+ did: sc.dids.bob,
542
+ },
543
+ })
544
+ await expect(getRepoLabels(sc.dids.bob)).resolves.toEqual([
545
+ 'puppies',
546
+ 'doggies',
547
+ ])
548
+ await emitLabelEvent({
549
+ negateLabelVals: ['puppies', 'doggies'],
550
+ createLabelVals: [],
551
+ subject: {
552
+ $type: 'com.atproto.admin.defs#repoRef',
553
+ did: sc.dids.bob,
554
+ },
555
+ })
556
+ await expect(getRepoLabels(sc.dids.bob)).resolves.toEqual([])
557
+ })
558
+
559
+ it('creates and negates labels on a repo and reverses.', async () => {
560
+ const { ctx } = ozone
561
+ const modService = ctx.modService(ctx.db)
562
+ await modService.formatAndCreateLabels(
563
+ ctx.cfg.service.did,
564
+ sc.dids.bob,
565
+ null,
566
+ { create: ['kittens'] },
567
+ )
568
+ await emitLabelEvent({
569
+ createLabelVals: ['puppies'],
570
+ negateLabelVals: ['kittens'],
571
+ subject: {
572
+ $type: 'com.atproto.admin.defs#repoRef',
573
+ did: sc.dids.bob,
574
+ },
575
+ })
576
+ await expect(getRepoLabels(sc.dids.bob)).resolves.toEqual(['puppies'])
577
+
578
+ await emitLabelEvent({
579
+ negateLabelVals: ['puppies'],
580
+ createLabelVals: ['kittens'],
581
+ subject: {
582
+ $type: 'com.atproto.admin.defs#repoRef',
583
+ did: sc.dids.bob,
584
+ },
585
+ })
586
+ await expect(getRepoLabels(sc.dids.bob)).resolves.toEqual(['kittens'])
587
+ })
588
+
589
+ it('does not allow triage moderators to label.', async () => {
590
+ const attemptLabel = agent.api.com.atproto.admin.emitModerationEvent(
591
+ {
592
+ event: {
593
+ $type: 'com.atproto.admin.defs#modEventLabel',
594
+ negateLabelVals: ['a'],
595
+ createLabelVals: ['b', 'c'],
596
+ },
597
+ createdBy: 'did:example:moderator',
598
+ reason: 'Y',
599
+ subject: {
600
+ $type: 'com.atproto.admin.defs#repoRef',
601
+ did: sc.dids.bob,
602
+ },
603
+ },
604
+ {
605
+ encoding: 'application/json',
606
+ headers: network.bsky.adminAuthHeaders('triage'),
607
+ },
608
+ )
609
+ await expect(attemptLabel).rejects.toThrow(
610
+ 'Must be a full moderator to label content',
611
+ )
612
+ })
613
+
614
+ it('does not allow take down event on takendown post or reverse takedown on available post.', async () => {
615
+ await performTakedown({
616
+ account: sc.dids.bob,
617
+ })
618
+ await expect(
619
+ performTakedown({
620
+ account: sc.dids.bob,
621
+ }),
622
+ ).rejects.toThrow('Subject is already taken down')
623
+
624
+ // Cleanup
625
+ await performReverseTakedown({
626
+ account: sc.dids.bob,
627
+ })
628
+ await expect(
629
+ performReverseTakedown({
630
+ account: sc.dids.bob,
631
+ }),
632
+ ).rejects.toThrow('Subject is not taken down')
633
+ })
634
+
635
+ it('fans out repo takedowns to pds', async () => {
636
+ await performTakedown({
637
+ account: sc.dids.bob,
638
+ })
639
+ await ozone.processAll()
640
+
641
+ const res1 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
642
+ {
643
+ did: sc.dids.bob,
644
+ },
645
+ { headers: network.pds.adminAuthHeaders() },
646
+ )
647
+ expect(res1.data.takedown?.applied).toBe(true)
648
+
649
+ // cleanup
650
+ await performReverseTakedown({ account: sc.dids.bob })
651
+ await ozone.processAll()
652
+
653
+ const res2 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
654
+ {
655
+ did: sc.dids.bob,
656
+ },
657
+ { headers: network.pds.adminAuthHeaders() },
658
+ )
659
+ expect(res2.data.takedown?.applied).toBe(false)
660
+ })
661
+
662
+ it('fans out record takedowns to pds', async () => {
663
+ const post = sc.posts[sc.dids.bob][0]
664
+ const uri = post.ref.uriStr
665
+ const cid = post.ref.cidStr
666
+ await performTakedown({
667
+ content: { uri, cid },
668
+ })
669
+ await ozone.processAll()
670
+ const res1 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
671
+ { uri },
672
+ { headers: network.pds.adminAuthHeaders() },
673
+ )
674
+ expect(res1.data.takedown?.applied).toBe(true)
675
+
676
+ // cleanup
677
+ await performReverseTakedown({ content: { uri, cid } })
678
+ await ozone.processAll()
679
+
680
+ const res2 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
681
+ { uri },
682
+ { headers: network.pds.adminAuthHeaders() },
683
+ )
684
+ expect(res2.data.takedown?.applied).toBe(false)
685
+ })
686
+
687
+ it('allows full moderators to takedown.', async () => {
688
+ await agent.api.com.atproto.admin.emitModerationEvent(
689
+ {
690
+ event: {
691
+ $type: 'com.atproto.admin.defs#modEventTakedown',
692
+ },
693
+ createdBy: 'did:example:moderator',
694
+ subject: {
695
+ $type: 'com.atproto.admin.defs#repoRef',
696
+ did: sc.dids.bob,
697
+ },
698
+ },
699
+ {
700
+ encoding: 'application/json',
701
+ headers: network.bsky.adminAuthHeaders('moderator'),
702
+ },
703
+ )
704
+ // cleanup
705
+ await reverse({
706
+ subject: {
707
+ $type: 'com.atproto.admin.defs#repoRef',
708
+ did: sc.dids.bob,
709
+ },
710
+ })
711
+ })
712
+
713
+ it('does not allow non-full moderators to takedown.', async () => {
714
+ const attemptTakedownTriage =
715
+ agent.api.com.atproto.admin.emitModerationEvent(
716
+ {
717
+ event: {
718
+ $type: 'com.atproto.admin.defs#modEventTakedown',
719
+ },
720
+ createdBy: 'did:example:moderator',
721
+ subject: {
722
+ $type: 'com.atproto.admin.defs#repoRef',
723
+ did: sc.dids.bob,
724
+ },
725
+ },
726
+ {
727
+ encoding: 'application/json',
728
+ headers: network.bsky.adminAuthHeaders('triage'),
729
+ },
730
+ )
731
+ await expect(attemptTakedownTriage).rejects.toThrow(
732
+ 'Must be a full moderator to perform an account takedown',
733
+ )
734
+ })
735
+ it('automatically reverses actions marked with duration', async () => {
736
+ await createReport({
737
+ reasonType: REASONSPAM,
738
+ account: sc.dids.bob,
739
+ author: sc.dids.alice,
740
+ })
741
+ const { data: action } = await performTakedown({
742
+ account: sc.dids.bob,
743
+ // Use negative value to set the expiry time in the past so that the action is automatically reversed
744
+ // right away without having to wait n number of hours for a successful assertion
745
+ durationInHours: -1,
746
+ })
747
+ await ozone.processAll()
748
+
749
+ const { data: statusesAfterTakedown } =
750
+ await agent.api.com.atproto.admin.queryModerationStatuses(
751
+ { subject: sc.dids.bob },
752
+ { headers: network.bsky.adminAuthHeaders('moderator') },
753
+ )
754
+
755
+ expect(statusesAfterTakedown.subjectStatuses[0]).toMatchObject({
756
+ takendown: true,
757
+ })
758
+
759
+ // In the actual app, this will be instantiated and run on server startup
760
+ const reverser = new EventReverser(
761
+ network.ozone.ctx.db,
762
+ network.ozone.ctx.modService,
763
+ )
764
+ await reverser.findAndRevertDueActions()
765
+ await ozone.processAll()
766
+
767
+ const [{ data: eventList }, { data: statuses }] = await Promise.all([
768
+ agent.api.com.atproto.admin.queryModerationEvents(
769
+ { subject: sc.dids.bob },
770
+ { headers: network.bsky.adminAuthHeaders('moderator') },
771
+ ),
772
+ agent.api.com.atproto.admin.queryModerationStatuses(
773
+ { subject: sc.dids.bob },
774
+ { headers: network.bsky.adminAuthHeaders('moderator') },
775
+ ),
776
+ ])
777
+
778
+ expect(statuses.subjectStatuses[0]).toMatchObject({
779
+ takendown: false,
780
+ reviewState: REVIEWCLOSED,
781
+ })
782
+ // Verify that the automatic reversal is attributed to the original moderator of the temporary action
783
+ // and that the reason is set to indicate that the action was automatically reversed.
784
+ expect(eventList.events[0]).toMatchObject({
785
+ createdBy: action.createdBy,
786
+ event: {
787
+ $type: 'com.atproto.admin.defs#modEventReverseTakedown',
788
+ comment:
789
+ '[SCHEDULED_REVERSAL] Reverting action as originally scheduled',
790
+ },
791
+ })
792
+ })
793
+
794
+ async function emitLabelEvent(
795
+ opts: Partial<ComAtprotoAdminEmitModerationEvent.InputSchema> & {
796
+ subject: ComAtprotoAdminEmitModerationEvent.InputSchema['subject']
797
+ createLabelVals: ModEventLabel['createLabelVals']
798
+ negateLabelVals: ModEventLabel['negateLabelVals']
799
+ },
800
+ ) {
801
+ const { createLabelVals, negateLabelVals } = opts
802
+ const result = await agent.api.com.atproto.admin.emitModerationEvent(
803
+ {
804
+ event: {
805
+ $type: 'com.atproto.admin.defs#modEventLabel',
806
+ createLabelVals,
807
+ negateLabelVals,
808
+ },
809
+ createdBy: 'did:example:admin',
810
+ reason: 'Y',
811
+ ...opts,
812
+ },
813
+ {
814
+ encoding: 'application/json',
815
+ headers: network.bsky.adminAuthHeaders(),
816
+ },
817
+ )
818
+ return result.data
819
+ }
820
+
821
+ async function reverse(
822
+ opts: Partial<ComAtprotoAdminEmitModerationEvent.InputSchema> & {
823
+ subject: ComAtprotoAdminEmitModerationEvent.InputSchema['subject']
824
+ },
825
+ ) {
826
+ await agent.api.com.atproto.admin.emitModerationEvent(
827
+ {
828
+ event: {
829
+ $type: 'com.atproto.admin.defs#modEventReverseTakedown',
830
+ },
831
+ createdBy: 'did:example:admin',
832
+ reason: 'Y',
833
+ ...opts,
834
+ },
835
+ {
836
+ encoding: 'application/json',
837
+ headers: network.bsky.adminAuthHeaders(),
838
+ },
839
+ )
840
+ }
841
+
842
+ async function getRecordLabels(uri: string) {
843
+ const result = await agent.api.com.atproto.admin.getRecord(
844
+ { uri },
845
+ { headers: network.bsky.adminAuthHeaders() },
846
+ )
847
+ const labels = result.data.labels ?? []
848
+ return labels.map((l) => l.val)
849
+ }
850
+
851
+ async function getRepoLabels(did: string) {
852
+ const result = await agent.api.com.atproto.admin.getRepo(
853
+ { did },
854
+ { headers: network.bsky.adminAuthHeaders() },
855
+ )
856
+ const labels = result.data.labels ?? []
857
+ return labels.map((l) => l.val)
858
+ }
859
+ })
860
+
861
+ describe('blob takedown', () => {
862
+ let post: { ref: RecordRef; images: ImageRef[] }
863
+ let blob: ImageRef
864
+ let imageUri: string
865
+ beforeAll(async () => {
866
+ const { ctx } = network.bsky
867
+ post = sc.posts[sc.dids.carol][0]
868
+ blob = post.images[1]
869
+ imageUri = ctx.imgUriBuilder
870
+ .getPresetUri(
871
+ 'feed_thumbnail',
872
+ sc.dids.carol,
873
+ blob.image.ref.toString(),
874
+ )
875
+ .replace(ctx.cfg.publicUrl || '', network.bsky.url)
876
+ // Warm image server cache
877
+ await fetch(imageUri)
878
+ const cached = await fetch(imageUri)
879
+ expect(cached.headers.get('x-cache')).toEqual('hit')
880
+ await performTakedown({
881
+ content: {
882
+ uri: post.ref.uriStr,
883
+ cid: post.ref.cidStr,
884
+ },
885
+ subjectBlobCids: [blob.image.ref.toString()],
886
+ })
887
+ await ozone.processAll()
888
+ })
889
+
890
+ it('sets blobCids in moderation status', async () => {
891
+ const { subjectStatuses } = await getStatuses({
892
+ subject: post.ref.uriStr,
893
+ })
894
+
895
+ expect(subjectStatuses[0].subjectBlobCids).toEqual([
896
+ blob.image.ref.toString(),
897
+ ])
898
+ })
899
+
900
+ it('prevents resolution of blob', async () => {
901
+ const blobPath = `/blob/${sc.dids.carol}/${blob.image.ref.toString()}`
902
+ const resolveBlob = await fetch(`${network.bsky.url}${blobPath}`)
903
+ expect(resolveBlob.status).toEqual(404)
904
+ expect(await resolveBlob.json()).toEqual({
905
+ error: 'NotFoundError',
906
+ message: 'Blob not found',
907
+ })
908
+ })
909
+
910
+ it('prevents image blob from being served, even when cached.', async () => {
911
+ const fetchImage = await fetch(imageUri)
912
+ expect(fetchImage.status).toEqual(404)
913
+ expect(await fetchImage.json()).toEqual({ message: 'Image not found' })
914
+ })
915
+
916
+ it('fans takedown out to pds', async () => {
917
+ const res = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
918
+ {
919
+ did: sc.dids.carol,
920
+ blob: blob.image.ref.toString(),
921
+ },
922
+ { headers: network.pds.adminAuthHeaders() },
923
+ )
924
+ expect(res.data.takedown?.applied).toBe(true)
925
+ })
926
+
927
+ it('restores blob when action is reversed.', async () => {
928
+ await performReverseTakedown({
929
+ content: {
930
+ uri: post.ref.uriStr,
931
+ cid: post.ref.cidStr,
932
+ },
933
+ subjectBlobCids: [blob.image.ref.toString()],
934
+ })
935
+
936
+ await ozone.processAll()
937
+
938
+ // Can resolve blob
939
+ const blobPath = `/blob/${sc.dids.carol}/${blob.image.ref.toString()}`
940
+ const resolveBlob = await fetch(`${network.bsky.url}${blobPath}`)
941
+ expect(resolveBlob.status).toEqual(200)
942
+
943
+ // Can fetch through image server
944
+ const fetchImage = await fetch(imageUri)
945
+ expect(fetchImage.status).toEqual(200)
946
+ const size = Number(fetchImage.headers.get('content-length'))
947
+ expect(size).toBeGreaterThan(9000)
948
+ })
949
+
950
+ it('fans reversal out to pds', async () => {
951
+ const res = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
952
+ {
953
+ did: sc.dids.carol,
954
+ blob: blob.image.ref.toString(),
955
+ },
956
+ { headers: network.pds.adminAuthHeaders() },
957
+ )
958
+ expect(res.data.takedown?.applied).toBe(false)
959
+ })
960
+ })
961
+ })