all repos — donkey @ c724d6b267ac95d3b3d788f2800776523cd932df

The main backend for forlater.email

Implement emailing
Anirudh Oppiliappan x@icyphox.sh
Sat, 03 Apr 2021 11:11:48 +0530
commit

c724d6b267ac95d3b3d788f2800776523cd932df

parent

042bf159b226541e2a067d5c6781c0404bbba614

9 files changed, 128 insertions(+), 54 deletions(-)

jump to
M app/__init__.pyapp/__init__.py

@@ -0,0 +1,15 @@

+from flask import Flask +from flask_mail import Mail + +mail = Mail() + + +def init_app(): + app = Flask(__name__) + app.config.from_object("config.Config") + mail.init_app(app) + + with app.app_context(): + from . import main + + return app
M app/__main__.pyapp/__main__.py

@@ -1,4 +1,6 @@

-from .main import app +from . import init_app + +app = init_app() if __name__ == "__main__": - app.run(debug=True, host="localhost", port="8001") + app.run(host="localhost", port=8001, debug=True)
D app/cleanhtml.py

@@ -1,6 +0,0 @@

-from bs4 import BeautifulSoup - - -def cleaner(html): - soup = BeautifulSoup(html, "lxml") - return soup.get_text(separator=" ")
A app/html.py

@@ -0,0 +1,37 @@

+import requests +import urllib.parse +from bs4 import BeautifulSoup + +from .extract import extract_urls +from .utils import is_absolute_url + + +def cleaner(html): + soup = BeautifulSoup(html, "lxml") + return soup.get_text(separator=" ") + + +def process_urls(body, html=False): + if html: + cleaned = cleaner(body) + urls = extract_urls(cleaned) + else: + urls = extract_urls(body) + + return urls + + +def fetch_page(url): + res = requests.get(url) + soup = BeautifulSoup(res.content, "lxml") + for a in soup.find_all("a"): + if not is_absolute_url(a["href"]): + a["href"] = urllib.parse.urljoin(url, a["href"]) + + return {"title": soup.title.string, "html": str(soup.html)} + + +def fetch_plaintext(html): + soup = BeautifulSoup(html, "lxml") + return soup.get_text(strip=True) +
A app/mail.py

@@ -0,0 +1,10 @@

+from flask_mail import Message +from . import mail + + +def send_mail(to, replyto, subject, html, text): + msg = Message(subject, sender="forlater@monkagi.ga", reply_to=replyto) + msg.add_recipient(to) + msg.html = html + msg.body = text + mail.send(msg)
M app/main.pyapp/main.py

@@ -1,34 +1,7 @@

-from flask import Flask, request -from urlextract import URLExtract -from bs4 import BeautifulSoup -import requests -import urllib.parse - -from .extract import extract_urls -from .cleanhtml import cleaner -from .utils import is_absolute_url - -app = Flask(__name__) +from flask import Flask, request, current_app as app - -def process_urls(body, html=False): - if html: - cleaned = cleaner(body) - urls = extract_urls(cleaned) - else: - urls = extract_urls(body) - - return urls - - -def fetch_page(url): - res = requests.get(url) - soup = BeautifulSoup(res.content, "lxml") - for a in soup.find_all("a"): - if not is_absolute_url(a["href"]): - a["href"] = urllib.parse.urljoin(url, a["href"]) - - return {"title": soup.title.string, "html": str(soup.html)} +from .html import process_urls, fetch_page, fetch_plaintext +from .mail import send_mail @app.route("/webhook", methods=["POST"])

@@ -44,10 +17,12 @@ urls = process_urls(p, html=True)

for u in urls: pageinfo = fetch_page(u) + text = fetch_plaintext(pageinfo["html"]) send_mail( - mail["From"], - mail["ReplyTo"], - pageinfo["title"], - pageinfo["html"], + mail["From"].strip(), + mail["ReplyTo"].strip(), + u + ": " + pageinfo["title"].strip(), + pageinfo["html"].strip(), + text, ) return "ok"
A config.py

@@ -0,0 +1,9 @@

