feat(sounds): add per-JID mute support and enhance playback behavior
Some users requested the ability to mute notifications from specific JIDs without disabling all sounds. This change introduces `mute` and `unmute` commands to allow fine-grained control over sound playback. While implementing this, the sound path expansion logic was improved by switching to `pathlib` with `lru_cache` for efficiency and clarity. Playback is now also rate-limited to avoid overlapping audio triggers from rapid message bursts. This lays the groundwork for more user-specific customization of sound notifications while keeping the experience smooth and reliable. Additionally the bug with ignoring sound's `kind` was fixed: now the plugin respects specific sounds set for rooms, chats and private messages within rooms. Finally, code was formatted via Black to avoid ambiguity in the formatting.
This commit is contained in:
120
stable/sounds.py
120
stable/sounds.py
@@ -5,20 +5,43 @@ Default settings require mpg123 to be installed on the host system:
|
|||||||
brew install mpg123
|
brew install mpg123
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import subprocess
|
from functools import lru_cache
|
||||||
from os.path import expanduser
|
from pathlib import Path
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
import prof
|
import prof
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
|
||||||
|
_last_played = {}
|
||||||
|
|
||||||
|
|
||||||
def _play_sound(soundfile):
|
@lru_cache(maxsize=16)
|
||||||
if soundfile.startswith("~/"):
|
def get_expanded_path(path_str: str) -> Tuple[Path, str]:
|
||||||
soundfilenew = soundfile.replace("~", expanduser("~"), 1)
|
path = Path(path_str).expanduser()
|
||||||
else:
|
return path, str(path)
|
||||||
soundfilenew = soundfile
|
|
||||||
|
|
||||||
|
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")
|
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):
|
def _cmd_sounds(arg1=None, arg2=None, arg3=None):
|
||||||
@@ -66,7 +89,11 @@ def _cmd_sounds(arg1=None, arg2=None, arg3=None):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if arg1 == "test":
|
if arg1 == "test":
|
||||||
if arg2 not in ("chat", "private", "room",):
|
if arg2 not in (
|
||||||
|
"chat",
|
||||||
|
"private",
|
||||||
|
"room",
|
||||||
|
):
|
||||||
prof.cons_bad_cmd_usage("/sounds")
|
prof.cons_bad_cmd_usage("/sounds")
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -80,7 +107,7 @@ def _cmd_sounds(arg1=None, arg2=None, arg3=None):
|
|||||||
prof.cons_show("No sound file for " + arg2)
|
prof.cons_show("No sound file for " + arg2)
|
||||||
return
|
return
|
||||||
|
|
||||||
_play_sound(soundfile)
|
_play_sound(kind=arg2)
|
||||||
return
|
return
|
||||||
|
|
||||||
if arg1 == "set":
|
if arg1 == "set":
|
||||||
@@ -148,6 +175,22 @@ def _cmd_sounds(arg1=None, arg2=None, arg3=None):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if arg1 == "mute":
|
||||||
|
if not arg2:
|
||||||
|
prof.cons_bad_cmd_usage("/sounds mute <jid>")
|
||||||
|
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 <jid>")
|
||||||
|
else:
|
||||||
|
prof.settings_string_list_remove("sounds", "muted", arg2)
|
||||||
|
prof.cons_show("Unmuted JID: " + arg2)
|
||||||
|
return
|
||||||
|
|
||||||
prof.cons_bad_cmd_usage("/sounds")
|
prof.cons_bad_cmd_usage("/sounds")
|
||||||
|
|
||||||
|
|
||||||
@@ -164,12 +207,15 @@ def prof_init(version, status, account_name, fulljid):
|
|||||||
"/sounds clear chat",
|
"/sounds clear chat",
|
||||||
"/sounds clear room",
|
"/sounds clear room",
|
||||||
"/sounds clear private",
|
"/sounds clear private",
|
||||||
"/sounds rooms add <roomjid>"
|
"/sounds rooms add <roomjid>",
|
||||||
]
|
]
|
||||||
description = "Play sounds on various Profanity events. Calling with no args shows current sound files."
|
description = "Play sounds on various Profanity events. Calling with no args shows current sound files."
|
||||||
args = [
|
args = [
|
||||||
["on|off", "Enable or disable playing sounds."],
|
["on|off", "Enable or disable playing sounds."],
|
||||||
[ "set audioplayer <cmd>", "Commmand line of audio player to use for playing sound files. Defaults to mpg123."],
|
[
|
||||||
|
"set audioplayer <cmd>",
|
||||||
|
"Commmand line of audio player to use for playing sound files. Defaults to mpg123.",
|
||||||
|
],
|
||||||
["reset audioplayer", "Resets the audio player to the default mpg123."],
|
["reset audioplayer", "Resets the audio player to the default mpg123."],
|
||||||
["set chat <file>", "Path to sound file to play on chat messages."],
|
["set chat <file>", "Path to sound file to play on chat messages."],
|
||||||
["set room <file>", "Path to sound file to play on room messages."],
|
["set room <file>", "Path to sound file to play on room messages."],
|
||||||
@@ -178,9 +224,18 @@ def prof_init(version, status, account_name, fulljid):
|
|||||||
["clear chat", "Remove the sound for chat messages."],
|
["clear chat", "Remove the sound for chat messages."],
|
||||||
["clear room", "Remove the sound for room messages."],
|
["clear room", "Remove the sound for room messages."],
|
||||||
["clear private", "Remove the sound for private messages."],
|
["clear private", "Remove the sound for private messages."],
|
||||||
[ "rooms add <roomjid>", "Add the room to the list that will play the room sound."],
|
[
|
||||||
[ "rooms remove <roomjid>", "Remove the room from the list that will play the room sound."],
|
"rooms add <roomjid>",
|
||||||
[ "rooms clear", "Clear the room list, all rooms will play sounds on new messages."]
|
"Add the room to the list that will play the room sound.",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"rooms remove <roomjid>",
|
||||||
|
"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 = [
|
examples = [
|
||||||
"/sounds set audioplayer paplay",
|
"/sounds set audioplayer paplay",
|
||||||
@@ -190,12 +245,16 @@ def prof_init(version, status, account_name, fulljid):
|
|||||||
"/sounds test private",
|
"/sounds test private",
|
||||||
"/sounds remove private",
|
"/sounds remove private",
|
||||||
"/sounds rooms add myroom@conference.server.org",
|
"/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", "clear", "on", "off", "rooms", "reset", "mute", "unmute"]
|
||||||
|
)
|
||||||
prof.completer_add("/sounds set", ["chat", "room", "private", "audioplayer"])
|
prof.completer_add("/sounds set", ["chat", "room", "private", "audioplayer"])
|
||||||
prof.completer_add("/sounds reset", ["audioplayer"])
|
prof.completer_add("/sounds reset", ["audioplayer"])
|
||||||
prof.completer_add("/sounds test", ["chat", "room", "private"])
|
prof.completer_add("/sounds test", ["chat", "room", "private"])
|
||||||
@@ -211,11 +270,11 @@ def prof_post_chat_message_display(barejid, resource, message):
|
|||||||
if not enabled:
|
if not enabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
soundfile = prof.settings_string_get("sounds", "chat", None)
|
muted = prof.settings_string_list_get("sounds", "muted")
|
||||||
if not soundfile:
|
if muted and barejid in muted:
|
||||||
return
|
return
|
||||||
|
|
||||||
_play_sound(soundfile)
|
_play_sound(kind="chat")
|
||||||
|
|
||||||
|
|
||||||
def prof_post_room_message_display(barejid, nick, message):
|
def prof_post_room_message_display(barejid, nick, message):
|
||||||
@@ -224,21 +283,21 @@ def prof_post_room_message_display(barejid, nick, message):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if my_nick == nick:
|
if my_nick == nick:
|
||||||
return;
|
return
|
||||||
|
|
||||||
enabled = prof.settings_boolean_get("sounds", "enabled", False)
|
enabled = prof.settings_boolean_get("sounds", "enabled", False)
|
||||||
if not enabled:
|
if not enabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
soundfile = prof.settings_string_get("sounds", "room", None)
|
|
||||||
if not soundfile:
|
|
||||||
return
|
|
||||||
|
|
||||||
roomlist = prof.settings_string_list_get("sounds", "rooms")
|
roomlist = prof.settings_string_list_get("sounds", "rooms")
|
||||||
if roomlist and (barejid not in roomlist):
|
if roomlist and (barejid not in roomlist):
|
||||||
return
|
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):
|
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:
|
if not enabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
soundfile = prof.settings_string_get("sounds", "private", None)
|
muted = prof.settings_string_list_get("sounds", "muted")
|
||||||
if soundfile:
|
if muted and barejid in muted:
|
||||||
_play_sound(soundfile)
|
return
|
||||||
|
|
||||||
|
_play_sound(kind="private")
|
||||||
|
|||||||
Reference in New Issue
Block a user