Skip to content

Instantly share code, notes, and snippets.

@DrayChou
Last active May 26, 2023 04:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DrayChou/b1f4b6445f5946d7136ff55da65d216a to your computer and use it in GitHub Desktop.
Save DrayChou/b1f4b6445f5946d7136ff55da65d216a to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name 抓取30天之前的推文ID
// @namespace dray
// @version 1.0
// @description 抓取 Twitter 我的推文页面中30天之前的所有推文的ID
// @author dray
// @match https://twitter.com/*
// @grant none
// @run-at document-start
// @require unsafe-inline
// @require unsafe-eval
// @connect *
// @run-at document-idle
// ==/UserScript==
(function () {
"use strict";
// 删除多少天之前的推文
var deletefromdays = 30;
// 获取推文元素
var tweetScreenNameIds = [];
// 存储页面高度
var scrollHeight = 0;
const getOwnerScreenName = () => {
var t1 = document
.querySelector('div[data-testid="UserName"]')
.innerText.trim();
if (t1.length < 1) {
return "";
}
var t2 = t1.split("@");
if (t2.length < 2) {
return "";
}
return t2[1];
};
const query30dayTweet = (ownerScreenName, next) => {
var tweets = [];
var tweetList = document.querySelector(
'section[aria-labelledby="accessible-list-1"]>div[aria-label]'
);
if (tweetList != null) {
tweets = tweetList.querySelectorAll('[data-testid="tweet"]');
}
if (tweets.length == 0) {
tweetList = document.querySelector(
'section[aria-labelledby="accessible-list-0"]>div[aria-label]'
);
if (tweetList != null) {
tweets = tweetList.querySelectorAll('[data-testid="tweet"]');
}
}
if (tweets.length == 0) {
console.log("tweets.length == 0");
return;
}
// 获取3天之前的时间戳(毫秒)
var thirtyDaysAgo =
new Date().getTime() - deletefromdays * 24 * 60 * 60 * 1000;
// 遍历推文元素,获取30天之前发布的推文ID
for (var tweet of tweets) {
// 获取推文发布时间
var time = tweet.querySelector("time").getAttribute("datetime");
// 将时间转换为时间戳(毫秒)
var timestamp = new Date(time).getTime();
// 如果推文发布时间早于30天之前,则将推文ID存入tweetScreenNameIds数组
if (timestamp < thirtyDaysAgo) {
// 获取推文ID
var url = tweet
.querySelector("time")
.parentElement.getAttribute("href");
// 从 url 中取得当前的 screen_name 和 当前的 tweetId
var screen_name = url.split("/")[1];
var tweetId = url.split("/")[3];
console.log(screen_name, tweetId);
tweetScreenNameIds.push([screen_name, tweetId]);
}
}
};
const delete30dayTweet = () => {
// 在 a 标签 href="/settings/profile" 的按钮旁边添加 删除30天之前推文 的按钮
var a = document.querySelector('a[href="/settings/profile"]');
if (a == null) {
alert("請先進入個人主頁面");
return;
}
// 获取推文列表容器元素
// 输出结果
console.log("tweetScreenNameIds.length", tweetScreenNameIds.length);
if (tweetScreenNameIds.length == 0) {
console.log("没有需要删除的推文了");
// 把页面滚动条滚动到最下面
window.scrollTo(0, document.body.scrollHeight);
setTimeout(function () {
// 如果窗口页面高度跟之前比较没有变化,那么认为已经加载完毕了,停止
if (scrollHeight == document.body.scrollHeight) {
console.log("已经到底了");
return;
}
scrollHeight = document.body.scrollHeight;
query30dayTweet();
setTimeout(function () {
delete30dayTweet();
}, 1000);
}, 1000);
return;
}
// 获取当前页面的用户名 screen_name
var owner_screen_name = getOwnerScreenName();
// 如果 tweetScreenNameIds 里有ID,那么弹出第一个元素
var kv = tweetScreenNameIds.shift();
var sName = kv[0];
var tId = kv[1];
var tweetUrl = document.querySelector(`a[href="/${sName}/status/${tId}"]`);
if (tweetUrl == null) {
console.log("tweet == null", sName, tId);
delete30dayTweet();
return;
}
// 备份这些文件到文本文件并下载到本地
// 创建需要下载的文本内容
const content =
tweetUrl.closest("article").innerText +
"\n\nhttps://twitter.com/" +
tweetUrl.href;
// 创建 Blob 对象
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
// 生成下载链接
const url = URL.createObjectURL(blob);
// 创建 a 标签用于下载
const link = document.createElement("a");
link.href = url;
link.download = sName + "-" + tId + ".txt";
// 触发点击事件
document.body.appendChild(link);
link.click();
// 释放 URL 对象
URL.revokeObjectURL(url);
// 如果是我自己的推文,那么删除
if (sName === owner_screen_name) {
// 找到 刪除 这个文字所在的 div 元素所在的删除按钮
var caret = tweetUrl
.closest("article")
.querySelector('div[data-testid="caret"]');
if (caret == null) {
console.log("caret == null", sName, tId);
delete30dayTweet();
return;
}
caret.click();
// 200 毫秒之后点击删除按钮
setTimeout(function () {
var menuitem = document.querySelector(
'div[role="menu"] div[role="menuitem"][tabindex="0"]'
);
if (menuitem == null) {
console.log("menuitem == null", sName, tId);
delete30dayTweet();
return;
}
menuitem.click();
//200 毫秒之后点击确认
setTimeout(function () {
var confirmationSheetConfirm = document.querySelector(
'div[data-testid="confirmationSheetConfirm"]'
);
if (confirmationSheetConfirm == null) {
console.log("confirmationSheetConfirm == null", sName, tId);
delete30dayTweet();
return;
}
confirmationSheetConfirm.click();
}, 200);
}, 200);
} else {
// 否则,找到 转推按钮,点击取消转推
var unretweet = tweetUrl
.closest("article")
.querySelector('div[data-testid="unretweet"]');
if (unretweet == null) {
console.log("unretweet == null", sName, tId);
delete30dayTweet();
return;
}
unretweet.click();
// 点击转推按钮
// 200 毫秒后,点击确认取消转推按钮
setTimeout(function () {
var unretweetConfirm = document.querySelector(
'div[role="menu"] div[data-testid="unretweetConfirm"]'
);
if (unretweetConfirm == null) {
console.log("unretweetConfirm == null", sName, tId);
delete30dayTweet();
return;
}
unretweetConfirm.click();
}, 200);
}
// 如果 tweetScreenNameIds 里有ID,那么回调他自己
setTimeout(function () {
delete30dayTweet();
}, 1000);
};
// 在页面加载完成后执行
window.onload = function () {
console.log("页面加载完成!");
// 延迟10秒执行下面的代码
setTimeout(function () {
// 执行添加按钮的代码
// 添加浮动悬浮窗,悬浮在右侧上下居中,里面是一个按钮,按钮名称是 删除30天之前推文
var div = document.createElement("div");
div.style.position = "fixed";
div.style.right = "0px";
div.style.top = "30%";
div.style.transform = "translateY(-70%)";
div.style.zIndex = "9999";
div.style.padding = "10px";
div.style.backgroundColor = "#1da1f2";
div.style.color = "white";
div.style.border = "none";
div.style.borderRadius = "5px";
div.style.fontSize = "16px";
div.style.fontWeight = "bold";
div.style.cursor = "pointer";
div.style.width = "140px";
// button 之前添加一个输入框,提示用户输入要删除多少天前的推文
div.innerHTML = `<input type="number" style="padding: 10px; border-radius: 5px; margin-bottom: 10px; width: 108px;" placeholder="请输入要删除多少天前的推文" /><br/><button style="padding: 10px; background-color: #1da1f2; color: white; border-radius: 5px; font-size: 16px;">删除之前的推文</button>`;
div.querySelector("button").onclick = delete30dayTweet;
// input 框的内容如果发生改变,那么修改全局变量 deletefromdays 的值
div.querySelector("input").onchange = function (e) {
// 转为 int 数字
deletefromdays = parseInt(e.target.value);
};
div.querySelector("input").value = deletefromdays;
document.body.appendChild(div);
console.log("添加按钮完成!");
}, 3000);
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment