@dsh-overdrive/gateway 0.1.6 → 0.1.8
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/adapter.d.ts +8 -0
- package/dist/adapters/discord.js +7 -0
- package/dist/adapters/discord.js.map +1 -1
- package/dist/adapters/slack.js +8 -0
- package/dist/adapters/slack.js.map +1 -1
- package/dist/adapters/telegram.js +11 -1
- package/dist/adapters/telegram.js.map +1 -1
- package/dist/adapters/whatsapp.js +16 -0
- package/dist/adapters/whatsapp.js.map +1 -1
- package/dist/commands.d.ts +19 -0
- package/dist/commands.js +33 -0
- package/dist/commands.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +113 -4
- package/dist/index.js.map +1 -1
- package/dist/memory.d.ts +24 -0
- package/dist/memory.js +100 -0
- package/dist/memory.js.map +1 -0
- package/package.json +2 -2
- package/src/adapter.ts +9 -0
- package/src/adapters/discord.ts +7 -0
- package/src/adapters/slack.ts +8 -0
- package/src/adapters/telegram.ts +10 -1
- package/src/adapters/whatsapp.ts +15 -0
- package/src/commands.ts +34 -1
- package/src/index.ts +102 -3
- package/src/memory.ts +108 -0
- package/test/commands.test.ts +19 -0
- package/test/memory.test.ts +60 -0
- package/test/multi.test.ts +111 -1
package/test/multi.test.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { wireAdapter } from '../src/index.js';
|
|
2
|
+
import { mediaKindFromPath, remindSchedule, wireAdapter } from '../src/index.js';
|
|
3
3
|
import { GatewayClient } from '@dsh-overdrive/sdk';
|
|
4
|
+
import { MemoryStore } from '../src/memory.js';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import { rmSync, writeFileSync } from 'node:fs';
|
|
4
8
|
import type { Adapter, NormalizedMessage, OutboundPayload, ReplySender } from '../src/adapter.js';
|
|
5
9
|
|
|
6
10
|
/** 可编程 FakeAdapter:验证 wiring 逻辑(白名单/会话键/错误兜底)。 */
|
|
@@ -39,6 +43,7 @@ function fakeClient() {
|
|
|
39
43
|
approvals.push({ reqId, decision });
|
|
40
44
|
return { ok: true };
|
|
41
45
|
},
|
|
46
|
+
listTasks: async () => ({ tasks: [] }),
|
|
42
47
|
// wireAdapter 末尾会订阅事件流(client.connect),假客户端需实现完整接口
|
|
43
48
|
connect: async () => () => undefined,
|
|
44
49
|
} as unknown as GatewayClient;
|
|
@@ -104,4 +109,109 @@ describe('wireAdapter(多适配器装配核心)', () => {
|
|
|
104
109
|
await adapter.emit({ chatId: 'C1', userId: 'U1', text: 'hi' });
|
|
105
110
|
expect(adapter.sent[0].payload.text).toContain('❌');
|
|
106
111
|
});
|
|
112
|
+
|
|
113
|
+
it('相关记忆自动注入到发给 agent 的文本(OpenClaw 式)', async () => {
|
|
114
|
+
const adapter = new FakeAdapter('telegram');
|
|
115
|
+
const { client, messages } = fakeClient();
|
|
116
|
+
const memory = new MemoryStore();
|
|
117
|
+
memory.add('telegram:222', '用户喜欢美式咖啡');
|
|
118
|
+
await wireAdapter(adapter, client, { allowlist: ['telegram:111:222'], memory });
|
|
119
|
+
|
|
120
|
+
await adapter.emit({ chatId: '111', userId: '222', text: '帮我点一杯咖啡' });
|
|
121
|
+
expect(messages[0].text).toContain('帮我点一杯咖啡');
|
|
122
|
+
expect(messages[0].text).toContain('📌 相关记忆');
|
|
123
|
+
expect(messages[0].text).toContain('用户喜欢美式咖啡');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('无相关记忆时不注入', async () => {
|
|
127
|
+
const adapter = new FakeAdapter('telegram');
|
|
128
|
+
const { client, messages } = fakeClient();
|
|
129
|
+
const memory = new MemoryStore();
|
|
130
|
+
memory.add('telegram:222', '用户喜欢美式咖啡');
|
|
131
|
+
await wireAdapter(adapter, client, { allowlist: ['telegram:111:222'], memory });
|
|
132
|
+
|
|
133
|
+
await adapter.emit({ chatId: '111', userId: '222', text: '今天天气如何' });
|
|
134
|
+
expect(messages[0].text).toBe('今天天气如何');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('/remember 命令写入记忆并回执', async () => {
|
|
138
|
+
const adapter = new FakeAdapter('telegram');
|
|
139
|
+
const { client } = fakeClient();
|
|
140
|
+
const memory = new MemoryStore();
|
|
141
|
+
await wireAdapter(adapter, client, { allowlist: ['telegram:111:222'], memory });
|
|
142
|
+
|
|
143
|
+
await adapter.emit({ chatId: '111', userId: '222', text: '/remember 用户住在杭州' });
|
|
144
|
+
expect(memory.count('telegram:222')).toBe(1);
|
|
145
|
+
expect(adapter.sent[0].payload.text).toContain('已记住');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('/send <path> 读取文件并以媒体载荷发送', async () => {
|
|
149
|
+
const adapter = new FakeAdapter('telegram');
|
|
150
|
+
const { client } = fakeClient();
|
|
151
|
+
const tmp = join(tmpdir(), `dsh-send-test-${Date.now()}.png`);
|
|
152
|
+
writeFileSync(tmp, Buffer.from([1, 2, 3]));
|
|
153
|
+
try {
|
|
154
|
+
await wireAdapter(adapter, client, { allowlist: ['telegram:111:222'] });
|
|
155
|
+
await adapter.emit({ chatId: '111', userId: '222', text: `/send ${tmp}` });
|
|
156
|
+
expect(adapter.sent[0].payload.media).toMatchObject({ kind: 'image', path: tmp });
|
|
157
|
+
expect(adapter.sent[0].payload.text).toContain('📎');
|
|
158
|
+
} finally {
|
|
159
|
+
rmSync(tmp, { force: true });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('/send 不存在的文件回错误文本', async () => {
|
|
164
|
+
const adapter = new FakeAdapter('telegram');
|
|
165
|
+
const { client } = fakeClient();
|
|
166
|
+
await wireAdapter(adapter, client, { allowlist: ['telegram:111:222'] });
|
|
167
|
+
await adapter.emit({ chatId: '111', userId: '222', text: '/send /no/such/file.png' });
|
|
168
|
+
expect(adapter.sent[0].payload.text).toContain('❌');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('/status 返回适配器/记忆/定时任务概览', async () => {
|
|
172
|
+
const adapter = new FakeAdapter('telegram');
|
|
173
|
+
const { client } = fakeClient();
|
|
174
|
+
const memory = new MemoryStore();
|
|
175
|
+
memory.add('telegram:222', 'x');
|
|
176
|
+
await wireAdapter(adapter, client, { allowlist: ['telegram:111:222'], memory });
|
|
177
|
+
await adapter.emit({ chatId: '111', userId: '222', text: '/status' });
|
|
178
|
+
expect(adapter.sent[0].payload.text).toContain('📊 状态');
|
|
179
|
+
expect(adapter.sent[0].payload.text).toContain('telegram');
|
|
180
|
+
expect(adapter.sent[0].payload.text).toContain('记忆: 1 条');
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('remindSchedule', () => {
|
|
185
|
+
it('相对分钟 → cron 5 字段', () => {
|
|
186
|
+
const now = new Date('2026-08-20T10:05:00');
|
|
187
|
+
expect(remindSchedule(10, null, now)).toBe('15 10 20 8 *');
|
|
188
|
+
});
|
|
189
|
+
it('定点时间 → cron;已过则推到明天', () => {
|
|
190
|
+
const now = new Date('2026-08-20T10:05:00');
|
|
191
|
+
expect(remindSchedule(0, '14:30', now)).toBe('30 14 20 8 *');
|
|
192
|
+
expect(remindSchedule(0, '09:00', now)).toBe('0 9 21 8 *');
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe('mediaKindFromPath', () => {
|
|
197
|
+
it('图片/语音/其他文件分类', () => {
|
|
198
|
+
expect(mediaKindFromPath('a.png')).toBe('image');
|
|
199
|
+
expect(mediaKindFromPath('b.JPG')).toBe('image');
|
|
200
|
+
expect(mediaKindFromPath('c.webp')).toBe('image');
|
|
201
|
+
expect(mediaKindFromPath('v.ogg')).toBe('voice');
|
|
202
|
+
expect(mediaKindFromPath('r.pdf')).toBe('file');
|
|
203
|
+
expect(mediaKindFromPath('x.zip')).toBe('file');
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('remindSchedule', () => {
|
|
208
|
+
it('相对分钟 → cron 5 字段', () => {
|
|
209
|
+
const now = new Date('2026-08-20T10:05:00');
|
|
210
|
+
expect(remindSchedule(10, null, now)).toBe('15 10 20 8 *');
|
|
211
|
+
});
|
|
212
|
+
it('定点时间 → cron;已过则推到明天', () => {
|
|
213
|
+
const now = new Date('2026-08-20T10:05:00');
|
|
214
|
+
expect(remindSchedule(0, '14:30', now)).toBe('30 14 20 8 *');
|
|
215
|
+
expect(remindSchedule(0, '09:00', now)).toBe('0 9 21 8 *');
|
|
216
|
+
});
|
|
107
217
|
});
|