Python push уведомления windows

  1. 1. Введение
  2. 2. Mac OS
  3. 3. Linux
  4. 4. Windows
  5. 5. Делаем скрипт кросс-платформенным
  6. 6. Вывод

Введение

Мы часто используем

print

что-бы проследить за состоянием скрипта, или когда занимаемся отладкой. Вместо того, что-бы постоянно смотреть в окно терминала и ждать какой-то вывод, мы можем исползовать всплывающие уведомления как способ для просмотра вывода и параллельно с этим заниматься чем-то еще.

Сделать это совсем не сложно — мы будем использовать встроенные комманды нашей операционной системы для вывода уведомлений, просто запуская их через Python. Несколько строчек кода, и никаких сторонних модулей.


Mac OS

Следующая команда запускает AppleScript (встроенный скриптовый язык компании Apple) для уведомлений.


osascript -e ‘display notification «Your message goes here» with title «Title»‘

Если вы запустите эту комманду то увидите следующий результат:

Подробнее о самом скрипте:

Итак, если вы используете Mac OS, тогда вы можете выполнить следующий Python код:

import os
title = "Готово"
message = "Файл скачан"
command = f'''
osascript -e 'display notification "{message}" with title "{title}"'
'''
os.system(command)

Запустив этот скрипт, вы увидите схожий результат как и выше.

Linux

Linux предлагает еще более простой вариант:


notify-send «Your message» «Title»

Аналогично тому, как мы выводили уведомление на Mac OS через os.system, так же можно поступить в Linux системе.

Windows

В Windows нет такой команды которая запускала бы уведомления как в Linux и Mac OS, но это все равно можно сделать с помощью библиотеки

win10toast

, сначала её нужно установить:

pip install win10toast

и теперь пример ее использования:

import win10toast

toaster = win10toast.ToastNotifier()
toaster.show_toast("Заголовок", "Описание уведомления")

Метод

show_toast

принимает и другие аргументы, но подробнее о них вы можете напрямую почитать — просто вызвав

help

передав ей объект функции о которой нужно узнать подробнее.

Делаем скрипт кросс-платформенным

Что-бы узнать какая система стоит на вашем компьютере используется команда

system()

из встроенной библиотеки

platform

. Для Mac OS эта команда возвращает строку «Darwin», для Linux «Linux», для Windows — «Windows», теперь, зная все это, можем поместить весь код в функцию

push

. Вот что получится:

import platform, os

def push(title, message):
    plt = platform.system()
    if plt == "Darwin":
        command = '''
        osascript -e 'display notification "{message}" with title "{title}"'
        '''
    elif plt == "Linux":
        command = f'''
        notify-send "{title}" "{message}"
        '''
    elif plt == "Windows":
        win10toast.ToastNotifier().show_toast(title, message)
        return
    else:
        return
    os.system(command)

Вывод

В статье было показано как вывести всплывающие уведомления на Python’е, определенно это был не единственный способ. Существует достаточное количество библиотек которые выполняют подобные цели.

Project description

Python Notifications

Python3 module for sending notifications.

The list of available backends:

  • Platform (pynotifier.backends.platform.Backend):
    • macOS
    • Linux
    • Windows
  • Email (pynotifier.backends.smtp.Backend)

Platform notifications requirements

Windows:

WinToaster — Python module

Linux:

libnotify-bin CLI tool (manual installation is required). For Ubuntu run:

sudo apt-get install libnotify-bin

Installation

Install using pip:

pip install py-notifier

Example

import ssl
import smtplib

from pynotifier import NotificationClient, Notification
from pynotifier.backends import platform, smtp

smtp_server = smtplib.SMTP_SSL('smtp.gmail.com', 465, context=ssl.create_default_context())

c = NotificationClient()

c.register_backend(platform.Backend())
c.register_backend(smtp.Backend(server=smtp_server,
                                name='My Organization',
                                email='sender@organization.com',
                                password='super_password'))

