├── .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 | | Image | Image | 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 | --------------------------------------------------------------------------------