Skip to content

Commit 7b394e7

Browse files
committed
Add NeoForge update notifier
1 parent 3b7e4f4 commit 7b394e7

File tree

10 files changed

+296
-55
lines changed

10 files changed

+296
-55
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
bot_version=4.1.7
1+
bot_version=4.2.0

src/commander/java/com/mcmoddev/mmdbot/commander/config/Configuration.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,15 @@ public List<SnowflakeValue> forge() {
161161
return forge;
162162
}
163163

164+
@Required
165+
@Setting("neoforge")
166+
@Comment("A list of Snowflake IDs of channels in which to send NeoForge update notifiers.")
167+
private List<SnowflakeValue> neoforge = new ArrayList<>();
168+
169+
public List<SnowflakeValue> neoforge() {
170+
return neoforge;
171+
}
172+
164173
@Required
165174
@Setting("parchment")
166175
@Comment("A list of Snowflake IDs of channels in which to send Parchment update notifiers.")

src/commander/java/com/mcmoddev/mmdbot/commander/updatenotifiers/SharedVersionHelpers.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,20 @@
2121
package com.mcmoddev.mmdbot.commander.updatenotifiers;
2222

2323
import com.mcmoddev.mmdbot.commander.TheCommander;
24+
import org.xml.sax.SAXException;
2425

2526
import javax.annotation.Nullable;
27+
import javax.xml.parsers.DocumentBuilderFactory;
28+
import javax.xml.parsers.ParserConfigurationException;
29+
import javax.xml.xpath.XPathExpression;
30+
import javax.xml.xpath.XPathExpressionException;
31+
import javax.xml.xpath.XPathFactory;
2632
import java.io.IOException;
2733
import java.io.InputStream;
2834
import java.io.InputStreamReader;
2935
import java.net.URL;
3036
import java.nio.charset.StandardCharsets;
37+
import java.util.regex.Pattern;
3138

