@atproto/api 0.20.8 → 0.20.9

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 (36) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/client/index.d.ts +1 -0
  3. package/dist/client/index.d.ts.map +1 -1
  4. package/dist/client/index.js +1 -0
  5. package/dist/client/index.js.map +1 -1
  6. package/dist/client/lexicons.d.ts +214 -10
  7. package/dist/client/lexicons.d.ts.map +1 -1
  8. package/dist/client/lexicons.js +108 -0
  9. package/dist/client/lexicons.js.map +1 -1
  10. package/dist/client/types/app/bsky/draft/defs.d.ts +10 -0
  11. package/dist/client/types/app/bsky/draft/defs.d.ts.map +1 -1
  12. package/dist/client/types/app/bsky/draft/defs.js +7 -0
  13. package/dist/client/types/app/bsky/draft/defs.js.map +1 -1
  14. package/dist/client/types/app/bsky/embed/gallery.d.ts +45 -0
  15. package/dist/client/types/app/bsky/embed/gallery.d.ts.map +1 -0
  16. package/dist/client/types/app/bsky/embed/gallery.js +33 -0
  17. package/dist/client/types/app/bsky/embed/gallery.js.map +1 -0
  18. package/dist/client/types/app/bsky/embed/record.d.ts +2 -1
  19. package/dist/client/types/app/bsky/embed/record.d.ts.map +1 -1
  20. package/dist/client/types/app/bsky/embed/record.js.map +1 -1
  21. package/dist/client/types/app/bsky/embed/recordWithMedia.d.ts +3 -2
  22. package/dist/client/types/app/bsky/embed/recordWithMedia.d.ts.map +1 -1
  23. package/dist/client/types/app/bsky/embed/recordWithMedia.js.map +1 -1
  24. package/dist/client/types/app/bsky/feed/defs.d.ts +2 -1
  25. package/dist/client/types/app/bsky/feed/defs.d.ts.map +1 -1
  26. package/dist/client/types/app/bsky/feed/defs.js.map +1 -1
  27. package/dist/client/types/app/bsky/feed/post.d.ts +2 -1
  28. package/dist/client/types/app/bsky/feed/post.d.ts.map +1 -1
  29. package/dist/client/types/app/bsky/feed/post.js.map +1 -1
  30. package/dist/moderation/subjects/post.d.ts.map +1 -1
  31. package/dist/moderation/subjects/post.js +69 -1
  32. package/dist/moderation/subjects/post.js.map +1 -1
  33. package/package.json +4 -4
  34. package/src/moderation/subjects/post.ts +73 -0
  35. package/tests/moderation-mutewords.test.ts +178 -1
  36. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,4 +1,4 @@
1
- import { AppBskyEmbedExternal, AppBskyEmbedImages, AppBskyEmbedRecord, AppBskyEmbedRecordWithMedia, AppBskyFeedPost, } from '../../client/index.js';
1
+ import { AppBskyEmbedExternal, AppBskyEmbedGallery, AppBskyEmbedImages, AppBskyEmbedRecord, AppBskyEmbedRecordWithMedia, AppBskyFeedPost, } from '../../client/index.js';
2
2
  import { ModerationDecision } from '../decision.js';
3
3
  import { matchMuteWords } from '../mutewords.js';
4
4
  import { decideAccount } from './account.js';
@@ -136,6 +136,22 @@ function matchAllMuteWords(subject, mutedWords) {
136
136
  }
137
137
  }
138
138
  }
139
+ if (post.embed && AppBskyEmbedGallery.isMain(post.embed)) {
140
+ // post gallery items
141
+ for (const item of post.embed.items) {
142
+ if (AppBskyEmbedGallery.isImage(item)) {
143
+ const matches = matchMuteWords({
144
+ mutedWords,
145
+ text: item.alt,
146
+ languages: post.langs,
147
+ actor: postAuthor,
148
+ });
149
+ if (matches) {
150
+ return matches;
151
+ }
152
+ }
153
+ }
154
+ }
139
155
  }
140
156
  const { embed } = subject;
141
157
  if (embed) {
@@ -172,6 +188,22 @@ function matchAllMuteWords(subject, mutedWords) {
172
188
  }
173
189
  }
174
190
  }
191
+ // quoted post's gallery
192
+ if (AppBskyEmbedGallery.isMain(embeddedPost.embed)) {
193
+ for (const item of embeddedPost.embed.items) {
194
+ if (AppBskyEmbedGallery.isImage(item)) {
195
+ const matches = matchMuteWords({
196
+ mutedWords,
197
+ text: item.alt,
198
+ languages: embeddedPost.langs,
199
+ actor: embedAuthor,
200
+ });
201
+ if (matches) {
202
+ return matches;
203
+ }
204
+ }
205
+ }
206
+ }
175
207
  // quoted post's link card
176
208
  if (AppBskyEmbedExternal.isMain(embeddedPost.embed)) {
177
209
  const { external } = embeddedPost.embed;
@@ -215,6 +247,24 @@ function matchAllMuteWords(subject, mutedWords) {
215
247
  }
216
248
  }
217
249
  }
250
+ // quoted post's gallery when it did a quote + media
251
+ if (AppBskyEmbedGallery.isMain(embeddedPost.embed.media)) {
252
+ for (const item of embeddedPost.embed.media.items) {
253
+ if (AppBskyEmbedGallery.isImage(item)) {
254
+ const matches = matchMuteWords({
255
+ mutedWords,
256
+ text: item.alt,
257
+ languages: AppBskyFeedPost.isRecord(embeddedPost.record)
258
+ ? embeddedPost.langs
259
+ : [],
260
+ actor: embedAuthor,
261
+ });
262
+ if (matches) {
263
+ return matches;
264
+ }
265
+ }
266
+ }
267
+ }
218
268
  }
219
269
  }
220
270
  }
@@ -266,6 +316,24 @@ function matchAllMuteWords(subject, mutedWords) {
266
316
  }
267
317
  }
268
318
  }
