Compare commits

..

6 Commits

Author SHA1 Message Date
b32f9b9ed9 bugfix (wow this code is trash 2026-06-18 15:52:21 -04:00
245ba2980d add a hole punch test server 2026-05-24 17:27:25 -04:00
fdef38618b a 2026-05-15 16:31:39 -04:00
faa9e09af4 what 2026-05-15 16:29:49 -04:00
7b1a10cb71 what 2026-05-15 16:29:48 -04:00
ccb9c18544 sutff 2026-05-15 16:27:58 -04:00
10 changed files with 85 additions and 92 deletions

6
.gitignore vendored
View File

@@ -1,2 +1,8 @@
chats.json
config.json
.vscode/
__pycache__/
.venv/
build/
dist/
p2pchat.spec

View File

@@ -1,8 +0,0 @@
{
"python.languageServer": "Pylance",
"python.analysis.diagnosticSeverityOverrides": {
"reportMissingModuleSource": "none",
"reportShadowedImports": "none"
},
"circuitpython.board.version": null
}

24
LICENCE Normal file
View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

View File

@@ -1 +1,3 @@
hi
if u want to run this then download the repo, have uv python setup and run uv run main.py
its a udp hole punching secure messaging app
that i dont wanna make a readme for rn

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,16 @@
import socket
SERVER_PORT = 41234
SERVER_SEND_PORT = 41235
sock_recv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock_recv.bind(("0.0.0.0", SERVER_PORT))
sock_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock_send.bind(("0.0.0.0", SERVER_SEND_PORT))
print("Running...")
while True:
data, addr = sock_recv.recvfrom(1024)
print(f"Recieved test from addr {addr}")
sock_send.sendto(data, addr)

View File

@@ -1,4 +0,0 @@
import os, base64, json
key = os.urandom(32)
print(base64.b64encode(key).decode())

47
main.py
View File

@@ -15,6 +15,8 @@ import ipaddress
import pyperclip
import requests
HOLE_PUNCH_TEST = ("195.201.227.193", 41234)
class SetupApp(App):
DEFAULT_CSS = """
@@ -446,12 +448,35 @@ class SettingsScreen(Screen):
id="instructions",
)
yield Button("Add Contact", id="add-contact")
with TabPane("Extras"):
yield Label(
"This button will run a test to see if this application will work correctly."
)
yield Button("Test hole punching", id="test-hole")
def test_hole_punch(self):
test_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
test_sock.bind((my_ip, 0))
test_sock.settimeout(5)
test_sock.sendto(b"TEST", HOLE_PUNCH_TEST)
try:
data, addr = test_sock.recvfrom(1024)
except socket.timeout:
self.app.call_from_thread(self.notify, "Test failed", severity="error")
else:
self.app.call_from_thread(self.notify, "Test worked!")
test_sock.close()
def on_button_pressed(self, event):
if event.button.id == "close-btn":
self.dismiss()
elif event.button.id == "add-contact":
self.app.push_screen(ChoiceScreen(), self.on_screen_done)
elif event.button.id == "test-hole":
self.notify("Test started....", severity="information")
threading.Thread(target=self.test_hole_punch).start()
elif event.button.id == "profile-confirm":
valid_ip = True
valid_port = True
@@ -762,23 +787,21 @@ class ChatApp(App):
self.sock.settimeout(None)
self.connected = True
if self.other_name != chat["name"]:
with open("chats.json", "r") as f:
current_chats_config = json.load(f)
# with open("chats.json", "r") as f:
# current_chats_config = json.load(f)
self.call_from_thread(
self.add_message,
"system: other user has new name, updating... (only you can see this)",
)
for file_config in current_chats_config:
for local_config in self.chats:
if (
file_config["key"] == local_config["key"]
and file_config["ip"] == local_config["ip"]
):
file_config["name"] = self.other_name
local_config["name"] = self.other_name
break
for local_config in self.chats:
if (
local_config["key"] == chat["key"]
and local_config["ip"] == chat["ip"]
):
local_config["name"] = self.other_name
break
with open("chats.json", "w") as f:
json.dump(current_chats_config, f)
json.dump(self.chats, f)
index = self.chats.index(local_config)
self.call_from_thread(
lambda: list(self.query_one("#contacts").query("ListItem"))[index]

View File

@@ -1,66 +0,0 @@
from textual.app import App, ComposeResult
from textual.widgets import Input, RichLog, ListItem, ListView, Label, Static, Button
from textual.containers import Horizontal, Vertical, ScrollableContainer
class ChatApp(App):
DEFAULT_CSS = """
#contacts {
width: 20;
}
#messages {
height: 1fr;
padding: 0 0 0 1;
background: $background;
}
#input-row {
height: auto;
}
#input-row > Input {
width: 1fr;
}
#input-row > Button {
width: 10;
margin-right: 1;
}
"""
def send(self):
input_box = self.query_one("#input-box", Input)
message = input_box.value
if message.strip():
self.query_one("#messages").mount(ListItem(Label(message)))
input_box.clear()
def compose(self) -> ComposeResult:
with Horizontal():
yield ListView(
ListItem(Label("One")),
ListItem(Label("Two")),
ListItem(Label("Three")),
id="contacts",
)
with Vertical():
yield ListView(id="messages")
with Horizontal(id="input-row"):
yield Input(placeholder="Message...", id="input-box")
yield Button("Send", id="send-btn", disabled=True)
def on_input_submitted(self, event: Input.Submitted) -> None:
if event.input.id == "input-box":
self.send()
def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "send-btn":
self.send()
def on_input_changed(self, event: Input.Changed) -> None:
if event.input.id == "input-box":
self.query_one("#send-btn", Button).disabled = not event.value.strip()
def on_mount(self) -> None:
self.query_one(Input).focus()
print(self.query_one(Button))
ChatApp().run()