@atproto/api 0.12.26-next.0 → 0.12.26

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 (62) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/bsky-agent.d.ts +23 -1
  3. package/dist/bsky-agent.d.ts.map +1 -1
  4. package/dist/bsky-agent.js +106 -42
  5. package/dist/bsky-agent.js.map +1 -1
  6. package/dist/client/index.d.ts +0 -2
  7. package/dist/client/index.d.ts.map +1 -1
  8. package/dist/client/index.js +4 -6
  9. package/dist/client/index.js.map +1 -1
  10. package/dist/client/lexicons.d.ts +29 -101
  11. package/dist/client/lexicons.d.ts.map +1 -1
  12. package/dist/client/lexicons.js +32 -112
  13. package/dist/client/lexicons.js.map +1 -1
  14. package/dist/client/types/app/bsky/actor/defs.d.ts +5 -0
  15. package/dist/client/types/app/bsky/actor/defs.d.ts.map +1 -1
  16. package/dist/client/types/app/bsky/actor/defs.js.map +1 -1
  17. package/dist/client/types/app/bsky/embed/images.d.ts +10 -3
  18. package/dist/client/types/app/bsky/embed/images.d.ts.map +1 -1
  19. package/dist/client/types/app/bsky/embed/images.js +11 -1
  20. package/dist/client/types/app/bsky/embed/images.js.map +1 -1
  21. package/dist/client/types/app/bsky/embed/record.d.ts +1 -2
  22. package/dist/client/types/app/bsky/embed/record.d.ts.map +1 -1
  23. package/dist/client/types/app/bsky/embed/record.js.map +1 -1
  24. package/dist/client/types/app/bsky/embed/recordWithMedia.d.ts +2 -3
  25. package/dist/client/types/app/bsky/embed/recordWithMedia.d.ts.map +1 -1
  26. package/dist/client/types/app/bsky/embed/recordWithMedia.js.map +1 -1
  27. package/dist/client/types/app/bsky/feed/defs.d.ts +1 -2
  28. package/dist/client/types/app/bsky/feed/defs.d.ts.map +1 -1
  29. package/dist/client/types/app/bsky/feed/defs.js.map +1 -1
  30. package/dist/client/types/app/bsky/feed/post.d.ts +1 -2
  31. package/dist/client/types/app/bsky/feed/post.d.ts.map +1 -1
  32. package/dist/client/types/app/bsky/feed/post.js.map +1 -1
  33. package/dist/moderation/mutewords.d.ts +2 -1
  34. package/dist/moderation/mutewords.d.ts.map +1 -1
  35. package/dist/moderation/mutewords.js +7 -1
  36. package/dist/moderation/mutewords.js.map +1 -1
  37. package/dist/moderation/subjects/post.js +13 -0
  38. package/dist/moderation/subjects/post.js.map +1 -1
  39. package/package.json +3 -3
  40. package/src/bsky-agent.ts +139 -45
  41. package/src/client/index.ts +0 -4
  42. package/src/client/lexicons.ts +35 -114
  43. package/src/client/types/app/bsky/actor/defs.ts +5 -0
  44. package/src/client/types/app/bsky/embed/images.ts +21 -3
  45. package/src/client/types/app/bsky/embed/record.ts +0 -2
  46. package/src/client/types/app/bsky/embed/recordWithMedia.ts +0 -3
  47. package/src/client/types/app/bsky/feed/defs.ts +0 -2
  48. package/src/client/types/app/bsky/feed/post.ts +0 -2
  49. package/src/moderation/mutewords.ts +11 -0
  50. package/src/moderation/subjects/post.ts +15 -0
  51. package/tests/bsky-agent.test.ts +525 -156
  52. package/tests/moderation-mutewords.test.ts +308 -50
  53. package/dist/client/types/app/bsky/embed/defs.d.ts +0 -13
  54. package/dist/client/types/app/bsky/embed/defs.d.ts.map +0 -1
  55. package/dist/client/types/app/bsky/embed/defs.js +0 -16
  56. package/dist/client/types/app/bsky/embed/defs.js.map +0 -1
  57. package/dist/client/types/app/bsky/embed/video.d.ts +0 -33
  58. package/dist/client/types/app/bsky/embed/video.d.ts.map +0 -1
  59. package/dist/client/types/app/bsky/embed/video.js +0 -35
  60. package/dist/client/types/app/bsky/embed/video.js.map +0 -1
  61. package/src/client/types/app/bsky/embed/defs.ts +0 -26
  62. package/src/client/types/app/bsky/embed/video.ts +0 -67
