This repository has been archived on 2026-05-21. You can view files and clone it, but cannot push or open issues or pull requests.
TyChat-TUI/socket_manager.py

112 lines
4.8 KiB
Python

import socketio
import base64
import numpy as np
import threading
import sounddevice as sd
import protocol
sio = socketio.Client()
_client_instance = None
def init_network(client_instance):
"""Binds the network events to the active TUI client instance state."""
global _client_instance
_client_instance = client_instance
@sio.event
def incoming_packet(data):
if not _client_instance: return
from_uin = data["from_uin"]
payload_base64 = data["payload"]
try:
audio_bytes = base64.b64decode(payload_base64.encode('utf-8'))
audio_data = np.frombuffer(audio_bytes, dtype=np.float32)
srv_tone = protocol.detect_service_tone(audio_data)
if srv_tone:
sd.play(audio_data, protocol.SAMPLE_RATE)
if from_uin not in _client_instance.contacts:
return
if srv_tone == protocol.FREQ_QUERY:
if _client_instance.is_busy:
_client_instance.send_service_tone(from_uin, protocol.FREQ_RESP_YES)
else:
_client_instance.pending_windows.append({"type": "query", "uin": from_uin})
_client_instance.refresh_ui()
return
elif srv_tone == protocol.FREQ_ALERT:
_client_instance.pending_windows.append({"type": "alert", "uin": from_uin})
_client_instance.refresh_ui()
return
elif srv_tone == protocol.FREQ_RESP_YES:
_client_instance.add_to_history(from_uin, f"[SYSTEM]: Quick status answer -> Yes, I'm busy")
return
elif srv_tone == protocol.FREQ_RESP_NO:
_client_instance.add_to_history(from_uin, f"[SYSTEM]: Quick status answer -> No, go on")
return
fast_text = protocol.fast_decode(audio_data)
if fast_text.startswith("SYS:"):
cmd = fast_text[4:]
if cmd.startswith("ROOM_MSG:"):
parts = cmd.split(":", 3)
if len(parts) >= 4:
room_id = parts[1]
sender_uin = parts[2]
actual_msg = parts[3]
if room_id in _client_instance.groups:
_client_instance.add_to_history(room_id, f"[UIN {sender_uin}]: {actual_msg}")
return
elif cmd == "REQ_ADD":
if from_uin in _client_instance.blocklist:
_client_instance.send_sys_packet(from_uin, "RES_DEC")
elif _client_instance.is_busy:
_client_instance.send_sys_packet(from_uin, "RES_BSY")
else:
if from_uin not in _client_instance.pending_requests and from_uin not in _client_instance.contacts:
_client_instance.pending_requests.append(from_uin)
_client_instance.refresh_ui()
return
elif cmd == "RES_ACC":
if _client_instance.dialing_uin == from_uin:
_client_instance.dialing_uin = None
if from_uin not in _client_instance.contacts:
_client_instance.contacts[from_uin] = {"status": "online", "unread": 0, "attention": False}
_client_instance.history[from_uin] = ["[SYSTEM]: Handshake accepted. Contact added."]
_client_instance.save_config()
_client_instance.active_chat = from_uin
_client_instance.refresh_ui()
return
elif cmd == "RES_DEC" or cmd == "RES_BSY":
if _client_instance.dialing_uin == from_uin:
_client_instance.dialing_uin = None
return
if from_uin not in _client_instance.contacts:
return
if _client_instance.active_chat != from_uin:
_client_instance.contacts[from_uin]["unread"] += 1
threading.Thread(target=_client_instance.play_and_decode, args=(audio_bytes, from_uin), daemon=True).start()
except Exception as e:
pass
@sio.event
def online_statuses_response(data):
if not _client_instance: return
for uin, status in data.items():
if uin in _client_instance.contacts:
_client_instance.contacts[uin]["status"] = status
_client_instance.refresh_ui()
@sio.event
def error(data):
if not _client_instance: return
msg = data.get("message")
target = data.get("target_uin")
if msg == "offline" and target:
if _client_instance.dialing_uin == target:
_client_instance.dialing_uin = None
if target in _client_instance.contacts:
_client_instance.contacts[target]["status"] = "offline"
_client_instance.add_to_history(target, f"[SYSTEM]: They went offline. Outgoing stack will be preserved.")