🏠 Home 

Misskey Hashflags

TwitterのHashflagsをMisskeyに表示するやつ


Installer dette script?
  1. // ==UserScript==
  2. // @name Misskey Hashflags
  3. // @namespace https://midra.me/
  4. // @version 1.0.5
  5. // @description TwitterのHashflagsをMisskeyに表示するやつ
  6. // @author Midra
  7. // @license MIT
  8. // @match https://*/*
  9. // @icon https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png
  10. // @run-at document-body
  11. // @grant GM_getValue
  12. // @grant GM_setValue
  13. // ==/UserScript==
  14. // @ts-check
  15. /**
  16. * @typedef TwitterHashflag
  17. * @property {string} hashtag
  18. * @property {string} asset_url
  19. * @property {number} starting_timestamp_ms
  20. * @property {number} ending_timestamp_ms
  21. * @property {boolean} is_hashfetti_enabled
  22. */
  23. ;(async () => {
  24. 'use strict'
  25. const HASHFLAGS_UPDATE_INTERVAL = 2 * 60 * 60 * 1000
  26. const isTwitter =
  27. location.href.startsWith('https://twitter.com/') ||
  28. location.href.startsWith('https://x.com/')
  29. const isMisskey =
  30. document
  31. .querySelector('meta[name="application-name"]')
  32. ?.getAttribute('content') === 'Misskey'
  33. if (!isTwitter && !isMisskey) return
  34. /** @type {TwitterHashflag[]} */
  35. const hashflags = GM_getValue('hashflags', [])
  36. /** @type {TwitterHashflag[]} */
  37. const activeHashflags = hashflags
  38. .filter((v) => Date.now() < v.ending_timestamp_ms)
  39. .map((v) => ((v.hashtag = v.hashtag.toLowerCase()), v))
  40. const activeHashtags = activeHashflags.map((v) => v.hashtag)
  41. /**
  42. * @param {string} [hashtag]
  43. * @returns {TwitterHashflag | undefined}
  44. */
  45. const getHashflag = (hashtag) => {
  46. if (!hashtag) return
  47. const hashflag =
  48. activeHashflags[activeHashtags.indexOf(hashtag.toLowerCase())]
  49. if (
  50. hashflag &&
  51. hashflag.starting_timestamp_ms <= Date.now() &&
  52. Date.now() < hashflag.ending_timestamp_ms
  53. ) {
  54. return hashflag
  55. }
  56. }
  57. /**
  58. * @param {Element} target
  59. */
  60. const addHashflags = (target) => {
  61. if (activeHashflags.length === 0) return
  62. /** @type {NodeListOf<HTMLAnchorElement>} */
  63. const hashtags = target.querySelectorAll('a[href^="/tags/"]')
  64. for (const tag of hashtags) {
  65. if (tag.classList.contains('hasTwitterHashflag')) continue
  66. const text = tag.textContent
  67. if (!text?.startsWith('#')) continue
  68. const hashflag = getHashflag(text.substring(1))
  69. if (hashflag) {
  70. const img = document.createElement('img')
  71. img.classList.add('twitter_hashflag')
  72. img.src = hashflag.asset_url
  73. tag.appendChild(img)
  74. tag.classList.add('hasTwitterHashflag')
  75. }
  76. }
  77. }
  78. // /**
  79. // * @param {Element} target
  80. // */
  81. // const removeHashflags = (target) => {
  82. // for (const elm of target.getElementsByClassName('twitter_hashflag')) {
  83. // elm.remove()
  84. // }
  85. // for (const elm of target.getElementsByClassName('hasTwitterHashflag')) {
  86. // elm.classList.remove('hasTwitterHashflag')
  87. // }
  88. // }
  89. // Twitter (Hashflagsの取得・保存)
  90. if (isTwitter) {
  91. console.log('[Misskey Hashflags] Twitter')
  92. const lastUpdated = GM_getValue('hashflags_lastupdated', 0)
  93. if (HASHFLAGS_UPDATE_INTERVAL < Date.now() - lastUpdated) {
  94. try {
  95. const res = await fetch('https://twitter.com/i/api/1.1/hashflags.json')
  96. /** @type {TwitterHashflag[]} */
  97. const json = await res.json()
  98. if (json && 0 < json.length) {
  99. GM_setValue('hashflags', json)
  100. GM_setValue('hashflags_lastupdated', Date.now())
  101. console.log('[Misskey Hashflags] Hashflagsを保存しました')
  102. }
  103. } catch (e) {
  104. console.error('[Misskey Hashflags]', e)
  105. }
  106. }
  107. }
  108. // Misskey
  109. else if (isMisskey) {
  110. console.log('[Misskey Hashflags] Misskey')
  111. addHashflags(document.body)
  112. /** @type {MutationObserverInit} */
  113. const obs_options = {
  114. childList: true,
  115. subtree: true,
  116. }
  117. const obs = new MutationObserver((mutations) => {
  118. obs.disconnect()
  119. for (const mutation of mutations) {
  120. if (!(mutation.target instanceof HTMLElement)) continue
  121. if (0 < mutation.addedNodes.length) {
  122. addHashflags(mutation.target)
  123. }
  124. }
  125. obs.observe(document.body, obs_options)
  126. })
  127. obs.observe(document.body, obs_options)
  128. // style
  129. const style = document.createElement('style')
  130. style.textContent = `
  131. .twitter_hashflag {
  132. display: inline-block;
  133. height: 1.1em;
  134. margin: 0 2px -0.15em;
  135. }
  136. `
  137. document.body.appendChild(style)
  138. }
  139. })()