CodeForces-显示AC数

display AC count

  1. // ==UserScript==
  2. // @name CodeForces-显示AC数
  3. // @namespace http://tampermonkey.net/
  4. // @version 2024-05-10
  5. // @description display AC count
  6. // @author Qianfan
  7. // @match https://codeforces.com/profile/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=codeforces.com
  9. // @run-at document-end
  10. // @grant GM_addStyle
  11. // ==/UserScript==
  12.  
  13. (async function() {
  14. 'use strict';
  15. //注入css
  16. GM_addStyle(`
  17. .dropbtn {
  18. background-color: #4CAF50;
  19. color: white;
  20. padding: 10px;
  21. min-width: 100px;
  22. font-size: 14px;
  23. border: none;
  24. cursor: pointer;
  25. border-radius: 5px;
  26. }
  27.  
  28. .dropbtn.loading {
  29. background-color: #c7a233;
  30. }
  31.  
  32. .dropdown {
  33. position: relative;
  34. display: inline-block;
  35. }
  36.  
  37. .dropdown-content {
  38. border-radius: 5px;
  39. transform-origin: center top;
  40. transform: scaleY(0);
  41. transition: transform 0.4s ease;
  42. position: absolute;
  43. background-color: #f9f9f9;
  44. min-width: 150px;
  45. box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  46. }
  47.  
  48. .dropdown-content a {
  49. color: black;
  50. padding: 12px 16px;
  51. text-decoration: none;
  52. display: block;
  53. cursor: pointer;
  54. }
  55.  
  56. .dropdown-content a:last-child {
  57. color: red;
  58. }
  59.  
  60. .dropdown-content a:hover {
  61. background-color: #f1f1f1
  62. }
  63.  
  64. .dropdown:hover .dropdown-content {
  65. transform: scaleY(1);
  66. }
  67.  
  68. `)
  69. //重要变量
  70. const earliest = document.querySelector('rect').dataset.date
  71. let endDate = earliest, topMap
  72. const activeGraph = document.querySelector('div#userActivityGraph')
  73. const months = {
  74. 'Jan': '01',
  75. 'Feb': '02',
  76. 'Mar': '03',
  77. 'Apr': '04',
  78. 'May': '05',
  79. 'Jun': '06',
  80. 'Jul': '07',
  81. 'Aug': '08',
  82. 'Sep': '09',
  83. 'Oct': '10',
  84. 'Nov': '11',
  85. 'Dec': '12'
  86. }, dateRegex = /([A-Z][a-z][a-z])/
  87. const domParser = new DOMParser()
  88. let stroageKey = '__CHONGTIAN_codeforces_daily_ac_v2_'
  89. const filePaths = window.location.pathname.split('/'),
  90. username = filePaths[filePaths.length - 1]
  91. , URL = `https://codeforces.com/submissions/${username}/page`
  92. stroageKey += username
  93. let topMapStr = localStorage.getItem(stroageKey)
  94. const dropDown = document.createElement('div'),
  95. box = document.querySelector('div.roundbox.userActivityRoundBox.borderTopRound.borderBottomRound')
  96. //
  97. dropDown.innerHTML = `<div class="dropdown">
  98. <button class="dropbtn">完成!</button>
  99. <div class="dropdown-content">
  100. <a class="render">渲染</a>
  101. <a class="refresh">刷新最近数据</a>
  102. <a class="refresh-all">刷新全部数据</a>
  103. </div>
  104. </div>`
  105. box.insertBefore(dropDown, box.firstChild)
  106. const realDropDown = dropDown.querySelector('div.dropdown'),
  107. dropbtn = dropDown.querySelector('button.dropbtn')
  108. dropDown.querySelector('.dropdown-content a.render').addEventListener('click', f => render())
  109. dropDown.querySelector('.dropdown-content a.refresh').addEventListener('click', async f => await fetchData()
  110. )
  111. dropDown.querySelector('.dropdown-content a.refresh-all').addEventListener('click', async f => {
  112. if (confirm('确定要刷新全部数据吗?'))
  113. await fetchData(earliest)
  114. }
  115. )
  116. //
  117. const convertDate = date => date.replace(dateRegex, (match, p1) => months[p1])
  118. const aLaterOrEquals = (a, b) => {
  119. const aArr = a.split('/'), bArr = b.split('/');
  120. if (aArr[2] !== bArr[2]) return aArr[2] > bArr[2]
  121. if (aArr[0] !== bArr[0]) return aArr[0] > bArr[0]
  122. return aArr[1] >= bArr[1]
  123. }
  124. const solveDoc = (html, map, endDate2, getTot = false) => {
  125. const doc = domParser.parseFromString(html, 'text/html'),
  126. submissions = doc.querySelectorAll('tr[data-submission-id]')
  127. let tot = void 0, flag = false, latest = void 0
  128. if (getTot) {
  129. const lis = doc.querySelectorAll('div.pagination ul li')
  130. tot = parseInt(lis[lis.length - 2].innerText)
  131. }
  132. for (const elem of submissions) {
  133. if (!elem.querySelector('span.verdict-accepted')) continue
  134. const date = convertDate(elem.querySelector('span.format-time').innerText.split(' ')[0])
  135. if (!latest) latest = date
  136. if (!aLaterOrEquals(date, endDate2)) {
  137. flag = true
  138. break
  139. }
  140. if (!map[date]) map[date] = new Set()
  141. map[date].add(parseInt(elem.querySelector('td[data-problemid]').dataset.problemid))
  142. }
  143. return { tot, flag, latest }
  144. }
  145. const render = f => {
  146. if (!activeGraph) return
  147. for (const key in topMap) {
  148. const rect = activeGraph.querySelector(`rect[data-date="${key}"]:not([fill="#EBEDF0"])`)
  149. if (!rect) continue
  150. rect.dataset.date = `${rect.dataset.date},Accept ${topMap[key]}`
  151. }
  152. }
  153. const fetchData = async endDate1 => {
  154. dropbtn.classList.toggle('loading')
  155. dropbtn.innerHTML = '获取数据...'
  156. const dataMap = {}
  157. if (topMapStr) {
  158. topMap = JSON.parse(topMapStr)
  159. endDate = topMap.latest
  160. }
  161. endDate1 = endDate1 ?? endDate
  162. const res = await fetch(`${URL}/1`)
  163. const html = await res.text()
  164. let { flag, tot, latest } = solveDoc(html, dataMap, endDate1, true)
  165. for (let page = 2; page <= tot && !flag; ++page) {
  166. const res = await fetch(`${URL}/${page}`)
  167. const html = await res.text()
  168. flag = solveDoc(html, dataMap, endDate1).flag
  169. }
  170. for (const k in dataMap)
  171. dataMap[k] = dataMap[k].size
  172. topMap = {
  173. ...topMap,
  174. ...dataMap,
  175. latest,
  176. }
  177. dropbtn.innerHTML = '完成!'
  178. dropbtn.classList.toggle('loading')
  179. }
  180. //main
  181. await fetchData()
  182. topMapStr = JSON.stringify(topMap)
  183. localStorage.setItem(stroageKey, topMapStr)
  184. render()
  185. })();