diff --git a/images/NoAvatar.png b/images/NoAvatar.png
index fb9b1ea..14fd0ca 100644
--- a/images/NoAvatar.png
+++ b/images/NoAvatar.png
Binary files differ
diff --git a/lists/copypastas.txt b/lists/copypastas.txt
index 863731d..5842b63 100644
--- a/lists/copypastas.txt
+++ b/lists/copypastas.txt
@@ -1,6 +1,20 @@
-Cooldog || ╰━▅╮ && ╰╮ ┳━━╯ && ╰┳╯
+// Copypastas
+// Bits of text that tend to be copied and pasted
+
+// False Discord PSA
+False PSA || out for a Discord && going around && those who accept && send this to as many && if you see this
+Fake Discord shutdown || closing down && active discord members && please send && who do not send && deleted without hesitation
+
+// Share or else
+Cooldog || ╰━▅╮ && ╰╮ ┳━━╯ && ╰┳╯
Dog || ╰┳┳┳╯ && ▔╰━╯ && ╱╲╱╲▏
-Fake PSA || out for a Discord && going around && those who accept && send this to as many && if you see this
Memecat || Λ_Λ && ( 'ㅅ' ) && > ⌒ヽ
+"Read or you die" || Carry on reading && Once there was && Now every week && send this to && break this chain
+
+// Unicode
Bob || /▌ && /\ && This is bob
+Lennipede || ╚═(███)═╝ && Lenn
+
+// Text
Navy Seals || say about me && I’ll have you know I && I am trained && Think again && Not only am I extensively trained && kiddo
+Rick & Morty || have to have a very high IQ && extremely && solid grasp && deftly woven && heavily && intellectual capacity && cryptic reference
\ No newline at end of file
diff --git a/lists/referral_domains.txt b/lists/referral_domains.txt
index cc645e5..75a7d39 100644
--- a/lists/referral_domains.txt
+++ b/lists/referral_domains.txt
@@ -40,11 +40,13 @@
bc.vc
adfoc.us
linkbucks.com
+pnd.tl
// Referral Sites
wn.nr
// Scams
+fortnitevb.com
fortnite-special.com
fortnite-vbucks.net
fortnite-vbucks.de
@@ -55,4 +57,5 @@
steamsummer.com
steamreward.com
steamspecial.com
-easter-event.com
\ No newline at end of file
+easter-event.com
+steam-wallet-rewards.com
\ No newline at end of file
diff --git a/lists/safe_domains.txt b/lists/safe_domains.txt
index 40379ee..deddb5e 100644
--- a/lists/safe_domains.txt
+++ b/lists/safe_domains.txt
@@ -1,7 +1,7 @@
// Safe Domain List
// This list contains URLs that are considered non-redirecting URLs
-// Discord links
+// Discord Links
discord.gg
dis.gd
discordapp.net
@@ -37,6 +37,7 @@
9gag.com
twimg.com
tenor.co
+tenor.com
// Development
github.com
@@ -44,6 +45,8 @@
hastebin.com
pastebin.com
trello.com
+npmjs.com
+stackoverflow.com
// Social Media
twitter.com
@@ -77,4 +80,14 @@
speedtest.net
myanimelist.net
xkcd.com
-knowyourmeme.com
\ No newline at end of file
+knowyourmeme.com
+qrserver.com
+timeanddate.com
+mit.edu
+
+// Whitelist/Common links
+ygg.fun
+jagrosh.com
+mhgh.info
+giveawaybot.party
+discord.pw
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index d22f598..7576005 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
net.dv8tion
JDA
- 3.5.1_342
+ 3.6.0_355
com.jagrosh
diff --git a/src/main/java/com/jagrosh/vortex/Constants.java b/src/main/java/com/jagrosh/vortex/Constants.java
index 98db000..e05a6d4 100644
--- a/src/main/java/com/jagrosh/vortex/Constants.java
+++ b/src/main/java/com/jagrosh/vortex/Constants.java
@@ -52,5 +52,6 @@
public final static String LOG_TIMEZONE = WIKI_BASE + "/Log-Timezone";
public final static String RAID_MODE = WIKI_BASE + "/Raid-Mode";
public final static String COMMANDS = WIKI_BASE + "/Commands";
+ public final static String AUTOMOD = WIKI_BASE + "/Auto-Moderation";
}
}
diff --git a/src/main/java/com/jagrosh/vortex/Listener.java b/src/main/java/com/jagrosh/vortex/Listener.java
index 53c81dc..e48cf63 100644
--- a/src/main/java/com/jagrosh/vortex/Listener.java
+++ b/src/main/java/com/jagrosh/vortex/Listener.java
@@ -20,14 +20,12 @@
import java.util.stream.Collectors;
import net.dv8tion.jda.core.JDA.ShardInfo;
import net.dv8tion.jda.core.entities.Message;
+import net.dv8tion.jda.core.entities.Role;
import net.dv8tion.jda.core.events.Event;
import net.dv8tion.jda.core.events.ReadyEvent;
import net.dv8tion.jda.core.events.guild.GuildBanEvent;
import net.dv8tion.jda.core.events.guild.GuildUnbanEvent;
-import net.dv8tion.jda.core.events.guild.member.GuildMemberJoinEvent;
-import net.dv8tion.jda.core.events.guild.member.GuildMemberLeaveEvent;
-import net.dv8tion.jda.core.events.guild.member.GuildMemberRoleAddEvent;
-import net.dv8tion.jda.core.events.guild.member.GuildMemberRoleRemoveEvent;
+import net.dv8tion.jda.core.events.guild.member.*;
import net.dv8tion.jda.core.events.guild.voice.GuildVoiceJoinEvent;
import net.dv8tion.jda.core.events.guild.voice.GuildVoiceLeaveEvent;
import net.dv8tion.jda.core.events.guild.voice.GuildVoiceMoveEvent;
@@ -37,6 +35,9 @@
import net.dv8tion.jda.core.events.message.guild.GuildMessageUpdateEvent;
import net.dv8tion.jda.core.events.user.UserAvatarUpdateEvent;
import net.dv8tion.jda.core.events.user.UserNameUpdateEvent;
+import net.dv8tion.jda.core.events.user.update.UserUpdateAvatarEvent;
+import net.dv8tion.jda.core.events.user.update.UserUpdateDiscriminatorEvent;
+import net.dv8tion.jda.core.events.user.update.UserUpdateNameEvent;
import net.dv8tion.jda.core.hooks.EventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -143,7 +144,8 @@
GuildMemberRoleAddEvent gmrae = (GuildMemberRoleAddEvent) event;
// Signal the modlogger if someone was muted
- if(gmrae.getRoles().stream().anyMatch(r -> r.getName().equalsIgnoreCase("muted")))
+ Role mRole = vortex.getDatabase().settings.getSettings(gmrae.getGuild()).getMutedRole(gmrae.getGuild());
+ if(gmrae.getRoles().contains(mRole))
vortex.getModLogger().setNeedUpdate(gmrae.getGuild());
}
else if (event instanceof GuildMemberRoleRemoveEvent)
@@ -151,20 +153,31 @@
GuildMemberRoleRemoveEvent gmrre = (GuildMemberRoleRemoveEvent) event;
// Signal the modlogger if someone was unmuted
- if(gmrre.getRoles().stream().anyMatch(r -> r.getName().equalsIgnoreCase("muted")))
+ Role mRole = vortex.getDatabase().settings.getSettings(gmrre.getGuild()).getMutedRole(gmrre.getGuild());
+ if(gmrre.getRoles().contains(mRole))
{
vortex.getDatabase().tempmutes.removeMute(gmrre.getGuild(), gmrre.getUser().getIdLong());
vortex.getModLogger().setNeedUpdate(gmrre.getGuild());
}
}
- else if (event instanceof UserNameUpdateEvent)
+ else if (event instanceof UserUpdateNameEvent)
{
+ UserUpdateNameEvent unue = (UserUpdateNameEvent)event;
// Log the name change
- vortex.getBasicLogger().logNameChange((UserNameUpdateEvent)event);
+ vortex.getBasicLogger().logNameChange(unue);
+ unue.getUser().getMutualGuilds().stream().map(g -> g.getMember(unue.getUser())).forEach(m -> vortex.getAutoMod().dehoist(m));
}
- else if (event instanceof UserAvatarUpdateEvent)
+ else if (event instanceof UserUpdateDiscriminatorEvent)
{
- UserAvatarUpdateEvent uaue = (UserAvatarUpdateEvent)event;
+ vortex.getBasicLogger().logNameChange((UserUpdateDiscriminatorEvent)event);
+ }
+ else if (event instanceof GuildMemberNickChangeEvent)
+ {
+ vortex.getAutoMod().dehoist(((GuildMemberNickChangeEvent) event).getMember());
+ }
+ else if (event instanceof UserUpdateAvatarEvent)
+ {
+ UserUpdateAvatarEvent uaue = (UserUpdateAvatarEvent)event;
// Log the avatar change
if(!uaue.getUser().isBot())
@@ -203,7 +216,7 @@
vortex.getLogWebhook().send("\uD83C\uDF00 Shard `"+shardinfo+"` has connected. Guilds: `"
+event.getJDA().getGuildCache().size()+"` Users: `"+event.getJDA().getUserCache().size()+"`");
vortex.getThreadpool().scheduleWithFixedDelay(() -> vortex.getDatabase().tempbans.checkUnbans(event.getJDA()), 0, 2, TimeUnit.MINUTES);
- vortex.getThreadpool().scheduleWithFixedDelay(() -> vortex.getDatabase().tempmutes.checkUnmutes(event.getJDA()), 0, 45, TimeUnit.SECONDS);
+ vortex.getThreadpool().scheduleWithFixedDelay(() -> vortex.getDatabase().tempmutes.checkUnmutes(event.getJDA(), vortex.getDatabase().settings), 0, 45, TimeUnit.SECONDS);
}
}
}
diff --git a/src/main/java/com/jagrosh/vortex/Vortex.java b/src/main/java/com/jagrosh/vortex/Vortex.java
index ab6fa72..9cdbea1 100644
--- a/src/main/java/com/jagrosh/vortex/Vortex.java
+++ b/src/main/java/com/jagrosh/vortex/Vortex.java
@@ -108,6 +108,7 @@
new String[]{"Moderation commands","Configurable automoderation","Very easy setup"},Constants.PERMISSIONS),
new InviteCmd(),
new PingCommand(),
+ new RoleinfoCmd(),
new ServerinfoCmd(),
new UserinfoCmd(),
@@ -117,6 +118,7 @@
new SoftbanCmd(this),
new CleanCmd(this),
new VoicemoveCmd(this),
+ new VoicekickCmd(this),
new MuteCmd(this),
new UnmuteCmd(this),
new RaidCmd(this),
@@ -145,12 +147,14 @@
new MaxlinesCmd(this),
new MaxmentionsCmd(this),
new AntiduplicateCmd(this),
+ new AutodehoistCmd(this),
new ResolvelinksCmd(this),
new AutoraidmodeCmd(this),
new IgnoreCmd(this),
new UnignoreCmd(this),
// Tools
+ new AuditCmd(),
new DehoistCmd(),
new InvitepruneCmd(this),
new LookupCmd(this),
diff --git a/src/main/java/com/jagrosh/vortex/automod/AutoMod.java b/src/main/java/com/jagrosh/vortex/automod/AutoMod.java
index 90aafa7..4bcbb64 100644
--- a/src/main/java/com/jagrosh/vortex/automod/AutoMod.java
+++ b/src/main/java/com/jagrosh/vortex/automod/AutoMod.java
@@ -15,10 +15,12 @@
*/
package com.jagrosh.vortex.automod;
+import com.jagrosh.vortex.Constants;
import com.jagrosh.vortex.Vortex;
import com.jagrosh.vortex.database.managers.AutomodManager;
import com.jagrosh.vortex.database.managers.AutomodManager.AutomodSettings;
import com.jagrosh.vortex.utils.FixedCache;
+import com.jagrosh.vortex.utils.FormatUtil;
import com.jagrosh.vortex.utils.OtherUtil;
import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
@@ -26,15 +28,13 @@
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.dv8tion.jda.core.Permission;
-import net.dv8tion.jda.core.entities.Guild;
+import net.dv8tion.jda.core.entities.*;
import net.dv8tion.jda.core.entities.Guild.VerificationLevel;
-import net.dv8tion.jda.core.entities.Member;
-import net.dv8tion.jda.core.entities.Message;
-import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.events.guild.member.GuildMemberJoinEvent;
import net.dv8tion.jda.core.exceptions.PermissionException;
import org.slf4j.Logger;
@@ -46,23 +46,27 @@
*/
public class AutoMod
{
- private final static Pattern INVITES = Pattern.compile("discord\\s?(?:\\.\\s?gg|app\\s?.\\s?com\\s?\\/\\s?invite)\\s?\\/\\s?([A-Z0-9-]{2,18})",Pattern.CASE_INSENSITIVE);
+ private static final Pattern INVITES = Pattern.compile("discord\\s?(?:(?:\\.|dot|\\(\\.\\)|\\(dot\\))\\s?gg|app\\s?\\.\\s?com\\s?\\/\\s?invite)\\s?\\/\\s?([A-Z0-9-]{2,18})",
+ Pattern.CASE_INSENSITIVE);
- private final static String[] REF_LINK_LIST = OtherUtil.readLines("referral_domains");
- private final static Pattern REF = Pattern.compile("https?:\\/\\/(?:(?:[a-z0-9-_]+\\.)?(?:"
- + (String.join("|", REF_LINK_LIST).replace(".", "\\."))
- + ")[/?]|\\S+[?&]ref=|\\S+\\/ref\\/)\\S+", Pattern.CASE_INSENSITIVE);
- private final static Pattern LINK = Pattern.compile("https?:\\/\\/\\S+", Pattern.CASE_INSENSITIVE);
- private final static String INVITE_LINK = "https?:\\/\\/discord(?:app\\.com\\/invite|\\.gg)\\/(\\S+)";
- private final static String REF_LINK = REF.pattern();
+ private static final Pattern REF = Pattern.compile("https?:\\/\\/\\S+(?:\\/ref\\/|[?]ref=)\\S+", Pattern.CASE_INSENSITIVE);
+ private static final Pattern BASE_URL = Pattern.compile("https?:\\/\\/(?:\\S+\\.)?(\\S+\\.\\S+?)[/?]\\S+", Pattern.CASE_INSENSITIVE);
- private final static String CONDENSER = "(.+?)\\s*(\\1\\s*)+";
- private final static Logger LOG = LoggerFactory.getLogger("AutoMod");
- public final static String RESTORE_MUTE_ROLE_AUDIT = "Restoring Muted Role";
+ private static final Pattern LINK = Pattern.compile("https?:\\/\\/\\S+", Pattern.CASE_INSENSITIVE);
+ private static final String INVITE_LINK = "https?:\\/\\/discord(?:app\\.com\\/invite|\\.gg)\\/(\\S+)";
+
+ private static final String CONDENSER = "(.+?)\\s*(\\1\\s*)+";
+ private static final Logger LOG = LoggerFactory.getLogger("AutoMod");
+ public static final String RESTORE_MUTE_ROLE_AUDIT = "Restoring Muted Role";
+
+ public static final Character[] VALID_DEHOIST_CHAR = {'!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/'};
+ public static final String DEHOIST_PREFIX = "\uD82F\uDCA2";
+ public static final String DEHOIST_JOINED = "`"+FormatUtil.join("`, `", (Character c) -> Character.toString(c), VALID_DEHOIST_CHAR)+"`";
private final Vortex vortex;
+ private String[] refLinkList;
private final URLResolver urlResolver = new URLResolver();
private final InviteResolver inviteResolver = new InviteResolver();
private final CopypastaResolver copypastaResolver = new CopypastaResolver();
@@ -73,6 +77,7 @@
{
this.vortex = vortex;
loadCopypastas();
+ loadReferralDomains();
}
public final void loadCopypastas()
@@ -85,6 +90,11 @@
this.urlResolver.loadSafeDomains();
}
+ public final void loadReferralDomains()
+ {
+ this.refLinkList = OtherUtil.readLines("referral_domains");
+ }
+
public void enableRaidMode(Guild guild, Member moderator, OffsetDateTime now, String reason)
{
vortex.getDatabase().settings.enableRaidMode(guild);
@@ -166,9 +176,10 @@
{
try
{
- event.getGuild().getController().addSingleRoleToMember(event.getMember(), OtherUtil.getMutedRole(event.getGuild())).reason(RESTORE_MUTE_ROLE_AUDIT).queue();
+ event.getGuild().getController().addSingleRoleToMember(event.getMember(), vortex.getDatabase().settings.getSettings(event.getGuild()).getMutedRole(event.getGuild())).reason(RESTORE_MUTE_ROLE_AUDIT).queue();
} catch(Exception ex){}
}
+ dehoist(event.getMember());
}
latestGuildJoin.put(event.getGuild().getIdLong(), now);
@@ -202,10 +213,7 @@
return false;
// if a channel is specified, ignore users that can manage messages in that channel
- if(channel!=null && member.hasPermission(channel, Permission.MESSAGE_MANAGE))
- return false;
-
- if(vortex.getDatabase().ignores.isIgnored(channel))
+ if(channel!=null && (member.hasPermission(channel, Permission.MESSAGE_MANAGE) || vortex.getDatabase().ignores.isIgnored(channel)))
return false;
if(vortex.getDatabase().ignores.isIgnored(member))
@@ -216,20 +224,28 @@
public void dehoist(Member member)
{
+ if(!member.getGuild().getSelfMember().hasPermission(Permission.NICKNAME_MANAGE))
+ return;
+
if(!shouldPerformAutomod(member, null))
return;
AutomodSettings settings = vortex.getDatabase().automod.getSettings(member.getGuild());
- if(settings==null)
+ if(settings==null || settings.dehoistChar==(char)0 || member.getEffectiveName().charAt(0)>settings.dehoistChar)
return;
-
+ String newname = AutoMod.DEHOIST_PREFIX+member.getEffectiveName();
+ if(newname.length()>32)
+ newname = newname.substring(0,32);
+ try
+ {
+ member.getGuild().getController().setNickname(member, newname).reason("Dehoisting").queue();
+ }
+ catch(Exception ex) {}
}
public void performAutomod(Message message)
{
- //simple automod
-
//ignore users with Manage Messages, Kick Members, Ban Members, Manage Server, or anyone the bot can't interact with
if(!shouldPerformAutomod(message.getMember(), message.getTextChannel()))
return;
@@ -244,6 +260,7 @@
boolean preventInvites = message.getTextChannel().getTopic()==null || !message.getTextChannel().getTopic().toLowerCase().contains("{invites}");
boolean shouldDelete = false;
+ String shouldChannelMute = null;
int strikeTotal = 0;
StringBuilder reason = new StringBuilder();
@@ -263,7 +280,10 @@
int offenses = status.update(content, now);
if(offenses==settings.dupeDeleteThresh)
+ {
+ shouldChannelMute = "Please stop spamming.";
purgeMessages(message.getGuild(), m -> m.getAuthor().getIdLong()==message.getAuthor().getIdLong() && m.getCreationTime().plusMinutes(2).isAfter(now));
+ }
else if(offenses>settings.dupeDeleteThresh)
shouldDelete = true;
@@ -322,6 +342,20 @@
reason.append(", Referral link");
shouldDelete = true;
}
+ else
+ {
+ m = BASE_URL.matcher(message.getContentRaw().toLowerCase());
+ while(m.find())
+ {
+ if(isReferralUrl(m.group(1)))
+ {
+ strikeTotal += settings.refStrikes;
+ reason.append(", Referral link");
+ shouldDelete = true;
+ break;
+ }
+ }
+ }
}
// prevent copypastas
@@ -332,6 +366,7 @@
{
strikeTotal += settings.copypastaStrikes;
reason.append(", ").append(copypastaName).append(" copypasta");
+ shouldDelete = true;
}
}
@@ -343,21 +378,20 @@
while(m.find())
invites.add(m.group(1));
LOG.trace("Found "+invites.size()+" invites.");
- try{
- for(String inviteCode : invites)
+ for(String inviteCode : invites)
+ {
+ long gid = inviteResolver.resolve(message.getJDA(), inviteCode);
+ if(gid != message.getGuild().getIdLong())
{
- long gid = inviteResolver.resolve(message.getJDA(), inviteCode);
- if(gid != message.getGuild().getIdLong())
- {
- strikeTotal += settings.inviteStrikes;
- reason.append(", Advertising");
- shouldDelete = true;
- break;
- }
+ strikeTotal += settings.inviteStrikes;
+ reason.append(", Advertising");
+ shouldDelete = true;
+ break;
}
- }catch(PermissionException ex){}
+ }
}
+
// delete the message if applicable
if(shouldDelete)
{
@@ -367,6 +401,41 @@
}catch(PermissionException e){}
}
+ // channel mute if applicable (prevent sending messages in that channel for a short time as a 'warning'
+ if(shouldChannelMute!=null && message.getGuild().getSelfMember().hasPermission(message.getTextChannel(), Permission.MANAGE_PERMISSIONS, Permission.MESSAGE_WRITE))
+ {
+ message.getChannel().sendMessage(message.getAuthor().getAsMention() + Constants.WARNING + " " + shouldChannelMute).queue(m ->
+ {
+ m.delete().queueAfter(1500, TimeUnit.MILLISECONDS, s->{}, f->{});
+ try
+ {
+ PermissionOverride po = message.getTextChannel().getPermissionOverride(message.getMember());
+ if(po==null)
+ {
+
+ message.getTextChannel().createPermissionOverride(message.getMember()).setDeny(Permission.MESSAGE_READ).queue(p ->
+ {
+ try
+ {
+ p.delete().queueAfter(3, TimeUnit.SECONDS, s->{}, f->{});
+ } catch(Exception ex) {}
+ }, f->{});
+ }
+ else
+ {
+ long existingDenied = po.getDeniedRaw();
+ po.getManager().deny(Permission.MESSAGE_READ).queue(su ->
+ {
+ try
+ {
+ po.getManager().deny(existingDenied).queueAfter(3, TimeUnit.SECONDS, s->{}, f->{});
+ } catch(Exception ex) {}
+ }, f->{});
+ }
+ } catch(Exception ex) {}
+ }, f->{});
+ }
+
// assign strikes if necessary
if(strikeTotal>0)
{
@@ -399,8 +468,12 @@
if(inviteResolver.resolve(message.getJDA(), resolved.replaceAll(INVITE_LINK, "$1")) != message.getGuild().getIdLong())
containsInvite = true;
}
- if(settings.refStrikes>0 && resolved.matches(REF_LINK))
- containsRef = true;
+ if(settings.refStrikes>0)
+ {
+ if(resolved.matches(REF.pattern()) || isReferralUrl(resolved.replaceAll(BASE_URL.pattern(), "$1")))
+ containsRef = true;
+ }
+
}
if((containsInvite || settings.inviteStrikes<1) && (containsRef || settings.refStrikes<1))
break;
@@ -433,9 +506,12 @@
});
}
- private static OffsetDateTime latestTime(Message m)
+ private boolean isReferralUrl(String url)
{
- return m.isEdited() ? m.getEditedTime() : m.getCreationTime();
+ for(String reflink: refLinkList)
+ if(reflink.equals(url))
+ return true;
+ return false;
}
private static String condensedContent(Message m)
@@ -445,6 +521,11 @@
return sb.toString().trim().replaceAll(CONDENSER, "$1");
}
+ private static OffsetDateTime latestTime(Message m)
+ {
+ return m.isEdited() ? m.getEditedTime() : m.getCreationTime();
+ }
+
private class DupeStatus
{
private String content;
diff --git a/src/main/java/com/jagrosh/vortex/automod/CopypastaResolver.java b/src/main/java/com/jagrosh/vortex/automod/CopypastaResolver.java
index b8d2ac5..0aa8496 100644
--- a/src/main/java/com/jagrosh/vortex/automod/CopypastaResolver.java
+++ b/src/main/java/com/jagrosh/vortex/automod/CopypastaResolver.java
@@ -16,6 +16,7 @@
package com.jagrosh.vortex.automod;
import com.jagrosh.vortex.utils.OtherUtil;
+import java.util.Arrays;
import java.util.HashMap;
/**
diff --git a/src/main/java/com/jagrosh/vortex/automod/StrikeHandler.java b/src/main/java/com/jagrosh/vortex/automod/StrikeHandler.java
index ae0228f..52543f1 100644
--- a/src/main/java/com/jagrosh/vortex/automod/StrikeHandler.java
+++ b/src/main/java/com/jagrosh/vortex/automod/StrikeHandler.java
@@ -138,7 +138,7 @@
if(muteDuration>0)
{
vortex.getDatabase().tempmutes.setMute(moderator.getGuild(), target.getIdLong(), muteTime(now, muteDuration));
- Role muted = moderator.getGuild().getRoles().stream().filter(r -> r.getName().equalsIgnoreCase("Muted")).findFirst().orElse(null);
+ Role muted = vortex.getDatabase().settings.getSettings(moderator.getGuild()).getMutedRole(moderator.getGuild());
Member mem = moderator.getGuild().getMember(target);
if(muted==null || mem==null || !moderator.getGuild().getSelfMember().canInteract(muted))
{
diff --git a/src/main/java/com/jagrosh/vortex/commands/LogCommand.java b/src/main/java/com/jagrosh/vortex/commands/LogCommand.java
index a229f7e..910b8e0 100644
--- a/src/main/java/com/jagrosh/vortex/commands/LogCommand.java
+++ b/src/main/java/com/jagrosh/vortex/commands/LogCommand.java
@@ -64,7 +64,7 @@
}
if(list.size()>1)
{
- event.reply(FormatUtil.listOfText(list, event.getArgs()));
+ event.replyWarning(FormatUtil.listOfText(list, event.getArgs()));
return;
}
diff --git a/src/main/java/com/jagrosh/vortex/commands/automod/AutodehoistCmd.java b/src/main/java/com/jagrosh/vortex/commands/automod/AutodehoistCmd.java
new file mode 100644
index 0000000..525f3c1
--- /dev/null
+++ b/src/main/java/com/jagrosh/vortex/commands/automod/AutodehoistCmd.java
@@ -0,0 +1,63 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jagrosh.vortex.commands.automod;
+
+import com.jagrosh.jdautilities.command.Command;
+import com.jagrosh.jdautilities.command.CommandEvent;
+import net.dv8tion.jda.core.Permission;
+import com.jagrosh.vortex.Vortex;
+import com.jagrosh.vortex.automod.AutoMod;
+import com.jagrosh.vortex.commands.CommandExceptionListener;
+import com.jagrosh.vortex.database.managers.PunishmentManager;
+
+/**
+ *
+ * @author John Grosh (john.a.grosh@gmail.com)
+ */
+public class AutodehoistCmd extends Command
+{
+ private final Vortex vortex;
+
+ public AutodehoistCmd(Vortex vortex)
+ {
+ this.vortex = vortex;
+ this.name = "autodehoist";
+ this.guildOnly = true;
+ this.aliases = new String[]{"auto-dehoist"};
+ this.category = new Category("AutoMod");
+ this.arguments = "";
+ this.help = "prevents name-hoisting via usernames or nicknames";
+ this.userPermissions = new Permission[]{Permission.MANAGE_SERVER};
+ }
+
+ @Override
+ protected void execute(CommandEvent event)
+ {
+ if(event.getArgs().isEmpty())
+ throw new CommandExceptionListener.CommandErrorException("Please provide a valid dehoist character, or OFF");
+ else if(event.getArgs().equalsIgnoreCase("none") || event.getArgs().equalsIgnoreCase("off"))
+ {
+ vortex.getDatabase().automod.setDehoistChar(event.getGuild(), (char)0);
+ event.replySuccess("No action will be taken on name hoisting.");
+ return;
+ }
+ char symbol;
+ if(event.getArgs().length()==1)
+ symbol = event.getArgs().charAt(0);
+ else
+ throw new CommandExceptionListener.CommandErrorException("Provided symbol must be one character of the following: "+AutoMod.DEHOIST_JOINED);
+ boolean allowed = false;
+ for(char c: AutoMod.VALID_DEHOIST_CHAR)
+ if(c==symbol)
+ allowed = true;
+ if(!allowed)
+ throw new CommandExceptionListener.CommandErrorException("Provided symbol must be one character of the following: "+AutoMod.DEHOIST_JOINED);
+
+ vortex.getDatabase().automod.setDehoistChar(event.getGuild(), symbol);
+ boolean also = vortex.getDatabase().actions.useDefaultSettings(event.getGuild());
+ event.replySuccess("Users will now be dehoisted if their effective name starts with `"+symbol+"` or higher."+(also ? PunishmentManager.DEFAULT_SETUP_MESSAGE : ""));
+ }
+}
diff --git a/src/main/java/com/jagrosh/vortex/commands/automod/IgnoreCmd.java b/src/main/java/com/jagrosh/vortex/commands/automod/IgnoreCmd.java
index ab78eb4..f0beaf9 100644
--- a/src/main/java/com/jagrosh/vortex/commands/automod/IgnoreCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/automod/IgnoreCmd.java
@@ -59,7 +59,7 @@
builder.append("\n").append(r.getAsMention()).append(" [elevated perms]");
});
channels.forEach(c -> builder.append("\n").append(c.getAsMention()));
- ebuilder.setDescription(builder.toString());
+ ebuilder.setDescription(builder.length() > 2045 ? builder.substring(0, 2048) + "..." : builder.toString());
event.reply(ebuilder.build());
return;
}
@@ -87,6 +87,6 @@
event.replySuccess("Automod is now ignoring role `"+roles.get(0).getName()+"`");
}
else
- event.reply(FormatUtil.listOfRoles(roles, event.getArgs()));
+ event.replyWarning(FormatUtil.listOfRoles(roles, event.getArgs()));
}
}
diff --git a/src/main/java/com/jagrosh/vortex/commands/automod/UnignoreCmd.java b/src/main/java/com/jagrosh/vortex/commands/automod/UnignoreCmd.java
index b749cd9..206c25a 100644
--- a/src/main/java/com/jagrosh/vortex/commands/automod/UnignoreCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/automod/UnignoreCmd.java
@@ -74,6 +74,6 @@
+ "\n`[elevated perms]` - the role has one of the following permissions: Kick Members, Ban Members, Manage Server, Manage Messages, Administrator");
}
else
- event.reply(FormatUtil.listOfRoles(roles, event.getArgs()));
+ event.replyWarning(FormatUtil.listOfRoles(roles, event.getArgs()));
}
}
diff --git a/src/main/java/com/jagrosh/vortex/commands/general/RoleinfoCmd.java b/src/main/java/com/jagrosh/vortex/commands/general/RoleinfoCmd.java
new file mode 100644
index 0000000..27d447d
--- /dev/null
+++ b/src/main/java/com/jagrosh/vortex/commands/general/RoleinfoCmd.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2016 John Grosh (jagrosh).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jagrosh.vortex.commands.general;
+
+import java.time.format.DateTimeFormatter;
+import com.jagrosh.jdautilities.command.Command;
+import com.jagrosh.jdautilities.command.CommandEvent;
+import com.jagrosh.jdautilities.commons.utils.FinderUtil;
+import com.jagrosh.vortex.commands.CommandExceptionListener.CommandErrorException;
+import com.jagrosh.vortex.utils.FormatUtil;
+import java.util.List;
+import net.dv8tion.jda.core.EmbedBuilder;
+import net.dv8tion.jda.core.MessageBuilder;
+import net.dv8tion.jda.core.Permission;
+import net.dv8tion.jda.core.entities.Member;
+import net.dv8tion.jda.core.entities.Role;
+
+/**
+ *
+ * @author John Grosh (jagrosh)
+ */
+public class RoleinfoCmd extends Command
+{
+ private final String linestart = "\u25AB";
+
+ public RoleinfoCmd()
+ {
+ this.name = "roleinfo";
+ this.aliases = new String[]{"rinfo","rankinfo"};
+ this.help = "shows info about a role";
+ this.arguments = "";
+ this.guildOnly = true;
+ this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
+ }
+
+ @Override
+ protected void execute(CommandEvent event)
+ {
+ Role role;
+ if(event.getArgs().isEmpty())
+ throw new CommandErrorException("Please provide the name of a role!");
+ else
+ {
+ List found = FinderUtil.findRoles(event.getArgs(), event.getGuild());
+ if(found.isEmpty())
+ {
+ event.replyError("I couldn't find the role you were looking for!");
+ return;
+ }
+ else if(found.size()>1)
+ {
+ event.replyWarning(FormatUtil.listOfRoles(found, event.getArgs()));
+ return;
+ }
+ else
+ {
+ role = found.get(0);
+ }
+ }
+
+ String title = "\uD83C\uDFAD Information about **"+role.getName()+"**:";
+ List list = role.isPublicRole() ? event.getGuild().getMembers() : event.getGuild().getMembersWithRoles(role);
+ StringBuilder desr = new StringBuilder(linestart+"ID: **"+role.getId()+"**\n"
+ + linestart+"Creation: **"+role.getCreationTime().format(DateTimeFormatter.RFC_1123_DATE_TIME)+"**\n"
+ + linestart+"Position: **"+role.getPosition()+"**\n"
+ + linestart+"Color: **#"+(role.getColor()==null ? "000000" : Integer.toHexString(role.getColor().getRGB()).toUpperCase().substring(2))+"**\n"
+ + linestart+"Mentionable: **"+role.isMentionable()+"**\n"
+ + linestart+"Hoisted: **"+role.isHoisted()+"**\n"
+ + linestart+"Managed: **"+role.isManaged()+"**\n"
+ + linestart+"Permissions: ");
+ if(role.getPermissions().isEmpty())
+ desr.append("None");
+ else
+ desr.append(role.getPermissions().stream().map(p -> "`, `"+p.getName()).reduce("", String::concat).substring(3)).append("`");
+ desr.append("\n").append(linestart).append("Members: **").append(list.size()).append("**\n");
+ if(list.size()*24<=2048-desr.length())
+ list.forEach(m -> desr.append("<@").append(m.getUser().getId()).append("> "));
+
+ event.reply(new MessageBuilder()
+ .append(FormatUtil.filterEveryone(title))
+ .setEmbed(new EmbedBuilder()
+ .setDescription(desr.toString().trim())
+ .setColor(role.getColor()).build())
+ .build());
+ }
+}
diff --git a/src/main/java/com/jagrosh/vortex/commands/general/UserinfoCmd.java b/src/main/java/com/jagrosh/vortex/commands/general/UserinfoCmd.java
index 7ca905b..dd95dac 100644
--- a/src/main/java/com/jagrosh/vortex/commands/general/UserinfoCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/general/UserinfoCmd.java
@@ -68,7 +68,7 @@
}
else if(found.size()>1)
{
- event.reply(FormatUtil.listOfMember(found, event.getArgs()));
+ event.replyWarning(FormatUtil.listOfMember(found, event.getArgs()));
return;
}
else
diff --git a/src/main/java/com/jagrosh/vortex/commands/moderation/CheckCmd.java b/src/main/java/com/jagrosh/vortex/commands/moderation/CheckCmd.java
index 591d9b4..7ad9b60 100644
--- a/src/main/java/com/jagrosh/vortex/commands/moderation/CheckCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/moderation/CheckCmd.java
@@ -25,6 +25,7 @@
import java.util.List;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Member;
+import net.dv8tion.jda.core.entities.Role;
import net.dv8tion.jda.core.entities.User;
/**
@@ -83,7 +84,8 @@
{
int strikes = vortex.getDatabase().strikes.getStrikes(event.getGuild(), user.getIdLong());
int minutesMuted = vortex.getDatabase().tempmutes.timeUntilUnmute(event.getGuild(), user.getIdLong());
- boolean actuallyMuted = event.getGuild().isMember(user) && event.getGuild().getMember(user).getRoles().stream().anyMatch(r -> r.getName().equalsIgnoreCase("Muted"));
+ Role mRole = vortex.getDatabase().settings.getSettings(event.getGuild()).getMutedRole(event.getGuild());
+ boolean actuallyMuted = mRole!=null && event.getGuild().isMember(user) && event.getGuild().getMember(user).getRoles().contains(mRole);
int minutesBanned = vortex.getDatabase().tempbans.timeUntilUnban(event.getGuild(), user.getIdLong());
String str = "Moderation Information for "+FormatUtil.formatFullUser(user)+":\n"
+ Action.STRIKE.getEmoji() + " Strikes: **"+strikes+"**\n";
diff --git a/src/main/java/com/jagrosh/vortex/commands/moderation/CleanCmd.java b/src/main/java/com/jagrosh/vortex/commands/moderation/CleanCmd.java
index 8d8630c..8e71791 100644
--- a/src/main/java/com/jagrosh/vortex/commands/moderation/CleanCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/moderation/CleanCmd.java
@@ -222,7 +222,7 @@
}
else
params = "all";
- vortex.getTextUploader().upload(LogUtil.logMessages("Cleaned Messages", del), "CleanedMessages", (view, download) ->
+ vortex.getTextUploader().upload(LogUtil.logMessagesBackwards("Cleaned Messages", del), "CleanedMessages", (view, download) ->
{
vortex.getModLogger().postCleanCase(event.getMember(), event.getMessage().getCreationTime(), del.size(),
event.getTextChannel(), params, null, new EmbedBuilder().setColor(event.getSelfMember().getColor())
diff --git a/src/main/java/com/jagrosh/vortex/commands/moderation/MuteCmd.java b/src/main/java/com/jagrosh/vortex/commands/moderation/MuteCmd.java
index 6786713..9b73e6d 100644
--- a/src/main/java/com/jagrosh/vortex/commands/moderation/MuteCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/moderation/MuteCmd.java
@@ -48,10 +48,10 @@
@Override
protected void execute(CommandEvent event)
{
- Role muteRole = event.getGuild().getRoles().stream().filter(r -> r.getName().equalsIgnoreCase("muted")).findFirst().orElse(null);
+ Role muteRole = vortex.getDatabase().settings.getSettings(event.getGuild()).getMutedRole(event.getGuild());
if(muteRole == null)
{
- event.replyError("No role called 'Muted' exists!");
+ event.replyError("No Muted role exists!");
return;
}
if(!event.getMember().canInteract(muteRole))
diff --git a/src/main/java/com/jagrosh/vortex/commands/moderation/UnmuteCmd.java b/src/main/java/com/jagrosh/vortex/commands/moderation/UnmuteCmd.java
index 7e6ecbb..93d4601 100644
--- a/src/main/java/com/jagrosh/vortex/commands/moderation/UnmuteCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/moderation/UnmuteCmd.java
@@ -46,10 +46,10 @@
@Override
protected void execute(CommandEvent event)
{
- Role muteRole = event.getGuild().getRoles().stream().filter(r -> r.getName().equalsIgnoreCase("muted")).findFirst().orElse(null);
+ Role muteRole = vortex.getDatabase().settings.getSettings(event.getGuild()).getMutedRole(event.getGuild());
if(muteRole == null)
{
- event.replyError("No role called 'Muted' exists!");
+ event.replyError("No Muted role exists!");
return;
}
if(!event.getMember().canInteract(muteRole))
diff --git a/src/main/java/com/jagrosh/vortex/commands/moderation/VoicekickCmd.java b/src/main/java/com/jagrosh/vortex/commands/moderation/VoicekickCmd.java
new file mode 100644
index 0000000..d08c3bf
--- /dev/null
+++ b/src/main/java/com/jagrosh/vortex/commands/moderation/VoicekickCmd.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2016 John Grosh (jagrosh).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jagrosh.vortex.commands.moderation;
+
+import java.util.LinkedList;
+import com.jagrosh.jdautilities.command.CommandEvent;
+import com.jagrosh.vortex.Vortex;
+import com.jagrosh.vortex.commands.ModCommand;
+import net.dv8tion.jda.core.Permission;
+import net.dv8tion.jda.core.entities.Member;
+import com.jagrosh.vortex.utils.ArgsUtil;
+import com.jagrosh.vortex.utils.FormatUtil;
+import com.jagrosh.vortex.utils.LogUtil;
+import java.util.List;
+import net.dv8tion.jda.core.entities.VoiceChannel;
+
+/**
+ *
+ * @author John Grosh (jagrosh)
+ */
+public class VoicekickCmd extends ModCommand
+{
+ public VoicekickCmd(Vortex vortex)
+ {
+ super(vortex, Permission.VOICE_MOVE_OTHERS);
+ this.name = "voicekick";
+ this.aliases = new String[]{"vckick"};
+ this.arguments = "<@users> [reason]";
+ this.help = "removes users from voice channels";
+ this.botPermissions = new Permission[]{Permission.VOICE_MOVE_OTHERS, Permission.MANAGE_CHANNEL, Permission.VOICE_CONNECT};
+ this.guildOnly = true;
+ }
+
+ @Override
+ protected void execute(CommandEvent event)
+ {
+ ArgsUtil.ResolvedArgs args = ArgsUtil.resolve(event.getArgs(), event.getGuild());
+ if(args.isEmpty())
+ {
+ event.replyError("Please include at least one user to kick from voice (@mention or ID)!");
+ return;
+ }
+ String reason = LogUtil.auditReasonFormat(event.getMember(), args.reason);
+ StringBuilder builder = new StringBuilder();
+ List toKick = new LinkedList<>();
+
+ args.members.forEach(m ->
+ {
+ if(!event.getMember().canInteract(m))
+ builder.append("\n").append(event.getClient().getError()).append(" You do not have permission to voicekick ").append(FormatUtil.formatUser(m.getUser()));
+ else if(!event.getSelfMember().canInteract(m))
+ builder.append("\n").append(event.getClient().getError()).append(" I am unable to voicekick ").append(FormatUtil.formatUser(m.getUser()));
+ else if(!m.getVoiceState().inVoiceChannel())
+ builder.append("\n").append(event.getClient().getWarning()).append(" ").append(FormatUtil.formatUser(m.getUser())).append(" is not in a voice channel!");
+ else
+ toKick.add(m);
+ });
+
+ args.unresolved.forEach(un -> builder.append("\n").append(event.getClient().getWarning()).append(" Could not resolve `").append(un).append("` to a member"));
+
+ args.users.forEach(u -> builder.append("\n").append(event.getClient().getWarning()).append("The user ").append(FormatUtil.formatUser(u)).append(" is not in this server."));
+
+ args.ids.forEach(id -> builder.append("\n").append(event.getClient().getWarning()).append("The user <@").append(id).append("> is not in this server."));
+
+ if(toKick.isEmpty())
+ {
+ event.reply(builder.toString());
+ return;
+ }
+
+ if(toKick.size() > 5)
+ event.reactSuccess();
+
+ // do this async because its a nightmare to do it sync
+ event.async(() ->
+ {
+ VoiceChannel vc;
+ try
+ {
+ vc = (VoiceChannel)event.getGuild().getController().createVoiceChannel("Voice Kick Channel")
+ .setParent(toKick.get(0).getVoiceState().getChannel().getParent()).reason(reason).complete();
+ }
+ catch(Exception ex)
+ {
+ builder.append("\n").append(event.getClient().getError()).append(" Failed to create a voice kick channel.");
+ event.reply(builder.toString());
+ return;
+ }
+ for(int i=0; i1)
{
- event.reply(FormatUtil.listOfVoice(list, event.getArgs()));
+ event.replyWarning(FormatUtil.listOfVoice(list, event.getArgs()));
return;
}
vc = list.get(0);
diff --git a/src/main/java/com/jagrosh/vortex/commands/owner/DebugCmd.java b/src/main/java/com/jagrosh/vortex/commands/owner/DebugCmd.java
index fb81a16..90495b3 100644
--- a/src/main/java/com/jagrosh/vortex/commands/owner/DebugCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/owner/DebugCmd.java
@@ -55,7 +55,8 @@
+ "\nShard Total: **"+vortex.getShardManager().getShardsTotal()+"**"
+ "\nShard Connectivity: ```diff");
vortex.getShardManager().getShards().forEach(jda -> sb.append("\n").append(jda.getStatus()==JDA.Status.CONNECTED ? "+ " : "- ")
- .append(jda.getShardInfo().getShardId()<10 ? "0" : "").append(jda.getShardInfo().getShardId()).append(": ").append(jda.getStatus()));
+ .append(jda.getShardInfo().getShardId()<10 ? "0" : "").append(jda.getShardInfo().getShardId()).append(": ").append(jda.getStatus())
+ .append(" ~ ").append(jda.getGuildCache().size()).append(" guilds"));
sb.append("\n```");
event.reply(sb.toString().trim());
}
diff --git a/src/main/java/com/jagrosh/vortex/commands/owner/EvalCmd.java b/src/main/java/com/jagrosh/vortex/commands/owner/EvalCmd.java
index 6222f7a..fb5302f 100644
--- a/src/main/java/com/jagrosh/vortex/commands/owner/EvalCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/owner/EvalCmd.java
@@ -40,7 +40,8 @@
}
@Override
- protected void execute(CommandEvent event) {
+ protected void execute(CommandEvent event)
+ {
ScriptEngine se = new ScriptEngineManager().getEngineByName("Nashorn");
se.put("bot", vortex);
se.put("event", event);
diff --git a/src/main/java/com/jagrosh/vortex/commands/owner/ReloadCmd.java b/src/main/java/com/jagrosh/vortex/commands/owner/ReloadCmd.java
index d0c920a..d9ce72d 100644
--- a/src/main/java/com/jagrosh/vortex/commands/owner/ReloadCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/owner/ReloadCmd.java
@@ -46,7 +46,8 @@
switch(event.getArgs().toLowerCase())
{
case "ref":
- event.replyWarning("Not yet implemented");
+ vortex.getAutoMod().loadReferralDomains();
+ event.replySuccess("Reloaded ref domains");
break;
case "safe":
vortex.getAutoMod().loadSafeDomains();
@@ -57,7 +58,7 @@
event.replySuccess("Reloaded copypastas");
break;
default:
- throw new CommandErrorException("Invalid reload selection");
+ throw new CommandErrorException("Invalid reload selection: `ref` `safe` `copy`");
}
}
}
diff --git a/src/main/java/com/jagrosh/vortex/commands/settings/ModroleCmd.java b/src/main/java/com/jagrosh/vortex/commands/settings/ModroleCmd.java
index 5179e49..8436707 100644
--- a/src/main/java/com/jagrosh/vortex/commands/settings/ModroleCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/settings/ModroleCmd.java
@@ -41,7 +41,7 @@
this.arguments = "";
this.category = new Category("Settings");
this.guildOnly = true;
- this.userPermissions = new Permission[]{Permission.MANAGE_SERVER};
+ this.userPermissions = new Permission[]{Permission.MANAGE_SERVER, Permission.BAN_MEMBERS};
}
@Override
@@ -53,7 +53,7 @@
return;
}
- else if(event.getArgs().equalsIgnoreCase("none"))
+ else if(event.getArgs().equalsIgnoreCase("none") || event.getArgs().equalsIgnoreCase("off"))
{
vortex.getDatabase().settings.setModeratorRole(event.getGuild(), null);
event.replySuccess("Moderation commands can now only be used by members that can perform the actions manually.");
@@ -69,6 +69,6 @@
event.replySuccess("Users with the `"+roles.get(0).getName()+"` role can now use all Moderation commands.");
}
else
- event.reply(FormatUtil.listOfRoles(roles, event.getArgs()));
+ event.replyWarning(FormatUtil.listOfRoles(roles, event.getArgs()));
}
}
diff --git a/src/main/java/com/jagrosh/vortex/commands/settings/PunishmentCmd.java b/src/main/java/com/jagrosh/vortex/commands/settings/PunishmentCmd.java
index a66120b..37b245f 100644
--- a/src/main/java/com/jagrosh/vortex/commands/settings/PunishmentCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/settings/PunishmentCmd.java
@@ -33,7 +33,7 @@
*/
public class PunishmentCmd extends Command
{
- private final static String SETTING_STRIKES = "\n\nUsage: `"+Constants.PREFIX+"setstrikes [time]`\n"
+ private final static String SETTING_STRIKES = "\n\nUsage: `"+Constants.PREFIX+"punishment [time]`\n"
+ "`` - the number of strikes at which to perform the action\n"
+ "`` - the action, such as `None`, `Kick`, `Mute`, `Softban`, or `Ban`\n"
+ "`[time]` - optional, the amount of time to keep the user muted or banned\n"
@@ -98,6 +98,8 @@
}
vortex.getDatabase().actions.setAction(event.getGuild(), numstrikes, Action.MUTE, minutes);
successMessage = "Users will now be `muted` "+(minutes>0 ? "for "+FormatUtil.secondsToTime(minutes*60)+" " : "")+"upon reaching `"+numstrikes+"` strikes.";
+ if(vortex.getDatabase().settings.getSettings(event.getGuild()).getMutedRole(event.getGuild())==null)
+ successMessage += event.getClient().getWarning()+" No muted role currently exists!";
break;
}
case "kick":
diff --git a/src/main/java/com/jagrosh/vortex/commands/settings/SetupCmd.java b/src/main/java/com/jagrosh/vortex/commands/settings/SetupCmd.java
index 54732e0..49b6fc4 100644
--- a/src/main/java/com/jagrosh/vortex/commands/settings/SetupCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/settings/SetupCmd.java
@@ -21,7 +21,7 @@
import com.jagrosh.jdautilities.menu.ButtonMenu;
import com.jagrosh.vortex.Constants;
import com.jagrosh.vortex.Vortex;
-import com.jagrosh.vortex.utils.OtherUtil;
+import com.jagrosh.vortex.database.managers.AutomodManager.AutomodSettings;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.PermissionOverride;
import net.dv8tion.jda.core.entities.Role;
@@ -34,11 +34,6 @@
*/
public class SetupCmd extends Command
{
- private final ButtonMenu.Builder buttons;
- private final String MUTE = "\uD83D\uDD07";
- private final String LOGS = "\uD83D\uDCDD";
- private final String AUTOMOD = "\uD83E\uDD16";
-
private final String CANCEL = "\u274C";
private final String CONFIRM = "\u2611";
@@ -53,53 +48,133 @@
this.guildOnly = true;
this.userPermissions = new Permission[]{Permission.MANAGE_SERVER};
this.botPermissions = new Permission[]{Permission.ADMINISTRATOR};
- this.buttons = new ButtonMenu.Builder()
- .setText(Constants.SUCCESS+" **Please select a setup option**:\n\n"
- +MUTE+" 'Muted' Role\n"
- //+AUTOMOD+" Automod\n"
- +CANCEL+" Cancel")
- .setChoices(MUTE,CANCEL)
- .setEventWaiter(vortex.getEventWaiter())
- .setTimeout(1, TimeUnit.MINUTES)
- .setFinalAction(m -> m.delete().queue(s->{}, f->{}))
- ;
- this.cooldown = 20;
- this.cooldownScope = CooldownScope.GUILD;
+ this.children = new Command[]{new MuteSetupCmd(), new AutomodSetupCmd()};
}
@Override
protected void execute(CommandEvent event)
{
- buttons.setAction(re ->
+ StringBuilder sb = new StringBuilder("The following commands can be used to set up a **").append(event.getSelfUser().getName()).append("** feature:\n");
+ for(Command cmd: children)
+ sb.append("\n`").append(Constants.PREFIX).append(name).append(" ").append(cmd.getName()).append("` - ").append(cmd.getHelp());
+ event.replySuccess(sb.toString());
+ }
+
+ private class AutomodSetupCmd extends Command
+ {
+ private AutomodSetupCmd()
{
- switch(re.getName())
+ this.name = "automod";
+ this.aliases = new String[]{"auto"};
+ this.category = new Category("Settings");
+ this.help = "sets up the auto-moderator";
+ this.guildOnly = true;
+ this.userPermissions = new Permission[]{Permission.MANAGE_SERVER};
+ this.botPermissions = new Permission[]{Permission.ADMINISTRATOR};
+ this.cooldown = 20;
+ this.cooldownScope = CooldownScope.GUILD;
+ }
+
+ @Override
+ protected void execute(CommandEvent event)
+ {
+ waitForConfirmation(event, "This will enable all automod defaults. Any existing settings will not be overwritten.", () ->
{
- case MUTE:
- Role muted = OtherUtil.getMutedRole(event.getGuild());
- String confirmation;
- if(muted!=null)
- {
- if(!event.getSelfMember().canInteract(muted))
- {
- event.replyError("I cannot interact with the existing '"+muted.getName()+"' role. Please move my role(s) higher and then try again.");
- return;
- }
- if(!event.getMember().canInteract(muted))
- {
- event.replyError("You do not have permission to interact with the existing '"+muted.getName()+"' role.");
- return;
- }
- confirmation = "This will modify the existing '"+muted.getName()+"' role and assign it overrides in every channel.";
- }
- else
- confirmation = "This will create a role called 'Muted' and assign it overrides in every channel.";
- waitForConfirmation(event, confirmation, () -> setUpMutedRole(event, muted));
- break;
- case AUTOMOD:
-
- break;
+ event.getChannel().sendTyping().queue();
+ StringBuilder sb = new StringBuilder("**Automod setup complete!**");
+ if(vortex.getDatabase().actions.useDefaultSettings(event.getGuild()))
+ sb.append("\n").append(Constants.SUCCESS).append(" Set up default punishments");
+ AutomodSettings ams = vortex.getDatabase().automod.getSettings(event.getGuild());
+ if(ams.inviteStrikes==0)
+ {
+ vortex.getDatabase().automod.setInviteStrikes(event.getGuild(), 2);
+ sb.append("\n").append(Constants.SUCCESS).append(" Anti-invite set to `2` strikes");
+ }
+ if(ams.refStrikes==0)
+ {
+ vortex.getDatabase().automod.setRefStrikes(event.getGuild(), 3);
+ sb.append("\n").append(Constants.SUCCESS).append(" Anti-referral set to `3` strikes");
+ }
+ if(!ams.useAntiDuplicate())
+ {
+ vortex.getDatabase().automod.setDupeSettings(event.getGuild(), 1, 2, 4);
+ sb.append("\n").append(Constants.SUCCESS).append(" Anti-duplicate will start deleting at duplicate `2`, and will assign `1` strike each duplicate starting at duplicate `4`");
+ }
+ if(ams.copypastaStrikes==0)
+ {
+ vortex.getDatabase().automod.setCopypastaStrikes(event.getGuild(), 1);
+ sb.append("\n").append(Constants.SUCCESS).append(" Anti-copypasta set to `1` strikes");
+ }
+ if(ams.maxMentions==0)
+ {
+ vortex.getDatabase().automod.setMaxMentions(event.getGuild(), 10);
+ sb.append("\n").append(Constants.SUCCESS).append(" Maximum mentions set to `10` mentions");
+ }
+ if(ams.maxRoleMentions==0)
+ {
+ vortex.getDatabase().automod.setMaxRoleMentions(event.getGuild(), 4);
+ sb.append("\n").append(Constants.SUCCESS).append(" Maximum role mentions set to `4` mentions");
+ }
+ if(ams.maxLines==0)
+ {
+ vortex.getDatabase().automod.setMaxLines(event.getGuild(), 10);
+ sb.append("\n").append(Constants.SUCCESS).append(" Maximum lines set to `10` lines");
+ }
+ if(!ams.useAutoRaidMode())
+ {
+ vortex.getDatabase().automod.setAutoRaidMode(event.getGuild(), 10, 10);
+ sb.append("\n").append(Constants.SUCCESS).append(" Anti-Raid Mode will activate upon `10` joins in `10` seconds");
+ }
+ if(ams.dehoistChar==(char)0)
+ {
+ vortex.getDatabase().automod.setDehoistChar(event.getGuild(), '!');
+ sb.append("\n").append(Constants.SUCCESS).append(" Names starting with `!` will be dehoisted");
+ }
+ sb.append("\n").append(Constants.WARNING).append(" Any settings not shown here were not set due to being already set. Please check the automod section of the wiki (<")
+ .append(Constants.Wiki.AUTOMOD).append(">) for more information about the automod.");
+ event.replySuccess(sb.toString());
+ });
+ }
+ }
+
+ private class MuteSetupCmd extends Command
+ {
+ private MuteSetupCmd()
+ {
+ this.name = "muterole";
+ this.aliases = new String[]{"muted","mute","mutedrole"};
+ this.category = new Category("Settings");
+ this.help = "sets up the 'Muted' role";
+ this.guildOnly = true;
+ this.userPermissions = new Permission[]{Permission.MANAGE_SERVER};
+ this.botPermissions = new Permission[]{Permission.ADMINISTRATOR};
+ this.cooldown = 20;
+ this.cooldownScope = CooldownScope.GUILD;
+ }
+
+ @Override
+ protected void execute(CommandEvent event)
+ {
+ Role muted = vortex.getDatabase().settings.getSettings(event.getGuild()).getMutedRole(event.getGuild());
+ String confirmation;
+ if(muted!=null)
+ {
+ if(!event.getSelfMember().canInteract(muted))
+ {
+ event.replyError("I cannot interact with the existing '"+muted.getName()+"' role. Please move my role(s) higher and then try again.");
+ return;
+ }
+ if(!event.getMember().canInteract(muted))
+ {
+ event.replyError("You do not have permission to interact with the existing '"+muted.getName()+"' role.");
+ return;
+ }
+ confirmation = "This will modify the existing '"+muted.getName()+"' role and assign it overrides in every channel.";
}
- }).setUsers(event.getAuthor()).build().display(event.getChannel());
+ else
+ confirmation = "This will create a role called 'Muted' and assign it overrides in every channel.";
+ waitForConfirmation(event, confirmation, () -> setUpMutedRole(event, muted));
+ }
}
private void setUpMutedRole(CommandEvent event, Role role)
diff --git a/src/main/java/com/jagrosh/vortex/commands/tools/AuditCmd.java b/src/main/java/com/jagrosh/vortex/commands/tools/AuditCmd.java
new file mode 100644
index 0000000..cb17f95
--- /dev/null
+++ b/src/main/java/com/jagrosh/vortex/commands/tools/AuditCmd.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2018 John Grosh (john.a.grosh@gmail.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jagrosh.vortex.commands.tools;
+
+import com.jagrosh.jdautilities.command.Command;
+import com.jagrosh.jdautilities.command.CommandEvent;
+import com.jagrosh.jdautilities.commons.utils.FinderUtil;
+import com.jagrosh.vortex.Action;
+import com.jagrosh.vortex.Constants;
+import com.jagrosh.vortex.commands.CommandExceptionListener.CommandErrorException;
+import com.jagrosh.vortex.commands.CommandExceptionListener.CommandWarningException;
+import com.jagrosh.vortex.utils.FormatUtil;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import net.dv8tion.jda.core.EmbedBuilder;
+import net.dv8tion.jda.core.MessageBuilder;
+import net.dv8tion.jda.core.Permission;
+import net.dv8tion.jda.core.audit.ActionType;
+import net.dv8tion.jda.core.entities.Emote;
+import net.dv8tion.jda.core.entities.Role;
+import net.dv8tion.jda.core.entities.TextChannel;
+import net.dv8tion.jda.core.entities.User;
+import net.dv8tion.jda.core.requests.restaction.pagination.AuditLogPaginationAction;
+
+/**
+ *
+ * @author John Grosh (john.a.grosh@gmail.com)
+ */
+public class AuditCmd extends Command
+{
+ private final String actions;
+ private final String linestart = "\u25AB";
+ private final String unknown = "*Unknown*";
+
+ public AuditCmd()
+ {
+ this.name = "audit";
+ this.aliases = new String[]{"auditlog","auditlogs","audits"};
+ this.arguments = " [target]";
+ this.help = "fetches recent audit logs";
+ this.guildOnly = true;
+ this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS, Permission.VIEW_AUDIT_LOGS, Permission.MESSAGE_EXT_EMOJI};
+ this.userPermissions = new Permission[]{Permission.VIEW_AUDIT_LOGS};
+ this.cooldown = 5;
+ this.category = new Category("Tools");
+ StringBuilder sb = new StringBuilder("\n\nValid actions: `");
+ for(ActionType type: ActionType.values())
+ sb.append(type.name()).append("`, `");
+ actions = sb.toString().substring(0, sb.length()-3);
+ }
+
+ @Override
+ protected void execute(CommandEvent event)
+ {
+ String[] parts = event.getArgs().split("\\s+", 2);
+ AuditLogPaginationAction action = event.getGuild().getAuditLogs().cache(false).limit(10);
+ switch(parts[0].toLowerCase())
+ {
+ case "all":
+ break;
+ case "from":
+ if(parts.length==1)
+ throw new CommandErrorException("Please include a user");
+ List list = FinderUtil.findUsers(parts[1], event.getJDA());
+ if(list.isEmpty())
+ throw new CommandWarningException("No users found matching `"+parts[1]+"`");
+ if(list.size()>1)
+ throw new CommandWarningException(FormatUtil.listOfUser(list, event.getArgs()));
+ action = action.user(list.get(0));
+ break;
+ case "action":
+ if(parts.length==1)
+ throw new CommandErrorException("Please include an action"+actions);
+ ActionType type = null;
+ for(ActionType t: ActionType.values())
+ {
+ if(t.name().toLowerCase().replace("_", "").equals(parts[1].toLowerCase().replace("_", "").replace(" ", "")))
+ {
+ type = t;
+ break;
+ }
+ }
+ if(type==null)
+ throw new CommandErrorException("Please include a valid action"+actions);
+ action = action.type(type);
+ break;
+ default:
+ throw new CommandErrorException("Valid subcommands:\n\n"
+ + "`" + Constants.PREFIX + name + " all` - shows recent audit log entries\n"
+ + "`" + Constants.PREFIX + name + " from ` - shows recent entries by a user\n"
+ + "`" + Constants.PREFIX + name + " action ` shows recent entries of a certain action");
+ }
+ event.getChannel().sendTyping().queue();
+ action.queue(list ->
+ {
+ if(list.isEmpty())
+ {
+ event.replyWarning("No audit log entries found matching your criteria");
+ return;
+ }
+ EmbedBuilder eb = new EmbedBuilder().setColor(event.getSelfMember().getColor());
+ list.forEach(ale ->
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(linestart).append("User: ").append(FormatUtil.formatFullUser(ale.getUser()));
+ switch(ale.getTargetType())
+ {
+ case CHANNEL:
+ TextChannel tc = event.getGuild().getTextChannelById(ale.getTargetIdLong());
+ sb.append("\n").append(linestart).append("Channel: ")
+ .append(tc==null ? unknown : "**#"+tc.getName()+"**")
+ .append(" (ID:").append(ale.getTargetId()).append(")");
+ break;
+ case EMOTE:
+ Emote e = event.getGuild().getEmoteById(ale.getTargetIdLong());
+ sb.append("\n").append(linestart).append("Emote: ")
+ .append(e==null ? unknown : e.getAsMention())
+ .append(" (ID:").append(ale.getTargetId()).append(")");
+ break;
+ case GUILD:
+ break;
+ case INVITE:
+ break;
+ case MEMBER:
+ User u = event.getJDA().getUserById(ale.getTargetIdLong());
+ sb.append("\n").append(linestart).append("Member: ")
+ .append(u==null ? unknown : FormatUtil.formatFullUser(u));
+ break;
+ case ROLE:
+ Role r = event.getGuild().getRoleById(ale.getTargetIdLong());
+ sb.append("\n").append(linestart).append("Role: ")
+ .append(r==null ? unknown : "**"+r.getName()+"**")
+ .append(" (ID:").append(ale.getTargetId()).append(")");
+ break;
+ case WEBHOOK:
+ sb.append("\n").append(linestart).append("Webhook ID: ").append(ale.getTargetId());
+ break;
+ case UNKNOWN:
+ default:
+ sb.append("\n").append(linestart).append("Target ID: ").append(ale.getTargetId());
+ }
+ ale.getChanges().keySet().forEach(change ->
+ {
+ sb.append("\n").append(linestart).append(fixCase(change)).append(": ")
+ .append(ale.getChangeByKey(change).getOldValue()==null ? "" : "**"+ale.getChangeByKey(change).getOldValue()+"**")
+ .append(ale.getChangeByKey(change).getOldValue()==null || ale.getChangeByKey(change).getNewValue()==null ? "" : " → ")
+ .append(ale.getChangeByKey(change).getNewValue()==null ? "" : "**"+ale.getChangeByKey(change).getNewValue()+"**");
+ });
+ ale.getOptions().keySet().forEach(option ->
+ {
+ sb.append("\n").append(linestart).append(fixCase(option)).append(": ")
+ .append(ale.getOptionByName(option)==null ? unknown : "**"+ale.getOptionByName(option)+"**");
+ });
+ if(ale.getReason()!=null)
+ sb.append("\n").append(linestart).append("Reason: ").append(ale.getReason());
+ sb.append("\n").append(linestart).append("Time: **").append(ale.getCreationTime().format(DateTimeFormatter.RFC_1123_DATE_TIME)).append("**\n\u200B");
+ String str = sb.length()>1024 ? sb.substring(0,1020)+" ..." : sb.toString();
+ eb.addField(actionToEmote(ale.getType())+" "+fixCase(ale.getType().name()), str, true);
+ });
+ event.reply(new MessageBuilder()
+ .setContent(Constants.SUCCESS+" Recent Audit Logs in **"+FormatUtil.filterEveryone(event.getGuild().getName())+"**:")
+ .setEmbed(eb.build()).build());
+ }, f -> event.replyWarning("Failed to retrieve audit logs"));
+ }
+
+ private String fixCase(String input)
+ {
+ String ret = "";
+ for(int i=0; i toDehoist = event.getGuild().getMembers().stream()
.filter(m -> event.getSelfMember().canInteract(m) && m.getEffectiveName().charAt(0)<=symbol)
.collect(Collectors.toList());
- event.getChannel().sendTyping().queue();
toDehoist.forEach(m ->
{
- String newname = dehoistChar+m.getEffectiveName();
+ String newname = AutoMod.DEHOIST_PREFIX+m.getEffectiveName();
if(newname.length()>32)
newname = newname.substring(0,32);
event.getGuild().getController().setNickname(m, newname).queue();
diff --git a/src/main/java/com/jagrosh/vortex/commands/tools/InvitepruneCmd.java b/src/main/java/com/jagrosh/vortex/commands/tools/InvitepruneCmd.java
index 233e732..dc9bc2c 100644
--- a/src/main/java/com/jagrosh/vortex/commands/tools/InvitepruneCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/tools/InvitepruneCmd.java
@@ -78,7 +78,7 @@
event.getChannel().sendTyping().queue();
event.getGuild().getInvites().queue(list ->
{
- List toPrune = list.stream().filter(i -> i.getUses()<=uses).collect(Collectors.toList());
+ List toPrune = list.stream().filter(i -> !i.getInviter().isBot() && i.getUses()<=uses).collect(Collectors.toList());
toPrune.forEach(i -> i.delete().queue());
event.replySuccess("Deleted `"+toPrune.size()+"` invites with `"+uses+"` or fewer uses.");
});
diff --git a/src/main/java/com/jagrosh/vortex/database/managers/AutomodManager.java b/src/main/java/com/jagrosh/vortex/database/managers/AutomodManager.java
index 61ccd0a..ac5505e 100644
--- a/src/main/java/com/jagrosh/vortex/database/managers/AutomodManager.java
+++ b/src/main/java/com/jagrosh/vortex/database/managers/AutomodManager.java
@@ -55,6 +55,8 @@
public final static SQLColumn DUPE_STRIKES = new IntegerColumn("DUPE_STRIKES", false, 0);
public final static SQLColumn DUPE_DELETE_THRESH = new IntegerColumn("DUPE_DELETE_THRESH", false, 0);
public final static SQLColumn DUPE_STRIKE_THRESH = new IntegerColumn("DUPE_STRIKES_THRESH", false, 0);
+
+ public final static SQLColumn DEHOIST_CHAR = new IntegerColumn("DEHOIST_CHAR", false, 0);
// Cache
private final FixedCache cache = new FixedCache<>(1000);
@@ -97,9 +99,13 @@
? "Disabled\n\n"
: "Max Lines / Message: "+(settings.maxLines==0 ? "Disabled\n" : "`"+settings.maxLines+"`\n") +
"Copypasta: `" + settings.copypastaStrikes + " " + Action.STRIKE.getEmoji() + "`\n\n")
- + "__Auto Anti-Raid Mode__\n" + (settings.useAutoRaidMode()
- ? "`" + settings.raidmodeNumber + "` joins / `" + settings.raidmodeTime + "` seconds\n"
- : "Disabled\n")
+ + "__Miscellaneous__\n"
+ + "Auto AntiRaid: " + (settings.useAutoRaidMode()
+ ? "`" + settings.raidmodeNumber + "` joins/`" + settings.raidmodeTime + "`s\n"
+ : "Disabled\n")
+ + "Auto Dehoist: " + (settings.dehoistChar==(char)0
+ ? "Disabled"
+ : "`"+settings.dehoistChar+"` and above")
/*+ "\u200B"*/, true);
}
@@ -309,6 +315,26 @@
});
}
+ public void setDehoistChar(Guild guild, char dehoistChar)
+ {
+ invalidateCache(guild);
+ readWrite(selectAll(GUILD_ID.is(guild.getIdLong())), rs ->
+ {
+ if(rs.next())
+ {
+ DEHOIST_CHAR.updateValue(rs, (int)dehoistChar);
+ rs.updateRow();
+ }
+ else
+ {
+ rs.moveToInsertRow();
+ GUILD_ID.updateValue(rs, guild.getIdLong());
+ DEHOIST_CHAR.updateValue(rs, (int)dehoistChar);
+ rs.insertRow();
+ }
+ });
+ }
+
private void invalidateCache(Guild guild)
{
invalidateCache(guild.getIdLong());
@@ -327,6 +353,7 @@
public final int raidmodeNumber, raidmodeTime;
public final int inviteStrikes, refStrikes, copypastaStrikes;
public final int dupeStrikes, dupeDeleteThresh, dupeStrikeThresh;
+ public final char dehoistChar;
private AutomodSettings()
{
@@ -342,6 +369,7 @@
this.dupeStrikes = 0;
this.dupeDeleteThresh = 0;
this.dupeStrikeThresh = 0;
+ this.dehoistChar = 0;
}
private AutomodSettings(ResultSet rs) throws SQLException
@@ -358,6 +386,7 @@
this.dupeStrikes = DUPE_STRIKES.getValue(rs);
this.dupeDeleteThresh = DUPE_DELETE_THRESH.getValue(rs);
this.dupeStrikeThresh = DUPE_STRIKE_THRESH.getValue(rs);
+ this.dehoistChar = (char)((int)DEHOIST_CHAR.getValue(rs));
}
public boolean useAutoRaidMode()
diff --git a/src/main/java/com/jagrosh/vortex/database/managers/GuildSettingsDataManager.java b/src/main/java/com/jagrosh/vortex/database/managers/GuildSettingsDataManager.java
index 42a6b9f..21a8b75 100644
--- a/src/main/java/com/jagrosh/vortex/database/managers/GuildSettingsDataManager.java
+++ b/src/main/java/com/jagrosh/vortex/database/managers/GuildSettingsDataManager.java
@@ -86,8 +86,10 @@
TextChannel voicelog = settings.getVoiceLogChannel(guild);
TextChannel avylog = settings.getAvatarLogChannel(guild);
Role modrole = settings.getModeratorRole(guild);
+ Role muterole = settings.getMutedRole(guild);
return new Field(SETTINGS_TITLE, "Prefix: `"+(settings.prefix==null ? Constants.PREFIX : settings.prefix)+"`"
+ "\nMod Role: "+(modrole==null ? "None" : modrole.getAsMention())
+ + "\nMuted Role: "+(muterole==null ? "None" : muterole.getAsMention())
+ "\nMod Log: "+(modlog==null ? "None" : modlog.getAsMention())
+ "\nMessage Log: "+(messagelog==null ? "None" : messagelog.getAsMention())
+ "\nVoice Log: "+(voicelog==null ? "None" : voicelog.getAsMention())
@@ -317,7 +319,7 @@
public class GuildSettings implements GuildSettingsProvider
{
- private final long modRole, modlog, serverlog, messagelog, voicelog, avatarlog;
+ private final long modRole, muteRole, modlog, serverlog, messagelog, voicelog, avatarlog;
private final String prefix;
private final ZoneId timezone;
private final int raidMode;
@@ -326,6 +328,7 @@
{
this.modRole = 0;
this.modlog = 0;
+ this.muteRole = 0;
this.serverlog = 0;
this.messagelog = 0;
this.voicelog = 0;
@@ -338,6 +341,7 @@
private GuildSettings(ResultSet rs) throws SQLException
{
this.modRole = MOD_ROLE_ID.getValue(rs);
+ this.muteRole = 0;
this.modlog = MODLOG_ID.getValue(rs);
this.serverlog = SERVERLOG_ID.getValue(rs);
this.messagelog = MESSAGELOG_ID.getValue(rs);
@@ -354,6 +358,14 @@
return guild.getRoleById(modRole);
}
+ public Role getMutedRole(Guild guild)
+ {
+ Role rid = guild.getRoleById(muteRole);
+ if(rid!=null)
+ return rid;
+ return guild.getRoles().stream().filter(r -> r.getName().equalsIgnoreCase("Muted")).findFirst().orElse(null);
+ }
+
public TextChannel getModLogChannel(Guild guild)
{
return guild.getTextChannelById(modlog);
diff --git a/src/main/java/com/jagrosh/vortex/database/managers/PunishmentManager.java b/src/main/java/com/jagrosh/vortex/database/managers/PunishmentManager.java
index 29da0fe..2b58608 100644
--- a/src/main/java/com/jagrosh/vortex/database/managers/PunishmentManager.java
+++ b/src/main/java/com/jagrosh/vortex/database/managers/PunishmentManager.java
@@ -43,7 +43,7 @@
public static final String DEFAULT_SETUP_MESSAGE = "\n" + Constants.WARNING + " It looks like you've set up some automoderation without assigning any punishments! "
+ "I've gone ahead and set up some default punishments; you can see the settings with `" + Constants.PREFIX
- + "settings` and set or change any punishments with the `" + Constants.PREFIX+"setstrikes` command!";
+ + "settings` and set or change any punishments with the `" + Constants.PREFIX+"punishment` command!";
private static final int[] DEFAULT_STRIKE_COUNTS = {2, 3, 4, 5, 6, 7, 8};
private static final Action[] DEFAULT_ACTIONS = {Action.TEMPMUTE, Action.TEMPMUTE, Action.KICK, Action.SOFTBAN, Action.TEMPMUTE, Action.TEMPBAN, Action.BAN};
private static final int[] DEFAULT_TIMES = {10, 120, 0, 0, 1440, 1440, 0};
diff --git a/src/main/java/com/jagrosh/vortex/database/managers/TempBanManager.java b/src/main/java/com/jagrosh/vortex/database/managers/TempBanManager.java
index 2801803..026682b 100644
--- a/src/main/java/com/jagrosh/vortex/database/managers/TempBanManager.java
+++ b/src/main/java/com/jagrosh/vortex/database/managers/TempBanManager.java
@@ -92,24 +92,18 @@
});
}
- public void checkUnbans(Guild guild)
+ public void checkUnbans(JDA jda)
{
- if(!guild.isAvailable())
- return;
- if(!guild.getSelfMember().hasPermission(Permission.BAN_MEMBERS))
- return;
- readWrite(selectAll(GUILD_ID.is(guild.getId())+" AND "+FINISH.isLessThan(Instant.now().getEpochSecond())), rs ->
+ readWrite(selectAll(FINISH.isLessThan(Instant.now().getEpochSecond())), rs ->
{
while(rs.next())
{
- guild.getController().unban(Long.toString(USER_ID.getValue(rs))).reason("Temporary Ban Completed").queue(s->{}, f->{});
+ Guild g = jda.getGuildById(GUILD_ID.getValue(rs));
+ if(g==null || !g.isAvailable() || !g.getSelfMember().hasPermission(Permission.BAN_MEMBERS))
+ continue;
+ g.getController().unban(Long.toString(USER_ID.getValue(rs))).reason("Temporary Ban Completed").queue(s->{}, f->{});
rs.deleteRow();
}
});
}
-
- public void checkUnbans(JDA jda)
- {
- jda.getGuilds().forEach(g -> checkUnbans(g));
- }
}
diff --git a/src/main/java/com/jagrosh/vortex/database/managers/TempMuteManager.java b/src/main/java/com/jagrosh/vortex/database/managers/TempMuteManager.java
index 1df44f3..ba73d6b 100644
--- a/src/main/java/com/jagrosh/vortex/database/managers/TempMuteManager.java
+++ b/src/main/java/com/jagrosh/vortex/database/managers/TempMuteManager.java
@@ -20,7 +20,7 @@
import com.jagrosh.easysql.SQLColumn;
import com.jagrosh.easysql.columns.InstantColumn;
import com.jagrosh.easysql.columns.LongColumn;
-import java.sql.ResultSet;
+import com.jagrosh.vortex.utils.OtherUtil;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import net.dv8tion.jda.core.JDA;
@@ -124,29 +124,26 @@
});
}
- public void checkUnmutes(Guild guild)
+ public void checkUnmutes(JDA jda, GuildSettingsDataManager data)
{
- if(!guild.isAvailable())
- return;
- if(!guild.getSelfMember().hasPermission(Permission.MANAGE_ROLES))
- return;
- Role muted = guild.getRoles().stream().filter(r -> r.getName().equalsIgnoreCase("Muted")).findFirst().orElse(null);
- if(muted==null || !guild.getSelfMember().canInteract(muted))
- return;
- readWrite(selectAll(GUILD_ID.is(guild.getId())+" AND "+FINISH.isLessThan(Instant.now().getEpochSecond())), rs ->
+ readWrite(selectAll(FINISH.isLessThan(Instant.now().getEpochSecond())), rs ->
{
while(rs.next())
{
- Member m = guild.getMemberById(USER_ID.getValue(rs));
- if(m!=null && m.getRoles().contains(muted))
- guild.getController().removeSingleRoleFromMember(m, muted).reason("Temporary Mute Completed").queue();
+ Guild g = jda.getGuildById(GUILD_ID.getValue(rs));
+ if(g==null || !g.isAvailable() || !g.getSelfMember().hasPermission(Permission.MANAGE_ROLES))
+ continue;
+ Role mRole = data.getSettings(g).getMutedRole(g);
+ if(mRole==null || !g.getSelfMember().canInteract(mRole))
+ {
+ rs.deleteRow();
+ continue;
+ }
+ Member m = g.getMemberById(USER_ID.getValue(rs));
+ if(m!=null && m.getRoles().contains(mRole))
+ g.getController().removeSingleRoleFromMember(m, mRole).reason("Temporary Mute Completed").queue();
rs.deleteRow();
}
});
}
-
- public void checkUnmutes(JDA jda)
- {
- jda.getGuilds().forEach(g -> checkUnmutes(g));
- }
}
diff --git a/src/main/java/com/jagrosh/vortex/logging/BasicLogger.java b/src/main/java/com/jagrosh/vortex/logging/BasicLogger.java
index c329bee..825c583 100644
--- a/src/main/java/com/jagrosh/vortex/logging/BasicLogger.java
+++ b/src/main/java/com/jagrosh/vortex/logging/BasicLogger.java
@@ -20,7 +20,6 @@
import com.jagrosh.vortex.utils.FormatUtil;
import com.jagrosh.vortex.utils.LogUtil;
import java.awt.Color;
-import java.awt.image.BufferedImage;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
@@ -28,8 +27,10 @@
import java.util.stream.Collectors;
import net.dv8tion.jda.core.EmbedBuilder;
import net.dv8tion.jda.core.MessageBuilder;
+import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.entities.MessageEmbed;
+import net.dv8tion.jda.core.entities.PermissionOverride;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.events.guild.member.GuildMemberJoinEvent;
import net.dv8tion.jda.core.events.guild.member.GuildMemberLeaveEvent;
@@ -38,6 +39,9 @@
import net.dv8tion.jda.core.events.guild.voice.GuildVoiceMoveEvent;
import net.dv8tion.jda.core.events.user.UserAvatarUpdateEvent;
import net.dv8tion.jda.core.events.user.UserNameUpdateEvent;
+import net.dv8tion.jda.core.events.user.update.UserUpdateAvatarEvent;
+import net.dv8tion.jda.core.events.user.update.UserUpdateDiscriminatorEvent;
+import net.dv8tion.jda.core.events.user.update.UserUpdateNameEvent;
import net.dv8tion.jda.core.exceptions.PermissionException;
/**
@@ -88,6 +92,9 @@
{
if(oldMessage==null)
return;
+ PermissionOverride po = oldMessage.getTextChannel().getPermissionOverride(oldMessage.getGuild().getSelfMember());
+ if(po!=null && po.getDenied().contains(Permission.MESSAGE_HISTORY))
+ return;
TextChannel tc = vortex.getDatabase().settings.getSettings(newMessage.getGuild()).getMessageLogChannel(newMessage.getGuild());
if(tc==null)
return;
@@ -110,6 +117,9 @@
{
if(oldMessage==null)
return;
+ PermissionOverride po = oldMessage.getTextChannel().getPermissionOverride(oldMessage.getGuild().getSelfMember());
+ if(po!=null && po.getDenied().contains(Permission.MESSAGE_HISTORY))
+ return;
TextChannel tc = vortex.getDatabase().settings.getSettings(oldMessage.getGuild()).getMessageLogChannel(oldMessage.getGuild());
if(tc==null)
return;
@@ -135,6 +145,9 @@
//log(OffsetDateTime.now(), tc, "\uD83D\uDEAE", "**"+count+"** messages were deleted from "+text.getAsMention()+" (**"+messages.size()+"** logged)", null);
return;
}
+ PermissionOverride po = messages.get(0).getTextChannel().getPermissionOverride(messages.get(0).getGuild().getSelfMember());
+ if(po!=null && po.getDenied().contains(Permission.MESSAGE_HISTORY))
+ return;
if(messages.size()==1)
{
String formatted = FormatUtil.formatMessage(messages.get(0));
@@ -147,7 +160,7 @@
FormatUtil.formatFullUser(messages.get(0).getAuthor())+"'s message has been deleted from "+messages.get(0).getTextChannel().getAsMention()+":", delete.build());
return;
}
- vortex.getTextUploader().upload(LogUtil.logMessages("Deleted Messages", messages), "DeletedMessages", (view, download) ->
+ vortex.getTextUploader().upload(LogUtil.logMessagesForwards("Deleted Messages", messages), "DeletedMessages", (view, download) ->
{
log(OffsetDateTime.now(), tc, "\uD83D\uDEAE", "**"+count+"** messages were deleted from "+text.getAsMention()+" (**"+messages.size()+"** logged):",
new EmbedBuilder().setColor(Color.RED.darker().darker())
@@ -171,7 +184,7 @@
// Server Logs
- public void logNameChange(UserNameUpdateEvent event)
+ public void logNameChange(UserUpdateNameEvent event)
{
OffsetDateTime now = OffsetDateTime.now();
event.getUser().getMutualGuilds().stream()
@@ -180,7 +193,20 @@
.forEachOrdered(tc ->
{
log(now, tc, "\uD83D\uDCDB",
- "**"+event.getOldName()+"**#"+event.getOldDiscriminator()+" (ID:"+event.getUser().getId()+") has changed names to "+FormatUtil.formatUser(event.getUser()), null);
+ "**"+event.getOldName()+"**#"+event.getUser().getDiscriminator()+" (ID:"+event.getUser().getId()+") has changed names to "+FormatUtil.formatUser(event.getUser()), null);
+ });
+ }
+
+ public void logNameChange(UserUpdateDiscriminatorEvent event)
+ {
+ OffsetDateTime now = OffsetDateTime.now();
+ event.getUser().getMutualGuilds().stream()
+ .map(guild -> vortex.getDatabase().settings.getSettings(guild).getServerLogChannel(guild))
+ .filter(tc -> tc!=null)
+ .forEachOrdered(tc ->
+ {
+ log(now, tc, "\uD83D\uDCDB",
+ "**"+event.getUser().getName()+"**#"+event.getOldDiscriminator()+" (ID:"+event.getUser().getId()+") has changed names to "+FormatUtil.formatUser(event.getUser()), null);
});
}
@@ -210,7 +236,7 @@
{
rlist= new StringBuilder("\nRoles: `"+event.getMember().getRoles().get(0).getName());
for(int i=1; i logs = event.getUser().getMutualGuilds().stream()
.map(guild -> vortex.getDatabase().settings.getSettings(guild).getAvatarLogChannel(guild))
@@ -262,7 +288,7 @@
OffsetDateTime now = OffsetDateTime.now();
vortex.getThreadpool().execute(() ->
{
- byte[] im = AvatarUtil.makeAvatarImage(event.getUser(), event.getPreviousAvatarUrl(), event.getPreviousAvatarId());
+ byte[] im = AvatarUtil.makeAvatarImage(event.getUser(), event.getOldAvatarUrl(), event.getOldAvatarId());
if(im!=null)
logs.forEach(tc -> logFile(now, tc, "\uD83D\uDDBC", FormatUtil.formatFullUser(event.getUser())+" has changed avatars"
+(event.getUser().getAvatarId()!=null && event.getUser().getAvatarId().startsWith("a_") ? " <:gif:314068430624129039>" : "")
diff --git a/src/main/java/com/jagrosh/vortex/logging/ModLogger.java b/src/main/java/com/jagrosh/vortex/logging/ModLogger.java
index 4c4733a..18707f5 100644
--- a/src/main/java/com/jagrosh/vortex/logging/ModLogger.java
+++ b/src/main/java/com/jagrosh/vortex/logging/ModLogger.java
@@ -18,6 +18,7 @@
import com.jagrosh.vortex.Action;
import com.jagrosh.vortex.Vortex;
import com.jagrosh.vortex.automod.AutoMod;
+import com.jagrosh.vortex.database.managers.GuildSettingsDataManager.GuildSettings;
import com.jagrosh.vortex.utils.FixedCache;
import com.jagrosh.vortex.utils.FormatUtil;
import com.jagrosh.vortex.utils.LogUtil;
@@ -36,12 +37,7 @@
import net.dv8tion.jda.core.audit.AuditLogChange;
import net.dv8tion.jda.core.audit.AuditLogEntry;
import net.dv8tion.jda.core.audit.AuditLogKey;
-import net.dv8tion.jda.core.entities.Guild;
-import net.dv8tion.jda.core.entities.Member;
-import net.dv8tion.jda.core.entities.Message;
-import net.dv8tion.jda.core.entities.MessageEmbed;
-import net.dv8tion.jda.core.entities.TextChannel;
-import net.dv8tion.jda.core.entities.User;
+import net.dv8tion.jda.core.entities.*;
import net.dv8tion.jda.core.exceptions.PermissionException;
/**
@@ -196,9 +192,11 @@
{
if(guild==null)
return;
- TextChannel modlog = vortex.getDatabase().settings.getSettings(guild).getModLogChannel(guild);
+ GuildSettings gs = vortex.getDatabase().settings.getSettings(guild);
+ TextChannel modlog = gs.getModLogChannel(guild);
if(modlog==null || !modlog.canTalk() || !modlog.getGuild().getSelfMember().hasPermission(Permission.VIEW_AUDIT_LOGS))
return;
+ Role mRole = gs.getMutedRole(guild);
try
{
List list = guild.getAuditLogs().cache(false).limit(limit).complete();
@@ -211,10 +209,12 @@
case KICK: act = Action.KICK; break;
case UNBAN: act = Action.UNBAN; break;
case MEMBER_ROLE_UPDATE:
+ if(mRole==null)
+ break;
AuditLogChange added = ale.getChangeByKey(AuditLogKey.MEMBER_ROLES_ADD);
if(added!=null)
{
- if (((ArrayList>)added.getNewValue()).stream().anyMatch(hm -> hm.get("name").equals("Muted")))
+ if (((ArrayList>)added.getNewValue()).stream().anyMatch(hm -> hm.get("id").equals(mRole.getId())))
{
act = Action.MUTE;
break;
@@ -223,7 +223,7 @@
AuditLogChange removed = ale.getChangeByKey(AuditLogKey.MEMBER_ROLES_REMOVE);
if(removed!=null)
{
- if (((ArrayList>)removed.getNewValue()).stream().anyMatch(hm -> hm.get("name").equals("Muted")))
+ if (((ArrayList>)removed.getNewValue()).stream().anyMatch(hm -> hm.get("id").equals(mRole.getId())))
{
act = Action.UNMUTE;
break;
diff --git a/src/main/java/com/jagrosh/vortex/utils/ArgsUtil.java b/src/main/java/com/jagrosh/vortex/utils/ArgsUtil.java
index a0a8fa3..63a6cc4 100644
--- a/src/main/java/com/jagrosh/vortex/utils/ArgsUtil.java
+++ b/src/main/java/com/jagrosh/vortex/utils/ArgsUtil.java
@@ -92,7 +92,13 @@
mat = ID.matcher(args);
if(mat.find())
{
- i = Long.parseLong(mat.group(1));
+ try
+ {
+ i = Long.parseLong(mat.group(1));
+ }catch(NumberFormatException ex)
+ {
+ i = 0;
+ }
u = guild.getJDA().getUserById(i);
if(u==null)
ids.add(i);
diff --git a/src/main/java/com/jagrosh/vortex/utils/FormatUtil.java b/src/main/java/com/jagrosh/vortex/utils/FormatUtil.java
index 3563b79..31fa892 100644
--- a/src/main/java/com/jagrosh/vortex/utils/FormatUtil.java
+++ b/src/main/java/com/jagrosh/vortex/utils/FormatUtil.java
@@ -25,6 +25,7 @@
import com.jagrosh.vortex.Vortex;
import java.awt.Color;
import java.util.Collections;
+import java.util.function.Function;
import java.util.stream.Collectors;
import net.dv8tion.jda.core.EmbedBuilder;
import net.dv8tion.jda.core.MessageBuilder;
@@ -38,7 +39,7 @@
*/
public class FormatUtil {
- private final static String MULTIPLE_FOUND = Constants.WARNING+" **Multiple %s found matching \"%s\":**";
+ private final static String MULTIPLE_FOUND = "**Multiple %s found matching \"%s\":**";
private final static String CMD_EMOJI = "\uD83D\uDCDC";
public static String filterEveryone(String input)
@@ -72,6 +73,16 @@
return Character.toUpperCase(input.charAt(0))+input.substring(1).toLowerCase();
}
+ public static String join(String delimiter, Function function, T... items)
+ {
+ if(items==null || items.length==0)
+ return "";
+ StringBuilder sb = new StringBuilder(function.apply(items[0]));
+ for(int i=1; i list, String query)
{
String out = String.format(MULTIPLE_FOUND, "voice channels", query);
@@ -227,8 +238,8 @@
if(cmd.isHidden() || cmd.isOwnerCommand())
return false;
if(cmd.getCategory()==null)
- return event.getArgs().equalsIgnoreCase("general");
- return cmd.getCategory().getName().equalsIgnoreCase(event.getArgs());
+ return "general".startsWith(event.getArgs().toLowerCase());
+ return cmd.getCategory().getName().toLowerCase().startsWith(event.getArgs().toLowerCase());
}).collect(Collectors.toList());
if(commandsInCategory.isEmpty())
content = Constants.WARNING+" No Category `"+event.getArgs()+"` found.";
diff --git a/src/main/java/com/jagrosh/vortex/utils/LogUtil.java b/src/main/java/com/jagrosh/vortex/utils/LogUtil.java
index 79c1024..587127b 100644
--- a/src/main/java/com/jagrosh/vortex/utils/LogUtil.java
+++ b/src/main/java/com/jagrosh/vortex/utils/LogUtil.java
@@ -112,7 +112,25 @@
return String.format(BASICLOG_FORMAT, timeF(time, zone), emoji, content);
}
- public static String logMessages(String title, List messages)
+ public static String logMessagesForwards(String title, List messages)
+ {
+ TextChannel deltc = messages.get(0).getTextChannel();
+ Guild delg = messages.get(0).getGuild();
+ StringBuilder sb = new StringBuilder("-- "+title+" -- #"+deltc.getName()+" ("+deltc.getId()+") -- "+delg.getName()+" ("+delg.getId()+") --");
+ Message m;
+ for(int i=0; i sb.append("\n").append(att.getUrl()));
+ }
+ return sb.toString().trim();
+ }
+
+ public static String logMessagesBackwards(String title, List messages)
{
TextChannel deltc = messages.get(0).getTextChannel();
Guild delg = messages.get(0).getGuild();
@@ -137,8 +155,8 @@
private final static String AUDIT_BASIC_FORMAT = A_MOD + A_REASON;
private final static String AUDIT_TIMED_FORMAT = A_MOD + A_TIME + A_REASON;
- private final static Pattern AUDIT_BASIC_PATTERN = Pattern.compile("^(\\S.{0,32}\\S)#(\\d{4}): (.*)$");
- private final static Pattern AUDIT_TIMED_PATTERN = Pattern.compile("^(\\S.{0,32}\\S)#(\\d{4}) \\((\\d{1,9})m\\): (.*)$");
+ private final static Pattern AUDIT_BASIC_PATTERN = Pattern.compile("^(\\S.{0,32}\\S)#(\\d{4}): (.*)$", Pattern.DOTALL);
+ private final static Pattern AUDIT_TIMED_PATTERN = Pattern.compile("^(\\S.{0,32}\\S)#(\\d{4}) \\((\\d{1,9})m\\): (.*)$", Pattern.DOTALL);
// Auditlog methods
public static String auditStrikeReasonFormat(Member moderator, int minutes, int oldstrikes, int newstrikes, String reason)
diff --git a/src/main/java/com/jagrosh/vortex/utils/OtherUtil.java b/src/main/java/com/jagrosh/vortex/utils/OtherUtil.java
index 58b4564..52b1393 100644
--- a/src/main/java/com/jagrosh/vortex/utils/OtherUtil.java
+++ b/src/main/java/com/jagrosh/vortex/utils/OtherUtil.java
@@ -23,7 +23,6 @@
import net.dv8tion.jda.core.Region;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
-import net.dv8tion.jda.core.entities.Role;
import net.dv8tion.jda.core.entities.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -36,11 +35,6 @@
{
private final static Logger LOG = LoggerFactory.getLogger(OtherUtil.class);
- public static Role getMutedRole(Guild guild)
- {
- return guild.getRoles().stream().filter(r -> r.getName().equalsIgnoreCase("Muted")).findFirst().orElse(null);
- }
-
public static void safeDM(User user, String message, boolean shouldDM, Runnable then)
{
if(user==null || !shouldDM)