PEP8 cleanup of the entire codebase. Unchanged are many cases of too-long lines, partly because of the rewrite they would require but also because splitting many lines up would make the code harder to read. Also the third-party libraries (idmapper, prettytable etc) were not cleaned.

This commit is contained in:
Griatch 2013-11-14 19:31:17 +01:00
parent 30b7d2a405
commit 1ae17bcbe4
154 changed files with 5613 additions and 4054 deletions

View file

@ -1,12 +1,14 @@
"""
Makes it easier to import by grouping all relevant things already at this level.
Makes it easier to import by grouping all relevant things already at this
level.
You can henceforth import most things directly from src.comms
Also, the initiated object manager is available as src.comms.msgmanager and src.comms.channelmanager.
Also, the initiated object manager is available as src.comms.msgmanager and
src.comms.channelmanager.
"""
from src.comms.models import *
from src.comms.models import *
msgmanager = Msg.objects
channelmanager = ChannelDB.objects

View file

@ -1,51 +1,57 @@
#
# This sets up how models are displayed
# in the web admin interface.
#
from django.contrib import admin
from src.comms.models import ChannelDB, Msg, PlayerChannelConnection, ExternalChannelConnection
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
#admin.site.register(Msg, MsgAdmin)
class PlayerChannelConnectionInline(admin.TabularInline):
model = PlayerChannelConnection
fieldsets = (
(None, {
'fields':(('db_player', 'db_channel')),
'classes':('collapse',)}),)
extra = 1
class ExternalChannelConnectionInline(admin.StackedInline):
model = ExternalChannelConnection
fieldsets = (
(None, {
'fields':(('db_is_enabled','db_external_key', 'db_channel'), 'db_external_send_code', 'db_external_config'),
'classes':('collapse',)
}),)
extra = 1
class ChannelAdmin(admin.ModelAdmin):
inlines = (PlayerChannelConnectionInline, ExternalChannelConnectionInline)
list_display = ('id', 'db_key', 'db_lock_storage')
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
fieldsets = (
(None, {'fields':(('db_key',),'db_lock_storage',)}),
)
#
# This sets up how models are displayed
# in the web admin interface.
#
from django.contrib import admin
from src.comms.models import ChannelDB, Msg, PlayerChannelConnection, ExternalChannelConnection
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
#admin.site.register(Msg, MsgAdmin)
class PlayerChannelConnectionInline(admin.TabularInline):
model = PlayerChannelConnection
fieldsets = (
(None, {
'fields':(('db_player', 'db_channel')),
'classes':('collapse',)}),)
extra = 1
class ExternalChannelConnectionInline(admin.StackedInline):
model = ExternalChannelConnection
fieldsets = (
(None, {
'fields': (('db_is_enabled','db_external_key', 'db_channel'),
'db_external_send_code', 'db_external_config'),
'classes': ('collapse',)
}),)
extra = 1
class ChannelAdmin(admin.ModelAdmin):
inlines = (PlayerChannelConnectionInline, ExternalChannelConnectionInline)
list_display = ('id', 'db_key', 'db_lock_storage')
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
fieldsets = (
(None, {'fields': (('db_key',), 'db_lock_storage',)}),
)
admin.site.register(ChannelDB, ChannelAdmin)

View file

@ -23,9 +23,9 @@ update() on the channelhandler. Or use Channel.objects.delete() which
does this for you.
"""
from src.comms.models import ChannelDB, Msg
from src.comms.models import ChannelDB
from src.commands import cmdset, command
from src.utils import utils
class ChannelCommand(command.Command):
"""
@ -51,7 +51,8 @@ class ChannelCommand(command.Command):
"""
Simple parser
"""
channelname, msg = self.args.split(":", 1) # cmdhandler sends channame:msg here.
# cmdhandler sends channame:msg here.
channelname, msg = self.args.split(":", 1)
self.args = (channelname.strip(), msg.strip())
def func(self):
@ -128,7 +129,7 @@ class ChannelHandler(object):
help_category="Channel names",
obj=channel,
is_channel=True)
cmd.__doc__= self._format_help(channel)
cmd.__doc__ = self._format_help(channel)
self.cached_channel_cmds.append(cmd)
self.cached_cmdsets = {}

View file

