Search in sources :

Example 1 with ShardMonitorEvent

use of net.kodehawa.mantarobot.core.listeners.events.ShardMonitorEvent in project MantaroBot by Mantaro.

the class CommandListener method onEvent.

@Override
public void onEvent(Event event) {
    if (event instanceof ShardMonitorEvent) {
        if (MantaroBot.getInstance().getShardedMantaro().getShards()[shardId].getEventManager().getLastJDAEventTimeDiff() > 30000)
            return;
        // Hey, this listener is alive! (This won't pass if somehow this is blocked)
        ((ShardMonitorEvent) event).alive(shardId, ShardMonitorEvent.COMMAND_LISTENER);
        return;
    }
    if (event instanceof GuildMessageReceivedEvent) {
        GuildMessageReceivedEvent msg = (GuildMessageReceivedEvent) event;
        // Inserts a cached message into the cache. This only holds the id and the content, and is way lighter than saving the entire jda object.
        messageCache.put(msg.getMessage().getId(), Optional.of(new CachedMessage(msg.getAuthor().getIdLong(), msg.getMessage().getContentDisplay())));
        // Ignore myself and bots.
        if (msg.getAuthor().isBot() || msg.getAuthor().equals(msg.getJDA().getSelfUser()))
            return;
        shard.getCommandPool().execute(() -> onCommand(msg));
    }
}
Also used : ShardMonitorEvent(net.kodehawa.mantarobot.core.listeners.events.ShardMonitorEvent) GuildMessageReceivedEvent(net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent) CachedMessage(net.kodehawa.mantarobot.core.listeners.entities.CachedMessage)

Example 2 with ShardMonitorEvent

use of net.kodehawa.mantarobot.core.listeners.events.ShardMonitorEvent in project MantaroBot by Mantaro.

the class ShardWatcher method run.

@Override
public void run() {
    LogUtils.shard("ShardWatcherThread started");
    // Executes the restart queue handler. For the actual logic behind all this, check the next while(true) loop.
    THREAD_POOL.execute(() -> {
        while (true) {
            MantaroShard shard = RESTART_QUEUE.poll();
            if (shard == null) {
                // poll the queue every 10 seconds
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    LogUtils.shard("Shard restarter task interrupted");
                    return;
                }
                // Continue to the next loop cycle if no shard is on the restart queue.
                continue;
            }
            // Alert us, plz no panic
            LogUtils.shard(String.format("(Resume request failed or errored) " + "Dead shard? Starting automatic shard restart on shard #%d due to it being inactive for longer than 30 seconds.", shard.getId()));
            try {
                // Reboot the shard.
                shard.start(true);
            } catch (Exception e) {
                // If the shard wasn't able to restart by itself, alert us so we can reboot manually later.
                LogUtils.shard(String.format("Shard %d was unable to be restarted: %s", shard.getId(), e));
            }
            try {
                // Wait 5 seconds as a backoff.
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                LogUtils.shard("Shard restarter task interrupted");
                return;
            }
        }
    });
    final int wait = MantaroData.config().get().shardWatcherWait;
    while (true) {
        try {
            // Run every x ms (usually every 10 minutes unless changed)
            Thread.sleep(wait);
            MantaroEventManager.getLog().info("Checking shards...");
            // Just in case...
            if (shardedMantaro == null)
                shardedMantaro = MantaroBot.getInstance().getShardedMantaro();
            // Get and propagate the shard event.
            // This event will propagate over all Mantaro-specific listeners, and see if the shards are responding accordingly.
            ShardMonitorEvent sme = new ShardMonitorEvent(shardedMantaro.getTotalShards());
            EventUtils.propagateEvent(sme);
            // Start the procedure...
            int[] dead = sme.getDeadShards();
            // Oh well... we can try to recover them now!
            if (dead.length != 0) {
                MantaroEventManager.getLog().error("Dead shards found: {}", Arrays.toString(dead));
                // restart on..." kinda message.
                if (dead.length > 15) {
                    LogUtils.shard("Seems like Megumin struck our castle and we got a horribly high amount of dead shards (" + dead.length + ")\n" + "This could be just due to them reconnecting though, if nothing appears down there talking about how the shards are rebooting " + "you might aswell ignore this warning.");
                }
                // Under the hood this basically calls for a RESUME JDA instance and if it fails, it adds it to the restart queue to replace it with a completely new one.
                for (int id : dead) {
                    try {
                        MantaroShard shard = MantaroBot.getInstance().getShard(id);
                        // But, if the shard has been inactive for too long, we're better off scrapping this session as the shard might be stuck on connecting.
                        if ((shard.getStatus() == JDA.Status.RECONNECT_QUEUED || shard.getStatus() == JDA.Status.ATTEMPTING_TO_RECONNECT || shard.getStatus() == JDA.Status.SHUTDOWN) && shard.getEventManager().getLastJDAEventTimeDiff() < 200000) {
                            LogUtils.shard(String.format("Skipping shard %d due to it being currently reconnecting to the websocket or was shutdown manually...", id));
                            continue;
                        }
                        LogUtils.shard(String.format("Found dead shard (#%d)... attempting RESUME request and waiting 20 seconds to validate.", id));
                        // Send the RESUME request.
                        ((JDAImpl) (shard.getJDA())).getClient().close(4000);
                        RESUME_WAITER.schedule(() -> {
                            if (shard.getEventManager().getLastJDAEventTimeDiff() > 18000) {
                                RESTART_QUEUE.add(shard);
                            }
                        }, 20, TimeUnit.SECONDS);
                    } catch (Exception e) {
                        // Print the exception so we can look at it later...
                        e.printStackTrace();
                        // Force add into the queue
                        RESUME_WAITER.schedule(() -> RESTART_QUEUE.add(MantaroBot.getInstance().getShard(id)), 30, TimeUnit.SECONDS);
                        // Somehow we couldn't reboot the shard.
                        LogUtils.shard(String.format("Cannot restart shard %d. Try to do it manually.", id));
                    }
                }
            } else {
                // yay
                MantaroEventManager.getLog().info("No dead shards found");
                long ping = MantaroBot.getInstance().getPing();
                // We might have a few soft-dead shards on here... (or internet went to shit)
                if (ping > 400) {
                    LogUtils.shard(String.format("No dead shards found, but average ping is high (%dms). Ping breakdown: %s", ping, Arrays.toString(MantaroBot.getInstance().getPings())));
                }
            }
        } catch (InterruptedException e) {
            // Just in case we stop this for any reason, we want to know that we interrupted this, just so we know we won't have a shard watcher running on the background.
            log.error("ShardWatcher interrupted, stopping...");
            LogUtils.shard("ShardWatcher interrupted, stopping...");
            return;
        }
    }
}
Also used : ShardMonitorEvent(net.kodehawa.mantarobot.core.listeners.events.ShardMonitorEvent) MantaroShard(net.kodehawa.mantarobot.core.shard.MantaroShard)

