Full Code of MasterGroosha/my-id-bot for AI

master 882d7b9b15ab cached
29 files
37.7 KB
10.5k tokens
32 symbols
1 requests
Download .txt
Repository: MasterGroosha/my-id-bot
Branch: master
Commit: 882d7b9b15ab
Files: 29
Total size: 37.7 KB

Directory structure:
gitextract_0g8cu8tb/

├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── bot/
│   ├── __init__.py
│   ├── __main__.py
│   ├── config_reader.py
│   ├── fluent_helper.py
│   ├── handlers/
│   │   ├── __init__.py
│   │   ├── add_or_migrate.py
│   │   ├── commands.py
│   │   ├── errors.py
│   │   ├── inline_mode.py
│   │   └── pm.py
│   ├── locales/
│   │   ├── en/
│   │   │   └── strings.ftl
│   │   ├── es/
│   │   │   └── strings.ftl
│   │   ├── ro/
│   │   │   └── strings.ftl
│   │   ├── ru/
│   │   │   └── strings.ftl
│   │   └── uk/
│   │       └── strings.ftl
│   ├── logs.py
│   ├── middlewares/
│   │   ├── __init__.py
│   │   ├── l10n.py
│   │   └── log_unhandled.py
│   ├── migration_cache.py
│   └── ui_commands.py
├── docker-compose.example.yml
├── env_example
├── my-id-bot.example.service
└── requirements.txt

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.idea/
venv/
__pycache__/
*.log
/.env
my-id-bot.service
docker-compose.yml


================================================
FILE: Dockerfile
================================================
# Separate "build" image
FROM python:3.11-slim as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY bot /app/bot

# Final stage
FROM gcr.io/distroless/python3-debian12:nonroot
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH=/usr/local/lib/python3.11/site-packages
CMD ["-m", "bot"]


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019-present Aleksandr K. (also known as MasterGroosha on GitHub)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Bot to get users/chats IDs in Telegram

This is a simple bot written with [aiogram 3.x](https://github.com/aiogram/aiogram) framework to show some IDs, like:

* Your user ID (when asked in inline mode or in private chat with any message);  
* Group/supergroup ID (when added to that group or with /id command);  
* Channel ID (when message forwarded from channel to one-to-one chat with bot);  
* Supergroup ID (when message forwarded from anonymous group admin);  
* Topic ID for [forum supergroups](https://telegram.org/blog/topics-in-groups-collectible-usernames#topics-in-groups);  
* Sticker ID (they can be re-used with any bot);
* Group to supergroup migrate information (both old and new ID).

## Requirements:
* Python 3.11 and newer;  
* Linux (should work on Windows, but not tested);   
* Systemd init system (optional).  
* Docker (optional).

## Installation:

### Just to test (not recommended)
1. Clone this repo;
2. `cd` to cloned directory and initialize Python virtual environment (venv);
3. Activate the venv and install all dependencies from `requirements.txt` file;
4. Copy `env_example` to `.env` (with the leading dot), open `.env` and edit the variables;
5. In the activated venv: `python -m bot`

### Systemd 
1. Perform steps 1-4 from "just to test" option above;
2. Copy `my-id-bot.example.service` to `my-id-bot.service` (or whatever your prefer), open it and edit `WorkingDirectory` 
and `ExecStart` directives;
3. Copy (or symlink) that service file to `/etc/systemd/system/` directory;
4. Enable your service `sudo systemctl enable my-id-bot --now`;
5. Check that service is running: `systemctcl status my-id-bot` (can be used without root privileges).

### Docker + Docker Compose
1. Get `docker-compose.example.yml` file and rename it as `docker-compose.yml`;
2. Get `env_example` file, rename it as `.env` (with the leading dot), open it and edit the variables;
3. Run the bot: `docker compose up -d`;
4. Check that container is up and running: `docker compose ps`


================================================
FILE: bot/__init__.py
================================================


================================================
FILE: bot/__main__.py
================================================
import asyncio
from pathlib import Path

import structlog
from aiogram import Bot, Dispatcher
from aiogram.client.default import DefaultBotProperties
from aiogram.enums import ParseMode
from structlog.typing import FilteringBoundLogger

from bot.config_reader import bot_config, log_config
from bot.fluent_helper import FluentDispenser
from bot.handlers import commands, pm, add_or_migrate, inline_mode, errors
from bot.logs import get_structlog_config
from bot.middlewares import L10nMiddleware, UnhandledUpdatesLoggerMiddleware
from bot.ui_commands import set_bot_commands

logger: FilteringBoundLogger = structlog.get_logger()


async def main():
    structlog.configure(**get_structlog_config(log_config))

    bot = Bot(
        token=bot_config.bot_token.get_secret_value(),
        default=DefaultBotProperties(
            parse_mode=ParseMode.HTML,
        )
    )

    # Setup dispatcher
    dp = Dispatcher()

    dispenser = FluentDispenser(
        locales_dir=Path(__file__).parent.joinpath("locales"),
        default_language="en"
    )
    dp.update.middleware(L10nMiddleware(dispenser))

    if log_config.log_unhandled:
        dp.update.outer_middleware(UnhandledUpdatesLoggerMiddleware())

    # Register handlers
    dp.include_routers(
        commands.router,
        pm.router,
        add_or_migrate.router,
        inline_mode.router,
        errors.router
    )

    # Set bot commands in UI
    await set_bot_commands(bot, dispenser)

    # Run bot
    await logger.awarning(
        "Important! This version is the last one to use environment variables for configuration. "
        "The next version is going to use TOML file. Be careful when upgrading bot version in the future."
    )
    await logger.ainfo("Starting bot")
    await dp.start_polling(bot, allowed_updates=dp.resolve_used_update_types())
    await logger.ainfo("Bot stopped")


if __name__ == "__main__":
    asyncio.run(main())


================================================
FILE: bot/config_reader.py
================================================
from enum import Enum

from pydantic import SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict


class LoggingRenderer(str, Enum):
    JSON = "json"
    CONSOLE = "console"


class LoggingSettings(BaseSettings):
    level: str = "INFO"
    format: str = "%Y-%m-%d %H:%M:%S"
    is_utc: bool = False
    renderer: LoggingRenderer = LoggingRenderer.JSON
    log_unhandled: bool = False

    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        env_prefix="LOGGING_",
        extra="allow",
    )


class BotSettings(BaseSettings):
    bot_token: SecretStr

    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        extra="allow",
    )


bot_config = BotSettings()
log_config = LoggingSettings()


================================================
FILE: bot/fluent_helper.py
================================================
from pathlib import Path

