Enable Webmentions for public posts

This commit is contained in:
Thomas Sileo 2019-09-08 12:09:34 +02:00
parent 4d968264f2
commit 018b7bf553
7 changed files with 65 additions and 7 deletions

View file

@ -32,8 +32,8 @@ from config import MEDIA_CACHE
from config import _drop_db from config import _drop_db
from core import feed from core import feed
from core.activitypub import activity_url from core.activitypub import activity_url
from core.activitypub import post_to_outbox
from core.activitypub import new_context from core.activitypub import new_context
from core.activitypub import post_to_outbox
from core.meta import Box from core.meta import Box
from core.meta import MetaKey from core.meta import MetaKey
from core.meta import _meta from core.meta import _meta

View file

@ -24,6 +24,7 @@ from core.activitypub import Box
from core.activitypub import _actor_hash from core.activitypub import _actor_hash
from core.activitypub import _add_answers_to_question from core.activitypub import _add_answers_to_question
from core.activitypub import _cache_actor_icon from core.activitypub import _cache_actor_icon
from core.activitypub import is_from_outbox
from core.activitypub import post_to_outbox from core.activitypub import post_to_outbox
from core.activitypub import save_reply from core.activitypub import save_reply
from core.activitypub import update_cached_actor from core.activitypub import update_cached_actor
@ -48,6 +49,7 @@ from core.shared import p
from core.tasks import Tasks from core.tasks import Tasks
from utils import opengraph from utils import opengraph
from utils.media import is_video from utils.media import is_video
from utils.webmentions import discover_webmention_endpoint
blueprint = flask.Blueprint("tasks", __name__) blueprint = flask.Blueprint("tasks", __name__)
@ -305,6 +307,41 @@ def task_cache_attachment() -> _Response:
return "" return ""
@blueprint.route("/task/send_webmention", methods=["POST"])
def task_send_webmention() -> _Response:
task = p.parse(flask.request)
app.logger.info(f"task={task!r}")
note_url = task.payload["note_url"]
link = task.payload["link"]
remote_id = task.payload["remote_id"]
try:
app.logger.info(f"trying to send webmention source={note_url} target={link}")
webmention_endpoint = discover_webmention_endpoint(link)
if not webmention_endpoint:
app.logger.info("no webmention endpoint")
return ""
resp = requests.post(
webmention_endpoint,
data={"source": note_url, "target": link},
headers={"User-Agent": config.USER_AGENT},
)
app.logger.info(f"webmention endpoint resp={resp}/{resp.text}")
resp.raise_for_status()
except HTTPError as err:
app.logger.exception("request failed")
if 400 >= err.response.status_code >= 499:
app.logger.info("client error, no retry")
return ""
raise TaskError() from err
except Exception as err:
app.logger.exception(f"failed to cache actor for {link}/{remote_id}/{note_url}")
raise TaskError() from err
return ""
@blueprint.route("/task/cache_actor", methods=["POST"]) @blueprint.route("/task/cache_actor", methods=["POST"])
def task_cache_actor() -> _Response: def task_cache_actor() -> _Response:
task = p.parse(flask.request) task = p.parse(flask.request)
@ -319,10 +356,18 @@ def task_cache_actor() -> _Response:
# Fetch the Open Grah metadata if it's a `Create` # Fetch the Open Grah metadata if it's a `Create`
if activity.has_type(ap.ActivityType.CREATE): if activity.has_type(ap.ActivityType.CREATE):
links = opengraph.links_from_note(activity.get_object().to_dict()) obj = activity.get_object()
links = opengraph.links_from_note(obj.to_dict())
if links: if links:
Tasks.fetch_og_meta(iri) Tasks.fetch_og_meta(iri)
# Send Webmentions only if it's from the outbox, and public
if (
is_from_outbox(obj)
and ap.get_visibility(obj) == ap.Visibility.PUBLIC
):
Tasks.send_webmentions(activity, links)
if activity.has_type(ap.ActivityType.FOLLOW): if activity.has_type(ap.ActivityType.FOLLOW):
if actor.id == config.ID: if actor.id == config.ID:
# It's a new following, cache the "object" (which is the actor we follow) # It's a new following, cache the "object" (which is the actor we follow)

