diff --git a/main.py b/main.py index 4449aa2..9d9159b 100644 --- a/main.py +++ b/main.py @@ -475,7 +475,6 @@ class GiteaClient: return [] return data - storage = Storage(DB_PATH) telegram_client = TelegramClient(TELEGRAM_BOT_TOKEN) gitea_client = GiteaClient(GITEA_BASE_URL, GITEA_TOKEN) @@ -574,6 +573,29 @@ def calculate_effective_approvals(reviews: list[dict[str, Any]]) -> int: return sum(1 for _, state in latest_state_by_login.values() if state == "approved") +def get_pr_author_login( + repo_full_name: str, pr_number: int, pull_request: dict[str, Any] | None = None +) -> str | None: + """Логин создателя (автора) PR.""" + if pull_request: + login = ((pull_request.get("user") or {}).get("login") or "").strip() + if login: + return login + rows = storage.get_assignments_for_pr(repo_full_name, pr_number) + return rows[0]["author_login"] if rows else None + + +async def get_pr_author_chat_id( + repo_full_name: str, pr_number: int, pull_request: dict[str, Any] | None = None +) -> int | None: + """Chat_id создателя PR в Telegram (если зарегистрирован).""" + author_login = get_pr_author_login(repo_full_name, pr_number, pull_request) + if not author_login: + return None + user = storage.get_user_by_gitea_login(author_login) + return user.telegram_chat_id if user else None + + async def get_pr_participant_chat_ids( repo_full_name: str, pr_number: int, @@ -913,7 +935,7 @@ async def handle_pull_request_comment(payload: dict[str, Any]) -> None: async def handle_pr_reviewed(payload: dict[str, Any]) -> None: - """Изменение статуса ревью (approved / request changes) — уведомляем участников.""" + """Изменение статуса ревью (approved / request changes) — уведомляем только создателя PR.""" pull_request = payload.get("pull_request") or {} repository = payload.get("repository") or {} review = payload.get("review") or {} @@ -937,15 +959,15 @@ async def handle_pr_reviewed(payload: dict[str, Any]) -> None: msg = f"PR {repo_full_name}#{pr_number}: ревью {status_text} ({reviewer_login})." else: msg = f"PR {repo_full_name}#{pr_number}: ревью {status_text}." - for chat_id in await get_pr_participant_chat_ids( - repo_full_name, - int(pr_number), - pull_request=pull_request, - exclude_login=reviewer_login, - ): - await telegram_client.send_message(chat_id, msg) + author_chat_id = await get_pr_author_chat_id(repo_full_name, int(pr_number), pull_request) + if author_chat_id is not None: + await telegram_client.send_message(author_chat_id, msg) + else: + log_warn( + f"[notify] ревью для {repo_full_name}#{pr_number}: автор не зарегистрирован в боте." + ) - # Доп. нотификация, когда набран порог approve для merge. + # Уведомление «можно мерджить» — только создателю PR. if not gitea_client.enabled: return reviews = await gitea_client.list_pull_request_reviews(repo_full_name, int(pr_number)) @@ -954,21 +976,14 @@ async def handle_pr_reviewed(payload: dict[str, Any]) -> None: already_notified = storage.get_kv(merge_ready_key) == "1" if approvals_count >= APPROVALS_REQUIRED_FOR_MERGE: - if not already_notified: + if not already_notified and author_chat_id is not None: merge_msg = ( f"PR {repo_full_name}#{pr_number} набрал {approvals_count} approve " f"(порог {APPROVALS_REQUIRED_FOR_MERGE}) — можно мерджить." ) - for chat_id in await get_pr_participant_chat_ids( - repo_full_name, - int(pr_number), - pull_request=pull_request, - ): - await telegram_client.send_message(chat_id, merge_msg) + await telegram_client.send_message(author_chat_id, merge_msg) storage.set_kv(merge_ready_key, "1") else: - # Если approvals опустились ниже порога (например, после request changes), - # разрешаем повторную нотификацию при следующем достижении порога. if already_notified: storage.delete_kv(merge_ready_key)