@ -53,7 +53,6 @@ class Comm(TypeClass):
else:
return '%s: %s' % (sender_string, message)
def format_external(self, msg, senders, emit=False):
"""
Used for formatting external messages. This is needed as a separate
@ -71,7 +70,6 @@ class Comm(TypeClass):
senders = ', '.join(senders)
return self.pose_transform(msg, senders)
def format_message(self, msg, emit=False):
"""
Formats a message body for display.
@ -169,30 +167,39 @@ class Comm(TypeClass):
conn.player.msg(msg.message, from_obj=msg.senders)
except AttributeError:
try:
conn.to_external(msg.message, senders=msg.senders, from_channel=self)
conn.to_external(msg.message,
senders=msg.senders, from_channel=self)
except Exception:
logger.log_trace("Cannot send msg to connection '%s'" % conn)
def msg(self, msgobj, header=None, senders=None, sender_strings=None,
persistent=True, online=False, emit=False, external=False):
"""
Send the given message to all players connected to channel. Note that
no permission-checking is done here; it is assumed to have been
done before calling this method. The optional keywords are not used if persistent is False.
done before calling this method. The optional keywords are not used if
persistent is False.
msgobj - a Msg/TempMsg instance or a message string. If one of the former, the remaining
keywords will be ignored. If a string, this will either be sent as-is (if persistent=False) or
it will be used together with header and senders keywords to create a Msg instance on the fly.
senders - an object, player or a list of objects or players. Optional if persistent=False.
sender_strings - Name strings of senders. Used for external connections where the sender
is not a player or object. When this is defined, external will be assumed.
msgobj - a Msg/TempMsg instance or a message string. If one of the
former, the remaining keywords will be ignored. If a string,
this will either be sent as-is (if persistent=False) or it
will be used together with header and senders keywords to
create a Msg instance on the fly.
senders - an object, player or a list of objects or players.
Optional if persistent=False.
sender_strings - Name strings of senders. Used for external
connections where the sender is not a player or object. When
this is defined, external will be assumed.
external - Treat this message agnostic of its sender.
persistent (bool) - ignored if msgobj is a Msg or TempMsg. If True, a Msg will be created, using
header and senders keywords. If False, other keywords will be ignored.
online (bool) - If this is set true, only messages people who are online. Otherwise, messages all players
connected. This can make things faster, but may not trigger listeners on players that are offline.
emit (bool) - Signals to the message formatter that this message is not to be directly associated with a name.
persistent (bool) - ignored if msgobj is a Msg or TempMsg. If True,
a Msg will be created, using header and senders keywords. If
False, other keywords will be ignored.
online (bool) - If this is set true, only messages people who are
online. Otherwise, messages all players connected. This can
make things faster, but may not trigger listeners on players
that are offline.
emit (bool) - Signals to the message formatter that this message is
not to be directly associated with a name.
"""
if senders:
senders = make_iter(senders)
@ -209,7 +216,7 @@ class Comm(TypeClass):
msgobj = TempMsg()
msgobj.header = header
msgobj.message = msg
msgobj.channels = [self.dbobj] # add this channel
msgobj.channels = [self.dbobj] # add this channel
if not msgobj.senders:
msgobj.senders = senders

View file

