or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

assets.mdattachments.mdconfiguration.mdcore-notifications.mdindex.mdlocalization.mdplugin-system.mdstorage.md
tile.json

plugin-system.mddocs/

Plugin Architecture

Base classes and managers for notification service plugins, configuration loaders, and attachment handlers. The plugin system enables extensibility and service discovery, allowing Apprise to support over 100 notification services through a unified interface.

Capabilities

NotifyBase Class

Base class for all notification service plugins providing common functionality and interface standardization.

class NotifyBase(URLBase):
    def send(self, body, title='', notify_type=NotifyType.INFO, attach=None, **kwargs):
        """
        Send notification through this service.
        
        Parameters:
        - body (str): Notification message content
        - title (str, optional): Notification title
        - notify_type (NotifyType): Type of notification
        - attach (AppriseAttachment, optional): File attachments
        - **kwargs: Service-specific parameters
        
        Returns:
        bool: True if notification was sent successfully
        """

    def url(self, privacy=False, *args, **kwargs):
        """
        Generate service URL for this notification service.
        
        Parameters:
        - privacy (bool): Mask sensitive information
        - *args, **kwargs: Additional URL parameters
        
        Returns:
        str: Service URL string
        """

    def throttle(self, last_notification=None):
        """
        Handle rate limiting for this service.
        
        Parameters:
        - last_notification (datetime, optional): Time of last notification
        
        Returns:
        float: Delay in seconds before next notification allowed
        """

    @property
    def enabled(self):
        """
        Service availability status.
        
        Returns:
        bool: True if service is available and properly configured
        """

    @property
    def templates(self):
        """
        URL templates supported by this service.
        
        Returns:
        tuple: URL template patterns for service configuration
        """

    @property
    def requirements(self):
        """
        Required dependencies for this service.
        
        Returns:
        dict: Required Python packages and versions
        """

    @property
    def notify_format(self):
        """
        Supported body format for notifications.
        
        Returns:
        NotifyFormat: Preferred format (TEXT, HTML, MARKDOWN)
        """

URLBase Class

Foundation class for URL parsing and handling used by all plugins.

class URLBase:
    def parse_url(self, url, verify_host=True):
        """
        Parse URL into components for service configuration.
        
        Parameters:
        - url (str): Service URL to parse
        - verify_host (bool): Verify host accessibility
        
        Returns:
        dict: Parsed URL components (scheme, host, path, params, etc.)
        """

    def parse_native_url(self, url):
        """
        Parse service-native URL format.
        
        Parameters:
        - url (str): Native service URL
        
        Returns:
        dict: Parsed native URL components
        """

    def url(self, privacy=False, *args, **kwargs):
        """
        Generate standardized URL for this service.
        
        Parameters:
        - privacy (bool): Mask sensitive information
        - *args, **kwargs: URL generation parameters
        
        Returns:
        str: Generated service URL
        """

    def __len__(self):
        """
        Get length representation of the service.
        
        Returns:
        int: Service length indicator
        """

    @property
    def app_id(self):
        """
        Application identifier for this service.
        
        Returns:
        str: Application ID
        """

    @property
    def app_desc(self):
        """
        Application description for this service.
        
        Returns:
        str: Application description
        """

    @property
    def service_name(self):
        """
        Human-readable service name.
        
        Returns:
        str: Service display name
        """

    @property
    def service_url(self):
        """
        Service homepage URL.
        
        Returns:
        str: URL to service documentation or homepage
        """

    @property
    def protocol(self):
        """
        URL protocol/scheme for this service.
        
        Returns:
        str: Protocol identifier (e.g., 'slack', 'discord', 'mailto')
        """

NotificationManager Class

Singleton manager for notification service plugin discovery and loading.

class NotificationManager:
    def load_modules(self, path=None, name=None):
        """
        Load notification plugin modules.
        
        Parameters:
        - path (str, optional): Path to plugin directory
        - name (str, optional): Specific plugin name to load
        
        Returns:
        bool: True if modules were loaded successfully
        """

    def plugins(self, include_disabled=True):
        """
        Get available notification plugins.
        
        Parameters:
        - include_disabled (bool): Include disabled/unavailable plugins
        
        Returns:
        dict: Available notification plugin classes keyed by protocol
        """

    def schemas(self):
        """
        Get supported notification URL schemas.
        
        Returns:
        list: Supported URL schemas for all loaded plugins
        """

