@yoooclaw/phone-notifications 1.11.4-beta.1 → 1.11.4-beta.2
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/dist/index.cjs +209 -29
- package/dist/index.cjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5480,7 +5480,7 @@ function readBuildInjectedVersion() {
|
|
|
5480
5480
|
if (false) {
|
|
5481
5481
|
return void 0;
|
|
5482
5482
|
}
|
|
5483
|
-
const version = "1.11.4-beta.
|
|
5483
|
+
const version = "1.11.4-beta.2".trim();
|
|
5484
5484
|
return version || void 0;
|
|
5485
5485
|
}
|
|
5486
5486
|
function readPluginVersionFromPackageJson() {
|
|
@@ -7097,6 +7097,109 @@ function resolveLightTitle(title, reason, segments) {
|
|
|
7097
7097
|
return `Effect: ${modeDesc || "custom"}`;
|
|
7098
7098
|
}
|
|
7099
7099
|
|
|
7100
|
+
// src/light-rules/local-matcher.ts
|
|
7101
|
+
var CONTACT_PREFIXES = ["\u91CD\u8981\u8054\u7CFB\u4EBA", "\u8054\u7CFB\u4EBA"];
|
|
7102
|
+
var CONTACT_ACTION_RE = /(给我)?发(?:来)?消息|(给我)?发信息|来消息|回复(?:我)?|回应(?:我)?|私聊(?:我)?/u;
|
|
7103
|
+
var LEADING_SENDER_RE = /^(.{1,40}?)(?:\s+回应)?\s*[::]/u;
|
|
7104
|
+
function matchNotificationsLocally(notifications, rules) {
|
|
7105
|
+
const parsedRules = rules.map((rule) => parseContactRule(rule)).filter((item) => item !== null);
|
|
7106
|
+
if (parsedRules.length === 0) {
|
|
7107
|
+
return [];
|
|
7108
|
+
}
|
|
7109
|
+
const results = [];
|
|
7110
|
+
notifications.forEach((notification, notificationIndex) => {
|
|
7111
|
+
const senders = collectSenderCandidates(notification);
|
|
7112
|
+
if (senders.length === 0) {
|
|
7113
|
+
return;
|
|
7114
|
+
}
|
|
7115
|
+
parsedRules.forEach((rule) => {
|
|
7116
|
+
if (senders.some((sender) => senderMatchesContact(sender, rule.contactName))) {
|
|
7117
|
+
results.push({ notificationIndex, ruleName: rule.ruleName });
|
|
7118
|
+
}
|
|
7119
|
+
});
|
|
7120
|
+
});
|
|
7121
|
+
return results;
|
|
7122
|
+
}
|
|
7123
|
+
function parseContactRule(rule) {
|
|
7124
|
+
const contactName = extractImportantContact(rule.description);
|
|
7125
|
+
if (!contactName) {
|
|
7126
|
+
return null;
|
|
7127
|
+
}
|
|
7128
|
+
return {
|
|
7129
|
+
ruleName: rule.name,
|
|
7130
|
+
contactName
|
|
7131
|
+
};
|
|
7132
|
+
}
|
|
7133
|
+
function extractImportantContact(description) {
|
|
7134
|
+
const text = description.trim();
|
|
7135
|
+
if (!text) {
|
|
7136
|
+
return null;
|
|
7137
|
+
}
|
|
7138
|
+
for (const prefix of CONTACT_PREFIXES) {
|
|
7139
|
+
const start = text.indexOf(prefix);
|
|
7140
|
+
if (start < 0) {
|
|
7141
|
+
continue;
|
|
7142
|
+
}
|
|
7143
|
+
const tail = text.slice(start + prefix.length);
|
|
7144
|
+
const actionMatch = CONTACT_ACTION_RE.exec(tail);
|
|
7145
|
+
if (!actionMatch || actionMatch.index === 0) {
|
|
7146
|
+
continue;
|
|
7147
|
+
}
|
|
7148
|
+
const rawName = tail.slice(0, actionMatch.index).trim();
|
|
7149
|
+
const cleaned = trimContactName(rawName);
|
|
7150
|
+
if (cleaned) {
|
|
7151
|
+
return cleaned;
|
|
7152
|
+
}
|
|
7153
|
+
}
|
|
7154
|
+
return null;
|
|
7155
|
+
}
|
|
7156
|
+
function trimContactName(value) {
|
|
7157
|
+
const cleaned = value.replace(/^[是为叫做名为\s]+/u, "").replace(/[,。,.;;].*$/u, "").trim();
|
|
7158
|
+
return cleaned || null;
|
|
7159
|
+
}
|
|
7160
|
+
function collectSenderCandidates(notification) {
|
|
7161
|
+
const candidates = [
|
|
7162
|
+
notification.senderName,
|
|
7163
|
+
notification.title,
|
|
7164
|
+
notification.conversationName,
|
|
7165
|
+
extractLeadingSender(notification.content)
|
|
7166
|
+
];
|
|
7167
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
7168
|
+
for (const candidate of candidates) {
|
|
7169
|
+
if (!candidate) {
|
|
7170
|
+
continue;
|
|
7171
|
+
}
|
|
7172
|
+
const normalized = normalizeComparableName(candidate);
|
|
7173
|
+
if (normalized.length < 2) {
|
|
7174
|
+
continue;
|
|
7175
|
+
}
|
|
7176
|
+
deduped.set(normalized, candidate);
|
|
7177
|
+
}
|
|
7178
|
+
return Array.from(deduped.values());
|
|
7179
|
+
}
|
|
7180
|
+
function extractLeadingSender(content) {
|
|
7181
|
+
const text = content.trim();
|
|
7182
|
+
if (!text) {
|
|
7183
|
+
return null;
|
|
7184
|
+
}
|
|
7185
|
+
const match = LEADING_SENDER_RE.exec(text);
|
|
7186
|
+
if (!match) {
|
|
7187
|
+
return null;
|
|
7188
|
+
}
|
|
7189
|
+
return match[1]?.trim() || null;
|
|
7190
|
+
}
|
|
7191
|
+
function senderMatchesContact(sender, contactName) {
|
|
7192
|
+
const normalizedSender = normalizeComparableName(sender);
|
|
7193
|
+
const normalizedContact = normalizeComparableName(contactName);
|
|
7194
|
+
if (!normalizedSender || !normalizedContact) {
|
|
7195
|
+
return false;
|
|
7196
|
+
}
|
|
7197
|
+
return normalizedSender.includes(normalizedContact) || normalizedContact.includes(normalizedSender);
|
|
7198
|
+
}
|
|
7199
|
+
function normalizeComparableName(value) {
|
|
7200
|
+
return value.normalize("NFKC").replace(new RegExp("\\p{Extended_Pictographic}", "gu"), "").replace(/\s+/gu, "").replace(/[^\p{L}\p{N}]/gu, "");
|
|
7201
|
+
}
|
|
7202
|
+
|
|
7100
7203
|
// src/light-rules/inline-evaluator.ts
|
|
7101
7204
|
var InlineLightRuleEvaluator = class {
|
|
7102
7205
|
logger;
|
|
@@ -7116,8 +7219,40 @@ var InlineLightRuleEvaluator = class {
|
|
|
7116
7219
|
if (notifications.length === 0) return true;
|
|
7117
7220
|
const rules = this.registry.getEnabled();
|
|
7118
7221
|
if (rules.length === 0) return true;
|
|
7119
|
-
const
|
|
7120
|
-
|
|
7222
|
+
const localMatches = matchNotificationsLocally(notifications, rules);
|
|
7223
|
+
const localMatchedRuleNames = new Set(localMatches.map((match) => match.ruleName));
|
|
7224
|
+
const llmCandidateRules = rules.filter((rule) => !localMatchedRuleNames.has(rule.name));
|
|
7225
|
+
const combinedMatches = /* @__PURE__ */ new Map();
|
|
7226
|
+
for (const match of localMatches) {
|
|
7227
|
+
combinedMatches.set(
|
|
7228
|
+
`${match.notificationIndex}:${match.ruleName}`,
|
|
7229
|
+
match
|
|
7230
|
+
);
|
|
7231
|
+
}
|
|
7232
|
+
if (localMatches.length > 0) {
|
|
7233
|
+
this.logger.info(
|
|
7234
|
+
`lightrules: local matched ${localMatchedRuleNames.size} rule(s) (notifications=${notifications.length}, remainingRules=${llmCandidateRules.length})`
|
|
7235
|
+
);
|
|
7236
|
+
}
|
|
7237
|
+
if (llmCandidateRules.length > 0) {
|
|
7238
|
+
const llmMatches = await this.invoker.matchNotifications(notifications, llmCandidateRules);
|
|
7239
|
+
if (llmMatches === null) {
|
|
7240
|
+
if (combinedMatches.size === 0) {
|
|
7241
|
+
return false;
|
|
7242
|
+
}
|
|
7243
|
+
this.logger.warn(
|
|
7244
|
+
`lightrules: invoker failed; using ${combinedMatches.size} local fallback match(es)`
|
|
7245
|
+
);
|
|
7246
|
+
} else {
|
|
7247
|
+
for (const match of llmMatches) {
|
|
7248
|
+
combinedMatches.set(
|
|
7249
|
+
`${match.notificationIndex}:${match.ruleName}`,
|
|
7250
|
+
match
|
|
7251
|
+
);
|
|
7252
|
+
}
|
|
7253
|
+
}
|
|
7254
|
+
}
|
|
7255
|
+
const matches = Array.from(combinedMatches.values());
|
|
7121
7256
|
if (matches.length === 0) {
|
|
7122
7257
|
this.logger.info(
|
|
7123
7258
|
`lightrules: 0 matches (notifications=${notifications.length}, rules=${rules.length})`
|
|
@@ -7135,14 +7270,15 @@ var InlineLightRuleEvaluator = class {
|
|
|
7135
7270
|
continue;
|
|
7136
7271
|
}
|
|
7137
7272
|
firedRules.add(match.ruleName);
|
|
7138
|
-
|
|
7273
|
+
const notification = notifications[match.notificationIndex];
|
|
7274
|
+
await this.triggerLight(rule, notification);
|
|
7139
7275
|
}
|
|
7140
7276
|
this.logger.info(
|
|
7141
7277
|
`lightrules: fired ${firedRules.size} rule(s): ${[...firedRules].join(", ")} (from ${matches.length} match(es) across ${notifications.length} notification(s))`
|
|
7142
7278
|
);
|
|
7143
7279
|
return true;
|
|
7144
7280
|
}
|
|
7145
|
-
async triggerLight(rule) {
|
|
7281
|
+
async triggerLight(rule, notification) {
|
|
7146
7282
|
let apiKey;
|
|
7147
7283
|
try {
|
|
7148
7284
|
apiKey = requireApiKey();
|
|
@@ -7152,13 +7288,14 @@ var InlineLightRuleEvaluator = class {
|
|
|
7152
7288
|
);
|
|
7153
7289
|
return;
|
|
7154
7290
|
}
|
|
7291
|
+
const reason = buildTriggerReason(rule, notification);
|
|
7155
7292
|
try {
|
|
7156
7293
|
const result = await sendLightEffect(
|
|
7157
7294
|
apiKey,
|
|
7158
7295
|
rule.segments,
|
|
7159
7296
|
this.logger,
|
|
7160
7297
|
{ repeat_times: rule.repeat_times },
|
|
7161
|
-
|
|
7298
|
+
reason,
|
|
7162
7299
|
rule.title
|
|
7163
7300
|
);
|
|
7164
7301
|
if (!result.ok) {
|
|
@@ -7173,6 +7310,31 @@ var InlineLightRuleEvaluator = class {
|
|
|
7173
7310
|
}
|
|
7174
7311
|
}
|
|
7175
7312
|
};
|
|
7313
|
+
var REASON_CONTENT_MAX = 40;
|
|
7314
|
+
function buildTriggerReason(rule, notification) {
|
|
7315
|
+
const ruleLabel = rule.title?.trim() || rule.name;
|
|
7316
|
+
if (!notification) {
|
|
7317
|
+
return `\u89E6\u53D1"${ruleLabel}"\u706F\u6548`;
|
|
7318
|
+
}
|
|
7319
|
+
const app = notification.appDisplayName?.trim() || notification.appName.trim();
|
|
7320
|
+
const sender = notification.senderName?.trim() || notification.title?.trim();
|
|
7321
|
+
const conversation = notification.conversationName?.trim();
|
|
7322
|
+
const isGroup = notification.conversationType === "group";
|
|
7323
|
+
let where = app;
|
|
7324
|
+
if (isGroup) {
|
|
7325
|
+
where = conversation ? `${app}\u7FA4\u804A"${conversation}"` : `${app}\u7FA4\u804A`;
|
|
7326
|
+
}
|
|
7327
|
+
const action = sender ? `${sender}\u53D1\u6765\u6D88\u606F` : "\u6536\u5230\u65B0\u6D88\u606F";
|
|
7328
|
+
const snippet = summarizeContent(notification.content);
|
|
7329
|
+
const detail = snippet ? `\uFF1A${snippet}` : "";
|
|
7330
|
+
return `\u68C0\u6D4B\u5230${where}\u4E2D${action}${detail}\uFF0C\u89E6\u53D1"${ruleLabel}"\u706F\u6548`;
|
|
7331
|
+
}
|
|
7332
|
+
function summarizeContent(content) {
|
|
7333
|
+
const trimmed = content?.trim();
|
|
7334
|
+
if (!trimmed) return "";
|
|
7335
|
+
if (trimmed.length <= REASON_CONTENT_MAX) return trimmed;
|
|
7336
|
+
return `${trimmed.slice(0, REASON_CONTENT_MAX)}\u2026`;
|
|
7337
|
+
}
|
|
7176
7338
|
|
|
7177
7339
|
// src/light-rules/pi-invoker.ts
|
|
7178
7340
|
var import_agent_runtime = require("openclaw/plugin-sdk/agent-runtime");
|
|
@@ -7180,6 +7342,7 @@ var DEFAULT_PROVIDER = "anthropic";
|
|
|
7180
7342
|
var DEFAULT_MODEL_ID = "claude-haiku-4-5-20251001";
|
|
7181
7343
|
var DEFAULT_AGENT_ID = "main";
|
|
7182
7344
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
7345
|
+
var DEFAULT_MAX_ATTEMPTS = 2;
|
|
7183
7346
|
var MAX_OUTPUT_TOKENS = 512;
|
|
7184
7347
|
var PiAiInvoker = class {
|
|
7185
7348
|
constructor(api, logger, options = {}) {
|
|
@@ -7205,35 +7368,52 @@ var PiAiInvoker = class {
|
|
|
7205
7368
|
}
|
|
7206
7369
|
const systemPrompt = buildSystemPrompt(rules);
|
|
7207
7370
|
const userMessage = buildUserMessage(notifications);
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7371
|
+
const baseTimeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
7372
|
+
const maxAttempts = Math.max(1, this.options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS);
|
|
7373
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
7374
|
+
const timeoutMs = baseTimeoutMs * attempt;
|
|
7375
|
+
try {
|
|
7376
|
+
const resp = await (0, import_agent_runtime.completeWithPreparedSimpleCompletionModel)({
|
|
7377
|
+
model: prepared.model,
|
|
7378
|
+
auth: prepared.auth,
|
|
7379
|
+
context: {
|
|
7380
|
+
systemPrompt,
|
|
7381
|
+
messages: [
|
|
7382
|
+
{ role: "user", content: userMessage, timestamp: Date.now() }
|
|
7383
|
+
]
|
|
7384
|
+
},
|
|
7385
|
+
options: {
|
|
7386
|
+
temperature: 0,
|
|
7387
|
+
maxTokens: MAX_OUTPUT_TOKENS,
|
|
7388
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
7389
|
+
cacheRetention: "short"
|
|
7390
|
+
}
|
|
7391
|
+
});
|
|
7392
|
+
if (resp.stopReason === "error" || resp.stopReason === "aborted") {
|
|
7393
|
+
const retryable = resp.stopReason === "aborted" && attempt < maxAttempts;
|
|
7394
|
+
this.logger.warn(
|
|
7395
|
+
`PiAiInvoker: complete stopped with ${resp.stopReason}: ${resp.errorMessage ?? "n/a"} (attempt ${attempt}/${maxAttempts}, timeoutMs=${timeoutMs}${retryable ? ", retrying" : ""})`
|
|
7396
|
+
);
|
|
7397
|
+
if (retryable) {
|
|
7398
|
+
continue;
|
|
7399
|
+
}
|
|
7400
|
+
return null;
|
|
7223
7401
|
}
|
|
7224
|
-
|
|
7225
|
-
|
|
7402
|
+
const text = extractText(resp.content);
|
|
7403
|
+
return parseMatchResult(text, notifications.length, rules);
|
|
7404
|
+
} catch (err2) {
|
|
7405
|
+
const message = err2?.message ?? String(err2);
|
|
7406
|
+
const retryable = attempt < maxAttempts && (err2?.name === "AbortError" || /\babort(?:ed)?\b/i.test(message));
|
|
7226
7407
|
this.logger.warn(
|
|
7227
|
-
`PiAiInvoker: complete
|
|
7408
|
+
`PiAiInvoker: complete failed: ${message} (attempt ${attempt}/${maxAttempts}${retryable ? ", retrying" : ""})`
|
|
7228
7409
|
);
|
|
7410
|
+
if (retryable) {
|
|
7411
|
+
continue;
|
|
7412
|
+
}
|
|
7229
7413
|
return null;
|
|
7230
7414
|
}
|
|
7231
|
-
const text = extractText(resp.content);
|
|
7232
|
-
return parseMatchResult(text, notifications.length, rules);
|
|
7233
|
-
} catch (err2) {
|
|
7234
|
-
this.logger.warn(`PiAiInvoker: complete failed: ${err2?.message ?? err2}`);
|
|
7235
|
-
return null;
|
|
7236
7415
|
}
|
|
7416
|
+
return null;
|
|
7237
7417
|
}
|
|
7238
7418
|
async prepareCompletionModel() {
|
|
7239
7419
|
if (this.options.provider || this.options.modelId) {
|