diff --git a/.forgejo/scripts/update-wiki.py b/.forgejo/scripts/update-wiki.py
new file mode 100644
index 0000000..16ffa5e
--- /dev/null
+++ b/.forgejo/scripts/update-wiki.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+
+from pip._vendor import requests
+from pip._vendor import tomli
+import json
+import glob
+import os
+
+mods = dict()
+count = dict()
+count["server"] = 0
+count["client"] = 0
+count["both"] = 0
+
+cache = dict()
+
+class fragile(object):
+ class Break(Exception):
+ """Break out of the with statement"""
+
+ def __init__(self, value):
+ self.value = value
+
+ def __enter__(self):
+ return self.value.__enter__()
+
+ def __exit__(self, etype, value, traceback):
+ error = self.value.__exit__(etype, value, traceback)
+ if etype == self.Break:
+ return True
+ return error
+
+if os.path.isfile("cache/licenses.json"):
+ with open("cache/licenses.json", "r") as f:
+ cache = json.load(f)
+
+for mod in glob.glob("pack/mods/*.toml"):
+ with open(mod, "r") as f:
+ data = tomli.load(f)
+ moddata = dict()
+ moddata["name"] = data["name"]
+ moddata["url"] = ""
+ moddata["side"] = data["side"]
+ license = dict()
+
+ if "update" in data:
+ if "modrinth" in data["update"]:
+ id = data["update"]["modrinth"]["mod-id"]
+ moddata["id"] = id
+ moddata["url"] = "https://modrinth.com/mod/" + id
+ moddata["site"] = "Modrinth"
+
+ if id in cache:
+ data = cache[id]
+ else:
+ data = requests.get("https://api.modrinth.com/v2/project/" + id).json()["license"]
+ cache[id] = data
+ moddata["license"] = data
+ elif "curseforge" in data["update"]:
+ moddata["url"] = "https://legacy.curseforge.com/projects/" + str(data["update"]["curseforge"]["project-id"])
+ moddata["site"] = "CurseForge"
+
+ count[moddata["side"]] += 1
+ mods[moddata["name"]] = moddata
+
+with fragile(open("wiki/Modlist.md", "w")) as f:
+ if (count["server"] + count["client"] + count["both"]) == 0:
+ f.write("## No mods.")
+ raise fragile.Break
+
+ f.write("## Total Count\r\n")
+ f.write("
")
+ f.write("\r\n")
+ f.write("Side | ")
+ f.write("Count | ")
+ f.write("
\r\n")
+
+ f.write("")
+ f.write("Total | ")
+ f.write("" + str(count["server"] + count["client"] + count["both"]) + " | ")
+ f.write("
\r\n")
+
+ if count["both"] > 0:
+ f.write("")
+ f.write("Shared | ")
+ f.write("" + str(count["both"]) + " | ")
+ f.write("
\r\n")
+
+ if count["client"] > 0:
+ f.write("")
+ f.write("Client | ")
+ f.write("" + str(count["client"]) + " | ")
+ f.write("
\r\n")
+
+ if count["server"] > 0:
+ f.write("")
+ f.write("Server | ")
+ f.write("" + str(count["server"]) + " | ")
+ f.write("
\r\n")
+
+ f.write("
\r\n\r\n")
+
+ f.write("## Individual Mods\r\n")
+ f.write("")
+ f.write("\r\n")
+ f.write("Name | ")
+ f.write("Side | ")
+ f.write("Link | ")
+ f.write("
\r\n")
+
+ for mod in mods:
+ data = mods[mod]
+ f.write("\r\n")
+ f.write("" + mod + " | ")
+ f.write("" + data["side"] + " | ")
+ if data["url"] != "":
+ f.write("" + data["site"] + " | ")
+ else:
+ f.write("N/A | ")
+ f.write("\r\n
")
+ f.write("\r\n
")
+
+with fragile(open("wiki/Licenses.md", "w")) as f:
+ if (count["server"] + count["client"] + count["both"]) == 0:
+ f.write("## No mods.")
+ raise fragile.Break
+
+ for mod in mods:
+ data = mods[mod]
+ f.write("## " + mod + "\r\n")
+ f.write("")
+ if "site" in data and data["site"] == "CurseForge":
+ f.write("CurseForge API does not provide license information, see mod page for details.")
+ elif "license" in data:
+ license = data["license"]
+ if license["name"] == "":
+ if license["id"] == "LicenseRef-Custom":
+ license["name"] = "Custom"
+ else:
+ license["name"] = license["id"][11:].replace("-", " ")
+
+ if license["url"] != None:
+ f.write("" + license["name"] + "")
+ else:
+ f.write(license["name"])
+ else:
+ f.write("Unknown")
+ f.write("\r\n")
+
+if not os.path.exists("cache"):
+ os.makedirs("cache")
+
+with open("cache/licenses.json", "w") as f:
+ json.dump(cache, f)
diff --git a/.forgejo/workflows/automation.yaml b/.forgejo/workflows/automation.yaml
new file mode 100644
index 0000000..af334c3
--- /dev/null
+++ b/.forgejo/workflows/automation.yaml
@@ -0,0 +1,57 @@
+on: [push]
+jobs:
+ update:
+ name: "Update wiki"
+ runs-on: ubuntu-latest
+ steps:
+ - name: "Clone modpack"
+ uses: actions/checkout@v3
+ with:
+ repository: ${{ github.repository }}
+ path: "pack"
+
+ - name: "Clone wiki"
+ uses: actions/checkout@v3
+ with:
+ repository: ${{ github.repository }}.wiki
+ path: "wiki"
+ ref: "main"
+
+ - name: Restore cached licenses
+ id: cache-licenses-restore
+ uses: actions/cache/restore@v4
+ with:
+ path: cache
+ key: licenses
+
+ - name: "Update modlist"
+ run: python pack/.forgejo/scripts/update-wiki.py
+
+ - name: Save cached licenses
+ id: cache-licenses-save
+ uses: actions/cache/save@v4
+ with:
+ path: cache
+ key: ${{ steps.cache-licenses-restore.outputs.cache-primary-key }}
+
+ - name: "Commit changes"
+ run: |
+ cd wiki
+ git add .
+ git config --global user.name 'Modpack Wiki Updater'
+ git config --global user.email 'modupdater@incest.world'
+ git commit -am "Automated wiki update"
+ git push
+
+ restart:
+ if: "contains(github.event.head_commit.message, '[restart]')"
+ name: "Restart server"
+ runs-on: ubuntu-latest
+ steps:
+ - name: Restart the server
+ uses: https://git.incest.world/actions/ssh-action@master
+ with:
+ host: ${{ vars.SSH_SERVER }}
+ username: ${{ secrets.SSH_USER }}
+ key: ${{ secrets.SSH_KEY }}
+ script: ${{ vars.RESTART_COMMAND }}