mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 19:44:20 +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 typing
|
||||||
import time
|
import time
|
||||||
from .youtubedl import YtdlFile, YtdlInfo
|
from .youtubedl import YtdlFile, YtdlInfo
|
||||||
from ..utils import safefilename
|
from ..utils import fileformat
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -82,14 +82,14 @@ class RoyalPCMFile(YtdlFile):
|
||||||
Warning:
|
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...
|
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
|
@property
|
||||||
def audio_filename(self) -> str:
|
def audio_filename(self) -> str:
|
||||||
"""
|
"""
|
||||||
Returns:
|
Returns:
|
||||||
The name of the downloaded and PCM-converted audio file."""
|
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):
|
def delete_audio_file(self):
|
||||||
"""Delete the PCM-converted audio file."""
|
"""Delete the PCM-converted audio file."""
|
||||||
|
|
|
@ -6,7 +6,7 @@ from .generic import GenericBot
|
||||||
from ..commands import NullCommand
|
from ..commands import NullCommand
|
||||||
from ..utils import asyncify, Call, Command
|
from ..utils import asyncify, Call, Command
|
||||||
from ..error import UnregisteredError, NoneFoundError, TooManyFoundError, InvalidConfigError, RoyalnetResponseError
|
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 ..database import DatabaseConfig
|
||||||
from ..audio import PlayMode, Playlist, RoyalPCMAudio
|
from ..audio import PlayMode, Playlist, RoyalPCMAudio
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import asyncio
|
||||||
import logging
|
import logging
|
||||||
from ..utils import Command, NetworkHandler, Call
|
from ..utils import Command, NetworkHandler, Call
|
||||||
from ..commands import NullCommand
|
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
|
from ..database import Alchemy, DatabaseConfig, relationshiplinkchain
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,10 @@ from .skip import SkipCommand
|
||||||
from .playmode import PlaymodeCommand
|
from .playmode import PlaymodeCommand
|
||||||
from .videochannel import VideochannelCommand
|
from .videochannel import VideochannelCommand
|
||||||
from .missing import MissingCommand
|
from .missing import MissingCommand
|
||||||
|
from .cv import CvCommand
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["NullCommand", "PingCommand", "ShipCommand", "SmecdsCommand", "CiaoruoziCommand", "ColorCommand",
|
__all__ = ["NullCommand", "PingCommand", "ShipCommand", "SmecdsCommand", "CiaoruoziCommand", "ColorCommand",
|
||||||
"SyncCommand", "DiarioCommand", "RageCommand", "DateparserCommand", "AuthorCommand", "ReminderCommand",
|
"SyncCommand", "DiarioCommand", "RageCommand", "DateparserCommand", "AuthorCommand", "ReminderCommand",
|
||||||
"KvactiveCommand", "KvCommand", "KvrollCommand", "VideoinfoCommand", "SummonCommand", "PlayCommand",
|
"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]")
|
await call.reply(f"⚠️ Il bot non è stato configurato correttamente, quindi questo comando non può essere eseguito.\n[p]{exception}[/p]")
|
||||||
return
|
return
|
||||||
if isinstance(exception, RoyalnetRequestError):
|
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
|
return
|
||||||
if isinstance(exception, ExternalError):
|
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]")
|
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,
|
commands = [PingCommand, ShipCommand, SmecdsCommand, ColorCommand, CiaoruoziCommand, DebugCreateCommand, SyncCommand,
|
||||||
AuthorCommand, DiarioCommand, RageCommand, DateparserCommand, ReminderCommand, KvactiveCommand, KvCommand,
|
AuthorCommand, DiarioCommand, RageCommand, DateparserCommand, ReminderCommand, KvactiveCommand, KvCommand,
|
||||||
KvrollCommand, VideoinfoCommand, SummonCommand, PlayCommand, SkipCommand, PlaymodeCommand,
|
KvrollCommand, VideoinfoCommand, SummonCommand, PlayCommand, SkipCommand, PlaymodeCommand,
|
||||||
VideochannelCommand]
|
VideochannelCommand, CvCommand]
|
||||||
|
|
||||||
address, port = "127.0.0.1", 1234
|
address, port = "127.0.0.1", 1234
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,8 @@ from .commandargs import CommandArgs
|
||||||
from .safeformat import safeformat
|
from .safeformat import safeformat
|
||||||
from .classdictjanitor import cdj
|
from .classdictjanitor import cdj
|
||||||
from .sleepuntil import sleep_until
|
from .sleepuntil import sleep_until
|
||||||
from .plusformat import plusformat
|
|
||||||
from .networkhandler import NetworkHandler
|
from .networkhandler import NetworkHandler
|
||||||
from .safefilename import safefilename
|
from .formatters import andformat, plusformat, fileformat
|
||||||
|
|
||||||
__all__ = ["asyncify", "Call", "Command", "safeformat", "cdj", "sleep_until", "plusformat", "CommandArgs",
|
__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