async def on_edit(c, cq): lang = cq._lang key = cq.matches[0]['key'] value = (await Config.get_or_none(key=key)).value text = lang.edit_env_text(key=key, value=value) keyboard = ikb([[(lang.back, 'setting_env')]]) last_msg = await cq.edit(text, keyboard) env_requires_restart = ['PREFIXES', 'DATABASE_URL', 'BOT_TOKEN'] try: while True: msg = await cq.from_user.listen(filters.text & ~filters.edited, None) await last_msg.remove_keyboard() await Config.get(key=key).update(value=msg.text) if key in env_requires_restart: text = lang.edit_env_text_restart(key=key, value=msg.text) keyboard = ikb([[(lang.restart_now, 'restart_now')], [(lang.back, 'setting_env')]]) else: text = lang.edit_env_text(key=key, value=msg.text) keyboard = ikb([[(lang.back, 'setting_env')]]) last_msg = await msg.reply_text(text, reply_markup=keyboard) except errors.ListenerCanceled: pass
async def request_episodes_confirm(bot: Amime, callback: CallbackQuery): user = callback.from_user lang = callback._lang anime_id = int(callback.matches[0].group(1)) language = callback.matches[0].group(2) anime = await anilist.AsyncClient().get(anime_id) is_collaborator = await filters.collaborator(bot, callback) or bot.is_sudo(user) requests = await Requests.filter(item=anime_id, type="anime") requests = sorted(requests, key=lambda request: request.id) now_date = datetime.datetime.now().replace(tzinfo=datetime.timezone.utc) if not is_collaborator and len(requests) > 0: request = requests[-1] request_date = request.datetime date = now_date - request_date if date.seconds < (1 * 60 * 60 * 24): await callback.answer( lang.requested_in_last_24h_alert( date=request_date.strftime("%H:%M:%S - %d/%m/%Y")), show_alert=True, ) return await Requests.create( user=user.id, item=anime_id, type="anime", datetime=now_date, done=False, ) text = "<b>New request</b>" text += f"\n<b>From</b>: {user.mention()}" text += "\n<b>Anime</b>:" text += f"\n <b>ID</b>: <code>{anime.id}</code>" text += f"\n <b>Name</b>: <code>{anime.title.romaji}</code>" text += ( f"\n <b>Language</b>: <code>{lang.strings[language]['LANGUAGE_NAME']}</code>" ) text += "\n\n#REQUEST" await bot.send_message( CHATS["requests"], text, reply_markup=ikb([[("🆙 Get", f"request get anime {anime_id} {language}")]]), ) await callback.answer(lang.request_sent_alert, show_alert=True) matches = re.match(r"(\d+) (\d+)\s?(\d+)?", f"{anime_id} {user.id}") callback.matches = [matches] await anime_view(bot, callback)
async def request_episodes(bot: Amime, callback: CallbackQuery): message = callback.message user = callback.from_user lang = callback._lang anime_id = int(callback.matches[0].group(1)) language = callback.matches[0].group(2) buttons = [] for code, obj in lang.strings.items(): text, data = ((f"✅ {obj['LANGUAGE_NAME']}", "noop") if code == language else ( obj["LANGUAGE_NAME"], f"request episodes {anime_id} {code}", )) buttons.append((text, data)) keyboard = array_chunk(buttons, 2) keyboard.append([ (lang.confirm_button, f"request episodes confirm {anime_id} {language}"), (lang.back_button, f"anime {anime_id} {user.id}"), ]) await message.edit_text( lang.request_content_text, reply_markup=ikb(keyboard), )
async def on_info_plugin(c, cq): lang = cq._lang basename = cq.matches[0]['basename'] plugin_type = cq.matches[0]['plugin_type'] pg = int(cq.matches[0]['pg']) if basename not in plugins[plugin_type]: return await cq.answer('UNKNOWN') plugin = plugins[plugin_type][basename] status = lang.active first_btn = (lang.deactivate, f'deactivate_plugin {basename} {plugin_type} {pg}') inactive = await get_inactive_plugins(plugins) if plugin['notation'] in inactive: status = lang.inactive first_btn = (lang.activate, f'activate_plugin {basename} {plugin_type} {pg}') status_line = '\n' + status lines = [[ first_btn, (lang.remove, f'remove_plugin {basename} {plugin_type} {pg}') ]] if 'settings' in plugin and plugin['settings']: lines.append([(lang.settings, f'plugin_settings {basename} {plugin_type} {pg}')]) lines.append([(lang.back, f'{plugin_type}_plugins {pg}')]) keyb = ikb(lines) text = write_plugin_info(plugins, lang, plugin, status_line=status_line) await cq.edit(text, keyb, disable_web_page_preview=True)
async def notify_episodes(bot: Amime, callback: CallbackQuery): message = callback.message lang = callback._lang anime_id = int(callback.matches[0].group(1)) season = int(callback.matches[0].group(2)) subtitled = bool(int(callback.matches[0].group(3))) language = callback.matches[0].group(4) page = int(callback.matches[0].group(5)) keyboard = [ [ ( lang.confirm_button, f"notify episodes confirm {anime_id} {season} {int(subtitled)} {language} {page}", ), ( lang.cancel_button, f"manage anime {anime_id} {season} {int(subtitled)} {language} {page}", ), ], ] await message.edit_text( lang.confirm_text, reply_markup=ikb(keyboard), )
async def favorite_callback(bot: Amime, callback: CallbackQuery): content_type = callback.matches[0]["type"] content_id = int(callback.matches[0]["id"]) message = callback.message user = callback.from_user lang = callback._lang favorite = await Favorites.get_or_none(user=user.id, item=content_id, type=content_type) if favorite is None: await Favorites.create(user=user.id, item=content_id, type=content_type) await callback.answer(lang.added_to_favorites_alert, show_alert=True) else: await favorite.delete() await callback.answer(lang.removed_from_favorites_alert, show_alert=True) keyboard = bki(message.reply_markup) for line, column in enumerate(keyboard): for index, button in enumerate(column): if button[1].startswith("favorite"): keyboard[line][index] = await get_favorite_button( lang, user, content_type, content_id) await callback.edit_message_reply_markup(ikb(keyboard))
async def get_device(u, model: Dict): is_query = isinstance(u, CallbackQuery) text = '<b>HyconOS</b>\n' text += '\n' text += f'<b>Brand</b>: <code>{model["brand"]}</code>\n' text += f'<b>Device</b>: <code>{model["name"]}</code>\n' text += f'<b>Codename</b>: <code>{model["codename"]}</code>\n' versions = [] maintainers = [] for version in model['supported_versions']: maintainers.append(version['maintainer_name'] + ' ' + '(' + version['version_name'] + ')') versions.append(version['version_name'] + ' (' + version['version_code'] + ')') text += f'<b>Maintainer(s)</b>: <code>{", ".join(maintainers)}</code>\n' text += f'<b>Version(s)</b>: <code>{", ".join(versions)}</code>\n' keyboard = [] builds = get_builds(model['codename']) text += f'<b>Builds</b>: <code>{len(builds)}</code>\n' if len(builds) > 0: id = len(BUILDS.keys()) BUILDS[str(id)] = { 'user_id': u.from_user.id, 'chat_id': u.message.chat.id if is_query else u.chat.id, 'codename': model['codename'], 'builds': builds } keyboard.append(('📓 Builds', f'get_builds {id} 1')) kwargs = {} if len(keyboard) > 0: kwargs['reply_markup'] = ikb([keyboard]) await (u.edit_message_text if is_query else u.reply_text)(text=text, **kwargs)
async def manga_upcoming(bot: Amime, callback: CallbackQuery): page = int(callback.matches[0]["page"]) message = callback.message lang = callback._lang keyboard = [] async with httpx.AsyncClient(http2=True) as client: response = await client.post( url="https://graphql.anilist.co", json=dict( query=""" query($per_page: Int) { Page(page: 1, perPage: $per_page) { media(type: MANGA, sort: POPULARITY_DESC, status: NOT_YET_RELEASED) { id title { romaji english native } siteUrl } } } """, variables=dict(per_page=50, ), ), headers={ "Content-Type": "application/json", "Accept": "application/json", }, ) data = response.json() await client.aclose() if data["data"]: items = data["data"]["Page"]["media"] suggestions = [ Manga(id=item["id"], title=item["title"], url=item["siteUrl"]) for item in items ] layout = Pagination( suggestions, item_data=lambda i, pg: f"manga {i.id}", item_title=lambda i, pg: i.title.romaji, page_data=lambda pg: f"upcoming manga {pg}", ) lines = layout.create(page, lines=8) if len(lines) > 0: keyboard += lines keyboard.append([(lang.back_button, "manga")]) await message.edit_text( lang.upcoming_text, reply_markup=ikb(keyboard), )
async def manga_favorites(bot: Amime, callback: CallbackQuery): page = int(callback.matches[0]["page"]) message = callback.message user = callback.from_user lang = callback._lang keyboard = [] async with anilist.AsyncClient() as client: favorites = await Favorites.filter(user=user.id, type="manga") results = [] for favorite in favorites: manga = await client.get(favorite.item, "manga") results.append((favorite, manga)) layout = Pagination( results, item_data=lambda i, pg: f"manga {i[0].item}", item_title=lambda i, pg: i[1].title.romaji, page_data=lambda pg: f"favorites manga {pg}", ) lines = layout.create(page, lines=8) if len(lines) > 0: keyboard += lines keyboard.append([(lang.back_button, "manga")]) await message.edit_text( lang.favorites_text, reply_markup=ikb(keyboard), )
async def request_get(bot: Amime, callback: CallbackQuery): message = callback.message user = callback.from_user lang = callback._lang content_type = callback.matches[0].group(1) content_id = int(callback.matches[0].group(2)) language = callback.matches[0].group(3) text_splited = message.text.html.splitlines() text = "\n".join(text_splited[: len(text_splited) - 2]) text += f"\n<b>Caught by</b>: {user.mention()}" text += "\n\n#REQUEST" await message.edit_text( text, reply_markup=ikb( [ [ ( "↩️ Drop", f"request drop {content_type} {content_id} {language} {user.id}", ), ( "✅ Done", f"request done {content_type} {content_id} {language} {user.id}", ), ] ] ), )
async def manga_view_more(bot: Amime, callback: CallbackQuery): message = callback.message user = callback.from_user lang = callback._lang manga_id = int(callback.matches[0].group(1)) user_id = int(callback.matches[0].group(2)) if user_id != user.id: return async with anilist.AsyncClient() as client: manga = await client.get(manga_id, "manga") buttons = [ (lang.description_button, f"manga description {manga_id} {user_id} 1"), (lang.characters_button, f"manga characters {manga_id} {user_id}"), (lang.studios_button, f"manga studios {manga_id} {user_id}"), ] buttons.append(("🐢 Anilist", manga.url, "url")) keyboard = array_chunk(buttons, 2) keyboard.append([(lang.back_button, f"manga {manga_id} {user_id}")]) await message.edit_text( lang.view_more_text, reply_markup=ikb(keyboard), )
async def sudoers_interface(cq): lang = cq._lang c = cq._client text = lang.setting_sudoers_text + "\n" buttons = [] added = [] for user_id in sudoers: try: user_obj = await c.get_users(user_id) except: import traceback traceback.print_exc() user_obj = None id = user_obj.id if user_obj else user_id if id in added: continue added.append(id) mention = user_id if user_obj: mention = f'@{user_obj.username}' if user_obj.username else user_obj.first_name text += f"\n👤 {mention}" if id not in ['me', user.me.id, cq.from_user.id]: buttons.append((f"🗑 {mention}", f'remove_sudoer {user_id}')) lines = array_chunk(buttons, 2) if bot.me.username: lines.append([ (lang.add_sudoer, f"https://t.me/{bot.me.username}?start=add_sudoer", 'url') ]) lines.append([(lang.back, 'settings')]) keyboard = ikb(lines) return text, keyboard
async def anime_view_more(bot: Amime, callback: CallbackQuery): message = callback.message user = callback.from_user lang = callback._lang anime_id = int(callback.matches[0].group(1)) user_id = int(callback.matches[0].group(2)) if user_id != user.id: return async with anilist.AsyncClient() as client: anime = await client.get(anime_id, "anime") buttons = [ (lang.description_button, f"anime description {anime_id} {user_id} 1"), (lang.characters_button, f"anime characters {anime_id} {user_id}"), (lang.studios_button, f"anime studios {anime_id} {user_id}"), ] if hasattr(anime, "trailer"): if hasattr(anime.trailer, "url"): buttons.append((lang.trailer_button, anime.trailer.url, "url")) buttons.append(("🐢 Anilist", anime.url, "url")) keyboard = array_chunk(buttons, 2) keyboard.append([(lang.back_button, f"anime {anime_id} {user_id}")]) await message.edit_text( lang.view_more_text, reply_markup=ikb(keyboard), )
async def upgrade_message(bot: Amime, message: Message): sent = await message.reply_text("Checking for updates...") await (await asyncio.create_subprocess_shell("git fetch origin")).communicate() proc = await asyncio.create_subprocess_shell( "git log HEAD..origin/main", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT, ) stdout = (await proc.communicate())[0].decode() if proc.returncode == 0: if len(stdout) > 0: changelog = "<b>Changelog</b>:\n" commits = parse_commits(stdout) for c_hash, commit in commits.items(): changelog += f" - [<code>{c_hash[:7]}</code>] {commit['title']}\n" changelog += f"\n<b>New commits count</b>: <code>{len(commits)}</code>." keyboard = [[("🆕 Upgrade", "upgrade")]] await sent.edit_text(changelog, reply_markup=ikb(keyboard)) else: await sent.edit_text("There is nothing to update.") else: error = "" lines = stdout.split("\n") for line in lines: error += f"<code>{line}</code>\n" await sent.edit_text( f"Update failed (process exited with {proc.returncode}):\n{error}")
async def anime_view_characters(bot: Amime, callback: CallbackQuery): message = callback.message chat = message.chat user = callback.from_user lang = callback._lang anime_id = int(callback.matches[0].group(1)) user_id = int(callback.matches[0].group(2)) if user_id != user.id: return async with anilist.AsyncClient() as client: anime = await client.get(anime_id, "anime") keyboard = [ [ (lang.back_button, f"anime more {anime_id} {user_id}"), ], ] text = lang.characters_text characters = sorted(anime.characters, key=lambda character: character.id) for character in characters: text += f"\n• <code>{character.id}</code> - <a href='https://t.me/{bot.me.username}/?start=character_{character.id}'>{character.name.full}</a> (<i>{character.role}</i>)" await message.edit_text( text, reply_markup=ikb(keyboard), )
async def onabout(client, query): lang = query._lang text = lang.about_text keyboard = ikb([[(lang.back, 'start')]]) await query.edit_message_text(text, reply_markup=keyboard) await query.answer()
async def on_info_plugin(c, cq): lang = cq._lang basename = cq.matches[0]["basename"] plugin_type = cq.matches[0]["plugin_type"] pg = int(cq.matches[0]["pg"]) if basename not in plugins[plugin_type]: return await cq.answer("UNKNOWN") plugin = plugins[plugin_type][basename] status = lang.active first_btn = (lang.deactivate, f"deactivate_plugin {basename} {plugin_type} {pg}") inactive = await get_inactive_plugins(plugins) if plugin["notation"] in inactive: status = lang.inactive first_btn = (lang.activate, f"activate_plugin {basename} {plugin_type} {pg}") status_line = "\n" + status lines = [[ first_btn, (lang.remove, f"remove_plugin {basename} {plugin_type} {pg}") ]] if "settings" in plugin and plugin["settings"]: lines.append([(lang.settings, f"plugin_settings {basename} {plugin_type} {pg}")]) lines.append([(lang.back, f"{plugin_type}_plugins {pg}")]) keyb = ikb(lines) text = write_plugin_info(plugins, lang, plugin, status_line=status_line) await cq.edit(text, keyb, disable_web_page_preview=True)
async def manga_categorie(bot: Amime, callback: CallbackQuery): categorie = callback.matches[0]["categorie"] page = int(callback.matches[0]["page"]) message = callback.message lang = callback._lang genre = categorie.replace("_", " ") results = await anilist.AsyncClient().search(genre, "manga", 50) layout = Pagination( results, item_data=lambda i, pg: f"manga {i.id}", item_title=lambda i, pg: i.title.romaji, page_data=lambda pg: f"categorie manga {categorie} {pg}", ) lines = layout.create(page, lines=8) keyboard = [] if len(lines) > 0: keyboard += lines keyboard.append([(lang.back_button, "categories manga 1")]) await message.edit_text( lang.categorie_text.format(genre=lang.strings[lang.code][categorie]), reply_markup=ikb(keyboard), )
async def manga_start(bot: Amime, union: Union[CallbackQuery, Message]): is_callback = isinstance(union, CallbackQuery) message = union.message if is_callback else union lang = union._lang keyboard = [ [ (lang.suggestions_button, "suggestions manga 1"), (lang.categories_button, "categories manga 1"), ], [ (lang.upcoming_button, "upcoming manga 1"), (lang.favorites_button, "favorites manga 1"), ], [ (lang.search_button, "!m ", "switch_inline_query_current_chat"), ], ] if is_callback: keyboard.append([(lang.back_button, "start")]) await (message.edit_text if is_callback else message.reply_text)( lang.manga_text, reply_markup=ikb(keyboard), )
async def about(bot: Amime, union: Union[CallbackQuery, Message]): is_callback = isinstance(union, CallbackQuery) message = union.message if is_callback else union lang = union._lang kwargs: Dict = {} is_private = await filters.private(bot, message) if is_private and is_callback: keyboard = [ [ (lang.back_button, "start"), ], ] kwargs["reply_markup"] = ikb(keyboard) await (message.edit_text if is_callback else message.reply_text)( lang.about_text.format( bot_name=bot.me.first_name, github="<a href='https://github.com/AmanoTeam/AmimeWatch'>GitHub</a>", channel=f"<a href='https://t.me/c/{str(CHANNELS[lang.code])[4:]}/-1'>{lang.channel}</a>", group=f"<a href='https://t.me/c/{str(GROUPS[lang.code])[4:]}/-1'>{lang.group}</a>", ), disable_web_page_preview=True, **kwargs, )
async def on_about_userlixo(c, cq): lang = cq._lang subject = cq.matches[0]['subject'] keyboard = ikb([[(lang.back, 'help')]]) text = { "userlixo": lang.about_userlixo_text, "plugins": lang.about_plugins_text, "commands": lang.about_commands_text } text = text[subject] if subject == 'commands': commands = [*cmds.keys()] total = len(commands) prefixes = os.getenv('PREFIXES') examples = [] for n, p in enumerate([*prefixes]): if n > total - 1: # if passed the end break examples.append('<code>' + p + commands[n] + '</code>') examples = ', '.join(examples) commands_list = [*map(lambda x: f'<code>{x}</code>', commands)] text.escape_html = False text = text(total=total, commands=', '.join(commands_list[:-1]), last_command=commands_list[-1], prefixes=prefixes, examples=examples) await cq.edit(text, keyboard, disable_web_page_preview=True)
async def watched_callback(bot: Amime, callback: CallbackQuery): episode_id = int(callback.matches[0]["id"]) message = callback.message user = callback.from_user lang = callback._lang watched = await Watched.get_or_none( user=user.id, episode=episode_id, ) if watched is None: await Watched.create(user=user.id, episode=episode_id) else: await watched.delete() keyboard = bki(message.reply_markup) for line, column in enumerate(keyboard): for index, button in enumerate(column): if button[1].startswith("watched"): keyboard[line][index] = await get_watched_button( lang, user, episode_id, ) await callback.edit_message_reply_markup(ikb(keyboard))
async def on_help_module_m(c: Client, m: Message): module = m.matches[0]['module'] await m.reply_text( text='Press the button below to continue.', reply_markup=ikb([[ ('Check in PM', f'https://t.me/{bot.me.username}?start=help_{module}', 'url') ]]))
async def edit_text(self, text: str, reply_markup=None, *args, **kwargs): if type(reply_markup) == list: reply_markup = ikb(reply_markup) return await self._client.edit_message_text(self.chat.id, self.message_id, text, reply_markup=reply_markup, **kwargs)
def start(update, lang): text = lang.start_text( first_name=update.from_user.first_name ) keyboard = ikb([ [(lang.help, 'help'), (lang.about, 'about')] ]) return text, keyboard
def gen_options(lang): options = [] for LANGUAGE_CODE, obj in lang.strings.items(): button = [(obj['NAME'], f'set_lang {LANGUAGE_CODE}'), (f"✅ {obj['NAME']}", 'noop')][obj['NAME'] == lang.NAME] options.append(button) lines = [options] return ikb(lines)
async def nhentai_inline(bot: Amime, inline_query: InlineQuery): query = inline_query.matches[0]["query"].strip() lang = inline_query._lang results: List[InlineQueryResultPhoto] = [] if query.isdecimal(): search_results = [await get_data(int(query))] else: search_results = [ *filter(lambda manga: query.lower() in manga.title.lower(), await nHentai.all()) ] for manga in search_results: if manga is None: continue if len(results) >= 15: break text = f"<b>{manga.title}</b>" text += f"\n\n<b>ID</b>: <code>{manga.id}</code> (<b>NHENTAI</b>)" text += f"\n<b>{lang.artist}</b>: <a href=\"https://nhentai.net/artist/{manga.artist.replace(' ', '-')}/\">{manga.artist}</a>" tags = [ f"<a href=\"https://nhentai.net/tag/{tag.replace(' ', '-')}/\">{tag}</a>" for tag in manga.tags.split(", ") ] text += f"\n<b>{lang.tags}</b>: {', '.join(tags)}" text += f"\n<b>{lang.page}s</b>: <code>{manga.pages}</code>" results.append( InlineQueryResultPhoto( photo_url=manga.photo, title=manga.title, description=manga.tags, caption=text, reply_markup=ikb([[ ( "👠nHentai", f"https://nhentai.net/g/{manga.id}", "url", ), (lang.read_button, manga.url, "url"), ]]), )) if len(results) > 0: try: await inline_query.answer( results=results, is_gallery=False, cache_time=3, ) except QueryIdInvalid: pass
async def on_help_u(c, u): is_query = hasattr(u, 'data') lang = u._lang keyb = [[(lang.about_userlixo, 'about_userlixo')], [(lang.commands, 'about_commands'), (lang.plugins, 'about_plugins')], [(lang.chat, 'https://t.me/UserLixoChat', 'url'), (lang.channel, 'https://t.me/UserLixo', 'url')]] keyb = ikb(keyb) await (u.edit if is_query else u.reply)(lang.help_text, keyb)
async def on_start_u(c, u): is_query = hasattr(u, 'data') lang = u._lang keyb = ikb([[(lang.upgrade, 'upgrade'), [lang.restart, 'restart']], [(lang.commands, 'list_commands 0'), (lang.plugins, 'list_plugins')], [(lang.help, 'help'), (lang.settings, 'settings')]]) text = lang.start_text kwargs = {} if not is_query: kwargs['quote'] = True await (u.edit if is_query else u.reply)(text, keyb, **kwargs)
async def on_setting_language(c, cq): lang = cq._lang buttons = [] for code, obj in lang.strings.items(): text, data = ((f"✅ {obj['NAME']}", "noop") if obj["LANGUAGE_CODE"] == lang.code else (obj["NAME"], f"set_language {obj['LANGUAGE_CODE']}")) buttons.append((text, data)) lines = array_chunk(buttons, 2) lines.append([(lang.back, "settings")]) keyboard = ikb(lines) await cq.edit(lang.choose_language, keyboard)