feat: Переделка Telegram интеграции на единого бота (v2.1.0)
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 54s

- Единый бот для всех пользователей (токен из .env)
- Deep link для подключения через /start команду
- Отдельная таблица todoist_integrations для Todoist webhook
- Персональные отчеты для каждого пользователя
- Автоматическое применение миграции 012 при старте
- Обновлен Frontend: кнопка подключения вместо поля ввода токена
This commit is contained in:
Play Life Bot
2026-01-02 14:47:51 +03:00
parent 4df054536a
commit 8ba7e8fd45
5 changed files with 845 additions and 609 deletions

View File

@@ -4,12 +4,9 @@ import './Integrations.css'
function TelegramIntegration({ onBack }) {
const { authFetch } = useAuth()
const [botToken, setBotToken] = useState('')
const [chatId, setChatId] = useState('')
const [integration, setIntegration] = useState(null)
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
const [error, setError] = useState('')
const [success, setSuccess] = useState('')
useEffect(() => {
fetchIntegration()
@@ -23,8 +20,7 @@ function TelegramIntegration({ onBack }) {
throw new Error('Ошибка при загрузке интеграции')
}
const data = await response.json()
setBotToken(data.bot_token || '')
setChatId(data.chat_id || '')
setIntegration(data)
} catch (error) {
console.error('Error fetching integration:', error)
setError('Не удалось загрузить данные интеграции')
@@ -33,40 +29,22 @@ function TelegramIntegration({ onBack }) {
}
}
const handleSave = async () => {
if (!botToken.trim()) {
setError('Bot Token обязателен для заполнения')
return
const handleOpenBot = () => {
if (integration?.deep_link) {
window.open(integration.deep_link, '_blank')
}
}
try {
setSaving(true)
setError('')
setSuccess('')
const handleRefresh = () => {
fetchIntegration()
}
const response = await authFetch('/api/integrations/telegram', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ bot_token: botToken }),
})
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || 'Ошибка при сохранении')
}
const data = await response.json()
setBotToken(data.bot_token || '')
setChatId(data.chat_id || '')
setSuccess('Bot Token успешно сохранен!')
} catch (error) {
console.error('Error saving integration:', error)
setError(error.message || 'Не удалось сохранить Bot Token')
} finally {
setSaving(false)
}
if (loading) {
return (
<div className="p-4 md:p-6">
<div className="text-gray-500">Загрузка...</div>
</div>
)
}
return (
@@ -77,96 +55,77 @@ function TelegramIntegration({ onBack }) {
<h1 className="text-2xl font-bold mb-6">Telegram интеграция</h1>
{loading ? (
<div className="text-gray-500">Загрузка...</div>
) : (
<>
<div className="bg-white rounded-lg shadow-md p-6 mb-6">
<h2 className="text-lg font-semibold mb-4">Настройки</h2>
{error && (
<div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg text-red-700">
{error}
</div>
)}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-2">
Telegram Bot Token
</label>
<input
type="text"
value={botToken}
onChange={(e) => setBotToken(e.target.value)}
placeholder="Введите Bot Token"
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
/>
<div className="bg-white rounded-lg shadow-md p-6 mb-6">
<h2 className="text-lg font-semibold mb-4">Статус подключения</h2>
{integration?.is_connected ? (
<div className="space-y-4">
<div className="p-4 bg-green-50 border border-green-200 rounded-lg">
<div className="flex items-center text-green-700">
<span className="text-xl mr-2"></span>
<span className="font-medium">Telegram подключен</span>
</div>
</div>
{chatId && (
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-2">
Chat ID (устанавливается автоматически)
</label>
<input
type="text"
value={chatId}
readOnly
className="w-full px-4 py-2 border border-gray-300 rounded-lg bg-gray-50"
/>
</div>
)}
{error && (
<div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg text-red-700 text-sm">
{error}
</div>
)}
{success && (
<div className="mb-4 p-3 bg-green-50 border border-green-200 rounded-lg text-green-700 text-sm">
{success}
{integration.telegram_user_id && (
<div className="text-sm text-gray-600">
Telegram ID: <span className="font-mono">{integration.telegram_user_id}</span>
</div>
)}
<button
onClick={handleSave}
disabled={saving || !botToken.trim()}
className="w-full px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed"
onClick={handleOpenBot}
className="w-full px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700"
>
{saving ? 'Сохранение...' : 'Сохранить Bot Token'}
Открыть бота
</button>
</div>
) : (
<div className="space-y-4">
<div className="p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
<div className="flex items-center text-yellow-700">
<span className="text-xl mr-2"></span>
<span className="font-medium">Telegram не подключен</span>
</div>
<p className="mt-2 text-sm text-gray-600">
Нажмите кнопку ниже и отправьте команду /start в боте
</p>
</div>
<div className="bg-blue-50 border border-blue-200 rounded-lg p-6 mb-6">
<h3 className="text-lg font-semibold mb-3 text-blue-900">
Откуда взять Bot Token
</h3>
<ol className="list-decimal list-inside space-y-2 text-gray-700">
<li>Откройте Telegram и найдите бота @BotFather</li>
<li>Отправьте команду /newbot</li>
<li>Следуйте инструкциям для создания нового бота</li>
<li>
После создания бота BotFather предоставит вам Bot Token
</li>
<li>Скопируйте токен и вставьте его в поле выше</li>
</ol>
</div>
<button
onClick={handleOpenBot}
disabled={!integration?.deep_link}
className="w-full px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
>
Подключить Telegram
</button>
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-6">
<h3 className="text-lg font-semibold mb-3 text-yellow-900">
Что нужно сделать после сохранения Bot Token
</h3>
<ol className="list-decimal list-inside space-y-2 text-gray-700">
<li>После сохранения Bot Token отправьте первое сообщение вашему боту в Telegram</li>
<li>
Chat ID будет автоматически сохранен после обработки первого
сообщения
</li>
<li>
После этого бот сможет отправлять вам ответные сообщения
</li>
</ol>
<button
onClick={handleRefresh}
className="w-full px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50"
>
Проверить подключение
</button>
</div>
</>
)}
)}
</div>
<div className="bg-blue-50 border border-blue-200 rounded-lg p-6">
<h3 className="text-lg font-semibold mb-3 text-blue-900">Инструкция</h3>
<ol className="list-decimal list-inside space-y-2 text-gray-700">
<li>Нажмите кнопку "Подключить Telegram"</li>
<li>В открывшемся Telegram нажмите "Start" или отправьте /start</li>
<li>Вернитесь сюда и нажмите "Проверить подключение"</li>
</ol>
</div>
</div>
)
}
export default TelegramIntegration