@atproto/bsky 0.0.21 → 0.0.22

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 (42) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/api/com/atproto/moderation/util.d.ts +1 -1
  3. package/dist/db/index.js +17 -1
  4. package/dist/db/index.js.map +3 -3
  5. package/dist/db/migrations/20231213T181744386Z-moderation-subject-appeal.d.ts +3 -0
  6. package/dist/db/migrations/index.d.ts +1 -0
  7. package/dist/db/tables/moderation.d.ts +3 -1
  8. package/dist/index.js +213 -32
  9. package/dist/index.js.map +3 -3
  10. package/dist/lexicon/index.d.ts +1 -0
  11. package/dist/lexicon/lexicons.d.ts +35 -0
  12. package/dist/lexicon/types/com/atproto/admin/defs.d.ts +10 -1
  13. package/dist/lexicon/types/com/atproto/admin/queryModerationStatuses.d.ts +1 -0
  14. package/dist/lexicon/types/com/atproto/admin/sendEmail.d.ts +1 -0
  15. package/dist/lexicon/types/com/atproto/moderation/defs.d.ts +2 -1
  16. package/dist/services/feed/views.d.ts +1 -1
  17. package/dist/services/moderation/index.d.ts +7 -2
  18. package/package.json +5 -5
  19. package/src/api/app/bsky/feed/getPostThread.ts +4 -3
  20. package/src/api/app/bsky/feed/getTimeline.ts +54 -0
  21. package/src/api/com/atproto/admin/queryModerationStatuses.ts +2 -0
  22. package/src/api/com/atproto/moderation/createReport.ts +14 -3
  23. package/src/api/com/atproto/moderation/util.ts +2 -0
  24. package/src/api/health.ts +14 -0
  25. package/src/auto-moderator/index.ts +19 -0
  26. package/src/db/migrations/20231213T181744386Z-moderation-subject-appeal.ts +23 -0
  27. package/src/db/migrations/index.ts +1 -0
  28. package/src/db/tables/moderation.ts +3 -0
  29. package/src/lexicon/index.ts +1 -0
  30. package/src/lexicon/lexicons.ts +40 -0
  31. package/src/lexicon/types/com/atproto/admin/defs.ts +28 -0
  32. package/src/lexicon/types/com/atproto/admin/queryModerationStatuses.ts +2 -0
  33. package/src/lexicon/types/com/atproto/admin/sendEmail.ts +2 -0
  34. package/src/lexicon/types/com/atproto/moderation/defs.ts +3 -0
  35. package/src/services/feed/views.ts +6 -3
  36. package/src/services/moderation/index.ts +9 -0
  37. package/src/services/moderation/status.ts +24 -0
  38. package/src/services/moderation/views.ts +2 -0
  39. package/tests/admin/moderation-appeals.test.ts +269 -0
  40. package/tests/auto-moderator/labeler.test.ts +39 -0
  41. package/tests/views/blocks.test.ts +14 -1
  42. package/tests/views/timeline.test.ts +14 -0
@@ -0,0 +1,3 @@
1
+ import { Kysely } from 'kysely';
2
+ export declare function up(db: Kysely<unknown>): Promise<void>;
3
+ export declare function down(db: Kysely<unknown>): Promise<void>;
@@ -28,3 +28,4 @@ export * as _20230920T213858047Z from './20230920T213858047Z-add-tags-to-post';
28
28
  export * as _20230929T192920807Z from './20230929T192920807Z-record-cursor-indexes';
29
29
  export * as _20231003T202833377Z from './20231003T202833377Z-create-moderation-subject-status';
30
30
  export * as _20231205T000257238Z from './20231205T000257238Z-remove-did-cache';
31
+ export * as _20231213T181744386Z from './20231213T181744386Z-moderation-subject-appeal';
@@ -4,7 +4,7 @@ export declare const eventTableName = "moderation_event";
4
4
  export declare const subjectStatusTableName = "moderation_subject_status";
