samschott / desktop-notifier Goto Github PK
View Code? Open in Web Editor NEWPython library for cross-platform desktop notifications
Home Page: https://desktop-notifier.readthedocs.io
License: MIT License
Python library for cross-platform desktop notifications
Home Page: https://desktop-notifier.readthedocs.io
License: MIT License
The on_pressed
callback is never called for the first button added to a notification. This is because the action_key
is '0'
in this case, which evaluates to False
after converting it to an int
, so the elif button_number
condition on line 203 of desktop_notifier.dbus
fails.
python3 examples/eventloop.py
Click the Mark as read button; notice that Marked as read
is never printed to the console.
Hi, thanks for your hard workings.
in my office we use this library to create 2 necessary softwares one: to alert us extracting price list has done and the other: urgent sell has been issued.
for our developers, lack of docs and tutorials are pain. To be honest ,apologize my French!, a saw in our asses! not gonna lie!!
and for for sounds:
with this in mind this library play a vital role in my department having a same sound for 2 different software is not good.better to be able to use custome sounds
p.s. : all OS above are using in my office!
Describe the bug
I used to be able to run Maestral on macOS, but not anymore, and I wonder whether it is related to macOS Big Sur upgrade (or perhaps another upgrade?).
To Reproduce
miniconda
.conda create -n dropbox python=3.8
, then activate it.maestral start
.At this point, I got this error:
Starting Maestral... [FAILED]
Please check logs for more information.
Expected behaviour
Maestral daemon should run.
System:
Additional context
When I show the logs with maestral logs show
, I see the following:
2021-06-20 12:08:29 daemon ERROR: Could not communicate with daemon
Traceback (most recent call last):
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/client.py", line 278, in connect_and_handshake
sock = socketutil.create_socket(connect=connect_location,
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/socketutil.py", line 283, in create_socket
sock.connect(connect)
FileNotFoundError: [Errno 2] No such file or directory
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/maestral/daemon.py", line 617, in start_maestral_daemon_process
wait_for_startup(config_name, timeout)
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/maestral/daemon.py", line 407, in wait_for_startup
raise exc
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/maestral/daemon.py", line 403, in wait_for_startup
maestral_daemon._pyroBind()
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/client.py", line 182, in _pyroBind
return self.__pyroCreateConnection(True)
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/client.py", line 342, in __pyroCreateConnection
connect_and_handshake(conn)
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/client.py", line 304, in connect_and_handshake
raise errors.CommunicationError(err) from x
Pyro5.errors.CommunicationError: cannot connect to /Users/rafidka/Library/Application Support/maestral/maestral.sock: [Errno 2] No such file or directory
2021-06-20 12:08:29 daemon ERROR: Daemon stopped with return code -6
2021-06-20 12:12:02 daemon ERROR: Could not communicate with daemon
Traceback (most recent call last):
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/client.py", line 278, in connect_and_handshake
sock = socketutil.create_socket(connect=connect_location,
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/socketutil.py", line 283, in create_socket
sock.connect(connect)
FileNotFoundError: [Errno 2] No such file or directory
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/maestral/daemon.py", line 617, in start_maestral_daemon_process
wait_for_startup(config_name, timeout)
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/maestral/daemon.py", line 407, in wait_for_startup
raise exc
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/maestral/daemon.py", line 403, in wait_for_startup
maestral_daemon._pyroBind()
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/client.py", line 182, in _pyroBind
return self.__pyroCreateConnection(True)
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/client.py", line 342, in __pyroCreateConnection
connect_and_handshake(conn)
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/client.py", line 304, in connect_and_handshake
raise errors.CommunicationError(err) from x
Pyro5.errors.CommunicationError: cannot connect to /Users/rafidka/Library/Application Support/maestral/maestral.sock: [Errno 2] No such file or directory
2021-06-20 12:12:02 daemon ERROR: Daemon stopped with return code -6
2021-06-20 12:29:30 daemon ERROR: Could not communicate with daemon
Traceback (most recent call last):
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/client.py", line 278, in connect_and_handshake
sock = socketutil.create_socket(connect=connect_location,
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/socketutil.py", line 283, in create_socket
sock.connect(connect)
FileNotFoundError: [Errno 2] No such file or directory
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/maestral/daemon.py", line 617, in start_maestral_daemon_process
wait_for_startup(config_name, timeout)
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/maestral/daemon.py", line 407, in wait_for_startup
raise exc
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/maestral/daemon.py", line 403, in wait_for_startup
maestral_daemon._pyroBind()
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/client.py", line 182, in _pyroBind
return self.__pyroCreateConnection(True)
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/client.py", line 342, in __pyroCreateConnection
connect_and_handshake(conn)
File "/Users/rafidka/miniconda3/envs/dropbox/lib/python3.8/site-packages/Pyro5/client.py", line 304, in connect_and_handshake
raise errors.CommunicationError(err) from x
Pyro5.errors.CommunicationError: cannot connect to /Users/rafidka/Library/Application Support/maestral/maestral.sock: [Errno 2] No such file or directory
2021-06-20 12:29:30 daemon ERROR: Daemon stopped with return code -6
I tried to manually execute the daemon from Python and this is what I see:
>>> import maestral.daemon
>>> maestral.daemon.start_maestral_daemon('maestral', True)
2021-06-20 12:26:11.732 python[43518:841675] *** Assertion failure in +[UNUserNotificationCenter currentNotificationCenter], UNUserNotificationCenter.m:54
2021-06-20 12:26:11.732 python[43518:841675] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'bundleProxyForCurrentProcess is nil: mainBundle.bundleURL file:///Users/rafidka/miniconda3/envs/dropbox/bin/'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff2079887b __exceptionPreprocess + 242
1 libobjc.A.dylib 0x00007fff204d0d92 objc_exception_throw + 48
2 CoreFoundation 0x00007fff207c19e2 +[NSException raise:format:arguments:] + 88
3 Foundation 0x00007fff2157d512 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191
4 UserNotifications 0x00007fff2a691540 __53+[UNUserNotificationCenter currentNotificationCenter]_block_invoke + 985
5 libdispatch.dylib 0x00007fff2047b806 _dispatch_client_callout + 8
6 libdispatch.dylib 0x00007fff2047c98c _dispatch_once_callout + 20
7 UserNotifications 0x00007fff2a691165 +[UNUserNotificationCenter currentNotificationCenter] + 101
8 libffi.7.dylib 0x000000010fb45ead ffi_call_unix64 + 85
9 ??? 0x00007ffee0e07210 0x0 + 140732671226384
)
libc++abi: terminating with uncaught exception of type NSException
[1] 43518 abort python
At least on Linux, the notification spec supports specifying the timeout for every notification. It would be great if this option is exposed to the top-level API as timeout: int = -1
(-1 means OS default).
For other OSes, maybe this can be implemented by spawning an async Timer task that will clear the notif after expiration.
PS: Thanks for making this project. It is extremely useful to have a singular and full-featured interface for cross-platform notifications! :)
I am trying the callback mechanism using the Et tu brutus example on Windows. There is no feedback after responding to the notification, I checked if the terminal was hanging by hitting enter a few times, but no, the callbacks don't seem to be executed.
Any special things I forgot for Windows? It works on my mac...
This script saved as send.py
import asyncio
from desktop_notifier import DesktopNotifier, Urgency, Button, ReplyField
notifier = DesktopNotifier()
async def main():
await notifier.send(
title="Julius Caesar",
message="Et tu, Brute?",
urgency=Urgency.Critical,
buttons=[
Button(
title="Mark as read",
on_pressed=lambda: print("Marked as read")),
],
reply_field=ReplyField(
title="Reply",
button_title="Send",
on_replied=lambda text: print("Brutus replied:", text),
),
on_clicked=lambda: print("Notification clicked"),
on_dismissed=lambda: print("Notification dismissed"),
sound=True,
)
loop = asyncio.get_event_loop()
loop.create_task(main())
loop.run_forever()
Executed
python send.py
Then entered answer in the textbox and clicked send
I'm trying to use this library from a PyInstaller-built binary aw-notify
running as a subprocess in my application.
The whole thing is correctly signed with PyInstaller (see spec file). The CFBundleExecutable
is aw-qt
, which manages aw-notify
as a subprocess.
However, the subprocess doesn't seem to get NSBundle.mainBundle.bundleIdentifier
set. Which fails the check at:
@samschott wrote here:
The alternative would be to try and fake a valid bundle identifier at runtime. Some other packages do this but its a nasty hack and will prevent any apps using it from being accepted in the App store.
I am now assuming that I would need some kind of workaround like that, and that things would probably work if it wasn't for this NSBundle check (hopeful/wishful thinking).
Since the testing process is rather lengthy for me, given how I have to wait for CI to sign etc, I thought I'd make this issue before I venture further. I've only done a little bit of research on this, and am not sure if I'm likely to meet further resistance once I've worked around the bundleIdentifier
check.
While I know Desktop Notifier can be installed via pip
, I was trying to build this from source; unfortunately, I'm not terribly familiar with Python and couldn't quite figure out which are the right commands to run. Without the setup.py
file, I'm at a bit out of familiarity.
I could not find easy way, how to suppress notification icon. Culprit is this code:
if not icon:
icon = self.app_icon
elif isinstance(icon, Path):
icon = icon.as_uri()
I'd expect eg. empty string to disable icon, but empty string evaluates to False
.
Is a better way?
I am trying to push a notification like this:
notifier = DesktopNotifier("SyncLyrics", ICON_URL)
await notifier.send(title="SyncLyrics", message="wow")
But I get this error:
Traceback (most recent call last):
File "C:\Users\Kostas\Desktop\Programming\SyncLyrics\sync_lyrics.py", line 122, in <module>
asyncio.run(main())
File "C:\Users\Kostas\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 190, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "C:\Users\Kostas\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Kostas\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "C:\Users\Kostas\Desktop\Programming\SyncLyrics\sync_lyrics.py", line 105, in main
await notifier.send(title="SyncLyrics", message=lyric)
File "C:\Users\Kostas\Desktop\Programming\SyncLyrics\env\Lib\site-packages\desktop_notifier\main.py", line 317, in send
return await self.send_notification(notification)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Kostas\Desktop\Programming\SyncLyrics\env\Lib\site-packages\desktop_notifier\main.py", line 223, in send_notification
await self.request_authorisation()
File "C:\Users\Kostas\Desktop\Programming\SyncLyrics\env\Lib\site-packages\desktop_notifier\main.py", line 203, in request_authorisation
return await self._impl.request_authorisation()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Kostas\Desktop\Programming\SyncLyrics\env\Lib\site-packages\desktop_notifier\winrt.py", line 98, in request_authorisation
return bool(self.notifier.setting == NotificationSetting.ENABLED)
^^^^^^^^^^^^^^^^^^^^^
OSError: [WinError -2147023728] Element not found
By the way I saw some related closed issues, maybe the issue (or a similar one) is back. Just adding a return statement on the top of request_authorisation (just avoiding to run the function) fixes the problem and notifications are pushed perfectly.
async def request_authorisation(self) -> bool:
"""
Requests authorisation to send user notifications. This will be automatically
called for you when sending a notification for the first time. It also can be
called manually to request authorisation in advance.
On some platforms such as macOS and iOS, a prompt will be shown to the user
when this method is called for the first time. This method does nothing on
platforms where user authorisation is not required.
:returns: Whether authorisation has been granted.
"""
return # The holly statement that fixes everything
with self._lock:
self._did_request_authorisation = True
return await self._impl.request_authorisation()
PS:
I hope you don't hate me for finding issues every 5 days like I am a workaholic tester that has a beef with you.
Is your feature request related to a problem? Please describe.
Currently, there is only support for turning notification sounds off (default) or on. When enabled, the platform's default sound for notifications will be used, if available. It would be nice to support custom sounds.
Describe the solution you'd like
Ideally, we would allow specifying either:
Both options will require some abstraction over platform differences: macOS only provides two notification sounds, default and critical, and critical alerts require a special entitlement issued by Apple. Linux desktops typically allow using all platforms sounds from the sound-theme-spec. Both platforms allow specifying sound files but supported audio formats will be limited. In addition, macOS requires the sound file to be stored either in the app bundle or in the /Library/Sounds
directory of the app's container directory (see the UNNotificationSound docs). It therefore appears that custom sound files are only support from bundled apps.
Describe alternatives you've considered
Keep the sound option as is: default or nothing.
I want to see notifications add, and add, until dismissed - whether one at a time or en masse.
Instead, one notification dismisses another, so I only see one at a time. Dismissing notification n+1 does not expose notification n.
I'm testing with urgency=Urgency.Critical, but it doesn't appear to be enough. Does relevancy or something control this?
Here's the test script:
#!/usr/bin/python3
import asyncio
from desktop_notifier import DesktopNotifier, Urgency, Button, ReplyField
notifier = DesktopNotifier()
async def main():
await notifier.send(
title="Julius Caesar",
message="Et tu, Brute?",
urgency=Urgency.Critical,
buttons=[
Button(
title="Mark as read",
on_pressed=lambda: print("Marked as read")),
],
reply_field=ReplyField(
title="Reply",
button_title="Send",
on_replied=lambda text: print("Brutus replied:", text),
),
on_clicked=lambda: print("Notification clicked"),
on_dismissed=lambda: print("Notification dismissed"),
sound=True,
)
loop = asyncio.get_event_loop()
loop.create_task(main())
loop.run_forever()
$ pip freeze
desktop-notifier==3.4.1
packaging==21.3
pyparsing==3.0.9
winsdk==1.0.0b7
I tried to run basic example on README
Traceback (most recent call last):
File "C:\Users\ilker\Desktop\dev\env-trash\desktop-notifier\main.py", line 4, in <module>
notifier = DesktopNotifier()
File "C:\Users\ilker\Desktop\dev\env-trash\desktop-notifier\venv\lib\site-packages\desktop_notifier\main.py", line 161, in __init__
self._impl = impl_cls(app_name, app_icon, notification_limit)
File "C:\Users\ilker\Desktop\dev\env-trash\desktop-notifier\venv\lib\site-packages\desktop_notifier\winrt.py", line 74, in __init__
self.notifier = self.manager.create_toast_notifier(self._appid)
OSError: [WinError -2147024809] Parametre hatalı
Parametre hatalı -> The parameter is incorrect (in english)
The library cannot be imported from a virtual environment using venv.
I simply tried to:
from desktop_notifier import DesktopNotifier
And got with this traceback:
Traceback (most recent call last):
File "/home/kostas/programming/lyrics-app/main.py", line 4, in <module>
from desktop_notifier import DesktopNotifier
File "/home/kostas/programming/lyrics-app/env/lib/python3.10/site-packages/desktop_notifier/__init__.py", line 2, in <module>
from .main import DesktopNotifier, Button, ReplyField, Notification, Urgency
File "/home/kostas/programming/lyrics-app/env/lib/python3.10/site-packages/desktop_notifier/main.py", line 29, in <module>
from .base import (
File "/home/kostas/programming/lyrics-app/env/lib/python3.10/site-packages/desktop_notifier/base.py", line 22, in <module>
from importlib_resources import path as resource_path
ImportError: cannot import name 'path' from 'importlib_resources' (/home/kostas/programming/lyrics-app/env/lib/python3.10/site-packages/importlib_resources/__init__.py
Hello, love the project, but i cant agree more with #32 , the readme.md could get a new look.
In the base.py you have all those parameters :
class Notification:
"""A desktop notification
:param title: Notification title.
:param message: Notification message.
:param urgency: Notification level: low, normal or critical.
:param icon: URI for an icon to use for the notification or icon name.
:param buttons: A list of buttons for the notification.
:param reply_field: An optional reply field/
:param on_clicked: Callback to call when the notification is clicked. The
callback will be called without any arguments.
:param on_dismissed: Callback to call when the notification is dismissed. The
callback will be called without any arguments.
:attachment: URI for an attachment to the notification.
:param sound: Whether to play a sound when the notification is shown.
:param thread: An identifier to group related notifications together.
:param timeout: Duration for which the notification in shown.
"""
but only 8 params are showed in the readme :(
what if i want to change the icon of the notification or if i want my notification to never timed out ?
Is there any way to use a relative path to custom icon ?
Currently using this :
icon="file:///home/justalternate/Projects/PokeNotif/assets/icon.png"
Something like this would be cool :
icon = "assets/icon.png"
Most of the platform APIs to schedule notifications and interact with the platform's notification centre are asynchronous:
For those reasons, the business of dealing with desktop notifications is inherently async. However, async methods may be difficult to use under particular circumstances and there is value in just firing a non-interactive notification without starting an event loop. For this reason, desktop-notifier provides a synchronous interface where possible. The only exception is DesktopNotifier.request_authorisation()
which would otherwise block until the user has granted or denied authorisation. Instead, this method accepts a callback.
Moving the public API to async methods would have the following advantages:
A possible alternative would be to offer both synchronous and asynchronous methods in the main API and handle any conversion there instead of in the backends.
example code
import asyncio
from desktop_notifier import DesktopNotifier
notify = DesktopNotifier()
async def main():
n = await notify.send(title="Hello world!", message="Sent from Python")
await notify.clear(n) # removes the notification
await notify.clear_all() # removes all notifications for this app
asyncio.run(main())
run python ./notify.py
File "./notify.py", line 6
async def main():
^
SyntaxError: invalid syntax
Paste the command(s) you ran and the output.
If there was a crash, please include the traceback here.
Windows support is currently still missing since I have no experience with Windows GUI frameworks.
Different options may be:
Shell_NotifyIconW
API documented here. Advantages: Can be used from pure Python code using ctypes.windll
. See plyer for an example implementation. Disadvantages: Does not appear to support buttons.pythonnet
with extension modules.i am trying to run the most simple example of code that's in the Readme file and getting this message:
Notification Center can only be used from a signed Framework or app bundle
when reading the Readme file there is a section about signed apps, but the part about passing the macos_legacy
param is wrong - the param is not in the code as an option.
In Terminal:
conda create --name mac-notifier-playground python=3.9 -y
conda activate mac-notifier-playground
pip install desktop-notifier
In Code:
import asyncio
from desktop_notifier import DesktopNotifier
notify = DesktopNotifier(app_name="Sample App")
async def main():
n = await notify.send(title="Hello world!", message="Sent from Python")
await asyncio.sleep(5) # wait a bit before clearing notification
await notify.clear(n) # removes the notification
await notify.clear_all() # removes all notifications for this app
asyncio.run(main())
Output:
Notification Center can only be used from a signed Framework or app bundle
As per samschott/maestral#516 (comment)
and request to log samschott/maestral#516 (comment)
When starting the sync via terminal, following error is generated;
Notification failed
Traceback (most recent call last):
File "Contents/Resources/app_packages/rubicon/objc/api.py", line 206, in __call__
KeyError: frozenset({''})
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "Contents/Resources/app_packages/desktop_notifier/base.py", line 204, in send
File "Contents/Resources/app_packages/desktop_notifier/macos.py", line 252, in _send
File "Contents/Resources/app_packages/desktop_notifier/macos.py", line 366, in _create_category_for_notification
File "Contents/Resources/app_packages/rubicon/objc/api.py", line 236, in __call__
File "Contents/Resources/app_packages/rubicon/objc/api.py", line 215, in __call__
File "Contents/Resources/app_packages/rubicon/objc/api.py", line 153, in __call__
File "Contents/Resources/app_packages/rubicon/objc/api.py", line 1519, in ns_from_py
File "Contents/Resources/app_packages/rubicon/objc/api.py", line 236, in __call__
File "Contents/Resources/app_packages/rubicon/objc/api.py", line 208, in __call__
ValueError: No method was found starting with 'addObject' and with keywords set()
Known keywords are:
frozenset({'toPropertyWithKey', ''})
frozenset({'', 'toBothSidesOfRelationshipWithKey'})
resources/python.png is missing in v3.3.3
$ maestral start
Starting Maestral...Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/usr/lib/python3.9/site-packages/maestral/daemon.py", line 375, in start_maestral_daemon
from .main import Maestral
File "/usr/lib/python3.9/site-packages/maestral/main.py", line 41, in <module>
from .sync import SyncDirection
File "/usr/lib/python3.9/site-packages/maestral/sync.py", line 69, in <module>
from . import notify
File "/usr/lib/python3.9/site-packages/maestral/notify.py", line 13, in <module>
from desktop_notifier import DesktopNotifier, Urgency, Button
File "/usr/lib/python3.9/site-packages/desktop_notifier/__init__.py", line 2, in <module>
from .main import DesktopNotifier, Button, ReplyField, Notification, Urgency
File "/usr/lib/python3.9/site-packages/desktop_notifier/main.py", line 30, in <module>
from .base import (
File "/usr/lib/python3.9/site-packages/desktop_notifier/base.py", line 21, in <module>
PYTHON_ICON_PATH = path("desktop_notifier.resources", "python.png").__enter__()
File "/usr/lib/python3.9/contextlib.py", line 119, in __enter__
return next(self.gen)
File "/usr/lib/python3.9/importlib/resources.py", line 175, in _path_from_reader
opener_reader = reader.open_resource(norm_resource)
File "<frozen importlib._bootstrap_external>", line 1055, in open_resource
FileNotFoundError: [Errno 2] No such file or directory: '/usr/lib/python3.9/site-packages/desktop_notifier/resources/python.png'
I'm trying to run a simple notification without a button, but default
button keeps poping up. I went through docs twice but obviously I'm still missing some obvious way to disable it.
import asyncio
from desktop_notifier import DesktopNotifier
notifier = DesktopNotifier()
async def main():
await notifier.send(
title="Julius Caesar",
message="Et tu, Brute?",
icon=f"{icons}/selection/check.svg",
timeout=5
)
asyncio.run(main())
Dear Sam,
while we haven't worked on caronc/apprise#742 yet, I wanted to humbly make you aware of a request submitted by @mobilelifeful at caronc/apprise#786.
The terminal-notifier program has a sweet feature to make the notification message clickable, by providing a target URL. Do you think this feature could also be added to desktop-notifier
in any way?
With kind regards,
Andreas.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.