openclaw-heychat 2026.2.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +265 -0
- package/index.ts +17 -0
- package/package.json +44 -0
- package/src/accounts.ts +150 -0
- package/src/channel.ts +811 -0
- package/src/config-schema.ts +38 -0
- package/src/policy.ts +99 -0
- package/src/runtime.ts +14 -0
- package/src/types.ts +28 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export { z };
|
|
3
|
+
|
|
4
|
+
const DmPolicySchema = z.enum(["pairing", "open", "allowlist"]);
|
|
5
|
+
const GroupPolicySchema = z.enum(["open", "allowlist", "disabled"]);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Per-account configuration.
|
|
9
|
+
* All fields are optional - missing fields inherit from top-level config.
|
|
10
|
+
*/
|
|
11
|
+
export const HeychatAccountConfigSchema = z
|
|
12
|
+
.object({
|
|
13
|
+
enabled: z.boolean().optional(),
|
|
14
|
+
name: z.string().optional(), // Display name for this account
|
|
15
|
+
token: z.string().optional(),
|
|
16
|
+
tokenFile: z.string().optional(),
|
|
17
|
+
dmPolicy: DmPolicySchema.optional(),
|
|
18
|
+
groupPolicy: GroupPolicySchema.optional(),
|
|
19
|
+
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
20
|
+
groups: z.record(z.string(), z.object({ requireMention: z.boolean().optional() }).optional()).optional(),
|
|
21
|
+
})
|
|
22
|
+
.strict();
|
|
23
|
+
|
|
24
|
+
export const HeychatConfigSchema = z
|
|
25
|
+
.object({
|
|
26
|
+
enabled: z.boolean().optional(),
|
|
27
|
+
// Top-level credentials (backward compatible for single-account mode)
|
|
28
|
+
token: z.string().optional(),
|
|
29
|
+
tokenFile: z.string().optional(),
|
|
30
|
+
name: z.string().optional(),
|
|
31
|
+
dmPolicy: DmPolicySchema.optional().default("pairing"),
|
|
32
|
+
groupPolicy: GroupPolicySchema.optional().default("open"),
|
|
33
|
+
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
34
|
+
groups: z.record(z.string(), z.object({ requireMention: z.boolean().optional() }).optional()).optional(),
|
|
35
|
+
// Multi-account configuration
|
|
36
|
+
accounts: z.record(z.string(), HeychatAccountConfigSchema.optional()).optional(),
|
|
37
|
+
})
|
|
38
|
+
.strict();
|
package/src/policy.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { AllowlistMatch, ChannelGroupContext, GroupToolPolicyConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { HeychatConfig } from "./types.js";
|
|
3
|
+
|
|
4
|
+
export type HeychatAllowlistMatch = AllowlistMatch<"wildcard" | "id">;
|
|
5
|
+
|
|
6
|
+
function normalizeHeychatAllowEntry(raw: string): string {
|
|
7
|
+
const trimmed = raw.trim();
|
|
8
|
+
if (!trimmed) {
|
|
9
|
+
return "";
|
|
10
|
+
}
|
|
11
|
+
if (trimmed === "*") {
|
|
12
|
+
return "*";
|
|
13
|
+
}
|
|
14
|
+
// Remove heychat: or hc: prefix
|
|
15
|
+
const withoutProviderPrefix = trimmed.replace(/^(heychat|hc):/i, "");
|
|
16
|
+
return withoutProviderPrefix.trim().toLowerCase();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function resolveHeychatAllowlistMatch(params: {
|
|
20
|
+
allowFrom: Array<string | number>;
|
|
21
|
+
senderId: string;
|
|
22
|
+
senderIds?: Array<string | null | undefined>;
|
|
23
|
+
senderName?: string | null;
|
|
24
|
+
}): HeychatAllowlistMatch {
|
|
25
|
+
const allowFrom = params.allowFrom
|
|
26
|
+
.map((entry) => normalizeHeychatAllowEntry(String(entry)))
|
|
27
|
+
.filter(Boolean);
|
|
28
|
+
if (allowFrom.length === 0) {
|
|
29
|
+
return { allowed: false };
|
|
30
|
+
}
|
|
31
|
+
if (allowFrom.includes("*")) {
|
|
32
|
+
return { allowed: true, matchKey: "*", matchSource: "wildcard" };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const senderCandidates = [params.senderId, ...(params.senderIds ?? [])]
|
|
36
|
+
.map((entry) => normalizeHeychatAllowEntry(String(entry ?? "")))
|
|
37
|
+
.filter(Boolean);
|
|
38
|
+
|
|
39
|
+
for (const senderId of senderCandidates) {
|
|
40
|
+
if (allowFrom.includes(senderId)) {
|
|
41
|
+
return { allowed: true, matchKey: senderId, matchSource: "id" };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { allowed: false };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function resolveHeychatGroupConfig(params: {
|
|
49
|
+
cfg?: HeychatConfig;
|
|
50
|
+
groupId?: string | null;
|
|
51
|
+
}): Record<string, { requireMention?: boolean }> | undefined {
|
|
52
|
+
const groups = params.cfg?.groups ?? {};
|
|
53
|
+
const groupId = params.groupId?.trim();
|
|
54
|
+
if (!groupId) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const direct = groups[groupId];
|
|
59
|
+
if (direct) {
|
|
60
|
+
return direct;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const lowered = groupId.toLowerCase();
|
|
64
|
+
const matchKey = Object.keys(groups).find((key) => key.toLowerCase() === lowered);
|
|
65
|
+
return matchKey ? groups[matchKey] : undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function resolveHeychatGroupToolPolicy(
|
|
69
|
+
params: ChannelGroupContext,
|
|
70
|
+
): GroupToolPolicyConfig | undefined {
|
|
71
|
+
const cfg = params.cfg.channels?.heychat as HeychatConfig | undefined;
|
|
72
|
+
if (!cfg) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const groupConfig = resolveHeychatGroupConfig({
|
|
77
|
+
cfg,
|
|
78
|
+
groupId: params.groupId,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return undefined; // Heychat doesn't support tool policies yet
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function isHeychatGroupAllowed(params: {
|
|
85
|
+
groupPolicy: "open" | "allowlist" | "disabled";
|
|
86
|
+
allowFrom: Array<string | number>;
|
|
87
|
+
senderId: string;
|
|
88
|
+
senderIds?: Array<string | null | undefined>;
|
|
89
|
+
senderName?: string | null;
|
|
90
|
+
}): boolean {
|
|
91
|
+
const { groupPolicy } = params;
|
|
92
|
+
if (groupPolicy === "disabled") {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (groupPolicy === "open") {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
return resolveHeychatAllowlistMatch(params).allowed;
|
|
99
|
+
}
|
package/src/runtime.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
2
|
+
|
|
3
|
+
let runtime: PluginRuntime | null = null;
|
|
4
|
+
|
|
5
|
+
export function setHeychatRuntime(next: PluginRuntime) {
|
|
6
|
+
runtime = next;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getHeychatRuntime(): PluginRuntime {
|
|
10
|
+
if (!runtime) {
|
|
11
|
+
throw new Error("Heychat runtime not initialized");
|
|
12
|
+
}
|
|
13
|
+
return runtime;
|
|
14
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { BaseProbeResult } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { HeychatConfigSchema, HeychatAccountConfigSchema, z } from "./config-schema.js";
|
|
3
|
+
|
|
4
|
+
export type HeychatConfig = z.infer<typeof HeychatConfigSchema>;
|
|
5
|
+
export type HeychatAccountConfig = z.infer<typeof HeychatAccountConfigSchema>;
|
|
6
|
+
|
|
7
|
+
export type ResolvedHeychatAccount = {
|
|
8
|
+
accountId: string;
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
configured: boolean;
|
|
11
|
+
name?: string;
|
|
12
|
+
token?: string;
|
|
13
|
+
tokenSource: "config" | "file" | "env" | "none";
|
|
14
|
+
/** Merged config (top-level defaults + account-specific overrides) */
|
|
15
|
+
config: {
|
|
16
|
+
dmPolicy?: "pairing" | "open" | "allowlist";
|
|
17
|
+
groupPolicy?: "open" | "allowlist" | "disabled";
|
|
18
|
+
allowFrom?: string[];
|
|
19
|
+
groups?: Record<string, { requireMention?: boolean }>;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type HeychatProbeResult = BaseProbeResult<string> & {
|
|
24
|
+
bot?: {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
};
|
|
28
|
+
};
|