@atproto/api 0.12.5 → 0.12.7
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.
- package/CHANGELOG.md +14 -0
- package/dist/bsky-agent.d.ts +19 -0
- package/dist/bsky-agent.d.ts.map +1 -1
- package/dist/bsky-agent.js +179 -0
- package/dist/bsky-agent.js.map +1 -1
- package/dist/client/lexicons.d.ts +68 -0
- package/dist/client/lexicons.d.ts.map +1 -1
- package/dist/client/lexicons.js +78 -0
- package/dist/client/lexicons.js.map +1 -1
- package/dist/client/types/app/bsky/actor/defs.d.ts +16 -1
- package/dist/client/types/app/bsky/actor/defs.d.ts.map +1 -1
- package/dist/client/types/app/bsky/actor/defs.js +21 -1
- package/dist/client/types/app/bsky/actor/defs.js.map +1 -1
- package/dist/client/types/tools/ozone/moderation/defs.d.ts +22 -2
- package/dist/client/types/tools/ozone/moderation/defs.d.ts.map +1 -1
- package/dist/client/types/tools/ozone/moderation/defs.js +22 -2
- package/dist/client/types/tools/ozone/moderation/defs.js.map +1 -1
- package/dist/client/types/tools/ozone/moderation/emitEvent.d.ts +1 -1
- package/dist/client/types/tools/ozone/moderation/emitEvent.d.ts.map +1 -1
- package/dist/client/types/tools/ozone/moderation/emitEvent.js.map +1 -1
- package/dist/client/types/tools/ozone/moderation/queryStatuses.d.ts +2 -0
- package/dist/client/types/tools/ozone/moderation/queryStatuses.d.ts.map +1 -1
- package/dist/client/types/tools/ozone/moderation/queryStatuses.js.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util.d.ts +13 -0
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +55 -1
- package/dist/util.js.map +1 -1
- package/package.json +1 -1
- package/src/bsky-agent.ts +222 -1
- package/src/client/lexicons.ts +80 -0
- package/src/client/types/app/bsky/actor/defs.ts +38 -0
- package/src/client/types/tools/ozone/moderation/defs.ts +56 -0
- package/src/client/types/tools/ozone/moderation/emitEvent.ts +3 -0
- package/src/client/types/tools/ozone/moderation/queryStatuses.ts +2 -0
- package/src/types.ts +4 -0
- package/src/util.ts +72 -0
- package/tests/bsky-agent.test.ts +1021 -2
- package/tests/moderation-prefs.test.ts +4 -0
package/src/bsky-agent.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AtUri, ensureValidDid } from '@atproto/syntax'
|
|
2
|
+
import { TID } from '@atproto/common-web'
|
|
2
3
|
import { AtpAgent } from './agent'
|
|
3
4
|
import {
|
|
4
5
|
AppBskyFeedPost,
|
|
@@ -19,7 +20,12 @@ import {
|
|
|
19
20
|
ModerationPrefs,
|
|
20
21
|
} from './moderation/types'
|
|
21
22
|
import { DEFAULT_LABEL_SETTINGS } from './moderation/const/labels'
|
|
22
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
sanitizeMutedWordValue,
|
|
25
|
+
validateSavedFeed,
|
|
26
|
+
savedFeedsToUriArrays,
|
|
27
|
+
getSavedFeedType,
|
|
28
|
+
} from './util'
|
|
23
29
|
import { interpretLabelValueDefinitions } from './moderation'
|
|
24
30
|
|
|
25
31
|
const FEED_VIEW_PREF_DEFAULTS = {
|
|
@@ -365,6 +371,8 @@ export class BskyAgent extends AtpAgent {
|
|
|
365
371
|
saved: undefined,
|
|
366
372
|
pinned: undefined,
|
|
367
373
|
},
|
|
374
|
+
// @ts-ignore populating below
|
|
375
|
+
savedFeeds: undefined,
|
|
368
376
|
feedViewPrefs: {
|
|
369
377
|
home: {
|
|
370
378
|
...FEED_VIEW_PREF_DEFAULTS,
|
|
@@ -412,6 +420,11 @@ export class BskyAgent extends AtpAgent {
|
|
|
412
420
|
labels: {},
|
|
413
421
|
})),
|
|
414
422
|
)
|
|
423
|
+
} else if (
|
|
424
|
+
AppBskyActorDefs.isSavedFeedsPrefV2(pref) &&
|
|
425
|
+
AppBskyActorDefs.validateSavedFeedsPrefV2(pref).success
|
|
426
|
+
) {
|
|
427
|
+
prefs.savedFeeds = pref.items
|
|
415
428
|
} else if (
|
|
416
429
|
AppBskyActorDefs.isSavedFeedsPref(pref) &&
|
|
417
430
|
AppBskyActorDefs.validateSavedFeedsPref(pref).success
|
|
@@ -467,6 +480,75 @@ export class BskyAgent extends AtpAgent {
|
|
|
467
480
|
}
|
|
468
481
|
}
|
|
469
482
|
|
|
483
|
+
/*
|
|
484
|
+
* If `prefs.savedFeeds` is undefined, no `savedFeedsPrefV2` exists, which
|
|
485
|
+
* means we want to try to migrate if needed.
|
|
486
|
+
*
|
|
487
|
+
* If v1 prefs exist, they will be migrated to v2.
|
|
488
|
+
*
|
|
489
|
+
* If no v1 prefs exist, the user is either new, or could be old and has
|
|
490
|
+
* never edited their feeds.
|
|
491
|
+
*/
|
|
492
|
+
if (prefs.savedFeeds === undefined) {
|
|
493
|
+
const { saved, pinned } = prefs.feeds
|
|
494
|
+
|
|
495
|
+
if (saved && pinned) {
|
|
496
|
+
const uniqueMigratedSavedFeeds: Map<
|
|
497
|
+
string,
|
|
498
|
+
AppBskyActorDefs.SavedFeed
|
|
499
|
+
> = new Map()
|
|
500
|
+
|
|
501
|
+
// insert Following feed first
|
|
502
|
+
uniqueMigratedSavedFeeds.set('timeline', {
|
|
503
|
+
id: TID.nextStr(),
|
|
504
|
+
type: 'timeline',
|
|
505
|
+
value: 'following',
|
|
506
|
+
pinned: true,
|
|
507
|
+
})
|
|
508
|
+
|
|
509
|
+
// use pinned as source of truth for feed order
|
|
510
|
+
for (const uri of pinned) {
|
|
511
|
+
const type = getSavedFeedType(uri)
|
|
512
|
+
// only want supported types
|
|
513
|
+
if (type === 'unknown') continue
|
|
514
|
+
uniqueMigratedSavedFeeds.set(uri, {
|
|
515
|
+
id: TID.nextStr(),
|
|
516
|
+
type,
|
|
517
|
+
value: uri,
|
|
518
|
+
pinned: true,
|
|
519
|
+
})
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
for (const uri of saved) {
|
|
523
|
+
if (!uniqueMigratedSavedFeeds.has(uri)) {
|
|
524
|
+
const type = getSavedFeedType(uri)
|
|
525
|
+
// only want supported types
|
|
526
|
+
if (type === 'unknown') continue
|
|
527
|
+
uniqueMigratedSavedFeeds.set(uri, {
|
|
528
|
+
id: TID.nextStr(),
|
|
529
|
+
type,
|
|
530
|
+
value: uri,
|
|
531
|
+
pinned: false,
|
|
532
|
+
})
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
prefs.savedFeeds = Array.from(uniqueMigratedSavedFeeds.values())
|
|
537
|
+
} else {
|
|
538
|
+
prefs.savedFeeds = [
|
|
539
|
+
{
|
|
540
|
+
id: TID.nextStr(),
|
|
541
|
+
type: 'timeline',
|
|
542
|
+
value: 'following',
|
|
543
|
+
pinned: true,
|
|
544
|
+
},
|
|
545
|
+
]
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// save to user preferences so this migration doesn't re-occur
|
|
549
|
+
await this.overwriteSavedFeeds(prefs.savedFeeds)
|
|
550
|
+
}
|
|
551
|
+
|
|
470
552
|
// apply the label prefs
|
|
471
553
|
for (const pref of labelPrefs) {
|
|
472
554
|
if (pref.labelerDid) {
|
|
@@ -491,6 +573,63 @@ export class BskyAgent extends AtpAgent {
|
|
|
491
573
|
return prefs
|
|
492
574
|
}
|
|
493
575
|
|
|
576
|
+
async overwriteSavedFeeds(savedFeeds: AppBskyActorDefs.SavedFeed[]) {
|
|
577
|
+
savedFeeds.forEach(validateSavedFeed)
|
|
578
|
+
const uniqueSavedFeeds = new Map<string, AppBskyActorDefs.SavedFeed>()
|
|
579
|
+
savedFeeds.forEach((feed) => {
|
|
580
|
+
// remove and re-insert to preserve order
|
|
581
|
+
if (uniqueSavedFeeds.has(feed.id)) {
|
|
582
|
+
uniqueSavedFeeds.delete(feed.id)
|
|
583
|
+
}
|
|
584
|
+
uniqueSavedFeeds.set(feed.id, feed)
|
|
585
|
+
})
|
|
586
|
+
return updateSavedFeedsV2Preferences(this, () =>
|
|
587
|
+
Array.from(uniqueSavedFeeds.values()),
|
|
588
|
+
)
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
async updateSavedFeeds(savedFeedsToUpdate: AppBskyActorDefs.SavedFeed[]) {
|
|
592
|
+
savedFeedsToUpdate.map(validateSavedFeed)
|
|
593
|
+
return updateSavedFeedsV2Preferences(this, (savedFeeds) => {
|
|
594
|
+
return savedFeeds.map((savedFeed) => {
|
|
595
|
+
const updatedVersion = savedFeedsToUpdate.find(
|
|
596
|
+
(updated) => savedFeed.id === updated.id,
|
|
597
|
+
)
|
|
598
|
+
if (updatedVersion) {
|
|
599
|
+
return {
|
|
600
|
+
...savedFeed,
|
|
601
|
+
// only update pinned
|
|
602
|
+
pinned: updatedVersion.pinned,
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return savedFeed
|
|
606
|
+
})
|
|
607
|
+
})
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
async addSavedFeeds(
|
|
611
|
+
savedFeeds: Pick<AppBskyActorDefs.SavedFeed, 'type' | 'value' | 'pinned'>[],
|
|
612
|
+
) {
|
|
613
|
+
const toSave: AppBskyActorDefs.SavedFeed[] = savedFeeds.map((f) => ({
|
|
614
|
+
...f,
|
|
615
|
+
id: TID.nextStr(),
|
|
616
|
+
}))
|
|
617
|
+
toSave.forEach(validateSavedFeed)
|
|
618
|
+
return updateSavedFeedsV2Preferences(this, (savedFeeds) => [
|
|
619
|
+
...savedFeeds,
|
|
620
|
+
...toSave,
|
|
621
|
+
])
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
async removeSavedFeeds(ids: string[]) {
|
|
625
|
+
return updateSavedFeedsV2Preferences(this, (savedFeeds) => [
|
|
626
|
+
...savedFeeds.filter((feed) => !ids.find((id) => feed.id === id)),
|
|
627
|
+
])
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* @deprecated use `overwriteSavedFeeds`
|
|
632
|
+
*/
|
|
494
633
|
async setSavedFeeds(saved: string[], pinned: string[]) {
|
|
495
634
|
return updateFeedPreferences(this, () => ({
|
|
496
635
|
saved,
|
|
@@ -498,6 +637,9 @@ export class BskyAgent extends AtpAgent {
|
|
|
498
637
|
}))
|
|
499
638
|
}
|
|
500
639
|
|
|
640
|
+
/**
|
|
641
|
+
* @deprecated use `addSavedFeeds`
|
|
642
|
+
*/
|
|
501
643
|
async addSavedFeed(v: string) {
|
|
502
644
|
return updateFeedPreferences(this, (saved: string[], pinned: string[]) => ({
|
|
503
645
|
saved: [...saved.filter((uri) => uri !== v), v],
|
|
@@ -505,6 +647,9 @@ export class BskyAgent extends AtpAgent {
|
|
|
505
647
|
}))
|
|
506
648
|
}
|
|
507
649
|
|
|
650
|
+
/**
|
|
651
|
+
* @deprecated use `removeSavedFeeds`
|
|
652
|
+
*/
|
|
508
653
|
async removeSavedFeed(v: string) {
|
|
509
654
|
return updateFeedPreferences(this, (saved: string[], pinned: string[]) => ({
|
|
510
655
|
saved: saved.filter((uri) => uri !== v),
|
|
@@ -512,6 +657,9 @@ export class BskyAgent extends AtpAgent {
|
|
|
512
657
|
}))
|
|
513
658
|
}
|
|
514
659
|
|
|
660
|
+
/**
|
|
661
|
+
* @deprecated use `addSavedFeeds` or `updateSavedFeeds`
|
|
662
|
+
*/
|
|
515
663
|
async addPinnedFeed(v: string) {
|
|
516
664
|
return updateFeedPreferences(this, (saved: string[], pinned: string[]) => ({
|
|
517
665
|
saved: [...saved.filter((uri) => uri !== v), v],
|
|
@@ -519,6 +667,9 @@ export class BskyAgent extends AtpAgent {
|
|
|
519
667
|
}))
|
|
520
668
|
}
|
|
521
669
|
|
|
670
|
+
/**
|
|
671
|
+
* @deprecated use `updateSavedFeeds` or `removeSavedFeeds`
|
|
672
|
+
*/
|
|
522
673
|
async removePinnedFeed(v: string) {
|
|
523
674
|
return updateFeedPreferences(this, (saved: string[], pinned: string[]) => ({
|
|
524
675
|
saved,
|
|
@@ -945,6 +1096,76 @@ async function updateFeedPreferences(
|
|
|
945
1096
|
return res
|
|
946
1097
|
}
|
|
947
1098
|
|
|
1099
|
+
async function updateSavedFeedsV2Preferences(
|
|
1100
|
+
agent: BskyAgent,
|
|
1101
|
+
cb: (
|
|
1102
|
+
savedFeedsPref: AppBskyActorDefs.SavedFeed[],
|
|
1103
|
+
) => AppBskyActorDefs.SavedFeed[],
|
|
1104
|
+
): Promise<AppBskyActorDefs.SavedFeed[]> {
|
|
1105
|
+
let maybeMutatedSavedFeeds: AppBskyActorDefs.SavedFeed[] = []
|
|
1106
|
+
|
|
1107
|
+
await updatePreferences(agent, (prefs: AppBskyActorDefs.Preferences) => {
|
|
1108
|
+
let existingV2Pref = prefs.findLast(
|
|
1109
|
+
(pref) =>
|
|
1110
|
+
AppBskyActorDefs.isSavedFeedsPrefV2(pref) &&
|
|
1111
|
+
AppBskyActorDefs.validateSavedFeedsPrefV2(pref).success,
|
|
1112
|
+
) as AppBskyActorDefs.SavedFeedsPrefV2 | undefined
|
|
1113
|
+
let existingV1Pref = prefs.findLast(
|
|
1114
|
+
(pref) =>
|
|
1115
|
+
AppBskyActorDefs.isSavedFeedsPref(pref) &&
|
|
1116
|
+
AppBskyActorDefs.validateSavedFeedsPref(pref).success,
|
|
1117
|
+
) as AppBskyActorDefs.SavedFeedsPref | undefined
|
|
1118
|
+
|
|
1119
|
+
if (existingV2Pref) {
|
|
1120
|
+
maybeMutatedSavedFeeds = cb(existingV2Pref.items)
|
|
1121
|
+
existingV2Pref = {
|
|
1122
|
+
...existingV2Pref,
|
|
1123
|
+
items: maybeMutatedSavedFeeds,
|
|
1124
|
+
}
|
|
1125
|
+
} else {
|
|
1126
|
+
maybeMutatedSavedFeeds = cb([])
|
|
1127
|
+
existingV2Pref = {
|
|
1128
|
+
$type: 'app.bsky.actor.defs#savedFeedsPrefV2',
|
|
1129
|
+
items: maybeMutatedSavedFeeds,
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// enforce ordering, pinned then saved
|
|
1134
|
+
const pinned = existingV2Pref.items.filter((i) => i.pinned)
|
|
1135
|
+
const saved = existingV2Pref.items.filter((i) => !i.pinned)
|
|
1136
|
+
existingV2Pref.items = pinned.concat(saved)
|
|
1137
|
+
|
|
1138
|
+
let updatedPrefs = prefs
|
|
1139
|
+
.filter((pref) => !AppBskyActorDefs.isSavedFeedsPrefV2(pref))
|
|
1140
|
+
.concat(existingV2Pref)
|
|
1141
|
+
|
|
1142
|
+
/*
|
|
1143
|
+
* If there's a v2 pref present, it means this account was migrated from v1
|
|
1144
|
+
* to v2. During the transition period, we double write v2 prefs back to
|
|
1145
|
+
* v1, but NOT the other way around.
|
|
1146
|
+
*/
|
|
1147
|
+
if (existingV1Pref) {
|
|
1148
|
+
const { saved, pinned } = existingV1Pref
|
|
1149
|
+
const v2Compat = savedFeedsToUriArrays(
|
|
1150
|
+
// v1 only supports feeds and lists
|
|
1151
|
+
existingV2Pref.items.filter((i) => ['feed', 'list'].includes(i.type)),
|
|
1152
|
+
)
|
|
1153
|
+
existingV1Pref = {
|
|
1154
|
+
...existingV1Pref,
|
|
1155
|
+
saved: Array.from(new Set([...saved, ...v2Compat.saved])),
|
|
1156
|
+
pinned: Array.from(new Set([...pinned, ...v2Compat.pinned])),
|
|
1157
|
+
}
|
|
1158
|
+
updatedPrefs = updatedPrefs
|
|
1159
|
+
.filter((pref) => !AppBskyActorDefs.isSavedFeedsPref(pref))
|
|
1160
|
+
.concat(existingV1Pref)
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
return updatedPrefs
|
|
1164
|
+
})
|
|
1165
|
+
|
|
1166
|
+
return maybeMutatedSavedFeeds
|
|
1167
|
+
}
|
|
1168
|
+
|
|
948
1169
|
/**
|
|
949
1170
|
* Helper to transform the legacy content preferences.
|
|
950
1171
|
*/
|
package/src/client/lexicons.ts
CHANGED
|
@@ -3822,6 +3822,7 @@ export const schemaDict = {
|
|
|
3822
3822
|
'lex:app.bsky.actor.defs#adultContentPref',
|
|
3823
3823
|
'lex:app.bsky.actor.defs#contentLabelPref',
|
|
3824
3824
|
'lex:app.bsky.actor.defs#savedFeedsPref',
|
|
3825
|
+
'lex:app.bsky.actor.defs#savedFeedsPrefV2',
|
|
3825
3826
|
'lex:app.bsky.actor.defs#personalDetailsPref',
|
|
3826
3827
|
'lex:app.bsky.actor.defs#feedViewPref',
|
|
3827
3828
|
'lex:app.bsky.actor.defs#threadViewPref',
|
|
@@ -3860,6 +3861,38 @@ export const schemaDict = {
|
|
|
3860
3861
|
},
|
|
3861
3862
|
},
|
|
3862
3863
|
},
|
|
3864
|
+
savedFeed: {
|
|
3865
|
+
type: 'object',
|
|
3866
|
+
required: ['id', 'type', 'value', 'pinned'],
|
|
3867
|
+
properties: {
|
|
3868
|
+
id: {
|
|
3869
|
+
type: 'string',
|
|
3870
|
+
},
|
|
3871
|
+
type: {
|
|
3872
|
+
type: 'string',
|
|
3873
|
+
knownValues: ['feed', 'list', 'timeline'],
|
|
3874
|
+
},
|
|
3875
|
+
value: {
|
|
3876
|
+
type: 'string',
|
|
3877
|
+
},
|
|
3878
|
+
pinned: {
|
|
3879
|
+
type: 'boolean',
|
|
3880
|
+
},
|
|
3881
|
+
},
|
|
3882
|
+
},
|
|
3883
|
+
savedFeedsPrefV2: {
|
|
3884
|
+
type: 'object',
|
|
3885
|
+
required: ['items'],
|
|
3886
|
+
properties: {
|
|
3887
|
+
items: {
|
|
3888
|
+
type: 'array',
|
|
3889
|
+
items: {
|
|
3890
|
+
type: 'ref',
|
|
3891
|
+
ref: 'lex:app.bsky.actor.defs#savedFeed',
|
|
3892
|
+
},
|
|
3893
|
+
},
|
|
3894
|
+
},
|
|
3895
|
+
},
|
|
3863
3896
|
savedFeedsPref: {
|
|
3864
3897
|
type: 'object',
|
|
3865
3898
|
required: ['pinned', 'saved'],
|
|
@@ -8375,6 +8408,9 @@ export const schemaDict = {
|
|
|
8375
8408
|
'lex:tools.ozone.moderation.defs#modEventAcknowledge',
|
|
8376
8409
|
'lex:tools.ozone.moderation.defs#modEventEscalate',
|
|
8377
8410
|
'lex:tools.ozone.moderation.defs#modEventMute',
|
|
8411
|
+
'lex:tools.ozone.moderation.defs#modEventUnmute',
|
|
8412
|
+
'lex:tools.ozone.moderation.defs#modEventMuteReporter',
|
|
8413
|
+
'lex:tools.ozone.moderation.defs#modEventUnmuteReporter',
|
|
8378
8414
|
'lex:tools.ozone.moderation.defs#modEventEmail',
|
|
8379
8415
|
'lex:tools.ozone.moderation.defs#modEventResolveAppeal',
|
|
8380
8416
|
'lex:tools.ozone.moderation.defs#modEventDivert',
|
|
@@ -8434,6 +8470,9 @@ export const schemaDict = {
|
|
|
8434
8470
|
'lex:tools.ozone.moderation.defs#modEventAcknowledge',
|
|
8435
8471
|
'lex:tools.ozone.moderation.defs#modEventEscalate',
|
|
8436
8472
|
'lex:tools.ozone.moderation.defs#modEventMute',
|
|
8473
|
+
'lex:tools.ozone.moderation.defs#modEventUnmute',
|
|
8474
|
+
'lex:tools.ozone.moderation.defs#modEventMuteReporter',
|
|
8475
|
+
'lex:tools.ozone.moderation.defs#modEventUnmuteReporter',
|
|
8437
8476
|
'lex:tools.ozone.moderation.defs#modEventEmail',
|
|
8438
8477
|
'lex:tools.ozone.moderation.defs#modEventResolveAppeal',
|
|
8439
8478
|
'lex:tools.ozone.moderation.defs#modEventDivert',
|
|
@@ -8513,6 +8552,10 @@ export const schemaDict = {
|
|
|
8513
8552
|
type: 'string',
|
|
8514
8553
|
format: 'datetime',
|
|
8515
8554
|
},
|
|
8555
|
+
muteReportingUntil: {
|
|
8556
|
+
type: 'string',
|
|
8557
|
+
format: 'datetime',
|
|
8558
|
+
},
|
|
8516
8559
|
lastReviewedBy: {
|
|
8517
8560
|
type: 'string',
|
|
8518
8561
|
format: 'did',
|
|
@@ -8636,6 +8679,11 @@ export const schemaDict = {
|
|
|
8636
8679
|
comment: {
|
|
8637
8680
|
type: 'string',
|
|
8638
8681
|
},
|
|
8682
|
+
isReporterMuted: {
|
|
8683
|
+
type: 'boolean',
|
|
8684
|
+
description:
|
|
8685
|
+
"Set to true if the reporter was muted from reporting at the time of the event. These reports won't impact the reviewState of the subject.",
|
|
8686
|
+
},
|
|
8639
8687
|
reportType: {
|
|
8640
8688
|
type: 'ref',
|
|
8641
8689
|
ref: 'lex:com.atproto.moderation.defs#reasonType',
|
|
@@ -8704,6 +8752,30 @@ export const schemaDict = {
|
|
|
8704
8752
|
},
|
|
8705
8753
|
},
|
|
8706
8754
|
},
|
|
8755
|
+
modEventMuteReporter: {
|
|
8756
|
+
type: 'object',
|
|
8757
|
+
description: 'Mute incoming reports from an account',
|
|
8758
|
+
required: ['durationInHours'],
|
|
8759
|
+
properties: {
|
|
8760
|
+
comment: {
|
|
8761
|
+
type: 'string',
|
|
8762
|
+
},
|
|
8763
|
+
durationInHours: {
|
|
8764
|
+
type: 'integer',
|
|
8765
|
+
description: 'Indicates how long the account should remain muted.',
|
|
8766
|
+
},
|
|
8767
|
+
},
|
|
8768
|
+
},
|
|
8769
|
+
modEventUnmuteReporter: {
|
|
8770
|
+
type: 'object',
|
|
8771
|
+
description: 'Unmute incoming reports from an account',
|
|
8772
|
+
properties: {
|
|
8773
|
+
comment: {
|
|
8774
|
+
type: 'string',
|
|
8775
|
+
description: 'Describe reasoning behind the reversal.',
|
|
8776
|
+
},
|
|
8777
|
+
},
|
|
8778
|
+
},
|
|
8707
8779
|
modEventEmail: {
|
|
8708
8780
|
type: 'object',
|
|
8709
8781
|
description: 'Keep a log of outgoing email to a user',
|
|
@@ -9088,6 +9160,9 @@ export const schemaDict = {
|
|
|
9088
9160
|
'lex:tools.ozone.moderation.defs#modEventLabel',
|
|
9089
9161
|
'lex:tools.ozone.moderation.defs#modEventReport',
|
|
9090
9162
|
'lex:tools.ozone.moderation.defs#modEventMute',
|
|
9163
|
+
'lex:tools.ozone.moderation.defs#modEventUnmute',
|
|
9164
|
+
'lex:tools.ozone.moderation.defs#modEventMuteReporter',
|
|
9165
|
+
'lex:tools.ozone.moderation.defs#modEventUnmuteReporter',
|
|
9091
9166
|
'lex:tools.ozone.moderation.defs#modEventReverseTakedown',
|
|
9092
9167
|
'lex:tools.ozone.moderation.defs#modEventUnmute',
|
|
9093
9168
|
'lex:tools.ozone.moderation.defs#modEventEmail',
|
|
@@ -9396,6 +9471,11 @@ export const schemaDict = {
|
|
|
9396
9471
|
description:
|
|
9397
9472
|
"By default, we don't include muted subjects in the results. Set this to true to include them.",
|
|
9398
9473
|
},
|
|
9474
|
+
onlyMuted: {
|
|
9475
|
+
type: 'boolean',
|
|
9476
|
+
description:
|
|
9477
|
+
'When set to true, only muted subjects and reporters will be returned.',
|
|
9478
|
+
},
|
|
9399
9479
|
reviewState: {
|
|
9400
9480
|
type: 'string',
|
|
9401
9481
|
description: 'Specify when fetching subjects in a certain state',
|
|
@@ -132,6 +132,7 @@ export type Preferences = (
|
|
|
132
132
|
| AdultContentPref
|
|
133
133
|
| ContentLabelPref
|
|
134
134
|
| SavedFeedsPref
|
|
135
|
+
| SavedFeedsPrefV2
|
|
135
136
|
| PersonalDetailsPref
|
|
136
137
|
| FeedViewPref
|
|
137
138
|
| ThreadViewPref
|
|
@@ -178,6 +179,43 @@ export function validateContentLabelPref(v: unknown): ValidationResult {
|
|
|
178
179
|
return lexicons.validate('app.bsky.actor.defs#contentLabelPref', v)
|
|
179
180
|
}
|
|
180
181
|
|
|
182
|
+
export interface SavedFeed {
|
|
183
|
+
id: string
|
|
184
|
+
type: 'feed' | 'list' | 'timeline' | (string & {})
|
|
185
|
+
value: string
|
|
186
|
+
pinned: boolean
|
|
187
|
+
[k: string]: unknown
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function isSavedFeed(v: unknown): v is SavedFeed {
|
|
191
|
+
return (
|
|
192
|
+
isObj(v) &&
|
|
193
|
+
hasProp(v, '$type') &&
|
|
194
|
+
v.$type === 'app.bsky.actor.defs#savedFeed'
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function validateSavedFeed(v: unknown): ValidationResult {
|
|
199
|
+
return lexicons.validate('app.bsky.actor.defs#savedFeed', v)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface SavedFeedsPrefV2 {
|
|
203
|
+
items: SavedFeed[]
|
|
204
|
+
[k: string]: unknown
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function isSavedFeedsPrefV2(v: unknown): v is SavedFeedsPrefV2 {
|
|
208
|
+
return (
|
|
209
|
+
isObj(v) &&
|
|
210
|
+
hasProp(v, '$type') &&
|
|
211
|
+
v.$type === 'app.bsky.actor.defs#savedFeedsPrefV2'
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function validateSavedFeedsPrefV2(v: unknown): ValidationResult {
|
|
216
|
+
return lexicons.validate('app.bsky.actor.defs#savedFeedsPrefV2', v)
|
|
217
|
+
}
|
|
218
|
+
|
|
181
219
|
export interface SavedFeedsPref {
|
|
182
220
|
pinned: string[]
|
|
183
221
|
saved: string[]
|
|
@@ -22,6 +22,9 @@ export interface ModEventView {
|
|
|
22
22
|
| ModEventAcknowledge
|
|
23
23
|
| ModEventEscalate
|
|
24
24
|
| ModEventMute
|
|
25
|
+
| ModEventUnmute
|
|
26
|
+
| ModEventMuteReporter
|
|
27
|
+
| ModEventUnmuteReporter
|
|
25
28
|
| ModEventEmail
|
|
26
29
|
| ModEventResolveAppeal
|
|
27
30
|
| ModEventDivert
|
|
@@ -61,6 +64,9 @@ export interface ModEventViewDetail {
|
|
|
61
64
|
| ModEventAcknowledge
|
|
62
65
|
| ModEventEscalate
|
|
63
66
|
| ModEventMute
|
|
67
|
+
| ModEventUnmute
|
|
68
|
+
| ModEventMuteReporter
|
|
69
|
+
| ModEventUnmuteReporter
|
|
64
70
|
| ModEventEmail
|
|
65
71
|
| ModEventResolveAppeal
|
|
66
72
|
| ModEventDivert
|
|
@@ -105,6 +111,7 @@ export interface SubjectStatusView {
|
|
|
105
111
|
/** Sticky comment on the subject. */
|
|
106
112
|
comment?: string
|
|
107
113
|
muteUntil?: string
|
|
114
|
+
muteReportingUntil?: string
|
|
108
115
|
lastReviewedBy?: string
|
|
109
116
|
lastReviewedAt?: string
|
|
110
117
|
lastReportedAt?: string
|
|
@@ -237,6 +244,8 @@ export function validateModEventComment(v: unknown): ValidationResult {
|
|
|
237
244
|
/** Report a subject */
|
|
238
245
|
export interface ModEventReport {
|
|
239
246
|
comment?: string
|
|
247
|
+
/** Set to true if the reporter was muted from reporting at the time of the event. These reports won't impact the reviewState of the subject. */
|
|
248
|
+
isReporterMuted?: boolean
|
|
240
249
|
reportType: ComAtprotoModerationDefs.ReasonType
|
|
241
250
|
[k: string]: unknown
|
|
242
251
|
}
|
|
@@ -346,6 +355,53 @@ export function validateModEventUnmute(v: unknown): ValidationResult {
|
|
|
346
355
|
return lexicons.validate('tools.ozone.moderation.defs#modEventUnmute', v)
|
|
347
356
|
}
|
|
348
357
|
|
|
358
|
+
/** Mute incoming reports from an account */
|
|
359
|
+
export interface ModEventMuteReporter {
|
|
360
|
+
comment?: string
|
|
361
|
+
/** Indicates how long the account should remain muted. */
|
|
362
|
+
durationInHours: number
|
|
363
|
+
[k: string]: unknown
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function isModEventMuteReporter(v: unknown): v is ModEventMuteReporter {
|
|
367
|
+
return (
|
|
368
|
+
isObj(v) &&
|
|
369
|
+
hasProp(v, '$type') &&
|
|
370
|
+
v.$type === 'tools.ozone.moderation.defs#modEventMuteReporter'
|
|
371
|
+
)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export function validateModEventMuteReporter(v: unknown): ValidationResult {
|
|
375
|
+
return lexicons.validate(
|
|
376
|
+
'tools.ozone.moderation.defs#modEventMuteReporter',
|
|
377
|
+
v,
|
|
378
|
+
)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/** Unmute incoming reports from an account */
|
|
382
|
+
export interface ModEventUnmuteReporter {
|
|
383
|
+
/** Describe reasoning behind the reversal. */
|
|
384
|
+
comment?: string
|
|
385
|
+
[k: string]: unknown
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export function isModEventUnmuteReporter(
|
|
389
|
+
v: unknown,
|
|
390
|
+
): v is ModEventUnmuteReporter {
|
|
391
|
+
return (
|
|
392
|
+
isObj(v) &&
|
|
393
|
+
hasProp(v, '$type') &&
|
|
394
|
+
v.$type === 'tools.ozone.moderation.defs#modEventUnmuteReporter'
|
|
395
|
+
)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export function validateModEventUnmuteReporter(v: unknown): ValidationResult {
|
|
399
|
+
return lexicons.validate(
|
|
400
|
+
'tools.ozone.moderation.defs#modEventUnmuteReporter',
|
|
401
|
+
v,
|
|
402
|
+
)
|
|
403
|
+
}
|
|
404
|
+
|
|
349
405
|
/** Keep a log of outgoing email to a user */
|
|
350
406
|
export interface ModEventEmail {
|
|
351
407
|
/** The subject line of the email sent to the user. */
|
|
@@ -21,6 +21,9 @@ export interface InputSchema {
|
|
|
21
21
|
| ToolsOzoneModerationDefs.ModEventLabel
|
|
22
22
|
| ToolsOzoneModerationDefs.ModEventReport
|
|
23
23
|
| ToolsOzoneModerationDefs.ModEventMute
|
|
24
|
+
| ToolsOzoneModerationDefs.ModEventUnmute
|
|
25
|
+
| ToolsOzoneModerationDefs.ModEventMuteReporter
|
|
26
|
+
| ToolsOzoneModerationDefs.ModEventUnmuteReporter
|
|
24
27
|
| ToolsOzoneModerationDefs.ModEventReverseTakedown
|
|
25
28
|
| ToolsOzoneModerationDefs.ModEventUnmute
|
|
26
29
|
| ToolsOzoneModerationDefs.ModEventEmail
|
|
@@ -22,6 +22,8 @@ export interface QueryParams {
|
|
|
22
22
|
reviewedBefore?: string
|
|
23
23
|
/** By default, we don't include muted subjects in the results. Set this to true to include them. */
|
|
24
24
|
includeMuted?: boolean
|
|
25
|
+
/** When set to true, only muted subjects and reporters will be returned. */
|
|
26
|
+
onlyMuted?: boolean
|
|
25
27
|
/** Specify when fetching subjects in a certain state */
|
|
26
28
|
reviewState?: string
|
|
27
29
|
ignoreSubjects?: string[]
|
package/src/types.ts
CHANGED
|
@@ -112,10 +112,14 @@ export interface BskyInterestsPreference {
|
|
|
112
112
|
* Bluesky preferences
|
|
113
113
|
*/
|
|
114
114
|
export interface BskyPreferences {
|
|
115
|
+
/**
|
|
116
|
+
* @deprecated use `savedFeeds`
|
|
117
|
+
*/
|
|
115
118
|
feeds: {
|
|
116
119
|
saved?: string[]
|
|
117
120
|
pinned?: string[]
|
|
118
121
|
}
|
|
122
|
+
savedFeeds: AppBskyActorDefs.SavedFeed[]
|
|
119
123
|
feedViewPrefs: Record<string, BskyFeedViewPreference>
|
|
120
124
|
threadViewPrefs: BskyThreadViewPreference
|
|
121
125
|
moderationPrefs: ModerationPrefs
|
package/src/util.ts
CHANGED
|
@@ -1,6 +1,78 @@
|
|
|
1
|
+
import { AtUri } from '@atproto/syntax'
|
|
2
|
+
import { TID } from '@atproto/common-web'
|
|
3
|
+
|
|
4
|
+
import { AppBskyActorDefs } from './client'
|
|
5
|
+
|
|
1
6
|
export function sanitizeMutedWordValue(value: string) {
|
|
2
7
|
return value
|
|
3
8
|
.trim()
|
|
4
9
|
.replace(/^#(?!\ufe0f)/, '')
|
|
5
10
|
.replace(/[\r\n\u00AD\u2060\u200D\u200C\u200B]+/, '')
|
|
6
11
|
}
|
|
12
|
+
|
|
13
|
+
export function savedFeedsToUriArrays(
|
|
14
|
+
savedFeeds: AppBskyActorDefs.SavedFeed[],
|
|
15
|
+
): {
|
|
16
|
+
pinned: string[]
|
|
17
|
+
saved: string[]
|
|
18
|
+
} {
|
|
19
|
+
const pinned: string[] = []
|
|
20
|
+
const saved: string[] = []
|
|
21
|
+
|
|
22
|
+
for (const feed of savedFeeds) {
|
|
23
|
+
if (feed.pinned) {
|
|
24
|
+
pinned.push(feed.value)
|
|
25
|
+
// saved in v1 includes pinned
|
|
26
|
+
saved.push(feed.value)
|
|
27
|
+
} else {
|
|
28
|
+
saved.push(feed.value)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
pinned,
|
|
34
|
+
saved,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get the type of a saved feed, used by deprecated methods for backwards
|
|
40
|
+
* compat. Should not be used moving forward. *Invalid URIs will throw.*
|
|
41
|
+
*
|
|
42
|
+
* @param uri - The AT URI of the saved feed
|
|
43
|
+
*/
|
|
44
|
+
export function getSavedFeedType(
|
|
45
|
+
uri: string,
|
|
46
|
+
): AppBskyActorDefs.SavedFeed['type'] {
|
|
47
|
+
const urip = new AtUri(uri)
|
|
48
|
+
|
|
49
|
+
switch (urip.collection) {
|
|
50
|
+
case 'app.bsky.feed.generator':
|
|
51
|
+
return 'feed'
|
|
52
|
+
case 'app.bsky.graph.list':
|
|
53
|
+
return 'list'
|
|
54
|
+
default:
|
|
55
|
+
return 'unknown'
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function validateSavedFeed(savedFeed: AppBskyActorDefs.SavedFeed) {
|
|
60
|
+
new TID(savedFeed.id)
|
|
61
|
+
|
|
62
|
+
if (['feed', 'list'].includes(savedFeed.type)) {
|
|
63
|
+
const uri = new AtUri(savedFeed.value)
|
|
64
|
+
const isFeed = uri.collection === 'app.bsky.feed.generator'
|
|
65
|
+
const isList = uri.collection === 'app.bsky.graph.list'
|
|
66
|
+
|
|
67
|
+
if (savedFeed.type === 'feed' && !isFeed) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`Saved feed of type 'feed' must be a feed, got ${uri.collection}`,
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
if (savedFeed.type === 'list' && !isList) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Saved feed of type 'list' must be a list, got ${uri.collection}`,
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|