Remove broken and un-maintained IMC2 support.

As discussed with @Griatch, IMC2 is now rarely seen in the wild. Also, this
feature hasn't worked in ages. Time to de-clutter.
This commit is contained in:
Greg Taylor 2016-10-28 22:53:43 -07:00
parent 0554492b79
commit 4424dec668
20 changed files with 9 additions and 1786 deletions

View file

@ -70,6 +70,3 @@ class PlayerCmdSet(CmdSet):
self.add(comms.CmdPage())
self.add(comms.CmdIRC2Chan())
self.add(comms.CmdRSS2Chan())
#self.add(comms.CmdIMC2Chan())
#self.add(comms.CmdIMCInfo())
#self.add(comms.CmdIMCTell())

View file

@ -10,7 +10,6 @@ for easy handling.
from past.builtins import cmp
from django.conf import settings
from evennia.comms.models import ChannelDB, Msg
#from evennia.comms import irc, imc2, rss
from evennia.players.models import PlayerDB
from evennia.players import bots
from evennia.comms.channelhandler import CHANNELHANDLER
@ -24,8 +23,7 @@ COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
__all__ = ("CmdAddCom", "CmdDelCom", "CmdAllCom",
"CmdChannels", "CmdCdestroy", "CmdCBoot", "CmdCemit",
"CmdCWho", "CmdChannelCreate", "CmdClock", "CmdCdesc",
"CmdPage", "CmdIRC2Chan", "CmdRSS2Chan")#, "CmdIMC2Chan", "CmdIMCInfo",
#"CmdIMCTell")
"CmdPage", "CmdIRC2Chan", "CmdRSS2Chan")
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
@ -995,224 +993,3 @@ class CmdRSS2Chan(COMMAND_DEFAULT_CLASS):
bot = create.create_player(botname, None, None, typeclass=bots.RSSBot)
bot.start(ev_channel=channel, rss_url=url, rss_rate=10)
self.msg("RSS reporter created. Fetching RSS.")
#class CmdIMC2Chan(COMMAND_DEFAULT_CLASS):
# """
# link an evennia channel to an external IMC2 channel
#
# Usage:
# @imc2chan[/switches] <evennia_channel> = <imc2_channel>
#
# Switches:
# /disconnect - this clear the imc2 connection to the channel.
# /remove - "
# /list - show all imc2<->evennia mappings
#
# Example:
# @imc2chan myimcchan = ievennia
#
# Connect an existing evennia channel to a channel on an IMC2
# network. The network contact information is defined in settings and
# should already be accessed at this point. Use @imcchanlist to see
# available IMC channels.
#
# """
#
# key = "@imc2chan"
# locks = "cmd:serversetting(IMC2_ENABLED) and pperm(Immortals)"
# help_category = "Comms"
#
# def func(self):
# "Setup the imc-channel mapping"
#
# if not settings.IMC2_ENABLED:
# string = """IMC is not enabled. You need to activate it in game/settings.py."""
# self.msg(string)
# return
#
# if 'list' in self.switches:
# # show all connections
# connections = ExternalChannelConnection.objects.filter(db_external_key__startswith='imc2_')
# if connections:
# table = prettytable.PrettyTable(["Evennia channel", "IMC channel"])
# for conn in connections:
# table.add_row([conn.channel.key, conn.external_config])
# string = "{wIMC connections:{n\n%s" % table
# self.msg(string)
# else:
# self.msg("No connections found.")
# return
#
# if not self.args or not self.rhs:
# string = "Usage: @imc2chan[/switches] <evennia_channel> = <imc2_channel>"
# self.msg(string)
# return
#
# channel = self.lhs
# imc2_channel = self.rhs
#
# if('disconnect' in self.switches or 'remove' in self.switches or
# 'delete' in self.switches):
# # we don't search for channels before this since we want
# # to clear the link also if the channel no longer exists.
# ok = imc2.delete_connection(channel, imc2_channel)
# if not ok:
# self.msg("IMC2 connection could not be removed, does it exist?")
# else:
# self.msg("IMC2 connection destroyed.")
# return
#
# # actually get the channel object
# channel = find_channel(self.caller, channel)
# if not channel:
# return
#
# ok = imc2.create_connection(channel, imc2_channel)
# if not ok:
# self.msg("The connection %s <-> %s already exists." % (channel.key, imc2_channel))
# return
# self.msg("Created connection channel %s <-> IMC channel %s." % (channel.key, imc2_channel))
#
#
#class CmdIMCInfo(COMMAND_DEFAULT_CLASS):
# """
# get various IMC2 information
#
# Usage:
# @imcinfo[/switches]
# @imcchanlist - list imc2 channels
# @imclist - list connected muds
# @imcwhois <playername> - whois info about a remote player
#
# Switches for @imcinfo:
# channels - as @imcchanlist (default)
# games or muds - as @imclist
# whois - as @imcwhois (requires an additional argument)
# update - force an update of all lists
#
# Shows lists of games or channels on the IMC2 network.
# """
#
# key = "@imcinfo"
# aliases = ["@imcchanlist", "@imclist", "@imcwhois"]
# locks = "cmd: serversetting(IMC2_ENABLED) and pperm(Wizards)"
# help_category = "Comms"
#
# def func(self):
# "Run the command"
#
# if not settings.IMC2_ENABLED:
# string = """IMC is not enabled. You need to activate it in game/settings.py."""
# self.msg(string)
# return
#
# if "update" in self.switches:
# # update the lists
# import time
# from evennia.comms.imc2lib import imc2_packets as pck
# from evennia.comms.imc2 import IMC2_MUDLIST, IMC2_CHANLIST, IMC2_CLIENT
# # update connected muds
# IMC2_CLIENT.send_packet(pck.IMC2PacketKeepAliveRequest())
# # prune inactive muds
# for name, mudinfo in IMC2_MUDLIST.mud_list.items():
# if time.time() - mudinfo.last_updated > 3599:
# del IMC2_MUDLIST.mud_list[name]
# # update channel list
# IMC2_CLIENT.send_packet(pck.IMC2PacketIceRefresh())
# self.msg("IMC2 lists were re-synced.")
#
# elif("games" in self.switches or "muds" in self.switches
# or self.cmdstring == "@imclist"):
# # list muds
# from evennia.comms.imc2 import IMC2_MUDLIST
#
# muds = IMC2_MUDLIST.get_mud_list()
# networks = set(mud.networkname for mud in muds)
# string = ""
# nmuds = 0
# for network in networks:
# table = prettytable.PrettyTable(["Name", "Url", "Host", "Port"])
# for mud in (mud for mud in muds if mud.networkname == network):
# nmuds += 1
# table.add_row([mud.name, mud.url, mud.host, mud.port])
# string += "\n{wMuds registered on %s:{n\n%s" % (network, table)
# string += "\n %i Muds found." % nmuds
# self.msg(string)
#
# elif "whois" in self.switches or self.cmdstring == "@imcwhois":
# # find out about a player
# if not self.args:
# self.msg("Usage: @imcwhois <playername>")
# return
# from evennia.comms.imc2 import IMC2_CLIENT
# self.msg("Sending IMC whois request. If you receive no response, no matches were found.")
# IMC2_CLIENT.msg_imc2(None,
# from_obj=self.caller,
# packet_type="imcwhois",
# target=self.args)
#
# elif(not self.switches or "channels" in self.switches or
# self.cmdstring == "@imcchanlist"):
# # show channels
# from evennia.comms.imc2 import IMC2_CHANLIST, IMC2_CLIENT
#
# channels = IMC2_CHANLIST.get_channel_list()
# string = ""
# nchans = 0
# table = prettytable.PrettyTable(["Full name", "Name", "Owner", "Perm", "Policy"])
# for chan in channels:
# nchans += 1
# table.add_row([chan.name, chan.localname, chan.owner,
# chan.level, chan.policy])
# string += "\n{wChannels on %s:{n\n%s" % (IMC2_CLIENT.factory.network, table)
# string += "\n%i Channels found." % nchans
# self.msg(string)
# else:
# # no valid inputs
# string = "Usage: imcinfo|imcchanlist|imclist"
# self.msg(string)
#
#
## unclear if this is working ...
#class CmdIMCTell(COMMAND_DEFAULT_CLASS):
# """
# send a page to a remote IMC player
#
# Usage:
# imctell User@MUD = <msg>
# imcpage "
#
# Sends a page to a user on a remote MUD, connected
# over IMC2.
# """
#
# key = "imctell"
# aliases = ["imcpage", "imc2tell", "imc2page"]
# locks = "cmd: serversetting(IMC2_ENABLED)"
# help_category = "Comms"
#
# def func(self):
# "Send tell across IMC"
#
# if not settings.IMC2_ENABLED:
# string = """IMC is not enabled. You need to activate it in game/settings.py."""
# self.msg(string)
# return
#
# from evennia.comms.imc2 import IMC2_CLIENT
#
# if not self.args or not '@' in self.lhs or not self.rhs:
# string = "Usage: imctell User@Mud = <msg>"
# self.msg(string)
# return
# target, destination = self.lhs.split("@", 1)
# message = self.rhs.strip()
# data = {"target":target, "destination":destination}
#
# # send to imc2
# IMC2_CLIENT.msg_imc2(message, from_obj=self.caller, packet_type="imctell", **data)
#
# self.msg("You paged {c%s@%s{n (over IMC): '%s'." % (target, destination, message))
#
#

