|
1 | | -"""Copyright (c) 2021 The gitlab.com/mohan43u/weenotifier Authors. |
| 1 | +""" |
| 2 | +Copyright (c) 2021-present Mohan Raman <[email protected]>. |
2 | 3 |
|
3 | 4 | All rights reserved. |
4 | 5 |
|
|
25 | 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 | 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 28 |
|
28 | | -20210-07-01: Mohan R |
29 | | - 0.0.1: Initial release |
| 29 | +2021-07-01: Mohan R |
| 30 | + 0.0.1: Initial release with IrssiNotifier |
| 31 | + (https://irssinotifier.appspot.com/) |
| 32 | +
|
| 33 | +2024-10-06: Mohan R |
| 34 | + 0.1.0: switched to gotify (https://gotify.net/) |
| 35 | + it should also support ntfy.sh (https://ntfy.sh) |
30 | 36 |
|
31 | | -Description: notifier using IrssiNotifier (https://irssinotifier.appspot.com/) |
| 37 | +Description: notifier for push notification |
32 | 38 | Project URL: https://gitlab.com/mohan43u/weenotifier |
33 | 39 | """ |
34 | 40 |
|
35 | | -import os |
36 | | -import base64 |
37 | 41 | from urllib.parse import urlencode |
38 | 42 |
|
39 | 43 | try: |
40 | 44 | import weechat |
41 | | - weechat.register('weenotifier', |
42 | | - 'Mohan R', |
43 | | - '0.0.1', |
44 | | - 'BSD 2-Clause Simplified', |
45 | | - 'notifier using \ |
46 | | - IrssiNotifier (https://irssinotifier.appspot.com/)', |
47 | | - 'shutdown_cb', |
48 | | - '') |
49 | | -except ImportError as exception: |
50 | | - raise ImportError('This script has to run under \ |
51 | | - WeeChat (https://weechat.org/)') from exception |
52 | | - |
53 | | - |
54 | | -try: |
55 | | - from cryptography.hazmat.primitives import hashes, ciphers, padding |
| 45 | + result = weechat.register("weenotifier", |
| 46 | + "Mohan R", |
| 47 | + "0.1.0", |
| 48 | + "BSD 2-Clause Simplified", |
| 49 | + "notifier for push notification", |
| 50 | + "shutdown_cb", |
| 51 | + "") |
56 | 52 | except ImportError as exception: |
57 | | - raise ImportError('failed to import cryptography module \ |
58 | | - (https://cryptography.io)') from exception |
59 | | - |
60 | | - |
61 | | -weenotifier = None |
| 53 | + raise ImportError("This script has to run under" + |
| 54 | + "WeeChat (https://weechat.org/)") from exception |
62 | 55 |
|
63 | 56 |
|
64 | 57 | class WeeNotifier: |
65 | | - """Weenotifier - primary class.""" |
| 58 | + """ |
| 59 | + Weenotifier - primary class. |
| 60 | + """ |
66 | 61 |
|
67 | 62 | def __init__(self): |
68 | | - """Weenotifier - initialization.""" |
| 63 | + """ |
| 64 | + Weenotifier - initialization. |
| 65 | + """ |
| 66 | + |
69 | 67 | self.options = { |
70 | | - 'url': 'https://irssinotifier.appspot.com/API/Message', |
71 | | - 'token': '', |
72 | | - 'password': 'password' |
| 68 | + "url": "", # gotify: https://selfhostedgotify.yrdomain.tld/message |
| 69 | + # ntfy.sh: https://ntfy.sh/youruniquetopic |
| 70 | + "token": "" |
73 | 71 | } |
74 | 72 |
|
75 | 73 | for option, value in self.options.items(): |
76 | 74 | if not weechat.config_is_set_plugin(option): |
77 | | - weechat.config_set_plugin(option, value) |
78 | | - |
79 | | - self.url = weechat.config_get_plugin('url') |
80 | | - if self.url is None or len(self.url) <= 0: |
81 | | - raise NameError('weenotifier: url not configured') |
82 | | - |
83 | | - self.token = weechat.config_get_plugin('token') |
84 | | - if self.token is None or len(self.token) <= 0: |
85 | | - raise NameError('weenotifier: token not configured') |
86 | | - |
87 | | - self.password = weechat.config_get_plugin('password') |
88 | | - if self.password is None or len(self.password) <= 0: |
89 | | - raise NameError('weenotifier: password not configured') |
90 | | - |
91 | | - self.version = weechat.info_get('version_number', '') or '0' |
92 | | - weechat.hook_print('', '', '', 1, 'message_cb', '') |
93 | | - |
94 | | - def encrypt(self, password, data): |
95 | | - """Encrypt given data using md5 + salt + aes-128-cbc.""" |
96 | | - # IrssiNotifier requires null at the end |
97 | | - databytes = data.encode() + bytes(1) |
98 | | - padder = padding.PKCS7(128).padder() |
99 | | - padded_data = padder.update(databytes) + padder.finalize() |
100 | | - salt = os.urandom(8) |
101 | | - key = None |
102 | | - iv = None |
103 | | - |
104 | | - for n in range(2): |
105 | | - # this following logic is similar to EVP_BytesToKey() from openssl |
106 | | - md5hash = hashes.Hash(hashes.MD5()) |
107 | | - if key is not None: |
108 | | - md5hash.update(key) |
109 | | - md5hash.update(password.encode()) |
110 | | - md5hash.update(salt) |
111 | | - md5 = md5hash.finalize() |
112 | | - if key is None: |
113 | | - key = md5 |
114 | | - continue |
115 | | - if iv is None: |
116 | | - iv = md5 |
117 | | - |
118 | | - aes256cbc = ciphers.Cipher(ciphers.algorithms.AES(key), |
119 | | - ciphers.modes.CBC(iv)) |
120 | | - encryptor = aes256cbc.encryptor() |
121 | | - edata = encryptor.update(padded_data) + encryptor.finalize() |
122 | | - edata = 'Salted__'.encode() + salt + edata |
123 | | - edata = base64.b64encode(edata, b'-_') |
124 | | - edata = edata.replace(b'=', b'') |
125 | | - return edata |
126 | | - |
127 | | - def message(self, data, buffer, date, tags, isdisplayed, ishighlight, |
128 | | - prefix, message): |
129 | | - """Send message to IrssiNotifier.""" |
130 | | - if int(ishighlight): |
131 | | - channel = weechat.buffer_get_string(buffer, 'sort_name') or \ |
132 | | - weechat.buffer_get_string(buffer, 'name') |
133 | | - post = urlencode({'apiToken': self.token, |
134 | | - 'channel': self.encrypt(self.password, channel), |
135 | | - 'nick': self.encrypt(self.password, prefix), |
136 | | - 'message': self.encrypt(self.password, message), |
137 | | - 'version': self.version}) |
138 | | - weechat.hook_process_hashtable('url:' + self.url, |
139 | | - {'postfields': post}, 10000, '', '') |
| 75 | + _ = weechat.config_set_plugin(option, |
| 76 | + value) |
| 77 | + |
| 78 | + self.url = weechat.config_get_plugin("url") |
| 79 | + if len(self.url) <= 0: |
| 80 | + raise NameError("weenotifier: 'url' not configured") |
| 81 | + |
| 82 | + # for gotify |
| 83 | + token = weechat.config_get_plugin("token") |
| 84 | + if len(token) > 0: |
| 85 | + self.url += "?token=" + token |
| 86 | + |
| 87 | + self.version = weechat.info_get("version_number", |
| 88 | + "") or "0" |
| 89 | + _ = weechat.hook_print("", |
| 90 | + "", |
| 91 | + "", |
| 92 | + 1, |
| 93 | + "message_cb", |
| 94 | + "") |
| 95 | + |
| 96 | + def message(self, |
| 97 | + _data: str, |
| 98 | + buffer: str, |
| 99 | + _date: int, |
| 100 | + tags: list[str], |
| 101 | + _displayed: int, |
| 102 | + highlight: int, |
| 103 | + prefix: str, |
| 104 | + message: str): |
| 105 | + """ |
| 106 | + Send message to push notification server. |
| 107 | + """ |
| 108 | + |
| 109 | + if highlight or ("notify_private" in tags): |
| 110 | + channel = weechat.buffer_get_string(buffer, "short_name") or \ |
| 111 | + weechat.buffer_get_string(buffer, "name") |
| 112 | + message = prefix + ": " + message |
| 113 | + post = channel + ": " + message |
| 114 | + |
| 115 | + # format for gotify |
| 116 | + if "?token=" in self.url: |
| 117 | + post = urlencode({"title": channel, |
| 118 | + "message": message, |
| 119 | + "priority": "4"}) |
| 120 | + |
| 121 | + _ = weechat.hook_process_hashtable("url:" + self.url, |
| 122 | + {"post": "1", |
| 123 | + "postfields": post}, |
| 124 | + 10000, |
| 125 | + "result_cb", |
| 126 | + "") |
| 127 | + return weechat.WEECHAT_RC_OK |
| 128 | + |
| 129 | + def result(self, |
| 130 | + data: str, |
| 131 | + url: str, |
| 132 | + returncode: int, |
| 133 | + output: str, |
| 134 | + err: str): |
| 135 | + """ |
| 136 | + Print result of url request |
| 137 | + """ |
| 138 | + |
| 139 | + if returncode != 0 or ("error" in output) or len(err) > 0: |
| 140 | + print(url, |
| 141 | + data, |
| 142 | + returncode, |
| 143 | + output, |
| 144 | + err) |
| 145 | + return weechat.WEECHAT_RC_ERROR |
140 | 146 | return weechat.WEECHAT_RC_OK |
141 | 147 |
|
142 | 148 | def shutdown(self): |
143 | | - """Shutdown callback.""" |
| 149 | + """ |
| 150 | + Shutdown |
| 151 | + """ |
| 152 | + |
| 153 | + print("shutdown invoked") |
144 | 154 | return weechat.WEECHAT_RC_OK |
145 | 155 |
|
146 | 156 |
|
147 | | -def message_cb(data, buffer, date, tags, isdisplayed, ishighlight, prefix, |
148 | | - message): |
149 | | - """Message callback Which will be called by weechat-C-api.""" |
| 157 | +weenotifier: WeeNotifier | None = None |
| 158 | + |
| 159 | + |
| 160 | +def message_cb(data: str, |
| 161 | + buffer: str, |
| 162 | + date: int, |
| 163 | + tags: list[str], |
| 164 | + displayed: int, |
| 165 | + highlight: int, |
| 166 | + prefix: str, |
| 167 | + message: str): |
| 168 | + """ |
| 169 | + Message callback by weechat-C-api. |
| 170 | + """ |
| 171 | + |
| 172 | + if weenotifier is not None: |
| 173 | + return weenotifier.message(data, |
| 174 | + buffer, |
| 175 | + date, |
| 176 | + tags, |
| 177 | + displayed, |
| 178 | + highlight, |
| 179 | + prefix, |
| 180 | + message) |
| 181 | + return weechat.WEECHAT_RC_ERROR |
| 182 | + |
| 183 | + |
| 184 | +def result_cb(data: str, |
| 185 | + url: str, |
| 186 | + returncode: int, |
| 187 | + output: str, |
| 188 | + err: str): |
| 189 | + """ |
| 190 | + Result callback by weechat-C-api |
| 191 | + """ |
| 192 | + |
150 | 193 | if weenotifier is not None: |
151 | | - return weenotifier.message(data, buffer, date, tags, isdisplayed, |
152 | | - ishighlight, prefix, message) |
| 194 | + return weenotifier.result(data, |
| 195 | + url, |
| 196 | + returncode, |
| 197 | + output, |
| 198 | + err) |
153 | 199 | return weechat.WEECHAT_RC_ERROR |
154 | 200 |
|
155 | 201 |
|
156 | 202 | def shutdown_cb(): |
157 | | - """Shutdown callback Which will be called by weechat-C-api.""" |
| 203 | + """ |
| 204 | + Shutdown callback by weechat-C-api |
| 205 | + """ |
| 206 | + |
158 | 207 | if weenotifier is not None: |
159 | 208 | return weenotifier.shutdown() |
160 | 209 | return weechat.WEECHAT_RC_ERROR |
161 | 210 |
|
162 | 211 |
|
163 | 212 | def main(): |
164 | | - """Start point.""" |
165 | 213 | global weenotifier |
166 | 214 | weenotifier = WeeNotifier() |
167 | 215 |
|
168 | 216 |
|
169 | | -if __name__ == '__main__': |
| 217 | +if __name__ == "__main__": |
170 | 218 | main() |
0 commit comments