telegroq 0.0.1__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.
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.0.1] - 2026-10-30
8
+
9
+ ### Added
10
+ - `telegroq.createBot(token)` returning `TeleGroqBot`, a wrapper around `telebot.TeleBot`.
11
+ - `bot.set_groq_key(api_key, model=...)` and `telegroq.configure_groq(...)`.
12
+ - `telegroq.setCh(system_prompt, max_history)` returning `Character`: per-user memory,
13
+ `get_response` / `ask` (with a `Reply` status object), async variants, `clear_history`.
14
+ - Friendly fallback messages for Groq rate limits, bad keys, timeouts and outages.
15
+ - `@bot.cmd`, `@bot.on_message`, `@bot.callback` (exact / prefix / regex), `@bot.on_error`,
16
+ `bot.auto_reply(character)`; handlers receive `(user, msg)` / `(user, call)`.
17
+ - `Message` and `CallbackContext` wrappers (`msg.text` is always a `str`, `call.edit()`,
18
+ automatic callback answers).
19
+ - Inline keyboard helpers `button()` and `inline_keyboard()`.
20
+ - Pluggable history stores: `MemoryHistoryStore`, `SQLiteHistoryStore`, `HistoryStore` base class.
21
+ - `bot.send_message` / `bot.reply`: automatic splitting of long texts, plain-text retry on
22
+ formatting errors, `reply_markup` on the last part.
23
+ - Command menu publishing (`@bot.cmd("start", description="...")`), non-text message notice.
telegroq-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AYM
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,246 @@
1
+ Metadata-Version: 2.5
2
+ Name: telegroq
3
+ Version: 0.0.1
4
+ Summary: Deploy a Groq-powered Telegram bot with a custom persona in a few lines (pyTelegramBotAPI + Groq).
5
+ Keywords: telegram,bot,groq,llm,chatbot,ai,persona,pytelegrambotapi,telebot
6
+ Author: AYM
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-Expression: MIT
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Communications :: Chat
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ License-File: LICENSE
23
+ Requires-Dist: pyTelegramBotAPI>=4.16
24
+ Requires-Dist: groq>=0.9
25
+ Requires-Dist: pytest>=7.0 ; extra == "dev"
26
+ Requires-Dist: ruff>=0.5 ; extra == "dev"
27
+ Requires-Dist: flit>=3.11 ; extra == "dev"
28
+ Requires-Dist: twine>=6.1 ; extra == "dev"
29
+ Project-URL: Changelog, https://github.com/kkkik/telegroq/blob/main/CHANGELOG.md
30
+ Project-URL: Homepage, https://github.com/kkkik/telegroq
31
+ Project-URL: Issues, https://github.com/kkkik/telegroq/issues
32
+ Project-URL: Source, https://github.com/kkkik/telegroq
33
+ Provides-Extra: dev
34
+ Import-Name: telegroq
35
+
36
+ # 🤖 telegroq
37
+
38
+ **Give your Telegram bot a brain and a personality - in 5 lines of Python.**
39
+
40
+ [![PyPI](https://img.shields.io/pypi/v/telegroq.svg)](https://pypi.org/project/telegroq/)
41
+ ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
42
+ ![License](https://img.shields.io/badge/license-MIT-green)
43
+ [![CI](https://github.com/kkkik/telegroq/actions/workflows/ci.yml/badge.svg)](https://github.com/kkkik/telegroq/actions/workflows/ci.yml)
44
+
45
+ `telegroq` connects [pyTelegramBotAPI](https://github.com/eternnoir/pyTelegramBotAPI) (`telebot`) with the
46
+ blazing-fast [Groq API](https://console.groq.com/docs). Write handlers with simple decorators, create
47
+ characters that remember each user, and let the library deal with rate limits, bad keys, long messages and
48
+ photos or stickers - so your bot keeps running no matter what.
49
+
50
+ ```python
51
+ import telegroq
52
+
53
+ bot = telegroq.createBot("TELEGRAM_BOT_TOKEN")
54
+ bot.set_groq_key("GROQ_API_KEY")
55
+ bot.auto_reply(telegroq.setCh("You are a witty, friendly assistant."))
56
+ bot.run()
57
+ ```
58
+
59
+ That's a complete AI chatbot. 🎉
60
+
61
+ > Unofficial project. Not affiliated with or endorsed by Telegram or Groq.
62
+
63
+ ## ✨ Features
64
+
65
+ - 🎭 **Characters with memory** - a persona (system prompt) with per-user conversation history.
66
+ - ⚡ **Groq-powered** - fast answers from Llama and other models hosted on Groq.
67
+ - 🧩 **Friendly decorators** - `@bot.cmd`, `@bot.on_message`, `@bot.callback`, `@bot.on_error`.
68
+ - 🔘 **Inline buttons made easy** - keyboards and callbacks in a few lines.
69
+ - 💾 **Pluggable storage** - in-memory by default, SQLite built in, or bring your own.
70
+ - 🛡️ **Never crashes on errors** - rate limits, timeouts, bad keys and handler bugs are handled gracefully.
71
+ - ✂️ **Smart sending** - long replies are split automatically; broken Markdown/HTML falls back to plain text.
72
+ - 🧪 **Typed and tested** - ships with type hints (`py.typed`) and a test suite that needs no network.
73
+
74
+ ## 📦 Install
75
+
76
+ ```bash
77
+ pip install telegroq
78
+ ```
79
+
80
+ Requires Python 3.10+. You need a Telegram bot token from [@BotFather](https://t.me/BotFather) and a Groq API key
81
+ from [console.groq.com](https://console.groq.com/keys).
82
+
83
+ ## 🚀 Quickstart
84
+
85
+ ```python
86
+ import telegroq
87
+
88
+ bot = telegroq.createBot("123456:TELEGRAM_BOT_TOKEN")
89
+ bot.set_groq_key("gsk_...", model="llama-3.3-70b-versatile")
90
+
91
+ character = telegroq.setCh(
92
+ system_prompt="You are a funny, witty, and energetic AI assistant.",
93
+ max_history=10,
94
+ )
95
+
96
+ @bot.cmd("/start")
97
+ def start(user, msg):
98
+ bot.send_message(user.id, f"Hello {user.first_name}! I am ready.")
99
+
100
+ @bot.cmd("/reset")
101
+ def reset(user, msg):
102
+ character.clear_history(user.id)
103
+ bot.send_message(user.id, "Conversation memory cleared!")
104
+
105
+ @bot.on_message()
106
+ def chat(user, msg):
107
+ bot.reply(user.id, character.get_response(user.id, msg.text), message_id=msg.message_id)
108
+
109
+ bot.run()
110
+ ```
111
+
112
+ Want it even shorter? Replace the `chat` handler with `bot.auto_reply(character)`.
113
+
114
+ 🔐 Keep secrets out of your code: read them from environment variables, as in
115
+ [`examples/main.py`](https://github.com/kkkik/telegroq/blob/main/examples/main.py).
116
+
117
+ ## 🧠 Core ideas
118
+
119
+ ### Handlers receive `(user, msg)`
120
+
121
+ - `user` is telebot's `User` (`.id`, `.username`, `.first_name`, ...).
122
+ - `msg` is a `telegroq.Message`. `msg.text` is **always a string** (`""` for photos, stickers and voice notes),
123
+ `msg.args` is the text after a command, `msg.chat_id` / `msg.is_private` describe the chat, and
124
+ `msg.reply(text)` answers in the chat the message came from. Everything else (`msg.message_id`,
125
+ `msg.caption`, `msg.photo`, ...) is forwarded to the raw telebot message (`msg.raw`).
126
+
127
+ > **Groups:** `bot.send_message(user.id, ...)` writes to the user's *private* chat. In group chats use
128
+ > `msg.reply(...)` or `bot.send_message(msg.chat_id, ...)`. Restrict AI handlers with
129
+ > `@bot.on_message(chat_types=["private"])` if you do not want the bot to answer everything in a group.
130
+
131
+ ### Commands
132
+
133
+ ```python
134
+ @bot.cmd("/start", description="Say hello") # description appears in Telegram's command menu
135
+ @bot.cmd("help", "about") # several names, slash optional, any letter case
136
+ ```
137
+
138
+ Registered commands are never delivered to `@bot.on_message()`, whatever order you declare them in.
139
+
140
+ ### 🎭 Characters (personas with memory)
141
+
142
+ ```python
143
+ character = telegroq.setCh("You are a pirate.", max_history=10, temperature=0.9, model="llama-3.1-8b-instant")
144
+
145
+ character.get_response(user.id, "Hi!") # -> str (never raises on Groq errors)
146
+ character.ask(user.id, "Hi!") # -> Reply(text, ok, error_kind, error)
147
+ await character.aget_response(user.id, "Hi") # async variant (runs in a worker thread)
148
+ character.clear_history(user.id)
149
+ character.get_history(user.id)
150
+ ```
151
+
152
+ - History is kept **per user id** and trimmed to the last `max_history` messages (`0` = stateless).
153
+ Calls for the same user are serialised, so quick successive messages cannot scramble the conversation.
154
+ - `system_prompt` may also be a callable `(user_id) -> str` for per-user prompts.
155
+ - `extra_params={"top_p": 0.9}` is forwarded to `chat.completions.create`.
156
+ - Pass `client=` to use your own Groq-compatible client, or `bot=` / `.bind(bot)` to pick which bot's key is used.
157
+ Without either, the most recently configured key is used.
158
+
159
+ ### 🔘 Inline buttons and callbacks
160
+
161
+ ```python
162
+ from telegroq import inline_keyboard
163
+
164
+ @bot.cmd("persona")
165
+ def choose(user, msg):
166
+ bot.send_message(msg.chat_id, "Who am I?", reply_markup=inline_keyboard(
167
+ [("Pirate", "persona:pirate"), ("Poet", "persona:poet")],
168
+ ))
169
+
170
+ @bot.callback(prefix="persona:") # or @bot.callback("exact:data") / @bot.callback(regex=...)
171
+ def picked(user, call):
172
+ call.answer(f"OK, {call.args}!") # optional: telegroq answers for you if you don't
173
+ call.edit(f"Persona: {call.args}") # edit the message that holds the buttons
174
+ ```
175
+
176
+ `call` is a `CallbackContext` with `.data`, `.args` (data after the prefix), `.message`, `.answer()`,
177
+ `.edit()`, `.edit_keyboard()` and `.reply()`. The button's loading spinner is cleared automatically.
178
+ `button(text, callback_data)` enforces Telegram's 64-byte limit; `button(text, url=...)` makes link buttons.
179
+
180
+ A complete persona-switching bot lives in
181
+ [`examples/persona_switcher.py`](https://github.com/kkkik/telegroq/blob/main/examples/persona_switcher.py).
182
+
183
+ ### 💾 Persistent memory
184
+
185
+ ```python
186
+ from telegroq import SQLiteHistoryStore
187
+
188
+ character = telegroq.setCh("...", store=SQLiteHistoryStore("history.db"))
189
+ ```
190
+
191
+ Implement `telegroq.HistoryStore` (`load`, `save`, `clear`) to use Redis, Postgres, etc. The default
192
+ `MemoryHistoryStore` forgets everything on restart.
193
+
194
+ ### 🛡️ Errors never take the bot down
195
+
196
+ | Situation | What happens |
197
+ |---|---|
198
+ | Groq rate limit, bad key, timeout, outage | The user gets a friendly fallback message; the failed turn is not saved. Customise with `setCh(..., fallbacks={"rate_limit": "..."})`. |
199
+ | Your handler raises | The error is logged, the user gets `bot.error_reply` (or run your own `@bot.on_error`), polling continues. |
200
+ | Photo, sticker, voice note, ... | `msg.text == ""`; in private chats the user gets `bot.non_text_reply` unless you handle that type (`@bot.on_message(content_types=["photo"])`). Set it to `None` to stay silent. |
201
+ | Model output breaks Markdown/HTML | `send_message(..., parse_mode=...)` retries as plain text. |
202
+ | Reply longer than 4096 characters | Split into several messages automatically. |
203
+ | Network errors while polling | `bot.run()` keeps retrying. |
204
+
205
+ Forgetting `set_groq_key` raises `telegroq.ConfigurationError` - a setup bug, not a runtime failure.
206
+
207
+ ### 📝 Logging
208
+
209
+ telegroq logs to the `telegroq` logger and is silent by default. Enable it with
210
+ `logging.basicConfig(level=logging.INFO)`.
211
+
212
+ ## 📚 API at a glance
213
+
214
+ | | |
215
+ |---|---|
216
+ | `telegroq.createBot(token, **telebot_kwargs)` | Create a `TeleGroqBot` (wraps `telebot.TeleBot`, available as `bot.telebot`; unknown attributes forward to it). |
217
+ | `bot.set_groq_key(api_key, model=...)` | Create the Groq client (`timeout`, `max_retries` optional). |
218
+ | `telegroq.setCh(system_prompt, max_history=10, **opts)` | Create a `Character`. |
219
+ | `@bot.cmd(*names, description=None, chat_types=None)` | Command handler `fn(user, msg)`. |
220
+ | `@bot.on_message(content_types=None, chat_types=None, regexp=None, func=None, typing=True)` | Wildcard handler `fn(user, msg)`. |
221
+ | `bot.auto_reply(character, ...)` | Answer every text message with a character. |
222
+ | `@bot.callback(*data, prefix=None, regex=None)` | Inline-button handler `fn(user, call)`. |
223
+ | `@bot.on_error` | `fn(exc, user, update)` called when a handler raises. |
224
+ | `bot.send_message(user_id, text, **kw)` / `bot.reply(user_id, text, message_id=None, **kw)` | Sending helpers (splitting, plain-text retry). |
225
+ | `bot.run()` / `bot.stop()` | Start / stop long polling. |
226
+ | `telegroq.button`, `telegroq.inline_keyboard` | Keyboard helpers. |
227
+ | `HistoryStore`, `MemoryHistoryStore`, `SQLiteHistoryStore` | Conversation storage. |
228
+
229
+ Every public function and class has a docstring - try `help(telegroq.Character)`.
230
+
231
+ ## 🛠️ Development
232
+
233
+ ```bash
234
+ git clone https://github.com/kkkik/telegroq && cd telegroq
235
+ python -m pip install -e ".[dev]"
236
+ pytest # no network or API keys needed (Telegram and Groq are faked)
237
+ ruff check .
238
+ ```
239
+
240
+ Contributions and ideas are welcome - open an
241
+ [issue](https://github.com/kkkik/telegroq/issues) or a pull request.
242
+
243
+ ## 📄 License
244
+
245
+ MIT - see [LICENSE](https://github.com/kkkik/telegroq/blob/main/LICENSE).
246
+
@@ -0,0 +1,210 @@
1
+ # 🤖 telegroq
2
+
3
+ **Give your Telegram bot a brain and a personality - in 5 lines of Python.**
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/telegroq.svg)](https://pypi.org/project/telegroq/)
6
+ ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
7
+ ![License](https://img.shields.io/badge/license-MIT-green)
8
+ [![CI](https://github.com/kkkik/telegroq/actions/workflows/ci.yml/badge.svg)](https://github.com/kkkik/telegroq/actions/workflows/ci.yml)
9
+
10
+ `telegroq` connects [pyTelegramBotAPI](https://github.com/eternnoir/pyTelegramBotAPI) (`telebot`) with the
11
+ blazing-fast [Groq API](https://console.groq.com/docs). Write handlers with simple decorators, create
12
+ characters that remember each user, and let the library deal with rate limits, bad keys, long messages and
13
+ photos or stickers - so your bot keeps running no matter what.
14
+
15
+ ```python
16
+ import telegroq
17
+
18
+ bot = telegroq.createBot("TELEGRAM_BOT_TOKEN")
19
+ bot.set_groq_key("GROQ_API_KEY")
20
+ bot.auto_reply(telegroq.setCh("You are a witty, friendly assistant."))
21
+ bot.run()
22
+ ```
23
+
24
+ That's a complete AI chatbot. 🎉
25
+
26
+ > Unofficial project. Not affiliated with or endorsed by Telegram or Groq.
27
+
28
+ ## ✨ Features
29
+
30
+ - 🎭 **Characters with memory** - a persona (system prompt) with per-user conversation history.
31
+ - ⚡ **Groq-powered** - fast answers from Llama and other models hosted on Groq.
32
+ - 🧩 **Friendly decorators** - `@bot.cmd`, `@bot.on_message`, `@bot.callback`, `@bot.on_error`.
33
+ - 🔘 **Inline buttons made easy** - keyboards and callbacks in a few lines.
34
+ - 💾 **Pluggable storage** - in-memory by default, SQLite built in, or bring your own.
35
+ - 🛡️ **Never crashes on errors** - rate limits, timeouts, bad keys and handler bugs are handled gracefully.
36
+ - ✂️ **Smart sending** - long replies are split automatically; broken Markdown/HTML falls back to plain text.
37
+ - 🧪 **Typed and tested** - ships with type hints (`py.typed`) and a test suite that needs no network.
38
+
39
+ ## 📦 Install
40
+
41
+ ```bash
42
+ pip install telegroq
43
+ ```
44
+
45
+ Requires Python 3.10+. You need a Telegram bot token from [@BotFather](https://t.me/BotFather) and a Groq API key
46
+ from [console.groq.com](https://console.groq.com/keys).
47
+
48
+ ## 🚀 Quickstart
49
+
50
+ ```python
51
+ import telegroq
52
+
53
+ bot = telegroq.createBot("123456:TELEGRAM_BOT_TOKEN")
54
+ bot.set_groq_key("gsk_...", model="llama-3.3-70b-versatile")
55
+
56
+ character = telegroq.setCh(
57
+ system_prompt="You are a funny, witty, and energetic AI assistant.",
58
+ max_history=10,
59
+ )
60
+
61
+ @bot.cmd("/start")
62
+ def start(user, msg):
63
+ bot.send_message(user.id, f"Hello {user.first_name}! I am ready.")
64
+
65
+ @bot.cmd("/reset")
66
+ def reset(user, msg):
67
+ character.clear_history(user.id)
68
+ bot.send_message(user.id, "Conversation memory cleared!")
69
+
70
+ @bot.on_message()
71
+ def chat(user, msg):
72
+ bot.reply(user.id, character.get_response(user.id, msg.text), message_id=msg.message_id)
73
+
74
+ bot.run()
75
+ ```
76
+
77
+ Want it even shorter? Replace the `chat` handler with `bot.auto_reply(character)`.
78
+
79
+ 🔐 Keep secrets out of your code: read them from environment variables, as in
80
+ [`examples/main.py`](https://github.com/kkkik/telegroq/blob/main/examples/main.py).
81
+
82
+ ## 🧠 Core ideas
83
+
84
+ ### Handlers receive `(user, msg)`
85
+
86
+ - `user` is telebot's `User` (`.id`, `.username`, `.first_name`, ...).
87
+ - `msg` is a `telegroq.Message`. `msg.text` is **always a string** (`""` for photos, stickers and voice notes),
88
+ `msg.args` is the text after a command, `msg.chat_id` / `msg.is_private` describe the chat, and
89
+ `msg.reply(text)` answers in the chat the message came from. Everything else (`msg.message_id`,
90
+ `msg.caption`, `msg.photo`, ...) is forwarded to the raw telebot message (`msg.raw`).
91
+
92
+ > **Groups:** `bot.send_message(user.id, ...)` writes to the user's *private* chat. In group chats use
93
+ > `msg.reply(...)` or `bot.send_message(msg.chat_id, ...)`. Restrict AI handlers with
94
+ > `@bot.on_message(chat_types=["private"])` if you do not want the bot to answer everything in a group.
95
+
96
+ ### Commands
97
+
98
+ ```python
99
+ @bot.cmd("/start", description="Say hello") # description appears in Telegram's command menu
100
+ @bot.cmd("help", "about") # several names, slash optional, any letter case
101
+ ```
102
+
103
+ Registered commands are never delivered to `@bot.on_message()`, whatever order you declare them in.
104
+
105
+ ### 🎭 Characters (personas with memory)
106
+
107
+ ```python
108
+ character = telegroq.setCh("You are a pirate.", max_history=10, temperature=0.9, model="llama-3.1-8b-instant")
109
+
110
+ character.get_response(user.id, "Hi!") # -> str (never raises on Groq errors)
111
+ character.ask(user.id, "Hi!") # -> Reply(text, ok, error_kind, error)
112
+ await character.aget_response(user.id, "Hi") # async variant (runs in a worker thread)
113
+ character.clear_history(user.id)
114
+ character.get_history(user.id)
115
+ ```
116
+
117
+ - History is kept **per user id** and trimmed to the last `max_history` messages (`0` = stateless).
118
+ Calls for the same user are serialised, so quick successive messages cannot scramble the conversation.
119
+ - `system_prompt` may also be a callable `(user_id) -> str` for per-user prompts.
120
+ - `extra_params={"top_p": 0.9}` is forwarded to `chat.completions.create`.
121
+ - Pass `client=` to use your own Groq-compatible client, or `bot=` / `.bind(bot)` to pick which bot's key is used.
122
+ Without either, the most recently configured key is used.
123
+
124
+ ### 🔘 Inline buttons and callbacks
125
+
126
+ ```python
127
+ from telegroq import inline_keyboard
128
+
129
+ @bot.cmd("persona")
130
+ def choose(user, msg):
131
+ bot.send_message(msg.chat_id, "Who am I?", reply_markup=inline_keyboard(
132
+ [("Pirate", "persona:pirate"), ("Poet", "persona:poet")],
133
+ ))
134
+
135
+ @bot.callback(prefix="persona:") # or @bot.callback("exact:data") / @bot.callback(regex=...)
136
+ def picked(user, call):
137
+ call.answer(f"OK, {call.args}!") # optional: telegroq answers for you if you don't
138
+ call.edit(f"Persona: {call.args}") # edit the message that holds the buttons
139
+ ```
140
+
141
+ `call` is a `CallbackContext` with `.data`, `.args` (data after the prefix), `.message`, `.answer()`,
142
+ `.edit()`, `.edit_keyboard()` and `.reply()`. The button's loading spinner is cleared automatically.
143
+ `button(text, callback_data)` enforces Telegram's 64-byte limit; `button(text, url=...)` makes link buttons.
144
+
145
+ A complete persona-switching bot lives in
146
+ [`examples/persona_switcher.py`](https://github.com/kkkik/telegroq/blob/main/examples/persona_switcher.py).
147
+
148
+ ### 💾 Persistent memory
149
+
150
+ ```python
151
+ from telegroq import SQLiteHistoryStore
152
+
153
+ character = telegroq.setCh("...", store=SQLiteHistoryStore("history.db"))
154
+ ```
155
+
156
+ Implement `telegroq.HistoryStore` (`load`, `save`, `clear`) to use Redis, Postgres, etc. The default
157
+ `MemoryHistoryStore` forgets everything on restart.
158
+
159
+ ### 🛡️ Errors never take the bot down
160
+
161
+ | Situation | What happens |
162
+ |---|---|
163
+ | Groq rate limit, bad key, timeout, outage | The user gets a friendly fallback message; the failed turn is not saved. Customise with `setCh(..., fallbacks={"rate_limit": "..."})`. |
164
+ | Your handler raises | The error is logged, the user gets `bot.error_reply` (or run your own `@bot.on_error`), polling continues. |
165
+ | Photo, sticker, voice note, ... | `msg.text == ""`; in private chats the user gets `bot.non_text_reply` unless you handle that type (`@bot.on_message(content_types=["photo"])`). Set it to `None` to stay silent. |
166
+ | Model output breaks Markdown/HTML | `send_message(..., parse_mode=...)` retries as plain text. |
167
+ | Reply longer than 4096 characters | Split into several messages automatically. |
168
+ | Network errors while polling | `bot.run()` keeps retrying. |
169
+
170
+ Forgetting `set_groq_key` raises `telegroq.ConfigurationError` - a setup bug, not a runtime failure.
171
+
172
+ ### 📝 Logging
173
+
174
+ telegroq logs to the `telegroq` logger and is silent by default. Enable it with
175
+ `logging.basicConfig(level=logging.INFO)`.
176
+
177
+ ## 📚 API at a glance
178
+
179
+ | | |
180
+ |---|---|
181
+ | `telegroq.createBot(token, **telebot_kwargs)` | Create a `TeleGroqBot` (wraps `telebot.TeleBot`, available as `bot.telebot`; unknown attributes forward to it). |
182
+ | `bot.set_groq_key(api_key, model=...)` | Create the Groq client (`timeout`, `max_retries` optional). |
183
+ | `telegroq.setCh(system_prompt, max_history=10, **opts)` | Create a `Character`. |
184
+ | `@bot.cmd(*names, description=None, chat_types=None)` | Command handler `fn(user, msg)`. |
185
+ | `@bot.on_message(content_types=None, chat_types=None, regexp=None, func=None, typing=True)` | Wildcard handler `fn(user, msg)`. |
186
+ | `bot.auto_reply(character, ...)` | Answer every text message with a character. |
187
+ | `@bot.callback(*data, prefix=None, regex=None)` | Inline-button handler `fn(user, call)`. |
188
+ | `@bot.on_error` | `fn(exc, user, update)` called when a handler raises. |
189
+ | `bot.send_message(user_id, text, **kw)` / `bot.reply(user_id, text, message_id=None, **kw)` | Sending helpers (splitting, plain-text retry). |
190
+ | `bot.run()` / `bot.stop()` | Start / stop long polling. |
191
+ | `telegroq.button`, `telegroq.inline_keyboard` | Keyboard helpers. |
192
+ | `HistoryStore`, `MemoryHistoryStore`, `SQLiteHistoryStore` | Conversation storage. |
193
+
194
+ Every public function and class has a docstring - try `help(telegroq.Character)`.
195
+
196
+ ## 🛠️ Development
197
+
198
+ ```bash
199
+ git clone https://github.com/kkkik/telegroq && cd telegroq
200
+ python -m pip install -e ".[dev]"
201
+ pytest # no network or API keys needed (Telegram and Groq are faked)
202
+ ruff check .
203
+ ```
204
+
205
+ Contributions and ideas are welcome - open an
206
+ [issue](https://github.com/kkkik/telegroq/issues) or a pull request.
207
+
208
+ ## 📄 License
209
+
210
+ MIT - see [LICENSE](https://github.com/kkkik/telegroq/blob/main/LICENSE).
@@ -0,0 +1,61 @@
1
+ """Minimal Groq-powered Telegram bot with a persona.
2
+
3
+ Setup:
4
+ pip install telegroq
5
+ export TELEGRAM_BOT_TOKEN="123456:ABC..." # from @BotFather
6
+ export GROQ_API_KEY="gsk_..." # from console.groq.com
7
+ python main.py
8
+ """
9
+
10
+ import logging
11
+ import os
12
+ import sys
13
+
14
+ import telegroq
15
+
16
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
17
+
18
+ TELEGRAM_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN")
19
+ GROQ_KEY = os.environ.get("GROQ_API_KEY")
20
+ if not TELEGRAM_TOKEN or not GROQ_KEY:
21
+ sys.exit("Set TELEGRAM_BOT_TOKEN and GROQ_API_KEY first (see the top of this file).")
22
+
23
+ # 1. Initialize the bot
24
+ bot = telegroq.createBot(TELEGRAM_TOKEN)
25
+ bot.set_groq_key(GROQ_KEY, model="llama-3.3-70b-versatile")
26
+
27
+ # 2. Define the AI persona (remembers the last 10 messages per user)
28
+ character = telegroq.setCh(
29
+ system_prompt=(
30
+ "You are a funny, witty, and energetic AI assistant. "
31
+ "Keep answers short and punchy, and use plain text (no Markdown)."
32
+ ),
33
+ max_history=10,
34
+ )
35
+
36
+
37
+ # 3. Custom commands (the description shows up in Telegram's command menu)
38
+ @bot.cmd("/start", description="Say hello")
39
+ def start(user, msg):
40
+ bot.send_message(user.id, f"Hello {user.first_name}! I am ready. Send me anything.")
41
+
42
+
43
+ @bot.cmd("/reset", description="Forget our conversation")
44
+ def reset(user, msg):
45
+ character.clear_history(user.id)
46
+ bot.send_message(user.id, "Conversation memory cleared!")
47
+
48
+
49
+ # 4. AI auto-responder: every text message that is not a command ends up here.
50
+ # Photos, stickers and voice notes get a polite "text only" notice automatically.
51
+ @bot.on_message()
52
+ def chat_handler(user, msg):
53
+ reply = character.get_response(user.id, msg.text)
54
+ bot.reply(user.id, reply, message_id=msg.message_id)
55
+
56
+ # Shorter alternative: replace this whole handler with bot.auto_reply(character)
57
+
58
+
59
+ # 5. Launch (Ctrl+C to stop)
60
+ if __name__ == "__main__":
61
+ bot.run()
@@ -0,0 +1,75 @@
1
+ """Inline buttons choose the persona; conversations survive restarts (SQLite).
2
+
3
+ Setup:
4
+ pip install telegroq
5
+ export TELEGRAM_BOT_TOKEN="123456:ABC..."
6
+ export GROQ_API_KEY="gsk_..."
7
+ python persona_switcher.py
8
+
9
+ Try /persona, press a button, then chat.
10
+ """
11
+
12
+ import logging
13
+ import os
14
+ import sys
15
+
16
+ import telegroq
17
+ from telegroq import SQLiteHistoryStore, inline_keyboard
18
+
19
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
20
+
21
+ TELEGRAM_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN")
22
+ GROQ_KEY = os.environ.get("GROQ_API_KEY")
23
+ if not TELEGRAM_TOKEN or not GROQ_KEY:
24
+ sys.exit("Set TELEGRAM_BOT_TOKEN and GROQ_API_KEY first (see the top of this file).")
25
+
26
+ bot = telegroq.createBot(TELEGRAM_TOKEN)
27
+ bot.set_groq_key(GROQ_KEY)
28
+
29
+ PROMPTS = {
30
+ "pirate": "You are a swashbuckling pirate. Answer in pirate speak, briefly.",
31
+ "teacher": "You are a patient teacher. Explain step by step, in simple words.",
32
+ "poet": "You are a poet. Reply in a short rhyming verse.",
33
+ }
34
+ # One character per persona; one SQLite file, one table per persona.
35
+ personas = {
36
+ name: telegroq.setCh(prompt, max_history=12, store=SQLiteHistoryStore("history.db", table=name))
37
+ for name, prompt in PROMPTS.items()
38
+ }
39
+ chosen: dict[int, str] = {} # user id -> persona name (use a database for real deployments)
40
+
41
+
42
+ @bot.cmd("start", description="Start the bot")
43
+ @bot.cmd("persona", description="Choose who I am")
44
+ def persona_menu(user, msg):
45
+ keyboard = inline_keyboard([(name.title(), f"persona:{name}") for name in personas])
46
+ bot.send_message(msg.chat_id, "Who should I be?", reply_markup=keyboard)
47
+
48
+
49
+ @bot.callback(prefix="persona:")
50
+ def persona_chosen(user, call):
51
+ chosen[user.id] = call.args # e.g. "persona:pirate" -> "pirate"
52
+ call.answer(f"Now chatting with the {call.args}!")
53
+ call.edit(f"Persona: {call.args.title()}. Say something!")
54
+
55
+
56
+ @bot.cmd("reset", description="Forget our conversation")
57
+ def reset(user, msg):
58
+ personas[chosen.get(user.id, "pirate")].clear_history(user.id)
59
+ msg.reply("Memory cleared.")
60
+
61
+
62
+ @bot.on_message(chat_types=["private"])
63
+ def chat(user, msg):
64
+ character = personas[chosen.get(user.id, "pirate")]
65
+ msg.reply(character.get_response(user.id, msg.text))
66
+
67
+
68
+ @bot.on_error
69
+ def log_errors(exc, user, update):
70
+ logging.getLogger(__name__).error("handler failed for user %s: %r", user.id, exc)
71
+ update.reply("Sorry, that went wrong. Try again?") # works for messages and button presses
72
+
73
+
74
+ if __name__ == "__main__":
75
+ bot.run()