Cleaned and updated the i18n strings for various server-core system. Removed i18n for all strings that are only visible on stdout or in logs. Still missing i18n on certain specific things such as model field help and attribute warnings. Updated Swedish translation to match.

This commit is contained in:
Griatch 2012-06-14 02:43:35 +02:00
parent 80da420ee7
commit 4c849ec351
26 changed files with 918 additions and 1420 deletions

View file

@ -124,8 +124,6 @@ SECRET_KEY = '%s'
# Test the import of the settings file
#------------------------------------------------------------
try:
# i18n
from django.utils.translation import ugettext as _
from game import settings
except Exception:
import traceback
@ -157,11 +155,11 @@ if __name__ == "__main__":
# checks if the settings file was created this run
if _CREATED_SETTINGS:
print _("""
print """
Edit your new settings.py file as needed, then run
'python manage syncdb' and follow the prompts to
create the database and your superuser account.
""")
"""
sys.exit()
# run the standard django manager, if dependencies match

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -45,6 +45,8 @@ from src.commands.cmdset import CmdSet
from src.commands.cmdparser import at_multimatch_cmd
from src.utils.utils import string_suggestions
from django.utils.translation import ugettext as _
__all__ = ("cmdhandler",)
# This decides which command parser is to be used.
@ -196,18 +198,18 @@ def cmdhandler(caller, raw_string, testing=False):
if syscmd:
sysarg = raw_string
else:
sysarg = "Command '%s' is not available." % raw_string
sysarg = _("Command '%s' is not available.") % raw_string
suggestions = string_suggestions(raw_string, cmdset.get_all_cmd_keys_and_aliases(caller), cutoff=0.7, maxnum=3)
if suggestions:
sysarg += " Maybe you meant %s?" % utils.list_to_string(suggestions, 'or', addquote=True)
sysarg += _(" Maybe you meant %s?") % utils.list_to_string(suggestions, _('or'), addquote=True)
else:
sysarg += " Type \"help\" for help."
sysarg += _(" Type \"help\" for help.")
raise ExecSystemCommand(syscmd, sysarg)
if len(matches) > 1:
# We have a multiple-match
syscmd = yield cmdset.get(CMD_MULTIMATCH)
sysarg = "There where multiple matches."
sysarg = _("There where multiple matches.")
if syscmd:
syscmd.matches = matches
else:
@ -301,19 +303,19 @@ def cmdhandler(caller, raw_string, testing=False):
string += "If logging out/in doesn't solve the problem, try to "
string += "contact the server admin through some other means "
string += "for assistance."
caller.msg(string)
caller.msg(_(string))
logger.log_errmsg("No cmdsets found: %s" % caller)
except Exception:
# We should not end up here. If we do, it's a programming bug.
string = "%s\nAbove traceback is from an untrapped error."
string += " Please file a bug report."
logger.log_trace(string)
logger.log_trace(_(string))
caller.msg(string % format_exc())
except Exception:
# This catches exceptions in cmdhandler exceptions themselves
string = "%s\nAbove traceback is from a Command handler bug."
string += " Please contact an admin and/or file a bug report."
logger.log_trace(string)
logger.log_trace(_(string))
caller.msg(string % format_exc())

View file

@ -6,6 +6,7 @@ return a CommandCandidates object.
"""
from src.utils.logger import log_trace
from django.utils.translation import ugettext as _
def cmdparser(raw_string, cmdset, caller, match_index=None):
"""
@ -140,7 +141,7 @@ def at_search_result(msg_obj, ostring, results, global_search=False):
string = ""
if not results:
# no results.
string = "Could not find '%s'." % ostring
string = _("Could not find '%s'." % ostring)
results = None
elif len(results) > 1:
@ -152,11 +153,12 @@ def at_search_result(msg_obj, ostring, results, global_search=False):
string += "More than one match for '%s'" % ostring
string += " (please narrow target):"
string = _(string)
for num, result in enumerate(results):
invtext = ""
dbreftext = ""
if hasattr(result, "location") and result.location == msg_obj:
invtext = " (carried)"
if hasattr(result, _("location")) and result.location == msg_obj:
invtext = _(" (carried)")
if show_dbref:
dbreftext = "(#%i)" % result.dbid
string += "\n %i-%s%s%s" % (num+1, result.name,
@ -235,11 +237,11 @@ def at_multimatch_cmd(caller, matches):
is_channel = hasattr(cmd, "is_channel") and cmd.is_channel
if is_channel:
is_channel = " (channel)"
is_channel = _(" (channel)")
else:
is_channel = ""
if cmd.is_exit and cmd.destination:
is_exit = " (exit to %s)" % cmd.destination
is_exit = _(" (exit to %s)") % cmd.destination
else:
is_exit = ""

View file

@ -15,6 +15,7 @@ together to create interesting in-game effects.
"""
import copy
from django.utils.translation import ugettext as _
from src.utils.utils import inherits_from, is_iter
__all__ = ("CmdSet",)
@ -303,9 +304,9 @@ class CmdSet(object):
try:
cmd = self._instantiate(cmd)
except RuntimeError, e:
string = "Adding cmdset %s to %s lead to an infinite loop. When adding a cmdset to another, "
string = "Adding cmdset %(cmd)s to %(class)s lead to an infinite loop. When adding a cmdset to another, "
string += "make sure they are not themself cyclically added to the new cmdset somewhere in the chain."
raise RuntimeError(string % (cmd, self.__class__))
raise RuntimeError(_(string) % {"cmd":cmd, "class":self.__class__})
cmds = cmd.commands
elif is_iter(cmd):
cmds = [self._instantiate(c) for c in cmd]

View file

@ -67,6 +67,8 @@ import traceback
from src.utils import logger, utils
from src.commands.cmdset import CmdSet
from src.server.models import ServerConfig
from django.utils.translation import ugettext as _
__all__ = ("import_cmdset", "CmdSetHandler")
_CACHED_CMDSETS = {}
@ -110,15 +112,15 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
return cmdsetclass
except ImportError:
errstring = "Error loading cmdset: Couldn't import module '%s'."
errstring = _("Error loading cmdset: Couldn't import module '%s'.")
errstring = errstring % modulepath
raise
except KeyError:
errstring = "Error in loading cmdset: No cmdset class '%s' in %s."
errstring = errstring % (classname, modulepath)
errstring = _("Error in loading cmdset: No cmdset class '%(classname)s' in %(modulepath)s.")
errstring = errstring % {"classname":classname, "modulepath":modulepath}
raise
except Exception:
errstring = "Compile/Run error when loading cmdset '%s'. Error was logged."
errstring = _("Compile/Run error when loading cmdset '%s'. Error was logged.")
errstring = errstring % (python_path)
raise
except Exception:
@ -194,15 +196,17 @@ class CmdSetHandler(object):
mergetype = self.mergetype_stack[-1]
if mergetype != self.current.mergetype:
merged_on = self.cmdset_stack[-2].key
mergetype = "custom %s on cmdset '%s'" % (mergetype, merged_on)
mergetype = _("custom %(mergetype)s on cmdset '%(merged_on)s'") % {"mergetype":mergetype, "merged_on":merged_on}
if mergelist:
string += " <Merged %s (%s, prio %i)>: %s" % ("+".join(mergelist), mergetype, self.current.priority, self.current)
string += _(" <Merged %(mergelist)s (%(mergetype)s, prio %(prio)i)>: %(current)s") % \
{"mergelist": "+".join(mergelist), "mergetype":mergetype, "prio":self.current.priority, "current":self.current}
else:
permstring = "non-perm"
if self.current.permanent:
permstring = "perm"
string += " <%s (%s, prio %i, %s)>: %s" % (self.current.key, mergetype, self.current.priority, permstring,
", ".join(cmd.key for cmd in sorted(self.current, key=lambda o:o.key)))
string += _(" <%(key)s (%(mergetype)s, prio %(prio)i, %(permstring)s)>: %(keylist)s") % \
{"key":self.current.key, "mergetype":mergetype, "prio":self.current.priority, "permstring":permstring,
"keylost":", ".join(cmd.key for cmd in sorted(self.current, key=lambda o:o.key))}
return string.strip()
def _import_cmdset(self, cmdset_path, emit_to_obj=None):
@ -274,7 +278,7 @@ class CmdSetHandler(object):
"""
if callable(cmdset):
if not utils.inherits_from(cmdset, CmdSet):
raise Exception("Only CmdSets can be added to the cmdsethandler!")
raise Exception(_("Only CmdSets can be added to the cmdsethandler!"))
cmdset = cmdset(self.obj)
elif isinstance(cmdset, basestring):
# this is (maybe) a python path. Try to import from cache.
@ -306,7 +310,7 @@ class CmdSetHandler(object):
"""
if callable(cmdset):
if not utils.inherits_from(cmdset, CmdSet):
raise Exception("Only CmdSets can be added to the cmdsethandler!")
raise Exception(_("Only CmdSets can be added to the cmdsethandler!"))
cmdset = cmdset(self.obj)
elif isinstance(cmdset, basestring):
# this is (maybe) a python path. Try to import from cache.

View file

@ -1,20 +1,20 @@
#
# This sets up how models are displayed
# in the web admin interface.
# This sets up how models are displayed
# in the web admin interface.
#
from django.contrib import admin
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')
class MsgAdmin(admin.ModelAdmin):
list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers', 'db_channels', 'db_message', 'db_lock_storage')
list_display_links = ("id",)
ordering = ["db_date_sent", 'db_sender', 'db_receivers', 'db_channels']
#readonly_fields = ['db_message', 'db_sender', 'db_receivers', 'db_channels']
search_fields = ['id', '^db_date_sent', '^db_message']
save_as = True
save_on_top = True
list_select_related = True
save_as = True
save_on_top = True
list_select_related = True
#admin.site.register(Msg, MsgAdmin)
class PlayerChannelConnectionInline(admin.TabularInline):
@ -41,12 +41,12 @@ class ChannelAdmin(admin.ModelAdmin):
list_display_links = ("id", 'db_key')
ordering = ["db_key"]
search_fields = ['id', 'db_key', 'db_aliases']
save_as = True
save_on_top = True
list_select_related = True
save_as = True
save_on_top = True
list_select_related = True
fieldsets = (
(None, {'fields':(('db_key', 'db_aliases', 'db_desc'),'db_lock_storage', 'db_keep_log')}),
)
)
admin.site.register(Channel, ChannelAdmin)
@ -55,9 +55,9 @@ admin.site.register(Channel, ChannelAdmin)
# 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
# save_as = True
# save_on_top = True
# list_select_related = True
# admin.site.register(PlayerChannelConnection, PlayerChannelConnectionAdmin)
# class ExternalChannelConnectionAdmin(admin.ModelAdmin):
@ -65,8 +65,8 @@ admin.site.register(Channel, ChannelAdmin)
# 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
# save_as = True
# save_on_top = True
# list_select_related = True
# admin.site.register(ExternalChannelConnection, ExternalChannelConnectionAdmin)

View file

@ -16,6 +16,8 @@ from src.comms.imc2lib import imc2_packets as pck
from src.comms.imc2lib.imc2_trackers import IMC2MudList, IMC2ChanList
from src.comms.imc2lib.imc2_listeners import handle_whois_reply
from django.utils.translation import ugettext as _
# IMC2 network setup
IMC2_MUDNAME = settings.SERVERNAME
IMC2_NETWORK = settings.IMC2_NETWORK
@ -26,8 +28,8 @@ IMC2_SERVER_PWD = settings.IMC2_SERVER_PWD
# channel to send info to
INFOCHANNEL = Channel.objects.channel_search(settings.CHANNEL_MUDINFO[0])
# all linked channel connections
IMC2_CLIENT = None
# IMC2 debug mode
IMC2_CLIENT = None
# IMC2 debug mode
IMC2_DEBUG = False
# Use this instance to keep track of the other games on the network.
IMC2_MUDLIST = IMC2MudList()
@ -41,7 +43,7 @@ IMC2_CHANLIST = IMC2ChanList()
def msg_info(message):
"""
Send info to default info channel
"""
"""
try:
INFOCHANNEL[0].msg(message)
message = '[%s][IMC2]: %s' % (INFOCHANNEL[0].key, message)
@ -49,7 +51,7 @@ def msg_info(message):
logger.log_infomsg("MUDinfo (imc2): %s" % message)
#
# Regular scripts
# Regular scripts
#
class Send_IsAlive(Script):
@ -61,8 +63,8 @@ class Send_IsAlive(Script):
def at_script_creation(self):
self.key = 'IMC2_Send_IsAlive'
self.interval = 900
self.desc = "Send an IMC2 is-alive packet"
self.persistent = True
self.desc = _("Send an IMC2 is-alive packet")
self.persistent = True
def at_repeat(self):
IMC2_CLIENT.send_packet(pck.IMC2PacketIsAlive())
def is_valid(self):
@ -77,9 +79,9 @@ class Send_Keepalive_Request(Script):
def at_script_creation(self):
self.key = "IMC2_Send_Keepalive_Request"
self.interval = 3500
self.desc = "Send an IMC2 keepalive-request packet"
self.persistent = True
def at_repeat(self):
self.desc = _("Send an IMC2 keepalive-request packet")
self.persistent = True
def at_repeat(self):
IMC2_CLIENT.channel.send_packet(pck.IMC2PacketKeepAliveRequest())
def is_valid(self):
"Is only valid as long as there are channels to update"
@ -94,8 +96,8 @@ class Prune_Inactive_Muds(Script):
def at_script_creation(self):
self.key = "IMC2_Prune_Inactive_Muds"
self.interval = 1800
self.desc = "Check IMC2 list for inactive games"
self.persistent = True
self.desc = _("Check IMC2 list for inactive games")
self.persistent = True
self.inactive_threshold = 3599
def at_repeat(self):
for name, mudinfo in IMC2_MUDLIST.mud_list.items():
@ -104,7 +106,7 @@ class Prune_Inactive_Muds(Script):
def is_valid(self):
"Is only valid as long as there are channels to update"
return any(service for service in SESSIONS.server.services if service.name.startswith("imc2_"))
class Sync_Server_Channel_List(Script):
"""
Re-syncs the network's channel list. This will
@ -114,11 +116,11 @@ class Sync_Server_Channel_List(Script):
"""
def at_script_creation(self):
self.key = "IMC2_Sync_Server_Channel_List"
self.interval = 24 * 3600 # once every day
self.desc = "Re-sync IMC2 network channel list"
self.persistent = True
self.interval = 24 * 3600 # once every day
self.desc = _("Re-sync IMC2 network channel list")
self.persistent = True
def at_repeat(self):
checked_networks = []
checked_networks = []
network = IMC2_CLIENT.factory.network
if not network in checked_networks:
channel.send_packet(pkg.IMC2PacketIceRefresh())
@ -126,7 +128,7 @@ class Sync_Server_Channel_List(Script):
def is_valid(self):
return any(service for service in SESSIONS.server.services if service.name.startswith("imc2_"))
#
# IMC2 protocol
# IMC2 protocol
#
class IMC2Protocol(telnet.StatefulTelnetProtocol):
@ -143,7 +145,7 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
self.network_name = None
self.sequence = None
def connectionMade(self):
"""
Triggered after connecting to the IMC2 network.
@ -153,19 +155,19 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
logger.log_infomsg("IMC2: Connected to network server.")
logger.log_infomsg("IMC2: Sending authentication packet.")
self.send_packet(pck.IMC2PacketAuthPlaintext())
def connectionLost(self, reason=None):
"""
This is executed when the connection is lost for
whatever reason.
This is executed when the connection is lost for
whatever reason.
"""
try:
service = SESSIONS.server.services.getServiceNamed("imc2_%s:%s(%s)" % (IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME))
except Exception:
return
return
if service.running:
service.stopService()
def send_packet(self, packet):
"""
Given a sub-class of IMC2Packet, assemble the packet and send it
@ -182,14 +184,14 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
logger.log_infomsg("IMC2: SENT> %s" % packet_str)
logger.log_infomsg(str(packet))
self.sendLine(packet_str)
def _parse_auth_response(self, line):
"""
Parses the IMC2 network authentication packet.
"""
if self.auth_type == "plaintext":
# Plain text passwords.
# SERVER Sends: PW <servername> <serverpw> version=<version#> <networkname>
# Plain text passwords.
# SERVER Sends: PW <servername> <serverpw> version=<version#> <networkname>
if IMC2_DEBUG:
logger.log_infomsg("IMC2: AUTH< %s" % line)
@ -199,26 +201,26 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
autosetup_present = line_split[0] == 'autosetup'
if "reject" in line_split:
auth_message = "IMC2 server rejected connection."
auth_message = _("IMC2 server rejected connection.")
logger.log_infomsg(auth_message)
msg_info(auth_message)
return
return
if pw_present:
self.server_name = line_split[1]
self.network_name = line_split[4]
elif autosetup_present:
logger.log_infomsg("IMC2: Autosetup response found.")
logger.log_infomsg(_("IMC2: Autosetup response found."))
self.server_name = line_split[1]
self.network_name = line_split[3]
self.network_name = line_split[3]
self.is_authenticated = True
self.sequence = int(time())
# Log to stdout and notify over MUDInfo.
auth_message = "Successfully authenticated to the '%s' network." % self.factory.network
auth_message = _("Successfully authenticated to the '%s' network.") % self.factory.network
logger.log_infomsg('IMC2: %s' % auth_message)
msg_info(auth_message)
# Ask to see what other MUDs are connected.
self.send_packet(pck.IMC2PacketKeepAliveRequest())
# IMC2 protocol states that KeepAliveRequests should be followed
@ -226,7 +228,7 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
self.send_packet(pck.IMC2PacketIsAlive())
# Get a listing of channels.
self.send_packet(pck.IMC2PacketIceRefresh())
def _msg_evennia(self, packet):
"""
Handle the sending of packet data to Evennia channel
@ -235,34 +237,35 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
conn_name = packet.optional_data.get('channel', None)
# If the packet lacks the 'echo' key, don't bother with it.
if not conn_name or not packet.optional_data.get('echo', None):
return
if not conn_name or not packet.optional_data.get('echo', None):
return
imc2_channel = conn_name.split(':', 1)[1]
# Look for matching IMC2 channel maps mapping to this imc2 channel.
conns = ExternalChannelConnection.objects.filter(db_external_key__startswith="imc2_")
conns = [conn for conn in conns if imc2_channel in conn.db_external_config.split(",")]
if not conns:
# we are not listening to this imc2 channel.
return
# we are not listening to this imc2 channel.
return
# Format the message to send to local channel(s).
for conn in conns:
message = '[%s] %s@%s: %s' % (conn.channel.key, packet.sender, packet.origin, packet.optional_data.get('text'))
message = '[%s] %s@%s: %s' % (conn.channel.key, packet.sender, packet.origin, packet.optional_data.get('text'))
conn.to_channel(message)
def _format_tell(self, packet):
"""
Handle tells over IMC2 by formatting the text properly
Handle tells over IMC2 by formatting the text properly
"""
return "{c%s@%s{n {wpages (over IMC):{n %s" % (packet.sender, packet.origin,
packet.optional_data.get('text', 'ERROR: No text provided.'))
return _("{c%(sender)s@%(origin)s{n {wpages (over IMC):{n %(msg)s") % {"sender":packet.sender,
"origin":packet.origin,
"msg":packet.optional_data.get('text', 'ERROR: No text provided.')}
def lineReceived(self, line):
"""
Triggered when text is received from the IMC2 network. Figures out
what to do with the packet.
IMC2 -> Evennia
IMC2 -> Evennia
"""
line = line.strip()
@ -271,18 +274,18 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
else:
if IMC2_DEBUG and not 'is-alive' in line:
# if IMC2_DEBUG mode is on, print the contents of the packet
# to stdout.
# to stdout.
logger.log_infomsg("IMC2: RECV> %s" % line)
# Parse the packet and encapsulate it for easy access
packet = pck.IMC2Packet(self.factory.mudname, packet_str=line)
if IMC2_DEBUG and packet.packet_type not in ('is-alive', 'keepalive-request'):
# Print the parsed packet's __str__ representation.
# is-alive and keepalive-requests happen pretty frequently.
# Don't bore us with them in stdout.
logger.log_infomsg(str(packet))
# Figure out what kind of packet we're dealing with and hand it
# off to the correct handler.
@ -305,9 +308,9 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
elif packet.packet_type == 'tell':
player = search.players(packet.target)
if not player:
return
return
player[0].msg(self._format_tell(packet))
def msg_imc2(self, message, from_obj=None, packet_type="imcbroadcast", data=None):
"""
Called by Evennia to send a message through the imc2 connection
@ -319,33 +322,33 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
from_name = from_obj
else:
from_name = self.factory.mudname
if packet_type == "imcbroadcast":
if type(data) == dict:
conns = ExternalChannelConnection.objects.filter(db_external_key__startswith="imc2_",
db_channel=data.get("channel", "Unknown"))
if type(data) == dict:
conns = ExternalChannelConnection.objects.filter(db_external_key__startswith="imc2_",
db_channel=data.get("channel", "Unknown"))
if not conns:
return
# we remove the extra channel info since imc2 supplies this anyway
if ":" in message:
header, message = [part.strip() for part in message.split(":", 1)]
# send the packet
# send the packet
imc2_channel = conns[0].db_external_config.split(',')[0] # only send to the first channel
self.send_packet(pck.IMC2PacketIceMsgBroadcasted(self.factory.servername, imc2_channel,
from_name, message))
elif packet_type == "imctell":
# send a tell
# send a tell
if type(data) == dict:
target = data.get("target", "Unknown")
destination = data.get("destination", "Unknown")
self.send_packet(pck.IMC2PacketTell(from_name, target, destination, message))
elif packet_type == "imcwhois":
# send a whois request
# send a whois request
if type(data) == dict:
target = data.get("target", "Unknown")
self.send_packet(pck.IMC2PacketWhois(from_obj.id, target))
class IMC2Factory(protocol.ClientFactory):
"""
Creates instances of the IMC2Protocol. Should really only ever
@ -353,24 +356,24 @@ class IMC2Factory(protocol.ClientFactory):
"""
protocol = IMC2Protocol
def __init__(self, network, port, mudname, client_pwd, server_pwd):
def __init__(self, network, port, mudname, client_pwd, server_pwd):
self.pretty_key = "%s:%s(%s)" % (network, port, mudname)
self.network = network
sname, host = network.split(".", 1)
self.servername = sname.strip()
self.port = port
self.port = port
self.mudname = mudname
self.protocol_version = '2'
self.client_pwd = client_pwd
self.server_pwd = server_pwd
self.server_pwd = server_pwd
def clientConnectionFailed(self, connector, reason):
message = 'Connection failed: %s' % reason.getErrorMessage()
def clientConnectionFailed(self, connector, reason):
message = _('Connection failed: %s') % reason.getErrorMessage()
msg_info(message)
logger.log_errmsg('IMC2: %s' % message)
def clientConnectionLost(self, connector, reason):
message = 'Connection lost: %s' % reason.getErrorMessage()
message = _('Connection lost: %s') % reason.getErrorMessage()
msg_info(message)
logger.log_errmsg('IMC2: %s' % message)
@ -386,10 +389,10 @@ def start_scripts(validate=False):
Start all the needed scripts
"""
if validate:
if validate:
from src.scripts.models import ScriptDB
ScriptDB.objects.validate()
return
return
if not search.scripts("IMC2_Send_IsAlive"):
create.create_script(Send_IsAlive)
if not search.scripts("IMC2_Send_Keepalive_Request"):
@ -397,7 +400,7 @@ def start_scripts(validate=False):
if not search.scripts("IMC2_Prune_Inactive_Muds"):
create.create_script(Prune_Inactive_Muds)
if not search.scripts("IMC2_Sync_Server_Channel_List"):
create.create_script(Sync_Server_Channel_List)
create.create_script(Sync_Server_Channel_List)
def create_connection(channel, imc2_channel):
"""
@ -407,61 +410,61 @@ def create_connection(channel, imc2_channel):
if not type(channel) == Channel:
new_channel = Channel.objects.filter(db_key=channel)
if not new_channel:
logger.log_errmsg("Cannot attach IMC2<->Evennia: Evennia Channel '%s' not found" % channel)
logger.log_errmsg(_("Cannot attach IMC2<->Evennia: Evennia Channel '%s' not found") % channel)
return False
channel = new_channel[0]
key = build_connection_key(channel, imc2_channel)
old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
if old_conns:
# this evennia channel is already connected to imc. Check if imc2_channel is different.
# this evennia channel is already connected to imc. Check if imc2_channel is different.
# connection already exists. We try to only connect a new channel
old_config = old_conns[0].db_external_config.split(",")
old_config = old_conns[0].db_external_config.split(",")
if imc2_channel in old_config:
return False # we already listen to this channel
else:
# We add a new imc2_channel to listen to
old_config.append(imc2_channel)
old_conns[0].db_external_config = ",".join(old_config)
old_conns[0].save()
old_conns[0].save()
return True
else:
# no old connection found; create a new one.
config = imc2_channel
# how the evennia channel will be able to contact this protocol in reverse
# how the evennia channel will be able to contact this protocol in reverse
send_code = "from src.comms.imc2 import IMC2_CLIENT\n"
send_code += "data={'channel':from_channel}\n"
send_code += "IMC2_CLIENT.msg_imc2(message, from_obj=from_obj, data=data)\n"
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
db_external_config=config)
conn.save()
return True
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
db_external_config=config)
conn.save()
return True
def delete_connection(channel, imc2_channel):
"Destroy a connection"
if hasattr(channel, "key"):
channel = channel.key
key = build_connection_key(channel, imc2_channel)
try:
conn = ExternalChannelConnection.objects.get(db_external_key=key)
except ExternalChannelConnection.DoesNotExist:
return False
conn.delete()
return True
return False
conn.delete()
return True
def connect_to_imc2():
"Create the imc instance and connect to the IMC2 network."
"Create the imc instance and connect to the IMC2 network."
# connect
imc = internet.TCPClient(IMC2_NETWORK, int(IMC2_PORT), IMC2Factory(IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME,
# connect
imc = internet.TCPClient(IMC2_NETWORK, int(IMC2_PORT), IMC2Factory(IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME,
IMC2_CLIENT_PWD, IMC2_SERVER_PWD))
imc.setName("imc2_%s:%s(%s)" % (IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME))
imc.setName("imc2_%s:%s(%s)" % (IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME))
SESSIONS.server.services.addService(imc)
def connect_all():
"""
Activates the imc2 system. Called by the server if IMC2_ENABLED=True.
Activates the imc2 system. Called by the server if IMC2_ENABLED=True.
"""
connect_to_imc2()
start_scripts()

View file

@ -1,6 +1,6 @@
"""
ANSI parser - this adds colour to text according to
special markup strings.
special markup strings.
This is a IMC2 complacent version.
"""
@ -14,9 +14,9 @@ class IMCANSIParser(ansi.ANSIParser):
"""
def __init__(self):
normal = ansi.ANSI_NORMAL
hilite = ansi.ANSI_HILITE
hilite = ansi.ANSI_HILITE
self.ansi_map = [
(r'~Z', normal), # Random
(r'~Z', normal), # Random
(r'~x', normal + ansi.ANSI_BLACK), # Black
(r'~D', hilite + ansi.ANSI_BLACK), # Dark Grey
(r'~z', hilite + ansi.ANSI_BLACK),
@ -25,21 +25,21 @@ class IMCANSIParser(ansi.ANSIParser):
(r'~g', normal + ansi.ANSI_GREEN), # Dark Green
(r'~G', hilite + ansi.ANSI_GREEN), # Green
(r'~p', normal + ansi.ANSI_MAGENTA), # Dark magenta
(r'~m', normal + ansi.ANSI_MAGENTA),
(r'~m', normal + ansi.ANSI_MAGENTA),
(r'~M', hilite + ansi.ANSI_MAGENTA), # Magenta
(r'~P', hilite + ansi.ANSI_MAGENTA),
(r'~c', normal + ansi.ANSI_CYAN), # Cyan
(r'~y', normal + ansi.ANSI_YELLOW), # Dark Yellow (brown)
(r'~Y', hilite + ansi.ANSI_YELLOW), # Yellow
(r'~Y', hilite + ansi.ANSI_YELLOW), # Yellow
(r'~b', normal + ansi.ANSI_BLUE), # Dark Blue
(r'~B', hilite + ansi.ANSI_BLUE), # Blue
(r'~C', hilite + ansi.ANSI_BLUE),
(r'~C', hilite + ansi.ANSI_BLUE),
(r'~r', normal + ansi.ANSI_RED), # Dark Red
(r'~R', hilite + ansi.ANSI_RED), # Red
(r'~R', hilite + ansi.ANSI_RED), # Red
## Formatting
(r'~L', hilite), # Bold/hilite
(r'~!', normal), # reset
(r'~!', normal), # reset
(r'\\r', normal),
(r'\\n', ansi.ANSI_RETURN),
]
@ -51,7 +51,7 @@ class IMCANSIParser(ansi.ANSIParser):
ANSI_PARSER = IMCANSIParser()
def parse_ansi(string, strip_ansi=False, parser=ANSI_PARSER):
"""
Shortcut to use the IMC2 ANSI parser.

View file

@ -5,6 +5,8 @@ This module handles some of the -reply packets like whois-reply.
from src.objects.models import ObjectDB
from src.comms.imc2lib import imc2_ansi
from django.utils.translation import ugettext as _
def handle_whois_reply(packet):
"""
When the player sends an imcwhois <playername> request, the outgoing
@ -15,7 +17,7 @@ def handle_whois_reply(packet):
try:
pobject = ObjectDB.objects.get(id=packet.target)
response_text = imc2_ansi.parse_ansi(packet.optional_data.get('text', 'Unknown'))
string = 'Whois reply from %s: %s' % (packet.origin, response_text)
string = _('Whois reply from %(origin)s: %(msg)s') % {"origin":packet.origin, "msg":response_text}
pobject.msg(string.strip())
except ObjectDB.DoesNotExist:
# No match found for whois sender. Ignore it.

View file

@ -4,7 +4,7 @@ http://www.mudbytes.net/index.php?a=articles&s=imc2_protocol
"""
import shlex
from django.conf import settings
from django.conf import settings
class Lexxer(shlex.shlex):
"""
@ -28,7 +28,7 @@ class IMC2Packet(object):
Optionally, parse a packet and load it up.
"""
# The following fields are all according to the basic packet format of:
# <sender>@<origin> <sequence> <route> <packet-type> <target>@<destination> <data...>
# <sender>@<origin> <sequence> <route> <packet-type> <target>@<destination> <data...>
self.sender = None
if not mudname:
mudname = settings.SERVERNAME
@ -42,13 +42,13 @@ class IMC2Packet(object):
self.optional_data = {}
# Reference to the IMC2Protocol object doing the sending.
self.imc2_protocol = None
if packet_str:
# The lexxer handles the double quotes correctly, unlike just
# splitting. Spaces throw things off, so shlex handles it
# gracefully, ala POSIX shell-style parsing.
lex = Lexxer(packet_str)
# Token counter.
counter = 0
for token in lex:
@ -90,7 +90,7 @@ class IMC2Packet(object):
pass
# Increment and continue to the next token (if applicable)
counter += 1
def __str__(self):
retval = """
--IMC2 package (%s)
@ -101,15 +101,15 @@ class IMC2Packet(object):
Type: %s
Target: %s
Dest.: %s
Data:
%s
Data:
%s
------------------------""" % (self.packet_type, self.sender,
self.origin, self.sequence,
self.route, self.packet_type,
self.target, self.destination,
"\n ".join(["%s: %s" % items for items in self.optional_data.items()]))
return retval.strip()
def _get_optional_data_string(self):
"""
Generates the optional data string to tack on to the end of the packet.
@ -126,7 +126,7 @@ class IMC2Packet(object):
return data_string.strip()
else:
return ''
def _get_sender_name(self):
"""
Calculates the sender name to be sent with the packet.
@ -139,9 +139,9 @@ class IMC2Packet(object):
elif type(self.sender) in [type(u""),type(str())]:
#this is used by e.g. IRC where no user object is present.
return self.sender.strip().replace(' ', '_')
elif self.sender:
elif self.sender:
# Player object.
name = self.sender.get_name(fullname=False, show_dbref=False,
name = self.sender.get_name(fullname=False, show_dbref=False,
show_flags=False,
no_ansi=True)
# IMC2 does not allow for spaces.
@ -149,12 +149,12 @@ class IMC2Packet(object):
else:
# None value. Do something or other.
return 'Unknown'
def assemble(self, mudname=None, client_pwd=None, server_pwd=None):
"""
Assembles the packet and returns the ready-to-send string.
Note that the arguments are not used, they are there for
consistency across all packets.
Note that the arguments are not used, they are there for
consistency across all packets.
"""
self.sequence = self.imc2_protocol.sequence
packet = "%s@%s %s %s %s %s@%s %s\n" % (
@ -167,29 +167,29 @@ class IMC2Packet(object):
self.destination,
self._get_optional_data_string())
return packet.strip()
class IMC2PacketAuthPlaintext(object):
"""
IMC2 plain-text authentication packet. Auth packets are strangely
formatted, so this does not sub-class IMC2Packet. The SHA and plain text
auth packets are the two only non-conformers.
CLIENT Sends:
CLIENT Sends:
PW <mudname> <clientpw> version=<version#> autosetup <serverpw> (SHA256)
Optional Arguments( required if using the specified authentication method:
(SHA256) The literal string: SHA256. This is sent to notify the server
that the MUD is SHA256-Enabled. All future logins from this
client will be expected in SHA256-AUTH format if the server
supports it.
"""
(SHA256) The literal string: SHA256. This is sent to notify the server
that the MUD is SHA256-Enabled. All future logins from this
client will be expected in SHA256-AUTH format if the server
supports it.
"""
def assemble(self, mudname=None, client_pwd=None, server_pwd=None):
"""
This is one of two strange packets, just assemble the packet manually
and go.
"""
return 'PW %s %s version=2 autosetup %s\n' %(mudname, client_pwd, server_pwd)
class IMC2PacketKeepAliveRequest(IMC2Packet):
"""
Description:
@ -197,15 +197,15 @@ class IMC2PacketKeepAliveRequest(IMC2Packet):
This packet is usually followed by the sending MUD's own is-alive packet.
It is used in the filling of a client's MUD list, thus any MUD that doesn't
respond with an is-alive isn't marked as online on the sending MUD's mudlist.
Data:
(none)
Example of a received keepalive-request:
*@YourMUD 1234567890 YourMUD!Hub1 keepalive-request *@*
Example of a sent keepalive-request:
*@YourMUD 1234567890 YourMUD keepalive-request *@*
*@YourMUD 1234567890 YourMUD keepalive-request *@*
"""
def __init__(self):
super(IMC2PacketKeepAliveRequest, self).__init__()
@ -213,40 +213,40 @@ class IMC2PacketKeepAliveRequest(IMC2Packet):
self.packet_type = 'keepalive-request'
self.target = '*'
self.destination = '*'
class IMC2PacketIsAlive(IMC2Packet):
"""
Description:
This packet is the reply to a keepalive-request packet. It is responsible
This packet is the reply to a keepalive-request packet. It is responsible
for filling a client's mudlist with the information about other MUDs on the
network.
Data:
versionid=<string>
versionid=<string>
Where <string> is the text version ID of the client. ("IMC2 4.5 MUD-Net")
url=<string>
url=<string>
Where <string> is the proper URL of the client. (http://www.domain.com)
host=<string>
host=<string>
Where <string> is the telnet address of the MUD. (telnet://domain.com)
port=<int>
port=<int>
Where <int> is the telnet port of the MUD.
(These data fields are not sent by the MUD, they are added by the server.)
networkname=<string>
networkname=<string>
Where <string> is the network name that the MUD/server is on. ("MyNetwork")
sha256=<int>
This is an optional tag that denotes the SHA-256 capabilities of a
This is an optional tag that denotes the SHA-256 capabilities of a
MUD or server.
Example of a received is-alive:
*@SomeMUD 1234567890 SomeMUD!Hub2 is-alive *@YourMUD versionid="IMC2 4.5 MUD-Net" url="http://www.domain.com" networkname="MyNetwork" sha256=1 host=domain.com port=5500
Example of a sent is-alive:
*@YourMUD 1234567890 YourMUD is-alive *@* versionid="IMC2 4.5 MUD-Net" url="http://www.domain.com" host=domain.com port=5500
*@YourMUD 1234567890 YourMUD is-alive *@* versionid="IMC2 4.5 MUD-Net" url="http://www.domain.com" host=domain.com port=5500
"""
def __init__(self):
super(IMC2PacketIsAlive, self).__init__()
@ -258,19 +258,19 @@ class IMC2PacketIsAlive(IMC2Packet):
'url': '"http://www.evennia.com"',
'host': 'test.com',
'port': '5555'}
class IMC2PacketIceRefresh(IMC2Packet):
"""
Description:
This packet is sent by the MUD to request data about the channels on the
network. Servers with channels reply with an ice-update packet for each
This packet is sent by the MUD to request data about the channels on the
network. Servers with channels reply with an ice-update packet for each
channel they control. The usual target for this packet is IMC@$.
Data:
(none)
Example:
*@YourMUD 1234567890 YourMUD!Hub1 ice-refresh IMC@$
*@YourMUD 1234567890 YourMUD!Hub1 ice-refresh IMC@$
"""
def __init__(self):
super(IMC2PacketIceRefresh, self).__init__()
@ -284,40 +284,40 @@ class IMC2PacketIceUpdate(IMC2Packet):
Description:
A server returns this packet with the data of a channel when prompted with
an ice-refresh request.
Data:
channel=<string>
channel=<string>
The channel's network name in the format of ServerName:ChannelName
owner=<string>
owner=<string>
The Name@MUD of the channel's owner
operators=<string>
operators=<string>
A space-seperated list of the Channel's operators, in the format of Person@MUD
policy=<string>
The policy is either "open" or "private" with no quotes.
invited=<string>
The space-seperated list of invited User@MUDs, only valid for a
The space-seperated list of invited User@MUDs, only valid for a
"private" channel.
excluded=<string>
The space-seperated list of banned User@MUDs, only valid for "open"
The space-seperated list of banned User@MUDs, only valid for "open"
channels.
level=<string> The default level of the channel: Admin, Imp, Imm,
level=<string> The default level of the channel: Admin, Imp, Imm,
Mort, or None
localname=<string> The suggested local name of the channel.
Examples:
Open Policy:
ICE@Hub1 1234567890 Hub1!Hub2 ice-update *@YourMUD channel=Hub1:ichat owner=Imm@SomeMUD operators=Other@SomeMUD policy=open excluded="Flamer@badMUD Jerk@dirtyMUD" level=Imm localname=ichat
Private Policy:
ICE@Hub1 1234567890 Hub1!Hub2 ice-update *@YourMUD channel=Hub1:secretchat owner=Imm@SomeMUD operators=Other@SomeMUD policy=private invited="SpecialDude@OtherMUD CoolDude@WeirdMUD" level=Mort localname=schat
ICE@Hub1 1234567890 Hub1!Hub2 ice-update *@YourMUD channel=Hub1:secretchat owner=Imm@SomeMUD operators=Other@SomeMUD policy=private invited="SpecialDude@OtherMUD CoolDude@WeirdMUD" level=Mort localname=schat
"""
pass
@ -328,29 +328,29 @@ class IMC2PacketIceMsgRelayed(IMC2Packet):
ice-msg-p packet, are used with private policy channels. The 'r' stands
for 'relay'. All incoming channel messages are from ICE@<server>, where
<server> is the server hosting the channel.
Data:
realfrom=<string>
realfrom=<string>
The User@MUD the message came from.
channel=<string>
channel=<string>
The Server:Channel the message is intended to be displayed on.
text=<string>
text=<string>
The message text.
emote=<int>
An integer value designating emotes. 0 for no emote, 1 for an emote,
emote=<int>
An integer value designating emotes. 0 for no emote, 1 for an emote,
and 2 for a social.
Examples:
ICE@Hub1 1234567890 Hub1!Hub2 ice-msg-r *@YourMUD realfrom=You@YourMUD channel=hub1:secret text="Aha! I got it!" emote=0
ICE@Hub1 1234567890 Hub1!Hub2 ice-msg-r *@YourMUD realfrom=You@YourMUD channel=hub1:secret text=Ahh emote=0
ICE@Hub1 1234567890 Hub1!Hub2 ice-msg-r *@YourMUD realfrom=You@YourMUD channel=hub1:secret text="grins evilly." emote=1
ICE@Hub1 1234567890 Hub1!Hub2 ice-msg-r *@YourMUD realfrom=You@YourMUD channel=hub1:secret text="You@YourMUD grins evilly!" emote=2
ICE@Hub1 1234567890 Hub1!Hub2 ice-msg-r *@YourMUD realfrom=You@YourMUD channel=hub1:secret text="You@YourMUD grins evilly!" emote=2
"""
pass
@ -360,27 +360,27 @@ class IMC2PacketIceMsgPrivate(IMC2Packet):
This packet is sent when a player sends a message to a private channel.
This packet should never be seen as incoming to a client. The target of
this packet should be IMC@<server> of the server hosting the channel.
Data:
channel=<string>
channel=<string>
The Server:Channel the message is intended to be displayed on.
text=<string>
The message text.
emote=<int>
An integer value designating emotes. 0 for no emote, 1 for an emote,
and 2 for a social.
echo=<int>
Tells the server to echo the message back to the sending MUD. This is only
seen on out-going messages.
Examples:
You@YourMUD 1234567890 YourMUD ice-msg-p IMC@Hub1 channel=Hub1:secret text="Ahh! I got it!" emote=0 echo=1
You@YourMUD 1234567890 YourMUD ice-msg-p IMC@Hub1 channel=Hub1:secret text=Ahh! emote=0 echo=1
You@YourMUD 1234567890 YourMUD ice-msg-p IMC@Hub1 channel=Hub1:secret text="grins evilly." emote=1 echo=1
You@YourMUD 1234567890 YourMUD ice-msg-p IMC@Hub1 channel=Hub1:secret text="You@YourMUD grins evilly." emote=2 echo=1
You@YourMUD 1234567890 YourMUD ice-msg-p IMC@Hub1 channel=Hub1:secret text="You@YourMUD grins evilly." emote=2 echo=1
"""
pass
@ -392,34 +392,34 @@ class IMC2PacketIceMsgBroadcasted(IMC2Packet):
as it was sent by the originating MUD. The server that hosts the channel
sends the packet back to the originating MUD as an 'echo' by removing the
"echo=1" and attaching the "sender=Person@MUD" data field.
Data:
channel=<string>
The Server:Channel the message is intended to be displayed on.
text=<string>
The message text.
emote=<int>
An integer value designating emotes. 0 for no emote, 1 for an emote,
An integer value designating emotes. 0 for no emote, 1 for an emote,
and 2 for a social.
*echo=<int>
This stays on broadcasted messages. It tells the channel's server to
relay an echo back.
*sender=<string>
The hosting server replaces "echo=1" with this when sending the echo back
to the originating MUD.
Examples:
Examples:
(See above for emote/social examples as they are pretty much the same)
Return Echo Packet:
You-YourMUD@Hub1 1234567890 Hub1 ice-msg-b *@YourMUD text=Hi! channel=Hub1:ichat sender=You@YourMUD emote=0
Broadcasted Packet:
You@YourMUD 1234567890 YourMUD!Hub1 ice-msg-b *@* channel=Hub1:ichat text=Hi! emote=0 echo=1
You@YourMUD 1234567890 YourMUD!Hub1 ice-msg-b *@* channel=Hub1:ichat text=Hi! emote=0 echo=1
"""
def __init__(self, server, channel, pobject, message):
"""
@ -445,27 +445,27 @@ class IMC2PacketUserCache(IMC2Packet):
Sent by a MUD with a new IMC2-able player or when a player's gender changes,
this packet contains only the gender for data. The packet's origination
should be the Player@MUD.
Data:
gender=<int> 0 is male, 1 is female, 2 is anything else such as neuter.
Will be referred to as "it".
Example:
Dude@someMUD 1234567890 SomeMUD!Hub2!Hub1 user-cache *@* gender=0
Dude@someMUD 1234567890 SomeMUD!Hub2!Hub1 user-cache *@* gender=0
"""
pass
class IMC2PacketUserCacheRequest(IMC2Packet):
"""
Description:
The MUD sends this packet out when making a request for the user-cache
The MUD sends this packet out when making a request for the user-cache
information of the user included in the data part of the packet.
Data:
user=<string> The Person@MUD whose data the MUD is seeking.
Example:
*@YourMUD 1234567890 YourMUD user-cache-request *@SomeMUD user=Dude@SomeMUD
*@YourMUD 1234567890 YourMUD user-cache-request *@SomeMUD user=Dude@SomeMUD
"""
pass
@ -474,16 +474,16 @@ class IMC2PacketUserCacheReply(IMC2Packet):
Description:
A reply to the user-cache-request packet. It contains the user and gender
for the user.
Data:
user=<string>
The Person@MUD whose data the MUD requested.
gender=<int>
The gender of the Person@MUD in the 'user' field.
Example:
*@someMUD 1234567890 SomeMUD!Hub2!Hub1 user-cache-reply *@YourMUD user=Dude@SomeMUD gender=0
*@someMUD 1234567890 SomeMUD!Hub2!Hub1 user-cache-reply *@YourMUD user=Dude@SomeMUD gender=0
"""
pass
@ -492,18 +492,18 @@ class IMC2PacketTell(IMC2Packet):
Description:
This packet is used to communicate private messages between users on MUDs
across the network.
Data:
text=<string> Message text
isreply=<int> Two settings: 1 denotes a reply, 2 denotes a tell social.
Example:
Originating:
You@YourMUD 1234567890 YourMUD tell Dude@SomeMUD text="Having fun?"
Reply from Dude:
Dude@SomeMUD 1234567890 SomeMUD!Hub1 tell You@YourMUD text="Yeah, this is cool!" isreply=1
Dude@SomeMUD 1234567890 SomeMUD!Hub1 tell You@YourMUD text="Yeah, this is cool!" isreply=1
"""
def __init__(self, pobject, target, destination, message):
super(IMC2PacketTell, self).__init__()
@ -511,94 +511,94 @@ class IMC2PacketTell(IMC2Packet):
self.packet_type = "tell"
self.target = target
self.destination = destination
self.optional_data = {"text": message,
self.optional_data = {"text": message,
"isreply":None}
def assemble(self, mudname=None, client_pwd=None, server_pwd=None):
self.sequence = self.imc2_protocol.sequence
#self.route = "%s!%s" % (self.origin, self.imc2_protocol.factory.servername.capitalize())
return '''"%s@%s %s %s tell %s@%s text="%s"''' % (self.sender, self.origin, self.sequence,
self.route, self.target, self.destination,
return '''"%s@%s %s %s tell %s@%s text="%s"''' % (self.sender, self.origin, self.sequence,
self.route, self.target, self.destination,
self.optional_data.get("text","NO TEXT GIVEN"))
class IMC2PacketEmote(IMC2Packet):
"""
Description:
This packet seems to be sent by servers when notifying the network of a new
channel or the destruction of a channel.
Data:
channel=<int>
Unsure of what this means. The channel seen in both creation and
channel=<int>
Unsure of what this means. The channel seen in both creation and
destruction packets is 15.
level=<int>
I am assuming this is the permission level of the sender. In both
creation and destruction messages, this is -1.
text=<string>
This is the message to be sent to the users.
Examples:
ICE@Hub1 1234567890 Hub1 emote *@* channel=15 level=-1 text="the channel called hub1:test has been destroyed by You@YourMUD."
ICE@Hub1 1234567890 Hub1 emote *@* channel=15 level=-1 text="the channel called hub1:test has been destroyed by You@YourMUD."
"""
pass
class IMC2PacketRemoteAdmin(IMC2Packet):
"""
Description:
This packet is used in remote server administration. Please note that
SHA-256 Support is *required* for a client to use this feature. The command
can vary, in fact this very packet is highly dependant on the server it's
being directed to. In most cases, sending the 'list' command will have a
This packet is used in remote server administration. Please note that
SHA-256 Support is *required* for a client to use this feature. The command
can vary, in fact this very packet is highly dependant on the server it's
being directed to. In most cases, sending the 'list' command will have a
remote-admin enabled server send you the list of commands it will accept.
Data:
command=<string>
command=<string>
The command being sent to the server for processing.
data=<string>
data=<string>
Data associated with the command. This is not always required.
hash=<string>
The SHA-256 hash that is verified by the server. This hash is generated in
hash=<string>
The SHA-256 hash that is verified by the server. This hash is generated in
the same manner as an authentication packet.
Example:
You@YourMUD 1234567890 YourMUD remote-admin IMC@Hub1 command=list hash=<hash goes here>
You@YourMUD 1234567890 YourMUD remote-admin IMC@Hub1 command=list hash=<hash goes here>
"""
pass
class IMC2PacketIceCmd(IMC2Packet):
"""
Description:
Used for remote channel administration. In most cases, one must be listed
as a channel creator on the target server in order to do much with this
Used for remote channel administration. In most cases, one must be listed
as a channel creator on the target server in order to do much with this
packet. Other cases include channel operators.
Data:
channel=<string>
channel=<string>
The target server:channel for the command.
command=<string>
command=<string>
The command to be processed.
data=<string>
data=<string>
Data associated with the command. This is not always required.
Example:
You@YourMUD 1234567890 YourMUD ice-cmd IMC@hub1 channel=hub1:ichat command=list
You@YourMUD 1234567890 YourMUD ice-cmd IMC@hub1 channel=hub1:ichat command=list
"""
pass
class IMC2PacketDestroy(IMC2Packet):
"""
Description:
Sent by a server to indicate the destruction of a channel it hosted.
Sent by a server to indicate the destruction of a channel it hosted.
The mud should remove this channel from its local configuration.
Data:
channel=<string> The server:channel being destroyed.
channel=<string> The server:channel being destroyed.
"""
pass
@ -606,39 +606,39 @@ class IMC2PacketWho(IMC2Packet):
"""
Description:
A seemingly mutli-purpose information-requesting packet. The istats
packet currently only works on servers, or at least that's the case on
MUD-Net servers. The 'finger' type takes a player name in addition to the
packet currently only works on servers, or at least that's the case on
MUD-Net servers. The 'finger' type takes a player name in addition to the
type name.
Example: "finger Dude". The 'who' and 'info' types take no argument.
The MUD is responsible for building the reply text sent in the who-reply
Example: "finger Dude". The 'who' and 'info' types take no argument.
The MUD is responsible for building the reply text sent in the who-reply
packet.
Data:
type=<string> Types: who, info, "finger <name>", istats (server only)
Example:
Dude@SomeMUD 1234567890 SomeMUD!Hub1 who *@YourMUD type=who
Dude@SomeMUD 1234567890 SomeMUD!Hub1 who *@YourMUD type=who
"""
pass
class IMC2PacketWhoReply(IMC2Packet):
"""
Description:
The multi-purpose reply to the multi-purpose information-requesting 'who'
packet. The MUD is responsible for building the return data, including the
format of it. The mud can use the permission level sent in the original who
The multi-purpose reply to the multi-purpose information-requesting 'who'
packet. The MUD is responsible for building the return data, including the
format of it. The mud can use the permission level sent in the original who
packet to filter the output. The example below is the MUD-Net format.
Data:
text=<string> The formatted reply to a 'who' packet.
Additional Notes:
The example below is for the who list packet. The same construction would
The example below is for the who list packet. The same construction would
go into formatting the other types of who packets.
Example:
*@YourMUD 1234567890 YourMUD who-reply Dude@SomeMUD text="\n\r~R-=< ~WPlayers on YourMUD ~R>=-\n\r ~Y-=< ~Wtelnet://yourmud.domain.com:1234 ~Y>=-\n\r\n\r~B--------------------------------=< ~WPlayers ~B>=---------------------------------\n\r\n\r ~BPlayer ~z<--->~G Mortal the Toy\n\r\n\r~R-------------------------------=< ~WImmortals ~R>=--------------------------------\n\r\n\r ~YStaff ~z<--->~G You the Immortal\n\r\n\r~Y<~W2 Players~Y> ~Y<~WHomepage: http://www.yourmud.com~Y> <~W 2 Max Since Reboot~Y>\n\r~Y<~W3 logins since last reboot on Tue Feb 24, 2004 6:55:59 PM EST~Y>"
*@YourMUD 1234567890 YourMUD who-reply Dude@SomeMUD text="\n\r~R-=< ~WPlayers on YourMUD ~R>=-\n\r ~Y-=< ~Wtelnet://yourmud.domain.com:1234 ~Y>=-\n\r\n\r~B--------------------------------=< ~WPlayers ~B>=---------------------------------\n\r\n\r ~BPlayer ~z<--->~G Mortal the Toy\n\r\n\r~R-------------------------------=< ~WImmortals ~R>=--------------------------------\n\r\n\r ~YStaff ~z<--->~G You the Immortal\n\r\n\r~Y<~W2 Players~Y> ~Y<~WHomepage: http://www.yourmud.com~Y> <~W 2 Max Since Reboot~Y>\n\r~Y<~W3 logins since last reboot on Tue Feb 24, 2004 6:55:59 PM EST~Y>"
"""
pass
@ -646,12 +646,12 @@ class IMC2PacketWhois(IMC2Packet):
"""
Description:
Sends a request to the network for the location of the specified player.
Data:
level=<int> The permission level of the person making the request.
Example:
You@YourMUD 1234567890 YourMUD whois dude@* level=5
You@YourMUD 1234567890 YourMUD whois dude@* level=5
"""
def __init__(self, pobject_id, whois_target):
super(IMC2PacketWhois, self).__init__()
@ -664,98 +664,98 @@ class IMC2PacketWhois(IMC2Packet):
class IMC2PacketWhoisReply(IMC2Packet):
"""
Description:
The reply to a whois packet. The MUD is responsible for building and formatting
the text sent back to the requesting player, and can use the permission level
The reply to a whois packet. The MUD is responsible for building and formatting
the text sent back to the requesting player, and can use the permission level
sent in the original whois packet to filter or block the response.
Data:
text=<string> The whois text.
Example:
*@SomeMUD 1234567890 SomeMUD!Hub1 whois-reply You@YourMUD text="~RIMC Locate: ~YDude@SomeMUD: ~cOnline.\n\r"
*@SomeMUD 1234567890 SomeMUD!Hub1 whois-reply You@YourMUD text="~RIMC Locate: ~YDude@SomeMUD: ~cOnline.\n\r"
"""
pass
class IMC2PacketBeep(IMC2Packet):
"""
Description:
Sends out a beep packet to the Player@MUD. The client receiving this should
Sends out a beep packet to the Player@MUD. The client receiving this should
then send a bell-character to the target player to 'beep' them.
Example:
You@YourMUD 1234567890 YourMUD beep dude@somemud
You@YourMUD 1234567890 YourMUD beep dude@somemud
"""
pass
class IMC2PacketIceChanWho(IMC2Packet):
"""
Description:
Sends a request to the specified MUD or * to list all the users listening
Sends a request to the specified MUD or * to list all the users listening
to the specified channel.
Data:
level=<int>
level=<int>
Sender's permission level.
channel=<string>
channel=<string>
The server:chan name of the channel.
lname=<string>
lname=<string>
The localname of the channel.
Example:
You@YourMUD 1234567890 YourMUD ice-chan-who somemud level=5 channel=Hub1:ichat lname=ichat
You@YourMUD 1234567890 YourMUD ice-chan-who somemud level=5 channel=Hub1:ichat lname=ichat
"""
pass
class IMC2PacketIceChanWhoReply(IMC2Packet):
"""
Description:
This is the reply packet for an ice-chan-who. The MUD is responsible for
creating and formatting the list sent back in the 'list' field. The
permission level sent in the original ice-chan-who packet can be used to
This is the reply packet for an ice-chan-who. The MUD is responsible for
creating and formatting the list sent back in the 'list' field. The
permission level sent in the original ice-chan-who packet can be used to
filter or block the response.
Data:
channel=<string>
channel=<string>
The server:chan of the requested channel.
list=<string>
list=<string>
The formatted list of local listeners for that MUD.
Example:
*@SomeMUD 1234567890 SomeMUD!Hub1 ice-chan-whoreply You@YourMUD channel=Hub1:ichat list="The following people are listening to ichat on SomeMUD:\n\r\n\rDude\n\r"
*@SomeMUD 1234567890 SomeMUD!Hub1 ice-chan-whoreply You@YourMUD channel=Hub1:ichat list="The following people are listening to ichat on SomeMUD:\n\r\n\rDude\n\r"
"""
pass
class IMC2PacketLaston(IMC2Packet):
"""
Description:
This packet queries the server the mud is connected to to find out when a
This packet queries the server the mud is connected to to find out when a
specified user was last seen by the network on a public channel.
Data:
username=<string> The user, user@mud, or "all" being queried. Responses
username=<string> The user, user@mud, or "all" being queried. Responses
to this packet will be sent by the server in the form of a series of tells.
Example: User@MUD 1234567890 MUD imc-laston SERVER username=somenamehere
Example: User@MUD 1234567890 MUD imc-laston SERVER username=somenamehere
"""
pass
class IMC2PacketCloseNotify(IMC2Packet):
"""
Description:
This packet alerts the network when a server or MUD has disconnected. The
server hosting the server or MUD is responsible for sending this packet
out across the network. Clients need only process the packet to remove the
This packet alerts the network when a server or MUD has disconnected. The
server hosting the server or MUD is responsible for sending this packet
out across the network. Clients need only process the packet to remove the
disconnected MUD from their MUD list (or mark it as Disconnected).
Data:
host=<string>
host=<string>
The MUD or server that has disconnected from the network.
Example:
*@Hub2 1234567890 Hub2!Hub1 close-notify *@* host=DisconnMUD
*@Hub2 1234567890 Hub2!Hub1 close-notify *@* host=DisconnMUD
"""
pass
@ -763,4 +763,4 @@ if __name__ == "__main__":
packstr = "Kayle@MW 1234567 MW!Server02!Server01 ice-msg-b *@* channel=Server01:ichat text=\"*they're going woot\" emote=0 echo=1"
packstr = "*@Lythelian 1234567 Lythelian!Server01 is-alive *@* versionid=\"Tim's LPC IMC2 client 30-Jan-05 / Dead Souls integrated\" networkname=Mudbytes url=http://dead-souls.net host=70.32.76.142 port=6666 sha256=0"
print IMC2Packet(packstr)

View file

@ -2,7 +2,7 @@
Certain periodic packets are sent by connected MUDs (is-alive, user-cache,
etc). The IMC2 protocol assumes that each connected MUD will capture these and
populate/maintain their own lists of other servers connected. This module
contains stuff like this.
contains stuff like this.
"""
from time import time
@ -20,7 +20,7 @@ class IMC2Mud(object):
self.sha256 = packet.optional_data.get('sha256', None)
# This is used to determine when a Mud has fallen into inactive status.
self.last_updated = time()
class IMC2MudList(object):
"""
Keeps track of other MUDs connected to the IMC network.
@ -28,7 +28,7 @@ class IMC2MudList(object):
def __init__(self):
# Mud list is stored in a dict, key being the IMC Mud name.
self.mud_list = {}
def get_mud_list(self):
"""
Returns a sorted list of connected Muds.
@ -36,7 +36,7 @@ class IMC2MudList(object):
muds = self.mud_list.items()
muds.sort()
return [value for key, value in muds]
def update_mud_from_packet(self, packet):
"""
This grabs relevant info from the packet and stuffs it in the
@ -44,7 +44,7 @@ class IMC2MudList(object):
"""
mud = IMC2Mud(packet)
self.mud_list[mud.name] = mud
def remove_mud_from_packet(self, packet):
"""
Removes a mud from the Mud list when given a packet.
@ -55,7 +55,7 @@ class IMC2MudList(object):
except KeyError:
# No matching entry, no big deal.
pass
class IMC2Channel(object):
"""
Stores information about channels available on the network.
@ -67,7 +67,7 @@ class IMC2Channel(object):
self.owner = packet.optional_data.get('owner', None)
self.policy = packet.optional_data.get('policy', None)
self.last_updated = time()
class IMC2ChanList(object):
"""
Keeps track of other MUDs connected to the IMC network.
@ -75,7 +75,7 @@ class IMC2ChanList(object):
def __init__(self):
# Chan list is stored in a dict, key being the IMC Mud name.
self.chan_list = {}
def get_channel_list(self):
"""
Returns a sorted list of cached channels.
@ -83,7 +83,7 @@ class IMC2ChanList(object):
channels = self.chan_list.items()
channels.sort()
return [value for key, value in channels]
def update_channel_from_packet(self, packet):
"""
This grabs relevant info from the packet and stuffs it in the
@ -91,7 +91,7 @@ class IMC2ChanList(object):
"""
channel = IMC2Channel(packet)
self.chan_list[channel.name] = channel
def remove_channel_from_packet(self, packet):
"""
Removes a channel from the Channel list when given a packet.

View file

@ -11,6 +11,8 @@ from src.comms.models import ExternalChannelConnection, Channel
from src.utils import logger, utils
from src.server.sessionhandler import SESSIONS
from django.utils.translation import ugettext as _
INFOCHANNEL = Channel.objects.channel_search(settings.CHANNEL_MUDINFO[0])
IRC_CHANNELS = []
@ -27,77 +29,77 @@ def msg_info(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.
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):
def signedOn(self):
# 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.
# can access it later to send data.
global IRC_CHANNELS
self.join(self.factory.channel)
IRC_CHANNELS.append(self)
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 = _("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
"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:
return
#format message:
user = user.split("!")[0]
if user:
user.strip()
else:
user = "Unknown"
user = _("Unknown")
msg = "[%s] %s@%s: %s" % (self.factory.evennia_channel, user, irc_channel, msg.strip())
#logger.log_infomsg("<IRC: " + msg)
#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.
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.
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 IRCbotFactory(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.pretty_key = "%s:%s%s ('%s')" % (network, port, channel, nickname)
self.network = network
self.port = port
self.port = port
self.channel = channel
self.nickname = nickname
self.evennia_channel = evennia_channel
def clientConnectionLost(self, connector, reason):
def clientConnectionLost(self, connector, reason):
from twisted.internet.error import ConnectionDone
if type(reason.type) == type(ConnectionDone):
msg_info("Connection closed.")
msg_info(_("Connection closed."))
else:
msg_info("Lost connection %s. Reason: '%s'. Reconnecting." % (self.pretty_key, reason))
msg_info(_("Lost connection %(key)s. Reason: '%(reason)s'. Reconnecting.") % {"key":self.pretty_key, "reason":reason})
connector.connect()
def clientConnectionFailed(self, connector, reason):
msg = "Could not connect %s Reason: '%s'" % (self.pretty_key, reason)
msg = _("Could not connect %(key)s Reason: '%(reason)s'") % {"key":self.pretty_key, "reason":reason}
msg_info(msg)
logger.log_errmsg(msg)
@ -112,70 +114,70 @@ def build_service_key(key):
def create_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
"""
This will create a new IRC<->channel connection.
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)
logger.log_errmsg(_("Cannot attach IRC<->Evennia: Evennia Channel '%s' not found") % channel)
return False
channel = new_channel[0]
key = build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick)
old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
if old_conns:
return False
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 = "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 = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
db_external_config=config)
conn.save()
# connect
# connect
connect_to_irc(conn)
return True
return True
def delete_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
"Destroy a connection"
if hasattr(channel, 'key'):
channel = channel.key
key = build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick)
key = build_connection_key(channel, 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()
return False
conn.delete()
try:
service = SESSIONS.server.services.getServiceNamed(service_key)
except Exception:
return True
if service.running:
return True
if service.running:
SESSIONS.server.services.removeService(service)
return True
return True
def connect_to_irc(connection):
"Create the bot instance and connect to the IRC network and channel."
"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), IRCbotFactory(key, irc_channel, irc_network, irc_port, irc_bot_nick,
# connect
bot = internet.TCPClient(irc_network, int(irc_port), IRCbotFactory(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.
Activate all irc bots.
"""
for connection in ExternalChannelConnection.objects.filter(db_external_key__startswith='irc_'):
connect_to_irc(connection)

View file

@ -1,5 +1,5 @@
"""
These managers handles the
These managers handles the
"""
import itertools
@ -18,7 +18,7 @@ def to_object(inp, objtype='player'):
Locates the object related to the given
playername or channel key. If input was already
the correct object, return it.
inp - the input object/string
inp - the input object/string
objtype - 'player' or 'channel'
"""
from src.players.models import PlayerDB
@ -30,43 +30,43 @@ def to_object(inp, objtype='player'):
else:
umatch = PlayerDB.objects.filter(user__username__iexact=inp)
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]
return umatch[0]
else:
# have to import this way to avoid circular imports
from src.comms.models import Channel
#= ContentType.objects.get(app_label="comms",
from src.comms.models import Channel
#= ContentType.objects.get(app_label="comms",
# model="channel").model_class()
if type(inp) == Channel:
return inp
cmatch = Channel.objects.filter(db_key__iexact=inp)
if cmatch:
return cmatch[0]
return None
return None
#
# Msg manager
#
class MsgManager(models.Manager):
"""
This MsgManager implements methods for searching
This MsgManager implements methods for searching
and manipulating Messages directly from the database.
These methods will all return database objects
These methods will all return database objects
(or QuerySets) directly.
A Message represents one unit of communication, be it over a
A Message represents one unit of communication, be it over a
Channel or via some form of in-game mail system. Like an e-mail,
it always has a sender and can have any number of receivers (some
of which may be Channels).
of which may be Channels).
Evennia-specific:
get_message_by_id
get_messages_by_sender
@ -75,22 +75,22 @@ class MsgManager(models.Manager):
text_search
message_search (equivalent to ev.search_messages)
"""
def get_message_by_id(self, idnum):
"Retrieve message by its id."
"Retrieve message by its id."
try:
idnum = int(idnum)
return self.get(id=id)
except Exception:
return None
def get_messages_by_sender(self, player):
"""
Get all messages sent by one player
"""
player = to_object(player, objtype='player')
player = to_object(player, objtype='player')
if not player:
return None
return None
return self.filter(db_sender=player).exclude(db_hide_from_sender=True)
def get_messages_by_receiver(self, receiver):
@ -99,7 +99,7 @@ class MsgManager(models.Manager):
"""
receiver = to_object(receiver)
if not receiver:
return None
return None
return [msg for msg in self.all()
if receiver in msg.receivers
and receiver not in msg.hide_from_receivers]
@ -110,30 +110,30 @@ class MsgManager(models.Manager):
"""
channel = to_object(channel, objtype='channel')
if not channel:
return None
return None
return [msg for msg in self.all()
if channel in msg.channels
if channel in msg.channels
and channel not in msg.hide_from_channels]
#TODO add search limited by send_times
#TODO add search limited by send_times
def text_search(self, searchstring, filterdict=None):
"""
Returns all messages that contain the matching
search string. To avoid too many results, and also
since this can be a very computing-
heavy operation, it's recommended to be filtered
by at least channel or sender/receiver.
by at least channel or sender/receiver.
searchstring - string to search for
filterdict -
{'channels':[list],
'senders':[list],
'receivers':[list]}
lists can contain either the name/keys of the
objects or the actual objects to filter by.
"""
objects or the actual objects to filter by.
"""
if filterdict:
# obtain valid objects for all filters
# obtain valid objects for all filters
channels = [chan for chan in
[to_object(chan, objtype='channel')
for chan in filterdict.get('channels',[])]
@ -141,23 +141,23 @@ class MsgManager(models.Manager):
senders = [sender for sender in
[to_object(sender)
for sender in filterdict.get('senders',[])]
if sender]
if sender]
receivers = [receiver for receiver in
[to_object(receiver)
for receiver in filterdict.get('receivers',[])]
if receiver]
# filter the messages lazily using the filter objects
# filter the messages lazily using the filter objects
msgs = []
for sender in senders:
msgs = list(sender.message_set.filter(
db_message__icontains=searchstring))
db_message__icontains=searchstring))
for receiver in receivers:
rec_msgs = receiver.message_set.filter(
db_message__icontains=searchstring)
if msgs:
msgs = [msg for msg in rec_msgs if msg in msgs]
else:
msgs = rec_msgs
msgs = rec_msgs
for channel in channels:
chan_msgs = list(channel.message_set.filter(
db_message__icontains=searchstring))
@ -168,21 +168,21 @@ class MsgManager(models.Manager):
return list(set(msgs))
return list(self.all().filter(db_message__icontains=searchstring))
def message_search(self, sender=None, receiver=None, channel=None, freetext=None, dbref=None):
def message_search(self, sender=None, receiver=None, channel=None, freetext=None, dbref=None):
"""
Search the message database for particular messages. At least one
of the arguments must be given to do a search.
Search the message database for particular messages. At least one
of the arguments must be given to do a search.
sender - get messages sent by a particular player
receiver - get messages received by a certain player or players
channel - get messages sent to a particular channel or channels
freetext - Search for a text string in a message.
freetext - Search for a text string in a message.
NOTE: This can potentially be slow, so make sure to supply
one of the other arguments to limit the search.
dbref - (int) the exact database id of the message. This will override
one of the other arguments to limit the search.
dbref - (int) the exact database id of the message. This will override
all other search crieteria since it's unique and
always gives a list with only one match.
"""
"""
if dbref:
return self.filter(id=dbref)
if freetext:
@ -199,12 +199,12 @@ class MsgManager(models.Manager):
msgs = []
if sender:
msgs = self.get_messages_by_sender(sender)
if receiver:
if receiver:
rec_msgs = self.get_messages_by_receiver(receiver)
if msgs:
msgs = [msg for msg in rec_msgs if msg in msgs]
else:
msgs = rec_msgs
msgs = rec_msgs
if channel:
chan_msgs = self.get_messaqge_by_channel(channel)
if msgs:
@ -212,20 +212,20 @@ class MsgManager(models.Manager):
else:
msgs = chan_msgs
return msgs
#
# Channel manager
#
class ChannelManager(models.Manager):
"""
This ChannelManager implements methods for searching
This ChannelManager implements methods for searching
and manipulating Channels directly from the database.
These methods will all return database objects
These methods will all return database objects
(or QuerySets) directly.
A Channel is an in-game venue for communication. It's
A Channel is an in-game venue for communication. It's
essentially representation of a re-sender: Users sends
Messages to the Channel, and the Channel re-sends those
messages to all users subscribed to the Channel.
@ -248,9 +248,9 @@ class ChannelManager(models.Manager):
def get_channel(self, channelkey):
"""
Return the channel object if given its key.
Also searches its aliases.
Also searches its aliases.
"""
# first check the channel key
# first check the channel key
channels = self.filter(db_key__iexact=channelkey)
if not channels:
# also check aliases
@ -258,23 +258,23 @@ class ChannelManager(models.Manager):
if channelkey in channel.aliases]
if channels:
return channels[0]
return None
return None
def del_channel(self, channelkey):
"""
Delete channel matching channelkey.
Also cleans up channelhandler.
Also cleans up channelhandler.
"""
channels = self.filter(db_key__iexact=channelkey)
if not channels:
# no aliases allowed for deletion.
return False
return False
for channel in channels:
channel.delete()
channel.delete()
from src.comms.channelhandler import CHANNELHANDLER
CHANNELHANDLER.update()
return None
return None
def get_all_connections(self, channel):
"""
Return the connections of all players listening
@ -282,9 +282,9 @@ class ChannelManager(models.Manager):
"""
# import here to avoid circular imports
#from src.comms.models import PlayerChannelConnection
PlayerChannelConnection = ContentType.objects.get(app_label="comms",
PlayerChannelConnection = ContentType.objects.get(app_label="comms",
model="playerchannelconnection").model_class()
ExternalChannelConnection = ContentType.objects.get(app_label="comms",
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))
@ -297,10 +297,10 @@ class ChannelManager(models.Manager):
"""
channels = []
try:
# try an id match first
# try an id match first
dbref = int(ostring.strip('#'))
channels = self.filter(id=dbref)
except Exception:
except Exception:
pass
if not channels:
# no id match. Search on the key.
@ -308,17 +308,17 @@ class ChannelManager(models.Manager):
if not channels:
# still no match. Search by alias.
channels = [channel for channel in self.all() if ostring.lower in [a.lower for a in channel.aliases]]
return channels
return channels
#
# PlayerChannelConnection manager
#
class PlayerChannelConnectionManager(models.Manager):
"""
This PlayerChannelConnectionManager implements methods for searching
This PlayerChannelConnectionManager implements methods for searching
and manipulating PlayerChannelConnections directly from the database.
These methods will all return database objects
These methods will all return database objects
(or QuerySets) directly.
A PlayerChannelConnection defines a user's subscription to an in-game
@ -333,7 +333,7 @@ class PlayerChannelConnectionManager(models.Manager):
break_connection
"""
def get_all_player_connections(self, player):
"Get all connections that the given player has."
player = to_object(player)
@ -346,18 +346,18 @@ class PlayerChannelConnectionManager(models.Manager):
if player and channel:
return self.filter(db_player=player).filter(db_channel=channel).count() > 0
return False
def get_all_connections(self, channel):
"""
Get all connections for a channel
"""
channel = to_object(channel, objtype='channel')
return self.filter(db_channel=channel)
def create_connection(self, player, channel):
"""
Connect a player to a channel. player and channel
can be actual objects or keystrings.
can be actual objects or keystrings.
"""
player = to_object(player)
channel = to_object(channel, objtype='channel')
@ -379,13 +379,13 @@ class PlayerChannelConnectionManager(models.Manager):
class ExternalChannelConnectionManager(models.Manager):
"""
This ExternalChannelConnectionManager implements methods for searching
This ExternalChannelConnectionManager implements methods for searching
and manipulating HelpEntries directly from the database.
These methods will all return database objects
These methods will all return database objects
(or QuerySets) directly.
An ExternalChannelConnetion describes the connection between an in-game
An ExternalChannelConnetion describes the connection between an in-game
channel and some external source, such as an IRC or IMC channel.
Evennia-specific:
@ -394,9 +394,9 @@ class ExternalChannelConnectionManager(models.Manager):
get_all_connections
create_connection
break_connection
"""
def get_all_external_connections(self, external):
"Get all connections that the given as external."
external = to_object(external, objtype='external')
@ -409,18 +409,18 @@ class ExternalChannelConnectionManager(models.Manager):
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.
can be actual objects or keystrings.
"""
channel = to_object(channel, objtype='channel')
if not channel:

View file

@ -1,24 +1,24 @@
"""
RSS parser for Evennia
This connects an RSS feed to an in-game Evennia channel, sending messages
to the channel whenever the feed updates.
This connects an RSS feed to an in-game Evennia channel, sending messages
to the channel whenever the feed updates.
"""
import re
from twisted.internet import task
from django.conf import settings
from src.comms.models import ExternalChannelConnection, Channel
from django.conf import settings
from src.comms.models import ExternalChannelConnection, Channel
from src.utils import logger, utils
from src.scripts.models import ScriptDB
RSS_ENABLED = settings.RSS_ENABLED
RSS_ENABLED = settings.RSS_ENABLED
RSS_UPDATE_INTERVAL = settings.RSS_UPDATE_INTERVAL
INFOCHANNEL = Channel.objects.channel_search(settings.CHANNEL_MUDINFO[0])
RETAG = re.compile(r'<[^>]*?>')
# holds rss readers they can be shut down at will.
# holds rss readers they can be shut down at will.
RSS_READERS = {}
def msg_info(message):
@ -35,8 +35,8 @@ if RSS_ENABLED:
try:
import feedparser
except ImportError:
raise ImportError("RSS requirs python-feedparser to be installed. Install or set RSS_ENABLED=False.")
raise ImportError("RSS requires python-feedparser to be installed. Install or set RSS_ENABLED=False.")
class RSSReader(object):
"""
Reader script used to connect to each individual RSS feed
@ -45,47 +45,47 @@ class RSSReader(object):
"""
The reader needs an rss url and It also needs an interval
for how often it is to check for new updates (defaults
to 10 minutes)
to 10 minutes)
"""
self.key = key
self.url = url
self.interval = interval
self.url = url
self.interval = interval
self.entries = {} # stored feeds
self.task = None
# first we do is to load the feed so we don't resend
# old entries whenever the reader starts.
self.update_feed()
self.task = None
# first we do is to load the feed so we don't resend
# old entries whenever the reader starts.
self.update_feed()
# start runner
self.start()
def update_feed(self):
"Read the url for new updated data and determine what's new."
feed = feedparser.parse(self.url)
new = []
for entry in (e for e in feed['entries'] if e['id'] not in self.entries):
txt = "[RSS] %s: %s" % (RETAG.sub("", entry['title']), entry['link'].replace('\n','').encode('utf-8'))
txt = "[RSS] %s: %s" % (RETAG.sub("", entry['title']), entry['link'].replace('\n','').encode('utf-8'))
self.entries[entry['id']] = txt
new.append(txt)
return new
new.append(txt)
return new
def update(self):
"""
Called every self.interval seconds - tries to get new feed entries,
and if so, uses the appropriate ExternalChannelConnection to send the
data to subscribing channels.
data to subscribing channels.
"""
new = self.update_feed()
if not new:
return
conns = ExternalChannelConnection.objects.filter(db_external_key=self.key)
for conn in (conn for conn in conns if conn.channel):
for txt in new:
if not new:
return
conns = ExternalChannelConnection.objects.filter(db_external_key=self.key)
for conn in (conn for conn in conns if conn.channel):
for txt in new:
conn.to_channel("%s:%s" % (conn.channel.key, txt))
def start(self):
"""
Starting the update task and store a reference in the
global variable so it can be found and shut down later.
Starting the update task and store a reference in the
global variable so it can be found and shut down later.
"""
global RSS_READERS
self.task = task.LoopingCall(self.update)
@ -95,7 +95,7 @@ class RSSReader(object):
def build_connection_key(channel, url):
"This is used to id the connection"
if hasattr(channel, 'key'):
channel = channel.key
channel = channel.key
return "rss_%s>%s" % (url, channel)
def create_connection(channel, url, interval):
@ -106,19 +106,19 @@ def create_connection(channel, url, interval):
new_channel = Channel.objects.filter(db_key=channel)
if not new_channel:
logger.log_errmsg("Cannot attach RSS->Evennia: Evennia Channel '%s' not found." % channel)
return False
return False
channel = new_channel[0]
key = build_connection_key(channel, url)
old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
if old_conns:
return False
return False
config = "%s|%i" % (url, interval)
# There is no sendback from evennia to the rss, so we need not define any sendback code.
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_config=config)
conn.save()
conn.save()
connect_to_rss(conn)
return True
return True
def delete_connection(channel, url):
"""
@ -128,29 +128,29 @@ def delete_connection(channel, url):
try:
conn = ExternalChannelConnection.objects.get(db_external_key=key)
except Exception:
return False
return False
conn.delete()
reader = RSS_READERS.get(key, None)
if reader and reader.task:
if reader and reader.task:
reader.task.stop()
return True
return True
def connect_to_rss(connection):
"""
Create the parser instance and connect to RSS feed and channel
"""
global RSS_READERS
key = utils.to_str(connection.external_key)
key = utils.to_str(connection.external_key)
url, interval = [utils.to_str(conf) for conf in connection.external_config.split('|')]
# Create reader (this starts the running task and stores a reference in RSS_TASKS)
RSSReader(key, url, int(interval))
def connect_all():
"""
Activate all rss feed parsers
Activate all rss feed parsers
"""
if not RSS_ENABLED:
return
return
for connection in ExternalChannelConnection.objects.filter(db_external_key__startswith="rss_"):
print "connecting RSS: %s" % connection
connect_to_rss(connection)

View file

@ -107,7 +107,10 @@ to any other identifier you can use.
import re, inspect
from django.conf import settings
from src.utils import logger, utils
from django.utils.translation import ugettext as _
__all__ = ("LockHandler", )
#
# Exception class
#
@ -215,7 +218,7 @@ class LockHandler(object):
funcname, rest = (part.strip().strip(')') for part in funcstring.split('(', 1))
func = _LOCKFUNCS.get(funcname, None)
if not callable(func):
elist.append("Lock: function '%s' is not available." % funcstring)
elist.append(_("Lock: function '%s' is not available.") % funcstring)
continue
args = list(arg.strip() for arg in rest.split(',') if not '=' in arg)
kwargs = dict([arg.split('=', 1) for arg in rest.split(',') if '=' in arg])
@ -228,12 +231,12 @@ class LockHandler(object):
evalstring = " ".join(_RE_OK.findall(evalstring))
eval(evalstring % tuple(True for func in funclist), {}, {})
except Exception:
elist.append("Lock: definition '%s' has syntax errors." % raw_lockstring)
elist.append(_("Lock: definition '%s' has syntax errors.") % raw_lockstring)
continue
if access_type in locks:
duplicates += 1
wlist.append("Lock: access type '%s' changed from '%s' to '%s' " % \
(access_type, locks[access_type][2], raw_lockstring))
wlist.append(_("Lock: access type '%(access_type)s' changed from '%(source)s' to '%(goal)s' " % \
{"access_type":access_type, "source":locks[access_type][2], "goal":raw_lockstring}))
locks[access_type] = (evalstring, tuple(lock_funcs), raw_lockstring)
if wlist and self.log_obj:
# a warning text was set, it's not an error, so only report if log_obj is available.
@ -266,17 +269,17 @@ class LockHandler(object):
# sanity checks
for lockdef in lockstring.split(';'):
if not ':' in lockstring:
self._log_error("Lock: '%s' contains no colon (:)." % lockdef)
self._log_error(_("Lock: '%s' contains no colon (:).") % lockdef)
return False
access_type, rhs = [part.strip() for part in lockdef.split(':', 1)]
if not access_type:
self._log_error("Lock: '%s' has no access_type (left-side of colon is empty)." % lockdef)
self._log_error(_("Lock: '%s' has no access_type (left-side of colon is empty).") % lockdef)
return False
if rhs.count('(') != rhs.count(')'):
self._log_error("Lock: '%s' has mismatched parentheses." % lockdef)
self._log_error(_("Lock: '%s' has mismatched parentheses.") % lockdef)
return False
if not _RE_FUNCS.findall(rhs):
self._log_error("Lock: '%s' has no valid lock functions." % lockdef)
self._log_error(_("Lock: '%s' has no valid lock functions.") % lockdef)
return False
# get the lock string
storage_lockstring = self.obj.lock_storage

View file

@ -9,6 +9,8 @@ from src.scripts.models import ScriptDB
from src.utils import create
from src.utils import logger
from django.utils.translation import ugettext as _
class ScriptHandler(object):
"""
Implements the handler. This sits on each game object.
@ -38,11 +40,8 @@ class ScriptHandler(object):
repeats = script.repeats
try: next_repeat = script.time_until_next_repeat()
except: next_repeat = "?"
string += "\n '%s' (%s/%s, %s repeats): %s" % (script.key,
next_repeat,
interval,
repeats,
script.desc)
string += _("\n '%(key)s' (%(next_repeat)s/%(interval)s, %(repeats)s repeats): %(desc)s") % \
{"key":script.key, "next_repeat":next_repeat, "interval":interval,"repeats":repeats,"desc":script.desc}
return string.strip()
def add(self, scriptclass, key=None, autostart=True):

View file

@ -17,6 +17,7 @@ from src.typeclasses.models import _ATTRIBUTE_CACHE
from src.scripts.models import ScriptDB
from src.comms import channelhandler
from src.utils import logger
from django.utils.translation import ugettext as _
__all__ = ("Script", "DoNothing", "CheckSessions", "ValidateScripts", "ValidateChannelHandler", "ClearAttributeCache")
@ -66,7 +67,8 @@ class ScriptClass(TypeClass):
def _step_err_callback(self, e):
"callback for runner errors"
cname = self.__class__.__name__
estring = "Script %s(#%i) of type '%s': at_repeat() error '%s'." % (self.key, self.dbid, cname, e.getErrorMessage())
estring = _("Script %(key)s(#%(dbid)i) of type '%(cname)s': at_repeat() error '%(err)s'.") % \
{"key":self.key, "dbid":self.dbid, "cname":cname, "err":e.getErrorMessage()}
try:
self.dbobj.db_obj.msg(estring)
except Exception:
@ -392,14 +394,14 @@ class DoNothing(Script):
def at_script_creation(self):
"Setup the script"
self.key = "sys_do_nothing"
self.desc = "This is an empty placeholder script."
self.desc = _("This is an empty placeholder script.")
class CheckSessions(Script):
"Check sessions regularly."
def at_script_creation(self):
"Setup the script"
self.key = "sys_session_check"
self.desc = "Checks sessions so they are live."
self.desc = _("Checks sessions so they are live.")
self.interval = 60 # repeat every 60 seconds
self.persistent = True
@ -414,7 +416,7 @@ class ValidateScripts(Script):
def at_script_creation(self):
"Setup the script"
self.key = "sys_scripts_validate"
self.desc = "Validates all scripts regularly."
self.desc = _("Validates all scripts regularly.")
self.interval = 3600 # validate every hour.
self.persistent = True
@ -428,7 +430,7 @@ class ValidateChannelHandler(Script):
def at_script_creation(self):
"Setup the script"
self.key = "sys_channels_validate"
self.desc = "Updates the channel handler"
self.desc = _("Updates the channel handler")
self.interval = 3700 # validate a little later than ValidateScripts
self.persistent = True
@ -442,7 +444,7 @@ class ClearAttributeCache(Script):
def at_script_creation(self):
"Setup the script"
self.key = "sys_cache_clear"
self.desc = "Clears the Attribute Cache"
self.desc = _("Clears the Attribute Cache")
self.interval = 3600 * 2
self.persistent = True
def at_repeat(self):

View file

@ -13,7 +13,6 @@ from src.server.models import ServerConfig
from src.help.models import HelpEntry
from src.utils import create
# i18n
from django.utils.translation import ugettext as _
def create_config_values():
@ -34,7 +33,7 @@ def create_objects():
Creates the #1 player and Limbo room.
"""
print _(" Creating objects (Player #1 and Limbo room) ...")
print " Creating objects (Player #1 and Limbo room) ..."
# Set the initial User's account object's username on the #1 object.
# This object is pure django and only holds name, email and password.
@ -89,7 +88,7 @@ def create_channels():
"""
Creates some sensible default channels.
"""
print _(" Creating default channels ...")
print " Creating default channels ..."
# public channel
key, aliases, desc, locks = settings.CHANNEL_PUBLIC
@ -112,12 +111,11 @@ def import_MUX_help_files():
"""
Imports the MUX help files.
"""
print _(" Importing MUX help database (devel reference only) ...")
print " Importing MUX help database (devel reference only) ..."
management.call_command('loaddata', '../src/help/mux_help_db.json', verbosity=0)
# categorize the MUX help files into its own category.
default_category = "MUX"
print _(" Moving imported help db to help category '%(default)s'." \
% {'default': default_category})
print " Moving imported help db to help category '%(default)s'." % {'default': default_category}
HelpEntry.objects.all_to_category(default_category)
def create_system_scripts():
@ -127,7 +125,7 @@ def create_system_scripts():
"""
from src.scripts import scripts
print _(" Creating and starting global scripts ...")
print " Creating and starting global scripts ..."
# check so that all sessions are alive.
script1 = create.create_script(scripts.CheckSessions)
@ -138,7 +136,7 @@ def create_system_scripts():
# clear the attribute cache regularly
script4 = create.create_script(scripts.ClearAttributeCache)
if not script1 or not script2 or not script3 or not script4:
print _(" Error creating system scripts.")
print " Error creating system scripts."
def start_game_time():
"""
@ -147,7 +145,7 @@ def start_game_time():
the total run time of the server as well as its current uptime
(the uptime can also be found directly from the server though).
"""
print _(" Starting in-game time ...")
print " Starting in-game time ..."
from src.utils import gametime
gametime.init_gametime()
@ -170,20 +168,20 @@ def create_admin_media_links():
dpath = os.path.join(django.__path__[0], 'contrib', 'admin', 'static', 'admin')
apath = os.path.join(settings.ADMIN_MEDIA_ROOT)
if os.path.isdir(apath):
print _(" ADMIN_MEDIA_ROOT already exists. Ignored.")
print " ADMIN_MEDIA_ROOT already exists. Ignored."
return
if os.name == 'nt':
print _(" Admin-media files copied to ADMIN_MEDIA_ROOT (Windows mode).")
print " Admin-media files copied to ADMIN_MEDIA_ROOT (Windows mode)."
os.mkdir(apath)
os.system('xcopy "%s" "%s" /e /q /c' % (dpath, apath))
if os.name == 'posix':
try:
os.symlink(dpath, apath)
print _(" Admin-media symlinked to ADMIN_MEDIA_ROOT.")
print " Admin-media symlinked to ADMIN_MEDIA_ROOT."
except OSError, e:
print _(" There was an error symlinking Admin-media to ADMIN_MEDIA_ROOT:\n %s\n -> \n %s\n (%s)\n If you see issues, link manually." % (dpath, apath, e))
print " There was an error symlinking Admin-media to ADMIN_MEDIA_ROOT:\n %s\n -> \n %s\n (%s)\n If you see issues, link manually." % (dpath, apath, e)
else:
print _(" Admin-media files should be copied manually to ADMIN_MEDIA_ROOT.")
print " Admin-media files should be copied manually to ADMIN_MEDIA_ROOT."
def at_initial_setup():
"""
@ -199,7 +197,7 @@ def at_initial_setup():
mod = __import__(modname, fromlist=[None])
except (ImportError, ValueError):
return
print _(" Running at_initial_setup() hook.")
print " Running at_initial_setup() hook."
if mod.__dict__.get("at_initial_setup", None):
mod.at_initial_setup()
@ -211,7 +209,7 @@ def reset_server():
It also checks so the warm-reset mechanism works as it should.
"""
from src.server.sessionhandler import SESSIONS
print _(" Initial setup complete. Resetting/reloading Server.")
print " Initial setup complete. Resetting/reloading Server."
SESSIONS.server.shutdown(mode='reset')
def handle_setup(last_step):

View file

@ -18,9 +18,6 @@ from src.utils.idmapper.models import SharedMemoryModel
from src.utils import logger, utils
from src.server.manager import ServerConfigManager
# i18n
from django.utils.translation import ugettext as _
#------------------------------------------------------------
#
# ServerConfig
@ -88,7 +85,7 @@ class ServerConfig(SharedMemoryModel):
"Setter. Allows for self.value = value"
if utils.has_parent('django.db.models.base.Model', value):
# we have to protect against storing db objects.
logger.log_errmsg(_("ServerConfig cannot store db objects! (%s)" % value))
logger.log_errmsg("ServerConfig cannot store db objects! (%s)" % value)
return
self.db_value = pickle.dumps(value)
self.save()

View file

@ -26,9 +26,6 @@ if os.name == 'nt':
# For Windows we need to handle pid files manually.
PORTAL_PIDFILE = os.path.join(settings.GAME_DIR, 'portal.pid')
# i18n
from django.utils.translation import ugettext as _
#------------------------------------------------------------
# Evennia Portal settings
#------------------------------------------------------------
@ -103,7 +100,7 @@ class Portal(object):
"""
Outputs server startup info to the terminal.
"""
print _(' %(servername)s Portal (%(version)s) started.') % {'servername': SERVERNAME, 'version': VERSION}
print ' %(servername)s Portal (%(version)s) started.' % {'servername': SERVERNAME, 'version': VERSION}
if AMP_ENABLED:
print " amp (Server): %s" % AMP_PORT
if TELNET_ENABLED:
@ -135,7 +132,7 @@ class Portal(object):
if mode == None:
return
f = open(PORTAL_RESTART, 'w')
print _("writing mode=%(mode)s to %(portal_restart)s") % {'mode': mode, 'portal_restart': PORTAL_RESTART}
print "writing mode=%(mode)s to %(portal_restart)s" % {'mode': mode, 'portal_restart': PORTAL_RESTART}
f.write(str(mode))
f.close()

View file

@ -172,7 +172,7 @@ class Evennia(object):
if not last_initial_setup_step:
# None is only returned if the config does not exist,
# i.e. this is an empty DB that needs populating.
print _(' Server started for the first time. Setting defaults.')
print ' Server started for the first time. Setting defaults.'
initial_setup.handle_setup(0)
print '-'*50
elif int(last_initial_setup_step) >= 0:
@ -180,8 +180,8 @@ class Evennia(object):
# modules and setup will resume from this step, retrying
# the last failed module. When all are finished, the step
# is set to -1 to show it does not need to be run again.
print _(' Resuming initial setup from step %(last)s.' % \
{'last': last_initial_setup_step})
print ' Resuming initial setup from step %(last)s.' % \
{'last': last_initial_setup_step}
initial_setup.handle_setup(int(last_initial_setup_step))
print '-'*50
@ -207,7 +207,7 @@ class Evennia(object):
"""
Outputs server startup info to the terminal.
"""
print _(' %(servername)s Server (%(version)s) started.') % {'servername': SERVERNAME, 'version': VERSION}
print ' %(servername)s Server (%(version)s) started.' % {'servername': SERVERNAME, 'version': VERSION}
print ' amp (Portal): %s' % AMP_PORT
def set_restart_mode(self, mode=None):

View file

@ -28,9 +28,6 @@ from src.server import session
from src.players.models import PlayerDB
from src.utils import ansi, utils, logger
# i18n
from django.utils.translation import ugettext as _
ENCODINGS = settings.ENCODINGS
CTRL_C = '\x03'
@ -295,7 +292,7 @@ def getKeyPair(pubkeyfile, privkeyfile):
if not (os.path.exists(pubkeyfile) and os.path.exists(privkeyfile)):
# No keypair exists. Generate a new RSA keypair
print _(" Generating SSH RSA keypair ..."),
print " Generating SSH RSA keypair ...",
from Crypto.PublicKey import RSA
KEY_LENGTH = 1024
@ -340,8 +337,8 @@ def makeFactory(configdict):
factory.publicKeys = {'ssh-rsa': publicKey}
factory.privateKeys = {'ssh-rsa': privateKey}
except Exception, e:
print _(" getKeyPair error: %(e)s\n WARNING: Evennia could not auto-generate SSH keypair. Using conch default keys instead.") % {'e': e}
print _(" If this error persists, create game/%(pub)s and game/%(priv)s yourself using third-party tools.") % {'pub': pubkeyfile, 'priv': privkeyfile}
print " getKeyPair error: %(e)s\n WARNING: Evennia could not auto-generate SSH keypair. Using conch default keys instead." % {'e': e}
print " If this error persists, create game/%(pub)s and game/%(priv)s yourself using third-party tools." % {'pub': pubkeyfile, 'priv': privkeyfile}
factory.services = factory.services.copy()
factory.services['ssh-userauth'] = ExtraInfoAuthServer

View file

@ -8,7 +8,7 @@ from twisted.internet import ssl as twisted_ssl
try:
import OpenSSL
except ImportError:
print _(" SSL_ENABLED requires PyOpenSSL.")
print " SSL_ENABLED requires PyOpenSSL."
sys.exit(5)
from src.server.telnet import TelnetProtocol
@ -33,7 +33,7 @@ def verify_SSL_key_and_cert(keyfile, certfile):
from Crypto.PublicKey import RSA
from twisted.conch.ssh.keys import Key
print _(" Creating SSL key and certificate ... "),
print " Creating SSL key and certificate ... ",
try:
# create the RSA key and store it.
@ -42,8 +42,8 @@ def verify_SSL_key_and_cert(keyfile, certfile):
keyString = rsaKey.toString(type="OPENSSH")
file(keyfile, 'w+b').write(keyString)
except Exception,e:
print _("rsaKey error: %(e)s\n WARNING: Evennia could not auto-generate SSL private key.") % {'e': e}
print _("If this error persists, create game/%(keyfile)s yourself using third-party tools.") % {'keyfile': keyfile}
print "rsaKey error: %(e)s\n WARNING: Evennia could not auto-generate SSL private key." % {'e': e}
print "If this error persists, create game/%(keyfile)s yourself using third-party tools." % {'keyfile': keyfile}
sys.exit(5)
# try to create the certificate
@ -55,12 +55,14 @@ def verify_SSL_key_and_cert(keyfile, certfile):
try:
err = subprocess.call(exestring)#, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
except OSError, e:
print " %s\n" % e
print _(" Evennia's SSL context factory could not automatically create an SSL certificate game/%(cert)s.") % {'cert': certfile}
print _(" A private key 'ssl.key' was already created. Please create %(cert)s manually using the commands valid") % {'cert': certfile}
print _(" for your operating system.")
print _(" Example (linux, using the openssl program): ")
print " %s" % exestring
string = "\n".join([
" %s\n" % e,
" Evennia's SSL context factory could not automatically create an SSL certificate game/%(cert)s." % {'cert': certfile},
" A private key 'ssl.key' was already created. Please create %(cert)s manually using the commands valid" % {'cert': certfile},
" for your operating system.",
" Example (linux, using the openssl program): ",
" %s" % exestring])
print string
sys.exit(5)
print "done."

View file

@ -42,11 +42,11 @@ simple format. The engine runs each command in sequence, as if they
had been run at the game prompt.
Each evennia command must be delimited by a line comment to mark its
end.
end.
#INSERT path.batchcmdfile - this as the first entry on a line will
import and run a batch.ev file in this position, as if it was
written in this file.
import and run a batch.ev file in this position, as if it was
written in this file.
This way entire game worlds can be created and planned offline; it is
especially useful in order to create long room descriptions where a
@ -54,11 +54,11 @@ real offline text editor is often much better than any online text
editor or prompt.
Example of batch.ev file:
----------------------------
----------------------------
# batch file
# all lines starting with # are comments; they also indicate
# that a command definition is over.
# that a command definition is over.
@create box
@ -66,10 +66,10 @@ Example of batch.ev file:
@set box/desc = A large box.
Inside are some scattered piles of clothing.
Inside are some scattered piles of clothing.
It seems the bottom of the box is a bit loose.
It seems the bottom of the box is a bit loose.
# Again, this comment indicates the @set command is over. Note how
# the description could be freely added. Excess whitespace on a line
@ -81,7 +81,7 @@ It seems the bottom of the box is a bit loose.
# (Assuming #221 is a warehouse or something.)
# (remember, this comment ends the @teleport command! Don'f forget it)
# Example of importing another file at this point.
# Example of importing another file at this point.
#IMPORT examples.batch
@drop box
@ -90,7 +90,7 @@ It seems the bottom of the box is a bit loose.
# close the @drop command since it's the end of the file)
-------------------------
An example batch file is game/gamesrc/commands/examples/batch_example.ev.
An example batch file is game/gamesrc/commands/examples/batch_example.ev.
==========================================================================
@ -114,9 +114,9 @@ code words.
automatically be pasted at the top of all code
blocks. Observe that changes to these variables made in one
block is not preserved between blocks!
#CODE
#CODE
#CODE (info)
#CODE (info) objname1, objname1, ... -
#CODE (info) objname1, objname1, ... -
This designates a code block that will be executed like a
stand-alone piece of code together with any #HEADER
defined. (info) text is used by the interactive mode to
@ -140,12 +140,12 @@ caller - the object executing the script
Example batch.py file
-----------------------------------
#HEADER
#HEADER
import traceback
import traceback
from django.config import settings
from src.utils import create
from game.gamesrc.typeclasses import basetypes
from game.gamesrc.typeclasses import basetypes
GOLD = 10
@ -188,12 +188,12 @@ def read_batchfile(pythonpath, file_ending='.py'):
Filename is considered to be the name of the batch file
relative the directory specified in settings.py.
file_ending specify which batchfile ending should be
file_ending specify which batchfile ending should be
assumed (.ev or .py).
"""
"""
# open the file
if pythonpath and not (pythonpath.startswith('src.') or pythonpath.startswith('game.')
if pythonpath and not (pythonpath.startswith('src.') or pythonpath.startswith('game.')
or pythonpath.startswith('contrib.')):
abspaths = []
for basepath in settings.BASE_BATCHPROCESS_PATHS:
@ -202,24 +202,24 @@ def read_batchfile(pythonpath, file_ending='.py'):
abspaths = [utils.pypath_to_realpath(pythonpath, file_ending)]
fobj, lines, err = None, [], None
for file_encoding in ENCODINGS:
# try different encodings, in order
# try different encodings, in order
load_errors = []
for abspath in abspaths:
# try different paths, until we get a match
try:
# try different paths, until we get a match
try:
# we read the file directly into unicode.
fobj = codecs.open(abspath, 'r', encoding=file_encoding)
except IOError:
load_errors.append("Could not open batchfile '%s'." % abspath)
continue
continue
break
if not fobj:
continue
continue
load_errors = []
err =None
err =None
# We have successfully found and opened the file. Now actually
# try to decode it using the given protocol.
# try to decode it using the given protocol.
try:
lines = fobj.readlines()
except UnicodeDecodeError:
@ -230,19 +230,19 @@ def read_batchfile(pythonpath, file_ending='.py'):
for lnum, line in enumerate(fobj):
pass
except UnicodeDecodeError, err:
# lnum starts from 0, so we add +1 line,
# lnum starts from 0, so we add +1 line,
# besides the faulty line is never read
# so we add another 1 (thus +2) to get
# the actual line number seen in an editor.
err.linenum = lnum + 2
# the actual line number seen in an editor.
err.linenum = lnum + 2
fobj.close()
# possibly try another encoding
continue
continue
# if we get here, the encoding worked. Stop iteration.
break
if load_errors:
break
if load_errors:
logger.log_errmsg("\n".join(load_errors))
if err:
if err:
return err
else:
return lines
@ -257,7 +257,7 @@ class BatchCommandProcessor(object):
"""
This class implements a batch-command processor.
"""
"""
def parse_file(self, pythonpath):
"""
This parses the lines of a batchfile according to the following
@ -266,15 +266,15 @@ class BatchCommandProcessor(object):
It is also a comment and any number of # can exist on subsequent
lines (but not inside comments).
2) #INSERT at the beginning of a line imports another
batch-cmd file file and pastes it into the batch file as if
it was written there.
batch-cmd file file and pastes it into the batch file as if
it was written there.
3) Commands are placed alone at the beginning of a line and their
arguments are considered to be everything following (on any
number of lines) until the next comment line beginning with #.
4) Newlines are ignored in command definitions
5) A completely empty line in a command line definition is condered
a newline (so two empty lines is a paragraph).
6) Excess spaces and indents inside arguments are stripped.
6) Excess spaces and indents inside arguments are stripped.
"""
@ -287,7 +287,7 @@ class BatchCommandProcessor(object):
if line.strip().startswith("#INSERT"):
return "insert"
elif line.strip()[0] == '#':
return "comment"
return "comment"
else:
return "commanddef"
except IndexError:
@ -295,10 +295,10 @@ class BatchCommandProcessor(object):
#read the indata, if possible.
lines = read_batchfile(pythonpath, file_ending='.ev')
#line = utils.to_unicode(line)
if not lines:
return None
return None
commands = []
curr_cmd = ""
@ -309,34 +309,34 @@ class BatchCommandProcessor(object):
#parse all command definitions into a list.
for line in lines:
typ = identify_line(line)
if typ == "commanddef":
curr_cmd += line
elif typ == "empty" and curr_cmd:
curr_cmd += "\r\n"
elif typ == "insert":
# note that we are not safeguarding for
# note that we are not safeguarding for
# cyclic imports here!
if curr_cmd:
commands.append(curr_cmd.strip())
curr_cmd = ""
filename = line.lstrip("#INSERT").strip()
filename = line.lstrip("#INSERT").strip()
insert_commands = self.parse_file(filename)
if insert_commands == None:
insert_commands = ["{rINSERT ERROR: %s{n" % filename]
commands.extend(insert_commands)
commands.extend(insert_commands)
else: #comment
if curr_cmd:
commands.append(curr_cmd.strip())
curr_cmd = ""
if curr_cmd:
commands.append(curr_cmd.strip())
curr_cmd = ""
if curr_cmd:
commands.append(curr_cmd.strip())
#second round to clean up now merged line edges etc.
reg2 = re.compile(r"[ \t\f\v]+")
commands = [reg2.sub(" ", c) for c in commands]
commands = [reg2.sub(" ", c) for c in commands]
#remove eventual newline at the end of commands
commands = [c.strip('\r\n') for c in commands]
@ -359,7 +359,7 @@ def tb_iter(tb):
class BatchCodeProcessor(object):
"""
This implements a batch-code processor
"""
def parse_file(self, pythonpath):
@ -369,8 +369,8 @@ class BatchCodeProcessor(object):
1) Lines starting with #HEADER starts a header block (ends other blocks)
2) Lines starting with #CODE begins a code block (ends other blocks)
3) #CODE headers may be of the following form: #CODE (info) objname, objname2, ...
4) Lines starting with #INSERT are on form #INSERT filename.
3) #CODE headers may be of the following form: #CODE (info) objname, objname2, ...
4) Lines starting with #INSERT are on form #INSERT filename.
3) All lines outside blocks are stripped.
4) All excess whitespace beginning/ending a block is stripped.
@ -379,9 +379,9 @@ class BatchCodeProcessor(object):
# helper function
def parse_line(line):
"""
Identifies the line type: block command, comment, empty or normal code.
Identifies the line type: block command, comment, empty or normal code.
"""
"""
parseline = line.strip()
if parseline.startswith("#HEADER"):
@ -399,7 +399,7 @@ class BatchCodeProcessor(object):
if info:
info = info[0]
line = line.replace(info, "")
objs = [o.strip() for o in line.split(",") if o.strip()]
objs = [o.strip() for o in line.split(",") if o.strip()]
return ("codeheader", info, objs)
elif parseline.startswith('#'):
return ('comment', "", "%s" % line)
@ -422,25 +422,25 @@ class BatchCodeProcessor(object):
in_code = False
for line in lines:
# parse line
# parse line
mode, info, line = parse_line(line)
# try:
# print "::", in_header, in_code, mode, line.strip()
# print "::", in_header, in_code, mode, line.strip()
# except:
# print "::", in_header, in_code, mode, line
if mode == 'insert':
# print "::", in_header, in_code, mode, line
if mode == 'insert':
# recursive load of inserted code files - note that we
# are not checking for cyclic imports!
in_header = False
in_code = False
inserted_codes = self.parse_file(line) or [{'objs':"", 'info':line, 'code':""}]
for codedict in inserted_codes:
codedict["inserted"] = True
for codedict in inserted_codes:
codedict["inserted"] = True
codes.extend(inserted_codes)
elif mode == 'header':
in_header = True
in_code = False
elif mode == 'codeheader':
elif mode == 'codeheader':
in_header = False
in_code = True
# the line is a list of object variable names
@ -468,9 +468,9 @@ class BatchCodeProcessor(object):
# just check for errors.
if not codedict['code']:
codedict['code'] = "{r#INSERT ERROR: %s{n" % codedict['info']
else:
else:
objs = ", ".join(codedict["objs"])
if objs:
if objs:
objs = "[%s]" % objs
codedict["code"] = "#CODE %s %s \n%s\n\n%s" % (codedict['info'],
objs,
@ -486,7 +486,7 @@ class BatchCodeProcessor(object):
"""
# define the execution environment
environ = "setup_environ(settings_module)"
environdict = {"setup_environ":setup_environ,
environdict = {"setup_environ":setup_environ,
"settings_module":settings_module}
if extra_environ:
for key, value in extra_environ.items():
@ -499,11 +499,11 @@ class BatchCodeProcessor(object):
for obj in codedict['objs']:
code += "\ntry: %s.delete()\nexcept: pass" % obj
# execute the block
# execute the block
try:
exec(code, environdict)
except Exception:
etype, value, tb = sys.exc_info()
etype, value, tb = sys.exc_info()
fname = tb_filename(tb)
for tb in tb_iter(tb):
@ -512,15 +512,15 @@ class BatchCodeProcessor(object):
lineno = tb.tb_lineno - 1
err = ""
for iline, line in enumerate(code.split("\n")):
if iline == lineno:
err += "\n{w%02i{n: %s" % (iline + 1, line)
elif lineno - 5 < iline < lineno + 5:
err += "\n%02i: %s" % (iline + 1, line)
if iline == lineno:
err += "\n{w%02i{n: %s" % (iline + 1, line)
elif lineno - 5 < iline < lineno + 5:
err += "\n%02i: %s" % (iline + 1, line)
err += "\n".join(traceback.format_exception(etype, value, tb))
err += "\n".join(traceback.format_exception(etype, value, tb))
#errlist = format_exc().split('\n')
#if len(errlist) > 4:
# errlist = errlist[4:]
# errlist = errlist[4:]
#err = "\n".join(" %s" % line for line in errlist if line)
if debug: