106 lines
3.1 KiB
Python
106 lines
3.1 KiB
Python
# Copyright (c) 2021 iliana etaoin
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Affero General Public License as published by the Free
|
|
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
# later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
import urllib.parse
|
|
|
|
import requests
|
|
from collections import defaultdict
|
|
from dataclasses import dataclass
|
|
from flask import Flask, redirect, render_template, request, url_for
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
def slug_filter(s):
|
|
return s.lower().replace(" ", "-")
|
|
|
|
|
|
app.jinja_env.filters["slug"] = slug_filter
|
|
|
|
|
|
@dataclass
|
|
class Emoj:
|
|
__slots__ = frozenset({"shortcode", "url"})
|
|
shortcode: str
|
|
url: str
|
|
|
|
|
|
@app.route("/<domain>")
|
|
def emojo(domain):
|
|
if request.args.get("show_all", "") == "on":
|
|
show_all = True
|
|
else:
|
|
show_all = False
|
|
if request.args.get("show_animated", "") == "on":
|
|
show_animated = True
|
|
else:
|
|
show_animated = False
|
|
|
|
scheme = "http" if domain.endswith(".onion") else "https"
|
|
url = urllib.parse.urlunsplit((scheme, domain, "/api/v1/custom_emojis", "", ""))
|
|
try:
|
|
response = requests.get(url)
|
|
except requests.exceptions.RequestException:
|
|
return render_template("oh_no.html", domain=domain)
|
|
|
|
if response.status_code == 401:
|
|
return render_template("forbidden.html", domain=domain)
|
|
|
|
categories = defaultdict(list)
|
|
for emoji in sorted(
|
|
response.json(),
|
|
# sort by category,
|
|
# then name within each category,
|
|
# then disambiguate by capitalization
|
|
key=lambda x: (x.get("category", ""), x["shortcode"].lower(), x["shortcode"]),
|
|
):
|
|
if not show_all and not emoji.get("visible_in_picker", True):
|
|
continue
|
|
|
|
url = emoji["url" if show_animated else "static_url"]
|
|
categories[emoji.get("category")].append(
|
|
Emoj(shortcode=emoji["shortcode"], url=url)
|
|
)
|
|
|
|
return render_template(
|
|
"emojo.html", domain=domain, categories=categories, show_animated=show_animated
|
|
)
|
|
|
|
|
|
@app.route("/favicon.ico")
|
|
@app.route("/robots.txt")
|
|
def no_content():
|
|
return ("", 204)
|
|
|
|
|
|
@app.route("/", methods=("GET", "POST"))
|
|
def index():
|
|
if request.method == "POST":
|
|
if "instance" in request.form:
|
|
show_all = request.form.get("show_all")
|
|
show_animated = request.form.get("show_animated")
|
|
return redirect(
|
|
url_for(
|
|
"emojo",
|
|
domain=request.form["instance"],
|
|
show_all=show_all,
|
|
show_animated=show_animated,
|
|
)
|
|
)
|
|
else:
|
|
return redirect(url_for("index"))
|
|
else:
|
|
return render_template("index.html")
|