diff --git a/packages/api/src/agent.ts b/packages/api/src/agent.ts index 1bd37640bc4..732d82715e6 100644 --- a/packages/api/src/agent.ts +++ b/packages/api/src/agent.ts @@ -20,6 +20,8 @@ import { } from './types' import { BSKY_LABELER_DID } from './const' +const MAX_MOD_AUTHORITIES = 3 +const MAX_LABELERS = 10 const REFRESH_SESSION = 'com.atproto.server.refreshSession' /** @@ -30,7 +32,7 @@ export class AtpAgent { service: URL api: AtpServiceClient session?: AtpSessionData - labelersHeader: string[] = [BSKY_LABELER_DID] + labelersHeader: string[] = [] /** * The PDS URL, driven by the did doc. May be undefined. @@ -50,11 +52,21 @@ export class AtpAgent { */ static fetch: AtpAgentFetchHandler | undefined = defaultFetchHandler + /** + * The moderation authorities to be used across all requests + */ + static modAuthoritiesHeader: string[] = [BSKY_LABELER_DID] + /** * Configures the API globally. */ static configure(opts: AtpAgentGlobalOpts) { - AtpAgent.fetch = opts.fetch + if (opts.fetch) { + AtpAgent.fetch = opts.fetch + } + if (opts.modAuthorities) { + AtpAgent.modAuthoritiesHeader = opts.modAuthorities + } } constructor(opts: AtpAgentOpts) { @@ -212,12 +224,21 @@ export class AtpAgent { authorization: `Bearer ${this.session.accessJwt}`, } } + if (AtpAgent.modAuthoritiesHeader.length) { + reqHeaders = { + ...reqHeaders, + 'atproto-mod-authorities': AtpAgent.modAuthoritiesHeader + .filter((str) => str.startsWith('did:')) + .slice(0, MAX_MOD_AUTHORITIES) + .join(','), + } + } if (this.labelersHeader.length) { reqHeaders = { ...reqHeaders, 'atproto-labelers': this.labelersHeader .filter((str) => str.startsWith('did:')) - .slice(0, 10) + .slice(0, MAX_LABELERS) .join(','), } } diff --git a/packages/api/src/bsky-agent.ts b/packages/api/src/bsky-agent.ts index 5b5b9d4c257..38c18bcec45 100644 --- a/packages/api/src/bsky-agent.ts +++ b/packages/api/src/bsky-agent.ts @@ -13,7 +13,6 @@ import { BskyInterestsPreference, } from './types' import { LabelPreference } from './moderation/types' -import { BSKY_LABELER_DID } from './const' import { DEFAULT_LABEL_SETTINGS } from './moderation/const/labels' import { sanitizeMutedWordValue } from './util' @@ -419,17 +418,6 @@ export class BskyAgent extends AtpAgent { } } - // ensure the bluesky moderation is configured - const bskyModeration = prefs.moderationPrefs.mods.find( - (modPref) => modPref.did === BSKY_LABELER_DID, - ) - if (!bskyModeration) { - prefs.moderationPrefs.mods.unshift({ - did: BSKY_LABELER_DID, - labels: {}, - }) - } - // apply the label prefs for (const pref of labelPrefs) { if (pref.labelerDid) { @@ -893,9 +881,6 @@ function prefsArrayToLabelerDids( if (modsPref) { dids = (modsPref as AppBskyActorDefs.ModsPref).mods.map((mod) => mod.did) } - if (!dids.includes(BSKY_LABELER_DID)) { - dids.unshift(BSKY_LABELER_DID) - } return dids } diff --git a/packages/api/src/types.ts b/packages/api/src/types.ts index dac0666a41a..cd513271745 100644 --- a/packages/api/src/types.ts +++ b/packages/api/src/types.ts @@ -67,7 +67,8 @@ export type AtpAgentFetchHandler = ( * AtpAgent global config opts */ export interface AtpAgentGlobalOpts { - fetch: AtpAgentFetchHandler + fetch?: AtpAgentFetchHandler + modAuthorities?: string[] } /** diff --git a/packages/api/tests/agent.test.ts b/packages/api/tests/agent.test.ts index 89491cc1616..e4ee8dd251f 100644 --- a/packages/api/tests/agent.test.ts +++ b/packages/api/tests/agent.test.ts @@ -5,6 +5,7 @@ import { AtpAgentFetchHandlerResponse, AtpSessionEvent, AtpSessionData, + BSKY_LABELER_DID, } from '..' import { TestNetworkNoAppView } from '@atproto/dev-env' import { getPdsEndpoint, isValidDidDoc } from '@atproto/common-web' @@ -481,6 +482,29 @@ describe('agent', () => { }) }) + describe('Mod authorities header', () => { + it('adds the authorities header as expected', async () => { + const server = await createHeaderEchoServer(15991) + const agent = new AtpAgent({ service: 'http://localhost:15991' }) + const agent2 = new AtpAgent({ service: 'http://localhost:15991' }) + + const res1 = await agent.com.atproto.server.describeServer() + expect(res1.data['atproto-mod-authorities']).toEqual(BSKY_LABELER_DID) + + AtpAgent.configure({ modAuthorities: ['did:plc:test1', 'did:plc:test2'] }) + const res2 = await agent.com.atproto.server.describeServer() + expect(res2.data['atproto-mod-authorities']).toEqual( + 'did:plc:test1,did:plc:test2', + ) + const res3 = await agent2.com.atproto.server.describeServer() + expect(res3.data['atproto-mod-authorities']).toEqual( + 'did:plc:test1,did:plc:test2', + ) + + await new Promise((r) => server.close(r)) + }) + }) + describe('configureLabelersHeader', () => { it('adds the labelers header as expected', async () => { const server = await createHeaderEchoServer(15991) diff --git a/packages/api/tests/bsky-agent.test.ts b/packages/api/tests/bsky-agent.test.ts index bc7aa54570b..bb914b0ebcc 100644 --- a/packages/api/tests/bsky-agent.test.ts +++ b/packages/api/tests/bsky-agent.test.ts @@ -3,7 +3,6 @@ import { BskyAgent, ComAtprotoRepoPutRecord, AppBskyActorProfile, - BSKY_LABELER_DID, DEFAULT_LABEL_SETTINGS, } from '..' @@ -229,12 +228,7 @@ describe('agent', () => { moderationPrefs: { adultContentEnabled: false, labels: DEFAULT_LABEL_SETTINGS, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -263,12 +257,7 @@ describe('agent', () => { moderationPrefs: { adultContentEnabled: true, labels: DEFAULT_LABEL_SETTINGS, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -297,12 +286,7 @@ describe('agent', () => { moderationPrefs: { adultContentEnabled: false, labels: DEFAULT_LABEL_SETTINGS, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -331,12 +315,7 @@ describe('agent', () => { moderationPrefs: { adultContentEnabled: false, labels: { ...DEFAULT_LABEL_SETTINGS, misinfo: 'hide' }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -369,12 +348,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -410,12 +384,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -451,12 +420,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -492,12 +456,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -533,12 +492,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -574,12 +528,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -621,12 +570,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -662,12 +606,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -703,12 +642,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: new Date('2023-09-11T18:05:42.556Z'), feedViewPrefs: { @@ -744,12 +678,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: new Date('2023-09-11T18:05:42.556Z'), feedViewPrefs: { @@ -785,12 +714,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: new Date('2023-09-11T18:05:42.556Z'), feedViewPrefs: { @@ -826,12 +750,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: new Date('2023-09-11T18:05:42.556Z'), feedViewPrefs: { @@ -874,12 +793,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: new Date('2023-09-11T18:05:42.556Z'), feedViewPrefs: { @@ -922,12 +836,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: new Date('2023-09-11T18:05:42.556Z'), feedViewPrefs: { @@ -970,12 +879,7 @@ describe('agent', () => { misinfo: 'hide', spam: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: new Date('2023-09-11T18:05:42.556Z'), feedViewPrefs: { @@ -1041,7 +945,7 @@ describe('agent', () => { $type: 'app.bsky.actor.defs#modsPref', mods: [ { - did: BSKY_LABELER_DID, + did: 'did:plc:first-labeler', }, ], }, @@ -1049,7 +953,7 @@ describe('agent', () => { $type: 'app.bsky.actor.defs#modsPref', mods: [ { - did: BSKY_LABELER_DID, + did: 'did:plc:first-labeler', }, { did: 'did:plc:other', @@ -1135,7 +1039,7 @@ describe('agent', () => { }, mods: [ { - did: BSKY_LABELER_DID, + did: 'did:plc:first-labeler', labels: {}, }, { @@ -1179,7 +1083,7 @@ describe('agent', () => { }, mods: [ { - did: BSKY_LABELER_DID, + did: 'did:plc:first-labeler', labels: {}, }, { @@ -1223,7 +1127,7 @@ describe('agent', () => { }, mods: [ { - did: BSKY_LABELER_DID, + did: 'did:plc:first-labeler', labels: {}, }, { @@ -1267,7 +1171,7 @@ describe('agent', () => { }, mods: [ { - did: BSKY_LABELER_DID, + did: 'did:plc:first-labeler', labels: {}, }, ], @@ -1307,7 +1211,7 @@ describe('agent', () => { }, mods: [ { - did: BSKY_LABELER_DID, + did: 'did:plc:first-labeler', labels: {}, }, ], @@ -1347,7 +1251,7 @@ describe('agent', () => { }, mods: [ { - did: BSKY_LABELER_DID, + did: 'did:plc:first-labeler', labels: {}, }, ], @@ -1398,7 +1302,7 @@ describe('agent', () => { }, mods: [ { - did: BSKY_LABELER_DID, + did: 'did:plc:first-labeler', labels: {}, }, ], @@ -1440,7 +1344,7 @@ describe('agent', () => { $type: 'app.bsky.actor.defs#modsPref', mods: [ { - did: BSKY_LABELER_DID, + did: 'did:plc:first-labeler', }, ], }, diff --git a/packages/api/tests/moderation-prefs.test.ts b/packages/api/tests/moderation-prefs.test.ts index 3129c25a626..03678842c16 100644 --- a/packages/api/tests/moderation-prefs.test.ts +++ b/packages/api/tests/moderation-prefs.test.ts @@ -1,5 +1,5 @@ import { TestNetworkNoAppView } from '@atproto/dev-env' -import { BskyAgent, BSKY_LABELER_DID, DEFAULT_LABEL_SETTINGS } from '..' +import { BskyAgent, DEFAULT_LABEL_SETTINGS } from '..' import './util/moderation-behavior' describe('agent', () => { @@ -63,12 +63,7 @@ describe('agent', () => { sexual: 'ignore', gore: 'ignore', }, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -86,7 +81,6 @@ describe('agent', () => { sort: 'oldest', }, }) - expect(agent.labelersHeader).toStrictEqual([BSKY_LABELER_DID]) }) it('adds/removes moderation services', async () => { @@ -99,10 +93,7 @@ describe('agent', () => { }) await agent.addModService('did:plc:other') - expect(agent.labelersHeader).toStrictEqual([ - BSKY_LABELER_DID, - 'did:plc:other', - ]) + expect(agent.labelersHeader).toStrictEqual(['did:plc:other']) await expect(agent.getPreferences()).resolves.toStrictEqual({ feeds: { pinned: undefined, saved: undefined }, hiddenPosts: [], @@ -111,10 +102,6 @@ describe('agent', () => { adultContentEnabled: false, labels: DEFAULT_LABEL_SETTINGS, mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, { did: 'did:plc:other', labels: {}, @@ -137,13 +124,10 @@ describe('agent', () => { prioritizeFollowedUsers: true, }, }) - expect(agent.labelersHeader).toStrictEqual([ - BSKY_LABELER_DID, - 'did:plc:other', - ]) + expect(agent.labelersHeader).toStrictEqual(['did:plc:other']) await agent.removeModService('did:plc:other') - expect(agent.labelersHeader).toStrictEqual([BSKY_LABELER_DID]) + expect(agent.labelersHeader).toStrictEqual([]) await expect(agent.getPreferences()).resolves.toStrictEqual({ feeds: { pinned: undefined, saved: undefined }, hiddenPosts: [], @@ -151,12 +135,7 @@ describe('agent', () => { moderationPrefs: { adultContentEnabled: false, labels: DEFAULT_LABEL_SETTINGS, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], + mods: [], }, birthDate: undefined, feedViewPrefs: { @@ -174,51 +153,7 @@ describe('agent', () => { prioritizeFollowedUsers: true, }, }) - expect(agent.labelersHeader).toStrictEqual([BSKY_LABELER_DID]) - }) - - it('cant remove the default moderation service', async () => { - const agent = new BskyAgent({ service: network.pds.url }) - - await agent.createAccount({ - handle: 'user6.test', - email: 'user6@test.com', - password: 'password', - }) - - await agent.removeModService(BSKY_LABELER_DID) - expect(agent.labelersHeader).toStrictEqual([BSKY_LABELER_DID]) - await expect(agent.getPreferences()).resolves.toStrictEqual({ - feeds: { pinned: undefined, saved: undefined }, - hiddenPosts: [], - interests: { tags: [] }, - moderationPrefs: { - adultContentEnabled: false, - labels: DEFAULT_LABEL_SETTINGS, - mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, - ], - }, - birthDate: undefined, - feedViewPrefs: { - home: { - hideReplies: false, - hideRepliesByUnfollowed: true, - hideRepliesByLikeCount: 0, - hideReposts: false, - hideQuotePosts: false, - }, - }, - mutedWords: [], - threadViewPrefs: { - sort: 'oldest', - prioritizeFollowedUsers: true, - }, - }) - expect(agent.labelersHeader).toStrictEqual([BSKY_LABELER_DID]) + expect(agent.labelersHeader).toStrictEqual([]) }) it('sets label preferences globally and per-moderator', async () => { @@ -243,10 +178,6 @@ describe('agent', () => { adultContentEnabled: false, labels: { ...DEFAULT_LABEL_SETTINGS, porn: 'ignore' }, mods: [ - { - did: BSKY_LABELER_DID, - labels: {}, - }, { did: 'did:plc:other', labels: {