Discord: refine presence config defaults (#10855) (thanks @h0tp-ftw)

This commit is contained in:
Shadow
2026-02-13 13:12:16 -06:00
committed by Shadow
parent 770e904c21
commit 6acea69b20
9 changed files with 208 additions and 7 deletions

View File

@@ -0,0 +1,42 @@
import { describe, expect, it } from "vitest";
import { resolveDiscordPresenceUpdate } from "./presence.js";
describe("resolveDiscordPresenceUpdate", () => {
it("returns null when no presence config provided", () => {
expect(resolveDiscordPresenceUpdate({})).toBeNull();
});
it("returns status-only presence when activity is omitted", () => {
const presence = resolveDiscordPresenceUpdate({ status: "dnd" });
expect(presence).not.toBeNull();
expect(presence?.status).toBe("dnd");
expect(presence?.activities).toEqual([]);
});
it("defaults to custom activity type when activity is set without type", () => {
const presence = resolveDiscordPresenceUpdate({ activity: "Focus time" });
expect(presence).not.toBeNull();
expect(presence?.status).toBe("online");
expect(presence?.activities).toHaveLength(1);
expect(presence?.activities[0]).toMatchObject({
type: 4,
name: "Custom Status",
state: "Focus time",
});
});
it("includes streaming url when activityType is streaming", () => {
const presence = resolveDiscordPresenceUpdate({
activity: "Live",
activityType: 1,
activityUrl: "https://twitch.tv/openclaw",
});
expect(presence).not.toBeNull();
expect(presence?.activities).toHaveLength(1);
expect(presence?.activities[0]).toMatchObject({
type: 1,
name: "Live",
url: "https://twitch.tv/openclaw",
});
});
});

View File

@@ -0,0 +1,49 @@
import type { Activity, UpdatePresenceData } from "@buape/carbon/gateway";
import type { DiscordAccountConfig } from "../../config/config.js";
const DEFAULT_CUSTOM_ACTIVITY_TYPE = 4;
const CUSTOM_STATUS_NAME = "Custom Status";
type DiscordPresenceConfig = Pick<
DiscordAccountConfig,
"activity" | "status" | "activityType" | "activityUrl"
>;
export function resolveDiscordPresenceUpdate(
config: DiscordPresenceConfig,
): UpdatePresenceData | null {
const activityText = typeof config.activity === "string" ? config.activity.trim() : "";
const status = typeof config.status === "string" ? config.status.trim() : "";
const activityType = config.activityType;
const activityUrl = typeof config.activityUrl === "string" ? config.activityUrl.trim() : "";
const hasActivity = Boolean(activityText);
const hasStatus = Boolean(status);
if (!hasActivity && !hasStatus) {
return null;
}
const activities: Activity[] = [];
if (hasActivity) {
const resolvedType = activityType ?? DEFAULT_CUSTOM_ACTIVITY_TYPE;
const activity: Activity =
resolvedType === DEFAULT_CUSTOM_ACTIVITY_TYPE
? { name: CUSTOM_STATUS_NAME, type: resolvedType, state: activityText }
: { name: activityText, type: resolvedType };
if (resolvedType === 1 && activityUrl) {
activity.url = activityUrl;
}
activities.push(activity);
}
return {
since: null,
activities,
status: (status || "online") as UpdatePresenceData["status"],
afk: false,
};
}

View File

@@ -45,6 +45,7 @@ import {
createDiscordCommandArgFallbackButton,
createDiscordNativeCommand,
} from "./native-command.js";
import { resolveDiscordPresenceUpdate } from "./presence.js";
export type MonitorDiscordOpts = {
token?: string;