Skip to content

Telegram Integration with Cross-Provider Session Sharing#22

Closed
arnemoor wants to merge 12 commits intoopenclaw:mainfrom
arnemoor:feat/telegram-integration
Closed

Telegram Integration with Cross-Provider Session Sharing#22
arnemoor wants to merge 12 commits intoopenclaw:mainfrom
arnemoor:feat/telegram-integration

Conversation

@arnemoor
Copy link

@arnemoor arnemoor commented Dec 6, 2025

Overview

Adds native Telegram as a messaging provider alongside the existing Twilio/WhatsApp support, with the ability to share Claude sessions across providers.

Key Changes

Telegram Provider (src/telegram/)

  • Full Telegram client using GramJS (MTProto)
  • Login flow with phone/2FA support
  • Message monitoring, sending, and media download
  • Phone number normalization to E.164 format

Multi-Provider Relay (src/cli/multi-relay.ts)

  • Run multiple providers concurrently (--providers twilio,telegram)
  • Unified message handling across providers
  • Aligned console logging for multi-provider mode

CLI Updates

  • warelay relay --providers telegram or --providers twilio,telegram
  • warelay identity command for managing cross-provider mappings
  • Telegram-specific options: --telegram-session, --telegram-allow-from

Cross-Provider Session Sharing (detailed)

The session sharing architecture allows a user to seamlessly continue their Claude conversation when switching between WhatsApp and Telegram.

Identity Mapping System (src/identity/)

// ~/.clawdis/identity-map.json

{
"version": 1,
"mappings": {
    "arne": {
    "id": "arne",
    "name": "Arne Moor",
    "identities": {
        "whatsapp": "+41791234567",
        "telegram": "123456789"
    },
    "createdAt": "...",
    "updatedAt": "..."
    }
}
}

How it works:

  1. findMappingByIdentity(provider, rawId) - Looks up if a provider identity (e.g., WhatsApp phone or Telegram user ID) is mapped to a shared identity
  2. normalizeSessionId(provider, rawId) - Called from deriveSessionKey() in sessions.ts:
  • If a mapping exists → returns the shared id (e.g., "arne")
  • If no mapping → returns provider-specific format (telegram:123456789 or +41791234567)
  1. Session storage - Sessions are keyed by the normalized ID in ~/.clawdis/sessions.json:
    {
    "arne": { "sessionId": "abc123", "updatedAt": 1234567890 }
    }

Result: Messages from both +41791234567 (WhatsApp) and 123456789 (Telegram) resolve to the same session key "arne", sharing Claude conversation state.

Management via CLI:
warelay identity add arne --whatsapp "+41791234567" --telegram "123456789"
warelay identity list
warelay identity remove arne

Add Telegram as a third messaging provider alongside web and twilio.

Core Features:
- Interactive login flow with phone/SMS/2FA authentication
- Send text and media messages (images, videos, audio, documents)
- Monitor incoming messages with auto-reply support
- Session management at ~/.clawdis/telegram/session/
- Full CLI integration (login, logout, status, send, relay commands)

Implementation Details:
- Uses telegram npm package for MTProto API access
- Supports both URL and local file media sending
- Cross-platform path handling (Windows/Unix)
- Optional Twilio env vars (supports Telegram-only usage)
- Minimal provider abstraction pattern
- Comprehensive test coverage (440 tests passing)

Changes:
- Add Telegram module (client, login, monitor, inbound, outbound, session)
- Add provider factory and base interfaces
- Wire Telegram functions into CLI deps
- Update env validation to make Twilio fields optional
- Add telegram to all CLI commands (login, logout, status, send, relay)
- Add null checks in Twilio code for optional env fields
- Fix send command to properly load session and connect
- Add local file support with cross-platform path handling
- Update login message to show correct ~/.clawdis path
- Add comprehensive tests and documentation

Basic Usage:
  warelay login --provider telegram
  warelay send --provider telegram --to "@user" --message "Hi"
  warelay send --provider telegram --to "@user" --media "/path/to/file.jpg"
  warelay relay --provider telegram

