Skip to main content

A simple notification CLI for Bark and Weixin

Project description

owl-notify

PyPI version Python License Downloads

A simple notification CLI for Bark, Weixin, and custom webhooks.

Installation

pip install owl-notify

Supported Platforms

  • Bark: iOS notification service
  • Weixin (Text): Weixin Work Bot with plain text format
  • Weixin (Markdown V2): Weixin Work Bot with markdown formatting support
  • Custom Webhooks: Any HTTP-based webhook service (Slack, Discord, Feishu, etc.)

Naming Convention

New in v0.2.0: Unified naming convention for all platforms:

  • Platform and channel are separated by dot (.)
  • Platform names use underscores (_) for word separation
  • Channel names use hyphens (-) for word separation
  • Example: weixin_markdown_v2.nbl-alerts
Platform Structure:
platform.channel-name

Examples:
- bark.phone-1
- weixin.team-alerts
- weixin_markdown_v2.nbl-alerts
- webhook.slack-team1

Configuration

Create a config file at ~/.owl-notify.toml (get path via owl --show-config):

# Base platforms
[bark]
server_url = "https://api.day.app"
token = "your-bark-token"

[weixin]
bot_url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-key"

[weixin_markdown_v2]
bot_url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-key"

# Channels (inherit from base platforms)
[bark.phone-1]
token = "phone1-token"
# Inherits server_url from [bark]

[weixin.team-alerts]
key = "team-alerts-key"
# Auto-generates bot_url

[weixin_markdown_v2.nbl-alerts]
key = "nbl-alerts-key"

# Webhook channels (independent, no inheritance)
[webhook.slack-team1]
url = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
method = "POST"
body = '{"text": "{{title}}\\n{{message}}"}'

CLI Usage

# Send via Bark (default)
owl "Title" "Message"

# Send via specific platform
owl "Title" "Message" --platform bark
owl "Title" "Message" --platform weixin
owl "Title" "Message" --platform weixin_markdown_v2

# Send via channel
owl "Title" "Message" --platform bark.phone-1
owl "Title" "Message" --platform weixin.team-alerts
owl "Title" "Message" --platform weixin_markdown_v2.nbl-alerts

# Send via webhook
owl "Title" "Message" --platform webhook.slack-team1

# Use extra fields for webhook templates
owl "Title" "Message" --platform webhook.discord-alerts --extra from="Bot" --extra priority="high"

# Short form
owl "Title" "Message" -p webhook.slack-team1 -e from="Alert System"

# Use custom config file
owl "Title" "Message" --config /path/to/config.toml

# Show config file path
owl --show-config

# List all available platforms and channels
owl --list-platforms

Python API

Simple Usage (Recommended)

import owl_notify

# Use default config (~/.owl-notify.toml)
owl_notify.send("Title", "Message")

# Method 1: Using platform constants (recommended - type-safe, autocomplete)
owl_notify.send("Title", "Message", platform=owl_notify.platform.bark)
owl_notify.send("Title", "Message", platform=owl_notify.platform.weixin)
owl_notify.send("Title", "Message", platform=owl_notify.platform.weixin_markdown_v2)

# Using channels
owl_notify.send("Title", "Message", platform=owl_notify.platform.bark.channel("phone-1"))
owl_notify.send("Title", "Message", platform=owl_notify.platform.weixin.channel("team-alerts"))
owl_notify.send("Title", "Message", platform=owl_notify.platform.weixin_markdown_v2.channel("nbl-alerts"))

# Using webhook channels
owl_notify.send("Title", "Message", platform=owl_notify.platform.webhook.channel("slack-team1"))

# Method 2: Using strings (also supported)
owl_notify.send("Title", "Message", platform="bark")
owl_notify.send("Title", "Message", platform="bark.phone-1")
owl_notify.send("Title", "Message", platform="weixin.team-alerts")
owl_notify.send("Title", "Message", platform="webhook.slack-team1")

# Use custom config file
owl_notify.send("Title", "Message", config_path="/path/to/config.toml")

# Use extra fields for webhook templates
owl_notify.send(
    "Title",
    "Message",
    platform=owl_notify.platform.webhook.channel("discord-alerts"),
    extra={"from": "Bot", "priority": "high"}
)

Class-based Usage

from owl_notify import Notify, platform

# Initialize
notifier = Notify()  # Uses ~/.owl-notify.toml
notifier = Notify(config_path="/path/to/config.toml")  # Custom config

# Send notifications
notifier.send("Title", "Message", platform=platform.bark)
notifier.send("Title", "Message", platform=platform.bark.channel("phone-1"))
notifier.send("Title", "Message", platform=platform.weixin.channel("team-alerts"))
notifier.send("Title", "Message", platform=platform.webhook.channel("slack-team1"))

# List available platforms and channels
platforms = notifier.list_platforms()
# Returns: {"builtin": [...], "channels": [...]}

Platform Channels

Inheritance (bark, weixin, weixin_markdown_v2)

Channels inherit configuration from their base platform:

# Base platform
[bark]
server_url = "https://api.day.app"
token = "default-token"

# Channels inherit server_url, only override token
[bark.phone-1]
token = "phone1-token"

[bark.phone-2]
token = "phone2-token"

Weixin Key Auto-Concatenation

For Weixin platforms, use the key field for convenience:

[weixin.team-alerts]
key = "your-webhook-key"
# Auto-generates: bot_url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-webhook-key"

Webhook Channels (Independent)

Webhook channels are independent and do not support inheritance:

