gh-space-shooter 0.0.1__py3-none-any.whl

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,172 @@
1
+ """GitHub API client for fetching contribution graph data."""
2
+
3
+ from datetime import datetime
4
+ from typing import TypedDict
5
+
6
+ import httpx
7
+ from dotenv import load_dotenv
8
+
9
+ from .constants import NUM_WEEKS
10
+
11
+ # Load environment variables from .env file
12
+ load_dotenv()
13
+
14
+
15
+ class ContributionDay(TypedDict):
16
+ """Represents a single day's contribution data."""
17
+
18
+ date: str
19
+ count: int
20
+ level: int # 0-4 intensity level
21
+
22
+
23
+ class ContributionWeek(TypedDict):
24
+ """Represents a week of contribution data."""
25
+
26
+ days: list[ContributionDay]
27
+
28
+
29
+ class ContributionData(TypedDict):
30
+ """Complete contribution graph data."""
31
+
32
+ username: str
33
+ total_contributions: int
34
+ weeks: list[ContributionWeek]
35
+ fetched_at: str
36
+
37
+
38
+ class GitHubAPIError(Exception):
39
+ """Raised when GitHub API request fails."""
40
+
41
+ pass
42
+
43
+
44
+ class GitHubClient:
45
+ """Client for interacting with GitHub's GraphQL API."""
46
+
47
+ GITHUB_API_URL = "https://api.github.com/graphql"
48
+ GET_CONTRIBUTION_GRAPH_QUERY = """
49
+ query($username: String!) {
50
+ user(login: $username) {
51
+ contributionsCollection {
52
+ contributionCalendar {
53
+ totalContributions
54
+ weeks {
55
+ contributionDays {
56
+ date
57
+ contributionCount
58
+ contributionLevel
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ """
66
+
67
+ def __init__(self, token: str):
68
+ """
69
+ Initialize GitHub client.
70
+
71
+ Args:
72
+ token: GitHub personal access token (required).
73
+ """
74
+ self.token = token
75
+ self.client = httpx.Client(
76
+ headers={
77
+ "Authorization": f"Bearer {self.token}",
78
+ "Content-Type": "application/json",
79
+ },
80
+ timeout=30.0,
81
+ )
82
+
83
+ def __enter__(self):
84
+ """Context manager entry."""
85
+ return self
86
+
87
+ def __exit__(self, exc_type, exc_val, exc_tb):
88
+ """Context manager exit - close HTTP client."""
89
+ self.close()
90
+
91
+ def close(self):
92
+ self.client.close()
93
+
94
+ def get_contribution_graph(self, username: str) -> ContributionData:
95
+ """
96
+ Fetch contribution graph for a GitHub user (last 52 weeks).
97
+
98
+ Args:
99
+ username: GitHub username to fetch data for
100
+
101
+ Returns:
102
+ ContributionData with user's contribution information
103
+
104
+ Raises:
105
+ GitHubAPIError: If the API request fails
106
+ """
107
+
108
+ try:
109
+ response = self.client.post(
110
+ self.GITHUB_API_URL,
111
+ json={
112
+ "query": self.GET_CONTRIBUTION_GRAPH_QUERY,
113
+ "variables": {"username": username}
114
+ },
115
+ )
116
+ response.raise_for_status()
117
+ except httpx.HTTPError as e:
118
+ raise GitHubAPIError(f"Failed to fetch data from GitHub API: {e}") from e
119
+
120
+ data = response.json()
121
+
122
+ # Check for GraphQL errors
123
+ if "errors" in data:
124
+ errors = data["errors"]
125
+ error_messages = [error.get("message", str(error)) for error in errors]
126
+ raise GitHubAPIError(f"GraphQL errors: {', '.join(error_messages)}")
127
+
128
+ # Check if user exists
129
+ if not data.get("data", {}).get("user"):
130
+ raise GitHubAPIError(f"User '{username}' not found")
131
+
132
+ # Extract contribution data
133
+ calendar = data["data"]["user"]["contributionsCollection"][
134
+ "contributionCalendar"
135
+ ]
136
+
137
+ # Parse weeks and days
138
+ weeks: list[ContributionWeek] = []
139
+ for week_data in calendar["weeks"]:
140
+ days: list[ContributionDay] = []
141
+ for day_data in week_data["contributionDays"]:
142
+ days.append(
143
+ {
144
+ "date": day_data["date"],
145
+ "count": day_data["contributionCount"],
146
+ "level": self._contribution_level_to_int(
147
+ day_data["contributionLevel"]
148
+ ),
149
+ }
150
+ )
151
+ weeks.append({"days": days})
152
+
153
+ # Always return exactly NUM_WEEKS (truncate if more)
154
+ weeks = weeks[-NUM_WEEKS:] if len(weeks) > NUM_WEEKS else weeks
155
+
156
+ return {
157
+ "username": username,
158
+ "total_contributions": calendar["totalContributions"],
159
+ "weeks": weeks,
160
+ "fetched_at": datetime.now().isoformat(),
161
+ }
162
+
163
+ LEVEL_MAP = {
164
+ "NONE": 0,
165
+ "FIRST_QUARTILE": 1,
166
+ "SECOND_QUARTILE": 2,
167
+ "THIRD_QUARTILE": 3,
168
+ "FOURTH_QUARTILE": 4,
169
+ }
170
+
171
+ def _contribution_level_to_int(self, level: str) -> int:
172
+ return self.LEVEL_MAP.get(level, 0)
File without changes
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: gh-space-shooter
3
+ Version: 0.0.1
4
+ Summary: A CLI tool that visualizes GitHub contribution graphs as gamified GIFs
5
+ Author-email: zane <czl970721@gmail.com>
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.13
8
+ Requires-Dist: httpx>=0.27.0
9
+ Requires-Dist: pillow>=10.0.0
10
+ Requires-Dist: python-dotenv>=1.0.0
11
+ Requires-Dist: rich>=13.0.0
12
+ Requires-Dist: typer>=0.12.0
13
+ Description-Content-Type: text/markdown
14
+
15
+ # gh-space-shooter 🚀
16
+
17
+ Transform your GitHub contribution graph into an epic space shooter game!
18
+
19
+ ![Example Game](example.gif)
20
+
21
+ ## Features
22
+
23
+ - 🚀 **Galaga-style space shooter** - Classic arcade gameplay with your contribution data
24
+ - 📊 **GitHub integration** - Fetches your last 52 weeks of contributions automatically
25
+ - 🎮 **Smart enemy AI** - Multiple attack strategies (columns, rows, random patterns)
26
+ - 💥 **Particle effects** - Explosions with randomized particles and smooth animations
27
+ - 🎨 **Polished graphics** - Rounded enemies, smooth ship design, starfield background
28
+ - 📈 **Contribution stats** - View your coding activity statistics
29
+ - 💾 **Export options** - Save both the GIF and raw JSON data
30
+
31
+ ## Installation
32
+
33
+ ### From PyPI (Recommended)
34
+
35
+ ```bash
36
+ pip install gh-space-shooter
37
+ ```
38
+
39
+ ### From Source
40
+
41
+ ```bash
42
+ # Clone the repository
43
+ git clone https://github.com/yourusername/gh-space-shooter.git
44
+ cd gh-space-shooter
45
+
46
+ # Install with uv
47
+ uv sync
48
+
49
+ # Or with pip
50
+ pip install -e .
51
+ ```
52
+
53
+ ## Setup
54
+
55
+ 1. Create a GitHub Personal Access Token:
56
+ - Go to https://github.com/settings/tokens
57
+ - Click "Generate new token (classic)"
58
+ - Select scopes: `read:user`
59
+ - Copy the generated token
60
+
61
+ 2. Set up your environment:
62
+ ```bash
63
+ # Copy the example env file
64
+ touch .env
65
+ echo "GH_TOKEN=your_token_here" >> .env
66
+ ```
67
+
68
+ Alternatively, export the token directly:
69
+ ```bash
70
+ export GH_TOKEN=your_token_here
71
+ ```
72
+
73
+ ## Usage
74
+
75
+ ### Generate Your Game GIF
76
+
77
+ Transform your GitHub contributions into an epic space shooter!
78
+
79
+ ```bash
80
+ # Basic usage - generates username-gh-space-shooter.gif
81
+ gh-space-shooter <username>
82
+
83
+ # Examples
84
+ gh-space-shooter torvalds
85
+ gh-space-shooter octocat
86
+
87
+ # Specify custom output filename
88
+ gh-space-shooter torvalds --output my-epic-game.gif
89
+ gh-space-shooter torvalds -o my-game.gif
90
+
91
+ # Choose enemy attack strategy
92
+ gh-space-shooter torvalds --strategy column # Enemies attack in columns
93
+ gh-space-shooter torvalds --strategy row # Enemies attack in rows
94
+ gh-space-shooter torvalds -s random # Random chaos (default)
95
+ ```
96
+
97
+ This creates an animated GIF showing:
98
+ - Your contribution graph as enemies (more contributions = stronger enemies)
99
+ - A Galaga-style spaceship battling through your coding history
100
+ - Enemy attack patterns based on your chosen strategy
101
+ - Smooth animations with randomized particle effects
102
+ - Your contribution stats displayed in the console
103
+
104
+ ### Advanced Options
105
+
106
+ ```bash
107
+ # Save raw contribution data to JSON
108
+ gh-space-shooter torvalds --raw-output data.json
109
+
110
+ # Load from previously saved JSON (saves API rate limits)
111
+ gh-space-shooter --raw-input data.json --output game.gif
112
+
113
+ # Combine options
114
+ gh-space-shooter torvalds -o game.gif -ro data.json -s column
115
+ ```
116
+
117
+ ### Data Format
118
+
119
+ When saved to JSON, the data includes:
120
+ ```json
121
+ {
122
+ "username": "torvalds",
123
+ "total_contributions": 1234,
124
+ "weeks": [
125
+ {
126
+ "days": [
127
+ {
128
+ "date": "2024-01-01",
129
+ "count": 5,
130
+ "level": 2
131
+ }
132
+ ]
133
+ }
134
+ ],
135
+ "fetched_at": "2024-12-30T12:00:00"
136
+ }
137
+ ```
138
+
139
+ ## License
140
+
141
+ MIT
@@ -0,0 +1,28 @@
1
+ gh_space_shooter/__init__.py,sha256=jBFfHY3YC8-3m-eVPIo3CWoQLsy5Yvd0vcbHVbUDTWY,337
2
+ gh_space_shooter/cli.py,sha256=AHONWh6Kv9Y8D7OWsnOH6kOjN73aSCXF8Ats9jKJwkY,5310
3
+ gh_space_shooter/console_printer.py,sha256=opT5vITkPJ_9BCPNMDays6EtXez9m86YXY9pK5_Hdh8,2345
4
+ gh_space_shooter/constants.py,sha256=HR_FYewYnjQ8V5fJwMS2UhiYSS3l0iR5Y8bmaS3thQE,498
5
+ gh_space_shooter/github_client.py,sha256=Ugt4wi8RAvnYPO8fF7-MgugD5t91nxQUcEnCf-Hmc-8,4771
6
+ gh_space_shooter/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ gh_space_shooter/game/__init__.py,sha256=bIc-S0hOiYqYBtdmd9_CedqYY07Il6XiV__TqcbtJNA,707
8
+ gh_space_shooter/game/animator.py,sha256=5nCY0xfRl6tifwixUlUlVXCQfDjhsR6HbQBKHHmYHH8,2998
9
+ gh_space_shooter/game/game_state.py,sha256=pvKc-FAapVXuFNb2hCGoOhJI1P8kWXkyCf_lruc-j50,2804
10
+ gh_space_shooter/game/render_context.py,sha256=TPJw9RmRB0_786W-O7nfB61UoDzDT_SlEVdaSlW750c,1805
11
+ gh_space_shooter/game/renderer.py,sha256=J13eUOWHkImtiNWGyvRqORM7ngg1Q7LBUgs4cWFl26g,1617
12
+ gh_space_shooter/game/drawables/__init__.py,sha256=lzo3O5cxahrYTyOQVrz7rQ3Prkaj8Z_dpzlt0UdurOo,306
13
+ gh_space_shooter/game/drawables/bullet.py,sha256=8DjqXI1Ab4IINi77szJuRc5Bqilb0gvofKgcQ9s0ONM,2920
14
+ gh_space_shooter/game/drawables/drawable.py,sha256=xIJWI5m-kZCbvCfgYF4jiqTvPFelViJ1gLilz7adJoM,738
15
+ gh_space_shooter/game/drawables/enemy.py,sha256=RCIE4Vn2tis0iJmJO_JUIxnomSgDSsfFhrcPPWmFLhE,1921
16
+ gh_space_shooter/game/drawables/explosion.py,sha256=Ek2CCjjifkLVSpaJ4Nv8ey1VhkXNK6HeKK3wB0fRI5g,2618
17
+ gh_space_shooter/game/drawables/ship.py,sha256=j389lXOUWvPoFZUNvVN-OEjhTtIlEkAVeEYqF7gDBIE,3216
18
+ gh_space_shooter/game/drawables/starfield.py,sha256=ON_cQ6Qfg30ocLHQEp1dceqputHmxJ7sf61o4uNF4Ng,2530
19
+ gh_space_shooter/game/strategies/__init__.py,sha256=a34i4FlNv-aQ-oEkT1ha7oJDBt7Cd7EFpbMWsYNHpW0,338
20
+ gh_space_shooter/game/strategies/base_strategy.py,sha256=IwPdthCWkgsFdUsMjQgifpSJv1bPRCCuUMF0o00y6pk,1152
21
+ gh_space_shooter/game/strategies/column_strategy.py,sha256=AQHXVTRe5BEjc4QHRL9QLtkkelTzGUGf2531POGtkG0,1728
22
+ gh_space_shooter/game/strategies/random_strategy.py,sha256=l0GKMkGJa_QEvNrN57R_KgWDBafGebP926fZJqpdghc,2215
23
+ gh_space_shooter/game/strategies/row_strategy.py,sha256=8izcioSGrVYGdQ__GrdfAQxnJt1BLhwNdumeuQiDhpg,1426
24
+ gh_space_shooter-0.0.1.dist-info/METADATA,sha256=YIu9i1FpQeGP4PCJvohXG_cs2_O9bFR9HMHhWTu0J84,3493
25
+ gh_space_shooter-0.0.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
26
+ gh_space_shooter-0.0.1.dist-info/entry_points.txt,sha256=SmK2ET5vz62eaMC4mhxmLJ1f_H9qSTXOvFOHNo-qwCk,62
27
+ gh_space_shooter-0.0.1.dist-info/licenses/LICENSE,sha256=teCrgzzcmjYCQ-RqXkDmICcHMN1AfaabrjZsW6O3KEk,1075
28
+ gh_space_shooter-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ gh-space-shooter = gh_space_shooter.cli:app
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Zane Chen
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
13
+ all 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
21
+ THE SOFTWARE.