LHS Push Notification service is a module for sending push notifications (currently only to iOS) using Python. There are plans to extend this to allow push notifications to be sent to other platforms with the same system.
LHSPNservice is written in Python with the following extra libraries:
- MySQLdb
Deviced are stored in a MySQL database, the structure of which is described below.
After creating a MySQL database, use the following to create the table structure:
CREATE TABLE `LHSPNservice_devices` (
`token` varchar(64) NOT NULL,
`badge` int(11) unsigned NOT NULL,
`OSVersion` varchar(10) NOT NULL DEFAULT '',
`isDev` tinyint(1) NOT NULL,
`userInfo` varchar(1024) NOT NULL DEFAULT '{}',
`updatedAt` int(32) NOT NULL,
`createdAt` int(32) NOT NULL,
PRIMARY KEY (`token`),
UNIQUE KEY `token` (`token`),
KEY `token_2` (`token`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
NOTE: To work with and use push notifications with Apple devices you will need a Apple developer licence for ether iOS or OS X. Go to: https://developer.apple.com/programs/ for more information.
The module PushNotificationDeviceHandler.py
deals with connecting to a MySQL database and all the information to be stored on a device.
The structure of this database is talked about above. ^
For information on creating APNs certificates please refer to the Apple documents.
To create and send notifications to a device or series of devices please use the following steps:
-
Call
makeAlert(body, actionLocKey, locKey, locArgs, launchImage):
to create a alert dictionary. -
Then create an instance of
APNservice
class. -
Queue the notifications with
APNservice.queueNotifications(tokens, alert, sound):
. If you just want to send the alert to one device then just maketokensArray
contain one device token. -
LHS will send your notification to all tokens.
tokensArray = ["19e5d3a4a27eb08e9b2d22166152a5492fd645868f1e6909e80ba99256c8590f", "27788ada507d3ea5e0f7a01b2305f7ffe9a116b03a22677dfe765771fdc28148"]
alertDict = makeAlert("This is a test Push Notification!", None, None, None, None)
pushService = APNservice()
pushService.queueNotifications(tokensArray, alertDict, None)
LHS uses a queuing system to send push notifications. This is because Apple does not return a response when a notification has been sent, and also because a returned error can be received after more notifications have already been sent. The following outlines the process used for queuing and sending notifications:
-
All notifications are created and packed into binary form. As they are created they are assigned an ID incrementing from
0
. -
Each binary is stored in the
notifBinaryDict
dictionary with its ID being the key. -
The
queueIDsList
list is then populated with the keys ofnotifBinaryDict
, opens a socket and begins to send the notifications after starting an asynchronous thread reading from the socket. -
After a notification has been sent its ID gets put in the
sentIDsList
list. -
If an error is read from the socket, sending is stopped. Every ID in the
sentIDsList
that was sent after the failed notification gets put back at the front of thequeueIDsList
list to send again. -
The sending is then restarted, opening a new socket.
-
When all notifications are sent the socket is closed, the ID count reset to
0
and the reading thread is stopped.
The failed notification is logged with its ID and error status. If it is due to error status 1 or 10 then it tries to resend. If a notification reaches its retry count then it is logged and removed from the queue. For more information of error status codes check the Apple documents.
For more information on this method of sending push notifications please read this blog about it: The Problem With Apples Push Notification Service... Solutions and Workarounds...
Apple recommends checking the feedback service to remove any device tokens from your database that are no longer active. To read more about this check out the Apple documents.
To do this with LHS, create an instance of APNservice and call the APNservice.checkFeedbackService():
method, which returns an array of tuples containing:
[0] Unix timestamp
[1] Token length
[2] Token
These are device tokens that are no longer valid along with a timestamp of when that token was invalidated. You should remove these from your data of tokens since these devices have uninstalled your app and can no longer receive notifications.
NOTE: It is recomended to check the timestamp from when the token was invalidated to the last updated timestamp in your database, incase the user has re-installed the app between the invalidation and your check.
pushService = APNservice()
invalidTokens = pushService.checkFeedbackService()
print invalidTokens
>> [(1373042568, 32, '19e5d3a4a27eb08e9b2d22166152a5492fd645868f1e6909e80ba99256c8590f'), (1373042568, 32, '27788ada507d3ea5e0f7a01b2305f7ffe9a116b03a22677dfe765771fdc28148')]
Returns a dictionary of the alert information. For a simple message just set body
as a string for your alert and send None
to all the other variables. For more information of the type of data and localisation protocols for Apple push notifications, please refer to the Apple documents.
Creates an instance of APNservice
to use to queue notifications and connect to the feedback service.
Creates, queues and sends the notifications it is passed. Set the alert using makeAlert()
and sound
as a string of the sound file name to play (if you want to use the default sound then pass None
).
This method returns an array of tuples. The tuples hold a timestamp, token length and token. If there were no invalid tokens then this method returns an empty array.
Connects to the MySQL database. Remember to always close a database connection when you are finished with it with db.close()
.
Get a device details as a dictionary from the database. Requres a database variable to be ba passed to db
. (Use db = connectToDatabase():
to setup the database connection and db.close()
to close it.) Also pass token of the device you want to get returned to the token
var.
This function will search the database userInfo
field for any of the key-value pairs in the searchDict
.
For example, if searchDict
contains {"aKey":"someValue", "anoutherKey":"someotherValue"}
then it will search the database for any entry that contrains, "aKey":"someValue"
OR "anoutherKey":"someotherValue"
in the userInfo
fields JSON string.
For this method but with an AND statement to look for all the key-value pairs, look at getDevicesWithAndDict(db, searchDict, sendToDev):
.
Requres a database variable to be ba passed to db
. (Use db = connectToDatabase():
to setup the database connection and db.close()
to close it.)
This function will search the database userInfo
field for all the key-value pairs in the searchDict
.
For example, if searchDict
contains {"aKey":"someValue", "anoutherKey":"someotherValue"}
then it will search the database for any entry that contrains "aKey":"someValue"
AND "anoutherKey":"someotherValue"
in the userInfo
fields JSON string.
For this method but with an OR statement to look for any of the key-value pairs look at getDevicesWithOrDict(db, searchDict, sendToDev):
.
Requres a database variable to be ba passed to db
. (Use db = connectToDatabase():
to setup the database connection and db.close()
to close it.)
Saves a new device in a database with token
as the key. Requres a database variable to be ba passed to db
. (Use db = connectToDatabase():
to setup the database connection and db.close()
to close it.) userInfo
is a JSON dictionary string to hold extra data. OSVersion
is to define the platform the device is running (E.g. iOS, Android, etc).
Updated a device that already exists in the database. Requres a database variable to be ba passed to db
. (Use db = connectToDatabase():
to setup the database connection and db.close()
to close it.)
Delete a device with the key passed in the token
variable. Requres a database variable to be ba passed to db
. (Use db = connectToDatabase():
to setup the database connection and db.close()
to close it.)
Increments the badge count for a device with the token
variable as the key. This function creates it own database connection.
Resets the badge count beck to 0 for a device with the token
variable as the key. This function creates and closes it own database connection.
This function deletes a device from the database from a tuple recived from the APNservice feedback request. The device will only be deleted if it was not updated after the feedback response was recived. This function creates and closes it own database connection.
This is a PHP file that uses REST to save, update or delete a device from the MySQL database.
To save a new device or update a current one use the POST call and include the following paramaters:
token
This must be included as it is the database key,userInfo
A JSON dictionary,OSVersion
Defines the OS the device is running,isDev
ABOOL
defining if the device is returning a development token or distribution.
HTTP Request Method: POST
YourURL/saveToken.php?token=19e5d3a4a27eb08e9b2d22166152a5492fd645868f1e6909e80ba99256c8590f&userInfo={}&OSVersion=iOS&isDev=0
To delete a device use the DELETE call and set the paramater as the token of the device to delete.
HTTP Request Method: DELETE
YourURL/saveToken.php?19e5d3a4a27eb08e9b2d22166152a5492fd645868f1e6909e80ba99256c8590f
LHSPNservice is available under the MIT license. See the LICENSE file for more info.