0

Initial commit of Redstoner-Bot.

This commit is contained in:
Logan Fick
2019-03-10 18:28:57 -04:00
commit 3c88f2e9f2
25 changed files with 1350 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
package com.redstoner.discordbot;
import com.redstoner.discordbot.commands.LinkAccount;
import com.redstoner.discordbot.commands.LinkStatus;
import com.redstoner.discordbot.commands.Nickname;
import com.redstoner.discordbot.tasks.ForumNotificationTask;
import com.redstoner.discordbot.tasks.GuildRankSyncTask;
import com.redstoner.discordbot.utils.RankUtil;
import dev.logal.logalbot.LogalBot;
import dev.logal.logalbot.commands.CommandManager;
import dev.logal.logalbot.utils.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
public final class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] arguments) {
logger.info("Beginning startup of Redstoner-Bot...");
RankUtil.registerRank("visitor", "470250026925293568");
RankUtil.registerRank("member", "470233551284076555");
RankUtil.registerRank("builder", "470233521919885317");
RankUtil.registerRank("trusted", "470233420501352469");
RankUtil.registerRank("helper", "518547428690100227");
RankUtil.registerRank("trainingmod", "470233311541985320");
RankUtil.registerRank("mod", "470233311541985320");
RankUtil.registerRank("admin", "470233150514003969");
RankUtil.registerRank("+lead", "516775853976453140");
RankUtil.registerRank("+donor", "470237589052325888");
RankUtil.registerRank("+donorplus", "470258077648683008");
RankUtil.registerRank("+retired", "537816902584631301");
RankUtil.registerRank("+dev", "508329092110614529");
logger.info("Starting LogalBot...");
LogalBot.start(false);
logger.info("Unregistering unnecessary commands...");
CommandManager.unregisterCommand("help");
CommandManager.unregisterCommand("about");
logger.info("Registering Redstoner commands...");
CommandManager.registerCommand("linkaccount", new LinkAccount(), false);
CommandManager.registerCommandAlias("link", "linkaccount");
CommandManager.registerCommandAlias("lnk", "linkaccount");
CommandManager.registerCommand("nickname", new Nickname(), true);
CommandManager.registerCommandAlias("nick", "nickname");
CommandManager.registerCommand("linkstatus", new LinkStatus(), false);
CommandManager.registerCommandAlias("ls", "linkstatus");
logger.info("Starting tasks...");
Scheduler.scheduleAtFixedRate(new ForumNotificationTask(), 1, 1, TimeUnit.MINUTES);
Scheduler.scheduleAtFixedRate(new GuildRankSyncTask(), 1, 5, TimeUnit.MINUTES);
logger.info("Redstoner-Bot setup complete.");
}
}

View File

