Trunk: Merged the Devel-branch (branches/griatch) into /trunk. This constitutes a major refactoring of Evennia. Development will now continue in trunk. See the wiki and the past posts to the mailing list for info. /Griatch
This commit is contained in:
parent
df29defbcd
commit
f83c2bddf8
222 changed files with 22304 additions and 14371 deletions
0
src/comms/__init__.py
Normal file
0
src/comms/__init__.py
Normal file
40
src/comms/admin.py
Normal file
40
src/comms/admin.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#
|
||||
# This sets up how models are displayed
|
||||
# in the web admin interface.
|
||||
#
|
||||
|
||||
from django.contrib import admin
|
||||
from src.comms.models import Channel, Msg, ChannelConnection
|
||||
|
||||
class MsgAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers', 'db_channels', 'db_message')
|
||||
list_display_links = ("id",)
|
||||
ordering = ["db_date_sent", 'db_sender', 'db_receivers', 'db_channels']
|
||||
readonly_fields = ['db_permissions', 'db_message', 'db_sender', 'db_receivers', 'db_channels']
|
||||
search_fields = ['id', '^db_date_sent', '^db_message']
|
||||
save_as = True
|
||||
save_on_top = True
|
||||
list_select_related = True
|
||||
admin.site.register(Msg, MsgAdmin)
|
||||
|
||||
class ChannelAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'db_key', 'db_desc', 'db_aliases', 'db_keep_log', 'db_permissions')
|
||||
list_display_links = ("id", 'db_key')
|
||||
ordering = ["db_key"]
|
||||
readonly_fields = ['db_permissions']
|
||||
search_fields = ['id', 'db_key', 'db_aliases']
|
||||
save_as = True
|
||||
save_on_top = True
|
||||
list_select_related = True
|
||||
admin.site.register(Channel, ChannelAdmin)
|
||||
|
||||
class ChannelConnectionAdmin(admin.ModelAdmin):
|
||||
list_display = ('db_channel', 'db_player')
|
||||
list_display_links = ("db_player", 'db_channel')
|
||||
ordering = ["db_channel"]
|
||||
search_fields = ['db_channel', 'db_player']
|
||||
save_as = True
|
||||
save_on_top = True
|
||||
list_select_related = True
|
||||
admin.site.register(ChannelConnection, ChannelConnectionAdmin)
|
||||
|
||||
138
src/comms/channelhandler.py
Normal file
138
src/comms/channelhandler.py
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
"""
|
||||
The channel handler 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
|
||||
|
||||
> 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.
|
||||
|
||||
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.
|
||||
|
||||
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 src.comms.models import Channel, Msg
|
||||
from src.commands import cmdset, command
|
||||
from src.permissions.permissions import has_perm
|
||||
|
||||
class ChannelCommand(command.Command):
|
||||
"""
|
||||
Channel
|
||||
|
||||
Usage:
|
||||
<channel name or alias> <message>
|
||||
|
||||
This is a channel. 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
|
||||
# (which is customizable by admin)
|
||||
key = "general"
|
||||
help_category = "Channels"
|
||||
permissions = "cmd:use_channels"
|
||||
is_channel = True
|
||||
obj = None
|
||||
|
||||
def parse(self):
|
||||
"""
|
||||
Simple parser
|
||||
"""
|
||||
channelname, msg = self.args.split(":", 1)
|
||||
self.args = (channelname.strip(), msg.strip())
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
Create a new message and send it to channel, using
|
||||
the already formatted input.
|
||||
"""
|
||||
channelkey, msg = self.args
|
||||
caller = self.caller
|
||||
if not msg:
|
||||
caller.msg("Say what?")
|
||||
return
|
||||
channel = Channel.objects.get_channel(channelkey)
|
||||
if not channel:
|
||||
caller.msg("Channel '%s' not found." % channelkey)
|
||||
return
|
||||
if not channel.has_connection(caller):
|
||||
string = "You are not connected to channel '%s'."
|
||||
caller.msg(string % channelkey)
|
||||
return
|
||||
if not has_perm(caller, channel, 'chan_send'):
|
||||
string = "You are not permitted to send to channel '%s'."
|
||||
caller.msg(string % channelkey)
|
||||
return
|
||||
msg = "[%s] %s: %s" % (channel.key, caller.name, msg)
|
||||
# we can't use the utils.create function to make the Msg,
|
||||
# since that creates an import recursive loop.
|
||||
msgobj = Msg(db_sender=caller.player, db_message=msg)
|
||||
msgobj.save()
|
||||
msgobj.channels = channel
|
||||
# send new message object to channel
|
||||
channel.msg(msgobj)
|
||||
|
||||
class ChannelHandler(object):
|
||||
"""
|
||||
Handles the set of commands related to channels.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.cached_channel_cmds = []
|
||||
|
||||
def __str__(self):
|
||||
return ", ".join(str(cmd) for cmd in self.cached_channel_cmds)
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Reset the cache storage.
|
||||
"""
|
||||
self.cached_channel_cmds = []
|
||||
|
||||
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.
|
||||
"""
|
||||
# map the channel to a searchable command
|
||||
cmd = ChannelCommand()
|
||||
cmd.key = channel.key.strip().lower()
|
||||
cmd.obj = channel
|
||||
if channel.aliases:
|
||||
cmd.aliases = channel.aliases
|
||||
self.cached_channel_cmds.append(cmd)
|
||||
|
||||
def update(self):
|
||||
"Updates the handler completely."
|
||||
self.cached_channel_cmds = []
|
||||
for channel in Channel.objects.all():
|
||||
self.add_channel(channel)
|
||||
|
||||
def get_cmdset(self, source_object):
|
||||
"""
|
||||
Retrieve cmdset for channels this source_object has
|
||||
access to send to.
|
||||
"""
|
||||
# create a temporary cmdset holding all channels
|
||||
chan_cmdset = cmdset.CmdSet()
|
||||
chan_cmdset.key = '_channelset'
|
||||
chan_cmdset.priority = 10
|
||||
for cmd in [cmd for cmd in self.cached_channel_cmds
|
||||
if has_perm(source_object, cmd, 'chan_send')]:
|
||||
chan_cmdset.add(cmd)
|
||||
return chan_cmdset
|
||||
|
||||
CHANNELHANDLER = ChannelHandler()
|
||||
322
src/comms/managers.py
Normal file
322
src/comms/managers.py
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
"""
|
||||
These managers handles the
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
from src.players.models import PlayerDB
|
||||
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'
|
||||
"""
|
||||
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]
|
||||
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):
|
||||
"""
|
||||
Handle msg database
|
||||
"""
|
||||
|
||||
def get_message_by_id(self, idnum):
|
||||
"Retrieve message by its id."
|
||||
try:
|
||||
idnum = int(idnum)
|
||||
return self.get(id=id)
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_messages_by_sender(self, sender):
|
||||
"""
|
||||
Get all messages sent by one player
|
||||
"""
|
||||
sender = to_object(sender)
|
||||
if not sender:
|
||||
return None
|
||||
return [msg for msg in sender.sender_set.all()
|
||||
if sender not in msg.hide_from_senders.all()]
|
||||
|
||||
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 receiver.receiver_set.all()
|
||||
if receiver not in msg.hide_from_receivers.all()]
|
||||
|
||||
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 channel.channel_set.all()
|
||||
if channel not in msg.hide_from_channels.all()]
|
||||
|
||||
|
||||
#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):
|
||||
"""
|
||||
Handle channel database
|
||||
"""
|
||||
|
||||
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 has_connection(self, player, channel):
|
||||
"Check so the player is really listening to this channel."
|
||||
ChannelConnection = ContentType.objects.get(app_label="comms",
|
||||
model="channelconnection").model_class()
|
||||
return ChannelConnection.objects.has_connection(player, channel)
|
||||
|
||||
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 ChannelConnection
|
||||
#= ContentType.objects.get(app_label="comms",
|
||||
# model="channelconnection").model_class()
|
||||
return ChannelConnection.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=ostring)
|
||||
return channels
|
||||
|
||||
#
|
||||
# ChannelConnection manager
|
||||
#
|
||||
class ChannelConnectionManager(models.Manager):
|
||||
"""
|
||||
This handles all connections between a player and
|
||||
a channel.
|
||||
"""
|
||||
|
||||
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()
|
||||
|
||||
590
src/comms/models.py
Normal file
590
src/comms/models.py
Normal file
|
|
@ -0,0 +1,590 @@
|
|||
"""
|
||||
Models for the comsystem.
|
||||
|
||||
The comsystem's main component is the Message, 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).
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
from src.utils.idmapper.models import SharedMemoryModel
|
||||
from src.players.models import PlayerDB
|
||||
from src.comms import managers
|
||||
from src.server import sessionhandler
|
||||
from src.permissions.permissions import has_perm
|
||||
from src.utils.utils import is_iter
|
||||
from src.utils.utils import dbref as is_dbref
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
# Utils
|
||||
#
|
||||
#------------------------------------------------------------
|
||||
|
||||
def obj_to_id(inp):
|
||||
"""
|
||||
Converts input object to an id string.
|
||||
"""
|
||||
dbref = is_dbref(inp)
|
||||
if dbref:
|
||||
return str(dbref)
|
||||
if hasattr(inp, 'id'):
|
||||
return str(inp.id)
|
||||
if hasattr(inp, 'dbobj') and hasattr(inp.dbobj, 'id'):
|
||||
return str(inp.dbobj.id)
|
||||
return str(inp)
|
||||
|
||||
def id_to_obj(dbref, db_model='PlayerDB'):
|
||||
"""
|
||||
loads from dbref to object. Uses the db_model to search
|
||||
for the id.
|
||||
"""
|
||||
if db_model == 'PlayerDB':
|
||||
from src.player.objects import PlayerDB as db_model
|
||||
else:
|
||||
db_model = Channel
|
||||
try:
|
||||
dbref = int(dbref.strip())
|
||||
return db_model.objects.get(id=dbref)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
# Msg
|
||||
#
|
||||
#------------------------------------------------------------
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
|
||||
#
|
||||
# Msg database model setup
|
||||
#
|
||||
#
|
||||
# These databse fields are all set using their corresponding properties,
|
||||
# named same as the field, but withtout the db_* prefix.
|
||||
|
||||
# There must always be one sender of the message.
|
||||
db_sender = models.ForeignKey(PlayerDB, related_name='sender_set')
|
||||
# The destination objects of this message. Stored as a
|
||||
# comma-separated string of object dbrefs. Can be defined along
|
||||
# with channels below.
|
||||
db_receivers = models.CharField(max_length=255, null=True, blank=True)
|
||||
# The channels this message was sent to. Stored as a
|
||||
# comma-separated string of channel dbrefs. A message can both
|
||||
# have channel targets and destination objects.
|
||||
db_channels = models.CharField(max_length=255, null=True, blank=True)
|
||||
# The actual message and a timestamp. The message field
|
||||
# should itself handle eventual headers etc.
|
||||
db_message = models.TextField()
|
||||
db_date_sent = models.DateTimeField(editable=False, auto_now_add=True)
|
||||
# These are settable by senders/receivers/channels respectively.
|
||||
# Stored as a comma-separated string of dbrefs. Can be used by the
|
||||
# game to mask out messages from being visible in the archive (no
|
||||
# messages are actually deleted)
|
||||
#db_hide_from_sender = models.BooleanField(default=False)
|
||||
#db_hide_from_receivers = models.CharField(max_length=255, null=True, blank=True)
|
||||
#db_hide_from_channels = models.CharField(max_length=255, null=True, blank=True)
|
||||
# permission strings, separated by commas
|
||||
db_permissions = models.CharField(max_length=255, blank=True)
|
||||
|
||||
# Database manager
|
||||
objects = managers.MsgManager()
|
||||
|
||||
class Meta:
|
||||
"Define Django meta options"
|
||||
verbose_name = "Message"
|
||||
verbose_name_plural = "Messages"
|
||||
|
||||
# Wrapper properties to easily set database fields. These are
|
||||
# @property decorators that allows to access these fields using
|
||||
# normal python operations (without having to remember to save()
|
||||
# etc). So e.g. a property 'attr' has a get/set/del decorator
|
||||
# defined that allows the user to do self.attr = value,
|
||||
# value = self.attr and del self.attr respectively (where self
|
||||
# is the object in question).
|
||||
|
||||
# sender property (wraps db_sender)
|
||||
#@property
|
||||
def sender_get(self):
|
||||
"Getter. Allows for value = self.sender"
|
||||
return self.db_sender
|
||||
#@sender.setter
|
||||
def sender_set(self, value):
|
||||
"Setter. Allows for self.sender = value"
|
||||
self.db_sender = value
|
||||
self.save()
|
||||
#@sender.deleter
|
||||
def sender_del(self):
|
||||
"Deleter. Allows for del self.sender"
|
||||
raise Exception("You cannot delete the sender of a message!")
|
||||
sender = property(sender_get, sender_set, sender_del)
|
||||
|
||||
# receivers property
|
||||
#@property
|
||||
def receivers_get(self):
|
||||
"Getter. Allows for value = self.receivers. Returns a list of receivers."
|
||||
if self.db_receivers:
|
||||
return [id_to_obj(dbref) for dbref in self.db_receivers.split(',')]
|
||||
return []
|
||||
#@receivers.setter
|
||||
def receivers_set(self, value):
|
||||
"Setter. Allows for self.receivers = value. Stores as a comma-separated string."
|
||||
if is_iter(value):
|
||||
value = ",".join([obj_to_id(val) for val in value])
|
||||
self.db_receivers = obj_to_id(value)
|
||||
self.save()
|
||||
#@receivers.deleter
|
||||
def receivers_del(self):
|
||||
"Deleter. Allows for del self.receivers"
|
||||
self.db_receivers = ""
|
||||
self.save()
|
||||
receivers = property(receivers_get, receivers_set, receivers_del)
|
||||
|
||||
# channels property
|
||||
#@property
|
||||
def channels_get(self):
|
||||
"Getter. Allows for value = self.channels. Returns a list of channels."
|
||||
if self.db_channels:
|
||||
return [id_to_obj(dbref, 'Channel') for dbref in self.db_channels.split(',')]
|
||||
return []
|
||||
#@channels.setter
|
||||
def channels_set(self, value):
|
||||
"Setter. Allows for self.channels = value. Stores as a comma-separated string."
|
||||
if is_iter(value):
|
||||
value = ",".join([obj_to_id(val) for val in value])
|
||||
self.db_channels = obj_to_id(value)
|
||||
self.save()
|
||||
#@channels.deleter
|
||||
def channels_del(self):
|
||||
"Deleter. Allows for del self.channels"
|
||||
self.db_channels = ""
|
||||
self.save()
|
||||
channels = property(channels_get, channels_set, channels_del)
|
||||
|
||||
# message property (wraps db_message)
|
||||
#@property
|
||||
def message_get(self):
|
||||
"Getter. Allows for value = self.message"
|
||||
return self.db_message
|
||||
#@message.setter
|
||||
def message_set(self, value):
|
||||
"Setter. Allows for self.message = value"
|
||||
self.db_message = value
|
||||
self.save()
|
||||
#@message.deleter
|
||||
def message_del(self):
|
||||
"Deleter. Allows for del self.message"
|
||||
self.db_message = ""
|
||||
self.save()
|
||||
message = property(message_get, message_set, message_del)
|
||||
|
||||
# date_sent property (wraps db_date_sent)
|
||||
#@property
|
||||
def date_sent_get(self):
|
||||
"Getter. Allows for value = self.date_sent"
|
||||
return self.db_date_sent
|
||||
#@date_sent.setter
|
||||
def date_sent_set(self, value):
|
||||
"Setter. Allows for self.date_sent = value"
|
||||
self.db_date_sent = value
|
||||
self.save()
|
||||
#@date_sent.deleter
|
||||
def date_sent_del(self):
|
||||
"Deleter. Allows for del self.date_sent"
|
||||
raise Exception("You cannot delete the date_sent property!")
|
||||
date_sent = property(date_sent_get, date_sent_set, date_sent_del)
|
||||
|
||||
# hide_from_sender property
|
||||
#@property
|
||||
def hide_from_sender_get(self):
|
||||
"Getter. Allows for value = self.hide_from_sender."
|
||||
return self.db_hide_from_sender
|
||||
#@hide_from_sender.setter
|
||||
def hide_from_sender_set(self, value):
|
||||
"Setter. Allows for self.hide_from_senders = value."
|
||||
self.db_hide_from_sender = value
|
||||
self.save()
|
||||
#@hide_from_sender.deleter
|
||||
def hide_from_sender_del(self):
|
||||
"Deleter. Allows for del self.hide_from_senders"
|
||||
self.db_hide_from_sender = False
|
||||
self.save()
|
||||
hide_from_sender = property(hide_from_sender_get, hide_from_sender_set, hide_from_sender_del)
|
||||
|
||||
# hide_from_receivers property
|
||||
#@property
|
||||
def hide_from_receivers_get(self):
|
||||
"Getter. Allows for value = self.hide_from_receivers. Returns a list of hide_from_receivers."
|
||||
if self.db_hide_from_receivers:
|
||||
return [id_to_obj(dbref) for dbref in self.db_hide_from_receivers.split(',')]
|
||||
return []
|
||||
#@hide_from_receivers.setter
|
||||
def hide_from_receivers_set(self, value):
|
||||
"Setter. Allows for self.hide_from_receivers = value. Stores as a comma-separated string."
|
||||
if is_iter(value):
|
||||
value = ",".join([obj_to_id(val) for val in value])
|
||||
self.db_hide_from_receivers = obj_to_id(value)
|
||||
self.save()
|
||||
#@hide_from_receivers.deleter
|
||||
def hide_from_receivers_del(self):
|
||||
"Deleter. Allows for del self.hide_from_receivers"
|
||||
self.db_hide_from_receivers = ""
|
||||
self.save()
|
||||
hide_from_receivers = property(hide_from_receivers_get, hide_from_receivers_set, hide_from_receivers_del)
|
||||
|
||||
# hide_from_channels property
|
||||
#@property
|
||||
def hide_from_channels_get(self):
|
||||
"Getter. Allows for value = self.hide_from_channels. Returns a list of hide_from_channels."
|
||||
if self.db_hide_from_channels:
|
||||
return [id_to_obj(dbref) for dbref in self.db_hide_from_channels.split(',')]
|
||||
return []
|
||||
#@hide_from_channels.setter
|
||||
def hide_from_channels_set(self, value):
|
||||
"Setter. Allows for self.hide_from_channels = value. Stores as a comma-separated string."
|
||||
if is_iter(value):
|
||||
value = ",".join([obj_to_id(val) for val in value])
|
||||
self.db_hide_from_channels = obj_to_id(value)
|
||||
self.save()
|
||||
#@hide_from_channels.deleter
|
||||
def hide_from_channels_del(self):
|
||||
"Deleter. Allows for del self.hide_from_channels"
|
||||
self.db_hide_from_channels = ""
|
||||
self.save()
|
||||
hide_from_channels = property(hide_from_channels_get, hide_from_channels_set, hide_from_channels_del)
|
||||
|
||||
# permissions property
|
||||
#@property
|
||||
def permissions_get(self):
|
||||
"Getter. Allows for value = self.permissions. Returns a list of permissions."
|
||||
if self.db_permissions:
|
||||
return [perm.strip() for perm in self.db_permissions.split(',')]
|
||||
return []
|
||||
#@permissions.setter
|
||||
def permissions_set(self, value):
|
||||
"Setter. Allows for self.permissions = value. Stores as a comma-separated string."
|
||||
if is_iter(value):
|
||||
value = ",".join([str(val).strip().lower() for val in value])
|
||||
self.db_permissions = value
|
||||
self.save()
|
||||
#@permissions.deleter
|
||||
def permissions_del(self):
|
||||
"Deleter. Allows for del self.permissions"
|
||||
self.db_permissions = ""
|
||||
self.save()
|
||||
permissions = property(permissions_get, permissions_set, permissions_del)
|
||||
|
||||
#
|
||||
# Msg class method
|
||||
#
|
||||
|
||||
def __str__(self):
|
||||
"Print text"
|
||||
if self.channels:
|
||||
return "%s -> %s: %s" % (self.sender.key,
|
||||
", ".join([chan.key for chan in self.channels]),
|
||||
self.message)
|
||||
else:
|
||||
return "%s -> %s: %s" % (self.sender.key,
|
||||
", ".join([rec.key for rec in self.receivers]),
|
||||
self.message)
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
# Channel
|
||||
#
|
||||
#------------------------------------------------------------
|
||||
|
||||
class Channel(SharedMemoryModel):
|
||||
"""
|
||||
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
|
||||
keep_log - bool if the channel should remember messages
|
||||
permissions - perm strings
|
||||
|
||||
"""
|
||||
|
||||
#
|
||||
# Channel database model setup
|
||||
#
|
||||
#
|
||||
# These databse fields are all set using their corresponding properties,
|
||||
# named same as the field, but withtout the db_* prefix.
|
||||
|
||||
# unique identifier for this channel
|
||||
db_key = models.CharField(max_length=255, unique=True)
|
||||
# optional description of channel
|
||||
db_desc = models.CharField(max_length=80, blank=True, null=True)
|
||||
# aliases for the channel. These are searched by cmdhandler
|
||||
# as well to determine if a command is the name of a channel.
|
||||
# Several aliases are separated by commas.
|
||||
db_aliases = models.CharField(max_length=255)
|
||||
# Whether this channel should remember its past messages
|
||||
db_keep_log = models.BooleanField(default=True)
|
||||
# Permission strings, separated by commas
|
||||
db_permissions = models.CharField(max_length=255, blank=True)
|
||||
|
||||
# Database manager
|
||||
objects = managers.ChannelManager()
|
||||
|
||||
# Wrapper properties to easily set database fields. These are
|
||||
# @property decorators that allows to access these fields using
|
||||
# normal python operations (without having to remember to save()
|
||||
# etc). So e.g. a property 'attr' has a get/set/del decorator
|
||||
# defined that allows the user to do self.attr = value,
|
||||
# value = self.attr and del self.attr respectively (where self
|
||||
# is the object in question).
|
||||
|
||||
# key property (wraps db_key)
|
||||
#@property
|
||||
def key_get(self):
|
||||
"Getter. Allows for value = self.key"
|
||||
return self.db_key
|
||||
#@key.setter
|
||||
def key_set(self, value):
|
||||
"Setter. Allows for self.key = value"
|
||||
self.db_key = value
|
||||
self.save()
|
||||
#@key.deleter
|
||||
def key_del(self):
|
||||
"Deleter. Allows for del self.key"
|
||||
raise Exception("You cannot delete the channel key!")
|
||||
key = property(key_get, key_set, key_del)
|
||||
|
||||
# desc property (wraps db_desc)
|
||||
#@property
|
||||
def desc_get(self):
|
||||
"Getter. Allows for value = self.desc"
|
||||
return self.db_desc
|
||||
#@desc.setter
|
||||
def desc_set(self, value):
|
||||
"Setter. Allows for self.desc = value"
|
||||
self.db_desc = value
|
||||
self.save()
|
||||
#@desc.deleter
|
||||
def desc_del(self):
|
||||
"Deleter. Allows for del self.desc"
|
||||
self.db_desc = ""
|
||||
self.save()
|
||||
desc = property(desc_get, desc_set, desc_del)
|
||||
|
||||
# aliases property
|
||||
#@property
|
||||
def aliases_get(self):
|
||||
"Getter. Allows for value = self.aliases. Returns a list of aliases."
|
||||
if self.db_aliases:
|
||||
return [perm.strip() for perm in self.db_aliases.split(',')]
|
||||
return []
|
||||
#@aliases.setter
|
||||
def aliases_set(self, value):
|
||||
"Setter. Allows for self.aliases = value. Stores as a comma-separated string."
|
||||
if is_iter(value):
|
||||
value = ",".join([str(val).strip().lower() for val in value])
|
||||
self.db_aliases = value
|
||||
self.save()
|
||||
#@aliases_del.deleter
|
||||
def aliases_del(self):
|
||||
"Deleter. Allows for del self.aliases"
|
||||
self.db_aliases = ""
|
||||
self.save()
|
||||
aliases = property(aliases_get, aliases_set, aliases_del)
|
||||
|
||||
# keep_log property (wraps db_keep_log)
|
||||
#@property
|
||||
def keep_log_get(self):
|
||||
"Getter. Allows for value = self.keep_log"
|
||||
return self.db_keep_log
|
||||
#@keep_log.setter
|
||||
def keep_log_set(self, value):
|
||||
"Setter. Allows for self.keep_log = value"
|
||||
self.db_keep_log = value
|
||||
self.save()
|
||||
#@keep_log.deleter
|
||||
def keep_log_del(self):
|
||||
"Deleter. Allows for del self.keep_log"
|
||||
self.db_keep_log = False
|
||||
self.save()
|
||||
keep_log = property(keep_log_get, keep_log_set, keep_log_del)
|
||||
|
||||
# permissions property
|
||||
#@property
|
||||
def permissions_get(self):
|
||||
"Getter. Allows for value = self.permissions. Returns a list of permissions."
|
||||
if self.db_permissions:
|
||||
return [perm.strip() for perm in self.db_permissions.split(',')]
|
||||
return []
|
||||
#@permissions.setter
|
||||
def permissions_set(self, value):
|
||||
"Setter. Allows for self.permissions = value. Stores as a comma-separated string."
|
||||
if is_iter(value):
|
||||
value = ",".join([str(val).strip().lower() for val in value])
|
||||
self.db_permissions = value
|
||||
self.save()
|
||||
#@permissions.deleter
|
||||
def permissions_del(self):
|
||||
"Deleter. Allows for del self.permissions"
|
||||
self.db_permissions = ""
|
||||
self.save()
|
||||
permissions = property(permissions_get, permissions_set, permissions_del)
|
||||
|
||||
class Meta:
|
||||
"Define Django meta options"
|
||||
verbose_name = "Channel"
|
||||
verbose_name_plural = "Channels"
|
||||
|
||||
#
|
||||
# Channel class methods
|
||||
#
|
||||
|
||||
def __str__(self):
|
||||
return "Channel '%s' (%s)" % (self.key, self.desc)
|
||||
|
||||
def has_connection(self, player):
|
||||
"""
|
||||
Checks so this player is actually listening
|
||||
to this channel.
|
||||
"""
|
||||
return Channel.objects.has_connection(player, self)
|
||||
|
||||
def msg(self, msgobj, from_obj=None):
|
||||
"""
|
||||
Send the given message to all players connected to channel. Note that
|
||||
no permission-checking is done here; it is assumed to have been
|
||||
done before calling this method.
|
||||
|
||||
msgobj - a Msg instance. May be a message string.
|
||||
from_obj - if msgobj is not an Msg-instance, this is used to create
|
||||
a message on the fly. The advantage of this is that such
|
||||
messages are logged.
|
||||
|
||||
"""
|
||||
if not type(msgobj) == Msg:
|
||||
# the given msgobj is not an Msg instance. If it is a string and from_obj
|
||||
# was given, we create the message on the fly instead.
|
||||
if from_obj and isinstance(msgobj, basestring):
|
||||
msgobj = Msg(db_sender=from_obj, db_message=msgobj)
|
||||
msgobj.save()
|
||||
msgobj.channels = [self]
|
||||
msg = msgobj.message
|
||||
else:
|
||||
# this just sends a message, without any sender
|
||||
# (and without storing it in a persistent Msg object)
|
||||
msg = str(msgobj)
|
||||
else:
|
||||
msg = msgobj.message
|
||||
|
||||
# get all players connected to this channel
|
||||
conns = Channel.objects.get_all_connections(self)
|
||||
|
||||
# send message to all connected players
|
||||
for conn in conns:
|
||||
for session in \
|
||||
sessionhandler.find_sessions_from_username(conn.player.name):
|
||||
session.msg(msg)
|
||||
return True
|
||||
|
||||
def connect_to(self, player):
|
||||
"Connect the user to this channel"
|
||||
if not has_perm(player, self, 'chan_listen'):
|
||||
return False
|
||||
conn = ChannelConnection.objects.create_connection(player, self)
|
||||
if conn:
|
||||
return True
|
||||
return False
|
||||
|
||||
def disconnect_from(self, player):
|
||||
"Disconnect user from this channel."
|
||||
ChannelConnection.objects.break_connection(player, self)
|
||||
|
||||
def delete(self):
|
||||
"Clean out all connections to this channel and delete it."
|
||||
for connection in Channel.objects.get_all_connections(self):
|
||||
connection.delete()
|
||||
super(Channel, self).delete()
|
||||
|
||||
|
||||
class ChannelConnection(SharedMemoryModel):
|
||||
"""
|
||||
This connects a user object to a particular comm channel.
|
||||
The advantage of making it like this is that one can easily
|
||||
break the connection just by deleting this object.
|
||||
"""
|
||||
# Player connected to a channel
|
||||
db_player = models.ForeignKey(PlayerDB)
|
||||
# Channel the player is connected to
|
||||
db_channel = models.ForeignKey(Channel)
|
||||
|
||||
# Database manager
|
||||
objects = managers.ChannelConnectionManager()
|
||||
|
||||
# player property (wraps db_player)
|
||||
#@property
|
||||
def player_get(self):
|
||||
"Getter. Allows for value = self.player"
|
||||
return self.db_player
|
||||
#@player.setter
|
||||
def player_set(self, value):
|
||||
"Setter. Allows for self.player = value"
|
||||
self.db_player = value
|
||||
self.save()
|
||||
#@player.deleter
|
||||
def player_del(self):
|
||||
"Deleter. Allows for del self.player. Deletes connection."
|
||||
self.delete()
|
||||
player = property(player_get, player_set, player_del)
|
||||
|
||||
# channel property (wraps db_channel)
|
||||
#@property
|
||||
def channel_get(self):
|
||||
"Getter. Allows for value = self.channel"
|
||||
return self.db_channel
|
||||
#@channel.setter
|
||||
def channel_set(self, value):
|
||||
"Setter. Allows for self.channel = value"
|
||||
self.db_channel = value
|
||||
self.save()
|
||||
#@channel.deleter
|
||||
def channel_del(self):
|
||||
"Deleter. Allows for del self.channel. Deletes connection."
|
||||
self.delete()
|
||||
channel = property(channel_get, channel_set, channel_del)
|
||||
|
||||
def __str__(self):
|
||||
return "Connection Player '%s' <-> %s" % (self.player, self.channel)
|
||||
|
||||
class Meta:
|
||||
"Define Django meta options"
|
||||
verbose_name = "Channel<->Player link"
|
||||
verbose_name_plural = "Channel<->Player links"
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue