Telegram source 확정 + objectFields 재편(2026-07-21) · 코드 반영 전

Social Fetcher URL Structure Audit — 2026-07-19
관련 코드: social-fetcher.router.ts(classifyTelegram) · fetchers/telegram.fetcher.ts
관련 데이터: samples/tg_channel.json · samples/tg_guard_group.json
소스 채택 권장도 conditional-adopt (구현 확장 전제)
밈코인 트레이더 관점에서 텔레그램은 원래 모멘텀이 사는 채널(페르소나 브리프: X/Telegram 高)이 맞다 — 문제는 소스 자체가 아니라 현재 구현이 그 모멘텀을 못 건드린다는 것. tg_channel 경로는 title/subscriberCount/nameMatch 3개뿐이라 '도달 규모(절대치)'와 '사칭 의심(nameMatch)' 정도만 주고, 트레이더가 진짜 원하는 참여 속도·최근 활동 신선도(post 단위 messageDate)는 tg_s_preview가 미구현이라 아예 없다. 게다가 t.me/+·joinchat 초대전용 그룹은 무조건 blocked(data=null)라, 밈코인 특유의 '알파 시그널이 도는 비공개방' 구조 자체를 통째로 못 본다 — 이건 우리 쪽 구현 갭이 아니라 텔레그램 구조상 원천 차단(인벤토리 rank1~4 어느 경로로도 그룹 멤버 열거 불가)이라 fetcher 개선으로도 못 메운다.
트레이더 관점: 지금 상태로는 '구독자 5천 vs 50명' 같은 거친 필터링, '채널명이 토큰명과 다르면 의심' 정도의 1차 스크리닝에만 쓴다 — 이건 actionable이지만 얕다. t.me/s/<name> 프리뷰(rank1, 무료·미구현)를 붙여 게시물 타임스탬프/조회수를 얻으면 '방금 만든 채널에 오늘 아침에야 첫 글 올라옴' 같은 진짜 신선도·활동 tell을 얻을 수 있어 채택가치가 확 오른다. Apify 유료 액터(rank4)는 결국 이 무료 t.me/s/ HTML을 재포장하는 것으로 보여 추가 신뢰도 없이 비용만 나가므로 트레이더 입장에서 우선순위 낮음 — 무료 확장(rank1) 먼저.
✅ 최종 결정 (수기 audit 반영)
원칙: 조인 키는 guardChatId(가드 verify 링크의 -100…) 우선, 없으면 username 폴백. 텔레그램 username 은 다른 6개 소셜과 달리 키로 쓸 수 없다 — 소유자가 바꿀 수 있고 놓으면 타인이 가져간다(X 핸들과 같고 Reddit 과 반대). 실측으로 이관 2건을 잡았다: seelprotocol·flybubbleman 은 6월 토큰이 링크했는데 채널 생성일이 7월이다 → 그 사이 같은 이름이 다른 채널로 넘어갔다. 반면 -100… 은 Telegram 이 부여한 숫자 chat id 라 포털 이름을 갈아끼워도 불변이다. ⚠️ 다만 가드가 없으면 이 키가 없다(표본의 48%) — 그래서 완료 rubric #9(키=immutable)의 유일한 의도된 예외로 기록한다. 근거·다이어그램: [telegram-guard-reuse.html](../system-ideations/telegram-guard-reuse.html)
조인 키
객체비고
Venue — 가드 포털 (tg_portal)guardChatId (-100 숫자)실측: 포털 본문 verify 링크 t.me/{bot}?start=-1004322983935 에서 추출. 표본 포털 36개 → 고유 id 36종(중복 0) 으로 1:1. ⚠️ Telegram 내부에서 채널과 슈퍼그룹은 같은 객체 타입·같은 ID 풀(broadcast/megagroup 플래그로만 구분)이라 이 값만으로 채널인지 그룹인지 못 가른다 → 이름을 guardGroupId 가 아니라 guardChatId 로 둔다. ⚠️ 이 값이 실제 입장 대상인지는 직접 확인하지 않았다(딥링크 페이로드는 봇이 임의로 정하는 문자열).
Venue — 그 외 (tg_channel · tg_shell)username (폴백, 가변)가드가 없으면 -100… 을 얻을 방법이 없다. username 을 키로 쓰되 가변임을 전제한다 — 관측 시점이 다르면 다른 대상일 수 있다. 실측 aegisprivacy 는 두 관측 사이 title AEGIS Portalaegisprivacy, subs 49→4 로 값이 실제로 바뀌었다.
Venue — 초대전용 (tg_guard_group)inviteHash (관측만, 조인 불가)t.me/+{hash} 는 어떤 경로로도 열리지 않는다(fetcher 가 blocked 반환). 해시는 관리자가 폐기·재발급할 수 있어 방 정체성이 아니다 — 존재 신호로만 쓴다. 실측 362건 중 3건(0.8%).
sourceType 통합 맵
최종 타입포함 패턴
tg_portal (신규 · fetcher 판정)t.me/{name}본문에 가드 verify 링크가 있는 것. 실측 표본 69개 중 36개(52%) — 가장 흔한 형태인데 기존 문서엔 객체 자체가 없었다. 우리가 관측하는 건 포털이지 커뮤니티가 아니다(포털 486 subscribers vs 실제 방 1,377 members).
tg_shell (신규 · fetcher 판정)t.me/{name}사람이 올린 메시지가 0건(Channel created·Channel photo updated 서비스 메시지뿐). 실측 21개(30%). 설명만 있거나, 그룹이라 t.me/s/ 프리뷰가 안 나오는 경우.
tg_channel (의미 축소)t.me/{name} · t.me/s/{name}실제 메시지가 있는 활성 채널. 실측 4개(6%) 뿐 — 기존 문서가 이 타입 하나로 위 셋을 다 덮고 있었다.
tg_channel (변경 없음)t.me/{name}/{messageId} — messageId 를 버리고 채널로 흡수. 실측 362건 중 1건(0.3%) 이라 별도 타입 신설의 이득이 없고, t.me/s/ 채택 시 어차피 그 채널의 최근 메시지를 전부 받는다.
tg_guard_group (변경 없음)t.me/+{hash} · t.me/joinchat/{hash} — 멤버 열거 불가. blocked 유지.
구조 결정 — 필드 단위 key/keep/remove로 표현할 수 없는 결정
항목내용
sourceType 을 fetcher 가 정한다 — telegram 만의 예외t.me/{name} URL 만으로는 포털인지 빈 채널인지 활성 채널인지 알 수 없다. 프리뷰를 받아봐야 갈린다. 그래서 router 는 전부 tg_channel 로 두고 fetcher 가 관측 결과로 좁힌다. 다른 6개 소셜은 router 가 URL 로 결정하므로 이 문서만 규약이 다르다.
t.me/{name}t.me/s/{name} 로 교체 (Venue 확장만)같은 1회 요청으로 훨씬 많이 온다(실측 10.9KB → 24.8KB, 구독자수는 동일). 신규 필드 전부가 여기서만 나온다 — 가드 chat id·봇·채널 생성일·카운터·메시지. Content 레코드는 만들지 않는다 — 실제 콘텐츠가 있는 채널이 6% 뿐이라 이득이 작다.
관측하는 지표가 커뮤니티 지표가 아니다포털이면 subscriberCount포털 구독자다. 실측 포털 36개 중 값이 있는 건 21개, 중앙값 7명(활성 채널 중앙 113명). 유일한 대조 사례에서 포털 486 vs 실제 방 1,377(2.8배). → 커뮤니티 규모·나이 기반 필터는 만들 수 없다. 텔레그램 축의 성격이 “어떤 커뮤니티인가”가 아니라 “이 팀이 어떻게 셋업했나” 로 바뀐다.
-100… 으로 역방향 조회는 불가 — 등가 비교만 가능MTProto 는 InputChannelchannel_id + access_hash 를 함께 요구하고, access_hash 는 username 해석·같은 방 소속 등 사전 상호작용으로만 얻는다(스팸 방지 설계). Bot API getChat봇이 그 방 멤버여야 동작한다 — 공개 채널을 username 으로 불러도 마찬가지. → 이 키는 “같은 방인가” 판정 전용이다.
공식 API 를 써도 막힌 곳은 그대로Bot API 는 사전 멤버십이 필요해 임의 토큰 채널에 스케일 불가(지금 스크래핑보다 오히려 적게 얻는다). MTProto 는 공개 포털에서만 조금 더 준다(verified 플래그·반응 상세·그룹 지원) — 대가는 전화번호 로그인·flood-wait·계정정지·ToS 회색지대. 가드 뒤와 정확한 생성일은 세 경로 전부 불가.
가드봇 신원은 화이트리스트 대조 — 판정에 33% 공백실측 포털 36개 → 고유 봇 4종. Collab.Land 는 공식 문서가 *“정확히 collablandbot 이 아니면 사칭”* 이라 명시해 24건(67%) 을 사칭으로 판정할 수 있다(CollabLandlBot=소문자 l 삽입, coIIab_Iands_bot=대문자 I 위장, Collab_Land_Media_bot). 반면 @safeguard(12건, 33%)는 공식 목록을 못 찾아 판정 불가다. Fake Safeguard 공격 계열(클립보드 주입→PowerShell→RAT)이 문서화돼 있으나 원문이 봇 username 을 특정하지 않는다.
이 문서에서 쓰는 pill 범례
관측 source-data에서 실제 발견됨 웹리서치/실측 검색·curl·실제 호출로 확인 정상/유지 차단·버그·갭·drop 신규/분리 타입 fixed / mutable confidence: high medium low
목차   1a. 개념 글로서리 · 1b. URL 패턴 · 2. sourceType/sourceKey 판정 · 3. Fetch 테스트 + 획득 방법 · 4. 필드 통합 (변동성·파싱·판정) · 다음 액션

