Compare commits

...

32 Commits

Author SHA1 Message Date
Félix MARQUET
de60020b01 Merge pull request #13 from BreizhHardware/dev
fix(dependabot): ensure updates target the dev branch
2025-06-10 11:47:55 +02:00
Félix MARQUET
1430d39b5c fix(dependabot): ensure updates target the dev branch 2025-06-10 11:46:40 +02:00
Félix MARQUET
47fa8f820e Merge pull request #10 from BreizhHardware/dev
Create dependabot.yaml
2025-06-10 11:31:06 +02:00
Félix MARQUET
56439d8c62 Update dependabot.yaml 2025-06-10 11:30:56 +02:00
Félix MARQUET
013c5bd70d Create dependabot.yaml 2025-06-10 11:29:08 +02:00
Félix MARQUET
c81fc26881 fix(dockerfile): add send_slack.py to the dockerfile 2025-05-05 09:08:18 +00:00
Félix MARQUET
e7b89930f1 Merge pull request #7 from Mahmoud0x00/slack-notification-add
feat: add Slack notification
2025-05-05 08:36:45 +02:00
Mahmoud Osama
246b727d0a Update notification messages format
Update notification messages format to use Emojis with:
* Discord
* Gotify
* Ntfy
2025-05-04 19:59:39 +03:00
Mahmoud Osama
83cfd9a2f1 Update README.md with SLACK_WEBHOOK_URL 2025-05-04 14:13:43 +03:00
Mahmoud Osama
8795add7f0 Add SLACK_WEBHOOK_URL to Dockerfile 2025-05-03 20:52:08 +03:00
Mahmoud Osama
3d33cb8282 Add Slack notifcation
 Support Slack incoming webhook notifications with good looking block message design
2025-05-03 20:17:20 +03:00
Félix MARQUET
aa2f654d4b Merge pull request #6 from BreizhHardware/dev
feat: Refactor web UI
2024-12-23 20:17:52 +01:00
e1b16ac645 feat: Refactor web UI
- Refactor of the webUI
2024-12-23 20:12:11 +01:00
Félix MARQUET
7a221a9ab9 Merge pull request #5 from BreizhHardware/dev
feat: merge dev into main

    Created the connection with Discord.
    Added CONTRIBUTION.md.
    Reworked GitHub CI.
    Fixed threading issues.
2024-12-23 20:00:10 +01:00
a3e892c8f0 fix: use separate database connections for each function
- Refactored `send_discord.py` to better work with webhook (but remove the embed)
- Updated Dockerfile to adjust package installation order.
2024-12-23 19:56:57 +01:00
e8eb8d18d2 fix: use separate database connections for each function
- Refactored `send_gotify.py`, `send_ntfy.py`, and `send_discord.py` to use separate database connections for each function.
- Added `get_db_connection` function to create a new database connection.
- Updated `github_send_to_gotify`, `docker_send_to_gotify`, `github_send_to_ntfy`, `docker_send_to_ntfy`, `github_send_to_discord`, and `docker_send_to_discord` functions to use the new `get_db_connection` function.
- Modified `ntfy.py` to use threading for calling notification functions simultaneously.
2024-12-23 19:33:49 +01:00
Félix MARQUET
66759932f0 chore(ci): remove GitHub release build to use Docker only
Edit CI configuration to remove GitHub release build. Now, builds are only for Docker, and I will use the Conventional Release Bot app to manage releases.
2024-12-23 15:47:49 +00:00
Félix MARQUET
dc831c958f docs: update README.md with dependancies installation
- Add instruction for installing the dependencies
2024-12-23 12:41:57 +00:00
Félix MARQUET
edff2e3806 docs: add CONTRIBUTION.md and update README.md with contribution guidelines
- Added CONTRIBUTION.md to provide guidelines for contributing to the project.
- Updated README.md to include a link to CONTRIBUTION.md for easy access to contribution guidelines.
2024-12-23 12:29:18 +00:00
Félix MARQUET
91cc7bc9bf First itération for discord 2024-12-23 12:20:13 +00:00
fc577ea17f Fix error 2024-11-21 17:20:57 +01:00
ae95654ec3 Fix error 2024-11-21 17:19:52 +01:00
Félix MARQUET
7c0e34c08c Merge pull request #4 from BreizhHardware/dev
Fix DOCKERFILE error
2024-10-25 08:39:33 +02:00
Félix MARQUET
921f40e98e Fix DOCKERFILE error 2024-10-25 06:39:03 +00:00
Félix MARQUET
dcf9edba97 Merge pull request #3 from BreizhHardware/dev
New Readme.md
2024-10-24 15:54:55 +02:00
Félix MARQUET
5f2e86d86a New Readme.md 2024-10-24 13:54:27 +00:00
Félix MARQUET
66e22f6788 Merge pull request #2 from BreizhHardware/dev
Add Gotify support and arm64 and armv7 support
2024-10-24 15:32:08 +02:00
Félix MARQUET
71cf7baa32 Add arm64 and armv7 compatibility 2024-10-24 13:26:18 +00:00
Félix MARQUET
8d26c2821c Fix typo and add error message if neither ntfy_url or gotify_url is set 2024-10-24 13:10:59 +00:00
Félix MARQUET
3e59106fa6 Start of the gotify implementation 2024-10-24 13:10:10 +00:00
Félix MARQUET
d6c0e4e08e Update README.md 2024-10-23 13:03:14 +02:00
Félix MARQUET
4bfc6e254a Update README.md 2024-10-23 12:59:41 +02:00
17 changed files with 1029 additions and 331 deletions

12
.github/dependabot.yaml vendored Normal file
View File

@@ -0,0 +1,12 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "pip" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
target-branch: "dev"

View File

@@ -1,73 +1,30 @@
name: Docker Build and Release
on:
push:
branches:
- main
jobs:
build-and-push-on-docker-hub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/github-ntfy:latest
release-on-github:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Get the latest tag
id: get_latest_tag
run: echo "latest_tag=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_ENV
- name: Increment version
id: increment_version
run: |
latest_tag=${{ env.latest_tag }}
if [ -z "$latest_tag" ]; then
new_version="v1.4.3"
else
IFS='.' read -r -a version_parts <<< "${latest_tag#v}"
new_version="v${version_parts[0]}.$((version_parts[1] + 1)).0"
fi
echo "new_version=$new_version" >> $GITHUB_ENV
- name: Read changelog
id: read_changelog
run: echo "changelog=$(base64 -w 0 CHANGELOG.md)" >> $GITHUB_ENV
- name: Decode changelog
id: decode_changelog
run: echo "${{ env.changelog }}" | base64 -d > decoded_changelog.txt
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }}
with:
tag_name: ${{ env.new_version }}
release_name: Release ${{ env.new_version }}
body: ${{ steps.decode_changelog.outputs.changelog }}
draft: false
prerelease: false
name: Docker Build and Release
on:
push:
branches:
- main
jobs:
build-and-push-on-docker-hub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/github-ntfy:latest

View File

