import random import re import discord import discord.opus import discord.voice_client import functools import sys import db import youtube_dl import concurrent.futures import typing import os import asyncio import configparser import async_timeout import raven import logging import errors import datetime import sqlalchemy.exc import coloredlogs import errors import math import enum logging.getLogger().disabled = True logger = logging.getLogger(__name__) os.environ["COLOREDLOGS_LOG_FORMAT"] = "%(asctime)s %(levelname)s %(name)s %(message)s" coloredlogs.install(level="DEBUG", logger=logger) # Queue emojis queue_emojis = [":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:", ":keycap_ten:"] # Zalgo text zalgo_up = ['̍', '̎', '̄', '̅', '̿', '̑', '̆', '̐', '͒', '͗', '͑', '̇', '̈', '̊', '͂', '̓', '̈́', '͊', '͋', '͌', '̃', '̂', '̌', '͐', '́', '̋', '̏', '̽', '̉', 'ͣ', 'ͤ', 'ͥ', 'ͦ', 'ͧ', 'ͨ', 'ͩ', 'ͪ', 'ͫ', 'ͬ', 'ͭ', 'ͮ', 'ͯ', '̾', '͛', '͆', '̚', ] zalgo_down = ['̖', '̗', '̘', '̙', '̜', '̝', '̞', '̟', '̠', '̤', '̥', '̦', '̩', '̪', '̫', '̬', '̭', '̮', '̯', '̰', '̱', '̲', '̳', '̹', '̺', '̻', '̼', 'ͅ', '͇', '͈', '͉', '͍', '͎', '͓', '͔', '͕', '͖', '͙', '͚', '', ] zalgo_middle = ['̕', '̛', '̀', '́', '͘', '̡', '̢', '̧', '̨', '̴', '̵', '̶', '͜', '͝', '͞', '͟', '͠', '͢', '̸', '̷', '͡', ] # Init the event loop loop = asyncio.get_event_loop() # Radio messages radio_messages = ["https://www.youtube.com/watch?v=3-yeK1Ck4yk", "https://youtu.be/YcR7du_A1Vc", "https://clyp.it/byg3i52l"] song_special_messages = { "despacito": ":arrow_forward: this is so sad. alexa play {song}", "faded": ":arrow_forward: Basta Garf, lasciami ascoltare {song}", "ligma": ":arrow_forward: What is ligma? {song}!", "sugma": ":arrow_forward: What is sugma? {song}!", "sugondese": ":arrow_forward: What is sugondese? {song}!", "bofa": ":arrow_forward: What is bofa? {song}!", "updog": ":arrow_forward: What's up, dog? {song}!", "sayo-nara": ":arrow_forward: I gently open the door. {song} awaits me inside.", "monika": ":arrow_forward: Just Monika. Just Monika. Just {song}.", "country road": ":arrow_forward: Take me home, to {song}, the place I belong!", "never gonna give you up": ":arrow_forward: Rickrolling in 2018. Enjoy {song}!", "september": ":arrow_forward: Do you remember? {song}.", "homestuck": ":arrow_forward: > Enter song name. {song}", "undertale": ":arrow_forward: Howdy! I'm Flowey! Listen to this friendly song: {song}", "pumped up kicks": ":arrow_forward: Non metterti mica in testa strane idee ascoltando {song}...", "jesus": ":arrow_forward: Respawn in 3 giorni. Intanto, ascolta {song}.", "through The fire And flames": ":arrow_forward: Fai {song} su osu!, se ne sei capace!", "slow clap": ":arrow_forward: :clap: :clap: :clap: {song} :clap: :clap: :clap:", "pub scrubs": ":arrow_forward: MAV COME BACK WE MISS {song}!", "alleluia": ":arrow_forward: Wah. Waaaah. Waluigi tiime: {song}", "wah": ":arrow_forward: Wah. Waaaah. Waluigi tiime: {song}", "waluigi": ":arrow_forward: Wah. Waaaah. Waluigi tiime: {song}", "nyan cat": ":arrow_forward: Meow! :3 {song}", "dragonborn": ":arrow_forward: FUS RO {song}!", "dovahkiin": ":arrow_forward: FUS RO {song}!", "initial d": ":arrow_forward: Guarda mamma sto driftando sul balcone di Balu grazie a {song}!", "persona": ":arrow_forward: You'll never see {song} comiiiiing!", "flamingo": ":arrow_forward: How many {song} do you have to eat?", "linkin park": ":arrow_forward: Crawling in my {song}!", "magicite": "⚠️ Warning: {song} contiene numerosi bug. E' ora in riproduzione.", "papers please": ":arrow_forward: Glory to Arstotzka! {song}!", "we are number one": ":arrow_forward: Now paying respect to Robbie Rotten: {song}", "jump up superstar": ":arrow_forward: Is {song} the Tengen Toppa Guren Lagann opening?", "the world revolving": ":arrow_forward: CHAOS! CHAOS! I CAN DO {song}!", "deltarune": ":arrow_forward: You hug Ralsei. A strange music starts playing: {song}", "song of unhealing": ":arrow_forward: BEN {song}", "police academy": ":arrow_forward: {song} - freedom.png", "super smash bros. ultimate": ":arrow_forward: Re-awaken the undying light with {song}!", "powerwolf": ":arrow_forward: Spaggia, ma non ti sei un po' stancato di {song}?", "eurobeat": ":arrow_forward: Nemesis approva la scelta di {song}. Ben fatto, amico." } # FFmpeg settings ffmpeg_settings = {} # Init the executor executor = concurrent.futures.ThreadPoolExecutor(max_workers=3) class Succ: """All calls to this class return itself.""" def __bool__(self): return False def __getattr__(self): return Succ() def __call__(self, *args, **kwargs): return Succ() def __str__(self): return "succ" def __repr__(self): return "" # Init the Sentry client if config.get("Sentry") and config["Sentry"].get("token"): sentry = raven.Client(config["Sentry"]["token"], release=raven.fetch_git_sha(os.path.dirname(__file__)), install_logging_hook=False, hook_libraries=[]) else: logger.warning("Sentry not set, ignoring all calls to it.") sentry = Succ() class OldVideo: """A video to be played in the bot.""" def __init__(self, url: str = None, file: str = None, info: dict = None, enqueuer: discord.Member = None): # Url of the video if it has to be downloaded self.url = url # Filename of the downloaded video if file is None and info is None: # Get it from the url hash self.file = str(hash(url)) + ".opus" elif info is not None: # Get it from the video title self.file = "./opusfiles/" + re.sub(r'[/\\?*"<>|!:]', "_", info["title"]) + ".opus" else: # The filename was explicitly passed self.file = file # Was the file already downloaded? self.downloaded = (file is not None) # Do we already have info on the video? self.info = info # Who added the video to the queue? self.enqueuer = enqueuer # How long and what title has the video? if info is not None: self.duration = info.get("duration") self.title = info.get("title") else: self.duration = None self.title = None # No audio source exists yet self.audio_source = None def __str__(self): """Format the title to be used on Discord using Markdown.""" if self.info is None or "title" not in self.info: return f"`{self.file}`" return f"_{self.info['title']}_" def __repr__(self): return f"" def plain_text(self): """Format the video title without any Markdown.""" if self.info is None or "title" not in self.info: return self.file return self.info['title'] def download(self, progress_hooks: typing.List["function"] = None): # File already downloaded if self.downloaded: raise errors.AlreadyDownloadedError() # No progress hooks if progress_hooks is None: progress_hooks = [] # Check if under max duration self.duration = datetime.timedelta(seconds=self.info.get("duration", 0)) # Refuse downloading if over YouTube max_duration if self.info is not None and self.duration.total_seconds() > self.max_duration: raise errors.DurationError() # Download the file logger.info(f"Downloading: {repr(self)}") with youtube_dl.YoutubeDL({"noplaylist": True, "format": "best", "postprocessors": [{ "key": 'FFmpegExtractAudio', "preferredcodec": 'opus' }], "outtmpl": self.file, "progress_hooks": progress_hooks, "quiet": True}) as ytdl: ytdl.download(self.url) logger.info(f"Download complete: {repr(self)}") self.downloaded = True def load(self) -> None: # Check if the file has been downloaded if not self.downloaded: raise errors.FileNotDownloadedError() self.audio_source = discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(f"{self.file}", **ffmpeg_settings)) def suggestion(self) -> typing.Optional[str]: """The suggested video to add to the queue after this one.""" raise NotImplementedError() class Video: def __init__(self, enqueuer: typing.Optional[discord.Member]=None): self.is_ready = False self.name = None self.enqueuer = enqueuer self.audio_source = None def __str__(self): return self.name def plain_text(self): """Title without formatting to be printed on terminals.""" return self.name def database_text(self): """The text to be stored in the database for the stats. Usually the same as plain_text().""" return self.name def __repr__(self): return f"