188 lines
5.1 KiB
Python
188 lines
5.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
import datetime
|
|
import logging
|
|
from collections import MutableMapping
|
|
|
|
logger = logging.getLogger('tsstats')
|
|
|
|
|
|
class Clients(MutableMapping):
|
|
'''
|
|
A high-level-interface to multiple Client-objects
|
|
'''
|
|
def __init__(self, ident_map=None, *args, **kwargs):
|
|
'''
|
|
Initialize a new Client-collection
|
|
|
|
:param ident_map: Identity-map (see :doc:`identmap`)
|
|
:type ident_map: dict
|
|
'''
|
|
self.ident_map = ident_map or {}
|
|
|
|
self.store = dict()
|
|
self.update(dict(*args, **kwargs))
|
|
|
|
def apply_events(self, events):
|
|
'''
|
|
Apply events to this Client-collection
|
|
|
|
:param events: list of events to apply
|
|
:type events: list
|
|
'''
|
|
for event in events:
|
|
# find corresponding client
|
|
client = self.setdefault(
|
|
event.identifier,
|
|
Client(self.ident_map.get(event.identifier, event.identifier))
|
|
)
|
|
if event.action == 'set_nick':
|
|
client.nick = event.arg
|
|
continue
|
|
if event.arg_is_client:
|
|
# if arg is client, replace identifier with Client-obj
|
|
event = event._replace(
|
|
arg=self.setdefault(event.arg, Client(event.arg))
|
|
)
|
|
client.__getattribute__(event.action)(event.arg)
|
|
|
|
def __add__(self, client):
|
|
'''
|
|
Add a Client to the collection
|
|
|
|
:param client: Client to add to the collection
|
|
:type id_or_uid: Client
|
|
'''
|
|
identifier = client.identifier
|
|
self.store[self.ident_map.get(identifier, identifier)] = client
|
|
return self
|
|
|
|
def __iter__(self):
|
|
'''
|
|
Yield all Client-objects from the collection
|
|
'''
|
|
return iter(self.store.keys())
|
|
|
|
def __getitem__(self, key):
|
|
return self.store[self.ident_map.get(key, key)]
|
|
|
|
def __delitem__(self, key):
|
|
del self.store[key]
|
|
|
|
def __len__(self):
|
|
return len(self.store)
|
|
|
|
def __setitem__(self, key, value):
|
|
self.store[self.ident_map.get(key, key)] = value
|
|
|
|
def __str__(self):
|
|
return str(list(map(str, self)))
|
|
|
|
|
|
class Client(object):
|
|
'''
|
|
Client provides high-level-access to a Teamspeak-Client
|
|
'''
|
|
|
|
def __init__(self, identifier, nick=None):
|
|
'''
|
|
Initialize a new Client
|
|
|
|
:param identifier: Identifier of the client
|
|
:type identifier: int or str
|
|
'''
|
|
# public
|
|
self.identifier = identifier
|
|
self._nick = nick
|
|
self.nick_history = set()
|
|
self.connected = 0
|
|
self.onlinetime = datetime.timedelta()
|
|
self.kicks = 0
|
|
self.pkicks = 0
|
|
self.bans = 0
|
|
self.pbans = 0
|
|
self.last_seen = None
|
|
# private
|
|
self._last_connect = 0
|
|
|
|
@property
|
|
def nick(self):
|
|
return self._nick
|
|
|
|
@nick.setter
|
|
def nick(self, new_nick):
|
|
if self._nick and new_nick != self._nick:
|
|
# add old nick to history
|
|
self.nick_history.add(self._nick)
|
|
# set new nick
|
|
self._nick = new_nick
|
|
|
|
def connect(self, timestamp):
|
|
'''
|
|
Connect client at `timestamp`
|
|
|
|
:param timestamp: time of connect
|
|
:type timestamp: int
|
|
'''
|
|
logger.debug('[%s] CONNECT %s', timestamp, self)
|
|
self.connected += 1
|
|
self._last_connect = timestamp
|
|
|
|
def disconnect(self, timestamp):
|
|
'''
|
|
Disconnect client at `timestamp`
|
|
|
|
:param timestamp: time of disconnect
|
|
:type timestamp: int
|
|
'''
|
|
logger.debug('[%s] DISCONNECT %s', timestamp, self)
|
|
if not self.connected:
|
|
logger.debug('^ disconnect before connect')
|
|
return
|
|
self.connected -= 1
|
|
session_time = timestamp - self._last_connect
|
|
logger.debug('Session lasted %s', session_time)
|
|
self.onlinetime += session_time
|
|
self.last_seen = timestamp
|
|
|
|
def kick(self, target):
|
|
'''
|
|
Let client kick `target`
|
|
|
|
:param target: client to kick
|
|
:type target: Client
|
|
'''
|
|
logger.debug('KICK %s -> %s', self, target)
|
|
target.pkicks += 1
|
|
self.kicks += 1
|
|
|
|
def ban(self, target):
|
|
'''
|
|
Let client ban `target`
|
|
|
|
:param target: client to ban
|
|
:type target: Client
|
|
'''
|
|
logger.debug('BAN %s -> %s', self, target)
|
|
target.pbans += 1
|
|
self.bans += 1
|
|
|
|
def __str__(self):
|
|
return u'<{}, {}>'.format(self.identifier, self.nick)
|
|
|
|
def __repr__(self):
|
|
return self.__str__()
|
|
|
|
def __eq__(self, other):
|
|
if type(other) != Client:
|
|
raise NotImplemented
|
|
return self.identifier == other.identifier \
|
|
and self.nick == other.nick \
|
|
and self.nick_history == other.nick_history \
|
|
and self.connected == other.connected \
|
|
and self.onlinetime == other.onlinetime \
|
|
and self.kicks == other.kicks \
|
|
and self.pkicks == other.pkicks \
|
|
and self.bans == other.bans \
|
|
and self.pbans == other.pbans \
|
|
and self.last_seen == other.last_seen
|