홈으로 돌아가기

IT를 위한 Telegram to Obsidian 변환기

변환기는 Telegram JSON 내보내기를 Obsidian으로 전송하며 미디어, 포맷팅 및 채팅 구조를 보존합니다. 파일 인덱싱, 다단계 검색 및 엔티티 처리가 구현되었습니다. 수천 개의 메시지로부터 오프라인 데이터베이스 생성에 적합합니다.

Obsidian으로 Telegram 채팅 가져오기: 코드와 스키마
Advertisement 728x90

텔레그램 채팅을 옵시디언으로 자동 변환하는 방법: 미디어 및 링크 처리 포함

이 변환기는 텔레그램 데스크톱의 JSON 내보내기 파일을 체계적으로 정리된 옵시디언 볼트로 전환합니다. 메시지를 분석하고, 미디어 파일을 복사하며, 텔레그램 형식을 깔끔한 마크다운으로 변환하고, 채팅 유형별로(연락처, 그룹, 채널) 구조화합니다. 이 솔루션은 신뢰성 높은 경로 해결을 위한 견고한 파일 인덱스를 구축하며, 중복된 파일명 충돌도 자연스럽게 처리합니다.

원본 텔레그램 내보내기에는 result.json(메타데이터), HTML 기반 chats/ 폴더(메시지 포함), 그리고 관련 미디어 파일이 포함됩니다. 목표 폴더 구조는 Telegram_Export/이며, 하위에 Contacts/, Groups/, Channels/ 폴더와 중앙 Index.md 파일이 위치합니다.

변환기 아키텍처

시스템은 다섯 가지 핵심 모듈로 구성됩니다: JSON 파서, 미디어 인덱서, 마크다운 변환기, 파일 매니저, 볼트 구조 생성기. 데이터 흐름은 다음과 같습니다: JSON → 파싱 → 인덱싱 → 변환 → 미디어가 노트 폴더에 복사된 옵시디언 볼트.

Google AdInline article slot

주요 구성 요소:

  • 파서: result.json에서 채팅, 메시지, 메타데이터를 추출합니다.
  • 인덱서: .jpg, .mp4, .pdf 등 확장자별로 미디어 파일의 검색 가능한 맵을 구축합니다.
  • 변환기: 다중 전략 미디어 조회 및 풍부한 텍스트 서식 규칙을 적용합니다.
  • 파일 매니저: 중복 이름 제거와 함께 자산을 해당 노트 폴더로 복사합니다.

설정은 환경 변수로 관리됩니다: TELEGRAM_JSON_FILE, OBSIDIAN_OUTPUT_DIR, COPY_MEDIA.

import os
from pathlib import Path

JSON_FILE = os.getenv('TELEGRAM_JSON_FILE', 'result.json')
EXPORT_BASE = Path(os.getenv('TELEGRAM_EXPORT_BASE', '.'))
OUTPUT_DIR = Path(os.getenv('OBSIDIAN_OUTPUT_DIR', 'Telegram_Export'))
COPY_MEDIA = os.getenv('COPY_MEDIA', 'true').lower() == 'true'
GROUP_BY_DAY = os.getenv('GROUP_BY_DAY', 'true').lower() == 'true'

미디어 인덱싱 및 조회

patch.txt 파일은 ls -R 명령어로 생성되며, 텔레그램 내보내기 전체 디렉터리 트리를 기록합니다. 인덱서는 파일명, 상대 경로, 절대 경로를 모두 매핑해 빠르고 결정론적인 조회를 가능하게 합니다.