@@ -0,0 +1,93 @@
package com.redstoner.discordbot.commands;
import com.redstoner.discordbot.utils.RankUtil;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.DataManager;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public final class LinkAccount implements Command {
private static final Logger logger = LoggerFactory.getLogger(LinkAccount.class);
private final String databaseUsername = System.getenv("MYSQL_USERNAME");
private final String databasePassword = System.getenv("MYSQL_PASSWORD");
private final String tokenDatabaseName = System.getenv("MYSQL_TOKEN_DATABASE_NAME");
@Override
public CommandResponse execute(String[] arguments, Member executor, TextChannel channel) {
if (DataManager.getUserValue(executor, "minecraftUUID") != null) {
return new CommandResponse("no_entry_sign", "Sorry " + executor.getAsMention() + ", but your account is already linked.").setDeletionDelay(
10, TimeUnit.SECONDS);
}
String token = arguments[0];
if (token.length() != 8 || !token.matches("[A-Za-z0-9]+")) {
return new CommandResponse("no_entry_sign", "Sorry " + executor.getAsMention() + ", but that doesn't appear to be a token.").setDeletionDelay(
10, TimeUnit.SECONDS);
}
UUID uuid;
try {
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/" + tokenDatabaseName, databaseUsername, databasePassword);
Statement statement = connection.createStatement();
ResultSet results = statement.executeQuery("SELECT uuid FROM discord WHERE token='" + token + "' AND used=0");
if (!results.next()) {
return new CommandResponse(
"no_entry_sign", "Sorry " + executor.getAsMention() + ", but that token doesn't appear to be valid.").setDeletionDelay(
10, TimeUnit.SECONDS);
}
uuid = UUID.fromString(UUID
.fromString(results
.getString(1)
.replaceFirst(
"(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)",
"$1-$2-$3-$4-$5"
))
.toString());
statement = connection.createStatement();
statement.execute("UPDATE discord SET used=1 WHERE token='" + token + "'");
connection.close();
} catch (Throwable exception) {
logger.error(
"An error occurred while trying to look up the token for " + executor.getEffectiveName() + " (" + executor.getUser().getId() + ")!",
exception
);
return new CommandResponse(
"sos", "Sorry " + executor.getAsMention() + ", but an error occurred while trying to look up your token.").setDeletionDelay(
10, TimeUnit.SECONDS);
}
DataManager.setUserValue(executor, "minecraftUUID", uuid.toString());
try {
RankUtil.syncRanks(executor);
} catch (Throwable exception) {
logger.error(
"An error occurred while trying to sync the ranks for " + executor.getEffectiveName() + " (" + executor.getUser().getId() + ")!",
exception
);
return new CommandResponse(
"exclamation",
executor.getAsMention() + ", your account has been successfully linked, but your ranks could not be synchronized at this time."
).setDeletionDelay(10, TimeUnit.SECONDS);
}
return new CommandResponse("link", executor.getAsMention() + ", your account has been successfully linked and your ranks have been synchronized.");
}
}

View File

@@ -0,0 +1,18 @@
package com.redstoner.discordbot.commands;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.DataManager;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class LinkStatus implements Command {
@Override
public CommandResponse execute(String[] arguments, Member executor, TextChannel channel) {
if (DataManager.getUserValue(executor, "minecraftUUID") == null) {
return new CommandResponse("negative_squared_cross_mark", executor.getAsMention() + ", your account is not linked.");
} else {
return new CommandResponse("ballot_box_with_check", executor.getAsMention() + ", your account is linked.");
}
}
}

View File

@@ -0,0 +1,96 @@
package com.redstoner.discordbot.commands;
import com.redstoner.discordbot.utils.RankUtil;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.DataManager;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
import java.util.concurrent.TimeUnit;
public final class Nickname implements Command {
@Override
public CommandResponse execute(String[] arguments, Member executor, TextChannel channel) {
Guild guild = channel.getGuild();
if (arguments.length == 0) {
if (DataManager.getUserValue(executor, "minecraftUUID") == null) {
return new CommandResponse(
"no_entry_sign",
"Sorry " + executor.getAsMention() + ", but your nickname could not be reset because your account is not linked."
).setDeletionDelay(10, TimeUnit.SECONDS);
} else {
DataManager.setUserValue(executor, "nickname", "");
try {
RankUtil.syncRanks(executor);
return new CommandResponse("white_check_mark", executor.getAsMention() + ", your nickname has been reset.");
} catch (Throwable exception) {
return new CommandResponse(
"sos",
"Sorry " + executor.getAsMention() + ", but your nickname could not be reset because an error occurred while trying to look up your name."
).setDeletionDelay(10, TimeUnit.SECONDS);
}
}
}
String userID = arguments[0].replaceFirst("<@[!]?([0-9]*)>", "$1");
Member target;
try {
target = guild.getMemberById(userID);
} catch (Throwable exception) {
target = null;
}
if (target == null) {
if (DataManager.getUserValue(executor, "minecraftUUID") == null) {
return new CommandResponse(
"no_entry_sign",
"Sorry " + executor.getAsMention() + ", but your nickname could not be reset because your account is not linked."
).setDeletionDelay(10, TimeUnit.SECONDS);
} else {
DataManager.setUserValue(executor, "nickname", arguments[0]);
try {
RankUtil.syncRanks(executor);
return new CommandResponse("white_check_mark", executor.getAsMention() + ", your nickname has been set.");
} catch (Throwable exception) {
return new CommandResponse(
"sos",
"Sorry " + executor.getAsMention() + ", but your nickname could not be set because an error occurred while trying to look up your name."
).setDeletionDelay(10, TimeUnit.SECONDS);
}
}
} else {
if (DataManager.getUserValue(target, "minecraftUUID") == null) {
return new CommandResponse(
"no_entry_sign",
"Sorry " + executor.getAsMention() + ", but the nickname for that user could not be set because their account is not linked."
).setDeletionDelay(10, TimeUnit.SECONDS);
}
if (arguments.length == 1) {
try {
DataManager.setUserValue(target, "nickname", "");
RankUtil.syncRanks(target);
return new CommandResponse("white_check_mark", executor.getAsMention() + " has reset the nickname for " + target.getAsMention() + ".");
} catch (Throwable exception) {
return new CommandResponse(
"sos",
"Sorry " + executor.getAsMention() + ", but the nickname for that user could not be reset because an error occurred while trying to look up their name."
).setDeletionDelay(10, TimeUnit.SECONDS);
}
} else {
try {
DataManager.setUserValue(target, "nickname", arguments[1]);
RankUtil.syncRanks(target);
return new CommandResponse("white_check_mark", executor.getAsMention() + " has set the nickname for " + target.getAsMention() + ".");
} catch (Throwable exception) {
return new CommandResponse(
"sos",
"Sorry " + executor.getAsMention() + ", but the nickname for that user could not be set because an error occurred while trying to look up their name."
).setDeletionDelay(10, TimeUnit.SECONDS);
}
}
}
}
}

