From fcc3635b9f24051ae07ed597632597e20372c889 Mon Sep 17 00:00:00 2001 From: SamlonDev Date: Tue, 20 May 2025 05:45:23 +0200 Subject: initial push --- .gitignore | 5 +++ cogs/compression.py | 88 +++++++++++++++++++++++++++++++++++++++++++ cogs/music/join.py | 13 +++++++ cogs/music/leave.py | 13 +++++++ cogs/music/loop.py | 22 +++++++++++ cogs/music/play.py | 53 ++++++++++++++++++++++++++ cogs/music/play_compressed.py | 88 +++++++++++++++++++++++++++++++++++++++++++ cogs/music/queue.py | 22 +++++++++++ cogs/music/skip.py | 47 +++++++++++++++++++++++ cogs/ping_commands.py | 60 +++++++++++++++++++++++++++++ cogs/tts.py | 47 +++++++++++++++++++++++ cogs/vc_logs.py | 83 ++++++++++++++++++++++++++++++++++++++++ main.py | 35 +++++++++++++++++ 13 files changed, 576 insertions(+) create mode 100644 .gitignore create mode 100644 cogs/compression.py create mode 100644 cogs/music/join.py create mode 100644 cogs/music/leave.py create mode 100644 cogs/music/loop.py create mode 100644 cogs/music/play.py create mode 100644 cogs/music/play_compressed.py create mode 100644 cogs/music/queue.py create mode 100644 cogs/music/skip.py create mode 100644 cogs/ping_commands.py create mode 100644 cogs/tts.py create mode 100644 cogs/vc_logs.py create mode 100644 main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..deb381b --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.env +__pycache__/ +temp/ +cogs/__pycache__ +cogs/music/__pycache__ \ No newline at end of file diff --git a/cogs/compression.py b/cogs/compression.py new file mode 100644 index 0000000..dfd96ae --- /dev/null +++ b/cogs/compression.py @@ -0,0 +1,88 @@ +import discord +from discord.ext import commands +import asyncio +import mimetypes +import os +import subprocess +import tempfile + +def filetype(filename): + mime_type, _ = mimetypes.guess_type(filename) + if mime_type: + if mime_type.startswith('video/'): + return "Video" + elif mime_type.startswith('audio/'): + return "Audio" + elif mime_type.startswith('image/'): + return "Image" + return "Unknown" + +class Compression(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.slash_command( + name="kompresja", + description="skompresuj wideło albo dźwienk :)", + ) + @discord.option( + "plik", + description="plik do skompresowania", + required=True, + type=discord.Attachment + ) + async def compression(self, ctx, plik): + await ctx.defer() + # Save files to temp directory inside the bot's root directory + bot_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + temp_dir = os.path.join(bot_dir, "temp") + if not os.path.exists(temp_dir): + os.makedirs(temp_dir) + file_path = os.path.join(temp_dir, plik.filename) + await plik.save(file_path) + + input_path = file_path + output_path = os.path.join(temp_dir, f"better_{plik.filename}") + file_type = filetype(file_path) + if file_type == "Video": + # Execute ffmpeg command to compress video + ffmpeg_command = f"ffmpeg -i {input_path} -c:v libx264 -crf 51 -b:v 100 -vf 'fps=10' -preset ultrafast -c:a libopus -b:a 500 {output_path}" + elif file_type == "Audio": + ffmpeg_command = f"ffmpeg -i {input_path} -ar 8000 -ac 1 -b:a 16k -acodec libmp3lame -q:a 9 {output_path}" + elif file_type == "Image": + # Use ImageMagick's magick to heavily compress image (JPEG/PNG/GIF) + # For JPEG: quality 5, strip metadata, resize to 30% of original size + # For PNG: magick to JPEG, then compress + # For GIF: optimize and reduce colors + ext = os.path.splitext(input_path)[1].lower() + if ext in [".jpg", ".jpeg"]: + img_command = f"magick {input_path} -strip -resize 30% -quality 5 {output_path}" + elif ext == ".gif": + # Optimize GIF: reduce colors, optimize layers, and resize + img_command = f"magick {input_path} -coalesce -resize 30% -colors 32 -strip -layers Optimize {output_path}" + else: + # magick non-JPEG images to JPEG for maximum compression + output_path = os.path.splitext(output_path)[0] + ".jpg" + img_command = f"magick {input_path} -strip -resize 30% -quality 5 {output_path}" + ffmpeg_command = img_command + else: + await ctx.respond("Plik jest nieobsługiwany", ephemeral=True) + return + + try: + subprocess.run(ffmpeg_command, check=True, shell=True) + # Upload compressed file + await ctx.respond(file=discord.File(output_path)) + # Delete compressed file + os.remove(input_path) + os.remove(output_path) + except subprocess.CalledProcessError as e: + await ctx.respond(f"Compression failed: {str(e)}", ephemeral=True) + if os.path.exists(input_path): + os.remove(input_path) + if os.path.exists(output_path): + os.remove(output_path) + + +def setup(bot): + bot.add_cog(Compression(bot)) \ No newline at end of file diff --git a/cogs/music/join.py b/cogs/music/join.py new file mode 100644 index 0000000..91a0b18 --- /dev/null +++ b/cogs/music/join.py @@ -0,0 +1,13 @@ +import discord +from discord.ext import commands + +class Join(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.slash_command(name="join", description="dołącza do kanalu") + async def join(self, ctx): + await ctx.voice_client.connect() + +def setup(bot): + bot.add_cog(Join(bot)) \ No newline at end of file diff --git a/cogs/music/leave.py b/cogs/music/leave.py new file mode 100644 index 0000000..0be2068 --- /dev/null +++ b/cogs/music/leave.py @@ -0,0 +1,13 @@ +import discord +from discord.ext import commands + +class Leave(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.slash_command(name="kick", description="kickuje bota z kanalu") + async def kick(self, ctx): + await ctx.voice_client.disconnect() + +def setup(bot): + bot.add_cog(Leave(bot)) \ No newline at end of file diff --git a/cogs/music/loop.py b/cogs/music/loop.py new file mode 100644 index 0000000..5f51c7e --- /dev/null +++ b/cogs/music/loop.py @@ -0,0 +1,22 @@ +from discord.ext import commands + +class Loop(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.looping = {} + + def is_looping(self, guild_id): + return self.looping.get(guild_id, False) + + def set_looping(self, guild_id, value): + self.looping[guild_id] = value + + @commands.slash_command(name="loop", description="włącz/wyłącz loopowanie dla bieżącej melodyji") + async def loop(self, ctx): + guild_id = ctx.guild.id + looping = not self.is_looping(guild_id) + self.set_looping(guild_id, looping) + await ctx.respond(f"looping jest teraz {'włączony' if looping else 'wyłączony'}.") + +def setup(bot): + bot.add_cog(Loop(bot)) \ No newline at end of file diff --git a/cogs/music/play.py b/cogs/music/play.py new file mode 100644 index 0000000..1eafbca --- /dev/null +++ b/cogs/music/play.py @@ -0,0 +1,53 @@ +import discord +from discord.ext import commands +import asyncio +from youtubesearchpython import VideosSearch + +class Play(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.slash_command(name="grajcuj", description="grajcuj melodyje") + async def play(self, ctx, *, search: str): + await ctx.defer() + if not (ctx.author.voice and ctx.author.voice.channel): + await ctx.followup.send("nie jesteś w kanale głosowym") + return + channel = ctx.author.voice.channel + if ctx.voice_client is not None: + vc = ctx.voice_client + await vc.move_to(channel) + else: + vc = await channel.connect() + videos_search = VideosSearch(search, limit=1) + result = videos_search.result() + if not result['result']: + await ctx.followup.send("nie znaleziono wideo dla twojego wyszukiwania.", ephemeral=True) + return + video = result['result'][0] + video_id = video['id'] + title = video['title'] + url = video['link'] + proc = await asyncio.create_subprocess_exec( + "yt-dlp", "-f", "bestaudio", "-g", "--no-cache-dir", url, + stdout=asyncio.subprocess.PIPE + ) + stdout, _ = await proc.communicate() + stream_url = stdout.decode().strip() + FFMPEG_OPTIONS = { + 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', + 'options': '-vn' + } + audio = discord.FFmpegPCMAudio( + stream_url, + before_options=FFMPEG_OPTIONS['before_options'], + options=FFMPEG_OPTIONS['options'] + ) + if not vc.is_playing(): + vc.play(audio) + await ctx.followup.send(f"gra: {title}") + else: + await ctx.followup.send(f"dodano do koleyjki: {title}") + +def setup(bot): + bot.add_cog(Play(bot)) \ No newline at end of file diff --git a/cogs/music/play_compressed.py b/cogs/music/play_compressed.py new file mode 100644 index 0000000..3297110 --- /dev/null +++ b/cogs/music/play_compressed.py @@ -0,0 +1,88 @@ +import discord +from discord.ext import commands +import asyncio +import os +import subprocess +from youtubesearchpython import VideosSearch + +class GrajcujZkompresowane(commands.Cog): + def __init__(self, bot): + self.bot = bot + + def prepend_title_to_file(self, file_path, title): + try: + with open(file_path, 'rb') as original: + data = original.read() + with open(file_path, 'wb') as modified: + modified.write(f"{title}\n".encode('utf-8')) + modified.write(data) + except Exception as e: + print(f"Failed to prepend title: {e}") + + def compress_audio(self, input_path): + base, ext = os.path.splitext(input_path) + output_path = f"{base}_compressed{ext}" + ffmpeg_command = f"ffmpeg -i {input_path} -ar 8000 -ac 1 -b:a 500 -acodec libmp3lame -q:a 9 {output_path}" + subprocess.run(ffmpeg_command, check=True, shell=True) + return output_path + + @commands.slash_command(name="grajcuj_zkompresowane", description="grajcuj zkompresowane melodyje") + async def grajcuj_zkompresowane(self, ctx, *, search: str): + await ctx.defer() + if not (ctx.author.voice and ctx.author.voice.channel): + await ctx.followup.send("nie jesteś w kanale głosowym") + return + channel = ctx.author.voice.channel + if ctx.voice_client is not None: + vc = ctx.voice_client + await vc.move_to(channel) + else: + vc = await channel.connect() + bot_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + music_dir = os.path.join(bot_dir, "temp", "music") + if not os.path.exists(music_dir): + os.makedirs(music_dir) + videos_search = VideosSearch(search, limit=1) + result = videos_search.result() + if not result['result']: + await ctx.followup.send("Could not find a video for your search.", ephemeral=True) + return + video = result['result'][0] + video_id = video['id'] + title = video['title'] + url = video['link'] + filename = f"{video_id}.mp3" + file_path = os.path.join(music_dir, filename) + compressed_file_path = os.path.join(music_dir, f"{video_id}_compressed.mp3") + if os.path.exists(compressed_file_path): + found_file = compressed_file_path + found_title = title + else: + if not os.path.exists(file_path): + cmd = [ + "yt-dlp", + "-x", + "--audio-format", "mp3", + "-o", file_path, + url + ] + process = await asyncio.create_subprocess_exec(*cmd) + await process.communicate() + self.prepend_title_to_file(file_path, title) + if not os.path.exists(compressed_file_path): + try: + compressed_file_path = self.compress_audio(file_path) + except subprocess.CalledProcessError as e: + await ctx.followup.send(f"skompresowanie nie powiodło się: {str(e)}", ephemeral=True) + return + found_file = compressed_file_path + found_title = title + title = found_title + if not vc.is_playing(): + vc.play(discord.FFmpegPCMAudio(found_file)) + await ctx.followup.send(f"gra (zkompresowane): {title}") + else: + await ctx.followup.send(f"dodano do koleyjki (zkompresowane): {title}") + +def setup(bot): + bot.add_cog(GrajcujZkompresowane(bot)) \ No newline at end of file diff --git a/cogs/music/queue.py b/cogs/music/queue.py new file mode 100644 index 0000000..29f0c36 --- /dev/null +++ b/cogs/music/queue.py @@ -0,0 +1,22 @@ +from discord.ext import commands + +class Queue(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.queues = {} + + def get_queue(self, guild_id): + return self.queues.setdefault(guild_id, []) + + @commands.slash_command(name="koleyjka", description="pokazuje koleyjke") + async def queue(self, ctx): + guild_id = ctx.guild.id + queue = self.get_queue(guild_id) + if queue: + queue_str = "\n".join(f"{i+1}. {title}" for i, (_, title, _) in enumerate(queue)) + await ctx.respond(f"koleyjka:\n{queue_str}") + else: + await ctx.respond("koleyjka jest pustawa") + +def setup(bot): + bot.add_cog(Queue(bot)) \ No newline at end of file diff --git a/cogs/music/skip.py b/cogs/music/skip.py new file mode 100644 index 0000000..f48218b --- /dev/null +++ b/cogs/music/skip.py @@ -0,0 +1,47 @@ +from discord.ext import commands +import discord + +class Skip(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.queues = {} + + def get_queue(self, guild_id): + return self.queues.setdefault(guild_id, []) + + def play_next(self, ctx, vc, guild_id): + queue = self.get_queue(guild_id) + if queue: + _, _, file_path = queue[0] + def after_playing(error=None): + if queue: + queue.pop(0) + if queue: + _, _, next_file = queue[0] + if not vc.is_playing(): + vc.play(discord.FFmpegPCMAudio(next_file), after=after_playing) + if not vc.is_playing(): + vc.play(discord.FFmpegPCMAudio(file_path), after=after_playing) + + @commands.slash_command(name="pomiń", description="przeskocz do nastepnej melodyji") + async def pomin(self, ctx): + guild_id = ctx.guild.id + vc = ctx.voice_client + if vc and vc.is_playing(): + queue = self.get_queue(guild_id) + if queue: + queue.pop(0) + if queue: + vc.stop() + self.play_next(ctx, vc, guild_id) + await ctx.respond("przeskoczono do nastepnej melodyji") + else: + await ctx.respond("koleyjka jest pustawa") + await vc.disconnect() + else: + await ctx.respond("nic nie gra") + else: + await ctx.respond("nic nie gra") + +def setup(bot): + bot.add_cog(Skip(bot)) \ No newline at end of file diff --git a/cogs/ping_commands.py b/cogs/ping_commands.py new file mode 100644 index 0000000..beb9685 --- /dev/null +++ b/cogs/ping_commands.py @@ -0,0 +1,60 @@ +import discord +from discord.ext import commands +import asyncio +from yt_dlp import VideosSearch + +class PingCommands(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.Cog.command() + async def vc(self, ctx): + # join voice channel + pass + + @commands.slash_command(name="grajcuj", description="grajcuj melodyje") + async def play(self, ctx, *, search: str): + await ctx.defer() + if not (ctx.author.voice and ctx.author.voice.channel): + await ctx.followup.send("nie jesteś w kanale głosowym") + return + channel = ctx.author.voice.channel + if ctx.voice_client is not None: + vc = ctx.voice_client + await vc.move_to(channel) + else: + vc = await channel.connect() + videos_search = VideosSearch(search, limit=1) + result = videos_search.result() + if not result['result']: + await ctx.followup.send("nie znaleziono wideo dla twojego wyszukiwania.", ephemeral=True) + return + video = result['result'][0] + title = video['title'] + url = video['link'] + + # Get direct audio stream URL + proc = await asyncio.create_subprocess_exec( + "yt-dlp", "-f", "bestaudio", "-g", url, + stdout=asyncio.subprocess.PIPE + ) + stdout, _ = await proc.communicate() + stream_url = stdout.decode().strip() + + ffmpeg_options = { + "before_options": "-probesize 32k -analyzeduration 0 -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5", + "options": "-vn" + } + audio = discord.FFmpegPCMAudio( + stream_url, + before_options=ffmpeg_options["before_options"], + options=ffmpeg_options["options"] + ) + if not vc.is_playing(): + vc.play(audio) + await ctx.followup.send(f"gra: {title}") + else: + await ctx.followup.send(f"dodano do koleyjki: {title}") + + + diff --git a/cogs/tts.py b/cogs/tts.py new file mode 100644 index 0000000..41dc366 --- /dev/null +++ b/cogs/tts.py @@ -0,0 +1,47 @@ +import discord +from discord.ext import commands +from gtts import gTTS +import os +import datetime + +class TTS(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.slash_command(name="tts", description="Generuje TTS dla danego tekstu") + async def tts(self, ctx, + text: str = discord.Option(str, "Tekst do przeczytania", required=True), + output: str = discord.Option(str, "Wybierz sposób odtwarzania", choices=["file", "vc"], required=True) + ): + tts = gTTS(text=text, lang='pl') + tts.save('tts.mp3') + if output == "file": + await ctx.respond(file=discord.File('tts.mp3')) + os.remove('tts.mp3') + elif output == "vc": + # Check if user is in a voice channel + if not (ctx.author.voice and ctx.author.voice.channel): + await ctx.respond("Musisz być na kanale głosowym, aby odtworzyć TTS!", ephemeral=True) + os.remove('tts.mp3') + return + channel = ctx.author.voice.channel + # Connect to voice channel if not already connected + voice_client = discord.utils.get(self.bot.voice_clients, guild=channel.guild) + if not voice_client or not voice_client.is_connected(): + voice_client = await channel.connect() + elif voice_client.channel != channel: + await voice_client.move_to(channel) + # Play the TTS audio + if not voice_client.is_playing(): + audio_source = discord.FFmpegPCMAudio('tts.mp3', options='-filter:a "volume=6"') + voice_client.play(audio_source) + # Wait for playback to finish + while voice_client.is_playing(): + await discord.utils.sleep_until(datetime.datetime.now() + datetime.timedelta(seconds=1)) + os.remove('tts.mp3') + else: + await ctx.respond("Nieznany sposób odtwarzania.", ephemeral=True) + os.remove('tts.mp3') + +def setup(bot): + bot.add_cog(TTS(bot)) \ No newline at end of file diff --git a/cogs/vc_logs.py b/cogs/vc_logs.py new file mode 100644 index 0000000..669be3b --- /dev/null +++ b/cogs/vc_logs.py @@ -0,0 +1,83 @@ +import discord +from discord.ext import commands +import datetime +from gtts import gTTS +import os + +class vc_logs(commands.Cog): + def __init__(self, bot): + self.bot = bot + + async def play_tts(self, channel, text): + # Generate TTS audio + tts = gTTS(text=text, lang='pl') + tts.save('temp_tts.mp3') + + # Connect to voice channel if not already connected + voice_client = discord.utils.get(self.bot.voice_clients, guild=channel.guild) + if not voice_client or not voice_client.is_connected(): + voice_client = await channel.connect() + elif voice_client.channel != channel: + await voice_client.move_to(channel) + + # Play the TTS audio + if not voice_client.is_playing(): + audio_source = discord.FFmpegPCMAudio('temp_tts.mp3', options='-filter:a "volume=6"') + voice_client.play(audio_source) + while voice_client.is_playing(): + await discord.utils.sleep_until(datetime.datetime.now() + datetime.timedelta(seconds=1)) + os.remove('temp_tts.mp3') + + @commands.Cog.listener() + async def on_voice_state_update(self, member, before, after): + log_channel = self.bot.get_channel(1213843195457183885) + current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + tts_text = None + tts_channel = None + # Ignore if user is a bot + if member.bot: + return + if before.channel is None and after.channel is not None: + message = f"[{current_time}] {member.name} dołączył na {after.channel.name}" + tts_text = f"{member.display_name} sie zespawnił" + tts_channel = after.channel + await log_channel.send(message) + print(message) + elif before.channel is not None and after.channel is None: + message = f"[{current_time}] {member.name} rozłączył się z {before.channel.name}" + tts_text = f"{member.display_name} jest chujem" + tts_channel = before.channel + await log_channel.send(message) + print(message) + + if before.self_deaf != after.self_deaf: + if after.self_deaf: + message = f"[{current_time}] {member.name} ogłuchnął się na {after.channel.name}" + tts_text = f"{member.display_name} jest bity przez ojca" + else: + message = f"[{current_time}] {member.name} odgłuchął się na {after.channel.name}" + tts_text = f"{member.display_name} uciek od ojca" + tts_channel = after.channel + await log_channel.send(message) + print(message) + if tts_text and tts_channel: + await self.play_tts(tts_channel, tts_text) + return + + if before.self_mute != after.self_mute: + if after.self_mute: + message = f"[{current_time}] {member.name} wyciszył się na {after.channel.name}" + tts_text = f"{member.display_name} idzie robić jajecznice" + else: + message = f"[{current_time}] {member.name} odciszył się na {after.channel.name}" + tts_text = f"{member.display_name} zrobił jajecznice" + tts_channel = after.channel + await log_channel.send(message) + print(message) + + if tts_text and tts_channel: + await self.play_tts(tts_channel, tts_text) + + +def setup(bot): + bot.add_cog(vc_logs(bot)) \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..52af124 --- /dev/null +++ b/main.py @@ -0,0 +1,35 @@ +import datetime +import os +from dotenv import load_dotenv +# create a pycord bot template +import discord +from discord.ext import commands + +bot = commands.Bot(intents=discord.Intents.all()) +@bot.event +async def on_ready(): + print(f"ŁUKASZ SPERMA AKTYWACJA" + datetime.datetime.now().strftime("(%Y-%m-%d %H:%M:%S)")) + # send message to channel ID + channel = bot.get_channel(1081667823799111804) + await channel.send("ŁUKASZ SPERMA AKTYWACJA") + +if __name__ == "__main__": + load_dotenv() + extensions = [ + "cogs.vc_logs", + "cogs.compression", + 'cogs.tts', + 'cogs.music.play', + 'cogs.music.play_compressed', + 'cogs.music.skip', + 'cogs.music.queue', + 'cogs.music.join', + 'cogs.music.leave', + ] + for i, extension in enumerate(extensions, 1): + bot.load_extension(extension) + print(f"Loaded {i}/{len(extensions)} extensions") + +# run bot +token = os.getenv("TOKEN") +bot.run(token) -- cgit v1.2.3