From de1174b558dd67782a8a6c101008716b80ce65c1 Mon Sep 17 00:00:00 2001 From: Coma <75559250+skk7414@users.noreply.github.com> Date: Tue, 25 Jan 2022 19:06:14 +0900 Subject: [PATCH] Add files via upload --- cogs/bottiket.py | 196 +++++++-- cogs/help.py | 412 ++++++++++++++++-- cogs/musci.py | 1087 ++++++++++++++++------------------------------ 3 files changed, 931 insertions(+), 764 deletions(-) diff --git a/cogs/bottiket.py b/cogs/bottiket.py index f4a0b07..585ca10 100644 --- a/cogs/bottiket.py +++ b/cogs/bottiket.py @@ -1,3 +1,4 @@ +from logging import PlaceHolder import discord from discord import embeds from discord.ext import commands @@ -5,8 +6,13 @@ import os from discord.ext.commands.core import command import pytz +import asyncio +import discord_components import datetime - +from pycord_components import ( + Select, + SelectOption +) ticket_guild_id = 915551354800451616 category_id = 915561810411814973 close_ticket_category_id = 915561835267231774 @@ -84,34 +90,140 @@ async def on_message(self, message: discord.Message): else: if message.author.bot: return - open_ticket_category = ticket_guild.get_channel(category_id) # 이건 왜 길드 아이디지 - new_ticket = await message.author.send( - embed = discord.Embed( - title=f'문의', - description =f"문의를 해주셔서 감사힙니다. \n 답변이 늦을수도 있으니 \n 기다려주세요.\n \n \n **:warning: 주의사항** \n \n `` 불필요한 문의는 제재 됩니다.`` \n \n ``관리자를 욕할시 처벌대상이 됩니다.`` \n \n ``관리자를 존중해주세요.``" , - colour = discord.Colour.blue(), - ).set_thumbnail(url="https://cdn.discordapp.com/avatars/872714206246469662/810ef9d933f9985d82f441de0a03fb6b.webp?size=80") + open_ticket_category = ticket_guild.get_channel(category_id) + embed1 = discord.Embed(title=f'문의',description =f"문의를 해주셔서 감사힙니다. \n 답변이 늦을수도 있으니 \n 기다려주세요. \n \n **문의한 모든 내용은 영구저장됩니다**" , + colour = discord.Colour.blue() ) - - ticket_channel = await open_ticket_category.create_text_channel( - message.author.dm_channel.id, - topic=f"{message.author.id}", - position = 1 + embed1.set_thumbnail(url="https://cdn.discordapp.com/avatars/915546504054333450/b26cea253b3433d2b84b7ec6b55b0a0e.webp?size=1024") + embed1.add_field(name="**:warning: 주의사항**", value="`` 불필요한 문의는 제재 됩니다.`` \n \n ``관리자를 욕할시 처벌대상이 됩니다.`` \n \n ``관리자를 존중해주세요.``") + embed = discord.Embed(title=f'문의',description =f"문의를 해주셔서 감사힙니다. \n 답변이 늦을수도 있으니 \n 기다려주세요. \n \n **문의한 모든 내용은 영구저장됩니다**" , + colour = discord.Colour.blue() ) - staff = ticket_guild.get_role(857395557793792020) # 이건 안넣냐 - await ticket_channel.set_permissions( - staff, - read_messages=True, - send_messages=True, - read_message_history=True, - ) - await ticket_channel.send( - embed=discord.Embed(description=message.content).set_author( - icon_url=ctx.author.avatar_url, - name=f"{ctx.author} ({ctx.author.id})", + embed.set_thumbnail(url="https://cdn.discordapp.com/avatars/915546504054333450/b26cea253b3433d2b84b7ec6b55b0a0e.webp?size=1024") + embed.add_field(name="**카테고리**", value="아래 카테고리를 선택해서 클릭해주세요.") + embed.add_field(name="🌀일반문의", value="일반문의는 봇의 대한 불만 사항또는 추가사항 \n 일반문의로 넣어주시면 됩니다") + embed.add_field(name="⛔오류제보", value="오류제보는 오류가 발생하거나 명령어 작동이 안될때 \n 오류제보로 넣어주시면 됩니다") + embed.add_field(name="❔궁금증", value="궁금증은 명령어를 어떻게 사용하는지 등 궁금할때 \n 궁금증으로 넣어주시면 됩니다") + embed.add_field(name="🚫신고", value="버그악용등 신고할때 \n 신고로 넣어주시면 됩니다") + await message.author.send(embed=embed1) + new_ticket = await message.author.send(embed=embed, components = [ + Select(placeholder="문의", + options=[ + SelectOption(label = "일반문의", emoji="🌀",description="봇의 대한 불만 사항또는 추가사항", value="il"), + SelectOption(label = "오류제보", emoji="⛔",description="봇에게 출력되는 오류 메시지", value="war1"), + SelectOption(label = "궁금증", emoji="❔",description="봇의 궁금증", value="qu"), + SelectOption(label = "신고", emoji="🚫",description="신고", value="si"), + SelectOption(label = "문의취소", emoji = "❌",description="문의 취소", value = "cancel"), + ]) + ]) + il1= discord.Embed(title="문의", colour=discord.Colour.random()) + il1.add_field(name="일반문의", value="문의 카테고리가 **일반 문의**로 되었습니다") + war11= discord.Embed(title="문의", colour=discord.Colour.random()) + war11.add_field(name="오류제보", value="문의 카테고리가 **오류 제보**로 되었습니다") + qu1= discord.Embed(title="문의", colour=discord.Colour.random()) + qu1.add_field(name="궁금증", value="문의 카테고리가 **궁금증**으로 되었습니다") + si1= discord.Embed(title="문의", colour=discord.Colour.random()) + si1.add_field(name="신고", value="문의 카테고리가 **신고**로 되었습니다") + cancle1= discord.Embed(title="문의", colour=discord.Colour.random()) + cancle1.add_field(name="취소", value="문의가 정삭적으로 취소 되었습니다") + try: + interaction = await self.bot.wait_for("select_option", + check=lambda i: i.user.id == ctx.author.id and i.message.id == new_ticket.id, + timeout=60) + value = interaction.values[0] + except asyncio.TimeoutError: + await new_ticket.edit("시간이 초과되었어요!", components=[]) + return + if value == "il": + ticket_channel = await open_ticket_category.create_text_channel( + f'일반-{message.author.dm_channel.id}({message.author.name})', + topic=f"{message.author.id}", + position = 1 ) - ) - await message.add_reaction("✅") + await ticket_channel.send('<@866297659362246706>') + staff = ticket_guild.get_role(922067926247415848) # 이건 안넣냐 + await ticket_channel.set_permissions( + staff, + read_messages=True, + send_messages=True, + read_message_history=True, + ) + await ticket_channel.send( + embed=discord.Embed(description=message.content).set_author( + icon_url=ctx.author.avatar_url, + name=f"{ctx.author} ({ctx.author.id})", + ) + ) + await message.add_reaction("✅") + await new_ticket.edit(embed=il1, components=[]) + if value == "war1": + ticket_channel = await open_ticket_category.create_text_channel( + f'오류-{message.author.dm_channel.id}({message.author.name})', + topic=f"{message.author.id}", + position = 1 + )# 이건 안넣냐 + await ticket_channel.send('<@866297659362246706>') + staff = ticket_guild.get_role(922067926247415848) # 이건 안넣냐 + await ticket_channel.set_permissions( + staff, + read_messages=True, + send_messages=True, + read_message_history=True, + ) + await ticket_channel.send( + embed=discord.Embed(description=message.content).set_author( + icon_url=ctx.author.avatar_url, + name=f"{ctx.author} ({ctx.author.id})", + ) + ) + await message.add_reaction("✅") + await new_ticket.edit(embed=war11, components=[]) + if value == "qu": + ticket_channel = await open_ticket_category.create_text_channel( + f'궁금증-{message.author.dm_channel.id}({message.author.name})', + topic=f"{message.author.id}", + position = 1 + )# 이건 안넣냐 + await ticket_channel.send('<@866297659362246706>') + staff = ticket_guild.get_role(922067926247415848) # 이건 안넣냐 + await ticket_channel.set_permissions( + staff, + read_messages=True, + send_messages=True, + read_message_history=True, + ) + await ticket_channel.send( + embed=discord.Embed(description=message.content).set_author( + icon_url=ctx.author.avatar_url, + name=f"{ctx.author} ({ctx.author.id})", + ) + ) + await message.add_reaction("✅") + await new_ticket.edit(embed=qu1, components=[]) + if value == "si": + ticket_channel = await open_ticket_category.create_text_channel( + f'신고-{message.author.dm_channel.id}({message.author.name})', + topic=f"{message.author.id}", + position = 1 + )# 이건 안넣냐 + await ticket_channel.send('<@866297659362246706>') + staff = ticket_guild.get_role(922067926247415848) # 이건 안넣냐 + await ticket_channel.set_permissions( + staff, + read_messages=True, + send_messages=True, + read_message_history=True, + ) + await ticket_channel.send( + embed=discord.Embed(description=message.content).set_author( + icon_url=ctx.author.avatar_url, + name=f"{ctx.author} ({ctx.author.id})", + ) + ) + await message.add_reaction("✅") + await new_ticket.edit(embed=si1, components=[]) + if value == "cancle": + await new_ticket.edit(embed=cancle1, components=[]) elif str(message.channel.type) != "private": try: if message.channel.category.id == category_id: @@ -125,6 +237,32 @@ async def on_message(self, message: discord.Message): await message.add_reaction("✅") except: pass + # @commands.command(name="티켓오픈") + # @commands.is_owner() + # async def on_message(self, ctx, user_id:int): + # user = await self.bot.fetch_user(user_id) + # ticket_guild = self.bot.get_guild(ticket_guild_id) + # open_ticket_category = ticket_guild.get_channel(category_id) + # ticket_channel = await open_ticket_category.create_text_channel( + # f'일반-{user.dm_channel.id}{user.name}', + # topic=f"{user_id}", + # position = 1 + # ) + # staff = ticket_guild.get_role(922067926247415848) # 이건 안넣냐 + # await ticket_channel.set_permissions( + # staff, + # read_messages=True, + # send_messages=True, + # read_message_history=True, + # ) + # await (await self.bot.fetch_user(int(ctx.channel.topic))).send( + # embed = discord.Embed(title=f'문의', + # description ="안녕하세요 봇개발자로 부터 티켓이 열렸습니다.", + # colour = discord.Colour.blue(), + # ).set_thumbnail(url="https://cdn.discordapp.com/avatars/915546504054333450/b26cea253b3433d2b84b7ec6b55b0a0e.webp?size=1024") + + # ) + # await ctx.send("티켓오픈!") @commands.command(name="문의종료", aliases=["종료", "close"]) @commands.has_permissions(administrator=True) async def ticket_end(self, ctx): @@ -132,10 +270,10 @@ async def ticket_end(self, ctx): ticket_guild = self.bot.get_guild(ticket_guild_id) ticket_channel = self.bot.get_channel(ctx.channel.id) await (await self.bot.fetch_user(int(ctx.channel.topic))).send( - embed = discord.Embed(title=f'문의', - description =f"**문의종료**\n 문의를 종료하겠습니다 문의를 해주셔서 감사합니다.", + embed = discord.Embed(title=f'문의종료', + description =f"문의를 해주셔서 감사합니다! \n**더욱 더 성장있는 짱구가 되겠습니다** \n**문의한 내용들은 영구적으로 보관되며 삭제가 불가능합니다** \n \n 감사합니다!", colour = discord.Colour.blue(), - ).set_thumbnail(url="https://cdn.discordapp.com/avatars/872714206246469662/810ef9d933f9985d82f441de0a03fb6b.webp?size=80") + ).set_thumbnail(url="https://cdn.discordapp.com/avatars/915546504054333450/b26cea253b3433d2b84b7ec6b55b0a0e.webp?size=1024") ) await ctx.channel.edit( diff --git a/cogs/help.py b/cogs/help.py index ce9d719..8baa39b 100644 --- a/cogs/help.py +++ b/cogs/help.py @@ -6,7 +6,6 @@ import discordSuperUtils from discord.ext import commands import aiosqlite -from cogs.util import util # 1️⃣ 키캡 디지트 원 # 2️⃣ 키캡 숫자 2 # 3️⃣ 키캡 숫자 3 @@ -76,19 +75,26 @@ async def pagination(self, ctx): 4️⃣|4. 게임페이지 🕹️ 5️⃣|5. 음악 🎵 6️⃣|6. 도박 💴 +7️⃣|7. 생일 🎉 +8️⃣|8. 학교검색 🏫 +9️⃣|9. 방송 🎥 ``문의는 봇DM으로 해주시면 감사합니다!`` [서포트서버](https://discord.gg/294KSUxcz2) [짱구봇 초대](https://discord.com/api/oauth2/authorize?client_id=915546504054333450&permissions=8&scope=bot) -옵션&생일&입장메시지&레벨링&초대정보등의 코드는 팀에서 개발된 하린봇의 코드를 사용했음을 알려드립니다. +옵션&생일&입장메시지&레벨링&학교검색&방송&프리미엄&검색&애니&초대정보등의 코드는 팀에서 개발된 하린봇의 코드를 사용했음을 알려드립니다. +뮤직기능은 Wavelink기본 코드를 사용했음을 알려드립니다 +[Wavelink](https://github.com/PythonistaGuild/Wavelink) [하린봇깃헙](https://github.com/spacedev-official/harin) ``짱구야 하트인증`` 한번씩해주세요! """, colour=discord.Colour.random() ) - main.set_footer(text=f"1 / 6페이지",icon_url=ctx.author.avatar_url) + main.set_thumbnail(url=self.bot.user.avatar_url) + main.set_image(url="https://media.discordapp.net/attachments/921555509935480853/921555519578189834/c265877614d80026.png") + main.set_footer(text=f"1 / 9페이지",icon_url=ctx.author.avatar_url) manage = discord.Embed( @@ -124,7 +130,10 @@ async def pagination(self, ctx): manage.add_field(name="짱구야 티켓설정 [#티켓채널] [@지원팀역할] [티켓안내내용]", value="```\n티켓을 설정해서 문의를 받아보세요!\n```", inline=False) - manage.set_footer(text=f"2 / 6페이지",icon_url=ctx.author.avatar_url) + manage.add_field(name="짱구야 뮤직셋업", + value="```\n이 기능은 프리미엄기능이며 프리미엄을 \n구매하셔야 사용가능합니다 \n구매는 짱구봇 DM 일반문의로 넣어주세요\n```", + inline=False) + manage.set_footer(text=f"2 / 9페이지",icon_url=ctx.author.avatar_url) utili = discord.Embed( @@ -160,11 +169,50 @@ async def pagination(self, ctx): utili.add_field(name="짱구야 초대정보 @유저", value="```\n지정한 유저 혹은 자신의 초대정보를 보여줘요.\n```", inline=False) + utili.add_field(name="짱구야 봇정보", + value="```\n짱구봇 정보를 알려줘요\n```", + inline=False) utili.add_field(name="짱구야 옵션", value="```\n여러 기능을 설정할 수 있는 명령어에요!\n```", inline=False) - - utili.set_footer(text=f"3 / 6페이지",icon_url=ctx.author.avatar_url) + utili.add_field(name="짱구야 애니 검색 [제목]", + value="```\n애니를 검색해 보세요!\n```", + inline=False) + utili.add_field(name="짱구야 애니 댓글달기 [댓글내용]", + value="```\n애니 검색결과 메세지에 답장형태로 사용하여 댓글을 남겨요. \n부적절한 댓글은 무통보삭제가 되요.\n```", + inline=False) + utili.add_field(name="짱구야 애니 댓글수정 [댓글내용]", + value="```\n애니 검색결과 메세지에 답장형태로 사용하여 댓글을 수정해요. \n부적절한 댓글은 무통보삭제가 되요.\n```", + inline=False) + utili.add_field(name="짱구야 애니 댓글삭제", + value="```\n애니 검색결과 메세지에 답장형태로 사용하여 남긴 댓글을 삭제해요. \n부적절한 댓글은 무통보삭제가 되요.\n```", + inline=False) + utili.add_field( + name="짱구야 메일 (전체)", + value="```\n전체 옵션을 사용하지않으면 수신된 메일을 보여주고 사용하면 모든 메일을 볼 수 있어요!\n```", + inline=False + ) + utili.add_field( + name="짱구야 영화검색", + value="```\n요번에 나온 영화를 검색해 보세요!\n```", + inline=False + ) + utili.add_field( + name="짱구야 뉴스검색", + value="```\n디스코드로 뉴스를 검색해 보세요!\n```", + inline=False + ) + utili.add_field( + name="짱구야 카페검색", + value="```\n네이버 카페를 검색해 보세요!\n```", + inline=False + ) + utili.add_field( + name="짱구야 웹검색", + value="```\n네이버등 웹사이트를 검색해 보세요!```", + inline=False + ) + utili.set_footer(text=f"3 / 9페이지",icon_url=ctx.author.avatar_url) games=discord.Embed( title="게임 🕹️", @@ -174,19 +222,19 @@ async def pagination(self, ctx): """, colour=discord.Colour.random() ) - games.add_field(name="천상아 가위바위보", + games.add_field(name="짱구야 가위바위보", value="```\n가위바위보 게임\n```", inline=False) - games.add_field(name="천상아 주사위", + games.add_field(name="짱구야 주사위", value="```\n주사위를 돌려 누가 많이 나오는지 \n 내기를 해보세요!\n```", inline=False) - games.set_footer(text=f"4 / 6페이지",icon_url=ctx.author.avatar_url) + games.set_footer(text=f"4 / 9페이지",icon_url=ctx.author.avatar_url) - music=discord.Embed( - title="음악 🎵", + music = discord.Embed( + title="뮤직 🎶", description=""" -노래명령어를 사용해보세요! - """, + 이곳에서 노래 관련 명령어를 확인해보세요! + """, colour=discord.Colour.random() ) music.add_field( @@ -195,23 +243,23 @@ async def pagination(self, ctx): inline=False ) music.add_field( - name="짱구야 재생 [인지값]", + name="짱구야 재생 인자값", value="```\n입력한 인자값(제목 또는 링크)을 불러와 재생해요.\n```", inline=False ) music.add_field( - name="짱구야 일시정지", - value="```\n현재 재생중인 곡을 일시정지해요\n```", + name="짱구야 지금곡", + value="```\n현재 재생중인 노래의 정보를 불러와요.\n```", inline=False ) music.add_field( - name="짱구야 현재곡", - value="```\n현재 재생중인 노래의 정보를 불러와요.\n```", + name="짱구야 일시정지", + value="```\n현재 재생중인 곡을 일시정지해요.\n```", inline=False ) music.add_field( - name="짱구야 스킵", - value="```\n현재 재생중인 곡을 스킵해요\n```", + name="짱구야 이어재생", + value="```\n일시정지된 곡을 이어서 재생해요.\n```", inline=False ) music.add_field( @@ -220,8 +268,38 @@ async def pagination(self, ctx): inline=False ) music.add_field( - name="짱구야 재생목록", - value="```\n재생목록을 확인해요\n```", + name="짱구야 루프", + value="```\n반복기능을 활성화하거나 비활성화해요.\n```", + inline=False + ) + music.add_field( + name="짱구야 큐루프", + value="```\n큐반복기능을 활성화하거나 비활성화해요.\n```", + inline=False + ) + music.add_field( + name="짱구야 노래기록", + value="```\n지금까지 재생됐던 노래기록을 불러와요.\n```", + inline=False + ) + music.add_field( + name="짱구야 정지", + value="```\n현재 재생중인 곡을 완전히 정지해요.\n```", + inline=False + ) + music.add_field( + name="짱구야 스킵", + value="```\n현재 재생중인 곡을 스킵하거나 요청해요.\n```", + inline=False + ) + music.add_field( + name="짱구야 큐", + value="```\n현재 대기중인 큐목록을 보여줘요.\n```", + inline=False + ) + music.add_field( + name="짱구야 반복확인", + value="```\n현재 설정된 반복상태를 보여줘요.\n```", inline=False ) music.add_field( @@ -229,7 +307,17 @@ async def pagination(self, ctx): value="```\n셔플기능을 활성화하거나 비활성화해요.\n```", inline=False ) - music.set_footer(text=f"5 / 6페이지",icon_url=ctx.author.avatar_url) + music.add_field( + name="짱구야 이전곡", + value="```\n이전곡을 재생해요.\n```", + inline=False + ) + music.add_field( + name="짱구야 나가", + value="```\n현재 접속한 음성채널에서 노래를 멈추고 나가요.\n```", + inline=False + ) + music.set_footer(text=f"5 / 9페이지",icon_url=ctx.author.avatar_url) eco=discord.Embed( title="도박 💴", description=""" @@ -240,15 +328,15 @@ async def pagination(self, ctx): eco.add_field(name="짱구야 가입", value="```\n도박시스템에 가입을합니다.\n```", inline=False) - eco.add_field(name="짱구야 인벤", + eco.add_field(name="짱구야 돈확인", value="```\n도박시스템에 인벤토리를 확인합니다.\n```", inline=False) - eco.add_field(name="짱구야 구입 [이름]", - value="```\n상품을 구입을 가능합니다.\n```", - inline=False) - eco.add_field(name="짱구야 목록", - value="```\n상품 목록을 확인합니다!\n```", + eco.add_field(name="짱구야 입금 [돈]", + value="```\n통장에 돈을 넣으세요!\n```", inline=False) + eco.add_field(name="짱구야 출금 [돈]", + value="```\n통장에 돈을 빼세요!\n```", + inline=False) eco.add_field(name="짱구야 송금 [유저맨션] [돈]", value="```\n맨션된 유저한테 돈을 보냅니다.\n```", inline=False) @@ -258,7 +346,122 @@ async def pagination(self, ctx): eco.add_field(name="짱구야 도박 [돈]", value="```\n도박을 해서 돈을 벌어보세요!\n```", inline=False) - eco.set_footer(text=f"6 / 6페이지",icon_url=ctx.author.avatar_url) + eco.add_field(name="짱구야 탈퇴 [돈]", + value="```\n도박을 해서 돈을 벌어보세요!\n```", + inline=False) + eco.set_footer(text=f"6 / 9페이지",icon_url=ctx.author.avatar_url) + birthday = discord.Embed( + title="생일 🎉", + description=""" + 이곳에서 생일 관련 명령어를 확인해보세요! + """, + colour=discord.Colour.random() + ) + birthday.add_field( + name="짱구야 생일등록", + value="```\n자신의 생일을 등록해요.\n```", + inline=False + ) + birthday.add_field( + name="짱구야 생일삭제", + value="```\n등록된 자신의 생일을 삭제해요.\n```", + inline=False + ) + birthday.add_field( + name="짱구야 생일 @유저맨션", + value="```\n자신 혹은 지정한 유저의 생일을 조회해요.\n```", + inline=False + ) + birthday.add_field( + name="짱구야 생일목록", + value="```\n현재서버에 등록된 멤버들의 생일을 보여줘요.\n```", + inline=False + ) + birthday.set_footer(text=f"7 / 9페이지",icon_url=ctx.author.avatar_url) + birthday = discord.Embed( + title="생일 🎉", + description=""" + 이곳에서 생일 관련 명령어를 확인해보세요! + """, + colour=discord.Colour.random() + ) + birthday.add_field( + name="짱구야 생일등록", + value="```\n자신의 생일을 등록해요.\n```", + inline=False + ) + birthday.add_field( + name="짱구야 생일삭제", + value="```\n등록된 자신의 생일을 삭제해요.\n```", + inline=False + ) + birthday.add_field( + name="짱구야 생일 (@user)", + value="```\n자신 혹은 지정한 유저의 생일을 조회해요.\n```", + inline=False + ) + birthday.add_field( + name="짱구야 생일목록", + value="```\n현재길드에 등록된 멤버들의 생일을 보여줘요.\n```", + inline=False + ) + birthday.set_footer(text=f"5 / 8페이지",icon_url=ctx.author.avatar_url) + + school = discord.Embed( + title="학교검색 🏫", + description=""" + 이곳에서 학교검색 관련 명령어를 확인해보세요! + """, + colour=discord.Colour.random() + ) + school.add_field( + name="짱구야 학교검색 학교명", + value="```\n학교의 정보를 조회해볼 수 있는 명령어에요!\n```", + inline=False + ) + school.add_field( + name="짱구야 학교검색 급식 학교명", + value="```\n학교급식을 조회해볼 수 있는 명령어에요!\n```", + inline=False + ) + school.set_footer(text=f"8 / 9페이지",icon_url=ctx.author.avatar_url) + + broadcast = discord.Embed( + title="방송 🎥", + description="트위치/유튜브 알림과 검색에 관련한 명령어를 확인해보세요.\n트위치/유튜브 알림 채널은 무료플랜은 1개, 프리미엄플랜은 5개까지 등록가능합니다.", + colour=discord.Colour.random() + ) + broadcast.add_field( + name="짱구야 트위치", + value="```\n트위치 스트리밍 알림 서비스에 등록된 채널 목록을 보여드려요.\n```", + inline=False + ) + broadcast.add_field( + name="짱구야 트위치 등록 @알림역할 #알림채널 [유저ID]", + value="```\n트위치 스트리밍 알림 서비스에 등록해요.\n```", + inline=False + ) + broadcast.add_field( + name="짱구야 트위치 해제", + value="```\n트위치 스트리밍 알림 서비스에서 해제해요.\n```", + inline=False + ) + broadcast.add_field( + name="짱구야 유튜브 [채널이름]", + value="```\n입력한 채널이름으로 검색해요.\n```", + inline=False + ) + broadcast.add_field( + name="짱구야 유튜브 등록 @알림역할 #알림채널 [채널ID]", + value="```\n유튜브 업로드 알림 서비스에 등록해요.\n```", + inline=False + ) + broadcast.add_field( + name="짱구야 유튜브 해제", + value="```\n유튜브 업로드 알림 서비스에서 해제해요.\n```", + inline=False + ) + broadcast.set_footer(text="9 / 9페이지", icon_url=ctx.author.avatar_url) desc = { "메인 페이지": "메뉴가 있는 메인페이지", "서버 관리 🔰": "서버 관리 명령어가 있는 페이지.", @@ -266,9 +469,12 @@ async def pagination(self, ctx): "게임 🕹️":"게임 명령어가 있는 페이지", "음악 🎵":"음악 명령어가 있는 페이지", "도박 💴":"도박 명령어가 있는 페이지", + "생일 🎉": "생일 명령어가 있는 페이지", + "학교검색 🏫":"트위치/유튜브 명령어가 있는페이지", + "방송 🎥":"트위치/유튜브 명령어가 있는페이지", } - embeds = [main,manage,utili,games,music,eco] + embeds = [main,manage,utili,games,music,eco,birthday,broadcast,school] e = Paginator( client=self.bot.components_manager, embeds=embeds, @@ -278,6 +484,150 @@ async def pagination(self, ctx): use_select=True, desc=desc) await e.start() + + @commands.command(name="메일", help="`짱구야 메일 (전체)`로 메일을 확인합니다.") + async def read_mail(self, ctx, mode=None): + if mode is None: + dictcommand = await self.read_email_from_db(ctx=ctx) + database = dictcommand["database"] + contents = dictcommand["contents"] + cur = dictcommand["cur"] + uncheck_cur = dictcommand["uncheck_cur"] + timess = dictcommand["timess"] + pages = dictcommand["pages"] + check2 = await cur.fetchone() + uncheck_cur_fetchone = await uncheck_cur.fetchone() + if uncheck_cur_fetchone is None: + await database.execute("INSERT INTO uncheck VALUES (?, ?)", (ctx.author.id, str(pages))) + await database.commit() + mal = discord.Embed(title=f"📫짱구의 메일함 | {str(pages)}개 수신됨", + description="주기적으로 메일함을 확인해주세요! 소소한 업데이트 및 이벤트개최등 여러소식을 확인해보세요.", + colour=ctx.author.colour) + cur_page = 1 + else: + if str(pages) == str(uncheck_cur_fetchone[1]): + mal = discord.Embed(title=f"📫짱구의 메일함 | 수신된 메일이 없어요.", + description="주기적으로 메일함을 확인해주세요! 소소한 업데이트 및 이벤트개최등 여러소식을 확인해보세요.", + colour=ctx.author.colour) + mal.add_field(name="📭빈 메일함", value="✅모든 메일을 읽으셨어요. 전체메일을 보고싶으시면 `짱구야 메일 전체`를 입력하세요.") + return await ctx.send(embed=mal) + await database.execute("UPDATE uncheck SET check_s = ? WHERE user_id = ?", + (str(pages), ctx.author.id)) + await database.commit() + mal = discord.Embed(title=f"📫짱구의 메일함 | {pages - int(uncheck_cur_fetchone[1])}개 수신됨", + description="주기적으로 메일함을 확인해주세요! 소소한 업데이트 및 이벤트개최등 여러소식을 확인해보세요.", + colour=ctx.author.colour) + cur_page = int(uncheck_cur_fetchone[1]) + # noinspection DuplicatedCode + mal.add_field(name=f"{pages}중 {cur_page}번째 메일({timess[contents[cur_page - 1]]}작성)", + value=contents[cur_page - 1]) + message = await ctx.send(embed=mal) + # getting the message object for editing and reacting + + await message.add_reaction("◀️") + await message.add_reaction("▶️") + + def check(reaction, user): + return user == ctx.author and str(reaction.emoji) in ["◀️", "▶️"] and reaction.message.id == message.id + + while True: + try: + reaction, user = await self.bot.wait_for("reaction_add", timeout=60, check=check) + # waiting for a reaction to be added - times out after x seconds, 60 in this + # example + + if str(reaction.emoji) == "▶️" and cur_page != pages: + if check2 is None: + cur_page += 1 + mal = discord.Embed(title=f"📫짱구의 메일함 | {str(pages)}개 수신됨", + description="주기적으로 메일함을 확인해주세요! 소소한 업데이트 및 이벤트개최등 여러소식을 확인해보세요.", + colour=ctx.author.colour) + mal.add_field(name=f"{pages}중 {cur_page}번째 메일", value=contents[cur_page - 1]) + else: + cur_page += 1 + mal = discord.Embed(title=f"📫짱구의 메일함 | {pages - int(uncheck_cur_fetchone[1])}개 수신됨", + description="주기적으로 메일함을 확인해주세요! 소소한 업데이트 및 이벤트개최등 여러소식을 확인해보세요.", + colour=ctx.author.colour) + mal.add_field(name=f"{pages}중 {cur_page}번째 메일({timess[contents[cur_page - 1]]}작성)", + value=contents[cur_page - 1]) + await message.edit(embed=mal) + + elif str(reaction.emoji) == "◀️" and cur_page > 1: + if check2 is None: + cur_page -= 1 + mal = discord.Embed(title=f"📫짱구의 메일함 | {str(pages)}개 수신됨", + description="주기적으로 메일함을 확인해주세요! 소소한 업데이트 및 이벤트개최등 여러소식을 확인해보세요.", + colour=ctx.author.colour) + mal.add_field(name=f"{pages}중 {cur_page}번째 메일", value=contents[cur_page - 1]) + else: + cur_page -= 1 + mal = discord.Embed(title=f"📫짱구의 메일함 | {pages - int(uncheck_cur_fetchone[1])}개 수신됨", + description="주기적으로 메일함을 확인해주세요! 소소한 업데이트 및 이벤트개최등 여러소식을 확인해보세요.", + colour=ctx.author.colour) + mal.add_field(name=f"{pages}중 {cur_page}번째 메일({timess[contents[cur_page - 1]]}작성)", + value=contents[cur_page - 1]) + await message.edit(embed=mal) + except asyncio.TimeoutError: + break + elif mode == "전체": + dictcommand = await self.read_email_from_db(ctx=ctx) + contents = dictcommand["contents"] + timess = dictcommand["timess"] + pages = dictcommand["pages"] + mal = discord.Embed(title=f"📫짱구의 메일함", + description="주기적으로 메일함을 확인해주세요! 소소한 업데이트 및 이벤트개최등 여러소식을 확인해보세요.", + colour=ctx.author.colour) + cur_page = 1 + # noinspection DuplicatedCode + mal.add_field(name=f"{pages}중 {cur_page}번째 메일({timess[contents[cur_page - 1]]}작성)", + value=contents[cur_page - 1]) + message = await ctx.send(embed=mal) + + await message.add_reaction("◀️") + await message.add_reaction("▶️") + + def check(reaction, user): + return user == ctx.author and str(reaction.emoji) in ["◀️", "▶️"] and reaction.message.id == message.id + # This makes sure nobody except the command sender can interact with the "menu" + + while True: + try: + reaction, user = await self.bot.wait_for("reaction_add", timeout=60, check=check) + # waiting for a reaction to be added - times out after x seconds, 60 in this + # example + + if str(reaction.emoji) == "▶️" and cur_page != pages: + cur_page += 1 + mal = discord.Embed(title=f"📫짱구의 메일함", + description="주기적으로 메일함을 확인해주세요! 소소한 업데이트 및 이벤트개최등 여러소식을 확인해보세요.", + colour=ctx.author.colour) + mal.add_field(name=f"{pages}중 {cur_page}번째 메일({timess[contents[cur_page - 1]]}작성)", + value=contents[cur_page - 1]) + await message.edit(embed=mal) + + elif str(reaction.emoji) == "◀️" and cur_page > 1: + cur_page -= 1 + mal = discord.Embed(title=f"📫짱구의 메일함", + description="주기적으로 메일함을 확인해주세요! 소소한 업데이트 및 이벤트개최등 여러소식을 확인해보세요.", + colour=ctx.author.colour) + mal.add_field(name=f"{pages}중 {cur_page}번째 메일({timess[contents[cur_page - 1]]}작성)", + value=contents[cur_page - 1]) + await message.edit(embed=mal) + except asyncio.TimeoutError: + break + @staticmethod + async def read_email_from_db(ctx): + contents = [] + timess = {} + database = await aiosqlite.connect("db/db.sqlite") + cur = await database.execute('SELECT * FROM mail') + uncheck_cur = await database.execute('SELECT * FROM uncheck WHERE user_id = ?',(ctx.author.id,)) + mails = await cur.fetchall() + for i in mails: + contents.append(i[1]) + timess[i[1]] = i[2] + pages = len(contents) + return {"contents": contents, "timess": timess, "database": database, "cur": cur, "uncheck_cur":uncheck_cur, "pages": pages} def setup(bot): bot.add_cog(help(bot)) \ No newline at end of file diff --git a/cogs/musci.py b/cogs/musci.py index c93f3dd..db03926 100644 --- a/cogs/musci.py +++ b/cogs/musci.py @@ -1,763 +1,442 @@ -import asyncio -import queue -import async_timeout -import copy import datetime -from click import CommandCollection, command -import discord -import math -import random -import re -import traceback -import typing -import wavelink -import aiosqlite -from discord.ext import commands, menus -from pycord_components import ( - Button, - ButtonStyle, - Interaction -) -# URL matching REGEX... -URL_REG = re.compile(r'https?://(?:www\.)?.+') - - -class NoChannelProvided(commands.CommandError): - """Error raised when no suitable voice channel was supplied.""" - pass - +import time +from typing import Optional -class IncorrectChannelError(commands.CommandError): - """Error raised when commands are issued outside of the players session channel.""" - pass - - -class Track(wavelink.Track): - """Wavelink Track object with a requester attribute.""" - - __slots__ = ('requester', ) +import aiosqlite +import discord +import discordSuperUtils +from discord.ext import commands +from discordSuperUtils import MusicManager - def __init__(self, *args, **kwargs): - super().__init__(*args) - self.requester = kwargs.get('requester') +# Format duration +def parse_duration(duration: Optional[float]) -> str: + return ( + time.strftime("%H:%M:%S", time.gmtime(duration)) + if duration != "LIVE" + else duration + ) -class Player(wavelink.Player): - """Custom wavelink Player class.""" +# Format view count +# noinspection DuplicatedCode +def parse_count(count): + original_count = count - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + count = float("{:.3g}".format(count)) + magnitude = 0 + matches = ["", "K", "M", "B", "T", "Qua", "Qui"] - self.context: commands.Context = kwargs.get('context', None) - if self.context: - self.dj: discord.Member = self.context.author + while abs(count) >= 1000: + if magnitude >= 5: + break - self.queue = asyncio.Queue() - self.controller = None + magnitude += 1 + count /= 1000.0 - self.waiting = False - self.updating = False + try: + return "{}{}".format( + "{:f}".format(count).rstrip("0").rstrip("."), matches[magnitude] + ) + except IndexError: + return original_count - self.pause_votes = set() - self.resume_votes = set() - self.skip_votes = set() - self.shuffle_votes = set() - self.stop_votes = set() - async def do_next(self) -> None: - if self.is_playing or self.waiting: - return +class Music(commands.Cog, discordSuperUtils.CogManager.Cog, name="Music"): + def __init__(self, bot): + self.bot = bot + self.skip_votes = {} # Skip vote counter dictionary - # Clear the votes for a new song... - self.pause_votes.clear() - self.resume_votes.clear() - self.skip_votes.clear() - self.shuffle_votes.clear() - self.stop_votes.clear() + # self.client_secret = "" # spotify client_secret + # self.client_id = "" # spotify client_id - try: - self.waiting = True - with async_timeout.timeout(300): - track = await self.queue.get() - except asyncio.TimeoutError: - # No music has been played for 5 minutes, cleanup and disconnect... - return await self.teardown() + # Get your's from here https://developer.spotify.com/ - await self.play(track) - self.waiting = False + self.MusicManager = MusicManager(self.bot, spotify_support=False) - # Invoke our players controller... - await self.invoke_controller() + # self.MusicManager = MusicManager(bot, + # client_id=self.client_id, + # client_secret=self.client_secret, + # spotify_support=True) - async def invoke_controller(self) -> None: - """Method which updates or sends a new player controller.""" - if self.updating: - return + # If using spotify support use this instead ^^^ - self.updating = True + self.ImageManager = discordSuperUtils.ImageManager() + super().__init__() - if not self.controller: - self.controller = InteractiveController(embed=self.build_embed(), player=self) - await self.controller.start(self.context) + # noinspection DuplicatedCode - elif not await self.is_position_fresh(): - try: - await self.controller.message.delete() - except discord.HTTPException: - pass + # Play function + async def play_cmd(self, ctx, query): + async with ctx.typing(): + player = await self.MusicManager.create_player(query, ctx.author) - self.controller.stop() + if player: + if not ctx.voice_client or not ctx.voice_client.is_connected(): + await self.MusicManager.join(ctx) - self.controller = InteractiveController(embed=self.build_embed(), player=self) - await self.controller.start(self.context) + await self.MusicManager.queue_add(players=player, ctx=ctx) + if not await self.MusicManager.play(ctx): + await ctx.send(f"{player[0].title}을 재생목록에 추가했어요.") + else: + await ctx.send("✅",delete_after=5) else: - embed = self.build_embed() - await self.controller.message.edit(content=None, embed=embed) - - self.updating = False - def build_embed(self) -> typing.Optional[discord.Embed]: - """Method which builds our players controller embed.""" - track = self.current - if not track: - return - - channel = self.bot.get_channel(int(self.channel_id)) - qsize = self.queue.qsize() - - embed = discord.Embed(title=f'채널 | {channel.name}', colour=0xebb145) - embed.description = f'제목:\n**`{track.title}`**\n\n' - embed.set_thumbnail(url=track.thumb) - - embed.add_field(name='길이:', value=str(datetime.timedelta(milliseconds=int(track.length)))) - embed.add_field(name='Queue Length', value=str(qsize)) - embed.add_field(name='불륨', value=f'**`{self.volume}%`**') - embed.add_field(name='노래요청자', value=track.requester.mention) - embed.add_field(name='DJ', value=self.dj.mention) - embed.add_field(name='유튜브', value=f'[유튜브]({track.uri})') - - return embed - - async def is_position_fresh(self) -> bool: - """Method which checks whether the player controller should be remade or updated.""" - try: - async for message in self.context.channel.history(limit=5): - if message.id == self.controller.message.id: - return True - except (discord.HTTPException, AttributeError): - return False - - return False - - async def teardown(self): - """Clear internal states, remove player controller and disconnect.""" - try: - await self.controller.message.delete() - except discord.HTTPException: - pass - - self.controller.stop() - - try: - await self.destroy() - except KeyError: - pass - - -class InteractiveController(menus.Menu): - """The Players interactive controller menu class.""" - - def __init__(self, *, embed: discord.Embed, player: Player): - super().__init__(timeout=None) - - self.embed = embed - self.player = player - - def update_context(self, payload: discord.RawReactionActionEvent): - """Update our context with the user who reacted.""" - ctx = copy.copy(self.ctx) - ctx.author = payload.member - - return ctx - - def reaction_check(self, payload: discord.RawReactionActionEvent): - if payload.event_type == 'REACTION_REMOVE': - return False - - if not payload.member: - return False - if payload.member.bot: - return False - if payload.message_id != self.message.id: - return False - if payload.member not in self.bot.get_channel(int(self.player.channel_id)).members: - return False - - return payload.emoji in self.buttons - - async def send_initial_message(self, ctx: commands.Context, channel: discord.TextChannel) -> discord.Message: - return await channel.send(embed=self.embed) - - @menus.button(emoji='\u25B6') - async def resume_command(self, payload: discord.RawReactionActionEvent): - """Resume button.""" - ctx = self.update_context(payload) - - command = self.bot.get_command('resume') - ctx.command = command - - await self.bot.invoke(ctx) - - @menus.button(emoji='\u23F8') - async def pause_command(self, payload: discord.RawReactionActionEvent): - """Pause button""" - ctx = self.update_context(payload) - - command = self.bot.get_command('pause') - ctx.command = command - - await self.bot.invoke(ctx) - - @menus.button(emoji='\u23F9') - async def stop_command(self, payload: discord.RawReactionActionEvent): - """Stop button.""" - ctx = self.update_context(payload) - - command = self.bot.get_command('stop') - ctx.command = command - - await self.bot.invoke(ctx) - - @menus.button(emoji='\u23ED') - async def skip_command(self, payload: discord.RawReactionActionEvent): - """Skip button.""" - ctx = self.update_context(payload) - - command = self.bot.get_command('skip') - ctx.command = command - - await self.bot.invoke(ctx) - - @menus.button(emoji='\U0001F500') - async def shuffle_command(self, payload: discord.RawReactionActionEvent): - """Shuffle button.""" - ctx = self.update_context(payload) - - command = self.bot.get_command('shuffle') - ctx.command = command - - await self.bot.invoke(ctx) - - @menus.button(emoji='\u2795') - async def volup_command(self, payload: discord.RawReactionActionEvent): - """Volume up button""" - ctx = self.update_context(payload) - - command = self.bot.get_command('vol_up') - ctx.command = command - - await self.bot.invoke(ctx) - - @menus.button(emoji='\u2796') - async def voldown_command(self, payload: discord.RawReactionActionEvent): - """Volume down button.""" - ctx = self.update_context(payload) - - command = self.bot.get_command('vol_down') - ctx.command = command - - await self.bot.invoke(ctx) - - @menus.button(emoji='\U0001F1F6') - async def queue_command(self, payload: discord.RawReactionActionEvent): - """Player queue button.""" - ctx = self.update_context(payload) - - command = self.bot.get_command('queue') - ctx.command = command - - await self.bot.invoke(ctx) - - -class PaginatorSource(menus.ListPageSource): - """Player queue paginator class.""" - - def __init__(self, entries, *, per_page=8): - super().__init__(entries, per_page=per_page) - - async def format_page(self, menu: menus.Menu, page): - embed = discord.Embed(title='재생목록', colour=0x4f0321) - embed.description = '\n'.join(f'`{index}. {title}`' for index, title in enumerate(page, 1)) - - return embed - - def is_paginating(self): - # We always want to embed even on 1 page of results... - return True - - -class Music(commands.Cog, wavelink.WavelinkMixin): - """Music Cog.""" - def __init__(self, bot: commands.Bot): - self.bot = bot - - if not hasattr(bot, 'wavelink'): - bot.wavelink = wavelink.Client(bot=bot) - - bot.loop.create_task(self.start_nodes()) - async def cog_before_invoke(self, ctx: commands.Context): - print(ctx.command) - if ctx.command.name != '메일': - database = await aiosqlite.connect("db/db.sqlite") - cur = await database.execute( - 'SELECT * FROM uncheck WHERE user_id = ?', (ctx.author.id,) + await ctx.send("쿼리를 찾지 못했어요.") + + # cog error handler + async def cog_command_error( + self, ctx: commands.Context, error: commands.CommandError + ): + print("An error occurred: {}".format(str(error))) + + # Error handler + @discordSuperUtils.CogManager.event(discordSuperUtils.MusicManager) + async def on_music_error(self, ctx, error): + errors = { + discordSuperUtils.NotPlaying: "지금은 노래를 재생중이지 않아요..", + discordSuperUtils.NotConnected: '제가 아직 음성채널에 접속중이지 않아요!', + discordSuperUtils.NotPaused: "노래가 아직 멈추지않았어요!", + discordSuperUtils.QueueEmpty: "큐가 비어있어요!", + discordSuperUtils.AlreadyConnected: "이미 음성채널에 접속되어있어요!", + discordSuperUtils.QueueError: "큐에 문제가 생겼어요!", + discordSuperUtils.SkipError: "스킵할 노래가 없어요!", + discordSuperUtils.UserNotConnected: "명령자님이 아직 음성채널에 접속중이지 않아요!", + discordSuperUtils.InvalidSkipIndex: "스킵인덱스값은 사용할 수가 없어요!", + } + + for error_type, response in errors.items(): + if isinstance(error, error_type): + await ctx.send(response) + return + + print("unexpected error") + raise error + + # On music play event + @discordSuperUtils.CogManager.event(discordSuperUtils.MusicManager) + async def on_play(self, ctx, player): # This returns a player object + + # Extracting useful data from player object + thumbnail = player.data["videoDetails"]["thumbnail"]["thumbnails"][-1]["url"] + title = player.data["videoDetails"]["title"] + url = player.url + uploader = player.data["videoDetails"]["author"] + requester = player.requester.mention if player.requester else "Autoplay" + + embed = discord.Embed( + title="현재 곡", + color=discord.Color.from_rgb(255, 255, 0), + timestamp=datetime.datetime.now(datetime.timezone.utc), + description=f"[**{title}**]({url}) by **{uploader}**", + ) + embed.add_field(name="요청자", value=requester) + embed.set_thumbnail(url=thumbnail) + + await ctx.send(embed=embed) + # Clearing skip votes for each song + if self.skip_votes.get(ctx.guild.id): + self.skip_votes.pop(ctx.guild.id) + + # On queue end event + @discordSuperUtils.CogManager.event(discordSuperUtils.MusicManager) + async def on_queue_end(self, ctx): + print(f"The queue has ended in {ctx}") + await ctx.send("큐가 끝났어요.") + # You could wait and check activity, etc... + + # On inactivity disconnect event + @discordSuperUtils.CogManager.event(discordSuperUtils.MusicManager) + async def on_inactivity_disconnect(self, ctx): + print(f"I have left {ctx} due to inactivity") + await ctx.send("사용하지않아 채널에서 나갔어요") + + # On ready event + + # Leave command + @commands.command(name="나가") + async def leave(self, ctx): + if await self.MusicManager.leave(ctx): + await ctx.send("👋", delete_after=5) + # Or + # await message.add_reaction("👋") + + # Lyrics command + + # Now playing command + @commands.command(name="지금곡", aliases = ["현재곡"]) + async def now_playing(self, ctx): + if player := await self.MusicManager.now_playing(ctx): + # Played duration + duration_played = round( + await self.MusicManager.get_player_played_duration(ctx, player) ) - if await cur.fetchone() is None: - cur = await database.execute("SELECT * FROM mail") - mails = await cur.fetchall() - check = sum(1 for _ in mails) - mal = discord.Embed( - title=f'📫짱구의 메일함 | {check}개 수신됨', - description="아직 읽지 않은 메일이 있어요.'`짱구야 메일`'로 확인하세요.\n주기적으로 메일함을 확인해주세요! 소소한 업데이트 및 이벤트개최등 여러소식을 확인해보세요.", - colour=ctx.author.colour, - ) - - return await ctx.send(embed=mal) - cur = await database.execute('SELECT * FROM mail') - mails = await cur.fetchall() - check = sum(1 for _ in mails) - # noinspection DuplicatedCode - cur = await database.execute("SELECT * FROM uncheck WHERE user_id = ?", (ctx.author.id,)) - # noinspection DuplicatedCode - check2 = await cur.fetchone() - if str(check) != str(check2[1]): - mal = discord.Embed( - title=f'📫짱구의 메일함 | {int(check) - int(check2[1])}개 수신됨', - description="아직 읽지 않은 메일이 있어요.'`짱구야 메일`'로 확인하세요.\n주기적으로 메일함을 확인해주세요! 소소한 업데이트 및 이벤트개최등 여러소식을 확인해보세요.", - colour=ctx.author.colour, - ) - - await ctx.send(embed=mal) - async def start_nodes(self) -> None: - """Connect and intiate nodes.""" - await self.bot.wait_until_ready() - - if self.bot.wavelink.nodes: - previous = self.bot.wavelink.nodes.copy() - - for node in previous.values(): - await node.destroy() - - nodes = {'MAIN': {'host': '121.164.146.45', - 'port': 2333, - 'rest_uri': 'http://121.164.146.45:2333', - 'password': 'bainble0211', - 'identifier': 'MAIN', - 'region': 'us_central' - }} - - for n in nodes.values(): - await self.bot.wavelink.initiate_node(**n) - - @wavelink.WavelinkMixin.listener() - async def on_node_ready(self, node: wavelink.Node): - print(f'Node {node.identifier} is ready!') - - @wavelink.WavelinkMixin.listener('on_track_stuck') - @wavelink.WavelinkMixin.listener('on_track_end') - @wavelink.WavelinkMixin.listener('on_track_exception') - async def on_player_stop(self, node: wavelink.Node, payload): - await payload.player.do_next() - - @commands.Cog.listener() - async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): - if member.bot: - return - - player: Player = self.bot.wavelink.get_player(member.guild.id, cls=Player) - - if not player.channel_id or not player.context: - player.node.players.pop(member.guild.id) - return - - channel = self.bot.get_channel(int(player.channel_id)) - - if member == player.dj and after.channel is None: - for m in channel.members: - if m.bot: - continue - else: - player.dj = m - return - - elif after.channel == channel and player.dj not in channel.members: - player.dj = member - - async def cog_command_error(self, ctx: commands.Context, error: Exception): - """Cog wide error handler.""" - if isinstance(error, IncorrectChannelError): - return - - if isinstance(error, NoChannelProvided): - return await ctx.send('You must be in a voice channel or provide one to connect to.') - - async def cog_check(self, ctx: commands.Context): - """Cog wide check, which disallows commands in DMs.""" - if not ctx.guild: - await ctx.send('Music commands are not available in Private Messages.') - return False - - return True - - async def cog_before_invoke(self, ctx: commands.Context): - """Coroutine called before command invocation. - We mainly just want to check whether the user is in the players controller channel. - """ - player: Player = self.bot.wavelink.get_player(ctx.guild.id, cls=Player, context=ctx) - - if player.context: - if player.context.channel != ctx.channel: - await ctx.send(f'{ctx.author.mention}, you must be in {player.context.channel.mention} for this session.') - raise IncorrectChannelError - - if ctx.command.name == 'connect' and not player.context: - return - elif self.is_privileged(ctx): - return - - if not player.channel_id: - return - - channel = self.bot.get_channel(int(player.channel_id)) - if not channel: - return - - if player.is_connected: - if ctx.author not in channel.members: - await ctx.send(f'{ctx.author.mention}, you must be in `{channel.name}` to use voice commands.') - raise IncorrectChannelError - - def required(self, ctx: commands.Context): - """Method which returns required votes based on amount of members in a channel.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) - channel = self.bot.get_channel(int(player.channel_id)) - required = math.ceil((len(channel.members) - 1) / 2.5) - - if ctx.command.name == 'stop': - if len(channel.members) == 3: - required = 2 - - return required - - def is_privileged(self, ctx: commands.Context): - """Check whether the user is an Admin or DJ.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) - - return player.dj == ctx.author or ctx.author.guild_permissions.kick_members - + # Loop status + loop = (await self.MusicManager.get_queue(ctx)).loop + if loop == discordSuperUtils.Loops.LOOP: + loop_status = "반복기능이 활성화 되었어요. 🟢" + elif loop == discordSuperUtils.Loops.QUEUE_LOOP: + loop_status = "큐 반복기능이 활성화 되었어요. 🟢" + else: + loop_status = "반복 기능이 비활성화 되었어요. 버튼 🔴" + + # Fecthing other details + thumbnail = player.data["videoDetails"]["thumbnail"]["thumbnails"][-1][ + "url" + ] + title = player.data["videoDetails"]["title"] + url = player.url + uploader = player.data["videoDetails"]["author"] + requester = player.requester.mention if player.requester else "Autoplay" + + embed = discord.Embed( + title="현재곡", + description=f"**{title}**", + timestamp=datetime.datetime.utcnow(), + color=discord.Color.from_rgb(0, 255, 255), + ) + embed.add_field(name="현재 재생시간", value=parse_duration(duration_played)) + embed.add_field(name="재생길이", value=parse_duration(player.duration)) + embed.add_field(name="반복상태", value=loop_status) + embed.add_field(name="요청자", value=requester) + embed.add_field(name="업로더", value=uploader) + embed.add_field(name="URL", value=f"[Click]({url})") + embed.set_thumbnail(url=thumbnail) + embed.set_image(url=r"https://i.imgur.com/ufxvZ0j.gif") + embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar_url) + + await ctx.send(embed=embed) + + # Join voice channel command @commands.command(name="들어와") - async def connect(self, ctx: commands.Context, *, channel: typing.Union[discord.VoiceChannel, discord.StageChannel] = None): - """Connect to a voice channel.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) - - if player.is_connected: - return - - channel = getattr(ctx.author.voice, 'channel', channel) - if channel is None: - raise NoChannelProvided - - await player.connect(channel.id) - - @commands.command(aliases=["재생"]) - async def play(self, ctx: commands.Context, *, query: str): - """Play or queue a song with the given query.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) - - if not player.is_connected: - await ctx.invoke(self.connect) - - query = query.strip('<>') - if not URL_REG.match(query): - query = f'ytsearch:{query}' - - tracks = await self.bot.wavelink.get_tracks(query) - if not tracks: - return await ctx.send('No songs were found with that query. Please try again.', delete_after=15) - - if isinstance(tracks, wavelink.TrackPlaylist): - for track in tracks.tracks: - track = Track(track.id, track.info, requester=ctx.author) - await player.queue.put(track) - - await ctx.send(f'```ini\n🎶 {tracks.data["playlistInfo"]["name"]}' - f' with {len(tracks.tracks)} 트렉로드 완료(플레이리스트에 추가!)🎶\n```', delete_after=15) - else: - track = Track(tracks[0].id, tracks[0].info, requester=ctx.author) - await ctx.send(f'```ini\n🎶 {track.title} 재생🎶\n```', delete_after=15) - await player.queue.put(track) - - if not player.is_playing: - await player.do_next() - - @commands.command() - async def pause(self, ctx: commands.Context): - """Pause the currently playing song.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) - - if player.is_paused or not player.is_connected: - return - - if self.is_privileged(ctx): - await ctx.send('노래가 일시정지되었습니다', delete_after=10) - player.pause_votes.clear() - - return await player.set_pause(True) - - required = self.required(ctx) - player.pause_votes.add(ctx.author) - - if len(player.pause_votes) >= required: - await ctx.send('Vote to pause passed. Pausing player.', delete_after=10) - player.pause_votes.clear() - await player.set_pause(True) - else: - await ctx.send(f'{ctx.author.mention} has voted to pause the player.', delete_after=15) - - @commands.command() - async def resume(self, ctx: commands.Context): - """Resume a currently paused player.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) - - if not player.is_paused or not player.is_connected: - return - - if self.is_privileged(ctx): - await ctx.send('노래 재생중', delete_after=10) - player.resume_votes.clear() - - return await player.set_pause(False) - - required = self.required(ctx) - player.resume_votes.add(ctx.author) - - if len(player.resume_votes) >= required: - await ctx.send('Vote to resume passed. Resuming player.', delete_after=10) - player.resume_votes.clear() - await player.set_pause(False) - else: - await ctx.send(f'{ctx.author.mention} has voted to resume the player.', delete_after=15) - - @commands.command(aliases=["스킵"]) - async def skip(self, ctx: commands.Context): - """Skip the currently playing song.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) - - if not player.is_connected: - return - - if self.is_privileged(ctx): - await ctx.send('An admin or DJ has skipped the song.', delete_after=10) - player.skip_votes.clear() - - return await player.stop() - - if ctx.author == player.current.requester: - await ctx.send('The song requester has skipped the song.', delete_after=10) - player.skip_votes.clear() - - return await player.stop() - - required = self.required(ctx) - player.skip_votes.add(ctx.author) - - if len(player.skip_votes) >= required: - await ctx.send('Vote to skip passed. Skipping song.', delete_after=10) - player.skip_votes.clear() - await player.stop() - else: - await ctx.send(f'{ctx.author.mention} has voted to skip the song.', delete_after=15) - - @commands.command(aliases=["스탑"]) - async def stop(self, ctx: commands.Context): - """Stop the player and clear all internal states.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) - - if not player.is_connected: - return - - if self.is_privileged(ctx): - await ctx.send('노래종료', delete_after=10) - return await player.teardown() - - required = self.required(ctx) - player.stop_votes.add(ctx.author) - - if len(player.stop_votes) >= required: - await ctx.send('Vote to stop passed. Stopping the player.', delete_after=10) - await player.teardown() - else: - await ctx.send(f'{ctx.author.mention} has voted to stop the player.', delete_after=15) - - @commands.command(aliases=['v', 'vol','불륨']) - async def volume(self, ctx: commands.Context, *, vol: int): - """Change the players volume, between 1 and 100.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) - - if not player.is_connected: - return - - if not self.is_privileged(ctx): - return await ctx.send('Only the DJ or admins may change the volume.') - - if not 0 < vol < 101: - return await ctx.send('Please enter a value between 1 and 100.') - - await player.set_volume(vol) - await ctx.send(f'Set the volume to **{vol}**%', delete_after=7) - - @commands.command(aliases=['mix','셔플']) - async def shuffle(self, ctx: commands.Context): - """Shuffle the players queue.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) - - if not player.is_connected: - return - - if player.queue.qsize() < 3: - return await ctx.send('Add more songs to the queue before shuffling.', delete_after=15) - - if self.is_privileged(ctx): - await ctx.send('An admin or DJ has shuffled the playlist.', delete_after=10) - player.shuffle_votes.clear() - return random.shuffle(player.queue._queue) - - required = self.required(ctx) - player.shuffle_votes.add(ctx.author) - - if len(player.shuffle_votes) >= required: - await ctx.send('Vote to shuffle passed. Shuffling the playlist.', delete_after=10) - player.shuffle_votes.clear() - random.shuffle(player.queue._queue) - else: - await ctx.send(f'{ctx.author.mention} has voted to shuffle the playlist.', delete_after=15) - - @commands.command(hidden=True) - async def vol_up(self, ctx: commands.Context): - """Command used for volume up button.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) - - if not player.is_connected or not self.is_privileged(ctx): - return - - vol = int(math.ceil((player.volume + 10) / 10)) * 10 + async def join(self, ctx): + if await self.MusicManager.join(ctx): + await ctx.send(f"{ctx.author.voice.channel.mention}에 접속했어요!") + + # Play song command + @commands.command(name="재생") + async def play(self, ctx, *, query: str): + # Calling the play function + await Music.play_cmd(self, ctx, query) + + # Pause command + @commands.command(name="일시정지") + async def pause(self, ctx): + if await self.MusicManager.pause(ctx): + await ctx.send("일시정지했어요. ⏸",delete_after=5) + + # Resume command + @commands.command(name="이어재생") + async def resume(self, ctx): + if await self.MusicManager.resume(ctx): + await ctx.send("이어서 재생할게요. ⏯", delete_after=5) + + # Volume command + @commands.command(name="볼륨") + async def volume(self, ctx, volume: int = None): + if volume is None: + current_volume = await self.MusicManager.volume(ctx) + await ctx.send("현재 볼륨 " + current_volume + "%") + if await self.MusicManager.volume(ctx, volume) is not None: + current_volume = await self.MusicManager.volume(ctx, volume) + await ctx.send(f"볼름은 다음으로 설정했어요. `{current_volume}%`") + + # Song loop command + @commands.command(name="루프", aliases = ["반복"]) + async def loop(self, ctx): + is_loop = await self.MusicManager.loop(ctx) + + if is_loop is not None: + await ctx.send( + f"반복기능을 {'활성화 🟢' if is_loop else '비활성화 🔴'} 했어요", delete_after=5) + + # Queue loop command + @commands.command(name="큐루프") + async def queueloop(self, ctx): + is_loop = await self.MusicManager.queueloop(ctx) + + if is_loop is not None: + await ctx.send( + f"큐반복기능을 {'활성화 🟢' if is_loop else '비활성화 🔴'} 했어요", delete_after=5) + + # History command + @commands.command(name="노래기록") + async def history(self, ctx): + if queue := await self.MusicManager.get_queue(ctx): + auto = "Autoplay" + formatted_history = [ + f"제목: '{x.title}\n요청자: {x.requester.mention if x.requester else auto}" + for x in queue.history + ] + + embeds = discordSuperUtils.generate_embeds( + formatted_history, + "노래 기록", + "지금까지 재생한 곡의 기록을 보여드려요", + 25, + string_format="{}", + ) - if vol > 100: - vol = 100 - await ctx.send('Maximum volume reached', delete_after=7) + for embed in embeds: + embed.timestamp = datetime.datetime.utcnow() - await player.set_volume(vol) + await discordSuperUtils.PageManager(ctx, embeds, public=True).run() - @commands.command(hidden=True) - async def vol_down(self, ctx: commands.Context): - """Command used for volume down button.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) + # Stop command + @commands.command(name="정지", aliases = ["스탑"]) + async def stop(self, ctx): + await self.MusicManager.cleanup(ctx.voice_client, ctx.guild) + await ctx.send("⏹️") - if not player.is_connected or not self.is_privileged(ctx): - return + # Skip command with voting + @commands.command(name="스킵") + async def skip(self, ctx, index: int = None): + if queue := (await self.MusicManager.get_queue(ctx)): + requester = (await self.MusicManager.now_playing(ctx)).requester - vol = int(math.ceil((player.volume - 10) / 10)) * 10 + # Checking if the song is autoplayed + if requester is None: + await ctx.send("자동재생 곡을 스킵했어요.⏩") + await self.MusicManager.skip(ctx, index) - if vol < 0: - vol = 0 - await ctx.send('Player is currently muted', delete_after=10) + # Checking if queue is empty and autoplay is disabled + elif not queue.queue and not queue.autoplay: + await ctx.send("큐의 마지막 곡을 스킵할 수 없어요") - await player.set_volume(vol) + else: + # Checking if guild id list is in skip votes dictionary + if not self.skip_votes.get(ctx.guild.id): + self.skip_votes[ctx.guild.id] = [] - @commands.command(aliases=['eq']) - async def equalizer(self, ctx: commands.Context, *, equalizer: str): - """Change the players equalizer.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) + # Checking the voter + voter = ctx.author - if not player.is_connected: - return + # If voter is requester than skips automatically + if voter == (await self.MusicManager.now_playing(ctx)).requester: + skipped_player = await self.MusicManager.skip(ctx, index) - if not self.is_privileged(ctx): - return await ctx.send('Only the DJ or admins may change the equalizer.') + await ctx.send("요청자의 요청으로 스킵했어요. ⏩",delete_after=5) - eqs = {'flat': wavelink.Equalizer.flat(), - 'boost': wavelink.Equalizer.boost(), - 'metal': wavelink.Equalizer.metal(), - 'piano': wavelink.Equalizer.piano()} + if not skipped_player.requester: + await ctx.send("다음 자동재생곡으로 스킵했어요. ⏩",delete_after=5) - eq = eqs.get(equalizer.lower(), None) + # clearing the skip votes + self.skip_votes.pop(ctx.guild.id) - if not eq: - joined = "\n".join(eqs.keys()) - return await ctx.send(f'Invalid EQ provided. Valid EQs:\n\n{joined}') + # Voting + elif ( + voter.id not in self.skip_votes[ctx.guild.id] + ): # Checking if someone already voted + # Adding the voter id to skip votes + self.skip_votes[ctx.guild.id].append(voter.id) - await ctx.send(f'Successfully changed equalizer to {equalizer}', delete_after=15) - await player.set_eq(eq) + # Calculating total votes + total_votes = len(self.skip_votes[ctx.guild.id]) - @commands.command(aliases=['q', '재생목록']) - async def queue(self, ctx: commands.Context): - """Display the players queued songs.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) + # If total votes >=3 then it will skip + if total_votes >= 3: + skipped_player = await self.MusicManager.skip(ctx, index) - if not player.is_connected: - return + await ctx.send("투표로 스킵되어졌어요. ⏩",delete_after=5) - if player.queue.qsize() == 0: - return await ctx.send('There are no more songs in the queue.', delete_after=15) + if not skipped_player.requester: + await ctx.send("다음 자동재생곡으로 스킵했어요. ⏩",delete_after=5) - entries = [track.title for track in player.queue._queue] - pages = PaginatorSource(entries=entries) - paginator = menus.MenuPages(source=pages, timeout=None, delete_message_after=True) + # Clearing skip votes of the guild + self.skip_votes.pop(ctx.guild.id) - await paginator.start(ctx) - @commands.command(name="현재곡",aliases=['np', 'now_playing', 'current']) - async def nowplaying(self, ctx: commands.Context): - """Update the player controller.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) + # Shows voting status + else: + await ctx.send( + f"스킵 투표가 추가되었어요, 현재 투표수 - **{total_votes}/3**" + ) - if not player.is_connected: - return + # If someone uses vote command twice + else: + await ctx.send("이미 현재곡에 투표하셨어요!",delete_after=5) + + # Queue command + @commands.command(name="큐", aliases = ["재생목록"]) + async def queue(self, ctx): + if queue := await self.MusicManager.get_queue(ctx): + auto = "Autoplay" + formatted_queue = [ + f"제목: '{x.title}\n요청자: {x.requester.mention if x.requester else auto}" + for x in queue.queue + ] + + embeds = discordSuperUtils.generate_embeds( + formatted_queue, + "큐", # Title of embed + f"Now Playing: {await self.MusicManager.now_playing(ctx)}", + 25, # Number of rows in one pane + string_format="{}", + color=11658814, # Color of embed in decimal color + ) - await player.invoke_controller() + for embed in embeds: + embed.timestamp = datetime.datetime.utcnow() - @commands.command(aliases=['swap']) - async def swap_dj(self, ctx: commands.Context, *, member: discord.Member = None): - """Swap the current DJ to another member in the voice channel.""" - player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx) + await discordSuperUtils.PageManager(ctx, embeds, public=True).run() - if not player.is_connected: - return + # Loop status command + @commands.command(name="반복확인") + async def loop_check(self, ctx): + if queue := await self.MusicManager.get_queue(ctx): + loop = queue.loop + loop_status = None - if not self.is_privileged(ctx): - return await ctx.send('Only admins and the DJ may use this command.', delete_after=15) + if loop == discordSuperUtils.Loops.LOOP: + loop_status = "반복 기능이 활성화 되었어요. 🟢" - members = self.bot.get_channel(int(player.channel_id)).members + elif loop == discordSuperUtils.Loops.QUEUE_LOOP: + loop_status = "큐 반복 기능이 활성화 되었어요. 🟢" - if member and member not in members: - return await ctx.send(f'{member} is not currently in voice, so can not be a DJ.', delete_after=15) + elif loop == discordSuperUtils.Loops.NO_LOOP: + loop_status = "반복 기능이 비활성화 되었어요. 🔴" - if member and member == player.dj: - return await ctx.send('Cannot swap DJ to the current DJ... :)', delete_after=15) + if loop_status: + embed = discord.Embed( + title=loop_status, + color=0x00FF00, + timestamp=datetime.datetime.utcnow(), + ) - if len(members) <= 2: - return await ctx.send('No more members to swap to.', delete_after=15) + await ctx.send(embed=embed) - if member: - player.dj = member - return await ctx.send(f'{member.mention} is now the DJ.') + # Shuffle command + @commands.command(name="셔플") + async def shuffle(self, ctx): + is_shuffle = await self.MusicManager.shuffle(ctx) - for m in members: - if m == player.dj or m.bot: - continue + if is_shuffle is not None: + if is_shuffle: + await ctx.send('셔플이 활성화되었어요.',delete_after=5) else: - player.dj = m - return await ctx.send(f'{member.mention} is now the DJ.') -def setup(bot: commands.Bot): + await ctx.send('셔플이 비활성화되었어요.',delete_after=5) + + # Previous/Rewind command + @commands.command(name="이전곡") + async def previous(self, ctx, index: int = None): + if previous_player := await self.MusicManager.previous(ctx, index): + await ctx.send(f"{previous_player[0].title}로부터 이전곡을 재생해요",delete_after=5) + + # Before invoke checks. Add more commands if you wish to + @join.before_invoke + @play.before_invoke + async def ensure_voice_state(self, ctx: commands.Context): + if not ctx.author.voice or not ctx.author.voice.channel: + await ctx.send("You are not connected to any voice channel.") + raise commands.CommandError() + + if ( + ctx.voice_client + and ctx.voice_client.channel != ctx.author.voice.channel + ): + await ctx.send("Bot is already in a voice channel.") + raise commands.CommandError() + + +def setup(bot): bot.add_cog(Music(bot)) \ No newline at end of file