View file

@ -44,7 +44,6 @@ MSSPTable = {
# Roleplaying, Simulation, Social or Strategy
"STATUS": "Open Beta", # Alpha, Closed Beta, Open Beta, Live
"GAMESYSTEM": "Custom", # D&D, d20 System, World of Darkness, etc. Use Custom if homebrew
"INTERMUD": "IMC2", # evennia supports IMC2.
"SUBGENRE": "None", # LASG, Medieval Fantasy, World War II, Frankenstein,
# Cyberpunk, Dragonlance, etc. Or None if not available.

View file

@ -240,24 +240,6 @@ msgstr "Utilisateur #1."
msgid "Limbo"
msgstr "Limbes."
#: server/portal/imc2.py:201
#, python-format
msgid "Whois reply from %(origin)s: %(msg)s"
msgstr "Réponse whois de %(origin)s: %(msg)s"
#: server/portal/imc2.py:213
#, python-format
msgid "{c%(sender)s@%(origin)s{n {wpages (over IMC):{n %(msg)s"
msgstr "{c%(sender)s@%(origin)s{n {wpages (over IMC):{n %(msg)s"
#: server/portal/imc2.py:238
msgid "IMC2 server rejected connection."
msgstr "Serveur IMC2 a rejeté la connexion."
#: server/portal/imc2.py:246
msgid "IMC2: Autosetup response found."
msgstr "IMC2: Réponse autosetup trouvé."
#: server/sessionhandler.py:258
msgid " ... Server restarted."
msgstr " ... Serveur redémarré."

View file

@ -241,24 +241,6 @@ msgstr "Questo è l'Utente #1."
msgid "Limbo"
msgstr "Limbo"
#: .\server\portal\imc2.py:167
#, python-format
msgid "Whois reply from %(origin)s: %(msg)s"
msgstr "Risposta Whois da %(origin)s: %(msg)s"
#: .\server\portal\imc2.py:175
#, python-format
msgid "{c%(sender)s@%(origin)s{n {wpages (over IMC):{n %(msg)s"
msgstr "{c%(sender)s@%(origin)s{n {wpages (over IMC):{n %(msg)s"
#: .\server\portal\imc2.py:193
msgid "IMC2 server rejected connection."
msgstr "Il server IMC2 ha rifiutato la connessione."
#: .\server\portal\imc2.py:201
msgid "IMC2: Autosetup response found."
msgstr "IMC2: Trovato responso di autosetup."
#: .\server\sessionhandler.py:192
#, python-format
msgid "Connection dropped: %s %s (%s)"

View file

@ -231,24 +231,6 @@ msgstr "Este é o Usuário #1."
msgid "Limbo"
msgstr "Limbo"
#: server/portal/imc2.py:203
#, python-format
msgid "Whois reply from %(origin)s: %(msg)s"
msgstr "Resposta Whois da %(origin)s: %(msg)s"
#: server/portal/imc2.py:215
#, python-format
msgid "{c%(sender)s@%(origin)s{n {wpages (over IMC):{n %(msg)s"
msgstr "{c%(sender)s@%(origin)s{n {wpages (over IMC):{n %(msg)s"
#: server/portal/imc2.py:240
msgid "IMC2 server rejected connection."
msgstr "Servidor IMC2 rejeitou a conexão."
#: server/portal/imc2.py:248
msgid "IMC2: Autosetup response found."
msgstr "IMC2: Encontrada resposta de autosetup."
#: server/sessionhandler.py:244
msgid " ... Server restarted."
msgstr " ... Servidor reiniciado."

View file

@ -230,24 +230,6 @@ msgstr "Detta är användare #1."
msgid "Limbo"
msgstr "Limbo"
#: server/portal/imc2.py:167
#, python-format
msgid "Whois reply from %(origin)s: %(msg)s"
msgstr "Whois-svar från %(origin)s: %(msg)s"
#: server/portal/imc2.py:175
#, python-format
msgid "{c%(sender)s@%(origin)s{n {wpages (over IMC):{n %(msg)s"
msgstr "{c%(sender)s@%(origin)s{n {wskickar (over IMC):{n %(msg)s"
#: server/portal/imc2.py:193
msgid "IMC2 server rejected connection."
msgstr "IMC2 server avvisade uppkopplingen."
#: server/portal/imc2.py:201
msgid "IMC2: Autosetup response found."
msgstr "IMC2: Autosetup-svar hittat."
#: server/sessionhandler.py:221
msgid " ... Server restarted."
msgstr "... Servern startades om."
@ -373,29 +355,12 @@ msgstr "Timeout. Kopplar ur."
#~ msgid " (exit to %s)"
#~ msgstr "(utgång till %s)"
#~ msgid "Send an IMC2 is-alive packet"
#~ msgstr "Skicka ett IMC2-is-alive paket"
#~ msgid "Send an IMC2 keepalive-request packet"
#~ msgstr "Skicka ett IMC2 keepalive-request paket"
#~ msgid "Check IMC2 list for inactive games"
#~ msgstr "Genomsök IMC2-listan efter inaktiva spel"
#~ msgid "Re-sync IMC2 network channel list"
#~ msgstr "Återsynca nätverkslistan över IMC2 kanaler"
#~ msgid "Successfully authenticated to the '%s' network."
#~ msgstr "Identifierade sig framgångsrikt till nätverket '%s'."
#~ msgid "Connection lost: %s"
#~ msgstr "Uppkopplingen förlorades: %s"
#~ msgid "Cannot attach IMC2<->Evennia: Evennia Channel '%s' not found"
#~ msgstr ""
#~ "Kan inte sammankoppla IMC2<->Evennia: Evennia-kanalen '%s' gick inte att "
#~ "hitta."
#~ msgid "joined %s."
#~ msgstr "lyssnar till %s."

View file

@ -312,100 +312,3 @@ class RSSBot(Bot):
self.ndb.ev_channel = self.db.ev_channel
if self.ndb.ev_channel:
self.ndb.ev_channel.msg(text, senders=self.id)
# IMC2 (not tested currently)
class IMC2Bot(Bot):
"""
IMC2 Bot
"""
def start(self, ev_channel=None, imc2_network=None, imc2_mudname=None,
imc2_port=None, imc2_client_pwd=None, imc2_server_pwd=None):
"""
Start by telling the portal to start a new session
Args:
ev_channel (str, optional): Key of the Evennia channel to connect to.
imc2_network (str, optional): IMC2 network name.
imc2_mudname (str, optional): Registered mudname (if not given, use settings.SERVERNAME).
imc2_port (int, optional): Port number of the IMC2 network.
imc2_client_pwd (str, optional): Client password registered with IMC2 network.
imc2_server_pwd (str, optional): Server password registered with IMC2 network.
Raises:
RuntimeError: If `ev_channel` was not found.
"""
global _SESSIONS
if not _SESSIONS:
from evennia.server.sessionhandler import SESSIONS as _SESSIONS
if ev_channel:
# connect to Evennia channel
channel = search.channel_search(ev_channel)
if not channel:
raise RuntimeError("Evennia Channel '%s' not found." % ev_channel)
channel = channel[0]
channel.connect(self)
self.db.ev_channel = channel
if imc2_network:
self.db.imc2_network = imc2_network
if imc2_port:
self.db.imc2_port = imc2_port
if imc2_mudname:
self.db.imc2_mudname = imc2_mudname
elif not self.db.imc2_mudname:
self.db.imc2_mudname = settings.SERVERNAME
# storing imc2 passwords in attributes - a possible
# security issue?
if imc2_server_pwd:
self.db.imc2_server_pwd = imc2_server_pwd
if imc2_client_pwd:
self.db.imc2_client_pwd = imc2_client_pwd
configdict = {"uid": self.dbid,
"mudname": self.db.imc2_mudname,
"network": self.db.imc2_network,
"port": self.db.imc2_port,
"client_pwd": self.db.client_pwd,
"server_pwd": self.db.server_pwd}
_SESSIONS.start_bot_session("evennia.server.portal.imc2.IMC2BotFactory", configdict)
def msg(self, text=None, **kwargs):
"""
Takes text from connected channel (only).
Args:
text (str): Message from channel.
Kwargs:
options (dict): Option dict with these allowed keys:
- from_channel (str): dbid of a channel this text originated from.
- from_obj (str): dbid of an object sending this text.
"""
options = kwargs.get("options", {})
if not self.ndb.ev_channel and self.db.ev_channel:
# cache channel lookup
self.ndb.ev_channel = self.db.ev_channel
if "from_channel" in options and text and self.ndb.ev_channel.dbid == options["from_channel"]:
if "from_obj" not in options or options["from_obj"] != [self.id]:
self.msg(text=text, options={"bot_data_out": True})
def execute_cmd(self, text=None, session=None):
"""
Relay incoming data to connected channel.
Args:
text (str, optional): Command string.
session (Session, optional): Session responsible for this
command.
"""
if not self.ndb.ev_channel and self.db.ev_channel:
# cache channel lookup
self.ndb.ev_channel = self.db.ev_channel
if self.ndb.ev_channel:
self.ndb.ev_channel.msg(text, senders=self.id)

View file

@ -34,7 +34,7 @@ class Migration(migrations.Migration):
('db_lock_storage', models.TextField(help_text=b"locks limit access to an entity. A lock is defined as a 'lock string' on the form 'type:lockfunctions', defining what functionality is locked and how to determine access. Not defining a lock means no access is granted.", verbose_name=b'locks', blank=True)),
('db_is_connected', models.BooleanField(default=False, help_text=b'If player is connected to game or not', verbose_name=b'is_connected')),
('db_cmdset_storage', models.CharField(help_text=b'optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.', max_length=255, null=True, verbose_name=b'cmdset')),
('db_is_bot', models.BooleanField(default=False, help_text=b'Used to identify irc/imc2/rss bots', verbose_name=b'is_bot')),
('db_is_bot', models.BooleanField(default=False, help_text=b'Used to identify irc/rss bots', verbose_name=b'is_bot')),
('db_attributes', models.ManyToManyField(help_text=b'attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).', to='typeclasses.Attribute', null=True)),
('db_tags', models.ManyToManyField(help_text=b'tags on this object. Tags are simple string markers to identify, group and alias objects.', to='typeclasses.Tag', null=True)),
('groups', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of his/her group.', verbose_name='groups')),

View file

@ -91,7 +91,7 @@ class PlayerDB(TypedObject, AbstractUser):
db_cmdset_storage = models.CharField('cmdset', max_length=255, null=True,
help_text="optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.")
# marks if this is a "virtual" bot player object
db_is_bot = models.BooleanField(default=False, verbose_name="is_bot", help_text="Used to identify irc/imc2/rss bots")
db_is_bot = models.BooleanField(default=False, verbose_name="is_bot", help_text="Used to identify irc/rss bots")
# Database manager
objects = PlayerDBManager()

View file

@ -48,7 +48,7 @@ SDISCONN = chr(5) # server session disconnect
SDISCONNALL = chr(6) # server session disconnect all
SSHUTD = chr(7) # server shutdown
SSYNC = chr(8) # server session sync
SCONN = chr(11) # server creating new connection (for irc/imc2 bots etc)
SCONN = chr(11) # server creating new connection (for irc bots and etc)
PCONNSYNC = chr(12) # portal post-syncing a session
PDISCONNALL = chr(13) # portal session disconnect all
AMP_MAXLEN = amp.MAX_VALUE_LENGTH # max allowed data length in AMP protocol (cannot be changed)
@ -589,7 +589,7 @@ class AMPProtocol(amp.AMP):
# set a flag in case we are about to shut down soon
self.factory.server_restart_mode = True
elif operation == SCONN: # server_force_connection (for irc/imc2 etc)
elif operation == SCONN: # server_force_connection (for irc/etc)
portal_sessionhandler.server_connect(**kwargs)
else:

View file

@ -1,480 +0,0 @@
"""
IMC2 client module. Handles connecting to and communicating with an IMC2 server.
"""
from builtins import object
from future.utils import listitems
from time import time
from twisted.internet import task
from twisted.application import internet
from twisted.internet import protocol
from twisted.conch import telnet
from evennia.server.session import Session
from evennia.utils import logger, utils
from evennia.server.portal.imc2lib import imc2_ansi
from evennia.server.portal.imc2lib import imc2_packets as pck
from django.utils.translation import ugettext as _
# storage containers for IMC2 muds and channels
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(dict):
"""
Keeps track of other MUDs connected to the IMC network.
"""
def get_mud_list(self):
"""
Returns a sorted list of connected Muds.
"""
muds = listitems(self)
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.
Args:
packet (Packet): incoming packet.
"""
mud = IMC2Mud(packet)
self[mud.name] = mud
def remove_mud_from_packet(self, packet):
"""
Removes a mud from the Mud list when given a packet.
Args:
packet (Packet): Incoming packet.
"""
mud = IMC2Mud(packet)
try:
del self[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):
"Initialize channel."
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(dict):
"""
Keeps track of Channels on the IMC network.
"""
def get_channel_list(self):
"""
Returns a sorted list of cached channels.
"""
channels = listitems(self)
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.
Args:
packet (Packet): incoming packet.
"""
channel = IMC2Channel(packet)
self[channel.name] = channel
def remove_channel_from_packet(self, packet):
"""
Removes a channel from the Channel list when given a packet.
Args:
packet (Packet): incoming packet.
"""
channel = IMC2Channel(packet)
try:
del self[channel.name]
except KeyError:
# No matching entry, no big deal.
pass
#
# IMC2 protocol
#
class IMC2Bot(telnet.StatefulTelnetProtocol, Session):
"""
Provides the abstraction for the IMC2 protocol. Handles connection,
authentication, and all necessary packets.
"""
def __init__(self):
"Initialize bot."
self.is_authenticated = False
# only support plaintext passwords
self.auth_type = "plaintext"
self.sequence = None
self.imc2_mudlist = IMC2MudList()
self.imc2_chanlist = IMC2ChanList()
def _send_packet(self, packet):
"""
Helper function to send packets across the wire.
Args:
packet (Packet): Outgoing packet.
"""
packet.imc2_protocol = self
packet_str = utils.to_str(packet.assemble(self.factory.mudname,
self.factory.client_pwd, self.factory.server_pwd))
self.sendLine(packet_str)
def _isalive(self):
"Send an isalive packet."
self._send_packet(pck.IMC2PacketIsAlive())
def _keepalive(self):
"Send a keepalive packet."
# send to channel?
self._send_packet(pck.IMC2PacketKeepAliveRequest())
def _channellist(self):
"Sync the network channel list."
checked_networks = []
if not self.network in checked_networks:
self._send_packet(pck.IMC2PacketIceRefresh())
checked_networks.append(self.network)
def _prune(self):
"Prune active channel list."
t0 = time()
for name, mudinfo in self.imc2_mudlist.items():
if t0 - mudinfo.last_updated > 3599:
del self.imc2_mudlist[name]
def _whois_reply(self, packet):
"""
Handle reply from server from an imcwhois request.
Args:
packet (Packet): Data packet.
"""
# packet.target potentially contains the id of an character to target
# not using that here
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}
# somehow pass reply on to a given player, for now we just send to channel
self.data_in(string)
def _format_tell(self, packet):
"""
Handle tells over IMC2 by formatting the text properly
Args:
packet (Packet): Data packet.
"""
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 _imc_login(self, line):
"""
Connect and identify to imc network as per the
`self.auth_type` setting.
Args:
line (str): Incoming text.
"""
if self.auth_type == "plaintext":
# Only support Plain text passwords.
# SERVER Sends: PW <servername> <serverpw> version=<version#> <networkname>
logger.log_info("IMC2: AUTH< %s" % line)
line_split = line.split(' ')
pw_present = line_split[0] == 'PW'
autosetup_present = line_split[0] == 'autosetup'
if "reject" in line_split:
auth_message = _("IMC2 server rejected connection.")
logger.log_info(auth_message)
return
if pw_present:
self.server_name = line_split[1]
self.network_name = line_split[4]
elif autosetup_present:
logger.log_info(_("IMC2: Autosetup response found."))
self.server_name = line_split[1]
self.network_name = line_split[3]
self.is_authenticated = True
self.sequence = int(time())
# Log to stdout and notify over MUDInfo.
logger.log_info('IMC2: Authenticated to %s' % self.factory.network)
# Ask to see what other MUDs are connected.
self._send_packet(pck.IMC2PacketKeepAliveRequest())
# IMC2 protocol states that KeepAliveRequests should be followed
# up by the requester sending an IsAlive packet.
self._send_packet(pck.IMC2PacketIsAlive())
# Get a listing of channels.
self._send_packet(pck.IMC2PacketIceRefresh())
def connectionMade(self):
"""
Triggered after connecting to the IMC2 network.
"""
self.stopping = False
self.factory.bot = self
address = "%s@%s" % (self.mudname, self.network)
self.init_session("ircbot", address, self.factory.sessionhandler)
# link back and log in
self.uid = int(self.factory.uid)
self.logged_in = True
self.factory.sessionhandler.connect(self)
logger.log_info("IMC2 bot connected to %s." % self.network)
# Send authentication packet. The reply will be caught by lineReceived
self._send_packet(pck.IMC2PacketAuthPlaintext())
def lineReceived(self, line):
"""
IMC2 -> Evennia
Triggered when text is received from the IMC2 network. Figures out
what to do with the packet. This deals with the following
Args:
line (str): Incoming text.
"""
line = line.strip()
if not self.is_authenticated:
# we are not authenticated yet. Deal with this.
self._imc_login(line)
return
# Parse the packet and encapsulate it for easy access
packet = pck.IMC2Packet(self.mudname, packet_str=line)
# Figure out what kind of packet we're dealing with and hand it
# off to the correct handler.
if packet.packet_type == 'is-alive':
self.imc2_mudlist.update_mud_from_packet(packet)
elif packet.packet_type == 'keepalive-request':
# Don't need to check the destination, we only receive these
# packets when they are intended for us.
self.send_packet(pck.IMC2PacketIsAlive())
elif packet.packet_type == 'ice-msg-b':
self.data_out(text=line, packettype="broadcast")
elif packet.packet_type == 'whois-reply':
# handle eventual whois reply
self._whois_reply(packet)
elif packet.packet_type == 'close-notify':
self.imc2_mudlist.remove_mud_from_packet(packet)
elif packet.packet_type == 'ice-update':
self.imc2_chanlist.update_channel_from_packet(packet)
elif packet.packet_type == 'ice-destroy':
self.imc2_chanlist.remove_channel_from_packet(packet)
elif packet.packet_type == 'tell':
# send message to identified player
pass
def data_in(self, text=None, **kwargs):
"""
Data IMC2 -> Evennia.
Kwargs:
text (str): Incoming text.
kwargs (any): Other data from protocol.
"""
text = "bot_data_in " + text
self.sessionhandler.data_in(self, text=text, **kwargs)
def data_out(self, text=None, **kwargs):
"""
Evennia -> IMC2.
Kwargs:
text (str): Outgoing text.
packet_type (str):
- broadcast: Send to everyone on IMC channel.
- tell: Send a tell (see target keyword).
- whois: Get whois information (see target keyword).
sender (str): Used by tell to identify the mud sending.
target (str): Key identifier of target to tells or whois. If not
given "Unknown" will be used.
destination (str): Used by tell to specify mud
destination to send to.
"""
if self.sequence:
# This gets incremented with every command.
self.sequence += 1
packet_type = kwargs.get("packet_type", "imcbroadcast")
if packet_type == "broadcast":
# broadcast to everyone on IMC channel
if text.startswith("bot_data_out"):
text = text.split(" ", 1)[1]
else:
return
# we remove the extra channel info since imc2 supplies this anyway
if ":" in text:
header, message = [part.strip() for part in text.split(":", 1)]
# Create imc2packet and send it
self._send_packet(pck.IMC2PacketIceMsgBroadcasted(self.servername,
self.channel,
header, text))
elif packet_type == "tell":
# send an IMC2 tell
sender = kwargs.get("sender", self.mudname)
target = kwargs.get("target", "Unknown")
destination = kwargs.get("destination", "Unknown")
self._send_packet(pck.IMC2PacketTell(sender, target, destination, text))
elif packet_type == "whois":
# send a whois request
sender = kwargs.get("sender", self.mudname)
target = kwargs.get("target", "Unknown")
self._send_packet(pck.IMC2PacketWhois(sender, target))
class IMC2BotFactory(protocol.ReconnectingClientFactory):
"""
Creates instances of the IMC2Protocol. Should really only ever
need to create one connection. Tied in via evennia/server.py.
"""
initialDelay = 1
factor = 1.5
maxDelay = 60
def __init__(self, sessionhandler, uid=None, network=None, channel=None,
port=None, mudname=None, client_pwd=None, server_pwd=None):
"Initialize the bot factory."
self.uid = uid
self.network = network
sname, host = network.split(".", 1)
self.servername = sname.strip()
self.channel = channel
self.port = port
self.mudname = mudname
self.protocol_version = '2'
self.client_pwd = client_pwd
self.server_pwd = server_pwd
self.bot = None
self.task_isalive = None
self.task_keepalive = None
self.task_prune = None
self.task_channellist = None
def buildProtocol(self, addr):
"""
Build the protocol.
Args:
addr (str): Protocl address.
Returns:
protocol (Protocol): The new protocol.
"""
protocol = IMC2Bot()
protocol.factory = self
protocol.network = self.network
protocol.servername = self.servername
protocol.channel = self.channel
protocol.mudname = self.mudname
protocol.port = self.port
return protocol
def clientConnectionFailed(self, connector, reason):
"""
Called when Client could not connect.
Args:
connector (Connector): Reprsents the connection.
reason (str): Reason for the failure.
"""
self.retry(connector)
def clientConnectionLost(self, connector, reason):
"""
Called when Client looses connection.
Args:
connector (Connector): Reprsents the connection.
reason (str): Reason for the failure.
"""
if not self.bot.stopping:
self.retry(connector)
def start(self):
"Connect session to sessionhandler"
def errback(fail):
logger.log_err(fail.value)
if self.port:
service = internet.TCPClient(self.network, int(self.port), self)
self.sessionhandler.portal.services.addService(service)
# start tasks
self.task_isalive = task.LoopingCall(self.bot._isalive)
self.task_keepalive = task.LoopingCall(self.bot._keepalive)
self.task_prune = task.LoopingCall(self.bot._prune)
self.task_channellist = task.LoopingCall(self.bot._channellist)
self.task_isalive.start(900, now=False)
self.task_keepalive.start(3500, now=False)
self.task_prune.start(1800, now=False)
self.task_channellist.start(3600 * 24, now=False)

View file

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

View file

@ -1,60 +0,0 @@
"""
ANSI parser - this adds colour to text according to
special markup strings.
This is a IMC2 complacent version.
"""
import re
from evennia.utils import ansi
class IMCANSIParser(ansi.ANSIParser):
"""
This parser is per the IMC2 specification.
"""
def __init__(self):
normal = ansi.ANSI_NORMAL
hilite = ansi.ANSI_HILITE
self.ansi_map = [
(r'~Z', normal), # Random
(r'~x', normal + ansi.ANSI_BLACK), # Black
(r'~D', hilite + ansi.ANSI_BLACK), # Dark Grey
(r'~z', hilite + ansi.ANSI_BLACK),
(r'~w', normal + ansi.ANSI_WHITE), # Grey
(r'~W', hilite + ansi.ANSI_WHITE), # White
(r'~g', normal + ansi.ANSI_GREEN), # Dark Green
(r'~G', hilite + ansi.ANSI_GREEN), # Green
(r'~p', normal + ansi.ANSI_MAGENTA), # Dark magenta
(r'~m', normal + ansi.ANSI_MAGENTA),
(r'~M', hilite + ansi.ANSI_MAGENTA), # Magenta
(r'~P', hilite + ansi.ANSI_MAGENTA),
(r'~c', normal + ansi.ANSI_CYAN), # Cyan
(r'~y', normal + ansi.ANSI_YELLOW), # Dark Yellow (brown)
(r'~Y', hilite + ansi.ANSI_YELLOW), # Yellow
(r'~b', normal + ansi.ANSI_BLUE), # Dark Blue
(r'~B', hilite + ansi.ANSI_BLUE), # Blue
(r'~C', hilite + ansi.ANSI_BLUE),
(r'~r', normal + ansi.ANSI_RED), # Dark Red
(r'~R', hilite + ansi.ANSI_RED), # Red
## Formatting
(r'~L', hilite), # Bold/hilite
(r'~!', normal), # reset
(r'\\r', normal),
(r'\\n', ansi.ANSI_RETURN),
]
# prepare regex matching
self.ansi_sub = [(re.compile(sub[0], re.DOTALL), sub[1])
for sub in self.ansi_map]
# prepare matching ansi codes overall
self.ansi_regex = re.compile("\033\[[0-9;]+m")
ANSI_PARSER = IMCANSIParser()
def parse_ansi(string, strip_ansi=False, parser=ANSI_PARSER):
"""
Shortcut to use the IMC2 ANSI parser.
"""
return parser.parse_ansi(string, strip_ansi=strip_ansi)

View file

@ -1,797 +0,0 @@
"""
IMC2 packets. These are pretty well documented at:
http://www.mudbytes.net/index.php?a=articles&s=imc2_protocol
"""
from __future__ import print_function
from builtins import object
import shlex
from django.conf import settings
class Lexxer(shlex.shlex):
"""
A lexical parser for interpreting IMC2 packets.
"""
def __init__(self, packet_str, posix=True):
shlex.shlex.__init__(self, packet_str, posix=True)
# Single-quotes are notably not present. This is important!
self.quotes = '"'
self.commenters = ''
# This helps denote what constitutes a continuous token.
self.wordchars += "~`!@#$%^&*()-_+=[{]}|\\;:',<.>/?"
class IMC2Packet(object):
"""
Base IMC2 packet class. This is generally sub-classed, aside from using it
to parse incoming packets from the IMC2 network server.
"""
def __init__(self, mudname=None, packet_str=None):
"""
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...>
self.sender = None
if not mudname:
mudname = settings.SERVERNAME
self.origin = mudname
self.sequence = None
self.route = mudname
self.packet_type = None
self.target = None
self.destination = None
# Optional data.
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:
if counter == 0:
# This is the sender@origin token.
sender_origin = token
split_sender_origin = sender_origin.split('@')
self.sender = split_sender_origin[0].strip()
self.origin = split_sender_origin[1]
elif counter == 1:
# Numeric time-based sequence.
self.sequence = token
elif counter == 2:
# Packet routing info.
self.route = token
elif counter == 3:
# Packet type string.
self.packet_type = token
elif counter == 4:
# Get values for the target and destination attributes.
target_destination = token
split_target_destination = target_destination.split('@')
self.target = split_target_destination[0]
try:
self.destination = split_target_destination[1]
except IndexError:
# There is only one element to the target@dest segment
# of the packet. Wipe the target and move the captured
# value to the destination attrib.
self.target = '*'
self.destination = split_target_destination[0]
elif counter > 4:
# Populate optional data.
try:
key, value = token.split('=', 1)
self.optional_data[key] = value
except ValueError:
# Failed to split on equal sign, disregard.
pass
# Increment and continue to the next token (if applicable)
counter += 1
def __str__(self):
retval = """
--IMC2 package (%s)
Sender: %s
Origin: %s
Sequence: %s
Route: %s
Type: %s
Target: %s
Dest.: %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.
"""
if self.optional_data:
data_string = ''
for key, value in self.optional_data.items():
# Determine the number of words in this value.
words = len(str(value).split(' '))
# Anything over 1 word needs double quotes.
if words > 1:
value = '"%s"' % (value,)
data_string += '%s=%s ' % (key, value)
return data_string.strip()
else:
return ''
def _get_sender_name(self):
"""
Calculates the sender name to be sent with the packet.
"""
if self.sender == '*':
# Some packets have no sender.
return '*'
elif str(self.sender).isdigit():
return self.sender
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:
# Player object.
name = self.sender.get_name(fullname=False, show_dbref=False,
show_flags=False,
no_ansi=True)
# IMC2 does not allow for spaces.
return name.strip().replace(' ', '_')
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.
"""
self.sequence = self.imc2_protocol.sequence
packet = "%s@%s %s %s %s %s@%s %s\n" % (
self._get_sender_name(),
self.origin,
self.sequence,
self.route,
self.packet_type,
self.target,
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:
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.
"""
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:
This packet is sent by a MUD to trigger is-alive packets from other MUDs.
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 *@*
"""
def __init__(self):
super(IMC2PacketKeepAliveRequest, self).__init__()
self.sender = '*'
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
for filling a client's mudlist with the information about other MUDs on the
network.
Data:
versionid=<string>
Where <string> is the text version ID of the client. ("IMC2 4.5 MUD-Net")
url=<string>
Where <string> is the proper URL of the client. (http://www.domain.com)
host=<string>
Where <string> is the telnet address of the MUD. (telnet://domain.com)
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>
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
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
"""
def __init__(self):
super(IMC2PacketIsAlive, self).__init__()
self.sender = '*'
self.packet_type = 'is-alive'
self.target = '*'
self.destination = '*'
self.optional_data = {'versionid': 'Evennia IMC2',
'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
channel they control. The usual target for this packet is IMC@$.
Data:
(none)
Example:
*@YourMUD 1234567890 YourMUD!Hub1 ice-refresh IMC@$
"""
def __init__(self):
super(IMC2PacketIceRefresh, self).__init__()
self.sender = '*'
self.packet_type = 'ice-refresh'
self.target = 'IMC'
self.destination = '$'
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>
The channel's network name in the format of ServerName:ChannelName
owner=<string>
The Name@MUD of the channel's owner
operators=<string>
A space-seperated list of the Channel's operators, (format: 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
"private" channel.
excluded=<string>
The space-seperated list of banned User@MUDs, only valid for "open"
channels.
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
"""
pass
class IMC2PacketIceMsgRelayed(IMC2Packet):
"""
Description:
The -r in this ice-msg packet means it was relayed. This, along with the
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>
The User@MUD the message came from.
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.
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
"""
pass
class IMC2PacketIceMsgPrivate(IMC2Packet):
"""
Description:
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>
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
"""
pass
class IMC2PacketIceMsgBroadcasted(IMC2Packet):
"""
Description:
This is the packet used to chat on open policy channels. When sent from a
MUD, it is broadcasted across the network. Other MUDs receive it in-tact
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,
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:
(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
"""
def __init__(self, server, channel, pobject, message):
"""
Args:
server: (String) Server name the channel resides on (obs - this is
e.g. Server01, not the full network name!)
channel: (String) Name of the IMC2 channel.
pobject: (Object) Object sending the message.
message: (String) Message to send.
"""
super(IMC2PacketIceMsgBroadcasted, self).__init__()
self.sender = pobject
self.packet_type = 'ice-msg-b'
self.target = '*'
self.destination = '*'
self.optional_data = {'channel': '%s:%s' % (server, channel),
'text': message,
'emote': 0,
'echo': 1}
class IMC2PacketUserCache(IMC2Packet):
"""
Description:
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
"""
pass
class IMC2PacketUserCacheRequest(IMC2Packet):
"""
Description:
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
"""
pass
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
"""
pass
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
"""
def __init__(self, pobject, target, destination, message):
super(IMC2PacketTell, self).__init__()
self.sender = pobject
self.packet_type = "tell"
self.target = target
self.destination = destination
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,
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
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."
"""
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
remote-admin enabled server send you the list of commands it will accept.
Data:
command=<string>
The command being sent to the server for processing.
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
the same manner as an authentication packet.
Example:
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
packet. Other cases include channel operators.
Data:
channel=<string>
The target server:channel for the command.
command=<string>
The command to be processed.
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
"""
pass
class IMC2PacketDestroy(IMC2Packet):
"""
Description:
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.
"""
pass
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
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
packet.
Data:
type=<string> Types: who, info, "finger <name>", istats (server only)
Example:
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
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
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>"
"""
pass
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
"""
def __init__(self, pobject_id, whois_target):
super(IMC2PacketWhois, self).__init__()
# Use the dbref, it's easier to trace back for the whois-reply.
self.sender = pobject_id
self.packet_type = 'whois'
self.target = whois_target
self.destination = '*'
self.optional_data = {'level': '5'}
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
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"
"""
pass
class IMC2PacketBeep(IMC2Packet):
"""
Description:
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
"""
pass
class IMC2PacketIceChanWho(IMC2Packet):
"""
Description:
Sends a request to the specified MUD or * to list all the users listening
to the specified channel.
Data:
level=<int>
Sender's permission level.
channel=<string>
The server:chan name of the channel.
lname=<string>
The localname of the channel.
Example:
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
filter or block the response.
Data:
channel=<string>
The server:chan of the requested channel.
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"
"""
pass
class IMC2PacketLaston(IMC2Packet):
"""
Description:
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
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
"""
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
disconnected MUD from their MUD list (or mark it as Disconnected).
Data:
host=<string>
The MUD or server that has disconnected from the network.
Example:
*@Hub2 1234567890 Hub2!Hub1 close-notify *@* host=DisconnMUD
"""
pass
if __name__ == "__main__":
packstr = "Kayle@MW 1234567 MW!Server02!Server01 ice-msg-b *@* channel=Server01:ichat text=\"*they're going woot\" emote=0 echo=1"
packstr = "*@Lythelian 1234567 Lythelian!Server01 is-alive *@* versionid=\"Tim's LPC IMC2 client 30-Jan-05 / Dead Souls integrated\" networkname=Mudbytes url=http://dead-souls.net host=70.32.76.142 port=6666 sha256=0"
print(IMC2Packet(packstr))

View file

@ -114,7 +114,6 @@ class Mssp(object):
# Roleplaying, Simulation, Social or Strategy
"STATUS": "Open Beta", # Alpha, Closed Beta, Open Beta, Live
"GAMESYSTEM": "Custom", # D&D, d20 System, World of Darkness, etc. Use Custom if homebrew
"INTERMUD": "IMC2", # evennia supports IMC2.
"SUBGENRE": "None", # LASG, Medieval Fantasy, World War II, Frankenstein,
# Cyberpunk, Dragonlance, etc. Or None if not available.

View file

@ -201,7 +201,7 @@ class PortalSessionHandler(SessionHandler):
Called by server to force the initialization of a new protocol
instance. Server wants this instance to get a unique sessid
and to be connected back as normal. This is used to initiate
irc/imc2/rss etc connections.
irc/rss etc connections.
Args:
protocol_path (st): Full python path to the class factory

View file

@ -76,7 +76,6 @@ GUEST_ENABLED = settings.GUEST_ENABLED
# server-channel mappings
WEBSERVER_ENABLED = settings.WEBSERVER_ENABLED and WEBSERVER_PORTS and WEBSERVER_INTERFACES
IMC2_ENABLED = settings.IMC2_ENABLED
IRC_ENABLED = settings.IRC_ENABLED
RSS_ENABLED = settings.RSS_ENABLED
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
@ -537,10 +536,6 @@ if IRC_ENABLED:
# IRC channel connections
ENABLED.append('irc')
if IMC2_ENABLED:
# IMC2 channel connections
ENABLED.append('imc2')
if RSS_ENABLED:
# RSS feed channel connections
ENABLED.append('rss')

View file

@ -520,8 +520,8 @@ DEFAULT_CHANNELS = [
# Note: You do *not* have to make your MUD open to
# the public to use the external connections, they
# operate as long as you have an internet connection,
# just like stand-alone chat clients. IRC and IMC2
# requires that you have twisted.words installed.
# just like stand-alone chat clients. IRC requires
# that you have twisted.words installed.
# Evennia can connect to external IRC channels and
# echo what is said on the channel to IRC and vice
@ -537,26 +537,6 @@ IRC_ENABLED = False
RSS_ENABLED=False
RSS_UPDATE_INTERVAL = 60*10 # 10 minutes
# IMC (Inter-MUD communication) allows to connect an Evennia channel
# to an IMC2 server. This lets them talk to people on other MUDs also
# using IMC. Evennia's IMC2 client was developed against MudByte's
# network. You must register your MUD on the network before you can
# use it, go to http://www.mudbytes.net/imc2-intermud-join-network.
# Choose 'Other unsupported IMC2 version' from the choices and and
# enter your information there. You should enter the same 'short mud
# name' as your SERVERNAME above, then choose imc network server as
# well as client/server passwords same as below. When enabled, the
# command @imc2chan becomes available in-game and allows you to
# connect Evennia channels to IMC channels on the network. The Evennia
# discussion channel 'ievennia' is on server01.mudbytes.net:5000.
# NOTE - IMC2 is currently NOT FUNCTIONAL due to lack of testing means.
IMC2_ENABLED = False
IMC2_NETWORK = "server01.mudbytes.net"
IMC2_PORT = 5000 # this is the imc2 port, not on localhost
IMC2_CLIENT_PWD = ""
IMC2_SERVER_PWD = ""
######################################################################
# Django web features
######################################################################

View file

@ -28,7 +28,7 @@ except AttributeError:
PLAYER_RELATED = ['Players']
GAME_ENTITIES = ['Objects', 'Scripts', 'Comms', 'Help']
GAME_SETUP = ['Permissions', 'Config']
CONNECTIONS = ['Irc', 'Imc2']
CONNECTIONS = ['Irc']
WEBSITE = ['Flatpages', 'News', 'Sites']