mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-12 20:52:54 +00:00
chore: Lint extensions folder.
This commit is contained in:
@@ -6,21 +6,29 @@ import type { ResolvedZalouserAccount, ZalouserAccountConfig, ZalouserConfig } f
|
||||
|
||||
function listConfiguredAccountIds(cfg: OpenClawConfig): string[] {
|
||||
const accounts = (cfg.channels?.zalouser as ZalouserConfig | undefined)?.accounts;
|
||||
if (!accounts || typeof accounts !== "object") return [];
|
||||
if (!accounts || typeof accounts !== "object") {
|
||||
return [];
|
||||
}
|
||||
return Object.keys(accounts).filter(Boolean);
|
||||
}
|
||||
|
||||
export function listZalouserAccountIds(cfg: OpenClawConfig): string[] {
|
||||
const ids = listConfiguredAccountIds(cfg);
|
||||
if (ids.length === 0) return [DEFAULT_ACCOUNT_ID];
|
||||
return ids.sort((a, b) => a.localeCompare(b));
|
||||
if (ids.length === 0) {
|
||||
return [DEFAULT_ACCOUNT_ID];
|
||||
}
|
||||
return ids.toSorted((a, b) => a.localeCompare(b));
|
||||
}
|
||||
|
||||
export function resolveDefaultZalouserAccountId(cfg: OpenClawConfig): string {
|
||||
const zalouserConfig = cfg.channels?.zalouser as ZalouserConfig | undefined;
|
||||
if (zalouserConfig?.defaultAccount?.trim()) return zalouserConfig.defaultAccount.trim();
|
||||
if (zalouserConfig?.defaultAccount?.trim()) {
|
||||
return zalouserConfig.defaultAccount.trim();
|
||||
}
|
||||
const ids = listZalouserAccountIds(cfg);
|
||||
if (ids.includes(DEFAULT_ACCOUNT_ID)) return DEFAULT_ACCOUNT_ID;
|
||||
if (ids.includes(DEFAULT_ACCOUNT_ID)) {
|
||||
return DEFAULT_ACCOUNT_ID;
|
||||
}
|
||||
return ids[0] ?? DEFAULT_ACCOUNT_ID;
|
||||
}
|
||||
|
||||
@@ -29,7 +37,9 @@ function resolveAccountConfig(
|
||||
accountId: string,
|
||||
): ZalouserAccountConfig | undefined {
|
||||
const accounts = (cfg.channels?.zalouser as ZalouserConfig | undefined)?.accounts;
|
||||
if (!accounts || typeof accounts !== "object") return undefined;
|
||||
if (!accounts || typeof accounts !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
return accounts[accountId] as ZalouserAccountConfig | undefined;
|
||||
}
|
||||
|
||||
@@ -41,9 +51,15 @@ function mergeZalouserAccountConfig(cfg: OpenClawConfig, accountId: string): Zal
|
||||
}
|
||||
|
||||
function resolveZcaProfile(config: ZalouserAccountConfig, accountId: string): string {
|
||||
if (config.profile?.trim()) return config.profile.trim();
|
||||
if (process.env.ZCA_PROFILE?.trim()) return process.env.ZCA_PROFILE.trim();
|
||||
if (accountId !== DEFAULT_ACCOUNT_ID) return accountId;
|
||||
if (config.profile?.trim()) {
|
||||
return config.profile.trim();
|
||||
}
|
||||
if (process.env.ZCA_PROFILE?.trim()) {
|
||||
return process.env.ZCA_PROFILE.trim();
|
||||
}
|
||||
if (accountId !== DEFAULT_ACCOUNT_ID) {
|
||||
return accountId;
|
||||
}
|
||||
return "default";
|
||||
}
|
||||
|
||||
@@ -111,7 +127,9 @@ export async function getZcaUserInfo(
|
||||
profile: string,
|
||||
): Promise<{ userId?: string; displayName?: string } | null> {
|
||||
const result = await runZca(["me", "info", "-j"], { profile, timeout: 10000 });
|
||||
if (!result.ok) return null;
|
||||
if (!result.ok) {
|
||||
return null;
|
||||
}
|
||||
return parseJsonOutput<{ userId?: string; displayName?: string }>(result.stdout);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ describe("zalouser outbound chunker", () => {
|
||||
it("chunks without empty strings and respects limit", () => {
|
||||
const chunker = zalouserPlugin.outbound?.chunker;
|
||||
expect(chunker).toBeTypeOf("function");
|
||||
if (!chunker) return;
|
||||
if (!chunker) {
|
||||
return;
|
||||
}
|
||||
|
||||
const limit = 10;
|
||||
const chunks = chunker("hello world\nthis is a test", limit);
|
||||
|
||||
@@ -85,7 +85,7 @@ function resolveZalouserGroupToolPolicy(
|
||||
params: ChannelGroupContext,
|
||||
): GroupToolPolicyConfig | undefined {
|
||||
const account = resolveZalouserAccountSync({
|
||||
cfg: params.cfg as OpenClawConfig,
|
||||
cfg: params.cfg,
|
||||
accountId: params.accountId ?? undefined,
|
||||
});
|
||||
const groups = account.config.groups ?? {};
|
||||
@@ -96,7 +96,9 @@ function resolveZalouserGroupToolPolicy(
|
||||
);
|
||||
for (const key of candidates) {
|
||||
const entry = groups[key];
|
||||
if (entry?.tools) return entry.tools;
|
||||
if (entry?.tools) {
|
||||
return entry.tools;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -111,9 +113,9 @@ export const zalouserDock: ChannelDock = {
|
||||
outbound: { textChunkLimit: 2000 },
|
||||
config: {
|
||||
resolveAllowFrom: ({ cfg, accountId }) =>
|
||||
(
|
||||
resolveZalouserAccountSync({ cfg: cfg as OpenClawConfig, accountId }).config.allowFrom ?? []
|
||||
).map((entry) => String(entry)),
|
||||
(resolveZalouserAccountSync({ cfg: cfg, accountId }).config.allowFrom ?? []).map((entry) =>
|
||||
String(entry),
|
||||
),
|
||||
formatAllowFrom: ({ allowFrom }) =>
|
||||
allowFrom
|
||||
.map((entry) => String(entry).trim())
|
||||
@@ -146,13 +148,12 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
reload: { configPrefixes: ["channels.zalouser"] },
|
||||
configSchema: buildChannelConfigSchema(ZalouserConfigSchema),
|
||||
config: {
|
||||
listAccountIds: (cfg) => listZalouserAccountIds(cfg as OpenClawConfig),
|
||||
resolveAccount: (cfg, accountId) =>
|
||||
resolveZalouserAccountSync({ cfg: cfg as OpenClawConfig, accountId }),
|
||||
defaultAccountId: (cfg) => resolveDefaultZalouserAccountId(cfg as OpenClawConfig),
|
||||
listAccountIds: (cfg) => listZalouserAccountIds(cfg),
|
||||
resolveAccount: (cfg, accountId) => resolveZalouserAccountSync({ cfg: cfg, accountId }),
|
||||
defaultAccountId: (cfg) => resolveDefaultZalouserAccountId(cfg),
|
||||
setAccountEnabled: ({ cfg, accountId, enabled }) =>
|
||||
setAccountEnabledInConfigSection({
|
||||
cfg: cfg as OpenClawConfig,
|
||||
cfg: cfg,
|
||||
sectionKey: "zalouser",
|
||||
accountId,
|
||||
enabled,
|
||||
@@ -160,7 +161,7 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
}),
|
||||
deleteAccount: ({ cfg, accountId }) =>
|
||||
deleteAccountFromConfigSection({
|
||||
cfg: cfg as OpenClawConfig,
|
||||
cfg: cfg,
|
||||
sectionKey: "zalouser",
|
||||
accountId,
|
||||
clearBaseFields: [
|
||||
@@ -188,9 +189,9 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
configured: undefined,
|
||||
}),
|
||||
resolveAllowFrom: ({ cfg, accountId }) =>
|
||||
(
|
||||
resolveZalouserAccountSync({ cfg: cfg as OpenClawConfig, accountId }).config.allowFrom ?? []
|
||||
).map((entry) => String(entry)),
|
||||
(resolveZalouserAccountSync({ cfg: cfg, accountId }).config.allowFrom ?? []).map((entry) =>
|
||||
String(entry),
|
||||
),
|
||||
formatAllowFrom: ({ allowFrom }) =>
|
||||
allowFrom
|
||||
.map((entry) => String(entry).trim())
|
||||
@@ -201,9 +202,7 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
security: {
|
||||
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
||||
const useAccountPath = Boolean(
|
||||
(cfg as OpenClawConfig).channels?.zalouser?.accounts?.[resolvedAccountId],
|
||||
);
|
||||
const useAccountPath = Boolean(cfg.channels?.zalouser?.accounts?.[resolvedAccountId]);
|
||||
const basePath = useAccountPath
|
||||
? `channels.zalouser.accounts.${resolvedAccountId}.`
|
||||
: "channels.zalouser.";
|
||||
@@ -228,7 +227,7 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
resolveAccountId: ({ accountId }) => normalizeAccountId(accountId),
|
||||
applyAccountName: ({ cfg, accountId, name }) =>
|
||||
applyAccountNameToChannelSection({
|
||||
cfg: cfg as OpenClawConfig,
|
||||
cfg: cfg,
|
||||
channelKey: "zalouser",
|
||||
accountId,
|
||||
name,
|
||||
@@ -236,7 +235,7 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
validateInput: () => null,
|
||||
applyAccountConfig: ({ cfg, accountId, input }) => {
|
||||
const namedConfig = applyAccountNameToChannelSection({
|
||||
cfg: cfg as OpenClawConfig,
|
||||
cfg: cfg,
|
||||
channelKey: "zalouser",
|
||||
accountId,
|
||||
name: input.name,
|
||||
@@ -268,9 +267,9 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
...next.channels?.zalouser,
|
||||
enabled: true,
|
||||
accounts: {
|
||||
...(next.channels?.zalouser?.accounts ?? {}),
|
||||
...next.channels?.zalouser?.accounts,
|
||||
[accountId]: {
|
||||
...(next.channels?.zalouser?.accounts?.[accountId] ?? {}),
|
||||
...next.channels?.zalouser?.accounts?.[accountId],
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
@@ -282,13 +281,17 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
messaging: {
|
||||
normalizeTarget: (raw) => {
|
||||
const trimmed = raw?.trim();
|
||||
if (!trimmed) return undefined;
|
||||
if (!trimmed) {
|
||||
return undefined;
|
||||
}
|
||||
return trimmed.replace(/^(zalouser|zlu):/i, "");
|
||||
},
|
||||
targetResolver: {
|
||||
looksLikeId: (raw) => {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return false;
|
||||
if (!trimmed) {
|
||||
return false;
|
||||
}
|
||||
return /^\d{3,}$/.test(trimmed);
|
||||
},
|
||||
hint: "<threadId>",
|
||||
@@ -297,8 +300,10 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
directory: {
|
||||
self: async ({ cfg, accountId, runtime }) => {
|
||||
const ok = await checkZcaInstalled();
|
||||
if (!ok) throw new Error("Missing dependency: `zca` not found in PATH");
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg as OpenClawConfig, accountId });
|
||||
if (!ok) {
|
||||
throw new Error("Missing dependency: `zca` not found in PATH");
|
||||
}
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg, accountId });
|
||||
const result = await runZca(["me", "info", "-j"], {
|
||||
profile: account.profile,
|
||||
timeout: 10000,
|
||||
@@ -308,7 +313,9 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
return null;
|
||||
}
|
||||
const parsed = parseJsonOutput<ZcaUserInfo>(result.stdout);
|
||||
if (!parsed?.userId) return null;
|
||||
if (!parsed?.userId) {
|
||||
return null;
|
||||
}
|
||||
return mapUser({
|
||||
id: String(parsed.userId),
|
||||
name: parsed.displayName ?? null,
|
||||
@@ -318,8 +325,10 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
},
|
||||
listPeers: async ({ cfg, accountId, query, limit }) => {
|
||||
const ok = await checkZcaInstalled();
|
||||
if (!ok) throw new Error("Missing dependency: `zca` not found in PATH");
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg as OpenClawConfig, accountId });
|
||||
if (!ok) {
|
||||
throw new Error("Missing dependency: `zca` not found in PATH");
|
||||
}
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg, accountId });
|
||||
const args = query?.trim() ? ["friend", "find", query.trim()] : ["friend", "list", "-j"];
|
||||
const result = await runZca(args, { profile: account.profile, timeout: 15000 });
|
||||
if (!result.ok) {
|
||||
@@ -340,8 +349,10 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
},
|
||||
listGroups: async ({ cfg, accountId, query, limit }) => {
|
||||
const ok = await checkZcaInstalled();
|
||||
if (!ok) throw new Error("Missing dependency: `zca` not found in PATH");
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg as OpenClawConfig, accountId });
|
||||
if (!ok) {
|
||||
throw new Error("Missing dependency: `zca` not found in PATH");
|
||||
}
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg, accountId });
|
||||
const result = await runZca(["group", "list", "-j"], {
|
||||
profile: account.profile,
|
||||
timeout: 15000,
|
||||
@@ -367,8 +378,10 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
},
|
||||
listGroupMembers: async ({ cfg, accountId, groupId, limit }) => {
|
||||
const ok = await checkZcaInstalled();
|
||||
if (!ok) throw new Error("Missing dependency: `zca` not found in PATH");
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg as OpenClawConfig, accountId });
|
||||
if (!ok) {
|
||||
throw new Error("Missing dependency: `zca` not found in PATH");
|
||||
}
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg, accountId });
|
||||
const result = await runZca(["group", "members", groupId, "-j"], {
|
||||
profile: account.profile,
|
||||
timeout: 20000,
|
||||
@@ -383,7 +396,9 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
? parsed
|
||||
.map((m) => {
|
||||
const id = m.userId ?? (m as { id?: string | number }).id;
|
||||
if (!id) return null;
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
return mapUser({
|
||||
id: String(id),
|
||||
name: (m as { displayName?: string }).displayName ?? null,
|
||||
@@ -412,7 +427,7 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
}
|
||||
try {
|
||||
const account = resolveZalouserAccountSync({
|
||||
cfg: cfg as OpenClawConfig,
|
||||
cfg: cfg,
|
||||
accountId: accountId ?? DEFAULT_ACCOUNT_ID,
|
||||
});
|
||||
const args =
|
||||
@@ -422,7 +437,9 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
: ["friend", "list", "-j"]
|
||||
: ["group", "list", "-j"];
|
||||
const result = await runZca(args, { profile: account.profile, timeout: 15000 });
|
||||
if (!result.ok) throw new Error(result.stderr || "zca lookup failed");
|
||||
if (!result.ok) {
|
||||
throw new Error(result.stderr || "zca lookup failed");
|
||||
}
|
||||
if (kind === "user") {
|
||||
const parsed = parseJsonOutput<ZcaFriend[]>(result.stdout) ?? [];
|
||||
const matches = Array.isArray(parsed)
|
||||
@@ -469,9 +486,11 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
idLabel: "zalouserUserId",
|
||||
normalizeAllowEntry: (entry) => entry.replace(/^(zalouser|zlu):/i, ""),
|
||||
notifyApproval: async ({ cfg, id }) => {
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg as OpenClawConfig });
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg });
|
||||
const authenticated = await checkZcaAuthenticated(account.profile);
|
||||
if (!authenticated) throw new Error("Zalouser not authenticated");
|
||||
if (!authenticated) {
|
||||
throw new Error("Zalouser not authenticated");
|
||||
}
|
||||
await sendMessageZalouser(id, "Your pairing request has been approved.", {
|
||||
profile: account.profile,
|
||||
});
|
||||
@@ -480,7 +499,7 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
auth: {
|
||||
login: async ({ cfg, accountId, runtime }) => {
|
||||
const account = resolveZalouserAccountSync({
|
||||
cfg: cfg as OpenClawConfig,
|
||||
cfg: cfg,
|
||||
accountId: accountId ?? DEFAULT_ACCOUNT_ID,
|
||||
});
|
||||
const ok = await checkZcaInstalled();
|
||||
@@ -501,8 +520,12 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
outbound: {
|
||||
deliveryMode: "direct",
|
||||
chunker: (text, limit) => {
|
||||
if (!text) return [];
|
||||
if (limit <= 0 || text.length <= limit) return [text];
|
||||
if (!text) {
|
||||
return [];
|
||||
}
|
||||
if (limit <= 0 || text.length <= limit) {
|
||||
return [text];
|
||||
}
|
||||
const chunks: string[] = [];
|
||||
let remaining = text;
|
||||
while (remaining.length > limit) {
|
||||
@@ -510,21 +533,27 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
const lastNewline = window.lastIndexOf("\n");
|
||||
const lastSpace = window.lastIndexOf(" ");
|
||||
let breakIdx = lastNewline > 0 ? lastNewline : lastSpace;
|
||||
if (breakIdx <= 0) breakIdx = limit;
|
||||
if (breakIdx <= 0) {
|
||||
breakIdx = limit;
|
||||
}
|
||||
const rawChunk = remaining.slice(0, breakIdx);
|
||||
const chunk = rawChunk.trimEnd();
|
||||
if (chunk.length > 0) chunks.push(chunk);
|
||||
if (chunk.length > 0) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
const brokeOnSeparator = breakIdx < remaining.length && /\s/.test(remaining[breakIdx]);
|
||||
const nextStart = Math.min(remaining.length, breakIdx + (brokeOnSeparator ? 1 : 0));
|
||||
remaining = remaining.slice(nextStart).trimStart();
|
||||
}
|
||||
if (remaining.length) chunks.push(remaining);
|
||||
if (remaining.length) {
|
||||
chunks.push(remaining);
|
||||
}
|
||||
return chunks;
|
||||
},
|
||||
chunkerMode: "text",
|
||||
textChunkLimit: 2000,
|
||||
sendText: async ({ to, text, accountId, cfg }) => {
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg as OpenClawConfig, accountId });
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg, accountId });
|
||||
const result = await sendMessageZalouser(to, text, { profile: account.profile });
|
||||
return {
|
||||
channel: "zalouser",
|
||||
@@ -534,7 +563,7 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
};
|
||||
},
|
||||
sendMedia: async ({ to, text, mediaUrl, accountId, cfg }) => {
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg as OpenClawConfig, accountId });
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg, accountId });
|
||||
const result = await sendMessageZalouser(to, text, {
|
||||
profile: account.profile,
|
||||
mediaUrl,
|
||||
@@ -591,7 +620,9 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
let userLabel = "";
|
||||
try {
|
||||
const userInfo = await getZcaUserInfo(account.profile);
|
||||
if (userInfo?.displayName) userLabel = ` (${userInfo.displayName})`;
|
||||
if (userInfo?.displayName) {
|
||||
userLabel = ` (${userInfo.displayName})`;
|
||||
}
|
||||
ctx.setStatus({
|
||||
accountId: account.accountId,
|
||||
user: userInfo,
|
||||
@@ -603,7 +634,7 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
||||
const { monitorZalouserProvider } = await import("./monitor.js");
|
||||
return monitorZalouserProvider({
|
||||
account,
|
||||
config: ctx.cfg as OpenClawConfig,
|
||||
config: ctx.cfg,
|
||||
runtime: ctx.runtime,
|
||||
abortSignal: ctx.abortSignal,
|
||||
statusSink: (patch) => ctx.setStatus({ accountId: ctx.accountId, ...patch }),
|
||||
|
||||
@@ -29,7 +29,9 @@ function buildNameIndex<T>(items: T[], nameFn: (item: T) => string | undefined):
|
||||
const index = new Map<string, T[]>();
|
||||
for (const item of items) {
|
||||
const name = nameFn(item)?.trim().toLowerCase();
|
||||
if (!name) continue;
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
const list = index.get(name) ?? [];
|
||||
list.push(item);
|
||||
index.set(name, list);
|
||||
@@ -46,7 +48,9 @@ function logVerbose(core: ZalouserCoreRuntime, runtime: RuntimeEnv, message: str
|
||||
}
|
||||
|
||||
function isSenderAllowed(senderId: string, allowFrom: string[]): boolean {
|
||||
if (allowFrom.includes("*")) return true;
|
||||
if (allowFrom.includes("*")) {
|
||||
return true;
|
||||
}
|
||||
const normalizedSenderId = senderId.toLowerCase();
|
||||
return allowFrom.some((entry) => {
|
||||
const normalized = entry.toLowerCase().replace(/^(zalouser|zlu):/i, "");
|
||||
@@ -56,7 +60,9 @@ function isSenderAllowed(senderId: string, allowFrom: string[]): boolean {
|
||||
|
||||
function normalizeGroupSlug(raw?: string | null): string {
|
||||
const trimmed = raw?.trim().toLowerCase() ?? "";
|
||||
if (!trimmed) return "";
|
||||
if (!trimmed) {
|
||||
return "";
|
||||
}
|
||||
return trimmed
|
||||
.replace(/^#/, "")
|
||||
.replace(/[^a-z0-9]+/g, "-")
|
||||
@@ -70,7 +76,9 @@ function isGroupAllowed(params: {
|
||||
}): boolean {
|
||||
const groups = params.groups ?? {};
|
||||
const keys = Object.keys(groups);
|
||||
if (keys.length === 0) return false;
|
||||
if (keys.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const candidates = [
|
||||
params.groupId,
|
||||
`group:${params.groupId}`,
|
||||
@@ -79,11 +87,15 @@ function isGroupAllowed(params: {
|
||||
].filter(Boolean);
|
||||
for (const candidate of candidates) {
|
||||
const entry = groups[candidate];
|
||||
if (!entry) continue;
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
return entry.allow !== false && entry.enabled !== false;
|
||||
}
|
||||
const wildcard = groups["*"];
|
||||
if (wildcard) return wildcard.allow !== false && wildcard.enabled !== false;
|
||||
if (wildcard) {
|
||||
return wildcard.allow !== false && wildcard.enabled !== false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -104,7 +116,9 @@ function startZcaListener(
|
||||
buffer = lines.pop() ?? "";
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) continue;
|
||||
if (!trimmed) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed) as ZcaMessage;
|
||||
onMessage(parsed);
|
||||
@@ -118,7 +132,9 @@ function startZcaListener(
|
||||
|
||||
proc.stderr?.on("data", (data: Buffer) => {
|
||||
const text = data.toString().trim();
|
||||
if (text) runtime.error(`[zalouser] zca stderr: ${text}`);
|
||||
if (text) {
|
||||
runtime.error(`[zalouser] zca stderr: ${text}`);
|
||||
}
|
||||
});
|
||||
|
||||
void promise.then((result) => {
|
||||
@@ -147,7 +163,9 @@ async function processMessage(
|
||||
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void,
|
||||
): Promise<void> {
|
||||
const { threadId, content, timestamp, metadata } = message;
|
||||
if (!content?.trim()) return;
|
||||
if (!content?.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isGroup = metadata?.isGroup ?? false;
|
||||
const senderId = metadata?.fromId ?? threadId;
|
||||
@@ -476,7 +494,9 @@ export async function monitorZalouserProvider(
|
||||
for (const entry of groupKeys) {
|
||||
const cleaned = normalizeZalouserEntry(entry);
|
||||
if (/^\d+$/.test(cleaned)) {
|
||||
if (!nextGroups[cleaned]) nextGroups[cleaned] = groupsConfig[entry];
|
||||
if (!nextGroups[cleaned]) {
|
||||
nextGroups[cleaned] = groupsConfig[entry];
|
||||
}
|
||||
mapping.push(`${entry}→${cleaned}`);
|
||||
continue;
|
||||
}
|
||||
@@ -484,7 +504,9 @@ export async function monitorZalouserProvider(
|
||||
const match = matches[0];
|
||||
const id = match?.groupId ? String(match.groupId) : undefined;
|
||||
if (id) {
|
||||
if (!nextGroups[id]) nextGroups[id] = groupsConfig[entry];
|
||||
if (!nextGroups[id]) {
|
||||
nextGroups[id] = groupsConfig[entry];
|
||||
}
|
||||
mapping.push(`${entry}→${id}`);
|
||||
} else {
|
||||
unresolved.push(entry);
|
||||
|
||||
@@ -73,19 +73,29 @@ async function promptZalouserAllowFrom(params: {
|
||||
|
||||
const resolveUserId = async (input: string): Promise<string | null> => {
|
||||
const trimmed = input.trim();
|
||||
if (!trimmed) return null;
|
||||
if (/^\d+$/.test(trimmed)) return trimmed;
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
if (/^\d+$/.test(trimmed)) {
|
||||
return trimmed;
|
||||
}
|
||||
const ok = await checkZcaInstalled();
|
||||
if (!ok) return null;
|
||||
if (!ok) {
|
||||
return null;
|
||||
}
|
||||
const result = await runZca(["friend", "find", trimmed], {
|
||||
profile: resolved.profile,
|
||||
timeout: 15000,
|
||||
});
|
||||
if (!result.ok) return null;
|
||||
if (!result.ok) {
|
||||
return null;
|
||||
}
|
||||
const parsed = parseJsonOutput<ZcaFriend[]>(result.stdout);
|
||||
const rows = Array.isArray(parsed) ? parsed : [];
|
||||
const match = rows[0];
|
||||
if (!match?.userId) return null;
|
||||
if (!match?.userId) {
|
||||
return null;
|
||||
}
|
||||
if (rows.length > 1) {
|
||||
await prompter.note(
|
||||
`Multiple matches for "${trimmed}", using ${match.displayName ?? match.userId}.`,
|
||||
@@ -140,9 +150,9 @@ async function promptZalouserAllowFrom(params: {
|
||||
...cfg.channels?.zalouser,
|
||||
enabled: true,
|
||||
accounts: {
|
||||
...(cfg.channels?.zalouser?.accounts ?? {}),
|
||||
...cfg.channels?.zalouser?.accounts,
|
||||
[accountId]: {
|
||||
...(cfg.channels?.zalouser?.accounts?.[accountId] ?? {}),
|
||||
...cfg.channels?.zalouser?.accounts?.[accountId],
|
||||
enabled: cfg.channels?.zalouser?.accounts?.[accountId]?.enabled ?? true,
|
||||
dmPolicy: "allowlist",
|
||||
allowFrom: unique,
|
||||
@@ -180,9 +190,9 @@ function setZalouserGroupPolicy(
|
||||
...cfg.channels?.zalouser,
|
||||
enabled: true,
|
||||
accounts: {
|
||||
...(cfg.channels?.zalouser?.accounts ?? {}),
|
||||
...cfg.channels?.zalouser?.accounts,
|
||||
[accountId]: {
|
||||
...(cfg.channels?.zalouser?.accounts?.[accountId] ?? {}),
|
||||
...cfg.channels?.zalouser?.accounts?.[accountId],
|
||||
enabled: cfg.channels?.zalouser?.accounts?.[accountId]?.enabled ?? true,
|
||||
groupPolicy,
|
||||
},
|
||||
@@ -219,9 +229,9 @@ function setZalouserGroupAllowlist(
|
||||
...cfg.channels?.zalouser,
|
||||
enabled: true,
|
||||
accounts: {
|
||||
...(cfg.channels?.zalouser?.accounts ?? {}),
|
||||
...cfg.channels?.zalouser?.accounts,
|
||||
[accountId]: {
|
||||
...(cfg.channels?.zalouser?.accounts?.[accountId] ?? {}),
|
||||
...cfg.channels?.zalouser?.accounts?.[accountId],
|
||||
enabled: cfg.channels?.zalouser?.accounts?.[accountId]?.enabled ?? true,
|
||||
groups,
|
||||
},
|
||||
@@ -241,14 +251,18 @@ async function resolveZalouserGroups(params: {
|
||||
profile: account.profile,
|
||||
timeout: 15000,
|
||||
});
|
||||
if (!result.ok) throw new Error(result.stderr || "Failed to list groups");
|
||||
if (!result.ok) {
|
||||
throw new Error(result.stderr || "Failed to list groups");
|
||||
}
|
||||
const groups = (parseJsonOutput<ZcaGroup[]>(result.stdout) ?? []).filter((group) =>
|
||||
Boolean(group.groupId),
|
||||
);
|
||||
const byName = new Map<string, ZcaGroup[]>();
|
||||
for (const group of groups) {
|
||||
const name = group.name?.trim().toLowerCase();
|
||||
if (!name) continue;
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
const list = byName.get(name) ?? [];
|
||||
list.push(group);
|
||||
byName.set(name, list);
|
||||
@@ -256,8 +270,12 @@ async function resolveZalouserGroups(params: {
|
||||
|
||||
return params.entries.map((input) => {
|
||||
const trimmed = input.trim();
|
||||
if (!trimmed) return { input, resolved: false };
|
||||
if (/^\d+$/.test(trimmed)) return { input, resolved: true, id: trimmed };
|
||||
if (!trimmed) {
|
||||
return { input, resolved: false };
|
||||
}
|
||||
if (/^\d+$/.test(trimmed)) {
|
||||
return { input, resolved: true, id: trimmed };
|
||||
}
|
||||
const matches = byName.get(trimmed.toLowerCase()) ?? [];
|
||||
const match = matches[0];
|
||||
return match?.groupId
|
||||
@@ -271,16 +289,15 @@ const dmPolicy: ChannelOnboardingDmPolicy = {
|
||||
channel,
|
||||
policyKey: "channels.zalouser.dmPolicy",
|
||||
allowFromKey: "channels.zalouser.allowFrom",
|
||||
getCurrent: (cfg) =>
|
||||
((cfg as OpenClawConfig).channels?.zalouser?.dmPolicy ?? "pairing") as "pairing",
|
||||
setPolicy: (cfg, policy) => setZalouserDmPolicy(cfg as OpenClawConfig, policy),
|
||||
getCurrent: (cfg) => (cfg.channels?.zalouser?.dmPolicy ?? "pairing") as "pairing",
|
||||
setPolicy: (cfg, policy) => setZalouserDmPolicy(cfg, policy),
|
||||
promptAllowFrom: async ({ cfg, prompter, accountId }) => {
|
||||
const id =
|
||||
accountId && normalizeAccountId(accountId)
|
||||
? (normalizeAccountId(accountId) ?? DEFAULT_ACCOUNT_ID)
|
||||
: resolveDefaultZalouserAccountId(cfg as OpenClawConfig);
|
||||
: resolveDefaultZalouserAccountId(cfg);
|
||||
return promptZalouserAllowFrom({
|
||||
cfg: cfg as OpenClawConfig,
|
||||
cfg: cfg,
|
||||
prompter,
|
||||
accountId: id,
|
||||
});
|
||||
@@ -291,10 +308,10 @@ export const zalouserOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
channel,
|
||||
dmPolicy,
|
||||
getStatus: async ({ cfg }) => {
|
||||
const ids = listZalouserAccountIds(cfg as OpenClawConfig);
|
||||
const ids = listZalouserAccountIds(cfg);
|
||||
let configured = false;
|
||||
for (const accountId of ids) {
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg as OpenClawConfig, accountId });
|
||||
const account = resolveZalouserAccountSync({ cfg: cfg, accountId });
|
||||
const isAuth = await checkZcaAuthenticated(account.profile);
|
||||
if (isAuth) {
|
||||
configured = true;
|
||||
@@ -332,12 +349,12 @@ export const zalouserOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
}
|
||||
|
||||
const zalouserOverride = accountOverrides.zalouser?.trim();
|
||||
const defaultAccountId = resolveDefaultZalouserAccountId(cfg as OpenClawConfig);
|
||||
const defaultAccountId = resolveDefaultZalouserAccountId(cfg);
|
||||
let accountId = zalouserOverride ? normalizeAccountId(zalouserOverride) : defaultAccountId;
|
||||
|
||||
if (shouldPromptAccountIds && !zalouserOverride) {
|
||||
accountId = await promptAccountId({
|
||||
cfg: cfg as OpenClawConfig,
|
||||
cfg: cfg,
|
||||
prompter,
|
||||
label: "Zalo Personal",
|
||||
currentId: accountId,
|
||||
@@ -346,7 +363,7 @@ export const zalouserOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
});
|
||||
}
|
||||
|
||||
let next = cfg as OpenClawConfig;
|
||||
let next = cfg;
|
||||
const account = resolveZalouserAccountSync({ cfg: next, accountId });
|
||||
const alreadyAuthenticated = await checkZcaAuthenticated(account.profile);
|
||||
|
||||
@@ -411,9 +428,9 @@ export const zalouserOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
...next.channels?.zalouser,
|
||||
enabled: true,
|
||||
accounts: {
|
||||
...(next.channels?.zalouser?.accounts ?? {}),
|
||||
...next.channels?.zalouser?.accounts,
|
||||
[accountId]: {
|
||||
...(next.channels?.zalouser?.accounts?.[accountId] ?? {}),
|
||||
...next.channels?.zalouser?.accounts?.[accountId],
|
||||
enabled: true,
|
||||
profile: account.profile,
|
||||
},
|
||||
|
||||
@@ -34,7 +34,9 @@ export async function sendMessageZalouser(
|
||||
|
||||
// Send text message
|
||||
const args = ["msg", "send", threadId.trim(), text.slice(0, 2000)];
|
||||
if (options.isGroup) args.push("-g");
|
||||
if (options.isGroup) {
|
||||
args.push("-g");
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await runZca(args, { profile });
|
||||
@@ -79,7 +81,9 @@ async function sendMediaZalouser(
|
||||
if (options.caption) {
|
||||
args.push("-m", options.caption.slice(0, 2000));
|
||||
}
|
||||
if (options.isGroup) args.push("-g");
|
||||
if (options.isGroup) {
|
||||
args.push("-g");
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await runZca(args, { profile });
|
||||
@@ -104,7 +108,9 @@ export async function sendImageZalouser(
|
||||
if (options.caption) {
|
||||
args.push("-m", options.caption.slice(0, 2000));
|
||||
}
|
||||
if (options.isGroup) args.push("-g");
|
||||
if (options.isGroup) {
|
||||
args.push("-g");
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await runZca(args, { profile });
|
||||
@@ -124,7 +130,9 @@ export async function sendLinkZalouser(
|
||||
): Promise<ZalouserSendResult> {
|
||||
const profile = options.profile || process.env.ZCA_PROFILE || "default";
|
||||
const args = ["msg", "link", threadId.trim(), url.trim()];
|
||||
if (options.isGroup) args.push("-g");
|
||||
if (options.isGroup) {
|
||||
args.push("-g");
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await runZca(args, { profile });
|
||||
@@ -140,7 +148,9 @@ export async function sendLinkZalouser(
|
||||
function extractMessageId(stdout: string): string | undefined {
|
||||
// Try to extract message ID from output
|
||||
const match = stdout.match(/message[_\s]?id[:\s]+(\S+)/i);
|
||||
if (match) return match[1];
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
// Return first word if it looks like an ID
|
||||
const firstWord = stdout.trim().split(/\s+/)[0];
|
||||
if (firstWord && /^[a-zA-Z0-9_-]+$/.test(firstWord)) {
|
||||
|
||||
@@ -15,7 +15,9 @@ const asString = (value: unknown): string | undefined =>
|
||||
typeof value === "string" ? value : typeof value === "number" ? String(value) : undefined;
|
||||
|
||||
function readZalouserAccountStatus(value: ChannelAccountSnapshot): ZalouserAccountStatus | null {
|
||||
if (!isRecord(value)) return null;
|
||||
if (!isRecord(value)) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
accountId: value.accountId,
|
||||
enabled: value.enabled,
|
||||
@@ -26,7 +28,9 @@ function readZalouserAccountStatus(value: ChannelAccountSnapshot): ZalouserAccou
|
||||
}
|
||||
|
||||
function isMissingZca(lastError?: string): boolean {
|
||||
if (!lastError) return false;
|
||||
if (!lastError) {
|
||||
return false;
|
||||
}
|
||||
const lower = lastError.toLowerCase();
|
||||
return lower.includes("zca") && (lower.includes("not found") || lower.includes("enoent"));
|
||||
}
|
||||
@@ -37,10 +41,14 @@ export function collectZalouserStatusIssues(
|
||||
const issues: ChannelStatusIssue[] = [];
|
||||
for (const entry of accounts) {
|
||||
const account = readZalouserAccountStatus(entry);
|
||||
if (!account) continue;
|
||||
if (!account) {
|
||||
continue;
|
||||
}
|
||||
const accountId = asString(account.accountId) ?? "default";
|
||||
const enabled = account.enabled !== false;
|
||||
if (!enabled) continue;
|
||||
if (!enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const configured = account.configured === true;
|
||||
const lastError = asString(account.lastError)?.trim();
|
||||
|
||||
@@ -62,7 +62,9 @@ export async function executeZalouserTool(
|
||||
throw new Error("threadId and message required for send action");
|
||||
}
|
||||
const args = ["msg", "send", params.threadId, params.message];
|
||||
if (params.isGroup) args.push("-g");
|
||||
if (params.isGroup) {
|
||||
args.push("-g");
|
||||
}
|
||||
const result = await runZca(args, { profile: params.profile });
|
||||
if (!result.ok) {
|
||||
throw new Error(result.stderr || "Failed to send message");
|
||||
@@ -78,8 +80,12 @@ export async function executeZalouserTool(
|
||||
throw new Error("url required for image action");
|
||||
}
|
||||
const args = ["msg", "image", params.threadId, "-u", params.url];
|
||||
if (params.message) args.push("-m", params.message);
|
||||
if (params.isGroup) args.push("-g");
|
||||
if (params.message) {
|
||||
args.push("-m", params.message);
|
||||
}
|
||||
if (params.isGroup) {
|
||||
args.push("-g");
|
||||
}
|
||||
const result = await runZca(args, { profile: params.profile });
|
||||
if (!result.ok) {
|
||||
throw new Error(result.stderr || "Failed to send image");
|
||||
@@ -92,7 +98,9 @@ export async function executeZalouserTool(
|
||||
throw new Error("threadId and url required for link action");
|
||||
}
|
||||
const args = ["msg", "link", params.threadId, params.url];
|
||||
if (params.isGroup) args.push("-g");
|
||||
if (params.isGroup) {
|
||||
args.push("-g");
|
||||
}
|
||||
const result = await runZca(args, { profile: params.profile });
|
||||
if (!result.ok) {
|
||||
throw new Error(result.stderr || "Failed to send link");
|
||||
@@ -142,10 +150,12 @@ export async function executeZalouserTool(
|
||||
});
|
||||
}
|
||||
|
||||
default:
|
||||
default: {
|
||||
params.action satisfies never;
|
||||
throw new Error(
|
||||
`Unknown action: ${params.action}. Valid actions: send, image, link, friends, groups, me, status`,
|
||||
`Unknown action: ${String(params.action)}. Valid actions: send, image, link, friends, groups, me, status`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
return json({
|
||||
|
||||
@@ -109,6 +109,7 @@ export function runZcaInteractive(args: string[], options?: ZcaRunOptions): Prom
|
||||
}
|
||||
|
||||
function stripAnsi(str: string): string {
|
||||
// oxlint-disable-next-line no-control-regex
|
||||
return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user