From 28c60aa7a3205abc4d76c81e10bf966b7a35785f Mon Sep 17 00:00:00 2001 From: wowlikon Date: Sat, 11 Oct 2025 18:40:44 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=D0=B0=20?= =?UTF-8?q?=D0=B8=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=86=D0=B8=D0=B8=20?= =?UTF-8?q?=D0=BE=20=D0=BF=D0=B0=D1=82=D1=87=D0=B0=D1=85,=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=B0?= =?UTF-8?q?=D1=82=D1=87=D0=B0=20=D0=BB=D0=B0=D0=B9=D0=BA=D0=BE=D0=B2/?= =?UTF-8?q?=D0=B4=D0=B8=D0=B7=D0=BB=D0=B0=D0=B9=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/comment_vote.json | 1 + configs/insert_new.json | 1 - configs/welcome.json | 1 + main.py | 40 +++++++++---- patches/change_server.py | 13 ++--- patches/color_theme.py | 24 ++++---- patches/comment_vote.py | 99 ++++++++++++++++++++++++++++++++ patches/compress.py | 25 ++++---- patches/disable_ad.py | 3 +- patches/disable_beta_banner.py | 3 +- patches/insert_new.py | 34 ----------- patches/package_name.py | 13 +++-- patches/replace_navbar.py | 3 + patches/selectable_text.py | 5 +- patches/settings_urls.py | 14 ++--- patches/share_links.py | 6 +- patches/todo_custom_speed.py | 3 +- patches/todo_example.py | 3 +- patches/welcome.py | 56 ++++++++++++++++++ resources/ic_chevron_down.xml | 11 ++++ resources/ic_chevron_down_40.xml | 12 ++++ resources/ic_chevron_up.xml | 11 ++++ resources/ic_chevron_up_40.xml | 12 ++++ utils/hex.py | 6 -- utils/public.py | 18 ++++-- utils/smali_parser.py | 8 +++ utils/tools.py | 2 + 27 files changed, 313 insertions(+), 114 deletions(-) create mode 100644 configs/comment_vote.json delete mode 100644 configs/insert_new.json create mode 100644 configs/welcome.json create mode 100644 patches/comment_vote.py delete mode 100644 patches/insert_new.py create mode 100644 patches/welcome.py create mode 100644 resources/ic_chevron_down.xml create mode 100644 resources/ic_chevron_down_40.xml create mode 100644 resources/ic_chevron_up.xml create mode 100644 resources/ic_chevron_up_40.xml delete mode 100644 utils/hex.py diff --git a/configs/comment_vote.json b/configs/comment_vote.json new file mode 100644 index 0000000..e0caaa6 --- /dev/null +++ b/configs/comment_vote.json @@ -0,0 +1 @@ +{"enabled":true,"replace":true,"custom_icons":true,"icon_size":"18.0dip"} \ No newline at end of file diff --git a/configs/insert_new.json b/configs/insert_new.json deleted file mode 100644 index 310f75e..0000000 --- a/configs/insert_new.json +++ /dev/null @@ -1 +0,0 @@ -{"enabled":true} \ No newline at end of file diff --git a/configs/welcome.json b/configs/welcome.json new file mode 100644 index 0000000..a87e7c6 --- /dev/null +++ b/configs/welcome.json @@ -0,0 +1 @@ +{"enabled":true,"title":"Anixarty","description":"Описание","link_text":"МЫ В TELEGRAM","link_url":"https://t.me/http_teapod","skip_text":"Пропустить","title_bg_color":"#FFFFFF"} \ No newline at end of file diff --git a/main.py b/main.py index cdde703..968d3cb 100644 --- a/main.py +++ b/main.py @@ -13,7 +13,6 @@ from rich.prompt import Prompt from utils.config import * from utils.tools import * -# --- Paths --- console = Console() app = typer.Typer() @@ -30,6 +29,7 @@ class Patch: self.config = module.Config.model_validate_json((CONFIGS / f"{name}.json").read_text()) except Exception as e: console.print(f"[red]Ошибка при загрузке конфигурации патча {name}: {e}") + console.print(f"[yellow]Используются значения по умолчанию") self.config = module.Config() def apply(self, conf: Dict[str, Any]) -> bool: @@ -42,7 +42,7 @@ class Patch: return False -# ======================= INIT ========================= +# ========================= INIT ========================= @app.command() def init(): """Создание директорий и скачивание инструментов""" @@ -71,7 +71,7 @@ def init(): raise typer.Exit(1) -# ======================= INFO ========================= +# ========================= INFO ========================= @app.command() def info(patch_name: str = ""): """Вывод информации о патче""" @@ -81,18 +81,36 @@ def info(patch_name: str = ""): console.print(f"[green]Информация о патче {patch.name}:") console.print(f" [yellow]Приоритет: {patch.priority}") console.print(f" [yellow]Описание: {patch.module.__doc__}") + + console.print(f"[blue]Поля конфигурации") + for field_name, field_info in type(patch.config).model_fields.items(): + field_data = { + 'type': field_info.annotation.__name__, + 'description': field_info.description, + 'default': field_info.default, + 'json_schema_extra': field_info.json_schema_extra, + } + console.print(f'{field_name} {field_data}') + console.print("\n[blue]" + "="*50 + "\n") + else: + conf = load_config(console) console.print("[cyan]Список патчей:") + patch_list = [] for f in PATCHES.glob("*.py"): - if f.name.startswith("todo_") or f.name == "__init__.py": + if f.name == "__init__.py": continue + if f.name.startswith("todo_"): + try: priority = __import__(f"patches.{f.stem}.priority", fromlist=[""]) + except: priority = None + patch_list.append((priority, f" [{priority}] [yellow]{f.stem}: [yellow]⚠ в разработке")) continue - name = f.stem - if conf["patches"].get(name, {}).get("enabled", True): - console.print(f" [yellow]{name}: [green]✔ enabled") - else: - console.print(f" [yellow]{name}: [red]✘ disabled") + patch = Patch(f.stem, __import__(f"patches.{f.stem}", fromlist=[""])) + if patch.config.enabled: patch_list.append((patch.priority, f" [{patch.priority}] [yellow]{f.stem}: [green]✔ включен")) + else: patch_list.append((patch.priority, f" [{patch.priority}] [yellow]{f.stem}: [red]✘ выключен")) + for _, patch in sorted(patch_list, key=lambda x: (x[0] is None, x[0]), reverse=True): console.print(patch) +# ========================= UTIL ========================= def select_apk() -> Path: apks = [f for f in ORIGINAL.glob("*.apk")] if not apks: @@ -174,6 +192,7 @@ def compile(apk: Path, patches: List[Patch]): f.write(f"{'✔' if p.applied else '✘'} {p.name}\n") +# ========================= BUILD ========================= @app.command() def build( force: bool = typer.Option(False, "--force", "-f", help="Принудительная сборка"), @@ -219,5 +238,4 @@ def build( raise typer.Exit(1) -if __name__ == "__main__": - app() +if __name__ == "__main__": app() diff --git a/patches/change_server.py b/patches/change_server.py index 668dff9..543d716 100644 --- a/patches/change_server.py +++ b/patches/change_server.py @@ -1,5 +1,4 @@ -""" - Заменяет сервер api +"""Заменяет сервер api "change_server": { "enabled": true, @@ -24,11 +23,11 @@ class Config(PatchConfig): # Patch def apply(config: Config, base: Dict[str, Any]) -> bool: - response = requests.get(config.server) + response = requests.get(config.server) # Получаем данные для патча assert response.status_code == 200, f"Failed to fetch data {response.status_code} {response.text}" new_api = json.loads(response.text) - for item in new_api['modifications']: + for item in new_api['modifications']: # Применяем замены API tqdm.write(f"Изменение {item['file']}") filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/network/api/'+item['file'] @@ -40,7 +39,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: tqdm.write(f"⚠ Не найдено {item['src']}") f.write(content.replace(item['src'], item['dst'])) - tqdm.write(f"Изменение Github ссылки") + tqdm.write(f"Изменение Github ссылки") # Обновление ссылки на поиск серверов в Github filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/utils/anixnet/GithubPagesNetFetcher.smali' with open(filepath, 'r') as f: @@ -49,10 +48,10 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: with open(filepath, 'w') as f: f.write(content.replace('const-string v1, "https://anixhelper.github.io/pages/urls.json"', f'const-string v1, "{new_api["gh"]}"')) - content = "" - tqdm.write("Удаление динамического выбора сервера") + tqdm.write("Удаление динамического выбора сервера") # Отключение автовыбора сервера filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/DaggerApp_HiltComponents_SingletonC$SingletonCImpl$SwitchingProvider.smali' + content = "" with open(filepath, 'r') as f: for line in f.readlines(): if "addInterceptor" in line: continue diff --git a/patches/color_theme.py b/patches/color_theme.py index 9add593..ed8f1cb 100644 --- a/patches/color_theme.py +++ b/patches/color_theme.py @@ -1,5 +1,4 @@ -""" - Изменяет цветовую тему приложения и иконку +"""Изменяет цветовую тему приложения и иконку "color_theme": { "enabled": true, @@ -59,7 +58,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: main_color = config.colors.primary splash_color = config.colors.secondary - # No connection alert coolor + # Обновление сообщения об отсутствии подключения with open("./decompiled/assets/no_connection.html", "r", encoding="utf-8") as file: file_contents = file.read() @@ -68,33 +67,32 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: with open("./decompiled/assets/no_connection.html", "w", encoding="utf-8") as file: file.write(new_contents) - # For logo + # Суффиксы лого drawable_types = ["", "-night"] for drawable_type in drawable_types: - # Application logo gradient colors + # Градиент лого приложения file_path = f"./decompiled/res/drawable{drawable_type}/$logo__0.xml" parser = etree.XMLParser(remove_blank_text=True) tree = etree.parse(file_path, parser) root = tree.getroot() - # Change attributes with namespace + # Замена атрибутов значениями из конфигурации root.set(f"{{{base['xml_ns']['android']}}}angle", str(config.logo.gradient.angle)) root.set(f"{{{base['xml_ns']['android']}}}startColor", config.logo.gradient.start_color) root.set(f"{{{base['xml_ns']['android']}}}endColor", config.logo.gradient.end_color) - # Save back + # Сохранение tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8") - # Application logo anim color + # Замена анимации лого file_path = f"./decompiled/res/drawable{drawable_type}/$logo_splash_anim__0.xml" parser = etree.XMLParser(remove_blank_text=True) tree = etree.parse(file_path, parser) root = tree.getroot() - # Finding "path" for el in root.findall("path", namespaces=base["xml_ns"]): name = el.get(f"{{{base['xml_ns']['android']}}}name") if name == "path": @@ -102,7 +100,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: elif name in ["path_1", "path_2"]: el.set(f"{{{base['xml_ns']['android']}}}fillColor", config.logo.ears_color) - # Save back + # Сохранение tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8") for filename in ["$ic_launcher_foreground__0", "$ic_banner_foreground__0"]: @@ -112,21 +110,23 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: tree = etree.parse(file_path, parser) root = tree.getroot() - # Change attributes with namespace + # Замена атрибутов значениями из конфигурации root.set(f"{{{base['xml_ns']['android']}}}angle", str(config.logo.gradient.angle)) items = root.findall("item", namespaces=base['xml_ns']) assert len(items) == 2 items[0].set(f"{{{base['xml_ns']['android']}}}color", config.logo.gradient.start_color) items[1].set(f"{{{base['xml_ns']['android']}}}color", config.logo.gradient.end_color) - # Save back + # Сохранение tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8") + # Добаление новых цветов для темы insert_after_public("carmine", "custom_color") insert_after_public("carmine_alpha_10", "custom_color_alpha_10") insert_after_color("carmine", "custom_color", main_color[0]+'ff'+main_color[1:]) insert_after_color("carmine_alpha_10", "custom_color_alpha_10", main_color[0]+'1a'+main_color[1:]) + # Замена цветов change_color("accent_alpha_10", main_color[0]+'1a'+main_color[1:]) change_color("accent_alpha_20", main_color[0]+'33'+main_color[1:]) change_color("accent_alpha_50", main_color[0]+'80'+main_color[1:]) diff --git a/patches/comment_vote.py b/patches/comment_vote.py new file mode 100644 index 0000000..1bc75a6 --- /dev/null +++ b/patches/comment_vote.py @@ -0,0 +1,99 @@ +"""Меняет местами кнопки лайка и дизлайка у коментария и иконки + +"comment_vote": { + "enabled": true, + "replace": true, + "custom_icons": true, + "icons_size": "14.0dip" +} +""" + +priority = 0 + +# imports +import os +import shutil +from tqdm import tqdm +from lxml import etree +from pydantic import Field +from typing import Dict, Any +from utils.config import PatchConfig + +#Config +class Config(PatchConfig): + replace: bool = Field(True, description="Менять местами лайк/дизлайк") + custom_icons: bool = Field(True, description="Кастомные иконки") + icon_size: str = Field("18.0dip", description="Размер иконки") + +# Patch +def apply(config, base: Dict[str, Any]) -> bool: + file_path = "./decompiled/res/layout/item_comment.xml" + parser = etree.XMLParser(remove_blank_text=True) + tree = etree.parse(file_path, parser) + root = tree.getroot() + + tqdm.write("Меняем размер иконок лайка и дизлайка...") + for icon in root.xpath( + ".//*[@android:id='@id/votePlusInactive']//ImageView | " + ".//*[@android:id='@id/votePlusActive']//ImageView | " + ".//*[@android:id='@id/voteMinusInactive']//ImageView | " + ".//*[@android:id='@id/voteMinusActive']//ImageView", + namespaces=base['xml_ns'], + ): + icon.set(f"{{{base['xml_ns']['android']}}}layout_width", config.icon_size) + # icon.set(f"{{{base['xml_ns']['android']}}}layout_height", config.icon_size) + + if config.replace: + tqdm.write("Меняем местами лайк и дизлайк комментария...") + + containers = root.xpath( + ".//LinearLayout[.//*[@android:id='@id/voteMinus'] and .//*[@android:id='@id/votePlus']]", + namespaces=base["xml_ns"], + ) + + found = False + for container in containers: + children = list(container) + vote_plus = None + vote_minus = None + + for ch in children: + cid = ch.get(f'{{{base["xml_ns"]["android"]}}}id') + if cid == "@id/votePlus": + vote_plus = ch + elif cid == "@id/voteMinus": + vote_minus = ch + + if vote_plus is not None and vote_minus is not None: + found = True + i_plus = children.index(vote_plus) + i_minus = children.index(vote_minus) + children[i_plus], children[i_minus] = children[i_minus], children[i_plus] + container[:] = children + tqdm.write("Кнопки лайк и дизлайк поменялись местами.") + break + + if not found: + tqdm.write("Не удалось найти оба узла votePlus/voteMinus даже в общих LinearLayout.") + + if config.custom_icons: + tqdm.write("Заменяем иконки лайка и дизлайка на кастомные...") + for suffix in ["up", "up_40", "down", "down_40"]: + shutil.copy( + f"./resources/ic_chevron_{suffix}.xml", + f"./decompiled/res/drawable/ic_chevron_{suffix}.xml", + ) + + for inactive in root.xpath( + ".//*[@android:id='@id/votePlusInactive'] | .//*[@android:id='@id/voteMinusInactive']", + namespaces=base["xml_ns"], + ): + for img in inactive.xpath(".//ImageView[@android:src]", namespaces=base["xml_ns"]): + src = img.get(f'{{{base["xml_ns"]["android"]}}}src', "") + if src.startswith("@drawable/") and not src.endswith("_40"): + img.set(f'{{{base["xml_ns"]["android"]}}}src', src + "_40") + + # Сохраняем + tree.write(file_path, encoding="utf-8", xml_declaration=True, pretty_print=True) + + return True diff --git a/patches/compress.py b/patches/compress.py index a534f6e..ae7d1b2 100644 --- a/patches/compress.py +++ b/patches/compress.py @@ -1,19 +1,18 @@ -""" - Удаляет ненужное и сжимает ресурсы что-бы уменьшить размер АПК +"""Удаляет ненужное и сжимает ресурсы что-бы уменьшить размер АПК Эффективность на проверена на версии 9.0 Beta 7 разница = оригинальный размер апк - патченный размер апк, не учитывая другие патчи -| Настройка | Размер файла | Разница | % | -| :----------- | :-------------------: | :-----------------: | :-: | -| None | 17092 bytes - 17.1 MB | - | - | -| Compress PNG | 17072 bytes - 17.1 MB | 20 bytes - 0.0 MB | 0.11% | -| Remove files | 17020 bytes - 17.0 MB | 72 bytes - 0.1 MB | 0.42% | -| Remove draws | 16940 bytes - 16.9 MB | 152 bytes - 0.2 MB | 0.89% | -| Remove lines | 16444 bytes - 16.4 MB | 648 bytes - 0.7 MB | 3.79% | -| Remove ai vo | 15812 bytes - 15.8 MB | 1280 bytes - 1.3 MB | 7.49% | -| Remove langs | 15764 bytes - 15.7 MB | 1328 bytes - 1.3 MB | 7.76% | -| Все включены | 13592 bytes - 13.6 MB | 3500 bytes - 4.8 MB | 20.5% | +| Настройка | Размер файла | Разница | % | +| :--------------: | :-------------------: | :-----------------: | :-: | +| Ничего | 17092 bytes - 17.1 MB | - | - | +| Сжатие PNG | 17072 bytes - 17.1 MB | 20 bytes - 0.0 MB | 0.11% | +| Удалить unknown | 17020 bytes - 17.0 MB | 72 bytes - 0.1 MB | 0.42% | +| Удалить draws | 16940 bytes - 16.9 MB | 152 bytes - 0.2 MB | 0.89% | +| Удалить lines | 16444 bytes - 16.4 MB | 648 bytes - 0.7 MB | 3.79% | +| Удалить ai voice | 15812 bytes - 15.8 MB | 1280 bytes - 1.3 MB | 7.49% | +| Удалить языки | 15764 bytes - 15.7 MB | 1328 bytes - 1.3 MB | 7.76% | +| Все включены | 13592 bytes - 13.6 MB | 3500 bytes - 4.8 MB | 20.5% | "compress": { "enabled": true, @@ -34,8 +33,8 @@ import os import shutil import subprocess from tqdm import tqdm -from typing import Dict, List, Any from pydantic import Field +from typing import Dict, List, Any from utils.config import PatchConfig from utils.smali_parser import get_smali_lines, save_smali_lines diff --git a/patches/disable_ad.py b/patches/disable_ad.py index 69f9a37..ffe7a90 100644 --- a/patches/disable_ad.py +++ b/patches/disable_ad.py @@ -1,5 +1,4 @@ -""" - Удаляет баннеры рекламы +"""Удаляет баннеры рекламы "disable_ad": { "enabled": true diff --git a/patches/disable_beta_banner.py b/patches/disable_beta_banner.py index 9ab00d9..08fbc97 100644 --- a/patches/disable_beta_banner.py +++ b/patches/disable_beta_banner.py @@ -1,5 +1,4 @@ -""" - Удаляет баннеры бета-версии +"""Удаляет баннеры бета-версии "disable_beta_banner": { "enabled": true diff --git a/patches/insert_new.py b/patches/insert_new.py deleted file mode 100644 index 552111f..0000000 --- a/patches/insert_new.py +++ /dev/null @@ -1,34 +0,0 @@ -""" - Вставляет новые файлы в проект - -"insert_new": { - "enabled": true -} -""" - -priority = 0 - -# imports -import os -import shutil -from typing import Dict, Any -from utils.config import PatchConfig -from utils.public import insert_after_public - - -#Config -class Config(PatchConfig): ... - -# Patch -def apply(config: Config, base: Dict[str, Any]) -> bool: - # Mod first launch window - shutil.copy("./resources/avatar.png", "./decompiled/assets/avatar.png") - shutil.copy( - "./resources/OpenSans-Regular.ttf", - "./decompiled/assets/OpenSans-Regular.ttf", - ) - shutil.copytree( - "./resources/smali_classes4/", "./decompiled/smali_classes4/" - ) - - return True diff --git a/patches/package_name.py b/patches/package_name.py index 1496995..ed160ff 100644 --- a/patches/package_name.py +++ b/patches/package_name.py @@ -1,5 +1,4 @@ -""" - Изменяет имя пакета в apk, удаляет вход по google и vk +"""Изменяет имя пакета в apk, удаляет вход по google и vk "package_name": { "enabled": true, @@ -34,7 +33,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: file_path = os.path.join(root, filename) if os.path.isfile(file_path): - try: + try: # Изменяем имя пакета в файлах with open(file_path, "r", encoding="utf-8") as file: file_contents = file.read() @@ -44,12 +43,16 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: new_contents = new_contents.replace( "com/swiftsoft/anixartd", config.package_name.replace(".", "/"), + ).replace( + "com/swiftsoft", + "/".join(config.package_name.split(".")[:2]), ) with open(file_path, "w", encoding="utf-8") as file: file.write(new_contents) except: pass + # Изменяем названия папок if os.path.exists("./decompiled/smali/com/swiftsoft/anixartd"): rename_dir( "./decompiled/smali/com/swiftsoft/anixartd", @@ -72,7 +75,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: os.path.join( "./decompiled", "smali_classes4", - "/".join(config.package_name.split(".")[:-1]), + "/".join(config.package_name.split(".")[:2]), ), ) @@ -85,6 +88,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: # ), # ) + # Замена названия пакета для smali_classes4 for root, dirs, files in os.walk("./decompiled/smali_classes4/"): for filename in files: file_path = os.path.join(root, filename) @@ -103,6 +107,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: except: pass + # Скрытие входа по Google и VK (НЕ РАБОТАЮТ В МОДАХ) file_path = "./decompiled/res/layout/fragment_sign_in.xml" parser = etree.XMLParser(remove_blank_text=True) tree = etree.parse(file_path, parser) diff --git a/patches/replace_navbar.py b/patches/replace_navbar.py index 8d3f1be..e273458 100644 --- a/patches/replace_navbar.py +++ b/patches/replace_navbar.py @@ -29,6 +29,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: tree = etree.parse(file_path, parser) root = tree.getroot() + # Получение элементов панели навигации items = root.findall("item", namespaces=base['xml_ns']) def get_id_suffix(item): @@ -38,11 +39,13 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: items_by_id = {get_id_suffix(item): item for item in items} existing_order = [get_id_suffix(item) for item in items] + # Размещение в новом порядке ordered_items = [] for key in config.items: if key in items_by_id: ordered_items.append(items_by_id[key]) + # Если есть не указанные в конфиге они помещаются в конец списка extra = [i for i in items if get_id_suffix(i) not in config.items] if extra: tqdm.write("⚠Найдены лишние элементы: " + str([get_id_suffix(i) for i in extra])) diff --git a/patches/selectable_text.py b/patches/selectable_text.py index 029093e..3ff4511 100644 --- a/patches/selectable_text.py +++ b/patches/selectable_text.py @@ -1,5 +1,4 @@ -""" - Делает текст в описании аниме копируемым +"""Делает текст в описании аниме копируемым "selectable_text": { "enabled": true @@ -37,7 +36,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: if f"{{{base['xml_ns']['android']}}}textIsSelectable" not in element.attrib: element.set(f"{{{base['xml_ns']['android']}}}textIsSelectable", "true") - # Сохраняем обратно + # Сохраняем tree.write(file_path, encoding="utf-8", xml_declaration=True, pretty_print=True) return True diff --git a/patches/settings_urls.py b/patches/settings_urls.py index 5f6dfd4..0ca43a0 100644 --- a/patches/settings_urls.py +++ b/patches/settings_urls.py @@ -1,5 +1,4 @@ -""" - Добавляет в настройки ссылки и добвляет текст к версии приложения +"""Добавляет в настройки ссылки и добвляет текст к версии приложения "settings_urls": { "enabled": true, @@ -61,7 +60,7 @@ DEFAULT_MENU = { "Прочее": [ { "title": "Помочь проекту", - "description": "Вы можете помочь нам в разработке мода, написании кода или тестировании.", + "description": "Вы можете помочь нам с идеями, написанием кода или тестированием.", "url": "https://git.wowlikon.tech/anixart-mod", "icon": "@drawable/ic_custom_crown", "icon_space_reserved": "false" @@ -94,6 +93,7 @@ def make_category(ns, name, items): return cat def apply(config: Config, base: Dict[str, Any]) -> bool: + # Добавление кастомных иконок shutil.copy( "./resources/ic_custom_crown.xml", "./decompiled/res/drawable/ic_custom_crown.xml", @@ -111,16 +111,16 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: tree = etree.parse(file_path, parser) root = tree.getroot() - # Insert new PreferenceCategory before the last element - last = root[-1] # last element - pos = root.index(last) + # Вставка новых пунктов перед последним + pos = root.index(root[-1]) for section, items in config.menu.items(): root.insert(pos, make_category(base["xml_ns"], section, items)) pos += 1 - # Save back + # Сохранение tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8") + # Добавление суффикса версии filepaths = [ "./decompiled/smali_classes2/com/swiftsoft/anixartd/ui/activity/UpdateActivity.smali", "./decompiled/smali_classes2/com/swiftsoft/anixartd/ui/fragment/main/preference/MainPreferenceFragment.smali", diff --git a/patches/share_links.py b/patches/share_links.py index bfc4620..5dbb1d3 100644 --- a/patches/share_links.py +++ b/patches/share_links.py @@ -1,5 +1,4 @@ -""" - Изменяет формат "поделиться" +"""Изменяет формат "поделиться" "selectable_text": { "enabled": true, @@ -31,7 +30,7 @@ DEFAULT_FORMATS = { } class Config(PatchConfig): - format: Dict[str, str] = Field(DEFAULT_FORMATS) + format: Dict[str, str] = Field(DEFAULT_FORMATS, description="Строки для замены в `strings.xml`") # Patch def apply(config: Config, base: Dict[str, Any]) -> bool: @@ -42,6 +41,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool: tree = etree.parse(file_path, parser) root = tree.getroot() + # Обновляем значения for string in root.findall("string"): name = string.get("name") if name in config.format: diff --git a/patches/todo_custom_speed.py b/patches/todo_custom_speed.py index 84859b8..d7f6f59 100644 --- a/patches/todo_custom_speed.py +++ b/patches/todo_custom_speed.py @@ -1,5 +1,4 @@ -""" - Добавляет пользовательские скорости воспроизведения видео +"""Добавляет пользовательские скорости воспроизведения видео "custom_speed": { "enabled": true, diff --git a/patches/todo_example.py b/patches/todo_example.py index d7922f6..ece6039 100644 --- a/patches/todo_example.py +++ b/patches/todo_example.py @@ -1,5 +1,4 @@ -""" - Шаблон патча +"""Шаблон патча Здесь вы можете добавить описание патча, его назначение и другие детали. diff --git a/patches/welcome.py b/patches/welcome.py new file mode 100644 index 0000000..508f9d4 --- /dev/null +++ b/patches/welcome.py @@ -0,0 +1,56 @@ +"""Добавляет всплывающее окно при первом входе + +"welcome": { + "enabled": true, + "title": "Anixarty", + "description": "Описание", + "link_text": "МЫ В TELEGRAM", + "link_url": "https://t.me/http_teapod", + "skip_text": "Пропустить", + "title_bg_color": "#FFFFFF" +} +""" + +priority = 0 + +# imports +import os +import shutil +from pydantic import Field +from typing import Dict, Any +from utils.config import PatchConfig +from utils.smali_parser import ( + find_and_replace_smali_line, + get_smali_lines, + save_smali_lines +) + +#Config +class Config(PatchConfig): + title: str = Field("Anixarty", description="Заголовок") + description: str = Field("Описание", description="Описание") + link_text: str = Field("МЫ В TELEGRAM", description="Текст ссылки") + link_url: str = Field("https://t.me/http_teapod", description="Ссылка") + skip_text: str = Field("Пропустить", description="Текст кнопки пропуска") + title_bg_color: str = Field("#FFFFFF", description="Цвет фона заголовка") + + +# Patch +def apply(config: Config, base: Dict[str, Any]) -> bool: + # Добавление ресурсов окна первого входа + shutil.copy("./resources/avatar.png", "./decompiled/assets/avatar.png") + shutil.copy( + "./resources/OpenSans-Regular.ttf", + "./decompiled/assets/OpenSans-Regular.ttf", + ) + shutil.copytree( + "./resources/smali_classes4/", "./decompiled/smali_classes4/" + ) + + file_path = "./decompiled/smali_classes2/com/swiftsoft/anixartd/ui/activity/MainActivity.smali" + method = "invoke-super {p0}, Lmoxy/MvpAppCompatActivity;->onResume()V" + lines = get_smali_lines(file_path) + lines = find_and_replace_smali_line(lines, method, method+"\ninvoke-static {p0}, Lcom/swiftsoft/about/$2;->oooooo(Landroid/content/Context;)Ljava/lang/Object;") + save_smali_lines(file_path, lines) + + return True diff --git a/resources/ic_chevron_down.xml b/resources/ic_chevron_down.xml new file mode 100644 index 0000000..6da7775 --- /dev/null +++ b/resources/ic_chevron_down.xml @@ -0,0 +1,11 @@ + + + + diff --git a/resources/ic_chevron_down_40.xml b/resources/ic_chevron_down_40.xml new file mode 100644 index 0000000..087a2f1 --- /dev/null +++ b/resources/ic_chevron_down_40.xml @@ -0,0 +1,12 @@ + + + + diff --git a/resources/ic_chevron_up.xml b/resources/ic_chevron_up.xml new file mode 100644 index 0000000..066f1e6 --- /dev/null +++ b/resources/ic_chevron_up.xml @@ -0,0 +1,11 @@ + + + + diff --git a/resources/ic_chevron_up_40.xml b/resources/ic_chevron_up_40.xml new file mode 100644 index 0000000..e7ad29b --- /dev/null +++ b/resources/ic_chevron_up_40.xml @@ -0,0 +1,12 @@ + + + + diff --git a/utils/hex.py b/utils/hex.py deleted file mode 100644 index 03f1f0a..0000000 --- a/utils/hex.py +++ /dev/null @@ -1,6 +0,0 @@ -import struct - - -def float_to_hex(f): - b = struct.pack(">f", f) - return b.hex() diff --git a/utils/public.py b/utils/public.py index c517384..1d663c2 100644 --- a/utils/public.py +++ b/utils/public.py @@ -1,8 +1,9 @@ -from lxml import etree +from typing_extensions import Optional from copy import deepcopy +from lxml import etree -def insert_after_public(anchor_name: str, elem_name: str): +def insert_after_public(anchor_name: str, elem_name: str) -> Optional[int]: file_path = "./decompiled/res/values/public.xml" parser = etree.XMLParser(remove_blank_text=True) tree = etree.parse(file_path, parser) @@ -19,6 +20,8 @@ def insert_after_public(anchor_name: str, elem_name: str): anchor = (elem, attrs) types[attrs["type"]] = types.get(attrs["type"], []) + [int(attrs["id"], 16)] + assert anchor != None + free_ids = set() group = types[anchor[1]["type"]] for i in range(min(group), max(group) + 1): @@ -47,7 +50,7 @@ def insert_after_public(anchor_name: str, elem_name: str): return new_id -def insert_after_id(anchor_name: str, elem_name: str): +def insert_after_id(anchor_name: str, elem_name: str) -> None: file_path = "./decompiled/res/values/ids.xml" parser = etree.XMLParser(remove_blank_text=True) tree = etree.parse(file_path, parser) @@ -62,13 +65,15 @@ def insert_after_id(anchor_name: str, elem_name: str): assert anchor == None anchor = (elem, attrs) + assert anchor != None + new_elem = deepcopy(anchor[0]) new_elem.set("name", elem_name) anchor[0].addnext(new_elem) tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8") -def change_color(name: str, value: str): +def change_color(name: str, value: str) -> None: file_path = "./decompiled/res/values/colors.xml" parser = etree.XMLParser(remove_blank_text=True) tree = etree.parse(file_path, parser) @@ -86,7 +91,8 @@ def change_color(name: str, value: str): assert replacements >= 1 tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8") -def insert_after_color(anchor_name: str, elem_name: str, elem_value: str): + +def insert_after_color(anchor_name: str, elem_name: str, elem_value: str) -> None: file_path = "./decompiled/res/values/colors.xml" parser = etree.XMLParser(remove_blank_text=True) tree = etree.parse(file_path, parser) @@ -101,6 +107,8 @@ def insert_after_color(anchor_name: str, elem_name: str, elem_value: str): assert anchor == None anchor = (elem, attrs) + assert anchor != None + new_elem = deepcopy(anchor[0]) new_elem.set("name", elem_name) anchor[0].addnext(new_elem) diff --git a/utils/smali_parser.py b/utils/smali_parser.py index 7cc6a12..8778d69 100644 --- a/utils/smali_parser.py +++ b/utils/smali_parser.py @@ -67,3 +67,11 @@ def find_and_replace_smali_line( def float_to_hex(f): b = struct.pack(">f", f) return b.hex() + + +def quick_replace(file: str) -> None: + content = "" + with open(file, "r", encoding="utf-8") as smali: + content = smali.read() + with open(file, "w", encoding="utf-8") as f: + f.writelines(content) diff --git a/utils/tools.py b/utils/tools.py index 9731e23..454751f 100644 --- a/utils/tools.py +++ b/utils/tools.py @@ -19,6 +19,7 @@ def ensure_dirs(): for d in [TOOLS, ORIGINAL, MODIFIED, DECOMPILED, PATCHES, CONFIGS]: d.mkdir(exist_ok=True) + def run(console: Console, cmd: List[str], hide_output=True): prog = local[cmd[0]][cmd[1:]] try: @@ -28,6 +29,7 @@ def run(console: Console, cmd: List[str], hide_output=True): console.print(e.stderr) raise typer.Exit(1) + def download(console: Console, url: str, dest: Path): console.print(f"[cyan]Скачивание {url} → {dest.name}")