Search in sources :

Example 1 with Member

use of net.dv8tion.jda.api.entities.Member in project MantaroBot by Mantaro.

the class BirthdayTask method handle.

public static void handle(int shardId) {
    final var bot = MantaroBot.getInstance();
    final var instant = Instant.now();
    try {
        final var cache = bot.getBirthdayCacher();
        // There's no cache to be seen here
        if (cache == null) {
            return;
        }
        // We haven't finished caching all members, somehow?
        if (!cache.isDone) {
            return;
        }
        final var start = System.currentTimeMillis();
        var membersAssigned = 0;
        var membersDivested = 0;
        final var jda = bot.getShardManager().getShardById(shardId);
        if (jda == null) {
            // To be fair, this shouldn't be possible as it only starts it with the shards it knows...
            return;
        }
        log.info("Checking birthdays in shard {} to assign roles...", jda.getShardInfo().getShardId());
        // Well, fuck, this was a day off. NYC time was 23:00 when Chicago time was at 00:00, so it checked the
        // birthdays for THE WRONG DAY. Heck.
        // 17-02-2022: Fuck again, I was using the wrong thing. Now it works, lol.
        final var timezone = ZonedDateTime.ofInstant(instant, ZoneId.of("America/Chicago"));
        // Example: 25-02
        final var now = timezone.format(dayMonthFormat);
        // Example: 02
        final var month = timezone.format(monthFormat);
        // Example: 01
        final var lastMonthTz = ZonedDateTime.ofInstant(instant, ZoneId.of("America/Chicago")).minusMonths(1);
        final var lastMonth = lastMonthTz.format(dateFormat);
        final var cached = cache.getCachedBirthdays();
        final var guilds = jda.getGuildCache();
        // Backoff sending: we need to backoff the birthday requests,
        // else we're gonna find ourselves quite often hitting ratelimits, which might slow the whole
        // bot down. Therefore, we're just gonna get all of the messages we need to send and *slowly*
        // send them over the course of a few minutes, instead of trying to send them all at once.
        Map<BirthdayGuildInfo, Queue<Message>> toSend = new HashMap<>();
        List<BirthdayRoleInfo> roleBackoffAdd = new ArrayList<>();
        List<BirthdayRoleInfo> roleBackoffRemove = new ArrayList<>();
        // For all current -cached- guilds.
        for (final var guild : guilds) {
            // This is quite a db spam, lol
            final var dbGuild = MantaroData.db().getGuild(guild);
            final var guildData = dbGuild.getData();
            // If we have a birthday guild and channel here, continue
            if (guildData.getBirthdayChannel() != null && guildData.getBirthdayRole() != null) {
                final var birthdayRole = guild.getRoleById(guildData.getBirthdayRole());
                final var channel = guild.getTextChannelById(guildData.getBirthdayChannel());
                if (channel != null && birthdayRole != null) {
                    if (!guild.getSelfMember().canInteract(birthdayRole))
                        // Go to next guild...
                        continue;
                    if (!channel.canTalk())
                        // cannot talk here...
                        continue;
                    if (guildData.getGuildAutoRole() != null && birthdayRole.getId().equals(guildData.getGuildAutoRole()))
                        // Birthday role is autorole role
                        continue;
                    if (birthdayRole.isPublicRole())
                        // Birthday role is public role
                        continue;
                    if (birthdayRole.isManaged())
                        // This was meant to be a bot role?
                        continue;
                    // Guild map is now created from allowed birthdays. This is a little hacky, but we don't really care.
                    // The other solution would have been just disabling this completely, which would have been worse.
                    // @formatter:off
                    Map<Long, BirthdayCacher.BirthdayData> guildMap = cached.entrySet().stream().filter(map -> guildData.getAllowedBirthdays().contains(String.valueOf(map.getKey()))).filter(map -> map.getValue().getBirthday().substring(3, 5).equals(month) || map.getValue().getBirthday().substring(3, 5).equals(lastMonth)).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
                    // @formatter:on
                    var birthdayAnnouncerText = new MessageBuilder();
                    birthdayAnnouncerText.append("**New birthdays for today, wish them Happy Birthday!**").append("\n\n");
                    int birthdayNumber = 0;
                    List<Long> nullMembers = new ArrayList<>();
                    for (var data : guildMap.entrySet()) {
                        var birthday = data.getValue().getBirthday();
                        if (guildData.getBirthdayBlockedIds().contains(String.valueOf(data.getKey()))) {
                            continue;
                        }
                        if (birthday == null) {
                            log.debug("Birthday is null? Continuing...");
                            nullMembers.add(data.getKey());
                            continue;
                        }
                        // This needs to be a retrieveMemberById call, sadly. This will get cached, though.
                        Member member;
                        try {
                            // This is expensive!
                            member = guild.retrieveMemberById(data.getKey(), false).complete();
                        } catch (Exception ex) {
                            nullMembers.add(data.getKey());
                            continue;
                        }
                        // Make sure we announce on March 1st for birthdays on February 29 if the current
                        // year is not a leap year.
                        var compare = birthday.substring(0, 5);
                        if (compare.equals("29-02") && !Year.isLeap(LocalDate.now().getYear())) {
                            compare = "28-02";
                        }
                        if (compare.equals(now)) {
                            log.debug("Assigning birthday role on guild {} (M: {})", guild.getId(), member.getEffectiveName());
                            var tempBirthdayMessage = String.format(EmoteReference.POPPER + "**%s is a year older now! Wish them a happy birthday.** :tada:", member.getEffectiveName());
                            if (guildData.getBirthdayMessage() != null) {
                                tempBirthdayMessage = guildData.getBirthdayMessage().replace("$(user)", member.getEffectiveName()).replace("$(usermention)", member.getAsMention()).replace("$(tag)", member.getUser().getAsTag());
                            }
                            // Variable used in lambda expression should be final or effectively final...
                            final var birthdayMessage = tempBirthdayMessage;
                            if (!member.getRoles().contains(birthdayRole)) {
                                log.debug("Backing off adding birthday role on guild {} (M: {})", guild.getId(), member.getEffectiveName());
                                // We can pretty much do all of this only based on the IDs
                                roleBackoffAdd.add(new BirthdayRoleInfo(guild.getId(), member.getId(), birthdayRole));
                                birthdayAnnouncerText.append(birthdayMessage).append("\n");
                                membersAssigned++;
                                birthdayNumber++;
                                Metrics.BIRTHDAY_COUNTER.inc();
                            }
                        } else {
                            // day passed
                            if (member.getRoles().contains(birthdayRole)) {
                                log.debug("Backing off removing birthday role on guild {} (M: {})", guild.getId(), member.getEffectiveName());
                                roleBackoffRemove.add(new BirthdayRoleInfo(guild.getId(), member.getId(), birthdayRole));
                                membersDivested++;
                            }
                        }
                    }
                    if (birthdayNumber != 0) {
                        toSend.put(new BirthdayGuildInfo(guild.getId(), channel.getId()), birthdayAnnouncerText.buildAll(MessageBuilder.SplitPolicy.NEWLINE));
                    }
                    // If any of the member lookups to discord returned null, remove them.
                    if (!nullMembers.isEmpty()) {
                        guildData.getAllowedBirthdays().removeAll(nullMembers.stream().map(String::valueOf).collect(Collectors.toList()));
                        dbGuild.save();
                    }
                }
            }
        }
        final var end = System.currentTimeMillis();
        log.info("{} (birthdays): people assigned: {}, people divested: {}, took {}ms", jda.getShardInfo(), membersAssigned, membersDivested, (end - start));
        // A poll inside a pool?
        // Send the backoff sending comment above, this basically avoids hitting
        // discord with one billion requests at once.
        final var backoff = 400;
        final var roleBackoff = 100;
        backOffPool.submit(() -> {
            log.info("{} (birthdays): Backoff messages: {}. Sending them with {}ms backoff.", jda.getShardInfo(), toSend.size(), backoff);
            final var startMessage = System.currentTimeMillis();
            for (var entry : toSend.entrySet()) {
                try {
                    final var info = entry.getKey();
                    final var guildId = info.guildId;
                    final var channelId = info.channelId;
                    final var messages = entry.getValue();
                    final var guild = bot.getShardManager().getGuildById(guildId);
                    if (guild == null)
                        continue;
                    final var channel = guild.getTextChannelById(channelId);
                    if (channel == null)
                        continue;
                    messages.forEach(message -> channel.sendMessage(message).allowedMentions(EnumSet.of(Message.MentionType.USER, Message.MentionType.CHANNEL, Message.MentionType.ROLE, Message.MentionType.EMOTE)).queue());
                    // If 100 guilds (about 1/10th of all the shard guilds! so very unlikely) do
                    // get a birthday now, the maximum delay will be 40,000ms, which is 40 seconds.
                    // Not much of an issue for the end user, but avoid sending too many requests
                    // to discord at once. If half of all the guilds in the shard do, the delay
                    // will be about 200,000ms, so 2 minutes.
                    Thread.sleep(backoff);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            final var endMessage = System.currentTimeMillis();
            toSend.clear();
            log.info("Sent all birthday backoff messages, backoff was {}ms, took {}ms", backoff, endMessage - startMessage);
        });
        backOffRolePool.submit(() -> {
            log.info("{} (birthdays): Backoff roles (add): {}. Sending them with {}ms backoff.", jda.getShardInfo(), roleBackoffAdd.size(), roleBackoff);
            final var startRole = System.currentTimeMillis();
            for (var roleInfo : roleBackoffAdd) {
                try {
                    var guild = bot.getShardManager().getGuildById(roleInfo.guildId);
                    if (guild == null)
                        continue;
                    guild.addRoleToMember(roleInfo.memberId, roleInfo.role).reason(modLogMessage).queue();
                    Thread.sleep(roleBackoff);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            log.info("{} (birthdays): Backoff roles (remove): {}. Sending them with {}ms backoff.", jda.getShardInfo(), roleBackoffRemove.size(), roleBackoff);
            for (var roleInfo : roleBackoffRemove) {
                try {
                    var guild = bot.getShardManager().getGuildById(roleInfo.guildId);
                    if (guild == null)
                        continue;
                    guild.removeRoleFromMember(roleInfo.memberId, roleInfo.role).reason(modLogMessage).queue();
                    Thread.sleep(roleBackoff);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            final var endRole = System.currentTimeMillis();
            roleBackoffAdd.clear();
            roleBackoffRemove.clear();
            log.info("{} (birthdays): All roles done (add and removal), backoff was {}ms. Took {}ms", jda.getShardInfo(), roleBackoff, endRole - startRole);
        });
    } catch (Exception e) {
        e.printStackTrace();
    }
}
Also used : ThreadFactoryBuilder(com.google.common.util.concurrent.ThreadFactoryBuilder) Message(net.dv8tion.jda.api.entities.Message) java.util(java.util) Logger(org.slf4j.Logger) LoggerFactory(org.slf4j.LoggerFactory) Member(net.dv8tion.jda.api.entities.Member) Metrics(net.kodehawa.mantarobot.utils.exporters.Metrics) Collectors(java.util.stream.Collectors) Executors(java.util.concurrent.Executors) MantaroBot(net.kodehawa.mantarobot.MantaroBot) java.time(java.time) MessageBuilder(net.dv8tion.jda.api.MessageBuilder) Role(net.dv8tion.jda.api.entities.Role) EmoteReference(net.kodehawa.mantarobot.utils.commands.EmoteReference) DateTimeFormatter(java.time.format.DateTimeFormatter) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) MantaroData(net.kodehawa.mantarobot.data.MantaroData) MessageBuilder(net.dv8tion.jda.api.MessageBuilder) Member(net.dv8tion.jda.api.entities.Member)

Example 2 with Member

use of net.dv8tion.jda.api.entities.Member in project MantaroBot by Mantaro.

the class ImageActionCmd method call.

@Override
protected void call(Context ctx, String content) {
    if (!RatelimitUtils.ratelimit(rateLimiter, ctx, null)) {
        return;
    }
    var languageContext = ctx.getGuildLanguageContext();
    var random = "";
    try {
        if (type != null) {
            var result = weebapi.getRandomImageByType(type, false, "gif");
            var image = result.getKey();
            if (image == null) {
                ctx.sendLocalized("commands.action.error_retrieving", EmoteReference.SAD);
                return;
            }
            images = Collections.singletonList(image);
            // Guaranteed random selection :^).
            random = images.get(0);
        } else {
            if (images.isEmpty()) {
                ctx.sendLocalized("commands.action.no_type", EmoteReference.ERROR);
                return;
            }
            random = images.get(rand.nextInt(images.size()));
        }
    } catch (Exception e) {
        ctx.sendLocalized("commands.action.error_retrieving", EmoteReference.ERROR);
        return;
    }
    try {
        var mentionedMembers = ctx.getMentionedMembers();
        if (mentionedMembers.isEmpty()) {
            ctx.sendLocalized("commands.action.no_mention", EmoteReference.ERROR);
            return;
        }
        boolean filtered = false;
        if (mentionedMembers.size() == 1) {
            final var dbUser = ctx.getDBUser(mentionedMembers.get(0).getId());
            if (dbUser.getData().isActionsDisabled()) {
                ctx.sendLocalized("commands.action.actions_disabled", EmoteReference.ERROR);
                return;
            }
        } else {
            var filter = mentionedMembers.stream().filter(member -> ctx.getDBUser(member).getData().isActionsDisabled()).collect(Collectors.toList());
            // Needs to be mutable.
            mentionedMembers = new ArrayList<>(mentionedMembers);
            if (mentionedMembers.removeAll(filter)) {
                filtered = true;
            }
            if (mentionedMembers.isEmpty()) {
                ctx.sendLocalized("commands.action.no_mention_disabled", EmoteReference.ERROR);
                return;
            }
        }
        var toSend = new MessageBuilder().append(emoji).append(String.format(languageContext.get(format), "**%s**".formatted(noMentions(mentionedMembers)), "**%s**".formatted(ctx.getMember().getEffectiveName())));
        if (swapNames) {
            toSend = new MessageBuilder().append(emoji).append(String.format(languageContext.get(format), "**%s**".formatted(ctx.getMember().getEffectiveName()), "**%s**".formatted(noMentions(mentionedMembers))));
        }
        if (isLonely(ctx)) {
            toSend = new MessageBuilder().append("**").append(languageContext.get(lonelyLine)).append("**");
        }
        if (isMentioningBot(ctx)) {
            toSend = new MessageBuilder().append("**").append(languageContext.get(botLine)).append("**");
        }
        if (filtered) {
            toSend.append("\n").append(String.format(languageContext.get("commands.action.filtered"), EmoteReference.WARNING));
        }
        var member = ctx.getMember();
        toSend.setEmbeds(new EmbedBuilder().setColor(ctx.getMemberColor()).setImage(random).build());
        ctx.getChannel().sendMessage(toSend.build()).queue();
    } catch (Exception e) {
        e.printStackTrace();
        ctx.sendLocalized("commands.action.permission_or_unexpected_error", EmoteReference.ERROR);
    }
}
Also used : Color(java.awt.Color) java.util(java.util) HelpContent(net.kodehawa.mantarobot.core.modules.commands.help.HelpContent) NoArgsCommand(net.kodehawa.mantarobot.core.modules.commands.NoArgsCommand) IncreasingRateLimiter(net.kodehawa.mantarobot.utils.commands.ratelimit.IncreasingRateLimiter) EmbedBuilder(net.dv8tion.jda.api.EmbedBuilder) Member(net.dv8tion.jda.api.entities.Member) Collectors(java.util.stream.Collectors) TimeUnit(java.util.concurrent.TimeUnit) RatelimitUtils(net.kodehawa.mantarobot.utils.commands.ratelimit.RatelimitUtils) CommandCategory(net.kodehawa.mantarobot.core.modules.commands.base.CommandCategory) MessageBuilder(net.dv8tion.jda.api.MessageBuilder) EmoteReference(net.kodehawa.mantarobot.utils.commands.EmoteReference) MantaroData(net.kodehawa.mantarobot.data.MantaroData) Context(net.kodehawa.mantarobot.core.modules.commands.base.Context) EmbedBuilder(net.dv8tion.jda.api.EmbedBuilder) MessageBuilder(net.dv8tion.jda.api.MessageBuilder)

Example 3 with Member

use of net.dv8tion.jda.api.entities.Member in project MantaroBot by Mantaro.

the class BirthdayCmd method sendBirthdayList.

private void sendBirthdayList(Context ctx, List<Member> members, Map<Long, BirthdayCacher.BirthdayData> guildCurrentBirthdays, Calendar calendar, boolean month) {
    StringBuilder builder = new StringBuilder();
    var languageContext = ctx.getLanguageContext();
    var guild = ctx.getGuild();
    var memberSort = members.stream().sorted(Comparator.comparingInt(i -> {
        var bd = guildCurrentBirthdays.get(i.getIdLong());
        // And this is so we get a stable sort of day/month
        return (int) (bd.day + (bd.month << 5));
    })).collect(Collectors.toList());
    for (Member member : memberSort) {
        var birthday = guildCurrentBirthdays.get(member.getIdLong());
        builder.append("+ %-20s : %s ".formatted(StringUtils.limit(member.getEffectiveName(), 20), birthday.getDay() + "-" + birthday.getMonth()));
        builder.append("\n");
    }
    var parts = DiscordUtils.divideString(1000, '\n', builder);
    List<String> messages = new LinkedList<>();
    for (String part : parts) {
        var help = languageContext.get("general.button_react");
        if (month && calendar != null) {
            messages.add(languageContext.get("commands.birthday.header").formatted(ctx.getGuild().getName(), Utils.capitalize(calendar.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.ENGLISH))) + "```diff\n%s```".formatted(part));
        } else {
            messages.add(languageContext.get("commands.birthday.full_header").formatted(guild.getName(), (parts.size() > 1 ? help : "") + "```diff\n%s```".formatted(part)));
        }
    }
    if (parts.isEmpty()) {
        ctx.sendLocalized("commands.birthday.no_guild_birthdays", EmoteReference.ERROR);
        return;
    }
    DiscordUtils.listButtons(ctx, 45, messages);
}
Also used : Member(net.dv8tion.jda.api.entities.Member)

Example 4 with Member

use of net.dv8tion.jda.api.entities.Member in project c0debaseBot by Biospheere.

the class SinceCommand method getMemberCoundByDays.

private Integer getMemberCoundByDays(final Integer since, final Guild guild) {
    final AtomicInteger atomicInteger = new AtomicInteger(0);
    guild.getMembers().forEach(member -> {
        if (member.hasTimeJoined() && ChronoUnit.DAYS.between(member.getTimeJoined(), LocalDateTime.now().atOffset(ZoneOffset.UTC)) >= since) {
            atomicInteger.incrementAndGet();
        } else {
            final Member requestedMember = guild.retrieveMemberById(member.getId(), true).complete();
            if (ChronoUnit.DAYS.between(requestedMember.getTimeJoined(), LocalDateTime.now().atOffset(ZoneOffset.UTC)) >= since) {
                atomicInteger.incrementAndGet();
            }
        }
    });
    return atomicInteger.get();
}
Also used : AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Member(net.dv8tion.jda.api.entities.Member)

Example 5 with Member

use of net.dv8tion.jda.api.entities.Member in project c0debaseBot by Biospheere.

the class SinceCommand method execute.

@Override
public void execute(final String[] args, final Message message) {
    final Member member = message.getMentionedMembers().size() == 0 ? message.getMember() : message.getMentionedMembers().get(0);
    final EmbedBuilder embedBuilder = getEmbed(message.getGuild(), message.getAuthor());
    if (args.length > 0 && message.getMentionedMembers().isEmpty() && StringUtils.isInteger(args[0]) && message.getGuild().isLoaded()) {
        final Integer since = Integer.valueOf(args[0]);
        if (since < 0) {
            final Member oldestMember = DiscordUtils.getOldestMember(message.getGuild());
            embedBuilder.setDescription("!since [0-" + ((oldestMember == null) ? "?]" : ChronoUnit.DAYS.between(oldestMember.getTimeJoined(), LocalDateTime.now().atOffset(ZoneOffset.UTC)) + "]"));
        } else {
            final Integer memberCount = getMemberCoundByDays(since, message.getGuild());
            final float percentage = ((float) memberCount / (float) message.getGuild().getMemberCount()) * 100;
            embedBuilder.setDescription("Es gibt " + memberCount + " Mitglieder welche seit mehr als " + since + " Tagen auf diesem Server sind.\n Das sind " + DECIMAL_FORMAT.format(percentage) + "%");
        }
    } else {
        embedBuilder.setDescription(member.getAsMention() + " ist seit " + ChronoUnit.DAYS.between(member.getTimeJoined(), LocalDateTime.now().atOffset(ZoneOffset.UTC)) + " Tagen auf " + message.getGuild().getName());
    }
    message.getTextChannel().sendMessage(embedBuilder.build()).queue();
}
Also used : AtomicInteger(java.util.concurrent.atomic.AtomicInteger) EmbedBuilder(net.dv8tion.jda.api.EmbedBuilder) Member(net.dv8tion.jda.api.entities.Member)

Aggregations

Member (net.dv8tion.jda.api.entities.Member)18 EmbedBuilder (net.dv8tion.jda.api.EmbedBuilder)7 Map (java.util.Map)3 Collectors (java.util.stream.Collectors)3 MessageBuilder (net.dv8tion.jda.api.MessageBuilder)3 User (de.c0debase.bot.database.model.User)2 java.util (java.util)2 TimeUnit (java.util.concurrent.TimeUnit)2 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)2 Role (net.dv8tion.jda.api.entities.Role)2 MantaroBot (net.kodehawa.mantarobot.MantaroBot)2 CommandCategory (net.kodehawa.mantarobot.core.modules.commands.base.CommandCategory)2 Context (net.kodehawa.mantarobot.core.modules.commands.base.Context)2 HelpContent (net.kodehawa.mantarobot.core.modules.commands.help.HelpContent)2 MantaroData (net.kodehawa.mantarobot.data.MantaroData)2 EmoteReference (net.kodehawa.mantarobot.utils.commands.EmoteReference)2 Subscribe (com.google.common.eventbus.Subscribe)1 ThreadFactoryBuilder (com.google.common.util.concurrent.ThreadFactoryBuilder)1 java.awt (java.awt)1 Color (java.awt.Color)1