Plugin Development

Creating Custom Notification Plugins

import apprise
from apprise.plugins.base import NotifyBase
from apprise import NotifyType, NotifyFormat

class NotifyCustomService(NotifyBase):
    """
    Custom notification service plugin.
    """
    
    # Service metadata
    service_name = 'Custom Service'
    service_url = 'https://custom-service.com'
    protocol = 'custom'
    
    # URL templates this plugin supports
    templates = (
        '{schema}://{user}:{password}@{host}',
        '{schema}://{token}@{host}/{channel}',
    )
    
    # Required dependencies
    requirements = {
        'requests': '',  # Any version
        'custom-sdk': '>=1.0.0',  # Minimum version
    }
    
    # Supported notification format
    notify_format = NotifyFormat.MARKDOWN
    
    def __init__(self, user=None, password=None, token=None, 
                 host=None, channel=None, **kwargs):
        """
        Initialize custom service plugin.
        """
        super().__init__(**kwargs)
        
        self.user = user
        self.password = password  
        self.token = token
        self.host = host
        self.channel = channel
        
    def send(self, body, title='', notify_type=NotifyType.INFO, 
             attach=None, **kwargs):
        """
        Send notification through custom service.
        """
        try:
            # Implement actual notification sending logic
            # This would typically make HTTP requests or API calls
            
            # Example implementation structure:
            payload = {
                'message': body,
                'title': title,
                'type': notify_type,
                'channel': self.channel
            }
            
            # Send notification using service API
            # response = custom_service_api.send(payload)
            # return response.success
            
            return True  # Success
            
        except Exception as e:
            self.logger.warning(f'Failed to send notification: {e}')
            return False
    
    def url(self, privacy=False, *args, **kwargs):
        """
        Generate URL for this service configuration.
        """
        if self.token:
            # Token-based authentication
            if privacy:
                return f'{self.protocol}://***@{self.host}/{self.channel}'
            else:
                return f'{self.protocol}://{self.token}@{self.host}/{self.channel}'
        else:
            # User/password authentication
            if privacy:
                return f'{self.protocol}://***:***@{self.host}'
            else:
                return f'{self.protocol}://{self.user}:{self.password}@{self.host}'
    
    @staticmethod
    def parse_url(url):
        """
        Parse URL into configuration parameters.
        """
        # Implementation would parse the URL and return configuration dict
        # This is called when loading services from URL strings
        pass

Plugin Usage Examples

Using Plugin Manager

import apprise

# Get notification manager instance
manager = apprise.NotificationManager()

# Load all available plugins
manager.load_modules()

# Get available plugins
plugins = manager.plugins(include_disabled=False)
print(f"Available plugins: {len(plugins)}")

for protocol, plugin_class in plugins.items():
    print(f"  {protocol}: {plugin_class.service_name}")

# Get supported schemas
schemas = manager.schemas()
print(f"Supported URL schemas: {schemas}")

Plugin Discovery and Information

import apprise

# Get detailed plugin information
apobj = apprise.Apprise()
details = apobj.details(show_requirements=True, show_disabled=False)

for service_key, service_info in details.items():
    print(f"\nService: {service_info.get('service_name', service_key)}")
    print(f"  Protocols: {service_info.get('protocols', [])}")
    print(f"  Enabled: {service_info.get('enabled', False)}")
    
    # Show requirements if any
    requirements = service_info.get('requirements', {})
    if requirements:
        print(f"  Requirements:")
        for package, version in requirements.items():
            print(f"    {package} {version or '(any version)'}")
    
    # Show URL templates
    templates = service_info.get('templates', [])
    if templates:
        print(f"  URL Templates:")
        for template in templates:
            print(f"    {template}")

Custom Plugin Integration

import apprise

# Register custom plugin (if not auto-discovered)
# This would typically be done in plugin initialization

