Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@ target/
tests/helpers/keys.py
.pypirc
sandbox.py
*.backup
3 changes: 3 additions & 0 deletions py_pushover/Constants/OS.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ANDROID = 'Android'
IOS = 'iOS'
DESKTOP = 'Desktop'
2 changes: 1 addition & 1 deletion py_pushover/Constants/PRIORITIES.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
Priority - Collection of Priorities.

This is to be used with the 'priority' arguemnt of the PushOverManager.push_notification method.
This is to be used with the 'priority' argument of the push_message function/method.

see also: https://pushover.net/api#priority
"""
Expand Down
5 changes: 3 additions & 2 deletions py_pushover/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from py_pushover.Constants import PRIORITIES, SOUNDS
from py_pushover.Constants import PRIORITIES, SOUNDS, OS
from py_pushover._base import BaseManager, send, base_url
from py_pushover import client, groups, license, message, subscription, verification


__all__ = ['PRIORITIES', 'SOUNDS', 'client', 'groups', 'license', 'message', 'subscription', 'verification']
__all__ = ['PRIORITIES', 'SOUNDS', 'OS', 'client', 'groups', 'license', 'message', 'subscription', 'verification']


28 changes: 15 additions & 13 deletions py_pushover/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,11 @@
"""
import websocket
import logging
from multiprocessing import Process
from multiprocessing import Process, Pipe

from py_pushover import BaseManager, send, base_url

logging.basicConfig(filename='client.log', level=logging.INFO)

class ClientManager(BaseManager):
"""
Expand Down Expand Up @@ -122,9 +123,7 @@ def __init__(self, app_token, secret=None, device_id=None):
on_close=self._on_ws_close
)
self.__on_msg_receipt__ = None
self.__p__ = None

self.__logger__ = logging.basicConfig(filename='client.log')
self.__p__ = Process()

@property
def secret(self):
Expand Down Expand Up @@ -239,9 +238,9 @@ def _on_ws_open(self, ws):

:param ws: the websocket
"""
self.__logger__.info("Opening connection to Pushover server...")
logging.info("Opening connection to Pushover server...")
ws.send(self._ws_login.format(device_id=self.__device_id__, secret=self.__secret__))
self.__logger__.info("----Server Connection Established----")
logging.info("----Server Connection Established----")

def _on_ws_message(self, ws, message):
"""
Expand All @@ -251,26 +250,29 @@ def _on_ws_message(self, ws, message):
1. `#` - Keep-alive packet, no response needed.
2. `!` - A new message has arrived; you should perform a sync.
3. `R` - Reload request; you should drop your connection and re-connect.
4. `E` - Error; a permanent problem occured and you should not automatically re-connect. Prompt the user to login again or re-enable the device.
4. `E` - Error; a permanent problem occured and you should not automatically re-connect.
Prompt the user to login again or re-enable the device.

:param ws: the websocket
:param message: message received from remote server
"""
message = message.decode("utf-8")
self.__logger__.debug("Message received: " + message)
logging.debug("Message received: " + message)
if message == "#":
pass

elif message == "!":
self.retrieve_message()
self.__on_msg_receipt__(self.messages)
if self.__on_msg_receipt__:
self.__on_msg_receipt__(self.messages)

elif message == "R":
self.__logger__.info("Reconnecting to server (requested from server)...")
logging.info("Reconnecting to server (requested from server)...")
ws.close()
self.listen(self.__on_msg_receipt__)

elif message == "E":
self.__logger__.error("Server connection failure!")
logging.error("Server connection failure!")

else: # message isn't of the type expected. Raise an error.
raise NotImplementedError #todo Implement an appropriate exception
Expand All @@ -282,14 +284,14 @@ def _on_ws_error(self, ws, error):
:param ws: the websocket
:param error: the error encountered
"""
self.__logger__.error('Error: ' + error)
logging.error('Error: ' + error)

def _on_ws_close(self, ws):
"""
Function used when the websocket closes the connection to the remote server.

:param ws: the websocket
"""
self.__logger__.info("----Server Connection Closed----")
logging.info("----Server Connection Closed----")
self._ws_app = None

41 changes: 39 additions & 2 deletions py_pushover/license.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from py_pushover import BaseManager
from py_pushover import BaseManager, send, base_url

_assign_url = base_url + "licenses/assign.json"


class LicenseManager(BaseManager):
Expand All @@ -10,5 +12,40 @@ def __init__(self, app_token, user_key=None, email=None):
self._email = email

if self._email is None and self._user_key is None:
raise NotImplementedError # todo: identify or create a relevant error
raise ValueError("An email or user_key is required")

raise NotImplementedError

def assign(self, os=None):
assign_license(self._app_token, user=self._user_key, email=self._email, os=os)


def assign_license(token, user=None, email=None, os=None):
"""

