my eye

__init__.py

Raw

"""
Manage mentions on your website.

Implements [Webmention][0] receiving and sending.

> Webmention is a simple way to notify any URL when you mention it on
> your site. From the receiver's perspective, it's a way to request
> notifications when other sites mention it. 

[0]: https://w3.org/TR/webmention

"""

# TODO https://indieweb.org/Salmention
# TODO https://indieweb.org/Vouch

import newmath
import web
import webagt

app = web.application(
    __name__,
    prefix="mentions",
    args={"mention_id": r"\w+"},
    model={
        "received_mentions": {  # others mentioning you
            "mention_id": "TEXT",
            "mentioned": "DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP",
            "source": "TEXT",
            "target": "TEXT",
            "data": "JSON",
        },
        "sent_mentions": {  # you mentioning others
            "mention_id": "TEXT",
            "mentioned": "DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP",
            "source": "TEXT",
            "target": "TEXT",
            "response": "JSON",
        },
    },
)


def receive(source, target) -> None:
    """
    Receive a webmention for `target` on behalf of `source`.

    """
    # TODO re-queue failures w/ a logarithmic backoff
    mention_id = app.model.store_received_mention(source, target)
    mention = webagt.get(source).mention(target).data
    web.tx.db.update(
        "received_mentions",
        what="data = ?",
        where="mention_id = ?",
        vals=[mention, mention_id],
    )
    return

    # XXX source_data = mf.parse(source_doc.text, url=source)
    # XXX source_repr = mf.util.representative_card(source_data, source)
    # XXX if not source_repr:
    # XXX     raise web.BadRequest("No representative h-card found at source.")

    # XXX ZZZ path = web.uri(target).path
    # XXX ZZZ plural, _, resource_path = path.partition("/")
    # XXX ZZZ try:
    # XXX ZZZ     t = get_type(plural)
    # XXX ZZZ     resource = get_resource(t.s, resource_path)
    # XXX ZZZ except (TypeError, ResourceNotFound):
    # XXX ZZZ     resource = None

    # cache.put(source_url, parse=True)  # , html=source_doc.text)
    # source = interpret(source_url)
    # mention_data["source"] = source

    # # TODO ensure links are in page as with likes below
    # if "entry" in source:
    #     entry = source["entry"]
    #     entry_type = entry["-type"]
    #     if resource:
    #         if entry_type == "photo":
    #             print("photo post")
    #         elif entry_type == "associate":
    #             store("associations", path, source_url, mention_id)
    #         elif entry_type == "like":
    #             for like_of in entry["like-of"]:
    #                 if like_of["url"] == target:
    #                     store("likes", path, source_url, mention_id)
    #         elif entry_type == "vote":
    #             vote_on = entry["vote-on"][0]
    #             if vote_on["url"] == target:
    #                 store("votes", path, source_url, mention_id)
    #         elif entry_type == "note" and t.s == "code":
    #             t.utility(resource["-id"]).add_issue(source_url, entry,
    #                                                  mention_id)
    #         elif entry_type in ("note", "image") and \
    #                                t.s in ("note", "image"):
    #             if "in-reply-to" in entry:
    #                 for in_reply_to in entry["in-reply-to"]:
    #                     if in_reply_to["url"] == target:
    #                         store("replies", path, source_url, mention_id)
    #                 # bubble up mention if resource itself is a reply
    #                 if "in-reply-to" in resource:
    #                     send(path, resource["in-reply-to"])
    #     else:
    #         if entry_type == "follow":
    #             store("follows", path, source_url, mention_id)
    #         elif entry_type == "associate":
    #             store("associations", path, source_url, mention_id)
    # else:
    #     store("generic", path, source_url, mention_id)

    # mention_data["confirmed"] = time.time()
    # web.tx.kv["mentioned"][mention_id] = pickle.dumps(mention_data)

    # TODO web.tx.kv.db.publish(f"{web.tx.host.identity}:resources:{path}",
    # TODO                  f"<section class=h-entry>a {entry_type}</section>")
    #               str(web.tx.view.entry_template(entry, resource,
    #                                      t.view.type(entry, resource))))

    # # check if networking request
    # source_followees = source_data["rels"].get("followee", [])
    # source_followers = source_data["rels"].get("follower", [])
    # print()
    # print("ees", source_followees)
    # print("ers", source_followers)
    # if "code" in form:
    #     print("code", form.code)
    # print()
    # if "https://{}".format(web.tx.host.name) in source_followees:
    #     # TODO private webmention for initial follow
    #     # TODO public webmention for public follow
    #     # TODO check first private then public
    #     # TODO represent & reciprocate follow accordingly
    #     root_url = web.uri(source_repr["properties"]["url"][0], secure=True)
    #     person = get_person(root_url.minimized)
    #     rel = web.tx.db.select("person__person",
    #                        where="from_id = 1 AND to_id = ?",
    #                        vals=[person["id"]])[0]
    #     if rel:
    #         web.tx.db.update("person__person", what="private = 0",
    #                      where="from_id = 1 AND to_id = ?",
    #                      vals=[person["id"]])
    #         # TODO send return mention to notify if publicize
    #     return "okay"
    #     root_data = mf.parse(url=root_url)
    #     root_repr = mf.util.representative_card(root_data, root_url)
    #     if not root_repr:
    #         raise web.BadRequest("No representative h-card found "
    #                              "at source's root.")
    #     name = root_repr["properties"]["name"][0]
    #     try:
    #         email = root_repr["properties"]["email"][0]
    #     except IndexError:
    #         email = ""
    #     pubkey = ""
    #     person_feed = mf.util.interpret_feed(root_data, source_url)
    #     person_feed_license = root_data["rels"]["license"][0]
    #     add_person(name, root_url, email, pubkey, person_feed["name"],
    #                person_feed_license, "follower")


