going-flying.com gemini git repository
c1f88f146e3384d471d41000051bf68842a3eb9b - Matthew Ernisse - 1649440419
use my own gemini client now
diff --git a/build/build.py b/build/build.py index e4baa6e..8f80025 100755 --- a/build/build.py +++ b/build/build.py @@ -1,7 +1,34 @@ #!/usr/bin/env python3 - +''' build.py (c) 2020-2022 Matthew J. Ernisse <matt@going-flying.com> +All Rights Reserved. + +Redistribution and use in source and binary forms, +with or without modification, are permitted provided +that the following conditions are met: + + * Redistributions of source code must retain the + above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce + the above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +''' import datetime import feedparser +import gemini import jinja2 import os import pytz @@ -11,9 +38,6 @@ import sys from feedgen.feed import FeedGenerator from urllib.parse import quote_plus as quote -sys.path.append('/var/gemini/capcom') -import gusmobile - URLBASE = 'gemini://going-flying.com/~mernisse/' @@ -261,11 +285,12 @@ if __name__ == '__main__': if not feed.should_notify(): sys.exit() - myurl = quote('gemini://going-flying.com/~mernisse/atom.xml') + print('Trying to notify Antenna') - # gusmobile has a bug where it needs the port in the URL I guess. - url = 'gemini://warmedal.se:1965/~antenna/submit' - # resp = gusmobile.fetch(f'{url}?{myurl}') - # if not resp.status == 20: - # print('Failed to notify Antenna') - print(f'Would call {url}?{myurl}') + try: + resp = gemini.fetch( + 'gemini://warmedal.se/~antenna/submit' + query='gemini://going-flying.com/~mernisse/atom.xml' + ) + except Exception as e: + print(f'Failed to notify Antenna {e!s}') diff --git a/build/gemini.py b/build/gemini.py new file mode 100644 index 0000000..3993861 --- /dev/null +++ b/build/gemini.py @@ -0,0 +1,158 @@ +''' gemini.py (c) 2022 Matthew J. Ernisse <matt@going-flying.com> +All Rights Reserved. + +Redistribution and use in source and binary forms, +with or without modification, are permitted provided +that the following conditions are met: + + * Redistributions of source code must retain the + above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce + the above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +'''import certifi +import select +import socket +import ssl +import urllib.parse as urlparse + + +CHUNK_SIZE = 1024 +GEMINI_TIMEOUT = 15.0 + +class GEMINIError(Exception): + ''' Exception raised when a server returns an error condition, + either temporary or permanent. The code is stored as the + code property. + ''' + def __init__(self, message, code=None): + super().__init__(message) + self.code = code + + +class Response(object): + ''' Hold a Gemini response. ''' + def __init__(self, url): + self.url = url + + def set_data(self, data): + self.content_type = 'text/gemini' + self.data = data + self.text = data.decode('utf-8') + + self.meta = self.text.split('\r\n', 1)[0] + self.code = int(self.meta[0:2]) + + self.meta = self.meta.split(None, 1)[1] + + if self.code == 20: + self.content_type = self.meta + + elif self.code >= 40 and self.code < 60: + raise GEMINIError(self.meta, self.code) + + elif self.code >= 60: + raise Exception('Not Implemented.') + + def is_redirect(self): + if self.code >= 30 and self.code < 40: + return True + + return False + + def is_ok(self): + if self.code > 30: + return False + + return True + + +def request(url, **kwargs): + ''' Perform a Gemini request. The following + keyword arguments are supported: + + query - Append as a query parameter. Will be + url encoded prior to sending. Defaults + to nothing. + + verify - Verify the TLS certificate chain presented. + This is often not possible with Gemini's use + of self signed certificates (unfortunatly) + so it defaults to False. + ''' + + query = kwargs.get('query', None) + verify = kwargs.get('verify', False) + + parts = urlparse.urlparse(url) + if not parts: + raise ValueError(f'{url} is not a valid url.') + + if not parts.scheme == 'gemini': + raise ValueError(f'{url} not a gemini url') + + + host = parts.netloc + port = 1965 + + if ':' in host: + (host, port) = host.split(':') + + ctx = ssl.create_default_context() + ctx.minimum_version = ssl.TLSVersion.TLSv1_2 + + if not verify: + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + else: + ctx.load_verify_locations(certifi.where()) + + request = url + if query: + request += f'?{urlparse.quote(query)}' + + request += '\r\n' + request = request.encode('utf-8') + if len(request) > 1024: + raise ValueError('Encoded request is over 1024 bytes') + + response = Response(url) + response.request = request + + socket.setdefaulttimeout(GEMINI_TIMEOUT) + + with socket.create_connection((host, port)) as sock: + with ctx.wrap_socket(sock, server_hostname=host) as tlssock: + + response.tls_cipher = tlssock.cipher() + response.tls_version = tlssock.version() + # This will be nothing if verify is false. + response.tls_certificate = tlssock.getpeercert() + + tlssock.sendall(request) + + data = tlssock.recv(CHUNK_SIZE) + while True: + chunk = tlssock.recv(CHUNK_SIZE) + data += chunk + + if len(chunk) < CHUNK_SIZE: + break + + response.set_data(data) + + return response diff --git a/files/thoughts-to-gemini.py b/files/thoughts-to-gemini.py index 7015a21..a6b919a 100755 --- a/files/thoughts-to-gemini.py +++ b/files/thoughts-to-gemini.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: UTF-8 -*- -'''thoughts-to-gemini.py (c) 2020-2021 Matthew J Ernisse <matt@going-flying.com> +'''thoughts-to-gemini.py (c) 2020-2022 Matthew J Ernisse <matt@going-flying.com> All Rights Reserved. Redistribution and use in source and binary forms, @@ -77,7 +77,7 @@ index_template = '''``` # 🕰️ Generated at {{ build_time }} -🚀 © MMXX-MMXXI matt@going-flying.com +🚀 © MMXX-MMXXII matt@going-flying.com ''' URLBASE = 'gemini://going-flying.com/thoughts/' diff --git a/index.gmi b/index.gmi index bd88ca5..38c8ad7 100644 --- a/index.gmi +++ b/index.gmi @@ -64,4 +64,4 @@ And of course, we wouldn't be here without the project itself. => gemini://gemini.circumlunar.space Obligatory Gemini Information Link -🚀 © MMXX-MMXXI matt@going-flying.com +🚀 © MMXX-MMXXII matt@going-flying.com