This commit is contained in:
Giovanni Harting 2016-06-01 21:03:15 +02:00
parent c42113d712
commit 27d0d313d2
27 changed files with 1657 additions and 1518 deletions

View File

@ -57,7 +57,7 @@ modules-enabled folder.
## Requirements
mumo requires:
* python 2.7*
* python >=3.2
* python-zeroc-ice
* murmur >=1.2.3*

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# -*- coding: utf-8
# Copyright (C) 2010 Stefan Hacker <dd0t@users.sourceforge.net>
@ -29,40 +29,42 @@
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import ConfigParser
import configparser
import types
class Config(object):
"""
Small abstraction for config loading
"""
def __init__(self, filename = None, default = None):
def __init__(self, filename=None, default=None):
if (filename and not default) or \
(not filename and not default): return
sections = set(default.iterkeys())
(not filename and not default): return
sections = set(default.keys())
if filename:
cfg = ConfigParser.RawConfigParser()
cfg = configparser.RawConfigParser()
cfg.optionxform = str
with open(filename) as f:
cfg.readfp(f)
cfg.read_file(f)
sections.update(cfg.sections())
for section in sections:
if type(section) == types.FunctionType: continue
if isinstance(section, types.FunctionType):
continue
match = None
for default_section in default.iterkeys():
for default_section in default.keys():
try:
if section == default_section or \
(type(default_section) == types.FunctionType and default_section(section)):
(isinstance(default_section, types.FunctionType) and default_section(section)):
match = default_section
break
except ValueError:
continue
if match == None:
if match is None:
continue
optiondefaults = default[match]
@ -74,7 +76,7 @@ class Config(object):
else:
try:
self.__dict__[section] = cfg.items(section)
except ConfigParser.NoSectionError:
except configparser.NoSectionError:
self.__dict__[section] = []
else:
self.__dict__[section] = Config()
@ -84,42 +86,46 @@ class Config(object):
else:
try:
self.__dict__[section].__dict__[name] = conv(cfg.get(section, name))
except (ValueError, ConfigParser.NoSectionError, ConfigParser.NoOptionError):
except (ValueError, configparser.NoSectionError, configparser.NoOptionError):
self.__dict__[section].__dict__[name] = vdefault
def __getitem__(self, key):
return self.__dict__.__getitem__(key)
def __contains__(self, key):
return self.__dict__.__contains__(key)
def x2bool(s):
"""
Helper function to convert strings from the config to bool
"""
if isinstance(s, bool):
return s
elif isinstance(s, basestring):
elif isinstance(s, str):
return s.strip().lower() in ['1', 'true']
raise ValueError()
def commaSeperatedIntegers(s):
"""
Helper function to convert a string from the config
containing comma seperated integers into a list of integers
"""
return map(int, s.split(','))
return list(map(int, s.split(',')))
def commaSeperatedStrings(s):
"""
Helper function to convert a string from the config
containing comma seperated strings into a list of strings
"""
return map(str.strip, s.split(','))
return list(map(str.strip, s.split(',')))
def commaSeperatedBool(s):
"""
Helper function to convert a string from the config
containing comma seperated strings into a list of booleans
"""
return map(x2bool, s.split(','))
return list(map(x2bool, s.split(',')))

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# -*- coding: utf-8
# Copyright (C) 2010 Stefan Hacker <dd0t@users.sourceforge.net>
@ -29,13 +29,15 @@
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import unittest
from config import Config, x2bool, commaSeperatedIntegers, commaSeperatedStrings, commaSeperatedBool
from tempfile import mkstemp
import os
import re
import unittest
from tempfile import mkstemp
def create_file(content = None):
from config import Config, x2bool, commaSeperatedIntegers, commaSeperatedStrings, commaSeperatedBool
def create_file(content=None):
"""
Creates a temp file filled with 'content' and returns its path.
The file has to be manually deleted later on
@ -43,11 +45,12 @@ def create_file(content = None):
fd, path = mkstemp()
f = os.fdopen(fd, "wb")
if content:
f.write(content)
f.write(content.encode())
f.flush()
f.close()
return path
class ConfigTest(unittest.TestCase):
cfg_content = """[world]
domination = True
@ -62,15 +65,15 @@ value = False
[Server_2]
value = True
"""
cfg_default = {'world':(('domination', x2bool, False),
('somestr', str, "fail"),
('somenum', int, 0),
('somenumtest', int, 1),
('blubber', str, "empty"),
('serverregex', re.compile, '.*')),
(lambda x: re.match("Server_\d+",x)):(('value', x2bool, True),),
'somethingelse':(('bla', str, "test"),)}
cfg_default = {'world': (('domination', x2bool, False),
('somestr', str, "fail"),
('somenum', int, 0),
('somenumtest', int, 1),
('blubber', str, "empty"),
('serverregex', re.compile, '.*')),
(lambda x: re.match("Server_\d+", x)): (('value', x2bool, True),),
'somethingelse': (('bla', str, "test"),)}
def setUp(self):
pass
@ -78,76 +81,75 @@ value = True
def tearDown(self):
pass
def testEmpty(self):
path = create_file()
try:
cfg = Config(path, self.cfg_default)
assert(cfg.world.domination == False)
assert(cfg.world.somestr == "fail")
assert(cfg.world.somenum == 0)
assert (cfg.world.domination == False)
assert (cfg.world.somestr == "fail")
assert (cfg.world.somenum == 0)
self.assertRaises(AttributeError, getattr, cfg.world, "testfallbacknum")
assert(cfg.somethingelse.bla == "test")
assert (cfg.somethingelse.bla == "test")
finally:
os.remove(path)
def testX2bool(self):
assert(x2bool(" true") == True)
assert(x2bool("false") == False)
assert(x2bool(" TrUe") == True)
assert(x2bool("FaLsE ") == False)
assert(x2bool("0 ") == False)
assert(x2bool("1") == True)
assert(x2bool(" 10") == False)
assert(x2bool("notabool") == False)
assert (x2bool(" true") == True)
assert (x2bool("false") == False)
assert (x2bool(" TrUe") == True)
assert (x2bool("FaLsE ") == False)
assert (x2bool("0 ") == False)
assert (x2bool("1") == True)
assert (x2bool(" 10") == False)
assert (x2bool("notabool") == False)
def testCommaSeperatedIntegers(self):
assert(commaSeperatedIntegers(" 1,2 , 333 ") == [1,2,333])
assert (commaSeperatedIntegers(" 1,2 , 333 ") == [1, 2, 333])
self.assertRaises(ValueError, commaSeperatedIntegers, "1,2,a")
def testCommaSeperatedStrings(self):
assert(commaSeperatedStrings("Bernd, the, bred !") == ["Bernd", "the", "bred !"])
assert (commaSeperatedStrings("Bernd, the, bred !") == ["Bernd", "the", "bred !"])
def testCommaSeperatedBool(self):
assert(commaSeperatedBool("tRue ,false, 0, 0, 1,1, test") == [True, False, False, False, True, True, False])
assert (commaSeperatedBool("tRue ,false, 0, 0, 1,1, test") == [True, False, False, False, True, True, False])
def testConfig(self):
path = create_file(self.cfg_content)
try:
try:
cfg = Config(path, self.cfg_default)
except Exception, e:
print e
assert(cfg.world.domination == True)
assert(cfg.world.somestr == "Blabla")
assert(cfg.world.somenum == 10)
except Exception as e:
print(e)
assert (cfg.world.domination == True)
assert (cfg.world.somestr == "Blabla")
assert (cfg.world.somenum == 10)
self.assertRaises(AttributeError, getattr, cfg.world, "testfallbacknum")
self.assertEqual(cfg.world.blubber, "Things %(doesnotexistsasdefault)s")
self.assertEqual(cfg.world.serverregex, re.compile("^\[[\w\d\-\(\):]{1,20}\]$"))
assert(cfg.somethingelse.bla == "test")
assert(cfg.Server_10.value == False)
assert(cfg.Server_2.value == True)
assert(cfg.Server_9.value == True)
assert (cfg.somethingelse.bla == "test")
assert (cfg.Server_10.value == False)
assert (cfg.Server_2.value == True)
assert (cfg.Server_9.value == True)
finally:
os.remove(path)
def testLoadDefault(self):
cfg = Config(default=self.cfg_default)
assert(cfg.world.domination == False)
assert(cfg.somethingelse.bla == "test")
assert(cfg.world.somenum == 0)
assert (cfg.world.domination == False)
assert (cfg.somethingelse.bla == "test")
assert (cfg.world.somenum == 0)
def testGetItem(self):
cfg = Config(default=self.cfg_default)
assert(cfg["world"]["domination"] == False)
assert("world" in cfg)
assert (cfg["world"]["domination"] == False)
assert ("world" in cfg)
def invalidaccess(c):
c["nointhisconfig"]
self.assertRaises(KeyError, invalidaccess, cfg)
if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testName']
# import sys;sys.argv = ['', 'Test.testName']
unittest.main()

View File

@ -17,7 +17,7 @@ DESC="Mumo bot for Mumble"
WORKDIR=/opt/mumo
PIDDIR=$WORKDIR
PIDFILE=$PIDDIR/mumo.pid
DAEMON=/usr/bin/python
DAEMON=/usr/bin/python3
USER=mumo
GROUP=mumo

View File

@ -1,2 +1,2 @@
# No real module, just here to keep pydev and its
# test runner happy.
# test runner happy.

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- coding: utf-8
# Copyright (C) 2010-2011 Stefan Hacker <dd0t@users.sourceforge.net>
@ -35,77 +35,75 @@
# gamestate reported by Mumble positional audio plugins
#
from mumo_module import (MumoModule,
x2bool)
import json
import re
try:
import json
except ImportError: # Fallback for python < 2.6
import simplejson as json
from config import x2bool
from mumo_module import MumoModule
class bf2(MumoModule):
default_config = {'bf2':(
('gamecount', int, 1),
),
lambda x: re.match('g\d+', x):(
('name', str, ''),
('mumble_server', int, 1),
('ipport_filter_negate', x2bool, False),
('ipport_filter', re.compile, re.compile('.*')),
('base', int, 0),
('left', int, -1),
('blufor', int, -1),
('blufor_commander', int, -1),
('blufor_no_squad', int, -1),
('blufor_first_squad', int, -1),
('blufor_first_squad_leader', int, -1),
('blufor_second_squad', int, -1),
('blufor_second_squad_leader', int, -1),
('blufor_third_squad', int, -1),
('blufor_third_squad_leader', int, -1),
('blufor_fourth_squad', int, -1),
('blufor_fourth_squad_leader', int, -1),
('blufor_fifth_squad', int, -1),
('blufor_fifth_squad_leader', int, -1),
('blufor_sixth_squad', int, -1),
('blufor_sixth_squad_leader', int, -1),
('blufor_seventh_squad', int, -1),
('blufor_seventh_squad_leader', int, -1),
('blufor_eighth_squad', int, -1),
('blufor_eighth_squad_leader', int, -1),
('blufor_ninth_squad', int, -1),
('blufor_ninth_squad_leader', int, -1),
('opfor', int, -1),
('opfor_commander', int, -1),
('opfor_no_squad', int, -1),
('opfor_first_squad', int, -1),
('opfor_first_squad_leader', int, -1),
('opfor_second_squad', int, -1),
('opfor_second_squad_leader', int, -1),
('opfor_third_squad', int, -1),
('opfor_third_squad_leader', int, -1),
('opfor_fourth_squad', int, -1),
('opfor_fourth_squad_leader', int, -1),
('opfor_fifth_squad', int, -1),
('opfor_fifth_squad_leader', int, -1),
('opfor_sixth_squad', int, -1),
('opfor_sixth_squad_leader', int, -1),
('opfor_seventh_squad', int, -1),
('opfor_seventh_squad_leader', int, -1),
('opfor_eighth_squad', int, -1),
('opfor_eighth_squad_leader', int, -1),
('opfor_ninth_squad', int, -1),
('opfor_ninth_squad_leader', int, -1)
),
}
default_config = {'bf2': (
('gamecount', int, 1),
),
lambda x: re.match('g\d+', x): (
('name', str, ''),
('mumble_server', int, 1),
('ipport_filter_negate', x2bool, False),
('ipport_filter', re.compile, re.compile('.*')),
('base', int, 0),
('left', int, -1),
('blufor', int, -1),
('blufor_commander', int, -1),
('blufor_no_squad', int, -1),
('blufor_first_squad', int, -1),
('blufor_first_squad_leader', int, -1),
('blufor_second_squad', int, -1),
('blufor_second_squad_leader', int, -1),
('blufor_third_squad', int, -1),
('blufor_third_squad_leader', int, -1),
('blufor_fourth_squad', int, -1),
('blufor_fourth_squad_leader', int, -1),
('blufor_fifth_squad', int, -1),
('blufor_fifth_squad_leader', int, -1),
('blufor_sixth_squad', int, -1),
('blufor_sixth_squad_leader', int, -1),
('blufor_seventh_squad', int, -1),
('blufor_seventh_squad_leader', int, -1),
('blufor_eighth_squad', int, -1),
('blufor_eighth_squad_leader', int, -1),
('blufor_ninth_squad', int, -1),
('blufor_ninth_squad_leader', int, -1),
('opfor', int, -1),
('opfor_commander', int, -1),
('opfor_no_squad', int, -1),
('opfor_first_squad', int, -1),
('opfor_first_squad_leader', int, -1),
('opfor_second_squad', int, -1),
('opfor_second_squad_leader', int, -1),
('opfor_third_squad', int, -1),
('opfor_third_squad_leader', int, -1),
('opfor_fourth_squad', int, -1),
('opfor_fourth_squad_leader', int, -1),
('opfor_fifth_squad', int, -1),
('opfor_fifth_squad_leader', int, -1),
('opfor_sixth_squad', int, -1),
('opfor_sixth_squad_leader', int, -1),
('opfor_seventh_squad', int, -1),
('opfor_seventh_squad_leader', int, -1),
('opfor_eighth_squad', int, -1),
('opfor_eighth_squad_leader', int, -1),
('opfor_ninth_squad', int, -1),
('opfor_ninth_squad_leader', int, -1)
),
}
id_to_squad_name = ["no", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth"]
def __init__(self, name, manager, configuration = None):
def __init__(self, name, manager, configuration=None):
MumoModule.__init__(self, name, manager, configuration)
self.murmur = manager.getMurmurModule()
@ -114,7 +112,7 @@ class bf2(MumoModule):
manager = self.manager()
log = self.log()
log.debug("Register for Server callbacks")
servers = set()
for i in range(cfg.bf2.gamecount):
try:
@ -122,23 +120,24 @@ class bf2(MumoModule):
except KeyError:
log.error("Invalid configuration. Game configuration for 'g%d' not found.", i)
return
self.sessions = {} # {serverid:{sessionid:laststate}}
self.sessions = {} # {serverid:{sessionid:laststate}}
manager.subscribeServerCallbacks(self, servers)
manager.subscribeMetaCallbacks(self, servers)
def disconnected(self): pass
def disconnected(self):
pass
#
#--- Module specific state handling code
# --- Module specific state handling code
#
def update_state(self, server, oldstate, newstate):
log = self.log()
sid = server.id()
session = newstate.session
newoldchannel = newstate.channel
try:
opc = oldstate.parsedcontext
ogcfgname = opc["gamename"]
@ -147,15 +146,15 @@ class bf2(MumoModule):
opi = oldstate.parsedidentity
except (AttributeError, KeyError):
og = None
opi = {}
opc = {}
if oldstate and oldstate.is_linked:
oli = True
else:
oli = False
try:
npc = newstate.parsedcontext
ngcfgname = npc["gamename"]
@ -164,23 +163,25 @@ class bf2(MumoModule):
npi = newstate.parsedidentity
except (AttributeError, KeyError):
ng = None
npi = {}
npc = {}
nli = False
if newstate and newstate.is_linked:
nli = True
else:
nli = False
if not oli and nli:
log.debug("User '%s' (%d|%d) on server %d now linked", newstate.name, newstate.session, newstate.userid, sid)
log.debug("User '%s' (%d|%d) on server %d now linked", newstate.name, newstate.session, newstate.userid,
sid)
server.addUserToGroup(0, session, "bf2_linked")
if opi and opc:
squadname = self.id_to_squad_name[opi["squad"]]
log.debug("Removing user '%s' (%d|%d) on server %d from groups of game %s / squad %s", newstate.name, newstate.session, newstate.userid, sid, og or ogcfgname, squadname)
log.debug("Removing user '%s' (%d|%d) on server %d from groups of game %s / squad %s", newstate.name,
newstate.session, newstate.userid, sid, og or ogcfgname, squadname)
server.removeUserFromGroup(ogcfg["base"], session, "bf2_%s_game" % (og or ogcfgname))
server.removeUserFromGroup(ogcfg[opi["team"]], session, "bf2_commander")
server.removeUserFromGroup(ogcfg[opi["team"]], session, "bf2_squad_leader")
@ -189,72 +190,74 @@ class bf2(MumoModule):
server.removeUserFromGroup(ogcfg[opi["team"]], session, "bf2_team")
channame = "left"
newstate.channel = ogcfg["left"]
if npc and npi:
log.debug("Updating user '%s' (%d|%d) on server %d in game %s: %s", newstate.name, newstate.session, newstate.userid, sid, ng or ngcfgname, str(npi))
log.debug("Updating user '%s' (%d|%d) on server %d in game %s: %s", newstate.name, newstate.session,
newstate.userid, sid, ng or ngcfgname, str(npi))
squadname = self.id_to_squad_name[npi["squad"]]
# Add to game group
location = "base"
group = "bf2_%s_game" % (ng or ngcfgname)
server.addUserToGroup(ngcfg[location], session, group)
log.debug("Added '%s' @ %s to group %s in %s", newstate.name, ng or ngcfgname, group, location)
# Then add to team group
location = npi["team"]
group = "bf2_team"
server.addUserToGroup(ngcfg[location], session, group)
log.debug("Added '%s' @ %s to group %s in %s", newstate.name, ng or ngcfgname, group, location)
# Then add to squad group
group = "bf2_%s_squad" % squadname
server.addUserToGroup(ngcfg[location], session, group)
log.debug("Added '%s' @ %s to group %s in %s", newstate.name, ng or ngcfgname, group, location)
channame = "%s_%s_squad" % (npi["team"], self.id_to_squad_name[npi["squad"]])
newstate.channel = ngcfg[channame]
if npi["squad_leader"]:
# In case the leader flag is set add to leader group
group = "bf2_%s_squad_leader" % squadname
server.addUserToGroup(ngcfg[location], session, group)
log.debug("Added '%s' @ %s to group %s in %s", newstate.name, ng or ngcfgname, group, location)
group = "bf2_squad_leader"
server.addUserToGroup(ngcfg[location], session, group)
log.debug("Added '%s' @ %s to group %s in %s", newstate.name, ng or ngcfgname, group, location)
# Override previous moves
channame = "%s_%s_squad_leader" % (npi["team"], self.id_to_squad_name[npi["squad"]])
newstate.channel = ngcfg[channame]
if npi["commander"]:
group = "bf2_commander"
server.addUserToGroup(ngcfg[location], session, group)
log.debug("Added '%s' @ %s to group %s in %s", newstate.name, ng or ngcfgname, group, location)
# Override previous moves
channame = "%s_commander" % npi["team"]
newstate.channel = ngcfg[channame]
if oli and not nli:
log.debug("User '%s' (%d|%d) on server %d no longer linked", newstate.name, newstate.session, newstate.userid, sid)
log.debug("User '%s' (%d|%d) on server %d no longer linked", newstate.name, newstate.session,
newstate.userid, sid)
server.removeUserFromGroup(0, session, "bf2_linked")
if newstate.channel >= 0 and newoldchannel != newstate.channel:
if ng == None:
if 0 <= newstate.channel != newoldchannel:
if ng is None:
log.debug("Moving '%s' leaving %s to channel %s", newstate.name, og or ogcfgname, channame)
else:
log.debug("Moving '%s' @ %s to channel %s", newstate.name, ng or ngcfgname, channame)
server.setState(newstate)
def handle(self, server, state):
def verify(mdict, key, vtype):
if not isinstance(mdict[key], vtype):
raise ValueError("'%s' of invalid type" % key)
cfg = self.cfg()
log = self.log()
sid = server.id()
@ -263,17 +266,17 @@ class bf2(MumoModule):
state.parsedidentity = {}
state.parsedcontext = {}
state.is_linked = False
if sid not in self.sessions: # Make sure there is a dict to store states in
if sid not in self.sessions: # Make sure there is a dict to store states in
self.sessions[sid] = {}
update = False
if state.session in self.sessions[sid]:
if state.identity != self.sessions[sid][state.session].identity or \
state.context != self.sessions[sid][state.session].context:
state.context != self.sessions[sid][state.session].context:
# identity or context changed => update
update = True
else: # id and context didn't change hence the old data must still be valid
else: # id and context didn't change hence the old data must still be valid
state.is_linked = self.sessions[sid][state.session].is_linked
state.parsedcontext = self.sessions[sid][state.session].parsedcontext
state.parsedidentity = self.sessions[sid][state.session].parsedidentity
@ -282,46 +285,48 @@ class bf2(MumoModule):
# New user with engaged plugin => update
self.sessions[sid][state.session] = None
update = True
if not update:
self.sessions[sid][state.session] = state
return
# The plugin will always prefix "Battlefield 2\0" to the context for the bf2 PA plugin
# don't bother analyzing anything if it isn't there
splitcontext = state.context.split('\0', 1)
if splitcontext[0] == "Battlefield 2":
state.is_linked = True
if state.identity and len(splitcontext) == 1:
#LEGACY: Assume broken Ice 3.2 which doesn't transmit context after \0
splitcontext.append('{"ipport":""}') # Obviously this doesn't give full functionality but it doesn't crash either ;-)
# LEGACY: Assume broken Ice 3.2 which doesn't transmit context after \0
splitcontext.append(
'{"ipport":""}') # Obviously this doesn't give full functionality but it doesn't crash either ;-)
if state.is_linked and len(splitcontext) == 2 and state.identity:
if state.is_linked and len(splitcontext) == 2 and state.identity:
try:
context = json.loads(splitcontext[1])
verify(context, "ipport", basestring)
verify(context, "ipport", str)
for i in range(cfg.bf2.gamecount):
# Try to find a matching game
gamename = "g%d" % i
gamecfg = getattr(cfg, gamename)
if gamecfg.mumble_server == server.id():
not_matched = (gamecfg.ipport_filter.match(context["ipport"]) == None)
not_matched = (gamecfg.ipport_filter.match(context["ipport"]) is None)
if not_matched == gamecfg.ipport_filter_negate:
break
gamename = None
if not gamename:
raise ValueError("No matching game found")
context["gamecfg"] = gamecfg
context["gamename"] = gamename
state.parsedcontext = context
except (ValueError, KeyError, AttributeError), e:
log.debug("Invalid context for %s (%d|%d) on server %d: %s", state.name, state.session, state.userid, sid, repr(e))
except (ValueError, KeyError, AttributeError) as e:
log.debug("Invalid context for %s (%d|%d) on server %d: %s", state.name, state.session, state.userid,
sid, repr(e))
try:
identity = json.loads(state.identity)
verify(identity, "commander", bool)
@ -329,48 +334,57 @@ class bf2(MumoModule):
verify(identity, "squad", int)
if identity["squad"] < 0 or identity["squad"] > 9:
raise ValueError("Invalid squad number")
verify(identity, "team", basestring)
verify(identity, "team", str)
if identity["team"] != "opfor" and identity["team"] != "blufor":
raise ValueError("Invalid team identified")
#LEGACY: Ice 3.2 cannot handle unicode strings
# LEGACY: Ice 3.2 cannot handle unicode strings
identity["team"] = str(identity["team"])
state.parsedidentity = identity
except (KeyError, ValueError), e:
log.debug("Invalid identity for %s (%d|%d) on server %d: %s", state.name, state.session, state.userid, sid, repr(e))
except (KeyError, ValueError) as e:
log.debug("Invalid identity for %s (%d|%d) on server %d: %s", state.name, state.session, state.userid,
sid, repr(e))
# Update state and remember it
self.update_state(server, self.sessions[sid][state.session], state)
self.sessions[sid][state.session] = state
#
#--- Server callback functions
# --- Server callback functions
#
def userDisconnected(self, server, state, context = None):
def userDisconnected(self, server, state, context=None):
try:
sid = server.id()
del self.sessions[sid][state.session]
except KeyError: pass
def userStateChanged(self, server, state, context = None):
except KeyError:
pass
def userStateChanged(self, server, state, context=None):
self.handle(server, state)
def userConnected(self, server, state, context = None):
def userConnected(self, server, state, context=None):
self.handle(server, state)
def userTextMessage(self, server, user, message, current=None): pass
def channelCreated(self, server, state, context = None): pass
def channelRemoved(self, server, state, context = None): pass
def channelStateChanged(self, server, state, context = None): pass
def userTextMessage(self, server, user, message, current=None):
pass
def channelCreated(self, server, state, context=None):
pass
def channelRemoved(self, server, state, context=None):
pass
def channelStateChanged(self, server, state, context=None):
pass
#
#--- Meta callback functions
# --- Meta callback functions
#
def started(self, server, context = None):
def started(self, server, context=None):
self.sessions[server.id()] = {}
def stopped(self, server, context = None):
def stopped(self, server, context=None):
self.sessions[server.id()] = {}

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- coding: utf-8
# Copyright (C) 2010-2011 Stefan Hacker <dd0t@users.sourceforge.net>
@ -37,45 +37,41 @@
# once they become active again
#
from mumo_module import (commaSeperatedIntegers,
commaSeperatedBool,
commaSeperatedStrings,
MumoModule)
from threading import Timer
import re
from threading import Timer
from config import commaSeperatedIntegers, commaSeperatedBool, commaSeperatedStrings
from mumo_module import MumoModule
class idlemove(MumoModule):
default_config = {'idlemove':(
('interval', float, 0.1),
('servers', commaSeperatedIntegers, []),
),
lambda x: re.match('(all)|(server_\d+)', x):(
('threshold', commaSeperatedIntegers, [3600]),
('mute', commaSeperatedBool, [True]),
('deafen', commaSeperatedBool, [False]),
('channel', commaSeperatedIntegers, [1]),
('source_channel', commaSeperatedIntegers, [-1]),
('whitelist', commaSeperatedStrings, []),
('channel_whitelist', commaSeperatedIntegers, [])
),
}
default_config = {'idlemove': (
('interval', float, 0.1),
('servers', commaSeperatedIntegers, []),
),
lambda x: re.match('(all)|(server_\d+)', x): (
['threshold', commaSeperatedIntegers, [3600]],
('mute', commaSeperatedBool, [True]),
('deafen', commaSeperatedBool, [False]),
('channel', commaSeperatedIntegers, [1]),
('source_channel', commaSeperatedIntegers, [-1]),
('whitelist', commaSeperatedStrings, []),
('channel_whitelist', commaSeperatedIntegers, [])
),
}
def __init__(self, name, manager, configuration=None):
MumoModule.__init__(self, name, manager, configuration)
self.murmur = manager.getMurmurModule()
self.watchdog = None
def connected(self):
self.affectedusers = {} # {serverid:set(sessionids,...)}
self.affectedusers = {} # {serverid:set(sessionids,...)}
manager = self.manager()
log = self.log()
log.debug("Register for Meta & Server callbacks")
cfg = self.cfg()
servers = cfg.idlemove.servers
if not servers:
@ -83,11 +79,11 @@ class idlemove(MumoModule):
manager.subscribeServerCallbacks(self, servers)
manager.subscribeMetaCallbacks(self, servers)
if not self.watchdog:
self.watchdog = Timer(cfg.idlemove.interval, self.handleIdleMove)
self.watchdog.start()
def disconnected(self):
self.affectedusers = {}
if self.watchdog:
@ -98,58 +94,55 @@ class idlemove(MumoModule):
cfg = self.cfg()
try:
meta = self.manager().getMeta()
if not cfg.idlemove.servers:
servers = meta.getBootedServers()
else:
servers = [meta.getServer(server) for server in cfg.idlemove.servers]
for server in servers:
if not server: continue
if server:
for user in server.getUsers().itervalues():
self.UpdateUserAutoAway(server, user)
for user in server.getUsers().values():
self.UpdateUserAutoAway(server, user)
finally:
# Renew the timer
self.watchdog = Timer(cfg.idlemove.interval, self.handleIdleMove)
self.watchdog.start()
def UpdateUserAutoAway(self, server, user):
log = self.log()
sid = server.id()
try:
scfg = getattr(self.cfg(), 'server_%d' % sid)
except AttributeError:
scfg = self.cfg().all
try:
index = self.affectedusers[sid]
except KeyError:
self.affectedusers[sid] = set()
index = self.affectedusers[sid]
# Check if the user is whitelisted
if user.name in scfg.whitelist:
return
# Remember values so we can see changes later
threshold = None
mute = user.mute
deafen = user.deaf
channel = user.channel
update = False
over_threshold = False
# Search all our stages top down for a violated treshold and pick the first
for i in range(len(scfg.threshold) - 1, -1, -1):
try:
source_channel = scfg.source_channel[i]
except IndexError:
source_channel = -1
try:
threshold = scfg.threshold[i]
mute = scfg.mute[i]
@ -159,28 +152,27 @@ class idlemove(MumoModule):
log.warning("Incomplete configuration for stage %d of server %i, ignored", i, server.id())
continue
if user.idlesecs > threshold and\
user.channel not in scfg.channel_whitelist and\
(source_channel == -1 or\
user.channel == source_channel or\
user.channel == channel):
if user.idlesecs > threshold and user.channel not in scfg.channel_whitelist and (
source_channel == -1 or user.channel == source_channel or user.channel == channel):
over_threshold = True
# Update if state changes needed
if user.deaf != deafen:
update = True
if user.mute != mute:
update = True
if channel >= 0 and user.channel != channel:
if 0 <= channel != user.channel:
update = True
if update:
index.add(user.session)
log.info('%ds > %ds: State transition for user %s (%d/%d) from mute %s -> %s / deaf %s -> %s | channel %d -> %d on server %d',
user.idlesecs, threshold, user.name, user.session, user.userid, user.mute, mute, user.deaf, deafen,
user.channel, channel, server.id())
log.info(
'%ds > %ds: State transition for user %s (%d/%d) from mute %s -> %s / deaf %s -> %s | channel %d -> %d on server %d',
user.idlesecs, threshold, user.name, user.session, user.userid, user.mute, mute, user.deaf,
deafen,
user.channel, channel, server.id())
break
if not over_threshold and user.session in self.affectedusers[sid]:
deafen = False
mute = False
@ -188,15 +180,15 @@ class idlemove(MumoModule):
index.remove(user.session)
log.info("Restore user %s (%d/%d) on server %d", user.name, user.session, user.userid, server.id())
update = True
if update:
user.deaf = deafen
user.mute = mute
user.channel = channel
server.setState(user)
#
#--- Server callback functions
# --- Server callback functions
#
def userDisconnected(self, server, state, context=None):
try:
@ -205,27 +197,35 @@ class idlemove(MumoModule):
index.remove(state.session)
except KeyError:
pass
def userStateChanged(self, server, state, context=None):
self.UpdateUserAutoAway(server, state)
def userConnected(self, server, state, context=None): pass # Unused callbacks
def userTextMessage(self, server, user, message, current=None): pass
def channelCreated(self, server, state, context=None): pass
def channelRemoved(self, server, state, context=None): pass
def channelStateChanged(self, server, state, context=None): pass
def userConnected(self, server, state, context=None):
pass # Unused callbacks
def userTextMessage(self, server, user, message, current=None):
pass
def channelCreated(self, server, state, context=None):
pass
def channelRemoved(self, server, state, context=None):
pass
def channelStateChanged(self, server, state, context=None):
pass
#
#--- Meta callback functions
# --- Meta callback functions
#
def started(self, server, context = None):
def started(self, server, context=None):
sid = server.id()
self.affectedusers[sid] = set()
self.log().debug('Handling server %d', sid)
def stopped(self, server, context = None):
def stopped(self, server, context=None):
sid = server.id()
self.affectedusers[sid] = set()
self.log().debug('Server %d gone', sid)

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- coding: utf-8
# Copyright (C) 2010-2011 Stefan Hacker <dd0t@users.sourceforge.net>
@ -35,24 +35,25 @@
# they connect regardless of which channel they were in when they left.
#
from mumo_module import (commaSeperatedIntegers,
MumoModule)
import re
from config import commaSeperatedIntegers
from mumo_module import MumoModule
class onjoin(MumoModule):
default_config = {'onjoin':(
('servers', commaSeperatedIntegers, []),
),
'all':(
('channel', int, 1),
),
lambda x: re.match('server_\d+', x):(
('channel', int, 1),
)
}
def __init__(self, name, manager, configuration = None):
default_config = {'onjoin': (
('servers', commaSeperatedIntegers, []),
),
'all': (
('channel', int, 1),
),
lambda x: re.match('server_\d+', x): (
('channel', int, 1),
)
}
def __init__(self, name, manager, configuration=None):
MumoModule.__init__(self, name, manager, configuration)
self.murmur = manager.getMurmurModule()
@ -60,39 +61,53 @@ class onjoin(MumoModule):
manager = self.manager()
log = self.log()
log.debug("Register for Server callbacks")
servers = self.cfg().onjoin.servers
if not servers:
servers = manager.SERVERS_ALL
manager.subscribeServerCallbacks(self, servers)
def disconnected(self): pass
def disconnected(self):
pass
#
#--- Server callback functions
# --- Server callback functions
#
def userConnected(self, server, state, context = None):
def userConnected(self, server, state, context=None):
log = self.log()
sid = server.id()
try:
scfg = getattr(self.cfg(), 'server_%d' % sid)
except AttributeError:
scfg = self.cfg().all
if state.channel != scfg.channel:
log.debug("Moving user '%s' from channel %d to %d on server %d", state.name, state.channel, scfg.channel, sid)
log.debug("Moving user '%s' from channel %d to %d on server %d", state.name, state.channel, scfg.channel,
sid)
state.channel = scfg.channel
try:
server.setState(state)
except self.murmur.InvalidChannelException:
log.error("Moving user '%s' failed, target channel %d does not exist on server %d", state.name, scfg.channel, sid)
def userDisconnected(self, server, state, context = None): pass
def userStateChanged(self, server, state, context = None): pass
def userTextMessage(self, server, user, message, current=None): pass
def channelCreated(self, server, state, context = None): pass
def channelRemoved(self, server, state, context = None): pass
def channelStateChanged(self, server, state, context = None): pass
log.error("Moving user '%s' failed, target channel %d does not exist on server %d", state.name,
scfg.channel, sid)
def userDisconnected(self, server, state, context=None):
pass
def userStateChanged(self, server, state, context=None):
pass
def userTextMessage(self, server, user, message, current=None):
pass
def channelCreated(self, server, state, context=None):
pass
def channelRemoved(self, server, state, context=None):
pass
def channelStateChanged(self, server, state, context=None):
pass

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- coding: utf-8
# Copyright (C) 2015 Stefan Hacker <dd0t@users.sourceforge.net>
@ -35,17 +35,19 @@
# entries to a user's context menu.
#
from mumo_module import (commaSeperatedIntegers,
MumoModule)
import cgi
from config import commaSeperatedIntegers
from mumo_module import MumoModule
class samplecontext(MumoModule):
default_config = {'samplecontext':(
('servers', commaSeperatedIntegers, []),
),
}
def __init__(self, name, manager, configuration = None):
default_config = {'samplecontext': (
('servers', commaSeperatedIntegers, []),
),
}
def __init__(self, name, manager, configuration=None):
MumoModule.__init__(self, name, manager, configuration)
self.murmur = manager.getMurmurModule()
self.action_poke_user = manager.getUniqueAction()
@ -56,19 +58,19 @@ class samplecontext(MumoModule):
manager = self.manager()
log = self.log()
log.debug("Register for Server callbacks")
servers = self.cfg().samplecontext.servers
if not servers:
servers = manager.SERVERS_ALL
manager.subscribeServerCallbacks(self, servers)
def disconnected(self): pass
#
#--- Server callback functions
# --- Server callback functions
#
def __on_poke_user(self, server, action, user, target):
assert action == self.action_poke_user
self.log().info(user.name + " poked " + target.name)
@ -78,7 +80,7 @@ class samplecontext(MumoModule):
assert action == self.action_info
self.log().info(user.name + " wants info on " + str(target));
server.sendMessage(user.session,
"<small><pre>" + cgi.escape(str(target)) + "</pre></small>")
"<small><pre>" + cgi.escape(str(target)) + "</pre></small>")
def __on_remove_this(self, server, action, user, target):
# This will remove the entry identified by "action" from
@ -86,7 +88,7 @@ class samplecontext(MumoModule):
self.log().info(user.name + " triggered removal")
self.