class TheRipperPremiumHUB {
|
|
constructor() {
|
|
this.outdatedModules = {};
|
|
this.announcementsHtml = "";
|
|
this._debug = false;
|
|
this.converter = new showdown.Converter();
|
|
this.init();
|
|
}
|
|
|
|
get repositoryIndex() {
|
|
return {
|
|
megapack: "megapack",
|
|
"vtt-desktop-client": "fvtt-player-client",
|
|
levels: "Levels",
|
|
enhancedcombathud: "enhancedcombathud",
|
|
"automated-evocations": "automated-evocations",
|
|
betterroofs: "Better-Roofs",
|
|
"combat-tracker-dock": "combat-tracker-dock",
|
|
bossbar: "Boss-Bar",
|
|
combatbooster: "Combat-Booster",
|
|
"fuzzy-foundry": "fuzzy-foundry",
|
|
"hover-distance": "hover-distance",
|
|
"hurry-up": "hurry-up",
|
|
levelsautocover: "levelsautocover",
|
|
levelsvolumetrictemplates: "levelsvolumetrictemplates",
|
|
patrol: "Patrol",
|
|
"damage-numbers": "damage-numbers",
|
|
smarttarget: "Smart-Target",
|
|
splatter: "splatter",
|
|
"dnd-randomizer": "dnd-randomizer",
|
|
"tile-sort": "tile-sort",
|
|
"tile-scroll": "tile-scroll",
|
|
"token-z": "token-z",
|
|
"foundry-taskbar": "foundry-taskbar",
|
|
quickdraw: "quickdraw",
|
|
"config-presets": "config-presets",
|
|
"light-switch": "light-switch",
|
|
"filepicker-plus": "filepicker-plus",
|
|
"progress-tracker": "progress-tracker",
|
|
"situational-shortcuts": "situational-shortcuts",
|
|
"quick-doors": "quick-doors",
|
|
"image-context": "image-context",
|
|
"inactive-tokens-lmao": "inactive-tokens-lmao",
|
|
"levels-3d-preview": "levels-3d-preview",
|
|
choices: "choices",
|
|
mmm: "Maxwell-s-Manual-of-Malicious-Maladies",
|
|
socketmacros: "socketmacros",
|
|
tokenflip: "tokenflip",
|
|
"token-notes": "token-notes",
|
|
"wall-height": "wall-height",
|
|
canvas3dcompendium: "canvas3dcompendium",
|
|
canvas3dtokencompendium: "canvas3dtokencompendium",
|
|
"theripper-premium-hub": "theripper-premium-hub",
|
|
"levels-layer-effects": "levels-layer-effects",
|
|
"three-actor-portrait": "three-actor-portrait",
|
|
mastercrafted: "mastercrafted",
|
|
gatherer: "gatherer",
|
|
"camera-dock": "camera-dock",
|
|
"macro-wheel": "macro-wheel",
|
|
};
|
|
}
|
|
|
|
async init() {
|
|
this.moduleData = await this.fetchData();
|
|
this.announcements = this.moduleData.announcements;
|
|
delete this.moduleData.announcements;
|
|
this.getOutdatedModules();
|
|
if (game.settings.get("theripper-premium-hub", "autoCheck")) {
|
|
this.displayOutdated(false);
|
|
}
|
|
this.displayAnnouncements();
|
|
}
|
|
|
|
async getForgeData(moduleId) {
|
|
return await fetch(`https://forge-vtt.com/api/bazaar/package/${moduleId}`)
|
|
.then((response) => response.json())
|
|
.then((data) => data)
|
|
}
|
|
|
|
getOutdatedModules() {
|
|
const checkDisabled = game.settings.get("theripper-premium-hub", "checkDisabled");
|
|
for (let [k, v] of Object.entries(this.moduleData)) {
|
|
const installedModule = game.modules.get(k);
|
|
if (!installedModule) continue;
|
|
if (!checkDisabled && !installedModule?.active) continue;
|
|
if (isNewerVersion(v.version, installedModule.version) || this._debug) {
|
|
this.outdatedModules[k] = v;
|
|
this.outdatedModules[k].title = installedModule.title;
|
|
this.outdatedModules[k].currentVersion = installedModule.version;
|
|
}
|
|
}
|
|
}
|
|
|
|
async displayOutdated(notify = false) {
|
|
if (Object.keys(this.outdatedModules).length === 0) return notify ? ui.notifications.info(`No outdated modules found.`) : null;
|
|
let displayUpdated = {};
|
|
if (!notify) {
|
|
const viewedUpdates = game.settings.get("theripper-premium-hub", "viewedUpdates");
|
|
for (let [k, v] of Object.entries(this.outdatedModules)) {
|
|
if (viewedUpdates[k] && v.version === viewedUpdates[k]) continue;
|
|
displayUpdated[k] = v;
|
|
}
|
|
} else {
|
|
displayUpdated = this.outdatedModules;
|
|
}
|
|
if (Object.keys(displayUpdated).length === 0) return null;
|
|
const html = await renderTemplate("modules/theripper-premium-hub/templates/modlist.hbs", displayUpdated);
|
|
Dialog.prompt({
|
|
title: "TheRipper93 Premium HUB - Updates Available!",
|
|
content: html,
|
|
rejectClose: false,
|
|
callback: () => {
|
|
const viewedUpdates = game.settings.get("theripper-premium-hub", "viewedUpdates");
|
|
for (let [k, v] of Object.entries(this.outdatedModules)) {
|
|
viewedUpdates[k] = v.version;
|
|
}
|
|
game.settings.set("theripper-premium-hub", "viewedUpdates", viewedUpdates);
|
|
},
|
|
close: () => {},
|
|
});
|
|
}
|
|
|
|
async displayAnnouncements() {
|
|
const announcements = this.announcements;
|
|
if (!announcements) return;
|
|
const ids = Object.keys(announcements);
|
|
const viewedAnnouncements = game.settings.get("theripper-premium-hub", "viewedAnnouncements") ?? "";
|
|
const allViewed = ids.every((id) => viewedAnnouncements.includes(id));
|
|
const html = await renderTemplate("modules/theripper-premium-hub/templates/announcements.hbs", announcements);
|
|
this.announcementsHtml = html;
|
|
if (allViewed && !this._debug) return;
|
|
game.settings.set("theripper-premium-hub", "viewedAnnouncements", ids.join(","));
|
|
Dialog.prompt({
|
|
title: "TheRipper93 Premium HUB - Announcement!",
|
|
content:
|
|
html +
|
|
` <p style="text-align: center;">Want to support me and get access to premium modules?</p><hr>
|
|
<p style="text-align: center;"><a href="https://theripper93.com/" target="_blank" rel="nofollow" title="https://theripper93.com/">Check out my Website</a></p>`,
|
|
rejectClose: false,
|
|
callback: () => {},
|
|
close: () => {},
|
|
});
|
|
}
|
|
|
|
async fetchData() {
|
|
const modules = game.modules.filter((v) => v.authors && v.authors.some((a) => a.name === "theripper93"));
|
|
const announcements = await this.fetchAnnouncements();
|
|
const promises = modules.map((m) => this.fetchModuleData(m));
|
|
|
|
const obj = {};
|
|
|
|
await Promise.all(promises).then((data) => {
|
|
data.forEach((d) => {
|
|
if (d) {
|
|
obj[d.id] = d;
|
|
}
|
|
});
|
|
});
|
|
obj.announcements = announcements.announcements;
|
|
return obj;
|
|
}
|
|
|
|
async fetchModuleData(module) {
|
|
const checkDisabled = game.settings.get("theripper-premium-hub", "checkDisabled");
|
|
if (!checkDisabled && !module.active) return null;
|
|
const isPremium = module?.manifest?.includes("foundryvtt");
|
|
const id = module.id;
|
|
const owner = "theripper93";
|
|
const repo = this.repositoryIndex[id] ?? id;
|
|
const releases = await this.getAllReleasesFromGitHub(owner, repo, isPremium, module);
|
|
if (!releases) return null;
|
|
const latestRelease = Object.keys(releases).sort((a, b) => isNewerVersion(b, a))[0];
|
|
if(!latestRelease) return null;
|
|
return {
|
|
version: latestRelease,
|
|
title: module.title,
|
|
changelog: releases,
|
|
latestChangeLog: this.converter.makeHtml(releases[latestRelease]),
|
|
id: id,
|
|
};
|
|
/*return await fetch(`https://forge-vtt.com/api/bazaar/package/${moduleId}`)
|
|
.then((response) => response.json())
|
|
.then((data) => data);*/
|
|
}
|
|
|
|
async getAllReleasesFromGitHub(owner, repo, isPremium, module) {
|
|
if (isPremium) {
|
|
const changelogURL = `https://raw.githubusercontent.com/theripper93/theripper-premium-hub/master/premium-changelogs/${repo}.md`;
|
|
try {
|
|
const response = await fetch(changelogURL);
|
|
if (!response.ok) return null;
|
|
const changelog = await response.text();
|
|
if (!changelog) return null;
|
|
const releases = {};
|
|
//parse changelog
|
|
const changelogLines = changelog.split(/\r?\n/);
|
|
let currentVersion = "";
|
|
for (const line of changelogLines) {
|
|
if (line.startsWith("##")) {
|
|
const version = line.replace("## Version ", "");
|
|
releases[version] = "";
|
|
currentVersion = version;
|
|
} else {
|
|
const trimmed = line.trim();
|
|
if (trimmed != "") releases[currentVersion] += line + "\n";
|
|
}
|
|
}
|
|
return releases;
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
} else {
|
|
const releasesPerPage = 10; // Maximum allowed by GitHub API
|
|
const apiUrl = `https://api.github.com/repos/${owner}/${repo}/releases?per_page=${releasesPerPage}`;
|
|
|
|
try {
|
|
let compiledReleases = {};
|
|
|
|
const response = await fetch(`${apiUrl}&page=1`);
|
|
|
|
if (response.status === 404 && !module.download) {
|
|
ui.notifications.error(`The module <strong>${module.title}</strong> is still installed using the old manual installation, please switch to the new Foundry/Patreon integration. <a href="https://theripper93.com/info/installation" target="_blank" rel="nofollow">More Information</a>`, { permanent: true });
|
|
}
|
|
|
|
|
|
|
|
const releases = await response.json();
|
|
releases.forEach((release) => {
|
|
compiledReleases[release.tag_name] = release.body;
|
|
});
|
|
|
|
return compiledReleases;
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
async fetchAnnouncements() {
|
|
return await fetch(`https://api.theripper93.com/moduleListing/latest`, { cache: "no-cache" })
|
|
.then((response) => response.json())
|
|
.then((data) => data);
|
|
}
|
|
|
|
_getdataforfile() {
|
|
const mods = {};
|
|
game.modules.forEach((v, k) => {
|
|
if (v.authors && v.authors.some((a) => a.name === "theripper93") && v.manifest.includes("foundryvtt")) {
|
|
mods[k] = {
|
|
title: v.title,
|
|
version: v.version,
|
|
downloadURL: this.moduleData[k]?.downloadURL ?? "",
|
|
};
|
|
}
|
|
});
|
|
return mods;
|
|
}
|
|
|
|
getDependencies(moduleId) {
|
|
const rootModule = game.modules.get(moduleId);
|
|
const dependencies = new Set();
|
|
const addDependecies = (module) => {
|
|
const moduleDependencies = Array.from(module.relationships.requires).map((m) => game.modules.get(m.id));
|
|
moduleDependencies.forEach((m) => {
|
|
dependencies.add(m);
|
|
addDependecies(m);
|
|
});
|
|
};
|
|
addDependecies(rootModule);
|
|
return dependencies;
|
|
}
|
|
|
|
async troubleshoot(moduleId) {
|
|
const confirm = await Dialog.confirm({
|
|
title: "TheRipper93 Premium HUB - Troubleshoot",
|
|
content: `<p>Do you want to start the troubleshoot for ${game.modules.get(moduleId).title}?</p><br><p>This will disable all modules except the one you selected and its dependencies. You will be prompted to restore your modules after the troubleshoot.</p>`,
|
|
yes: () => {
|
|
return true;
|
|
},
|
|
no: () => {
|
|
return false;
|
|
},
|
|
});
|
|
if (!confirm) return;
|
|
const dependencies = this.getDependencies(moduleId);
|
|
const dependenciesIds = [...Array.from(dependencies).map((m) => m.id), moduleId, "theripper-premium-hub"];
|
|
const modulesSetting = game.settings.get("core", ModuleManagement.CONFIG_SETTING);
|
|
const currentlyEnabled = [];
|
|
for (let [k, v] of Object.entries(modulesSetting)) {
|
|
if (v) currentlyEnabled.push(k);
|
|
if (dependenciesIds.includes(k)) continue;
|
|
modulesSetting[k] = false;
|
|
}
|
|
await game.settings.set("theripper-premium-hub", "prevEnabledModules", currentlyEnabled);
|
|
await game.settings.set("core", ModuleManagement.CONFIG_SETTING, modulesSetting);
|
|
debouncedReload();
|
|
}
|
|
}
|