Add real-time bridge status push option

This commit is contained in:
Tulir Asokan
2021-06-09 20:04:17 +03:00
parent ec152cbd9d
commit c385aa0b8d
4 changed files with 32 additions and 12 deletions
+4
View File
@@ -10,6 +10,10 @@ homeserver:
asmux: false asmux: false
# Number of retries for all HTTP requests if the homeserver isn't reachable. # Number of retries for all HTTP requests if the homeserver isn't reachable.
http_retry_count: 4 http_retry_count: 4
# The URL to push real-time bridge status to.
# If set, the bridge will make POST requests to this URL whenever a user's Telegram connection state changes.
# The bridge will use the appservice as_token to authorize requests.
status_endpoint: null
# Application service host/registration related details # Application service host/registration related details
# Changing these values requires regeneration of the registration. # Changing these values requires regeneration of the registration.
+25 -9
View File
@@ -15,7 +15,6 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from typing import (Awaitable, Dict, List, Iterable, NamedTuple, Optional, Tuple, Any, cast, from typing import (Awaitable, Dict, List, Iterable, NamedTuple, Optional, Tuple, Any, cast,
TYPE_CHECKING) TYPE_CHECKING)
from collections import defaultdict
from datetime import datetime, timezone from datetime import datetime, timezone
import logging import logging
import asyncio import asyncio
@@ -32,7 +31,7 @@ from telethon.tl.functions.account import UpdateStatusRequest
from mautrix.client import Client from mautrix.client import Client
from mautrix.errors import MatrixRequestError, MNotFound from mautrix.errors import MatrixRequestError, MNotFound
from mautrix.types import UserID, RoomID, PushRuleScope, PushRuleKind, PushActionType, RoomTagInfo from mautrix.types import UserID, RoomID, PushRuleScope, PushRuleKind, PushActionType, RoomTagInfo
from mautrix.bridge import BaseUser from mautrix.bridge import BaseUser, BridgeState
from mautrix.util.logging import TraceLogger from mautrix.util.logging import TraceLogger
from mautrix.util.opt_prometheus import Gauge from mautrix.util.opt_prometheus import Gauge
@@ -52,6 +51,11 @@ SearchResult = NamedTuple('SearchResult', puppet='pu.Puppet', similarity=int)
METRIC_LOGGED_IN = Gauge('bridge_logged_in', 'Users logged into bridge') METRIC_LOGGED_IN = Gauge('bridge_logged_in', 'Users logged into bridge')
METRIC_CONNECTED = Gauge('bridge_connected', 'Users connected to Telegram') METRIC_CONNECTED = Gauge('bridge_connected', 'Users connected to Telegram')
BridgeState.human_readable_errors.update({
"tg-not-connected": "Your Telegram connection failed",
"logged-out": "You're not logged into Telegram",
})
class User(AbstractUser, BaseUser): class User(AbstractUser, BaseUser):
log: TraceLogger = logging.getLogger("mau.user") log: TraceLogger = logging.getLogger("mau.user")
@@ -74,8 +78,9 @@ class User(AbstractUser, BaseUser):
saved_contacts: int = 0, is_bot: bool = False, saved_contacts: int = 0, is_bot: bool = False,
db_portals: Optional[Iterable[Tuple[TelegramID, TelegramID]]] = None, db_portals: Optional[Iterable[Tuple[TelegramID, TelegramID]]] = None,
db_instance: Optional[DBUser] = None) -> None: db_instance: Optional[DBUser] = None) -> None:
super().__init__() AbstractUser.__init__(self)
self.mxid = mxid self.mxid = mxid
BaseUser.__init__(self)
self.tgid = tgid self.tgid = tgid
self.is_bot = is_bot self.is_bot = is_bot
self.username = username self.username = username
@@ -87,12 +92,8 @@ class User(AbstractUser, BaseUser):
self.db_portals = db_portals or [] self.db_portals = db_portals or []
self._db_instance = db_instance self._db_instance = db_instance
self._ensure_started_lock = asyncio.Lock() self._ensure_started_lock = asyncio.Lock()
self.dm_update_lock = asyncio.Lock()
self._metric_value = defaultdict(lambda: False)
self._track_connection_task = None self._track_connection_task = None
self.command_status = None
(self.relaybot_whitelisted, (self.relaybot_whitelisted,
self.whitelisted, self.whitelisted,
self.puppet_whitelisted, self.puppet_whitelisted,
@@ -104,8 +105,6 @@ class User(AbstractUser, BaseUser):
if tgid: if tgid:
self.by_tgid[tgid] = self self.by_tgid[tgid] = self
self.log = self.log.getChild(self.mxid)
@property @property
def name(self) -> str: def name(self) -> str:
return self.mxid return self.mxid
@@ -219,6 +218,21 @@ class User(AbstractUser, BaseUser):
connected = bool(self.client._sender._transport_connected connected = bool(self.client._sender._transport_connected
if self.client and self.client._sender else False) if self.client and self.client._sender else False)
self._track_metric(METRIC_CONNECTED, connected) self._track_metric(METRIC_CONNECTED, connected)
await self.push_bridge_state(ok=connected, ttl=3600 if connected else 240,
error="tg-not-connected" if not connected else None)
async def fill_bridge_state(self, state: BridgeState) -> None:
await super().fill_bridge_state(state)
state.remote_id = str(self.tgid)
state.remote_name = self.human_tg_id
async def get_bridge_state(self) -> BridgeState:
if not self.client:
return BridgeState(ok=False, error="logged-out")
elif not self.client._sender or not self.client._sender._transport_connected:
return BridgeState(ok=False, error="tg-not-connected")
else:
return BridgeState(ok=True)
async def stop(self) -> None: async def stop(self) -> None:
await super().stop() await super().stop()
@@ -226,6 +240,7 @@ class User(AbstractUser, BaseUser):
self._track_connection_task.cancel() self._track_connection_task.cancel()
self._track_connection_task = None self._track_connection_task = None
self._track_metric(METRIC_CONNECTED, False) self._track_metric(METRIC_CONNECTED, False)
await self.push_bridge_state(ok=False, error="tg-not-connected")
async def post_login(self, info: TLUser = None, first_login: bool = False) -> None: async def post_login(self, info: TLUser = None, first_login: bool = False) -> None:
if config["metrics.enabled"] and not self._track_connection_task: if config["metrics.enabled"] and not self._track_connection_task:
@@ -330,6 +345,7 @@ class User(AbstractUser, BaseUser):
self.delete() self.delete()
await self.stop() await self.stop()
self._track_metric(METRIC_LOGGED_IN, False) self._track_metric(METRIC_LOGGED_IN, False)
await self.push_bridge_state(ok=False, error="logged-out")
return True return True
def _search_local(self, query: str, max_results: int = 5, min_similarity: int = 45 def _search_local(self, query: str, max_results: int = 5, min_similarity: int = 45
+2 -2
View File
@@ -15,13 +15,13 @@ qrcode>=6,<7
moviepy>=1,<2 moviepy>=1,<2
#/metrics #/metrics
prometheus_client>=0.6,<0.11 prometheus_client>=0.6,<0.12
#/postgres #/postgres
psycopg2-binary>=2,<3 psycopg2-binary>=2,<3
#/e2be #/e2be
asyncpg>=0.20,<0.23 asyncpg>=0.20,<0.24
python-olm>=3,<4 python-olm>=3,<4
pycryptodome>=3,<4 pycryptodome>=3,<4
unpaddedbase64>=1,<2 unpaddedbase64>=1,<2
+1 -1
View File
@@ -5,6 +5,6 @@ python-magic>=0.4,<0.5
commonmark>=0.8,<0.10 commonmark>=0.8,<0.10
aiohttp>=3,<4 aiohttp>=3,<4
yarl>=1,<2 yarl>=1,<2
mautrix>=0.9.1,<0.10 mautrix>=0.9.3,<0.10
telethon>=1.20,<1.22 telethon>=1.20,<1.22
telethon-session-sqlalchemy>=0.2.14,<0.3 telethon-session-sqlalchemy>=0.2.14,<0.3