Obs:Migrate. Made Comm system more generic, including the ability to connect arbitrary protocols to channels. Re-worked the IRC connectivity system - you can now again communicate between IRC and in-game evennia channels.

This commit is contained in:
Griatch 2011-04-10 12:39:07 +00:00
parent c81d238b0c
commit 52785e8f3e
20 changed files with 960 additions and 258 deletions

View file

@ -4,7 +4,7 @@
#
from django.contrib import admin
from src.comms.models import Channel, Msg, ChannelConnection
from src.comms.models import Channel, Msg, PlayerChannelConnection, ExternalChannelConnection
class MsgAdmin(admin.ModelAdmin):
list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers', 'db_channels', 'db_message', 'db_lock_storage')
@ -27,13 +27,23 @@ class ChannelAdmin(admin.ModelAdmin):
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)
class PlayerChannelConnectionAdmin(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(PlayerChannelConnection, PlayerChannelConnectionAdmin)
class ExternalChannelConnectionAdmin(admin.ModelAdmin):
list_display = ('db_channel', 'db_external_key', 'db_external_config')
list_display_links = ("db_channel", 'db_external_key', 'db_external_config')
ordering = ["db_channel"]
search_fields = ['db_channel', 'db_external_key']
save_as = True
save_on_top = True
list_select_related = True
admin.site.register(ExternalChannelConnection, ExternalChannelConnectionAdmin)

View file

@ -84,7 +84,7 @@ class ChannelCommand(command.Command):
msgobj.save()
msgobj.channels = channel
# send new message object to channel
channel.msg(msgobj)
channel.msg(msgobj, from_obj=caller.player)
class ChannelHandler(object):
"""

178
src/comms/irc.py Normal file
View file

@ -0,0 +1,178 @@
"""
This connects to an IRC network/channel and launches an 'bot' onto it.
The bot then pipes what is being said between the IRC channel and one or
more Evennia channels.
"""
from twisted.application import internet
from twisted.words.protocols import irc
from twisted.internet import protocol
from django.conf import settings
from src.comms.models import ExternalChannelConnection, Channel
from src.utils import logger, utils
from src.server.sessionhandler import SESSIONS
INFOCHANNEL = Channel.objects.channel_search(settings.CHANNEL_MUDINFO[0])
IRC_CHANNELS = []
def msg_info(message):
"""
Send info to default info channel
"""
message = '[%s][IRC]: %s' % (INFOCHANNEL[0].key, message)
try:
INFOCHANNEL[0].msg(message)
except AttributeError:
logger.log_infomsg("MUDinfo (irc): %s" % message)
class IRC_Bot(irc.IRCClient):
"""
This defines an IRC bot that connects to an IRC channel
and relays data to and from an evennia game.
"""
def _get_nickname(self):
"required for correct nickname setting"
return self.factory.nickname
nickname = property(_get_nickname)
def signedOn(self):
global IRC_CHANNELS
self.join(self.factory.channel)
# This is the first point the protocol is instantiated.
# add this protocol instance to the global list so we
# can access it later to send data.
IRC_CHANNELS.append(self)
#msg_info("Client connecting to %s.'" % (self.factory.channel))
def joined(self, channel):
msg = "joined %s." % self.factory.pretty_key
msg_info(msg)
logger.log_infomsg(msg)
def privmsg(self, user, irc_channel, msg):
"Someone has written something in irc channel. Echo it to the evennia channel"
#find irc->evennia channel mappings
conns = ExternalChannelConnection.objects.filter(db_external_key=self.factory.key)
if not conns:
return
#format message:
user = user.split("!")[0]
if user:
user.strip()
else:
user = "Unknown"
msg = "[%s] %s@%s: %s" % (self.factory.evennia_channel, user, irc_channel, msg.strip())
#logger.log_infomsg("<IRC: " + msg)
for conn in conns:
if conn.channel:
conn.to_channel(msg)
def msg_irc(self, msg, from_obj=None):
"""
Called by evennia when sending something to mapped IRC channel.
Note that this cannot simply be called msg() since that's the
name of of the twisted irc hook as well, this leads to some
initialization messages to be sent without checks, causing loops.
"""
self.msg(utils.to_str(self.factory.channel), utils.to_str(msg))
class Factory(protocol.ClientFactory):
protocol = IRC_Bot
def __init__(self, key, channel, network, port, nickname, evennia_channel):
self.key = key
self.pretty_key = "%s:%s%s ('%s')" % (network, port, channel, nickname)
self.network = network
self.port = port
self.channel = channel
self.nickname = nickname
self.evennia_channel = evennia_channel
def clientConnectionLost(self, connector, reason):
from twisted.internet.error import ConnectionDone
if type(reason.type) == type(ConnectionDone):
msg_info("Connection closed.")
else:
msg_info("Lost connection %s. Reason: '%s'. Reconnecting." % (self.pretty_key, reason))
connector.connect()
def clientConnectionFailed(self, connector, reason):
msg = "Could not connect %s Reason: '%s'" % (self.pretty_key, reason)
msg_info(msg)
logger.log_errmsg(msg)
def build_connection_key(irc_network, irc_port, irc_channel, irc_bot_nick):
"Build an id hash for the connection"
return "irc_%s:%s%s(%s)" % (irc_network, irc_port, irc_channel, irc_bot_nick)
def build_service_key(key):
return "IRCbot:%s" % key
def create_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
"""
This will create a new IRC<->channel connection.
"""
if not type(channel) == Channel:
new_channel = Channel.objects.filter(db_key=channel)
if not new_channel:
logger.log_errmsg("Cannot attach IRC<->Evennia: Evennia Channel '%s' not found" % channel)
return False
channel = new_channel[0]
key = build_connection_key(irc_network, irc_port, irc_channel, irc_bot_nick)
old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
if old_conns:
return False
config = "%s|%s|%s|%s" % (irc_network, irc_port, irc_channel, irc_bot_nick)
# how the channel will be able to contact this protocol
send_code = "from src.comms.irc import IRC_CHANNELS\n"
send_code += "matched_ircs = [irc for irc in IRC_CHANNELS if irc.factory.key == '%s']\n" % key
send_code += "[irc.msg_irc(message, from_obj=from_obj) for irc in matched_ircs]\n"
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
db_external_config=config)
conn.save()
# connect
connect_to_irc(conn)
return True
def delete_connection(irc_network, irc_port, irc_channel, irc_bot_nick):
"Destroy a connection"
key = build_connection_key(irc_network, irc_port, irc_channel, irc_bot_nick)
service_key = build_service_key(key)
try:
conn = ExternalChannelConnection.objects.get(db_external_key=key)
except Exception:
return False
conn.delete()
try:
service = SESSIONS.server.services.getServiceNamed(service_key)
except Exception:
return True
if service.running:
SESSIONS.server.services.removeService(service)
return True
def connect_to_irc(connection):
"Create the bot instance and connect to the IRC network and channel."
# get config
key = utils.to_str(connection.external_key)
service_key = build_service_key(key)
irc_network, irc_port, irc_channel, irc_bot_nick = [utils.to_str(conf) for conf in connection.external_config.split('|')]
# connect
bot = internet.TCPClient(irc_network, int(irc_port), Factory(key, irc_channel, irc_network, irc_port, irc_bot_nick,
connection.channel.key))
bot.setName(service_key)
SESSIONS.server.services.addService(bot)
def connect_all():
"""
Activate all irc bots.
Returns a list of (key, TCPClient) tuples for server to properly set services.
"""
for connection in ExternalChannelConnection.objects.filter(db_external_key__startswith='irc_'):
connect_to_irc(connection)

View file

@ -2,6 +2,7 @@
These managers handles the
"""
import itertools
from django.db import models
from django.contrib.contenttypes.models import ContentType
from src.utils.utils import is_iter
@ -30,6 +31,13 @@ def to_object(inp, objtype='player'):
umatch = PlayerDB.objects.filter(user__username__iexact=inp)
if umatch:
return umatch[0]
elif objtype == 'external':
from src.comms.models import ExternalChannelConnection
if type (inp) == ExternalChannelConnection:
return inp
umatch = ExternalChannelConnection.objects.filter(db_key=inp)
if umatch:
return umatch[0]
else:
# have to import this way to avoid circular imports
from src.comms.models import Channel
@ -239,10 +247,13 @@ class ChannelManager(models.Manager):
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)
#from src.comms.models import PlayerChannelConnection
PlayerChannelConnection = ContentType.objects.get(app_label="comms",
model="playerchannelconnection").model_class()
ExternalChannelConnection = ContentType.objects.get(app_label="comms",
model="externalchannelconnection").model_class()
return itertools.chain(PlayerChannelConnection.objects.get_all_connections(channel),
ExternalChannelConnection.objects.get_all_connections(channel))
def channel_search(self, ostring):
"""
@ -266,9 +277,9 @@ class ChannelManager(models.Manager):
return channels
#
# ChannelConnection manager
# PlayerChannelConnection manager
#
class ChannelConnectionManager(models.Manager):
class PlayerChannelConnectionManager(models.Manager):
"""
This handles all connections between a player and
a channel.
@ -316,4 +327,51 @@ class ChannelConnectionManager(models.Manager):
conns = self.filter(db_player=player).filter(db_channel=channel)
for conn in conns:
conn.delete()
class ExternalChannelConnectionManager(models.Manager):
"""
This handles all connections between a external and
a channel.
"""
def get_all_external_connections(self, external):
"Get all connections that the given external."
external = to_object(external, objtype='external')
return self.filter(db_external_key=external)
def has_connection(self, external, channel):
"Checks so a connection exists external<->channel"
external = to_object(external, objtype='external')
channel = to_object(channel, objtype="channel")
if external and channel:
return self.filter(db_external_key=external).filter(db_channel=channel).count() > 0
return False
def get_all_connections(self, channel):
"""
Get all connections for a channel
"""
channel = to_object(channel, objtype='channel')
return self.filter(db_channel=channel)
def create_connection(self, external, channel, config=""):
"""
Connect a external to a channel. external and channel
can be actual objects or keystrings.
"""
channel = to_object(channel, objtype='channel')
if not channel:
raise CommError("NOTFOUND")
new_connection = self.model(db_external_key=external, db_channel=channel, db_external_config=config)
new_connection.save()
return new_connection
def break_connection(self, external, channel):
"Remove link between external and channel"
external = to_object(external)
channel = to_object(channel, objtype='channel')
if not external or not channel:
raise CommError("NOTFOUND")
conns = self.filter(db_external_key=external).filter(db_channel=channel)
for conn in conns:
conn.delete()

