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