// ==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 = $(`P`)
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 = $(`