mirror of
https://github.com/evennia/evennia.git
synced 2026-03-18 22:06:30 +01:00
156 lines
5.2 KiB
Python
156 lines
5.2 KiB
Python
"""
|
|
RSS parser for Evennia
|
|
|
|
This connects an RSS feed to an in-game Evennia channel, sending messages
|
|
to the channel whenever the feed updates.
|
|
|
|
"""
|
|
|
|
import re
|
|
from twisted.internet import task
|
|
from django.conf import settings
|
|
from src.comms.models import ExternalChannelConnection, Channel
|
|
from src.utils import logger, utils
|
|
from src.scripts.models import ScriptDB
|
|
|
|
RSS_ENABLED = settings.RSS_ENABLED
|
|
RSS_UPDATE_INTERVAL = settings.RSS_UPDATE_INTERVAL
|
|
INFOCHANNEL = Channel.objects.channel_search(settings.CHANNEL_MUDINFO[0])
|
|
RETAG = re.compile(r'<[^>]*?>')
|
|
|
|
# holds rss readers they can be shut down at will.
|
|
RSS_READERS = {}
|
|
|
|
def msg_info(message):
|
|
"""
|
|
Send info to default info channel
|
|
"""
|
|
message = '[%s][RSS]: %s' % (INFOCHANNEL[0].key, message)
|
|
try:
|
|
INFOCHANNEL[0].msg(message)
|
|
except AttributeError:
|
|
logger.log_infomsg("MUDinfo (rss): %s" % message)
|
|
|
|
if RSS_ENABLED:
|
|
try:
|
|
import feedparser
|
|
except ImportError:
|
|
raise ImportError("RSS requirs python-feedparser to be installed. Install or set RSS_ENABLED=False.")
|
|
|
|
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) == Channel:
|
|
new_channel = Channel.objects.filter(db_key=channel)
|
|
if not new_channel:
|
|
logger.log_errmsg("Cannot attach RSS->Evennia: Evennia Channel '%s' not found." % channel)
|
|
return False
|
|
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)
|