Compare commits
11 Commits
Author | SHA1 | Date |
---|---|---|
|
e2819f22b2 | |
|
bc339caad6 | |
|
78791c6ee2 | |
|
d9e916e7b2 | |
|
f27eb90f82 | |
|
d03f54594a | |
|
537e2fc237 | |
|
23356da050 | |
|
cff2af302f | |
|
c159eea8fd | |
|
117a9c5607 |
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: compliance
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build
|
||||||
|
image: maven:3-openjdk-16
|
||||||
|
commands:
|
||||||
|
- mvn install
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: release
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
- main
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build
|
||||||
|
image: maven:3-openjdk-16
|
||||||
|
commands:
|
||||||
|
- mvn install
|
||||||
|
- name: gitea-release
|
||||||
|
pull: always
|
||||||
|
image: jolheiser/drone-gitea-main:latest
|
||||||
|
settings:
|
||||||
|
token:
|
||||||
|
from_secret: gitea_token
|
||||||
|
base: https://git.canopymc.net
|
||||||
|
files:
|
||||||
|
- "target/MineAlert-*.jar"
|
|
@ -10,7 +10,7 @@ OreAlert monitors specific blocks and sends alerts when a player finds "too many
|
||||||
|
|
||||||
## GriefAlert
|
## GriefAlert
|
||||||
|
|
||||||
TBA
|
GriefAlert monitors specific actions (lighting a fire, placing TnT, etc.) and sends alerts to anyone in-game until a threshold is crossed, after which it ping Discord and then stops notifying for a period of time to avoid spam.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
16
pom.xml
16
pom.xml
|
@ -3,7 +3,7 @@
|
||||||
<groupId>xyz.etztech</groupId>
|
<groupId>xyz.etztech</groupId>
|
||||||
<artifactId>MineAlert</artifactId>
|
<artifactId>MineAlert</artifactId>
|
||||||
<!-- Version is used in plugin.yml -->
|
<!-- Version is used in plugin.yml -->
|
||||||
<version>0.0.2</version>
|
<version>0.0.5</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<!-- Plugin Information -->
|
<!-- Plugin Information -->
|
||||||
|
@ -31,20 +31,24 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
<artifactId>spigot-api</artifactId>
|
<artifactId>spigot-api</artifactId>
|
||||||
<version>1.16.1-R0.1-SNAPSHOT</version>
|
<version>1.16.4-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>xyz.etztech</groupId>
|
<groupId>xyz.etztech</groupId>
|
||||||
<artifactId>javacord</artifactId>
|
<artifactId>javacord</artifactId>
|
||||||
<version>0.2.0</version>
|
<version>0.2.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-lang</groupId>
|
<groupId>commons-lang</groupId>
|
||||||
<artifactId>commons-lang</artifactId>
|
<artifactId>commons-lang</artifactId>
|
||||||
<version>2.6</version>
|
<version>2.6</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>xyz.etztech</groupId>
|
||||||
|
<artifactId>plugin-api</artifactId>
|
||||||
|
<version>1.0.7</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
|
@ -53,8 +57,8 @@
|
||||||
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>etztech-repo</id>
|
<id>canopy-repo</id>
|
||||||
<url>http://repo.etztech.xyz/</url>
|
<url>https://mvn.canopymc.net/</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>mvn-repo</id>
|
<id>mvn-repo</id>
|
||||||
|
|
|
@ -5,16 +5,16 @@ import net.md_5.bungee.api.chat.TextComponent;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.command.ConsoleCommandSender;
|
import org.bukkit.command.ConsoleCommandSender;
|
||||||
|
|
||||||
public enum Color {
|
public class Color {
|
||||||
DEFAULT("#AAAAAA"),
|
public static Color DEFAULT = new Color("#AAAAAA");
|
||||||
ERROR("#F14668"),
|
public static Color ERROR = new Color("#F14668");
|
||||||
INFO("#3298DC"),
|
public static Color INFO = new Color("#3298DC");
|
||||||
PRIMARY("#3273DC");
|
public static Color PRIMARY = new Color("#3273DC");
|
||||||
|
|
||||||
private final String hex;
|
private final String hex;
|
||||||
private final ChatColor chatColor;
|
private final ChatColor chatColor;
|
||||||
|
|
||||||
Color(String hex) {
|
public Color(String hex) {
|
||||||
this.hex = hex;
|
this.hex = hex;
|
||||||
this.chatColor = ChatColor.of(hex);
|
this.chatColor = ChatColor.of(hex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,16 @@ public enum Lang {
|
||||||
NO_PERMISSION("You don't have permission to do that.", Color.ERROR),
|
NO_PERMISSION("You don't have permission to do that.", Color.ERROR),
|
||||||
UNKNOWN_COMMAND("This command wasn't recognized.", Color.ERROR),
|
UNKNOWN_COMMAND("This command wasn't recognized.", Color.ERROR),
|
||||||
PLUGIN_RELOADED("MineAlert reloaded.", Color.INFO),
|
PLUGIN_RELOADED("MineAlert reloaded.", Color.INFO),
|
||||||
ALERT("%s has found %d %s veins.", Color.DEFAULT),
|
WEBHOOK_FAILED("Could not send webhook.", Color.ERROR),
|
||||||
NOT_ENOUGH_ARGS("%s requires %d arguments.", Color.ERROR),
|
PLAYER_NOT_FOUND("Could not find the player specified.", Color.ERROR),
|
||||||
WEBHOOK_FAILED("Could not send webhook.", Color.ERROR);
|
DURATION_PARSE_ERROR("Error parsing time format.", Color.ERROR),
|
||||||
|
ALERT_MUTE("%s's alerts have been muted.", Color.PRIMARY),
|
||||||
|
|
||||||
|
ORE_ALERT("%s has found %d %s veins.", Color.DEFAULT),
|
||||||
|
|
||||||
|
IGNITE_ALERT("%s started a fire.", Color.DEFAULT),
|
||||||
|
TNT_ALERT("%s placed TnT.", Color.DEFAULT),
|
||||||
|
LAVA_ALERT("%s poured lava.", Color.DEFAULT);
|
||||||
|
|
||||||
private final String message;
|
private final String message;
|
||||||
private final Color color;
|
private final Color color;
|
||||||
|
@ -21,8 +28,8 @@ public enum Lang {
|
||||||
this.color = color;
|
this.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
public String getMessage(Object ...args) {
|
||||||
return this.message;
|
return String.format(this.message, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color getColor() {
|
public Color getColor() {
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
package xyz.etztech.minealert;
|
package xyz.etztech.minealert;
|
||||||
|
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import xyz.etztech.Javacord;
|
||||||
|
import xyz.etztech.Webhook;
|
||||||
|
import xyz.etztech.embed.Author;
|
||||||
|
import xyz.etztech.embed.Embed;
|
||||||
|
import xyz.etztech.embed.Field;
|
||||||
|
import xyz.etztech.minealert.commands.AlertMute;
|
||||||
import xyz.etztech.minealert.commands.MainCommand;
|
import xyz.etztech.minealert.commands.MainCommand;
|
||||||
|
import xyz.etztech.minealert.listeners.GriefAlertListener;
|
||||||
import xyz.etztech.minealert.listeners.OreAlertListener;
|
import xyz.etztech.minealert.listeners.OreAlertListener;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class MineAlert extends JavaPlugin {
|
public class MineAlert extends JavaPlugin {
|
||||||
|
@ -19,7 +29,9 @@ public class MineAlert extends JavaPlugin {
|
||||||
|
|
||||||
if (isEnabled()) {
|
if (isEnabled()) {
|
||||||
new MainCommand(this);
|
new MainCommand(this);
|
||||||
|
new GriefAlertListener(this);
|
||||||
new OreAlertListener(this);
|
new OreAlertListener(this);
|
||||||
|
new AlertMute(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,5 +74,77 @@ public class MineAlert extends JavaPlugin {
|
||||||
}
|
}
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param player Player to check
|
||||||
|
* @param muteType Type of alert to check fo
|
||||||
|
* @return True if the player ignores the alert, false otherwise
|
||||||
|
*/
|
||||||
|
static public Boolean hasIgnoreAlertPerm(Player player, MuteType muteType) {
|
||||||
|
return player.hasPermission("minealert.ignore") || player.hasPermission(muteType.getMetadataValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param baseURL Template URL string
|
||||||
|
* @param player Player that generated the alert
|
||||||
|
* @param location Location of the alert
|
||||||
|
* @return Formatted alert URL
|
||||||
|
*/
|
||||||
|
static public String formatAlertURL(String baseURL, Player player, Location location) {
|
||||||
|
return baseURL.replaceAll("\\{username}", player.getName())
|
||||||
|
.replaceAll("\\{x}", String.valueOf(location.getBlockX()))
|
||||||
|
.replaceAll("\\{y}", String.valueOf(location.getBlockY()))
|
||||||
|
.replaceAll("\\{z}", String.valueOf(location.getBlockZ()))
|
||||||
|
.replaceAll("\\{world_name}", location.getWorld().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param location Location to format
|
||||||
|
* @return Location string
|
||||||
|
*/
|
||||||
|
static public String formattedLocation(Location location) {
|
||||||
|
return String.format("%d %d %d", location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param color Color of the embed
|
||||||
|
* @param player Player the alert is for
|
||||||
|
* @param location Location of the alert
|
||||||
|
* @param message Alert message
|
||||||
|
* @param baseURL Template url
|
||||||
|
* @return Alert Embed
|
||||||
|
*/
|
||||||
|
static public Embed buildAlertEmbed(Color color, Player player, Location location, String message, String baseURL) {
|
||||||
|
String usernameURL = formatAlertURL(baseURL, player, location);
|
||||||
|
return new Embed()
|
||||||
|
.color(color.getInt())
|
||||||
|
.description(Javacord.escapeFormat(message))
|
||||||
|
.timestamp(OffsetDateTime.now())
|
||||||
|
.author(new Author(player.getName(),
|
||||||
|
!"".equals(usernameURL) ? usernameURL : "",
|
||||||
|
String.format("https://minotar.net/helm/%s/100.png", player.getName()),
|
||||||
|
""))
|
||||||
|
.addField(new Field("Location", String.format("`%s`", MineAlert.formattedLocation(location))))
|
||||||
|
.addField(new Field("World", String.format("`%s`", location.getWorld().getName())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param webhookURL URL to send the webhook to
|
||||||
|
* @param color Color of the embed
|
||||||
|
* @param player Player the alert is for
|
||||||
|
* @param location Location of the alert
|
||||||
|
* @param message Alert message
|
||||||
|
* @param baseURL Template url
|
||||||
|
*/
|
||||||
|
public void sendWebhook(String webhookURL, Color color, Player player, Location location, String message, String baseURL) {
|
||||||
|
this.getServer().getScheduler().runTaskAsynchronously(this, () -> {
|
||||||
|
Embed embed = buildAlertEmbed(color, player, location, message, baseURL);
|
||||||
|
try {
|
||||||
|
Javacord.sendWebhook(webhookURL, new Webhook("@here", embed));
|
||||||
|
} catch (Exception e) {
|
||||||
|
this.log(Lang.WEBHOOK_FAILED.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package xyz.etztech.minealert;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.metadata.FixedMetadataValue;
|
||||||
|
|
||||||
|
public enum MuteType {
|
||||||
|
OREALERT_MUTE("minealert.ignore.ore"),
|
||||||
|
GRIEFALERT_MUTE("minealert.ignore.grief");
|
||||||
|
|
||||||
|
private final String metadataValue;
|
||||||
|
|
||||||
|
MuteType(String s) {
|
||||||
|
this.metadataValue = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMetadataValue() {
|
||||||
|
return metadataValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMuteStatus(Player player, MineAlert plugin) {
|
||||||
|
player.setMetadata(getMetadataValue(), new FixedMetadataValue(plugin, getMetadataValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeMuteStatus(Player player, MineAlert plugin) {
|
||||||
|
player.removeMetadata(getMetadataValue(), plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasMuteStatus(Player player, MineAlert plugin) {
|
||||||
|
return player.hasMetadata(this.getMetadataValue());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package xyz.etztech.minealert.commands;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import xyz.etztech.minealert.Color;
|
||||||
|
import xyz.etztech.minealert.Lang;
|
||||||
|
import xyz.etztech.minealert.MineAlert;
|
||||||
|
import xyz.etztech.core.command.TickDuration;
|
||||||
|
import xyz.etztech.minealert.MuteType;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AlertMute implements CommandExecutor {
|
||||||
|
MineAlert plugin;
|
||||||
|
|
||||||
|
public AlertMute(MineAlert plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.plugin.getCommand("alertmute").setExecutor(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender commandSender, Command command, String commandPassed, String[] args) {
|
||||||
|
if (!commandSender.hasPermission("minealert.alert_mute")) {
|
||||||
|
Lang.NO_PERMISSION.sms(commandSender);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length == 0) {
|
||||||
|
BaseComponent[] message = new ComponentBuilder()
|
||||||
|
.color(Color.INFO.getChatColor())
|
||||||
|
.append("/")
|
||||||
|
.append(commandPassed)
|
||||||
|
.append(" <player> <duration> [ore|grief]")
|
||||||
|
.create();
|
||||||
|
commandSender.spigot().sendMessage(message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Player player = commandSender.getServer().getPlayer(args[0]);
|
||||||
|
if (player == null) {
|
||||||
|
Lang.PLAYER_NOT_FOUND.sms(commandSender);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TickDuration duration;
|
||||||
|
|
||||||
|
try {
|
||||||
|
duration = TickDuration.parse(args[1]);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Lang.DURATION_PARSE_ERROR.sms(commandSender);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MuteType> mutes = new LinkedList<>();
|
||||||
|
|
||||||
|
if (args.length == 3) {
|
||||||
|
switch (args[2].toLowerCase()) {
|
||||||
|
case "ore":
|
||||||
|
mutes.add(MuteType.OREALERT_MUTE);
|
||||||
|
break;
|
||||||
|
case "grief":
|
||||||
|
mutes.add(MuteType.GRIEFALERT_MUTE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Lang.UNKNOWN_COMMAND.sms(commandSender);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mutes.add(MuteType.GRIEFALERT_MUTE);
|
||||||
|
mutes.add(MuteType.OREALERT_MUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MuteType mute: mutes) {
|
||||||
|
mute.setMuteStatus(player, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
for (MuteType mute: mutes) {
|
||||||
|
mute.removeMuteStatus(player, plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, duration.toTicks());
|
||||||
|
|
||||||
|
Lang.ALERT_MUTE.sms(commandSender, player.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
package xyz.etztech.minealert.listeners;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
import net.md_5.bungee.api.chat.ClickEvent;
|
||||||
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.block.BlockIgniteEvent;
|
||||||
|
import org.bukkit.event.block.BlockPlaceEvent;
|
||||||
|
import org.bukkit.event.player.PlayerBucketEmptyEvent;
|
||||||
|
import xyz.etztech.minealert.Color;
|
||||||
|
import xyz.etztech.minealert.Lang;
|
||||||
|
import xyz.etztech.minealert.MineAlert;
|
||||||
|
import xyz.etztech.minealert.MuteType;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class GriefAlertListener implements Listener {
|
||||||
|
private final MineAlert plugin;
|
||||||
|
private final Map<String, List<Date>> map = new HashMap<>();
|
||||||
|
|
||||||
|
public GriefAlertListener(MineAlert plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.plugin.getServer().getPluginManager().registerEvents(this, this.plugin);
|
||||||
|
this.plugin.getServer().getScheduler().runTaskTimerAsynchronously(this.plugin, this::purge, 0, 20 * 60 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void purge() {
|
||||||
|
Date now = Calendar.getInstance().getTime();
|
||||||
|
int purge = 1000 * 60 * this.plugin.getConfig().getInt("grief.reset", 10);
|
||||||
|
for (Iterator<List<Date>> it = map.values().iterator(); it.hasNext(); ) {
|
||||||
|
List<Date> dates = it.next();
|
||||||
|
dates.removeIf(date -> new Date(date.getTime() + purge).before(now));
|
||||||
|
if (dates.size() == 0) it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAlert(Player player, Location eventLocation, Lang lang) {
|
||||||
|
if (MineAlert.hasIgnoreAlertPerm(player, MuteType.GRIEFALERT_MUTE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String playerName = player.getName();
|
||||||
|
String alert = lang.getMessage(playerName);
|
||||||
|
purge();
|
||||||
|
|
||||||
|
int aboveY = this.plugin.getConfigIntFallback(
|
||||||
|
0,
|
||||||
|
"grief.above_y"
|
||||||
|
);
|
||||||
|
|
||||||
|
int belowY = this.plugin.getConfigIntFallback(
|
||||||
|
255,
|
||||||
|
"grief.below_y"
|
||||||
|
);
|
||||||
|
|
||||||
|
int eventYLevel = eventLocation.getBlockY();
|
||||||
|
if (eventYLevel > belowY || eventYLevel < aboveY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Date> dates = map.getOrDefault(alert, new ArrayList<>());
|
||||||
|
dates.add(new Date());
|
||||||
|
map.put(alert, dates);
|
||||||
|
|
||||||
|
if (MuteType.GRIEFALERT_MUTE.hasMuteStatus(player, plugin)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color color = new Color(plugin.getConfigStringFallback(
|
||||||
|
"#FFA500",
|
||||||
|
"grief.color"
|
||||||
|
));
|
||||||
|
String usernameURL = this.plugin.getConfigStringFallback(
|
||||||
|
"",
|
||||||
|
"grief.url",
|
||||||
|
"url"
|
||||||
|
);
|
||||||
|
|
||||||
|
int threshold = this.plugin.getConfig().getInt("grief.threshold", 5);
|
||||||
|
if (dates.size() <= threshold) {
|
||||||
|
StringBuilder extra = new StringBuilder();
|
||||||
|
if (dates.size() == threshold) {
|
||||||
|
extra.append(" Suppressing more alerts for a while");
|
||||||
|
String webhook = this.plugin.getConfigStringFallback(
|
||||||
|
"",
|
||||||
|
"grief.webhook",
|
||||||
|
"webhook"
|
||||||
|
);
|
||||||
|
if (!"".equals(webhook)) {
|
||||||
|
extra.append(" and pinging Discord");
|
||||||
|
this.plugin.sendWebhook(webhook, color, player, eventLocation, alert, usernameURL);
|
||||||
|
}
|
||||||
|
extra.append("...");
|
||||||
|
}
|
||||||
|
ComponentBuilder builder = new ComponentBuilder()
|
||||||
|
.append(alert + extra.toString()).color(color.getChatColor());
|
||||||
|
if (!"".equals(usernameURL)) {
|
||||||
|
builder.event(new ClickEvent(ClickEvent.Action.OPEN_URL, usernameURL));
|
||||||
|
}
|
||||||
|
sendAlert(builder.create());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendAlert(BaseComponent[] message) {
|
||||||
|
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||||
|
if (player.hasPermission("minealert.alert")) {
|
||||||
|
player.spigot().sendMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onBlockIgnite(BlockIgniteEvent event) {
|
||||||
|
if (
|
||||||
|
event.getPlayer() != null &&
|
||||||
|
this.plugin.getConfig().getStringList("grief.enabled").contains("ignition") &&
|
||||||
|
(
|
||||||
|
event.getCause() == BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL ||
|
||||||
|
event.getCause() == BlockIgniteEvent.IgniteCause.FIREBALL
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
addAlert(event.getPlayer(), event.getBlock().getLocation(), Lang.IGNITE_ALERT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onBlockPlace(BlockPlaceEvent event) {
|
||||||
|
if(
|
||||||
|
this.plugin.getConfig().getStringList("grief.enabled").contains("tnt") &&
|
||||||
|
event.getBlockPlaced().getType() == Material.TNT
|
||||||
|
) {
|
||||||
|
addAlert(event.getPlayer(), event.getBlock().getLocation(), Lang.TNT_ALERT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onBucketEmpty(PlayerBucketEmptyEvent event) {
|
||||||
|
if(
|
||||||
|
this.plugin.getConfig().getStringList("grief.enabled").contains("lava") &&
|
||||||
|
event.getBucket() == Material.LAVA_BUCKET
|
||||||
|
) {
|
||||||
|
addAlert(event.getPlayer(), event.getBlock().getLocation(), Lang.LAVA_ALERT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,35 +1,32 @@
|
||||||
package xyz.etztech.minealert.listeners;
|
package xyz.etztech.minealert.listeners;
|
||||||
|
|
||||||
import net.md_5.bungee.api.ChatColor;
|
|
||||||
import net.md_5.bungee.api.chat.BaseComponent;
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
import net.md_5.bungee.api.chat.ClickEvent;
|
import net.md_5.bungee.api.chat.ClickEvent;
|
||||||
import net.md_5.bungee.api.chat.ComponentBuilder;
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.block.BlockBreakEvent;
|
import org.bukkit.event.block.BlockBreakEvent;
|
||||||
import org.bukkit.event.block.BlockPlaceEvent;
|
import org.bukkit.event.block.BlockPlaceEvent;
|
||||||
import xyz.etztech.Javacord;
|
|
||||||
import xyz.etztech.Webhook;
|
|
||||||
import xyz.etztech.embed.Author;
|
|
||||||
import xyz.etztech.embed.Embed;
|
|
||||||
import xyz.etztech.minealert.Color;
|
import xyz.etztech.minealert.Color;
|
||||||
import xyz.etztech.minealert.Lang;
|
import xyz.etztech.minealert.Lang;
|
||||||
import xyz.etztech.minealert.MineAlert;
|
import xyz.etztech.minealert.MineAlert;
|
||||||
|
import xyz.etztech.minealert.MuteType;
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
public class OreAlertListener implements Listener {
|
public class OreAlertListener implements Listener {
|
||||||
|
|
||||||
private final MineAlert plugin;
|
private final MineAlert plugin;
|
||||||
private static final HashSet<Location> cache = new HashSet<>();
|
private static final Set<Location> cache = Collections.synchronizedSet(new HashSet<>());
|
||||||
private static final Map<Player, List<BlockEvent>> map = new HashMap<>();
|
private static final Map<UUID, List<BlockEvent>> map = new ConcurrentHashMap<>();
|
||||||
private static final Queue<BlockEvent> queue = new ConcurrentLinkedQueue<>();
|
private static final Queue<BlockEvent> queue = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
public OreAlertListener(MineAlert plugin) {
|
public OreAlertListener(MineAlert plugin) {
|
||||||
|
@ -42,7 +39,10 @@ public class OreAlertListener implements Listener {
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onBlockBreak(BlockBreakEvent event) {
|
public void onBlockBreak(BlockBreakEvent event) {
|
||||||
|
if (MineAlert.hasIgnoreAlertPerm(event.getPlayer(), MuteType.OREALERT_MUTE)) return;
|
||||||
if (cache.contains(event.getBlock().getLocation())) return;
|
if (cache.contains(event.getBlock().getLocation())) return;
|
||||||
|
if (!isMaterialTracked(event.getBlock().getType())) return;
|
||||||
|
|
||||||
queue.add(new BlockEvent(event.getPlayer(), event.getBlock().getType(), event.getBlock().getLocation(), true));
|
queue.add(new BlockEvent(event.getPlayer(), event.getBlock().getType(), event.getBlock().getLocation(), true));
|
||||||
|
|
||||||
int radius = this.plugin.getConfig().getInt("ore.radius", 3);
|
int radius = this.plugin.getConfig().getInt("ore.radius", 3);
|
||||||
|
@ -51,7 +51,8 @@ public class OreAlertListener implements Listener {
|
||||||
for (int z = -radius; z < radius; z++) {
|
for (int z = -radius; z < radius; z++) {
|
||||||
if (x == 0 && y == 0 && z == 0) continue;
|
if (x == 0 && y == 0 && z == 0) continue;
|
||||||
Block block = event.getBlock().getRelative(x, y, z);
|
Block block = event.getBlock().getRelative(x, y, z);
|
||||||
if (cache.contains(block.getLocation())) return;
|
if (cache.contains(block.getLocation())) continue;
|
||||||
|
if (!isMaterialTracked(block.getType())) continue;
|
||||||
queue.add(new BlockEvent(event.getPlayer(), block.getType(), block.getLocation(), false));
|
queue.add(new BlockEvent(event.getPlayer(), block.getType(), block.getLocation(), false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +62,18 @@ public class OreAlertListener implements Listener {
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onBlockPlace(BlockPlaceEvent event) {
|
public void onBlockPlace(BlockPlaceEvent event) {
|
||||||
this.plugin.getServer().getScheduler().runTaskAsynchronously(this.plugin, () -> {
|
this.plugin.getServer().getScheduler().runTaskAsynchronously(this.plugin, () -> {
|
||||||
if (isMaterialTracked(event.getBlock().getType())) cache.add(event.getBlock().getLocation());
|
Block eventBlock = event.getBlock();
|
||||||
|
if (isMaterialTracked(eventBlock.getType())) cache.add(eventBlock.getLocation());
|
||||||
|
if (eventBlock.getType() == Material.TNT || (eventBlock.getType().toString().contains("_BED") && eventBlock.getWorld().getEnvironment() == World.Environment.NETHER)) {
|
||||||
|
for (int x = -4; x < 4; x++) {
|
||||||
|
for (int y = -4; y < 4; y++) {
|
||||||
|
for (int z = -4; z < 4; z++) {
|
||||||
|
Block block = eventBlock.getRelative(x, y, z);
|
||||||
|
cache.add(block.getLocation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,20 +92,19 @@ public class OreAlertListener implements Listener {
|
||||||
if (event != null) {
|
if (event != null) {
|
||||||
if (cache.contains(event.getLocation())) continue;
|
if (cache.contains(event.getLocation())) continue;
|
||||||
cache.add(event.getLocation());
|
cache.add(event.getLocation());
|
||||||
if (isMaterialTracked(event.getMaterial())) {
|
if (event.isParent()) {
|
||||||
addStrike(event);
|
addStrike(event);
|
||||||
if (event.isParent()) {
|
check(event);
|
||||||
check(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addStrike(BlockEvent event) {
|
private void addStrike(BlockEvent event) {
|
||||||
List<BlockEvent> events = map.getOrDefault(event.getPlayer(), new ArrayList<>());
|
List<BlockEvent> events = map.getOrDefault(event.getPlayer().getUniqueId(), new ArrayList<>());
|
||||||
|
|
||||||
events.add(event);
|
events.add(event);
|
||||||
map.put(event.getPlayer(), events);
|
map.put(event.getPlayer().getUniqueId(), events);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void check(BlockEvent event) {
|
private void check(BlockEvent event) {
|
||||||
|
@ -113,10 +124,29 @@ public class OreAlertListener implements Listener {
|
||||||
String.format("ore.blocks.%s.ping", blockKey),
|
String.format("ore.blocks.%s.ping", blockKey),
|
||||||
"ore.ping"
|
"ore.ping"
|
||||||
);
|
);
|
||||||
purge(map.getOrDefault(event.getPlayer(), new ArrayList<>()).iterator());
|
int belowY = this.plugin.getConfigIntFallback(
|
||||||
|
255,
|
||||||
|
String.format("ore.blocks.%s.below_y", blockKey),
|
||||||
|
"ore.below_y"
|
||||||
|
);
|
||||||
|
int aboveY = this.plugin.getConfigIntFallback(
|
||||||
|
0,
|
||||||
|
String.format("ore.blocks.%s.above_y", blockKey),
|
||||||
|
"ore.above_y"
|
||||||
|
);
|
||||||
|
purge(map.getOrDefault(event.getPlayer().getUniqueId(), new ArrayList<>()).iterator());
|
||||||
|
|
||||||
|
int yLevel = event.location.getBlockY();
|
||||||
|
if (yLevel > belowY || yLevel < aboveY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MuteType.GRIEFALERT_MUTE.hasMuteStatus(event.getPlayer(), plugin)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int strikes = 0;
|
int strikes = 0;
|
||||||
for (BlockEvent e : map.getOrDefault(event.getPlayer(), new ArrayList<>())) {
|
for (BlockEvent e : map.getOrDefault(event.getPlayer().getUniqueId(), new ArrayList<>())) {
|
||||||
if (e.isParent() && e.getMaterial().name().equals(event.getMaterial().name())) {
|
if (e.isParent() && e.getMaterial().name().equals(event.getMaterial().name())) {
|
||||||
strikes++;
|
strikes++;
|
||||||
}
|
}
|
||||||
|
@ -135,10 +165,10 @@ public class OreAlertListener implements Listener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanup() {
|
private void cleanup() {
|
||||||
for (Iterator<Player> it = map.keySet().iterator(); it.hasNext(); ) {
|
for (Iterator<UUID> it = map.keySet().iterator(); it.hasNext(); ) {
|
||||||
Player player = it.next();
|
UUID playerUUID = it.next();
|
||||||
purge(map.get(player).iterator());
|
purge(map.get(playerUUID).iterator());
|
||||||
if (map.get(player).isEmpty()) {
|
if (map.get(playerUUID).isEmpty()) {
|
||||||
it.remove();
|
it.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,22 +191,23 @@ public class OreAlertListener implements Listener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendAlert(BlockEvent event, int strikes, boolean ping) {
|
private void sendAlert(BlockEvent event, int strikes, boolean ping) {
|
||||||
String message = String.format(Lang.ALERT.getMessage(),
|
String message = Lang.ORE_ALERT.getMessage(event.getPlayer().getName(), strikes, Lang.getMaterialName(event.getMaterial()));
|
||||||
event.getPlayer().getName(), strikes, Lang.getMaterialName(event.getMaterial()));
|
Color color = new Color(plugin.getConfigStringFallback(
|
||||||
String hexColor = this.plugin.getConfigStringFallback(
|
|
||||||
"#AAAAAA",
|
"#AAAAAA",
|
||||||
String.format("ore.blocks.%s.color", Lang.getMaterialKey(event.getMaterial())),
|
String.format("ore.blocks.%s.color", Lang.getMaterialKey(event.getMaterial())),
|
||||||
"ore.color"
|
"ore.color"
|
||||||
);
|
));
|
||||||
String usernameURL = this.plugin.getConfigStringFallback(
|
String usernameURL = this.plugin.getConfigStringFallback(
|
||||||
"",
|
"",
|
||||||
String.format("ore.blocks.%s.url", Lang.getMaterialKey(event.getMaterial())),
|
String.format("ore.blocks.%s.url", Lang.getMaterialKey(event.getMaterial())),
|
||||||
"ore.url",
|
"ore.url",
|
||||||
"url"
|
"url"
|
||||||
).replaceAll("\\{username}", event.getPlayer().getName());
|
);
|
||||||
|
|
||||||
|
usernameURL = MineAlert.formatAlertURL(usernameURL, event.getPlayer(), event.getLocation());
|
||||||
|
|
||||||
ComponentBuilder builder = new ComponentBuilder()
|
ComponentBuilder builder = new ComponentBuilder()
|
||||||
.append(message).color(ChatColor.of(hexColor));
|
.append(message).color(color.getChatColor());
|
||||||
if (!"".equals(usernameURL)) {
|
if (!"".equals(usernameURL)) {
|
||||||
builder.event(new ClickEvent(ClickEvent.Action.OPEN_URL, usernameURL));
|
builder.event(new ClickEvent(ClickEvent.Action.OPEN_URL, usernameURL));
|
||||||
}
|
}
|
||||||
|
@ -195,69 +226,55 @@ public class OreAlertListener implements Listener {
|
||||||
"webhook"
|
"webhook"
|
||||||
);
|
);
|
||||||
if (!"".equals(webhook)) {
|
if (!"".equals(webhook)) {
|
||||||
Embed embed = new Embed()
|
this.plugin.sendWebhook(webhook, color, event.getPlayer(), event.getLocation(), message, usernameURL);
|
||||||
.color(Color.hexToInt(hexColor))
|
|
||||||
.description(message)
|
|
||||||
.timestamp(OffsetDateTime.now())
|
|
||||||
.author(new Author(Javacord.escapeFormat(event.getPlayer().getName()),
|
|
||||||
!"".equals(usernameURL) ? usernameURL : "",
|
|
||||||
String.format("https://minotar.net/helm/%s/100.png", event.getPlayer().getName()),
|
|
||||||
""));
|
|
||||||
this.plugin.getServer().getScheduler().runTaskAsynchronously(this.plugin, () -> {
|
|
||||||
try {
|
|
||||||
Javacord.sendWebhook(webhook, new Webhook(ping ? "@here" : "", embed));
|
|
||||||
} catch (Exception e) {
|
|
||||||
this.plugin.log(Lang.WEBHOOK_FAILED.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HashSet<Location> getCache() {
|
public static Set<Location> getCache() {
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Map<Player, List<BlockEvent>> getMap() {
|
public static Map<UUID, List<BlockEvent>> getMap() {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Queue<BlockEvent> getQueue() {
|
public static Queue<BlockEvent> getQueue() {
|
||||||
return queue;
|
return queue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class BlockEvent {
|
private static class BlockEvent {
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final Material material;
|
private final Material material;
|
||||||
private final Location location;
|
private final Location location;
|
||||||
private final Boolean parent;
|
private final Boolean parent;
|
||||||
private final Date time;
|
private final Date time;
|
||||||
|
|
||||||
BlockEvent(Player player, Material material, Location location, Boolean parent) {
|
BlockEvent(Player player, Material material, Location location, Boolean parent) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.material = material;
|
this.material = material;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.time = Calendar.getInstance().getTime();
|
this.time = Calendar.getInstance().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Material getMaterial() {
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location getLocation() {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean isParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public Player getPlayer() {
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Material getMaterial() {
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Location getLocation() {
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean isParent() {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getTime() {
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,6 +5,28 @@ webhook: ''
|
||||||
# Can use {username} as a placeholder for the player's username
|
# Can use {username} as a placeholder for the player's username
|
||||||
url: 'https://website.com/{username}'
|
url: 'https://website.com/{username}'
|
||||||
|
|
||||||
|
# GriefAlert
|
||||||
|
grief:
|
||||||
|
# How many alerts before temporarily muting
|
||||||
|
threshold: 5
|
||||||
|
# How long before un-muting (in minutes)
|
||||||
|
reset: 10
|
||||||
|
# Discord webhook
|
||||||
|
webhook: ''
|
||||||
|
# Webhook color
|
||||||
|
color: ''
|
||||||
|
# Override
|
||||||
|
url: ''
|
||||||
|
# y level upper limit
|
||||||
|
below_y: 255
|
||||||
|
# y level lower limit
|
||||||
|
above_y: 20
|
||||||
|
# enabled alert types (ignition, tnt, or lava)
|
||||||
|
enabled:
|
||||||
|
- ignition
|
||||||
|
- tnt
|
||||||
|
- lava
|
||||||
|
|
||||||
# OreAlert
|
# OreAlert
|
||||||
ore:
|
ore:
|
||||||
# Radius to search around block for similar blocks in the "vein"
|
# Radius to search around block for similar blocks in the "vein"
|
||||||
|
@ -27,6 +49,10 @@ ore:
|
||||||
webhook: ''
|
webhook: ''
|
||||||
# Override
|
# Override
|
||||||
url: ''
|
url: ''
|
||||||
|
# y level upper limit
|
||||||
|
below_y: 20
|
||||||
|
# y level lower limit
|
||||||
|
above_y: 0
|
||||||
|
|
||||||
# Only blocks listed here will be monitored
|
# Only blocks listed here will be monitored
|
||||||
# Each of the above notify settings can be overridden per-block if needed
|
# Each of the above notify settings can be overridden per-block if needed
|
||||||
|
|
|
@ -9,6 +9,9 @@ commands:
|
||||||
minealert:
|
minealert:
|
||||||
aliases: ma
|
aliases: ma
|
||||||
description: Base command
|
description: Base command
|
||||||
|
alertmute:
|
||||||
|
aliases: am
|
||||||
|
description: Mutes alerts from a user
|
||||||
permissions:
|
permissions:
|
||||||
minealert.admin:
|
minealert.admin:
|
||||||
description: Ability to reload the plugin
|
description: Ability to reload the plugin
|
||||||
|
@ -16,3 +19,19 @@ permissions:
|
||||||
minealert.alert:
|
minealert.alert:
|
||||||
description: Get alerts
|
description: Get alerts
|
||||||
default: op
|
default: op
|
||||||
|
minealert.alert_mute:
|
||||||
|
description: Ability to mute alerts
|
||||||
|
default: op
|
||||||
|
minealert.ignore:
|
||||||
|
description: Ignore all alerts for this player
|
||||||
|
default: op
|
||||||
|
children:
|
||||||
|
minealert.ignore.grief: true
|
||||||
|
minealert.ignore.ore: true
|
||||||
|
minealert.ignore.grief:
|
||||||
|
description: Ignore grief alerts for this player
|
||||||
|
default: op
|
||||||
|
minealert.ignore.ore:
|
||||||
|
description: Ignore ore alerts for this player
|
||||||
|
default: op
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue