├── requirements.txt ├── pushover ├── __init__.py └── pushover.py ├── .gitignore ├── example.pushover ├── setup.py ├── test.py └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | Requests==2.32.3 2 | -------------------------------------------------------------------------------- /pushover/__init__.py: -------------------------------------------------------------------------------- 1 | from .pushover import * 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | *.pyo 4 | .DS_* 5 | build/ 6 | dist/ 7 | *.egg-info/ 8 | .pushover 9 | -------------------------------------------------------------------------------- /example.pushover: -------------------------------------------------------------------------------- 1 | [pushover] 2 | # fill in the values with your app token and user key 3 | # save this file as .pushover in your homedirectory. 4 | # go to https://pushover.net/apps/build to create an app key 5 | app_key = ############################### 6 | # put your user key from http://pushover.net here 7 | user_key = ############################## 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | 5 | setup( 6 | name='pushover', 7 | version='0.5', 8 | description='Send messages via https://pushover.net/ to phones', 9 | author='Larry Price, Daniele Sluijters, Josh Kaizoku, Bryce Jasmer', 10 | author_email='laprice@gmail.com, github@daenney.net, kaizoku@phear.cc, bryce@jasmer.com', 11 | py_modules=['pushover.pushover'], 12 | install_requires = ['Requests == 2.32.3'], 13 | license='BSD', 14 | url='http://github.com/laprice/pushover', 15 | entry_points={ 16 | 'console_scripts': ['pushover = pushover.pushover:main'] 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from pushover import PushoverClient, PushoverException, PushoverMessageTooBig 4 | import sys 5 | 6 | def test_no_config(): 7 | try: 8 | ps = PushoverClient(configfile="file_does_not_exist") 9 | except PushoverException: 10 | cls, instance, traceback = sys.exc_info() 11 | assert(instance.message=="No valid configuration found") 12 | return 13 | 14 | def test_message_too_big(): 15 | try: 16 | ps = PushoverClient() 17 | ps.send_message(""" 18 | Whereas recognition of the inherent dignity and of the equal and inalienable rights of all members of the human family is the foundation of freedom, justice and peace in the world, 19 | 20 | Whereas disregard and contempt for human rights have resulted in barbarous acts which have outraged the conscience of mankind, and the advent of a world in which human beings shall enjoy freedom of speech and belief and freedom from fear and want has been proclaimed as the highest aspiration of the common people, 21 | 22 | Whereas it is essential, if man is not to be compelled to have recourse, as a last resort, to rebellion against tyranny and oppression, that human rights should be protected by the rule of law, 23 | 24 | Whereas it is essential to promote the development of friendly relations between nations,""") 25 | except PushoverMessageTooBig as instance: 26 | assert(str(instance)=="The supplied message is bigger than 512 characters.") 27 | return 28 | 29 | def test_send_message(): 30 | ps = PushoverClient() 31 | ps.send_message("Test message from PushoverClient") 32 | 33 | def test_message_with_kwargs(): 34 | """pushover.net updated their API to support a number of additional arguments 35 | see https://pushover.net/api 36 | """ 37 | ps = PushoverClient() 38 | ps.send_message("Fancy Test Message", 39 | title="fancy test", 40 | url="https://pushover.net/api", 41 | priority="1") 42 | 43 | if __name__=="__main__": 44 | test_no_config() 45 | test_message_too_big() 46 | test_send_message() 47 | test_message_with_kwargs() 48 | 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python Pushover 2 | 3 | This is a library and a commandline client to the [Pushover.net](http://pushover.net/ "Pushover") notification service. 4 | 5 | ## Requirements 6 | 7 | * Either JSON (Python 2.6+) or [simplejson](http://pypi.python.org/pypi/simplejson/ "simplejson") (Python 2.5+) 8 | * [requests](http://docs.python-requests.org/en/latest/index.html "python requests") 9 | 10 | ## Import as a module 11 | 12 | You can import pushover simply with ```import pushover``` 13 | 14 | When initialising a ```PushoverCLient``` you may pass a path to a pushover configuration file as seen in example.pushover. The library will also look for a ```$HOME/.pushover``` but the configuration file passed in manually takes precedence. 15 | 16 | ```python 17 | import pushover 18 | client = pushover.PushoverClient("/a/path/to/a/file") 19 | try: 20 | client.send_message("Some message") 21 | except SomeError: 22 | ... deal with it ... 23 | ``` 24 | 25 | Take note that when using this as a module the message you send will not be automatically truncated to 512 characters. It is up to your application code to ensure the message passed meets the requirements or catch the ```pushover.PushoverMessageTooBig``` exception. 26 | 27 | ## Commandline 28 | 29 | Just run ```pushover --help``` for help. 30 | 31 | You'll have to at least supply a ```-m some_message``` or ```--message=some_message``` and you may supply the debug flag or an alternative configuration file. 32 | 33 | Contrary to when this module is imported, when called on the commandline the message will be truncated to 512 characters in order to avoid those kind of errors. 34 | 35 | ``` 36 | Usage: pushover [options] 37 | 38 | This module will send a message through the Pushover.net notification service. 39 | It requires at least the '-m' / '--message' parameter to be passed. 40 | 41 | Options: 42 | -h, --help show this help message and exit 43 | -c CONFIGFILE, --config=CONFIGFILE 44 | Location of the Pushover config file. 45 | -d, --debug Log at the DEBUG loglevel. 46 | -m MESSAGE, --message=MESSAGE 47 | The message to send, will truncate to 512 chars. 48 | ``` 49 | 50 | ## Todo 51 | 52 | * Be able to pass in a configurationobject instead of a file when being imported. 53 | -------------------------------------------------------------------------------- /pushover/pushover.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import os 5 | import requests 6 | 7 | # ConfigParser renamed to configparser in Python 3 8 | try: 9 | from ConfigParser import SafeConfigParser as CP 10 | except ModuleNotFoundError: 11 | from configparser import ConfigParser as CP 12 | except ImportError: 13 | from configparser import SafeConfigParser as CP 14 | 15 | try: 16 | import simplejson as json 17 | except ImportError: 18 | try: 19 | import json 20 | except ImportError: 21 | sys.exit("Please install the simplejson library or upgrade to Python 2.6+") 22 | 23 | import logging 24 | # Set up logging and add a NullHandler 25 | logger = logging.getLogger(__name__) 26 | try: 27 | logger.addHandler(logging.NullHandler()) 28 | except AttributeError: 29 | class NullHandler(logging.Handler): 30 | def emit(self, record): 31 | pass 32 | logger.addHandler(NullHandler()) 33 | 34 | class PushoverException(Exception): 35 | pass 36 | 37 | class PushoverMessageTooBig(PushoverException): 38 | pass 39 | 40 | class PushoverClient(object): 41 | """ PushoverClient, used to send messages to the Pushover.net service. """ 42 | def __init__(self, configfile=""): 43 | self.configfile = configfile 44 | self.parser = CP() 45 | self.files = self.parser.read([self.configfile, os.path.expanduser("~/.pushover")]) 46 | if not self.files: 47 | logger.critical("No valid configuration found, exiting.") 48 | raise PushoverException("No valid configuration found") 49 | self.conf = { "app_key": self.parser.get("pushover","app_key"), 50 | "user_key": self.parser.get("pushover","user_key")} 51 | 52 | def send_message(self, message, **kwargs): 53 | if len(message) > 512: 54 | raise PushoverMessageTooBig("The supplied message is bigger than 512 characters.") 55 | payload = { 56 | "token": self.conf["app_key"], 57 | "user" : self.conf["user_key"], 58 | "message": message, 59 | } 60 | for key,value in kwargs.items(): 61 | payload[key] = value 62 | r = requests.post("https://api.pushover.net/1/messages.json", data=payload ) 63 | if not r.status_code == requests.codes.ok: 64 | raise r.raise_for_status() 65 | 66 | def main(): 67 | from optparse import OptionParser 68 | parser = OptionParser() 69 | parser.description = "This module will send a message through the Pushover.net notification service. It requires at least the '-m' / '--message' parameter to be passed." 70 | parser.add_option("-c", "--config", dest = "configfile", help = "Location of the Pushover config file.") 71 | parser.add_option("-d", "--debug", dest = "debug", action = "store_true", help = "Log at the DEBUG loglevel.") 72 | parser.add_option("-m", "--message", dest = "message", help = "The message to send, will truncate to 512 chars.") 73 | if len(sys.argv) <= 1: 74 | parser.print_help() 75 | sys.exit(1) 76 | (options, args) = parser.parse_args() 77 | 78 | if options.debug: 79 | loglevel = logging.DEBUG 80 | else: 81 | loglevel = logging.INFO 82 | logging.basicConfig(level=loglevel, format="%(asctime)s [%(module)s] %(levelname)s: %(message)s") 83 | 84 | if options.configfile: 85 | client = PushoverClient(options.configfile) 86 | else: 87 | client = PushoverClient() 88 | 89 | if options.message: 90 | options.message = options.message[:512] 91 | else: 92 | parser.error("Can't do anything without a message now can I?") 93 | 94 | try: 95 | client.send_message(options.message) 96 | except Exception as e: 97 | logger.critical("Something went wrong: {0}".format(e)) 98 | 99 | --------------------------------------------------------------------------------