from fluent.runtime import FluentLocalization, FluentResourceLoader


class FluentDispenser:
    def __init__(self, locales_dir: Path, default_language: str = "en"):
        self.__loader = FluentResourceLoader(str(locales_dir) + "/{locale}")
        self.__default_language = default_language
        self.languages = dict()

        dirs_names = set()
        default_language_dir = None
        for item in locales_dir.iterdir():
            dirs_names.add(item.name)
            if item.name == self.__default_language:
                default_language_dir = item

        if not default_language_dir:
            raise ValueError("FluentDispenser: default language directory not found")

        ftl_files_list = [item.name for item in default_language_dir.iterdir() if item.suffix == ".ftl"]

        for name in dirs_names:
            if name == default_language:
                self.languages[name] = FluentLocalization([self.__default_language], ftl_files_list, self.__loader)
            else:
                self.languages[name] = FluentLocalization([name, self.__default_language], ftl_files_list, self.__loader)

    @property
    def default_locale(self) -> FluentLocalization:
        return self.languages[self.__default_language]

    @property
    def available_languages(self) -> list[str]:
        return list(self.languages.keys())

    def get_language(self, language_code: str):
        return self.languages.get(language_code, self.default_locale)


================================================
FILE: bot/handlers/__init__.py
================================================


================================================
FILE: bot/handlers/add_or_migrate.py
================================================
from asyncio import sleep

from aiogram import Bot, html, Router, F
from aiogram.enums import ChatType
from aiogram.filters.chat_member_updated import \
    ChatMemberUpdatedFilter, JOIN_TRANSITION
from aiogram.types import ChatMemberUpdated, Message
from fluent.runtime import FluentLocalization

from bot.migration_cache import cache

router = Router()


@router.my_chat_member(
    ChatMemberUpdatedFilter(
        member_status_changed=JOIN_TRANSITION
    ),
    F.chat.type.in_({ChatType.GROUP, ChatType.SUPERGROUP})
)
async def bot_added_to_group(event: ChatMemberUpdated, bot: Bot, l10n: FluentLocalization):
    """
    Bot was added to group.

    :param event: an event from Telegram of type "my_chat_member"
    :param bot: bot who message was addressed to
    :param l10n: Fluent localization object
    :return:
    """
    await sleep(1.0)
    if event.chat.id not in cache.keys():
        await bot.send_message(
            chat_id=event.chat.id,
            text=l10n.format_value(
                "any-chat",
                args={"type": event.chat.type, "id": html.code(event.chat.id)}
            )
        )


@router.message(F.migrate_to_chat_id)
async def group_to_supergroup_migration(message: Message, bot: Bot, l10n: FluentLocalization):
    await bot.send_message(
        message.migrate_to_chat_id,
        l10n.format_value(
            "group-to-supergroup",
            args={"old_id": html.code(message.chat.id), "new_id": html.code(message.migrate_to_chat_id)}
        )
    )

    cache[message.migrate_to_chat_id] = True


================================================
FILE: bot/handlers/commands.py
================================================
from aiogram import Router, html, F
from aiogram.enums import ChatType
from aiogram.filters import Command, CommandStart
from aiogram.types import Message
from aiogram.utils.keyboard import InlineKeyboardBuilder
from fluent.runtime import FluentLocalization

router = Router()
router.message.filter(~F.forward_from & ~F.forward_from_chat)


@router.message(F.chat.type == ChatType.PRIVATE, CommandStart())
async def cmd_start(message: Message, l10n: FluentLocalization):
    """
    /start command handler for private chats
    :param message: Telegram message with "/start" command
    :param l10n: Fluent localization object
    """
    builder = InlineKeyboardBuilder()
    builder.button(text=l10n.format_value("cmd-start-inline-try-here"), switch_inline_query_current_chat="")
    builder.button(text=l10n.format_value("cmd-start-inline-try-other"), switch_inline_query="")
    await message.answer(
        l10n.format_value("cmd-start", args={"id": html.code(message.chat.id)}),
        reply_markup=builder.adjust(1).as_markup()
    )


@router.message(F.chat.type == ChatType.PRIVATE, Command("id"))
async def cmd_id_pm(message: Message, l10n: FluentLocalization):
    """
    /id command handler for private messages
    :param message: Telegram message with "/id" command
    :param l10n: Fluent localization object
    """
    await message.answer(
        l10n.format_value("cmd-id-pm", args={"id": html.code(message.from_user.id)})
    )


@router.message(
    F.chat.type.in_({ChatType.GROUP, ChatType.SUPERGROUP}),
    Command("id")
)
@router.message(
    F.chat.type.in_({ChatType.GROUP, ChatType.SUPERGROUP}),
    CommandStart(deep_link=True, magic=F.args == "id")
)
async def cmd_id_groups(message: Message, l10n: FluentLocalization):
    """
    /id command handler for (super)groups
    :param message: Telegram message with "/id" command
    :param l10n: Fluent localization object
    """
    chat_type_str = l10n.format_value(message.chat.type)
    msg = [l10n.format_value("any-chat", args={"type": chat_type_str, "id": html.code(message.chat.id)})]

    if message.is_topic_message:
        msg.append(
            l10n.format_value(
                "cmd-id-group-topic-id",
                args={"type": message.chat.type, "id": html.code(message.message_thread_id)}
            )
        )

    if message.sender_chat is None:
        msg.append(l10n.format_value("cmd-id-pm", args={"id": html.code(message.from_user.id)}))
    else:
        msg.append(l10n.format_value("cmd-id-group-as-channel", args={"id": html.code(message.sender_chat.id)}))

    await message.reply("\n".join(msg))


@router.message(Command("help"))
async def cmd_help(message: Message, l10n: FluentLocalization):
    """
    /help command handler for all chats
    :param message: Telegram message with "/help" command
    :param l10n: Fluent localization object
    """
    await message.answer(l10n.format_value("cmd-help"), disable_web_page_preview=True)


================================================
FILE: bot/handlers/errors.py
================================================
import structlog
from aiogram import Router
from aiogram.exceptions import TelegramAPIError
from aiogram.types.error_event import ErrorEvent
from structlog.typing import FilteringBoundLogger

router = Router(name="errors-router")
logger: FilteringBoundLogger = structlog.get_logger()


@router.errors()
async def handle_errors(event: ErrorEvent):
    if isinstance(event.exception, TelegramAPIError):
        error_message = event.exception.message
        error_source = "BotAPI"
    else:
        error_message = str(event.exception)
        error_source = "Python"

    await logger.aerror(
        "Outgoing bot message error",
        exception_type=event.exception.__class__.__name__,
        message=error_message,
        update=event.update.dict(),
        error_source=error_source
    )


