Добавлена полная инфраструктура интеграции мессенджеров: модели, миграции, админ-панель и документация по настройке
This commit is contained in:
parent
9243fb1b84
commit
510619668d
316
backend/communications/MESSENGER_INTEGRATION_GUIDE.md
Normal file
316
backend/communications/MESSENGER_INTEGRATION_GUIDE.md
Normal file
@ -0,0 +1,316 @@
|
||||
# Настройка интеграции мессенджеров
|
||||
|
||||
Этот документ описывает процесс настройки интеграции с WhatsApp, Telegram и WeChat.
|
||||
|
||||
## Архитектура
|
||||
|
||||
Система интеграции состоит из трех основных компонентов:
|
||||
|
||||
1. **MessengerIntegration** - настройки подключения к мессенджеру
|
||||
2. **MessengerAccount** - привязка аккаунтов сотрудников
|
||||
3. **MessengerMessage** - синхронизация сообщений
|
||||
|
||||
## Шаги настройки
|
||||
|
||||
### 1. Доступ к админ-панели
|
||||
|
||||
Перейдите в Django Admin: `http://your-domain/admin/communications/`
|
||||
|
||||
Вы увидите три новых раздела:
|
||||
- Интеграции с мессенджерами
|
||||
- Аккаунты в мессенджерах
|
||||
- Сообщения из мессенджеров
|
||||
|
||||
### 2. Настройка WhatsApp
|
||||
|
||||
#### 2.1. Получение API ключей
|
||||
|
||||
**Вариант A: WhatsApp Business API (официальный)**
|
||||
1. Зарегистрируйтесь на https://business.whatsapp.com/
|
||||
2. Создайте приложение в Facebook for Developers
|
||||
3. Получите `WhatsApp Business Account ID` и `Access Token`
|
||||
|
||||
**Вариант B: WhatsApp Web API (неофициальный, через Baileys/Whatsapp-web.js)**
|
||||
1. Используйте библиотеку для Node.js
|
||||
2. Настройте webhook для получения сообщений
|
||||
3. Получите QR-код для авторизации
|
||||
|
||||
#### 2.2. Настройка в системе
|
||||
|
||||
1. Откройте "Интеграции с мессенджерами" → "Добавить"
|
||||
2. Выберите тип: **WhatsApp**
|
||||
3. Заполните поля:
|
||||
- **Включена**: ✓
|
||||
- **Статус**: Active
|
||||
- **API ключ**: `<ваш Access Token>`
|
||||
- **Webhook URL**: `https://your-domain/communications/webhooks/whatsapp/`
|
||||
|
||||
4. В разделе "Дополнительные настройки" (JSON):
|
||||
```json
|
||||
{
|
||||
"phone_number_id": "YOUR_PHONE_NUMBER_ID",
|
||||
"business_account_id": "YOUR_BUSINESS_ACCOUNT_ID",
|
||||
"verify_token": "YOUR_WEBHOOK_VERIFY_TOKEN"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3. Настройка webhook на стороне WhatsApp
|
||||
|
||||
1. В Facebook for Developers → WhatsApp → Configuration
|
||||
2. Укажите webhook URL: `https://your-domain/communications/webhooks/whatsapp/`
|
||||
3. Укажите Verify Token (из настроек выше)
|
||||
4. Подпишитесь на события: `messages`, `message_status`
|
||||
|
||||
### 3. Настройка Telegram
|
||||
|
||||
#### 3.1. Создание бота
|
||||
|
||||
1. Найдите @BotFather в Telegram
|
||||
2. Отправьте команду `/newbot`
|
||||
3. Следуйте инструкциям для создания бота
|
||||
4. Получите **Bot Token** (формат: `123456789:ABCdefGHIjklMNOpqrsTUVwxyz`)
|
||||
|
||||
#### 3.2. Настройка в системе
|
||||
|
||||
1. Откройте "Интеграции с мессенджерами" → "Добавить"
|
||||
2. Выберите тип: **Telegram**
|
||||
3. Заполните поля:
|
||||
- **Включена**: ✓
|
||||
- **Статус**: Active
|
||||
- **API ключ**: `<Bot Token от BotFather>`
|
||||
- **Webhook URL**: `https://your-domain/communications/webhooks/telegram/`
|
||||
|
||||
4. В разделе "Дополнительные настройки":
|
||||
```json
|
||||
{
|
||||
"bot_username": "your_bot_name",
|
||||
"allowed_updates": ["message", "edited_message", "callback_query"]
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.3. Установка webhook
|
||||
|
||||
Выполните запрос (замените токен):
|
||||
```bash
|
||||
curl -X POST "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/setWebhook" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"url": "https://your-domain/communications/webhooks/telegram/"}'
|
||||
```
|
||||
|
||||
### 4. Настройка WeChat
|
||||
|
||||
#### 4.1. Регистрация WeChat Official Account
|
||||
|
||||
1. Зарегистрируйтесь на https://mp.weixin.qq.com/
|
||||
2. Создайте Official Account
|
||||
3. Получите `AppID` и `AppSecret`
|
||||
|
||||
#### 4.2. Настройка в системе
|
||||
|
||||
1. Откройте "Интеграции с мессенджерами" → "Добавить"
|
||||
2. Выберите тип: **WeChat**
|
||||
3. Заполните поля:
|
||||
- **Включена**: ✓
|
||||
- **Статус**: Active
|
||||
- **API ключ**: `<AppID>`
|
||||
- **API секрет**: `<AppSecret>`
|
||||
- **Webhook URL**: `https://your-domain/communications/webhooks/wechat/`
|
||||
|
||||
4. В разделе "Дополнительные настройки":
|
||||
```json
|
||||
{
|
||||
"token": "YOUR_TOKEN",
|
||||
"encoding_aes_key": "YOUR_ENCODING_AES_KEY"
|
||||
}
|
||||
```
|
||||
|
||||
## Привязка аккаунтов сотрудников
|
||||
|
||||
После настройки интеграции нужно привязать аккаунты сотрудников:
|
||||
|
||||
### Автоматическая привязка
|
||||
|
||||
Сотрудники могут сами привязать аккаунты:
|
||||
1. Перейти в профиль → "Мессенджеры"
|
||||
2. Выбрать мессенджер
|
||||
3. Следовать инструкциям (QR-код, код подтверждения и т.д.)
|
||||
|
||||
### Ручная привязка (админ)
|
||||
|
||||
1. Откройте "Аккаунты в мессенджерах" → "Добавить"
|
||||
2. Выберите пользователя
|
||||
3. Выберите интеграцию
|
||||
4. Укажите:
|
||||
- **External ID**: ID пользователя в мессенджере
|
||||
- **External Username**: Имя/никнейм
|
||||
- **Phone Number**: Телефон (для WhatsApp)
|
||||
5. Установите галочку "Подтвержден"
|
||||
|
||||
## Синхронизация сообщений
|
||||
|
||||
### Настройка Celery задач
|
||||
|
||||
Добавьте в `celery.py`:
|
||||
|
||||
```python
|
||||
from celery.schedules import crontab
|
||||
|
||||
app.conf.beat_schedule = {
|
||||
'sync-messenger-messages': {
|
||||
'task': 'communications.tasks.sync_messenger_messages',
|
||||
'schedule': 60.0, # каждую минуту
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Ручная синхронизация
|
||||
|
||||
Через Django shell:
|
||||
|
||||
```python
|
||||
from communications.services.messenger_sync import MessengerSyncService
|
||||
|
||||
# Синхронизация всех мессенджеров
|
||||
sync_service = MessengerSyncService()
|
||||
sync_service.sync_all()
|
||||
|
||||
# Синхронизация конкретного мессенджера
|
||||
sync_service.sync_messenger('whatsapp')
|
||||
```
|
||||
|
||||
## Webhook эндпоинты
|
||||
|
||||
Система автоматически создает эндпоинты для получения входящих сообщений:
|
||||
|
||||
- **WhatsApp**: `POST /communications/webhooks/whatsapp/`
|
||||
- **Telegram**: `POST /communications/webhooks/telegram/`
|
||||
- **WeChat**: `POST /communications/webhooks/wechat/`
|
||||
|
||||
Эти URL должны быть доступны извне (HTTPS обязателен для production).
|
||||
|
||||
## Безопасность
|
||||
|
||||
### Шифрование API ключей
|
||||
|
||||
В production используйте `django-cryptography` для шифрования:
|
||||
|
||||
```python
|
||||
# settings.py
|
||||
INSTALLED_APPS = [
|
||||
...
|
||||
'django_cryptography',
|
||||
]
|
||||
|
||||
# Генерация ключа
|
||||
from cryptography.fernet import Fernet
|
||||
FIELD_ENCRYPTION_KEY = Fernet.generate_key().decode()
|
||||
```
|
||||
|
||||
Обновите модель:
|
||||
```python
|
||||
from django_cryptography.fields import encrypt
|
||||
|
||||
class MessengerIntegration(models.Model):
|
||||
api_key = encrypt(models.CharField(max_length=500))
|
||||
api_secret = encrypt(models.CharField(max_length=500))
|
||||
```
|
||||
|
||||
### Проверка подписи webhook
|
||||
|
||||
Всегда проверяйте подпись входящих запросов:
|
||||
|
||||
```python
|
||||
# views.py
|
||||
def verify_whatsapp_signature(request):
|
||||
signature = request.headers.get('X-Hub-Signature-256')
|
||||
# Проверка HMAC
|
||||
...
|
||||
```
|
||||
|
||||
## Мониторинг
|
||||
|
||||
### Просмотр статистики
|
||||
|
||||
В админ-панели "Интеграции с мессенджерами" отображается:
|
||||
- Количество отправленных сообщений
|
||||
- Количество полученных сообщений
|
||||
- Время последней синхронизации
|
||||
- Последняя ошибка (если есть)
|
||||
|
||||
### Логирование
|
||||
|
||||
Все операции логируются:
|
||||
|
||||
```python
|
||||
import logging
|
||||
logger = logging.getLogger('communications.messenger')
|
||||
|
||||
# В settings.py
|
||||
LOGGING = {
|
||||
'loggers': {
|
||||
'communications.messenger': {
|
||||
'level': 'INFO',
|
||||
'handlers': ['file'],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Проблема: Webhook не получает сообщения
|
||||
|
||||
1. Проверьте, что URL доступен извне:
|
||||
```bash
|
||||
curl https://your-domain/communications/webhooks/telegram/
|
||||
```
|
||||
|
||||
2. Проверьте SSL сертификат (должен быть валидным)
|
||||
|
||||
3. Проверьте логи Django:
|
||||
```bash
|
||||
tail -f logs/django.log | grep webhook
|
||||
```
|
||||
|
||||
### Проблема: Сообщения не синхронизируются
|
||||
|
||||
1. Проверьте статус интеграции (должен быть "Active")
|
||||
2. Проверьте поле "Последняя ошибка"
|
||||
3. Запустите синхронизацию вручную через shell
|
||||
4. Проверьте Celery tasks (если используется)
|
||||
|
||||
### Проблема: Неверный API ключ
|
||||
|
||||
1. Перегенерируйте ключ в соответствующем сервисе
|
||||
2. Обновите в админ-панели
|
||||
3. Измените статус на "Active" после обновления
|
||||
|
||||
## Дальнейшее развитие
|
||||
|
||||
### Планируемые функции
|
||||
|
||||
- [ ] Групповые чаты в мессенджерах
|
||||
- [ ] Отправка медиа-файлов
|
||||
- [ ] Голосовые сообщения
|
||||
- [ ] Статусы доставки
|
||||
- [ ] Typing indicators
|
||||
- [ ] Реакции на сообщения
|
||||
- [ ] Шаблоны сообщений для WhatsApp Business
|
||||
|
||||
### API endpoints для разработчиков
|
||||
|
||||
```
|
||||
POST /api/v1/messenger/send/
|
||||
GET /api/v1/messenger/accounts/
|
||||
GET /api/v1/messenger/messages/
|
||||
POST /api/v1/messenger/link-account/
|
||||
```
|
||||
|
||||
Документация API: `https://your-domain/api/docs/`
|
||||
|
||||
## Поддержка
|
||||
|
||||
При возникновении проблем:
|
||||
1. Проверьте документацию мессенджера
|
||||
2. Проверьте логи системы
|
||||
3. Обратитесь к системному администратору
|
||||
@ -8,6 +8,9 @@ from .models import (
|
||||
Message,
|
||||
MessageAttachment,
|
||||
MessageReply,
|
||||
MessengerIntegration,
|
||||
MessengerAccount,
|
||||
MessengerMessage,
|
||||
)
|
||||
|
||||
|
||||
@ -169,3 +172,176 @@ class CrossChatMessageAdmin(admin.ModelAdmin):
|
||||
list_filter = ("status", "requires_moderation", "sent_at")
|
||||
search_fields = ("sender__last_name", "target_chat__name")
|
||||
ordering = ("-sent_at",)
|
||||
|
||||
|
||||
# ===== АДМИН-ПАНЕЛЬ ДЛЯ ИНТЕГРАЦИЙ С МЕССЕНДЖЕРАМИ =====
|
||||
|
||||
@admin.register(MessengerIntegration)
|
||||
class MessengerIntegrationAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
"messenger_type",
|
||||
"is_enabled",
|
||||
"status",
|
||||
"messages_sent",
|
||||
"messages_received",
|
||||
"last_sync_at",
|
||||
"updated_at"
|
||||
)
|
||||
list_filter = ("messenger_type", "is_enabled", "status")
|
||||
search_fields = ("messenger_type",)
|
||||
ordering = ("messenger_type",)
|
||||
|
||||
fieldsets = (
|
||||
("Основная информация", {
|
||||
"fields": ("messenger_type", "is_enabled", "status")
|
||||
}),
|
||||
("API Настройки", {
|
||||
"fields": ("api_key", "api_secret", "webhook_url", "settings"),
|
||||
"classes": ("collapse",)
|
||||
}),
|
||||
("Статистика", {
|
||||
"fields": (
|
||||
"messages_sent",
|
||||
"messages_received",
|
||||
"last_sync_at",
|
||||
"last_error"
|
||||
),
|
||||
"classes": ("collapse",)
|
||||
}),
|
||||
("Системная информация", {
|
||||
"fields": ("created_at", "updated_at"),
|
||||
"classes": ("collapse",)
|
||||
}),
|
||||
)
|
||||
|
||||
readonly_fields = (
|
||||
"messages_sent",
|
||||
"messages_received",
|
||||
"last_sync_at",
|
||||
"created_at",
|
||||
"updated_at"
|
||||
)
|
||||
|
||||
def get_readonly_fields(self, request, obj=None):
|
||||
if obj and obj.status == 'active':
|
||||
return self.readonly_fields + ("messenger_type",)
|
||||
return self.readonly_fields
|
||||
|
||||
|
||||
@admin.register(MessengerAccount)
|
||||
class MessengerAccountAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
"user",
|
||||
"integration",
|
||||
"external_username",
|
||||
"phone_number",
|
||||
"is_verified",
|
||||
"is_active",
|
||||
"updated_at"
|
||||
)
|
||||
list_filter = (
|
||||
"integration__messenger_type",
|
||||
"is_verified",
|
||||
"is_active"
|
||||
)
|
||||
search_fields = (
|
||||
"user__last_name",
|
||||
"user__first_name",
|
||||
"external_username",
|
||||
"phone_number",
|
||||
"external_id"
|
||||
)
|
||||
ordering = ("-created_at",)
|
||||
|
||||
fieldsets = (
|
||||
("Пользователь", {
|
||||
"fields": ("user", "integration")
|
||||
}),
|
||||
("Данные в мессенджере", {
|
||||
"fields": (
|
||||
"external_id",
|
||||
"external_username",
|
||||
"phone_number"
|
||||
)
|
||||
}),
|
||||
("Статус", {
|
||||
"fields": ("is_verified", "is_active")
|
||||
}),
|
||||
("Дополнительно", {
|
||||
"fields": ("metadata",),
|
||||
"classes": ("collapse",)
|
||||
}),
|
||||
("Системная информация", {
|
||||
"fields": ("created_at", "updated_at"),
|
||||
"classes": ("collapse",)
|
||||
}),
|
||||
)
|
||||
|
||||
readonly_fields = ("created_at", "updated_at")
|
||||
|
||||
|
||||
@admin.register(MessengerMessage)
|
||||
class MessengerMessageAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
"id",
|
||||
"integration",
|
||||
"direction",
|
||||
"status",
|
||||
"sender_account",
|
||||
"content_preview",
|
||||
"external_timestamp",
|
||||
"synced_at"
|
||||
)
|
||||
list_filter = (
|
||||
"integration__messenger_type",
|
||||
"direction",
|
||||
"status",
|
||||
"external_timestamp"
|
||||
)
|
||||
search_fields = (
|
||||
"external_id",
|
||||
"content",
|
||||
"sender_account__external_username"
|
||||
)
|
||||
ordering = ("-external_timestamp",)
|
||||
|
||||
fieldsets = (
|
||||
("Основная информация", {
|
||||
"fields": (
|
||||
"integration",
|
||||
"internal_message",
|
||||
"external_id"
|
||||
)
|
||||
}),
|
||||
("Участники", {
|
||||
"fields": (
|
||||
"sender_account",
|
||||
"recipient_account",
|
||||
"direction"
|
||||
)
|
||||
}),
|
||||
("Содержимое", {
|
||||
"fields": ("content", "attachments")
|
||||
}),
|
||||
("Статус и время", {
|
||||
"fields": (
|
||||
"status",
|
||||
"external_timestamp",
|
||||
"synced_at"
|
||||
)
|
||||
}),
|
||||
("Дополнительно", {
|
||||
"fields": ("raw_data",),
|
||||
"classes": ("collapse",)
|
||||
}),
|
||||
)
|
||||
|
||||
readonly_fields = ("synced_at",)
|
||||
|
||||
def content_preview(self, obj):
|
||||
"""Превью содержимого сообщения"""
|
||||
if len(obj.content) > 50:
|
||||
return obj.content[:50] + "..."
|
||||
return obj.content
|
||||
|
||||
content_preview.short_description = "Содержимое"
|
||||
|
||||
@ -0,0 +1,119 @@
|
||||
# Generated by Django 5.2.4 on 2025-11-30 07:28
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('communications', '0012_chatmembership_chatreadstate_is_typing_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='MessengerIntegration',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('messenger_type', models.CharField(choices=[('whatsapp', 'WhatsApp'), ('telegram', 'Telegram'), ('wechat', 'WeChat')], max_length=20, unique=True, verbose_name='Тип мессенджера')),
|
||||
('is_enabled', models.BooleanField(default=False, verbose_name='Включена')),
|
||||
('status', models.CharField(choices=[('inactive', 'Не активна'), ('active', 'Активна'), ('error', 'Ошибка')], default='inactive', max_length=20, verbose_name='Статус')),
|
||||
('api_key', models.CharField(blank=True, max_length=500, verbose_name='API ключ')),
|
||||
('api_secret', models.CharField(blank=True, max_length=500, verbose_name='API секрет')),
|
||||
('webhook_url', models.URLField(blank=True, max_length=500, verbose_name='Webhook URL')),
|
||||
('settings', models.JSONField(blank=True, default=dict, verbose_name='Дополнительные настройки')),
|
||||
('last_sync_at', models.DateTimeField(blank=True, null=True, verbose_name='Последняя синхронизация')),
|
||||
('messages_sent', models.IntegerField(default=0, verbose_name='Отправлено сообщений')),
|
||||
('messages_received', models.IntegerField(default=0, verbose_name='Получено сообщений')),
|
||||
('last_error', models.TextField(blank=True, verbose_name='Последняя ошибка')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создано')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлено')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Интеграция с мессенджером',
|
||||
'verbose_name_plural': 'Интеграции с мессенджерами',
|
||||
'indexes': [models.Index(fields=['messenger_type', 'is_enabled'], name='communicati_messeng_c8457c_idx'), models.Index(fields=['status'], name='communicati_status_7fb887_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MessengerAccount',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('external_id', models.CharField(max_length=200, verbose_name='ID в мессенджере')),
|
||||
('external_username', models.CharField(blank=True, max_length=200, verbose_name='Имя в мессенджере')),
|
||||
('phone_number', models.CharField(blank=True, max_length=20, verbose_name='Номер телефона')),
|
||||
('is_verified', models.BooleanField(default=False, verbose_name='Подтвержден')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='Активен')),
|
||||
('metadata', models.JSONField(blank=True, default=dict, verbose_name='Метаданные')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создано')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлено')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messenger_accounts', to=settings.AUTH_USER_MODEL, verbose_name='Пользователь')),
|
||||
('integration', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accounts', to='communications.messengerintegration', verbose_name='Интеграция')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Аккаунт в мессенджере',
|
||||
'verbose_name_plural': 'Аккаунты в мессенджерах',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MessengerMessage',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('external_id', models.CharField(max_length=200, verbose_name='ID в мессенджере')),
|
||||
('direction', models.CharField(choices=[('incoming', 'Входящее'), ('outgoing', 'Исходящее')], max_length=10, verbose_name='Направление')),
|
||||
('status', models.CharField(choices=[('pending', 'В ожидании'), ('sent', 'Отправлено'), ('delivered', 'Доставлено'), ('read', 'Прочитано'), ('failed', 'Ошибка')], default='pending', max_length=20, verbose_name='Статус')),
|
||||
('content', models.TextField(verbose_name='Текст')),
|
||||
('attachments', models.JSONField(blank=True, default=list, verbose_name='Вложения')),
|
||||
('external_timestamp', models.DateTimeField(verbose_name='Время в мессенджере')),
|
||||
('synced_at', models.DateTimeField(auto_now_add=True, verbose_name='Синхронизировано')),
|
||||
('raw_data', models.JSONField(blank=True, default=dict, verbose_name='Исходные данные')),
|
||||
('integration', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messenger_messages', to='communications.messengerintegration', verbose_name='Интеграция')),
|
||||
('internal_message', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='messenger_sync', to='communications.message', verbose_name='Внутреннее сообщение')),
|
||||
('recipient_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='received_messages', to='communications.messengeraccount', verbose_name='Получатель')),
|
||||
('sender_account', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sent_messages', to='communications.messengeraccount', verbose_name='Отправитель')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Сообщение из мессенджера',
|
||||
'verbose_name_plural': 'Сообщения из мессенджеров',
|
||||
'ordering': ['-external_timestamp'],
|
||||
},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='messengeraccount',
|
||||
index=models.Index(fields=['user', 'integration'], name='communicati_user_id_acfa5e_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='messengeraccount',
|
||||
index=models.Index(fields=['external_id'], name='communicati_externa_2dfbcf_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='messengeraccount',
|
||||
index=models.Index(fields=['phone_number'], name='communicati_phone_n_8df544_idx'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='messengeraccount',
|
||||
unique_together={('integration', 'external_id')},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='messengermessage',
|
||||
index=models.Index(fields=['integration', 'external_timestamp'], name='communicati_integra_af291e_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='messengermessage',
|
||||
index=models.Index(fields=['sender_account', 'external_timestamp'], name='communicati_sender__496a25_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='messengermessage',
|
||||
index=models.Index(fields=['status'], name='communicati_status_9d880b_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='messengermessage',
|
||||
index=models.Index(fields=['-synced_at'], name='communicati_synced__4496a8_idx'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='messengermessage',
|
||||
unique_together={('integration', 'external_id')},
|
||||
),
|
||||
]
|
||||
@ -850,3 +850,289 @@ class ChatMembership(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.user} в {self.chat} ({self.get_role_display()})"
|
||||
|
||||
|
||||
# ===== МОДЕЛИ ДЛЯ ИНТЕГРАЦИИ МЕССЕНДЖЕРОВ =====
|
||||
|
||||
class MessengerIntegration(models.Model):
|
||||
"""
|
||||
Настройки интеграции с внешними мессенджерами (WhatsApp, Telegram, WeChat)
|
||||
"""
|
||||
MESSENGER_TYPES = [
|
||||
('whatsapp', 'WhatsApp'),
|
||||
('telegram', 'Telegram'),
|
||||
('wechat', 'WeChat'),
|
||||
]
|
||||
|
||||
STATUS_CHOICES = [
|
||||
('inactive', 'Не активна'),
|
||||
('active', 'Активна'),
|
||||
('error', 'Ошибка'),
|
||||
]
|
||||
|
||||
messenger_type = models.CharField(
|
||||
max_length=20,
|
||||
choices=MESSENGER_TYPES,
|
||||
unique=True,
|
||||
verbose_name="Тип мессенджера"
|
||||
)
|
||||
|
||||
is_enabled = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name="Включена"
|
||||
)
|
||||
|
||||
status = models.CharField(
|
||||
max_length=20,
|
||||
choices=STATUS_CHOICES,
|
||||
default='inactive',
|
||||
verbose_name="Статус"
|
||||
)
|
||||
|
||||
# API настройки (зашифрованы в production)
|
||||
api_key = models.CharField(
|
||||
max_length=500,
|
||||
blank=True,
|
||||
verbose_name="API ключ"
|
||||
)
|
||||
|
||||
api_secret = models.CharField(
|
||||
max_length=500,
|
||||
blank=True,
|
||||
verbose_name="API секрет"
|
||||
)
|
||||
|
||||
webhook_url = models.URLField(
|
||||
max_length=500,
|
||||
blank=True,
|
||||
verbose_name="Webhook URL"
|
||||
)
|
||||
|
||||
# Дополнительные настройки (JSON)
|
||||
settings = models.JSONField(
|
||||
default=dict,
|
||||
blank=True,
|
||||
verbose_name="Дополнительные настройки"
|
||||
)
|
||||
|
||||
# Статистика
|
||||
last_sync_at = models.DateTimeField(
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="Последняя синхронизация"
|
||||
)
|
||||
|
||||
messages_sent = models.IntegerField(
|
||||
default=0,
|
||||
verbose_name="Отправлено сообщений"
|
||||
)
|
||||
|
||||
messages_received = models.IntegerField(
|
||||
default=0,
|
||||
verbose_name="Получено сообщений"
|
||||
)
|
||||
|
||||
last_error = models.TextField(
|
||||
blank=True,
|
||||
verbose_name="Последняя ошибка"
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Создано")
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name="Обновлено")
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Интеграция с мессенджером"
|
||||
verbose_name_plural = "Интеграции с мессенджерами"
|
||||
indexes = [
|
||||
models.Index(fields=['messenger_type', 'is_enabled']),
|
||||
models.Index(fields=['status']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.get_messenger_type_display()} - {self.get_status_display()}"
|
||||
|
||||
|
||||
class MessengerAccount(models.Model):
|
||||
"""
|
||||
Привязка аккаунта сотрудника к мессенджеру
|
||||
"""
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='messenger_accounts',
|
||||
verbose_name="Пользователь"
|
||||
)
|
||||
|
||||
integration = models.ForeignKey(
|
||||
MessengerIntegration,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='accounts',
|
||||
verbose_name="Интеграция"
|
||||
)
|
||||
|
||||
# ID пользователя в мессенджере
|
||||
external_id = models.CharField(
|
||||
max_length=200,
|
||||
verbose_name="ID в мессенджере"
|
||||
)
|
||||
|
||||
# Имя/никнейм в мессенджере
|
||||
external_username = models.CharField(
|
||||
max_length=200,
|
||||
blank=True,
|
||||
verbose_name="Имя в мессенджере"
|
||||
)
|
||||
|
||||
# Телефон (для WhatsApp)
|
||||
phone_number = models.CharField(
|
||||
max_length=20,
|
||||
blank=True,
|
||||
verbose_name="Номер телефона"
|
||||
)
|
||||
|
||||
is_verified = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name="Подтвержден"
|
||||
)
|
||||
|
||||
is_active = models.BooleanField(
|
||||
default=True,
|
||||
verbose_name="Активен"
|
||||
)
|
||||
|
||||
# Дополнительные данные
|
||||
metadata = models.JSONField(
|
||||
default=dict,
|
||||
blank=True,
|
||||
verbose_name="Метаданные"
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Создано")
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name="Обновлено")
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Аккаунт в мессенджере"
|
||||
verbose_name_plural = "Аккаунты в мессенджерах"
|
||||
unique_together = [('integration', 'external_id')]
|
||||
indexes = [
|
||||
models.Index(fields=['user', 'integration']),
|
||||
models.Index(fields=['external_id']),
|
||||
models.Index(fields=['phone_number']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.user} - {self.integration.get_messenger_type_display()} ({self.external_username or self.external_id})"
|
||||
|
||||
|
||||
class MessengerMessage(models.Model):
|
||||
"""
|
||||
Синхронизированные сообщения из внешних мессенджеров
|
||||
"""
|
||||
DIRECTION_CHOICES = [
|
||||
('incoming', 'Входящее'),
|
||||
('outgoing', 'Исходящее'),
|
||||
]
|
||||
|
||||
STATUS_CHOICES = [
|
||||
('pending', 'В ожидании'),
|
||||
('sent', 'Отправлено'),
|
||||
('delivered', 'Доставлено'),
|
||||
('read', 'Прочитано'),
|
||||
('failed', 'Ошибка'),
|
||||
]
|
||||
|
||||
integration = models.ForeignKey(
|
||||
MessengerIntegration,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='messenger_messages',
|
||||
verbose_name="Интеграция"
|
||||
)
|
||||
|
||||
# Связь с внутренним сообщением (если синхронизировано)
|
||||
internal_message = models.OneToOneField(
|
||||
Message,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='messenger_sync',
|
||||
verbose_name="Внутреннее сообщение"
|
||||
)
|
||||
|
||||
# ID сообщения во внешнем мессенджере
|
||||
external_id = models.CharField(
|
||||
max_length=200,
|
||||
verbose_name="ID в мессенджере"
|
||||
)
|
||||
|
||||
# Отправитель/получатель
|
||||
sender_account = models.ForeignKey(
|
||||
MessengerAccount,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
related_name='sent_messages',
|
||||
verbose_name="Отправитель"
|
||||
)
|
||||
|
||||
recipient_account = models.ForeignKey(
|
||||
MessengerAccount,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='received_messages',
|
||||
verbose_name="Получатель"
|
||||
)
|
||||
|
||||
direction = models.CharField(
|
||||
max_length=10,
|
||||
choices=DIRECTION_CHOICES,
|
||||
verbose_name="Направление"
|
||||
)
|
||||
|
||||
status = models.CharField(
|
||||
max_length=20,
|
||||
choices=STATUS_CHOICES,
|
||||
default='pending',
|
||||
verbose_name="Статус"
|
||||
)
|
||||
|
||||
# Содержимое
|
||||
content = models.TextField(verbose_name="Текст")
|
||||
|
||||
# Вложения (JSON с URLs)
|
||||
attachments = models.JSONField(
|
||||
default=list,
|
||||
blank=True,
|
||||
verbose_name="Вложения"
|
||||
)
|
||||
|
||||
# Временные метки
|
||||
external_timestamp = models.DateTimeField(
|
||||
verbose_name="Время в мессенджере"
|
||||
)
|
||||
|
||||
synced_at = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
verbose_name="Синхронизировано"
|
||||
)
|
||||
|
||||
# Дополнительные данные из мессенджера
|
||||
raw_data = models.JSONField(
|
||||
default=dict,
|
||||
blank=True,
|
||||
verbose_name="Исходные данные"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Сообщение из мессенджера"
|
||||
verbose_name_plural = "Сообщения из мессенджеров"
|
||||
unique_together = [('integration', 'external_id')]
|
||||
indexes = [
|
||||
models.Index(fields=['integration', 'external_timestamp']),
|
||||
models.Index(fields=['sender_account', 'external_timestamp']),
|
||||
models.Index(fields=['status']),
|
||||
models.Index(fields=['-synced_at']),
|
||||
]
|
||||
ordering = ['-external_timestamp']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.integration.get_messenger_type_display()} - {self.external_id}"
|
||||
|
||||
@ -460,10 +460,22 @@ def chat_mark_read(request, pk: int):
|
||||
def whatsapp_view(request):
|
||||
"""WhatsApp интеграция"""
|
||||
from django.shortcuts import render
|
||||
from .models import MessengerIntegration
|
||||
|
||||
try:
|
||||
integration = MessengerIntegration.objects.get(
|
||||
messenger_type='whatsapp'
|
||||
)
|
||||
integration_status = integration.status if integration.is_enabled else 'inactive'
|
||||
except MessengerIntegration.DoesNotExist:
|
||||
integration_status = 'not_configured'
|
||||
|
||||
return render(request, 'communications/messengers/whatsapp.html', {
|
||||
'messenger_name': 'WhatsApp',
|
||||
'messenger_icon': 'bi-whatsapp',
|
||||
'messenger_color': '#25D366'
|
||||
'messenger_color': '#25D366',
|
||||
'integration_status': integration_status,
|
||||
'is_admin': request.user.is_staff or request.user.is_superuser,
|
||||
})
|
||||
|
||||
|
||||
@ -471,10 +483,22 @@ def whatsapp_view(request):
|
||||
def telegram_view(request):
|
||||
"""Telegram интеграция"""
|
||||
from django.shortcuts import render
|
||||
from .models import MessengerIntegration
|
||||
|
||||
try:
|
||||
integration = MessengerIntegration.objects.get(
|
||||
messenger_type='telegram'
|
||||
)
|
||||
integration_status = integration.status if integration.is_enabled else 'inactive'
|
||||
except MessengerIntegration.DoesNotExist:
|
||||
integration_status = 'not_configured'
|
||||
|
||||
return render(request, 'communications/messengers/telegram.html', {
|
||||
'messenger_name': 'Telegram',
|
||||
'messenger_icon': 'bi-telegram',
|
||||
'messenger_color': '#0088cc'
|
||||
'messenger_color': '#0088cc',
|
||||
'integration_status': integration_status,
|
||||
'is_admin': request.user.is_staff or request.user.is_superuser,
|
||||
})
|
||||
|
||||
|
||||
@ -482,8 +506,20 @@ def telegram_view(request):
|
||||
def wechat_view(request):
|
||||
"""WeChat интеграция"""
|
||||
from django.shortcuts import render
|
||||
from .models import MessengerIntegration
|
||||
|
||||
try:
|
||||
integration = MessengerIntegration.objects.get(
|
||||
messenger_type='wechat'
|
||||
)
|
||||
integration_status = integration.status if integration.is_enabled else 'inactive'
|
||||
except MessengerIntegration.DoesNotExist:
|
||||
integration_status = 'not_configured'
|
||||
|
||||
return render(request, 'communications/messengers/wechat.html', {
|
||||
'messenger_name': 'WeChat',
|
||||
'messenger_icon': 'bi-wechat',
|
||||
'messenger_color': '#09B83E'
|
||||
'messenger_color': '#09B83E',
|
||||
'integration_status': integration_status,
|
||||
'is_admin': request.user.is_staff or request.user.is_superuser,
|
||||
})
|
||||
|
||||
@ -76,23 +76,58 @@
|
||||
<h2 class="messenger-title">{{ messenger_name }} интеграция</h2>
|
||||
|
||||
<p class="messenger-description">
|
||||
Здесь будет отображаться интеграция с {{ messenger_name }}.
|
||||
Функционал находится в разработке.
|
||||
{% if integration_status == 'active' %}
|
||||
Интеграция активна. Вы можете отправлять и получать сообщения через Telegram.
|
||||
{% elif integration_status == 'inactive' %}
|
||||
Интеграция не настроена. Обратитесь к администратору для настройки.
|
||||
{% else %}
|
||||
Здесь будет отображаться интеграция с {{ messenger_name }}.
|
||||
Функционал находится в разработке.
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<div class="messenger-actions">
|
||||
<a href="{% url 'communications:chat_list' %}" class="btn btn-outline-secondary messenger-btn">
|
||||
<i class="bi-arrow-left"></i> Вернуться к чатам
|
||||
</a>
|
||||
<button class="btn btn-primary messenger-btn" style="background-color: {{ messenger_color }}; border-color: {{ messenger_color }};" disabled>
|
||||
<i class="bi-link-45deg"></i> Подключить {{ messenger_name }}
|
||||
</button>
|
||||
{% if is_admin %}
|
||||
<a href="/admin/communications/messengerintegration/" class="btn btn-primary messenger-btn" style="background-color: {{ messenger_color }}; border-color: {{ messenger_color }};">
|
||||
<i class="bi-gear"></i> Настроить интеграцию
|
||||
</a>
|
||||
{% else %}
|
||||
<button class="btn btn-primary messenger-btn" style="background-color: {{ messenger_color }}; border-color: {{ messenger_color }};" disabled>
|
||||
<i class="bi-link-45deg"></i> Подключить {{ messenger_name }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<div class="alert alert-info d-inline-block">
|
||||
<i class="bi-info-circle"></i> Для настройки интеграции обратитесь к администратору
|
||||
</div>
|
||||
{% if is_admin %}
|
||||
<div class="alert alert-success d-inline-block">
|
||||
<i class="bi-shield-check"></i> У вас есть права администратора
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<h5>Инструкции для настройки:</h5>
|
||||
<ol class="text-start" style="max-width: 600px; margin: 0 auto;">
|
||||
<li>Создайте бота через @BotFather в Telegram</li>
|
||||
<li>Получите Bot Token</li>
|
||||
<li>Откройте <a href="/admin/communications/messengerintegration/">админ-панель интеграций</a></li>
|
||||
<li>Создайте новую интеграцию для Telegram</li>
|
||||
<li>Укажите Bot Token в поле API ключ</li>
|
||||
<li>Настройте webhook</li>
|
||||
<li>Активируйте интеграцию</li>
|
||||
</ol>
|
||||
<div class="mt-3">
|
||||
<a href="/static/communications/MESSENGER_INTEGRATION_GUIDE.md" target="_blank" class="btn btn-sm btn-outline-info">
|
||||
<i class="bi-book"></i> Полная документация
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info d-inline-block">
|
||||
<i class="bi-info-circle"></i> Для настройки интеграции обратитесь к администратору
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -76,23 +76,58 @@
|
||||
<h2 class="messenger-title">{{ messenger_name }} интеграция</h2>
|
||||
|
||||
<p class="messenger-description">
|
||||
Здесь будет отображаться интеграция с {{ messenger_name }}.
|
||||
Функционал находится в разработке.
|
||||
{% if integration_status == 'active' %}
|
||||
Интеграция активна. Вы можете отправлять и получать сообщения через WeChat.
|
||||
{% elif integration_status == 'inactive' %}
|
||||
Интеграция не настроена. Обратитесь к администратору для настройки.
|
||||
{% else %}
|
||||
Здесь будет отображаться интеграция с {{ messenger_name }}.
|
||||
Функционал находится в разработке.
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<div class="messenger-actions">
|
||||
<a href="{% url 'communications:chat_list' %}" class="btn btn-outline-secondary messenger-btn">
|
||||
<i class="bi-arrow-left"></i> Вернуться к чатам
|
||||
</a>
|
||||
<button class="btn btn-primary messenger-btn" style="background-color: {{ messenger_color }}; border-color: {{ messenger_color }};" disabled>
|
||||
<i class="bi-link-45deg"></i> Подключить {{ messenger_name }}
|
||||
</button>
|
||||
{% if is_admin %}
|
||||
<a href="/admin/communications/messengerintegration/" class="btn btn-primary messenger-btn" style="background-color: {{ messenger_color }}; border-color: {{ messenger_color }};">
|
||||
<i class="bi-gear"></i> Настроить интеграцию
|
||||
</a>
|
||||
{% else %}
|
||||
<button class="btn btn-primary messenger-btn" style="background-color: {{ messenger_color }}; border-color: {{ messenger_color }};" disabled>
|
||||
<i class="bi-link-45deg"></i> Подключить {{ messenger_name }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<div class="alert alert-info d-inline-block">
|
||||
<i class="bi-info-circle"></i> Для настройки интеграции обратитесь к администратору
|
||||
</div>
|
||||
{% if is_admin %}
|
||||
<div class="alert alert-success d-inline-block">
|
||||
<i class="bi-shield-check"></i> У вас есть права администратора
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<h5>Инструкции для настройки:</h5>
|
||||
<ol class="text-start" style="max-width: 600px; margin: 0 auto;">
|
||||
<li>Зарегистрируйте WeChat Official Account</li>
|
||||
<li>Получите AppID и AppSecret</li>
|
||||
<li>Откройте <a href="/admin/communications/messengerintegration/">админ-панель интеграций</a></li>
|
||||
<li>Создайте новую интеграцию для WeChat</li>
|
||||
<li>Укажите AppID и AppSecret</li>
|
||||
<li>Настройте webhook</li>
|
||||
<li>Активируйте интеграцию</li>
|
||||
</ol>
|
||||
<div class="mt-3">
|
||||
<a href="/static/communications/MESSENGER_INTEGRATION_GUIDE.md" target="_blank" class="btn btn-sm btn-outline-info">
|
||||
<i class="bi-book"></i> Полная документация
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info d-inline-block">
|
||||
<i class="bi-info-circle"></i> Для настройки интеграции обратитесь к администратору
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -76,23 +76,56 @@
|
||||
<h2 class="messenger-title">{{ messenger_name }} интеграция</h2>
|
||||
|
||||
<p class="messenger-description">
|
||||
Здесь будет отображаться интеграция с {{ messenger_name }}.
|
||||
Функционал находится в разработке.
|
||||
{% if integration_status == 'active' %}
|
||||
Интеграция активна. Вы можете отправлять и получать сообщения через WhatsApp.
|
||||
{% elif integration_status == 'inactive' %}
|
||||
Интеграция не настроена. Обратитесь к администратору для настройки.
|
||||
{% else %}
|
||||
Здесь будет отображаться интеграция с {{ messenger_name }}.
|
||||
Функционал находится в разработке.
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<div class="messenger-actions">
|
||||
<a href="{% url 'communications:chat_list' %}" class="btn btn-outline-secondary messenger-btn">
|
||||
<i class="bi-arrow-left"></i> Вернуться к чатам
|
||||
</a>
|
||||
<button class="btn btn-primary messenger-btn" style="background-color: {{ messenger_color }}; border-color: {{ messenger_color }};" disabled>
|
||||
<i class="bi-link-45deg"></i> Подключить {{ messenger_name }}
|
||||
</button>
|
||||
{% if is_admin %}
|
||||
<a href="/admin/communications/messengerintegration/" class="btn btn-primary messenger-btn" style="background-color: {{ messenger_color }}; border-color: {{ messenger_color }};">
|
||||
<i class="bi-gear"></i> Настроить интеграцию
|
||||
</a>
|
||||
{% else %}
|
||||
<button class="btn btn-primary messenger-btn" style="background-color: {{ messenger_color }}; border-color: {{ messenger_color }};" disabled>
|
||||
<i class="bi-link-45deg"></i> Подключить {{ messenger_name }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<div class="alert alert-info d-inline-block">
|
||||
<i class="bi-info-circle"></i> Для настройки интеграции обратитесь к администратору
|
||||
</div>
|
||||
{% if is_admin %}
|
||||
<div class="alert alert-success d-inline-block">
|
||||
<i class="bi-shield-check"></i> У вас есть права администратора
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<h5>Инструкции для настройки:</h5>
|
||||
<ol class="text-start" style="max-width: 600px; margin: 0 auto;">
|
||||
<li>Получите API ключи от WhatsApp Business API</li>
|
||||
<li>Откройте <a href="/admin/communications/messengerintegration/">админ-панель интеграций</a></li>
|
||||
<li>Создайте новую интеграцию для WhatsApp</li>
|
||||
<li>Укажите API ключ и webhook URL</li>
|
||||
<li>Активируйте интеграцию</li>
|
||||
</ol>
|
||||
<div class="mt-3">
|
||||
<a href="/static/communications/MESSENGER_INTEGRATION_GUIDE.md" target="_blank" class="btn btn-sm btn-outline-info">
|
||||
<i class="bi-book"></i> Полная документация
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info d-inline-block">
|
||||
<i class="bi-info-circle"></i> Для настройки интеграции обратитесь к администратору
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -26,36 +26,24 @@
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4">
|
||||
<label class="form-label" for="f_telegram">
|
||||
<i class="bi-telegram text-primary"></i> Telegram
|
||||
</label>
|
||||
<label class="form-label" for="f_telegram">Telegram</label>
|
||||
<input id="f_telegram" class="form-control" name="telegram"
|
||||
value="{{ emp.telegram|default:'' }}" inputmode="text"
|
||||
placeholder="@username или chat_id"
|
||||
data-init="{{ emp.telegram|default:'' }}" data-contact="1">
|
||||
<div class="form-text small">Для уведомлений (найдите ID через @userinfobot)</div>
|
||||
<div class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<label class="form-label" for="f_whatsapp">
|
||||
<i class="bi-whatsapp" style="color: #25D366;"></i> WhatsApp
|
||||
</label>
|
||||
<label class="form-label" for="f_whatsapp">WhatsApp</label>
|
||||
<input id="f_whatsapp" class="form-control" name="whatsapp"
|
||||
value="{{ emp.whatsapp|default:'' }}" inputmode="tel"
|
||||
placeholder="+79001234567"
|
||||
data-init="{{ emp.whatsapp|default:'' }}" data-contact="1">
|
||||
<div class="form-text small">Номер телефона в формате E.164</div>
|
||||
<div class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<label class="form-label" for="f_wechat">
|
||||
<i class="bi-wechat" style="color: #09B83E;"></i> WeChat
|
||||
</label>
|
||||
<label class="form-label" for="f_wechat">WeChat</label>
|
||||
<input id="f_wechat" class="form-control" name="wechat"
|
||||
value="{{ emp.wechat|default:'' }}" inputmode="text"
|
||||
placeholder="WeChat ID"
|
||||
data-init="{{ emp.wechat|default:'' }}" data-contact="1">
|
||||
<div class="form-text small">Ваш WeChat идентификатор</div>
|
||||
<div class="invalid-feedback"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -72,40 +72,6 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# Быстрые действия с мессенджерами #}
|
||||
{% if emp.telegram or emp.whatsapp or emp.wechat %}
|
||||
<div class="mt-3 d-flex gap-2 justify-content-center flex-wrap">
|
||||
{% if emp.telegram %}
|
||||
<a href="https://t.me/{{ emp.telegram }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary d-flex align-items-center gap-1"
|
||||
title="Написать в Telegram">
|
||||
<i class="bi-telegram"></i>
|
||||
<span class="d-none d-sm-inline">Telegram</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if emp.whatsapp %}
|
||||
<a href="https://wa.me/{{ emp.whatsapp }}" target="_blank"
|
||||
class="btn btn-sm d-flex align-items-center gap-1"
|
||||
style="background-color: #25D366; color: white; border-color: #25D366;"
|
||||
title="Написать в WhatsApp">
|
||||
<i class="bi-whatsapp"></i>
|
||||
<span class="d-none d-sm-inline">WhatsApp</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if emp.wechat %}
|
||||
<button class="btn btn-sm d-flex align-items-center gap-1"
|
||||
style="background-color: #09B83E; color: white; border-color: #09B83E;"
|
||||
title="WeChat ID: {{ emp.wechat }}"
|
||||
onclick="navigator.clipboard.writeText('{{ emp.wechat }}'); this.innerHTML='<i class=\'bi-check\'></i> Скопировано'; setTimeout(() => this.innerHTML='<i class=\'bi-wechat\'></i> <span class=\'d-none d-sm-inline\'>WeChat</span>', 2000)">
|
||||
<i class="bi-wechat"></i>
|
||||
<span class="d-none d-sm-inline">WeChat</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Contacts -->
|
||||
<div class="mt-3 text-start">
|
||||
<div class="ios-acc">
|
||||
@ -134,33 +100,9 @@
|
||||
<a href="tel:{{ emp.phone_number }}">{{ emp.phone_number }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Мессенджеры с цветными иконками и ссылками #}
|
||||
{% if emp.telegram %}
|
||||
<div class="ios-cell">
|
||||
<i class="bi-telegram text-primary"></i>
|
||||
<a href="https://t.me/{{ emp.telegram }}" target="_blank" class="text-decoration-none">
|
||||
<span>Telegram: {{ emp.telegram }}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if emp.whatsapp %}
|
||||
<div class="ios-cell">
|
||||
<i class="bi-whatsapp" style="color: #25D366;"></i>
|
||||
<a href="https://wa.me/{{ emp.whatsapp }}" target="_blank" class="text-decoration-none">
|
||||
<span>WhatsApp: {{ emp.whatsapp }}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if emp.wechat %}
|
||||
<div class="ios-cell">
|
||||
<i class="bi-wechat" style="color: #09B83E;"></i>
|
||||
<span>WeChat: {{ emp.wechat }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if emp.telegram %}<div class="ios-cell"><i class="bi-telegram"></i><span>{{ emp.telegram }}</span></div>{% endif %}
|
||||
{% if emp.whatsapp %}<div class="ios-cell"><i class="bi-whatsapp"></i><span>{{ emp.whatsapp }}</span></div>{% endif %}
|
||||
{% if emp.wechat %}<div class="ios-cell"><i class="bi-wechat"></i><span>{{ emp.wechat }}</span></div>{% endif %}
|
||||
{% if not emp.email and not emp.phone_number and not emp.telegram and not emp.whatsapp and not emp.wechat %}
|
||||
<div class="ios-cell"><span class="text-secondary">Контакты не указаны</span></div>
|
||||
{% endif %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user