Compare commits

..

No commits in common. "64c46222008c35816e555af62709624f02ca5513" and "4ce85e09bf85fb13a2aa1e659c440c155f81f6c7" have entirely different histories.

4 changed files with 41 additions and 182 deletions

View file

@ -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"),
]
)

View file

@ -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()

View file

@ -53,4 +53,3 @@ toml
unify
yamllint
yaspin
pytimeparse

View file

@ -1,26 +1,25 @@
aiohttp==3.8.5
aiohttp==3.8.4
aiohttp-basicauth==1.0.0
aiosignal==1.3.1
aiosql==9.0
aiosql==8.0
alabaster==0.7.13
annotated-types==0.5.0
async-lru==2.0.4
async-lru==2.0.2
async-timeout==4.0.2
attrs==23.1.0
autocommand==2.2.2
autoflake==2.2.0
autoflake==2.1.1
Babel==2.12.1
beautifulsoup4==4.12.2
black==23.7.0
black==23.3.0
blinker==1.6.2
build==0.10.0
cachetools==5.3.1
certifi==2023.7.22
charset-normalizer==3.2.0
certifi==2023.5.7
charset-normalizer==3.1.0
cheroot==10.0.0
CherryPy==18.8.0
click==8.1.6
colored==2.2.3
click==8.1.3
colored==1.4.4
commonmark==0.9.1
coverage==7.2.7
decorator==5.1.1
@ -29,54 +28,53 @@ docutils==0.20.1
ExifRead==3.0.0
flake8==6.0.0
Flask==2.3.2
frozenlist==1.4.0
hypothesis==6.82.0
frozenlist==1.3.3
hypothesis==6.75.9
ibis==3.2.0
icmplib==3.0.3
idna==3.4
imagesize==1.4.1
inflect==7.0.0
inflect==6.0.4
iniconfig==2.0.0
isort==5.12.0
itsdangerous==2.1.2
jaraco.collections==4.3.0
jaraco.collections==4.2.0
jaraco.context==4.3.0
jaraco.functools==3.8.0
jaraco.text==3.11.1
jaraco.functools==3.7.0
jaraco.text @ git+https://github.com/arrdem/jaraco.text.git@0dd8d0b25a93c3fad896f3a92d11caff61ff273d
jedi==0.18.2
Jinja2==3.1.2
jsonschema==4.18.4
jsonschema-spec==0.2.3
jsonschema-specifications==2023.7.1
lark==1.1.7
jsonschema==4.17.3
jsonschema-spec==0.1.4
lark==1.1.5
lazy-object-proxy==1.9.0
libsass==0.22.0
livereload==2.6.3
lxml==4.9.3
Markdown==3.4.4
lxml==4.9.2
Markdown==3.4.3
MarkupSafe==2.1.3
mccabe==0.7.0
meraki==1.34.0
meraki==1.33.0
mirakuru==2.5.1
mistune==2.0.5
more-itertools==10.0.0
more-itertools==9.1.0
multidict==6.0.4
mypy-extensions==1.0.0
octorest==0.4
openapi-schema-validator==0.6.0
openapi-spec-validator==0.6.0
openapi-schema-validator==0.4.4
openapi-spec-validator==0.5.6
packaging==23.1
parso==0.8.3
pathable==0.4.3
pathspec==0.11.1
picobox==3.0.0
pip==23.1.2
pip-tools==7.1.0
platformdirs==3.9.1
pluggy==1.2.0
port-for==0.7.1
portend==3.2.0
prompt-toolkit==3.0.39
pip-tools==6.13.0
platformdirs==3.5.1
pluggy==1.0.0
port-for==0.6.3
portend==3.1.0
prompt-toolkit==3.0.38
proquint==0.2.1
psutil==5.9.5
psycopg==3.1.9
@ -85,34 +83,30 @@ pudb==2022.1.3
py==1.11.0
pycodestyle==2.10.0
pycryptodome==3.18.0
pydantic==2.1.1
pydantic_core==2.4.0
pydantic==1.10.8
pyflakes==3.0.1
Pygments==2.15.1
pyproject_hooks==1.0.0
pyrsistent==0.19.3
pytest==7.4.0
pytest==7.3.1
pytest-cov==4.1.0
pytest-postgresql==5.0.0
pytest-pudb==0.7.0
pytest-timeout==2.1.0
pytimeparse==1.1.8
pytz==2023.3
PyYAML==6.0.1
PyYAML==6.0
recommonmark==0.7.1
redis==4.6.0
referencing==0.29.3
redis==4.5.5
requests==2.31.0
retry==0.9.2
rfc3339-validator==0.1.4
rpds-py==0.9.2
setuptools==68.0.0
setuptools==67.7.2
six==1.16.0
smbus2==0.4.2
snowballstemmer==2.2.0
sortedcontainers==2.4.0
soupsieve==2.4.1
Sphinx==7.1.1
Sphinx==7.0.1
sphinx_mdinclude==0.5.3
sphinxcontrib-applehelp==1.0.4
sphinxcontrib-devhelp==1.0.2
@ -123,19 +117,19 @@ sphinxcontrib-openapi==0.8.1
sphinxcontrib-programoutput==0.17
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
tempora==5.5.0
tempora==5.2.2
termcolor==2.3.0
toml==0.10.2
tornado==6.3.2
typing_extensions==4.7.1
typing_extensions==4.6.3
unify==0.5
untokenize==0.1.1
urllib3==2.0.4
urllib3==2.0.2
urwid==2.1.2
urwid-readline==0.13
wcwidth==0.2.6
websocket-client==1.6.1
Werkzeug==2.3.6
websocket-client==1.5.2
Werkzeug==2.3.4
wheel==0.40.0
yamllint==1.32.0
yarl==1.9.2