Initial push (copy from private UserScripts)
Some checks are pending
Node.js CI / build (push) Waiting to run

This commit is contained in:
Nicholas Phillips 2024-08-02 10:24:49 -04:00
commit 81b336ff1a
13 changed files with 3586 additions and 0 deletions

8
.babelrc Normal file
View file

@ -0,0 +1,8 @@
{
"presets": [
"@babel/preset-env"
],
"plugins": [
["inline-json", {}]
]
}

View file

@ -0,0 +1,46 @@
name: Node.js CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: "0 0 */1 * *" # Every day at midnight UTC
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '21.5.0'
- name: Install dependencies
run: npm install
- name: Build project
run: npm run build
- name: Temporarily allow build directory
run: |
sed -i '/^build\//d' .gitignore
- name: Stage build artifacts
run: |
git add build/
git restore --staged .gitignore
- name: Commit and push changes
run: |
git config --global user.name 'forgejo-actions[bot]'
git config --global user.email 'forgejo-actions[bot]@forgejo.localhost'
git commit -m 'Build and commit changes'
git push
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules
build/

4
.prettierrc Normal file
View file

@ -0,0 +1,4 @@
{
"tabWidth": 2,
"useTabs": false
}

197
build.js Normal file
View file

@ -0,0 +1,197 @@
const fs = require("fs");
const path = require("path");
const http = require("http");
const https = require("https");
const { execSync } = require("child_process");
const srcDir = "src";
const outDir = "build";
const downloads = [
{
url: "https://coomer.su/api/v1/creators.txt",
out: path.join(srcDir, "coomer.su.json"),
dont_redownload: true,
after: function (pathd) {
let data = fs.readFileSync(pathd, "utf8");
let creators = JSON.parse(data);
let newCreators = {};
for (let i = 0; i < creators.length; i++) {
/*
{"id":"carbcollector","name":"carbcollector","service":"onlyfans","indexed":1703401003,"updated":1705076060,"favorited":19}
*/
let creator = creators[i];
let id = creator.id;
let service = creator.service;
let name = creator.name;
let creatorObj = {
id: id,
name: name,
};
if (newCreators[service] === undefined) {
newCreators[service] = [];
}
newCreators[service].push(creatorObj);
}
let ret = JSON.stringify(newCreators);
fs.writeFileSync(path.join(srcDir, "coomer.su-modded.json"), ret);
console.log(
"coomer.su-modded.json is " +
(data.length - ret.length) +
" smaller, roughly " +
Math.round(((data.length - ret.length) / data.length) * 100) +
"%"
);
},
},
{
url: "https://kemono.su/api/v1/creators.txt",
out: path.join(srcDir, "kemono.su.json"),
dont_redownload: true,
after: function (pathd) {
let data = fs.readFileSync(pathd, "utf8");
let creators = JSON.parse(data);
let newCreators = {};
for (let i = 0; i < creators.length; i++) {
/*
{"id":"carbcollector","name":"carbcollector","service":"onlyfans","indexed":1703401003,"updated":1705076060,"favorited":19}
*/
let creator = creators[i];
let id = creator.id;
let service = creator.service;
if (service === "discord") continue;
let name = creator.name;
let creatorObj = {
id: id,
name: name,
};
if (newCreators[service] === undefined) {
newCreators[service] = [];
}
newCreators[service].push(creatorObj);
}
let ret = JSON.stringify(newCreators);
fs.writeFileSync(path.join(srcDir, "kemono.su-modded.json"), ret);
console.log(
"kemono.su-modded.json is " +
(data.length - ret.length) +
" smaller, roughly " +
Math.round(((data.length - ret.length) / data.length) * 100) +
"%"
);
},
},
];
function getRemoteFile(file, url) {
return new Promise((resolve, reject) => {
let localFile = fs.createWriteStream(file);
const client = url.startsWith("https") ? https : http;
const request = client.get(url, function (response) {
// var len = parseInt(response.headers['content-length'], 10);
// var cur = 0;
// var total = len / 1048576; //1048576 - bytes in 1 Megabyte
// response.on('data', function(chunk) {
// cur += chunk.length;
// showProgress(file, cur, len, total);
// });
response.on("end", function () {
console.log("[" + url + "] Download complete => " + file);
resolve();
});
response.pipe(localFile);
localFile.on("finish", () => localFile.close());
localFile.on("error", (err) => {
fs.unlink(file); // Delete the file async if there's an error
reject(err);
});
});
request.on("error", (err) => {
reject(err);
});
});
}
async function downloadFiles() {
for (let i = 0; i < downloads.length; i++) {
let download = downloads[i];
let file = download.out;
let url = download.url;
if (download.dont_redownload) {
if (fs.statSync(file).isFile()) {
continue;
}
}
await getRemoteFile(file, url);
download.after(file);
}
}
async function buildScript(scriptPath) {
const scriptName = path.basename(scriptPath, ".user.js");
const metaPath = path.join(srcDir, `${scriptName}.meta.js`);
const outPath = path.join(outDir, `${scriptName}.user.js`);
const tempPath = path.join(outDir, `${scriptName}.user.temp.js`);
const minifiedPath = path.join(outDir, `${scriptName}.min.user.js`);
if (!fs.existsSync(metaPath)) {
console.error(`Meta file not found: ${metaPath}`);
return;
}
console.log(`Compiling ${scriptPath}`);
// Compile the script with Babel
execSync(`npx babel ${scriptPath} -o ${tempPath}`);
// Read and prepend the meta content
let metaContentOriginal = fs
.readFileSync(metaPath, "utf8")
.replace(/{{UNIXDATE}}/gim, Date.now());
let metaContent = metaContentOriginal.replace(
/{{FILE_URL}}/gim,
`https://git.bowu.dev/bowu/UserScripts/raw/branch/main/build/${scriptName}.user.js`
);
let metaMiniContent = metaContentOriginal.replace(
/{{FILE_URL}}/gim,
`https://git.bowu.dev/bowu/UserScripts/raw/branch/main/build/${scriptName}.min.user.js`
);
const scriptContent = fs.readFileSync(tempPath, "utf8");
fs.writeFileSync(outPath, `${metaContent}\n${scriptContent}`);
console.log(`Built: ${outPath}`);
// Minify the script while preserving comments in the meta file
const Terser = require("terser");
const minifiedResult = await Terser.minify(scriptContent, {
output: {
comments: false, // Do not keep comments in the minified output
},
});
if (minifiedResult.error) {
console.error(`Error minifying script: ${minifiedResult.error}`);
return;
}
// Prepend the meta content to the minified script
fs.writeFileSync(minifiedPath, `${metaMiniContent}\n${minifiedResult.code}`);
console.log(`Minified: ${minifiedPath}`);
fs.unlinkSync(tempPath);
}
function buildAll() {
const files = fs.readdirSync(srcDir);
files.forEach((file) => {
if (file.endsWith(".user.js")) {
buildScript(path.join(srcDir, file));
}
});
}
(async function () {
await downloadFiles();
buildAll();
})();

