logging: use separate loggers for each module with their names

pull/88/head
Ryan Barrett 2022-02-11 22:38:56 -08:00
rodzic 2731e706f4
commit 96dba41c9b
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
9 zmienionych plików z 64 dodań i 50 usunięć

Wyświetl plik

@ -20,6 +20,8 @@ from common import redirect_unwrap, redirect_wrap
from models import Follower, MagicKey from models import Follower, MagicKey
from httpsig.requests_auth import HTTPSignatureAuth from httpsig.requests_auth import HTTPSignatureAuth
logger = logging.getLogger(__name__)
CACHE_TIME = datetime.timedelta(seconds=15) CACHE_TIME = datetime.timedelta(seconds=15)
SUPPORTED_TYPES = ( SUPPORTED_TYPES = (
@ -49,7 +51,7 @@ def send(activity, inbox_url, user_domain):
Returns: Returns:
requests.Response requests.Response
""" """
logging.info('Sending AP request from {user_domain}: {json_dumps(activity, indent=2)}') logger.info('Sending AP request from {user_domain}: {json_dumps(activity, indent=2)}')
# prepare HTTP Signature (required by Mastodon) # prepare HTTP Signature (required by Mastodon)
# https://w3c.github.io/activitypub/#authorization # https://w3c.github.io/activitypub/#authorization
@ -88,7 +90,7 @@ def actor(domain):
mf2 = util.fetch_mf2(f'http://{domain}/', gateway=True, headers=common.HEADERS) mf2 = util.fetch_mf2(f'http://{domain}/', gateway=True, headers=common.HEADERS)
hcard = mf2util.representative_hcard(mf2, mf2['url']) hcard = mf2util.representative_hcard(mf2, mf2['url'])
logging.info(f'Representative h-card: {json_dumps(hcard, indent=2)}') logger.info(f'Representative h-card: {json_dumps(hcard, indent=2)}')
if not hcard: if not hcard:
error(f"Couldn't find a representative h-card (http://microformats.org/wiki/representative-hcard-parsing) on {mf2['url']}") error(f"Couldn't find a representative h-card (http://microformats.org/wiki/representative-hcard-parsing) on {mf2['url']}")
@ -102,7 +104,7 @@ def actor(domain):
'following': f'{request.host_url}{domain}/following', 'following': f'{request.host_url}{domain}/following',
'followers': f'{request.host_url}{domain}/followers', 'followers': f'{request.host_url}{domain}/followers',
}) })
logging.info(f'Returning: {json_dumps(obj, indent=2)}') logger.info(f'Returning: {json_dumps(obj, indent=2)}')
return (obj, { return (obj, {
'Content-Type': common.CONTENT_TYPE_AS2, 'Content-Type': common.CONTENT_TYPE_AS2,
@ -114,7 +116,7 @@ def actor(domain):
def inbox(domain): def inbox(domain):
"""Accepts POSTs to /[DOMAIN]/inbox and converts to outbound webmentions.""" """Accepts POSTs to /[DOMAIN]/inbox and converts to outbound webmentions."""
body = request.get_data(as_text=True) body = request.get_data(as_text=True)
logging.info(f'Got: {body}') logger.info(f'Got: {body}')
# parse and validate AS2 activity # parse and validate AS2 activity
try: try:
@ -186,7 +188,7 @@ def accept_follow(follow, follow_unwrapped):
follow: dict, AP Follow activity follow: dict, AP Follow activity
follow_unwrapped: dict, same, except with redirect URLs unwrapped follow_unwrapped: dict, same, except with redirect URLs unwrapped
""" """
logging.info('Replying to Follow with Accept') logger.info('Replying to Follow with Accept')
followee = follow.get('object') followee = follow.get('object')
followee_unwrapped = follow_unwrapped.get('object') followee_unwrapped = follow_unwrapped.get('object')
@ -232,7 +234,7 @@ def undo_follow(undo_unwrapped):
Args: Args:
undo_unwrapped: dict, AP Undo activity with redirect URLs unwrapped undo_unwrapped: dict, AP Undo activity with redirect URLs unwrapped
""" """
logging.info('Undoing Follow') logger.info('Undoing Follow')
follow = undo_unwrapped.get('object', {}) follow = undo_unwrapped.get('object', {})
follower = follow.get('actor') follower = follow.get('actor')
@ -244,10 +246,10 @@ def undo_follow(undo_unwrapped):
user_domain = util.domain_from_link(followee) user_domain = util.domain_from_link(followee)
follower_obj = Follower.get_by_id(Follower._id(user_domain, follower)) follower_obj = Follower.get_by_id(Follower._id(user_domain, follower))
if follower_obj: if follower_obj:
logging.info(f'Marking {follower_obj.key} as inactive') logger.info(f'Marking {follower_obj.key} as inactive')
follower_obj.status = 'inactive' follower_obj.status = 'inactive'
follower_obj.put() follower_obj.put()
else: else:
logging.warning(f'No Follower found for {user_domain} {follower}') logger.warning(f'No Follower found for {user_domain} {follower}')
# TODO send webmention with 410 of u-follow # TODO send webmention with 410 of u-follow

Wyświetl plik

@ -16,6 +16,8 @@ from werkzeug.exceptions import BadGateway
from models import Response from models import Response
logger = logging.getLogger(__name__)
DOMAIN_RE = r'([^/:]+\.[^/:]+)' DOMAIN_RE = r'([^/:]+\.[^/:]+)'
ACCT_RE = r'(?:acct:)?([^@]+)@' + DOMAIN_RE ACCT_RE = r'(?:acct:)?([^@]+)@' + DOMAIN_RE
TLD_BLOCKLIST = ('7z', 'asp', 'aspx', 'gif', 'html', 'ico', 'jpg', 'jpeg', 'js', TLD_BLOCKLIST = ('7z', 'asp', 'aspx', 'gif', 'html', 'ico', 'jpg', 'jpeg', 'js',
@ -78,18 +80,18 @@ def _requests_fn(fn, url, parse_json=False, **kwargs):
kwargs.setdefault('headers', {}).update(HEADERS) kwargs.setdefault('headers', {}).update(HEADERS)
resp = fn(url, gateway=True, **kwargs) resp = fn(url, gateway=True, **kwargs)
logging.info(f'Got {resp.status_code} headers: {resp.headers}') logger.info(f'Got {resp.status_code} headers: {resp.headers}')
type = content_type(resp) type = content_type(resp)
if (type and type != 'text/html' and if (type and type != 'text/html' and
(type.startswith('text/') or type.endswith('+json') or type.endswith('/json'))): (type.startswith('text/') or type.endswith('+json') or type.endswith('/json'))):
logging.info(resp.text) logger.info(resp.text)
if parse_json: if parse_json:
try: try:
return resp.json() return resp.json()
except ValueError: except ValueError:
msg = "Couldn't parse response as JSON" msg = "Couldn't parse response as JSON"
logging.info(msg, exc_info=True) logger.info(msg, exc_info=True)
raise BadGateway(msg) raise BadGateway(msg)
return resp return resp
@ -116,7 +118,7 @@ def get_as2(url):
""" """
def _error(resp): def _error(resp):
msg = "Couldn't fetch %s as ActivityStreams 2" % url msg = "Couldn't fetch %s as ActivityStreams 2" % url
logging.warning(msg) logger.warning(msg)
err = BadGateway(msg) err = BadGateway(msg)
err.requests_response = resp err.requests_response = resp
raise err raise err
@ -192,7 +194,7 @@ def send_webmentions(activity_wrapped, proxy=None, **response_props):
errors = [] # stores (code, body) tuples errors = [] # stores (code, body) tuples
for target in targets: for target in targets:
if util.domain_from_link(target) == util.domain_from_link(source): if util.domain_from_link(target) == util.domain_from_link(source):
logging.info(f'Skipping same-domain webmention from {source} to {target}') logger.info(f'Skipping same-domain webmention from {source} to {target}')
continue continue
response = Response(source=source, target=target, direction='in', response = Response(source=source, target=target, direction='in',
@ -201,17 +203,17 @@ def send_webmentions(activity_wrapped, proxy=None, **response_props):
wm_source = (response.proxy_url() wm_source = (response.proxy_url()
if verb in ('follow', 'like', 'share') or proxy if verb in ('follow', 'like', 'share') or proxy
else source) else source)
logging.info(f'Sending webmention from {wm_source} to {target}') logger.info(f'Sending webmention from {wm_source} to {target}')
try: try:
endpoint = webmention.discover(target, headers=HEADERS).endpoint endpoint = webmention.discover(target, headers=HEADERS).endpoint
if endpoint: if endpoint:
webmention.send(endpoint, wm_source, target, headers=HEADERS) webmention.send(endpoint, wm_source, target, headers=HEADERS)
response.status = 'complete' response.status = 'complete'
logging.info('Success!') logger.info('Success!')
else: else:
response.status = 'ignored' response.status = 'ignored'
logging.info('Ignoring.') logger.info('Ignoring.')
except BaseException as e: except BaseException as e:
errors.append(util.interpret_http_exception(e)) errors.append(util.interpret_http_exception(e))
response.put() response.put()
@ -262,7 +264,7 @@ def postprocess_as2(activity, target=None, key=None):
activity['inReplyTo'] = target_id activity['inReplyTo'] = target_id
elif isinstance(in_reply_to, list): elif isinstance(in_reply_to, list):
if len(in_reply_to) > 1: if len(in_reply_to) > 1:
logging.warning( logger.warning(
"AS2 doesn't support multiple inReplyTo URLs! " "AS2 doesn't support multiple inReplyTo URLs! "
'Only using the first: %s' % in_reply_to[0]) 'Only using the first: %s' % in_reply_to[0])
activity['inReplyTo'] = in_reply_to[0] activity['inReplyTo'] = in_reply_to[0]

Wyświetl plik

@ -8,6 +8,8 @@ from flask import request
from google.cloud import ndb from google.cloud import ndb
from oauth_dropins.webutil.models import StringIdModel from oauth_dropins.webutil.models import StringIdModel
logger = logging.getLogger(__name__)
class MagicKey(StringIdModel): class MagicKey(StringIdModel):
"""Stores a user's public/private key pair used for Magic Signatures. """Stores a user's public/private key pair used for Magic Signatures.
@ -87,12 +89,12 @@ class Response(StringIdModel):
if source and target: if source and target:
assert 'id' not in kwargs assert 'id' not in kwargs
kwargs['id'] = self._id(source, target) kwargs['id'] = self._id(source, target)
logging.info(f"Response id (source target): {kwargs['id']}") logger.info(f"Response id (source target): {kwargs['id']}")
super(Response, self).__init__(**kwargs) super(Response, self).__init__(**kwargs)
@classmethod @classmethod
def get_or_create(cls, source=None, target=None, **kwargs): def get_or_create(cls, source=None, target=None, **kwargs):
logging.info(f'Response source target: {source} {target}') logger.info(f'Response source target: {source} {target}')
return cls.get_or_insert(cls._id(source, target), **kwargs) return cls.get_or_insert(cls._id(source, target), **kwargs)
def source(self): def source(self):
@ -149,6 +151,6 @@ class Follower(StringIdModel):
@classmethod @classmethod
def get_or_create(cls, user_domain, follower_id, **kwargs): def get_or_create(cls, user_domain, follower_id, **kwargs):
logging.info(f'new Follower for {user_domain} {follower_id}') logger.info(f'new Follower for {user_domain} {follower_id}')
return cls.get_or_insert(cls._id(user_domain, follower_id), **kwargs) return cls.get_or_insert(cls._id(user_domain, follower_id), **kwargs)

Wyświetl plik

@ -25,6 +25,8 @@ from app import app, cache
import common import common
from models import MagicKey from models import MagicKey
logger = logging.getLogger(__name__)
CACHE_TIME = datetime.timedelta(seconds=15) CACHE_TIME = datetime.timedelta(seconds=15)
@ -49,10 +51,10 @@ def redir(to):
urllib.parse.urlparse(to).hostname)) urllib.parse.urlparse(to).hostname))
for domain in domains: for domain in domains:
if domain and MagicKey.get_by_id(domain): if domain and MagicKey.get_by_id(domain):
logging.info(f'Found MagicKey for domain {domain}') logger.info(f'Found MagicKey for domain {domain}')
break break
else: else:
logging.info(f'No user found for any of {domains}; returning 404') logger.info(f'No user found for any of {domains}; returning 404')
abort(404) abort(404)
# poor man's conneg, only handle single Accept values, not multiple with # poor man's conneg, only handle single Accept values, not multiple with
@ -62,7 +64,7 @@ def redir(to):
return convert_to_as2(to) return convert_to_as2(to)
# redirect # redirect
logging.info(f'redirecting to {to}') logger.info(f'redirecting to {to}')
return redirect(to, code=301) return redirect(to, code=301)
@ -74,10 +76,10 @@ def convert_to_as2(url):
""" """
mf2 = util.fetch_mf2(url) mf2 = util.fetch_mf2(url)
entry = mf2util.find_first_entry(mf2, ['h-entry']) entry = mf2util.find_first_entry(mf2, ['h-entry'])
logging.info(f"Parsed mf2 for {mf2['url']}: {json_dumps(entry, indent=2)}") logger.info(f"Parsed mf2 for {mf2['url']}: {json_dumps(entry, indent=2)}")
obj = common.postprocess_as2(as2.from_as1(microformats2.json_to_object(entry))) obj = common.postprocess_as2(as2.from_as1(microformats2.json_to_object(entry)))
logging.info(f'Returning: {json_dumps(obj, indent=2)}') logger.info(f'Returning: {json_dumps(obj, indent=2)}')
return obj, { return obj, {
'Content-Type': common.CONTENT_TYPE_AS2, 'Content-Type': common.CONTENT_TYPE_AS2,

Wyświetl plik

@ -16,6 +16,8 @@ from oauth_dropins.webutil.flask_util import error
from app import app from app import app
import common import common
logger = logging.getLogger(__name__)
# from django_salmon.feeds # from django_salmon.feeds
ATOM_NS = 'http://www.w3.org/2005/Atom' ATOM_NS = 'http://www.w3.org/2005/Atom'
ATOM_THREADING_NS = 'http://purl.org/syndication/thread/1.0' ATOM_THREADING_NS = 'http://purl.org/syndication/thread/1.0'
@ -36,14 +38,14 @@ def slap(acct):
"""Accepts POSTs to /[ACCT]/salmon and converts to outbound webmentions.""" """Accepts POSTs to /[ACCT]/salmon and converts to outbound webmentions."""
# TODO: unify with activitypub # TODO: unify with activitypub
body = request.get_data(as_text=True) body = request.get_data(as_text=True)
logging.info(f'Got: {body}') logger.info(f'Got: {body}')
try: try:
parsed = utils.parse_magic_envelope(body) parsed = utils.parse_magic_envelope(body)
except ParseError as e: except ParseError as e:
error('Could not parse POST body as XML', exc_info=True) error('Could not parse POST body as XML', exc_info=True)
data = parsed['data'] data = parsed['data']
logging.info(f'Decoded: {data}') logger.info(f'Decoded: {data}')
# check that we support this activity type # check that we support this activity type
try: try:
@ -62,10 +64,10 @@ def slap(acct):
elif not author.startswith('acct:'): elif not author.startswith('acct:'):
error(f'Author URI {author} has unsupported scheme; expected acct:') error(f'Author URI {author} has unsupported scheme; expected acct:')
logging.info(f'Fetching Salmon key for {author}') logger.info(f'Fetching Salmon key for {author}')
if not magicsigs.verify(data, parsed['sig'], author_uri=author): if not magicsigs.verify(data, parsed['sig'], author_uri=author):
error('Could not verify magic signature.') error('Could not verify magic signature.')
logging.info('Verified magic signature.') logger.info('Verified magic signature.')
# Verify that the timestamp is recent. Required by spec. # Verify that the timestamp is recent. Required by spec.
# I get that this helps prevent spam, but in practice it's a bit silly, # I get that this helps prevent spam, but in practice it's a bit silly,

Wyświetl plik

@ -10,6 +10,8 @@ from flask import request
from app import app from app import app
logger = logging.getLogger(__name__)
@app.route(r'/superfeedr/', methods=['GET', 'POST']) @app.route(r'/superfeedr/', methods=['GET', 'POST'])
@app.route(r'/superfeedr/<path:_>', methods=['GET', 'POST']) @app.route(r'/superfeedr/<path:_>', methods=['GET', 'POST'])
@ -18,5 +20,5 @@ def superfeedr(_=None):
https://documentation.superfeedr.com/publishers.html#subscription-callback https://documentation.superfeedr.com/publishers.html#subscription-callback
""" """
logging.info(f'Got:\n{request.get_data(as_text=True)}') logger.info(f'Got:\n{request.get_data(as_text=True)}')
return '', 204 return '', 204

Wyświetl plik

@ -1,7 +1,5 @@
# coding=utf-8 # coding=utf-8
"""Unit tests for common.py.""" """Unit tests for common.py."""
import logging
import os
from unittest import mock from unittest import mock
from oauth_dropins.webutil import util from oauth_dropins.webutil import util

Wyświetl plik

@ -22,6 +22,8 @@ import models
CACHE_TIME = datetime.timedelta(seconds=15) CACHE_TIME = datetime.timedelta(seconds=15)
NON_TLDS = frozenset(('html', 'json', 'php', 'xml')) NON_TLDS = frozenset(('html', 'json', 'php', 'xml'))
logger = logging.getLogger(__name__)
# TODO # TODO
# @cache.cached( # @cache.cached(
@ -34,7 +36,7 @@ class User(flask_util.XrdOrJrd):
return 'webfinger_user' return 'webfinger_user'
def template_vars(self, domain=None, url=None): def template_vars(self, domain=None, url=None):
logging.debug(f'Headers: {list(request.headers.items())}') logger.debug(f'Headers: {list(request.headers.items())}')
if domain.split('.')[-1] in NON_TLDS: if domain.split('.')[-1] in NON_TLDS:
error(f"{domain} doesn't look like a domain", status=404) error(f"{domain} doesn't look like a domain", status=404)
@ -48,15 +50,15 @@ class User(flask_util.XrdOrJrd):
resp = common.requests_get(candidate) resp = common.requests_get(candidate)
parsed = util.parse_html(resp) parsed = util.parse_html(resp)
mf2 = util.parse_mf2(parsed, url=resp.url) mf2 = util.parse_mf2(parsed, url=resp.url)
# logging.debug(f'Parsed mf2 for {resp.url}: {json_dumps(mf2, indent=2)}') # logger.debug(f'Parsed mf2 for {resp.url}: {json_dumps(mf2, indent=2)}')
hcard = mf2util.representative_hcard(mf2, resp.url) hcard = mf2util.representative_hcard(mf2, resp.url)
if hcard: if hcard:
logging.info(f'Representative h-card: {json_dumps(hcard, indent=2)}') logger.info(f'Representative h-card: {json_dumps(hcard, indent=2)}')
break break
else: else:
error(f"didn't find a representative h-card (http://microformats.org/wiki/representative-hcard-parsing) on {resp.url}") error(f"didn't find a representative h-card (http://microformats.org/wiki/representative-hcard-parsing) on {resp.url}")
logging.info(f'Generating WebFinger data for {domain}') logger.info(f'Generating WebFinger data for {domain}')
key = models.MagicKey.get_or_create(domain) key = models.MagicKey.get_or_create(domain)
props = hcard.get('properties', {}) props = hcard.get('properties', {})
urls = util.dedupe_urls(props.get('url', []) + [resp.url]) urls = util.dedupe_urls(props.get('url', []) + [resp.url])
@ -68,7 +70,7 @@ class User(flask_util.XrdOrJrd):
urluser, urldomain = util.parse_acct_uri(url) urluser, urldomain = util.parse_acct_uri(url)
if urldomain == domain: if urldomain == domain:
acct = f'{urluser}@{domain}' acct = f'{urluser}@{domain}'
logging.info(f'Found custom username: acct:{acct}') logger.info(f'Found custom username: acct:{acct}')
break break
# discover atom feed, if any # discover atom feed, if any
@ -139,7 +141,7 @@ class User(flask_util.XrdOrJrd):
'href': f'{request.host_url}{domain}/salmon', 'href': f'{request.host_url}{domain}/salmon',
}] }]
}) })
logging.info(f'Returning WebFinger data: {json_dumps(data, indent=2)}') logger.info(f'Returning WebFinger data: {json_dumps(data, indent=2)}')
return data return data

Wyświetl plik

@ -27,6 +27,8 @@ from app import app
import common import common
from models import Follower, MagicKey, Response from models import Follower, MagicKey, Response
logger = logging.getLogger(__name__)
SKIP_EMAIL_DOMAINS = frozenset(('localhost', 'snarfed.org')) SKIP_EMAIL_DOMAINS = frozenset(('localhost', 'snarfed.org'))
@ -39,7 +41,7 @@ class Webmention(View):
target_resp = None # requests.Response target_resp = None # requests.Response
def dispatch_request(self): def dispatch_request(self):
logging.info(f'Params: {list(request.form.items())}') logger.info(f'Params: {list(request.form.items())}')
# fetch source page # fetch source page
source = flask_util.get_required_param('source') source = flask_util.get_required_param('source')
@ -48,7 +50,7 @@ class Webmention(View):
self.source_domain = urllib.parse.urlparse(self.source_url).netloc.split(':')[0] self.source_domain = urllib.parse.urlparse(self.source_url).netloc.split(':')[0]
self.source_mf2 = util.parse_mf2(source_resp) self.source_mf2 = util.parse_mf2(source_resp)
# logging.debug(f'Parsed mf2 for {source_resp.url} : {json_dumps(self.source_mf2 indent=2)}') # logger.debug(f'Parsed mf2 for {source_resp.url} : {json_dumps(self.source_mf2 indent=2)}')
# check for backlink to bridgy fed (for webmention spec and to confirm # check for backlink to bridgy fed (for webmention spec and to confirm
# source's intent to federate to mastodon) # source's intent to federate to mastodon)
@ -61,7 +63,7 @@ class Webmention(View):
if not entry: if not entry:
error(f'No microformats2 found on {self.source_url}') error(f'No microformats2 found on {self.source_url}')
logging.info(f'First entry: {json_dumps(entry, indent=2)}') logger.info(f'First entry: {json_dumps(entry, indent=2)}')
# make sure it has url, since we use that for AS2 id, which is required # make sure it has url, since we use that for AS2 id, which is required
# for ActivityPub. # for ActivityPub.
props = entry.setdefault('properties', {}) props = entry.setdefault('properties', {})
@ -69,7 +71,7 @@ class Webmention(View):
props['url'] = [self.source_url] props['url'] = [self.source_url]
self.source_obj = microformats2.json_to_object(entry, fetch_mf2=True) self.source_obj = microformats2.json_to_object(entry, fetch_mf2=True)
logging.info(f'Converted to AS1: {json_dumps(self.source_obj, indent=2)}') logger.info(f'Converted to AS1: {json_dumps(self.source_obj, indent=2)}')
for method in self.try_activitypub, self.try_salmon: for method in self.try_activitypub, self.try_salmon:
ret = method() ret = method()
@ -112,7 +114,7 @@ class Webmention(View):
new_content = content(self.source_mf2) new_content = content(self.source_mf2)
if orig_content and new_content and orig_content == new_content: if orig_content and new_content and orig_content == new_content:
msg = f'Skipping; new content is same as content published before at {resp.updated}' msg = f'Skipping; new content is same as content published before at {resp.updated}'
logging.info(msg) logger.info(msg)
return msg return msg
source_activity['type'] = 'Update' source_activity['type'] = 'Update'
@ -241,7 +243,7 @@ class Webmention(View):
if targets: if targets:
target = targets[0] target = targets[0]
if not target: if not target:
logging.warning("No targets or followers. Ignoring.") logger.warning("No targets or followers. Ignoring.")
return return
status = None status = None
@ -285,7 +287,7 @@ class Webmention(View):
feed = common.requests_get(atom_url).text feed = common.requests_get(atom_url).text
parsed = feedparser.parse(feed) parsed = feedparser.parse(feed)
entry = parsed.entries[0] entry = parsed.entries[0]
logging.info(f'Parsed: {json_dumps(entry, indent=2)}') logger.info(f'Parsed: {json_dumps(entry, indent=2)}')
target_id = entry.id target_id = entry.id
in_reply_to = self.source_obj.get('inReplyTo') in_reply_to = self.source_obj.get('inReplyTo')
source_obj_obj = self.source_obj.get('object') source_obj_obj = self.source_obj.get('object')
@ -307,7 +309,7 @@ class Webmention(View):
self.source_obj.setdefault('tags', []).append({'url': url}) self.source_obj.setdefault('tags', []).append({'url': url})
# extract and discover salmon endpoint # extract and discover salmon endpoint
logging.info(f'Discovering Salmon endpoint in {atom_url}') logger.info(f'Discovering Salmon endpoint in {atom_url}')
endpoint = django_salmon.discover_salmon_endpoint(feed) endpoint = django_salmon.discover_salmon_endpoint(feed)
if not endpoint: if not endpoint:
@ -328,23 +330,23 @@ class Webmention(View):
if not endpoint: if not endpoint:
error('No salmon endpoint found!') error('No salmon endpoint found!')
logging.info(f'Discovered Salmon endpoint {endpoint}') logger.info(f'Discovered Salmon endpoint {endpoint}')
# construct reply Atom object # construct reply Atom object
activity = self.source_obj activity = self.source_obj
if self.source_obj.get('verb') not in source.VERBS_WITH_OBJECT: if self.source_obj.get('verb') not in source.VERBS_WITH_OBJECT:
activity = {'object': self.source_obj} activity = {'object': self.source_obj}
entry = atom.activity_to_atom(activity, xml_base=self.source_url) entry = atom.activity_to_atom(activity, xml_base=self.source_url)
logging.info(f'Converted {self.source_url} to Atom:\n{entry}') logger.info(f'Converted {self.source_url} to Atom:\n{entry}')
# sign reply and wrap in magic envelope # sign reply and wrap in magic envelope
domain = urllib.parse.urlparse(self.source_url).netloc domain = urllib.parse.urlparse(self.source_url).netloc
key = MagicKey.get_or_create(domain) key = MagicKey.get_or_create(domain)
logging.info(f'Using key for {domain}: {key}') logger.info(f'Using key for {domain}: {key}')
magic_envelope = magicsigs.magic_envelope( magic_envelope = magicsigs.magic_envelope(
entry, common.CONTENT_TYPE_ATOM, key).decode() entry, common.CONTENT_TYPE_ATOM, key).decode()
logging.info(f'Sending Salmon slap to {endpoint}') logger.info(f'Sending Salmon slap to {endpoint}')
common.requests_post( common.requests_post(
endpoint, data=common.XML_UTF8 + magic_envelope, endpoint, data=common.XML_UTF8 + magic_envelope,
headers={'Content-Type': common.CONTENT_TYPE_MAGIC_ENVELOPE}) headers={'Content-Type': common.CONTENT_TYPE_MAGIC_ENVELOPE})