mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-08 23:13:17 +08:00
Sync bot chat language with DSH system locale.
Localize WhatsApp access-grant approvals via t() and push Host language from the UI on load and locale change. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
6107692418
commit
45f430fb28
10 changed files with 181 additions and 33 deletions
|
|
@ -191,7 +191,32 @@ test('quote approval helpers parse intent, pending id, and notify refs', () => {
|
|||
pushName: 'Carol',
|
||||
requestText: 'hi',
|
||||
});
|
||||
assert.match(body, /请求编号:p_abc123/);
|
||||
assert.equal(parsePendingIdFromNotifyText(body), 'p_abc123');
|
||||
const enBody = formatPendingNotifyBody({
|
||||
id: 'p_abc123',
|
||||
kind: 'direct',
|
||||
phone: '8618888888888',
|
||||
pushName: 'Carol',
|
||||
requestText: 'hi',
|
||||
}, (text, params) => {
|
||||
const map = {
|
||||
'[dsh-im-ops] 访问申请': '[dsh-im-ops] Access request',
|
||||
'私聊': 'Direct chat',
|
||||
'场景:{scene}': 'Scene: {scene}',
|
||||
'申请人:{who}': 'Requester: {who}',
|
||||
'请求编号:{id}': 'Request: {id}',
|
||||
'原文:{text}': 'Original message: {text}',
|
||||
'请引用本条消息回复「同意」或「拒绝」。': 'Quote this message…',
|
||||
'未知': 'unknown',
|
||||
};
|
||||
const translated = map[text] ?? text;
|
||||
if (params == null) return translated;
|
||||
return translated.replace(/\{(\w+)\}/g, (match, name) =>
|
||||
Object.hasOwn(params, name) ? String(params[name]) : match);
|
||||
});
|
||||
assert.match(enBody, /Request: p_abc123/);
|
||||
assert.equal(parsePendingIdFromNotifyText(enBody), 'p_abc123');
|
||||
const withRef = {
|
||||
...grant(),
|
||||
pending: [{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { createUpdateRpcHandler, installUpdateRpc } from '../plugin-src/host/update-rpc.mjs';
|
||||
import { createUpdateRpcHandler, installUpdateRpc, HOST_LANGUAGE_SET_ENDPOINT } from '../plugin-src/host/update-rpc.mjs';
|
||||
import { createImHostPlugin } from '../plugin-src/host/index.mjs';
|
||||
import { getImHostLanguage, setImHostLanguage } from '../src/channels/shared/i18n.mjs';
|
||||
|
||||
test('update RPC rejects arbitrary commands, paths, profiles, sources and invalid request identifiers', async () => {
|
||||
const calls = [];
|
||||
|
|
@ -13,22 +14,53 @@ test('update RPC rejects arbitrary commands, paths, profiles, sources and invali
|
|||
['update.install', { checkId: 'id', requestId: 'request', command: 'anything' }],
|
||||
['update.install', { checkId: 'id', requestId: '; rm -rf /' }],
|
||||
['update.install', { checkId: 'id', requestId: 'request', version: '9.0.0' }],
|
||||
[HOST_LANGUAGE_SET_ENDPOINT, {}],
|
||||
[HOST_LANGUAGE_SET_ENDPOINT, { language: 'en', extra: true }],
|
||||
[HOST_LANGUAGE_SET_ENDPOINT, { language: '' }],
|
||||
]) {
|
||||
assert.equal((await handle(endpoint, payload)).error.code, 'bad-request');
|
||||
}
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('update RPC keeps local authority even when other management uses trusted-host', () => {
|
||||
const calls = [];
|
||||
test('host.language.set follows DSH locale ids and returns the normalized host language', async () => {
|
||||
const handle = createUpdateRpcHandler({});
|
||||
setImHostLanguage('zh');
|
||||
try {
|
||||
assert.deepEqual(await handle(HOST_LANGUAGE_SET_ENDPOINT, { language: 'en' }), {
|
||||
ok: true, value: { language: 'en' },
|
||||
});
|
||||
assert.equal(getImHostLanguage(), 'en');
|
||||
assert.deepEqual(await handle(HOST_LANGUAGE_SET_ENDPOINT, { language: 'zh-CN' }), {
|
||||
ok: true, value: { language: 'zh' },
|
||||
});
|
||||
assert.equal(getImHostLanguage(), 'zh');
|
||||
} finally {
|
||||
setImHostLanguage('zh');
|
||||
}
|
||||
});
|
||||
|
||||
test('update RPC mounts on /dsh-im through webServer', () => {
|
||||
const routes = [];
|
||||
const service = { close: async () => {} };
|
||||
const ctx = {
|
||||
connection: { rpc: { handle: (...args) => calls.push(args) } },
|
||||
effect: () => {},
|
||||
connection: {
|
||||
requestRejection: () => undefined,
|
||||
},
|
||||
webServer: {
|
||||
register: (route) => {
|
||||
routes.push(route);
|
||||
return () => {};
|
||||
},
|
||||
},
|
||||
effect: (factory) => {
|
||||
const dispose = factory();
|
||||
return typeof dispose === 'function' ? dispose : () => {};
|
||||
},
|
||||
};
|
||||
installUpdateRpc(ctx, { service, runtime: {} });
|
||||
assert.equal(calls[0][0], '/dsh-im');
|
||||
assert.deepEqual(calls[0][2], { authority: 'loopback' });
|
||||
assert.equal(routes[0]?.path, '/dsh-im');
|
||||
assert.equal(routes[0]?.kind, 'prefix');
|
||||
});
|
||||
|
||||
test('update RPC returns only safe codes for unanticipated runtime errors', async () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue