Обновление вывода информации о патчах, добавление патча лайков/дизлайков

This commit is contained in:
2025-10-11 18:40:44 +03:00
parent b646dbf6fe
commit 28c60aa7a3
27 changed files with 313 additions and 114 deletions
+1
View File
@@ -0,0 +1 @@
{"enabled":true,"replace":true,"custom_icons":true,"icon_size":"18.0dip"}
-1
View File
@@ -1 +0,0 @@
{"enabled":true}
+1
View File
@@ -0,0 +1 @@
{"enabled":true,"title":"Anixarty","description":"Описание","link_text":"МЫ В TELEGRAM","link_url":"https://t.me/http_teapod","skip_text":"Пропустить","title_bg_color":"#FFFFFF"}
+29 -11
View File
@@ -13,7 +13,6 @@ from rich.prompt import Prompt
from utils.config import * from utils.config import *
from utils.tools import * from utils.tools import *
# --- Paths ---
console = Console() console = Console()
app = typer.Typer() app = typer.Typer()
@@ -30,6 +29,7 @@ class Patch:
self.config = module.Config.model_validate_json((CONFIGS / f"{name}.json").read_text()) self.config = module.Config.model_validate_json((CONFIGS / f"{name}.json").read_text())
except Exception as e: except Exception as e:
console.print(f"[red]Ошибка при загрузке конфигурации патча {name}: {e}") console.print(f"[red]Ошибка при загрузке конфигурации патча {name}: {e}")
console.print(f"[yellow]Используются значения по умолчанию")
self.config = module.Config() self.config = module.Config()
def apply(self, conf: Dict[str, Any]) -> bool: def apply(self, conf: Dict[str, Any]) -> bool:
@@ -42,7 +42,7 @@ class Patch:
return False return False
# ======================= INIT ========================= # ========================= INIT =========================
@app.command() @app.command()
def init(): def init():
"""Создание директорий и скачивание инструментов""" """Создание директорий и скачивание инструментов"""
@@ -71,7 +71,7 @@ def init():
raise typer.Exit(1) raise typer.Exit(1)
# ======================= INFO ========================= # ========================= INFO =========================
@app.command() @app.command()
def info(patch_name: str = ""): def info(patch_name: str = ""):
"""Вывод информации о патче""" """Вывод информации о патче"""
@@ -81,18 +81,36 @@ def info(patch_name: str = ""):
console.print(f"[green]Информация о патче {patch.name}:") console.print(f"[green]Информация о патче {patch.name}:")
console.print(f" [yellow]Приоритет: {patch.priority}") console.print(f" [yellow]Приоритет: {patch.priority}")
console.print(f" [yellow]Описание: {patch.module.__doc__}") 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: else:
conf = load_config(console)
console.print("[cyan]Список патчей:") console.print("[cyan]Список патчей:")
patch_list = []
for f in PATCHES.glob("*.py"): 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 continue
name = f.stem patch = Patch(f.stem, __import__(f"patches.{f.stem}", fromlist=[""]))
if conf["patches"].get(name, {}).get("enabled", True): if patch.config.enabled: patch_list.append((patch.priority, f" [{patch.priority}] [yellow]{f.stem}: [green]✔ включен"))
console.print(f" [yellow]{name}: [green]✔ enabled") else: patch_list.append((patch.priority, f" [{patch.priority}] [yellow]{f.stem}: [red]✘ выключен"))
else: for _, patch in sorted(patch_list, key=lambda x: (x[0] is None, x[0]), reverse=True): console.print(patch)
console.print(f" [yellow]{name}: [red]✘ disabled")
# ========================= UTIL =========================
def select_apk() -> Path: def select_apk() -> Path:
apks = [f for f in ORIGINAL.glob("*.apk")] apks = [f for f in ORIGINAL.glob("*.apk")]
if not apks: if not apks:
@@ -174,6 +192,7 @@ def compile(apk: Path, patches: List[Patch]):
f.write(f"{'' if p.applied else ''} {p.name}\n") f.write(f"{'' if p.applied else ''} {p.name}\n")
# ========================= BUILD =========================
@app.command() @app.command()
def build( def build(
force: bool = typer.Option(False, "--force", "-f", help="Принудительная сборка"), force: bool = typer.Option(False, "--force", "-f", help="Принудительная сборка"),
@@ -219,5 +238,4 @@ def build(
raise typer.Exit(1) raise typer.Exit(1)
if __name__ == "__main__": if __name__ == "__main__": app()
app()
+6 -7
View File
@@ -1,5 +1,4 @@
""" """Заменяет сервер api
Заменяет сервер api
"change_server": { "change_server": {
"enabled": true, "enabled": true,
@@ -24,11 +23,11 @@ class Config(PatchConfig):
# Patch # Patch
def apply(config: Config, base: Dict[str, Any]) -> bool: 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}" assert response.status_code == 200, f"Failed to fetch data {response.status_code} {response.text}"
new_api = json.loads(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']}") tqdm.write(f"Изменение {item['file']}")
filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/network/api/'+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']}") tqdm.write(f"Не найдено {item['src']}")
f.write(content.replace(item['src'], item['dst'])) 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' filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/utils/anixnet/GithubPagesNetFetcher.smali'
with open(filepath, 'r') as f: 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: 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"]}"')) 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' filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/DaggerApp_HiltComponents_SingletonC$SingletonCImpl$SwitchingProvider.smali'
content = ""
with open(filepath, 'r') as f: with open(filepath, 'r') as f:
for line in f.readlines(): for line in f.readlines():
if "addInterceptor" in line: continue if "addInterceptor" in line: continue
+12 -12
View File
@@ -1,5 +1,4 @@
""" """Изменяет цветовую тему приложения и иконку
Изменяет цветовую тему приложения и иконку
"color_theme": { "color_theme": {
"enabled": true, "enabled": true,
@@ -59,7 +58,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
main_color = config.colors.primary main_color = config.colors.primary
splash_color = config.colors.secondary splash_color = config.colors.secondary
# No connection alert coolor # Обновление сообщения об отсутствии подключения
with open("./decompiled/assets/no_connection.html", "r", encoding="utf-8") as file: with open("./decompiled/assets/no_connection.html", "r", encoding="utf-8") as file:
file_contents = file.read() 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: with open("./decompiled/assets/no_connection.html", "w", encoding="utf-8") as file:
file.write(new_contents) file.write(new_contents)
# For logo # Суффиксы лого
drawable_types = ["", "-night"] drawable_types = ["", "-night"]
for drawable_type in drawable_types: for drawable_type in drawable_types:
# Application logo gradient colors # Градиент лого приложения
file_path = f"./decompiled/res/drawable{drawable_type}/$logo__0.xml" file_path = f"./decompiled/res/drawable{drawable_type}/$logo__0.xml"
parser = etree.XMLParser(remove_blank_text=True) parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser) tree = etree.parse(file_path, parser)
root = tree.getroot() 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']}}}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']}}}startColor", config.logo.gradient.start_color)
root.set(f"{{{base['xml_ns']['android']}}}endColor", config.logo.gradient.end_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") 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" file_path = f"./decompiled/res/drawable{drawable_type}/$logo_splash_anim__0.xml"
parser = etree.XMLParser(remove_blank_text=True) parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser) tree = etree.parse(file_path, parser)
root = tree.getroot() root = tree.getroot()
# Finding "path"
for el in root.findall("path", namespaces=base["xml_ns"]): for el in root.findall("path", namespaces=base["xml_ns"]):
name = el.get(f"{{{base['xml_ns']['android']}}}name") name = el.get(f"{{{base['xml_ns']['android']}}}name")
if name == "path": if name == "path":
@@ -102,7 +100,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
elif name in ["path_1", "path_2"]: elif name in ["path_1", "path_2"]:
el.set(f"{{{base['xml_ns']['android']}}}fillColor", config.logo.ears_color) 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") tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8")
for filename in ["$ic_launcher_foreground__0", "$ic_banner_foreground__0"]: 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) tree = etree.parse(file_path, parser)
root = tree.getroot() 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']}}}angle", str(config.logo.gradient.angle))
items = root.findall("item", namespaces=base['xml_ns']) items = root.findall("item", namespaces=base['xml_ns'])
assert len(items) == 2 assert len(items) == 2
items[0].set(f"{{{base['xml_ns']['android']}}}color", config.logo.gradient.start_color) 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) 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") tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8")
# Добаление новых цветов для темы
insert_after_public("carmine", "custom_color") insert_after_public("carmine", "custom_color")
insert_after_public("carmine_alpha_10", "custom_color_alpha_10") 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", "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:]) 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_10", main_color[0]+'1a'+main_color[1:])
change_color("accent_alpha_20", main_color[0]+'33'+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:]) change_color("accent_alpha_50", main_color[0]+'80'+main_color[1:])
+99
View File
@@ -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
+10 -11
View File
@@ -1,18 +1,17 @@
""" """Удаляет ненужное и сжимает ресурсы что-бы уменьшить размер АПК
Удаляет ненужное и сжимает ресурсы что-бы уменьшить размер АПК
Эффективность на проверена на версии 9.0 Beta 7 Эффективность на проверена на версии 9.0 Beta 7
разница = оригинальный размер апк - патченный размер апк, не учитывая другие патчи разница = оригинальный размер апк - патченный размер апк, не учитывая другие патчи
| Настройка | Размер файла | Разница | % | | Настройка | Размер файла | Разница | % |
| :----------- | :-------------------: | :-----------------: | :-: | | :--------------: | :-------------------: | :-----------------: | :-: |
| None | 17092 bytes - 17.1 MB | - | - | | Ничего | 17092 bytes - 17.1 MB | - | - |
| Compress PNG | 17072 bytes - 17.1 MB | 20 bytes - 0.0 MB | 0.11% | | Сжатие 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% | | Удалить unknown | 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% | | Удалить 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% | | Удалить 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% | | Удалить ai voice | 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% | | Удалить языки | 15764 bytes - 15.7 MB | 1328 bytes - 1.3 MB | 7.76% |
| Все включены | 13592 bytes - 13.6 MB | 3500 bytes - 4.8 MB | 20.5% | | Все включены | 13592 bytes - 13.6 MB | 3500 bytes - 4.8 MB | 20.5% |
"compress": { "compress": {
@@ -34,8 +33,8 @@ import os
import shutil import shutil
import subprocess import subprocess
from tqdm import tqdm from tqdm import tqdm
from typing import Dict, List, Any
from pydantic import Field from pydantic import Field
from typing import Dict, List, Any
from utils.config import PatchConfig from utils.config import PatchConfig
from utils.smali_parser import get_smali_lines, save_smali_lines from utils.smali_parser import get_smali_lines, save_smali_lines
+1 -2
View File
@@ -1,5 +1,4 @@
""" """Удаляет баннеры рекламы
Удаляет баннеры рекламы
"disable_ad": { "disable_ad": {
"enabled": true "enabled": true
+1 -2
View File
@@ -1,5 +1,4 @@
""" """Удаляет баннеры бета-версии
Удаляет баннеры бета-версии
"disable_beta_banner": { "disable_beta_banner": {
"enabled": true "enabled": true
-34
View File
@@ -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
+9 -4
View File
@@ -1,5 +1,4 @@
""" """Изменяет имя пакета в apk, удаляет вход по google и vk
Изменяет имя пакета в apk, удаляет вход по google и vk
"package_name": { "package_name": {
"enabled": true, "enabled": true,
@@ -34,7 +33,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
file_path = os.path.join(root, filename) file_path = os.path.join(root, filename)
if os.path.isfile(file_path): if os.path.isfile(file_path):
try: try: # Изменяем имя пакета в файлах
with open(file_path, "r", encoding="utf-8") as file: with open(file_path, "r", encoding="utf-8") as file:
file_contents = file.read() file_contents = file.read()
@@ -44,12 +43,16 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
new_contents = new_contents.replace( new_contents = new_contents.replace(
"com/swiftsoft/anixartd", "com/swiftsoft/anixartd",
config.package_name.replace(".", "/"), config.package_name.replace(".", "/"),
).replace(
"com/swiftsoft",
"/".join(config.package_name.split(".")[:2]),
) )
with open(file_path, "w", encoding="utf-8") as file: with open(file_path, "w", encoding="utf-8") as file:
file.write(new_contents) file.write(new_contents)
except: except:
pass pass
# Изменяем названия папок
if os.path.exists("./decompiled/smali/com/swiftsoft/anixartd"): if os.path.exists("./decompiled/smali/com/swiftsoft/anixartd"):
rename_dir( rename_dir(
"./decompiled/smali/com/swiftsoft/anixartd", "./decompiled/smali/com/swiftsoft/anixartd",
@@ -72,7 +75,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
os.path.join( os.path.join(
"./decompiled", "./decompiled",
"smali_classes4", "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 root, dirs, files in os.walk("./decompiled/smali_classes4/"):
for filename in files: for filename in files:
file_path = os.path.join(root, filename) file_path = os.path.join(root, filename)
@@ -103,6 +107,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
except: except:
pass pass
# Скрытие входа по Google и VK (НЕ РАБОТАЮТ В МОДАХ)
file_path = "./decompiled/res/layout/fragment_sign_in.xml" file_path = "./decompiled/res/layout/fragment_sign_in.xml"
parser = etree.XMLParser(remove_blank_text=True) parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser) tree = etree.parse(file_path, parser)
+3
View File
@@ -29,6 +29,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
tree = etree.parse(file_path, parser) tree = etree.parse(file_path, parser)
root = tree.getroot() root = tree.getroot()
# Получение элементов панели навигации
items = root.findall("item", namespaces=base['xml_ns']) items = root.findall("item", namespaces=base['xml_ns'])
def get_id_suffix(item): 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} items_by_id = {get_id_suffix(item): item for item in items}
existing_order = [get_id_suffix(item) for item in items] existing_order = [get_id_suffix(item) for item in items]
# Размещение в новом порядке
ordered_items = [] ordered_items = []
for key in config.items: for key in config.items:
if key in items_by_id: if key in items_by_id:
ordered_items.append(items_by_id[key]) ordered_items.append(items_by_id[key])
# Если есть не указанные в конфиге они помещаются в конец списка
extra = [i for i in items if get_id_suffix(i) not in config.items] extra = [i for i in items if get_id_suffix(i) not in config.items]
if extra: if extra:
tqdm.write("⚠Найдены лишние элементы: " + str([get_id_suffix(i) for i in extra])) tqdm.write("⚠Найдены лишние элементы: " + str([get_id_suffix(i) for i in extra]))
+2 -3
View File
@@ -1,5 +1,4 @@
""" """Делает текст в описании аниме копируемым
Делает текст в описании аниме копируемым
"selectable_text": { "selectable_text": {
"enabled": true "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: if f"{{{base['xml_ns']['android']}}}textIsSelectable" not in element.attrib:
element.set(f"{{{base['xml_ns']['android']}}}textIsSelectable", "true") element.set(f"{{{base['xml_ns']['android']}}}textIsSelectable", "true")
# Сохраняем обратно # Сохраняем
tree.write(file_path, encoding="utf-8", xml_declaration=True, pretty_print=True) tree.write(file_path, encoding="utf-8", xml_declaration=True, pretty_print=True)
return True return True
+7 -7
View File
@@ -1,5 +1,4 @@
""" """Добавляет в настройки ссылки и добвляет текст к версии приложения
Добавляет в настройки ссылки и добвляет текст к версии приложения
"settings_urls": { "settings_urls": {
"enabled": true, "enabled": true,
@@ -61,7 +60,7 @@ DEFAULT_MENU = {
"Прочее": [ "Прочее": [
{ {
"title": "Помочь проекту", "title": "Помочь проекту",
"description": "Вы можете помочь нам в разработке мода, написании кода или тестировании.", "description": "Вы можете помочь нам с идеями, написанием кода или тестированием.",
"url": "https://git.wowlikon.tech/anixart-mod", "url": "https://git.wowlikon.tech/anixart-mod",
"icon": "@drawable/ic_custom_crown", "icon": "@drawable/ic_custom_crown",
"icon_space_reserved": "false" "icon_space_reserved": "false"
@@ -94,6 +93,7 @@ def make_category(ns, name, items):
return cat return cat
def apply(config: Config, base: Dict[str, Any]) -> bool: def apply(config: Config, base: Dict[str, Any]) -> bool:
# Добавление кастомных иконок
shutil.copy( shutil.copy(
"./resources/ic_custom_crown.xml", "./resources/ic_custom_crown.xml",
"./decompiled/res/drawable/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) tree = etree.parse(file_path, parser)
root = tree.getroot() root = tree.getroot()
# Insert new PreferenceCategory before the last element # Вставка новых пунктов перед последним
last = root[-1] # last element pos = root.index(root[-1])
pos = root.index(last)
for section, items in config.menu.items(): for section, items in config.menu.items():
root.insert(pos, make_category(base["xml_ns"], section, items)) root.insert(pos, make_category(base["xml_ns"], section, items))
pos += 1 pos += 1
# Save back # Сохранение
tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8") tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8")
# Добавление суффикса версии
filepaths = [ filepaths = [
"./decompiled/smali_classes2/com/swiftsoft/anixartd/ui/activity/UpdateActivity.smali", "./decompiled/smali_classes2/com/swiftsoft/anixartd/ui/activity/UpdateActivity.smali",
"./decompiled/smali_classes2/com/swiftsoft/anixartd/ui/fragment/main/preference/MainPreferenceFragment.smali", "./decompiled/smali_classes2/com/swiftsoft/anixartd/ui/fragment/main/preference/MainPreferenceFragment.smali",
+3 -3
View File
@@ -1,5 +1,4 @@
""" """Изменяет формат "поделиться"
Изменяет формат "поделиться"
"selectable_text": { "selectable_text": {
"enabled": true, "enabled": true,
@@ -31,7 +30,7 @@ DEFAULT_FORMATS = {
} }
class Config(PatchConfig): class Config(PatchConfig):
format: Dict[str, str] = Field(DEFAULT_FORMATS) format: Dict[str, str] = Field(DEFAULT_FORMATS, description="Строки для замены в `strings.xml`")
# Patch # Patch
def apply(config: Config, base: Dict[str, Any]) -> bool: 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) tree = etree.parse(file_path, parser)
root = tree.getroot() root = tree.getroot()
# Обновляем значения
for string in root.findall("string"): for string in root.findall("string"):
name = string.get("name") name = string.get("name")
if name in config.format: if name in config.format:
+1 -2
View File
@@ -1,5 +1,4 @@
""" """Добавляет пользовательские скорости воспроизведения видео
Добавляет пользовательские скорости воспроизведения видео
"custom_speed": { "custom_speed": {
"enabled": true, "enabled": true,
+1 -2
View File
@@ -1,5 +1,4 @@
""" """Шаблон патча
Шаблон патча
Здесь вы можете добавить описание патча, его назначение и другие детали. Здесь вы можете добавить описание патча, его назначение и другие детали.
+56
View File
@@ -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
+11
View File
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12.59 23.26l-0.01 0-0.07 0.03-0.02 0.01-0.01-0.01-0.07-0.03q-0.02-0.01-0.03 0l0 0.01-0.02 0.43 0.01 0.02 0.01 0.02 0.1 0.07 0.02 0 0.01 0 0.1-0.07 0.01-0.02 0.01-0.02-0.02-0.42q0-0.02-0.02-0.02m0.27-0.12l-0.01 0.01-0.19 0.09-0.01 0.01 0 0.01 0.02 0.43 0 0.01 0.01 0.01 0.2 0.09q0.02 0.01 0.03-0.01l0-0.01-0.03-0.61q-0.01-0.02-0.02-0.03m-0.72 0.01a0.02 0.02 0 0 0-0.02 0l-0.01 0.02-0.03 0.61q0 0.02 0.01 0.02l0.02 0 0.2-0.09 0.01-0.01 0-0.01 0.02-0.43 0-0.01-0.01-0.01z"/>
<path
android:fillColor="#FF000000"
android:pathData="M15 16h2.4a4 4 0 0 0 3.94-4.72l-0.91-5A4 4 0 0 0 16.5 3H8v12l1.82 5.79c0.3 0.69 1.06 1.32 2.02 1.13C13.37 21.63 15 20.43 15 18.5z m-9-1a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3z"/>
</vector>
+12
View File
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12.59 23.26l-0.01 0-0.07 0.03-0.02 0.01-0.01-0.01-0.07-0.03q-0.02-0.01-0.03 0l0 0.01-0.02 0.43 0.01 0.02 0.01 0.02 0.1 0.07 0.02 0 0.01 0 0.1-0.07 0.01-0.02 0.01-0.02-0.02-0.42q0-0.02-0.02-0.02m0.27-0.12l-0.01 0.01-0.19 0.09-0.01 0.01 0 0.01 0.02 0.43 0 0.01 0.01 0.01 0.2 0.09q0.02 0.01 0.03-0.01l0-0.01-0.03-0.61q-0.01-0.02-0.02-0.03m-0.72 0.01a0.02 0.02 0 0 0-0.02 0l-0.01 0.02-0.03 0.61q0 0.02 0.01 0.02l0.02 0 0.2-0.09 0.01-0.01 0-0.01 0.02-0.43 0-0.01-0.01-0.01z"/>
<path
android:fillType="evenOdd"
android:fillColor="#FF000000"
android:pathData="M16.5 3a4 4 0 0 1 3.93 3.28l0.91 5a4 4 0 0 1-3.94 4.72H15v2.5c0 1.93-1.63 3.12-3.15 3.42-0.96 0.18-1.73-0.44-2.03-1.13l-2.48-5.79H6a3 3 0 0 1-3-3v-6a3 3 0 0 1 3-3z m0 2H9v8.59a1 1 0 0 0 0.08 0.39l2.54 5.94c0.88-0.22 1.38-0.83 1.38-1.42v-2.5a2 2 0 0 1 2-2h2.4a2 2 0 0 0 1.97-2.36l-0.91-5a2 2 0 0 0-1.96-1.64M7 5H6a1 1 0 0 0-0.99 0.88L5 6v6a1 1 0 0 0 0.88 0.99l0.12 0.01h1z"/>
</vector>
+11
View File
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12.59 23.26l-0.01 0-0.07 0.03-0.02 0.01-0.01-0.01-0.07-0.03q-0.02-0.01-0.03 0l0 0.01-0.02 0.43 0.01 0.02 0.01 0.02 0.1 0.07 0.02 0 0.01 0 0.1-0.07 0.01-0.02 0.01-0.02-0.02-0.42q0-0.02-0.02-0.02m0.27-0.12l-0.01 0.01-0.19 0.09-0.01 0.01 0 0.01 0.02 0.43 0 0.01 0.01 0.01 0.2 0.09q0.02 0.01 0.03-0.01l0-0.01-0.03-0.61q-0.01-0.02-0.02-0.03m-0.72 0.01a0.02 0.02 0 0 0-0.02 0l-0.01 0.02-0.03 0.61q0 0.02 0.01 0.02l0.02 0 0.2-0.09 0.01-0.01 0-0.01 0.02-0.43 0-0.01-0.01-0.01z"/>
<path
android:fillColor="#FF000000"
android:pathData="M15 8h2.4a4 4 0 0 1 3.94 4.72l-0.91 5A4 4 0 0 1 16.5 21H8V9l1.82-5.79c0.3-0.69 1.06-1.32 2.02-1.13C13.37 2.38 15 3.57 15 5.5zM6 9a3 3 0 0 0-3 3v6a3 3 0 0 0 3 3z"/>
</vector>
+12
View File
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12.59 23.26l-0.01 0-0.07 0.03-0.02 0.01-0.01-0.01-0.07-0.03q-0.02-0.01-0.03 0l0 0.01-0.02 0.43 0.01 0.02 0.01 0.02 0.1 0.07 0.02 0 0.01 0 0.1-0.07 0.01-0.02 0.01-0.02-0.02-0.42q0-0.02-0.02-0.02m0.27-0.12l-0.01 0.01-0.19 0.09-0.01 0.01 0 0.01 0.02 0.43 0 0.01 0.01 0.01 0.2 0.09q0.02 0.01 0.03-0.01l0-0.01-0.03-0.61q-0.01-0.02-0.02-0.03m-0.72 0.01a0.02 0.02 0 0 0-0.02 0l-0.01 0.02-0.03 0.61q0 0.02 0.01 0.02l0.02 0 0.2-0.09 0.01-0.01 0-0.01 0.02-0.43 0-0.01-0.01-0.01z"/>
<path
android:fillType="evenOdd"
android:fillColor="#FF000000"
android:pathData="M9.82 3.21c0.3-0.69 1.06-1.32 2.02-1.13 1.47 0.28 3.04 1.4 3.15 3.22L15 5.5V8h2.4a4 4 0 0 1 3.97 4.52l-0.03 0.2-0.91 5a4 4 0 0 1-3.74 3.28l-0.19 0H6a3 3 0 0 1-3-2.82L3 18v-6a3 3 0 0 1 2.82-3L6 9h1.34zM7 11H6a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h1z m4.63-6.92l-2.55 5.94a1 1 0 0 0-0.07 0.26L9 10.41V19h7.5a2 2 0 0 0 1.93-1.49l0.03-0.15 0.91-5a2 2 0 0 0-1.82-2.35L17.41 10H15a2 2 0 0 1-2-1.85L13 8V5.5c0-0.55-0.43-1.12-1.21-1.37z"/>
</vector>
-6
View File
@@ -1,6 +0,0 @@
import struct
def float_to_hex(f):
b = struct.pack(">f", f)
return b.hex()
+13 -5
View File
@@ -1,8 +1,9 @@
from lxml import etree from typing_extensions import Optional
from copy import deepcopy 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" file_path = "./decompiled/res/values/public.xml"
parser = etree.XMLParser(remove_blank_text=True) parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser) tree = etree.parse(file_path, parser)
@@ -19,6 +20,8 @@ def insert_after_public(anchor_name: str, elem_name: str):
anchor = (elem, attrs) anchor = (elem, attrs)
types[attrs["type"]] = types.get(attrs["type"], []) + [int(attrs["id"], 16)] types[attrs["type"]] = types.get(attrs["type"], []) + [int(attrs["id"], 16)]
assert anchor != None
free_ids = set() free_ids = set()
group = types[anchor[1]["type"]] group = types[anchor[1]["type"]]
for i in range(min(group), max(group) + 1): 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 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" file_path = "./decompiled/res/values/ids.xml"
parser = etree.XMLParser(remove_blank_text=True) parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser) tree = etree.parse(file_path, parser)
@@ -62,13 +65,15 @@ def insert_after_id(anchor_name: str, elem_name: str):
assert anchor == None assert anchor == None
anchor = (elem, attrs) anchor = (elem, attrs)
assert anchor != None
new_elem = deepcopy(anchor[0]) new_elem = deepcopy(anchor[0])
new_elem.set("name", elem_name) new_elem.set("name", elem_name)
anchor[0].addnext(new_elem) anchor[0].addnext(new_elem)
tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8") 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" file_path = "./decompiled/res/values/colors.xml"
parser = etree.XMLParser(remove_blank_text=True) parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser) tree = etree.parse(file_path, parser)
@@ -86,7 +91,8 @@ def change_color(name: str, value: str):
assert replacements >= 1 assert replacements >= 1
tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8") 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" file_path = "./decompiled/res/values/colors.xml"
parser = etree.XMLParser(remove_blank_text=True) parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser) 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 assert anchor == None
anchor = (elem, attrs) anchor = (elem, attrs)
assert anchor != None
new_elem = deepcopy(anchor[0]) new_elem = deepcopy(anchor[0])
new_elem.set("name", elem_name) new_elem.set("name", elem_name)
anchor[0].addnext(new_elem) anchor[0].addnext(new_elem)
+8
View File
@@ -67,3 +67,11 @@ def find_and_replace_smali_line(
def float_to_hex(f): def float_to_hex(f):
b = struct.pack(">f", f) b = struct.pack(">f", f)
return b.hex() 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)
+2
View File
@@ -19,6 +19,7 @@ def ensure_dirs():
for d in [TOOLS, ORIGINAL, MODIFIED, DECOMPILED, PATCHES, CONFIGS]: for d in [TOOLS, ORIGINAL, MODIFIED, DECOMPILED, PATCHES, CONFIGS]:
d.mkdir(exist_ok=True) d.mkdir(exist_ok=True)
def run(console: Console, cmd: List[str], hide_output=True): def run(console: Console, cmd: List[str], hide_output=True):
prog = local[cmd[0]][cmd[1:]] prog = local[cmd[0]][cmd[1:]]
try: try:
@@ -28,6 +29,7 @@ def run(console: Console, cmd: List[str], hide_output=True):
console.print(e.stderr) console.print(e.stderr)
raise typer.Exit(1) raise typer.Exit(1)
def download(console: Console, url: str, dest: Path): def download(console: Console, url: str, dest: Path):
console.print(f"[cyan]Скачивание {url}{dest.name}") console.print(f"[cyan]Скачивание {url}{dest.name}")