@qihoo/tuitui-openclaw-channel 1.0.15 → 1.0.17
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/package.json +1 -1
- package/src/channel.ts +31 -6
- package/src/confs.ts +4 -2
- package/src/histories.ts +68 -6
- package/src/outbound.ts +1 -1
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -24,8 +24,27 @@ const isConfigured = (account: any)=> !!(account?.appId && account?.appSecret);
|
|
|
24
24
|
|
|
25
25
|
const wsReadyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'] as const;
|
|
26
26
|
let wsNumber = 0;
|
|
27
|
+
const checkTuiTuiConfig = async (apiRuntime: any) => {
|
|
28
|
+
const cfg = await apiRuntime.config.loadConfig();
|
|
29
|
+
const channels = cfg?.channels || {};
|
|
30
|
+
const currChannel = channels?.[CHANNEL_ID] || {};
|
|
31
|
+
if (currChannel?.appId === undefined && !currChannel.accounts) {
|
|
32
|
+
await apiRuntime.config.writeConfigFile({
|
|
33
|
+
...cfg,
|
|
34
|
+
channels: {
|
|
35
|
+
...channels,
|
|
36
|
+
[CHANNEL_ID]: {
|
|
37
|
+
...currChannel,
|
|
38
|
+
...baseFildsDefault,
|
|
39
|
+
appSecret: undefined,
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
27
45
|
|
|
28
46
|
export function createTuiTuiChannelPlugin(apiRuntime: any) {
|
|
47
|
+
checkTuiTuiConfig(apiRuntime);
|
|
29
48
|
return {
|
|
30
49
|
id: CHANNEL_ID,
|
|
31
50
|
meta: {
|
|
@@ -44,16 +63,15 @@ export function createTuiTuiChannelPlugin(apiRuntime: any) {
|
|
|
44
63
|
config: {
|
|
45
64
|
listAccountIds: (cfg: any) => {
|
|
46
65
|
const base = cfg?.channels?.[CHANNEL_ID];
|
|
47
|
-
if (!base) return [];
|
|
48
66
|
const ids = new Set<string>();
|
|
49
|
-
|
|
50
|
-
if (base
|
|
67
|
+
ids.add(DEFAULT_ACCOUNT_ID);
|
|
68
|
+
if (base?.accounts) for (const k in base.accounts) ids.add(k);
|
|
51
69
|
return Array.from(ids);
|
|
52
70
|
},
|
|
53
71
|
|
|
54
72
|
resolveAccount,
|
|
55
73
|
|
|
56
|
-
defaultAccountId: (
|
|
74
|
+
defaultAccountId: () => DEFAULT_ACCOUNT_ID,
|
|
57
75
|
|
|
58
76
|
isEnabled,
|
|
59
77
|
|
|
@@ -102,11 +120,18 @@ export function createTuiTuiChannelPlugin(apiRuntime: any) {
|
|
|
102
120
|
},
|
|
103
121
|
|
|
104
122
|
status: {
|
|
123
|
+
defaultRuntime: {
|
|
124
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
125
|
+
running: false,
|
|
126
|
+
lastStartAt: null,
|
|
127
|
+
lastStopAt: null,
|
|
128
|
+
lastError: null,
|
|
129
|
+
},
|
|
105
130
|
buildAccountSnapshot: (params: any) => {
|
|
106
131
|
const account = params.account;
|
|
107
132
|
return {
|
|
108
133
|
...params.runtime,
|
|
109
|
-
accountId: params.accountId
|
|
134
|
+
accountId: params.accountId || account?.accountId || DEFAULT_ACCOUNT_ID,
|
|
110
135
|
enabled: isEnabled(account?.enabled),
|
|
111
136
|
configured: isConfigured(account),
|
|
112
137
|
};
|
|
@@ -220,7 +245,7 @@ export function createTuiTuiChannelPlugin(apiRuntime: any) {
|
|
|
220
245
|
wsNumber++;
|
|
221
246
|
const wsId = `${wsNumber}-${Date.now()}`;
|
|
222
247
|
const wsEvtIds = new Set<string>();
|
|
223
|
-
let wsRetryTimerId = 0;
|
|
248
|
+
let wsRetryTimerId: any = 0;
|
|
224
249
|
|
|
225
250
|
const wsUrl = `wss://im.live.360.cn:8282/robot/callback/ws?auth=${account.appId}.${account.appSecret}`;
|
|
226
251
|
const defSendMsg = (msg: any) => {
|
package/src/confs.ts
CHANGED
|
@@ -13,8 +13,8 @@ export const capabilities = {
|
|
|
13
13
|
|
|
14
14
|
/*** 一些配置字段设定信息 ***/
|
|
15
15
|
const baseFields = {
|
|
16
|
-
appId: { type: 'string' } ,
|
|
17
|
-
appSecret: { type: 'string' },
|
|
16
|
+
appId: { type: 'string', default: '' } ,
|
|
17
|
+
appSecret: { type: 'string', default: '' },
|
|
18
18
|
dmPolicy: {
|
|
19
19
|
type: 'string',
|
|
20
20
|
default: 'pairing',
|
|
@@ -22,6 +22,7 @@ const baseFields = {
|
|
|
22
22
|
},
|
|
23
23
|
allowFrom: {
|
|
24
24
|
type: 'array',
|
|
25
|
+
default: [],
|
|
25
26
|
items: { type: 'string' }
|
|
26
27
|
},
|
|
27
28
|
groupPolicy: {
|
|
@@ -35,6 +36,7 @@ const baseFields = {
|
|
|
35
36
|
},
|
|
36
37
|
groupAllowFrom: {
|
|
37
38
|
type: 'array',
|
|
39
|
+
default: [],
|
|
38
40
|
items: { type: 'string' }
|
|
39
41
|
},
|
|
40
42
|
channelContext: {
|
package/src/histories.ts
CHANGED
|
@@ -1,8 +1,70 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
const DEFAULT_GROUP_HISTORY_LIMIT = 50;
|
|
2
|
+
const MAX_HISTORY_KEYS = 1000;
|
|
3
|
+
type HistoryEntry = {
|
|
4
|
+
sender: string;
|
|
5
|
+
body: string;
|
|
6
|
+
timestamp?: number;
|
|
7
|
+
messageId?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
function evictOldHistoryKeys<T>(
|
|
11
|
+
historyMap: Map<string, T[]>,
|
|
12
|
+
maxKeys: number = MAX_HISTORY_KEYS,
|
|
13
|
+
): void {
|
|
14
|
+
if (historyMap.size <= maxKeys) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const keysToDelete = historyMap.size - maxKeys;
|
|
18
|
+
const iterator = historyMap.keys();
|
|
19
|
+
for (let i = 0; i < keysToDelete; i++) {
|
|
20
|
+
const key = iterator.next().value;
|
|
21
|
+
if (key !== undefined) {
|
|
22
|
+
historyMap.delete(key);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function appendHistoryEntry<T extends HistoryEntry>(params: {
|
|
28
|
+
historyMap: Map<string, T[]>;
|
|
29
|
+
historyKey: string;
|
|
30
|
+
entry: T;
|
|
31
|
+
limit: number;
|
|
32
|
+
}): T[] {
|
|
33
|
+
const { historyMap, historyKey, entry } = params;
|
|
34
|
+
if (params.limit <= 0) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
const history = historyMap.get(historyKey) ?? [];
|
|
38
|
+
history.push(entry);
|
|
39
|
+
while (history.length > params.limit) {
|
|
40
|
+
history.shift();
|
|
41
|
+
}
|
|
42
|
+
if (historyMap.has(historyKey)) {
|
|
43
|
+
// Refresh insertion order so eviction keeps recently used histories.
|
|
44
|
+
historyMap.delete(historyKey);
|
|
45
|
+
}
|
|
46
|
+
historyMap.set(historyKey, history);
|
|
47
|
+
// Evict oldest keys if map exceeds max size to prevent unbounded memory growth
|
|
48
|
+
evictOldHistoryKeys(historyMap);
|
|
49
|
+
return history;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function recordPendingHistoryEntryIfEnabled<T extends HistoryEntry>(params: {
|
|
53
|
+
historyMap: Map<string, T[]>;
|
|
54
|
+
historyKey: string;
|
|
55
|
+
entry?: T | null;
|
|
56
|
+
limit: number;
|
|
57
|
+
}): T[] {
|
|
58
|
+
if (!params.entry || params.limit <= 0) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
return appendHistoryEntry({
|
|
62
|
+
historyMap: params.historyMap,
|
|
63
|
+
historyKey: params.historyKey,
|
|
64
|
+
entry: params.entry,
|
|
65
|
+
limit: params.limit,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
6
68
|
|
|
7
69
|
// 每个 accountId 独立的 group history map,防止多账户冲突
|
|
8
70
|
const accountGroupHistories = new Map<string, Map<string, HistoryEntry[]>>();
|
|
@@ -10,7 +72,7 @@ const accountGroupHistories = new Map<string, Map<string, HistoryEntry[]>>();
|
|
|
10
72
|
/**
|
|
11
73
|
* 记录没有被@,被忽略的群消息到对应 account 的 pending history 中
|
|
12
74
|
*/
|
|
13
|
-
export async function addUnmentionedHistory(apiRuntime, accountId: string, chatId: string, tuituiUserName: any, tuituiAccount: string, text: any, timestamp :any) {
|
|
75
|
+
export async function addUnmentionedHistory(apiRuntime: any, accountId: string, chatId: string, tuituiUserName: any, tuituiAccount: string, text: any, timestamp :any) {
|
|
14
76
|
const cfg = await apiRuntime.config.loadConfig();
|
|
15
77
|
const senderDesc = tuituiUserName ? `${tuituiUserName} (${tuituiAccount})` : tuituiAccount;
|
|
16
78
|
|
package/src/outbound.ts
CHANGED
|
@@ -376,7 +376,7 @@ function getTargets(chatId: string, chatType: ChatType): TuiTuiToTargets {
|
|
|
376
376
|
|
|
377
377
|
function replaceSingleNewlines(content: string): string {
|
|
378
378
|
// 匹配单个换行符,前后都不是换行符
|
|
379
|
-
return content.replace(/([^\n])\n([^\n])/g, '$1\n\n$2');
|
|
379
|
+
return content.replace(/([^\n|])\n([^\n|])/g, '$1\n\n$2');
|
|
380
380
|
}
|
|
381
381
|
|
|
382
382
|
/*
|