View File

@@ -0,0 +1,178 @@
package com.redstoner.discordbot.tasks;
import dev.logal.logalbot.LogalBot;
import dev.logal.logalbot.utils.DataManager;
import dev.logal.logalbot.utils.StringUtil;
import net.dv8tion.jda.core.entities.Guild;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;
import java.net.URL;
public final class ForumNotificationTask implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(ForumNotificationTask.class);
private static final Guild redstonerGuild = LogalBot.getJDA().getGuildById("470229570310766593");
private static final String announcementsChannel = "489957437978181635";
private static final String staffGeneralChannel = "472452678367313941";
private static final String staffDevelopmentChannel = "491050574092042241";
@Override
public void run() {
try {
// Blog posts
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
InputStream stream = new URL("https://redstoner.com/blog.atom").openStream();
Document doc = docBuilder.parse(stream);
Element topEntry = (Element) doc.getDocumentElement().getElementsByTagName("entry").item(0);
String title = topEntry.getElementsByTagName("title").item(0).getFirstChild().getNodeValue();
Element authorBlock = (Element) topEntry.getElementsByTagName("author").item(0);
String author = authorBlock.getElementsByTagName("name").item(0).getFirstChild().getNodeValue();
String id = topEntry.getElementsByTagName("id").item(0).getFirstChild().getNodeValue();
String link = topEntry.getElementsByTagName("link").item(0).getAttributes().getNamedItem("href").getNodeValue();
if (DataManager.getGuildValue(redstonerGuild, "knownAnnouncement:" + id) == null) {
LogalBot
.getJDA()
.getTextChannelById(announcementsChannel)
.sendMessage(
":loudspeaker: New blog post **" + StringUtil.sanitize(title) + "** authored by *" + StringUtil.sanitize(author) + "*\n" + link)
.queue();
DataManager.setGuildValue(redstonerGuild, "knownAnnouncement:" + id, "true");
}
} catch (Throwable exception) {
logger.error("An error occurred while posting new blog posts to Discord!", exception);
}
try {
// Blames
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
InputStream stream = new URL("https://redstoner.com/forums/21.atom").openStream();
Document doc = docBuilder.parse(stream);
Element topEntry = (Element) doc.getDocumentElement().getElementsByTagName("entry").item(0);
String title = topEntry.getElementsByTagName("title").item(0).getFirstChild().getNodeValue();
Element authorBlock = (Element) topEntry.getElementsByTagName("author").item(0);
String author = authorBlock.getElementsByTagName("name").item(0).getFirstChild().getNodeValue();
String id = topEntry.getElementsByTagName("id").item(0).getFirstChild().getNodeValue();
String link = topEntry.getElementsByTagName("link").item(0).getAttributes().getNamedItem("href").getNodeValue();
if (DataManager.getGuildValue(redstonerGuild, "knownBlame:" + id) == null) {
LogalBot
.getJDA()
.getTextChannelById(staffGeneralChannel)
.sendMessage(
":loudspeaker: New blame **" + StringUtil.sanitize(title) + "** authored by *" + StringUtil.sanitize(author) + "*\n" + link)
.queue();
DataManager.setGuildValue(redstonerGuild, "knownBlame:" + id, "true");
}
} catch (Throwable exception) {
logger.error("An error occurred while posting new blames to Discord!", exception);
}
try {
// Appeals
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
InputStream stream = new URL("https://redstoner.com/forums/5.atom").openStream();
Document doc = docBuilder.parse(stream);
Element topEntry = (Element) doc.getDocumentElement().getElementsByTagName("entry").item(0);
String title = topEntry.getElementsByTagName("title").item(0).getFirstChild().getNodeValue();
Element authorBlock = (Element) topEntry.getElementsByTagName("author").item(0);
String author = authorBlock.getElementsByTagName("name").item(0).getFirstChild().getNodeValue();
String id = topEntry.getElementsByTagName("id").item(0).getFirstChild().getNodeValue();
String link = topEntry.getElementsByTagName("link").item(0).getAttributes().getNamedItem("href").getNodeValue();
if (DataManager.getGuildValue(redstonerGuild, "knownAppeal:" + id) == null) {
LogalBot
.getJDA()
.getTextChannelById(staffGeneralChannel)
.sendMessage(
":loudspeaker: New appeal **" + StringUtil.sanitize(title) + "** authored by *" + StringUtil.sanitize(author) + "*\n" + link)
.queue();
DataManager.setGuildValue(redstonerGuild, "knownAppeal:" + id, "true");
}
} catch (Throwable exception) {
logger.error("An error occurred while posting new appeals to Discord!", exception);
}
try {
// Problems & Bugs
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
InputStream stream = new URL("https://redstoner.com/forums/11.atom").openStream();
Document doc = docBuilder.parse(stream);
Element topEntry = (Element) doc.getDocumentElement().getElementsByTagName("entry").item(0);
String title = topEntry.getElementsByTagName("title").item(0).getFirstChild().getNodeValue();
Element authorBlock = (Element) topEntry.getElementsByTagName("author").item(0);
String author = authorBlock.getElementsByTagName("name").item(0).getFirstChild().getNodeValue();
String id = topEntry.getElementsByTagName("id").item(0).getFirstChild().getNodeValue();
String link = topEntry.getElementsByTagName("link").item(0).getAttributes().getNamedItem("href").getNodeValue();
if (DataManager.getGuildValue(redstonerGuild, "knownBugReport:" + id) == null) {
LogalBot
.getJDA()
.getTextChannelById(staffDevelopmentChannel)
.sendMessage(":loudspeaker: New bug report **" + StringUtil.sanitize(title) + "** authored by *" + StringUtil.sanitize(
author) + "*\n" + link)
.queue();
DataManager.setGuildValue(redstonerGuild, "knownBugReport:" + id, "true");
}
} catch (Throwable exception) {
logger.error("An error occurred while posting new bug reports to Discord!", exception);
}
try {
// Website Problems & Bugs
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
InputStream stream = new URL("https://redstoner.com/forums/4.atom").openStream();
Document doc = docBuilder.parse(stream);
Element topEntry = (Element) doc.getDocumentElement().getElementsByTagName("entry").item(0);
String title = topEntry.getElementsByTagName("title").item(0).getFirstChild().getNodeValue();
Element authorBlock = (Element) topEntry.getElementsByTagName("author").item(0);
String author = authorBlock.getElementsByTagName("name").item(0).getFirstChild().getNodeValue();
String id = topEntry.getElementsByTagName("id").item(0).getFirstChild().getNodeValue();
String link = topEntry.getElementsByTagName("link").item(0).getAttributes().getNamedItem("href").getNodeValue();
if (DataManager.getGuildValue(redstonerGuild, "knownWebsiteBugReport:" + id) == null) {
LogalBot
.getJDA()
.getTextChannelById(staffDevelopmentChannel)
.sendMessage(":loudspeaker: New website bug report **" + StringUtil.sanitize(title) + "** authored by *" + StringUtil.sanitize(
author) + "*\n" + link)
.queue();
DataManager.setGuildValue(redstonerGuild, "knownWebsiteBugReport:" + id, "true");
}
} catch (Throwable exception) {
logger.error("An error occurred while posting new website bug reports to Discord!", exception);
}
}
}