@app.query
def send_mention(db, source, target):
    if not source.startswith(web.tx.origin):
        source = f"{web.tx.origin}/{source.lstrip('/')}"
    mention_id = newmath.nbrandom(5)
    db.insert("sent_mentions", mention_id=mention_id, source=source, target=target)
    # TODO web.enqueue(webmention.send, source, target) & update DB with results
    endpoint = webagt.get(target).link("webmention")
    webagt.post(endpoint, data={"source": source, "target": target})


@app.query
def store_received_mention(db, source, target):
    mention_id = newmath.nbrandom(5)
    db.insert("received_mentions", mention_id=mention_id, source=source, target=target)
    return mention_id


@app.query
def get_received_mentions(db):
    """Return a list of all received mentions ordered by most recent."""
    return db.select("received_mentions", order="mentioned DESC")


@app.query
def get_received_mention_by_id(db, mention_id):
    return db.select("received_mentions", where="mention_id = ?", vals=[mention_id])


@app.query
def get_received_mentions_by_target(db, url):
    return db.select("received_mentions", where="target = ?", vals=[str(url)])


@app.query
def get_sent_mentions(db):
    """Return a list of all sent mentions ordered by most recent."""
    return db.select("sent_mentions", order="mentioned DESC")


@app.wrap
def linkify_head(handler, main_app):
    """Ensure receiver link is in head of current document."""
    yield
    web.add_rel_links(webmention="/mentions")


@app.wrap
def track_referrers(handler, main_app):
    """Store the origin provided in any Referer request headers."""
    # TODO print(f"REFERER: {web.tx.request.headers.get('Referer')}")
    yield


# TODO @app.wrap
# TODO def get_mentions(handler, main_app):
# TODO     web.tx.host.mentions = get_received_mentions(web.tx.db)
# TODO     yield


@app.control("")
class Mentions:
    """Your mentions."""

    def get(self):
        """Return a page of all mentions."""
        return app.view.index(
            app.model.get_received_mentions(),
            app.model.get_sent_mentions(),
        )

    def post(self):
        """"""
        form = web.form("source", "target")
        web.enqueue(receive, form.source, form.target)
        raise web.Accepted("webmention received")


@app.control("received")
class ReceivedMentions:
    """."""

    def get(self):
        """Details of the webmention, with status information in mf2."""
        return app.view.received.index(
            app.model.get_received_mention_by_id(self.mention_id)
        )


@app.control("received/{mention_id}")
class ReceivedMention:
    """."""

    def get(self):
        """Details of the webmention, with status information in mf2."""
        return app.view.received.mention(
            app.model.get_received_mention_by_id(self.mention_id)
        )

    def post(self):
        """Details of the webmention."""
        raise web.Accepted("webmention received")
        # XXX f"{web.tx.host.name}/mentions/{mention_id}")


@app.control("sent")
class SentMentions:
    """."""

    def get(self):
        """Details of the webmention, with status information in mf2."""
        return app.view.sent.index()