Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# LiveMotdManager

LiveMotdManager is a multi-platform Minecraft plugin that keeps your server list message dynamic.
It supports Spigot/Paper/Purpur/Folia servers and BungeeCord/Waterfall (1.16+) or Velocity proxies.
It supports Spigot/Paper/Purpur/Folia servers from 1.16 through 1.21 and BungeeCord/Waterfall (1.16+) or Velocity proxies.
The MOTD can react to time of day, player counts, TPS, real world weather and Discord activity.

## Building
Expand Down Expand Up @@ -36,8 +36,9 @@ See `config.yml` for an example configuration with multiple templates and integr

## Weather

Uses [open-meteo.com](https://open-meteo.com/) APIs with no key required. The plugin performs an
initial API test on startup and logs the result to the console.
Uses the [OpenWeather](https://openweathermap.org/) API. An API key is required and must be
specified in `weather.api-key` in the configuration. The plugin performs an initial API test on
startup and logs the result to the console.

## Discord

Expand Down
2 changes: 2 additions & 0 deletions bungee/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ motd:
weather:
enable: true
city: "Riga"
# Obtain a free API key from https://openweathermap.org/api
api-key: "<YOUR_API_KEY>"
update-interval-minutes: 10

discord:
Expand Down
2 changes: 2 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ motd:
weather:
enable: true
city: "Riga"
# Obtain a free API key from https://openweathermap.org/api
api-key: "<YOUR_API_KEY>"
update-interval-minutes: 10

discord:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public static class MotdRule {
public static class WeatherSettings {
public boolean enable = true;
public String city = "";
public String apiKey = "";
public int updateIntervalMinutes = 10;
}

Expand Down
58 changes: 18 additions & 40 deletions core/src/main/java/com/livemotdmanager/core/WeatherService.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Locale;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
* Fetches real world weather information using open-meteo.com.
* Fetches real world weather information using the OpenWeather API.
*/
public class WeatherService {
private final boolean enabled;
private final String city;
private final String apiKey;
private final int updateIntervalMinutes;
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private volatile String cached = "";
Expand All @@ -28,6 +30,7 @@ public class WeatherService {
public WeatherService(MotdConfig.WeatherSettings settings) {
this.enabled = settings.enable;
this.city = settings.city;
this.apiKey = settings.apiKey;
this.updateIntervalMinutes = settings.updateIntervalMinutes;
}

Expand All @@ -48,49 +51,24 @@ public String getCachedWeather() {
}

private void update() {
if (!enabled || city == null || city.isEmpty()) return;
if (!enabled || city == null || city.isEmpty() || apiKey == null || apiKey.isEmpty()) return;
try {
// Geocode city (encode to handle spaces and special characters)
String encodedCity = URLEncoder.encode(city, StandardCharsets.UTF_8);
String geocodeUrl = String.format("https://geocoding-api.open-meteo.com/v1/search?count=1&name=%s", encodedCity);
HttpRequest geoReq = HttpRequest.newBuilder().uri(URI.create(geocodeUrl)).timeout(Duration.ofSeconds(10)).build();
HttpResponse<String> geoResp = http.send(geoReq, HttpResponse.BodyHandlers.ofString());
JsonObject geoJson = JsonParser.parseString(geoResp.body()).getAsJsonObject();
if (!geoJson.has("results")) return;
JsonObject first = geoJson.getAsJsonArray("results").get(0).getAsJsonObject();
double lat = first.get("latitude").getAsDouble();
double lon = first.get("longitude").getAsDouble();

String weatherUrl = String.format("https://api.open-meteo.com/v1/forecast?latitude=%f&longitude=%f&current_weather=true", lat, lon);
HttpRequest weatherReq = HttpRequest.newBuilder().uri(URI.create(weatherUrl)).timeout(Duration.ofSeconds(10)).build();
HttpResponse<String> weatherResp = http.send(weatherReq, HttpResponse.BodyHandlers.ofString());
JsonObject weatherJson = JsonParser.parseString(weatherResp.body()).getAsJsonObject();
if (!weatherJson.has("current_weather")) return;
JsonObject cur = weatherJson.getAsJsonObject("current_weather");
double temp = cur.get("temperature").getAsDouble();
int code = cur.get("weathercode").getAsInt();
String desc = WeatherCode.describe(code);
cached = String.format("%s %.1f°C", desc, temp);
String url = String.format("https://api.openweathermap.org/data/2.5/weather?q=%s&appid=%s&units=metric", encodedCity, apiKey);
HttpRequest req = HttpRequest.newBuilder().uri(URI.create(url)).timeout(Duration.ofSeconds(10)).build();
HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString());
JsonObject json = JsonParser.parseString(resp.body()).getAsJsonObject();
if (!json.has("weather") || !json.has("main")) return;
JsonObject main = json.getAsJsonObject("main");
double temp = main.get("temp").getAsDouble();
JsonObject first = json.getAsJsonArray("weather").get(0).getAsJsonObject();
String desc = first.get("description").getAsString();
if (!desc.isEmpty()) {
desc = desc.substring(0,1).toUpperCase(Locale.ROOT) + desc.substring(1);
}
cached = String.format(Locale.US, "%s %.1f°C", desc, temp);
} catch (Exception e) {
// ignore but keep cached
}
}

/**
* Maps open-meteo weather codes to short descriptions.
*/
public static class WeatherCode {
public static String describe(int code) {
switch (code) {
case 0: return "Clear";
case 1: case 2: case 3: return "Cloudy";
case 45: case 48: return "Fog";
case 51: case 53: case 55: return "Drizzle";
case 61: case 63: case 65: return "Rain";
case 71: case 73: case 75: return "Snow";
case 95: return "Thunder";
default: return "Weather";
}
}
}
}
2 changes: 1 addition & 1 deletion spigot/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.20.1-R0.1-SNAPSHOT</version>
<version>1.16.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
Expand Down
2 changes: 2 additions & 0 deletions spigot/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ motd:
weather:
enable: true
city: "Riga"
# Obtain a free API key from https://openweathermap.org/api
api-key: "<YOUR_API_KEY>"
update-interval-minutes: 10

discord:
Expand Down
2 changes: 2 additions & 0 deletions velocity/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ motd:
weather:
enable: true
city: "Riga"
# Obtain a free API key from https://openweathermap.org/api
api-key: "<YOUR_API_KEY>"
update-interval-minutes: 10

discord:
Expand Down
Loading