Example 3 with ShardMonitorEvent

use of net.kodehawa.mantarobot.core.listeners.events.ShardMonitorEvent in project MantaroBot by Mantaro.

the class MantaroListener method onEvent.

@Override
public void onEvent(Event event) {
    if (event instanceof ShardMonitorEvent) {
        if (MantaroBot.getInstance().getShardedMantaro().getShards()[shardId].getEventManager().getLastJDAEventTimeDiff() > 30000)
            return;
        ((ShardMonitorEvent) event).alive(shardId, ShardMonitorEvent.MANTARO_LISTENER);
        return;
    }
    if (event instanceof GuildMessageReceivedEvent) {
        MantaroBot.getInstance().getStatsClient().increment("messages_received");
        GuildMessageReceivedEvent e = (GuildMessageReceivedEvent) event;
        onMessage(e);
        return;
    }
    if (event instanceof GuildMemberJoinEvent) {
        shard.getThreadPool().execute(() -> onUserJoin((GuildMemberJoinEvent) event));
        return;
    }
    if (event instanceof GuildMemberLeaveEvent) {
        shard.getThreadPool().execute(() -> onUserLeave((GuildMemberLeaveEvent) event));
        return;
    }
    // Doesn't run on the thread pool as there's no need for it.
    if (event instanceof GuildMemberRoleAddEvent) {
        // It only runs on the thread pool if needed.
        handleNewPatron((GuildMemberRoleAddEvent) event);
    }
    if (event instanceof GuildMessageUpdateEvent) {
        logEdit((GuildMessageUpdateEvent) event);
        return;
    }
    if (event instanceof GuildMessageDeleteEvent) {
        logDelete((GuildMessageDeleteEvent) event);
        return;
    }
    if (event instanceof GuildUnbanEvent) {
        logUnban((GuildUnbanEvent) event);
        return;
    }
    if (event instanceof GuildBanEvent) {
        logBan((GuildBanEvent) event);
        return;
    }
    // Internal events
    if (event instanceof GuildJoinEvent) {
        GuildJoinEvent e = (GuildJoinEvent) event;
        if (e.getGuild().getSelfMember().getJoinDate().isBefore(OffsetDateTime.now().minusSeconds(30)))
            return;
        onJoin(e);
        if (MantaroCore.hasLoadedCompletely()) {
            MantaroBot.getInstance().getStatsClient().gauge("guilds", MantaroBot.getInstance().getGuildCache().size());
            MantaroBot.getInstance().getStatsClient().gauge("users", MantaroBot.getInstance().getUserCache().size());
        }
        return;
    }
    if (event instanceof GuildLeaveEvent) {
        onLeave((GuildLeaveEvent) event);
        if (MantaroCore.hasLoadedCompletely()) {
            MantaroBot.getInstance().getStatsClient().gauge("guilds", MantaroBot.getInstance().getGuildCache().size());
            MantaroBot.getInstance().getStatsClient().gauge("users", MantaroBot.getInstance().getUserCache().size());
        }
        return;
    }
    // debug
    if (event instanceof StatusChangeEvent) {
        logStatusChange((StatusChangeEvent) event);
        return;
    }
    if (event instanceof DisconnectEvent) {
        onDisconnect((DisconnectEvent) event);
        return;
    }
    if (event instanceof ExceptionEvent) {
        MantaroBot.getInstance().getStatsClient().increment("exceptions");
        onException((ExceptionEvent) event);
        return;
    }
    if (event instanceof HttpRequestEvent) {
        MantaroBot.getInstance().getStatsClient().incrementCounter("http_requests");
        return;
    }
    if (event instanceof ReconnectedEvent) {
        MantaroBot.getInstance().getStatsClient().increment("shard.reconnect");
        MantaroBot.getInstance().getStatsClient().recordEvent(com.timgroup.statsd.Event.builder().withTitle("shard.reconnect").withText("Shard reconnected").withDate(new Date()).build());
        return;
    }
    if (event instanceof ResumedEvent) {
        MantaroBot.getInstance().getStatsClient().increment("shard.resume");
        MantaroBot.getInstance().getStatsClient().recordEvent(com.timgroup.statsd.Event.builder().withTitle("shard.resume").withText("Shard resumed").withDate(new Date()).build());
    }
}
Also used : GuildJoinEvent(net.dv8tion.jda.core.events.guild.GuildJoinEvent) HttpRequestEvent(net.dv8tion.jda.core.events.http.HttpRequestEvent) GuildMemberJoinEvent(net.dv8tion.jda.core.events.guild.member.GuildMemberJoinEvent) GuildMemberRoleAddEvent(net.dv8tion.jda.core.events.guild.member.GuildMemberRoleAddEvent) GuildMemberLeaveEvent(net.dv8tion.jda.core.events.guild.member.GuildMemberLeaveEvent) GuildMessageUpdateEvent(net.dv8tion.jda.core.events.message.guild.GuildMessageUpdateEvent) GuildLeaveEvent(net.dv8tion.jda.core.events.guild.GuildLeaveEvent) Date(java.util.Date) GuildBanEvent(net.dv8tion.jda.core.events.guild.GuildBanEvent) ShardMonitorEvent(net.kodehawa.mantarobot.core.listeners.events.ShardMonitorEvent) GuildMessageDeleteEvent(net.dv8tion.jda.core.events.message.guild.GuildMessageDeleteEvent) GuildUnbanEvent(net.dv8tion.jda.core.events.guild.GuildUnbanEvent) GuildMessageReceivedEvent(net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent)

