Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@ updates:
interval: "weekly"
cooldown:
default-days: 7

- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "daily"
cooldown:
default-days: 7

ignore:
- dependency-name: "python"
update-types:
- "version-update:semver-minor"
- "version-update:semver-major"

- package-ecosystem: "pip"
directory: "/"
schedule:
Expand Down
178 changes: 178 additions & 0 deletions .github/scripts/update_readme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import json
import os
import re
import subprocess
import urllib.request
from pathlib import Path

README = Path("README.md")

CONTRIBUTORS_START = "<!-- CONTRIBUTORS:START -->"
CONTRIBUTORS_END = "<!-- CONTRIBUTORS:END -->"

VERSION_START = "<!-- VERSION:START -->"
VERSION_END = "<!-- VERSION:END -->"

GITHUB_REPOSITORY = os.environ["GITHUB_REPOSITORY"]


def github_api(url: str):
request = urllib.request.Request(
url,
headers={
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2026-03-10",
"User-Agent": "README-metadata-updater",
},
)

with urllib.request.urlopen(request) as response:
return json.load(response)


def get_contributors():
"""Fetch all repository collaborators, handling pagination."""
contributors = []
page = 1

while True:
url = (
f"https://api.github.com/repos/"
f"{GITHUB_REPOSITORY}/contributors"
f"?per_page=100&page={page}"
)

users = github_api(url)

if not users:
break

contributors.extend(users)

if len(users) < 100:
break

page += 1

return contributors


def generate_contributors(contributors):
"""Generate the Markdown for the contributors."""
contributors.sort(key=lambda user: user["login"].lower())

entries = []

for user in contributors:
if user.get("type") == "Bot":
continue
login = user["login"].capitalize()
avatar_url = user["avatar_url"]
profile_url = user["html_url"]

entries.append(
f'<img src="{avatar_url}" '
f'alt="{login}" '
f'width="25"/> '
f"[{login}]({profile_url})"
)

return ", ".join(entries)


def get_latest_tag():
"""
Return the highest version tag according to Git's version sorting.

Examples:
v1.10.0 > v1.9.0
v2.0.0 > v1.99.0
"""

result = subprocess.run(
[
"git",
"tag",
"--sort=-v:refname",
],
capture_output=True,
text=True,
check=True,
)

tags = [tag.strip() for tag in result.stdout.splitlines() if tag.strip()]

if not tags:
raise RuntimeError("No Git tags found.")

return tags[0]


def replace_section(
content: str,
start_marker: str,
end_marker: str,
replacement: str,
):
"""Replace the content between two README markers."""

pattern = re.escape(start_marker) + r".*?" + re.escape(end_marker)

replacement_text = f"{start_marker} {replacement} {end_marker}"

updated, count = re.subn(
pattern,
replacement_text,
content,
count=1,
flags=re.DOTALL,
)

if count != 1:
raise RuntimeError(
f"Could not find README markers:\n" f"{start_marker}\n" f"...\n" f"{end_marker}"
)

return updated


def main():
if not README.exists():
raise FileNotFoundError("README.md not found.")

content = README.read_text(encoding="utf-8")

# Fetch contributors once.
contributors_data = get_contributors()
contributors = generate_contributors(contributors_data)

# Get the latest version tag.
latest_tag = get_latest_tag()

# Update contributors.
content = replace_section(
content,
CONTRIBUTORS_START,
CONTRIBUTORS_END,
contributors,
)

# Update version.
content = replace_section(
content,
VERSION_START,
VERSION_END,
latest_tag,
)

README.write_text(
content,
encoding="utf-8",
)

print(f"Found {len(contributors_data)} contributors.")
print(f"Latest tag: {latest_tag}")


if __name__ == "__main__":
main()
52 changes: 52 additions & 0 deletions .github/workflows/update-readme.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Update README metadata

on:
push:
tags:
- "*"

workflow_dispatch:

permissions:
contents: write

jobs:
update-readme:
runs-on: ubuntu-latest

steps:
- name: Checkout default branch
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.repository.default_branch }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Fetch all tags
run: git fetch --tags --force

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.x"

- name: Update README
env:
GITHUB_REPOSITORY: ${{ github.repository }}
run: python .github/scripts/update_readme.py

- name: Commit README changes
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

git add README.md

if git diff --cached --quiet; then
echo "README is already up to date."
exit 0
fi

git commit -m "docs: update README metadata"

git push origin HEAD:${{ github.event.repository.default_branch }}
32 changes: 17 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<h3 align="center">
<img src="https://avatars.githubusercontent.com/u/78621926?s=200&v=4" width="75"><br/>
<img alt="Image showing Graven development discord logo" src="https://avatars.githubusercontent.com/u/78621926?s=200&v=4" width="75"><br/>
DJ4H <br/>
This project is under the <a href="https://choosealicense.com/licenses/gpl-3.0/">GNU GPL v3</a> license<br/><br/>
</h3>
Expand All @@ -17,11 +17,11 @@ Most of the contributors are part of the staff but the members are also allowed

