Files
DeEarthX-V2/src/utils/DeEarth.ts
2025-09-07 17:42:56 +08:00

199 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Power by.Tianpao
* 本工具可能会判断失误,但也能为您节省不少时间!
* DeEarth V2 From StarNet.X
* Writing in 07.10.2025(latest)
* ©2024-2025
*/
import AdmZip from "adm-zip";
import got from "got";
import fs from "fs";
import crypto from "crypto";
import toml from 'smol-toml'
import path from 'path';
import pMap from "p-map";
import { usemirror } from "./utils.js";
import { LOGGER } from "./logger.js";
import { MultiBar } from "cli-progress";
export async function DeEarthMain(modspath: string, movepath: any) {
if (!fs.existsSync(movepath)) {
fs.mkdirSync(movepath)
}
LOGGER.info(`DeEarth V1.0.0`)
LOGGER.info(`如有无法筛选的mods请前往 https://dearth.0771010.xyz/ 提交未成功筛选的模组的modid`)
LOGGER.info(`Probejs 7.0.0以上版本为非客户端mod如.rubbish中有请自行添加回去`)
const resaddr = fs.readdirSync(modspath)
LOGGER.info(`获取目录列表,一共${resaddr.length}个jar文件。`)
const totalBar = multibar.create(resaddr.length, 0, { filename: '总文件数' })
for (let i = 0; i < resaddr.length; i++) {
const e = `${modspath}/${resaddr[i]}`
if (e.endsWith(".jar") && fs.statSync(e).isFile()) { //判断是否以.jar结尾并且是文件
//console.log(e)
await DeEarth(e, movepath) //使用DeEarth进行审查mod并移动
totalBar.increment()
}
}
multibar.stop()
}
export async function DeEarth(modpath: string, movepath: string) {
let mrurl = "https://api.modrinth.com"
if (usemirror){
mrurl = "https://mod.mcimirror.top/modrinth"
}
let pid:string|undefined = undefined
try {
const data = fs.readFileSync(modpath)
const hash = crypto.createHash('sha1').update(data).digest('hex')
const pjson = await got.get(`${mrurl}/v2/version_file/${hash}?algorithm=sha1`,{
headers:{
"User-Agent": "DeEarth"
}
}).json<{project_id:string}>()
pid = pjson.project_id
//console.log("哈希命中成功正在查询Modrinth")
}catch(e){}
const zipinfo = ZipInfo(modpath)
let modid:string = ""
if(zipinfo){
if(zipinfo.modinfo.type === "forge"){
modid = zipinfo.modinfo.data.mods[0].modId
}else if(zipinfo.modinfo.type === "fabric"){
modid = zipinfo.modinfo.data.id
}
try { //Modrinth
let body:any
if (pid){
body = JSON.parse(await FastGot(`${mrurl}/v2/project/${pid}`))
}else{
body = JSON.parse(await FastGot(`${mrurl}/v2/project/${modid}`))
}
if(body.client_side == "required" && body.server_side !== "required"){
fs.renameSync(modpath, `${movepath}/${path.basename(modpath)}`)
}
} catch (error) { //DeEarthPublic
try {
if (JSON.parse(await FastGot(`https://dearth.0771010.xyz/api/modid?modid=${modid}`)).isClient) {
fs.renameSync(modpath, `${movepath}/${path.basename(modpath)}`)
}else{
throw new Error("DeEarthPublic Not Found This Mod")
}
} catch (error) { //mods.toml或fabric.mod.json判断
try{
if(zipinfo.modinfo.type === "forge"){
const mcside = zipinfo.modinfo.data.dependencies[modid].find((mod: { modId: string; }) => mod.modId === "minecraft").side //Minecraft
if (mcside == "CLIENT") {
fs.renameSync(modpath, `${movepath}/${path.basename(modpath)}`)
}
const forgeside = zipinfo.modinfo.data.dependencies[modid].find((mod: { modId: string; }) => mod.modId === "forge").side //Forge
if (forgeside == "CLIENT") {
fs.renameSync(modpath, `${movepath}/${path.basename(modpath)}`)
}
const neoside = zipinfo.modinfo.data.dependencies[modid].find((mod: { modId: string; }) => mod.modId === "neoforge").side //NeoForge
if (neoside == "CLIENT") {
fs.renameSync(modpath, `${movepath}/${path.basename(modpath)}`)
}
}else if(zipinfo.modinfo.type === "fabric"){ //Fabric
const fmj = zipinfo.modinfo.data.environment
if (fmj == "client") {
fs.renameSync(modpath, `${movepath}/${path.basename(modpath)}`)
}
}
}catch(error){
try{
for (let i = 0; i < zipinfo.mixins.length; i++) {
const e = zipinfo.mixins[i]
const info = JSON.parse(e.info)
if (isMixin(info)) {
fs.renameSync(modpath, `${movepath}/${path.basename(modpath)}`)
}
}
}catch(error){
LOGGER.error(`大天才JSON写注释了估计模组路径:${modpath},过滤失败`)
}
}
}
}
}
}
function ZipInfo(modpath: string) {
interface ZipInfo {
mixins: { filename: string, info: string }[]
modinfo: {type:string,data:any};
}
try{
let zipinfo: ZipInfo = { mixins: [], modinfo: {type: "",data: {}} }
const zip = new AdmZip(modpath).getEntries();
for (let i = 0; i < zip.length; i++) {
const e = zip[i]
if (isMixinFile(e.entryName)) {
zipinfo.mixins.push({ filename: e.entryName, info: e.getData().toString('utf-8') })
} else if (isForge(e.entryName)) {
zipinfo.modinfo.type = "forge"
zipinfo.modinfo.data = toml.parse(e.getData().toString('utf-8'))
} else if (e.entryName.endsWith("fabric.mod.json")) {
zipinfo.modinfo.type = "fabric"
zipinfo.modinfo.data = JSON.parse(e.getData().toString('utf-8'))
}
}
return zipinfo;
}catch(error){
LOGGER.error(error)
}
}
async function FastGot(url: string) {
let e = []
e.push([url])
const fastgot = await pMap(e, async (e: any[]) => {
try {
if (e[0] !== null) { //防止URL为空
//if(isChinaIpAddress((await got.get("https://4.ipw.cn/")).body)){
return (await got.get(e[0], { headers: { "User-Agent": "DeEarthX" } })).body
//}else{
//return (await got.get(`https://mod.mcimirror.top/modrinth/${new URL(e[0]).pathname}`, { headers: { "User-Agent": "DeEarth" } })).body //MCIM源
//}
}
} catch (error: any) {
if (error.message !== "Response code 404 (Not Found)") {
LOGGER.error({ err: error })
} else {
throw new Error(error)
}
}
}, {
concurrency: 48
})
if (fastgot[0] !== undefined) {
return fastgot[0]
} else {
return "null"
}
}
const multibar = new MultiBar({
format: ' {bar} | {filename} | {value}/{total}',
noTTYOutput: true,
notTTYSchedule: 10 * 1000,
})
function isMixin(resx: { mixins: {} | null; client: {}; }) {
return resx.mixins == null || Object.keys(resx.mixins).length == 0 && Object.keys(resx.client).length !== 0
}
function isForge(name: string): boolean {
return name.endsWith("mods.toml") || name.endsWith("neoforge.mod.toml")
}
function isMixinFile(name: string): boolean {
return !name.includes("/") && name.endsWith(".json") && !name.endsWith("refmap.json") && !name.endsWith("mod.json")
}
function isChinaIpAddress(ipAddress: string) {
const chinaRegex = /^((?:(?:1(?:0|1|2[0-7]|[3-9][0-9])|2(?:[0-4][0-9]|5[0-5])|[3-9][0-9]{2})\.){3}(?:(?:1(?:0|1|2[0-7]|[3-9][0-9])|2(?:[0-4][0-9]|5[0-5])|[3-9][0-9]{2})))$/;
return chinaRegex.test(ipAddress);
}