@ -65,11 +65,14 @@ class Send_IsAlive(Script):
self.interval = 900
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):
"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_"))
return any(service for service in SESSIONS.server.services
if service.name.startswith("imc2_"))
class Send_Keepalive_Request(Script):
"""
@ -81,11 +84,14 @@ class Send_Keepalive_Request(Script):
self.interval = 3500
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"
return any(service for service in SESSIONS.server.services if service.name.startswith("imc2_"))
return any(service for service in SESSIONS.server.services
if service.name.startswith("imc2_"))
class Prune_Inactive_Muds(Script):
"""
@ -99,13 +105,17 @@ class Prune_Inactive_Muds(Script):
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():
if time() - mudinfo.last_updated > self.inactive_threshold:
del IMC2_MUDLIST.mud_list[name]
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_"))
return any(service for service in SESSIONS.server.services
if service.name.startswith("imc2_"))
class Sync_Server_Channel_List(Script):
"""
@ -116,21 +126,25 @@ 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.interval = 24 * 3600 # once every day
self.desc = _("Re-sync IMC2 network channel list")
self.persistent = True
def at_repeat(self):
checked_networks = []
network = IMC2_CLIENT.factory.network
if not network in checked_networks:
channel.send_packet(pkg.IMC2PacketIceRefresh())
checked_networks.append(network)
def is_valid(self):
return any(service for service in SESSIONS.server.services if service.name.startswith("imc2_"))
return any(service for service in SESSIONS.server.services
if service.name.startswith("imc2_"))
#
# IMC2 protocol
#
class IMC2Protocol(telnet.StatefulTelnetProtocol):
"""
Provides the abstraction for the IMC2 protocol. Handles connection,
@ -145,7 +159,6 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
self.network_name = None
self.sequence = None
def connectionMade(self):
"""
Triggered after connecting to the IMC2 network.
@ -179,8 +192,10 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
# This gets incremented with every command.
self.sequence += 1
packet.imc2_protocol = self
packet_str = utils.to_str(packet.assemble(self.factory.mudname, self.factory.client_pwd, self.factory.server_pwd))
if IMC2_DEBUG and not (hasattr(packet, 'packet_type') and packet.packet_type == "is-alive"):
packet_str = utils.to_str(packet.assemble(self.factory.mudname,
self.factory.client_pwd, self.factory.server_pwd))
if IMC2_DEBUG and not (hasattr(packet, 'packet_type') and
packet.packet_type == "is-alive"):
logger.log_infomsg("IMC2: SENT> %s" % packet_str)
logger.log_infomsg(str(packet))
self.sendLine(packet_str)
@ -257,9 +272,9 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
"""
Handle tells over IMC2 by formatting the text properly
"""
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.')}
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):
"""
@ -349,6 +364,7 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
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
@ -382,7 +398,9 @@ def build_connection_key(channel, imc2_channel):
"Build an id hash for the connection"
if hasattr(channel, "key"):
channel = channel.key
return "imc2_%s:%s(%s)%s<>%s" % (IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME, imc2_channel, channel)
return "imc2_%s:%s(%s)%s<>%s" % (IMC2_NETWORK, IMC2_PORT,
IMC2_MUDNAME, imc2_channel, channel)
def start_scripts(validate=False):
"""
@ -402,6 +420,7 @@ def start_scripts(validate=False):
if not search.scripts("IMC2_Sync_Server_Channel_List"):
create.create_script(Sync_Server_Channel_List)
def create_connection(channel, imc2_channel):
"""
This will create a new IMC2<->channel connection.
@ -417,7 +436,8 @@ def create_connection(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(",")
if imc2_channel in old_config:
@ -432,9 +452,9 @@ def create_connection(channel, imc2_channel):
# no old connection found; create a new one.
config = imc2_channel
# 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, senders=[self])\n"
send_code = "from src.comms.imc2 import IMC2_CLIENT\n"
send_code += "data={'channel':from_channel}\n"
send_code += "IMC2_CLIENT.msg_imc2(message, senders=[self])\n"
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
db_external_config=config)
conn.save()
@ -453,15 +473,22 @@ def delete_connection(channel, imc2_channel):
conn.delete()
return True
def connect_to_imc2():
"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,
IMC2_CLIENT_PWD, IMC2_SERVER_PWD))
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))
SESSIONS.server.services.addService(imc)
def connect_all():
"""
Activates the imc2 system. Called by the server if IMC2_ENABLED=True.

View file

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View file

@ -1,59 +1,60 @@
"""
ANSI parser - this adds colour to text according to
special markup strings.
This is a IMC2 complacent version.
"""
import re
from src.utils import ansi
class IMCANSIParser(ansi.ANSIParser):
"""
This parser is per the IMC2 specification.
"""
def __init__(self):
normal = ansi.ANSI_NORMAL
hilite = ansi.ANSI_HILITE
self.ansi_map = [
(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),
(r'~w', normal + ansi.ANSI_WHITE), # Grey
(r'~W', hilite + ansi.ANSI_WHITE), # White
(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', 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'~b', normal + ansi.ANSI_BLUE), # Dark Blue
(r'~B', hilite + ansi.ANSI_BLUE), # Blue
(r'~C', hilite + ansi.ANSI_BLUE),
(r'~r', normal + ansi.ANSI_RED), # Dark Red
(r'~R', hilite + ansi.ANSI_RED), # Red
## Formatting
(r'~L', hilite), # Bold/hilite
(r'~!', normal), # reset
(r'\\r', normal),
(r'\\n', ansi.ANSI_RETURN),
]
# prepare regex matching
self.ansi_sub = [(re.compile(sub[0], re.DOTALL), sub[1])
for sub in self.ansi_map]
# prepare matching ansi codes overall
self.ansi_regex = re.compile("\033\[[0-9;]+m")
ANSI_PARSER = IMCANSIParser()
def parse_ansi(string, strip_ansi=False, parser=ANSI_PARSER):
"""
Shortcut to use the IMC2 ANSI parser.
"""
return parser.parse_ansi(string, strip_ansi=strip_ansi)
"""
ANSI parser - this adds colour to text according to
special markup strings.
This is a IMC2 complacent version.
"""
import re
from src.utils import ansi
class IMCANSIParser(ansi.ANSIParser):
"""
This parser is per the IMC2 specification.
"""
def __init__(self):
normal = ansi.ANSI_NORMAL
hilite = ansi.ANSI_HILITE
self.ansi_map = [
(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),
(r'~w', normal + ansi.ANSI_WHITE), # Grey
(r'~W', hilite + ansi.ANSI_WHITE), # White
(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', 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'~b', normal + ansi.ANSI_BLUE), # Dark Blue
(r'~B', hilite + ansi.ANSI_BLUE), # Blue
(r'~C', hilite + ansi.ANSI_BLUE),
(r'~r', normal + ansi.ANSI_RED), # Dark Red
(r'~R', hilite + ansi.ANSI_RED), # Red
## Formatting
(r'~L', hilite), # Bold/hilite
(r'~!', normal), # reset
(r'\\r', normal),
(r'\\n', ansi.ANSI_RETURN),
]
# prepare regex matching
self.ansi_sub = [(re.compile(sub[0], re.DOTALL), sub[1])
for sub in self.ansi_map]
# prepare matching ansi codes overall
self.ansi_regex = re.compile("\033\[[0-9;]+m")
ANSI_PARSER = IMCANSIParser()
def parse_ansi(string, strip_ansi=False, parser=ANSI_PARSER):
"""
Shortcut to use the IMC2 ANSI parser.
"""
return parser.parse_ansi(string, strip_ansi=strip_ansi)

View file

@ -1,24 +1,24 @@
"""
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
packet contains the id of the one asking. This handler catches the
(possible) reply from the server, parses the id back to the
original asker and tells them the result.
"""
try:
pobject = ObjectDB.objects.get(id=packet.target)
response_text = imc2_ansi.parse_ansi(packet.optional_data.get('text', 'Unknown'))
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.
pass
"""
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
packet contains the id of the one asking. This handler catches the
(possible) reply from the server, parses the id back to the
original asker and tells them the result.
"""
try:
pobject = ObjectDB.objects.get(id=packet.target)
response_text = imc2_ansi.parse_ansi(packet.optional_data.get('text', 'Unknown'))
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.
pass

File diff suppressed because it is too large Load diff

View file

@ -1,104 +1,108 @@
"""
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.
"""
from time import time
class IMC2Mud(object):
"""
Stores information about other games connected to our current IMC2 network.
"""
def __init__(self, packet):
self.name = packet.origin
self.versionid = packet.optional_data.get('versionid', None)
self.networkname = packet.optional_data.get('networkname', None)
self.url = packet.optional_data.get('url', None)
self.host = packet.optional_data.get('host', None)
self.port = packet.optional_data.get('port', None)
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.
"""
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.
"""
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
Mud list for later retrieval.
"""
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.
"""
mud = IMC2Mud(packet)
try:
del self.mud_list[mud.name]
except KeyError:
# No matching entry, no big deal.
pass
class IMC2Channel(object):
"""
Stores information about channels available on the network.
"""
def __init__(self, packet):
self.localname = packet.optional_data.get('localname', None)
self.name = packet.optional_data.get('channel', None)
self.level = packet.optional_data.get('level', None)
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.
"""
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.
"""
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
channel list for later retrieval.
"""
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.
"""
channel = IMC2Channel(packet)
try:
del self.chan_list[channel.name]
except KeyError:
# No matching entry, no big deal.
pass
"""
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.
"""
from time import time
class IMC2Mud(object):
"""
Stores information about other games connected to our current IMC2 network.
"""
def __init__(self, packet):
self.name = packet.origin
self.versionid = packet.optional_data.get('versionid', None)
self.networkname = packet.optional_data.get('networkname', None)
self.url = packet.optional_data.get('url', None)
self.host = packet.optional_data.get('host', None)
self.port = packet.optional_data.get('port', None)
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.
"""
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.
"""
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
Mud list for later retrieval.
"""
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.
"""
mud = IMC2Mud(packet)
try:
del self.mud_list[mud.name]
except KeyError:
# No matching entry, no big deal.
pass
class IMC2Channel(object):
"""
Stores information about channels available on the network.
"""
def __init__(self, packet):
self.localname = packet.optional_data.get('localname', None)
self.name = packet.optional_data.get('channel', None)
self.level = packet.optional_data.get('level', None)
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.
"""
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.
"""
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
channel list for later retrieval.
"""
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.
"""
channel = IMC2Channel(packet)
try:
del self.chan_list[channel.name]
except KeyError:
# No matching entry, no big deal.
pass

View file

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

View file

@ -18,10 +18,12 @@ _User = None
# error class
class CommError(Exception):
"Raise by comm system, to allow feedback to player when caught."
pass
#
# helper functions
#
@ -43,6 +45,7 @@ def dbref(dbref, reqhash=True):
return None
return dbref
def identify_object(inp):
"identify if an object is a player or an object; return its database model"
# load global stores
@ -61,18 +64,25 @@ def identify_object(inp):
return inp, None
# try to identify the type
try:
obj = _GA(inp, "dbobj") # this works for all typeclassed entities
obj = _GA(inp, "dbobj") # this works for all typeclassed entities
except AttributeError:
obj = inp
typ = type(obj)
if typ == _PlayerDB: return obj, "player"
elif typ == _ObjectDB: return obj, "object"
elif typ == _ChannelDB: return obj, "channel"
elif dbref(obj): return dbref(obj), "dbref"
elif typ == basestring: return obj, "string"
elif typ == _ExternalConnection: return obj, "external"
if typ == _PlayerDB:
return obj, "player"
elif typ == _ObjectDB:
return obj, "object"
elif typ == _ChannelDB:
return obj, "channel"
elif dbref(obj):
return dbref(obj), "dbref"
elif typ == basestring:
return obj, "string"
elif typ == _ExternalConnection:
return obj, "external"
return obj, None # Something else
def to_object(inp, objtype='player'):
"""
Locates the object related to the given
@ -85,28 +95,39 @@ def to_object(inp, objtype='player'):
if typ == objtype:
return obj
if objtype == 'player':
if typ == 'object': return obj.player
if typ == 'string': return _PlayerDB.objects.get(user_username__iexact=obj)
if typ == 'dbref': return _PlayerDB.objects.get(id=obj)
if typ == 'object':
return obj.player
if typ == 'string':
return _PlayerDB.objects.get(user_username__iexact=obj)
if typ == 'dbref':
return _PlayerDB.objects.get(id=obj)
print objtype, inp, obj, typ, type(inp)
raise CommError()
elif objtype == 'object':
if typ == 'player': return obj.obj
if typ == 'string': return _ObjectDB.objects.get(db_key__iexact=obj)
if typ == 'dbref': return _ObjectDB.objects.get(id=obj)
if typ == 'player':
return obj.obj
if typ == 'string':
return _ObjectDB.objects.get(db_key__iexact=obj)
if typ == 'dbref':
return _ObjectDB.objects.get(id=obj)
print objtype, inp, obj, typ, type(inp)
raise CommError()
elif objtype == 'channel':
if typ == 'string': return _ChannelDB.objects.get(db_key__iexact=obj)
if typ == 'dbref': return _ChannelDB.objects.get(id=obj)
if typ == 'string':
return _ChannelDB.objects.get(db_key__iexact=obj)
if typ == 'dbref':
return _ChannelDB.objects.get(id=obj)
print objtype, inp, obj, typ, type(inp)
raise CommError()
elif objtype == 'external':
if typ == 'string': return _ExternalConnection.objects.get(db_key=inp)
if typ == 'dbref': return _ExternalConnection.objects.get(id=obj)
if typ == 'string':
return _ExternalConnection.objects.get(db_key=inp)
if typ == 'dbref':
return _ExternalConnection.objects.get(id=obj)
print objtype, inp, obj, typ, type(inp)
raise CommError()
#
# Msg manager
#
@ -146,17 +167,21 @@ class MsgManager(models.Manager):
def get_messages_by_sender(self, obj, exclude_channel_messages=False):
"""
Get all messages sent by one entity - this could be either a player or an object
Get all messages sent by one entity - this could be either a
player or an object
only_non_channel: only return messages -not- aimed at a channel (e.g. private tells)
only_non_channel: only return messages -not- aimed at a channel
(e.g. private tells)
"""
obj, typ = identify_object(obj)
if exclude_channel_messages:
# explicitly exclude channel recipients
if typ == 'player':
return list(self.filter(db_sender_players=obj, db_receivers_channels__isnull=True).exclude(db_hide_from_players=obj))
return list(self.filter(db_sender_players=obj,
db_receivers_channels__isnull=True).exclude(db_hide_from_players=obj))
elif typ == 'object':
return list(self.filter(db_sender_objects=obj, db_receivers_channels__isnull=True).exclude(db_hide_from_objects=obj))
return list(self.filter(db_sender_objects=obj,
db_receivers_channels__isnull=True).exclude(db_hide_from_objects=obj))
else:
raise CommError
else:
@ -208,9 +233,10 @@ class MsgManager(models.Manager):
if msg:
return msg[0]
# We use Q objects to gradually build up the query - this way we only need to do one
# database lookup at the end rather than gradually refining with multiple filter:s.
# Django Note: Q objects can be combined with & and | (=AND,OR). ~ negates the queryset
# We use Q objects to gradually build up the query - this way we only
# need to do one database lookup at the end rather than gradually
# refining with multiple filter:s. Django Note: Q objects can be
# combined with & and | (=AND,OR). ~ negates the queryset
# filter by sender
sender, styp = identify_object(sender)
@ -238,6 +264,7 @@ class MsgManager(models.Manager):
# execute the query
return list(self.filter(sender_restrict & receiver_restrict & fulltext_restrict))
#
# Channel manager
#
@ -350,9 +377,12 @@ class ChannelManager(models.Manager):
channels = self.filter(db_key__iexact=ostring)
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.all()]]
channels = [channel for channel in self.all()
if ostring.lower() in [a.lower
for a in channel.aliases.all()]]
return channels
#
# PlayerChannelConnection manager
#
@ -419,6 +449,7 @@ class PlayerChannelConnectionManager(models.Manager):
for conn in conns:
conn.delete()
class ExternalChannelConnectionManager(models.Manager):
"""
This ExternalChannelConnectionManager implements methods for searching

View file

@ -30,12 +30,14 @@ from src.locks.lockhandler import LockHandler
from src.utils import logger
from src.utils.utils import is_iter, to_str, crop, make_iter
__all__ = ("Msg", "TempMsg", "ChannelDB", "PlayerChannelConnection", "ExternalChannelConnection")
__all__ = ("Msg", "TempMsg", "ChannelDB",
"PlayerChannelConnection", "ExternalChannelConnection")
_GA = object.__getattribute__
_SA = object.__setattr__
_DA = object.__delattr__
#------------------------------------------------------------
#
# Msg
@ -66,9 +68,9 @@ class Msg(SharedMemoryModel):
# These databse fields are all set using their corresponding properties,
# named same as the field, but withtout the db_* prefix.
# Sender is either a player, an object or an external sender, like an IRC channel
# normally there is only one, but if co-modification of a message is allowed, there
# may be more than one "author"
# Sender is either a player, an object or an external sender, like
# an IRC channel; normally there is only one, but if co-modification of
# a message is allowed, there may be more than one "author"
db_sender_players = models.ManyToManyField("players.PlayerDB", related_name='sender_player_set', null=True, verbose_name='sender(player)', db_index=True)
db_sender_objects = models.ManyToManyField("objects.ObjectDB", related_name='sender_object_set', null=True, verbose_name='sender(object)', db_index=True)
db_sender_external = models.CharField('external sender', max_length=255, null=True, db_index=True,
@ -80,8 +82,8 @@ class Msg(SharedMemoryModel):
db_receivers_objects = models.ManyToManyField('objects.ObjectDB', related_name='receiver_object_set', null=True, help_text="object receivers")
db_receivers_channels = models.ManyToManyField("ChannelDB", related_name='channel_set', null=True, help_text="channel recievers")
# header could be used for meta-info about the message if your system needs it, or as a separate
# store for the mail subject line maybe.
# header could be used for meta-info about the message if your system needs
# it, or as a separate store for the mail subject line maybe.
db_header = models.TextField('header', null=True, blank=True)
# the message body itself
db_message = models.TextField('messsage')
@ -124,6 +126,7 @@ class Msg(SharedMemoryModel):
list(self.db_sender_players.all()) +
list(self.db_sender_objects.all()) +
self.extra_senders]
#@sender.setter
def __senders_set(self, value):
"Setter. Allows for self.sender = value"
@ -143,6 +146,7 @@ class Msg(SharedMemoryModel):
else:
raise ValueError(obj)
self.save()
#@sender.deleter
def __senders_del(self):
"Deleter. Clears all senders"
@ -173,12 +177,19 @@ class Msg(SharedMemoryModel):
# receivers property
#@property
def __receivers_get(self):
"Getter. Allows for value = self.receivers. Returns three lists of receivers: players, objects and channels."
"""
Getter. Allows for value = self.receivers.
Returns three lists of receivers: players, objects and channels.
"""
return [hasattr(o, "typeclass") and o.typeclass or o for o in
list(self.db_receivers_players.all()) + list(self.db_receivers_objects.all())]
#@receivers.setter
def __receivers_set(self, value):
"Setter. Allows for self.receivers = value. This appends a new receiver to the message."
"""
Setter. Allows for self.receivers = value.
This appends a new receiver to the message.
"""
for val in (v for v in make_iter(value) if v):
obj, typ = identify_object(val)
if typ == 'player':
@ -190,6 +201,7 @@ class Msg(SharedMemoryModel):
else:
raise ValueError
self.save()
#@receivers.deleter
def __receivers_del(self):
"Deleter. Clears all receivers"
@ -215,11 +227,15 @@ class Msg(SharedMemoryModel):
def __channels_get(self):
"Getter. Allows for value = self.channels. Returns a list of channels."
return self.db_receivers_channels.all()
#@channels.setter
def __channels_set(self, value):
"Setter. Allows for self.channels = value. Requires a channel to be added."
"""
Setter. Allows for self.channels = value.
Requires a channel to be added."""
for val in (v.dbobj for v in make_iter(value) if v):
self.db_receivers_channels.add(val)
#@channels.deleter
def __channels_del(self):
"Deleter. Allows for del self.channels"
@ -228,8 +244,12 @@ class Msg(SharedMemoryModel):
channels = property(__channels_get, __channels_set, __channels_del)
def __hide_from_get(self):
"Getter. Allows for value = self.hide_from. Returns 3 lists of players, objects and channels"
"""
Getter. Allows for value = self.hide_from.
Returns 3 lists of players, objects and channels
"""
return self.db_hide_from_players.all(), self.db_hide_from_objects.all(), self.db_hide_from_channels.all()
#@hide_from_sender.setter
def __hide_from_set(self, value):
"Setter. Allows for self.hide_from = value. Will append to hiders"
@ -243,6 +263,7 @@ class Msg(SharedMemoryModel):
else:
raise ValueError
self.save()
#@hide_from_sender.deleter
def __hide_from_del(self):
"Deleter. Allows for del self.hide_from_senders"
@ -275,7 +296,6 @@ class TempMsg(object):
temporary messages that will not be stored.
It mimics the "real" Msg object, but don't require
sender to be given.
"""
def __init__(self, senders=None, receivers=None, channels=None, message="", header="", type="", lockstring="", hide_from=None):
self.senders = senders and make_iter(senders) or []
@ -301,7 +321,7 @@ class TempMsg(object):
try:
self.senders.remove(o)
except ValueError:
pass # nothing to remove
pass # nothing to remove
def remove_receiver(self, obj):
"Remove a sender or a list of senders"
@ -309,11 +329,13 @@ class TempMsg(object):
try:
self.senders.remove(o)
except ValueError:
pass # nothing to remove
pass # nothing to remove
def access(self, accessing_obj, access_type='read', default=False):
"checks lock access"
return self.locks.check(accessing_obj, access_type=access_type, default=default)
return self.locks.check(accessing_obj,
access_type=access_type, default=default)
#------------------------------------------------------------
#
@ -347,7 +369,6 @@ class ChannelDB(TypedObject):
_SA(self, "aliases", AliasHandler(self, category_prefix="comm_"))
_SA(self, "attributes", AttributeHandler(self))
class Meta:
"Define Django meta options"
verbose_name = "Channel"
@ -415,6 +436,7 @@ class ChannelDB(TypedObject):
"""
return self.locks.check(accessing_obj, access_type=access_type, default=default)
class PlayerChannelConnection(SharedMemoryModel):
"""
This connects a player object to a particular comm channel.
@ -435,11 +457,13 @@ class PlayerChannelConnection(SharedMemoryModel):
def player_get(self):
"Getter. Allows for value = self.player"
return self.db_player
#@player.setter
def player_set(self, value):
"Setter. Allows for self.player = value"
self.db_player = value
self.save()
#@player.deleter
def player_del(self):
"Deleter. Allows for del self.player. Deletes connection."
@ -451,11 +475,13 @@ class PlayerChannelConnection(SharedMemoryModel):
def channel_get(self):
"Getter. Allows for value = self.channel"
return self.db_channel.typeclass
#@channel.setter
def channel_set(self, value):
"Setter. Allows for self.channel = value"
self.db_channel = value.dbobj
self.save()
#@channel.deleter
def channel_del(self):
"Deleter. Allows for del self.channel. Deletes connection."
@ -507,10 +533,12 @@ class ExternalChannelConnection(SharedMemoryModel):
"Getter. Allows for value = self.channel"
return self.db_channel
#@channel.setter
def channel_set(self, value):
"Setter. Allows for self.channel = value"
self.db_channel = value
self.save()
#@channel.deleter
def channel_del(self):
"Deleter. Allows for del self.channel. Deletes connection."
@ -522,11 +550,13 @@ class ExternalChannelConnection(SharedMemoryModel):
def external_key_get(self):
"Getter. Allows for value = self.external_key"
return self.db_external_key
#@external_key.setter
def external_key_set(self, value):
"Setter. Allows for self.external_key = value"
self.db_external_key = value
self.save()
#@external_key.deleter
def external_key_del(self):
"Deleter. Allows for del self.external_key. Deletes connection."
@ -538,11 +568,13 @@ class ExternalChannelConnection(SharedMemoryModel):
def external_send_code_get(self):
"Getter. Allows for value = self.external_send_code"
return self.db_external_send_code
#@external_send_code.setter
def external_send_code_set(self, value):
"Setter. Allows for self.external_send_code = value"
self.db_external_send_code = value
self.save()
#@external_send_code.deleter
def external_send_code_del(self):
"Deleter. Allows for del self.external_send_code. Deletes connection."
@ -555,11 +587,13 @@ class ExternalChannelConnection(SharedMemoryModel):
def external_config_get(self):
"Getter. Allows for value = self.external_config"
return self.db_external_config
#@external_config.setter
def external_config_set(self, value):
"Setter. Allows for self.external_config = value"
self.db_external_config = value
self.save()
#@external_config.deleter
def external_config_del(self):
"Deleter. Allows for del self.external_config. Deletes connection."
@ -572,11 +606,13 @@ class ExternalChannelConnection(SharedMemoryModel):
def is_enabled_get(self):
"Getter. Allows for value = self.is_enabled"
return self.db_is_enabled
#@is_enabled.setter
def is_enabled_set(self, value):
"Setter. Allows for self.is_enabled = value"
self.db_is_enabled = value
self.save()
#@is_enabled.deleter
def is_enabled_del(self):
"Deleter. Allows for del self.is_enabled. Deletes connection."
@ -589,8 +625,8 @@ class ExternalChannelConnection(SharedMemoryModel):
def to_channel(self, message, *args, **kwargs):
"Send external -> channel"
if 'from_obj' in kwargs and kwargs.pop('from_obj'):
from_obj = self.external_key
#if 'from_obj' in kwargs and kwargs.pop('from_obj'):
# from_obj = self.external_key
self.channel.msg(message, senders=[self], *args, **kwargs)
def to_external(self, message, senders=None, from_channel=None):
@ -599,12 +635,13 @@ class ExternalChannelConnection(SharedMemoryModel):
# make sure we are not echoing back our own message to ourselves
# (this would result in a nasty infinite loop)
#print senders
if self in make_iter(senders):#.external_key:
if self in make_iter(senders): #.external_key:
return
try:
# we execute the code snippet that should make it possible for the
# connection to contact the protocol correctly (as set by the protocol).
# connection to contact the protocol correctly (as set by the
# protocol).
# Note that the code block has access to the variables here, such
# as message, from_obj and from_channel.
exec(to_str(self.external_send_code))