@@ -0,0 +1,73 @@
name: Docker Build and Release
on:
push:
branches:
- main
jobs:
build-and-push-on-docker-hub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/github-ntfy:latest
release-on-github:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Get the latest tag
id: get_latest_tag
run: echo "latest_tag=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_ENV
- name: Increment version
id: increment_version
run: |
latest_tag=${{ env.latest_tag }}
if [ -z "$latest_tag" ]; then
new_version="v1.5.2"
else
IFS='.' read -r -a version_parts <<< "${latest_tag#v}"
new_version="v${version_parts[0]}.$((version_parts[1] + 1)).0"
fi
echo "new_version=$new_version" >> $GITHUB_ENV
- name: Read changelog
id: read_changelog
run: echo "changelog=$(base64 -w 0 CHANGELOG.md)" >> $GITHUB_ENV
- name: Decode changelog
id: decode_changelog
run: echo "${{ env.changelog }}" | base64 -d > decoded_changelog.txt
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }}
with:
tag_name: ${{ env.new_version }}
release_name: Release ${{ env.new_version }}
body: ${{ steps.decode_changelog.outputs.changelog }}
draft: false
prerelease: false

View File

@@ -0,0 +1,38 @@
name: Docker Build and Release for arm64
on:
push:
branches:
- main
jobs:
build-and-push-on-docker-hub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
install: true
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/arm64
tags: ${{ secrets.DOCKER_USERNAME }}/github-ntfy:arm64

View File

@@ -0,0 +1,38 @@
name: Docker Build and Release for armv7
on:
push:
branches:
- main
jobs:
build-and-push-on-docker-hub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm/v7
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
install: true
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/arm/v7
tags: ${{ secrets.DOCKER_USERNAME }}/github-ntfy:armv7

View File

@@ -1,9 +1,6 @@
**New features**:
- Change database struct to be able to handle docker hub repos
- Now you can add docker hub repo to your watch list
- You can connect your docker account to bypass the rate limit of the docker hub API
- Add gotify compatibility please Read the README.md
- Add arm64 support
- Add armv7 support
**Breaking changes**:
- Change in the docker-compose file, you need to add the DOCKER_USERNAME and DOCKER_PASSWORD environment variables
**Full Changelog**: https://github.com/BreizhHardware/ntfy_alerts/compare/v1.3...v1.4.3
**Full Changelog**: https://github.com/BreizhHardware/ntfy_alerts/compare/v1.4.3...v1.5

54
CONTRIBUTION.md Normal file
View File

@@ -0,0 +1,54 @@
# Contribution Guidelines
Thank you for considering contributing to this project! Your help is greatly appreciated. Please follow these guidelines to ensure a smooth contribution process.
## How to Contribute
1. **Fork the repository**: Click the "Fork" button at the top right of this repository to create a copy of the repository in your GitHub account.
2. **Clone your fork**: Clone your forked repository to your local machine.
```sh
git clone https://github.com/BreizhHardware/ntfy_alerts.git
cd ntfy_alerts
```
3. **Create a new branch**: Create a new branch for your feature or bugfix.
```sh
git checkout -b feat/my-feature-branch
```
4. **Make your changes**: Make your changes to the codebase. Ensure your code follows the project's coding standards and includes appropriate tests.
5. **Commit your changes**: Commit your changes with a clear and concise commit message using conventional commit.
```sh
git add .
git commit -m "feat: add feature X"
```
6. **Push to your fork**: Push your changes to your forked repository.
```sh
git push origin feat/my-feature-branch
```
7. **Create a Pull Request**: Go to the original repository and create a pull request from your forked repository. Provide a clear description of your changes and the problem they solve.
## Code Style
- Follow the existing code style and conventions.
- Write clear and concise comments where necessary.
- Ensure your code is well-documented.
## Testing
- Write tests for any new features or bug fixes.
- Ensure all tests pass before submitting your pull request.
## Reporting Issues
If you find a bug or have a feature request, please create an issue on the GitHub repository. Provide as much detail as possible to help us understand and address the issue.
## Code of Conduct
Please note that this project is released with a Contributor Code of Conduct. By participating in this project, you agree to abide by its terms.
Thank you for contributing!

View File

@@ -7,9 +7,13 @@ ADD ntfy.py /
ADD ntfy_api.py /
ADD requirements.txt /
ADD entrypoint.sh /
ADD send_ntfy.py /
ADD send_gotify.py /
ADD send_discord.py /
ADD send_slack.py /
ADD index.html /var/www/html/index.html
ADD script.js /var/www/html/script.js
RUN apk add --no-cache sqlite-dev sqlite-libs gcc musl-dev nginx
RUN apk add --no-cache sqlite-dev sqlite-libs musl-dev nginx gcc
RUN pip install -r requirements.txt
RUN chmod 700 /entrypoint.sh
@@ -21,6 +25,10 @@ ENV USERNAME="" \
GHNTFY_TOKEN="" \
DOCKER_USERNAME="" \
DOCKER_PASSWORD="" \
GOTIFY_URL="" \
GOTIFY_TOKEN="" \
DISCORD_WEBHOOK_URL="" \
SLACK_WEBHOOK_URL="" \
FLASK_ENV=production
# Exposer le port 5000 pour l'API et le port 80 pour le serveur web

139
README.md
View File

