telegroq 0.1.0__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.

Potentially problematic release.


This version of telegroq might be problematic. Click here for more details.

@@ -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.1.0] - 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.1.0/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,222 @@
1
+ Metadata-Version: 2.5
2
+ Name: telegroq
3
+ Version: 0.1.0
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
+ **A Groq-powered Telegram bot with its own personality in a few lines of Python.**
39
+
40
+ `telegroq` bridges [pyTelegramBotAPI](https://github.com/eternnoir/pyTelegramBotAPI) (`telebot`) and the
41
+ [Groq API](https://console.groq.com/docs). You get decorators for commands, messages and inline buttons,
42
+ characters with per-user memory, and sensible behaviour when things go wrong (rate limits, bad keys,
43
+ photos and stickers instead of text) - without writing the glue yourself.
44
+
45
+ > Unofficial project. Not affiliated with or endorsed by Telegram or Groq.
46
+
47
+ ## Install
48
+
49
+ ```bash
50
+ pip install telegroq
51
+ ```
52
+
53
+ Requires Python 3.10+. You need a Telegram bot token from [@BotFather](https://t.me/BotFather) and a Groq API key from
54
+ [console.groq.com](https://console.groq.com/keys).
55
+
56
+ ## Quickstart
57
+
58
+ ```python
59
+ import telegroq
60
+
61
+ bot = telegroq.createBot("123456:TELEGRAM_BOT_TOKEN")
62
+ bot.set_groq_key("gsk_...", model="llama-3.3-70b-versatile")
63
+
64
+ character = telegroq.setCh(
65
+ system_prompt="You are a funny, witty, and energetic AI assistant.",
66
+ max_history=10,
67
+ )
68
+
69
+ @bot.cmd("/start")
70
+ def start(user, msg):
71
+ bot.send_message(user.id, f"Hello {user.first_name}! I am ready.")
72
+
73
+ @bot.cmd("/reset")
74
+ def reset(user, msg):
75
+ character.clear_history(user.id)
76
+ bot.send_message(user.id, "Conversation memory cleared!")
77
+
78
+ @bot.on_message()
79
+ def chat(user, msg):
80
+ bot.reply(user.id, character.get_response(user.id, msg.text), message_id=msg.message_id)
81
+
82
+ bot.run()
83
+ ```
84
+
85
+ Even shorter - let telegroq answer every text message for you: replace the `chat` handler with
86
+ `bot.auto_reply(character)`.
87
+
88
+ Keep secrets out of your code: read them from environment variables (see [`examples/main.py`](examples/main.py)).
89
+
90
+ ## Concepts
91
+
92
+ ### Handlers receive `(user, msg)`
93
+
94
+ * `user` is telebot's `User` (`.id`, `.username`, `.first_name`, ...).
95
+ * `msg` is a `telegroq.Message`. `msg.text` is **always a string** (`""` for photos, stickers and voice notes),
96
+ `msg.args` is the text after a command, `msg.chat_id` / `msg.is_private` describe the chat, and
97
+ `msg.reply(text)` answers in the chat the message came from. Everything else (`msg.message_id`,
98
+ `msg.caption`, `msg.photo`, ...) is forwarded to the raw telebot message (`msg.raw`).
99
+
100
+ > **Groups:** `bot.send_message(user.id, ...)` writes to the user's *private* chat. In group chats use
101
+ > `msg.reply(...)` or `bot.send_message(msg.chat_id, ...)`. Restrict AI handlers with
102
+ > `@bot.on_message(chat_types=["private"])` if you do not want the bot to answer everything in a group.
103
+
104
+ ### Commands
105
+
106
+ ```python
107
+ @bot.cmd("/start", description="Say hello") # description appears in Telegram's command menu
108
+ @bot.cmd("help", "about") # several names, slash optional, any letter case
109
+ ```
110
+
111
+ Registered commands are never delivered to `@bot.on_message()`, whatever order you declare them in.
112
+
113
+ ### Characters (personas with memory)
114
+
115
+ ```python
116
+ character = telegroq.setCh("You are a pirate.", max_history=10, temperature=0.9, model="llama-3.1-8b-instant")
117
+
118
+ character.get_response(user.id, "Hi!") # -> str (never raises on Groq errors)
119
+ character.ask(user.id, "Hi!") # -> Reply(text, ok, error_kind, error)
120
+ await character.aget_response(user.id, "Hi") # async variant (runs in a worker thread)
121
+ character.clear_history(user.id)
122
+ character.get_history(user.id)
123
+ ```
124
+
125
+ * History is kept **per user id** and trimmed to the last `max_history` messages (`0` = stateless).
126
+ Calls for the same user are serialised, so quick successive messages cannot scramble the conversation.
127
+ * `system_prompt` may also be a callable `(user_id) -> str` for per-user prompts.
128
+ * `extra_params={"top_p": 0.9}` is forwarded to `chat.completions.create`.
129
+ * Pass `client=` to use your own Groq-compatible client, or `bot=` / `.bind(bot)` to pick which bot's key is used.
130
+ Without either, the most recently configured key is used.
131
+
132
+ ### Inline buttons and callbacks
133
+
134
+ ```python
135
+ from telegroq import inline_keyboard
136
+
137
+ @bot.cmd("persona")
138
+ def choose(user, msg):
139
+ bot.send_message(msg.chat_id, "Who am I?", reply_markup=inline_keyboard(
140
+ [("Pirate", "persona:pirate"), ("Poet", "persona:poet")],
141
+ ))
142
+
143
+ @bot.callback(prefix="persona:") # or @bot.callback("exact:data") / @bot.callback(regex=...)
144
+ def picked(user, call):
145
+ call.answer(f"OK, {call.args}!") # optional: telegroq answers for you if you don't
146
+ call.edit(f"Persona: {call.args}") # edit the message that holds the buttons
147
+ ```
148
+
149
+ `call` is a `CallbackContext` with `.data`, `.args` (data after the prefix), `.message`, `.answer()`,
150
+ `.edit()`, `.edit_keyboard()` and `.reply()`. The button's loading spinner is cleared automatically.
151
+ `button(text, callback_data)` enforces Telegram's 64-byte limit; `button(text, url=...)` makes link buttons.
152
+
153
+ ### Persistent memory
154
+
155
+ ```python
156
+ from telegroq import SQLiteHistoryStore
157
+
158
+ character = telegroq.setCh("...", store=SQLiteHistoryStore("history.db"))
159
+ ```
160
+
161
+ Implement `telegroq.HistoryStore` (`load`, `save`, `clear`) to use Redis, Postgres, etc. The default
162
+ `MemoryHistoryStore` forgets everything on restart.
163
+
164
+ ### Errors never take the bot down
165
+
166
+ | Situation | What happens |
167
+ |---|---|
168
+ | 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": "..."})`. |
169
+ | Your handler raises | The error is logged, the user gets `bot.error_reply` (or run your own `@bot.on_error`), polling continues. |
170
+ | 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. |
171
+ | Model output breaks Markdown/HTML | `send_message(..., parse_mode=...)` retries as plain text. |
172
+ | Reply longer than 4096 characters | Split into several messages automatically. |
173
+ | Network errors while polling | `bot.run()` keeps retrying. |
174
+
175
+ Forgetting `set_groq_key` raises `telegroq.ConfigurationError` - a setup bug, not a runtime failure.
176
+
177
+ ### Logging
178
+
179
+ telegroq logs to the `telegroq` logger and is silent by default. Enable it with
180
+ `logging.basicConfig(level=logging.INFO)`.
181
+
182
+ ## API summary
183
+
184
+ | | |
185
+ |---|---|
186
+ | `telegroq.createBot(token, **telebot_kwargs)` | Create a `TeleGroqBot` (wraps `telebot.TeleBot`, available as `bot.telebot`; unknown attributes forward to it). |
187
+ | `bot.set_groq_key(api_key, model=...)` | Create the Groq client (`timeout`, `max_retries` optional). |
188
+ | `telegroq.setCh(system_prompt, max_history=10, **opts)` | Create a `Character`. |
189
+ | `@bot.cmd(*names, description=None, chat_types=None)` | Command handler `fn(user, msg)`. |
190
+ | `@bot.on_message(content_types=None, chat_types=None, regexp=None, func=None, typing=True)` | Wildcard handler `fn(user, msg)`. |
191
+ | `bot.auto_reply(character, ...)` | Answer every text message with a character. |
192
+ | `@bot.callback(*data, prefix=None, regex=None)` | Inline-button handler `fn(user, call)`. |
193
+ | `@bot.on_error` | `fn(exc, user, update)` called when a handler raises. |
194
+ | `bot.send_message(user_id, text, **kw)` / `bot.reply(user_id, text, message_id=None, **kw)` | Sending helpers (splitting, plain-text retry). |
195
+ | `bot.run()` / `bot.stop()` | Start / stop long polling. |
196
+ | `telegroq.button`, `telegroq.inline_keyboard` | Keyboard helpers. |
197
+ | `HistoryStore`, `MemoryHistoryStore`, `SQLiteHistoryStore` | Conversation storage. |
198
+
199
+ Every public function and class has a docstring; try `help(telegroq.Character)`.
200
+
201
+ ## Development
202
+
203
+ ```bash
204
+ git clone https://github.com/kkkik/telegroq && cd telegroq
205
+ python -m pip install -e ".[dev]"
206
+ pytest # no network or API keys needed (Telegram and Groq are faked)
207
+ ruff check .
208
+ ```
209
+
210
+ ### Releasing to PyPI (with flit)
211
+
212
+ 1. Bump `__version__` in `src/telegroq/__init__.py` and add a section to `CHANGELOG.md`.
213
+ 2. Build and check: `flit build && twine check --strict dist/*`.
214
+ 3. Try it on TestPyPI first:
215
+ `FLIT_INDEX_URL=https://test.pypi.org/legacy/ FLIT_USERNAME=__token__ FLIT_PASSWORD=<testpypi-token> flit publish`.
216
+ 4. Publish: `FLIT_USERNAME=__token__ FLIT_PASSWORD=<pypi-token> flit publish`, or create a GitHub
217
+ release and let `.github/workflows/publish.yml` publish via PyPI trusted publishing.
218
+
219
+ ## License
220
+
221
+ MIT - see [LICENSE](LICENSE).
222
+
@@ -0,0 +1,186 @@
1
+ # telegroq
2
+
3
+ **A Groq-powered Telegram bot with its own personality in a few lines of Python.**
4
+
5
+ `telegroq` bridges [pyTelegramBotAPI](https://github.com/eternnoir/pyTelegramBotAPI) (`telebot`) and the
6
+ [Groq API](https://console.groq.com/docs). You get decorators for commands, messages and inline buttons,
7
+ characters with per-user memory, and sensible behaviour when things go wrong (rate limits, bad keys,
8
+ photos and stickers instead of text) - without writing the glue yourself.
9
+
10
+ > Unofficial project. Not affiliated with or endorsed by Telegram or Groq.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pip install telegroq
16
+ ```
17
+
18
+ Requires Python 3.10+. You need a Telegram bot token from [@BotFather](https://t.me/BotFather) and a Groq API key from
19
+ [console.groq.com](https://console.groq.com/keys).
20
+
21
+ ## Quickstart
22
+
23
+ ```python
24
+ import telegroq
25
+
26
+ bot = telegroq.createBot("123456:TELEGRAM_BOT_TOKEN")
27
+ bot.set_groq_key("gsk_...", model="llama-3.3-70b-versatile")
28
+
29
+ character = telegroq.setCh(
30
+ system_prompt="You are a funny, witty, and energetic AI assistant.",
31
+ max_history=10,
32
+ )
33
+
34
+ @bot.cmd("/start")
35
+ def start(user, msg):
36
+ bot.send_message(user.id, f"Hello {user.first_name}! I am ready.")
37
+
38
+ @bot.cmd("/reset")
39
+ def reset(user, msg):
40
+ character.clear_history(user.id)
41
+ bot.send_message(user.id, "Conversation memory cleared!")
42
+
43
+ @bot.on_message()
44
+ def chat(user, msg):
45
+ bot.reply(user.id, character.get_response(user.id, msg.text), message_id=msg.message_id)
46
+
47
+ bot.run()
48
+ ```
49
+
50
+ Even shorter - let telegroq answer every text message for you: replace the `chat` handler with
51
+ `bot.auto_reply(character)`.
52
+
53
+ Keep secrets out of your code: read them from environment variables (see [`examples/main.py`](examples/main.py)).
54
+
55
+ ## Concepts
56
+
57
+ ### Handlers receive `(user, msg)`
58
+
59
+ * `user` is telebot's `User` (`.id`, `.username`, `.first_name`, ...).
60
+ * `msg` is a `telegroq.Message`. `msg.text` is **always a string** (`""` for photos, stickers and voice notes),
61
+ `msg.args` is the text after a command, `msg.chat_id` / `msg.is_private` describe the chat, and
62
+ `msg.reply(text)` answers in the chat the message came from. Everything else (`msg.message_id`,
63
+ `msg.caption`, `msg.photo`, ...) is forwarded to the raw telebot message (`msg.raw`).
64
+
65
+ > **Groups:** `bot.send_message(user.id, ...)` writes to the user's *private* chat. In group chats use
66
+ > `msg.reply(...)` or `bot.send_message(msg.chat_id, ...)`. Restrict AI handlers with
67
+ > `@bot.on_message(chat_types=["private"])` if you do not want the bot to answer everything in a group.
68
+
69
+ ### Commands
70
+
71
+ ```python
72
+ @bot.cmd("/start", description="Say hello") # description appears in Telegram's command menu
73
+ @bot.cmd("help", "about") # several names, slash optional, any letter case
74
+ ```
75
+
76
+ Registered commands are never delivered to `@bot.on_message()`, whatever order you declare them in.
77
+
78
+ ### Characters (personas with memory)
79
+
80
+ ```python
81
+ character = telegroq.setCh("You are a pirate.", max_history=10, temperature=0.9, model="llama-3.1-8b-instant")
82
+
83
+ character.get_response(user.id, "Hi!") # -> str (never raises on Groq errors)
84
+ character.ask(user.id, "Hi!") # -> Reply(text, ok, error_kind, error)
85
+ await character.aget_response(user.id, "Hi") # async variant (runs in a worker thread)
86
+ character.clear_history(user.id)
87
+ character.get_history(user.id)
88
+ ```
89
+
90
+ * History is kept **per user id** and trimmed to the last `max_history` messages (`0` = stateless).
91
+ Calls for the same user are serialised, so quick successive messages cannot scramble the conversation.
92
+ * `system_prompt` may also be a callable `(user_id) -> str` for per-user prompts.
93
+ * `extra_params={"top_p": 0.9}` is forwarded to `chat.completions.create`.
94
+ * Pass `client=` to use your own Groq-compatible client, or `bot=` / `.bind(bot)` to pick which bot's key is used.
95
+ Without either, the most recently configured key is used.
96
+
97
+ ### Inline buttons and callbacks
98
+
99
+ ```python
100
+ from telegroq import inline_keyboard
101
+
102
+ @bot.cmd("persona")
103
+ def choose(user, msg):
104
+ bot.send_message(msg.chat_id, "Who am I?", reply_markup=inline_keyboard(
105
+ [("Pirate", "persona:pirate"), ("Poet", "persona:poet")],
106
+ ))
107
+
108
+ @bot.callback(prefix="persona:") # or @bot.callback("exact:data") / @bot.callback(regex=...)
109
+ def picked(user, call):
110
+ call.answer(f"OK, {call.args}!") # optional: telegroq answers for you if you don't
111
+ call.edit(f"Persona: {call.args}") # edit the message that holds the buttons
112
+ ```
113
+
114
+ `call` is a `CallbackContext` with `.data`, `.args` (data after the prefix), `.message`, `.answer()`,
115
+ `.edit()`, `.edit_keyboard()` and `.reply()`. The button's loading spinner is cleared automatically.
116
+ `button(text, callback_data)` enforces Telegram's 64-byte limit; `button(text, url=...)` makes link buttons.
117
+
118
+ ### Persistent memory
119
+
120
+ ```python
121
+ from telegroq import SQLiteHistoryStore
122
+
123
+ character = telegroq.setCh("...", store=SQLiteHistoryStore("history.db"))
124
+ ```
125
+
126
+ Implement `telegroq.HistoryStore` (`load`, `save`, `clear`) to use Redis, Postgres, etc. The default
127
+ `MemoryHistoryStore` forgets everything on restart.
128
+
129
+ ### Errors never take the bot down
130
+
131
+ | Situation | What happens |
132
+ |---|---|
133
+ | 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": "..."})`. |
134
+ | Your handler raises | The error is logged, the user gets `bot.error_reply` (or run your own `@bot.on_error`), polling continues. |
135
+ | 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. |
136
+ | Model output breaks Markdown/HTML | `send_message(..., parse_mode=...)` retries as plain text. |
137
+ | Reply longer than 4096 characters | Split into several messages automatically. |
138
+ | Network errors while polling | `bot.run()` keeps retrying. |
139
+
140
+ Forgetting `set_groq_key` raises `telegroq.ConfigurationError` - a setup bug, not a runtime failure.
141
+
142
+ ### Logging
143
+
144
+ telegroq logs to the `telegroq` logger and is silent by default. Enable it with
145
+ `logging.basicConfig(level=logging.INFO)`.
146
+
147
+ ## API summary
148
+
149
+ | | |
150
+ |---|---|
151
+ | `telegroq.createBot(token, **telebot_kwargs)` | Create a `TeleGroqBot` (wraps `telebot.TeleBot`, available as `bot.telebot`; unknown attributes forward to it). |
152
+ | `bot.set_groq_key(api_key, model=...)` | Create the Groq client (`timeout`, `max_retries` optional). |
153
+ | `telegroq.setCh(system_prompt, max_history=10, **opts)` | Create a `Character`. |
154
+ | `@bot.cmd(*names, description=None, chat_types=None)` | Command handler `fn(user, msg)`. |
155
+ | `@bot.on_message(content_types=None, chat_types=None, regexp=None, func=None, typing=True)` | Wildcard handler `fn(user, msg)`. |
156
+ | `bot.auto_reply(character, ...)` | Answer every text message with a character. |
157
+ | `@bot.callback(*data, prefix=None, regex=None)` | Inline-button handler `fn(user, call)`. |
158
+ | `@bot.on_error` | `fn(exc, user, update)` called when a handler raises. |
159
+ | `bot.send_message(user_id, text, **kw)` / `bot.reply(user_id, text, message_id=None, **kw)` | Sending helpers (splitting, plain-text retry). |
160
+ | `bot.run()` / `bot.stop()` | Start / stop long polling. |
161
+ | `telegroq.button`, `telegroq.inline_keyboard` | Keyboard helpers. |
162
+ | `HistoryStore`, `MemoryHistoryStore`, `SQLiteHistoryStore` | Conversation storage. |
163
+
164
+ Every public function and class has a docstring; try `help(telegroq.Character)`.
165
+
166
+ ## Development
167
+
168
+ ```bash
169
+ git clone https://github.com/kkkik/telegroq && cd telegroq
170
+ python -m pip install -e ".[dev]"
171
+ pytest # no network or API keys needed (Telegram and Groq are faked)
172
+ ruff check .
173
+ ```
174
+
175
+ ### Releasing to PyPI (with flit)
176
+
177
+ 1. Bump `__version__` in `src/telegroq/__init__.py` and add a section to `CHANGELOG.md`.
178
+ 2. Build and check: `flit build && twine check --strict dist/*`.
179
+ 3. Try it on TestPyPI first:
180
+ `FLIT_INDEX_URL=https://test.pypi.org/legacy/ FLIT_USERNAME=__token__ FLIT_PASSWORD=<testpypi-token> flit publish`.
181
+ 4. Publish: `FLIT_USERNAME=__token__ FLIT_PASSWORD=<pypi-token> flit publish`, or create a GitHub
182
+ release and let `.github/workflows/publish.yml` publish via PyPI trusted publishing.
183
+
184
+ ## License
185
+
186
+ MIT - see [LICENSE](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()
@@ -0,0 +1,67 @@
1
+ [build-system]
2
+ requires = ["flit_core>=3.11,<4"]
3
+ build-backend = "flit_core.buildapi"
4
+
5
+ [project]
6
+ name = "telegroq"
7
+ dynamic = ["version"]
8
+ description = "Deploy a Groq-powered Telegram bot with a custom persona in a few lines (pyTelegramBotAPI + Groq)."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "AYM" }]
14
+ keywords = ["telegram", "bot", "groq", "llm", "chatbot", "ai", "persona", "pytelegrambotapi", "telebot"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3 :: Only",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Communications :: Chat",
26
+ "Topic :: Software Development :: Libraries :: Python Modules",
27
+ "Typing :: Typed",
28
+ ]
29
+ dependencies = [
30
+ "pyTelegramBotAPI>=4.16",
31
+ "groq>=0.9",
32
+ ]
33
+
34
+ [project.optional-dependencies]
35
+ dev = [
36
+ "pytest>=7.0",
37
+ "ruff>=0.5",
38
+ "flit>=3.11",
39
+ "twine>=6.1",
40
+ ]
41
+
42
+ [project.urls]
43
+ Homepage = "https://github.com/kkkik/telegroq"
44
+ Source = "https://github.com/kkkik/telegroq"
45
+ Issues = "https://github.com/kkkik/telegroq/issues"
46
+ Changelog = "https://github.com/kkkik/telegroq/blob/main/CHANGELOG.md"
47
+
48
+ [tool.flit.module]
49
+ name = "telegroq" # found in src/telegroq; version is read from __version__ in src/telegroq/__init__.py
50
+
51
+ [tool.flit.sdist]
52
+ include = ["tests/", "examples/", "CHANGELOG.md"]
53
+ exclude = ["**/__pycache__/", "**/*.pyc", "publish.sh", "token.txt"]
54
+
55
+ [tool.pytest.ini_options]
56
+ testpaths = ["tests"]
57
+ pythonpath = ["src"]
58
+
59
+ [tool.ruff]
60
+ line-length = 120
61
+ target-version = "py310"
62
+
63
+ [tool.ruff.lint]
64
+ select = ["E", "F", "W"]
65
+
66
+ [tool.ruff.lint.per-file-ignores]
67
+ "tests/*" = ["E501"]