diff --git a/skills/polymarket-browse/SKILL.md b/skills/polymarket-browse/SKILL.md index a983882..bfbbc11 100644 --- a/skills/polymarket-browse/SKILL.md +++ b/skills/polymarket-browse/SKILL.md @@ -35,7 +35,7 @@ hermes mcp add polymarket https://docs.polymarket.com/mcp ## Usage ``` -polymarket-browse [--category "Counter Strike"] [--limit 5] [--matches N] [--non-matches N] [--search "TeamName"] [--matches-only] [--non-matches-only] [--detail N] [--raw] [--telegram] [--no-cache] [--max-total N] [--starts-before TIMESTAMP] +polymarket-browse [--category "Counter Strike"] [--limit 5] [--matches N] [--non-matches N] [--search "TeamName"] [--matches-only] [--non-matches-only] [--detail N] [--raw] [--telegram] [--no-cache] [--max-total N] [--starts-before TIMESTAMP] [--timezone UTC+X] ``` ## Arguments @@ -53,6 +53,7 @@ polymarket-browse [--category "Counter Strike"] [--limit 5] [--matches N] [--non - `--no-cache` : Disable caching and fetch fresh data from the API. - `--max-total` : Maximum total events to fetch before early exit. Default: no limit. Useful for quick snapshots. - `--starts-before` : Unix timestamp filter. Only show match events starting before this time (LIVE events always shown regardless of timestamp). +- `--timezone` : Timezone for displaying times. Format: `UTC+X` or `UTC-X` (e.g., `UTC+7`, `UTC-5`). Default: UTC+7 (WIB). - `--telegram` : Send results to Telegram. Requires `BOT_TOKEN` and `CHAT_ID` in environment variables. ## Output Format diff --git a/skills/polymarket-browse/scripts/browse.py b/skills/polymarket-browse/scripts/browse.py index 027f633..fda055e 100644 --- a/skills/polymarket-browse/scripts/browse.py +++ b/skills/polymarket-browse/scripts/browse.py @@ -98,6 +98,45 @@ PAGE_SIZE = 50 MAX_RETRIES = 5 INITIAL_RETRY_DELAY = 2 # exponential backoff starts at 2s WIB = timezone(timedelta(hours=7)) # UTC+7 for Indonesian users +_DISPLAY_TZ = WIB # Module-level timezone for display (configurable via --timezone) + + +def parse_timezone(tz_str: str) -> timezone: + """ + Parse timezone string to datetime.timezone. + Supports: UTC offset format (UTC+7, UTC-5). + Falls back to WIB (UTC+7) on parse failure. + """ + tz_str = tz_str.strip() + if tz_str.startswith("UTC"): + offset_str = tz_str[3:].strip() + if not offset_str: + return timezone.utc + sign = -1 if offset_str[0] == "-" else 1 + if offset_str[0] in "+-": + offset_str = offset_str[1:] + try: + if ":" in offset_str: + hours, minutes = offset_str.split(":") + hours = int(hours) + minutes = int(minutes) + else: + hours = int(offset_str) + minutes = 0 + total_minutes = hours * 60 + minutes + if sign == -1: + total_minutes = -total_minutes + return timezone(timedelta(minutes=total_minutes)) + except ValueError: + return WIB + return WIB + try: + from datetime import ZoneInfo + + return ZoneInfo(tz_str).utcoffset(None) + except Exception: + return WIB + GAME_CATEGORIES = { "All Esports": "Esports", @@ -453,12 +492,12 @@ def _get_time_data(e: dict[str, Any], tz: timezone | None = None) -> TimeData: Args: e: Event dict with 'startTime' or 'startDate' key. tz: datetime.timezone for abs_time formatting. - Defaults to WIB (UTC+7). + Defaults to _DISPLAY_TZ (set via --timezone, or WIB). Returns: TimeData with time_status, time_urgency, and abs_time """ - tz = tz or WIB + tz = tz or _DISPLAY_TZ start_str = e.get("startTime") or e.get("startDate", "") if not start_str: @@ -863,11 +902,10 @@ def format_detail_event(e: dict[str, Any]) -> DetailEvent: def get_header_date() -> str: - """Return current date string like 'Mar 25, 2026'""" + """Return current date string like 'Mar 25, 2026' in display timezone.""" now_utc = datetime.now(timezone.utc) - utc7 = timezone(timedelta(hours=7)) - now_utc7 = now_utc.astimezone(utc7) - return now_utc7.strftime("%b %d, %Y") + now_display = now_utc.astimezone(_DISPLAY_TZ) + return now_display.strftime("%b %d, %Y") def get_tournament(title: str) -> str: @@ -1224,6 +1262,12 @@ def main() -> None: default=None, help="Unix timestamp filter. Only show match events starting before this time (LIVE events always shown).", ) + parser.add_argument( + "--timezone", + type=str, + default="UTC+7", + help="Timezone for displaying times (e.g., UTC+7, UTC-5). Default: UTC+7", + ) parser.add_argument( "--telegram", action="store_true", @@ -1243,6 +1287,9 @@ def main() -> None: matches_max = args.matches if args.matches is not None else args.limit non_matches_max = args.non_matches if args.non_matches is not None else args.limit + global _DISPLAY_TZ + _DISPLAY_TZ = parse_timezone(args.timezone) + if args.search: print(f"\nFetching {args.category} events matching '{args.search}'...") else: diff --git a/skills/polymarket-browse/tests/test_browse.py b/skills/polymarket-browse/tests/test_browse.py index 6d0ac00..0479700 100644 --- a/skills/polymarket-browse/tests/test_browse.py +++ b/skills/polymarket-browse/tests/test_browse.py @@ -1926,5 +1926,48 @@ class TestStartsBeforeFilter(unittest.TestCase): self.assertEqual(len(result["match_events"]), 2) +class TestTimezoneParsing(unittest.TestCase): + """Tests for parse_timezone() and timezone display.""" + + def test_parse_timezone_utc_plus7(self): + """UTC+7 should parse to WIB.""" + from browse import parse_timezone + from datetime import timezone, timedelta + + tz = parse_timezone("UTC+7") + self.assertEqual(tz, timezone(timedelta(hours=7))) + + def test_parse_timezone_utc_minus5(self): + """UTC-5 should parse correctly.""" + from browse import parse_timezone + from datetime import timezone, timedelta + + tz = parse_timezone("UTC-5") + self.assertEqual(tz, timezone(timedelta(hours=-5))) + + def test_parse_timezone_utc_no_offset(self): + """UTC should return timezone.utc.""" + from browse import parse_timezone + + tz = parse_timezone("UTC") + self.assertEqual(tz, timezone.utc) + + def test_parse_timezone_with_minutes(self): + """UTC+5:30 should parse correctly.""" + from browse import parse_timezone + from datetime import timezone, timedelta + + tz = parse_timezone("UTC+5:30") + self.assertEqual(tz, timezone(timedelta(hours=5, minutes=30))) + + def test_parse_timezone_invalid_falls_back_to_wib(self): + """Invalid timezone should fall back to WIB.""" + from browse import parse_timezone + from datetime import timezone, timedelta + + tz = parse_timezone("Invalid/Timezone") + self.assertEqual(tz, timezone(timedelta(hours=7))) + + if __name__ == "__main__": unittest.main()