#!/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)