================================================
FILE: bot/handlers/inline_mode.py
================================================
from aiogram import html, Router
from aiogram.enums import ChatType
from aiogram.types import InlineQuery, InlineQueryResultArticle, InputTextMessageContent
from fluent.runtime import FluentLocalization

router = Router()


@router.inline_query()
async def inline_mode_handler(query: InlineQuery, l10n: FluentLocalization):
    result = InlineQueryResultArticle(
        id=".",
        title=l10n.format_value("inline-mode-title", args={"id": query.from_user.id}),
        description=l10n.format_value("inline-mode-description"),
        input_message_content=InputTextMessageContent(
            message_text=l10n.format_value("inline-mode-text", args={"id": html.code(query.from_user.id)})
        )
    )
    # Do not forget about is_personal parameter! Otherwise, all people will see the same ID
    switch_pm_text = l10n.format_value("inline-mode-tryme") if query.chat_type != ChatType.SENDER else None
    await query.answer(
        results=[result], cache_time=3600, is_personal=True,
        switch_pm_parameter="1", switch_pm_text=switch_pm_text
    )


================================================
FILE: bot/handlers/pm.py
================================================
from aiogram import Router, html, F
from aiogram.enums import ChatType
from aiogram.filters import MagicData
from aiogram.types import Message
from fluent.runtime import FluentLocalization

router = Router()
router.message.filter(F.chat.type == ChatType.PRIVATE)


@router.message(F.forward_from_chat.type.as_("chat_type"))
async def get_channel_or_supergroup_id(message: Message, chat_type: str, l10n: FluentLocalization):
    """
    Handler for message forwarded from channel
    or from anonymous admin writing on behalf
    of a supergroup
    :param message: Telegram message with "forward_from_chat" field not empty
    :param chat_type: parsed chat_type ("channel" or "supergroup")
    :param l10n: Fluent localization object
    """
    chat_type_str = l10n.format_value(chat_type)
    msg = l10n.format_value("any-chat", args={"type": chat_type_str, "id": html.code(message.forward_from_chat.id)})
    if message.sticker:
        msg += "\n" + l10n.format_value("sticker-id", args={"id": html.code(message.sticker.file_id)})
    await message.reply(msg)


@router.message(F.forward_from)
async def get_user_id_no_privacy(message: Message, l10n: FluentLocalization):
    """
    Handler for message forwarded from other user who doesn't hide their ID
    :param message: Telegram message with "forward_from" field not empty
    :param l10n: Fluent localization object
    """
    account_type = "bot" if message.forward_from.is_bot else "user"
    chat_type_str = l10n.format_value(account_type)
    msg = l10n.format_value("any-chat", args={"type": chat_type_str, "id": html.code(message.forward_from.id)})
    if message.sticker:
        msg += "\n" + l10n.format_value("sticker-id", args={"id": html.code(message.sticker.file_id)})
    await message.reply(msg)


@router.message(F.forward_sender_name)
async def get_user_id_with_privacy(message: Message, l10n: FluentLocalization):
    """
    Handler for message forwarded from other user who hides their ID
    :param message: Telegram message with "forward_sender_name" field not empty
    :param l10n: Fluent localization object
    """
    msg = l10n.format_value("user-id-hidden")
    if message.sticker:
        msg += "\n\n" + l10n.format_value("sticker-id", args={"id": html.code(message.sticker.file_id)})
    await message.reply(msg)


@router.message(F.sticker)
async def sticker_in_pm(message: Message, l10n: FluentLocalization):
    """
    /start command handler for private chats
    :param message: Telegram message with "/start" command
    :param l10n: Fluent localization object
    """
    #
    await message.reply(
        l10n.format_value("sticker-id-extended", args={"id": html.code(message.sticker.file_id)})
    )


@router.message(F.via_bot, MagicData(F.event.via_bot.id != F.bot.id))  # noqa
async def other_inline_bot_in_pm(message: Message, l10n: FluentLocalization):
    """
    Message via some other inline bot in PM
    :param message: Any Telegram message
    :param l10n: Fluent localization object
    """
    bot_str = l10n.format_value("bot")
    await message.answer(
        l10n.format_value("any-chat", args={"type": bot_str, "id": html.code(message.via_bot.id)})
    )


@router.message(~F.via_bot)
async def other_in_pm(message: Message, l10n: FluentLocalization):
    """
    Any other message in PM, not via inline bot
    :param message: Any Telegram message
    :param l10n: Fluent localization object
    """
    await message.answer(l10n.format_value("cmd-id-pm", args={"id": html.code(message.from_user.id)}))


================================================
FILE: bot/locales/en/strings.ftl
================================================
# without @ !
bot-username = my_id_bot

# do not translate!
bot-group-deeplink = https://t.me/{bot-username}?startgroup=id

source-code-link = https://github.com/MasterGroosha/my-id-bot

cmd-start =
    Your Telegram ID is { $id }
    Help and source code: /help

    You can also use this bot in inline mode to share its ID! Try using one of the buttons below.
    Please note, that bot uses your language in PM and English in any other chats.

cmd-start-inline-try-here = Try here
cmd-start-inline-try-other = Try in other chat

# Used in strings like "This channel id is xxx"
supergroup = supergroup
group = group
channel = channel
user = user
bot = bot

any-chat = This { $type } ID is { $id }

cmd-id-pm = Your Telegram ID is { $id }
cmd-id-group-topic-id = This forum topic ID is { $id }
cmd-id-group-as-channel = And you've sent this message as channel with ID { $id }