+import os + +class Config: + MAIL_DEFAULT_SENDER="saved@forlater.email" + MAIL_SERVER = "in-v3.mailjet.com" + MAIL_USERNAME = os.environ.get("MAILJET_API_KEY") + MAIL_PASSWORD = os.environ.get("MAILJET_SECRET_KEY") + MAIL_PORT = 587 + MAIL_USE_TLS = True
M poetry.lockpoetry.lock

@@ -22,6 +22,14 @@ html5lib = ["html5lib"]

lxml = ["lxml"] [[package]] +name = "blinker" +version = "1.4" +description = "Fast, simple object-to-object and broadcast signaling" +category = "main" +optional = false +python-versions = "*" + +[[package]] name = "bs4" version = "0.0.1" description = "Dummy package for Beautiful Soup"

@@ -41,14 +49,6 @@ optional = false

python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] -name = "dotenv" -version = "0.0.5" -description = "Handle .env files" -category = "main" -optional = false -python-versions = "*" - -[[package]] name = "filelock" version = "3.0.12" description = "A platform independent file lock."

@@ -76,6 +76,18 @@ docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"]

dotenv = ["python-dotenv"] [[package]] +name = "flask-mail" +version = "0.9.1" +description = "Flask extension for sending email" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +blinker = "*" +Flask = "*" + +[[package]] name = "idna" version = "3.1" description = "Internationalized Domain Names in Applications (IDNA)"

@@ -128,6 +140,17 @@ optional = false

python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" [[package]] +name = "python-dotenv" +version = "0.17.0" +description = "Read key-value pairs from a .env file and set them as environment variables" +category = "main" +optional = false +python-versions = "*" + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] name = "soupsieve" version = "2.2.1" description = "A modern CSS selector implementation for Beautiful Soup."

@@ -172,7 +195,7 @@

[metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "12c360a22c6df5009cd2b319dbb334365d344359835c0761970abb24c92f95f6" +content-hash = "f21013f826b70d9a041a831f644484bf64c0d6515f08efbaadb2e62dc6fe8e64" [metadata.files] appdirs = [

@@ -184,6 +207,9 @@ {file = "beautifulsoup4-4.9.3-py2-none-any.whl", hash = "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35"},

{file = "beautifulsoup4-4.9.3-py3-none-any.whl", hash = "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"}, {file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"}, ] +blinker = [ + {file = "blinker-1.4.tar.gz", hash = "sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6"}, +] bs4 = [ {file = "bs4-0.0.1.tar.gz", hash = "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a"}, ]

@@ -191,9 +217,6 @@ click = [

{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, ] -dotenv = [ - {file = "dotenv-0.0.5.tar.gz", hash = "sha256:b58d2ab3f83dbd4f8a362b21158a606bee87317a9444485566b3c8f0af847091"}, -] filelock = [ {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},

@@ -201,6 +224,9 @@ ]

flask = [ {file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"}, {file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"}, +] +flask-mail = [ + {file = "Flask-Mail-0.9.1.tar.gz", hash = "sha256:22e5eb9a940bf407bcf30410ecc3708f3c56cc44b29c34e1726fe85006935f41"}, ] idna = [ {file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"},

@@ -305,6 +331,10 @@ {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"},

{file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"}, {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"}, {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, +] +python-dotenv = [ + {file = "python-dotenv-0.17.0.tar.gz", hash = "sha256:471b782da0af10da1a80341e8438fca5fadeba2881c54360d5fd8d03d03a4f4a"}, + {file = "python_dotenv-0.17.0-py2.py3-none-any.whl", hash = "sha256:49782a97c9d641e8a09ae1d9af0856cc587c8d2474919342d5104d85be9890b2"}, ] soupsieve = [ {file = "soupsieve-2.2.1-py3-none-any.whl", hash = "sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b"},
M pyproject.tomlpyproject.toml

@@ -11,6 +11,8 @@ Flask = "^1.1.2"

urlextract = "^1.2.0" bs4 = "^0.0.1" lxml = "^4.6.3" +Flask-Mail = "^0.9.1" +python-dotenv = "^0.17.0" [tool.poetry.dev-dependencies]