plexpy/lib/rumps/notifications.py
dependabot[bot] 79cf61c53e
Bump rumps from 0.3.0 to 0.4.0 (#1896)
* Bump rumps from 0.3.0 to 0.4.0

Bumps [rumps](https://github.com/jaredks/rumps) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/jaredks/rumps/releases)
- [Changelog](https://github.com/jaredks/rumps/blob/master/CHANGES.rst)
- [Commits](https://github.com/jaredks/rumps/commits)

---
updated-dependencies:
- dependency-name: rumps
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update rumps==0.4.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-14 11:27:16 -08:00

267 lines
8.7 KiB
Python

# -*- coding: utf-8 -*-
_ENABLED = True
try:
from Foundation import NSUserNotification, NSUserNotificationCenter
except ImportError:
_ENABLED = False
import datetime
import os
import sys
import traceback
import Foundation
from . import _internal
from . import compat
from . import events
def on_notification(f):
"""Decorator for registering a function to serve as a "notification center"
for the application. This function will receive the data associated with an
incoming macOS notification sent using :func:`rumps.notification`. This
occurs whenever the user clicks on a notification for this application in
the macOS Notification Center.
.. code-block:: python
@rumps.notifications
def notification_center(info):
if 'unix' in info:
print 'i know this'
"""
return events.on_notification.register(f)
def _gather_info_issue_9(): # pragma: no cover
missing_plist = False
missing_bundle_ident = False
info_plist_path = os.path.join(os.path.dirname(sys.executable), 'Info.plist')
try:
with open(info_plist_path) as f:
import plistlib
try:
load_plist = plistlib.load
except AttributeError:
load_plist = plistlib.readPlist
try:
load_plist(f)['CFBundleIdentifier']
except Exception:
missing_bundle_ident = True
except IOError as e:
import errno
if e.errno == errno.ENOENT: # No such file or directory
missing_plist = True
info = '\n\n'
if missing_plist:
info += 'In this case there is no file at "%(info_plist_path)s"'
info += '\n\n'
confidence = 'should'
elif missing_bundle_ident:
info += 'In this case the file at "%(info_plist_path)s" does not contain a value for "CFBundleIdentifier"'
info += '\n\n'
confidence = 'should'
else:
confidence = 'may'
info += 'Running the following command %(confidence)s fix the issue:\n'
info += '/usr/libexec/PlistBuddy -c \'Add :CFBundleIdentifier string "rumps"\' %(info_plist_path)s\n'
return info % {'info_plist_path': info_plist_path, 'confidence': confidence}
def _default_user_notification_center():
notification_center = NSUserNotificationCenter.defaultUserNotificationCenter()
if notification_center is None: # pragma: no cover
info = (
'Failed to setup the notification center. This issue occurs when the "Info.plist" file '
'cannot be found or is missing "CFBundleIdentifier".'
)
try:
info += _gather_info_issue_9()
except Exception:
pass
raise RuntimeError(info)
else:
return notification_center
def _init_nsapp(nsapp):
if _ENABLED:
try:
notification_center = _default_user_notification_center()
except RuntimeError:
pass
else:
notification_center.setDelegate_(nsapp)
@_internal.guard_unexpected_errors
def _clicked(ns_user_notification_center, ns_user_notification):
from . import rumps
ns_user_notification_center.removeDeliveredNotification_(ns_user_notification)
ns_dict = ns_user_notification.userInfo()
if ns_dict is None:
data = None
else:
dumped = ns_dict['value']
app = getattr(rumps.App, '*app_instance', rumps.App)
try:
data = app.serializer.loads(dumped)
except Exception:
traceback.print_exc()
return
# notification center function not specified => no error but log warning
if not events.on_notification.callbacks:
rumps._log(
'WARNING: notification received but no function specified for '
'answering it; use @notifications decorator to register a function.'
)
else:
notification = Notification(ns_user_notification, data)
events.on_notification.emit(notification)
def notify(title, subtitle, message, data=None, sound=True,
action_button=None, other_button=None, has_reply_button=False,
icon=None, ignoreDnD=False):
"""Send a notification to Notification Center (OS X 10.8+). If running on a
version of macOS that does not support notifications, a ``RuntimeError``
will be raised. Apple says,
"The userInfo content must be of reasonable serialized size (less than
1k) or an exception will be thrown."
So don't do that!
:param title: text in a larger font.
:param subtitle: text in a smaller font below the `title`.
:param message: text representing the body of the notification below the
`subtitle`.
:param data: will be passed to the application's "notification center" (see
:func:`rumps.notifications`) when this notification is clicked.
:param sound: whether the notification should make a noise when it arrives.
:param action_button: title for the action button.
:param other_button: title for the other button.
:param has_reply_button: whether or not the notification has a reply button.
:param icon: the filename of an image for the notification's icon, will
replace the default.
:param ignoreDnD: whether the notification should ignore do not disturb,
e.g., appear also while screen sharing.
"""
from . import rumps
if not _ENABLED:
raise RuntimeError('OS X 10.8+ is required to send notifications')
_internal.require_string_or_none(title, subtitle, message)
notification = NSUserNotification.alloc().init()
notification.setTitle_(title)
notification.setSubtitle_(subtitle)
notification.setInformativeText_(message)
if data is not None:
app = getattr(rumps.App, '*app_instance', rumps.App)
dumped = app.serializer.dumps(data)
objc_string = _internal.string_to_objc(dumped)
ns_dict = Foundation.NSMutableDictionary.alloc().init()
ns_dict.setDictionary_({'value': objc_string})
notification.setUserInfo_(ns_dict)
if icon is not None:
notification.set_identityImage_(rumps._nsimage_from_file(icon))
if sound:
notification.setSoundName_("NSUserNotificationDefaultSoundName")
if action_button:
notification.setActionButtonTitle_(action_button)
notification.set_showsButtons_(True)
if other_button:
notification.setOtherButtonTitle_(other_button)
notification.set_showsButtons_(True)
if has_reply_button:
notification.setHasReplyButton_(True)
if ignoreDnD:
notification.set_ignoresDoNotDisturb_(True)
notification.setDeliveryDate_(Foundation.NSDate.dateWithTimeInterval_sinceDate_(0, Foundation.NSDate.date()))
notification_center = _default_user_notification_center()
notification_center.scheduleNotification_(notification)
class Notification(compat.collections_abc.Mapping):
def __init__(self, ns_user_notification, data):
self._ns = ns_user_notification
self._data = data
def __repr__(self):
return '<{0}: [data: {1}]>'.format(type(self).__name__, repr(self._data))
@property
def title(self):
return compat.text_type(self._ns.title())
@property
def subtitle(self):
return compat.text_type(self._ns.subtitle())
@property
def message(self):
return compat.text_type(self._ns.informativeText())
@property
def activation_type(self):
activation_type = self._ns.activationType()
if activation_type == 1:
return 'contents_clicked'
elif activation_type == 2:
return 'action_button_clicked'
elif activation_type == 3:
return 'replied'
elif activation_type == 4:
return 'additional_action_clicked'
@property
def delivered_at(self):
ns_date = self._ns.actualDeliveryDate()
seconds = ns_date.timeIntervalSince1970()
dt = datetime.datetime.fromtimestamp(seconds)
return dt
@property
def response(self):
ns_attributed_string = self._ns.response()
if ns_attributed_string is None:
return None
ns_string = ns_attributed_string.string()
return compat.text_type(ns_string)
@property
def data(self):
return self._data
def _check_if_mapping(self):
if not isinstance(self._data, compat.collections_abc.Mapping):
raise TypeError(
'notification cannot be used as a mapping when data is not a '
'mapping'
)
def __getitem__(self, key):
self._check_if_mapping()
return self._data[key]
def __iter__(self):
self._check_if_mapping()
return iter(self._data)
def __len__(self):
self._check_if_mapping()
return len(self._data)