View File

@@ -0,0 +1,30 @@
package com.redstoner.discordbot.tasks;
import com.redstoner.discordbot.utils.RankUtil;
import dev.logal.logalbot.LogalBot;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class GuildRankSyncTask implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(GuildRankSyncTask.class);
private static final String redstonerGuild = "470229570310766593";
@Override
public void run() {
Guild guild = LogalBot.getJDA().getGuildById(redstonerGuild);
for (Member member : guild.getMembers()) {
try {
RankUtil.syncRanks(member);
} catch (Throwable exception) {
logger.error(
"An error occurred while trying to sync the ranks for " + member.getUser().getName() + " (" + member.getUser().getId() + ")!",
exception
);
}
}
}
}

View File

@@ -0,0 +1,87 @@
package com.redstoner.discordbot.utils;
import dev.logal.logalbot.utils.DataManager;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.Role;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
public final class RankUtil {
private static final Logger logger = LoggerFactory.getLogger(RankUtil.class);
private static final String databaseUsername = System.getenv("MYSQL_USERNAME");
private static final String databasePassword = System.getenv("MYSQL_PASSWORD");
private static final String pexDatabaseName = System.getenv("MYSQL_PEX_DATABASE_NAME");
private static final HashMap<String, String> roleDictionary = new HashMap<>();
public static void registerRank(String rankName, String roleID) {
roleDictionary.put(rankName, roleID);
}
public static void syncRanks(Member member) throws SQLException, ClassNotFoundException {
String uuid = DataManager.getUserValue(member, "minecraftUUID");
if (uuid == null) {
return;
}
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/" + pexDatabaseName, databaseUsername, databasePassword);
Statement statement = connection.createStatement();
ResultSet results = statement.executeQuery("SELECT parent FROM permissions_inheritance WHERE child='" + uuid + "'");
Guild guild = member.getGuild();
ArrayList<Role> rolesToAdd = new ArrayList<>();
while (results.next()) {
String parent = results.getString(1);
Role role = null;
if (roleDictionary.containsKey(parent)) {
role = guild.getRoleById(roleDictionary.get(parent));
}
if (role != null) {
rolesToAdd.add(role);
}
}
String nickname = DataManager.getUserValue(member, "nickname");
if (nickname != null && !nickname.equals("")) {
guild.getController().setNickname(member, nickname).reason("Redstoner Name Synchronization").queue();
} else {
statement = connection.createStatement();
results = statement.executeQuery("SELECT value FROM permissions WHERE type=1 AND permission='name' AND name='" + uuid + "'");
if (!results.next()) {
logger.warn(member.getUser().getName() + " (" + member
.getUser()
.getId() + ") has a linked account, but has no name according to PermissionsEx. Leaving Discord nickname as is.");
} else {
String name = results.getString(1);
if (!member.getEffectiveName().equals(name)) {
guild.getController().setNickname(member, name).reason("Redstoner Name Synchronization").queue();
}
}
}
connection.close();
if (rolesToAdd.size() == 0) {
logger.warn(member.getUser().getName() + " (" + member
.getUser()
.getId() + ") has a linked account, but has no ranks according to PermissionsEx. Assuming they are visitor rank.");
rolesToAdd.add(guild.getRoleById(roleDictionary.get("visitor")));
}
if (!CollectionUtils.isEqualCollection(member.getRoles(), rolesToAdd)) {
logger.info("Updating roles for " + member.getUser().getName() + " (" + member.getUser().getId() + ").");
guild.getController().modifyMemberRoles(member, rolesToAdd).reason("Redstoner Rank Synchronization").queue();
}
}
}