Aggregations

ShardMonitorEvent (net.kodehawa.mantarobot.core.listeners.events.ShardMonitorEvent)3 GuildMessageReceivedEvent (net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent)2 Date (java.util.Date)1 GuildBanEvent (net.dv8tion.jda.core.events.guild.GuildBanEvent)1 GuildJoinEvent (net.dv8tion.jda.core.events.guild.GuildJoinEvent)1 GuildLeaveEvent (net.dv8tion.jda.core.events.guild.GuildLeaveEvent)1 GuildUnbanEvent (net.dv8tion.jda.core.events.guild.GuildUnbanEvent)1 GuildMemberJoinEvent (net.dv8tion.jda.core.events.guild.member.GuildMemberJoinEvent)1 GuildMemberLeaveEvent (net.dv8tion.jda.core.events.guild.member.GuildMemberLeaveEvent)1 GuildMemberRoleAddEvent (net.dv8tion.jda.core.events.guild.member.GuildMemberRoleAddEvent)1 HttpRequestEvent (net.dv8tion.jda.core.events.http.HttpRequestEvent)1 GuildMessageDeleteEvent (net.dv8tion.jda.core.events.message.guild.GuildMessageDeleteEvent)1 GuildMessageUpdateEvent (net.dv8tion.jda.core.events.message.guild.GuildMessageUpdateEvent)1 CachedMessage (net.kodehawa.mantarobot.core.listeners.entities.CachedMessage)1 MantaroShard (net.kodehawa.mantarobot.core.shard.MantaroShard)1