3016
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

25
package.json Normal file
View file

@ -0,0 +1,25 @@
{
"name": "userscripts",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "node build.js",
"watch": "chokidar 'src/**/*.js' -c 'npm run build'"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/cli": "^7.24.8",
"@babel/core": "^7.24.9",
"@babel/preset-env": "^7.25.0",
"babel-plugin-inline-json": "^2.1.0",
"babel-plugin-inline-json-import": "^0.3.2",
"chokidar-cli": "^3.0.0"
},
"dependencies": {
"babel-plugin-import-json-value": "^0.1.2",
"terser": "^5.31.3"
}
}

File diff suppressed because one or more lines are too long

1
src/coomer.su.json Normal file

File diff suppressed because one or more lines are too long

17
src/free-porn.meta.js Normal file
View file

@ -0,0 +1,17 @@
// ==UserScript==
// @name 🦜 We like free things / PORN edition
// @namespace Bowud Scripts
// @match https://boosty.to/*
// @match https://candfans.jp/*
// @match https://fansly.com/*
// @match https://fantia.jp/*
// @match https://onlyfans.com/*
// @match https://patreon.com/*
// @match https://www.patreon.com/*
// @match https://*.gumroad.com/*
// @match https://*.fanbox.cc/*
// @match https://subscribestar.adult/*
// @grant none
// @downloadURL {{FILE_URL}}
// @version {{UNIXDATE}}
// ==/UserScript==

