Files
leonapp/leonapp-cli/leonapp_cli.py
Leonmmcoset e553768c1d feat: 添加应用详情窗口和更新检查功能
- 实现全新的应用详情窗口,包含统计信息、基本信息和描述展示
- 添加应用更新检查功能到CLI工具
- 优化版本列表页面的文件路径处理逻辑
- 升级GUI版本至Beta 0.4
- 增强公告详情页面的链接处理能力
2025-09-24 21:58:25 +08:00

487 lines
21 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
LeonApp CLI - 一个用于访问App Store API的命令行工具
"""
import os
import sys
import argparse
import json
import platform
from datetime import datetime
import requests
from colorama import init, Fore, Style
# 当前版本
APP_VERSION = "Beta 0.3"
# 初始化colorama
def init_colorama():
"""初始化colorama确保在Windows和其他平台上都能正确显示彩色文本"""
init(autoreset=True)
class LeonAppCLI:
def __init__(self):
# API基础URL
self.api_base_url = "http://leonmmcoset.jjxmm.win:8010/api.php"
# 终端宽度,用于格式化输出
self.terminal_width = self.get_terminal_width()
# 初始化colorama
init_colorama()
def get_terminal_width(self):
"""获取终端宽度"""
try:
return os.get_terminal_size().columns
except OSError:
# 如果无法获取终端宽度,返回默认值
return 80
def print_separator(self):
"""打印分隔线"""
print(Fore.YELLOW + "=" * self.terminal_width)
def print_header(self, text):
"""打印带样式的标题"""
self.print_separator()
print(Fore.GREEN + Style.BRIGHT + text.center(self.terminal_width))
self.print_separator()
def print_error(self, message):
"""打印错误消息"""
print(Fore.RED + f"错误: {message}")
def print_success(self, message):
"""打印成功消息"""
print(Fore.GREEN + f"成功: {message}")
def print_info(self, message):
"""打印信息消息"""
print(Fore.BLUE + message)
def make_api_request(self, endpoint_type, params=None):
"""
发送API请求
:param endpoint_type: API端点类型
:param params: 请求参数
:return: 响应数据(字典)
"""
if params is None:
params = {}
# 添加API类型参数
params['t'] = endpoint_type
try:
self.print_info(f"正在请求API: {self.api_base_url}?t={endpoint_type}")
response = requests.get(self.api_base_url, params=params, timeout=30)
# 检查响应状态
if response.status_code != 200:
self.print_error(f"API请求失败状态码: {response.status_code}")
return None
# 解析JSON响应
data = response.json()
# 检查响应状态
if data.get('status') == 'error':
self.print_error(data.get('message', '未知错误'))
return None
return data.get('data')
except requests.exceptions.RequestException as e:
self.print_error(f"网络请求异常: {str(e)}")
return None
except json.JSONDecodeError:
self.print_error("无法解析API响应")
return None
def list_all_apps(self, page=1, limit=20):
"""获取所有APP列表"""
data = self.make_api_request('getallapps', {'page': page, 'limit': limit})
if data:
self.print_header(f"应用列表 (第 {page} 页,共 {data['pagination']['totalPages']} 页)")
if not data['apps']:
self.print_info("暂无应用数据")
return
# 打印应用列表
for app in data['apps']:
print(Fore.CYAN + f"ID: {app['id']}")
print(Fore.YELLOW + f"名称: {app['name']}")
print(Fore.WHITE + f"描述: {app['description'][:100]}{'...' if len(app['description']) > 100 else ''}")
print(Fore.WHITE + f"评分: {app.get('avg_rating', '暂无')}")
print(Fore.WHITE + f"下载量: {app.get('total_downloads', 0)}")
print("-")
# 打印分页信息
self.print_info(f"总数: {data['pagination']['total']} 个应用")
def get_app_info(self, app_id):
"""获取APP详细信息"""
data = self.make_api_request('getappinfo', {'id': app_id})
if data:
self.print_header(f"应用详情 - {data['name']}")
# 打印应用基本信息
print(Fore.CYAN + f"ID: {data['id']}")
print(Fore.YELLOW + f"名称: {data['name']}")
print(Fore.WHITE + f"版本: {data['version']}")
print(Fore.WHITE + f"年龄分级: {data['age_rating']}")
print(Fore.WHITE + f"描述: {data['description']}")
print(Fore.WHITE + f"评分: {data.get('avg_rating', '暂无')}")
print(Fore.WHITE + f"下载量: {data.get('total_downloads', 0)}")
# 打印标签信息
if 'tags' in data and data['tags']:
tags = ', '.join([tag['name'] for tag in data['tags']])
print(Fore.WHITE + f"标签: {tags}")
# 打印版本信息
if 'versions' in data and data['versions']:
print(Fore.MAGENTA + "\n版本历史:")
for version in data['versions'][:3]: # 只显示前3个版本
print(Fore.WHITE + f"- 版本 {version['version']} ({version['download_count']} 下载)")
# 打印图片数量
if 'images' in data and data['images']:
print(Fore.MAGENTA + f"\n图片数量: {len(data['images'])}")
def list_all_tags(self):
"""获取所有标签"""
data = self.make_api_request('getalltags')
if data:
self.print_header("所有标签")
# 按字母顺序排序标签
data.sort(key=lambda x: x['name'])
# 打印标签列表
for i, tag in enumerate(data, 1):
print(Fore.CYAN + f"{i}. {tag['name']} (ID: {tag['id']})")
self.print_info(f"总共有 {len(data)} 个标签")
def list_tag_apps(self, tag_id, page=1, limit=20):
"""获取某标签下的APP列表"""
data = self.make_api_request('gettagapp', {'id': tag_id, 'page': page, 'limit': limit})
if data:
self.print_header(f"标签 '{data['tag']['name']}' 下的应用列表")
if not data['apps']:
self.print_info("该标签下暂无应用")
return
# 打印应用列表
for app in data['apps']:
print(Fore.CYAN + f"ID: {app['id']}")
print(Fore.YELLOW + f"名称: {app['name']}")
print(Fore.WHITE + f"描述: {app['description'][:100]}{'...' if len(app['description']) > 100 else ''}")
print(Fore.WHITE + f"评分: {app.get('avg_rating', '暂无')}")
print("-")
# 打印分页信息
self.print_info(f"总数: {data['pagination']['total']} 个应用")
def list_developer_apps(self, developer_id, page=1, limit=20):
"""获取某开发者的APP列表"""
data = self.make_api_request('getdeveloperapp', {'id': developer_id, 'page': page, 'limit': limit})
if data:
self.print_header(f"开发者ID {developer_id} 的应用列表")
if not data['apps']:
self.print_info("该开发者暂无应用")
return
# 打印应用列表
for app in data['apps']:
print(Fore.CYAN + f"ID: {app['id']}")
print(Fore.YELLOW + f"名称: {app['name']}")
print(Fore.WHITE + f"描述: {app['description'][:100]}{'...' if len(app['description']) > 100 else ''}")
print(Fore.WHITE + f"评分: {app.get('avg_rating', '暂无')}")
print(Fore.WHITE + f"下载量: {app.get('total_downloads', 0)}")
print("-")
# 打印分页信息
self.print_info(f"总数: {data['pagination']['total']} 个应用")
def get_developer_info(self, developer_id):
"""获取开发者信息"""
data = self.make_api_request('getdeveloperinfo', {'id': developer_id})
if data:
self.print_header(f"开发者信息")
print(Fore.CYAN + f"ID: {data['id']}")
print(Fore.YELLOW + f"用户名: {data['username']}")
print(Fore.WHITE + f"邮箱: {data['email']}")
print(Fore.WHITE + f"注册时间: {data['created_at']}")
print(Fore.WHITE + f"是否验证: {'' if data['is_verified'] else ''}")
if data['is_verified'] and data['verified_at']:
print(Fore.WHITE + f"验证时间: {data['verified_at']}")
print(Fore.GREEN + f"应用数量: {data['app_count']}")
def list_all_announcements(self, page=1, limit=20):
"""获取所有公告"""
data = self.make_api_request('getacc', {'page': page, 'limit': limit})
if data:
self.print_header(f"公告列表 (第 {page} 页,共 {data['pagination']['totalPages']} 页)")
if not data['announcements']:
self.print_info("暂无公告")
return
# 打印公告列表
for announcement in data['announcements']:
print(Fore.CYAN + f"ID: {announcement['id']}")
print(Fore.YELLOW + f"标题: {announcement['title']}")
print(Fore.WHITE + f"内容: {announcement['content'][:100]}{'...' if len(announcement['content']) > 100 else ''}")
print(Fore.WHITE + f"发布时间: {announcement['created_at']}")
print(Fore.WHITE + f"管理员ID: {announcement['admin_id']}")
print("-")
# 打印分页信息
self.print_info(f"总数: {data['pagination']['total']} 条公告")
def get_count_info(self):
"""获取计数信息"""
data = self.make_api_request('getcount')
if data:
self.print_header("应用商店统计信息")
print(Fore.GREEN + f"应用总数: {data['total_apps']}")
print(Fore.GREEN + f"开发者总数: {data['total_developers']}")
print(Fore.GREEN + f"标签总数: {data['total_tags']}")
print(Fore.GREEN + f"公告总数: {data['total_announcements']}")
print(Fore.GREEN + f"总下载量: {data['total_downloads']}")
def interactive_mode(self):
"""交互式模式"""
self.print_header("LeonApp CLI 交互式模式")
self.print_info("输入命令或 'help' 获取帮助,'exit' 退出")
while True:
try:
command = input(Fore.YELLOW + "\n> " + Fore.WHITE).strip().lower()
if not command:
continue
if command == 'exit' or command == 'quit':
self.print_success("感谢使用,再见!")
break
elif command == 'help':
self.show_help()
elif command == 'list apps':
try:
page = int(input("页码 (默认1): ") or "1")
limit = int(input("每页数量 (默认20): ") or "20")
self.list_all_apps(page, limit)
except ValueError:
self.print_error("请输入有效的数字")
elif command.startswith('app '):
try:
app_id = command.split()[1]
self.get_app_info(app_id)
except IndexError:
self.print_error("请提供应用ID")
elif command == 'list tags':
self.list_all_tags()
elif command.startswith('tag apps '):
try:
tag_id = command.split()[2]
page = int(input("页码 (默认1): ") or "1")
limit = int(input("每页数量 (默认20): ") or "20")
self.list_tag_apps(tag_id, page, limit)
except (IndexError, ValueError):
self.print_error("请提供有效的标签ID")
elif command.startswith('developer apps '):
try:
developer_id = command.split()[2]
page = int(input("页码 (默认1): ") or "1")
limit = int(input("每页数量 (默认20): ") or "20")
self.list_developer_apps(developer_id, page, limit)
except (IndexError, ValueError):
self.print_error("请提供有效的开发者ID")
elif command.startswith('developer info '):
try:
developer_id = command.split()[2]
self.get_developer_info(developer_id)
except IndexError:
self.print_error("请提供开发者ID")
elif command == 'list announcements':
try:
page = int(input("页码 (默认1): ") or "1")
limit = int(input("每页数量 (默认20): ") or "20")
self.list_all_announcements(page, limit)
except ValueError:
self.print_error("请输入有效的数字")
elif command == 'stats':
self.get_count_info()
elif command == 'check-update':
self.check_update()
else:
self.print_error("未知命令,请输入 'help' 获取帮助")
except KeyboardInterrupt:
print()
self.print_info("'exit' 退出")
except Exception as e:
self.print_error(f"发生错误: {str(e)}")
def show_help(self):
"""显示帮助信息"""
self.print_header("命令帮助")
print(Fore.CYAN + "exit/quit" + Fore.WHITE + " - 退出程序")
print(Fore.CYAN + "help" + Fore.WHITE + " - 显示此帮助信息")
print(Fore.CYAN + "list apps" + Fore.WHITE + " - 列出所有应用")
print(Fore.CYAN + "app [id]" + Fore.WHITE + " - 查看应用详情")
print(Fore.CYAN + "list tags" + Fore.WHITE + " - 列出所有标签")
print(Fore.CYAN + "tag apps [id]" + Fore.WHITE + " - 查看标签下的应用")
print(Fore.CYAN + "developer apps [id]" + Fore.WHITE + " - 查看开发者的应用")
print(Fore.CYAN + "developer info [id]" + Fore.WHITE + " - 查看开发者信息")
print(Fore.CYAN + "list announcements" + Fore.WHITE + " - 列出所有公告")
print(Fore.CYAN + "stats" + Fore.WHITE + " - 查看统计信息")
print(Fore.CYAN + "check-update" + Fore.WHITE + " - 检查更新")
def parse_arguments(self):
"""解析命令行参数"""
parser = argparse.ArgumentParser(description='LeonApp CLI - 一个用于访问App Store API的命令行工具')
# 子命令
subparsers = parser.add_subparsers(dest='command', help='可用命令')
# list-apps 命令
list_apps_parser = subparsers.add_parser('list-apps', help='列出所有应用')
list_apps_parser.add_argument('--page', type=int, default=1, help='页码 (默认: 1)')
list_apps_parser.add_argument('--limit', type=int, default=20, help='每页数量 (默认: 20)')
# app-info 命令
app_info_parser = subparsers.add_parser('app-info', help='查看应用详情')
app_info_parser.add_argument('id', help='应用ID')
# list-tags 命令
subparsers.add_parser('list-tags', help='列出所有标签')
# tag-apps 命令
tag_apps_parser = subparsers.add_parser('tag-apps', help='查看标签下的应用')
tag_apps_parser.add_argument('id', help='标签ID')
tag_apps_parser.add_argument('--page', type=int, default=1, help='页码 (默认: 1)')
tag_apps_parser.add_argument('--limit', type=int, default=20, help='每页数量 (默认: 20)')
# developer-apps 命令
developer_apps_parser = subparsers.add_parser('developer-apps', help='查看开发者的应用')
developer_apps_parser.add_argument('id', help='开发者ID')
developer_apps_parser.add_argument('--page', type=int, default=1, help='页码 (默认: 1)')
developer_apps_parser.add_argument('--limit', type=int, default=20, help='每页数量 (默认: 20)')
# developer-info 命令
developer_info_parser = subparsers.add_parser('developer-info', help='查看开发者信息')
developer_info_parser.add_argument('id', help='开发者ID')
# list-announcements 命令
list_announcements_parser = subparsers.add_parser('list-announcements', help='列出所有公告')
list_announcements_parser.add_argument('--page', type=int, default=1, help='页码 (默认: 1)')
list_announcements_parser.add_argument('--limit', type=int, default=20, help='每页数量 (默认: 20)')
# stats 命令
subparsers.add_parser('stats', help='查看统计信息')
# interactive 命令
subparsers.add_parser('interactive', help='进入交互式模式')
return parser.parse_args()
def check_update(self):
"""检查更新功能"""
self.print_header("检查更新")
self.print_info("正在检查更新...")
# 获取LeonAPP的版本信息App ID为15
data = self.make_api_request('getappversions', {'id': 15})
if data:
try:
# 检查数据格式
if not isinstance(data, dict) or 'versions' not in data:
self.print_error("获取版本信息失败:数据格式不正确")
return
versions = data.get('versions', [])
if not versions:
self.print_error("未找到任何版本信息")
return
# 最新版本通常在列表的第一个
latest_version = versions[0].get('version', '未知')
current_version = APP_VERSION
# 比较版本号
if latest_version != current_version:
# 版本不一致,显示更新提示
self.print_separator()
self.print_success(f"发现新版本:{latest_version}")
self.print_info(f"当前版本:{current_version}")
self.print_info("建议前往应用商店下载最新版本。")
self.print_separator()
else:
# 已是最新版本
self.print_success(f"已是最新版本:{current_version}")
except Exception as e:
self.print_error(f"处理版本信息时发生错误:{str(e)}")
else:
self.print_error("检查更新失败,请稍后重试。")
def run(self):
"""运行CLI"""
# 解析命令行参数
args = self.parse_arguments()
# 根据命令执行相应操作
if args.command == 'list-apps':
self.list_all_apps(args.page, args.limit)
elif args.command == 'app-info':
self.get_app_info(args.id)
elif args.command == 'list-tags':
self.list_all_tags()
elif args.command == 'tag-apps':
self.list_tag_apps(args.id, args.page, args.limit)
elif args.command == 'developer-apps':
self.list_developer_apps(args.id, args.page, args.limit)
elif args.command == 'developer-info':
self.get_developer_info(args.id)
elif args.command == 'list-announcements':
self.list_all_announcements(args.page, args.limit)
elif args.command == 'stats':
self.get_count_info()
elif args.command == 'check-update':
self.check_update()
elif args.command == 'interactive':
self.interactive_mode()
else:
# 如果没有提供命令或者命令未知,显示帮助信息并进入交互式模式
self.show_help()
self.interactive_mode()
if __name__ == "__main__":
# 创建CLI实例并运行
cli = LeonAppCLI()
try:
cli.run()
except KeyboardInterrupt:
print()
cli.print_success("程序已中断,再见!")
except Exception as e:
cli.print_error(f"程序发生错误: {str(e)}")
sys.exit(1)