mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-16 14:34:17 +02:00
Compare commits
2 Commits
main
...
update-sub
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d203b114a8 | ||
|
|
0e4c07b5b5 |
370
SunnexGB/Heroku-Modules/LiveLyrics.py
Normal file
370
SunnexGB/Heroku-Modules/LiveLyrics.py
Normal file
@@ -0,0 +1,370 @@
|
||||
# meta developer: @SunnexGB
|
||||
# requires: aiohttp
|
||||
# meta pic: https://r2.fakecrime.bio/uploads/6725e5a0-0c9e-48ed-be85-dfd857c2aa5f.jpg
|
||||
# meta banner: https://r2.fakecrime.bio/uploads/6725e5a0-0c9e-48ed-be85-dfd857c2aa5f.jpg
|
||||
# meta fhsdesc: Spotify, YaMusic, music, музыка, Lyrics, слова, текст, трек, песня
|
||||
|
||||
__version__ = (1, 0, 0)
|
||||
|
||||
from herokutl.types import Message
|
||||
from .. import loader, utils
|
||||
from ..types import InlineCall
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import re
|
||||
|
||||
@loader.tds
|
||||
class LiveLyrics(loader.Module):
|
||||
"""life lyrics current song"""
|
||||
|
||||
strings = {
|
||||
"name": "LiveLyrics",
|
||||
"no_spotifymod": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>SpotifyMod not found,but u can install it. You can also support developer: </b> @ke_mods",
|
||||
"no_yamusic": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>YaMusicMod not found,but u can install it. You can also support developer: </b> @codrago_m",
|
||||
"no_auth_spotify": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> You not authorized in SpotifyMod, visit you Saved Messages.</b>",
|
||||
"no_auth_yamusic": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> You not authorized in SpotifyMod, visit you Saved Messages and setup token to continue.</b>",
|
||||
"no_spotify": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>Nothing is playing on Spotify.</b>",
|
||||
"no_ym": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>Nothing is playing on YaMusic.</b>",
|
||||
"no_lyrics": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>Lyrics not found for:</b> <code>{}</code>",
|
||||
"not_synced": "<i><tg-emoji emoji-id=5431445849026611010>⚠️</tg-emoji> Lyrics are not synchronized.</i>\n\n",
|
||||
"TrackEnded": "<tg-emoji emoji-id=5429638011392377649>‼️</tg-emoji> Playback ended or track changed.",
|
||||
"header": "<tg-emoji emoji-id=5429413328768224565>🎤</tg-emoji> <b>{} - {}</b>\n\n",
|
||||
"timeout": "<b><tg-emoji emoji-id=5429455831764584284>⏳</tg-emoji></b><b> Oopsi, looks like we've got a timeout here</b>.",
|
||||
"yamusic_installed": "YaMusic installed!",
|
||||
"spotify_installed": "SpotifyMod installed!",
|
||||
"song_link": "🔗 song.link",
|
||||
"close": "❌ Close",
|
||||
"ok": "OK",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"_cls_doc": "Лайв слова текущей песни.",
|
||||
"no_spotifymod": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>SpotifyMod не найден,но его можно установить. Вы также можете поддержать разработчика: </b> @ke_mods",
|
||||
"no_yamusic": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>YaMusicMod не найден, но его можно установить. Вы также можете поддержать разработчика: </b> @codrago_m",
|
||||
"no_auth_spotify": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji> <b>Вы не авторизованы в SpotifyMod. Перейдите в Избранное.</b>",
|
||||
"no_auth_yamusic": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> Вы не авторизированы в YaMusicMod, перейдите в Избранное и установите токен для продолжения работы.</b>",
|
||||
"no_spotify": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>В Spotify ничего не играет.</b>",
|
||||
"no_ym": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>В YaMusic ничего не играет.</b>",
|
||||
"no_lyrics": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>Текст не найден для:</b> <code>{}</code>",
|
||||
"not_synced": "<i><tg-emoji emoji-id=5431445849026611010>⚠️</tg-emoji> Текст не синхронизирован.</i>\n\n",
|
||||
"TrackEnded": "<tg-emoji emoji-id=5429638011392377649>‼️</tg-emoji> Воспроизведение завершено или трек сменился.",
|
||||
"header": "<tg-emoji emoji-id=5429413328768224565>🎤</tg-emoji> <b>{} - {}</b>\n\n",
|
||||
"timeout": "<b><tg-emoji emoji-id=5429455831764584284>⏳</tg-emoji></b><b> Упси, похоже кто то словил таймаут.</b>.",
|
||||
"yamusic_installed": "YaMusic Установлен!",
|
||||
"spotify_installed": "SpotifyMod Установлен!",
|
||||
"song_link": "🔗 song.link",
|
||||
"close": "❌ Закрыть",
|
||||
"ok": "OK",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self._active_tasks: dict = {}
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"emoji_current",
|
||||
"<tg-emoji emoji-id='5215679757366089921'>🤯</tg-emoji>",
|
||||
"Emoji for the current line",
|
||||
validator=loader.validators.String(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"dot",
|
||||
"♪",
|
||||
"instrumental_emoji or text",
|
||||
validator=loader.validators.String(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"text_lines",
|
||||
"6",
|
||||
"Count lines in message, with synchronized text",
|
||||
validator=loader.validators.Integer(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"lyrics_delay",
|
||||
0.5,
|
||||
"delay in switching to a new timing sector with words",
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"request_timeout",
|
||||
12,
|
||||
"timeout value",
|
||||
validator=loader.validators.Integer(),
|
||||
),
|
||||
)
|
||||
|
||||
async def install_mod(self, call: InlineCall, heroku_module: str):
|
||||
if heroku_module == "SpotifyMod":
|
||||
download_url = "https://raw.githubusercontent.com/radiocycle/Modules/refs/heads/master/SpotifyMod.py"
|
||||
module_name = "SpotifyMod"
|
||||
installed_btn = self.strings["spotify_installed"]
|
||||
no_auth_str = self.strings["no_auth_spotify"]
|
||||
auth_command = "sauth"
|
||||
else:
|
||||
download_url = "https://raw.githubusercontent.com/coddrago/modules/main/YaMusic.py"
|
||||
module_name = "YaMusic"
|
||||
installed_btn = self.strings["yamusic_installed"]
|
||||
no_auth_str = self.strings["no_auth_yamusic"]
|
||||
auth_command = "yguide"
|
||||
try:
|
||||
m = self.lookup("loader")
|
||||
await m.download_and_install(download_url)
|
||||
await call.answer(installed_btn, show_alert=True)
|
||||
mod = self.lookup(module_name)
|
||||
if heroku_module == "SpotifyMod":
|
||||
authorized = mod and mod.get("acs_tkn")
|
||||
else:
|
||||
authorized = mod and mod.get("__config__")["token"]
|
||||
if not authorized:
|
||||
await self.invoke(auth_command, " ", "me")
|
||||
await call.edit(
|
||||
no_auth_str,
|
||||
reply_markup=[
|
||||
[
|
||||
{"text": self.strings["ok"], "callback": self.close}
|
||||
]
|
||||
],
|
||||
)
|
||||
else:
|
||||
await call.delete()
|
||||
except Exception as e:
|
||||
await call.answer(f"Error: {e}", show_alert=True)
|
||||
|
||||
def close(self, call: InlineCall):
|
||||
return call.delete()
|
||||
|
||||
async def get_lyrics(self, artist: str, track: str):
|
||||
ClearTimeSections = re.sub(r"\(.*?\)|\[.*?\]", "", track).strip()
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
"https://lrclib.net/api/search",
|
||||
params={"track_name": ClearTimeSections, "artist_name": artist},
|
||||
timeout=aiohttp.ClientTimeout(total=self.config["request_timeout"]),
|
||||
) as resp:
|
||||
if resp.status == 200:
|
||||
result = await resp.json()
|
||||
return result[0] if result else None
|
||||
except asyncio.TimeoutError:
|
||||
return {"timeout": True}
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def parse_synced(self, synced_text: str) -> list:
|
||||
lines = []
|
||||
for line in synced_text.split("\n"):
|
||||
m = re.search(r"\[(\d+):(\d+\.\d+)\](.*)", line)
|
||||
if m:
|
||||
mins, secs, text = m.groups()
|
||||
lines.append({
|
||||
"time": (int(mins) * 60 + float(secs)) * 1000,
|
||||
"text": text.strip(),
|
||||
})
|
||||
return lines
|
||||
|
||||
def build_lyrics(self, artist, track, lines, plain, progress_ms, not_synced_str):
|
||||
header = self.strings["header"].format(
|
||||
utils.escape_html(artist),
|
||||
utils.escape_html(track),
|
||||
)
|
||||
if lines:
|
||||
curr_idx = 0
|
||||
for i, line in enumerate(lines):
|
||||
if progress_ms >= line["time"]:
|
||||
curr_idx = i
|
||||
l_start = max(0, curr_idx - 1)
|
||||
l_end = min(len(lines), l_start + self.config["text_lines"])
|
||||
rows = []
|
||||
for i in range(l_start, l_end):
|
||||
t = lines[i]["text"] or self.config["dot"]
|
||||
if i == curr_idx:
|
||||
rows.append(f"<b>{self.config['emoji_current']} {utils.escape_html(t)}</b>")
|
||||
else:
|
||||
rows.append(f"<code>{utils.escape_html(t)}</code>")
|
||||
return header + "\n".join(rows)
|
||||
return header + not_synced_str + f"<blockquote expandable>{utils.escape_html((plain or '')[:4000])}</blockquote>"
|
||||
|
||||
def build_keyboard(self, song_url):
|
||||
return [
|
||||
[
|
||||
{"text": self.strings["song_link"], "url": song_url}
|
||||
],
|
||||
[
|
||||
{"text": self.strings["close"], "callback": self.close_cb}
|
||||
],
|
||||
]
|
||||
|
||||
async def close_cb(self, call: InlineCall):
|
||||
for track_id, task in list(self._active_tasks.items()):
|
||||
task.cancel()
|
||||
self._active_tasks.pop(track_id, None)
|
||||
try:
|
||||
await call.answer()
|
||||
await call.delete()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
async def za_loop_a(self, form, mod, track_id, artist_name, track_name, song_url, lines, plain, not_synced_str, heroku_module: str):
|
||||
buffer_clipboard = ""
|
||||
try:
|
||||
while True:
|
||||
if heroku_module == "SpotifyMod":
|
||||
pb = mod.sp.current_playback()
|
||||
TrackEnded = not pb or not pb.get("item") or pb["item"]["id"] != track_id
|
||||
else:
|
||||
pb = await mod._YaMusicMod__get_now_playing()
|
||||
TrackEnded = not pb or not pb.get("track") or pb["track"]["track_id"] != track_id
|
||||
if TrackEnded:
|
||||
try:
|
||||
await form.edit(
|
||||
self.strings["TrackEnded"],
|
||||
reply_markup=[[{"text": self.strings["close"], "callback": self.close_cb}]],
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
break
|
||||
prog = pb.get("progress_ms", 0)
|
||||
content = self.build_lyrics(artist_name, track_name, lines, plain, prog, not_synced_str)
|
||||
if content != buffer_clipboard:
|
||||
try:
|
||||
await form.edit(content, reply_markup=self.build_keyboard(song_url))
|
||||
buffer_clipboard = content
|
||||
except Exception:
|
||||
break
|
||||
if not lines:
|
||||
break
|
||||
await asyncio.sleep(self.config["lyrics_delay"])
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
self._active_tasks.pop(track_id, None)
|
||||
|
||||
@loader.command(ru_doc="- показать синхронизированный текст песни")
|
||||
async def snowlcmd(self, message: Message):
|
||||
"""- show synchronized lyrics for current track"""
|
||||
mod = self.lookup("SpotifyMod")
|
||||
if not mod:
|
||||
form = await self.inline.form("⏳", message=message)
|
||||
await form.edit(
|
||||
self.strings["no_spotifymod"],
|
||||
reply_markup=[
|
||||
[
|
||||
{
|
||||
"text": self.strings["spotify_installed"],
|
||||
"callback": self.install_mod,
|
||||
"kwargs": {"heroku_module": "SpotifyMod"}
|
||||
}
|
||||
]
|
||||
],
|
||||
)
|
||||
return
|
||||
if not mod.get("acs_tkn"):
|
||||
await self.invoke("sauth", " ", "me")
|
||||
form = await self.inline.form("⏳", message=message)
|
||||
await form.edit(
|
||||
self.strings["no_auth_spotify"],
|
||||
reply_markup=[[{"text": self.strings["ok"], "callback": self.close}]],
|
||||
)
|
||||
return
|
||||
|
||||
playback = mod.sp.current_playback()
|
||||
if not playback or not playback.get("item"):
|
||||
return await utils.answer(message, self.strings["no_spotify"])
|
||||
track = playback["item"]
|
||||
track_id = track["id"]
|
||||
artist_name = track["artists"][0]["name"]
|
||||
track_name = track["name"]
|
||||
song_url = f"https://song.link/s/{track_id}"
|
||||
|
||||
old = self._active_tasks.pop(track_id, None)
|
||||
if old:
|
||||
old.cancel()
|
||||
|
||||
data = await self.get_lyrics(artist_name, track_name)
|
||||
if data and data.get("timeout"):
|
||||
return await utils.answer(message, self.strings["timeout"])
|
||||
if not data or data.get("instrumental"):
|
||||
return await utils.answer(
|
||||
message,
|
||||
self.strings["no_lyrics"].format(f"{utils.escape_html(track_name)} - {utils.escape_html(artist_name)}"),
|
||||
)
|
||||
|
||||
synced_raw = data.get("syncedLyrics")
|
||||
plain = data.get("plainLyrics", "")
|
||||
lines = self.parse_synced(synced_raw) if synced_raw else []
|
||||
not_synced_str = self.strings["not_synced"]
|
||||
form = await self.inline.form(
|
||||
text=self.build_lyrics(artist_name, track_name, lines, plain, playback.get("progress_ms", 0), not_synced_str),
|
||||
message=message,
|
||||
reply_markup=self.build_keyboard(song_url),
|
||||
)
|
||||
|
||||
self._active_tasks[track_id] = asyncio.ensure_future(
|
||||
self.za_loop_a(form, mod, track_id, artist_name, track_name, song_url, lines, plain, not_synced_str, heroku_module="SpotifyMod")
|
||||
)
|
||||
|
||||
@loader.command(ru_doc="- показать синхронизированный текст песни")
|
||||
async def ynowlcmd(self, message: Message):
|
||||
"""- show synchronized lyrics for current track"""
|
||||
mod = self.lookup("YaMusic")
|
||||
if not mod:
|
||||
form = await self.inline.form("⏳", message=message)
|
||||
await form.edit(
|
||||
self.strings["no_yamusic"],
|
||||
reply_markup=[
|
||||
[
|
||||
{
|
||||
"text": "Install YaMusicMod",
|
||||
"callback": self.install_mod,
|
||||
"kwargs": {"heroku_module": "YaMusic"}
|
||||
}
|
||||
]
|
||||
],
|
||||
)
|
||||
return
|
||||
if not mod.get("__config__")["token"]:
|
||||
await self.invoke("yguide", " ", "me")
|
||||
form = await self.inline.form("⏳", message=message)
|
||||
await form.edit(
|
||||
self.strings["no_auth_yamusic"],
|
||||
reply_markup=[[{"text": self.strings["ok"], "callback": self.close}]],
|
||||
)
|
||||
return
|
||||
|
||||
playback = await mod._YaMusicMod__get_now_playing()
|
||||
if not playback or not playback.get("track"):
|
||||
return await utils.answer(message, self.strings["no_ym"])
|
||||
track = playback["track"]
|
||||
track_id = track["track_id"]
|
||||
artist_name = ", ".join(track["artist"])
|
||||
track_name = track["title"]
|
||||
song_url = f"https://song.link/s/{track_id}"
|
||||
|
||||
old = self._active_tasks.pop(track_id, None)
|
||||
if old:
|
||||
old.cancel()
|
||||
|
||||
data = await self.get_lyrics(artist_name, track_name)
|
||||
if data and data.get("timeout"):
|
||||
return await utils.answer(message, self.strings["timeout"])
|
||||
if not data or data.get("instrumental"):
|
||||
return await utils.answer(
|
||||
message,
|
||||
self.strings["no_lyrics"].format(f"{utils.escape_html(track_name)} - {utils.escape_html(artist_name)}"),
|
||||
)
|
||||
|
||||
synced_raw = data.get("syncedLyrics")
|
||||
plain = data.get("plainLyrics", "")
|
||||
lines = self.parse_synced(synced_raw) if synced_raw else []
|
||||
not_synced_str = self.strings["not_synced"]
|
||||
|
||||
form = await self.inline.form(
|
||||
text=self.build_lyrics(artist_name, track_name, lines, plain, playback.get("progress_ms", 0), not_synced_str),
|
||||
message=message,
|
||||
reply_markup=self.build_keyboard(song_url),
|
||||
)
|
||||
|
||||
self._active_tasks[track_id] = asyncio.ensure_future(
|
||||
self.za_loop_a(form, mod, track_id, artist_name, track_name, song_url, lines, plain, not_synced_str, heroku_module="YaMusic")
|
||||
)
|
||||
@@ -1,285 +1,241 @@
|
||||
# meta developer: @SunnexGB
|
||||
# requires: aiohttp
|
||||
# meta pic: https://r2.fakecrime.bio/uploads/f49a9294-36ad-4fc4-801f-48cb049111d6.jpg
|
||||
# meta banner: https://r2.fakecrime.bio/uploads/f49a9294-36ad-4fc4-801f-48cb049111d6.jpg
|
||||
# meta fhsdesc: Spotify, music, музыка, спотифай,Lyrics, слова, текст, трек, песня
|
||||
# все же я не знаю трек или сонг, так что пусть будет трек, а не сонг потому что интуитивнее поняттнее,наверное?
|
||||
# крутой баннер да?
|
||||
#current version
|
||||
__version__ = (1, 1, 2)
|
||||
__version__ = (1, 1, 3)
|
||||
|
||||
from herokutl.types import Message
|
||||
from .. import loader, utils
|
||||
from ..types import InlineCall
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import re
|
||||
|
||||
|
||||
@loader.tds
|
||||
class SpotifyLyrics(loader.Module):
|
||||
"""life lyrics current song"""
|
||||
|
||||
strings = {
|
||||
"name": "SpotifyLyrics",
|
||||
"no_spotifymod": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>SpotifyMod not found,but u can install it. You can also support developer: </b> @ke_mods",
|
||||
"no_auth": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> You not authorized in SpotifyMod, visit you Saved Messages.</b>",
|
||||
"no_spotify": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>Nothing is playing on Spotify.</b>",
|
||||
"no_lyrics": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>Lyrics not found for:</b> <code>{}</code>",
|
||||
"not_synced": "<i><tg-emoji emoji-id=5431445849026611010>⚠️</tg-emoji> Lyrics are not synchronized.</i>\n\n",
|
||||
"finished": "<tg-emoji emoji-id=5429638011392377649>‼️</tg-emoji> Playback ended or track changed.",
|
||||
"header": "<tg-emoji emoji-id=5429413328768224565>🎤</tg-emoji> <b>{} - {}</b>\n\n",
|
||||
"timeout": "<b><tg-emoji emoji-id=5429455831764584284>⏳</tg-emoji></b><b> Oopsi, looks like we've got a timeout here</b>.",
|
||||
"name": "migration",
|
||||
"no_SpotifyLyrics": "u're not use SpotifyLyrics,install my new module.",
|
||||
"inline_msg": "if u ready to migration pls click on the button.",
|
||||
"already": "module already install, u're ready for migration ur db?",
|
||||
"restartf": "u ready restart ur ub?",
|
||||
"restartf_btn": "Ok",
|
||||
"done": "migration is done.",
|
||||
"confirm": "confirm",
|
||||
"install": "install LiveLyrics",
|
||||
"close": "close",
|
||||
"migrate_db": "Yep"
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"cls_doc": "Лайв слова текущей песни.",
|
||||
"no_spotifymod": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>SpotifyMod не найден,но его можно установить. Вы также можете поддержать разработчика: </b> @ke_mods",
|
||||
"no_auth": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> Вы не авторизированы в SpotifyMod, перейдите в Избранное.</b>",
|
||||
"no_spotify": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>В Spotify ничего не играет.</b>",
|
||||
"no_lyrics": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>Текст не найден для:</b> <code>{}</code>",
|
||||
"not_synced": "<i><tg-emoji emoji-id=5431445849026611010>⚠️</tg-emoji> Текст не синхронизирован.</i>\n\n",
|
||||
"finished": "<tg-emoji emoji-id=5429638011392377649>‼️</tg-emoji> Воспроизведение завершено или трек сменился.",
|
||||
"header": "<tg-emoji emoji-id=5429413328768224565>🎤</tg-emoji> <b>{} - {}</b>\n\n",
|
||||
"timeout": "<b><tg-emoji emoji-id=5429455831764584284>⏳</tg-emoji></b><b> Упси, похоже кто то словил таймаут.</b>.",
|
||||
"no_SpotifyLyrics": "ты не юзал SpotifyLyrics,установи новый модуль.",
|
||||
"inline_msg": "если вы готовы к миграции нажмите на кнопку ниже.",
|
||||
"already": "модуль уже стоиит,готовы к переносу данных?",
|
||||
"restartf": "вы готовы к перезапуску юб?",
|
||||
"restartf_btn": "Лан",
|
||||
"done": "миграция завершена.",
|
||||
"confirm": "подтвердить",
|
||||
"install": "установить LiveLyrics",
|
||||
"close": "закрыть",
|
||||
"migrate_db": "Ага"
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self._active_tasks: dict = {}
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"emoji_current",
|
||||
"<tg-emoji emoji-id='5215679757366089921'>🤯</tg-emoji>",
|
||||
"Emoji for current line",
|
||||
validator=loader.validators.String(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"dot",
|
||||
"♪",
|
||||
"instrumental_emoji or text",
|
||||
validator=loader.validators.String(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"lyrics_delay",
|
||||
0.5,
|
||||
"delay in switching to a new timing sector with words",
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"request_timeout",
|
||||
12,
|
||||
"timeout value",
|
||||
),
|
||||
async def install_livelyrics(self, call: InlineCall):
|
||||
m = self.lookup("Modules") or self.lookup("loader")
|
||||
await m.download_and_install("https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/refs/heads/main/LiveLyrics.py")
|
||||
await call.delete()
|
||||
|
||||
async def confirm(self, call: InlineCall):
|
||||
config = self.db.get("SpotifyLyrics", "__config__") or {}
|
||||
self.db.set("LiveLyrics", "__config__", config)
|
||||
await asyncio.sleep(1)
|
||||
await self.invoke("unloadmod", "-f SpotifyLyrics", "me")
|
||||
await call.edit(self.strings["done"])
|
||||
await asyncio.sleep(2)
|
||||
await call.edit(
|
||||
self.strings["restartf"],
|
||||
reply_markup=[
|
||||
[{"text": self.strings["restartf_btn"], "callback": self.restartf}],
|
||||
[{"text": self.strings["close"], "callback": self.close}],
|
||||
],
|
||||
)
|
||||
|
||||
async def migrate_db(self, call: InlineCall):
|
||||
await call.edit(
|
||||
self.strings["inline_msg"],
|
||||
reply_markup=[
|
||||
[{"text": self.strings["confirm"], "callback": self.confirm}],
|
||||
[{"text": self.strings["close"], "callback": self.close}],
|
||||
],
|
||||
)
|
||||
|
||||
async def install_spotifymod(self, call: InlineCall):
|
||||
mod_url = "https://raw.githubusercontent.com/radiocycle/Modules/refs/heads/master/SpotifyMod.py"
|
||||
try:
|
||||
m = self.lookup("Modules") or self.lookup("loader")
|
||||
await m.download_and_install(mod_url)
|
||||
await call.answer("SpotifyMod installed!", show_alert=True)
|
||||
mod = self.lookup("SpotifyMod")
|
||||
acs_tkn = mod.get("acs_tkn") if mod else None
|
||||
if not acs_tkn:
|
||||
await self.invoke("sauth", " ", "me")
|
||||
await call.edit(
|
||||
self.strings("no_auth"),
|
||||
reply_markup=[[{"text": "Хорошо", "callback": self.close}]],
|
||||
)
|
||||
else:
|
||||
await call.delete()
|
||||
except Exception as e:
|
||||
await call.answer(f"Error! Check logs.\n{e}", show_alert=True)
|
||||
async def restartf(self, call: InlineCall):
|
||||
await call.delete()
|
||||
await self.invoke("restart", "-f", "me")
|
||||
|
||||
def close(self, call: InlineCall):
|
||||
return call.delete()
|
||||
async def close(self, call: InlineCall):
|
||||
await call.delete()
|
||||
|
||||
async def _get_lyrics(self, artist: str, track: str):
|
||||
clean_track = re.sub(r"\(.*?\)|\[.*?\]", "", track).strip()
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
"https://lrclib.net/api/search",
|
||||
params={"track_name": clean_track, "artist_name": artist},
|
||||
timeout=aiohttp.ClientTimeout(total=(self.config["request_timeout"])),
|
||||
) as resp:
|
||||
if resp.status == 200:
|
||||
res = await resp.json()
|
||||
return res[0] if res else None
|
||||
except asyncio.TimeoutError:
|
||||
return {"timeout": True}
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def _parse_synced(self, synced_text: str) -> list:
|
||||
lines = []
|
||||
for line in synced_text.split("\n"):
|
||||
m = re.search(r"\[(\d+):(\d+\.\d+)\](.*)", line)
|
||||
if m:
|
||||
mins, secs, text = m.groups()
|
||||
lines.append({
|
||||
"time": (int(mins) * 60 + float(secs)) * 1000,
|
||||
"text": text.strip(),
|
||||
})
|
||||
return lines
|
||||
|
||||
def _build_content(self, artist, track, lines, plain, progress_ms, not_synced_str):
|
||||
header = self.strings("header").format(
|
||||
utils.escape_html(artist),
|
||||
utils.escape_html(track),
|
||||
)
|
||||
if lines:
|
||||
curr_idx = 0
|
||||
for i, line in enumerate(lines):
|
||||
if progress_ms >= line["time"]:
|
||||
curr_idx = i
|
||||
win_start = max(0, curr_idx - 1)
|
||||
win_end = min(len(lines), curr_idx + 6)
|
||||
rows = []
|
||||
for i in range(win_start, win_end):
|
||||
t = lines[i]["text"] or self.config["dot"]
|
||||
if i == curr_idx:
|
||||
rows.append(
|
||||
f"<b>{self.config['emoji_current']} {utils.escape_html(t)}</b>"
|
||||
)
|
||||
else:
|
||||
rows.append(f"<code>{utils.escape_html(t)}</code>")
|
||||
return header + "\n".join(rows)
|
||||
else:
|
||||
return header + not_synced_str + f"<blockquote expandable>{utils.escape_html((plain or '')[:4000])}</blockquote>"
|
||||
|
||||
def _markup(self, song_url):
|
||||
return [
|
||||
[{"text": "🔗 song.link", "url": song_url}],
|
||||
[{"text": "❌ Close", "callback": self._close_cb}],
|
||||
]
|
||||
|
||||
async def _close_cb(self, call):
|
||||
for track_id, task in list(self._active_tasks.items()):
|
||||
task.cancel()
|
||||
self._active_tasks.pop(track_id, None)
|
||||
try:
|
||||
await call.answer()
|
||||
await call.delete()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
async def run_loop(self, form, mod, track_id, artist_name, track_name, song_url, lines, plain, not_synced_str):
|
||||
last_display = ""
|
||||
try:
|
||||
while True:
|
||||
pb = mod.sp.current_playback()
|
||||
if not pb or not pb.get("item") or pb["item"]["id"] != track_id:
|
||||
try:
|
||||
await form.edit(
|
||||
self.strings("finished"),
|
||||
reply_markup=[[{"text": "❌ Close", "callback": self._close_cb}]],
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
break
|
||||
|
||||
prog = pb.get("progress_ms", 0)
|
||||
content = self._build_content(
|
||||
artist_name, track_name, lines, plain, prog, not_synced_str
|
||||
)
|
||||
|
||||
if content != last_display:
|
||||
try:
|
||||
await form.edit(content, reply_markup=self._markup(song_url))
|
||||
last_display = content
|
||||
except Exception:
|
||||
break
|
||||
|
||||
if not lines:
|
||||
break
|
||||
|
||||
await asyncio.sleep(self.config["lyrics_delay"])
|
||||
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
self._active_tasks.pop(track_id, None)
|
||||
|
||||
@loader.command(ru_doc="- показать синхронизированный текст песни")
|
||||
async def snowlcmd(self, message: Message):
|
||||
"""- show synchronized lyrics for current Spotify track"""
|
||||
mod = self.lookup("SpotifyMod")
|
||||
if not mod:
|
||||
form = await self.inline.form("⏳", message=message)
|
||||
await form.edit(
|
||||
self.strings("no_spotifymod"),
|
||||
reply_markup=[[{"text": "Install SpotifyMod", "callback": self.install_spotifymod}]],
|
||||
)
|
||||
return
|
||||
|
||||
acs_tkn = mod.get("acs_tkn")
|
||||
if not acs_tkn:
|
||||
await self.invoke("sauth", " ", "me")
|
||||
form = await self.inline.form("⏳", message=message)
|
||||
await form.edit(
|
||||
self.strings("no_auth"),
|
||||
reply_markup=[[{"text": "Хорошо", "callback": self.close}]],
|
||||
)
|
||||
return
|
||||
|
||||
playback = mod.sp.current_playback()
|
||||
if not playback or not playback.get("item"):
|
||||
return await utils.answer(message, self.strings("no_spotify"))
|
||||
|
||||
track = playback["item"]
|
||||
track_id = track["id"]
|
||||
artist_name = track["artists"][0]["name"]
|
||||
track_name = track["name"]
|
||||
song_url = f"https://song.link/s/{track_id}"
|
||||
|
||||
old = self._active_tasks.pop(track_id, None)
|
||||
if old:
|
||||
old.cancel()
|
||||
|
||||
data = await self._get_lyrics(artist_name, track_name)
|
||||
if data and data.get("timeout"):
|
||||
return utils.answer(
|
||||
message,
|
||||
self.strings["timeout"]
|
||||
)
|
||||
if not data or data.get("instrumental"):
|
||||
track_and_artist = f"{utils.escape_html(track_name)} - {utils.escape_html(artist_name)}"
|
||||
@loader.command()
|
||||
async def snowl(self, message: Message):
|
||||
SL_cfg = self.db.get("SpotifyLyrics", "__config__")
|
||||
if SL_cfg is None:
|
||||
return await utils.answer(
|
||||
message,
|
||||
self.strings("no_lyrics").format(track_and_artist),
|
||||
self.strings["no_SpotifyLyrics"],
|
||||
reply_markup=[
|
||||
[{"text": self.strings["install"], "callback": self.install_livelyrics}],
|
||||
[{"text": self.strings["close"], "callback": self.close}],
|
||||
],
|
||||
)
|
||||
|
||||
synced_raw = data.get("syncedLyrics")
|
||||
plain = data.get("plainLyrics", "")
|
||||
|
||||
lines = self._parse_synced(synced_raw) if synced_raw else []
|
||||
not_synced_str = self.strings("not_synced")
|
||||
|
||||
prog = playback.get("progress_ms", 0)
|
||||
initial_content = self._build_content(
|
||||
artist_name, track_name, lines, plain, prog, not_synced_str
|
||||
)
|
||||
|
||||
form = await self.inline.form(
|
||||
text=initial_content,
|
||||
message=message,
|
||||
reply_markup=self._markup(song_url),
|
||||
)
|
||||
|
||||
task = asyncio.ensure_future(
|
||||
self.run_loop(
|
||||
form=form,
|
||||
mod=mod,
|
||||
track_id=track_id,
|
||||
artist_name=artist_name,
|
||||
track_name=track_name,
|
||||
song_url=song_url,
|
||||
lines=lines,
|
||||
plain=plain,
|
||||
not_synced_str=not_synced_str,
|
||||
if self.lookup("LiveLyrics"):
|
||||
return await utils.answer(
|
||||
message,
|
||||
"модуль уже стоиит,готовы к переносу данных",
|
||||
reply_markup=[
|
||||
[{"text": self.strings["migrate_db"], "callback": self.migrate_db}],
|
||||
[{"text": self.strings["close"], "callback": self.close}],
|
||||
],
|
||||
)
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings["inline_msg"],
|
||||
reply_markup=[
|
||||
[{"text": self.strings["confirm"], "callback": self.confirm}],
|
||||
[{"text": self.strings["close"], "callback": self.close}],
|
||||
],
|
||||
)
|
||||
self._active_tasks[track_id] = task
|
||||
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣠⠤⠤⣄⠠⠤⣤⠤⢤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠤⠒⠉⠁⠀⠀⠀⣠⠖⠉⣀⡀⣀⣤⠍⢩⣵⡢⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠖⠋⠁⠀⠀⠀⠀⢀⣴⠊⠀⣠⠞⣠⠞⠉⢳⣦⠀⡈⠻⣦⠑⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⠖⠉⠀⠀⠀⠀⠀⠀⣠⣴⡿⠁⠀⡰⢁⡾⠁⠐⠃⢸⡇⠀⠸⡄⠀⠀⠈⠷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣆⠀⠀⠀⠀⣀⣠⡴⡿⠁⣼⡅⠀⣰⢣⢿⡇⡴⠀⠀⣾⡅⠀⠀⣧⠀⠀⠀⠀⢈⢂⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠛⠻⠿⠿⠷⠒⠒⠋⠁⡏⠸⠁⣸⣿⠇⢀⣧⠏⢸⡿⠁⠀⡼⠙⣧⠀⢀⣿⡄⢀⠠⠀⠘⡄⢡⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠁⣤⣠⣯⢿⠀⢸⣟⡴⡾⣳⣴⣾⡷⢠⠛⢆⣾⣿⡇⡀⠀⣦⠀⡇⠀⢣⠀⠀⠀⠀⠀⠀
|
||||
# ⢠⡄⠀⡄⠀⠀⠀⢀⣒⣒⣶⡀⠀⠀⠀⠀⠀⠀⠀⠀⢼⠀⣿⡿⣿⢸⠐⣿⣿⣿⣿⡿⣶⡿⢷⣯⣮⣾⢯⡟⣇⡇⣸⡏⢠⣠⠀⠀⢣⠀⠀⠀⠀⠀
|
||||
# ⠀⣿⣿⣿⣦⡄⣤⡾⢿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⢸⢠⣿⣿⣿⣼⣇⡿⣟⠛⠛⠃⠀⠀⠁⠉⣠⣶⣷⣄⣿⣼⣿⡆⣸⣧⡀⠀⠀⢆⠀⠀⠀⠀
|
||||
# ⢸⣿⣿⡿⠛⠋⠉⢩⣍⣉⣻⡇⠀⠀⠀⠀⠀⠀⠀⠀⡇⣼⣿⢹⢿⡝⢿⣇⡈⠃⠀⠀⠀⠀⠀⠀⠛⢿⡏⣿⣿⢿⣿⡱⡸⠁⠈⠢⣀⠘⡆⠀⠀⠀
|
||||
# ⠈⣿⡏⣤⠄⠘⠃⠸⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⢰⢱⠻⡇⢸⠸⣷⡀⣿⡁⠀⠀⠀⠀⡀⠀⠀⠀⢀⡼⣯⠁⣾⣿⠁⠀⠀⠀⠀⠈⠑⠃⠀⠀⠀
|
||||
# ⢠⣬⣼⣧⡀⠀⠀⠀⢹⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⣾⣿⡦⠧⠬⣧⡸⣿⣧⠙⢠⡀⠀⠀⠀⠀⣀⡤⠊⠀⣿⣰⢿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⢸⣿⣿⣿⡀⢀⡀⠀⣸⣿⣿⡁⠀⠀⠀⠀⠀⠀⣰⣯⠟⠛⠛⠛⢿⣭⣿⣿⠷⣶⣟⣷⣶⣒⡉⠁⠀⠀⠜⢹⣿⡼⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠈⠛⠛⠛⠛⠛⠛⠓⠛⠛⠛⠃⠀⠀⠀⠀⢀⢼⣿⣏⣰⣿⣷⣴⣦⠙⣿⣿⡛⠛⠿⠥⢯⣀⣈⡇⢀⠀⠚⢙⡞⠑⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⢽⣿⣿⢻⣿⣿⣟⣿⣿⢿⣿⢦⣄⠀⣀⣀⠉⠩⠿⣷⣾⣭⠑⠒⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⠏⠈⣿⣿⣿⣿⣿⣿⣿⣿⣾⣿⣧⢽⣯⣁⠀⠀⠀⠀⠒⠗⢺⣿⣭⣤⣄⠙⠢⣀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⠏⠀⠀⣿⡟⠈⠻⣿⣿⣻⣿⣿⣟⣡⣴⣿⣿⣷⡞⠒⠒⠶⠶⠶⠤⣽⡿⠽⠦⣄⣀⠑⡄⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠋⠀⢀⣴⣿⡇⠀⠀⠉⠻⣥⣽⠿⠋⠙⠻⣽⣿⣤⠃⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠀⠈⠑⠳⣄⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⣠⠊⣼⣿⡇⠀⠀⠐⢿⡿⠁⠀⠀⠀⢀⣀⣩⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢢⡀⠀⠀
|
||||
# ⠀⠀⣠⠤⢤⣀⣀⡀⠀⠀⠀⠀⢠⠏⢀⡜⠁⢰⠇⣿⣿⠀⠀⠀⣈⡇⠀⠀⠀⠀⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢳⠀⠀
|
||||
# ⠀⠀⠳⡀⠀⠀⠀⠉⠉⠙⠓⠒⠳⠤⠯⣄⡀⡞⠀⡿⣡⡴⠞⠋⢙⡇⠀⠀⠀⠀⢟⡛⠀⣿⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀
|
||||
# ⠀⠀⠀⠹⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠁⠈⣯⣉⠀⠀⠀⢸⡇⠀⠀⠀⢰⡶⠀⢠⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡼⠀⠀
|
||||
# ⠀⠀⠀⠀⢹⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣧⠀⢰⠃⠟⢳⣄⠀⣾⡇⠀⠀⠀⠈⠀⣰⣯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡜⠁⠀⠀
|
||||
# ⠀⠀⠀⠀⠈⠓⠛⠙⣹⡟⠉⠉⡇⠀⠀⠀⣿⠀⠞⡸⠀⡜⢿⣷⡈⢿⡀⠀⠀⠀⣼⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣠⠤⢆⣴⠉⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⣴⠏⠀⠀⢠⡇⠀⠀⠀⣿⠀⠀⠁⢰⠁⢸⠻⣟⠾⡇⠀⢀⣾⣿⡇⣠⣴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣰⣾⣿⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⣼⠋⠀⠀⠀⢸⡇⠀⠀⠀⠈⠙⠒⠒⠢⠭⣍⠀⢸⠓⢤⠴⠚⣿⡉⠀⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣟⡏⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⡜⡽⠀⠀⠀⠀⢸⡇⠀⠀⠀⢀⣀⣀⣀⡀⠀⢸⠀⠺⠤⠼⢠⣾⡉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⠟⠻⣷⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⣸⢹⡇⠀⠀⠀⠀⢠⠏⠉⠉⠉⠉⠉⠉⠉⡝⢣⠘⡆⢰⣠⠞⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⠘⡆⠀⢹⡄⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⢀⡇⣿⡇⠀⠀⠀⡰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢆⠷⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠃⠀⠸⡄⠸⡇⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⢸⠀⡿⣇⠀⣠⠎⠀⠀⠀⠀⠀⠀⠀⣠⠎⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡆⠈⣧⠀⠱⣸⣇⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⢾⠀⡇⢻⣾⠃⠀⠀⠀⠀⠀⠀⢠⢺⠃⠀⡴⢋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠇⠀⠘⡗⢆⠻⡏⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⢸⠀⡷⢁⠋⠓⠦⡴⠂⠀⠀⠀⣼⠃⢠⠎⠀⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡤⠀⠀⠀⢸⠀⡄⠀⠘⠞⢱⣧⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⣨⣇⢳⡎⢀⡴⠻⠇⠀⠀⠀⠀⠃⢠⠋⠀⠀⢹⠀⠀⠀⠀⠀⠀⠀⠀⠀⢦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡞⢦⠑⠀⠀⠀⢇⠸⣇⠀⠀⠀
|
||||
# ⠀⢀⢸⠃⠈⣦⣷⣅⡀⢀⡄⠀⠀⠀⠀⢠⠇⠀⠀⠀⠀⣇⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⡀⠀⠀⠀⠀⠀⠀⠀⠀⢰⡃⠈⠃⠈⠐⠀⠈⢢⡓⢆⠀⠀
|
||||
# ⢠⢃⡏⠀⣰⣿⠀⠉⠉⠉⡇⠀⠀⢀⠀⡞⠀⠀⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢦⡀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⣄⡀⠀⠀⢻⢸⠀⠀
|
||||
# ⡇⢸⣾⢠⠇⢿⡀⠀⠀⠀⡧⠀⢰⡇⢸⠁⢰⠀⠀⠀⠀⠀⢳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠋⠀⠀⢣⠀⠀⢱⠈⠒⢄⢸⠀⡇⠀
|
||||
# ⠁⢸⢹⡼⠀⠀⠙⠒⢀⢜⡡⢠⠋⡇⡏⠀⢸⠀⠀⠀⠀⠀⠈⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠣⡀⠀⠀⡄⠀⠀⠀⢻⠀⠀⠀⠀⠀⢹⠀⡿⡄
|
||||
# ⠀⠀⠈⠳⣤⢀⣠⠶⠓⢩⡡⠃⠀⢷⠀⠀⣼⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢄⠀⡇⠀⠀⠀⠈⢧⠀⠀⠀⠀⡎⡀⡗⢵
|
||||
# ⠀⠀⠀⠀⠋⠉⠀⢀⡠⠋⠀⠀⠀⡎⠀⠀⠇⠀⠀⠀⠀⠀⠀⠀⢳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢢⡁⠀⠀⠀⠀⠈⢧⠀⢀⡼⠞⣧⠁⢸
|
||||
# ⠀⠀⠀⠀⠀⠀⠈⠉⠀⠀⠀⠀⢰⠃⠀⠀⡃⠀⠀⠀⠀⠀⠀⠀⠈⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⠈⣖⠉⠀⣰⠃⠀⠈
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡼⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⠘⡄⠖⠁⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⢳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⠱⡀⠀⠀⠀⠀⠹⡀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠁⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⡀⡈⡇⠀⠀⠀⠀⠀⢀⡠⠔⠒⠋⠉⠀⠀⠀⢹⢳⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡾⠀⠀⠀⠀⣇⣠⡤⠒⠒⠛⠉⠉⠉⠹⡤⠋⠀⠀⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⢸⢸⠀⢀⡠⠖⠋⠉⣧⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⠷⢶⣶⠖⠚⠀⡇⠀⠀⠀⠀⠀⠀⠀⠈⠑⠒⠒⠊⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠈⠉⠉⠀⣀⡤⠞⠁⠀⠀⠀
|
||||
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⠖⠚⠛⠲⣾⣷⣦⣤⠤⠤⠶⠦⠤⠤⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠐⢁⡠⠖⠀⠉⠒⣬⣿⣿⣿⣡⠤⠴⠒⠲⠤⢄⠀⠈⠉⠓⠢⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠎⡴⠋⢀⠤⠒⣩⣿⣿⠟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠓⠒⠽⣶⣾⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠎⠎⠀⠐⠁⢠⣶⡿⠋⠁⠀⠀⠀⠀⠀⠀⣀⡴⠶⠖⠂⠀⠀⠀⣀⠀⠀⠈⠙⣿⣿⣿⠙⡄⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⢠⢞⠿⣯⢳⠀⠀⣼⢆⡀⠀⠀⠀⡸⠋⠀⠀⠀⠀⠀⠀⠀⡠⠞⠁⠀⠀⠀⠀⠀⠀⠀⢉⠻⣄⠀⠀⠈⢻⣿⣷⡸⡀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⠳⣵⡋⠀⣸⢯⠎⠀⠀⢀⡜⡡⠀⠀⠀⠀⠀⠀⢠⠞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠙⡆⠀⠀⠀⠹⣿⣏⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠯⠼⣰⡿⠃⠀⠀⢠⣾⢾⠀⠀⠀⠀⠀⠀⡠⢁⡤⠊⠀⠀⠀⠀⠀⠀⢠⠀⠀⠀⠀⡇⠸⡄⠀⠀⠱⣹⡿⠇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠏⠀⠀⠀⣠⡿⡵⠁⠀⠀⠀⡤⠀⡼⣡⠏⠀⠀⠀⢀⠀⠀⠀⠀⢸⠀⠀⠀⠀⢇⢁⢳⠀⠀⠀⢻⡅⡆⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⢀⢞⡟⡼⠁⠀⠀⠀⣰⠁⣼⡿⠁⠀⡰⢡⣦⡏⠀⠀⠀⠀⡼⠀⠀⠀⠀⢼⢸⠸⡄⠀⠀⠀⣿⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠞⠀⠀⢀⠔⠁⡞⣼⢃⡆⠀⠀⢠⠇⣰⡟⠤⢤⢾⣴⡵⣵⣧⠀⠀⠀⠀⡇⠀⠀⠀⡆⡬⠀⡇⡇⠀⠀⠀⢹⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⡠⠘⢁⠔⠁⠀⣠⠂⣸⢰⣿⢸⠀⠀⠀⡜⢀⡟⣠⢔⣵⠿⠋⢀⣾⠆⠀⠀⠀⡼⠀⠀⣀⣸⢁⡇⠀⣿⡇⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⢀⣀⡤⠴⠋⠀⡀⠋⢀⣴⠞⠁⡴⣇⣾⡇⢼⠀⠀⢀⣿⣸⣯⣶⣿⣧⣤⣄⣾⠋⠀⠀⢠⣴⡇⠀⠀⣬⡟⣾⣿⡀⢻⡇⠀⠀⠀⠸⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⣭⢭⡴⢋⡴⠋⢀⠔⢀⣴⡿⠃⢠⢞⡵⣿⣏⣾⢸⣰⡀⠀⢻⢻⡏⣿⣿⣿⣿⠻⣷⠒⠦⠴⠿⣟⣁⣀⣼⣟⡴⠁⠘⡧⣿⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⡔⣡⣴⠎⠀⣠⢃⣴⡿⠋⢀⣴⠷⡏⢀⠙⣟⢼⡄⣿⣇⠀⢸⣾⡇⣽⣬⡽⠋⠀⠀⠀⠀⠀⠀⠀⢨⣿⣿⣿⣤⡀⠀⢳⡿⠀⠀⠀⢸⢠⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⣼⣿⠃⠀⣰⢯⣾⠏⣠⡴⠛⠁⣸⢁⠆⠀⠘⣺⠳⣿⠻⣄⠀⣿⣧⠠⠅⠁⠀⠀⠀⠀⠀⠀⠀⠀⢻⠿⢿⡿⠙⢿⣧⣼⠃⡄⠀⢀⣾⢸⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⣿⡇⠀⢀⣿⣿⣯⠞⠋⠀⠀⢠⢃⠎⠀⢰⢀⡏⢠⣿⣿⣿⡷⢼⣎⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠳⢯⣤⠤⢺⡿⣣⢾⠄⣰⣿⡇⣿⢿⡀⠀⠀⠀⠀⠀⠀
|
||||
# ⣿⠀⢸⢸⣿⡿⠁⠀⠀⠀⢀⣯⠏⠀⣰⠃⡸⡀⣼⣿⣿⣿⣿⡄⠉⠀⠀⠀⠀⠀⠀⠀⠀⠂⠀⠀⠀⠀⠂⠀⣤⣿⣾⣿⣯⣾⣿⣿⡼⣿⢿⣿⣄⠀⠀⠀⠀⠀
|
||||
# ⢿⡀⢸⠘⡿⠁⠀⠀⠀⢀⠞⠁⠀⣰⠇⡴⣱⢱⣿⣿⣿⣿⠁⠙⢆⠀⠀⠀⠀⠀⢀⢄⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⢹⡿⠛⡇⢹⠘⣿⡷⢝⣲⡶⠞⠀
|
||||
# ⠓⢷⠼⣧⢳⡀⠀⢀⡴⠋⠀⢀⡼⠃⡼⣵⢣⣿⣿⡿⢿⣿⣧⠀⠀⠑⣄⠀⠀⠀⠀⠈⠀⠀⠀⠀⢀⣤⣾⣿⣿⣿⣿⡿⣿⠀⡇⢸⡇⣸⠀⣿⣿⠀⠀⠀⠀⠀
|
||||
# ⠀⠈⢢⡈⢷⣵⣔⡋⠀⠀⣠⠏⣠⢞⣽⣿⣿⣿⣿⢉⣹⣿⣿⣷⣤⠀⠀⠙⢦⡀⢀⣀⣤⣴⠒⠊⠉⠉⠉⠉⠙⠻⣿⣷⠹⣧⢣⣾⠃⡟⢀⣿⠟⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⣩⠦⣵⣯⣭⣵⠞⣡⡾⣣⣾⣿⠟⠛⠉⠸⣏⠀⠚⣿⣿⣿⣿⣷⣠⣤⣭⣿⣿⡟⢿⣦⠀⢀⠀⠀⠀⠀⠀⠀⠙⣷⣯⣯⢎⡼⣡⠞⠁⠀⠀⠀⠀⠀⠀
|
||||
# ⢀⡴⠊⢡⠞⢁⡰⢊⣵⡾⠟⠋⠉⢹⣹⠀⠀⠀⠀⠈⠒⣞⠁⣹⠿⡷⣮⣭⣭⣽⢿⢯⡗⠈⢻⣷⡋⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⡻⣍⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠉⠖⢠⠋⣴⡫⡾⠋⠁⠀⠀⠀⠢⣄⣏⡇⠀⠀⠀⠀⠀⠈⠋⠻⣤⢷⣃⣹⣏⣉⡦⠞⠁⢀⠞⠳⡳⡀⠀⠀⠀⠀⠀⠀⠀⢹⠑⠷⣌⠓⢤⡀⠀⠀⠀⠀⠀⠀
|
||||
# ⡞⢠⣯⡿⠋⡜⠀⠀⠀⠀⠀⠀⠀⠀⢹⠻⡤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠁⠀⠀⠀⢀⡴⠀⠀⠀⠱⡑⡄⢀⡠⠤⠤⠤⠤⠬⠧⠤⠬⠷⣦⣈⡓⠦⣀⠀⠀⠀
|
||||
# ⠀⣾⠋⢠⣾⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣇⢧⠀⠉⠓⠲⠤⣀⣀⠀⠀⠀⠀⠀⠀⡰⠋⠀⠀⠀⣀⠴⢛⣉⡠⠤⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⣿⣧⡈⠙⠢⣄
|
||||
# ⢰⠃⡴⡇⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⢸⡀⠀⠀⠀⠀⠀⠈⠉⠙⠂⠀⠀⠀⠀⠀⢀⡠⣊⠥⠚⠁⣀⡤⠴⠒⠋⠁⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⡇⠀⠘⢦
|
||||
# ⣿⢞⡾⢠⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣇⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣻⢚⣁⣤⠖⠋⣁⡤⣤⡴⠒⠒⠋⠁⢀⣀⣀⣀⡤⡼⣧⡟⢿⠀⠀⠈
|
||||
# ⣏⣼⡇⣿⢧⠀⠀⠀⠀⠀⠀⠐⡄⠀⠀⠀⢸⢹⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢩⣿⣶⣶⣾⣇⠔⣉⣤⡖⠿⣍⣹⣿⣿⣯⣿⡿⠋⢹⡜⣾⡇⠀⠀
|
||||
# ⣿⣿⢹⡇⢸⠀⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀⠈⡏⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⣾⣿⣿⣿⣿⣿⡉⠉⣿⣿⢄⡀⠀⠈⠉⣳⠀⠀⠀⣾⣷⢸⡇⠀⠀
|
||||
# ⢿⣿⣸⡇⣿⡇⠀⠀⠀⠀⠀⠀⠀⡗⠀⠀⢀⡵⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣸⣿⣿⣿⣿⣿⣿⣧⠀⣏⣋⠀⠀⠀⠀⠀⡇⠀⠀⠀⣿⣿⣿⡇⠀⢸
|
||||
# ⠀⠻⡏⢧⣿⣿⡀⠀⠀⠀⠀⠀⠀⡆⢀⠔⠋⠀⠻⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣿⣿⣿⣿⣿⡿⠀⣿⡯⢹⣦⣀⣀⣠⠇⠀⠀⢸⣿⣿⣿⠁⢀⣿
|
||||
# ⡀⠀⠙⢼⣿⣿⣧⠀⠀⠀⠀⠀⢠⠗⠁⠀⠀⠀⣰⠅⢺⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⣿⣿⣿⣿⣿⣿⠁⢰⣷⢤⡀⠀⠀⠀⠉⡇⠀⠀⢸⣿⡿⠃⢀⣾⠟
|
||||
# ⢽⣆⠙⢆⡙⢿⣿⡆⠀⠀⠀⠀⠈⡶⣄⠀⢠⠞⠁⠀⠀⠉⣵⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣿⠃⠀⡾⠿⡀⠁⠀⠀⠀⠸⡇⠀⠀⣾⠟⠁⢠⠟⠁⠀
|
||||
# ⠀⠙⢧⠀⠙⣦⣝⢷⠀⠀⠀⠀⠀⣧⠟⡟⠁⠀⠀⠀⢀⣀⣼⠻⣥⣄⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠏⠀⢸⣿⠀⠈⠳⣄⠀⠀⡔⣧⠀⠀⣯⡇⣰⠃⠀⠀⠀
|
||||
# ⠀⠀⠀⡄⠀⠸⣿⣧⣇⠀⠀⠀⠀⢿⠀⠻⣦⠀⠀⡾⣍⠀⠀⠀⠈⠻⣿⣦⡀⠀⠀⠀⠀⠘⣿⣿⡟⠀⢠⣧⡁⠀⠂⠤⢴⡓⠋⠀⡏⠀⠀⣿⠀⡇⠀⠀⠀⠀
|
||||
# ⢧⠀⢠⣇⠀⠀⣿⣿⢻⡄⠀⠀⠀⢸⣄⡀⠈⠳⣦⠿⢛⡷⠀⠠⠖⠉⠀⠙⢿⢢⣀⠀⠀⠀⣿⡿⠀⢀⣞⠏⠈⢦⡀⠀⣴⠃⢀⣾⠃⠀⠀⣿⡀⣇⠀⠀⠀⠀
|
||||
# ⢦⣠⢾⠏⢀⣼⡿⠇⢸⣧⠀⠀⠀⠘⡿⣿⣦⣄⡈⠉⠙⢷⣼⣷⡀⢀⡠⠖⠊⠉⠻⡗⢤⣰⡟⠁⣠⣿⠟⠤⣄⡈⠳⣰⡋⢠⡾⠃⠀⠀⢠⠿⣷⡈⠣⣄⣀⡴
|
||||
# ⡯⠵⡷⠖⠋⣽⠀⠀⣾⣿⡀⠀⠀⠀⠹⣄⠈⠛⠻⠿⠶⣾⣷⣼⣇⢺⣹⣧⠀⠀⣠⠔⠛⠿⣿⣶⠿⠁⠀⠀⠀⣸⣶⣷⠾⠋⠀⠀⠀⢀⡞⠀⠈⠉⠙⠛⠉⠀
|
||||
# ⠀⠀⡇⡀⠀⡏⠀⡸⣿⣿⡇⠀⠀⠀⠀⠙⣦⡀⠀⠀⠀⠀⠀⠀⢨⡉⠁⢸⣷⡎⠀⠀⠀⢠⠃⠈⢳⡀⠀⢀⣴⠛⠉⠁⠀⠀⠀⢀⣴⠙⡆⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⡸⣱⠃⠀⣷⢰⠃⣿⣹⡇⠀⠀⠀⠀⠀⠈⠛⢶⣤⣀⣀⣰⣶⣶⣿⣿⣿⡿⠟⠦⣄⢀⠏⠀⠀⠀⢙⣦⣾⣿⣇⠀⠀⠀⢀⣶⠉⢸⡇⢳⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⢰⠃⡏⠀⠀⢿⡏⠀⢻⡳⡇⠀⠀⠀⠀⠀⠀⠀⠀⢸⡉⠉⠛⠛⠛⠉⠉⠀⠀⠀⠀⠈⠉⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⠓⠚⣹⡇⠀⢸⡇⡜⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⢸⡈⣷⠀⠀⠘⣿⡀⠀⠙⢻⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠃⢻⣿⣿⣿⣿⠃⠀⢠⡟⠀⢠⡿⣱⠃⠀⠀⠀⠀⠀⠀⠀
|
||||
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣯⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⢀⣀⣀⣀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣻⣿⣿⣿⣿⠟⠙⣿⣿⣿⣶⣶⣶⡞⠉⠀⡉⠒⢤
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⠀⠀⠈⠻⠟⠋⠀⠀⠉⠒⠤⢥⣤⡬
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⠿⢿⣿⣿⠿⢿⣿⡿⢿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⠋⢻⣧⣾⣏⠙⣷⣿⣉⣷⣴⡿⢿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣼⣿⣿⣿⣧⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣾⣿⣿⣿⣷⣶⣶⣶⣤⣤⣀⣀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣴⣶⣿⣿⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⢿⣿⣶⣶
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⡀⣠⠴⠒⠒⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⠿⣛⣛⣯⣭⣭⣿⣿⣿⠿⠿⢿⣿⣿⣿⣿⣿⡿⠿⢻⣿⣿⣷⣶⣿⣿⣿⠿⠛⠋
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠈⠓⠮⣙⠢⠶⣿⣿⡿⢿⣻⣿⣿⣿⣶⣶⣶⣾⣿⣿⣿⣿⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣿⣿⣿⣿⣶⣿⣿⣿⣿⣿⡿⠟⠋⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠛⠛⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣿⣿⠛⠉⠉⠉⠿⠟⢹⣿⣿⣿⣿⣿⣿⣿⣟⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠙⡟⣿⣿⣿⣿⣇⠙⠋⠀⠀⠀⠀⠀⢠⠿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠂⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⢸⣿⣿⣿⣿⣧⡀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⣿⣿⢿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⢿⣿⣿⣿⣿⣿⣷⣦⣤⡴⠚⢑⣿⣿⣿⣿⡿⠙⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡸⠟⠛⢿⣿⣿⣿⣿⣷⣾⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠎⠀⠀⢀⣿⣿⡿⠁⠀⠈⠁⠉⠩⢐⣿⣿⣿⣧⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⢤⣤⡂⠀⢴⠞⣿⣿⠏⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⡿⠁⠀⠀⠉⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣋⠀⠀⠉⢳⣾⣾⣿⣿⣦⣀⡀⠀⠀⠀⠀⢸⡛⣿⣿⡇⠀⠀⠀⠀⢸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⡤⠤⣤⣄⣀⠀⣠⣤⣴⣷⣾⣿⣿⣷⣦⣄⣚⣉⣙⣛⣛⣛⠛⢳⡀⢀⠴⢦⠤⢭⣙⣿⡛⠒⣠⠆⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⢠⡤⠄⠖⠒⠒⠚⠋⠛⢆⠘⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡀⠑⢋⣬⣥⣄⣀⠀⠰⠾⢿⣿⠟⠓⠒⣓⡄⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠘⢣⡀⢱⠖⢝⠻⡷⠾⠶⠾⡄⢀⡀⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣻⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣦⣤⣭⣧⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠹⣶⠂⠂⠁⠛⠂⠒⠤⢽⣿⠿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠛⠁⠀⢸⣿⣿⣿⣿⣿⣿⡻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⣟⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠚⠛⠿⢿⣛⡻⢯⣁⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣤⡀⠀⣀⡠⠴
|
||||
# ⠀⠀⠀⢹⡀⢐⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠓⠀⠠⢼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⢿⣿⣿⣿⣿⣿⣿⣿⡿⣻⢟⡡⢎⡿
|
||||
# ⠀⠀⠀⠘⡇⠘⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⢿⣿⣿⣿⣿⣿⣿⠶⠋⣭⠞⢴⣟
|
||||
# ⠀⠀⠀⠀⠃⠀⠑⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡷⠶⠶⠶⠿⢻⡿⠟⠋⡁⠀⠃⠀⠀⢠⣿
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠣⠑⠂⠀⠀⠀⠀⡀⠋⡠⠅⠈⠀⠠⢨⣿
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⡴⠀⠀⠀⠀⠋⠀⠀⠀⠀⠀⠀⠁⣾⠋
|
||||
# ⠀⠀⠀⠀⠀⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣯⠄⡀⠀⠄⠀⠔⠁⠀⠀⠀⠀⠀⢰⠃⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢄⠀⠀⠀⠀⠀⠀⠀⠀⢨⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡎⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡘⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⡀⠀⠀⠀⡾⠛⠛⠛⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠁⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⣸⣷⠿⣿⣿⣶⣤⣍⡻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⡎⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⡇⠀⠀⠈⠉⠛⠿⣿⣿⣦⣽⣛⣿⣿⣿⣯⣭⣥⣤⣶⣾⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⡰⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠁⠀⠀⠀⠀⠀⠀⠀⠉⠙⣿⣿⣿⠟⠋⠉⠉⠉⠉⠉⠙⠛⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⢿⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠋⠁⠀⠀⠀⠀⠀⠀⠂⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⢰⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⢠⡏⢸⠁⠀⠀⠀⠀⠀⠀⠀⠀⢠⠇⠀⠀⠀⠀⠀⠀⠀⠀⢰⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀⠀⠸⡇⡾⠀⠀⠀⠀⠀⠀⠀⠀⢀⡞⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢷⣄⣀⣀⣀⣀⣀⠤⢴⣧⣷⣦⣀⠀⠀⠀⠀⠀⢀⡞⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠁⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⡈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⡀⠀⠀⢀⣀⣀⠘⣿⣿⣿⣿⣿⣷⣶⣶⣴⡞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⢀⣐⡒⠒⣉⣥⠤⣿⣿⣿⣿⣿⣿⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⡼⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
|
||||
@@ -1,352 +1,241 @@
|
||||
# meta developer: @SunnexGB
|
||||
# requires: aiohttp
|
||||
# meta pic: https://r2.fakecrime.bio/uploads/ab42b5e2-91f1-4ed1-8002-51b3184e3839.jpg
|
||||
# meta banner: https://r2.fakecrime.bio/uploads/ab42b5e2-91f1-4ed1-8002-51b3184e3839.jpg
|
||||
# meta fhsdesc: YaMusic, music, музыка, яндекс музыка,Lyrics, слова, текст, трек, песня
|
||||
# все же я не знаю трек или сонг, так что пусть будет трек, а не сонг потому что интуитивнее поняттнее,наверное?
|
||||
# крутой баннер да?
|
||||
#current version
|
||||
__version__ = (1, 1, 2)
|
||||
__version__ = (1, 1, 3)
|
||||
|
||||
from herokutl.types import Message
|
||||
from .. import loader, utils
|
||||
from ..types import InlineCall
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import re
|
||||
|
||||
|
||||
@loader.tds
|
||||
class YandexLyrics(loader.Module):
|
||||
"""life lyrics current song"""
|
||||
|
||||
strings = {
|
||||
"name": "YandexLyrics",
|
||||
"no_YaMusicMod": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>YaMusicMod not found,but u can install it. You can also support developer: </b> @codrago_m",
|
||||
"no_auth": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> You not authorized in SpotifyMod, visit you Saved Messages and setup token to continue.</b>",
|
||||
"no_ym": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>Nothing is playing on YaMusic.</b>",
|
||||
"no_lyrics": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>Lyrics not found for:</b> <code>{}</code>",
|
||||
"not_synced": "<i><tg-emoji emoji-id=5431445849026611010>⚠️</tg-emoji> Lyrics are not synchronized.</i>\n\n",
|
||||
"finished": "<tg-emoji emoji-id=5429638011392377649>‼️</tg-emoji> Playback ended or track changed.",
|
||||
"header": "<tg-emoji emoji-id=5429413328768224565>🎤</tg-emoji> <b>{} - {}</b>\n\n",
|
||||
"timeout": "<b><tg-emoji emoji-id=5429455831764584284>⏳</tg-emoji></b><b> Oopsi, looks like we've got a timeout here</b>.",
|
||||
|
||||
"name": "migration",
|
||||
"no_YaMusicMod": "u're not use YaMusicMod,install my new module.",
|
||||
"inline_msg": "if u ready to migration pls click on the button.",
|
||||
"already": "module already install, u're ready for migration ur db?",
|
||||
"restartf": "u ready restart ur ub?",
|
||||
"restartf_btn": "Ok",
|
||||
"done": "migration is done.",
|
||||
"confirm": "confirm",
|
||||
"install": "install LiveLyrics",
|
||||
"close": "close",
|
||||
"migrate_db": "Yep"
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"cls_doc": "Лайв слова текущей песни.",
|
||||
"no_YaMusicMod": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>YaMusicMod не найден,но его можно установить. Вы также можете поддержать разработчика: </b> @codrago_m",
|
||||
"no_auth": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> Вы не авторизированы в YaMusicMod, перейдите в Избранное и установите токен для продолжения работы.</b>",
|
||||
"no_ym": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>В YaMusic ничего не играет.</b>",
|
||||
"no_lyrics": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>Текст не найден для:</b> <code>{}</code>",
|
||||
"not_synced": "<i><tg-emoji emoji-id=5431445849026611010>⚠️</tg-emoji> Текст не синхронизирован.</i>\n\n",
|
||||
"finished": "<tg-emoji emoji-id=5429638011392377649>‼️</tg-emoji> Воспроизведение завершено или трек сменился.",
|
||||
"header": "<tg-emoji emoji-id=5429413328768224565>🎤</tg-emoji> <b>{} - {}</b>\n\n",
|
||||
"timeout": "<b><tg-emoji emoji-id=5429455831764584284>⏳</tg-emoji></b><b> Упси, похоже кто то словил таймаут.</b>.",
|
||||
"no_YaMusicMod": "ты не юзал YaMusicMod,установи новый модуль.",
|
||||
"inline_msg": "если вы готовы к миграции нажмите на кнопку ниже.",
|
||||
"already": "модуль уже стоиит,готовы к переносу данных?",
|
||||
"restartf": "вы готовы к перезапуску юб?",
|
||||
"restartf_btn": "Лан",
|
||||
"done": "миграция завершена.",
|
||||
"confirm": "подтвердить",
|
||||
"install": "установить LiveLyrics",
|
||||
"close": "закрыть",
|
||||
"migrate_db": "Ага"
|
||||
}
|
||||
|
||||
async def install_livelyrics(self, call: InlineCall):
|
||||
m = self.lookup("Modules") or self.lookup("loader")
|
||||
await m.download_and_install("https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/refs/heads/main/LiveLyrics.py")
|
||||
await call.delete()
|
||||
|
||||
def __init__(self):
|
||||
self._active_tasks: dict = {}
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"emoji_current",
|
||||
"<tg-emoji emoji-id='5215679757366089921'>🤯</tg-emoji>",
|
||||
"Emoji for current line",
|
||||
validator=loader.validators.String(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"dot",
|
||||
"♪",
|
||||
"instrumental_emoji or text",
|
||||
validator=loader.validators.String(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"lyrics_delay",
|
||||
0.5,
|
||||
"delay in switching to a new timing sector with words",
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"request_timeout",
|
||||
12,
|
||||
"timeout value",
|
||||
),
|
||||
async def confirm(self, call: InlineCall):
|
||||
config = self.db.get("YaMusicMod", "__config__") or {}
|
||||
self.db.set("LiveLyrics", "__config__", config)
|
||||
await asyncio.sleep(1)
|
||||
await self.invoke("unloadmod", "-f YaMusicMod", "me")
|
||||
await call.edit(self.strings["done"])
|
||||
await asyncio.sleep(2)
|
||||
await call.edit(
|
||||
self.strings["restartf"],
|
||||
reply_markup=[
|
||||
[{"text": self.strings["restartf_btn"], "callback": self.restartf}],
|
||||
[{"text": self.strings["close"], "callback": self.close}],
|
||||
],
|
||||
)
|
||||
|
||||
async def migrate_db(self, call: InlineCall):
|
||||
await call.edit(
|
||||
self.strings["inline_msg"],
|
||||
reply_markup=[
|
||||
[{"text": self.strings["confirm"], "callback": self.confirm}],
|
||||
[{"text": self.strings["close"], "callback": self.close}],
|
||||
],
|
||||
)
|
||||
|
||||
async def install_yamusic(self, call: InlineCall):
|
||||
mod_url = "https://raw.githubusercontent.com/coddrago/modules/main/YaMusic.py"
|
||||
try:
|
||||
m = self.lookup("Modules") or self.lookup("loader")
|
||||
await m.download_and_install(mod_url)
|
||||
await call.answer("YaMusicMod installed!", show_alert=True)
|
||||
mod = self.lookup("YaMusicMod")
|
||||
acs_tkn = mod.get("__config__")["token"] if mod else "****"
|
||||
if not acs_tkn:
|
||||
await self.invoke("yguide", " ", "me")
|
||||
await call.edit(
|
||||
self.strings("no_auth"),
|
||||
reply_markup=[[{"text": "Хорошо", "callback": self.close}]],
|
||||
)
|
||||
else:
|
||||
await call.delete()
|
||||
except Exception as e:
|
||||
await call.answer(f"Error! Check logs.\n{e}", show_alert=True)
|
||||
async def restartf(self, call: InlineCall):
|
||||
await call.delete()
|
||||
await self.invoke("restart", "-f", "me")
|
||||
|
||||
def close(self, call: InlineCall):
|
||||
return call.delete()
|
||||
async def close(self, call: InlineCall):
|
||||
await call.delete()
|
||||
|
||||
async def _get_lyrics(self, artist: str, track: str):
|
||||
clean_track = re.sub(r"\(.*?\)|\[.*?\]", "", track).strip()
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
"https://lrclib.net/api/search",
|
||||
params={"track_name": clean_track, "artist_name": artist},
|
||||
timeout=aiohttp.ClientTimeout(total=(self.config["request_timeout"])),
|
||||
) as resp:
|
||||
if resp.status == 200:
|
||||
res = await resp.json()
|
||||
return res[0] if res else None
|
||||
except asyncio.TimeoutError:
|
||||
return {"timeout": True}
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def _parse_synced(self, synced_text: str) -> list:
|
||||
lines = []
|
||||
for line in synced_text.split("\n"):
|
||||
m = re.search(r"\[(\d+):(\d+\.\d+)\](.*)", line)
|
||||
if m:
|
||||
mins, secs, text = m.groups()
|
||||
lines.append({
|
||||
"time": (int(mins) * 60 + float(secs)) * 1000,
|
||||
"text": text.strip(),
|
||||
})
|
||||
return lines
|
||||
|
||||
def _build_content(self, artist, track, lines, plain, progress_ms, not_synced_str):
|
||||
header = self.strings("header").format(
|
||||
utils.escape_html(artist),
|
||||
utils.escape_html(track),
|
||||
)
|
||||
if lines:
|
||||
curr_idx = 0
|
||||
for i, line in enumerate(lines):
|
||||
if progress_ms >= line["time"]:
|
||||
curr_idx = i
|
||||
win_start = max(0, curr_idx - 1)
|
||||
win_end = min(len(lines), curr_idx + 6)
|
||||
rows = []
|
||||
for i in range(win_start, win_end):
|
||||
t = lines[i]["text"] or self.config["dot"]
|
||||
if i == curr_idx:
|
||||
rows.append(
|
||||
f"<b>{self.config['emoji_current']} {utils.escape_html(t)}</b>"
|
||||
)
|
||||
else:
|
||||
rows.append(f"<code>{utils.escape_html(t)}</code>")
|
||||
return header + "\n".join(rows)
|
||||
else:
|
||||
return header + not_synced_str + f"<blockquote expandable>{utils.escape_html((plain or '')[:4000])}</blockquote>"
|
||||
|
||||
def _markup(self, song_url):
|
||||
return [
|
||||
[{"text": "🔗 song.link", "url": song_url}],
|
||||
[{"text": "❌ Close", "callback": self._close_cb}],
|
||||
]
|
||||
|
||||
async def _close_cb(self, call):
|
||||
for track_id, task in list(self._active_tasks.items()):
|
||||
task.cancel()
|
||||
self._active_tasks.pop(track_id, None)
|
||||
try:
|
||||
await call.answer()
|
||||
await call.delete()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
async def run_loop(self, form, mod, track_id, artist_name, track_name, song_url, lines, plain, not_synced_str):
|
||||
last_display = ""
|
||||
try:
|
||||
while True:
|
||||
pb = await mod._YaMusicMod__get_now_playing()
|
||||
if not pb or not pb.get("track") or pb["track"]["track_id"] != track_id:
|
||||
try:
|
||||
await form.edit(
|
||||
self.strings("finished"),
|
||||
reply_markup=[[{"text": "❌ Close", "callback": self._close_cb}]],
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
break
|
||||
|
||||
prog = pb.get("progress_ms", 0)
|
||||
content = self._build_content(
|
||||
artist_name, track_name, lines, plain, prog, not_synced_str
|
||||
)
|
||||
|
||||
if content != last_display:
|
||||
try:
|
||||
await form.edit(content, reply_markup=self._markup(song_url))
|
||||
last_display = content
|
||||
except Exception:
|
||||
break
|
||||
|
||||
if not lines:
|
||||
break
|
||||
|
||||
await asyncio.sleep(self.config["lyrics_delay"])
|
||||
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
self._active_tasks.pop(track_id, None)
|
||||
|
||||
@loader.command(ru_doc="- показать синхронизированный текст песни")
|
||||
async def ynowlcmd(self, message: Message):
|
||||
"""- show synchronized lyrics for current YaMusic track"""
|
||||
mod = self.lookup("YaMusic")
|
||||
if not mod:
|
||||
form = await self.inline.form("⏳", message=message)
|
||||
await form.edit(
|
||||
self.strings("no_YaMusicMod"),
|
||||
reply_markup=[[{"text": "Install YaMusic", "callback": self.install_yamusic}]],
|
||||
)
|
||||
return
|
||||
|
||||
ya_token = mod.get("__config__")["token"]
|
||||
if not ya_token:
|
||||
await self.invoke("yguide", " ", "me")
|
||||
form = await self.inline.form("⏳", message=message)
|
||||
await form.edit(
|
||||
self.strings("no_auth"),
|
||||
reply_markup=[[{"text": "Хорошо", "callback": self.close}]],
|
||||
)
|
||||
return
|
||||
|
||||
playback = await mod._YaMusicMod__get_now_playing()
|
||||
if not playback or not playback.get("track"):
|
||||
return await utils.answer(message, self.strings("no_ym"))
|
||||
|
||||
track = playback["track"]
|
||||
track_id = track["track_id"]
|
||||
artist_name = ", ".join(track["artist"])
|
||||
track_name = track["title"]
|
||||
song_url = f"https://song.link/s/{track_id}"
|
||||
|
||||
old = self._active_tasks.pop(track_id, None)
|
||||
if old:
|
||||
old.cancel()
|
||||
|
||||
data = await self._get_lyrics(artist_name, track_name)
|
||||
if data and data.get("timeout"):
|
||||
return utils.answer(
|
||||
message,
|
||||
self.strings["timeout"]
|
||||
)
|
||||
if not data or data.get("instrumental"):
|
||||
track_and_artist = f"{utils.escape_html(track_name)} - {utils.escape_html(artist_name)}"
|
||||
@loader.command()
|
||||
async def ynowl(self, message: Message):
|
||||
SL_cfg = self.db.get("YaMusicMod", "__config__")
|
||||
if SL_cfg is None:
|
||||
return await utils.answer(
|
||||
message,
|
||||
self.strings("no_lyrics").format(track_and_artist),
|
||||
self.strings["no_YaMusicMod"],
|
||||
reply_markup=[
|
||||
[{"text": self.strings["install"], "callback": self.install_livelyrics}],
|
||||
[{"text": self.strings["close"], "callback": self.close}],
|
||||
],
|
||||
)
|
||||
|
||||
synced_raw = data.get("syncedLyrics")
|
||||
plain = data.get("plainLyrics", "")
|
||||
|
||||
lines = self._parse_synced(synced_raw) if synced_raw else []
|
||||
not_synced_str = self.strings("not_synced")
|
||||
|
||||
prog = playback.get("progress_ms", 0)
|
||||
initial_content = self._build_content(
|
||||
artist_name, track_name, lines, plain, prog, not_synced_str
|
||||
)
|
||||
|
||||
form = await self.inline.form(
|
||||
text=initial_content,
|
||||
message=message,
|
||||
reply_markup=self._markup(song_url),
|
||||
)
|
||||
|
||||
task = asyncio.create_task(
|
||||
self.run_loop(
|
||||
form=form,
|
||||
mod=mod,
|
||||
track_id=track_id,
|
||||
artist_name=artist_name,
|
||||
track_name=track_name,
|
||||
song_url=song_url,
|
||||
lines=lines,
|
||||
plain=plain,
|
||||
not_synced_str=not_synced_str,
|
||||
if self.lookup("LiveLyrics"):
|
||||
return await utils.answer(
|
||||
message,
|
||||
"модуль уже стоиит,готовы к переносу данных",
|
||||
reply_markup=[
|
||||
[{"text": self.strings["migrate_db"], "callback": self.migrate_db}],
|
||||
[{"text": self.strings["close"], "callback": self.close}],
|
||||
],
|
||||
)
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings["inline_msg"],
|
||||
reply_markup=[
|
||||
[{"text": self.strings["confirm"], "callback": self.confirm}],
|
||||
[{"text": self.strings["close"], "callback": self.close}],
|
||||
],
|
||||
)
|
||||
self._active_tasks[track_id] = task
|
||||
|
||||
# Fan-fantasizing
|
||||
# Fa-fa-fa-fantasizing
|
||||
# You and I-I-I
|
||||
# When I close my eyes (my eyes)
|
||||
# Nothings real
|
||||
# Fantasizing (fantasizing)
|
||||
# Bout you and I
|
||||
# Cos you only hit my line
|
||||
# When you wanna waste time
|
||||
# I know you're so busy
|
||||
# But trust me baby I'm not blind (blind)
|
||||
# Uh oh, you and I
|
||||
# We could never be
|
||||
# Uh oh you and I
|
||||
# Cos we will never be
|
||||
# Uh oh, you and I
|
||||
# No, we will never be
|
||||
# That pretty picture that I painted in my mind (mind)
|
||||
# So tell me what (tell me, tell me)
|
||||
# The view is like
|
||||
# With your head in the clouds
|
||||
# And tell me what (tell me, tell me)
|
||||
# It feels like to be right all the time
|
||||
# You say that you love me
|
||||
# But you don't even love yourself (no)
|
||||
# Wanna get in my head
|
||||
# But I ain't gonna let you close (no)
|
||||
# Tryna control me
|
||||
# But I ain't gon' play your game
|
||||
# No more
|
||||
# No I won't
|
||||
# When I close (when I close)
|
||||
# My eyes (my eyes)
|
||||
# Nothing's real (no)
|
||||
# Fantasizing (fantasize)
|
||||
# bout you-you-you-you-you
|
||||
# You-you-you-you
|
||||
# You-you-you-you
|
||||
# You-you-you
|
||||
# And I
|
||||
# You-you-you-you-you-you-you
|
||||
# You-you-you-you-you-you-you
|
||||
# You-you-you-you-you
|
||||
# And I
|
||||
# This is the last time I tell you
|
||||
# Don't come round my way if you're just gon' waste my time
|
||||
# And no, I won't be there for the long run
|
||||
# No, not I
|
||||
# But you never get (never get)
|
||||
# The message, do you?
|
||||
# You never seem (never seem)
|
||||
# To grip an understanding
|
||||
# That you emulate a ghost
|
||||
# I pointed out all of your flaws
|
||||
# But you still came up with excuses for em all
|
||||
# So typical (so typical)
|
||||
# You know it all
|
||||
# So of course, I'm the one that's wrong (right?)
|
||||
# When I close my eyes
|
||||
# Nothings real
|
||||
# Fan- fantasize
|
||||
# Fa-fa-fa-fa
|
||||
# Fantasizing
|
||||
# Fa-fantasizing bout you and I
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣠⠤⠤⣄⠠⠤⣤⠤⢤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠤⠒⠉⠁⠀⠀⠀⣠⠖⠉⣀⡀⣀⣤⠍⢩⣵⡢⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠖⠋⠁⠀⠀⠀⠀⢀⣴⠊⠀⣠⠞⣠⠞⠉⢳⣦⠀⡈⠻⣦⠑⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⠖⠉⠀⠀⠀⠀⠀⠀⣠⣴⡿⠁⠀⡰⢁⡾⠁⠐⠃⢸⡇⠀⠸⡄⠀⠀⠈⠷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣆⠀⠀⠀⠀⣀⣠⡴⡿⠁⣼⡅⠀⣰⢣⢿⡇⡴⠀⠀⣾⡅⠀⠀⣧⠀⠀⠀⠀⢈⢂⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠛⠻⠿⠿⠷⠒⠒⠋⠁⡏⠸⠁⣸⣿⠇⢀⣧⠏⢸⡿⠁⠀⡼⠙⣧⠀⢀⣿⡄⢀⠠⠀⠘⡄⢡⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠁⣤⣠⣯⢿⠀⢸⣟⡴⡾⣳⣴⣾⡷⢠⠛⢆⣾⣿⡇⡀⠀⣦⠀⡇⠀⢣⠀⠀⠀⠀⠀⠀
|
||||
# ⢠⡄⠀⡄⠀⠀⠀⢀⣒⣒⣶⡀⠀⠀⠀⠀⠀⠀⠀⠀⢼⠀⣿⡿⣿⢸⠐⣿⣿⣿⣿⡿⣶⡿⢷⣯⣮⣾⢯⡟⣇⡇⣸⡏⢠⣠⠀⠀⢣⠀⠀⠀⠀⠀
|
||||
# ⠀⣿⣿⣿⣦⡄⣤⡾⢿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⢸⢠⣿⣿⣿⣼⣇⡿⣟⠛⠛⠃⠀⠀⠁⠉⣠⣶⣷⣄⣿⣼⣿⡆⣸⣧⡀⠀⠀⢆⠀⠀⠀⠀
|
||||
# ⢸⣿⣿⡿⠛⠋⠉⢩⣍⣉⣻⡇⠀⠀⠀⠀⠀⠀⠀⠀⡇⣼⣿⢹⢿⡝⢿⣇⡈⠃⠀⠀⠀⠀⠀⠀⠛⢿⡏⣿⣿⢿⣿⡱⡸⠁⠈⠢⣀⠘⡆⠀⠀⠀
|
||||
# ⠈⣿⡏⣤⠄⠘⠃⠸⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⢰⢱⠻⡇⢸⠸⣷⡀⣿⡁⠀⠀⠀⠀⡀⠀⠀⠀⢀⡼⣯⠁⣾⣿⠁⠀⠀⠀⠀⠈⠑⠃⠀⠀⠀
|
||||
# ⢠⣬⣼⣧⡀⠀⠀⠀⢹⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⣾⣿⡦⠧⠬⣧⡸⣿⣧⠙⢠⡀⠀⠀⠀⠀⣀⡤⠊⠀⣿⣰⢿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⢸⣿⣿⣿⡀⢀⡀⠀⣸⣿⣿⡁⠀⠀⠀⠀⠀⠀⣰⣯⠟⠛⠛⠛⢿⣭⣿⣿⠷⣶⣟⣷⣶⣒⡉⠁⠀⠀⠜⢹⣿⡼⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠈⠛⠛⠛⠛⠛⠛⠓⠛⠛⠛⠃⠀⠀⠀⠀⢀⢼⣿⣏⣰⣿⣷⣴⣦⠙⣿⣿⡛⠛⠿⠥⢯⣀⣈⡇⢀⠀⠚⢙⡞⠑⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⢽⣿⣿⢻⣿⣿⣟⣿⣿⢿⣿⢦⣄⠀⣀⣀⠉⠩⠿⣷⣾⣭⠑⠒⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⠏⠈⣿⣿⣿⣿⣿⣿⣿⣿⣾⣿⣧⢽⣯⣁⠀⠀⠀⠀⠒⠗⢺⣿⣭⣤⣄⠙⠢⣀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⠏⠀⠀⣿⡟⠈⠻⣿⣿⣻⣿⣿⣟⣡⣴⣿⣿⣷⡞⠒⠒⠶⠶⠶⠤⣽⡿⠽⠦⣄⣀⠑⡄⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠋⠀⢀⣴⣿⡇⠀⠀⠉⠻⣥⣽⠿⠋⠙⠻⣽⣿⣤⠃⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠀⠈⠑⠳⣄⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⣠⠊⣼⣿⡇⠀⠀⠐⢿⡿⠁⠀⠀⠀⢀⣀⣩⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢢⡀⠀⠀
|
||||
# ⠀⠀⣠⠤⢤⣀⣀⡀⠀⠀⠀⠀⢠⠏⢀⡜⠁⢰⠇⣿⣿⠀⠀⠀⣈⡇⠀⠀⠀⠀⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢳⠀⠀
|
||||
# ⠀⠀⠳⡀⠀⠀⠀⠉⠉⠙⠓⠒⠳⠤⠯⣄⡀⡞⠀⡿⣡⡴⠞⠋⢙⡇⠀⠀⠀⠀⢟⡛⠀⣿⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀
|
||||
# ⠀⠀⠀⠹⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠁⠈⣯⣉⠀⠀⠀⢸⡇⠀⠀⠀⢰⡶⠀⢠⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡼⠀⠀
|
||||
# ⠀⠀⠀⠀⢹⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣧⠀⢰⠃⠟⢳⣄⠀⣾⡇⠀⠀⠀⠈⠀⣰⣯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡜⠁⠀⠀
|
||||
# ⠀⠀⠀⠀⠈⠓⠛⠙⣹⡟⠉⠉⡇⠀⠀⠀⣿⠀⠞⡸⠀⡜⢿⣷⡈⢿⡀⠀⠀⠀⣼⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣠⠤⢆⣴⠉⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⣴⠏⠀⠀⢠⡇⠀⠀⠀⣿⠀⠀⠁⢰⠁⢸⠻⣟⠾⡇⠀⢀⣾⣿⡇⣠⣴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣰⣾⣿⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⣼⠋⠀⠀⠀⢸⡇⠀⠀⠀⠈⠙⠒⠒⠢⠭⣍⠀⢸⠓⢤⠴⠚⣿⡉⠀⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣟⡏⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⡜⡽⠀⠀⠀⠀⢸⡇⠀⠀⠀⢀⣀⣀⣀⡀⠀⢸⠀⠺⠤⠼⢠⣾⡉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⠟⠻⣷⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⣸⢹⡇⠀⠀⠀⠀⢠⠏⠉⠉⠉⠉⠉⠉⠉⡝⢣⠘⡆⢰⣠⠞⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⠘⡆⠀⢹⡄⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⢀⡇⣿⡇⠀⠀⠀⡰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢆⠷⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠃⠀⠸⡄⠸⡇⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⢸⠀⡿⣇⠀⣠⠎⠀⠀⠀⠀⠀⠀⠀⣠⠎⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡆⠈⣧⠀⠱⣸⣇⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⢾⠀⡇⢻⣾⠃⠀⠀⠀⠀⠀⠀⢠⢺⠃⠀⡴⢋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠇⠀⠘⡗⢆⠻⡏⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⢸⠀⡷⢁⠋⠓⠦⡴⠂⠀⠀⠀⣼⠃⢠⠎⠀⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡤⠀⠀⠀⢸⠀⡄⠀⠘⠞⢱⣧⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⣨⣇⢳⡎⢀⡴⠻⠇⠀⠀⠀⠀⠃⢠⠋⠀⠀⢹⠀⠀⠀⠀⠀⠀⠀⠀⠀⢦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡞⢦⠑⠀⠀⠀⢇⠸⣇⠀⠀⠀
|
||||
# ⠀⢀⢸⠃⠈⣦⣷⣅⡀⢀⡄⠀⠀⠀⠀⢠⠇⠀⠀⠀⠀⣇⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⡀⠀⠀⠀⠀⠀⠀⠀⠀⢰⡃⠈⠃⠈⠐⠀⠈⢢⡓⢆⠀⠀
|
||||
# ⢠⢃⡏⠀⣰⣿⠀⠉⠉⠉⡇⠀⠀⢀⠀⡞⠀⠀⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢦⡀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⣄⡀⠀⠀⢻⢸⠀⠀
|
||||
# ⡇⢸⣾⢠⠇⢿⡀⠀⠀⠀⡧⠀⢰⡇⢸⠁⢰⠀⠀⠀⠀⠀⢳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠋⠀⠀⢣⠀⠀⢱⠈⠒⢄⢸⠀⡇⠀
|
||||
# ⠁⢸⢹⡼⠀⠀⠙⠒⢀⢜⡡⢠⠋⡇⡏⠀⢸⠀⠀⠀⠀⠀⠈⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠣⡀⠀⠀⡄⠀⠀⠀⢻⠀⠀⠀⠀⠀⢹⠀⡿⡄
|
||||
# ⠀⠀⠈⠳⣤⢀⣠⠶⠓⢩⡡⠃⠀⢷⠀⠀⣼⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢄⠀⡇⠀⠀⠀⠈⢧⠀⠀⠀⠀⡎⡀⡗⢵
|
||||
# ⠀⠀⠀⠀⠋⠉⠀⢀⡠⠋⠀⠀⠀⡎⠀⠀⠇⠀⠀⠀⠀⠀⠀⠀⢳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢢⡁⠀⠀⠀⠀⠈⢧⠀⢀⡼⠞⣧⠁⢸
|
||||
# ⠀⠀⠀⠀⠀⠀⠈⠉⠀⠀⠀⠀⢰⠃⠀⠀⡃⠀⠀⠀⠀⠀⠀⠀⠈⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⠈⣖⠉⠀⣰⠃⠀⠈
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡼⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⠘⡄⠖⠁⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⢳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⠱⡀⠀⠀⠀⠀⠹⡀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠁⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⡀⡈⡇⠀⠀⠀⠀⠀⢀⡠⠔⠒⠋⠉⠀⠀⠀⢹⢳⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡾⠀⠀⠀⠀⣇⣠⡤⠒⠒⠛⠉⠉⠉⠹⡤⠋⠀⠀⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⢸⢸⠀⢀⡠⠖⠋⠉⣧⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⠷⢶⣶⠖⠚⠀⡇⠀⠀⠀⠀⠀⠀⠀⠈⠑⠒⠒⠊⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠈⠉⠉⠀⣀⡤⠞⠁⠀⠀⠀
|
||||
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⠖⠚⠛⠲⣾⣷⣦⣤⠤⠤⠶⠦⠤⠤⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠐⢁⡠⠖⠀⠉⠒⣬⣿⣿⣿⣡⠤⠴⠒⠲⠤⢄⠀⠈⠉⠓⠢⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠎⡴⠋⢀⠤⠒⣩⣿⣿⠟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠓⠒⠽⣶⣾⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠎⠎⠀⠐⠁⢠⣶⡿⠋⠁⠀⠀⠀⠀⠀⠀⣀⡴⠶⠖⠂⠀⠀⠀⣀⠀⠀⠈⠙⣿⣿⣿⠙⡄⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⢠⢞⠿⣯⢳⠀⠀⣼⢆⡀⠀⠀⠀⡸⠋⠀⠀⠀⠀⠀⠀⠀⡠⠞⠁⠀⠀⠀⠀⠀⠀⠀⢉⠻⣄⠀⠀⠈⢻⣿⣷⡸⡀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⠳⣵⡋⠀⣸⢯⠎⠀⠀⢀⡜⡡⠀⠀⠀⠀⠀⠀⢠⠞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠙⡆⠀⠀⠀⠹⣿⣏⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠯⠼⣰⡿⠃⠀⠀⢠⣾⢾⠀⠀⠀⠀⠀⠀⡠⢁⡤⠊⠀⠀⠀⠀⠀⠀⢠⠀⠀⠀⠀⡇⠸⡄⠀⠀⠱⣹⡿⠇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠏⠀⠀⠀⣠⡿⡵⠁⠀⠀⠀⡤⠀⡼⣡⠏⠀⠀⠀⢀⠀⠀⠀⠀⢸⠀⠀⠀⠀⢇⢁⢳⠀⠀⠀⢻⡅⡆⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⢀⢞⡟⡼⠁⠀⠀⠀⣰⠁⣼⡿⠁⠀⡰⢡⣦⡏⠀⠀⠀⠀⡼⠀⠀⠀⠀⢼⢸⠸⡄⠀⠀⠀⣿⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠞⠀⠀⢀⠔⠁⡞⣼⢃⡆⠀⠀⢠⠇⣰⡟⠤⢤⢾⣴⡵⣵⣧⠀⠀⠀⠀⡇⠀⠀⠀⡆⡬⠀⡇⡇⠀⠀⠀⢹⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⡠⠘⢁⠔⠁⠀⣠⠂⣸⢰⣿⢸⠀⠀⠀⡜⢀⡟⣠⢔⣵⠿⠋⢀⣾⠆⠀⠀⠀⡼⠀⠀⣀⣸⢁⡇⠀⣿⡇⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⢀⣀⡤⠴⠋⠀⡀⠋⢀⣴⠞⠁⡴⣇⣾⡇⢼⠀⠀⢀⣿⣸⣯⣶⣿⣧⣤⣄⣾⠋⠀⠀⢠⣴⡇⠀⠀⣬⡟⣾⣿⡀⢻⡇⠀⠀⠀⠸⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⣭⢭⡴⢋⡴⠋⢀⠔⢀⣴⡿⠃⢠⢞⡵⣿⣏⣾⢸⣰⡀⠀⢻⢻⡏⣿⣿⣿⣿⠻⣷⠒⠦⠴⠿⣟⣁⣀⣼⣟⡴⠁⠘⡧⣿⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⡔⣡⣴⠎⠀⣠⢃⣴⡿⠋⢀⣴⠷⡏⢀⠙⣟⢼⡄⣿⣇⠀⢸⣾⡇⣽⣬⡽⠋⠀⠀⠀⠀⠀⠀⠀⢨⣿⣿⣿⣤⡀⠀⢳⡿⠀⠀⠀⢸⢠⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⣼⣿⠃⠀⣰⢯⣾⠏⣠⡴⠛⠁⣸⢁⠆⠀⠘⣺⠳⣿⠻⣄⠀⣿⣧⠠⠅⠁⠀⠀⠀⠀⠀⠀⠀⠀⢻⠿⢿⡿⠙⢿⣧⣼⠃⡄⠀⢀⣾⢸⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⣿⡇⠀⢀⣿⣿⣯⠞⠋⠀⠀⢠⢃⠎⠀⢰⢀⡏⢠⣿⣿⣿⡷⢼⣎⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠳⢯⣤⠤⢺⡿⣣⢾⠄⣰⣿⡇⣿⢿⡀⠀⠀⠀⠀⠀⠀
|
||||
# ⣿⠀⢸⢸⣿⡿⠁⠀⠀⠀⢀⣯⠏⠀⣰⠃⡸⡀⣼⣿⣿⣿⣿⡄⠉⠀⠀⠀⠀⠀⠀⠀⠀⠂⠀⠀⠀⠀⠂⠀⣤⣿⣾⣿⣯⣾⣿⣿⡼⣿⢿⣿⣄⠀⠀⠀⠀⠀
|
||||
# ⢿⡀⢸⠘⡿⠁⠀⠀⠀⢀⠞⠁⠀⣰⠇⡴⣱⢱⣿⣿⣿⣿⠁⠙⢆⠀⠀⠀⠀⠀⢀⢄⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⢹⡿⠛⡇⢹⠘⣿⡷⢝⣲⡶⠞⠀
|
||||
# ⠓⢷⠼⣧⢳⡀⠀⢀⡴⠋⠀⢀⡼⠃⡼⣵⢣⣿⣿⡿⢿⣿⣧⠀⠀⠑⣄⠀⠀⠀⠀⠈⠀⠀⠀⠀⢀⣤⣾⣿⣿⣿⣿⡿⣿⠀⡇⢸⡇⣸⠀⣿⣿⠀⠀⠀⠀⠀
|
||||
# ⠀⠈⢢⡈⢷⣵⣔⡋⠀⠀⣠⠏⣠⢞⣽⣿⣿⣿⣿⢉⣹⣿⣿⣷⣤⠀⠀⠙⢦⡀⢀⣀⣤⣴⠒⠊⠉⠉⠉⠉⠙⠻⣿⣷⠹⣧⢣⣾⠃⡟⢀⣿⠟⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⣩⠦⣵⣯⣭⣵⠞⣡⡾⣣⣾⣿⠟⠛⠉⠸⣏⠀⠚⣿⣿⣿⣿⣷⣠⣤⣭⣿⣿⡟⢿⣦⠀⢀⠀⠀⠀⠀⠀⠀⠙⣷⣯⣯⢎⡼⣡⠞⠁⠀⠀⠀⠀⠀⠀
|
||||
# ⢀⡴⠊⢡⠞⢁⡰⢊⣵⡾⠟⠋⠉⢹⣹⠀⠀⠀⠀⠈⠒⣞⠁⣹⠿⡷⣮⣭⣭⣽⢿⢯⡗⠈⢻⣷⡋⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⡻⣍⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠉⠖⢠⠋⣴⡫⡾⠋⠁⠀⠀⠀⠢⣄⣏⡇⠀⠀⠀⠀⠀⠈⠋⠻⣤⢷⣃⣹⣏⣉⡦⠞⠁⢀⠞⠳⡳⡀⠀⠀⠀⠀⠀⠀⠀⢹⠑⠷⣌⠓⢤⡀⠀⠀⠀⠀⠀⠀
|
||||
# ⡞⢠⣯⡿⠋⡜⠀⠀⠀⠀⠀⠀⠀⠀⢹⠻⡤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠁⠀⠀⠀⢀⡴⠀⠀⠀⠱⡑⡄⢀⡠⠤⠤⠤⠤⠬⠧⠤⠬⠷⣦⣈⡓⠦⣀⠀⠀⠀
|
||||
# ⠀⣾⠋⢠⣾⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣇⢧⠀⠉⠓⠲⠤⣀⣀⠀⠀⠀⠀⠀⠀⡰⠋⠀⠀⠀⣀⠴⢛⣉⡠⠤⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⣿⣧⡈⠙⠢⣄
|
||||
# ⢰⠃⡴⡇⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⢸⡀⠀⠀⠀⠀⠀⠈⠉⠙⠂⠀⠀⠀⠀⠀⢀⡠⣊⠥⠚⠁⣀⡤⠴⠒⠋⠁⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⡇⠀⠘⢦
|
||||
# ⣿⢞⡾⢠⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣇⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣻⢚⣁⣤⠖⠋⣁⡤⣤⡴⠒⠒⠋⠁⢀⣀⣀⣀⡤⡼⣧⡟⢿⠀⠀⠈
|
||||
# ⣏⣼⡇⣿⢧⠀⠀⠀⠀⠀⠀⠐⡄⠀⠀⠀⢸⢹⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢩⣿⣶⣶⣾⣇⠔⣉⣤⡖⠿⣍⣹⣿⣿⣯⣿⡿⠋⢹⡜⣾⡇⠀⠀
|
||||
# ⣿⣿⢹⡇⢸⠀⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀⠈⡏⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⣾⣿⣿⣿⣿⣿⡉⠉⣿⣿⢄⡀⠀⠈⠉⣳⠀⠀⠀⣾⣷⢸⡇⠀⠀
|
||||
# ⢿⣿⣸⡇⣿⡇⠀⠀⠀⠀⠀⠀⠀⡗⠀⠀⢀⡵⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣸⣿⣿⣿⣿⣿⣿⣧⠀⣏⣋⠀⠀⠀⠀⠀⡇⠀⠀⠀⣿⣿⣿⡇⠀⢸
|
||||
# ⠀⠻⡏⢧⣿⣿⡀⠀⠀⠀⠀⠀⠀⡆⢀⠔⠋⠀⠻⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣿⣿⣿⣿⣿⡿⠀⣿⡯⢹⣦⣀⣀⣠⠇⠀⠀⢸⣿⣿⣿⠁⢀⣿
|
||||
# ⡀⠀⠙⢼⣿⣿⣧⠀⠀⠀⠀⠀⢠⠗⠁⠀⠀⠀⣰⠅⢺⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⣿⣿⣿⣿⣿⣿⠁⢰⣷⢤⡀⠀⠀⠀⠉⡇⠀⠀⢸⣿⡿⠃⢀⣾⠟
|
||||
# ⢽⣆⠙⢆⡙⢿⣿⡆⠀⠀⠀⠀⠈⡶⣄⠀⢠⠞⠁⠀⠀⠉⣵⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣿⠃⠀⡾⠿⡀⠁⠀⠀⠀⠸⡇⠀⠀⣾⠟⠁⢠⠟⠁⠀
|
||||
# ⠀⠙⢧⠀⠙⣦⣝⢷⠀⠀⠀⠀⠀⣧⠟⡟⠁⠀⠀⠀⢀⣀⣼⠻⣥⣄⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠏⠀⢸⣿⠀⠈⠳⣄⠀⠀⡔⣧⠀⠀⣯⡇⣰⠃⠀⠀⠀
|
||||
# ⠀⠀⠀⡄⠀⠸⣿⣧⣇⠀⠀⠀⠀⢿⠀⠻⣦⠀⠀⡾⣍⠀⠀⠀⠈⠻⣿⣦⡀⠀⠀⠀⠀⠘⣿⣿⡟⠀⢠⣧⡁⠀⠂⠤⢴⡓⠋⠀⡏⠀⠀⣿⠀⡇⠀⠀⠀⠀
|
||||
# ⢧⠀⢠⣇⠀⠀⣿⣿⢻⡄⠀⠀⠀⢸⣄⡀⠈⠳⣦⠿⢛⡷⠀⠠⠖⠉⠀⠙⢿⢢⣀⠀⠀⠀⣿⡿⠀⢀⣞⠏⠈⢦⡀⠀⣴⠃⢀⣾⠃⠀⠀⣿⡀⣇⠀⠀⠀⠀
|
||||
# ⢦⣠⢾⠏⢀⣼⡿⠇⢸⣧⠀⠀⠀⠘⡿⣿⣦⣄⡈⠉⠙⢷⣼⣷⡀⢀⡠⠖⠊⠉⠻⡗⢤⣰⡟⠁⣠⣿⠟⠤⣄⡈⠳⣰⡋⢠⡾⠃⠀⠀⢠⠿⣷⡈⠣⣄⣀⡴
|
||||
# ⡯⠵⡷⠖⠋⣽⠀⠀⣾⣿⡀⠀⠀⠀⠹⣄⠈⠛⠻⠿⠶⣾⣷⣼⣇⢺⣹⣧⠀⠀⣠⠔⠛⠿⣿⣶⠿⠁⠀⠀⠀⣸⣶⣷⠾⠋⠀⠀⠀⢀⡞⠀⠈⠉⠙⠛⠉⠀
|
||||
# ⠀⠀⡇⡀⠀⡏⠀⡸⣿⣿⡇⠀⠀⠀⠀⠙⣦⡀⠀⠀⠀⠀⠀⠀⢨⡉⠁⢸⣷⡎⠀⠀⠀⢠⠃⠈⢳⡀⠀⢀⣴⠛⠉⠁⠀⠀⠀⢀⣴⠙⡆⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⡸⣱⠃⠀⣷⢰⠃⣿⣹⡇⠀⠀⠀⠀⠀⠈⠛⢶⣤⣀⣀⣰⣶⣶⣿⣿⣿⡿⠟⠦⣄⢀⠏⠀⠀⠀⢙⣦⣾⣿⣇⠀⠀⠀⢀⣶⠉⢸⡇⢳⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⢰⠃⡏⠀⠀⢿⡏⠀⢻⡳⡇⠀⠀⠀⠀⠀⠀⠀⠀⢸⡉⠉⠛⠛⠛⠉⠉⠀⠀⠀⠀⠈⠉⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⠓⠚⣹⡇⠀⢸⡇⡜⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⢸⡈⣷⠀⠀⠘⣿⡀⠀⠙⢻⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠃⢻⣿⣿⣿⣿⠃⠀⢠⡟⠀⢠⡿⣱⠃⠀⠀⠀⠀⠀⠀⠀
|
||||
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣯⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⢀⣀⣀⣀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣻⣿⣿⣿⣿⠟⠙⣿⣿⣿⣶⣶⣶⡞⠉⠀⡉⠒⢤
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⠀⠀⠈⠻⠟⠋⠀⠀⠉⠒⠤⢥⣤⡬
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⠿⢿⣿⣿⠿⢿⣿⡿⢿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⠋⢻⣧⣾⣏⠙⣷⣿⣉⣷⣴⡿⢿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣼⣿⣿⣿⣧⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣾⣿⣿⣿⣷⣶⣶⣶⣤⣤⣀⣀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣴⣶⣿⣿⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⢿⣿⣶⣶
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⡀⣠⠴⠒⠒⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⠿⣛⣛⣯⣭⣭⣿⣿⣿⠿⠿⢿⣿⣿⣿⣿⣿⡿⠿⢻⣿⣿⣷⣶⣿⣿⣿⠿⠛⠋
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠈⠓⠮⣙⠢⠶⣿⣿⡿⢿⣻⣿⣿⣿⣶⣶⣶⣾⣿⣿⣿⣿⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣿⣿⣿⣿⣶⣿⣿⣿⣿⣿⡿⠟⠋⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠛⠛⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣿⣿⠛⠉⠉⠉⠿⠟⢹⣿⣿⣿⣿⣿⣿⣿⣟⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠙⡟⣿⣿⣿⣿⣇⠙⠋⠀⠀⠀⠀⠀⢠⠿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠂⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⢸⣿⣿⣿⣿⣧⡀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⣿⣿⢿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⢿⣿⣿⣿⣿⣿⣷⣦⣤⡴⠚⢑⣿⣿⣿⣿⡿⠙⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡸⠟⠛⢿⣿⣿⣿⣿⣷⣾⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠎⠀⠀⢀⣿⣿⡿⠁⠀⠈⠁⠉⠩⢐⣿⣿⣿⣧⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⢤⣤⡂⠀⢴⠞⣿⣿⠏⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⡿⠁⠀⠀⠉⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣋⠀⠀⠉⢳⣾⣾⣿⣿⣦⣀⡀⠀⠀⠀⠀⢸⡛⣿⣿⡇⠀⠀⠀⠀⢸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⡤⠤⣤⣄⣀⠀⣠⣤⣴⣷⣾⣿⣿⣷⣦⣄⣚⣉⣙⣛⣛⣛⠛⢳⡀⢀⠴⢦⠤⢭⣙⣿⡛⠒⣠⠆⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⢠⡤⠄⠖⠒⠒⠚⠋⠛⢆⠘⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡀⠑⢋⣬⣥⣄⣀⠀⠰⠾⢿⣿⠟⠓⠒⣓⡄⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠘⢣⡀⢱⠖⢝⠻⡷⠾⠶⠾⡄⢀⡀⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣻⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣦⣤⣭⣧⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠹⣶⠂⠂⠁⠛⠂⠒⠤⢽⣿⠿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠛⠁⠀⢸⣿⣿⣿⣿⣿⣿⡻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⣟⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠚⠛⠿⢿⣛⡻⢯⣁⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣤⡀⠀⣀⡠⠴
|
||||
# ⠀⠀⠀⢹⡀⢐⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠓⠀⠠⢼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⢿⣿⣿⣿⣿⣿⣿⣿⡿⣻⢟⡡⢎⡿
|
||||
# ⠀⠀⠀⠘⡇⠘⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⢿⣿⣿⣿⣿⣿⣿⠶⠋⣭⠞⢴⣟
|
||||
# ⠀⠀⠀⠀⠃⠀⠑⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡷⠶⠶⠶⠿⢻⡿⠟⠋⡁⠀⠃⠀⠀⢠⣿
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠣⠑⠂⠀⠀⠀⠀⡀⠋⡠⠅⠈⠀⠠⢨⣿
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⡴⠀⠀⠀⠀⠋⠀⠀⠀⠀⠀⠀⠁⣾⠋
|
||||
# ⠀⠀⠀⠀⠀⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣯⠄⡀⠀⠄⠀⠔⠁⠀⠀⠀⠀⠀⢰⠃⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢄⠀⠀⠀⠀⠀⠀⠀⠀⢨⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡎⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡘⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⡀⠀⠀⠀⡾⠛⠛⠛⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠁⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⣸⣷⠿⣿⣿⣶⣤⣍⡻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⡎⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⡇⠀⠀⠈⠉⠛⠿⣿⣿⣦⣽⣛⣿⣿⣿⣯⣭⣥⣤⣶⣾⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⡰⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠁⠀⠀⠀⠀⠀⠀⠀⠉⠙⣿⣿⣿⠟⠋⠉⠉⠉⠉⠉⠙⠛⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⢿⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠋⠁⠀⠀⠀⠀⠀⠀⠂⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⢰⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⢠⡏⢸⠁⠀⠀⠀⠀⠀⠀⠀⠀⢠⠇⠀⠀⠀⠀⠀⠀⠀⠀⢰⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⠸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀⠀⠸⡇⡾⠀⠀⠀⠀⠀⠀⠀⠀⢀⡞⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢷⣄⣀⣀⣀⣀⣀⠤⢴⣧⣷⣦⣀⠀⠀⠀⠀⠀⢀⡞⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠁⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⡈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⡀⠀⠀⢀⣀⣀⠘⣿⣿⣿⣿⣿⣷⣶⣶⣴⡞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
# ⠀⠀⠀⠀⠀⠀⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⢀⣐⡒⠒⣉⣥⠤⣿⣿⣿⣿⣿⣿⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⡼⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
|
||||
190
modules.json
190
modules.json
@@ -471,7 +471,7 @@
|
||||
},
|
||||
"SunnexGB/Heroku-Modules/SpotifyLyrics.py": {
|
||||
"name": "SpotifyLyrics",
|
||||
"description": "life lyrics current song",
|
||||
"description": "",
|
||||
"cls_doc": {},
|
||||
"meta": {
|
||||
"pic": "https://r2.fakecrime.bio/uploads/f49a9294-36ad-4fc4-801f-48cb049111d6.jpg",
|
||||
@@ -481,16 +481,15 @@
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"snowl": "- show synchronized lyrics for current Spotify track | (RU) - показать синхронизированный текст песни"
|
||||
"snowl": ""
|
||||
}
|
||||
],
|
||||
"new_commands": [
|
||||
{
|
||||
"name": "snowl",
|
||||
"original_name": "snowlcmd",
|
||||
"original_name": "snowl",
|
||||
"description": {
|
||||
"default": "- show synchronized lyrics for current Spotify track",
|
||||
"ru": "- показать синхронизированный текст песни"
|
||||
"default": ""
|
||||
},
|
||||
"cmd_names": {},
|
||||
"aliases": [],
|
||||
@@ -502,24 +501,27 @@
|
||||
],
|
||||
"inline_handlers": [],
|
||||
"strings": {
|
||||
"name": "SpotifyLyrics",
|
||||
"no_spotifymod": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>SpotifyMod not found,but u can install it. You can also support developer: </b> @ke_mods",
|
||||
"no_auth": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> You not authorized in SpotifyMod, visit you Saved Messages.</b>",
|
||||
"no_spotify": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>Nothing is playing on Spotify.</b>",
|
||||
"no_lyrics": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>Lyrics not found for:</b> <code>{}</code>",
|
||||
"not_synced": "<i><tg-emoji emoji-id=5431445849026611010>⚠️</tg-emoji> Lyrics are not synchronized.</i>\n\n",
|
||||
"finished": "<tg-emoji emoji-id=5429638011392377649>‼️</tg-emoji> Playback ended or track changed.",
|
||||
"header": "<tg-emoji emoji-id=5429413328768224565>🎤</tg-emoji> <b>{} - {}</b>\n\n",
|
||||
"timeout": "<b><tg-emoji emoji-id=5429455831764584284>⏳</tg-emoji></b><b> Oopsi, looks like we've got a timeout here</b>.",
|
||||
"cls_doc_ru": "Лайв слова текущей песни.",
|
||||
"no_spotifymod_ru": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>SpotifyMod не найден,но его можно установить. Вы также можете поддержать разработчика: </b> @ke_mods",
|
||||
"no_auth_ru": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> Вы не авторизированы в SpotifyMod, перейдите в Избранное.</b>",
|
||||
"no_spotify_ru": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>В Spotify ничего не играет.</b>",
|
||||
"no_lyrics_ru": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>Текст не найден для:</b> <code>{}</code>",
|
||||
"not_synced_ru": "<i><tg-emoji emoji-id=5431445849026611010>⚠️</tg-emoji> Текст не синхронизирован.</i>\n\n",
|
||||
"finished_ru": "<tg-emoji emoji-id=5429638011392377649>‼️</tg-emoji> Воспроизведение завершено или трек сменился.",
|
||||
"header_ru": "<tg-emoji emoji-id=5429413328768224565>🎤</tg-emoji> <b>{} - {}</b>\n\n",
|
||||
"timeout_ru": "<b><tg-emoji emoji-id=5429455831764584284>⏳</tg-emoji></b><b> Упси, похоже кто то словил таймаут.</b>."
|
||||
"name": "migration",
|
||||
"no_SpotifyLyrics": "u're not use SpotifyLyrics,install my new module.",
|
||||
"inline_msg": "if u ready to migration pls click on the button.",
|
||||
"already": "module already install, u're ready for migration ur db?",
|
||||
"restartf": "u ready restart ur ub?",
|
||||
"restartf_btn": "Ok",
|
||||
"done": "migration is done.",
|
||||
"confirm": "confirm",
|
||||
"install": "install LiveLyrics",
|
||||
"close": "close",
|
||||
"migrate_db": "Yep",
|
||||
"no_SpotifyLyrics_ru": "ты не юзал SpotifyLyrics,установи новый модуль.",
|
||||
"inline_msg_ru": "если вы готовы к миграции нажмите на кнопку ниже.",
|
||||
"already_ru": "модуль уже стоиит,готовы к переносу данных?",
|
||||
"restartf_ru": "вы готовы к перезапуску юб?",
|
||||
"restartf_btn_ru": "Лан",
|
||||
"done_ru": "миграция завершена.",
|
||||
"confirm_ru": "подтвердить",
|
||||
"install_ru": "установить LiveLyrics",
|
||||
"close_ru": "закрыть",
|
||||
"migrate_db_ru": "Ага"
|
||||
},
|
||||
"has_on_load": false,
|
||||
"has_on_unload": false,
|
||||
@@ -705,6 +707,96 @@
|
||||
"has_on_unload": false,
|
||||
"class_cmd_names": {}
|
||||
},
|
||||
"SunnexGB/Heroku-Modules/LiveLyrics.py": {
|
||||
"name": "LiveLyrics",
|
||||
"description": "life lyrics current song",
|
||||
"cls_doc": {
|
||||
"ru": "Лайв слова текущей песни."
|
||||
},
|
||||
"meta": {
|
||||
"pic": "https://r2.fakecrime.bio/uploads/6725e5a0-0c9e-48ed-be85-dfd857c2aa5f.jpg",
|
||||
"banner": "https://r2.fakecrime.bio/uploads/6725e5a0-0c9e-48ed-be85-dfd857c2aa5f.jpg",
|
||||
"developer": "@SunnexGB",
|
||||
"fhsdesc": "Spotify, YaMusic, music, музыка, Lyrics, слова, текст, трек, песня"
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"snowl": "- show synchronized lyrics for current track | (RU) - показать синхронизированный текст песни"
|
||||
},
|
||||
{
|
||||
"ynowl": "- show synchronized lyrics for current track | (RU) - показать синхронизированный текст песни"
|
||||
}
|
||||
],
|
||||
"new_commands": [
|
||||
{
|
||||
"name": "snowl",
|
||||
"original_name": "snowlcmd",
|
||||
"description": {
|
||||
"default": "- show synchronized lyrics for current track",
|
||||
"ru": "- показать синхронизированный текст песни"
|
||||
},
|
||||
"cmd_names": {},
|
||||
"aliases": [],
|
||||
"usage": null,
|
||||
"inline": false,
|
||||
"is_inline_handler": false,
|
||||
"decorators": []
|
||||
},
|
||||
{
|
||||
"name": "ynowl",
|
||||
"original_name": "ynowlcmd",
|
||||
"description": {
|
||||
"default": "- show synchronized lyrics for current track",
|
||||
"ru": "- показать синхронизированный текст песни"
|
||||
},
|
||||
"cmd_names": {},
|
||||
"aliases": [],
|
||||
"usage": null,
|
||||
"inline": false,
|
||||
"is_inline_handler": false,
|
||||
"decorators": []
|
||||
}
|
||||
],
|
||||
"inline_handlers": [],
|
||||
"strings": {
|
||||
"name": "LiveLyrics",
|
||||
"no_spotifymod": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>SpotifyMod not found,but u can install it. You can also support developer: </b> @ke_mods",
|
||||
"no_yamusic": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>YaMusicMod not found,but u can install it. You can also support developer: </b> @codrago_m",
|
||||
"no_auth_spotify": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> You not authorized in SpotifyMod, visit you Saved Messages.</b>",
|
||||
"no_auth_yamusic": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> You not authorized in SpotifyMod, visit you Saved Messages and setup token to continue.</b>",
|
||||
"no_spotify": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>Nothing is playing on Spotify.</b>",
|
||||
"no_ym": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>Nothing is playing on YaMusic.</b>",
|
||||
"no_lyrics": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>Lyrics not found for:</b> <code>{}</code>",
|
||||
"not_synced": "<i><tg-emoji emoji-id=5431445849026611010>⚠️</tg-emoji> Lyrics are not synchronized.</i>\n\n",
|
||||
"TrackEnded": "<tg-emoji emoji-id=5429638011392377649>‼️</tg-emoji> Playback ended or track changed.",
|
||||
"header": "<tg-emoji emoji-id=5429413328768224565>🎤</tg-emoji> <b>{} - {}</b>\n\n",
|
||||
"timeout": "<b><tg-emoji emoji-id=5429455831764584284>⏳</tg-emoji></b><b> Oopsi, looks like we've got a timeout here</b>.",
|
||||
"yamusic_installed": "YaMusic installed!",
|
||||
"spotify_installed": "SpotifyMod installed!",
|
||||
"song_link": "🔗 song.link",
|
||||
"close": "❌ Close",
|
||||
"ok": "OK",
|
||||
"no_spotifymod_ru": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>SpotifyMod не найден,но его можно установить. Вы также можете поддержать разработчика: </b> @ke_mods",
|
||||
"no_yamusic_ru": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>YaMusicMod не найден, но его можно установить. Вы также можете поддержать разработчика: </b> @codrago_m",
|
||||
"no_auth_spotify_ru": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji> <b>Вы не авторизованы в SpotifyMod. Перейдите в Избранное.</b>",
|
||||
"no_auth_yamusic_ru": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> Вы не авторизированы в YaMusicMod, перейдите в Избранное и установите токен для продолжения работы.</b>",
|
||||
"no_spotify_ru": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>В Spotify ничего не играет.</b>",
|
||||
"no_ym_ru": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>В YaMusic ничего не играет.</b>",
|
||||
"no_lyrics_ru": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>Текст не найден для:</b> <code>{}</code>",
|
||||
"not_synced_ru": "<i><tg-emoji emoji-id=5431445849026611010>⚠️</tg-emoji> Текст не синхронизирован.</i>\n\n",
|
||||
"TrackEnded_ru": "<tg-emoji emoji-id=5429638011392377649>‼️</tg-emoji> Воспроизведение завершено или трек сменился.",
|
||||
"header_ru": "<tg-emoji emoji-id=5429413328768224565>🎤</tg-emoji> <b>{} - {}</b>\n\n",
|
||||
"timeout_ru": "<b><tg-emoji emoji-id=5429455831764584284>⏳</tg-emoji></b><b> Упси, похоже кто то словил таймаут.</b>.",
|
||||
"yamusic_installed_ru": "YaMusic Установлен!",
|
||||
"spotify_installed_ru": "SpotifyMod Установлен!",
|
||||
"song_link_ru": "🔗 song.link",
|
||||
"close_ru": "❌ Закрыть",
|
||||
"ok_ru": "OK"
|
||||
},
|
||||
"has_on_load": false,
|
||||
"has_on_unload": false,
|
||||
"class_cmd_names": {}
|
||||
},
|
||||
"SunnexGB/Heroku-Modules/spotify_ph.py": {
|
||||
"name": "spotifyph",
|
||||
"description": "Progress bar current track in spotify",
|
||||
@@ -859,7 +951,7 @@
|
||||
},
|
||||
"SunnexGB/Heroku-Modules/YandexLyrics.py": {
|
||||
"name": "YandexLyrics",
|
||||
"description": "life lyrics current song",
|
||||
"description": "",
|
||||
"cls_doc": {},
|
||||
"meta": {
|
||||
"pic": "https://r2.fakecrime.bio/uploads/ab42b5e2-91f1-4ed1-8002-51b3184e3839.jpg",
|
||||
@@ -869,16 +961,15 @@
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"ynowl": "- show synchronized lyrics for current YaMusic track | (RU) - показать синхронизированный текст песни"
|
||||
"ynowl": ""
|
||||
}
|
||||
],
|
||||
"new_commands": [
|
||||
{
|
||||
"name": "ynowl",
|
||||
"original_name": "ynowlcmd",
|
||||
"original_name": "ynowl",
|
||||
"description": {
|
||||
"default": "- show synchronized lyrics for current YaMusic track",
|
||||
"ru": "- показать синхронизированный текст песни"
|
||||
"default": ""
|
||||
},
|
||||
"cmd_names": {},
|
||||
"aliases": [],
|
||||
@@ -890,24 +981,27 @@
|
||||
],
|
||||
"inline_handlers": [],
|
||||
"strings": {
|
||||
"name": "YandexLyrics",
|
||||
"no_YaMusicMod": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>YaMusicMod not found,but u can install it. You can also support developer: </b> @codrago_m",
|
||||
"no_auth": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> You not authorized in SpotifyMod, visit you Saved Messages and setup token to continue.</b>",
|
||||
"no_ym": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>Nothing is playing on YaMusic.</b>",
|
||||
"no_lyrics": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>Lyrics not found for:</b> <code>{}</code>",
|
||||
"not_synced": "<i><tg-emoji emoji-id=5431445849026611010>⚠️</tg-emoji> Lyrics are not synchronized.</i>\n\n",
|
||||
"finished": "<tg-emoji emoji-id=5429638011392377649>‼️</tg-emoji> Playback ended or track changed.",
|
||||
"header": "<tg-emoji emoji-id=5429413328768224565>🎤</tg-emoji> <b>{} - {}</b>\n\n",
|
||||
"timeout": "<b><tg-emoji emoji-id=5429455831764584284>⏳</tg-emoji></b><b> Oopsi, looks like we've got a timeout here</b>.",
|
||||
"cls_doc_ru": "Лайв слова текущей песни.",
|
||||
"no_YaMusicMod_ru": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>YaMusicMod не найден,но его можно установить. Вы также можете поддержать разработчика: </b> @codrago_m",
|
||||
"no_auth_ru": "<tg-emoji emoji-id=5429225166250984904>⁉️</tg-emoji><b> Вы не авторизированы в YaMusicMod, перейдите в Избранное и установите токен для продолжения работы.</b>",
|
||||
"no_ym_ru": "<tg-emoji emoji-id=5429164207780152924>😅</tg-emoji> <b>В YaMusic ничего не играет.</b>",
|
||||
"no_lyrics_ru": "<tg-emoji emoji-id=5431402435497181911>💢</tg-emoji> <b>Текст не найден для:</b> <code>{}</code>",
|
||||
"not_synced_ru": "<i><tg-emoji emoji-id=5431445849026611010>⚠️</tg-emoji> Текст не синхронизирован.</i>\n\n",
|
||||
"finished_ru": "<tg-emoji emoji-id=5429638011392377649>‼️</tg-emoji> Воспроизведение завершено или трек сменился.",
|
||||
"header_ru": "<tg-emoji emoji-id=5429413328768224565>🎤</tg-emoji> <b>{} - {}</b>\n\n",
|
||||
"timeout_ru": "<b><tg-emoji emoji-id=5429455831764584284>⏳</tg-emoji></b><b> Упси, похоже кто то словил таймаут.</b>."
|
||||
"name": "migration",
|
||||
"no_YaMusicMod": "u're not use YaMusicMod,install my new module.",
|
||||
"inline_msg": "if u ready to migration pls click on the button.",
|
||||
"already": "module already install, u're ready for migration ur db?",
|
||||
"restartf": "u ready restart ur ub?",
|
||||
"restartf_btn": "Ok",
|
||||
"done": "migration is done.",
|
||||
"confirm": "confirm",
|
||||
"install": "install LiveLyrics",
|
||||
"close": "close",
|
||||
"migrate_db": "Yep",
|
||||
"no_YaMusicMod_ru": "ты не юзал YaMusicMod,установи новый модуль.",
|
||||
"inline_msg_ru": "если вы готовы к миграции нажмите на кнопку ниже.",
|
||||
"already_ru": "модуль уже стоиит,готовы к переносу данных?",
|
||||
"restartf_ru": "вы готовы к перезапуску юб?",
|
||||
"restartf_btn_ru": "Лан",
|
||||
"done_ru": "миграция завершена.",
|
||||
"confirm_ru": "подтвердить",
|
||||
"install_ru": "установить LiveLyrics",
|
||||
"close_ru": "закрыть",
|
||||
"migrate_db_ru": "Ага"
|
||||
},
|
||||
"has_on_load": false,
|
||||
"has_on_unload": false,
|
||||
@@ -80016,7 +80110,7 @@
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"total_modules": 1011,
|
||||
"generated_at": "2026-06-12T08:37:15.698186"
|
||||
"total_modules": 1012,
|
||||
"generated_at": "2026-06-16T03:26:11.134799"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user