Compare commits
No commits in common. "64c46222008c35816e555af62709624f02ca5513" and "4ce85e09bf85fb13a2aa1e659c440c155f81f6c7" have entirely different histories.
64c4622200
...
4ce85e09bf
4 changed files with 41 additions and 182 deletions
|
@ -1,9 +0,0 @@
|
||||||
py_project(
|
|
||||||
name = "gh-unnotifier",
|
|
||||||
main = "src/python/ghunnotif/__main__.py",
|
|
||||||
main_deps = [
|
|
||||||
py_requirement("click"),
|
|
||||||
py_requirement("requests"),
|
|
||||||
py_requirement("pytimeparse"),
|
|
||||||
]
|
|
||||||
)
|
|
|
@ -1,125 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
import tomllib
|
|
||||||
from typing import Optional
|
|
||||||
from datetime import datetime, timedelta, timezone
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
import click
|
|
||||||
import requests
|
|
||||||
import pytimeparse
|
|
||||||
|
|
||||||
class Client(object):
|
|
||||||
BASE = "https://api.github.com"
|
|
||||||
HEADERS = {"Accept": "application/vnd.github+json"}
|
|
||||||
|
|
||||||
def __init__(self, token):
|
|
||||||
self._token = token
|
|
||||||
self._session = requests.Session()
|
|
||||||
self._session.headers["Authorization"] = f"Bearer {token}"
|
|
||||||
|
|
||||||
def get_notifications(self, page=1):
|
|
||||||
resp = self._session.get(f"{self.BASE}/notifications", headers=self.HEADERS, params={"page": page, "all": "false"})
|
|
||||||
resp.raise_for_status()
|
|
||||||
return resp.json()
|
|
||||||
|
|
||||||
def get_all_notifications(self):
|
|
||||||
page = 1
|
|
||||||
while True:
|
|
||||||
results = self.get_notifications(page=page)
|
|
||||||
if not results:
|
|
||||||
return
|
|
||||||
yield from results
|
|
||||||
page += 1
|
|
||||||
|
|
||||||
def read(self, notif):
|
|
||||||
return self._session.patch(notif["url"]).raise_for_status()
|
|
||||||
|
|
||||||
def unsubscribe(self, notif):
|
|
||||||
return self._session.delete(notif["subscription_url"]).raise_for_status()
|
|
||||||
|
|
||||||
def get_pr(self,
|
|
||||||
url: Optional[str] = None,
|
|
||||||
repo: Optional[str] = None,
|
|
||||||
id: Optional[int] = None):
|
|
||||||
url = url or f"{self.BASE}/{repo}/pulls/{id}"
|
|
||||||
resp = self._session.get(url, headers=self.HEADERS)
|
|
||||||
resp.raise_for_status()
|
|
||||||
return resp.json()
|
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
|
||||||
def cli():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
|
||||||
@click.option("--config", "config_path", type=Path, default=lambda: Path(os.getenv("BUILD_WORKSPACE_DIRECTORY", "")) / "projects/gh-unnotifier/config.toml")
|
|
||||||
def oneshot(config_path: Path):
|
|
||||||
with open(config_path, "rb") as fp:
|
|
||||||
config = tomllib.load(fp)
|
|
||||||
|
|
||||||
client = Client(config["gh-unnotifier"]["api_key"])
|
|
||||||
for notif in client.get_all_notifications():
|
|
||||||
subject = notif["subject"]
|
|
||||||
pr = None
|
|
||||||
if "/pulls/" in subject["url"]:
|
|
||||||
pr = client.get_pr(url=subject["url"])
|
|
||||||
if pr["state"] == "closed":
|
|
||||||
client.read(notif)
|
|
||||||
client.unsubscribe(notif)
|
|
||||||
|
|
||||||
print("Resolved", notif["id"])
|
|
||||||
continue
|
|
||||||
|
|
||||||
print(notif["id"], notif["subscription_url"], notif["reason"], notif["subject"], pr)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_seconds(text: str) -> timedelta:
|
|
||||||
return timedelta(seconds=pytimeparse.parse(text))
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
|
||||||
@click.option("--config", "config_path", type=Path, default=lambda: Path(os.getenv("BUILD_WORKSPACE_DIRECTORY", "")) / "projects/gh-unnotifier/config.toml")
|
|
||||||
@click.option("--schedule", "schedule", default="15 seconds", type=parse_seconds)
|
|
||||||
def maintain(config_path: Path, schedule: timedelta):
|
|
||||||
with open(config_path, "rb") as fp:
|
|
||||||
config = tomllib.load(fp)
|
|
||||||
|
|
||||||
client = Client(config["gh-unnotifier"]["api_key"])
|
|
||||||
mark = None
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
prev = mark
|
|
||||||
mark = datetime.now(timezone.utc)
|
|
||||||
tick = mark + schedule
|
|
||||||
assert tick - schedule == mark
|
|
||||||
for notif in client.get_all_notifications():
|
|
||||||
subject = notif["subject"]
|
|
||||||
|
|
||||||
# Don't waste time on notifications which haven't changed since the last scrub
|
|
||||||
updated_at = datetime.fromisoformat(notif["updated_at"])
|
|
||||||
if prev and updated_at < prev:
|
|
||||||
continue
|
|
||||||
|
|
||||||
pr = None
|
|
||||||
if "/pulls/" in subject["url"]:
|
|
||||||
pr = client.get_pr(url=subject["url"])
|
|
||||||
if pr["state"] == "closed":
|
|
||||||
client.read(notif)
|
|
||||||
client.unsubscribe(notif)
|
|
||||||
|
|
||||||
click.echo(f"Resolved {notif['id']}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
click.echo("Napping...")
|
|
||||||
sleep((tick - datetime.now(timezone.utc)).total_seconds())
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
cli()
|
|
|
@ -53,4 +53,3 @@ toml
|
||||||
unify
|
unify
|
||||||
yamllint
|
yamllint
|
||||||
yaspin
|
yaspin
|
||||||
pytimeparse
|
|
||||||
|
|
|
@ -1,26 +1,25 @@
|
||||||
aiohttp==3.8.5
|
aiohttp==3.8.4
|
||||||
aiohttp-basicauth==1.0.0
|
aiohttp-basicauth==1.0.0
|
||||||
aiosignal==1.3.1
|
aiosignal==1.3.1
|
||||||
aiosql==9.0
|
aiosql==8.0
|
||||||
alabaster==0.7.13
|
alabaster==0.7.13
|
||||||
annotated-types==0.5.0
|
async-lru==2.0.2
|
||||||
async-lru==2.0.4
|
|
||||||
async-timeout==4.0.2
|
async-timeout==4.0.2
|
||||||
attrs==23.1.0
|
attrs==23.1.0
|
||||||
autocommand==2.2.2
|
autocommand==2.2.2
|
||||||
autoflake==2.2.0
|
autoflake==2.1.1
|
||||||
Babel==2.12.1
|
Babel==2.12.1
|
||||||
beautifulsoup4==4.12.2
|
beautifulsoup4==4.12.2
|
||||||
black==23.7.0
|
black==23.3.0
|
||||||
blinker==1.6.2
|
blinker==1.6.2
|
||||||
build==0.10.0
|
build==0.10.0
|
||||||
cachetools==5.3.1
|
cachetools==5.3.1
|
||||||
certifi==2023.7.22
|
certifi==2023.5.7
|
||||||
charset-normalizer==3.2.0
|
charset-normalizer==3.1.0
|
||||||
cheroot==10.0.0
|
cheroot==10.0.0
|
||||||
CherryPy==18.8.0
|
CherryPy==18.8.0
|
||||||
click==8.1.6
|
click==8.1.3
|
||||||
colored==2.2.3
|
colored==1.4.4
|
||||||
commonmark==0.9.1
|
commonmark==0.9.1
|
||||||
coverage==7.2.7
|
coverage==7.2.7
|
||||||
decorator==5.1.1
|
decorator==5.1.1
|
||||||
|
@ -29,54 +28,53 @@ docutils==0.20.1
|
||||||
ExifRead==3.0.0
|
ExifRead==3.0.0
|
||||||
flake8==6.0.0
|
flake8==6.0.0
|
||||||
Flask==2.3.2
|
Flask==2.3.2
|
||||||
frozenlist==1.4.0
|
frozenlist==1.3.3
|
||||||
hypothesis==6.82.0
|
hypothesis==6.75.9
|
||||||
ibis==3.2.0
|
ibis==3.2.0
|
||||||
icmplib==3.0.3
|
icmplib==3.0.3
|
||||||
idna==3.4
|
idna==3.4
|
||||||
imagesize==1.4.1
|
imagesize==1.4.1
|
||||||
inflect==7.0.0
|
inflect==6.0.4
|
||||||
iniconfig==2.0.0
|
iniconfig==2.0.0
|
||||||
isort==5.12.0
|
isort==5.12.0
|
||||||
itsdangerous==2.1.2
|
itsdangerous==2.1.2
|
||||||
jaraco.collections==4.3.0
|
jaraco.collections==4.2.0
|
||||||
jaraco.context==4.3.0
|
jaraco.context==4.3.0
|
||||||
jaraco.functools==3.8.0
|
jaraco.functools==3.7.0
|
||||||
jaraco.text==3.11.1
|
jaraco.text @ git+https://github.com/arrdem/jaraco.text.git@0dd8d0b25a93c3fad896f3a92d11caff61ff273d
|
||||||
jedi==0.18.2
|
jedi==0.18.2
|
||||||
Jinja2==3.1.2
|
Jinja2==3.1.2
|
||||||
jsonschema==4.18.4
|
jsonschema==4.17.3
|
||||||
jsonschema-spec==0.2.3
|
jsonschema-spec==0.1.4
|
||||||
jsonschema-specifications==2023.7.1
|
lark==1.1.5
|
||||||
lark==1.1.7
|
|
||||||
lazy-object-proxy==1.9.0
|
lazy-object-proxy==1.9.0
|
||||||
libsass==0.22.0
|
libsass==0.22.0
|
||||||
livereload==2.6.3
|
livereload==2.6.3
|
||||||
lxml==4.9.3
|
lxml==4.9.2
|
||||||
Markdown==3.4.4
|
Markdown==3.4.3
|
||||||
MarkupSafe==2.1.3
|
MarkupSafe==2.1.3
|
||||||
mccabe==0.7.0
|
mccabe==0.7.0
|
||||||
meraki==1.34.0
|
meraki==1.33.0
|
||||||
mirakuru==2.5.1
|
mirakuru==2.5.1
|
||||||
mistune==2.0.5
|
mistune==2.0.5
|
||||||
more-itertools==10.0.0
|
more-itertools==9.1.0
|
||||||
multidict==6.0.4
|
multidict==6.0.4
|
||||||
mypy-extensions==1.0.0
|
mypy-extensions==1.0.0
|
||||||
octorest==0.4
|
octorest==0.4
|
||||||
openapi-schema-validator==0.6.0
|
openapi-schema-validator==0.4.4
|
||||||
openapi-spec-validator==0.6.0
|
openapi-spec-validator==0.5.6
|
||||||
packaging==23.1
|
packaging==23.1
|
||||||
parso==0.8.3
|
parso==0.8.3
|
||||||
pathable==0.4.3
|
pathable==0.4.3
|
||||||
pathspec==0.11.1
|
pathspec==0.11.1
|
||||||
picobox==3.0.0
|
picobox==3.0.0
|
||||||
pip==23.1.2
|
pip==23.1.2
|
||||||
pip-tools==7.1.0
|
pip-tools==6.13.0
|
||||||
platformdirs==3.9.1
|
platformdirs==3.5.1
|
||||||
pluggy==1.2.0
|
pluggy==1.0.0
|
||||||
port-for==0.7.1
|
port-for==0.6.3
|
||||||
portend==3.2.0
|
portend==3.1.0
|
||||||
prompt-toolkit==3.0.39
|
prompt-toolkit==3.0.38
|
||||||
proquint==0.2.1
|
proquint==0.2.1
|
||||||
psutil==5.9.5
|
psutil==5.9.5
|
||||||
psycopg==3.1.9
|
psycopg==3.1.9
|
||||||
|
@ -85,34 +83,30 @@ pudb==2022.1.3
|
||||||
py==1.11.0
|
py==1.11.0
|
||||||
pycodestyle==2.10.0
|
pycodestyle==2.10.0
|
||||||
pycryptodome==3.18.0
|
pycryptodome==3.18.0
|
||||||
pydantic==2.1.1
|
pydantic==1.10.8
|
||||||
pydantic_core==2.4.0
|
|
||||||
pyflakes==3.0.1
|
pyflakes==3.0.1
|
||||||
Pygments==2.15.1
|
Pygments==2.15.1
|
||||||
pyproject_hooks==1.0.0
|
pyproject_hooks==1.0.0
|
||||||
pyrsistent==0.19.3
|
pyrsistent==0.19.3
|
||||||
pytest==7.4.0
|
pytest==7.3.1
|
||||||
pytest-cov==4.1.0
|
pytest-cov==4.1.0
|
||||||
pytest-postgresql==5.0.0
|
pytest-postgresql==5.0.0
|
||||||
pytest-pudb==0.7.0
|
pytest-pudb==0.7.0
|
||||||
pytest-timeout==2.1.0
|
pytest-timeout==2.1.0
|
||||||
pytimeparse==1.1.8
|
|
||||||
pytz==2023.3
|
pytz==2023.3
|
||||||
PyYAML==6.0.1
|
PyYAML==6.0
|
||||||
recommonmark==0.7.1
|
recommonmark==0.7.1
|
||||||
redis==4.6.0
|
redis==4.5.5
|
||||||
referencing==0.29.3
|
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
retry==0.9.2
|
retry==0.9.2
|
||||||
rfc3339-validator==0.1.4
|
rfc3339-validator==0.1.4
|
||||||
rpds-py==0.9.2
|
setuptools==67.7.2
|
||||||
setuptools==68.0.0
|
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
smbus2==0.4.2
|
smbus2==0.4.2
|
||||||
snowballstemmer==2.2.0
|
snowballstemmer==2.2.0
|
||||||
sortedcontainers==2.4.0
|
sortedcontainers==2.4.0
|
||||||
soupsieve==2.4.1
|
soupsieve==2.4.1
|
||||||
Sphinx==7.1.1
|
Sphinx==7.0.1
|
||||||
sphinx_mdinclude==0.5.3
|
sphinx_mdinclude==0.5.3
|
||||||
sphinxcontrib-applehelp==1.0.4
|
sphinxcontrib-applehelp==1.0.4
|
||||||
sphinxcontrib-devhelp==1.0.2
|
sphinxcontrib-devhelp==1.0.2
|
||||||
|
@ -123,19 +117,19 @@ sphinxcontrib-openapi==0.8.1
|
||||||
sphinxcontrib-programoutput==0.17
|
sphinxcontrib-programoutput==0.17
|
||||||
sphinxcontrib-qthelp==1.0.3
|
sphinxcontrib-qthelp==1.0.3
|
||||||
sphinxcontrib-serializinghtml==1.1.5
|
sphinxcontrib-serializinghtml==1.1.5
|
||||||
tempora==5.5.0
|
tempora==5.2.2
|
||||||
termcolor==2.3.0
|
termcolor==2.3.0
|
||||||
toml==0.10.2
|
toml==0.10.2
|
||||||
tornado==6.3.2
|
tornado==6.3.2
|
||||||
typing_extensions==4.7.1
|
typing_extensions==4.6.3
|
||||||
unify==0.5
|
unify==0.5
|
||||||
untokenize==0.1.1
|
untokenize==0.1.1
|
||||||
urllib3==2.0.4
|
urllib3==2.0.2
|
||||||
urwid==2.1.2
|
urwid==2.1.2
|
||||||
urwid-readline==0.13
|
urwid-readline==0.13
|
||||||
wcwidth==0.2.6
|
wcwidth==0.2.6
|
||||||
websocket-client==1.6.1
|
websocket-client==1.5.2
|
||||||
Werkzeug==2.3.6
|
Werkzeug==2.3.4
|
||||||
wheel==0.40.0
|
wheel==0.40.0
|
||||||
yamllint==1.32.0
|
yamllint==1.32.0
|
||||||
yarl==1.9.2
|
yarl==1.9.2
|
||||||
|
|
Loading…
Reference in a new issue