diff --git a/stable/sounds.py b/stable/sounds.py index 608828b..8e007bb 100644 --- a/stable/sounds.py +++ b/stable/sounds.py @@ -5,20 +5,43 @@ Default settings require mpg123 to be installed on the host system: brew install mpg123 """ -import subprocess -from os.path import expanduser +from functools import lru_cache +from pathlib import Path +from typing import Tuple import prof +import subprocess +import time + +_last_played = {} -def _play_sound(soundfile): - if soundfile.startswith("~/"): - soundfilenew = soundfile.replace("~", expanduser("~"), 1) - else: - soundfilenew = soundfile +@lru_cache(maxsize=16) +def get_expanded_path(path_str: str) -> Tuple[Path, str]: + path = Path(path_str).expanduser() + return path, str(path) + + +def _play_sound_file(soundfile: str): + soundfile_path, soundfile_str = get_expanded_path(soundfile) + + if not soundfile_path.exists(): + prof.cons_show(f"Sound file not found: {soundfile_str}") + return + + now = time.monotonic() + if _last_played.get(soundfile_str, 0) + 0.5 > now: + return # skip if played too recently + _last_played[soundfile_str] = now audioplayer = prof.settings_string_get("sounds", "audioplayer", "mpg123 -q") - subprocess.Popen([e for e in audioplayer.split(" ") + [soundfilenew]]) + subprocess.Popen([*audioplayer.split(), soundfile_str], start_new_session=True) + + +def _play_sound(kind: str = "chat"): + soundfile = prof.settings_string_get("sounds", kind, None) + if soundfile: + _play_sound_file(soundfile) def _cmd_sounds(arg1=None, arg2=None, arg3=None): @@ -52,7 +75,7 @@ def _cmd_sounds(arg1=None, arg2=None, arg3=None): else: prof.cons_show("No sounds set.") - + return if arg1 == "on": @@ -66,10 +89,14 @@ def _cmd_sounds(arg1=None, arg2=None, arg3=None): return if arg1 == "test": - if arg2 not in ("chat", "private", "room",): + if arg2 not in ( + "chat", + "private", + "room", + ): prof.cons_bad_cmd_usage("/sounds") return - + enabled = prof.settings_boolean_get("sounds", "enabled", False) if not enabled: prof.cons_show("Sounds disabled") @@ -80,7 +107,7 @@ def _cmd_sounds(arg1=None, arg2=None, arg3=None): prof.cons_show("No sound file for " + arg2) return - _play_sound(soundfile) + _play_sound(kind=arg2) return if arg1 == "set": @@ -148,11 +175,27 @@ def _cmd_sounds(arg1=None, arg2=None, arg3=None): return + if arg1 == "mute": + if not arg2: + prof.cons_bad_cmd_usage("/sounds mute ") + else: + prof.settings_string_list_add("sounds", "muted", arg2) + prof.cons_show("Muted JID: " + arg2) + return + + if arg1 == "unmute": + if not arg2: + prof.cons_bad_cmd_usage("/sounds unmute ") + else: + prof.settings_string_list_remove("sounds", "muted", arg2) + prof.cons_show("Unmuted JID: " + arg2) + return + prof.cons_bad_cmd_usage("/sounds") def prof_init(version, status, account_name, fulljid): - synopsis = [ + synopsis = [ "/sounds", "/sounds on|off", "/sounds set audioplayer ", @@ -164,23 +207,35 @@ def prof_init(version, status, account_name, fulljid): "/sounds clear chat", "/sounds clear room", "/sounds clear private", - "/sounds rooms add " + "/sounds rooms add ", ] description = "Play sounds on various Profanity events. Calling with no args shows current sound files." args = [ - [ "on|off", "Enable or disable playing sounds." ], - [ "set audioplayer ", "Commmand line of audio player to use for playing sound files. Defaults to mpg123."], - [ "reset audioplayer", "Resets the audio player to the default mpg123." ], - [ "set chat ", "Path to sound file to play on chat messages." ], - [ "set room ", "Path to sound file to play on room messages." ], - [ "set private ", "Path to sound file to play on private room messages." ], - [ "test private|room|chat", "Test the configured audio player and sound file."], - [ "clear chat", "Remove the sound for chat messages." ], - [ "clear room", "Remove the sound for room messages." ], - [ "clear private", "Remove the sound for private messages." ], - [ "rooms add ", "Add the room to the list that will play the room sound."], - [ "rooms remove ", "Remove the room from the list that will play the room sound."], - [ "rooms clear", "Clear the room list, all rooms will play sounds on new messages."] + ["on|off", "Enable or disable playing sounds."], + [ + "set audioplayer ", + "Commmand line of audio player to use for playing sound files. Defaults to mpg123.", + ], + ["reset audioplayer", "Resets the audio player to the default mpg123."], + ["set chat ", "Path to sound file to play on chat messages."], + ["set room ", "Path to sound file to play on room messages."], + ["set private ", "Path to sound file to play on private room messages."], + ["test private|room|chat", "Test the configured audio player and sound file."], + ["clear chat", "Remove the sound for chat messages."], + ["clear room", "Remove the sound for room messages."], + ["clear private", "Remove the sound for private messages."], + [ + "rooms add ", + "Add the room to the list that will play the room sound.", + ], + [ + "rooms remove ", + "Remove the room from the list that will play the room sound.", + ], + [ + "rooms clear", + "Clear the room list, all rooms will play sounds on new messages.", + ], ] examples = [ "/sounds set audioplayer paplay", @@ -190,17 +245,21 @@ def prof_init(version, status, account_name, fulljid): "/sounds test private", "/sounds remove private", "/sounds rooms add myroom@conference.server.org", - "/sounds on" + "/sounds on", ] - prof.register_command("/sounds", 0, 3, synopsis, description, args, examples, _cmd_sounds) + prof.register_command( + "/sounds", 0, 3, synopsis, description, args, examples, _cmd_sounds + ) - prof.completer_add("/sounds", [ "set", "clear", "on", "off", "rooms", "reset" ]) - prof.completer_add("/sounds set", [ "chat", "room", "private", "audioplayer" ]) - prof.completer_add("/sounds reset", [ "audioplayer" ]) - prof.completer_add("/sounds test", [ "chat", "room", "private" ]) - prof.completer_add("/sounds clear", [ "chat", "room", "private" ]) - prof.completer_add("/sounds rooms", [ "add", "remove", "clear" ]) + prof.completer_add( + "/sounds", ["set", "clear", "on", "off", "rooms", "reset", "mute", "unmute"] + ) + prof.completer_add("/sounds set", ["chat", "room", "private", "audioplayer"]) + prof.completer_add("/sounds reset", ["audioplayer"]) + prof.completer_add("/sounds test", ["chat", "room", "private"]) + prof.completer_add("/sounds clear", ["chat", "room", "private"]) + prof.completer_add("/sounds rooms", ["add", "remove", "clear"]) prof.filepath_completer_add("/sounds set chat") prof.filepath_completer_add("/sounds set room") prof.filepath_completer_add("/sounds set private") @@ -211,11 +270,11 @@ def prof_post_chat_message_display(barejid, resource, message): if not enabled: return - soundfile = prof.settings_string_get("sounds", "chat", None) - if not soundfile: + muted = prof.settings_string_list_get("sounds", "muted") + if muted and barejid in muted: return - _play_sound(soundfile) + _play_sound(kind="chat") def prof_post_room_message_display(barejid, nick, message): @@ -224,21 +283,21 @@ def prof_post_room_message_display(barejid, nick, message): return if my_nick == nick: - return; + return enabled = prof.settings_boolean_get("sounds", "enabled", False) if not enabled: return - soundfile = prof.settings_string_get("sounds", "room", None) - if not soundfile: - return - roomlist = prof.settings_string_list_get("sounds", "rooms") if roomlist and (barejid not in roomlist): return - _play_sound(soundfile) + muted = prof.settings_string_list_get("sounds", "muted") + if muted and barejid in muted: + return + + _play_sound(soundfile, kind="room") def prof_post_priv_message_display(barejid, nick, message): @@ -246,7 +305,8 @@ def prof_post_priv_message_display(barejid, nick, message): if not enabled: return - soundfile = prof.settings_string_get("sounds", "private", None) - if soundfile: - _play_sound(soundfile) + muted = prof.settings_string_list_get("sounds", "muted") + if muted and barejid in muted: + return + _play_sound(kind="private")