# Each webhook channel must have complete configuration
[webhook.slack-team1]
url = "https://hooks.slack.com/services/T11111111/B11111111/YYYYYYYYYYYYYYYYYYYY"
method = "POST"
body = '{"text": "{{title}}\\n{{message}}"}'

[webhook.slack-team2]
url = "https://hooks.slack.com/services/T22222222/B22222222/ZZZZZZZZZZZZZZZZZZZZ"
method = "POST"
body = '{"text": "{{title}}\\n{{message}}"}'

Custom Platforms (For Reusable Configurations)

If you need to reuse webhook configurations, create a custom platform:

# Custom platform
[slack]
method = "POST"
body = '{"text": "{{title}}\\n{{message}}"}'

# Channels inherit method and body
[slack.team1]
url = "https://hooks.slack.com/services/T11111111/B11111111/YYYYYYYYYYYYYYYYYYYY"

[slack.team2]
url = "https://hooks.slack.com/services/T22222222/B22222222/ZZZZZZZZZZZZZZZZZZZZ"

Custom Webhook Configuration

Template Placeholders

Use {{placeholder}} syntax in URLs and body templates:

  • {{title}} - Notification title
  • {{message}} - Notification message
  • {{key}} - Any extra field passed via --extra key=value

Common Platform Examples

Slack

[webhook.slack-team1]
url = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
method = "POST"
body = '{"text": "{{title}}\\n\\n{{message}}"}'

Discord

[webhook.discord-alerts]
url = "https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN"
method = "POST"
body = '{"content": "**{{title}}**\\n{{message}}", "username": "{{from}}"}'

Usage:

owl "Alert" "Something happened" -p webhook.discord-alerts -e from="Monitor"

Feishu (飞书)

[webhook.feishu-team1]
url = "https://open.feishu.cn/open-apis/bot/v2/hook/YOUR_WEBHOOK_TOKEN"
method = "POST"
body = '{"msg_type": "text", "content": {"text": "{{title}}\\n{{message}}\\nFrom: {{from}}"}}'

Custom GET Request

[webhook.custom-api]
url = "https://api.example.com/notify?title={{title}}&message={{message}}&priority={{priority}}"
method = "GET"

Global Defaults

Set global defaults for all platforms:

[defaults]
timeout = 30        # Request timeout in seconds

Error Handling and Retry

New in v0.2.0: Configure automatic retry and error notifications:

[error]
max_retries = 3                                    # Maximum retry attempts (default: 3)
retry_delay_ms = 1000                              # Delay between retries in milliseconds (default: 1000ms = 1s)
notification_platform = "weixin_markdown_v2.nbl-alerts"  # Platform for error notifications

How It Works

  1. Automatic Retry: When a notification fails, it automatically retries up to max_retries times
  2. Retry Delay: Waits retry_delay_ms milliseconds between each retry attempt
  3. Error Notification: If all retries fail, sends an error notification to notification_platform
  4. No Infinite Loop: Error notifications themselves do not retry (to prevent infinite loops)

Example Error Notification

When a notification fails after all retries, you'll receive:

🚨 Notification Failed: Original Title

**Failed to send notification**

**Original Platform:** bark.phone-1
**Original Title:** Original Title
**Original Message:**
Original message content

**Time:** 2026-04-12 15:30:45

Configuration Examples

# Send error notifications to Weixin Markdown
[error]
max_retries = 3
retry_delay_ms = 1000
notification_platform = "weixin_markdown_v2.nbl-alerts"

# Send error notifications to Bark with longer retry delay
[error]
max_retries = 5
retry_delay_ms = 2000  # 2 seconds
notification_platform = "bark.phone-1"

# Fast retry for critical notifications
[error]
max_retries = 10
retry_delay_ms = 500  # 0.5 seconds
notification_platform = "weixin_markdown_v2.nbl-alerts"

# Disable error notifications (only retry)
[error]
max_retries = 3
retry_delay_ms = 1000
# notification_platform = ""  # Leave empty or omit

See .owl-notify.toml.example for a complete configuration example.

License

Apache License 2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

owl_notify-0.2.0.tar.gz (19.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

owl_notify-0.2.0-py3-none-any.whl (16.6 kB view details)

Uploaded Python 3

File details

Details for the file owl_notify-0.2.0.tar.gz.

File metadata

  • Download URL: owl_notify-0.2.0.tar.gz
  • Upload date:
  • Size: 19.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for owl_notify-0.2.0.tar.gz
Algorithm Hash digest
SHA256 7170f05175f9eb2a40c7b77a3687bce0bf211f64254ea92432e99a5322b92137
MD5 cdb024e9a55780ab7983976b15f4cef2
BLAKE2b-256 cc8072e7b4b0d9759390d78b7e7f20fe0a266a86e8d065ecfa727341ad8044f1

See more details on using hashes here.

Provenance

The following attestation bundles were made for owl_notify-0.2.0.tar.gz:

Publisher: publish.yml on liguoqinjim/owl-notify

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file owl_notify-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: owl_notify-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 16.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for owl_notify-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ec4c163db149d7cc6303722ca87d638f1ce68b6183e5b0ae0c6598efcd4703e7
MD5 b4c239c221593a17550015881bbf35e9
BLAKE2b-256 9dc0924e10d41d65011f6c5f90661aa33e7f279cf92165a0bce3102c30f43c0e

See more details on using hashes here.

Provenance

The following attestation bundles were made for owl_notify-0.2.0-py3-none-any.whl:

Publisher: publish.yml on liguoqinjim/owl-notify

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page