@jackle.dev/zalox 1.0.1
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/LICENSE +21 -0
- package/README.md +185 -0
- package/dist/cli/commands/admin.d.ts +17 -0
- package/dist/cli/commands/admin.d.ts.map +1 -0
- package/dist/cli/commands/admin.js +359 -0
- package/dist/cli/commands/admin.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +3 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +88 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/autoreply.d.ts +12 -0
- package/dist/cli/commands/autoreply.d.ts.map +1 -0
- package/dist/cli/commands/autoreply.js +162 -0
- package/dist/cli/commands/autoreply.js.map +1 -0
- package/dist/cli/commands/bulk.d.ts +9 -0
- package/dist/cli/commands/bulk.d.ts.map +1 -0
- package/dist/cli/commands/bulk.js +169 -0
- package/dist/cli/commands/bulk.js.map +1 -0
- package/dist/cli/commands/config.d.ts +14 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +122 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/conv.d.ts +3 -0
- package/dist/cli/commands/conv.d.ts.map +1 -0
- package/dist/cli/commands/conv.js +229 -0
- package/dist/cli/commands/conv.js.map +1 -0
- package/dist/cli/commands/daemon.d.ts +13 -0
- package/dist/cli/commands/daemon.d.ts.map +1 -0
- package/dist/cli/commands/daemon.js +102 -0
- package/dist/cli/commands/daemon.js.map +1 -0
- package/dist/cli/commands/export.d.ts +10 -0
- package/dist/cli/commands/export.d.ts.map +1 -0
- package/dist/cli/commands/export.js +98 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/friend.d.ts +13 -0
- package/dist/cli/commands/friend.d.ts.map +1 -0
- package/dist/cli/commands/friend.js +337 -0
- package/dist/cli/commands/friend.js.map +1 -0
- package/dist/cli/commands/group-settings.d.ts +11 -0
- package/dist/cli/commands/group-settings.d.ts.map +1 -0
- package/dist/cli/commands/group-settings.js +154 -0
- package/dist/cli/commands/group-settings.js.map +1 -0
- package/dist/cli/commands/group.d.ts +14 -0
- package/dist/cli/commands/group.d.ts.map +1 -0
- package/dist/cli/commands/group.js +365 -0
- package/dist/cli/commands/group.js.map +1 -0
- package/dist/cli/commands/license.d.ts +13 -0
- package/dist/cli/commands/license.d.ts.map +1 -0
- package/dist/cli/commands/license.js +218 -0
- package/dist/cli/commands/license.js.map +1 -0
- package/dist/cli/commands/listen.d.ts +3 -0
- package/dist/cli/commands/listen.d.ts.map +1 -0
- package/dist/cli/commands/listen.js +177 -0
- package/dist/cli/commands/listen.js.map +1 -0
- package/dist/cli/commands/me.d.ts +9 -0
- package/dist/cli/commands/me.d.ts.map +1 -0
- package/dist/cli/commands/me.js +135 -0
- package/dist/cli/commands/me.js.map +1 -0
- package/dist/cli/commands/msg.d.ts +11 -0
- package/dist/cli/commands/msg.d.ts.map +1 -0
- package/dist/cli/commands/msg.js +255 -0
- package/dist/cli/commands/msg.js.map +1 -0
- package/dist/cli/commands/profile.d.ts +3 -0
- package/dist/cli/commands/profile.d.ts.map +1 -0
- package/dist/cli/commands/profile.js +50 -0
- package/dist/cli/commands/profile.js.map +1 -0
- package/dist/cli/commands/schedule.d.ts +12 -0
- package/dist/cli/commands/schedule.d.ts.map +1 -0
- package/dist/cli/commands/schedule.js +175 -0
- package/dist/cli/commands/schedule.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +3 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +87 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/template.d.ts +12 -0
- package/dist/cli/commands/template.d.ts.map +1 -0
- package/dist/cli/commands/template.js +163 -0
- package/dist/cli/commands/template.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +116 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output.d.ts +63 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +144 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/core/autoreply.d.ts +57 -0
- package/dist/core/autoreply.d.ts.map +1 -0
- package/dist/core/autoreply.js +159 -0
- package/dist/core/autoreply.js.map +1 -0
- package/dist/core/bulk.d.ts +36 -0
- package/dist/core/bulk.d.ts.map +1 -0
- package/dist/core/bulk.js +117 -0
- package/dist/core/bulk.js.map +1 -0
- package/dist/core/client.d.ts +8 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/client.js +91 -0
- package/dist/core/client.js.map +1 -0
- package/dist/core/daemon.d.ts +30 -0
- package/dist/core/daemon.d.ts.map +1 -0
- package/dist/core/daemon.js +213 -0
- package/dist/core/daemon.js.map +1 -0
- package/dist/core/dm-commands.d.ts +41 -0
- package/dist/core/dm-commands.d.ts.map +1 -0
- package/dist/core/dm-commands.js +313 -0
- package/dist/core/dm-commands.js.map +1 -0
- package/dist/core/export.d.ts +20 -0
- package/dist/core/export.d.ts.map +1 -0
- package/dist/core/export.js +92 -0
- package/dist/core/export.js.map +1 -0
- package/dist/core/gate.d.ts +39 -0
- package/dist/core/gate.d.ts.map +1 -0
- package/dist/core/gate.js +75 -0
- package/dist/core/gate.js.map +1 -0
- package/dist/core/group-settings.d.ts +35 -0
- package/dist/core/group-settings.d.ts.map +1 -0
- package/dist/core/group-settings.js +70 -0
- package/dist/core/group-settings.js.map +1 -0
- package/dist/core/index.d.ts +22 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +12 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/license.d.ts +103 -0
- package/dist/core/license.d.ts.map +1 -0
- package/dist/core/license.js +444 -0
- package/dist/core/license.js.map +1 -0
- package/dist/core/scheduler.d.ts +45 -0
- package/dist/core/scheduler.d.ts.map +1 -0
- package/dist/core/scheduler.js +203 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/templates.d.ts +35 -0
- package/dist/core/templates.d.ts.map +1 -0
- package/dist/core/templates.js +107 -0
- package/dist/core/templates.js.map +1 -0
- package/dist/core/types.d.ts +57 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +10 -0
- package/dist/core/types.js.map +1 -0
- package/dist/server/api.d.ts +2 -0
- package/dist/server/api.d.ts.map +1 -0
- package/dist/server/api.js +79 -0
- package/dist/server/api.js.map +1 -0
- package/dist/server/index.d.ts +16 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +48 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/license-api.d.ts +55 -0
- package/dist/server/license-api.d.ts.map +1 -0
- package/dist/server/license-api.js +496 -0
- package/dist/server/license-api.js.map +1 -0
- package/dist/server/middleware.d.ts +18 -0
- package/dist/server/middleware.d.ts.map +1 -0
- package/dist/server/middleware.js +92 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/webhook.d.ts +10 -0
- package/dist/server/webhook.d.ts.map +1 -0
- package/dist/server/webhook.js +53 -0
- package/dist/server/webhook.js.map +1 -0
- package/dist/server/ws.d.ts +66 -0
- package/dist/server/ws.d.ts.map +1 -0
- package/dist/server/ws.js +203 -0
- package/dist/server/ws.js.map +1 -0
- package/dist/utils/cache.d.ts +35 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +78 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/config.d.ts +16 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +88 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +51 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZaloX — Bulk Send System
|
|
3
|
+
*
|
|
4
|
+
* Multi-recipient message sending with rate limiting per license tier.
|
|
5
|
+
* Daily usage tracked in ~/.openclaw/zalox/bulk-usage.json
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
import { ThreadType } from 'zca-js';
|
|
10
|
+
import { getConfigDir, ensureConfigDir } from '../utils/config.js';
|
|
11
|
+
import { validateLicense } from './license.js';
|
|
12
|
+
import { getTemplate, renderTemplate } from './templates.js';
|
|
13
|
+
// ── Usage Tracking ───────────────────────────────────────
|
|
14
|
+
function getUsagePath() {
|
|
15
|
+
return join(getConfigDir(), 'bulk-usage.json');
|
|
16
|
+
}
|
|
17
|
+
function getTodayStr() {
|
|
18
|
+
return new Date().toISOString().split('T')[0];
|
|
19
|
+
}
|
|
20
|
+
export function getBulkUsage() {
|
|
21
|
+
const path = getUsagePath();
|
|
22
|
+
if (!existsSync(path)) {
|
|
23
|
+
return { date: getTodayStr(), count: 0 };
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const usage = JSON.parse(readFileSync(path, 'utf-8'));
|
|
27
|
+
// Reset if different day
|
|
28
|
+
if (usage.date !== getTodayStr()) {
|
|
29
|
+
return { date: getTodayStr(), count: 0 };
|
|
30
|
+
}
|
|
31
|
+
return usage;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return { date: getTodayStr(), count: 0 };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function saveUsage(usage) {
|
|
38
|
+
ensureConfigDir();
|
|
39
|
+
writeFileSync(getUsagePath(), JSON.stringify(usage, null, 2));
|
|
40
|
+
}
|
|
41
|
+
function incrementUsage(count) {
|
|
42
|
+
const usage = getBulkUsage();
|
|
43
|
+
usage.count += count;
|
|
44
|
+
usage.date = getTodayStr();
|
|
45
|
+
saveUsage(usage);
|
|
46
|
+
return usage;
|
|
47
|
+
}
|
|
48
|
+
// ── Core Functions ───────────────────────────────────────
|
|
49
|
+
/**
|
|
50
|
+
* Send a message to multiple recipients with rate limiting.
|
|
51
|
+
*/
|
|
52
|
+
export async function bulkSend(api, options, onProgress) {
|
|
53
|
+
const { threadIds, isGroup, delayMs = 1000 } = options;
|
|
54
|
+
const licenseStatus = validateLicense();
|
|
55
|
+
const limit = licenseStatus.features.bulkSendLimit;
|
|
56
|
+
// Check daily limit
|
|
57
|
+
const usage = getBulkUsage();
|
|
58
|
+
const remaining = limit === -1 ? Infinity : limit - usage.count;
|
|
59
|
+
if (remaining <= 0) {
|
|
60
|
+
return {
|
|
61
|
+
total: threadIds.length,
|
|
62
|
+
sent: 0,
|
|
63
|
+
failed: threadIds.length,
|
|
64
|
+
results: threadIds.map((id) => ({
|
|
65
|
+
threadId: id,
|
|
66
|
+
ok: false,
|
|
67
|
+
error: `Daily bulk send limit reached (${limit}/day)`,
|
|
68
|
+
})),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Resolve message content
|
|
72
|
+
let text = options.message;
|
|
73
|
+
if (options.templateId) {
|
|
74
|
+
const tmpl = getTemplate(options.templateId);
|
|
75
|
+
if (!tmpl) {
|
|
76
|
+
throw new Error(`Template "${options.templateId}" not found`);
|
|
77
|
+
}
|
|
78
|
+
text = renderTemplate(tmpl, options.templateVars || {});
|
|
79
|
+
}
|
|
80
|
+
const type = isGroup ? ThreadType.Group : ThreadType.User;
|
|
81
|
+
const toSend = threadIds.slice(0, remaining === Infinity ? undefined : remaining);
|
|
82
|
+
const results = [];
|
|
83
|
+
let sent = 0;
|
|
84
|
+
for (let i = 0; i < toSend.length; i++) {
|
|
85
|
+
const threadId = toSend[i];
|
|
86
|
+
try {
|
|
87
|
+
await api.sendMessage(text, threadId, type);
|
|
88
|
+
results.push({ threadId, ok: true });
|
|
89
|
+
sent++;
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
results.push({ threadId, ok: false, error: err.message || String(err) });
|
|
93
|
+
}
|
|
94
|
+
onProgress?.(i + 1, toSend.length);
|
|
95
|
+
// Delay between sends (except the last one)
|
|
96
|
+
if (i < toSend.length - 1 && delayMs > 0) {
|
|
97
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Track usage
|
|
101
|
+
incrementUsage(sent);
|
|
102
|
+
// Add skipped entries if we hit the limit
|
|
103
|
+
for (let i = toSend.length; i < threadIds.length; i++) {
|
|
104
|
+
results.push({
|
|
105
|
+
threadId: threadIds[i],
|
|
106
|
+
ok: false,
|
|
107
|
+
error: 'Skipped — daily limit reached',
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
total: threadIds.length,
|
|
112
|
+
sent,
|
|
113
|
+
failed: threadIds.length - sent,
|
|
114
|
+
results,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=bulk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulk.js","sourceRoot":"","sources":["../../src/core/bulk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type API, type Credentials } from 'zca-js';
|
|
2
|
+
export declare function getApi(profileName?: string): Promise<API>;
|
|
3
|
+
export declare function login(credentials: Credentials): Promise<API>;
|
|
4
|
+
export declare function loginQR(profileName?: string): Promise<API>;
|
|
5
|
+
export declare function saveProfileCredentials(profileName: string, credentials: any): void;
|
|
6
|
+
export declare function getApiInstance(): API | null;
|
|
7
|
+
export declare function clearApiInstance(): void;
|
|
8
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,GAAG,EAAE,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;AAS1D,wBAAsB,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAoB/D;AAED,wBAAsB,KAAK,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CASlE;AAED,wBAAsB,OAAO,CAAC,WAAW,GAAE,MAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,CA0B3E;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,QAS3E;AAeD,wBAAgB,cAAc,IAAI,GAAG,GAAG,IAAI,CAE3C;AAED,wBAAgB,gBAAgB,SAE/B"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Zalo } from 'zca-js';
|
|
2
|
+
import { getConfig, saveConfig, getProfilePath, ensureConfigDir } from '../utils/config.js';
|
|
3
|
+
import { logger } from '../utils/logger.js';
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { homedir } from 'os';
|
|
7
|
+
let apiInstance = null;
|
|
8
|
+
export async function getApi(profileName) {
|
|
9
|
+
if (apiInstance)
|
|
10
|
+
return apiInstance;
|
|
11
|
+
const config = getConfig();
|
|
12
|
+
const profile = profileName || config.activeProfile;
|
|
13
|
+
const profilePath = getProfilePath(profile);
|
|
14
|
+
if (!existsSync(profilePath)) {
|
|
15
|
+
// Try migrating from zca-cli config
|
|
16
|
+
const zcaCredentials = getZcaCredentials();
|
|
17
|
+
if (zcaCredentials) {
|
|
18
|
+
logger.info('Found zca-cli credentials, migrating...');
|
|
19
|
+
saveProfileCredentials(profile, zcaCredentials);
|
|
20
|
+
return login(zcaCredentials);
|
|
21
|
+
}
|
|
22
|
+
throw new Error('Not logged in. Run "zalox auth login" first.');
|
|
23
|
+
}
|
|
24
|
+
const credentials = JSON.parse(readFileSync(profilePath, 'utf-8'));
|
|
25
|
+
return login(credentials);
|
|
26
|
+
}
|
|
27
|
+
export async function login(credentials) {
|
|
28
|
+
const zalo = new Zalo();
|
|
29
|
+
try {
|
|
30
|
+
apiInstance = await zalo.login(credentials);
|
|
31
|
+
logger.success('Logged in successfully');
|
|
32
|
+
return apiInstance;
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
throw new Error(`Login failed: ${err.message}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function loginQR(profileName = 'default') {
|
|
39
|
+
const zalo = new Zalo();
|
|
40
|
+
logger.info('Generating QR code...');
|
|
41
|
+
apiInstance = await zalo.loginQR({}, (qrData) => {
|
|
42
|
+
if (typeof qrData === 'string') {
|
|
43
|
+
logger.info(`QR URL: ${qrData}`);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
// Save credentials for next time
|
|
47
|
+
try {
|
|
48
|
+
const cookie = await apiInstance.getCookie();
|
|
49
|
+
const context = await apiInstance.getContext();
|
|
50
|
+
const creds = {
|
|
51
|
+
cookie: cookie,
|
|
52
|
+
imei: context.imei || '',
|
|
53
|
+
userAgent: context.userAgent || '',
|
|
54
|
+
};
|
|
55
|
+
saveProfileCredentials(profileName, creds);
|
|
56
|
+
logger.success('Logged in and credentials saved');
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
logger.warn('Logged in but could not save credentials');
|
|
60
|
+
}
|
|
61
|
+
return apiInstance;
|
|
62
|
+
}
|
|
63
|
+
export function saveProfileCredentials(profileName, credentials) {
|
|
64
|
+
ensureConfigDir();
|
|
65
|
+
const profilePath = getProfilePath(profileName);
|
|
66
|
+
writeFileSync(profilePath, JSON.stringify(credentials, null, 2));
|
|
67
|
+
// Register in config
|
|
68
|
+
const config = getConfig();
|
|
69
|
+
config.profiles[profileName] = { name: profileName, lastLogin: new Date().toISOString() };
|
|
70
|
+
saveConfig(config);
|
|
71
|
+
}
|
|
72
|
+
function getZcaCredentials() {
|
|
73
|
+
const zcaConfigPath = join(homedir(), '.config', 'zca-cli-nodejs', 'config.json');
|
|
74
|
+
if (existsSync(zcaConfigPath)) {
|
|
75
|
+
try {
|
|
76
|
+
const zcaConfig = JSON.parse(readFileSync(zcaConfigPath, 'utf-8'));
|
|
77
|
+
return zcaConfig?.profiles?.default?.credentials || null;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
export function getApiInstance() {
|
|
86
|
+
return apiInstance;
|
|
87
|
+
}
|
|
88
|
+
export function clearApiInstance() {
|
|
89
|
+
apiInstance = null;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZaloX — Daemon
|
|
3
|
+
*
|
|
4
|
+
* All-in-one background service:
|
|
5
|
+
* - Message listener (receive messages)
|
|
6
|
+
* - Auto-reply engine (rule-based responses)
|
|
7
|
+
* - Scheduler (timed messages)
|
|
8
|
+
* - Group settings enforcement
|
|
9
|
+
*
|
|
10
|
+
* Runs standalone — no AI agent required.
|
|
11
|
+
*
|
|
12
|
+
* Usage: zalox daemon [--port 3456]
|
|
13
|
+
*/
|
|
14
|
+
export interface DaemonOptions {
|
|
15
|
+
port?: number;
|
|
16
|
+
autoReply?: boolean;
|
|
17
|
+
scheduler?: boolean;
|
|
18
|
+
verbose?: boolean;
|
|
19
|
+
ownerId?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface DaemonStats {
|
|
22
|
+
startedAt: string;
|
|
23
|
+
messagesReceived: number;
|
|
24
|
+
autoRepliesSent: number;
|
|
25
|
+
scheduledSent: number;
|
|
26
|
+
errors: number;
|
|
27
|
+
}
|
|
28
|
+
export declare function getDaemonStats(): DaemonStats;
|
|
29
|
+
export declare function startDaemon(opts?: DaemonOptions): Promise<void>;
|
|
30
|
+
//# sourceMappingURL=daemon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/core/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAUD,wBAAgB,cAAc,IAAI,WAAW,CAE5C;AAED,wBAAsB,WAAW,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqMzE"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZaloX — Daemon
|
|
3
|
+
*
|
|
4
|
+
* All-in-one background service:
|
|
5
|
+
* - Message listener (receive messages)
|
|
6
|
+
* - Auto-reply engine (rule-based responses)
|
|
7
|
+
* - Scheduler (timed messages)
|
|
8
|
+
* - Group settings enforcement
|
|
9
|
+
*
|
|
10
|
+
* Runs standalone — no AI agent required.
|
|
11
|
+
*
|
|
12
|
+
* Usage: zalox daemon [--port 3456]
|
|
13
|
+
*/
|
|
14
|
+
import { getApi } from './client.js';
|
|
15
|
+
import { matchRules, renderResponse } from './autoreply.js';
|
|
16
|
+
import { runScheduler } from './scheduler.js';
|
|
17
|
+
import { getGroupSettings } from './group-settings.js';
|
|
18
|
+
import { isFeatureEnabled } from './gate.js';
|
|
19
|
+
import { handleDmCommand } from './dm-commands.js';
|
|
20
|
+
import { logger } from '../utils/logger.js';
|
|
21
|
+
import { writeCache, CACHE_KEYS } from '../utils/cache.js';
|
|
22
|
+
import { getConfig } from '../utils/config.js';
|
|
23
|
+
import { ThreadType } from 'zca-js';
|
|
24
|
+
const stats = {
|
|
25
|
+
startedAt: new Date().toISOString(),
|
|
26
|
+
messagesReceived: 0,
|
|
27
|
+
autoRepliesSent: 0,
|
|
28
|
+
scheduledSent: 0,
|
|
29
|
+
errors: 0,
|
|
30
|
+
};
|
|
31
|
+
export function getDaemonStats() {
|
|
32
|
+
return { ...stats };
|
|
33
|
+
}
|
|
34
|
+
export async function startDaemon(opts = {}) {
|
|
35
|
+
const { autoReply = true, scheduler = true, verbose = false, ownerId, } = opts;
|
|
36
|
+
const log = (msg) => {
|
|
37
|
+
if (verbose)
|
|
38
|
+
logger.info(msg);
|
|
39
|
+
};
|
|
40
|
+
console.log();
|
|
41
|
+
console.log(' 🤖 ZaloX Daemon starting...');
|
|
42
|
+
console.log(' ─────────────────────────────');
|
|
43
|
+
// ── 1. Login & Connect ─────────────────────────────
|
|
44
|
+
const api = await getApi();
|
|
45
|
+
const listener = api.listener;
|
|
46
|
+
// Cache user info
|
|
47
|
+
const config = getConfig();
|
|
48
|
+
const profile = config.activeProfile || 'default';
|
|
49
|
+
try {
|
|
50
|
+
const accountInfo = await api.fetchAccountInfo();
|
|
51
|
+
writeCache(profile, CACHE_KEYS.ME_INFO, accountInfo);
|
|
52
|
+
console.log(` ✓ Logged in as: ${accountInfo.displayName || accountInfo.userId}`);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
console.log(' ✓ Logged in (name unknown)');
|
|
56
|
+
}
|
|
57
|
+
// Cache friends & groups
|
|
58
|
+
try {
|
|
59
|
+
const friends = await api.getAllFriends();
|
|
60
|
+
writeCache(profile, CACHE_KEYS.FRIENDS, friends);
|
|
61
|
+
console.log(` ✓ ${friends.length} friends cached`);
|
|
62
|
+
}
|
|
63
|
+
catch { /* ok */ }
|
|
64
|
+
try {
|
|
65
|
+
const groupResult = await api.getAllGroups();
|
|
66
|
+
const groupIds = Object.keys(groupResult.gridVerMap || {});
|
|
67
|
+
if (groupIds.length > 0) {
|
|
68
|
+
const groupInfo = await api.getGroupInfo(groupIds);
|
|
69
|
+
const groups = Object.values(groupInfo.gridInfoMap || {});
|
|
70
|
+
writeCache(profile, CACHE_KEYS.GROUPS, groups);
|
|
71
|
+
console.log(` ✓ ${groups.length} groups cached`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch { /* ok */ }
|
|
75
|
+
// ── 2. Auto-Reply Engine ───────────────────────────
|
|
76
|
+
if (autoReply) {
|
|
77
|
+
const canAutoReply = isFeatureEnabled('autoReply');
|
|
78
|
+
if (canAutoReply) {
|
|
79
|
+
console.log(' ✓ Auto-reply engine: ON');
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
console.log(' âš Auto-reply: requires Pro license (disabled)');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// ── 3. Scheduler ───────────────────────────────────
|
|
86
|
+
if (scheduler) {
|
|
87
|
+
const canSchedule = isFeatureEnabled('scheduling');
|
|
88
|
+
if (canSchedule) {
|
|
89
|
+
console.log(' ✓ Scheduler daemon: ON');
|
|
90
|
+
// Run scheduler in background
|
|
91
|
+
runScheduler((msg) => {
|
|
92
|
+
log(msg);
|
|
93
|
+
if (msg.includes('✓ Sent'))
|
|
94
|
+
stats.scheduledSent++;
|
|
95
|
+
}).catch((err) => {
|
|
96
|
+
logger.error(`Scheduler crashed: ${err.message}`);
|
|
97
|
+
stats.errors++;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
console.log(' âš Scheduler: requires Pro license (disabled)');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// ── 4. DM Command Handler ──────────────────────────
|
|
105
|
+
if (ownerId) {
|
|
106
|
+
console.log(` ✓ DM commands: ON (owner: ${ownerId})`);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
console.log(' ✓ DM commands: ON (any sender)');
|
|
110
|
+
}
|
|
111
|
+
// ── 5. Message Listener ────────────────────────────
|
|
112
|
+
console.log(' ✓ Listener: ON');
|
|
113
|
+
console.log(' ─────────────────────────────');
|
|
114
|
+
console.log(' Ready! Waiting for messages... (Ctrl+C to stop)');
|
|
115
|
+
console.log();
|
|
116
|
+
// Cache own ID to avoid repeated fetchAccountInfo
|
|
117
|
+
let ownUserId = null;
|
|
118
|
+
try {
|
|
119
|
+
const meInfo = await api.fetchAccountInfo();
|
|
120
|
+
ownUserId = String(meInfo.userId);
|
|
121
|
+
}
|
|
122
|
+
catch { /* ok */ }
|
|
123
|
+
listener.on('message', async (msg) => {
|
|
124
|
+
stats.messagesReceived++;
|
|
125
|
+
const data = msg.data || msg;
|
|
126
|
+
const isGroup = msg.type === 1;
|
|
127
|
+
const senderId = data.uidFrom ? String(data.uidFrom) : (data.senderId ? String(data.senderId) : '');
|
|
128
|
+
const threadId = data.idTo ? String(data.idTo) : (data.threadId ? String(data.threadId) : '');
|
|
129
|
+
const content = data.content || data.msg || '';
|
|
130
|
+
const senderName = data.dName || data.senderName || senderId;
|
|
131
|
+
// Skip empty messages
|
|
132
|
+
if (!content || !senderId)
|
|
133
|
+
return;
|
|
134
|
+
// Skip own messages
|
|
135
|
+
if (ownUserId && senderId === ownUserId)
|
|
136
|
+
return;
|
|
137
|
+
const time = new Date().toLocaleTimeString('vi-VN');
|
|
138
|
+
const icon = isGroup ? '👥' : '👤';
|
|
139
|
+
log(`[${time}] ${icon} ${senderName}: ${content}`);
|
|
140
|
+
// ── DM Command Handler (/ commands) ────────
|
|
141
|
+
if (!isGroup) {
|
|
142
|
+
try {
|
|
143
|
+
const result = await handleDmCommand(content, {
|
|
144
|
+
senderId,
|
|
145
|
+
senderName,
|
|
146
|
+
api,
|
|
147
|
+
ownerId,
|
|
148
|
+
});
|
|
149
|
+
if (result.handled && result.response) {
|
|
150
|
+
await api.sendMessage(result.response, senderId, ThreadType.User);
|
|
151
|
+
log(` [DM] ✓ Command response sent to ${senderName}`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
// If not handled (not a / command), fall through to auto-reply
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
stats.errors++;
|
|
158
|
+
logger.error(`DM command failed: ${err.message}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// ── Auto-Reply ─────────────────────────────
|
|
162
|
+
if (autoReply && isFeatureEnabled('autoReply')) {
|
|
163
|
+
const context = {
|
|
164
|
+
threadId: isGroup ? threadId : senderId,
|
|
165
|
+
senderId,
|
|
166
|
+
senderName,
|
|
167
|
+
isGroup,
|
|
168
|
+
};
|
|
169
|
+
// Check group settings — is auto-reply enabled for this group?
|
|
170
|
+
if (isGroup) {
|
|
171
|
+
const gs = getGroupSettings(threadId);
|
|
172
|
+
if (gs && gs.autoReplyEnabled === false) {
|
|
173
|
+
log(` [AutoReply] Disabled for group ${threadId}`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const rule = matchRules(content, context);
|
|
178
|
+
if (rule) {
|
|
179
|
+
try {
|
|
180
|
+
const response = renderResponse(rule.response, { ...context, message: content });
|
|
181
|
+
const replyTo = isGroup ? threadId : senderId;
|
|
182
|
+
const type = isGroup ? ThreadType.Group : ThreadType.User;
|
|
183
|
+
await api.sendMessage(response, replyTo, type);
|
|
184
|
+
stats.autoRepliesSent++;
|
|
185
|
+
log(` [AutoReply] ✓ Rule "${rule.name}" → ${response.substring(0, 50)}...`);
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
stats.errors++;
|
|
189
|
+
logger.error(`AutoReply failed: ${err.message}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
listener.on('error', (err) => {
|
|
195
|
+
stats.errors++;
|
|
196
|
+
logger.error(`Listener error: ${err.message || err}`);
|
|
197
|
+
// Auto-restart if licensed
|
|
198
|
+
if (isFeatureEnabled('listenerAutoRestart')) {
|
|
199
|
+
logger.info('Reconnecting in 3 seconds...');
|
|
200
|
+
setTimeout(() => {
|
|
201
|
+
try {
|
|
202
|
+
listener.start();
|
|
203
|
+
}
|
|
204
|
+
catch { /* ignore */ }
|
|
205
|
+
}, 3000);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
// Start listener with auto-restart if licensed
|
|
209
|
+
listener.start({ retryOnClose: isFeatureEnabled('listenerAutoRestart') });
|
|
210
|
+
// Keep alive
|
|
211
|
+
await new Promise(() => { });
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/core/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZaloX — DM Command Handler
|
|
3
|
+
*
|
|
4
|
+
* When running in daemon mode (standalone, no AI agent),
|
|
5
|
+
* the bot can respond to DM commands from the owner.
|
|
6
|
+
*
|
|
7
|
+
* Commands via Zalo DM:
|
|
8
|
+
* /help — Show available commands
|
|
9
|
+
* /status — Show bot status
|
|
10
|
+
* /rules — List auto-reply rules
|
|
11
|
+
* /rules on|off <id> — Toggle a rule
|
|
12
|
+
* /jobs — List scheduled jobs
|
|
13
|
+
* /jobs on|off <id> — Toggle a job
|
|
14
|
+
* /gs — List group settings
|
|
15
|
+
* /gs <groupId> autoReply on|off — Toggle group auto-reply
|
|
16
|
+
* /gs <groupId> welcome <text> — Set welcome message
|
|
17
|
+
* /send <id> <message> — Send message to someone
|
|
18
|
+
* /send-group <id> <message> — Send to group
|
|
19
|
+
* /broadcast <message> — Send to all friends (bulk)
|
|
20
|
+
* /templates — List templates
|
|
21
|
+
* /template send <name> <id> — Send template
|
|
22
|
+
* /friends — Friend count & recent online
|
|
23
|
+
* /groups — Group list
|
|
24
|
+
*/
|
|
25
|
+
export interface DmCommandContext {
|
|
26
|
+
senderId: string;
|
|
27
|
+
senderName: string;
|
|
28
|
+
api: any;
|
|
29
|
+
ownerId?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface DmCommandResult {
|
|
32
|
+
handled: boolean;
|
|
33
|
+
response?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Process a DM message as a bot command.
|
|
37
|
+
* Returns { handled: true, response } if it was a command.
|
|
38
|
+
* Returns { handled: false } if it's not a command (pass to auto-reply or agent).
|
|
39
|
+
*/
|
|
40
|
+
export declare function handleDmCommand(message: string, ctx: DmCommandContext): Promise<DmCommandResult>;
|
|
41
|
+
//# sourceMappingURL=dm-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dm-commands.d.ts","sourceRoot":"","sources":["../../src/core/dm-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAUH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,eAAe,CAAC,CA4D1B"}
|