CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-desktop-notifier

Python library for cross-platform desktop notifications

Pending
Overview
Eval results
Files

notification-components.mddocs/

Notification Components

Data classes and types for constructing rich, interactive notifications with buttons, reply fields, media attachments, and custom styling options.

Capabilities

Notification

The main data class representing a complete desktop notification with all possible configuration options and interaction handlers.

@dataclass(frozen=True)
class Notification:
    title: str
    """Notification title"""
    
    message: str
    """Notification message body"""
    
    urgency: Urgency = field(default=Urgency.Normal, repr=False)
    """
    Notification urgency level affecting appearance and behavior.
    Determines stickiness, visual styling, and ability to break through 
    do-not-disturb settings.
    """
    
    icon: Icon | None = field(default=None, repr=False)
    """Custom icon for this notification (overrides app default)"""
    
    buttons: tuple[Button, ...] = field(default_factory=tuple, repr=False)
    """Interactive buttons for user actions (platform limits apply)"""
    
    buttons_dict: immutabledict[str, Button] = field(
        default_factory=immutabledict, init=False, repr=False, compare=False
    )
    """Buttons indexed by identifier for lookup (computed automatically)"""
    
    reply_field: ReplyField | None = field(default=None, repr=False)
    """Text input field for user responses"""
    
    on_dispatched: Callable[[], Any] | None = field(default=None, repr=False)
    """Callback when notification is sent to notification server"""
    
    on_clicked: Callable[[], Any] | None = field(default=None, repr=False)
    """Callback when notification body is clicked"""
    
    on_dismissed: Callable[[], Any] | None = field(default=None, repr=False)
    """Callback when notification is dismissed"""
    
    attachment: Attachment | None = field(default=None, repr=False)
    """File attachment displayed as preview (platform-dependent file types)"""
    
    sound: Sound | None = field(default=None, repr=False)
    """Custom sound to play when notification appears"""
    
    thread: str | None = field(default=None, repr=False)
    """
    Thread identifier for grouping related notifications.
    Useful for chat apps, email threads, or any sequence of related notifications.
    """
    
    timeout: int = field(default=-1, repr=False)
    """
    Display duration in seconds.
    -1 uses system default, 0 makes sticky, positive values set timeout.
    """
    
    identifier: str = field(default_factory=uuid_str)
    """
    Unique notification identifier.
    Generated automatically if not provided, used for callbacks and clearing.
    """

Button

Interactive button component for notifications enabling custom user actions with callback support.

@dataclass(frozen=True)
class Button:
    title: str
    """
    Localized button text displayed to user.
    Should be concise and action-oriented (e.g., "Reply", "Mark as Read").
    """
    
    on_pressed: Callable[[], Any] | None = None
    """
    Callback function invoked when button is pressed.
    Receives no arguments, use closures to capture context.
    """
    
    identifier: str = dataclasses.field(default_factory=uuid_str)
    """
    Unique button identifier for callback routing.
    Generated automatically if not provided.
    """

ReplyField

Text input field component enabling direct text responses from notifications without opening the main application.

@dataclass(frozen=True)
class ReplyField:
    title: str = "Reply"
    """
    Title for the reply field itself.
    On macOS, this becomes the title of the button that reveals the field.
    """
    
    button_title: str = "Send"
    """
    Title of the button that submits the reply text.
    Should be localized for the target user's language.
    """
    
    on_replied: Callable[[str], Any] | None = None
    """
    Callback function invoked when user submits reply.
    Receives the reply text as a string argument.
    """

Exception Classes

Exception types for notification authorization and delivery failures.

class AuthorisationError(Exception):
    """
    Raised when application lacks permission to send notifications.
    
    Typically occurs on first run before user grants permission,
    or if user later revokes notification permissions in system settings.
    """

Usage Examples

Basic Notification Construction

from desktop_notifier import Notification, Urgency, DEFAULT_SOUND

# Simple notification
notification = Notification(
    title="System Alert",
    message="Your backup has completed successfully",
    urgency=Urgency.Normal
)

# Rich notification with all options
rich_notification = Notification(
    title="New Message",
    message="John Doe: Hey, are you available for a quick call?",
    urgency=Urgency.Critical,
    sound=DEFAULT_SOUND,
    thread="chat_john_doe",
    timeout=30,
    on_clicked=lambda: print("User clicked the notification"),
    on_dismissed=lambda: print("User dismissed the notification")
)

