您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Twitchで他のユーザーのチャット履歴を見ることができるスクリプトです.
// ==UserScript== // @name Twitch User Chat Log // @namespace TwitchUserChatLogScript // @version 1.1 // @description Twitchで他のユーザーのチャット履歴を見ることができるスクリプトです. // @author Emble // @match https://www.twitch.tv/* // @require https://code.jquery.com/jquery-3.3.1.min.js // @noframes // @grant none // ==/UserScript== (function() { var intervalID = [] let retryCounter = [0, 0] const Page = { isStreaming: (location.pathname.indexOf('video') !== -1)? false : true, getChatArea: ()=>{return (location.pathname.indexOf('video') !== -1)?document.getElementsByClassName('tw-align-items-end tw-flex tw-flex-wrap tw-full-width')[0]:document.getElementsByClassName('chat-scrollable-area__message-container tw-flex-grow-1 tw-pd-b-1')[0]}, putOpenPanelButton: ()=>{ if(!Page.checkExistElementClass('tw-flex tw-flex-grow-1 tw-flex-wrap tw-mg-b-05 tw-mg-l-1')){ return } const prt = document.getElementsByClassName('tw-flex tw-flex-grow-1 tw-flex-wrap tw-mg-b-05 tw-mg-l-1')[0] const btn = document.createElement('div') btn.className = 'tw-mg-r-05 tw-mg-t-05' btn.innerHTML = HTML.logButton prt.appendChild(btn) return }, putClosePanelButton: ()=>{ if(!Page.checkExistElementId('TUCL_top_bar') || Page.checkExistElementId('TUCL_button_close_panel')){ return } const prt = document.getElementById('TUCL_top_bar') const btn = document.createElement('button') btn.id = 'TUCL_button_close_panel' btn.innerHTML = '<span>X</span>' btn.style.width = '20px' btn.style.height = '20px' btn.style.zIndex = '2' btn.onclick = Page.closeLogPanel prt.appendChild(btn) }, putEvent: ()=>{ document.getElementById('TUCL_button_open_panel').onclick = Page.createLogPanel return }, createLogPanel: ()=>{ if(Page.checkExistElementId('TUCL_LogPanel')){ Page.clearChatLog() Page.drawChatLog() return } const pr = document.getElementsByClassName('chat-room__content tw-c-text-base tw-flex tw-flex-column tw-flex-grow-1 tw-flex-nowrap tw-full-height tw-relative')[0] const pl = document.createElement('div') pl.id = 'TUCL_LogPanel' pl.style.background = 'var(--color-background-body)' pl.style.overflowY = 'scroll' pl.style.whiteSpace = 'normal' pl.style.position = 'absolute' pl.style.width = '350px' pl.style.height = '400px' pl.style.left = '-300px' pl.style.opacity = '90%' pl.style.zIndex = '1' pl.innerHTML = '<div id="TUCL_top_bar"></div><div id="TUCL_chat_log_list"></div>' pr.appendChild(pl) Page.drawChatLog() Page.putClosePanelButton() }, closeLogPanel: ()=>{ const el = document.getElementById('TUCL_LogPanel') el.parentNode.removeChild(el) }, clearChatLog: ()=>{ if(!Page.checkExistElementId('TUCL_chat_log_list')){ return } document.getElementById('TUCL_chat_log_list').innerHTML = '' }, getTargetUserName: () => { const el = document.getElementsByClassName('tw-link tw-link--hover-color-inherit tw-link--hover-underline-none tw-link--inherit')[0] const href = el.getAttribute('href') console.log(href.substr(1)) return href.substr(1) }, drawChatLog: () => { if(!Page.checkExistElementId('TUCL_chat_log_list')){ return } const name = Page.getTargetUserName() //名前の検索 const array = JSON.parse(sessionStorage.getItem('TUCL_Log')) let chatLog = [] for(let key of Object.keys(array)){ if(key == name){ chatLog = array[key] break } } let html = '' chatLog.map((chat)=>{ html += '<span style="font-size: 16px;color: var(--color-text-base);display: block;border-bottom: solid 1px var(--color-text-base);">'+chat+'</span>' }) const panel = document.getElementById('TUCL_chat_log_list') panel.innerHTML = html console.log(chatLog) }, waitUserCardLoading: ()=>{ clearInterval(intervalID[1]) intervalID[1] = setInterval(()=>{ if(Page.checkExistElementClass('tw-flex tw-flex-grow-1 tw-flex-wrap tw-mg-b-05 tw-mg-l-1')){ if(!Page.checkExistElementId('TUCL_Label')){ Page.putOpenPanelButton() Page.putEvent() } Page.clearChatLog() Page.drawChatLog() clearInterval(intervalID[1]) } if(retryCounter[1] > 10){ clearInterval(intervalID[1]) } },500) }, observeUserCard: new MutationObserver((ms)=>{ ms.forEach((e) => { const addedElements = e.addedNodes for(let i = 0; i < addedElements.length; i++){ if(addedElements[i].getAttribute('data-a-target') === 'viewer-card-positioner' || addedElements[i].className==='tw-border-radius-medium tw-c-background-base tw-elevation-2 tw-flex tw-flex-column viewer-card'){ Page.waitUserCardLoading() } } }) }), checkExistElementId: (el) => { if(document.getElementById(el) !== null){ return true } return false }, checkExistElementClass: (el) => { if(document.getElementsByClassName(el).length > 0){ return true } return false } } const HTML = { logButton: `<div class="tw-inline-flex viewer-card-drag-cancel" id="TUCL_Label"> <button class="ScCoreButton-sc-1qn4ixc-0 ScCoreButtonPrimary-sc-1qn4ixc-1 jeBpig tw-core-button" data-a-target="usercard-whisper-button" data-test-selector="whisper-button" id="TUCL_button_open_panel"> <div class="ScCoreButtonLabel-lh1yxp-0 bUTtZU tw-core-button-label"> <div class="tw-align-items-center tw-flex tw-mg-r-05"> <div class="ScCoreButtonIcon-khv8ri-0 fVWBSS tw-core-button-icon"> <div class="ScIconLayout-sc-1bgeryd-0 kbOjdP tw-icon" data-a-selector="tw-core-button-icon"> <div class="ScAspectRatio-sc-1sw3lwy-1 dNNaBC tw-aspect"> <div class="ScAspectSpacer-sc-1sw3lwy-0 gkBhyN"> </div> <svg width="100%" height="100%" version="1.1" viewBox="0 0 20 20" x="0px" y="0px" class="ScIconSVG-sc-1bgeryd-1 cMQeyU"><g><path fill-rule="evenodd" d="M7.828 13L10 15.172 12.172 13H15V5H5v8h2.828zM10 18l-3-3H5a2 2 0 01-2-2V5a2 2 0 012-2h10a2 2 0 012 2v8a2 2 0 01-2 2h-2l-3 3z" clip-rule="evenodd"></path></g></svg> </div> </div> </div> </div> <div data-a-target="tw-core-button-label-text" class="tw-align-items-center tw-flex tw-flex-grow-0 tw-justify-content-start"> 履歴 </div> </div> </button> </div>` } const Chat = { Observer: new MutationObserver((ms) => { ms.forEach((e) => { const addedChats = e.addedNodes for(let i = 0; i < addedChats.length; i++){ if(addedChats[i].className === 'tw-accent-region'){ continue } const chatData = Chat.convertChat(addedChats[i]) addStorage(chatData.userName, chatData.chat) } }) }), convertChat: (chat) =>{ let Data = { chat: null, userName: null } const getUserName = () => { if(chat.getElementsByClassName('chat-author__intl-login').length > 0){ const name = chat.getElementsByClassName('chat-author__intl-login')[0].innerHTML return name.match(/(?<=\().*?(?=\))/)[0] } if(chat.getElementsByClassName('chat-author__display-name').length > 0){ return chat.getElementsByClassName('chat-author__display-name')[0].innerHTML } return null } Data.chat = (chat.getElementsByClassName('text-fragment')[0])?chat.getElementsByClassName('text-fragment')[0].innerHTML:null Data.userName = getUserName() return Data } } const findChatArea = (c) => { intervalID[0] = setInterval(()=>{ let chatAreaElement = Page.getChatArea() if(chatAreaElement !== undefined ){ log('Found element') initialize() clearInterval(intervalID[0]) } if(retryCounter[0] > c){ log('Element not found.') clearInterval(intervalID[0]) } retryCounter++ },1000) } const runObserver = () => { if(Page.getChatArea() === undefined || null)return if(document.getElementsByClassName('tw-full-height tw-full-width tw-relative tw-z-above viewer-card-layer').length <= 0)return Page.observeUserCard.disconnect() Page.observeUserCard.observe(document.getElementsByClassName('tw-full-height tw-full-width tw-relative tw-z-above viewer-card-layer')[0], {childList: true, characterData: true, subtree: true}) Chat.Observer.disconnect() Chat.Observer.observe(Page.getChatArea(), {childList: true}) } const initialize = () => { if(!sessionStorage.getItem('TUCL_Log')){ let array = {} sessionStorage.setItem('TUCL_Log', JSON.stringify(array)) } runObserver() } const addStorage = (name, chat) =>{ /* * let array = { * "user1": ["chat1", "chat2"], * "user2": ["chatA", "chatB", "chatC"] * . * . * . * } */ //名前の検索 let array = JSON.parse(sessionStorage.getItem('TUCL_Log')) for(let key of Object.keys(array)){ if(key == name){ let val = array[key] val.push(chat) array[key] = val sessionStorage.setItem('TUCL_Log', JSON.stringify(array)) return } } //見つからなければ新規追加 array[name] = [] let val = array[name] val.push(chat) array[name] = val sessionStorage.setItem('TUCL_Log', JSON.stringify(array)) } const getStorage = (n) => { return JSON.parse(sessionStorage.getItem('TUCL_Log')) } const openLogWindow = () => { } let storedHref = location.href; const URLObserver = new MutationObserver(function(ms){ ms.forEach(function(m){ if(storedHref !== location.href){ storedHref = location.href log('URL Changed', storedHref, location.href) Chat.Observer.disconnect() clearInterval(intervalID[0]) findChatArea(60) } }) }) const log = (m) => console.log('[TUCL] '+m) window.onload = () => { if(!sessionStorage){ alert('セッションストレージ非対応ブラウザです。') return } URLObserver.observe(document, {childList: true, subtree: true}) findChatArea(60) } })()
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址