Skip to content

Instantly share code, notes, and snippets.

@dkong

dkong/Help.md Secret

Forked from aaronlevy/push_debug.py
Last active December 22, 2020 22:51
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dkong/c29255ff0d68698d1ec9 to your computer and use it in GitHub Desktop.
Save dkong/c29255ff0d68698d1ec9 to your computer and use it in GitHub Desktop.
APNS push debug script

Usage

usage: push_debug.py [-h] [-s] certificate device_id

Send test APNS notifications to device using cert

positional arguments:
  certificate    Push certificate
  device_id      Device ID

optional arguments:
  -h, --help     show this help message and exit
  -s, --sandbox  Use APNS sandbox environment

The push certificate file is the same file that would be uploaded to PubNub. Read the iOS docs for more instructions.

Examples

Send notification through production environment

python push_debug.py my_certificate.pem c35124fd2676d646423705b0721004e3b8426d163e10dbf76b46347a4477f12b

Send notification through sandbox environment

python push_debug.py -s my_certificate.pem 3f3980225497e1846fb5c2db9e0b3510023402c7a772011106c702d2aec20cc5
import json
import logging
import os
import socket
import ssl
import struct
import sys
import time
import uuid
import argparse
APNS_HOST = 'gateway.push.apple.com'
APNS_HOST_SANDBOX = 'gateway.sandbox.push.apple.com'
APNS_PORT = 2195
APNS_ERRORS = {
1:'Processing error',
2:'Missing device token',
3:'missing topic',
4:'missing payload',
5:'invalid token size',
6:'invalid topic size',
7:'invalid payload size',
8:'invalid token',
255:'Unknown'
}
def push(cert_path, device, sandbox):
if not os.path.exists(cert_path):
logging.error("Invalid certificate path: %s" % cert_path)
sys.exit(1)
device = device.decode('hex')
expiry = time.time() + 3600
try:
sock = ssl.wrap_socket(
socket.socket(socket.AF_INET, socket.SOCK_STREAM),
certfile=cert_path
)
host = APNS_HOST_SANDBOX if sandbox else APNS_HOST
sock.connect((host, APNS_PORT))
sock.settimeout(1)
except Exception as e:
logging.error("Failed to connect: %s" % e)
sys.exit(1)
logging.info("Connected to APNS\n")
for ident in range(1,4):
logging.info("Sending %d of 3 push notifications" % ident)
payload = json.dumps({
'aps': {
'alert': 'Push Test %d: %s' % (ident, str(uuid.uuid4())[:8])
}
})
items = [1, ident, expiry, 32, device, len(payload), payload]
try:
sent = sock.write(struct.pack('!BIIH32sH%ds'%len(payload), *items))
if sent:
logging.info("Message sent\n")
else:
logging.error("Unable to send message\n")
except socket.error as e:
logging.error("Socket write error: %s", e)
# If there was an error sending, we will get a response on socket
try:
response = sock.read(6)
command, status, failed_ident = struct.unpack('!BBI',response[:6])
logging.info("APNS Error: %s\n", APNS_ERRORS.get(status))
sys.exit(1)
except socket.timeout:
pass
except ssl.SSLError:
pass
sock.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Send test APNS notifications to device using cert")
parser.add_argument("certificate", help="Push certificate")
parser.add_argument("device_id", help="Device ID")
parser.add_argument("-s", "--sandbox", action="store_true", help="Use APNS sandbox environment")
args = parser.parse_args()
logging.basicConfig(level=logging.INFO)
push(args.certificate, args.device_id, args.sandbox)
logging.info("Complete\n")
@shahankit
Copy link

gives me error

command, status, failed_ident = struct.unpack('=BBI',response[:6])
struct.error: unpack requires a string argument of length 6

@elcolie
Copy link

elcolie commented May 5, 2019

Please revise to Python3

Traceback (most recent call last):
  File "push_debug.py", line 117, in <module>
    push(args.certificate, args.device_id, args.sandbox)
  File "push_debug.py", line 58, in push
    device = device.decode('hex')
AttributeError: 'str' object has no attribute 'decode'

@elcolie
Copy link

elcolie commented May 5, 2019

Python3 user needs to modify code as follows

import codecs
decode_hex = codecs.getdecoder('hex_codec')
device = decode_hex(device)[0]

items

items = [1, ident, expiry, 32, device, len(payload), payload.encode('utf-8')]

And last one struck.pack change from int to float

'!BIfH32sH%ds'

@robertmryan
Copy link

Do you have a rendition of this script for those of us not using the old APNs certificate key pattern, but rather are using the contemporary auth key pattern?

@dkong
Copy link
Author

dkong commented Dec 22, 2020

@robertmryan Sorry I haven't used this script in a long time. These days I just use https://github.com/onmyway133/PushNotifications to quickly test push notifications:

@robertmryan
Copy link

@dkong I use the same app. Lol.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment