1import gc
2import os
3import signal
4from datetime import datetime
5
6from errbot import BotPlugin, arg_botcmd, botcmd
7from errbot.utils import format_timedelta, global_restart
8
9
10class Health(BotPlugin):
11    @botcmd(template="status")
12    def status(self, msg, args):
13        """If I am alive I should be able to respond to this one"""
14        plugins_statuses = self.status_plugins(msg, args)
15        loads = self.status_load(msg, args)
16        gc = self.status_gc(msg, args)
17
18        return {
19            "plugins_statuses": plugins_statuses["plugins_statuses"],
20            "loads": loads["loads"],
21            "gc": gc["gc"],
22        }
23
24    @botcmd(template="status_load")
25    def status_load(self, _, args):
26        """shows the load status"""
27        try:
28            from posix import getloadavg
29
30            loads = getloadavg()
31        except Exception:
32            loads = None
33
34        return {"loads": loads}
35
36    @botcmd(template="status_gc")
37    def status_gc(self, _, args):
38        """shows the garbage collection details"""
39        return {"gc": gc.get_count()}
40
41    @botcmd(template="status_plugins")
42    def status_plugins(self, _, args):
43        """shows the plugin status"""
44        pm = self._bot.plugin_manager
45        all_blacklisted = pm.get_blacklisted_plugin()
46        all_loaded = pm.get_all_active_plugin_names()
47        all_attempted = sorted(pm.plugin_infos.keys())
48        plugins_statuses = []
49        for name in all_attempted:
50            if name in all_blacklisted:
51                if name in all_loaded:
52                    plugins_statuses.append(("BA", name))
53                else:
54                    plugins_statuses.append(("BD", name))
55            elif name in all_loaded:
56                plugins_statuses.append(("A", name))
57            elif (
58                pm.get_plugin_obj_by_name(name) is not None
59                and pm.get_plugin_obj_by_name(name).get_configuration_template()
60                is not None
61                and pm.get_plugin_configuration(name) is None
62            ):
63                plugins_statuses.append(("C", name))
64            else:
65                plugins_statuses.append(("D", name))
66
67        return {"plugins_statuses": plugins_statuses}
68
69    @botcmd
70    def uptime(self, _, args):
71        """Return the uptime of the bot"""
72        u = format_timedelta(datetime.now() - self._bot.startup_time)
73        since = self._bot.startup_time.strftime("%A, %b %d at %H:%M")
74        return f"I've been up for {u} (since {since})."
75
76    # noinspection PyUnusedLocal
77    @botcmd(admin_only=True)
78    def restart(self, msg, args):
79        """Restart the bot."""
80        self.send(msg.frm, "Deactivating all the plugins...")
81        self._bot.plugin_manager.deactivate_all_plugins()
82        self.send(msg.frm, "Restarting")
83        self._bot.shutdown()
84        global_restart()
85        return "I'm restarting..."
86
87    # noinspection PyUnusedLocal
88    @arg_botcmd(
89        "--confirm",
90        dest="confirmed",
91        action="store_true",
92        help="confirm you want to shut down",
93        admin_only=True,
94    )
95    @arg_botcmd(
96        "--kill",
97        dest="kill",
98        action="store_true",
99        help="kill the bot instantly, don't shut down gracefully",
100        admin_only=True,
101    )
102    def shutdown(self, msg, confirmed, kill):
103        """
104        Shutdown the bot.
105
106        Useful when the things are going crazy and you don't have access to the machine.
107        """
108        if not confirmed:
109            yield "Please provide `--confirm` to confirm you really want me to shut down."
110            return
111
112        if kill:
113            yield "Killing myself right now!"
114            os.kill(os.getpid(), signal.SIGKILL)
115        else:
116            yield "Roger that. I am shutting down."
117            os.kill(os.getpid(), signal.SIGINT)
118