## Global information

| Global information | |
|--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Description | DJ4H is a Discord bot designed to enhance server engagement. It includes features for tracking activity engagement, providing a dynamic and interactive experience for communities. |
| Collaborators | <img src="https://avatars.githubusercontent.com/u/73261020?v=4" alt="drawing" width="25"/> [Gamingdy](https://github.com/Gamingdy), <img src="https://avatars.githubusercontent.com/u/34105327?s=64&v=4" alt="drawing" width="25"/> [Lindwen](https://github.com/Lindwen), <img src="https://avatars.githubusercontent.com/u/1571189?s=64&v=4" alt="drawing" width="25"/> [Loïc R](https://github.com/Lramelot), <img src="https://avatars.githubusercontent.com/u/69684024?s=64&v=4" alt="drawing" width="25"/> [GabHas](https://github.com/TheRealGabHas), <img src="https://avatars.githubusercontent.com/u/26577763?s=64&v=4" alt="drawing" width="25"/> [AntoineJT](https://github.com/AntoineJT) |
| Version | 1.0.0 |
| Global information | |
|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Description | DJ4H is a Discord bot designed to enhance server engagement. It includes features for tracking activity engagement, providing a dynamic and interactive experience for communities. |
| Contributors | <!-- CONTRIBUTORS:START --> <img src="https://avatars.githubusercontent.com/u/44125445?v=4" alt="Alessevan" width="25"/> [Alessevan](https://github.com/Alessevan), <img src="https://avatars.githubusercontent.com/u/185201144?v=4" alt="Alexandre-josepavel" width="25"/> [Alexandre-josepavel](https://github.com/alexandre-josepavel), <img src="https://avatars.githubusercontent.com/u/26577763?v=4" alt="Antoinejt" width="25"/> [Antoinejt](https://github.com/AntoineJT), <img src="https://avatars.githubusercontent.com/u/74816698?v=4" alt="Flenderrax" width="25"/> [Flenderrax](https://github.com/FlenderrAX), <img src="https://avatars.githubusercontent.com/u/73261020?v=4" alt="Gamingdy" width="25"/> [Gamingdy](https://github.com/gamingdy), <img src="https://avatars.githubusercontent.com/u/34105327?v=4" alt="Lindwen" width="25"/> [Lindwen](https://github.com/Lindwen), <img src="https://avatars.githubusercontent.com/u/1571189?v=4" alt="Lramelot" width="25"/> [Lramelot](https://github.com/Lramelot), <img src="https://avatars.githubusercontent.com/u/84503460?v=4" alt="Mityno" width="25"/> [Mityno](https://github.com/Mityno), <img src="https://avatars.githubusercontent.com/u/44524788?v=4" alt="Redstom" width="25"/> [Redstom](https://github.com/RedsTom), <img src="https://avatars.githubusercontent.com/u/69684024?v=4" alt="Therealgabhas" width="25"/> [Therealgabhas](https://github.com/TheRealGabHas) <!-- CONTRIBUTORS:END --> |
| Version | <!-- VERSION:START --> v1.5.5 <!-- VERSION:END --> |

---

Expand Down Expand Up @@ -59,18 +59,18 @@ Required command parameters are shown in brackets (`[required]`) while optional

### Configuration (Admin only)

| Command | Description |
| --- | --- |
| Command | Description |
|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| `/config [channel] [delay]` | Sets the game channel and the required delay to score a point. Time prefixes: `s` (seconds), `m` (minutes), `h` (hours), `d` (days). |
| `/set [member] [score]` | Sets a member's score to the specified value. |
| `/dump_log` | Retrieves the bot's log file. |
| `/set [member] [score]` | Sets a member's score to the specified value. |
| `/dump_log` | Retrieves the bot's log file. |

### Player

| Command | Description |
| --- | --- |
| Command | Description |
|----------------|--------------------------------------------|
| `/leaderboard` | Displays an image with the top 10 players. |
| `/score` | Shows your own score. |
| `/score` | Shows your own score. |

## Installation

Expand All @@ -97,7 +97,8 @@ Required command parameters are shown in brackets (`[required]`) while optional
cp .env.example .env
```

Then fill in `BOT_TOKEN`. Every variable is documented in `.env.example`; the commented-out ones are optional and show their default value.
Then fill in `BOT_TOKEN`. Every variable is documented in `.env.example`; the commented-out ones are optional and show
their default value.

## Usage

Expand All @@ -116,6 +117,7 @@ docker compose -f compose.prod.yaml up -d
```

Make a deployment

```bash
# clone repository
git checkout master
Expand Down Expand Up @@ -148,7 +150,7 @@ DJ4H/
├── config.py # Configuration and logging
├── commands/ # Discord commands
│ ├── cogs/game.py # /leaderboard and /score commands
�� └── handler/events.py # Main game logic
│ └── handler/events.py # Main game logic
├── utils/
│ ├── database/ # Database layer
│ │ ├── connection.py # SQLite connection
Expand Down
Loading
Loading