This commit is contained in:
Raykov-MS 2026-06-24 17:44:00 +03:00
parent 3dcf947c68
commit bbac9918e1
2 changed files with 42 additions and 3 deletions

View File

@ -44,6 +44,8 @@ python -m uvicorn main:app --host 0.0.0.0 --port 8080 --reload --env-file .env
- `AUTO_ASSIGNED_LABEL` - необязательно, по умолчанию `auto-assigned` - `AUTO_ASSIGNED_LABEL` - необязательно, по умолчанию `auto-assigned`
- `AUTO_ASSIGN_REVIEWERS_COUNT` - необязательно, по умолчанию `2` (сколько ревьюеров назначать автоматически) - `AUTO_ASSIGN_REVIEWERS_COUNT` - необязательно, по умолчанию `2` (сколько ревьюеров назначать автоматически)
- `AUTO_ASSIGN_EXCLUDED_AUTHORS` - необязательно, CSV логинов Gitea; для этих авторов автоназначение и auto-label на `PR opened` не выполняются - `AUTO_ASSIGN_EXCLUDED_AUTHORS` - необязательно, CSV логинов Gitea; для этих авторов автоназначение и auto-label на `PR opened` не выполняются
- `BACKEND_USERS` - необязательно, CSV логинов Gitea backend-команды; если автор PR входит в этот список, ревьюеры выбираются только из этого же списка
- `FRONTEND_USERS` - необязательно, CSV логинов Gitea frontend-команды; если автор PR входит в этот список, ревьюеры выбираются только из этого же списка
- `APPROVALS_REQUIRED_FOR_MERGE` - необязательно, по умолчанию `2` (порог approve для сообщения "можно мерджить") - `APPROVALS_REQUIRED_FOR_MERGE` - необязательно, по умолчанию `2` (порог approve для сообщения "можно мерджить")
- `NOTIFY_ON_WEEKENDS` - необязательно, по умолчанию `true`; если `false`, бот не отправляет ЛС в субботу и воскресенье (МСК) - `NOTIFY_ON_WEEKENDS` - необязательно, по умолчанию `true`; если `false`, бот не отправляет ЛС в субботу и воскресенье (МСК)
- `ALLOW_SELF_ASSIGN` - необязательно, по умолчанию `false` (для локального теста можно `true`) - `ALLOW_SELF_ASSIGN` - необязательно, по умолчанию `false` (для локального теста можно `true`)
@ -52,6 +54,8 @@ python -m uvicorn main:app --host 0.0.0.0 --port 8080 --reload --env-file .env
- `GITEA_COUNT_REPOS` - необязательно, CSV репозиториев `org/repo` для расчета нагрузки (исторический счетчик назначений) - `GITEA_COUNT_REPOS` - необязательно, CSV репозиториев `org/repo` для расчета нагрузки (исторический счетчик назначений)
- `BOT_DB_PATH` - необязательно, по умолчанию `botreviewer.sqlite3` - `BOT_DB_PATH` - необязательно, по умолчанию `botreviewer.sqlite3`
Если автор PR не входит ни в `BACKEND_USERS`, ни в `FRONTEND_USERS`, бот использует общий пул доступных пользователей (как раньше).
## Webhook-эндпоинты ## Webhook-эндпоинты
- Обновления Telegram: `POST /telegram/webhook` - Обновления Telegram: `POST /telegram/webhook`

39
main.py
View File

