Add a public server list to the initial login page
This commit is contained in:
@@ -2,12 +2,17 @@
|
||||
|
||||
import asyncio
|
||||
import logging as log
|
||||
import math
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, DefaultDict, Dict, List, Optional, Tuple
|
||||
|
||||
import aiohttp
|
||||
from appdirs import AppDirs
|
||||
|
||||
import nio
|
||||
@@ -18,7 +23,7 @@ from .matrix_client import MatrixClient
|
||||
from .media_cache import MediaCache
|
||||
from .models import SyncId
|
||||
from .models.filters import FieldSubstringFilter
|
||||
from .models.items import Account, Event
|
||||
from .models.items import Account, Event, Homeserver, PingStatus
|
||||
from .models.model import Model
|
||||
from .models.model_store import ModelStore
|
||||
from .presence import Presence
|
||||
@@ -107,6 +112,9 @@ class Backend:
|
||||
self._sso_server: Optional[SSOServer] = None
|
||||
self._sso_server_task: Optional[asyncio.Future] = None
|
||||
|
||||
self._ping_tasks: Dict[str, asyncio.Future] = {}
|
||||
self._stability_tasks: Dict[str, asyncio.Future] = {}
|
||||
|
||||
self.profile_cache: Dict[str, nio.ProfileGetResponse] = {}
|
||||
self.get_profile_locks: DefaultDict[str, asyncio.Lock] = \
|
||||
DefaultDict(asyncio.Lock) # {user_id: lock}
|
||||
@@ -474,3 +482,139 @@ class Backend:
|
||||
|
||||
if device.ed25519 == ed25519_key:
|
||||
client.blacklist_device(device)
|
||||
|
||||
|
||||
async def _ping_homeserver(
|
||||
self, session: aiohttp.ClientSession, homeserver_url: str,
|
||||
) -> None:
|
||||
"""Ping a homeserver present in our model and set its `ping` field."""
|
||||
|
||||
item = self.models["homeservers"][homeserver_url]
|
||||
times = []
|
||||
|
||||
for i in range(16):
|
||||
start = time.time()
|
||||
|
||||
try:
|
||||
await session.get(f"{homeserver_url}/_matrix/client/versions")
|
||||
except Exception as err:
|
||||
log.warning("Failed pinging %s: %r", homeserver_url, err)
|
||||
item.status = PingStatus.Failed
|
||||
return
|
||||
|
||||
times.append(round((time.time() - start) * 1000))
|
||||
|
||||
if i == 7 or i == 15:
|
||||
item.set_fields(
|
||||
ping=sum(times) // len(times), status=PingStatus.Done,
|
||||
)
|
||||
|
||||
|
||||
async def _get_homeserver_stability(
|
||||
self,
|
||||
session: aiohttp.ClientSession,
|
||||
homeserver_url: str,
|
||||
uptimerobot_id: int,
|
||||
) -> None:
|
||||
api = "https://matrixservers.anchel.nl/api/getMonitor/wkMJmFGvo2?m={}"
|
||||
|
||||
response = await session.get(api.format(uptimerobot_id))
|
||||
logs = (await response.json())["monitor"]["logs"]
|
||||
stability = 100.0
|
||||
|
||||
for period in logs:
|
||||
started_at = datetime.fromtimestamp(period["time"])
|
||||
time_since_now = datetime.now() - started_at
|
||||
|
||||
if time_since_now.days > 30 or period["class"] != "danger":
|
||||
continue
|
||||
|
||||
lasted_hours, lasted_mins = [
|
||||
int(x.split()[0]) for x in period["duration"].split(", ")
|
||||
]
|
||||
lasted_mins += lasted_hours * 60
|
||||
stability -= (
|
||||
(lasted_mins * stability / 1000) /
|
||||
max(1, time_since_now.days / 3)
|
||||
)
|
||||
|
||||
self.models["homeservers"][homeserver_url].stability = stability
|
||||
|
||||
|
||||
async def _add_homeserver_item(
|
||||
self,
|
||||
session: aiohttp.ClientSession,
|
||||
homeserver_url: str,
|
||||
uptimerobot_id: int,
|
||||
**fields,
|
||||
) -> Homeserver:
|
||||
"""Add homeserver to our model & start info-gathering tasks."""
|
||||
|
||||
if not re.match(r"^https?://.+", homeserver_url):
|
||||
homeserver_url = f"https://{homeserver_url}"
|
||||
|
||||
if fields.get("country") == "USA":
|
||||
fields["country"] = "United States"
|
||||
|
||||
if homeserver_url in self._ping_tasks:
|
||||
self._ping_tasks[homeserver_url].cancel()
|
||||
|
||||
if homeserver_url in self._stability_tasks:
|
||||
self._stability_tasks[homeserver_url].cancel()
|
||||
|
||||
item = Homeserver(id=homeserver_url, **fields)
|
||||
self.models["homeservers"][homeserver_url] = item
|
||||
|
||||
self._ping_tasks[homeserver_url] = asyncio.ensure_future(
|
||||
self._ping_homeserver(session, homeserver_url),
|
||||
)
|
||||
|
||||
self._stability_tasks[homeserver_url] = asyncio.ensure_future(
|
||||
self._get_homeserver_stability(
|
||||
session, item.id, uptimerobot_id,
|
||||
),
|
||||
)
|
||||
|
||||
return item
|
||||
|
||||
|
||||
async def fetch_homeservers(self) -> None:
|
||||
"""Retrieve a list of public homeservers and add them to our model."""
|
||||
|
||||
api_list = "https://publiclist.anchel.nl/staticlist.json"
|
||||
|
||||
tmout = aiohttp.ClientTimeout(total=20)
|
||||
session = aiohttp.ClientSession(raise_for_status=True, timeout=tmout)
|
||||
response = await session.get(api_list)
|
||||
data = (await response.json())["staticlist"]
|
||||
|
||||
await self._add_homeserver_item(
|
||||
session = session,
|
||||
homeserver_url = "https://matrix-client.matrix.org",
|
||||
name = "matrix.org",
|
||||
site_url = "https://matrix.org",
|
||||
country = "Cloudflare",
|
||||
uptimerobot_id = 783115140,
|
||||
)
|
||||
|
||||
await self._add_homeserver_item(
|
||||
session = session,
|
||||
homeserver_url = "https://mozilla.modular.im",
|
||||
name = "mozilla.org",
|
||||
site_url = "https://mozilla.org",
|
||||
country = "United States",
|
||||
uptimerobot_id = 784321494,
|
||||
)
|
||||
|
||||
for server in data:
|
||||
if server["homeserver"].startswith("http://"): # insecure server
|
||||
continue
|
||||
|
||||
await self._add_homeserver_item(
|
||||
session = session,
|
||||
homeserver_url = server["homeserver"],
|
||||
name = server["name"],
|
||||
site_url = server["url"],
|
||||
country = server["country"],
|
||||
uptimerobot_id = server["uptrid"],
|
||||
)
|
||||
|
@@ -30,6 +30,30 @@ class TypeSpecifier(AutoStrEnum):
|
||||
MembershipChange = auto()
|
||||
|
||||
|
||||
class PingStatus(AutoStrEnum):
|
||||
"""Enum for the status of a homeserver ping operation."""
|
||||
|
||||
Done = auto()
|
||||
Pinging = auto()
|
||||
Failed = auto()
|
||||
|
||||
|
||||
@dataclass
|
||||
class Homeserver(ModelItem):
|
||||
"""A homeserver we can connect to. The `id` field is the server's URL."""
|
||||
|
||||
id: str = field()
|
||||
name: str = field()
|
||||
site_url: str = field()
|
||||
country: str = field()
|
||||
ping: int = -1
|
||||
status: PingStatus = PingStatus.Pinging
|
||||
stability: float = -1
|
||||
|
||||
def __lt__(self, other: "Homeserver") -> bool:
|
||||
return (self.name.lower(), self.id) < (other.name.lower(), other.id)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Account(ModelItem):
|
||||
"""A logged in matrix account."""
|
||||
|
Reference in New Issue
Block a user