Commit Graph

48 Commits

Author SHA1 Message Date
shoko
54679cac44 Merge branch 'pr-35' into 0.0.3-draft 2026-03-27 02:26:52 +00:00
shoko
ca13a2e194 Merge branch 'pr-34' (URL encoding) into 0.0.3-draft 2026-03-27 02:26:41 +00:00
shoko
893243ba39 Merge branch 'pr-33' into 0.0.3-draft 2026-03-27 02:24:59 +00:00
shoko
2b7a2bda90 Merge branch 'pr-32' into 0.0.3-draft 2026-03-27 02:24:48 +00:00
shoko
aef5f79dad Merge branch 'pr-31' (timezone) into 0.0.3-draft
Conflicts resolved:
- browse.py: keep both --starts-before and --timezone args
- test_browse.py: combine TestStartsBeforeFilter and TestTimezoneParsing
- SKILL.md: combine documentation for both args
2026-03-27 02:24:28 +00:00
shoko
b4148570f4 Merge branch 'pr-30' into 0.0.3-draft 2026-03-27 01:50:42 +00:00
shoko
2c636048e7 security(polymarket-browse): improve response size limit with dynamic calculation
- Replace fixed 10MB limit with dynamic calculation
- get_max_response_size() computes limit based on PAGE_SIZE * multiplier
- Uses 10x multiplier (e.g., PAGE_SIZE=50 -> ~500KB * 10 = ~5MB)
- Clamped between 10MB minimum and 100MB maximum
- Formula: max(PAGE_SIZE * multiplier, 10MB) capped at 100MB
2026-03-27 01:36:32 +00:00
shoko
3016d1287c test(polymarket-browse): add URL encoding unit tests
Add TestUrlEncoding class testing quote() encodes:
- Space -> %20
- & -> %26
- = -> %3D
- % -> %25
- + -> %2B
- ( -> %28
- ) -> %29
- # -> %23
2026-03-27 01:14:45 +00:00
shoko
36a7e8b3eb security(polymarket-browse): add MAX_RESPONSE_SIZE limit to prevent memory exhaustion
- Add MAX_RESPONSE_SIZE = 10MB constant
- Check response size before json.loads() in fetch_page()
- Raises ValueError if response exceeds limit
- Prevents memory exhaustion from malicious/gigantic API responses
2026-03-26 19:13:13 +00:00
shoko
bb7eebf502 security(polymarket-browse): use proper URL encoding for --search parameter
- Import quote from urllib.parse
- Replace q.replace(' ', '%20') with quote(q, safe='')
- Properly encodes: &, =, %, +, #, ?, and other special chars
- Prevents URL injection attacks
2026-03-26 19:11:59 +00:00
shoko
3928cdef7c security(polymarket-browse): validate --detail argument and show error if out of range
- Add sys import for stderr/exit
- Validate --detail index before accessing array
- Show error with available range instead of silent fallback to first event
- Exit with code 1 if --detail is out of range
2026-03-26 19:10:40 +00:00
shoko
8bd76f3301 security(polymarket-browse): replace bare except: with specific exception handling
- Lines 386, 400: except: changed to except (ValueError, TypeError):
- ValueError: datetime.fromisoformat parse failure
- TypeError: input is not a string
- Prevents swallowing KeyboardInterrupt, SystemExit, MemoryError
2026-03-26 19:09:19 +00:00
shoko
0a1aab7883 fix(polymarket-browse): add --timezone CLI argument for display timezone
- Add parse_timezone() function supporting UTC+X format
- Add --timezone argument (default: UTC+7)
- Module-level _DISPLAY_TZ controls all time display formatting
- get_header_date() and _get_time_data() use _DISPLAY_TZ
- Add TestTimezoneParsing unit tests
- Update SKILL.md documentation
2026-03-26 19:07:59 +00:00
shoko
dfad8d3072 chore(polymarket-browse): add version 0.0.2 to SKILL.md frontmatter polymarket-browse/v0.0.2 2026-03-26 18:43:26 +00:00
shoko
cc197b0c7e feat(polymarket-browse): add --starts-before filter for match events
- Add --starts-before CLI argument accepting Unix timestamp
- Filter match events to only show those starting before timestamp
- LIVE events are always included regardless of timestamp
- Update SKILL.md documentation
- Add TestStartsBeforeFilter with 3 unit tests
2026-03-26 18:27:54 +00:00
c0f008ab8b Merge pull request 'Fix: Event happening exactly now shows LIVE instead of In 0m' (#28) from fix/live-time-display into master 2026-03-26 18:48:23 +01:00
shoko
53c268511a Add test for event happening exactly now shows LIVE
Added test_get_time_data_live_exactly_now to prevent regression.
2026-03-26 17:46:53 +00:00
shoko
7f3b885521 Fix: Event happening exactly now shows 'LIVE' instead of 'In 0m'
Changed condition from total_sec < 0 to total_sec <= 0 to catch the edge case where an event is happening right now.
2026-03-26 17:43:28 +00:00
shoko
e0ae6bbd14 Remove incorrect '100 pages safety cap' claim from SKILL.md
This limit doesn't exist in the code.
2026-03-26 17:37:41 +00:00
f468a12ea4 Merge pull request 'Add parallel fetching, caching, and max_total parameter' (#26) from feat/parallel-fetch-cache into master 2026-03-26 18:23:20 +01:00
shoko
c75d123dfd Update SKILL.md with new caching and parallel fetching documentation 2026-03-26 17:19:03 +00:00
shoko
9d1e328f53 Make page size calculation dynamic based on first API response
- Uses actual event count from page 1 to calculate total_pages
- Removes hardcoded '5' for events per page
- API changes to page size will be handled automatically
- Updated tests to match real API behavior (5 events per page)
2026-03-26 17:15:28 +00:00
shoko
09f3cb9066 Add comment explaining total_pages ceiling division calculation 2026-03-26 17:06:25 +00:00
shoko
1ae60f5661 Fix total_pages calculation bug and add tests
- Fixed total_pages calculation: API returns 5 events/page, not PAGE_SIZE
- This was causing partial=false positives when max_total was used
- Updated tests to use correct pagination values
2026-03-26 16:54:41 +00:00
shoko
bab373ab8f Add unit tests for parallelization, cache, and max_total
- TestParallelFetchConcurrency: verify batch size of 5 and concurrency limit
- TestCacheFunctions: test cache read/write error handling
- TestMaxTotalParameter: test max_total event limiting
2026-03-26 16:43:13 +00:00
shoko
eafbdba4a5 Add parallel fetching, caching, and max_total parameter
- Parallel page fetching with ThreadPoolExecutor (concurrency=5)
- File-based cache with 5 min TTL in ~/.cache/polymarket-browse/
- New --no-cache flag to bypass cache
- New --max-total parameter for early exit
- Updated tests to work with new implementation
2026-03-26 16:29:25 +00:00
0a7911653b Merge pull request 'Fix line lengths in browse.py' (#24) from fix/line-lengths into master 2026-03-26 16:42:53 +01:00
bae69043f8 Merge pull request 'Add type hints to browse.py' (#23) from feat/add-type-hints into master 2026-03-26 16:42:08 +01:00
shoko
d6c0342c0f Fix line lengths in browse.py
Split 2 lines that exceeded 120 chars in print_detail function.
All 62 tests pass.
2026-03-26 15:40:21 +00:00
shoko
ce526b1aa3 Add type hints to browse.py
- Added TypedDict classes for typed event/market structures
- Added type annotations to all functions
- Used Python 3.10+ union syntax (str | None, dict[str, Any])
- All 62 tests pass
2026-03-26 15:35:18 +00:00
ae50fd14f0 Merge pull request 'Fix #14: Refactor print_browse/send_to_telegram into single pipeline' (#22) from fix/issue-14-refactor-browse into master 2026-03-25 20:11:07 +01:00
shoko
c348d6daa1 tests: Add unit tests for browse_events, fetch_all_pages, filter_events, is_match_market, get_ml_market, get_ml_volume, sort_events
New test classes:
- TestIsMatchMarket: 5 tests for is_match_market() classification
- TestGetMlMarket: 5 tests for get_ml_market() and get_ml_volume()
- TestFilterEvents: 5 tests for filter_events() and sort_events()
- TestFetchAllPages: 4 tests for fetch_all_pages() early-exit logic
- TestBrowseEvents: 5 tests for browse_events() sort_by parameter

Total: 24 new tests (62 total, all passing)
2026-03-25 19:08:36 +00:00
shoko
764c75e712 Fix: Switch fetch_page from subprocess to urllib, add early-exit to fetch_all_pages, add sort_by to browse_events
- fetch_page: replace subprocess.run(curl) with urllib (stdlib, cleaner)
- fetch_all_pages: add matches_max/non_matches_max params for early-exit.
  When both are set, stop fetching once quotas are satisfied.
- browse_events: add sort_by param (None='fast' early-exit, 'volume'=full fetch+sort).
  Early-exit only used when sort_by=None (no client-side sort needed).
- Remove subprocess import (no longer needed after migration)
2026-03-25 18:53:11 +00:00
shoko
3a9f8fb365 Fix #14: Refactor print_browse/send_to_telegram into single pipeline
Replace duplicate inline formatting with unified format+render pipeline.

New functions:
- format_match_event(e) — canonical dict for match events
- format_non_match_event(e) — canonical dict for non-match events
- render_match_lines(event_dict, i, mode) — text/HTML renderer
- render_non_match_lines(event_dict, i, mode) — text/HTML renderer
- send_chunked(...) — extracted Telegram chunking logic

Also fixed send_chunked() chunking bug: the original '. ' in line
check never matched event lines (period is followed by '</b>' not space).

Tests: 38 total, all passing.

Fixes: #14
2026-03-25 17:50:54 +00:00
shoko
a7837cec0f Merge #15: Unify duplicate time functions 2026-03-25 14:34:05 +00:00
shoko
8cde441996 Fix #15: Unify duplicate time functions into _get_time_data()
Replace three duplicated time parsing functions with a single
_get_time_data(e, tz) helper returning {time_status, time_urgency, abs_time}.

Deleted functions:
- get_match_time_status(e)  — urgency + status string
- get_match_time_str(e)    — status string only
- get_start_time_wib(e)    — (abs_time, rel_str) tuple

New unified helper:
- _get_time_data(e, tz=None) returns {time_status, time_urgency, abs_time}
- tz defaults to WIB (UTC+7, Indonesia)
- canonical rel_str format: 'LIVE', 'In 6h', '12h ago', etc.
- time_urgency: 0-3 (higher=livelier)

All call sites updated to use _get_time_data():
- format_event(), format_detail_event()
- print_browse(), print_detail()
- send_to_telegram()

Also: removed dead code in print_detail() that called get_match_time_str()
but never used the result.

Tests: 9 new tests for _get_time_data() covering TBD, future, live,
and past event scenarios. 19 tests total, all passing.

Fixes: #15
2026-03-25 13:59:54 +00:00
b2180a4a34 Merge pull request 'Fix #5: HTML injection in Telegram messages' (#20) from fix/issue-5-html-injection-telegram into master 2026-03-25 13:13:52 +01:00
shoko
d0534aedbf Fix #5: HTML injection in Telegram messages
Add escape_html() function to prevent HTML injection in Telegram
parse_mode=HTML messages. Apply escaping to event titles inserted
into <a> tags in send_to_telegram().

- Add escape_html() using stdlib html.escape()
- Escape match event titles (line 648) and non-match titles (line 676)
- Add TestHtmlInjection with 2 tests proving fix:
  - <script> tags escaped as &lt;script&gt;
  - & ampersands escaped as &amp;
- Fixes HIGH severity: titles from Polymarket API were inserted
  without escaping, allowing malformed HTML in Telegram messages
2026-03-25 11:42:42 +00:00
2703b942c1 Merge pull request 'Fix #4: Extract send() to module-level send_telegram_message() for testability' (#19) from fix/issue-4-telegram-token-refactor into master 2026-03-25 12:17:00 +01:00
shoko
f9c4bac7b8 Refactor send() to module-level send_telegram_message() for testability
Extract the nested send() function into a module-level
send_telegram_message(bot_token, chat_id, text, timeout=10)
function. This enables unit testing without hitting the real
Telegram API.

Changes:
- Add send_telegram_message() at module level in TELEGRAM section
- Replace nested send() with thin wrapper that calls
  send_telegram_message()
- Update argparse --telegram help text to use TELEGRAM_BOT_TOKEN
- Add tests/test_browse.py with 8 unit tests covering:
  - Success case (returns message_id)
  - API error (RuntimeError)
  - Invalid token (HTTPError 404)
  - Rate limit (HTTPError 429)
  - Network error (URLError)
  - Timeout (URLError)
  - Custom timeout parameter
  - HTML parse_mode in request

Ref: #4
2026-03-25 11:07:10 +00:00
shoko
c49600cd4d Fix CRITICAL: Telegram bot token exposed in process command line
Replace curl subprocess with urllib.request to prevent token leakage via
ps aux / /proc/*/cmdline. Token now stays in process memory only.

Changes:
- Remove subprocess import, add urllib.parse.urlencode and urllib.request
- Replace curl subprocess call with urlopen(Request(...))
- Change env var BOT_TOKEN -> TELEGRAM_BOT_TOKEN (clearer naming)
- Raise RuntimeError on missing env vars, API errors, or network errors
- Add 10s timeout to urlopen

Fixes #4
2026-03-25 10:46:10 +00:00
shoko
3a988943b9 docs: rename review folder to match skill structure
docs/polymarket-browse/ mirrors skills/polymarket-browse/
Future reviews for this skill can use date-based filenames in the same folder.
2026-03-25 10:02:43 +00:00
shoko
da367c594b docs: add polymarket-browse review (2026-03-25)
Security audit + code quality review of polymarket-browse skill.
Contains 8 security issues, 6 code quality issues, 2 docs issues.
Issues tracked in repo.
2026-03-25 10:00:12 +00:00
shoko
b1b5ecaa2e polymarket-browse: fix relative time - show LIVE instead of In 0m when event is starting now 2026-03-25 06:20:13 +00:00
shoko
88dc651232 polymarket-browse: add --search, --telegram, --matches-only, --non-matches-only flags; fix partial fetch warnings; clean up output formatting 2026-03-24 13:26:29 +00:00
46ecb38049 Add OpenClaw installation guide 2026-03-24 07:26:43 +00:00
a8560f7170 Add installation guide to SKILL.md 2026-03-24 07:20:51 +00:00
cd842df060 Add polymarket-browse skill
Browse tradeable Polymarket events by game category.
Supports Dota 2, Counter Strike, League of Legends, Valorant, NBA, NFL, UFC, Tennis.
Features WIB timezone, compact 6-line format, exponential backoff rate limiting.
2026-03-24 07:18:05 +00:00