niconizer-tweetdeck

Twitter の新着ツイートを niconizer へ連携します

// ==UserScript==
// @name         niconizer-tweetdeck
// @namespace    https://github.com/matzkoh
// @version      1.1.0
// @description  Twitter の新着ツイートを niconizer へ連携します
// @author       matzkoh
// @include      https://tweetdeck.twitter.com/
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @noframes
// ==/UserScript==

GM_registerMenuCommand(`[niconizer]: Set server URL`, () => {
  const currentValue = GM_getValue('wsUrl', 'ws://localhost:25252/')
  const value = prompt('Set server URL', currentValue)
  if (value && value !== currentValue) {
    GM_setValue('wsUrl', value)
    location.reload()
  }
})
const $3c4b2b695963bd2f$var$ws = new Promise(resolve => {
  const wsUrl = GM_getValue('wsUrl', 'ws://localhost:25252/')
  const ws = new WebSocket(wsUrl)
  ws.addEventListener('open', () => resolve(ws))
})
function $3c4b2b695963bd2f$var$escapeHtml(str) {
  const el = document.createElement('div')
  el.textContent = str
  return el.innerHTML
}
async function $3c4b2b695963bd2f$var$sendComment(comment) {
  ;(await $3c4b2b695963bd2f$var$ws).send($3c4b2b695963bd2f$var$escapeHtml(comment))
}
document.addEventListener('click', event => {
  const icon = event.target.closest('.niconizer-toggle-icon')
  if (!icon) return
  const isActive = icon.classList.toggle('icon-toggle-on')
  icon.classList.toggle('icon-toggle-off')
  event.target.closest('.column')?.classList.toggle('niconizer-watching', isActive)
})
new MutationObserver(mutations => {
  const addedElements = mutations.flatMap(m => Array.from(m.addedNodes)).filter(n => n.nodeType === Node.ELEMENT_NODE)
  addedElements
    .filter(el => el.matches('.column:not(.js-simple-column)'))
    .filter(el => el.querySelector('.column-header-links'))
    .filter(el => !el.querySelector('.niconizer-toggle-icon'))
    .forEach($3c4b2b695963bd2f$var$appendToggleIcon)
  addedElements
    .filter(el => el.matches('.column-header, .column-header-links'))
    .filter(el => !el.querySelector('.niconizer-toggle-icon'))
    .map(el => el.closest('.column:not(.js-simple-column)'))
    .filter(Boolean)
    .forEach($3c4b2b695963bd2f$var$appendToggleIcon)
  addedElements
    .filter(el => el.matches('.stream-item'))
    .filter(el => el.closest('.column.niconizer-watching'))
    .map($3c4b2b695963bd2f$var$getCommentBodyFromTweet)
    .filter(Boolean)
    .forEach($3c4b2b695963bd2f$var$sendComment)
}).observe(document.body, {
  childList: true,
  subtree: true,
})
function $3c4b2b695963bd2f$var$appendToggleIcon(column) {
  const buttonContainer = column.querySelector('.column-header-links')
  const btn = document.createElement('span')
  btn.className = 'column-header-link'
  btn.innerHTML =
    '<i class="niconizer-toggle-icon icon js-show-tip is-actionable" data-tooltip-position="bottom" title="niconizer 連携"></i>'
  const isActive = column.classList.contains('niconizer-watching')
  const icon = btn.firstElementChild
  icon.classList.toggle('icon-toggle-on', isActive)
  icon.classList.toggle('icon-toggle-off', !isActive)
  buttonContainer.insertBefore(btn, buttonContainer.firstElementChild)
}
function $3c4b2b695963bd2f$var$getCommentBodyFromTweet(el) {
  const isRetweet = el.querySelector('.tweet-context')
  if (isRetweet) return null
  const textEl = el.querySelector('.tweet-text')
  if (!textEl) return null
  let text = textEl.textContent
  el.closest('.column')
    ?.querySelector('.column-title-edit-box')
    ?.value.match(/#[^\s()]+/g)
    ?.forEach(hashTag => {
      text = text.split(hashTag).join('')
    })
  return text.trim().replace(/\s+/g, ' ')
}