Make desktop notifications follow push rules

This commit is contained in:
miruka 2020-11-03 07:29:32 -04:00
parent 2ef1edb3dc
commit 3dd12691b8
5 changed files with 78 additions and 63 deletions

View File

@ -4,17 +4,22 @@
- PCN error handling - PCN error handling
- Change docs linking to dev branch back to master - Change docs linking to dev branch back to master
- Implement fallback QML notifications, usable if dbus isn't available
- profiles missing in notifications
- option to use plaintext notifications
- Notification urgency level (plyer)?
- Rename the alertOn(Mention/Message)ForMsec options, default Message to non-0
- Make local unread counter an optional turned off by default
- fix DeviceSection padding - fix DeviceSection padding
- Implement fallback QML notifications, usable if dbus isn't available - Implement fallback QML notifications, usable if dbus isn't available
- annoying tooltips when menu open - annoying tooltips when menu open
- profiles missing in notifications
- add http_proxy support - add http_proxy support
- image viewer: can't expand image in reduced window layout - image viewer: can't expand image in reduced window layout
- Encrypted rooms don't show invites in member list after Mirage restart - Encrypted rooms don't show invites in member list after Mirage restart
- Room display name not updated when someone removes theirs - Room display name not updated when someone removes theirs
- Fix right margin of own `<image url>\n<image url>` messages - Fix right margin of own `<image url>\n<image url>` messages
- option to use plaintext notifications
- warn on ambiguously activated shortcut - warn on ambiguously activated shortcut
- SSO device delete? - SSO device delete?

View File

@ -209,8 +209,7 @@ class MatrixClient(nio.AsyncClient):
self.unassigned_event_last_read_by: DefaultDict[str, Dict[str, int]] =\ self.unassigned_event_last_read_by: DefaultDict[str, Dict[str, int]] =\
DefaultDict(dict) DefaultDict(dict)
self.previous_server_unreads: Dict[str, int] = {} self.push_rules: nio.PushRulesEvent = nio.PushRulesEvent()
self.previous_server_highlights: Dict[str, int] = {}
# {room_id: event} # {room_id: event}
self.power_level_events: Dict[str, nio.PowerLevelsEvent] = {} self.power_level_events: Dict[str, nio.PowerLevelsEvent] = {}
@ -2247,55 +2246,52 @@ class MatrixClient(nio.AsyncClient):
await self.update_account_unread_counts() await self.update_account_unread_counts()
return item return item
self.models[self.user_id, "rooms"][room.room_id].local_unreads = True
await self.update_account_unread_counts()
# Alerts & notifications # Alerts & notifications
room_item = self.models[self.user_id, "rooms"][room.room_id] name = self.models["accounts"][self.user_id].display_name
nio_rule = self.push_rules.global_rules.matching_rule(ev, room, name)
unread = \ if not nio_rule:
room_item.unreads and \ return item
room_item.unreads != \
self.previous_server_unreads.get(room.room_id, 0)
highlight = \ model = self.models[self.user_id, "pushrules"]
room_item.highlights and \ rule = model[nio_rule.kind.value, nio_rule.id]
room_item.highlights != \
self.previous_server_highlights.get(room.room_id, 0)
self.previous_server_unreads[room.room_id] = room_item.unreads if not rule.notify and not rule.highlight:
self.previous_server_highlights[room.room_id] = room_item.highlights return item
room_item.local_unreads = True sender = item.sender_name or item.sender_id
if unread or highlight: if isinstance(ev, nio.RoomMessageEmote):
members = self.models[self.user_id, room.room_id, "members"] body = f"<i>{sender} {item.inline_content}</i>"
room_name = room.display_name elif not isinstance(ev, nio.RoomMessage):
sender = item.sender_name or item.sender_id body = item.inline_content.replace(
"%1", item.sender_name or item.sender_id,
if isinstance(ev, nio.RoomMessageEmote): ).replace(
body = f"<i>{sender} {item.inline_content}</i>" "%2", item.target_name or item.target_id,
elif not isinstance(ev, nio.RoomMessage):
body = item.inline_content.replace(
"%1", item.sender_name or item.sender_id,
).replace(
"%2", item.target_name or item.target_id,
)
elif len(members) == 2 and room_name == sender:
body = item.inline_content
else:
body = f"{sender}: {item.inline_content}"
NotificationRequested(
id = item.id,
high_importance = highlight,
title = room_name,
body = body.replace("", "<br>")
.replace(" ⏎⏎ ", f"<br>{'' * 24}<br>"),
image = await self.get_notification_avatar(
mxc=item.sender_avatar, user_id=item.sender_id,
) if item.sender_avatar else "",
) )
elif room.member_count == 2 and room.display_name == sender:
body = item.inline_content
else:
body = f"{sender}: {item.inline_content}"
NotificationRequested(
id = item.id,
critical = rule.highlight,
bubble = rule.bubble,
sound = rule.sound,
urgency_hint = rule.urgency_hint,
title = room.display_name,
body = body.replace("", "<br>")
.replace(" ⏎⏎ ", f"<br>{'' * 24}<br>"),
image = await self.get_notification_avatar(
mxc=item.sender_avatar, user_id=item.sender_id,
) if item.sender_avatar else "",
)
await self.update_account_unread_counts()
return item return item