All tests pass (63 files, 440 tests). Zero TypeScript errors.
Implements identity mapping to allow linking WhatsApp, Telegram, and Twilio
identities for shared Claude conversation sessions across providers.

Core Features:
- Identity mapping storage in ~/.clawdis/identity-map.json
- Session ID normalization for unified sessions
- CLI commands for managing identity mappings
- Full backwards compatibility (opt-in feature)

New Identity Module (src/identity/):
- types.ts: Type definitions for identity mappings
- storage.ts: CRUD operations for identity persistence
- normalize.ts: Session ID normalization logic
- Comprehensive test coverage (29 tests passing)

CLI Commands (src/commands/identity.ts):
- identity link: Link multiple provider identities
- identity list: Show all identity mappings
- identity show: Display specific mapping details
- identity unlink: Remove identity mapping
- Input validation for E.164 phone numbers and Telegram usernames
- JSON output support for list/show commands

Session Integration:
- Made deriveSessionKey() async to support identity lookups
- Updated all callers: auto-reply, agent command, web provider
- Group conversations excluded from identity mapping
- Provider detection based on ID format

Documentation:
- docs/session-sharing.md: Comprehensive user documentation
- Architecture overview and use cases
- CLI usage examples and troubleshooting guide

Test Coverage:
- src/identity/normalize.test.ts: 11 tests for normalization
- src/identity/storage.test.ts: 18 tests for storage operations
- 100% coverage of identity module functionality

Files Changed:
- 10 new files (identity module, CLI, docs, tests)
- 5 modified files (sessions, CLI integration, auto-reply)

Build Status:
- All tests passing (29/29)
- Zero TypeScript errors
- Ready for production use
Implement --providers option to run multiple messaging providers
simultaneously in a single relay command. This enables seamless
session sharing across WhatsApp, Telegram, and Twilio.

Changes:
- Add src/cli/multi-relay.ts: Core multi-provider orchestration
  - Concurrent provider execution with Promise.allSettled
  - Graceful shutdown handling with AbortController
  - Unified startup and shutdown messaging
  - Per-provider error isolation

- Add selectProviders() to src/web/session.ts
  - Validates provider authentication before starting
  - Expands "auto" to all authenticated providers
  - User-friendly error messages for missing auth

- Update src/cli/program.ts relay command
  - Add --providers option (comma-separated list)
  - Refactor to build webTuning before multi-provider check
  - Pass suppressStartMessage=true to prevent duplicate logs

- Fix src/config/sessions.test.ts
  - Update tests to await async deriveSessionKey()

Tests:
- Add 11 new tests in src/cli/multi-relay.test.ts
  - Provider startup and shutdown
  - Concurrent execution
  - SIGINT handling
  - Error isolation
  - Console log alignment
- All 480 tests passing

Usage:
  warelay relay --providers web,telegram
  warelay relay --providers auto --verbose
Suppress individual provider startup messages when running in
multi-provider mode to avoid duplicate/conflicting console output.
The multi-relay orchestrator provides unified startup messaging.

Changes:
- Add suppressStartMessage to WebMonitorTuning type
- Update monitorWebProvider to suppress startup message when flag set
- Update Telegram monitor to suppress initial "Starting..." message
- Pass suppressStartMessage: true from multi-relay to both providers

Console output before:
  📡 Starting 2 provider(s): web, telegram
  📡 Starting Telegram relay...
  📡 Listening for personal WhatsApp Web inbound messages. Leave this running; Ctrl+C to stop.
  ✅ All 2 provider(s) active. Listening for messages... (Ctrl+C to stop)

Console output after:
  📡 Starting 2 provider(s): web, telegram
  ✅ All 2 provider(s) active. Listening for messages... (Ctrl+C to stop)
- Fix /new command session creation by checking isNewSession flag
- Add timestamp prefix to Telegram messages matching WhatsApp format
- Add reply logging for Telegram matching WhatsApp behavior
- Move think level token extraction before bodyPrefix to avoid false matches
- Update agent specs (claude, gemini, opencode) to send identity on new sessions
- Pass sessionIntro in command context for proper agent initialization
- Add Telegram handling to relay command when selected via pickProvider
- Add Telegram delivery support to agent --deliver command
- Add telegram: prefix to sender identifiers to prevent session ID collisions
- Update detectProvider to properly handle telegram: prefixed identifiers
- Document provider detection logic and session ID collision prevention

Fixes session ID collisions where Telegram users without usernames (numeric IDs
or phone numbers) were being misidentified as WhatsApp users, causing cross-
provider session leakage.
Document how provider prefixes prevent session ID collisions and explain
the identity-map.json feature for intentional cross-provider session sharing.

Covers:
- Default isolated session behavior (telegram: prefix)
- Why isolation prevents context confusion
- How to use identity-map.json for intentional sharing
- Provider identifier format reference
- Update Full Configuration example to include Telegram identifiers
- Expand allowFrom documentation to show all supported formats
- Add note about telegram: prefix requirement in allowFrom lists
- Show examples of telegram usernames, phone numbers, and numeric IDs
- Fixed normalizeAllowFromEntry to properly handle telegram: prefix
- Removed unused telegram.allowFrom config field from schema
- Updated all documentation to use ~/.clawdis paths with legacy fallback notes
- Fixed selectProviders to include Twilio support
- Added provider disconnect in agent delivery
- Improved media type detection
- Made Telegram inbound media save buffer to disk for Claude access

All Telegram identifiers now require telegram: prefix in shared inbound.allowFrom config.
Fixed extractSenderIdentifier to always add + prefix to phone numbers
from GramJS, ensuring they match normalized allowFrom entries.

Without this fix, Telegram senders with phone-based identifiers would
be blocked when using allowFrom whitelists because:
- GramJS returns phone as "123456789"
- normalizeAllowFromEntry expects "+123456789"
- Comparison fails: telegram:123456789 != telegram:+123456789

Now all phone identifiers are consistently normalized to telegram:+phone.
Fixes three critical bugs in Telegram integration:

1. Case-insensitive username matching
   - Telegram usernames are case-insensitive, but the API returns them with original casing
   - Now lowercase all usernames when extracting sender identifier to match config entries
   - Fixes: messages from @arnemoor now match config entry telegram:@arnemoor

2. Entity resolution for sending replies
   - resolveEntity() now strips telegram: prefix before resolving users/chats
   - Fixes: "Could not resolve Telegram entity: telegram:@username" errors when sending replies

3. Improved skip logging
   - Messages skipped due to allowFrom filtering now always logged (not just in verbose mode)
   - Uses clear emoji indicator: ⏭️ Skipped message from...
@arnemoor
Copy link
Author

arnemoor commented Dec 6, 2025

This one is 101 commits behind your changes from yesterday, if you like the approach either take it or let me know if I should re-implement on the current main.

Applied formatting fixes to 21 files:
- Fixed import ordering
- Normalized indentation
- Wrapped long lines

No logic changes, formatting only.
@steipete
Copy link
Contributor

steipete commented Dec 7, 2025

Thank you! Yeah I wanted to first get the foundation right, revamp session management and so on before tackling Telegram.

I'll see if I can salvage anything from here - defo helps with planning, thank you!

@tiagoefreitas
Copy link

@steipete this or works more like the whatsapp version, my pr uses the bot api that does not need a phone number and is better for this use case. This pr is better when we want clawdis to reply to any message from anyone or proactively send messages. I think both could be integrated, the bot api to communicate with clawdis, and the user api to ask clawdis to send messsages/replies on our behalf

@steipete
Copy link
Contributor

steipete commented Dec 8, 2025

I went with the bot-version, seems sufficient for what. we need here + safer? https://github.com/tiagoefreitas/warelay
Did you consider both when building this?

@tiagoefreitas
Copy link

I went with the bot-version, seems sufficient for what. we need here + safer? https://github.com/tiagoefreitas/warelay

Did you consider both when building this?

I did consider and agree bot is safer but I still like this PR so the agent can act on my behalf but would need some safety tweaks like approved destinations, prefix/suffix saying it's not me writing, and should probably be a tool/cli and not in clawdis itself

@tiagoefreitas
Copy link

About multi provider switching I don't think it's necessary between WhatsApp and telegram but it's interesting between those and the web cli, and adding /new and /resume commands to the telegram bot commands.

@arnemoor arnemoor closed this Dec 8, 2025
dgarson referenced this pull request in dgarson/clawdbot Feb 2, 2026
feat(ui): add command history & recents to command palette
linustan pushed a commit to linustan/Komatachi that referenced this pull request Feb 5, 2026
…trator deferred

Agent communicates via structured JSON-lines on stdin/stdout. Orchestrator
is a separate process that owns agent processes and handles out-of-band
concerns. Orchestrator reads agent state from conversation store on disk.

https://claude.ai/code/session_019ZjUw1xvyg4ovLRrdQySpv
linustan added a commit to linustan/Komatachi that referenced this pull request Feb 5, 2026
Implement Decision openclaw#22: agent as headless worker process communicating
via stdin/stdout JSON-lines. Rust CLI spawns a Docker container running
the TypeScript agent, exchanges messages over a simple protocol.

- src/index.ts: application entry point wiring all modules + Anthropic SDK
- cli/: Rust REPL that builds/spawns Docker, manages container lifecycle
- Dockerfile: multi-stage build (test, typecheck, app)
- npm/node sandboxed to Docker only; lockfile generated via Docker
alexprime1889-prog pushed a commit to alexprime1889-prog/moltbot that referenced this pull request Feb 8, 2026
frodo-harborbot added a commit to harborworks/openclaw that referenced this pull request Feb 16, 2026
…nclaw#22)

* fix: add env_file to database service so it reads DATABASE_PASSWORD from .env.prod

Without env_file, the ${DATABASE_PASSWORD:-postgres} interpolation always
falls back to 'postgres' because the variable is never set in the database
service's environment. The backend reads .env.prod and gets the real password,
causing an auth mismatch.

* fix: build schema package before running tests

The @harbor-app/schema package must be built before backend tests can
resolve its exports. Without this, vitest fails with 'Failed to resolve
entry for package @harbor-app/schema'.
ivanuser added a commit to ivanuser/cortex that referenced this pull request Feb 23, 2026
…claw#22, openclaw#23)

Issue openclaw#22 — QR Code Pairing:
- QRPairModal component: generates QR with encoded gateway URL + invite code + role
- Nodes page: QR Pair button in header toolbar
- SetupWizard: detects ?qr= URL parameter, auto-fills gateway URL and invite code
- Uses invite.create RPC to generate one-time invite codes (falls back if unavailable)
- QR codes auto-expire after 5 minutes with regenerate prompt

Issue openclaw#23 — LAN Auto-Approve:
- Added gateway.security.lanAutoApprove config option (default: false)
- Added gateway.security.lanAutoApproveRole config option (default: 'operator')
- Config types (GatewaySecurityConfig) and zod schema validation
- Message handler checks isPrivateOrLoopbackAddress for LAN clients
- LAN auto-approved devices get securityRole from config
- Audit logged as device.pair.lan-auto-approve
- approveReason field added to PairedDevice type
- Nodes page shows 'LAN auto-approved' badge on auto-approved devices

