diff --git a/pom.xml b/pom.xml index dbf27603..8bb101a0 100644 --- a/pom.xml +++ b/pom.xml @@ -35,10 +35,31 @@ 8 + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + bungeecore + + + dv8tion + m2-dv8tion + https://m2.dv8tion.net/releases + + + steamwar @@ -61,5 +82,23 @@ system ${main.basedir}/lib/BungeeTabListPlus.jar + + net.dv8tion + JDA + 4.3.0_299 + compile + + + club.minnced + opus-java + + + + + org.projectlombok + lombok + 1.18.20 + provided + \ No newline at end of file diff --git a/src/de/steamwar/bungeecore/BungeeCore.java b/src/de/steamwar/bungeecore/BungeeCore.java index f0a8bb79..8a457e29 100644 --- a/src/de/steamwar/bungeecore/BungeeCore.java +++ b/src/de/steamwar/bungeecore/BungeeCore.java @@ -19,6 +19,8 @@ package de.steamwar.bungeecore; +import de.steamwar.bungeecore.bot.SteamwarDiscordBot; +import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig; import de.steamwar.bungeecore.commands.*; import de.steamwar.bungeecore.comms.SpigotReceiver; import de.steamwar.bungeecore.listeners.*; @@ -124,6 +126,7 @@ public class BungeeCore extends Plugin { new ResourcereloadCommand(); new ListCommand(); new StatCommand(); + new VerifyCommand(); if(!EVENT_MODE){ new WebregisterCommand(); @@ -141,6 +144,7 @@ public class BungeeCore extends Plugin { new EventStarter(); new SessionManager(); new SpigotReceiver(); + new SteamwarDiscordBot(); new TablistManager(); getProxy().getScheduler().schedule(this, () -> { @@ -254,6 +258,7 @@ public class BungeeCore extends Plugin { ); ArenaMode.init(config.getSection("games")); + SteamwarDiscordBotConfig.loadConfig(config.getSection("discord")); final Configuration servers = config.getSection("servers"); for(final String serverName : servers.getKeys()){ diff --git a/src/de/steamwar/bungeecore/bot/AuthManager.java b/src/de/steamwar/bungeecore/bot/AuthManager.java new file mode 100644 index 00000000..d574c460 --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/AuthManager.java @@ -0,0 +1,78 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot; + +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig; +import de.steamwar.bungeecore.sql.SteamwarUser; +import net.dv8tion.jda.api.MessageBuilder; +import net.dv8tion.jda.api.entities.Emoji; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.interactions.components.ActionRow; +import net.dv8tion.jda.api.interactions.components.Button; +import net.md_5.bungee.api.scheduler.ScheduledTask; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class AuthManager { + + private static final Map TOKENS = new HashMap<>(); + private static final Random rand = new Random(); + + public static String createDiscordAuthToken(Member member) { + if(TOKENS.containsValue(member.getIdLong())) return null; + + byte[] randBytes = new byte[16]; + rand.nextBytes(randBytes); + randBytes[0] = 'D'; + randBytes[1] = 'C'; + String code = Base64.getEncoder().encodeToString(randBytes); + + TOKENS.put(code, member.getIdLong()); + BungeeCore.log("Created Discord Auth-Token: " + code + " for: " + member.getUser().getAsTag()); + ScheduledTask[] task = new ScheduledTask[1]; + task[0] = BungeeCore.get().getProxy().getScheduler().schedule(BungeeCore.get(), () -> { + TOKENS.remove(code); + task[0].cancel(); + }, 10, 10, TimeUnit.MINUTES); + return code; + } + + public static Member connectAuth(SteamwarUser user, String code) { + if (TOKENS.containsKey(code)) { + Member member = SteamwarDiscordBot.instance().getJda().getGuildById(SteamwarDiscordBotConfig.GUILD).retrieveMemberById(TOKENS.get(code).longValue()).complete(); + if(member == null) return null; + user.setDiscordId(member.getId()); + MessageBuilder builder = new MessageBuilder(); + builder.setContent(":white_check_mark: Dein Discord Konto wurde mit **" + user.getUserName() + "** verknüpft"); + builder.setActionRows(ActionRow.of(Button.success("tada", Emoji.fromUnicode("U+1F389")), Button.danger("invalid", "Ich war das nicht"))); + + member.getUser().openPrivateChannel().complete().sendMessage(builder.build()).complete(); + if(member.getNickname() == null) { + member.getGuild().modifyNickname(member, user.getUserName()).complete(); + } + TOKENS.remove(code); + return member; + } else { + return null; + } + } +} diff --git a/src/de/steamwar/bungeecore/bot/SteamwarDiscordBot.java b/src/de/steamwar/bungeecore/bot/SteamwarDiscordBot.java new file mode 100644 index 00000000..03dceb9a --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/SteamwarDiscordBot.java @@ -0,0 +1,114 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot; + +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig; +import de.steamwar.bungeecore.bot.events.EventManager; +import de.steamwar.bungeecore.bot.listeners.AnnouncementListener; +import de.steamwar.bungeecore.bot.listeners.DiscordAuthListener; +import de.steamwar.bungeecore.bot.listeners.DiscordTicketListener; +import de.steamwar.bungeecore.bot.listeners.RolesInteractionButtonListener; +import de.steamwar.bungeecore.bot.util.DiscordTicketMessage; +import de.steamwar.bungeecore.bot.util.DiscordRolesMessage; +import de.steamwar.bungeecore.bot.util.DiscordRulesMessage; +import de.steamwar.bungeecore.sql.Event; +import lombok.Getter; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.OnlineStatus; +import net.dv8tion.jda.api.entities.Activity; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.utils.MemberCachePolicy; +import net.md_5.bungee.api.ProxyServer; + +import javax.security.auth.login.LoginException; +import java.util.concurrent.TimeUnit; + +public class SteamwarDiscordBot { + + private static SteamwarDiscordBot INSTANCE; + + public static SteamwarDiscordBot instance() { + return INSTANCE; + } + + @Getter + private AnnouncementListener announcementListener; + + @Getter + private final JDA jda; + + public SteamwarDiscordBot() { + INSTANCE = this; + JDABuilder builder = JDABuilder.createDefault(SteamwarDiscordBotConfig.TOKEN); + builder.setStatus(OnlineStatus.ONLINE); + builder.setMemberCachePolicy(MemberCachePolicy.ONLINE); + try { + jda = builder.build(); + } catch (LoginException e) { + throw new SecurityException("Could not Login: " + SteamwarDiscordBotConfig.TOKEN, e); + } + try { + jda.awaitReady(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + activity(); + EventManager.update(); + ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), () -> { + activity(); + EventManager.update(); + }, 30, 30, TimeUnit.SECONDS); + DiscordRolesMessage.sendMessage(); + DiscordRulesMessage.sendMessage(); + DiscordTicketMessage.sendMessage(); + + new RolesInteractionButtonListener(); + new DiscordTicketListener(); + new DiscordAuthListener(); + announcementListener = new AnnouncementListener(); + } + + private int index = 0; + + private void activity() { + switch (index) { + case 0: + Event event = Event.get(); + if (event != null) { + jda.getPresence().setActivity(Activity.competing("dem Event " + event.getEventName())); + } else { + jda.getPresence().setActivity(Activity.playing("auf SteamWar.de")); + } + break; + case 1: + jda.getPresence().setActivity(Activity.playing("mit " + BungeeCore.get().getProxy().getOnlineCount() + " Spielern")); + index = 0; + return; + } + index++; + } + + public void addListener(ListenerAdapter listenerAdapter) { + jda.addEventListener(listenerAdapter); + } +} diff --git a/src/de/steamwar/bungeecore/bot/config/DiscordRole.java b/src/de/steamwar/bungeecore/bot/config/DiscordRole.java new file mode 100644 index 00000000..68b57bee --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/config/DiscordRole.java @@ -0,0 +1,39 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot.config; + +import lombok.AllArgsConstructor; +import lombok.Data; +import net.dv8tion.jda.api.entities.Emoji; +import net.dv8tion.jda.api.interactions.components.Button; +import net.dv8tion.jda.api.interactions.components.ButtonStyle; + +@Data +@AllArgsConstructor +public class DiscordRole { + + private String emoji; + private String label; + private String roleId; + + public Button toButton() { + return Button.of(ButtonStyle.SECONDARY, roleId, label, Emoji.fromUnicode(emoji)); + } +} diff --git a/src/de/steamwar/bungeecore/bot/config/DiscordRulesLink.java b/src/de/steamwar/bungeecore/bot/config/DiscordRulesLink.java new file mode 100644 index 00000000..4f2ebe01 --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/config/DiscordRulesLink.java @@ -0,0 +1,36 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot.config; + +import lombok.AllArgsConstructor; +import lombok.Data; +import net.dv8tion.jda.api.interactions.components.Button; + +@Data +@AllArgsConstructor +public class DiscordRulesLink { + + private String label; + private String link; + + public Button toButton() { + return Button.link(link, label); + } +} diff --git a/src/de/steamwar/bungeecore/bot/config/DiscordTicketType.java b/src/de/steamwar/bungeecore/bot/config/DiscordTicketType.java new file mode 100644 index 00000000..5ba4c654 --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/config/DiscordTicketType.java @@ -0,0 +1,41 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot.config; + +import lombok.AllArgsConstructor; +import lombok.Data; +import net.dv8tion.jda.api.entities.Emoji; +import net.dv8tion.jda.api.interactions.components.Button; +import net.dv8tion.jda.api.interactions.components.ButtonStyle; + +@Data +@AllArgsConstructor +public class DiscordTicketType { + + private String key; + private String emoji; + private String label; + private String color; + private String preMessage; + + public Button toButton() { + return Button.of(ButtonStyle.valueOf(color), key, Emoji.fromUnicode(emoji)).withLabel(label); + } +} diff --git a/src/de/steamwar/bungeecore/bot/config/SteamwarDiscordBotConfig.java b/src/de/steamwar/bungeecore/bot/config/SteamwarDiscordBotConfig.java new file mode 100644 index 00000000..14f1efbd --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/config/SteamwarDiscordBotConfig.java @@ -0,0 +1,108 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot.config; + +import de.steamwar.bungeecore.sql.UserGroup; +import net.md_5.bungee.config.Configuration; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SteamwarDiscordBotConfig { + + public static String TOKEN; + public static String GUILD; + public static String ANNOUNCEMENTS_CHANNEL; + public static String EVENTS_CHANNEL; + public static String ROLES_CHANNEL; + public static String ROLES_BASE_MESSAGE; + public static String ROLES_ADDED; + public static String ROLES_REMOVED; + public static List ROLES; + public static String RULES_CHANNEL; + public static String RULES_TITLE; + public static List RULES_RULES; + public static List RULES_LINKS; + public static String TICKET_CATEGORY; + public static String TICKET_CHANNEL; + public static String TICKET_MESSAGE; + public static String TICKET_CREATED; + public static String TICKET_LOG; + public static Map TICKET_TYPES; + public static Map RANKS; + + public static void loadConfig(Configuration config) { + TOKEN = config.getString("token"); + GUILD = config.getString("guild"); + ANNOUNCEMENTS_CHANNEL = config.getString("announcements-channel"); + EVENTS_CHANNEL = config.getString("events-channel"); + Configuration rolesSection = config.getSection("roles-claim"); + ROLES_CHANNEL = rolesSection.getString("channel"); + ROLES_BASE_MESSAGE = rolesSection.getString("base"); + ROLES_ADDED = rolesSection.getString("added"); + ROLES_REMOVED = rolesSection.getString("removed"); + ROLES = new ArrayList<>(); + + for (String roles : rolesSection.getSection("roles").getKeys()) { + Configuration role = rolesSection.getSection("roles").getSection(roles); + ROLES.add(new DiscordRole(role.getString("emoji"), + role.getString("label"), + role.getString("roleId"))); + } + + Configuration rulesSection = config.getSection("rules"); + RULES_CHANNEL = rulesSection.getString("channel"); + RULES_TITLE = rulesSection.getString("title"); + RULES_RULES = rulesSection.getStringList("rules"); + + RULES_LINKS = new ArrayList<>(); + + for (String links : rulesSection.getSection("links").getKeys()) { + Configuration link = rulesSection.getSection("links").getSection(links); + RULES_LINKS.add(new DiscordRulesLink(link.getString("label"), + link.getString("url"))); + } + + Configuration ticketSection = config.getSection("tickets"); + TICKET_CATEGORY = ticketSection.getString("category"); + TICKET_CHANNEL = ticketSection.getString("channel"); + TICKET_MESSAGE = ticketSection.getString("message"); + TICKET_CREATED = ticketSection.getString("created"); + TICKET_LOG = ticketSection.getString("log"); + TICKET_TYPES = new HashMap<>(); + + for (String types : ticketSection.getSection("types").getKeys()) { + Configuration type = ticketSection.getSection("types").getSection(types); + TICKET_TYPES.put(types, new DiscordTicketType(types, + type.getString("emoji"), + type.getString("label"), + type.getString("color"), + type.getString("pre"))); + } + + RANKS = new HashMap<>(); + Configuration ranksSections = config.getSection("ranks"); + for (String type : ranksSections.getKeys()) { + RANKS.put(UserGroup.getUsergroup(type), ranksSections.getString(type)); + } + } +} diff --git a/src/de/steamwar/bungeecore/bot/events/EventManager.java b/src/de/steamwar/bungeecore/bot/events/EventManager.java new file mode 100644 index 00000000..53daa79e --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/events/EventManager.java @@ -0,0 +1,128 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot.events; + +import de.steamwar.bungeecore.bot.SteamwarDiscordBot; +import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig; +import de.steamwar.bungeecore.sql.Event; +import de.steamwar.bungeecore.sql.EventFight; +import de.steamwar.bungeecore.sql.Team; +import de.steamwar.bungeecore.sql.TeamTeilnahme; +import lombok.experimental.UtilityClass; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.MessageBuilder; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.TextChannel; + +import java.awt.*; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.stream.Collectors; + +@UtilityClass +public class EventManager { + + private Message message; + private TextChannel textChannel; + + static { + textChannel = SteamwarDiscordBot.instance().getJda().getGuildById(SteamwarDiscordBotConfig.GUILD).getTextChannelById(SteamwarDiscordBotConfig.EVENTS_CHANNEL); + assert textChannel != null; + if(textChannel.hasLatestMessage()) { + textChannel.getIterableHistory().complete().forEach(message -> message.delete().complete()); + } + } + + public void update() { + if (Event.get() == null) { + updateComing(); + } else { + updateCurrent(); + } + } + + private void updateComing() { + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setColor(Color.GRAY); + embedBuilder.setTitle("Zukünftige Events"); + embedBuilder.setAuthor("SteamWar", "https://www.steamwar.de"); + + Timestamp now = Timestamp.from(Instant.now()); + Event.getComing().forEach(event -> { + StringBuilder st = new StringBuilder(); + if (event.getDeadline().after(now)) { + st.append("Deadline: \n"); + } + st.append("Start: "); + String teilname = TeamTeilnahme.getTeams(event.getEventID()).stream().map(Team::getTeamKuerzel).collect(Collectors.joining(", ")); + if (!teilname.isEmpty()) { + st.append("\nAngemeldete Teams: ").append(teilname); + } + embedBuilder.addField(event.getEventName(), st.toString(), false); + }); + + MessageBuilder messageBuilder = new MessageBuilder(); + messageBuilder.setEmbeds(embedBuilder.build()); + if (message == null) { + message = textChannel.sendMessage(messageBuilder.build()).complete(); + } else { + message.editMessage(messageBuilder.build()).complete(); + } + } + + private void updateCurrent() { + Event event = Event.get(); + if (event == null) return; + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setColor(Color.GRAY); + embedBuilder.setTitle("Event: " + event.getEventName()); + embedBuilder.setAuthor("SteamWar", "https://www.steamwar.de"); + + Instant now = Instant.now(); + EventFight.getEvent(event.getEventID()).forEach(eventFight -> { + Team teamBlue = Team.get(eventFight.getTeamBlue()); + Team teamRed = Team.get(eventFight.getTeamRed()); + + StringBuilder st = new StringBuilder(); + st.append("Fightbeginn: "); + if(now.isAfter(eventFight.getStartTime().toInstant().plus(35, ChronoUnit.MINUTES))) { + st.append("\n"); + if (eventFight.getErgebnis() == 1) { + st.append("Sieg ").append(teamBlue.getTeamKuerzel()); + } else if (eventFight.getErgebnis() == 2) { + st.append("Sieg ").append(teamRed.getTeamKuerzel()); + } else { + st.append("Unentschieden"); + } + } + embedBuilder.addField(teamBlue.getTeamKuerzel() + " vs. " + teamRed.getTeamKuerzel(), st.toString(), true); + }); + + MessageBuilder messageBuilder = new MessageBuilder(); + messageBuilder.setEmbeds(embedBuilder.build()); + if (message == null) { + message = textChannel.sendMessage(messageBuilder.build()).complete(); + } else { + message.editMessage(messageBuilder.build()).complete(); + } + } + +} diff --git a/src/de/steamwar/bungeecore/bot/listeners/AnnouncementListener.java b/src/de/steamwar/bungeecore/bot/listeners/AnnouncementListener.java new file mode 100644 index 00000000..f3803d2c --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/listeners/AnnouncementListener.java @@ -0,0 +1,50 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot.listeners; + +import de.steamwar.bungeecore.Message; +import de.steamwar.bungeecore.bot.SteamwarDiscordBot; +import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig; +import net.dv8tion.jda.api.MessageBuilder; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; +import org.jetbrains.annotations.NotNull; + +public class AnnouncementListener extends BasicDiscordListener { + + @Override + public void onGuildMessageReceived(@NotNull GuildMessageReceivedEvent event) { + if (!event.getChannel().getId().equals(SteamwarDiscordBotConfig.ANNOUNCEMENTS_CHANNEL)) { + return; + } + if (event.getAuthor().isBot()) { + return; + } + Message.broadcast("ALERT", event.getMessage().getContentRaw()); + } + + public void announce(String message) { + TextChannel textChannel = SteamwarDiscordBot.instance().getJda().getGuildById(SteamwarDiscordBotConfig.GUILD).getTextChannelById(SteamwarDiscordBotConfig.ANNOUNCEMENTS_CHANNEL); + assert textChannel != null; + MessageBuilder messageBuilder = new MessageBuilder(); + messageBuilder.append(message.replace("&", "")); + textChannel.sendMessage(messageBuilder.build()).complete(); + } +} diff --git a/src/de/steamwar/bungeecore/bot/listeners/BasicDiscordListener.java b/src/de/steamwar/bungeecore/bot/listeners/BasicDiscordListener.java new file mode 100644 index 00000000..1c8a5ffd --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/listeners/BasicDiscordListener.java @@ -0,0 +1,30 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot.listeners; + +import de.steamwar.bungeecore.bot.SteamwarDiscordBot; +import net.dv8tion.jda.api.hooks.ListenerAdapter; + +public abstract class BasicDiscordListener extends ListenerAdapter { + + BasicDiscordListener() { + SteamwarDiscordBot.instance().addListener(this); + } +} diff --git a/src/de/steamwar/bungeecore/bot/listeners/DiscordAuthListener.java b/src/de/steamwar/bungeecore/bot/listeners/DiscordAuthListener.java new file mode 100644 index 00000000..514b193f --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/listeners/DiscordAuthListener.java @@ -0,0 +1,59 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot.listeners; + +import de.steamwar.bungeecore.bot.AuthManager; +import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig; +import de.steamwar.bungeecore.sql.SteamwarUser; +import net.dv8tion.jda.api.entities.ChannelType; +import net.dv8tion.jda.api.events.interaction.GenericComponentInteractionCreateEvent; +import net.dv8tion.jda.api.interactions.InteractionType; +import org.jetbrains.annotations.NotNull; + +public class DiscordAuthListener extends BasicDiscordListener { + + @Override + public void onGenericComponentInteractionCreate(@NotNull GenericComponentInteractionCreateEvent event) { + if(event.getType() == InteractionType.COMPONENT) { + if(event.getChannel().getId().equals(SteamwarDiscordBotConfig.RULES_CHANNEL) && event.getComponentId().equals("auth")) { + String authMessage = AuthManager.createDiscordAuthToken(event.getMember()); + if(authMessage != null) { + event.reply("Gebe innerhalb der nächsten 10 Minuten ``/verify " + authMessage + "`` auf dem Minecraft Server ein").setEphemeral(true).complete(); + } else { + event.reply("Du hast bereits einen Code am laufen").setEphemeral(true).complete(); + } + } + + if(event.getComponentId().equals("tada") && event.getChannelType() == ChannelType.PRIVATE) { + event.reply(":tada:").setEphemeral(false).complete(); + } + + if(event.getComponentId().equals("invalid") && event.getChannelType() == ChannelType.PRIVATE) { + SteamwarUser user = SteamwarUser.get(event.getUser().getIdLong()); + if(user == null) { + event.reply(":question: Da ist keine verknüpfung?").setEphemeral(false).complete(); + } else { + user.setDiscordId(null); + event.reply(":x: Die Verknüpfung wurde beendet").setEphemeral(false).complete(); + } + } + } + } +} diff --git a/src/de/steamwar/bungeecore/bot/listeners/DiscordTicketListener.java b/src/de/steamwar/bungeecore/bot/listeners/DiscordTicketListener.java new file mode 100644 index 00000000..7c18c6fb --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/listeners/DiscordTicketListener.java @@ -0,0 +1,145 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot.listeners; + +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.Message; +import de.steamwar.bungeecore.bot.config.DiscordTicketType; +import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig; +import de.steamwar.bungeecore.sql.SteamwarUser; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.MessageBuilder; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.events.interaction.GenericComponentInteractionCreateEvent; +import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; +import net.dv8tion.jda.api.interactions.InteractionType; +import net.dv8tion.jda.api.interactions.components.ActionRow; +import net.dv8tion.jda.api.interactions.components.Button; +import net.dv8tion.jda.api.interactions.components.selections.SelectionMenu; +import net.md_5.bungee.api.chat.ClickEvent; +import org.jetbrains.annotations.NotNull; + +import java.awt.Color; +import java.time.Instant; +import java.util.LinkedList; + +public class DiscordTicketListener extends BasicDiscordListener { + + @Override + public void onGenericComponentInteractionCreate(@NotNull GenericComponentInteractionCreateEvent event) { + if(event.getType() == InteractionType.COMPONENT && event.getChannelType() == ChannelType.TEXT && event.getTextChannel().getParent() != null && event.getTextChannel().getParent().getId().equals(SteamwarDiscordBotConfig.TICKET_CATEGORY)) { + if(event.getTextChannel().getId().equals(SteamwarDiscordBotConfig.TICKET_CHANNEL) && SteamwarDiscordBotConfig.TICKET_TYPES.containsKey(event.getComponentId())) { + DiscordTicketType ticketType = SteamwarDiscordBotConfig.TICKET_TYPES.get(event.getComponentId()); + Category ct = event.getGuild().getCategoryById(SteamwarDiscordBotConfig.TICKET_CATEGORY); + SteamwarUser swUser = SteamwarUser.get(event.getUser().getIdLong()); + TextChannel ticketChannel = ct.createTextChannel((swUser == null?event.getUser().getName():swUser.getUserName()) + "-" + event.getComponentId() + "-" + System.currentTimeMillis() % 1000).complete(); + ticketChannel.createPermissionOverride(event.getMember()).setAllow(Permission.VIEW_CHANNEL, + Permission.MESSAGE_WRITE, + Permission.MESSAGE_ATTACH_FILES, + Permission.MESSAGE_ADD_REACTION, + Permission.MESSAGE_READ, + Permission.MESSAGE_EMBED_LINKS, + Permission.MESSAGE_HISTORY).complete(); + ticketChannel.getManager().setTopic(event.getUser().getId()).complete(); + + MessageBuilder messageBuilder = new MessageBuilder(); + EmbedBuilder builder = new EmbedBuilder(); + builder.setDescription(ticketType.getPreMessage()); + builder.setTitle("Steamwar Ticket"); + builder.setColor(Color.GREEN); + + Button closeButton = Button.danger("close-" + ticketChannel.getName(), "Schliesen").withEmoji(Emoji.fromUnicode("U+26A0")); + messageBuilder.setEmbeds(builder.build()); + messageBuilder.setActionRows(ActionRow.of(closeButton)); + + ticketChannel.sendMessage(messageBuilder.build()).complete(); + event.reply(SteamwarDiscordBotConfig.TICKET_CREATED.replace("%channel%", ticketChannel.getAsMention())).setEphemeral(true).complete(); + Message.team("DISCORD_TICKET_NEW", ticketChannel.getName()); + } else if(event.getComponentId().startsWith("close-")) { + TextChannel logChannel = event.getGuild().getTextChannelById(SteamwarDiscordBotConfig.TICKET_LOG); + + MessageBuilder builder = new MessageBuilder(); + StringBuilder stringBuilder = new StringBuilder(); + + new LinkedList<>(event.getTextChannel().getIterableHistory().complete()).descendingIterator().forEachRemaining(message -> { + if(message.getAuthor().isSystem() || message.getAuthor().isBot()) return; + stringBuilder.append(" ") + .append("**") + .append(message.getAuthor().getName()) + .append("**: ") + .append(message.getContentRaw()); + if(!message.getAttachments().isEmpty()) { + stringBuilder.append("\n") + .append("Files: ").append("\n"); + message.getAttachments().forEach(attachment -> stringBuilder.append(attachment.getUrl()).append("\n")); + } + + stringBuilder.append("\n"); + }); + + stringBuilder.append("\n"); + stringBuilder.append(" ") + .append("**").append(event.getUser().getName()).append("**: ") + .append("Ticket geschlossen"); + + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setColor(Color.GREEN); + User user = event.getJDA().retrieveUserById(event.getTextChannel().getTopic()).complete(); + SteamwarUser swuser = SteamwarUser.get(user.getIdLong()); + + embedBuilder.setAuthor(user.getName(), swuser==null?"https://steamwar.de/":("https://steamwar.de/users/" + swuser.getUserName().toLowerCase() + "/"), user.getAvatarUrl()); + embedBuilder.setTimestamp(Instant.now()); + embedBuilder.setTitle(event.getTextChannel().getName()); + embedBuilder.setDescription(stringBuilder); + + builder.setEmbeds(embedBuilder.build()); + + logChannel.sendMessage(builder.build()).complete(); + + Message.team("DISCORD_TICKET_CLOSED", event.getTextChannel().getName()); + event.getTextChannel().delete().reason("Closed").complete(); + } + } + } + + @Override + public void onGuildMessageReceived(@NotNull GuildMessageReceivedEvent event) { + if(event.getChannel().getParent() != null && event.getChannel().getParent().getId().equals(SteamwarDiscordBotConfig.TICKET_CATEGORY)) { + if(!event.getChannel().getId().equals(SteamwarDiscordBotConfig.TICKET_CHANNEL) && !event.getChannel().getId().equals(SteamwarDiscordBotConfig.TICKET_LOG)) { + BungeeCore.get().getProxy().getPlayers().forEach(player -> { + if(event.getAuthor().isBot() || event.getAuthor().isSystem()) return; + SteamwarUser user = SteamwarUser.get(player); + if(user.getDiscordId() == null) { + if(user.getUserGroup().isTeamGroup()) { + Message.sendPrefixless("DISCORD_TICKET_MESSAGE", player, "Zur nachricht", new ClickEvent(ClickEvent.Action.OPEN_URL, event.getMessage().getJumpUrl()), event.getChannel().getName(), event.getAuthor().getName(), event.getMessage().getContentRaw()); + } + } else { + if(event.getAuthor().getId().equals(user.getDiscordId())) return; + Member member = event.getGuild().retrieveMemberById(user.getDiscordId()).complete(); + if(member.hasPermission(event.getChannel().getManager().getChannel(), Permission.MESSAGE_WRITE)) { + Message.sendPrefixless("DISCORD_TICKET_MESSAGE", player, "Zur nachricht", new ClickEvent(ClickEvent.Action.OPEN_URL, event.getMessage().getJumpUrl()), event.getChannel().getName(), event.getAuthor().getName(), event.getMessage().getContentRaw()); + } + } + }); + } + } + } +} diff --git a/src/de/steamwar/bungeecore/bot/listeners/RolesInteractionButtonListener.java b/src/de/steamwar/bungeecore/bot/listeners/RolesInteractionButtonListener.java new file mode 100644 index 00000000..035cd77c --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/listeners/RolesInteractionButtonListener.java @@ -0,0 +1,42 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot.listeners; + +import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig; +import net.dv8tion.jda.api.entities.ChannelType; +import net.dv8tion.jda.api.events.interaction.GenericComponentInteractionCreateEvent; +import net.dv8tion.jda.api.interactions.InteractionType; +import org.jetbrains.annotations.NotNull; + +public class RolesInteractionButtonListener extends BasicDiscordListener { + + @Override + public void onGenericComponentInteractionCreate(@NotNull GenericComponentInteractionCreateEvent event) { + if(event.getType() == InteractionType.COMPONENT && event.getChannelType() == ChannelType.TEXT && event.getTextChannel().getId().equals(SteamwarDiscordBotConfig.ROLES_CHANNEL) && SteamwarDiscordBotConfig.ROLES.stream().anyMatch(discordRole -> discordRole.getRoleId().equals(event.getComponentId()))) { + if (event.getMember().getRoles().stream().anyMatch(role -> role.getId().equals(event.getComponentId()))) { + event.getGuild().removeRoleFromMember(event.getMember(), event.getGuild().getRoleById(event.getComponentId())).complete(); + event.reply(SteamwarDiscordBotConfig.ROLES_REMOVED.replace("%role%", event.getGuild().getRoleById(event.getComponentId()).getAsMention())).setEphemeral(true).complete(); + } else { + event.getGuild().addRoleToMember(event.getMember(), event.getGuild().getRoleById(event.getComponentId())).complete(); + event.reply(SteamwarDiscordBotConfig.ROLES_ADDED.replace("%role%", event.getGuild().getRoleById(event.getComponentId()).getAsMention())).setEphemeral(true).complete(); + } + } + } +} diff --git a/src/de/steamwar/bungeecore/bot/util/DiscordRanks.java b/src/de/steamwar/bungeecore/bot/util/DiscordRanks.java new file mode 100644 index 00000000..9b976cef --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/util/DiscordRanks.java @@ -0,0 +1,67 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot.util; + +import de.steamwar.bungeecore.bot.SteamwarDiscordBot; +import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig; +import de.steamwar.bungeecore.sql.SteamwarUser; +import de.steamwar.bungeecore.sql.UserGroup; +import lombok.experimental.UtilityClass; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Role; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@UtilityClass +public class DiscordRanks { + + public void update(SteamwarUser steamwarUser) { + if (steamwarUser.getDiscordId() == null) { + return; + } + Guild guild = SteamwarDiscordBot.instance().getJda().getGuildById(SteamwarDiscordBotConfig.GUILD); + Member member = guild.retrieveMemberById(steamwarUser.getDiscordId()).complete(); + if (member == null) { + return; + } + + List roleList = member.getRoles(); + Set strings = new HashSet<>(SteamwarDiscordBotConfig.RANKS.values()); + String needed = SteamwarDiscordBotConfig.RANKS.get(steamwarUser.getUserGroup()); + for (Role role : roleList) { + if (!strings.contains(role.getId())) { + continue; + } + if (role.getId().equals(needed)) { + needed = ""; + continue; + } + guild.removeRoleFromMember(member, role).complete(); + } + + if (!needed.isEmpty()) { + guild.addRoleToMember(member, guild.getRoleById(needed)).complete(); + } + } +} diff --git a/src/de/steamwar/bungeecore/bot/util/DiscordRolesMessage.java b/src/de/steamwar/bungeecore/bot/util/DiscordRolesMessage.java new file mode 100644 index 00000000..deb65ed2 --- /dev/null +++ b/src/de/steamwar/bungeecore/bot/util/DiscordRolesMessage.java @@ -0,0 +1,51 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.bungeecore.bot.util; + +import de.steamwar.bungeecore.bot.SteamwarDiscordBot; +import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig; +import net.dv8tion.jda.api.MessageBuilder; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.interactions.components.ActionRow; +import net.dv8tion.jda.api.interactions.components.Button; +import net.dv8tion.jda.api.requests.restaction.MessageAction; + +import java.util.ArrayList; +import java.util.List; + +public class DiscordRolesMessage { + + public static void sendMessage() { + TextChannel channel = SteamwarDiscordBot.instance().getJda().getGuildById(SteamwarDiscordBotConfig.GUILD).getTextChannelById(SteamwarDiscordBotConfig.ROLES_CHANNEL); + assert channel != null; + if(channel.hasLatestMessage()) { + channel.getIterableHistory().complete().forEach(message -> message.delete().complete()); + } + + MessageBuilder builder = new MessageBuilder(); + builder.setContent(SteamwarDiscordBotConfig.ROLES_BASE_MESSAGE); + List