Fediverse: Add @featured command.

This commit is contained in:
Valentin Lorentz 2020-05-09 20:29:24 +02:00
parent cb679d8599
commit 79f13f3051
2 changed files with 58 additions and 17 deletions

View File

@ -167,9 +167,10 @@ def get_public_key_pem():
) )
def _signed_request(url, headers, data=None): def signed_request(url, headers=None, data=None):
method = "get" if data is None else "post" method = "get" if data is None else "post"
instance_actor_url = get_instance_actor_url() instance_actor_url = get_instance_actor_url()
headers = headers or {}
if instance_actor_url: if instance_actor_url:
signed_headers = [ signed_headers = [
@ -209,7 +210,7 @@ def actor_url(localuser, hostname):
def get_actor(localuser, hostname): def get_actor(localuser, hostname):
url = actor_url(localuser, hostname) url = actor_url(localuser, hostname)
content = _signed_request(url, headers={"Accept": ACTIVITY_MIMETYPE}) content = signed_request(url, headers={"Accept": ACTIVITY_MIMETYPE})
assert content is not None assert content is not None

View File

@ -33,17 +33,19 @@ import json
import importlib import importlib
import urllib.parse import urllib.parse
from supybot import conf, utils, plugins, ircutils, callbacks, httpserver from supybot import utils, callbacks, httpserver
from supybot.commands import * from supybot.commands import wrap
from supybot.i18n import PluginInternationalization from supybot.i18n import PluginInternationalization
_ = PluginInternationalization("Fediverse")
from . import activitypub as ap from . import activitypub as ap
importlib.reload(ap) importlib.reload(ap)
_ = PluginInternationalization("Fediverse")
_username_re = re.compile("@(?P<localuser>[^@]+)@(?P<hostname>[^@]+)") _username_re = re.compile("@(?P<localuser>[^@]+)@(?P<hostname>[^@]+)")
@ -92,7 +94,7 @@ class FediverseHttp(httpserver.SupyHTTPServerCallback):
return return
pem = ap.get_public_key_pem() pem = ap.get_public_key_pem()
actor_url = ap.get_instance_actor_url() actor_url = ap.get_instance_actor_url()
hostname = urllib.parse.urlparse(hostname).hostname hostname = urllib.parse.urlparse(actor_url).hostname
actor = { actor = {
"@context": [ "@context": [
"https://www.w3.org/ns/activitystreams", "https://www.w3.org/ns/activitystreams",
@ -119,6 +121,7 @@ class Fediverse(callbacks.Plugin):
def __init__(self, irc): def __init__(self, irc):
super().__init__(irc) super().__init__(irc)
self._startHttp() self._startHttp()
self._actor_cache = utils.structures.TimeoutDict(timeout=600)
def _startHttp(self): def _startHttp(self):
callback = FediverseHttp() callback = FediverseHttp()
@ -132,11 +135,9 @@ class Fediverse(callbacks.Plugin):
def _stopHttp(self): def _stopHttp(self):
httpserver.unhook("fediverse") httpserver.unhook("fediverse")
@wrap(["somethingWithoutSpaces"]) def _get_actor(self, irc, username):
def profile(self, irc, msg, args, username): if username in self._actor_cache:
"""<@user@instance> return self._actor_cache[username]
Returns generic information on the account @user@instance."""
match = _username_re.match(username) match = _username_re.match(username)
if not match: if not match:
irc.errorInvalid("fediverse username", username) irc.errorInvalid("fediverse username", username)
@ -145,20 +146,59 @@ class Fediverse(callbacks.Plugin):
try: try:
actor = ap.get_actor(localuser, hostname) actor = ap.get_actor(localuser, hostname)
except ap.ActorNotFound as e: except ap.ActorNotFound:
# Usually a 404 # Usually a 404
irc.error("Unknown user %s." % username, Raise=True) irc.error("Unknown user %s." % username, Raise=True)
self._actor_cache[username] = actor
self._actor_cache[actor["id"]] = actor
return actor
def _format_actor_username(self, actor):
hostname = urllib.parse.urlparse(actor["id"]).hostname
return "@%s@%s" % (actor["preferredUsername"], hostname)
def _format_status(self, irc, status):
assert status["type"] == "Note", status
author_url = status["attributedTo"]
author = self._get_actor(irc, author_url)
return _("%s (%s): %s") % (
author["name"],
self._format_actor_username(author),
utils.web.htmlToText(status["content"]),
)
@wrap(["somethingWithoutSpaces"])
def profile(self, irc, msg, args, username):
"""<@user@instance>
Returns generic information on the account @user@instance."""
actor = self._get_actor(irc, username)
irc.reply( irc.reply(
_("\x02%s\x02 (@%s@%s): %s") _("\x02%s\x02 (%s): %s")
% ( % (
actor["name"], actor["name"],
actor["preferredUsername"], self._format_actor_username(actor),
hostname, utils.web.htmlToText(actor["summary"]),
utils.web.htmlToText(actor["summary"], tagReplace=""),
) )
) )
@wrap(["somethingWithoutSpaces"])
def featured(self, irc, msg, args, username):
"""<@user@instance>
Returned the featured statuses of @user@instance (aka. pinned toots).
"""
actor = self._get_actor(irc, username)
if "featured" not in actor:
irc.error(_("No featured statuses."), Raise=True)
statuses = json.loads(ap.signed_request(actor["featured"])).get(
"orderedItems", []
)
irc.replies([self._format_status(irc, status) for status in statuses])
Class = Fediverse Class = Fediverse