267
src/free-porn.user.js Normal file
View file

@ -0,0 +1,267 @@
(function (coomerCreators, kemonoCreators) {
const coomerDomain = "coomer.su";
const kemonoDomain = "kemono.su";
const config = {
creators: {
...coomerCreators,
...kemonoCreators,
},
domains: {
"onlyfans.com": [coomerDomain, "onlyfans"],
"fansly.com": [coomerDomain, "fansly"],
"candfans.jp": [coomerDomain, "candfans"],
"patreon.com": [kemonoDomain, "patreon"],
"fantia.jp": [kemonoDomain, "fantia"],
"boosty.to": [kemonoDomain, "boosty"],
"gumroad.com": [kemonoDomain, "gumroad"],
"subscribestar.adult": [kemonoDomain, "subscribestar"],
"fanbox.cc": [kemonoDomain, "fanbox"],
},
handlers: {
onlyfans: () => {
console.log("[🦜] onlyfans called");
},
fansly: () => {
console.log("[🦜] fansly called");
},
candfans: () => {
console.log("[🦜] candfans called");
// Find an image with a similar url to this
// data-src="https://image.candfans.jp/user/154310/profile/i0YW0NNR864lVp7s7dHYhfg25rqJgeLx2ulzAEMG.png"
let imgs = document.querySelectorAll("img[data-src]");
let pattern =
/https:\/\/image\.candfans\.jp\/user\/(\d+)\/profile\/.*\.\w+/i;
let id;
for (let i = 0; i < imgs.length; i++) {
let img = imgs[i];
let src = img.getAttribute("data-src");
let match = src.match(pattern);
if (match) {
id = match[1];
break;
}
}
if (!id) return;
let dd = figureDomain();
let dCreators = config.creators[dd[1]];
for (const key in dCreators) {
if (Object.hasOwnProperty.call(dCreators, key)) {
const creator = dCreators[key];
if (creator.id === id) {
clearInterval(looper);
let dest = locationGen(dd[0], dd[1], id);
if (confirm("Redirecting to " + dest)) {
location.href = dest;
}
break;
}
}
}
},
patreon: () => {
console.log("[🦜] patreon called");
let parsed = new URL(location.href);
let id = parsed.searchParams.get("u");
let dd = figureDomain();
let dCreators = config.creators[dd[1]];
for (const key in dCreators) {
if (Object.hasOwnProperty.call(dCreators, key)) {
const creator = dCreators[key];
if (creator.id === id) {
clearInterval(looper);
let dest = locationGen(dd[0], dd[1], id);
if (confirm("Redirecting to " + dest)) {
location.href = dest;
}
break;
}
}
}
},
fantia: () => {
console.log("[🦜] fantia called");
// https://fantia.jp/fanclubs/83679
let parsed = new URL(location.href);
let id = parsed.pathname.split("/")[2];
clearInterval(looper);
let dd = figureDomain();
let dest = locationGen(dd[0], dd[1], id);
if (confirm("Redirecting to " + dest)) {
location.href = dest;
}
},
boosty: () => {
console.log("[🦜] boosty called");
// https://boosty.to/temikmiu
let parsed = new URL(location.href);
let id = parsed.pathname.split("/")[1];
clearInterval(looper);
let dd = figureDomain();
let dCreators = config.creators[dd[1]];
for (const key in dCreators) {
if (Object.hasOwnProperty.call(dCreators, key)) {
const creator = dCreators[key];
if (creator.id === id) {
clearInterval(looper);
let dest = locationGen(dd[0], dd[1], id);
if (confirm("Redirecting to " + dest)) {
location.href = dest;
}
break;
}
}
}
},
gumroad: () => {
console.log("[🦜] gumroad called");
let id = JSON.parse(
document.querySelector(`script[data-component-name="Profile"]`)
.textContent
).creator_profile.external_id;
clearInterval(looper);
let dd = figureDomain();
let dCreators = config.creators[dd[1]];
for (const key in dCreators) {
if (Object.hasOwnProperty.call(dCreators, key)) {
const creator = dCreators[key];
if (creator.id === id) {
clearInterval(looper);
let dest = locationGen(dd[0], dd[1], id);
if (confirm("Redirecting to " + dest)) {
location.href = dest;
}
break;
}
}
}
},
subscribestar: () => {
console.log("[🦜] subscribestar called");
// https://subscribestar.adult/slugbox
let parsed = new URL(location.href);
let id = parsed.pathname.split("/")[1];
clearInterval(looper);
let dd = figureDomain();
let dCreators = config.creators[dd[1]];
for (const key in dCreators) {
if (Object.hasOwnProperty.call(dCreators, key)) {
const creator = dCreators[key];
if (creator.id === id) {
clearInterval(looper);
let dest = locationGen(dd[0], dd[1], id);
if (confirm("Redirecting to " + dest)) {
location.href = dest;
}
break;
}
}
}
},
fanbox: () => {
console.log("[🦜] fanbox called");
// <script type="application/ld+json">[{"@context":"http:\/\/schema.org","@type":"CollectionPage","author":{"@type":"Person","name":"M\u3060S\u305f\u308d\u3046","url":"https:\/\/msmspc.fanbox.cc\/"},"description":"M\u3060S\u305f\u308d\u3046\u3067\u3059\u3002\u8910\u8272\u30a4\u30e9\u30b9\u30c8\u30ec\u30fc\u30bf\u30fc\u3068\u3057\u3066\u6d3b\u52d5\u3057\u3066\u304a\u308a\u307e\u3059\u3002\n\n\u3053\u3053\u3067\u306fR18 \u30e9\u30d5\u30fb\u4e0b\u66f8\u304d\u30fb\u3089\u304f\u304c\u304d\u30fb\u8584\u3044\u4fee\u6b63\u30fb\u540c\u4eba\u8a8c\u3092\u30d7\u30e9\u30f3\u5225\u306b\u898b\u3089\u308c\u307e\u3059\u3002\n\u4eca\u307e\u3067\u306b\u7121\u6148\u60b2\u306a\u4fee\u6b63\u3055\u308c\u305f\u300c\u8584\u3044\u4fee\u6b63\u753b\u50cf\u300d\u3092\u4e0a\u3052\u3066\u3044\u304d\u307e\u3059\u3002\npixiv\u898f\u5b9a\u306e\u7bc4\u56f2\u5185\u3067\u306e\u4fee\u6b63\u306a\u306e\u3067\u3054\u4e86\u627f\u304f\u3060\u3055\u3044\u307e\u3059\u3088\u3046\u304a\u9858\u3044\u81f4\u3057\u307e\u3059\u3002\n\n","headline":"M\u3060S\u305f\u308d\u3046","image":"https:\/\/pixiv.pximg.net\/c\/1200x630_90_a2_g5\/fanbox\/public\/images\/creator\/3316400\/cover\/mZXFThZDHXtQspHbtZGwBLTl.jpeg","mainEntityOfPage":"https:\/\/msmspc.fanbox.cc\/","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","height":"56","url":"https:\/\/s.pximg.net\/common\/image\/fanbox\/logo_s.png","width":"411"},"name":"pixivFANBOX"}},{"@context":"http:\/\/schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","item":{"@id":"https:\/\/msmspc.fanbox.cc\/","name":"M\u3060S\u305f\u308d\u3046"},"position":1}]}]</script>
let el = document.querySelector("script[type='application/ld+json']");
let dat = JSON.parse(el.textContent);
// 'https://pixiv.pximg.net/c/1200x630_90_a2_g5/fanbox/public/images/creator/3316400/cover/mZXFThZDHXtQspHbtZGwBLTl.jpeg'
// 3316400 = id
let img = dat[0].image;
let pattern =
/https:\/\/.*\/fanbox\/public\/images\/creator\/(\d+)\/cover\/.*\.\w+/i;
let match = img.match(pattern);
if (!match) return;
let id = match[1];
let dd = figureDomain();
let dCreators = config.creators[dd[1]];
for (const key in dCreators) {
if (Object.hasOwnProperty.call(dCreators, key)) {
const creator = dCreators[key];
if (creator.id === id) {
clearInterval(looper);
let dest = locationGen(dd[0], dd[1], id);
if (confirm("Redirecting to " + dest)) {
location.href = dest;
}
break;
}
}
}
},
},
};
function locationGen(domain, service, id) {
let l = `https://${domain}/${service}/user/${id}`;
return l;
}
function figureDomain() {
for (const key in config.domains) {
if (config.domains.hasOwnProperty(key)) {
const domain = config.domains[key];
if (location.host.endsWith("." + key) || location.host == key) {
return domain;
}
}
}
return null;
}
// coomerCreators = null; // This is to reduce memory costs
// kemonoCreators = null; // This is to reduce memory costs
// function fn() {
// const domains = {
// "onlyfans.com": [coomerDomain, "onlyfans"],
// "fansly.com": [coomerDomain, "fansly"],
// "candfans.jp": [coomerDomain, "candfans"],
// "patreon.com": [kemonoDomain, "patreon"],
// "fantia.jp": [kemonoDomain, "fantia"],
// };
// if (domains[location.host]) {
// const service = domains[location.host][1];
// let id = location.pathname.split("/")[2];
// if (service === "candfans") {
// // Find an image with a similar url to this
// // data-src="https://image.candfans.jp/user/154310/profile/i0YW0NNR864lVp7s7dHYhfg25rqJgeLx2ulzAEMG.png"
// let imgs = document.querySelectorAll("img[data-src]");
// let pattern =
// /https:\/\/image\.candfans\.jp\/user\/(\d+)\/profile\/.*\.\w+/i;
// for (let i = 0; i < imgs.length; i++) {
// let img = imgs[i];
// let src = img.getAttribute("data-src");
// let match = src.match(pattern);
// if (match) {
// id = match[1];
// break;
// }
// }
// } else if (service === "patreon") {
// // https://www.patreon.com/user?u=5564244
// let parsed = new URL(location.href);
// id = parsed.searchParams.get("u");
// } else if (service == "fantia") {
// // https://fantia.jp/fanclubs/83679
// let parsed = new URL(location.href);
// id = parsed.pathname.split("/")[2];
// }
// let dCreators = creators[domains[location.host]];
// for (const key in dCreators) {
// if (Object.hasOwnProperty.call(dCreators, key)) {
// const creator = dCreators[key];
// if (creator.id === id) {
// clearInterval(looper);
// let dest = locationGen(domains[location.host][0], service, id);
// if (confirm("Redirecting to " + dest)) {
// location.href = dest;
// }
// break;
// }
// }
// }
// }
// }
function fn() {
let dd = figureDomain();
if (dd === null) return;
let [destDomain, service] = dd;
config.handlers[service]();
}
let looper = setInterval(fn, 1000);
fn();
})(require("./coomer.su-modded.json"), require("./kemono.su-modded.json"));

File diff suppressed because one or more lines are too long

1
src/kemono.su.json Normal file

File diff suppressed because one or more lines are too long