@atproto/api 0.20.22 → 0.20.25
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 +26 -0
- package/dist/client/lexicons.d.ts +28 -12
- package/dist/client/lexicons.d.ts.map +1 -1
- package/dist/client/lexicons.js +14 -6
- package/dist/client/lexicons.js.map +1 -1
- package/dist/client/types/app/bsky/ageassurance/defs.d.ts +2 -0
- package/dist/client/types/app/bsky/ageassurance/defs.d.ts.map +1 -1
- package/dist/client/types/app/bsky/ageassurance/defs.js.map +1 -1
- package/package.json +22 -16
- package/definitions/labels.json +0 -170
- package/docs/moderation.md +0 -260
- package/jest.config.cjs +0 -23
- package/jest.d.ts +0 -20
- package/jest.setup.ts +0 -97
- package/scripts/code/labels.mjs +0 -77
- package/scripts/generate-code.mjs +0 -4
- package/src/age-assurance.test.ts +0 -218
- package/src/age-assurance.ts +0 -137
- package/src/agent.ts +0 -1645
- package/src/atp-agent.ts +0 -570
- package/src/bsky-agent.ts +0 -14
- package/src/const.ts +0 -1
- package/src/index.ts +0 -44
- package/src/mocker.ts +0 -220
- package/src/moderation/decision.ts +0 -389
- package/src/moderation/index.ts +0 -69
- package/src/moderation/mutewords.ts +0 -170
- package/src/moderation/subjects/account.ts +0 -44
- package/src/moderation/subjects/feed-generator.ts +0 -24
- package/src/moderation/subjects/notification.ts +0 -25
- package/src/moderation/subjects/post.ts +0 -433
- package/src/moderation/subjects/profile.ts +0 -26
- package/src/moderation/subjects/status.ts +0 -27
- package/src/moderation/subjects/user-list.ts +0 -48
- package/src/moderation/types.ts +0 -191
- package/src/moderation/ui.ts +0 -21
- package/src/moderation/util.ts +0 -111
- package/src/predicate.ts +0 -52
- package/src/rich-text/detection.ts +0 -146
- package/src/rich-text/rich-text.ts +0 -418
- package/src/rich-text/sanitization.ts +0 -42
- package/src/rich-text/unicode.ts +0 -47
- package/src/rich-text/util.ts +0 -15
- package/src/session-manager.ts +0 -5
- package/src/types.ts +0 -165
- package/src/util.ts +0 -96
- package/tests/atp-agent.test.ts +0 -3863
- package/tests/dispatcher.test.ts +0 -527
- package/tests/errors.test.ts +0 -29
- package/tests/moderation-behaviors.test.ts +0 -951
- package/tests/moderation-custom-labels.test.ts +0 -334
- package/tests/moderation-mutewords.test.ts +0 -1299
- package/tests/moderation-prefs.test.ts +0 -402
- package/tests/moderation-quoteposts.test.ts +0 -277
- package/tests/moderation.test.ts +0 -739
- package/tests/rich-text-detection.test.ts +0 -456
- package/tests/rich-text-sanitization.test.ts +0 -216
- package/tests/rich-text.test.ts +0 -705
- package/tests/util/echo-server.ts +0 -37
- package/tests/util/moderation-behavior.ts +0 -268
- package/tsconfig.build.json +0 -9
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -10
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { ModerationDecision } from '../decision.js'
|
|
2
|
-
import { Label, ModerationOpts, ModerationSubjectProfile } from '../types.js'
|
|
3
|
-
|
|
4
|
-
export function decideProfile(
|
|
5
|
-
subject: ModerationSubjectProfile,
|
|
6
|
-
opts: ModerationOpts,
|
|
7
|
-
): ModerationDecision {
|
|
8
|
-
const acc = new ModerationDecision()
|
|
9
|
-
|
|
10
|
-
acc.setDid(subject.did)
|
|
11
|
-
acc.setIsMe(subject.did === opts.userDid)
|
|
12
|
-
for (const label of filterProfileLabels(subject.labels)) {
|
|
13
|
-
acc.addLabel('profile', label, opts)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return acc
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function filterProfileLabels(labels?: Label[]): Label[] {
|
|
20
|
-
if (!labels) {
|
|
21
|
-
return []
|
|
22
|
-
}
|
|
23
|
-
return labels.filter((label) =>
|
|
24
|
-
label.uri.endsWith('/app.bsky.actor.profile/self'),
|
|
25
|
-
)
|
|
26
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { ModerationDecision } from '../decision.js'
|
|
2
|
-
import { ModerationOpts, ModerationSubjectProfile } from '../types.js'
|
|
3
|
-
import { decideAccount } from './account.js'
|
|
4
|
-
import { decideProfile } from './profile.js'
|
|
5
|
-
|
|
6
|
-
export function decideStatus(
|
|
7
|
-
subject: ModerationSubjectProfile,
|
|
8
|
-
opts: ModerationOpts,
|
|
9
|
-
): ModerationDecision {
|
|
10
|
-
const acc = new ModerationDecision()
|
|
11
|
-
|
|
12
|
-
acc.setDid(subject.did)
|
|
13
|
-
acc.setIsMe(subject.did === opts.userDid)
|
|
14
|
-
if ('status' in subject) {
|
|
15
|
-
if (subject.status?.labels?.length) {
|
|
16
|
-
for (const label of subject.status.labels) {
|
|
17
|
-
acc.addLabel('content', label, opts)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return ModerationDecision.merge(
|
|
23
|
-
acc,
|
|
24
|
-
decideAccount(subject, opts),
|
|
25
|
-
decideProfile(subject, opts),
|
|
26
|
-
)
|
|
27
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { AtUri } from '@atproto/syntax'
|
|
2
|
-
import { AppBskyActorDefs } from '../../client/index.js'
|
|
3
|
-
import { ModerationDecision } from '../decision.js'
|
|
4
|
-
import { ModerationOpts, ModerationSubjectUserList } from '../types.js'
|
|
5
|
-
import { decideAccount } from './account.js'
|
|
6
|
-
import { decideProfile } from './profile.js'
|
|
7
|
-
|
|
8
|
-
export function decideUserList(
|
|
9
|
-
subject: ModerationSubjectUserList,
|
|
10
|
-
opts: ModerationOpts,
|
|
11
|
-
): ModerationDecision {
|
|
12
|
-
const acc = new ModerationDecision()
|
|
13
|
-
|
|
14
|
-
const creator =
|
|
15
|
-
// Note: ListViewBasic should not contain a creator field, but let's support it anyway
|
|
16
|
-
'creator' in subject && isProfile(subject.creator)
|
|
17
|
-
? subject.creator
|
|
18
|
-
: undefined
|
|
19
|
-
|
|
20
|
-
if (creator) {
|
|
21
|
-
acc.setDid(creator.did)
|
|
22
|
-
acc.setIsMe(creator.did === opts.userDid)
|
|
23
|
-
if (subject.labels?.length) {
|
|
24
|
-
for (const label of subject.labels) {
|
|
25
|
-
acc.addLabel('content', label, opts)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return ModerationDecision.merge(
|
|
29
|
-
acc,
|
|
30
|
-
decideAccount(creator, opts),
|
|
31
|
-
decideProfile(creator, opts),
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const creatorDid = new AtUri(subject.uri).hostname
|
|
36
|
-
acc.setDid(creatorDid)
|
|
37
|
-
acc.setIsMe(creatorDid === opts.userDid)
|
|
38
|
-
if (subject.labels?.length) {
|
|
39
|
-
for (const label of subject.labels) {
|
|
40
|
-
acc.addLabel('content', label, opts)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return acc
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function isProfile(v: any): v is AppBskyActorDefs.ProfileViewBasic {
|
|
47
|
-
return v && typeof v === 'object' && 'did' in v
|
|
48
|
-
}
|
package/src/moderation/types.ts
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AppBskyActorDefs,
|
|
3
|
-
AppBskyFeedDefs,
|
|
4
|
-
AppBskyGraphDefs,
|
|
5
|
-
AppBskyNotificationListNotifications,
|
|
6
|
-
ChatBskyActorDefs,
|
|
7
|
-
ComAtprotoLabelDefs,
|
|
8
|
-
} from '../client/index.js'
|
|
9
|
-
import { KnownLabelValue } from './const/labels.js'
|
|
10
|
-
import { MuteWordMatch } from './mutewords.js'
|
|
11
|
-
|
|
12
|
-
// syntax
|
|
13
|
-
// =
|
|
14
|
-
|
|
15
|
-
export const CUSTOM_LABEL_VALUE_RE = /^[a-z-]+$/
|
|
16
|
-
|
|
17
|
-
// behaviors
|
|
18
|
-
// =
|
|
19
|
-
|
|
20
|
-
export interface ModerationBehavior {
|
|
21
|
-
profileList?: 'blur' | 'alert' | 'inform'
|
|
22
|
-
profileView?: 'blur' | 'alert' | 'inform'
|
|
23
|
-
avatar?: 'blur' | 'alert'
|
|
24
|
-
banner?: 'blur'
|
|
25
|
-
displayName?: 'blur'
|
|
26
|
-
contentList?: 'blur' | 'alert' | 'inform'
|
|
27
|
-
contentView?: 'blur' | 'alert' | 'inform'
|
|
28
|
-
contentMedia?: 'blur'
|
|
29
|
-
}
|
|
30
|
-
export const BLOCK_BEHAVIOR: ModerationBehavior = {
|
|
31
|
-
profileList: 'blur',
|
|
32
|
-
profileView: 'alert',
|
|
33
|
-
avatar: 'blur',
|
|
34
|
-
banner: 'blur',
|
|
35
|
-
contentList: 'blur',
|
|
36
|
-
contentView: 'blur',
|
|
37
|
-
}
|
|
38
|
-
export const MUTE_BEHAVIOR: ModerationBehavior = {
|
|
39
|
-
profileList: 'inform',
|
|
40
|
-
profileView: 'alert',
|
|
41
|
-
contentList: 'blur',
|
|
42
|
-
contentView: 'inform',
|
|
43
|
-
}
|
|
44
|
-
export const MUTEWORD_BEHAVIOR: ModerationBehavior = {
|
|
45
|
-
contentList: 'blur',
|
|
46
|
-
contentView: 'blur',
|
|
47
|
-
}
|
|
48
|
-
export const HIDE_BEHAVIOR: ModerationBehavior = {
|
|
49
|
-
contentList: 'blur',
|
|
50
|
-
contentView: 'blur',
|
|
51
|
-
}
|
|
52
|
-
export const NOOP_BEHAVIOR: ModerationBehavior = {}
|
|
53
|
-
|
|
54
|
-
// labels
|
|
55
|
-
// =
|
|
56
|
-
|
|
57
|
-
export type Label = ComAtprotoLabelDefs.Label
|
|
58
|
-
export type LabelTarget = 'account' | 'profile' | 'content'
|
|
59
|
-
export type LabelPreference = 'ignore' | 'warn' | 'hide'
|
|
60
|
-
|
|
61
|
-
export type LabelValueDefinitionFlag =
|
|
62
|
-
| 'no-override'
|
|
63
|
-
| 'adult'
|
|
64
|
-
| 'unauthed'
|
|
65
|
-
| 'no-self'
|
|
66
|
-
|
|
67
|
-
export interface InterpretedLabelValueDefinition
|
|
68
|
-
extends ComAtprotoLabelDefs.LabelValueDefinition {
|
|
69
|
-
definedBy?: string | undefined // did of labeler or undefined for global
|
|
70
|
-
configurable: boolean
|
|
71
|
-
defaultSetting: LabelPreference // type narrowing
|
|
72
|
-
flags: LabelValueDefinitionFlag[]
|
|
73
|
-
behaviors: {
|
|
74
|
-
account?: ModerationBehavior
|
|
75
|
-
profile?: ModerationBehavior
|
|
76
|
-
content?: ModerationBehavior
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export type LabelDefinitionMap = Record<
|
|
81
|
-
KnownLabelValue,
|
|
82
|
-
InterpretedLabelValueDefinition
|
|
83
|
-
>
|
|
84
|
-
|
|
85
|
-
// subjects
|
|
86
|
-
// =
|
|
87
|
-
|
|
88
|
-
export type ModerationSubjectProfile =
|
|
89
|
-
| AppBskyActorDefs.ProfileViewBasic
|
|
90
|
-
| AppBskyActorDefs.ProfileView
|
|
91
|
-
| AppBskyActorDefs.ProfileViewDetailed
|
|
92
|
-
| ChatBskyActorDefs.ProfileViewBasic
|
|
93
|
-
|
|
94
|
-
export type ModerationSubjectPost = AppBskyFeedDefs.PostView
|
|
95
|
-
|
|
96
|
-
export type ModerationSubjectNotification =
|
|
97
|
-
AppBskyNotificationListNotifications.Notification
|
|
98
|
-
|
|
99
|
-
export type ModerationSubjectFeedGenerator = AppBskyFeedDefs.GeneratorView
|
|
100
|
-
|
|
101
|
-
export type ModerationSubjectUserList =
|
|
102
|
-
| AppBskyGraphDefs.ListViewBasic
|
|
103
|
-
| AppBskyGraphDefs.ListView
|
|
104
|
-
|
|
105
|
-
export type ModerationSubject =
|
|
106
|
-
| ModerationSubjectProfile
|
|
107
|
-
| ModerationSubjectPost
|
|
108
|
-
| ModerationSubjectNotification
|
|
109
|
-
| ModerationSubjectFeedGenerator
|
|
110
|
-
| ModerationSubjectUserList
|
|
111
|
-
|
|
112
|
-
// behaviors
|
|
113
|
-
// =
|
|
114
|
-
|
|
115
|
-
export type ModerationCauseSource =
|
|
116
|
-
| { type: 'user' }
|
|
117
|
-
| { type: 'list'; list: AppBskyGraphDefs.ListViewBasic }
|
|
118
|
-
| { type: 'labeler'; did: string }
|
|
119
|
-
|
|
120
|
-
export type ModerationCause =
|
|
121
|
-
| {
|
|
122
|
-
type: 'blocking'
|
|
123
|
-
source: ModerationCauseSource
|
|
124
|
-
priority: 3
|
|
125
|
-
downgraded?: boolean
|
|
126
|
-
}
|
|
127
|
-
| {
|
|
128
|
-
type: 'blocked-by'
|
|
129
|
-
source: ModerationCauseSource
|
|
130
|
-
priority: 4
|
|
131
|
-
downgraded?: boolean
|
|
132
|
-
}
|
|
133
|
-
| {
|
|
134
|
-
type: 'block-other'
|
|
135
|
-
source: ModerationCauseSource
|
|
136
|
-
priority: 4
|
|
137
|
-
downgraded?: boolean
|
|
138
|
-
}
|
|
139
|
-
| {
|
|
140
|
-
type: 'label'
|
|
141
|
-
source: ModerationCauseSource
|
|
142
|
-
label: Label
|
|
143
|
-
labelDef: InterpretedLabelValueDefinition
|
|
144
|
-
target: LabelTarget
|
|
145
|
-
setting: LabelPreference
|
|
146
|
-
behavior: ModerationBehavior
|
|
147
|
-
noOverride: boolean
|
|
148
|
-
priority: 1 | 2 | 5 | 7 | 8
|
|
149
|
-
downgraded?: boolean
|
|
150
|
-
}
|
|
151
|
-
| {
|
|
152
|
-
type: 'muted'
|
|
153
|
-
source: ModerationCauseSource
|
|
154
|
-
priority: 6
|
|
155
|
-
downgraded?: boolean
|
|
156
|
-
}
|
|
157
|
-
| {
|
|
158
|
-
type: 'mute-word'
|
|
159
|
-
source: ModerationCauseSource
|
|
160
|
-
priority: 6
|
|
161
|
-
downgraded?: boolean
|
|
162
|
-
matches: MuteWordMatch[]
|
|
163
|
-
}
|
|
164
|
-
| {
|
|
165
|
-
type: 'hidden'
|
|
166
|
-
source: ModerationCauseSource
|
|
167
|
-
priority: 6
|
|
168
|
-
downgraded?: boolean
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export interface ModerationPrefsLabeler {
|
|
172
|
-
did: string
|
|
173
|
-
labels: Record<string, LabelPreference>
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export interface ModerationPrefs {
|
|
177
|
-
adultContentEnabled: boolean
|
|
178
|
-
labels: Record<string, LabelPreference>
|
|
179
|
-
labelers: ModerationPrefsLabeler[]
|
|
180
|
-
mutedWords: AppBskyActorDefs.MutedWord[]
|
|
181
|
-
hiddenPosts: string[]
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export interface ModerationOpts {
|
|
185
|
-
userDid: string | undefined
|
|
186
|
-
prefs: ModerationPrefs
|
|
187
|
-
/**
|
|
188
|
-
* Map of labeler did -> custom definitions
|
|
189
|
-
*/
|
|
190
|
-
labelDefs?: Record<string, InterpretedLabelValueDefinition[]>
|
|
191
|
-
}
|
package/src/moderation/ui.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { ModerationCause } from './types.js'
|
|
2
|
-
|
|
3
|
-
export class ModerationUI {
|
|
4
|
-
noOverride = false
|
|
5
|
-
filters: ModerationCause[] = []
|
|
6
|
-
blurs: ModerationCause[] = []
|
|
7
|
-
alerts: ModerationCause[] = []
|
|
8
|
-
informs: ModerationCause[] = []
|
|
9
|
-
get filter(): boolean {
|
|
10
|
-
return this.filters.length !== 0
|
|
11
|
-
}
|
|
12
|
-
get blur(): boolean {
|
|
13
|
-
return this.blurs.length !== 0
|
|
14
|
-
}
|
|
15
|
-
get alert(): boolean {
|
|
16
|
-
return this.alerts.length !== 0
|
|
17
|
-
}
|
|
18
|
-
get inform(): boolean {
|
|
19
|
-
return this.informs.length !== 0
|
|
20
|
-
}
|
|
21
|
-
}
|
package/src/moderation/util.ts
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AppBskyEmbedRecord,
|
|
3
|
-
AppBskyEmbedRecordWithMedia,
|
|
4
|
-
AppBskyLabelerDefs,
|
|
5
|
-
ComAtprotoLabelDefs,
|
|
6
|
-
} from '../client/index.js'
|
|
7
|
-
import { asPredicate } from '../client/util.js'
|
|
8
|
-
import {
|
|
9
|
-
InterpretedLabelValueDefinition,
|
|
10
|
-
LabelPreference,
|
|
11
|
-
LabelValueDefinitionFlag,
|
|
12
|
-
ModerationBehavior,
|
|
13
|
-
} from './types.js'
|
|
14
|
-
|
|
15
|
-
export function isQuotedPost(embed: unknown): embed is AppBskyEmbedRecord.View {
|
|
16
|
-
return Boolean(embed && AppBskyEmbedRecord.isView(embed))
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function isQuotedPostWithMedia(
|
|
20
|
-
embed: unknown,
|
|
21
|
-
): embed is AppBskyEmbedRecordWithMedia.View {
|
|
22
|
-
return Boolean(embed && AppBskyEmbedRecordWithMedia.isView(embed))
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function interpretLabelValueDefinition(
|
|
26
|
-
def: ComAtprotoLabelDefs.LabelValueDefinition,
|
|
27
|
-
definedBy: string | undefined,
|
|
28
|
-
): InterpretedLabelValueDefinition {
|
|
29
|
-
const behaviors: {
|
|
30
|
-
account: ModerationBehavior
|
|
31
|
-
profile: ModerationBehavior
|
|
32
|
-
content: ModerationBehavior
|
|
33
|
-
} = {
|
|
34
|
-
account: {},
|
|
35
|
-
profile: {},
|
|
36
|
-
content: {},
|
|
37
|
-
}
|
|
38
|
-
const alertOrInform: 'alert' | 'inform' | undefined =
|
|
39
|
-
def.severity === 'alert'
|
|
40
|
-
? 'alert'
|
|
41
|
-
: def.severity === 'inform'
|
|
42
|
-
? 'inform'
|
|
43
|
-
: undefined
|
|
44
|
-
if (def.blurs === 'content') {
|
|
45
|
-
// target=account, blurs=content
|
|
46
|
-
behaviors.account.profileList = alertOrInform
|
|
47
|
-
behaviors.account.profileView = alertOrInform
|
|
48
|
-
behaviors.account.contentList = 'blur'
|
|
49
|
-
behaviors.account.contentView = def.adultOnly ? 'blur' : alertOrInform
|
|
50
|
-
// target=profile, blurs=content
|
|
51
|
-
behaviors.profile.profileList = alertOrInform
|
|
52
|
-
behaviors.profile.profileView = alertOrInform
|
|
53
|
-
// target=content, blurs=content
|
|
54
|
-
behaviors.content.contentList = 'blur'
|
|
55
|
-
behaviors.content.contentView = def.adultOnly ? 'blur' : alertOrInform
|
|
56
|
-
} else if (def.blurs === 'media') {
|
|
57
|
-
// target=account, blurs=media
|
|
58
|
-
behaviors.account.profileList = alertOrInform
|
|
59
|
-
behaviors.account.profileView = alertOrInform
|
|
60
|
-
behaviors.account.avatar = 'blur'
|
|
61
|
-
behaviors.account.banner = 'blur'
|
|
62
|
-
// target=profile, blurs=media
|
|
63
|
-
behaviors.profile.profileList = alertOrInform
|
|
64
|
-
behaviors.profile.profileView = alertOrInform
|
|
65
|
-
behaviors.profile.avatar = 'blur'
|
|
66
|
-
behaviors.profile.banner = 'blur'
|
|
67
|
-
// target=content, blurs=media
|
|
68
|
-
behaviors.content.contentMedia = 'blur'
|
|
69
|
-
} else if (def.blurs === 'none') {
|
|
70
|
-
// target=account, blurs=none
|
|
71
|
-
behaviors.account.profileList = alertOrInform
|
|
72
|
-
behaviors.account.profileView = alertOrInform
|
|
73
|
-
behaviors.account.contentList = alertOrInform
|
|
74
|
-
behaviors.account.contentView = alertOrInform
|
|
75
|
-
// target=profile, blurs=none
|
|
76
|
-
behaviors.profile.profileList = alertOrInform
|
|
77
|
-
behaviors.profile.profileView = alertOrInform
|
|
78
|
-
// target=content, blurs=none
|
|
79
|
-
behaviors.content.contentList = alertOrInform
|
|
80
|
-
behaviors.content.contentView = alertOrInform
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
let defaultSetting: LabelPreference = 'warn'
|
|
84
|
-
if (def.defaultSetting === 'hide' || def.defaultSetting === 'ignore') {
|
|
85
|
-
defaultSetting = def.defaultSetting as LabelPreference
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const flags: LabelValueDefinitionFlag[] = ['no-self']
|
|
89
|
-
if (def.adultOnly) {
|
|
90
|
-
flags.push('adult')
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
...def,
|
|
95
|
-
definedBy,
|
|
96
|
-
configurable: true,
|
|
97
|
-
defaultSetting,
|
|
98
|
-
flags,
|
|
99
|
-
behaviors,
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function interpretLabelValueDefinitions(
|
|
104
|
-
labelerView: AppBskyLabelerDefs.LabelerViewDetailed,
|
|
105
|
-
): InterpretedLabelValueDefinition[] {
|
|
106
|
-
return (labelerView.policies?.labelValueDefinitions || [])
|
|
107
|
-
.filter(asPredicate(ComAtprotoLabelDefs.validateLabelValueDefinition))
|
|
108
|
-
.map((labelValDef) =>
|
|
109
|
-
interpretLabelValueDefinition(labelValDef, labelerView.creator.did),
|
|
110
|
-
)
|
|
111
|
-
}
|
package/src/predicate.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { AppBskyActorDefs, AppBskyActorProfile } from './client/index.js'
|
|
2
|
-
import { asPredicate } from './client/util.js'
|
|
3
|
-
|
|
4
|
-
export const isValidProfile = asPredicate(AppBskyActorProfile.validateRecord)
|
|
5
|
-
export const isValidAdultContentPref = asPredicate(
|
|
6
|
-
AppBskyActorDefs.validateAdultContentPref,
|
|
7
|
-
)
|
|
8
|
-
export const isValidBskyAppStatePref = asPredicate(
|
|
9
|
-
AppBskyActorDefs.validateBskyAppStatePref,
|
|
10
|
-
)
|
|
11
|
-
export const isValidContentLabelPref = asPredicate(
|
|
12
|
-
AppBskyActorDefs.validateContentLabelPref,
|
|
13
|
-
)
|
|
14
|
-
export const isValidFeedViewPref = asPredicate(
|
|
15
|
-
AppBskyActorDefs.validateFeedViewPref,
|
|
16
|
-
)
|
|
17
|
-
export const isValidHiddenPostsPref = asPredicate(
|
|
18
|
-
AppBskyActorDefs.validateHiddenPostsPref,
|
|
19
|
-
)
|
|
20
|
-
export const isValidInterestsPref = asPredicate(
|
|
21
|
-
AppBskyActorDefs.validateInterestsPref,
|
|
22
|
-
)
|
|
23
|
-
export const isValidLabelersPref = asPredicate(
|
|
24
|
-
AppBskyActorDefs.validateLabelersPref,
|
|
25
|
-
)
|
|
26
|
-
export const isValidMutedWordsPref = asPredicate(
|
|
27
|
-
AppBskyActorDefs.validateMutedWordsPref,
|
|
28
|
-
)
|
|
29
|
-
export const isValidPersonalDetailsPref = asPredicate(
|
|
30
|
-
AppBskyActorDefs.validatePersonalDetailsPref,
|
|
31
|
-
)
|
|
32
|
-
export const isValidDeclaredAgePref = asPredicate(
|
|
33
|
-
AppBskyActorDefs.validateDeclaredAgePref,
|
|
34
|
-
)
|
|
35
|
-
export const isValidPostInteractionSettingsPref = asPredicate(
|
|
36
|
-
AppBskyActorDefs.validatePostInteractionSettingsPref,
|
|
37
|
-
)
|
|
38
|
-
export const isValidSavedFeedsPref = asPredicate(
|
|
39
|
-
AppBskyActorDefs.validateSavedFeedsPref,
|
|
40
|
-
)
|
|
41
|
-
export const isValidSavedFeedsPrefV2 = asPredicate(
|
|
42
|
-
AppBskyActorDefs.validateSavedFeedsPrefV2,
|
|
43
|
-
)
|
|
44
|
-
export const isValidThreadViewPref = asPredicate(
|
|
45
|
-
AppBskyActorDefs.validateThreadViewPref,
|
|
46
|
-
)
|
|
47
|
-
export const isValidVerificationPrefs = asPredicate(
|
|
48
|
-
AppBskyActorDefs.validateVerificationPrefs,
|
|
49
|
-
)
|
|
50
|
-
export const isValidLiveEventPreferences = asPredicate(
|
|
51
|
-
AppBskyActorDefs.validateLiveEventPreferences,
|
|
52
|
-
)
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import TLDs from 'tlds' with { type: 'json' }
|
|
2
|
-
import { AppBskyRichtextFacet } from '../client/index.js'
|
|
3
|
-
import { UnicodeString } from './unicode.js'
|
|
4
|
-
import {
|
|
5
|
-
CASHTAG_REGEX,
|
|
6
|
-
MENTION_REGEX,
|
|
7
|
-
TAG_REGEX,
|
|
8
|
-
TRAILING_PUNCTUATION_REGEX,
|
|
9
|
-
URL_REGEX,
|
|
10
|
-
} from './util.js'
|
|
11
|
-
|
|
12
|
-
export type Facet = AppBskyRichtextFacet.Main
|
|
13
|
-
|
|
14
|
-
export function detectFacets(text: UnicodeString): Facet[] | undefined {
|
|
15
|
-
let match
|
|
16
|
-
const facets: Facet[] = []
|
|
17
|
-
{
|
|
18
|
-
// mentions
|
|
19
|
-
const re = MENTION_REGEX
|
|
20
|
-
while ((match = re.exec(text.utf16))) {
|
|
21
|
-
if (!isValidDomain(match[3]) && !match[3].endsWith('.test')) {
|
|
22
|
-
continue // probably not a handle
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const start = text.utf16.indexOf(match[3], match.index) - 1
|
|
26
|
-
facets.push({
|
|
27
|
-
$type: 'app.bsky.richtext.facet',
|
|
28
|
-
index: {
|
|
29
|
-
byteStart: text.utf16IndexToUtf8Index(start),
|
|
30
|
-
byteEnd: text.utf16IndexToUtf8Index(start + match[3].length + 1),
|
|
31
|
-
},
|
|
32
|
-
features: [
|
|
33
|
-
{
|
|
34
|
-
$type: 'app.bsky.richtext.facet#mention',
|
|
35
|
-
did: match[3], // must be resolved afterwards
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
{
|
|
42
|
-
// links
|
|
43
|
-
const re = URL_REGEX
|
|
44
|
-
while ((match = re.exec(text.utf16))) {
|
|
45
|
-
let uri = match[2]
|
|
46
|
-
if (!uri.startsWith('http')) {
|
|
47
|
-
const domain = match.groups?.domain
|
|
48
|
-
if (!domain || !isValidDomain(domain)) {
|
|
49
|
-
continue
|
|
50
|
-
}
|
|
51
|
-
uri = `https://${uri}`
|
|
52
|
-
}
|
|
53
|
-
const start = text.utf16.indexOf(match[2], match.index)
|
|
54
|
-
const index = { start, end: start + match[2].length }
|
|
55
|
-
// strip ending puncuation
|
|
56
|
-
if (/[.,;:!?]$/.test(uri)) {
|
|
57
|
-
uri = uri.slice(0, -1)
|
|
58
|
-
index.end--
|
|
59
|
-
}
|
|
60
|
-
if (/[)]$/.test(uri) && !uri.includes('(')) {
|
|
61
|
-
uri = uri.slice(0, -1)
|
|
62
|
-
index.end--
|
|
63
|
-
}
|
|
64
|
-
facets.push({
|
|
65
|
-
index: {
|
|
66
|
-
byteStart: text.utf16IndexToUtf8Index(index.start),
|
|
67
|
-
byteEnd: text.utf16IndexToUtf8Index(index.end),
|
|
68
|
-
},
|
|
69
|
-
features: [
|
|
70
|
-
{
|
|
71
|
-
$type: 'app.bsky.richtext.facet#link',
|
|
72
|
-
uri,
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
})
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
{
|
|
79
|
-
const re = TAG_REGEX
|
|
80
|
-
while ((match = re.exec(text.utf16))) {
|
|
81
|
-
const leading = match[1]
|
|
82
|
-
let tag = match[2]
|
|
83
|
-
|
|
84
|
-
if (!tag) continue
|
|
85
|
-
|
|
86
|
-
// strip ending punctuation and any spaces
|
|
87
|
-
tag = tag.trim().replace(TRAILING_PUNCTUATION_REGEX, '')
|
|
88
|
-
|
|
89
|
-
if (tag.length === 0 || tag.length > 64) continue
|
|
90
|
-
|
|
91
|
-
const index = match.index + leading.length
|
|
92
|
-
|
|
93
|
-
facets.push({
|
|
94
|
-
index: {
|
|
95
|
-
byteStart: text.utf16IndexToUtf8Index(index),
|
|
96
|
-
byteEnd: text.utf16IndexToUtf8Index(index + 1 + tag.length),
|
|
97
|
-
},
|
|
98
|
-
features: [
|
|
99
|
-
{
|
|
100
|
-
$type: 'app.bsky.richtext.facet#tag',
|
|
101
|
-
tag: tag,
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
})
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
{
|
|
108
|
-
// cashtags
|
|
109
|
-
const re = CASHTAG_REGEX
|
|
110
|
-
while ((match = re.exec(text.utf16))) {
|
|
111
|
-
const leading = match[1]
|
|
112
|
-
let ticker = match[2]
|
|
113
|
-
|
|
114
|
-
if (!ticker) continue
|
|
115
|
-
|
|
116
|
-
// Normalize to uppercase
|
|
117
|
-
ticker = ticker.toUpperCase()
|
|
118
|
-
|
|
119
|
-
const index = match.index + leading.length
|
|
120
|
-
|
|
121
|
-
facets.push({
|
|
122
|
-
index: {
|
|
123
|
-
byteStart: text.utf16IndexToUtf8Index(index),
|
|
124
|
-
byteEnd: text.utf16IndexToUtf8Index(index + 1 + ticker.length), // +1 for $
|
|
125
|
-
},
|
|
126
|
-
features: [
|
|
127
|
-
{
|
|
128
|
-
$type: 'app.bsky.richtext.facet#tag',
|
|
129
|
-
tag: '$' + ticker, // Store with $ prefix
|
|
130
|
-
},
|
|
131
|
-
],
|
|
132
|
-
})
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return facets.length > 0 ? facets : undefined
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function isValidDomain(str: string): boolean {
|
|
139
|
-
return !!TLDs.find((tld) => {
|
|
140
|
-
const i = str.lastIndexOf(tld)
|
|
141
|
-
if (i === -1) {
|
|
142
|
-
return false
|
|
143
|
-
}
|
|
144
|
-
return str.charAt(i - 1) === '.' && i === str.length - tld.length
|
|
145
|
-
})
|
|
146
|
-
}
|