@@ -1,25 +1,26 @@
# ntfy_alerts
Personal ntfy alerts system
<h1 align="center">Welcome to ntfy_alerts 👋</h1>
<p>
<img alt="Version" src="https://img.shields.io/badge/version-1.5-blue.svg?cacheSeconds=2592000" />
<a href="#" target="_blank">
<img alt="License: GPL--3" src="https://img.shields.io/badge/License-GPL--3-yellow.svg" />
</a>
<a href="https://twitter.com/BreizhHardware" target="_blank">
<img alt="Twitter: BreizhHardware" src="https://img.shields.io/twitter/follow/BreizhHardware.svg?style=social" />
</a>
</p>
Feel free to contribute and to fork !
> This project allows you to receive notifications about new GitHub or Docker Hub releases on ntfy, gotify, and Discord.
# Python ntfy.py
## Description:
This script is used to watch the github repos and send a notification to the ntfy server when a new release is published.
## Installation
It can aloso watch Docker Hub repos and do the same as github.
## Utilisation:
auth and ntfy_url are required to be set as environment variables.
To install the dependencies, run:
```sh
pip install -r requirements.txt
```
auth: can be generataed by the folowing command: echo -n 'username:password' | base64
## Usage
ntfy_url: the url of the ntfy server including the topic
````python
python ntfy.py
````
## Docker:
If you want to use the docker image you can use the following docker-compose file:
If you want to use the Docker image, you can use the following docker-compose file for x86_64:
````yaml
services:
github-ntfy:
@@ -28,20 +29,84 @@ services:
environment:
- USERNAME=username # Required
- PASSWORD=password # Required
- NTFY_URL=ntfy_url # Required
- NTFY_URL=ntfy_url # Required if ntfy is used
- GHNTFY_TIMEOUT=timeout # Default is 3600 (1 hour)
- GHNTFY_TOKEN= # Default is empty (Github token)
- DOCKER_USERNAME= # Default is empty (Docker Hub username)
- DOCKER_PASSWORD= # Default is empty (Docker Hub password)
- GOTIFY_URL=gotify_url # Required if gotify is used
- GOTIFY_TOKEN= # Required if gotify is used
- DISCORD_WEBHOOK_URL= # Required if discord is used
- SLACK_WEBHOOK_URL= # Required if Slack is used
volumes:
- /path/to/github-ntfy:/github-ntfy/
ports:
- 80:80
restart: unless-stopped
````
GHNTFY_TOKEN, need to have repo, read:org and read:user
For arm64 this docker compose file is ok:
````yaml
services:
github-ntfy:
image: breizhhardware/github-ntfy:arm64
container_name: github-ntfy
environment:
- USERNAME=username # Required
- PASSWORD=password # Required
- NTFY_URL=ntfy_url # Required if ntfy is used
- GHNTFY_TIMEOUT=timeout # Default is 3600 (1 hour)
- GHNTFY_TOKEN= # Default is empty (Github token)
- DOCKER_USERNAME= # Default is empty (Docker Hub username)
- DOCKER_PASSWORD= # Default is empty (Docker Hub password)
- GOTIFY_URL=gotify_url # Required if gotify is used
- GOTIFY_TOKEN= # Required if gotify is used
- DISCORD_WEBHOOK_URL= # Required if discord is used
- SLACK_WEBHOOK_URL= # Required if Slack is used
volumes:
- /path/to/github-ntfy:/github-ntfy/
ports:
- 80:80
restart: unless-stopped
````
For armV7 this docker compose is ok:
````yaml
services:
github-ntfy:
image: breizhhardware/github-ntfy:armv7
container_name: github-ntfy
environment:
- USERNAME=username # Required
- PASSWORD=password # Required
- NTFY_URL=ntfy_url # Required if ntfy is used
- GHNTFY_TIMEOUT=timeout # Default is 3600 (1 hour)
- GHNTFY_TOKEN= # Default is empty (Github token)
- DOCKER_USERNAME= # Default is empty (Docker Hub username)
- DOCKER_PASSWORD= # Default is empty (Docker Hub password)
- GOTIFY_URL=gotify_url # Required if gotify is used
- GOTIFY_TOKEN= # Required if gotify is used
- DISCORD_WEBHOOK_URL= # Required if discord is used
- SLACK_WEBHOOK_URL= # Required if Slack is used
volumes:
- /path/to/github-ntfy:/github-ntfy/
ports:
- 80:80
restart: unless-stopped
````
GHNTFY_TOKEN is a github token, it need to have repo, read:org and read:user
## Author
👤 **BreizhHardware**
* Website: https://mrqt.fr?ref=github
* Twitter: [@BreizhHardware](https://twitter.com/BreizhHardware)
* Github: [@BreizhHardware](https://github.com/BreizhHardware)
* LinkedIn: [@félix-marquet-5071bb167](https://linkedin.com/in/félix-marquet-5071bb167)
## Contribution
If you want to contribut, feel free to open a pull request, but first read the [contribution guide](CONTRIBUTION.md)!
Docker Hub repo: https://hub.docker.com/r/breizhhardware/github-ntfy
## TODO:
- [x] Dockerize the ntfy.py
- [x] Add the watched repos list as a parameter
@@ -49,34 +114,10 @@ Docker Hub repo: https://hub.docker.com/r/breizhhardware/github-ntfy
- [x] Add the watched repos list as a web interface
- [x] Add Docker Hub compatibility
- [ ] Rework of the web interface
# Bash setup-notify.sh
## Description:
This script is used to setup the ntfy notification system on ssh login for a new server.
## Utilisation:
````bash
bash setup-notify.sh <ntfy_url> <username> <password> <topic>
````
ntfy_url: the url of the ntfy server
- [x] Compatibility with Gotify
- [x] Compatibility with Discord Webhook
- [x] Compatibility and distribution for arm64 and armv7
username: the username of the user
password: the password of the user
topic: the topic of the notification
This script will create a send-notify.sh in the root of your disk and add the login-notify.sh to the /etc/profile.d/ folder.
# Bash send-notify.sh
## Description:
This script is used to send a notification to the ntfy server.
## Utilisation:
````bash
bash send-notify.sh <ntfy_url> <basic_auth> <topic> <message>
````
ntfy_url: the url of the ntfy server
basic_auth: the basic auth of the user
topic: the topic of the notification
message: the message of the notification
## Show your support
Give a ⭐️ if this project helped you!

View File

@@ -3,74 +3,67 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Github-Ntfy Add a repo</title>
<title>Github-Ntfy Add a Repo</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="./script.js" defer></script>
</head>
<body class="bg-gradient-to-b from-stone-500 to-green-700">
<h1 class="text-4xl font-semibold leading-10 text-gray-900 text-center">Github-Ntfy</h1>
<div class="flex flex-row gap-2 justify-center items-center my-2 h-screen">
<div class="flex flex-col gap-2 justify-center items-center my-2 h-screen border-double border-2 border-white p-2" id="github">
<h1>Add a github repo</h1>
<form id="addRepoForm">
<div class="space-y-12">
<div class="border-b border-gray-900/10 pb-12">
<h2 class="text-base font-semibold leading-7 text-gray-900">Name of the github repo</h2>
<div class="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
<div class="sm:col-span-4">
<div class="mt-2">
<div class="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600 sm:max-w-md">
<span class="flex select-none items-center pl-3 sm:text-sm">github.com/</span>
<input type="text" name="repo" id="repo" autocomplete="repo" class="block flex-1 border-0 bg-transparent py-1.5 pl-1 placeholder:text-gray-600 focus:ring-0 sm:text-sm sm:leading-6" placeholder="BreizhHardware/ntfy_alerts">
</div>
</div>
</div>
</div>
</div>
<body class="bg-[#1b2124] text-gray-200">
<header class="text-center py-8 bg-[#23453d] shadow-lg">
<h1 class="text-5xl font-bold tracking-wide text-white">Github-Ntfy</h1>
</header>
<main class="flex flex-wrap justify-center gap-8 py-12">
<!-- Github Repo Section -->
<section class="bg-[#23453d] rounded-lg shadow-lg p-6 w-full max-w-lg">
<h2 class="text-2xl font-semibold mb-4">Add a Github Repo</h2>
<form id="addRepoForm" class="space-y-6">
<div>
<label for="repo" class="block text-sm font-medium">Name of the Github Repo</label>
<div class="mt-2 flex items-center border rounded-md bg-gray-700">
<span class="px-3 text-gray-400">github.com/</span>
<input type="text" name="repo" id="repo" autocomplete="repo" class="flex-1 py-2 px-3 bg-transparent focus:outline-none" placeholder="BreizhHardware/ntfy_alerts">
</div>
<div class="mt-6 flex items-center justify-end gap-x-6">
<button type="button" class="text-sm font-semibold leading-6 text-gray-900">Cancel</button>
<button type="submit" class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Save</button>
</div>
</form>
<div class="mt-8">
<h2 class="text-base font-semibold leading-7 text-gray-900">Watched Github Repositories</h2>
<ul id="watchedReposList" class="mt-4">
<!-- Dynamically populated with JavaScript -->
</ul>
</div>
</div>
<div class="flex flex-col gap-2 justify-center items-center my-2 h-screen border-double border-2 border-white p-2" id="docker">
<h1>Add a docker repo</h1>
<form id="addDockerRepoForm">
<div class="space-y-12">
<div class="border-b border-gray-900/10 pb-12">
<h2 class="text-base font-semibold leading-7 text-gray-900">Name of the docker repo</h2>
<div class="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
<div class="sm:col-span-4">
<div class="mt-2">
<div class="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600 sm:max-w-md">
<span class="flex select-none items-center pl-3 sm:text-sm">hub.docker.com/r/</span>
<input type="text" name="dockerRepo" id="dockerRepo" autocomplete="dockerRepo" class="block flex-1 border-0 bg-transparent py-1.5 pl-1 placeholder:text-gray-600 focus:ring-0 sm:text-sm sm:leading-6" placeholder="breizhhardware/github-ntfy">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="mt-6 flex items-center justify-end gap-x-6">
<button type="button" class="text-sm font-semibold leading-6 text-gray-900">Cancel</button>
<button type="submit" class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Save</button>
</div>
</form>
<div class="mt-8">
<h2 class="text-base font-semibold leading-7 text-gray-900">Watched Docker Repositories</h2>
<ul id="watchedDockerReposList" class="mt-4">
<!-- Dynamically populated with JavaScript -->
</ul>
<div class="flex justify-end gap-4">
<button type="button" class="px-4 py-2 text-gray-400 hover:text-white">Cancel</button>
<button type="submit" class="px-4 py-2 bg-green-700 hover:bg-green-600 text-white font-semibold rounded-md">Save</button>
</div>
</form>
<div class="mt-8">
<h3 class="text-lg font-semibold mb-2">Watched Github Repositories</h3>
<ul id="watchedReposList" class="space-y-2">
<!-- Dynamically populated with JavaScript -->
</ul>
</div>
</div>
<p class="font-semibold leading-10 text-gray-900 text-center">I know this web interface is awfull but I'm not a web designer ^^.</p>
</section>
<!-- Docker Repo Section -->
<section class="bg-[#23453d] rounded-lg shadow-lg p-6 w-full max-w-lg">
<h2 class="text-2xl font-semibold mb-4">Add a Docker Repo</h2>
<form id="addDockerRepoForm" class="space-y-6">
<div>
<label for="dockerRepo" class="block text-sm font-medium">Name of the Docker Repo</label>
<div class="mt-2 flex items-center border rounded-md bg-gray-700">
<span class="px-3 text-gray-400">hub.docker.com/r/</span>
<input type="text" name="dockerRepo" id="dockerRepo" autocomplete="dockerRepo" class="flex-1 py-2 px-3 bg-transparent focus:outline-none" placeholder="breizhhardware/github-ntfy">
</div>
</div>
<div class="flex justify-end gap-4">
<button type="button" class="px-4 py-2 text-gray-400 hover:text-white">Cancel</button>
<button type="submit" class="px-4 py-2 bg-green-700 hover:bg-green-600 text-white font-semibold rounded-md">Save</button>
</div>
</form>
<div class="mt-8">
<h3 class="text-lg font-semibold mb-2">Watched Docker Repositories</h3>
<ul id="watchedDockerReposList" class="space-y-2">
<!-- Dynamically populated with JavaScript -->
</ul>
</div>
</section>
</main>
<footer class="text-center py-6 bg-[#23453d]">
<p class="text-sm">I know this web interface is simple, but I'm improving!</p>
</footer>
</body>
</html>
</html>

237
ntfy.py
View File

@@ -5,22 +5,46 @@ import logging
import sqlite3
import subprocess
import json
import threading
from send_ntfy import (
github_send_to_ntfy,
docker_send_to_ntfy,
)
from send_gotify import (
github_send_to_gotify,
docker_send_to_gotify,
)
from send_discord import (
github_send_to_discord,
docker_send_to_discord,
)
from send_slack import (
github_send_to_slack,
docker_send_to_slack,
)
# Configuring the logger
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
github_token = os.environ.get('GHNTFY_TOKEN')
github_token = os.environ.get("GHNTFY_TOKEN")
github_headers = {}
if github_token:
github_headers['Authorization'] = f"token {github_token}"
github_headers["Authorization"] = f"token {github_token}"
docker_username = os.environ.get('DOCKER_USERNAME')
docker_password = os.environ.get('DOCKER_PASSWORD')
docker_username = os.environ.get("DOCKER_USERNAME")
docker_password = os.environ.get("DOCKER_PASSWORD")
discord_webhook_url = os.environ.get("DISCORD_WEBHOOK_URL")
def create_dockerhub_token(username, password):
url = "https://hub.docker.com//v2/users/login"
url = "https://hub.docker.com/v2/users/login"
headers = {"Content-Type": "application/json"}
data = json.dumps({"username": username, "password": password})
@@ -40,31 +64,42 @@ def create_dockerhub_token(username, password):
docker_token = create_dockerhub_token(docker_username, docker_password)
docker_header = {}
if docker_token:
docker_header['Authorization'] = f"Bearer {docker_token}"
docker_header["Authorization"] = f"Bearer {docker_token}"
# Connecting to the database to store previous versions
conn = sqlite3.connect('/github-ntfy/ghntfy_versions.db', check_same_thread=False)
conn = sqlite3.connect(
"/github-ntfy/ghntfy_versions.db",
check_same_thread=False,
)
cursor = conn.cursor()
# Creating the table if it does not exist
cursor.execute('''CREATE TABLE IF NOT EXISTS versions
(repo TEXT PRIMARY KEY, version TEXT, changelog TEXT)''')
cursor.execute(
"""CREATE TABLE IF NOT EXISTS versions
(repo TEXT PRIMARY KEY, version TEXT, changelog TEXT)"""
)
conn.commit()
cursor.execute('''CREATE TABLE IF NOT EXISTS docker_versions
(repo TEXT PRIMARY KEY, digest TEXT, changelog TEXT)''')
cursor.execute(
"""CREATE TABLE IF NOT EXISTS docker_versions
(repo TEXT PRIMARY KEY, digest TEXT)"""
)
conn.commit()
logger.info("Starting version monitoring...")
conn2 = sqlite3.connect('/github-ntfy/watched_repos.db', check_same_thread=False)
conn2 = sqlite3.connect("/github-ntfy/watched_repos.db", check_same_thread=False)
cursor2 = conn2.cursor()
cursor2.execute('''CREATE TABLE IF NOT EXISTS watched_repos
(id INTEGER PRIMARY KEY, repo TEXT)''')
cursor2.execute(
"""CREATE TABLE IF NOT EXISTS watched_repos
(id INTEGER PRIMARY KEY, repo TEXT)"""
)
conn2.commit()
cursor2.execute('''CREATE TABLE IF NOT EXISTS docker_watched_repos
(id INTEGER PRIMARY KEY, repo TEXT)''')
cursor2.execute(
"""CREATE TABLE IF NOT EXISTS docker_watched_repos
(id INTEGER PRIMARY KEY, repo TEXT)"""
)
conn2.commit()
@@ -98,36 +133,41 @@ def get_latest_releases(watched_repos):
if response.status_code == 200:
release_info = response.json()
changelog = get_changelog(repo)
release_date = release_info.get('published_at', 'Release date not available')
releases.append({
"repo": repo,
"name": release_info["name"],
"tag_name": release_info["tag_name"],
"html_url": release_info["html_url"],
"changelog": changelog,
"published_at": release_date
})
release_date = release_info.get("published_at", "Release date not available")
releases.append(
{
"repo": repo,
"name": release_info["name"],
"tag_name": release_info["tag_name"],
"html_url": release_info["html_url"],
"changelog": changelog,
"published_at": release_date,
}
)
else:
logger.error(f"Failed to fetch release info for {repo}")
return releases
def get_latest_docker_releases(watched_repos):
releases = []
for repo in watched_repos:
url = f"https://hub.docker.com/v2/repositories/{repo_name}/tags/latest"
url = f"https://hub.docker.com/v2/repositories/{repo}/tags/latest"
response = requests.get(url, headers=docker_header)
if response.status_code == 200:
release_info = response.json()
release_date=release_info["last_upated"]
release_date = release_info["last_upated"]
digest = release_date["digest"]
releases.append({
"repo": repo,
"digest": digest,
"html_url": "https://hub.docker.com/r/" + repo,
"published_at": release_date
})
releases.append(
{
"repo": repo,
"digest": digest,
"html_url": "https://hub.docker.com/r/" + repo,
"published_at": release_date,
}
)
else:
logger.error(f"Failed to fetch Docker Hub info for {repo_name}")
logger.error(f"Failed to fetch Docker Hub info for {repo}")
return releases
@@ -138,103 +178,78 @@ def get_changelog(repo):
releases = response.json()
if releases:
latest_release_list = releases[0]
if 'body' in latest_release_list:
return latest_release_list['body']
if "body" in latest_release_list:
return latest_release_list["body"]
return "Changelog not available"
def notify_all_services(github_latest_release, docker_latest_release, auth, ntfy_url, gotify_url, gotify_token, discord_webhook_url, slack_webhook_url):
threads = []
def github_send_to_ntfy(releases, auth, url):
for release in releases:
app_name = release['repo'].split('/')[-1] # Getting the application name from the repo
version_number = release['tag_name'] # Getting the version number
app_url = release['html_url'] # Getting the application URL
changelog = release['changelog'] # Getting the changelog
release_date = release['published_at'] # Getting the release date
release_date = release_date.replace("T", " ").replace("Z", "") # Formatting the release date
if ntfy_url:
if github_latest_release:
threads.append(threading.Thread(target=github_send_to_ntfy, args=(github_latest_release, auth, ntfy_url)))
if docker_latest_release:
threads.append(threading.Thread(target=docker_send_to_ntfy, args=(docker_latest_release, auth, ntfy_url)))
# Checking if the version has changed since the last time
cursor.execute("SELECT version FROM versions WHERE repo=?", (app_name,))
previous_version = cursor.fetchone()
if previous_version and previous_version[0] == version_number:
logger.info(f"The version of {app_name} has not changed. No notification sent.")
continue # Move on to the next application
if gotify_url and gotify_token:
if github_latest_release:
threads.append(threading.Thread(target=github_send_to_gotify, args=(github_latest_release, gotify_token, gotify_url)))
if docker_latest_release:
threads.append(threading.Thread(target=docker_send_to_gotify, args=(docker_latest_release, gotify_token, gotify_url)))
message = f"New version: {version_number}\nFor: {app_name}\nPublished on: {release_date}\nChangelog:\n{changelog}\n{app_url}"
# Updating the previous version for this application
cursor.execute("INSERT OR REPLACE INTO versions (repo, version, changelog) VALUES (?, ?, ?)",
(app_name, version_number, changelog))
conn.commit()
if discord_webhook_url:
if github_latest_release:
threads.append(threading.Thread(target=github_send_to_discord, args=(github_latest_release, discord_webhook_url)))
if docker_latest_release:
threads.append(threading.Thread(target=docker_send_to_discord, args=(docker_latest_release, discord_webhook_url)))
headers = {
"Authorization": f"Basic {auth}",
"Title": f"New version for {app_name}",
"Priority": "urgent",
"Markdown": "yes",
"Actions": f"view, Update {app_name}, {app_url}, clear=true"}
response = requests.post(f"{url}", headers=headers, data=message)
if response.status_code == 200:
logger.info(f"Message sent to Ntfy for {app_name}")
continue
else:
logger.error(f"Failed to send message to Ntfy. Status code: {response.status_code}")
if slack_webhook_url:
if github_latest_release:
threads.append(threading.Thread(target=github_send_to_slack, args=(github_latest_release, slack_webhook_url)))
if docker_latest_release:
threads.append(threading.Thread(target=docker_send_to_slack, args=(docker_latest_release, slack_webhook_url)))
for thread in threads:
thread.start()
def docker_send_to_ntfy(releases, auth, url):
for release in releases:
app_name = release['repo'].split('/')[-1] # Getting the application name from the repo
digest_number = release['digest']
app_url = release['html_url'] # Getting the application URL
release_date = release['published_at'] # Getting the release date
release_date = release_date.replace("T", " ").replace("Z", "") # Formatting the release date
for thread in threads:
thread.join()
# Checking if the version has changed since the last time
cursor.execute("SELECT digest FROM docker_versions WHERE repo=?", (app_name,))
previous_digest = cursor.fetchone()
if previous_digest and previous_digest[0] == digest_number:
logger.info(f"The digest of {app_name} has not changed. No notification sent.")
continue # Move on to the next application
message = f"New version: {digest_number}\nFor: {app_name}\nPublished on: {release_date}\nChangelog:\n{changelog}\n{app_url}"
# Updating the previous digest for this application
cursor.execute("INSERT OR REPLACE INTO docker_versions (repo, digest, changelog) VALUES (?, ?, ?)",
(app_name, digest_number, changelog))
conn.commit()
headers = {
"Authorization": f"Basic {auth}",
"Title": f"New version for {app_name}",
"Priority": "urgent",
"Markdown": "yes",
"Actions": f"view, Update {app_name}, {app_url}, clear=true"}
response = requests.post(f"{url}", headers=headers, data=message)
if response.status_code == 200:
logger.info(f"Message sent to Ntfy for {app_name}")
continue
else:
logger.error(f"Failed to send message to Ntfy. Status code: {response.status_code}")
if __name__ == "__main__":
start_api()
with open('/auth.txt', 'r') as f:
with open("/auth.txt", "r") as f:
auth = f.read().strip()
ntfy_url = os.environ.get('NTFY_URL')
timeout = float(os.environ.get('GHNTFY_TIMEOUT'))
ntfy_url = os.environ.get("NTFY_URL")
gotify_url = os.environ.get("GOTIFY_URL")
gotify_token = os.environ.get("GOTIFY_TOKEN")
discord_webhook_url = os.environ.get("DISCORD_WEBHOOK_URL")
timeout = float(os.environ.get("GHNTFY_TIMEOUT"))
slack_webhook_url = os.environ.get("SLACK_WEBHOOK_URL")
if auth and ntfy_url:
if auth and (ntfy_url or gotify_url or discord_webhook_url):
while True:
github_watched_repos_list = get_watched_repos()
github_latest_release = get_latest_releases(github_watched_repos_list)
if github_latest_release:
github_send_to_ntfy(github_latest_release, auth, ntfy_url)
docker_watched_repos_list = get_docker_watched_repos()
docker_latest_release = get_latest_docker_releases(docker_watched_repos_list)
if docker_latest_release:
docker_send_to_ntfy(docker_latest_release, auth, ntfy_url)
time.sleep(timeout) # Wait an hour before checking again
notify_all_services(github_latest_release, docker_latest_release, auth, ntfy_url, gotify_url, gotify_token, discord_webhook_url, slack_webhook_url)
time.sleep(timeout)
else:
logger.error("Usage: python ntfy.py")
logger.error(
"auth: can be generataed by the folowing command: echo -n 'username:password' | base64 and need to be "
"stored in a file named auth.txt")
"stored in a file named auth.txt"
)
logger.error("NTFY_URL: the url of the ntfy server need to be stored in an environment variable named NTFY_URL")
logger.error(
"GOTIFY_URL: the url of the gotify server need to be stored in an environment variable named GOTIFY_URL"
)
logger.error(
"GOTIFY_TOKEN: the token of the gotify server need to be stored in an environment variable named GOTIFY_TOKEN"
)
logger.error("DISCORD_WEBHOOK_URL: the webhook URL for Discord notifications need to be stored in an environment variable named DISCORD_WEBHOOK_URL")
logger.error("GHNTFY_TIMEOUT: the time interval between each check")

View File

@@ -6,8 +6,9 @@ app = Flask(__name__)
CORS(app)
app.logger.setLevel("WARNING")
def get_db_connection():
conn = sqlite3.connect('/github-ntfy/watched_repos.db')
conn = sqlite3.connect("/github-ntfy/watched_repos.db")
conn.row_factory = sqlite3.Row
return conn
@@ -16,14 +17,17 @@ def close_db_connection(conn):
conn.close()
@app.route('/app_repo', methods=['POST'])
@app.route("/app_repo", methods=["POST"])
def app_repo():
data = request.json
repo = data.get('repo')
repo = data.get("repo")
# Vérifier si le champ 'repo' est présent dans les données JSON
if not repo:
return jsonify({"error": "The repo field is required."}), 400
return (
jsonify({"error": "The repo field is required."}),
400,
)
# Établir une connexion à la base de données
conn = get_db_connection()
@@ -31,13 +35,22 @@ def app_repo():
try:
# Vérifier si le dépôt existe déjà dans la base de données
cursor.execute("SELECT * FROM watched_repos WHERE repo=?", (repo,))
cursor.execute(
"SELECT * FROM watched_repos WHERE repo=?",
(repo,),
)
existing_repo = cursor.fetchone()
if existing_repo:
return jsonify({"error": f"The GitHub repo {repo} is already in the database."}), 409
return (
jsonify({"error": f"The GitHub repo {repo} is already in the database."}),
409,
)
# Ajouter le dépôt à la base de données
cursor.execute("INSERT INTO watched_repos (repo) VALUES (?)", (repo,))
cursor.execute(
"INSERT INTO watched_repos (repo) VALUES (?)",
(repo,),
)
conn.commit()
return jsonify({"message": f"The GitHub repo {repo} as been added to the watched repos."})
finally:
@@ -45,14 +58,17 @@ def app_repo():
close_db_connection(conn)
@app.route('/app_docker_repo', methods=['POST'])
@app.route("/app_docker_repo", methods=["POST"])
def app_docker_repo():
data = request.json
repo = data.get('repo')
repo = data.get("repo")
# Vérifier si le champ 'repo' est présent dans les données JSON
if not repo:
return jsonify({"error": "The repo field is required."}), 400
return (
jsonify({"error": "The repo field is required."}),
400,
)
# Établir une connexion à la base de données
conn = get_db_connection()
@@ -60,13 +76,22 @@ def app_docker_repo():
try:
# Vérifier si le dépôt existe déjà dans la base de données
cursor.execute("SELECT * FROM docker_watched_repos WHERE repo=?", (repo,))
cursor.execute(
"SELECT * FROM docker_watched_repos WHERE repo=?",
(repo,),
)
existing_repo = cursor.fetchone()
if existing_repo:
return jsonify({"error": f"The Docker repo {repo} is already in the database."}), 409
return (
jsonify({"error": f"The Docker repo {repo} is already in the database."}),
409,
)
# Ajouter le dépôt à la base de données
cursor.execute("INSERT INTO docker_watched_repos (repo) VALUES (?)", (repo,))
cursor.execute(
"INSERT INTO docker_watched_repos (repo) VALUES (?)",
(repo,),
)
conn.commit()
return jsonify({"message": f"The Docker repo {repo} as been added to the watched repos."})
finally:
@@ -74,7 +99,7 @@ def app_docker_repo():
close_db_connection(conn)
@app.route('/watched_repos', methods=['GET'])
@app.route("/watched_repos", methods=["GET"])
def get_watched_repos():
db = get_db_connection()
cursor = db.cursor()
@@ -85,7 +110,7 @@ def get_watched_repos():
return jsonify(watched_repos)
@app.route('/watched_docker_repos', methods=['GET'])
@app.route("/watched_docker_repos", methods=["GET"])
def get_watched_docker_repos():
db = get_db_connection()
cursor = db.cursor()
@@ -96,14 +121,17 @@ def get_watched_docker_repos():
return jsonify(watched_repos)
@app.route('/delete_repo', methods=['POST'])
@app.route("/delete_repo", methods=["POST"])
def delete_repo():
data = request.json
repo = data.get('repo')
repo = data.get("repo")
# Vérifier si le champ 'repo' est présent dans les données JSON
if not repo:
return jsonify({"error": "The repo field is required."}), 400
return (
jsonify({"error": "The repo field is required."}),
400,
)
# Établir une connexion à la base de données
conn = get_db_connection()
@@ -111,13 +139,22 @@ def delete_repo():
try:
# Vérifier si le dépôt existe dans la base de données
cursor.execute("SELECT * FROM watched_repos WHERE repo=?", (repo,))
cursor.execute(
"SELECT * FROM watched_repos WHERE repo=?",
(repo,),
)
existing_repo = cursor.fetchone()
if not existing_repo:
return jsonify({"error": f"The GitHub repo {repo} is not in the database."}), 404
return (
jsonify({"error": f"The GitHub repo {repo} is not in the database."}),
404,
)
# Supprimer le dépôt de la base de données
cursor.execute("DELETE FROM watched_repos WHERE repo=?", (repo,))
cursor.execute(
"DELETE FROM watched_repos WHERE repo=?",
(repo,),
)
conn.commit()
return jsonify({"message": f"The GitHub repo {repo} as been deleted from the watched repos."})
finally:
@@ -125,14 +162,17 @@ def delete_repo():
close_db_connection(conn)
@app.route('/delete_docker_repo', methods=['POST'])
@app.route("/delete_docker_repo", methods=["POST"])
def delete_docker_repo():
data = request.json
repo = data.get('repo')
repo = data.get("repo")
# Vérifier si le champ 'repo' est présent dans les données JSON
if not repo:
return jsonify({"error": "The repo field is required."}), 400
return (
jsonify({"error": "The repo field is required."}),
400,
)
# Établir une connexion à la base de données
conn = get_db_connection()
@@ -140,13 +180,22 @@ def delete_docker_repo():
try:
# Vérifier si le dépôt existe dans la base de données
cursor.execute("SELECT * FROM docker_watched_repos WHERE repo=?", (repo,))
cursor.execute(
"SELECT * FROM docker_watched_repos WHERE repo=?",
(repo,),
)
existing_repo = cursor.fetchone()
if not existing_repo:
return jsonify({"error": f"The Docker repo {repo} is not in the database."}), 404
return (
jsonify({"error": f"The Docker repo {repo} is not in the database."}),
404,
)
# Supprimer le dépôt de la base de données
cursor.execute("DELETE FROM docker_watched_repos WHERE repo=?", (repo,))
cursor.execute(
"DELETE FROM docker_watched_repos WHERE repo=?",
(repo,),
)
conn.commit()
return jsonify({"message": f"The Docker repo {repo} as been deleted from the watched repos."})
finally:

2
pyproject.toml Normal file
View File

@@ -0,0 +1,2 @@
[tool.black]
line-length = 120

94
send_discord.py Normal file
View File

@@ -0,0 +1,94 @@
import requests
import sqlite3
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
def get_db_connection():
return sqlite3.connect("/github-ntfy/ghntfy_versions.db", check_same_thread=False)
def github_send_to_discord(releases, webhook_url):
conn = get_db_connection()
cursor = conn.cursor()
for release in releases:
app_name = release["repo"].split("/")[-1]
version_number = release["tag_name"]
app_url = release["html_url"]
changelog = release["changelog"]
release_date = release["published_at"].replace("T", " ").replace("Z", "")
cursor.execute("SELECT version FROM versions WHERE repo=?", (app_name,))
previous_version = cursor.fetchone()
if previous_version and previous_version[0] == version_number:
logger.info(f"The version of {app_name} has not changed. No notification sent.")
continue # Move on to the next application
message = f"📌 *New version*: {version_number}\n\n📦*For*: {app_name}\n\n📅 *Published on*: {release_date}\n\n📝 *Changelog*:\n\n```{changelog}```"
if len(message) > 2000:
message = f"📌 *New version*: {version_number}\n\n📦*For*: {app_name}\n\n📅 *Published on*: {release_date}\n\n🔗 *Release Link*: {app_url}"
# Updating the previous version for this application
cursor.execute(
"INSERT OR REPLACE INTO versions (repo, version, changelog) VALUES (?, ?, ?)",
(app_name, version_number, changelog),
)
conn.commit()
data = {
"content": message,
"username": "GitHub Ntfy"
}
headers = {
"Content-Type": "application/json"
}
response = requests.post(webhook_url, json=data, headers=headers)
if 200 <= response.status_code < 300:
logger.info(f"Message sent to Discord for {app_name}")
else:
logger.error(f"Failed to send message to Discord. Status code: {response.status_code}")
logger.error(f"Response: {response.text}")
conn.close()
def docker_send_to_discord(releases, webhook_url):
conn = get_db_connection()
cursor = conn.cursor()
for release in releases:
app_name = release["repo"].split("/")[-1]
digest_number = release["digest"]
app_url = release["html_url"]
release_date = release["published_at"].replace("T", " ").replace("Z", "")
cursor.execute("SELECT digest FROM docker_versions WHERE repo=?", (app_name,))
previous_digest = cursor.fetchone()
if previous_digest and previous_digest[0] == digest_number:
logger.info(f"The digest of {app_name} has not changed. No notification sent.")
continue
message = f"🐳 *Docker Image Updated!*\n\n🔐 *New Digest*: `{digest_number}`\n\n📦 *App*: {app_name}\n\n📢*Published*: {release_date}\n\n🔗 *Link*: {app_url}"
cursor.execute(
"INSERT OR REPLACE INTO docker_versions (repo, digest) VALUES (?, ?)",
(app_name, digest_number),
)
conn.commit()
data = {
"content": message,
"username": "GitHub Ntfy"
}
headers = {
"Content-Type": "application/json"
}
logger.info(f"Sending payload to Discord: {data}")
response = requests.post(webhook_url, json=data, headers=headers)
if 200 <= response.status_code < 300:
logger.info(f"Message sent to Discord for {app_name}")
else:
logger.error(f"Failed to send message to Discord. Status code: {response.status_code}")
logger.error(f"Response: {response.text}")
conn.close()

98
send_gotify.py Normal file
View File

@@ -0,0 +1,98 @@
import requests
import sqlite3
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
def get_db_connection():
return sqlite3.connect("/github-ntfy/ghntfy_versions.db", check_same_thread=False)
def github_send_to_gotify(releases, token, url):
conn = get_db_connection()
cursor = conn.cursor()
url = url + "/message"
url = url + "?token=" + token
for release in releases:
app_name = release["repo"].split("/")[-1] # Getting the application name from the repo
version_number = release["tag_name"] # Getting the version number
app_url = release["html_url"] # Getting the application URL
changelog = release["changelog"] # Getting the changelog
release_date = release["published_at"] # Getting the release date
release_date = release_date.replace("T", " ").replace("Z", "") # Formatting the release date
# Checking if the version has changed since the last time
cursor.execute(
"SELECT version FROM versions WHERE repo=?",
(app_name,),
)
previous_version = cursor.fetchone()
if previous_version and previous_version[0] == version_number:
logger.info(f"The version of {app_name} has not changed. No notification sent.")
continue # Move on to the next application
message = f"📌 *New version*: {version_number}\n\n📦*For*: {app_name}\n\n📅 *Published on*: {release_date}\n\n📝 *Changelog*:\n\n```{changelog}```\n\n🔗 *Release Url*:{app_url}"
# Updating the previous version for this application
cursor.execute(
"INSERT OR REPLACE INTO versions (repo, version, changelog) VALUES (?, ?, ?)",
(app_name, version_number, changelog),
)
conn.commit()
content = {
"title": f"New version for {app_name}",
"message": message,
"priority": "2",
}
response = requests.post(url, json=content)
if response.status_code == 200:
logger.info(f"Message sent to Gotify for {app_name}")
continue
else:
logger.error(f"Failed to send message to Gotify. Status code: {response.status_code}")
def docker_send_to_gotify(releases, token, url):
conn = get_db_connection()
cursor = conn.cursor()
url = url + "/message"
url = url + "?token=" + token
for release in releases:
app_name = release["repo"].split("/")[-1] # Getting the application name from the repo
digest_number = release["digest"]
app_url = release["html_url"] # Getting the application URL
release_date = release["published_at"] # Getting the release date
release_date = release_date.replace("T", " ").replace("Z", "") # Formatting the release date
# Checking if the version has changed since the last time
cursor.execute(
"SELECT digest FROM docker_versions WHERE repo=?",
(app_name,),
)
previous_digest = cursor.fetchone()
if previous_digest and previous_digest[0] == digest_number:
logger.info(f"The digest of {app_name} has not changed. No notification sent.")
continue # Move on to the next application
message = f"🐳 *Docker Image Updated!*\n\n🔐 *New Digest*: `{digest_number}`\n\n📦 *App*: {app_name}\n\n📢 *Published*: {release_date}\n\n🔗 *Release Url*:{app_url}"
# Updating the previous digest for this application
cursor.execute(
"INSERT OR REPLACE INTO docker_versions (repo, digest) VALUES (?, ?, ?)",
(app_name, digest_number),
)
conn.commit()
content = {
"title": f"New version for {app_name}",
"message": message,
"priority": "2",
}
response = requests.post(url, json=content)
if response.status_code == 200:
logger.info(f"Message sent to Gotify for {app_name}")
continue
else:
logger.error(f"Failed to send message to Gotify. Status code: {response.status_code}")

98
send_ntfy.py Normal file
View File

@@ -0,0 +1,98 @@
import requests
import sqlite3
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
def get_db_connection():
return sqlite3.connect("/github-ntfy/ghntfy_versions.db", check_same_thread=False)
def github_send_to_ntfy(releases, auth, url):
conn = get_db_connection()
cursor = conn.cursor()
for release in releases:
app_name = release["repo"].split("/")[-1] # Getting the application name from the repo
version_number = release["tag_name"] # Getting the version number
app_url = release["html_url"] # Getting the application URL
changelog = release["changelog"] # Getting the changelog
release_date = release["published_at"] # Getting the release date
release_date = release_date.replace("T", " ").replace("Z", "") # Formatting the release date
# Checking if the version has changed since the last time
cursor.execute(
"SELECT version FROM versions WHERE repo=?",
(app_name,),
)
previous_version = cursor.fetchone()
if previous_version and previous_version[0] == version_number:
logger.info(f"The version of {app_name} has not changed. No notification sent.")
continue # Move on to the next application
message = f"📌 *New version*: {version_number}\n\n📦*For*: {app_name}\n\n📅 *Published on*: {release_date}\n\n📝 *Changelog*:\n\n```{changelog}```\n\n 🔗 *Release Url*: {app_url}"
# Updating the previous version for this application
cursor.execute(
"INSERT OR REPLACE INTO versions (repo, version, changelog) VALUES (?, ?, ?)",
(app_name, version_number, changelog),
)
conn.commit()
headers = {
"Authorization": f"Basic {auth}",
"Title": f"New version for {app_name}",
"Priority": "urgent",
"Markdown": "yes",
"Actions": f"view, Update {app_name}, {app_url}, clear=true",
}
response = requests.post(f"{url}", headers=headers, data=message)
if response.status_code == 200:
logger.info(f"Message sent to Ntfy for {app_name}")
continue
else:
logger.error(f"Failed to send message to Ntfy. Status code: {response.status_code}")
def docker_send_to_ntfy(releases, auth, url):
conn = get_db_connection()
cursor = conn.cursor()
for release in releases:
app_name = release["repo"].split("/")[-1] # Getting the application name from the repo
digest_number = release["digest"]
app_url = release["html_url"] # Getting the application URL
release_date = release["published_at"] # Getting the release date
release_date = release_date.replace("T", " ").replace("Z", "") # Formatting the release date
# Checking if the version has changed since the last time
cursor.execute(
"SELECT digest FROM docker_versions WHERE repo=?",
(app_name,),
)
previous_digest = cursor.fetchone()
if previous_digest and previous_digest[0] == digest_number:
logger.info(f"The digest of {app_name} has not changed. No notification sent.")
continue # Move on to the next application
message = f"🐳 *Docker Image Updated!*\n\n🔐 *New Digest*: `{digest_number}`\n\n📦 *App*: {app_name}\n\n📢*Published*: {release_date}\n\n 🔗 *Release Url*: {app_url}"
# Updating the previous digest for this application
cursor.execute(
"INSERT OR REPLACE INTO docker_versions (repo, digest) VALUES (?, ?, ?)",
(app_name, digest_number),
)
conn.commit()
headers = {
"Authorization": f"Basic {auth}",
"Title": f"🆕 New version for {app_name}",
"Priority": "urgent",
"Markdown": "yes",
"Actions": f"View, Update {app_name}, {app_url}, clear=true",
}
response = requests.post(f"{url}", headers=headers, data=message)
if response.status_code == 200:
logger.info(f"Message sent to Ntfy for {app_name}")
continue
else:
logger.error(f"Failed to send message to Ntfy. Status code: {response.status_code}")

131
send_slack.py Normal file
View File

@@ -0,0 +1,131 @@
import requests
import sqlite3
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
def get_db_connection():
return sqlite3.connect("/github-ntfy/ghntfy_versions.db", check_same_thread=False)
def github_send_to_slack(releases, webhook_url):
conn = get_db_connection()
cursor = conn.cursor()
for release in releases:
app_name = release["repo"].split("/")[-1]
version_number = release["tag_name"]
app_url = release["html_url"]
changelog = release["changelog"]
release_date = release["published_at"].replace("T", " ").replace("Z", "")
cursor.execute("SELECT version FROM versions WHERE repo=?", (app_name,))
previous_version = cursor.fetchone()
if previous_version and previous_version[0] == version_number:
logger.info(f"The version of {app_name} has not changed. No notification sent.")
continue
message = f"📌 *New version*: {version_number}\n\n📦*For*: {app_name}\n\n📅 *Published on*: {release_date}\n\n📝 *Changelog*:\n\n```{changelog}```"
if len(message) > 2000:
message = f"📌 *New version*: {version_number}\n\n📦*For*: {app_name}\n\n📅 *Published on*: {release_date}\n\n📝 *Changelog*:\n\n `truncated..` use 🔗 instead "
cursor.execute(
"INSERT OR REPLACE INTO versions (repo, version, changelog) VALUES (?, ?, ?)",
(app_name, version_number, changelog),
)
conn.commit()
message = {
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"{message}"
},
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"text": "🔗 Release Url"
},
"url": f"{app_url}",
"action_id": "button-action"
}
},
{
"type": "divider"
}
]
}
headers = {
"Content-Type": "application/json"
}
response = requests.post(webhook_url, json=message, headers=headers)
if response.status_code == 200:
logger.info(f"Message sent to Slack for {app_name}")
else:
logger.error(f"Failed to send message to Slack. Status code: {response.status_code}")
logger.error(f"Response: {response.text}")
conn.close()
def docker_send_to_slack(releases, webhook_url):
conn = get_db_connection()
cursor = conn.cursor()
for release in releases:
app_name = release["repo"].split("/")[-1]
digest_number = release["digest"]
app_url = release["html_url"]
release_date = release["published_at"].replace("T", " ").replace("Z", "")
cursor.execute("SELECT digest FROM docker_versions WHERE repo=?", (app_name,))
previous_digest = cursor.fetchone()
if previous_digest and previous_digest[0] == digest_number:
logger.info(f"The digest of {app_name} has not changed. No notification sent.")
continue
message = f"🐳 *Docker Image Updated!*\n\n🔐 *New Digest*: `{digest_number}`\n\n📦 *App*: {app_name}\n\n📢*Published*: {release_date}"
cursor.execute(
"INSERT OR REPLACE INTO docker_versions (repo, digest) VALUES (?, ?)",
(app_name, digest_number),
)
conn.commit()
message = {
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"{message}"
},
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"text": "🔗 Release Url"
},
"url": f"{app_url}",
"action_id": "button-action"
}
},
{
"type": "divider"
}
]
}
headers = {
"Content-Type": "application/json"
}
response = requests.post(webhook_url, json=message, headers=headers)
if 200 <= response.status_code < 300:
logger.info(f"Message sent to Slack for {app_name}")
else:
logger.error(f"Failed to send message to Slack. Status code: {response.status_code}")
logger.error(f"Response: {response.text}")
conn.close()