diff --git a/evennia/comms/admin.py b/evennia/comms/admin.py index b972ed3da4..97eabe8a85 100644 --- a/evennia/comms/admin.py +++ b/evennia/comms/admin.py @@ -1,7 +1,7 @@ -# -# This sets up how models are displayed -# in the web admin interface. -# +""" +This defines how Comm models are displayed in the web admin interface. + +""" from django.contrib import admin from evennia.comms.models import ChannelDB @@ -9,14 +9,26 @@ from evennia.typeclasses.admin import AttributeInline, TagInline class ChannelAttributeInline(AttributeInline): + """ + Inline display of Channel Attribute - experimental + + """ model = ChannelDB.db_attributes.through class ChannelTagInline(TagInline): + """ + Inline display of Channel Tags - experimental + + """ model = ChannelDB.db_tags.through class MsgAdmin(admin.ModelAdmin): + """ + Defines display for Msg objects + + """ list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers', 'db_channels', 'db_message', 'db_lock_storage') list_display_links = ("id",) @@ -30,6 +42,10 @@ class MsgAdmin(admin.ModelAdmin): class ChannelAdmin(admin.ModelAdmin): + """ + Defines display for Channel objects + + """ inlines = [ChannelTagInline, ChannelAttributeInline] list_display = ('id', 'db_key', 'db_lock_storage', "subscriptions") list_display_links = ("id", 'db_key') @@ -43,7 +59,13 @@ class ChannelAdmin(admin.ModelAdmin): ) def subscriptions(self, obj): - "Helper method to get subs from a channel" + """ + Helper method to get subs from a channel. + + Args: + obj (Channel): The channel to get subs from. + + """ return ", ".join([str(sub) for sub in obj.db_subscriptions.all()]) admin.site.register(ChannelDB, ChannelAdmin) diff --git a/evennia/comms/channelhandler.py b/evennia/comms/channelhandler.py index 97cf80ef04..27b3f5222b 100644 --- a/evennia/comms/channelhandler.py +++ b/evennia/comms/channelhandler.py @@ -1,28 +1,29 @@ """ -The channel handler handles the stored set of channels -and how they are represented against the cmdhandler. +The channel handler, accessed from this module as CHANNEL_HANDLER is a +singleton that handles the stored set of channels and how they are +represented against the cmdhandler. -If there is a channel named 'newbie', we want to be able -to just write +If there is a channel named 'newbie', we want to be able to just write -> newbie Hello! + newbie Hello! -For this to work, 'newbie', the name of the channel, must -be identified by the cmdhandler as a command name. The -channelhandler stores all channels as custom 'commands' -that the cmdhandler can import and look through. +For this to work, 'newbie', the name of the channel, must be +identified by the cmdhandler as a command name. The channelhandler +stores all channels as custom 'commands' that the cmdhandler can +import and look through. -Warning - channel names take precedence over command names, -so make sure to not pick clashing channel names. +> Warning - channel names take precedence over command names, so make +sure to not pick clashing channel names. -Unless deleting a channel you normally don't need to bother about -the channelhandler at all - the create_channel method handles the update. +Unless deleting a channel you normally don't need to bother about the +channelhandler at all - the create_channel method handles the update. To delete a channel cleanly, delete the channel object, then call update() on the channelhandler. Or use Channel.objects.delete() which does this for you. """ + from evennia.comms.models import ChannelDB from evennia.commands import cmdset, command @@ -37,6 +38,7 @@ class ChannelCommand(command.Command): This is a channel. If you have subscribed to it, you can send to it by entering its name or alias, followed by the text you want to send. + """ # this flag is what identifies this cmd as a channel cmd # and branches off to the system send-to-channel command @@ -100,17 +102,31 @@ class ChannelHandler(object): self.cached_cmdsets = {} def __str__(self): - "Returns the string representation of the handler" + """ + Returns the string representation of the handler + + """ return ", ".join(str(cmd) for cmd in self.cached_channel_cmds) def clear(self): """ Reset the cache storage. + """ self.cached_channel_cmds = [] def _format_help(self, channel): - "builds a doc string" + """ + Builds an automatic doc string for the channel. + + Args: + channel (Channel): Source of help info. + + Returns: + doc (str): The docstring for the channel. + + """ + key = channel.key aliases = channel.aliases.all() ustring = "%s " % key.lower() + "".join(["\n %s " % alias.lower() for alias in aliases]) @@ -129,9 +145,17 @@ class ChannelHandler(object): def add_channel(self, channel): """ Add an individual channel to the handler. This should be - called whenever a new channel is created. To - remove a channel, simply delete the channel object - and run self.update on the handler. + called whenever a new channel is created. + + Args: + channel (Channel): The channel to add. + + Notes: + To remove a channel, simply delete the channel object and + run self.update on the handler. This should usually be + handled automatically by one of the deletion methos of + the Channel itself. + """ # map the channel to a searchable command cmd = ChannelCommand(key=channel.key.strip().lower(), @@ -146,7 +170,9 @@ class ChannelHandler(object): def update(self): """ - Updates the handler completely. + Updates the handler completely, including removing old removed + Channel objects. This must be called after deleting a Channel. + """ self.cached_channel_cmds = [] self.cached_cmdsets = {} @@ -157,6 +183,15 @@ class ChannelHandler(object): """ Retrieve cmdset for channels this source_object has access to send to. + + Args: + source_object (Object): An object subscribing to one + or more channels. + + Returns: + cmdsets (list): The Channel-Cmdsets `source_object` has + access to. + """ if source_object in self.cached_cmdsets: return self.cached_cmdsets[source_object] diff --git a/evennia/comms/comms.py b/evennia/comms/comms.py index 1196cce81e..00c8097754 100644 --- a/evennia/comms/comms.py +++ b/evennia/comms/comms.py @@ -1,8 +1,8 @@ """ -Default Typeclass for Comms. +Base typeclass for in-game Channels. -See objects.objects for more information on Typeclassing. """ + from django.conf import settings from evennia.typeclasses.models import TypeclassBase from evennia.comms.models import Msg, TempMsg, ChannelDB @@ -13,8 +13,9 @@ from evennia.utils.utils import make_iter class DefaultChannel(ChannelDB): """ - This is the base class for all Comms. Inherit from this to create different - types of communication channels. + This is the base class for all Channel Comms. Inherit from this to + create different types of communication channels. + """ # typeclass setup __metaclass__ = TypeclassBase @@ -25,6 +26,7 @@ class DefaultChannel(ChannelDB): Called by the typeclass system the very first time the channel is saved to the database. Generally, don't overload this but the hooks called by this method. + """ self.at_channel_creation() @@ -51,6 +53,7 @@ class DefaultChannel(ChannelDB): def at_channel_creation(self): """ Called once, when the channel is first created. + """ pass @@ -133,15 +136,19 @@ class DefaultChannel(ChannelDB): def access(self, accessing_obj, access_type='listen', default=False): """ Determines if another object has permission to access. - accessing_obj - object trying to access this one - access_type - type of access sought - default - what to return if no lock of access_type was found + + Args: + accessing_obj (Object): Object trying to access this one. + access_type (str): Type of access sought. + default (bool): What to return if no lock of access_type was found + """ return self.locks.check(accessing_obj, access_type=access_type, default=default) def delete(self): """ - Deletes channel while also cleaning up channelhandler + Deletes channel while also cleaning up channelhandler. + """ self.attributes.clear() self.aliases.clear() @@ -153,6 +160,15 @@ class DefaultChannel(ChannelDB): sender_strings=None, external=False): """ Generates the formatted string sent to listeners on a channel. + + Args: + msg (str): Message to send. + emit (bool, optional): In emit mode the message is not associated + with a specific sender name. + prefix (bool, optional): Prefix `msg` with a text given by `self.channel_prefix`. + sender_strings (list, optional): Used by bots etc, one string per external sender. + external (bool, optional): If this is an external sender or not. + """ if sender_strings or external: body = self.format_external(msg, sender_strings, emit=emit) @@ -167,6 +183,11 @@ class DefaultChannel(ChannelDB): """ Method for grabbing all listeners that a message should be sent to on this channel, and sending them a message. + + msg (str): Message to distribute. + online (bool): Only send to receivers who are actually online + (not currently used): + """ # get all players connected to this channel and send to them for entity in self.subscriptions.all(): @@ -185,26 +206,34 @@ class DefaultChannel(ChannelDB): done before calling this method. The optional keywords are not used if persistent is False. - msgobj - a Msg/TempMsg instance or a message string. If one of the - former, the remaining keywords will be ignored. If a string, - this will either be sent as-is (if persistent=False) or it - will be used together with header and senders keywords to - create a Msg instance on the fly. - senders - an object, player or a list of objects or players. - Optional if persistent=False. - sender_strings - Name strings of senders. Used for external - connections where the sender is not a player or object. When - this is defined, external will be assumed. - external - Treat this message agnostic of its sender. - persistent (default False) - ignored if msgobj is a Msg or TempMsg. + Args: + msgobj (Msg, TempMsg or str): If a Msg/TempMsg, the remaining + keywords will be ignored (since the Msg/TempMsg object already + has all the data). If a string, this will either be sent as-is + (if persistent=False) or it will be used together with `header` + and `senders` keywords to create a Msg instance on the fly. + header (str, optional): A header for building the message. + senders (Object, Player or list, optional): Optional if persistent=False, used + to build senders for the message. + sender_strings (list, optional): Name strings of senders. Used for external + connections where the sender is not a player or object. + When this is defined, external will be assumed. + persistent (bool, optional): Ignored if msgobj is a Msg or TempMsg. If True, a Msg will be created, using header and senders keywords. If False, other keywords will be ignored. - online (bool) - If this is set true, only messages people who are + online (bool, optional) - If this is set true, only messages people who are online. Otherwise, messages all players connected. This can make things faster, but may not trigger listeners on players that are offline. - emit (bool) - Signals to the message formatter that this message is + emit (bool, optional) - Signals to the message formatter that this message is not to be directly associated with a name. + external (bool, optional): Treat this message as being + agnostic of its sender. + + Returns: + success (bool): Returns `True` if message sending was + successful, `False` otherwise. + """ if senders: senders = make_iter(senders) @@ -238,6 +267,12 @@ class DefaultChannel(ChannelDB): def tempmsg(self, message, header=None, senders=None): """ A wrapper for sending non-persistent messages. + + Args: + message (str): Message to send. + header (str, optional): Header of message to send. + senders (Object or list, optional): Senders of message to send. + """ self.msg(message, senders=senders, header=header, persistent=False) @@ -247,28 +282,57 @@ class DefaultChannel(ChannelDB): def channel_prefix(self, msg=None, emit=False): """ - How the channel should prefix itself for users. Return a string. + Hook method. How the channel should prefix itself for users. + + Args: + msg (str, optional): Prefix text + emit (bool, optional): Switches to emit mode, which usually + means to ignore any sender information. Not used by default. + + Returns: + prefix (str): The created channel prefix. + """ return '[%s] ' % self.key def format_senders(self, senders=None): """ - Function used to format a list of sender names. + Hook method. Function used to format a list of sender names. + + Args: + senders (list): Sender object names. + + Returns: + formatted_list (str): The list of names formatted appropriately. + + Notes: + This function exists separately so that external sources + can use it to format source names in the same manner as + normal object/player names. - This function exists separately so that external sources can use - it to format source names in the same manner as normal object/player - names. """ if not senders: return '' return ', '.join(senders) - def pose_transform(self, msg, sender_string): + def pose_transform(self, msgobj, sender_string): """ - Detects if the sender is posing, and modifies the message accordingly. + Hook method. Detects if the sender is posing, and modifies the + message accordingly. + + Args: + msgob (Msg or TempMsg): The message to analyze for a pose. + sender_string (str): The name of the sender/poser. + + Returns: + string (str): A message that combines the `sender_string` + component with `msg` in different ways depending on if a + pose was performed or not (this must be analyzed by the + hook). + """ pose = False - message = msg.message + message = msgobj.message message_start = message.lstrip() if message_start.startswith((':', ';')): pose = True @@ -281,91 +345,131 @@ class DefaultChannel(ChannelDB): else: return '%s: %s' % (sender_string, message) - def format_external(self, msg, senders, emit=False): + def format_external(self, msgobj, senders, emit=False): """ - Used for formatting external messages. This is needed as a separate - operation because the senders of external messages may not be in-game - objects/players, and so cannot have things like custom user - preferences. + Hook method. Used for formatting external messages. This is + needed as a separate operation because the senders of external + messages may not be in-game objects/players, and so cannot + have things like custom user preferences. + + Args: + msgobj (Msg or TempMsg): The message to send. + senders (list): Strings, one per sender. + emit (bool, optional): A sender-agnostic message or not. + + Returns: + transformed (str): A formatted string. - senders should be a list of strings, each containing a sender. - msg should contain the body of the message to be sent. """ - if not senders: - emit = True - if emit: - return msg.message + if emit or not senders: + return msgobj.message senders = ', '.join(senders) - return self.pose_transform(msg, senders) + return self.pose_transform(msgobj, senders) - def format_message(self, msg, emit=False): + def format_message(self, msgobj, emit=False): """ - Formats a message body for display. + Hook method. Formats a message body for display. + + Args: + msgob (Msg or TempMsg): The message object to send. + emit (bool, optional): The message is agnostic of senders. + + Returns: + transformed (str): The formatted message. - If emit is True, it means the message is intended to be posted detached - from an identity. """ # We don't want to count things like external sources as senders for # the purpose of constructing the message string. - senders = [sender for sender in msg.senders if hasattr(sender, 'key')] + senders = [sender for sender in msgobj.senders if hasattr(sender, 'key')] if not senders: emit = True if emit: - return msg.message + return msgobj.message else: - senders = [sender.key for sender in msg.senders] + senders = [sender.key for sender in msgobj.senders] senders = ', '.join(senders) - return self.pose_transform(msg, senders) + return self.pose_transform(msgobj, senders) def pre_join_channel(self, joiner): """ - Run right before a channel is joined. If this returns a false value, - channel joining is aborted. + Hook method. Runs right before a channel is joined. If this + returns a false value, channel joining is aborted. + + Args: + joiner (object): The joining object. + + Returns: + should_join (bool): If `False`, channel joining is aborted. + """ return True def post_join_channel(self, joiner): """ - Run right after an object or player joins a channel. + Hook method. Runs right after an object or player joins a channel. + + Args: + joiner (object): The joining object. + """ - return True + pass def pre_leave_channel(self, leaver): """ - Run right before a user leaves a channel. If this returns a false + Hook method. Runs right before a user leaves a channel. If this returns a false value, leaving the channel will be aborted. + + Args: + joiner (object): The joining object. + + Returns: + should_leave (bool): If `False`, channel parting is aborted. + """ return True def post_leave_channel(self, leaver): """ - Run right after an object or player leaves a channel. + Hook method. Runs right after an object or player leaves a channel. + + Args: + joiner (object): The joining object. + """ pass def pre_send_message(self, msg): """ - Run before a message is sent to the channel. - - This should return the message object, after any transformations. + Hook method. Runs before a message is sent to the channel and + should return the message object, after any transformations. If the message is to be discarded, return a false value. + + Args: + msg (Msg or TempMsg): Message to send. + + Returns: + result (Msg, TempMsg or bool): If False, abort send. + """ return msg def post_send_message(self, msg): """ - Run after a message is sent to the channel. + Hook method. Run after a message is sent to the channel. + + Args: + msg (Msg or TempMsg): Message sent. + """ pass def at_init(self): """ - This is always called whenever this channel is initiated -- - that is, whenever it its typeclass is cached from memory. This - happens on-demand first time the channel is used or activated - in some way after being created but also after each server - restart or reload. + Hook method. This is always called whenever this channel is + initiated -- that is, whenever it its typeclass is cached from + memory. This happens on-demand first time the channel is used + or activated in some way after being created but also after + each server restart or reload. + """ pass - - diff --git a/evennia/comms/managers.py b/evennia/comms/managers.py index f633231fc5..9db1edca11 100644 --- a/evennia/comms/managers.py +++ b/evennia/comms/managers.py @@ -1,5 +1,7 @@ """ -These managers handles the +These managers define helper methods for accessing the database from +Comm system components. + """ from django.db import models @@ -17,7 +19,9 @@ _SESSIONS = None class CommError(Exception): - "Raise by comm system, to allow feedback to player when caught." + """ + Raised by comm system, to allow feedback to player when caught. + """ pass @@ -27,9 +31,17 @@ class CommError(Exception): def dbref(dbref, reqhash=True): """ - Valid forms of dbref (database reference number) - are either a string '#N' or an integer N. - Output is the integer part. + Valid forms of dbref (database reference number) are either a + string '#N' or an integer N. + + Args: + dbref (int or str): A possible dbref to check syntactically. + reqhash (bool): Require an initial hash `#` to accept. + + Returns: + is_dbref (int or None): The dbref integer part if a valid + dbref, otherwise `None`. + """ if reqhash and not (isinstance(dbref, basestring) and dbref.startswith("#")): return None @@ -44,7 +56,19 @@ def dbref(dbref, reqhash=True): def identify_object(inp): - "identify if an object is a player or an object; return its database model" + """ + Helper function. Identifies if an object is a player or an object; + return its database model + + Args: + inp (any): Entity to be idtified. + + Returns: + identified (tuple): This is a tuple with (`inp`, identifier) + where `identifier` is one of "player", "object", "channel", + "string", "dbref" or None. + + """ if hasattr(inp, "__dbclass__"): clsname = inp.__dbclass__.__name__ if clsname == "PlayerDB": @@ -63,11 +87,16 @@ def identify_object(inp): 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' + Locates the object related to the given playername or channel key. + If input was already the correct object, return it. + + Args: + inp (any): The input object/string + objtype (str): Either 'player' or 'channel'. + + Returns: + obj (object): The correct object related to `inp`. + """ obj, typ = identify_object(inp) if typ == objtype: @@ -104,46 +133,68 @@ def to_object(inp, objtype='player'): class MsgManager(models.Manager): """ - This MsgManager implements methods for searching - and manipulating Messages directly from the database. + This MsgManager implements methods for searching and manipulating + Messages directly from the database. - These methods will all return database objects - (or QuerySets) directly. + 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 evennia.search_messages) """ - def identify_object(self, obj): - "method version for easy access" - return identify_object(obj) + def identify_object(self, inp): + """ + Wrapper to identify_object if accessing via the manager directly. + + Args: + inp (any): Entity to be idtified. + + Returns: + identified (tuple): This is a tuple with (`inp`, identifier) + where `identifier` is one of "player", "object", "channel", + "string", "dbref" or None. + + """ + return identify_object(inp) def get_message_by_id(self, idnum): - "Retrieve message by its id." + """ + Retrieve message by its id. + + Args: + idnum (int or str): The dbref to retrieve. + + Returns: + message (Msg): The message. + + """ try: return self.get(id=self.dbref(idnum, reqhash=False)) except Exception: return None - def get_messages_by_sender(self, obj, exclude_channel_messages=False): + def get_messages_by_sender(self, sender, exclude_channel_messages=False): """ Get all messages sent by one entity - this could be either a player or an object - only_non_channel: only return messages -not- aimed at a channel - (e.g. private tells) + Args: + sender (Player or Object): The sender of the message. + exclude_channel_messages (bool, optional): Only return messages + not aimed at a channel (that is, private tells for example) + + Returns: + messages (list): List of matching messages + + Raises: + CommError: For incorrect sender types. + """ - obj, typ = identify_object(obj) + obj, typ = identify_object(sender) if exclude_channel_messages: # explicitly exclude channel recipients if typ == 'player': @@ -163,11 +214,21 @@ class MsgManager(models.Manager): else: raise CommError - def get_messages_by_receiver(self, obj): + def get_messages_by_receiver(self, recipient): """ - Get all messages sent to one give recipient + Get all messages sent to one given recipient. + + Args: + recipient (Object, Player or Channel): The recipient of the messages to search for. + + Returns: + messages (list): Matching messages. + + Raises: + CommError: If the `recipient` is not of a valid type. + """ - obj, typ = identify_object(obj) + obj, typ = identify_object(recipient) if typ == 'player': return list(self.filter(db_receivers_players=obj).exclude(db_hide_from_players=obj)) elif typ == 'object': @@ -179,23 +240,36 @@ class MsgManager(models.Manager): def get_messages_by_channel(self, channel): """ - Get all messages sent to one channel + Get all persistent messages sent to one channel. + + Args: + channel (Channel): The channel to find messages for. + + Returns: + messages (list): Persistent Msg objects saved for this channel. + """ return self.filter(db_receivers_channels=channel).exclude(db_hide_from_channels=channel) def message_search(self, sender=None, receiver=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. + Search the message database for particular messages. At least + one of the arguments must be given to do a search. + + Args: + sender (Object or Player, optional): Get messages sent by a particular player or object + receiver (Object, Player or Channel, optional): Get messages + received by a certain player,object or channel + freetext (str): 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 criteria since it's unique and + always gives only one match. + + Returns: + messages (list or Msg): A list of message matches or a single match if `dbref` was given. - sender - get messages sent by a particular player or object - receiver - get messages received by a certain player,object or channel - 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 criteria since it's unique and - always gives a list with only one match. """ # unique msg id if dbref: @@ -241,28 +315,26 @@ class MsgManager(models.Manager): class ChannelDBManager(TypedObjectManager): """ - This ChannelManager implements methods for searching - and manipulating Channels directly from the database. + This ChannelManager implements methods for searching and + manipulating Channels directly from the database. - These methods will all return database objects - (or QuerySets) directly. + 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(channel) - get_subscriptions(player) - channel_search (equivalent to evennia.search_channel) + 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. """ @returns_typeclass_list def get_all_channels(self): """ - Returns all channels in game. + Get all channels. + + Returns: + channels (list): All channels in game. + """ return self.all() @@ -271,6 +343,13 @@ class ChannelDBManager(TypedObjectManager): """ Return the channel object if given its key. Also searches its aliases. + + Args: + channelkey (str): Channel key to search for. + + Returns: + channel (Channel or None): A channel match. + """ # first check the channel key channels = self.filter(db_key__iexact=channelkey) @@ -283,15 +362,22 @@ class ChannelDBManager(TypedObjectManager): return None @returns_typeclass_list - def get_subscriptions(self, entity): + def get_subscriptions(self, subscriber): """ - Return all channels a given player is subscribed to + Return all channels a given entity is subscribed to. + + Args: + subscriber (Object or Player): The one subscribing. + + Returns: + subscriptions (list): Channel subscribed to. + """ - clsname = entity.__dbclass__.__name__ + clsname = subscriber.__dbclass__.__name__ if clsname == "PlayerDB": - return entity.subscription_set.all() + return subscriber.subscription_set.all() if clsname == "ObjectDB": - return entity.object_subscription_set.all() + return subscriber.object_subscription_set.all() return [] @returns_typeclass_list @@ -299,8 +385,11 @@ class ChannelDBManager(TypedObjectManager): """ Search the channel database for a particular channel. - ostring - the key or database id of the channel. - exact - require an exact key match (still not case sensitive) + Args: + ostring (str): The key or database id of the channel. + exact (bool, optional): Require an exact (but not + case sensitive) match. + """ channels = [] if not ostring: return channels @@ -324,6 +413,9 @@ class ChannelDBManager(TypedObjectManager): return channels class ChannelManager(ChannelDBManager, TypeclassManager): + """ + Wrapper to group the typeclass manager to a consistent name. + """ pass diff --git a/evennia/comms/models.py b/evennia/comms/models.py index 618050809e..48b782be20 100644 --- a/evennia/comms/models.py +++ b/evennia/comms/models.py @@ -1,22 +1,21 @@ """ -Models for the comsystem. The Commsystem is intended to be -used by Players (thematic IC communication is probably -best handled by custom commands instead). +Models for the in-game communication system. -The comm system could take the form of channels, but can also -be adopted for storing tells or in-game mail. +The comm system could take the form of channels, but can also be +adopted for storing tells or in-game mail. -The comsystem's main component is the Message (Msg), which -carries the actual information between two parties. -Msgs are stored in the database and usually not -deleted. -A Msg always have one sender (a user), but can have -any number targets, both users and channels. +The comsystem's main component is the Message (Msg), which carries the +actual information between two parties. Msgs are stored in the +database and usually not deleted. A Msg always have one sender (a +user), but can have any number targets, both users and channels. -Channels are central objects that act as targets for -Msgs. Players can connect to channels by use of a -ChannelConnect object (this object is necessary to easily -be able to delete connections on the fly). +For non-persistent (and slightly faster) use one can also use the +TempMsg, which mimics the Msg API but without actually saving to the +database. + +Channels are central objects that act as targets for Msgs. Players can +connect to channels by use of a ChannelConnect object (this object is +necessary to easily be able to delete connections on the fly). """ from django.conf import settings @@ -47,16 +46,22 @@ class Msg(SharedMemoryModel): A single message. This model describes all ooc messages sent in-game, both to channels and between players. - The Msg class defines the following properties: - sender - sender of message - receivers - list of target objects for message - channels - list of channels message was sent to - message - the text being sent - date_sent - time message was sent - hide_from_sender - bool if message should be hidden from sender - hide_from_receivers - list of receiver objects to hide message from - hide_from_channels - list of channels objects to hide message from - permissions - perm strings + The Msg class defines the following database fields (all + accessed via specific handler methods): + + - db_sender_players: Player senders + - db_sender_objects: Object senders + - db_sender_external: External senders (defined as string names) + - db_receivers_players: Receiving players + - db_receivers_objects: Receiving objects + - db_receivers_channels: Receiving channels + - db_header: Header text + - db_message: The actual message text + - db_date_sent: time message was sent + - db_hide_from_sender: bool if message should be hidden from sender + - db_hide_from_receivers: list of receiver objects to hide message from + - db_hide_from_channels: list of channels objects to hide message from + - db_lock_storage: Internal storage of lock strings. """ # @@ -160,7 +165,13 @@ class Msg(SharedMemoryModel): senders = property(__senders_get, __senders_set, __senders_del) def remove_sender(self, senders): - "Remove a single sender or a list of senders" + """ + Remove a single sender or a list of senders. + + Args: + senders (Player, Object, str or list): Senders to remove. + + """ for sender in make_iter(senders): if not sender: continue @@ -210,7 +221,13 @@ class Msg(SharedMemoryModel): receivers = property(__receivers_get, __receivers_set, __receivers_del) def remove_receiver(self, receivers): - "Remove a single receiver or a list of receivers" + """ + Remove a single receiver or a list of receivers. + + Args: + receivers (Player, Object, Channel or list): Receiver to remove. + + """ for receiver in make_iter(receivers): if not receiver: continue @@ -295,12 +312,26 @@ class Msg(SharedMemoryModel): class TempMsg(object): """ - This is a non-persistent object for sending - temporary messages that will not be stored. - It mimics the "real" Msg object, but don't require - sender to be given. + This is a non-persistent object for sending temporary messages + that will not be stored. It mimics the "real" Msg object, but + doesn't require sender to be given. + """ def __init__(self, senders=None, receivers=None, channels=None, message="", header="", type="", lockstring="", hide_from=None): + """ + Creates the temp message. + + Args: + senders (any or list, optional): Senders of the message. + receivers (Player, Object, Channel or list, optional): Receivers of this message. + channels (Channel or list, optional): Channels to send to. + message (str, optional): Message to send. + header (str, optional): Header of message. + type (str, optional): Message class, if any. + lockstring (str, optional): Lock for the message. + hide_from (Player, Object, Channel or list, optional): Entities to hide this message from. + + """ self.senders = senders and make_iter(senders) or [] self.receivers = receivers and make_iter(receivers) or [] self.channels = channels and make_iter(channels) or [] @@ -316,29 +347,54 @@ class TempMsg(object): return LockHandler(self) def __str__(self): - "This handles what is shown when e.g. printing the message" + """ + This handles what is shown when e.g. printing the message. + """ senders = ",".join(obj.key for obj in self.senders) receivers = ",".join(["[%s]" % obj.key for obj in self.channels] + [obj.key for obj in self.receivers]) return "%s->%s: %s" % (senders, receivers, crop(self.message, width=40)) - def remove_sender(self, obj): - "Remove a sender or a list of senders" - for o in make_iter(obj): + def remove_sender(self, sender): + """ + Remove a sender or a list of senders. + + Args: + sender (Object, Player, str or list): Senders to remove. + + """ + for o in make_iter(sender): try: self.senders.remove(o) except ValueError: pass # nothing to remove - def remove_receiver(self, obj): - "Remove a sender or a list of senders" - for o in make_iter(obj): + def remove_receiver(self, receiver): + """ + Remove a receiver or a list of receivers + + Args: + receiver (Object, Player, Channel, str or list): Receivers to remove. + """ + + for o in make_iter(receiver): try: self.senders.remove(o) except ValueError: pass # nothing to remove def access(self, accessing_obj, access_type='read', default=False): - "checks lock access" + """ + Checks lock access. + + Args: + accessing_obj (Object or Player): The object trying to gain access. + access_type (str, optional): The type of lock access to check. + default (bool): Fallback to use if `access_type` lock is not defined. + + Returns: + result (bool): If access was granted or not. + + """ return self.locks.check(accessing_obj, access_type=access_type, default=default) @@ -453,11 +509,12 @@ class ChannelDB(TypedObject): This is the basis of a comm channel, only implementing the very basics of distributing messages. - The Channel class defines the following properties: - key - main name for channel - desc - optional description of channel - aliases - alternative names for the channel - permissions - perm strings + The Channel class defines the following database fields + beyond the ones inherited from TypedObject: + + - db_subscriptions: The Player subscriptions (this is the most + usual case, named this way for legacy. + - db_object_subscriptions: The Object subscriptions. """ db_subscriptions = models.ManyToManyField("players.PlayerDB", @@ -479,6 +536,7 @@ class ChannelDB(TypedObject): verbose_name_plural = "Channels" def __str__(self): + "Echoes the text representation of the channel." return "Channel '%s' (%s)" % (self.key, self.db.desc) @lazy_property