View file

@ -6,11 +6,11 @@ from enum import Enum
from pathlib import Path from pathlib import Path
import yaml import yaml
from bleach import linkify
from itsdangerous import JSONWebSignatureSerializer from itsdangerous import JSONWebSignatureSerializer
from little_boxes import strtobool from little_boxes import strtobool
from little_boxes.activitypub import CTX_AS as AP_DEFAULT_CTX from little_boxes.activitypub import CTX_AS as AP_DEFAULT_CTX
from pymongo import MongoClient from pymongo import MongoClient
from bleach import linkify
import sass import sass
from utils.emojis import _load_emojis from utils.emojis import _load_emojis

View file

@ -9,6 +9,7 @@ from little_boxes.errors import NotAnActivityError
import config import config
from core.activitypub import _answer_key from core.activitypub import _answer_key
from core.activitypub import handle_replies from core.activitypub import handle_replies
from core.activitypub import new_context
from core.activitypub import post_to_outbox from core.activitypub import post_to_outbox
from core.activitypub import update_cached_actor from core.activitypub import update_cached_actor
from core.db import DB from core.db import DB
@ -163,6 +164,7 @@ def _follow_process_inbox(activity: ap.Follow, new_meta: _NewMeta) -> None:
actor_id = activity.get_actor().id actor_id = activity.get_actor().id
accept = ap.Accept( accept = ap.Accept(
actor=config.ID, actor=config.ID,
context=new_context(activity),
object={ object={
"type": "Follow", "type": "Follow",
"id": activity.id, "id": activity.id,

View file

@ -3,6 +3,7 @@ from datetime import datetime
from datetime import timezone from datetime import timezone
from typing import Any from typing import Any
from typing import Dict from typing import Dict
from typing import Set
from little_boxes import activitypub as ap from little_boxes import activitypub as ap
from poussetaches import PousseTaches from poussetaches import PousseTaches
@ -40,6 +41,18 @@ class Tasks:
p.push({"url": url, "iri": iri}, "/task/cache_emoji") p.push({"url": url, "iri": iri}, "/task/cache_emoji")
@staticmethod
def send_webmentions(activity: ap.Create, links: Set[str]) -> None:
for link in links:
p.push(
{
"link": link,
"note_url": activity.get_object().get_url(),
"remote_id": activity.id,
},
"/task/send_webmention",
)
@staticmethod @staticmethod
def cache_emojis(activity: ap.BaseActivity) -> None: def cache_emojis(activity: ap.BaseActivity) -> None:
for emoji in activity.get_emojis(): for emoji in activity.get_emojis():

View file

@ -1,5 +1,3 @@
import json
import little_boxes.activitypub as ap import little_boxes.activitypub as ap
import mf2py import mf2py
import requests import requests
@ -49,7 +47,7 @@ def lookup(url: str) -> ap.BaseActivity:
# Maybe the page was JSON-LD? # Maybe the page was JSON-LD?
data = resp.json() data = resp.json()
return ap.parse_activity(data) return ap.parse_activity(data)
except json.JSONDecodeError: except Exception:
pass pass
# Try content negotiation (retry with the AP Accept header) # Try content negotiation (retry with the AP Accept header)

View file

@ -40,7 +40,7 @@ def _discover_webmention_endoint(url: str) -> Optional[str]:
return None return None
def discover_webmention_endoint(url: str) -> Optional[str]: def discover_webmention_endpoint(url: str) -> Optional[str]:
"""Discover the Webmention endpoint of a given URL, if any. """Discover the Webmention endpoint of a given URL, if any.
Passes all the tests at https://webmention.rocks! Passes all the tests at https://webmention.rocks!