From 23fc397384f664710aa8867a9c680e184368626a Mon Sep 17 00:00:00 2001 From: saces Date: Mon, 9 Mar 2026 17:27:43 +0100 Subject: [PATCH] refactor pygomx-module -> pygomx --- Containerfile.debian | 44 +++---- README.txt | 6 +- {pygomx-module => pygomx}/.gitignore | 0 {pygomx-module => pygomx}/MANIFEST.in | 0 {pygomx-module => pygomx}/README.txt | 4 +- {pygomx-module => pygomx}/build_ffi.py | 0 {pygomx-module => pygomx}/pyproject.toml | 5 +- {pygomx-module => pygomx}/setup.py | 2 +- pygomx/src/pygomx/__init__.py | 0 pygomx/src/pygomx/errors.py | 13 +++ pygomx/src/pygomx/pygomx.py | 141 +++++++++++++++++++++++ 11 files changed, 181 insertions(+), 34 deletions(-) rename {pygomx-module => pygomx}/.gitignore (100%) rename {pygomx-module => pygomx}/MANIFEST.in (100%) rename {pygomx-module => pygomx}/README.txt (84%) rename {pygomx-module => pygomx}/build_ffi.py (100%) rename {pygomx-module => pygomx}/pyproject.toml (85%) rename {pygomx-module => pygomx}/setup.py (96%) create mode 100644 pygomx/src/pygomx/__init__.py create mode 100644 pygomx/src/pygomx/errors.py create mode 100644 pygomx/src/pygomx/pygomx.py diff --git a/Containerfile.debian b/Containerfile.debian index 07a9c7a..12e3b7e 100644 --- a/Containerfile.debian +++ b/Containerfile.debian @@ -4,40 +4,32 @@ ARG GOLANG_VERSION=1.24 ARG DEBIAN_VERSION=trixie ARG PYTHON_VERSION=3.14 -FROM docker.io/library/golang:${GOLANG_VERSION}-${DEBIAN_VERSION} AS gobuilder - -RUN apt update -RUN apt -y install libolm-dev - -RUN --mount=type=bind,source=./libmxclient/go.mod,target=/libmxclient/build/go.mod --mount=type=bind,source=./libmxclient/go.sum,target=/libmxclient/build/go.sum <=80", "cffi>=2.0.0"] build-backend = "setuptools.build_meta" [project] -name = "pygomx-module" +name = "pygomx" version = "0.0.1" requires-python = ">=3.10" description = "python pindings for a golang matrix library" @@ -15,3 +15,6 @@ dependencies = ["cffi>=2.0.0"] [project.urls] homepage = "https://codeberg.org/saces/pygomx" heimseite = "https://code.c-base.org/saces/pygomx" + +[tool.setuptools.package-dir] +"pygomx" = "src/pygomx" diff --git a/pygomx-module/setup.py b/pygomx/setup.py similarity index 96% rename from pygomx-module/setup.py rename to pygomx/setup.py index 506b9db..d6ec407 100644 --- a/pygomx-module/setup.py +++ b/pygomx/setup.py @@ -51,7 +51,7 @@ class CustomCommand(Command): "-tags", ",".join(go_tags), "-o", - f"../pygomx-module/libmxclient{build_mode_ext}", + f"../pygomx/libmxclient{build_mode_ext}", ".", ] subprocess.call(go_call, cwd="../libmxclient") diff --git a/pygomx/src/pygomx/__init__.py b/pygomx/src/pygomx/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pygomx/src/pygomx/errors.py b/pygomx/src/pygomx/errors.py new file mode 100644 index 0000000..38ca664 --- /dev/null +++ b/pygomx/src/pygomx/errors.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + + +class APIError(Exception): + """Exception raised for api usage errors. + + Attributes: + message -- explanation of the error + """ + + def __init__(self, message): + self.message = message[4:] + super().__init__(self.message) diff --git a/pygomx/src/pygomx/pygomx.py b/pygomx/src/pygomx/pygomx.py new file mode 100644 index 0000000..2c0ebc7 --- /dev/null +++ b/pygomx/src/pygomx/pygomx.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- +import logging +from _pygomx import lib, ffi +import json +from .errors import APIError + +logger = logging.getLogger(__name__) + + +def checkApiError(cstr): + result = ffi.string(cstr).decode("utf-8") + lib.FreeCString(cstr) + + if result.startswith("ERR:"): + raise APIError(result) + + if result == "SUCCESS.": + return + + logger.debug(result) + + result_dict = json.loads(result) + return result_dict + + +class _MXClient: + """ + core binding + """ + + def __init__(self): + super().__init__() + self._createMXClient() + # create a c-handle for self and keep it alive + self._ffi_selfhandle = ffi.new_handle(self) + + r = lib.apiv0_set_on_event_handler( + self.client_id, on_event_callback, self._ffi_selfhandle + ) + result = ffi.string(r) + lib.FreeCString(r) + if result.startswith(b"ERR:"): + raise APIError(result) + + r = lib.apiv0_set_on_message_handler( + self.client_id, on_message_callback, self._ffi_selfhandle + ) + result = ffi.string(r) + lib.FreeCString(r) + if result.startswith(b"ERR:"): + raise APIError(result) + + r = lib.apiv0_set_on_sys_handler( + self.client_id, on_sys_callback, self._ffi_selfhandle + ) + result = ffi.string(r) + lib.FreeCString(r) + if result.startswith(b"ERR:"): + raise APIError(result) + + def _createMXClient(self): + r = lib.apiv0_createclient_pass(b".mxpass", b".", b"*", b"*", b"*") + + result = ffi.string(r) + lib.FreeCString(r) + if result.startswith(b"ERR:"): + raise APIError(result) + + result_dict = json.loads(result) + self.client_id = result_dict["id"] + self.UserID = result_dict["userid"] + self.DeviceID = result_dict["deviceid"] + + def _sync(self): + r = lib.apiv0_startclient(self.client_id) + checkApiError(r) + + def _stopsync(self): + r = lib.apiv0_stopclient(self.client_id) + checkApiError(r) + + def _sendmessage(self, data_dict): + data = json.dumps(data_dict).encode(encoding="utf-8") + r = lib.apiv0_sendmessage(self.client_id, data) + result = checkApiError(r) + return result + + def leaveroom(self, roomid): + r = lib.apiv0_leaveroom(self.client_id, roomid.encode(encoding="utf-8")) + checkApiError(r) + + def joinedrooms(self): + r = lib.apiv0_joinedrooms(self.client_id) + return checkApiError(r) + + def _createroom(self, data_dict): + data = json.dumps(data_dict).encode(encoding="utf-8") + r = lib.apiv0_createroom(self.client_id, data) + return checkApiError(r) + + def process_event(self, evt): + if hasattr(self, "on_event") and callable(self.on_event): + self.on_event(evt) + else: + logger.warn(f"got event but on_event not declared: {evt}") + + def process_message(self, msg): + if hasattr(self, "on_message") and callable(self.on_message): + self.on_message(msg) + else: + logger.warn(f"got message but on_message not declared: {msg}") + + def process_sys(self, ntf): + if hasattr(self, "on_sys") and callable(self.on_sys): + self.on_sys(ntf) + else: + logger.warn(f"got systen notification but on_sys not declared: {ntf}") + + +@ffi.callback("void(char*, void*)") +def on_event_callback(evt, pobj): + cli = ffi.from_handle(pobj) + e = ffi.string(evt).decode("utf-8") + evt = json.loads(e) + cli.process_event(evt) + + +@ffi.callback("void(char*, void*)") +def on_message_callback(msg, pobj): + cli = ffi.from_handle(pobj) + m = ffi.string(msg).decode("utf-8") + msg = json.loads(m) + cli.process_message(msg) + + +@ffi.callback("void(char*, void*)") +def on_sys_callback(msg, pobj): + cli = ffi.from_handle(pobj) + m = ffi.string(msg).decode("utf-8") + sys = json.loads(m) + cli.process_sys(sys)