Skip to content

Instantly share code, notes, and snippets.

@aymkx
Last active July 10, 2021 12:59
Show Gist options
  • Save aymkx/b6e9d807d47294befdd7a72f480fb6bd to your computer and use it in GitHub Desktop.
Save aymkx/b6e9d807d47294befdd7a72f480fb6bd to your computer and use it in GitHub Desktop.
TweetDeckのActivityカラムにフィルターをかけるUserScript
"use strict";
class Article {
readonly dom: HTMLElement;
readonly userId: string = "";
readonly userName: string = "";
readonly screenName: string = "";
readonly tweetId?: string;
readonly tweetUrl?: string;
readonly timestamp?: Date;
readonly body?: string;
readonly activityType?: string;
readonly activityUserId?: string;
readonly activityUserName?: string;
readonly activityUserSN?: string;
constructor(ar: HTMLElement) {
this.dom = ar;
const acHeader = ar.querySelector("div.activity-header");
const splited = ar.dataset["key"]?.split("_");
if (splited !== undefined && splited.length > 1) {
this.activityType = splited[0];
if (this.activityType == "gap") {
throw new Error("argument is not article");
}
this.activityUserId = splited[1];
if (this.activityType === "follow") {
this.userId = splited[2];
}
}
for (const a of acHeader?.getElementsByTagName("a") || []) {
if (a.classList.contains("account-link")) {
const url = new URL(a.href);
this.activityUserSN = url.pathname.substring(1);
this.activityUserName = a.innerText;
}
}
if (ar.dataset["dragType"] === "tweet") {
this.tweetId = ar.dataset["tweetId"];
const header = ar.querySelector("header");
const time = header?.getElementsByTagName("time");
if (time !== undefined && time.length > 0) {
this.timestamp = new Date(time[0].dateTime);
const a = time[0].querySelector("a");
if (a !== null) {
this.tweetUrl = a.href;
}
}
for (const a of header?.getElementsByTagName("a") || []) {
if (a.classList.contains("account-link")) {
const url = new URL(a.href);
this.screenName = url.pathname.substring(1);
}
}
this.userName =
header?.querySelector<HTMLElement>("b.fullname")?.innerText || "";
const body = ar.querySelector<HTMLElement>("div.tweet-body");
this.body = body?.innerText.trim();
const footer = ar.querySelector("footer");
for (const li of footer?.querySelectorAll("li.tweet-action-item") || []) {
for (const a of li.getElementsByTagName("a")) {
this.userId = a.dataset["userId"] || "";
}
}
} else {
const acSum = ar.querySelector<HTMLElement>("div.account-summary");
for (const a of acSum?.getElementsByTagName("a") || []) {
if (a.classList.contains("account-link")) {
const url = new URL(a.href);
this.screenName = url.pathname.substring(1);
}
}
this.userName =
acSum?.querySelector<HTMLElement>("b.fullname")?.innerText || "";
}
}
}
function isActivityColumn(c: Element): boolean {
if (c.className.includes("column")) {
for (const t of c.getElementsByClassName("column-title")) {
if (
t instanceof HTMLElement &&
t.innerText.toLowerCase().includes("activity")
) {
return true;
}
}
}
return false;
}
function getActivityColumn(): HTMLElement | undefined {
for (const s of document.getElementsByTagName("section")) {
if (isActivityColumn(s)) {
return s;
}
}
}
function isToBeHidden(article: Article): boolean {
const activity = "favorite";
const sn = "<screen_name>";
return article.activityType === activity && article.activityUserSN === sn;
}
async function filter(articleElement: HTMLElement) {
if (articleElement.dataset["key"]?.startsWith("gap_")) {
return;
}
const article = new Article(articleElement);
if (isToBeHidden(article) && article.dom.style.display !== "none") {
article.dom.style.display = "none";
console.log(article);
}
if (article.activityType !== "favorite") {
console.log(article);
}
}
function nodeFilter(n: Node): void {
if (n instanceof HTMLElement) {
if (n.tagName.toLowerCase() === "article") {
filter(n);
} else {
for (const a of n.getElementsByTagName("article")) {
filter(a);
}
}
}
}
const config = {
childList: true,
attributes: true,
subtree: true,
};
const mo = new MutationObserver((mutationList, observer) => {
for (const m of mutationList) {
switch (m.type) {
case "childList":
for (const n of m.addedNodes) {
if (
n instanceof Element &&
n.tagName.toLowerCase() === "section" &&
isActivityColumn(n)
) {
observer.disconnect();
observer.observe(n, config);
}
nodeFilter(n);
}
break;
case "attributes":
if (m.attributeName === "style") {
nodeFilter(m.target);
}
break;
}
}
});
mo.observe(getActivityColumn() || document, config);
@nannja
Copy link

nannja commented Jun 23, 2021

ฅ^•ﻌ•^ฅ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment