TeLLMgramBot 3.13.2__tar.gz → 3.13.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/PKG-INFO +10 -10
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/README.md +9 -9
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/TeLLMgramBot.py +10 -10
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/initialize.py +75 -84
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/tools.py +4 -4
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot.egg-info/PKG-INFO +10 -10
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/setup.py +1 -1
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/LICENSE +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/__init__.py +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/archive.py +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/conversation.py +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/database.py +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/message_handlers.py +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/models.py +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/providers/__init__.py +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/providers/anthropic_provider.py +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/providers/base.py +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/providers/factory.py +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/providers/openai_provider.py +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/utils.py +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/web_utils.py +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot.egg-info/SOURCES.txt +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot.egg-info/dependency_links.txt +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot.egg-info/requires.txt +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot.egg-info/top_level.txt +0 -0
- {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TeLLMgramBot
|
|
3
|
-
Version: 3.13.
|
|
3
|
+
Version: 3.13.3
|
|
4
4
|
Summary: LLM-powered Telegram bot (OpenAI + Anthropic)
|
|
5
5
|
Home-page: https://github.com/Digital-Heresy/TeLLMgramBot
|
|
6
6
|
Author: Digital Heresy
|
|
@@ -82,11 +82,11 @@ TeLLMgramBot creates the following directories:
|
|
|
82
82
|
- `test_personality.prmpt` - Sample bot personality (multi-provider, can be renamed)
|
|
83
83
|
- `url_analysis.prmpt` - URL summarization prompt template
|
|
84
84
|
- A system appendix is automatically appended to every persona at runtime, teaching the LLM about cross-chat memory and search behavior. User messages include speaker annotations with chat context and timestamps so the LLM always knows who is speaking, in which chat, and when.
|
|
85
|
-
- **`logs`** - Bot instance logs (one per startup, named after the bot's Telegram username or `
|
|
86
|
-
- Logs include anonymized Telegram IDs for privacy. Console shows INFO-level TeLLMgramBot messages only, prefixed with an `[identity label]` (the bot's Telegram username by default, or `
|
|
85
|
+
- **`logs`** - Bot instance logs (one per startup, named after the bot's Telegram username or `instance_name` config, e.g. `my_bot_2026-03-29_10-30-45.log`)
|
|
86
|
+
- Logs include anonymized Telegram IDs for privacy. Console shows INFO-level TeLLMgramBot messages only, prefixed with an `[identity label]` (the bot's Telegram username by default, or `instance_name` when configured).
|
|
87
87
|
- Bot keeps the 10 most recent logs per bot instance, automatically pruning older ones.
|
|
88
88
|
- Pass `-v` or `--verbose` on startup for DEBUG-level logging.
|
|
89
|
-
- **`data`** - SQLite database (default `conversations.db`, customizable via `
|
|
89
|
+
- **`data`** - SQLite database (default `conversations.db`, customizable via `instance_name` config) storing all messages, users, and chats
|
|
90
90
|
- Users manage their data via `/forget` and `/private` commands.
|
|
91
91
|
|
|
92
92
|
### Environment Variables for Paths
|
|
@@ -102,13 +102,13 @@ Override default directory locations by setting these environment variables (use
|
|
|
102
102
|
If unset, all paths default to subdirectories of the execution directory (the directory containing your entry-point script).
|
|
103
103
|
|
|
104
104
|
## API Keys
|
|
105
|
-
TeLLMgramBot supports four API keys,
|
|
105
|
+
TeLLMgramBot supports four API keys. OpenAI, Anthropic, and VirusTotal keys load from environment variables or `.key` files. The Telegram key loads from `telegram_api_key` in `config.yaml` or its env var (no `.key` file):
|
|
106
106
|
|
|
107
|
-
| Key | Env Var | File | When required |
|
|
108
|
-
|
|
107
|
+
| Key | Env Var | File/Config | When required |
|
|
108
|
+
|-----|---------|-------------|---------------|
|
|
109
109
|
| [OpenAI](https://platform.openai.com/api-keys) | `TELLMGRAMBOT_OPENAI_API_KEY` | `openai.key` | For `gpt-*` models |
|
|
110
110
|
| [Anthropic](https://console.anthropic.com/settings/keys) | `TELLMGRAMBOT_ANTHROPIC_API_KEY` | `anthropic.key` | For `claude-*` models |
|
|
111
|
-
| [Telegram](https://t.me/BotFather) | `TELLMGRAMBOT_TELEGRAM_API_KEY` | `
|
|
111
|
+
| [Telegram](https://t.me/BotFather) | `TELLMGRAMBOT_TELEGRAM_API_KEY` | `telegram_api_key` in config.yaml | Always required |
|
|
112
112
|
| [VirusTotal](https://www.virustotal.com/gui/my-apikey) | `TELLMGRAMBOT_VIRUSTOTAL_API_KEY` | `virustotal.key` | For URL analysis |
|
|
113
113
|
|
|
114
114
|
Missing provider keys (OpenAI or Anthropic) disable chat and URL analysis but allow the bot to start. Missing VirusTotal disables URL analysis. Telegram key is required - the bot will not start without it.
|
|
@@ -151,9 +151,9 @@ When the bot is triggered in a group and about to respond (not deferring to anot
|
|
|
151
151
|
- `bot_owner`: Telegram username(s) with admin access (required, no `@`). Accepts a single string or a YAML list of usernames.
|
|
152
152
|
- `chat_model`: LLM model for conversation (e.g. `gpt-4o-mini` or `claude-sonnet-4-6`)
|
|
153
153
|
- `url_model`: LLM model for URL analysis (e.g. `gpt-4o` or `claude-haiku-4-5`)
|
|
154
|
+
- `telegram_api_key`: Telegram bot API key (required). Lookup order: TELLMGRAMBOT_TELEGRAM_API_KEY env var -> config field.
|
|
154
155
|
- `bot_nickname` / `bot_initials`: Names the bot responds to in groups
|
|
155
|
-
- `
|
|
156
|
-
- `log_name`: Optional label used for the console prefix and log filename (e.g. `MyBot` produces `[MyBot] INFO: ...` on the console and `MyBot_{timestamp}.log`); omit to use the bot's Telegram username. Useful for multi-platform deployments sharing the same config.
|
|
156
|
+
- `instance_name`: Optional label for console prefix, log filename, and database name (e.g. `MyBot` produces `[MyBot] INFO: ...` on console, `MyBot_{timestamp}.log` logs, and `MyBot.db` database); omit to use bot's Telegram username for logging and `conversations.db` for database. Use distinct names when running multiple bot instances in the same directory.
|
|
157
157
|
- `token_limit`: Max tokens (optional; defaults to model's maximum)
|
|
158
158
|
- `search_limit`: Max search results (optional; defaults to 30)
|
|
159
159
|
- `archive_days`: Days before messages are eligible for archival (optional; default 60, minimum 1). Older messages are distilled into daily summaries, then progressively compressed into monthly digests. Once archived their respective raw messages do not return to the LLM context any more, only when searching messages.
|
|
@@ -50,11 +50,11 @@ TeLLMgramBot creates the following directories:
|
|
|
50
50
|
- `test_personality.prmpt` - Sample bot personality (multi-provider, can be renamed)
|
|
51
51
|
- `url_analysis.prmpt` - URL summarization prompt template
|
|
52
52
|
- A system appendix is automatically appended to every persona at runtime, teaching the LLM about cross-chat memory and search behavior. User messages include speaker annotations with chat context and timestamps so the LLM always knows who is speaking, in which chat, and when.
|
|
53
|
-
- **`logs`** - Bot instance logs (one per startup, named after the bot's Telegram username or `
|
|
54
|
-
- Logs include anonymized Telegram IDs for privacy. Console shows INFO-level TeLLMgramBot messages only, prefixed with an `[identity label]` (the bot's Telegram username by default, or `
|
|
53
|
+
- **`logs`** - Bot instance logs (one per startup, named after the bot's Telegram username or `instance_name` config, e.g. `my_bot_2026-03-29_10-30-45.log`)
|
|
54
|
+
- Logs include anonymized Telegram IDs for privacy. Console shows INFO-level TeLLMgramBot messages only, prefixed with an `[identity label]` (the bot's Telegram username by default, or `instance_name` when configured).
|
|
55
55
|
- Bot keeps the 10 most recent logs per bot instance, automatically pruning older ones.
|
|
56
56
|
- Pass `-v` or `--verbose` on startup for DEBUG-level logging.
|
|
57
|
-
- **`data`** - SQLite database (default `conversations.db`, customizable via `
|
|
57
|
+
- **`data`** - SQLite database (default `conversations.db`, customizable via `instance_name` config) storing all messages, users, and chats
|
|
58
58
|
- Users manage their data via `/forget` and `/private` commands.
|
|
59
59
|
|
|
60
60
|
### Environment Variables for Paths
|
|
@@ -70,13 +70,13 @@ Override default directory locations by setting these environment variables (use
|
|
|
70
70
|
If unset, all paths default to subdirectories of the execution directory (the directory containing your entry-point script).
|
|
71
71
|
|
|
72
72
|
## API Keys
|
|
73
|
-
TeLLMgramBot supports four API keys,
|
|
73
|
+
TeLLMgramBot supports four API keys. OpenAI, Anthropic, and VirusTotal keys load from environment variables or `.key` files. The Telegram key loads from `telegram_api_key` in `config.yaml` or its env var (no `.key` file):
|
|
74
74
|
|
|
75
|
-
| Key | Env Var | File | When required |
|
|
76
|
-
|
|
75
|
+
| Key | Env Var | File/Config | When required |
|
|
76
|
+
|-----|---------|-------------|---------------|
|
|
77
77
|
| [OpenAI](https://platform.openai.com/api-keys) | `TELLMGRAMBOT_OPENAI_API_KEY` | `openai.key` | For `gpt-*` models |
|
|
78
78
|
| [Anthropic](https://console.anthropic.com/settings/keys) | `TELLMGRAMBOT_ANTHROPIC_API_KEY` | `anthropic.key` | For `claude-*` models |
|
|
79
|
-
| [Telegram](https://t.me/BotFather) | `TELLMGRAMBOT_TELEGRAM_API_KEY` | `
|
|
79
|
+
| [Telegram](https://t.me/BotFather) | `TELLMGRAMBOT_TELEGRAM_API_KEY` | `telegram_api_key` in config.yaml | Always required |
|
|
80
80
|
| [VirusTotal](https://www.virustotal.com/gui/my-apikey) | `TELLMGRAMBOT_VIRUSTOTAL_API_KEY` | `virustotal.key` | For URL analysis |
|
|
81
81
|
|
|
82
82
|
Missing provider keys (OpenAI or Anthropic) disable chat and URL analysis but allow the bot to start. Missing VirusTotal disables URL analysis. Telegram key is required - the bot will not start without it.
|
|
@@ -119,9 +119,9 @@ When the bot is triggered in a group and about to respond (not deferring to anot
|
|
|
119
119
|
- `bot_owner`: Telegram username(s) with admin access (required, no `@`). Accepts a single string or a YAML list of usernames.
|
|
120
120
|
- `chat_model`: LLM model for conversation (e.g. `gpt-4o-mini` or `claude-sonnet-4-6`)
|
|
121
121
|
- `url_model`: LLM model for URL analysis (e.g. `gpt-4o` or `claude-haiku-4-5`)
|
|
122
|
+
- `telegram_api_key`: Telegram bot API key (required). Lookup order: TELLMGRAMBOT_TELEGRAM_API_KEY env var -> config field.
|
|
122
123
|
- `bot_nickname` / `bot_initials`: Names the bot responds to in groups
|
|
123
|
-
- `
|
|
124
|
-
- `log_name`: Optional label used for the console prefix and log filename (e.g. `MyBot` produces `[MyBot] INFO: ...` on the console and `MyBot_{timestamp}.log`); omit to use the bot's Telegram username. Useful for multi-platform deployments sharing the same config.
|
|
124
|
+
- `instance_name`: Optional label for console prefix, log filename, and database name (e.g. `MyBot` produces `[MyBot] INFO: ...` on console, `MyBot_{timestamp}.log` logs, and `MyBot.db` database); omit to use bot's Telegram username for logging and `conversations.db` for database. Use distinct names when running multiple bot instances in the same directory.
|
|
125
125
|
- `token_limit`: Max tokens (optional; defaults to model's maximum)
|
|
126
126
|
- `search_limit`: Max search results (optional; defaults to 30)
|
|
127
127
|
- `archive_days`: Days before messages are eligible for archival (optional; default 60, minimum 1). Older messages are distilled into daily summaries, then progressively compressed into monthly digests. Once archived their respective raw messages do not return to the LLM context any more, only when searching messages.
|
|
@@ -103,7 +103,7 @@ class TelegramBot:
|
|
|
103
103
|
self.telegram['username'] = bot.username
|
|
104
104
|
self.telegram['first_name'] = bot.first_name
|
|
105
105
|
self.telegram['last_name'] = bot.last_name
|
|
106
|
-
bind_log_identity(bot.username, self.
|
|
106
|
+
bind_log_identity(bot.username, self._instance_name)
|
|
107
107
|
except Exception as e:
|
|
108
108
|
# Fail fast if the bot's identity cannot be fetched to avoid
|
|
109
109
|
# continuing with an uninitialized self.telegram['username'].
|
|
@@ -183,7 +183,7 @@ class TelegramBot:
|
|
|
183
183
|
List all registered tools available to this bot instance (admin-only, private chat only).
|
|
184
184
|
|
|
185
185
|
Shows name and description for every tool the bot exposes, including the built-in
|
|
186
|
-
search_messages tool and any webhook tools defined in config.
|
|
186
|
+
search_messages tool and any webhook tools defined in bot config. Restricted to
|
|
187
187
|
bot_owner in a private chat; any other caller receives a generic denial.
|
|
188
188
|
|
|
189
189
|
Args:
|
|
@@ -964,7 +964,7 @@ class TelegramBot:
|
|
|
964
964
|
archive_days = INIT_BOT_CONFIG['archive_days'],
|
|
965
965
|
persona_prompt = INIT_BOT_CONFIG['persona_prompt'],
|
|
966
966
|
key_status: ApiKeyStatus | None = None,
|
|
967
|
-
|
|
967
|
+
instance_name: str | None = None,
|
|
968
968
|
webhook_schemas: list | None = None,
|
|
969
969
|
webhook_defs: dict | None = None,
|
|
970
970
|
):
|
|
@@ -983,8 +983,8 @@ class TelegramBot:
|
|
|
983
983
|
persona_temp: LLM temperature (0.0-2.0). If None, defaults to 1.0.
|
|
984
984
|
persona_prompt: System prompt defining the bot's behavior and personality.
|
|
985
985
|
key_status: ApiKeyStatus object indicating available features. If None, calls init_structure().
|
|
986
|
-
|
|
987
|
-
|
|
986
|
+
instance_name: Optional label for console prefix and log file name (from bot config `instance_name`).
|
|
987
|
+
Defaults to the bot's Telegram username when not set.
|
|
988
988
|
archive_days: Days before messages are eligible for Tier 1 archival (default: 60).
|
|
989
989
|
Must be an integer >= 1; invalid values log a warning and fall back to 60.
|
|
990
990
|
Tier 2 compression triggers at archive_days * 2.
|
|
@@ -1001,7 +1001,7 @@ class TelegramBot:
|
|
|
1001
1001
|
"""
|
|
1002
1002
|
# Starting to initialize, not online yet
|
|
1003
1003
|
self._online = False
|
|
1004
|
-
self.
|
|
1004
|
+
self._instance_name = instance_name
|
|
1005
1005
|
|
|
1006
1006
|
# Bootstrap structure and logging; init_logging() is a no-op if init_structure already ran it.
|
|
1007
1007
|
self.key_status = key_status if key_status is not None else init_structure()[0]
|
|
@@ -1104,13 +1104,13 @@ class TelegramBot:
|
|
|
1104
1104
|
Side Effects:
|
|
1105
1105
|
- Calls init_structure() which creates directories, config/prompt files, and checks API keys.
|
|
1106
1106
|
- Calls discover_mcp_tools() if any 'mcp_server:' entries are in config (gracefully degrades if called from async context).
|
|
1107
|
-
-
|
|
1108
|
-
- Log identity/file label is taken from config
|
|
1107
|
+
- May log warnings (for missing config values, empty prompt, or skipped MCP discovery), but does not print a startup API key status summary.
|
|
1108
|
+
- Log identity/file label is taken from bot config `instance_name` when set; otherwise defaults to the bot's Telegram username once _tele_info() resolves.
|
|
1109
1109
|
"""
|
|
1110
1110
|
# Bootstrap directories, logging, config, prompt (with appendix), and API keys in one call.
|
|
1111
1111
|
key_status, config, prompt = init_structure(config_file, prompt_file)
|
|
1112
1112
|
|
|
1113
|
-
# Build the webhook tool registry from the optional 'tools:' block in config.
|
|
1113
|
+
# Build the webhook tool registry from the optional 'tools:' block in bot config.
|
|
1114
1114
|
allow_local = config['allow_local_webhooks'] or False
|
|
1115
1115
|
webhook_schemas, webhook_defs = build_tool_registry(config.get('tools') or [], allow_local)
|
|
1116
1116
|
|
|
@@ -1145,7 +1145,7 @@ class TelegramBot:
|
|
|
1145
1145
|
archive_days = config['archive_days'],
|
|
1146
1146
|
persona_prompt = prompt,
|
|
1147
1147
|
key_status = key_status,
|
|
1148
|
-
|
|
1148
|
+
instance_name = config['instance_name'],
|
|
1149
1149
|
webhook_schemas = webhook_schemas,
|
|
1150
1150
|
webhook_defs = webhook_defs,
|
|
1151
1151
|
)
|
|
@@ -96,14 +96,16 @@ class ApiKeyStatus:
|
|
|
96
96
|
disabled_reasons: dict = field(default_factory=dict)
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
_TELEGRAM_KEY_RE = re.compile(r'^\d+:[A-Za-z0-9_-]+$')
|
|
100
|
+
|
|
99
101
|
INIT_BOT_CONFIG = {
|
|
100
102
|
'bot_owner': '<YOUR USERNAME>',
|
|
101
103
|
'bot_nickname': 'Testy',
|
|
102
104
|
'bot_initials': 'TB',
|
|
103
105
|
'chat_model': 'gpt-5-mini',
|
|
104
106
|
'url_model': 'gpt-5',
|
|
105
|
-
'
|
|
106
|
-
'
|
|
107
|
+
'telegram_api_key': '<YOUR TELEGRAM API KEY HERE>',
|
|
108
|
+
'instance_name': None,
|
|
107
109
|
'token_limit': None,
|
|
108
110
|
'search_limit': None,
|
|
109
111
|
'persona_temp': None,
|
|
@@ -113,8 +115,7 @@ INIT_BOT_CONFIG = {
|
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
INIT_BOT_CONFIG_COMMENTS = {
|
|
116
|
-
'
|
|
117
|
-
'log_name': '# Optional, label for console prefix and log file name. Defaults to the bot\'s Telegram username',
|
|
118
|
+
'instance_name': '# Optional, sets log label and DB filename (e.g. MyBot -> MyBot.db). If omitted, log label defaults to the bot\'s Telegram username and DB filename remains conversations.db',
|
|
118
119
|
'token_limit': '# Optional, overrides the chat_model\'s default max token limit',
|
|
119
120
|
'search_limit': '# Optional, max results returned by message search (default: 30)',
|
|
120
121
|
'persona_temp': '# Optional, LLM temperature 0.0-2.0 (default: model\'s default)',
|
|
@@ -165,24 +166,24 @@ def init_logging():
|
|
|
165
166
|
logging.getLogger('httpcore').setLevel(logging.WARNING)
|
|
166
167
|
|
|
167
168
|
|
|
168
|
-
def bind_log_identity(username: str,
|
|
169
|
+
def bind_log_identity(username: str, instance_name: Optional[str] = None):
|
|
169
170
|
"""
|
|
170
171
|
Bind the bot's identity to the logging system.
|
|
171
172
|
|
|
172
|
-
Updates the console handler formatter to prefix every line with [
|
|
173
|
-
the per-instance log file named {
|
|
173
|
+
Updates the console handler formatter to prefix every line with [instance_name] and opens
|
|
174
|
+
the per-instance log file named {instance_name}_{timestamp}.log.
|
|
174
175
|
|
|
175
176
|
Must be called after the bot's Telegram username is resolved by _tele_info().
|
|
176
177
|
|
|
177
178
|
Args:
|
|
178
179
|
username: The bot's Telegram username (without @); used as the identity label when
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
180
|
+
instance_name is not set.
|
|
181
|
+
instance_name: Optional override from bot config. When set, used for both the console
|
|
182
|
+
prefix and log file name instead of the Telegram username -- useful for
|
|
183
|
+
multi-platform deployments where the same bot runs on Telegram and Discord.
|
|
183
184
|
"""
|
|
184
|
-
raw_label =
|
|
185
|
-
# Sanitize: strip path separators so a misconfigured
|
|
185
|
+
raw_label = instance_name or username
|
|
186
|
+
# Sanitize: strip path separators so a misconfigured instance_name cannot escape TELLMGRAMBOT_LOGS_PATH
|
|
186
187
|
label = os.path.basename(raw_label).strip()
|
|
187
188
|
if not label:
|
|
188
189
|
label = username
|
|
@@ -244,7 +245,7 @@ def _requires_provider(config: dict) -> dict:
|
|
|
244
245
|
all other non-empty models require OpenAI.
|
|
245
246
|
|
|
246
247
|
Args:
|
|
247
|
-
config: Parsed bot configuration dict (from init_bot_config or read_yaml on config
|
|
248
|
+
config: Parsed bot configuration dict (from init_bot_config or read_yaml on bot config).
|
|
248
249
|
|
|
249
250
|
Returns:
|
|
250
251
|
Dict with keys 'openai' and 'anthropic', each a bool indicating if that provider key is needed.
|
|
@@ -266,66 +267,77 @@ def _model_provider(model: str) -> str | None:
|
|
|
266
267
|
return 'anthropic' if model.startswith('claude-') else 'openai'
|
|
267
268
|
|
|
268
269
|
|
|
269
|
-
def
|
|
270
|
+
def _load_telegram_key(config: dict):
|
|
270
271
|
"""
|
|
271
|
-
Load
|
|
272
|
+
Load and validate the Telegram API key into TELLMGRAMBOT_TELEGRAM_API_KEY.
|
|
272
273
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
disable the associated features rather than exiting.
|
|
274
|
+
Lookup order: TELLMGRAMBOT_TELEGRAM_API_KEY env var -> bot config `telegram_api_key`.
|
|
275
|
+
No .key file support. Exits on placeholder, missing, or malformed key.
|
|
276
276
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
Args:
|
|
278
|
+
config: Parsed bot configuration dict from init_bot_config().
|
|
279
|
+
"""
|
|
280
|
+
env_var = 'TELLMGRAMBOT_TELEGRAM_API_KEY'
|
|
281
|
+
if not os.environ.get(env_var):
|
|
282
|
+
config_val = (config.get('telegram_api_key') or '').strip()
|
|
283
|
+
is_placeholder = config_val.startswith('<') and config_val.endswith('>')
|
|
284
|
+
if config_val and not is_placeholder:
|
|
285
|
+
os.environ[env_var] = config_val
|
|
286
|
+
elif is_placeholder:
|
|
287
|
+
sys.exit("telegram_api_key in bot config is still a placeholder. Replace it with your Telegram bot API key. Exiting...")
|
|
288
|
+
else:
|
|
289
|
+
sys.exit(
|
|
290
|
+
"Telegram API key is required. Set telegram_api_key in bot config "
|
|
291
|
+
"or TELLMGRAMBOT_TELEGRAM_API_KEY env var. Exiting..."
|
|
292
|
+
)
|
|
293
|
+
if not _TELEGRAM_KEY_RE.match(os.environ.get(env_var, '')):
|
|
294
|
+
sys.exit(
|
|
295
|
+
"Telegram API key is not in valid format '<digits>:<letters/digits/_/->' "
|
|
296
|
+
"(e.g. 123456789:ABCdef...) - exiting..."
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def init_keys(config: dict) -> ApiKeyStatus:
|
|
301
|
+
"""
|
|
302
|
+
Load API keys and return an ApiKeyStatus indicating which features are available.
|
|
303
|
+
|
|
304
|
+
Telegram key is loaded and validated first via _load_telegram_key(). All other
|
|
305
|
+
keys (OpenAI, Anthropic, VirusTotal) are optional: missing keys disable the
|
|
306
|
+
associated features rather than exiting. These keys are loaded from env var or
|
|
307
|
+
key file (TELLMGRAMBOT_KEYS_PATH or base execution path). Placeholder files are
|
|
308
|
+
created for any missing keys.
|
|
281
309
|
|
|
282
310
|
Args:
|
|
283
|
-
config:
|
|
284
|
-
TELLMGRAMBOT_CONFIGS_PATH/config.yaml (or execution_dir/config.yaml).
|
|
311
|
+
config: Parsed bot configuration dict from init_bot_config().
|
|
285
312
|
|
|
286
313
|
Returns:
|
|
287
314
|
ApiKeyStatus with chat_enabled and url_analysis_enabled flags set based on which keys are available.
|
|
288
315
|
"""
|
|
289
|
-
|
|
290
|
-
# Failures here are non-fatal: config = {} means no models configured.
|
|
291
|
-
if config is None:
|
|
292
|
-
try:
|
|
293
|
-
config_path = os.path.join(os.environ.get('TELLMGRAMBOT_CONFIGS_PATH', execution_dir()), 'config.yaml')
|
|
294
|
-
config = read_yaml(config_path) or {}
|
|
295
|
-
except Exception:
|
|
296
|
-
config = {}
|
|
316
|
+
_load_telegram_key(config)
|
|
297
317
|
|
|
298
318
|
# Determine which provider keys are needed and which feature each model uses.
|
|
299
319
|
provider_needs = _requires_provider(config)
|
|
300
320
|
chat_provider = _model_provider(config.get('chat_model', ''))
|
|
301
321
|
url_provider = _model_provider(config.get('url_model', ''))
|
|
302
322
|
|
|
303
|
-
#
|
|
323
|
+
# Only load the required provider key(s) + VirusTotal.
|
|
304
324
|
provider_keys = {
|
|
305
325
|
'openai': 'https://platform.openai.com/api-keys',
|
|
306
326
|
'anthropic': 'https://docs.anthropic.com/en/api/getting-started',
|
|
307
327
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
'virustotal': 'https://developers.virustotal.com/reference/overview'
|
|
311
|
-
}
|
|
312
|
-
keys = {k: v for k, v in provider_keys.items() if provider_needs.get(k)} | always_keys
|
|
328
|
+
optional_keys = {k: v for k, v in provider_keys.items() if provider_needs.get(k)}
|
|
329
|
+
optional_keys['virustotal'] = 'https://developers.virustotal.com/reference/overview'
|
|
313
330
|
|
|
314
|
-
available = set() #
|
|
315
|
-
for key, url in
|
|
331
|
+
available = set(['telegram']) # telegram already validated above
|
|
332
|
+
for key, url in optional_keys.items():
|
|
316
333
|
env_var = f"TELLMGRAMBOT_{key.upper()}_API_KEY"
|
|
317
334
|
|
|
318
|
-
# If the key isn't already in the environment, try loading it from a key file.
|
|
319
335
|
if not os.environ.get(env_var):
|
|
320
336
|
path = os.path.join(os.environ.get('TELLMGRAMBOT_KEYS_PATH', execution_dir()), f"{key}.key")
|
|
321
337
|
if not os.path.exists(path):
|
|
322
|
-
# Key file missing: create a placeholder and warn (or exit for Telegram).
|
|
323
338
|
generate_file_path(os.path.dirname(path), os.path.basename(path), "API key",
|
|
324
339
|
f"YOUR {key.upper()} API KEY HERE - {url}\n"
|
|
325
340
|
)
|
|
326
|
-
if key == 'telegram':
|
|
327
|
-
sys.exit("Telegram API key is required to run TeLLMgramBot! Exiting...")
|
|
328
|
-
# Build a human-readable list of which features this key covers.
|
|
329
341
|
which = [f"chat_model ({config.get('chat_model', '?')})" if chat_provider == key else None,
|
|
330
342
|
f"url_model ({config.get('url_model', '?')})" if url_provider == key else None,
|
|
331
343
|
"VirusTotal integration" if key == 'virustotal' else None]
|
|
@@ -333,14 +345,10 @@ def init_keys(config: Optional[dict] = None) -> ApiKeyStatus:
|
|
|
333
345
|
logger.warning(f"{key}.key not found - {reason} will be disabled.")
|
|
334
346
|
continue
|
|
335
347
|
else:
|
|
336
|
-
# Key file exists: load it into the environment for this process.
|
|
337
348
|
os.environ[env_var] = read_text(path)
|
|
338
349
|
logger.info(f"Loaded {env_var} secret from: {path}")
|
|
339
350
|
|
|
340
|
-
# Reject blank or whitespace-only values (common copy-paste mistake).
|
|
341
351
|
if not os.environ.get(env_var) or any(c.isspace() for c in os.environ.get(env_var)):
|
|
342
|
-
if key == 'telegram':
|
|
343
|
-
sys.exit("Telegram API key is blank or invalid! Exiting...")
|
|
344
352
|
which = [f"chat_model ({config.get('chat_model', '?')})" if chat_provider == key else None,
|
|
345
353
|
f"url_model ({config.get('url_model', '?')})" if url_provider == key else None,
|
|
346
354
|
"VirusTotal integration" if key == 'virustotal' else None]
|
|
@@ -374,26 +382,9 @@ def init_keys(config: Optional[dict] = None) -> ApiKeyStatus:
|
|
|
374
382
|
reasons.append("missing VirusTotal API key")
|
|
375
383
|
key_status.disabled_reasons['url_analysis'] = ", ".join(reasons)
|
|
376
384
|
|
|
377
|
-
print_api_key_status(key_status)
|
|
378
385
|
return key_status
|
|
379
386
|
|
|
380
387
|
|
|
381
|
-
def print_api_key_status(key_status: ApiKeyStatus):
|
|
382
|
-
"""Print a startup summary of which API keys are available and which features they enable."""
|
|
383
|
-
print("--- TeLLMgramBot API Key Status ---")
|
|
384
|
-
features = [
|
|
385
|
-
('chat', 'Chat (LLM)', key_status.chat_enabled),
|
|
386
|
-
('url_analysis', 'URL Analysis', key_status.url_analysis_enabled),
|
|
387
|
-
]
|
|
388
|
-
for key, label, enabled in features:
|
|
389
|
-
if enabled:
|
|
390
|
-
print(f" {label:<12} : ENABLED")
|
|
391
|
-
else:
|
|
392
|
-
reason = key_status.disabled_reasons.get(key, 'unavailable')
|
|
393
|
-
print(f" {label:<12} : DISABLED ({reason})")
|
|
394
|
-
print("-----------------------------------")
|
|
395
|
-
|
|
396
|
-
|
|
397
388
|
def init_bot_config(file: str = 'config.yaml') -> dict:
|
|
398
389
|
"""
|
|
399
390
|
Returns contents of a bot configuration file (default 'config.yaml' if created).
|
|
@@ -412,9 +403,9 @@ def init_bot_config(file: str = 'config.yaml') -> dict:
|
|
|
412
403
|
env_var = "TELLMGRAMBOT_CONFIGS_PATH"
|
|
413
404
|
try:
|
|
414
405
|
text = ''.join(
|
|
415
|
-
'%s
|
|
416
|
-
parameter
|
|
417
|
-
value if value else INIT_BOT_CONFIG_COMMENTS.get(parameter, '# Optional')
|
|
406
|
+
'%s: %s\n' % (
|
|
407
|
+
parameter,
|
|
408
|
+
(f"'{value}'" if isinstance(value, str) else value) if value is not None else INIT_BOT_CONFIG_COMMENTS.get(parameter, '# Optional')
|
|
418
409
|
)
|
|
419
410
|
for parameter, value in INIT_BOT_CONFIG.items()
|
|
420
411
|
if parameter != 'persona_prompt'
|
|
@@ -532,15 +523,15 @@ def init_structure(
|
|
|
532
523
|
Performs the whole TeLLMgramBot first-run setup including:
|
|
533
524
|
- Directories for configurations, prompts, and conversation/error logs.
|
|
534
525
|
- Logging configuration (console handler; file handler bound later by bind_log_identity()).
|
|
535
|
-
- Database naming and schema initialization from config (conversations.db or custom
|
|
536
|
-
- Provider-conditional API keys (OpenAI/Anthropic determined by config
|
|
537
|
-
- Configuration files (config
|
|
526
|
+
- Database naming and schema initialization from config (conversations.db or custom instance_name).
|
|
527
|
+
- Provider-conditional API keys (OpenAI/Anthropic determined by bot config model prefixes).
|
|
528
|
+
- Configuration files (bot config, models.yaml) with inline parameter descriptions.
|
|
538
529
|
- System prompt files (test_personality.prmpt, url_analysis.prmpt).
|
|
539
530
|
|
|
540
|
-
Provider key requirements are inferred from config
|
|
531
|
+
Provider key requirements are inferred from bot config model prefixes (claude-* -> Anthropic,
|
|
541
532
|
gpt-* -> OpenAI, etc.) to streamline setup for single-provider deployments.
|
|
542
533
|
|
|
543
|
-
Database naming: After init_bot_config() runs, if
|
|
534
|
+
Database naming: After init_bot_config() runs, if instance_name is configured in bot config,
|
|
544
535
|
calls set_db_filename() and performs a one-time rename migration of conversations.db to
|
|
545
536
|
the custom filename (only if the target does not yet exist). This ordering ensures the DB
|
|
546
537
|
filename is settled before schema init.
|
|
@@ -563,19 +554,19 @@ def init_structure(
|
|
|
563
554
|
init_models_config()
|
|
564
555
|
|
|
565
556
|
# Configure DB filename and run schema init now that config is available.
|
|
566
|
-
# Must happen after init_bot_config() so
|
|
567
|
-
|
|
568
|
-
if
|
|
557
|
+
# Must happen after init_bot_config() so instance_name is known before init_db() runs.
|
|
558
|
+
instance_name = config.get('instance_name')
|
|
559
|
+
if instance_name:
|
|
569
560
|
# Sanitize to a plain filename - reject path separators and absolute paths so a
|
|
570
|
-
# misconfigured
|
|
571
|
-
|
|
572
|
-
if
|
|
561
|
+
# misconfigured instance_name cannot escape TELLMGRAMBOT_DATA_PATH.
|
|
562
|
+
instance_name_safe = os.path.basename(instance_name).strip()
|
|
563
|
+
if instance_name_safe != instance_name.strip() or not instance_name_safe:
|
|
573
564
|
logger.warning(
|
|
574
|
-
f"
|
|
565
|
+
f"instance_name '{instance_name}' contains path components; ignoring and using conversations.db"
|
|
575
566
|
)
|
|
576
|
-
|
|
567
|
+
instance_name = None
|
|
577
568
|
else:
|
|
578
|
-
set_db_filename(
|
|
569
|
+
set_db_filename(instance_name_safe)
|
|
579
570
|
# One-time migration: rename conversations.db to the new name if the target
|
|
580
571
|
# doesn't exist yet. Assumes a single-bot deployment owns conversations.db.
|
|
581
572
|
data_dir = os.environ.get('TELLMGRAMBOT_DATA_PATH', os.path.join(execution_dir(), 'data'))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Webhook and MCP tool registry and executors for TeLLMgramBot.
|
|
3
3
|
|
|
4
|
-
Provides build_tool_registry() for startup parsing of config
|
|
4
|
+
Provides build_tool_registry() for startup parsing of bot config 'tools:' webhook
|
|
5
5
|
entries, discover_mcp_tools() for async MCP server discovery, execute_webhook() for
|
|
6
6
|
dispatching webhook tool calls, and execute_mcp() for MCP tool calls at runtime.
|
|
7
7
|
"""
|
|
@@ -96,7 +96,7 @@ def build_tool_registry(
|
|
|
96
96
|
are skipped with warnings. Duplicate tool names are deduplicated (first wins).
|
|
97
97
|
|
|
98
98
|
Args:
|
|
99
|
-
tools_config: List of raw tool definition dicts from config
|
|
99
|
+
tools_config: List of raw tool definition dicts from bot config 'tools:' key.
|
|
100
100
|
Pass an empty list or None to produce an empty registry.
|
|
101
101
|
allow_local: If True, the executor permits webhook URLs targeting loopback or
|
|
102
102
|
link-local addresses. Mirrors the 'allow_local_webhooks' config key.
|
|
@@ -387,7 +387,7 @@ async def execute_mcp(tool_def: dict, arguments: dict) -> str:
|
|
|
387
387
|
logger.warning(f"MCP '{name}': SSRF block - '{parsed.hostname}' is loopback or link-local")
|
|
388
388
|
return (
|
|
389
389
|
f"[Tool error: '{name}' targets a loopback or link-local address. "
|
|
390
|
-
f"Set allow_local_webhooks: true in config
|
|
390
|
+
f"Set allow_local_webhooks: true in bot config to permit local addresses.]"
|
|
391
391
|
)
|
|
392
392
|
|
|
393
393
|
call_url = f"{server_url}/tools/call"
|
|
@@ -489,7 +489,7 @@ async def execute_webhook(tool_def: dict, arguments: dict) -> str:
|
|
|
489
489
|
logger.warning(f"Webhook '{name}': SSRF block - '{hostname}' is loopback or link-local")
|
|
490
490
|
return (
|
|
491
491
|
f"[Tool error: '{name}' targets a loopback or link-local address. "
|
|
492
|
-
f"Set allow_local_webhooks: true in config
|
|
492
|
+
f"Set allow_local_webhooks: true in bot config to permit local addresses.]"
|
|
493
493
|
)
|
|
494
494
|
|
|
495
495
|
host_part = f"{parsed.hostname}:{parsed.port}" if parsed.port else parsed.hostname
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TeLLMgramBot
|
|
3
|
-
Version: 3.13.
|
|
3
|
+
Version: 3.13.3
|
|
4
4
|
Summary: LLM-powered Telegram bot (OpenAI + Anthropic)
|
|
5
5
|
Home-page: https://github.com/Digital-Heresy/TeLLMgramBot
|
|
6
6
|
Author: Digital Heresy
|
|
@@ -82,11 +82,11 @@ TeLLMgramBot creates the following directories:
|
|
|
82
82
|
- `test_personality.prmpt` - Sample bot personality (multi-provider, can be renamed)
|
|
83
83
|
- `url_analysis.prmpt` - URL summarization prompt template
|
|
84
84
|
- A system appendix is automatically appended to every persona at runtime, teaching the LLM about cross-chat memory and search behavior. User messages include speaker annotations with chat context and timestamps so the LLM always knows who is speaking, in which chat, and when.
|
|
85
|
-
- **`logs`** - Bot instance logs (one per startup, named after the bot's Telegram username or `
|
|
86
|
-
- Logs include anonymized Telegram IDs for privacy. Console shows INFO-level TeLLMgramBot messages only, prefixed with an `[identity label]` (the bot's Telegram username by default, or `
|
|
85
|
+
- **`logs`** - Bot instance logs (one per startup, named after the bot's Telegram username or `instance_name` config, e.g. `my_bot_2026-03-29_10-30-45.log`)
|
|
86
|
+
- Logs include anonymized Telegram IDs for privacy. Console shows INFO-level TeLLMgramBot messages only, prefixed with an `[identity label]` (the bot's Telegram username by default, or `instance_name` when configured).
|
|
87
87
|
- Bot keeps the 10 most recent logs per bot instance, automatically pruning older ones.
|
|
88
88
|
- Pass `-v` or `--verbose` on startup for DEBUG-level logging.
|
|
89
|
-
- **`data`** - SQLite database (default `conversations.db`, customizable via `
|
|
89
|
+
- **`data`** - SQLite database (default `conversations.db`, customizable via `instance_name` config) storing all messages, users, and chats
|
|
90
90
|
- Users manage their data via `/forget` and `/private` commands.
|
|
91
91
|
|
|
92
92
|
### Environment Variables for Paths
|
|
@@ -102,13 +102,13 @@ Override default directory locations by setting these environment variables (use
|
|
|
102
102
|
If unset, all paths default to subdirectories of the execution directory (the directory containing your entry-point script).
|
|
103
103
|
|
|
104
104
|
## API Keys
|
|
105
|
-
TeLLMgramBot supports four API keys,
|
|
105
|
+
TeLLMgramBot supports four API keys. OpenAI, Anthropic, and VirusTotal keys load from environment variables or `.key` files. The Telegram key loads from `telegram_api_key` in `config.yaml` or its env var (no `.key` file):
|
|
106
106
|
|
|
107
|
-
| Key | Env Var | File | When required |
|
|
108
|
-
|
|
107
|
+
| Key | Env Var | File/Config | When required |
|
|
108
|
+
|-----|---------|-------------|---------------|
|
|
109
109
|
| [OpenAI](https://platform.openai.com/api-keys) | `TELLMGRAMBOT_OPENAI_API_KEY` | `openai.key` | For `gpt-*` models |
|
|
110
110
|
| [Anthropic](https://console.anthropic.com/settings/keys) | `TELLMGRAMBOT_ANTHROPIC_API_KEY` | `anthropic.key` | For `claude-*` models |
|
|
111
|
-
| [Telegram](https://t.me/BotFather) | `TELLMGRAMBOT_TELEGRAM_API_KEY` | `
|
|
111
|
+
| [Telegram](https://t.me/BotFather) | `TELLMGRAMBOT_TELEGRAM_API_KEY` | `telegram_api_key` in config.yaml | Always required |
|
|
112
112
|
| [VirusTotal](https://www.virustotal.com/gui/my-apikey) | `TELLMGRAMBOT_VIRUSTOTAL_API_KEY` | `virustotal.key` | For URL analysis |
|
|
113
113
|
|
|
114
114
|
Missing provider keys (OpenAI or Anthropic) disable chat and URL analysis but allow the bot to start. Missing VirusTotal disables URL analysis. Telegram key is required - the bot will not start without it.
|
|
@@ -151,9 +151,9 @@ When the bot is triggered in a group and about to respond (not deferring to anot
|
|
|
151
151
|
- `bot_owner`: Telegram username(s) with admin access (required, no `@`). Accepts a single string or a YAML list of usernames.
|
|
152
152
|
- `chat_model`: LLM model for conversation (e.g. `gpt-4o-mini` or `claude-sonnet-4-6`)
|
|
153
153
|
- `url_model`: LLM model for URL analysis (e.g. `gpt-4o` or `claude-haiku-4-5`)
|
|
154
|
+
- `telegram_api_key`: Telegram bot API key (required). Lookup order: TELLMGRAMBOT_TELEGRAM_API_KEY env var -> config field.
|
|
154
155
|
- `bot_nickname` / `bot_initials`: Names the bot responds to in groups
|
|
155
|
-
- `
|
|
156
|
-
- `log_name`: Optional label used for the console prefix and log filename (e.g. `MyBot` produces `[MyBot] INFO: ...` on the console and `MyBot_{timestamp}.log`); omit to use the bot's Telegram username. Useful for multi-platform deployments sharing the same config.
|
|
156
|
+
- `instance_name`: Optional label for console prefix, log filename, and database name (e.g. `MyBot` produces `[MyBot] INFO: ...` on console, `MyBot_{timestamp}.log` logs, and `MyBot.db` database); omit to use bot's Telegram username for logging and `conversations.db` for database. Use distinct names when running multiple bot instances in the same directory.
|
|
157
157
|
- `token_limit`: Max tokens (optional; defaults to model's maximum)
|
|
158
158
|
- `search_limit`: Max search results (optional; defaults to 30)
|
|
159
159
|
- `archive_days`: Days before messages are eligible for archival (optional; default 60, minimum 1). Older messages are distilled into daily summaries, then progressively compressed into monthly digests. Once archived their respective raw messages do not return to the LLM context any more, only when searching messages.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|