Google AdInline article slot
def index_media_from_patch(self):
    media_extensions = {'.jpg', '.jpeg', '.png', '.gif', '.webp', '.mp4', '.webm', '.pdf', '.zip', '.mp3'}
    current_dir = None
    
    with open(PATCH_FILE, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if line.endswith(':'):
                current_dir = line[:-1]
                continue
            if current_dir and any(line.endswith(ext) for ext in media_extensions):
                full_path = Path(current_dir) / line
                self.media_index[line] = full_path
                if 'chats/' in str(full_path):
                    rel_path = str(full_path).split('chats/', 1)[-1]
                    self.media_index[rel_path] = full_path

미디어 조회는 네 가지 대체 전략을 사용합니다: 정확한 파일명 일치, 전체 경로 일치, 부분 경로 일치, EXPORT_BASE 기준 직접 상대 경로 해석.

충돌 처리를 통한 미디어 복사

파일은 공유 첨부 폴더가 아닌 각 노트 폴더 내부로 복사됩니다. 이를 통해 옵시디언의 기본 임베드 문법(![[file]])이 즉시 작동합니다. media_cache는 반복 조회를 가속화합니다. 이름 충돌 시 _1, _2와 같은 접미사가 자동으로 추가됩니다.

def copy_media_file(self, source_path: str, note_folder: Path = None) -> Optional[str]:
    if not COPY_MEDIA or not source_path:
        return None
    
    source_file = self.find_media_file(source_path)
    if not source_file or not source_file.exists():
        return None
    
    target_dir = note_folder if note_folder else OUTPUT_DIR / "Attachments"
    target_dir.mkdir(parents=True, exist_ok=True)
    target_file = target_dir / source_file.name
    
    if target_file.exists():
        stem = target_file.stem
        suffix = target_file.suffix
        counter = 1
        while target_file.exists():
            target_file = target_dir / f"{stem}_{counter}{suffix}"
            counter += 1
    
    shutil.copy2(source_file, target_file)
    self.stats['media_files'] += 1
    return source_file.name

텍스트 및 엔티티 파싱

HTML과 텔레그램 전용 엔티티(볼드, 링크, 스포일러 등)는 의미 있는 마크다운으로 변환됩니다. 전용 핸들러가 의미 보존과 옵시디언 호환성을 보장합니다.

Google AdInline article slot
def parse_text_entities(self, text: Union[str, List], entities: Optional[List[Dict]] = None) -> str:
    if isinstance(text, str) and ('<' in text or '&' in text):
        return self.html_to_markdown(text)
    
    if entities and isinstance(text, str):
        return self._process_entities(text, entities)
    
    handlers = {
        'bold': lambda t: f"**{t}**",
        'italic': lambda t: f"*{t}*",
        'code': lambda t: f"`{t}`",
        'pre': lambda t: f"""
{t}
""",
        'link': lambda t, e: f"[{t}]({e.get('url', '')})",
        'spoiler': lambda t: f"\n> [!spoiler] {t}\n"
    }
    return str(text) if text else ""

메시지 서식 논리

각 메시지는 타임스탬프, 발신자, 서식화된 텍스트(엔티티 포함), 내장 미디어 참조를 포함합니다. photo, video, audio와 같은 키는 정확한 미디어 추출을 유발합니다.

처리 단계:

  • datefrom 필드 추출.
  • text_entities 로직을 사용해 text 파싱.
  • 미디어 키 감지 → 파일 복사 → ![alt](path) 또는 ![[file]] 링크 삽입.
  • GROUP_BY_DAY 활성화 시 날짜별 메시지 그룹화.

내장 통계 기능은 처리된 채팅, 메시지, 미디어 파일, 연락처 수를 실시간 추적합니다.

주요 설계 원칙

  • patch.txt 인덱싱은 JSON 경로와 실제 파일 시스템 구조 간 불일치를 해소합니다.
  • 4단계 미디어 조회 전략으로 자산 해결률 95% 이상 달성.
  • 노트별 미디어 복사는 옵시디언 임베드 기능의 완벽한 무결성을 보장합니다.
  • 텔레그램의 풍부한 텍스트 기능(볼드, 코드 블록, 링크, 멘션, 스포일러)을 완전히 지원합니다.
  • 환경 변수 기반 설정으로 프로덕션 배포 및 CI/CD 통합이 용이합니다.

— Editorial Team

Advertisement 728x90

다음 읽기