├── .dokku
├── DOKKU.md
├── Procfile
└── app.json
├── .env.copy
├── .gitignore
├── LICENSE
├── Pipfile
├── Pipfile.lock
├── README.md
└── bot.py
/.dokku/DOKKU.md:
--------------------------------------------------------------------------------
1 | # Dokku Deploy
2 |
3 | Dokku Deploy Notes
4 |
5 | ### Step 1 - SSH to your server and create the app with the config vars
6 |
7 | ```sh
8 | # replace IP with your server's IP
9 | ssh -i ~/path/to/your/key root@IP
10 | ```
11 |
12 | ```sh
13 | # we are using tgvn as the app name
14 | dokku apps:create tgvn
15 | # set the config vars
16 | dokku config:set tgvn TELEGRAM_BOT_TOKEN=
17 | dokku config:set tgvn GROQ_API_KEY=
18 | # set the app.json and Procfile path
19 | dokku app-json:set tgvn appjson-path .dokku/app.json
20 | dokku ps:set tgvn procfile-path .dokku/Procfile
21 | ```
22 |
23 | ### Step 2 - Deploy the app
24 |
25 | ```sh
26 | ssh-add ~/path/to/your/key
27 | # replace IP with your server's IP
28 | git remote add dokku dokku@IP:tgvn
29 | git push dokku master
30 | # Procfile runs the prod script
31 | ```
32 |
--------------------------------------------------------------------------------
/.dokku/Procfile:
--------------------------------------------------------------------------------
1 | worker: python bot.py
2 |
--------------------------------------------------------------------------------
/.dokku/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "formation": {
3 | "worker": {
4 | "quantity": 1
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/.env.copy:
--------------------------------------------------------------------------------
1 | TELEGRAM_BOT_TOKEN=
2 | GROQ_API_KEY=
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .env
3 | __pycache__
4 |
5 | # notes on my dokku server
6 | .dokku/MY_DOKKU.md
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Avi Aryan
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.
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.org/simple"
3 | verify_ssl = true
4 | name = "pypi"
5 |
6 | [packages]
7 | python-telegram-bot = "==20.7"
8 | groq = "==0.18.0"
9 | python-dotenv = "==1.0.0"
10 |
11 | [dev-packages]
12 |
13 | [requires]
14 | python_version = "3.10"
15 |
--------------------------------------------------------------------------------
/Pipfile.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_meta": {
3 | "hash": {
4 | "sha256": "d95465989388bb553ba8691446a08b2367a0a5ef58459e21616ef3649b03066e"
5 | },
6 | "pipfile-spec": 6,
7 | "requires": {
8 | "python_version": "3.10"
9 | },
10 | "sources": [
11 | {
12 | "name": "pypi",
13 | "url": "https://pypi.org/simple",
14 | "verify_ssl": true
15 | }
16 | ]
17 | },
18 | "default": {
19 | "annotated-types": {
20 | "hashes": [
21 | "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53",
22 | "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"
23 | ],
24 | "markers": "python_version >= '3.8'",
25 | "version": "==0.7.0"
26 | },
27 | "anyio": {
28 | "hashes": [
29 | "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a",
30 | "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"
31 | ],
32 | "markers": "python_version >= '3.9'",
33 | "version": "==4.8.0"
34 | },
35 | "certifi": {
36 | "hashes": [
37 | "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651",
38 | "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"
39 | ],
40 | "markers": "python_version >= '3.6'",
41 | "version": "==2025.1.31"
42 | },
43 | "distro": {
44 | "hashes": [
45 | "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed",
46 | "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"
47 | ],
48 | "markers": "python_version >= '3.6'",
49 | "version": "==1.9.0"
50 | },
51 | "exceptiongroup": {
52 | "hashes": [
53 | "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b",
54 | "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"
55 | ],
56 | "markers": "python_version < '3.11'",
57 | "version": "==1.2.2"
58 | },
59 | "groq": {
60 | "hashes": [
61 | "sha256:81d5ac00057a45d8ce559d23ab5d3b3893011d1f12c35187ab35a9182d826ea6",
62 | "sha256:8e2ccfea406d68b3525af4b7c0e321fcb3d2a73fc60bb70b4156e6cd88c72f03"
63 | ],
64 | "index": "pypi",
65 | "markers": "python_version >= '3.8'",
66 | "version": "==0.18.0"
67 | },
68 | "h11": {
69 | "hashes": [
70 | "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d",
71 | "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"
72 | ],
73 | "markers": "python_version >= '3.7'",
74 | "version": "==0.14.0"
75 | },
76 | "httpcore": {
77 | "hashes": [
78 | "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c",
79 | "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"
80 | ],
81 | "markers": "python_version >= '3.8'",
82 | "version": "==1.0.7"
83 | },
84 | "httpx": {
85 | "hashes": [
86 | "sha256:8b8fcaa0c8ea7b05edd69a094e63a2094c4efcb48129fb757361bc423c0ad9e8",
87 | "sha256:a05d3d052d9b2dfce0e3896636467f8a5342fb2b902c819428e1ac65413ca118"
88 | ],
89 | "markers": "python_version >= '3.8'",
90 | "version": "==0.25.2"
91 | },
92 | "idna": {
93 | "hashes": [
94 | "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9",
95 | "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
96 | ],
97 | "markers": "python_version >= '3.6'",
98 | "version": "==3.10"
99 | },
100 | "pydantic": {
101 | "hashes": [
102 | "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584",
103 | "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"
104 | ],
105 | "markers": "python_version >= '3.8'",
106 | "version": "==2.10.6"
107 | },
108 | "pydantic-core": {
109 | "hashes": [
110 | "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278",
111 | "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50",
112 | "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9",
113 | "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f",
114 | "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6",
115 | "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc",
116 | "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54",
117 | "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630",
118 | "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9",
119 | "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236",
120 | "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7",
121 | "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee",
122 | "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b",
123 | "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048",
124 | "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc",
125 | "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130",
126 | "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4",
127 | "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd",
128 | "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4",
129 | "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7",
130 | "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7",
131 | "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4",
132 | "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e",
133 | "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa",
134 | "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6",
135 | "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962",
136 | "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b",
137 | "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f",
138 | "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474",
139 | "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5",
140 | "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459",
141 | "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf",
142 | "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a",
143 | "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c",
144 | "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76",
145 | "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362",
146 | "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4",
147 | "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934",
148 | "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320",
149 | "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118",
150 | "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96",
151 | "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306",
152 | "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046",
153 | "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3",
154 | "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2",
155 | "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af",
156 | "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9",
157 | "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67",
158 | "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a",
159 | "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27",
160 | "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35",
161 | "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b",
162 | "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151",
163 | "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b",
164 | "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154",
165 | "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133",
166 | "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef",
167 | "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145",
168 | "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15",
169 | "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4",
170 | "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc",
171 | "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee",
172 | "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c",
173 | "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0",
174 | "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5",
175 | "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57",
176 | "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b",
177 | "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8",
178 | "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1",
179 | "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da",
180 | "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e",
181 | "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc",
182 | "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993",
183 | "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656",
184 | "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4",
185 | "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c",
186 | "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb",
187 | "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d",
188 | "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9",
189 | "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e",
190 | "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1",
191 | "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc",
192 | "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a",
193 | "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9",
194 | "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506",
195 | "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b",
196 | "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1",
197 | "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d",
198 | "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99",
199 | "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3",
200 | "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31",
201 | "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c",
202 | "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39",
203 | "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a",
204 | "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308",
205 | "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2",
206 | "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228",
207 | "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b",
208 | "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9",
209 | "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"
210 | ],
211 | "markers": "python_version >= '3.8'",
212 | "version": "==2.27.2"
213 | },
214 | "python-dotenv": {
215 | "hashes": [
216 | "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba",
217 | "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"
218 | ],
219 | "index": "pypi",
220 | "markers": "python_version >= '3.8'",
221 | "version": "==1.0.0"
222 | },
223 | "python-telegram-bot": {
224 | "hashes": [
225 | "sha256:462326c65671c8c39e76c8c96756ee918be6797d225f8db84d2ec0f883383b8c",
226 | "sha256:4f146c39de5f5e0b3723c2abedaf78046ebd30a6a49d2281ee4b3af5eb116b68"
227 | ],
228 | "index": "pypi",
229 | "markers": "python_version >= '3.8'",
230 | "version": "==20.7"
231 | },
232 | "sniffio": {
233 | "hashes": [
234 | "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2",
235 | "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"
236 | ],
237 | "markers": "python_version >= '3.7'",
238 | "version": "==1.3.1"
239 | },
240 | "typing-extensions": {
241 | "hashes": [
242 | "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d",
243 | "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"
244 | ],
245 | "markers": "python_version >= '3.8'",
246 | "version": "==4.12.2"
247 | }
248 | },
249 | "develop": {}
250 | }
251 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Voice Note Transcription and Summarizer Bot for Telegram 🎙️📝
2 |
3 | A powerful Telegram bot that transcribes and summarizes voice notes using state-of-the-art AI models. Built with Python and powered by Groq's API with Whisper and Llama 3 model for transcription and summarization.
4 |
5 | ## Screenshots 📸
6 |
7 | | Transcription Example | Summary Example |
8 | |:---:|:---:|
9 | |
|
|
10 |
11 | ## Features ✨
12 |
13 | - Transcribe voice notes from forwarded messages
14 | - Handle direct voice note recordings
15 | - Generate accurate transcriptions using Whisper
16 | - Provide concise summaries of the transcribed content using Llama 3
17 | - Support for multiple audio formats
18 | - Well-formatted, easy-to-read output
19 |
20 | ## Prerequisites 📋
21 |
22 | - Python 3.10 or higher
23 | - pipenv (Python package manager)
24 | - Telegram Bot Token (from [@BotFather](https://t.me/botfather))
25 | - Groq API Key ([Get it here](https://console.groq.com))
26 |
27 | ## Installation 🚀
28 |
29 | 1. Clone the repository:
30 | ```bash
31 | git clone https://github.com/aviaryan/voice-transcribe-summarize-telegram-bot.git
32 | cd voice-transcribe-summarize-telegram-bot
33 | ```
34 |
35 | 2. Install dependencies using pipenv:
36 | ```bash
37 | pipenv install
38 | ```
39 |
40 | 3. Create a `.env` file in the root directory:
41 | ```bash
42 | cp .env.copy .env
43 | ```
44 |
45 | 4. Fill in your environment variables in the `.env` file:
46 | ```
47 | TELEGRAM_BOT_TOKEN=your_telegram_bot_token
48 | GROQ_API_KEY=your_groq_api_key
49 | ```
50 |
51 | 5. Configure authorized users:
52 | - Open `bot.py` and locate the `AUTHORIZED_USERS` array
53 | - Add your Telegram user ID to the array (you can get your ID by messaging @userinfobot on Telegram)
54 | ```python
55 | AUTHORIZED_USERS = [your_telegram_id] # Add more user IDs as needed
56 | ```
57 |
58 | ## Usage 🎯
59 |
60 | 1. Activate the virtual environment:
61 | ```bash
62 | pipenv shell
63 | ```
64 |
65 | 2. Start the bot:
66 | ```bash
67 | python bot.py
68 | ```
69 |
70 | 3. In Telegram:
71 | - Forward any message containing a voice note to the bot
72 | - Record and send a voice note directly to the bot
73 | - Wait for the bot to process and return both transcription and summary
74 |
75 | ## How it Works 🔄
76 |
77 | 1. The bot receives a voice note through Telegram
78 | 2. Audio is processed and sent to Groq's API
79 | 3. Whisper model transcribes the audio content
80 | 4. Another pass through Groq's API generates a concise summary using Llama 3 model
81 | 5. Both transcription and summary are returned to the user in a well-formatted message
82 |
83 | ## Contributing 🤝
84 |
85 | Contributions are welcome! Feel free to:
86 | - Open issues for bugs or feature requests
87 | - Submit pull requests
88 | - Improve documentation
89 | - Share feedback
90 |
91 | ## License 📄
92 |
93 | This project is licensed under the MIT License - see the LICENSE file for details.
94 |
--------------------------------------------------------------------------------
/bot.py:
--------------------------------------------------------------------------------
1 | import os
2 | from telegram import Update
3 | from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes
4 | from groq import Groq
5 | import tempfile
6 | from dotenv import load_dotenv
7 | load_dotenv()
8 |
9 | # List of authorized user IDs
10 | AUTHORIZED_USERS = [95165304] # Add more user IDs as needed
11 |
12 | # Initialize Groq client
13 | groq_client = Groq(api_key=os.getenv("GROQ_API_KEY"))
14 |
15 |
16 | async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
17 | """Send a message when the command /start is issued."""
18 | print(f"Received start command from user: {update.effective_user.id}")
19 | user_name = update.effective_user.first_name
20 | try:
21 | await update.message.reply_text(
22 | f"👋 Hi {user_name}! I'm a Voice Note Summarizer Bot.\n\n"
23 | "You can:\n"
24 | "1. Forward me voice messages\n"
25 | "2. Send me direct voice recordings\n\n"
26 | "I'll transcribe them and provide you with both the transcription and a summary!"
27 | )
28 | print(f"Sent welcome message to user: {update.effective_user.id}")
29 | except Exception as e:
30 | print(f"Error in start handler: {str(e)}")
31 |
32 | async def handle_voice(update: Update, context: ContextTypes.DEFAULT_TYPE):
33 | """Handle voice messages and voice notes."""
34 | try:
35 | # Check user authorization
36 | if update.effective_user.id not in AUTHORIZED_USERS:
37 | await update.message.reply_text("⛔ Sorry, you are not authorized to use this bot. Contact @aviaryan.")
38 | return
39 |
40 | # Send initial status
41 | status_message = await update.message.reply_text("🎵 Processing your voice note...")
42 |
43 | # Get voice file
44 | voice_file = await update.message.voice.get_file()
45 |
46 | # Download the voice file to a temporary location
47 | with tempfile.NamedTemporaryFile(suffix='.ogg', delete=False) as temp_file:
48 | await voice_file.download_to_drive(temp_file.name)
49 | temp_path = temp_file.name
50 |
51 | # Transcribe using Whisper via Groq
52 | transcription = await transcribe_audio(temp_path)
53 |
54 | # Generate summary using LLama 3 via Groq
55 | summary = await generate_summary(transcription)
56 |
57 | # Split and send transcription if it's too long
58 | if len(transcription) > 3000: # Leave room for summary and formatting
59 | await status_message.edit_text("📝 *Transcription (Part 1):*", parse_mode='Markdown')
60 |
61 | # Split transcription into chunks of 4000 characters
62 | chunk_size = 4000
63 | transcription_chunks = [transcription[i:i + chunk_size]
64 | for i in range(0, len(transcription), chunk_size)]
65 |
66 | # Send transcription chunks
67 | for i, chunk in enumerate(transcription_chunks, 1):
68 | await update.message.reply_text(
69 | f"*Transcription (Part {i}):*\n{chunk}",
70 | parse_mode='Markdown'
71 | )
72 |
73 | # Send summary separately
74 | await update.message.reply_text(
75 | "📌 *Summary:*\n"
76 | f"{summary}",
77 | parse_mode='Markdown'
78 | )
79 | else:
80 | # Send everything in one message if it's short enough
81 | await status_message.edit_text(
82 | "📝 *Transcription:*\n"
83 | f"{transcription}\n\n"
84 | "📌 *Summary:*\n"
85 | f"{summary}",
86 | parse_mode='Markdown'
87 | )
88 |
89 | # Cleanup temporary file
90 | os.unlink(temp_path)
91 |
92 | except Exception as e:
93 | await update.message.reply_text(f"❌ Sorry, an error occurred: {str(e)}")
94 |
95 | async def transcribe_audio(file_path: str) -> str:
96 | """Transcribe audio using Whisper via Groq API."""
97 | with open(file_path, "rb") as file:
98 | transcription = groq_client.audio.transcriptions.create(
99 | file=(file_path, file.read()), # Required audio file
100 | model="distil-whisper-large-v3-en", # Required model to use for transcription
101 | # ^ using cheapest model to manage costs (https://groq.com/pricing/)
102 | # prompt="Specify context or spelling", # Optional
103 | # response_format="json", # Optional
104 | # temperature=0.0 # Optional
105 | )
106 | return transcription.text.strip()
107 |
108 | async def generate_summary(text: str) -> str:
109 | """Generate a summary using LLama 3 via Groq API."""
110 | completion = groq_client.chat.completions.create(
111 | model="llama-3.3-70b-versatile", # Replace with actual Groq LLama 3 model name
112 | messages=[
113 | {"role": "system", "content": "Generate a concise summary of the following text:"},
114 | {"role": "user", "content": text}
115 | ],
116 | max_completion_tokens=32768,
117 | )
118 | return completion.choices[0].message.content
119 |
120 | async def handle_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
121 | """Handle text messages and generate summaries."""
122 | try:
123 | # Check user authorization
124 | if update.effective_user.id not in AUTHORIZED_USERS:
125 | await update.message.reply_text("⛔ Sorry, you are not authorized to use this bot. Contact @aviaryan.")
126 | return
127 |
128 | # Send initial status
129 | status_message = await update.message.reply_text("📝 Generating summary...")
130 |
131 | # Get the text message
132 | text = update.message.text
133 |
134 | # Generate summary using LLama 3 via Groq
135 | summary = await generate_summary(text)
136 |
137 | # Send the summary
138 | await status_message.edit_text(
139 | "📌 *Summary:*\n"
140 | f"{summary}",
141 | parse_mode='Markdown'
142 | )
143 |
144 | except Exception as e:
145 | await update.message.reply_text(f"❌ Sorry, an error occurred: {str(e)}")
146 |
147 | async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
148 | """Log Errors caused by Updates."""
149 | print(f"Update {update} caused error {context.error}")
150 |
151 | # Initialize bot application
152 | """Create and configure the bot application."""
153 | application = (
154 | Application.builder()
155 | .token(os.getenv("TELEGRAM_BOT_TOKEN"))
156 | .build()
157 | )
158 | # Add handlers
159 | application.add_handler(CommandHandler("start", start))
160 | application.add_handler(MessageHandler(filters.VOICE, handle_voice))
161 | application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_text)) # Add text message handler
162 | application.add_error_handler(error_handler)
163 |
164 | if __name__ == '__main__':
165 | application.run_polling(allowed_updates=Update.ALL_TYPES, drop_pending_updates=True)
166 |
--------------------------------------------------------------------------------