···11import { AtpAgent, ComAtprotoLabelDefs } from "@atproto/api";
22+import { loginAgentOrCredentials } from "./util.js";
2334/**
45 * Declare the labels this labeler will apply. Necessary for users to be able to configure what they see.
55- * @param agent The agent logged into the labeler account.
66+ * @param credentials The credentials of the labeler account.
67 * @param labelDefinitions The label definitions to declare. You can learn about the definition format [here](https://docs.bsky.app/docs/advanced-guides/moderation#custom-label-values).
88+ * @param overwriteExisting Whether to overwrite the existing label definitions if they already exist.
79 */
810export async function declareLabeler(
99- agent: AtpAgent,
1111+ credentials: { pds?: string; identifier: string; password: string },
1012 labelDefinitions: Array<ComAtprotoLabelDefs.LabelValueDefinition>,
1313+ overwriteExisting?: boolean,
1114): Promise<void>;
1215/**
1316 * Declare the labels this labeler will apply. Necessary for users to be able to configure what they see.
1414- * @param credentials The credentials of the labeler account.
1717+ * @param agent The agent logged into the labeler account.
1518 * @param labelDefinitions The label definitions to declare. You can learn about the definition format [here](https://docs.bsky.app/docs/advanced-guides/moderation#custom-label-values).
1919+ * @param overwriteExisting Whether to overwrite the existing label definitions if they already exist.
1620 */
1721export async function declareLabeler(
1818- credentials: { pds?: string; identifier: string; password: string },
2222+ agent: AtpAgent,
1923 labelDefinitions: Array<ComAtprotoLabelDefs.LabelValueDefinition>,
2424+ overwriteExisting?: boolean,
2025): Promise<void>;
2126export async function declareLabeler(
2227 agentOrCredentials: AtpAgent | { pds?: string; identifier: string; password: string },
2328 labelDefinitions: Array<ComAtprotoLabelDefs.LabelValueDefinition>,
2929+ overwriteExisting?: boolean,
2430) {
2525- const agent = agentOrCredentials instanceof AtpAgent
2626- ? agentOrCredentials
2727- : new AtpAgent({ service: agentOrCredentials.pds || "https://bsky.social" });
2828- if (!agent.hasSession) {
2929- if (!(agentOrCredentials instanceof AtpAgent)) {
3030- await agent.login(agentOrCredentials);
3131- } else {
3232- throw new Error("A password must be provided to log in to the labeler account.");
3333- }
3131+ const agent = await loginAgentOrCredentials(agentOrCredentials);
3232+ const labelValues = labelDefinitions.map(({ identifier }) => identifier);
3333+3434+ const existing = await getLabelerLabelDefinitions(agent);
3535+ if (existing?.length && !overwriteExisting) {
3636+ if (overwriteExisting === false) return;
3737+ throw new Error(
3838+ "Label definitions already exist. Use `overwriteExisting: true` to update them, or `overwriteExisting: false` to silence this error.",
3939+ );
3440 }
35413636- const labelValues = labelDefinitions.map(({ identifier }) => identifier);
3737- await agent.app.bsky.labeler.service.create({ repo: agent.did }, {
4242+ await agent.app.bsky.labeler.service.create({ repo: agent.accountDid }, {
3843 policies: { labelValues, labelValueDefinitions: labelDefinitions },
3944 createdAt: new Date().toUTCString(),
4045 });
4146}
4747+4848+/**
4949+ * Get the label definitions currently declared by the labeler.
5050+ * @param credentials The credentials of the labeler account.
5151+ * @returns The label definitions.
5252+ */
5353+export async function getLabelerLabelDefinitions(
5454+ credentials: { pds?: string; identifier: string; password: string },
5555+): Promise<Array<ComAtprotoLabelDefs.LabelValueDefinition>>;
5656+/**
5757+ * Get the label definitions currently declared by the labeler.
5858+ * @param agent The agent logged into the labeler account.
5959+ * @returns The label definitions.
6060+ */
6161+export async function getLabelerLabelDefinitions(
6262+ agent: AtpAgent,
6363+): Promise<Array<ComAtprotoLabelDefs.LabelValueDefinition>>;
6464+export async function getLabelerLabelDefinitions(
6565+ agentOrCredentials: AtpAgent | { pds?: string; identifier: string; password: string },
6666+) {
6767+ const agent = await loginAgentOrCredentials(agentOrCredentials);
6868+ const { value: { policies } } = await agent.app.bsky.labeler.service.get({
6969+ rkey: "self",
7070+ repo: agent.accountDid,
7171+ });
7272+ return policies.labelValueDefinitions;
7373+}
+8-16
src/scripts/plc.ts
···11import { AtpAgent, ComAtprotoIdentitySignPlcOperation } from "@atproto/api";
22import { P256Keypair, Secp256k1Keypair } from "@atproto/crypto";
33import * as ui8 from "uint8arrays";
44+import { loginAgentOrCredentials } from "./util.js";
4556/** Options for the {@link plcSetupLabeler} function. */
67export interface PlcSetupLabelerOptions {
···126127/**
127128 * Request a PLC token, needed for {@link plcSetupLabeler}. The token will be sent to the email
128129 * associated with the labeler account.
129129- * @param agent An agent logged into the labeler account.
130130- */
131131-export async function plcRequestToken(agent: AtpAgent): Promise<void>;
132132-/**
133133- * Request a PLC token, needed for {@link plcSetupLabeler}. The token will be sent to the email
134134- * associated with the labeler account.
135130 * @param credentials The credentials of the labeler account.
136131 */
137132export async function plcRequestToken(
138133 credentials: { pds?: string; identifier: string; password: string },
139134): Promise<void>;
135135+/**
136136+ * Request a PLC token, needed for {@link plcSetupLabeler}. The token will be sent to the email
137137+ * associated with the labeler account.
138138+ * @param agent An agent logged into the labeler account.
139139+ */
140140+export async function plcRequestToken(agent: AtpAgent): Promise<void>;
140141export async function plcRequestToken(
141142 agentOrCredentials: AtpAgent | { pds?: string; identifier: string; password: string },
142143) {
143143- const agent = agentOrCredentials instanceof AtpAgent
144144- ? agentOrCredentials
145145- : new AtpAgent({ service: agentOrCredentials.pds || "https://bsky.social" });
146146- if (!agent.hasSession) {
147147- if (!(agentOrCredentials instanceof AtpAgent)) {
148148- await agent.login(agentOrCredentials);
149149- } else {
150150- throw new Error("A password must be provided to log in to the labeler account.");
151151- }
152152- }
144144+ const agent = await loginAgentOrCredentials(agentOrCredentials);
153145 await agent.com.atproto.identity.requestPlcOperationSignature();
154146}
+26
src/scripts/util.ts
···11+import { AtpAgent } from "@atproto/api";
22+33+export interface LoginCredentials {
44+ /** The URL of the PDS where the account is located. Defaults to "https://bsky.social". */
55+ pds?: string;
66+ /** The account identifier; a DID or handle. */
77+ identifier: string;
88+ /** The account password. */
99+ password: string;
1010+}
1111+1212+export async function loginAgentOrCredentials(
1313+ agentOrCredentials: AtpAgent | { pds?: string; identifier: string; password: string },
1414+) {
1515+ const agent = agentOrCredentials instanceof AtpAgent
1616+ ? agentOrCredentials
1717+ : new AtpAgent({ service: agentOrCredentials.pds || "https://bsky.social" });
1818+ if (!agent.hasSession) {
1919+ if (!(agentOrCredentials instanceof AtpAgent)) {
2020+ await agent.login(agentOrCredentials);
2121+ } else {
2222+ throw new Error("A password must be provided to log in to the labeler account.");
2323+ }
2424+ }
2525+ return agent;
2626+}