cmd-help =
    Use this bot to get ID for different entities across Telegram:

    • Forward message from channel to get channel ID;
    • Forward message from anonymous supergroup admin to get supergroup ID;
    • Forward message from user to get their ID (unless they restrict from doing so);
    • Forward message from some other bot or use it via inline mode to get bot ID;
    • Send a sticker to get its file_id (currently you can use the sticker's file_id with any bot);
    • <a href="{bot-group-deeplink}">Add bot to group</a> to get its ID (it will even tell you when you migrate from group to supergroup);
    • Use inline mode to send your Telegram ID to any chat.

    Source code: { source-code-link }

group-to-supergroup =
    Group upgraded to supergroup.
    Old ID: { $old_id }
    New ID: { $new_id }

inline-mode-title = Your ID is { NUMBER($id, useGrouping: 0) }
inline-mode-description = Tap to send your ID to current chat
inline-mode-text = My Telegram ID is { $id }
inline-mode-tryme = Or try me in PM >>>

sticker-id = Also this sticker's ID is { $id }
sticker-id-extended =
    This sticker ID is
    { $id }
    Sticker is currently the only media type which file_ids can be used by any bot.

user-id-hidden =
    This user decided to <b>hide</b> their ID.
    Learn more about this feature <a href="https://telegram.org/blog/unsend-privacy-emoji#anonymous-forwarding">here</a>.

# Commands in UI (when you press "/" or "Menu" button)
cmd-hint-id-pm = Print your Telegram ID
cmd-hint-id-group = Print Telegram ID of this group chat
cmd-hint-help = Help and source code


================================================
FILE: bot/locales/es/strings.ftl
================================================
# without @ !
bot-username = my_id_bot

# do not translate!
bot-group-deeplink = https://t.me/{bot-username}?startgroup=id

source-code-link = https://github.com/MasterGroosha/my-id-bot

cmd-start =
    Tu ID de Telegram es { $id }.
    Ayuda y código fuente: /help

    También puedes usar este bot en modo inline para compartir la ID! Prueba usando lso botones de abajo.
    Por favor, ten en cuenta que el bot usa tu idioma en privado y en inglés en cualquier otro chat.

cmd-start-inline-try-here = Intenta aquí
cmd-start-inline-try-other = Intenta en otro chat

# Used in strings like "This channel id is xxx"
supergroup = supergrupo
group = grupo
channel = canal
user = usuario
bot = bot

any-chat = Esta { $type } ID es { $id }

cmd-id-pm = Tu ID de Telegram es { $id }.
cmd-id-group-topic-id = La ID de este grupo es { $id }.
cmd-id-group-as-channel = Y has envido este mensaje de  un canal con ID { $id }.

cmd-help =
    Usa este bot para obtener la ID de diferentes entidades en Telegram:

    • Reenvía un mensaje de un canal para obtener la ID del canal;
    • Reenvía un mensaje de un admin de supergrupo anónimo para obtener la ID del supergrupo;
    • Reenvía un mensaje de un usuario para obtener su ID (Solo si no tiene activada la protección);
    • Reenvía un mensaje de otro bot o usa el modo inline para obtener la ID de este;
    • Envía un sticker para obtener su file_id (Puedes usar el file_id de sticker con cualquier bot);
    • <a href="{bot-group-deeplink}">Añade el bot a un grupo</a> para obtener su ID (incluso te dirá cuando pases de grupo a supergrupo);
    • Usa el modo inline para enviar tu ID de Telegram a otros chats.

    Source code: { source-code-link }

group-to-supergroup =
    El grupo se ha convertido en supergroup.
    Antigua ID: { $old_id }
    Nueva ID: { $new_id }

inline-mode-title = Tu ID es { NUMBER($id, useGrouping: 0) }
inline-mode-description = has click para mandar tu ID al chat actual.
inline-mode-text = Mi ID de Telegram es { $id }.
inline-mode-tryme = O prueba el bot en PM >>>

sticker-id = Ademas, la ID del sticker es { $id }.
sticker-id-extended =
    La ID del sticker es
    { $id }
    El sticker es actualmente el único tipo de datos cuya file_ids pueden usar los bots.

user-id-hidden =
    Este usuario ha decidido <b>ocultar</b> su ID.
    Puedes leer más sobre este cambio <a href="https://telegram.org/blog/unsend-privacy-emoji#anonymous-forwarding">aquí</a>.

# Commands in UI (when you press "/" or "Menu" button)
cmd-hint-id-pm = Muestra tu ID de Telegram.
cmd-hint-id-group = Muestra la ID de Telegram de este chat de grupo.
cmd-hint-help = Ayuda y código fuente.


================================================
FILE: bot/locales/ro/strings.ftl
================================================
# without @ !
bot-username = my_id_bot

# do not translate!
bot-group-deeplink = https://t.me/{bot-username}?startgroup=id

source-code-link = https://github.com/MasterGroosha/my-id-bot

cmd-start =
    ID-ul tău de Telegram este { $id }
    Ajutor si cod sursă: /help

    Poți folosi și acest bot în modul inline pentru a partaja ID-ul său! Încearcă să folosești unul dintre butoanele de mai jos.
    Te rugăm să reții că botul folosește limba ta în mesajele private și engleza în orice alte conversații.

cmd-start-inline-try-here = Încearcă aici
cmd-start-inline-try-other = Încearcă într-un alt chat

# Used in strings like "This channel id is xxx"
supegroup = supegrup
group = grup
channel = canal
user = utilizator
bot = bot

any-chat = Acest { $type } de ID este { $id }

cmd-id-pm = ID-ul tău de Telegram este { $id }
cmd-id-group-topic-id = ID-ul acestui subiect de forum este { $id }
cmd-id-groupd-as-channel = Și ai trimis acest mesaj ca canal cu ID-ul { $id }

cmd-help =
    Folosește acest bot pentru a obține ID-ul pentru diferite entități din Telegram:

    • Trimite mai departe mesajul din canal pentru a obține ID-ul canalului;
    • Trimite mai departe mesajul de la un administrator anonim al supergrupului pentru a obține ID-ul supergrupului;
    • Trimite mai departe mesajul de la utilizator pentru a obține ID-ul lor (cu excepția cazului în care aceștia restricționează acest lucru);
    • Trimite mai departe mesajul de la un alt bot sau folosește-l prin modul inline pentru a obține ID-ul botului;
    • Trimite un sticker pentru a obține file_id-ul său (în prezent, poți folosi file_id-ul sticker-ului cu orice bot);
    • <a href="{bot-group-deeplink}">Adaugă botul în grup</a> pentru a obține ID-ul său (îți va spune chiar și când migrezi de la grup la supergrup);
    • Folosește modul inline pentru a trimite ID-ul tău de Telegram în orice conversație.

    Cod sursă: { source-code-link }

group-to-supegroup =
    Grupul a fost actualizat la supergrup.
    ID-ul vechi: { $old_id }
    ID-ul nou: { $new_id }

inline-mode-title = ID-ul tău este { NUMBER($id, useGrouping: 0) }
inline-mode-description = Apasă pentru a trimite ID-ul tău în conversația curentă
inline-mode-text = ID-ul meu de Telegram este { $id }
inline-mode-tryme = Sau acceseaza-mă în mesaj privat >>>

sticker-id = De asemenea, ID-ul acestui sticker este { $id }
sticker-id-extended =
    ID-ul acestui sticker este
    { $id }
    Stickerul este în prezent singurul tip de media al cărui file_id poate fi folosit de orice bot.

user-id-hidden =
    Acest utilizator a decis să își <b>ascundă</b> ID-ul.
    Află mai multe despre această funcție <a href="https://telegram.org/blog/unsend-privacy-emoji#anonymous-forwarding">aici</a>.

# Commands in UI (when you press "/" or "Menu" button)
cmd-hint-id-pm = Printează ID-ul tău de Telegram
cmd-hint-id-group = Printează Telegram ID-ul al acestui chat de grup
cmd-hint-help = Suport și cod sursă


================================================
FILE: bot/locales/ru/strings.ftl
================================================
# without @ !
bot-username = my_id_bot

# do not translate!
bot-group-deeplink = https://t.me/{bot-username}?startgroup=id

source-code-link = https://github.com/MasterGroosha/my-id-bot

cmd-start =
    Ваш Telegram ID: { $id }
    Помощь и исходники: /help

    Вы также можете использовать этого бота в инлайн-режиме! Попробуйте одну из кнопок ниже.
    Учтите, что бот отвечает на вашем языке в личных сообщениях и на английском во всех других чатах.

cmd-start-inline-try-here = Попробовать здесь
cmd-start-inline-try-other = Попробовать в другом чате

# Used in strings like "This channel id is xxx"
supergroup = супергруппа
group = группа
channel = канал
user = пользователь
bot = бот

any-chat = Это { $type } с ID { $id }

cmd-id-pm = Ваш Telegram ID: { $id }
cmd-id-group-topic-id = Это топик с ID { $id }
cmd-id-group-as-channel = И вы отправили это сообщение от имени канала с ID { $id }

cmd-help =
    Этот бот предназначен для получения ID разных сущностей в Telegram:

    • Перешлите сообщение из канала, чтобы узнать его ID;
    • Перешлите сообщение от анонимного администратора супергруппы, чтобы узнать ID этой супергруппы;
    • Перешлите сообщение от юзера, чтобы узнать его/её ID (если они не запретили это);
    • Перешлите сообщение от другого бота или используйте его в инлайн-режиме, чтобы узнать ID бота;
    • Отправьте стикер, чтобы узнать его file_id (их можно использовать с любыми ботами);
    • <a href="{bot-group-deeplink}">Добавьте бота в группу</a>, чтобы узнать её ID (бот также сообщит о миграции группы в супергруппу);
    • Попробуйте бота в инлайн-режиме, чтобы отправить свой Telegram ID в любой чат.

    Исходники бота: { source-code-link }

group-to-supergroup =
    Группа обновлена до супергруппы.
    Старый ID: { $old_id }
    Новый ID: { $new_id }

inline-mode-title = Ваш ID { NUMBER($id, useGrouping: 0) }
inline-mode-description = Нажмите, чтобы отправить его в текущий чат
inline-mode-text = Мой Telegram ID { $id }
inline-mode-tryme = Попробуйте меня в ЛС >>>

sticker-id = ID этого стикера: { $id }
sticker-id-extended =
    ID этого стикера
    { $id }
    В настоящий момент только айди стикеров можно использовать любыми ботами.

user-id-hidden =
    Этот пользователь <b>скрыл</b> свой айди при пересылке.
    <a href="https://telegram.org/blog/unsend-privacy-emoji#anonymous-forwarding">Подробнее об этой фиче</a>.

# Commands in UI (when you press "/" or "Menu" button)
cmd-hint-id-pm = Узнать свой ID
cmd-hint-id-group = Узнать ID этой группы
cmd-hint-help = Справка и исходники


================================================
FILE: bot/locales/uk/strings.ftl
================================================
# without @ !
bot-username = my_id_bot

# do not translate!
bot-group-deeplink = https://t.me/{bot-username}?startgroup=id

source-code-link = https://github.com/MasterGroosha/my-id-bot

cmd-start =
    Ваш Telegram ID: { $id }
    Допомога та вихідний код: /help

    Ви також можете використовувати цього бота в інлайн-режимі! Спробуйте одну з кнопок нижче.
    Врахуйте, що бот відповідає на вашій мові лише в особистих повідомленнях і на англійській у всіх інших чатах.

cmd-start-inline-try-here = Спробувати тут
cmd-start-inline-try-other = Спробувати в іншому чаті

# Used in strings like "This channel id is xxx"
supergroup = супергрупа
group = група
channel = канал
user = користувач
bot = бот

any-chat = Це { $type } з ID { $id }

cmd-id-pm = Ваш Telegram ID: { $id }
cmd-id-group-topic-id = Це топік з ID { $id }
cmd-id-group-as-channel = І ви відправили це повідомлення від імені каналу з ID { $id }

cmd-help =
    Цей бот призначений для отримання ID різних об'єктів у Telegram:

    • Перешліть повідомлення з каналу, щоб дізнатися його ID;
    • Перешліть повідомлення від анонімного адміністратора супергрупи, щоб дізнатися ID цієї супергрупи;
    • Перешліть повідомлення від юзера, щоб дізнатися його/її ID (якщо вони не заборонили це);
    • Перешліть повідомлення від іншого бота або використайте його в інлайн-режимі, щоб дізнатись ID бота;
    • Відправте стікер, щоб дізнатись його file_id (надалі, цей file_id можна використовувати у будь-яких ботах);
    • <a href="{bot-group-deeplink}">Додайте бота до групи</a>, щоб дізнатись її ID (бот також повідомить про міграцію групи у супергрупу);
    • Спробуйте бота в інлайн-режимі, щоб надіслати свій Telegram ID у будь-який чат.

    Вихідний код бота: { source-code-link }

group-to-supergroup =
    Група перетворена у супергрупу.
    Старий ID: { $old_id }
    Новий ID: { $new_id }

inline-mode-title = Ваш ID { NUMBER($id, useGrouping: 0) }
inline-mode-description = Натисніть, щоб надіслати його у поточний чат
inline-mode-text = Мій Telegram ID { $id }
inline-mode-tryme = Спробуйте мене в особистих повідомленнях >>>

sticker-id = ID этого стикера: { $id }
sticker-id-extended =
    ID цього стікера
    { $id }
    У даний момент тільки айді стікерів можна використовувати у будь-яких ботах.

user-id-hidden =
    Цей користувач <b>приховав</b> свій айді при пересиланні.
    <a href="https://telegram.org/blog/unsend-privacy-emoji#anonymous-forwarding">Детальніше про цю фічу</a>.

# Commands in UI (when you press "/" or "Menu" button)
cmd-hint-id-pm = Дізнатись свій ID
cmd-hint-id-group = Дізнатись ID цієї групи
cmd-hint-help = Допомога ти вихідний код


================================================
FILE: bot/logs.py
================================================
import logging
from json import dumps

import structlog
from structlog import WriteLoggerFactory

from bot.config_reader import LoggingSettings, LoggingRenderer


def get_structlog_config(config: LoggingSettings) -> dict:
    return {
        "processors": get_processors(config),
        "cache_logger_on_first_use": True,
        "wrapper_class": structlog.make_filtering_bound_logger(logging.getLevelName(config.level)),
        "logger_factory": WriteLoggerFactory()
    }


def get_processors(config: LoggingSettings) -> list:
    def custom_json_serializer(data, *args, **kwargs):
        result = dict()
        # Set keys in specific order
        for key in ("timestamp", "level", "event"):
            if key in data:
                result[key] = data.pop(key)

        # Add all other fields
        result.update(**data)
        return dumps(result, default=str)

    processors = [
        structlog.processors.TimeStamper(fmt=config.format, utc=config.is_utc),
        structlog.processors.add_log_level
    ]

    if config.renderer == LoggingRenderer.JSON:
        processors.append(structlog.processors.JSONRenderer(serializer=custom_json_serializer))
    else:
        processors.append(structlog.dev.ConsoleRenderer())
    return processors


================================================
FILE: bot/middlewares/__init__.py
================================================
from .l10n import L10nMiddleware
from .log_unhandled import UnhandledUpdatesLoggerMiddleware

__all__ = [
    "L10nMiddleware",
    "UnhandledUpdatesLoggerMiddleware"
]


================================================
FILE: bot/middlewares/l10n.py
================================================
from typing import Any, Awaitable, Callable, Dict, Final

from aiogram import BaseMiddleware
from aiogram.enums import ChatType
from aiogram.types import TelegramObject, User, Update

from bot.fluent_helper import FluentDispenser


def is_pm(event: Update) -> bool:
    return \
            (event.message and event.message.chat.type == ChatType.PRIVATE) or \
            (event.inline_query and event.inline_query.chat_type == ChatType.SENDER)


class L10nMiddleware(BaseMiddleware):
    middleware_key: Final[str] = "l10n"

    def __init__(self, dispenser: FluentDispenser):
        self.dispenser = dispenser

    async def __call__(
            self,
            handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
            event: TelegramObject,
            data: Dict[str, Any],
    ) -> Any:
        event: Update
        if is_pm(event):
            user: User = data["event_from_user"]
            data[self.middleware_key] = self.dispenser.get_language(user.language_code)
        else:
            data[self.middleware_key] = self.dispenser.default_locale

        return await handler(event, data)


================================================
FILE: bot/middlewares/log_unhandled.py
================================================
from typing import Any, Awaitable, Callable, Dict

import structlog
from aiogram import BaseMiddleware
from aiogram.dispatcher.event.bases import UNHANDLED
from aiogram.types import TelegramObject
from structlog.typing import FilteringBoundLogger

logger: FilteringBoundLogger = structlog.get_logger()


class UnhandledUpdatesLoggerMiddleware(BaseMiddleware):
    async def __call__(
            self,
            handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
            event: TelegramObject,
            data: Dict[str, Any],
    ) -> Any:
        result = await handler(event, data)
        if result is UNHANDLED:
            await logger.awarning(
                "Unhandled update",
                update=event.dict()
            )


================================================
FILE: bot/migration_cache.py
================================================
from math import inf
from cachetools import TTLCache

"""
This is a simple TTL Cache, so that my_chat_member doesn't trigger on group to supergroup migration event
"""
cache = TTLCache(maxsize=inf, ttl=10.0)


================================================
FILE: bot/ui_commands.py
================================================
from aiogram import Bot
from aiogram.types import BotCommand, BotCommandScopeAllPrivateChats, BotCommandScopeAllGroupChats
from fluent.runtime import FluentLocalization

from bot.fluent_helper import FluentDispenser


async def set_bot_commands(bot: Bot, dispenser: FluentDispenser) -> None:
    """
    Set bot commands in UI (using Menu or "/" buttons)
    :param bot: Bot object
    :param dispenser: FluentDispenser object
    """
    data = list()

    for lang_key in dispenser.available_languages:
        locale_object: FluentLocalization = dispenser.get_language(lang_key)

        data.extend(
            [
                (
                    [
                        BotCommand(command="id", description=locale_object.format_value("cmd-hint-id-pm")),
                        BotCommand(command="help", description=locale_object.format_value("cmd-hint-help")),
                    ],
                    BotCommandScopeAllPrivateChats(),
                    lang_key
                ),
                (
                    [BotCommand(command="id", description=locale_object.format_value("cmd-hint-id-group"))],
                    BotCommandScopeAllGroupChats(),
                    lang_key
                ),
            ]

        )
    for commands_list, commands_scope, language in data:
        await bot.set_my_commands(commands=commands_list, scope=commands_scope, language_code=language)


================================================
FILE: docker-compose.example.yml
================================================
version: "3.8"
services:
    bot:
        image: groosha/my-id-bot:latest
        restart: unless-stopped
        env_file: .env
        # uncomment and set your paths if you want to use your own locales
#        volumes:
#            -  "./locales:/app/bot/locales"

================================================
FILE: env_example
================================================
# rename this file to .env (with the following dot)

# Bot token, get one from https://t.me/botfather
BOT_TOKEN=12345:abcxyz

### Logging configuration ###

# Render mode: pretty with "console", or more production-like with "json"
# Warning: case-sensitive!
LOGGING_RENDERER=console

# Minimum logging level: DEBUG, INFO, WARNING, ERROR or CRITICAL
# Warning: case-sensitive!
LOGGING_LEVEL=INFO

# Datetime format for logs
LOGGING_FORMAT=%Y-%m-%d %H:%M:%S

# true/yes/1 if you want to show time in UTC timezone instead of computer's local tz
LOGGING_IS_UTC=no

# true/yes/1 if you want to log unhandled updates (updates which your bot received but could not find handler to answer)
LOGGING_LOG_UNHANDLED=no

================================================
FILE: my-id-bot.example.service
================================================
[Unit]
Description=Telegram My Id Bot
After=network.target

[Service]
Type=simple
WorkingDirectory=/home/user/my-id-bot
EnvironmentFile=/home/user/my-id-bot/.env
ExecStart=/home/user/my-id-bot/venv/bin/python -m bot
KillMode=process
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

================================================
FILE: requirements.txt
================================================
# This file was autogenerated by uv via the following command:
#    uv pip compile -o requirements.txt requirements.in
aiofiles==24.1.0
    # via aiogram
aiogram==3.18.0
    # via -r requirements.in
aiohappyeyeballs==2.4.6
    # via aiohttp
aiohttp==3.11.13
    # via aiogram
aiosignal==1.3.2
    # via aiohttp
annotated-types==0.7.0
    # via pydantic
attrs==25.1.0
    # via
    #   aiohttp
    #   fluent-runtime
babel==2.17.0
    # via fluent-runtime
cachetools==5.5.2
    # via -r requirements.in
certifi==2025.1.31
    # via aiogram
fluent-runtime==0.4.0
    # via -r requirements.in
fluent-syntax==0.19.0
    # via fluent-runtime
frozenlist==1.5.0
    # via
    #   aiohttp
    #   aiosignal
idna==3.10
    # via yarl
magic-filter==1.0.12
    # via aiogram
multidict==6.1.0
    # via
    #   aiohttp
    #   yarl
propcache==0.3.0
    # via
    #   aiohttp
    #   yarl
pydantic==2.10.6
    # via
    #   aiogram
    #   pydantic-settings
pydantic-core==2.27.2
    # via pydantic
pydantic-settings==2.4.0
    # via -r requirements.in
python-dotenv==1.0.1
    # via pydantic-settings
pytz==2025.1
    # via fluent-runtime
structlog==25.1.0
    # via -r requirements.in
typing-extensions==4.12.2
    # via
    #   aiogram
    #   fluent-runtime
    #   fluent-syntax
    #   pydantic
    #   pydantic-core
yarl==1.18.3
    # via aiohttp
Download .txt
gitextract_0g8cu8tb/

├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── bot/
│   ├── __init__.py
│   ├── __main__.py
│   ├── config_reader.py
│   ├── fluent_helper.py
│   ├── handlers/
│   │   ├── __init__.py
│   │   ├── add_or_migrate.py
│   │   ├── commands.py
│   │   ├── errors.py
│   │   ├── inline_mode.py
│   │   └── pm.py
│   ├── locales/
│   │   ├── en/
│   │   │   └── strings.ftl
│   │   ├── es/
│   │   │   └── strings.ftl
│   │   ├── ro/
│   │   │   └── strings.ftl
│   │   ├── ru/
│   │   │   └── strings.ftl
│   │   └── uk/
│   │       └── strings.ftl
│   ├── logs.py
│   ├── middlewares/
│   │   ├── __init__.py
│   │   ├── l10n.py
│   │   └── log_unhandled.py
│   ├── migration_cache.py
│   └── ui_commands.py
├── docker-compose.example.yml
├── env_example
├── my-id-bot.example.service
└── requirements.txt
Download .txt
SYMBOL INDEX (32 symbols across 12 files)

FILE: bot/__main__.py
  function main (line 20) | async def main():

FILE: bot/config_reader.py
  class LoggingRenderer (line 7) | class LoggingRenderer(str, Enum):
  class LoggingSettings (line 12) | class LoggingSettings(BaseSettings):
  class BotSettings (line 27) | class BotSettings(BaseSettings):

FILE: bot/fluent_helper.py
  class FluentDispenser (line 6) | class FluentDispenser:
    method __init__ (line 7) | def __init__(self, locales_dir: Path, default_language: str = "en"):
    method default_locale (line 31) | def default_locale(self) -> FluentLocalization:
    method available_languages (line 35) | def available_languages(self) -> list[str]:
    method get_language (line 38) | def get_language(self, language_code: str):

FILE: bot/handlers/add_or_migrate.py
  function bot_added_to_group (line 21) | async def bot_added_to_group(event: ChatMemberUpdated, bot: Bot, l10n: F...
  function group_to_supergroup_migration (line 42) | async def group_to_supergroup_migration(message: Message, bot: Bot, l10n...

FILE: bot/handlers/commands.py
  function cmd_start (line 13) | async def cmd_start(message: Message, l10n: FluentLocalization):
  function cmd_id_pm (line 29) | async def cmd_id_pm(message: Message, l10n: FluentLocalization):
  function cmd_id_groups (line 48) | async def cmd_id_groups(message: Message, l10n: FluentLocalization):
  function cmd_help (line 74) | async def cmd_help(message: Message, l10n: FluentLocalization):

FILE: bot/handlers/errors.py
  function handle_errors (line 12) | async def handle_errors(event: ErrorEvent):

FILE: bot/handlers/inline_mode.py
  function inline_mode_handler (line 10) | async def inline_mode_handler(query: InlineQuery, l10n: FluentLocalizati...

FILE: bot/handlers/pm.py
  function get_channel_or_supergroup_id (line 12) | async def get_channel_or_supergroup_id(message: Message, chat_type: str,...
  function get_user_id_no_privacy (line 29) | async def get_user_id_no_privacy(message: Message, l10n: FluentLocalizat...
  function get_user_id_with_privacy (line 44) | async def get_user_id_with_privacy(message: Message, l10n: FluentLocaliz...
  function sticker_in_pm (line 57) | async def sticker_in_pm(message: Message, l10n: FluentLocalization):
  function other_inline_bot_in_pm (line 70) | async def other_inline_bot_in_pm(message: Message, l10n: FluentLocalizat...
  function other_in_pm (line 83) | async def other_in_pm(message: Message, l10n: FluentLocalization):

FILE: bot/logs.py
  function get_structlog_config (line 10) | def get_structlog_config(config: LoggingSettings) -> dict:
  function get_processors (line 19) | def get_processors(config: LoggingSettings) -> list:

FILE: bot/middlewares/l10n.py
  function is_pm (line 10) | def is_pm(event: Update) -> bool:
  class L10nMiddleware (line 16) | class L10nMiddleware(BaseMiddleware):
    method __init__ (line 19) | def __init__(self, dispenser: FluentDispenser):
    method __call__ (line 22) | async def __call__(

FILE: bot/middlewares/log_unhandled.py
  class UnhandledUpdatesLoggerMiddleware (line 12) | class UnhandledUpdatesLoggerMiddleware(BaseMiddleware):
    method __call__ (line 13) | async def __call__(

FILE: bot/ui_commands.py
  function set_bot_commands (line 8) | async def set_bot_commands(bot: Bot, dispenser: FluentDispenser) -> None:
Condensed preview — 29 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (44K chars).
[
  {
    "path": ".gitignore",
    "chars": 75,
    "preview": ".idea/\nvenv/\n__pycache__/\n*.log\n/.env\nmy-id-bot.service\ndocker-compose.yml\n"
  },
  {
    "path": "Dockerfile",
    "chars": 443,
    "preview": "# Separate \"build\" image\nFROM python:3.11-slim as builder\nWORKDIR /app\nCOPY requirements.txt .\nRUN pip install --no-cach"
  },
  {
    "path": "LICENSE",
    "chars": 1117,
    "preview": "MIT License\n\nCopyright (c) 2019-present Aleksandr K. (also known as MasterGroosha on GitHub)\n\nPermission is hereby grant"
  },
  {
    "path": "README.md",
    "chars": 2002,
    "preview": "# Bot to get users/chats IDs in Telegram\n\nThis is a simple bot written with [aiogram 3.x](https://github.com/aiogram/aio"
  },
  {
    "path": "bot/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "bot/__main__.py",
    "chars": 1927,
    "preview": "import asyncio\nfrom pathlib import Path\n\nimport structlog\nfrom aiogram import Bot, Dispatcher\nfrom aiogram.client.defaul"
  },
  {
    "path": "bot/config_reader.py",
    "chars": 815,
    "preview": "from enum import Enum\n\nfrom pydantic import SecretStr\nfrom pydantic_settings import BaseSettings, SettingsConfigDict\n\n\nc"
  },
  {
    "path": "bot/fluent_helper.py",
    "chars": 1501,
    "preview": "from pathlib import Path\n\nfrom fluent.runtime import FluentLocalization, FluentResourceLoader\n\n\nclass FluentDispenser:\n "
  },
  {
    "path": "bot/handlers/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "bot/handlers/add_or_migrate.py",
    "chars": 1558,
    "preview": "from asyncio import sleep\n\nfrom aiogram import Bot, html, Router, F\nfrom aiogram.enums import ChatType\nfrom aiogram.filt"
  },
  {
    "path": "bot/handlers/commands.py",
    "chars": 2960,
    "preview": "from aiogram import Router, html, F\nfrom aiogram.enums import ChatType\nfrom aiogram.filters import Command, CommandStart"
  },
  {
    "path": "bot/handlers/errors.py",
    "chars": 798,
    "preview": "import structlog\nfrom aiogram import Router\nfrom aiogram.exceptions import TelegramAPIError\nfrom aiogram.types.error_eve"
  },
  {
    "path": "bot/handlers/inline_mode.py",
    "chars": 1064,
    "preview": "from aiogram import html, Router\nfrom aiogram.enums import ChatType\nfrom aiogram.types import InlineQuery, InlineQueryRe"
  },
  {
    "path": "bot/handlers/pm.py",
    "chars": 3526,
    "preview": "from aiogram import Router, html, F\nfrom aiogram.enums import ChatType\nfrom aiogram.filters import MagicData\nfrom aiogra"
  },
  {
    "path": "bot/locales/en/strings.ftl",
    "chars": 2475,
    "preview": "# without @ !\nbot-username = my_id_bot\n\n# do not translate!\nbot-group-deeplink = https://t.me/{bot-username}?startgroup="
  },
  {
    "path": "bot/locales/es/strings.ftl",
    "chars": 2650,
    "preview": "# without @ !\nbot-username = my_id_bot\n\n# do not translate!\nbot-group-deeplink = https://t.me/{bot-username}?startgroup="
  },
  {
    "path": "bot/locales/ro/strings.ftl",
    "chars": 2948,
    "preview": "# without @ !\nbot-username = my_id_bot\n\n# do not translate!\nbot-group-deeplink = https://t.me/{bot-username}?startgroup="
  },
  {
    "path": "bot/locales/ru/strings.ftl",
    "chars": 2544,
    "preview": "# without @ !\nbot-username = my_id_bot\n\n# do not translate!\nbot-group-deeplink = https://t.me/{bot-username}?startgroup="
  },
  {
    "path": "bot/locales/uk/strings.ftl",
    "chars": 2642,
    "preview": "# without @ !\nbot-username = my_id_bot\n\n# do not translate!\nbot-group-deeplink = https://t.me/{bot-username}?startgroup="
  },
  {
    "path": "bot/logs.py",
    "chars": 1261,
    "preview": "import logging\nfrom json import dumps\n\nimport structlog\nfrom structlog import WriteLoggerFactory\n\nfrom bot.config_reader"
  },
  {
    "path": "bot/middlewares/__init__.py",
    "chars": 169,
    "preview": "from .l10n import L10nMiddleware\nfrom .log_unhandled import UnhandledUpdatesLoggerMiddleware\n\n__all__ = [\n    \"L10nMiddl"
  },
  {
    "path": "bot/middlewares/l10n.py",
    "chars": 1131,
    "preview": "from typing import Any, Awaitable, Callable, Dict, Final\n\nfrom aiogram import BaseMiddleware\nfrom aiogram.enums import C"
  },
  {
    "path": "bot/middlewares/log_unhandled.py",
    "chars": 763,
    "preview": "from typing import Any, Awaitable, Callable, Dict\n\nimport structlog\nfrom aiogram import BaseMiddleware\nfrom aiogram.disp"
  },
  {
    "path": "bot/migration_cache.py",
    "chars": 208,
    "preview": "from math import inf\nfrom cachetools import TTLCache\n\n\"\"\"\nThis is a simple TTL Cache, so that my_chat_member doesn't tri"
  },
  {
    "path": "bot/ui_commands.py",
    "chars": 1413,
    "preview": "from aiogram import Bot\nfrom aiogram.types import BotCommand, BotCommandScopeAllPrivateChats, BotCommandScopeAllGroupCha"
  },
  {
    "path": "docker-compose.example.yml",
    "chars": 266,
    "preview": "version: \"3.8\"\nservices:\n    bot:\n        image: groosha/my-id-bot:latest\n        restart: unless-stopped\n        env_fi"
  },
  {
    "path": "env_example",
    "chars": 706,
    "preview": "# rename this file to .env (with the following dot)\n\n# Bot token, get one from https://t.me/botfather\nBOT_TOKEN=12345:ab"
  },
  {
    "path": "my-id-bot.example.service",
    "chars": 299,
    "preview": "[Unit]\nDescription=Telegram My Id Bot\nAfter=network.target\n\n[Service]\nType=simple\nWorkingDirectory=/home/user/my-id-bot\n"
  },
  {
    "path": "requirements.txt",
    "chars": 1341,
    "preview": "# This file was autogenerated by uv via the following command:\n#    uv pip compile -o requirements.txt requirements.in\na"
  }
]

About this extraction

This page contains the full source code of the MasterGroosha/my-id-bot GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 29 files (37.7 KB), approximately 10.5k tokens, and a symbol index with 32 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!