Interactive Notifications with Buttons

from desktop_notifier import Notification, Button, Urgency

def handle_approve():
    print("User approved the request")
    # Add approval logic here

def handle_deny():
    print("User denied the request")
    # Add denial logic here

# Create notification with action buttons
notification = Notification(
    title="Permission Request",
    message="App wants to access your camera. Allow access?",
    urgency=Urgency.Critical,
    buttons=(
        Button(title="Allow", on_pressed=handle_approve),
        Button(title="Deny", on_pressed=handle_deny),
    ),
    timeout=60  # Give user time to respond
)

Reply Field for Text Input

from desktop_notifier import Notification, ReplyField, Button

def handle_reply(reply_text: str):
    print(f"User replied: {reply_text}")
    # Process the reply (send to chat, save to database, etc.)

def handle_mark_read():
    print("Message marked as read")

# Notification with reply capability (like messaging apps)
message_notification = Notification(
    title="Sarah Connor",
    message="The meeting has been moved to 3 PM. Can you make it?",
    buttons=(
        Button(title="Mark as Read", on_pressed=handle_mark_read),
    ),
    reply_field=ReplyField(
        title="Reply",
        button_title="Send",
        on_replied=handle_reply
    ),
    thread="chat_sarah_connor"
)

Notification with Media Attachment

from desktop_notifier import Notification, Attachment
from pathlib import Path

# Notification with image preview
image_notification = Notification(
    title="Photo Shared",
    message="New photo from your camera roll",
    attachment=Attachment(path=Path("/path/to/photo.jpg")),
    on_clicked=lambda: print("User wants to view the photo")
)

# Notification with document attachment
document_notification = Notification(
    title="Document Ready",
    message="Your report has been generated",
    attachment=Attachment(uri="file:///path/to/report.pdf")
)

Threaded Notifications

from desktop_notifier import DesktopNotifier, Notification
import asyncio

async def send_conversation_notifications():
    notifier = DesktopNotifier(app_name="Chat App")
    
    # All notifications with same thread ID will be grouped together
    thread_id = "conversation_team_alpha"
    
    await notifier.send_notification(Notification(
        title="Team Alpha",
        message="Alice: Has everyone reviewed the proposal?",
        thread=thread_id
    ))
    
    await asyncio.sleep(2)
    
    await notifier.send_notification(Notification(
        title="Team Alpha", 
        message="Bob: I'm still going through it, need 10 more minutes",
        thread=thread_id
    ))
    
    await asyncio.sleep(2)
    
    await notifier.send_notification(Notification(
        title="Team Alpha",
        message="Carol: Looks good to me, just one minor suggestion",
        thread=thread_id
    ))

asyncio.run(send_conversation_notifications())

Custom Notification Callbacks

from desktop_notifier import DesktopNotifier, Notification
import asyncio

class NotificationHandler:
    def __init__(self, app_name: str):
        self.notifier = DesktopNotifier(app_name=app_name)
        self.active_notifications = {}
    
    async def send_with_tracking(self, title: str, message: str):
        notification = Notification(
            title=title,
            message=message,
            on_dispatched=self.on_notification_sent,
            on_clicked=self.on_notification_clicked,
            on_dismissed=self.on_notification_dismissed
        )
        
        notification_id = await self.notifier.send_notification(notification)
        self.active_notifications[notification_id] = {
            'title': title,
            'sent_at': asyncio.get_event_loop().time()
        }
        return notification_id
    
    def on_notification_sent(self):
        print("Notification successfully displayed to user")
    
    def on_notification_clicked(self):
        print("User interacted with notification")
    
    def on_notification_dismissed(self):
        print("User dismissed notification")

# Usage
async def main():
    handler = NotificationHandler("Tracking App")
    await handler.send_with_tracking(
        "Task Complete", 
        "Your data processing job has finished"
    )

asyncio.run(main())

Install with Tessl CLI

npx tessl i tessl/pypi-desktop-notifier

docs

core-api.md

enums-constants.md

index.md

notification-components.md

resources.md

tile.json