@atproto/bsky 0.0.14 → 0.0.16

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 (195) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/api/app/bsky/feed/searchPosts.d.ts +3 -0
  3. package/dist/api/com/atproto/moderation/util.d.ts +4 -3
  4. package/dist/config.d.ts +2 -2
  5. package/dist/context.d.ts +16 -1
  6. package/dist/db/index.js +26 -1
  7. package/dist/db/index.js.map +3 -3
  8. package/dist/db/migrations/20231003T202833377Z-create-moderation-subject-status.d.ts +3 -0
  9. package/dist/db/migrations/index.d.ts +1 -0
  10. package/dist/db/pagination.d.ts +2 -1
  11. package/dist/db/{periodic-moderation-action-reversal.d.ts → periodic-moderation-event-reversal.d.ts} +3 -5
  12. package/dist/db/tables/moderation.d.ts +24 -34
  13. package/dist/feed-gen/types.d.ts +1 -1
  14. package/dist/index.d.ts +2 -1
  15. package/dist/index.js +3332 -2430
  16. package/dist/index.js.map +3 -3
  17. package/dist/lexicon/index.d.ts +18 -18
  18. package/dist/lexicon/lexicons.d.ts +460 -385
  19. package/dist/lexicon/types/app/bsky/feed/defs.d.ts +1 -7
  20. package/dist/lexicon/types/app/bsky/graph/defs.d.ts +1 -0
  21. package/dist/lexicon/types/com/atproto/admin/defs.d.ts +116 -48
  22. package/dist/lexicon/types/com/atproto/admin/{takeModerationAction.d.ts → emitModerationEvent.d.ts} +5 -6
  23. package/dist/lexicon/types/com/atproto/admin/{getModerationAction.d.ts → getModerationEvent.d.ts} +1 -1
  24. package/dist/lexicon/types/com/atproto/admin/{getModerationActions.d.ts → queryModerationEvents.d.ts} +5 -1
  25. package/dist/lexicon/types/com/atproto/admin/{getModerationReports.d.ts → queryModerationStatuses.d.ts} +12 -6
  26. package/dist/lexicon/types/com/atproto/admin/sendEmail.d.ts +1 -0
  27. package/dist/lexicon/types/com/atproto/{admin/getModerationReport.d.ts → temp/fetchLabels.d.ts} +7 -3
  28. package/dist/migrate-moderation-data.d.ts +1 -0
  29. package/dist/services/actor/views.d.ts +2 -5
  30. package/dist/services/feed/index.d.ts +1 -0
  31. package/dist/services/feed/util.d.ts +9 -1
  32. package/dist/services/feed/views.d.ts +6 -17
  33. package/dist/services/graph/index.d.ts +5 -29
  34. package/dist/services/graph/types.d.ts +1 -0
  35. package/dist/services/moderation/index.d.ts +135 -72
  36. package/dist/services/moderation/pagination.d.ts +36 -0
  37. package/dist/services/moderation/status.d.ts +13 -0
  38. package/dist/services/moderation/types.d.ts +35 -0
  39. package/dist/services/moderation/views.d.ts +18 -14
  40. package/dist/util/debug.d.ts +1 -1
  41. package/package.json +14 -15
  42. package/src/api/app/bsky/actor/getSuggestions.ts +45 -21
  43. package/src/api/app/bsky/feed/getActorFeeds.ts +2 -1
  44. package/src/api/app/bsky/feed/getActorLikes.ts +1 -3
  45. package/src/api/app/bsky/feed/getAuthorFeed.ts +1 -3
  46. package/src/api/app/bsky/feed/getFeed.ts +9 -9
  47. package/src/api/app/bsky/feed/getFeedGenerator.ts +3 -0
  48. package/src/api/app/bsky/feed/getFeedGenerators.ts +2 -1
  49. package/src/api/app/bsky/feed/getListFeed.ts +1 -3
  50. package/src/api/app/bsky/feed/getPostThread.ts +31 -58
  51. package/src/api/app/bsky/feed/getPosts.ts +21 -18
  52. package/src/api/app/bsky/feed/getSuggestedFeeds.ts +2 -1
  53. package/src/api/app/bsky/feed/getTimeline.ts +1 -3
  54. package/src/api/app/bsky/feed/searchPosts.ts +130 -0
  55. package/src/api/app/bsky/graph/getList.ts +6 -3
  56. package/src/api/app/bsky/graph/getListBlocks.ts +3 -2
  57. package/src/api/app/bsky/graph/getListMutes.ts +2 -1
  58. package/src/api/app/bsky/graph/getLists.ts +2 -1
  59. package/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts +3 -1
  60. package/src/api/blob-resolver.ts +6 -11
  61. package/src/api/com/atproto/admin/emitModerationEvent.ts +220 -0
  62. package/src/api/com/atproto/admin/{getModerationActions.ts → getModerationEvent.ts} +5 -11
  63. package/src/api/com/atproto/admin/getRecord.ts +1 -0
  64. package/src/api/com/atproto/admin/{getModerationReports.ts → queryModerationEvents.ts} +13 -16
  65. package/src/api/com/atproto/admin/queryModerationStatuses.ts +55 -0
  66. package/src/api/com/atproto/admin/util.ts +3 -1
  67. package/src/api/com/atproto/moderation/createReport.ts +9 -7
  68. package/src/api/com/atproto/moderation/util.ts +38 -20
  69. package/src/api/com/atproto/temp/fetchLabels.ts +30 -0
  70. package/src/api/index.ts +12 -14
  71. package/src/auth.ts +29 -21
  72. package/src/auto-moderator/index.ts +26 -19
  73. package/src/config.ts +6 -6
  74. package/src/context.ts +15 -9
  75. package/src/db/migrations/20231003T202833377Z-create-moderation-subject-status.ts +123 -0
  76. package/src/db/migrations/index.ts +1 -0
  77. package/src/db/pagination.ts +26 -3
  78. package/src/db/{periodic-moderation-action-reversal.ts → periodic-moderation-event-reversal.ts} +51 -55
  79. package/src/db/tables/moderation.ts +35 -52
  80. package/src/feed-gen/best-of-follows.ts +6 -3
  81. package/src/feed-gen/bsky-team.ts +1 -1
  82. package/src/feed-gen/hot-classic.ts +1 -1
  83. package/src/feed-gen/mutuals.ts +6 -2
  84. package/src/feed-gen/types.ts +1 -1
  85. package/src/feed-gen/whats-hot.ts +1 -1
  86. package/src/feed-gen/with-friends.ts +7 -3
  87. package/src/index.ts +2 -1
  88. package/src/lexicon/index.ts +52 -67
  89. package/src/lexicon/lexicons.ts +674 -579
  90. package/src/lexicon/types/app/bsky/actor/defs.ts +2 -2
  91. package/src/lexicon/types/app/bsky/actor/searchActors.ts +2 -2
  92. package/src/lexicon/types/app/bsky/actor/searchActorsTypeahead.ts +2 -2
  93. package/src/lexicon/types/app/bsky/feed/defs.ts +1 -18
  94. package/src/lexicon/types/app/bsky/feed/searchPosts.ts +3 -3
  95. package/src/lexicon/types/app/bsky/graph/defs.ts +3 -2
  96. package/src/lexicon/types/app/bsky/unspecced/searchActorsSkeleton.ts +4 -4
  97. package/src/lexicon/types/app/bsky/unspecced/searchPostsSkeleton.ts +3 -3
  98. package/src/lexicon/types/com/atproto/admin/defs.ts +278 -84
  99. package/src/lexicon/types/com/atproto/admin/disableAccountInvites.ts +1 -1
  100. package/src/lexicon/types/com/atproto/admin/{takeModerationAction.ts → emitModerationEvent.ts} +13 -11
  101. package/src/lexicon/types/com/atproto/admin/enableAccountInvites.ts +1 -1
  102. package/src/lexicon/types/com/atproto/admin/{getModerationReport.ts → getModerationEvent.ts} +1 -1
  103. package/src/lexicon/types/com/atproto/admin/{getModerationReports.ts → queryModerationEvents.ts} +8 -15
  104. package/src/lexicon/types/com/atproto/admin/queryModerationStatuses.ts +70 -0
  105. package/src/lexicon/types/com/atproto/admin/sendEmail.ts +1 -0
  106. package/src/lexicon/types/com/atproto/label/defs.ts +9 -9
  107. package/src/lexicon/types/com/atproto/label/queryLabels.ts +2 -2
  108. package/src/lexicon/types/com/atproto/repo/applyWrites.ts +1 -1
  109. package/src/lexicon/types/com/atproto/repo/createRecord.ts +2 -2
  110. package/src/lexicon/types/com/atproto/repo/deleteRecord.ts +2 -2
  111. package/src/lexicon/types/com/atproto/repo/listRecords.ts +1 -1
  112. package/src/lexicon/types/com/atproto/repo/putRecord.ts +3 -3
  113. package/src/lexicon/types/com/atproto/sync/listBlobs.ts +1 -1
  114. package/src/lexicon/types/com/atproto/sync/subscribeRepos.ts +4 -4
  115. package/src/lexicon/types/com/atproto/{admin/getModerationActions.ts → temp/fetchLabels.ts} +3 -5
  116. package/src/migrate-moderation-data.ts +414 -0
  117. package/src/services/actor/views.ts +5 -14
  118. package/src/services/feed/index.ts +26 -7
  119. package/src/services/feed/util.ts +47 -19
  120. package/src/services/feed/views.ts +68 -4
  121. package/src/services/graph/index.ts +21 -3
  122. package/src/services/graph/types.ts +1 -0
  123. package/src/services/indexing/plugins/block.ts +2 -3
  124. package/src/services/indexing/plugins/feed-generator.ts +2 -3
  125. package/src/services/indexing/plugins/follow.ts +2 -3
  126. package/src/services/indexing/plugins/like.ts +2 -3
  127. package/src/services/indexing/plugins/list-block.ts +2 -3
  128. package/src/services/indexing/plugins/list-item.ts +2 -3
  129. package/src/services/indexing/plugins/list.ts +2 -3
  130. package/src/services/indexing/plugins/post.ts +3 -4
  131. package/src/services/indexing/plugins/repost.ts +2 -3
  132. package/src/services/indexing/plugins/thread-gate.ts +2 -3
  133. package/src/services/label/index.ts +2 -3
  134. package/src/services/moderation/index.ts +380 -395
  135. package/src/services/moderation/pagination.ts +96 -0
  136. package/src/services/moderation/status.ts +244 -0
  137. package/src/services/moderation/types.ts +49 -0
  138. package/src/services/moderation/views.ts +278 -329
  139. package/src/util/debug.ts +2 -2
  140. package/tests/__snapshots__/feed-generation.test.ts.snap +322 -6
  141. package/tests/__snapshots__/indexing.test.ts.snap +0 -6
  142. package/tests/admin/__snapshots__/get-record.test.ts.snap +30 -132
  143. package/tests/admin/__snapshots__/get-repo.test.ts.snap +14 -60
  144. package/tests/admin/__snapshots__/moderation-events.test.ts.snap +146 -0
  145. package/tests/admin/__snapshots__/moderation-statuses.test.ts.snap +64 -0
  146. package/tests/admin/__snapshots__/moderation.test.ts.snap +0 -125
  147. package/tests/admin/get-record.test.ts +5 -9
  148. package/tests/admin/get-repo.test.ts +38 -9
  149. package/tests/admin/moderation-events.test.ts +221 -0
  150. package/tests/admin/moderation-statuses.test.ts +145 -0
  151. package/tests/admin/moderation.test.ts +512 -860
  152. package/tests/admin/repo-search.test.ts +2 -3
  153. package/tests/auto-moderator/fuzzy-matcher.test.ts +2 -1
  154. package/tests/auto-moderator/takedowns.test.ts +45 -18
  155. package/tests/feed-generation.test.ts +57 -9
  156. package/tests/views/__snapshots__/block-lists.test.ts.snap +3 -9
  157. package/tests/views/__snapshots__/blocks.test.ts.snap +0 -9
  158. package/tests/views/__snapshots__/mute-lists.test.ts.snap +5 -5
  159. package/tests/views/__snapshots__/mutes.test.ts.snap +0 -3
  160. package/tests/views/__snapshots__/thread.test.ts.snap +0 -30
  161. package/tests/views/actor-search.test.ts +2 -3
  162. package/tests/views/author-feed.test.ts +42 -36
  163. package/tests/views/follows.test.ts +40 -35
  164. package/tests/views/list-feed.test.ts +17 -9
  165. package/tests/views/notifications.test.ts +13 -9
  166. package/tests/views/profile.test.ts +20 -18
  167. package/tests/views/suggestions.test.ts +15 -7
  168. package/tests/views/thread.test.ts +54 -26
  169. package/tests/views/threadgating.test.ts +51 -19
  170. package/tests/views/timeline.test.ts +21 -13
  171. package/dist/api/com/atproto/admin/reverseModerationAction.d.ts +0 -3
  172. package/dist/api/com/atproto/admin/takeModerationAction.d.ts +0 -3
  173. package/dist/lexicon/types/com/atproto/admin/resolveModerationReports.d.ts +0 -36
  174. package/dist/lexicon/types/com/atproto/admin/reverseModerationAction.d.ts +0 -36
  175. package/src/api/com/atproto/admin/getModerationAction.ts +0 -44
  176. package/src/api/com/atproto/admin/getModerationReport.ts +0 -43
  177. package/src/api/com/atproto/admin/resolveModerationReports.ts +0 -24
  178. package/src/api/com/atproto/admin/reverseModerationAction.ts +0 -115
  179. package/src/api/com/atproto/admin/takeModerationAction.ts +0 -156
  180. package/src/lexicon/types/com/atproto/admin/getModerationAction.ts +0 -41
  181. package/src/lexicon/types/com/atproto/admin/resolveModerationReports.ts +0 -49
  182. package/src/lexicon/types/com/atproto/admin/reverseModerationAction.ts +0 -49
  183. package/tests/admin/__snapshots__/get-moderation-action.test.ts.snap +0 -172
  184. package/tests/admin/__snapshots__/get-moderation-actions.test.ts.snap +0 -178
  185. package/tests/admin/__snapshots__/get-moderation-report.test.ts.snap +0 -177
  186. package/tests/admin/__snapshots__/get-moderation-reports.test.ts.snap +0 -307
  187. package/tests/admin/get-moderation-action.test.ts +0 -100
  188. package/tests/admin/get-moderation-actions.test.ts +0 -164
  189. package/tests/admin/get-moderation-report.test.ts +0 -100
  190. package/tests/admin/get-moderation-reports.test.ts +0 -332
  191. /package/dist/api/com/atproto/admin/{getModerationAction.d.ts → emitModerationEvent.d.ts} +0 -0
  192. /package/dist/api/com/atproto/admin/{getModerationActions.d.ts → getModerationEvent.d.ts} +0 -0
  193. /package/dist/api/com/atproto/admin/{getModerationReport.d.ts → queryModerationEvents.d.ts} +0 -0
  194. /package/dist/api/com/atproto/admin/{getModerationReports.d.ts → queryModerationStatuses.d.ts} +0 -0
  195. /package/dist/api/com/atproto/{admin/resolveModerationReports.d.ts → temp/fetchLabels.d.ts} +0 -0
