Update it + legacy ver #4

Open
win7he wants to merge 3 commits from win7he/r2chat:master into master
Showing only changes of commit 78d1fbffd1 - Show all commits

update r2chat

win7he 2026-04-18 01:50:27 -05:00

109
r2chat.py
View file

@ -1,15 +1,14 @@
import tkinter as tk import tkinter as tk
from tkinter import scrolledtext, messagebox from tkinter import scrolledtext, messagebox
from tkinter import ttk, colorchooser, font as tkfont from tkinter import ttk, colorchooser, font as tkfont
import os, sys, json import os, sys, json, time, getpass, webbrowser, re, subprocess
from pathlib import Path from pathlib import Path
import time
import getpass
from plyer import notification from plyer import notification
import pygame as media import pygame as media
import webbrowser from random import randint
import re import wlang
import subprocess
wlang.setlangfile('assets/internal/lang.json')
if sys.platform == "win32": if sys.platform == "win32":
ROOT = Path("C:/Users/Public") ROOT = Path("C:/Users/Public")
@ -18,8 +17,8 @@ else:
try: try:
if sys.argv[1]!='chat': if sys.argv[1]!='chat':
CHANCHAN = sys.argv[1].replace(' ','_').replace('.','-') CHANCHAN = sys.argv[1].replace(' ','_').replace('.','-').replace('\\','/').replace(':','/')
CHANNEL = 'chat_'+CHANCHAN CHANNEL = 'chat_'+CHANCHAN.replace('/','#')
else: else:
CHANNEL = 'chat' CHANNEL = 'chat'
CHANCHAN = 'chat' CHANCHAN = 'chat'
@ -49,11 +48,12 @@ DEFAULT_CONFIG = {
"border_width": 2, "border_width": 2,
"bg_color": "#f0f0f0", "bg_color": "#f0f0f0",
"fg_color": "#1e1e1e", "fg_color": "#1e1e1e",
"user_color": "#0000ff", "user_color": f"#{['0000ff','af0000','00af00','ffaf00','ff00ff','00ffff','7f00ff','ff7f00','00ff7f','007fff'][randint(0,9)]}",
"status_join": "{username} is now online.", "status_join": "{username} is now online.",
"status_leave": "{username} is now offline.", "status_leave": "{username} is now offline.",
"status_inactive": "{username} will be right back.", "status_inactive": "{username} will be right back.",
"status_busy": "{username} is busy." "status_busy": "{username} is busy.",
"language": "en"
} }
def load_config(): def load_config():
@ -76,8 +76,8 @@ class r2chat:
def __init__(self, root): def __init__(self, root):
self.root = root self.root = root
self.cfg = load_config() self.cfg = load_config()
self.root.title(f"r2chat :: {username} / #{CHANCHAN}") self.root.title(f"r2chat :: {username} / #{CHANCHAN.replace('#','/')}")
wlang.setlang(self.cfg['language'])
self.last_size = 0 self.last_size = 0
# configs # configs
@ -98,28 +98,29 @@ class r2chat:
## chat menu ## chat menu
self.chatmenu = tk.Menu(self.menubar, tearoff=0) self.chatmenu = tk.Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label="Chat", menu=self.chatmenu) self.menubar.add_cascade(label=wlang.getobj('chat'), menu=self.chatmenu)
self.chatmenu.add_command(label="Registered users", command=lambda: self.show_users()) self.chatmenu.add_command(label=wlang.getobj('regusers'), command=lambda: self.show_users())
self.chatmenu.add_command(label="Channel list", command=lambda: self.show_channels()) self.chatmenu.add_command(label=wlang.getobj('chlist'), command=lambda: self.show_channels())
self.chatmenu.add_command(label="Leave channel", command=lambda: self.on_close()) self.chatmenu.add_command(label=wlang.getobj('lchan'), command=lambda: self.on_close())
## insert menu ## insert menu
self.chatmenu = tk.Menu(self.menubar, tearoff=0) self.chatmenu = tk.Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label="Insert", menu=self.chatmenu) self.menubar.add_cascade(label=wlang.getobj('insert'), menu=self.chatmenu)
self.chatmenu.add_command(label="Emoticon", command=lambda: print("")) self.chatmenu.add_command(label=wlang.getobj('emoticon'), command=lambda: print(""))
self.chatmenu.add_command(label="GIF", command=lambda: print("")) ## its pronounced jif
self.chatmenu.add_command(label=wlang.getobj('gif'), command=lambda: print(""))
## status menu ## status menu
self.chatmenu = tk.Menu(self.menubar, tearoff=0) self.chatmenu = tk.Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label="Status", menu=self.chatmenu) self.menubar.add_cascade(label=wlang.getobj('status'), menu=self.chatmenu)
self.chatmenu.add_command(label="Online (All notifs)", command=lambda: self.setstatus("online")) self.chatmenu.add_command(label=wlang.getobj('onlinestat'), command=lambda: self.setstatus("online"))
self.chatmenu.add_command(label="Inactive (Pings only)", command=lambda: self.setstatus("inactive")) self.chatmenu.add_command(label=wlang.getobj('inactstat'), command=lambda: self.setstatus("inactive"))
self.chatmenu.add_command(label="Busy (No notifs)", command=lambda: self.setstatus("busy")) self.chatmenu.add_command(label=wlang.getobj('busystat'), command=lambda: self.setstatus("busy"))
self.chatmenu.add_command(label="Appear offline", command=lambda: self.setstatus("offline")) self.chatmenu.add_command(label=wlang.getobj('offlinestat'), command=lambda: self.setstatus("offline"))
## menu buttons ## menu buttons
self.menubar.add_command(label="Preferences", command=lambda: self.openprefs()) self.menubar.add_command(label=wlang.getobj('pref'), command=lambda: self.openprefs())
self.menubar.add_command(label="Help", command=lambda: webbrowser.open_new_tab("https://forgejo.nishi.boats/Dogo6647/r2chat")) self.menubar.add_command(label=wlang.getobj('help'), command=lambda: webbrowser.open_new_tab("https://forgejo.nishi.boats/Dogo6647/r2chat"))
# MAIN MENU STUFFS # MAIN MENU STUFFS
self.entry = tk.Entry(root, relief='sunken', borderwidth=self.cfg["border_width"]) self.entry = tk.Entry(root, relief='sunken', borderwidth=self.cfg["border_width"])
@ -135,7 +136,7 @@ class r2chat:
open(CHAT_FILE, "a").close() open(CHAT_FILE, "a").close()
self.update_chat() self.update_chat()
self.write_system_message(f'◉ {self.cfg["status_join"]}') self.write_system_message(f'((#00af00 | ◉)) {self.cfg["status_join"]}')
def on_close(self): def on_close(self):
self.write_system_message(f'◌ {self.cfg["status_leave"]}') self.write_system_message(f'◌ {self.cfg["status_leave"]}')
@ -180,13 +181,13 @@ class r2chat:
status = new_status status = new_status
if status == "online": if status == "online":
self.write_system_message(msg=f'◉ {self.cfg["status_join"]}') self.write_system_message(msg=f'((#00af00 | ◉)) {self.cfg["status_join"]}')
elif status == "offline": elif status == "offline":
self.write_system_message(msg=f'◌ {self.cfg["status_leave"]}') self.write_system_message(msg=f'◌ {self.cfg["status_leave"]}')
elif status == "inactive": elif status == "inactive":
self.write_system_message(msg=f'◍ {self.cfg["status_inactive"]}') self.write_system_message(msg=f'((#ffaf00 | ◍)) {self.cfg["status_inactive"]}')
elif status == "busy": elif status == "busy":
self.write_system_message(msg=f'◎ {self.cfg["status_busy"]}') self.write_system_message(msg=f'((#af0000 | ◎)) {self.cfg["status_busy"]}')
self.user_data["status"] = status self.user_data["status"] = status
self.save_userdata(self.user_data) self.save_userdata(self.user_data)
@ -213,15 +214,15 @@ class r2chat:
def show_users(self): def show_users(self):
win = tk.Toplevel(self.root) win = tk.Toplevel(self.root)
win.title("Registered users") win.title(wlang.getobj('regusers'))
win.geometry("420x320") win.geometry("420x320")
tree = ttk.Treeview(win, columns=("user", "status", "channels", "last_seen"), show="headings") tree = ttk.Treeview(win, columns=("user", "status", "channels", "last_seen"), show="headings")
tree.heading("user", text="User") tree.heading("user", text=wlang.getobj('user'))
tree.heading("status", text="Status") tree.heading("status", text=wlang.getobj('status'))
tree.heading("channels", text="Channels") tree.heading("channels", text=wlang.getobj('chans'))
tree.heading("last_seen", text="Last seen") tree.heading("last_seen", text=wlang.getobj('lseen'))
tree.column("user", width=80) tree.column("user", width=80)
tree.column("status", width=60, anchor="center") tree.column("status", width=60, anchor="center")
@ -250,7 +251,7 @@ class r2chat:
def show_channels(self): def show_channels(self):
win = tk.Toplevel(self.root) win = tk.Toplevel(self.root)
win.title("Channel list") win.title("Channel list")
win.geometry("200x320") win.geometry("600x320")
tree = ttk.Treeview(win, columns=("channel"), show="headings") tree = ttk.Treeview(win, columns=("channel"), show="headings")
tree.heading("channel", text="Channels") tree.heading("channel", text="Channels")
@ -275,7 +276,7 @@ class r2chat:
tree.bind("<Double-1>", on_select) tree.bind("<Double-1>", on_select)
def open_channel(self, channel): def open_channel(self, channel):
self.write_system_message(f"⇄ {username} is chatting in {channel}.") self.write_system_message(f"((#006fff | ⇄)) {username} is chatting in {channel.replace('#','/')}.")
if sys.platform == "win32": if sys.platform == "win32":
subprocess.Popen(f'pythonw "{os.path.abspath(__file__)}" "{channel}"', shell=True) subprocess.Popen(f'pythonw "{os.path.abspath(__file__)}" "{channel}"', shell=True)
else: else:
@ -317,7 +318,7 @@ class r2chat:
try: try:
current_size = os.path.getsize(CHAT_FILE) current_size = os.path.getsize(CHAT_FILE)
if current_size > self.last_size: if current_size != self.last_size:
with open(CHAT_FILE, "r", encoding="utf-8") as f: with open(CHAT_FILE, "r", encoding="utf-8") as f:
f.seek(self.last_size) f.seek(self.last_size)
new_data = f.read() new_data = f.read()
@ -326,7 +327,7 @@ class r2chat:
self.chat_area.config(state='normal') self.chat_area.config(state='normal')
if f"@{username} " in new_data and not "---" in new_data and not status == "busy" and not status == "offline": if f"@{username} " in new_data and not "---" in new_data and not status == "busy" and not status == "offline":
media.mixer.Sound("assets/internal/ping.ogg").play() media.mixer.Sound("assets/internal/ping.ogg").play()
messagebox.showinfo("Important message!", new_data) messagebox.showinfo(wlang.getobj('impmes'), new_data)
if self.root.wm_state() == 'iconic': if self.root.wm_state() == 'iconic':
if not f"@{username} " in new_data and not status == "busy" and not status == "inactive": if not f"@{username} " in new_data and not status == "busy" and not status == "inactive":
notification.notify(title="r2chat", message=new_data) notification.notify(title="r2chat", message=new_data)
@ -347,7 +348,7 @@ class PreferencesWindow:
self.on_save = on_save self.on_save = on_save
self.win = tk.Toplevel(parent) self.win = tk.Toplevel(parent)
self.win.title("r2chat prefs") self.win.title(f"r2chat {wlang.getobj('prefl')}")
self.win.geometry("420x520") self.win.geometry("420x520")
self.win.resizable(False, False) self.win.resizable(False, False)
self.win.transient(parent) self.win.transient(parent)
@ -366,16 +367,16 @@ class PreferencesWindow:
btn_frame = tk.Frame(self.win) btn_frame = tk.Frame(self.win)
btn_frame.pack(fill=tk.X, padx=10, pady=(0, 10)) btn_frame.pack(fill=tk.X, padx=10, pady=(0, 10))
tk.Button(btn_frame, text="Save", width=10, command=self._save).pack(side=tk.RIGHT, padx=(4, 0)) tk.Button(btn_frame, text=wlang.getobj('save'), width=10, command=self._save).pack(side=tk.RIGHT, padx=(4, 0))
tk.Button(btn_frame, text="Cancel", width=10, command=self.win.destroy).pack(side=tk.RIGHT) tk.Button(btn_frame, text=wlang.getobj('cancel'), width=10, command=self.win.destroy).pack(side=tk.RIGHT)
tk.Button(btn_frame, text="Back to defaults", command=self._reset).pack(side=tk.LEFT) tk.Button(btn_frame, text=wlang.getobj('btodef'), command=self._reset).pack(side=tk.LEFT)
def _build_appearance_tab(self, notebook): def _build_appearance_tab(self, notebook):
frame = tk.Frame(notebook) frame = tk.Frame(notebook)
notebook.add(frame, text="Interface") notebook.add(frame, text=wlang.getobj('interface'))
# font family # font family
self._row(frame, 0, "Font family") self._row(frame, 0, wlang.getobj('ffam'))
font_var = tk.StringVar(value=self.cfg["font_family"]) font_var = tk.StringVar(value=self.cfg["font_family"])
self.vars["font_family"] = font_var self.vars["font_family"] = font_var
families = sorted(tkfont.families()) families = sorted(tkfont.families())
@ -383,34 +384,34 @@ class PreferencesWindow:
font_combo.grid(row=0, column=1, padx=10, pady=6, sticky=tk.W) font_combo.grid(row=0, column=1, padx=10, pady=6, sticky=tk.W)
# font size # font size
self._row(frame, 1, "Font size") self._row(frame, 1, wlang.getobj('fsiz'))
size_var = tk.IntVar(value=self.cfg["font_size"]) size_var = tk.IntVar(value=self.cfg["font_size"])
self.vars["font_size"] = size_var self.vars["font_size"] = size_var
tk.Spinbox(frame, from_=7, to=24, textvariable=size_var, width=6).grid(row=1, column=1, padx=10, pady=6, sticky=tk.W) tk.Spinbox(frame, from_=7, to=24, textvariable=size_var, width=6).grid(row=1, column=1, padx=10, pady=6, sticky=tk.W)
# border width # border width
self._row(frame, 2, "Frame thickness") self._row(frame, 2, wlang.getobj('fthi'))
bw_var = tk.IntVar(value=self.cfg["border_width"]) bw_var = tk.IntVar(value=self.cfg["border_width"])
self.vars["border_width"] = bw_var self.vars["border_width"] = bw_var
tk.Spinbox(frame, from_=0, to=6, textvariable=bw_var, width=6).grid(row=2, column=1, padx=10, pady=6, sticky=tk.W) tk.Spinbox(frame, from_=0, to=6, textvariable=bw_var, width=6).grid(row=2, column=1, padx=10, pady=6, sticky=tk.W)
# color pickers # color pickers
for i, (key, label) in enumerate([ for i, (key, label) in enumerate([
("bg_color", "Background color"), ("bg_color", wlang.getobj('backc')),
("fg_color", "Text color"), ("fg_color", wlang.getobj('textc')),
("user_color", "Username color"), ("user_color", wlang.getobj('userc')),
], start=3): ], start=3):
self._row(frame, i, label) self._row(frame, i, label)
self._color_picker_row(frame, i, key) self._color_picker_row(frame, i, key)
def _build_status_messages_tab(self, notebook): def _build_status_messages_tab(self, notebook):
frame = tk.Frame(notebook) frame = tk.Frame(notebook)
notebook.add(frame, text="Status messages") notebook.add(frame, text=wlang.getobj('statmes'))
tk.Label(frame, text="Use {username} where you want your username to be.", fg="gray", font=("Lucida Console", 9)).grid( tk.Label(frame, text=wlang.getobj('usernh'), fg="gray", font=("Lucida Console", 9)).grid(
row=0, column=0, columnspan=2, padx=10, pady=(10, 4), sticky=tk.W) row=0, column=0, columnspan=2, padx=10, pady=(10, 4), sticky=tk.W)
for i, (key, label) in enumerate([("status_join", "Join message"),("status_leave", "Leave message"),("status_inactive", "Inactive message"),("status_busy", "Busy message")], start=1): for i, (key, label) in enumerate([("status_join", wlang.getobj('joinm')),("status_leave", wlang.getobj('leavm')),("status_inactive", wlang.getobj('inacm')),("status_busy", wlang.getobj('busym'))], start=1):
self._row(frame, i, label) self._row(frame, i, label)
var = tk.StringVar(value=self.cfg[key]) var = tk.StringVar(value=self.cfg[key])
self.vars[key] = var self.vars[key] = var
@ -432,7 +433,7 @@ class PreferencesWindow:
v.set(result[1]) v.set(result[1])
p.config(bg=result[1]) p.config(bg=result[1])
tk.Button(frame, text="Choose…", command=pick, width=8).grid(row=row, column=1, padx=(44, 0), pady=6, sticky=tk.W) tk.Button(frame, text=wlang.getobj('choose'), command=pick, width=8).grid(row=row, column=1, padx=(44, 0), pady=6, sticky=tk.W)
def _save(self): def _save(self):
for key, var in self.vars.items(): for key, var in self.vars.items():