Faster startup time and replace ' with "

This commit is contained in:
Andreas Bielawski 2019-01-05 15:14:54 +01:00
parent fafd04eb39
commit 1fd46d9a8f

173
bot.py
View File

@ -11,6 +11,7 @@ from urllib.parse import urlparse
import feedparser import feedparser
import redis import redis
import telegram import telegram
from telegram.error import Unauthorized
from telegram.ext import CommandHandler, Updater from telegram.ext import CommandHandler, Updater
from telegram.ext.dispatcher import run_async from telegram.ext.dispatcher import run_async
@ -18,9 +19,9 @@ import utils
config = ConfigParser() config = ConfigParser()
try: try:
config.read_file(open('config.ini')) config.read_file(open("config.ini"))
except FileNotFoundError: except FileNotFoundError:
logging.critical('Config.ini nicht gefunden') logging.critical("Config.ini nicht gefunden")
sys.exit(1) sys.exit(1)
# Logging # Logging
@ -43,35 +44,35 @@ logger = logging.getLogger(__name__)
# Bot token # Bot token
try: try:
bot_token = config['DEFAULT']['token'] bot_token = config["DEFAULT"]["token"]
except KeyError: except KeyError:
logger.error('Kein Bot-Token gesetzt, bitte config.ini prüfen') logger.error("Kein Bot-Token gesetzt, bitte config.ini prüfen")
sys.exit(1) sys.exit(1)
if not bot_token: if not bot_token:
logger.error('Kein Bot-Token gesetzt, bitte config.ini prüfen') logger.error("Kein Bot-Token gesetzt, bitte config.ini prüfen")
sys.exit(1) sys.exit(1)
# Admins # Admins
try: try:
admins = loads(config["ADMIN"]["id"]) admins = loads(config["ADMIN"]["id"])
except KeyError: except KeyError:
logger.error('Keine Admin-IDs gesetzt, bitte config.ini prüfen.') logger.error("Keine Admin-IDs gesetzt, bitte config.ini prüfen.")
sys.exit(1) sys.exit(1)
if not admins: if not admins:
logger.error('Keine Admin-IDs gesetzt, bitte config.ini prüfen.') logger.error("Keine Admin-IDs gesetzt, bitte config.ini prüfen.")
sys.exit(1) sys.exit(1)
for admin in admins: for admin in admins:
if not isinstance(admin, int): if not isinstance(admin, int):
logger.error('Admin-IDs müssen Integer sein.') logger.error("Admin-IDs müssen Integer sein.")
sys.exit(1) sys.exit(1)
# Redis # Redis
redis_conf = config['REDIS'] redis_conf = config["REDIS"]
redis_db = redis_conf.get('db', 0) redis_db = redis_conf.get("db", 0)
redis_host = redis_conf.get('host', '127.0.0.1') redis_host = redis_conf.get("host", "127.0.0.1")
redis_port = redis_conf.get('port', 6379) redis_port = redis_conf.get("port", 6379)
redis_socket = redis_conf.get('socket_path') redis_socket = redis_conf.get("socket_path")
if redis_socket: if redis_socket:
r = redis.Redis(unix_socket_path=redis_socket, db=int(redis_db), decode_responses=True) r = redis.Redis(unix_socket_path=redis_socket, db=int(redis_db), decode_responses=True)
else: else:
@ -81,7 +82,7 @@ if not r.ping():
logging.getLogger("Redis").critical("Redis-Verbindungsfehler, config.ini prüfen") logging.getLogger("Redis").critical("Redis-Verbindungsfehler, config.ini prüfen")
sys.exit(1) sys.exit(1)
feed_hash = 'pythonbot:rss:{0}' feed_hash = "pythonbot:rss:{0}"
@run_async @run_async
@ -89,7 +90,7 @@ def start(bot, update):
if not utils.can_use_bot(update): if not utils.can_use_bot(update):
return return
update.message.reply_text( update.message.reply_text(
text='<b>Willkommen beim RSS-Bot!</b>\nSende /help, um zu starten.', text="<b>Willkommen beim RSS-Bot!</b>\nSende /help, um zu starten.",
parse_mode=telegram.ParseMode.HTML parse_mode=telegram.ParseMode.HTML
) )
@ -99,9 +100,9 @@ def help_text(bot, update):
if not utils.can_use_bot(update): if not utils.can_use_bot(update):
return return
update.message.reply_text( update.message.reply_text(
text='<b>/rss</b> <i>[Chat]</i>: Abonnierte Feeds anzeigen\n' text="<b>/rss</b> <i>[Chat]</i>: Abonnierte Feeds anzeigen\n"
'<b>/sub</b> <i>Feed-URL</i> <i>[Chat]</i>: Feed abonnieren\n' "<b>/sub</b> <i>Feed-URL</i> <i>[Chat]</i>: Feed abonnieren\n"
'<b>/del</b> <i>n</i> <i>[Chat]</i>: Feed löschen', "<b>/del</b> <i>n</i> <i>[Chat]</i>: Feed löschen",
parse_mode=telegram.ParseMode.HTML parse_mode=telegram.ParseMode.HTML
) )
@ -115,24 +116,24 @@ def list_feeds(bot, update, args):
try: try:
resp = bot.getChat(chat_name) resp = bot.getChat(chat_name)
except telegram.error.BadRequest: except telegram.error.BadRequest:
update.message.reply_text('❌ Dieser Kanal existiert nicht.') update.message.reply_text("❌ Dieser Kanal existiert nicht.")
return return
chat_id = str(resp.id) chat_id = str(resp.id)
chat_title = resp.title chat_title = resp.title
else: else:
chat_id = str(update.message.chat.id) chat_id = str(update.message.chat.id)
if update.message.chat.type == 'private': if update.message.chat.type == "private":
chat_title = update.message.chat.first_name chat_title = update.message.chat.first_name
else: else:
chat_title = update.message.chat.title chat_title = update.message.chat.title
subs = r.smembers(feed_hash.format(chat_id)) subs = r.smembers(feed_hash.format(chat_id))
if not subs: if not subs:
text = '❌ Keine Feeds abonniert.' text = "❌ Keine Feeds abonniert."
else: else:
text = '<b>' + html.escape(chat_title) + '</b> hat abonniert:\n' text = "<b>" + html.escape(chat_title) + "</b> hat abonniert:\n"
for n, feed in enumerate(subs): for n, feed in enumerate(subs):
text += '<b>' + str(n + 1) + ')</b> ' + feed + '\n' text += "<b>" + str(n + 1) + ")</b> " + feed + "\n"
update.message.reply_text( update.message.reply_text(
text=text, text=text,
@ -145,11 +146,11 @@ def subscribe(bot, update, args):
if not utils.can_use_bot(update): if not utils.can_use_bot(update):
return return
if not args: if not args:
update.message.reply_text('❌ Keine Feed-URL angegeben.') update.message.reply_text("❌ Keine Feed-URL angegeben.")
return return
feed_url = args[0] feed_url = args[0]
if not re.match("^http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&~+]|[!*(),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+$", feed_url): if not re.match("^http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&~+]|[!*(),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+$", feed_url):
update.message.reply_text('❌ Das ist keine URL.') update.message.reply_text("❌ Das ist keine URL.")
return return
# Get Chat ID from name if given # Get Chat ID from name if given
@ -158,45 +159,45 @@ def subscribe(bot, update, args):
try: try:
resp = bot.getChat(chat_name) resp = bot.getChat(chat_name)
except telegram.error.BadRequest: except telegram.error.BadRequest:
update.message.reply_text('❌ Dieser Kanal existiert nicht.') update.message.reply_text("❌ Dieser Kanal existiert nicht.")
return return
chat_id = str(resp.id) chat_id = str(resp.id)
resp = bot.getChatMember(chat_id, bot.id) resp = bot.getChatMember(chat_id, bot.id)
if resp.status != 'administrator': if resp.status != "administrator":
update.message.reply_text('❌ Bot ist kein Administrator in diesem Kanal.') update.message.reply_text("❌ Bot ist kein Administrator in diesem Kanal.")
return return
else: else:
chat_id = str(update.message.chat.id) chat_id = str(update.message.chat.id)
bot.sendChatAction(update.message.chat.id, action=telegram.ChatAction.TYPING) bot.sendChatAction(update.message.chat.id, action=telegram.ChatAction.TYPING)
data = feedparser.parse(feed_url) data = feedparser.parse(feed_url)
if 'link' not in data.feed: if "link" not in data.feed:
update.message.reply_text('❌ Kein gültiger Feed.') update.message.reply_text("❌ Kein gültiger Feed.")
return return
feed_url = data.href # Follow all redirects feed_url = data.href # Follow all redirects
if r.sismember(feed_hash.format(chat_id), feed_url): if r.sismember(feed_hash.format(chat_id), feed_url):
update.message.reply_text('✅ Dieser Feed wurde bereits abonniert.') update.message.reply_text("✅ Dieser Feed wurde bereits abonniert.")
return return
if 'title' not in data.feed: if "title" not in data.feed:
feed_title = feed_url feed_title = feed_url
else: else:
feed_title = html.escape(data.feed['title']) feed_title = html.escape(data.feed["title"])
# Save the last entry in Redis, if it doesn't exist # Save the last entry in Redis, if it doesn't exist
if data.entries: if data.entries:
last_entry_hash = feed_hash.format(feed_url + ':last_entry') last_entry_hash = feed_hash.format(feed_url + ":last_entry")
if not r.exists(last_entry_hash): if not r.exists(last_entry_hash):
if 'id' not in data.entries[0]: if "id" not in data.entries[0]:
last_entry = data.entries[0]['link'] last_entry = data.entries[0]["link"]
else: else:
last_entry = data.entries[0]['id'] last_entry = data.entries[0]["id"]
r.set(last_entry_hash, last_entry) r.set(last_entry_hash, last_entry)
r.sadd(feed_hash.format(feed_url + ':subs'), chat_id) r.sadd(feed_hash.format(feed_url + ":subs"), chat_id)
r.sadd(feed_hash.format(chat_id), feed_url) r.sadd(feed_hash.format(chat_id), feed_url)
update.message.reply_text( update.message.reply_text(
text='✅ <b>' + feed_title + '</b> hinzugefügt!', text="✅ <b>" + feed_title + "</b> hinzugefügt!",
parse_mode=telegram.ParseMode.HTML parse_mode=telegram.ParseMode.HTML
) )
@ -206,7 +207,7 @@ def unsubscribe(bot, update, args):
if not utils.can_use_bot(update): if not utils.can_use_bot(update):
return return
if not args: if not args:
update.message.reply_text('❌ Keine Nummer angegeben.') update.message.reply_text("❌ Keine Nummer angegeben.")
return return
# Get Chat ID from name if given # Get Chat ID from name if given
@ -215,7 +216,7 @@ def unsubscribe(bot, update, args):
try: try:
resp = bot.getChat(chat_name) resp = bot.getChat(chat_name)
except telegram.error.BadRequest: except telegram.error.BadRequest:
update.message.reply_text('❌ Dieser Kanal existiert nicht.') update.message.reply_text("❌ Dieser Kanal existiert nicht.")
return return
chat_id = str(resp.id) chat_id = str(resp.id)
else: else:
@ -224,78 +225,78 @@ def unsubscribe(bot, update, args):
try: try:
n = int(args[0]) n = int(args[0])
except ValueError: except ValueError:
update.message.reply_text('❌ Keine Nummer angegeben.') update.message.reply_text("❌ Keine Nummer angegeben.")
return return
chat_hash = feed_hash.format(chat_id) chat_hash = feed_hash.format(chat_id)
subs = r.smembers(chat_hash) subs = r.smembers(chat_hash)
if n < 1: if n < 1:
update.message.reply_text('❌ Nummer muss größer als 0 sein!') update.message.reply_text("❌ Nummer muss größer als 0 sein!")
return return
elif n > len(subs): elif n > len(subs):
update.message.reply_text('❌ Feed-ID zu hoch.') update.message.reply_text("❌ Feed-ID zu hoch.")
return return
feed_url = list(subs)[n - 1] feed_url = list(subs)[n - 1]
sub_hash = feed_hash.format(feed_url + ':subs') sub_hash = feed_hash.format(feed_url + ":subs")
r.srem(chat_hash, feed_url) r.srem(chat_hash, feed_url)
r.srem(sub_hash, chat_id) r.srem(sub_hash, chat_id)
if not r.smembers(sub_hash): # no one subscribed, remove it if not r.smembers(sub_hash): # no one subscribed, remove it
r.delete(feed_hash.format(feed_url + ':last_entry')) r.delete(feed_hash.format(feed_url + ":last_entry"))
update.message.reply_text( update.message.reply_text(
text='✅ <b>' + feed_url + '</b> entfernt.', text="✅ <b>" + feed_url + "</b> entfernt.",
parse_mode=telegram.ParseMode.HTML parse_mode=telegram.ParseMode.HTML
) )
@run_async @run_async
def check_feed(bot, key): def check_feed(bot, key):
feed_url = re.match('^' + feed_hash.format('(.+):subs$'), key).group(1) feed_url = re.match("^" + feed_hash.format("(.+):subs$"), key).group(1)
logger.info(feed_url) logger.info(feed_url)
data = feedparser.parse(feed_url) data = feedparser.parse(feed_url)
if 'link' not in data.feed: if "link" not in data.feed:
if 'status' in data and data["status"] != 200: if "status" in data and data["status"] != 200:
logger.warning(feed_url + ' - Kein gültiger Feed, HTTP-Status-Code ' + str(data["status"])) logger.warning(feed_url + " - Kein gültiger Feed, HTTP-Status-Code " + str(data["status"]))
else: else:
logger.warning(feed_url + ' - Kein gültiger Feed: ' + str(data.bozo_exception)) logger.warning(feed_url + " - Kein gültiger Feed: " + str(data.bozo_exception))
return None return None
if 'title' not in data.feed: if "title" not in data.feed:
feed_title = data.feed['link'] feed_title = data.feed["link"]
else: else:
feed_title = data.feed['title'] feed_title = data.feed["title"]
last_entry_hash = feed_hash.format(feed_url + ':last_entry') last_entry_hash = feed_hash.format(feed_url + ":last_entry")
last_entry = r.get(last_entry_hash) last_entry = r.get(last_entry_hash)
new_entries = utils.get_new_entries(data.entries, last_entry) new_entries = utils.get_new_entries(data.entries, last_entry)
for entry in reversed(new_entries): for entry in reversed(new_entries):
if 'title' not in entry: if "title" not in entry:
post_title = 'Kein Titel' post_title = "Kein Titel"
else: else:
post_title = utils.remove_html_tags(entry['title']).strip() post_title = utils.remove_html_tags(entry["title"]).strip()
post_title = post_title.replace('<', '&lt;').replace('>', '&gt;') post_title = post_title.replace("<", "&lt;").replace(">", "&gt;")
if 'link' not in entry: if "link" not in entry:
post_link = data.link post_link = data.link
link_name = post_link link_name = post_link
else: else:
post_link = entry.link post_link = entry.link
feedproxy = re.search('^https?://feedproxy\.google\.com/~r/(.+?)/.*', post_link) # feedproxy.google.com feedproxy = re.search("^https?://feedproxy\.google\.com/~r/(.+?)/.*", post_link) # feedproxy.google.com
if feedproxy: if feedproxy:
link_name = feedproxy.group(1) link_name = feedproxy.group(1)
else: else:
link_name = urlparse(post_link).netloc link_name = urlparse(post_link).netloc
link_name = re.sub('^www\d?\.', '', link_name) # remove www. link_name = re.sub("^www\d?\.", "", link_name) # remove www.
if 'content' in entry: if "content" in entry:
content = utils.get_content(entry.content[0]['value']) content = utils.get_content(entry.content[0]["value"])
elif 'summary' in entry: elif "summary" in entry:
content = utils.get_content(entry.summary) content = utils.get_content(entry.summary)
else: else:
content = '' content = ""
text = '<b>{post_title}</b>\n<i>{feed_title}</i>\n{content}'.format( text = "<b>{post_title}</b>\n<i>{feed_title}</i>\n{content}".format(
post_title=post_title, post_title=post_title,
feed_title=feed_title, feed_title=feed_title,
content=content content=content
) )
text += '\n<a href="{post_link}">Auf {link_name} weiterlesen</a>\n'.format( text += "\n<a href=\"{post_link}\">Auf {link_name} weiterlesen</a>\n".format(
post_link=post_link, post_link=post_link,
link_name=link_name link_name=link_name
) )
@ -308,12 +309,12 @@ def check_feed(bot, key):
disable_web_page_preview=True disable_web_page_preview=True
) )
except telegram.error.Unauthorized: except telegram.error.Unauthorized:
logger.warning('Chat ' + member + ' existiert nicht mehr, wird gelöscht.') logger.warning("Chat " + member + " existiert nicht mehr, wird gelöscht.")
r.srem(key, member) r.srem(key, member)
r.delete(feed_hash.format(member)) r.delete(feed_hash.format(member))
except telegram.error.ChatMigrated as new_chat: except telegram.error.ChatMigrated as new_chat:
new_chat_id = new_chat.new_chat_id new_chat_id = new_chat.new_chat_id
logger.info('Chat migriert: ' + member + ' -> ' + str(new_chat_id)) logger.info("Chat migriert: " + member + " -> " + str(new_chat_id))
r.srem(key, member) r.srem(key, member)
r.sadd(key, new_chat_id) r.sadd(key, new_chat_id)
r.rename(feed_hash.format(member), feed_hash.format(new_chat_id)) r.rename(feed_hash.format(member), feed_hash.format(new_chat_id))
@ -334,7 +335,7 @@ def check_feed(bot, key):
# Set the new last entry if there are any # Set the new last entry if there are any
if new_entries: if new_entries:
if 'id' not in new_entries[0]: if "id" not in new_entries[0]:
new_last_entry = new_entries[0].link new_last_entry = new_entries[0].link
else: else:
new_last_entry = new_entries[0].id new_last_entry = new_entries[0].id
@ -343,8 +344,8 @@ def check_feed(bot, key):
@run_async @run_async
def run_job(bot, job=None): def run_job(bot, job=None):
logger.info('================================') logger.info("================================")
keys = r.keys(feed_hash.format('*:subs')) keys = r.keys(feed_hash.format("*:subs"))
for key in keys: for key in keys:
check_feed(bot, key) check_feed(bot, key)
@ -358,27 +359,25 @@ def main():
# Setup the updater and show bot info # Setup the updater and show bot info
updater = Updater(token=bot_token) updater = Updater(token=bot_token)
try: try:
bot_info = updater.bot.getMe() logger.info("Starte {0}, AKA @{1} ({2})".format(updater.bot.first_name, updater.bot.username, updater.bot.id))
except telegram.error.Unauthorized: except Unauthorized:
logger.error('Anmeldung nicht möglich, Bot-Token falsch?') logger.critical("Anmeldung nicht möglich, Bot-Token falsch?")
sys.exit(1) sys.exit(1)
logger.info('Starte ' + bot_info.first_name + ', AKA @' + bot_info.username + ' (' + str(bot_info.id) + ')')
# Register Handlers # Register Handlers
handlers = [ handlers = [
CommandHandler('start', start), CommandHandler("start", start),
CommandHandler('help', help_text), CommandHandler("help", help_text),
CommandHandler('rss', list_feeds, pass_args=True), CommandHandler("rss", list_feeds, pass_args=True),
CommandHandler('sub', subscribe, pass_args=True), CommandHandler("sub", subscribe, pass_args=True),
CommandHandler('del', unsubscribe, pass_args=True), CommandHandler("del", unsubscribe, pass_args=True),
CommandHandler('sync', run_job) CommandHandler("sync", run_job)
] ]
for handler in handlers: for handler in handlers:
updater.dispatcher.add_handler(handler) updater.dispatcher.add_handler(handler)
# Hide "Error while getting Updates" because it's not our fault # Hide "Error while getting Updates" because it's not our fault
updater.logger.addFilter((lambda log: not log.msg.startswith('Error while getting Updates:'))) updater.logger.addFilter((lambda log: not log.msg.startswith("Error while getting Updates:")))
# Fix for Python <= 3.5 # Fix for Python <= 3.5
updater.dispatcher.add_error_handler(onerror) updater.dispatcher.add_error_handler(onerror)
@ -401,5 +400,5 @@ def main():
updater.idle() updater.idle()
if __name__ == '__main__': if __name__ == "__main__":
main() main()