diff --git a/discordbot.py b/discordbot.py index b013aff1..e5a9b893 100644 --- a/discordbot.py +++ b/discordbot.py @@ -298,6 +298,7 @@ class RoyalDiscordBot(discord.Client): if not isinstance(self.main_guild, discord.Guild): raise InvalidConfigError("The main guild does not exist!") await self.change_presence(status=discord.Status.online, activity=None) + logger.info("Bot is ready!") async def on_message(self, message: discord.Message): if message.channel != self.main_channel or message.author.bot: @@ -318,6 +319,7 @@ class RoyalDiscordBot(discord.Client): if data[0] not in self.commands: await message.channel.send(":warning: Comando non riconosciuto.") return + logger.debug(f"Received command: {message.content}") sentry.extra_context({ "command": data[0], "message": message @@ -351,7 +353,7 @@ class RoyalDiscordBot(discord.Client): await self.wait_until_ready() while True: msg = await loop.run_in_executor(executor, connection.recv) - logger.debug(f"Received \"{msg}\" from the Telegram-Discord pipe.") + logger.debug(f"Received from the Telegram-Discord pipe: {msg}") if msg == "get cv": discord_members = list(self.main_guild.members) channels = {0: None} @@ -369,13 +371,17 @@ class RoyalDiscordBot(discord.Client): else: members_in_channels[0].append(member) # Edit the message, sorted by channel - for channel in channels: + 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 += "Non in chat vocale:\n" + message += "Non in chat vocale:\n" else: - message += f"In #{channels[channel].name}:\n" + message += f"In #{channels[channel].name}:\n" for member in members_in_channels[channel]: + # Ignore not-connected non-notable members + if 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 @@ -405,22 +411,29 @@ class RoyalDiscordBot(discord.Client): message += member.name # Game or stream if member.activity is not None: - if member.activity == discord.ActivityType.playing: + if member.activity.type == discord.ActivityType.playing: message += f" | 🎮 {member.activity.name}" - elif member.activity == discord.ActivityType.streaming: + # Rich presence + if member.activity.state: + message += f" ({member.activity.state})" + elif member.activity.details: + message += f" ({member.activity.details})" + elif member.activity.type == discord.ActivityType.streaming: message += f" | 📡 [{member.activity.name}]({member.activity.url})" - elif member.activity == discord.ActivityType.listening: + elif member.activity.type == discord.ActivityType.listening: message += f" | 🎧 {member.activity.name}" elif member.activity.type == discord.ActivityType.watching: message += f" | 📺 {member.activity.name}" message += "\n" message += "\n" connection.send(message) + logger.debug(f"Answered successfully cvlist request.") elif msg.startswith("!"): data = msg.split(" ") if data[0] not in self.commands: connection.send("error") continue + logger.debug(f"Received command: {msg}") await self.main_channel.send(f"{msg}\n" f"_(da Telegram)_") await self.commands[data[0]](channel=self.main_channel, @@ -437,6 +450,8 @@ class RoyalDiscordBot(discord.Client): with async_timeout.timeout(int(config["YouTube"]["download_timeout"])): await video.download() except asyncio.TimeoutError: + logger.warning(f"Video download took more than {config['YouTube']['download_timeout']}s:" + f" {video.plain_text()}") await self.main_channel.send(f"⚠️ Il download di {str(video)} ha richiesto più di" f" {config['YouTube']['download_timeout']} secondi, pertanto è stato" f" rimosso dalla coda.") @@ -449,6 +464,18 @@ class RoyalDiscordBot(discord.Client): del self.video_queue[index] continue except Exception as e: + sentry.user_context({ + "discord": { + "discord_id": video.enqueuer.id, + "name": video.enqueuer.name, + "discriminator": video.enqueuer.discriminator + } + }) + sentry.extra_context({ + "video": video.plain_text() + }) + sentry.captureException() + logger.error(f"Video download error: {str(e)}") await self.main_channel.send(f"⚠️ E' stato incontrato un errore durante il download di " f"{str(video)}, quindi è stato rimosso dalla coda.\n\n" f"**Dettagli sull'errore:**\n" @@ -468,18 +495,18 @@ class RoyalDiscordBot(discord.Client): continue if len(self.video_queue) == 0: self.now_playing = None - await self.change_presence() continue now_playing = self.video_queue[0] try: audio_source = now_playing.create_audio_source() except FileNotDownloadedError: continue - logger.info(f"Started playing {repr(now_playing)}") + logger.info(f"Started playing {repr(now_playing)}.") voice_client.play(audio_source) del self.video_queue[0] activity = discord.Activity(name=now_playing.plain_text(), type=discord.ActivityType.playing) + logger.debug(f"Updated bot presence to {now_playing.plain_text()}.") await self.change_presence(status=discord.Status.online, activity=activity) if now_playing.enqueuer is not None: try: @@ -511,29 +538,32 @@ class RoyalDiscordBot(discord.Client): continue for voice_client in self.voice_clients: if voice_client.is_connected(): + logger.info("Disconnecting due to inactivity.") await voice_client.disconnect() await self.main_channel.send("💤 Mi sono disconnesso dalla cv per inattività.") async def add_video_from_url(self, url, index: typing.Optional[int] = None, enqueuer: discord.Member = None): # Retrieve info + logger.debug(f"Retrieving info for {url}.") with youtube_dl.YoutubeDL({"quiet": True, "ignoreerrors": True, "simulate": True}) as ytdl: info = await loop.run_in_executor(executor, functools.partial(ytdl.extract_info, url=url, download=False)) if info is None: + logger.debug(f"No video found at {url}.") await self.main_channel.send(f"⚠ Non è stato trovato nessun video all'URL `{url}`," f" pertanto non è stato aggiunto alla coda.") return if "entries" in info: - # This is a playlist + logger.debug(f"Playlist detected at {url}.") for entry in info["entries"]: if index is not None: self.video_queue.insert(index, Video(url=entry["webpage_url"], info=entry, enqueuer=enqueuer)) else: self.video_queue.append(Video(url=entry["webpage_url"], info=entry, enqueuer=enqueuer)) return - # This is a single video + logger.debug(f"Single video detected at {url}.") if index is not None: self.video_queue.insert(index, Video(url=url, info=info, enqueuer=enqueuer)) else: @@ -598,10 +628,12 @@ class RoyalDiscordBot(discord.Client): # Check if there's already a connected client for voice_client in self.voice_clients: if voice_client.channel in self.main_guild.channels and voice_client.is_connected(): + logger.info(f"Moving to {author.voice.channel.name}.") await voice_client.move_to(author.voice.channel) await channel.send(f"⤵️ Mi sono spostato in <#{author.voice.channel.id}>.") break else: + logger.info(f"Connecting to {author.voice.channel.name}.") await author.voice.channel.connect() await channel.send(f"⤵️ Mi sono connesso in <#{author.voice.channel.id}>.") @@ -621,12 +653,14 @@ class RoyalDiscordBot(discord.Client): self.next_radio_message_in = int(config["Discord"]["radio_messages_every"]) await self.add_video_from_url(radio_message) await channel.send(f"📻 Aggiunto un messaggio radio, disattiva con `!radiomessages off`.") + logger.info(f"Radio message added to the queue.") # Parse the parameter as URL url = re.match(r"(?:https?://|ytsearch[0-9]*:).*", " ".join(params[1:]).strip("<>")) if url is not None: # This is a url await self.add_video_from_url(url.group(0), enqueuer=author) await channel.send(f"✅ Video aggiunto alla coda.") + logger.debug(f"Added {url} to the queue as URL.") return # Parse the parameter as file file_path = os.path.join(os.path.join(os.path.curdir, "opusfiles"), " ".join(params[1:])) @@ -634,18 +668,21 @@ class RoyalDiscordBot(discord.Client): # This is a file await self.add_video_from_file(file=file_path, enqueuer=author) await channel.send(f"✅ Video aggiunto alla coda.") + logger.debug(f"Added {file_path} to the queue as file.") return file_path += ".opus" if os.path.exists(file_path): # This is a file await self.add_video_from_file(file=file_path, enqueuer=author) await channel.send(f"✅ Video aggiunto alla coda.") + logger.debug(f"Added {file_path} to the queue as file.") return # Search the parameter on youtube search = " ".join(params[1:]) # This is a search await self.add_video_from_url(url=f"ytsearch:{search}", enqueuer=author) await channel.send(f"✅ Video aggiunto alla coda.") + logger.debug(f"Added ytsearch:{search} to the queue as YouTube search.") @command @requires_connected_voice_client @@ -654,6 +691,7 @@ class RoyalDiscordBot(discord.Client): if voice_client.is_playing(): voice_client.stop() await channel.send(f"⏩ Video saltato.") + logger.debug(f"A song was skipped.") break else: await channel.send("⚠ Non c'è nessun video in riproduzione.") @@ -680,6 +718,7 @@ class RoyalDiscordBot(discord.Client): return video = self.video_queue.pop(index) await channel.send(f":regional_indicator_x: {str(video)} è stato rimosso dalla coda.") + logger.debug(f"Removed from queue: {video.plain_text()}") return try: start = int(params[1]) - 1 @@ -709,6 +748,7 @@ class RoyalDiscordBot(discord.Client): return del self.video_queue[start:end] await channel.send(f":regional_indicator_x: {end - start} video rimossi dalla coda.") + logger.debug(f"Removed from queue {end - start} videos.") @command async def cmd_queue(self, channel: discord.TextChannel, author: discord.Member, params: typing.List[str]): @@ -730,6 +770,7 @@ class RoyalDiscordBot(discord.Client): await channel.send("⚠ Non ci sono video in coda!") return random.shuffle(self.video_queue) + logger.info(f"The queue was shuffled by {author.name}#{author.discriminator}.") await channel.send("♠️ ♦️ ♣️ ♥️ Shuffle completo!") @command @@ -739,6 +780,7 @@ class RoyalDiscordBot(discord.Client): await channel.send("⚠ Non ci sono video in coda!") return self.video_queue = [] + logger.info(f"The queue was cleared by {author.name}#{author.discriminator}.") await channel.send(":regional_indicator_x: Tutti i video in coda rimossi.") @command @@ -751,31 +793,36 @@ class RoyalDiscordBot(discord.Client): for voice_client in self.voice_clients: if voice_client.is_playing(): voice_client.stop() + logger.info(f"Video play was forced by {author.name}#{author.discriminator}.") # Parse the parameter as URL url = re.match(r"(?:https?://|ytsearch[0-9]*:).*", " ".join(params[1:]).strip("<>")) if url is not None: # This is a url - await self.add_video_from_url(url.group(0), enqueuer=author, index=0) - await channel.send(f"✅ Riproduzione del video forzata.") + await self.add_video_from_url(url.group(0), enqueuer=author) + await channel.send(f"✅ Video aggiunto alla coda.") + logger.debug(f"Forced {url} as URL.") return # Parse the parameter as file file_path = os.path.join(os.path.join(os.path.curdir, "opusfiles"), " ".join(params[1:])) if os.path.exists(file_path): # This is a file - await self.add_video_from_file(file=file_path, enqueuer=author, index=0) - await channel.send(f"✅ Riproduzione del video forzata.") + await self.add_video_from_file(file=file_path, enqueuer=author) + await channel.send(f"✅ Video aggiunto alla coda.") + logger.debug(f"Forced {file_path} as file.") return file_path += ".opus" if os.path.exists(file_path): # This is a file - await self.add_video_from_file(file=file_path, enqueuer=author, index=0) - await channel.send(f"✅ Riproduzione del video forzata.") + await self.add_video_from_file(file=file_path, enqueuer=author) + await channel.send(f"✅ Video aggiunto alla coda.") + logger.debug(f"Forced {file_path} as file.") return # Search the parameter on youtube search = " ".join(params[1:]) # This is a search - await self.add_video_from_url(url=f"ytsearch:{search}", enqueuer=author, index=0) - await channel.send(f"✅ Riproduzione del video forzata.") + await self.add_video_from_url(url=f"ytsearch:{search}", enqueuer=author) + await channel.send(f"✅ Video aggiunto alla coda.") + logger.debug(f"Forced ytsearch:{search} as YouTube search.") @command async def cmd_radiomessages(self, channel: discord.TextChannel, author: discord.Member, params: typing.List[str]): @@ -790,6 +837,7 @@ class RoyalDiscordBot(discord.Client): await channel.send("⚠ Sintassi del comando non valida.\n" "Sintassi: `!radiomessages [on|off]`") return + logger.info(f"Radio messages status toggled to {self.radio_messages}.") await channel.send(f"📻 Messaggi radio **{'attivati' if self.radio_messages else 'disattivati'}**.") @command @@ -798,6 +846,7 @@ class RoyalDiscordBot(discord.Client): for voice_client in self.voice_clients: if voice_client.is_playing(): voice_client.pause() + logger.debug(f"The audio stream was paused.") await channel.send(f"⏸ Riproduzione messa in pausa.\n" f"Riprendi con `!resume`.") @@ -807,6 +856,7 @@ class RoyalDiscordBot(discord.Client): for voice_client in self.voice_clients: if voice_client.is_playing(): voice_client.resume() + logger.debug(f"The audio stream was resumed.") await channel.send(f"⏯ Riproduzione ripresa.") diff --git a/telegrambot.py b/telegrambot.py index 604dee05..9b002a40 100644 --- a/telegrambot.py +++ b/telegrambot.py @@ -109,7 +109,7 @@ def cmd_discord(bot: Bot, update: Update): return discord_connection.send("get cv") message = discord_connection.recv() - bot.send_message(update.message.chat.id, message, disable_web_page_preview=True) + bot.send_message(update.message.chat.id, message, disable_web_page_preview=True, parse_mode="HTML") @catch_and_report