Skip to content

Instantly share code, notes, and snippets.

@LinusCDE
Last active February 19, 2020 06:05
Show Gist options
  • Save LinusCDE/88d804b60df0ede98ee7ac633458db93 to your computer and use it in GitHub Desktop.
Save LinusCDE/88d804b60df0ede98ee7ac633458db93 to your computer and use it in GitHub Desktop.
Youtube Comment-Block Detector
// Raw elements:
RAW_ELEMENT_COMMENT_BLOCKED = `
<span class="ytcbd ytcbd-blocked-info"
style="background-color: #800000; color: #fefefe; padding: 0 4px; margin: 0 3px; border-radius: 4px; font-weight: 800; font-size: 8pt;"
title="Dieser Kommentar wurde höchstwarscheinlich blockiert und ist nur für dich sichtbar!
YouTubes Interne Antwort lautet:
%MESSAGE%">
<span style="font-size: 9pt">✘</span>
Kommentar blockiert!
</span>
`
RAW_ELEMENT_COMMENT_OK = `
<span class="ytcbd ytcbd-accepted-info"
style="color: #088000; padding: 0 4px; font-size: 10pt;"
title="Dieser Kommentar scheint in Ordnung zu sein.">
</span>
`
YTCBD_DEBUGING = false;
function debug(message) {
if(YTCBD_DEBUGING)
console.log('[DEBUG] ' + message);
}
function toEnclosedElement(html) {
let span = document.createElement('span');
span.innerHTML = html;
return span;
}
function createOkElement() {
return toEnclosedElement(RAW_ELEMENT_COMMENT_OK);
}
function createBlockedElement(message) {
while(message.indexOf('"') !== -1)
message = message.replace('"', '&quot;');
return toEnclosedElement(RAW_ELEMENT_COMMENT_BLOCKED.replace('%MESSAGE%', message));
}
// Intercept request (Source: https://stackoverflow.com/a/48134114)
(function(xhr) {
var XHR = XMLHttpRequest.prototype;
var open = XHR.open;
var send = XHR.send;
var setRequestHeader = XHR.setRequestHeader;
XHR.open = function(method, url) {
this._method = method;
this._url = url;
this._requestHeaders = {};
this._startTime = new Date().toISOString();
return open.apply(this, arguments);
};
XHR.setRequestHeader = function(header, value) {
this._requestHeaders[header] = value;
return setRequestHeader.apply(this, arguments);
};
XHR.send = function(postData) {
this.addEventListener("load", function() {
var endTime = new Date().toISOString();
var myUrl = this._url ? this._url.toLowerCase() : this._url;
if (myUrl) {
if (postData) {
if (typeof postData === "string") {
try {
// here you get the REQUEST HEADERS, in JSON format, so you can also use JSON.parse
this._requestHeaders = postData;
} catch (err) {
console.log(
"Request Header JSON decode failed, transfer_encoding field could be base64"
);
console.log(err);
}
} else if (
typeof postData === "object" ||
typeof postData === "array" ||
typeof postData === "number" ||
typeof postData === "boolean"
) {
// do something if you need
}
}
// here you get the RESPONSE HEADERS
var responseHeaders = this.getAllResponseHeaders();
try {
if (this.responseType != "blob" && this.responseText) {
// responseText is string or null
debug('Response from ' + this._url);
onResponseReceived(this._url, this.responseText);
}
} catch (err) {
// Probably not in json format
}
}
});
return send.apply(this, arguments);
};
})(XMLHttpRequest);
async function delay(millis) {
return new Promise((resolve, reject) => {
setTimeout(resolve, millis);
});
}
async function onResponseReceived(url, responseText) {
debug('Response received.');
if (url != 'https://www.youtube.com/service_ajax?name=createCommentEndpoint' &&
url != 'https://www.youtube.com/service_ajax?name=updateCommentEndpoint')
return; // Not a response to a created comment
debug('Response is comment.');
try {
let responseObj = JSON.parse(responseText);
let commentRenderer;
if(url.endsWith('createCommentEndpoint')) // Comment created
commentRenderer = commentRenderer = responseObj.data.actions[0].createCommentAction.contents.commentThreadRenderer.comment.commentRenderer;
else // Commend edited
commentRenderer = commentRenderer = responseObj.data.actions[0].updateCommentAction.contents.commentRenderer;
let element = null;
if (commentRenderer.hasOwnProperty('replyDisabledNoticeText')) {
// Blocked
debug('Comment is likely blocked.');
let messageLines = [];
try {
for (run of commentRenderer.replyDisabledNoticeText.runs) {
messageLines.push(run.text);
}
} catch (ex) { }
if (messageLines.length == 0) {
messageLines = [ 'No message given.' ]
}
element = createBlockedElement(messageLines.join('\n'));
}else {
debug('Comment is likely ok.');
// Not blocked
element = createOkElement();
}
let commentId = commentRenderer.commentId;
debug('Comment Id is ' + commentId + '.');
let comment = null;
let polls = 0;
while(polls < 20 && comment == null) {
polls++;
await delay(100);
debug('Poll comment.');
comment = findComment(commentId);
}
debug('Found comment (after ' + polls + ' of 20 polls).');
if(commentId === null) {
console.log('ERROR: Couldn\'t find comment!');
return;
}
clearOldMessages(comment);
appendToHeader(comment, element);
debug('Likely appended to header.');
} catch (ex) {
console.log('ERROR: Failed to check comment!');
console.log(ex);
}
}
function findComment(searchedCommentId) {
for(comment of document.getElementsByTagName('ytd-comment-renderer')) {
for(elementWithId of comment.querySelectorAll('.published-time-text > a')) {
if(! elementWithId.hasAttribute('href'))
continue;
let href = elementWithId.getAttribute('href');
// href="/watch?v=8O7qr-36mzc&lc=UgxyGddo8fEowF3Oa2l4A5AzAg"
let commentId = null;
try {
let queryString = href.split('?')[1];
let queryArgs = ((queryString.indexOf('&') !== -1) ? queryString.split('&') : [ queryString ]);
for(arg of queryArgs) {
if(arg.startsWith('lc='))
commentId = arg.split('lc=')[1];
}
} catch (ex) {} // Parsing fail. Probably no id here.
if(commentId !== null && commentId === searchedCommentId)
return comment;
}
}
return null;
}
function clearOldMessages(comment) {
// Deleting and readding comments keeps the old messages also.
let header = comment.querySelector('#header-author');
for(oldMessage of header.querySelectorAll('.ytcbd')) {
header.removeChild(oldMessage.parentNode);
}
}
function appendToHeader(comment, element) {
let header = comment.querySelector('#header-author');
header.appendChild(element);
}
console.log('[YoutubeCommentBlockDetector] Script running.');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment