itch-jam-scout 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arcade Forge
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: itch-jam-scout
3
+ Version: 0.2.0
4
+ Summary: Read-only CLI that checks an itch.io jam's real phase, deadlines, and pool size from itch's own embedded JSON, instead of the page's countdown text.
5
+ Author: Arcade Forge
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/fernforge-arcade/itch-jam-scout
8
+ Keywords: itch.io,game-jam,gamedev,cli
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Games/Entertainment
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Dynamic: license-file
18
+
19
+ # itch-jam-scout
20
+
21
+ A read-only CLI that answers the two questions that actually matter before you
22
+ enter an itch.io game jam: **what phase is it in right now**, and **how big is
23
+ the pool you're competing in**.
24
+
25
+ ```
26
+ pip install itch-jam-scout
27
+ jam-scout trijam-379 codex-game-jam-2026
28
+ ```
29
+
30
+ ```json
31
+ {
32
+ "url": "https://itch.io/jam/trijam-379",
33
+ "start_date": "2026-07-04 00:00:00",
34
+ "end_date": "2026-07-06 00:00:00",
35
+ "voting_end_date": "2026-07-13 00:00:00",
36
+ "phase": "voting open",
37
+ "rating_queue_disclosed": false,
38
+ "coverage_rule_disclosed": false,
39
+ "entry_count_hint": "28 entries"
40
+ }
41
+ ```
42
+
43
+ No `pip`? It's one file with zero dependencies — `python3 jam_scout.py <jam-slug>` works straight from a clone.
44
+
45
+ ## Why this exists
46
+
47
+ itch jam pages show a countdown timer in the page header, and that timer is
48
+ sometimes wrong — hosts extend deadlines after the page ships, and the
49
+ human-readable text doesn't always get updated in step. The one place that
50
+ can't lie is the `I.ViewJam(...)` JSON blob itch embeds in every jam page to
51
+ drive its own countdown widget. This tool parses that blob directly instead
52
+ of trusting the rendered text, so a moved deadline shows up correctly the
53
+ first time you run it, not after you've already miscounted.
54
+
55
+ It also surfaces the join/entry count where itch exposes it. That number
56
+ matters more than people give it credit for: in a jam with 800 entries, your
57
+ own rating activity is noise. In a jam with 20, it's a meaningful fraction of
58
+ total votes — worth knowing before you decide where to spend a Saturday.
59
+
60
+ ## What it checks
61
+
62
+ - **Phase** — not started / submissions open / voting open / closed, computed
63
+ against the jam's actual start/end/voting-end timestamps, not the countdown
64
+ text.
65
+ - **Pool size** — join or entry count, when itch's page markup exposes it.
66
+ - **Rating Queue mention** — whether the host's own jam description names
67
+ itch's Rating Queue system in text. This is a text search, not an API flag:
68
+ a `false` means "not mentioned on the page," not "confirmed off." Treat a
69
+ `true` as a real signal and a `false` as no signal either way.
70
+ - **Coverage rule mention** — whether the page states a minimum number of
71
+ ratings you have to give or receive to count as a valid entry. Worth
72
+ checking because jam size alone doesn't predict how many entries actually
73
+ get rated: a 65-entry jam with no enforced minimum left 11 entries at zero
74
+ ratings, while a 485-entry jam with one rated 483 of them. In practice most
75
+ hosts don't put this in the page description even when it's true, so
76
+ expect `false` a lot — read it as "not stated here," not "doesn't exist."
77
+
78
+ ## What it doesn't do
79
+
80
+ No login, no rating, no write actions of any kind — it fetches one public
81
+ page and parses it. It can't tell you whether Rating Queue is toggled on for
82
+ a jam that doesn't say so in its description; that toggle only becomes
83
+ visible to logged-in entrants on the jam's own rate page, which is out of
84
+ scope for something meant to run anonymously against any jam slug.
85
+
86
+ ## Usage
87
+
88
+ ```
89
+ jam-scout <jam-slug> [<jam-slug> ...]
90
+ jam-scout https://itch.io/jam/some-jam-2026
91
+ ```
92
+
93
+ Or, without installing anything: `python3 jam_scout.py <jam-slug>`.
94
+
95
+ Pass as many slugs or full URLs as you want; each prints its own JSON object.
96
+
97
+ ## Requirements
98
+
99
+ Python 3.8+, standard library only. No dependencies to install.
100
+
101
+ ---
102
+
103
+ Built autonomously by an AI agent as part of [Arcade Forge](https://fernforge.itch.io), an itch.io distribution experiment.
@@ -0,0 +1,85 @@
1
+ # itch-jam-scout
2
+
3
+ A read-only CLI that answers the two questions that actually matter before you
4
+ enter an itch.io game jam: **what phase is it in right now**, and **how big is
5
+ the pool you're competing in**.
6
+
7
+ ```
8
+ pip install itch-jam-scout
9
+ jam-scout trijam-379 codex-game-jam-2026
10
+ ```
11
+
12
+ ```json
13
+ {
14
+ "url": "https://itch.io/jam/trijam-379",
15
+ "start_date": "2026-07-04 00:00:00",
16
+ "end_date": "2026-07-06 00:00:00",
17
+ "voting_end_date": "2026-07-13 00:00:00",
18
+ "phase": "voting open",
19
+ "rating_queue_disclosed": false,
20
+ "coverage_rule_disclosed": false,
21
+ "entry_count_hint": "28 entries"
22
+ }
23
+ ```
24
+
25
+ No `pip`? It's one file with zero dependencies — `python3 jam_scout.py <jam-slug>` works straight from a clone.
26
+
27
+ ## Why this exists
28
+
29
+ itch jam pages show a countdown timer in the page header, and that timer is
30
+ sometimes wrong — hosts extend deadlines after the page ships, and the
31
+ human-readable text doesn't always get updated in step. The one place that
32
+ can't lie is the `I.ViewJam(...)` JSON blob itch embeds in every jam page to
33
+ drive its own countdown widget. This tool parses that blob directly instead
34
+ of trusting the rendered text, so a moved deadline shows up correctly the
35
+ first time you run it, not after you've already miscounted.
36
+
37
+ It also surfaces the join/entry count where itch exposes it. That number
38
+ matters more than people give it credit for: in a jam with 800 entries, your
39
+ own rating activity is noise. In a jam with 20, it's a meaningful fraction of
40
+ total votes — worth knowing before you decide where to spend a Saturday.
41
+
42
+ ## What it checks
43
+
44
+ - **Phase** — not started / submissions open / voting open / closed, computed
45
+ against the jam's actual start/end/voting-end timestamps, not the countdown
46
+ text.
47
+ - **Pool size** — join or entry count, when itch's page markup exposes it.
48
+ - **Rating Queue mention** — whether the host's own jam description names
49
+ itch's Rating Queue system in text. This is a text search, not an API flag:
50
+ a `false` means "not mentioned on the page," not "confirmed off." Treat a
51
+ `true` as a real signal and a `false` as no signal either way.
52
+ - **Coverage rule mention** — whether the page states a minimum number of
53
+ ratings you have to give or receive to count as a valid entry. Worth
54
+ checking because jam size alone doesn't predict how many entries actually
55
+ get rated: a 65-entry jam with no enforced minimum left 11 entries at zero
56
+ ratings, while a 485-entry jam with one rated 483 of them. In practice most
57
+ hosts don't put this in the page description even when it's true, so
58
+ expect `false` a lot — read it as "not stated here," not "doesn't exist."
59
+
60
+ ## What it doesn't do
61
+
62
+ No login, no rating, no write actions of any kind — it fetches one public
63
+ page and parses it. It can't tell you whether Rating Queue is toggled on for
64
+ a jam that doesn't say so in its description; that toggle only becomes
65
+ visible to logged-in entrants on the jam's own rate page, which is out of
66
+ scope for something meant to run anonymously against any jam slug.
67
+
68
+ ## Usage
69
+
70
+ ```
71
+ jam-scout <jam-slug> [<jam-slug> ...]
72
+ jam-scout https://itch.io/jam/some-jam-2026
73
+ ```
74
+
75
+ Or, without installing anything: `python3 jam_scout.py <jam-slug>`.
76
+
77
+ Pass as many slugs or full URLs as you want; each prints its own JSON object.
78
+
79
+ ## Requirements
80
+
81
+ Python 3.8+, standard library only. No dependencies to install.
82
+
83
+ ---
84
+
85
+ Built autonomously by an AI agent as part of [Arcade Forge](https://fernforge.itch.io), an itch.io distribution experiment.
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: itch-jam-scout
3
+ Version: 0.2.0
4
+ Summary: Read-only CLI that checks an itch.io jam's real phase, deadlines, and pool size from itch's own embedded JSON, instead of the page's countdown text.
5
+ Author: Arcade Forge
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/fernforge-arcade/itch-jam-scout
8
+ Keywords: itch.io,game-jam,gamedev,cli
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Games/Entertainment
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Dynamic: license-file
18
+
19
+ # itch-jam-scout
20
+
21
+ A read-only CLI that answers the two questions that actually matter before you
22
+ enter an itch.io game jam: **what phase is it in right now**, and **how big is
23
+ the pool you're competing in**.
24
+
25
+ ```
26
+ pip install itch-jam-scout
27
+ jam-scout trijam-379 codex-game-jam-2026
28
+ ```
29
+
30
+ ```json
31
+ {
32
+ "url": "https://itch.io/jam/trijam-379",
33
+ "start_date": "2026-07-04 00:00:00",
34
+ "end_date": "2026-07-06 00:00:00",
35
+ "voting_end_date": "2026-07-13 00:00:00",
36
+ "phase": "voting open",
37
+ "rating_queue_disclosed": false,
38
+ "coverage_rule_disclosed": false,
39
+ "entry_count_hint": "28 entries"
40
+ }
41
+ ```
42
+
43
+ No `pip`? It's one file with zero dependencies — `python3 jam_scout.py <jam-slug>` works straight from a clone.
44
+
45
+ ## Why this exists
46
+
47
+ itch jam pages show a countdown timer in the page header, and that timer is
48
+ sometimes wrong — hosts extend deadlines after the page ships, and the
49
+ human-readable text doesn't always get updated in step. The one place that
50
+ can't lie is the `I.ViewJam(...)` JSON blob itch embeds in every jam page to
51
+ drive its own countdown widget. This tool parses that blob directly instead
52
+ of trusting the rendered text, so a moved deadline shows up correctly the
53
+ first time you run it, not after you've already miscounted.
54
+
55
+ It also surfaces the join/entry count where itch exposes it. That number
56
+ matters more than people give it credit for: in a jam with 800 entries, your
57
+ own rating activity is noise. In a jam with 20, it's a meaningful fraction of
58
+ total votes — worth knowing before you decide where to spend a Saturday.
59
+
60
+ ## What it checks
61
+
62
+ - **Phase** — not started / submissions open / voting open / closed, computed
63
+ against the jam's actual start/end/voting-end timestamps, not the countdown
64
+ text.
65
+ - **Pool size** — join or entry count, when itch's page markup exposes it.
66
+ - **Rating Queue mention** — whether the host's own jam description names
67
+ itch's Rating Queue system in text. This is a text search, not an API flag:
68
+ a `false` means "not mentioned on the page," not "confirmed off." Treat a
69
+ `true` as a real signal and a `false` as no signal either way.
70
+ - **Coverage rule mention** — whether the page states a minimum number of
71
+ ratings you have to give or receive to count as a valid entry. Worth
72
+ checking because jam size alone doesn't predict how many entries actually
73
+ get rated: a 65-entry jam with no enforced minimum left 11 entries at zero
74
+ ratings, while a 485-entry jam with one rated 483 of them. In practice most
75
+ hosts don't put this in the page description even when it's true, so
76
+ expect `false` a lot — read it as "not stated here," not "doesn't exist."
77
+
78
+ ## What it doesn't do
79
+
80
+ No login, no rating, no write actions of any kind — it fetches one public
81
+ page and parses it. It can't tell you whether Rating Queue is toggled on for
82
+ a jam that doesn't say so in its description; that toggle only becomes
83
+ visible to logged-in entrants on the jam's own rate page, which is out of
84
+ scope for something meant to run anonymously against any jam slug.
85
+
86
+ ## Usage
87
+
88
+ ```
89
+ jam-scout <jam-slug> [<jam-slug> ...]
90
+ jam-scout https://itch.io/jam/some-jam-2026
91
+ ```
92
+
93
+ Or, without installing anything: `python3 jam_scout.py <jam-slug>`.
94
+
95
+ Pass as many slugs or full URLs as you want; each prints its own JSON object.
96
+
97
+ ## Requirements
98
+
99
+ Python 3.8+, standard library only. No dependencies to install.
100
+
101
+ ---
102
+
103
+ Built autonomously by an AI agent as part of [Arcade Forge](https://fernforge.itch.io), an itch.io distribution experiment.
@@ -0,0 +1,9 @@
1
+ LICENSE
2
+ README.md
3
+ jam_scout.py
4
+ pyproject.toml
5
+ itch_jam_scout.egg-info/PKG-INFO
6
+ itch_jam_scout.egg-info/SOURCES.txt
7
+ itch_jam_scout.egg-info/dependency_links.txt
8
+ itch_jam_scout.egg-info/entry_points.txt
9
+ itch_jam_scout.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ jam-scout = jam_scout:cli
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ jam_scout.py — screen an itch.io jam page for the visibility-algorithm axis
4
+ (opportunity p-gje5aa: pick jams whose ranking mechanic rewards rating volume).
5
+
6
+ Usage:
7
+ python3 jam_scout.py <jam-slug-or-url> [<jam-slug-or-url> ...]
8
+
9
+ For each jam, fetches the public jam page and reports:
10
+ - submission/voting window (parsed from itch's embedded I.ViewJam JSON — the
11
+ only authoritative source; the human-readable countdown text has been wrong
12
+ on this jam before)
13
+ - current phase (submissions open / voting open / closed) as of now (UTC)
14
+ - whether the host's own description text names itch's Rating Queue system
15
+ (the fairness-floor mechanic — NOT a "rate more to be seen more" multiplier,
16
+ see memory/discovery-backlog.md) — this is host-disclosed, not an API flag,
17
+ so a false "no" just means "not verifiable from the page", not "absent"
18
+ - entry count, when the page exposes it, since a small pool is where an
19
+ agent's own rating activity is a bigger fraction of total votes
20
+ - whether the page states a minimum-ratings-to-unlock-results rule, since
21
+ real coverage data shows jam SIZE alone doesn't predict how many entries
22
+ get rated (a 65-entry jam left 11 entries at zero; a 485-entry jam with an
23
+ enforced rating requirement rated 483/485) — an explicit rule is a much
24
+ better signal than pool size
25
+
26
+ No login, no rating, no write actions — read-only reconnaissance only.
27
+ """
28
+ import sys
29
+ import re
30
+ import json
31
+ import urllib.request
32
+ from datetime import datetime, timezone
33
+
34
+ UA = "Mozilla/5.0 ArcadeForge/1.0"
35
+
36
+
37
+ def fetch(url):
38
+ req = urllib.request.Request(url, headers={"User-Agent": UA})
39
+ with urllib.request.urlopen(req, timeout=20) as r:
40
+ return r.read().decode("utf-8", errors="replace")
41
+
42
+
43
+ def normalize_url(arg):
44
+ if arg.startswith("http"):
45
+ return arg
46
+ return f"https://itch.io/jam/{arg}"
47
+
48
+
49
+ def parse_view_jam(html):
50
+ m = re.search(r"I\.ViewJam\('#view_jam_\d+',\s*(\{.*?\})\)", html)
51
+ if not m:
52
+ return None
53
+ return json.loads(m.group(1))
54
+
55
+
56
+ def parse_dt(s):
57
+ return datetime.strptime(s, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc)
58
+
59
+
60
+ def phase(meta, now):
61
+ start = parse_dt(meta["start_date"])
62
+ end = parse_dt(meta["end_date"])
63
+ voting_end = meta.get("voting_end_date")
64
+ voting_end = parse_dt(voting_end) if voting_end else None
65
+ if now < start:
66
+ return "not started"
67
+ if now < end:
68
+ return "submissions open"
69
+ if voting_end and now < voting_end:
70
+ return "voting open"
71
+ return "closed"
72
+
73
+
74
+ def rating_queue_signal(html):
75
+ return bool(re.search(r"rating queue", html, re.IGNORECASE))
76
+
77
+
78
+ def coverage_rule_signal(html):
79
+ # Look for host-stated language requiring a minimum number of ratings
80
+ # given/received to unlock results or count as a valid entry. This is a
81
+ # weaker text-match than rating_queue_signal on purpose: hosts phrase it
82
+ # many ways ("rate at least 5 games", "minimum of 3 ratings to qualify").
83
+ # A False here means "not stated in the page text", not "does not exist" —
84
+ # some jams enforce this via a separate rules tab this tool doesn't fetch.
85
+ return bool(re.search(
86
+ r"(rate|review)\s+(at least|a minimum of|\d+)\s+\w*\s*(game|entr|submission)",
87
+ html, re.IGNORECASE,
88
+ ))
89
+
90
+
91
+ def entry_count(html):
92
+ # itch shows a "Joined"/"Entries" stat pair in stat_box widgets; entries only
93
+ # appears once submissions have closed, so "Joined" is the best pre-close proxy
94
+ # for pool size (bigger pool = your own ratings move the needle less).
95
+ stats = {label: value for value, label in re.findall(
96
+ r'stat_value">(\d+)</div><div class="stat_label">([^<]+)', html)}
97
+ for label in ("Entries", "Submissions", "Joined"):
98
+ if label in stats:
99
+ return f"{stats[label]} {label.lower()}"
100
+ m = re.search(r'([\d,]+)\s+(?:entries|submissions|games)\b', html, re.IGNORECASE)
101
+ if m:
102
+ return m.group(1)
103
+ return None
104
+
105
+
106
+ def scout(arg, now=None):
107
+ url = normalize_url(arg)
108
+ now = now or datetime.now(timezone.utc)
109
+ html = fetch(url)
110
+ meta = parse_view_jam(html)
111
+ result = {"url": url}
112
+ if meta is None:
113
+ result["error"] = "no I.ViewJam block found (page shape changed or not a jam page)"
114
+ return result
115
+ result["start_date"] = meta.get("start_date")
116
+ result["end_date"] = meta.get("end_date")
117
+ result["voting_end_date"] = meta.get("voting_end_date")
118
+ result["phase"] = phase(meta, now)
119
+ result["rating_queue_disclosed"] = rating_queue_signal(html)
120
+ result["coverage_rule_disclosed"] = coverage_rule_signal(html)
121
+ result["entry_count_hint"] = entry_count(html)
122
+ return result
123
+
124
+
125
+ def main(argv):
126
+ if len(argv) < 2:
127
+ print(__doc__)
128
+ return 1
129
+ for arg in argv[1:]:
130
+ r = scout(arg)
131
+ print(json.dumps(r, indent=2))
132
+ return 0
133
+
134
+
135
+ def cli():
136
+ sys.exit(main(sys.argv))
137
+
138
+
139
+ if __name__ == "__main__":
140
+ cli()
@@ -0,0 +1,29 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "itch-jam-scout"
7
+ version = "0.2.0"
8
+ description = "Read-only CLI that checks an itch.io jam's real phase, deadlines, and pool size from itch's own embedded JSON, instead of the page's countdown text."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Arcade Forge" }]
13
+ keywords = ["itch.io", "game-jam", "gamedev", "cli"]
14
+ classifiers = [
15
+ "Environment :: Console",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Topic :: Games/Entertainment",
20
+ ]
21
+
22
+ [project.urls]
23
+ Homepage = "https://github.com/fernforge-arcade/itch-jam-scout"
24
+
25
+ [project.scripts]
26
+ jam-scout = "jam_scout:cli"
27
+
28
+ [tool.setuptools]
29
+ py-modules = ["jam_scout"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+