View file

@ -0,0 +1,176 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'ExternalChannelConnection'
db.create_table('comms_externalchannelconnection', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('db_channel', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['comms.Channel'])),
('db_external_key', self.gf('django.db.models.fields.CharField')(max_length=128)),
('db_external_path', self.gf('django.db.models.fields.CharField')(max_length=128)),
('db_external_config', self.gf('django.db.models.fields.TextField')(blank=True)),
('db_is_enabled', self.gf('django.db.models.fields.BooleanField')(default=True)),
))
db.send_create_signal('comms', ['ExternalChannelConnection'])
db.rename_table('comms_channelconnection', 'comms_playerchannelconnection')
# # Adding model 'PlayerChannelConnection'
# db.create_table('comms_playerchannelconnection', (
# ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
# ('db_player', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['players.PlayerDB'])),
# ('db_channel', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['comms.Channel'])),
# ))
# db.send_create_signal('comms', ['PlayerChannelConnection'])
# # move channelconnections to playerchannelconnections
# for conn in orm.ChannelConnection.objects.all():
# ncon = orm.PlayerChannelConnection(db_player=conn.db_player, db_channel=conn.db_channel)
# ncon.save()
# db
# # Deleting model 'ChannelConnection'
# db.delete_table('comms_channelconnection')
# Adding field 'Msg.db_sender_external'
db.add_column('comms_msg', 'db_sender_external', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False)
# Changing field 'Msg.db_sender'
db.alter_column('comms_msg', 'db_sender_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['players.PlayerDB']))
def backwards(self, orm):
# # Adding model 'ChannelConnection'
# db.create_table('comms_channelconnection', (
# ('db_channel', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['comms.Channel'])),
# ('db_player', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['players.PlayerDB'])),
# ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
# ))
# db.send_create_signal('comms', ['ChannelConnection'])
#
db.rename_table('comms_playerchannelconnection', 'comms_channelconnection')
# Deleting model 'ExternalChannelConnection'
db.delete_table('comms_externalchannelconnection')
# # Deleting model 'PlayerChannelConnection'
# db.delete_table('comms_playerchannelconnection')
# Deleting field 'Msg.db_sender_external'
db.delete_column('comms_msg', 'db_sender_external')
# User chose to not deal with backwards NULL issues for 'Msg.db_sender'
raise RuntimeError("Cannot reverse this migration. 'Msg.db_sender' and its values cannot be restored.")
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'comms.channel': {
'Meta': {'object_name': 'Channel'},
'db_aliases': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_desc': ('django.db.models.fields.CharField', [], {'max_length': '80', 'null': 'True', 'blank': 'True'}),
'db_keep_log': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'comms.externalchannelconnection': {
'Meta': {'object_name': 'ExternalChannelConnection'},
'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['comms.Channel']"}),
'db_external_config': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_external_key': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'db_external_path': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'db_is_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'comms.msg': {
'Meta': {'object_name': 'Msg'},
'db_channels': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'db_date_sent': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_hide_from_channels': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'db_hide_from_receivers': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'db_hide_from_sender': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_message': ('django.db.models.fields.TextField', [], {}),
'db_receivers': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'db_sender': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sender_set'", 'null': 'True', 'to': "orm['players.PlayerDB']"}),
'db_sender_external': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'comms.playerchannelconnection': {
'Meta': {'object_name': 'PlayerChannelConnection'},
'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['comms.Channel']"}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'objects.objectdb': {
'Meta': {'object_name': 'ObjectDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerdb': {
'Meta': {'object_name': 'PlayerDB'},
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
}
}
complete_apps = ['comms']

View file

@ -0,0 +1,129 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting field 'ExternalChannelConnection.db_external_path'
db.delete_column('comms_externalchannelconnection', 'db_external_path')
# Adding field 'ExternalChannelConnection.db_external_send_code'
db.add_column('comms_externalchannelconnection', 'db_external_send_code', self.gf('django.db.models.fields.TextField')(default='', blank=True), keep_default=False)
def backwards(self, orm):
# User chose to not deal with backwards NULL issues for 'ExternalChannelConnection.db_external_path'
raise RuntimeError("Cannot reverse this migration. 'ExternalChannelConnection.db_external_path' and its values cannot be restored.")
# Deleting field 'ExternalChannelConnection.db_external_send_code'
db.delete_column('comms_externalchannelconnection', 'db_external_send_code')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'comms.channel': {
'Meta': {'object_name': 'Channel'},
'db_aliases': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_desc': ('django.db.models.fields.CharField', [], {'max_length': '80', 'null': 'True', 'blank': 'True'}),
'db_keep_log': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'comms.externalchannelconnection': {
'Meta': {'object_name': 'ExternalChannelConnection'},
'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['comms.Channel']"}),
'db_external_config': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_external_key': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'db_external_send_code': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_is_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'comms.msg': {
'Meta': {'object_name': 'Msg'},
'db_channels': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'db_date_sent': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_hide_from_channels': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'db_hide_from_receivers': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'db_hide_from_sender': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_message': ('django.db.models.fields.TextField', [], {}),
'db_receivers': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'db_sender': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sender_set'", 'null': 'True', 'to': "orm['players.PlayerDB']"}),
'db_sender_external': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'comms.playerchannelconnection': {
'Meta': {'object_name': 'PlayerChannelConnection'},
'db_channel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['comms.Channel']"}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'objects.objectdb': {
'Meta': {'object_name': 'ObjectDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerdb': {
'Meta': {'object_name': 'PlayerDB'},
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
}
}
complete_apps = ['comms']

View file

@ -16,10 +16,11 @@ be able to delete connections on the fly).
from django.db import models
from src.utils.idmapper.models import SharedMemoryModel
from src.server.sessionhandler import SESSIONS
#from src.server.sessionhandler import SESSIONS
from src.comms import managers
from src.locks.lockhandler import LockHandler
from src.utils.utils import is_iter
from src.utils import logger
from src.utils.utils import is_iter, to_str
from src.utils.utils import dbref as is_dbref
@ -88,7 +89,9 @@ class Msg(SharedMemoryModel):
# named same as the field, but withtout the db_* prefix.
# There must always be one sender of the message.
db_sender = models.ForeignKey("players.PlayerDB", related_name='sender_set')
db_sender = models.ForeignKey("players.PlayerDB", related_name='sender_set', null=True)
# in the case of external senders, no Player object might be available
db_sender_external = models.CharField(max_length=255, null=True, blank=True)
# The destination objects of this message. Stored as a
# comma-separated string of object dbrefs. Can be defined along
# with channels below.
@ -149,6 +152,22 @@ class Msg(SharedMemoryModel):
raise Exception("You cannot delete the sender of a message!")
sender = property(sender_get, sender_set, sender_del)
# sender_external property (wraps db_sender_external)
#@property
def sender_external_get(self):
"Getter. Allows for value = self.sender_external"
return self.db_sender_external
#@sender_external.setter
def sender_external_set(self, value):
"Setter. Allows for self.sender_external = value"
self.db_sender_external = value
self.save()
#@sender_external.deleter
def sender_external_del(self):
"Deleter. Allows for del self.sender_external"
raise Exception("You cannot delete the sender_external of a message!")
sender_external = property(sender_external_get, sender_external_set, sender_external_del)
# receivers property
#@property
def receivers_get(self):
@ -323,6 +342,29 @@ class Msg(SharedMemoryModel):
return self.locks.check(accessing_obj, access_type=access_type, default=default)
#------------------------------------------------------------
#
# TempMsg
#
#------------------------------------------------------------
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.
"""
def __init__(self, sender=None, receivers=[], channels=[], message="", permissions=[]):
self.sender = sender
self.receivers = receivers
self.message = message
self.permissions = permissions
self.hide_from_sender = False
self.hide_from_sender = receivers = False
self.hide_from_channels = False
#------------------------------------------------------------
#
# Channel
@ -482,7 +524,7 @@ class Channel(SharedMemoryModel):
Checks so this player is actually listening
to this channel.
"""
return ChannelConnection.objects.has_connection(player, self)
return PlayerChannelConnection.objects.has_connection(player, self)
def msg(self, msgobj, from_obj=None):
"""
@ -490,49 +532,64 @@ class Channel(SharedMemoryModel):
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.
msgobj - a Msg instance or a message string. In the latter case a Msg will be created.
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)
a message on the fly. If from_obj is None, no Msg object will
be created and the message will be sent without being logged.
"""
if isinstance(msgobj, basestring):
# given msgobj is a string
if from_obj:
if isinstance(from_obj, basestring):
msgobj = Msg(db_sender_external=from_obj, db_message=msgobj)
else:
msgobj = Msg(db_sender=from_obj, db_message=msgobj)
# try to use
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)
msg = to_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 \
SESSIONS.sessions_from_player(conn.player):
session.msg(msg)
# get all players connected to this channel and send to them
for conn in Channel.objects.get_all_connections(self):
try:
conn.player.msg(msg, from_obj)
except AttributeError:
try:
conn.to_external(msg, from_obj)
except Exception:
logger.log_trace("Cannot send msg to connection '%s'" % conn)
return True
def tempmsg(self, message):
"""
A wrapper for sending non-persistent messages. Nothing
will be stored in the database.
message - a Msg object or a text string.
"""
if type(msgobj) == Msg:
# extract only the string
message = message.message
return self.msg(message)
def connect_to(self, player):
"Connect the user to this channel"
if not self.access(player, 'listen'):
return False
conn = ChannelConnection.objects.create_connection(player, self)
conn = PlayerChannelConnection.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)
PlayerChannelConnection.objects.break_connection(player, self)
def delete(self):
"Clean out all connections to this channel and delete it."
@ -548,19 +605,20 @@ class Channel(SharedMemoryModel):
"""
return self.locks.check(accessing_obj, access_type=access_type, default=default)
class ChannelConnection(SharedMemoryModel):
class PlayerChannelConnection(SharedMemoryModel):
"""
This connects a user object to a particular comm channel.
This connects a player 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("players.PlayerDB")
# Channel the player is connected to
db_channel = models.ForeignKey(Channel)
# Database manager
objects = managers.ChannelConnectionManager()
objects = managers.PlayerChannelConnectionManager()
# player property (wraps db_player)
#@property
@ -602,3 +660,140 @@ class ChannelConnection(SharedMemoryModel):
verbose_name = "Channel<->Player link"
verbose_name_plural = "Channel<->Player links"
class ExternalChannelConnection(SharedMemoryModel):
"""
This defines an external protocol connecting to
a channel, while storing some critical info about
that connection.
"""
# evennia channel connecting to
db_channel = models.ForeignKey(Channel)
# external connection identifier
db_external_key = models.CharField(max_length=128)
# eval-code to use when the channel tries to send a message
# to the external protocol.
db_external_send_code = models.TextField(blank=True)
# custom config for the connection
db_external_config = models.TextField(blank=True)
# activate the connection
db_is_enabled = models.BooleanField(default=True)
objects = managers.ExternalChannelConnectionManager()
class Meta:
verbose_name = "External Channel Connection"
verbose_name_plural = "External Channel Connections"
def __str__(self):
return "%s <-> external %s" % (self.channel.key, self.db_external_key)
# 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)
# external_key property (wraps db_external_key)
#@property
def external_key_get(self):
"Getter. Allows for value = self.external_key"
return self.db_external_key
#@external_key.setter
def external_key_set(self, value):
"Setter. Allows for self.external_key = value"
self.db_external_key = value
self.save()
#@external_key.deleter
def external_key_del(self):
"Deleter. Allows for del self.external_key. Deletes connection."
self.delete()
external_key = property(external_key_get, external_key_set, external_key_del)
# external_send_code property (wraps db_external_send_code)
#@property
def external_send_code_get(self):
"Getter. Allows for value = self.external_send_code"
return self.db_external_send_code
#@external_send_code.setter
def external_send_code_set(self, value):
"Setter. Allows for self.external_send_code = value"
self.db_external_send_code = value
self.save()
#@external_send_code.deleter
def external_send_code_del(self):
"Deleter. Allows for del self.external_send_code. Deletes connection."
self.db_external_send_code = ""
self.save()
external_send_code = property(external_send_code_get, external_send_code_set, external_send_code_del)
# external_config property (wraps db_external_config)
#@property
def external_config_get(self):
"Getter. Allows for value = self.external_config"
return self.db_external_config
#@external_config.setter
def external_config_set(self, value):
"Setter. Allows for self.external_config = value"
self.db_external_config = value
self.save()
#@external_config.deleter
def external_config_del(self):
"Deleter. Allows for del self.external_config. Deletes connection."
self.db_external_config = ""
self.save()
external_config = property(external_config_get, external_config_set, external_config_del)
# is_enabled property (wraps db_is_enabled)
#@property
def is_enabled_get(self):
"Getter. Allows for value = self.is_enabled"
return self.db_is_enabled
#@is_enabled.setter
def is_enabled_set(self, value):
"Setter. Allows for self.is_enabled = value"
self.db_is_enabled = value
self.save()
#@is_enabled.deleter
def is_enabled_del(self):
"Deleter. Allows for del self.is_enabled. Deletes connection."
self.delete()
is_enabled = property(is_enabled_get, is_enabled_set, is_enabled_del)
#
# methods
#
def to_channel(self, message, from_obj=None):
"Send external -> channel"
if not from_obj:
from_obj = self.external_key
self.channel.msg(message, from_obj=from_obj)
def to_external(self, message, from_obj=None):
"Send channel -> external"
# make sure we are not echoing back our own message to ourselves
# (this would result in a nasty infinite loop)
if from_obj == self.external_key:
return
try:
# we execute the code snippet that should make it possible for the
# connection to contact the protocol correctly (as set by the protocol).
# Note that the code block has access to the variables here, such
# as message and from_obj.
exec(to_str(self.external_send_code))
except Exception:
logger.log_trace("Channel %s could not send to External %s" % (self.channel, self.external_key))