@rune-kit/rune 2.8.0 → 2.11.0
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 -21
- package/README.md +68 -34
- package/agents/adversary.md +27 -0
- package/agents/architect.md +19 -29
- package/agents/asset-creator.md +18 -4
- package/agents/audit.md +25 -4
- package/agents/autopsy.md +19 -4
- package/agents/ba.md +35 -0
- package/agents/brainstorm.md +31 -4
- package/agents/browser-pilot.md +21 -4
- package/agents/coder.md +21 -29
- package/agents/completion-gate.md +20 -4
- package/agents/constraint-check.md +18 -4
- package/agents/context-engine.md +22 -4
- package/agents/context-pack.md +32 -0
- package/agents/cook.md +41 -4
- package/agents/db.md +19 -4
- package/agents/debug.md +33 -4
- package/agents/dependency-doctor.md +20 -4
- package/agents/deploy.md +27 -4
- package/agents/design.md +22 -4
- package/agents/doc-processor.md +27 -0
- package/agents/docs-seeker.md +19 -4
- package/agents/docs.md +31 -0
- package/agents/fix.md +37 -4
- package/agents/git.md +29 -0
- package/agents/hallucination-guard.md +20 -4
- package/agents/incident.md +21 -4
- package/agents/integrity-check.md +18 -4
- package/agents/journal.md +19 -4
- package/agents/launch.md +32 -4
- package/agents/logic-guardian.md +26 -11
- package/agents/marketing.md +23 -4
- package/agents/mcp-builder.md +26 -0
- package/agents/neural-memory.md +30 -0
- package/agents/onboard.md +22 -4
- package/agents/perf.md +21 -4
- package/agents/plan.md +29 -4
- package/agents/preflight.md +22 -4
- package/agents/problem-solver.md +20 -4
- package/agents/rescue.md +23 -4
- package/agents/research.md +19 -4
- package/agents/researcher.md +19 -29
- package/agents/retro.md +32 -0
- package/agents/review-intake.md +20 -4
- package/agents/review.md +32 -4
- package/agents/reviewer.md +20 -28
- package/agents/safeguard.md +19 -4
- package/agents/sast.md +18 -4
- package/agents/scaffold.md +41 -0
- package/agents/scanner.md +19 -28
- package/agents/scope-guard.md +18 -4
- package/agents/scout.md +23 -4
- package/agents/sentinel-env.md +26 -0
- package/agents/sentinel.md +33 -4
- package/agents/sequential-thinking.md +20 -4
- package/agents/session-bridge.md +24 -4
- package/agents/skill-forge.md +22 -4
- package/agents/skill-router.md +26 -4
- package/agents/slides.md +24 -0
- package/agents/surgeon.md +19 -4
- package/agents/team.md +30 -4
- package/agents/test.md +36 -4
- package/agents/trend-scout.md +17 -4
- package/agents/verification.md +20 -4
- package/agents/video-creator.md +20 -4
- package/agents/watchdog.md +19 -4
- package/agents/worktree.md +17 -4
- package/commands/rune.md +168 -168
- package/compiler/__tests__/analytics.test.js +370 -0
- package/compiler/adapters/openclaw.js +2 -2
- package/compiler/analytics.js +385 -0
- package/compiler/bin/rune.js +68 -2
- package/compiler/dashboard.js +883 -0
- package/compiler/transforms/branding.js +1 -1
- package/contexts/dev.md +34 -34
- package/contexts/research.md +43 -43
- package/contexts/review.md +55 -55
- package/extensions/ai-ml/PACK.md +88 -88
- package/extensions/ai-ml/skills/ai-agents.md +172 -172
- package/extensions/ai-ml/skills/code-sandbox.md +187 -187
- package/extensions/ai-ml/skills/deep-research.md +146 -146
- package/extensions/ai-ml/skills/embedding-search.md +66 -66
- package/extensions/ai-ml/skills/fine-tuning-guide.md +74 -74
- package/extensions/ai-ml/skills/llm-architect.md +125 -125
- package/extensions/ai-ml/skills/llm-integration.md +64 -64
- package/extensions/ai-ml/skills/prompt-patterns.md +72 -72
- package/extensions/ai-ml/skills/rag-patterns.md +66 -66
- package/extensions/ai-ml/skills/web-extraction.md +114 -114
- package/extensions/analytics/PACK.md +92 -92
- package/extensions/analytics/skills/ab-testing.md +72 -72
- package/extensions/analytics/skills/dashboard-patterns.md +83 -83
- package/extensions/analytics/skills/data-validation.md +68 -68
- package/extensions/analytics/skills/funnel-analysis.md +81 -81
- package/extensions/analytics/skills/sql-patterns.md +57 -57
- package/extensions/analytics/skills/statistical-analysis.md +79 -79
- package/extensions/analytics/skills/tracking-setup.md +71 -71
- package/extensions/backend/PACK.md +104 -104
- package/extensions/backend/skills/api-patterns.md +84 -84
- package/extensions/backend/skills/async-pipeline.md +193 -193
- package/extensions/backend/skills/auth-patterns.md +97 -97
- package/extensions/backend/skills/background-jobs.md +133 -133
- package/extensions/backend/skills/caching-patterns.md +108 -108
- package/extensions/backend/skills/cli-generation.md +133 -133
- package/extensions/backend/skills/database-patterns.md +87 -87
- package/extensions/backend/skills/middleware-patterns.md +104 -104
- package/extensions/chrome-ext/PACK.md +93 -93
- package/extensions/chrome-ext/skills/cws-preflight.md +143 -143
- package/extensions/chrome-ext/skills/cws-publish.md +104 -104
- package/extensions/chrome-ext/skills/ext-ai-integration.md +251 -251
- package/extensions/chrome-ext/skills/ext-messaging.md +139 -139
- package/extensions/chrome-ext/skills/ext-storage.md +133 -133
- package/extensions/chrome-ext/skills/mv3-scaffold.md +164 -164
- package/extensions/content/PACK.md +96 -96
- package/extensions/content/skills/blog-patterns.md +88 -88
- package/extensions/content/skills/cms-integration.md +131 -131
- package/extensions/content/skills/content-scoring.md +107 -107
- package/extensions/content/skills/i18n.md +83 -83
- package/extensions/content/skills/mdx-authoring.md +137 -137
- package/extensions/content/skills/reference.md +1014 -1014
- package/extensions/content/skills/seo-patterns.md +67 -67
- package/extensions/content/skills/video-repurpose.md +153 -153
- package/extensions/devops/PACK.md +101 -101
- package/extensions/devops/skills/chaos-testing.md +67 -67
- package/extensions/devops/skills/ci-cd.md +75 -75
- package/extensions/devops/skills/docker.md +58 -58
- package/extensions/devops/skills/edge-serverless.md +163 -163
- package/extensions/devops/skills/infra-as-code.md +158 -158
- package/extensions/devops/skills/kubernetes.md +110 -110
- package/extensions/devops/skills/monitoring.md +57 -57
- package/extensions/devops/skills/server-setup.md +64 -64
- package/extensions/devops/skills/ssl-domain.md +42 -42
- package/extensions/ecommerce/PACK.md +116 -116
- package/extensions/ecommerce/skills/cart-system.md +79 -79
- package/extensions/ecommerce/skills/inventory-mgmt.md +102 -102
- package/extensions/ecommerce/skills/order-management.md +126 -126
- package/extensions/ecommerce/skills/payment-integration.md +472 -472
- package/extensions/ecommerce/skills/shopify-dev.md +69 -69
- package/extensions/ecommerce/skills/subscription-billing.md +93 -93
- package/extensions/ecommerce/skills/tax-compliance.md +117 -117
- package/extensions/gamedev/PACK.md +142 -142
- package/extensions/gamedev/skills/asset-pipeline.md +74 -74
- package/extensions/gamedev/skills/audio-system.md +129 -129
- package/extensions/gamedev/skills/camera-system.md +87 -87
- package/extensions/gamedev/skills/ecs.md +98 -98
- package/extensions/gamedev/skills/game-loops.md +72 -72
- package/extensions/gamedev/skills/input-system.md +199 -199
- package/extensions/gamedev/skills/multiplayer.md +180 -180
- package/extensions/gamedev/skills/particles.md +105 -105
- package/extensions/gamedev/skills/physics-engine.md +89 -89
- package/extensions/gamedev/skills/scene-management.md +146 -146
- package/extensions/gamedev/skills/threejs-patterns.md +90 -90
- package/extensions/gamedev/skills/webgl.md +71 -71
- package/extensions/mobile/PACK.md +106 -106
- package/extensions/mobile/skills/app-store-connect.md +152 -152
- package/extensions/mobile/skills/app-store-prep.md +66 -66
- package/extensions/mobile/skills/deep-linking.md +109 -109
- package/extensions/mobile/skills/flutter.md +60 -60
- package/extensions/mobile/skills/ios-build-pipeline.md +142 -142
- package/extensions/mobile/skills/native-bridge.md +66 -66
- package/extensions/mobile/skills/ota-updates.md +97 -97
- package/extensions/mobile/skills/push-notifications.md +111 -111
- package/extensions/mobile/skills/react-native.md +82 -82
- package/extensions/saas/PACK.md +116 -116
- package/extensions/saas/skills/billing-integration.md +200 -200
- package/extensions/saas/skills/feature-flags.md +130 -130
- package/extensions/saas/skills/multi-tenant.md +103 -103
- package/extensions/saas/skills/onboarding-flow.md +139 -139
- package/extensions/saas/skills/subscription-flow.md +95 -95
- package/extensions/saas/skills/team-management.md +144 -144
- package/extensions/security/PACK.md +99 -99
- package/extensions/security/skills/api-security.md +140 -140
- package/extensions/security/skills/compliance.md +68 -68
- package/extensions/security/skills/owasp-audit.md +64 -64
- package/extensions/security/skills/pentest-patterns.md +77 -77
- package/extensions/security/skills/secret-mgmt.md +65 -65
- package/extensions/security/skills/supply-chain.md +65 -65
- package/extensions/trading/PACK.md +80 -80
- package/extensions/trading/skills/chart-components.md +55 -55
- package/extensions/trading/skills/experiment-loop.md +125 -125
- package/extensions/trading/skills/fintech-patterns.md +47 -47
- package/extensions/trading/skills/indicator-library.md +58 -58
- package/extensions/trading/skills/quant-analysis.md +111 -111
- package/extensions/trading/skills/realtime-data.md +58 -58
- package/extensions/trading/skills/trade-logic.md +104 -104
- package/extensions/ui/PACK.md +130 -130
- package/extensions/ui/skills/a11y-audit.md +91 -91
- package/extensions/ui/skills/animation-patterns.md +127 -106
- package/extensions/ui/skills/component-patterns.md +100 -75
- package/extensions/ui/skills/design-decision.md +108 -108
- package/extensions/ui/skills/design-system.md +68 -68
- package/extensions/ui/skills/landing-patterns.md +155 -155
- package/extensions/ui/skills/palette-picker.md +173 -173
- package/extensions/ui/skills/react-health.md +90 -90
- package/extensions/ui/skills/type-system.md +125 -125
- package/extensions/ui/skills/web-vitals.md +153 -153
- package/extensions/zalo/PACK.md +145 -145
- package/extensions/zalo/skills/zalo-oa-mcp.md +317 -317
- package/extensions/zalo/skills/zalo-oa-messaging.md +429 -429
- package/extensions/zalo/skills/zalo-oa-setup.md +236 -236
- package/extensions/zalo/skills/zalo-oa-webhook.md +189 -189
- package/extensions/zalo/skills/zalo-personal-messaging.md +194 -194
- package/extensions/zalo/skills/zalo-personal-setup.md +153 -153
- package/extensions/zalo/skills/zalo-rate-guard.md +219 -219
- package/hooks/auto-format/index.cjs +48 -48
- package/hooks/context-watch/index.cjs +95 -68
- package/hooks/hooks.json +111 -111
- package/hooks/metrics-collector/index.cjs +86 -42
- package/hooks/post-session-reflect/index.cjs +189 -153
- package/hooks/pre-compact/index.cjs +95 -95
- package/hooks/run-hook.cmd +1 -1
- package/hooks/secrets-scan/index.cjs +100 -100
- package/hooks/session-start/index.cjs +71 -65
- package/hooks/typecheck/index.cjs +65 -65
- package/package.json +63 -63
- package/references/ui-pro-max-data/LICENSE-UI-PRO-MAX +21 -21
- package/references/ui-pro-max-data/charts.csv +26 -26
- package/references/ui-pro-max-data/colors.csv +161 -161
- package/references/ui-pro-max-data/styles.csv +68 -68
- package/references/ui-pro-max-data/typography.csv +74 -74
- package/references/ui-pro-max-data/ui-reasoning.csv +162 -162
- package/references/ui-pro-max-data/ux-guidelines.csv +99 -99
- package/skills/adversary/SKILL.md +283 -283
- package/skills/asset-creator/SKILL.md +157 -157
- package/skills/audit/SKILL.md +148 -2
- package/skills/autopsy/SKILL.md +335 -259
- package/skills/autopsy/references/repo-analysis-patterns.md +113 -0
- package/skills/ba/SKILL.md +72 -2
- package/skills/brainstorm/SKILL.md +342 -341
- package/skills/browser-pilot/SKILL.md +168 -168
- package/skills/constraint-check/SKILL.md +165 -165
- package/skills/context-engine/SKILL.md +404 -404
- package/skills/cook/SKILL.md +917 -834
- package/skills/cook/references/output-format.md +33 -0
- package/skills/db/SKILL.md +273 -272
- package/skills/debug/SKILL.md +465 -443
- package/skills/dependency-doctor/SKILL.md +265 -235
- package/skills/deploy/SKILL.md +274 -231
- package/skills/design/DESIGN-REFERENCE.md +365 -365
- package/skills/design/SKILL.md +589 -482
- package/skills/doc-processor/SKILL.md +254 -254
- package/skills/docs/SKILL.md +374 -373
- package/skills/docs-seeker/SKILL.md +177 -177
- package/skills/fix/SKILL.md +330 -308
- package/skills/git/SKILL.md +339 -339
- package/skills/graft/SKILL.md +352 -0
- package/skills/graft/references/challenge-framework.md +98 -0
- package/skills/graft/references/mode-decision.md +44 -0
- package/skills/hallucination-guard/SKILL.md +219 -219
- package/skills/incident/SKILL.md +254 -251
- package/skills/integrity-check/SKILL.md +169 -169
- package/skills/journal/SKILL.md +240 -238
- package/skills/launch/SKILL.md +344 -342
- package/skills/logic-guardian/SKILL.md +251 -251
- package/skills/marketing/SKILL.md +290 -245
- package/skills/mcp-builder/SKILL.md +425 -423
- package/skills/mcp-builder/references/auto-discovery-pattern.md +169 -0
- package/skills/neural-memory/SKILL.md +362 -362
- package/skills/onboard/SKILL.md +404 -403
- package/skills/perf/SKILL.md +346 -346
- package/skills/plan/SKILL.md +433 -370
- package/skills/plan/references/feature-map.md +84 -0
- package/skills/preflight/SKILL.md +415 -396
- package/skills/problem-solver/SKILL.md +380 -284
- package/skills/rescue/SKILL.md +474 -450
- package/skills/retro/SKILL.md +5 -1
- package/skills/review/SKILL.md +612 -535
- package/skills/review-intake/SKILL.md +249 -249
- package/skills/safeguard/SKILL.md +200 -200
- package/skills/sast/SKILL.md +190 -190
- package/skills/scaffold/SKILL.md +328 -286
- package/skills/scope-guard/SKILL.md +180 -162
- package/skills/scout/SKILL.md +263 -263
- package/skills/sentinel/SKILL.md +382 -353
- package/skills/sentinel-env/SKILL.md +254 -254
- package/skills/sequential-thinking/SKILL.md +234 -234
- package/skills/session-bridge/SKILL.md +543 -397
- package/skills/skill-forge/SKILL.md +581 -539
- package/skills/skill-router/{skill.md → SKILL.md} +30 -2
- package/skills/surgeon/SKILL.md +215 -215
- package/skills/team/SKILL.md +556 -514
- package/skills/test/SKILL.md +614 -587
- package/skills/trend-scout/SKILL.md +145 -145
- package/skills/verification/SKILL.md +326 -325
- package/skills/video-creator/SKILL.md +201 -201
- package/skills/watchdog/SKILL.md +168 -168
- package/skills/worktree/SKILL.md +140 -140
|
@@ -1,194 +1,194 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: zalo-personal-messaging
|
|
3
|
-
pack: "@rune/zalo"
|
|
4
|
-
description: Personal and group messaging via zca-js — text, media, reactions, group management, mention gating, message buffer for context. UNOFFICIAL — risk-gated.
|
|
5
|
-
model: sonnet
|
|
6
|
-
tools: "Read, Glob, Grep, Bash, Write, Edit"
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# zalo-personal-messaging
|
|
10
|
-
|
|
11
|
-
> ⚠️ Track B (unofficial). See zalo-personal-setup for full risk disclaimer.
|
|
12
|
-
> This skill assumes you have completed zalo-personal-setup and have an active API instance.
|
|
13
|
-
|
|
14
|
-
## Overview
|
|
15
|
-
|
|
16
|
-
Send messages, media, and reactions to Zalo personal accounts and groups via zca-js. Covers 1:1 DMs, group messaging, mention-gated bot patterns, and context buffering.
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## Direct Messages (1:1)
|
|
21
|
-
|
|
22
|
-
```typescript
|
|
23
|
-
// Send text
|
|
24
|
-
await api.sendMessage('Hello!', threadId, 'User')
|
|
25
|
-
|
|
26
|
-
// Send image (local file path — download first if URL)
|
|
27
|
-
await api.sendMessage({
|
|
28
|
-
body: 'Check this image',
|
|
29
|
-
attachments: [imagePath]
|
|
30
|
-
}, threadId, 'User')
|
|
31
|
-
|
|
32
|
-
// Chunk long messages (2000-char limit applies to DMs too)
|
|
33
|
-
async function sendLong(text: string, threadId: string, type: 'User' | 'Group') {
|
|
34
|
-
const chunks = text.match(/.{1,1900}/gs) ?? [text]
|
|
35
|
-
for (const chunk of chunks) {
|
|
36
|
-
await api.sendMessage(chunk, threadId, type)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## Group Messaging
|
|
44
|
-
|
|
45
|
-
```typescript
|
|
46
|
-
// Send to group
|
|
47
|
-
await api.sendMessage('Hello group!', groupId, 'Group')
|
|
48
|
-
|
|
49
|
-
// Send with mention
|
|
50
|
-
await api.sendMessage({
|
|
51
|
-
body: '@John check this',
|
|
52
|
-
mentions: [{ pos: 0, len: 5, uid: johnUserId }]
|
|
53
|
-
}, groupId, 'Group')
|
|
54
|
-
|
|
55
|
-
// Group management
|
|
56
|
-
await api.createGroup('Bot Test Group', [userId1, userId2]) // min 3 members incl. self
|
|
57
|
-
await api.addGroupMembers(groupId, [newMemberId])
|
|
58
|
-
await api.removeGroupMembers(groupId, [memberId])
|
|
59
|
-
await api.changeGroupName(groupId, 'New Name')
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## Media Types
|
|
65
|
-
|
|
66
|
-
| Type | Notes |
|
|
67
|
-
|------|-------|
|
|
68
|
-
| Text | 2000-char limit — chunk if needed |
|
|
69
|
-
| Image | Local file path only — download URL first |
|
|
70
|
-
| Video | Local file path |
|
|
71
|
-
| Voice | Local file path |
|
|
72
|
-
| Sticker | By sticker ID — IDs undocumented, capture from received msgs |
|
|
73
|
-
| File | Local file path |
|
|
74
|
-
| Contact card | User ID reference |
|
|
75
|
-
| Link | Auto-generates preview |
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## Reactions
|
|
80
|
-
|
|
81
|
-
```typescript
|
|
82
|
-
// React to a message (11 types)
|
|
83
|
-
await api.sendReaction(messageId, threadId, '❤️', 'User')
|
|
84
|
-
|
|
85
|
-
// Available: ❤️ 😆 😮 😢 😠 👍 👎 ✊ 🎉 😏 🥰
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## Mention Gating Pattern
|
|
91
|
-
|
|
92
|
-
For group bots — only process when @mentioned, buffer other messages for context:
|
|
93
|
-
|
|
94
|
-
```typescript
|
|
95
|
-
function isMentioned(msg: GroupMessage, botId: string): boolean {
|
|
96
|
-
return msg.data.mentions?.some(m => m.uid === botId) ?? false
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
listener.on('group_message', async (msg) => {
|
|
100
|
-
if (!isMentioned(msg, BOT_USER_ID)) {
|
|
101
|
-
messageBuffer.push(msg) // buffer for context
|
|
102
|
-
return
|
|
103
|
-
}
|
|
104
|
-
// Bot was mentioned — process with buffered context
|
|
105
|
-
const context = messageBuffer.getRecent(msg.threadId, 20)
|
|
106
|
-
const response = await processWithContext(msg, context)
|
|
107
|
-
await api.sendMessage(response, msg.threadId, 'Group')
|
|
108
|
-
})
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
> Use `msg.data.mentions` array — never parse `@` from message text (unreliable).
|
|
112
|
-
|
|
113
|
-
---
|
|
114
|
-
|
|
115
|
-
## Message Buffer
|
|
116
|
-
|
|
117
|
-
Buffer recent group messages per thread for context injection:
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
class MessageBuffer {
|
|
121
|
-
private buffer: Map<string, Message[]> = new Map()
|
|
122
|
-
private maxPerThread = 50
|
|
123
|
-
|
|
124
|
-
push(msg: Message) {
|
|
125
|
-
const threadId = msg.threadId
|
|
126
|
-
const msgs = this.buffer.get(threadId) ?? []
|
|
127
|
-
msgs.push(msg)
|
|
128
|
-
if (msgs.length > this.maxPerThread) msgs.shift() // cap to avoid unbounded growth
|
|
129
|
-
this.buffer.set(threadId, msgs)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
getRecent(threadId: string, count: number): Message[] {
|
|
133
|
-
return (this.buffer.get(threadId) ?? []).slice(-count)
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## Name Cache
|
|
141
|
-
|
|
142
|
-
Resolve user IDs to display names with TTL to avoid API hammering:
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
class NameCache {
|
|
146
|
-
private cache = new Map<string, { name: string; expiresAt: number }>()
|
|
147
|
-
private ttl = 60 * 60 * 1000 // 1 hour
|
|
148
|
-
|
|
149
|
-
async resolve(userId: string, api: ZaloApi): Promise<string> {
|
|
150
|
-
const cached = this.cache.get(userId)
|
|
151
|
-
if (cached && cached.expiresAt > Date.now()) return cached.name
|
|
152
|
-
try {
|
|
153
|
-
const profile = await api.getUserInfo(userId)
|
|
154
|
-
const name = profile.displayName || 'Unknown'
|
|
155
|
-
this.cache.set(userId, { name, expiresAt: Date.now() + this.ttl })
|
|
156
|
-
return name
|
|
157
|
-
} catch {
|
|
158
|
-
return 'Unknown'
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
---
|
|
165
|
-
|
|
166
|
-
## Event Listeners
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
// DM and group events are SEPARATE — wire both
|
|
170
|
-
listener.on('message', async (msg) => {
|
|
171
|
-
// 1:1 personal messages
|
|
172
|
-
const senderId = msg.data.uidFrom
|
|
173
|
-
await handleDM(msg, senderId)
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
listener.on('group_message', async (msg) => {
|
|
177
|
-
// Group messages — includes mention data
|
|
178
|
-
const senderId = msg.data.uidFrom
|
|
179
|
-
await handleGroup(msg, senderId)
|
|
180
|
-
})
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
---
|
|
184
|
-
|
|
185
|
-
## Sharp Edges
|
|
186
|
-
|
|
187
|
-
- **Separate events**: `message` (DM) vs `group_message` (group) — missing one = silent drop
|
|
188
|
-
- **Mention detection**: check `msg.data.mentions` array, not text `@` parsing
|
|
189
|
-
- **2000-char limit**: applies to both DM and group — always chunk
|
|
190
|
-
- **Image upload**: local file path only — download remote URLs before sending
|
|
191
|
-
- **Sticker IDs**: undocumented — sniff from received sticker messages to build your own map
|
|
192
|
-
- **Group create**: minimum 3 members including self — 2-member call throws
|
|
193
|
-
- **Buffer cap**: always set `maxPerThread` — unbounded growth crashes long-running bots
|
|
194
|
-
- **Name cache TTL**: don't skip — `getUserInfo` rate-limited aggressively on personal accounts
|
|
1
|
+
---
|
|
2
|
+
name: zalo-personal-messaging
|
|
3
|
+
pack: "@rune/zalo"
|
|
4
|
+
description: Personal and group messaging via zca-js — text, media, reactions, group management, mention gating, message buffer for context. UNOFFICIAL — risk-gated.
|
|
5
|
+
model: sonnet
|
|
6
|
+
tools: "Read, Glob, Grep, Bash, Write, Edit"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# zalo-personal-messaging
|
|
10
|
+
|
|
11
|
+
> ⚠️ Track B (unofficial). See zalo-personal-setup for full risk disclaimer.
|
|
12
|
+
> This skill assumes you have completed zalo-personal-setup and have an active API instance.
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
Send messages, media, and reactions to Zalo personal accounts and groups via zca-js. Covers 1:1 DMs, group messaging, mention-gated bot patterns, and context buffering.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Direct Messages (1:1)
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// Send text
|
|
24
|
+
await api.sendMessage('Hello!', threadId, 'User')
|
|
25
|
+
|
|
26
|
+
// Send image (local file path — download first if URL)
|
|
27
|
+
await api.sendMessage({
|
|
28
|
+
body: 'Check this image',
|
|
29
|
+
attachments: [imagePath]
|
|
30
|
+
}, threadId, 'User')
|
|
31
|
+
|
|
32
|
+
// Chunk long messages (2000-char limit applies to DMs too)
|
|
33
|
+
async function sendLong(text: string, threadId: string, type: 'User' | 'Group') {
|
|
34
|
+
const chunks = text.match(/.{1,1900}/gs) ?? [text]
|
|
35
|
+
for (const chunk of chunks) {
|
|
36
|
+
await api.sendMessage(chunk, threadId, type)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Group Messaging
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// Send to group
|
|
47
|
+
await api.sendMessage('Hello group!', groupId, 'Group')
|
|
48
|
+
|
|
49
|
+
// Send with mention
|
|
50
|
+
await api.sendMessage({
|
|
51
|
+
body: '@John check this',
|
|
52
|
+
mentions: [{ pos: 0, len: 5, uid: johnUserId }]
|
|
53
|
+
}, groupId, 'Group')
|
|
54
|
+
|
|
55
|
+
// Group management
|
|
56
|
+
await api.createGroup('Bot Test Group', [userId1, userId2]) // min 3 members incl. self
|
|
57
|
+
await api.addGroupMembers(groupId, [newMemberId])
|
|
58
|
+
await api.removeGroupMembers(groupId, [memberId])
|
|
59
|
+
await api.changeGroupName(groupId, 'New Name')
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Media Types
|
|
65
|
+
|
|
66
|
+
| Type | Notes |
|
|
67
|
+
|------|-------|
|
|
68
|
+
| Text | 2000-char limit — chunk if needed |
|
|
69
|
+
| Image | Local file path only — download URL first |
|
|
70
|
+
| Video | Local file path |
|
|
71
|
+
| Voice | Local file path |
|
|
72
|
+
| Sticker | By sticker ID — IDs undocumented, capture from received msgs |
|
|
73
|
+
| File | Local file path |
|
|
74
|
+
| Contact card | User ID reference |
|
|
75
|
+
| Link | Auto-generates preview |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Reactions
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// React to a message (11 types)
|
|
83
|
+
await api.sendReaction(messageId, threadId, '❤️', 'User')
|
|
84
|
+
|
|
85
|
+
// Available: ❤️ 😆 😮 😢 😠 👍 👎 ✊ 🎉 😏 🥰
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Mention Gating Pattern
|
|
91
|
+
|
|
92
|
+
For group bots — only process when @mentioned, buffer other messages for context:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
function isMentioned(msg: GroupMessage, botId: string): boolean {
|
|
96
|
+
return msg.data.mentions?.some(m => m.uid === botId) ?? false
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
listener.on('group_message', async (msg) => {
|
|
100
|
+
if (!isMentioned(msg, BOT_USER_ID)) {
|
|
101
|
+
messageBuffer.push(msg) // buffer for context
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
// Bot was mentioned — process with buffered context
|
|
105
|
+
const context = messageBuffer.getRecent(msg.threadId, 20)
|
|
106
|
+
const response = await processWithContext(msg, context)
|
|
107
|
+
await api.sendMessage(response, msg.threadId, 'Group')
|
|
108
|
+
})
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
> Use `msg.data.mentions` array — never parse `@` from message text (unreliable).
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Message Buffer
|
|
116
|
+
|
|
117
|
+
Buffer recent group messages per thread for context injection:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
class MessageBuffer {
|
|
121
|
+
private buffer: Map<string, Message[]> = new Map()
|
|
122
|
+
private maxPerThread = 50
|
|
123
|
+
|
|
124
|
+
push(msg: Message) {
|
|
125
|
+
const threadId = msg.threadId
|
|
126
|
+
const msgs = this.buffer.get(threadId) ?? []
|
|
127
|
+
msgs.push(msg)
|
|
128
|
+
if (msgs.length > this.maxPerThread) msgs.shift() // cap to avoid unbounded growth
|
|
129
|
+
this.buffer.set(threadId, msgs)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getRecent(threadId: string, count: number): Message[] {
|
|
133
|
+
return (this.buffer.get(threadId) ?? []).slice(-count)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Name Cache
|
|
141
|
+
|
|
142
|
+
Resolve user IDs to display names with TTL to avoid API hammering:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
class NameCache {
|
|
146
|
+
private cache = new Map<string, { name: string; expiresAt: number }>()
|
|
147
|
+
private ttl = 60 * 60 * 1000 // 1 hour
|
|
148
|
+
|
|
149
|
+
async resolve(userId: string, api: ZaloApi): Promise<string> {
|
|
150
|
+
const cached = this.cache.get(userId)
|
|
151
|
+
if (cached && cached.expiresAt > Date.now()) return cached.name
|
|
152
|
+
try {
|
|
153
|
+
const profile = await api.getUserInfo(userId)
|
|
154
|
+
const name = profile.displayName || 'Unknown'
|
|
155
|
+
this.cache.set(userId, { name, expiresAt: Date.now() + this.ttl })
|
|
156
|
+
return name
|
|
157
|
+
} catch {
|
|
158
|
+
return 'Unknown'
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Event Listeners
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// DM and group events are SEPARATE — wire both
|
|
170
|
+
listener.on('message', async (msg) => {
|
|
171
|
+
// 1:1 personal messages
|
|
172
|
+
const senderId = msg.data.uidFrom
|
|
173
|
+
await handleDM(msg, senderId)
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
listener.on('group_message', async (msg) => {
|
|
177
|
+
// Group messages — includes mention data
|
|
178
|
+
const senderId = msg.data.uidFrom
|
|
179
|
+
await handleGroup(msg, senderId)
|
|
180
|
+
})
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Sharp Edges
|
|
186
|
+
|
|
187
|
+
- **Separate events**: `message` (DM) vs `group_message` (group) — missing one = silent drop
|
|
188
|
+
- **Mention detection**: check `msg.data.mentions` array, not text `@` parsing
|
|
189
|
+
- **2000-char limit**: applies to both DM and group — always chunk
|
|
190
|
+
- **Image upload**: local file path only — download remote URLs before sending
|
|
191
|
+
- **Sticker IDs**: undocumented — sniff from received sticker messages to build your own map
|
|
192
|
+
- **Group create**: minimum 3 members including self — 2-member call throws
|
|
193
|
+
- **Buffer cap**: always set `maxPerThread` — unbounded growth crashes long-running bots
|
|
194
|
+
- **Name cache TTL**: don't skip — `getUserInfo` rate-limited aggressively on personal accounts
|