dm: прямой обмен сообщениями (DM) + soundtouch + архитектура звонков

- src/dm/: dm_core/dm_crypto/dm_mailbox — прямой обмен сообщениями (DM)
- lib/soundtouch-master/: vendored SoundTouch (изменение темпа/высоты аудио)
- doc/dm_arch.md, doc/chat_call_arch.md: архитектура DM и звонков
- tests: test_dm.c, test_dm_e2e.c
- chat: рефакторинг chat_* модулей, member_sync, invite_build
- chatgui/chatgui-android: синхронизация с новым API чата
- lib/u_async, debug_config, utun_instance: сопутствующие изменения
This commit is contained in:
evgeny committed 2026-09-15 21:09:17 +03:00
1 parent 761f64cf76
commit 73fe99b33f
123 files changed
+14095 -1270

No files matched your search

+30
View File
@@ -21,6 +21,10 @@
# create <name> create new channel
# invite_to <ch_id> <node_id> <pubkey_hex> <ip:port> [proto]
# invite a node to add our channel
# dm_start <node_id> [ch_id] start direct chat (DM) with a group member
# dm_list list DM conversations
# dm_messages <conv_id> [count] read DM messages (decrypted)
# dm_send <conv_id> <text> send DM text message
# listen interactive event listener
import sys, os, json, socket, struct
@@ -126,6 +130,24 @@ def cmd_invite_to(ch_id, node_id, pubkey, addr, proto=None):
params["proto"] = int(proto)
_req("invite_to", **params)
def cmd_dm_start(node_id, ch=None):
params = {"node_id": node_id}
if ch:
params["ch"] = ch
_req("dm_start", **params)
def cmd_dm_list():
_req("dm_list")
def cmd_dm_messages(conv_id, count=None):
params = {"conv_id": conv_id}
if count:
params["count"] = int(count)
_req("dm_messages", **params)
def cmd_dm_send(conv_id, text):
_req("dm_send", conv_id=conv_id, data=text)
def cmd_listen():
print(f"Listening on {DEFAULT_HOST}:{DEFAULT_PORT} (Ctrl+C to quit)")
s = _connect()
@@ -149,6 +171,10 @@ def cmd_listen():
print(f"[{obj.get('ch','?')}] channel updated")
elif evt == "invite_received":
print(f"INVITE: ch={obj.get('ch','?')} name={obj.get('ch_name','?')} from={obj.get('from_id','?')}")
elif evt == "dm_msg":
print(f"DM[{obj.get('conv','?')}] from={obj.get('author_id','?')}: new message")
elif evt == "dm_conv":
print(f"DM[{obj.get('conv','?')}] conversation updated")
else:
print(json.dumps(obj, indent=2, ensure_ascii=False))
except KeyboardInterrupt:
@@ -191,6 +217,10 @@ def main():
elif cmd in ("connect","join"): cmd_connect(*args[1:2] if len(args) > 1 else (_die("usage: connect <utun://...>"),))
elif cmd == "create": cmd_create(*args[1:2] if len(args) > 1 else (_die("usage: create <name>"),))
elif cmd == "invite_to": cmd_invite_to(*args[1:6] if len(args) > 4 else (_die("usage: invite_to <ch_id> <node_id> <pubkey_hex> <ip:port> [proto]"),))
elif cmd == "dm_start": cmd_dm_start(*args[1:3] if len(args) > 1 else (_die("usage: dm_start <node_id> [ch_id]"),))
elif cmd == "dm_list": cmd_dm_list()
elif cmd in ("dm_messages","dm_msgs"): cmd_dm_messages(*args[1:3] if len(args) > 1 else (_die("usage: dm_messages <conv_id> [count]"),))
elif cmd == "dm_send": cmd_dm_send(*args[1:3] if len(args) > 2 else (_die("usage: dm_send <conv_id> <text>"),))
elif cmd == "listen": cmd_listen()
else: print(f"Unknown command: {cmd}\nUse --help for usage", file=sys.stderr); sys.exit(1)
except socket.timeout: