mirror of
https://github.com/evennia/evennia.git
synced 2026-03-19 22:36:31 +01:00
Implemented untested IMC2 in the new system.
This commit is contained in:
parent
97991a2238
commit
84f5c4dca5
9 changed files with 359 additions and 742 deletions
|
|
@ -121,6 +121,8 @@ class Bot(Player):
|
|||
|
||||
# Bot implementations
|
||||
|
||||
# IRC
|
||||
|
||||
class IRCBot(Bot):
|
||||
"""
|
||||
Bot for handling IRC connections.
|
||||
|
|
@ -194,6 +196,8 @@ class IRCBot(Bot):
|
|||
self.ndb.ev_channel.msg(text, senders=self.dbobj.id)
|
||||
|
||||
|
||||
# RSS
|
||||
|
||||
class RSSBot(Bot):
|
||||
"""
|
||||
An RSS relayer. The RSS protocol itself runs a ticker to update its feed at regular
|
||||
|
|
@ -240,4 +244,75 @@ class RSSBot(Bot):
|
|||
if self.ndb.ev_channel:
|
||||
self.ndb.ev_channel.msg(text, senders=self.dbobj.id)
|
||||
|
||||
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
|
||||
ev_channel - key of the Evennia channel to connect to
|
||||
imc2_network - IMC2 network name
|
||||
imc2_mudname - registered mudname (if not given, use settings.SERVERNAME)
|
||||
imc2_port - port number of IMC2 network
|
||||
imc2_client_pwd - client password registered with IMC2 network
|
||||
imc2_server_pwd - server password registered with IMC2 network
|
||||
"""
|
||||
global _SESSIONS
|
||||
if not _SESSIONS:
|
||||
from src.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("src.server.portal.imc2.IMC2BotFactory", configdict)
|
||||
|
||||
def msg(self, text=None, **kwargs):
|
||||
"""
|
||||
Takes text from connected channel (only)
|
||||
"""
|
||||
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 kwargs and text and self.ndb.ev_channel.dbid == kwargs["from_channel"]:
|
||||
if "from_obj" not in kwargs or kwargs["from_obj"] != [self.dbobj.id]:
|
||||
text = "bot_data_out %s" % text
|
||||
self.dbobj.msg(text=text)
|
||||
|
||||
def execute_cmd(self, text=None, sessid=None):
|
||||
"""
|
||||
Relay incoming data to connected channel.
|
||||
"""
|
||||
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.dbobj.id)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,213 +3,186 @@ IMC2 client module. Handles connecting to and communicating with an IMC2 server.
|
|||
"""
|
||||
|
||||
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 django.conf import settings
|
||||
|
||||
from src.utils import logger, create, search, utils
|
||||
from src.server.sessionhandler import SESSIONS
|
||||
from src.scripts.scripts import Script
|
||||
from src.comms.models import ChannelDB, ExternalChannelConnection
|
||||
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 src.server.session import Session
|
||||
from src.utils import logger, utils
|
||||
from src.server.portal.imc2lib import imc2_packets as pck
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
# IMC2 network setup
|
||||
IMC2_MUDNAME = settings.SERVERNAME
|
||||
IMC2_NETWORK = settings.IMC2_NETWORK
|
||||
IMC2_PORT = settings.IMC2_PORT
|
||||
IMC2_CLIENT_PWD = settings.IMC2_CLIENT_PWD
|
||||
IMC2_SERVER_PWD = settings.IMC2_SERVER_PWD
|
||||
|
||||
# channel to send info to
|
||||
INFOCHANNEL = ChannelDB.objects.channel_search(settings.CHANNEL_MUDINFO[0])
|
||||
# all linked channel connections
|
||||
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()
|
||||
# Tracks the list of available channels on the network.
|
||||
IMC2_CHANLIST = IMC2ChanList()
|
||||
# storage containers for IMC2 muds and channels
|
||||
|
||||
#
|
||||
# Helper method
|
||||
#
|
||||
|
||||
def msg_info(message):
|
||||
class IMC2Mud(object):
|
||||
"""
|
||||
Send info to default info channel
|
||||
Stores information about other games connected to our current IMC2 network.
|
||||
"""
|
||||
try:
|
||||
INFOCHANNEL[0].msg(message)
|
||||
message = '[%s][IMC2]: %s' % (INFOCHANNEL[0].key, message)
|
||||
except Exception:
|
||||
logger.log_infomsg("MUDinfo (imc2): %s" % message)
|
||||
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()
|
||||
|
||||
#
|
||||
# Regular scripts
|
||||
#
|
||||
|
||||
class Send_IsAlive(Script):
|
||||
class IMC2MudList(dict):
|
||||
"""
|
||||
Sends periodic keepalives to network neighbors. This lets the other
|
||||
games know that our game is still up and connected to the network. Also
|
||||
provides some useful information about the client game.
|
||||
Keeps track of other MUDs connected to the IMC network.
|
||||
"""
|
||||
def at_script_creation(self):
|
||||
self.key = 'IMC2_Send_IsAlive'
|
||||
self.interval = 900
|
||||
self.desc = _("Send an IMC2 is-alive packet")
|
||||
self.persistent = True
|
||||
def get_mud_list(self):
|
||||
"""
|
||||
Returns a sorted list of connected Muds.
|
||||
"""
|
||||
muds = self.items()
|
||||
muds.sort()
|
||||
return [value for key, value in muds]
|
||||
|
||||
def at_repeat(self):
|
||||
IMC2_CLIENT.send_packet(pck.IMC2PacketIsAlive())
|
||||
def update_mud_from_packet(self, packet):
|
||||
"""
|
||||
This grabs relevant info from the packet and stuffs it in the
|
||||
Mud list for later retrieval.
|
||||
"""
|
||||
mud = IMC2Mud(packet)
|
||||
self[mud.name] = mud
|
||||
|
||||
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_"))
|
||||
def remove_mud_from_packet(self, packet):
|
||||
"""
|
||||
Removes a mud from the Mud list when given a packet.
|
||||
"""
|
||||
mud = IMC2Mud(packet)
|
||||
try:
|
||||
del self[mud.name]
|
||||
except KeyError:
|
||||
# No matching entry, no big deal.
|
||||
pass
|
||||
|
||||
class Send_Keepalive_Request(Script):
|
||||
|
||||
class IMC2Channel(object):
|
||||
"""
|
||||
Event: Sends a keepalive-request to connected games in order to see who
|
||||
is connected.
|
||||
Stores information about channels available on the network.
|
||||
"""
|
||||
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 __init__(self, packet):
|
||||
self.localname = packet.optional_data.get('localname', None)
|
||||
self.name = packet.optional_data.get('channel', None)
|
||||
self.level = packet.optional_data.get('level', None)
|
||||
self.owner = packet.optional_data.get('owner', None)
|
||||
self.policy = packet.optional_data.get('policy', None)
|
||||
self.last_updated = time()
|
||||
|
||||
def at_repeat(self):
|
||||
IMC2_CLIENT.channel.send_packet(pck.IMC2PacketKeepAliveRequest())
|
||||
|
||||
def is_valid(self):
|
||||
"Is only valid as long as there are channels to update"
|
||||
return any(service for service in SESSIONS.server.services
|
||||
if service.name.startswith("imc2_"))
|
||||
|
||||
class Prune_Inactive_Muds(Script):
|
||||
class IMC2ChanList(dict):
|
||||
"""
|
||||
Prunes games that have not sent is-alive packets for a while. If
|
||||
we haven't heard from them, they're probably not connected or don't
|
||||
implement the protocol correctly. In either case, good riddance to them.
|
||||
Keeps track of Channels on the IMC network.
|
||||
"""
|
||||
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.inactive_threshold = 3599
|
||||
|
||||
def at_repeat(self):
|
||||
for name, mudinfo in IMC2_MUDLIST.mud_list.items():
|
||||
if time() - mudinfo.last_updated > self.inactive_threshold:
|
||||
del IMC2_MUDLIST.mud_list[name]
|
||||
def get_channel_list(self):
|
||||
"""
|
||||
Returns a sorted list of cached channels.
|
||||
"""
|
||||
channels = self.items()
|
||||
channels.sort()
|
||||
return [value for key, value in channels]
|
||||
|
||||
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_"))
|
||||
def update_channel_from_packet(self, packet):
|
||||
"""
|
||||
This grabs relevant info from the packet and stuffs it in the
|
||||
channel list for later retrieval.
|
||||
"""
|
||||
channel = IMC2Channel(packet)
|
||||
self[channel.name] = channel
|
||||
|
||||
def remove_channel_from_packet(self, packet):
|
||||
"""
|
||||
Removes a channel from the Channel list when given a packet.
|
||||
"""
|
||||
channel = IMC2Channel(packet)
|
||||
try:
|
||||
del self[channel.name]
|
||||
except KeyError:
|
||||
# No matching entry, no big deal.
|
||||
pass
|
||||
|
||||
|
||||
class Sync_Server_Channel_List(Script):
|
||||
"""
|
||||
Re-syncs the network's channel list. This will
|
||||
cause a cascade of reply packets of a certain type
|
||||
from the network. These are handled by the protocol,
|
||||
gradually updating the channel cache.
|
||||
"""
|
||||
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
|
||||
|
||||
def at_repeat(self):
|
||||
checked_networks = []
|
||||
network = IMC2_CLIENT.factory.network
|
||||
if not network in checked_networks:
|
||||
channel.send_packet(pkg.IMC2PacketIceRefresh())
|
||||
checked_networks.append(network)
|
||||
|
||||
def is_valid(self):
|
||||
return any(service for service in SESSIONS.server.services
|
||||
if service.name.startswith("imc2_"))
|
||||
|
||||
|
||||
#
|
||||
# IMC2 protocol
|
||||
#
|
||||
class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
||||
class IMC2Bot(telnet.StatefulTelnetProtocol, Session):
|
||||
"""
|
||||
Provides the abstraction for the IMC2 protocol. Handles connection,
|
||||
authentication, and all necessary packets.
|
||||
"""
|
||||
def __init__(self):
|
||||
global IMC2_CLIENT
|
||||
IMC2_CLIENT = self
|
||||
self.is_authenticated = False
|
||||
self.auth_type = None
|
||||
self.server_name = None
|
||||
self.network_name = None
|
||||
self.sequence = None
|
||||
|
||||
def connectionMade(self):
|
||||
"""
|
||||
Triggered after connecting to the IMC2 network.
|
||||
"""
|
||||
# only support plaintext passwords
|
||||
self.auth_type = "plaintext"
|
||||
if IMC2_DEBUG:
|
||||
logger.log_infomsg("IMC2: Connected to network server.")
|
||||
logger.log_infomsg("IMC2: Sending authentication packet.")
|
||||
self.send_packet(pck.IMC2PacketAuthPlaintext())
|
||||
self.sequence = None
|
||||
self.imc2_mudlist = IMC2MudList()
|
||||
self.imc2_chanlist = IMC2ChanList()
|
||||
|
||||
def connectionLost(self, reason=None):
|
||||
"""
|
||||
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
|
||||
if service.running:
|
||||
service.stopService()
|
||||
|
||||
def send_packet(self, packet):
|
||||
"""
|
||||
Given a sub-class of IMC2Packet, assemble the packet and send it
|
||||
on its way to the IMC2 server.
|
||||
|
||||
Evennia -> IMC2
|
||||
"""
|
||||
if self.sequence:
|
||||
# This gets incremented with every command.
|
||||
self.sequence += 1
|
||||
def _send_packet(self, packet):
|
||||
"Helper function to send packets across the wire"
|
||||
packet.imc2_protocol = self
|
||||
packet_str = utils.to_str(packet.assemble(self.factory.mudname,
|
||||
self.factory.client_pwd, self.factory.server_pwd))
|
||||
if IMC2_DEBUG and not (hasattr(packet, 'packet_type') and
|
||||
packet.packet_type == "is-alive"):
|
||||
logger.log_infomsg("IMC2: SENT> %s" % packet_str)
|
||||
logger.log_infomsg(str(packet))
|
||||
self.factory.client_pwd, self.factory.server_pwd))
|
||||
self.sendLine(packet_str)
|
||||
|
||||
def _parse_auth_response(self, line):
|
||||
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"
|
||||
# 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
|
||||
|
||||
def _format_tell(self, packet):
|
||||
"""
|
||||
Parses the IMC2 network authentication packet.
|
||||
Handle tells over IMC2 by formatting the text properly
|
||||
"""
|
||||
return _("{c%(sender)s@%(origin)s{n {wpages (over IMC):{n %(msg)s") % {"sender": packet.sender,
|
||||
"origin": packet.origin,
|
||||
"msg": packet.optional_data.get('text', 'ERROR: No text provided.')}
|
||||
|
||||
def _imc_login(self, line):
|
||||
"Connect and identify to imc network"
|
||||
|
||||
if self.auth_type == "plaintext":
|
||||
# Plain text passwords.
|
||||
# Only support Plain text passwords.
|
||||
# SERVER Sends: PW <servername> <serverpw> version=<version#> <networkname>
|
||||
|
||||
if IMC2_DEBUG:
|
||||
logger.log_infomsg("IMC2: AUTH< %s" % line)
|
||||
logger.log_infomsg("IMC2: AUTH< %s" % line)
|
||||
|
||||
line_split = line.split(' ')
|
||||
pw_present = line_split[0] == 'PW'
|
||||
|
|
@ -218,7 +191,6 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
|||
if "reject" in line_split:
|
||||
auth_message = _("IMC2 server rejected connection.")
|
||||
logger.log_infomsg(auth_message)
|
||||
msg_info(auth_message)
|
||||
return
|
||||
|
||||
if pw_present:
|
||||
|
|
@ -232,266 +204,193 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
|||
self.sequence = int(time())
|
||||
|
||||
# Log to stdout and notify over MUDInfo.
|
||||
auth_message = _("Successfully authenticated to the '%s' network.") % self.factory.network
|
||||
logger.log_infomsg('IMC2: %s' % auth_message)
|
||||
msg_info(auth_message)
|
||||
logger.log_infomsg('IMC2: Authenticated to %s' % self.factory.network)
|
||||
|
||||
# Ask to see what other MUDs are connected.
|
||||
self.send_packet(pck.IMC2PacketKeepAliveRequest())
|
||||
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())
|
||||
self._send_packet(pck.IMC2PacketIsAlive())
|
||||
# Get a listing of channels.
|
||||
self.send_packet(pck.IMC2PacketIceRefresh())
|
||||
self._send_packet(pck.IMC2PacketIceRefresh())
|
||||
|
||||
def _msg_evennia(self, packet):
|
||||
def connectionMade(self):
|
||||
"""
|
||||
Handle the sending of packet data to Evennia channel
|
||||
(Message from IMC2 -> Evennia)
|
||||
Triggered after connecting to the IMC2 network.
|
||||
"""
|
||||
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
|
||||
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
|
||||
|
||||
# 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'))
|
||||
conn.to_channel(message)
|
||||
|
||||
def _format_tell(self, packet):
|
||||
"""
|
||||
Handle tells over IMC2 by formatting the text properly
|
||||
"""
|
||||
return _("{c%(sender)s@%(origin)s{n {wpages (over IMC):{n %(msg)s") % {"sender": packet.sender,
|
||||
"origin": packet.origin,
|
||||
"msg": packet.optional_data.get('text', 'ERROR: No text provided.')}
|
||||
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_infomsg("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):
|
||||
"""
|
||||
Triggered when text is received from the IMC2 network. Figures out
|
||||
what to do with the packet.
|
||||
IMC2 -> Evennia
|
||||
|
||||
Triggered when text is received from the IMC2 network. Figures out
|
||||
what to do with the packet. This deals with the following
|
||||
|
||||
"""
|
||||
line = line.strip()
|
||||
|
||||
if not self.is_authenticated:
|
||||
self._parse_auth_response(line)
|
||||
else:
|
||||
if IMC2_DEBUG and not 'is-alive' in line:
|
||||
# if IMC2_DEBUG mode is on, print the contents of the packet
|
||||
# to stdout.
|
||||
logger.log_infomsg("IMC2: RECV> %s" % line)
|
||||
# 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.factory.mudname, packet_str=line)
|
||||
#logger.log_infomsg("IMC2: RECV> %s" % 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))
|
||||
# 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.
|
||||
# Figure out what kind of packet we're dealing with and hand it
|
||||
# off to the correct handler.
|
||||
|
||||
if packet.packet_type == 'is-alive':
|
||||
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._msg_evennia(packet)
|
||||
elif packet.packet_type == 'whois-reply':
|
||||
handle_whois_reply(packet)
|
||||
elif packet.packet_type == 'close-notify':
|
||||
IMC2_MUDLIST.remove_mud_from_packet(packet)
|
||||
elif packet.packet_type == 'ice-update':
|
||||
IMC2_CHANLIST.update_channel_from_packet(packet)
|
||||
elif packet.packet_type == 'ice-destroy':
|
||||
IMC2_CHANLIST.remove_channel_from_packet(packet)
|
||||
elif packet.packet_type == 'tell':
|
||||
player = search.players(packet.target)
|
||||
if not player:
|
||||
return
|
||||
player[0].msg(self._format_tell(packet))
|
||||
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
|
||||
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 msg_imc2(self, message, from_obj=None, packet_type="imcbroadcast", data=None):
|
||||
def data_in(self, text=None, **kwargs):
|
||||
"""
|
||||
Called by Evennia to send a message through the imc2 connection
|
||||
Data IMC2 -> Evennia
|
||||
"""
|
||||
if from_obj:
|
||||
if hasattr(from_obj, 'key'):
|
||||
from_name = from_obj.key
|
||||
self.sessionhandler.data_in(self, text=text, **kwargs)
|
||||
|
||||
def data_out(self, text=None, **kwargs):
|
||||
"""
|
||||
Evennia -> IMC2
|
||||
|
||||
Keywords
|
||||
packet_type:
|
||||
broadcast - send to everyone on IMC channel
|
||||
tell - send a tell (see target keyword)
|
||||
whois - get whois information (see target keyword)
|
||||
sender - used by tell to identify the sender
|
||||
target - key identifier of target to tells or whois. If not
|
||||
given "Unknown" will be used.
|
||||
destination - 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:
|
||||
from_name = from_obj
|
||||
else:
|
||||
from_name = self.factory.mudname
|
||||
return
|
||||
|
||||
if packet_type == "imcbroadcast":
|
||||
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
|
||||
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
|
||||
if type(data) == dict:
|
||||
target = data.get("target", "Unknown")
|
||||
destination = data.get("destination", "Unknown")
|
||||
self.send_packet(pck.IMC2PacketTell(from_name, target, destination, message))
|
||||
# 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 == "imcwhois":
|
||||
elif packet_type == "whois":
|
||||
# send a whois request
|
||||
if type(data) == dict:
|
||||
target = data.get("target", "Unknown")
|
||||
self.send_packet(pck.IMC2PacketWhois(from_obj.id, target))
|
||||
sender = kwargs.get("sender", self.mudname)
|
||||
target = kwargs.get("target", "Unknown")
|
||||
self._send_packet(pck.IMC2PacketWhois(sender, target))
|
||||
|
||||
|
||||
class IMC2Factory(protocol.ClientFactory):
|
||||
class IMC2BotFactory(protocol.ReconnectingClientFactory):
|
||||
"""
|
||||
Creates instances of the IMC2Protocol. Should really only ever
|
||||
need to create one connection. Tied in via src/server.py.
|
||||
"""
|
||||
protocol = IMC2Protocol
|
||||
initialDelay = 1
|
||||
factor = 1.5
|
||||
maxDelay = 60
|
||||
|
||||
def __init__(self, network, port, mudname, client_pwd, server_pwd):
|
||||
self.pretty_key = "%s:%s(%s)" % (network, port, mudname)
|
||||
def __init__(self, sessionhandler, uid=None, network=None, channel=None,
|
||||
port=None, mudname=None, client_pwd=None, server_pwd=None):
|
||||
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"
|
||||
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):
|
||||
message = _('Connection failed: %s') % reason.getErrorMessage()
|
||||
msg_info(message)
|
||||
logger.log_errmsg('IMC2: %s' % message)
|
||||
self.retry(connector)
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
message = _('Connection lost: %s') % reason.getErrorMessage()
|
||||
msg_info(message)
|
||||
logger.log_errmsg('IMC2: %s' % message)
|
||||
if not self.bot.stopping:
|
||||
self.retry(connector)
|
||||
|
||||
def start(self):
|
||||
"Connect session to sessionhandler"
|
||||
def errback(fail):
|
||||
logger.log_errmsg(fail.value)
|
||||
|
||||
def build_connection_key(channel, imc2_channel):
|
||||
"Build an id hash for the connection"
|
||||
if hasattr(channel, "key"):
|
||||
channel = channel.key
|
||||
return "imc2_%s:%s(%s)%s<>%s" % (IMC2_NETWORK, IMC2_PORT,
|
||||
IMC2_MUDNAME, imc2_channel, channel)
|
||||
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)
|
||||
|
||||
|
||||
def start_scripts(validate=False):
|
||||
"""
|
||||
Start all the needed scripts
|
||||
"""
|
||||
|
||||
if validate:
|
||||
from src.scripts.models import ScriptDB
|
||||
ScriptDB.objects.validate()
|
||||
return
|
||||
if not search.scripts("IMC2_Send_IsAlive"):
|
||||
create.create_script(Send_IsAlive)
|
||||
if not search.scripts("IMC2_Send_Keepalive_Request"):
|
||||
create.create_script(Send_Keepalive_Request)
|
||||
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)
|
||||
|
||||
|
||||
def create_connection(channel, imc2_channel):
|
||||
"""
|
||||
This will create a new IMC2<->channel connection.
|
||||
"""
|
||||
|
||||
if not type(channel) == ChannelDB:
|
||||
new_channel = ChannelDB.objects.filter(db_key=channel)
|
||||
if not new_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.
|
||||
# connection already exists. We try to only connect a new channel
|
||||
old_config = old_conns[0].db_external_config.split(",")
|
||||
if imc2_channel in old_config:
|
||||
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()
|
||||
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
|
||||
send_code = "from src.comms.imc2 import IMC2_CLIENT\n"
|
||||
send_code += "data={'channel':from_channel}\n"
|
||||
send_code += "IMC2_CLIENT.msg_imc2(message, senders=[self])\n"
|
||||
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
|
||||
db_external_config=config)
|
||||
conn.save()
|
||||
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
|
||||
|
||||
|
||||
def connect_to_imc2():
|
||||
"Create the imc instance and connect to the IMC2 network."
|
||||
|
||||
# connect
|
||||
imc = internet.TCPClient(IMC2_NETWORK,
|
||||
int(IMC2_PORT),
|
||||
IMC2Factory(IMC2_NETWORK,
|
||||
IMC2_PORT,
|
||||
IMC2_MUDNAME,
|
||||
IMC2_CLIENT_PWD,
|
||||
IMC2_SERVER_PWD))
|
||||
imc.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.
|
||||
"""
|
||||
connect_to_imc2()
|
||||
start_scripts()
|
||||
|
|
|
|||
|
|
@ -23,19 +23,15 @@ class IMC2Mud(object):
|
|||
self.last_updated = time()
|
||||
|
||||
|
||||
class IMC2MudList(object):
|
||||
class IMC2MudList(dict):
|
||||
"""
|
||||
Keeps track of other MUDs connected to the IMC network.
|
||||
"""
|
||||
def __init__(self):
|
||||
# Mud list is stored in a dict, key being the IMC Mud name.
|
||||
self.mud_list = {}
|
||||
|
||||
def get_mud_list(self):
|
||||
"""
|
||||
Returns a sorted list of connected Muds.
|
||||
"""
|
||||
muds = self.mud_list.items()
|
||||
muds = self.items()
|
||||
muds.sort()
|
||||
return [value for key, value in muds]
|
||||
|
||||
|
|
@ -45,7 +41,7 @@ class IMC2MudList(object):
|
|||
Mud list for later retrieval.
|
||||
"""
|
||||
mud = IMC2Mud(packet)
|
||||
self.mud_list[mud.name] = mud
|
||||
self[mud.name] = mud
|
||||
|
||||
def remove_mud_from_packet(self, packet):
|
||||
"""
|
||||
|
|
@ -53,7 +49,7 @@ class IMC2MudList(object):
|
|||
"""
|
||||
mud = IMC2Mud(packet)
|
||||
try:
|
||||
del self.mud_list[mud.name]
|
||||
del self[mud.name]
|
||||
except KeyError:
|
||||
# No matching entry, no big deal.
|
||||
pass
|
||||
|
|
@ -72,19 +68,16 @@ class IMC2Channel(object):
|
|||
self.last_updated = time()
|
||||
|
||||
|
||||
class IMC2ChanList(object):
|
||||
class IMC2ChanList(dict):
|
||||
"""
|
||||
Keeps track of other MUDs connected to the IMC network.
|
||||
Keeps track of Channels on the IMC network.
|
||||
"""
|
||||
def __init__(self):
|
||||
# Chan list is stored in a dict, key being the IMC Mud name.
|
||||
self.chan_list = {}
|
||||
|
||||
def get_channel_list(self):
|
||||
"""
|
||||
Returns a sorted list of cached channels.
|
||||
"""
|
||||
channels = self.chan_list.items()
|
||||
channels = self.items()
|
||||
channels.sort()
|
||||
return [value for key, value in channels]
|
||||
|
||||
|
|
@ -94,7 +87,7 @@ class IMC2ChanList(object):
|
|||
channel list for later retrieval.
|
||||
"""
|
||||
channel = IMC2Channel(packet)
|
||||
self.chan_list[channel.name] = channel
|
||||
self[channel.name] = channel
|
||||
|
||||
def remove_channel_from_packet(self, packet):
|
||||
"""
|
||||
|
|
@ -102,7 +95,7 @@ class IMC2ChanList(object):
|
|||
"""
|
||||
channel = IMC2Channel(packet)
|
||||
try:
|
||||
del self.chan_list[channel.name]
|
||||
del self[channel.name]
|
||||
except KeyError:
|
||||
# No matching entry, no big deal.
|
||||
pass
|
||||
|
|
@ -123,218 +123,3 @@ class IRCBotFactory(protocol.ReconnectingClientFactory):
|
|||
if self.port:
|
||||
service = internet.TCPClient(self.network, int(self.port), self)
|
||||
self.sessionhandler.portal.services.addService(service)
|
||||
|
||||
|
||||
#
|
||||
#from twisted.application import internet
|
||||
#from twisted.words.protocols import irc
|
||||
#from twisted.internet import protocol
|
||||
#from django.conf import settings
|
||||
#from src.comms.models import ExternalChannelConnection, ChannelDB
|
||||
#from src.utils import logger, utils
|
||||
#from src.server.sessionhandler import SESSIONS
|
||||
#
|
||||
#from django.utils.translation import ugettext as _
|
||||
#
|
||||
#INFOCHANNEL = ChannelDB.objects.channel_search(settings.CHANNEL_MUDINFO[0])
|
||||
#IRC_CHANNELS = []
|
||||
#
|
||||
#def msg_info(message):
|
||||
# """
|
||||
# Send info to default info channel
|
||||
# """
|
||||
# message = '[%s][IRC]: %s' % (INFOCHANNEL[0].key, message)
|
||||
# try:
|
||||
# INFOCHANNEL[0].msg(message)
|
||||
# except AttributeError:
|
||||
# logger.log_infomsg("MUDinfo (irc): %s" % message)
|
||||
#
|
||||
#
|
||||
#class IRC_Bot(irc.IRCClient):
|
||||
# """
|
||||
# This defines an IRC bot that connects to an IRC channel
|
||||
# and relays data to and from an evennia game.
|
||||
# """
|
||||
#
|
||||
# def _get_nickname(self):
|
||||
# "required for correct nickname setting"
|
||||
# return self.factory.nickname
|
||||
# nickname = property(_get_nickname)
|
||||
#
|
||||
# def signedOn(self):
|
||||
# # This is the first point the protocol is instantiated.
|
||||
# # add this protocol instance to the global list so we
|
||||
# # can access it later to send data.
|
||||
# global IRC_CHANNELS
|
||||
# self.join(self.factory.channel)
|
||||
#
|
||||
# IRC_CHANNELS.append(self)
|
||||
# #msg_info("Client connecting to %s.'" % (self.factory.channel))
|
||||
#
|
||||
# def joined(self, channel):
|
||||
# msg = _("joined %s.") % self.factory.pretty_key
|
||||
# msg_info(msg)
|
||||
# logger.log_infomsg(msg)
|
||||
#
|
||||
# def get_mesg_info(self, user, irc_channel, msg):
|
||||
# """
|
||||
# Get basic information about a message posted in IRC.
|
||||
# """
|
||||
# #find irc->evennia channel mappings
|
||||
# conns = ExternalChannelConnection.objects.filter(db_external_key=self.factory.key)
|
||||
# if not conns:
|
||||
# return
|
||||
# #format message:
|
||||
# user = user.split("!")[0]
|
||||
# if user:
|
||||
# user.strip()
|
||||
# else:
|
||||
# user = _("Unknown")
|
||||
# msg = msg.strip()
|
||||
# sender_strings = ["%s@%s" % (user, irc_channel)]
|
||||
# return conns, msg, sender_strings
|
||||
#
|
||||
# def privmsg(self, user, irc_channel, msg):
|
||||
# "Someone has written something in irc channel. Echo it to the evennia channel"
|
||||
# conns, msg, sender_strings = self.get_mesg_info(user, irc_channel, msg)
|
||||
# #logger.log_infomsg("<IRC: " + msg)
|
||||
# for conn in conns:
|
||||
# if conn.channel:
|
||||
# conn.to_channel(msg, sender_strings=sender_strings)
|
||||
#
|
||||
# def action(self, user, irc_channel, msg):
|
||||
# "Someone has performed an action, e.g. using /me <pose>"
|
||||
# #find irc->evennia channel mappings
|
||||
# conns = ExternalChannelConnection.objects.filter(db_external_key=self.factory.key)
|
||||
# if not conns:
|
||||
# return
|
||||
# conns, msg, sender_strings = self.get_mesg_info(user, irc_channel, msg)
|
||||
# # Transform this into a pose.
|
||||
# msg = ':' + msg
|
||||
# #logger.log_infomsg("<IRC: " + msg)
|
||||
# for conn in conns:
|
||||
# if conn.channel:
|
||||
# conn.to_channel(msg, sender_strings=sender_strings)
|
||||
#
|
||||
# def msg_irc(self, msg, senders=None):
|
||||
# """
|
||||
# Called by evennia when sending something to mapped IRC channel.
|
||||
#
|
||||
# Note that this cannot simply be called msg() since that's the
|
||||
# name of of the twisted irc hook as well, this leads to some
|
||||
# initialization messages to be sent without checks, causing loops.
|
||||
# """
|
||||
# self.msg(utils.to_str(self.factory.channel), utils.to_str(msg))
|
||||
#
|
||||
#
|
||||
#class IRCbotFactory(protocol.ClientFactory):
|
||||
# protocol = IRC_Bot
|
||||
#
|
||||
# def __init__(self, key, channel, network, port, nickname, evennia_channel):
|
||||
# self.key = key
|
||||
# self.pretty_key = "%s:%s%s ('%s')" % (network, port, channel, nickname)
|
||||
# self.network = network
|
||||
# self.port = port
|
||||
# self.channel = channel
|
||||
# self.nickname = nickname
|
||||
# self.evennia_channel = evennia_channel
|
||||
#
|
||||
# def clientConnectionLost(self, connector, reason):
|
||||
# from twisted.internet.error import ConnectionDone
|
||||
# if type(reason.type) == type(ConnectionDone):
|
||||
# msg_info(_("Connection closed."))
|
||||
# else:
|
||||
# msg_info(_("Lost connection %(key)s. Reason: '%(reason)s'. Reconnecting.") % {"key":self.pretty_key, "reason":reason})
|
||||
# connector.connect()
|
||||
#
|
||||
# def clientConnectionFailed(self, connector, reason):
|
||||
# msg = _("Could not connect %(key)s Reason: '%(reason)s'") % {"key":self.pretty_key, "reason":reason}
|
||||
# msg_info(msg)
|
||||
# logger.log_errmsg(msg)
|
||||
#
|
||||
#
|
||||
#def build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
|
||||
# "Build an id hash for the connection"
|
||||
# if hasattr(channel, 'key'):
|
||||
# channel = channel.key
|
||||
# return "irc_%s:%s%s(%s)<>%s" % (irc_network, irc_port,
|
||||
# irc_channel, irc_bot_nick, channel)
|
||||
#
|
||||
#
|
||||
#def build_service_key(key):
|
||||
# return "IRCbot:%s" % key
|
||||
#
|
||||
#
|
||||
#def create_connection(channel, irc_network, irc_port,
|
||||
# irc_channel, irc_bot_nick):
|
||||
# """
|
||||
# This will create a new IRC<->channel connection.
|
||||
# """
|
||||
# if not type(channel) == ChannelDB:
|
||||
# new_channel = ChannelDB.objects.filter(db_key=channel)
|
||||
# if not new_channel:
|
||||
# logger.log_errmsg(_("Cannot attach IRC<->Evennia: Evennia Channel '%s' not found") % channel)
|
||||
# return False
|
||||
# channel = new_channel[0]
|
||||
# key = build_connection_key(channel, irc_network, irc_port,
|
||||
# irc_channel, irc_bot_nick)
|
||||
#
|
||||
# old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
|
||||
# if old_conns:
|
||||
# return False
|
||||
# config = "%s|%s|%s|%s" % (irc_network, irc_port, irc_channel, irc_bot_nick)
|
||||
# # how the channel will be able to contact this protocol
|
||||
# send_code = "from src.comms.irc import IRC_CHANNELS\n"
|
||||
# send_code += "matched_ircs = [irc for irc in IRC_CHANNELS if irc.factory.key == '%s']\n" % key
|
||||
# send_code += "[irc.msg_irc(message, senders=[self]) for irc in matched_ircs]\n"
|
||||
# conn = ExternalChannelConnection(db_channel=channel,
|
||||
# db_external_key=key,
|
||||
# db_external_send_code=send_code,
|
||||
# db_external_config=config)
|
||||
# conn.save()
|
||||
#
|
||||
# # connect
|
||||
# connect_to_irc(conn)
|
||||
# return True
|
||||
#
|
||||
#def delete_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
|
||||
# "Destroy a connection"
|
||||
# if hasattr(channel, 'key'):
|
||||
# channel = channel.key
|
||||
#
|
||||
# key = build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick)
|
||||
# service_key = build_service_key(key)
|
||||
# try:
|
||||
# conn = ExternalChannelConnection.objects.get(db_external_key=key)
|
||||
# except Exception:
|
||||
# return False
|
||||
# conn.delete()
|
||||
#
|
||||
# try:
|
||||
# service = SESSIONS.server.services.getServiceNamed(service_key)
|
||||
# except Exception:
|
||||
# return True
|
||||
# if service.running:
|
||||
# SESSIONS.server.services.removeService(service)
|
||||
# return True
|
||||
#
|
||||
#def connect_to_irc(connection):
|
||||
# "Create the bot instance and connect to the IRC network and channel."
|
||||
# # get config
|
||||
# key = utils.to_str(connection.external_key)
|
||||
# service_key = build_service_key(key)
|
||||
# irc_network, irc_port, irc_channel, irc_bot_nick = [utils.to_str(conf) for conf in connection.external_config.split('|')]
|
||||
# # connect
|
||||
# bot = internet.TCPClient(irc_network, int(irc_port), IRCbotFactory(key, irc_channel, irc_network, irc_port, irc_bot_nick,
|
||||
# connection.channel.key))
|
||||
# bot.setName(service_key)
|
||||
# SESSIONS.server.services.addService(bot)
|
||||
#
|
||||
#def connect_all():
|
||||
# """
|
||||
# Activate all irc bots.
|
||||
# """
|
||||
# for connection in ExternalChannelConnection.objects.filter(db_external_key__startswith='irc_'):
|
||||
# connect_to_irc(connection)
|
||||
#
|
||||
#
|
||||
|
|
|
|||
|
|
@ -6,20 +6,13 @@ to the channel whenever the feed updates.
|
|||
|
||||
"""
|
||||
|
||||
import re
|
||||
from twisted.internet import task, threads
|
||||
from django.conf import settings
|
||||
from src.comms.models import ExternalChannelConnection, ChannelDB
|
||||
from src.server.session import Session
|
||||
from src.utils import logger, utils
|
||||
from src.utils import logger
|
||||
|
||||
RSS_ENABLED = settings.RSS_ENABLED
|
||||
RSS_UPDATE_INTERVAL = settings.RSS_UPDATE_INTERVAL
|
||||
INFOCHANNEL = ChannelDB.objects.channel_search(settings.CHANNEL_MUDINFO[0])
|
||||
RETAG = re.compile(r'<[^>]*?>')
|
||||
|
||||
# holds rss readers they can be shut down at will.
|
||||
RSS_READERS = {}
|
||||
#RETAG = re.compile(r'<[^>]*?>')
|
||||
|
||||
if RSS_ENABLED:
|
||||
try:
|
||||
|
|
@ -27,7 +20,6 @@ if RSS_ENABLED:
|
|||
except ImportError:
|
||||
raise ImportError("RSS requires python-feedparser to be installed. Install or set RSS_ENABLED=False.")
|
||||
|
||||
|
||||
class RSSReader(Session):
|
||||
"""
|
||||
A simple RSS reader using universal feedparser
|
||||
|
|
@ -68,7 +60,7 @@ class RSSReader(Session):
|
|||
|
||||
def _errback(self, fail):
|
||||
"Report error"
|
||||
print "RSS feed error: %s" % fail.value
|
||||
logger.log_errmsg("RSS feed error: %s" % fail.value)
|
||||
|
||||
def update(self, init=False):
|
||||
"Request feed"
|
||||
|
|
@ -93,143 +85,16 @@ class RSSBotFactory(object):
|
|||
Called by portalsessionhandler
|
||||
"""
|
||||
def errback(fail):
|
||||
print fail.value
|
||||
logger.log_errmsg(fail.value)
|
||||
|
||||
# set up session and connect it to sessionhandler
|
||||
self.bot.init_session("rssbot", self.url, self.sessionhandler)
|
||||
self.bot.uid = self.uid
|
||||
self.bot.logged_in = True
|
||||
self.sessionhandler.connect(self.bot)
|
||||
|
||||
# start repeater task
|
||||
#self.bot.update(init=True)
|
||||
self.bot.update(init=True)
|
||||
self.task = task.LoopingCall(self.bot.update)
|
||||
if self.rate:
|
||||
self.task.start(self.rate, now=False).addErrback(errback)
|
||||
|
||||
#class RSSReader(object):
|
||||
# """
|
||||
# Reader script used to connect to each individual RSS feed
|
||||
# """
|
||||
# def __init__(self, key, url, interval):
|
||||
# """
|
||||
# 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)
|
||||
# """
|
||||
# self.key = key
|
||||
# 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()
|
||||
# # 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'))
|
||||
# self.entries[entry['id']] = txt
|
||||
# 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.
|
||||
# """
|
||||
# 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:
|
||||
# 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.
|
||||
# """
|
||||
# global RSS_READERS
|
||||
# self.task = task.LoopingCall(self.update)
|
||||
# self.task.start(self.interval, now=False)
|
||||
# RSS_READERS[self.key] = self
|
||||
#
|
||||
#
|
||||
#def build_connection_key(channel, url):
|
||||
# "This is used to id the connection"
|
||||
# if hasattr(channel, 'key'):
|
||||
# channel = channel.key
|
||||
# return "rss_%s>%s" % (url, channel)
|
||||
#
|
||||
#
|
||||
#def create_connection(channel, url, interval):
|
||||
# """
|
||||
# This will create a new RSS->channel connection
|
||||
# """
|
||||
# if not type(channel) == ChannelDB:
|
||||
# new_channel = ChannelDB.objects.filter(db_key=channel)
|
||||
# if not new_channel:
|
||||
# logger.log_errmsg("Cannot attach RSS->Evennia: Evennia Channel '%s' not found." % channel)
|
||||
# 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
|
||||
# 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()
|
||||
#
|
||||
# connect_to_rss(conn)
|
||||
# return True
|
||||
#
|
||||
#
|
||||
#def delete_connection(channel, url):
|
||||
# """
|
||||
# Delete rss connection between channel and url
|
||||
# """
|
||||
# key = build_connection_key(channel, url)
|
||||
# try:
|
||||
# conn = ExternalChannelConnection.objects.get(db_external_key=key)
|
||||
# except Exception:
|
||||
# return False
|
||||
# conn.delete()
|
||||
# reader = RSS_READERS.get(key, None)
|
||||
# if reader and reader.task:
|
||||
# reader.task.stop()
|
||||
# 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)
|
||||
# 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
|
||||
# """
|
||||
# if not RSS_ENABLED:
|
||||
# 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