Compare commits
2
Commits
698e4d60a3
...
4945c7cbd3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4945c7cbd3 | ||
|
|
9e672623ac |
No files matched your search
@@ -2,7 +2,6 @@
|
||||
|
||||
A read-only Telegram client that keeps everything. A userbot on your own account logs every incoming message into Postgres, including the ones later deleted or edited, along with media, reactions, presence and stories. A web client reads that archive back, and an MCP server hands it to agents.
|
||||
|
||||
<<<<<<< before updating
|
||||
Useful on its own: a Telegram where nothing disappears. In the [beaver](https://git.kotikot.com/beaver) setup it is one of the agent's hands - the dispatcher reads chat history by the `chat_id` stored in a person's card.
|
||||
|
||||
## What it keeps
|
||||
@@ -32,7 +31,8 @@ Read-only: `list_watched_bots`, `get_bot_stats`, `get_bot_updates`, `search_mess
|
||||
```sh
|
||||
make env # .env and docker-compose.override.yml from the examples
|
||||
make -C backend session # log the account in once; the session file stays in backend/sessions
|
||||
make deploy # build what changed, migrate, start the containers
|
||||
make -C backend migrate # the schema
|
||||
make recreate # build and start the services in COMPOSE_PROFILES
|
||||
```
|
||||
|
||||
`COMPOSE_PROFILES` in `.env` selects what runs on the machine, `docker-compose.override.yml` in each part holds the published ports and the `caddy` network aliases. The web client and the MCP endpoint are served by `api`; the bearer for both is `AUTH__TOKEN`.
|
||||
@@ -44,12 +44,3 @@ make fmt
|
||||
make check
|
||||
make -C frontend dev
|
||||
```
|
||||
=======
|
||||
```sh
|
||||
make env # .env and docker-compose.override.yml from the examples
|
||||
make recreate # the services in COMPOSE_PROFILES
|
||||
make check
|
||||
```
|
||||
|
||||
Parts: `backend/`, `frontend/`, each with its own Makefile.
|
||||
>>>>>>> after updating
|
||||
@@ -1,13 +1,4 @@
|
||||
COMPOSE := cd .. && docker compose
|
||||
<<<<<<< before updating
|
||||
IMAGE := beavergram/backend
|
||||
IMAGE_INPUTS := pyproject.toml uv.lock Dockerfile
|
||||
CODE := src scripts ../frontend
|
||||
SERVICES := api userbot watcher
|
||||
changed = ! git diff --quiet $(1) -- $(2) 2>/dev/null
|
||||
stamp = { git update-ref $(1) HEAD 2>/dev/null || true; }
|
||||
=======
|
||||
>>>>>>> after updating
|
||||
|
||||
.PHONY: fmt check build migrate revision session
|
||||
|
||||
|
||||
@@ -16,13 +16,12 @@ if config.config_file_name is not None:
|
||||
target_metadata = SQLModel.metadata
|
||||
|
||||
|
||||
<<<<<<< before updating
|
||||
def _async_url() -> str:
|
||||
return env.db.connection_url.replace("postgresql://", "postgresql+asyncpg://", 1)
|
||||
=======
|
||||
|
||||
|
||||
def include_name(name: str | None, type_: str, _parents: object) -> bool:
|
||||
return not (type_ == "table" and name is not None and name.startswith("taskiq_"))
|
||||
>>>>>>> after updating
|
||||
|
||||
|
||||
def run_migrations_offline() -> None:
|
||||
@@ -37,17 +36,12 @@ def run_migrations_offline() -> None:
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
<<<<<<< before updating
|
||||
def _do_run_migrations(connection) -> None: # noqa: ANN001
|
||||
context.configure(connection=connection, target_metadata=target_metadata)
|
||||
=======
|
||||
def do_run_migrations(connection: Connection) -> None:
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
include_name=include_name,
|
||||
)
|
||||
>>>>>>> after updating
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ unfixable = ["F401"]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"scripts/*" = ["INP"]
|
||||
"scripts/check_imports.py" = ["T201"]
|
||||
"t/*" = ["ALL"]
|
||||
"*.lock" = ["ALL"]
|
||||
"migrations/*" = ["INP", "D", "E501"]
|
||||
|
||||
@@ -4,8 +4,9 @@ import sys
|
||||
|
||||
HEAVY = ["aiogram", "pyrogram", "fastapi", "google.genai"]
|
||||
SERVICES = {
|
||||
"api": (["api.app"], {"fastapi"}),
|
||||
"api": (["api.app"], {"fastapi", "pyrogram"}),
|
||||
"userbot": (["userbot.runner", "userbot.handlers"], {"pyrogram"}),
|
||||
"watcher": (["watcher.runner"], {"pyrogram"}),
|
||||
}
|
||||
PROBE = """
|
||||
import importlib, sys
|
||||
@@ -13,7 +14,7 @@ for module in sys.argv[1].split(","):
|
||||
importlib.import_module(module)
|
||||
print(" ".join(name for name in sys.argv[2:] if name in sys.modules))
|
||||
"""
|
||||
ENV = dict(os.environ)
|
||||
ENV = {**os.environ, "AUTH__TOKEN": "check"}
|
||||
|
||||
|
||||
def loaded(modules: list[str]) -> tuple[set[str], str]:
|
||||
|
||||
@@ -5,7 +5,6 @@ from userbot.modules.client import PyroClient
|
||||
from utils.env import env
|
||||
from utils.logging import logger, setup_logging
|
||||
|
||||
<<<<<<< before updating
|
||||
setup_logging()
|
||||
|
||||
|
||||
@@ -19,9 +18,6 @@ def _finalize_session(sessions_dir: Path, name: str) -> Path:
|
||||
target = sessions_dir / f"{name}.session"
|
||||
(sessions_dir / "generated.session").rename(target)
|
||||
return target
|
||||
=======
|
||||
from userbot.modules.client import PyroClient
|
||||
>>>>>>> after updating
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
|
||||
@@ -9,7 +9,7 @@ from pyrogram.errors import AuthTokenExpired, SessionPasswordNeeded
|
||||
from pyrogram.qrlogin import QRLogin
|
||||
from pyrogram.types import User
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.client import PyroClient
|
||||
from utils.env import env
|
||||
|
||||
LOGIN_TTL_SECONDS = 900
|
||||
|
||||
@@ -9,7 +9,7 @@ from pyrogram.errors import FloodWait, RPCError
|
||||
from pyrogram.types import User
|
||||
|
||||
from api.login import LoginError, login_manager
|
||||
from userbot import DEVICE_MODEL_LIMIT
|
||||
from userbot.modules.client import DEVICE_MODEL_LIMIT
|
||||
from utils.read import accounts
|
||||
from utils.read.models import AccountView
|
||||
|
||||
|
||||
@@ -3,8 +3,4 @@ from dishka import make_async_container
|
||||
from dependencies.providers.postgres import DbProvider
|
||||
from dependencies.providers.storage import StorageProvider
|
||||
|
||||
<<<<<<< before updating
|
||||
container = make_async_container(DbProvider(), StorageProvider())
|
||||
=======
|
||||
container = make_async_container(DbProvider(), RepositoryProvider())
|
||||
>>>>>>> after updating
|
||||
@@ -1,6 +0,0 @@
|
||||
<<<<<<< before updating
|
||||
from userbot.modules.client import DEVICE_MODEL, DEVICE_MODEL_LIMIT, PyroClient
|
||||
|
||||
__all__ = ["DEVICE_MODEL", "DEVICE_MODEL_LIMIT", "PyroClient"]
|
||||
=======
|
||||
>>>>>>> after updating
|
||||
@@ -1,8 +1,8 @@
|
||||
from pyrogram.types import Message
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.capture import repository
|
||||
from userbot.modules.capture.repository import CHANNEL_ID_THRESHOLD
|
||||
from userbot.modules.client import PyroClient
|
||||
from utils.events import notify_bg_event
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from pyrogram.types import Message
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.capture import repository
|
||||
from userbot.modules.capture.chat_meta import meta_from_chat
|
||||
from userbot.modules.capture.message import sender_id
|
||||
from userbot.modules.client import PyroClient
|
||||
from userbot.modules.media import capture_media, media_unique_id, self_destruct_ttl
|
||||
from userbot.modules.raw_json import raw_json
|
||||
from utils.events import notify_bg_event
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from pyrogram.types import Message
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.capture import capture_message
|
||||
from userbot.modules.capture.chat_meta import meta_from_chat
|
||||
from userbot.modules.client import PyroClient
|
||||
from userbot.modules.stt import is_transcribable
|
||||
from userbot.modules.stt.gate import safe_transcribe
|
||||
from utils.events import notify_bg_event
|
||||
|
||||
@@ -2,7 +2,7 @@ from datetime import UTC, datetime
|
||||
|
||||
from pyrogram.types import User
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.client import PyroClient
|
||||
from userbot.modules.presence import repository
|
||||
from userbot.modules.raw_json import raw_json
|
||||
from utils.events import notify_bg_event
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from pyrogram import raw
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.client import PyroClient
|
||||
|
||||
HANDLES = (raw.types.UpdateContactsReset, raw.types.UpdateUser)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from pyrogram import raw
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.client import PyroClient
|
||||
|
||||
HANDLES = (
|
||||
raw.types.UpdateDialogFilter,
|
||||
|
||||
@@ -2,7 +2,6 @@ from collections.abc import Awaitable, Callable
|
||||
|
||||
from pyrogram import raw
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.handlers.raw import (
|
||||
contacts,
|
||||
dialog_filters,
|
||||
@@ -12,6 +11,7 @@ from userbot.handlers.raw import (
|
||||
read_receipts,
|
||||
transcribed,
|
||||
)
|
||||
from userbot.modules.client import PyroClient
|
||||
|
||||
RawHandler = Callable[..., Awaitable[None]]
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ from dataclasses import replace
|
||||
|
||||
from pyrogram import raw
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.avatars import capture_avatar
|
||||
from userbot.modules.client import PyroClient
|
||||
from userbot.modules.profiles import active_username, snapshot_from_user
|
||||
from userbot.modules.profiles.parse import ProfileFields
|
||||
from userbot.modules.profiles.repository import get_peer, write_profile
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from pyrogram import raw, utils
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.capture import repository
|
||||
from userbot.modules.capture.chat_meta import meta_from_peer
|
||||
from userbot.modules.client import PyroClient
|
||||
from utils.events import notify_bg_event
|
||||
|
||||
HANDLES = (raw.types.UpdateMessageReactions,)
|
||||
|
||||
@@ -2,8 +2,8 @@ from datetime import UTC, datetime
|
||||
|
||||
from pyrogram import raw, utils
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.capture.chat_meta import meta_from_chat_id
|
||||
from userbot.modules.client import PyroClient
|
||||
from userbot.modules.raw_json import raw_json
|
||||
from userbot.modules.read_receipts import repository as receipts
|
||||
from userbot.modules.stt import repository
|
||||
|
||||
@@ -2,7 +2,7 @@ from datetime import UTC, datetime
|
||||
|
||||
from pyrogram import raw, utils
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.client import PyroClient
|
||||
from userbot.modules.raw_json import raw_json
|
||||
from userbot.modules.read_receipts import repository
|
||||
from utils.events import notify_bg_event
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from pyrogram import raw
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.capture.chat_meta import meta_from_peer
|
||||
from userbot.modules.client import PyroClient
|
||||
from userbot.modules.stt import repository
|
||||
|
||||
HANDLES = (raw.types.UpdateTranscribedAudio,)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from pyrogram.types import Story
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.client import PyroClient
|
||||
from userbot.modules.stories.service import save_story
|
||||
|
||||
|
||||
|
||||
@@ -41,9 +41,7 @@ class ChatMetaCache:
|
||||
chat_id: int,
|
||||
meta: tuple[str | None, str | None],
|
||||
) -> bool:
|
||||
from userbot.modules.groups.repository import ( # noqa: PLC0415
|
||||
get_latest_chat_meta,
|
||||
)
|
||||
from userbot.modules.groups.repository import get_latest_chat_meta # noqa: PLC0415
|
||||
|
||||
if chat_id not in self._cache:
|
||||
self._cache[chat_id] = await get_latest_chat_meta(pool, account_id, chat_id)
|
||||
|
||||
@@ -10,8 +10,8 @@ import asyncpg
|
||||
import uvloop
|
||||
|
||||
from dependencies.container import container
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.capture import CaptureContext, build_capture_context
|
||||
from userbot.modules.client import PyroClient
|
||||
from userbot.modules.jobs import JobConsumer
|
||||
from utils.env import env
|
||||
from utils.jobs import enqueue
|
||||
|
||||
@@ -4,27 +4,7 @@ from pydantic import Field, SecretStr
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
<<<<<<< before updating
|
||||
class DatabaseSettings(BaseSettings):
|
||||
=======
|
||||
def is_prod() -> bool:
|
||||
return os.getenv("RUN_ENVIRONMENT") == "prod"
|
||||
|
||||
|
||||
class Section(BaseSettings):
|
||||
model_config = SettingsConfigDict(extra="ignore")
|
||||
|
||||
|
||||
class LogSettings(Section):
|
||||
level: str = "INFO"
|
||||
level_external: str = "WARNING"
|
||||
show_time: bool = False
|
||||
show_locals: bool = False
|
||||
console_width: int = 150
|
||||
|
||||
|
||||
class DatabaseSettings(Section):
|
||||
>>>>>>> after updating
|
||||
host: str = "postgres"
|
||||
port: int = 5432
|
||||
user: str = "beavergram"
|
||||
@@ -76,6 +56,7 @@ class LogSettings(BaseSettings):
|
||||
level: str = "INFO"
|
||||
level_external: str = "WARNING"
|
||||
show_time: bool = False
|
||||
show_locals: bool = False
|
||||
console_width: int = 150
|
||||
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ from dataclasses import dataclass
|
||||
from pyrogram import raw, types
|
||||
from pyrogram.errors import RPCError
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.capture import capture_message
|
||||
from userbot.modules.capture.chat_meta import meta_from_chat
|
||||
from userbot.modules.capture.context import CaptureContext
|
||||
from userbot.modules.client import PyroClient
|
||||
from utils.logging import logger
|
||||
|
||||
EARLIEST_PTS = 1
|
||||
|
||||
@@ -11,9 +11,9 @@ from pyrogram.handlers import (
|
||||
MessageHandler,
|
||||
)
|
||||
|
||||
from userbot import PyroClient
|
||||
from userbot.modules.capture import capture_message
|
||||
from userbot.modules.capture.chat_meta import meta_from_chat
|
||||
from userbot.modules.client import PyroClient
|
||||
from userbot.modules.jobs import JobConsumer
|
||||
from utils.events import notify_bg_event
|
||||
from utils.jobs import enqueue
|
||||
|
||||
Reference in new issue
Block a user