mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 06:30:45 +08:00
137 lines
5.5 KiB
JavaScript
137 lines
5.5 KiB
JavaScript
import * as React from 'react';
|
||
|
||
import { TelegramLogoGlyph } from '../../channel-logos.js';
|
||
import { createTokenChannelSettings } from '../shared/token-channel.js';
|
||
import { h } from '../../i18n.js';
|
||
import {
|
||
TELEGRAM_ENDPOINTS,
|
||
telegramClientApi,
|
||
} from './api.js';
|
||
import { installTelegramStyles } from './styles.js';
|
||
|
||
function policyFor(account) {
|
||
return {
|
||
accessMode: account?.accessPolicy?.accessMode === 'private-allowlist'
|
||
? 'private-allowlist' : 'compatible',
|
||
allowedUsers: Array.isArray(account?.accessPolicy?.allowedUsers)
|
||
? account.accessPolicy.allowedUsers : [],
|
||
};
|
||
}
|
||
|
||
function allowedUsersFromText(value) {
|
||
const entries = value.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
|
||
if (entries.some((entry) => !/^[1-9]\d{0,15}$/.test(entry))) {
|
||
throw new TypeError('User ID 必须是 1–16 位正整数,每行一个。');
|
||
}
|
||
return [...new Set(entries)];
|
||
}
|
||
|
||
export function TelegramAccessSettings({ account, busy = false, onSave }) {
|
||
const policy = policyFor(account);
|
||
const sourceUsers = policy.allowedUsers.join('\n');
|
||
const accessHelpId = React.useId();
|
||
const [accessMode, setAccessMode] = React.useState(policy.accessMode);
|
||
const [allowedUsers, setAllowedUsers] = React.useState(sourceUsers);
|
||
const [error, setError] = React.useState(null);
|
||
|
||
React.useEffect(() => {
|
||
setAccessMode(policy.accessMode);
|
||
setAllowedUsers(sourceUsers);
|
||
setError(null);
|
||
}, [policy.accessMode, sourceUsers]);
|
||
|
||
const save = async (event) => {
|
||
event.preventDefault();
|
||
setError(null);
|
||
try {
|
||
const normalized = allowedUsersFromText(allowedUsers);
|
||
if (typeof onSave !== 'function') throw new Error('Telegram 访问设置暂不可用。');
|
||
await onSave({ accessMode, allowedUsers: normalized });
|
||
} catch (caught) {
|
||
setError(caught?.message ?? 'Telegram 访问设置保存失败。');
|
||
}
|
||
};
|
||
|
||
const privateAllowlist = accessMode === 'private-allowlist';
|
||
const savedPrivateAllowlist = policy.accessMode === 'private-allowlist';
|
||
const emptyAllowlist = privateAllowlist && allowedUsers.trim() === '';
|
||
return h('form', { className: 'dtg-access', onSubmit: save },
|
||
h('div', { className: 'dtg-accessHeading' },
|
||
h('strong', null, '访问设置'),
|
||
h('span', { className: 'dtg-accessStatus' },
|
||
h('span', { className: 'dtg-accessBadge', 'data-mode': policy.accessMode },
|
||
savedPrivateAllowlist ? '已生效:安全模式' : '已生效:兼容模式'),
|
||
h('span', { className: 'dtg-accessHelp' },
|
||
h('button', {
|
||
type: 'button',
|
||
className: 'dtg-accessHelpButton',
|
||
'aria-label': '查看 Telegram 访问模式说明',
|
||
'aria-describedby': accessHelpId,
|
||
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
||
h('span', {
|
||
id: accessHelpId,
|
||
className: 'dtg-accessTooltip',
|
||
role: 'tooltip',
|
||
},
|
||
h('span', { className: 'dtg-accessTooltipItem' },
|
||
h('strong', null, '兼容模式'),
|
||
h('span', null, '保持原有行为:私聊直接响应,群聊在被提及或回复时响应。')),
|
||
h('span', { className: 'dtg-accessTooltipItem' },
|
||
h('strong', null, '安全模式'),
|
||
h('span', null, '群聊全部忽略,私聊仅允许白名单用户。')))))),
|
||
h('label', { className: 'dtg-accessField' },
|
||
h('span', null, '模式'),
|
||
h('select', {
|
||
value: accessMode,
|
||
disabled: busy,
|
||
'aria-label': 'Telegram 访问模式',
|
||
onChange: (event) => { setAccessMode(event.target.value); setError(null); },
|
||
},
|
||
h('option', { value: 'compatible' }, '兼容模式(默认)'),
|
||
h('option', { value: 'private-allowlist' }, '安全模式(私聊白名单)'))),
|
||
h('label', { className: 'dtg-accessField' },
|
||
h('span', null, '允许私聊的 Telegram User ID'),
|
||
h('textarea', {
|
||
value: allowedUsers,
|
||
disabled: busy || !privateAllowlist,
|
||
rows: 3,
|
||
placeholder: '每行一个数字 User ID',
|
||
'aria-label': '允许私聊的 Telegram User ID',
|
||
onChange: (event) => { setAllowedUsers(event.target.value); setError(null); },
|
||
}),
|
||
h('small', null, privateAllowlist
|
||
? '白名单仅属于当前机器人。'
|
||
: '兼容模式下暂不使用白名单,切换模式时会保留。')),
|
||
emptyAllowlist
|
||
? h('p', { className: 'dtg-accessWarning', role: 'status' },
|
||
'白名单为空;保存后该机器人会拒绝所有入站消息。')
|
||
: null,
|
||
error ? h('p', { className: 'dtg-accessError', role: 'alert' }, error) : null,
|
||
h('div', { className: 'dtg-accessActions' },
|
||
h('button', {
|
||
type: 'submit',
|
||
className: 'ddt-button',
|
||
'data-kind': 'secondary',
|
||
disabled: busy,
|
||
}, busy ? '正在保存…' : '保存访问设置')));
|
||
}
|
||
|
||
const channel = createTokenChannelSettings({
|
||
channel: 'Telegram',
|
||
endpoints: TELEGRAM_ENDPOINTS,
|
||
api: telegramClientApi,
|
||
LogoGlyph: TelegramLogoGlyph,
|
||
installStyles: installTelegramStyles,
|
||
pageClass: 'dtg-page',
|
||
avatarClass: 'dtg-avatar',
|
||
connectionLabel: 'Bot API 长轮询',
|
||
tokenPlaceholder: '填写 @BotFather 生成的 Bot Token',
|
||
emptyTitle: '接入 Telegram 机器人',
|
||
emptyDescription: '先通过 @BotFather 获取 Bot Token,再在这里完成接入。',
|
||
platformLabel: 'Telegram',
|
||
AccountSettings: TelegramAccessSettings,
|
||
accountSettingsEndpoint: TELEGRAM_ENDPOINTS.setAccessPolicy,
|
||
});
|
||
|
||
export const TelegramSettingsTab = channel.SettingsTab;
|
||
export const TelegramAccountCard = channel.AccountCard;
|