feature pack 1

This commit is contained in:
2025-10-10 23:32:52 +00:00
parent b49955a974
commit 58770684db

View File

@@ -1,8 +1,9 @@
#!/usr/bin/env python3
"""
Desktop GUI wrapper for chatter.py using Tkinter.
Supports text messages and attachments (files) with clickable filenames.
Supports text messages and attachments with clickable filenames.
Polls server every 1.5s for updates.
Includes 24-hour toggle and timezone selection.
"""
import threading
@@ -10,6 +11,7 @@ import tkinter as tk
from tkinter import filedialog, scrolledtext, messagebox
import requests, os, time, webbrowser
from datetime import datetime
from dateutil import parser, tz
from plyer import notification
import cv2
from PIL import Image, ImageTk
@@ -20,6 +22,7 @@ SESSION = requests.Session()
LAST_MSG_IDS = set()
# ---------------- ChatApp ----------------
class ChatApp:
def __init__(self, root):
self.root = root
@@ -27,9 +30,12 @@ class ChatApp:
root.geometry("800x600")
root.minsize(500, 400)
# Settings
self.settings = {
"popup_notification": True,
"camera_notification": False,
"time_24h": False,
"timezone": time.tzname[0] # Default local timezone
}
self.username = None
@@ -88,19 +94,32 @@ class ChatApp:
def open_settings(self):
win = tk.Toplevel(self.root)
win.title("Settings")
win.geometry("300x200")
win.geometry("350x250")
win.resizable(False, False)
popup_var = tk.BooleanVar(value=self.settings["popup_notification"])
cam_var = tk.BooleanVar(value=self.settings["camera_notification"])
time24_var = tk.BooleanVar(value=self.settings.get("time_24h", False))
tz_var = tk.StringVar(value=self.settings.get("timezone", time.tzname[0]))
tk.Label(win, text="Notification Options", font=("Courier", 12, "bold")).pack(pady=10)
tk.Checkbutton(win, text="Popup Notification", variable=popup_var).pack(anchor="w", padx=20, pady=5)
tk.Checkbutton(win, text="Camera Notification", variable=cam_var).pack(anchor="w", padx=20, pady=5)
tk.Label(win, text="Notification Options", font=("Courier", 12, "bold")).pack(pady=5)
tk.Checkbutton(win, text="Popup Notification", variable=popup_var).pack(anchor="w", padx=20)
tk.Checkbutton(win, text="Camera Notification", variable=cam_var).pack(anchor="w", padx=20)
tk.Label(win, text="Time Display", font=("Courier", 12, "bold")).pack(pady=5)
tk.Checkbutton(win, text="24-Hour Clock", variable=time24_var).pack(anchor="w", padx=20)
tk.Label(win, text="Timezone", font=("Courier", 12, "bold")).pack(pady=5)
# Common timezones for dropdown
tz_options = ["UTC", "US/Eastern", "US/Central", "US/Mountain", "US/Pacific", "Europe/London", "Europe/Berlin"]
tk.OptionMenu(win, tz_var, *tz_options).pack(anchor="w", padx=20)
def save_settings():
self.settings["popup_notification"] = popup_var.get()
self.settings["camera_notification"] = cam_var.get()
self.settings["time_24h"] = time24_var.get()
self.settings["timezone"] = tz_var.get()
self.update_chat_timestamps()
messagebox.showinfo("Saved", "Settings updated.")
win.destroy()
@@ -164,7 +183,6 @@ class ChatApp:
tk.Label(popup, wrap="400", text=f"{username} sent: {file_name}", font=("Courier", 12, "bold")).pack(pady=10)
# Attempt to preview image inline
try:
resp = requests.get(file_url)
pil_img = Image.open(BytesIO(resp.content))
@@ -189,6 +207,53 @@ class ChatApp:
tk.Button(btn_frame, text="Download", command=download_file).pack(side=tk.LEFT, padx=5)
tk.Button(btn_frame, text="Preview", command=lambda: webbrowser.open(preview_url)).pack(side=tk.LEFT, padx=5)
# ---------- Timestamp helper ----------
def format_timestamp(self, ts):
try:
dt = parser.parse(ts)
dt = dt.astimezone(tz.gettz(self.settings.get("timezone", time.tzname[0])))
if self.settings.get("time_24h"):
return dt.strftime("%Y-%m-%d %H:%M:%S")
else:
return dt.strftime("%Y-%m-%d %I:%M:%S %p")
except Exception:
return str(ts)
def update_chat_timestamps(self):
"""Re-render chat box to apply new timezone or 24h/12h setting."""
try:
# Re-poll messages and re-render
r = SESSION.get(SERVER + "/api/messages")
if r.status_code != 200:
return
msgs = r.json()
self.chat_box.config(state='normal')
self.chat_box.delete("1.0", tk.END)
for m in msgs:
ts = m.get("created_at") or time.time()
ts_str = self.format_timestamp(ts)
if m.get("attachment"):
attach_name = m["attachment"]
tag_name = f"attach{m['id']}"
self.chat_box.insert(tk.END, f"[{ts_str}] {m['username']} sent a file: ", f"grey{m['id']}")
self.chat_box.tag_config(f"grey{m['id']}", foreground="grey", font=("Courier", 10, "italic"))
self.chat_box.insert(tk.END, f"{attach_name}\n", tag_name)
self.chat_box.tag_config(tag_name, foreground="blue", underline=True)
self.chat_box.tag_bind(tag_name, "<Button-1>",
lambda e, fn=attach_name, un=m["username"]: self.show_attachment_popup(fn, un))
else:
text = m.get("text", "")
self.chat_box.insert(tk.END, f"[{ts_str}] {m['username']}: {text}\n")
self.chat_box.config(state='disabled')
self.chat_box.yview(tk.END)
except Exception as e:
print(f"Update timestamp error: {e}")
# ---------- Poll messages ----------
def poll_messages(self, force=False):
try:
r = SESSION.get(SERVER + "/api/messages")
@@ -199,34 +264,7 @@ class ChatApp:
new_msgs = [m for m in msgs if m["id"] not in LAST_MSG_IDS]
if new_msgs or force:
self.chat_box.config(state='normal')
self.chat_box.delete("1.0", tk.END)
for m in msgs:
ts = m.get("created_at") or time.time()
try:
ts_str = datetime.fromtimestamp(float(ts)).strftime("%Y-%m-%d %H:%M:%S")
except:
ts_str = str(ts)
if m.get("attachment"):
attach_name = m["attachment"]
tag_name = f"attach{m['id']}"
# Grey italic text for "sent a file"
self.chat_box.insert(tk.END, f"[{ts_str}] {m['username']} sent a file: ", f"grey{m['id']}")
self.chat_box.tag_config(f"grey{m['id']}", foreground="grey", font=("Courier", 10, "italic"))
# Blue clickable filename
self.chat_box.insert(tk.END, f"{attach_name}\n", tag_name)
self.chat_box.tag_config(tag_name, foreground="blue", underline=True)
self.chat_box.tag_bind(tag_name, "<Button-1>",
lambda e, fn=attach_name, un=m["username"]: self.show_attachment_popup(fn, un))
else:
text = m.get("text", "")
self.chat_box.insert(tk.END, f"[{ts_str}] {m['username']}: {text}\n")
self.chat_box.config(state='disabled')
self.chat_box.yview(tk.END)
self.update_chat_timestamps()
# Notify user about new messages
for m in new_msgs:
@@ -250,12 +288,7 @@ class ChatApp:
def show_system_notification(self, title, message):
try:
notification.notify(
title=title,
app_name="chatter",
message=message[:200],
timeout=5
)
notification.notify(title=title, app_name="chatter", message=message[:200], timeout=5)
except Exception as e:
print(f"Notification failed: {e}")