Args:
token:
user:
email:
os:

Returns:

"""
if not(user) and not(email):
raise ValueError("An email or user_key is required")

params = {
'token':token
}

# if both user key and email are sent, we prefer the user key
if user:
params['user'] = user
else:
params['email'] = email

if os:
params['os'] = os

send(_assign_url, data_out=params)
4 changes: 2 additions & 2 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ def full_suite():
unittest.TestLoader().loadTestsFromTestCase(TestBasic),
unittest.TestLoader().loadTestsFromTestCase(TestClient),
unittest.TestLoader().loadTestsFromTestCase(TestGroup),
unittest.TestLoader().loadTestsFromTestCase(TestLicense),
#unittest.TestLoader().loadTestsFromTestCase(TestLicense),
unittest.TestLoader().loadTestsFromTestCase(TestMessage),
unittest.TestLoader().loadTestsFromTestCase(TestSubscription),
#unittest.TestLoader().loadTestsFromTestCase(TestSubscription),
unittest.TestLoader().loadTestsFromTestCase(TestVerifcation)
])
4 changes: 2 additions & 2 deletions tests/helpers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from tests.helpers.keys import user_key, group_key, app_key
from tests.helpers.keys import user_key, group_key, app_key, secret, device_id

__all__ = ['user_key', 'group_key', 'app_key']
__all__ = ['user_key', 'group_key', 'app_key', 'secret', 'device_id']
50 changes: 28 additions & 22 deletions tests/runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,23 @@
class TestMessage(unittest.TestCase):
def setUp(self):
self.pm = py_po.message.MessageManager(app_key, user_key)
# self.client = py_po.client.ClientManager(app_key, secret=secret, device_id=device_id)
# self.cleanUpClient()
self.client = py_po.client.ClientManager(app_key, secret=secret, device_id=device_id)
self.cleanUpClient()
# self.client.listen_async(self.client_message_receieved)

# def tearDown(self):
# self.client.stop_listening()
# self.cleanUpClient()
def tearDown(self):
# self.client.stop_listening()
self.cleanUpClient()

# def cleanUpClient(self):
# self.client.retrieve_message()
# for msg in self.client.messages:
# if msg['priority'] >= py_po.PRIORITIES.EMERGENCY:
# self.client.acknowledge_message(msg['receipt'])
# self.client.clear_server_messages()
#
# self.client.retrieve_message()
# self.assertEquals(len(self.client.messages), 0)
def cleanUpClient(self):
self.client.retrieve_message()
for msg in self.client.messages:
if msg['priority'] >= py_po.PRIORITIES.EMERGENCY and msg['acked'] != 1:
self.client.acknowledge_message(msg['receipt'])
self.client.clear_server_messages()

self.client.retrieve_message()
self.assertEquals(len(self.client.messages), 0)

def client_message_receieved(self, messages):
self.stored_messages = messages
Expand All @@ -50,28 +50,34 @@ def test_val_msg(self):
# Testing a normal push message
send_message = 'Testing normal push'
self.pm.push_message(send_message, device='test_device')
#self.assertEquals(send_message, self.stored_messages[0]['message'])
self.client.retrieve_message()

self.assertEquals(send_message, self.client.messages[0]['message'])

py_po.message.push_message(app_key, user_key, send_message, device='test_device')
self.client.retrieve_message()

py_po.message.push_message(app_key, user_key, send_message)
#self.assertEquals(send_message, self.stored_messages[1]['message'])
self.assertEquals(send_message, self.client.messages[1]['message'])

#self.client.clear_server_messages()
self.client.clear_server_messages()

# Testing normal push message with Title

val_pm = py_po.message.MessageManager(app_key)
val_pm.push_message('Testing Title and manual user_key', title='Success!', user=user_key)
val_pm.push_message('Testing Title and manual user_key', title='Success!', user=user_key, device_id='test_device')


self.pm.push_message("Valid message with 'device' param", device='test_device')

self.pm.push_message("Valid message with 'url', and 'url_title' params",
url="https://pushover.net/api#urls",
url_title="Pushover Api URLS"
url_title="Pushover Api URLS",
device='test_device'
)

self.pm.push_message("Valid message with 'timestamp' param", timestamp=datetime.datetime.now())
self.pm.push_message("Valid message with 'timestamp' param", timestamp=datetime.datetime.now(), device='test_device')

self.pm.push_message("Valid message with 'sound' param", sound=py_po.SOUNDS.SHORT_BIKE)
self.pm.push_message("Valid message with 'sound' param", sound=py_po.SOUNDS.SHORT_BIKE, device='test_device')

def test_inv_msg(self):
inv_pm = py_po.message.MessageManager(app_key)
Expand Down