#[title: rb-aut] #[language: python] #[class: 工具类] #[service: 10010] 设备重启管理(设备管理模式) #[disable: false] #[admin: false] #[rule: ^(rb-now|rb管理)$] #[priority: 999] #[platform: qq,qb,wx,tb,tg,web,wxmp] #[open_source: false] #[icon: 图标url] #[version: 1.0.0] #[public: false] #[price: 0.00] #[description: 设备重启管理,支持多设备管理。
指令:rb-now(设备选单重启)、rb管理(设备管理),adb方式重启需安装依赖adbtool,并且首次运行需在设备授权] import sys import shlex import time import json import logging import subprocess from typing import Tuple, Optional import colorlog import middleware import sender # 存储桶前缀 BUCKET = 'rb-aut' sender = middleware.Sender(middleware.getSenderID()) class TOOL: def __init__(self): self.plugin_pre = BUCKET self.color_formatter = colorlog.ColoredFormatter( '%(log_color)s%(levelname)s: %(message)s', log_colors={ 'DEBUG': 'cyan', 'INFO': 'green', 'WARNING': 'yellow', 'ERROR': 'red', 'CRITICAL': 'red,bg_white', } ) self.logger = self._configure_logger() def _configure_logger(self): logger_obj = logging.getLogger(self.plugin_pre) logger_obj.setLevel(logging.DEBUG) if not any(isinstance(h, logging.StreamHandler) for h in logger_obj.handlers): console_handler = logging.StreamHandler() console_handler.setLevel(logging.DEBUG) console_handler.setFormatter(self.color_formatter) logger_obj.addHandler(console_handler) return logger_obj def log(self, level, message): self.logger.log(level, message) def reply(self, content): return sender.reply(content) def bucket_get(key: str, default: Optional[str] = None) -> Optional[str]: val = middleware.bucketGet(BUCKET, key) return val if val is not None and val != '' else default def bucket_set(key: str, value: str) -> None: middleware.bucketSet(BUCKET, key, value) def get_all_devices() -> dict: """获取所有设备配置""" devices = {} all_keys = middleware.bucketAllKeys(BUCKET) for key in all_keys: if key.startswith('device.'): device_id = key[7:] # 去掉 'device.' 前缀 device_data = bucket_get(key) if device_data: try: devices[device_id] = json.loads(device_data) except Exception: continue return devices def save_device(device_id: str, device_config: dict) -> None: """保存设备配置""" bucket_set(f'device.{device_id}', json.dumps(device_config)) def delete_device(device_id: str) -> None: """删除设备配置""" middleware.bucketDel(BUCKET, f'device.{device_id}') def has_device_permission(device_config: dict, user_id: str, is_admin: bool) -> bool: """检查用户是否有设备权限 Args: device_config: 设备配置字典 user_id: 用户ID is_admin: 是否是管理员 Returns: bool: 是否有权限 """ # 管理员有所有权限 if is_admin: return True # 检查设备权限配置 admin_only = device_config.get('admin_only', False) if admin_only: return False # 检查允许的用户列表 allowed_users = device_config.get('allowed_users', []) if isinstance(allowed_users, str): # 兼容旧格式(逗号分隔的字符串) allowed_users = [u.strip() for u in allowed_users.split(',') if u.strip()] if not isinstance(allowed_users, list): allowed_users = [] # 如果允许的用户列表为空,则默认所有用户都可以访问 if not allowed_users: return True return user_id in allowed_users def get_device_list(user_id: Optional[str] = None, is_admin: bool = False) -> list: """获取设备列表,返回格式化的设备信息 Args: user_id: 用户ID,如果提供则只返回该用户有权限的设备 is_admin: 是否是管理员,管理员可以看到所有设备 """ devices = get_all_devices() device_list = [] for device_id, config in devices.items(): device_type = config.get('type', 'unknown') name = config.get('name', device_id) # 如果指定了用户ID且不是管理员,检查权限 if user_id and not is_admin: if not has_device_permission(config, user_id, False): continue if device_type == 'adb': device_list.append({ 'id': device_id, 'name': name, 'type': 'ADB', 'info': name }) elif device_type == 'ssh': device_list.append({ 'id': device_id, 'name': name, 'type': 'SSH', 'info': name }) return device_list def run_command(command: str, timeout_seconds: int = 20) -> Tuple[int, str, str]: try: process = subprocess.Popen( shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) stdout, stderr = process.communicate(timeout=timeout_seconds) return process.returncode, (stdout or '').strip(), (stderr or '').strip() except subprocess.TimeoutExpired: try: process.kill() except Exception: pass return 124, '', f"Command timed out after {timeout_seconds}s: {command}" except FileNotFoundError: return 127, '', f"Command not found: {command.split()[0]}" class ADBRebootClient: def __init__(self, address: str, adb_path: str = 'adb', timeout: int = 20, tool: Optional[TOOL] = None): self.address = address self.adb = adb_path or 'adb' self.timeout = timeout self.tool = tool def _log(self, level, message: str): if self.tool: self.tool.log(level, message) def disconnect(self) -> None: run_command(f"{self.adb} disconnect {self.address}", timeout_seconds=min(self.timeout, 5)) def kill_server(self) -> None: run_command(f"{self.adb} kill-server", timeout_seconds=min(self.timeout, 5)) def cleanup(self) -> None: """清理ADB连接和服务""" try: # 先断开指定设备连接 self.disconnect() # 等待一下确保断开完成 time.sleep(1) # 然后kill整个adb服务 self.kill_server() if self.tool: self.tool.log(logging.INFO, "✓ ADB服务已清理完成") except Exception as e: if self.tool: self.tool.log(logging.WARNING, f"ADB清理过程中出现异常: {e}") def ensure_adb(self) -> bool: code, out, err = run_command(f"{self.adb} version", timeout_seconds=self.timeout) if code != 0: self._log(logging.ERROR, "未找到可用的 adb,请安装或在参数中指定 adb 路径") if err: self._log(logging.ERROR, err) return False self._log(logging.INFO, (out or 'adb 可用')) return True def connect(self) -> bool: self.disconnect() code, out, err = run_command(f"{self.adb} connect {self.address}", timeout_seconds=self.timeout) msg = ((out or '') + ("\n" + err if err else '')).strip() if code != 0: self._log(logging.WARNING, f"adb connect 首次失败(code={code}),尝试重启 adb 并重试...\n{msg}") self.kill_server() self.disconnect() code, out, err = run_command(f"{self.adb} connect {self.address}", timeout_seconds=self.timeout) msg = ((out or '') + ("\n" + err if err else '')).strip() if code != 0: self._log(logging.ERROR, f"adb connect 重试失败(code={code}):\n{msg}") return False low = msg.lower() if ("connected to" in low) or ("already connected to" in low): self._log(logging.INFO, "✓ ADB 连接成功") else: self._log(logging.WARNING, "adb connect 返回0,但输出未包含已连接提示") if msg: self._log(logging.INFO, msg) ok, detail = self.get_state() if not ok: self._log(logging.ERROR, f"连接校验失败:{detail}") return False self._log(logging.INFO, f"设备状态:{detail}") return True def get_state(self) -> Tuple[bool, str]: code, out, err = run_command(f"{self.adb} -s {self.address} get-state", timeout_seconds=min(self.timeout, 10)) text = (out or err or '').strip() if code == 0 and (out or '').strip() == 'device': return True, 'device' return False, (text or f"get-state 失败(code={code})") def reboot(self) -> bool: def looks_like_reboot_sent(message: str) -> bool: low = (message or '').lower() hints = [ 'closed', 'device offline', 'no devices/emulators found', 'cannot connect to daemon', 'daemon not running', 'failed to connect to', 'connection refused', 'connection reset', 'transport endpoint is not connected', 'broken pipe', 'read failed', 'i/o error', 'more than one device/emulator', ] return any(h in low for h in hints) def observe_post_reboot_disconnect(max_attempts: int = 8, interval_seconds: float = 0.5) -> bool: for _ in range(max_attempts): code_s, out_s, err_s = run_command( f"{self.adb} -s {self.address} get-state", timeout_seconds=min(self.timeout, 5) ) if code_s != 0: return True if (out_s or '').strip() != 'device': return True time.sleep(interval_seconds) return False code, out, err = run_command(f"{self.adb} -s {self.address} reboot", timeout_seconds=self.timeout) combined = ((out or '') + ("\n" + err if err else '')).strip() if code == 0: self._log(logging.INFO, "✓ 已发送重启指令") return True if looks_like_reboot_sent(combined): self._log(logging.WARNING, f"adb reboot 返回非零(code={code}),但检测到重启迹象,视为成功:\n{combined}") return True self._log(logging.WARNING, f"adb reboot 失败(code={code}),尝试使用 shell reboot 回退...\n{combined}") code2, out2, err2 = run_command(f"{self.adb} -s {self.address} shell reboot", timeout_seconds=self.timeout) combined2 = ((out2 or '') + ("\n" + err2 if err2 else '')).strip() if code2 == 0 or looks_like_reboot_sent(combined2): self._log(logging.INFO, "✓ 已通过 shell reboot 发送重启指令") return True if observe_post_reboot_disconnect(): self._log(logging.WARNING, "检测到重启后的断连迹象,尽管命令返回失败,仍视为已下发成功") return True self._log(logging.ERROR, f"shell reboot 仍失败(code={code2}):\n{combined2}") return False class SSHRebootClient: def __init__(self, hostname: str, username: str, password: str, port: int = 5722, timeout: int = 30, tool: Optional[TOOL] = None): self.hostname = hostname self.username = username self.password = password self.port = port self.timeout = timeout self.tool = tool self.client = None def connect(self) -> bool: try: if self.tool: self.tool.log(logging.INFO, f"正在连接到 {self.hostname}:{self.port}...") import paramiko self.client = paramiko.SSHClient() self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) self.client.connect( hostname=self.hostname, username=self.username, password=self.password, port=self.port, timeout=self.timeout, allow_agent=False, look_for_keys=False, ) if self.tool: self.tool.log(logging.INFO, "✓ SSH连接建立成功!") return True except Exception as e: if self.tool: self.tool.log(logging.ERROR, f"✗ SSH连接失败: {e}") return False def _execute_reboot_command(self, command: str) -> bool: reboot_commands = [ command, f"sudo {command}", f"nohup {command} > /dev/null 2>&1 &", f"sudo nohup {command} > /dev/null 2>&1 &", ] for i, cmd in enumerate(reboot_commands): try: if self.tool: self.tool.log(logging.INFO, f"尝试执行重启命令 ({i+1}/{len(reboot_commands)}): {cmd}") stdin, stdout, stderr = self.client.exec_command(cmd, timeout=3) try: _ = stdout.read() _ = stderr.read() except Exception: pass time.sleep(1) try: t_in, t_out, t_err = self.client.exec_command("echo test", timeout=1) _ = t_out.read() except Exception: if self.tool: self.tool.log(logging.INFO, "✓ 检测到SSH连接断开,重启命令生效!") self.tool.log(logging.INFO, "✓ 设备正在重启中...") return True if i < len(reboot_commands) - 1: if self.tool: self.tool.log(logging.INFO, "连接仍在,尝试下一种命令格式...") time.sleep(1) except Exception as e: if self.tool: self.tool.log(logging.WARNING, f"命令执行异常: {e}") try: _ = self.client.get_transport().is_active() except Exception: if self.tool: self.tool.log(logging.INFO, "✓ 检测到SSH连接断开,重启命令生效!") return True if i < len(reboot_commands) - 1: if self.tool: self.tool.log(logging.INFO, "尝试下一种命令格式...") time.sleep(1) if self.tool: self.tool.log(logging.WARNING, "⚠️ 所有重启命令格式都尝试过了;重启可能已发送") return True def send_reboot_command(self, command: str = 'reboot') -> bool: if not self.client: if self.tool: self.tool.log(logging.ERROR, "✗ 未建立SSH连接") return False try: if command.lower() in ['reboot', 'shutdown', 'poweroff']: return self._execute_reboot_command(command) stdin, stdout, stderr = self.client.exec_command(command, timeout=30) exit_status = stdout.channel.recv_exit_status() _ = stdout.read() _ = stderr.read() if exit_status == 0: if self.tool: self.tool.log(logging.INFO, "✓ 命令执行成功!") return True if self.tool: self.tool.log(logging.ERROR, f"✗ 命令执行失败,退出码: {exit_status}") return False except Exception as e: if self.tool: self.tool.log(logging.ERROR, f"✗ 执行命令时出错: {e}") return False def is_connected(self) -> bool: try: if self.client and self.client.get_transport(): return self.client.get_transport().is_active() return False except Exception: return False def close(self) -> None: if self.client: try: self.client.close() if self.tool: self.tool.log(logging.INFO, "SSH连接已关闭") except Exception: if self.tool: self.tool.log(logging.INFO, "SSH连接已断开") def run_adb_reboot(tool: TOOL, device_config: dict) -> bool: """执行ADB设备重启""" addr = device_config.get('address', '') name = device_config.get('name', '未知设备') tool.reply(f"开始执行 {name} ADB 重启流程...") if not addr: tool.reply("❌ 设备配置不完整,缺少 address") return tool.log(logging.INFO, "配置信息:") tool.log(logging.INFO, f" 设备名称: {name}") tool.log(logging.INFO, f" ADB地址: {addr}") client = ADBRebootClient(address=addr, adb_path='adb', timeout=20, tool=tool) try: if not client.ensure_adb(): tool.reply("❌ 未检测到 adb,请安装 adb") return False tool.log(logging.INFO, "🚀 正在连接设备...") if not client.connect(): tool.reply("❌ ADB 连接失败,请检查地址与网络") return False tool.log(logging.INFO, "🔁 正在发送重启指令...") if client.reboot(): tool.reply("✅ 重启指令已发送!设备即将重启") # 等待一下让重启指令生效 time.sleep(2) # 清理ADB连接和服务 tool.log(logging.INFO, "🔌 正在清理ADB连接...") client.cleanup() return True else: tool.reply("❌ 重启指令发送失败") # 即使失败也清理连接 tool.log(logging.INFO, "🔌 正在清理ADB连接...") client.cleanup() return False except Exception as exc: tool.log(logging.ERROR, f"程序执行出错: {exc}") tool.reply("❌ 程序执行出错,请检查配置与网络") # 异常情况下也尝试清理 try: tool.log(logging.INFO, "🔌 异常情况下清理ADB连接...") client.cleanup() except Exception: pass return False def run_ssh_reboot(tool: TOOL, device_config: dict) -> bool: """执行SSH设备重启""" host = device_config.get('hostname', '') username = device_config.get('username', 'root') password = device_config.get('password', '') port = device_config.get('port', 22) timeout = device_config.get('timeout', 30) command = device_config.get('command', 'reboot') name = device_config.get('name', '未知设备') tool.reply(f"开始执行 {name} SSH 重启流程...") if not host or not username or not password: tool.reply("❌ 设备配置不完整,缺少 hostname/username/password") return False tool.log(logging.INFO, f"配置信息:") tool.log(logging.INFO, f" 设备名称: {name}") tool.log(logging.INFO, f" 主机: {host}") tool.log(logging.INFO, f" 用户: {username}") tool.log(logging.INFO, f" 端口: {port}") tool.log(logging.INFO, f" 命令: {command}") tool.log(logging.INFO, f" 超时: {timeout}秒") client = SSHRebootClient(hostname=host, username=username, password=password, port=port, timeout=timeout, tool=tool) try: if not client.connect(): tool.reply("❌ SSH连接失败,请检查设备地址和认证信息") return False tool.log(logging.INFO, "🚀 开始执行重启操作...") result = client.send_reboot_command(command) if result: tool.reply("✅ 重启操作完成!") tool.reply("📱 设备正在重启中,请稍后检查设备状态") time.sleep(3) if not client.is_connected(): tool.log(logging.INFO, "🔌 SSH连接已断开,确认设备正在重启") else: tool.log(logging.WARNING, "⚠️ SSH连接仍在,设备可能未重启成功") return True else: tool.reply("❌ 重启命令执行失败!") return False except Exception as e: tool.log(logging.ERROR, f"程序执行出错: {e}") tool.reply("❌ 程序执行出错,请检查配置和网络连接") return False finally: client.close() tool.log(logging.INFO, "🏁 脚本执行完毕") def reboot_device(tool: TOOL, device_config: dict) -> bool: """根据设备类型执行重启,返回是否成功""" device_type = device_config.get('type') if device_type == 'adb': return run_adb_reboot(tool, device_config) if device_type == 'ssh': return run_ssh_reboot(tool, device_config) tool.reply("❌ 未知设备类型") return False def cmd_rb_now(tool: TOOL, user_id: str, is_admin: bool): """rb-now 命令:显示设备选单并执行重启""" device_list = get_device_list(user_id, is_admin) if not device_list: tool.reply("❌ 暂无可用设备,请联系管理员添加设备") return # 显示设备选单(只显示名称),0 为全部重启 menu_text = "请选择要重启的设备:\n0. 重启所有设备\n" for i, device in enumerate(device_list, 1): menu_text += f"{i}. {device['name']}\n" tool.reply(menu_text) # 等待用户选择 try: choice = sender.listen(60000) if not choice or choice == 'q' or choice == 'error': tool.reply("已取消") return # 选择 0 重启所有设备 if choice == '0': devices = get_all_devices() total = len(device_list) success = 0 fail = 0 for item in device_list: cfg = devices.get(item['id']) if not cfg: fail += 1 continue # 再次验证权限,确保安全 if not has_device_permission(cfg, user_id, is_admin): fail += 1 continue ok = reboot_device(tool, cfg) if ok: success += 1 else: fail += 1 tool.reply(f"执行完成:成功 {success}/{total},失败 {fail}/{total}") return choice_num = int(choice) if 1 <= choice_num <= len(device_list): selected_device = device_list[choice_num - 1] device_id = selected_device['id'] devices = get_all_devices() device_config = devices.get(device_id) if not device_config: tool.reply("❌ 设备配置不存在") return # 再次验证权限,确保安全 if not has_device_permission(device_config, user_id, is_admin): tool.reply("❌ 您没有权限重启此设备") return reboot_device(tool, device_config) else: tool.reply("❌ 无效选择") except ValueError: tool.reply("❌ 请输入有效数字") except Exception as e: tool.reply(f"❌ 选择设备时出错: {e}") def cmd_rb_manage(tool: TOOL, user_id: str, is_admin: bool): """rb管理 命令:设备管理(仅管理员可用)""" if not is_admin: tool.reply("❌ 此命令仅管理员可使用") return device_list = get_device_list(user_id, is_admin) if not device_list: tool.reply("设备管理菜单:\n1. 添加设备\n0. 返回") try: choice = sender.listen(60000) if not choice or choice == '0' or choice == 'q' or choice == 'error': tool.reply("已返回") return if choice == '1': add_device(tool) else: tool.reply("❌ 无效选择") except Exception as e: tool.reply(f"❌ 操作出错: {e}") return # 显示设备列表和管理选项(仅名称),提示 + 和 - 用法 menu_text = "设备管理菜单:\n" for i, device in enumerate(device_list, 1): menu_text += f"{i}. {device['name']}\n" menu_text += "+ (添加设备)\n- 序号 (删除设备)\n0. 返回" tool.reply(menu_text) try: choice = sender.listen(60000) if not choice or choice == '0' or choice == 'q' or choice == 'error': tool.reply("已返回") return if choice == '+': add_device(tool) elif choice.startswith('-'): # 删除设备:-序号 try: device_num = int(choice[1:]) if 1 <= device_num <= len(device_list): delete_device_by_number(tool, device_list, device_num - 1) else: tool.reply("❌ 无效的设备序号") except ValueError: tool.reply("❌ 请输入正确的格式:-序号") elif choice.isdigit(): # 查看/修改设备:序号 device_num = int(choice) if 1 <= device_num <= len(device_list): view_edit_device(tool, device_list, device_num - 1) else: tool.reply("❌ 无效的设备序号") else: tool.reply("❌ 无效选择") except Exception as e: tool.reply(f"❌ 操作出错: {e}") def add_device(tool: TOOL): """添加设备""" tool.reply("请选择设备类型:\n1. ADB设备\n2. SSH设备\n0. 取消") try: type_choice = sender.listen(60000) if not type_choice or type_choice == '0' or type_choice == 'q' or type_choice == 'error': tool.reply("已取消") return if type_choice == '1': add_adb_device(tool) elif type_choice == '2': add_ssh_device(tool) else: tool.reply("❌ 无效选择") except Exception as e: tool.reply(f"❌ 添加设备出错: {e}") def add_adb_device(tool: TOOL): """添加ADB设备""" tool.reply("请输入设备名称(q:退出)") name = sender.listen(60000) if not name or name == 'q' or name == 'error': tool.reply("已退出") return tool.reply("请输入ADB地址(示例:192.168.1.100:5555,q:退出)") address = sender.listen(60000) if not address or address == 'q' or address == 'error': tool.reply("已退出") return # 设置设备权限 tool.reply("请设置设备权限:\n1. 仅管理员可用\n2. 所有用户可用\n3. 指定用户可用\n0. 取消") perm_choice = sender.listen(60000) if not perm_choice or perm_choice == '0' or perm_choice == 'q' or perm_choice == 'error': tool.reply("已取消") return device_config = { 'type': 'adb', 'name': name, 'address': address, 'admin_only': False, 'allowed_users': [] } if perm_choice == '1': device_config['admin_only'] = True elif perm_choice == '2': device_config['admin_only'] = False device_config['allowed_users'] = [] # 空列表表示所有用户可用 elif perm_choice == '3': tool.reply("请输入允许访问的用户ID(多个用户用逗号分隔,q:退出)") users_input = sender.listen(60000) if not users_input or users_input == 'q' or users_input == 'error': tool.reply("已取消") return allowed_users = [u.strip() for u in users_input.split(',') if u.strip()] device_config['admin_only'] = False device_config['allowed_users'] = allowed_users else: tool.reply("❌ 无效选择,已取消") return # 生成设备ID device_id = f"adb_{int(time.time())}" save_device(device_id, device_config) tool.reply(f"✅ ADB设备 '{name}' 添加成功!") def add_ssh_device(tool: TOOL): """添加SSH设备""" tool.reply("请输入设备名称(q:退出)") name = sender.listen(60000) if not name or name == 'q' or name == 'error': tool.reply("已退出") return tool.reply("请输入SSH主机地址(示例:192.168.1.200,q:退出)") hostname = sender.listen(60000) if not hostname or hostname == 'q' or hostname == 'error': tool.reply("已退出") return tool.reply("请输入SSH用户名(默认root,直接回车跳过)") username = sender.listen(60000) if not username or username == 'q' or username == 'error': username = 'root' tool.reply("请输入SSH密码(q:退出)") password = sender.listen(60000) if not password or password == 'q' or password == 'error': tool.reply("已退出") return tool.reply("请输入SSH端口(默认22,直接回车跳过)") port_str = sender.listen(60000) port = 22 if port_str and port_str.isdigit(): port = int(port_str) tool.reply("请输入重启命令(默认reboot,直接回车跳过)") command = sender.listen(60000) if not command or command == 'q' or command == 'error': command = 'reboot' tool.reply("请输入超时时间(默认30秒,直接回车跳过)") timeout_str = sender.listen(60000) timeout = 30 if timeout_str and timeout_str.isdigit(): timeout = int(timeout_str) # 设置设备权限 tool.reply("请设置设备权限:\n1. 仅管理员可用\n2. 所有用户可用\n3. 指定用户可用\n0. 取消") perm_choice = sender.listen(60000) if not perm_choice or perm_choice == '0' or perm_choice == 'q' or perm_choice == 'error': tool.reply("已取消") return device_config = { 'type': 'ssh', 'name': name, 'hostname': hostname, 'username': username, 'password': password, 'port': port, 'command': command, 'timeout': timeout, 'admin_only': False, 'allowed_users': [] } if perm_choice == '1': device_config['admin_only'] = True elif perm_choice == '2': device_config['admin_only'] = False device_config['allowed_users'] = [] # 空列表表示所有用户可用 elif perm_choice == '3': tool.reply("请输入允许访问的用户ID(多个用户用逗号分隔,q:退出)") users_input = sender.listen(60000) if not users_input or users_input == 'q' or users_input == 'error': tool.reply("已取消") return allowed_users = [u.strip() for u in users_input.split(',') if u.strip()] device_config['admin_only'] = False device_config['allowed_users'] = allowed_users else: tool.reply("❌ 无效选择,已取消") return # 生成设备ID device_id = f"ssh_{int(time.time())}" save_device(device_id, device_config) tool.reply(f"✅ SSH设备 '{name}' 添加成功!") def show_device_list(tool: TOOL): """显示设备列表""" device_list = get_device_list() if not device_list: tool.reply("❌ 暂无设备") return text = "当前设备列表:\n" for i, device in enumerate(device_list, 1): text += f"{i}. {device['name']}\n" tool.reply(text) def view_edit_device(tool: TOOL, device_list: list, device_index: int): """查看/编辑设备配置""" selected_device = device_list[device_index] device_id = selected_device['id'] device_name = selected_device['name'] device_type = selected_device['type'] # 获取设备详细配置 devices = get_all_devices() device_config = devices.get(device_id) if not device_config: tool.reply("❌ 设备配置不存在") return # 显示设备配置 config_text = f"设备配置详情:\n" config_text += f"设备名称: {device_config.get('name', '未知')}\n" config_text += f"设备类型: {device_config.get('type', '未知')}\n" # 显示权限信息 admin_only = device_config.get('admin_only', False) allowed_users = device_config.get('allowed_users', []) if isinstance(allowed_users, str): allowed_users = [u.strip() for u in allowed_users.split(',') if u.strip()] if not isinstance(allowed_users, list): allowed_users = [] if admin_only: config_text += f"权限设置: 仅管理员可用\n" elif not allowed_users: config_text += f"权限设置: 所有用户可用\n" else: config_text += f"权限设置: 指定用户可用 ({', '.join(allowed_users)})\n" if device_type == 'ADB': config_text += f"ADB地址: {device_config.get('address', '未设置')}\n" elif device_type == 'SSH': config_text += f"SSH主机: {device_config.get('hostname', '未设置')}\n" config_text += f"SSH用户: {device_config.get('username', 'root')}\n" config_text += f"SSH密码: {'******' if device_config.get('password') else '未设置'}\n" config_text += f"SSH端口: {device_config.get('port', 22)}\n" config_text += f"重启命令: {device_config.get('command', 'reboot')}\n" config_text += f"超时时间: {device_config.get('timeout', 30)}秒\n" config_text += "\n操作选项:\n1. 修改配置\n2. 删除设备\n0. 返回" tool.reply(config_text) try: choice = sender.listen(60000) if not choice or choice == '0' or choice == 'q' or choice == 'error': tool.reply("已返回") return if choice == '1': edit_device_config(tool, device_id, device_config) elif choice == '2': # 确认删除 tool.reply(f"确认删除设备 '{device_name}' 吗?(y/n)") confirm = sender.listen(30000) if confirm and confirm.lower() in ['y', 'yes', '是']: delete_device(device_id) tool.reply(f"✅ 设备 '{device_name}' 已删除") else: tool.reply("已取消删除") else: tool.reply("❌ 无效选择") except Exception as e: tool.reply(f"❌ 操作出错: {e}") def edit_device_config(tool: TOOL, device_id: str, device_config: dict): """编辑设备配置""" device_type = device_config.get('type') device_name = device_config.get('name', '未知设备') tool.reply(f"正在编辑设备 '{device_name}' 的配置...") if device_type == 'adb': edit_adb_device_config(tool, device_id, device_config) elif device_type == 'ssh': edit_ssh_device_config(tool, device_id, device_config) else: tool.reply("❌ 未知设备类型") def edit_adb_device_config(tool: TOOL, device_id: str, device_config: dict): """编辑ADB设备配置""" tool.reply("请选择要修改的配置项:\n1. 设备名称\n2. ADB地址\n3. 权限设置\n0. 返回") try: choice = sender.listen(60000) if not choice or choice == '0' or choice == 'q' or choice == 'error': tool.reply("已返回") return if choice == '1': tool.reply("请输入新的设备名称(q:退出)") new_name = sender.listen(60000) if new_name and new_name != 'q' and new_name != 'error': device_config['name'] = new_name save_device(device_id, device_config) tool.reply(f"✅ 设备名称已更新为: {new_name}") else: tool.reply("已取消") elif choice == '2': tool.reply("请输入新的ADB地址(示例:192.168.1.100:5555,q:退出)") new_address = sender.listen(60000) if new_address and new_address != 'q' and new_address != 'error': device_config['address'] = new_address save_device(device_id, device_config) tool.reply(f"✅ ADB地址已更新为: {new_address}") else: tool.reply("已取消") elif choice == '3': tool.reply("请设置设备权限:\n1. 仅管理员可用\n2. 所有用户可用\n3. 指定用户可用\n0. 取消") perm_choice = sender.listen(60000) if not perm_choice or perm_choice == '0' or perm_choice == 'q' or perm_choice == 'error': tool.reply("已取消") return if perm_choice == '1': device_config['admin_only'] = True device_config['allowed_users'] = [] save_device(device_id, device_config) tool.reply("✅ 权限已设置为:仅管理员可用") elif perm_choice == '2': device_config['admin_only'] = False device_config['allowed_users'] = [] save_device(device_id, device_config) tool.reply("✅ 权限已设置为:所有用户可用") elif perm_choice == '3': tool.reply("请输入允许访问的用户ID(多个用户用逗号分隔,q:退出)") users_input = sender.listen(60000) if not users_input or users_input == 'q' or users_input == 'error': tool.reply("已取消") return allowed_users = [u.strip() for u in users_input.split(',') if u.strip()] device_config['admin_only'] = False device_config['allowed_users'] = allowed_users save_device(device_id, device_config) tool.reply(f"✅ 权限已设置为:指定用户可用 ({', '.join(allowed_users)})") else: tool.reply("❌ 无效选择") else: tool.reply("❌ 无效选择") except Exception as e: tool.reply(f"❌ 操作出错: {e}") def edit_ssh_device_config(tool: TOOL, device_id: str, device_config: dict): """编辑SSH设备配置""" tool.reply("请选择要修改的配置项:\n1. 设备名称\n2. SSH主机\n3. SSH用户\n4. SSH密码\n5. SSH端口\n6. 重启命令\n7. 超时时间\n8. 权限设置\n0. 返回") try: choice = sender.listen(60000) if not choice or choice == '0' or choice == 'q' or choice == 'error': tool.reply("已返回") return if choice == '1': tool.reply("请输入新的设备名称(q:退出)") new_name = sender.listen(60000) if new_name and new_name != 'q' and new_name != 'error': device_config['name'] = new_name save_device(device_id, device_config) tool.reply(f"✅ 设备名称已更新为: {new_name}") else: tool.reply("已取消") elif choice == '2': tool.reply("请输入新的SSH主机地址(q:退出)") new_hostname = sender.listen(60000) if new_hostname and new_hostname != 'q' and new_hostname != 'error': device_config['hostname'] = new_hostname save_device(device_id, device_config) tool.reply(f"✅ SSH主机已更新为: {new_hostname}") else: tool.reply("已取消") elif choice == '3': tool.reply("请输入新的SSH用户名(q:退出)") new_username = sender.listen(60000) if new_username and new_username != 'q' and new_username != 'error': device_config['username'] = new_username save_device(device_id, device_config) tool.reply(f"✅ SSH用户已更新为: {new_username}") else: tool.reply("已取消") elif choice == '4': tool.reply("请输入新的SSH密码(q:退出)") new_password = sender.listen(60000) if new_password and new_password != 'q' and new_password != 'error': device_config['password'] = new_password save_device(device_id, device_config) tool.reply("✅ SSH密码已更新") else: tool.reply("已取消") elif choice == '5': tool.reply("请输入新的SSH端口(q:退出)") new_port = sender.listen(60000) if new_port and new_port != 'q' and new_port != 'error': if new_port.isdigit(): device_config['port'] = int(new_port) save_device(device_id, device_config) tool.reply(f"✅ SSH端口已更新为: {new_port}") else: tool.reply("❌ 端口必须是数字") else: tool.reply("已取消") elif choice == '6': tool.reply("请输入新的重启命令(q:退出)") new_command = sender.listen(60000) if new_command and new_command != 'q' and new_command != 'error': device_config['command'] = new_command save_device(device_id, device_config) tool.reply(f"✅ 重启命令已更新为: {new_command}") else: tool.reply("已取消") elif choice == '7': tool.reply("请输入新的超时时间(秒,q:退出)") new_timeout = sender.listen(60000) if new_timeout and new_timeout != 'q' and new_timeout != 'error': if new_timeout.isdigit(): device_config['timeout'] = int(new_timeout) save_device(device_id, device_config) tool.reply(f"✅ 超时时间已更新为: {new_timeout}秒") else: tool.reply("❌ 超时时间必须是数字") else: tool.reply("已取消") elif choice == '8': tool.reply("请设置设备权限:\n1. 仅管理员可用\n2. 所有用户可用\n3. 指定用户可用\n0. 取消") perm_choice = sender.listen(60000) if not perm_choice or perm_choice == '0' or perm_choice == 'q' or perm_choice == 'error': tool.reply("已取消") return if perm_choice == '1': device_config['admin_only'] = True device_config['allowed_users'] = [] save_device(device_id, device_config) tool.reply("✅ 权限已设置为:仅管理员可用") elif perm_choice == '2': device_config['admin_only'] = False device_config['allowed_users'] = [] save_device(device_id, device_config) tool.reply("✅ 权限已设置为:所有用户可用") elif perm_choice == '3': tool.reply("请输入允许访问的用户ID(多个用户用逗号分隔,q:退出)") users_input = sender.listen(60000) if not users_input or users_input == 'q' or users_input == 'error': tool.reply("已取消") return allowed_users = [u.strip() for u in users_input.split(',') if u.strip()] device_config['admin_only'] = False device_config['allowed_users'] = allowed_users save_device(device_id, device_config) tool.reply(f"✅ 权限已设置为:指定用户可用 ({', '.join(allowed_users)})") else: tool.reply("❌ 无效选择") else: tool.reply("❌ 无效选择") except Exception as e: tool.reply(f"❌ 操作出错: {e}") def delete_device_by_number(tool: TOOL, device_list: list, device_index: int): """按序号删除设备""" selected_device = device_list[device_index] device_id = selected_device['id'] device_name = selected_device['name'] # 确认删除 tool.reply(f"确认删除设备 '{device_name}' 吗?(y/n)") try: confirm = sender.listen(30000) if confirm and confirm.lower() in ['y', 'yes', '是']: delete_device(device_id) tool.reply(f"✅ 设备 '{device_name}' 已删除") else: tool.reply("已取消删除") except Exception as e: tool.reply(f"❌ 删除设备时出错: {e}") def _safe_get_message(): try: return sender.getMessage().strip('"') except Exception: return "" if __name__ == "__main__": tool = TOOL() msg = _safe_get_message() # 获取用户信息 try: user_id = sender.getUserID() is_admin = sender.isAdmin() except Exception: user_id = "" is_admin = False if "rb-now" in msg: cmd_rb_now(tool, user_id, is_admin) sys.exit(0) if "rb管理" in msg: cmd_rb_manage(tool, user_id, is_admin) sys.exit(0) sender.reply("指令不支持,请发送:rb-now 或 rb管理")