diff --git a/src/app/api/telegram/bind/route.ts b/src/app/api/telegram/bind/route.ts index 4b1b7ae..2233a8d 100644 --- a/src/app/api/telegram/bind/route.ts +++ b/src/app/api/telegram/bind/route.ts @@ -5,6 +5,7 @@ import { createTelegramBindSession, getTelegramBinding, getTelegramConfig, + getTelegramConfigProblems, getTelegramDeepLink, } from '@/lib/telegram'; @@ -18,9 +19,11 @@ export async function GET(request: NextRequest) { const config = await getTelegramConfig(); const binding = await getTelegramBinding(authInfo.username); + const problems = getTelegramConfigProblems(config, 'binding'); return NextResponse.json({ - enabled: config.enabled && config.bindingEnabled && Boolean(config.botToken), - botUsername: config.botUsername, + enabled: problems.length === 0, + problems, + botUsername: config.botUsername.replace(/^@/, ''), binding, }); } @@ -32,15 +35,26 @@ export async function POST(request: NextRequest) { } const config = await getTelegramConfig(); - if (!config.enabled || !config.bindingEnabled || !config.botToken) { - return NextResponse.json({ error: 'Telegram Bot 未启用' }, { status: 400 }); + const problems = getTelegramConfigProblems(config, 'binding'); + if (problems.length > 0) { + return NextResponse.json({ + error: `Telegram 绑定不可用:${problems.join('、')}`, + config: { + enabled: config.enabled, + bindingEnabled: config.bindingEnabled, + hasBotToken: Boolean(config.botToken), + hasBotUsername: Boolean(config.botUsername), + botUsername: config.botUsername || '', + }, + }, { status: 400 }); } const session = await createTelegramBindSession(authInfo.username); + const botUsername = config.botUsername.replace(/^@/, ''); return NextResponse.json({ code: session.code, expiresAt: session.expiresAt, - botUsername: config.botUsername, - deepLink: config.botUsername ? getTelegramDeepLink(config.botUsername, `bind_${session.code}`) : '', + botUsername, + deepLink: getTelegramDeepLink(botUsername, `bind_${session.code}`), }); } diff --git a/src/app/api/telegram/config/route.ts b/src/app/api/telegram/config/route.ts index b89e178..4aaf499 100644 --- a/src/app/api/telegram/config/route.ts +++ b/src/app/api/telegram/config/route.ts @@ -1,16 +1,19 @@ import { NextResponse } from 'next/server'; -import { getTelegramConfig } from '@/lib/telegram'; +import { getTelegramConfig, getTelegramConfigProblems } from '@/lib/telegram'; export const runtime = 'nodejs'; export async function GET() { const config = await getTelegramConfig(); + const loginProblems = getTelegramConfigProblems(config, 'login'); + const bindingProblems = getTelegramConfigProblems(config, 'binding'); return NextResponse.json({ enabled: config.enabled && Boolean(config.botToken), - botUsername: config.botUsername, - loginEnabled: config.loginEnabled, - bindingEnabled: config.bindingEnabled, + botUsername: config.botUsername.replace(/^@/, ''), + loginEnabled: loginProblems.length === 0, + bindingEnabled: bindingProblems.length === 0, notificationsEnabled: config.notificationsEnabled, + problems: Array.from(new Set([...loginProblems, ...bindingProblems])), }); } diff --git a/src/app/api/telegram/login/create/route.ts b/src/app/api/telegram/login/create/route.ts index 68eb40d..4fcd504 100644 --- a/src/app/api/telegram/login/create/route.ts +++ b/src/app/api/telegram/login/create/route.ts @@ -3,6 +3,7 @@ import { NextResponse } from 'next/server'; import { createTelegramLoginSession, getTelegramConfig, + getTelegramConfigProblems, getTelegramDeepLink, } from '@/lib/telegram'; @@ -10,15 +11,26 @@ export const runtime = 'nodejs'; export async function POST() { const config = await getTelegramConfig(); - if (!config.enabled || !config.loginEnabled || !config.botToken || !config.botUsername) { - return NextResponse.json({ error: 'Telegram 登录未启用' }, { status: 400 }); + const problems = getTelegramConfigProblems(config, 'login'); + if (problems.length > 0) { + return NextResponse.json({ + error: `Telegram 登录不可用:${problems.join('、')}`, + config: { + enabled: config.enabled, + loginEnabled: config.loginEnabled, + hasBotToken: Boolean(config.botToken), + hasBotUsername: Boolean(config.botUsername), + botUsername: config.botUsername || '', + }, + }, { status: 400 }); } const session = await createTelegramLoginSession(); + const botUsername = config.botUsername.replace(/^@/, ''); return NextResponse.json({ token: session.token, expiresAt: session.expiresAt, - botUsername: config.botUsername, - deepLink: getTelegramDeepLink(config.botUsername, `login_${session.token}`), + botUsername, + deepLink: getTelegramDeepLink(botUsername, `login_${session.token}`), }); } diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 66211a1..5a55a0e 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -288,7 +288,10 @@ function LoginPageClient() { const createRes = await fetch('/api/telegram/login/create', { method: 'POST' }); const createData = await createRes.json().catch(() => ({})); if (!createRes.ok) { - setError(createData.error || 'Telegram 登录未启用'); + const configDetail = createData.config + ? `(enabled=${String(createData.config.enabled)}, loginEnabled=${String(createData.config.loginEnabled)}, hasBotToken=${String(createData.config.hasBotToken)}, hasBotUsername=${String(createData.config.hasBotUsername)}, botUsername=${createData.config.botUsername || '-'})` + : `(HTTP ${createRes.status})`; + setError(`${createData.error || 'Telegram 登录接口不可用'}${configDetail}`); return; } diff --git a/src/lib/telegram.ts b/src/lib/telegram.ts index 6cb517c..225b02a 100644 --- a/src/lib/telegram.ts +++ b/src/lib/telegram.ts @@ -481,7 +481,21 @@ export async function dispatchTelegramNotification( } export function getTelegramDeepLink(botUsername: string, payload: string) { - return `https://t.me/${botUsername}?start=${encodeURIComponent(payload)}`; + return `https://t.me/${botUsername.replace(/^@/, '')}?start=${encodeURIComponent(payload)}`; +} + +export function getTelegramConfigProblems( + config: TelegramConfig, + feature?: 'login' | 'binding' | 'notifications' +): string[] { + const problems: string[] = []; + if (!config.enabled) problems.push('总开关未开启'); + if (!config.botToken) problems.push('Bot Token 为空'); + if ((feature === 'login' || feature === 'binding') && !config.botUsername) problems.push('Bot 用户名为空'); + if (feature === 'login' && !config.loginEnabled) problems.push('Telegram 登录开关未开启'); + if (feature === 'binding' && !config.bindingEnabled) problems.push('Telegram 绑定开关未开启'); + if (feature === 'notifications' && !config.notificationsEnabled) problems.push('Telegram 通知开关未开启'); + return problems; } function parseMessageText(update: any) { @@ -500,6 +514,16 @@ function parseMessageText(update: any) { export async function handleTelegramWebhookUpdate(update: any): Promise { const parsed = parseMessageText(update); if (parsed) { + if (/^\/start$/i.test(parsed.text)) { + await sendTelegramMessage(parsed.chatId, 'MoonTVPlus Telegram Bot 已连接。\n\n可用命令:\n/bind 绑定码 - 绑定账号\n/status - 查看状态\n/unbind - 解除绑定'); + return; + } + + if (/^\/bind$/i.test(parsed.text)) { + await sendTelegramMessage(parsed.chatId, '请先在站内「账号/通知设置」里生成 6 位 Telegram 绑定码,然后发送:\n/bind 123456'); + return; + } + const startLoginMatch = parsed.text.match(/^\/start\s+login_(.+)$/i); if (startLoginMatch) { try { diff --git a/src/middleware.ts b/src/middleware.ts index 8f3770d..8afb5af 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -189,6 +189,6 @@ function isTVModePath(pathname: string): boolean { // 配置middleware匹配规则 export const config = { matcher: [ - '/((?!_next/static|_next/image|favicon.ico|login|register|oidc-register|qr-login|warning|tv/login|api/login|api/register|api/logout|api/auth/oidc|api/auth/qr|api/auth/refresh|api/cron/|api/server-config|api/proxy-m3u8|api/cms-proxy|api/tvbox/subscribe|api/theme/css|api/openlist/cms-proxy|api/openlist/play|api/emby/cms-proxy|api/emby/play|api/emby/subtitle|api/emby/sources|tvbox/).*)', + '/((?!_next/static|_next/image|favicon.ico|login|register|oidc-register|qr-login|warning|tv/login|api/login|api/register|api/logout|api/auth/oidc|api/auth/qr|api/auth/refresh|api/telegram/login|api/telegram/config|api/telegram/webhook|api/cron/|api/server-config|api/proxy-m3u8|api/cms-proxy|api/tvbox/subscribe|api/theme/css|api/openlist/cms-proxy|api/openlist/play|api/emby/cms-proxy|api/emby/play|api/emby/subtitle|api/emby/sources|tvbox/).*)', ], };