최초 프로젝트 업로드 (Script Auto Commit)

This commit is contained in:
2025-12-03 22:40:47 +09:00
commit dd9acf62a3
39 changed files with 5251 additions and 0 deletions

108
src/notifications.py Normal file
View File

@@ -0,0 +1,108 @@
import os
import threading
import time
import requests
from .common import logger
__all__ = ["send_telegram", "send_telegram_with_retry", "report_error", "send_startup_test_message"]
def send_telegram_with_retry(
token: str,
chat_id: str,
text: str,
add_thread_prefix: bool = True,
parse_mode: str = None,
max_retries: int | None = None,
) -> bool:
"""
재시도 로직이 포함된 텔레그램 메시지 전송
Args:
token: 텔레그램 봇 토큰
chat_id: 채팅 ID
text: 메시지 내용
add_thread_prefix: 스레드 prefix 추가 여부
parse_mode: HTML/Markdown 파싱 모드
max_retries: 최대 재시도 횟수 (None이면 기본값 3)
Returns:
성공 여부 (True/False)
"""
if max_retries is None:
max_retries = 3
for attempt in range(max_retries):
try:
# 이제 send_telegram은 실패 시 예외를 발생시킴
send_telegram(token, chat_id, text, add_thread_prefix, parse_mode)
return True
except Exception as e:
if attempt < max_retries - 1:
wait_time = 2**attempt # Exponential backoff: 1s, 2s, 4s
logger.warning(
"텔레그램 전송 실패 (시도 %d/%d), %d초 후 재시도: %s", attempt + 1, max_retries, wait_time, e
)
time.sleep(wait_time)
else:
logger.error("텔레그램 전송 최종 실패 (%d회 시도): %s", max_retries, e)
return False
return False
def send_telegram(token: str, chat_id: str, text: str, add_thread_prefix: bool = True, parse_mode: str = None):
"""
텔레그램 메시지를 한 번 전송합니다. 실패 시 예외를 발생시킵니다.
"""
if add_thread_prefix:
thread_name = threading.current_thread().name
# 기본 Thread-N 이름이면 prefix 생략 (의미 없는 정보)
if not thread_name.startswith("Thread-"):
payload_text = f"[{thread_name}] {text}"
else:
payload_text = text
else:
payload_text = text
url = f"https://api.telegram.org/bot{token}/sendMessage"
payload = {"chat_id": chat_id, "text": payload_text}
if parse_mode:
payload["parse_mode"] = parse_mode
try:
resp = requests.post(url, json=payload, timeout=10)
resp.raise_for_status() # 2xx 상태 코드가 아니면 HTTPError 발생
logger.debug("텔레그램 메시지 전송 성공: %s", text[:80])
return True
except requests.exceptions.RequestException as e:
logger.warning("텔레그램 API 요청 실패: %s", e)
raise # 예외를 다시 발생시켜 호출자가 처리하도록 함
def report_error(bot_token: str, chat_id: str, message: str, dry_run: bool):
"""
Report an error via Telegram.
"""
if not dry_run and bot_token and chat_id:
# 재시도 로직이 포함된 함수 사용
send_telegram_with_retry(bot_token, chat_id, message, add_thread_prefix=True)
def send_startup_test_message(bot_token: str, chat_id: str, parse_mode: str, dry_run: bool):
"""
Send a startup test message to verify Telegram settings.
"""
if dry_run:
logger.info("[dry-run] Telegram 테스트 메시지 전송 생략")
return
if bot_token and chat_id:
test_msg = "[테스트] Telegram 설정 확인용 메시지입니다. 봇/채팅 설정이 올바르면 이 메시지가 도착합니다."
logger.info("텔레그램 테스트 메시지 전송 시도")
# 재시도 로직이 포함된 함수 사용
if send_telegram_with_retry(bot_token, chat_id, test_msg, add_thread_prefix=False, parse_mode=parse_mode):
logger.info("텔레그램 테스트 메시지 전송 성공")
else:
logger.warning("텔레그램 테스트 메시지 전송 실패")
else:
logger.warning("TELEGRAM_TEST=1 이지만 TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID가 설정되어 있지 않습니다")