-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
156 lines (130 loc) · 5.24 KB
/
main.py
File metadata and controls
156 lines (130 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import socket
import threading
import json
import time
import hashlib
import urllib.request
import random
import os
import customtkinter as ctk
# --- CONFIGURACIÓN ---
URL = "https://dubbed.onrender.com"
NODE_ID = hashlib.sha1(str(random.random()).encode()).hexdigest()[:6]
SEEN_IDS = set()
LOCAL_PEERS = set()
CACHE_FILE = "peers_cache.json"
def setup_udp():
for p in range(5000, 5010):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.bind(("0.0.0.0", p))
return s, p
except: continue
return None, None
sock, port = setup_udp()
def http_req(path, data=None):
try:
req = urllib.request.Request(
f"{URL}{path}",
data=json.dumps(data).encode() if data else None,
headers={'Content-Type': 'application/json'},
method='POST' if data else 'GET'
)
with urllib.request.urlopen(req, timeout=5) as r:
return json.loads(r.read().decode())
except: return None
# --- APP INTERFAZ ---
class ChatApp(ctk.CTk):
def __init__(self):
super().__init__()
self.title(f"P2P Relay Chat - ID: {NODE_ID}")
self.geometry("500x600")
ctk.set_appearance_mode("dark")
# Layout
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(0, weight=1)
# Chat Textbox
self.chat_display = ctk.CTkTextbox(self, state="disabled", wrap="word")
self.chat_display.grid(row=0, column=0, padx=20, pady=20, sticky="nsew")
# Input Frame
self.input_frame = ctk.CTkFrame(self, fg_color="transparent")
self.input_frame.grid(row=1, column=0, padx=20, pady=(0, 20), sticky="ew")
self.input_frame.grid_columnconfigure(0, weight=1)
self.entry = ctk.CTkEntry(self.input_frame, placeholder_text="Escribe un mensaje...")
self.entry.grid(row=0, column=0, padx=(0, 10), sticky="ew")
self.entry.bind("<Return>", lambda e: self.send_message())
self.send_button = ctk.CTkButton(self.input_frame, text="Enviar", width=80, command=self.send_message)
self.send_button.grid(row=0, column=1)
# Cargar caché y lanzar hilos
self.load_cache()
threading.Thread(target=self.fetch_loop, daemon=True).start()
threading.Thread(target=self.udp_listener, daemon=True).start()
def append_text(self, user, msg, prefix=""):
self.chat_display.configure(state="normal")
tag = f"[{prefix}{user}]: "
self.chat_display.insert("end", f"{tag}{msg}\n")
self.chat_display.configure(state="disabled")
self.chat_display.see("end")
def send_message(self):
txt = self.entry.get().strip()
if not txt: return
self.entry.delete(0, "end")
m_id = hashlib.sha1(f"{txt}{time.time()}".encode()).hexdigest()
payload = {"id": m_id, "user": NODE_ID, "msg": txt}
SEEN_IDS.add(m_id)
self.append_text("Yo", txt)
# Enviar vía HTTP (Relay)
threading.Thread(target=lambda: http_req("/send", payload), daemon=True).start()
# Enviar vía UDP (P2P Local + Caché)
if sock:
msg_data = json.dumps(payload).encode()
threading.Thread(target=self.udp_broadcast, args=(msg_data,), daemon=True).start()
def udp_broadcast(self, data):
# Local
sock.sendto(data, ("255.255.255.255", 5000))
# Directo a peers conocidos en caché
for p in list(LOCAL_PEERS):
try:
ip, pt = p.split(":")
sock.sendto(data, (ip, int(pt)))
except: pass
def fetch_loop(self):
while True:
# Mensajes
res = http_req("/get")
if res and "messages" in res:
for m in res["messages"]:
if m["id"] not in SEEN_IDS:
SEEN_IDS.add(m["id"])
self.after(0, self.append_text, m["user"], m["msg"])
# Registro y actualización de Peers
reg = http_req("/join", {"port": port})
if reg and "peers" in reg:
for p in reg["peers"]:
if p not in LOCAL_PEERS:
LOCAL_PEERS.add(p)
self.save_cache()
time.sleep(2.5)
def udp_listener(self):
if not sock: return
while True:
try:
data, addr = sock.recvfrom(4096)
m = json.loads(data.decode())
if m["id"] not in SEEN_IDS:
SEEN_IDS.add(m["id"])
self.after(0, self.append_text, m["user"], m["msg"], "P2P ")
except: pass
def save_cache(self):
with open(CACHE_FILE, "w") as f:
json.dump(list(LOCAL_PEERS), f)
def load_cache(self):
if os.path.exists(CACHE_FILE):
try:
with open(CACHE_FILE, "r") as f:
for p in json.load(f): LOCAL_PEERS.add(p)
except: pass
if __name__ == "__main__":
app = ChatApp()
app.mainloop()