mirror of
https://github.com/evennia/evennia.git
synced 2026-03-31 04:57:16 +02:00
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:
parent
30b7d2a405
commit
1ae17bcbe4
154 changed files with 5613 additions and 4054 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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 = {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
423
src/comms/irc.py
423
src/comms/irc.py
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue