NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name 트위터 유령트윗 열어보기 // @namespace gaeulbyul.userscripts // @match https://twitter.com/* // @match https://mobile.twitter.com/* // @grant none // @version 0.0.2 // @author 가을별 // @license MIT // @description 유령계정이 작성한 것으로 보이는 트윗을 열어봅니다. // ==/UserScript== function dig(obj) { try { return obj() } catch (err) { if (err instanceof TypeError) { return null } else { throw err } } } function getReactEventHandlers(target) { const key = Object.keys(target) .filter(k => k.startsWith('__reactEventHandlers')) .pop() return key ? target[key] : null } function* elemsFromMutations(mutations) { for (const {addedNodes} of mutations) { for (const node of addedNodes) { if (node instanceof HTMLElement) { yield node } } } } new MutationObserver(mutations => { const tweetElems = [] for (const elem of elemsFromMutations(mutations)) { Array .from(elem.querySelectorAll('article[role=article]')) .forEach(elem => tweetElems.push(elem)) } for (const tweetElem of tweetElems) { const tombstoneNotice = tweetElem.querySelector('a[href="https://help.twitter.com/rules-and-policies/notices-on-twitter"]') if (!tombstoneNotice) { continue } const reh = getReactEventHandlers(tweetElem.parentElement) const key = reh.children._owner.key const epitaph = dig(() => reh.children._owner.stateNode.props.data.data.content.epitaph) // missing이 아닌 다른게 들어가면 다른 사유로 트윗이 안 보이는 경우. (예를 들어 프로텍트) if (!/missing/i.test(epitaph)) { continue } const tweetIdMatch = /^tweet-(\d+)$/.exec(key) if (tweetIdMatch) { const tweetId = tweetIdMatch[1] const showTweetElem = document.createElement('a') // 공개된 트윗의 경우, 유저네임 부분에 임의로 넣어도 알아서 리다이렉트가 된다. // 따라서, 유저네임 부분을 i로 한다. showTweetElem.href = `https://${location.hostname}/i/status/${tweetId}` showTweetElem.textContent = '<이 트윗 열어보기>' showTweetElem.title = `이 트윗 링크를 열어봅니다. 유령계의 트윗은 보이나 프로텍트,차단당함,트윗/계정삭제,계정정지에 해당하는 트윗은 보이지 않습니다.` showTweetElem.style.color = 'violet' showTweetElem.style.margin = '0 5px' tombstoneNotice.parentElement.appendChild(showTweetElem) } } }).observe(document.body, { childList: true, subtree: true, })