mattermost: автозаполнение бота из Mattermost по токену

Если у бота задан только access-token (без id/username/имени), при сохранении
в админке дёргаем GET users/me этим токеном и заполняем user_id, username,
display_name автоматически.

- MattermostService.identity_for_token(token) — кто владелец токена;
- MattermostBotAdmin.save_model подставляет пустые поля, сообщает результат.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 20:32:34 +03:00
parent 982c7e6203
commit 58efe33070
2 changed files with 57 additions and 1 deletions
+30 -1
View File
@@ -1,5 +1,5 @@
from django import forms
from django.contrib import admin
from django.contrib import admin, messages
from unfold.admin import ModelAdmin, TabularInline
from unfold.decorators import display
from django.utils.html import format_html
@@ -78,6 +78,35 @@ class MattermostBotAdmin(ModelAdmin):
def caps_display(self, obj):
return ', '.join(obj.caps()) or '—'
def save_model(self, request, obj, form, change):
# Задан только токен — подтянуть id/username/имя из Mattermost (GET users/me).
if obj.token and (not obj.user_id or not obj.username or not obj.display_name):
try:
from apps.mattermost_connector.mattermost_service import MattermostService
info = MattermostService.identity_for_token(obj.token)
if info and info.get('user_id'):
obj.user_id = obj.user_id or info['user_id']
obj.username = obj.username or info['username']
obj.display_name = obj.display_name or info['display_name']
self.message_user(
request,
f'Данные бота получены из Mattermost: @{obj.username} (id={obj.user_id})',
level=messages.SUCCESS,
)
elif info is None:
self.message_user(
request,
'Коннектор Mattermost не настроен — автозаполнение бота пропущено.',
level=messages.WARNING,
)
except Exception as e:
self.message_user(
request,
f'Не удалось получить данные бота из Mattermost (проверьте токен): {e}',
level=messages.WARNING,
)
super().save_model(request, obj, form, change)
@admin.register(MattermostTeam)
class MattermostTeamAdmin(ModelAdmin):
@@ -85,6 +85,33 @@ class MattermostService:
"""Получение пользователя по username"""
return self._call(f'GET users/username/{username}', self.driver.users.get_user_by_username, username)
@staticmethod
def identity_for_token(token: str) -> Dict | None:
"""Кто владелец токена (GET users/me): id/username/display_name.
Используется, чтобы автозаполнить карточку бота, когда задан только
access-token. Возвращает None, если коннектор не настроен или токен пуст.
"""
from core.models import ConnectorConfig
cfg = ConnectorConfig.objects.filter(connector_type='mattermost', is_enabled=True).first()
if not cfg or not cfg.host or not token:
return None
scheme = 'https' if cfg.use_ssl else 'http'
port = cfg.port or (443 if cfg.use_ssl else 80)
driver = TypedDriver({'url': cfg.host, 'port': port, 'scheme': scheme, 'timeout': 30})
driver.client.token = token
me = driver.users.get_user('me')
display = (
me.get('nickname')
or f"{me.get('first_name', '')} {me.get('last_name', '')}".strip()
or me.get('username', '')
)
return {
'user_id': me.get('id', ''),
'username': me.get('username', ''),
'display_name': display,
}
def update_user(self, user_id: str, user_data: Dict) -> Dict:
"""Обновление данных пользователя через PATCH"""
return self._call(f'PATCH users/{user_id}', self.driver.users.patch_user, user_id, log_data=user_data, **user_data)