1a. 플랫폼 개념 글로서리

이 플랫폼을 이해하는 데 필요한 도메인 개념. URL/필드 설명에서 참조.

개념설명
채널(Channel)단방향 브로드캐스트 소셜 계정 유형. 관리자만 게시, 구독자는 읽기 전용. sourceType=tg_channel로 분류됨 관측
공개 채널/그룹 username URL (t.me/{value})고정 공개 식별자(username) 기반 URL. tg_channel 표본 360건 중 359건이 이 형태(t.me/NvidiaMoonTG 등) 관측
게시물 딥링크 (t.me/{username}/{messageId})채널 내 특정 메시지(게시물)를 가리키는 URL. 표본에서 t.me/pumpfuns_tech_updates/7 형태 1건 관측, sourceField가 telegram이 아닌 website로 기록됨(채널 홍보글을 웹사이트 필드에 넣은 케이스) 관측
초대 링크(Invite Link, t.me/+{hash})username 없이(비공개 또는 username 미설정) 그룹/채널에 참여시키는 해시 기반 링크. '+' 접두사로 식별. tg_guard_group 표본 3건 전부 이 형태이며 status=blocked(비로그인/비참여 상태로는 fetch 불가)로 관측됨 관측
username(공개 식별자) 대소문자 규칙Telegram username은 대소문자를 구분하지 않음(case-insensitive) — 실제 계정은 NvidiaMoonTG, PRSMONSOL처럼 대문자 혼용 표기가 흔하지만 telegram 서버 라우팅상 동일 계정으로 취급. 표본의 sourceKey는 sourceUrl의 value를 소문자로 정규화해 저장(nvidiamoontg, prsmonsol 등) — 이는 audit 파이프라인이 채택한 정규화 규칙 관측
초대 해시(invite hash) 대소문자 규칙username과 달리 t.me/+{hash} 의 hash는 대소문자를 구분하는 opaque 토큰(base64 유사). 표본 sourceKey가 원본 대소문자를 그대로 보존(df1YH6zqisBkOWYx, I8tlBry71KEzMjE0 등) — username 정규화 규칙과 달리 소문자화되지 않음 관측
구독자 수(subscriberCount)채널의 팔로워 격 지표. 표본에서 mutable(시간에 따라 변함)로 명시 분류되며 값 자체는 4~67 등 소규모로 관측됨 관측
제목(title) / nameMatchtitle은 fixed(구조적으로 값이 고정 성격, 자유 변경 가능하지만 스키마상 취급은 고정)로 분류. nameMatch는 title이 토큰 이름과 일치하는지 여부를 나타내는 파생 플래그(true/false 관측) 관측
status: blockedfetch 시도 결과 상태값 중 하나로, 초대 링크형(tg_guard_group) URL은 3건 전부 blocked로 관측 — 비공개/가드봇 그룹 특성상 미인증 접근이 차단됨을 시사하는 관측 패턴(원인은 telegram 정책 조사 필요, 여기선 관측 사실만) 관측
봇 가드 그룹(guard group)진입 시 CAPTCHA/봇 인증을 요구하는 그룹을 가리키는 sourceType 분류(tg_guard_group). 스캠/봇 방지 목적의 그룹으로, audit 파이프라인이 별도 sourceType으로 분리해 관리 관측
채널(Channel) vs 그룹(Group) vs 슈퍼그룹(Supergroup)Telegram 대화 개체 3종. 채널=단방향 브로드캐스트(무제한 구독자, 관리자만 게시), 그룹=일반 대화방(최대 200명, 기본 대화형), 슈퍼그룹=대규모 대화방(최대 20만명, 그룹의 확장판으로 관리 기능 강화) 웹리서치/실측
username 규칙(공식)5~32자, 영문/숫자/언더스코어만 허용, 문자로 시작, 숫자로 끝날 수 없음. 전역 유일(unique)하며 t.me/{username} 형태로 공개 접근 가능. 대소문자 구분 없음(공식 정책) 웹리서치/실측
초대 링크(Invite Link) 메커니즘비공개(private) 채널/그룹이나 username 미설정 개체에 발급되는 t.me/+{hash} 또는 구형 t.me/joinchat/{hash} 형태 링크. 관리자가 생성/폐기(revoke) 가능하며 만료·가입인원 제한 설정 가능. 폐기 시 해당 링크는 영구 무효화됨 웹리서치/실측
메시지 딥링크(Message permalink)공개 채널/그룹의 개별 메시지에 대한 영구 링크 t.me/{username}/{message_id}. 비공개 그룹의 경우 t.me/c/{internal_id}/{message_id} 형태(로그인 필요)로 별도 존재 웹리서치/실측
채널/그룹 내부 숫자 ID (internal chat id)모든 대화 개체는 username과 별개로 영속적인 내부 숫자 ID를 가짐. 비공개 채널의 t.me/c/{internal_id}/... 링크나 Bot API 호출 시 사용되며, username 변경/제거와 무관하게 고정됨 웹리서치/실측
공유(Forward) / 전달Telegram의 메시지 재전송 기능. X의 retweet와 유사하나 quote 개념은 없고, 원본 출처(forward_from) 메타데이터가 유지된 채로 전달되는 것이 특징(X는 RT/QT가 별도 엔티티, TG는 forward가 메시지 속성) 웹리서치/실측
핀 메시지(Pinned message)채널/그룹 상단에 고정 노출되는 메시지. 공지·중요 링크 게시 용도로 흔히 사용되며 URL 구조상 별도 표시 없이 일반 메시지 링크와 동일 형태 웹리서치/실측
봇 딥링크(Bot deep link, t.me/{bot_username}?start={payload})봇 시작 파라미터를 포함한 링크. 리퍼럴 트래킹, 가드봇 인증 플로우 등에 활용되는 TG 특유 URL 패턴(username 링크의 쿼리 변형) 웹리서치/실측

1b. URL 패턴

t.meNvidiaMoonTG
공개 채널 (tg_channel) — 구독자수는 og 메타 아님, tgme_page_extra div 파싱, 변동값
t.me+df1YH6zqisBkOWYx
비공개 초대 링크 (tg_guard_group) — 멤버 열거 차단, 항상 blocked
URL 패턴의미ID 후보관측출처/비고
t.me/{value}공개 채널/그룹 핸들 → tg_channel (fetcher가 https://t.me/{key} HTML 프리뷰를 GET해 og:title/tgme_page_extra 파싱, subscriberCount mutable, title/nameMatch fixed)채널 핸들(username), 라우터가 소문자화해 sourceKey로 사용관측 359건samples/tg_channel.json 구조1 (count 359/360)
t.me/{value}/{messageId}채널의 특정 게시물(post) 딥링크. 라우터는 메시지ID를 무시하고 첫 세그먼트만 sourceKey로 써서 tg_channel로 동일 분류(채널 단위 fetch, 특정 글은 조회 안 함)채널 핸들(첫 세그먼트); 메시지ID(예:'7')는 라우터/fetcher 모두 사용 안 함 — 버려짐관측 1건samples/tg_channel.json 구조2 't.me/{value}/7' (sourceField='website'에서도 유입)
t.me/s/{value}HTTP 프리뷰 전용 경로(모바일 앱 우회, /s/ 접두). 라우터 classifyTelegram에서 first==='s'일 때 segments[1]을 이름으로 사용해 t.me/{value}와 동일하게 tg_channel로 정규화채널 핸들(segments[1])관측 0건표본엔 없으나 라우터 코드(social-fetcher.router.ts:133)에 명시적 분기 존재; Telegram 공식 웹 프리뷰 패턴
t.me/+{inviteHash}비공개 초대 링크(신형, '+' 접두 해시) → tg_guard_group. fetcher는 멤버 열거 불가로 무조건 status='blocked', data=null (존재 신호만)초대 해시 문자열(대소문자 구분 원형, '+' 제거 후)관측 3건samples/tg_guard_group.json 구조 3건 전부
t.me/joinchat/{inviteHash}비공개 초대 링크(구형 경로) → tg_guard_group, t.me/+{hash}와 동일하게 blocked 처리초대 해시 문자열(segments[1], 원형)관측 0건표본엔 없으나 라우터 코드(router.ts:129-131)에 명시적 분기 존재; Telegram 레거시 초대 링크 정식 포맷

2. sourceType / sourceKey 판정 👤 판단 반영

2.1 sourceType 적합도 👤 판단 반영

패턴현재 type판정/제안sourceKeyconf근거사용자 결정
t.me/{value}
classifyTelegram: first가 '+' 아니고 'joinchat' 아니면 name=first → tg_channel, sourceKey=lowercase(name)tg_channel (변경 없음)lowercase(handle)high라우터 분류와 fetcher(https://t.me/{key} GET, og:title/tgme_page_extra 파싱)가 정확히 일치. subscriberCount(mutable)·title/nameMatch(fixed) valueNature 구분도 fetcher 코드와 부합. 공개 채널 도달력(구독자수)은 context.md의 '모멘텀/하이프' 축(참여속도·도달)에 직결되는 트레이더 actionable 신호.fetcher 판정으로 세분화 — router 는 tg_channel 유지하되 fetcher 가 tg_portal(52%) / tg_shell(30%) / tg_channel(6%) 로 좁힌다. URL 만으로는 구분 불가라 telegram 만의 의도된 예외.
t.me/{value}/{messageId}
classifyTelegram: first==='s' 아니므로 name=first(첫 세그먼트)만 사용, messageId(segments[1])는 버려짐 → tg_channel, sourceKey=lowercase(첫 세그먼트)tg_channel 유지(라우팅 자체는 맞음). 단 messageId 폐기는 gap으로 별도 플래그lowercase(채널 핸들); messageId 미보존mediumsourceType 판정(채널 단위) 자체는 fetcher 능력(채널 루트 HTML만 조회 가능, 특정 게시물 view count 없음)과 일치하므로 오분류는 아님. 다만 context.md의 '타이밍/신선도: 원본 vs 재활용' 축은 게시물 단위 타임스탬프/조회수가 있어야 판별력이 생기는데 현재는 messageId가 통째로 버려져 어떤 게시물이 딥링크됐는지 복원 불가 — 카피캣/재활용 판별에 쓸 수 있는 잠재 신호가 라우팅 단계에서 소실됨. observed=1로 현재 볼륨은 낮아 즉시 조치 필요성은 낮음.기존 제안 유지 — 채널로 흡수. 실측 362건 중 1건(0.3%)이라 타입 신설 이득이 없고, t.me/s/ 채택 시 그 채널의 최근 메시지를 전부 받는다.
t.me/s/{value}
classifyTelegram: first==='s'일 때 name=segments[1] → tg_channel, sourceKey=lowercase(segments[1])tg_channel (변경 없음)lowercase(segments[1])highrouter.ts:133 코드로 명시적 분기 확인. t.me/{value}와 동일 채널 엔티티를 가리키는 프록시 경로이므로 동일 sourceType/key로 정규화하는 게 맞음(중복 fetch 방지). 표본 관측 0건이나 코드 경로는 결정적(deterministic)이라 신뢰도 high 유지.변경 없음(같은 sourceKey 로 수렴).fetcher 가 이제 이 엔드포인트를 실제로 호출한다 — 같은 1회 요청으로 가드 id·봇·생성일·카운터·메시지를 얻는다.
t.me/+{inviteHash}
classifyTelegram: first.startsWith('+') → tg_guard_group, sourceKey=first.slice(1)(원형 유지)tg_guard_group (변경 없음)초대 해시(원형, '+' 제거)highfetcher가 tg_guard_group을 무조건 status='blocked', data=null로 처리 — 라우터 분류가 fetcher의 실제 능력 한계(멤버 열거 불가)를 정확히 반영. tg_channel과 분리 유지가 맞는 이유: 동일 t.me 호스트라도 반환 가능한 데이터 형태(존재 신호 vs 구독자수+제목)가 근본적으로 다름. 트레이더 관점에서도 초대 전용=폐쇄형 커뮤니티라는 존재 자체가 러그 리스크/하이프 게이팅 tell로 tg_channel과 다른 해석이 필요해 별도 타입이 타당.변경 없음 — tg_guard_group 유지. blocked 반환. 실측 0.8%.
t.me/joinchat/{inviteHash}
classifyTelegram: first==='joinchat' && segments[1] → tg_guard_group, sourceKey=segments[1](원형)tg_guard_group (변경 없음)초대 해시(원형)highrouter.ts:129-131 코드로 명시적 분기 확인. t.me/+{hash}의 레거시 동의어이므로 동일 sourceType으로 병합 처리하는 현재 설계가 맞음. 표본 관측 0건이나 Telegram 공식 레거시 포맷이라 코드 커버리지 유지 필요성은 여전히 유효.변경 없음 — tg_guard_group 유지. 레거시 초대 형식.

2.2 id 추출 적합도

패턴추출 idresolve?판정conf근거사용자 결정
t.me/{value}segments[0] 소문자화 → tg_channel sourceKey (라우터 classifyTelegram)okhighlive-test로 https://t.me/NvidiaMoonTG GET이 HTTP 200, og:title/tgme_page_extra 정상 파싱 확인. Telegram username은 전역 고유(글로벌 네임스페이스)라 sourceKey가 실제 채널을 유일하게 가리킴. subscriberCount는 스냅샷(21)과 실측(17)이 달라 mutable 재확인 — 식별자 자체의 안정성과는 무관, id는 정확히 resolve됨.
t.me/{value}/{messageId}첫 세그먼트(채널 핸들)만 sourceKey, messageId는 버려짐okhigh라우터가 messageId를 의도적으로 무시하고 채널 단위로 접힘(tg_channel과 동일 분류) — fetcher도 채널 페이지만 조회하므로 sourceKey는 여전히 유일한 채널 식별자로 정확히 resolve됨. 다만 게시물 단위 정보(투고 시각·바이럴 확산 등)는 현재 설계상 손실되는데, live-test 보너스(t.me/s/{value})가 tgme_widget_message_date href로 t.me/{value}/{msgId} 형태·정확한 ISO datetime을 노출함을 실측 확인해, messageId가 향후 acquisitionLadder rank1(게시물 신선도)로 확장 가능한 유효 식별자임도 같이 확인됨. 현재 구현 판정은 '채널 식별자로서 ok'.
t.me/s/{value}segments[1](first==='s'일 때) 소문자화 → tg_channel sourceKey, t.me/{value}와 완전 동일 취급okhigh표본엔 0건이라 직접 관측은 없으나, 라우터 코드(router.ts:133)에 명시적 분기가 있고 live-test 보너스 타깃으로 https://t.me/s/NvidiaMoonTG를 직접 GET해 200/27.7KB 정상 페이지(같은 채널의 인스턴트뷰) 확인 — /s/ 프리픽스 제거 후 segments[1]이 t.me/{value}와 동일한 고유 채널 핸들로 정확히 resolve됨을 실측으로 검증. 참고로 fetcher가 실제 호출하는 엔드포인트는 t.me/{key}(비-/s/ 버전)이고 /s/ 버전은 라우터가 입력 URL 형태만 정규화하는 것 — 결과 sourceKey는 두 형태에서 동일.
t.me/+{inviteHash}first.slice(1)(선행 '+' 제거, 원형 보존) → tg_guard_group sourceKeyokhighlive-test id-probe로 raw HTML의 al:ios:url='tg://join?invite=df1YH6zqisBkOWYx'와 라우터가 뽑은 sourceKey('df1YH6zqisBkOWYx')가 문자 그대로 일치 — invite hash가 해당 그룹을 유일하게 가리키는 정식 식별자임을 실측 확인. 단, fetcher는 이 sourceKey로 실제 fetch를 수행하지 않고 즉시 status='blocked', data=null 반환(httpGet 자체 미호출)이므로 id는 정확하지만 현재 파이프라인에서 활용되진 않음 — 이는 id-fit이 아니라 field-necessity/구현 이슈. og:title/description이 실제로 노출되는데(예: 'BSTRD on Solana [Portal]') 파싱하지 않는 점은 별도 개선 여지로 플래그.
t.me/joinchat/{inviteHash}segments[1] 원형 → tg_guard_group sourceKey, +{hash}와 동일 취급(무조건 blocked)okmedium레거시 joinchat 포맷은 표본 0건·live-test 직접 검증 대상 아님(코드만 확인). 다만 라우터 로직이 t.me/+{hash} 케이스와 완전히 동일한 패턴(segments[1] 원형 추출 → tg_guard_group)이고, Telegram 공식 문서상 joinchat/{hash}와 +{hash}는 동일한 invite-hash 네임스페이스를 가리키는 신·구 표기라 구조적으로 같은 신뢰도를 가짐. id-probe를 이 패턴으로 직접 재현하지 않았으므로 resolves는 미확정으로 null 처리, verdict는 코드 대칭성 근거로 ok.
커버리지 갭 — t.me/{value}/{messageId}: 게시물 딥링크의 messageId가 라우팅 단계에서 완전히 버려짐(fetcher도 채널 루트 페이지만 조회). context.md의 '원본 vs 재활용' 축에 게시물 단위 타임스탬프가 필요하다면 현재 구조로는 복원 불가 — 저볼륨(observed 1)이라 당장 조치는 낮은 우선순위.
커버리지 갭 — telegram.dog 등 t.me/telegram.me의 미러/우회 도메인은 host 화이트리스트(t.me, telegram.me)에 없음 — 감사 대상 패턴 목록엔 없었으나 향후 host 커버리지 이슈로 남을 수 있음.
커버리지 갭 — t.me/s/{value}, t.me/joinchat/{value} 두 패턴은 실제 샘플 관측이 0건이라 코드 분기는 존재하나 실데이터 검증은 안 된 상태(결정론적 로직이라 위험은 낮음).

3. Fetch 테스트 + 획득 방법

3.1 획득 사다리 (공짜 → 유료)

방법비용설명트레이드오프conf
확정 ✅ (2026-07-21)사용자 선택 완료 — tg_channel→official:tg_s_preview ($0) · tg_guard_group→official:og-title-probe ($0) adopted$0Phase A 후보 사다리에서 사람이 선택한 확정 source. §4 통합 필드 뷰는 이 source의 실제 스키마 기준.신규 채택 source는 현행 코드 미구현분이 §4에서 parseStatus=dropped(구현 필요)로 표시됨. 실제 코드 전환·스키마 검증은 반영 단계에서.high
0무료 공개 HTML 프리뷰 — t.me/<name> (현재 구현, telegram.fetcher.ts) 구현됨$0 (일반 HTTP GET)라우터(social-fetcher.router.ts classifyTelegram)가 t.me/+<hash>·joinchat/<hash>는 sourceType='tg_guard_group'(초대전용, 멤버 열거 불가)로, 그 외 t.me/<name> 또는 t.me/s/<name>은 sourceType='tg_channel', sourceKey=소문자 name으로 분류. fetcher는 tg_guard_group을 fetch 시도 없이 즉시 'blocked' 리턴하고, tg_channel만 https://t.me/<sourceKey> 를 GET해 og:title 과 div.tgme_page_extra 정규식 파싱으로 title + subscriberCount(대략치, K/M 단위 파싱)만 얻는다. nameMatch(title에 토큰명 포함 여부)도 부가 생성. valueNature: title/nameMatch='fixed', subscriberCount='mutable'.공개 채널(broadcast)의 제목+구독자수 개략치만 확보. 우리가 필요로 하는 게시물 단위(작성자·게시일·참여수)는 전혀 안 가져옴. 계정 생성일·인증뱃지 필드 자체가 이 프리뷰 페이지에 없음. 초대전용 그룹은 무료/유료 어떤 경로로도(코드 주석상) 멤버 열거 자체가 차단된다고 전제.높음 — 코드 직접 확인
1무료 확장(미구현) — t.me/s/<name> 인스턴트뷰 프리뷰 미구현, 라우팅 로직은 이미 지원(같은 sourceKey로 접힘)하지만 fetcher가 이 엔드포인트를 안 씀$0공개 '채널'(broadcast, 개인/그룹 아님)에 한해 t.me/s/<channel> 은 최근 메시지 목록 HTML을 반환하며 각 메시지에 datetime(게시일), tgme_widget_message_views(조회수=참여수 proxy), 채널 서명 옵션 켠 경우 작성자명이 포함됨. 현재 라우터가 만드는 sourceKey 그대로 재사용 가능해 구현 비용 낮음.게시물의 '작성자'는 대부분 채널 자체이며 개별 유저 작성자 개념이 없음(서명 옵션 켠 채널만 예외). 그룹(비-broadcast) 채팅에는 /s/ 프리뷰가 존재하지 않아 그룹 게시물은 이 경로로 못 얻음. 계정 프로필(팔로워/생성일/인증)은 여전히 대상 밖 — 텔레그램은 애초에 '개인 프로필'을 공개 조회하는 공식 창구가 없음. 마크업 변경에 취약한 스크레이핑.중간 — 기법 자체는 잘 알려져 있으나 이 레포에 구현·검증된 코드는 없음
2공식 Telegram Bot API (getChat / getChatMemberCount) 구조적으로 우리 유즈케이스에 부적합 — 채택 비권장$0 (Bot API 자체는 무상, 문서상 별도 과금 없음)getChat/getChatMemberCount는 무료지만, 봇이 대상 채널/그룹에 사전에 멤버(대개 admin)로 추가되어 있어야만 호출 가능. 우리는 저볼륨 온디맨드로 임의 토큰의 텔레그램 채널을 외부에서 관찰하는 입장이라 각 채널 소유자가 우리 봇을 admin으로 넣어줄 것을 기대할 수 없음. Chat 객체 스키마 자체에 팔로워수 외 계정 생성일/인증뱃지 필드가 없음.구독자수를 얻는 대가로 '채널에 우리 봇을 심어야' 하는 선행조건이 우리 통제 밖 — 스케일 불가. 계정 생성일/인증 상태는 API 자체에 필드 없음.높음 (공식 문서·다수 개발자 사례로 반복 확인되는 제약)
3공식 Telegram Client API — MTProto (api_id/api_hash, my.telegram.org 무료 발급) 기술적으로 가능하나 미구현, 운영 리스크 있음$0 금전(등록 무료). 단 실제 전화번호로 로그인한 '유저 계정' 세션 필요 — SIM/번호 확보 및 flood-wait·계정정지 리스크라는 운영비용 발생contacts.resolveUsername + channels.getFullChannel 로 가입 없이 공개 채널 조회 가능(구독자수, description, verified/scam/fake 플래그 포함). messages.getHistory 로 메시지별 date/views/reactions 확보 가능해 게시물 단위 데이터까지 커버. 단 봇 계정이 아닌 사람 전화번호로 인증한 세션이 필요.자동화된 대량 조회는 텔레그램 ToS상 회색지대이며 flood-wait/계정 정지 리스크. 계정 생성일 필드는 MTProto 스키마에도 존재하지 않음 — 텔레그램이 공식적으로 이 정보를 서버에 노출한 적이 없다는 점이 다수 문서(bugs.telegram.org, OSINT 커뮤니티)에서 반복 확인됨. 즉 공식 경로로도 정확한 계정 생성일은 원천적으로 획득 불가.중간 — API 능력은 문서로 확인되나 이 레포 적용 사례·실측 안정성은 미검증
4서드파티 유료 — Apify Telegram 스크레이퍼 액터 미채택, 필요시 즉시 구매 가능액터별 상이: pay-per-event 예) 메시지 1000건+채널정보 5건 ≈ $2/batch 수준(일부는 $0.10 start + $0.001/message); 정기 사용 시 Starter $49/월 권장; 무료 플랜은 매월 $5 크레딧검색된 액터 다수: tri_angle/telegram-scraper, thescrapelab/apify-telegram-scraper, automation-lab/telegram-scraper('No API Key Needed'), sovereigntaylor/telegram-scraper('Messages, Media & Member Stats'), i-scraper/telegram-channels-scraper 등. 마케팅 문구상 채널 메타(제목/구독자수/description/verification status)와 메시지(텍스트/타임스탬프/조회수/리액션/작성자 서명)를 함께 제공한다고 주장.이런 액터들은 사실상 t.me/s/ 공개 HTML(위 rank 1과 동일 소스)을 재포장하는 경우가 유력해 보임 — 우리가 자체 구현해도 무료로 얻을 수 있는 데이터를 유상으로 아웃소싱하는 셈일 가능성이 높다(안정적 프록시/IP로테이션+파싱 유지보수 대행이 실질 가치). 'verification status'/생성일을 제공한다는 문구는 검증 안 된 벤더 마케팅 문구이며, 텔레그램이 애초 이 값을 어떤 공식 경로로도 노출하지 않으므로(위 rank 3 참고) 액터가 실제로 정확한 값을 줄 수 있는지 의심스러움 — 실사용 샘플 검증 없이는 신뢰 낮음. 그룹(초대전용) 멤버 열거를 표방하는 액터(bhansalisoft 등)도 발견되나 텔레그램 구조상 비공개 그룹 멤버 열거는 원천 차단되어 있어(현재 코드의 tg_guard_group=blocked 전제와 상충) 실효성 회의적.낮음~중간 — 액터 상세페이지/마케팅 카피 기반이며 실제 응답 샘플로 검증하지 않음. 벤더 편향 감안 필요

3.2 공식 API 상세

확신도 높음 — 가장 중요한 결론: 계정 생성일(가입일)은 무료/공식/유료를 통틀어 어떤 경로로도 정확히 얻을 수 없다 — 텔레그램이 이 값을 서버 응답 어디에도 노출하지 않는다는 점이 공식 버그트래커(bugs.telegram.org)와 다수 OSINT 커뮤니티 문서에서 일관되게 확인된다. '생성일 제공'을 주장하는 봇/서비스는 전부 유저ID 시퀀스 기반 추정치(오차 큼)이지 실측값이 아니다. 두 번째로, 공식 Bot API의 getChat/getChatMemberCount 는 무상이지만 대상 채널에 우리 봇이 사전에 멤버로 들어가 있어야 하므로 '임의 토큰의 텔레그램 채널을 외부에서 온디맨드로 관찰'하는 우리 유즈케이스에는 구조적으로 맞지 않는다. 실질적으로 쓸 수 있는 공식 경로는 (a) 이미 구현된 t.me/<name> 프리뷰, (b) 미구현이지만 무료인 t.me/s/<name> 게시물 프리뷰, (c) MTProto 유저 세션(전화번호 인증, 자동화 리스크 있음) 세 가지뿐이며 셋 다 팔로워/구독자수·게시물 date·조회수(참여수 proxy)까지는 얻을 수 있어도 인증뱃지·정확한 계정 생성일은 얻지 못한다.
단계내용conf
저비용 우선순위: t.me/s/<name> 프리뷰를 telegram.fetcher.ts에 추가 구현 (라우터 sourceKey 재사용, 신규 라우팅 불필요)게시물 date/조회수까지 무료로 확보 가능. 채널(broadcast) 한정, 그룹 미지원, 작성자는 채널 서명 옵션 켠 경우만 실명.높음
Bot API 채택은 보류/배제 권장getChatMemberCount 등은 봇이 이미 채널 멤버여야 호출 가능 — 임의 토큰 채널 사전 가입을 우리가 통제할 수 없어 스케일 불가. 계정 생성일/인증뱃지 필드 자체가 스키마에 없음.높음
MTProto(GramJS 등)로 확장은 게시물 참여수/채널 verified 플래그까지 필요할 때만 검토my.telegram.org에서 api_id/api_hash 무료 발급, 단 전화번호 로그인 유저 세션 필요 → flood-wait·계정정지·ToS 회색지대라는 운영 리스크. 계정 생성일은 이 경로로도 불가.중간
Apify 등 서드파티는 위 무료 경로로 커버 안 되는 안정성/유지보수(마크업 변화 대응, IP 로테이션)가 아쉬울 때만 pay-per-event로 국지 도입정기 구매 전 실제 응답 샘플을 받아 '구독자수/게시물 date/조회수' 정확도와 '계정 생성일/인증' 주장의 진위를 직접 검증할 것 — 벤더 마케팅 카피만으로는 신뢰 불가.낮음~중간

3.3 라이브 테스트 실측

대상방법과금결과비고
tg_channel — https://t.me/NvidiaMoonTG (fetcher가 실제 호출하는 정확한 엔드포인트, 무료 GET, User-Agent: 'Mozilla/5.0 (social-fetcher)')curl GET무료성공(HTTP 200). og:title='NVIDIAMOON Community' 파싱됨. tgme_page_extra div='17 subscribers' → parseCount()로 17 추출. isContactPage=false(title이 'Telegram: Contact'로 시작 안 함) → status='ok'. 스냅샷(docs/features/social-url-structure-audit/samples/tg_channel.json, 캡처 시점)의 subscriberCount=21과 지금(17)이 달라 mutable 필드임이 실측으로도 재확인됨.
tg_guard_group — https://t.me/+df1YH6zqisBkOWYx (id-probe: 라우터가 sourceKey로 추출하는 invite hash와 실제 페이지의 canonical invite 대조. 참고: fetcher는 이 sourceType에 대해 코드상 httpGet 자체를 호출하지 않고 즉시 blocked 반환하므로, 이 curl은 fetcher가 실제 실행하는 호출이 아니라 '차단이 타당한지' 검증용 별도 id-probe)curl -sI (redirect follow) + curl body GET무료HTTP 200, 리다이렉트 없음(초대링크가 그대로 종단). 페이지는 og:title='BSTRD on Solana [Portal]', al:ios:url='tg://join?invite=df1YH6zqisBkOWYx' 포함 — invite hash가 라우터 classifyTelegram이 뽑은 sourceKey('df1YH6zqisBkOWYx', '+' 제거분)와 정확히 일치, id-probe 통과. 단 이 페이지는 title/description을 실제로 노출하고 있음에도(위 rawResponse 참고) fetcher는 이를 파싱하지 않고 무조건 status='blocked', data=null 반환 — 코드상 의도된 설계(멤버 열거만 불가할 뿐 title 자체는 얻을 수 있는데 현재 안 씀).
보너스(acquisitionLadder rank1, 미구현) — https://t.me/s/NvidiaMoonTG 인스턴트뷰 프리뷰. 현재 라우터가 만드는 sourceKey를 그대로 재사용 가능한지, 그리고 게시물 date가 실제로 나오는지 무료로 검증curl GET무료성공(HTTP 200, 27.7KB). 메시지별 <time datetime="2026-06-18T00:52:57+00:00"> 형태로 정확한 ISO 게시일 파싱 가능함을 실측 확인 — acquisitionLadder rank1 설명이 사실과 일치. tgme_widget_message_date href가 t.me/NvidiaMoonTG/{msgId} 형태라 게시물 단위 sourceKey 확장도 가능함을 확인.
.env.dev / .env.prod에 TELEGRAM Bot API 토큰 존재 여부(rank2 Bot API 채택 검토용)grep '^TELEGRAM' .env.dev .env.prod무료매치 없음 — 키 없음(미검증). 어차피 rank2(Bot API)는 봇이 대상 채널에 사전 admin으로 들어가 있어야 호출 가능해 이 유즈케이스에 구조적으로 부적합하다는 acquisition 결론과 일치해 실호출 생략.

tg_channel — https://t.me/NvidiaMoonTG (fetcher가 실제 호출하는 정확한 엔드포인트, 무료 GET, User-Agent: 'Mozilla/5.0 (social-fetcher)') 실측 응답 원문:

<meta property="og:title" content="NVIDIAMOON Community"> ... class="tgme_page_extra">17 subscribers ... <title>Telegram: View @NvidiaMoonTG</title>

tg_guard_group — https://t.me/+df1YH6zqisBkOWYx (id-probe: 라우터가 sourceKey로 추출하는 invite hash와 실제 페이지의 canonical invite 대조. 참고: fetcher는 이 sourceType에 대해 코드상 httpGet 자체를 호출하지 않고 즉시 blocked 반환하므로, 이 curl은 fetcher가 실제 실행하는 호출이 아니라 '차단이 타당한지' 검증용 별도 id-probe) 실측 응답 원문:

<title>Telegram: Join Group Chat</title> <meta property="og:title" content="BSTRD on Solana [Portal]"> <meta property="og:description" content="SafeGuard Portal for BSTRD"> <meta property="al:ios:url" content="tg://join?invite=df1YH6zqisBkOWYx">

보너스(acquisitionLadder rank1, 미구현) — https://t.me/s/NvidiaMoonTG 인스턴트뷰 프리뷰. 현재 라우터가 만드는 sourceKey를 그대로 재사용 가능한지, 그리고 게시물 date가 실제로 나오는지 무료로 검증 실측 응답 원문:

<title>NVIDIAMOON Community – Telegram</title> ... tgme_widget_message_date" href="https://t.me/NvidiaMoonTG/1"><time datetime="2026-06-18T00:52:57+00:00">
tg_channel 라이브 호출은 실제 코드 경로(httpGet https://t.me/<sourceKey>)와 100% 동일하게 성공 확인. og:title/tgme_page_extra 파싱 로직이 실데이터와 정확히 매치.
subscriberCount가 스냅샷(21, 과거 캡처)과 라이브(17)에서 달라 valueNature='mutable' 태깅이 실측으로 검증됨 — subscriberCount는 시계열로 변하는 값이라 캐시/스냅샷 시점 명시 필요.
tg_guard_group은 코드가 httpGet을 아예 호출하지 않고 즉시 blocked를 반환하지만, id-probe 결과 실제로 og:title/og:description은 초대링크 페이지에도 노출되어 있음(예: 'BSTRD on Solana [Portal]', 'SafeGuard Portal for BSTRD'). 현재 설계는 '멤버 열거 불가'를 이유로 title조차 안 가져오는데, 이는 acquisition 문서가 말한 '멤버 열거만 차단'과는 별개로 title/description 정도는 무료로 더 얻을 수 있는 여지가 있다는 뜻 — 단, 이 태스크 범위는 실측 보고이며 코드 변경 여부는 별도 판단 필요.
acquisitionLadder rank1(t.me/s/<name> 인스턴트뷰)이 실제로 게시물별 정확한 ISO datetime을 제공함을 라이브로 확인 — 문서 주장이 사실과 일치.
TELEGRAM Bot API 토큰은 .env.dev/.env.prod 어디에도 없음 — rank2는 어차피 구조적 부적합 결론과 일치해 미검증 상태 유지가 타당.
무료 경로(tg_channel 실호출 + tg_guard_group id-probe)만으로 두 sourceType 모두 검증 가능했고 유료 Apify 호출은 불필요했음(paidCallsUsed=0).

4. 객체별 필드 통합 (변동성 · 파싱상태 · 트레이딩 유용성) 👤 판단 반영

Creator/Content 객체별로 필드를 한 표에 통합. 값 변동성 = 값이 시간에 따라 변하나(immutable 역사적 사실 / mutable 드리프트 / derivable 계산값) → look-ahead 안전성이 여기서 도출(immutable=safe · mutable=conditional/as-of · derivable=unsafe). 파싱 상태 = 현재 파이프라인이 실제로 저장하나.

4.1 · Venue — 가드 포털 (tg_portal) — telegram (portal channel)

source: official:tg_s_preview — 무료 t.me/s/{name} 인스턴트뷰 HTML (공식 API 아님, 정규식 파싱). 아직 미구현 — 현재 fetcher 는 t.me/{name} 을 받아 title/subscriberCount 만 뽑는다. 판정: 본문에 t.me/{bot}?start=-100… 이 있으면 포털. · 🔴 실측 표본의 52% 로 가장 흔한데 기존 문서엔 이 객체가 없었다. mutable 값은 fetch 시점 스냅샷 — 진입시점과 다를 수 있어 as-of 주의. 실측 aegisprivacy 는 두 관측 사이 title AEGIS Portalaegisprivacy, subs 49→4 로 실제로 바뀌었다. 관측 시각을 함께 저장해야 한다.
필드설명값 변동성파싱 상태(현재)최종 결정트레이딩 유용성
usernameURL 의 t.me/{name} — router sourceKey. 🔴 가드가 없을 때의 폴백 조인 키인데, 다른 6개 소셜과 달리 immutable 이 아니다. 소유자가 바꿀 수 있고 놓으면 타인이 가져간다. 실측 이관 2건(seelprotocol·flybubbleman — 6월 토큰이 링크했는데 채널 생성일이 7월).mutable✅ 캡처🔑 keycontext — 표시·라우팅용. 가드가 있으면 guardChatId 를 우선하고, 없을 때만 이걸 키로 쓴다(가변임을 전제).
guardChatId🆕 조인 키. verify 링크의 -100…. 포털 이름을 갈아끼워도 불변. ⚠️ 채널/슈퍼그룹 구분 불가(같은 ID 풀). ⚠️ 실제 입장 대상인지는 미확인.immutable⚠️ actor제공·미파싱🔑 keyactionable — 같은 방을 재사용한 토큰 묶기. 역방향 조회는 불가(access_hash 필요).
guardBot🆕 verify 봇 username. 실측 포털 36개 → 고유 4종이라 차원 테이블로 캐시하면 fetch 비용 0.mutable⚠️ actor제공·미파싱keepactionable — 화이트리스트 대조로 사칭 판정(Collab.Land 계열 24건=67% 사칭 확정). ⚠️ @safeguard 12건(33%)은 공식 목록 부재로 판정 불가.
guardSetupLagSec🆕 channelCreatedAt → 가드 설정 메시지 간격. 실측 2초 / 9초 / 76초 / 161초 / 10분 — 짧을수록 자동화 스크립트.immutable⚠️ actor제공·미파싱keepcontext — 셋업 자동화 정도. 미검증.
title채널 표시명. 소유자가 언제든 바꾼다 — 현재 코드가 valueNature 를 fixed 로 표기한 건 버그(look-ahead 안전을 거짓 보장).mutable✅ 캡처keepcontext — 토큰명 대조(nameMatch)용. 조인·dedup 금지.
descriptionog:description = 채널 bio. 🔴 현재 버리고 있다 — 코드에 “og:description 은 채널 bio”라는 주석까지 달아놓고 안 담는다. 실측 빈껍데기 6개 중 5개는 이것 말고 정보가 없다.mutable⚠️ actor제공·미파싱keepactionable — 프로젝트 설명·티커가 여기 담긴다(실측 AlbemarlePortalWALO 🇺🇸).
subscriberCount구독자 수. ⚠️ 포털이면 커뮤니티가 아니라 포털 구독자다(실측 중앙 7명 vs 활성 채널 113명). sourceType 과 함께 해석해야 한다.mutable✅ 캡처keepcontext — 절대값은 포털/채널에 따라 의미가 다르다. 규모 필터로 쓰면 안 된다.
previewAvailable카운터가 실제로 렌더됐는지(bool). null 이 “못 받음”인지 “0명”인지 가른다. 🆕 결측 자체가 신호 — 실측 결측군 rug 70.9% vs 값 있는 군 55.8%.mutable⚠️ actor제공·미파싱keepactionable — 결측이 저품질 tell.
channelCreatedAt🆕 메시지 #1Channel created 타임스탬프 = 채널 생성 시각(초 단위). 실측 커버 91%(메시지 보이는 46개 중 42개). ⚠️ 계정 생성일과 다른 축 — 계정 생성일은 레포 판정대로 어떤 경로로도 불가.immutable⚠️ actor제공·미파싱keepactionable — 재사용·이관 탐지의 기준선. 토큰 발행일보다 뒤면 username 이관이다.
firstMessageAt / lastMessageAt프리뷰에 보이는 가장 이른/늦은 메시지 시각. #1 이 안 보이는 채널(9%)에서는 channelCreatedAt 대신 활동 개시 하한으로 쓴다.immutable⚠️ actor제공·미파싱keepcontext — 방치 여부.
contentMessageCount🆕 서비스 메시지(Channel created·Channel photo updated)를 제외한 실 메시지 수. 빈껍데기 판별의 근거 — 이걸 안 세면 Channel created 하나뿐인 채널이 활성으로 잡힌다.mutable⚠️ actor제공·미파싱keepactionable — 0 이면 껍데기.
photoCount · videoCount · linkCount🆕 채널 카운터. t.me/{name} 에는 없고 t.me/s/ 에만 있다.mutable⚠️ actor제공·미파싱keepcontext — 실체 있는 채널인지 보조 판정.
nameMatchtitle 에 토큰명이 포함되는지. title(가변) × tokenName 계산값 — 현재 코드가 fixed 로 표기한 건 버그(derivable 이 맞다).derivable✅ 캡처keepcontext — 원본 두 값을 저장하면 질의 시점에 재계산 가능.
accountCreatedDate계정 가입일. 무료/공식/유료 어떤 경로로도 불가 — 텔레그램이 서버 응답 어디에도 노출하지 않는다(공식 버그트래커 + OSINT 문서 교차 확인). '제공'을 주장하는 서비스는 전부 ID 시퀀스 기반 추정치다. ⚠️ 위 channelCreatedAt(채널 생성일)과 혼동 금지.immutable❌ 없음skip
verifiedBadge채널 인증 뱃지. 무료 HTML 경로엔 없다. MTProto 유저 세션이면 얻지만 전화번호·계정정지 리스크.mutable❌ 없음skip

4.2 · Venue — 활성 채널 (tg_channel) — telegram (broadcast channel)

source: official:tg_s_preview — 무료 t.me/s/{name} 인스턴트뷰 HTML (공식 API 아님, 정규식 파싱). 아직 미구현 — 현재 fetcher 는 t.me/{name} 을 받아 title/subscriberCount 만 뽑는다. 판정: 가드 링크가 없고 실 메시지가 1건 이상. · ⚠️ 실측 표본의 6%(4개) 뿐이다. 기존 문서는 이 타입 하나로 포털·빈껍데기까지 전부 덮고 있었고, 아래 메시지 필드들이 전체에 해당하는 것처럼 보였다. mutable 값은 fetch 시점 스냅샷 — 진입시점과 다를 수 있어 as-of 주의. 실측 aegisprivacy 는 두 관측 사이 title AEGIS Portalaegisprivacy, subs 49→4 로 실제로 바뀌었다. 관측 시각을 함께 저장해야 한다.
필드설명값 변동성파싱 상태(현재)최종 결정트레이딩 유용성
usernameURL 의 t.me/{name} — router sourceKey. 🔴 가드가 없을 때의 폴백 조인 키인데, 다른 6개 소셜과 달리 immutable 이 아니다. 소유자가 바꿀 수 있고 놓으면 타인이 가져간다. 실측 이관 2건(seelprotocol·flybubbleman — 6월 토큰이 링크했는데 채널 생성일이 7월).mutable✅ 캡처🔑 keycontext — 표시·라우팅용. 가드가 있으면 guardChatId 를 우선하고, 없을 때만 이걸 키로 쓴다(가변임을 전제).
title채널 표시명. 소유자가 언제든 바꾼다 — 현재 코드가 valueNature 를 fixed 로 표기한 건 버그(look-ahead 안전을 거짓 보장).mutable✅ 캡처keepcontext — 토큰명 대조(nameMatch)용. 조인·dedup 금지.
descriptionog:description = 채널 bio. 🔴 현재 버리고 있다 — 코드에 “og:description 은 채널 bio”라는 주석까지 달아놓고 안 담는다. 실측 빈껍데기 6개 중 5개는 이것 말고 정보가 없다.mutable⚠️ actor제공·미파싱keepactionable — 프로젝트 설명·티커가 여기 담긴다(실측 AlbemarlePortalWALO 🇺🇸).
subscriberCount구독자 수. ⚠️ 포털이면 커뮤니티가 아니라 포털 구독자다(실측 중앙 7명 vs 활성 채널 113명). sourceType 과 함께 해석해야 한다.mutable✅ 캡처keepcontext — 절대값은 포털/채널에 따라 의미가 다르다. 규모 필터로 쓰면 안 된다.
previewAvailable카운터가 실제로 렌더됐는지(bool). null 이 “못 받음”인지 “0명”인지 가른다. 🆕 결측 자체가 신호 — 실측 결측군 rug 70.9% vs 값 있는 군 55.8%.mutable⚠️ actor제공·미파싱keepactionable — 결측이 저품질 tell.
channelCreatedAt🆕 메시지 #1Channel created 타임스탬프 = 채널 생성 시각(초 단위). 실측 커버 91%(메시지 보이는 46개 중 42개). ⚠️ 계정 생성일과 다른 축 — 계정 생성일은 레포 판정대로 어떤 경로로도 불가.immutable⚠️ actor제공·미파싱keepactionable — 재사용·이관 탐지의 기준선. 토큰 발행일보다 뒤면 username 이관이다.
firstMessageAt / lastMessageAt프리뷰에 보이는 가장 이른/늦은 메시지 시각. #1 이 안 보이는 채널(9%)에서는 channelCreatedAt 대신 활동 개시 하한으로 쓴다.immutable⚠️ actor제공·미파싱keepcontext — 방치 여부.
contentMessageCount🆕 서비스 메시지(Channel created·Channel photo updated)를 제외한 실 메시지 수. 빈껍데기 판별의 근거 — 이걸 안 세면 Channel created 하나뿐인 채널이 활성으로 잡힌다.mutable⚠️ actor제공·미파싱keepactionable — 0 이면 껍데기.
photoCount · videoCount · linkCount🆕 채널 카운터. t.me/{name} 에는 없고 t.me/s/ 에만 있다.mutable⚠️ actor제공·미파싱keepcontext — 실체 있는 채널인지 보조 판정.
nameMatchtitle 에 토큰명이 포함되는지. title(가변) × tokenName 계산값 — 현재 코드가 fixed 로 표기한 건 버그(derivable 이 맞다).derivable✅ 캡처keepcontext — 원본 두 값을 저장하면 질의 시점에 재계산 가능.
accountCreatedDate계정 가입일. 무료/공식/유료 어떤 경로로도 불가 — 텔레그램이 서버 응답 어디에도 노출하지 않는다(공식 버그트래커 + OSINT 문서 교차 확인). '제공'을 주장하는 서비스는 전부 ID 시퀀스 기반 추정치다. ⚠️ 위 channelCreatedAt(채널 생성일)과 혼동 금지.immutable❌ 없음skip
verifiedBadge채널 인증 뱃지. 무료 HTML 경로엔 없다. MTProto 유저 세션이면 얻지만 전화번호·계정정지 리스크.mutable❌ 없음skip
messageDate게시물 시각(<time datetime>). 활성 채널에서만 의미 있다.immutable⚠️ actor제공·미파싱keepcontext
messagePermalinkdata-post="{channel}/{id}"t.me/{channel}/{id}. 채널명이 바뀌면 깨진다 — 안정 키가 아니다.immutable⚠️ actor제공·미파싱keepcontext
messageViewCount조회수. ⚠️ 실측 3건 전부 조회수 > 구독자(4.7~14.1배) — 포털은 구독하지 않고 지나가는 통로라 조회수가 실제 유입에 더 가까울 수 있다(3건은 표본이 아니다).mutable⚠️ actor제공·미파싱keepactionable — 구독자수보다 나은 유입 proxy 후보.
messageIdGap🆕 max(messageId) − 보이는 메시지 수 = 삭제 건수. 실측 AlbemarlePortal#2 → #4(#3 삭제).mutable⚠️ actor제공·미파싱keepcontext — 지운 흔적. 미검증.
externalLinks🆕 메시지 본문의 외부 링크. 실측 hyperbullsolhyperbullsol.com · x.com/hyperbullsol. 채널명과 도메인·핸들이 일치하면 자기 프로젝트 방증. website 의 discoveredLinks남의 계정을 긁어와 폐기된 것과 방향이 반대다.immutable⚠️ actor제공·미파싱keepactionable — 소셜 필드에 없는 자기 링크 발견. 미검증.
messageText본문. 티커·CA 가 여기 있다(실측 $HBULL·$ANSEM). Venue 확장만 채택했으므로 Content 레코드는 만들지 않고 링크·티커 추출용으로만 파싱한다.immutable⚠️ actor제공·미파싱keepactionable
authorSignature채널 서명(옵션 켠 경우만). 실측 표본에서 관측되지 않았다.mutable⚠️ actor제공·미파싱skipnoise

4.3 · Venue — 빈껍데기 (tg_shell) — telegram (empty channel / group)

source: official:tg_s_preview — 무료 t.me/s/{name} 인스턴트뷰 HTML (공식 API 아님, 정규식 파싱). 아직 미구현 — 현재 fetcher 는 t.me/{name} 을 받아 title/subscriberCount 만 뽑는다. 판정: 실 메시지 0건(서비스 메시지만). · 실측 30%. 설명만 있거나, 그룹이라 t.me/s/ 프리뷰 자체가 없는 경우다(t.me/s/ 는 broadcast 채널 전용). 실측 kotonmaster 는 빈 채널인데 토큰 10개가 커뮤니티로 걸었다.
필드설명값 변동성파싱 상태(현재)최종 결정트레이딩 유용성
usernameURL 의 t.me/{name} — router sourceKey. 🔴 가드가 없을 때의 폴백 조인 키인데, 다른 6개 소셜과 달리 immutable 이 아니다. 소유자가 바꿀 수 있고 놓으면 타인이 가져간다. 실측 이관 2건(seelprotocol·flybubbleman — 6월 토큰이 링크했는데 채널 생성일이 7월).mutable✅ 캡처🔑 keycontext — 표시·라우팅용. 가드가 있으면 guardChatId 를 우선하고, 없을 때만 이걸 키로 쓴다(가변임을 전제).
title채널 표시명. 소유자가 언제든 바꾼다 — 현재 코드가 valueNature 를 fixed 로 표기한 건 버그(look-ahead 안전을 거짓 보장).mutable✅ 캡처keepcontext — 토큰명 대조(nameMatch)용. 조인·dedup 금지.
descriptionog:description = 채널 bio. 🔴 현재 버리고 있다 — 코드에 “og:description 은 채널 bio”라는 주석까지 달아놓고 안 담는다. 실측 빈껍데기 6개 중 5개는 이것 말고 정보가 없다.mutable⚠️ actor제공·미파싱keepactionable — 프로젝트 설명·티커가 여기 담긴다(실측 AlbemarlePortalWALO 🇺🇸).
subscriberCount구독자 수. ⚠️ 포털이면 커뮤니티가 아니라 포털 구독자다(실측 중앙 7명 vs 활성 채널 113명). sourceType 과 함께 해석해야 한다.mutable✅ 캡처keepcontext — 절대값은 포털/채널에 따라 의미가 다르다. 규모 필터로 쓰면 안 된다.
previewAvailable카운터가 실제로 렌더됐는지(bool). null 이 “못 받음”인지 “0명”인지 가른다. 🆕 결측 자체가 신호 — 실측 결측군 rug 70.9% vs 값 있는 군 55.8%.mutable⚠️ actor제공·미파싱keepactionable — 결측이 저품질 tell.
channelCreatedAt🆕 메시지 #1Channel created 타임스탬프 = 채널 생성 시각(초 단위). 실측 커버 91%(메시지 보이는 46개 중 42개). ⚠️ 계정 생성일과 다른 축 — 계정 생성일은 레포 판정대로 어떤 경로로도 불가.immutable⚠️ actor제공·미파싱keepactionable — 재사용·이관 탐지의 기준선. 토큰 발행일보다 뒤면 username 이관이다.
firstMessageAt / lastMessageAt프리뷰에 보이는 가장 이른/늦은 메시지 시각. #1 이 안 보이는 채널(9%)에서는 channelCreatedAt 대신 활동 개시 하한으로 쓴다.immutable⚠️ actor제공·미파싱keepcontext — 방치 여부.
contentMessageCount🆕 서비스 메시지(Channel created·Channel photo updated)를 제외한 실 메시지 수. 빈껍데기 판별의 근거 — 이걸 안 세면 Channel created 하나뿐인 채널이 활성으로 잡힌다.mutable⚠️ actor제공·미파싱keepactionable — 0 이면 껍데기.
photoCount · videoCount · linkCount🆕 채널 카운터. t.me/{name} 에는 없고 t.me/s/ 에만 있다.mutable⚠️ actor제공·미파싱keepcontext — 실체 있는 채널인지 보조 판정.
nameMatchtitle 에 토큰명이 포함되는지. title(가변) × tokenName 계산값 — 현재 코드가 fixed 로 표기한 건 버그(derivable 이 맞다).derivable✅ 캡처keepcontext — 원본 두 값을 저장하면 질의 시점에 재계산 가능.
accountCreatedDate계정 가입일. 무료/공식/유료 어떤 경로로도 불가 — 텔레그램이 서버 응답 어디에도 노출하지 않는다(공식 버그트래커 + OSINT 문서 교차 확인). '제공'을 주장하는 서비스는 전부 ID 시퀀스 기반 추정치다. ⚠️ 위 channelCreatedAt(채널 생성일)과 혼동 금지.immutable❌ 없음skip
verifiedBadge채널 인증 뱃지. 무료 HTML 경로엔 없다. MTProto 유저 세션이면 얻지만 전화번호·계정정지 리스크.mutable❌ 없음skip

4.4 · Venue — 초대전용 가드그룹 (tg_guard_group) — telegram (invite-only)

source: official:og-title-probe — t.me/+{hash} · t.me/joinchat/{hash}. fetcher 가 blocked 을 반환하고 data 는 null 이다. · 실측 362건 중 3건(0.8%). 무료·유료 어떤 경로로도 멤버 열거가 불가하다.
필드설명값 변동성파싱 상태(현재)최종 결정트레이딩 유용성
inviteHashURL 의 해시. 관리자가 폐기·재발급할 수 있어 방 정체성이 아니다 → 조인 키로 쓰지 않는다. router sourceKey 로만 남는다.immutable✅ 캡처keepcontext — 존재 신호.
gateType🔴 문서 오류 정정 — 기존 표는 captured 로 적혀 있었으나 fetcher 가 blocked 을 반환하며 data=null 이라 아무것도 안 담는다. sourceType 으로 이미 표현되므로 별도 필드가 불필요하다.immutable⚠️ actor제공·미파싱remove
title · description가드 뒤라 얻을 수 없다.mutable❌ 없음skip
memberCount얻을 수 없다. Bot API 는 봇이 멤버여야 하고, MTProto 는 access_hash 가 없다.mutable❌ 없음skip

4-부록. actor raw 응답 스키마 (ground truth)

위 통합 표의 원천 — actor가 실제 반환하는 원본 필드 전체.

dataType: "tg_channel (status='ok')"

필드예시값설명
title"NVIDIAMOON Community"og:title 파싱, valueNature='fixed'
subscriberCount17tgme_page_extra 정규식 파싱 후 K/M 단위 변환, valueNature='mutable'(실측 17→스냅샷 21로 변동 확인)
nameMatchtruetitle에 tokenName 포함 여부(대소문자 무시), valueNature='fixed' 표기이나 title이 바뀌면 값도 바뀔 수 있어 실질 derived

dataType: "tg_guard_group (status='blocked', 코드상 fetch 자체를 안 함)"

필드예시값설명
datanull항상 null — httpGet 미호출로 fetch 안 됨(의도된 설계)
valueNature{}항상 빈 객체 {}

dataType: "tg_s_preview (미구현, 라이브 probe로만 확인)"

필드예시값설명
messageDate (post 단위)"2026-06-18T00:52:57+00:00"tgme_widget_message_date 내 <time datetime> ISO8601, 게시물별
messagePermalink"https://t.me/NvidiaMoonTG/1"href=t.me/{channel}/{msgId} — 게시물 개별 sourceKey 확장 가능

다음 액션