You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
stacosys/app/core/processor.py

201 lines
5.3 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import logging
import re
6 years ago
import PyRSS2Gen
import markdown
import json
from datetime import datetime
from threading import Thread
from queue import Queue
from jinja2 import Environment
from jinja2 import FileSystemLoader
from models.site import Site
from models.comment import Comment
from helpers.hashing import md5
from conf import config
6 years ago
from core import mailer
logger = logging.getLogger(__name__)
queue = Queue()
proc = None
env = None
6 years ago
# keep client IP in memory until classified
7 years ago
client_ips = {}
class Processor(Thread):
def stop(self):
logger.info("stop requested")
self.is_running = False
def run(self):
6 years ago
logger.info("processor thread started")
self.is_running = True
while self.is_running:
try:
msg = queue.get()
6 years ago
if msg["request"] == "new_mail":
reply_comment_email(msg["data"])
send_delete_command(msg["data"])
else:
logger.info("throw unknown request " + str(msg))
except:
logger.exception("processing failure")
def reply_comment_email(data):
6 years ago
from_email = data["from"]
subject = data["subject"]
message = ""
for part in data["parts"]:
if part["content-type"] == "text/plain":
message = part["content"]
break
6 years ago
m = re.search("\[(\d+)\:(\w+)\]", subject)
if not m:
6 years ago
logger.warn("ignore corrupted email. No token %s" % subject)
return
comment_id = int(m.group(1))
token = m.group(2)
# retrieve site and comment rows
7 years ago
try:
comment = Comment.select().where(Comment.id == comment_id).get()
except:
6 years ago
logger.warn("unknown comment %d" % comment_id)
return
if comment.published:
6 years ago
logger.warn("ignore already published email. token %d" % comment_id)
7 years ago
return
if comment.site.token != token:
6 years ago
logger.warn("ignore corrupted email. Unknown token %d" % comment_id)
return
if not message:
6 years ago
logger.warn("ignore empty email")
return
# safe logic: no answer or unknown answer is a go for publishing
6 years ago
if message[:2].upper() in ("NO", "SP"):
7 years ago
# put a log to help fail2ban
6 years ago
if message[:2].upper() == "SP": # SPAM
7 years ago
if comment_id in client_ips:
6 years ago
logger.info(
"SPAM comment from %s: %d" % (client_ips[comment_id], comment_id)
)
7 years ago
else:
6 years ago
logger.info("cannot identify SPAM source: %d" % comment_id)
7 years ago
# forget client IP
if comment_id in client_ips:
del client_ips[comment_id]
6 years ago
logger.info("discard comment: %d" % comment_id)
comment.delete_instance()
6 years ago
email_body = get_template("drop_comment").render(original=message)
mail(from_email, "Re: " + subject, email_body)
else:
10 years ago
# update Comment row
comment.published = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
comment.save()
6 years ago
logger.info("commit comment: %d" % comment_id)
# rebuild RSS
rss(token)
# send approval confirmation email to admin
6 years ago
email_body = get_template("approve_comment").render(original=message)
mail(from_email, "Re: " + subject, email_body)
def get_email_metadata(message):
# retrieve metadata reader email from email body sent by admin
email = ""
6 years ago
m = re.search(r"email:\s(.+@.+\..+)", message)
if m:
email = m.group(1)
return email
def rss(token, onstart=False):
6 years ago
if onstart and os.path.isfile(config.rss["file"]):
return
site = Site.select().where(Site.token == token).get()
6 years ago
rss_title = get_template("rss_title_message").render(site=site.name)
md = markdown.Markdown()
items = []
6 years ago
for row in (
Comment.select()
.join(Site)
.where(Site.token == token, Comment.published)
.order_by(-Comment.published)
.limit(10)
):
item_link = "%s://%s%s" % (config.rss["proto"], site.url, row.url)
items.append(
PyRSS2Gen.RSSItem(
title="%s - %s://%s%s"
% (config.rss["proto"], row.author_name, site.url, row.url),
link=item_link,
description=md.convert(row.content),
guid=PyRSS2Gen.Guid("%s/%d" % (item_link, row.id)),
pubDate=row.published,
)
)
rss = PyRSS2Gen.RSS2(
title=rss_title,
6 years ago
link="%s://%s" % (config.rss["proto"], site.url),
description="Commentaires du site '%s'" % site.name,
lastBuildDate=datetime.now(),
6 years ago
items=items,
)
6 years ago
rss.write_xml(open(config.rss["file"], "w"), encoding="utf-8")
7 years ago
def send_delete_command(content):
6 years ago
# TODO delete mail
pass
def get_template(name):
6 years ago
return env.get_template(config.general["lang"] + "/" + name + ".tpl")
def enqueue(something):
queue.put(something)
def get_processor():
return proc
def start(template_dir):
global proc, env
# initialize Jinja 2 templating
logger.info("load templates from directory %s" % template_dir)
env = Environment(loader=FileSystemLoader(template_dir))
# generate RSS for all sites
for site in Site.select():
rss(site.token, True)
# start processor thread
proc = Processor()
proc.start()