0
# Notification Components
1
2
Data classes and types for constructing rich, interactive notifications with buttons, reply fields, media attachments, and custom styling options.
3
4
## Capabilities
5
6
### Notification
7
8
The main data class representing a complete desktop notification with all possible configuration options and interaction handlers.
9
10
```python { .api }
11
@dataclass(frozen=True)
12
class Notification:
13
title: str
14
"""Notification title"""
15
16
message: str
17
"""Notification message body"""
18
19
urgency: Urgency = field(default=Urgency.Normal, repr=False)
20
"""
21
Notification urgency level affecting appearance and behavior.
22
Determines stickiness, visual styling, and ability to break through
23
do-not-disturb settings.
24
"""
25
26
icon: Icon | None = field(default=None, repr=False)
27
"""Custom icon for this notification (overrides app default)"""
28
29
buttons: tuple[Button, ...] = field(default_factory=tuple, repr=False)
30
"""Interactive buttons for user actions (platform limits apply)"""
31
32
buttons_dict: immutabledict[str, Button] = field(
33
default_factory=immutabledict, init=False, repr=False, compare=False
34
)
35
"""Buttons indexed by identifier for lookup (computed automatically)"""
36
37
reply_field: ReplyField | None = field(default=None, repr=False)
38
"""Text input field for user responses"""
39
40
on_dispatched: Callable[[], Any] | None = field(default=None, repr=False)
41
"""Callback when notification is sent to notification server"""
42
43
on_clicked: Callable[[], Any] | None = field(default=None, repr=False)
44
"""Callback when notification body is clicked"""
45
46
on_dismissed: Callable[[], Any] | None = field(default=None, repr=False)
47
"""Callback when notification is dismissed"""
48
49
attachment: Attachment | None = field(default=None, repr=False)
50
"""File attachment displayed as preview (platform-dependent file types)"""
51
52
sound: Sound | None = field(default=None, repr=False)
53
"""Custom sound to play when notification appears"""
54
55
thread: str | None = field(default=None, repr=False)
56
"""
57
Thread identifier for grouping related notifications.
58
Useful for chat apps, email threads, or any sequence of related notifications.
59
"""
60
61
timeout: int = field(default=-1, repr=False)
62
"""
63
Display duration in seconds.
64
-1 uses system default, 0 makes sticky, positive values set timeout.
65
"""
66
67
identifier: str = field(default_factory=uuid_str)
68
"""
69
Unique notification identifier.
70
Generated automatically if not provided, used for callbacks and clearing.
71
"""
72
```
73
74
### Button
75
76
Interactive button component for notifications enabling custom user actions with callback support.
77
78
```python { .api }
79
@dataclass(frozen=True)
80
class Button:
81
title: str
82
"""
83
Localized button text displayed to user.
84
Should be concise and action-oriented (e.g., "Reply", "Mark as Read").
85
"""
86
87
on_pressed: Callable[[], Any] | None = None
88
"""
89
Callback function invoked when button is pressed.
90
Receives no arguments, use closures to capture context.
91
"""
92
93
identifier: str = dataclasses.field(default_factory=uuid_str)
94
"""
95
Unique button identifier for callback routing.
96
Generated automatically if not provided.
97
"""
98
```
99
100
### ReplyField
101
102
Text input field component enabling direct text responses from notifications without opening the main application.
103
104
```python { .api }
105
@dataclass(frozen=True)
106
class ReplyField:
107
title: str = "Reply"
108
"""
109
Title for the reply field itself.
110
On macOS, this becomes the title of the button that reveals the field.
111
"""
112
113
button_title: str = "Send"
114
"""
115
Title of the button that submits the reply text.
116
Should be localized for the target user's language.
117
"""
118
119
on_replied: Callable[[str], Any] | None = None
120
"""
121
Callback function invoked when user submits reply.
122
Receives the reply text as a string argument.
123
"""
124
```
125
126
### Exception Classes
127
128
Exception types for notification authorization and delivery failures.
129
130
```python { .api }
131
class AuthorisationError(Exception):
132
"""
133
Raised when application lacks permission to send notifications.
134
135
Typically occurs on first run before user grants permission,
136
or if user later revokes notification permissions in system settings.
137
"""
138
```
139
140
## Usage Examples
141
142
### Basic Notification Construction
143
144
```python
145
from desktop_notifier import Notification, Urgency, DEFAULT_SOUND
146
147
# Simple notification
148
notification = Notification(
149
title="System Alert",
150
message="Your backup has completed successfully",
151
urgency=Urgency.Normal
152
)
153
154
# Rich notification with all options
155
rich_notification = Notification(
156
title="New Message",
157
message="John Doe: Hey, are you available for a quick call?",
158
urgency=Urgency.Critical,
159
sound=DEFAULT_SOUND,
160
thread="chat_john_doe",
161
timeout=30,
162
on_clicked=lambda: print("User clicked the notification"),
163
on_dismissed=lambda: print("User dismissed the notification")
164
)
165
```
166
167
### Interactive Notifications with Buttons
168
169
```python
170
from desktop_notifier import Notification, Button, Urgency
171
172
def handle_approve():
173
print("User approved the request")
174
# Add approval logic here
175
176
def handle_deny():
177
print("User denied the request")
178
# Add denial logic here
179
180
# Create notification with action buttons
181
notification = Notification(
182
title="Permission Request",
183
message="App wants to access your camera. Allow access?",
184
urgency=Urgency.Critical,
185
buttons=(
186
Button(title="Allow", on_pressed=handle_approve),
187
Button(title="Deny", on_pressed=handle_deny),
188
),
189
timeout=60 # Give user time to respond
190
)
191
```
192
193
### Reply Field for Text Input
194
195
```python
196
from desktop_notifier import Notification, ReplyField, Button
197
198
def handle_reply(reply_text: str):
199
print(f"User replied: {reply_text}")
200
# Process the reply (send to chat, save to database, etc.)
201
202
def handle_mark_read():
203
print("Message marked as read")
204
205
# Notification with reply capability (like messaging apps)
206
message_notification = Notification(
207
title="Sarah Connor",
208
message="The meeting has been moved to 3 PM. Can you make it?",
209
buttons=(
210
Button(title="Mark as Read", on_pressed=handle_mark_read),
211
),
212
reply_field=ReplyField(
213
title="Reply",
214
button_title="Send",
215
on_replied=handle_reply
216
),
217
thread="chat_sarah_connor"
218
)
219
```
220
221
### Notification with Media Attachment
222
223
```python
224
from desktop_notifier import Notification, Attachment
225
from pathlib import Path
226
227
# Notification with image preview
228
image_notification = Notification(
229
title="Photo Shared",
230
message="New photo from your camera roll",
231
attachment=Attachment(path=Path("/path/to/photo.jpg")),
232
on_clicked=lambda: print("User wants to view the photo")
233
)
234
235
# Notification with document attachment
236
document_notification = Notification(
237
title="Document Ready",
238
message="Your report has been generated",
239
attachment=Attachment(uri="file:///path/to/report.pdf")
240
)
241
```
242
243
### Threaded Notifications
244
245
```python
246
from desktop_notifier import DesktopNotifier, Notification
247
import asyncio
248
249
async def send_conversation_notifications():
250
notifier = DesktopNotifier(app_name="Chat App")
251
252
# All notifications with same thread ID will be grouped together
253
thread_id = "conversation_team_alpha"
254
255
await notifier.send_notification(Notification(
256
title="Team Alpha",
257
message="Alice: Has everyone reviewed the proposal?",
258
thread=thread_id
259
))
260
261
await asyncio.sleep(2)
262
263
await notifier.send_notification(Notification(
264
title="Team Alpha",
265
message="Bob: I'm still going through it, need 10 more minutes",
266
thread=thread_id
267
))
268
269
await asyncio.sleep(2)
270
271
await notifier.send_notification(Notification(
272
title="Team Alpha",
273
message="Carol: Looks good to me, just one minor suggestion",
274
thread=thread_id
275
))
276
277
asyncio.run(send_conversation_notifications())
278
```
279
280
### Custom Notification Callbacks
281
282
```python
283
from desktop_notifier import DesktopNotifier, Notification
284
import asyncio
285
286
class NotificationHandler:
287
def __init__(self, app_name: str):
288
self.notifier = DesktopNotifier(app_name=app_name)
289
self.active_notifications = {}
290
291
async def send_with_tracking(self, title: str, message: str):
292
notification = Notification(
293
title=title,
294
message=message,
295
on_dispatched=self.on_notification_sent,
296
on_clicked=self.on_notification_clicked,
297
on_dismissed=self.on_notification_dismissed
298
)
299
300
notification_id = await self.notifier.send_notification(notification)
301
self.active_notifications[notification_id] = {
302
'title': title,
303
'sent_at': asyncio.get_event_loop().time()
304
}
305
return notification_id
306
307
def on_notification_sent(self):
308
print("Notification successfully displayed to user")
309
310
def on_notification_clicked(self):
311
print("User interacted with notification")
312
313
def on_notification_dismissed(self):
314
print("User dismissed notification")
315
316
# Usage
317
async def main():
318
handler = NotificationHandler("Tracking App")
319
await handler.send_with_tracking(
320
"Task Complete",
321
"Your data processing job has finished"
322
)
323
324
asyncio.run(main())
325
```