feat: add event watcher
This commit is contained in:
@@ -3,6 +3,7 @@ from pyrogram import Client
|
||||
|
||||
from userbot.modules.contacts import ContactCache
|
||||
from userbot.modules.folders import FolderCache
|
||||
from userbot.modules.watches import WatchCache
|
||||
from utils.policy.models import CaptureToggles, ChatMeta, PolicySet
|
||||
from utils.policy.repository import load_policy_set
|
||||
from utils.policy.resolver import resolve
|
||||
@@ -23,6 +24,7 @@ class CaptureContext:
|
||||
self.storage = storage
|
||||
self.folders = folders
|
||||
self.contacts = contacts
|
||||
self.watches = WatchCache(pool, account_id)
|
||||
self.policies = PolicySet()
|
||||
|
||||
async def reload_policies(self) -> None:
|
||||
@@ -44,4 +46,5 @@ async def build_capture_context(
|
||||
await contacts.refresh()
|
||||
ctx = CaptureContext(account_id, pool, storage, folders, contacts)
|
||||
await ctx.reload_policies()
|
||||
await ctx.watches.refresh()
|
||||
return ctx
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from userbot.modules.watches.cache import WatchCache
|
||||
|
||||
__all__ = ["WatchCache"]
|
||||
@@ -0,0 +1,60 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from userbot.modules.watches.evaluator import (
|
||||
KIND_KEYWORD,
|
||||
KIND_PEER_ONLINE,
|
||||
match_keyword,
|
||||
match_peer_online,
|
||||
)
|
||||
from utils.logging import logger
|
||||
from utils.read import watches as watches_read
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import asyncpg
|
||||
|
||||
from utils.read.models import WatchView
|
||||
|
||||
|
||||
class WatchCache:
|
||||
def __init__(self, pool: asyncpg.Pool, account_id: int) -> None:
|
||||
self._pool = pool
|
||||
self._account_id = account_id
|
||||
self.watches: list[WatchView] = []
|
||||
self._online: set[int] = set()
|
||||
|
||||
async def refresh(self) -> None:
|
||||
rows = await watches_read.list_watches(self._pool, self._account_id)
|
||||
self.watches = [watch for watch in rows if watch.enabled]
|
||||
logger.info(f"[green]Watches cached:[/] {len(self.watches)}")
|
||||
|
||||
async def on_text(self, chat_id: int, message_id: int, text: str | None) -> None:
|
||||
if not text:
|
||||
return
|
||||
for watch in self.watches:
|
||||
if watch.kind == KIND_KEYWORD and match_keyword(
|
||||
text, chat_id, watch.params
|
||||
):
|
||||
await watches_read.insert_alert(
|
||||
self._pool,
|
||||
self._account_id,
|
||||
watch.id,
|
||||
{"chat_id": chat_id, "message_id": message_id},
|
||||
dedup_key=f"{chat_id}:{message_id}",
|
||||
)
|
||||
|
||||
async def on_status(self, peer_id: int, *, is_online: bool) -> None:
|
||||
if not is_online:
|
||||
self._online.discard(peer_id)
|
||||
return
|
||||
if peer_id in self._online:
|
||||
return
|
||||
self._online.add(peer_id)
|
||||
for watch in self.watches:
|
||||
if watch.kind == KIND_PEER_ONLINE and match_peer_online(
|
||||
peer_id, watch.params
|
||||
):
|
||||
await watches_read.insert_alert(
|
||||
self._pool, self._account_id, watch.id, {"peer_id": peer_id}
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
KIND_KEYWORD = "keyword"
|
||||
KIND_PEER_ONLINE = "peer_online"
|
||||
|
||||
|
||||
def match_keyword(text: str, chat_id: int, params: dict[str, Any]) -> bool:
|
||||
target_chat = params.get("chat_id")
|
||||
if target_chat is not None and target_chat != chat_id:
|
||||
return False
|
||||
pattern = params.get("pattern")
|
||||
if not pattern:
|
||||
return False
|
||||
if params.get("regex"):
|
||||
try:
|
||||
return re.search(pattern, text) is not None
|
||||
except re.error:
|
||||
return False
|
||||
return pattern.casefold() in text.casefold()
|
||||
|
||||
|
||||
def match_peer_online(peer_id: int, params: dict[str, Any]) -> bool:
|
||||
return params.get("peer_id") == peer_id
|
||||
Reference in New Issue
Block a user