319
+ // quoted post gallery
320
+ if (AppBskyEmbedGallery.isView(embed.media)) {
321
+ for (const item of embed.media.items) {
322
+ if (AppBskyEmbedGallery.isViewImage(item)) {
323
+ const matches = matchMuteWords({
324
+ mutedWords,
325
+ text: item.alt,
326
+ languages: AppBskyFeedPost.isRecord(subject.record)
327
+ ? subject.record.langs
328
+ : [],
329
+ actor: embedAuthor,
330
+ });
331
+ if (matches) {
332
+ return matches;
333
+ }
334
+ }
335
+ }
336
+ }
269
337
  if (AppBskyEmbedExternal.isView(embed.media)) {
270
338
  const { external } = embed.media;
271
339
  const matches = matchMuteWords({
@@ -1 +1 @@
1
- {"version":3,"file":"post.js","sourceRoot":"","sources":["../../../src/moderation/subjects/post.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,2BAA2B,EAC3B,eAAe,GAChB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAiB,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAE/D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE5C,MAAM,UAAU,UAAU,CACxB,OAA8B,EAC9B,IAAoB;IAEpB,OAAO,kBAAkB,CAAC,KAAK,CAC7B,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,EAC5B,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,EAC7C,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,EACnC,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CACpC,CAAA;AACH,CAAC;AAED,SAAS,aAAa,CACpB,OAA8B,EAC9B,IAAoB;IAEpB,MAAM,GAAG,GAAG,IAAI,kBAAkB,EAAE,CAAA;IAEpC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAC9B,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,OAAO,CAAC,CAAA;IAChD,IAAI,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnC,GAAG,CAAC,QAAQ,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,CAAA;QACtC,CAAC;IACH,CAAC;IACD,GAAG,CAAC,SAAS,CAAC,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAA;IAC/D,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACd,GAAG,CAAC,YAAY,CAAC,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAA;IACrE,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,WAAW,CAClB,KAIqB,EACrB,IAAoB;IAEpB,IAAI,KAAK,EAAE,CAAC;QACV,IACE,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC;YAC/B,2BAA2B,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5C,kBAAkB,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,EAC7C,CAAC;YACD,aAAa;YACb,OAAO,gBAAgB,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QAC7C,CAAC;aAAM,IACL,2BAA2B,CAAC,MAAM,CAAC,KAAK,CAAC;YACzC,kBAAkB,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EACpD,CAAC;YACD,yBAAyB;YACzB,OAAO,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QACpD,CAAC;aAAM,IACL,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC;YAC/B,2BAA2B,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5C,kBAAkB,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,EAC9C,CAAC;YACD,qBAAqB;YACrB,OAAO,uBAAuB,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QACpD,CAAC;aAAM,IACL,2BAA2B,CAAC,MAAM,CAAC,KAAK,CAAC;YACzC,kBAAkB,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EACrD,CAAC;YACD,iCAAiC;YACjC,OAAO,uBAAuB,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QAC3D,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,gBAAgB,CACvB,OAAsC,EACtC,IAAoB;IAEpB,MAAM,GAAG,GAAG,IAAI,kBAAkB,EAAE,CAAA;IACpC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAC9B,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,OAAO,CAAC,CAAA;IAChD,IAAI,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnC,GAAG,CAAC,QAAQ,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,CAAA;QACtC,CAAC;IACH,CAAC;IACD,OAAO,kBAAkB,CAAC,KAAK,CAC7B,GAAG,EACH,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,EACnC,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CACpC,CAAA;AACH,CAAC;AAED,SAAS,uBAAuB,CAC9B,OAAuC,EACvC,IAAoB;IAEpB,MAAM,GAAG,GAAG,IAAI,kBAAkB,EAAE,CAAA;IACpC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAC9B,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,OAAO,CAAC,CAAA;IAChD,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;QACjC,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC;YACvC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;QACxD,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;QAC5C,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;QACpC,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;YAC1C,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;QAC9D,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;QAClD,CAAC;IACH,CAAC;IACD,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IAClD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,eAAe,CACtB,OAA8B,EAC9B,WAAiC;IAEjC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;QACzB,OAAO,KAAK,CAAA;IACd,CAAC;IACD,IAAI,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACtC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,IACE,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YACxC,kBAAkB,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC;YACrD,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAC9C,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC;QACD,IACE,2BAA2B,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YACjD,kBAAkB,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;YAC5D,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EACrD,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,iBAAiB,CACxB,OAA8B,EAC9B,UAAoD;IAEpD,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;QACxB,OAAM;IACR,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAA;IAEjC,IAAI,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,MAAgC,CAAA;QAErD,MAAM,OAAO,GAAG,cAAc,CAAC;YAC7B,UAAU;YACV,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,WAAW,EAAE,IAAI,CAAC,IAAI;YACtB,SAAS,EAAE,IAAI,CAAC,KAAK;YACrB,KAAK,EAAE,UAAU;SAClB,CAAC,CAAA;QACF,YAAY;QACZ,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,IAAI,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACxD,cAAc;YACd,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;gBACtC,MAAM,OAAO,GAAG,cAAc,CAAC;oBAC7B,UAAU;oBACV,IAAI,EAAE,KAAK,CAAC,GAAG;oBACf,SAAS,EAAE,IAAI,CAAC,KAAK;oBACrB,KAAK,EAAE,UAAU;iBAClB,CAAC,CAAA;gBACF,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,OAAO,CAAA;gBAChB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAA;IACzB,IAAI,KAAK,EAAE,CAAC;QACV,aAAa;QACb,IACE,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC;YAC/B,2BAA2B,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5C,kBAAkB,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,EAC7C,CAAC;YACD,IAAI,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjD,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,KAA+B,CAAA;gBACjE,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAA;gBACvC,MAAM,OAAO,GAAG,cAAc,CAAC;oBAC7B,UAAU;oBACV,IAAI,EAAE,YAAY,CAAC,IAAI;oBACvB,MAAM,EAAE,YAAY,CAAC,MAAM;oBAC3B,WAAW,EAAE,YAAY,CAAC,IAAI;oBAC9B,SAAS,EAAE,YAAY,CAAC,KAAK;oBAC7B,KAAK,EAAE,WAAW;iBACnB,CAAC,CAAA;gBAEF,mBAAmB;gBACnB,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,OAAO,CAAA;gBAChB,CAAC;gBAED,uBAAuB;gBACvB,IAAI,kBAAkB,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;oBAClD,KAAK,MAAM,KAAK,IAAI,YAAY,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;wBAC9C,MAAM,OAAO,GAAG,cAAc,CAAC;4BAC7B,UAAU;4BACV,IAAI,EAAE,KAAK,CAAC,GAAG;4BACf,SAAS,EAAE,YAAY,CAAC,KAAK;4BAC7B,KAAK,EAAE,WAAW;yBACnB,CAAC,CAAA;wBACF,IAAI,OAAO,EAAE,CAAC;4BACZ,OAAO,OAAO,CAAA;wBAChB,CAAC;oBACH,CAAC;gBACH,CAAC;gBAED,0BAA0B;gBAC1B,IAAI,oBAAoB,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;oBACpD,MAAM,EAAE,QAAQ,EAAE,GAAG,YAAY,CAAC,KAAK,CAAA;oBACvC,MAAM,OAAO,GAAG,cAAc,CAAC;wBAC7B,UAAU;wBACV,IAAI,EAAE,QAAQ,CAAC,KAAK,GAAG,GAAG,GAAG,QAAQ,CAAC,WAAW;wBACjD,SAAS,EAAE,EAAE;wBACb,KAAK,EAAE,WAAW;qBACnB,CAAC,CAAA;oBACF,IAAI,OAAO,EAAE,CAAC;wBACZ,OAAO,OAAO,CAAA;oBAChB,CAAC;gBACH,CAAC;gBAED,IAAI,2BAA2B,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC3D,sDAAsD;oBACtD,IAAI,oBAAoB,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;wBAC1D,MAAM,EAAE,QAAQ,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAA;wBAC7C,MAAM,OAAO,GAAG,cAAc,CAAC;4BAC7B,UAAU;4BACV,IAAI,EAAE,QAAQ,CAAC,KAAK,GAAG,GAAG,GAAG,QAAQ,CAAC,WAAW;4BACjD,SAAS,EAAE,EAAE;4BACb,KAAK,EAAE,WAAW;yBACnB,CAAC,CAAA;wBACF,IAAI,OAAO,EAAE,CAAC;4BACZ,OAAO,OAAO,CAAA;wBAChB,CAAC;oBACH,CAAC;oBAED,mDAAmD;oBACnD,IAAI,kBAAkB,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;wBACxD,KAAK,MAAM,KAAK,IAAI,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;4BACpD,MAAM,OAAO,GAAG,cAAc,CAAC;gCAC7B,UAAU;gCACV,IAAI,EAAE,KAAK,CAAC,GAAG;gCACf,SAAS,EAAE,eAAe,CAAC,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC;oCACtD,CAAC,CAAC,YAAY,CAAC,KAAK;oCACpB,CAAC,CAAC,EAAE;gCACN,KAAK,EAAE,WAAW;6BACnB,CAAC,CAAA;4BACF,IAAI,OAAO,EAAE,CAAC;gCACZ,OAAO,OAAO,CAAA;4BAChB,CAAC;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QACD,YAAY;aACP,IAAI,oBAAoB,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;YAC1B,MAAM,OAAO,GAAG,cAAc,CAAC;gBAC7B,UAAU;gBACV,IAAI,EAAE,QAAQ,CAAC,KAAK,GAAG,GAAG,GAAG,QAAQ,CAAC,WAAW;gBACjD,SAAS,EAAE,EAAE;gBACb,KAAK,EAAE,UAAU;aAClB,CAAC,CAAA;YACF,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,OAAO,CAAA;YAChB,CAAC;QACH,CAAC;QACD,wBAAwB;aACnB,IACH,2BAA2B,CAAC,MAAM,CAAC,KAAK,CAAC;YACzC,kBAAkB,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EACpD,CAAC;YACD,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAA;YAE9C,mBAAmB;YACnB,IAAI,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxD,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,KAA+B,CAAA;gBAChE,MAAM,OAAO,GAAG,cAAc,CAAC;oBAC7B,UAAU;oBACV,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,WAAW,EAAE,IAAI,CAAC,IAAI;oBACtB,SAAS,EAAE,IAAI,CAAC,KAAK;oBACrB,KAAK,EAAE,WAAW;iBACnB,CAAC,CAAA;gBACF,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,OAAO,CAAA;gBAChB,CAAC;YACH,CAAC;YAED,qBAAqB;YACrB,IAAI,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3C,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;oBACvC,MAAM,OAAO,GAAG,cAAc,CAAC;wBAC7B,UAAU;wBACV,IAAI,EAAE,KAAK,CAAC,GAAG;wBACf,SAAS,EAAE,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC;4BACjD,CAAC,CAAE,OAAO,CAAC,MAAiC,CAAC,KAAK;4BAClD,CAAC,CAAC,EAAE;wBACN,KAAK,EAAE,WAAW;qBACnB,CAAC,CAAA;oBACF,IAAI,OAAO,EAAE,CAAC;wBACZ,OAAO,OAAO,CAAA;oBAChB,CAAC;gBACH,CAAC;YACH,CAAC;YAED,IAAI,oBAAoB,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC7C,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,KAAK,CAAA;gBAChC,MAAM,OAAO,GAAG,cAAc,CAAC;oBAC7B,UAAU;oBACV,IAAI,EAAE,QAAQ,CAAC,KAAK,GAAG,GAAG,GAAG,QAAQ,CAAC,WAAW;oBACjD,SAAS,EAAE,EAAE;oBACb,KAAK,EAAE,WAAW;iBACnB,CAAC,CAAA;gBACF,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,OAAO,CAAA;gBAChB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["import {\n AppBskyActorDefs,\n AppBskyEmbedExternal,\n AppBskyEmbedImages,\n AppBskyEmbedRecord,\n AppBskyEmbedRecordWithMedia,\n AppBskyFeedPost,\n} from '../../client/index.js'\nimport { $Typed } from '../../client/util.js'\nimport { ModerationDecision } from '../decision.js'\nimport { MuteWordMatch, matchMuteWords } from '../mutewords.js'\nimport { ModerationOpts, ModerationSubjectPost } from '../types.js'\nimport { decideAccount } from './account.js'\nimport { decideProfile } from './profile.js'\n\nexport function decidePost(\n subject: ModerationSubjectPost,\n opts: ModerationOpts,\n): ModerationDecision {\n return ModerationDecision.merge(\n decideSubject(subject, opts),\n decideEmbed(subject.embed, opts)?.downgrade(),\n decideAccount(subject.author, opts),\n decideProfile(subject.author, opts),\n )\n}\n\nfunction decideSubject(\n subject: ModerationSubjectPost,\n opts: ModerationOpts,\n): ModerationDecision {\n const acc = new ModerationDecision()\n\n acc.setDid(subject.author.did)\n acc.setIsMe(subject.author.did === opts.userDid)\n if (subject.labels?.length) {\n for (const label of subject.labels) {\n acc.addLabel('content', label, opts)\n }\n }\n acc.addHidden(checkHiddenPost(subject, opts.prefs.hiddenPosts))\n if (!acc.isMe) {\n acc.addMutedWord(matchAllMuteWords(subject, opts.prefs.mutedWords))\n }\n\n return acc\n}\n\nfunction decideEmbed(\n embed:\n | undefined\n | $Typed<AppBskyEmbedRecord.View>\n | $Typed<AppBskyEmbedRecordWithMedia.View>\n | { $type: string },\n opts: ModerationOpts,\n) {\n if (embed) {\n if (\n (AppBskyEmbedRecord.isView(embed) ||\n AppBskyEmbedRecordWithMedia.isView(embed)) &&\n AppBskyEmbedRecord.isViewRecord(embed.record)\n ) {\n // quote post\n return decideQuotedPost(embed.record, opts)\n } else if (\n AppBskyEmbedRecordWithMedia.isView(embed) &&\n AppBskyEmbedRecord.isViewRecord(embed.record.record)\n ) {\n // quoted post with media\n return decideQuotedPost(embed.record.record, opts)\n } else if (\n (AppBskyEmbedRecord.isView(embed) ||\n AppBskyEmbedRecordWithMedia.isView(embed)) &&\n AppBskyEmbedRecord.isViewBlocked(embed.record)\n ) {\n // blocked quote post\n return decideBlockedQuotedPost(embed.record, opts)\n } else if (\n AppBskyEmbedRecordWithMedia.isView(embed) &&\n AppBskyEmbedRecord.isViewBlocked(embed.record.record)\n ) {\n // blocked quoted post with media\n return decideBlockedQuotedPost(embed.record.record, opts)\n }\n }\n\n return undefined\n}\n\nfunction decideQuotedPost(\n subject: AppBskyEmbedRecord.ViewRecord,\n opts: ModerationOpts,\n) {\n const acc = new ModerationDecision()\n acc.setDid(subject.author.did)\n acc.setIsMe(subject.author.did === opts.userDid)\n if (subject.labels?.length) {\n for (const label of subject.labels) {\n acc.addLabel('content', label, opts)\n }\n }\n return ModerationDecision.merge(\n acc,\n decideAccount(subject.author, opts),\n decideProfile(subject.author, opts),\n )\n}\n\nfunction decideBlockedQuotedPost(\n subject: AppBskyEmbedRecord.ViewBlocked,\n opts: ModerationOpts,\n) {\n const acc = new ModerationDecision()\n acc.setDid(subject.author.did)\n acc.setIsMe(subject.author.did === opts.userDid)\n if (subject.author.viewer?.muted) {\n if (subject.author.viewer?.mutedByList) {\n acc.addMutedByList(subject.author.viewer?.mutedByList)\n } else {\n acc.addMuted(subject.author.viewer?.muted)\n }\n }\n if (subject.author.viewer?.blocking) {\n if (subject.author.viewer?.blockingByList) {\n acc.addBlockingByList(subject.author.viewer?.blockingByList)\n } else {\n acc.addBlocking(subject.author.viewer?.blocking)\n }\n }\n acc.addBlockedBy(subject.author.viewer?.blockedBy)\n return acc\n}\n\nfunction checkHiddenPost(\n subject: ModerationSubjectPost,\n hiddenPosts: string[] | undefined,\n) {\n if (!hiddenPosts?.length) {\n return false\n }\n if (hiddenPosts.includes(subject.uri)) {\n return true\n }\n if (subject.embed) {\n if (\n AppBskyEmbedRecord.isView(subject.embed) &&\n AppBskyEmbedRecord.isViewRecord(subject.embed.record) &&\n hiddenPosts.includes(subject.embed.record.uri)\n ) {\n return true\n }\n if (\n AppBskyEmbedRecordWithMedia.isView(subject.embed) &&\n AppBskyEmbedRecord.isViewRecord(subject.embed.record.record) &&\n hiddenPosts.includes(subject.embed.record.record.uri)\n ) {\n return true\n }\n }\n return false\n}\n\nfunction matchAllMuteWords(\n subject: ModerationSubjectPost,\n mutedWords: AppBskyActorDefs.MutedWord[] | undefined,\n): MuteWordMatch[] | undefined {\n if (!mutedWords?.length) {\n return\n }\n\n const postAuthor = subject.author\n\n if (AppBskyFeedPost.isRecord(subject.record)) {\n const post = subject.record as AppBskyFeedPost.Record\n\n const matches = matchMuteWords({\n mutedWords,\n text: post.text,\n facets: post.facets,\n outlineTags: post.tags,\n languages: post.langs,\n actor: postAuthor,\n })\n // post text\n if (matches) {\n return matches\n }\n\n if (post.embed && AppBskyEmbedImages.isMain(post.embed)) {\n // post images\n for (const image of post.embed.images) {\n const matches = matchMuteWords({\n mutedWords,\n text: image.alt,\n languages: post.langs,\n actor: postAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n }\n\n const { embed } = subject\n if (embed) {\n // quote post\n if (\n (AppBskyEmbedRecord.isView(embed) ||\n AppBskyEmbedRecordWithMedia.isView(embed)) &&\n AppBskyEmbedRecord.isViewRecord(embed.record)\n ) {\n if (AppBskyFeedPost.isRecord(embed.record.value)) {\n const embeddedPost = embed.record.value as AppBskyFeedPost.Record\n const embedAuthor = embed.record.author\n const matches = matchMuteWords({\n mutedWords,\n text: embeddedPost.text,\n facets: embeddedPost.facets,\n outlineTags: embeddedPost.tags,\n languages: embeddedPost.langs,\n actor: embedAuthor,\n })\n\n // quoted post text\n if (matches) {\n return matches\n }\n\n // quoted post's images\n if (AppBskyEmbedImages.isMain(embeddedPost.embed)) {\n for (const image of embeddedPost.embed.images) {\n const matches = matchMuteWords({\n mutedWords,\n text: image.alt,\n languages: embeddedPost.langs,\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n\n // quoted post's link card\n if (AppBskyEmbedExternal.isMain(embeddedPost.embed)) {\n const { external } = embeddedPost.embed\n const matches = matchMuteWords({\n mutedWords,\n text: external.title + ' ' + external.description,\n languages: [],\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n\n if (AppBskyEmbedRecordWithMedia.isMain(embeddedPost.embed)) {\n // quoted post's link card when it did a quote + media\n if (AppBskyEmbedExternal.isMain(embeddedPost.embed.media)) {\n const { external } = embeddedPost.embed.media\n const matches = matchMuteWords({\n mutedWords,\n text: external.title + ' ' + external.description,\n languages: [],\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n\n // quoted post's images when it did a quote + media\n if (AppBskyEmbedImages.isMain(embeddedPost.embed.media)) {\n for (const image of embeddedPost.embed.media.images) {\n const matches = matchMuteWords({\n mutedWords,\n text: image.alt,\n languages: AppBskyFeedPost.isRecord(embeddedPost.record)\n ? embeddedPost.langs\n : [],\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n }\n }\n }\n // link card\n else if (AppBskyEmbedExternal.isView(embed)) {\n const { external } = embed\n const matches = matchMuteWords({\n mutedWords,\n text: external.title + ' ' + external.description,\n languages: [],\n actor: postAuthor,\n })\n if (matches) {\n return matches\n }\n }\n // quote post with media\n else if (\n AppBskyEmbedRecordWithMedia.isView(embed) &&\n AppBskyEmbedRecord.isViewRecord(embed.record.record)\n ) {\n const embedAuthor = embed.record.record.author\n\n // quoted post text\n if (AppBskyFeedPost.isRecord(embed.record.record.value)) {\n const post = embed.record.record.value as AppBskyFeedPost.Record\n const matches = matchMuteWords({\n mutedWords,\n text: post.text,\n facets: post.facets,\n outlineTags: post.tags,\n languages: post.langs,\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n\n // quoted post images\n if (AppBskyEmbedImages.isView(embed.media)) {\n for (const image of embed.media.images) {\n const matches = matchMuteWords({\n mutedWords,\n text: image.alt,\n languages: AppBskyFeedPost.isRecord(subject.record)\n ? (subject.record as AppBskyFeedPost.Record).langs\n : [],\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n\n if (AppBskyEmbedExternal.isView(embed.media)) {\n const { external } = embed.media\n const matches = matchMuteWords({\n mutedWords,\n text: external.title + ' ' + external.description,\n languages: [],\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n }\n}\n"]}
1
+ {"version":3,"file":"post.js","sourceRoot":"","sources":["../../../src/moderation/subjects/post.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AppBskyActorDefs,\n AppBskyEmbedExternal,\n AppBskyEmbedGallery,\n AppBskyEmbedImages,\n AppBskyEmbedRecord,\n AppBskyEmbedRecordWithMedia,\n AppBskyFeedPost,\n} from '../../client/index.js'\nimport { $Typed } from '../../client/util.js'\nimport { ModerationDecision } from '../decision.js'\nimport { MuteWordMatch, matchMuteWords } from '../mutewords.js'\nimport { ModerationOpts, ModerationSubjectPost } from '../types.js'\nimport { decideAccount } from './account.js'\nimport { decideProfile } from './profile.js'\n\nexport function decidePost(\n subject: ModerationSubjectPost,\n opts: ModerationOpts,\n): ModerationDecision {\n return ModerationDecision.merge(\n decideSubject(subject, opts),\n decideEmbed(subject.embed, opts)?.downgrade(),\n decideAccount(subject.author, opts),\n decideProfile(subject.author, opts),\n )\n}\n\nfunction decideSubject(\n subject: ModerationSubjectPost,\n opts: ModerationOpts,\n): ModerationDecision {\n const acc = new ModerationDecision()\n\n acc.setDid(subject.author.did)\n acc.setIsMe(subject.author.did === opts.userDid)\n if (subject.labels?.length) {\n for (const label of subject.labels) {\n acc.addLabel('content', label, opts)\n }\n }\n acc.addHidden(checkHiddenPost(subject, opts.prefs.hiddenPosts))\n if (!acc.isMe) {\n acc.addMutedWord(matchAllMuteWords(subject, opts.prefs.mutedWords))\n }\n\n return acc\n}\n\nfunction decideEmbed(\n embed:\n | undefined\n | $Typed<AppBskyEmbedRecord.View>\n | $Typed<AppBskyEmbedRecordWithMedia.View>\n | { $type: string },\n opts: ModerationOpts,\n) {\n if (embed) {\n if (\n (AppBskyEmbedRecord.isView(embed) ||\n AppBskyEmbedRecordWithMedia.isView(embed)) &&\n AppBskyEmbedRecord.isViewRecord(embed.record)\n ) {\n // quote post\n return decideQuotedPost(embed.record, opts)\n } else if (\n AppBskyEmbedRecordWithMedia.isView(embed) &&\n AppBskyEmbedRecord.isViewRecord(embed.record.record)\n ) {\n // quoted post with media\n return decideQuotedPost(embed.record.record, opts)\n } else if (\n (AppBskyEmbedRecord.isView(embed) ||\n AppBskyEmbedRecordWithMedia.isView(embed)) &&\n AppBskyEmbedRecord.isViewBlocked(embed.record)\n ) {\n // blocked quote post\n return decideBlockedQuotedPost(embed.record, opts)\n } else if (\n AppBskyEmbedRecordWithMedia.isView(embed) &&\n AppBskyEmbedRecord.isViewBlocked(embed.record.record)\n ) {\n // blocked quoted post with media\n return decideBlockedQuotedPost(embed.record.record, opts)\n }\n }\n\n return undefined\n}\n\nfunction decideQuotedPost(\n subject: AppBskyEmbedRecord.ViewRecord,\n opts: ModerationOpts,\n) {\n const acc = new ModerationDecision()\n acc.setDid(subject.author.did)\n acc.setIsMe(subject.author.did === opts.userDid)\n if (subject.labels?.length) {\n for (const label of subject.labels) {\n acc.addLabel('content', label, opts)\n }\n }\n return ModerationDecision.merge(\n acc,\n decideAccount(subject.author, opts),\n decideProfile(subject.author, opts),\n )\n}\n\nfunction decideBlockedQuotedPost(\n subject: AppBskyEmbedRecord.ViewBlocked,\n opts: ModerationOpts,\n) {\n const acc = new ModerationDecision()\n acc.setDid(subject.author.did)\n acc.setIsMe(subject.author.did === opts.userDid)\n if (subject.author.viewer?.muted) {\n if (subject.author.viewer?.mutedByList) {\n acc.addMutedByList(subject.author.viewer?.mutedByList)\n } else {\n acc.addMuted(subject.author.viewer?.muted)\n }\n }\n if (subject.author.viewer?.blocking) {\n if (subject.author.viewer?.blockingByList) {\n acc.addBlockingByList(subject.author.viewer?.blockingByList)\n } else {\n acc.addBlocking(subject.author.viewer?.blocking)\n }\n }\n acc.addBlockedBy(subject.author.viewer?.blockedBy)\n return acc\n}\n\nfunction checkHiddenPost(\n subject: ModerationSubjectPost,\n hiddenPosts: string[] | undefined,\n) {\n if (!hiddenPosts?.length) {\n return false\n }\n if (hiddenPosts.includes(subject.uri)) {\n return true\n }\n if (subject.embed) {\n if (\n AppBskyEmbedRecord.isView(subject.embed) &&\n AppBskyEmbedRecord.isViewRecord(subject.embed.record) &&\n hiddenPosts.includes(subject.embed.record.uri)\n ) {\n return true\n }\n if (\n AppBskyEmbedRecordWithMedia.isView(subject.embed) &&\n AppBskyEmbedRecord.isViewRecord(subject.embed.record.record) &&\n hiddenPosts.includes(subject.embed.record.record.uri)\n ) {\n return true\n }\n }\n return false\n}\n\nfunction matchAllMuteWords(\n subject: ModerationSubjectPost,\n mutedWords: AppBskyActorDefs.MutedWord[] | undefined,\n): MuteWordMatch[] | undefined {\n if (!mutedWords?.length) {\n return\n }\n\n const postAuthor = subject.author\n\n if (AppBskyFeedPost.isRecord(subject.record)) {\n const post = subject.record as AppBskyFeedPost.Record\n\n const matches = matchMuteWords({\n mutedWords,\n text: post.text,\n facets: post.facets,\n outlineTags: post.tags,\n languages: post.langs,\n actor: postAuthor,\n })\n // post text\n if (matches) {\n return matches\n }\n\n if (post.embed && AppBskyEmbedImages.isMain(post.embed)) {\n // post images\n for (const image of post.embed.images) {\n const matches = matchMuteWords({\n mutedWords,\n text: image.alt,\n languages: post.langs,\n actor: postAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n\n if (post.embed && AppBskyEmbedGallery.isMain(post.embed)) {\n // post gallery items\n for (const item of post.embed.items) {\n if (AppBskyEmbedGallery.isImage(item)) {\n const matches = matchMuteWords({\n mutedWords,\n text: item.alt,\n languages: post.langs,\n actor: postAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n }\n }\n\n const { embed } = subject\n if (embed) {\n // quote post\n if (\n (AppBskyEmbedRecord.isView(embed) ||\n AppBskyEmbedRecordWithMedia.isView(embed)) &&\n AppBskyEmbedRecord.isViewRecord(embed.record)\n ) {\n if (AppBskyFeedPost.isRecord(embed.record.value)) {\n const embeddedPost = embed.record.value as AppBskyFeedPost.Record\n const embedAuthor = embed.record.author\n const matches = matchMuteWords({\n mutedWords,\n text: embeddedPost.text,\n facets: embeddedPost.facets,\n outlineTags: embeddedPost.tags,\n languages: embeddedPost.langs,\n actor: embedAuthor,\n })\n\n // quoted post text\n if (matches) {\n return matches\n }\n\n // quoted post's images\n if (AppBskyEmbedImages.isMain(embeddedPost.embed)) {\n for (const image of embeddedPost.embed.images) {\n const matches = matchMuteWords({\n mutedWords,\n text: image.alt,\n languages: embeddedPost.langs,\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n\n // quoted post's gallery\n if (AppBskyEmbedGallery.isMain(embeddedPost.embed)) {\n for (const item of embeddedPost.embed.items) {\n if (AppBskyEmbedGallery.isImage(item)) {\n const matches = matchMuteWords({\n mutedWords,\n text: item.alt,\n languages: embeddedPost.langs,\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n }\n\n // quoted post's link card\n if (AppBskyEmbedExternal.isMain(embeddedPost.embed)) {\n const { external } = embeddedPost.embed\n const matches = matchMuteWords({\n mutedWords,\n text: external.title + ' ' + external.description,\n languages: [],\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n\n if (AppBskyEmbedRecordWithMedia.isMain(embeddedPost.embed)) {\n // quoted post's link card when it did a quote + media\n if (AppBskyEmbedExternal.isMain(embeddedPost.embed.media)) {\n const { external } = embeddedPost.embed.media\n const matches = matchMuteWords({\n mutedWords,\n text: external.title + ' ' + external.description,\n languages: [],\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n\n // quoted post's images when it did a quote + media\n if (AppBskyEmbedImages.isMain(embeddedPost.embed.media)) {\n for (const image of embeddedPost.embed.media.images) {\n const matches = matchMuteWords({\n mutedWords,\n text: image.alt,\n languages: AppBskyFeedPost.isRecord(embeddedPost.record)\n ? embeddedPost.langs\n : [],\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n\n // quoted post's gallery when it did a quote + media\n if (AppBskyEmbedGallery.isMain(embeddedPost.embed.media)) {\n for (const item of embeddedPost.embed.media.items) {\n if (AppBskyEmbedGallery.isImage(item)) {\n const matches = matchMuteWords({\n mutedWords,\n text: item.alt,\n languages: AppBskyFeedPost.isRecord(embeddedPost.record)\n ? embeddedPost.langs\n : [],\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n }\n }\n }\n }\n // link card\n else if (AppBskyEmbedExternal.isView(embed)) {\n const { external } = embed\n const matches = matchMuteWords({\n mutedWords,\n text: external.title + ' ' + external.description,\n languages: [],\n actor: postAuthor,\n })\n if (matches) {\n return matches\n }\n }\n // quote post with media\n else if (\n AppBskyEmbedRecordWithMedia.isView(embed) &&\n AppBskyEmbedRecord.isViewRecord(embed.record.record)\n ) {\n const embedAuthor = embed.record.record.author\n\n // quoted post text\n if (AppBskyFeedPost.isRecord(embed.record.record.value)) {\n const post = embed.record.record.value as AppBskyFeedPost.Record\n const matches = matchMuteWords({\n mutedWords,\n text: post.text,\n facets: post.facets,\n outlineTags: post.tags,\n languages: post.langs,\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n\n // quoted post images\n if (AppBskyEmbedImages.isView(embed.media)) {\n for (const image of embed.media.images) {\n const matches = matchMuteWords({\n mutedWords,\n text: image.alt,\n languages: AppBskyFeedPost.isRecord(subject.record)\n ? (subject.record as AppBskyFeedPost.Record).langs\n : [],\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n\n // quoted post gallery\n if (AppBskyEmbedGallery.isView(embed.media)) {\n for (const item of embed.media.items) {\n if (AppBskyEmbedGallery.isViewImage(item)) {\n const matches = matchMuteWords({\n mutedWords,\n text: item.alt,\n languages: AppBskyFeedPost.isRecord(subject.record)\n ? (subject.record as AppBskyFeedPost.Record).langs\n : [],\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n }\n\n if (AppBskyEmbedExternal.isView(embed.media)) {\n const { external } = embed.media\n const matches = matchMuteWords({\n mutedWords,\n text: external.title + ' ' + external.description,\n languages: [],\n actor: embedAuthor,\n })\n if (matches) {\n return matches\n }\n }\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/api",
3
- "version": "0.20.8",
3
+ "version": "0.20.9",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },
@@ -22,10 +22,10 @@
22
22
  "multiformats": "^13.0.0",
23
23
  "tlds": "^1.234.0",
24
24
  "zod": "^3.23.8",
25
- "@atproto/syntax": "^0.6.1",
26
- "@atproto/xrpc": "^0.8.0",
25
+ "@atproto/common-web": "^0.5.0",
27
26
  "@atproto/lexicon": "^0.7.1",
28
- "@atproto/common-web": "^0.5.0"
27
+ "@atproto/syntax": "^0.6.1",
28
+ "@atproto/xrpc": "^0.8.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@jest/globals": "^28.1.3",
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  AppBskyActorDefs,
3
3
  AppBskyEmbedExternal,
4
+ AppBskyEmbedGallery,
4
5
  AppBskyEmbedImages,
5
6
  AppBskyEmbedRecord,
6
7
  AppBskyEmbedRecordWithMedia,
@@ -200,6 +201,23 @@ function matchAllMuteWords(
200
201
  }
201
202
  }
202
203
  }
204
+
205
+ if (post.embed && AppBskyEmbedGallery.isMain(post.embed)) {
206
+ // post gallery items
207
+ for (const item of post.embed.items) {
208
+ if (AppBskyEmbedGallery.isImage(item)) {
209
+ const matches = matchMuteWords({
210
+ mutedWords,
211
+ text: item.alt,
212
+ languages: post.langs,
213
+ actor: postAuthor,
214
+ })
215
+ if (matches) {
216
+ return matches
217
+ }
218
+ }
219
+ }
220
+ }
203
221
  }
204
222
 
205
223
  const { embed } = subject
@@ -242,6 +260,23 @@ function matchAllMuteWords(
242
260
  }
243
261
  }
244
262
 
263
+ // quoted post's gallery
264
+ if (AppBskyEmbedGallery.isMain(embeddedPost.embed)) {
265
+ for (const item of embeddedPost.embed.items) {
266
+ if (AppBskyEmbedGallery.isImage(item)) {
267
+ const matches = matchMuteWords({
268
+ mutedWords,
269
+ text: item.alt,
270
+ languages: embeddedPost.langs,
271
+ actor: embedAuthor,
272
+ })
273
+ if (matches) {
274
+ return matches
275
+ }
276
+ }
277
+ }
278
+ }
279
+
245
280
  // quoted post's link card
246
281
  if (AppBskyEmbedExternal.isMain(embeddedPost.embed)) {
247
282
  const { external } = embeddedPost.embed
@@ -287,6 +322,25 @@ function matchAllMuteWords(
287
322
  }
288
323
  }
289
324
  }
325
+
326
+ // quoted post's gallery when it did a quote + media
327
+ if (AppBskyEmbedGallery.isMain(embeddedPost.embed.media)) {
328
+ for (const item of embeddedPost.embed.media.items) {
329
+ if (AppBskyEmbedGallery.isImage(item)) {
330
+ const matches = matchMuteWords({
331
+ mutedWords,
332
+ text: item.alt,
333
+ languages: AppBskyFeedPost.isRecord(embeddedPost.record)
334
+ ? embeddedPost.langs
335
+ : [],
336
+ actor: embedAuthor,
337
+ })
338
+ if (matches) {
339
+ return matches
340
+ }
341
+ }
342
+ }
343
+ }
290
344
  }
291
345
  }
292
346
  }
@@ -343,6 +397,25 @@ function matchAllMuteWords(
343
397
  }
344
398
  }
345
399
 
400
+ // quoted post gallery
401
+ if (AppBskyEmbedGallery.isView(embed.media)) {
402
+ for (const item of embed.media.items) {
403
+ if (AppBskyEmbedGallery.isViewImage(item)) {
404
+ const matches = matchMuteWords({
405
+ mutedWords,
406
+ text: item.alt,
407
+ languages: AppBskyFeedPost.isRecord(subject.record)
408
+ ? (subject.record as AppBskyFeedPost.Record).langs
409
+ : [],
410
+ actor: embedAuthor,
411
+ })
412
+ if (matches) {
413
+ return matches
414
+ }
415
+ }
416
+ }
417
+ }
418
+
346
419
  if (AppBskyEmbedExternal.isView(embed.media)) {
347
420
  const { external } = embed.media
348
421
  const matches = matchMuteWords({
@@ -1,5 +1,60 @@
1
- import { RichText, mock, moderatePost } from '../src/index.js'
1
+ import { $Typed } from '../src/client/util.js'
2
+ import {
3
+ AppBskyEmbedGallery,
4
+ AppBskyEmbedRecord,
5
+ AppBskyEmbedRecordWithMedia,
6
+ AppBskyFeedPost,
7
+ BlobRef,
8
+ RichText,
9
+ mock,
10
+ moderatePost,
11
+ } from '../src/index.js'
2
12
  import { matchMuteWords } from '../src/moderation/mutewords.js'
13
+ import { ModerationOpts } from '../src/moderation/types.js'
14
+
15
+ const FAKE_CID = 'bafyreiclp443lavogvhj3d2ob2cxbfuscni2k5jk7bebjzg7khl3esabwq'
16
+
17
+ const fakeBlob = (): BlobRef =>
18
+ new BlobRef({ '/': FAKE_CID } as any, 'image/jpeg', 1234)
19
+
20
+ const galleryRecord = (alts: string[]): $Typed<AppBskyEmbedGallery.Main> => ({
21
+ $type: 'app.bsky.embed.gallery',
22
+ items: alts.map(
23
+ (alt): $Typed<AppBskyEmbedGallery.Image> => ({
24
+ $type: 'app.bsky.embed.gallery#image',
25
+ image: fakeBlob(),
26
+ alt,
27
+ aspectRatio: { width: 4, height: 3 },
28
+ }),
29
+ ),
30
+ })
31
+
32
+ const galleryView = (alts: string[]): $Typed<AppBskyEmbedGallery.View> => ({
33
+ $type: 'app.bsky.embed.gallery#view',
34
+ items: alts.map(
35
+ (alt): $Typed<AppBskyEmbedGallery.ViewImage> => ({
36
+ $type: 'app.bsky.embed.gallery#viewImage',
37
+ thumbnail: 'https://example.test/thumb.jpg',
38
+ fullsize: 'https://example.test/full.jpg',
39
+ alt,
40
+ aspectRatio: { width: 4, height: 3 },
41
+ }),
42
+ ),
43
+ })
44
+
45
+ const galleryMuteOpts = (): ModerationOpts => ({
46
+ userDid: 'did:web:alice.test',
47
+ prefs: {
48
+ adultContentEnabled: false,
49
+ labels: {},
50
+ labelers: [],
51
+ mutedWords: [
52
+ { value: 'badword', targets: ['content'], actorTarget: 'all' },
53
+ ],
54
+ hiddenPosts: [],
55
+ },
56
+ labelDefs: {},
57
+ })
3
58
 
4
59
  describe(`matchMuteWords`, () => {
5
60
  describe(`tags`, () => {
@@ -1119,4 +1174,126 @@ describe(`matchMuteWords`, () => {
1119
1174
  ])
1120
1175
  })
1121
1176
  })
1177
+
1178
+ describe(`gallery embed alt text`, () => {
1179
+ const bob = mock.profileViewBasic({
1180
+ handle: 'bob.test',
1181
+ displayName: 'Bob',
1182
+ })
1183
+ const carol = mock.profileViewBasic({
1184
+ handle: 'carol.test',
1185
+ displayName: 'Carol',
1186
+ })
1187
+
1188
+ it(`matches alt text on a directly-attached gallery embed`, () => {
1189
+ const res = moderatePost(
1190
+ mock.postView({
1191
+ record: mock.post({
1192
+ text: 'innocent text',
1193
+ embed: galleryRecord(['fine', 'this contains badword here']),
1194
+ }),
1195
+ author: bob,
1196
+ labels: [],
1197
+ }),
1198
+ galleryMuteOpts(),
1199
+ )
1200
+ expect(res.causes.find((c) => c.type === 'mute-word')).toBeDefined()
1201
+ })
1202
+
1203
+ it(`does not match when no item alt contains a muted word`, () => {
1204
+ const res = moderatePost(
1205
+ mock.postView({
1206
+ record: mock.post({
1207
+ text: 'innocent text',
1208
+ embed: galleryRecord(['nothing here', 'still nothing']),
1209
+ }),
1210
+ author: bob,
1211
+ labels: [],
1212
+ }),
1213
+ galleryMuteOpts(),
1214
+ )
1215
+ expect(res.causes.find((c) => c.type === 'mute-word')).toBeUndefined()
1216
+ })
1217
+
1218
+ it(`matches alt text inside a quoted record's gallery`, () => {
1219
+ const quoted: AppBskyFeedPost.Record = mock.post({
1220
+ text: 'inside quoted',
1221
+ embed: galleryRecord(['contains badword inside']),
1222
+ })
1223
+ const res = moderatePost(
1224
+ mock.postView({
1225
+ record: mock.post({ text: 'outer post' }),
1226
+ embed: mock.embedRecordView({
1227
+ record: quoted,
1228
+ author: carol,
1229
+ labels: [],
1230
+ }),
1231
+ author: bob,
1232
+ labels: [],
1233
+ }),
1234
+ galleryMuteOpts(),
1235
+ )
1236
+ expect(res.causes.find((c) => c.type === 'mute-word')).toBeDefined()
1237
+ })
1238
+
1239
+ it(`matches alt text on a quoted record's recordWithMedia gallery`, () => {
1240
+ const quotedRecord: AppBskyFeedPost.Record = mock.post({
1241
+ text: 'inside quoted record-with-media',
1242
+ embed: {
1243
+ $type: 'app.bsky.embed.recordWithMedia',
1244
+ record: {
1245
+ $type: 'app.bsky.embed.record',
1246
+ record: {
1247
+ uri: 'at://did:web:dave.test/app.bsky.feed.post/inner',
1248
+ cid: FAKE_CID,
1249
+ },
1250
+ } satisfies $Typed<AppBskyEmbedRecord.Main>,
1251
+ media: galleryRecord(['contains badword in nested gallery']),
1252
+ } satisfies $Typed<AppBskyEmbedRecordWithMedia.Main>,
1253
+ })
1254
+ const res = moderatePost(
1255
+ mock.postView({
1256
+ record: mock.post({ text: 'outer post' }),
1257
+ embed: mock.embedRecordView({
1258
+ record: quotedRecord,
1259
+ author: carol,
1260
+ labels: [],
1261
+ }),
1262
+ author: bob,
1263
+ labels: [],
1264
+ }),
1265
+ galleryMuteOpts(),
1266
+ )
1267
+ expect(res.causes.find((c) => c.type === 'mute-word')).toBeDefined()
1268
+ })
1269
+
1270
+ it(`matches alt text on the view-side recordWithMedia gallery`, () => {
1271
+ const recordWithMediaView: $Typed<AppBskyEmbedRecordWithMedia.View> = {
1272
+ $type: 'app.bsky.embed.recordWithMedia#view',
1273
+ record: {
1274
+ $type: 'app.bsky.embed.record#view',
1275
+ record: {
1276
+ $type: 'app.bsky.embed.record#viewRecord',
1277
+ uri: 'at://did:web:dave.test/app.bsky.feed.post/inner',
1278
+ cid: FAKE_CID,
1279
+ author: carol,
1280
+ value: mock.post({ text: 'inner content' }),
1281
+ labels: [],
1282
+ indexedAt: new Date().toISOString(),
1283
+ },
1284
+ },
1285
+ media: galleryView(['contains badword on view side']),
1286
+ }
1287
+ const res = moderatePost(
1288
+ mock.postView({
1289
+ record: mock.post({ text: 'outer post' }),
1290
+ embed: recordWithMediaView,
1291
+ author: bob,
1292
+ labels: [],
1293
+ }),
1294
+ galleryMuteOpts(),
1295
+ )
1296
+ expect(res.causes.find((c) => c.type === 'mute-word')).toBeDefined()
1297
+ })
1298
+ })
1122
1299
  })