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:
parent
c81d238b0c
commit
52785e8f3e
20 changed files with 960 additions and 258 deletions
|
|
@ -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
|
# External Channel connections
|
||||||
###################################################
|
|
||||||
|
|
||||||
###################################################
|
|
||||||
# IRC config
|
|
||||||
###################################################
|
###################################################
|
||||||
|
|
||||||
###################################################
|
###################################################
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@ class DefaultCmdSet(CmdSet):
|
||||||
self.add(comms.CmdChannelCreate())
|
self.add(comms.CmdChannelCreate())
|
||||||
self.add(comms.CmdCdesc())
|
self.add(comms.CmdCdesc())
|
||||||
self.add(comms.CmdPage())
|
self.add(comms.CmdPage())
|
||||||
|
self.add(comms.CmdIRC2Chan())
|
||||||
|
|
||||||
# Batchprocessor commands
|
# Batchprocessor commands
|
||||||
self.add(batchprocess.CmdBatchCommands())
|
self.add(batchprocess.CmdBatchCommands())
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
"""
|
"""
|
||||||
Comsys command module.
|
Comsys command module.
|
||||||
"""
|
"""
|
||||||
|
from django.conf import settings
|
||||||
from src.comms.models import Channel, Msg, ChannelConnection
|
from src.comms.models import Channel, Msg, PlayerChannelConnection, ExternalChannelConnection
|
||||||
|
from src.comms import irc
|
||||||
from src.comms.channelhandler import CHANNELHANDLER
|
from src.comms.channelhandler import CHANNELHANDLER
|
||||||
from src.utils import create, utils
|
from src.utils import create, utils
|
||||||
from src.commands.default.muxcommand import MuxCommand
|
from src.commands.default.muxcommand import MuxCommand
|
||||||
|
from src.server.sessionhandler import SESSIONS
|
||||||
|
|
||||||
def find_channel(caller, channelname, silent=False):
|
def find_channel(caller, channelname, silent=False):
|
||||||
"""
|
"""
|
||||||
|
|
@ -263,7 +265,7 @@ class CmdChannels(MuxCommand):
|
||||||
caller.msg("No channels available")
|
caller.msg("No channels available")
|
||||||
return
|
return
|
||||||
# all channel we are already subscribed to
|
# 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":
|
if self.cmdstring != "comlist":
|
||||||
|
|
||||||
|
|
@ -823,3 +825,88 @@ class CmdPage(MuxCommand):
|
||||||
if rstrings:
|
if rstrings:
|
||||||
caller.msg(rstrings = "\n".join(rstrings))
|
caller.msg(rstrings = "\n".join(rstrings))
|
||||||
caller.msg("You paged %s with: '%s'." % (", ".join(received), message))
|
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.")
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ def format_help_entry(title, help_text, aliases=None,
|
||||||
"""
|
"""
|
||||||
This visually formats the help entry.
|
This visually formats the help entry.
|
||||||
"""
|
"""
|
||||||
string = "-"*70 + "\n"
|
string = "-"*78 + "\n"
|
||||||
if title:
|
if title:
|
||||||
string += "Help topic for {w%s{n" % (title.capitalize())
|
string += "Help topic for {w%s{n" % (title.capitalize())
|
||||||
if aliases:
|
if aliases:
|
||||||
|
|
@ -30,7 +30,7 @@ def format_help_entry(title, help_text, aliases=None,
|
||||||
string += "\nSuggested:\n"
|
string += "\nSuggested:\n"
|
||||||
string += fill(", ".join(suggested))
|
string += fill(", ".join(suggested))
|
||||||
string.strip()
|
string.strip()
|
||||||
string += "\n" + "-"*70
|
string += "\n" + "-"*78
|
||||||
return string
|
return string
|
||||||
|
|
||||||
def format_help_list(hdict_cmds, hdict_db):
|
def format_help_list(hdict_cmds, hdict_db):
|
||||||
|
|
|
||||||
|
|
@ -323,15 +323,17 @@ class CmdService(MuxCommand):
|
||||||
@service[/switch] <service>
|
@service[/switch] <service>
|
||||||
|
|
||||||
Switches:
|
Switches:
|
||||||
|
list - shows all available services (default)
|
||||||
start - activates a service
|
start - activates a service
|
||||||
stop - stops a service
|
stop - stops a service
|
||||||
list - shows all available services
|
|
||||||
|
|
||||||
Service management system. Allows for the listing,
|
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"
|
key = "@service"
|
||||||
|
aliases = ["@services"]
|
||||||
locks = "cmd:perm(service) or perm(Immortals)"
|
locks = "cmd:perm(service) or perm(Immortals)"
|
||||||
help_category = "System"
|
help_category = "System"
|
||||||
|
|
||||||
|
|
@ -341,11 +343,9 @@ class CmdService(MuxCommand):
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
switches = self.switches
|
switches = self.switches
|
||||||
|
|
||||||
if not switches or \
|
if switches and switches[0] not in ["list","start","stop"]:
|
||||||
switches[0] not in ["list","start","stop"]:
|
|
||||||
caller.msg("Usage: @service/<start|stop|list> [service]")
|
caller.msg("Usage: @service/<start|stop|list> [service]")
|
||||||
return
|
return
|
||||||
switch = switches[0]
|
|
||||||
|
|
||||||
# get all services
|
# get all services
|
||||||
sessions = caller.sessions
|
sessions = caller.sessions
|
||||||
|
|
@ -353,19 +353,20 @@ class CmdService(MuxCommand):
|
||||||
return
|
return
|
||||||
service_collection = SESSIONS.server.services
|
service_collection = SESSIONS.server.services
|
||||||
|
|
||||||
if switch == "list":
|
if not switches or switches[0] == "list":
|
||||||
# Just display the list of installed services and their
|
# Just display the list of installed services and their
|
||||||
# status, then exit.
|
# status, then exit.
|
||||||
string = "-" * 40
|
string = "-" * 78
|
||||||
string += "\nService Listing"
|
string += "\n{wServices{n (use @services/start|stop):"
|
||||||
|
|
||||||
for service in service_collection.services:
|
for service in service_collection.services:
|
||||||
if service.running:
|
if service.running:
|
||||||
status = 'Running'
|
status = 'Running'
|
||||||
|
string += '\n * {g%s{n (%s)' % (service.name, status)
|
||||||
else:
|
else:
|
||||||
status = 'Inactive'
|
status = 'Inactive'
|
||||||
string += '\n * %s (%s)' % (service.name, status)
|
string += '\n {R%s{n (%s)' % (service.name, status)
|
||||||
string += "\n" + "-" * 40
|
string += "\n" + "-" * 78
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -375,35 +376,34 @@ class CmdService(MuxCommand):
|
||||||
service = service_collection.getServiceNamed(self.args)
|
service = service_collection.getServiceNamed(self.args)
|
||||||
except Exception:
|
except Exception:
|
||||||
string = 'Invalid service name. This command is case-sensitive. '
|
string = 'Invalid service name. This command is case-sensitive. '
|
||||||
string += 'See @service/list.'
|
string += 'See @service/list for valid services.'
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
return
|
return
|
||||||
|
|
||||||
if switch == "stop":
|
if switches[0] == "stop":
|
||||||
# Stopping a service gracefully closes it and disconnects
|
# Stopping a service gracefully closes it and disconnects
|
||||||
# any connections (if applicable).
|
# any connections (if applicable).
|
||||||
|
|
||||||
if not service.running:
|
if not service.running:
|
||||||
caller.msg('That service is not currently running.')
|
caller.msg('That service is not currently running.')
|
||||||
return
|
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.
|
|
||||||
if service.name[:7] == 'Evennia':
|
if service.name[:7] == 'Evennia':
|
||||||
string = "You can not stop Evennia TCPServer services this way."
|
string = "You seem to be shutting down a core Evennia* service. Note that"
|
||||||
string += "\nTo e.g. remove a listening port, change settings file and restart."
|
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)
|
caller.msg(string)
|
||||||
return
|
|
||||||
#comsys.cemit_mudinfo("%s is *Stopping* the service '%s'." % (sname, service.name)) #TODO!
|
|
||||||
service.stopService()
|
service.stopService()
|
||||||
|
caller.msg("Stopping service '%s'." % self.args)
|
||||||
return
|
return
|
||||||
|
|
||||||
if switch == "start":
|
if switches[0] == "start":
|
||||||
#Starts a service.
|
#Starts a service.
|
||||||
if service.running:
|
if service.running:
|
||||||
caller.msg('That service is already running.')
|
caller.msg('That service is already running.')
|
||||||
return
|
return
|
||||||
#comsys.cemit_mudinfo("%s is *Starting* the service '%s'." % (sname,service.name)) #TODO!
|
caller.msg("Starting service '%s'." % self.args)
|
||||||
service.startService()
|
service.startService()
|
||||||
|
|
||||||
class CmdShutdown(MuxCommand):
|
class CmdShutdown(MuxCommand):
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.contrib import admin
|
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):
|
class MsgAdmin(admin.ModelAdmin):
|
||||||
list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers', 'db_channels', 'db_message', 'db_lock_storage')
|
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
|
list_select_related = True
|
||||||
admin.site.register(Channel, ChannelAdmin)
|
admin.site.register(Channel, ChannelAdmin)
|
||||||
|
|
||||||
# class ChannelConnectionAdmin(admin.ModelAdmin):
|
class PlayerChannelConnectionAdmin(admin.ModelAdmin):
|
||||||
# list_display = ('db_channel', 'db_player')
|
list_display = ('db_channel', 'db_player')
|
||||||
# list_display_links = ("db_player", 'db_channel')
|
list_display_links = ("db_player", 'db_channel')
|
||||||
# ordering = ["db_channel"]
|
ordering = ["db_channel"]
|
||||||
# search_fields = ['db_channel', 'db_player']
|
search_fields = ['db_channel', 'db_player']
|
||||||
# save_as = True
|
save_as = True
|
||||||
# save_on_top = True
|
save_on_top = True
|
||||||
# list_select_related = True
|
list_select_related = True
|
||||||
# admin.site.register(ChannelConnection, ChannelConnectionAdmin)
|
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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ class ChannelCommand(command.Command):
|
||||||
msgobj.save()
|
msgobj.save()
|
||||||
msgobj.channels = channel
|
msgobj.channels = channel
|
||||||
# send new message object to channel
|
# send new message object to channel
|
||||||
channel.msg(msgobj)
|
channel.msg(msgobj, from_obj=caller.player)
|
||||||
|
|
||||||
class ChannelHandler(object):
|
class ChannelHandler(object):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
178
src/comms/irc.py
Normal file
178
src/comms/irc.py
Normal 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
These managers handles the
|
These managers handles the
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import itertools
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from src.utils.utils import is_iter
|
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)
|
umatch = PlayerDB.objects.filter(user__username__iexact=inp)
|
||||||
if umatch:
|
if umatch:
|
||||||
return umatch[0]
|
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:
|
else:
|
||||||
# have to import this way to avoid circular imports
|
# have to import this way to avoid circular imports
|
||||||
from src.comms.models import Channel
|
from src.comms.models import Channel
|
||||||
|
|
@ -239,10 +247,13 @@ class ChannelManager(models.Manager):
|
||||||
to this channel
|
to this channel
|
||||||
"""
|
"""
|
||||||
# import here to avoid circular imports
|
# import here to avoid circular imports
|
||||||
from src.comms.models import ChannelConnection
|
#from src.comms.models import PlayerChannelConnection
|
||||||
#= ContentType.objects.get(app_label="comms",
|
PlayerChannelConnection = ContentType.objects.get(app_label="comms",
|
||||||
# model="channelconnection").model_class()
|
model="playerchannelconnection").model_class()
|
||||||
return ChannelConnection.objects.get_all_connections(channel)
|
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):
|
def channel_search(self, ostring):
|
||||||
"""
|
"""
|
||||||
|
|
@ -266,9 +277,9 @@ class ChannelManager(models.Manager):
|
||||||
return channels
|
return channels
|
||||||
|
|
||||||
#
|
#
|
||||||
# ChannelConnection manager
|
# PlayerChannelConnection manager
|
||||||
#
|
#
|
||||||
class ChannelConnectionManager(models.Manager):
|
class PlayerChannelConnectionManager(models.Manager):
|
||||||
"""
|
"""
|
||||||
This handles all connections between a player and
|
This handles all connections between a player and
|
||||||
a channel.
|
a channel.
|
||||||
|
|
@ -316,4 +327,51 @@ class ChannelConnectionManager(models.Manager):
|
||||||
conns = self.filter(db_player=player).filter(db_channel=channel)
|
conns = self.filter(db_player=player).filter(db_channel=channel)
|
||||||
for conn in conns:
|
for conn in conns:
|
||||||
conn.delete()
|
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()
|
||||||
|
|
|
||||||
|
|
@ -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']
|
||||||
|
|
@ -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']
|
||||||
|
|
@ -16,10 +16,11 @@ be able to delete connections on the fly).
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from src.utils.idmapper.models import SharedMemoryModel
|
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.comms import managers
|
||||||
from src.locks.lockhandler import LockHandler
|
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
|
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.
|
# named same as the field, but withtout the db_* prefix.
|
||||||
|
|
||||||
# There must always be one sender of the message.
|
# 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
|
# The destination objects of this message. Stored as a
|
||||||
# comma-separated string of object dbrefs. Can be defined along
|
# comma-separated string of object dbrefs. Can be defined along
|
||||||
# with channels below.
|
# with channels below.
|
||||||
|
|
@ -149,6 +152,22 @@ class Msg(SharedMemoryModel):
|
||||||
raise Exception("You cannot delete the sender of a message!")
|
raise Exception("You cannot delete the sender of a message!")
|
||||||
sender = property(sender_get, sender_set, sender_del)
|
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
|
# receivers property
|
||||||
#@property
|
#@property
|
||||||
def receivers_get(self):
|
def receivers_get(self):
|
||||||
|
|
@ -323,6 +342,29 @@ class Msg(SharedMemoryModel):
|
||||||
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
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
|
# Channel
|
||||||
|
|
@ -482,7 +524,7 @@ class Channel(SharedMemoryModel):
|
||||||
Checks so this player is actually listening
|
Checks so this player is actually listening
|
||||||
to this channel.
|
to this channel.
|
||||||
"""
|
"""
|
||||||
return ChannelConnection.objects.has_connection(player, self)
|
return PlayerChannelConnection.objects.has_connection(player, self)
|
||||||
|
|
||||||
def msg(self, msgobj, from_obj=None):
|
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
|
no permission-checking is done here; it is assumed to have been
|
||||||
done before calling this method.
|
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
|
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
|
a message on the fly. If from_obj is None, no Msg object will
|
||||||
messages are logged.
|
be created and the message will be sent without being logged.
|
||||||
|
"""
|
||||||
"""
|
if isinstance(msgobj, basestring):
|
||||||
if not type(msgobj) == Msg:
|
# given msgobj is a string
|
||||||
# the given msgobj is not an Msg instance. If it is a string and from_obj
|
if from_obj:
|
||||||
# was given, we create the message on the fly instead.
|
if isinstance(from_obj, basestring):
|
||||||
if from_obj and isinstance(msgobj, basestring):
|
msgobj = Msg(db_sender_external=from_obj, db_message=msgobj)
|
||||||
msgobj = Msg(db_sender=from_obj, db_message=msgobj)
|
else:
|
||||||
|
msgobj = Msg(db_sender=from_obj, db_message=msgobj)
|
||||||
|
# try to use
|
||||||
msgobj.save()
|
msgobj.save()
|
||||||
msgobj.channels = [self]
|
msgobj.channels = [self]
|
||||||
msg = msgobj.message
|
msg = msgobj.message
|
||||||
else:
|
else:
|
||||||
# this just sends a message, without any sender
|
# this just sends a message, without any sender
|
||||||
# (and without storing it in a persistent Msg object)
|
# (and without storing it in a persistent Msg object)
|
||||||
msg = str(msgobj)
|
msg = to_str(msgobj)
|
||||||
else:
|
else:
|
||||||
msg = msgobj.message
|
msg = msgobj.message
|
||||||
|
|
||||||
# get all players connected to this channel
|
# get all players connected to this channel and send to them
|
||||||
conns = Channel.objects.get_all_connections(self)
|
for conn in Channel.objects.get_all_connections(self):
|
||||||
|
try:
|
||||||
# send message to all connected players
|
conn.player.msg(msg, from_obj)
|
||||||
for conn in conns:
|
except AttributeError:
|
||||||
for session in \
|
try:
|
||||||
SESSIONS.sessions_from_player(conn.player):
|
conn.to_external(msg, from_obj)
|
||||||
session.msg(msg)
|
except Exception:
|
||||||
|
logger.log_trace("Cannot send msg to connection '%s'" % conn)
|
||||||
return True
|
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):
|
def connect_to(self, player):
|
||||||
"Connect the user to this channel"
|
"Connect the user to this channel"
|
||||||
if not self.access(player, 'listen'):
|
if not self.access(player, 'listen'):
|
||||||
return False
|
return False
|
||||||
conn = ChannelConnection.objects.create_connection(player, self)
|
conn = PlayerChannelConnection.objects.create_connection(player, self)
|
||||||
if conn:
|
if conn:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def disconnect_from(self, player):
|
def disconnect_from(self, player):
|
||||||
"Disconnect user from this channel."
|
"Disconnect user from this channel."
|
||||||
ChannelConnection.objects.break_connection(player, self)
|
PlayerChannelConnection.objects.break_connection(player, self)
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
"Clean out all connections to this channel and delete it."
|
"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)
|
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
|
The advantage of making it like this is that one can easily
|
||||||
break the connection just by deleting this object.
|
break the connection just by deleting this object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Player connected to a channel
|
# Player connected to a channel
|
||||||
db_player = models.ForeignKey("players.PlayerDB")
|
db_player = models.ForeignKey("players.PlayerDB")
|
||||||
# Channel the player is connected to
|
# Channel the player is connected to
|
||||||
db_channel = models.ForeignKey(Channel)
|
db_channel = models.ForeignKey(Channel)
|
||||||
|
|
||||||
# Database manager
|
# Database manager
|
||||||
objects = managers.ChannelConnectionManager()
|
objects = managers.PlayerChannelConnectionManager()
|
||||||
|
|
||||||
# player property (wraps db_player)
|
# player property (wraps db_player)
|
||||||
#@property
|
#@property
|
||||||
|
|
@ -602,3 +660,140 @@ class ChannelConnection(SharedMemoryModel):
|
||||||
verbose_name = "Channel<->Player link"
|
verbose_name = "Channel<->Player link"
|
||||||
verbose_name_plural = "Channel<->Player links"
|
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))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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))
|
|
||||||
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -317,3 +317,30 @@ def superuser(*args, **kwargs):
|
||||||
"""
|
"""
|
||||||
return False
|
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
|
||||||
|
|
|
||||||
|
|
@ -112,10 +112,10 @@ def create_channels():
|
||||||
|
|
||||||
# connect the god user to all these channels by default.
|
# connect the god user to all these channels by default.
|
||||||
goduser = get_god_user()
|
goduser = get_god_user()
|
||||||
from src.comms.models import ChannelConnection
|
from src.comms.models import PlayerChannelConnection
|
||||||
ChannelConnection.objects.create_connection(goduser, pchan)
|
PlayerChannelConnection.objects.create_connection(goduser, pchan)
|
||||||
ChannelConnection.objects.create_connection(goduser, ichan)
|
PlayerChannelConnection.objects.create_connection(goduser, ichan)
|
||||||
ChannelConnection.objects.create_connection(goduser, cchan)
|
PlayerChannelConnection.objects.create_connection(goduser, cchan)
|
||||||
|
|
||||||
def import_MUX_help_files():
|
def import_MUX_help_files():
|
||||||
"""
|
"""
|
||||||
|
|
@ -237,11 +237,11 @@ def handle_setup(last_step):
|
||||||
for profile in PlayerDB.objects.all():
|
for profile in PlayerDB.objects.all():
|
||||||
profile.delete()
|
profile.delete()
|
||||||
elif last_step + num == 3:
|
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():
|
for chan in Channel.objects.all():
|
||||||
chan.delete()
|
chan.delete()
|
||||||
for conn in ChannelConnection.objects.all():
|
for conn in PlayerChannelConnection.objects.all():
|
||||||
conn.delete()
|
conn.delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,7 @@ if TELNET_ENABLED:
|
||||||
factory = protocol.ServerFactory()
|
factory = protocol.ServerFactory()
|
||||||
factory.protocol = telnet.TelnetProtocol
|
factory.protocol = telnet.TelnetProtocol
|
||||||
telnet_service = internet.TCPServer(port, factory)
|
telnet_service = internet.TCPServer(port, factory)
|
||||||
telnet_service.setName('Evennia%s' % port)
|
telnet_service.setName('EvenniaTelnet%s' % port)
|
||||||
EVENNIA.services.addService(telnet_service)
|
EVENNIA.services.addService(telnet_service)
|
||||||
|
|
||||||
if WEBSERVER_ENABLED:
|
if WEBSERVER_ENABLED:
|
||||||
|
|
@ -244,11 +244,5 @@ if IRC_ENABLED:
|
||||||
|
|
||||||
# IRC channel connections
|
# IRC channel connections
|
||||||
|
|
||||||
from src.irc.connection import IRC_BotFactory
|
from src.comms import irc
|
||||||
irc = internet.TCPClient(settings.IRC_NETWORK,
|
irc.connect_all()
|
||||||
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)
|
|
||||||
|
|
|
||||||
|
|
@ -162,10 +162,10 @@ CMDSET_UNLOGGEDIN = "game.gamesrc.commands.basecmdset.UnloggedinCmdSet"
|
||||||
# Default set for logged in players (fallback)
|
# Default set for logged in players (fallback)
|
||||||
CMDSET_DEFAULT = "game.gamesrc.commands.basecmdset.DefaultCmdSet"
|
CMDSET_DEFAULT = "game.gamesrc.commands.basecmdset.DefaultCmdSet"
|
||||||
|
|
||||||
|
|
||||||
###################################################
|
###################################################
|
||||||
# Default Object typeclasses
|
# Default Object typeclasses
|
||||||
###################################################
|
###################################################
|
||||||
|
|
||||||
# Note that all typeclasses must originally
|
# Note that all typeclasses must originally
|
||||||
# inherit from src.objects.objects.Object somewhere in
|
# inherit from src.objects.objects.Object somewhere in
|
||||||
# their path.
|
# their path.
|
||||||
|
|
@ -256,9 +256,18 @@ CHANNEL_CONNECTINFO = ("MUDconnections", ('connections, mud_conns'),
|
||||||
"admin:perm(Immortals);listen:perm(Wizards);send:false()")
|
"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
|
# IMC (Inter-MUD communication) allows for an evennia chat channel
|
||||||
# that connects to people on other MUDs also using the IMC. Your
|
# 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
|
# 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.
|
# This isn't something you should generally change.
|
||||||
IMC2_PROTOCOL_VERSION = '2'
|
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
|
# Config for Django web features
|
||||||
|
|
@ -425,9 +412,7 @@ INSTALLED_APPS = (
|
||||||
'src.config',
|
'src.config',
|
||||||
'src.players',
|
'src.players',
|
||||||
'src.objects',
|
'src.objects',
|
||||||
'src.comms',
|
'src.comms',
|
||||||
'src.imc2',
|
|
||||||
'src.irc',
|
|
||||||
'src.help',
|
'src.help',
|
||||||
'src.scripts',
|
'src.scripts',
|
||||||
'src.web.news',
|
'src.web.news',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue