14
14
# You should have received a copy of the GNU Affero General Public License
15
15
# along with this program. If not, see <https://www.gnu.org/licenses/>.
16
16
from typing import Dict , Any
17
+ import asyncio
17
18
18
19
from telethon import __version__ as __telethon_version__
19
20
from alchemysession import AlchemySessionContainer
20
21
21
22
from mautrix .types import UserID , RoomID
22
23
from mautrix .bridge import Bridge
23
24
from mautrix .util .db import Base
25
+ from mautrix .util .opt_prometheus import Gauge
24
26
25
27
from .web .provisioning import ProvisioningAPI
26
28
from .web .public import PublicBridgeWebsite
29
31
from .config import Config
30
32
from .context import Context
31
33
from .db import init as init_db
34
+ from .db .user_activity import UserActivity
32
35
from .formatter import init as init_formatter
33
36
from .matrix import MatrixHandler
34
37
from .portal import Portal , init as init_portal
41
44
except ImportError :
42
45
prometheus = None
43
46
47
+ ACTIVE_USER_METRICS_INTERVAL_S = 5
48
+ METRIC_ACTIVE_PUPPETS = Gauge ('bridge_active_puppets_total' , 'Number of active Telegram users bridged into Matrix' )
49
+ METRIC_BLOCKING = Gauge ('bridge_blocked' , 'Is the bridge currently blocking messages' )
44
50
45
51
class TelegramBridge (Bridge ):
46
52
module = "mautrix_telegram"
@@ -58,6 +64,9 @@ class TelegramBridge(Bridge):
58
64
session_container : AlchemySessionContainer
59
65
bot : Bot
60
66
67
+ periodic_active_metrics_task : asyncio .Task
68
+ is_blocked : bool = False
69
+
61
70
def prepare_db (self ) -> None :
62
71
super ().prepare_db ()
63
72
init_db (self .db )
@@ -92,6 +101,7 @@ def prepare_bridge(self) -> None:
92
101
self .add_startup_actions (self .bot .start ())
93
102
if self .config ["bridge.resend_bridge_info" ]:
94
103
self .add_startup_actions (self .resend_bridge_info ())
104
+ self .add_startup_actions (self ._loop_active_puppet_metric ())
95
105
96
106
async def resend_bridge_info (self ) -> None :
97
107
self .config ["bridge.resend_bridge_info" ] = False
@@ -127,6 +137,35 @@ def is_bridge_ghost(self, user_id: UserID) -> bool:
127
137
async def count_logged_in_users (self ) -> int :
128
138
return len ([user for user in User .by_tgid .values () if user .tgid ])
129
139
140
+ async def _update_active_puppet_metric (self ) -> None :
141
+ active_users = UserActivity .get_active_count (
142
+ self .config ['bridge.limits.puppet_inactivity_days' ],
143
+ self .config ['bridge.limits.min_puppet_activity_days' ],
144
+ )
145
+
146
+ block_on_limit_reached = self .config ['bridge.limits.block_on_limit_reached' ]
147
+ max_puppet_limit = self .config ['bridge.limits.max_puppet_limit' ]
148
+ if block_on_limit_reached is not None and max_puppet_limit is not None :
149
+ self .is_blocked = max_puppet_limit < active_users
150
+ METRIC_BLOCKING .set (int (self .is_blocked ))
151
+ self .log .debug (f"Current active puppet count is { active_users } " )
152
+ METRIC_ACTIVE_PUPPETS .set (active_users )
153
+
154
+ async def _loop_active_puppet_metric (self ) -> None :
155
+ while True :
156
+ try :
157
+ await asyncio .sleep (ACTIVE_USER_METRICS_INTERVAL_S )
158
+ except asyncio .CancelledError :
159
+ return
160
+ self .log .info ("Executing periodic active puppet metric check" )
161
+ try :
162
+ await self ._update_active_puppet_metric ()
163
+ except asyncio .CancelledError :
164
+ return
165
+ except Exception as e :
166
+ self .log .exception (f"Error while checking: { e } " )
167
+
168
+
130
169
async def manhole_global_namespace (self , user_id : UserID ) -> Dict [str , Any ]:
131
170
return {
132
171
** await super ().manhole_global_namespace (user_id ),
0 commit comments