TeLLMgramBot 3.4.0__tar.gz → 3.4.2__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.4.0 → tellmgrambot-3.4.2}/PKG-INFO +2 -9
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/README.md +1 -8
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/TeLLMgramBot.py +28 -24
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/conversation.py +8 -4
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/database.py +37 -46
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/message_handlers.py +0 -21
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot.egg-info/PKG-INFO +2 -9
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/setup.py +1 -1
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/LICENSE +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/__init__.py +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/initialize.py +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/models.py +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/providers/__init__.py +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/providers/anthropic_provider.py +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/providers/base.py +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/providers/factory.py +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/providers/openai_provider.py +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/utils.py +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot/web_utils.py +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot.egg-info/SOURCES.txt +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot.egg-info/dependency_links.txt +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot.egg-info/requires.txt +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/TeLLMgramBot.egg-info/top_level.txt +0 -0
- {tellmgrambot-3.4.0 → tellmgrambot-3.4.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TeLLMgramBot
|
|
3
|
-
Version: 3.4.
|
|
3
|
+
Version: 3.4.2
|
|
4
4
|
Summary: LLM-powered Telegram bot (OpenAI + Anthropic)
|
|
5
5
|
Home-page: https://github.com/Digital-Heresy/TeLLMgramBot
|
|
6
6
|
Author: Digital Heresy
|
|
@@ -144,14 +144,7 @@ Each file with the associated API key will update its respective environment var
|
|
|
144
144
|
## Commands and Interactions
|
|
145
145
|
|
|
146
146
|
### Read Receipt Acknowledgement (Group Chats Only)
|
|
147
|
-
In group and supergroup chats, you
|
|
148
|
-
|
|
149
|
-
- `@botname acknowledge`
|
|
150
|
-
- `botname mark as read`
|
|
151
|
-
- `BN did you read that?`
|
|
152
|
-
- `botname confirm you received`
|
|
153
|
-
|
|
154
|
-
The bot will respond with a 👀 reaction emoji on your message (if the Telegram client supports message reactions), or with a short "Got it!" reply as fallback. Read receipt requests are processed immediately without touching the conversation context, making them ideal for confirming the bot is online without disrupting group flow.
|
|
147
|
+
In group and supergroup chats, whenever you mention the bot by username (`@botname`), nickname, or initials, the bot immediately responds with a 👀 reaction emoji on your message to confirm it is online, followed by a full LLM response. The reaction uses the Telegram message reaction API when available (👀 emoji), or falls back to a short "Got it!" text reply for older clients that don't support reactions.
|
|
155
148
|
|
|
156
149
|
## Bot Setup
|
|
157
150
|
This library includes an example script `test_local.py`, which uses files from the folders `configs` and `prompts` for TeLLMgramBot to process.
|
|
@@ -113,14 +113,7 @@ Each file with the associated API key will update its respective environment var
|
|
|
113
113
|
## Commands and Interactions
|
|
114
114
|
|
|
115
115
|
### Read Receipt Acknowledgement (Group Chats Only)
|
|
116
|
-
In group and supergroup chats, you
|
|
117
|
-
|
|
118
|
-
- `@botname acknowledge`
|
|
119
|
-
- `botname mark as read`
|
|
120
|
-
- `BN did you read that?`
|
|
121
|
-
- `botname confirm you received`
|
|
122
|
-
|
|
123
|
-
The bot will respond with a 👀 reaction emoji on your message (if the Telegram client supports message reactions), or with a short "Got it!" reply as fallback. Read receipt requests are processed immediately without touching the conversation context, making them ideal for confirming the bot is online without disrupting group flow.
|
|
116
|
+
In group and supergroup chats, whenever you mention the bot by username (`@botname`), nickname, or initials, the bot immediately responds with a 👀 reaction emoji on your message to confirm it is online, followed by a full LLM response. The reaction uses the Telegram message reaction API when available (👀 emoji), or falls back to a short "Got it!" text reply for older clients that don't support reactions.
|
|
124
117
|
|
|
125
118
|
## Bot Setup
|
|
126
119
|
This library includes an example script `test_local.py`, which uses files from the folders `configs` and `prompts` for TeLLMgramBot to process.
|
|
@@ -27,10 +27,10 @@ from .database import (
|
|
|
27
27
|
delete_messages_for_user,
|
|
28
28
|
delete_messages_for_chat,
|
|
29
29
|
delete_private_messages_for_user,
|
|
30
|
-
|
|
30
|
+
delete_bot_replies_for_user,
|
|
31
31
|
)
|
|
32
32
|
from .models import TokenLimits
|
|
33
|
-
from .message_handlers import handle_greetings, handle_common_queries, handle_url_ask
|
|
33
|
+
from .message_handlers import handle_greetings, handle_common_queries, handle_url_ask
|
|
34
34
|
from .utils import read_yaml, read_text, exact_word_match, generate_error_path, log_error
|
|
35
35
|
|
|
36
36
|
class TelegramBot:
|
|
@@ -108,19 +108,24 @@ class TelegramBot:
|
|
|
108
108
|
"""
|
|
109
109
|
Remove conversation history from the database (behavior depends on chat type).
|
|
110
110
|
|
|
111
|
-
In private chats: deletes all
|
|
112
|
-
|
|
113
|
-
deletes
|
|
114
|
-
|
|
111
|
+
In private chats: deletes all bot replies whose reply_to_id points to this user's messages,
|
|
112
|
+
then deletes the user's rows across all chats (full wipe including group messages),
|
|
113
|
+
then deletes any remaining messages in the private chat (pre-migration rows with NULL reply_to_id).
|
|
114
|
+
In group chats: deletes bot replies linked to this user's messages, then deletes the user's
|
|
115
|
+
rows across all chats, leaving other users' messages intact.
|
|
115
116
|
|
|
116
117
|
In both cases, evicts only the in-memory Conversation objects that contain this
|
|
117
118
|
user's data - scoped to the current chat, the user's private chat, and any other
|
|
118
119
|
chats where their context was previously merged. Other users' active sessions are
|
|
119
120
|
not affected.
|
|
120
121
|
"""
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
validated = await self.tele_validate(update)
|
|
123
|
+
if not validated:
|
|
124
|
+
return
|
|
125
|
+
(msg, chat, user) = validated
|
|
126
|
+
user_id = user.id
|
|
127
|
+
chat_id = chat.id
|
|
128
|
+
chat_type = chat.type
|
|
124
129
|
|
|
125
130
|
# Find all chat_ids where this user's context has been merged in memory,
|
|
126
131
|
# identified by user_id being present in the Conversation's _context_cursor.
|
|
@@ -131,19 +136,21 @@ class TelegramBot:
|
|
|
131
136
|
user_chat_ids.update({chat_id, user_id})
|
|
132
137
|
|
|
133
138
|
if chat_type == 'private':
|
|
139
|
+
# Wipe bot replies linked to this user, then user's own rows across all chats,
|
|
140
|
+
# then any remaining bot replies in the private chat (pre-migration rows).
|
|
141
|
+
await delete_bot_replies_for_user(user_id)
|
|
142
|
+
await delete_messages_for_user(user_id)
|
|
134
143
|
await delete_messages_for_chat(chat_id)
|
|
135
144
|
else:
|
|
145
|
+
await delete_bot_replies_for_user(user_id)
|
|
136
146
|
await delete_messages_for_user(user_id)
|
|
137
|
-
# Delete bot replies paired to this user's messages, plus any pre-existing
|
|
138
|
-
# orphaned assistant rows left by earlier /forget calls in this chat.
|
|
139
|
-
await delete_paired_bot_replies(chat_id, self.telegram['bot_id'])
|
|
140
147
|
|
|
141
148
|
# Evict only the Conversations that contain this user's data, not all sessions.
|
|
142
149
|
for evict_id in user_chat_ids:
|
|
143
150
|
self.conversations.pop(evict_id, None)
|
|
144
151
|
self.token_warning.pop(evict_id, None)
|
|
145
152
|
|
|
146
|
-
await
|
|
153
|
+
await msg.reply_text("My memories of our conversations are wiped!")
|
|
147
154
|
|
|
148
155
|
async def tele_private_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|
149
156
|
"""
|
|
@@ -261,7 +268,7 @@ class TelegramBot:
|
|
|
261
268
|
# Private mode only applies in private chats - group messages are never flagged private
|
|
262
269
|
user_private_mode = await get_private_mode(user_id)
|
|
263
270
|
is_private = (chat_type == 'private') and user_private_mode
|
|
264
|
-
await conv.add_user_message(text, user_id, username, is_private)
|
|
271
|
+
user_msg_id = await conv.add_user_message(text, user_id, username, is_private)
|
|
265
272
|
|
|
266
273
|
# Check if the user is asking about a [URL]
|
|
267
274
|
url_match = re.search(r'\[http(s)?://\S+]', text)
|
|
@@ -293,7 +300,7 @@ class TelegramBot:
|
|
|
293
300
|
# Propagate is_private so bot replies in private-mode exchanges are also excluded
|
|
294
301
|
# from group context loading - otherwise the assistant's half of the conversation leaks.
|
|
295
302
|
await conv.add_assistant_message(
|
|
296
|
-
reply, self.telegram['bot_id'], self.telegram['username'], is_private
|
|
303
|
+
reply, self.telegram['bot_id'], self.telegram['username'], is_private, user_msg_id
|
|
297
304
|
)
|
|
298
305
|
|
|
299
306
|
# Recompute token count after full exchange is stored, then prune if needed
|
|
@@ -307,9 +314,10 @@ class TelegramBot:
|
|
|
307
314
|
"""
|
|
308
315
|
Send a read receipt acknowledgement via message reaction.
|
|
309
316
|
|
|
310
|
-
Attempts to add a
|
|
311
|
-
read it
|
|
312
|
-
|
|
317
|
+
Attempts to add a 👀 emoji reaction to the user's message to confirm the bot
|
|
318
|
+
read it. If the message reaction API is unavailable (e.g., older Telegram clients),
|
|
319
|
+
falls back to a short "Got it!" text reply. Called unconditionally on every group
|
|
320
|
+
mention before the normal LLM response.
|
|
313
321
|
|
|
314
322
|
Args:
|
|
315
323
|
msg: The Telegram Message object to react to.
|
|
@@ -338,17 +346,13 @@ class TelegramBot:
|
|
|
338
346
|
if exact_word_match(self.telegram['username'], msg.text):
|
|
339
347
|
pattern = r'@?\b' + re.escape(self.telegram['username']) + r'\b'
|
|
340
348
|
new_text = re.sub(pattern, '', msg.text).strip()
|
|
341
|
-
|
|
342
|
-
await self._send_read_receipt(msg, context)
|
|
343
|
-
return
|
|
349
|
+
await self._send_read_receipt(msg, context)
|
|
344
350
|
response = await self.tele_handle_response(new_text, msg)
|
|
345
351
|
elif (
|
|
346
352
|
exact_word_match(self.telegram['nickname'], msg.text) or
|
|
347
353
|
exact_word_match(self.telegram['initials'], msg.text)
|
|
348
354
|
):
|
|
349
|
-
|
|
350
|
-
await self._send_read_receipt(msg, context)
|
|
351
|
-
return
|
|
355
|
+
await self._send_read_receipt(msg, context)
|
|
352
356
|
response = await self.tele_handle_response(msg.text, msg)
|
|
353
357
|
else:
|
|
354
358
|
return
|
|
@@ -80,7 +80,7 @@ class Conversation:
|
|
|
80
80
|
self.system_content = new_content
|
|
81
81
|
self.messages[0] = {"role": "system", "content": new_content}
|
|
82
82
|
|
|
83
|
-
async def add_user_message(self, content: str, user_id: int, username: str | None, is_private: bool = False):
|
|
83
|
+
async def add_user_message(self, content: str, user_id: int, username: str | None, is_private: bool = False) -> int:
|
|
84
84
|
"""
|
|
85
85
|
Add a user message to conversation memory and persist it to the database.
|
|
86
86
|
|
|
@@ -90,14 +90,17 @@ class Conversation:
|
|
|
90
90
|
username: Telegram username (display only, may be None).
|
|
91
91
|
is_private: If True, flags this message as private-mode excluded from
|
|
92
92
|
all context loads (user has /private ON in a private chat).
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
The database id of the newly inserted row, for use as reply_to_id.
|
|
93
96
|
"""
|
|
94
97
|
msg_dict = {"role": "user", "content": content}
|
|
95
98
|
self.messages.append(msg_dict)
|
|
96
99
|
if is_private:
|
|
97
100
|
self._private_message_ids.add(id(msg_dict))
|
|
98
|
-
await insert_message(self.chat_id, user_id, username, "user", content, is_private)
|
|
101
|
+
return await insert_message(self.chat_id, user_id, username, "user", content, is_private)
|
|
99
102
|
|
|
100
|
-
async def add_assistant_message(self, content: str, bot_user_id: int, bot_username: str | None, is_private: bool = False):
|
|
103
|
+
async def add_assistant_message(self, content: str, bot_user_id: int, bot_username: str | None, is_private: bool = False, reply_to_id: int | None = None):
|
|
101
104
|
"""
|
|
102
105
|
Add an assistant message to conversation memory and persist it to the database.
|
|
103
106
|
|
|
@@ -108,12 +111,13 @@ class Conversation:
|
|
|
108
111
|
is_private: If True, excludes this reply from group context loading. Should match
|
|
109
112
|
the is_private flag of the user message it responds to, so that both
|
|
110
113
|
sides of a private-mode exchange are excluded from shared contexts.
|
|
114
|
+
reply_to_id: Database id of the user message this reply responds to.
|
|
111
115
|
"""
|
|
112
116
|
msg_dict = {"role": "assistant", "content": content}
|
|
113
117
|
self.messages.append(msg_dict)
|
|
114
118
|
if is_private:
|
|
115
119
|
self._private_message_ids.add(id(msg_dict))
|
|
116
|
-
await insert_message(self.chat_id, bot_user_id, bot_username, "assistant", content, is_private)
|
|
120
|
+
await insert_message(self.chat_id, bot_user_id, bot_username, "assistant", content, is_private, reply_to_id)
|
|
117
121
|
|
|
118
122
|
async def get_message_token_count(self) -> int:
|
|
119
123
|
"""
|
|
@@ -22,14 +22,15 @@ _DDL = """
|
|
|
22
22
|
PRAGMA journal_mode=WAL;
|
|
23
23
|
|
|
24
24
|
CREATE TABLE IF NOT EXISTS messages (
|
|
25
|
-
id
|
|
26
|
-
chat_id
|
|
27
|
-
user_id
|
|
28
|
-
username
|
|
29
|
-
role
|
|
30
|
-
content
|
|
31
|
-
is_private
|
|
32
|
-
|
|
25
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
26
|
+
chat_id INTEGER NOT NULL,
|
|
27
|
+
user_id INTEGER NOT NULL,
|
|
28
|
+
username TEXT,
|
|
29
|
+
role TEXT NOT NULL CHECK(role IN ('user', 'assistant', 'system')),
|
|
30
|
+
content TEXT NOT NULL,
|
|
31
|
+
is_private INTEGER NOT NULL DEFAULT 0,
|
|
32
|
+
reply_to_id INTEGER,
|
|
33
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
33
34
|
);
|
|
34
35
|
|
|
35
36
|
CREATE INDEX IF NOT EXISTS idx_messages_chat_id ON messages(chat_id, created_at);
|
|
@@ -60,9 +61,14 @@ async def init_db() -> None:
|
|
|
60
61
|
Create the messages and private_mode tables if they do not exist.
|
|
61
62
|
Enables WAL journal mode for safe concurrent reads and writes.
|
|
62
63
|
Safe to call repeatedly - all statements use IF NOT EXISTS.
|
|
64
|
+
Migrates existing databases by adding reply_to_id if not present.
|
|
63
65
|
"""
|
|
64
66
|
async with aiosqlite.connect(get_db_path()) as db:
|
|
65
67
|
await db.executescript(_DDL)
|
|
68
|
+
# Migration: add reply_to_id to existing databases that predate this column.
|
|
69
|
+
existing = {row[1] async for row in await db.execute("PRAGMA table_info(messages)")}
|
|
70
|
+
if 'reply_to_id' not in existing:
|
|
71
|
+
await db.execute("ALTER TABLE messages ADD COLUMN reply_to_id INTEGER")
|
|
66
72
|
await db.commit()
|
|
67
73
|
|
|
68
74
|
async def insert_message(
|
|
@@ -72,9 +78,10 @@ async def insert_message(
|
|
|
72
78
|
role: str,
|
|
73
79
|
content: str,
|
|
74
80
|
is_private: bool = False,
|
|
75
|
-
|
|
81
|
+
reply_to_id: Optional[int] = None,
|
|
82
|
+
) -> int:
|
|
76
83
|
"""
|
|
77
|
-
Persist a single message row.
|
|
84
|
+
Persist a single message row and return its database id.
|
|
78
85
|
|
|
79
86
|
Args:
|
|
80
87
|
chat_id: Telegram chat ID (negative for groups, positive/==user_id for private).
|
|
@@ -83,14 +90,19 @@ async def insert_message(
|
|
|
83
90
|
role: 'user', 'assistant', or 'system'.
|
|
84
91
|
content: Message text.
|
|
85
92
|
is_private: If True, this message is excluded from group context loading.
|
|
93
|
+
reply_to_id: Database id of the message this is a reply to (None if not a reply).
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
The integer id of the newly inserted row.
|
|
86
97
|
"""
|
|
87
98
|
async with aiosqlite.connect(get_db_path()) as db:
|
|
88
|
-
await db.execute(
|
|
89
|
-
"INSERT INTO messages (chat_id, user_id, username, role, content, is_private) "
|
|
90
|
-
"VALUES (?, ?, ?, ?, ?, ?)",
|
|
91
|
-
(chat_id, user_id, username, role, content, int(is_private)),
|
|
99
|
+
cursor = await db.execute(
|
|
100
|
+
"INSERT INTO messages (chat_id, user_id, username, role, content, is_private, reply_to_id) "
|
|
101
|
+
"VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
102
|
+
(chat_id, user_id, username, role, content, int(is_private), reply_to_id),
|
|
92
103
|
)
|
|
93
104
|
await db.commit()
|
|
105
|
+
return cursor.lastrowid
|
|
94
106
|
|
|
95
107
|
async def load_messages_for_chat(
|
|
96
108
|
chat_id: int,
|
|
@@ -338,51 +350,30 @@ async def load_shared_group_context(
|
|
|
338
350
|
all_rows.sort(key=lambda r: (r[2], r[3]))
|
|
339
351
|
return [{"role": row[0], "content": row[1]} for row in all_rows]
|
|
340
352
|
|
|
341
|
-
async def delete_paired_bot_replies(chat_id: int, bot_id: int) -> None:
|
|
342
|
-
"""
|
|
343
|
-
Delete bot reply rows in a group chat that are paired with deleted user messages or orphaned.
|
|
344
353
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
them). These are caught by the orphan query below.
|
|
349
|
-
2. Pre-existing orphans: assistant rows left behind by earlier /forget calls that deleted
|
|
350
|
-
user rows but not their paired replies. Cleaned up opportunistically on every group
|
|
351
|
-
/forget to heal historical debris.
|
|
354
|
+
async def delete_bot_replies_for_user(user_id: int) -> None:
|
|
355
|
+
"""
|
|
356
|
+
Delete assistant rows whose reply_to_id references any message from this user.
|
|
352
357
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
preceding row unambiguously identifies the pairing. Other users' conversations are unaffected
|
|
357
|
-
as long as their user rows remain (their user rows serve as the preceding message for their
|
|
358
|
-
own bot replies).
|
|
358
|
+
Must be called before delete_messages_for_user so that the user's message ids
|
|
359
|
+
still exist for the subquery to match against. Rows with reply_to_id = NULL
|
|
360
|
+
(pre-migration messages) are unaffected.
|
|
359
361
|
|
|
360
362
|
Args:
|
|
361
|
-
|
|
362
|
-
bot_id: Telegram user ID of the bot account (identifies assistant rows by user_id).
|
|
363
|
+
user_id: Telegram user ID whose bot replies should be removed.
|
|
363
364
|
"""
|
|
364
365
|
async with aiosqlite.connect(get_db_path()) as db:
|
|
365
366
|
await db.execute(
|
|
366
|
-
"DELETE FROM messages "
|
|
367
|
-
"
|
|
368
|
-
|
|
369
|
-
" SELECT 1 FROM messages prev "
|
|
370
|
-
" WHERE prev.chat_id = messages.chat_id "
|
|
371
|
-
" AND prev.id = ("
|
|
372
|
-
" SELECT MAX(m2.id) FROM messages m2 "
|
|
373
|
-
" WHERE m2.chat_id = messages.chat_id "
|
|
374
|
-
" AND m2.id < messages.id"
|
|
375
|
-
" ) "
|
|
376
|
-
" AND prev.role = 'user'"
|
|
377
|
-
")",
|
|
378
|
-
(chat_id, bot_id),
|
|
367
|
+
"DELETE FROM messages WHERE role = 'assistant' "
|
|
368
|
+
"AND reply_to_id IN (SELECT id FROM messages WHERE user_id = ?)",
|
|
369
|
+
(user_id,),
|
|
379
370
|
)
|
|
380
371
|
await db.commit()
|
|
381
372
|
|
|
382
373
|
async def delete_messages_for_user(user_id: int) -> None:
|
|
383
374
|
"""
|
|
384
375
|
Delete all message rows for a given user_id across all chats.
|
|
385
|
-
Used by /forget
|
|
376
|
+
Used by /forget - call delete_bot_replies_for_user first to clean up linked bot replies.
|
|
386
377
|
"""
|
|
387
378
|
async with aiosqlite.connect(get_db_path()) as db:
|
|
388
379
|
await db.execute("DELETE FROM messages WHERE user_id = ?", (user_id,))
|
|
@@ -27,27 +27,6 @@ def handle_greetings(text: str) -> Optional[str]:
|
|
|
27
27
|
return f'{word}!'
|
|
28
28
|
return None
|
|
29
29
|
|
|
30
|
-
def handle_read_receipt(text: str) -> bool:
|
|
31
|
-
"""
|
|
32
|
-
Detect explicit user requests for read receipt acknowledgement.
|
|
33
|
-
|
|
34
|
-
Matches explicit acknowledgement trigger phrases: "acknowledge", "acknowledged", "mark as read",
|
|
35
|
-
"read receipt", "did you read", "did you get that", and "confirm you received". Uses word-boundary
|
|
36
|
-
matching to avoid false positives from casual conversation (e.g., "unacknowledged" does not match).
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
text: The message text to analyze (typically after bot mention/nickname stripped).
|
|
40
|
-
|
|
41
|
-
Returns:
|
|
42
|
-
True if the message matches a read receipt trigger phrase; False otherwise.
|
|
43
|
-
"""
|
|
44
|
-
text_clean = re.sub(r'[^\w\s]', '', text.lower().strip())
|
|
45
|
-
triggers = {
|
|
46
|
-
'acknowledge', 'acknowledged', 'mark as read',
|
|
47
|
-
'read receipt', 'did you read', 'did you get that',
|
|
48
|
-
'confirm you received',
|
|
49
|
-
}
|
|
50
|
-
return any(re.search(rf'\b{re.escape(t)}\b', text_clean) is not None for t in triggers)
|
|
51
30
|
|
|
52
31
|
def handle_common_queries(text: str) -> Optional[str]:
|
|
53
32
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TeLLMgramBot
|
|
3
|
-
Version: 3.4.
|
|
3
|
+
Version: 3.4.2
|
|
4
4
|
Summary: LLM-powered Telegram bot (OpenAI + Anthropic)
|
|
5
5
|
Home-page: https://github.com/Digital-Heresy/TeLLMgramBot
|
|
6
6
|
Author: Digital Heresy
|
|
@@ -144,14 +144,7 @@ Each file with the associated API key will update its respective environment var
|
|
|
144
144
|
## Commands and Interactions
|
|
145
145
|
|
|
146
146
|
### Read Receipt Acknowledgement (Group Chats Only)
|
|
147
|
-
In group and supergroup chats, you
|
|
148
|
-
|
|
149
|
-
- `@botname acknowledge`
|
|
150
|
-
- `botname mark as read`
|
|
151
|
-
- `BN did you read that?`
|
|
152
|
-
- `botname confirm you received`
|
|
153
|
-
|
|
154
|
-
The bot will respond with a 👀 reaction emoji on your message (if the Telegram client supports message reactions), or with a short "Got it!" reply as fallback. Read receipt requests are processed immediately without touching the conversation context, making them ideal for confirming the bot is online without disrupting group flow.
|
|
147
|
+
In group and supergroup chats, whenever you mention the bot by username (`@botname`), nickname, or initials, the bot immediately responds with a 👀 reaction emoji on your message to confirm it is online, followed by a full LLM response. The reaction uses the Telegram message reaction API when available (👀 emoji), or falls back to a short "Got it!" text reply for older clients that don't support reactions.
|
|
155
148
|
|
|
156
149
|
## Bot Setup
|
|
157
150
|
This library includes an example script `test_local.py`, which uses files from the folders `configs` and `prompts` for TeLLMgramBot to process.
|
|
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
|