Add Labels to GitHub Notifications

Use API calls to get the labels of all issues and pull requests from the notification list.

当前为 2023-08-03 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Add Labels to GitHub Notifications
  3. // @namespace https://gf.qytechs.cn/en/users/668659-denvercoder1
  4. // @match https://github.com/notifications
  5. // @grant none
  6. // @license MIT
  7. // @version 1.0.0
  8. // @author Jonah Lawrence
  9. // @description Use API calls to get the labels of all issues and pull requests from the notification list.
  10. // ==/UserScript==
  11.  
  12. /*
  13. * Manually clear cache by running the following in the console:
  14. * localStorage.setItem("labels", "{}");
  15. *
  16. * Get more GitHub API requests and enable private repos with a personal access token:
  17. * localStorage.setItem("gh_token", "YOUR_TOKEN_HERE");
  18. *
  19. * To get a personal access token go to https://github.com/settings/tokens/new
  20. * To enable private repos, you will need to enable the repos scope for the token.
  21. */
  22.  
  23. (() => {
  24. function hexToRgb(hex) {
  25. const bigint = parseInt(hex, 16);
  26. const r = (bigint >> 16) & 255;
  27. const g = (bigint >> 8) & 255;
  28. const b = bigint & 255;
  29. return [r, g, b];
  30. }
  31.  
  32. function hexToHsl(hex) {
  33. let [r, g, b] = hexToRgb(hex);
  34. r /= 255;
  35. g /= 255;
  36. b /= 255;
  37. const l = Math.max(r, g, b);
  38. const s = l - Math.min(r, g, b);
  39. const h = s ? (l === r ? (g - b) / s : l === g ? 2 + (b - r) / s : 4 + (r - g) / s) : 0;
  40. return [
  41. 60 * h < 0 ? 60 * h + 360 : 60 * h,
  42. 100 * (s ? (l <= 0.5 ? s / (2 * l - s) : s / (2 - (2 * l - s))) : 0),
  43. (100 * (2 * l - s)) / 2,
  44. ];
  45. }
  46.  
  47. function addLabels(labels, container) {
  48. // if there are already labels, do nothing
  49. if (container.querySelector(".js-issue-labels")) {
  50. return;
  51. }
  52. // append colored labels to the notification list
  53. const labelContainer = document.createElement("div");
  54. labelContainer.className = "js-issue-labels d-flex flex-wrap";
  55. labelContainer.style.marginTop = "10px";
  56. labelContainer.style.maxHeight = "20px";
  57. labels.forEach((label) => {
  58. const labelElement = document.createElement("span");
  59. labelElement.className = "IssueLabel hx_IssueLabel width-fit mb-1 mr-1 d-inline-flex";
  60. const [r, g, b] = hexToRgb(label.color);
  61. const [h, s, l] = hexToHsl(label.color);
  62. labelElement.setAttribute(
  63. "style",
  64. `--label-r:${r};--label-g:${g};--label-b:${b};--label-h:${h};--label-s:${s};--label-l:${l};`
  65. );
  66. labelElement.innerText = label.name;
  67. labelElement.addEventListener("click", (e) => {
  68. e.stopPropagation();
  69. window.open(label.url);
  70. });
  71. labelContainer.appendChild(labelElement);
  72. });
  73. container.appendChild(labelContainer);
  74. }
  75.  
  76. function run() {
  77. const cachedLabels = JSON.parse(localStorage.getItem("labels") || "{}");
  78. [...document.querySelectorAll(".notification-list-item-link:not(.added-notifications)")].map((a) => {
  79. a.classList.add("added-notifications");
  80. const url = a.href;
  81. if (cachedLabels[url]) {
  82. console.info("cached", url, cachedLabels[url]);
  83. addLabels(cachedLabels[url], a.parentElement);
  84. return;
  85. }
  86. const issueRegex = /https:\/\/github.com\/(.*)\/(.*)\/(issues|pull)\/(\d+)/;
  87. const match = url.match(issueRegex);
  88. if (match) {
  89. const [, owner, repo, , number] = match;
  90. const apiUrl = `https://api.github.com/repos/${owner}/${repo}/issues/${number}`;
  91. const headers = {
  92. Accept: "application/vnd.github.v3+json",
  93. };
  94. const token = localStorage.getItem("gh_token") || "";
  95. if (token) {
  96. headers.Authorization = `token ${token}`;
  97. }
  98. fetch(apiUrl, {
  99. headers,
  100. })
  101. .then((response) => response.json())
  102. .then((data) => {
  103. const labels = data.labels || [];
  104. console.info("fetched", apiUrl, labels);
  105. cachedLabels[url] = labels.map((label) => ({
  106. date: new Date(),
  107. name: label.name,
  108. color: label.color,
  109. url: label.url,
  110. }));
  111. localStorage.setItem("labels", JSON.stringify(cachedLabels));
  112. addLabels(cachedLabels[url], a.parentElement);
  113. })
  114. .catch((error) => console.error(error));
  115. }
  116. });
  117. }
  118.  
  119. function init() {
  120. // clear cache older than 2 hours
  121. const cachedLabels = JSON.parse(localStorage.getItem("labels") || "{}");
  122. Object.keys(cachedLabels).forEach((url) => {
  123. const { date } = cachedLabels[url];
  124. if (new Date() - new Date(date) > 2 * 60 * 60 * 1000) {
  125. delete cachedLabels[url];
  126. }
  127. });
  128. localStorage.setItem("labels", JSON.stringify(cachedLabels));
  129.  
  130. // run every 500ms
  131. setInterval(run, 500);
  132. }
  133.  
  134. // run init when the page loads or if it has already loaded
  135. if (document.readyState === "loading") {
  136. document.addEventListener("DOMContentLoaded", init);
  137. } else {
  138. init();
  139. }
  140. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址