feat: PMs via Mastodon Proxy (#21)
* feat: proxy pms * working proxy PMs * feat: support PMs for all SW * working testpull/23/head
parent
7e07709421
commit
cf12654102
|
@ -4,4 +4,7 @@ FEDISEER_LEMMY_DOMAIN="fediseer.com"
|
|||
FEDISEER_LEMMY_USERNAME="fediseer"
|
||||
FEDISEER_LEMMY_PASSWORD="LemmyPassword"
|
||||
ADMIN_API_KEY="Password"
|
||||
secret_key="VerySecretKey"
|
||||
secret_key="VerySecretKey"
|
||||
MASTODON_INSTANCE=botsin.space # Use only when logging in to a mastodon proxy account
|
||||
MASTODON_EMAIL=email@example.com # Use only when logging in to a mastodon proxy account
|
||||
MASTODON_PASSWORD=VerySecretPass # Use only when logging in to a mastodon proxy account
|
|
@ -134,4 +134,6 @@ fediseer.log
|
|||
fediseer*.bz2
|
||||
fediseer.db
|
||||
private.pem
|
||||
public.pem
|
||||
public.pem
|
||||
|
||||
pytooter*
|
|
@ -83,11 +83,17 @@ class Models:
|
|||
"message": fields.String(default='OK',required=True, description="The result of this operation."),
|
||||
"new_key": fields.String(default=None,required=False, description="The new API key"),
|
||||
})
|
||||
self.input_instance_claim = api.model('ClaimInstanceInput', {
|
||||
'admin': fields.String(required=True, min_length=1, description="The username of the admin who wants to register this domain", example="admin"),
|
||||
'guarantor': fields.String(required=False, description="(Optional) The domain of the guaranteeing instance. They will receive a PM to validate you", example="admin"),
|
||||
'pm_proxy': fields.String(required=False, enum=[e.name for e in enums.PMProxy], description="(Optional) If you do receive the PM from @fediseer@fediseer.com, set this to true to make the Fediseer PM your your API key via @fediseer@botsin.space. For this to work, ensure that botsin.space is not blocked in your instance and optimally follow @fediseer@botsin.space as well. If set, this will be used permanently for communication to your instance."),
|
||||
})
|
||||
self.input_api_key_reset = api.model('ApiKeyResetInput', {
|
||||
'admin_username': fields.String(required=False, description="If a username is given, their API key will be reset. Otherwise the user's whose API key was provided will be reset. This allows can be initiated by other instance admins or the fediseer.", example="admin"),
|
||||
'return_new_key': fields.Boolean(required=False, default=False, description="If True, the key will be returned as part of the response instead of PM'd. Fediseer will still PM a notification to the target admin account."),
|
||||
'sysadmins': fields.Integer(required=False, default=None, min=0, max=100, description="Report how many system administrators this instance currently has."),
|
||||
'moderators': fields.Integer(required=False, default=None, min=0, max=1000, description="Report how many instance moderators this instance currently has."),
|
||||
'pm_proxy': fields.String(required=False, enum=[e.name for e in enums.PMProxy], description="(Optional) If you do receive the PM from @fediseer@fediseer.com, set this to true to make the Fediseer PM your your API key via @fediseer@botsin.space. For this to work, ensure that botsin.space is not blocked in your instance and optimally follow @fediseer@botsin.space as well. If set, this will be used permanently for communication to your instance."),
|
||||
})
|
||||
self.response_model_reports = api.model('ActivityReport', {
|
||||
'source_domain': fields.String(description="The instance domain which initiated this activity", example="lemmy.dbzer0.com"),
|
||||
|
|
|
@ -11,7 +11,6 @@ from fediseer.utils import hash_api_key
|
|||
from fediseer.messaging import activitypub_pm
|
||||
from pythorhead import Lemmy
|
||||
from fediseer.fediverse import get_admin_for_software, get_nodeinfo
|
||||
from fediseer.consts import SUPPORTED_SOFTWARE
|
||||
|
||||
api = Namespace('v1', 'API Version 1' )
|
||||
|
||||
|
|
|
@ -145,12 +145,15 @@ class Endorsements(Resource):
|
|||
db.session.add(new_report)
|
||||
db.session.commit()
|
||||
if not database.has_recent_endorsement(target_instance.id):
|
||||
activitypub_pm.pm_admins(
|
||||
message=f"Your instance has just been [endorsed](https://fediseer.com/faq#what-is-an-endorsement) by {instance.domain}",
|
||||
domain=target_instance.domain,
|
||||
software=target_instance.software,
|
||||
instance=target_instance,
|
||||
)
|
||||
try:
|
||||
activitypub_pm.pm_admins(
|
||||
message=f"Your instance has just been [endorsed](https://fediseer.com/faq#what-is-an-endorsement) by {instance.domain}",
|
||||
domain=target_instance.domain,
|
||||
software=target_instance.software,
|
||||
instance=target_instance,
|
||||
)
|
||||
except:
|
||||
pass
|
||||
logger.info(f"{instance.domain} Endorsed {domain}")
|
||||
return {"message":'Changed'}, 200
|
||||
|
||||
|
@ -236,11 +239,14 @@ class Endorsements(Resource):
|
|||
)
|
||||
db.session.add(new_report)
|
||||
db.session.commit()
|
||||
activitypub_pm.pm_admins(
|
||||
message=f"Oh no. {instance.domain} has just withdrawn the endorsement of your instance",
|
||||
domain=target_instance.domain,
|
||||
software=target_instance.software,
|
||||
instance=target_instance,
|
||||
)
|
||||
try:
|
||||
activitypub_pm.pm_admins(
|
||||
message=f"Oh no. {instance.domain} has just withdrawn the endorsement of your instance",
|
||||
domain=target_instance.domain,
|
||||
software=target_instance.software,
|
||||
instance=target_instance,
|
||||
)
|
||||
except:
|
||||
pass
|
||||
logger.info(f"{instance.domain} Withdrew endorsement from {domain}")
|
||||
return {"message":'Changed'}, 200
|
||||
|
|
|
@ -113,20 +113,26 @@ class Guarantees(Resource):
|
|||
)
|
||||
db.session.add(new_report)
|
||||
db.session.commit()
|
||||
activitypub_pm.pm_admins(
|
||||
message=f"Congratulations! Your instance has just been [guaranteed](https://fediseer.com/faq#what-is-a-guarantee) by {instance.domain}. \n\nThis is an automated PM by the [Fediseer](https://fediseer.com) service. Replies will not be read.\nPlease contact @db0@lemmy.dbzer0.com for further inquiries.",
|
||||
domain=target_instance.domain,
|
||||
software=target_instance.software,
|
||||
instance=target_instance,
|
||||
)
|
||||
try:
|
||||
activitypub_pm.pm_admins(
|
||||
message=f"Congratulations! Your instance has just been [guaranteed](https://fediseer.com/faq#what-is-a-guarantee) by {instance.domain}. \n\nThis is an automated PM by the [Fediseer](https://fediseer.com) service. Replies will not be read.\nPlease contact @db0@lemmy.dbzer0.com for further inquiries.",
|
||||
domain=target_instance.domain,
|
||||
software=target_instance.software,
|
||||
instance=target_instance,
|
||||
)
|
||||
except:
|
||||
pass
|
||||
orphan_ids = database.get_guarantee_chain(target_instance.id)
|
||||
for orphan in database.get_instances_by_ids(orphan_ids):
|
||||
activitypub_pm.pm_admins(
|
||||
message=f"Phew! You guarantor chain has been repaired as {instance.domain} has guaranteed for {domain}.",
|
||||
domain=orphan.domain,
|
||||
software=orphan.software,
|
||||
instance=orphan,
|
||||
)
|
||||
try:
|
||||
activitypub_pm.pm_admins(
|
||||
message=f"Phew! You guarantor chain has been repaired as {instance.domain} has guaranteed for {domain}.",
|
||||
domain=orphan.domain,
|
||||
software=orphan.software,
|
||||
instance=orphan,
|
||||
)
|
||||
except:
|
||||
pass
|
||||
orphan.unset_as_orphan()
|
||||
logger.info(f"{instance.domain} Guaranteed for {domain}")
|
||||
return {"message":'Changed'}, 200
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from fediseer.apis.v1.base import *
|
||||
from fediseer.messaging import activitypub_pm
|
||||
from fediseer.classes.user import User, Claim
|
||||
from fediseer import enums
|
||||
|
||||
class Whitelist(Resource):
|
||||
get_parser = reqparse.RequestParser()
|
||||
|
@ -8,7 +9,7 @@ class Whitelist(Resource):
|
|||
get_parser.add_argument("endorsements", required=False, default=0, type=int, help="Limit to this amount of endorsements of more", location="args")
|
||||
get_parser.add_argument("guarantors", required=False, default=1, type=int, help="Limit to this amount of guarantors of more", location="args")
|
||||
get_parser.add_argument("csv", required=False, type=bool, help="Set to true to return just the domains as a csv. Mutually exclusive with domains", location="args")
|
||||
get_parser.add_argument("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args")
|
||||
get_parser.add_argument("domains", required=False, type=str, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args")
|
||||
|
||||
@api.expect(get_parser)
|
||||
@cache.cached(timeout=10, query_string=True)
|
||||
|
@ -49,9 +50,10 @@ class WhitelistDomain(Resource):
|
|||
put_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||
put_parser.add_argument("admin", required=True, type=str, help="The username of the admin who wants to register this domain", location="json")
|
||||
put_parser.add_argument("guarantor", required=False, type=str, help="(Optional) The domain of the guaranteeing instance. They will receive a PM to validate you", location="json")
|
||||
put_parser.add_argument("pm_proxy", required=False, default=False, type=str, help="(Optional) If you do receive the PM from @fediseer@fediseer.com, set this to true to make the Fediseer PM your your API key via @fediseer@botsin.space. For this to work, ensure that botsin.space is not blocked in your instance and optimally follow @fediseer@botsin.space as well. If set, this will be used permanently for communication to your instance.", location="json")
|
||||
|
||||
|
||||
@api.expect(put_parser)
|
||||
@api.expect(put_parser,models.input_instance_claim, validate=True)
|
||||
@api.marshal_with(models.response_model_instances, code=200, description='Instances')
|
||||
@api.response(400, 'Bad Request', models.response_model_error)
|
||||
def put(self, domain):
|
||||
|
@ -76,7 +78,16 @@ class WhitelistDomain(Resource):
|
|||
existing_claim = database.find_claim(f"@{self.args.admin}@{domain}")
|
||||
if existing_claim:
|
||||
raise e.Forbidden(f"You have already claimed this instance as this admin. Please use the PATCH method to reset your API key.")
|
||||
api_key = activitypub_pm.pm_new_api_key(domain, self.args.admin, instance.software)
|
||||
if self.args.pm_proxy is not None:
|
||||
proxy = enums.PMProxy[self.args.pm_proxy]
|
||||
if instance.pm_proxy != proxy:
|
||||
instance.pm_proxy = proxy
|
||||
api_key = activitypub_pm.pm_new_api_key(
|
||||
domain=domain,
|
||||
username=self.args.admin,
|
||||
software=instance.software,
|
||||
proxy=instance.pm_proxy,
|
||||
)
|
||||
if not api_key:
|
||||
raise e.BadRequest("Failed to generate API Key")
|
||||
new_user = User(
|
||||
|
@ -93,12 +104,15 @@ class WhitelistDomain(Resource):
|
|||
db.session.add(new_claim)
|
||||
db.session.commit()
|
||||
if guarantor_instance:
|
||||
activitypub_pm.pm_admins(
|
||||
message=f"New instance {domain} was just registered with the Fediseer and have asked you to guarantee for them!",
|
||||
domain=guarantor_instance.domain,
|
||||
software=guarantor_instance.software,
|
||||
instance=guarantor_instance,
|
||||
)
|
||||
try:
|
||||
activitypub_pm.pm_admins(
|
||||
message=f"New instance {domain} was just registered with the Fediseer and have asked you to guarantee for them!",
|
||||
domain=guarantor_instance.domain,
|
||||
software=guarantor_instance.software,
|
||||
instance=guarantor_instance,
|
||||
)
|
||||
except:
|
||||
pass
|
||||
return instance.get_details(),200
|
||||
|
||||
patch_parser = reqparse.RequestParser()
|
||||
|
@ -108,6 +122,7 @@ class WhitelistDomain(Resource):
|
|||
patch_parser.add_argument("return_new_key", default=False, required=False, type=bool, help="If True, the key will be returned as part of the response instead of PM'd. IT will still PM a notification to you.", location="json")
|
||||
patch_parser.add_argument("sysadmins", default=None, required=False, type=int, help="How many sysadmins this instance has.", location="json")
|
||||
patch_parser.add_argument("moderators", default=None, required=False, type=int, help="How many moderators this instance has.", location="json")
|
||||
patch_parser.add_argument("pm_proxy", required=False, default=False, type=str, help="(Optional) If you do receive the PM from @fediseer@fediseer.com, set this to true to make the Fediseer PM your your API key via @fediseer@botsin.space. For this to work, ensure that botsin.space is not blocked in your instance and optimally follow @fediseer@botsin.space as well. If set, this will be used permanently for communication to your instance.", location="json")
|
||||
|
||||
|
||||
@api.expect(patch_parser,models.input_api_key_reset, validate=True)
|
||||
|
@ -133,6 +148,13 @@ class WhitelistDomain(Resource):
|
|||
if self.args.moderators is not None and instance.moderators != self.args.moderators:
|
||||
instance.moderators = self.args.moderators
|
||||
changed = True
|
||||
if self.args.pm_proxy is not None:
|
||||
logger.debug(self.args.pm_proxy)
|
||||
proxy = enums.PMProxy[self.args.pm_proxy]
|
||||
if instance.pm_proxy != proxy:
|
||||
activitypub_pm.pm_new_proxy_switch(proxy,instance.pm_proxy,instance,user.username)
|
||||
instance.pm_proxy = proxy
|
||||
changed = True
|
||||
if self.args.admin_username:
|
||||
requestor = None
|
||||
if self.args.admin_username != user.username or user.username == "fediseer":
|
||||
|
@ -148,9 +170,21 @@ class WhitelistDomain(Resource):
|
|||
if self.args.return_new_key:
|
||||
if requestor is None:
|
||||
requestor = f"{user.username}@{requestor_instance.domain}"
|
||||
new_key = activitypub_pm.pm_new_key_notification(domain, self.args.admin_username, instance.software, requestor=requestor)
|
||||
new_key = activitypub_pm.pm_new_key_notification(
|
||||
domain=domain,
|
||||
username=self.args.admin_username,
|
||||
software=instance.software,
|
||||
requestor=requestor,
|
||||
proxy=instance.pm_proxy,
|
||||
)
|
||||
else:
|
||||
new_key = activitypub_pm.pm_new_api_key(domain, self.args.admin_username, instance.software, requestor=requestor)
|
||||
new_key = activitypub_pm.pm_new_api_key(
|
||||
domain=domain,
|
||||
username=self.args.admin_username,
|
||||
software=instance.software,
|
||||
requestor=requestor,
|
||||
proxy=instance.pm_proxy,
|
||||
)
|
||||
user.api_key = hash_api_key(new_key)
|
||||
changed = True
|
||||
db.session.commit()
|
||||
|
|
|
@ -8,6 +8,7 @@ from sqlalchemy.dialects.postgresql import UUID
|
|||
|
||||
from loguru import logger
|
||||
from fediseer.flask import db, SQLITE_MODE
|
||||
from fediseer import enums
|
||||
|
||||
uuid_column_type = lambda: UUID(as_uuid=True) if not SQLITE_MODE else db.String(36)
|
||||
|
||||
|
@ -82,12 +83,14 @@ class Instance(db.Model):
|
|||
created = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||
oprhan_since = db.Column(db.DateTime, nullable=True)
|
||||
|
||||
|
||||
open_registrations = db.Column(db.Boolean, unique=False, nullable=False, index=True)
|
||||
email_verify = db.Column(db.Boolean, unique=False, nullable=False, index=True)
|
||||
software = db.Column(db.String(50), unique=False, nullable=False, index=True)
|
||||
sysadmins = db.Column(db.Integer, unique=False, nullable=True)
|
||||
moderators = db.Column(db.Integer, unique=False, nullable=True)
|
||||
pm_proxy = db.Column(Enum(enums.PMProxy), default=enums.PMProxy.NONE, nullable=False)
|
||||
|
||||
|
||||
approvals = db.relationship("Endorsement", back_populates="approving_instance", cascade="all, delete-orphan", foreign_keys=[Endorsement.approving_id])
|
||||
endorsements = db.relationship("Endorsement", back_populates="endorsed_instance", cascade="all, delete-orphan", foreign_keys=[Endorsement.endorsed_id])
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
FEDISEER_VERSION = "0.13.0"
|
||||
FEDISEER_VERSION = "0.14.0"
|
||||
SUPPORTED_SOFTWARE = [
|
||||
"lemmy",
|
||||
"mastodon",
|
||||
"friendica",
|
||||
"pleroma",
|
||||
"akkoma",
|
||||
"firefish",
|
||||
"iceshrimp",
|
||||
"misskey",
|
||||
]
|
||||
|
|
|
@ -10,3 +10,7 @@ class ReportActivity(enum.Enum):
|
|||
ADDED = 0
|
||||
DELETED = 1
|
||||
MODIFIED = 2
|
||||
|
||||
class PMProxy(enum.Enum):
|
||||
NONE = 0
|
||||
MASTODON = 1
|
||||
|
|
|
@ -3,19 +3,19 @@ from loguru import logger
|
|||
from pythorhead import Lemmy
|
||||
from fediseer.consts import FEDISEER_VERSION
|
||||
|
||||
def get_lemmy_admins(domain):
|
||||
def get_lemmy_admins(domain,software):
|
||||
requested_lemmy = Lemmy(f"https://{domain}")
|
||||
try:
|
||||
site = requested_lemmy.site.get()
|
||||
except Exception as err:
|
||||
logger.error(f"Error retrieving mastodon site info for {domain}: {err}")
|
||||
logger.error(f"Error retrieving {software} site info for {domain}: {err}")
|
||||
raise err
|
||||
if not site:
|
||||
logger.error(f"Error retrieving mastodon site info for {domain}")
|
||||
raise Exception(f"Error retrieving mastodon site info for {domain}")
|
||||
logger.error(f"Error retrieving {software} site info for {domain}")
|
||||
raise Exception(f"Error retrieving {software} site info for {domain}")
|
||||
return [a["person"]["name"] for a in site["admins"]]
|
||||
|
||||
def get_mastodon_admins(domain):
|
||||
def get_mastodon_admins(domain,software):
|
||||
site = None
|
||||
try:
|
||||
site = requests.get(f"https://{domain}/api/v2/instance")
|
||||
|
@ -26,12 +26,65 @@ def get_mastodon_admins(domain):
|
|||
return [site_json["contact"]["account"]["username"]]
|
||||
except Exception as err:
|
||||
if site is not None:
|
||||
logger.error(f"Error retrieving mastodon site info for {domain}: {err}. Request text: {site.text()}")
|
||||
logger.error(f"Error retrieving {software} site info for {domain}: {err}. Request text: {site.text()}")
|
||||
else:
|
||||
logger.error(f"Error retrieving mastodon site info for {domain}: {err}")
|
||||
raise Exception(f"Error retrieving mastodon site info for {domain}: {err}")
|
||||
logger.error(f"Error retrieving {software} site info for {domain}: {err}")
|
||||
raise Exception(f"Error retrieving {software} site info for {domain}: {err}")
|
||||
|
||||
def get_unknown_admins(domain):
|
||||
def get_misskey_admins(domain,software):
|
||||
site = None
|
||||
try:
|
||||
site = requests.get(f"https://{domain}/api/v1/instance")
|
||||
site_json = site.json()
|
||||
if "contact_account" not in site_json or "username" not in site_json["contact_account"]:
|
||||
logger.error(f"No admin contact is specified for {domain}.")
|
||||
raise Exception(f"No admin contact is specified for {domain}.")
|
||||
return [site_json["contact_account"]["username"]]
|
||||
except Exception as err:
|
||||
if site is not None:
|
||||
logger.error(f"Error retrieving {software} site info for {domain}: {err}. Request text: {site.text()}")
|
||||
else:
|
||||
logger.error(f"Error retrieving {software} site info for {domain}: {err}")
|
||||
raise Exception(f"Error retrieving {software} site info for {domain}: {err}")
|
||||
|
||||
def get_pleroma_admins(domain,software):
|
||||
site = None
|
||||
try:
|
||||
site = requests.get(f"https://{domain}/api/v1/instance")
|
||||
site_json = site.json()
|
||||
if "email" not in site_json or site_json["email"] is None or site_json["email"] == '':
|
||||
logger.error(f"No admin contact is specified for {domain}.")
|
||||
raise Exception(f"No admin contact is specified for {domain}.")
|
||||
admin_username = site_json["email"].split('@',1)[0]
|
||||
return [admin_username]
|
||||
except Exception as err:
|
||||
if site is not None:
|
||||
logger.error(f"Error retrieving {software} site info for {domain}: {err}. Request text: {site.text()}")
|
||||
else:
|
||||
logger.error(f"Error retrieving {software} site info for {domain}: {err}")
|
||||
raise Exception(f"Error retrieving {software} site info for {domain}: {err}")
|
||||
|
||||
def discover_admins(domain,software):
|
||||
site = None
|
||||
try:
|
||||
site = requests.get(f"https://{domain}/api/v1/instance")
|
||||
site_json = site.json()
|
||||
# Pleroma/Akkoma style
|
||||
if "email" in site_json:
|
||||
admin_username = site_json["email"].split('@',1)[0]
|
||||
return [admin_username]
|
||||
# Misskey/Firefish style
|
||||
if "contact_account" in site_json:
|
||||
return [site_json["contact_account"]["username"]]
|
||||
# Mastodon style
|
||||
if "contact" in site_json:
|
||||
return [site_json["contact"]["account"]["username"]]
|
||||
raise Exception(f"Site software '{software} does not match any of the known APIs")
|
||||
except Exception as err:
|
||||
logger.error(f"Error retrieving {software} site info for {domain}: {err}")
|
||||
raise Exception(f"Error retrieving {software} site info for {domain}: {err}")
|
||||
|
||||
def get_unknown_admins(domain,software):
|
||||
return []
|
||||
|
||||
def get_admin_for_software(software: str, domain: str):
|
||||
|
@ -39,12 +92,17 @@ def get_admin_for_software(software: str, domain: str):
|
|||
"lemmy": get_lemmy_admins,
|
||||
"mastodon": get_mastodon_admins,
|
||||
"friendica": get_mastodon_admins,
|
||||
"pleroma": get_pleroma_admins,
|
||||
"akkoma": get_pleroma_admins,
|
||||
"firefish": get_misskey_admins,
|
||||
"iceshrimp": get_misskey_admins,
|
||||
"misskey": get_misskey_admins,
|
||||
"unknown": get_unknown_admins,
|
||||
"wildcard": get_unknown_admins,
|
||||
}
|
||||
if software not in software_map:
|
||||
return []
|
||||
return software_map[software](domain)
|
||||
return discover_admins(domain,software)
|
||||
return software_map[software](domain,software)
|
||||
|
||||
|
||||
def get_nodeinfo(domain):
|
||||
|
|
|
@ -11,13 +11,16 @@ import secrets
|
|||
import markdown
|
||||
import fediseer.exceptions as e
|
||||
from pythorhead import Lemmy
|
||||
from mastodon import Mastodon
|
||||
from loguru import logger
|
||||
from fediseer.database import functions as database
|
||||
from fediseer.consts import SUPPORTED_SOFTWARE, FEDISEER_VERSION
|
||||
from fediseer.fediverse import get_admin_for_software
|
||||
from fediseer import enums
|
||||
|
||||
class ActivityPubPM:
|
||||
class ActivityPubPM:
|
||||
private_key = None
|
||||
mastodon = None
|
||||
def __init__(self):
|
||||
with open('private.pem', 'rb') as file:
|
||||
private_key_data = file.read()
|
||||
|
@ -34,6 +37,11 @@ class ActivityPubPM:
|
|||
},
|
||||
}
|
||||
|
||||
self.mastodon = Mastodon(
|
||||
access_token = 'pytooter_usercred.secret',
|
||||
api_base_url = f"https://{os.environ['MASTODON_INSTANCE']}"
|
||||
)
|
||||
|
||||
def send_pm_to_right_software(self, message, username, domain, software):
|
||||
software_map = {
|
||||
"lemmy": self.send_lemmy_pm,
|
||||
|
@ -41,7 +49,9 @@ class ActivityPubPM:
|
|||
"friendica": self.send_mastodon_pm,
|
||||
"fediseer": self.send_fediseer_pm,
|
||||
}
|
||||
return software_map[software](message, username, domain)
|
||||
if software in software_map:
|
||||
return software_map[software](message, username, domain)
|
||||
raise e.BadRequest("This software does not have direct PM implemented. Please retry using a MASTODON pm_proxy setting.")
|
||||
|
||||
def send_fediseer_pm(self, message, username, domain):
|
||||
document = copy.deepcopy(self.document_core)
|
||||
|
@ -115,56 +125,98 @@ class ActivityPubPM:
|
|||
response = requests.post(url, data=document, headers=headers)
|
||||
return response.ok
|
||||
|
||||
def pm_new_api_key(self, domain: str, username: str, software: str, requestor = None):
|
||||
def pm_new_api_key(self, domain: str, username: str, software: str, requestor = None, proxy = None):
|
||||
api_key = secrets.token_urlsafe(16)
|
||||
if requestor:
|
||||
pm_content = f"user '{requestor}' has initiated an API Key reset for your domain {domain} on the [Fediseer](https://fediseer.com)\n\nThe new API key is\n\n{api_key}\n\n**Please purge this message after storing the API key**"
|
||||
pm_content = f"user '{requestor}' has initiated an API Key reset for your domain {domain} on the [Fediseer](https://fediseer.com)\n\nThe new API key is\n\n{api_key}\n\n**Please purge this message after storing the API key or use the Fediseer API to generate a new API key without PM**"
|
||||
else:
|
||||
pm_content = f"Your API Key for domain {domain} is\n\n{api_key}\n\nUse this to perform operations on the [Fediseer](https://fediseer.com).\n\n**Please purge this message after storing the API key**"
|
||||
if not self.send_pm_to_right_software(
|
||||
message=pm_content,
|
||||
username=username,
|
||||
domain=domain,
|
||||
software=software
|
||||
):
|
||||
raise e.BadRequest("API Key PM failed")
|
||||
pm_content = f"Your API Key for domain {domain} is\n\n{api_key}\n\nUse this to perform operations on the [Fediseer](https://fediseer.com).\n\n**Please purge this message after storing the API key or use the Fediseer API to generate a new API key without PM**"
|
||||
if proxy == enums.PMProxy.MASTODON:
|
||||
self.mastodon_proxy_pm(pm_content,username,domain)
|
||||
else:
|
||||
if not self.send_pm_to_right_software(
|
||||
message=pm_content,
|
||||
username=username,
|
||||
domain=domain,
|
||||
software=software
|
||||
):
|
||||
raise e.BadRequest("API Key PM failed")
|
||||
return api_key
|
||||
|
||||
def pm_new_key_notification(self, domain: str, username: str, software: str, requestor: str):
|
||||
def pm_new_key_notification(self, domain: str, username: str, software: str, requestor: str, proxy = None):
|
||||
api_key = secrets.token_urlsafe(16)
|
||||
pm_content = f"user '{requestor}' has initiated an API Key reset for your domain {domain} on the [Fediseer](https://fediseer.com)\n\nThe new API key was provided in the response already\n"
|
||||
logger.info(f"user '{requestor}' reset the API key for {username}@{domain} on the response.")
|
||||
if not self.send_pm_to_right_software(
|
||||
message=pm_content,
|
||||
username=username,
|
||||
domain=domain,
|
||||
software=software
|
||||
):
|
||||
raise e.BadRequest("API Key PM failed")
|
||||
if proxy == enums.PMProxy.MASTODON:
|
||||
self.mastodon_proxy_pm(pm_content,username,domain)
|
||||
else:
|
||||
if not self.send_pm_to_right_software(
|
||||
message=pm_content,
|
||||
username=username,
|
||||
domain=domain,
|
||||
software=software
|
||||
):
|
||||
raise e.BadRequest("API Key PM failed")
|
||||
return api_key
|
||||
|
||||
|
||||
def pm_new_proxy_switch(self, new_proxy: enums.PMProxy, old_proxy: enums.PMProxy, instance: str, requestor: str):
|
||||
if new_proxy == enums.PMProxy.NONE:
|
||||
pm_content = f"user '{requestor}' has switched the fediseer messaging for {instance.domain} to not use a proxy (was {old_proxy.name})."
|
||||
else:
|
||||
pm_content = f"user '{requestor}' has switched the fediseer messaging for {instance.domain} to use a {new_proxy.name} proxy (was {old_proxy.name})."
|
||||
logger.info(f"user '{requestor}' changed instance pm_proxy setting from {old_proxy} to {new_proxy} for {instance.domain}.")
|
||||
admins = [a.username for a in database.find_admins_by_instance(instance)]
|
||||
for admin_username in admins:
|
||||
if instance.domain == "lemmy.dbzer0.com" and admin_username != 'db0': # Debug
|
||||
logger.debug(f"skipping admin {admin_username} for debug")
|
||||
continue
|
||||
for proxy in [new_proxy,old_proxy]:
|
||||
if proxy == enums.PMProxy.MASTODON:
|
||||
self.mastodon_proxy_pm(pm_content,admin_username,instance.domain)
|
||||
else:
|
||||
if not self.send_pm_to_right_software(
|
||||
message=pm_content,
|
||||
username=admin_username,
|
||||
domain=instance.domain,
|
||||
software=instance.software
|
||||
):
|
||||
raise e.BadRequest("API Key PM failed")
|
||||
|
||||
def pm_admins(self, message: str, domain: str, software: str, instance):
|
||||
if software not in SUPPORTED_SOFTWARE:
|
||||
return None
|
||||
proxy = None
|
||||
admins = database.find_admins_by_instance(instance)
|
||||
if not admins:
|
||||
try:
|
||||
admins = get_admin_for_software(software, domain)
|
||||
except Exception as err:
|
||||
if software not in SUPPORTED_SOFTWARE:
|
||||
logger.warning(f"Failed to figure out admins from {software}: {domain}")
|
||||
raise e.BadRequest(f"Failed to retrieve admin list: {err}")
|
||||
else:
|
||||
admins = [a.username for a in admins]
|
||||
proxy = instance.pm_proxy
|
||||
if not admins:
|
||||
raise e.BadRequest(f"Could not determine admins for {domain}")
|
||||
for admin_username in admins:
|
||||
if not self.send_pm_to_right_software(
|
||||
message=message,
|
||||
username=admin_username,
|
||||
domain=domain,
|
||||
software=software
|
||||
):
|
||||
raise e.BadRequest("Admin PM Failed")
|
||||
if proxy == enums.PMProxy.MASTODON:
|
||||
self.mastodon_proxy_pm(message,admin_username,domain)
|
||||
else:
|
||||
if not self.send_pm_to_right_software(
|
||||
message=message,
|
||||
username=admin_username,
|
||||
domain=domain,
|
||||
software=software
|
||||
):
|
||||
raise e.BadRequest("Admin PM Failed")
|
||||
|
||||
|
||||
def mastodon_proxy_pm(self, message, username, domain):
|
||||
try:
|
||||
self.mastodon.status_post(
|
||||
status=f"@{username}@{domain} {message}",
|
||||
visibility="direct",
|
||||
)
|
||||
except Exception as err:
|
||||
raise e.BadRequest(f"PM via Mastodon Proxy Failed: {err}")
|
||||
|
||||
activitypub_pm = ActivityPubPM()
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import os
|
||||
from mastodon import Mastodon
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
Mastodon.create_app(
|
||||
'fediseer',
|
||||
api_base_url = f"https://{os.environ['MASTODON_INSTANCE']}",
|
||||
to_file = 'pytooter_clientcred.secret'
|
||||
)
|
||||
|
||||
mastodon = Mastodon(
|
||||
client_id = 'pytooter_clientcred.secret',
|
||||
api_base_url = f"https://{os.environ['MASTODON_INSTANCE']}"
|
||||
)
|
||||
mastodon.log_in(
|
||||
os.environ['MASTODON_EMAIL'],
|
||||
os.environ['MASTODON_PASSWORD'],
|
||||
to_file = 'pytooter_usercred.secret'
|
||||
)
|
|
@ -17,4 +17,4 @@ pythorhead>=0.8.2
|
|||
bleach
|
||||
boto3
|
||||
pybadges
|
||||
|
||||
mastodon.py
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
CREATE TYPE pmproxy AS ENUM ('MASTODON');
|
||||
ALTER TYPE pmproxy ADD VALUE 'NONE';
|
||||
ALTER TABLE instances ADD COLUMN pm_proxy pmproxy default 'NONE';
|
Loading…
Reference in New Issue