feat: unify main session and icon cues
This commit is contained in:
@@ -6,8 +6,8 @@ import { loadConfig, type WarelayConfig } from "../config/config.js";
|
||||
import {
|
||||
DEFAULT_IDLE_MINUTES,
|
||||
DEFAULT_RESET_TRIGGER,
|
||||
deriveSessionKey,
|
||||
loadSessionStore,
|
||||
resolveSessionKey,
|
||||
resolveStorePath,
|
||||
type SessionEntry,
|
||||
saveSessionStore,
|
||||
@@ -210,6 +210,7 @@ export async function getReplyFromConfig(
|
||||
|
||||
// Optional session handling (conversation reuse + /new resets)
|
||||
const sessionCfg = reply?.session;
|
||||
const mainKey = sessionCfg?.mainKey ?? "main";
|
||||
const resetTriggers = sessionCfg?.resetTriggers?.length
|
||||
? sessionCfg.resetTriggers
|
||||
: [DEFAULT_RESET_TRIGGER];
|
||||
@@ -261,7 +262,7 @@ export async function getReplyFromConfig(
|
||||
}
|
||||
}
|
||||
|
||||
sessionKey = deriveSessionKey(sessionScope, ctx);
|
||||
sessionKey = resolveSessionKey(sessionScope, ctx, mainKey);
|
||||
sessionStore = loadSessionStore(storePath);
|
||||
const entry = sessionStore[sessionKey];
|
||||
const idleMs = idleMinutes * 60_000;
|
||||
|
||||
@@ -12,6 +12,7 @@ export type MsgContext = {
|
||||
GroupMembers?: string;
|
||||
SenderName?: string;
|
||||
SenderE164?: string;
|
||||
Surface?: string;
|
||||
};
|
||||
|
||||
export type TemplateContext = MsgContext & {
|
||||
|
||||
@@ -15,8 +15,8 @@ import { type CliDeps, createDefaultDeps } from "../cli/deps.js";
|
||||
import { loadConfig, type WarelayConfig } from "../config/config.js";
|
||||
import {
|
||||
DEFAULT_IDLE_MINUTES,
|
||||
deriveSessionKey,
|
||||
loadSessionStore,
|
||||
resolveSessionKey,
|
||||
resolveStorePath,
|
||||
type SessionEntry,
|
||||
saveSessionStore,
|
||||
@@ -67,6 +67,7 @@ function resolveSession(opts: {
|
||||
}): SessionResolution {
|
||||
const sessionCfg = opts.replyCfg?.session;
|
||||
const scope = sessionCfg?.scope ?? "per-sender";
|
||||
const mainKey = sessionCfg?.mainKey ?? "main";
|
||||
const idleMinutes = Math.max(
|
||||
sessionCfg?.idleMinutes ?? DEFAULT_IDLE_MINUTES,
|
||||
1,
|
||||
@@ -78,7 +79,7 @@ function resolveSession(opts: {
|
||||
|
||||
let sessionKey: string | undefined =
|
||||
sessionStore && opts.to
|
||||
? deriveSessionKey(scope, { From: opts.to } as MsgContext)
|
||||
? resolveSessionKey(scope, { From: opts.to } as MsgContext, mainKey)
|
||||
: undefined;
|
||||
let sessionEntry =
|
||||
sessionKey && sessionStore ? sessionStore[sessionKey] : undefined;
|
||||
|
||||
@@ -23,6 +23,7 @@ export type SessionConfig = {
|
||||
sessionIntro?: string;
|
||||
typingIntervalSeconds?: number;
|
||||
heartbeatMinutes?: number;
|
||||
mainKey?: string;
|
||||
};
|
||||
|
||||
export type LoggingConfig = {
|
||||
@@ -135,6 +136,7 @@ const ReplySchema = z
|
||||
sendSystemOnce: z.boolean().optional(),
|
||||
sessionIntro: z.string().optional(),
|
||||
typingIntervalSeconds: z.number().int().positive().optional(),
|
||||
mainKey: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
heartbeatMinutes: z.number().int().nonnegative().optional(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { deriveSessionKey } from "./sessions.js";
|
||||
import { deriveSessionKey, resolveSessionKey } from "./sessions.js";
|
||||
|
||||
describe("sessions", () => {
|
||||
it("returns normalized per-sender key", () => {
|
||||
@@ -22,4 +22,16 @@ describe("sessions", () => {
|
||||
"group:12345-678@g.us",
|
||||
);
|
||||
});
|
||||
|
||||
it("maps direct chats to main key when provided", () => {
|
||||
expect(
|
||||
resolveSessionKey("per-sender", { From: "whatsapp:+1555" }, "main"),
|
||||
).toBe("main");
|
||||
});
|
||||
|
||||
it("leaves groups untouched even with main key", () => {
|
||||
expect(
|
||||
resolveSessionKey("per-sender", { From: "12345-678@g.us" }, "main"),
|
||||
).toBe("group:12345-678@g.us");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -77,3 +77,20 @@ export function deriveSessionKey(scope: SessionScope, ctx: MsgContext) {
|
||||
}
|
||||
return from || "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the session key with an optional canonical direct-chat key (e.g., "main").
|
||||
* All non-group direct chats collapse to `mainKey` when provided, keeping group isolation.
|
||||
*/
|
||||
export function resolveSessionKey(
|
||||
scope: SessionScope,
|
||||
ctx: MsgContext,
|
||||
mainKey?: string,
|
||||
) {
|
||||
const raw = deriveSessionKey(scope, ctx);
|
||||
if (scope === "global") return raw;
|
||||
const canonical = (mainKey ?? "").trim();
|
||||
const isGroup = raw.startsWith("group:") || raw.includes("@g.us");
|
||||
if (!isGroup && canonical) return canonical;
|
||||
return raw;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { loadConfig } from "./config/config.js";
|
||||
import {
|
||||
deriveSessionKey,
|
||||
loadSessionStore,
|
||||
resolveSessionKey,
|
||||
resolveStorePath,
|
||||
saveSessionStore,
|
||||
} from "./config/sessions.js";
|
||||
@@ -52,6 +53,7 @@ export {
|
||||
normalizeE164,
|
||||
PortInUseError,
|
||||
promptYesNo,
|
||||
resolveSessionKey,
|
||||
resolveStorePath,
|
||||
runCommandWithTimeout,
|
||||
runExec,
|
||||
|
||||
@@ -5,8 +5,8 @@ import { waitForever } from "../cli/wait.js";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import {
|
||||
DEFAULT_IDLE_MINUTES,
|
||||
deriveSessionKey,
|
||||
loadSessionStore,
|
||||
resolveSessionKey,
|
||||
resolveStorePath,
|
||||
saveSessionStore,
|
||||
} from "../config/sessions.js";
|
||||
@@ -213,11 +213,15 @@ export async function runWebHeartbeatOnce(opts: {
|
||||
});
|
||||
|
||||
const cfg = cfgOverride ?? loadConfig();
|
||||
const sessionCfg = cfg.inbound?.reply?.session;
|
||||
const mainKey = sessionCfg?.mainKey ?? "main";
|
||||
const sessionScope = sessionCfg?.scope ?? "per-sender";
|
||||
const sessionKey = resolveSessionKey(sessionScope, { From: to }, mainKey);
|
||||
if (sessionId) {
|
||||
const storePath = resolveStorePath(cfg.inbound?.reply?.session?.store);
|
||||
const store = loadSessionStore(storePath);
|
||||
store[to] = {
|
||||
...(store[to] ?? {}),
|
||||
store[sessionKey] = {
|
||||
...(store[sessionKey] ?? {}),
|
||||
sessionId,
|
||||
updatedAt: Date.now(),
|
||||
};
|
||||
@@ -432,7 +436,11 @@ function getSessionSnapshot(
|
||||
) {
|
||||
const sessionCfg = cfg.inbound?.reply?.session;
|
||||
const scope = sessionCfg?.scope ?? "per-sender";
|
||||
const key = deriveSessionKey(scope, { From: from, To: "", Body: "" });
|
||||
const key = resolveSessionKey(
|
||||
scope,
|
||||
{ From: from, To: "", Body: "" },
|
||||
sessionCfg?.mainKey ?? "main",
|
||||
);
|
||||
const store = loadSessionStore(resolveStorePath(sessionCfg?.store));
|
||||
const entry = store[key];
|
||||
const idleMinutes = Math.max(
|
||||
@@ -790,6 +798,7 @@ export async function monitorWebProvider(
|
||||
GroupMembers: latest.groupParticipants?.join(", "),
|
||||
SenderName: latest.senderName,
|
||||
SenderE164: latest.senderE164,
|
||||
Surface: "whatsapp",
|
||||
},
|
||||
{
|
||||
onReplyStart: latest.sendComposing,
|
||||
|
||||
Reference in New Issue
Block a user