diff --git a/pom.xml b/pom.xml
index 2cc3470..e8a7bdd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,9 +14,8 @@
- dv8tion
- m2-dv8tion
- https://m2.dv8tion.net/releases
+ jitpack.io
+ https://jitpack.io
jcenter
@@ -26,11 +25,11 @@
-
- net.dv8tion
- JDA
- 4.3.0_307
-
+
+ com.github.jagrosh
+ JDA
+ vortex-SNAPSHOT
+
com.jagrosh
jda-utilities
diff --git a/src/main/java/com/jagrosh/vortex/Listener.java b/src/main/java/com/jagrosh/vortex/Listener.java
index 60ef3b8..e4fad8e 100644
--- a/src/main/java/com/jagrosh/vortex/Listener.java
+++ b/src/main/java/com/jagrosh/vortex/Listener.java
@@ -30,6 +30,7 @@
import net.dv8tion.jda.api.events.guild.GuildUnbanEvent;
import net.dv8tion.jda.api.events.guild.member.*;
import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateNicknameEvent;
+import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateTimeOutEvent;
import net.dv8tion.jda.api.events.guild.voice.GuildVoiceJoinEvent;
import net.dv8tion.jda.api.events.guild.voice.GuildVoiceLeaveEvent;
import net.dv8tion.jda.api.events.guild.voice.GuildVoiceMoveEvent;
@@ -215,6 +216,11 @@
{
vortex.getDatabase().tempslowmodes.clearSlowmode(((TextChannelUpdateSlowmodeEvent) event).getChannel());
}
+ else if (event instanceof GuildMemberUpdateTimeOutEvent)
+ {
+ // Signal the modlogger because someone was timed out
+ vortex.getModLogger().setNeedUpdate(((GuildMemberUpdateTimeOutEvent) event).getGuild());
+ }
else if (event instanceof GuildJoinEvent)
{
Guild guild = ((GuildJoinEvent)event).getGuild();
diff --git a/src/main/java/com/jagrosh/vortex/Vortex.java b/src/main/java/com/jagrosh/vortex/Vortex.java
index 39032d6..7a6c890 100644
--- a/src/main/java/com/jagrosh/vortex/Vortex.java
+++ b/src/main/java/com/jagrosh/vortex/Vortex.java
@@ -80,7 +80,7 @@
System.setProperty("config.file", System.getProperty("config.file", "application.conf"));
config = ConfigFactory.load();
waiter = new EventWaiter(Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "eventwaiter")), false);
- threadpool = Executors.newScheduledThreadPool(100, r -> new Thread(r, "vortex"));
+ threadpool = Executors.newScheduledThreadPool(400, r -> new Thread(r, "vortex"));
database = new Database(config.getString("database.host"),
config.getString("database.username"),
config.getString("database.password"));
@@ -109,7 +109,7 @@
new InviteCmd(),
new PingCommand(),
new RoleinfoCommand(),
- new ServerinfoCommand(),
+ new ServerinfoCmd(),
new UserinfoCmd(),
// Moderation
@@ -140,6 +140,7 @@
new AvatarlogCmd(this),
new TimezoneCmd(this),
new ModroleCmd(this),
+ new MuteroleCmd(this),
new PrefixCmd(this),
new SettingsCmd(this),
@@ -203,6 +204,7 @@
threadpool.scheduleWithFixedDelay(() -> database.tempbans.checkUnbans(shards), 0, 2, TimeUnit.MINUTES);
threadpool.scheduleWithFixedDelay(() -> database.tempmutes.checkUnmutes(shards, database.settings), 0, 45, TimeUnit.SECONDS);
threadpool.scheduleWithFixedDelay(() -> database.tempslowmodes.checkSlowmode(shards), 0, 45, TimeUnit.SECONDS);
+ threadpool.scheduleWithFixedDelay(() -> database.premium.checkPremiumSubscriptions(shards), 5, 10, TimeUnit.HOURS);
}
diff --git a/src/main/java/com/jagrosh/vortex/automod/AutoMod.java b/src/main/java/com/jagrosh/vortex/automod/AutoMod.java
index 037df40..17faaa7 100644
--- a/src/main/java/com/jagrosh/vortex/automod/AutoMod.java
+++ b/src/main/java/com/jagrosh/vortex/automod/AutoMod.java
@@ -485,8 +485,8 @@
LOG.trace("Found "+invites.size()+" invites.");
for(String inviteCode : invites)
{
- LOG.info("Resolving invite in " + message.getGuild().getId() + ": " + inviteCode);
long gid = inviteResolver.resolve(inviteCode, message.getJDA());
+ LOG.info("Resolving invite in " + message.getGuild().getId() + ": " + inviteCode + " -> " + gid);
if(gid != message.getGuild().getIdLong() && !inviteWhitelist.contains(gid))
{
currentStatus.addStrikes(settings.inviteStrikes);
diff --git a/src/main/java/com/jagrosh/vortex/automod/InviteResolver.java b/src/main/java/com/jagrosh/vortex/automod/InviteResolver.java
index 5d9d135..3b81262 100644
--- a/src/main/java/com/jagrosh/vortex/automod/InviteResolver.java
+++ b/src/main/java/com/jagrosh/vortex/automod/InviteResolver.java
@@ -16,8 +16,19 @@
package com.jagrosh.vortex.automod;
import com.jagrosh.vortex.utils.FixedCache;
+import java.io.IOException;
+import java.time.OffsetDateTime;
+import java.util.Collections;
import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.entities.ChannelType;
+import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Invite;
+import net.dv8tion.jda.api.utils.MiscUtil;
+import net.dv8tion.jda.internal.entities.InviteImpl;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -27,19 +38,46 @@
*/
public class InviteResolver
{
+ private final static String[] BASE_URLS = {"discordapp.com", "ptb.discordapp.com", "canary.discordapp.com",
+ /*"discord.com",*/ "ptb.discord.com", "canary.discord.com", "discord.co", "watchanimeattheoffice.com", "discord.new"};
private final Logger log = LoggerFactory.getLogger(InviteResolver.class);
private final FixedCache cached = new FixedCache<>(5000);
+ private final OkHttpClient client = new OkHttpClient.Builder().build();
+
+ public Invite resolveFull(String code, JDA jda)
+ {
+ return resolveFull(code, jda.getToken());
+ }
+
+ public Invite resolveFull(String code, String token)
+ {
+ log.debug("Attempting to resolve " + code);
+ try
+ {
+ return getInvite(code, token);
+ }
+ catch(Exception ex)
+ {
+ return null;
+ }
+ }
public long resolve(String code, JDA jda)
{
+ return resolve(code, jda.getToken());
+ }
+
+ public long resolve(String code, String token)
+ {
log.debug("Attempting to resolve " + code);
if(cached.contains(code))
return cached.get(code);
try
{
- Invite i = Invite.resolve(jda, code).complete(false);
- cached.put(code, i.getGuild().getIdLong());
- return i.getGuild().getIdLong();
+ long guildId = getInviteGuild(code, token);
+ //long guildId = Invite.resolve(jda, code).complete(false).getGuild().getIdLong();
+ cached.put(code, guildId);
+ return guildId;
}
catch(Exception ex)
{
@@ -47,4 +85,69 @@
return 0L;
}
}
+
+ private long getInviteGuild(String code, String token) throws IOException
+ {
+ JSONObject json = getInviteJSON(code, token);
+ if(json != null)
+ return MiscUtil.parseSnowflake(json.getJSONObject("guild").getString("id"));
+ return 0L;
+ }
+
+ private InviteImpl getInvite(String code, String token) throws IOException
+ {
+ JSONObject json = getInviteJSON(code, token);
+ if(json != null)
+ {
+ Invite.Channel channel;
+ if(json.has("channel"))
+ {
+ JSONObject c = json.getJSONObject("channel");
+ channel = new InviteImpl.ChannelImpl(c.optLong("id"), c.optString("name"), c.optInt("type") == 0 ? ChannelType.TEXT : ChannelType.UNKNOWN);
+ }
+ else
+ channel = null;
+ Invite.Guild guild;
+ if(json.has("guild"))
+ {
+ JSONObject g = json.getJSONObject("guild");
+ guild = new InviteImpl.GuildImpl(g.optLong("id"), g.optString("icon"), g.optString("name"), g.optString("splash"), Guild.VerificationLevel.UNKNOWN, -1, -1, Collections.emptySet());
+ }
+ else
+ guild = null;
+ return new InviteImpl(null, code, false, null, 0, 0, false, OffsetDateTime.now(), 0, channel, guild, null, null, Invite.InviteType.GUILD);
+ }
+ return null;
+ }
+
+ private JSONObject getInviteJSON(String code, String token) throws IOException
+ {
+ Response res = client.newCall(new Request.Builder().get()
+ .addHeader("Authorization", token)
+ .addHeader("User-Agent", "DiscordBot (VortexInviteResolver, 1.0)")
+ .url("https://" + rand(BASE_URLS) + "/api/invites/" + code)
+ .build()).execute();
+ if(res.isSuccessful())
+ {
+ return new JSONObject(res.body().string());
+ }
+
+ // if it failed, log why (usually unknown invite or cloudflare issue)
+ try
+ {
+ String message = new JSONObject(res.body().string()).getString("message");
+ log.warn(String.format("Unsuccessful resolving '%s': %s", code, message));
+ }
+ catch(Exception ex)
+ {
+ log.warn(String.format("Unsuccessful resolving '%s' | Headers: %s | Body: %s", code,
+ res.headers().toMultimap().toString(), res.body() == null ? null : res.body().string()));
+ }
+ return null;
+ }
+
+ private T rand(T[] items)
+ {
+ return items[(int)(Math.random() * items.length)];
+ }
}
diff --git a/src/main/java/com/jagrosh/vortex/commands/automod/AntieveryoneCmd.java b/src/main/java/com/jagrosh/vortex/commands/automod/AntieveryoneCmd.java
index 84d7465..43105b0 100644
--- a/src/main/java/com/jagrosh/vortex/commands/automod/AntieveryoneCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/automod/AntieveryoneCmd.java
@@ -74,6 +74,9 @@
vortex.getDatabase().automod.setEveryoneStrikes(event.getGuild(), numstrikes);
event.replySuccess("Users will now receive `"+numstrikes+"` strikes for attempting to ping @\u0435veryone/here. " // cyrillic e
- + "This also considers pingable roles called 'everyone' and 'here'. This will not affect users that actually ");
+ + "This also considers pingable roles called 'everyone' and 'here'. This will not affect users that actually "
+ + "have permission to ping everyone. Pinging oneself or bots do not count towards this total. Duplicate "
+ + "pings in the same message are also not counted towards this total (pinging the same person 3 times only "
+ + "counts as 1 ping).");
}
}
diff --git a/src/main/java/com/jagrosh/vortex/commands/general/ServerinfoCmd.java b/src/main/java/com/jagrosh/vortex/commands/general/ServerinfoCmd.java
new file mode 100644
index 0000000..0ef3c49
--- /dev/null
+++ b/src/main/java/com/jagrosh/vortex/commands/general/ServerinfoCmd.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2022 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.general;
+
+import com.jagrosh.jdautilities.command.Command;
+import com.jagrosh.jdautilities.command.CommandEvent;
+import java.time.format.DateTimeFormatter;
+import net.dv8tion.jda.api.*;
+import net.dv8tion.jda.api.entities.Guild;
+import net.dv8tion.jda.api.entities.Member;
+
+/**
+ *
+ * @author John Grosh (john.a.grosh@gmail.com)
+ */
+public class ServerinfoCmd extends Command
+{
+ private final static String LINESTART = "\u25AB"; // â–«
+ private final static String GUILD_EMOJI = "\uD83D\uDDA5"; // 🖥
+ private final static String NO_REGION = "\u2754"; // �
+
+ public ServerinfoCmd()
+ {
+ this.name = "serverinfo";
+ this.aliases = new String[]{"server","guildinfo"};
+ this.help = "shows server info";
+ this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
+ this.guildOnly = true;
+ }
+
+ @Override
+ protected void execute(CommandEvent event)
+ {
+ Guild guild = event.getGuild();
+ Member owner = guild.getOwner();
+ long onlineCount = guild.getMembers().stream().filter(u -> u.getOnlineStatus() != OnlineStatus.OFFLINE).count();
+ long botCount = guild.getMembers().stream().filter(m -> m.getUser().isBot()).count();
+ EmbedBuilder builder = new EmbedBuilder();
+ String title = (GUILD_EMOJI + " Information about **" + guild.getName() + "**:")
+ .replace("@everyone", "@\u0435veryone") // cyrillic e
+ .replace("@here", "@h\u0435re") // cyrillic e
+ .replace("discord.gg/", "dis\u0441ord.gg/"); // cyrillic c;
+ String str = LINESTART + "ID: **" + guild.getId() + "**\n"
+ + LINESTART + "Owner: " + (owner == null ? "Unknown" : "**" + owner.getUser().getName() + "**#" + owner.getUser().getDiscriminator()) + "\n";
+ if(!guild.getVoiceChannels().isEmpty())
+ {
+ Region reg = guild.getVoiceChannels().get(0).getRegion();
+ str += LINESTART + "Location: " + (reg.getEmoji() == null || reg.getEmoji().isEmpty() ? NO_REGION : reg.getEmoji()) + " **" + reg.getName() + "**\n";
+ }
+ str += LINESTART + "Creation: **" + guild.getTimeCreated().format(DateTimeFormatter.RFC_1123_DATE_TIME) + "**\n"
+ + LINESTART + "Users: **" + guild.getMemberCache().size() + "** (" + onlineCount + " online, " + botCount + " bots)\n"
+ + LINESTART + "Channels: **" + guild.getTextChannelCache().size() + "** Text, **" + guild.getVoiceChannelCache().size() + "** Voice, **" + guild.getCategoryCache().size() + "** Categories\n"
+ + LINESTART + "Verification: **" + guild.getVerificationLevel().name() + "**";
+ if(!guild.getFeatures().isEmpty())
+ str += "\n" + LINESTART + "Features: **" + String.join("**, **", guild.getFeatures()) + "**";
+ if(guild.getSplashId() != null)
+ {
+ builder.setImage(guild.getSplashUrl() + "?size=1024");
+ str += "\n" + LINESTART + "Splash: ";
+ }
+ if(guild.getIconUrl()!=null)
+ builder.setThumbnail(guild.getIconUrl());
+ builder.setColor(owner == null ? null : owner.getColor());
+ builder.setDescription(str);
+ event.reply(new MessageBuilder().append(title).setEmbed(builder.build()).build());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/jagrosh/vortex/commands/moderation/UnbanCmd.java b/src/main/java/com/jagrosh/vortex/commands/moderation/UnbanCmd.java
index 85c10b0..6d8b88d 100644
--- a/src/main/java/com/jagrosh/vortex/commands/moderation/UnbanCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/moderation/UnbanCmd.java
@@ -25,7 +25,7 @@
import com.jagrosh.vortex.utils.LogUtil;
import java.util.LinkedList;
import java.util.List;
-import net.dv8tion.jda.api.entities.Guild.Ban;
+//import net.dv8tion.jda.api.entities.Guild.Ban;
/**
*
@@ -54,25 +54,25 @@
String reason = LogUtil.auditReasonFormat(event.getMember(), args.reason);
StringBuilder builder = new StringBuilder();
- event.getGuild().retrieveBanList().queue(list ->
- {
- List toUnban = new LinkedList<>();
- args.members.forEach(m -> args.users.add(m.getUser()));
+ //event.getGuild().retrieveBanList().queue(list ->
+ //{
+ List toUnban = new LinkedList<>();
+ args.members.forEach(m -> builder.append("\n").append(event.getClient().getError()).append(" ").append(FormatUtil.formatUser(m.getUser())).append(" is not banned!"));
args.users.forEach(u ->
{
- Ban ban = list.stream().filter(b -> b.getUser().getIdLong()==u.getIdLong()).findFirst().orElse(null);
- if(ban==null)
- builder.append("\n").append(event.getClient().getError()).append(" ").append(FormatUtil.formatUser(u)).append(" is not banned!");
- else
- toUnban.add(ban);
+ //Ban ban = list.stream().filter(b -> b.getUser().getIdLong()==u.getIdLong()).findFirst().orElse(null);
+ //if(ban==null)
+ // builder.append("\n").append(event.getClient().getError()).append(" ").append(FormatUtil.formatUser(u)).append(" is not banned!");
+ //else
+ toUnban.add(u.getIdLong());
});
args.ids.forEach(id ->
{
- Ban ban = list.stream().filter(b -> b.getUser().getIdLong()==id).findFirst().orElse(null);
- if(ban==null)
- builder.append("\n").append(event.getClient().getError()).append(" <@").append(id).append("> is not banned!");
- else
- toUnban.add(ban);
+ //Ban ban = list.stream().filter(b -> b.getUser().getIdLong()==id).findFirst().orElse(null);
+ //if(ban==null)
+ // builder.append("\n").append(event.getClient().getError()).append(" <@").append(id).append("> is not banned!");
+ //else
+ toUnban.add(id);
});
args.unresolved.forEach(un -> builder.append("\n").append(event.getClient().getWarning()).append(" Could not resolve `").append(un).append("` to a user ID"));
@@ -87,21 +87,21 @@
for(int i=0; i
+ event.getGuild().unban(Long.toString(ban)).reason(reason).queue(success ->
{
- builder.append("\n").append(event.getClient().getSuccess()).append(" Successfully unbanned ").append(FormatUtil.formatUser(ban.getUser()));
+ builder.append("\n").append(event.getClient().getSuccess()).append(" Successfully unbanned <@").append(ban).append(">");
if(last)
event.reply(builder.toString());
}, f ->
{
- builder.append("\n").append(event.getClient().getError()).append(" Failed to unban ").append(FormatUtil.formatUser(ban.getUser()));
+ builder.append("\n").append(event.getClient().getError()).append(" Failed to unban <@").append(ban).append(">");
if(last)
event.reply(builder.toString());
});
}
- }, f -> event.replyError("Failed to retreive the ban list."));
+ //}, f -> event.replyError("Failed to retreive the ban list."));
}
}
diff --git a/src/main/java/com/jagrosh/vortex/commands/moderation/VoicemoveCmd.java b/src/main/java/com/jagrosh/vortex/commands/moderation/VoicemoveCmd.java
index ae181d8..6a9df0e 100644
--- a/src/main/java/com/jagrosh/vortex/commands/moderation/VoicemoveCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/moderation/VoicemoveCmd.java
@@ -39,6 +39,8 @@
this.aliases = new String[]{"magnet"};
this.help = "mass-moves voice channel users";
this.arguments = "[channel]";
+ this.cooldown = 20;
+ this.cooldownScope = CooldownScope.GUILD;
}
@Override
@@ -91,6 +93,7 @@
catch(Exception e)
{
event.replyWarning(FormatUtil.filterEveryone("I could not connect to **"+vc.getName()+"**"));
+ event.getGuild().getAudioManager().closeAudioConnection();
return;
}
vortex.getEventWaiter().waitForEvent(GuildVoiceMoveEvent.class,
@@ -104,6 +107,8 @@
event.replyWarning("You waited too long, "+event.getMember().getAsMention());
});
event.reply("\uD83C\uDF9B Now, move me and I'll drag users to a new voice channel."); // 🎛
+ // failsafe to leave voice channel
+ event.getClient().getScheduleExecutor().schedule(() -> event.getGuild().getAudioManager().closeAudioConnection(), 2, TimeUnit.MINUTES);
}
}
diff --git a/src/main/java/com/jagrosh/vortex/commands/owner/PremiumCmd.java b/src/main/java/com/jagrosh/vortex/commands/owner/PremiumCmd.java
index fd04a01..cc6c565 100644
--- a/src/main/java/com/jagrosh/vortex/commands/owner/PremiumCmd.java
+++ b/src/main/java/com/jagrosh/vortex/commands/owner/PremiumCmd.java
@@ -24,6 +24,7 @@
import java.time.temporal.ChronoUnit;
import java.util.stream.Collectors;
import net.dv8tion.jda.api.entities.Guild;
+import net.dv8tion.jda.api.entities.User;
/**
*
@@ -37,48 +38,121 @@
{
this.vortex = vortex;
this.name = "premium";
- this.help = "gives premium";
- this.arguments = "