From 61b1d3808d86a12a6d2856513a4b863549a22739 Mon Sep 17 00:00:00 2001 From: Captain Zidgel <13912938+Coolster4000@users.noreply.github.com> Date: Mon, 10 Aug 2020 16:30:06 -0700 Subject: [PATCH] Added ACL querying --- API.md | 15 +++++++++++++++ pymumble_py3/callbacks.py | 29 +++++++++++++++-------------- pymumble_py3/channels.py | 6 +++++- pymumble_py3/constants.py | 8 +++++--- pymumble_py3/messages.py | 9 +++++++++ pymumble_py3/mumble.py | 18 ++++++++++++++---- 6 files changed, 63 insertions(+), 22 deletions(-) diff --git a/API.md b/API.md index 16af56f..90374c5 100644 --- a/API.md +++ b/API.md @@ -72,6 +72,7 @@ Callback names are in `pymumble.constants` module, starting with `PYMUMBLE_CLBK_ - `PYMUMBLE_CLBK_USERREMOVED`: send the removed user object and the mumble message as parameter - `PYMUMBLE_CLBK_SOUNDRECEIVED`: send the user object that received the sound and the SoundChunk object itself - `PYMUMBLE_CLBK_TEXTMESSAGERECEIVED`: send the received message +- `PYMUMBLE_CLBK_ACLRECEIVED`: send the received acl permissions **Callbacks are executed within the library looping thread. Keep it's work short or you could have jitter issues!** @@ -281,6 +282,20 @@ After moving into a channel, it's normal to not have the list of user. Pymumble Unlink every channels which is linked to the channel. +> `Channel.get_acl()` + +Request an ACL permissions [object](https://github.com/mumble-voip/mumble/blob/master/src/Mumble.proto#L317) (requires bot have Write ACL permissions). This will invoke PYMUMBLE_CLBK_ACLRECEIVED. Example of usage: + +``` +def onacl(event): + for group in event.groups: + if event.group.name == "admin": + print("The admin IDs are: ", [user for user in group.add]) + +Mumble.callbacks.set_callback(PYMUMBLE_CLBK_ACL_RECEIVED, onacl) +Mumble.channels[0].get_acl() #Request ACL for root channel +``` + ## SoundOutput object (accessible through Mumble.sound_output) Takes care of encoding, packetizing and sending the audio to the server. diff --git a/pymumble_py3/callbacks.py b/pymumble_py3/callbacks.py index a48016b..7cefb7f 100644 --- a/pymumble_py3/callbacks.py +++ b/pymumble_py3/callbacks.py @@ -26,55 +26,56 @@ def __init__(self): PYMUMBLE_CLBK_SOUNDRECEIVED: None, # send the user object that received the sound and the SoundChunk object itself PYMUMBLE_CLBK_TEXTMESSAGERECEIVED: None, # Send the received message PYMUMBLE_CLBK_CONTEXTACTIONRECEIVED: None, # Send the contextaction message + PYMUMBLE_CLBK_ACLRECEIVED: None, # Send the received ACL permissions object }) - + def set_callback(self, callback, dest): """Define the function to call for a specific callback. Suppress any existing callback function""" if callback not in self: raise UnknownCallbackError("Callback \"%s\" does not exists." % callback) - + self[callback] = [dest] - + def add_callback(self, callback, dest): """Add the function to call for a specific callback.""" if callback not in self: raise UnknownCallbackError("Callback \"%s\" does not exists." % callback) - + if self[callback] is None: self[callback] = list() self[callback].append(dest) - + def get_callback(self, callback): """Get the functions assigned to a callback as a list. Return None if no callback defined""" if callback not in self: raise UnknownCallbackError("Callback \"%s\" does not exists." % callback) - + return self[callback] - + def remove_callback(self, callback, dest): """Remove a specific function from a specific callback. Function object must be the one added before.""" if callback not in self: raise UnknownCallbackError("Callback \"%s\" does not exists." % callback) - + if self[callback] is None or dest not in self[callback]: raise UnknownCallbackError("Function not registered for callback \"%s\"." % callback) self[callback].remove(dest) if len(self[callback]) == 0: self[callback] = None - + def reset_callback(self, callback): """remove functions for a defined callback""" if callback not in self: raise UnknownCallbackError("Callback \"%s\" does not exists." % callback) - + self[callback] = None - + def call_callback(self, callback, *pos_parameters): """Call all the registered function for a specific callback.""" if callback not in self: raise UnknownCallbackError("Callback \"%s\" does not exists." % callback) - + if self[callback]: for func in self[callback]: if callback is PYMUMBLE_CLBK_TEXTMESSAGERECEIVED: @@ -82,11 +83,11 @@ def call_callback(self, callback, *pos_parameters): thr.start() else: func(*pos_parameters) - + def __call__(self, callback, *pos_parameters): """shortcut to be able to call the dict element as a function""" self.call_callback(callback, *pos_parameters) - + def get_callbacks_list(self): """Get a list of all callbacks""" return list(self.keys()) diff --git a/pymumble_py3/channels.py b/pymumble_py3/channels.py index fe57048..fa9566a 100644 --- a/pymumble_py3/channels.py +++ b/pymumble_py3/channels.py @@ -217,4 +217,8 @@ def unlink(self): """Unlink all channels which is linked to a specific channel.""" if "links" in self: cmd = messages.UnlinkChannel({"channel_id": self["channel_id"], "remove_ids": self["links"]}) - self.mumble_object.execute_command(cmd) \ No newline at end of file + self.mumble_object.execute_command(cmd) + + def get_acl(self): + cmd = messages.QueryACL(self["channel_id"]) + self.mumble_object.execute_command(cmd) diff --git a/pymumble_py3/constants.py b/pymumble_py3/constants.py index 739aa06..4ec3abe 100644 --- a/pymumble_py3/constants.py +++ b/pymumble_py3/constants.py @@ -35,7 +35,7 @@ PYMUMBLE_CONN_STATE_AUTHENTICATING = 1 PYMUMBLE_CONN_STATE_CONNECTED = 2 PYMUMBLE_CONN_STATE_FAILED = 3 - + # Mumble control messages types PYMUMBLE_MSG_TYPES_VERSION = 0 PYMUMBLE_MSG_TYPES_UDPTUNNEL = 1 @@ -66,7 +66,7 @@ # callbacks names PYMUMBLE_CLBK_CONNECTED = "connected" PYMUMBLE_CLBK_DISCONNECTED = "disconnected" -PYMUMBLE_CLBK_CHANNELCREATED = "channel_created" +PYMUMBLE_CLBK_CHANNELCREATED = "channel_created" PYMUMBLE_CLBK_CHANNELUPDATED = "channel_updated" PYMUMBLE_CLBK_CHANNELREMOVED = "channel_remove" PYMUMBLE_CLBK_USERCREATED = "user_created" @@ -75,6 +75,7 @@ PYMUMBLE_CLBK_SOUNDRECEIVED = "sound_received" PYMUMBLE_CLBK_TEXTMESSAGERECEIVED = "text_received" PYMUMBLE_CLBK_CONTEXTACTIONRECEIVED = "contextAction_received" +PYMUMBLE_CLBK_ACLRECEIVED = "acl_received" # audio types PYMUMBLE_AUDIO_TYPE_CELT_ALPHA = 0 @@ -89,4 +90,5 @@ PYMUMBLE_CMD_MODUSERSTATE = "update_user" PYMUMBLE_CMD_TEXTMESSAGE = "text_message" PYMUMBLE_CMD_TEXTPRIVATEMESSAGE = "text_private_message" -PYMUMBLE_CMD_CHANNELUNLINK = "unlink" \ No newline at end of file +PYMUMBLE_CMD_CHANNELUNLINK = "unlink" +PYMUMBLE_CMD_QUERYACL = "get_acl" diff --git a/pymumble_py3/messages.py b/pymumble_py3/messages.py index 9266c56..976fc2d 100644 --- a/pymumble_py3/messages.py +++ b/pymumble_py3/messages.py @@ -102,3 +102,12 @@ def __init__(self, params): self.cmd = PYMUMBLE_CMD_CHANNELUNLINK self.parameters = params + +class QueryACL(Cmd): + """Command to query ACL information for channel""" + + def __init__(self, channel_id): + Cmd.__init__(self) + + self.cmd = PYMUMBLE_CMD_QUERYACL + self.parameters = {"channel_id": channel_id} diff --git a/pymumble_py3/mumble.py b/pymumble_py3/mumble.py index d9d0130..c5900cc 100644 --- a/pymumble_py3/mumble.py +++ b/pymumble_py3/mumble.py @@ -69,7 +69,7 @@ def __init__(self, host, user, port=64738, password='', certfile=None, keyfile=N self.tokens = tokens self.__opus_profile = PYMUMBLE_AUDIO_TYPE_OPUS_PROFILE self.stereo = stereo - + if stereo: self.Log.debug("Working in STEREO mode.") else: @@ -189,7 +189,7 @@ def loop(self): waiting for a message from the server for maximum self.loop_rate time take care of sending the ping take care of sending the queued commands to the server - check on every iteration for outgoing sound + check on every iteration for outgoing sound check for disconnection """ self.Log.debug("entering loop") @@ -381,6 +381,8 @@ def dispatch_control_message(self, type, message): mess.ParseFromString(message) self.Log.debug("message: ACL : %s", mess) + self.callbacks(PYMUMBLE_CLBK_ACLRECEIVED, mess) + elif type == PYMUMBLE_MSG_TYPES_QUERYUSERS: mess = mumble_pb2.QueryUsers() mess.ParseFromString(message) @@ -513,7 +515,7 @@ def sound_received(self, message): if newsound is None: # In case audio have been disable for specific users return - + self.callbacks(PYMUMBLE_CLBK_SOUNDRECEIVED, self.users[session.value], newsound) sequence.value += int(round(newsound.duration / 1000 * 10)) # add 1 sequence per 10ms of audio @@ -672,6 +674,14 @@ def treat_command(self, cmd): cmd.response = True self.commands.answer(cmd) + elif cmd.cmd == PYMUMBLE_CMD_QUERYACL: + acl = mumble_pb2.ACL() + acl.channel_id = cmd.parameters["channel_id"] + acl.query = True + self.send_message(PYMUMBLE_MSG_TYPES_ACL, acl) + cmd.response = True + self.commands.answer(cmd) + def get_max_message_length(self): return self.server_max_message_length @@ -680,7 +690,7 @@ def get_max_image_length(self): def my_channel(self): return self.channels[self.users.myself["channel_id"]] - + def stop(self): self.reconnect = None self.exit = True