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.
Files changed (26) hide show
  1. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/PKG-INFO +10 -10
  2. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/README.md +9 -9
  3. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/TeLLMgramBot.py +10 -10
  4. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/initialize.py +75 -84
  5. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/tools.py +4 -4
  6. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot.egg-info/PKG-INFO +10 -10
  7. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/setup.py +1 -1
  8. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/LICENSE +0 -0
  9. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/__init__.py +0 -0
  10. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/archive.py +0 -0
  11. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/conversation.py +0 -0
  12. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/database.py +0 -0
  13. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/message_handlers.py +0 -0
  14. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/models.py +0 -0
  15. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/providers/__init__.py +0 -0
  16. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/providers/anthropic_provider.py +0 -0
  17. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/providers/base.py +0 -0
  18. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/providers/factory.py +0 -0
  19. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/providers/openai_provider.py +0 -0
  20. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/utils.py +0 -0
  21. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot/web_utils.py +0 -0
  22. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot.egg-info/SOURCES.txt +0 -0
  23. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot.egg-info/dependency_links.txt +0 -0
  24. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot.egg-info/requires.txt +0 -0
  25. {tellmgrambot-3.13.2 → tellmgrambot-3.13.3}/TeLLMgramBot.egg-info/top_level.txt +0 -0
  26. {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.2
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 `log_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 `log_name` when configured).
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 `db_name` config) storing all messages, users, and chats
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, each loadable from environment variables or `.key` files:
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` | `telegram.key` | Always required |
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
- - `db_name`: Optional custom database filename without extension (e.g. `MyBot` creates `MyBot.db`); omit for default `conversations.db`. Use distinct names when running multiple bot instances in the same directory.
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 `log_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 `log_name` when configured).
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 `db_name` config) storing all messages, users, and chats
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, each loadable from environment variables or `.key` files:
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` | `telegram.key` | Always required |
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
- - `db_name`: Optional custom database filename without extension (e.g. `MyBot` creates `MyBot.db`); omit for default `conversations.db`. Use distinct names when running multiple bot instances in the same directory.
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._log_name)
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.yaml. Restricted to
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
- log_name: str | None = None,
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
- log_name: Optional label for console prefix and log file name (from config.yaml `log_name`).
987
- Defaults to the bot's Telegram username when not set.
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._log_name = log_name
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
- - Prints API key status summary and optional warnings if config values are missing or prompt is empty.
1108
- - Log identity/file label is taken from config.yaml `log_name` when set; otherwise defaults to the bot's Telegram username once _tele_info() resolves.
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.yaml.
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
- log_name = config['log_name'],
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
- 'db_name': None,
106
- 'log_name': None,
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
- 'db_name': '# Optional, custom DB filename (e.g. MyBot -> MyBot.db). Defaults to conversations.db',
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, log_name: Optional[str] = None):
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 [log_name] and opens
173
- the per-instance log file named {log_name}_{timestamp}.log.
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
- log_name is not set.
180
- log_name: Optional override from config.yaml. When set, used for both the console
181
- prefix and log file name instead of the Telegram username -- useful for
182
- multi-platform deployments where the same bot runs on Telegram and Discord.
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 = log_name or username
185
- # Sanitize: strip path separators so a misconfigured log_name cannot escape TELLMGRAMBOT_LOGS_PATH
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.yaml).
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 init_keys(config: Optional[dict] = None) -> ApiKeyStatus:
270
+ def _load_telegram_key(config: dict):
270
271
  """
271
- Load API keys and return an ApiKeyStatus indicating which features are available.
272
+ Load and validate the Telegram API key into TELLMGRAMBOT_TELEGRAM_API_KEY.
272
273
 
273
- Telegram key is always required - bot exits if missing or invalid.
274
- All other keys (OpenAI, Anthropic, VirusTotal) are optional: missing keys
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
- Provider keys (OpenAI/Anthropic) are loaded conditionally based on configured
278
- models. Key files are read from TELLMGRAMBOT_KEYS_PATH or the base execution path.
279
- Placeholder files are created for any missing keys. Prints API key status summary
280
- before returning.
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: Optional parsed bot configuration dict. If None, reads from
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
- # If no config provided, fall back to reading the default config file.
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
- # Build the full set of keys to check: only the required provider key(s) + always-required keys.
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
- always_keys = {
309
- 'telegram': 'https://core.telegram.org/api',
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() # tracks which keys loaded successfully
315
- for key, url in keys.items():
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 : %s\n' % (
416
- parameter.ljust(12),
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 db_name).
536
- - Provider-conditional API keys (OpenAI/Anthropic determined by config.yaml model prefixes).
537
- - Configuration files (config.yaml, models.yaml) with inline parameter descriptions.
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.yaml model prefixes (claude-* -> Anthropic,
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 db_name is configured in config.yaml,
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 db_name is known before init_db() runs.
567
- db_name = config.get('db_name')
568
- if db_name:
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 db_name cannot escape TELLMGRAMBOT_DATA_PATH.
571
- db_name_safe = os.path.basename(db_name).strip()
572
- if db_name_safe != db_name.strip() or not db_name_safe:
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"db_name '{db_name}' contains path components; ignoring and using conversations.db"
565
+ f"instance_name '{instance_name}' contains path components; ignoring and using conversations.db"
575
566
  )
576
- db_name = None
567
+ instance_name = None
577
568
  else:
578
- set_db_filename(db_name_safe)
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.yaml 'tools:' webhook
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.yaml 'tools:' key.
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.yaml to permit local addresses.]"
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.yaml to permit local addresses.]"
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.2
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 `log_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 `log_name` when configured).
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 `db_name` config) storing all messages, users, and chats
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, each loadable from environment variables or `.key` files:
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` | `telegram.key` | Always required |
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
- - `db_name`: Optional custom database filename without extension (e.g. `MyBot` creates `MyBot.db`); omit for default `conversations.db`. Use distinct names when running multiple bot instances in the same directory.
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.
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setup(
7
7
  name='TeLLMgramBot',
8
- version='3.13.2',
8
+ version='3.13.3',
9
9
  packages=find_packages(),
10
10
  license='MIT',
11
11
  author='Digital Heresy',
File without changes
File without changes