View File

@ -839,6 +839,8 @@ class NioCallbacks:
urgency_hint = hint, urgency_hint = hint,
) )
self.client.push_rules = ev
# Presence event callbacks # Presence event callbacks

View File

@ -31,17 +31,22 @@ class PyOtherSideEvent:
@dataclass @dataclass
class NotificationRequested(PyOtherSideEvent): class NotificationRequested(PyOtherSideEvent):
"""Request a notification and window manager alert to be shown. """Request a notification bubble, sound or window urgency hint.
Alerts set the urgency hint for compliant X11/Wayland window managers or Urgency hints usually flash or highlight the program's icon in a taskbar,
flash the program's taskbar icon on Windows. dock or panel.
""" """
id: str = field() id: str = field()
title: str = field() critical: bool = False
body: str = "" bubble: bool = False
image: Union[Path, str] = "" sound: bool = False
high_importance: bool = False urgency_hint: bool = False
# Bubble parameters
title: str = ""
body: str = ""
image: Union[Path, str] = ""
@dataclass @dataclass

View File

@ -8,26 +8,33 @@ import ".."
QtObject { QtObject {
signal deviceUpdateSignal(string forAccount) signal deviceUpdateSignal(string forAccount)
function onNotificationRequested(id, title, body, image, highImportance) { function onNotificationRequested(
id, critical, bubble, sound, urgencyHint, title, body, image,
) {
const level = window.notificationLevel const level = window.notificationLevel
if (level === Window.NotificationLevel.None) return if (level === Window.NotificationLevel.None) return
if (level === Window.MentionsKeywords && ! highImportance) return if (level === Window.MentionsKeywords && ! critical) return
if (window.notifiedIds.has(id)) return if (window.notifiedIds.has(id)) return
window.notifiedIds.add(id) window.notifiedIds.add(id)
window.notifiedIdsChanged() window.notifiedIdsChanged()
if (Qt.application.state === Qt.ApplicationActive) return if (Qt.application.state === Qt.ApplicationActive)
return
py.callCoro("desktop_notify", [title, body, image]) if (bubble)
py.callCoro("desktop_notify", [title, body, image])
const msec = if (urgencyHint) {
highImportance ? const msec =
window.settings.Notifications.urgent_alert_time * 1000 : critical ?
window.settings.Notifications.alert_time * 1000 window.settings.Notifications.urgent_alert_time * 1000 :
window.settings.Notifications.alert_time * 1000
if (msec) window.alert(msec === -1 ? 0 : msec) // -1 0 = no time out // -1 ? 0 for no time out : msec
if (msec !== 0) window.alert(msec === -1 ? 0 : msec)
}
} }
function onCoroutineDone(uuid, result, error, traceback) { function onCoroutineDone(uuid, result, error, traceback) {