# Use custom plugin through URL
apobj = apprise.Apprise()

# Add custom service using URL
apobj.add('custom://token@api.example.com/channel-123')

# Or add with parameters
custom_url = 'custom://user:pass@api.example.com'
apobj.add(custom_url)

# Send notification through custom plugin
success = apobj.notify(
    body='Test message through custom plugin',
    title='Custom Plugin Test',
    notify_type=apprise.NotifyType.INFO
)

if success:
    print("Custom plugin notification sent successfully")

Plugin Capabilities and Features

import apprise

apobj = apprise.Apprise()

# Add various services to demonstrate plugin capabilities
services = [
    'mailto://user:pass@gmail.com',      # Email plugin
    'slack://TokenA/TokenB/TokenC/Ch',   # Slack plugin  
    'discord://webhook_id/webhook_token', # Discord plugin
    'telegram://bot_token/chat_id',      # Telegram plugin
]

for service_url in services:
    try:
        apobj.add(service_url)
    except Exception as e:
        print(f"Failed to add service {service_url}: {e}")

# Examine loaded services
for i, server in enumerate(apobj.servers):
    print(f"\nService {i + 1}: {server.service_name}")
    print(f"  Protocol: {server.protocol}")
    print(f"  Enabled: {server.enabled}")
    print(f"  Format: {server.notify_format}")
    print(f"  Requirements: {getattr(server, 'requirements', {})}")
    
    # Get service URL (with privacy)
    service_url = server.url(privacy=True)
    print(f"  URL: {service_url}")
    
    # Check rate limiting
    throttle_delay = server.throttle()
    if throttle_delay > 0:
        print(f"  Rate limit: {throttle_delay}s delay")

Plugin Error Handling and Diagnostics

import apprise

def diagnose_plugin_issues():
    """Diagnose common plugin configuration issues."""
    apobj = apprise.Apprise()
    
    # Test problematic service URLs
    test_urls = [
        'slack://invalid/token/format',      # Invalid format
        'discord://missing-webhook',         # Incomplete URL
        'mailto://badformat@',               # Malformed email
        'unknown://unsupported/service',     # Unknown protocol
    ]
    
    for url in test_urls:
        try:
            success = apobj.add(url)
            if success:
                print(f"✓ Successfully added: {url}")
            else:
                print(f"✗ Failed to add: {url}")
        except Exception as e:
            print(f"✗ Exception adding {url}: {e}")
    
    # Check for disabled services
    details = apobj.details(show_disabled=True)
    disabled_services = [
        name for name, info in details.items() 
        if not info.get('enabled', True)
    ]
    
    if disabled_services:
        print(f"\nDisabled services: {disabled_services}")
        for service in disabled_services:
            service_info = details[service]
            requirements = service_info.get('requirements', {})
            if requirements:
                print(f"  {service} requirements: {requirements}")

diagnose_plugin_issues()

Advanced Plugin Configuration

import apprise

# Configure services with advanced plugin features
def setup_advanced_notifications():
    apobj = apprise.Apprise()
    
    # Email with HTML support
    email_url = 'mailto://user:app_password@gmail.com'
    apobj.add(email_url)
    
    # Slack with custom formatting
    slack_url = 'slack://xoxb-token/channel?format=markdown'
    apobj.add(slack_url)
    
    # Discord with custom avatar
    discord_url = 'discord://webhook_id/webhook_token?avatar=no'
    apobj.add(discord_url)
    
    # Send with different formats to demonstrate plugin capabilities
    test_cases = [
        {
            'body': '<b>HTML formatted message</b>',
            'title': 'HTML Test',
            'body_format': apprise.NotifyFormat.HTML
        },
        {
            'body': '**Markdown** formatted message',
            'title': 'Markdown Test', 
            'body_format': apprise.NotifyFormat.MARKDOWN
        },
        {
            'body': 'Plain text message',
            'title': 'Text Test',
            'body_format': apprise.NotifyFormat.TEXT
        }
    ]
    
    for test_case in test_cases:
        success = apobj.notify(**test_case)
        print(f"Format {test_case['body_format']}: {'✓' if success else '✗'}")

setup_advanced_notifications()