/ clock.py
clock.py
  1  """Check emails to editor@proselint.com, lint them, and reply."""
  2  
  3  from apscheduler.schedulers.blocking import BlockingScheduler
  4  import gmail
  5  import smtplib
  6  from email.mime.multipart import MIMEMultipart
  7  from email.mime.text import MIMEText
  8  from worker import conn
  9  import requests
 10  import hashlib
 11  import json
 12  import os
 13  import logging
 14  import re
 15  
 16  logging.basicConfig()
 17  scheduler = BlockingScheduler()
 18  
 19  # Settings
 20  user = "hello@lifelinter.com"
 21  user_to = "editor@proselint.com"
 22  name = "proselint"
 23  password = os.environ['gmail_password']
 24  
 25  tagline = "Proselint, a linter for prose."
 26  url = "http://proselint.com"
 27  api_url = "http://api.proselint.com/v0/"
 28  
 29  
 30  def quoted(string, every=64):
 31      """Insert a quote before linebreaks."""
 32      return "> " + re.sub("\r\n(?=[^\r\n])", "\r\n> ", string)
 33  
 34  
 35  @scheduler.scheduled_job('interval', minutes=0.25)
 36  def check_email():
 37      """Check the mail account and lint new mail."""
 38      server = smtplib.SMTP("smtp.gmail.com", 587)
 39      server.ehlo()
 40      server.starttls()
 41      server.ehlo()
 42      server.login(user, password)
 43  
 44      g = gmail.login(user, password)
 45  
 46      # Check for unread messages.
 47      unread = g.inbox().mail(unread=True)
 48  
 49      # Submit a job to lint each email sent to editor@proselint.com. Record the
 50      # resulting job_ids somewhere (in Redis, I suppose), keyed by a hash of the
 51      # email.
 52      for u in unread:
 53  
 54          u.fetch()
 55  
 56          signature = (u.fr.decode('utf-8') +
 57                       u.subject.decode('utf-8') +
 58                       u.body.decode('utf-8'))
 59  
 60          hash = hashlib.sha256(signature.encode('utf-8')).hexdigest()
 61  
 62          if user_to in u.to or user_to in u.headers.get('Cc', []):
 63  
 64              job_id = conn.get(hash)
 65  
 66              if not job_id:
 67                  # If the email hasn't been sent for processing, send it.
 68                  r = requests.post(api_url, data={"text": u.body})
 69                  conn.set(hash, r.json()["job_id"])
 70                  print("Email {} sent for processing.".format(hash))
 71  
 72              else:
 73                  # Otherwise, check whether the results are ready, and if so,
 74                  # reply with them.
 75                  r = requests.get(api_url, params={"job_id": job_id})
 76  
 77                  if r.json()["status"] == "success":
 78  
 79                      reply = quoted(u.body)
 80                      errors = r.json()['data']['errors']
 81                      reply += "\r\n\r\n".join([json.dumps(e) for e in errors])
 82  
 83                      msg = MIMEMultipart()
 84                      msg["From"] = "{} <{}>".format(name, user)
 85                      msg["To"] = u.fr
 86                      msg["Subject"] = "Re: " + u.subject
 87  
 88                      if u.headers.get('Message-ID'):
 89                          msg.add_header("In-Reply-To", u.headers['Message-ID'])
 90                          msg.add_header("References", u.headers['Message-ID'])
 91  
 92                      body = reply + "\r\n\r\n--\r\n" + tagline + "\r\n" + url
 93                      msg.attach(MIMEText(body, "plain"))
 94  
 95                      text = msg.as_string()
 96                      server.sendmail(user, u.fr, text)
 97  
 98                      # Mark the email as read.
 99                      u.read()
100                      u.archive()
101  
102                      print("Email {} has been replied to.".format(hash))
103  
104  
105  scheduler.start()