feat: (BETA) %freq graph
This commit is contained in:
@@ -2,3 +2,5 @@ discord.py==1.7.0
|
|||||||
python-dotenv==0.15.0
|
python-dotenv==0.15.0
|
||||||
python-dateutil==2.8.1
|
python-dateutil==2.8.1
|
||||||
git+git://github.com/Klemek/miniscord.git
|
git+git://github.com/Klemek/miniscord.git
|
||||||
|
numpy
|
||||||
|
matplotlib
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
from typing import List
|
from typing import List
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import calendar
|
import calendar
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
from io import BytesIO
|
||||||
|
import discord
|
||||||
|
import time
|
||||||
|
|
||||||
from utils import (
|
from utils import (
|
||||||
from_now,
|
from_now,
|
||||||
@@ -30,6 +35,45 @@ class Frequency:
|
|||||||
self.longest_streak_start = None
|
self.longest_streak_start = None
|
||||||
self.longest_streak_author = None
|
self.longest_streak_author = None
|
||||||
|
|
||||||
|
def to_graph(self) -> List[str]:
|
||||||
|
self.dates.sort()
|
||||||
|
delta = self.dates[-1] - self.dates[0]
|
||||||
|
if delta.days == 0:
|
||||||
|
delta = timedelta(days=1)
|
||||||
|
day = {j: sum(self.hours[i][j] for i in range(7)) for j in range(24)}
|
||||||
|
busiest_hour = top_key(day)
|
||||||
|
n_hours = delta.days
|
||||||
|
if self.dates[0].hour <= busiest_hour and self.dates[-1].hour >= busiest_hour:
|
||||||
|
n_hours += 1
|
||||||
|
|
||||||
|
plt.style.use('dark_background')
|
||||||
|
|
||||||
|
fig, ax = plt.subplots()
|
||||||
|
|
||||||
|
fig.patch.set_facecolor('#36393F')
|
||||||
|
ax.patch.set_alpha(0)
|
||||||
|
|
||||||
|
times = range(25)
|
||||||
|
|
||||||
|
for i in range(7):
|
||||||
|
hours = [self.hours[i][hour]*7/n_hours for hour in range(24)] + [self.hours[i][0]*7/n_hours]
|
||||||
|
ax.plot(times, hours, label=calendar.day_name[i], linestyle='--', linewidth=0.8)
|
||||||
|
|
||||||
|
hours = [day[hour]/n_hours for hour in range(24)] + [day[0]/n_hours]
|
||||||
|
ax.plot(times, hours, c='r', label='average', linewidth=1.5)
|
||||||
|
|
||||||
|
ax.set_xlabel('hour of day')
|
||||||
|
ax.set_xlim([0, 24])
|
||||||
|
ax.set_ylabel('average messages')
|
||||||
|
ax.legend(framealpha=0)
|
||||||
|
|
||||||
|
with BytesIO() as f:
|
||||||
|
plt.savefig(f, format='png', facecolor=fig.get_facecolor(), edgecolor='none')
|
||||||
|
f.seek(0)
|
||||||
|
return [
|
||||||
|
discord.File(f, f"{time.time()}-plot.png")
|
||||||
|
]
|
||||||
|
|
||||||
def to_string(
|
def to_string(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
|
|||||||
@@ -14,11 +14,13 @@ from utils import generate_help
|
|||||||
class FrequencyScanner(Scanner):
|
class FrequencyScanner(Scanner):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def help() -> str:
|
def help() -> str:
|
||||||
return generate_help("freq", "Show frequency-related statistics")
|
return generate_help("freq", "(BETA) Show frequency-related statistics", args=[
|
||||||
|
"graph - plot hours of week",
|
||||||
|
],)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
valid_args=["all", "everyone"],
|
valid_args=["all", "everyone", "graph"],
|
||||||
help=FrequencyScanner.help(),
|
help=FrequencyScanner.help(),
|
||||||
intro_context="Frequency",
|
intro_context="Frequency",
|
||||||
)
|
)
|
||||||
@@ -27,6 +29,7 @@ class FrequencyScanner(Scanner):
|
|||||||
self.freq = Frequency()
|
self.freq = Frequency()
|
||||||
self.all_messages = "all" in args or "everyone" in args
|
self.all_messages = "all" in args or "everyone" in args
|
||||||
self.member_specific = len(self.members) > 0
|
self.member_specific = len(self.members) > 0
|
||||||
|
self.to_graph = "graph" in args
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def compute_message(self, channel: ChannelLogs, message: MessageLog):
|
def compute_message(self, channel: ChannelLogs, message: MessageLog):
|
||||||
@@ -36,10 +39,13 @@ class FrequencyScanner(Scanner):
|
|||||||
|
|
||||||
def get_results(self, intro: str) -> List[str]:
|
def get_results(self, intro: str) -> List[str]:
|
||||||
FrequencyScanner.compute_results(self.freq)
|
FrequencyScanner.compute_results(self.freq)
|
||||||
res = [intro]
|
if self.to_graph:
|
||||||
res += self.freq.to_string(
|
res = self.freq.to_graph()
|
||||||
member_specific=self.member_specific,
|
else:
|
||||||
)
|
res = [intro]
|
||||||
|
res += self.freq.to_string(
|
||||||
|
member_specific=self.member_specific,
|
||||||
|
)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
+10
-2
@@ -288,15 +288,20 @@ class Scanner(ABC):
|
|||||||
if self.mention_users
|
if self.mention_users
|
||||||
else discord.AllowedMentions.none()
|
else discord.AllowedMentions.none()
|
||||||
)
|
)
|
||||||
|
file = None
|
||||||
for r in results:
|
for r in results:
|
||||||
if r:
|
if r:
|
||||||
if isinstance(r, int) and r == SPLIT_TOKEN:
|
if isinstance(r, discord.File):
|
||||||
|
file = r
|
||||||
|
elif isinstance(r, int) and r == SPLIT_TOKEN:
|
||||||
await message.channel.send(
|
await message.channel.send(
|
||||||
response,
|
response,
|
||||||
reference=message if first else None,
|
reference=message if first else None,
|
||||||
allowed_mentions=allowed_mentions,
|
allowed_mentions=allowed_mentions,
|
||||||
|
file=file,
|
||||||
)
|
)
|
||||||
first = False
|
first = False
|
||||||
|
file = None
|
||||||
response = ""
|
response = ""
|
||||||
elif isinstance(r, str):
|
elif isinstance(r, str):
|
||||||
if len(response + "\n" + r) > 2000:
|
if len(response + "\n" + r) > 2000:
|
||||||
@@ -304,15 +309,18 @@ class Scanner(ABC):
|
|||||||
response,
|
response,
|
||||||
reference=message if first else None,
|
reference=message if first else None,
|
||||||
allowed_mentions=allowed_mentions,
|
allowed_mentions=allowed_mentions,
|
||||||
|
file=file,
|
||||||
)
|
)
|
||||||
first = False
|
first = False
|
||||||
|
file = None
|
||||||
response = ""
|
response = ""
|
||||||
response += "\n" + r
|
response += "\n" + r
|
||||||
if len(response) > 0:
|
if len(response) > 0 or file is not None:
|
||||||
await message.channel.send(
|
await message.channel.send(
|
||||||
response,
|
response,
|
||||||
reference=message if first else None,
|
reference=message if first else None,
|
||||||
allowed_mentions=allowed_mentions,
|
allowed_mentions=allowed_mentions,
|
||||||
|
file=file,
|
||||||
)
|
)
|
||||||
command_cache.cache(self, message, args)
|
command_cache.cache(self, message, args)
|
||||||
# Delete custom progress message
|
# Delete custom progress message
|
||||||
|
|||||||
Reference in New Issue
Block a user