diff --git a/README.md b/README.md index b0aa3c7..e9a9e4f 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,8 @@ Exports the following functions: - `http_set_ua(ua_str='Mozilla/5.0')`: set a default HTTP user agent string - `http_fetch(url, useragent=None, hdrs={})`: fetch text content from a URL into a string (returns `(status_code, content)` tuple) - `auth_header(username, password)`: get a dictionary fragment with the corresponding HTTP Basic Auth header according to the username and password +- `url_escape(s)`: escape a string to be used as a part of URL parameters +- `form_encode(params)`: encode a form body dictionary in the `x-www-form-urlencoded` format - `wget(url, filename='', useragent=None, hdrs={})`: download a file into a local path (returns `(status_code, filename)` tuple) - `wput(url, file_path, field_name="file", method="POST", format="x-www-form-urlencoded", headers={}, useragent=None)`: upload a file from a local path in the `x-www-form-urlencoded` or JSON format (returns `(status_code, content)` tuple) @@ -247,6 +249,21 @@ Supported chat commands: - `/modellist`: list model IDs available for current provider - `/exit` or `/quit`: exit the chat applet +### `app.blog` + +A simple interface to any POST-based blog API that supports HTTP Basic Auth. + +Requires the following environment variables to be configured (for T-Deck, use the `/settings.toml` file): + +- `BLOG_POST_ENDPOINT`: the full URL to send POST requests to +- `BLOG_POST_USER`: the username to send the requests as +- `BLOG_POST_PASS`: the password for this username + +Exports the following functions: + +- `blogpost()`: wait for a (multiline) input and send the post, +- `blogpost_str(content)`: post the content in a non-interactive manner. + ## FAQ ### Is this an OS shell? diff --git a/app/blog.py b/app/blog.py new file mode 100644 index 0000000..25828d0 --- /dev/null +++ b/app/blog.py @@ -0,0 +1,38 @@ +# My own microblogging interface that supports any POST endpoint +# with HTTP basic auth +# Usage: +# 1. Configure BLOG_POST_ENDPOINT, BLOG_POST_USER and BLOG_PORT_PASS +# variables in settings.toml +# 2. +# Created by Luxferre in 2025, released into public domain + +import os, sys + +POST_ENDPOINT = os.getenv('BLOG_POST_ENDPOINT', None) +if POST_ENDPOINT is None: + print('Please configure BLOG_POST_ENDPOINT variable in settings.toml') + sys.exit(1) + +POST_USER = os.getenv('BLOG_POST_USER', None) +POST_PASS = os.getenv('BLOG_POST_PASS', None) + +from deck.input import input, input_multi +from deck.http import requests, auth_header, form_encode + +auth_headers = {} +if POST_USER is not None: + auth_headers = auth_header(POST_USER, POST_PASS) + +# post a string +def blogpost_str(content): + payload = form_encode({'content': content}) + response = requests.post(POST_ENDPOINT, data=payload, headers=auth_headers) + return (response.status_code, response.text) + +# expect to post interactively without parameters +def blogpost(content=None): + if content is None: + print('Enter your post, ending with a dot:') + content = input_multi('> ') + print('Sending the post...') + return blogpost_str(content) diff --git a/deck/http.py b/deck/http.py index 45e980c..f10a99d 100644 --- a/deck/http.py +++ b/deck/http.py @@ -36,6 +36,17 @@ def auth_header(auth_username=None, auth_password=None): auth_b64 = binascii.b2a_base64(auth_str.encode("utf-8")).decode("utf-8").strip() return {"Authorization": f"Basic {auth_b64}"} +def url_escape(s): + """Prepare parameter value data for x-www-form-urlencoded format""" + return ''.join(c if c.isalpha() or c.isdigit() else '%%%02x' % ord(c) for c in s) + +def form_encode(params): + """Prepare parameter object for x-www-form-urlencoded format""" + out = [] + for param, val in params.items(): + out.append(url_escape(param) + '=' + url_escape(val)) + return '&'.join(out) + def wget(url, filename='', useragent=None, hdrs={}): """Download a file""" if filename == '': diff --git a/tdeckboot.py b/tdeckboot.py index 2840ad8..6f6e460 100644 --- a/tdeckboot.py +++ b/tdeckboot.py @@ -9,9 +9,11 @@ from deck.input import input, input_multi from deck.pager import print_paged, term_size from deck.time import * from deck.hwinfo import board_id, battery_v, cpu_f, REAL_HW -from deck.http import wget, wput, http_fetch, http_set_ua, auth_header +from deck.http import wget, wput, http_fetch, http_set_ua +from deck.http import auth_header, url_escape, form_encode from deck import xlat from app.ed import edit, view +from app.blog import blogpost, blogpost_str try: from deck import net requests = net.init_requests()