-
Notifications
You must be signed in to change notification settings - Fork 5
/
script.js
205 lines (149 loc) · 6.03 KB
/
script.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
clear();
let debug = true;
let delay = ms => {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
let findAllPosts = function () {
let posts = document.querySelectorAll("[aria-label='Actions for this post']");
return posts;
};
let filterFirstXPosts = function (posts, numberToKeep) {
posts = Array.from(posts);
let keeping = posts.slice(0, numberToKeep);
console.log("Keeping");
console.table(keeping, ["date", "author", "text"]);
return posts.slice(numberToKeep);
}
let constructDataContainer = function (posts) {
let c = [];
for (post of posts) {
try {
let p2 = post.parentNode.parentNode; // Step up two levels to container, this holds container of three dots
let p3 = p2.previousSibling; // Step to sibling above current element, this holds author name and date
let links = p3.getElementsByTagName('a');
let author = "";
let date = "";
let firstText = "";
author = links[0].innerText ?? '';
date = links[1].ariaLabel ?? '';
if (author.length < 1) {
console.error("Found invalid post with no author", links);
}
firstText = p2.parentNode.parentNode.parentNode.children[2].children[0].children[0].children[0].children[0].children[0].innerText;
let o = {node: post, author: author, date: date, text: firstText};
c.push(o);
} catch (e) {
console.log(e);
}
}
return c;
}
let deletePost = async function (domNode) {
if (debug) console.log("Deleting post", domNode);
domNode.scrollIntoView(true);
window.scrollBy(0,-250);
await delay(50);
domNode.click();
if (debug) console.log("Waiting for remove button");
let removeButtons = [];
let i = 0;
while (removeButtons.length < 1) {
await delay(200);
removeButtons = [].slice.call(document.querySelectorAll("span")).filter(a => a.textContent === "Remove post");
i++;
if (i > 30) {
break;
}
}
if (removeButtons.length !== 1) {
console.error("Could not find Remove Post button");
return true;
}
if (debug) console.log("Found remove button", "iterations", i, removeButtons);
removeButtons[0].click();
removeButtons[0].scrollIntoView(true);
window.scrollBy(0,-250);
await delay(50);
if (debug) console.log("Waiting for Confirm button");
let confirmDialogs = [];
i = 0;
while (confirmDialogs.length < 1) {
await delay(1000);
confirmDialogs = document.querySelectorAll("div[aria-label='Confirm']");
i++;
if (i > 30) {
console.log("Breaking");
break;
}
}
if (confirmDialogs.length !== 1) {
console.error("Could not find Confirm dialog", confirmDialogs);
return true;
}
let confirmButton = confirmDialogs[0].parentNode.querySelectorAll("div[aria-label='Confirm']");
if (confirmButton.length !== 1) {
console.error("Could not find Confirm button", confirmDialogs);
return true;
}
if (debug) console.log("Found Confirm button", "iterations", i, confirmDialogs);
confirmButton[0].click();
if (debug) console.log("Deleted successfully");
await delay(200);
}
let deletePosts = async function (posts) {
let i = 0;
let total = posts.length;
for (post of posts) {
i++;
console.log("Deleting post", post.date, post.author, "Completion:", (i*100/total) + "%");
await deletePost(post.node);
await delay(1500);
}
};
let scrollToBottom = async function (times) {
// Let's scroll to the bottom a few times to load some content
for (let i = 0; i < times; i++) {
window.scrollTo(0,document.body.scrollHeight);
await delay(1000);
console.log("Scrolling", (i*100/times) + "%")
}
if (debug) console.log("Done Scrolling");
};
let main = async function () {
let HowManyRecentPostsToKeep = 6;
let run = true;
// Comment this line if you just want to debug on the front page
await scrollToBottom((HowManyRecentPostsToKeep/2) + 2);
let i = 0;
while (run) {
let posts = findAllPosts();
if (debug) console.log("Found posts");
let c = constructDataContainer(posts);
let postsToDelete = filterFirstXPosts(c, HowManyRecentPostsToKeep);
if (debug) console.log("To delete", postsToDelete);
if (postsToDelete.length < 1 || i > 50) {
console.log("Done, reached limit", postsToDelete.length, i);
break;
}
await delay(3000); // So you can see the keeping table quickly
console.table("Deleting:", postsToDelete, ["date", "author", "text"]);
//deletePost(postsToDelete[3].node); // To delete a specific post, for testing only
//await deletePosts(postsToDelete); // Uncomment to this to delete all posts other that HowManyRecentPostsToKeep first
await scrollToBottom(4);
i++;
}
}
// First open Facebook in Google Chrome
// Then open up the Facebook group you want to delete posts in
// Then open up the Devtools by pressing F12
// Then go to tab Sources > Snippets > Create a new snippet
// Then Run this script (ctrl+enter) once and check that it works and the table you see in the console only contains posts you want to delete
// WARNING: YOU ARE RESPONSIBLE FOR ANY ACTIONS THIS SCRIPT MIGHT PERFORM
// WARNING: IF FACEBOOK CHANGES THEIR DESIGN IT MIGHT HAVE UNINTENDED CONSEQUENCES
// WARNING: NEVER RUN THIS SCRIPT PRIOR TO HAVING REVIEWED AND APPROVED THE SOURCE CODE THAT IT DOES EXACTLY WHAT YOU WANT IT TO DO
// WARNING: AUTHOR TAKES NOT RESPONSIBILITY AND IS NOT RESPONSIBLE IN ANY WAY
// WARNING: Then uncomment the //deletePosts() line to delete the posts and run this script again
// Monitor the script while running and restart the browser when it doesn't do any good any more, some posts can't be deleted via this script
main();