Lucene search
+L

Telegram Channel Profiler

🗓️ 10 Jul 2026 11:31:42Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 1 Views

Telegram Channel Profiler performs passive surface profiling of public t.me previews.

Refs
Code
from kittysploit import *
import json
import re
from urllib.parse import quote

from core.osint.providers import telegram_bot_token
from lib.protocols.http.http_client import Http_client


class Module(Auxiliary, Http_client):
    __info__ = {
        "name": "Telegram Channel Profiler",
        "author": ["KittySploit Team"],
        "description": (
            "Passive Telegram surface profiling via public t.me previews and optional "
            "Bot API (getChat) for authorized investigations. No account login required."
        ),
        "tags": ["osint", "passive", "telegram", "social"],
    }

    target = OptString("", "Domain, @username, or channel slug to profile", required=True)
    bot_token = OptString("", "Optional Telegram Bot token (or set in ~/.kittysploit/osint.toml)", required=False)
    max_channels = OptString("15", "Maximum channel candidates to probe", required=False)
    timeout = OptString("10", "HTTP timeout in seconds", required=False)
    output_file = OptString("", "Optional JSON output file", required=False)

    BROWSER_UA = "Mozilla/5.0 (compatible; KittyOSINT/1.0; +https://kittysploit.local)"

    def _to_int(self, value, default_value):
        try:
            return max(1, int(str(value).strip()))
        except Exception:
            return default_value

    def _http_get_host(self, host, path, timeout_seconds, headers=None, ssl=True, port=443):
        old_target = self.target
        old_port = getattr(self, "port", 443)
        old_ssl = getattr(self, "ssl", True)
        try:
            self.target = host
            self.port = int(port)
            self.ssl = ssl
            return self.http_request(
                method="GET",
                path=path,
                allow_redirects=True,
                timeout=timeout_seconds,
                headers=headers or {},
            )
        except Exception:
            return None
        finally:
            self.target = old_target
            self.port = old_port
            self.ssl = old_ssl

    def _derive_candidates(self, raw_target):
        text = str(raw_target or "").strip().lower()
        text = re.sub(r"^https?://", "", text)
        text = text.lstrip("@").split("/", 1)[0]
        candidates = set()
        if not text:
            return []
        if text.startswith("t.me"):
            text = text.split("/", 1)[-1]
        candidates.add(text)
        if "." in text:
            apex = text.split(".", 1)[0]
            candidates.add(apex)
            candidates.add(f"{apex}news")
            candidates.add(f"{apex}_official")
            candidates.add(f"{apex}channel")
        else:
            candidates.add(f"{text}news")
            candidates.add(f"{text}_official")
        return sorted(candidates)

    def _parse_preview_html(self, html, slug):
        title = ""
        description = ""
        m = re.search(r'<meta property="og:title" content="([^"]+)"', html or "")
        if m:
            title = m.group(1)
        m = re.search(r'<meta property="og:description" content="([^"]+)"', html or "")
        if m:
            description = m.group(1)
        members = None
        m = re.search(r"([\d\s\u202f\u00a0.,]+)\s+members?", html or "", re.IGNORECASE)
        if m:
            digits = re.sub(r"[^\d]", "", m.group(1))
            if digits.isdigit():
                members = int(digits)
        return {
            "username": slug,
            "title": title,
            "description": description[:500] if description else "",
            "member_count": members,
            "url": f"https://t.me/s/{slug}",
            "platform": "telegram",
            "source": "t.me_preview",
            "confidence": 72 if title else 45,
        }

    def _fetch_preview(self, slug, timeout_seconds):
        path = f"/s/{quote(slug)}"
        resp = self._http_get_host(
            "t.me",
            path,
            timeout_seconds,
            headers={"User-Agent": self.BROWSER_UA},
        )
        if not resp or resp.status_code != 200:
            return None
        body = resp.text or ""
        if "tgme_page" not in body and "og:title" not in body:
            return None
        finding = self._parse_preview_html(body, slug)
        if not finding.get("title"):
            return None
        return finding

    def _fetch_bot_api(self, slug, token, timeout_seconds):
        if not token:
            return None
        chat_id = f"@{slug}" if not slug.startswith("@") else slug
        path = f"/bot{token}/getChat?chat_id={quote(chat_id)}"
        resp = self._http_get_host("api.telegram.org", path, timeout_seconds)
        if not resp or resp.status_code != 200:
            return None
        try:
            data = resp.json()
        except Exception:
            return None
        if not data.get("ok"):
            return None
        result = data.get("result") or {}
        username = str(result.get("username") or slug).lstrip("@")
        return {
            "username": username,
            "title": str(result.get("title") or ""),
            "description": str(result.get("description") or "")[:500],
            "member_count": result.get("member_count"),
            "url": f"https://t.me/{username}",
            "platform": "telegram",
            "source": "telegram_bot_api",
            "confidence": 88,
        }

    def run(self):
        candidates = self._derive_candidates(self.target)
        if not candidates:
            print_error("target must be a domain, @username, or channel slug")
            return {"error": "invalid target"}

        timeout_seconds = self._to_int(self.timeout, 10)
        max_channels = self._to_int(self.max_channels, 15)
        token = telegram_bot_token(self.bot_token)

        print_info(f"Telegram profiling: {len(candidates)} candidate slug(s)")
        findings = []
        seen = set()

        for slug in candidates[:max_channels]:
            slug = slug.lstrip("@")
            if slug in seen:
                continue
            seen.add(slug)

            finding = None
            if token:
                finding = self._fetch_bot_api(slug, token, timeout_seconds)
            if not finding:
                finding = self._fetch_preview(slug, timeout_seconds)
            if finding:
                findings.append(finding)
                print_success(f"  @{finding.get('username')}: {finding.get('title', '')[:60]}")

        result = {
            "target": self.target,
            "findings": findings,
            "channel_count": len(findings),
            "bot_api_used": bool(token),
            "source_urls": [f.get("url") for f in findings if f.get("url")],
        }

        if not findings:
            print_warning("No public Telegram channels matched (try apex slug or @handle)")
        else:
            print_success(f"Telegram channels profiled: {len(findings)}")

        if self.output_file:
            try:
                with open(str(self.output_file), "w", encoding="utf-8") as handle:
                    json.dump(result, handle, indent=2)
                print_success(f"Results saved to {self.output_file}")
            except Exception as exc:
                print_error(f"Failed to save output: {exc}")

        return result

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

10 Jul 2026 11:31Current
5.8Medium risk
Vulners AI Score5.8
1