from __future__ import annotations from enum import StrEnum class LineErrorCode(StrEnum): INVALID_REPLY_TOKEN = "invalid_reply_token" REPLY_TOKEN_EXPIRED = "reply_token_expired" INVALID_CHANNEL_ACCESS_TOKEN = "invalid_channel_access_token" CHANNEL_ACCESS_TOKEN_EXPIRED = "channel_access_token_expired" RATE_LIMITED = "rate_limited" INVALID_MESSAGE = "invalid_message" USER_NOT_FOUND = "user_not_found" GROUP_NOT_FOUND = "group_not_found" NETWORK_ERROR = "network_error" UNKNOWN = "unknown" ERROR_PATTERN_MAP: dict[str, LineErrorCode] = { "Invalid reply token": LineErrorCode.INVALID_REPLY_TOKEN, "Reply token expired": LineErrorCode.REPLY_TOKEN_EXPIRED, "Reply token has already been used": LineErrorCode.REPLY_TOKEN_EXPIRED, "Invalid channel access token": LineErrorCode.INVALID_CHANNEL_ACCESS_TOKEN, "The channel access token expired": LineErrorCode.CHANNEL_ACCESS_TOKEN_EXPIRED, "Rate limit exceeded": LineErrorCode.RATE_LIMITED, "Invalid message": LineErrorCode.INVALID_MESSAGE, "user not found": LineErrorCode.USER_NOT_FOUND, "group not found": LineErrorCode.GROUP_NOT_FOUND, } def classify_line_error(status_code: int, error_message: str) -> LineErrorCode: msg_lower = error_message.lower() for pattern, code in ERROR_PATTERN_MAP.items(): if pattern.lower() in msg_lower: return code if status_code == 401: return LineErrorCode.INVALID_CHANNEL_ACCESS_TOKEN if status_code == 429: return LineErrorCode.RATE_LIMITED if status_code >= 500: return LineErrorCode.NETWORK_ERROR return LineErrorCode.UNKNOWN def is_reply_token_expired(error: LineErrorCode) -> bool: return error in (LineErrorCode.REPLY_TOKEN_EXPIRED, LineErrorCode.INVALID_REPLY_TOKEN) def is_retryable(error: LineErrorCode) -> bool: return error in ( LineErrorCode.RATE_LIMITED, LineErrorCode.NETWORK_ERROR, )