00a567daa0
* Rename bsky_labeler_did * Use labeldef default setting * Add definedBy to interpretted label defs * Improve dev-env mocks for labels * Remove global label defs for dmca-violation, doxxing, and !no-promote * Change nudity global label def to default to ignore and no longer be adult-only * Remove old !no-promote tests * Add mod authorities header and move bsky labeler into it * Rename modsPref and addModService/removeModService to labelersPref and add/removeLabeler * Add defaultSetting and adultOnly to custom label value definitions * Rename InterprettedLabelValueDefinition to InterpretedLabelValueDefinition * Update dev-env mock * Move muted words and hidden posts into moderationPrefs * Add muted word and hidden post handling to moderatePost * Add mutewords tests * Finish muteword tests * Add mod-authority.test to dev-env * Rename global label value def gore to graphic-media * Fix typo * Stop converting old label values * Update label target=profile behaviors: dont filter content on hide, dont blur display names, but do show the info cards * Update label target=account behaviors: dont blur media of content * Add muteword moderation behaviors * Fix mock label defs * Implement quote-post moderation handling * Add adult content test * Handle sync legacy labels (#2291) * Handle sync legacy labels * Remap values on read * Filter out double-written legacy label values * Better naming, fix types * Fix test * Update moderation docs in sdk * Update to new atproto-accept-labelers header behavior * Add getLabelDefinitions() helper method * Add proxy header support to agent * Update mock moderation * lint * Implement moderation for userlists and feedgens * Add another test label * fix labeler in dev-env agents * fix label hydration test * fix lint error * fix agent test * fix takedown labels test --------- Co-authored-by: Eric Bailey <git@esb.lol> Co-authored-by: dholms <dtholmgren@gmail.com>
334 lines
7.4 KiB
TypeScript
334 lines
7.4 KiB
TypeScript
import {
|
|
moderateProfile,
|
|
moderatePost,
|
|
mock,
|
|
ModerationOpts,
|
|
InterpretedLabelValueDefinition,
|
|
interpretLabelValueDefinition,
|
|
} from '../src'
|
|
import './util/moderation-behavior'
|
|
|
|
interface ScenarioResult {
|
|
profileList?: string[]
|
|
profileView?: string[]
|
|
avatar?: string[]
|
|
banner?: string[]
|
|
displayName?: string[]
|
|
contentList?: string[]
|
|
contentView?: string[]
|
|
contentMedia?: string[]
|
|
}
|
|
|
|
interface Scenario {
|
|
blurs: 'content' | 'media' | 'none'
|
|
severity: 'alert' | 'inform' | 'none'
|
|
account: ScenarioResult
|
|
profile: ScenarioResult
|
|
post: ScenarioResult
|
|
}
|
|
|
|
const TESTS: Scenario[] = [
|
|
{
|
|
blurs: 'content',
|
|
severity: 'alert',
|
|
account: {
|
|
profileList: ['filter', 'alert'],
|
|
profileView: ['alert'],
|
|
contentList: ['filter', 'blur'],
|
|
contentView: ['alert'],
|
|
},
|
|
profile: {
|
|
profileList: ['alert'],
|
|
profileView: ['alert'],
|
|
},
|
|
post: {
|
|
contentList: ['filter', 'blur'],
|
|
contentView: ['alert'],
|
|
},
|
|
},
|
|
{
|
|
blurs: 'content',
|
|
severity: 'inform',
|
|
account: {
|
|
profileList: ['filter', 'inform'],
|
|
profileView: ['inform'],
|
|
contentList: ['filter', 'blur'],
|
|
contentView: ['inform'],
|
|
},
|
|
profile: {
|
|
profileList: ['inform'],
|
|
profileView: ['inform'],
|
|
},
|
|
post: {
|
|
contentList: ['filter', 'blur'],
|
|
contentView: ['inform'],
|
|
},
|
|
},
|
|
{
|
|
blurs: 'content',
|
|
severity: 'none',
|
|
account: {
|
|
profileList: ['filter'],
|
|
profileView: [],
|
|
contentList: ['filter', 'blur'],
|
|
contentView: [],
|
|
},
|
|
profile: {
|
|
profileList: [],
|
|
profileView: [],
|
|
},
|
|
post: {
|
|
contentList: ['filter', 'blur'],
|
|
contentView: [],
|
|
},
|
|
},
|
|
|
|
{
|
|
blurs: 'media',
|
|
severity: 'alert',
|
|
account: {
|
|
profileList: ['filter', 'alert'],
|
|
profileView: ['alert'],
|
|
avatar: ['blur'],
|
|
banner: ['blur'],
|
|
contentList: ['filter'],
|
|
},
|
|
profile: {
|
|
profileList: ['alert'],
|
|
profileView: ['alert'],
|
|
avatar: ['blur'],
|
|
banner: ['blur'],
|
|
},
|
|
post: {
|
|
contentList: ['filter'],
|
|
contentMedia: ['blur'],
|
|
},
|
|
},
|
|
{
|
|
blurs: 'media',
|
|
severity: 'inform',
|
|
account: {
|
|
profileList: ['filter', 'inform'],
|
|
profileView: ['inform'],
|
|
avatar: ['blur'],
|
|
banner: ['blur'],
|
|
contentList: ['filter'],
|
|
},
|
|
profile: {
|
|
profileList: ['inform'],
|
|
profileView: ['inform'],
|
|
avatar: ['blur'],
|
|
banner: ['blur'],
|
|
},
|
|
post: {
|
|
contentList: ['filter'],
|
|
contentMedia: ['blur'],
|
|
},
|
|
},
|
|
{
|
|
blurs: 'media',
|
|
severity: 'none',
|
|
account: {
|
|
profileList: ['filter'],
|
|
avatar: ['blur'],
|
|
banner: ['blur'],
|
|
contentList: ['filter'],
|
|
},
|
|
profile: {
|
|
avatar: ['blur'],
|
|
banner: ['blur'],
|
|
},
|
|
post: {
|
|
contentList: ['filter'],
|
|
contentMedia: ['blur'],
|
|
},
|
|
},
|
|
|
|
{
|
|
blurs: 'none',
|
|
severity: 'alert',
|
|
account: {
|
|
profileList: ['filter', 'alert'],
|
|
profileView: ['alert'],
|
|
contentList: ['filter', 'alert'],
|
|
contentView: ['alert'],
|
|
},
|
|
profile: {
|
|
profileList: ['alert'],
|
|
profileView: ['alert'],
|
|
},
|
|
post: {
|
|
contentList: ['filter', 'alert'],
|
|
contentView: ['alert'],
|
|
},
|
|
},
|
|
{
|
|
blurs: 'none',
|
|
severity: 'inform',
|
|
account: {
|
|
profileList: ['filter', 'inform'],
|
|
profileView: ['inform'],
|
|
contentList: ['filter', 'inform'],
|
|
contentView: ['inform'],
|
|
},
|
|
profile: {
|
|
profileList: ['inform'],
|
|
profileView: ['inform'],
|
|
},
|
|
post: {
|
|
contentList: ['filter', 'inform'],
|
|
contentView: ['inform'],
|
|
},
|
|
},
|
|
{
|
|
blurs: 'none',
|
|
severity: 'none',
|
|
account: {
|
|
profileList: ['filter'],
|
|
contentList: ['filter'],
|
|
},
|
|
profile: {},
|
|
post: {
|
|
contentList: ['filter'],
|
|
},
|
|
},
|
|
]
|
|
|
|
describe('Moderation: custom labels', () => {
|
|
const scenarios = TESTS.flatMap((test) => [
|
|
{
|
|
blurs: test.blurs,
|
|
severity: test.severity,
|
|
target: 'post',
|
|
expected: test.post,
|
|
},
|
|
{
|
|
blurs: test.blurs,
|
|
severity: test.severity,
|
|
target: 'profile',
|
|
expected: test.profile,
|
|
},
|
|
{
|
|
blurs: test.blurs,
|
|
severity: test.severity,
|
|
target: 'account',
|
|
expected: test.account,
|
|
},
|
|
])
|
|
it.each(scenarios)(
|
|
'blurs=$blurs, severity=$severity, target=$target',
|
|
({ blurs, severity, target, expected }) => {
|
|
let res
|
|
if (target === 'post') {
|
|
res = moderatePost(
|
|
mock.postView({
|
|
record: {
|
|
text: 'Hello',
|
|
createdAt: new Date().toISOString(),
|
|
},
|
|
author: mock.profileViewBasic({
|
|
handle: 'bob.test',
|
|
displayName: 'Bob',
|
|
}),
|
|
labels: [
|
|
mock.label({
|
|
val: 'custom',
|
|
uri: 'at://did:web:bob.test/app.bsky.feed.post/fake',
|
|
src: 'did:web:labeler.test',
|
|
}),
|
|
],
|
|
}),
|
|
modOpts(blurs, severity),
|
|
)
|
|
} else if (target === 'profile') {
|
|
res = moderateProfile(
|
|
mock.profileViewBasic({
|
|
handle: 'bob.test',
|
|
displayName: 'Bob',
|
|
labels: [
|
|
mock.label({
|
|
val: 'custom',
|
|
uri: 'at://did:web:bob.test/app.bsky.actor.profile/self',
|
|
src: 'did:web:labeler.test',
|
|
}),
|
|
],
|
|
}),
|
|
modOpts(blurs, severity),
|
|
)
|
|
} else {
|
|
res = moderateProfile(
|
|
mock.profileViewBasic({
|
|
handle: 'bob.test',
|
|
displayName: 'Bob',
|
|
labels: [
|
|
mock.label({
|
|
val: 'custom',
|
|
uri: 'did:web:bob.test',
|
|
src: 'did:web:labeler.test',
|
|
}),
|
|
],
|
|
}),
|
|
modOpts(blurs, severity),
|
|
)
|
|
}
|
|
expect(res.ui('profileList')).toBeModerationResult(
|
|
expected.profileList || [],
|
|
)
|
|
expect(res.ui('profileView')).toBeModerationResult(
|
|
expected.profileView || [],
|
|
)
|
|
expect(res.ui('avatar')).toBeModerationResult(expected.avatar || [])
|
|
expect(res.ui('banner')).toBeModerationResult(expected.banner || [])
|
|
expect(res.ui('displayName')).toBeModerationResult(
|
|
expected.displayName || [],
|
|
)
|
|
expect(res.ui('contentList')).toBeModerationResult(
|
|
expected.contentList || [],
|
|
)
|
|
expect(res.ui('contentView')).toBeModerationResult(
|
|
expected.contentView || [],
|
|
)
|
|
expect(res.ui('contentMedia')).toBeModerationResult(
|
|
expected.contentMedia || [],
|
|
)
|
|
},
|
|
)
|
|
})
|
|
|
|
function modOpts(blurs: string, severity: string): ModerationOpts {
|
|
return {
|
|
userDid: 'did:web:alice.test',
|
|
prefs: {
|
|
adultContentEnabled: true,
|
|
labels: {},
|
|
labelers: [
|
|
{
|
|
did: 'did:web:labeler.test',
|
|
labels: { custom: 'hide' },
|
|
},
|
|
],
|
|
mutedWords: [],
|
|
hiddenPosts: [],
|
|
},
|
|
labelDefs: {
|
|
'did:web:labeler.test': [makeCustomLabel(blurs, severity)],
|
|
},
|
|
}
|
|
}
|
|
|
|
function makeCustomLabel(
|
|
blurs: string,
|
|
severity: string,
|
|
): InterpretedLabelValueDefinition {
|
|
return interpretLabelValueDefinition(
|
|
{
|
|
identifier: 'custom',
|
|
blurs,
|
|
severity,
|
|
defaultSetting: 'warn',
|
|
locales: [],
|
|
},
|
|
'did:web:labeler.test',
|
|
)
|
|
}
|