解决github幽灵通知问题
起因
今天打开 github 发现右上角有通知,打开列表发现该通知被归到一个名为ycombiiinator/-co
的项目,点开却没有通知内容,这就有点诡异了。
在社区反馈中找到这样一个 issue : Bug:ghost notifications
看来有很多人有同样的问题,令人意外的是,这个帖子已经存在好几年,官方仍未修复。
解决
通过请求 github 的 API /notifications/threads/{thread_id}
将该通知设为 done
即可。
API说明 (请求必须使用 tokens (classic)
,不支持 Fine-grained tokens
)
获取Token (需要勾选 notifications 权限)
自动化脚本
import urllib.request
import json
# ⚠️ Replace this with your GitHub personal access token (must include notifications scope)
GITHUB_TOKEN = "your_personal_access_token_here"
BASE_URL = "https://api.github.com"
HEADERS = {
"Authorization": f"Bearer {GITHUB_TOKEN}",
"Accept": "application/vnd.github+json",
"User-Agent": "Python-urllib" # GitHub requires a User-Agent header
}
def api_request(url, method="GET"):
"""Make an HTTP request to the GitHub API and return (status, body)."""
req = urllib.request.Request(url, method=method, headers=HEADERS)
try:
with urllib.request.urlopen(req) as resp:
status = resp.getcode()
body = resp.read().decode("utf-8")
return status, body
except urllib.error.HTTPError as e:
return e.code, e.read().decode("utf-8")
except urllib.error.URLError as e:
return None, str(e)
def list_notifications():
"""Fetch notifications from GitHub."""
url = f"{BASE_URL}/notifications"
status, body = api_request(url)
if status != 200:
print(f"Error fetching notifications: {status}")
print(body)
return []
return json.loads(body)
def mark_thread_done(thread_id):
"""Mark a notification thread as Done (archive it)."""
url = f"{BASE_URL}/notifications/threads/{thread_id}"
status, body = api_request(url, method="DELETE")
if status == 204:
print(f"✅ Thread {thread_id} marked as Done")
else:
print(f"❌ Failed to mark thread {thread_id} as Done: {status}")
print(body)
def main():
notifications = list_notifications()
if not notifications:
print("No notifications found or failed to fetch notifications.")
return
print("\n📬 Current notifications:")
for idx, n in enumerate(notifications, start=1):
repo = n["repository"]["full_name"]
title = n["subject"]["title"]
notif_type = n["subject"]["type"]
thread_id = n["id"]
unread = n.get("unread", False)
print(f"{idx}. [{repo}] ({notif_type}) {title} → thread_id={thread_id} unread={unread}")
while True:
inp = input("\nEnter notification numbers to mark as Done (q to quit): ").strip()
if inp.lower() == 'q':
break
# Support both spaces and commas as separators
parts = [p for p in inp.replace(',', ' ').split() if p.isdigit()]
if not parts:
print("⚠️ Invalid input. Please enter one or more numbers separated by space or comma.")
continue
indices = [int(p) for p in parts if 1 <= int(p) <= len(notifications)]
if not indices:
print("⚠️ No valid numbers found in input.")
continue
for idx in indices:
selected = notifications[idx - 1]
mark_thread_done(selected["id"])
if __name__ == "__main__":
main()