├── LICENSE ├── README.md └── ntfy.py /LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ntfy.py - A simple [nfty.sh](https://github.com/binwiederhier/ntfy/) client written in python 2 | 3 | ntfy.py is a simple nfty.sh client for sending notifications. 4 | 5 | Should support all parameters except file attachments. 6 | 7 | ## Usage 8 | 9 | ```bash 10 | ntfy.py myTopic --message="This message was sent using ntfy.py" --title="Hello World!" --click=https://github.com/ 11 | ``` 12 | 13 | ## Installation 14 | 15 | ```bash 16 | wget https://raw.githubusercontent.com/ioqy/ntfy-client-python/master/ntfy.py 17 | sudo mv ntfy.py /usr/local/bin/ 18 | sudo chmod 755 /usr/local/bin/ntfy.py 19 | ``` -------------------------------------------------------------------------------- /ntfy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import requests 5 | from urllib.parse import urljoin 6 | 7 | parser = argparse.ArgumentParser() 8 | 9 | parser.add_argument("topic", 10 | help="Name of the topic the message gets published to") 11 | parser.add_argument("--server", 12 | dest="server", 13 | required=False, 14 | default="https://ntfy.sh", 15 | help="The URL of the server beginning with the protocol (eg. https://). Default is https://ntfy.sh") 16 | parser.add_argument("--message", "-m", "--x-message", 17 | dest="message", 18 | required=False, 19 | help="Main body of the message as shown in the notification") 20 | parser.add_argument("--title", "-t", "--x-title", 21 | dest="title", 22 | required=False, 23 | help="Message title") 24 | parser.add_argument("--priority", "--prio", "-p", "--x-priority", 25 | dest="priority", 26 | required=False, 27 | help="Message priority") 28 | parser.add_argument("--tags", "--tag", "--ta", "--x-tags", 29 | dest="tags", 30 | required=False, 31 | help="Tags and emojis") 32 | parser.add_argument("--delay", "--x-at", "--at", "--x-in", "--in", "--x-delay", 33 | dest="delay", 34 | required=False, 35 | help="Timestamp or duration for delayed delivery") 36 | parser.add_argument("--actions", "--action", "--x-actions", 37 | dest="actions", 38 | required=False, 39 | help="JSON array or short format of user actions") 40 | parser.add_argument("--click", "--x-click", 41 | dest="click", 42 | required=False, 43 | help="URL to open when notification is clicked") 44 | parser.add_argument("--attach", "-a", "--x-attach", 45 | dest="attach", 46 | required=False, 47 | help="URL to send as an attachment, as an alternative to PUT/POST-ing an attachment") 48 | #parser.add_argument("--filename", "--file", "-f", "--x-filename", 49 | # dest="filename", 50 | # required=False, 51 | # help="Optional attachment filename, as it appears in the client") 52 | parser.add_argument("--email", "--e-mail", "--mail", "-e", "--x-email", "--x-e-mail", 53 | dest="email", 54 | required=False, 55 | help="E-mail address for e-mail notifications") 56 | parser.add_argument("--cache", "--x-cache", 57 | dest="cache", 58 | choices=["no"], 59 | required=False, 60 | help="Allows disabling message caching") 61 | parser.add_argument("--firebase", "--x-firebase", 62 | dest="firebase", 63 | choices=["no"], 64 | required=False, 65 | help="Allows disabling sending to Firebase") 66 | parser.add_argument("--unifiedpush", "--up", "--x-unifiedpush", 67 | dest="unifiedpush", 68 | required=False, 69 | help="UnifiedPush publish option, only to be used by UnifiedPush apps") 70 | parser.add_argument("--poll-id", "--x-poll-id", 71 | dest="poll-id", 72 | required=False, 73 | help="Internal parameter, used for iOS push notifications") 74 | parser.add_argument("--authorization", 75 | dest="authorization", 76 | required=False, 77 | help="If supported by the server, you can login to access protected topics") 78 | 79 | parser.add_argument("--verbose", 80 | required=False, 81 | action="store_true", 82 | help="Output the response from the POST request") 83 | 84 | args = parser.parse_args() 85 | 86 | # store some arguments in sperate variables because they will be removed in the next step 87 | server = args.server 88 | topic = args.topic 89 | message = args.message 90 | verbose = args.verbose 91 | 92 | # remove some parameter from arguments because everything else goes into the HTTP headers 93 | arg_dict = vars(args) 94 | arg_dict.pop("server") 95 | arg_dict.pop("topic") 96 | arg_dict.pop("message") 97 | arg_dict.pop("verbose") 98 | 99 | if (len(server.strip()) == 0 100 | or server.count(" ") > 0): 101 | raise Exception("Server must not be empty and not contain a space") 102 | 103 | if (len(topic.strip()) == 0 104 | or topic.count(" ") > 0): 105 | raise Exception("Topic must not be empty and not contain a space") 106 | 107 | # build new dictionary with headers 108 | headers = {} 109 | for arg in arg_dict: 110 | if arg_dict[arg] != None: 111 | headers[arg] = arg_dict[arg].encode("utf-8") 112 | 113 | if message != None: 114 | message = message.encode("utf-8") 115 | 116 | response = requests.post(urljoin(server, topic), 117 | data=message, 118 | headers=headers) 119 | 120 | if (verbose 121 | or response.ok != True): 122 | print(response.text) 123 | 124 | response.raise_for_status() 125 | --------------------------------------------------------------------------------