atcoder-hashtag-setter2

AtCoder の共有ボタンの埋め込みテキストにハッシュタグを追加して、コンテスト名または問題名を補完して、ハッシュタグを X で検索するボタンを追加します。

  1. // ==UserScript==
  2. // @name atcoder-hashtag-setter2
  3. // @namespace https://github.com/hotarupoyo
  4. // @version 3.0.0
  5. // @author hotarupoyo
  6. // @description AtCoder の共有ボタンの埋め込みテキストにハッシュタグを追加して、コンテスト名または問題名を補完して、ハッシュタグを X で検索するボタンを追加します。
  7. // @license MIT
  8. // @match https://atcoder.jp/contests/*
  9. // @exclude https://atcoder.jp/contests/
  10. // ==/UserScript==
  11.  
  12. (function () {
  13. 'use strict';
  14.  
  15. var _a;
  16. const contestId = location.pathname.split("/")[2];
  17. const contestTitle = (_a = document.querySelector(".contest-title")) == null ? void 0 : _a.innerText;
  18. const problemId = (() => {
  19. if (location.pathname.match(/contests\/.+\/tasks\/.+/) != null) {
  20. return location.pathname.match(/contests\/.+\/tasks\/.+/) != null ? location.pathname.split("/")[4] : void 0;
  21. }
  22. if (location.pathname.match(/contests\/.+\/submissions\/\d+/) != null) {
  23. const trs = Array.from(document.querySelectorAll("tr"));
  24. const tr = trs.find(
  25. (element) => ["問題", "Tasks"].includes(element.firstElementChild.innerText ?? "")
  26. );
  27. if (tr == null) {
  28. return void 0;
  29. }
  30. return tr.lastElementChild.firstElementChild.pathname.split(
  31. "/"
  32. )[4];
  33. }
  34. return void 0;
  35. })();
  36. const problemTitle = (() => {
  37. var _a2;
  38. if (location.pathname.match(/contests\/.+\/tasks\/.+/) != null) {
  39. return ((_a2 = document.querySelector("meta[property='og:title']")) == null ? void 0 : _a2.getAttribute("content")) ?? void 0;
  40. }
  41. if (location.pathname.match(/contests\/.+\/submissions\/\d+/) != null) {
  42. const trs = Array.from(document.querySelectorAll("tr"));
  43. const tr = trs.find(
  44. (element) => ["問題", "Tasks"].includes(element.firstElementChild.innerText ?? "")
  45. );
  46. if (tr == null) {
  47. return void 0;
  48. }
  49. return tr.lastElementChild.innerText.trim();
  50. }
  51. return void 0;
  52. })();
  53. (() => {
  54. const aElements = document.querySelectorAll("ul > li > ul > li > a");
  55. for (let i = 0; i < aElements.length; i++) {
  56. const element = aElements[i];
  57. if (element != null && ["マイプロフィール", "My Profile"].includes(element.innerText.trim())) {
  58. return element.pathname.split("/")[2];
  59. }
  60. }
  61. return void 0;
  62. })();
  63. const userIdSubmittedBy = (() => {
  64. if (location.pathname.match(/contests\/.+\/submissions\/\d+/) != null) {
  65. const trs = Array.from(document.querySelectorAll("tr"));
  66. const tr = trs.find(
  67. (element) => ["ユーザ", "User"].includes(element.firstElementChild.innerText ?? "")
  68. );
  69. if (tr == null) {
  70. return void 0;
  71. }
  72. return tr.lastElementChild.innerText.trim();
  73. }
  74. return void 0;
  75. })();
  76. const ConvertIso8601BasicToExtended = (iso8601basic) => {
  77. const d = iso8601basic;
  78. return `${d.substring(0, 4)}-${d.substring(4, 6)}-${d.substring(6, 11)}:${d.substring(11, 13)}`;
  79. };
  80. const contestDuration = document.querySelectorAll(".contest-duration > a");
  81. const contestStartTime = new Date(
  82. ConvertIso8601BasicToExtended(new URLSearchParams(contestDuration.item(0).search).get("iso") ?? "0")
  83. );
  84. const contestEndTime = new Date(
  85. ConvertIso8601BasicToExtended(new URLSearchParams(contestDuration.item(1).search).get("iso") ?? "0")
  86. );
  87. const hasContestEnded = /* @__PURE__ */ new Date() >= contestEndTime;
  88. const IsContestPermanent = contestEndTime.getTime() - contestStartTime.getTime() >= 365 * 24 * 60 * 60 * 1e3;
  89. (() => {
  90. var _a2, _b, _c, _d, _e;
  91. if (!(hasContestEnded || IsContestPermanent)) {
  92. return;
  93. }
  94. const contestHashtag = contestId ? `#AtCoder_${contestId}` : void 0;
  95. const problemHashtag = problemId ? `#AtCoder_${problemId}` : void 0;
  96. const userHashtag = userIdSubmittedBy ? `#AtCoder_${userIdSubmittedBy}` : void 0;
  97. const orgtext = (_a2 = document.querySelector("div.a2a_kit")) == null ? void 0 : _a2.getAttribute("data-a2a-title");
  98. if (orgtext == null) {
  99. console.warn("AtCoder HashTag Setter2", "共有ボタンの取得に失敗しました。");
  100. return;
  101. }
  102. const text = (() => {
  103. if (location.pathname.match(/contests\/.+\/tasks\/.+/) != null) {
  104. return `${orgtext} - ${contestTitle} ${contestHashtag} ${problemHashtag}`;
  105. }
  106. if (location.pathname.match(/contests\/.+\/submissions\/\d+/) != null) {
  107. return `${orgtext} - ${problemTitle} ${contestHashtag} ${problemHashtag} ${userHashtag}`;
  108. }
  109. return `${orgtext} ${contestHashtag}`;
  110. })();
  111. (_b = document.querySelector("div.a2a_kit")) == null ? void 0 : _b.setAttribute("data-a2a-title", text);
  112. if (userHashtag != null) {
  113. const searchUserUrl = `https://twitter.com/search?q=${encodeURIComponent(userHashtag)}`;
  114. const searchUserHtml = `<a class="btn btn-danger btn-xs" href="${searchUserUrl}" role="button">${userHashtag}</a>`;
  115. (_c = document.querySelector("div.a2a_kit")) == null ? void 0 : _c.insertAdjacentHTML("afterbegin", searchUserHtml);
  116. }
  117. if (problemHashtag != null) {
  118. const searchProblemUrl = `https://twitter.com/search?q=${encodeURIComponent(problemHashtag)}`;
  119. const searchProblemHtml = `<a class="btn btn-primary btn-xs" href="${searchProblemUrl}" role="button">${problemHashtag}</a>`;
  120. (_d = document.querySelector("div.a2a_kit")) == null ? void 0 : _d.insertAdjacentHTML("afterbegin", searchProblemHtml);
  121. }
  122. if (contestHashtag != null) {
  123. const searchContestUrl = `https://twitter.com/search?q=${encodeURIComponent(contestHashtag)}`;
  124. const searchContestHtml = `<a class="btn btn-success btn-xs" href="${searchContestUrl}" role="button">${contestHashtag}</a>`;
  125. (_e = document.querySelector("div.a2a_kit")) == null ? void 0 : _e.insertAdjacentHTML("afterbegin", searchContestHtml);
  126. }
  127. })();
  128.  
  129. })();