""" These managers handles the """ import itertools from django.db import models from django.contrib.contenttypes.models import ContentType from src.utils.utils import is_iter class CommError(Exception): "Raise by comm system, to allow feedback to player when caught." pass # helper function def to_object(inp, objtype='player'): """ Locates the object related to the given playername or channel key. If input was already the correct object, return it. inp - the input object/string objtype - 'player' or 'channel' """ from src.players.models import PlayerDB if objtype == 'player': if type(inp) == PlayerDB: return inp if hasattr(inp, 'player'): return inp.player else: umatch = PlayerDB.objects.filter(user__username__iexact=inp) if umatch: return umatch[0] elif objtype == 'external': from src.comms.models import ExternalChannelConnection if type (inp) == ExternalChannelConnection: return inp umatch = ExternalChannelConnection.objects.filter(db_key=inp) if umatch: return umatch[0] else: # have to import this way to avoid circular imports from src.comms.models import Channel #= ContentType.objects.get(app_label="comms", # model="channel").model_class() if type(inp) == Channel: return inp cmatch = Channel.objects.filter(db_key__iexact=inp) if cmatch: return cmatch[0] return None # # Msg manager # class MsgManager(models.Manager): """ This MsgManager implements methods for searching and manipulating Messages directly from the database. These methods will all return database objects (or QuerySets) directly. A Message represents one unit of communication, be it over a Channel or via some form of in-game mail system. Like an e-mail, it always has a sender and can have any number of receivers (some of which may be Channels). Evennia-specific: get_message_by_id get_messages_by_sender get_messages_by_receiver get_messages_by_channel text_search message_search (equivalent to ev.search_messages) """ def get_message_by_id(self, idnum): "Retrieve message by its id." try: idnum = int(idnum) return self.get(id=id) except Exception: return None def get_messages_by_sender(self, player): """ Get all messages sent by one player """ player = to_object(player, objtype='player') if not player: return None return self.filter(db_sender=player).exclude(db_hide_from_sender=True) def get_messages_by_receiver(self, receiver): """ Get all messages sent to one player """ receiver = to_object(receiver) if not receiver: return None return [msg for msg in self.all() if receiver in msg.receivers and receiver not in msg.hide_from_receivers] def get_messages_by_channel(self, channel): """ Get all messages sent to one channel """ channel = to_object(channel, objtype='channel') if not channel: return None return [msg for msg in self.all() if channel in msg.channels and channel not in msg.hide_from_channels] #TODO add search limited by send_times def text_search(self, searchstring, filterdict=None): """ Returns all messages that contain the matching search string. To avoid too many results, and also since this can be a very computing- heavy operation, it's recommended to be filtered by at least channel or sender/receiver. searchstring - string to search for filterdict - {'channels':[list], 'senders':[list], 'receivers':[list]} lists can contain either the name/keys of the objects or the actual objects to filter by. """ if filterdict: # obtain valid objects for all filters channels = [chan for chan in [to_object(chan, objtype='channel') for chan in filterdict.get('channels',[])] if chan] senders = [sender for sender in [to_object(sender) for sender in filterdict.get('senders',[])] if sender] receivers = [receiver for receiver in [to_object(receiver) for receiver in filterdict.get('receivers',[])] if receiver] # filter the messages lazily using the filter objects msgs = [] for sender in senders: msgs = list(sender.message_set.filter( db_message__icontains=searchstring)) for receiver in receivers: rec_msgs = receiver.message_set.filter( db_message__icontains=searchstring) if msgs: msgs = [msg for msg in rec_msgs if msg in msgs] else: msgs = rec_msgs for channel in channels: chan_msgs = list(channel.message_set.filter( db_message__icontains=searchstring)) if msgs: msgs = [msg for msg in chan_msgs if msg in msgs] else: msgs = chan_msgs return list(set(msgs)) return list(self.all().filter(db_message__icontains=searchstring)) def message_search(self, sender=None, receiver=None, channel=None, freetext=None, dbref=None): """ Search the message database for particular messages. At least one of the arguments must be given to do a search. sender - get messages sent by a particular player receiver - get messages received by a certain player or players channel - get messages sent to a particular channel or channels freetext - Search for a text string in a message. NOTE: This can potentially be slow, so make sure to supply one of the other arguments to limit the search. dbref - (int) the exact database id of the message. This will override all other search crieteria since it's unique and always gives a list with only one match. """ if dbref: return self.filter(id=dbref) if freetext: if sender: sender = [sender] if receiver and not is_iter(receiver): receiver = [receiver] if channel and not is_iter(channel): channel = [channel] filterdict = {"senders":sender, "receivers":receiver, "channels":channel} return self.textsearch(freetext, filterdict) msgs = [] if sender: msgs = self.get_messages_by_sender(sender) if receiver: rec_msgs = self.get_messages_by_receiver(receiver) if msgs: msgs = [msg for msg in rec_msgs if msg in msgs] else: msgs = rec_msgs if channel: chan_msgs = self.get_messaqge_by_channel(channel) if msgs: msgs = [msg for msg in chan_msgs if msg in msgs] else: msgs = chan_msgs return msgs # # Channel manager # class ChannelManager(models.Manager): """ This ChannelManager implements methods for searching and manipulating Channels directly from the database. These methods will all return database objects (or QuerySets) directly. A Channel is an in-game venue for communication. It's essentially representation of a re-sender: Users sends Messages to the Channel, and the Channel re-sends those messages to all users subscribed to the Channel. Evennia-specific: get_all_channels get_channel del_channel get_all_connections channel_search (equivalent to ev.search_channel) """ def get_all_channels(self): """ Returns all channels in game. """ return self.all() def get_channel(self, channelkey): """ Return the channel object if given its key. Also searches its aliases. """ # first check the channel key channels = self.filter(db_key__iexact=channelkey) if not channels: # also check aliases channels = [channel for channel in self.all() if channelkey in channel.aliases] if channels: return channels[0] return None def del_channel(self, channelkey): """ Delete channel matching channelkey. Also cleans up channelhandler. """ channels = self.filter(db_key__iexact=channelkey) if not channels: # no aliases allowed for deletion. return False for channel in channels: channel.delete() from src.comms.channelhandler import CHANNELHANDLER CHANNELHANDLER.update() return None def get_all_connections(self, channel): """ Return the connections of all players listening to this channel """ # import here to avoid circular imports #from src.comms.models import PlayerChannelConnection PlayerChannelConnection = ContentType.objects.get(app_label="comms", model="playerchannelconnection").model_class() ExternalChannelConnection = ContentType.objects.get(app_label="comms", model="externalchannelconnection").model_class() return itertools.chain(PlayerChannelConnection.objects.get_all_connections(channel), ExternalChannelConnection.objects.get_all_connections(channel)) def channel_search(self, ostring): """ Search the channel database for a particular channel. ostring - the key or database id of the channel. """ channels = [] try: # try an id match first dbref = int(ostring.strip('#')) channels = self.filter(id=dbref) except Exception: pass if not channels: # no id match. Search on the key. channels = self.filter(db_key__iexact=ostring) if not channels: # still no match. Search by alias. channels = [channel for channel in self.all() if ostring.lower in [a.lower for a in channel.aliases]] return channels # # PlayerChannelConnection manager # class PlayerChannelConnectionManager(models.Manager): """ This PlayerChannelConnectionManager implements methods for searching and manipulating PlayerChannelConnections directly from the database. These methods will all return database objects (or QuerySets) directly. A PlayerChannelConnection defines a user's subscription to an in-game channel - deleting the connection object will disconnect the player from the channel. Evennia-specific: get_all_player_connections has_connection get_all_connections create_connection break_connection """ def get_all_player_connections(self, player): "Get all connections that the given player has." player = to_object(player) return self.filter(db_player=player) def has_connection(self, player, channel): "Checks so a connection exists player<->channel" player = to_object(player) channel = to_object(channel, objtype="channel") if player and channel: return self.filter(db_player=player).filter(db_channel=channel).count() > 0 return False def get_all_connections(self, channel): """ Get all connections for a channel """ channel = to_object(channel, objtype='channel') return self.filter(db_channel=channel) def create_connection(self, player, channel): """ Connect a player to a channel. player and channel can be actual objects or keystrings. """ player = to_object(player) channel = to_object(channel, objtype='channel') if not player or not channel: raise CommError("NOTFOUND") new_connection = self.model(db_player=player, db_channel=channel) new_connection.save() return new_connection def break_connection(self, player, channel): "Remove link between player and channel" player = to_object(player) channel = to_object(channel, objtype='channel') if not player or not channel: raise CommError("NOTFOUND") conns = self.filter(db_player=player).filter(db_channel=channel) for conn in conns: conn.delete() class ExternalChannelConnectionManager(models.Manager): """ This ExternalChannelConnectionManager implements methods for searching and manipulating HelpEntries directly from the database. These methods will all return database objects (or QuerySets) directly. An ExternalChannelConnetion describes the connection between an in-game channel and some external source, such as an IRC or IMC channel. Evennia-specific: get_all_external_connections has_connection get_all_connections create_connection break_connection """ def get_all_external_connections(self, external): "Get all connections that the given as external." external = to_object(external, objtype='external') return self.filter(db_external_key=external) def has_connection(self, external, channel): "Checks so a connection exists external<->channel" external = to_object(external, objtype='external') channel = to_object(channel, objtype="channel") if external and channel: return self.filter(db_external_key=external).filter(db_channel=channel).count() > 0 return False def get_all_connections(self, channel): """ Get all connections for a channel """ channel = to_object(channel, objtype='channel') return self.filter(db_channel=channel) def create_connection(self, external, channel, config=""): """ Connect a external to a channel. external and channel can be actual objects or keystrings. """ channel = to_object(channel, objtype='channel') if not channel: raise CommError("NOTFOUND") new_connection = self.model(db_external_key=external, db_channel=channel, db_external_config=config) new_connection.save() return new_connection def break_connection(self, external, channel): "Remove link between external and channel" external = to_object(external) channel = to_object(channel, objtype='channel') if not external or not channel: raise CommError("NOTFOUND") conns = self.filter(db_external_key=external).filter(db_channel=channel) for conn in conns: conn.delete()