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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
import string
from datetime import datetime
from discord import Member, Message, Status
from discord.ext.commands import Cog, Context
from discord.ext.tasks import loop
from bot.bot import Bot
from bot.constants import Categories, 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
self.update_guild_boost.start()
@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
if message.channel.category.id == Categories.modmail:
if message.channel.id != Channels.incidents:
# Do not report modmail channels to stats, there are too many
# of them for interesting statistics to be drawn out of this.
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("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("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)
@loop(hours=1)
async def update_guild_boost(self) -> None:
"""Post the server boost level and tier every hour."""
await self.bot.wait_until_guild_available()
g = self.bot.get_guild(Guild.id)
self.bot.stats.gauge("boost.amount", g.premium_subscription_count)
self.bot.stats.gauge("boost.tier", g.premium_tier)
def cog_unload(self) -> None:
"""Stop the boost statistic task on unload of the Cog."""
self.update_guild_boost.stop()
def setup(bot: Bot) -> None:
"""Load the stats cog."""
bot.add_cog(Stats(bot))
|