A simple notification CLI for Bark and Weixin
Project description
owl-notify
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
- Automatic Retry: When a notification fails, it automatically retries up to
max_retriestimes - Retry Delay: Waits
retry_delay_msmilliseconds between each retry attempt - Error Notification: If all retries fail, sends an error notification to
notification_platform - 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7170f05175f9eb2a40c7b77a3687bce0bf211f64254ea92432e99a5322b92137
|
|
| MD5 |
cdb024e9a55780ab7983976b15f4cef2
|
|
| BLAKE2b-256 |
cc8072e7b4b0d9759390d78b7e7f20fe0a266a86e8d065ecfa727341ad8044f1
|
Provenance
The following attestation bundles were made for owl_notify-0.2.0.tar.gz:
Publisher:
publish.yml on liguoqinjim/owl-notify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
owl_notify-0.2.0.tar.gz -
Subject digest:
7170f05175f9eb2a40c7b77a3687bce0bf211f64254ea92432e99a5322b92137 - Sigstore transparency entry: 1280811155
- Sigstore integration time:
-
Permalink:
liguoqinjim/owl-notify@be8f6b23af0cc1b7ebc31989171344473b41562b -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/liguoqinjim
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@be8f6b23af0cc1b7ebc31989171344473b41562b -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec4c163db149d7cc6303722ca87d638f1ce68b6183e5b0ae0c6598efcd4703e7
|
|
| MD5 |
b4c239c221593a17550015881bbf35e9
|
|
| BLAKE2b-256 |
9dc0924e10d41d65011f6c5f90661aa33e7f279cf92165a0bce3102c30f43c0e
|
Provenance
The following attestation bundles were made for owl_notify-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on liguoqinjim/owl-notify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
owl_notify-0.2.0-py3-none-any.whl -
Subject digest:
ec4c163db149d7cc6303722ca87d638f1ce68b6183e5b0ae0c6598efcd4703e7 - Sigstore transparency entry: 1280811168
- Sigstore integration time:
-
Permalink:
liguoqinjim/owl-notify@be8f6b23af0cc1b7ebc31989171344473b41562b -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/liguoqinjim
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@be8f6b23af0cc1b7ebc31989171344473b41562b -
Trigger Event:
release
-
Statement type: