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

@ -66,7 +66,7 @@ from src.settings_default import *
###################################################
###################################################
# Batch processor
# Batch processors
###################################################
###################################################
@ -74,7 +74,7 @@ from src.settings_default import *
###################################################
###################################################
# Game Permissions
# In-game access
###################################################
###################################################
@ -82,11 +82,7 @@ from src.settings_default import *
###################################################
###################################################
# IMC2 Configuration
###################################################
###################################################
# IRC config
# External Channel connections
###################################################
###################################################

View file

@ -87,6 +87,7 @@ class DefaultCmdSet(CmdSet):
self.add(comms.CmdChannelCreate())
self.add(comms.CmdCdesc())
self.add(comms.CmdPage())
self.add(comms.CmdIRC2Chan())
# Batchprocessor commands
self.add(batchprocess.CmdBatchCommands())

View file

@ -1,11 +1,13 @@
"""
Comsys command module.
"""
from src.comms.models import Channel, Msg, ChannelConnection
from django.conf import settings
from src.comms.models import Channel, Msg, PlayerChannelConnection, ExternalChannelConnection
from src.comms import irc
from src.comms.channelhandler import CHANNELHANDLER
from src.utils import create, utils
from src.commands.default.muxcommand import MuxCommand
from src.server.sessionhandler import SESSIONS
def find_channel(caller, channelname, silent=False):
"""
@ -263,7 +265,7 @@ class CmdChannels(MuxCommand):
caller.msg("No channels available")
return
# all channel we are already subscribed to
subs = [conn.channel for conn in ChannelConnection.objects.get_all_player_connections(caller.player)]
subs = [conn.channel for conn in PlayerChannelConnection.objects.get_all_player_connections(caller.player)]
if self.cmdstring != "comlist":
@ -823,3 +825,88 @@ class CmdPage(MuxCommand):
if rstrings:
caller.msg(rstrings = "\n".join(rstrings))
caller.msg("You paged %s with: '%s'." % (", ".join(received), message))
class CmdIRC2Chan(MuxCommand):
"""
@irc2chan - link evennia channel to an IRC channel
Usage:
@irc2chan[/switches] <evennia_channel> = <ircnetwork> <port> <#irchannel> <botname>
Switches:
/disconnect - this will delete the bot and remove the irc connection to the channel.
/remove - "
/list - show all irc<->evennia mappings
Example:
@irc2chan myircchan = irc.dalnet.net 6667 myevennia-channel evennia-bot
This creates an IRC bot that connects to a given IRC network and channel. It will
relay everything said in the evennia channel to the IRC channel and vice versa. The
bot will automatically connect at server start, so this comman need only be given once.
The /disconnect switch will permanently delete the bot. To only temporarily deactivate it,
use the @services command instead.
"""
key = "@irc2chan"
locks = "cmd:serversetting(IRC_ENABLED) and perm(Wizards)"
help_category = "Comms"
def func(self):
"Setup the irc-channel mapping"
if 'list' in self.switches:
# show all connections
connections = ExternalChannelConnection.objects.filter(db_external_key__startswith='irc_')
if connections:
cols = [["Evennia channel"], ["IRC channel"]]
for conn in connections:
cols[0].append(conn.channel.key)
cols[1].append(" ".join(conn.external_config.split('|')))
ftable = utils.format_table(cols)
string = ""
for ir, row in enumerate(ftable):
if ir == 0:
string += "{w%s{n" % "".join(row)
else:
string += "\n" + "".join(row)
self.caller.msg(string)
else:
self.caller.msg("No connections found.")
return
if not settings.IRC_ENABLED:
string = """IRC is not enabled. You need to activate it in game/settings.py."""
self.caller.msg(string)
return
if not self.args or not self.rhs:
string = "Usage: @irc2chan[/switches] <evennia_channel> = <ircnetwork> <port> <#irchannel> <botname>"
self.caller.msg(string)
return
channel = self.lhs
self.rhs = self.rhs.replace('#', ' ') # to avoid Python comment issues
try:
irc_network, irc_port, irc_channel, irc_botname = [part.strip() for part in self.rhs.split(None, 3)]
irc_channel = "#%s" % irc_channel
except Exception:
string = "IRC bot definition '%s' is not valid." % self.rhs
self.caller.msg(string)
return
if 'disconnect' in self.switches or 'remove' in self.switches or 'delete' in self.switches:
ok = irc.delete_connection(irc_network, irc_port, irc_channel, irc_botname)
if not ok:
self.caller.msg("IRC connection/bot could not be removed, does it exist?")
else:
self.caller.msg("IRC connection destroyed.")
return
channel = find_channel(self.caller, channel)
if not channel:
return
ok = irc.create_connection(channel, irc_network, irc_port, irc_channel, irc_botname)
if not ok:
self.caller.msg("This IRC connection already exists.")
return
self.caller.msg("Connection created. Starting IRC bot.")

View file

@ -19,7 +19,7 @@ def format_help_entry(title, help_text, aliases=None,
"""
This visually formats the help entry.
"""
string = "-"*70 + "\n"
string = "-"*78 + "\n"
if title:
string += "Help topic for {w%s{n" % (title.capitalize())
if aliases:
@ -30,7 +30,7 @@ def format_help_entry(title, help_text, aliases=None,
string += "\nSuggested:\n"
string += fill(", ".join(suggested))
string.strip()
string += "\n" + "-"*70
string += "\n" + "-"*78
return string
def format_help_list(hdict_cmds, hdict_db):

View file

@ -323,15 +323,17 @@ class CmdService(MuxCommand):
@service[/switch] <service>
Switches:
list - shows all available services (default)
start - activates a service
stop - stops a service
list - shows all available services
Service management system. Allows for the listing,
starting, and stopping of services.
starting, and stopping of services. If no switches
are given, services will be listed.
"""
key = "@service"
aliases = ["@services"]
locks = "cmd:perm(service) or perm(Immortals)"
help_category = "System"
@ -341,11 +343,9 @@ class CmdService(MuxCommand):
caller = self.caller
switches = self.switches
if not switches or \
switches[0] not in ["list","start","stop"]:
if switches and switches[0] not in ["list","start","stop"]:
caller.msg("Usage: @service/<start|stop|list> [service]")
return
switch = switches[0]
return
# get all services
sessions = caller.sessions
@ -353,19 +353,20 @@ class CmdService(MuxCommand):
return
service_collection = SESSIONS.server.services
if switch == "list":
if not switches or switches[0] == "list":
# Just display the list of installed services and their
# status, then exit.
string = "-" * 40
string += "\nService Listing"
string = "-" * 78
string += "\n{wServices{n (use @services/start|stop):"
for service in service_collection.services:
if service.running:
status = 'Running'
string += '\n * {g%s{n (%s)' % (service.name, status)
else:
status = 'Inactive'
string += '\n * %s (%s)' % (service.name, status)
string += "\n" + "-" * 40
string += '\n {R%s{n (%s)' % (service.name, status)
string += "\n" + "-" * 78
caller.msg(string)
return
@ -375,35 +376,34 @@ class CmdService(MuxCommand):
service = service_collection.getServiceNamed(self.args)
except Exception:
string = 'Invalid service name. This command is case-sensitive. '
string += 'See @service/list.'
string += 'See @service/list for valid services.'
caller.msg(string)
return
if switch == "stop":
if switches[0] == "stop":
# Stopping a service gracefully closes it and disconnects
# any connections (if applicable).
if not service.running:
caller.msg('That service is not currently running.')
return
# We don't want to kill the main Evennia TCPServer services
# here. If wanting to kill a listening port, one needs to
# do it through settings.py and a restart.
return
if service.name[:7] == 'Evennia':
string = "You can not stop Evennia TCPServer services this way."
string += "\nTo e.g. remove a listening port, change settings file and restart."
string = "You seem to be shutting down a core Evennia* service. Note that"
string += "Stopping some TCP port services will *not* disconnect users *already*"
string += "connected on those ports, but *may* instead cause spurious errors for them. To "
string += "safely and permanently remove ports, change settings file and restart the server."
caller.msg(string)
return
#comsys.cemit_mudinfo("%s is *Stopping* the service '%s'." % (sname, service.name)) #TODO!
service.stopService()
caller.msg("Stopping service '%s'." % self.args)
return
if switch == "start":
if switches[0] == "start":
#Starts a service.
if service.running:
caller.msg('That service is already running.')
return
#comsys.cemit_mudinfo("%s is *Starting* the service '%s'." % (sname,service.name)) #TODO!
return
caller.msg("Starting service '%s'." % self.args)
service.startService()
class CmdShutdown(MuxCommand):

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))

View file

View file

@ -1,11 +0,0 @@
"""
This sets up a few fields in the admin interface for connecting IRC channels
to evennia channels.
"""
from src.irc.models import IRCChannelMapping
from django.contrib import admin
class IRCChannelMappingAdmin(admin.ModelAdmin):
list_display = ('channel', 'irc_server_name',
'irc_channel_name', 'is_enabled')
admin.site.register(IRCChannelMapping, IRCChannelMappingAdmin)

View file

@ -1,101 +0,0 @@
"""
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.
"""
# TODO: This is deprecated!
from twisted.words.protocols import irc
from twisted.internet import protocol
from twisted.internet import reactor
from django.conf import settings
from src.irc.models import IRCChannelMapping
#from src import comsys
from src.utils import logger
#store all irc channels
IRC_CHANNELS = []
def cemit_info(message):
"""
Send info to default info channel
"""
comsys.send_cmessage(settings.COMMCHAN_IRC_INFO, 'IRC: %s' % message)
class IRC_Bot(irc.IRCClient):
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)
cemit_info("Client connecting to %s.'" % (self.factory.channel))
def joined(self, channel):
msg = "Joined %s/%s as '%s'." % (self.factory.network,channel,self.factory.nickname)
cemit_info(msg)
logger.log_infomsg(msg)
def privmsg(self, user, irc_channel, msg):
"Someone has written something in channel. Echo it to the evennia channel"
try:
#find irc->evennia channel mappings
mappings = IRCChannelMapping.objects.filter(irc_channel_name=irc_channel)
if not mappings:
return
#format message:
user = user.split("!")[0]
if user:
user.strip()
else:
user = "Unknown"
msg = "%s@%s: %s" % (user,irc_channel,msg.strip())
#logger.log_infomsg("<IRC: " + msg)
for mapping in mappings:
if mapping.channel:
comsys.send_cmessage(mapping.channel, msg, from_external="IRC")
except IRCChannelMapping.DoesNotExist:
#no mappings found. Ignore.
pass
def send_msg(self,msg):
"Called by evennia when sending something to mapped IRC channel"
self.msg(self.factory.channel, msg)
#logger.log_infomsg(">IRC: " + msg)
class IRC_BotFactory(protocol.ClientFactory):
protocol = IRC_Bot
def __init__(self, channel, network, nickname):
self.network = network
self.channel = channel
self.nickname = nickname
def clientConnectionLost(self, connector, reason):
from twisted.internet.error import ConnectionDone
if type(reason.type) == type(ConnectionDone):
cemit_info("Connection closed.")
else:
cemit_info("Lost connection (%s), reconnecting." % reason)
connector.connect()
def clientConnectionFailed(self, connector, reason):
msg = "Could not connect: %s" % reason
cemit_info(msg)
logger.log_errmsg(msg)
def connect_to_IRC(irc_network,irc_port,irc_channel,irc_bot_nick ):
"Create the bot instance and connect to the IRC network and channel."
connect = reactor.connectTCP(irc_network, irc_port,
IRC_BotFactory(irc_channel,irc_network,irc_bot_nick))