View file

@ -21,6 +21,7 @@ RETAG = re.compile(r'<[^>]*?>')
# holds rss readers they can be shut down at will.
RSS_READERS = {}
def msg_info(message):
"""
Send info to default info channel
@ -37,6 +38,7 @@ if RSS_ENABLED:
except ImportError:
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
@ -50,7 +52,7 @@ class RSSReader(object):
self.key = key
self.url = url
self.interval = interval
self.entries = {} # stored feeds
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.
@ -63,7 +65,8 @@ class RSSReader(object):
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
@ -92,12 +95,14 @@ class RSSReader(object):
self.task.start(self.interval, now=False)
RSS_READERS[self.key] = self
def build_connection_key(channel, url):
"This is used to id the connection"
if hasattr(channel, 'key'):
channel = channel.key
return "rss_%s>%s" % (url, channel)
def create_connection(channel, url, interval):
"""
This will create a new RSS->channel connection
@ -113,13 +118,17 @@ def create_connection(channel, url, interval):
if old_conns:
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)
# 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()
connect_to_rss(conn)
return True
def delete_connection(channel, url):
"""
Delete rss connection between channel and url
@ -135,6 +144,7 @@ def delete_connection(channel, url):
reader.task.stop()
return True
def connect_to_rss(connection):
"""
Create the parser instance and connect to RSS feed and channel
@ -145,6 +155,7 @@ def connect_to_rss(connection):
# 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