3239
/**
3340
* Helper methods for the Fabric and Quilt mod loader.
@@ -57,6 +64,31 @@ public static InputStream getStream(final String urlString) {
5764
}
5865
}
5966

67+
public static String getLatestFromMavenMetadata(String url) {
68+
final InputStream stream = getStream(url);
69+
if (stream == null) {
70+
return null;
71+
}
72+
try {
73+
final var doc = DocumentBuilderFactory.newInstance()
74+
.newDocumentBuilder()
75+
.parse(stream);
76+
final XPathExpression expr = XPathFactory.newInstance()
77+
.newXPath()
78+
.compile("/metadata/versioning/latest/text()");
79+
return expr.evaluate(doc);
80+
} catch (SAXException | XPathExpressionException | ParserConfigurationException | IOException ex) {
81+
TheCommander.LOGGER.error("Failed to resolve latest version from url '{}'", url, ex);
82+
}
83+
return null;
84+
}
85+
86+
public static String replaceGitHubReferences(String changelog, String repo) {
87+
return changelog.replaceAll("\\(#(?<number>\\d+)\\)", "[(#$1)](https://github.com/" + repo + "/pull/$1)")
88+
.replaceAll("(?m)^ - ", "- ")
89+
.replaceAll("(?mi)(?<type>(?:close|fix|resolve)(?:s|d|es|ed)?) #(?<number>\\d+)", "$1 [#$2](https://github.com/" + repo + "/issues/$2)");
90+
}
91+
6092
/**
6193
* The type Shared Version Info.
6294
*

src/commander/java/com/mcmoddev/mmdbot/commander/updatenotifiers/UpdateNotifiers.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.mcmoddev.mmdbot.commander.updatenotifiers.fabric.FabricApiUpdateNotifier;
2525
import com.mcmoddev.mmdbot.commander.updatenotifiers.forge.ForgeUpdateNotifier;
2626
import com.mcmoddev.mmdbot.commander.updatenotifiers.minecraft.MinecraftUpdateNotifier;
27+
import com.mcmoddev.mmdbot.commander.updatenotifiers.neoforge.NeoForgeUpdateNotifier;
2728
import com.mcmoddev.mmdbot.commander.updatenotifiers.parchment.ParchmentUpdateNotifier;
2829
import com.mcmoddev.mmdbot.commander.updatenotifiers.quilt.QuiltUpdateNotifier;
2930
import com.mcmoddev.mmdbot.core.event.Events;
@@ -57,9 +58,10 @@ public static void register() {
5758
wasRegistered = true;
5859
Events.MISC_BUS.addListener((TaskScheduler.CollectTasksEvent event) -> {
5960
final long checkingPeriod = 15;
60-
LOGGER.info("Checking for Minecraft, Forge, Quilt and Fabric updates every {} minutes.", checkingPeriod);
61+
LOGGER.info("Checking for Minecraft, Forge, NeoForge, Quilt and Fabric updates every {} minutes.", checkingPeriod);
6162
event.addTask(new MinecraftUpdateNotifier(), 0, checkingPeriod, TimeUnit.MINUTES);
6263
event.addTask(new ForgeUpdateNotifier(), 0, checkingPeriod, TimeUnit.MINUTES);
64+
event.addTask(new NeoForgeUpdateNotifier(), 0, checkingPeriod, TimeUnit.MINUTES);
6365
event.addTask(new QuiltUpdateNotifier(), 0, checkingPeriod, TimeUnit.MINUTES);
6466
event.addTask(new FabricApiUpdateNotifier(), 0, checkingPeriod, TimeUnit.MINUTES);
6567

src/commander/java/com/mcmoddev/mmdbot/commander/updatenotifiers/fabric/FabricVersionHelper.java

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,10 @@
2222

2323
import com.google.gson.Gson;
2424
import com.google.gson.reflect.TypeToken;
25-
import com.mcmoddev.mmdbot.commander.TheCommander;
2625
import com.mcmoddev.mmdbot.commander.updatenotifiers.SharedVersionHelpers;
2726
import lombok.experimental.UtilityClass;
28-
import org.xml.sax.SAXException;
2927

3028
import javax.annotation.Nullable;
31-
import javax.xml.parsers.DocumentBuilderFactory;
32-
import javax.xml.parsers.ParserConfigurationException;
33-
import javax.xml.xpath.XPathExpression;
34-
import javax.xml.xpath.XPathExpressionException;
35-
import javax.xml.xpath.XPathFactory;
36-
import java.io.IOException;
37-
import java.io.InputStream;
3829
import java.io.InputStreamReader;
3930
import java.util.List;
4031
import java.util.Map;
@@ -120,21 +111,6 @@ public static String getLatestLoader() {
120111
*/
121112
@Nullable
122113
public static String getLatestApi() {
123-
final InputStream stream = getStream(API_URL);
124-
if (stream == null) {
125-
return null;
126-
}
127-
try {
128-
final var doc = DocumentBuilderFactory.newInstance()
129-
.newDocumentBuilder()
130-
.parse(stream);
131-
final XPathExpression expr = XPathFactory.newInstance()
132-
.newXPath()
133-
.compile("/metadata/versioning/latest/text()");
134-
return expr.evaluate(doc);
135-
} catch (SAXException | XPathExpressionException | ParserConfigurationException | IOException ex) {
136-
TheCommander.LOGGER.error("Failed to resolve latest Fabric API version", ex);
137-
}
138-
return null;
114+
return SharedVersionHelpers.getLatestFromMavenMetadata(API_URL);
139115
}
140116
}

src/commander/java/com/mcmoddev/mmdbot/commander/updatenotifiers/forge/ForgeUpdateNotifier.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package com.mcmoddev.mmdbot.commander.updatenotifiers.forge;
2222

2323
import com.mcmoddev.mmdbot.commander.config.Configuration;
24+
import com.mcmoddev.mmdbot.commander.updatenotifiers.SharedVersionHelpers;
2425
import com.mcmoddev.mmdbot.commander.updatenotifiers.UpdateNotifier;
2526
import com.mcmoddev.mmdbot.commander.util.StringSerializer;
2627
import net.dv8tion.jda.api.EmbedBuilder;
@@ -106,15 +107,17 @@ protected EmbedBuilder getEmbed(@Nullable final MinecraftForgeVersion oldVersion
106107

107108
private static void addChangelog(EmbedBuilder embedBuilder, String mcStart, String forgeStart, String mcEnd, String forgeEnd) {
108109
try {
109-
final var changelog = getChangelogBetweenVersions(
110+
String changelog = getChangelogBetweenVersions(
110111
mcStart, forgeStart, mcEnd, forgeEnd
111112
);
112113
if (changelog.isBlank()) return;
114+
115+
changelog = SharedVersionHelpers.replaceGitHubReferences(changelog, "MinecraftForge/MinecraftForge");
116+
113117
embedBuilder.setDescription("""
114118
[Changelog](%s):
115-
```
116119
%s
117-
```""".formatted(
120+
""".formatted(
118121
CHANGELOG_URL_TEMPLATE.formatted(mcEnd, forgeEnd), changelog
119122
));
120123
} catch (IOException ignored) {
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* MMDBot - https://github.com/MinecraftModDevelopment/MMDBot
3+
* Copyright (C) 2016-2023 <MMD - MinecraftModDevelopment>
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation;
8+
* Specifically version 2.1 of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18+
* USA
19+
* https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
20+
*/
21+
package com.mcmoddev.mmdbot.commander.updatenotifiers.neoforge;
22+
23+
import com.mcmoddev.mmdbot.commander.config.Configuration;
24+
import com.mcmoddev.mmdbot.commander.updatenotifiers.SharedVersionHelpers;
25+
import com.mcmoddev.mmdbot.commander.updatenotifiers.UpdateNotifier;
26+
import com.mcmoddev.mmdbot.commander.util.StringSerializer;
27+
import com.mcmoddev.mmdbot.core.util.Utils;
28+
import net.dv8tion.jda.api.EmbedBuilder;
29+
import net.dv8tion.jda.api.entities.MessageEmbed;
30+
import org.jetbrains.annotations.NotNull;
31+
32+
import javax.annotation.Nullable;
33+
import java.awt.Color;
34+
import java.io.IOException;
35+
import java.net.URL;
36+
import java.util.Map;
37+
import java.util.Objects;
38+
39+
import static com.mcmoddev.mmdbot.commander.updatenotifiers.forge.ForgeUpdateNotifier.getUrlAsString;
40+
41+
/**
42+
* The NeoForge update notifier.
43+
*
44+
* @author matyrobbrt
45+
*/
46+
public final class NeoForgeUpdateNotifier extends UpdateNotifier<NeoForgeVersions> {
47+
48+
public static final String CHANGELOG_URL = "https://maven.neoforged.net/releases/net/neoforged/neoforge/%1$s/neoforge-%1$s-changelog.txt";
49+
50+
public NeoForgeUpdateNotifier() {
51+
super(NotifierConfiguration.<NeoForgeVersions>builder()
52+
.name("neoforge")
53+
.channelGetter(Configuration.Channels.UpdateNotifiers::neoforge)
54+
.serializer(StringSerializer.json(StringSerializer.RECORD_GSON, NeoForgeVersions.class))
55+
.versionComparator(NotifierConfiguration.notEqual())
56+
.webhookInfo(new WebhookInfo("NeoForge Updates", "https://github.com/NeoForged.png"))
57+
.build());
58+
}
59+
60+
@Override
61+
protected NeoForgeVersions queryLatest() {
62+
return new NeoForgeVersions(NeoForgeVersionHelper.getNeoForgeVersions());
63+
}
64+
65+
@NotNull
66+
@Override
67+
protected EmbedBuilder getEmbed(@Nullable final NeoForgeVersions oldVersion, final NeoForgeVersions newVersion) {
68+
final String version;
69+
if (oldVersion == null) {
70+
version = newVersion.byMcVersion().entrySet().stream()
71+
.max(Map.Entry.comparingByKey())
72+
.orElseThrow()
73+
.getValue();
74+
} else {
75+
version = newVersion.byMcVersion().entrySet().stream()
76+
.filter(entry -> !Objects.equals(oldVersion.byMcVersion().get(entry.getKey()), entry.getValue()))
77+
.max(Map.Entry.comparingByKey())
78+
.orElseThrow()
79+
.getValue();
80+
}
81+
82+
final String[] split = version.split("\\.");
83+
84+
final String mcVersion = "1." + split[0] + "." + split[1];
85+
86+
final var embed = new EmbedBuilder();
87+
embed.addField("Minecraft Version", mcVersion, true);
88+
embed.setTitle("NeoForge version update");
89+
embed.setColor(Color.YELLOW);
90+
91+
final String oldNeoVersion = oldVersion == null ? null : oldVersion.byMcVersion().get(mcVersion);
92+
if (oldNeoVersion == null) {
93+
embed.addField("Version", version, true);
94+
} else {
95+
final boolean isNoLongerBeta = oldNeoVersion.endsWith("-beta") && !version.endsWith("-beta");
96+
embed.addField(isNoLongerBeta ? "New stable release" : "Latest", "**%s** -> **%s**".formatted(oldNeoVersion, version), true);
97+
}
98+
99+
addChangelog(embed, oldNeoVersion, version);
100+
101+
return embed;
102+
}
103+
104+
private static void addChangelog(EmbedBuilder embedBuilder, @Nullable String neoStart, String neoEnd) {
105+
try {
106+
String changelog = getChangelogBetweenVersions(
107+
neoStart, neoEnd
108+
);
109+
if (changelog.isBlank()) return;
110+
111+
changelog = SharedVersionHelpers.replaceGitHubReferences(changelog, "NeoForged/NeoForge");
112+
113+
embedBuilder.setDescription(Utils.truncate("""
114+
[Changelog](%s):
115+
%s
116+
""".formatted(
117+
CHANGELOG_URL.formatted(neoEnd), changelog
118+
), MessageEmbed.DESCRIPTION_MAX_LENGTH));
119+
} catch (IOException ignored) {
120+
}
121+
}
122+
123+
public static String getChangelogBetweenVersions(@Nullable final String neoStart, final String neoEnd) throws IOException {
124+
if (neoStart == null || neoStart.equals(neoEnd)) {
125+
final String[] split = getUrlAsString(new URL(CHANGELOG_URL.formatted(neoEnd))).split("\n");
126+
final StringBuilder changelog = new StringBuilder(split[0])
127+
.append('\n');
128+
for (int i = 1; i < split.length; i++) {
129+
// Neo version detected
130+
if (split[i].startsWith(" - ")) break;
131+
changelog.append(split[i]).append('\n');
132+
}
133+
return changelog.toString();
134+
}
135+
136+
final var startUrl = new URL(CHANGELOG_URL.formatted(neoStart));
137+
final var endUrl = new URL(CHANGELOG_URL.formatted(neoEnd));
138+
final var startChangelog = getUrlAsString(startUrl);
139+
140+
final var endChangelog = getUrlAsString(endUrl);
141+
142+
return endChangelog.replace(startChangelog, "");
143+
}
144+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* MMDBot - https://github.com/MinecraftModDevelopment/MMDBot
3+
* Copyright (C) 2016-2023 <MMD - MinecraftModDevelopment>
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation;
8+
* Specifically version 2.1 of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18+
* USA
19+
* https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
20+
*/
21+
package com.mcmoddev.mmdbot.commander.updatenotifiers.neoforge;
22+
23+
import com.mcmoddev.mmdbot.commander.TheCommander;
24+
import com.mcmoddev.mmdbot.commander.updatenotifiers.SharedVersionHelpers;
25+
import lombok.experimental.UtilityClass;
26+
import org.w3c.dom.NodeList;
27+
import org.xml.sax.SAXException;
28+
29+
import javax.xml.parsers.DocumentBuilderFactory;
30+
import javax.xml.parsers.ParserConfigurationException;
31+
import javax.xml.xpath.XPathConstants;
32+
import javax.xml.xpath.XPathExpression;
33+
import javax.xml.xpath.XPathExpressionException;
34+
import javax.xml.xpath.XPathFactory;
35+
import java.io.IOException;
36+
import java.io.InputStream;
37+
import java.util.LinkedHashMap;
38+
import java.util.Map;
39+
40+
@UtilityClass
41+
public final class NeoForgeVersionHelper extends SharedVersionHelpers {
42+
private static final String METADATA_URL = "https://maven.neoforged.net/net/neoforged/neoforge/maven-metadata.xml";
43+
44+
public static Map<String, String> getNeoForgeVersions() {
45+
final LinkedHashMap<String, String> versions = new LinkedHashMap<>();
46+
47+
final InputStream stream = getStream(METADATA_URL);
48+
if (stream == null) {
49+
return versions;
50+
}
51+
try {
52+
final var doc = DocumentBuilderFactory.newInstance()
53+
.newDocumentBuilder()
54+
.parse(stream);
55+
final XPathExpression expr = XPathFactory.newInstance()
56+
.newXPath()
57+
.compile("/metadata/versioning/versions/version");
58+
final NodeList versionsNode = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
59+
60+
for (int i = 0; i < versionsNode.getLength(); i++) {
61+
final String version = versionsNode.item(i).getTextContent();
62+
63+
final String[] split = version.split("\\.", 3);
64+
final String mcVersion = split[0] + "." + split[1];
65+
versions.put("1." + mcVersion, version);
66+
}
67+
} catch (SAXException | XPathExpressionException | ParserConfigurationException | IOException ex) {
68+
TheCommander.LOGGER.error("Failed to resolve latest version from NeoForge metadata URL", ex);
69+
}
70+
71+
return versions;
72+
}
73+
}

0 commit comments

Comments
 (0)