diff --git a/.gitignore b/.gitignore index a7ac018..d213692 100644 --- a/.gitignore +++ b/.gitignore @@ -186,18 +186,7 @@ pyproject.toml poetry.lock application.yml Lavalink.jar -tkablent-3.9/pyvenv.cfg -tkablent-3.9/Scripts/activate -tkablent-3.9/Scripts/activate.bat -tkablent-3.9/Scripts/Activate.ps1 -tkablent-3.9/Scripts/deactivate.bat -tkablent-3.9/Scripts/dotenv.exe -tkablent-3.9/Scripts/normalizer.exe -tkablent-3.9/Scripts/pip.exe -tkablent-3.9/Scripts/pip3.9.exe -tkablent-3.9/Scripts/pip3.exe -tkablent-3.9/Scripts/python.exe -tkablent-3.9/Scripts/pythonw.exe +tkablent_dev/* TKablent_Codespaces.code-workspace music_comp/data.json music_comp/search_cache.json diff --git a/main.py b/main.py index ad5a4e6..ff9a0a7 100644 --- a/main.py +++ b/main.py @@ -13,14 +13,13 @@ production = True prefix = "/" - branch = "master" if production: status = discord.Status.online production_status = "s" # ce for cutting edge, s for stable test_subject = "wl3.0_test" - bot_version = "m.20240318.2{}-{}".format(f".{test_subject}" if production_status != "s" else "", production_status) + bot_version = "m.20240318.3{}-{}".format(f".{test_subject}" if production_status != "s" else "", production_status) else: status = discord.Status.dnd bot_version = f"LOCAL DEVELOPMENT / {branch} Branch\nMusic Function" diff --git a/music_comp/player.py b/music_comp/player.py index 638ec31..00771de 100644 --- a/music_comp/player.py +++ b/music_comp/player.py @@ -64,28 +64,6 @@ async def _create_daemon(self): client=self.bot, ) - ############ - # sessdata refresh (currently not working) - ############ - async def _refresh_sessdata(self): - while True: - need_refresh = await self._bilibilic.chcek_refresh() - if need_refresh: - await self._bilibilic.refresh() - sessdata = self._bilibilic.sessdata - bili_jct = self._bilibilic.bili_jct - dotenv.set_key( - dotenv_path=rf"{os.getcwd()}/.env", - key_to_set="SESSDATA", - value_to_set=sessdata, - ) - dotenv.set_key( - dotenv_path=rf"{os.getcwd()}/.env", - key_to_set="BILI_JCT", - value_to_set=bili_jct, - ) - await asyncio.sleep(120) - ############# # Join Core # ############# @@ -93,7 +71,9 @@ async def _join(self, channel: discord.VoiceChannel): voice_client = channel.guild.voice_client mainplayhost = wavelink.Pool.get_node("TW_PlayBackNode") if voice_client is None: - await channel.connect(cls=wavelink.Player(nodes=[mainplayhost])) + player = wavelink.Player() + player.inactive_timeout = 600 + await channel.connect(cls=player) ############## # Leave Core # @@ -117,7 +97,6 @@ async def _pause(self, guild: discord.Guild): voice_client: wavelink.Player = guild.voice_client if not voice_client.paused and voice_client.playing: await voice_client.pause(True) - self._start_timer(guild) ############### # Resume Core # @@ -125,7 +104,6 @@ async def _pause(self, guild: discord.Guild): async def _resume(self, guild: discord.Guild): voice_client: wavelink.Player = guild.voice_client if voice_client.paused: - self._stop_timer(guild) await voice_client.pause(False) ############# @@ -179,7 +157,6 @@ async def _volume(self, guild: discord.Guild, volume: float): async def _play(self, guild: discord.Guild, channel: discord.TextChannel): self[guild.id].text_channel = channel voice_client: wavelink.Player = guild.voice_client - self._start_timer(guild) if (not voice_client.paused) and (voice_client.current is None) and (len(self._playlist[guild.id].order) > 0): await voice_client.play(self._playlist[guild.id].current()) @@ -187,28 +164,10 @@ async def _play(self, guild: discord.Guild, channel: discord.TextChannel): ######## # Misc # ######## - def _start_timer(self, guild: discord.Guild): - self._stop_timer(guild) - coro = self._timer(guild) - self[guild.id]._timer = self.bot.loop.create_task(coro) - - def _stop_timer(self, guild: discord.Guild): - if self[guild.id]._timer is not None: - self[guild.id]._timer.cancel() - self[guild.id]._timer = None - - async def _timer(self, guild: discord.Guild): - await asyncio.sleep(600.0) - self[guild.id]._timer_done = True - await self._leave(guild) - def _cleanup(self, guild: discord.Guild): if self[guild.id]._task is not None: self[guild.id]._task = None del self._playlist[guild.id] - if self[guild.id]._timer is not None: - self[guild.id]._timer.cancel() - self[guild.id]._timer = None # ======================================================================================================== @@ -232,6 +191,31 @@ async def post_boot(self): from .ui import groupbutton self.groupbutton = groupbutton + ############ + # sessdata refresh (currently not working) + ############ + async def _refresh_sessdata(self): + while True: + need_refresh = await self.track_helper._bilibilic.check_refresh() + if need_refresh: + await self.track_helper._bilibilic.refresh() + dotenv.set_key( + dotenv_path=rf"{os.getcwd()}/.env", + key_to_set="SESSDATA", + value_to_set=self.track_helper._bilibilic.sessdata, + ) + dotenv.set_key( + dotenv_path=rf"{os.getcwd()}/.env", + key_to_set="BILI_JCT", + value_to_set=self.track_helper._bilibilic.bili_jct, + ) + dotenv.set_key( + dotenv_path=rf"{os.getcwd()}/.env", + key_to_set="BUVID3", + value_to_set=self.track_helper._bilibilic.buvid3, + ) + await asyncio.sleep(120) + @app_commands.command(name="help", description="❓ | 不知道怎麼使用我嗎?來這裡就對了~") async def help(self, interaction: discord.Interaction): await self.ui.Changelogs.SendChangelogs(interaction) @@ -617,11 +601,7 @@ async def end_session( ): return guild = member.guild - channel = self[guild.id].text_channel - if self[guild.id]._timer_done: - self[guild.id]._timer_done = False - await self.ui.Leave.LeaveOnTimeout(channel) - elif after.channel is None: + if after.channel is None: await self._leave(member.guild) self._cleanup(guild) @@ -630,14 +610,17 @@ async def _get_current_stats(self): print(f"[Stats] Currently playing in {active_player}/{len(self.bot.guilds)} guilds ({round(active_player/len(self.bot.guilds), 3) * 100}% Usage)") + @commands.Cog.listener() + async def on_wavelink_inactive_player(self, player: wavelink.Player): + channel = self[player.guild.id].text_channel + await self._leave(player.guild) + await self.ui.Leave.LeaveOnTimeout(channel) + @commands.Cog.listener() async def on_wavelink_track_start(self, payload: wavelink.TrackStartEventPayload): await self._get_current_stats() try: guild = discord.Object(payload.track.extras.requested_guild) - if self[guild.id]._timer is not None: - self[guild.id]._timer.cancel() - self[guild.id]._timer = None if self.ui_guild_info(guild.id).playinfo is None: await self.ui.PlayerControl.PlayingMsg(self[guild.id].text_channel) else: @@ -658,11 +641,9 @@ async def on_wavelink_track_end(self, payload: wavelink.TrackEndEventPayload): if len(self._playlist[guild.id].order) == 0: if not ( (self.ui_guild_info(guild.id).leaveoperation) - or (self[guild.id]._timer_done) ): self.ui_guild_info(guild.id).leaveoperation = False await self.ui.PlayerControl.DonePlaying(self[guild.id].text_channel) - self._start_timer(guild) return else: await self.track_helper.process_suggestion(guild, self.ui_guild_info(guild.id)), diff --git a/music_comp/playlist.py b/music_comp/playlist.py index 17a3baf..71e3fde 100644 --- a/music_comp/playlist.py +++ b/music_comp/playlist.py @@ -166,7 +166,9 @@ async def add_songs( else: for track in trackinfo: track.requester = requester - track.extras = {"requested_guild": guild_id} + extras = dict(track.extras) + extras["requested_guild"] = guild_id + track.extras = extras try: if track.suggested != True or track.suggested is None: track.suggested = False diff --git a/music_comp/ui_comp/info.py b/music_comp/ui_comp/info.py index b58b488..95b6deb 100644 --- a/music_comp/ui_comp/info.py +++ b/music_comp/ui_comp/info.py @@ -65,6 +65,8 @@ def _SongInfo( # This part for non-playing state if len(playlist.order) == 0: + footer_notice = "" + # String that shows when bot leaving if isinstance(operation, LeaveType): match operation: @@ -165,18 +167,27 @@ def _SongInfo( playing_state = "" notice = "" + if song.source != "spotify" and song.source != "youtube": + title = song.extras.title + author = song.extras.author + length = song.extras.duration + else: + title = song.title + author = song.author + length = song.length + # Generate Time Field if not song.source == "spotify" and song.is_stream: embed = discord.Embed( - title=f"{song.title}", - description=f"**{song.author}**\n*🔴 直播*{notice}", + title=f"{title}", + description=f"**{author}**\n*🔴 直播*{notice}", colour=color, ) else: - time_string = self._sec_to_hms((song.length) / 1000, "zh") + time_string = self._sec_to_hms((length) / 1000, "zh") embed = discord.Embed( - title=f"{song.title}", - description=f"**{song.author}**\n*{time_string}*{notice}", + title=f"{title}", + description=f"**{author}**\n*{time_string}*{notice}", colour=color, ) @@ -309,7 +320,7 @@ def _SongInfo( inline=False, ) - if ("bilibili" in song.uri): + if (song.source == "http"): embed_opt["footer"]["text"] = ( "【!】bilibili 播放測試 | 此功能僅供試用,不保證穩定\n" + embed_opt["footer"]["text"] ) diff --git a/music_comp/ui_comp/survey.py b/music_comp/ui_comp/survey.py index e39fb35..4a6b146 100644 --- a/music_comp/ui_comp/survey.py +++ b/music_comp/ui_comp/survey.py @@ -14,21 +14,23 @@ def __init__(self): from ..ui import musicbot, auto_stage_available, guild_info self.enabled = False - self.survey_displayname = "TKablent 2024 年度 4/5 月使用者意見調查" - self.survey_description = "感謝貴伺服器使用 TKablent\n近期機器人已被超過 **1000** 伺服器所使用\n故想要透過此問卷來知道使用者們**想要的功能、改進**\n及您對於我們機器人的體驗評價" - self._survey_filename = "202404_05_usual" + if self.enabled: + self.survey_displayname = "TKablent 2024 年度 4/5 月使用者意見調查" + self.survey_description = "感謝貴伺服器使用 TKablent\n近期機器人已被超過 **1000** 伺服器所使用\n故想要透過此問卷來知道使用者們**想要的功能、改進**\n及您對於我們機器人的體驗評價" - self._file_name = rf"{os.getcwd()}/music_comp/surveys/{self._survey_filename}_survey.json" - self._survey_thread = 642704558996586496 + self._survey_filename = "202404_05_usual" - self._bot: commands.Bot = musicbot.bot - self._musicbot = musicbot - self._auto_stage_available = auto_stage_available - self._guild_info = guild_info + self._file_name = rf"{os.getcwd()}/music_comp/surveys/{self._survey_filename}_survey.json" + self._survey_thread = 642704558996586496 - with open(self._file_name, "r") as f: - self._survey = json.load(f) + self._bot: commands.Bot = musicbot.bot + self._musicbot = musicbot + self._auto_stage_available = auto_stage_available + self._guild_info = guild_info + + with open(self._file_name, "r") as f: + self._survey = json.load(f) def survey(self, item) -> dict: return self._survey[item] diff --git a/music_comp/utils/cache.py b/music_comp/utils/cache.py index 5b8e463..f4ebf55 100644 --- a/music_comp/utils/cache.py +++ b/music_comp/utils/cache.py @@ -17,7 +17,12 @@ def __init__(self): try: with open(self._cache_path, "rb") as f: self._cache: dict = json_decode(f.read()) - + + shutil.copyfile(self._cache_path, self._bak_cache_path) + except FileNotFoundError: + with open(self._cache_path, "wb") as f: + f.write(b"{}") + self._cache = {} shutil.copyfile(self._cache_path, self._bak_cache_path) except DecodeError: with open(self._bak_cache_path, "rb") as bak_f: diff --git a/music_comp/utils/track_helper.py b/music_comp/utils/track_helper.py index 0049008..9060420 100644 --- a/music_comp/utils/track_helper.py +++ b/music_comp/utils/track_helper.py @@ -37,7 +37,6 @@ def __init__(self, ui_comp: UI, playlist: Playlist): BILI_JCT = os.getenv("BILI_JCT") BUVID3 = os.getenv("BUVID3") DEDEUSERID = os.getenv("DEDEUSERID") - AC_TIME_VALUE = os.getenv("AC_TIME_VALUE") self.ui = ui_comp self._cacheworker: CacheWorker = CacheWorker() @@ -51,7 +50,6 @@ def __init__(self, ui_comp: UI, playlist: Playlist): bili_jct=BILI_JCT, buvid3=BUVID3, dedeuserid=DEDEUSERID, - ac_time_value=AC_TIME_VALUE, ) def __getitem__(self, guild_id: int) -> PlaylistBase: @@ -115,7 +113,22 @@ async def get_search_suggest( self, interaction: discord.Interaction, current: str ) -> List[app_commands.Choice[str]]: if validators.url(current) or current == "": - return [] + if current == "": + return [] + + if ("spotify" in current): + follow_text = " Spotify 曲目" + elif ("bilibili" in current) or ("b23.tv" in current): + follow_text = " Bilibili 曲目" + elif ("soundcloud" in current): + follow_text = " SoundCloud 曲目" + else: + follow_text = "曲目" + + return [app_commands.Choice( + name=f"透過 URL 點播{follow_text}", + value=f"{current}", + )] else: tracks = await self.get_track(interaction, current, quick_search=True) result = [] @@ -163,9 +176,11 @@ async def _get_bilibili_track(self, interaction: discord.Interaction, search: st detector = bilibili.video.VideoDownloadURLDataDetecter(download_url_data) data = detector.detect_all() + data.reverse() for t in data: if isinstance(t, bilibili.video.AudioStreamDownloadURL): - raw_url = t.url.replace("&", "%26") + #raw_url = t.url.replace("&", "%26") + raw_url = t.url try: trackinfo = await wavelink.Pool.fetch_tracks(raw_url) except Exception as e: @@ -173,7 +188,7 @@ async def _get_bilibili_track(self, interaction: discord.Interaction, search: st continue break else: - raw_url = None + break if raw_url == None: return None @@ -185,11 +200,12 @@ async def _get_bilibili_track(self, interaction: discord.Interaction, search: st track = trackinfo[0] vinfo = await v_data.get_info() - track.author = vinfo["owner"]["name"] - track.duration = vinfo["duration"] * 1000 - track.identifier = vinfo["bvid"] - track.is_seekable = True - track.title = vinfo["title"] + track.extras = { + "title": vinfo["title"], + "author": vinfo["owner"]["name"], + "identifier": vinfo["bvid"], + "duration": vinfo["duration"] * 1000, + } return track @@ -234,7 +250,7 @@ async def get_track( elif track is None: return [None] else: - tracks.extend(track) + tracks.append(track) elif (validators.url(search)) or ("sid=>" in search): url = self._parse_url(search, choice)