@@ -1,13 +1,13 @@
1
- import { Selectable } from 'kysely';
2
1
  import { CID } from 'multiformats/cid';
3
2
  import { AtUri } from '@atproto/syntax';
4
3
  import { PrimaryDatabase } from '../../db';
5
- import { ModerationAction, ModerationReport } from '../../db/tables/moderation';
6
4
  import { ModerationViews } from './views';
7
5
  import { ImageUriBuilder } from '../../image/uri';
6
+ import { Main as StrongRef } from '../../lexicon/types/com/atproto/repo/strongRef';
8
7
  import { ImageInvalidator } from '../../image/invalidator';
9
8
  import { RepoRef, RepoBlobRef } from '../../lexicon/types/com/atproto/admin/defs';
10
- import { Main as StrongRef } from '../../lexicon/types/com/atproto/repo/strongRef';
9
+ import { ModEventType, ModerationEventRow, ModerationEventRowWithHandle, ModerationSubjectStatusRow, ReversibleModerationEvent, SubjectInfo } from './types';
10
+ import { ModerationEvent } from '../../db/tables/moderation';
11
11
  export declare class ModerationService {
12
12
  db: PrimaryDatabase;
13
13
  imgUriBuilder: ImageUriBuilder;
@@ -15,52 +15,52 @@ export declare class ModerationService {
15
15
  constructor(db: PrimaryDatabase, imgUriBuilder: ImageUriBuilder, imgInvalidator: ImageInvalidator);
16
16
  static creator(imgUriBuilder: ImageUriBuilder, imgInvalidator: ImageInvalidator): (db: PrimaryDatabase) => ModerationService;
17
17
  views: ModerationViews;
18
- getAction(id: number): Promise<ModerationActionRow | undefined>;
19
- getActionOrThrow(id: number): Promise<ModerationActionRow>;
20
- getActions(opts: {
18
+ getEvent(id: number): Promise<ModerationEventRow | undefined>;
19
+ getEventOrThrow(id: number): Promise<ModerationEventRow>;
20
+ getEvents(opts: {
21
21
  subject?: string;
22
+ createdBy?: string;
22
23
  limit: number;
23
24
  cursor?: string;
24
- }): Promise<ModerationActionRow[]>;
25
- getReport(id: number): Promise<ModerationReportRow | undefined>;
26
- getReports(opts: {
27
- subject?: string;
28
- resolved?: boolean;
29
- actionType?: string;
30
- limit: number;
25
+ includeAllUserRecords: boolean;
26
+ types: ModerationEvent['action'][];
27
+ sortDirection?: 'asc' | 'desc';
28
+ }): Promise<{
31
29
  cursor?: string;
32
- ignoreSubjects?: string[];
33
- reverse?: boolean;
34
- reporters?: string[];
35
- actionedBy?: string;
36
- }): Promise<ModerationReportRowWithHandle[]>;
37
- getReportOrThrow(id: number): Promise<ModerationReportRow>;
38
- getCurrentActions(subject: {
30
+ events: ModerationEventRowWithHandle[];
31
+ }>;
32
+ getReport(id: number): Promise<ModerationEventRow | undefined>;
33
+ getCurrentStatus(subject: {
39
34
  did: string;
40
35
  } | {
41
36
  uri: AtUri;
42
37
  } | {
43
38
  cids: CID[];
44
39
  }): Promise<{
40
+ did: string;
45
41
  id: number;
46
- action: "com.atproto.admin.defs#takedown" | "com.atproto.admin.defs#flag" | "com.atproto.admin.defs#acknowledge" | "com.atproto.admin.defs#escalate";
47
- reason: string;
48
- createdBy: string;
49
42
  createdAt: string;
50
- durationInHours: number | null;
51
- createLabelVals: string | null;
52
- negateLabelVals: string | null;
53
- subjectCid: string | null;
54
- subjectDid: string;
55
- subjectType: "com.atproto.repo.strongRef" | "com.atproto.admin.defs#repoRef";
56
- subjectUri: string | null;
57
- reversedAt: string | null;
58
- reversedBy: string | null;
59
- reversedReason: string | null;
60
- expiresAt: string | null;
43
+ updatedAt: string;
44
+ reviewState: "com.atproto.admin.defs#reviewOpen" | "com.atproto.admin.defs#reviewEscalated" | "com.atproto.admin.defs#reviewClosed";
45
+ blobCids: string[] | null;
46
+ comment: string | null;
47
+ lastReportedAt: string | null;
48
+ lastReviewedAt: string | null;
49
+ muteUntil: string | null;
50
+ lastReviewedBy: string | null;
51
+ takendown: boolean;
52
+ suspendUntil: string | null;
53
+ recordCid: string | null;
54
+ recordPath: string;
61
55
  }[]>;
62
- logAction(info: {
63
- action: ModerationActionRow['action'];
56
+ buildSubjectInfo(subject: {
57
+ did: string;
58
+ } | {
59
+ uri: AtUri;
60
+ cid: CID;
61
+ }, subjectBlobCids?: CID[]): SubjectInfo;
62
+ logEvent(info: {
63
+ event: ModEventType;
64
64
  subject: {
65
65
  did: string;
66
66
  } | {
@@ -68,19 +68,31 @@ export declare class ModerationService {
68
68
  cid: CID;
69
69
  };
70
70
  subjectBlobCids?: CID[];
71
- reason: string;
72
- createLabelVals?: string[];
73
- negateLabelVals?: string[];
74
71
  createdBy: string;
75
72
  createdAt?: Date;
76
- durationInHours?: number;
77
- }): Promise<ModerationActionRow>;
78
- getActionsDueForReversal(): Promise<ModerationActionRow[]>;
79
- revertAction({ id, createdBy, createdAt, reason, }: ReversibleModerationAction): Promise<{
80
- result: ModerationActionRow;
73
+ }): Promise<ModerationEventRow>;
74
+ getLastReversibleEventForSubject({ did, muteUntil, recordPath, suspendUntil, }: ModerationSubjectStatusRow): Promise<{
75
+ id: number;
76
+ createdBy: string;
77
+ createdAt: string;
78
+ comment: string | null;
79
+ createLabelVals: string | null;
80
+ negateLabelVals: string | null;
81
+ durationInHours: number | null;
82
+ action: "com.atproto.admin.defs#modEventTakedown" | "com.atproto.admin.defs#modEventReverseTakedown" | "com.atproto.admin.defs#modEventComment" | "com.atproto.admin.defs#modEventReport" | "com.atproto.admin.defs#modEventLabel" | "com.atproto.admin.defs#modEventAcknowledge" | "com.atproto.admin.defs#modEventEscalate" | "com.atproto.admin.defs#modEventMute" | "com.atproto.admin.defs#modEventEmail";
83
+ subjectCid: string | null;
84
+ subjectDid: string;
85
+ subjectType: "com.atproto.repo.strongRef" | "com.atproto.admin.defs#repoRef";
86
+ subjectUri: string | null;
87
+ expiresAt: string | null;
88
+ meta: Record<string, string | boolean> | null;
89
+ legacyRefId: number | null;
90
+ } | null | undefined>;
91
+ getSubjectsDueForReversal(): Promise<ModerationSubjectStatusRow[]>;
92
+ revertState({ createdBy, createdAt, comment, action, subject, }: ReversibleModerationEvent): Promise<{
93
+ result: ModerationEventRow;
81
94
  restored?: TakedownSubjects;
82
95
  }>;
83
- logReverseAction(info: ReversibleModerationAction): Promise<ModerationActionRow>;
84
96
  takedownRepo(info: {
85
97
  takedownId: number;
86
98
  did: string;
@@ -97,14 +109,8 @@ export declare class ModerationService {
97
109
  reverseTakedownRecord(info: {
98
110
  uri: AtUri;
99
111
  }): Promise<void>;
100
- resolveReports(info: {
101
- reportIds: number[];
102
- actionId: number;
103
- createdBy: string;
104
- createdAt?: Date;
105
- }): Promise<void>;
106
112
  report(info: {
107
- reasonType: ModerationReportRow['reasonType'];
113
+ reasonType: NonNullable<ModerationEventRow['meta']>['reportType'];
108
114
  reason?: string;
109
115
  subject: {
110
116
  did: string;
@@ -114,28 +120,85 @@ export declare class ModerationService {
114
120
  };
115
121
  reportedBy: string;
116
122
  createdAt?: Date;
117
- }): Promise<ModerationReportRow>;
123
+ }): Promise<ModerationEventRow>;
124
+ getSubjectStatuses({ cursor, limit, takendown, reviewState, reviewedAfter, reviewedBefore, reportedAfter, reportedBefore, includeMuted, ignoreSubjects, sortDirection, lastReviewedBy, sortField, subject, }: {
125
+ cursor?: string;
126
+ limit?: number;
127
+ takendown?: boolean;
128
+ reviewedBefore?: string;
129
+ reviewState?: ModerationSubjectStatusRow['reviewState'];
130
+ reviewedAfter?: string;
131
+ reportedAfter?: string;
132
+ reportedBefore?: string;
133
+ includeMuted?: boolean;
134
+ subject?: string;
135
+ ignoreSubjects?: string[];
136
+ sortDirection: 'asc' | 'desc';
137
+ lastReviewedBy?: string;
138
+ sortField: 'lastReviewedAt' | 'lastReportedAt';
139
+ }): Promise<{
140
+ statuses: (import("kysely").Selection<{
141
+ record: import("../../db/tables/record").Record;
142
+ subscription: import("../../db/tables/subscription").Subscription;
143
+ post: import("../../db/tables/post").Post;
144
+ duplicate_record: import("../../db/tables/duplicate-record").DuplicateRecord;
145
+ profile: import("../../db/tables/profile").Profile;
146
+ profile_agg: import("../../db/tables/profile-agg").ProfileAgg;
147
+ post_embed_image: import("../../db/tables/post-embed").PostEmbedImage;
148
+ post_embed_external: import("../../db/tables/post-embed").PostEmbedExternal;
149
+ post_embed_record: import("../../db/tables/post-embed").PostEmbedRecord;
150
+ post_agg: import("../../db/tables/post-agg").PostAgg;
151
+ repost: import("../../db/tables/repost").Repost;
152
+ thread_gate: import("../../db/tables/thread-gate").ThreadGate;
153
+ feed_item: import("../../db/tables/feed-item").FeedItem;
154
+ follow: import("../../db/tables/follow").Follow;
155
+ like: import("../../db/tables/like").Like;
156
+ list: import("../../db/tables/list").List;
157
+ list_item: import("../../db/tables/list-item").ListItem;
158
+ list_mute: import("../../db/tables/list-mute").ListMute;
159
+ list_block: import("../../db/tables/list-block").ListBlock;
160
+ mute: import("../../db/tables/mute").Mute;
161
+ actor_block: import("../../db/tables/actor-block").ActorBlock;
162
+ feed_generator: import("../../db/tables/feed-generator").FeedGenerator;
163
+ actor: import("kysely/dist/cjs/util/type-utils").Nullable<import("../../db/tables/actor").Actor>;
164
+ actor_state: import("../../db/tables/actor-state").ActorState;
165
+ actor_sync: import("../../db/tables/actor-sync").ActorSync;
166
+ notification: import("../../db/tables/notification").Notification;
167
+ notification_push_token: import("../../db/tables/notification-push-token").NotificationPushToken;
168
+ did_cache: import("../../db/tables/did-cache").DidCache;
169
+ label: import("../../db/tables/label").Label;
170
+ moderation_event: ModerationEvent;
171
+ moderation_subject_status: import("../../db/tables/moderation").ModerationSubjectStatus;
172
+ algo_whats_hot_view: import("../../db/tables/algo").AlgoWhatsHotView;
173
+ view_param: import("../../db/tables/view-param").ViewParam;
174
+ suggested_follow: import("../../db/tables/suggested-follow").SuggestedFollow;
175
+ suggested_feed: import("../../db/tables/suggested-feed").SuggestedFeed;
176
+ }, "actor" | "moderation_subject_status", "actor.handle as handle"> & {
177
+ did: string;
178
+ id: number;
179
+ createdAt: string;
180
+ updatedAt: string;
181
+ reviewState: "com.atproto.admin.defs#reviewOpen" | "com.atproto.admin.defs#reviewEscalated" | "com.atproto.admin.defs#reviewClosed";
182
+ blobCids: string[] | null;
183
+ comment: string | null;
184
+ lastReportedAt: string | null;
185
+ lastReviewedAt: string | null;
186
+ muteUntil: string | null;
187
+ lastReviewedBy: string | null;
188
+ takendown: boolean;
189
+ suspendUntil: string | null;
190
+ recordCid: string | null;
191
+ recordPath: string;
192
+ })[];
193
+ cursor: string | undefined;
194
+ }>;
195
+ isSubjectTakendown(subject: {
196
+ did: string;
197
+ } | {
198
+ uri: AtUri;
199
+ }): Promise<boolean>;
118
200
  }
119
201
  export declare type TakedownSubjects = {
120
202
  did: string;
121
203
  subjects: (RepoRef | RepoBlobRef | StrongRef)[];
122
204
  };
123
- export declare type ModerationActionRow = Selectable<ModerationAction>;
124
- export declare type ReversibleModerationAction = Pick<ModerationActionRow, 'id' | 'createdBy' | 'reason'> & {
125
- createdAt?: Date;
126
- };
127
- export declare type ModerationReportRow = Selectable<ModerationReport>;
128
- export declare type ModerationReportRowWithHandle = ModerationReportRow & {
129
- handle?: string | null;
130
- };
131
- export declare type SubjectInfo = {
132
- subjectType: 'com.atproto.admin.defs#repoRef';
133
- subjectDid: string;
134
- subjectUri: null;
135
- subjectCid: null;
136
- } | {
137
- subjectType: 'com.atproto.repo.strongRef';
138
- subjectDid: string;
139
- subjectUri: string;
140
- subjectCid: string;
141
- };
@@ -0,0 +1,36 @@
1
+ import { Cursor, GenericKeyset } from '../../db/pagination';
2
+ declare type StatusKeysetParam = {
3
+ lastReviewedAt: string | null;
4
+ lastReportedAt: string | null;
5
+ id: number;
6
+ };
7
+ export declare class StatusKeyset extends GenericKeyset<StatusKeysetParam, Cursor> {
8
+ labelResult(result: StatusKeysetParam): Cursor;
9
+ labeledResultToCursor(labeled: Cursor): {
10
+ primary: string;
11
+ secondary: string;
12
+ };
13
+ cursorToLabeledResult(cursor: Cursor): {
14
+ primary: string;
15
+ secondary: string;
16
+ };
17
+ unpackCursor(cursorStr?: string): Cursor | undefined;
18
+ getSql(labeled?: Cursor, direction?: 'asc' | 'desc'): import("kysely").RawBuilder<unknown> | undefined;
19
+ }
20
+ declare type TimeIdKeysetParam = {
21
+ id: number;
22
+ createdAt: string;
23
+ };
24
+ declare type TimeIdResult = TimeIdKeysetParam;
25
+ export declare class TimeIdKeyset extends GenericKeyset<TimeIdKeysetParam, Cursor> {
26
+ labelResult(result: TimeIdResult): Cursor;
27
+ labeledResultToCursor(labeled: Cursor): {
28
+ primary: string;
29
+ secondary: string;
30
+ };
31
+ cursorToLabeledResult(cursor: Cursor): {
32
+ primary: string;
33
+ secondary: string;
34
+ };
35
+ }
36
+ export {};
@@ -0,0 +1,13 @@
1
+ import { AtUri } from '@atproto/syntax';
2
+ import { PrimaryDatabase } from '../../db';
3
+ import { ModerationSubjectStatus } from '../../db/tables/moderation';
4
+ import { ModerationEventRow } from './types';
5
+ import { CID } from 'multiformats/cid';
6
+ export declare const adjustModerationSubjectStatus: (db: PrimaryDatabase, moderationEvent: ModerationEventRow, blobCids?: CID[]) => Promise<import("kysely").InsertResult | null>;
7
+ declare type ModerationSubjectStatusFilter = Pick<ModerationSubjectStatus, 'did'> | Pick<ModerationSubjectStatus, 'did' | 'recordPath'> | Pick<ModerationSubjectStatus, 'did' | 'recordPath' | 'recordCid'>;
8
+ export declare const getModerationSubjectStatus: (db: PrimaryDatabase, filters: ModerationSubjectStatusFilter) => Promise<{} | undefined>;
9
+ export declare const getStatusIdentifierFromSubject: (subject: string | AtUri) => {
10
+ did: string;
11
+ recordPath: string;
12
+ };
13
+ export {};
@@ -0,0 +1,35 @@
1
+ import { Selectable } from 'kysely';
2
+ import { ModerationEvent, ModerationSubjectStatus } from '../../db/tables/moderation';
3
+ import { AtUri } from '@atproto/syntax';
4
+ import { CID } from 'multiformats/cid';
5
+ import { ComAtprotoAdminDefs } from '@atproto/api';
6
+ export declare type SubjectInfo = {
7
+ subjectType: 'com.atproto.admin.defs#repoRef';
8
+ subjectDid: string;
9
+ subjectUri: null;
10
+ subjectCid: null;
11
+ } | {
12
+ subjectType: 'com.atproto.repo.strongRef';
13
+ subjectDid: string;
14
+ subjectUri: string;
15
+ subjectCid: string;
16
+ };
17
+ export declare type ModerationEventRow = Selectable<ModerationEvent>;
18
+ export declare type ReversibleModerationEvent = Pick<ModerationEventRow, 'createdBy' | 'comment' | 'action'> & {
19
+ createdAt?: Date;
20
+ subject: {
21
+ did: string;
22
+ } | {
23
+ uri: AtUri;
24
+ cid: CID;
25
+ };
26
+ };
27
+ export declare type ModerationEventRowWithHandle = ModerationEventRow & {
28
+ subjectHandle?: string | null;
29
+ creatorHandle?: string | null;
30
+ };
31
+ export declare type ModerationSubjectStatusRow = Selectable<ModerationSubjectStatus>;
32
+ export declare type ModerationSubjectStatusRowWithHandle = ModerationSubjectStatusRow & {
33
+ handle: string | null;
34
+ };
35
+ export declare type ModEventType = ComAtprotoAdminDefs.ModEventTakedown | ComAtprotoAdminDefs.ModEventAcknowledge | ComAtprotoAdminDefs.ModEventEscalate | ComAtprotoAdminDefs.ModEventComment | ComAtprotoAdminDefs.ModEventLabel | ComAtprotoAdminDefs.ModEventReport | ComAtprotoAdminDefs.ModEventMute | ComAtprotoAdminDefs.ModEventReverseTakedown;
@@ -1,37 +1,41 @@
1
- import { Selectable } from 'kysely';
2
1
  import { BlobRef } from '@atproto/lexicon';
3
2
  import { Database } from '../../db';
4
3
  import { Actor } from '../../db/tables/actor';
5
4
  import { Record as RecordRow } from '../../db/tables/record';
6
- import { ModerationAction } from '../../db/tables/moderation';
7
- import { RepoView, RepoViewDetail, RecordView, RecordViewDetail, ActionView, ActionViewDetail, ReportView, ReportViewDetail, BlobView } from '../../lexicon/types/com/atproto/admin/defs';
5
+ import { ModEventView, RepoView, RepoViewDetail, RecordView, RecordViewDetail, ReportViewDetail, BlobView, SubjectStatusView, ModEventViewDetail } from '../../lexicon/types/com/atproto/admin/defs';
8
6
  import { OutputSchema as ReportOutput } from '../../lexicon/types/com/atproto/moderation/createReport';
9
7
  import { Label } from '../../lexicon/types/com/atproto/label/defs';
10
- import { ModerationReportRowWithHandle } from '.';
8
+ import { ModerationEventRowWithHandle, ModerationSubjectStatusRowWithHandle } from './types';
11
9
  export declare class ModerationViews {
12
10
  private db;
13
11
  constructor(db: Database);
14
12
  repo(result: RepoResult): Promise<RepoView>;
15
13
  repo(result: RepoResult[]): Promise<RepoView[]>;
14
+ event(result: EventResult): Promise<ModEventView>;
15
+ event(result: EventResult[]): Promise<ModEventView[]>;
16
+ eventDetail(result: EventResult): Promise<ModEventViewDetail>;
16
17
  repoDetail(result: RepoResult): Promise<RepoViewDetail>;
17
18
  record(result: RecordResult): Promise<RecordView>;
18
19
  record(result: RecordResult[]): Promise<RecordView[]>;
19
20
  recordDetail(result: RecordResult): Promise<RecordViewDetail>;
20
- action(result: ActionResult): Promise<ActionView>;
21
- action(result: ActionResult[]): Promise<ActionView[]>;
22
- actionDetail(result: ActionResult): Promise<ActionViewDetail>;
23
- report(result: ReportResult): Promise<ReportView>;
24
- report(result: ReportResult[]): Promise<ReportView[]>;
25
21
  reportPublic(report: ReportResult): ReportOutput;
26
- reportDetail(result: ReportResult): Promise<ReportViewDetail>;
27
22
  subject(result: SubjectResult): Promise<SubjectView>;
28
23
  blob(blobs: BlobRef[]): Promise<BlobView[]>;
29
24
  labels(subject: string, includeNeg?: boolean): Promise<Label[]>;
25
+ getSubjectStatus(subject: {
26
+ did: string;
27
+ recordPath?: string;
28
+ } | {
29
+ did: string;
30
+ recordPath?: string;
31
+ }[]): Promise<ModerationSubjectStatusRowWithHandle[]>;
32
+ subjectStatus(result: ModerationSubjectStatusRowWithHandle): SubjectStatusView;
33
+ subjectStatus(result: ModerationSubjectStatusRowWithHandle[]): SubjectStatusView[];
30
34
  }
31
35
  declare type RepoResult = Actor;
32
- declare type ActionResult = Selectable<ModerationAction>;
33
- declare type ReportResult = ModerationReportRowWithHandle;
36
+ declare type EventResult = ModerationEventRowWithHandle;
37
+ declare type ReportResult = ModerationEventRowWithHandle;
34
38
  declare type RecordResult = RecordRow;
35
- declare type SubjectResult = Pick<ActionResult & ReportResult, 'id' | 'subjectType' | 'subjectDid' | 'subjectUri' | 'subjectCid'>;
36
- declare type SubjectView = ActionViewDetail['subject'] & ReportViewDetail['subject'];
39
+ declare type SubjectResult = Pick<EventResult & ReportResult, 'id' | 'subjectType' | 'subjectDid' | 'subjectUri' | 'subjectCid'>;
40
+ declare type SubjectView = ModEventViewDetail['subject'] & ReportViewDetail['subject'];
37
41
  export {};
@@ -1 +1 @@
1
- export declare const debugCatch: <Func extends (...args: any[]) => any>(fn: Func) => (...args: any[]) => Promise<any>;
1
+ export declare const debugCatch: <Func extends (...args: any[]) => any>(fn: Func) => (...args: Parameters<Func>) => Promise<ReturnType<Func>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/bsky",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "license": "MIT",
5
5
  "description": "Reference implementation of app.bsky App View (Bluesky API)",
6
6
  "keywords": [
@@ -32,17 +32,17 @@
32
32
  "pg": "^8.10.0",
33
33
  "pino": "^8.15.0",
34
34
  "pino-http": "^8.2.1",
35
- "sharp": "^0.31.2",
35
+ "sharp": "^0.32.6",
36
36
  "typed-emitter": "^2.1.0",
37
37
  "uint8arrays": "3.0.0",
38
- "@atproto/api": "^0.6.23",
38
+ "@atproto/api": "^0.6.24",
39
39
  "@atproto/common": "^0.3.3",
40
- "@atproto/crypto": "^0.2.3",
41
- "@atproto/syntax": "^0.1.4",
42
- "@atproto/identity": "^0.3.1",
43
- "@atproto/lexicon": "^0.3.0",
44
- "@atproto/repo": "^0.3.4",
45
- "@atproto/xrpc-server": "^0.4.0"
40
+ "@atproto/crypto": "^0.3.0",
41
+ "@atproto/syntax": "^0.1.5",
42
+ "@atproto/identity": "^0.3.2",
43
+ "@atproto/lexicon": "^0.3.1",
44
+ "@atproto/repo": "^0.3.6",
45
+ "@atproto/xrpc-server": "^0.4.2"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@did-plc/server": "^0.0.1",
@@ -51,13 +51,12 @@
51
51
  "@types/express-serve-static-core": "^4.17.36",
52
52
  "@types/pg": "^8.6.6",
53
53
  "@types/qs": "^6.9.7",
54
- "@types/sharp": "^0.31.0",
55
54
  "axios": "^0.27.2",
56
- "@atproto/api": "^0.6.23",
57
- "@atproto/dev-env": "^0.2.14",
58
- "@atproto/lex-cli": "^0.2.4",
59
- "@atproto/pds": "^0.3.2",
60
- "@atproto/xrpc": "^0.4.0"
55
+ "@atproto/api": "^0.6.24",
56
+ "@atproto/dev-env": "^0.2.16",
57
+ "@atproto/lex-cli": "^0.2.5",
58
+ "@atproto/pds": "^0.3.4",
59
+ "@atproto/xrpc": "^0.4.1"
61
60
  },
62
61
  "scripts": {
63
62
  "codegen": "lex gen-server ./src/lexicon ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/*",
@@ -42,12 +42,12 @@ const skeleton = async (
42
42
  ctx: Context,
43
43
  ): Promise<SkeletonState> => {
44
44
  const { db } = ctx
45
- const { limit, cursor, viewer } = params
45
+ const { viewer } = params
46
+ const alreadyIncluded = parseCursor(params.cursor)
46
47
  const { ref } = db.db.dynamic
47
- let suggestionsQb = db.db
48
+ const suggestions = await db.db
48
49
  .selectFrom('suggested_follow')
49
50
  .innerJoin('actor', 'actor.did', 'suggested_follow.did')
50
- .innerJoin('profile_agg', 'profile_agg.did', 'actor.did')
51
51
  .where(notSoftDeletedClause(ref('actor')))
52
52
  .where('suggested_follow.did', '!=', viewer ?? '')
53
53
  .whereNotExists((qb) =>
@@ -57,27 +57,30 @@ const skeleton = async (
57
57
  .where('creator', '=', viewer ?? '')
58
58
  .whereRef('subjectDid', '=', ref('actor.did')),
59
59
  )
60
+ .if(alreadyIncluded.length > 0, (qb) =>
61
+ qb.where('suggested_follow.order', 'not in', alreadyIncluded),
62
+ )
60
63
  .selectAll()
61
- .select('profile_agg.postsCount as postsCount')
62
- .limit(limit)
63
64
  .orderBy('suggested_follow.order', 'asc')
65
+ .execute()
64
66
 
65
- if (cursor) {
66
- const cursorRow = await db.db
67
- .selectFrom('suggested_follow')
68
- .where('did', '=', cursor)
69
- .selectAll()
70
- .executeTakeFirst()
71
- if (cursorRow) {
72
- suggestionsQb = suggestionsQb.where(
73
- 'suggested_follow.order',
74
- '>',
75
- cursorRow.order,
76
- )
77
- }
78
- }
79
- const suggestions = await suggestionsQb.execute()
80
- return { params, suggestions, cursor: suggestions.at(-1)?.did }
67
+ // always include first two
68
+ const firstTwo = suggestions.filter(
69
+ (row) => row.order === 1 || row.order === 2,
70
+ )
71
+ const rest = suggestions.filter((row) => row.order !== 1 && row.order !== 2)
72
+ const limited = firstTwo.concat(shuffle(rest)).slice(0, params.limit)
73
+
74
+ // if the result set ends up getting larger, consider using a seed included in the cursor for for the randomized shuffle
75
+ const cursor =
76
+ limited.length > 0
77
+ ? limited
78
+ .map((row) => row.order.toString())
79
+ .concat(alreadyIncluded.map((id) => id.toString()))
80
+ .join(':')
81
+ : undefined
82
+
83
+ return { params, suggestions: limited, cursor }
81
84
  }
82
85
 
83
86
  const hydration = async (state: SkeletonState, ctx: Context) => {
@@ -110,6 +113,27 @@ const presentation = (state: HydrationState) => {
110
113
  return { actors: suggestedActors, cursor }
111
114
  }
112
115
 
116
+ const parseCursor = (cursor?: string): number[] => {
117
+ if (!cursor) {
118
+ return []
119
+ }
120
+ try {
121
+ return cursor
122
+ .split(':')
123
+ .map((id) => parseInt(id, 10))
124
+ .filter((id) => !isNaN(id))
125
+ } catch {
126
+ return []
127
+ }
128
+ }
129
+
130
+ const shuffle = <T>(arr: T[]): T[] => {
131
+ return arr
132
+ .map((value) => ({ value, sort: Math.random() }))
133
+ .sort((a, b) => a.sort - b.sort)
134
+ .map(({ value }) => value)
135
+ }
136
+
113
137
  type Context = {
114
138
  db: Database
115
139
  actorService: ActorService
@@ -1,4 +1,5 @@
1
1
  import { InvalidRequestError } from '@atproto/xrpc-server'
2
+ import { mapDefined } from '@atproto/common'
2
3
  import { Server } from '../../../../lexicon'
3
4
  import AppContext from '../../../../context'
4
5
  import { TimeCidKeyset, paginate } from '../../../../db/pagination'
@@ -42,7 +43,7 @@ export default function (server: Server, ctx: AppContext) {
42
43
  throw new InvalidRequestError(`Actor not found: ${actor}`)
43
44
  }
44
45
 
45
- const feeds = feedsRes.map((row) => {
46
+ const feeds = mapDefined(feedsRes, (row) => {
46
47
  const feed = {
47
48
  ...row,
48
49
  viewer: viewer ? { like: row.viewerLike } : undefined,
@@ -107,9 +107,7 @@ const noPostBlocks = (state: HydrationState) => {
107
107
  const presentation = (state: HydrationState, ctx: Context) => {
108
108
  const { feedService } = ctx
109
109
  const { feedItems, cursor, params } = state
110
- const feed = feedService.views.formatFeed(feedItems, state, {
111
- viewer: params.viewer,
112
- })
110
+ const feed = feedService.views.formatFeed(feedItems, state, params.viewer)
113
111
  return { feed, cursor }
114
112
  }
115
113
 
@@ -147,9 +147,7 @@ const noBlocksOrMutedReposts = (state: HydrationState) => {
147
147
  const presentation = (state: HydrationState, ctx: Context) => {
148
148
  const { feedService } = ctx
149
149
  const { feedItems, cursor, params } = state
150
- const feed = feedService.views.formatFeed(feedItems, state, {
151
- viewer: params.viewer,
152
- })
150
+ const feed = feedService.views.formatFeed(feedItems, state, params.viewer)
153
151
  return { feed, cursor }
154
152
  }
155
153
 
@@ -33,7 +33,7 @@ export default function (server: Server, ctx: AppContext) {
33
33
  presentation,
34
34
  )
35
35
  server.app.bsky.feed.getFeed({
36
- auth: ctx.authVerifierAnyAudience,
36
+ auth: ctx.authOptionalVerifierAnyAudience,
37
37
  handler: async ({ params, auth, req }) => {
38
38
  const db = ctx.db.getReplica()
39
39
  const feedService = ctx.services.feed(db)
@@ -98,22 +98,22 @@ const hydration = async (state: SkeletonState, ctx: Context) => {
98
98
 
99
99
  const noBlocksOrMutes = (state: HydrationState) => {
100
100
  const { viewer } = state.params
101
- state.feedItems = state.feedItems.filter(
102
- (item) =>
101
+ state.feedItems = state.feedItems.filter((item) => {
102
+ if (!viewer) return true
103
+ return (
103
104
  !state.bam.block([viewer, item.postAuthorDid]) &&
104
105
  !state.bam.block([viewer, item.originatorDid]) &&
105
106
  !state.bam.mute([viewer, item.postAuthorDid]) &&
106
- !state.bam.mute([viewer, item.originatorDid]),
107
- )
107
+ !state.bam.mute([viewer, item.originatorDid])
108
+ )
109
+ })
108
110
  return state
109
111
  }
110
112
 
111
113
  const presentation = (state: HydrationState, ctx: Context) => {
112
114
  const { feedService } = ctx
113
115
  const { feedItems, cursor, passthrough, params } = state
114
- const feed = feedService.views.formatFeed(feedItems, state, {
115
- viewer: params.viewer,
116
- })
116
+ const feed = feedService.views.formatFeed(feedItems, state, params.viewer)
117
117
  return {
118
118
  feed,
119
119
  cursor,
@@ -130,7 +130,7 @@ type Context = {
130
130
  authorization?: string
131
131
  }
132
132
 
133
- type Params = GetFeedParams & { viewer: string }
133
+ type Params = GetFeedParams & { viewer: string | null }
134
134
 
135
135
  type SkeletonState = {
136
136
  params: Params
@@ -55,6 +55,9 @@ export default function (server: Server, ctx: AppContext) {
55
55
  feedInfo,
56
56
  profiles,
57
57
  )
58
+ if (!feedView) {
59
+ throw new InvalidRequestError('could not find feed')
60
+ }
58
61
 
59
62
  return {
60
63
  encoding: 'application/json',