// ==UserScript== // @name YoutubeChatOnPTT // @name:zh-TW Youtube聊天使顯示PTT推文 // @namespace https://github.com/zoosewu/PTTChatOnYoutube // @version 1.0.8 // @description connect ptt pushes to youtube chatroom // @description:zh-tw 連結PTT推文到Youtube聊天室 // @author Zoosewu // @match https://www.youtube.com/watch?v=* // @match https://term.ptt.cc/* // @grant GM_xmlhttpRequest // @grant GM_info // @grant unsafeWindow // @run-at document-start // @require https://code.jquery.com/jquery-3.5.1.slim.min.js // @require https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js // @require https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js // @require https://cdn.jsdelivr.net/npm/vue // @connect www.ptt.cc // @homepageURL https://github.com/zoosewu/PTTChatOnYoutube/tree/master/homepage // @license MIT // @downloadURL none // ==/UserScript== 'use strict'; //user log const reportmode = false; //all log const showalllog = false; //dev log const showPTTscreen = (false || reportmode || showalllog); const showcommand = (false || reportmode || showalllog); const showPostMessage = (false || reportmode || showalllog); const showonMessage = (false || reportmode || showalllog); const showalertmsg = false || showalllog; //dev use let devmode = false; const defaultopen = false; const disablepttframe = false; const simulateisstreaming = false; // add listener to get msg const msg = { targetorigin: "", ownorigin: "", targetWindow: null, PostMessage: function (msg, data) { if (this.targetWindow !== null) { const d = { m: msg, d: data }; this.targetWindow.postMessage(d, this.targetorigin); if (showPostMessage) console.log(this.ownorigin + " message posted", d); } }, onMessage: function (event) { // Check sender origin to be trusted if (event.origin !== msg.targetorigin) return; const data = event.data; if (showonMessage) console.log(msg.ownorigin + " onMessage", data); if (typeof (msg[data.m]) == "function") { msg[data.m].call(null, data.d); } }, } if (window.addEventListener) { window.addEventListener("message", msg.onMessage, false); } else if (window.attachEvent) { window.attachEvent("onmessage", msg.onMessage, false); } let isTopframe = (window.top == window.self); if (/www\.youtube\.com\/watch\?v=/.exec(window.location.href) !== null) { //check script work in right frame if (!isTopframe) throw new Error("Script Stopped when Youtube is not top frame"); //init postmessage msg.targetorigin = "https://term.ptt.cc"; msg.ownorigin = "https://www.youtube.com"; msg["test"] = data => { console.log("test parent onmessage", data); }; //----- console.log("Script started at " + window.location.href); runYoutubeScript(); console.log("Youtube Script initialize finish."); //----- } else if (/term\.ptt\.cc/.exec(window.location.href) !== null) { //check script work in right frame if (isTopframe) throw new Error("Script Stopped when PTT is top frame"); //init postmessage msg.ownorigin = "https://term.ptt.cc"; msg.targetorigin = "https://www.youtube.com"; msg.targetWindow = top; msg["test"] = data => { console.log("test child onmessage", data); }; //----- console.log("Script started at " + window.location.href); runPTTScript(); console.log("PTT Script initialize finish."); //----- } //Youtube--------------------------------------------------------------------------------------------------------------------- function runYoutubeScript() { const testPTTurl = "https://www.ptt.cc/bbs/C_Chat/M.1606557604.A.904.html"; let player; let isinitPTT = false; let ConnectAlertDiv; let AutoScrolling = true; let isstreaming; let streamtime = new Date(); let streamtimeinput; //let urlPushData = {}; let PTTpostdata = {}; let gotomainchat = false; let autogetpush = false; let lastgetpushtime = Date.now(); let isstreambeforepost = false; let pushdata = { AID: "", board: "", posttime: new Date(), lastpushtime: new Date(), lastendline: 0, pushes: [], pushcount: 0, nowpush: 0, }; ChechChatInstanced(); (function () { (function AddBootstrap(frame) { const frameHead = $("head", frame); const frameBody = $("body", frame); frameHead.append($(``)); frameBody.append($(``)); frameBody.append($(``)); frameBody.append($(``)); })(document) })(); function ChechChatInstanced() { const ChatContainer = $(`ytd-live-chat-frame`); const defaultChat = $(`iframe`, ChatContainer); if (defaultChat.length > 0) { console.log("chat frame instanced"); ChatContainer.css({ "position": "relative" }); player = document.getElementsByTagName("video")[0]; if (simulateisstreaming) isstreaming = true; InitChatApp(defaultChat); } else { setTimeout(ChechChatInstanced, 1000); } } function InitChatApp(defaultChatApp) { //console.log(defaultChatApp); const PTTAppCollapse = $(`
`); const PTTApp = $(`
`); const PTTChatnavbar = $(` `); const PTTChatContents = $(`
    `); const MainBtn = $(``) PTTAppCollapse.insertBefore(defaultChatApp); PTTAppCollapse.append(PTTApp); setTimeout(() => { const YTbgcolor = getComputedStyle($('html')[0]).backgroundColor; let bdcolor, ptp, pid, ptm, pmsg, ptxt; const colorlight = "rgb(120, 120, 120)"; const colordark = "rgb(24, 24, 24)" if (YTbgcolor === colordark) { updatelog("ytcolor", "深色"); bdcolor = colorlight; ptp = "#fff"; pid = "#ff6"; ptm = "#bbb"; pmsg = "#990"; ptxt = "#f8f9fa"; //PTTApp.addClass("border-white"); MainBtn.addClass("btn-outline-light"); } else { updatelog("ytcolor", "淺色"); bdcolor = colordark; ptp = "#000"; pid = "#990"; ptm = "#bbb"; pmsg = "#550"; ptxt = "#343a40"; //PTTApp.addClass("border-dark"); MainBtn.addClass("btn-outline-dark"); } const PTTcss = //PTTmaincss `.ptttext { color: ` + ptxt + `; } .pttbg {background-color: ` + YTbgcolor + `; }` + //border `.border{ border-color: ` + bdcolor + `!important; border-top-color: `+ bdcolor + ` !important; border-right-color: `+ bdcolor + ` !important; border-bottom-color: `+ bdcolor + ` !important; border-left-color: `+ bdcolor + ` !important;}` + //PTTpushcss `.pid { color: ` + pid + `; } .ptime { color: ` + ptm + `; } .pmsg { color: `+ pmsg + `; } .ptype { color: ` + ptp + `}`; const style = document.createElement('style'); if (style.styleSheet) { style.styleSheet.cssText = PTTcss; } else { style.appendChild(document.createTextNode(PTTcss)); } $('head')[0].appendChild(style); }, 100); MainBtn.insertBefore(defaultChatApp); MainBtn.css({ "z-index": "450", "position": "absolute" }); if (defaultopen) { $(`#PTTMainBtn`)[0].click(); } PTTApp.append(PTTChatnavbar); PTTApp.append(PTTChatContents); PTTChatContents.css({ "height": defaultChatApp[0].clientHeight * 0.6 + "px" }); player.addEventListener('timeupdate', PlayerUpdate); /*------------------------------------CHAT------------------------------------*/ PTTChat_Chat_Main = $(`#PTTChat-contents-Chat-pushes`, PTTChatContents); PTTChat_Chat = $(`#PTTChat-contents-Chat-main`, PTTChatContents);; const PTTChat_Chat_btn = $(`#PTTChat-contents-Chat-btn`, PTTChatContents); const streamtimecollapse = $(`#PTTChat-Time`, PTTChatContents); const lbtn = $(`#minus-time`, streamtimecollapse); const rbtn = $(`#add-time`, streamtimecollapse); PTTChat_Chat[0].addEventListener("scroll", function () { const scrollnowpos = PTTChat_Chat[0].scrollTop; const t = Date.now() - scriptscrolltime; scriptscrolltime = Date.now(); //if (t < 0) { if ((scrolllastpos + 5 > scrollnowpos && scrollnowpos > scrolltargetpos - 5) || (scrolllastpos - 5 < scrollnowpos && scrollnowpos < scrolltargetpos + 5)) { console.log("auto scrolling, (targetpos, lastpos, nowpos): (" + scrolltargetpos + ", " + scrolllastpos + ", " + scrollnowpos + "), time: " + t); //script scrolling updatelog("targetscroll", scrolltargetpos); updatelog("nowscroll", scrollnowpos); updatelog("lastscroll", scrolllastpos); scrolllastpos = scrollnowpos; } else { //user scrolling console.log("user scrolling, (targetpos, lastpos, nowpos): (" + scrolltargetpos + ", " + scrolllastpos + ", " + scrollnowpos + ")"); AutoScrolling = false; //PTTChat_Chat_btn.css({ "z-index": "450" }); // PTTChat_Chat_btn.css({ "z-index": "450" }); PTTChat_Chat_btn.removeClass('d-none'); streamtimecollapse.removeClass('d-none'); } }); const autoscrollbtn = $(`#AutoScroll`, PTTChatContents); autoscrollbtn[0].addEventListener("click", function (event) { event.preventDefault(); AutoScrolling = true; ScrollToTime(true); // PTTChat_Chat_btn.css({ "z-index": "0" }); // PTTChat_Chat_btn.css({ "z-index": "0" }); PTTChat_Chat_btn.addClass('d-none'); streamtimecollapse.addClass('d-none'); }); lbtn[0].addEventListener('click', () => { const result = /(\d\d)\:(\d\d)/.exec(streamtimeinput[0].value); streamtimeinput[0].value = result[1] + ":" + (result[2] - 1); autoscrollbtn[0].click(); UpdateStreamTime(); }); rbtn[0].addEventListener('click', () => { const result = /(\d\d)\:(\d\d)/.exec(streamtimeinput[0].value); streamtimeinput[0].value = result[1] + ":" + (result[2] + 1); autoscrollbtn[0].click(); UpdateStreamTime(); }); /*------------------------------------Connect------------------------------------*/ const PTTChat_Connect = $(`#PTTChat-contents-Connect-main`, PTTChatContents); ConnectAlertDiv = $(`#PTTChat-contents-Connect-alert`, PTTChatContents); const PTTChat_ConnectContent = $(`
    檢測到新版本 `); const fakedata = '{"board":"Test","AID":"1VpKTOfx","title":"","posttime":"2020-12-06T21:04:22.000Z","pushes":[{"type":"→ ","id":"ZooseWu","content":"推文1","date":"2020-12-06T21:04:00.000Z"},{"type":"→ ","id":"ZooseWu","content":"推文2","date":"2020-12-06T21:05:00.000Z"},{"type":"→ ","id":"ZooseWu","content":"推文3","date":"2020-12-06T21:05:00.000Z"},{"type":"→ ","id":"ZooseWu","content":"","date":"2020-12-06T21:05:00.000Z"},{"type":"→ ","id":"ZooseWu","content":"推文5","date":"2020-12-06T21:05:00.000Z"},{"type":"→ ","id":"ZooseWu","content":"推文678","date":"2020-12-06T21:05:00.000Z"},{"type":"→ ","id":"ZooseWu","content":"推文100","date":"2020-12-06T21:06:00.000Z"},{"type":"→ ","id":"ZooseWu","content":"推文101","date":"2020-12-06T21:06:00.000Z"},{"type":"→ ","id":"ZooseWu","content":"推文102Y","date":"2020-12-06T21:10:00.000Z"},{"type":"→ ","id":"ZooseWu","content":"123","date":"2020-12-06T21:11:00.000Z"},{"type":"推 ","id":"hu7592","content":"☂","date":"2020-12-06T22:24:00.000Z"},{"type":"→ ","id":"ss15669659","content":"☂","date":"2020-12-06T23:56:00.000Z"},{"type":"→ ","id":"ZooseWu","content":"hey","date":"2020-12-07T00:31:00.000Z"}],"startline":"127","endline":"149","percent":"100"}'; const fakedata1push = '{"board":"Test","AID":"1VpKTOfx","title":"","posttime":"2020-12-06T21:04:22.000Z","pushes":[{"type":"→ ","id":"ZooseWu","content":"hey","date":"2020-12-07T00:31:00.000Z"}],"startline":"127","endline":"149","percent":"100"}'; PTTChat_Connect.append(PTTChat_ConnectContent); const loginbtn = $(`#PTTlogin`, PTTChat_Connect); const fakebtn = $(`#fakebtn`, PTTChat_Connect); const pptid = $(`#PTTid`, PTTChat_Connect); const pttpw = $(`#PTTpw`, PTTChat_Connect); const postinput = $(`#post0`, PTTChat_Connect); const postbtn = $(`#post0btn`, PTTChat_Connect); const streambeforepost = $(`#streambeforepost`, PTTChat_Connect); streamtimeinput = $(`#stream-time`, PTTChatContents); streamtimeinput[0].addEventListener("input", function () { UpdateStreamTime(); PlayerUpdate(); }, false); streambeforepost[0].addEventListener("click", () => { isstreambeforepost = streambeforepost[0].checked; UpdateStreamTime(); }); loginbtn[0].addEventListener("click", function () { const i = pptid[0].value; const p = pttpw[0].value; msg.PostMessage("login", { id: i, pw: p }); //GetChatData(posturl, AlertMsg, postindex); }); pptid[0].addEventListener("keyup", loginenter); pttpw[0].addEventListener("keyup", loginenter); function loginenter(event) { if (event.keyCode === 13) { event.preventDefault(); loginbtn[0].click(); } } postbtn[0].addEventListener("click", function () { const postAID = postinput[0].value; const result = /#(.+) \((.+)\)/.exec(postAID); if (!result || result.length <= 2) { /// AlertMsg(false, "文章AID格式錯誤,請重新輸入。"); } else { gotomainchat = true; if (pushdata.AID === result[1] && pushdata.board === result[2]) { msg.PostMessage("getpost", { AID: pushdata.AID, board: pushdata.board, startline: pushdata.lastendline }); } else { PTTChat_Chat_Main.html(""); pushdata = { AID: "", board: "", posttime: new Date(), lastpushtime: new Date(), lastendline: 0, pushes: [], pushcount: 0, nowpush: 0, }; msg.PostMessage("getpost", { AID: result[1], board: result[2], startline: 0 }); } } }); postinput[0].addEventListener("keyup", e => { if (e.keyCode === 13) { e.preventDefault(); postbtn[0].click(); } }); fakebtn[0].addEventListener("click", getfakedata); function getfakedata(e, f) { f = f || fakedata; console.log("分析假推文", f); const obj = JSON.parse(f, dateReviver); ParsePostData(obj); if (simulateisstreaming) setTimeout(getfakedata, 5000, null, fakedata1push); } /*-------------------------------------Other-------------------------------------*/ const other = `
    使用教學:

    1.設定紀錄檔開始的時間

    (實況無須設定)

    2.輸入帳號與密碼登入PTT

    3.在你自己的PTT找到想要同步的文章

    4.鍵入大寫Q複製文章完整AID

    5.將複製的AID貼上並讀取文章

     

     

    如果需要回報或有任何問題請打開除錯模式

     

     

     

     

     

     

     

    聲明:

    我沒有架伺服器,所以沒辦法自動幫你找今天討論串是什麼這種方便的功能。

     

    我的程式碼都公開在網路上了,如果覺得我會到帳號請不要使用。

     

    請保證打開Youtube時沒有其他PTT腳本同時執行,這很重要。

     

    我盡量確保你的帳號不會因為我的插件被盜了。

     

    但是如果你被盜了我不負責。

     

    如果你用了插件導致被水桶或被退註或封IP與我無關。

     

    完整聲明請點網站說明進入

     

    Zoosewu

     

    腳本介紹 greasyfork github `; $(`#PTTChat-contents-other-main`, PTTChatContents).html(other); $(`#opendevmode`, PTTChatContents)[0].addEventListener('click', () => { if (!devmode) { devmode = true; DevMode(); } }); /*------------------------------------PTT畫面------------------------------------*/ MainBtn[0].addEventListener("click", () => { checkScriptEvent(); if (!isinitPTT && !disablepttframe) { isinitPTT = true; //PTTChat - contents - PTT const PTTChat_PTT = $(`#PTTChat-contents-PTT-main`, PTTChatContents); const PTTFrame = $(`