Growl Notification Transport Protocol for Python
80
The gntp.core module provides direct access to GNTP (Growl Notification Transport Protocol) message construction, parsing, and validation. This low-level API is useful for applications requiring fine-grained control over protocol details, custom message handling, or integration with other notification systems.
The core module provides classes for each GNTP message type, enabling direct protocol message construction and parsing.
class GNTPRegister:
"""
Represents a GNTP Registration Command.
Required headers: Application-Name, Notifications-Count
"""
def __init__(self, data=None, password=None):
"""
Create GNTP Registration message.
Parameters:
- data (str): Optional raw GNTP data to decode
- password (str): Optional password for decoding
"""
def add_notification(self, name, enabled=True):
"""
Add notification type to registration.
Parameters:
- name (str): Notification type name
- enabled (bool): Enable by default in Growl
"""
def validate(self):
"""
Validate required headers and notification headers.
Raises:
ParseError: When required headers are missing
"""
def decode(self, data, password):
"""
Decode existing GNTP Registration message.
Parameters:
- data (str): Raw GNTP message data
- password (str): Password for authentication
"""
def encode(self):
"""
Encode to GNTP Registration message.
Returns:
bytes: Complete GNTP registration message
"""
class GNTPNotice:
"""
Represents a GNTP Notification Command.
Required headers: Application-Name, Notification-Name, Notification-Title
"""
def __init__(self, data=None, app=None, name=None, title=None, password=None):
"""
Create GNTP Notification message.
Parameters:
- data (str): Optional raw GNTP data to decode
- app (str): Application name
- name (str): Notification type name
- title (str): Notification title
- password (str): Optional password
"""
def validate(self):
"""
Validate required headers.
Raises:
ParseError: When required headers are missing
"""
def decode(self, data, password):
"""
Decode existing GNTP Notification message.
Parameters:
- data (str): Raw GNTP message data
- password (str): Password for authentication
"""
def encode(self):
"""
Encode to GNTP Notification message.
Returns:
bytes: Complete GNTP notification message
"""
class GNTPSubscribe:
"""
Represents a GNTP Subscribe Command.
Required headers: Subscriber-ID, Subscriber-Name
"""
def __init__(self, data=None, password=None):
"""
Create GNTP Subscribe message.
Parameters:
- data (str): Optional raw GNTP data to decode
- password (str): Optional password
"""
def validate(self):
"""
Validate required headers.
Raises:
ParseError: When required headers are missing
"""
def decode(self, data, password):
"""
Decode existing GNTP Subscribe message.
Parameters:
- data (str): Raw GNTP message data
- password (str): Password for authentication
"""
def encode(self):
"""
Encode to GNTP Subscribe message.
Returns:
bytes: Complete GNTP subscribe message
"""
class GNTPOK:
"""
Represents a GNTP OK Response.
Required headers: Response-Action
"""
def __init__(self, data=None, action=None):
"""
Create GNTP OK response.
Parameters:
- data (str): Optional raw GNTP data to decode
- action (str): Response action type
"""
def decode(self, data):
"""
Decode GNTP OK response.
Parameters:
- data (str): Raw GNTP response data
"""
def encode(self):
"""
Encode to GNTP OK response.
Returns:
bytes: Complete GNTP OK response
"""
class GNTPError:
"""
Represents a GNTP Error response.
Required headers: Error-Code, Error-Description
"""
def __init__(self, data=None, errorcode=None, errordesc=None):
"""
Create GNTP Error response.
Parameters:
- data (str): Optional raw GNTP data to decode
- errorcode (int): Error code
- errordesc (str): Error description
"""
def error(self):
"""
Get error information.
Returns:
tuple: (error_code, error_description)
"""
def decode(self, data):
"""
Decode GNTP Error response.
Parameters:
- data (str): Raw GNTP response data
"""
def encode(self):
"""
Encode to GNTP Error response.
Returns:
bytes: Complete GNTP error response
"""All GNTP message classes inherit common functionality from the base class:
# Base methods available on all GNTP message classes:
def set_password(self, password, encryptAlgo='MD5'):
"""
Set password for GNTP message authentication.
Parameters:
- password (str): Password string (None to clear)
- encryptAlgo (str): Hash algorithm ('MD5', 'SHA1', 'SHA256', 'SHA512')
Raises:
UnsupportedError: When hash algorithm is not supported
"""
def add_header(self, key, value):
"""
Add header to GNTP message.
Parameters:
- key (str): Header name
- value (str): Header value
"""
def add_resource(self, data):
"""
Add binary resource to GNTP message.
Parameters:
- data (bytes): Binary resource data
Returns:
str: Resource URL for referencing in headers
"""
def validate(self):
"""
Verify required headers are present.
Raises:
ParseError: When required headers are missing
"""
def encode(self):
"""
Encode message to GNTP protocol format.
Returns:
bytes: Complete GNTP message ready for transmission
"""def parse_gntp(data, password=None):
"""
Parse raw GNTP message data and return appropriate message object.
Parameters:
- data (str or bytes): Raw GNTP message data
- password (str): Optional password for authentication
Returns:
GNTPRegister | GNTPNotice | GNTPSubscribe | GNTPOK | GNTPError:
Parsed message object of appropriate type
Raises:
ParseError: When message format is invalid or parsing fails
"""import gntp.core
import socket
# Create registration message
register = gntp.core.GNTPRegister()
register.add_header('Application-Name', 'My Custom App')
register.add_header('Application-Icon', 'http://example.com/icon.png')
# Add notification types
register.add_notification('Alert', enabled=True)
register.add_notification('Info', enabled=False)
# Set password if needed
register.set_password('mypassword', 'SHA256')
# Validate before sending
register.validate()
# Encode to protocol format
data = register.encode()
# Send over socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 23053))
sock.send(data)
response = sock.recv(1024)
sock.close()
# Parse response
response_obj = gntp.core.parse_gntp(response)
if isinstance(response_obj, gntp.core.GNTPOK):
print("Registration successful")
else:
print(f"Registration failed: {response_obj.error()}")import gntp.core
# Create notification
notice = gntp.core.GNTPNotice()
notice.add_header('Application-Name', 'My Custom App')
notice.add_header('Notification-Name', 'Alert')
notice.add_header('Notification-Title', 'Important Message')
notice.add_header('Notification-Text', 'This is the notification body')
notice.add_header('Notification-Priority', '1')
notice.add_header('Notification-Sticky', 'True')
# Add binary icon resource
with open('icon.png', 'rb') as f:
icon_data = f.read()
resource_url = notice.add_resource(icon_data)
notice.add_header('Notification-Icon', resource_url)
# Set password and encode
notice.set_password('mypassword')
data = notice.encode()import gntp.core
import gntp.errors
try:
# Parse raw GNTP data
message = gntp.core.parse_gntp(raw_data, password='mypassword')
if isinstance(message, gntp.core.GNTPRegister):
print(f"Registration from: {message.headers['Application-Name']}")
print(f"Notifications: {len(message.notifications)}")
elif isinstance(message, gntp.core.GNTPNotice):
print(f"Notification: {message.headers['Notification-Title']}")
print(f"From: {message.headers['Application-Name']}")
elif isinstance(message, gntp.core.GNTPError):
code, desc = message.error()
print(f"Error {code}: {desc}")
except gntp.errors.ParseError as e:
print(f"Invalid GNTP message: {e}")
except gntp.errors.AuthError as e:
print(f"Authentication failed: {e}")import gntp.core
# Create subscription message
subscribe = gntp.core.GNTPSubscribe()
subscribe.add_header('Subscriber-ID', 'client-123')
subscribe.add_header('Subscriber-Name', 'My Client')
subscribe.add_header('Subscriber-Port', '23054')
subscribe.set_password('subscription-password')
# Create custom OK response
ok_response = gntp.core.GNTPOK()
ok_response.add_header('Response-Action', 'REGISTER')
ok_response.add_header('Custom-Header', 'Custom-Value')
# Create custom error response
error_response = gntp.core.GNTPError()
error_response.add_header('Error-Code', '500')
error_response.add_header('Error-Description', 'Internal Server Error')
error_response.add_header('Server-Name', 'My GNTP Server')import gntp.core
import hashlib
# Create notification with multiple resources
notice = gntp.core.GNTPNotice(
app='Media Player',
name='Now Playing',
title='Song Changed'
)
# Add album art
with open('album_art.jpg', 'rb') as f:
album_art = f.read()
art_url = notice.add_resource(album_art)
notice.add_header('Notification-Icon', art_url)
# Add audio preview (if supported by client)
with open('preview.mp3', 'rb') as f:
audio_data = f.read()
audio_url = notice.add_resource(audio_data)
notice.add_header('X-Audio-Preview', audio_url)
# Resources are automatically managed and included in encoded message
encoded = notice.encode()All GNTP messages follow this structure:
GNTP/1.0 MESSAGETYPE ENCRYPTION [KEYHASHALGO:KEYHASH.SALT]
Header-Name: Header Value
Another-Header: Another Value
[Optional resource data]MD5: MD5 hash (default, legacy)SHA1: SHA-1 hashSHA256: SHA-256 hash (recommended)SHA512: SHA-512 hash (most secure)Binary resources are automatically:
x-growl-resource:// URLs in headersCommon GNTP error codes:
400: Authentication failed500: Internal server error, parsing error, unsupported operationInstall with Tessl CLI
npx tessl i tessl/pypi-gntpevals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10