package/src/bsky-agent.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  AppBskyLabelerDefs,
10
10
  ComAtprotoRepoPutRecord,
11
11
  } from './client'
12
+ import { MutedWord } from './client/types/app/bsky/actor/defs'
12
13
  import {
13
14
  BskyPreferences,
14
15
  BskyFeedViewPreference,
@@ -477,6 +478,14 @@ export class BskyAgent extends AtpAgent {
477
478
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
478
479
  const { $type, ...v } = pref
479
480
  prefs.moderationPrefs.mutedWords = v.items
481
+
482
+ if (prefs.moderationPrefs.mutedWords.length) {
483
+ prefs.moderationPrefs.mutedWords =
484
+ prefs.moderationPrefs.mutedWords.map((word) => {
485
+ word.actorTarget = word.actorTarget || 'all'
486
+ return word
487
+ })
488
+ }
480
489
  } else if (
481
490
  AppBskyActorDefs.isHiddenPostsPref(pref) &&
482
491
  AppBskyActorDefs.validateHiddenPostsPref(pref).success
@@ -937,7 +946,19 @@ export class BskyAgent extends AtpAgent {
937
946
  })
938
947
  }
939
948
 
940
- async upsertMutedWords(newMutedWords: AppBskyActorDefs.MutedWord[]) {
949
+ /**
950
+ * Add a muted word to user preferences.
951
+ */
952
+ async addMutedWord(
953
+ mutedWord: Pick<
954
+ MutedWord,
955
+ 'value' | 'targets' | 'actorTarget' | 'expiresAt'
956
+ >,
957
+ ) {
958
+ const sanitizedValue = sanitizeMutedWordValue(mutedWord.value)
959
+
960
+ if (!sanitizedValue) return
961
+
941
962
  await updatePreferences(this, (prefs: AppBskyActorDefs.Preferences) => {
942
963
  let mutedWordsPref = prefs.findLast(
943
964
  (pref) =>
@@ -945,40 +966,27 @@ export class BskyAgent extends AtpAgent {
945
966
  AppBskyActorDefs.validateMutedWordsPref(pref).success,
946
967
  )
947
968
 
948
- if (mutedWordsPref && AppBskyActorDefs.isMutedWordsPref(mutedWordsPref)) {
949
- for (const updatedWord of newMutedWords) {
950
- let foundMatch = false
951
- const sanitizedUpdatedValue = sanitizeMutedWordValue(
952
- updatedWord.value,
953
- )
954
-
955
- // was trimmed down to an empty string e.g. single `#`
956
- if (!sanitizedUpdatedValue) continue
969
+ const newMutedWord: AppBskyActorDefs.MutedWord = {
970
+ id: TID.nextStr(),
971
+ value: sanitizedValue,
972
+ targets: mutedWord.targets || [],
973
+ actorTarget: mutedWord.actorTarget || 'all',
974
+ expiresAt: mutedWord.expiresAt || undefined,
975
+ }
957
976
 
958
- for (const existingItem of mutedWordsPref.items) {
959
- if (existingItem.value === sanitizedUpdatedValue) {
960
- existingItem.targets = Array.from(
961
- new Set([...existingItem.targets, ...updatedWord.targets]),
962
- )
963
- foundMatch = true
964
- break
965
- }
966
- }
977
+ if (mutedWordsPref && AppBskyActorDefs.isMutedWordsPref(mutedWordsPref)) {
978
+ mutedWordsPref.items.push(newMutedWord)
967
979
 
968
- if (!foundMatch) {
969
- mutedWordsPref.items.push({
970
- ...updatedWord,
971
- value: sanitizedUpdatedValue,
972
- })
973
- }
974
- }
980
+ /**
981
+ * Migrate any old muted words that don't have an id
982
+ */
983
+ mutedWordsPref.items = migrateLegacyMutedWordsItems(
984
+ mutedWordsPref.items,
985
+ )
975
986
  } else {
976
987
  // if the pref doesn't exist, create it
977
988
  mutedWordsPref = {
978
- items: newMutedWords.map((w) => ({
979
- ...w,
980
- value: sanitizeMutedWordValue(w.value),
981
- })),
989
+ items: [newMutedWord],
982
990
  }
983
991
  }
984
992
 
@@ -990,6 +998,28 @@ export class BskyAgent extends AtpAgent {
990
998
  })
991
999
  }
992
1000
 
1001
+ /**
1002
+ * Convenience method to add muted words to user preferences
1003
+ */
1004
+ async addMutedWords(newMutedWords: AppBskyActorDefs.MutedWord[]) {
1005
+ await Promise.all(newMutedWords.map((word) => this.addMutedWord(word)))
1006
+ }
1007
+
1008
+ /**
1009
+ * @deprecated use `addMutedWords` or `addMutedWord` instead
1010
+ */
1011
+ async upsertMutedWords(
1012
+ mutedWords: Pick<
1013
+ MutedWord,
1014
+ 'value' | 'targets' | 'actorTarget' | 'expiresAt'
1015
+ >[],
1016
+ ) {
1017
+ await this.addMutedWords(mutedWords)
1018
+ }
1019
+
1020
+ /**
1021
+ * Update a muted word in user preferences.
1022
+ */
993
1023
  async updateMutedWord(mutedWord: AppBskyActorDefs.MutedWord) {
994
1024
  await updatePreferences(this, (prefs: AppBskyActorDefs.Preferences) => {
995
1025
  const mutedWordsPref = prefs.findLast(
@@ -999,22 +1029,48 @@ export class BskyAgent extends AtpAgent {
999
1029
  )
1000
1030
 
1001
1031
  if (mutedWordsPref && AppBskyActorDefs.isMutedWordsPref(mutedWordsPref)) {
1002
- for (const existingItem of mutedWordsPref.items) {
1003
- if (existingItem.value === mutedWord.value) {
1004
- existingItem.targets = mutedWord.targets
1005
- break
1032
+ mutedWordsPref.items = mutedWordsPref.items.map((existingItem) => {
1033
+ const match = matchMutedWord(existingItem, mutedWord)
1034
+
1035
+ if (match) {
1036
+ const updated = {
1037
+ ...existingItem,
1038
+ ...mutedWord,
1039
+ }
1040
+ return {
1041
+ id: existingItem.id || TID.nextStr(),
1042
+ value:
1043
+ sanitizeMutedWordValue(updated.value) || existingItem.value,
1044
+ targets: updated.targets || [],
1045
+ actorTarget: updated.actorTarget || 'all',
1046
+ expiresAt: updated.expiresAt || undefined,
1047
+ }
1048
+ } else {
1049
+ return existingItem
1006
1050
  }
1007
- }
1051
+ })
1052
+
1053
+ /**
1054
+ * Migrate any old muted words that don't have an id
1055
+ */
1056
+ mutedWordsPref.items = migrateLegacyMutedWordsItems(
1057
+ mutedWordsPref.items,
1058
+ )
1059
+
1060
+ return prefs
1061
+ .filter((p) => !AppBskyActorDefs.isMutedWordsPref(p))
1062
+ .concat([
1063
+ { ...mutedWordsPref, $type: 'app.bsky.actor.defs#mutedWordsPref' },
1064
+ ])
1008
1065
  }
1009
1066
 
1010
1067
  return prefs
1011
- .filter((p) => !AppBskyActorDefs.isMutedWordsPref(p))
1012
- .concat([
1013
- { ...mutedWordsPref, $type: 'app.bsky.actor.defs#mutedWordsPref' },
1014
- ])
1015
1068
  })
1016
1069
  }
1017
1070
 
1071
+ /**
1072
+ * Remove a muted word from user preferences.
1073
+ */
1018
1074
  async removeMutedWord(mutedWord: AppBskyActorDefs.MutedWord) {
1019
1075
  await updatePreferences(this, (prefs: AppBskyActorDefs.Preferences) => {
1020
1076
  const mutedWordsPref = prefs.findLast(
@@ -1025,22 +1081,39 @@ export class BskyAgent extends AtpAgent {
1025
1081
 
1026
1082
  if (mutedWordsPref && AppBskyActorDefs.isMutedWordsPref(mutedWordsPref)) {
1027
1083
  for (let i = 0; i < mutedWordsPref.items.length; i++) {
1028
- const existing = mutedWordsPref.items[i]
1029
- if (existing.value === mutedWord.value) {
1084
+ const match = matchMutedWord(mutedWordsPref.items[i], mutedWord)
1085
+
1086
+ if (match) {
1030
1087
  mutedWordsPref.items.splice(i, 1)
1031
1088
  break
1032
1089
  }
1033
1090
  }
1091
+
1092
+ /**
1093
+ * Migrate any old muted words that don't have an id
1094
+ */
1095
+ mutedWordsPref.items = migrateLegacyMutedWordsItems(
1096
+ mutedWordsPref.items,
1097
+ )
1098
+
1099
+ return prefs
1100
+ .filter((p) => !AppBskyActorDefs.isMutedWordsPref(p))
1101
+ .concat([
1102
+ { ...mutedWordsPref, $type: 'app.bsky.actor.defs#mutedWordsPref' },
1103
+ ])
1034
1104
  }
1035
1105
 
1036
1106
  return prefs
1037
- .filter((p) => !AppBskyActorDefs.isMutedWordsPref(p))
1038
- .concat([
1039
- { ...mutedWordsPref, $type: 'app.bsky.actor.defs#mutedWordsPref' },
1040
- ])
1041
1107
  })
1042
1108
  }
1043
1109
 
1110
+ /**
1111
+ * Convenience method to remove muted words from user preferences
1112
+ */
1113
+ async removeMutedWords(mutedWords: AppBskyActorDefs.MutedWord[]) {
1114
+ await Promise.all(mutedWords.map((word) => this.removeMutedWord(word)))
1115
+ }
1116
+
1044
1117
  async hidePost(postUri: string) {
1045
1118
  await updateHiddenPost(this, postUri, 'hide')
1046
1119
  }
@@ -1369,3 +1442,24 @@ function isBskyPrefs(v: any): v is BskyPreferences {
1369
1442
  function isModPrefs(v: any): v is ModerationPrefs {
1370
1443
  return v && typeof v === 'object' && 'labelers' in v
1371
1444
  }
1445
+
1446
+ function migrateLegacyMutedWordsItems(items: AppBskyActorDefs.MutedWord[]) {
1447
+ return items.map((item) => ({
1448
+ ...item,
1449
+ id: item.id || TID.nextStr(),
1450
+ }))
1451
+ }
1452
+
1453
+ function matchMutedWord(
1454
+ existingWord: AppBskyActorDefs.MutedWord,
1455
+ newWord: AppBskyActorDefs.MutedWord,
1456
+ ): boolean {
1457
+ // id is undefined in legacy implementation
1458
+ const existingId = existingWord.id
1459
+ // prefer matching based on id
1460
+ const matchById = existingId && existingId === newWord.id
1461
+ // handle legacy case where id is not set
1462
+ const legacyMatchByValue = !existingId && existingWord.value === newWord.value
1463
+
1464
+ return matchById || legacyMatchByValue
1465
+ }
@@ -95,12 +95,10 @@ import * as AppBskyActorProfile from './types/app/bsky/actor/profile'
95
95
  import * as AppBskyActorPutPreferences from './types/app/bsky/actor/putPreferences'
96
96
  import * as AppBskyActorSearchActors from './types/app/bsky/actor/searchActors'
97
97
  import * as AppBskyActorSearchActorsTypeahead from './types/app/bsky/actor/searchActorsTypeahead'
98
- import * as AppBskyEmbedDefs from './types/app/bsky/embed/defs'
99
98
  import * as AppBskyEmbedExternal from './types/app/bsky/embed/external'
100
99
  import * as AppBskyEmbedImages from './types/app/bsky/embed/images'
101
100
  import * as AppBskyEmbedRecord from './types/app/bsky/embed/record'
102
101
  import * as AppBskyEmbedRecordWithMedia from './types/app/bsky/embed/recordWithMedia'
103
- import * as AppBskyEmbedVideo from './types/app/bsky/embed/video'
104
102
  import * as AppBskyFeedDefs from './types/app/bsky/feed/defs'
105
103
  import * as AppBskyFeedDescribeFeedGenerator from './types/app/bsky/feed/describeFeedGenerator'
106
104
  import * as AppBskyFeedGenerator from './types/app/bsky/feed/generator'
@@ -294,12 +292,10 @@ export * as AppBskyActorProfile from './types/app/bsky/actor/profile'
294
292
  export * as AppBskyActorPutPreferences from './types/app/bsky/actor/putPreferences'
295
293
  export * as AppBskyActorSearchActors from './types/app/bsky/actor/searchActors'
296
294
  export * as AppBskyActorSearchActorsTypeahead from './types/app/bsky/actor/searchActorsTypeahead'
297
- export * as AppBskyEmbedDefs from './types/app/bsky/embed/defs'
298
295
  export * as AppBskyEmbedExternal from './types/app/bsky/embed/external'
299
296
  export * as AppBskyEmbedImages from './types/app/bsky/embed/images'
300
297
  export * as AppBskyEmbedRecord from './types/app/bsky/embed/record'
301
298
  export * as AppBskyEmbedRecordWithMedia from './types/app/bsky/embed/recordWithMedia'
302
- export * as AppBskyEmbedVideo from './types/app/bsky/embed/video'
303
299
  export * as AppBskyFeedDefs from './types/app/bsky/feed/defs'
304
300
  export * as AppBskyFeedDescribeFeedGenerator from './types/app/bsky/feed/describeFeedGenerator'
305
301
  export * as AppBskyFeedGenerator from './types/app/bsky/feed/generator'
@@ -4341,6 +4341,9 @@ export const schemaDict = {
4341
4341
  description: 'A word that the account owner has muted.',
4342
4342
  required: ['value', 'targets'],
4343
4343
  properties: {
4344
+ id: {
4345
+ type: 'string',
4346
+ },
4344
4347
  value: {
4345
4348
  type: 'string',
4346
4349
  description: 'The muted word itself.',
@@ -4355,6 +4358,19 @@ export const schemaDict = {
4355
4358
  ref: 'lex:app.bsky.actor.defs#mutedWordTarget',
4356
4359
  },
4357
4360
  },
4361
+ actorTarget: {
4362
+ type: 'string',
4363
+ description:
4364
+ 'Groups of users to apply the muted word to. If undefined, applies to all users.',
4365
+ knownValues: ['all', 'exclude-following'],
4366
+ default: 'all',
4367
+ },
4368
+ expiresAt: {
4369
+ type: 'string',
4370
+ format: 'datetime',
4371
+ description:
4372
+ 'The date and time at which the muted word will expire and no longer be applied.',
4373
+ },
4358
4374
  },
4359
4375
  },
4360
4376
  mutedWordsPref: {
@@ -4762,28 +4778,6 @@ export const schemaDict = {
4762
4778
  },
4763
4779
  },
4764
4780
  },
4765
- AppBskyEmbedDefs: {
4766
- lexicon: 1,
4767
- id: 'app.bsky.embed.defs',
4768
- defs: {
4769
- aspectRatio: {
4770
- type: 'object',
4771
- description:
4772
- 'width:height represents an aspect ratio. It may be approximate, and may not correspond to absolute dimensions in any given unit.',
4773
- required: ['width', 'height'],
4774
- properties: {
4775
- width: {
4776
- type: 'integer',
4777
- minimum: 1,
4778
- },
4779
- height: {
4780
- type: 'integer',
4781
- minimum: 1,
4782
- },
4783
- },
4784
- },
4785
- },
4786
- },
4787
4781
  AppBskyEmbedExternal: {
4788
4782
  lexicon: 1,
4789
4783
  id: 'app.bsky.embed.external',
@@ -4888,7 +4882,23 @@ export const schemaDict = {
4888
4882
  },
4889
4883
  aspectRatio: {
4890
4884
  type: 'ref',
4891
- ref: 'lex:app.bsky.embed.defs#aspectRatio',
4885
+ ref: 'lex:app.bsky.embed.images#aspectRatio',
4886
+ },
4887
+ },
4888
+ },
4889
+ aspectRatio: {
4890
+ type: 'object',
4891
+ description:
4892
+ 'width:height represents an aspect ratio. It may be approximate, and may not correspond to absolute dimensions in any given unit.',
4893
+ required: ['width', 'height'],
4894
+ properties: {
4895
+ width: {
4896
+ type: 'integer',
4897
+ minimum: 1,
4898
+ },
4899
+ height: {
4900
+ type: 'integer',
4901
+ minimum: 1,
4892
4902
  },
4893
4903
  },
4894
4904
  },
@@ -4929,7 +4939,7 @@ export const schemaDict = {
4929
4939
  },
4930
4940
  aspectRatio: {
4931
4941
  type: 'ref',
4932
- ref: 'lex:app.bsky.embed.defs#aspectRatio',
4942
+ ref: 'lex:app.bsky.embed.images#aspectRatio',
4933
4943
  },
4934
4944
  },
4935
4945
  },
@@ -5011,7 +5021,6 @@ export const schemaDict = {
5011
5021
  type: 'union',
5012
5022
  refs: [
5013
5023
  'lex:app.bsky.embed.images#view',
5014
- 'lex:app.bsky.embed.video#view',
5015
5024
  'lex:app.bsky.embed.external#view',
5016
5025
  'lex:app.bsky.embed.record#view',
5017
5026
  'lex:app.bsky.embed.recordWithMedia#view',
@@ -5074,11 +5083,7 @@ export const schemaDict = {
5074
5083
  },
5075
5084
  media: {
5076
5085
  type: 'union',
5077
- refs: [
5078
- 'lex:app.bsky.embed.images',
5079
- 'lex:app.bsky.embed.video',
5080
- 'lex:app.bsky.embed.external',
5081
- ],
5086
+ refs: ['lex:app.bsky.embed.images', 'lex:app.bsky.embed.external'],
5082
5087
  },
5083
5088
  },
5084
5089
  },
@@ -5094,7 +5099,6 @@ export const schemaDict = {
5094
5099
  type: 'union',
5095
5100
  refs: [
5096
5101
  'lex:app.bsky.embed.images#view',
5097
- 'lex:app.bsky.embed.video#view',
5098
5102
  'lex:app.bsky.embed.external#view',
5099
5103
  ],
5100
5104
  },
@@ -5102,85 +5106,6 @@ export const schemaDict = {
5102
5106
  },
5103
5107
  },
5104
5108
  },
5105
- AppBskyEmbedVideo: {
5106
- lexicon: 1,
5107
- id: 'app.bsky.embed.video',
5108
- description: 'A video embedded in a Bluesky record (eg, a post).',
5109
- defs: {
5110
- main: {
5111
- type: 'object',
5112
- required: ['video'],
5113
- properties: {
5114
- video: {
5115
- type: 'blob',
5116
- accept: ['video/mp4'],
5117
- maxSize: 50000000,
5118
- },
5119
- captions: {
5120
- type: 'array',
5121
- items: {
5122
- type: 'ref',
5123
- ref: 'lex:app.bsky.embed.video#caption',
5124
- },
5125
- maxLength: 20,
5126
- },
5127
- alt: {
5128
- type: 'string',
5129
- description:
5130
- 'Alt text description of the video, for accessibility.',
5131
- maxGraphemes: 1000,
5132
- maxLength: 10000,
5133
- },
5134
- aspectRatio: {
5135
- type: 'ref',
5136
- ref: 'lex:app.bsky.embed.defs#aspectRatio',
5137
- },
5138
- },
5139
- },
5140
- caption: {
5141
- type: 'object',
5142
- required: ['lang', 'file'],
5143
- properties: {
5144
- lang: {
5145
- type: 'string',
5146
- format: 'language',
5147
- },
5148
- file: {
5149
- type: 'blob',
5150
- accept: ['text/vtt'],
5151
- maxSize: 20000,
5152
- },
5153
- },
5154
- },
5155
- view: {
5156
- type: 'object',
5157
- required: ['cid', 'playlist'],
5158
- properties: {
5159
- cid: {
5160
- type: 'string',
5161
- format: 'cid',
5162
- },
5163
- playlist: {
5164
- type: 'string',
5165
- format: 'uri',
5166
- },
5167
- thumbnail: {
5168
- type: 'string',
5169
- format: 'uri',
5170
- },
5171
- alt: {
5172
- type: 'string',
5173
- maxGraphemes: 1000,
5174
- maxLength: 10000,
5175
- },
5176
- aspectRatio: {
5177
- type: 'ref',
5178
- ref: 'lex:app.bsky.embed.defs#aspectRatio',
5179
- },
5180
- },
5181
- },
5182
- },
5183
- },
5184
5109
  AppBskyFeedDefs: {
5185
5110
  lexicon: 1,
5186
5111
  id: 'app.bsky.feed.defs',
@@ -5208,7 +5133,6 @@ export const schemaDict = {
5208
5133
  type: 'union',
5209
5134
  refs: [
5210
5135
  'lex:app.bsky.embed.images#view',
5211
- 'lex:app.bsky.embed.video#view',
5212
5136
  'lex:app.bsky.embed.external#view',
5213
5137
  'lex:app.bsky.embed.record#view',
5214
5138
  'lex:app.bsky.embed.recordWithMedia#view',
@@ -6563,7 +6487,6 @@ export const schemaDict = {
6563
6487
  type: 'union',
6564
6488
  refs: [
6565
6489
  'lex:app.bsky.embed.images',
6566
- 'lex:app.bsky.embed.video',
6567
6490
  'lex:app.bsky.embed.external',
6568
6491
  'lex:app.bsky.embed.record',
6569
6492
  'lex:app.bsky.embed.recordWithMedia',
@@ -11833,12 +11756,10 @@ export const ids = {
11833
11756
  AppBskyActorPutPreferences: 'app.bsky.actor.putPreferences',
11834
11757
  AppBskyActorSearchActors: 'app.bsky.actor.searchActors',
11835
11758
  AppBskyActorSearchActorsTypeahead: 'app.bsky.actor.searchActorsTypeahead',
11836
- AppBskyEmbedDefs: 'app.bsky.embed.defs',
11837
11759
  AppBskyEmbedExternal: 'app.bsky.embed.external',
11838
11760
  AppBskyEmbedImages: 'app.bsky.embed.images',
11839
11761
  AppBskyEmbedRecord: 'app.bsky.embed.record',
11840
11762
  AppBskyEmbedRecordWithMedia: 'app.bsky.embed.recordWithMedia',
11841
- AppBskyEmbedVideo: 'app.bsky.embed.video',
11842
11763
  AppBskyFeedDefs: 'app.bsky.feed.defs',
11843
11764
  AppBskyFeedDescribeFeedGenerator: 'app.bsky.feed.describeFeedGenerator',
11844
11765
  AppBskyFeedGenerator: 'app.bsky.feed.generator',
@@ -370,10 +370,15 @@ export type MutedWordTarget = 'content' | 'tag' | (string & {})
370
370
 
371
371
  /** A word that the account owner has muted. */
372
372
  export interface MutedWord {
373
+ id?: string
373
374
  /** The muted word itself. */
374
375
  value: string
375
376
  /** The intended targets of the muted word. */
376
377
  targets: MutedWordTarget[]
378
+ /** Groups of users to apply the muted word to. If undefined, applies to all users. */
379
+ actorTarget: 'all' | 'exclude-following' | (string & {})
380
+ /** The date and time at which the muted word will expire and no longer be applied. */
381
+ expiresAt?: string
377
382
  [k: string]: unknown
378
383
  }
379
384
 
@@ -5,7 +5,6 @@ import { ValidationResult, BlobRef } from '@atproto/lexicon'
5
5
  import { isObj, hasProp } from '../../../../util'
6
6
  import { lexicons } from '../../../../lexicons'
7
7
  import { CID } from 'multiformats/cid'
8
- import * as AppBskyEmbedDefs from './defs'
9
8
 
10
9
  export interface Main {
11
10
  images: Image[]
@@ -29,7 +28,7 @@ export interface Image {
29
28
  image: BlobRef
30
29
  /** Alt text description of the image, for accessibility. */
31
30
  alt: string
32
- aspectRatio?: AppBskyEmbedDefs.AspectRatio
31
+ aspectRatio?: AspectRatio
33
32
  [k: string]: unknown
34
33
  }
35
34
 
@@ -43,6 +42,25 @@ export function validateImage(v: unknown): ValidationResult {
43
42
  return lexicons.validate('app.bsky.embed.images#image', v)
44
43
  }
45
44
 
45
+ /** width:height represents an aspect ratio. It may be approximate, and may not correspond to absolute dimensions in any given unit. */
46
+ export interface AspectRatio {
47
+ width: number
48
+ height: number
49
+ [k: string]: unknown
50
+ }
51
+
52
+ export function isAspectRatio(v: unknown): v is AspectRatio {
53
+ return (
54
+ isObj(v) &&
55
+ hasProp(v, '$type') &&
56
+ v.$type === 'app.bsky.embed.images#aspectRatio'
57
+ )
58
+ }
59
+
60
+ export function validateAspectRatio(v: unknown): ValidationResult {
61
+ return lexicons.validate('app.bsky.embed.images#aspectRatio', v)
62
+ }
63
+
46
64
  export interface View {
47
65
  images: ViewImage[]
48
66
  [k: string]: unknown
@@ -65,7 +83,7 @@ export interface ViewImage {
65
83
  fullsize: string
66
84
  /** Alt text description of the image, for accessibility. */
67
85
  alt: string
68
- aspectRatio?: AppBskyEmbedDefs.AspectRatio
86
+ aspectRatio?: AspectRatio
69
87
  [k: string]: unknown
70
88
  }
71
89
 
@@ -12,7 +12,6 @@ import * as AppBskyLabelerDefs from '../labeler/defs'
12
12
  import * as AppBskyActorDefs from '../actor/defs'
13
13
  import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs'
14
14
  import * as AppBskyEmbedImages from './images'
15
- import * as AppBskyEmbedVideo from './video'
16
15
  import * as AppBskyEmbedExternal from './external'
17
16
  import * as AppBskyEmbedRecordWithMedia from './recordWithMedia'
18
17
 
@@ -69,7 +68,6 @@ export interface ViewRecord {
69
68
  likeCount?: number
70
69
  embeds?: (
71
70
  | AppBskyEmbedImages.View
72
- | AppBskyEmbedVideo.View
73
71
  | AppBskyEmbedExternal.View
74
72
  | View
75
73
  | AppBskyEmbedRecordWithMedia.View
@@ -7,14 +7,12 @@ import { lexicons } from '../../../../lexicons'
7
7
  import { CID } from 'multiformats/cid'
8
8
  import * as AppBskyEmbedRecord from './record'
9
9
  import * as AppBskyEmbedImages from './images'
10
- import * as AppBskyEmbedVideo from './video'
11
10
  import * as AppBskyEmbedExternal from './external'
12
11
 
13
12
  export interface Main {
14
13
  record: AppBskyEmbedRecord.Main
15
14
  media:
16
15
  | AppBskyEmbedImages.Main
17
- | AppBskyEmbedVideo.Main
18
16
  | AppBskyEmbedExternal.Main
19
17
  | { $type: string; [k: string]: unknown }
20
18
  [k: string]: unknown
@@ -37,7 +35,6 @@ export interface View {
37
35
  record: AppBskyEmbedRecord.View
38
36
  media:
39
37
  | AppBskyEmbedImages.View
40
- | AppBskyEmbedVideo.View
41
38
  | AppBskyEmbedExternal.View
42
39
  | { $type: string; [k: string]: unknown }
43
40
  [k: string]: unknown
@@ -7,7 +7,6 @@ import { lexicons } from '../../../../lexicons'
7
7
  import { CID } from 'multiformats/cid'
8
8
  import * as AppBskyActorDefs from '../actor/defs'
9
9
  import * as AppBskyEmbedImages from '../embed/images'
10
- import * as AppBskyEmbedVideo from '../embed/video'
11
10
  import * as AppBskyEmbedExternal from '../embed/external'
12
11
  import * as AppBskyEmbedRecord from '../embed/record'
13
12
  import * as AppBskyEmbedRecordWithMedia from '../embed/recordWithMedia'
@@ -22,7 +21,6 @@ export interface PostView {
22
21
  record: {}
23
22
  embed?:
24
23
  | AppBskyEmbedImages.View
25
- | AppBskyEmbedVideo.View
26
24
  | AppBskyEmbedExternal.View
27
25
  | AppBskyEmbedRecord.View
28
26
  | AppBskyEmbedRecordWithMedia.View
@@ -7,7 +7,6 @@ import { lexicons } from '../../../../lexicons'
7
7
  import { CID } from 'multiformats/cid'
8
8
  import * as AppBskyRichtextFacet from '../richtext/facet'
9
9
  import * as AppBskyEmbedImages from '../embed/images'
10
- import * as AppBskyEmbedVideo from '../embed/video'
11
10
  import * as AppBskyEmbedExternal from '../embed/external'
12
11
  import * as AppBskyEmbedRecord from '../embed/record'
13
12
  import * as AppBskyEmbedRecordWithMedia from '../embed/recordWithMedia'
@@ -24,7 +23,6 @@ export interface Record {
24
23
  reply?: ReplyRef
25
24
  embed?:
26
25
  | AppBskyEmbedImages.Main
27
- | AppBskyEmbedVideo.Main
28
26
  | AppBskyEmbedExternal.Main
29
27
  | AppBskyEmbedRecord.Main
30
28
  | AppBskyEmbedRecordWithMedia.Main
@@ -27,12 +27,14 @@ export function hasMutedWord({
27
27
  facets,
28
28
  outlineTags,
29
29
  languages,
30
+ actor,
30
31
  }: {
31
32
  mutedWords: AppBskyActorDefs.MutedWord[]
32
33
  text: string
33
34
  facets?: AppBskyRichtextFacet.Main[]
34
35
  outlineTags?: string[]
35
36
  languages?: string[]
37
+ actor?: AppBskyActorDefs.ProfileView
36
38
  }) {
37
39
  const exception = LANGUAGE_EXCEPTIONS.includes(languages?.[0] || '')
38
40
  const tags = ([] as string[])
@@ -48,6 +50,15 @@ export function hasMutedWord({
48
50
  const mutedWord = mute.value.toLowerCase()
49
51
  const postText = text.toLowerCase()
50
52
 
53
+ // expired, ignore
54
+ if (mute.expiresAt && mute.expiresAt < new Date().toISOString()) continue
55
+
56
+ if (
57
+ mute.actorTarget === 'exclude-following' &&
58
+ Boolean(actor?.viewer?.following)
59
+ )
60
+ continue
61
+
51
62
  // `content` applies to tags as well
52
63
  if (tags.includes(mutedWord)) return true
53
64
  // rest of the checks are for `content` only