1
Fork 0
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:
Steffo 2019-05-27 01:02:58 +02:00
parent a8d772b6fb
commit 08aebb71e8
11 changed files with 175 additions and 28 deletions

View file

@ -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."""

View 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

View file

@ -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

View file

@ -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
View 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"])

View file

@ -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]")

View file

@ -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

View file

@ -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"]

View 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)

View file

@ -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)

View file

@ -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)