@atproto/ozone 0.0.9 → 0.0.10

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 (51) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/api/moderation/util.d.ts +1 -1
  3. package/dist/db/index.js +20 -1
  4. package/dist/db/index.js.map +3 -3
  5. package/dist/db/migrations/20240208T213404429Z-add-tags-column-to-moderation-subject.d.ts +3 -0
  6. package/dist/db/migrations/index.d.ts +1 -0
  7. package/dist/db/schema/moderation_event.d.ts +3 -1
  8. package/dist/db/schema/moderation_subject_status.d.ts +1 -0
  9. package/dist/index.js +318 -51
  10. package/dist/index.js.map +3 -3
  11. package/dist/lexicon/lexicons.d.ts +57 -0
  12. package/dist/lexicon/types/com/atproto/admin/defs.d.ts +9 -0
  13. package/dist/lexicon/types/com/atproto/admin/emitModerationEvent.d.ts +1 -1
  14. package/dist/lexicon/types/com/atproto/admin/queryModerationEvents.d.ts +2 -0
  15. package/dist/lexicon/types/com/atproto/admin/queryModerationStatuses.d.ts +2 -0
  16. package/dist/logger.d.ts +1 -0
  17. package/dist/mod-service/index.d.ts +18 -4
  18. package/dist/mod-service/lang.d.ts +15 -0
  19. package/dist/mod-service/status.d.ts +1 -1
  20. package/dist/mod-service/types.d.ts +1 -1
  21. package/dist/mod-service/views.d.ts +2 -1
  22. package/package.json +6 -6
  23. package/src/api/admin/emitModerationEvent.ts +22 -10
  24. package/src/api/admin/queryModerationEvents.ts +4 -0
  25. package/src/api/admin/queryModerationStatuses.ts +4 -0
  26. package/src/api/moderation/createReport.ts +15 -4
  27. package/src/api/moderation/util.ts +1 -0
  28. package/src/db/migrations/20240208T213404429Z-add-tags-column-to-moderation-subject.ts +31 -0
  29. package/src/db/migrations/index.ts +1 -0
  30. package/src/db/schema/moderation_event.ts +3 -0
  31. package/src/db/schema/moderation_subject_status.ts +1 -0
  32. package/src/lexicon/lexicons.ts +62 -0
  33. package/src/lexicon/types/com/atproto/admin/defs.ts +24 -0
  34. package/src/lexicon/types/com/atproto/admin/emitModerationEvent.ts +1 -0
  35. package/src/lexicon/types/com/atproto/admin/queryModerationEvents.ts +4 -0
  36. package/src/lexicon/types/com/atproto/admin/queryModerationStatuses.ts +2 -0
  37. package/src/logger.ts +2 -0
  38. package/src/mod-service/index.ts +60 -10
  39. package/src/mod-service/lang.ts +82 -0
  40. package/src/mod-service/status.ts +18 -4
  41. package/src/mod-service/types.ts +1 -0
  42. package/src/mod-service/views.ts +21 -2
  43. package/tests/__snapshots__/get-record.test.ts.snap +6 -0
  44. package/tests/__snapshots__/get-repo.test.ts.snap +3 -0
  45. package/tests/__snapshots__/moderation-events.test.ts.snap +45 -4
  46. package/tests/__snapshots__/moderation-statuses.test.ts.snap +59 -3
  47. package/tests/__snapshots__/moderation.test.ts.snap +3 -3
  48. package/tests/get-record.test.ts +0 -8
  49. package/tests/moderation-events.test.ts +57 -5
  50. package/tests/moderation-status-tags.test.ts +92 -0
  51. package/tests/moderation-statuses.test.ts +20 -3
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 up4 = +matches[1];
27867
+ const up5 = +matches[1];
27868
27868
  jsonPointer = matches[2];
27869
27869
  if (jsonPointer === "#") {
27870
- if (up4 >= dataLevel)
27871
- throw new Error(errorMsg("property/index", up4));
27872
- return dataPathArr[dataLevel - up4];
27870
+ if (up5 >= dataLevel)
27871
+ throw new Error(errorMsg("property/index", up5));
27872
+ return dataPathArr[dataLevel - up5];
27873
27873
  }
