fix(bot): ✨ Improve p.stats.
This commit is contained in:
@@ -51,9 +51,11 @@ async def ReplyEmbed(
|
|||||||
|
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
color=constants.EMBED_COLOR,
|
color=constants.EMBED_COLOR,
|
||||||
description=formatting.trim(last_content.content)
|
description=(
|
||||||
if last_content.was_propagated or is_this_channel
|
formatting.trim(last_content.content)
|
||||||
else "*This message was censored.*",
|
if last_content.was_propagated or is_this_channel
|
||||||
|
else "*This message was censored.*"
|
||||||
|
),
|
||||||
author=discord.EmbedAuthor(
|
author=discord.EmbedAuthor(
|
||||||
name=f"Replied to {author_name}",
|
name=f"Replied to {author_name}",
|
||||||
icon_url=author_avatar_url,
|
icon_url=author_avatar_url,
|
||||||
|
|||||||
+90
-17
@@ -1,3 +1,5 @@
|
|||||||
|
from typing import Awaitable
|
||||||
|
|
||||||
from datetime import datetime, timezone, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@@ -13,7 +15,7 @@ from payphone import bot
|
|||||||
from payphone.db import models, utils
|
from payphone.db import models, utils
|
||||||
from payphone.cogs import base
|
from payphone.cogs import base
|
||||||
from payphone.cogs.mods import embeds, views
|
from payphone.cogs.mods import embeds, views
|
||||||
from payphone.core import checks, converters, constants
|
from payphone.core import checks, converters, constants, formatting as fmt
|
||||||
|
|
||||||
|
|
||||||
class Mods(base.Cog):
|
class Mods(base.Cog):
|
||||||
@@ -291,29 +293,100 @@ class Mods(base.Cog):
|
|||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.is_owner()
|
@commands.is_owner()
|
||||||
async def stats(self, ctx: commands.Context) -> None:
|
async def stats(
|
||||||
|
self, ctx: commands.Context, iterations: int = 5, wait: int = 5
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Shows all Payphone's stats.
|
Shows all Payphone's stats.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
- `<iterations>`: The amount of iterations (to display progress). Defaults to 5.
|
||||||
|
- `<wait>`: The amount of seconds to wait between iterations. Defaults to 5.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
stats = [
|
stats = [
|
||||||
f"On-call Channels: {await self.bot.redis.scard(constants.CHANNELS_ONCALL_SET)}",
|
(
|
||||||
f"Searching Channels: {await self.bot.redis.scard(constants.CHANNELS_SEARCH_QUEUE)}",
|
"On-call Channels",
|
||||||
f"Messages: {await models.Message.all().count()}",
|
lambda: self.bot.redis.scard(constants.CHANNELS_ONCALL_SET),
|
||||||
f"Message Contents: {await models.MessageContent.all().count()}",
|
),
|
||||||
f"Calls: {await models.Call.all().count()}",
|
(
|
||||||
f"Ongoing Calls: {await models.Call.filter(ended_at=None).count()}",
|
"Searching Channels",
|
||||||
f"Bans: {await models.Ban.all().count()}",
|
lambda: self.bot.redis.scard(constants.CHANNELS_SEARCH_QUEUE),
|
||||||
f"Reports: {await models.Report.all().count()}",
|
),
|
||||||
f"On-review Reports: {await models.Report.filter(status=models.ReportStatus.ON_REVIEW).count()}",
|
(
|
||||||
f"Users: {await models.User.all().count()}",
|
"Messages",
|
||||||
f"Profiles: {await models.Profile.all().count()}",
|
lambda: models.Message.all().count(),
|
||||||
f"Servers: {await models.Server.all().count()}",
|
),
|
||||||
f"Shards: {self.bot.shard_count}",
|
(
|
||||||
f"Current Shard: {ctx.guild.shard_id + 1}",
|
"Message Contents",
|
||||||
|
lambda: models.MessageContent.all().count(),
|
||||||
|
),
|
||||||
|
("Calls", lambda: models.Call.all().count()),
|
||||||
|
(
|
||||||
|
"Ongoing Calls",
|
||||||
|
lambda: models.Call.filter(ended_at=None).count(),
|
||||||
|
),
|
||||||
|
("Bans", lambda: models.Ban.all().count()),
|
||||||
|
("Reports", lambda: models.Report.all().count()),
|
||||||
|
(
|
||||||
|
"On-review Reports",
|
||||||
|
lambda: models.Report.filter(
|
||||||
|
status=models.ReportStatus.ON_REVIEW
|
||||||
|
).count(),
|
||||||
|
),
|
||||||
|
("Users", lambda: models.User.all().count()),
|
||||||
|
(
|
||||||
|
"Profiles",
|
||||||
|
lambda: models.Profile.all().count(),
|
||||||
|
),
|
||||||
|
("Servers", lambda: models.Server.all().count()),
|
||||||
|
("Shards", lambda: self.bot.shard_count),
|
||||||
|
("Current Shards", lambda: ctx.guild.shard_id + 1),
|
||||||
]
|
]
|
||||||
|
|
||||||
await ctx.send(f"```\n{"\n".join(stats)}\n```")
|
rows = [[stat[0]] for stat in stats]
|
||||||
|
rows_with_diff = [[stat[0]] for stat in stats]
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
message = None
|
||||||
|
|
||||||
|
while i < iterations:
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
for stat_i, stat in enumerate(stats):
|
||||||
|
value = stat[1]()
|
||||||
|
|
||||||
|
if isinstance(value, Awaitable):
|
||||||
|
value = await value
|
||||||
|
|
||||||
|
rows[stat_i].append(value)
|
||||||
|
prev_value = rows[stat_i][-2]
|
||||||
|
|
||||||
|
if isinstance(prev_value, str):
|
||||||
|
rows_with_diff[stat_i].append(value)
|
||||||
|
|
||||||
|
else:
|
||||||
|
diff = value - prev_value
|
||||||
|
|
||||||
|
rows_with_diff[stat_i].append(
|
||||||
|
str(value)
|
||||||
|
+ (f" ({"+" if diff > 0 else ""}{diff})" if diff != 0 else "")
|
||||||
|
)
|
||||||
|
|
||||||
|
table = fmt.monospace_table(
|
||||||
|
rows_with_diff,
|
||||||
|
[""] + [f"{wait * j}s" for j in range(i)],
|
||||||
|
padding=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
if message is None:
|
||||||
|
message = await ctx.send(f"```\n{table}\n```")
|
||||||
|
|
||||||
|
else:
|
||||||
|
message = await message.edit(f"```\n{table}\n```")
|
||||||
|
|
||||||
|
if i < iterations:
|
||||||
|
await asyncio.sleep(wait)
|
||||||
|
|
||||||
|
|
||||||
def setup(b: bot.Payphone) -> None:
|
def setup(b: bot.Payphone) -> None:
|
||||||
|
|||||||
@@ -28,15 +28,19 @@ class BanNotificationEmbed(discord.Embed):
|
|||||||
),
|
),
|
||||||
discord.EmbedField(
|
discord.EmbedField(
|
||||||
name="Expires At",
|
name="Expires At",
|
||||||
value=f"<t:{int(ban.expires_at.timestamp())}:F> (<t:{int(ban.expires_at.timestamp())}:R>)"
|
value=(
|
||||||
if ban.expires_at
|
f"<t:{int(ban.expires_at.timestamp())}:F> (<t:{int(ban.expires_at.timestamp())}:R>)"
|
||||||
else "*Does not expire.*",
|
if ban.expires_at
|
||||||
|
else "*Does not expire.*"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
discord.EmbedField(
|
discord.EmbedField(
|
||||||
name="Appeal",
|
name="Appeal",
|
||||||
value="*You haven't appealed this ban.*"
|
value=(
|
||||||
if ban.appeal is None
|
"*You haven't appealed this ban.*"
|
||||||
else ban.appeal,
|
if ban.appeal is None
|
||||||
|
else ban.appeal
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
author=bot.embed_author,
|
author=bot.embed_author,
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ class ReportView(discord.ui.View):
|
|||||||
await interaction.response.edit_message(
|
await interaction.response.edit_message(
|
||||||
content="✅ You're up to date! No reports left to check.",
|
content="✅ You're up to date! No reports left to check.",
|
||||||
embed=None,
|
embed=None,
|
||||||
view=None
|
view=None,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ class ReportView(discord.ui.View):
|
|||||||
await interaction.response.edit_message(
|
await interaction.response.edit_message(
|
||||||
content="✅ You're up to date! No reports left to check.",
|
content="✅ You're up to date! No reports left to check.",
|
||||||
embed=None,
|
embed=None,
|
||||||
view=None
|
view=None,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -175,9 +175,11 @@ class Users(base.Cog):
|
|||||||
logs += f"- `{call_id}`: "
|
logs += f"- `{call_id}`: "
|
||||||
|
|
||||||
participant_names = ", ".join(
|
participant_names = ", ".join(
|
||||||
call_rs.display_name
|
(
|
||||||
if call_rs.channel_id != data["user_rs"].channel_id
|
call_rs.display_name
|
||||||
else call_rs.profile.name
|
if call_rs.channel_id != data["user_rs"].channel_id
|
||||||
|
else call_rs.profile.name
|
||||||
|
)
|
||||||
for call_rs in data["others_rs"] + [data["user_rs"]]
|
for call_rs in data["others_rs"] + [data["user_rs"]]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -246,16 +246,16 @@ def moderate(message: str) -> str:
|
|||||||
match = next(PATTERN.finditer(message), None)
|
match = next(PATTERN.finditer(message), None)
|
||||||
|
|
||||||
if match is not None:
|
if match is not None:
|
||||||
return message[match.start():match.end()]
|
return message[match.start() : match.end()]
|
||||||
|
|
||||||
match = next(URL_PATTERN.finditer(message), None)
|
match = next(URL_PATTERN.finditer(message), None)
|
||||||
|
|
||||||
if match is not None:
|
if match is not None:
|
||||||
return message[match.start():match.end()]
|
return message[match.start() : match.end()]
|
||||||
|
|
||||||
match = next(INVITES_PATTERN.finditer(message), None)
|
match = next(INVITES_PATTERN.finditer(message), None)
|
||||||
|
|
||||||
if match is not None:
|
if match is not None:
|
||||||
return message[match.start():match.end()]
|
return message[match.start() : match.end()]
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ def is_unbanned():
|
|||||||
async def predicate(ctx: commands.Context) -> bool:
|
async def predicate(ctx: commands.Context) -> bool:
|
||||||
if await ctx.bot.is_banned(ctx.author):
|
if await ctx.bot.is_banned(ctx.author):
|
||||||
raise exceptions.BannedError()
|
raise exceptions.BannedError()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return commands.check(predicate)
|
return commands.check(predicate)
|
||||||
|
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ def is_on_call():
|
|||||||
|
|
||||||
if not is_on_call:
|
if not is_on_call:
|
||||||
raise exceptions.NotOnCallError()
|
raise exceptions.NotOnCallError()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return commands.check(predicate)
|
return commands.check(predicate)
|
||||||
|
|||||||
@@ -56,6 +56,68 @@ def monospace_grid(values: list[str], space: int = 14) -> str:
|
|||||||
return "```" + "".join([v + " " * (space - len(v)) for v in values]) + "```"
|
return "```" + "".join([v + " " * (space - len(v)) for v in values]) + "```"
|
||||||
|
|
||||||
|
|
||||||
|
def monospace_table(
|
||||||
|
rows: list[tuple],
|
||||||
|
headers: list | None = None,
|
||||||
|
column_separator: str = "|",
|
||||||
|
header_separator: str = "-",
|
||||||
|
intersection: str = "+",
|
||||||
|
padding: int = 1,
|
||||||
|
) -> str:
|
||||||
|
"""Creates a monospace table.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
rows (list[tuple]): The rows.
|
||||||
|
headers (list | None, optional): The headers.
|
||||||
|
column_separator (str, optional): The column separator. Defaults to "|".
|
||||||
|
header_separator (str, optional): The header separator. Defaults to "-".
|
||||||
|
intersection (str, optional): The intersection character where column and row separators meet.
|
||||||
|
padding (int, optional): The padding that each column has at its sides.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The monospace table.
|
||||||
|
"""
|
||||||
|
|
||||||
|
max_sizes = [
|
||||||
|
max(
|
||||||
|
*(
|
||||||
|
[len(str(row[i])) for row in rows]
|
||||||
|
+ ([len(headers[i])] if headers is not None else [])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for i in range(len(rows[0]))
|
||||||
|
]
|
||||||
|
|
||||||
|
def format_row(row: list) -> str:
|
||||||
|
return column_separator.join(
|
||||||
|
[
|
||||||
|
(" " * padding)
|
||||||
|
+ str(col)
|
||||||
|
+ (" " * (max_sizes[i] - len(str(col))))
|
||||||
|
+ (" " * padding)
|
||||||
|
for i, col in enumerate(row)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
formatted_rows = []
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
formatted_rows.append(format_row(row))
|
||||||
|
|
||||||
|
table = "\n".join(formatted_rows)
|
||||||
|
|
||||||
|
if headers is not None:
|
||||||
|
header = format_row(headers)
|
||||||
|
|
||||||
|
separator = intersection.join(
|
||||||
|
[header_separator * (size + padding * 2) for size in max_sizes]
|
||||||
|
)
|
||||||
|
|
||||||
|
table = f"{header}\n{separator}\n{table}"
|
||||||
|
|
||||||
|
return table
|
||||||
|
|
||||||
|
|
||||||
def clean(
|
def clean(
|
||||||
message: discord.Message, call_rss: list[models.CallUserChannelRelationship]
|
message: discord.Message, call_rss: list[models.CallUserChannelRelationship]
|
||||||
) -> str:
|
) -> str:
|
||||||
@@ -75,9 +137,11 @@ def clean(
|
|||||||
for call_rs in call_rss:
|
for call_rs in call_rss:
|
||||||
cleaned_message = cleaned_message.replace(
|
cleaned_message = cleaned_message.replace(
|
||||||
f"<@{call_rs.user_id}>",
|
f"<@{call_rs.user_id}>",
|
||||||
f"@**{call_rs.display_name}**"
|
(
|
||||||
if message.channel.id == call_rs.channel_id
|
f"@**{call_rs.display_name}**"
|
||||||
else f"@**{call_rs.profile.name}**",
|
if message.channel.id == call_rs.channel_id
|
||||||
|
else f"@**{call_rs.profile.name}**"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
for user in message.mentions:
|
for user in message.mentions:
|
||||||
|
|||||||
@@ -82,9 +82,11 @@ class HelpCommand(commands.HelpCommand):
|
|||||||
[f" <{option.name}>" for option in command.options]
|
[f" <{option.name}>" for option in command.options]
|
||||||
if isinstance(command, discord.SlashCommand)
|
if isinstance(command, discord.SlashCommand)
|
||||||
else [
|
else [
|
||||||
f" [<{name}>]"
|
(
|
||||||
if typing_utils.is_optional(info.annotation)
|
f" [<{name}>]"
|
||||||
else f" <{name}>"
|
if typing_utils.is_optional(info.annotation)
|
||||||
|
else f" <{name}>"
|
||||||
|
)
|
||||||
for name, info in command.params.items()
|
for name, info in command.params.items()
|
||||||
if name not in ["self", "ctx"]
|
if name not in ["self", "ctx"]
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ from payphone.core import config
|
|||||||
|
|
||||||
|
|
||||||
async def init() -> None:
|
async def init() -> None:
|
||||||
"""Initialize the database and it's connections.
|
"""Initialize the database and it's connections."""
|
||||||
"""
|
|
||||||
|
|
||||||
await tortoise.Tortoise.init(config.TORTOISE_ORM, use_tz=True)
|
await tortoise.Tortoise.init(config.TORTOISE_ORM, use_tz=True)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user