mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-27 13:34:28 +00:00
Add cv command
This commit is contained in:
parent
a8d772b6fb
commit
08aebb71e8
11 changed files with 175 additions and 28 deletions
|
@ -4,7 +4,7 @@ import os
|
|||
import typing
|
||||
import time
|
||||
from .youtubedl import YtdlFile, YtdlInfo
|
||||
from ..utils import safefilename
|
||||
from ..utils import fileformat
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -82,14 +82,14 @@ class RoyalPCMFile(YtdlFile):
|
|||
Warning:
|
||||
It's going to be deleted as soon as the :py:func:`royalnet.audio.RoyalPCMFile.__init__` function has completed, so it's probably not going to be very useful...
|
||||
"""
|
||||
return f"./downloads/{safefilename(self.info.title)}-{safefilename(str(int(self._time)))}.ytdl"
|
||||
return f"./downloads/{fileformat(self.info.title)}-{fileformat(str(int(self._time)))}.ytdl"
|
||||
|
||||
@property
|
||||
def audio_filename(self) -> str:
|
||||
"""
|
||||
Returns:
|
||||
The name of the downloaded and PCM-converted audio file."""
|
||||
return f"./downloads/{safefilename(self.info.title)}-{safefilename(str(int(self._time)))}.pcm"
|
||||
return f"./downloads/{fileformat(self.info.title)}-{fileformat(str(int(self._time)))}.pcm"
|
||||
|
||||
def delete_audio_file(self):
|
||||
"""Delete the PCM-converted audio file."""
|
||||
|
|
|
@ -6,7 +6,7 @@ from .generic import GenericBot
|
|||
from ..commands import NullCommand
|
||||
from ..utils import asyncify, Call, Command
|
||||
from ..error import UnregisteredError, NoneFoundError, TooManyFoundError, InvalidConfigError, RoyalnetResponseError
|
||||
from ..network import RoyalnetConfig, Request, Response, ResponseSuccess, ResponseError
|
||||
from ..network import RoyalnetConfig, Request, ResponseSuccess, ResponseError
|
||||
from ..database import DatabaseConfig
|
||||
from ..audio import PlayMode, Playlist, RoyalPCMAudio
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import asyncio
|
|||
import logging
|
||||
from ..utils import Command, NetworkHandler, Call
|
||||
from ..commands import NullCommand
|
||||
from ..network import RoyalnetLink, Request, Response, ResponseSuccess, ResponseError, RoyalnetConfig
|
||||
from ..network import RoyalnetLink, Request, Response, ResponseError, RoyalnetConfig
|
||||
from ..database import Alchemy, DatabaseConfig, relationshiplinkchain
|
||||
|
||||
|
||||
|
|
|
@ -22,9 +22,10 @@ from .skip import SkipCommand
|
|||
from .playmode import PlaymodeCommand
|
||||
from .videochannel import VideochannelCommand
|
||||
from .missing import MissingCommand
|
||||
from .cv import CvCommand
|
||||
|
||||
|
||||
__all__ = ["NullCommand", "PingCommand", "ShipCommand", "SmecdsCommand", "CiaoruoziCommand", "ColorCommand",
|
||||
"SyncCommand", "DiarioCommand", "RageCommand", "DateparserCommand", "AuthorCommand", "ReminderCommand",
|
||||
"KvactiveCommand", "KvCommand", "KvrollCommand", "VideoinfoCommand", "SummonCommand", "PlayCommand",
|
||||
"SkipCommand", "PlaymodeCommand", "VideochannelCommand", "MissingCommand"]
|
||||
"SkipCommand", "PlaymodeCommand", "VideochannelCommand", "MissingCommand", "CvCommand"]
|
||||
|
|
122
royalnet/commands/cv.py
Normal file
122
royalnet/commands/cv.py
Normal file
|
@ -0,0 +1,122 @@
|
|||
import typing
|
||||
import discord
|
||||
import asyncio
|
||||
from ..utils import Command, Call, NetworkHandler, andformat
|
||||
from ..network import Request, ResponseSuccess
|
||||
from ..error import NoneFoundError, TooManyFoundError
|
||||
if typing.TYPE_CHECKING:
|
||||
from ..bots import DiscordBot
|
||||
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
|
||||
class CvNH(NetworkHandler):
|
||||
message_type = "discord_cv"
|
||||
|
||||
@classmethod
|
||||
async def discord(cls, bot: "DiscordBot", data: dict):
|
||||
# Find the matching guild
|
||||
if data["guild_name"]:
|
||||
guild: discord.Guild = bot.client.find_guild_by_name(data["guild_name"])
|
||||
else:
|
||||
if len(bot.client.guilds) == 0:
|
||||
raise NoneFoundError("No guilds found")
|
||||
if len(bot.client.guilds) > 1:
|
||||
raise TooManyFoundError("Multiple guilds found")
|
||||
guild = list(bot.client.guilds)[0]
|
||||
# Edit the message, sorted by channel
|
||||
discord_members = list(guild.members)
|
||||
channels = {0: None}
|
||||
members_in_channels = {0: []}
|
||||
message = ""
|
||||
# Find all the channels
|
||||
for member in discord_members:
|
||||
if member.voice is not None:
|
||||
channel = members_in_channels.get(member.voice.channel.id)
|
||||
if channel is None:
|
||||
members_in_channels[member.voice.channel.id] = list()
|
||||
channel = members_in_channels[member.voice.channel.id]
|
||||
channels[member.voice.channel.id] = member.voice.channel
|
||||
channel.append(member)
|
||||
else:
|
||||
members_in_channels[0].append(member)
|
||||
# Edit the message, sorted by channel
|
||||
for channel in sorted(channels, key=lambda c: -c):
|
||||
members_in_channels[channel].sort(key=lambda x: x.nick if x.nick is not None else x.name)
|
||||
if channel == 0:
|
||||
message += "[b]Non in chat vocale:[/b]\n"
|
||||
else:
|
||||
message += f"[b]In #{channels[channel].name}:[/b]\n"
|
||||
for member in members_in_channels[channel]:
|
||||
member: typing.Union[discord.User, discord.Member]
|
||||
# Ignore not-connected non-notable members
|
||||
if not data["full"] and channel == 0 and len(member.roles) < 2:
|
||||
continue
|
||||
# Ignore offline members
|
||||
if member.status == discord.Status.offline and member.voice is None:
|
||||
continue
|
||||
# Online status emoji
|
||||
if member.bot:
|
||||
message += "🤖 "
|
||||
elif member.status == discord.Status.online:
|
||||
message += "🔵 "
|
||||
elif member.status == discord.Status.idle:
|
||||
message += "⚫️ "
|
||||
elif member.status == discord.Status.dnd:
|
||||
message += "🔴 "
|
||||
elif member.status == discord.Status.offline:
|
||||
message += "⚪️ "
|
||||
# Voice
|
||||
if channel != 0:
|
||||
# Voice status
|
||||
if member.voice.self_mute:
|
||||
message += f"🔇 "
|
||||
else:
|
||||
message += f"🔊 "
|
||||
# Nickname
|
||||
if member.nick is not None:
|
||||
message += f"[i]{member.nick}[/i]"
|
||||
else:
|
||||
message += member.name
|
||||
# Game or stream
|
||||
if member.activity is not None:
|
||||
if member.activity.type == discord.ActivityType.playing:
|
||||
message += f" | 🎮 {member.activity.name}"
|
||||
# Rich presence
|
||||
try:
|
||||
if member.activity.state is not None:
|
||||
message += f" ({member.activity.state}" \
|
||||
f" | {member.activity.details})"
|
||||
except AttributeError:
|
||||
pass
|
||||
elif member.activity.type == discord.ActivityType.streaming:
|
||||
message += f" | 📡 {member.activity.url})"
|
||||
elif member.activity.type == discord.ActivityType.listening:
|
||||
if isinstance(member.activity, discord.Spotify):
|
||||
if member.activity.title == member.activity.album:
|
||||
message += f" | 🎧 {member.activity.title} ({andformat(member.activity.artists, final=' e ')})"
|
||||
else:
|
||||
message += f" | 🎧 {member.activity.title} ({member.activity.album} | {andformat(member.activity.artists, final=' e ')})"
|
||||
else:
|
||||
message += f" | 🎧 {member.activity.name}"
|
||||
elif member.activity.type == discord.ActivityType.watching:
|
||||
message += f" | 📺 {member.activity.name}"
|
||||
message += "\n"
|
||||
message += "\n"
|
||||
return ResponseSuccess({"response": message})
|
||||
|
||||
|
||||
class CvCommand(Command):
|
||||
|
||||
command_name = "cv"
|
||||
command_description = "Elenca le persone attualmente connesse alla chat vocale."
|
||||
command_syntax = "[guildname]"
|
||||
|
||||
network_handlers = [CvNH]
|
||||
|
||||
@classmethod
|
||||
async def common(cls, call: Call):
|
||||
guild_name = call.args.optional(0)
|
||||
response = await call.net_request(Request("discord_cv", {"guild_name": guild_name, "full": False}), "discord")
|
||||
await call.reply(response["response"])
|
|
@ -34,7 +34,9 @@ class ErrorHandlerCommand(Command):
|
|||
await call.reply(f"⚠️ Il bot non è stato configurato correttamente, quindi questo comando non può essere eseguito.\n[p]{exception}[/p]")
|
||||
return
|
||||
if isinstance(exception, RoyalnetRequestError):
|
||||
await call.reply(f"⚠️ La richiesta a Royalnet ha restituito un errore: [p]{exception.error}[/p]")
|
||||
await call.reply(f"⚠️ La richiesta a Royalnet ha restituito un errore:\n"
|
||||
f"[p]{exception.error.extra_info['type']}\n"
|
||||
f"{exception.error.extra_info['str']}[/p]")
|
||||
return
|
||||
if isinstance(exception, ExternalError):
|
||||
await call.reply(f"⚠️ Una risorsa esterna necessaria per l'esecuzione del comando non ha funzionato correttamente, quindi il comando è stato annullato.\n[p]{exception}[/p]")
|
||||
|
|
|
@ -20,7 +20,7 @@ log.setLevel(logging.WARNING)
|
|||
commands = [PingCommand, ShipCommand, SmecdsCommand, ColorCommand, CiaoruoziCommand, DebugCreateCommand, SyncCommand,
|
||||
AuthorCommand, DiarioCommand, RageCommand, DateparserCommand, ReminderCommand, KvactiveCommand, KvCommand,
|
||||
KvrollCommand, VideoinfoCommand, SummonCommand, PlayCommand, SkipCommand, PlaymodeCommand,
|
||||
VideochannelCommand]
|
||||
VideochannelCommand, CvCommand]
|
||||
|
||||
address, port = "127.0.0.1", 1234
|
||||
|
||||
|
|
|
@ -7,9 +7,8 @@ from .commandargs import CommandArgs
|
|||
from .safeformat import safeformat
|
||||
from .classdictjanitor import cdj
|
||||
from .sleepuntil import sleep_until
|
||||
from .plusformat import plusformat
|
||||
from .networkhandler import NetworkHandler
|
||||
from .safefilename import safefilename
|
||||
from .formatters import andformat, plusformat, fileformat
|
||||
|
||||
__all__ = ["asyncify", "Call", "Command", "safeformat", "cdj", "sleep_until", "plusformat", "CommandArgs",
|
||||
"NetworkHandler", "safefilename"]
|
||||
"NetworkHandler", "andformat", "plusformat", "fileformat"]
|
||||
|
|
40
royalnet/utils/formatters.py
Normal file
40
royalnet/utils/formatters.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
import typing
|
||||
import re
|
||||
|
||||
|
||||
def andformat(l: typing.List[str], middle=", ", final=" and ") -> str:
|
||||
"""Convert a :py:class:`list` to a :py:class:`str` by adding ``final`` between the last two elements and ``middle`` between the others.
|
||||
|
||||
Parameters:
|
||||
l: the input :py:class:`list`.
|
||||
middle: the :py:class:`str` to be added between the middle elements.
|
||||
final: the :py:class:`str` to be added between the last two elements.
|
||||
|
||||
Returns:
|
||||
The resulting :py:class:`str`."""
|
||||
result = ""
|
||||
for index, item in enumerate(l):
|
||||
result += item
|
||||
if index == len(l) - 2:
|
||||
result += final
|
||||
elif index != len(l) - 1:
|
||||
result += middle
|
||||
return result
|
||||
|
||||
|
||||
def plusformat(i: int) -> str:
|
||||
"""Convert an :py:class:`int` to a :py:class:`str`, adding a ``+`` if they are greater than 0.
|
||||
|
||||
Parameters:
|
||||
i: the :py:class:`int` to convert.
|
||||
|
||||
Returns:
|
||||
The resulting :py:class:`str`."""
|
||||
if i >= 0:
|
||||
return f"+{i}"
|
||||
return str(i)
|
||||
|
||||
|
||||
def fileformat(string: str) -> str:
|
||||
"""Ensure a string can be used as a filename by replacing all non-word characters with underscores."""
|
||||
return re.sub(r"\W", "_", string)
|
|
@ -1,11 +0,0 @@
|
|||
def plusformat(i: int) -> str:
|
||||
"""Convert an :py:class:`int` to a string, adding a plus if they are greater than 0.
|
||||
|
||||
Parameters:
|
||||
i: the :py:class:`int` to convert.
|
||||
|
||||
Returns:
|
||||
The resulting :py:class:`str`."""
|
||||
if i >= 0:
|
||||
return f"+{i}"
|
||||
return str(i)
|
|
@ -1,6 +0,0 @@
|
|||
import re
|
||||
|
||||
|
||||
def safefilename(string: str) -> str:
|
||||
"""Ensure a string can be used as a filename by replacing all non-word characters with underscores."""
|
||||
return re.sub(r"\W", "_", string)
|
Loading…
Reference in a new issue