@c4t4/heyamigo 0.10.1 → 0.10.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/README.md +13 -8
- package/config/access.example.json +12 -2
- package/config/config.example.json +16 -0
- package/config/memory-instructions.md +1 -1
- package/config/personalities/casual.md +1 -1
- package/config/personalities/professional.md +1 -1
- package/config/personalities/sharp.md +2 -2
- package/dist/ai/claude.js +1 -0
- package/dist/ai/codex.js +1 -0
- package/dist/ai/grok.js +310 -0
- package/dist/ai/provider.js +5 -5
- package/dist/ai/providers.js +2 -0
- package/dist/ai/sessions.js +5 -1
- package/dist/boot.js +15 -6
- package/dist/channels/index.js +2 -1
- package/dist/channels/runtime.js +1 -0
- package/dist/channels/telegram.js +393 -0
- package/dist/cli/index.js +1 -1
- package/dist/cli/setup.js +168 -70
- package/dist/cli/start.js +25 -4
- package/dist/config.js +34 -1
- package/dist/db/address.js +13 -0
- package/dist/db/identity-sync.js +8 -0
- package/dist/gateway/bootstrap.js +15 -22
- package/dist/gateway/commands.js +13 -15
- package/dist/gateway/incoming.js +107 -254
- package/dist/gateway/ingest.js +240 -0
- package/dist/gateway/outgoing.js +3 -5
- package/dist/gateway/triggers.js +7 -40
- package/dist/memory/digest.js +5 -5
- package/dist/queue/async-tasks.js +11 -4
- package/dist/queue/browser-worker.js +5 -3
- package/dist/queue/cron-dispatch.js +6 -7
- package/dist/queue/job-address.js +4 -0
- package/dist/queue/outbound-postsend.js +4 -7
- package/dist/queue/worker.js +11 -5
- package/dist/wa/whitelist.js +40 -5
- package/package.json +3 -2
package/dist/gateway/triggers.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { jidDecode } from 'baileys';
|
|
2
1
|
import { config } from '../config.js';
|
|
3
2
|
function escapeRegex(s) {
|
|
4
3
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -11,20 +10,8 @@ function aliasMatches(text, aliases) {
|
|
|
11
10
|
}
|
|
12
11
|
return null;
|
|
13
12
|
}
|
|
14
|
-
function ownerNumbers(sock) {
|
|
15
|
-
const out = new Set();
|
|
16
|
-
if (config.owner.number)
|
|
17
|
-
out.add(config.owner.number);
|
|
18
|
-
const pn = sock.user?.id ? jidDecode(sock.user.id)?.user : undefined;
|
|
19
|
-
if (pn)
|
|
20
|
-
out.add(pn);
|
|
21
|
-
const lid = sock.user?.lid ? jidDecode(sock.user.lid)?.user : undefined;
|
|
22
|
-
if (lid)
|
|
23
|
-
out.add(lid);
|
|
24
|
-
return out;
|
|
25
|
-
}
|
|
26
13
|
export function checkTrigger(params) {
|
|
27
|
-
const { isGroup, text
|
|
14
|
+
const { isGroup, text } = params;
|
|
28
15
|
const mode = isGroup
|
|
29
16
|
? config.triggers.groupMode
|
|
30
17
|
: config.triggers.dmMode;
|
|
@@ -43,33 +30,13 @@ export function checkTrigger(params) {
|
|
|
43
30
|
const alias = aliasMatches(text, config.triggers.aliases);
|
|
44
31
|
if (alias)
|
|
45
32
|
return { triggered: true, reason: `alias:${alias}` };
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
(content.videoMessage?.contextInfo) ??
|
|
51
|
-
(content.audioMessage?.contextInfo) ??
|
|
52
|
-
(content.documentMessage?.contextInfo) ??
|
|
53
|
-
(content.documentWithCaptionMessage?.message?.documentMessage?.contextInfo) ??
|
|
54
|
-
(content.stickerMessage?.contextInfo);
|
|
55
|
-
// 2. WA @mention pointing at owner
|
|
56
|
-
const owners = ownerNumbers(sock);
|
|
57
|
-
const mentioned = contextInfo?.mentionedJid ?? [];
|
|
58
|
-
for (const m of mentioned) {
|
|
59
|
-
const user = jidDecode(m)?.user;
|
|
60
|
-
if (user && owners.has(user)) {
|
|
61
|
-
return { triggered: true, reason: 'wa mention' };
|
|
62
|
-
}
|
|
63
|
-
}
|
|
33
|
+
// 2. Channel-provided mention signal, e.g. WhatsApp @mention or
|
|
34
|
+
// Telegram bot username mention.
|
|
35
|
+
if (params.mentionedBot)
|
|
36
|
+
return { triggered: true, reason: 'mention' };
|
|
64
37
|
// 3. Reply to a bot/owner message
|
|
65
|
-
if (config.triggers.replyToBotCounts) {
|
|
66
|
-
|
|
67
|
-
if (quotedParticipant) {
|
|
68
|
-
const user = jidDecode(quotedParticipant)?.user;
|
|
69
|
-
if (user && owners.has(user)) {
|
|
70
|
-
return { triggered: true, reason: 'reply to bot' };
|
|
71
|
-
}
|
|
72
|
-
}
|
|
38
|
+
if (config.triggers.replyToBotCounts && params.replyToBot) {
|
|
39
|
+
return { triggered: true, reason: 'reply to bot' };
|
|
73
40
|
}
|
|
74
41
|
return { triggered: false, reason: 'no trigger match' };
|
|
75
42
|
}
|
package/dist/memory/digest.js
CHANGED
|
@@ -32,8 +32,8 @@ function profilePrompt(params) {
|
|
|
32
32
|
const personMessages = messages.filter((m) => m.senderNumber === number && m.direction === 'in');
|
|
33
33
|
const replyMessages = messages.filter((m) => m.direction === 'out');
|
|
34
34
|
const lines = [
|
|
35
|
-
`You are consolidating the long-term profile for a
|
|
36
|
-
`Contact
|
|
35
|
+
`You are consolidating the long-term profile for a chat contact.`,
|
|
36
|
+
`Contact key: ${number}`,
|
|
37
37
|
``,
|
|
38
38
|
`Current profile (may be empty):`,
|
|
39
39
|
current || '(empty)',
|
|
@@ -51,7 +51,7 @@ function profilePrompt(params) {
|
|
|
51
51
|
: '(none)',
|
|
52
52
|
``,
|
|
53
53
|
`Rewrite the profile in markdown. Structure:`,
|
|
54
|
-
`# <Name if known, else
|
|
54
|
+
`# <Name if known, else contact key>`,
|
|
55
55
|
`## Facts`,
|
|
56
56
|
`## Preferences`,
|
|
57
57
|
`## Patterns`,
|
|
@@ -69,8 +69,8 @@ function profilePrompt(params) {
|
|
|
69
69
|
function briefPrompt(params) {
|
|
70
70
|
const { jid, current, messages, reason } = params;
|
|
71
71
|
const lines = [
|
|
72
|
-
`You are consolidating the long-term brief for a
|
|
73
|
-
`Chat
|
|
72
|
+
`You are consolidating the long-term brief for a chat.`,
|
|
73
|
+
`Chat key: ${jid}`,
|
|
74
74
|
``,
|
|
75
75
|
`Current brief (may be empty):`,
|
|
76
76
|
current || '(empty)',
|
|
@@ -115,6 +115,7 @@ async function executeAsyncTask(task) {
|
|
|
115
115
|
logger.error({ err, id: task.id, jid: task.jid, elapsed: elapsedLog() }, 'async task claude call failed');
|
|
116
116
|
await initiate({
|
|
117
117
|
jid: task.jid,
|
|
118
|
+
address: task.address,
|
|
118
119
|
text: `Heads up: the background task "${truncate(task.description, 80)}" failed. Ask me again and I'll retry.`,
|
|
119
120
|
});
|
|
120
121
|
return;
|
|
@@ -188,7 +189,7 @@ async function executeAsyncTask(task) {
|
|
|
188
189
|
const chatText = clean.trim();
|
|
189
190
|
const anyMarkerFired = appendedCount > 0 || journalCreates.length > 0 || digest !== null;
|
|
190
191
|
if (chatText.length > 0) {
|
|
191
|
-
await initiate({ jid: task.jid, text: chatText });
|
|
192
|
+
await initiate({ jid: task.jid, address: task.address, text: chatText });
|
|
192
193
|
}
|
|
193
194
|
else if (anyMarkerFired) {
|
|
194
195
|
// Worker emitted only markers, no chat text. That's contract-breaking
|
|
@@ -205,6 +206,7 @@ async function executeAsyncTask(task) {
|
|
|
205
206
|
bits.push('digest scheduled');
|
|
206
207
|
await initiate({
|
|
207
208
|
jid: task.jid,
|
|
209
|
+
address: task.address,
|
|
208
210
|
text: `Done. ${bits.join(', ')}.`,
|
|
209
211
|
});
|
|
210
212
|
}
|
|
@@ -257,7 +259,7 @@ export function enqueueBrowserTask(input) {
|
|
|
257
259
|
startedAt: Math.floor(Date.now() / 1000),
|
|
258
260
|
};
|
|
259
261
|
enqueueBrowserJob({
|
|
260
|
-
address: formatAddress(jidToAddress(task.jid)),
|
|
262
|
+
address: task.address ?? formatAddress(jidToAddress(task.jid)),
|
|
261
263
|
description: task.description,
|
|
262
264
|
originatingMessage: task.originatingMessage,
|
|
263
265
|
senderNumber: task.senderNumber,
|
|
@@ -350,6 +352,7 @@ export async function runBrowserTask(task) {
|
|
|
350
352
|
logger.error({ err, id: task.id, jid: task.jid, elapsed: elapsedLog() }, 'browser task provider call failed');
|
|
351
353
|
await initiate({
|
|
352
354
|
jid: task.jid,
|
|
355
|
+
address: task.address,
|
|
353
356
|
text: `Heads up: the browser task "${truncate(task.description, 80)}" failed. Ask me again and I'll retry.`,
|
|
354
357
|
});
|
|
355
358
|
return;
|
|
@@ -409,7 +412,7 @@ export async function runBrowserTask(task) {
|
|
|
409
412
|
}
|
|
410
413
|
const chatText = clean.trim();
|
|
411
414
|
if (chatText.length > 0) {
|
|
412
|
-
await initiate({ jid: task.jid, text: chatText });
|
|
415
|
+
await initiate({ jid: task.jid, address: task.address, text: chatText });
|
|
413
416
|
}
|
|
414
417
|
else if (appendedCount > 0 ||
|
|
415
418
|
journalCreates.length > 0 ||
|
|
@@ -423,7 +426,11 @@ export async function runBrowserTask(task) {
|
|
|
423
426
|
}
|
|
424
427
|
if (digest)
|
|
425
428
|
bits.push('digest scheduled');
|
|
426
|
-
await initiate({
|
|
429
|
+
await initiate({
|
|
430
|
+
jid: task.jid,
|
|
431
|
+
address: task.address,
|
|
432
|
+
text: `Done. ${bits.join(', ')}.`,
|
|
433
|
+
});
|
|
427
434
|
}
|
|
428
435
|
logger.info({
|
|
429
436
|
id: task.id,
|
|
@@ -11,7 +11,7 @@ import { hostname } from 'os';
|
|
|
11
11
|
import { eq } from 'drizzle-orm';
|
|
12
12
|
import { config } from '../config.js';
|
|
13
13
|
import { getDb } from '../db/index.js';
|
|
14
|
-
import {
|
|
14
|
+
import { addressToChatKey } from '../db/address.js';
|
|
15
15
|
import { workers } from '../db/schema.js';
|
|
16
16
|
import { logger } from '../logger.js';
|
|
17
17
|
import { claimNextBrowserTask, markBrowserTaskDone, markBrowserTaskRetryOrDlq, } from './browser-queue.js';
|
|
@@ -82,7 +82,8 @@ function rowToAsyncTask(row) {
|
|
|
82
82
|
}
|
|
83
83
|
return {
|
|
84
84
|
id: `browser-${row.id}`,
|
|
85
|
-
jid:
|
|
85
|
+
jid: addressToChatKey(row.address),
|
|
86
|
+
address: row.address,
|
|
86
87
|
senderNumber: row.senderNumber,
|
|
87
88
|
senderName: row.senderName ?? undefined,
|
|
88
89
|
description: row.description,
|
|
@@ -109,7 +110,8 @@ async function processOne(workerId, row) {
|
|
|
109
110
|
// User-facing failure ack so the chat isn't left hanging.
|
|
110
111
|
try {
|
|
111
112
|
await initiate({
|
|
112
|
-
jid:
|
|
113
|
+
jid: addressToChatKey(row.address),
|
|
114
|
+
address: row.address,
|
|
113
115
|
text: `Heads up: the browser task "${row.description.slice(0, 80)}" failed. Ask me again and I'll retry.`,
|
|
114
116
|
});
|
|
115
117
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// worker can attribute token usage back via addCronUsage(). Lets
|
|
7
7
|
// /crons show running totals per recurring schedule.
|
|
8
8
|
import { logger } from '../logger.js';
|
|
9
|
+
import { addressToChatKey } from '../db/address.js';
|
|
9
10
|
import { enqueueBrowserJob } from './browser-queue.js';
|
|
10
11
|
import { getInternalCronHandler } from './cron-handlers.js';
|
|
11
12
|
import { enqueueInbound } from './inbound.js';
|
|
@@ -43,7 +44,8 @@ export function dispatchCron(row) {
|
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
46
|
enqueueAsyncTask({
|
|
46
|
-
jid: payload.address
|
|
47
|
+
jid: addressToChatKey(payload.address),
|
|
48
|
+
address: payload.address,
|
|
47
49
|
senderNumber: payload.senderNumber,
|
|
48
50
|
description: payload.description,
|
|
49
51
|
originatingMessage: `[cron:${row.name}]`,
|
|
@@ -85,18 +87,15 @@ function dispatchInboundPrompt(row, payload) {
|
|
|
85
87
|
logger.error({ id: row.id, payload }, 'cron prompt payload malformed');
|
|
86
88
|
return;
|
|
87
89
|
}
|
|
88
|
-
|
|
89
|
-
// so we extract the jid form for the synthesized Job.
|
|
90
|
-
// For wa:dm:1234@s.whatsapp.net → jid is the part after the second :
|
|
91
|
-
const jidMatch = /^wa:(?:dm|group):(.+)$/.exec(payload.address);
|
|
92
|
-
const rawJid = jidMatch ? jidMatch[1] : payload.address;
|
|
90
|
+
const chatKey = addressToChatKey(payload.address);
|
|
93
91
|
const now = Math.floor(Date.now() / 1000);
|
|
94
92
|
// Minimal Job — the chat worker calling processJob will recompute
|
|
95
93
|
// memoryPreamble / recentContext via the existing buildMemoryPreamble
|
|
96
94
|
// path. We just provide the user-facing text + the cronId for
|
|
97
95
|
// cost attribution.
|
|
98
96
|
const job = {
|
|
99
|
-
jid:
|
|
97
|
+
jid: chatKey,
|
|
98
|
+
address: payload.address,
|
|
100
99
|
text: payload.prompt,
|
|
101
100
|
input: payload.prompt, // chat worker will wrap with memory preamble
|
|
102
101
|
senderNumber: payload.senderNumber ?? 'system',
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { existsSync, unlinkSync } from 'fs';
|
|
12
12
|
import { isAbsolute, resolve } from 'path';
|
|
13
13
|
import { config } from '../config.js';
|
|
14
|
-
import { addressToExternalId, parseAddress } from '../db/address.js';
|
|
14
|
+
import { addressToChatKey, addressToExternalId, parseAddress } from '../db/address.js';
|
|
15
15
|
import { logger } from '../logger.js';
|
|
16
16
|
import { append } from '../store/messages.js';
|
|
17
17
|
export async function afterSend(row, sentMsgId) {
|
|
@@ -26,12 +26,9 @@ async function persistToMessageLog(row, msgId) {
|
|
|
26
26
|
catch {
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (address.channel !== 'wa')
|
|
33
|
-
return;
|
|
34
|
-
const jid = addressToExternalId(address);
|
|
29
|
+
const jid = address.channel === 'wa'
|
|
30
|
+
? addressToExternalId(address)
|
|
31
|
+
: addressToChatKey(address);
|
|
35
32
|
const messageType = row.kind === 'text' ? 'conversation' : `${row.kind}Message`;
|
|
36
33
|
try {
|
|
37
34
|
await append({
|
package/dist/queue/worker.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getProvider } from '../ai/providers.js';
|
|
2
2
|
import { clearSession, getSessionInfo, setSession, setUsage, } from '../ai/sessions.js';
|
|
3
3
|
import { config } from '../config.js';
|
|
4
|
-
import { formatAddress, jidToAddress } from '../db/address.js';
|
|
5
4
|
import { logger } from '../logger.js';
|
|
6
5
|
import { addDailyTokens } from '../store/usage.js';
|
|
7
6
|
import { getTimezoneForSenderNumber } from '../db/identity-sync.js';
|
|
@@ -13,11 +12,16 @@ import { addCronUsage, enqueueCron } from './crons.js';
|
|
|
13
12
|
import { compressThread, coolThread, createThread, dropThread, resolveThread, touchThread, updateThread, } from './threads.js';
|
|
14
13
|
import { setCategoryWeight } from './thread-weights.js';
|
|
15
14
|
import { enqueueMemoryWrite } from './memory-writes.js';
|
|
15
|
+
import { addressForJob } from './job-address.js';
|
|
16
16
|
import { enqueueOutbound } from './outbound.js';
|
|
17
17
|
import { formatLocalTime, resolveTimeExpression } from './time-expr.js';
|
|
18
18
|
function isStaleSessionError(err) {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
if (!(err instanceof Error))
|
|
20
|
+
return false;
|
|
21
|
+
const msg = err.message.toLowerCase();
|
|
22
|
+
return (msg.includes('no conversation found') ||
|
|
23
|
+
msg.includes('session not found') ||
|
|
24
|
+
msg.includes('no session found'));
|
|
21
25
|
}
|
|
22
26
|
async function callClaude(job) {
|
|
23
27
|
const startedAt = Date.now();
|
|
@@ -205,6 +209,7 @@ async function callClaude(job) {
|
|
|
205
209
|
const t = asyncTasks[i];
|
|
206
210
|
enqueueAsyncTask({
|
|
207
211
|
jid: job.jid,
|
|
212
|
+
address: job.address,
|
|
208
213
|
senderNumber: job.senderNumber,
|
|
209
214
|
description: t.description,
|
|
210
215
|
originatingMessage: job.text,
|
|
@@ -225,6 +230,7 @@ async function callClaude(job) {
|
|
|
225
230
|
const t = asyncBrowserTasks[i];
|
|
226
231
|
enqueueBrowserTask({
|
|
227
232
|
jid: job.jid,
|
|
233
|
+
address: job.address,
|
|
228
234
|
senderNumber: job.senderNumber,
|
|
229
235
|
description: t.description,
|
|
230
236
|
originatingMessage: job.text,
|
|
@@ -259,7 +265,7 @@ async function callClaude(job) {
|
|
|
259
265
|
// originating chat (job.jid) is the destination for both. Sender's
|
|
260
266
|
// timezone drives "9am" / "today at..." resolution so the schedule
|
|
261
267
|
// lands in their wall-clock time, not the server's.
|
|
262
|
-
const chatAddress =
|
|
268
|
+
const chatAddress = addressForJob(job);
|
|
263
269
|
const senderTz = getTimezoneForSenderNumber(job.senderNumber);
|
|
264
270
|
const nowSec = Math.floor(Date.now() / 1000);
|
|
265
271
|
const cronBase = `chat-cron-${job.jid}-${Date.now()}`;
|
|
@@ -450,7 +456,7 @@ async function callClaude(job) {
|
|
|
450
456
|
outputTokens: turnOutput,
|
|
451
457
|
cacheReadTokens: turnCacheRead,
|
|
452
458
|
totalContextTokens,
|
|
453
|
-
contextWindow:
|
|
459
|
+
contextWindow: provider.contextWindow,
|
|
454
460
|
fresh: wasFresh,
|
|
455
461
|
hasDigest: digest !== null,
|
|
456
462
|
journalSlugs: journals.map((j) => j.slug),
|
package/dist/wa/whitelist.js
CHANGED
|
@@ -3,6 +3,7 @@ import { resolve } from 'path';
|
|
|
3
3
|
import { jidDecode } from 'baileys';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { config } from '../config.js';
|
|
6
|
+
import { actorKeyFromAddress, parseAddress } from '../db/address.js';
|
|
6
7
|
import { logger } from '../logger.js';
|
|
7
8
|
const AccessModeSchema = z.enum(['off', 'silent', 'active']);
|
|
8
9
|
const RoleNameSchema = z.enum(['admin', 'user', 'guest']);
|
|
@@ -163,12 +164,21 @@ export function getAccess() {
|
|
|
163
164
|
// consents to the bot nudging them in their own DM. Other DMs and groups
|
|
164
165
|
// require an explicit `proactive: true` entry in access.json.
|
|
165
166
|
export function canSendProactive(jid) {
|
|
166
|
-
|
|
167
|
+
let parsedAddress = null;
|
|
168
|
+
try {
|
|
169
|
+
parsedAddress = parseAddress(jid);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
parsedAddress = null;
|
|
173
|
+
}
|
|
174
|
+
const isGroup = parsedAddress ? parsedAddress.scope === 'group' : jid.endsWith('@g.us');
|
|
167
175
|
if (isGroup) {
|
|
168
176
|
const entry = current.groups.find((g) => g.jid === jid);
|
|
169
177
|
return entry?.proactive === true;
|
|
170
178
|
}
|
|
171
|
-
const number =
|
|
179
|
+
const number = parsedAddress
|
|
180
|
+
? actorKeyFromAddress(parsedAddress)
|
|
181
|
+
: jidDecode(jid)?.user;
|
|
172
182
|
if (!number)
|
|
173
183
|
return false;
|
|
174
184
|
// Owner's self-DM is always allowed.
|
|
@@ -254,10 +264,10 @@ const storeAndRespond = (reason) => ({
|
|
|
254
264
|
reason,
|
|
255
265
|
});
|
|
256
266
|
export function checkAccess(params) {
|
|
257
|
-
const { jid, isGroup, senderNumber, fromMe } = params;
|
|
267
|
+
const { jid, address, isGroup, senderNumber, fromMe } = params;
|
|
258
268
|
const ownerAllowed = fromMe && config.owner.treatAsAllowedEverywhere;
|
|
259
269
|
if (isGroup) {
|
|
260
|
-
const group = current.groups.find((g) => g.jid === jid);
|
|
270
|
+
const group = current.groups.find((g) => g.jid === jid || (address && g.jid === address));
|
|
261
271
|
if (!group)
|
|
262
272
|
return DROP;
|
|
263
273
|
if (group.mode === 'off')
|
|
@@ -273,7 +283,15 @@ export function checkAccess(params) {
|
|
|
273
283
|
}
|
|
274
284
|
return storeOnly('group sender not in allowedSenders');
|
|
275
285
|
}
|
|
276
|
-
|
|
286
|
+
let partnerNumber = jidDecode(jid)?.user ?? '';
|
|
287
|
+
if (!partnerNumber && address) {
|
|
288
|
+
try {
|
|
289
|
+
partnerNumber = actorKeyFromAddress(address);
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
partnerNumber = '';
|
|
293
|
+
}
|
|
294
|
+
}
|
|
277
295
|
// Self-chat: owner messaging themselves — respond like a direct conversation with the bot
|
|
278
296
|
const isSelfChat = fromMe && partnerNumber === config.owner.number;
|
|
279
297
|
if (fromMe && !isSelfChat)
|
|
@@ -286,6 +304,23 @@ export function checkAccess(params) {
|
|
|
286
304
|
return storeOnly('dm silent');
|
|
287
305
|
return storeAndRespond('dm active');
|
|
288
306
|
}
|
|
307
|
+
export async function discoverAddressGroupIfNew(params) {
|
|
308
|
+
const parsed = parseAddress(params.address);
|
|
309
|
+
if (parsed.scope !== 'group')
|
|
310
|
+
return false;
|
|
311
|
+
if (current.groups.some((g) => g.jid === params.address))
|
|
312
|
+
return false;
|
|
313
|
+
const entry = {
|
|
314
|
+
jid: params.address,
|
|
315
|
+
name: params.name || 'Unknown group',
|
|
316
|
+
mode: 'off',
|
|
317
|
+
allowedSenders: params.ownerSender ? [params.ownerSender] : [],
|
|
318
|
+
proactive: false,
|
|
319
|
+
};
|
|
320
|
+
save({ ...current, groups: [...current.groups, entry] });
|
|
321
|
+
logger.info({ address: params.address, name: entry.name }, 'discovered new channel group — added to access.json with mode=off');
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
289
324
|
export async function discoverGroupIfNew(sock, jid) {
|
|
290
325
|
if (!jid.endsWith('@g.us'))
|
|
291
326
|
return false;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c4t4/heyamigo",
|
|
3
|
-
"version": "0.10.
|
|
4
|
-
"description": "WhatsApp AI bot powered by Claude with long-term memory, browser control, and role-based access",
|
|
3
|
+
"version": "0.10.2",
|
|
4
|
+
"description": "WhatsApp and Telegram AI bot powered by Claude, Codex, or Grok with long-term memory, browser control, and role-based access",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"keywords": [
|
|
32
32
|
"whatsapp",
|
|
33
|
+
"telegram",
|
|
33
34
|
"chatbot",
|
|
34
35
|
"claude",
|
|
35
36
|
"ai",
|