View file

@ -1,22 +0,0 @@
from django.db import models
from django.conf import settings
from src.comms.models import Channel
class IRCChannelMapping(models.Model):
"""
Each IRCChannelMapping object determines which in-game channel incoming
IRC messages are routed to.
"""
channel = models.ForeignKey(Channel)
irc_server_name = models.CharField(max_length=78)
irc_channel_name = models.CharField(max_length=78)
is_enabled = models.BooleanField(default=True)
class Meta:
verbose_name = "IRC Channel mapping"
verbose_name_plural = "IRC Channel mappings"
#permissions = settings.PERM_IRC
def __str__(self):
return "%s <-> %s (%s)" % (self.channel, self.irc_channel_name,
self.irc_server_name)

View file

@ -317,3 +317,30 @@ def superuser(*args, **kwargs):
"""
return False
def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
"""
Only returns true if the Evennia settings exists, alternatively has a certain value.
Usage:
serversetting(IRC_ENABLED)
serversetting(BASE_SCRIPT_PATH, game.gamesrc.scripts)
A given True/False or integers will be converted properly.
"""
if not args:
return False
if len(args) < 2:
setting = args[0]
val = "True"
else:
setting, val = args[0], args[1]
# convert
if val == 'True':
val = True
elif val == 'False':
val = False
elif val.isdigit():
val = int(val)
if setting in settings._wrapped.__dict__:
return settings._wrapped.__dict__[setting] == val
return False

View file

@ -112,10 +112,10 @@ def create_channels():
# connect the god user to all these channels by default.
goduser = get_god_user()
from src.comms.models import ChannelConnection
ChannelConnection.objects.create_connection(goduser, pchan)
ChannelConnection.objects.create_connection(goduser, ichan)
ChannelConnection.objects.create_connection(goduser, cchan)
from src.comms.models import PlayerChannelConnection
PlayerChannelConnection.objects.create_connection(goduser, pchan)
PlayerChannelConnection.objects.create_connection(goduser, ichan)
PlayerChannelConnection.objects.create_connection(goduser, cchan)
def import_MUX_help_files():
"""
@ -237,11 +237,11 @@ def handle_setup(last_step):
for profile in PlayerDB.objects.all():
profile.delete()
elif last_step + num == 3:
from src.comms.models import Channel, ChannelConnection
from src.comms.models import Channel, PlayerChannelConnection
for chan in Channel.objects.all():
chan.delete()
for conn in ChannelConnection.objects.all():
for conn in PlayerChannelConnection.objects.all():
conn.delete()

