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)