27874
- if (up4 > dataLevel)
27875
- throw new Error(errorMsg("data", up4));
27876
- data = dataNames[dataLevel - up4];
27874
+ if (up5 > dataLevel)
27875
+ throw new Error(errorMsg("data", up5));
27876
+ data = dataNames[dataLevel - up5];
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, up4) {
27890
- return `Cannot access ${pointerType} ${up4} levels up, current level is ${dataLevel}`;
27889
+ function errorMsg(pointerType, up5) {
27890
+ return `Cannot access ${pointerType} ${up5} levels up, current level is ${dataLevel}`;
27891
27891
  }
27892
27892
  }
27893
27893
  exports.getData = getData;
@@ -102188,6 +102188,12 @@ var schemaDict = {
102188
102188
  suspendUntil: {
102189
102189
  type: "string",
102190
102190
  format: "datetime"
102191
+ },
102192
+ tags: {
102193
+ type: "array",
102194
+ items: {
102195
+ type: "string"
102196
+ }
102191
102197
  }
102192
102198
  }
102193
102199
  },
@@ -102779,6 +102785,31 @@ var schemaDict = {
102779
102785
  }
102780
102786
  }
102781
102787
  },
102788
+ modEventTag: {
102789
+ type: "object",
102790
+ description: "Add/Remove a tag on a subject",
102791
+ required: ["add", "remove"],
102792
+ properties: {
102793
+ add: {
102794
+ type: "array",
102795
+ items: {
102796
+ type: "string"
102797
+ },
102798
+ description: "Tags to be added to the subject. If already exists, won't be duplicated."
102799
+ },
102800
+ remove: {
102801
+ type: "array",
102802
+ items: {
102803
+ type: "string"
102804
+ },
102805
+ description: "Tags to be removed to the subject. Ignores a tag If it doesn't exist, won't be duplicated."
102806
+ },
102807
+ comment: {
102808
+ type: "string",
102809
+ description: "Additional comment about added/removed tags."
102810
+ }
102811
+ }
102812
+ },
102782
102813
  communicationTemplateView: {
102783
102814
  type: "object",
102784
102815
  required: [
@@ -102953,7 +102984,8 @@ var schemaDict = {
102953
102984
  "lex:com.atproto.admin.defs#modEventMute",
102954
102985
  "lex:com.atproto.admin.defs#modEventReverseTakedown",
102955
102986
  "lex:com.atproto.admin.defs#modEventUnmute",
102956
- "lex:com.atproto.admin.defs#modEventEmail"
102987
+ "lex:com.atproto.admin.defs#modEventEmail",
102988
+ "lex:com.atproto.admin.defs#modEventTag"
102957
102989
  ]
102958
102990
  },
102959
102991
  subject: {
@@ -103375,6 +103407,20 @@ var schemaDict = {
103375
103407
  },
103376
103408
  description: "If specified, only events where all of these labels were removed are returned"
103377
103409
  },
103410
+ addedTags: {
103411
+ type: "array",
103412
+ items: {
103413
+ type: "string"
103414
+ },
103415
+ description: "If specified, only events where all of these tags were added are returned"
103416
+ },
103417
+ removedTags: {
103418
+ type: "array",
103419
+ items: {
103420
+ type: "string"
103421
+ },
103422
+ description: "If specified, only events where all of these tags were removed are returned"
103423
+ },
103378
103424
  reportTypes: {
103379
103425
  type: "array",
103380
103426
  items: {
@@ -103490,6 +103536,18 @@ var schemaDict = {
103490
103536
  maximum: 100,
103491
103537
  default: 50
103492
103538
  },
103539
+ tags: {
103540
+ type: "array",
103541
+ items: {
103542
+ type: "string"
103543
+ }
103544
+ },
103545
+ excludeTags: {
103546
+ type: "array",
103547
+ items: {
103548
+ type: "string"
103549
+ }
103550
+ },
103493
103551
  cursor: {
103494
103552
  type: "string"
103495
103553
  }
@@ -110190,6 +110248,9 @@ function isModEventMute(v) {
110190
110248
  function isModEventEmail(v) {
110191
110249
  return isObj2(v) && hasProp2(v, "$type") && v.$type === "com.atproto.admin.defs#modEventEmail";
110192
110250
  }
110251
+ function isModEventTag(v) {
110252
+ return isObj2(v) && hasProp2(v, "$type") && v.$type === "com.atproto.admin.defs#modEventTag";
110253
+ }
110193
110254
 
110194
110255
  // src/api/moderation/util.ts
110195
110256
  var getReasonType = (reasonType) => {
@@ -110233,7 +110294,8 @@ var eventTypes = /* @__PURE__ */ new Set([
110233
110294
  "com.atproto.admin.defs#modEventUnmute",
110234
110295
  "com.atproto.admin.defs#modEventReverseTakedown",
110235
110296
  "com.atproto.admin.defs#modEventEmail",
110236
- "com.atproto.admin.defs#modEventResolveAppeal"
110297
+ "com.atproto.admin.defs#modEventResolveAppeal",
110298
+ "com.atproto.admin.defs#modEventTag"
110237
110299
  ]);
110238
110300
 
110239
110301
  // src/mod-service/subject.ts
@@ -110325,6 +110387,80 @@ var RecordSubject = class {
110325
110387
  }
110326
110388
  };
110327
110389
 
110390
+ // src/logger.ts
110391
+ var import_pino_http = __toESM(require_logger());
110392
+ var dbLogger = subsystemLogger("ozone:db");
110393
+ var httpLogger = subsystemLogger("ozone");
110394
+ var langLogger = subsystemLogger("ozone:lang");
110395
+ var loggerMiddleware = (0, import_pino_http.default)({
110396
+ logger: httpLogger,
110397
+ serializers: {
110398
+ err: (err) => {
110399
+ return {
110400
+ code: err?.code,
110401
+ message: err?.message
110402
+ };
110403
+ }
110404
+ }
110405
+ });
110406
+
110407
+ // src/mod-service/lang.ts
110408
+ var ModerationLangService = class {
110409
+ constructor(moderationService) {
110410
+ this.moderationService = moderationService;
110411
+ }
110412
+ async tagSubjectWithLang({
110413
+ subject,
110414
+ subjectStatus,
110415
+ createdBy
110416
+ }) {
110417
+ if (subjectStatus && !subjectStatus.tags?.find((tag) => tag.includes("lang:"))) {
110418
+ try {
110419
+ const recordLangs = await this.getRecordLang({
110420
+ subject
110421
+ });
110422
+ await this.moderationService.logEvent({
110423
+ event: {
110424
+ $type: "com.atproto.admin.defs#modEventTag",
110425
+ add: recordLangs ? recordLangs.map((lang) => `lang:${lang}`) : ["lang:und"],
110426
+ remove: []
110427
+ },
110428
+ subject,
110429
+ createdBy
110430
+ });
110431
+ } catch (err) {
110432
+ langLogger.error({ subject, err }, "Error getting record langs");
110433
+ }
110434
+ }
110435
+ }
110436
+ async getRecordLang({
110437
+ subject
110438
+ }) {
110439
+ const isRecord = subject.isRecord();
110440
+ const langs = /* @__PURE__ */ new Set();
110441
+ if (subject.isRepo() || isRecord && subject.uri.endsWith("/app.bsky.actor.profile/self")) {
110442
+ const feed = await this.moderationService.views.fetchAuthorFeed(subject.did);
110443
+ feed.forEach((item) => {
110444
+ const itemLangs = item.post.record["langs"];
110445
+ if (itemLangs?.length) {
110446
+ itemLangs.forEach((lang) => langs.add(lang.split("-")[0]));
110447
+ }
110448
+ });
110449
+ }
110450
+ if (isRecord) {
110451
+ const recordByUri = await this.moderationService.views.fetchRecords([
110452
+ subject
110453
+ ]);
110454
+ const record = recordByUri.get(subject.uri);
110455
+ const recordLang = record?.value.langs;
110456
+ if (recordLang?.length) {
110457
+ recordLang.map((lang) => lang.split("-")[0]).forEach((lang) => langs.add(lang));
110458
+ }
110459
+ }
110460
+ return langs.size > 0 ? Array.from(langs) : null;
110461
+ }
110462
+ };
110463
+
110328
110464
  // src/api/moderation/createReport.ts
110329
110465
  function createReport_default(server, ctx) {
110330
110466
  server.com.atproto.moderation.createReport({
@@ -110339,12 +110475,19 @@ function createReport_default(server, ctx) {
110339
110475
  const db = ctx.db;
110340
110476
  const report = await db.transaction(async (dbTxn) => {
110341
110477
  const moderationTxn = ctx.modService(dbTxn);
110342
- return moderationTxn.report({
110478
+ const { event: reportEvent, subjectStatus } = await moderationTxn.report({
110343
110479
  reasonType: getReasonType(reasonType),
110344
110480
  reason,
110345
110481
  subject,
110346
110482
  reportedBy: requester || ctx.cfg.service.did
110347
110483
  });
110484
+ const moderationLangService = new ModerationLangService(moderationTxn);
110485
+ await moderationLangService.tagSubjectWithLang({
110486
+ subject,
110487
+ subjectStatus,
110488
+ createdBy: ctx.cfg.service.did
110489
+ });
110490
+ return reportEvent;
110348
110491
  });
110349
110492
  const body = ctx.modService(db).views.formatReport(report);
110350
110493
  return {
@@ -110402,28 +110545,34 @@ function emitModerationEvent_default(server, ctx) {
110402
110545
  subject,
110403
110546
  createdBy
110404
110547
  });
110548
+ const moderationLangService = new ModerationLangService(moderationTxn);
110549
+ await moderationLangService.tagSubjectWithLang({
110550
+ subject,
110551
+ createdBy: ctx.cfg.service.did,
110552
+ subjectStatus: result.subjectStatus
110553
+ });
110405
110554
  if (subject.isRepo()) {
110406
110555
  if (isTakedownEvent) {
110407
- const isSuspend = !!result.durationInHours;
110408
- await moderationTxn.takedownRepo(subject, result.id, isSuspend);
110556
+ const isSuspend = !!result.event.durationInHours;
110557
+ await moderationTxn.takedownRepo(subject, result.event.id, isSuspend);
110409
110558
  } else if (isReverseTakedownEvent) {
110410
110559
  await moderationTxn.reverseTakedownRepo(subject);
110411
110560
  }
110412
110561
  }
110413
110562
  if (subject.isRecord()) {
110414
110563
  if (isTakedownEvent) {
110415
- await moderationTxn.takedownRecord(subject, result.id);
110564
+ await moderationTxn.takedownRecord(subject, result.event.id);
110416
110565
  } else if (isReverseTakedownEvent) {
110417
110566
  await moderationTxn.reverseTakedownRecord(subject);
110418
110567
  }
110419
110568
  }
110420
110569
  if (isLabelEvent) {
110421
- await moderationTxn.formatAndCreateLabels(result.subjectUri ?? result.subjectDid, result.subjectCid, {
110422
- create: result.createLabelVals?.length ? result.createLabelVals.split(" ") : void 0,
110423
- negate: result.negateLabelVals?.length ? result.negateLabelVals.split(" ") : void 0
110570
+ await moderationTxn.formatAndCreateLabels(result.event.subjectUri ?? result.event.subjectDid, result.event.subjectCid, {
110571
+ create: result.event.createLabelVals?.length ? result.event.createLabelVals.split(" ") : void 0,
110572
+ negate: result.event.negateLabelVals?.length ? result.event.negateLabelVals.split(" ") : void 0
110424
110573
  });
110425
110574
  }
110426
- return result;
110575
+ return result.event;
110427
110576
  });
110428
110577
  return {
110429
110578
  encoding: "application/json",
@@ -110579,7 +110728,9 @@ function queryModerationStatuses_default(server, ctx) {
110579
110728
  sortField = "lastReportedAt",
110580
110729
  includeMuted = false,
110581
110730
  limit = 50,
110582
- cursor
110731
+ cursor,
110732
+ tags = [],
110733
+ excludeTags = []
110583
110734
  } = params2;
110584
110735
  const db = ctx.db;
110585
110736
  const modService = ctx.modService(db);
@@ -110598,7 +110749,9 @@ function queryModerationStatuses_default(server, ctx) {
110598
110749
  lastReviewedBy,
110599
110750
  sortField,
110600
110751
  limit,
110601
- cursor
110752
+ cursor,
110753
+ tags,
110754
+ excludeTags
110602
110755
  });
110603
110756
  const subjectStatuses = results.statuses.map((status) => modService.views.formatSubjectStatus(status));
110604
110757
  return {
@@ -110631,6 +110784,8 @@ function queryModerationEvents_default(server, ctx) {
110631
110784
  createdBefore,
110632
110785
  addedLabels = [],
110633
110786
  removedLabels = [],
110787
+ addedTags = [],
110788
+ removedTags = [],
110634
110789
  reportTypes
110635
110790
  } = params2;
110636
110791
  const db = ctx.db;
@@ -110648,7 +110803,9 @@ function queryModerationEvents_default(server, ctx) {
110648
110803
  createdAfter,
110649
110804
  createdBefore,
110650
110805
  addedLabels,
110806
+ addedTags,
110651
110807
  removedLabels,
110808
+ removedTags,
110652
110809
  reportTypes
110653
110810
  });
110654
110811
  return {
@@ -118419,22 +118576,6 @@ function api_default(server, ctx) {
118419
118576
  return server;
118420
118577
  }
118421
118578
 
118422
- // src/logger.ts
118423
- var import_pino_http = __toESM(require_logger());
118424
- var dbLogger = subsystemLogger("ozone:db");
118425
- var httpLogger = subsystemLogger("ozone");
118426
- var loggerMiddleware = (0, import_pino_http.default)({
118427
- logger: httpLogger,
118428
- serializers: {
118429
- err: (err) => {
118430
- return {
118431
- code: err?.code,
118432
- message: err?.message
118433
- };
118434
- }
118435
- }
118436
- });
118437
-
118438
118579
  // src/error.ts
118439
118580
  var handler = (err, _req, res, next) => {
118440
118581
  httpLogger.error(err, "unexpected internal server error");
@@ -119682,6 +119823,12 @@ var schemaDict2 = {
119682
119823
  suspendUntil: {
119683
119824
  type: "string",
119684
119825
  format: "datetime"
119826
+ },
119827
+ tags: {
119828
+ type: "array",
119829
+ items: {
119830
+ type: "string"
119831
+ }
119685
119832
  }
119686
119833
  }
119687
119834
  },
@@ -120273,6 +120420,31 @@ var schemaDict2 = {
120273
120420
  }
120274
120421
  }
120275
120422
  },
120423
+ modEventTag: {
120424
+ type: "object",
120425
+ description: "Add/Remove a tag on a subject",
120426
+ required: ["add", "remove"],
120427
+ properties: {
120428
+ add: {
120429
+ type: "array",
120430
+ items: {
120431
+ type: "string"
120432
+ },
120433
+ description: "Tags to be added to the subject. If already exists, won't be duplicated."
120434
+ },
120435
+ remove: {
120436
+ type: "array",
120437
+ items: {
120438
+ type: "string"
120439
+ },
120440
+ description: "Tags to be removed to the subject. Ignores a tag If it doesn't exist, won't be duplicated."
120441
+ },
120442
+ comment: {
120443
+ type: "string",
120444
+ description: "Additional comment about added/removed tags."
120445
+ }
120446
+ }
120447
+ },
120276
120448
  communicationTemplateView: {
120277
120449
  type: "object",
120278
120450
  required: [
@@ -120447,7 +120619,8 @@ var schemaDict2 = {
120447
120619
  "lex:com.atproto.admin.defs#modEventMute",
120448
120620
  "lex:com.atproto.admin.defs#modEventReverseTakedown",
120449
120621
  "lex:com.atproto.admin.defs#modEventUnmute",
120450
- "lex:com.atproto.admin.defs#modEventEmail"
120622
+ "lex:com.atproto.admin.defs#modEventEmail",
120623
+ "lex:com.atproto.admin.defs#modEventTag"
120451
120624
  ]
120452
120625
  },
120453
120626
  subject: {
@@ -120869,6 +121042,20 @@ var schemaDict2 = {
120869
121042
  },
120870
121043
  description: "If specified, only events where all of these labels were removed are returned"
120871
121044
  },
121045
+ addedTags: {
121046
+ type: "array",
121047
+ items: {
121048
+ type: "string"
121049
+ },
121050
+ description: "If specified, only events where all of these tags were added are returned"
121051
+ },
121052
+ removedTags: {
121053
+ type: "array",
121054
+ items: {
121055
+ type: "string"
121056
+ },
121057
+ description: "If specified, only events where all of these tags were removed are returned"
121058
+ },
120872
121059
  reportTypes: {
120873
121060
  type: "array",
120874
121061
  items: {
@@ -120984,6 +121171,18 @@ var schemaDict2 = {
120984
121171
  maximum: 100,
120985
121172
  default: 50
120986
121173
  },
121174
+ tags: {
121175
+ type: "array",
121176
+ items: {
121177
+ type: "string"
121178
+ }
121179
+ },
121180
+ excludeTags: {
121181
+ type: "array",
121182
+ items: {
121183
+ type: "string"
121184
+ }
121185
+ },
120987
121186
  cursor: {
120988
121187
  type: "string"
120989
121188
  }
@@ -131007,7 +131206,8 @@ var migrations_exports = {};
131007
131206
  __export(migrations_exports, {
131008
131207
  _20231219T205730722Z: () => T205730722Z_init_exports,
131009
131208
  _20240116T085607200Z: () => T085607200Z_communication_template_exports,
131010
- _20240201T051104136Z: () => T051104136Z_mod_event_blobs_exports
131209
+ _20240201T051104136Z: () => T051104136Z_mod_event_blobs_exports,
131210
+ _20240208T213404429Z: () => T213404429Z_add_tags_column_to_moderation_subject_exports
131011
131211
  });
131012
131212
 
131013
131213
  // src/db/migrations/20231219T205730722Z-init.ts
@@ -131078,6 +131278,23 @@ async function down3(db) {
131078
131278
  await db.schema.alterTable("moderation_event").dropColumn("subjectBlobCids").execute();
131079
131279
  }
131080
131280
 
131281
+ // src/db/migrations/20240208T213404429Z-add-tags-column-to-moderation-subject.ts
131282
+ var T213404429Z_add_tags_column_to_moderation_subject_exports = {};
131283
+ __export(T213404429Z_add_tags_column_to_moderation_subject_exports, {
131284
+ down: () => down4,
131285
+ up: () => up4
131286
+ });
131287
+ async function up4(db) {
131288
+ await db.schema.alterTable("moderation_event").addColumn("addedTags", "jsonb").execute();
131289
+ await db.schema.alterTable("moderation_event").addColumn("removedTags", "jsonb").execute();
131290
+ await db.schema.alterTable("moderation_subject_status").addColumn("tags", "jsonb").execute();
131291
+ }
131292
+ async function down4(db) {
131293
+ await db.schema.alterTable("moderation_event").dropColumn("addedTags").execute();
131294
+ await db.schema.alterTable("moderation_event").dropColumn("removedTags").execute();
131295
+ await db.schema.alterTable("moderation_subject_status").dropColumn("tags").execute();
131296
+ }
131297
+
131081
131298
  // src/db/migrations/provider.ts
131082
131299
  var CtxMigrationProvider = class {
131083
131300
  constructor(migrations, ctx) {
@@ -131333,6 +131550,10 @@ var ModerationViews = class {
131333
131550
  if (event.action === "com.atproto.admin.defs#modEventComment" && event.meta?.sticky) {
131334
131551
  eventView.event.sticky = true;
131335
131552
  }
131553
+ if (event.action === "com.atproto.admin.defs#modEventTag") {
131554
+ eventView.event.add = event.addedTags || [];
131555
+ eventView.event.remove = event.removedTags || [];
131556
+ }
131336
131557
  return eventView;
131337
131558
  }
131338
131559
  async eventDetail(result) {
@@ -131373,12 +131594,13 @@ var ModerationViews = class {
131373
131594
  const fetched = await Promise.all(subjects.map(async (subject) => {
131374
131595
  const uri2 = new AtUri(subject.uri);
131375
131596
  try {
131376
- return await this.appviewAgent.api.com.atproto.repo.getRecord({
131597
+ const record = await this.appviewAgent.api.com.atproto.repo.getRecord({
131377
131598
  repo: uri2.hostname,
131378
131599
  collection: uri2.collection,
131379
131600
  rkey: uri2.rkey,
131380
131601
  cid: subject.cid
131381
131602
  }, auth);
131603
+ return record;
131382
131604
  } catch {
131383
131605
  return null;
131384
131606
  }
@@ -131568,9 +131790,19 @@ var ModerationViews = class {
131568
131790
  appealed: status.appealed ?? void 0,
131569
131791
  subjectRepoHandle: status.handle ?? void 0,
131570
131792
  subjectBlobCids: status.blobCids || [],
131793
+ tags: status.tags || [],
131571
131794
  subject: subjectFromStatusRow(status).lex()
131572
131795
  };
131573
131796
  }
131797
+ async fetchAuthorFeed(actor) {
131798
+ const auth = await this.appviewAuth();
131799
+ if (!auth)
131800
+ return [];
131801
+ const {
131802
+ data: { feed }
131803
+ } = await this.appviewAgent.api.app.bsky.feed.getAuthorFeed({ actor }, auth);
131804
+ return feed;
131805
+ }
131574
131806
  };
131575
131807
  function parseSubjectId(subject) {
131576
131808
  if (subject.startsWith("did:")) {
@@ -131672,6 +131904,8 @@ var getSubjectStatusForModerationEvent = ({
131672
131904
  lastReviewedBy: createdBy,
131673
131905
  lastReviewedAt: createdAt
131674
131906
  };
131907
+ case "com.atproto.admin.defs#modEventTag":
131908
+ return { tags: [] };
131675
131909
  case "com.atproto.admin.defs#modEventResolveAppeal":
131676
131910
  return {
131677
131911
  appealed: false
@@ -131688,6 +131922,8 @@ var adjustModerationSubjectStatus = async (db, moderationEvent, blobCids) => {
131688
131922
  subjectCid,
131689
131923
  createdBy,
131690
131924
  meta,
131925
+ addedTags,
131926
+ removedTags,
131691
131927
  comment,
131692
131928
  createdAt
131693
131929
  } = moderationEvent;
@@ -131735,6 +131971,17 @@ var adjustModerationSubjectStatus = async (db, moderationEvent, blobCids) => {
131735
131971
  newStatus.comment = comment;
131736
131972
  subjectStatus.comment = comment;
131737
131973
  }
131974
+ if (action === "com.atproto.admin.defs#modEventTag") {
131975
+ let tags = currentStatus?.tags || [];
131976
+ if (addedTags?.length) {
131977
+ tags = tags.concat(addedTags);
131978
+ }
131979
+ if (removedTags?.length) {
131980
+ tags = tags.filter((tag) => !removedTags.includes(tag));
131981
+ }
131982
+ newStatus.tags = jsonb([...new Set(tags)]);
131983
+ subjectStatus.tags = newStatus.tags;
131984
+ }
131738
131985
  if (blobCids?.length) {
131739
131986
  const newBlobCids = jsonb(blobCids);
131740
131987
  newStatus.blobCids = newBlobCids;
@@ -131749,8 +131996,8 @@ var adjustModerationSubjectStatus = async (db, moderationEvent, blobCids) => {
131749
131996
  ...subjectStatus,
131750
131997
  updatedAt: now
131751
131998
  }));
131752
- const status = await insertQuery.executeTakeFirst();
131753
- return status;
131999
+ const status = await insertQuery.returningAll().executeTakeFirst();
132000
+ return status || null;
131754
132001
  };
131755
132002
  var getStatusIdentifierFromSubject = (subject) => {
131756
132003
  const isSubjectString = typeof subject === "string";
@@ -131945,8 +132192,11 @@ var ModerationService = class {
131945
132192
  createdBefore,
131946
132193
  addedLabels,
131947
132194
  removedLabels,
132195
+ addedTags,
132196
+ removedTags,
131948
132197
  reportTypes
131949
132198
  } = opts;
132199
+ const { ref } = this.db.db.dynamic;
131950
132200
  let builder = this.db.db.selectFrom("moderation_event").selectAll();
131951
132201
  if (subject) {
131952
132202
  builder = builder.where((qb) => {
@@ -131993,10 +132243,15 @@ var ModerationService = class {
131993
132243
  builder = builder.where("negateLabelVals", "ilike", `%${label}%`);
131994
132244
  });
131995
132245
  }
132246
+ if (addedTags.length) {
132247
+ builder = builder.where(sql`${ref("addedTags")} @> ${jsonb(addedTags)}`);
132248
+ }
132249
+ if (removedTags.length) {
132250
+ builder = builder.where(sql`${ref("removedTags")} @> ${jsonb(removedTags)}`);
132251
+ }
131996
132252
  if (reportTypes?.length) {
131997
132253
  builder = builder.where(sql`meta->>'reportType'`, "in", reportTypes);
131998
132254
  }
131999
- const { ref } = this.db.db.dynamic;
132000
132255
  const keyset = new TimeIdKeyset(ref(`moderation_event.createdAt`), ref("moderation_event.id"));
132001
132256
  const paginatedBuilder = paginate(builder, {
132002
132257
  limit,
@@ -132035,6 +132290,8 @@ var ModerationService = class {
132035
132290
  const createLabelVals = isModEventLabel(event) && event.createLabelVals.length > 0 ? event.createLabelVals.join(" ") : void 0;
132036
132291
  const negateLabelVals = isModEventLabel(event) && event.negateLabelVals.length > 0 ? event.negateLabelVals.join(" ") : void 0;
132037
132292
  const meta = {};
132293
+ const addedTags = isModEventTag(event) ? jsonb(event.add) : null;
132294
+ const removedTags = isModEventTag(event) ? jsonb(event.remove) : null;
132038
132295
  if (isModEventReport(event)) {
132039
132296
  meta.reportType = event.reportType;
132040
132297
  }
@@ -132052,6 +132309,8 @@ var ModerationService = class {
132052
132309
  createdBy,
132053
132310
  createLabelVals,
132054
132311
  negateLabelVals,
132312
+ addedTags,
132313
+ removedTags,
132055
132314
  durationInHours: event.durationInHours ? Number(event.durationInHours) : null,
132056
132315
  meta,
132057
132316
  expiresAt: (isModEventTakedown(event) || isModEventMute(event)) && event.durationInHours ? addHoursToDate(event.durationInHours, createdAt).toISOString() : void 0,
@@ -132061,8 +132320,8 @@ var ModerationService = class {
132061
132320
  subjectCid: subjectInfo.subjectCid,
132062
132321
  subjectBlobCids: jsonb(subjectInfo.subjectBlobCids)
132063
132322
  }).returningAll().executeTakeFirstOrThrow();
132064
- await adjustModerationSubjectStatus(this.db, modEvent, subject.blobCids);
132065
- return modEvent;
132323
+ const subjectStatus = await adjustModerationSubjectStatus(this.db, modEvent, subject.blobCids);
132324
+ return { event: modEvent, subjectStatus };
132066
132325
  }
132067
132326
  async getLastReversibleEventForSubject(subject) {
132068
132327
  if (!subject.reverseMute && !subject.reverseSuspend) {
@@ -132102,7 +132361,7 @@ var ModerationService = class {
132102
132361
  }) {
132103
132362
  const isRevertingTakedown = action === "com.atproto.admin.defs#modEventTakedown";
132104
132363
  this.db.assertTransaction();
132105
- const result = await this.logEvent({
132364
+ const { event } = await this.logEvent({
132106
132365
  event: {
132107
132366
  $type: isRevertingTakedown ? "com.atproto.admin.defs#modEventReverseTakedown" : "com.atproto.admin.defs#modEventUnmute",
132108
132367
  comment: comment ?? void 0
@@ -132118,7 +132377,7 @@ var ModerationService = class {
132118
132377
  await this.reverseTakedownRecord(subject);
132119
132378
  }
132120
132379
  }
132121
- return result;
132380
+ return event;
132122
132381
  }
132123
132382
  async takedownRepo(subject, takedownId, isSuspend = false) {
132124
132383
  const takedownRef = `BSKY-${isSuspend ? "SUSPEND" : "TAKEDOWN"}-${takedownId}`;
@@ -132259,7 +132518,7 @@ var ModerationService = class {
132259
132518
  createdAt = new Date(),
132260
132519
  subject
132261
132520
  } = info;
132262
- const event = await this.logEvent({
132521
+ const result = await this.logEvent({
132263
132522
  event: {
132264
132523
  $type: "com.atproto.admin.defs#modEventReport",
132265
132524
  reportType: reasonType,
@@ -132269,7 +132528,7 @@ var ModerationService = class {
132269
132528
  subject,
132270
132529
  createdAt
132271
132530
  });
132272
- return event;
132531
+ return result;
132273
132532
  }
132274
132533
  async getSubjectStatuses({
132275
132534
  cursor,
@@ -132286,9 +132545,12 @@ var ModerationService = class {
132286
132545
  sortDirection,
132287
132546
  lastReviewedBy,
132288
132547
  sortField,
132289
- subject
132548
+ subject,
132549
+ tags,
132550
+ excludeTags
132290
132551
  }) {
132291
132552
  let builder = this.db.db.selectFrom("moderation_subject_status").selectAll();
132553
+ const { ref } = this.db.db.dynamic;
132292
132554
  if (subject) {
132293
132555
  const subjectInfo = getStatusIdentifierFromSubject(subject);
132294
132556
  builder = builder.where("moderation_subject_status.did", "=", subjectInfo.did).where((qb) => subjectInfo.recordPath ? qb.where("recordPath", "=", subjectInfo.recordPath) : qb.where("recordPath", "=", ""));
@@ -132323,7 +132585,12 @@ var ModerationService = class {
132323
132585
  if (!includeMuted) {
132324
132586
  builder = builder.where((qb) => qb.where("muteUntil", "<", new Date().toISOString()).orWhere("muteUntil", "is", null));
132325
132587
  }
132326
- const { ref } = this.db.db.dynamic;
132588
+ if (tags.length) {
132589
+ builder = builder.where(sql`${ref("moderation_subject_status.tags")} @> ${jsonb(tags)}`);
132590
+ }
132591
+ if (excludeTags.length) {
132592
+ builder = builder.where((qb) => qb.where(sql`NOT(${ref("moderation_subject_status.tags")} @> ${jsonb(excludeTags)})`).orWhere("tags", "is", null));
132593
+ }
132327
132594
  const keyset = new StatusKeyset(ref(`moderation_subject_status.${sortField}`), ref("moderation_subject_status.id"));
132328
132595
  const paginatedBuilder = paginate(builder, {
132329
132596
  limit,