5
5
  export interface ModerationEvent {
6
6
  id: Generated<number>;
7
- action: 'com.atproto.admin.defs#modEventTakedown' | 'com.atproto.admin.defs#modEventAcknowledge' | 'com.atproto.admin.defs#modEventEscalate' | 'com.atproto.admin.defs#modEventComment' | 'com.atproto.admin.defs#modEventLabel' | 'com.atproto.admin.defs#modEventReport' | 'com.atproto.admin.defs#modEventMute' | 'com.atproto.admin.defs#modEventReverseTakedown' | 'com.atproto.admin.defs#modEventEmail';
7
+ action: 'com.atproto.admin.defs#modEventTakedown' | 'com.atproto.admin.defs#modEventAcknowledge' | 'com.atproto.admin.defs#modEventEscalate' | 'com.atproto.admin.defs#modEventComment' | 'com.atproto.admin.defs#modEventLabel' | 'com.atproto.admin.defs#modEventReport' | 'com.atproto.admin.defs#modEventMute' | 'com.atproto.admin.defs#modEventReverseTakedown' | 'com.atproto.admin.defs#modEventEmail' | 'com.atproto.admin.defs#modEventResolveAppeal';
8
8
  subjectType: 'com.atproto.admin.defs#repoRef' | 'com.atproto.repo.strongRef';
9
9
  subjectDid: string;
10
10
  subjectUri: string | null;
@@ -31,9 +31,11 @@ export interface ModerationSubjectStatus {
31
31
  lastReviewedBy: string | null;
32
32
  lastReviewedAt: string | null;
33
33
  lastReportedAt: string | null;
34
+ lastAppealedAt: string | null;
34
35
  muteUntil: string | null;
35
36
  suspendUntil: string | null;
36
37
  takendown: boolean;
38
+ appealed: boolean | null;
37
39
  comment: string | null;
38
40
  }
39
41
  export declare type PartialDB = {
package/dist/index.js CHANGED
@@ -27864,16 +27864,16 @@ var require_validate = __commonJS({
27864
27864
  const matches = RELATIVE_JSON_POINTER.exec($data);
27865
27865
  if (!matches)
27866
27866
  throw new Error(`Invalid JSON-pointer: ${$data}`);
27867
- const up31 = +matches[1];
27867
+ const up32 = +matches[1];
27868
27868
  jsonPointer = matches[2];
27869
27869
  if (jsonPointer === "#") {
27870
- if (up31 >= dataLevel)
27871
- throw new Error(errorMsg("property/index", up31));
27872
- return dataPathArr[dataLevel - up31];
27870
+ if (up32 >= dataLevel)
27871
+ throw new Error(errorMsg("property/index", up32));
27872
+ return dataPathArr[dataLevel - up32];
27873
27873
  }
27874
- if (up31 > dataLevel)
27875
- throw new Error(errorMsg("data", up31));
27876
- data = dataNames[dataLevel - up31];
27874
+ if (up32 > dataLevel)
27875
+ throw new Error(errorMsg("data", up32));
27876
+ data = dataNames[dataLevel - up32];
27877
27877
  if (!jsonPointer)
27878
27878
  return data;
27879
27879
  }
@@ -27886,8 +27886,8 @@ var require_validate = __commonJS({
27886
27886
  }
27887
27887
  }
27888
27888
  return expr;
27889
- function errorMsg(pointerType, up31) {
27890
- return `Cannot access ${pointerType} ${up31} levels up, current level is ${dataLevel}`;
27889
+ function errorMsg(pointerType, up32) {
27890
+ return `Cannot access ${pointerType} ${up32} levels up, current level is ${dataLevel}`;
27891
27891
  }
27892
27892
  }
27893
27893
  exports.getData = getData;
@@ -105995,6 +105995,11 @@ var AuthRequiredError = class extends XRPCError2 {
105995
105995
  super(401 /* AuthRequired */, errorMessage, customErrorName);
105996
105996
  }
105997
105997
  };
105998
+ var ForbiddenError = class extends XRPCError2 {
105999
+ constructor(errorMessage, customErrorName) {
106000
+ super(403 /* Forbidden */, errorMessage, customErrorName);
106001
+ }
106002
+ };
105998
106003
  var RateLimitExceededError = class extends XRPCError2 {
105999
106004
  constructor(status, errorMessage, customErrorName) {
106000
106005
  super(429 /* RateLimitExceeded */, errorMessage, customErrorName);
@@ -118840,6 +118845,9 @@ var skeleton = async (params2, ctx) => {
118840
118845
  if (algorithm && algorithm !== "reverse-chronological" /* ReverseChronological */) {
118841
118846
  throw new InvalidRequestError(`Unsupported algorithm: ${algorithm}`);
118842
118847
  }
118848
+ if (limit === 1 && !cursor) {
118849
+ return skeletonLimit1(params2, ctx);
118850
+ }
118843
118851
  const keyset = new FeedKeyset(ref("feed_item.sortAt"), ref("feed_item.cid"));
118844
118852
  const sortFrom = keyset.unpack(cursor)?.primary;
118845
118853
  let followQb = db.db.selectFrom("feed_item").innerJoin("follow", "follow.subjectDid", "feed_item.originatorDid").where("follow.creator", "=", viewer).innerJoin("post", "post.uri", "feed_item.postUri").where("feed_item.sortAt", ">", getFeedDateThreshold(sortFrom, 2)).selectAll("feed_item").select([
@@ -118881,6 +118889,28 @@ var skeleton = async (params2, ctx) => {
118881
118889
  cursor: keyset.packFromResult(feedItems)
118882
118890
  };
118883
118891
  };
118892
+ var skeletonLimit1 = async (params2, ctx) => {
118893
+ const { viewer } = params2;
118894
+ const { db } = ctx;
118895
+ const { ref } = db.db.dynamic;
118896
+ const creatorsQb = db.db.selectFrom("follow").where("creator", "=", viewer).select("subjectDid as did").unionAll(sql`select ${viewer} as did`);
118897
+ const feedItemsQb = db.db.selectFrom(creatorsQb.as("creator")).innerJoinLateral((eb) => {
118898
+ const keyset2 = new FeedKeyset(ref("feed_item.sortAt"), ref("feed_item.cid"));
118899
+ const creatorFeedItemQb = eb.selectFrom("feed_item").innerJoin("post", "post.uri", "feed_item.postUri").whereRef("feed_item.originatorDid", "=", "creator.did").where("feed_item.sortAt", ">", getFeedDateThreshold(void 0, 2)).selectAll("feed_item").select([
118900
+ "post.replyRoot",
118901
+ "post.replyParent",
118902
+ "post.creator as postAuthorDid"
118903
+ ]);
118904
+ return paginate(creatorFeedItemQb, { limit: 1, keyset: keyset2 }).as("result");
118905
+ }, (join) => join.onTrue()).selectAll("result");
118906
+ const keyset = new FeedKeyset(ref("result.sortAt"), ref("result.cid"));
118907
+ const feedItems = await paginate(feedItemsQb, { limit: 1, keyset }).execute();
118908
+ return {
118909
+ params: params2,
118910
+ feedItems,
118911
+ cursor: keyset.packFromResult(feedItems)
118912
+ };
118913
+ };
118884
118914
  var hydration = async (state, ctx) => {
118885
118915
  const { feedService } = ctx;
118886
118916
  const { params: params2, feedItems } = state;
@@ -119162,7 +119192,8 @@ var schemaDict = {
119162
119192
  "lex:com.atproto.admin.defs#modEventLabel",
119163
119193
  "lex:com.atproto.admin.defs#modEventAcknowledge",
119164
119194
  "lex:com.atproto.admin.defs#modEventEscalate",
119165
- "lex:com.atproto.admin.defs#modEventMute"
119195
+ "lex:com.atproto.admin.defs#modEventMute",
119196
+ "lex:com.atproto.admin.defs#modEventResolveAppeal"
119166
119197
  ]
119167
119198
  },
119168
119199
  subject: {
@@ -119296,9 +119327,18 @@ var schemaDict = {
119296
119327
  type: "string",
119297
119328
  format: "datetime"
119298
119329
  },
119330
+ lastAppealedAt: {
119331
+ type: "string",
119332
+ format: "datetime",
119333
+ description: "Timestamp referencing when the author of the subject appealed a moderation action"
119334
+ },
119299
119335
  takendown: {
119300
119336
  type: "boolean"
119301
119337
  },
119338
+ appealed: {
119339
+ type: "boolean",
119340
+ description: "True indicates that the a previously taken moderator action was appealed against, by the author of the content. False indicates last appeal was resolved by moderators."
119341
+ },
119302
119342
  suspendUntil: {
119303
119343
  type: "string",
119304
119344
  format: "datetime"
@@ -119772,6 +119812,16 @@ var schemaDict = {
119772
119812
  }
119773
119813
  }
119774
119814
  },
119815
+ modEventResolveAppeal: {
119816
+ type: "object",
119817
+ description: "Resolve appeal on a subject",
119818
+ properties: {
119819
+ comment: {
119820
+ type: "string",
119821
+ description: "Describe resolution."
119822
+ }
119823
+ }
119824
+ },
119775
119825
  modEventComment: {
119776
119826
  type: "object",
119777
119827
  description: "Add a comment to a subject",
@@ -119870,6 +119920,10 @@ var schemaDict = {
119870
119920
  subjectLine: {
119871
119921
  type: "string",
119872
119922
  description: "The subject line of the email sent to the user."
119923
+ },
119924
+ comment: {
119925
+ type: "string",
119926
+ description: "Additional comment about the outgoing comm."
119873
119927
  }
119874
119928
  }
119875
119929
  }
@@ -120404,6 +120458,10 @@ var schemaDict = {
120404
120458
  type: "boolean",
120405
120459
  description: "Get subjects that were taken down"
120406
120460
  },
120461
+ appealed: {
120462
+ type: "boolean",
120463
+ description: "Get subjects in unresolved appealed status"
120464
+ },
120407
120465
  limit: {
120408
120466
  type: "integer",
120409
120467
  minimum: 1,
@@ -120513,6 +120571,10 @@ var schemaDict = {
120513
120571
  senderDid: {
120514
120572
  type: "string",
120515
120573
  format: "did"
120574
+ },
120575
+ comment: {
120576
+ type: "string",
120577
+ description: "Additional comment by the sender that won't be used in the email itself but helpful to provide more context for moderators/reviewers"
120516
120578
  }
120517
120579
  }
120518
120580
  }
@@ -120972,7 +121034,8 @@ var schemaDict = {
120972
121034
  "com.atproto.moderation.defs#reasonMisleading",
120973
121035
  "com.atproto.moderation.defs#reasonSexual",
120974
121036
  "com.atproto.moderation.defs#reasonRude",
120975
- "com.atproto.moderation.defs#reasonOther"
121037
+ "com.atproto.moderation.defs#reasonOther",
121038
+ "com.atproto.moderation.defs#reasonAppeal"
120976
121039
  ]
120977
121040
  },
120978
121041
  reasonSpam: {
@@ -120998,6 +121061,10 @@ var schemaDict = {
120998
121061
  reasonOther: {
120999
121062
  type: "token",
121000
121063
  description: "Other: reports not falling under another report category"
121064
+ },
121065
+ reasonAppeal: {
121066
+ type: "token",
121067
+ description: "Appeal: appeal a previously taken moderation action"
121001
121068
  }
121002
121069
  }
121003
121070
  },
@@ -129036,23 +129103,27 @@ var _AtpAgent = class {
129036
129103
  try {
129037
129104
  this.session = session;
129038
129105
  const res = await this.api.com.atproto.server.getSession();
129039
- if (!res.success || res.data.did !== this.session.did) {
129040
- throw new Error("Invalid session");
129106
+ if (res.data.did !== this.session.did) {
129107
+ throw new XRPCError(400 /* InvalidRequest */, "Invalid session", "InvalidDID");
129041
129108
  }
129042
129109
  this.session.email = res.data.email;
129043
129110
  this.session.handle = res.data.handle;
129044
129111
  this.session.emailConfirmed = res.data.emailConfirmed;
129045
129112
  this._updateApiEndpoint(res.data.didDoc);
129113
+ this._persistSession?.("update", this.session);
129046
129114
  return res;
129047
129115
  } catch (e) {
129048
129116
  this.session = void 0;
129049
- throw e;
129050
- } finally {
129051
- if (this.session) {
129052
- this._persistSession?.("create", this.session);
129117
+ if (e instanceof XRPCError) {
129118
+ if ([1, 408, 425, 429, 500, 502, 503, 504, 522, 524].includes(e.status)) {
129119
+ this._persistSession?.("network-error", void 0);
129120
+ } else {
129121
+ this._persistSession?.("expired", void 0);
129122
+ }
129053
129123
  } else {
129054
- this._persistSession?.("create-failed", void 0);
129124
+ this._persistSession?.("network-error", void 0);
129055
129125
  }
129126
+ throw e;
129056
129127
  }
129057
129128
  }
129058
129129
  _addAuthHeader(reqHeaders) {
@@ -130595,7 +130666,8 @@ var schemaDict2 = {
130595
130666
  "lex:com.atproto.admin.defs#modEventLabel",
130596
130667
  "lex:com.atproto.admin.defs#modEventAcknowledge",
130597
130668
  "lex:com.atproto.admin.defs#modEventEscalate",
130598
- "lex:com.atproto.admin.defs#modEventMute"
130669
+ "lex:com.atproto.admin.defs#modEventMute",
130670
+ "lex:com.atproto.admin.defs#modEventResolveAppeal"
130599
130671
  ]
130600
130672
  },
130601
130673
  subject: {
@@ -130729,9 +130801,18 @@ var schemaDict2 = {
130729
130801
  type: "string",
130730
130802
  format: "datetime"
130731
130803
  },
130804
+ lastAppealedAt: {
130805
+ type: "string",
130806
+ format: "datetime",
130807
+ description: "Timestamp referencing when the author of the subject appealed a moderation action"
130808
+ },
130732
130809
  takendown: {
130733
130810
  type: "boolean"
130734
130811
  },
130812
+ appealed: {
130813
+ type: "boolean",
130814
+ description: "True indicates that the a previously taken moderator action was appealed against, by the author of the content. False indicates last appeal was resolved by moderators."
130815
+ },
130735
130816
  suspendUntil: {
130736
130817
  type: "string",
130737
130818
  format: "datetime"
@@ -131205,6 +131286,16 @@ var schemaDict2 = {
131205
131286
  }
131206
131287
  }
131207
131288
  },
131289
+ modEventResolveAppeal: {
131290
+ type: "object",
131291
+ description: "Resolve appeal on a subject",
131292
+ properties: {
131293
+ comment: {
131294
+ type: "string",
131295
+ description: "Describe resolution."
131296
+ }
131297
+ }
131298
+ },
131208
131299
  modEventComment: {
131209
131300
  type: "object",
131210
131301
  description: "Add a comment to a subject",
@@ -131303,6 +131394,10 @@ var schemaDict2 = {
131303
131394
  subjectLine: {
131304
131395
  type: "string",
131305
131396
  description: "The subject line of the email sent to the user."
131397
+ },
131398
+ comment: {
131399
+ type: "string",
131400
+ description: "Additional comment about the outgoing comm."
131306
131401
  }
131307
131402
  }
131308
131403
  }
@@ -131837,6 +131932,10 @@ var schemaDict2 = {
131837
131932
  type: "boolean",
131838
131933
  description: "Get subjects that were taken down"
131839
131934
  },
131935
+ appealed: {
131936
+ type: "boolean",
131937
+ description: "Get subjects in unresolved appealed status"
131938
+ },
131840
131939
  limit: {
131841
131940
  type: "integer",
131842
131941
  minimum: 1,
@@ -131946,6 +132045,10 @@ var schemaDict2 = {
131946
132045
  senderDid: {
131947
132046
  type: "string",
131948
132047
  format: "did"
132048
+ },
132049
+ comment: {
132050
+ type: "string",
132051
+ description: "Additional comment by the sender that won't be used in the email itself but helpful to provide more context for moderators/reviewers"
131949
132052
  }
131950
132053
  }
131951
132054
  }
@@ -132405,7 +132508,8 @@ var schemaDict2 = {
132405
132508
  "com.atproto.moderation.defs#reasonMisleading",
132406
132509
  "com.atproto.moderation.defs#reasonSexual",
132407
132510
  "com.atproto.moderation.defs#reasonRude",
132408
- "com.atproto.moderation.defs#reasonOther"
132511
+ "com.atproto.moderation.defs#reasonOther",
132512
+ "com.atproto.moderation.defs#reasonAppeal"
132409
132513
  ]
132410
132514
  },
132411
132515
  reasonSpam: {
@@ -132431,6 +132535,10 @@ var schemaDict2 = {
132431
132535
  reasonOther: {
132432
132536
  type: "token",
132433
132537
  description: "Other: reports not falling under another report category"
132538
+ },
132539
+ reasonAppeal: {
132540
+ type: "token",
132541
+ description: "Appeal: appeal a previously taken moderation action"
132434
132542
  }
132435
132543
  }
132436
132544
  },
@@ -138483,8 +138591,9 @@ var composeThread = (threadData, actors, state, ctx, viewer) => {
138483
138591
  const isAnchorPost = state.threadData.post.uri === threadData.post.postUri;
138484
138592
  const info = posts[threadData.post.postUri];
138485
138593
  const badReply = !!info?.violatesThreadGate;
138486
- const omitBadReply = !isAnchorPost && badReply;
138487
- if (!post || blocks[post.uri]?.reply || omitBadReply) {
138594
+ const violatesBlock = (post && blocks[post.uri]?.reply) ?? false;
138595
+ const omitBadReply = !isAnchorPost && (badReply || violatesBlock);
138596
+ if (!post || omitBadReply) {
138488
138597
  return {
138489
138598
  $type: "app.bsky.feed.defs#notFoundPost",
138490
138599
  uri: threadData.post.postUri,
@@ -138506,7 +138615,7 @@ var composeThread = (threadData, actors, state, ctx, viewer) => {
138506
138615
  };
138507
138616
  }
138508
138617
  let parent;
138509
- if (threadData.parent && !badReply) {
138618
+ if (threadData.parent && !badReply && !violatesBlock) {
138510
138619
  if (threadData.parent instanceof ParentNotFoundError) {
138511
138620
  parent = {
138512
138621
  $type: "app.bsky.feed.defs#notFoundPost",
@@ -140301,6 +140410,7 @@ var REASONMISLEADING = "com.atproto.moderation.defs#reasonMisleading";
140301
140410
  var REASONSEXUAL = "com.atproto.moderation.defs#reasonSexual";
140302
140411
  var REASONRUDE = "com.atproto.moderation.defs#reasonRude";
140303
140412
  var REASONOTHER = "com.atproto.moderation.defs#reasonOther";
140413
+ var REASONAPPEAL = "com.atproto.moderation.defs#reasonAppeal";
140304
140414
 
140305
140415
  // src/lexicon/types/com/atproto/admin/defs.ts
140306
140416
  var REVIEWOPEN = "com.atproto.admin.defs#reviewOpen";
@@ -140369,7 +140479,8 @@ var reasonTypes = /* @__PURE__ */ new Set([
140369
140479
  REASONMISLEADING,
140370
140480
  REASONRUDE,
140371
140481
  REASONSEXUAL,
140372
- REASONVIOLATION
140482
+ REASONVIOLATION,
140483
+ REASONAPPEAL
140373
140484
  ]);
140374
140485
  var eventTypes = /* @__PURE__ */ new Set([
140375
140486
  "com.atproto.admin.defs#modEventTakedown",
@@ -140398,12 +140509,18 @@ function createReport_default(server, ctx) {
140398
140509
  throw new AuthRequiredError();
140399
140510
  }
140400
140511
  }
140512
+ const reportReasonType = getReasonType(reasonType);
140513
+ const reportSubject = getSubject(subject);
140514
+ const subjectDid = "did" in reportSubject ? reportSubject.did : reportSubject.uri.host;
140515
+ if (reasonType === REASONAPPEAL && requester !== subjectDid) {
140516
+ throw new ForbiddenError("You cannot appeal this report");
140517
+ }
140401
140518
  const report = await db.transaction(async (dbTxn) => {
140402
140519
  const moderationTxn = ctx.services.moderation(dbTxn);
140403
140520
  return moderationTxn.report({
140404
- reasonType: getReasonType(reasonType),
140521
+ reasonType: reportReasonType,
140405
140522
  reason,
140406
- subject: getSubject(subject),
140523
+ subject: reportSubject,
140407
140524
  reportedBy: requester || ctx.cfg.serverDid
140408
140525
  });
140409
140526
  });
@@ -140706,6 +140823,7 @@ function queryModerationStatuses_default(server, ctx) {
140706
140823
  const {
140707
140824
  subject,
140708
140825
  takendown,
140826
+ appealed,
140709
140827
  reviewState,
140710
140828
  reviewedAfter,
140711
140829
  reviewedBefore,
@@ -140725,6 +140843,7 @@ function queryModerationStatuses_default(server, ctx) {
140725
140843
  reviewState: getReviewState(reviewState),
140726
140844
  subject,
140727
140845
  takendown,
140846
+ appealed,
140728
140847
  reviewedAfter,
140729
140848
  reviewedBefore,
140730
140849
  reportedAfter,
@@ -140886,6 +141005,14 @@ __export(health_exports, {
140886
141005
  var import_express2 = __toESM(require_express2());
140887
141006
  var createRouter = (ctx) => {
140888
141007
  const router = import_express2.default.Router();
141008
+ router.get("/", function(req, res) {
141009
+ res.type("text/plain");
141010
+ res.send('This is an AT Protocol Application View (AppView) for the "bsky.app" application: https://github.com/bluesky-social/atproto\n\nMost API routes are under /xrpc/');
141011
+ });
141012
+ router.get("/robots.txt", function(req, res) {
141013
+ res.type("text/plain");
141014
+ res.send('# Hello Friends!\n\n# Crawling the public parts of the API is allowed. HTTP 429 ("backoff") status codes are used for rate-limiting. Up to a handful concurrent requests should be ok.\nUser-agent: *\nAllow: /');
141015
+ });
140889
141016
  router.get("/xrpc/_health", async function(req, res) {
140890
141017
  const { version: version2 } = ctx.cfg;
140891
141018
  const db = ctx.db.getPrimary();
@@ -144347,7 +144474,7 @@ var FeedViews = class {
144347
144474
  for (const item of items) {
144348
144475
  const info = posts[item.postUri];
144349
144476
  const post = this.formatPostView(item.postUri, actors, posts, threadgates, embeds, labels, lists, viewer);
144350
- if (!post || blocks[post.uri]?.reply) {
144477
+ if (!post) {
144351
144478
  continue;
144352
144479
  }
144353
144480
  const feedPost = { post };
@@ -144364,8 +144491,8 @@ var FeedViews = class {
144364
144491
  }
144365
144492
  }
144366
144493
  if (item.replyParent && item.replyRoot && !info?.invalidReplyRoot && !info?.violatesThreadGate) {
144367
- const replyParent = this.formatMaybePostView(item.replyParent, actors, posts, threadgates, embeds, labels, lists, blocks, viewer, opts);
144368
- const replyRoot = this.formatMaybePostView(item.replyRoot, actors, posts, threadgates, embeds, labels, lists, blocks, viewer, opts);
144494
+ const replyParent = this.formatMaybePostView(item.replyParent, item.uri, actors, posts, threadgates, embeds, labels, lists, blocks, viewer, opts);
144495
+ const replyRoot = this.formatMaybePostView(item.replyRoot, item.uri, actors, posts, threadgates, embeds, labels, lists, blocks, viewer, opts);
144369
144496
  if (replyRoot && replyParent) {
144370
144497
  feedPost["reply"] = {
144371
144498
  root: replyRoot,
@@ -144440,14 +144567,14 @@ var FeedViews = class {
144440
144567
  }
144441
144568
  return true;
144442
144569
  }
144443
- formatMaybePostView(uri2, actors, posts, threadgates, embeds, labels, lists, blocks, viewer, opts) {
144570
+ formatMaybePostView(uri2, replyUri, actors, posts, threadgates, embeds, labels, lists, blocks, viewer, opts) {
144444
144571
  const post = this.formatPostView(uri2, actors, posts, threadgates, embeds, labels, lists, viewer);
144445
144572
  if (!post) {
144446
144573
  if (!opts?.usePostViewUnion)
144447
144574
  return;
144448
144575
  return this.notFoundPost(uri2);
144449
144576
  }
144450
- if (post.author.viewer?.blockedBy || post.author.viewer?.blocking || blocks[uri2]?.reply) {
144577
+ if (post.author.viewer?.blockedBy || post.author.viewer?.blocking || replyUri !== null && blocks[replyUri]?.reply) {
144451
144578
  if (!opts?.usePostViewUnion)
144452
144579
  return;
144453
144580
  return this.blockedPost(post);
@@ -145420,9 +145547,11 @@ var ModerationViews = class {
145420
145547
  lastReviewedBy: subjectStatus.lastReviewedBy ?? void 0,
145421
145548
  lastReviewedAt: subjectStatus.lastReviewedAt ?? void 0,
145422
145549
  lastReportedAt: subjectStatus.lastReportedAt ?? void 0,
145550
+ lastAppealedAt: subjectStatus.lastAppealedAt ?? void 0,
145423
145551
  muteUntil: subjectStatus.muteUntil ?? void 0,
145424
145552
  suspendUntil: subjectStatus.suspendUntil ?? void 0,
145425
145553
  takendown: subjectStatus.takendown ?? void 0,
145554
+ appealed: subjectStatus.appealed ?? void 0,
145426
145555
  subjectRepoHandle: subjectStatus.handle ?? void 0,
145427
145556
  subjectBlobCids: subjectStatus.blobCids || [],
145428
145557
  subject: !subjectStatus.recordPath ? {
@@ -145522,6 +145651,10 @@ var getSubjectStatusForModerationEvent = ({
145522
145651
  lastReviewedBy: createdBy,
145523
145652
  lastReviewedAt: createdAt
145524
145653
  };
145654
+ case "com.atproto.admin.defs#modEventResolveAppeal":
145655
+ return {
145656
+ appealed: false
145657
+ };
145525
145658
  default:
145526
145659
  return null;
145527
145660
  }
@@ -145537,6 +145670,7 @@ var adjustModerationSubjectStatus = async (db, moderationEvent, blobCids) => {
145537
145670
  comment,
145538
145671
  createdAt
145539
145672
  } = moderationEvent;
145673
+ const isAppealEvent = action === "com.atproto.admin.defs#modEventReport" && meta?.reportType === REASONAPPEAL;
145540
145674
  const subjectStatus = getSubjectStatusForModerationEvent({
145541
145675
  action,
145542
145676
  createdBy,
@@ -145566,6 +145700,16 @@ var adjustModerationSubjectStatus = async (db, moderationEvent, blobCids) => {
145566
145700
  newStatus.takendown = false;
145567
145701
  subjectStatus.takendown = false;
145568
145702
  }
145703
+ if (isAppealEvent) {
145704
+ newStatus.appealed = true;
145705
+ subjectStatus.appealed = true;
145706
+ newStatus.lastAppealedAt = createdAt;
145707
+ subjectStatus.lastAppealedAt = createdAt;
145708
+ }
145709
+ if (action === "com.atproto.admin.defs#modEventResolveAppeal" && subjectStatus.appealed) {
145710
+ newStatus.appealed = false;
145711
+ subjectStatus.appealed = false;
145712
+ }
145569
145713
  if (action === "com.atproto.admin.defs#modEventComment" && meta?.sticky) {
145570
145714
  newStatus.comment = comment;
145571
145715
  subjectStatus.comment = comment;
@@ -145977,6 +146121,7 @@ var ModerationService = class {
145977
146121
  cursor,
145978
146122
  limit = 50,
145979
146123
  takendown,
146124
+ appealed,
145980
146125
  reviewState,
145981
146126
  reviewedAfter,
145982
146127
  reviewedBefore,
@@ -146018,6 +146163,9 @@ var ModerationService = class {
146018
146163
  if (takendown) {
146019
146164
  builder = builder.where("takendown", "=", true);
146020
146165
  }
146166
+ if (appealed !== void 0) {
146167
+ builder = appealed === null ? builder.where("appealed", "is", null) : builder.where("appealed", "=", appealed);
146168
+ }
146021
146169
  if (!includeMuted) {
146022
146170
  builder = builder.where((qb) => qb.where("muteUntil", "<", new Date().toISOString()).orWhere("muteUntil", "is", null));
146023
146171
  }
@@ -146807,7 +146955,8 @@ __export(migrations_exports, {
146807
146955
  _20230920T213858047Z: () => T213858047Z_add_tags_to_post_exports,
146808
146956
  _20230929T192920807Z: () => T192920807Z_record_cursor_indexes_exports,
146809
146957
  _20231003T202833377Z: () => T202833377Z_create_moderation_subject_status_exports,
146810
- _20231205T000257238Z: () => T000257238Z_remove_did_cache_exports
146958
+ _20231205T000257238Z: () => T000257238Z_remove_did_cache_exports,
146959
+ _20231213T181744386Z: () => T181744386Z_moderation_subject_appeal_exports
146811
146960
  });
146812
146961
 
146813
146962
  // src/db/migrations/20230309T045948368Z-init.ts
@@ -147343,6 +147492,21 @@ async function down30(db) {
147343
147492
  await db.schema.createTable("did_cache").addColumn("did", "varchar", (col) => col.primaryKey()).addColumn("doc", "jsonb", (col) => col.notNull()).addColumn("updatedAt", "bigint", (col) => col.notNull()).execute();
147344
147493
  }
147345
147494
 
147495
+ // src/db/migrations/20231213T181744386Z-moderation-subject-appeal.ts
147496
+ var T181744386Z_moderation_subject_appeal_exports = {};
147497
+ __export(T181744386Z_moderation_subject_appeal_exports, {
147498
+ down: () => down31,
147499
+ up: () => up31
147500
+ });
147501
+ async function up31(db) {
147502
+ await db.schema.alterTable("moderation_subject_status").addColumn("lastAppealedAt", "varchar").execute();
147503
+ await db.schema.alterTable("moderation_subject_status").addColumn("appealed", "boolean").execute();
147504
+ }
147505
+ async function down31(db) {
147506
+ await db.schema.alterTable("moderation_subject_status").dropColumn("lastAppealedAt").execute();
147507
+ await db.schema.alterTable("moderation_subject_status").dropColumn("appealed").execute();
147508
+ }
147509
+
147346
147510
  // src/db/migrations/provider.ts
147347
147511
  var CtxMigrationProvider = class {
147348
147512
  constructor(migrations, ctx) {
@@ -150340,6 +150504,23 @@ var AutoModerator = class {
150340
150504
  async storeLabels(uri2, cid2, labels) {
150341
150505
  if (labels.length < 1)
150342
150506
  return;
150507
+ if (this.services.moderation) {
150508
+ await this.ctx.db.transaction(async (dbTxn) => {
150509
+ if (!this.services.moderation)
150510
+ return;
150511
+ const modSrvc = this.services.moderation(dbTxn);
150512
+ await modSrvc.logEvent({
150513
+ event: {
150514
+ $type: "com.atproto.admin.defs#modEventLabel",
150515
+ createLabelVals: labels,
150516
+ negateLabelVals: [],
150517
+ comment: "[AutoModerator]: Applying labels"
150518
+ },
150519
+ subject: { uri: uri2, cid: cid2 },
150520
+ createdBy: this.ctx.cfg.labelerDid
150521
+ });
150522
+ });
150523
+ }
150343
150524
  const labelSrvc = this.services.label(this.ctx.db);
150344
150525
  await labelSrvc.formatAndCreate(this.ctx.cfg.labelerDid, uri2.toString(), cid2.toString(), { create: labels });
150345
150526
  }