mirror of
https://github.com/nikdoof/mumblepy.git
synced 2025-12-13 06:22:17 +00:00
first commit
This commit is contained in:
3
mumble/__init__.py
Normal file
3
mumble/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .meta import Meta
|
||||
from .hooks import *
|
||||
from .server import Server
|
||||
23
mumble/channel.py
Normal file
23
mumble/channel.py
Normal file
@@ -0,0 +1,23 @@
|
||||
class Channel(object):
|
||||
def __init__(self, server, channel):
|
||||
self.__server = server
|
||||
self.__channel = channel
|
||||
|
||||
def delete(self):
|
||||
self.__server.remove_channel(self.__channel.id)
|
||||
|
||||
def update(self, **kwargs):
|
||||
for key, value in kwargs.items():
|
||||
setattr(self.__channel, key, value)
|
||||
self.__server.set_channel_state(self.__channel)
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
'id': self.__channel.id,
|
||||
'parent': self.__channel.parent,
|
||||
'links': self.__channel.links,
|
||||
'name': self.__channel.name,
|
||||
'description': self.__channel.description,
|
||||
'temporary': self.__channel.temporary,
|
||||
'position': self.__channel.position,
|
||||
}
|
||||
117
mumble/hooks.py
Normal file
117
mumble/hooks.py
Normal file
@@ -0,0 +1,117 @@
|
||||
class MetaCallback(object):
|
||||
definition = ('MetaCallback', 'addCallback', 'removeCallback')
|
||||
|
||||
def __init__(self, meta):
|
||||
self.meta = meta
|
||||
|
||||
def started(self, server):
|
||||
"""Called when a server is started. The server is up and running when this event is sent,
|
||||
so all methods that need a running server will work."""
|
||||
pass
|
||||
|
||||
def stopped(self, server):
|
||||
"""Called when a server is stopped. The server is already stopped when this event is sent,
|
||||
so no methods that need a running server will work."""
|
||||
pass
|
||||
|
||||
|
||||
class ServerCallback(object):
|
||||
definition = ('ServerCallback', 'addCallback', 'removeCallback')
|
||||
|
||||
def __init__(self, server_id):
|
||||
self.id = server_id
|
||||
|
||||
def user_connected(self, state):
|
||||
"""Called when a user connects to the server. """
|
||||
pass
|
||||
|
||||
def user_disconnected(self, state):
|
||||
"""Called when a user disconnects from the server."""
|
||||
pass
|
||||
|
||||
def user_state_changed(self, state):
|
||||
"""Called when a user state changes. This is called if the user moves, is renamed, is muted,
|
||||
deafened etc."""
|
||||
pass
|
||||
|
||||
def user_text_message(self, state, message):
|
||||
"""Called when user writes a text message."""
|
||||
pass
|
||||
|
||||
def channel_created(self, state):
|
||||
"""Called when a new channel is created."""
|
||||
pass
|
||||
|
||||
def channel_removed(self, state):
|
||||
"""Called when a channel is removed."""
|
||||
pass
|
||||
|
||||
def channel_state_changed(self, state):
|
||||
"""Called when a new channel state changes. This is called if the channel is moved, renamed
|
||||
or if new links are added."""
|
||||
pass
|
||||
|
||||
|
||||
class ServerContextCallback(object):
|
||||
definition = ('ServerContextCallback', 'addContextCallback', 'removeContextCallback')
|
||||
|
||||
def __init__(self, server_id):
|
||||
self.id = server_id
|
||||
|
||||
def context_action(self, action, user, session, channelid):
|
||||
pass
|
||||
|
||||
|
||||
class ServerAuthenticator(object):
|
||||
definition = ('ServerAuthenticator', 'setAuthenticator', None)
|
||||
fallthrough_values = dict(
|
||||
authenticate=(-2, None, None),
|
||||
get_info=(False, None,),
|
||||
name_to_id=-2,
|
||||
id_to_name='',
|
||||
id_to_texture=None,
|
||||
)
|
||||
|
||||
def __init__(self, server_id):
|
||||
self.id = server_id
|
||||
|
||||
def authenticate(self, name, password, certificates, certhash, certstrong):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_info(self, user_id):
|
||||
raise NotImplementedError
|
||||
|
||||
def name_to_id(self, name):
|
||||
raise NotImplementedError
|
||||
|
||||
def id_to_name(self, user_id):
|
||||
raise NotImplementedError
|
||||
|
||||
def id_to_texture(self, user_id):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ServerUpdatingAuthenticator(ServerAuthenticator):
|
||||
definition = ('ServerUpdatingAuthenticator', 'setAuthenticator', None)
|
||||
fallthrough_values = dict(
|
||||
register_user=-2,
|
||||
unregister_user=-1,
|
||||
get_registered_users={},
|
||||
set_info=-1,
|
||||
set_texture=-1,
|
||||
)
|
||||
|
||||
def register_user(self, info):
|
||||
raise NotImplementedError
|
||||
|
||||
def unregister_user(self, user_id):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_registered_users(self, filter):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_info(self, user_id, info):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_texture(self, user_id, texture):
|
||||
raise NotImplementedError
|
||||
37
mumble/iceutil.py
Normal file
37
mumble/iceutil.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import re
|
||||
|
||||
|
||||
def ice_method(attr):
|
||||
# Convert event name from CamelCase to underscores.
|
||||
attr = re.sub(r'(.)([A-Z][a-z]+)', r'\1_\2', attr)
|
||||
attr = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', attr).lower()
|
||||
# Strip out the `current` value which is the final one alwaus.
|
||||
return lambda self, *args: getattr(self.callback, attr)(*args[:-1])
|
||||
|
||||
|
||||
def ice_callback(name, bases, attrs):
|
||||
def __init__(self, callback):
|
||||
self.callback = callback
|
||||
attrs['__init__'] = __init__
|
||||
|
||||
# Detect Ice methods and wrap in more pythonic callbacks.
|
||||
for base in bases:
|
||||
for attr in dir(base):
|
||||
if attr.startswith('_op_') and not attr.startswith('_op_ice_'):
|
||||
attr = attr[4:]
|
||||
attrs[attr] = ice_method(attr)
|
||||
|
||||
return type(name, bases, attrs)
|
||||
|
||||
|
||||
_ice_class_cache = {}
|
||||
|
||||
|
||||
def ice_init(from_, name, *args, **kwargs):
|
||||
try:
|
||||
cls = _ice_class_cache[name]
|
||||
except KeyError:
|
||||
class MurmurClass(getattr(from_, name)):
|
||||
__metaclass__ = ice_callback
|
||||
cls = _ice_class_cache[name] = MurmurClass
|
||||
return cls(*args, **kwargs)
|
||||
134
mumble/meta.py
Normal file
134
mumble/meta.py
Normal file
@@ -0,0 +1,134 @@
|
||||
import Ice
|
||||
import IcePy
|
||||
import sys
|
||||
import tempfile
|
||||
import os
|
||||
import logging
|
||||
from .iceutil import ice_init
|
||||
from .server import Server
|
||||
|
||||
|
||||
class Logger(Ice.Logger):
|
||||
def _print(self, message):
|
||||
logging.info(message)
|
||||
|
||||
def trace(self, category, message):
|
||||
pass
|
||||
|
||||
def warning(self, message):
|
||||
logging.warning(message)
|
||||
|
||||
def error(self, message):
|
||||
logging.error(message)
|
||||
|
||||
|
||||
class Meta(object):
|
||||
def __init__(self, secret=None):
|
||||
self.secret = secret
|
||||
|
||||
self.__meta = None
|
||||
self.__ice = None
|
||||
self.__adapter = None
|
||||
|
||||
self.connect()
|
||||
|
||||
def __del__(self):
|
||||
self.disconnect()
|
||||
|
||||
def load_slice(self, proxy):
|
||||
mumble_slice = IcePy.Operation(
|
||||
'getSlice',
|
||||
Ice.OperationMode.Idempotent,
|
||||
Ice.OperationMode.Idempotent,
|
||||
True,
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
IcePy._t_string,
|
||||
()
|
||||
).invoke(proxy, ((), None))
|
||||
|
||||
_, temp = tempfile.mkstemp(suffix='.ice')
|
||||
|
||||
with open(temp, 'w') as slice_file:
|
||||
slice_file.write(mumble_slice)
|
||||
slice_file.flush()
|
||||
Ice.loadSlice('', ['-I' + Ice.getSliceDir(), temp])
|
||||
|
||||
os.remove(temp)
|
||||
|
||||
def connect(self):
|
||||
init_data = Ice.InitializationData()
|
||||
init_data.properties = Ice.createProperties(sys.argv)
|
||||
init_data.properties.setProperty('Ice.ImplicitContext', 'Shared')
|
||||
init_data.logger = Logger()
|
||||
|
||||
self.__ice = Ice.initialize(init_data)
|
||||
|
||||
if self.secret:
|
||||
self.__ice.getImplicitContext().put('secret', self.secret)
|
||||
|
||||
self.__adapter = self.__ice.createObjectAdapterWithEndpoints('Callback.Client', 'tcp -h 127.0.0.1')
|
||||
self.__adapter.activate()
|
||||
|
||||
proxy = self.__ice.stringToProxy('Meta:tcp -h 127.0.0.1 -p 6502')
|
||||
|
||||
self.load_slice(proxy)
|
||||
|
||||
import Murmur
|
||||
self.__meta = Murmur.MetaPrx.checkedCast(proxy)
|
||||
|
||||
def disconnect(self):
|
||||
self.__ice.shutdown()
|
||||
|
||||
def add_callback(self, callback):
|
||||
import Murmur
|
||||
callback_prx = Murmur.MetaCallbackPrx.uncheckedCast(self.__adapter.addWithUUID(callback))
|
||||
self.__meta.addCallback(callback_prx)
|
||||
|
||||
def remove_callback(self, callback):
|
||||
self.__meta.removeCallback(callback)
|
||||
|
||||
def get_version(self):
|
||||
return self.__meta.getVersion()
|
||||
|
||||
def get_booted_servers(self):
|
||||
return [Server(self, server) for server in self.__meta.getBootedServers()]
|
||||
|
||||
def get_all_servers(self):
|
||||
return [Server(self, server) for server in self.__meta.getAllServers()]
|
||||
|
||||
def get_default_conf(self):
|
||||
return self.__meta.getDefaultConf()
|
||||
|
||||
def new_server(self):
|
||||
return Server(self, self.__meta.newServer())
|
||||
|
||||
def get_server(self, server_id):
|
||||
server = self.__meta.getServer(server_id)
|
||||
if server:
|
||||
return Server(self, server)
|
||||
return None
|
||||
|
||||
def get_uptime(self):
|
||||
return self.__meta.getUptime()
|
||||
|
||||
def add_hook(self, cls):
|
||||
return self.add_hook_to(self.__meta, cls, self)
|
||||
|
||||
def add_hook_to(self, target, cls, *args, **kwargs):
|
||||
import Murmur
|
||||
name, add_func_name, _ = cls.definition
|
||||
hook = ice_init(Murmur, name, cls(*args, **kwargs))
|
||||
hook_with_uuid = self.__adapter.addWithUUID(hook)
|
||||
hook_prx = getattr(Murmur, '%sPrx' % name).checkedCast(hook_with_uuid)
|
||||
return getattr(target, add_func_name)(hook_prx)
|
||||
|
||||
def remove_hook(self, cls, hook_prx):
|
||||
self.remove_hook_from(self.__meta, cls, hook_prx)
|
||||
|
||||
def remove_hook_from(self, target, cls, hook_prx):
|
||||
_, _, remove_func_name = cls.definition
|
||||
if not remove_func_name:
|
||||
return
|
||||
getattr(target, remove_func_name).addCallback(hook_prx)
|
||||
96
mumble/server.py
Normal file
96
mumble/server.py
Normal file
@@ -0,0 +1,96 @@
|
||||
from .user import User
|
||||
from .channel import Channel
|
||||
|
||||
|
||||
class Server(object):
|
||||
def __init__(self, meta, server):
|
||||
self.id = server.id()
|
||||
|
||||
self.__meta = meta
|
||||
self.__server = server
|
||||
|
||||
def __len__(self):
|
||||
return
|
||||
|
||||
@property
|
||||
def running(self):
|
||||
return bool(self.__server.isRunning())
|
||||
|
||||
def start(self):
|
||||
if not self.running:
|
||||
return self.__server.start()
|
||||
|
||||
def stop(self):
|
||||
if self.running:
|
||||
return self.__server.stop()
|
||||
|
||||
def delete(self):
|
||||
self.stop()
|
||||
return self.__server.delete()
|
||||
|
||||
# Conf
|
||||
|
||||
def get_all_conf(self):
|
||||
conf = self.__meta.get_default_conf()
|
||||
conf.update(self.__server.getAllConf())
|
||||
return conf
|
||||
|
||||
def get_conf(self, key):
|
||||
return self.__server.getConf(key)
|
||||
|
||||
def set_conf(self, key, value):
|
||||
return self.__server.setConf(key, value)
|
||||
|
||||
# Channels
|
||||
|
||||
def get_channels(self):
|
||||
return [Channel(self, channel) for channel in self.__server.getChannels().values()]
|
||||
|
||||
def get_channel(self, channel_id):
|
||||
channel = self.__server.getChannelState(channel_id)
|
||||
|
||||
if channel is None:
|
||||
return None
|
||||
|
||||
return Channel(self, channel)
|
||||
|
||||
def set_channel_state(self, channel):
|
||||
self.__server.setChannelState(channel)
|
||||
|
||||
def add_channel(self, name, parent):
|
||||
return self.__server.addChannel(name, parent)
|
||||
|
||||
def remove_channel(self, channel_id):
|
||||
self.__server.removeChannel(channel_id)
|
||||
|
||||
# Users
|
||||
|
||||
def get_users(self):
|
||||
return [User(self, user) for user in self.__server.getUsers().values()]
|
||||
|
||||
def get_user(self, session):
|
||||
user = self.__server.getState(session)
|
||||
|
||||
if user is None:
|
||||
return None
|
||||
|
||||
return User(self, user)
|
||||
|
||||
def kick_user(self, session, reason=''):
|
||||
self.__server.kickUser(session, reason)
|
||||
|
||||
# Bans
|
||||
|
||||
def get_bans(self):
|
||||
return self.__server.getBans()
|
||||
|
||||
def set_bans(self, bans):
|
||||
self.__server.setBans(bans)
|
||||
|
||||
# Hooks
|
||||
|
||||
def add_hook(self, cls):
|
||||
self.__meta.add_hook_to(self.__server, cls, self.id)
|
||||
|
||||
def remove_hook(self, cls, hook):
|
||||
self.__meta.remove_hook_from(self.__server, cls, hook)
|
||||
38
mumble/user.py
Normal file
38
mumble/user.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import time
|
||||
|
||||
|
||||
class User(object):
|
||||
def __init__(self, server, user):
|
||||
self.__server = server
|
||||
self.__user = user
|
||||
|
||||
def ban(self, reason='', bits=128, duration=360):
|
||||
from Murmur import Ban
|
||||
bans = self.__server.get_bans()
|
||||
bans.append(Ban(
|
||||
reason=reason,
|
||||
bits=bits,
|
||||
duration=duration,
|
||||
start=int(time.time()),
|
||||
address=self.__user.address,
|
||||
))
|
||||
self.__server.set_bans(bans)
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
'session': self.__user.session,
|
||||
'id': self.__user.userid,
|
||||
'priority_speaker': self.__user.prioritySpeaker,
|
||||
'mute': self.__user.mute,
|
||||
'deaf': self.__user.deaf,
|
||||
'suppress': self.__user.suppress,
|
||||
'channel': self.__user.channel,
|
||||
'name': self.__user.name,
|
||||
'online_secs': self.__user.onlinesecs,
|
||||
'comment': self.__user.comment,
|
||||
'self_mute': self.__user.selfMute,
|
||||
'self_deaf': self.__user.selfDeaf,
|
||||
'idle_secs': self.__user.idlesecs,
|
||||
'ip': '.'.join(map(unicode, self.__user.address[-4:])),
|
||||
'os': self.__user.osversion
|
||||
}
|
||||
27
setup.py
Normal file
27
setup.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# coding=utf-8
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
DESCRIPTION = 'Python Mumble for Humans™'
|
||||
|
||||
with open('README.md') as f:
|
||||
LONG_DESCRIPTION = f.read()
|
||||
|
||||
VERSION = '0.1.0'
|
||||
|
||||
setup(
|
||||
name='mumble',
|
||||
version=VERSION,
|
||||
packages=find_packages(),
|
||||
author='Stanislav Vishnevskiy',
|
||||
author_email='vishnevskiy@gmail.com',
|
||||
url='https://github.com/vishnevskiy/mumblepy',
|
||||
license='MIT',
|
||||
include_package_data=True,
|
||||
description=DESCRIPTION,
|
||||
long_description=LONG_DESCRIPTION,
|
||||
install_requires=[],
|
||||
platforms=['any'],
|
||||
classifiers=[],
|
||||
test_suite='tests',
|
||||
)
|
||||
Reference in New Issue
Block a user