some sound additions
This commit is contained in:
@@ -10,6 +10,7 @@ pyinstaller `
|
|||||||
--hidden-import wx._xml `
|
--hidden-import wx._xml `
|
||||||
--add-data "FiraCode-Regular.ttf;." `
|
--add-data "FiraCode-Regular.ttf;." `
|
||||||
--add-data "FiraCode-SemiBold.ttf;." `
|
--add-data "FiraCode-SemiBold.ttf;." `
|
||||||
|
--add-data "src\sounds\*;sounds" `
|
||||||
--add-data "venv\Lib\site-packages\irc\codes.txt;irc" `
|
--add-data "venv\Lib\site-packages\irc\codes.txt;irc" `
|
||||||
--add-data "icon.ico;." `
|
--add-data "icon.ico;." `
|
||||||
--icon "icon.ico" `
|
--icon "icon.ico" `
|
||||||
|
|||||||
99
src/Win32API.py
Normal file
99
src/Win32API.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import ctypes
|
||||||
|
from ctypes import wintypes
|
||||||
|
import time
|
||||||
|
|
||||||
|
SW_HIDE = 0
|
||||||
|
SW_SHOW = 5
|
||||||
|
|
||||||
|
GWL_EXSTYLE = -20
|
||||||
|
WS_EX_TOOLWINDOW = 0x00000080
|
||||||
|
WS_EX_APPWINDOW = 0x00040000
|
||||||
|
WS_EX_LAYERED = 0x80000
|
||||||
|
LWA_ALPHA = 0x2
|
||||||
|
|
||||||
|
|
||||||
|
class Win32API:
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
self.hwnd = hwnd
|
||||||
|
self.hidden = False
|
||||||
|
self._load_api()
|
||||||
|
|
||||||
|
def _load_api(self):
|
||||||
|
user32 = ctypes.windll.user32
|
||||||
|
|
||||||
|
self.ShowWindow = user32.ShowWindow
|
||||||
|
self.GetWindowLong = user32.GetWindowLongW
|
||||||
|
self.SetWindowLong = user32.SetWindowLongW
|
||||||
|
self._RegisterHotKey = user32.RegisterHotKey
|
||||||
|
self._SetLayeredWindowAttributes = user32.SetLayeredWindowAttributes
|
||||||
|
|
||||||
|
self.ShowWindow.argtypes = [wintypes.HWND, ctypes.c_int]
|
||||||
|
self.GetWindowLong.argtypes = [wintypes.HWND, ctypes.c_int]
|
||||||
|
self.SetWindowLong.argtypes = [wintypes.HWND, ctypes.c_int, ctypes.c_long]
|
||||||
|
self._RegisterHotKey.argtypes = [
|
||||||
|
wintypes.HWND,
|
||||||
|
wintypes.INT,
|
||||||
|
wintypes.UINT,
|
||||||
|
wintypes.UINT,
|
||||||
|
]
|
||||||
|
self._SetLayeredWindowAttributes.argtypes = [
|
||||||
|
wintypes.HWND,
|
||||||
|
wintypes.COLORREF,
|
||||||
|
wintypes.BYTE,
|
||||||
|
wintypes.DWORD,
|
||||||
|
]
|
||||||
|
|
||||||
|
def remove_from_taskbar(self):
|
||||||
|
style = self.GetWindowLong(self.hwnd, GWL_EXSTYLE)
|
||||||
|
new_style = (style & ~WS_EX_APPWINDOW) | WS_EX_TOOLWINDOW
|
||||||
|
self.SetWindowLong(self.hwnd, GWL_EXSTYLE, new_style)
|
||||||
|
|
||||||
|
def hide(self):
|
||||||
|
self.ShowWindow(self.hwnd, SW_HIDE)
|
||||||
|
self.hidden = True
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
self.ShowWindow(self.hwnd, SW_SHOW)
|
||||||
|
self.hidden = False
|
||||||
|
|
||||||
|
def toggle(self, fade=True, duration=500, steps=20):
|
||||||
|
"""Toggle window visibility with optional fade effect"""
|
||||||
|
if self.hidden:
|
||||||
|
if fade:
|
||||||
|
self.fade_in(duration, steps)
|
||||||
|
else:
|
||||||
|
self.show()
|
||||||
|
else:
|
||||||
|
if fade:
|
||||||
|
self.fade_out(duration, steps)
|
||||||
|
else:
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
|
||||||
|
def register_hotkey(self, hotkey_id, key, modifiers):
|
||||||
|
vk = ord(key.upper())
|
||||||
|
return self._RegisterHotKey(None, hotkey_id, modifiers, vk)
|
||||||
|
|
||||||
|
def _set_layered(self):
|
||||||
|
style = self.GetWindowLong(self.hwnd, GWL_EXSTYLE)
|
||||||
|
self.SetWindowLong(self.hwnd, GWL_EXSTYLE, style | WS_EX_LAYERED)
|
||||||
|
|
||||||
|
def fade_in(self, duration=50, steps=50):
|
||||||
|
self.show()
|
||||||
|
self._set_layered()
|
||||||
|
for idx in range(steps + 1):
|
||||||
|
alpha = int(255 * (idx / steps))
|
||||||
|
self._SetLayeredWindowAttributes(self.hwnd, 0, alpha, LWA_ALPHA)
|
||||||
|
ctypes.windll.kernel32.Sleep(int(duration / steps))
|
||||||
|
self.hidden = False
|
||||||
|
|
||||||
|
def fade_out(self, duration=50, steps=50):
|
||||||
|
self._set_layered()
|
||||||
|
for idx in range(steps + 1):
|
||||||
|
alpha = int(255 * (1 - idx /steps))
|
||||||
|
self._SetLayeredWindowAttributes(self.hwnd, 0, alpha, LWA_ALPHA)
|
||||||
|
ctypes.windll.kernel32.Sleep(int(duration / steps))
|
||||||
|
self.hidden = True
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
|
||||||
97
src/Win32SoundHandler.py
Normal file
97
src/Win32SoundHandler.py
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import ctypes
|
||||||
|
import logging
|
||||||
|
from ctypes import wintypes
|
||||||
|
|
||||||
|
# Set up logging
|
||||||
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def get_resource_path(relative_path):
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
try:
|
||||||
|
base_path = sys._MEIPASS
|
||||||
|
except Exception:
|
||||||
|
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__)))
|
||||||
|
base_path = project_root
|
||||||
|
full_path = os.path.normpath(os.path.join(base_path, relative_path))
|
||||||
|
return full_path
|
||||||
|
|
||||||
|
class Win32SoundHandler:
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
self.hwnd = hwnd
|
||||||
|
self.SND_ALIAS = 0x00010000
|
||||||
|
self.SND_FILENAME = 0x00020000
|
||||||
|
self.SND_ASYNC = 0x00000001
|
||||||
|
self._setup_playsoundapi()
|
||||||
|
|
||||||
|
def _setup_playsoundapi(self):
|
||||||
|
winmm = ctypes.windll.winmm
|
||||||
|
self.PlaySoundW = winmm.PlaySoundW
|
||||||
|
self.PlaySoundW.argtypes = (
|
||||||
|
wintypes.LPCWSTR,
|
||||||
|
wintypes.HMODULE,
|
||||||
|
wintypes.DWORD
|
||||||
|
)
|
||||||
|
self.PlaySoundW.restype = wintypes.BOOL
|
||||||
|
|
||||||
|
def play_info_sound(self):
|
||||||
|
self.PlaySoundW("SystemAsterisk", None, self.SND_ALIAS | self.SND_ASYNC)
|
||||||
|
|
||||||
|
def play_error_sound(self):
|
||||||
|
self.PlaySoundW("SystemHand", None, self.SND_ALIAS | self.SND_ASYNC)
|
||||||
|
|
||||||
|
def play_warn_sound(self):
|
||||||
|
self.PlaySoundW("SystemExclamation", None, self.SND_ALIAS | self.SND_ASYNC)
|
||||||
|
|
||||||
|
def play_question_sound(self):
|
||||||
|
self.PlaySoundW("SystemQuestion", None, self.SND_ALIAS | self.SND_ASYNC)
|
||||||
|
|
||||||
|
def play_default_sound(self):
|
||||||
|
self.PlaySoundW("SystemDefault", None, self.SND_ALIAS | self.SND_ASYNC)
|
||||||
|
|
||||||
|
def play_notification_sound(self):
|
||||||
|
self.PlaySoundW("SystemNotification", None, self.SND_ALIAS | self.SND_ASYNC)
|
||||||
|
|
||||||
|
def play_mail_sound(self):
|
||||||
|
self.PlaySoundW("MailBeep", None, self.SND_ALIAS | self.SND_ASYNC)
|
||||||
|
|
||||||
|
def play_exit_sound(self):
|
||||||
|
self.PlaySoundW("SystemExit", None, self.SND_ALIAS | self.SND_ASYNC)
|
||||||
|
|
||||||
|
def play_connect_server_or_channel(self):
|
||||||
|
try:
|
||||||
|
sound_path = get_resource_path("sounds/space-pdj.wav")
|
||||||
|
import os
|
||||||
|
if os.path.exists(sound_path):
|
||||||
|
self.PlaySoundW(sound_path, None, self.SND_FILENAME | self.SND_ASYNC)
|
||||||
|
else:
|
||||||
|
logger.warning(f"Sound file not found: {sound_path}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error playing popout click sound: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def play_popout_click(self):
|
||||||
|
try:
|
||||||
|
sound_path = get_resource_path("sounds/startup.wav")
|
||||||
|
import os
|
||||||
|
if os.path.exists(sound_path):
|
||||||
|
self.PlaySoundW(sound_path, None, self.SND_FILENAME | self.SND_ASYNC)
|
||||||
|
else:
|
||||||
|
logger.warning(f"Sound file not found: {sound_path}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error playing popout click sound: {e}")
|
||||||
|
|
||||||
|
def play_msg_recv(self):
|
||||||
|
try:
|
||||||
|
sound_path = get_resource_path("sounds/balloon.wav")
|
||||||
|
import os
|
||||||
|
if os.path.exists(sound_path):
|
||||||
|
self.PlaySoundW(sound_path, None, self.SND_FILENAME | self.SND_ASYNC)
|
||||||
|
else:
|
||||||
|
logger.warning(f"Sound file not found: {sound_path}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error playing message received sound: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
95
src/main.py
95
src/main.py
@@ -16,6 +16,7 @@ from PrivacyNoticeDialog import PrivacyNoticeDialog
|
|||||||
from IRCPanel import IRCPanel
|
from IRCPanel import IRCPanel
|
||||||
from AboutDialog import AboutDialog
|
from AboutDialog import AboutDialog
|
||||||
from NotesDialog import NotesDialog
|
from NotesDialog import NotesDialog
|
||||||
|
if os.name == "nt": from Win32API import Win32API ; from Win32SoundHandler import Win32SoundHandler
|
||||||
from ScanWizard import ScanWizardDialog
|
from ScanWizard import ScanWizardDialog
|
||||||
from LocalServer import LocalServerManager
|
from LocalServer import LocalServerManager
|
||||||
|
|
||||||
@@ -300,6 +301,18 @@ class IRCFrame(wx.Frame):
|
|||||||
self.motd_lines = []
|
self.motd_lines = []
|
||||||
self.collecting_motd = False
|
self.collecting_motd = False
|
||||||
|
|
||||||
|
self.hwnd = self.GetHandle()
|
||||||
|
self.ctrl = Win32API(self.hwnd)
|
||||||
|
|
||||||
|
# Initialize sound handler for Windows
|
||||||
|
if os.name == "nt":
|
||||||
|
self.sound_handler = Win32SoundHandler(self.hwnd)
|
||||||
|
else:
|
||||||
|
self.sound_handler = None
|
||||||
|
|
||||||
|
# Sound toggle state (enabled by default)
|
||||||
|
self.sounds_enabled = True
|
||||||
|
|
||||||
self.setup_irc_handlers()
|
self.setup_irc_handlers()
|
||||||
self.create_menubar()
|
self.create_menubar()
|
||||||
self.setup_ui()
|
self.setup_ui()
|
||||||
@@ -330,6 +343,17 @@ class IRCFrame(wx.Frame):
|
|||||||
self.Bind(wx.EVT_MENU, self.on_find_previous, id=1002)
|
self.Bind(wx.EVT_MENU, self.on_find_previous, id=1002)
|
||||||
self.Bind(wx.EVT_MENU, self.on_quick_escape, id=1003)
|
self.Bind(wx.EVT_MENU, self.on_quick_escape, id=1003)
|
||||||
|
|
||||||
|
HOTKEY_ID = 1
|
||||||
|
MOD_CONTROL = 0x0002
|
||||||
|
MOD_ALT = 0x0001
|
||||||
|
|
||||||
|
# Register directly on the wx.Frame
|
||||||
|
self.RegisterHotKey(HOTKEY_ID, MOD_CONTROL | MOD_ALT, ord('H'))
|
||||||
|
self.Bind(wx.EVT_HOTKEY, self.on_hotkey, id=HOTKEY_ID)
|
||||||
|
|
||||||
|
def on_hotkey(self, event):
|
||||||
|
self.ctrl.toggle()
|
||||||
|
|
||||||
def build_theme(self):
|
def build_theme(self):
|
||||||
"""Build a small theme descriptor that respects the host platform."""
|
"""Build a small theme descriptor that respects the host platform."""
|
||||||
try:
|
try:
|
||||||
@@ -397,7 +421,6 @@ class IRCFrame(wx.Frame):
|
|||||||
def show_privacy_notice(self):
|
def show_privacy_notice(self):
|
||||||
"""Show privacy notice dialog at startup"""
|
"""Show privacy notice dialog at startup"""
|
||||||
dlg = PrivacyNoticeDialog(self)
|
dlg = PrivacyNoticeDialog(self)
|
||||||
dlg.ShowModal()
|
|
||||||
dlg.Destroy()
|
dlg.Destroy()
|
||||||
|
|
||||||
def get_user_color(self, username):
|
def get_user_color(self, username):
|
||||||
@@ -595,8 +618,12 @@ class IRCFrame(wx.Frame):
|
|||||||
file_menu = wx.Menu()
|
file_menu = wx.Menu()
|
||||||
file_menu.Append(300, "&About", "About wxIRC Client")
|
file_menu.Append(300, "&About", "About wxIRC Client")
|
||||||
file_menu.AppendSeparator()
|
file_menu.AppendSeparator()
|
||||||
|
self.sound_toggle_item = file_menu.AppendCheckItem(301, "Toggle &Sounds")
|
||||||
|
self.sound_toggle_item.Check(True) # Sounds enabled by default
|
||||||
|
file_menu.AppendSeparator()
|
||||||
file_menu.Append(wx.ID_EXIT, "E&xit\tCtrl+Q")
|
file_menu.Append(wx.ID_EXIT, "E&xit\tCtrl+Q")
|
||||||
self.Bind(wx.EVT_MENU, self.on_about, id=300)
|
self.Bind(wx.EVT_MENU, self.on_about, id=300)
|
||||||
|
self.Bind(wx.EVT_MENU, self.on_toggle_sounds, id=301)
|
||||||
self.Bind(wx.EVT_MENU, self.on_close, id=wx.ID_EXIT)
|
self.Bind(wx.EVT_MENU, self.on_close, id=wx.ID_EXIT)
|
||||||
|
|
||||||
# Edit menu with search
|
# Edit menu with search
|
||||||
@@ -669,6 +696,11 @@ class IRCFrame(wx.Frame):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error creating menu: {e}")
|
logger.error(f"Error creating menu: {e}")
|
||||||
|
|
||||||
|
def on_toggle_sounds(self, event):
|
||||||
|
"""Toggle sound effects on/off"""
|
||||||
|
self.sounds_enabled = self.sound_toggle_item.IsChecked()
|
||||||
|
logger.info(f"Sounds {'enabled' if self.sounds_enabled else 'disabled'}")
|
||||||
|
|
||||||
def on_about(self, event):
|
def on_about(self, event):
|
||||||
"""Show About dialog"""
|
"""Show About dialog"""
|
||||||
try:
|
try:
|
||||||
@@ -677,6 +709,11 @@ class IRCFrame(wx.Frame):
|
|||||||
dlg.Destroy()
|
dlg.Destroy()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error showing about dialog: {e}")
|
logger.error(f"Error showing about dialog: {e}")
|
||||||
|
if self.sound_handler and self.sounds_enabled:
|
||||||
|
try:
|
||||||
|
self.sound_handler.play_error_sound()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def on_notes(self, event):
|
def on_notes(self, event):
|
||||||
"""Open notes editor dialog"""
|
"""Open notes editor dialog"""
|
||||||
@@ -697,6 +734,11 @@ class IRCFrame(wx.Frame):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error opening notes dialog: {e}")
|
logger.error(f"Error opening notes dialog: {e}")
|
||||||
self.log_server(f"Error opening notes: {e}", wx.Colour(255, 0, 0))
|
self.log_server(f"Error opening notes: {e}", wx.Colour(255, 0, 0))
|
||||||
|
if self.sound_handler and self.sounds_enabled:
|
||||||
|
try:
|
||||||
|
self.sound_handler.play_error_sound()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def on_notes_closed(self, event=None):
|
def on_notes_closed(self, event=None):
|
||||||
"""Handle notes frame closing"""
|
"""Handle notes frame closing"""
|
||||||
@@ -863,6 +905,13 @@ class IRCFrame(wx.Frame):
|
|||||||
self.SetStatusText("Connection failed")
|
self.SetStatusText("Connection failed")
|
||||||
logger.error(f"Connection failed: {error_msg}")
|
logger.error(f"Connection failed: {error_msg}")
|
||||||
|
|
||||||
|
# Play error sound
|
||||||
|
if self.sound_handler and self.sounds_enabled:
|
||||||
|
try:
|
||||||
|
self.sound_handler.play_error_sound()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error playing connection failure sound: {e}")
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
"""Safely disconnect from IRC server"""
|
"""Safely disconnect from IRC server"""
|
||||||
try:
|
try:
|
||||||
@@ -892,6 +941,13 @@ class IRCFrame(wx.Frame):
|
|||||||
|
|
||||||
def on_disconnect_cleanup(self):
|
def on_disconnect_cleanup(self):
|
||||||
"""Clean up after disconnect"""
|
"""Clean up after disconnect"""
|
||||||
|
# Play disconnect notification sound
|
||||||
|
if self.sound_handler and self.sounds_enabled:
|
||||||
|
try:
|
||||||
|
self.sound_handler.play_warn_sound()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error playing disconnect sound: {e}")
|
||||||
|
|
||||||
with self.connection_lock:
|
with self.connection_lock:
|
||||||
self.connection = None
|
self.connection = None
|
||||||
self.is_connecting = False
|
self.is_connecting = False
|
||||||
@@ -1226,6 +1282,11 @@ Available commands:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error opening scan wizard: {e}")
|
logger.error(f"Error opening scan wizard: {e}")
|
||||||
self.log_server(f"Scan wizard failed to open: {e}", wx.Colour(255, 0, 0))
|
self.log_server(f"Scan wizard failed to open: {e}", wx.Colour(255, 0, 0))
|
||||||
|
if self.sound_handler and self.sounds_enabled:
|
||||||
|
try:
|
||||||
|
self.sound_handler.play_error_sound()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def quick_connect(self, server, port):
|
def quick_connect(self, server, port):
|
||||||
"""Populate connection fields and initiate a connection if idle."""
|
"""Populate connection fields and initiate a connection if idle."""
|
||||||
@@ -1434,6 +1495,13 @@ COMMANDS (type /help in chat for full list):
|
|||||||
self.log_server("Connected to server!", wx.Colour(0, 128, 0), bold=True) # Dark green
|
self.log_server("Connected to server!", wx.Colour(0, 128, 0), bold=True) # Dark green
|
||||||
self.log_server(f"Welcome message: {' '.join(event.arguments)}", wx.Colour(0, 100, 0))
|
self.log_server(f"Welcome message: {' '.join(event.arguments)}", wx.Colour(0, 100, 0))
|
||||||
|
|
||||||
|
# Play welcome/connection sound
|
||||||
|
if self.sound_handler and self.sounds_enabled:
|
||||||
|
try:
|
||||||
|
self.sound_handler.play_connect_server_or_channel()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error playing welcome sound: {e}")
|
||||||
|
|
||||||
# Auto-join channels
|
# Auto-join channels
|
||||||
for channel in self.auto_join_channels:
|
for channel in self.auto_join_channels:
|
||||||
if not channel.startswith('#'):
|
if not channel.startswith('#'):
|
||||||
@@ -1453,6 +1521,13 @@ COMMANDS (type /help in chat for full list):
|
|||||||
self.safe_ui_update(self.add_channel, channel)
|
self.safe_ui_update(self.add_channel, channel)
|
||||||
self.log_server(f"Joined channel {channel}", wx.Colour(0, 128, 0)) # Dark green
|
self.log_server(f"Joined channel {channel}", wx.Colour(0, 128, 0)) # Dark green
|
||||||
|
|
||||||
|
# Play sound when we join a channel
|
||||||
|
if self.sound_handler and self.sounds_enabled:
|
||||||
|
try:
|
||||||
|
self.sound_handler.play_connect_server_or_channel()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error playing channel join sound: {e}")
|
||||||
|
|
||||||
self.log_channel_message(channel, nick, f"→ {nick} joined", is_system=True)
|
self.log_channel_message(channel, nick, f"→ {nick} joined", is_system=True)
|
||||||
|
|
||||||
if nick not in self.channel_users[channel]:
|
if nick not in self.channel_users[channel]:
|
||||||
@@ -1510,6 +1585,13 @@ COMMANDS (type /help in chat for full list):
|
|||||||
else:
|
else:
|
||||||
self.log_channel_message(channel, nick, message)
|
self.log_channel_message(channel, nick, message)
|
||||||
|
|
||||||
|
# Play sound notification only for real user messages (not from self)
|
||||||
|
if self.sound_handler and self.sounds_enabled and nick.lower() != self.nick.lower():
|
||||||
|
try:
|
||||||
|
self.sound_handler.play_msg_recv()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error playing message sound: {e}")
|
||||||
|
|
||||||
# Highlight own nick in messages
|
# Highlight own nick in messages
|
||||||
if self.nick.lower() in message.lower():
|
if self.nick.lower() in message.lower():
|
||||||
self.safe_ui_update(wx.Bell)
|
self.safe_ui_update(wx.Bell)
|
||||||
@@ -1527,6 +1609,13 @@ COMMANDS (type /help in chat for full list):
|
|||||||
self.log_channel_message(nick, nick, message, is_action=True)
|
self.log_channel_message(nick, nick, message, is_action=True)
|
||||||
else:
|
else:
|
||||||
self.log_channel_message(nick, nick, message)
|
self.log_channel_message(nick, nick, message)
|
||||||
|
|
||||||
|
# Play mail sound for private messages
|
||||||
|
if self.sound_handler and self.sounds_enabled and nick.lower() != self.nick.lower():
|
||||||
|
try:
|
||||||
|
self.sound_handler.play_mail_sound()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error playing private message sound: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error in privmsg handler: {e}")
|
logger.error(f"Error in privmsg handler: {e}")
|
||||||
|
|
||||||
@@ -1728,14 +1817,12 @@ if os.name == 'nt':
|
|||||||
ctypes.windll.user32.SetProcessDPIAware()
|
ctypes.windll.user32.SetProcessDPIAware()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
else:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
enable_high_dpi()
|
enable_high_dpi()
|
||||||
|
|
||||||
app = wx.App()
|
app = wx.App()
|
||||||
frame = IRCFrame()
|
frame = IRCFrame()
|
||||||
frame.SetIcon(wx.Icon(get_resource_path("icon.ico"), wx.BITMAP_TYPE_ICO))
|
frame.SetIcon(wx.Icon(get_resource_path("icon.ico"), wx.BITMAP_TYPE_ICO))
|
||||||
|
|||||||
BIN
src/sounds/balloon.wav
Normal file
BIN
src/sounds/balloon.wav
Normal file
Binary file not shown.
BIN
src/sounds/space-pdj.wav
Normal file
BIN
src/sounds/space-pdj.wav
Normal file
Binary file not shown.
BIN
src/sounds/startup.wav
Normal file
BIN
src/sounds/startup.wav
Normal file
Binary file not shown.
Reference in New Issue
Block a user