From 4c9ba7dcdb8d52fb0aa66902b0589490ea1ae810 Mon Sep 17 00:00:00 2001 From: saces Date: Mon, 13 Apr 2026 22:08:04 +0200 Subject: [PATCH] pygomx: dm support --- libmxclient/mxclient/client.go | 42 ++++++++++++++++++++++++++++- libmxclient/mxclientlib.go | 49 +++++++++++++++++++++++++++++++++- pygomx/build_ffi.py | 2 ++ pygomx/libmxclient.def | 2 ++ pygomx/src/pygomx/apiv0.py | 10 ++++++- pygomx/src/pygomx/client.py | 7 +++++ pygomx/src/pygomx/util.py | 12 +++++++++ 7 files changed, 121 insertions(+), 3 deletions(-) diff --git a/libmxclient/mxclient/client.go b/libmxclient/mxclient/client.go index 39cda36..25ad3e9 100644 --- a/libmxclient/mxclient/client.go +++ b/libmxclient/mxclient/client.go @@ -87,6 +87,19 @@ func (mxc *MXClient) _storeDirectMap() error { return nil } +func (mxc *MXClient) GetUserDM(mxid string) []string { + var res = make([]string, 0) + + for room, uids := range mxc._directMap { + for _, uid := range uids { + if uid.String() == mxid { + res = append(res, room.String()) + } + } + } + return res +} + func (mxc *MXClient) _onEventMember(ctx context.Context, evt *event.Event) { if evt.GetStateKey() == mxc.UserID.String() && evt.Content.AsMember().Membership == event.MembershipInvite { if evt.Content.AsMember().IsDirect { @@ -108,8 +121,18 @@ func (mxc *MXClient) _onEventMember(ctx context.Context, evt *event.Event) { Str("inviter", evt.Sender.String()). Msg("Failed to join room after invite") } + } else if evt.Content.AsMember().Membership == event.MembershipJoin { + out, err := json.Marshal(evt) + if err != nil { + log.Error().Err(err). + Str("id", evt.ID.String()). + Str("joiner", evt.Sender.String()). + Msg("Marshalling error") + return + } + mxc.OnEvent(string(out)) } else { - fmt.Printf("\nGot member event: %#v\n", evt) + fmt.Printf("\nGot member event: %s\n%#v\n", evt.GetStateKey(), evt) } } @@ -161,6 +184,23 @@ func (mxc *MXClient) LeaveRoomAndForget(ctx context.Context, room id.RoomID) err return nil } +func (mxc *MXClient) CreateDM(ctx context.Context, uid id.UserID) (resp *mautrix.RespCreateRoom, err error) { + req := mautrix.ReqCreateRoom{ + IsDirect: true, + Preset: "trusted_private_chat", + Invite: []id.UserID{uid}, + } + + resp, err = mxc.CreateRoom(context.Background(), &req) + if err != nil { + return + } + + mxc.AddDirectRoom(uid, resp.RoomID) + err = mxc._storeDirectMap() + return +} + type sendmessage_data struct { RoomId id.RoomID `json:"roomid"` Type event.Type `json:"type"` diff --git a/libmxclient/mxclientlib.go b/libmxclient/mxclientlib.go index 7d477ff..c80d31a 100644 --- a/libmxclient/mxclientlib.go +++ b/libmxclient/mxclientlib.go @@ -435,7 +435,54 @@ func apiv0_createroom(cid C.int, data *C.char) *C.char { return C.CString(fmt.Sprintf("ERR: %v", err)) } - out, err := json.Marshal(resp) + type roomResult struct { + RoomId id.RoomID `json:"roomid"` + IsDirect bool `json:"is_direct"` + } + + out, err := json.Marshal(roomResult{RoomId: resp.RoomID, IsDirect: false}) + if err != nil { + return C.CString(fmt.Sprintf("ERR: %v", err)) + } + s := string(out) + return C.CString(s) +} + +//export apiv0_createdm +func apiv0_createdm(cid C.int, uid *C.char) *C.char { + cli, err := getClient(int(cid)) + if err != nil { + return C.CString(fmt.Sprintf("ERR: %v", err)) + } + + mxid := C.GoString(uid) + + resp, err := cli.CreateDM(context.Background(), id.UserID(mxid)) + if err != nil { + return C.CString(fmt.Sprintf("ERR: %v", err)) + } + + type roomResult struct { + RoomId id.RoomID `json:"roomid"` + IsDirect bool `json:"is_direct"` + } + + out, err := json.Marshal(roomResult{RoomId: resp.RoomID, IsDirect: true}) + if err != nil { + return C.CString(fmt.Sprintf("ERR: %v", err)) + } + s := string(out) + return C.CString(s) +} + +//export apiv0_getuserdm +func apiv0_getuserdm(cid C.int, userid *C.char) *C.char { + cli, err := getClient(int(cid)) + if err != nil { + return C.CString(fmt.Sprintf("ERR: %v", err)) + } + list := cli.GetUserDM(C.GoString(userid)) + out, err := json.Marshal(list) if err != nil { return C.CString(fmt.Sprintf("ERR: %v", err)) } diff --git a/pygomx/build_ffi.py b/pygomx/build_ffi.py index 74dd227..2adea06 100644 --- a/pygomx/build_ffi.py +++ b/pygomx/build_ffi.py @@ -63,7 +63,9 @@ ffibuilder.cdef( extern char* apiv0_leaveroom(int cid, char* roomid); extern char* apiv0_joinedrooms(int cid); extern char* apiv0_createroom(int cid, char* data); + extern char* apiv0_createdm(int cid, char* uid); extern char* apiv0_genericrequest(int cid, char* method, char* path, char* data); + extern char* apiv0_getuserdm(int cid, char* userid); extern int apiv0_removeclient(int cid); extern char* apiv0_listclients(); extern char* apiv0_getoptions(int cid); diff --git a/pygomx/libmxclient.def b/pygomx/libmxclient.def index 9abb032..b47f4f1 100644 --- a/pygomx/libmxclient.def +++ b/pygomx/libmxclient.def @@ -3,10 +3,12 @@ FreeCString apiv0_createclient apiv0_createclient_pass apiv0_createroom +apiv0_createdm apiv0_deinitialize apiv0_discover apiv0_genericrequest apiv0_getoptions +apiv0_getuserdm apiv0_initialize apiv0_joinedrooms apiv0_leaveroom diff --git a/pygomx/src/pygomx/apiv0.py b/pygomx/src/pygomx/apiv0.py index f27f076..578dcab 100644 --- a/pygomx/src/pygomx/apiv0.py +++ b/pygomx/src/pygomx/apiv0.py @@ -4,7 +4,7 @@ import logging from _pygomx import lib -from .util import _stringresult, _autostring, _autodict +from .util import _stringresult, _autostring, _autodict, _autolist from .errors import CheckApiError, CheckApiErrorOnly, CheckApiResult logger = logging.getLogger(__name__) @@ -57,6 +57,14 @@ class ApiV0Api: ) ) + @staticmethod + def createdm(cid, uid): + return _stringresult(lib.apiv0_createdm(cid, _autostring(uid))) + + @staticmethod + def getuserdm(cid, userid): + return _stringresult(lib.apiv0_getuserdm(cid, _autostring(userid))) + class ApiV0: """ApiV0""" diff --git a/pygomx/src/pygomx/client.py b/pygomx/src/pygomx/client.py index ef45f93..a5bd00f 100644 --- a/pygomx/src/pygomx/client.py +++ b/pygomx/src/pygomx/client.py @@ -96,6 +96,13 @@ class _AsyncClient: r = ApiV0Api.generic(self.client_id, method, path, data) return CheckApiErrorOnly(r) + async def createdm(self, uid): + r = ApiV0Api.createdm(self.client_id, uid) + return CheckApiResult(r) + + async def getuserdm(self, userid): + r = ApiV0Api.getuserdm(self.client_id, userid) + return CheckApiResult(r) def process_event(self, evt): if hasattr(self, "on_event") and callable(self.on_event): diff --git a/pygomx/src/pygomx/util.py b/pygomx/src/pygomx/util.py index 92369fd..5f33e91 100644 --- a/pygomx/src/pygomx/util.py +++ b/pygomx/src/pygomx/util.py @@ -30,3 +30,15 @@ def _autodict(xdict): return json.dumps(xdict).encode(encoding="utf-8") case _: raise TypeError("only str or bytes or dict allowed") + + +def _autolist(xlist): + match xlist: + case bytes(): + return xlist + case str(): + return xlist.encode(encoding="utf-8") + case list(): + return json.dumps(xlist).encode(encoding="utf-8") + case _: + raise TypeError("only str or bytes or list allowed")