MuMu.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. import subprocess
  2. import json
  3. import time
  4. import os
  5. import tkinter as tk
  6. class MuMuEmulatorManager:
  7. def __init__(self, manager_path=r"D:\MuMuPlayer\nx_main\MuMuManager.exe"):
  8. self.manager_path = manager_path
  9. if not os.path.exists(manager_path):
  10. raise FileNotFoundError(f"找不到 MuMuManager.exe: {manager_path}")
  11. def get_emulator_list(self):
  12. """获取所有模拟器列表"""
  13. cmd = [self.manager_path, "info", "-v", "all"]
  14. result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
  15. if result.returncode != 0:
  16. print(f"获取列表失败: {result.stderr}")
  17. return []
  18. try:
  19. data = json.loads(result.stdout)
  20. emulators = []
  21. for key, value in data.items():
  22. if isinstance(value, dict):
  23. value['index'] = key
  24. emulators.append(value)
  25. return emulators
  26. except json.JSONDecodeError as e:
  27. print(f"JSON解析失败: {e}")
  28. return []
  29. def start_emulator(self, index):
  30. """启动指定索引的模拟器 - 使用正确的 launch 命令"""
  31. print(f"正在启动模拟器 {index}...")
  32. # 正确的命令格式:control -v <index> launch
  33. cmd = [self.manager_path, "control", "-v", str(index), "launch"]
  34. print(f"执行命令: {' '.join(cmd)}")
  35. result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
  36. if result.returncode == 0:
  37. print(f"✅ 模拟器 {index} 启动命令已发送")
  38. return True
  39. else:
  40. print(f"❌ 启动失败")
  41. if result.stderr:
  42. print(f"错误信息: {result.stderr}")
  43. if result.stdout:
  44. print(f"输出: {result.stdout}")
  45. return False
  46. def wait_for_emulator_ready(self, index, timeout=120, check_interval=3):
  47. """等待模拟器启动完成(Android 系统就绪)"""
  48. print(f"\n等待模拟器 {index} 启动完成...")
  49. start_time = time.time()
  50. while time.time() - start_time < timeout:
  51. # 获取模拟器状态
  52. cmd = [self.manager_path, "info", "-v", str(index)]
  53. result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
  54. if result.returncode == 0:
  55. try:
  56. data = json.loads(result.stdout)
  57. # 检查 Android 是否已启动
  58. if data.get('is_android_started') == True:
  59. elapsed = time.time() - start_time
  60. print(f"✅ 模拟器 {index} 已就绪!耗时: {elapsed:.1f} 秒")
  61. return True
  62. elif data.get('is_process_started') == True:
  63. print(f"⏳ 进程已启动,等待 Android 系统... ({int(time.time() - start_time)}秒)")
  64. else:
  65. print(f"⏳ 等待进程启动... ({int(time.time() - start_time)}秒)")
  66. except:
  67. pass
  68. time.sleep(check_interval)
  69. print(f"❌ 超时!模拟器 {index} 在 {timeout} 秒内未就绪")
  70. return False
  71. def get_adb_port(self, index):
  72. """获取模拟器的 ADB 端口"""
  73. # 通过 MuMuManager 获取
  74. cmd = [self.manager_path, "info", "-v", str(index)]
  75. result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
  76. if result.returncode == 0:
  77. try:
  78. data = json.loads(result.stdout)
  79. # 注意:从之前的输出看,info 命令没有直接返回 adb_port
  80. # 可能需要通过其他方式获取
  81. pass
  82. except:
  83. pass
  84. # 使用 MuMu 默认端口规律:16384 + 32 * index
  85. default_port = 16384 + 32 * int(index)
  86. print(f"估算 ADB 端口: {default_port}")
  87. return default_port
  88. def install_apk(self, index, apk_path):
  89. """通过 ADB 安装 APK 到模拟器"""
  90. # 检查 APK 是否存在
  91. if not os.path.exists(apk_path):
  92. print(f"❌ 错误:APK 文件不存在: {apk_path}")
  93. return False
  94. # 获取 ADB 端口
  95. adb_port = self.get_adb_port(index)
  96. target_device = f"127.0.0.1:{adb_port}"
  97. # 连接 ADB
  98. print(f"\n连接 ADB {target_device}...")
  99. connect_result = subprocess.run(f"adb connect {target_device}",
  100. shell=True, capture_output=True, text=True)
  101. print(connect_result.stdout.strip())
  102. # 等待连接稳定
  103. time.sleep(2)
  104. # 检查设备连接
  105. devices_result = subprocess.run("adb devices", shell=True, capture_output=True, text=True)
  106. print(f"\n当前设备列表:\n{devices_result.stdout}")
  107. # 检查是否已安装 com.dragon.read
  108. print(f"\n检查是否已安装 com.dragon.read...")
  109. check_cmd = f"adb -s {target_device} shell pm list packages | findstr \"com.dragon.read\""
  110. check_result = subprocess.run(check_cmd, shell=True, capture_output=True, text=True)
  111. if "com.dragon.read" in check_result.stdout:
  112. print("✅ com.dragon.read 已安装,跳过安装步骤")
  113. return True
  114. # 安装 APK
  115. print(f"\n正在安装 APK: {os.path.basename(apk_path)}")
  116. install_cmd = f"adb -s {target_device} install -r \"{apk_path}\""
  117. result = subprocess.run(install_cmd, shell=True, capture_output=True, text=True)
  118. if "Success" in result.stdout:
  119. print("✅ APK 安装成功!")
  120. return True
  121. else:
  122. print("❌ APK 安装失败")
  123. print(f"输出: {result.stdout}")
  124. if result.stderr:
  125. print(f"错误: {result.stderr}")
  126. return False
  127. def display_emulator_list(self):
  128. """显示模拟器列表"""
  129. emulators = self.get_emulator_list()
  130. if not emulators:
  131. print("未找到任何模拟器")
  132. return
  133. print(f"\n{'='*60}")
  134. print(f"找到 {len(emulators)} 个模拟器:")
  135. print(f"{'='*60}\n")
  136. for i, emu in enumerate(emulators, 1):
  137. status = "🟢 运行中" if emu.get('is_process_started') else "🔴 未运行"
  138. android_status = "✅ 已启动" if emu.get('is_android_started') else "⏸️ 未启动"
  139. print(f"{i}. 索引 {emu.get('index')}: {emu.get('name')}")
  140. print(f" 进程状态: {status}")
  141. print(f" Android: {android_status}")
  142. print()
  143. def open_app(self, index, package_name="com.dragon.read"):
  144. """打开指定包名的应用"""
  145. # 获取 ADB 端口
  146. adb_port = self.get_adb_port(index)
  147. target_device = f"127.0.0.1:{adb_port}"
  148. # 连接 ADB
  149. print(f"\n连接 ADB {target_device}...")
  150. subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
  151. time.sleep(1)
  152. # 打开应用(使用 monkey 命令,最通用)
  153. print(f"\n正在打开应用: {package_name}")
  154. cmd = f"adb -s {target_device} shell monkey -p {package_name} -c android.intent.category.LAUNCHER 1"
  155. result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
  156. if "Events injected" in result.stdout or result.returncode == 0:
  157. print(f"✅ 应用已打开: {package_name}")
  158. return True
  159. else:
  160. print(f"❌ 打开失败")
  161. print(f"输出: {result.stdout}")
  162. if result.stderr:
  163. print(f"错误: {result.stderr}")
  164. return False
  165. def paste_text(self, index, text):
  166. """将文字放到系统剪贴板并粘贴"""
  167. # 获取 ADB 端口
  168. adb_port = self.get_adb_port(index)
  169. target_device = f"127.0.0.1:{adb_port}"
  170. # 连接 ADB
  171. subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
  172. time.sleep(0.5)
  173. # 1. 复制到电脑剪贴板
  174. """使用 Windows clip 命令复制到剪贴板"""
  175. root = tk.Tk()
  176. root.withdraw()
  177. root.clipboard_clear()
  178. root.clipboard_append(text)
  179. root.update()
  180. root.destroy()
  181. # 将文字设置到剪贴板
  182. escape_text = text.replace('"', '\\"').replace("'", "\\'")
  183. set_clipboard_cmd = f'adb -s {target_device} shell am broadcast -a clipper.set -e text "{escape_text}"'
  184. subprocess.run(set_clipboard_cmd, shell=True, capture_output=True)
  185. time.sleep(0.3)
  186. # 执行粘贴 (Ctrl+V)
  187. subprocess.run(f"adb -s {target_device} shell input keyevent 279", shell=True)
  188. print(f"✅ 已粘贴: {text[:50]}{'...' if len(text) > 50 else ''}")
  189. return True
  190. def tap(self, index, x, y):
  191. """点击指定坐标"""
  192. # 获取 ADB 端口
  193. adb_port = self.get_adb_port(index)
  194. target_device = f"127.0.0.1:{adb_port}"
  195. # 连接 ADB
  196. subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
  197. # 点击坐标
  198. cmd = f"adb -s {target_device} shell input tap {x} {y}"
  199. result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
  200. if result.returncode == 0:
  201. print(f"✅ 点击坐标 ({x}, {y})")
  202. return True
  203. else:
  204. print(f"❌ 点击失败: {result.stderr}")
  205. return False
  206. def main():
  207. # 配置
  208. MANAGER_PATH = r"D:\MuMuPlayer\nx_main\MuMuManager.exe"
  209. APK_PATH = r"fanqie.apk" # 请修改为你的 APK 实际路径
  210. TARGET_INDEX = 1 # 第二个模拟器的索引(索引从0开始,1是第二个)
  211. # 创建管理器实例
  212. try:
  213. manager = MuMuEmulatorManager(MANAGER_PATH)
  214. except FileNotFoundError as e:
  215. print(e)
  216. return
  217. # 1. 显示所有模拟器列表
  218. print("步骤1: 获取模拟器列表")
  219. manager.display_emulator_list()
  220. # 2. 启动第二个模拟器(索引为1)
  221. print(f"\n步骤2: 启动模拟器 {TARGET_INDEX}")
  222. if not manager.start_emulator(TARGET_INDEX):
  223. print("启动失败,退出")
  224. return
  225. # 3. 等待模拟器启动完成
  226. print(f"\n步骤3: 等待模拟器就绪")
  227. if not manager.wait_for_emulator_ready(TARGET_INDEX, timeout=180):
  228. print("模拟器未能在规定时间内就绪,退出")
  229. return
  230. time.sleep(5)
  231. # 4. 安装 APK
  232. print(f"\n步骤4: 安装 APK")
  233. if manager.install_apk(TARGET_INDEX, APK_PATH):
  234. print("\n🎉 全部步骤完成!")
  235. manager.open_app(TARGET_INDEX, package_name="com.dragon.read")
  236. time.sleep(40)
  237. manager.tap(TARGET_INDEX, 390, 90) # 点击输入框坐标(示例)
  238. time.sleep(2)
  239. manager.paste_text(TARGET_INDEX, "玄幻战神:开局就得到大佬的守护")
  240. time.sleep(2)
  241. manager.tap(TARGET_INDEX, 655, 92)
  242. # 进入数目
  243. time.sleep(5)
  244. manager.tap(TARGET_INDEX, 355, 333)
  245. ind = 0
  246. while ind < 10:
  247. time.sleep(30)
  248. manager.tap(TARGET_INDEX, 710, 632)
  249. ind += 1
  250. # 评价
  251. time.sleep(2)
  252. manager.tap(TARGET_INDEX, 360, 690)
  253. time.sleep(2)
  254. manager.tap(TARGET_INDEX, 680, 95)
  255. time.sleep(2)
  256. manager.tap(TARGET_INDEX, 633, 930)
  257. # 发表
  258. time.sleep(2)
  259. manager.tap(TARGET_INDEX, 640, 95)
  260. else:
  261. print("\n❌ 运行失败")
  262. if __name__ == "__main__":
  263. main()