Skip to main content
Glama
testflight_handler.py9.58 kB
""" App Store Connect TestFlight处理器 - 负责测试组和测试者管理 """ from typing import Any, List, Dict, Optional from ..models import BetaGroup, BetaTester from ...mcp_handler_interface import IMCPHandler class TestFlightHandler(IMCPHandler): """TestFlight处理器 - 负责测试组、测试者等TestFlight相关操作""" def __init__(self, client): self.client = client def register_tools(self, mcp: Any) -> None: """注册 AppStore TestFlight相关工具""" @mcp.tool("get_beta_groups") def get_beta_groups_tool(app_name: str) -> str: """ 获取指定应用的TestFlight测试组列表 Args: app_name (str): 应用名称,用于查找对应的应用 Returns: str: TestFlight测试组列表,包括组名和组类型(内部/外部测试组) """ try: app = self.client.handlers["AppHandler"].get_app_by_name(app_name) if not app: return f"未找到应用: {app_name}" groups = self.get_beta_groups_for_app(app.id) if not groups: return f"应用 {app_name} 没有TestFlight测试组" result = f"应用 {app_name} 的测试组:\n" for group in groups: result += f"- {group.name} ({group.group_type}测试组)\n" return result except Exception as e: return f"获取测试组失败: {str(e)}" @mcp.tool("get_beta_testers") def get_beta_testers_tool(app_name: str) -> str: """ 获取 AppStore 指定应用的TestFlight测试者列表 Args: app_name (str): 应用名称,用于查找对应的应用 Returns: str: TestFlight测试者列表,包括测试者邮箱、状态、加入时间等信息 """ try: app = self.client.handlers["AppHandler"].get_app_by_name(app_name) if not app: return f"未找到应用: {app_name}" testers = self.get_beta_testers_for_app(app.id) if not testers: return f"应用 {app_name} 没有TestFlight测试者" result = f"应用 {app_name} 的TestFlight测试者 ({len(testers)} 个):\n" for tester in testers: result += f"- {tester.email} ({tester.full_name}) - 状态: {tester.state_description}\n" return result except Exception as e: return f"获取TestFlight测试者失败: {str(e)}" @mcp.tool("remove_testflight_tester") def remove_testflight_tester_tool(email: str, app_name: str) -> str: """AppStore 从TestFlight测试组中移除测试者""" try: self.remove_beta_tester(email, app_name) return f"已成功从应用 {app_name} 的TestFlight测试组中移除用户 {email}" except ValueError as e: return str(e) except Exception as e: return f"移除TestFlight测试者失败: {str(e)}" def register_resources(self, mcp: Any) -> None: """注册TestFlight相关资源""" @mcp.resource("appstore://beta-testers") def get_beta_testers_resource() -> str: """AppStore 获取TestFlight测试者资源""" try: apps = self.client.handlers["AppHandler"].get_apps() if not apps: return "没有找到可用应用" all_testers = [] for app in apps: try: testers = self.get_beta_testers_for_app(app.id) for tester in testers: all_testers.append(f"{tester.email} - {app.name}") except Exception as e: print(f"获取应用 {app.name} 的测试者失败: {str(e)}") continue if not all_testers: return "没有找到TestFlight测试者" return f"所有TestFlight测试者:\n" + "\n".join([f"- {tester}" for tester in all_testers]) except Exception as e: return f"获取TestFlight测试者失败: {str(e)}" def register_prompts(self, mcp: Any) -> None: """注册TestFlight相关提示""" # TestFlight处理器暂时不需要特定的提示 pass # ============================================================================= # 业务逻辑方法 # ============================================================================= def get_beta_groups_for_app(self, app_id: str) -> List[BetaGroup]: """获取应用的TestFlight测试组""" response = self.client.make_api_request(f"apps/{app_id}/betaGroups") groups = [] for group_data in response.get("data", []): group = BetaGroup.from_api_response(group_data) groups.append(group) return groups def get_beta_testers_for_app(self, app_id: str) -> List[BetaTester]: """获取应用的TestFlight测试者列表""" try: # 首先获取应用的所有测试组 groups = self.get_beta_groups_for_app(app_id) if not groups: print(f"应用 {app_id} 没有找到任何测试组") return [] print(f"找到 {len(groups)} 个测试组,开始获取测试者...") unique_testers = {} # 用于去重,key 为 email # 遍历每个测试组,获取其中的测试者 for group in groups: try: print(f"正在获取测试组 '{group.name}' (ID: {group.id}) 的测试者...") # 获取该测试组的所有测试者 group_testers = self.get_beta_testers_for_group(group.id) # 添加到去重字典中 for tester in group_testers: unique_testers[tester.email] = tester print(f"测试组 '{group.name}' 有 {len(group_testers)} 个测试者") except Exception as group_error: print(f"获取测试组 '{group.name}' (ID: {group.id}) 的测试者失败: {str(group_error)}") # 继续处理下一个测试组,不中断整个流程 continue # 转换为列表并返回 all_testers = list(unique_testers.values()) print(f"总共获取到 {len(all_testers)} 个唯一的测试者") return all_testers except Exception as e: print(f"获取应用 {app_id} 的测试者失败: {str(e)}") raise Exception(f"无法获取应用 {app_id} 的测试者列表: {str(e)}") def get_beta_testers_for_group(self, group_id: str) -> List[BetaTester]: """获取指定测试组的测试人员列表""" try: response = self.client.make_api_request( f"betaGroups/{group_id}/betaTesters", method="GET", data={ "fields[betaTesters]": "email,firstName,lastName,state", "limit": "200" # 设置分页限制 } ) testers = [] for tester_data in response.get("data", []): tester = BetaTester.from_api_response(tester_data) testers.append(tester) return testers except Exception as e: print(f"获取测试组 {group_id} 的测试者失败: {str(e)}") raise Exception(f"无法获取测试组 {group_id} 的测试者列表: {str(e)}") def find_beta_tester_by_email(self, email: str, app_name: str) -> Optional[BetaTester]: """根据邮箱查找TestFlight测试者""" app = self.client.handlers["AppHandler"].get_app_by_name(app_name) if not app: return None testers = self.get_beta_testers_for_app(app.id) for tester in testers: if tester.email.lower() == email.lower(): return tester return None def add_beta_tester(self, email: str, first_name: str, beta_group_ids: List[str]) -> Dict[str, Any]: """添加TestFlight测试者""" last_name = "peropero" # 默认姓氏 data = { "data": { "type": "betaTesters", "attributes": { "email": email, "firstName": first_name, "lastName": last_name }, "relationships": { "betaGroups": { "data": [{"type": "betaGroups", "id": group_id} for group_id in beta_group_ids] } } } } return self.client.make_api_request("betaTesters", method="POST", data=data) def remove_beta_tester(self, email: str, app_name: str) -> Dict[str, Any]: """从TestFlight中移除测试者""" tester = self.find_beta_tester_by_email(email, app_name) if not tester: raise ValueError(f"用户 {email} 不在应用 {app_name} 的TestFlight测试组中") tester_id = tester.id # 调用删除API return self.client.make_api_request(f"betaTesters/{tester_id}", method="DELETE")

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/peroperogames/pero-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server