Skip to content

Commit 269f7c3

Browse files
authored
Merge pull request CloudBotIRC#158 from linuxdaemon/gonzobot+hook-stats
Add hook stats plugin
2 parents 27615fe + 943bea1 commit 269f7c3

1 file changed

Lines changed: 119 additions & 0 deletions

File tree

plugins/hook_stats.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
"""
2+
Tracks successful and errored launches of all hooks, allowing users to query the stats
3+
4+
Author:
5+
- linuxdaemon <https://github.com/linuxdaemon>
6+
"""
7+
8+
from collections import defaultdict
9+
10+
from cloudbot import hook
11+
from cloudbot.hook import Priority
12+
from cloudbot.util import web
13+
from cloudbot.util.formatting import gen_markdown_table
14+
15+
16+
def default_hook_counter():
17+
return {'success': 0, 'failure': 0}
18+
19+
20+
def hook_sorter(n):
21+
def _sorter(data):
22+
return sum(data[n].values())
23+
24+
return _sorter
25+
26+
27+
def get_stats(bot):
28+
try:
29+
stats = bot.memory["hook_stats"]
30+
except LookupError:
31+
bot.memory["hook_stats"] = stats = {
32+
'global': defaultdict(default_hook_counter),
33+
'network': defaultdict(lambda: defaultdict(default_hook_counter)),
34+
'channel': defaultdict(lambda: defaultdict(lambda: defaultdict(default_hook_counter))),
35+
}
36+
37+
return stats
38+
39+
40+
@hook.post_hook(priority=Priority.HIGHEST)
41+
def stats_sieve(launched_event, error, bot, launched_hook):
42+
chan = launched_event.chan
43+
conn = launched_event.conn
44+
status = 'success' if error is None else 'failure'
45+
stats = get_stats(bot)
46+
name = launched_hook.plugin.title + '.' + launched_hook.function_name
47+
stats['global'][name][status] += 1
48+
if conn:
49+
stats['network'][conn.name.casefold()][name][status] += 1
50+
51+
if chan:
52+
stats['channel'][conn.name.casefold()][chan.casefold()][name][status] += 1
53+
54+
55+
def do_basic_stats(data):
56+
table = [
57+
(hook_name, str(count['success']), str(count['failure']))
58+
for hook_name, count in sorted(data.items(), key=hook_sorter(1), reverse=True)
59+
]
60+
return ("Hook", "Uses - Success", "Uses - Errored"), table
61+
62+
63+
def do_global_stats(data):
64+
return do_basic_stats(data['global'])
65+
66+
67+
def do_network_stats(data, network):
68+
return do_basic_stats(data['network'][network.casefold()])
69+
70+
71+
def do_channel_stats(data, network, channel):
72+
return do_basic_stats(data['channel'][network.casefold()][channel.casefold()])
73+
74+
75+
def do_hook_stats(data, hook_name):
76+
table = [
77+
(net, chan, hooks[hook_name]) for net, chans in data['network'].items() for chan, hooks in chans.items()
78+
]
79+
return ("Network", "Channel", "Uses - Success", "Uses - Errored"), \
80+
[
81+
(net, chan, str(count['success']), str(count['failure']))
82+
for net, chan, count in sorted(table, key=hook_sorter(2), reverse=True)
83+
]
84+
85+
86+
stats_funcs = {
87+
'global': (do_global_stats, 0),
88+
'network': (do_network_stats, 1),
89+
'channel': (do_channel_stats, 2),
90+
'hook': (do_hook_stats, 1),
91+
}
92+
93+
94+
@hook.command(permissions=["snoonetstaff", "botcontrol"])
95+
def hookstats(text, bot, notice_doc):
96+
"""{global|network <name>|channel <network> <channel>|hook <hook>} - Get hook usage statistics"""
97+
args = text.split()
98+
stats_type = args.pop(0).lower()
99+
100+
data = get_stats(bot)
101+
102+
try:
103+
handler, arg_count = stats_funcs[stats_type]
104+
except LookupError:
105+
notice_doc()
106+
return
107+
108+
if len(args) < arg_count:
109+
notice_doc()
110+
return
111+
112+
headers, data = handler(data, *args[:arg_count])
113+
114+
if not data:
115+
return "No stats available."
116+
117+
table = gen_markdown_table(headers, data)
118+
119+
return web.paste(table, 'md', 'hastebin')

0 commit comments

Comments
 (0)