@ -51,6 +51,16 @@ AUTO_ASSIGN_EXCLUDED_AUTHORS = {
for v in os.getenv("AUTO_ASSIGN_EXCLUDED_AUTHORS", "").split(",") for v in os.getenv("AUTO_ASSIGN_EXCLUDED_AUTHORS", "").split(",")
if v.strip() if v.strip()
} }
BACKEND_USERS = {
v.strip().lower()
for v in os.getenv("BACKEND_USERS", "").split(",")
if v.strip()
}
FRONTEND_USERS = {
v.strip().lower()
for v in os.getenv("FRONTEND_USERS", "").split(",")
if v.strip()
}
NOTIFY_ON_WEEKENDS = os.getenv("NOTIFY_ON_WEEKENDS", "true").lower() == "true" NOTIFY_ON_WEEKENDS = os.getenv("NOTIFY_ON_WEEKENDS", "true").lower() == "true"
MSK_TZ = timezone(timedelta(hours=3)) MSK_TZ = timezone(timedelta(hours=3))
@ -529,6 +539,23 @@ def choose_reviewer(candidates: list[RegisteredUser]) -> RegisteredUser:
return random.choice(candidates) return random.choice(candidates)
def get_reviewer_group_for_author(author_login: str) -> tuple[set[str] | None, str]:
author_login_norm = author_login.strip().lower()
if author_login_norm in BACKEND_USERS:
return BACKEND_USERS, "backend"
if author_login_norm in FRONTEND_USERS:
return FRONTEND_USERS, "frontend"
return None, ""
def filter_candidates_by_author_group(author_login: str, candidates: list[RegisteredUser]) -> tuple[list[RegisteredUser], str]:
configured_group, group_name = get_reviewer_group_for_author(author_login)
if configured_group is None:
return candidates, ""
filtered = [candidate for candidate in candidates if candidate.gitea_login.strip().lower() in configured_group]
return filtered, group_name
async def build_reviewer_load_counts(candidates: list[RegisteredUser], current_repo_full_name: str) -> dict[str, int]: async def build_reviewer_load_counts(candidates: list[RegisteredUser], current_repo_full_name: str) -> dict[str, int]:
_ = current_repo_full_name _ = current_repo_full_name
return { return {
@ -835,12 +862,19 @@ async def handle_pr_opened(payload: dict[str, Any]) -> None:
log_warn("[gitea] Клиент не настроен, автоназначение пропущено.") log_warn("[gitea] Клиент не настроен, автоназначение пропущено.")
return return
candidates = storage.get_available_candidates( all_candidates = storage.get_available_candidates(
author_login=author_login, author_login=author_login,
today=msk_now().date(), today=msk_now().date(),
allow_self_assign=ALLOW_SELF_ASSIGN, allow_self_assign=ALLOW_SELF_ASSIGN,
) )
candidates, assigned_group_name = filter_candidates_by_author_group(author_login, all_candidates)
if not candidates: if not candidates:
if assigned_group_name:
log_warn(
f"[assign] Нет доступных кандидатов для группы '{assigned_group_name}' "
f"({repo_full_name}#{pr_number}, author={author_login})."
)
else:
log_warn(f"[assign] Нет доступных кандидатов для {repo_full_name}#{pr_number}.") log_warn(f"[assign] Нет доступных кандидатов для {repo_full_name}#{pr_number}.")
return return
@ -856,7 +890,8 @@ async def handle_pr_opened(payload: dict[str, Any]) -> None:
log_info( log_info(
f"[assign] Автоназначение для {repo_full_name}#{pr_number}. " f"[assign] Автоназначение для {repo_full_name}#{pr_number}. "
f"Кандидаты={len(candidates)}, выбраны={selected_logins}, нагрузки={dict(counts)}" f"Кандидаты={len(candidates)} из {len(all_candidates)}, "
f"группа={assigned_group_name or 'all'}, выбраны={selected_logins}, нагрузки={dict(counts)}"
) )
await gitea_client.assign_reviewers(repo_full_name, int(pr_number), selected_logins) await gitea_client.assign_reviewers(repo_full_name, int(pr_number), selected_logins)
await gitea_client.add_label(repo_full_name, int(pr_number), AUTO_ASSIGNED_LABEL) await gitea_client.add_label(repo_full_name, int(pr_number), AUTO_ASSIGNED_LABEL)