Converted the comm system folder to use Google style doc strings, as per #709.

This commit is contained in:
Griatch 2015-05-16 11:03:17 +02:00
parent e84db3df54
commit e37079aa5b
5 changed files with 512 additions and 201 deletions

View file

@ -1,7 +1,7 @@
# """
# This sets up how models are displayed This defines how Comm models are displayed in the web admin interface.
# in the web admin interface.
# """
from django.contrib import admin from django.contrib import admin
from evennia.comms.models import ChannelDB from evennia.comms.models import ChannelDB
@ -9,14 +9,26 @@ from evennia.typeclasses.admin import AttributeInline, TagInline
class ChannelAttributeInline(AttributeInline): class ChannelAttributeInline(AttributeInline):
"""
Inline display of Channel Attribute - experimental
"""
model = ChannelDB.db_attributes.through model = ChannelDB.db_attributes.through
class ChannelTagInline(TagInline): class ChannelTagInline(TagInline):
"""
Inline display of Channel Tags - experimental
"""
model = ChannelDB.db_tags.through model = ChannelDB.db_tags.through
class MsgAdmin(admin.ModelAdmin): class MsgAdmin(admin.ModelAdmin):
"""
Defines display for Msg objects
"""
list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers', list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers',
'db_channels', 'db_message', 'db_lock_storage') 'db_channels', 'db_message', 'db_lock_storage')
list_display_links = ("id",) list_display_links = ("id",)
@ -30,6 +42,10 @@ class MsgAdmin(admin.ModelAdmin):
class ChannelAdmin(admin.ModelAdmin): class ChannelAdmin(admin.ModelAdmin):
"""
Defines display for Channel objects
"""
inlines = [ChannelTagInline, ChannelAttributeInline] inlines = [ChannelTagInline, ChannelAttributeInline]
list_display = ('id', 'db_key', 'db_lock_storage', "subscriptions") list_display = ('id', 'db_key', 'db_lock_storage', "subscriptions")
list_display_links = ("id", 'db_key') list_display_links = ("id", 'db_key')
@ -43,7 +59,13 @@ class ChannelAdmin(admin.ModelAdmin):
) )
def subscriptions(self, obj): 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()]) return ", ".join([str(sub) for sub in obj.db_subscriptions.all()])
admin.site.register(ChannelDB, ChannelAdmin) admin.site.register(ChannelDB, ChannelAdmin)

View file

@ -1,28 +1,29 @@
""" """
The channel handler handles the stored set of channels The channel handler, accessed from this module as CHANNEL_HANDLER is a
and how they are represented against the cmdhandler. 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 If there is a channel named 'newbie', we want to be able to just write
to just write
> newbie Hello! newbie Hello!
For this to work, 'newbie', the name of the channel, must For this to work, 'newbie', the name of the channel, must be
be identified by the cmdhandler as a command name. The identified by the cmdhandler as a command name. The channelhandler
channelhandler stores all channels as custom 'commands' stores all channels as custom 'commands' that the cmdhandler can
that the cmdhandler can import and look through. import and look through.
Warning - channel names take precedence over command names, > Warning - channel names take precedence over command names, so make
so make sure to not pick clashing channel names. sure to not pick clashing channel names.
Unless deleting a channel you normally don't need to bother about Unless deleting a channel you normally don't need to bother about the
the channelhandler at all - the create_channel method handles the update. channelhandler at all - the create_channel method handles the update.
To delete a channel cleanly, delete the channel object, then call To delete a channel cleanly, delete the channel object, then call
update() on the channelhandler. Or use Channel.objects.delete() which update() on the channelhandler. Or use Channel.objects.delete() which
does this for you. does this for you.
""" """
from evennia.comms.models import ChannelDB from evennia.comms.models import ChannelDB
from evennia.commands import cmdset, command 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 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 it by entering its name or alias, followed by the text you want to
send. send.
""" """
# this flag is what identifies this cmd as a channel cmd # this flag is what identifies this cmd as a channel cmd
# and branches off to the system send-to-channel command # and branches off to the system send-to-channel command
@ -100,17 +102,31 @@ class ChannelHandler(object):
self.cached_cmdsets = {} self.cached_cmdsets = {}
def __str__(self): 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) return ", ".join(str(cmd) for cmd in self.cached_channel_cmds)
def clear(self): def clear(self):
""" """
Reset the cache storage. Reset the cache storage.
""" """
self.cached_channel_cmds = [] self.cached_channel_cmds = []
def _format_help(self, channel): 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 key = channel.key
aliases = channel.aliases.all() aliases = channel.aliases.all()
ustring = "%s <message>" % key.lower() + "".join(["\n %s <message>" % alias.lower() for alias in aliases]) ustring = "%s <message>" % key.lower() + "".join(["\n %s <message>" % alias.lower() for alias in aliases])
@ -129,9 +145,17 @@ class ChannelHandler(object):
def add_channel(self, channel): def add_channel(self, channel):
""" """
Add an individual channel to the handler. This should be Add an individual channel to the handler. This should be
called whenever a new channel is created. To called whenever a new channel is created.
remove a channel, simply delete the channel object
and run self.update on the handler. 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 # map the channel to a searchable command
cmd = ChannelCommand(key=channel.key.strip().lower(), cmd = ChannelCommand(key=channel.key.strip().lower(),
@ -146,7 +170,9 @@ class ChannelHandler(object):
def update(self): 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_channel_cmds = []
self.cached_cmdsets = {} self.cached_cmdsets = {}
@ -157,6 +183,15 @@ class ChannelHandler(object):
""" """
Retrieve cmdset for channels this source_object has Retrieve cmdset for channels this source_object has
access to send to. 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: if source_object in self.cached_cmdsets:
return self.cached_cmdsets[source_object] return self.cached_cmdsets[source_object]

View file

@ -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 django.conf import settings
from evennia.typeclasses.models import TypeclassBase from evennia.typeclasses.models import TypeclassBase
from evennia.comms.models import Msg, TempMsg, ChannelDB from evennia.comms.models import Msg, TempMsg, ChannelDB
@ -13,8 +13,9 @@ from evennia.utils.utils import make_iter
class DefaultChannel(ChannelDB): class DefaultChannel(ChannelDB):
""" """
This is the base class for all Comms. Inherit from this to create different This is the base class for all Channel Comms. Inherit from this to
types of communication channels. create different types of communication channels.
""" """
# typeclass setup # typeclass setup
__metaclass__ = TypeclassBase __metaclass__ = TypeclassBase
@ -25,6 +26,7 @@ class DefaultChannel(ChannelDB):
Called by the typeclass system the very first time the channel Called by the typeclass system the very first time the channel
is saved to the database. Generally, don't overload this but is saved to the database. Generally, don't overload this but
the hooks called by this method. the hooks called by this method.
""" """
self.at_channel_creation() self.at_channel_creation()
@ -51,6 +53,7 @@ class DefaultChannel(ChannelDB):
def at_channel_creation(self): def at_channel_creation(self):
""" """
Called once, when the channel is first created. Called once, when the channel is first created.
""" """
pass pass
@ -133,15 +136,19 @@ class DefaultChannel(ChannelDB):
def access(self, accessing_obj, access_type='listen', default=False): def access(self, accessing_obj, access_type='listen', default=False):
""" """
Determines if another object has permission to access. Determines if another object has permission to access.
accessing_obj - object trying to access this one
access_type - type of access sought Args:
default - what to return if no lock of access_type was found 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) return self.locks.check(accessing_obj, access_type=access_type, default=default)
def delete(self): def delete(self):
""" """
Deletes channel while also cleaning up channelhandler Deletes channel while also cleaning up channelhandler.
""" """
self.attributes.clear() self.attributes.clear()
self.aliases.clear() self.aliases.clear()
@ -153,6 +160,15 @@ class DefaultChannel(ChannelDB):
sender_strings=None, external=False): sender_strings=None, external=False):
""" """
Generates the formatted string sent to listeners on a channel. 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: if sender_strings or external:
body = self.format_external(msg, sender_strings, emit=emit) 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 Method for grabbing all listeners that a message should be
sent to on this channel, and sending them a message. 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 # get all players connected to this channel and send to them
for entity in self.subscriptions.all(): 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 done before calling this method. The optional keywords are not used if
persistent is False. persistent is False.
msgobj - a Msg/TempMsg instance or a message string. If one of the Args:
former, the remaining keywords will be ignored. If a string, msgobj (Msg, TempMsg or str): If a Msg/TempMsg, the remaining
this will either be sent as-is (if persistent=False) or it keywords will be ignored (since the Msg/TempMsg object already
will be used together with header and senders keywords to has all the data). If a string, this will either be sent as-is
create a Msg instance on the fly. (if persistent=False) or it will be used together with `header`
senders - an object, player or a list of objects or players. and `senders` keywords to create a Msg instance on the fly.
Optional if persistent=False. header (str, optional): A header for building the message.
sender_strings - Name strings of senders. Used for external senders (Object, Player or list, optional): Optional if persistent=False, used
connections where the sender is not a player or object. When to build senders for the message.
this is defined, external will be assumed. sender_strings (list, optional): Name strings of senders. Used for external
external - Treat this message agnostic of its sender. connections where the sender is not a player or object.
persistent (default False) - ignored if msgobj is a Msg or TempMsg. 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 If True, a Msg will be created, using header and senders
keywords. If False, other keywords will be ignored. 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 online. Otherwise, messages all players connected. This can
make things faster, but may not trigger listeners on players make things faster, but may not trigger listeners on players
that are offline. 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. 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: if senders:
senders = make_iter(senders) senders = make_iter(senders)
@ -238,6 +267,12 @@ class DefaultChannel(ChannelDB):
def tempmsg(self, message, header=None, senders=None): def tempmsg(self, message, header=None, senders=None):
""" """
A wrapper for sending non-persistent messages. 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) self.msg(message, senders=senders, header=header, persistent=False)
@ -247,28 +282,57 @@ class DefaultChannel(ChannelDB):
def channel_prefix(self, msg=None, emit=False): 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 return '[%s] ' % self.key
def format_senders(self, senders=None): 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: if not senders:
return '' return ''
return ', '.join(senders) 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 pose = False
message = msg.message message = msgobj.message
message_start = message.lstrip() message_start = message.lstrip()
if message_start.startswith((':', ';')): if message_start.startswith((':', ';')):
pose = True pose = True
@ -281,91 +345,131 @@ class DefaultChannel(ChannelDB):
else: else:
return '%s: %s' % (sender_string, message) 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 Hook method. Used for formatting external messages. This is
operation because the senders of external messages may not be in-game needed as a separate operation because the senders of external
objects/players, and so cannot have things like custom user messages may not be in-game objects/players, and so cannot
preferences. 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: if emit or not senders:
emit = True return msgobj.message
if emit:
return msg.message
senders = ', '.join(senders) 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 # We don't want to count things like external sources as senders for
# the purpose of constructing the message string. # 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: if not senders:
emit = True emit = True
if emit: if emit:
return msg.message return msgobj.message
else: else:
senders = [sender.key for sender in msg.senders] senders = [sender.key for sender in msgobj.senders]
senders = ', '.join(senders) senders = ', '.join(senders)
return self.pose_transform(msg, senders) return self.pose_transform(msgobj, senders)
def pre_join_channel(self, joiner): def pre_join_channel(self, joiner):
""" """
Run right before a channel is joined. If this returns a false value, Hook method. Runs right before a channel is joined. If this
channel joining is aborted. 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 return True
def post_join_channel(self, joiner): 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): 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. 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 return True
def post_leave_channel(self, leaver): 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 pass
def pre_send_message(self, msg): def pre_send_message(self, msg):
""" """
Run before a message is sent to the channel. Hook method. Runs before a message is sent to the channel and
should return the message object, after any transformations.
This should return the message object, after any transformations.
If the message is to be discarded, return a false value. 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 return msg
def post_send_message(self, 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 pass
def at_init(self): def at_init(self):
""" """
This is always called whenever this channel is initiated -- Hook method. This is always called whenever this channel is
that is, whenever it its typeclass is cached from memory. This initiated -- that is, whenever it its typeclass is cached from
happens on-demand first time the channel is used or activated memory. This happens on-demand first time the channel is used
in some way after being created but also after each server or activated in some way after being created but also after
restart or reload. each server restart or reload.
""" """
pass pass

View file

@ -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 from django.db import models
@ -17,7 +19,9 @@ _SESSIONS = None
class CommError(Exception): 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 pass
@ -27,9 +31,17 @@ class CommError(Exception):
def dbref(dbref, reqhash=True): def dbref(dbref, reqhash=True):
""" """
Valid forms of dbref (database reference number) Valid forms of dbref (database reference number) are either a
are either a string '#N' or an integer N. string '#N' or an integer N.
Output is the integer part.
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("#")): if reqhash and not (isinstance(dbref, basestring) and dbref.startswith("#")):
return None return None
@ -44,7 +56,19 @@ def dbref(dbref, reqhash=True):
def identify_object(inp): 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__"): if hasattr(inp, "__dbclass__"):
clsname = inp.__dbclass__.__name__ clsname = inp.__dbclass__.__name__
if clsname == "PlayerDB": if clsname == "PlayerDB":
@ -63,11 +87,16 @@ def identify_object(inp):
def to_object(inp, objtype='player'): def to_object(inp, objtype='player'):
""" """
Locates the object related to the given Locates the object related to the given playername or channel key.
playername or channel key. If input was already If input was already the correct object, return it.
the correct object, return it.
inp - the input object/string Args:
objtype - 'player' or 'channel' 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) obj, typ = identify_object(inp)
if typ == objtype: if typ == objtype:
@ -104,46 +133,68 @@ def to_object(inp, objtype='player'):
class MsgManager(models.Manager): class MsgManager(models.Manager):
""" """
This MsgManager implements methods for searching This MsgManager implements methods for searching and manipulating
and manipulating Messages directly from the database. Messages directly from the database.
These methods will all return database objects These methods will all return database objects (or QuerySets)
(or QuerySets) directly. directly.
A Message represents one unit of communication, be it over a 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, 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 it always has a sender and can have any number of receivers (some
of which may be Channels). 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): def identify_object(self, inp):
"method version for easy access" """
return identify_object(obj) 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): 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: try:
return self.get(id=self.dbref(idnum, reqhash=False)) return self.get(id=self.dbref(idnum, reqhash=False))
except Exception: except Exception:
return None 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 Get all messages sent by one entity - this could be either a
player or an object player or an object
only_non_channel: only return messages -not- aimed at a channel Args:
(e.g. private tells) 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: if exclude_channel_messages:
# explicitly exclude channel recipients # explicitly exclude channel recipients
if typ == 'player': if typ == 'player':
@ -163,11 +214,21 @@ class MsgManager(models.Manager):
else: else:
raise CommError 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': if typ == 'player':
return list(self.filter(db_receivers_players=obj).exclude(db_hide_from_players=obj)) return list(self.filter(db_receivers_players=obj).exclude(db_hide_from_players=obj))
elif typ == 'object': elif typ == 'object':
@ -179,23 +240,36 @@ class MsgManager(models.Manager):
def get_messages_by_channel(self, channel): 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) 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): def message_search(self, sender=None, receiver=None, freetext=None, dbref=None):
""" """
Search the message database for particular messages. At least one Search the message database for particular messages. At least
of the arguments must be given to do a search. 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 # unique msg id
if dbref: if dbref:
@ -241,28 +315,26 @@ class MsgManager(models.Manager):
class ChannelDBManager(TypedObjectManager): class ChannelDBManager(TypedObjectManager):
""" """
This ChannelManager implements methods for searching This ChannelManager implements methods for searching and
and manipulating Channels directly from the database. manipulating Channels directly from the database.
These methods will all return database objects These methods will all return database objects (or QuerySets)
(or QuerySets) directly. directly.
A Channel is an in-game venue for communication. It's A Channel is an in-game venue for communication. It's essentially
essentially representation of a re-sender: Users sends representation of a re-sender: Users sends Messages to the
Messages to the Channel, and the Channel re-sends those Channel, and the Channel re-sends those messages to all users
messages to all users subscribed to the Channel. subscribed to the Channel.
Evennia-specific:
get_all_channels
get_channel(channel)
get_subscriptions(player)
channel_search (equivalent to evennia.search_channel)
""" """
@returns_typeclass_list @returns_typeclass_list
def get_all_channels(self): def get_all_channels(self):
""" """
Returns all channels in game. Get all channels.
Returns:
channels (list): All channels in game.
""" """
return self.all() return self.all()
@ -271,6 +343,13 @@ class ChannelDBManager(TypedObjectManager):
""" """
Return the channel object if given its key. Return the channel object if given its key.
Also searches its aliases. 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 # first check the channel key
channels = self.filter(db_key__iexact=channelkey) channels = self.filter(db_key__iexact=channelkey)
@ -283,15 +362,22 @@ class ChannelDBManager(TypedObjectManager):
return None return None
@returns_typeclass_list @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": if clsname == "PlayerDB":
return entity.subscription_set.all() return subscriber.subscription_set.all()
if clsname == "ObjectDB": if clsname == "ObjectDB":
return entity.object_subscription_set.all() return subscriber.object_subscription_set.all()
return [] return []
@returns_typeclass_list @returns_typeclass_list
@ -299,8 +385,11 @@ class ChannelDBManager(TypedObjectManager):
""" """
Search the channel database for a particular channel. Search the channel database for a particular channel.
ostring - the key or database id of the channel. Args:
exact - require an exact key match (still not case sensitive) ostring (str): The key or database id of the channel.
exact (bool, optional): Require an exact (but not
case sensitive) match.
""" """
channels = [] channels = []
if not ostring: return channels if not ostring: return channels
@ -324,6 +413,9 @@ class ChannelDBManager(TypedObjectManager):
return channels return channels
class ChannelManager(ChannelDBManager, TypeclassManager): class ChannelManager(ChannelDBManager, TypeclassManager):
"""
Wrapper to group the typeclass manager to a consistent name.
"""
pass pass

View file

@ -1,22 +1,21 @@
""" """
Models for the comsystem. The Commsystem is intended to be Models for the in-game communication system.
used by Players (thematic IC communication is probably
best handled by custom commands instead).
The comm system could take the form of channels, but can also The comm system could take the form of channels, but can also be
be adopted for storing tells or in-game mail. adopted for storing tells or in-game mail.
The comsystem's main component is the Message (Msg), which The comsystem's main component is the Message (Msg), which carries the
carries the actual information between two parties. actual information between two parties. Msgs are stored in the
Msgs are stored in the database and usually not database and usually not deleted. A Msg always have one sender (a
deleted. user), but can have any number targets, both users and channels.
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 For non-persistent (and slightly faster) use one can also use the
Msgs. Players can connect to channels by use of a TempMsg, which mimics the Msg API but without actually saving to the
ChannelConnect object (this object is necessary to easily database.
be able to delete connections on the fly).
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 from django.conf import settings
@ -47,16 +46,22 @@ class Msg(SharedMemoryModel):
A single message. This model describes all ooc messages A single message. This model describes all ooc messages
sent in-game, both to channels and between players. sent in-game, both to channels and between players.
The Msg class defines the following properties: The Msg class defines the following database fields (all
sender - sender of message accessed via specific handler methods):
receivers - list of target objects for message
channels - list of channels message was sent to - db_sender_players: Player senders
message - the text being sent - db_sender_objects: Object senders
date_sent - time message was sent - db_sender_external: External senders (defined as string names)
hide_from_sender - bool if message should be hidden from sender - db_receivers_players: Receiving players
hide_from_receivers - list of receiver objects to hide message from - db_receivers_objects: Receiving objects
hide_from_channels - list of channels objects to hide message from - db_receivers_channels: Receiving channels
permissions - perm strings - 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) senders = property(__senders_get, __senders_set, __senders_del)
def remove_sender(self, senders): 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): for sender in make_iter(senders):
if not sender: if not sender:
continue continue
@ -210,7 +221,13 @@ class Msg(SharedMemoryModel):
receivers = property(__receivers_get, __receivers_set, __receivers_del) receivers = property(__receivers_get, __receivers_set, __receivers_del)
def remove_receiver(self, receivers): 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): for receiver in make_iter(receivers):
if not receiver: if not receiver:
continue continue
@ -295,12 +312,26 @@ class Msg(SharedMemoryModel):
class TempMsg(object): class TempMsg(object):
""" """
This is a non-persistent object for sending This is a non-persistent object for sending temporary messages
temporary messages that will not be stored. that will not be stored. It mimics the "real" Msg object, but
It mimics the "real" Msg object, but don't require doesn't require sender to be given.
sender to be given.
""" """
def __init__(self, senders=None, receivers=None, channels=None, message="", header="", type="", lockstring="", hide_from=None): 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.senders = senders and make_iter(senders) or []
self.receivers = receivers and make_iter(receivers) or [] self.receivers = receivers and make_iter(receivers) or []
self.channels = channels and make_iter(channels) or [] self.channels = channels and make_iter(channels) or []
@ -316,29 +347,54 @@ class TempMsg(object):
return LockHandler(self) return LockHandler(self)
def __str__(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) 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]) 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)) return "%s->%s: %s" % (senders, receivers, crop(self.message, width=40))
def remove_sender(self, obj): def remove_sender(self, sender):
"Remove a sender or a list of senders" """
for o in make_iter(obj): 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: try:
self.senders.remove(o) self.senders.remove(o)
except ValueError: except ValueError:
pass # nothing to remove pass # nothing to remove
def remove_receiver(self, obj): def remove_receiver(self, receiver):
"Remove a sender or a list of senders" """
for o in make_iter(obj): 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: try:
self.senders.remove(o) self.senders.remove(o)
except ValueError: except ValueError:
pass # nothing to remove pass # nothing to remove
def access(self, accessing_obj, access_type='read', default=False): 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, return self.locks.check(accessing_obj,
access_type=access_type, default=default) access_type=access_type, default=default)
@ -453,11 +509,12 @@ class ChannelDB(TypedObject):
This is the basis of a comm channel, only implementing This is the basis of a comm channel, only implementing
the very basics of distributing messages. the very basics of distributing messages.
The Channel class defines the following properties: The Channel class defines the following database fields
key - main name for channel beyond the ones inherited from TypedObject:
desc - optional description of channel
aliases - alternative names for the channel - db_subscriptions: The Player subscriptions (this is the most
permissions - perm strings usual case, named this way for legacy.
- db_object_subscriptions: The Object subscriptions.
""" """
db_subscriptions = models.ManyToManyField("players.PlayerDB", db_subscriptions = models.ManyToManyField("players.PlayerDB",
@ -479,6 +536,7 @@ class ChannelDB(TypedObject):
verbose_name_plural = "Channels" verbose_name_plural = "Channels"
def __str__(self): def __str__(self):
"Echoes the text representation of the channel."
return "Channel '%s' (%s)" % (self.key, self.db.desc) return "Channel '%s' (%s)" % (self.key, self.db.desc)
@lazy_property @lazy_property