主播工具.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. import sys
  2. import json
  3. import threading
  4. from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QHBoxLayout,
  5. QVBoxLayout, QListWidget, QListWidgetItem, QCheckBox,
  6. QPushButton, QLineEdit, QTextEdit, QGroupBox,
  7. QSplitter, QLabel, QMessageBox, QComboBox, QShortcut)
  8. from PyQt5.QtCore import Qt, QTimer, pyqtSignal, QObject
  9. from PyQt5.QtGui import QKeySequence
  10. import websocket
  11. import time
  12. class WebSocketClient(QObject):
  13. """WebSocket客户端,用于与服务器通信"""
  14. user_list_updated = pyqtSignal(list) # 用户列表更新信号
  15. message_received = pyqtSignal(dict) # 接收消息信号
  16. connection_status = pyqtSignal(bool) # 连接状态信号
  17. def __init__(self, type_str="抖音自动评论-153"):
  18. super().__init__()
  19. self.type_str = type_str
  20. self.ws = None
  21. self.ws_user_info = None
  22. self.is_connected = False
  23. self.running = True
  24. self.is_admin = True
  25. self.user_id = None
  26. self.ws_thread = None
  27. def start(self):
  28. """启动WebSocket连接"""
  29. self.running = True
  30. self.ws_thread = threading.Thread(target=self._connect_websocket, daemon=True)
  31. self.ws_thread.start()
  32. def stop(self):
  33. """停止WebSocket连接"""
  34. self.running = False
  35. if self.ws:
  36. try:
  37. self.ws.close()
  38. except:
  39. pass
  40. def _connect_websocket(self):
  41. """WebSocket连接逻辑(在独立线程中运行)"""
  42. time_connect = 0
  43. def reconnect():
  44. nonlocal time_connect
  45. while self.running:
  46. try:
  47. time_connect += 1
  48. print(f"第{time_connect}次重连")
  49. # 创建WebSocket连接
  50. self.ws = websocket.WebSocketApp(
  51. 'wss://ws.lamp.run',
  52. on_open=self._on_open,
  53. on_message=self._on_message,
  54. on_error=self._on_error,
  55. on_close=self._on_close
  56. )
  57. # 运行WebSocket
  58. self.ws.run_forever()
  59. # 如果连接断开且还在运行中,等待后重连
  60. if self.running:
  61. time.sleep(3)
  62. except Exception as e:
  63. print(f"WebSocket错误: {e}")
  64. if self.running:
  65. time.sleep(3)
  66. # 启动连接
  67. reconnect()
  68. def _on_open(self, ws):
  69. """WebSocket打开回调"""
  70. login_data = {
  71. "route": "login",
  72. "type": self.type_str,
  73. "admin": self.is_admin,
  74. "id": "PYQT_CLIENT"
  75. }
  76. ws.send(json.dumps(login_data))
  77. self.is_connected = True
  78. self.connection_status.emit(True)
  79. print("WebSocket连接成功")
  80. # 延迟一下,等登录完成后再获取列表
  81. def get_list():
  82. time.sleep(0.5)
  83. if self.ws and self.is_connected:
  84. self.send("getList")
  85. threading.Thread(target=get_list, daemon=True).start()
  86. def _on_message(self, ws, message):
  87. """接收消息回调"""
  88. try:
  89. data = json.loads(message)
  90. if data.get('type') == 'userInfo':
  91. self.ws_user_info = data.get('value')
  92. if self.ws_user_info:
  93. self.user_id = self.ws_user_info.get('userID')
  94. print(f"用户信息: {data.get('value')}")
  95. elif data.get('type') == 'getList':
  96. # 接收到用户列表
  97. user_list = data.get('value', [])
  98. # 提取用户ID列表
  99. user_ids = []
  100. for user in user_list:
  101. user_id = user.get('id')
  102. if user_id and user_id != 'PYQT_CLIENT': # 排除自己
  103. user_ids.append(user_id)
  104. # 发送信号更新UI
  105. self.user_list_updated.emit(user_ids)
  106. print(f"收到用户列表: {user_ids}")
  107. else:
  108. # 其他消息
  109. self.message_received.emit(data)
  110. except json.JSONDecodeError as e:
  111. print(f"JSON解析错误: {e}")
  112. except Exception as e:
  113. print(f"消息处理错误: {e}")
  114. def _on_error(self, ws, error):
  115. """WebSocket错误回调"""
  116. print(f"WebSocket错误: {error}")
  117. self.is_connected = False
  118. self.connection_status.emit(False)
  119. def _on_close(self, ws, close_status_code, close_msg):
  120. """WebSocket关闭回调"""
  121. print("WebSocket连接关闭")
  122. self.is_connected = False
  123. self.connection_status.emit(False)
  124. def send(self, route, value=None, send_to=None):
  125. """发送消息
  126. Args:
  127. route: 路由名称,如 "sendmessage"
  128. value: 要发送的内容(话术)
  129. send_to: 接收方的userID
  130. """
  131. if not self.ws or not self.is_connected:
  132. print("WebSocket未连接")
  133. return False
  134. try:
  135. message = {
  136. "route": route,
  137. "type": self.type_str,
  138. }
  139. # 添加userID(发送者的ID)
  140. if self.user_id:
  141. message["userID"] = self.user_id
  142. # 添加接收者ID
  143. if send_to:
  144. message["id"] = "抖音自动评论-153-" + send_to
  145. # 添加内容
  146. if value is not None:
  147. message["value"] = value
  148. # 发送消息
  149. self.ws.send(json.dumps(message))
  150. print(f"发送消息: {message}")
  151. return True
  152. except Exception as e:
  153. print(f"发送消息失败: {e}")
  154. return False
  155. def get_user_list(self):
  156. """主动获取用户列表"""
  157. if self.is_connected:
  158. return self.send("getList")
  159. return False
  160. class MainWindow(QMainWindow):
  161. def __init__(self):
  162. super().__init__()
  163. self.setWindowTitle("三栏布局 - 用户独立话术管理 (快捷键F1-F5)")
  164. self.setGeometry(100, 100, 1300, 750)
  165. # 数据存储:用户话术字典 {用户名: [话术列表]}
  166. self.user_scripts = {}
  167. # 当前选中的用户
  168. self.current_user = None
  169. # WebSocket客户端
  170. self.ws_client = None
  171. # 用户列表缓存
  172. self.user_cache = []
  173. # 中央部件
  174. central_widget = QWidget()
  175. self.setCentralWidget(central_widget)
  176. # 主布局 - 水平分割器
  177. main_splitter = QSplitter(Qt.Horizontal)
  178. central_layout = QHBoxLayout(central_widget)
  179. central_layout.addWidget(main_splitter)
  180. central_layout.setContentsMargins(10, 10, 10, 10)
  181. # ---------- 左侧:日志显示 ----------
  182. left_widget = QWidget()
  183. left_layout = QVBoxLayout(left_widget)
  184. left_layout.setContentsMargins(5, 5, 5, 5)
  185. log_label = QLabel("📋 运行日志")
  186. log_label.setStyleSheet("font-weight: bold; font-size: 14px;")
  187. left_layout.addWidget(log_label)
  188. self.log_text = QTextEdit()
  189. self.log_text.setReadOnly(True)
  190. self.log_text.setPlaceholderText("日志信息将显示在这里...")
  191. self.log_text.append("[系统] 程序启动")
  192. self.log_text.append("[系统] 界面初始化完成")
  193. self.log_text.append("[系统] 快捷键: F1-F5 快速切换用户")
  194. left_layout.addWidget(self.log_text)
  195. log_btn_layout = QHBoxLayout()
  196. clear_log_btn = QPushButton("清空日志")
  197. clear_log_btn.clicked.connect(self.clear_log)
  198. log_btn_layout.addStretch()
  199. log_btn_layout.addWidget(clear_log_btn)
  200. left_layout.addLayout(log_btn_layout)
  201. # ---------- 中间:在线用户列表 ----------
  202. middle_widget = QWidget()
  203. middle_layout = QVBoxLayout(middle_widget)
  204. middle_layout.setContentsMargins(5, 5, 5, 5)
  205. user_label = QLabel("👥 在线用户列表")
  206. user_label.setStyleSheet("font-weight: bold; font-size: 14px;")
  207. middle_layout.addWidget(user_label)
  208. # 连接状态和控制按钮
  209. control_layout = QHBoxLayout()
  210. self.status_label = QLabel("⚪ 未连接")
  211. self.status_label.setStyleSheet("color: red;")
  212. control_layout.addWidget(self.status_label)
  213. control_layout.addStretch()
  214. self.refresh_btn = QPushButton("🔄 刷新列表")
  215. self.refresh_btn.clicked.connect(self.refresh_user_list)
  216. self.refresh_btn.setEnabled(False)
  217. control_layout.addWidget(self.refresh_btn)
  218. middle_layout.addLayout(control_layout)
  219. select_btn_layout = QHBoxLayout()
  220. self.select_all_btn = QPushButton("✅ 全选")
  221. self.deselect_all_btn = QPushButton("❌ 取消全选")
  222. self.select_all_btn.clicked.connect(self.select_all_users)
  223. self.deselect_all_btn.clicked.connect(self.deselect_all_users)
  224. select_btn_layout.addWidget(self.select_all_btn)
  225. select_btn_layout.addWidget(self.deselect_all_btn)
  226. select_btn_layout.addStretch()
  227. middle_layout.addLayout(select_btn_layout)
  228. self.user_list_widget = QListWidget()
  229. self.user_list_widget.setSelectionMode(QListWidget.NoSelection)
  230. middle_layout.addWidget(self.user_list_widget)
  231. self.user_count_label = QLabel("当前在线人数: 0")
  232. self.user_count_label.setStyleSheet("color: gray;")
  233. middle_layout.addWidget(self.user_count_label)
  234. # ---------- 右侧:用户独立话术列表 ----------
  235. right_widget = QWidget()
  236. right_layout = QVBoxLayout(right_widget)
  237. right_layout.setContentsMargins(5, 5, 5, 5)
  238. # 顶部:用户选择下拉框
  239. user_select_layout = QHBoxLayout()
  240. user_select_layout.addWidget(QLabel("选择用户 (快捷键F1-F5):"))
  241. self.user_combo = QComboBox()
  242. self.user_combo.currentTextChanged.connect(self.on_user_changed)
  243. user_select_layout.addWidget(self.user_combo)
  244. user_select_layout.addStretch()
  245. right_layout.addLayout(user_select_layout)
  246. # 话术列表标题
  247. script_label = QLabel("📝 话术列表 (当前用户)")
  248. script_label.setStyleSheet("font-weight: bold; font-size: 14px;")
  249. right_layout.addWidget(script_label)
  250. # 话术列表显示
  251. self.script_list_widget = QListWidget()
  252. right_layout.addWidget(self.script_list_widget)
  253. # 话术编辑区域
  254. script_edit_layout = QVBoxLayout()
  255. # 添加话术
  256. add_layout = QHBoxLayout()
  257. self.script_input = QLineEdit()
  258. self.script_input.setPlaceholderText("输入新话术内容...")
  259. add_btn = QPushButton("➕ 添加")
  260. add_btn.clicked.connect(self.add_script)
  261. add_layout.addWidget(self.script_input)
  262. add_layout.addWidget(add_btn)
  263. script_edit_layout.addLayout(add_layout)
  264. # 删除和修改按钮
  265. modify_layout = QHBoxLayout()
  266. delete_btn = QPushButton("🗑️ 删除选中")
  267. delete_btn.clicked.connect(self.delete_script)
  268. modify_btn = QPushButton("✏️ 修改选中")
  269. modify_btn.clicked.connect(self.modify_script)
  270. modify_layout.addWidget(delete_btn)
  271. modify_layout.addWidget(modify_btn)
  272. script_edit_layout.addLayout(modify_layout)
  273. # 保存按钮
  274. save_layout = QHBoxLayout()
  275. save_layout.addStretch()
  276. self.save_scripts_btn = QPushButton("💾 保存话术配置")
  277. self.save_scripts_btn.setStyleSheet("background-color: #2196F3; color: white; font-weight: bold; padding: 8px 20px; border-radius: 5px;")
  278. self.save_scripts_btn.clicked.connect(self.save_user_scripts)
  279. save_layout.addWidget(self.save_scripts_btn)
  280. script_edit_layout.addLayout(save_layout)
  281. right_layout.addLayout(script_edit_layout)
  282. # 发送任务按钮
  283. send_layout = QHBoxLayout()
  284. send_layout.addStretch()
  285. self.send_task_btn = QPushButton("🚀 发送任务")
  286. self.send_task_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding: 8px 20px; border-radius: 5px;")
  287. self.send_task_btn.clicked.connect(self.send_task)
  288. send_layout.addWidget(self.send_task_btn)
  289. right_layout.addLayout(send_layout)
  290. # ---------- 将三个部件加入分割器 ----------
  291. main_splitter.addWidget(left_widget)
  292. main_splitter.addWidget(middle_widget)
  293. main_splitter.addWidget(right_widget)
  294. main_splitter.setSizes([300, 450, 450])
  295. # ---------- 设置快捷键 F1-F5 ----------
  296. self.setup_shortcuts()
  297. # 窗口样式
  298. self.setStyleSheet("""
  299. QGroupBox {
  300. font-weight: bold;
  301. border: 1px solid #ccc;
  302. border-radius: 5px;
  303. margin-top: 10px;
  304. }
  305. QGroupBox::title {
  306. subcontrol-origin: margin;
  307. left: 10px;
  308. padding: 0 5px 0 5px;
  309. }
  310. QListWidget::item {
  311. padding: 5px;
  312. }
  313. QTextEdit {
  314. background-color: #f8f8f8;
  315. font-family: Consolas, monospace;
  316. }
  317. QComboBox {
  318. padding: 5px;
  319. min-width: 150px;
  320. }
  321. """)
  322. # 连接WebSocket
  323. self.connect_websocket()
  324. # ---------- WebSocket连接管理 ----------
  325. def connect_websocket(self):
  326. """连接到WebSocket服务器"""
  327. self.log_text.append("[系统] 正在连接WebSocket服务器...")
  328. self.status_label.setText("🔄 正在连接...")
  329. self.status_label.setStyleSheet("color: orange;")
  330. # 创建WebSocket客户端
  331. self.ws_client = WebSocketClient("抖音自动评论-153")
  332. # 连接信号
  333. self.ws_client.user_list_updated.connect(self.on_user_list_updated)
  334. self.ws_client.message_received.connect(self.on_ws_message)
  335. self.ws_client.connection_status.connect(self.on_connection_status)
  336. # 启动WebSocket
  337. self.ws_client.start()
  338. # 定时器检查连接状态
  339. self.status_timer = QTimer()
  340. self.status_timer.timeout.connect(self.check_connection)
  341. self.status_timer.start(5000) # 每5秒检查一次
  342. def on_connection_status(self, connected):
  343. """WebSocket连接状态变化"""
  344. if connected:
  345. self.status_label.setText("🟢 已连接")
  346. self.status_label.setStyleSheet("color: green;")
  347. self.refresh_btn.setEnabled(True)
  348. self.log_text.append("[系统] WebSocket连接成功")
  349. # 延迟获取用户列表
  350. QTimer.singleShot(1000, self.refresh_user_list)
  351. else:
  352. self.status_label.setText("🔴 已断开")
  353. self.status_label.setStyleSheet("color: red;")
  354. self.refresh_btn.setEnabled(False)
  355. self.log_text.append("[系统] WebSocket连接断开")
  356. def check_connection(self):
  357. """检查WebSocket连接状态"""
  358. if self.ws_client:
  359. if not self.ws_client.is_connected:
  360. # 尝试重新获取列表
  361. self.ws_client.get_user_list()
  362. def refresh_user_list(self):
  363. """刷新用户列表"""
  364. if self.ws_client and self.ws_client.is_connected:
  365. self.log_text.append("[系统] 刷新用户列表...")
  366. self.ws_client.get_user_list()
  367. else:
  368. self.log_text.append("[系统] WebSocket未连接,无法刷新列表")
  369. def on_user_list_updated(self, user_list):
  370. """用户列表更新"""
  371. self.user_cache = user_list
  372. self.update_user_list(user_list)
  373. self.log_text.append(f"[系统] 更新用户列表,当前在线: {len(user_list)}人")
  374. def on_ws_message(self, message):
  375. """WebSocket消息接收"""
  376. # 处理其他类型的消息
  377. msg_type = message.get('type')
  378. if msg_type:
  379. self.log_text.append(f"[WebSocket] 收到消息: {msg_type}")
  380. # ---------- 用户管理 ----------
  381. def update_user_list(self, user_list):
  382. """更新用户列表UI"""
  383. # 保存当前选中的用户
  384. selected_users = self.get_selected_users()
  385. current_combo_text = self.user_combo.currentText()
  386. # 清空列表
  387. self.user_list_widget.clear()
  388. # 添加用户
  389. for user_id in user_list:
  390. self.add_user_item(user_id)
  391. # 如果用户不存在,初始化话术数据
  392. if user_id not in self.user_scripts:
  393. self.user_scripts[user_id] = [f"{user_id},666"]
  394. # 更新下拉框
  395. self.user_combo.clear()
  396. self.user_combo.addItems(user_list)
  397. # 恢复选中状态
  398. if current_combo_text in user_list:
  399. self.user_combo.setCurrentText(current_combo_text)
  400. elif user_list:
  401. self.user_combo.setCurrentIndex(0)
  402. # 恢复复选框选中状态
  403. for user in selected_users:
  404. if user in user_list:
  405. for i in range(self.user_list_widget.count()):
  406. item = self.user_list_widget.item(i)
  407. widget = self.user_list_widget.itemWidget(item)
  408. if isinstance(widget, QCheckBox) and widget.text() == user:
  409. widget.setChecked(True)
  410. break
  411. # 更新人数
  412. self.user_count_label.setText(f"当前在线人数: {len(user_list)}")
  413. def add_user_item(self, username):
  414. """添加一个带复选框的用户项"""
  415. item = QListWidgetItem()
  416. checkbox = QCheckBox(username)
  417. self.user_list_widget.addItem(item)
  418. self.user_list_widget.setItemWidget(item, checkbox)
  419. checkbox.stateChanged.connect(self.on_user_check_changed)
  420. def get_user_checkboxes(self):
  421. """获取所有用户复选框"""
  422. checkboxes = []
  423. for i in range(self.user_list_widget.count()):
  424. item = self.user_list_widget.item(i)
  425. widget = self.user_list_widget.itemWidget(item)
  426. if isinstance(widget, QCheckBox):
  427. checkboxes.append(widget)
  428. return checkboxes
  429. def get_selected_users(self):
  430. """获取所有选中的用户"""
  431. selected = []
  432. for cb in self.get_user_checkboxes():
  433. if cb.isChecked():
  434. selected.append(cb.text())
  435. return selected
  436. def select_all_users(self):
  437. for cb in self.get_user_checkboxes():
  438. cb.setChecked(True)
  439. self.log_text.append("[操作] 已全选所有用户")
  440. def deselect_all_users(self):
  441. for cb in self.get_user_checkboxes():
  442. cb.setChecked(False)
  443. self.log_text.append("[操作] 已取消全选所有用户")
  444. def on_user_check_changed(self):
  445. """复选框状态改变时更新下拉框列表"""
  446. # 获取当前所有用户
  447. current_users = []
  448. for i in range(self.user_list_widget.count()):
  449. item = self.user_list_widget.item(i)
  450. widget = self.user_list_widget.itemWidget(item)
  451. if isinstance(widget, QCheckBox):
  452. current_users.append(widget.text())
  453. # 更新下拉框
  454. current_selection = self.user_combo.currentText()
  455. self.user_combo.clear()
  456. self.user_combo.addItems(current_users)
  457. # 如果之前的用户还在列表中,恢复选择
  458. if current_selection in current_users:
  459. self.user_combo.setCurrentText(current_selection)
  460. elif current_users:
  461. self.user_combo.setCurrentIndex(0)
  462. # ---------- 快捷键设置 ----------
  463. def setup_shortcuts(self):
  464. """设置F1-F5快捷键切换用户"""
  465. shortcuts = [
  466. (Qt.Key_F1, 0),
  467. (Qt.Key_F2, 1),
  468. (Qt.Key_F3, 2),
  469. (Qt.Key_F4, 3),
  470. (Qt.Key_F5, 4),
  471. ]
  472. for key, index in shortcuts:
  473. shortcut = QShortcut(QKeySequence(key), self)
  474. shortcut.activated.connect(lambda idx=index: self.switch_to_user_by_index(idx))
  475. def switch_to_user_by_index(self, index):
  476. """根据索引切换到对应用户"""
  477. if index < self.user_combo.count():
  478. user = self.user_combo.itemText(index)
  479. if user:
  480. self.user_combo.setCurrentText(user)
  481. self.log_text.append(f"[快捷键] 切换到用户: {user}")
  482. else:
  483. self.log_text.append(f"[快捷键] 用户索引 {index} 不存在")
  484. # ---------- 话术管理 (用户独立) ----------
  485. def load_user_scripts(self, username):
  486. """加载指定用户的话术列表"""
  487. if username in self.user_scripts:
  488. self.script_list_widget.clear()
  489. for script in self.user_scripts[username]:
  490. self.script_list_widget.addItem(script)
  491. self.current_user = username
  492. self.log_text.append(f"[系统] 加载用户 '{username}' 的话术列表")
  493. def on_user_changed(self, username):
  494. """下拉框切换用户"""
  495. if username and username != self.current_user:
  496. # 如果当前用户有修改但未保存,先保存
  497. if self.current_user and self.current_user in self.user_scripts:
  498. self.save_current_user_scripts()
  499. self.load_user_scripts(username)
  500. def save_current_user_scripts(self):
  501. """保存当前用户的话术到内存"""
  502. if self.current_user and self.current_user in self.user_scripts:
  503. scripts = []
  504. for i in range(self.script_list_widget.count()):
  505. scripts.append(self.script_list_widget.item(i).text())
  506. self.user_scripts[self.current_user] = scripts
  507. self.log_text.append(f"[系统] 自动保存用户 '{self.current_user}' 的话术配置")
  508. def save_user_scripts(self):
  509. """手动保存当前用户的话术(并持久化)"""
  510. if not self.current_user:
  511. QMessageBox.warning(self, "提示", "请先选择一个用户!")
  512. return
  513. # 保存到内存
  514. self.save_current_user_scripts()
  515. # 持久化保存到文件
  516. try:
  517. with open("user_scripts_backup.json", "w", encoding="utf-8") as f:
  518. json.dump(self.user_scripts, f, ensure_ascii=False, indent=2)
  519. self.log_text.append(f"[系统] 话术配置已保存到文件 user_scripts_backup.json")
  520. QMessageBox.information(self, "保存成功", f"用户 '{self.current_user}' 的话术配置已保存!")
  521. except Exception as e:
  522. QMessageBox.critical(self, "保存失败", f"保存到文件时出错: {str(e)}")
  523. def add_script(self):
  524. """添加话术到当前用户"""
  525. text = self.script_input.text().strip()
  526. if text:
  527. self.script_list_widget.addItem(text)
  528. self.script_input.clear()
  529. self.save_current_user_scripts()
  530. self.log_text.append(f"[操作] 为用户 '{self.current_user}' 添加话术: {text}")
  531. else:
  532. QMessageBox.information(self, "提示", "话术内容不能为空!")
  533. def delete_script(self):
  534. """删除当前用户选中的话术"""
  535. current_row = self.script_list_widget.currentRow()
  536. if current_row >= 0:
  537. item_text = self.script_list_widget.item(current_row).text()
  538. self.script_list_widget.takeItem(current_row)
  539. self.save_current_user_scripts()
  540. self.log_text.append(f"[操作] 删除用户 '{self.current_user}' 的话术: {item_text}")
  541. else:
  542. QMessageBox.information(self, "提示", "请先选中一条话术!")
  543. def modify_script(self):
  544. """修改当前用户选中的话术"""
  545. current_row = self.script_list_widget.currentRow()
  546. if current_row >= 0:
  547. old_text = self.script_list_widget.item(current_row).text()
  548. new_text, ok = QMessageBox.getText(self, "修改话术", "请输入新的话术内容:",
  549. text=old_text)
  550. if ok and new_text.strip():
  551. self.script_list_widget.item(current_row).setText(new_text.strip())
  552. self.save_current_user_scripts()
  553. self.log_text.append(f"[操作] 修改用户 '{self.current_user}' 的话术: {old_text} -> {new_text.strip()}")
  554. elif ok and not new_text.strip():
  555. QMessageBox.information(self, "提示", "话术内容不能为空!")
  556. else:
  557. QMessageBox.information(self, "提示", "请先选中一条话术!")
  558. def clear_log(self):
  559. self.log_text.clear()
  560. self.log_text.append("[系统] 日志已清空")
  561. self.log_text.append("[系统] 快捷键: F1-F5 快速切换用户")
  562. # ---------- 发送任务 ----------
  563. def send_task(self):
  564. """发送任务到选中的用户"""
  565. selected_users = self.get_selected_users()
  566. if not selected_users:
  567. QMessageBox.warning(self, "提示", "请至少选择一个用户!")
  568. return
  569. # 获取当前选中的话术
  570. current_row = self.script_list_widget.currentRow()
  571. if current_row < 0:
  572. QMessageBox.warning(self, "提示", "请先在话术列表中选中一条话术!")
  573. return
  574. selected_script = self.script_list_widget.item(current_row).text()
  575. # 记录日志
  576. self.log_text.append(f"[任务] 发送话术: {selected_script}")
  577. self.log_text.append(f"[任务] 发送给 {len(selected_users)} 个用户:")
  578. # 通过WebSocket发送任务
  579. if self.ws_client and self.ws_client.is_connected:
  580. success_count = 0
  581. for user in selected_users:
  582. # 发送给指定用户,路由为 "sendmessage"
  583. if self.ws_client.send("sendmessage", selected_script, user):
  584. success_count += 1
  585. self.log_text.append(f"[任务] 已发送给用户: {user}")
  586. else:
  587. self.log_text.append(f"[任务] 发送给用户 {user} 失败")
  588. self.log_text.append(f"[任务] 发送完成,成功发送给 {success_count}/{len(selected_users)} 个用户")
  589. else:
  590. self.log_text.append("[任务] WebSocket未连接,无法发送")
  591. # ---------- 窗口关闭时保存 ----------
  592. def closeEvent(self, event):
  593. """关闭窗口时自动保存所有用户话术"""
  594. if self.current_user:
  595. self.save_current_user_scripts()
  596. try:
  597. with open("user_scripts_backup.json", "w", encoding="utf-8") as f:
  598. json.dump(self.user_scripts, f, ensure_ascii=False, indent=2)
  599. self.log_text.append("[系统] 程序退出,已自动保存所有话术配置")
  600. except:
  601. pass
  602. # 关闭WebSocket连接
  603. if self.ws_client:
  604. self.ws_client.stop()
  605. event.accept()
  606. if __name__ == "__main__":
  607. app = QApplication(sys.argv)
  608. window = MainWindow()
  609. window.show()
  610. sys.exit(app.exec_())