1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
import string
from datetime import datetime
from discord import Member, Message, Status
from discord.ext.commands import Bot, Cog, Context
from bot.constants import Channels, Guild, Stats as StatConf
CHANNEL_NAME_OVERRIDES = {
Channels.off_topic_0: "off_topic_0",
Channels.off_topic_1: "off_topic_1",
Channels.off_topic_2: "off_topic_2",
Channels.staff_lounge: "staff_lounge"
}
ALLOWED_CHARS = string.ascii_letters + string.digits + "_"
class Stats(Cog):
"""A cog which provides a way to hook onto Discord events and forward to stats."""
def __init__(self, bot: Bot):
self.bot = bot
self.last_presence_update = None
@Cog.listener()
async def on_message(self, message: Message) -> None:
"""Report message events in the server to statsd."""
if message.guild is None:
return
if message.guild.id != Guild.id:
return
reformatted_name = message.channel.name.replace('-', '_')
if CHANNEL_NAME_OVERRIDES.get(message.channel.id):
reformatted_name = CHANNEL_NAME_OVERRIDES.get(message.channel.id)
reformatted_name = "".join(char for char in reformatted_name if char in ALLOWED_CHARS)
stat_name = f"channels.{reformatted_name}"
self.bot.stats.incr(stat_name)
# Increment the total message count
self.bot.stats.incr("messages")
@Cog.listener()
async def on_command_completion(self, ctx: Context) -> None:
"""Report completed commands to statsd."""
command_name = ctx.command.qualified_name.replace(" ", "_")
self.bot.stats.incr(f"commands.{command_name}")
@Cog.listener()
async def on_member_join(self, member: Member) -> None:
"""Update member count stat on member join."""
if member.guild.id != Guild.id:
return
self.bot.stats.gauge(f"guild.total_members", len(member.guild.members))
@Cog.listener()
async def on_member_leave(self, member: Member) -> None:
"""Update member count stat on member leave."""
if member.guild.id != Guild.id:
return
self.bot.stats.gauge(f"guild.total_members", len(member.guild.members))
@Cog.listener()
async def on_member_update(self, _before: Member, after: Member) -> None:
"""Update presence estimates on member update."""
if after.guild.id != Guild.id:
return
if self.last_presence_update:
if (datetime.now() - self.last_presence_update).seconds < StatConf.presence_update_timeout:
return
self.last_presence_update = datetime.now()
online = 0
idle = 0
dnd = 0
offline = 0
for member in after.guild.members:
if member.status is Status.online:
online += 1
elif member.status is Status.dnd:
dnd += 1
elif member.status is Status.idle:
idle += 1
elif member.status is Status.offline:
offline += 1
self.bot.stats.gauge("guild.status.online", online)
self.bot.stats.gauge("guild.status.idle", idle)
self.bot.stats.gauge("guild.status.do_not_disturb", dnd)
self.bot.stats.gauge("guild.status.offline", offline)
def setup(bot: Bot) -> None:
"""Load the stats cog."""
bot.add_cog(Stats(bot))
|