├── .env.example ├── Assets ├── dark.png ├── image.png ├── white.png ├── image-1.png └── image-2.png ├── README.md └── weekly_commits /.env.example: -------------------------------------------------------------------------------- 1 | GITHUB_USERNAME= 2 | GITHUB_PAT= -------------------------------------------------------------------------------- /Assets/dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ad1822/weekly-github-waybar-module/HEAD/Assets/dark.png -------------------------------------------------------------------------------- /Assets/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ad1822/weekly-github-waybar-module/HEAD/Assets/image.png -------------------------------------------------------------------------------- /Assets/white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ad1822/weekly-github-waybar-module/HEAD/Assets/white.png -------------------------------------------------------------------------------- /Assets/image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ad1822/weekly-github-waybar-module/HEAD/Assets/image-1.png -------------------------------------------------------------------------------- /Assets/image-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ad1822/weekly-github-waybar-module/HEAD/Assets/image-2.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

🟩🟩⬛🟩⬛

3 | 4 | 5 | # GitHub Weekly Contributions Widget for Waybar 6 | 7 | A terminal or bar integration script that fetches your **GitHub contribution activity for the current week (Sunday to Today)** using the GitHub GraphQL API and renders a **colored heatmap** with detailed tooltips. 8 | 9 | Designed for seamless integration with status bars like **Waybar**, **Polybar**, or any custom desktop widget. 10 | 11 | --- 12 | 13 | ## Features 14 | 15 | - Pulls real-time contribution data from GitHub's GraphQL API. 16 | - Displays a **7-day contribution heatmap** (Sunday–Saturday layout). 17 | - Uses a **color-coded square** (■) system similar to GitHub's own UI. 18 | - Provides a **detailed tooltip** with: 19 | - Date-wise contribution breakdown. 20 | - Total contributions in the week. 21 | 22 | --- 23 | 24 | ## Color Levels 25 | 26 | #### Dark Theme (By Default, If no argv passed) 27 | | Contributions | Color Hex | Meaning | 28 | |---------------|------------|--------------------| 29 | | 0 | `#161b22` | No contributions | 30 | | 1–3 | `#0e4429` | Low activity | 31 | | 4–6 | `#006d32` | Moderate activity | 32 | | 7–9 | `#26a641` | High activity | 33 | | 10+ | `#39d353` | Very high activity | 34 | 35 | ![Dark Theme](Assets/dark.png) 36 | 37 | #### Light Theme 38 | | Contributions | Color Hex | Meaning | 39 | |---------------|------------|--------------------| 40 | | 0 | `#ebedf0` | No contributions | 41 | | 1–3 | `#9be9a8` | Low activity | 42 | | 4–6 | `#40c463` | Moderate activity | 43 | | 7–9 | `#30a14e` | High activity | 44 | | 10+ | `#216e39` | Very high activity | 45 | 46 | ![Light Theme](Assets/white.png) 47 | 48 | 49 | ## Example 50 | 51 | 52 | | Preview | 53 | |---------------------------------| 54 | | ![commits](Assets/image.png) | 55 | | | 56 | | ![whole](Assets/image-2.png) | 57 | | | 58 | | ![half](Assets/image-1.png) | 59 | 60 | 61 | --- 62 | 63 | ## Setup 64 | 65 | ### 1. Clone the Repository 66 | 67 | ```bash 68 | git clone https://github.com/ad1822/weekly-github-waybar-module.git 69 | cd weekly-github-waybar-module 70 | ```` 71 | 72 | --- 73 | 74 | ### 2. GitHub Authentication 75 | 76 | To make this module work, you need to provide: 77 | 78 | * Your **GitHub username** 79 | * A **Fine-Grained Personal Access Token (PAT)** 80 | 81 | * Scope: `Repository access → All repositories` 82 | * Minimum required permissions for reading contribution data 83 | 84 | ➡ **Generate your token here:** 85 | [https://github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new) 86 | 87 | --- 88 | 89 | ### 3. Create `.env` File 90 | 91 | Inside the project directory, create a `.env` file with the following content: 92 | 93 | ```env 94 | GITHUB_USERNAME=your_github_username 95 | GITHUB_PAT=ghp_yourGeneratedTokenHere 96 | ``` 97 | --- 98 | 99 | ### 4. Install Python Dependencies 100 | 101 | ```bash 102 | pip install -r requirements.txt 103 | ``` 104 | > Requires Python 3.7 or higher. 105 | > Dependencies: `requests`, `python-dotenv` 106 | 107 | --- 108 | 109 | ### 5. Waybar Integration 110 | 111 | Add the following block to your Waybar `config.jsonc`: 112 | 113 | ```jsonc 114 | "custom/gh_heatmap": { 115 | "exec": "sleep 1 & ~/.config/waybar/scripts/weekly_commits DARK/LIGHT", 116 | "return-type": "json", 117 | "interval": 2400, 118 | "tooltip": true, 119 | "on-click": "xdg-open https://github.com/", 120 | "on-click-right": "~/.config/waybar/scripts/weekly_commits DARK/LIGHT" 121 | } 122 | ``` 123 | - On `click` of that module, Your github profile open in browser 124 | - On `right-click` of that module, You can refresh module, So it can fetch latest commit data 125 | 126 | Then add styling in your `style.css`: 127 | 128 | ```css 129 | #custom-gh_heatmap { 130 | color: #39d353; 131 | background: rgba(30, 30, 46,0.89); // Put your own background color 132 | border-radius: 6px; 133 | margin-right: 2px; 134 | padding: 0px 8px; 135 | } 136 | ``` 137 | 138 | > Ensure the script is executable: 139 | > 140 | > ```bash 141 | > chmod +x ~/.config/waybar/scripts/weekly_commits 142 | > ``` 143 | 144 | 👉 Check out my custom Hyprland dotfiles: [**hyprdots**](https://github.com/ad1822/hyprdots) 145 | If you like it, consider giving it a ⭐ — it helps! -------------------------------------------------------------------------------- /weekly_commits: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import requests 4 | import datetime 5 | import json 6 | from collections import OrderedDict 7 | import os 8 | from dotenv import load_dotenv 9 | import sys 10 | import time 11 | 12 | load_dotenv() 13 | 14 | RETRY_DELAY = 2 15 | 16 | MAX_RETRIES = 5 17 | RETRY_DELAY = 3 # seconds 18 | 19 | output = {} 20 | success = False 21 | 22 | 23 | GITHUB_USERNAME = os.getenv("GITHUB_USERNAME") 24 | GITHUB_PAT = os.getenv("GITHUB_PAT") 25 | 26 | if not GITHUB_USERNAME or not GITHUB_PAT: 27 | raise ValueError("GITHUB_USERNAME or GITHUB_PAT not set in .env") 28 | 29 | 30 | today = datetime.date.today() 31 | start_of_week = today - datetime.timedelta(days=(today.weekday() + 1) % 7) 32 | from_date = start_of_week.isoformat() + "T00:00:00Z" 33 | to_date = today.isoformat() + "T23:59:59Z" 34 | 35 | query = """ 36 | query($user: String!, $from: DateTime!, $to: DateTime!) { 37 | user(login: $user) { 38 | contributionsCollection(from: $from, to: $to) { 39 | contributionCalendar { 40 | weeks { 41 | contributionDays { 42 | date 43 | contributionCount 44 | weekday 45 | } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | """ 52 | 53 | variables = { 54 | "user": GITHUB_USERNAME, 55 | "from": from_date, 56 | "to": to_date 57 | } 58 | 59 | headers = { 60 | "Authorization": f"Bearer {GITHUB_PAT}", 61 | "Accept": "application/vnd.github.v4.idl" 62 | } 63 | 64 | COLOR_SCHEME_DARK = { 65 | 0: "#202329", # No contributions 66 | 1: "#0e4429", # 1-3 commits 67 | 2: "#006d32", # 4-6 commits 68 | 3: "#26a641", # 7-9 commits 69 | 4: "#39d353" # 10+ commits 70 | } 71 | 72 | COLOR_SCHEME_LIGHT = { 73 | 0: "#ebedf0", # No contributions 74 | 1: "#9be9a8", # 1-3 commits 75 | 2: "#40c463", # 4-6 commits 76 | 3: "#30a14e", # 7-9 commits 77 | 4: "#216e39" # 10+ commits 78 | } 79 | 80 | 81 | theme = sys.argv[1].upper() if len(sys.argv) > 1 else "DARK" 82 | if theme not in ["DARK", "LIGHT"]: 83 | print(f"Unknown theme '{theme}', defaulting to DARK.") 84 | theme = "DARK" 85 | 86 | def get_color(count, theme): 87 | """Return heatmap color based on commit count and theme.""" 88 | scheme = COLOR_SCHEME_DARK if theme == "DARK" else COLOR_SCHEME_LIGHT 89 | 90 | if count == 0: 91 | return scheme[0] 92 | elif 1 <= count <= 3: 93 | return scheme[1] 94 | elif 4 <= count <= 6: 95 | return scheme[2] 96 | elif 7 <= count <= 9: 97 | return scheme[3] 98 | else: 99 | return scheme[4] 100 | 101 | def get_weekday_name(weekday): 102 | return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][(weekday + 1) % 7] 103 | 104 | 105 | for attempt in range(MAX_RETRIES): 106 | try: 107 | if attempt > 0: 108 | time.sleep(RETRY_DELAY) 109 | 110 | response = requests.post( 111 | "https://api.github.com/graphql", 112 | json={"query": query, "variables": variables}, 113 | headers=headers, 114 | timeout=10 115 | ) 116 | response.raise_for_status() 117 | data = response.json() 118 | 119 | weeks = data["data"]["user"]["contributionsCollection"]["contributionCalendar"]["weeks"] 120 | 121 | contributions = OrderedDict() 122 | for i in range(7): 123 | day = start_of_week + datetime.timedelta(days=i) 124 | contributions[day] = 0 125 | 126 | for week in weeks: 127 | for day in week["contributionDays"]: 128 | date = datetime.date.fromisoformat(day["date"]) 129 | if start_of_week <= date <= today: 130 | contributions[date] = day["contributionCount"] 131 | 132 | dots = [] 133 | tooltip_lines = [] 134 | total_contributions = 0 135 | 136 | for date, count in contributions.items(): 137 | color = get_color(count, theme) 138 | dots.append(f'') 139 | weekday = get_weekday_name(date.weekday()) 140 | tooltip_lines.append(f"{weekday} ({date.day}/{date.month}): {count} contribution{'s' if count != 1 else ''}") 141 | total_contributions += count 142 | 143 | output = { 144 | "text": " ".join(dots), 145 | "tooltip": "\n".join([ 146 | f"GitHub Contributions ({start_of_week.strftime('%d/%m')} to {today.strftime('%d/%m')})", 147 | f"Total: {total_contributions}" 148 | ] + tooltip_lines), 149 | "class": "github-contributions", 150 | "alt": "GitHub Contributions" 151 | } 152 | 153 | success = True 154 | break # exit retry loop on success 155 | 156 | except requests.exceptions.RequestException as e: 157 | if attempt == MAX_RETRIES - 1: 158 | output = { 159 | "text": "⚠", 160 | "tooltip": f"GitHub API error: {str(e)}", 161 | "class": "error", 162 | "alt": "GitHub Error" 163 | } 164 | 165 | except Exception as e: 166 | output = { 167 | "text": "⚠", 168 | "tooltip": f"Error: {str(e)}", 169 | "class": "error", 170 | "alt": "Error" 171 | } 172 | break # break if it's not a retryable error 173 | 174 | 175 | print(json.dumps(output)) --------------------------------------------------------------------------------