Skip to content

Commit

Permalink
Added ACL querying
Browse files Browse the repository at this point in the history
Merge pull request #80 from CaptainZidgel/acl
  • Loading branch information
azlux authored Sep 6, 2020
2 parents 5011492 + 61b1d38 commit 9de35a5
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 22 deletions.
15 changes: 15 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!**

Expand Down Expand Up @@ -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.

Expand Down
29 changes: 15 additions & 14 deletions pymumble_py3/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,67 +26,68 @@ 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:
thr = threading.Thread(target=func, args=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())
6 changes: 5 additions & 1 deletion pymumble_py3/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
self.mumble_object.execute_command(cmd)

def get_acl(self):
cmd = messages.QueryACL(self["channel_id"])
self.mumble_object.execute_command(cmd)
8 changes: 5 additions & 3 deletions pymumble_py3/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -89,4 +90,5 @@
PYMUMBLE_CMD_MODUSERSTATE = "update_user"
PYMUMBLE_CMD_TEXTMESSAGE = "text_message"
PYMUMBLE_CMD_TEXTPRIVATEMESSAGE = "text_private_message"
PYMUMBLE_CMD_CHANNELUNLINK = "unlink"
PYMUMBLE_CMD_CHANNELUNLINK = "unlink"
PYMUMBLE_CMD_QUERYACL = "get_acl"
9 changes: 9 additions & 0 deletions pymumble_py3/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}
18 changes: 14 additions & 4 deletions pymumble_py3/mumble.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down

0 comments on commit 9de35a5

Please sign in to comment.