Lea 2024-02-23 23:17:19 +01:00
commit a91c82de94
Signed by: Lea
GPG Key ID: 1BAFFE8347019C42
9 changed files with 343 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules
.env

45
delete.js Normal file
View File

@ -0,0 +1,45 @@
require("dotenv").config();
const axios = require("axios");
const outputKeyword = require("./output_keyword.json");
const outputBlurhash = require("./output-blurhash.json");
const outputFileNotes = require("./output-file_notes.json");
let noteIds = [
...(outputKeyword.map(i => i.id)),
...(outputFileNotes.map(i => i.id)),
];
noteIds = noteIds.filter((item, index) => noteIds.indexOf(item) == index); // Deduplicate
const fileIds = outputBlurhash.map(i => i.id);
(async () => {
console.log("Deleting notes");
let res = await Promise.allSettled(noteIds.map(id =>
axios.post(
"https://lea.pet/api/notes/delete",
JSON.stringify({ noteId: id }),
{
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.API_TOKEN}`
},
},
)
));
console.log(`Done, ${res.filter((r) => r.status == "rejected")} errors`);
console.log("Deleting files");
res = await Promise.allSettled(fileIds.map(id =>
axios.post(
"https://lea.pet/api/drive/files/delete",
JSON.stringify({ fileId: id }),
{
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.API_TOKEN}`
},
},
)
));
console.log(`Done, ${res.filter((r) => r.status == "rejected")} errors`);
})();

1
output-blurhash.json Normal file
View File

@ -0,0 +1 @@
[]

1
output-file_notes.json Normal file
View File

@ -0,0 +1 @@
[]

1
output_keyword.json Normal file
View File

@ -0,0 +1 @@
[]

17
package.json Normal file
View File

@ -0,0 +1,17 @@
{
"name": "lea.pet-spam-cleanup",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^1.6.7",
"dotenv": "^16.4.5",
"pg": "^8.11.3"
}
}

189
pnpm-lock.yaml Normal file
View File

@ -0,0 +1,189 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
axios:
specifier: ^1.6.7
version: 1.6.7
dotenv:
specifier: ^16.4.5
version: 16.4.5
pg:
specifier: ^8.11.3
version: 8.11.3
packages:
/asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: false
/axios@1.6.7:
resolution: {integrity: sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==}
dependencies:
follow-redirects: 1.15.5
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
dev: false
/buffer-writer@2.0.0:
resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==}
engines: {node: '>=4'}
dev: false
/combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
dependencies:
delayed-stream: 1.0.0
dev: false
/delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dev: false
/dotenv@16.4.5:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'}
dev: false
/follow-redirects@1.15.5:
resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
dev: false
/form-data@4.0.0:
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
engines: {node: '>= 6'}
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: false
/mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
dev: false
/mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
dependencies:
mime-db: 1.52.0
dev: false
/packet-reader@1.0.0:
resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==}
dev: false
/pg-cloudflare@1.1.1:
resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==}
requiresBuild: true
dev: false
optional: true
/pg-connection-string@2.6.2:
resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==}
dev: false
/pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
dev: false
/pg-pool@3.6.1(pg@8.11.3):
resolution: {integrity: sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==}
peerDependencies:
pg: '>=8.0'
dependencies:
pg: 8.11.3
dev: false
/pg-protocol@1.6.0:
resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
dev: false
/pg-types@2.2.0:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
engines: {node: '>=4'}
dependencies:
pg-int8: 1.0.1
postgres-array: 2.0.0
postgres-bytea: 1.0.0
postgres-date: 1.0.7
postgres-interval: 1.2.0
dev: false
/pg@8.11.3:
resolution: {integrity: sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==}
engines: {node: '>= 8.0.0'}
peerDependencies:
pg-native: '>=3.0.1'
peerDependenciesMeta:
pg-native:
optional: true
dependencies:
buffer-writer: 2.0.0
packet-reader: 1.0.0
pg-connection-string: 2.6.2
pg-pool: 3.6.1(pg@8.11.3)
pg-protocol: 1.6.0
pg-types: 2.2.0
pgpass: 1.0.5
optionalDependencies:
pg-cloudflare: 1.1.1
dev: false
/pgpass@1.0.5:
resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
dependencies:
split2: 4.2.0
dev: false
/postgres-array@2.0.0:
resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
engines: {node: '>=4'}
dev: false
/postgres-bytea@1.0.0:
resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==}
engines: {node: '>=0.10.0'}
dev: false
/postgres-date@1.0.7:
resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
engines: {node: '>=0.10.0'}
dev: false
/postgres-interval@1.2.0:
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
engines: {node: '>=0.10.0'}
dependencies:
xtend: 4.0.2
dev: false
/proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
dev: false
/split2@4.2.0:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
dev: false
/xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
dev: false

7
run.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
node ./scan.js
node ./delete.js
mkdir -p "backup/$(date +'%Y-%m-%d %H-%M_%S')"
cp output-*.json "backup/$(date +'%Y-%m-%d %H-%M_%S')/"

80
scan.js Normal file
View File

@ -0,0 +1,80 @@
require("dotenv").config();
const { Client } = require("pg");
const fs = require("fs");
const KEYWORD_MATCHES = [
"https://xn--68j5e377y.com",
"https://荒らし.com",
"https://ctkpaarr.org",
"https://discord.gg/ctkpaarr",
];
const BLURHASHES = [
"elJ+3xk9Oat6NgtKjcV[aeo0}7agoJofj?xabYkTj@j=p_jcsmWBow",
"eRQ0Hyrq.8IV%MVD4T%gxuIUrqD%ozxuRj~q?bM{M{%M~qxuM{t6bH",
];
(async () => {
const client = new Client({
host: process.env.PG_HOST,
port: process.env.PG_PORT,
user: process.env.PG_USER,
password: process.env.PG_PASS,
database: process.env.PG_DATABASE,
});
await client.connect();
console.log("Searching for suspicious profiles");
let res = await client.query(
`SELECT note.*, "user".username, "user".host
FROM note
INNER JOIN "user" ON "user"."id" = note."userId"
WHERE LENGTH("user".username) = 10
AND "user".username = LOWER("user".username)
AND "user"."avatarId" IS NULL
AND "user"."bannerId" IS NULL
AND "user"."followingCount" = 0`
);
console.log("Query returned " + res.rowCount + " rows");
const filtered = res.rows.filter((row) => {
for (const keyword of KEYWORD_MATCHES) {
if (row.text?.includes(keyword)) return true;
}
return false;
});
console.log("Found " + filtered.length + " rows after filtering");
fs.writeFileSync("output_keyword.json", JSON.stringify(filtered));
console.log("Searching for blurhash matches");
res = await client.query(
`SELECT drive_file.*
FROM drive_file
WHERE drive_file.blurhash = ANY($1::text[])`,
[BLURHASHES]
);
console.log("Found " + res.rowCount + " files matching blurhash");
fs.writeFileSync("output-blurhash.json", JSON.stringify(res.rows, null, 4));
console.log("Searching for notes using found files");
const notes = await client.query(
`SELECT *
FROM note
WHERE note."fileIds"::text[] && $1::text[]`,
[res.rows.map((row) => `${row.id}`)]
);
console.log("Found " + notes.rowCount + " notes matching files");
fs.writeFileSync("output-file_notes.json", JSON.stringify(notes.rows, null, 4));
await client.end();
})();