Create the unnotifier
This commit is contained in:
parent
e9efc1dc84
commit
ad9816f55d
2 changed files with 134 additions and 0 deletions
9
projects/gh-unnotifier/BUILD
Normal file
9
projects/gh-unnotifier/BUILD
Normal file
|
@ -0,0 +1,9 @@
|
|||
py_project(
|
||||
name = "gh-unnotifier",
|
||||
main = "src/python/ghunnotif/__main__.py",
|
||||
main_deps = [
|
||||
py_requirement("click"),
|
||||
py_requirement("requests"),
|
||||
py_requirement("pytimeparse"),
|
||||
]
|
||||
)
|
125
projects/gh-unnotifier/src/python/ghunnotif/__main__.py
Normal file
125
projects/gh-unnotifier/src/python/ghunnotif/__main__.py
Normal file
|
@ -0,0 +1,125 @@
|
|||
#!/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()
|
Loading…
Reference in a new issue