mirror of
https://github.com/evennia/evennia.git
synced 2026-04-01 13:37:17 +02:00
Cleaned and updated the i18n strings for various server-core system. Removed i18n for all strings that are only visible on stdout or in logs. Still missing i18n on certain specific things such as model field help and attribute warnings. Updated Swedish translation to match.
This commit is contained in:
parent
80da420ee7
commit
4c849ec351
26 changed files with 918 additions and 1420 deletions
|
|
@ -1,20 +1,20 @@
|
|||
#
|
||||
# This sets up how models are displayed
|
||||
# in the web admin interface.
|
||||
# This sets up how models are displayed
|
||||
# in the web admin interface.
|
||||
#
|
||||
|
||||
from django.contrib import admin
|
||||
from src.comms.models import Channel, Msg, PlayerChannelConnection, ExternalChannelConnection
|
||||
|
||||
class MsgAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers', 'db_channels', 'db_message', 'db_lock_storage')
|
||||
class MsgAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers', 'db_channels', 'db_message', 'db_lock_storage')
|
||||
list_display_links = ("id",)
|
||||
ordering = ["db_date_sent", 'db_sender', 'db_receivers', 'db_channels']
|
||||
#readonly_fields = ['db_message', 'db_sender', 'db_receivers', 'db_channels']
|
||||
search_fields = ['id', '^db_date_sent', '^db_message']
|
||||
save_as = True
|
||||
save_on_top = True
|
||||
list_select_related = True
|
||||
save_as = True
|
||||
save_on_top = True
|
||||
list_select_related = True
|
||||
#admin.site.register(Msg, MsgAdmin)
|
||||
|
||||
class PlayerChannelConnectionInline(admin.TabularInline):
|
||||
|
|
@ -41,12 +41,12 @@ class ChannelAdmin(admin.ModelAdmin):
|
|||
list_display_links = ("id", 'db_key')
|
||||
ordering = ["db_key"]
|
||||
search_fields = ['id', 'db_key', 'db_aliases']
|
||||
save_as = True
|
||||
save_on_top = True
|
||||
list_select_related = True
|
||||
save_as = True
|
||||
save_on_top = True
|
||||
list_select_related = True
|
||||
fieldsets = (
|
||||
(None, {'fields':(('db_key', 'db_aliases', 'db_desc'),'db_lock_storage', 'db_keep_log')}),
|
||||
)
|
||||
)
|
||||
|
||||
admin.site.register(Channel, ChannelAdmin)
|
||||
|
||||
|
|
@ -55,9 +55,9 @@ admin.site.register(Channel, ChannelAdmin)
|
|||
# list_display_links = ("db_player", 'db_channel')
|
||||
# ordering = ["db_channel"]
|
||||
# search_fields = ['db_channel', 'db_player']
|
||||
# save_as = True
|
||||
# save_on_top = True
|
||||
# list_select_related = True
|
||||
# save_as = True
|
||||
# save_on_top = True
|
||||
# list_select_related = True
|
||||
# admin.site.register(PlayerChannelConnection, PlayerChannelConnectionAdmin)
|
||||
|
||||
# class ExternalChannelConnectionAdmin(admin.ModelAdmin):
|
||||
|
|
@ -65,8 +65,8 @@ admin.site.register(Channel, ChannelAdmin)
|
|||
# list_display_links = ("db_channel", 'db_external_key', 'db_external_config')
|
||||
# ordering = ["db_channel"]
|
||||
# search_fields = ['db_channel', 'db_external_key']
|
||||
# save_as = True
|
||||
# save_on_top = True
|
||||
# list_select_related = True
|
||||
# save_as = True
|
||||
# save_on_top = True
|
||||
# list_select_related = True
|
||||
# admin.site.register(ExternalChannelConnection, ExternalChannelConnectionAdmin)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ from src.comms.imc2lib import imc2_packets as pck
|
|||
from src.comms.imc2lib.imc2_trackers import IMC2MudList, IMC2ChanList
|
||||
from src.comms.imc2lib.imc2_listeners import handle_whois_reply
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
# IMC2 network setup
|
||||
IMC2_MUDNAME = settings.SERVERNAME
|
||||
IMC2_NETWORK = settings.IMC2_NETWORK
|
||||
|
|
@ -26,8 +28,8 @@ IMC2_SERVER_PWD = settings.IMC2_SERVER_PWD
|
|||
# channel to send info to
|
||||
INFOCHANNEL = Channel.objects.channel_search(settings.CHANNEL_MUDINFO[0])
|
||||
# all linked channel connections
|
||||
IMC2_CLIENT = None
|
||||
# IMC2 debug mode
|
||||
IMC2_CLIENT = None
|
||||
# IMC2 debug mode
|
||||
IMC2_DEBUG = False
|
||||
# Use this instance to keep track of the other games on the network.
|
||||
IMC2_MUDLIST = IMC2MudList()
|
||||
|
|
@ -41,7 +43,7 @@ IMC2_CHANLIST = IMC2ChanList()
|
|||
def msg_info(message):
|
||||
"""
|
||||
Send info to default info channel
|
||||
"""
|
||||
"""
|
||||
try:
|
||||
INFOCHANNEL[0].msg(message)
|
||||
message = '[%s][IMC2]: %s' % (INFOCHANNEL[0].key, message)
|
||||
|
|
@ -49,7 +51,7 @@ def msg_info(message):
|
|||
logger.log_infomsg("MUDinfo (imc2): %s" % message)
|
||||
|
||||
#
|
||||
# Regular scripts
|
||||
# Regular scripts
|
||||
#
|
||||
|
||||
class Send_IsAlive(Script):
|
||||
|
|
@ -61,8 +63,8 @@ class Send_IsAlive(Script):
|
|||
def at_script_creation(self):
|
||||
self.key = 'IMC2_Send_IsAlive'
|
||||
self.interval = 900
|
||||
self.desc = "Send an IMC2 is-alive packet"
|
||||
self.persistent = True
|
||||
self.desc = _("Send an IMC2 is-alive packet")
|
||||
self.persistent = True
|
||||
def at_repeat(self):
|
||||
IMC2_CLIENT.send_packet(pck.IMC2PacketIsAlive())
|
||||
def is_valid(self):
|
||||
|
|
@ -77,9 +79,9 @@ class Send_Keepalive_Request(Script):
|
|||
def at_script_creation(self):
|
||||
self.key = "IMC2_Send_Keepalive_Request"
|
||||
self.interval = 3500
|
||||
self.desc = "Send an IMC2 keepalive-request packet"
|
||||
self.persistent = True
|
||||
def at_repeat(self):
|
||||
self.desc = _("Send an IMC2 keepalive-request packet")
|
||||
self.persistent = True
|
||||
def at_repeat(self):
|
||||
IMC2_CLIENT.channel.send_packet(pck.IMC2PacketKeepAliveRequest())
|
||||
def is_valid(self):
|
||||
"Is only valid as long as there are channels to update"
|
||||
|
|
@ -94,8 +96,8 @@ class Prune_Inactive_Muds(Script):
|
|||
def at_script_creation(self):
|
||||
self.key = "IMC2_Prune_Inactive_Muds"
|
||||
self.interval = 1800
|
||||
self.desc = "Check IMC2 list for inactive games"
|
||||
self.persistent = True
|
||||
self.desc = _("Check IMC2 list for inactive games")
|
||||
self.persistent = True
|
||||
self.inactive_threshold = 3599
|
||||
def at_repeat(self):
|
||||
for name, mudinfo in IMC2_MUDLIST.mud_list.items():
|
||||
|
|
@ -104,7 +106,7 @@ class Prune_Inactive_Muds(Script):
|
|||
def is_valid(self):
|
||||
"Is only valid as long as there are channels to update"
|
||||
return any(service for service in SESSIONS.server.services if service.name.startswith("imc2_"))
|
||||
|
||||
|
||||
class Sync_Server_Channel_List(Script):
|
||||
"""
|
||||
Re-syncs the network's channel list. This will
|
||||
|
|
@ -114,11 +116,11 @@ class Sync_Server_Channel_List(Script):
|
|||
"""
|
||||
def at_script_creation(self):
|
||||
self.key = "IMC2_Sync_Server_Channel_List"
|
||||
self.interval = 24 * 3600 # once every day
|
||||
self.desc = "Re-sync IMC2 network channel list"
|
||||
self.persistent = True
|
||||
self.interval = 24 * 3600 # once every day
|
||||
self.desc = _("Re-sync IMC2 network channel list")
|
||||
self.persistent = True
|
||||
def at_repeat(self):
|
||||
checked_networks = []
|
||||
checked_networks = []
|
||||
network = IMC2_CLIENT.factory.network
|
||||
if not network in checked_networks:
|
||||
channel.send_packet(pkg.IMC2PacketIceRefresh())
|
||||
|
|
@ -126,7 +128,7 @@ class Sync_Server_Channel_List(Script):
|
|||
def is_valid(self):
|
||||
return any(service for service in SESSIONS.server.services if service.name.startswith("imc2_"))
|
||||
#
|
||||
# IMC2 protocol
|
||||
# IMC2 protocol
|
||||
#
|
||||
|
||||
class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
||||
|
|
@ -143,7 +145,7 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
|||
self.network_name = None
|
||||
self.sequence = None
|
||||
|
||||
|
||||
|
||||
def connectionMade(self):
|
||||
"""
|
||||
Triggered after connecting to the IMC2 network.
|
||||
|
|
@ -153,19 +155,19 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
|||
logger.log_infomsg("IMC2: Connected to network server.")
|
||||
logger.log_infomsg("IMC2: Sending authentication packet.")
|
||||
self.send_packet(pck.IMC2PacketAuthPlaintext())
|
||||
|
||||
|
||||
def connectionLost(self, reason=None):
|
||||
"""
|
||||
This is executed when the connection is lost for
|
||||
whatever reason.
|
||||
This is executed when the connection is lost for
|
||||
whatever reason.
|
||||
"""
|
||||
try:
|
||||
service = SESSIONS.server.services.getServiceNamed("imc2_%s:%s(%s)" % (IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME))
|
||||
except Exception:
|
||||
return
|
||||
return
|
||||
if service.running:
|
||||
service.stopService()
|
||||
|
||||
|
||||
def send_packet(self, packet):
|
||||
"""
|
||||
Given a sub-class of IMC2Packet, assemble the packet and send it
|
||||
|
|
@ -182,14 +184,14 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
|||
logger.log_infomsg("IMC2: SENT> %s" % packet_str)
|
||||
logger.log_infomsg(str(packet))
|
||||
self.sendLine(packet_str)
|
||||
|
||||
|
||||
def _parse_auth_response(self, line):
|
||||
"""
|
||||
Parses the IMC2 network authentication packet.
|
||||
"""
|
||||
if self.auth_type == "plaintext":
|
||||
# Plain text passwords.
|
||||
# SERVER Sends: PW <servername> <serverpw> version=<version#> <networkname>
|
||||
# Plain text passwords.
|
||||
# SERVER Sends: PW <servername> <serverpw> version=<version#> <networkname>
|
||||
|
||||
if IMC2_DEBUG:
|
||||
logger.log_infomsg("IMC2: AUTH< %s" % line)
|
||||
|
|
@ -199,26 +201,26 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
|||
autosetup_present = line_split[0] == 'autosetup'
|
||||
|
||||
if "reject" in line_split:
|
||||
auth_message = "IMC2 server rejected connection."
|
||||
auth_message = _("IMC2 server rejected connection.")
|
||||
logger.log_infomsg(auth_message)
|
||||
msg_info(auth_message)
|
||||
return
|
||||
|
||||
return
|
||||
|
||||
if pw_present:
|
||||
self.server_name = line_split[1]
|
||||
self.network_name = line_split[4]
|
||||
elif autosetup_present:
|
||||
logger.log_infomsg("IMC2: Autosetup response found.")
|
||||
logger.log_infomsg(_("IMC2: Autosetup response found."))
|
||||
self.server_name = line_split[1]
|
||||
self.network_name = line_split[3]
|
||||
self.network_name = line_split[3]
|
||||
self.is_authenticated = True
|
||||
self.sequence = int(time())
|
||||
|
||||
|
||||
# Log to stdout and notify over MUDInfo.
|
||||
auth_message = "Successfully authenticated to the '%s' network." % self.factory.network
|
||||
auth_message = _("Successfully authenticated to the '%s' network.") % self.factory.network
|
||||
logger.log_infomsg('IMC2: %s' % auth_message)
|
||||
msg_info(auth_message)
|
||||
|
||||
|
||||
# Ask to see what other MUDs are connected.
|
||||
self.send_packet(pck.IMC2PacketKeepAliveRequest())
|
||||
# IMC2 protocol states that KeepAliveRequests should be followed
|
||||
|
|
@ -226,7 +228,7 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
|||
self.send_packet(pck.IMC2PacketIsAlive())
|
||||
# Get a listing of channels.
|
||||
self.send_packet(pck.IMC2PacketIceRefresh())
|
||||
|
||||
|
||||
def _msg_evennia(self, packet):
|
||||
"""
|
||||
Handle the sending of packet data to Evennia channel
|
||||
|
|
@ -235,34 +237,35 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
|||
conn_name = packet.optional_data.get('channel', None)
|
||||
|
||||
# If the packet lacks the 'echo' key, don't bother with it.
|
||||
if not conn_name or not packet.optional_data.get('echo', None):
|
||||
return
|
||||
if not conn_name or not packet.optional_data.get('echo', None):
|
||||
return
|
||||
imc2_channel = conn_name.split(':', 1)[1]
|
||||
|
||||
# Look for matching IMC2 channel maps mapping to this imc2 channel.
|
||||
conns = ExternalChannelConnection.objects.filter(db_external_key__startswith="imc2_")
|
||||
conns = [conn for conn in conns if imc2_channel in conn.db_external_config.split(",")]
|
||||
if not conns:
|
||||
# we are not listening to this imc2 channel.
|
||||
return
|
||||
|
||||
# we are not listening to this imc2 channel.
|
||||
return
|
||||
|
||||
# Format the message to send to local channel(s).
|
||||
for conn in conns:
|
||||
message = '[%s] %s@%s: %s' % (conn.channel.key, packet.sender, packet.origin, packet.optional_data.get('text'))
|
||||
message = '[%s] %s@%s: %s' % (conn.channel.key, packet.sender, packet.origin, packet.optional_data.get('text'))
|
||||
conn.to_channel(message)
|
||||
|
||||
|
||||
def _format_tell(self, packet):
|
||||
"""
|
||||
Handle tells over IMC2 by formatting the text properly
|
||||
Handle tells over IMC2 by formatting the text properly
|
||||
"""
|
||||
return "{c%s@%s{n {wpages (over IMC):{n %s" % (packet.sender, packet.origin,
|
||||
packet.optional_data.get('text', 'ERROR: No text provided.'))
|
||||
return _("{c%(sender)s@%(origin)s{n {wpages (over IMC):{n %(msg)s") % {"sender":packet.sender,
|
||||
"origin":packet.origin,
|
||||
"msg":packet.optional_data.get('text', 'ERROR: No text provided.')}
|
||||
|
||||
def lineReceived(self, line):
|
||||
"""
|
||||
Triggered when text is received from the IMC2 network. Figures out
|
||||
what to do with the packet.
|
||||
IMC2 -> Evennia
|
||||
IMC2 -> Evennia
|
||||
"""
|
||||
line = line.strip()
|
||||
|
||||
|
|
@ -271,18 +274,18 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
|||
else:
|
||||
if IMC2_DEBUG and not 'is-alive' in line:
|
||||
# if IMC2_DEBUG mode is on, print the contents of the packet
|
||||
# to stdout.
|
||||
# to stdout.
|
||||
logger.log_infomsg("IMC2: RECV> %s" % line)
|
||||
|
||||
|
||||
# Parse the packet and encapsulate it for easy access
|
||||
packet = pck.IMC2Packet(self.factory.mudname, packet_str=line)
|
||||
|
||||
|
||||
if IMC2_DEBUG and packet.packet_type not in ('is-alive', 'keepalive-request'):
|
||||
# Print the parsed packet's __str__ representation.
|
||||
# is-alive and keepalive-requests happen pretty frequently.
|
||||
# Don't bore us with them in stdout.
|
||||
logger.log_infomsg(str(packet))
|
||||
|
||||
|
||||
# Figure out what kind of packet we're dealing with and hand it
|
||||
# off to the correct handler.
|
||||
|
||||
|
|
@ -305,9 +308,9 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
|||
elif packet.packet_type == 'tell':
|
||||
player = search.players(packet.target)
|
||||
if not player:
|
||||
return
|
||||
return
|
||||
player[0].msg(self._format_tell(packet))
|
||||
|
||||
|
||||
def msg_imc2(self, message, from_obj=None, packet_type="imcbroadcast", data=None):
|
||||
"""
|
||||
Called by Evennia to send a message through the imc2 connection
|
||||
|
|
@ -319,33 +322,33 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
|||
from_name = from_obj
|
||||
else:
|
||||
from_name = self.factory.mudname
|
||||
|
||||
|
||||
if packet_type == "imcbroadcast":
|
||||
if type(data) == dict:
|
||||
conns = ExternalChannelConnection.objects.filter(db_external_key__startswith="imc2_",
|
||||
db_channel=data.get("channel", "Unknown"))
|
||||
if type(data) == dict:
|
||||
conns = ExternalChannelConnection.objects.filter(db_external_key__startswith="imc2_",
|
||||
db_channel=data.get("channel", "Unknown"))
|
||||
if not conns:
|
||||
return
|
||||
# we remove the extra channel info since imc2 supplies this anyway
|
||||
if ":" in message:
|
||||
header, message = [part.strip() for part in message.split(":", 1)]
|
||||
# send the packet
|
||||
# send the packet
|
||||
imc2_channel = conns[0].db_external_config.split(',')[0] # only send to the first channel
|
||||
self.send_packet(pck.IMC2PacketIceMsgBroadcasted(self.factory.servername, imc2_channel,
|
||||
from_name, message))
|
||||
elif packet_type == "imctell":
|
||||
# send a tell
|
||||
# send a tell
|
||||
if type(data) == dict:
|
||||
target = data.get("target", "Unknown")
|
||||
destination = data.get("destination", "Unknown")
|
||||
self.send_packet(pck.IMC2PacketTell(from_name, target, destination, message))
|
||||
|
||||
|
||||
elif packet_type == "imcwhois":
|
||||
# send a whois request
|
||||
# send a whois request
|
||||
if type(data) == dict:
|
||||
target = data.get("target", "Unknown")
|
||||
self.send_packet(pck.IMC2PacketWhois(from_obj.id, target))
|
||||
|
||||
|
||||
class IMC2Factory(protocol.ClientFactory):
|
||||
"""
|
||||
Creates instances of the IMC2Protocol. Should really only ever
|
||||
|
|
@ -353,24 +356,24 @@ class IMC2Factory(protocol.ClientFactory):
|
|||
"""
|
||||
protocol = IMC2Protocol
|
||||
|
||||
def __init__(self, network, port, mudname, client_pwd, server_pwd):
|
||||
def __init__(self, network, port, mudname, client_pwd, server_pwd):
|
||||
self.pretty_key = "%s:%s(%s)" % (network, port, mudname)
|
||||
self.network = network
|
||||
sname, host = network.split(".", 1)
|
||||
self.servername = sname.strip()
|
||||
self.port = port
|
||||
self.port = port
|
||||
self.mudname = mudname
|
||||
self.protocol_version = '2'
|
||||
self.client_pwd = client_pwd
|
||||
self.server_pwd = server_pwd
|
||||
self.server_pwd = server_pwd
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
message = 'Connection failed: %s' % reason.getErrorMessage()
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
message = _('Connection failed: %s') % reason.getErrorMessage()
|
||||
msg_info(message)
|
||||
logger.log_errmsg('IMC2: %s' % message)
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
message = 'Connection lost: %s' % reason.getErrorMessage()
|
||||
message = _('Connection lost: %s') % reason.getErrorMessage()
|
||||
msg_info(message)
|
||||
logger.log_errmsg('IMC2: %s' % message)
|
||||
|
||||
|
|
@ -386,10 +389,10 @@ def start_scripts(validate=False):
|
|||
Start all the needed scripts
|
||||
"""
|
||||
|
||||
if validate:
|
||||
if validate:
|
||||
from src.scripts.models import ScriptDB
|
||||
ScriptDB.objects.validate()
|
||||
return
|
||||
return
|
||||
if not search.scripts("IMC2_Send_IsAlive"):
|
||||
create.create_script(Send_IsAlive)
|
||||
if not search.scripts("IMC2_Send_Keepalive_Request"):
|
||||
|
|
@ -397,7 +400,7 @@ def start_scripts(validate=False):
|
|||
if not search.scripts("IMC2_Prune_Inactive_Muds"):
|
||||
create.create_script(Prune_Inactive_Muds)
|
||||
if not search.scripts("IMC2_Sync_Server_Channel_List"):
|
||||
create.create_script(Sync_Server_Channel_List)
|
||||
create.create_script(Sync_Server_Channel_List)
|
||||
|
||||
def create_connection(channel, imc2_channel):
|
||||
"""
|
||||
|
|
@ -407,61 +410,61 @@ def create_connection(channel, imc2_channel):
|
|||
if not type(channel) == Channel:
|
||||
new_channel = Channel.objects.filter(db_key=channel)
|
||||
if not new_channel:
|
||||
logger.log_errmsg("Cannot attach IMC2<->Evennia: Evennia Channel '%s' not found" % channel)
|
||||
logger.log_errmsg(_("Cannot attach IMC2<->Evennia: Evennia Channel '%s' not found") % channel)
|
||||
return False
|
||||
channel = new_channel[0]
|
||||
key = build_connection_key(channel, imc2_channel)
|
||||
|
||||
old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
|
||||
if old_conns:
|
||||
# this evennia channel is already connected to imc. Check if imc2_channel is different.
|
||||
# this evennia channel is already connected to imc. Check if imc2_channel is different.
|
||||
# connection already exists. We try to only connect a new channel
|
||||
old_config = old_conns[0].db_external_config.split(",")
|
||||
old_config = old_conns[0].db_external_config.split(",")
|
||||
if imc2_channel in old_config:
|
||||
return False # we already listen to this channel
|
||||
else:
|
||||
# We add a new imc2_channel to listen to
|
||||
old_config.append(imc2_channel)
|
||||
old_conns[0].db_external_config = ",".join(old_config)
|
||||
old_conns[0].save()
|
||||
old_conns[0].save()
|
||||
return True
|
||||
else:
|
||||
# no old connection found; create a new one.
|
||||
config = imc2_channel
|
||||
# how the evennia channel will be able to contact this protocol in reverse
|
||||
# how the evennia channel will be able to contact this protocol in reverse
|
||||
send_code = "from src.comms.imc2 import IMC2_CLIENT\n"
|
||||
send_code += "data={'channel':from_channel}\n"
|
||||
send_code += "IMC2_CLIENT.msg_imc2(message, from_obj=from_obj, data=data)\n"
|
||||
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
|
||||
db_external_config=config)
|
||||
conn.save()
|
||||
return True
|
||||
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
|
||||
db_external_config=config)
|
||||
conn.save()
|
||||
return True
|
||||
|
||||
def delete_connection(channel, imc2_channel):
|
||||
"Destroy a connection"
|
||||
if hasattr(channel, "key"):
|
||||
channel = channel.key
|
||||
key = build_connection_key(channel, imc2_channel)
|
||||
|
||||
|
||||
try:
|
||||
conn = ExternalChannelConnection.objects.get(db_external_key=key)
|
||||
except ExternalChannelConnection.DoesNotExist:
|
||||
return False
|
||||
conn.delete()
|
||||
return True
|
||||
return False
|
||||
conn.delete()
|
||||
return True
|
||||
|
||||
def connect_to_imc2():
|
||||
"Create the imc instance and connect to the IMC2 network."
|
||||
"Create the imc instance and connect to the IMC2 network."
|
||||
|
||||
# connect
|
||||
imc = internet.TCPClient(IMC2_NETWORK, int(IMC2_PORT), IMC2Factory(IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME,
|
||||
# connect
|
||||
imc = internet.TCPClient(IMC2_NETWORK, int(IMC2_PORT), IMC2Factory(IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME,
|
||||
IMC2_CLIENT_PWD, IMC2_SERVER_PWD))
|
||||
imc.setName("imc2_%s:%s(%s)" % (IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME))
|
||||
imc.setName("imc2_%s:%s(%s)" % (IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME))
|
||||
SESSIONS.server.services.addService(imc)
|
||||
|
||||
def connect_all():
|
||||
"""
|
||||
Activates the imc2 system. Called by the server if IMC2_ENABLED=True.
|
||||
Activates the imc2 system. Called by the server if IMC2_ENABLED=True.
|
||||
"""
|
||||
connect_to_imc2()
|
||||
start_scripts()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"""
|
||||
ANSI parser - this adds colour to text according to
|
||||
special markup strings.
|
||||
special markup strings.
|
||||
|
||||
This is a IMC2 complacent version.
|
||||
"""
|
||||
|
|
@ -14,9 +14,9 @@ class IMCANSIParser(ansi.ANSIParser):
|
|||
"""
|
||||
def __init__(self):
|
||||
normal = ansi.ANSI_NORMAL
|
||||
hilite = ansi.ANSI_HILITE
|
||||
hilite = ansi.ANSI_HILITE
|
||||
self.ansi_map = [
|
||||
(r'~Z', normal), # Random
|
||||
(r'~Z', normal), # Random
|
||||
(r'~x', normal + ansi.ANSI_BLACK), # Black
|
||||
(r'~D', hilite + ansi.ANSI_BLACK), # Dark Grey
|
||||
(r'~z', hilite + ansi.ANSI_BLACK),
|
||||
|
|
@ -25,21 +25,21 @@ class IMCANSIParser(ansi.ANSIParser):
|
|||
(r'~g', normal + ansi.ANSI_GREEN), # Dark Green
|
||||
(r'~G', hilite + ansi.ANSI_GREEN), # Green
|
||||
(r'~p', normal + ansi.ANSI_MAGENTA), # Dark magenta
|
||||
(r'~m', normal + ansi.ANSI_MAGENTA),
|
||||
(r'~m', normal + ansi.ANSI_MAGENTA),
|
||||
(r'~M', hilite + ansi.ANSI_MAGENTA), # Magenta
|
||||
(r'~P', hilite + ansi.ANSI_MAGENTA),
|
||||
(r'~c', normal + ansi.ANSI_CYAN), # Cyan
|
||||
(r'~y', normal + ansi.ANSI_YELLOW), # Dark Yellow (brown)
|
||||
(r'~Y', hilite + ansi.ANSI_YELLOW), # Yellow
|
||||
(r'~Y', hilite + ansi.ANSI_YELLOW), # Yellow
|
||||
(r'~b', normal + ansi.ANSI_BLUE), # Dark Blue
|
||||
(r'~B', hilite + ansi.ANSI_BLUE), # Blue
|
||||
(r'~C', hilite + ansi.ANSI_BLUE),
|
||||
(r'~C', hilite + ansi.ANSI_BLUE),
|
||||
(r'~r', normal + ansi.ANSI_RED), # Dark Red
|
||||
(r'~R', hilite + ansi.ANSI_RED), # Red
|
||||
(r'~R', hilite + ansi.ANSI_RED), # Red
|
||||
|
||||
## Formatting
|
||||
(r'~L', hilite), # Bold/hilite
|
||||
(r'~!', normal), # reset
|
||||
(r'~!', normal), # reset
|
||||
(r'\\r', normal),
|
||||
(r'\\n', ansi.ANSI_RETURN),
|
||||
]
|
||||
|
|
@ -51,7 +51,7 @@ class IMCANSIParser(ansi.ANSIParser):
|
|||
|
||||
|
||||
ANSI_PARSER = IMCANSIParser()
|
||||
|
||||
|
||||
def parse_ansi(string, strip_ansi=False, parser=ANSI_PARSER):
|
||||
"""
|
||||
Shortcut to use the IMC2 ANSI parser.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ This module handles some of the -reply packets like whois-reply.
|
|||
from src.objects.models import ObjectDB
|
||||
from src.comms.imc2lib import imc2_ansi
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
def handle_whois_reply(packet):
|
||||
"""
|
||||
When the player sends an imcwhois <playername> request, the outgoing
|
||||
|
|
@ -15,7 +17,7 @@ def handle_whois_reply(packet):
|
|||
try:
|
||||
pobject = ObjectDB.objects.get(id=packet.target)
|
||||
response_text = imc2_ansi.parse_ansi(packet.optional_data.get('text', 'Unknown'))
|
||||
string = 'Whois reply from %s: %s' % (packet.origin, response_text)
|
||||
string = _('Whois reply from %(origin)s: %(msg)s') % {"origin":packet.origin, "msg":response_text}
|
||||
pobject.msg(string.strip())
|
||||
except ObjectDB.DoesNotExist:
|
||||
# No match found for whois sender. Ignore it.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ http://www.mudbytes.net/index.php?a=articles&s=imc2_protocol
|
|||
|
||||
"""
|
||||
import shlex
|
||||
from django.conf import settings
|
||||
from django.conf import settings
|
||||
|
||||
class Lexxer(shlex.shlex):
|
||||
"""
|
||||
|
|
@ -28,7 +28,7 @@ class IMC2Packet(object):
|
|||
Optionally, parse a packet and load it up.
|
||||
"""
|
||||
# The following fields are all according to the basic packet format of:
|
||||
# <sender>@<origin> <sequence> <route> <packet-type> <target>@<destination> <data...>
|
||||
# <sender>@<origin> <sequence> <route> <packet-type> <target>@<destination> <data...>
|
||||
self.sender = None
|
||||
if not mudname:
|
||||
mudname = settings.SERVERNAME
|
||||
|
|
@ -42,13 +42,13 @@ class IMC2Packet(object):
|
|||
self.optional_data = {}
|
||||
# Reference to the IMC2Protocol object doing the sending.
|
||||
self.imc2_protocol = None
|
||||
|
||||
|
||||
if packet_str:
|
||||
# The lexxer handles the double quotes correctly, unlike just
|
||||
# splitting. Spaces throw things off, so shlex handles it
|
||||
# gracefully, ala POSIX shell-style parsing.
|
||||
lex = Lexxer(packet_str)
|
||||
|
||||
|
||||
# Token counter.
|
||||
counter = 0
|
||||
for token in lex:
|
||||
|
|
@ -90,7 +90,7 @@ class IMC2Packet(object):
|
|||
pass
|
||||
# Increment and continue to the next token (if applicable)
|
||||
counter += 1
|
||||
|
||||
|
||||
def __str__(self):
|
||||
retval = """
|
||||
--IMC2 package (%s)
|
||||
|
|
@ -101,15 +101,15 @@ class IMC2Packet(object):
|
|||
Type: %s
|
||||
Target: %s
|
||||
Dest.: %s
|
||||
Data:
|
||||
%s
|
||||
Data:
|
||||
%s
|
||||
------------------------""" % (self.packet_type, self.sender,
|
||||
self.origin, self.sequence,
|
||||
self.route, self.packet_type,
|
||||
self.target, self.destination,
|
||||
"\n ".join(["%s: %s" % items for items in self.optional_data.items()]))
|
||||
return retval.strip()
|
||||
|
||||
|
||||
def _get_optional_data_string(self):
|
||||
"""
|
||||
Generates the optional data string to tack on to the end of the packet.
|
||||
|
|
@ -126,7 +126,7 @@ class IMC2Packet(object):
|
|||
return data_string.strip()
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
def _get_sender_name(self):
|
||||
"""
|
||||
Calculates the sender name to be sent with the packet.
|
||||
|
|
@ -139,9 +139,9 @@ class IMC2Packet(object):
|
|||
elif type(self.sender) in [type(u""),type(str())]:
|
||||
#this is used by e.g. IRC where no user object is present.
|
||||
return self.sender.strip().replace(' ', '_')
|
||||
elif self.sender:
|
||||
elif self.sender:
|
||||
# Player object.
|
||||
name = self.sender.get_name(fullname=False, show_dbref=False,
|
||||
name = self.sender.get_name(fullname=False, show_dbref=False,
|
||||
show_flags=False,
|
||||
no_ansi=True)
|
||||
# IMC2 does not allow for spaces.
|
||||
|
|
@ -149,12 +149,12 @@ class IMC2Packet(object):
|
|||
else:
|
||||
# None value. Do something or other.
|
||||
return 'Unknown'
|
||||
|
||||
|
||||
def assemble(self, mudname=None, client_pwd=None, server_pwd=None):
|
||||
"""
|
||||
Assembles the packet and returns the ready-to-send string.
|
||||
Note that the arguments are not used, they are there for
|
||||
consistency across all packets.
|
||||
Note that the arguments are not used, they are there for
|
||||
consistency across all packets.
|
||||
"""
|
||||
self.sequence = self.imc2_protocol.sequence
|
||||
packet = "%s@%s %s %s %s %s@%s %s\n" % (
|
||||
|
|
@ -167,29 +167,29 @@ class IMC2Packet(object):
|
|||
self.destination,
|
||||
self._get_optional_data_string())
|
||||
return packet.strip()
|
||||
|
||||
|
||||
class IMC2PacketAuthPlaintext(object):
|
||||
"""
|
||||
IMC2 plain-text authentication packet. Auth packets are strangely
|
||||
formatted, so this does not sub-class IMC2Packet. The SHA and plain text
|
||||
auth packets are the two only non-conformers.
|
||||
|
||||
CLIENT Sends:
|
||||
|
||||
CLIENT Sends:
|
||||
PW <mudname> <clientpw> version=<version#> autosetup <serverpw> (SHA256)
|
||||
|
||||
|
||||
Optional Arguments( required if using the specified authentication method:
|
||||
(SHA256) The literal string: SHA256. This is sent to notify the server
|
||||
that the MUD is SHA256-Enabled. All future logins from this
|
||||
client will be expected in SHA256-AUTH format if the server
|
||||
supports it.
|
||||
"""
|
||||
(SHA256) The literal string: SHA256. This is sent to notify the server
|
||||
that the MUD is SHA256-Enabled. All future logins from this
|
||||
client will be expected in SHA256-AUTH format if the server
|
||||
supports it.
|
||||
"""
|
||||
def assemble(self, mudname=None, client_pwd=None, server_pwd=None):
|
||||
"""
|
||||
This is one of two strange packets, just assemble the packet manually
|
||||
and go.
|
||||
"""
|
||||
return 'PW %s %s version=2 autosetup %s\n' %(mudname, client_pwd, server_pwd)
|
||||
|
||||
|
||||
class IMC2PacketKeepAliveRequest(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
|
|
@ -197,15 +197,15 @@ class IMC2PacketKeepAliveRequest(IMC2Packet):
|
|||
This packet is usually followed by the sending MUD's own is-alive packet.
|
||||
It is used in the filling of a client's MUD list, thus any MUD that doesn't
|
||||
respond with an is-alive isn't marked as online on the sending MUD's mudlist.
|
||||
|
||||
|
||||
Data:
|
||||
(none)
|
||||
|
||||
|
||||
Example of a received keepalive-request:
|
||||
*@YourMUD 1234567890 YourMUD!Hub1 keepalive-request *@*
|
||||
|
||||
|
||||
Example of a sent keepalive-request:
|
||||
*@YourMUD 1234567890 YourMUD keepalive-request *@*
|
||||
*@YourMUD 1234567890 YourMUD keepalive-request *@*
|
||||
"""
|
||||
def __init__(self):
|
||||
super(IMC2PacketKeepAliveRequest, self).__init__()
|
||||
|
|
@ -213,40 +213,40 @@ class IMC2PacketKeepAliveRequest(IMC2Packet):
|
|||
self.packet_type = 'keepalive-request'
|
||||
self.target = '*'
|
||||
self.destination = '*'
|
||||
|
||||
|
||||
class IMC2PacketIsAlive(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
This packet is the reply to a keepalive-request packet. It is responsible
|
||||
This packet is the reply to a keepalive-request packet. It is responsible
|
||||
for filling a client's mudlist with the information about other MUDs on the
|
||||
network.
|
||||
|
||||
|
||||
Data:
|
||||
versionid=<string>
|
||||
versionid=<string>
|
||||
Where <string> is the text version ID of the client. ("IMC2 4.5 MUD-Net")
|
||||
|
||||
url=<string>
|
||||
|
||||
url=<string>
|
||||
Where <string> is the proper URL of the client. (http://www.domain.com)
|
||||
|
||||
host=<string>
|
||||
|
||||
host=<string>
|
||||
Where <string> is the telnet address of the MUD. (telnet://domain.com)
|
||||
|
||||
port=<int>
|
||||
|
||||
port=<int>
|
||||
Where <int> is the telnet port of the MUD.
|
||||
|
||||
|
||||
(These data fields are not sent by the MUD, they are added by the server.)
|
||||
networkname=<string>
|
||||
networkname=<string>
|
||||
Where <string> is the network name that the MUD/server is on. ("MyNetwork")
|
||||
|
||||
|
||||
sha256=<int>
|
||||
This is an optional tag that denotes the SHA-256 capabilities of a
|
||||
This is an optional tag that denotes the SHA-256 capabilities of a
|
||||
MUD or server.
|
||||
|
||||
|
||||
Example of a received is-alive:
|
||||
*@SomeMUD 1234567890 SomeMUD!Hub2 is-alive *@YourMUD versionid="IMC2 4.5 MUD-Net" url="http://www.domain.com" networkname="MyNetwork" sha256=1 host=domain.com port=5500
|
||||
|
||||
|
||||
Example of a sent is-alive:
|
||||
*@YourMUD 1234567890 YourMUD is-alive *@* versionid="IMC2 4.5 MUD-Net" url="http://www.domain.com" host=domain.com port=5500
|
||||
*@YourMUD 1234567890 YourMUD is-alive *@* versionid="IMC2 4.5 MUD-Net" url="http://www.domain.com" host=domain.com port=5500
|
||||
"""
|
||||
def __init__(self):
|
||||
super(IMC2PacketIsAlive, self).__init__()
|
||||
|
|
@ -258,19 +258,19 @@ class IMC2PacketIsAlive(IMC2Packet):
|
|||
'url': '"http://www.evennia.com"',
|
||||
'host': 'test.com',
|
||||
'port': '5555'}
|
||||
|
||||
|
||||
class IMC2PacketIceRefresh(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
This packet is sent by the MUD to request data about the channels on the
|
||||
network. Servers with channels reply with an ice-update packet for each
|
||||
This packet is sent by the MUD to request data about the channels on the
|
||||
network. Servers with channels reply with an ice-update packet for each
|
||||
channel they control. The usual target for this packet is IMC@$.
|
||||
|
||||
|
||||
Data:
|
||||
(none)
|
||||
|
||||
|
||||
Example:
|
||||
*@YourMUD 1234567890 YourMUD!Hub1 ice-refresh IMC@$
|
||||
*@YourMUD 1234567890 YourMUD!Hub1 ice-refresh IMC@$
|
||||
"""
|
||||
def __init__(self):
|
||||
super(IMC2PacketIceRefresh, self).__init__()
|
||||
|
|
@ -284,40 +284,40 @@ class IMC2PacketIceUpdate(IMC2Packet):
|
|||
Description:
|
||||
A server returns this packet with the data of a channel when prompted with
|
||||
an ice-refresh request.
|
||||
|
||||
|
||||
Data:
|
||||
channel=<string>
|
||||
channel=<string>
|
||||
The channel's network name in the format of ServerName:ChannelName
|
||||
|
||||
owner=<string>
|
||||
|
||||
owner=<string>
|
||||
The Name@MUD of the channel's owner
|
||||
|
||||
operators=<string>
|
||||
|
||||
operators=<string>
|
||||
A space-seperated list of the Channel's operators, in the format of Person@MUD
|
||||
|
||||
|
||||
policy=<string>
|
||||
The policy is either "open" or "private" with no quotes.
|
||||
|
||||
|
||||
invited=<string>
|
||||
The space-seperated list of invited User@MUDs, only valid for a
|
||||
The space-seperated list of invited User@MUDs, only valid for a
|
||||
"private" channel.
|
||||
|
||||
|
||||
excluded=<string>
|
||||
The space-seperated list of banned User@MUDs, only valid for "open"
|
||||
The space-seperated list of banned User@MUDs, only valid for "open"
|
||||
channels.
|
||||
|
||||
level=<string> The default level of the channel: Admin, Imp, Imm,
|
||||
|
||||
level=<string> The default level of the channel: Admin, Imp, Imm,
|
||||
Mort, or None
|
||||
|
||||
|
||||
localname=<string> The suggested local name of the channel.
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
|
||||
Open Policy:
|
||||
ICE@Hub1 1234567890 Hub1!Hub2 ice-update *@YourMUD channel=Hub1:ichat owner=Imm@SomeMUD operators=Other@SomeMUD policy=open excluded="Flamer@badMUD Jerk@dirtyMUD" level=Imm localname=ichat
|
||||
|
||||
|
||||
Private Policy:
|
||||
ICE@Hub1 1234567890 Hub1!Hub2 ice-update *@YourMUD channel=Hub1:secretchat owner=Imm@SomeMUD operators=Other@SomeMUD policy=private invited="SpecialDude@OtherMUD CoolDude@WeirdMUD" level=Mort localname=schat
|
||||
ICE@Hub1 1234567890 Hub1!Hub2 ice-update *@YourMUD channel=Hub1:secretchat owner=Imm@SomeMUD operators=Other@SomeMUD policy=private invited="SpecialDude@OtherMUD CoolDude@WeirdMUD" level=Mort localname=schat
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
@ -328,29 +328,29 @@ class IMC2PacketIceMsgRelayed(IMC2Packet):
|
|||
ice-msg-p packet, are used with private policy channels. The 'r' stands
|
||||
for 'relay'. All incoming channel messages are from ICE@<server>, where
|
||||
<server> is the server hosting the channel.
|
||||
|
||||
|
||||
Data:
|
||||
realfrom=<string>
|
||||
realfrom=<string>
|
||||
The User@MUD the message came from.
|
||||
|
||||
channel=<string>
|
||||
|
||||
channel=<string>
|
||||
The Server:Channel the message is intended to be displayed on.
|
||||
|
||||
text=<string>
|
||||
|
||||
text=<string>
|
||||
The message text.
|
||||
|
||||
emote=<int>
|
||||
An integer value designating emotes. 0 for no emote, 1 for an emote,
|
||||
|
||||
emote=<int>
|
||||
An integer value designating emotes. 0 for no emote, 1 for an emote,
|
||||
and 2 for a social.
|
||||
|
||||
|
||||
Examples:
|
||||
ICE@Hub1 1234567890 Hub1!Hub2 ice-msg-r *@YourMUD realfrom=You@YourMUD channel=hub1:secret text="Aha! I got it!" emote=0
|
||||
|
||||
|
||||
ICE@Hub1 1234567890 Hub1!Hub2 ice-msg-r *@YourMUD realfrom=You@YourMUD channel=hub1:secret text=Ahh emote=0
|
||||
|
||||
|
||||
ICE@Hub1 1234567890 Hub1!Hub2 ice-msg-r *@YourMUD realfrom=You@YourMUD channel=hub1:secret text="grins evilly." emote=1
|
||||
|
||||
ICE@Hub1 1234567890 Hub1!Hub2 ice-msg-r *@YourMUD realfrom=You@YourMUD channel=hub1:secret text="You@YourMUD grins evilly!" emote=2
|
||||
|
||||
ICE@Hub1 1234567890 Hub1!Hub2 ice-msg-r *@YourMUD realfrom=You@YourMUD channel=hub1:secret text="You@YourMUD grins evilly!" emote=2
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
@ -360,27 +360,27 @@ class IMC2PacketIceMsgPrivate(IMC2Packet):
|
|||
This packet is sent when a player sends a message to a private channel.
|
||||
This packet should never be seen as incoming to a client. The target of
|
||||
this packet should be IMC@<server> of the server hosting the channel.
|
||||
|
||||
|
||||
Data:
|
||||
channel=<string>
|
||||
channel=<string>
|
||||
The Server:Channel the message is intended to be displayed on.
|
||||
|
||||
|
||||
text=<string>
|
||||
The message text.
|
||||
|
||||
|
||||
emote=<int>
|
||||
An integer value designating emotes. 0 for no emote, 1 for an emote,
|
||||
and 2 for a social.
|
||||
|
||||
|
||||
echo=<int>
|
||||
Tells the server to echo the message back to the sending MUD. This is only
|
||||
seen on out-going messages.
|
||||
|
||||
|
||||
Examples:
|
||||
You@YourMUD 1234567890 YourMUD ice-msg-p IMC@Hub1 channel=Hub1:secret text="Ahh! I got it!" emote=0 echo=1
|
||||
You@YourMUD 1234567890 YourMUD ice-msg-p IMC@Hub1 channel=Hub1:secret text=Ahh! emote=0 echo=1
|
||||
You@YourMUD 1234567890 YourMUD ice-msg-p IMC@Hub1 channel=Hub1:secret text="grins evilly." emote=1 echo=1
|
||||
You@YourMUD 1234567890 YourMUD ice-msg-p IMC@Hub1 channel=Hub1:secret text="You@YourMUD grins evilly." emote=2 echo=1
|
||||
You@YourMUD 1234567890 YourMUD ice-msg-p IMC@Hub1 channel=Hub1:secret text="You@YourMUD grins evilly." emote=2 echo=1
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
@ -392,34 +392,34 @@ class IMC2PacketIceMsgBroadcasted(IMC2Packet):
|
|||
as it was sent by the originating MUD. The server that hosts the channel
|
||||
sends the packet back to the originating MUD as an 'echo' by removing the
|
||||
"echo=1" and attaching the "sender=Person@MUD" data field.
|
||||
|
||||
|
||||
Data:
|
||||
channel=<string>
|
||||
The Server:Channel the message is intended to be displayed on.
|
||||
|
||||
|
||||
text=<string>
|
||||
The message text.
|
||||
|
||||
|
||||
emote=<int>
|
||||
An integer value designating emotes. 0 for no emote, 1 for an emote,
|
||||
An integer value designating emotes. 0 for no emote, 1 for an emote,
|
||||
and 2 for a social.
|
||||
|
||||
|
||||
*echo=<int>
|
||||
This stays on broadcasted messages. It tells the channel's server to
|
||||
relay an echo back.
|
||||
|
||||
|
||||
*sender=<string>
|
||||
The hosting server replaces "echo=1" with this when sending the echo back
|
||||
to the originating MUD.
|
||||
|
||||
Examples:
|
||||
|
||||
Examples:
|
||||
(See above for emote/social examples as they are pretty much the same)
|
||||
|
||||
|
||||
Return Echo Packet:
|
||||
You-YourMUD@Hub1 1234567890 Hub1 ice-msg-b *@YourMUD text=Hi! channel=Hub1:ichat sender=You@YourMUD emote=0
|
||||
|
||||
|
||||
Broadcasted Packet:
|
||||
You@YourMUD 1234567890 YourMUD!Hub1 ice-msg-b *@* channel=Hub1:ichat text=Hi! emote=0 echo=1
|
||||
You@YourMUD 1234567890 YourMUD!Hub1 ice-msg-b *@* channel=Hub1:ichat text=Hi! emote=0 echo=1
|
||||
"""
|
||||
def __init__(self, server, channel, pobject, message):
|
||||
"""
|
||||
|
|
@ -445,27 +445,27 @@ class IMC2PacketUserCache(IMC2Packet):
|
|||
Sent by a MUD with a new IMC2-able player or when a player's gender changes,
|
||||
this packet contains only the gender for data. The packet's origination
|
||||
should be the Player@MUD.
|
||||
|
||||
|
||||
Data:
|
||||
gender=<int> 0 is male, 1 is female, 2 is anything else such as neuter.
|
||||
Will be referred to as "it".
|
||||
|
||||
|
||||
Example:
|
||||
Dude@someMUD 1234567890 SomeMUD!Hub2!Hub1 user-cache *@* gender=0
|
||||
Dude@someMUD 1234567890 SomeMUD!Hub2!Hub1 user-cache *@* gender=0
|
||||
"""
|
||||
pass
|
||||
|
||||
class IMC2PacketUserCacheRequest(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
The MUD sends this packet out when making a request for the user-cache
|
||||
The MUD sends this packet out when making a request for the user-cache
|
||||
information of the user included in the data part of the packet.
|
||||
|
||||
|
||||
Data:
|
||||
user=<string> The Person@MUD whose data the MUD is seeking.
|
||||
|
||||
|
||||
Example:
|
||||
*@YourMUD 1234567890 YourMUD user-cache-request *@SomeMUD user=Dude@SomeMUD
|
||||
*@YourMUD 1234567890 YourMUD user-cache-request *@SomeMUD user=Dude@SomeMUD
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
@ -474,16 +474,16 @@ class IMC2PacketUserCacheReply(IMC2Packet):
|
|||
Description:
|
||||
A reply to the user-cache-request packet. It contains the user and gender
|
||||
for the user.
|
||||
|
||||
|
||||
Data:
|
||||
user=<string>
|
||||
The Person@MUD whose data the MUD requested.
|
||||
|
||||
|
||||
gender=<int>
|
||||
The gender of the Person@MUD in the 'user' field.
|
||||
|
||||
|
||||
Example:
|
||||
*@someMUD 1234567890 SomeMUD!Hub2!Hub1 user-cache-reply *@YourMUD user=Dude@SomeMUD gender=0
|
||||
*@someMUD 1234567890 SomeMUD!Hub2!Hub1 user-cache-reply *@YourMUD user=Dude@SomeMUD gender=0
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
@ -492,18 +492,18 @@ class IMC2PacketTell(IMC2Packet):
|
|||
Description:
|
||||
This packet is used to communicate private messages between users on MUDs
|
||||
across the network.
|
||||
|
||||
|
||||
Data:
|
||||
text=<string> Message text
|
||||
isreply=<int> Two settings: 1 denotes a reply, 2 denotes a tell social.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
Originating:
|
||||
You@YourMUD 1234567890 YourMUD tell Dude@SomeMUD text="Having fun?"
|
||||
|
||||
|
||||
Reply from Dude:
|
||||
Dude@SomeMUD 1234567890 SomeMUD!Hub1 tell You@YourMUD text="Yeah, this is cool!" isreply=1
|
||||
Dude@SomeMUD 1234567890 SomeMUD!Hub1 tell You@YourMUD text="Yeah, this is cool!" isreply=1
|
||||
"""
|
||||
def __init__(self, pobject, target, destination, message):
|
||||
super(IMC2PacketTell, self).__init__()
|
||||
|
|
@ -511,94 +511,94 @@ class IMC2PacketTell(IMC2Packet):
|
|||
self.packet_type = "tell"
|
||||
self.target = target
|
||||
self.destination = destination
|
||||
self.optional_data = {"text": message,
|
||||
self.optional_data = {"text": message,
|
||||
"isreply":None}
|
||||
|
||||
|
||||
def assemble(self, mudname=None, client_pwd=None, server_pwd=None):
|
||||
self.sequence = self.imc2_protocol.sequence
|
||||
#self.route = "%s!%s" % (self.origin, self.imc2_protocol.factory.servername.capitalize())
|
||||
return '''"%s@%s %s %s tell %s@%s text="%s"''' % (self.sender, self.origin, self.sequence,
|
||||
self.route, self.target, self.destination,
|
||||
return '''"%s@%s %s %s tell %s@%s text="%s"''' % (self.sender, self.origin, self.sequence,
|
||||
self.route, self.target, self.destination,
|
||||
self.optional_data.get("text","NO TEXT GIVEN"))
|
||||
|
||||
|
||||
class IMC2PacketEmote(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
This packet seems to be sent by servers when notifying the network of a new
|
||||
channel or the destruction of a channel.
|
||||
|
||||
|
||||
Data:
|
||||
channel=<int>
|
||||
Unsure of what this means. The channel seen in both creation and
|
||||
channel=<int>
|
||||
Unsure of what this means. The channel seen in both creation and
|
||||
destruction packets is 15.
|
||||
|
||||
|
||||
level=<int>
|
||||
I am assuming this is the permission level of the sender. In both
|
||||
creation and destruction messages, this is -1.
|
||||
|
||||
|
||||
text=<string>
|
||||
This is the message to be sent to the users.
|
||||
|
||||
|
||||
Examples:
|
||||
ICE@Hub1 1234567890 Hub1 emote *@* channel=15 level=-1 text="the channel called hub1:test has been destroyed by You@YourMUD."
|
||||
ICE@Hub1 1234567890 Hub1 emote *@* channel=15 level=-1 text="the channel called hub1:test has been destroyed by You@YourMUD."
|
||||
"""
|
||||
pass
|
||||
|
||||
class IMC2PacketRemoteAdmin(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
This packet is used in remote server administration. Please note that
|
||||
SHA-256 Support is *required* for a client to use this feature. The command
|
||||
can vary, in fact this very packet is highly dependant on the server it's
|
||||
being directed to. In most cases, sending the 'list' command will have a
|
||||
This packet is used in remote server administration. Please note that
|
||||
SHA-256 Support is *required* for a client to use this feature. The command
|
||||
can vary, in fact this very packet is highly dependant on the server it's
|
||||
being directed to. In most cases, sending the 'list' command will have a
|
||||
remote-admin enabled server send you the list of commands it will accept.
|
||||
|
||||
|
||||
Data:
|
||||
command=<string>
|
||||
command=<string>
|
||||
The command being sent to the server for processing.
|
||||
|
||||
data=<string>
|
||||
|
||||
data=<string>
|
||||
Data associated with the command. This is not always required.
|
||||
|
||||
hash=<string>
|
||||
The SHA-256 hash that is verified by the server. This hash is generated in
|
||||
|
||||
hash=<string>
|
||||
The SHA-256 hash that is verified by the server. This hash is generated in
|
||||
the same manner as an authentication packet.
|
||||
|
||||
|
||||
Example:
|
||||
You@YourMUD 1234567890 YourMUD remote-admin IMC@Hub1 command=list hash=<hash goes here>
|
||||
You@YourMUD 1234567890 YourMUD remote-admin IMC@Hub1 command=list hash=<hash goes here>
|
||||
"""
|
||||
pass
|
||||
|
||||
class IMC2PacketIceCmd(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
Used for remote channel administration. In most cases, one must be listed
|
||||
as a channel creator on the target server in order to do much with this
|
||||
Used for remote channel administration. In most cases, one must be listed
|
||||
as a channel creator on the target server in order to do much with this
|
||||
packet. Other cases include channel operators.
|
||||
|
||||
|
||||
Data:
|
||||
channel=<string>
|
||||
channel=<string>
|
||||
The target server:channel for the command.
|
||||
|
||||
command=<string>
|
||||
|
||||
command=<string>
|
||||
The command to be processed.
|
||||
|
||||
data=<string>
|
||||
|
||||
data=<string>
|
||||
Data associated with the command. This is not always required.
|
||||
|
||||
|
||||
Example:
|
||||
You@YourMUD 1234567890 YourMUD ice-cmd IMC@hub1 channel=hub1:ichat command=list
|
||||
You@YourMUD 1234567890 YourMUD ice-cmd IMC@hub1 channel=hub1:ichat command=list
|
||||
"""
|
||||
pass
|
||||
|
||||
class IMC2PacketDestroy(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
Sent by a server to indicate the destruction of a channel it hosted.
|
||||
Sent by a server to indicate the destruction of a channel it hosted.
|
||||
The mud should remove this channel from its local configuration.
|
||||
|
||||
|
||||
Data:
|
||||
channel=<string> The server:channel being destroyed.
|
||||
channel=<string> The server:channel being destroyed.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
@ -606,39 +606,39 @@ class IMC2PacketWho(IMC2Packet):
|
|||
"""
|
||||
Description:
|
||||
A seemingly mutli-purpose information-requesting packet. The istats
|
||||
packet currently only works on servers, or at least that's the case on
|
||||
MUD-Net servers. The 'finger' type takes a player name in addition to the
|
||||
packet currently only works on servers, or at least that's the case on
|
||||
MUD-Net servers. The 'finger' type takes a player name in addition to the
|
||||
type name.
|
||||
|
||||
Example: "finger Dude". The 'who' and 'info' types take no argument.
|
||||
The MUD is responsible for building the reply text sent in the who-reply
|
||||
|
||||
Example: "finger Dude". The 'who' and 'info' types take no argument.
|
||||
The MUD is responsible for building the reply text sent in the who-reply
|
||||
packet.
|
||||
|
||||
|
||||
Data:
|
||||
type=<string> Types: who, info, "finger <name>", istats (server only)
|
||||
|
||||
|
||||
Example:
|
||||
Dude@SomeMUD 1234567890 SomeMUD!Hub1 who *@YourMUD type=who
|
||||
Dude@SomeMUD 1234567890 SomeMUD!Hub1 who *@YourMUD type=who
|
||||
"""
|
||||
pass
|
||||
|
||||
class IMC2PacketWhoReply(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
The multi-purpose reply to the multi-purpose information-requesting 'who'
|
||||
packet. The MUD is responsible for building the return data, including the
|
||||
format of it. The mud can use the permission level sent in the original who
|
||||
The multi-purpose reply to the multi-purpose information-requesting 'who'
|
||||
packet. The MUD is responsible for building the return data, including the
|
||||
format of it. The mud can use the permission level sent in the original who
|
||||
packet to filter the output. The example below is the MUD-Net format.
|
||||
|
||||
|
||||
Data:
|
||||
text=<string> The formatted reply to a 'who' packet.
|
||||
|
||||
|
||||
Additional Notes:
|
||||
The example below is for the who list packet. The same construction would
|
||||
The example below is for the who list packet. The same construction would
|
||||
go into formatting the other types of who packets.
|
||||
|
||||
|
||||
Example:
|
||||
*@YourMUD 1234567890 YourMUD who-reply Dude@SomeMUD text="\n\r~R-=< ~WPlayers on YourMUD ~R>=-\n\r ~Y-=< ~Wtelnet://yourmud.domain.com:1234 ~Y>=-\n\r\n\r~B--------------------------------=< ~WPlayers ~B>=---------------------------------\n\r\n\r ~BPlayer ~z<--->~G Mortal the Toy\n\r\n\r~R-------------------------------=< ~WImmortals ~R>=--------------------------------\n\r\n\r ~YStaff ~z<--->~G You the Immortal\n\r\n\r~Y<~W2 Players~Y> ~Y<~WHomepage: http://www.yourmud.com~Y> <~W 2 Max Since Reboot~Y>\n\r~Y<~W3 logins since last reboot on Tue Feb 24, 2004 6:55:59 PM EST~Y>"
|
||||
*@YourMUD 1234567890 YourMUD who-reply Dude@SomeMUD text="\n\r~R-=< ~WPlayers on YourMUD ~R>=-\n\r ~Y-=< ~Wtelnet://yourmud.domain.com:1234 ~Y>=-\n\r\n\r~B--------------------------------=< ~WPlayers ~B>=---------------------------------\n\r\n\r ~BPlayer ~z<--->~G Mortal the Toy\n\r\n\r~R-------------------------------=< ~WImmortals ~R>=--------------------------------\n\r\n\r ~YStaff ~z<--->~G You the Immortal\n\r\n\r~Y<~W2 Players~Y> ~Y<~WHomepage: http://www.yourmud.com~Y> <~W 2 Max Since Reboot~Y>\n\r~Y<~W3 logins since last reboot on Tue Feb 24, 2004 6:55:59 PM EST~Y>"
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
@ -646,12 +646,12 @@ class IMC2PacketWhois(IMC2Packet):
|
|||
"""
|
||||
Description:
|
||||
Sends a request to the network for the location of the specified player.
|
||||
|
||||
|
||||
Data:
|
||||
level=<int> The permission level of the person making the request.
|
||||
|
||||
|
||||
Example:
|
||||
You@YourMUD 1234567890 YourMUD whois dude@* level=5
|
||||
You@YourMUD 1234567890 YourMUD whois dude@* level=5
|
||||
"""
|
||||
def __init__(self, pobject_id, whois_target):
|
||||
super(IMC2PacketWhois, self).__init__()
|
||||
|
|
@ -664,98 +664,98 @@ class IMC2PacketWhois(IMC2Packet):
|
|||
class IMC2PacketWhoisReply(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
The reply to a whois packet. The MUD is responsible for building and formatting
|
||||
the text sent back to the requesting player, and can use the permission level
|
||||
The reply to a whois packet. The MUD is responsible for building and formatting
|
||||
the text sent back to the requesting player, and can use the permission level
|
||||
sent in the original whois packet to filter or block the response.
|
||||
|
||||
|
||||
Data:
|
||||
text=<string> The whois text.
|
||||
|
||||
|
||||
Example:
|
||||
*@SomeMUD 1234567890 SomeMUD!Hub1 whois-reply You@YourMUD text="~RIMC Locate: ~YDude@SomeMUD: ~cOnline.\n\r"
|
||||
*@SomeMUD 1234567890 SomeMUD!Hub1 whois-reply You@YourMUD text="~RIMC Locate: ~YDude@SomeMUD: ~cOnline.\n\r"
|
||||
"""
|
||||
pass
|
||||
|
||||
class IMC2PacketBeep(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
Sends out a beep packet to the Player@MUD. The client receiving this should
|
||||
Sends out a beep packet to the Player@MUD. The client receiving this should
|
||||
then send a bell-character to the target player to 'beep' them.
|
||||
|
||||
|
||||
Example:
|
||||
You@YourMUD 1234567890 YourMUD beep dude@somemud
|
||||
You@YourMUD 1234567890 YourMUD beep dude@somemud
|
||||
"""
|
||||
pass
|
||||
|
||||
class IMC2PacketIceChanWho(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
Sends a request to the specified MUD or * to list all the users listening
|
||||
Sends a request to the specified MUD or * to list all the users listening
|
||||
to the specified channel.
|
||||
|
||||
|
||||
Data:
|
||||
level=<int>
|
||||
level=<int>
|
||||
Sender's permission level.
|
||||
|
||||
channel=<string>
|
||||
|
||||
channel=<string>
|
||||
The server:chan name of the channel.
|
||||
|
||||
lname=<string>
|
||||
|
||||
lname=<string>
|
||||
The localname of the channel.
|
||||
|
||||
|
||||
Example:
|
||||
You@YourMUD 1234567890 YourMUD ice-chan-who somemud level=5 channel=Hub1:ichat lname=ichat
|
||||
You@YourMUD 1234567890 YourMUD ice-chan-who somemud level=5 channel=Hub1:ichat lname=ichat
|
||||
"""
|
||||
pass
|
||||
|
||||
class IMC2PacketIceChanWhoReply(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
This is the reply packet for an ice-chan-who. The MUD is responsible for
|
||||
creating and formatting the list sent back in the 'list' field. The
|
||||
permission level sent in the original ice-chan-who packet can be used to
|
||||
This is the reply packet for an ice-chan-who. The MUD is responsible for
|
||||
creating and formatting the list sent back in the 'list' field. The
|
||||
permission level sent in the original ice-chan-who packet can be used to
|
||||
filter or block the response.
|
||||
|
||||
|
||||
Data:
|
||||
channel=<string>
|
||||
channel=<string>
|
||||
The server:chan of the requested channel.
|
||||
|
||||
list=<string>
|
||||
|
||||
list=<string>
|
||||
The formatted list of local listeners for that MUD.
|
||||
|
||||
|
||||
Example:
|
||||
*@SomeMUD 1234567890 SomeMUD!Hub1 ice-chan-whoreply You@YourMUD channel=Hub1:ichat list="The following people are listening to ichat on SomeMUD:\n\r\n\rDude\n\r"
|
||||
*@SomeMUD 1234567890 SomeMUD!Hub1 ice-chan-whoreply You@YourMUD channel=Hub1:ichat list="The following people are listening to ichat on SomeMUD:\n\r\n\rDude\n\r"
|
||||
"""
|
||||
pass
|
||||
|
||||
class IMC2PacketLaston(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
This packet queries the server the mud is connected to to find out when a
|
||||
This packet queries the server the mud is connected to to find out when a
|
||||
specified user was last seen by the network on a public channel.
|
||||
|
||||
|
||||
Data:
|
||||
username=<string> The user, user@mud, or "all" being queried. Responses
|
||||
username=<string> The user, user@mud, or "all" being queried. Responses
|
||||
to this packet will be sent by the server in the form of a series of tells.
|
||||
|
||||
Example: User@MUD 1234567890 MUD imc-laston SERVER username=somenamehere
|
||||
|
||||
Example: User@MUD 1234567890 MUD imc-laston SERVER username=somenamehere
|
||||
"""
|
||||
pass
|
||||
|
||||
class IMC2PacketCloseNotify(IMC2Packet):
|
||||
"""
|
||||
Description:
|
||||
This packet alerts the network when a server or MUD has disconnected. The
|
||||
server hosting the server or MUD is responsible for sending this packet
|
||||
out across the network. Clients need only process the packet to remove the
|
||||
This packet alerts the network when a server or MUD has disconnected. The
|
||||
server hosting the server or MUD is responsible for sending this packet
|
||||
out across the network. Clients need only process the packet to remove the
|
||||
disconnected MUD from their MUD list (or mark it as Disconnected).
|
||||
|
||||
|
||||
Data:
|
||||
host=<string>
|
||||
host=<string>
|
||||
The MUD or server that has disconnected from the network.
|
||||
|
||||
|
||||
Example:
|
||||
*@Hub2 1234567890 Hub2!Hub1 close-notify *@* host=DisconnMUD
|
||||
*@Hub2 1234567890 Hub2!Hub1 close-notify *@* host=DisconnMUD
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
@ -763,4 +763,4 @@ if __name__ == "__main__":
|
|||
packstr = "Kayle@MW 1234567 MW!Server02!Server01 ice-msg-b *@* channel=Server01:ichat text=\"*they're going woot\" emote=0 echo=1"
|
||||
packstr = "*@Lythelian 1234567 Lythelian!Server01 is-alive *@* versionid=\"Tim's LPC IMC2 client 30-Jan-05 / Dead Souls integrated\" networkname=Mudbytes url=http://dead-souls.net host=70.32.76.142 port=6666 sha256=0"
|
||||
print IMC2Packet(packstr)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Certain periodic packets are sent by connected MUDs (is-alive, user-cache,
|
||||
etc). The IMC2 protocol assumes that each connected MUD will capture these and
|
||||
populate/maintain their own lists of other servers connected. This module
|
||||
contains stuff like this.
|
||||
contains stuff like this.
|
||||
"""
|
||||
from time import time
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ class IMC2Mud(object):
|
|||
self.sha256 = packet.optional_data.get('sha256', None)
|
||||
# This is used to determine when a Mud has fallen into inactive status.
|
||||
self.last_updated = time()
|
||||
|
||||
|
||||
class IMC2MudList(object):
|
||||
"""
|
||||
Keeps track of other MUDs connected to the IMC network.
|
||||
|
|
@ -28,7 +28,7 @@ class IMC2MudList(object):
|
|||
def __init__(self):
|
||||
# Mud list is stored in a dict, key being the IMC Mud name.
|
||||
self.mud_list = {}
|
||||
|
||||
|
||||
def get_mud_list(self):
|
||||
"""
|
||||
Returns a sorted list of connected Muds.
|
||||
|
|
@ -36,7 +36,7 @@ class IMC2MudList(object):
|
|||
muds = self.mud_list.items()
|
||||
muds.sort()
|
||||
return [value for key, value in muds]
|
||||
|
||||
|
||||
def update_mud_from_packet(self, packet):
|
||||
"""
|
||||
This grabs relevant info from the packet and stuffs it in the
|
||||
|
|
@ -44,7 +44,7 @@ class IMC2MudList(object):
|
|||
"""
|
||||
mud = IMC2Mud(packet)
|
||||
self.mud_list[mud.name] = mud
|
||||
|
||||
|
||||
def remove_mud_from_packet(self, packet):
|
||||
"""
|
||||
Removes a mud from the Mud list when given a packet.
|
||||
|
|
@ -55,7 +55,7 @@ class IMC2MudList(object):
|
|||
except KeyError:
|
||||
# No matching entry, no big deal.
|
||||
pass
|
||||
|
||||
|
||||
class IMC2Channel(object):
|
||||
"""
|
||||
Stores information about channels available on the network.
|
||||
|
|
@ -67,7 +67,7 @@ class IMC2Channel(object):
|
|||
self.owner = packet.optional_data.get('owner', None)
|
||||
self.policy = packet.optional_data.get('policy', None)
|
||||
self.last_updated = time()
|
||||
|
||||
|
||||
class IMC2ChanList(object):
|
||||
"""
|
||||
Keeps track of other MUDs connected to the IMC network.
|
||||
|
|
@ -75,7 +75,7 @@ class IMC2ChanList(object):
|
|||
def __init__(self):
|
||||
# Chan list is stored in a dict, key being the IMC Mud name.
|
||||
self.chan_list = {}
|
||||
|
||||
|
||||
def get_channel_list(self):
|
||||
"""
|
||||
Returns a sorted list of cached channels.
|
||||
|
|
@ -83,7 +83,7 @@ class IMC2ChanList(object):
|
|||
channels = self.chan_list.items()
|
||||
channels.sort()
|
||||
return [value for key, value in channels]
|
||||
|
||||
|
||||
def update_channel_from_packet(self, packet):
|
||||
"""
|
||||
This grabs relevant info from the packet and stuffs it in the
|
||||
|
|
@ -91,7 +91,7 @@ class IMC2ChanList(object):
|
|||
"""
|
||||
channel = IMC2Channel(packet)
|
||||
self.chan_list[channel.name] = channel
|
||||
|
||||
|
||||
def remove_channel_from_packet(self, packet):
|
||||
"""
|
||||
Removes a channel from the Channel list when given a packet.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ from src.comms.models import ExternalChannelConnection, Channel
|
|||
from src.utils import logger, utils
|
||||
from src.server.sessionhandler import SESSIONS
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
INFOCHANNEL = Channel.objects.channel_search(settings.CHANNEL_MUDINFO[0])
|
||||
IRC_CHANNELS = []
|
||||
|
||||
|
|
@ -27,77 +29,77 @@ def msg_info(message):
|
|||
class IRC_Bot(irc.IRCClient):
|
||||
"""
|
||||
This defines an IRC bot that connects to an IRC channel
|
||||
and relays data to and from an evennia game.
|
||||
and relays data to and from an evennia game.
|
||||
"""
|
||||
|
||||
|
||||
def _get_nickname(self):
|
||||
"required for correct nickname setting"
|
||||
return self.factory.nickname
|
||||
nickname = property(_get_nickname)
|
||||
|
||||
def signedOn(self):
|
||||
|
||||
def signedOn(self):
|
||||
# This is the first point the protocol is instantiated.
|
||||
# add this protocol instance to the global list so we
|
||||
# can access it later to send data.
|
||||
# can access it later to send data.
|
||||
global IRC_CHANNELS
|
||||
self.join(self.factory.channel)
|
||||
|
||||
IRC_CHANNELS.append(self)
|
||||
IRC_CHANNELS.append(self)
|
||||
#msg_info("Client connecting to %s.'" % (self.factory.channel))
|
||||
|
||||
|
||||
def joined(self, channel):
|
||||
msg = "joined %s." % self.factory.pretty_key
|
||||
msg = _("joined %s.") % self.factory.pretty_key
|
||||
msg_info(msg)
|
||||
logger.log_infomsg(msg)
|
||||
|
||||
def privmsg(self, user, irc_channel, msg):
|
||||
"Someone has written something in irc channel. Echo it to the evennia channel"
|
||||
#find irc->evennia channel mappings
|
||||
"Someone has written something in irc channel. Echo it to the evennia channel"
|
||||
#find irc->evennia channel mappings
|
||||
conns = ExternalChannelConnection.objects.filter(db_external_key=self.factory.key)
|
||||
if not conns:
|
||||
return
|
||||
#format message:
|
||||
return
|
||||
#format message:
|
||||
user = user.split("!")[0]
|
||||
if user:
|
||||
user.strip()
|
||||
else:
|
||||
user = "Unknown"
|
||||
user = _("Unknown")
|
||||
msg = "[%s] %s@%s: %s" % (self.factory.evennia_channel, user, irc_channel, msg.strip())
|
||||
#logger.log_infomsg("<IRC: " + msg)
|
||||
#logger.log_infomsg("<IRC: " + msg)
|
||||
for conn in conns:
|
||||
if conn.channel:
|
||||
conn.to_channel(msg)
|
||||
|
||||
|
||||
def msg_irc(self, msg, from_obj=None):
|
||||
"""
|
||||
Called by evennia when sending something to mapped IRC channel.
|
||||
Called by evennia when sending something to mapped IRC channel.
|
||||
|
||||
Note that this cannot simply be called msg() since that's the
|
||||
name of of the twisted irc hook as well, this leads to some
|
||||
initialization messages to be sent without checks, causing loops.
|
||||
name of of the twisted irc hook as well, this leads to some
|
||||
initialization messages to be sent without checks, causing loops.
|
||||
"""
|
||||
self.msg(utils.to_str(self.factory.channel), utils.to_str(msg))
|
||||
|
||||
|
||||
class IRCbotFactory(protocol.ClientFactory):
|
||||
protocol = IRC_Bot
|
||||
def __init__(self, key, channel, network, port, nickname, evennia_channel):
|
||||
self.key = key
|
||||
self.pretty_key = "%s:%s%s ('%s')" % (network, port, channel, nickname)
|
||||
self.pretty_key = "%s:%s%s ('%s')" % (network, port, channel, nickname)
|
||||
self.network = network
|
||||
self.port = port
|
||||
self.port = port
|
||||
self.channel = channel
|
||||
self.nickname = nickname
|
||||
self.evennia_channel = evennia_channel
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
from twisted.internet.error import ConnectionDone
|
||||
if type(reason.type) == type(ConnectionDone):
|
||||
msg_info("Connection closed.")
|
||||
msg_info(_("Connection closed."))
|
||||
else:
|
||||
msg_info("Lost connection %s. Reason: '%s'. Reconnecting." % (self.pretty_key, reason))
|
||||
msg_info(_("Lost connection %(key)s. Reason: '%(reason)s'. Reconnecting.") % {"key":self.pretty_key, "reason":reason})
|
||||
connector.connect()
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
msg = "Could not connect %s Reason: '%s'" % (self.pretty_key, reason)
|
||||
msg = _("Could not connect %(key)s Reason: '%(reason)s'") % {"key":self.pretty_key, "reason":reason}
|
||||
msg_info(msg)
|
||||
logger.log_errmsg(msg)
|
||||
|
||||
|
|
@ -112,70 +114,70 @@ def build_service_key(key):
|
|||
|
||||
def create_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
|
||||
"""
|
||||
This will create a new IRC<->channel connection.
|
||||
This will create a new IRC<->channel connection.
|
||||
"""
|
||||
if not type(channel) == Channel:
|
||||
new_channel = Channel.objects.filter(db_key=channel)
|
||||
if not new_channel:
|
||||
logger.log_errmsg("Cannot attach IRC<->Evennia: Evennia Channel '%s' not found" % channel)
|
||||
logger.log_errmsg(_("Cannot attach IRC<->Evennia: Evennia Channel '%s' not found") % channel)
|
||||
return False
|
||||
channel = new_channel[0]
|
||||
key = build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick)
|
||||
|
||||
old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
|
||||
if old_conns:
|
||||
return False
|
||||
return False
|
||||
config = "%s|%s|%s|%s" % (irc_network, irc_port, irc_channel, irc_bot_nick)
|
||||
# how the channel will be able to contact this protocol
|
||||
send_code = "from src.comms.irc import IRC_CHANNELS\n"
|
||||
send_code = "from src.comms.irc import IRC_CHANNELS\n"
|
||||
send_code += "matched_ircs = [irc for irc in IRC_CHANNELS if irc.factory.key == '%s']\n" % key
|
||||
send_code += "[irc.msg_irc(message, from_obj=from_obj) for irc in matched_ircs]\n"
|
||||
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
|
||||
db_external_config=config)
|
||||
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
|
||||
db_external_config=config)
|
||||
conn.save()
|
||||
|
||||
# connect
|
||||
# connect
|
||||
connect_to_irc(conn)
|
||||
return True
|
||||
return True
|
||||
|
||||
def delete_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
|
||||
"Destroy a connection"
|
||||
if hasattr(channel, 'key'):
|
||||
channel = channel.key
|
||||
|
||||
key = build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick)
|
||||
key = build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick)
|
||||
service_key = build_service_key(key)
|
||||
try:
|
||||
conn = ExternalChannelConnection.objects.get(db_external_key=key)
|
||||
except Exception:
|
||||
return False
|
||||
conn.delete()
|
||||
return False
|
||||
conn.delete()
|
||||
|
||||
try:
|
||||
service = SESSIONS.server.services.getServiceNamed(service_key)
|
||||
except Exception:
|
||||
return True
|
||||
if service.running:
|
||||
return True
|
||||
if service.running:
|
||||
SESSIONS.server.services.removeService(service)
|
||||
return True
|
||||
return True
|
||||
|
||||
def connect_to_irc(connection):
|
||||
"Create the bot instance and connect to the IRC network and channel."
|
||||
"Create the bot instance and connect to the IRC network and channel."
|
||||
# get config
|
||||
key = utils.to_str(connection.external_key)
|
||||
service_key = build_service_key(key)
|
||||
irc_network, irc_port, irc_channel, irc_bot_nick = [utils.to_str(conf) for conf in connection.external_config.split('|')]
|
||||
# connect
|
||||
bot = internet.TCPClient(irc_network, int(irc_port), IRCbotFactory(key, irc_channel, irc_network, irc_port, irc_bot_nick,
|
||||
# connect
|
||||
bot = internet.TCPClient(irc_network, int(irc_port), IRCbotFactory(key, irc_channel, irc_network, irc_port, irc_bot_nick,
|
||||
connection.channel.key))
|
||||
bot.setName(service_key)
|
||||
SESSIONS.server.services.addService(bot)
|
||||
|
||||
def connect_all():
|
||||
"""
|
||||
Activate all irc bots.
|
||||
Activate all irc bots.
|
||||
"""
|
||||
for connection in ExternalChannelConnection.objects.filter(db_external_key__startswith='irc_'):
|
||||
connect_to_irc(connection)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
These managers handles the
|
||||
These managers handles the
|
||||
"""
|
||||
|
||||
import itertools
|
||||
|
|
@ -18,7 +18,7 @@ def to_object(inp, objtype='player'):
|
|||
Locates the object related to the given
|
||||
playername or channel key. If input was already
|
||||
the correct object, return it.
|
||||
inp - the input object/string
|
||||
inp - the input object/string
|
||||
objtype - 'player' or 'channel'
|
||||
"""
|
||||
from src.players.models import PlayerDB
|
||||
|
|
@ -30,43 +30,43 @@ def to_object(inp, objtype='player'):
|
|||
else:
|
||||
umatch = PlayerDB.objects.filter(user__username__iexact=inp)
|
||||
if umatch:
|
||||
return umatch[0]
|
||||
return umatch[0]
|
||||
elif objtype == 'external':
|
||||
from src.comms.models import ExternalChannelConnection
|
||||
if type (inp) == ExternalChannelConnection:
|
||||
return inp
|
||||
umatch = ExternalChannelConnection.objects.filter(db_key=inp)
|
||||
if umatch:
|
||||
return umatch[0]
|
||||
return umatch[0]
|
||||
else:
|
||||
# have to import this way to avoid circular imports
|
||||
from src.comms.models import Channel
|
||||
#= ContentType.objects.get(app_label="comms",
|
||||
from src.comms.models import Channel
|
||||
#= ContentType.objects.get(app_label="comms",
|
||||
# model="channel").model_class()
|
||||
if type(inp) == Channel:
|
||||
return inp
|
||||
cmatch = Channel.objects.filter(db_key__iexact=inp)
|
||||
if cmatch:
|
||||
return cmatch[0]
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
#
|
||||
# Msg manager
|
||||
#
|
||||
|
||||
class MsgManager(models.Manager):
|
||||
"""
|
||||
This MsgManager implements methods for searching
|
||||
This MsgManager implements methods for searching
|
||||
and manipulating Messages directly from the database.
|
||||
|
||||
These methods will all return database objects
|
||||
These methods will all return database objects
|
||||
(or QuerySets) directly.
|
||||
|
||||
A Message represents one unit of communication, be it over a
|
||||
A Message represents one unit of communication, be it over a
|
||||
Channel or via some form of in-game mail system. Like an e-mail,
|
||||
it always has a sender and can have any number of receivers (some
|
||||
of which may be Channels).
|
||||
|
||||
of which may be Channels).
|
||||
|
||||
Evennia-specific:
|
||||
get_message_by_id
|
||||
get_messages_by_sender
|
||||
|
|
@ -75,22 +75,22 @@ class MsgManager(models.Manager):
|
|||
text_search
|
||||
message_search (equivalent to ev.search_messages)
|
||||
"""
|
||||
|
||||
|
||||
def get_message_by_id(self, idnum):
|
||||
"Retrieve message by its id."
|
||||
"Retrieve message by its id."
|
||||
try:
|
||||
idnum = int(idnum)
|
||||
return self.get(id=id)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def get_messages_by_sender(self, player):
|
||||
"""
|
||||
Get all messages sent by one player
|
||||
"""
|
||||
player = to_object(player, objtype='player')
|
||||
player = to_object(player, objtype='player')
|
||||
if not player:
|
||||
return None
|
||||
return None
|
||||
return self.filter(db_sender=player).exclude(db_hide_from_sender=True)
|
||||
|
||||
def get_messages_by_receiver(self, receiver):
|
||||
|
|
@ -99,7 +99,7 @@ class MsgManager(models.Manager):
|
|||
"""
|
||||
receiver = to_object(receiver)
|
||||
if not receiver:
|
||||
return None
|
||||
return None
|
||||
return [msg for msg in self.all()
|
||||
if receiver in msg.receivers
|
||||
and receiver not in msg.hide_from_receivers]
|
||||
|
|
@ -110,30 +110,30 @@ class MsgManager(models.Manager):
|
|||
"""
|
||||
channel = to_object(channel, objtype='channel')
|
||||
if not channel:
|
||||
return None
|
||||
return None
|
||||
return [msg for msg in self.all()
|
||||
if channel in msg.channels
|
||||
if channel in msg.channels
|
||||
and channel not in msg.hide_from_channels]
|
||||
|
||||
#TODO add search limited by send_times
|
||||
#TODO add search limited by send_times
|
||||
def text_search(self, searchstring, filterdict=None):
|
||||
"""
|
||||
Returns all messages that contain the matching
|
||||
search string. To avoid too many results, and also
|
||||
since this can be a very computing-
|
||||
heavy operation, it's recommended to be filtered
|
||||
by at least channel or sender/receiver.
|
||||
by at least channel or sender/receiver.
|
||||
searchstring - string to search for
|
||||
filterdict -
|
||||
{'channels':[list],
|
||||
'senders':[list],
|
||||
'receivers':[list]}
|
||||
lists can contain either the name/keys of the
|
||||
objects or the actual objects to filter by.
|
||||
"""
|
||||
objects or the actual objects to filter by.
|
||||
"""
|
||||
|
||||
if filterdict:
|
||||
# obtain valid objects for all filters
|
||||
# obtain valid objects for all filters
|
||||
channels = [chan for chan in
|
||||
[to_object(chan, objtype='channel')
|
||||
for chan in filterdict.get('channels',[])]
|
||||
|
|
@ -141,23 +141,23 @@ class MsgManager(models.Manager):
|
|||
senders = [sender for sender in
|
||||
[to_object(sender)
|
||||
for sender in filterdict.get('senders',[])]
|
||||
if sender]
|
||||
if sender]
|
||||
receivers = [receiver for receiver in
|
||||
[to_object(receiver)
|
||||
for receiver in filterdict.get('receivers',[])]
|
||||
if receiver]
|
||||
# filter the messages lazily using the filter objects
|
||||
# filter the messages lazily using the filter objects
|
||||
msgs = []
|
||||
for sender in senders:
|
||||
msgs = list(sender.message_set.filter(
|
||||
db_message__icontains=searchstring))
|
||||
db_message__icontains=searchstring))
|
||||
for receiver in receivers:
|
||||
rec_msgs = receiver.message_set.filter(
|
||||
db_message__icontains=searchstring)
|
||||
if msgs:
|
||||
msgs = [msg for msg in rec_msgs if msg in msgs]
|
||||
else:
|
||||
msgs = rec_msgs
|
||||
msgs = rec_msgs
|
||||
for channel in channels:
|
||||
chan_msgs = list(channel.message_set.filter(
|
||||
db_message__icontains=searchstring))
|
||||
|
|
@ -168,21 +168,21 @@ class MsgManager(models.Manager):
|
|||
return list(set(msgs))
|
||||
return list(self.all().filter(db_message__icontains=searchstring))
|
||||
|
||||
def message_search(self, sender=None, receiver=None, channel=None, freetext=None, dbref=None):
|
||||
def message_search(self, sender=None, receiver=None, channel=None, freetext=None, dbref=None):
|
||||
"""
|
||||
Search the message database for particular messages. At least one
|
||||
of the arguments must be given to do a search.
|
||||
Search the message database for particular messages. At least one
|
||||
of the arguments must be given to do a search.
|
||||
|
||||
sender - get messages sent by a particular player
|
||||
receiver - get messages received by a certain player or players
|
||||
channel - get messages sent to a particular channel or channels
|
||||
freetext - Search for a text string in a message.
|
||||
freetext - Search for a text string in a message.
|
||||
NOTE: This can potentially be slow, so make sure to supply
|
||||
one of the other arguments to limit the search.
|
||||
dbref - (int) the exact database id of the message. This will override
|
||||
one of the other arguments to limit the search.
|
||||
dbref - (int) the exact database id of the message. This will override
|
||||
all other search crieteria since it's unique and
|
||||
always gives a list with only one match.
|
||||
"""
|
||||
"""
|
||||
if dbref:
|
||||
return self.filter(id=dbref)
|
||||
if freetext:
|
||||
|
|
@ -199,12 +199,12 @@ class MsgManager(models.Manager):
|
|||
msgs = []
|
||||
if sender:
|
||||
msgs = self.get_messages_by_sender(sender)
|
||||
if receiver:
|
||||
if receiver:
|
||||
rec_msgs = self.get_messages_by_receiver(receiver)
|
||||
if msgs:
|
||||
msgs = [msg for msg in rec_msgs if msg in msgs]
|
||||
else:
|
||||
msgs = rec_msgs
|
||||
msgs = rec_msgs
|
||||
if channel:
|
||||
chan_msgs = self.get_messaqge_by_channel(channel)
|
||||
if msgs:
|
||||
|
|
@ -212,20 +212,20 @@ class MsgManager(models.Manager):
|
|||
else:
|
||||
msgs = chan_msgs
|
||||
return msgs
|
||||
|
||||
|
||||
#
|
||||
# Channel manager
|
||||
#
|
||||
|
||||
class ChannelManager(models.Manager):
|
||||
"""
|
||||
This ChannelManager implements methods for searching
|
||||
This ChannelManager implements methods for searching
|
||||
and manipulating Channels directly from the database.
|
||||
|
||||
These methods will all return database objects
|
||||
These methods will all return database objects
|
||||
(or QuerySets) directly.
|
||||
|
||||
A Channel is an in-game venue for communication. It's
|
||||
A Channel is an in-game venue for communication. It's
|
||||
essentially representation of a re-sender: Users sends
|
||||
Messages to the Channel, and the Channel re-sends those
|
||||
messages to all users subscribed to the Channel.
|
||||
|
|
@ -248,9 +248,9 @@ class ChannelManager(models.Manager):
|
|||
def get_channel(self, channelkey):
|
||||
"""
|
||||
Return the channel object if given its key.
|
||||
Also searches its aliases.
|
||||
Also searches its aliases.
|
||||
"""
|
||||
# first check the channel key
|
||||
# first check the channel key
|
||||
channels = self.filter(db_key__iexact=channelkey)
|
||||
if not channels:
|
||||
# also check aliases
|
||||
|
|
@ -258,23 +258,23 @@ class ChannelManager(models.Manager):
|
|||
if channelkey in channel.aliases]
|
||||
if channels:
|
||||
return channels[0]
|
||||
return None
|
||||
return None
|
||||
|
||||
def del_channel(self, channelkey):
|
||||
"""
|
||||
Delete channel matching channelkey.
|
||||
Also cleans up channelhandler.
|
||||
Also cleans up channelhandler.
|
||||
"""
|
||||
channels = self.filter(db_key__iexact=channelkey)
|
||||
if not channels:
|
||||
# no aliases allowed for deletion.
|
||||
return False
|
||||
return False
|
||||
for channel in channels:
|
||||
channel.delete()
|
||||
channel.delete()
|
||||
from src.comms.channelhandler import CHANNELHANDLER
|
||||
CHANNELHANDLER.update()
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
def get_all_connections(self, channel):
|
||||
"""
|
||||
Return the connections of all players listening
|
||||
|
|
@ -282,9 +282,9 @@ class ChannelManager(models.Manager):
|
|||
"""
|
||||
# import here to avoid circular imports
|
||||
#from src.comms.models import PlayerChannelConnection
|
||||
PlayerChannelConnection = ContentType.objects.get(app_label="comms",
|
||||
PlayerChannelConnection = ContentType.objects.get(app_label="comms",
|
||||
model="playerchannelconnection").model_class()
|
||||
ExternalChannelConnection = ContentType.objects.get(app_label="comms",
|
||||
ExternalChannelConnection = ContentType.objects.get(app_label="comms",
|
||||
model="externalchannelconnection").model_class()
|
||||
return itertools.chain(PlayerChannelConnection.objects.get_all_connections(channel),
|
||||
ExternalChannelConnection.objects.get_all_connections(channel))
|
||||
|
|
@ -297,10 +297,10 @@ class ChannelManager(models.Manager):
|
|||
"""
|
||||
channels = []
|
||||
try:
|
||||
# try an id match first
|
||||
# try an id match first
|
||||
dbref = int(ostring.strip('#'))
|
||||
channels = self.filter(id=dbref)
|
||||
except Exception:
|
||||
except Exception:
|
||||
pass
|
||||
if not channels:
|
||||
# no id match. Search on the key.
|
||||
|
|
@ -308,17 +308,17 @@ class ChannelManager(models.Manager):
|
|||
if not channels:
|
||||
# still no match. Search by alias.
|
||||
channels = [channel for channel in self.all() if ostring.lower in [a.lower for a in channel.aliases]]
|
||||
return channels
|
||||
return channels
|
||||
|
||||
#
|
||||
# PlayerChannelConnection manager
|
||||
#
|
||||
class PlayerChannelConnectionManager(models.Manager):
|
||||
"""
|
||||
This PlayerChannelConnectionManager implements methods for searching
|
||||
This PlayerChannelConnectionManager implements methods for searching
|
||||
and manipulating PlayerChannelConnections directly from the database.
|
||||
|
||||
These methods will all return database objects
|
||||
These methods will all return database objects
|
||||
(or QuerySets) directly.
|
||||
|
||||
A PlayerChannelConnection defines a user's subscription to an in-game
|
||||
|
|
@ -333,7 +333,7 @@ class PlayerChannelConnectionManager(models.Manager):
|
|||
break_connection
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def get_all_player_connections(self, player):
|
||||
"Get all connections that the given player has."
|
||||
player = to_object(player)
|
||||
|
|
@ -346,18 +346,18 @@ class PlayerChannelConnectionManager(models.Manager):
|
|||
if player and channel:
|
||||
return self.filter(db_player=player).filter(db_channel=channel).count() > 0
|
||||
return False
|
||||
|
||||
|
||||
def get_all_connections(self, channel):
|
||||
"""
|
||||
Get all connections for a channel
|
||||
"""
|
||||
channel = to_object(channel, objtype='channel')
|
||||
return self.filter(db_channel=channel)
|
||||
|
||||
|
||||
def create_connection(self, player, channel):
|
||||
"""
|
||||
Connect a player to a channel. player and channel
|
||||
can be actual objects or keystrings.
|
||||
can be actual objects or keystrings.
|
||||
"""
|
||||
player = to_object(player)
|
||||
channel = to_object(channel, objtype='channel')
|
||||
|
|
@ -379,13 +379,13 @@ class PlayerChannelConnectionManager(models.Manager):
|
|||
|
||||
class ExternalChannelConnectionManager(models.Manager):
|
||||
"""
|
||||
This ExternalChannelConnectionManager implements methods for searching
|
||||
This ExternalChannelConnectionManager implements methods for searching
|
||||
and manipulating HelpEntries directly from the database.
|
||||
|
||||
These methods will all return database objects
|
||||
These methods will all return database objects
|
||||
(or QuerySets) directly.
|
||||
|
||||
An ExternalChannelConnetion describes the connection between an in-game
|
||||
An ExternalChannelConnetion describes the connection between an in-game
|
||||
channel and some external source, such as an IRC or IMC channel.
|
||||
|
||||
Evennia-specific:
|
||||
|
|
@ -394,9 +394,9 @@ class ExternalChannelConnectionManager(models.Manager):
|
|||
get_all_connections
|
||||
create_connection
|
||||
break_connection
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def get_all_external_connections(self, external):
|
||||
"Get all connections that the given as external."
|
||||
external = to_object(external, objtype='external')
|
||||
|
|
@ -409,18 +409,18 @@ class ExternalChannelConnectionManager(models.Manager):
|
|||
if external and channel:
|
||||
return self.filter(db_external_key=external).filter(db_channel=channel).count() > 0
|
||||
return False
|
||||
|
||||
|
||||
def get_all_connections(self, channel):
|
||||
"""
|
||||
Get all connections for a channel
|
||||
"""
|
||||
channel = to_object(channel, objtype='channel')
|
||||
return self.filter(db_channel=channel)
|
||||
|
||||
|
||||
def create_connection(self, external, channel, config=""):
|
||||
"""
|
||||
Connect a external to a channel. external and channel
|
||||
can be actual objects or keystrings.
|
||||
can be actual objects or keystrings.
|
||||
"""
|
||||
channel = to_object(channel, objtype='channel')
|
||||
if not channel:
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
"""
|
||||
RSS parser for Evennia
|
||||
|
||||
This connects an RSS feed to an in-game Evennia channel, sending messages
|
||||
to the channel whenever the feed updates.
|
||||
This connects an RSS feed to an in-game Evennia channel, sending messages
|
||||
to the channel whenever the feed updates.
|
||||
|
||||
"""
|
||||
|
||||
import re
|
||||
from twisted.internet import task
|
||||
from django.conf import settings
|
||||
from src.comms.models import ExternalChannelConnection, Channel
|
||||
from django.conf import settings
|
||||
from src.comms.models import ExternalChannelConnection, Channel
|
||||
from src.utils import logger, utils
|
||||
from src.scripts.models import ScriptDB
|
||||
|
||||
RSS_ENABLED = settings.RSS_ENABLED
|
||||
RSS_ENABLED = settings.RSS_ENABLED
|
||||
RSS_UPDATE_INTERVAL = settings.RSS_UPDATE_INTERVAL
|
||||
INFOCHANNEL = Channel.objects.channel_search(settings.CHANNEL_MUDINFO[0])
|
||||
RETAG = re.compile(r'<[^>]*?>')
|
||||
|
||||
# holds rss readers they can be shut down at will.
|
||||
# holds rss readers they can be shut down at will.
|
||||
RSS_READERS = {}
|
||||
|
||||
def msg_info(message):
|
||||
|
|
@ -35,8 +35,8 @@ if RSS_ENABLED:
|
|||
try:
|
||||
import feedparser
|
||||
except ImportError:
|
||||
raise ImportError("RSS requirs python-feedparser to be installed. Install or set RSS_ENABLED=False.")
|
||||
|
||||
raise ImportError("RSS requires python-feedparser to be installed. Install or set RSS_ENABLED=False.")
|
||||
|
||||
class RSSReader(object):
|
||||
"""
|
||||
Reader script used to connect to each individual RSS feed
|
||||
|
|
@ -45,47 +45,47 @@ class RSSReader(object):
|
|||
"""
|
||||
The reader needs an rss url and It also needs an interval
|
||||
for how often it is to check for new updates (defaults
|
||||
to 10 minutes)
|
||||
to 10 minutes)
|
||||
"""
|
||||
self.key = key
|
||||
self.url = url
|
||||
self.interval = interval
|
||||
self.url = url
|
||||
self.interval = interval
|
||||
self.entries = {} # stored feeds
|
||||
self.task = None
|
||||
# first we do is to load the feed so we don't resend
|
||||
# old entries whenever the reader starts.
|
||||
self.update_feed()
|
||||
self.task = None
|
||||
# first we do is to load the feed so we don't resend
|
||||
# old entries whenever the reader starts.
|
||||
self.update_feed()
|
||||
# start runner
|
||||
self.start()
|
||||
|
||||
|
||||
def update_feed(self):
|
||||
"Read the url for new updated data and determine what's new."
|
||||
feed = feedparser.parse(self.url)
|
||||
new = []
|
||||
for entry in (e for e in feed['entries'] if e['id'] not in self.entries):
|
||||
txt = "[RSS] %s: %s" % (RETAG.sub("", entry['title']), entry['link'].replace('\n','').encode('utf-8'))
|
||||
txt = "[RSS] %s: %s" % (RETAG.sub("", entry['title']), entry['link'].replace('\n','').encode('utf-8'))
|
||||
self.entries[entry['id']] = txt
|
||||
new.append(txt)
|
||||
return new
|
||||
new.append(txt)
|
||||
return new
|
||||
|
||||
def update(self):
|
||||
"""
|
||||
Called every self.interval seconds - tries to get new feed entries,
|
||||
and if so, uses the appropriate ExternalChannelConnection to send the
|
||||
data to subscribing channels.
|
||||
data to subscribing channels.
|
||||
"""
|
||||
new = self.update_feed()
|
||||
if not new:
|
||||
return
|
||||
conns = ExternalChannelConnection.objects.filter(db_external_key=self.key)
|
||||
for conn in (conn for conn in conns if conn.channel):
|
||||
for txt in new:
|
||||
if not new:
|
||||
return
|
||||
conns = ExternalChannelConnection.objects.filter(db_external_key=self.key)
|
||||
for conn in (conn for conn in conns if conn.channel):
|
||||
for txt in new:
|
||||
conn.to_channel("%s:%s" % (conn.channel.key, txt))
|
||||
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Starting the update task and store a reference in the
|
||||
global variable so it can be found and shut down later.
|
||||
Starting the update task and store a reference in the
|
||||
global variable so it can be found and shut down later.
|
||||
"""
|
||||
global RSS_READERS
|
||||
self.task = task.LoopingCall(self.update)
|
||||
|
|
@ -95,7 +95,7 @@ class RSSReader(object):
|
|||
def build_connection_key(channel, url):
|
||||
"This is used to id the connection"
|
||||
if hasattr(channel, 'key'):
|
||||
channel = channel.key
|
||||
channel = channel.key
|
||||
return "rss_%s>%s" % (url, channel)
|
||||
|
||||
def create_connection(channel, url, interval):
|
||||
|
|
@ -106,19 +106,19 @@ def create_connection(channel, url, interval):
|
|||
new_channel = Channel.objects.filter(db_key=channel)
|
||||
if not new_channel:
|
||||
logger.log_errmsg("Cannot attach RSS->Evennia: Evennia Channel '%s' not found." % channel)
|
||||
return False
|
||||
return False
|
||||
channel = new_channel[0]
|
||||
key = build_connection_key(channel, url)
|
||||
old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
|
||||
if old_conns:
|
||||
return False
|
||||
return False
|
||||
config = "%s|%i" % (url, interval)
|
||||
# There is no sendback from evennia to the rss, so we need not define any sendback code.
|
||||
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_config=config)
|
||||
conn.save()
|
||||
conn.save()
|
||||
|
||||
connect_to_rss(conn)
|
||||
return True
|
||||
return True
|
||||
|
||||
def delete_connection(channel, url):
|
||||
"""
|
||||
|
|
@ -128,29 +128,29 @@ def delete_connection(channel, url):
|
|||
try:
|
||||
conn = ExternalChannelConnection.objects.get(db_external_key=key)
|
||||
except Exception:
|
||||
return False
|
||||
return False
|
||||
conn.delete()
|
||||
reader = RSS_READERS.get(key, None)
|
||||
if reader and reader.task:
|
||||
if reader and reader.task:
|
||||
reader.task.stop()
|
||||
return True
|
||||
|
||||
return True
|
||||
|
||||
def connect_to_rss(connection):
|
||||
"""
|
||||
Create the parser instance and connect to RSS feed and channel
|
||||
"""
|
||||
global RSS_READERS
|
||||
key = utils.to_str(connection.external_key)
|
||||
key = utils.to_str(connection.external_key)
|
||||
url, interval = [utils.to_str(conf) for conf in connection.external_config.split('|')]
|
||||
# Create reader (this starts the running task and stores a reference in RSS_TASKS)
|
||||
RSSReader(key, url, int(interval))
|
||||
|
||||
def connect_all():
|
||||
"""
|
||||
Activate all rss feed parsers
|
||||
Activate all rss feed parsers
|
||||
"""
|
||||
if not RSS_ENABLED:
|
||||
return
|
||||
return
|
||||
for connection in ExternalChannelConnection.objects.filter(db_external_key__startswith="rss_"):
|
||||
print "connecting RSS: %s" % connection
|
||||
connect_to_rss(connection)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue