├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── chrome-ext ├── images │ ├── logo │ │ ├── logo.png │ │ └── logo_1.png │ ├── logo_128.png │ ├── logo_16.png │ ├── logo_32.png │ └── logo_48.png ├── manifest.json ├── popup.html ├── popup.js └── script.js └── development ├── checkBox.js ├── fb.js ├── fb_scraps.js ├── live_chats.js └── scraps.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | chrome-ext.zip 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Erasure 2 | 3 | Relevant JS code is in `script.js`. 4 | 5 | 1. Work on fb.js script to delete Facebook comments. 6 | 7 | 2. Make a test page with fake comments for testing purposes -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2021 Ian Campbell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Erasure 2 | 3 | Delete your YouTube comment history. 4 | 5 | 6 | Add the Erasure Chrome extension: 7 | https://chrome.google.com/webstore/detail/erasure/lkhceldiinefjkocccdmpgheheeddfmb?hl=en-US 8 | 9 | Or navigate to https://www.youtube.com/feed/history/comment_history and then paste the entire script from "script.js" in your javascript console. 10 | 11 | Read CONTRIBUTING.md for todo list 12 | 13 | 14 | Update 2.0.1 15 | 16 | Now works on the new YouTube comment history page. 17 | -------------------------------------------------------------------------------- /chrome-ext/images/logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ian-campbell/Erasure/98a0d66796c890c8e53afa4f3afe7e0dd87ae1f2/chrome-ext/images/logo/logo.png -------------------------------------------------------------------------------- /chrome-ext/images/logo/logo_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ian-campbell/Erasure/98a0d66796c890c8e53afa4f3afe7e0dd87ae1f2/chrome-ext/images/logo/logo_1.png -------------------------------------------------------------------------------- /chrome-ext/images/logo_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ian-campbell/Erasure/98a0d66796c890c8e53afa4f3afe7e0dd87ae1f2/chrome-ext/images/logo_128.png -------------------------------------------------------------------------------- /chrome-ext/images/logo_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ian-campbell/Erasure/98a0d66796c890c8e53afa4f3afe7e0dd87ae1f2/chrome-ext/images/logo_16.png -------------------------------------------------------------------------------- /chrome-ext/images/logo_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ian-campbell/Erasure/98a0d66796c890c8e53afa4f3afe7e0dd87ae1f2/chrome-ext/images/logo_32.png -------------------------------------------------------------------------------- /chrome-ext/images/logo_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ian-campbell/Erasure/98a0d66796c890c8e53afa4f3afe7e0dd87ae1f2/chrome-ext/images/logo_48.png -------------------------------------------------------------------------------- /chrome-ext/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "YouTube Comment Deleter", 3 | "version": "2.1.2", 4 | "description": "Clear your YouTube comment history", 5 | "permissions": ["activeTab", "scripting"], 6 | "action": { 7 | "default_popup": "popup.html", 8 | "default_icon": 9 | { 10 | "16": "images/logo_16.png", 11 | "32": "images/logo_32.png", 12 | "48": "images/logo_48.png", 13 | "128": "images/logo_128.png" 14 | } 15 | }, 16 | "icons": 17 | { 18 | "16": "images/logo_16.png", 19 | "32": "images/logo_32.png", 20 | "48": "images/logo_48.png", 21 | "128": "images/logo_128.png" 22 | }, 23 | "manifest_version": 3 24 | } 25 | 26 | -------------------------------------------------------------------------------- /chrome-ext/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 65 | 66 | 67 | 68 | 69 | 70 |
71 | 72 | 73 |
74 |
75 | 76 | 77 |
78 |
79 | 80 | 81 |
82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /chrome-ext/popup.js: -------------------------------------------------------------------------------- 1 | const baseActivityPageURL = 2 | "https://myactivity.google.com/page?utm_source=my-activity&hl=en&page="; 3 | const deletionScript = "script.js"; 4 | 5 | 6 | // comment history 7 | deleteCommentHistory.onclick = function (element) { 8 | executeScriptInTab(chrome.tabs, chrome.scripting); 9 | }; 10 | commentHistory.onclick = function (element) { 11 | openTab(chrome.tabs, "https://www.youtube.com/feed/history/comment_history"); 12 | }; 13 | 14 | // live chat history 15 | deleteLiveChats.onclick = function (element) { 16 | executeScriptInTab(chrome.tabs, chrome.scripting); 17 | }; 18 | 19 | liveChats.onclick = function (element) { 20 | openTab(chrome.tabs, "https://www.youtube.com/feed/history/live_chat_history"); 21 | }; 22 | 23 | // community posts 24 | deleteCommunityPosts.onclick = function (element) { 25 | executeScriptInTab(chrome.tabs, chrome.scripting); 26 | }; 27 | 28 | communityPosts.onclick = function (element) { 29 | openTab(chrome.tabs, "https://myactivity.google.com/page?utm_source=my-activity&hl=en&page=youtube_posts_activity"); 30 | }; 31 | 32 | // reusable functions 33 | function openTab(tabs, page) { 34 | targetPage = page; 35 | tabs.create({ 36 | active: true, 37 | url: targetPage, 38 | }); 39 | } 40 | 41 | function executeScriptInTab(tabs, scripting) { 42 | tabs.query({ active: true, currentWindow: true }, function (tabs) { 43 | scripting.executeScript({ 44 | target: { tabId: tabs[0].id, allFrames: true }, 45 | files: [deletionScript], 46 | }); 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /chrome-ext/script.js: -------------------------------------------------------------------------------- 1 | // try 0, then try increasing values. this is delay between comment deletions 2 | var DELAY = 5000; 3 | 4 | // if script ends but yt has more comments loading then increase this pause value. 5 | // this provides 1 retry attempt between list updates. useful for slow cpu/network. 6 | // (electronoob: 800 was the ideal value for my machine) 7 | var PAUSE = 5000; 8 | 9 | var myList = document.getElementsByClassName("YxbmAc"); 10 | 11 | // scroll to the bottom of the page 12 | function autoScroll() { 13 | window.scrollTo({ 14 | left: 0, 15 | top: document.body.scrollHeight, 16 | behavior: "smooth", 17 | }); 18 | } 19 | 20 | // check for available comments 21 | function commentsAvailable2() { 22 | var m = document.getElementsByClassName("YxbmAc"); 23 | if (m.length > 0) { 24 | console.log("erasure: %s comments are available.", m.length); 25 | return true; 26 | } 27 | return false; 28 | } 29 | 30 | /* This function clicks the delete button for both regular and 31 | non-G-suite account users. */ 32 | function deleteClick(element, callback1) { 33 | try { 34 | element.querySelectorAll(".VfPpkd-rymPhb-pZXsl")[1].click(); 35 | setTimeout(callback1, DELAY); 36 | } catch { 37 | element.querySelectorAll(".VfPpkd-Bz112c-LgbsSe")[0].click(); 38 | setTimeout(callback1, DELAY); 39 | } 40 | } 41 | 42 | /* Main function */ 43 | function main(i) { 44 | if (commentsAvailable2()) { 45 | deleteClick(myList[i], () => { 46 | //++i; 47 | if (myList.length > 1) { 48 | main(i); 49 | } else { 50 | console.log("erasure: attempting to retry in %s ms", PAUSE); 51 | autoScroll(); 52 | setTimeout(() => { 53 | main(0); 54 | }, PAUSE); 55 | } 56 | }); 57 | } else { 58 | console.log("erasure: there are no comments, exiting."); 59 | } 60 | } 61 | 62 | main(0); 63 | -------------------------------------------------------------------------------- /development/checkBox.js: -------------------------------------------------------------------------------- 1 | // this script is supposed to click the popup permissions checkbox 2 | // doesn't work, the classes are correct though 3 | 4 | function checkboxClick(element, callbackcheck){ 5 | element.checked = true; 6 | setTimeout(callbackcheck, 200); 7 | } 8 | function checkboxOK(){ 9 | var c = document.getElementsByClassName("VfPpkd-LgbsSe VfPpkd-LgbsSe-OWXEXe-k8QpJ VfPpkd-LgbsSe-OWXEXe-dgl2Hf nCP5yc AjY5Oe DuMIQc"); 10 | c[0].click(); 11 | } 12 | 13 | /* Check for a Google permissions checkbox and check the checkbox and click OK */ 14 | /* Doesn't really work */ 15 | function checkBox(){ 16 | 17 | var checkbox = document.getElementsByClassName("VfPpkd-muHVFf-bMcfAe"); 18 | if (checkbox.length > 0){ 19 | console.log("Checkbox found"); 20 | setTimeout(checkboxClick, 200, checkbox[0], checkboxOK); 21 | } 22 | else{ 23 | console.log("erasure: no checkbox"); 24 | } 25 | } -------------------------------------------------------------------------------- /development/fb.js: -------------------------------------------------------------------------------- 1 | /* 2 | This script will delete Facebook comments, but gets 3 | blocked after deleting a certain number. To run it, go to your 4 | Activity Log, open the filter, select "Comments", and save. 5 | Then paste this script into your javascript console and run it. 6 | */ 7 | const PAUSE = 2000; 8 | const RELOAD = 5000; 9 | clik = () => { 10 | var s = document.querySelectorAll('[data-pagelet="root"]'); 11 | try{ 12 | s[2].getElementsByTagName('i')[0].click(); 13 | } 14 | catch{ 15 | setTimeout(()=>{ 16 | s[2].getElementsByTagName('i')[0].click(); 17 | }, 1500) 18 | } 19 | } 20 | 21 | // creates a list of all comments on the screen 22 | getList = () => { 23 | var n = document.querySelectorAll('[aria-label="Action options"]'); 24 | return n; 25 | } 26 | 27 | queryErrorCheck = () => { 28 | let e = document.querySelectorAll('[aria-label="Query Error"]'); 29 | if (e.length >= 1){ 30 | return true; 31 | } 32 | else{ 33 | return false; 34 | } 35 | } 36 | 37 | //check for available comments 38 | commentsAvailable = () => { 39 | let comments = document.querySelectorAll('[aria-label="Action options"]'); 40 | if (comments.length >= 1){ 41 | return true; 42 | } 43 | else { 44 | return false; 45 | } 46 | } 47 | 48 | // main function 49 | facebookCommentDelete = (i) => { 50 | // handle an error box 51 | if (queryErrorCheck()){ 52 | let r = document.querySelectorAll('[aria-label="OK"]'); 53 | r[0].click(); 54 | i = i + 1; 55 | setTimeout(() => { 56 | facebookCommentDelete(i); 57 | }, 5000) 58 | } 59 | if (commentsAvailable()){ 60 | var myList = getList(); 61 | myList[i].click(); 62 | setTimeout(()=>{ 63 | clik(); // click the delete button 64 | }, PAUSE); 65 | if (myList.length > 0){ 66 | setTimeout(()=>{ 67 | facebookCommentDelete(i); // recursive call 68 | }, PAUSE); 69 | } 70 | else{ console.log("erasure: attempting to retry in 3000 ms"); 71 | setTimeout(()=>{ 72 | facebookCommentDelete(i); // recursive call after waiting 73 | },RELOAD); 74 | } 75 | } 76 | else{ 77 | console.log("Erasure completed, exiting."); 78 | } 79 | } 80 | 81 | // try starting at 0, if it doesn't work try a bigger number 82 | facebookCommentDelete(0); 83 | -------------------------------------------------------------------------------- /development/fb_scraps.js: -------------------------------------------------------------------------------- 1 | // messenger unsend script 2 | let p = document.getElementsByClassName("oajrlxb2 gs1a9yip g5ia77u1 mtkw9kbi tlpljxtp qensuy8j ppp5ayq2 goun2846 ccm00jje s44p3ltw mk2mc5f4 rt8b4zig n8ej3o3l agehan2d sk4xxmp2 rq0escxv nhd2j8a9 j83agx80 mg4g778l btwxx1t3 pfnyh3mw p7hjln8o kvgmc6g5 cxmmr5t8 oygrvhab hcukyx3x tgvbjcpo hpfvmrgz jb3vyjys rz4wbd8a qt6c0cv9 a8nywdso l9j0dhe7 i1ao9s8h esuyzwwr f1sip0of du4w35lb lzcic4wl abiwlrkh p8dawk7l mkd47r93 rgmg9uty"); 3 | p[p.length-2].click() 4 | let x = document.getElementsByClassName("p1ueia1e pgctjfs5 l8rlqa9s sh06z9xi i2p6rm4e"); 5 | x[2].click() 6 | let r = document.querySelectorAll('[aria-label="Remove message"]'); 7 | r[0].click(); 8 | let y = document.querySelectorAll("[aria-label='Remove']"); 9 | y[0].click() 10 | // x 11 | 12 | 13 | 14 | // these commands delete a facebook comment 15 | var m = document.querySelectorAll('[aria-label="Action options"]'); 16 | m[0].click(); 17 | var s = document.querySelectorAll('[data-pagelet="root"]'); 18 | s[2].getElementsByTagName('i')[0].click() 19 | 20 | 21 | 22 | var t = Array.from(document.querySelectorAll('div')) 23 | .find(el => el.textContent === 'Activity Log'); 24 | 25 | 26 | sortByComments = () => { 27 | 28 | let h = document.getElementsByClassName('emxnvquj ni8dbmo4 tr9rh885 ciko2aqh'); 29 | let n = h[0].querySelectorAll('*[data-visualcompletion]'); 30 | let a = n[20]; 31 | let v = a.querySelectorAll("[class='bp9cbjyn j83agx80 btwxx1t3']"); 32 | v[0].click(); 33 | let save = document.querySelectorAll("[aria-label='Save Changes']"); 34 | save[0].click(); 35 | } 36 | 37 | openFilter = () => { 38 | var b = document.getElementsByClassName('j83agx80 pfnyh3mw'); 39 | b[23].firstElementChild.click(); 40 | setTimeout(() =>{ 41 | sortByComments(); 42 | }, 8000) 43 | } 44 | 45 | -------------------------------------------------------------------------------- /development/live_chats.js: -------------------------------------------------------------------------------- 1 | // try 0, then try increasing values. this is delay between comment deletions 2 | var DELAY = 0; 3 | 4 | // if script ends but yt has more comments loading then increase this pause value. 5 | // this provides 1 retry attempt between list updates. useful for slow cpu/network. 6 | // (electronoob: 800 was the ideal value for my machine) 7 | var PAUSE = 1000; 8 | 9 | //second delete button 10 | function confirmClick(callback3) { 11 | try{ 12 | document.getElementsByClassName("yt-simple-endpoint style-scope yt-button-renderer")[1].click(); 13 | } 14 | catch{ 15 | document.getElementsByClassName("style-scope yt-button-renderer style-text size-default")[1].click(); 16 | } 17 | setTimeout(callback3, DELAY); 18 | } 19 | 20 | //first delete button 21 | function itemClick(callback2, callback3) { 22 | try { 23 | document.getElementsByClassName("yt-simple-endpoint style-scope ytd-menu-navigation-item-renderer")[1].click(); 24 | } 25 | catch{ 26 | document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[1].click(); 27 | } 28 | setTimeout(callback2, DELAY, callback3); 29 | } 30 | 31 | //open edit/delete menu 32 | function listClick(element, callback1, callback2, callback3) { 33 | element.click(); 34 | setTimeout(callback1, DELAY, callback2, callback3); 35 | } 36 | 37 | //check for available comments 38 | function commentsAvailable () { 39 | for(x of document.getElementsByTagName("ytd-live-chat-history-entry-renderer")) { 40 | if(x.getAttribute("is-dismissed") == null) { 41 | return true; 42 | } 43 | } 44 | return false; 45 | } 46 | 47 | function doOne(i) { 48 | if(commentsAvailable()) { 49 | var myList = document.getElementsByClassName("style-scope yt-button-renderer style-default size-default"); 50 | listClick(myList[i], itemClick, function() { 51 | ++i; 52 | if (i < myList.length) { 53 | doOne(i); 54 | }else { 55 | console.log("erasure: attempting to retry in %s ms",PAUSE); 56 | setTimeout(()=>{ 57 | doOne(0); 58 | },PAUSE); 59 | } 60 | }); 61 | } else { 62 | console.log("erasure: there are no comments, exiting."); 63 | } 64 | } 65 | 66 | function checkboxClick(element, callbackcheck){ 67 | element.checked = true; 68 | setTimeout(callbackcheck, 200); 69 | } 70 | function checkboxOK(){ 71 | var c = document.getElementsByClassName("VfPpkd-LgbsSe VfPpkd-LgbsSe-OWXEXe-k8QpJ VfPpkd-LgbsSe-OWXEXe-dgl2Hf nCP5yc AjY5Oe DuMIQc"); 72 | c[0].click(); 73 | } 74 | 75 | /* Check for a Google permissions checkbox and check the checkbox and click OK */ 76 | function checkBox(){ 77 | 78 | var checkbox = document.getElementsByClassName("VfPpkd-muHVFf-bMcfAe"); 79 | if (checkbox.length > 0){ 80 | console.log("Checkbox found"); 81 | setTimeout(checkboxClick, 200, checkbox[0], checkboxOK); 82 | } 83 | else{ 84 | console.log("erasure: no checkbox"); 85 | } 86 | } 87 | 88 | //check for available comments in new YouTube Comment History page 89 | function commentsAvailable2 () { 90 | var m = document.getElementsByClassName("YxbmAc"); 91 | if(m.length > 0){ 92 | console.log('erasure: comments are available.'); 93 | return true; 94 | } 95 | return false; 96 | } 97 | 98 | /* Function for new YouTube comments history page */ 99 | function newOne() { 100 | if (commentsAvailable2()) { 101 | var elements_coll = document.getElementsByClassName("YxbmAc"); 102 | Array.from(elements_coll).forEach(myFunc); 103 | console.log("erasure: attempting to retry in %s ms",PAUSE); 104 | setTimeout(()=>{ 105 | newOne(); 106 | },PAUSE); 107 | } 108 | else { 109 | console.log("erasure: there are no more comments, exiting."); 110 | } 111 | } 112 | 113 | var myFunc = function(item, index){ 114 | checkBox(); 115 | try{ 116 | item.querySelectorAll(".VfPpkd-rymPhb-pZXsl")[1].click(); 117 | } 118 | catch{ 119 | item.querySelectorAll(".VfPpkd-Bz112c-LgbsSe")[0].click(); 120 | } 121 | } 122 | 123 | /* Determine which YouTube Comment History page is being displayed */ 124 | function wrapper(){ 125 | if (commentsAvailable()){ 126 | doOne(0); 127 | } 128 | if (commentsAvailable2()){ 129 | newOne(); 130 | } 131 | } 132 | 133 | wrapper(); 134 | -------------------------------------------------------------------------------- /development/scraps.js: -------------------------------------------------------------------------------- 1 | // This is the old script before the YouTube comment page was changed. 2 | // Keeping it for reference 3 | 4 | 5 | 6 | // try 0, then try increasing values. this is delay between comment deletions 7 | var DELAY = 0; 8 | 9 | // if script ends but yt has more comments loading then increase this pause value. 10 | // this provides 1 retry attempt between list updates. useful for slow cpu/network. 11 | // (electronoob: 800 was the ideal value for my machine) 12 | var PAUSE = 1000; 13 | 14 | var myList = document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer"); 15 | 16 | 17 | //second delete button 18 | function confirmClick(callback3) { 19 | try{ 20 | document.getElementsByClassName("yt-simple-endpoint style-scope yt-button-renderer")[1].click(); 21 | } 22 | catch{ 23 | document.getElementsByClassName("style-scope yt-button-renderer style-text size-default")[1].click(); 24 | } 25 | setTimeout(callback3, DELAY); 26 | } 27 | 28 | //first delete button 29 | function itemClick(callback2, callback3) { 30 | try { 31 | document.getElementsByClassName("yt-simple-endpoint style-scope ytd-menu-navigation-item-renderer")[1].click(); 32 | } 33 | catch{ 34 | document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[1].click(); 35 | } 36 | setTimeout(callback2, DELAY, callback3); 37 | } 38 | 39 | //open edit/delete menu 40 | function listClick(element, callback1, callback2, callback3) { 41 | element.click(); 42 | setTimeout(callback1, DELAY, callback2, callback3); 43 | } 44 | 45 | //check for available comments 46 | function commentsAvailable () { 47 | for(x of document.getElementsByTagName("ytd-comment-history-entry-renderer")) { 48 | if(x.getAttribute("is-dismissed") == null) { 49 | return true; 50 | } 51 | } 52 | return false; 53 | } 54 | 55 | function doOne(i) { 56 | if(commentsAvailable()) { 57 | listClick(myList[i], itemClick, confirmClick, function() { 58 | ++i; 59 | if (i < myList.length) { 60 | doOne(i); 61 | }else { 62 | console.log("erasure: attempting to retry in %s ms",PAUSE); 63 | setTimeout(()=>{ 64 | doOne(0); 65 | },PAUSE); 66 | } 67 | }); 68 | } else { 69 | console.log("erasure: there are no comments, exiting."); 70 | } 71 | } 72 | 73 | doOne(0); 74 | --------------------------------------------------------------------------------