Also includes invite/pairing code validation in connect handshake.
bottlerex pushed a commit to bottlerex/openclaw that referenced this pull request Feb 23, 2026
- git rm --cached config/openclaw.json (contains bot token, was already in .gitignore but still tracked)
- Added: logs/, metrics/, ssh-keys/, patches/, test files, plist files, config subdirs
- Fixes issue openclaw#22: bot token no longer in version control
songliu0403-rgb pushed a commit to songliu0403-rgb/openclaw that referenced this pull request Feb 26, 2026
emmalone added a commit to emmalone/openclaw that referenced this pull request Mar 1, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
speculatingwook added a commit to Pronto-Lab/prontoclaw that referenced this pull request Mar 3, 2026
- Move harness design doc from docs/tools/ to custom-docs/
- Add harness section (openclaw#22) to PRONTOLAB.md
- Remove SSH connection details from all prontolab docs (public repo)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
speculatingwook added a commit to Pronto-Lab/prontoclaw that referenced this pull request Mar 3, 2026
- Move harness design doc from docs/tools/ to custom-docs/
- Add harness section (openclaw#22) to PRONTOLAB.md
- Remove SSH connection details from all prontolab docs (public repo)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Piboonsak added a commit to Piboonsak/openclaw_github that referenced this pull request Mar 3, 2026
Config changes (openclaw#33):
- Switch primary model to anthropic/claude-sonnet-4-5 (Claude Sonnet 4.6)
- Add model to deploy.sh post-deploy config, update fallback chain

Nginx changes (openclaw#32, openclaw#21):
- Increase /line/ proxy_read_timeout from 120s to 300s for complex agent responses

Docker build (openclaw#39):
- Enable OPENCLAW_INSTALL_BROWSER=1 build-arg to include Chromium in image

Deploy pipeline fix:
- deploy-vps.yml now actually applies nginx config (was only staging to .new)
- Added backup + rollback safety for nginx config changes
- Removed broken nginx path from deploy.sh (was referencing wrong VPS path)

Issues addressed: #9, openclaw#20, openclaw#21, openclaw#22, openclaw#23, openclaw#32, openclaw#33, openclaw#37, openclaw#39, openclaw#42, openclaw#45, openclaw#46
guiramos added a commit to butley/openclaw that referenced this pull request Mar 4, 2026
…openclaw#15-openclaw#22)

Each patch dir now has a README.md (human context) and 001.patch
(git format-patch for mechanical re-application via git apply --3way).
Updated registry, verify script, and re-application order docs.
benieralexis-sudo pushed a commit to benieralexis-sudo/openclaw that referenced this pull request Mar 6, 2026
- openclaw#6 Reply classifier hallucination pattern: regex plus specifique (exige prenom+nom)
- openclaw#7 Fuzzy match domaine: ajoute verification local part + nom similaire
- openclaw#9 callOpenAI duplique: delegue au shared-nlp (tracking budget)
- openclaw#22 Collision cron 14h: mini-cycles decales 10h/12h/15h/17h
- openclaw#24 Bounce rate dilue: remplace derniere entree success par bounce
- openclaw#26 API chat+HITL sans auth: verification x-api-token/authorization
- openclaw#27 Passwords dashboard: generes aleatoirement (24 chars)
- Bonus: commande "statut systeme" protegee admin-only

28/28 bugs de l'audit corriges. 0 restant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
elliot-ylambda pushed a commit to elliot-ylambda/magister-openclaw that referenced this pull request Mar 7, 2026
…penclaw#22)

Removes misleading foundation text, makes unsubscribe link always
visible, and adds CAN-SPAM compliant copy to email footer.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
lucasmpramos added a commit to butley/openclaw that referenced this pull request Mar 9, 2026
Merges 33 commits from work branch:
- WhatsApp login tool HTTP invoke (openclaw#21/openclaw#25)
- Audio inbound transcription for webchat (openclaw#21)
- TTS audio playback in chat (openclaw#22)
- /media endpoint for audio/image serving (openclaw#19)
- image_generate tool with Gemini (openclaw#20)
- imageReady/mediaReady event pipeline (openclaw#22)
- Chat.send internal routing simplification (openclaw#23)
- Silent reply filter removal (openclaw#24)
- ThinkingDefault shortcut (openclaw#26)

Conflict resolution:
- patches/README.md: unified numbering (our #2-18 + his openclaw#19-26)
- patches/verify-patches.sh: combined both check sets (24 total)
- server-methods/chat.ts: used work version as base (has media pipeline),
  route resolution removal from openclaw#23 is intentional for webchat

Build: clean. Patches: 24/24 passing.

Co-authored-by: Bob
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants