Skip to content

Commit 83203d8

Browse files
committed
add ws client and update build
1 parent a3b4327 commit 83203d8

2 files changed

Lines changed: 112 additions & 0 deletions

File tree

selfagent/client/ws_client.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/env python3
2+
"""
3+
WebSocket 客户端 - 接收服务器推送并弹出 Windows 通知
4+
运行: python ws_client.py
5+
"""
6+
7+
import asyncio
8+
import websockets
9+
import json
10+
from datetime import datetime
11+
12+
# Windows 通知
13+
try:
14+
from win10toast import ToastNotifier
15+
toaster = ToastNotifier()
16+
HAS_TOAST = True
17+
except ImportError:
18+
HAS_TOAST = False
19+
print("提示: pip install win10toast 可启用 Windows 通知")
20+
21+
WS_URL = "ws://111.170.6.103:10002/ws"
22+
23+
def show_notification(title, body):
24+
"""显示 Windows 通知"""
25+
print(f"\n🔔 [{datetime.now().strftime('%H:%M:%S')}] {title}: {body}")
26+
if HAS_TOAST:
27+
try:
28+
toaster.show_toast(title, body, duration=5, threaded=True)
29+
except:
30+
pass
31+
32+
async def connect():
33+
"""连接 WebSocket 并监听消息"""
34+
while True:
35+
try:
36+
print(f"正在连接 {WS_URL} ...")
37+
async with websockets.connect(WS_URL) as ws:
38+
print("✅ 已连接,等待消息...\n")
39+
async for message in ws:
40+
try:
41+
data = json.loads(message)
42+
title = data.get("title", "通知")
43+
body = data.get("body", message)
44+
except:
45+
title, body = "通知", message
46+
show_notification(title, body)
47+
except Exception as e:
48+
print(f"❌ 连接失败: {e}")
49+
print("5秒后重连...")
50+
await asyncio.sleep(5)
51+
52+
if __name__ == "__main__":
53+
print("="*40)
54+
print("WebSocket 客户端 - 接收服务器推送")
55+
print("="*40)
56+
asyncio.run(connect())

selfagent/server/ws_server.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/env python3
2+
"""
3+
WebSocket 服务器 - 向 SelfAgent App 推送消息
4+
部署到服务器后运行: python ws_server.py
5+
App 会自动连接 ws://111.170.6.103:9999/ws
6+
"""
7+
8+
import asyncio
9+
import websockets
10+
import json
11+
from datetime import datetime
12+
13+
# 存储所有连接的客户端
14+
clients = set()
15+
16+
async def handler(websocket, path):
17+
"""处理客户端连接"""
18+
clients.add(websocket)
19+
client_ip = websocket.remote_address[0]
20+
print(f"[{datetime.now().strftime('%H:%M:%S')}] 新连接: {client_ip} (在线: {len(clients)})")
21+
22+
try:
23+
async for message in websocket:
24+
print(f"[{datetime.now().strftime('%H:%M:%S')}] 收到: {message}")
25+
except websockets.exceptions.ConnectionClosed:
26+
pass
27+
finally:
28+
clients.discard(websocket)
29+
print(f"[{datetime.now().strftime('%H:%M:%S')}] 断开: {client_ip} (在线: {len(clients)})")
30+
31+
async def broadcast(message):
32+
"""向所有客户端广播消息"""
33+
if clients:
34+
msg = json.dumps({"title": "服务器通知", "body": message, "time": datetime.now().strftime('%H:%M:%S')})
35+
await asyncio.gather(*[client.send(msg) for client in clients])
36+
print(f"[{datetime.now().strftime('%H:%M:%S')}] 已推送给 {len(clients)} 个客户端")
37+
38+
async def input_loop():
39+
"""命令行输入,输入消息后推送给所有客户端"""
40+
print("\n" + "="*50)
41+
print("WebSocket 服务器已启动 - ws://0.0.0.0:9999/ws")
42+
print("输入消息后回车即可推送到所有 App")
43+
print("="*50 + "\n")
44+
45+
loop = asyncio.get_event_loop()
46+
while True:
47+
message = await loop.run_in_executor(None, input, "推送消息> ")
48+
if message.strip():
49+
await broadcast(message.strip())
50+
51+
async def main():
52+
server = await websockets.serve(handler, "0.0.0.0", 9999, path="/ws")
53+
await asyncio.gather(server.wait_closed(), input_loop())
54+
55+
if __name__ == "__main__":
56+
asyncio.run(main())

0 commit comments

Comments
 (0)