filenames = [
  'path/to/file1.json',
  'path/to/file2.txt',
  # ...
]

attachments = []
for filename in filenames:
	attachments.append(smtp.Attachment(open(filename, 'rb'), filename))

smtp_config = smtp.NotificationConfig(emails=['receiver_1@email.com', 'receiver_2@email.com'],
                                      attachments=attachments)
notification = Notification(title='Hello', message='World', smtp=smtp_config)

c.notify_all(notification)

License

The project is licensed under the terms of the MIT License,
see the LICENSE file for more information.

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

File details

Details for the file py-notifier-0.5.0.tar.gz.

File metadata

  • Download URL:
    py-notifier-0.5.0.tar.gz

  • Upload date:
  • Size: 7.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.8.16

File hashes

Hashes for py-notifier-0.5.0.tar.gz

Algorithm Hash digest
SHA256 88445d25a20f901f4821cc7a17bf8f873c11c2316288083afb1277bd1a8d6112
MD5 d26af3e2c5c6ff660e1ed4d340d853e3
BLAKE2b-256 95431c61910687afd4e896b504d61dcb7eecb7c2816d5f154237853fafc143eb

See more details on using hashes here.

File details

Details for the file py_notifier-0.5.0-py3-none-any.whl.

File metadata

  • Download URL:
    py_notifier-0.5.0-py3-none-any.whl

  • Upload date:
  • Size: 10.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.8.16

File hashes

Hashes for py_notifier-0.5.0-py3-none-any.whl

Algorithm Hash digest
SHA256 cb75a4ae1ed7839b5794dc94c0dd5b38e791c32f53041e99c64f71d9a1dfb8d3
MD5 0d1716fbdd041882a5f4769efa207c04
BLAKE2b-256 cb93922e37bfd08533315863b9afb2184377c540a8b2545b0ded1b459c4ffbdd

See more details on using hashes here.

Всем доброго времени суток. Давайте поговорим сегодня о простой, но далеко не самой очевидной вещи, которая не лежит не виду, а когда с ней сталкиваешься, приходиться некоторое время гуглить информацию. О всплывающих сообщениях. Ведь порою в скриптах нужно отображать информацию не только в консоли. А если приложение имеет еще и пользовательский интерфейс, то сообщения, конечно же, лучше выводить без ее использования.

000.jpg

Что потребуется?

Тут все зависит от операционной системы и способа, которым вы выберете выводить сообщение. Если операционная система Linux, то здесь может не потребоваться ничего, так как будет использоваться функция операционной системы. А можно установить специальную библиотеку notify2. Установка происходит через терминал с помощью команды pip:

pip install notify2

А дальше импортируем в скрипт.

import notify2

Всплывающие сообщения в Linux

Давайте посмотрим, какие сообщения можно вывести с помощью данной библиотеки, и с какими небольшими «подводными камнями» я столкнулся при ее использовании.

Python:

icon_path = "/usr/share/icons/gnome/24x24/emotes/face-laugh.png"
notify2.init(text)
mess = notify2.Notification(text, icon=icon_path)
mess.set_urgency(notify2.URGENCY_NORMAL)
mess.show()

Первая переменная здесь – это просто путь до иконки, который будет выводиться в сообщении. Дальше инициализируем соединение d-bus. Это механизм, с помощью которого процессы взаимодействуют друг с другом и который используют программы в Linux. А потому, нужно подключиться через d-bus к процессу, который управляет системным интерфейсом и передать ему такие параметры, как значок, заголовок и текст сообщения.

Далее создаем объект уведомлений, в который передаем сообщение и его иконку. И, если в этом есть необходимость, можно установить уровень срочности сообщения, у которого их целых три: URGENCY_LOW, URGENCY_CRITICAL, URGENCY_NORMAL. Ну и с помощью метода show() показываем сообщение.

