0

Added role adding by reaction

This commit is contained in:
David Panić
2019-07-17 22:07:59 +02:00
parent 632b4bb217
commit 92e6242b7a
11 changed files with 718 additions and 12 deletions

View File

@@ -20,6 +20,8 @@ dependencies {
compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.26'
compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.16'
compile 'com.vdurmont:emoji-java:4.0.0'
}
jar {

View File

@@ -1,8 +1,6 @@
package com.redstoner.redstonerBot;
import com.redstoner.redstonerBot.managers.CommandManager;
import com.redstoner.redstonerBot.managers.DataManager;
import com.redstoner.redstonerBot.managers.DiscordManager;
import com.redstoner.redstonerBot.managers.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -18,8 +16,10 @@ public class Main {
static {
managers.add(new DataManager());
managers.add(new ReactionManager());
managers.add(new DiscordManager());
managers.add(new CommandManager());
managers.add(new InfoManager());
}
public static void main(final String... args) {

View File

@@ -0,0 +1,8 @@
package com.redstoner.redstonerBot;
import net.dv8tion.jda.core.entities.MessageReaction;
import net.dv8tion.jda.core.entities.User;
public interface ReactionHandler {
void handle(String messageId, MessageReaction reaction, User author, boolean added);
}

View File

@@ -0,0 +1,67 @@
package com.redstoner.redstonerBot.listeners;
import com.redstoner.redstonerBot.managers.DataManager;
import com.redstoner.redstonerBot.managers.ReactionManager;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.entities.User;
import net.dv8tion.jda.core.events.message.guild.react.GuildMessageReactionAddEvent;
import net.dv8tion.jda.core.events.message.guild.react.GuildMessageReactionRemoveEvent;
import net.dv8tion.jda.core.hooks.ListenerAdapter;
import net.dv8tion.jda.core.utils.Checks;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Objects;
public class MessageReaction extends ListenerAdapter {
private static final Logger logger = LoggerFactory.getLogger(MessageReaction.class);
@Override
public final void onGuildMessageReactionAdd(GuildMessageReactionAddEvent event) {
Checks.notNull(event, "Event");
final Guild guild = event.getGuild();
final TextChannel channel = event.getChannel();
final User author = event.getUser();
final net.dv8tion.jda.core.entities.MessageReaction reaction = event.getReaction();
final String messageId = event.getMessageId();
final Member self = guild.getSelfMember();
if (!Objects.equals(guild.getId(), DataManager.getConfigValue("guild_id"))) return;
if (author.isBot()) return;
if (!self.hasPermission(channel, Permission.MESSAGE_WRITE)) return;
if (!self.hasPermission(channel, Permission.MESSAGE_EMBED_LINKS)) return;
logger.info("[" + messageId + "] " + author.getAsTag() + " -> + " + reaction.getReactionEmote().getName());
ReactionManager.handle(messageId, reaction, author, true);
}
@Override
public final void onGuildMessageReactionRemove(GuildMessageReactionRemoveEvent event) {
Checks.notNull(event, "Event");
final Guild guild = event.getGuild();
final TextChannel channel = event.getChannel();
final User author = event.getUser();
final net.dv8tion.jda.core.entities.MessageReaction reaction = event.getReaction();
final String messageId = event.getMessageId();
final Member self = guild.getSelfMember();
if (!Objects.equals(guild.getId(), DataManager.getConfigValue("guild_id"))) return;
if (author.isBot()) return;
if (!self.hasPermission(channel, Permission.MESSAGE_WRITE)) return;
if (!self.hasPermission(channel, Permission.MESSAGE_EMBED_LINKS)) return;
logger.info("[" + messageId + "] " + author.getAsTag() + " -> - " + reaction.getReactionEmote().getName());
ReactionManager.handle(messageId, reaction, author, false);
}
}

View File

@@ -5,10 +5,7 @@ import com.redstoner.redstonerBot.Manager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -95,16 +92,42 @@ public class DataManager implements Manager {
return config.get(name);
}
public boolean loadConfig() {
config.clear();
public static boolean setConfigValue(String name, String value) {
String existing = getConfigValue(name);
if (existing != null && existing.equals(value)) return true;
try {
Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement("INSERT INTO config (name, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value=?");
ps.setString(1, name);
ps.setString(2, value);
ps.setString(3, value);
boolean succ = ps.execute();
if (succ) {
config.put(name, value);
}
return succ;
} catch (SQLException e) {
logger.error("SQL error:", e);
return false;
}
}
public static boolean loadConfig() {
try {
Connection conn = getConnection();
ResultSet rs = conn.prepareStatement("SELECT * FROM config").executeQuery();
config.clear();
while (rs.next()) {
String name = rs.getString(2);
String value = rs.getString(3);
String name = rs.getString(1);
String value = rs.getString(2);
config.put(name, value);
}
@@ -115,4 +138,64 @@ public class DataManager implements Manager {
return false;
}
}
public static List<String> getRules() {
try {
Connection conn = getConnection();
ResultSet rs = conn.prepareStatement("SELECT * FROM rules").executeQuery();
List<String> rules = new ArrayList<>();
while (rs.next()) {
rules.add(rs.getString(2));
}
return rules;
} catch (SQLException e) {
logger.error("SQL error:", e);
return null;
}
}
public static Map<String, String> getruleAgreeReactions() {
try {
Connection conn = getConnection();
ResultSet rs = conn.prepareStatement("SELECT * FROM rule_agree_reactions").executeQuery();
Map<String, String> reactions = new HashMap<>();
while (rs.next()) {
reactions.put(rs.getString(3), rs.getString(2));
}
return reactions;
} catch (SQLException e) {
logger.error("SQL error:", e);
return null;
}
}
public static List<Map<String, String>> getOptins() {
try {
Connection conn = getConnection();
ResultSet rs = conn.prepareStatement("SELECT * FROM opt_in").executeQuery();
List<Map<String, String>> optins = new ArrayList<>();
while (rs.next()) {
Map<String, String> optin = new HashMap<>();
optin.put("role_id", rs.getString(3));
optin.put("channel_id", rs.getString(4));
optin.put("emoji", rs.getString(5));
optins.add(optin);
}
return optins;
} catch (SQLException e) {
logger.error("SQL error:", e);
return null;
}
}
}

View File

@@ -2,22 +2,27 @@ package com.redstoner.redstonerBot.managers;
import com.redstoner.redstonerBot.Env;
import com.redstoner.redstonerBot.Manager;
import com.redstoner.redstonerBot.listeners.MessageReaction;
import com.redstoner.redstonerBot.listeners.MessageReceived;
import com.vdurmont.emoji.Emoji;
import com.vdurmont.emoji.EmojiManager;
import net.dv8tion.jda.core.AccountType;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.JDABuilder;
import net.dv8tion.jda.core.entities.Emote;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.security.auth.login.LoginException;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class DiscordManager implements Manager {
private static final Logger logger = LoggerFactory.getLogger(DiscordManager.class);
private JDA jda;
private static JDA jda;
public boolean start() {
logger.info("Discord Manager starting...");
@@ -31,6 +36,7 @@ public class DiscordManager implements Manager {
builder.setToken(Env.TOKEN);
builder.addEventListener(new MessageReceived());
builder.addEventListener(new MessageReaction());
jda = builder.build().awaitReady();
} catch (LoginException e) {
@@ -66,4 +72,18 @@ public class DiscordManager implements Manager {
public static void expireMessage(Message message) {
message.delete().reason("Redstoner Bot message expiry").queueAfter(5, TimeUnit.SECONDS);
}
public static JDA getJda() {
return jda;
}
public static Object getEmoteByName(String name) {
Emoji e = EmojiManager.getForAlias(name);
if (e != null) return e.getUnicode();
List<Emote> emotes = jda.getEmotesByName(name, true);
if (emotes.size() > 0) return emotes.get(0);
return null;
}
}

View File

@@ -0,0 +1,55 @@
package com.redstoner.redstonerBot.managers;
import com.redstoner.redstonerBot.Manager;
import com.redstoner.redstonerBot.reactableMessages.InfoReactableMessageHandler;
import com.redstoner.redstonerBot.reactableMessages.OptInReactableMessageHandler;
import com.redstoner.redstonerBot.reactableMessages.ReactableMessageHandler;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.TextChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class InfoManager implements Manager {
private static final Logger logger = LoggerFactory.getLogger(InfoManager.class);
private List<ReactableMessageHandler> messageHandlers = new ArrayList<>();
@Override
public boolean start() {
logger.info("Info Manager starting...");
JDA jda = DiscordManager.getJda();
Guild guild = jda.getGuildById(DataManager.getConfigValue("guild_id"));
TextChannel channel = guild.getTextChannelById(DataManager.getConfigValue("info_channel_id"));
messageHandlers.add(new InfoReactableMessageHandler(jda, guild, channel));
messageHandlers.add(new OptInReactableMessageHandler(jda, guild, channel));
new Thread(() -> {
for (ReactableMessageHandler handler : messageHandlers) {
handler.checkMessage();
}
}).start();
logger.info("Info Manager started!");
return true;
}
@Override
public boolean stop() {
logger.info("Info Manager stopping...");
for (ReactableMessageHandler handler : messageHandlers) {
ReactionManager.removeHandler(handler.msgId);
}
messageHandlers.clear();
logger.info("Info Manager stopped!");
return true;
}
}

View File

@@ -0,0 +1,49 @@
package com.redstoner.redstonerBot.managers;
import com.redstoner.redstonerBot.Manager;
import com.redstoner.redstonerBot.ReactionHandler;
import net.dv8tion.jda.core.entities.MessageReaction;
import net.dv8tion.jda.core.entities.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
public class ReactionManager implements Manager {
private static final Logger logger = LoggerFactory.getLogger(ReactionManager.class);
private static final Map<String, ReactionHandler> handlers = new HashMap<>();
@Override
public boolean start() {
logger.info("Reaction Manager starting...");
logger.info("Reaction Manager started!");
return true;
}
@Override
public boolean stop() {
logger.info("Reaction Manager stopping...");
logger.info("Reaction Manager stopped!");
return true;
}
public static void setHandler(String messageId, ReactionHandler handler) {
handlers.put(messageId, handler);
}
public static void removeHandler(String messageId) {
handlers.remove(messageId);
}
public static void handle(String messageId, MessageReaction reaction, User author, boolean added) {
ReactionHandler handler = handlers.get(messageId);
if (handler == null) return;
handler.handle(messageId, reaction, author, added);
}
}

View File

@@ -0,0 +1,146 @@
package com.redstoner.redstonerBot.reactableMessages;
import com.redstoner.redstonerBot.ReactionHandler;
import com.redstoner.redstonerBot.managers.DataManager;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.MessageBuilder;
import net.dv8tion.jda.core.entities.*;
import net.dv8tion.jda.core.managers.GuildController;
import java.util.*;
import java.util.function.Consumer;
public class InfoReactableMessageHandler extends ReactableMessageHandler {
static Map<String, String> configNames = new HashMap<>();
static {
configNames.put("msg_id", "info_msg_id");
configNames.put("prefix", "info_prefix");
configNames.put("suffix", "info_suffix");
configNames.put("format", "info_format");
}
public InfoReactableMessageHandler(JDA jda, Guild guild, TextChannel channel) {
super(jda, guild, channel);
}
@Override
Map<String, String> getConfigNames() {
return configNames;
}
@Override
public void send(Consumer<? super Message> onSent) {
String prefix = DataManager.getConfigValue(configNames.get("prefix"));
String suffix = DataManager.getConfigValue(configNames.get("suffix"));
String format = DataManager.getConfigValue(configNames.get("format"));
MessageBuilder msg = new MessageBuilder(prefix + "\n");
List<String> rules = DataManager.getRules();
if (rules != null) {
for (int i = 0; i < rules.size(); i++) {
msg.append(i + 1);
msg.append(". ");
msg.append(rules.get(i));
msg.append("\n");
}
msg.append("\n");
}
Map<String, String> reactions = DataManager.getruleAgreeReactions();
if (reactions != null) {
for (String emojiName : reactions.keySet()) {
String why = reactions.get(emojiName);
String reactionMsg = format;
reactionMsg = reactionMsg.replaceAll("%emoji", ":" + emojiName + ":");
reactionMsg = reactionMsg.replaceAll("%why", why);
msg.append(reactionMsg);
msg.append("\n");
}
}
msg.append("\n");
msg.append(suffix);
channel.sendMessage(msg.build()).queue(onSent);
}
@Override
public ReactionHandler getReactionHandler() {
return (messageId, reaction, author, added) -> {
String emoteName = reaction.getReactionEmote().getName();
String authorTag = author.getAsTag();
if (added) {
logger.info("Info reaction " + emoteName + " by user " + authorTag + " added!");
} else {
logger.info("Info reaction " + emoteName + " by user " + authorTag + " removed!");
}
switch (emoteName) {
case "\uD83D\uDC4C":
case "\uD83D\uDC4D":
case "\u270B":
channel.getMessageById(messageId).queue(message -> {
List<MessageReaction> reactions = message.getReactions();
new Thread(() -> {
int count = 0;
for (MessageReaction mr : reactions) {
List<User> users = mr.getUsers().complete();
if (users.contains(author)) {
count++;
}
}
logger.info("User " + author.getAsTag() + " has " + count + " reactions!");
GuildController guildController = guild.getController();
Member authorMember = guild.getMember(author);
Role rule_agree_role = guild.getRoleById(DataManager.getConfigValue("rule_agree_role_id"));
int requiredReactions = Integer.parseInt(DataManager.getConfigValue("rule_agree_required_reactions"));
if (count >= requiredReactions) {
logger.info("Adding role " + rule_agree_role.getName() + " to user " + authorTag);
guildController.addSingleRoleToMember(authorMember, rule_agree_role).queue();
} else {
logger.info("Removing role " + rule_agree_role.getName() + " from user " + authorTag);
guildController.removeSingleRoleFromMember(authorMember, rule_agree_role).queue();
}
}).start();
});
break;
default:
if (added) {
logger.info("Removing rogue reaction " + emoteName + " by user " + authorTag);
reaction.removeReaction(author).queue();
}
}
};
}
@Override
public Set<String> getReactions() {
Map<String, String> r = DataManager.getruleAgreeReactions();
if (r != null) {
return r.keySet();
} else {
return new HashSet<>();
}
}
}

View File

@@ -0,0 +1,188 @@
package com.redstoner.redstonerBot.reactableMessages;
import com.redstoner.redstonerBot.ReactionHandler;
import com.redstoner.redstonerBot.managers.DataManager;
import com.redstoner.redstonerBot.managers.DiscordManager;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.MessageBuilder;
import net.dv8tion.jda.core.entities.*;
import net.dv8tion.jda.core.managers.GuildController;
import java.util.*;
import java.util.function.Consumer;
public class OptInReactableMessageHandler extends ReactableMessageHandler {
static Map<String, String> configNames = new HashMap<>();
static {
configNames.put("msg_id", "opt_in_msg_id");
configNames.put("prefix", "opt_in_prefix");
configNames.put("suffix", "opt_in_suffix");
configNames.put("channel_format", "opt_in_channel_format");
configNames.put("role_format", "opt_in_role_format");
}
public OptInReactableMessageHandler(JDA jda, Guild guild, TextChannel channel) {
super(jda, guild, channel);
}
@Override
Map<String, String> getConfigNames() {
return configNames;
}
@Override
public void send(Consumer<? super Message> onSent) {
String prefix = DataManager.getConfigValue(configNames.get("prefix"));
String suffix = DataManager.getConfigValue(configNames.get("suffix"));
String channelFormat = DataManager.getConfigValue(configNames.get("channel_format"));
String roleFormat = DataManager.getConfigValue(configNames.get("role_format"));
MessageBuilder msg = new MessageBuilder(prefix + "\n");
List<Map<String, String>> optIns = DataManager.getOptins();
if (optIns != null) {
List<Map<String, String>> channelOptIns = new ArrayList<>();
List<Map<String, String>> roleOptIns = new ArrayList<>();
for (Map<String, String> optIn : optIns) {
if (optIn.get("channel_id") != null) {
channelOptIns.add(optIn);
} else {
roleOptIns.add(optIn);
}
}
if (channelOptIns.size() > 0) {
for (Map<String, String> optIn : channelOptIns) {
TextChannel optinChannel = guild.getTextChannelById(optIn.get("channel_id"));
String channelMention = optinChannel.getAsMention();
String emoji = optIn.get("emoji");
Object resolvedEmoji = DiscordManager.getEmoteByName(emoji);
if (resolvedEmoji instanceof String) emoji = (String) resolvedEmoji;
if (resolvedEmoji instanceof Emote) emoji = ((Emote) resolvedEmoji).getAsMention();
String reactionMsg = channelFormat;
reactionMsg = reactionMsg.replaceAll("%emoji", emoji);
reactionMsg = reactionMsg.replaceAll("%channel", channelMention);
msg.append(reactionMsg);
msg.append("\n");
}
msg.append("\n");
}
if (roleOptIns.size() > 0) {
for (Map<String, String> optIn : roleOptIns) {
Role optinRole = guild.getRoleById(optIn.get("role_id"));
String roleMention = optinRole.getAsMention();
String emoji = optIn.get("emoji");
Object resolvedEmoji = DiscordManager.getEmoteByName(emoji);
if (resolvedEmoji instanceof String) emoji = (String) resolvedEmoji;
if (resolvedEmoji instanceof Emote) emoji = ((Emote) resolvedEmoji).getAsMention();
String reactionMsg = roleFormat;
reactionMsg = reactionMsg.replaceAll("%emoji", emoji);
reactionMsg = reactionMsg.replaceAll("%role", roleMention);
msg.append(reactionMsg);
msg.append("\n");
}
msg.append("\n");
}
}
msg.append(suffix);
channel.sendMessage(msg.build()).queue(onSent);
}
@Override
public ReactionHandler getReactionHandler() {
return (messageId, reaction, author, added) -> {
String emoteName = reaction.getReactionEmote().getName();
String authorTag = author.getAsTag();
if (added) {
logger.info("Opt in reaction " + emoteName + " by user " + authorTag + " added!");
} else {
logger.info("Opt in reaction " + emoteName + " by user " + authorTag + " removed!");
}
List<Map<String, String>> optIns = DataManager.getOptins();
if (optIns == null) return;
String roleId = null;
for (Map<String, String> optIn : optIns) {
String rId = optIn.get("role_id");
String e = optIn.get("emoji");
if (rId != null && e != null) {
if (reaction.getReactionEmote().isEmote()) {
if (e.equals(emoteName)) {
roleId = rId;
break;
}
} else {
Object discordEmote = DiscordManager.getEmoteByName(e);
if (discordEmote != null) {
if (discordEmote instanceof String && discordEmote.equals(emoteName)) {
roleId = rId;
break;
} else if (discordEmote instanceof Emote && ((Emote) discordEmote).getName().equals(emoteName)) {
roleId = rId;
break;
}
}
}
}
}
if (roleId != null) {
GuildController guildController = guild.getController();
Member authorMember = guild.getMember(author);
Role optInRole = guild.getRoleById(roleId);
if (added) {
logger.info("Adding role " + optInRole.getName() + " to user " + authorTag);
guildController.addSingleRoleToMember(authorMember, optInRole).queue();
} else {
logger.info("Removing role " + optInRole.getName() + " from user " + authorTag);
guildController.removeSingleRoleFromMember(authorMember, optInRole).queue();
}
} else if (added) {
logger.info("Removing rogue reaction " + emoteName + " by user " + authorTag);
reaction.removeReaction(author).queue();
}
};
}
@Override
public Set<String> getReactions() {
Set<String> reactions = new HashSet<>();
List<Map<String, String>> optIns = DataManager.getOptins();
if (optIns != null) {
for (Map<String, String> optIn : optIns) {
reactions.add(optIn.get("emoji"));
}
}
return reactions;
}
}

View File

@@ -0,0 +1,88 @@
package com.redstoner.redstonerBot.reactableMessages;
import com.redstoner.redstonerBot.ReactionHandler;
import com.redstoner.redstonerBot.managers.DataManager;
import com.redstoner.redstonerBot.managers.DiscordManager;
import com.redstoner.redstonerBot.managers.ReactionManager;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.entities.Emote;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.entities.TextChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
public abstract class ReactableMessageHandler {
static final Logger logger = LoggerFactory.getLogger(ReactableMessageHandler.class);
public String msgId;
final JDA jda;
final Guild guild;
final TextChannel channel;
ReactableMessageHandler(JDA jda, Guild guild, TextChannel channel) {
this.jda = jda;
this.guild = guild;
this.channel = channel;
this.msgId = DataManager.getConfigValue(getConfigNames().get("msg_id"));
}
abstract Map<String, String> getConfigNames();
public abstract void send(Consumer<? super Message> onSent);
public abstract ReactionHandler getReactionHandler();
public abstract Set<String> getReactions();
public void checkMessage() {
logger.info("[Check] Checking message ID: " + msgId);
try {
checkAfterMessageFound(channel.getMessageById(msgId).complete());
} catch (Exception e) {
logger.info("[Check] Message with ID " + msgId + " does not exist, sending new one!");
send(this::checkAfterMessageFound);
}
}
private void checkAfterMessageFound(Message message) {
msgId = message.getId();
DataManager.setConfigValue(getConfigNames().get("msg_id"), msgId);
logger.info("[Check] Found message with ID: " + msgId);
checkReactions(message, getReactions());
ReactionManager.setHandler(msgId, getReactionHandler());
logger.info("[Check] Set reaction handler for message ID " + msgId);
}
private void checkReactions(Message message, Set<String> reactions) {
if (reactions != null) {
for (String emojiName : reactions) {
try {
logger.info("Adding reaction emote: " + emojiName);
Object reactionEmoji = DiscordManager.getEmoteByName(emojiName);
if (reactionEmoji instanceof Emote) {
message.addReaction((Emote) reactionEmoji).queue();
} else if (reactionEmoji instanceof String) {
message.addReaction((String) reactionEmoji).queue();
} else {
logger.error("Could not find emote for name '" + emojiName + "'");
}
} catch (Exception e) {
logger.error("Error adding reaction: ", e);
}
}
}
}
}