View file

@ -195,7 +195,7 @@ if TELNET_ENABLED:
factory = protocol.ServerFactory()
factory.protocol = telnet.TelnetProtocol
telnet_service = internet.TCPServer(port, factory)
telnet_service.setName('Evennia%s' % port)
telnet_service.setName('EvenniaTelnet%s' % port)
EVENNIA.services.addService(telnet_service)
if WEBSERVER_ENABLED:
@ -244,11 +244,5 @@ if IRC_ENABLED:
# IRC channel connections
from src.irc.connection import IRC_BotFactory
irc = internet.TCPClient(settings.IRC_NETWORK,
settings.IRC_PORT,
IRC_BotFactory(settings.IRC_CHANNEL,
settings.IRC_NETWORK,
settings.IRC_NICKNAME))
irc.setName("%s:%s" % ("IRC", settings.IRC_CHANNEL))
EVENNIA.services.addService(irc)
from src.comms import irc
irc.connect_all()

View file

@ -162,10 +162,10 @@ CMDSET_UNLOGGEDIN = "game.gamesrc.commands.basecmdset.UnloggedinCmdSet"
# Default set for logged in players (fallback)
CMDSET_DEFAULT = "game.gamesrc.commands.basecmdset.DefaultCmdSet"
###################################################
# Default Object typeclasses
###################################################
# Note that all typeclasses must originally
# inherit from src.objects.objects.Object somewhere in
# their path.
@ -256,9 +256,18 @@ CHANNEL_CONNECTINFO = ("MUDconnections", ('connections, mud_conns'),
"admin:perm(Immortals);listen:perm(Wizards);send:false()")
###################################################
# IMC2 Configuration
# External Channel connections
###################################################
# Evennia can connect to external IRC channels and
# echo what is said on the channel to IRC and vice
# versa. Obs - make sure the IRC network allows bots.
# If disabled, the default @irc2chan command won't be
# available in-game.
IRC_ENABLED = True
# OBS: IMC is not implemented at this point!
# IMC (Inter-MUD communication) allows for an evennia chat channel
# that connects to people on other MUDs also using the IMC. Your
# evennia server do *not* have to be open to the public to use IMC; it
@ -292,28 +301,6 @@ IMC2_DEBUG = False
# This isn't something you should generally change.
IMC2_PROTOCOL_VERSION = '2'
###################################################
# IRC config
###################################################
# This allows your evennia channels to connect to an external IRC
# channel. Evennia will connect under a nickname that then echoes what is
# said on the channel to IRC and vice versa.
# Obs - make sure the IRC network allows bots.
# Activate the IRC bot.
IRC_ENABLED = False
# Which channel to tie to the irc channel
COMMCHAN_IRC_INFO = 'MUDInfo'
# Which IRC network (e.g. irc.freenode.net)
IRC_NETWORK = "irc.freenode.net"
# Which IRC port to connect to (e.g. 6667)
IRC_PORT = 6667
# Which channel on the network to connect to (including the #)
IRC_CHANNEL = ""
# Under what nickname should Evennia connect to the channel
IRC_NICKNAME = ""
###################################################
# Config for Django web features
@ -425,9 +412,7 @@ INSTALLED_APPS = (
'src.config',
'src.players',
'src.objects',
'src.comms',
'src.imc2',
'src.irc',
'src.comms',
'src.help',
'src.scripts',
'src.web.news',