Вот в принципе и все. Казалось бы, какие тут могут быть «камни»? Но, я попытался запустить данный код в Kali. И о чудо, я получил ошибку, которая гласила, что d-bus не установлен в системе. Оказывается, бывает и такое. Тогда я сделал обработку ошибки и вывел сообщение уже более простым способом:

Python:

command = f'''notify-send {text}'''
system(command)

где command – это собственно команда и сообщение. А system эту команду исполняет. В итоге, получилась небольшая функция, которую я назвал linux_notify(text), на входе принимающая текст сообщения:

Python:

def linux_notify(text):
    try:
        import notify2
        icon_path = "/usr/share/icons/gnome/24x24/emotes/face-laugh.png"
        notify2.init(text)
        mess = notify2.Notification(text, icon=icon_path)
        mess.set_urgency(notify2.URGENCY_NORMAL)
        mess.show()
    except ModuleNotFoundError:
        command = f'''notify-send {text}'''
        system(command)

Всплывающие сообщение в Windows

Ну и после этого я подумал, а не поискать ли мне простые способы отображения всплывающих сообщений и в Windows. Подумал, поискал и вот, что нашел. Для начала — win10toast. Устанавливаем в терминале. Тут все стандартно:

pip install win10toast

Затем импортируем в скрипт:

from win10toast import ToastNotifier

и уже можно с ней работать.

Python:

ToastNotifier().show_toast("Демо сообщение", text, icon_path=r"C:\Windows\WinSxS\amd64_microsoft-windows-dxp-"
                                                                 r"deviceexperience_31bf3856ad364e35_10.0.19041.746"
                                                                 r"_none_251e769058968366\sync.ico",
                               duration=3, threaded=True)

Итак. Первый параметр, это заголовок сообщения. Второй, само сообщение. Третий – путь к иконке. Я нашел в недрах винды что-то более-менее подходящее. Не факт, что есть на других машинах, но в теории должно быть, так то. Параметр duration отвечает за количество секунд, которые будет отображаться сообщение. Ну и threaded. Забавный параметр, который обязательно включить, если вы сделали пользовательский интерфейс в PyQt. Иначе, после вывода сообщения окно программы у вас будет просто закрываться. Насколько я понял, это потоковый параметр, который ждет завершения работы уведомления. Это был первый способ.

Второй способ немного проще. Устанавливаем библиотеку plyer:

pip install plyer

и импортируем ее в скрипт:

from plyer import notification

А затем пишем простой код:

Python:

notification.notify(message=text, app_icon=r"C:\Windows\WinSxS\amd64_microsoft-windows-dxp-deviceexperience_"
                                               r"31bf3856ad364e35_10.0.19041.746_none_251e769058968366\sync.ico")

И все. Сообщение готово. Первый параметр тут текст сообщения. Второй – путь к иконке. И все работает. Причем в любых приложениях на винде. Хоть консольных, хоть с пользовательским интерфейсом.

Такие вот способы, которые более-менее позволяют вывести сообщение в Linux и Windows. Без сомнения, есть и другие, более сложные способы. Но, чтобы не лезть в дебри, а просто вывести сообщение, этих способов будет достаточно. И я оформил все в небольшой код. Который, в общем-то, не обязателен, но так все же приятнее и легче он читается.

Python:

# pip install notify2
# pip install win10toast
# pip install plyer

from platform import system as psystem
import time
from os import system


def linux_notify(text):
    try:
        import notify2
        icon_path = "/usr/share/icons/gnome/24x24/emotes/face-laugh.png"
        notify2.init(text)
        mess = notify2.Notification(text, icon=icon_path)
        mess.set_urgency(notify2.URGENCY_NORMAL)
        mess.show()
    except ModuleNotFoundError:
        command = f'''notify-send {text}'''
        system(command)


def windows_notify(text):
    from plyer import notification
    from win10toast import ToastNotifier
    ToastNotifier().show_toast("Демо сообщение", text, icon_path=r"C:\Windows\WinSxS\amd64_microsoft-windows-dxp-"
                                                                 r"deviceexperience_31bf3856ad364e35_10.0.19041.746"
                                                                 r"_none_251e769058968366\sync.ico",
                               duration=3, threaded=True)
    time.sleep(4)

    notification.notify(message=text, app_icon=r"C:\Windows\WinSxS\amd64_microsoft-windows-dxp-deviceexperience_"
                                               r"31bf3856ad364e35_10.0.19041.746_none_251e769058968366\sync.ico")


if __name__ == "__main__":
    message = input('[+] Введите текст сообщение: ')

    if psystem() == "Linux":
        linux_notify(message)
    elif psystem() == "Windows":
        windows_notify(message)

А на этом все. Какой из библиотек пользоваться, решать без сомнения вам.

Спасибо за внимание. Надеюсь, что данная информация будет кому-нибудь полезна

notify.py

Cross platform desktop notifications for Python scripts and applications.

Docs

You can read the docs on this Git’s Wiki, or here

Supported Platforms.

  • Windows 10/11
  • macOS 10 >=10.10
  • Linux (libnotify)

No dependencies are required other than loguru & jeepney (Only for linux/DBUS).


Install


Usage

Send Simple Notification

from notifypy import Notify

notification = Notify()
notification.title = "Cool Title"
notification.message = "Even cooler message."
notification.send()

Send Notification With Icon

from notifypy import Notify

notification = Notify()
notification.title = "Cool Title"
notification.message = "Even cooler message."
notification.icon = "path/to/icon.png"

notification.send()

Send Notification With Sound

from notifypy import Notify

notification = Notify()
notification.title = "Cool Title"
notification.message = "Even cooler message."
notification.audio = "path/to/audio/file.wav"

notification.send()

Sending Notifications without blocking

from notifypy import Notify

notification = Notify()
notification.send(block=False)

Sending with Default Notification Titles/Messages/Icons

from notifypy import Notify

notification = Notify(
  default_notification_title="Function Message",
  default_application_name="Great Application",
  default_notification_icon="path/to/icon.png",
  default_notification_audio="path/to/sound.wav"
)

def your_function():
  # stuff happening here.
  notification.message = "Function Result"
  notification.send()

CLI

A CLI is available when you install notify-py

notifypy --title --message --applicationName --iconPath --soundPath

You may need to add python3 -m to the beginning.


Important Caveats

  • As it stands (May 18, 2020), this is simply a notification service. There is no support for embedding custom actions (buttons, dialogs) regardless of platform. Other then telling you if the shell command was sent, there is also no confirmation on user action on the notification.

  • macOS does not support custom icons on the fly.. You will need to bundle a customized version of the notifier embedded with your custom icon.


Windows Specific.

  • No support for balloon tips (pre Win10).. This will be changed in the future.

Contributors

  • Leterax
  • jnoortheen
  • dynobo
  • Xou

Inspiration and Special Thanks

  • https://github.com/go-toast/toast — Ported their Windows 10 toast notification to Python.

  • Vítor Galvão for https://github.com/vitorgalvao/notificator

  • https://notificationsounds.com/notification-sounds/done-for-you-612 example_notification_sound.wav

  • https://github.com/mikaelbr/node-notifier


Contributing

Contributions are welcome!

Please base your changes on the latest development branch and open a PR to that branch. PR will not be accepted to the master branch. Tests are ran against all platforms.

Setting Up Environment

  • Install Poetry
    • poetry install
  • Add patches/new features/bug fiexes
  • Run tests
    • poetry run pytest tests/*
  • Run lints
    • poetry run pylint --errors-only notifypy/
  • Run Black Formatting
    • poetry run black notifypy
  • Open PR to dev branch.
  • Add your name to contributors list if you wish!

Зачем еще одно руководство?

Когда передо мной поставили задачу сделать черновой вариант push notifications, быстрый поиск показал, что на хабре уже есть много статей по настройке push notifications. Вот наиболее, на мой взгляд, годные:

Как работает JS: веб push-уведомления
Web PUSH Notifications быстро и просто924/
Service Workers. Web Push и где они обитают

Это все прекрасно, но лично мне очень не хватало простого и понятного руководства, которое позволило бы сразу, практически методом копипаста сделать так, чтобы все сразу заработало. Ну и кроме того среди руководств нет адаптированного под бек на питоне.

Настройка уведомлений в итоге заняла три дня и мне кажется, что это несколько многовато. Надеюсь, моя статья поможет кому-то настроить push notifications за три часа вместо трех дней.
Проект, на котором я работаю, реализован на Django и описывать ход работы я буду применительно к этому фреймворку, но желающие легко адаптируют его к Flask или чему-либо еще.

Итак, погнали.

Получаем ключи

Без ключей нас, естественно, никуда не пустят, поэтому начнем с них. Для генерации ключей я использовала py_vapid. Он устанавливается автоматически вместе с pywebpush, который нам все равно понадобится позже, поэтому мы, чтобы два раза не вставать, начнем с pywebpush:

> bin/pip install pywebpush
<Здесь вывод результата установки>
> bin/vapid --applicationServerKey
No private_key.pem file found.        
Do you want me to create one for you? (Y/n)Y
Generating private_key.pem
Generating public_key.pem
Application Server Key = <Мой Server Key>

Полученное значение Application Server Key добавляем в файл settings.py.

NOTIFICATION_KEY = <Мой Server Key>

Кроме того, нам нужно будет передать NOTIFICATION_KEY в контекст. Проще всего для этого написать свой context_processor.

Делаем Service worker

Service worker, кто не знает — это специальный файл, работающий в фоновом режиме. Нам он нужен для отображения наших уведомлений.

Самый простой код service worker будет выглядеть так:

self.addEventListener('push', function(event) {
  var message = JSON.parse(event.data.text()); //
  event.waitUntil(
    self.registration.showNotification(message.title, {
      body: message.body,
    })
  );
});

А теперь нам нужно зарегистрировать наш service worker. Процесс, в принципе, описан здесь. Поэтому кратко:


function checkWorkerAndPushManager () {
    if (!('serviceWorker' in navigator)) {
        console.log('Workers are not supported.');
        return;
    }
    if (!('PushManager' in window)) {
        console.log('Push notifications are not supported.');
        return;
    }
}

function registerWorker () {
	window.addEventListener('load', function () {
        navigator.serviceWorker.register('/static/js/sw.js').then(function (registration) {
            console.log('ServiceWorker registration successful');
        }, function (err) {
            console.log('ServiceWorker registration failed: ', err);
            return;
        });
    });
	return true;
}

var supported = checkWorkerAndPushManager();

if (supported){
        var worker = registerWorker ();
}

Отлично, можно проверить работу нашего service worker. Для этого зайдите в Developer Tools в браузере, убедитесь, что в консоли появилось сообщение об успешной регистрации вокера и перейдите на вкладку Application и слева выберите Service Worker.

image

Если уведомление не появляется — проверьте, что у вас в браузере разрешены уведомления.

Ну вот, мы уже умеем оправлять уведомления сами себе. Круто, едем дальше.

Получаем разрешение пользователя на показ уведомлений

После того, как вокер зарегистрирован, попросим у пользователя разрешение показывать уведомления.


function requestPermission() {
  return new Promise(function(resolve, reject) {
    const permissionResult = Notification.requestPermission(function(result) {
      // Поддержка устаревшей версии с функцией обратного вызова.
      resolve(result);
    });
    if (permissionResult) {
      permissionResult.then(resolve, reject);
    }
  })
  .then(function(permissionResult) {
    if (permissionResult !== 'granted') {
      console.log(permissionResult);
      throw new Error('Permission not granted.');
    }
  });
  return true;
}

К этому коду и комментариев никаких не нужно, он просто работает.

Подписываемся и сохраняем подписку

Подписка выполняется тоже на фронте. Здесь можно найти код подписки, но нет используемой функции urlBase64ToUint8Array, поэтому я код вместе с ней.


NOTIFICATION_KEY = '{{ NOTIFICATION_KEY }};

function urlBase64ToUint8Array(base64String) {
  const padding = '='.repeat((4 - base64String.length % 4) % 4);
  const base64 = (base64String + padding)
    .replace(/\-/g, '+')
    .replace(/_/g, '/')
  ;
  const rawData = window.atob(base64);
  return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)));
}

function subscribeUserToPush(key) {
   return navigator.serviceWorker.register('/static/path/to/js/sw.js')
  .then(function(registration) {
        var subscribeOptions = {
            userVisibleOnly: true,
            applicationServerKey: urlBase64ToUint8Array(key),
        };
   return registration.pushManager.subscribe(subscribeOptions)
   })
  .then(function(pushSubscription) {
    sendSubscriptionToBackEnd(pushSubscription);
  });
}

(Используемая здесь urlBase64ToUint8Array, видимо, из разряда костылей и велосипедов, но попытка перекодировать данные с помощью btoa не привела к успеху, уж не знаю, почему. Может кто подскажет).

Далее полученные данные отправляем на сервер. У меня это реализовано так:


function sendSubscriptionToBackEnd(subscription) {
    $.post(
        SAVE_REGISTRATION_URL,
        {
           'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val(),
           //Естественно, в темплейте есть тег {% csrf_token %}.
            'registration_data': JSON.stringify(subscription)
        }
    );
}

Ну и дальше сохраняем это на сервере. Можно прямо строкой. Да, не вздумайте делать связь user-subscription типа «один к одному». Вроде бы очевидно, но вдруг кому вздумается.
У меня для сохранения используется вот такая простая модель, она дальше будет использоваться, поэтому я ее приведу:

class UserSubscription(models.Model):
    subscription = models.CharField(max_length=500)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='subscriptions')

Последний шаг. Отправляем сообщение с помощью pywebpush

Здесь все по туториалу, никаких особенных тонкостей. Ну, разве что сообщение делаем такой структуры, чтобы наш service worker мог его разобрать.

import json
from pywebpush import webpush, WebPushException
from django.conf import settings

from .models import UserSubscription


def push_notification(user_id):
    user_subscriptions = UserSubscription.objects.filter(user_id=notification.user_id)
    for subscription in user_subscriptions:
        data = json.dumps({
        'title': 'Hello',
        'body': 'there',
    })
        try:
            webpush(
                subscription_info=json.loads(subscription.subscription),
                data=data,
                vapid_private_key='./private_key.pem',
                vapid_claims={
                    'sub': 'mailto:{}'.format(settings.ADMIN_EMAIL),
                }
            )
            notification.sent = True
            notification.save()
        except WebPushException as ex:
            print('I\'m sorry, Dave, but I can\'t do that: {}'.format(repr(ex)))
            print(ex)
            # Mozilla returns additional information in the body of the response.
            if ex.response and ex.response.json():
                extra = ex.response.json()
                print('Remote service replied with a {}:{}, {}',
                      extra.code,
                      extra.errno,
                      extra.message
                      )

Собственно, уже можно вызывать push_notification из django shell и пытаться запустить.
В этом коде хорошо бы еще сделать перехват ответа со статусом 410. Такой ответ говорит, что подписка аннулирована, и такие подписки хорошо бы удалять из БД.

В заключение

На самом деле есть еще одна замечательная библиотека django-webpush. Возможно, тем, кто работает с Django, стоит начинать именно с нее.

P.S. Всех с Днем программиста!

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Версии windows 10 pro education
  • Windows ustanovka cherez internet
  • Mak key windows server 2019
  • Lotin kirill tarjimon programma skachat windows 10
  • Крестики возле ярлыков windows 10