├── LICENSE ├── README.md ├── bot_custom_exceptions.py ├── bot_errors_logger.py ├── botprofilepic.jpg ├── config.py.example ├── constants.py ├── db ├── __init__.py ├── db.py └── functions.py ├── downloads └── readme.md ├── exporter_db_to_sqlite ├── LICENSE └── __init__.py ├── main.py ├── plugins ├── __init__.py ├── addtodb.py ├── callback.py ├── groupchat.py ├── inline.py ├── ocr.py └── privatechat.py ├── requirements.txt ├── requirements_mysql.txt └── tr.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Mr Centimetre 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gpytranslator Bot 2 | ##### _Pyrogram translate bot using GpyTranslate Library_ 3 |   4 | GpyTranslate is a word 'G+Py+Translate' which means 'Google Python Translate'. A bot to help you translate text (with emojis) to a language from any other language in world. 5 | 6 | 7 | ## Features 8 | 9 | - Supports in groups 10 | - Works with Inline 11 | - Supports emoji 12 | - Free to use 13 | - Easy 14 | 15 | ## Installation 16 | 17 | Clone the repository: 18 | 19 | ``` 20 | git clone https://github.com/mrcentimetre/gpytranslator-bot.git && cd gpytranslator-bot 21 | ``` 22 | 23 | Install the requirements 24 | 25 | ``` 26 | pip3 install -U -r requirements.txt 27 | ``` 28 | Edit the API_ID and API_HASH and TOKEN in [config.py.example](https://github.com/mrcentimetre/gpytranslator-bot/blob/main/config.py.example) 29 | Example: 30 | ``` 31 | --API_ID >> 1 32 | --API_HASH >> abcdef1234 33 | --TOKEN >> 1234567890:ABCDEF 34 | --sudousers >> [123456, 654321] 35 | ``` 36 | change the name of config.py.example to config.py 37 | ``` 38 | cp config.py.example config.py 39 | ``` 40 | 41 | Run the bot 42 | ``` 43 | python3 main.py 44 | ``` 45 | ## Credit 46 | 47 | ##### Inspiration 48 | * [Davide Galilei](https://github.com/DavideGalilei) 49 | 50 | ##### Development & contribution 51 | * [Mr Centimetre](https://github.com/mrcentimetre) 52 | * [iiiiii1wepfj](https://github.com/iiiiii1wepfj) 53 | * [Roj Serbest](https://github.com/rojserbest) 54 | 55 | ## License 56 | 57 | This software is licensed under MIT License, with the 58 | following exceptions 59 | 60 | - [exporter\_db\_to\_sqlite/](exporter_db_to_sqlite/) is licensed under 61 | Apache-2.0 62 | 63 | ``` 64 | MIT License 65 | 66 | Copyright (c) 2021 Mr Centimetre 67 | 68 | Permission is hereby granted, free of charge, to any person obtaining a copy 69 | of this software and associated documentation files (the "Software"), to deal 70 | in the Software without restriction, including without limitation the rights 71 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 72 | copies of the Software, and to permit persons to whom the Software is 73 | furnished to do so, subject to the following conditions: 74 | 75 | The above copyright notice and this permission notice shall be included in all 76 | copies or substantial portions of the Software. 77 | 78 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 79 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 80 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 81 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 82 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 83 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 84 | SOFTWARE. 85 | ``` 86 | -------------------------------------------------------------------------------- /bot_custom_exceptions.py: -------------------------------------------------------------------------------- 1 | class google_api_error(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /bot_errors_logger.py: -------------------------------------------------------------------------------- 1 | from hydrogram import Client, enums 2 | from hydrogram.types import Message 3 | from hydrogram.errors.exceptions.forbidden_403 import ChatWriteForbidden 4 | from functools import wraps 5 | import constants 6 | 7 | 8 | def logging_errors(f): 9 | @wraps(f) 10 | async def err_log(client: Client, message: Message, *args, **kwargs): 11 | try: 12 | return await f(client, message, *args, **kwargs) 13 | except ChatWriteForbidden: 14 | await message.chat.leave() 15 | return 16 | except Exception as e: 17 | try: 18 | await message.reply( 19 | constants.error_msg_string.format(f"{type(e).__name__}: {e}"), 20 | parse_mode=enums.ParseMode.MARKDOWN, 21 | reply_markup=constants.error_message_markup, 22 | disable_web_page_preview=True, 23 | ) 24 | except ChatWriteForbidden: 25 | await message.chat.leave() 26 | return 27 | 28 | return err_log 29 | -------------------------------------------------------------------------------- /botprofilepic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrcentimetre/gpytranslator-bot/f28488bc0a4696fd9b564237661b4fada5ec8b56/botprofilepic.jpg -------------------------------------------------------------------------------- /config.py.example: -------------------------------------------------------------------------------- 1 | from hydrogram import filters 2 | API_ID: int = your api id 3 | API_HASH: str = "your api hash" 4 | TOKEN: str = "bot token" 5 | 6 | sudousers: list = [id, id] 7 | sudofilter = filters.user(sudousers) 8 | db_url: str = "sqlite://userlanguages.db" 9 | -------------------------------------------------------------------------------- /constants.py: -------------------------------------------------------------------------------- 1 | from hydrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 2 | 3 | 4 | prefix: list = ["/", "!", "#", "."] 5 | 6 | 7 | start_message_text: str = """ 8 | Hello {} \U0001F60E I am GpyTranslatorBot AKA Gipy \ud83e\udd16 9 | 10 | Send any text or poll which you would like to translate for another language. 11 | 12 | **Available commands:** 13 | /donate - Support developers 14 | /help - Show this help message 15 | /language - Set your main language 16 | 17 | If you have questions about this bot or bots' development__ - Feel free to put your question in @TDICSupport 18 | 19 | Enjoy! ☺""" 20 | 21 | start_message_reply_markup = InlineKeyboardMarkup( 22 | [ 23 | [ 24 | InlineKeyboardButton( 25 | "➕ Add me to a Group ➕", 26 | url="http://t.me/GpyTranslatorBot?startgroup=tr", 27 | ) 28 | ], 29 | [ 30 | InlineKeyboardButton("🔍 Inline here", switch_inline_query_current_chat=" "), 31 | InlineKeyboardButton( 32 | "💳 Donate", url="https://www.paypal.com/paypalme/itayki" 33 | ), 34 | ], 35 | [ 36 | InlineKeyboardButton("🆘 Help", callback_data="help"), 37 | InlineKeyboardButton("Credits 💚", callback_data=b"Credits"), 38 | ], 39 | [ 40 | InlineKeyboardButton("📣 Channel", url="https://t.me/TDICProjects"), 41 | InlineKeyboardButton("Group 👥", url="https://t.me/TDICSupport"), 42 | ], 43 | ] 44 | ) 45 | 46 | help_markup = InlineKeyboardMarkup( 47 | [ 48 | [InlineKeyboardButton("🔙 Back", callback_data="back")], 49 | ] 50 | ) 51 | 52 | error_message_markup = InlineKeyboardMarkup( 53 | [ 54 | [InlineKeyboardButton("🗑 Delete this message", callback_data="closethismsg")], 55 | ] 56 | ) 57 | 58 | 59 | credits: str = """Development 🧑‍💻 60 | • @MrCentimetre 61 | • @itayki 62 | • @rojserbest 63 | 64 | Inspiration 👨🏻‍🏫 65 | • @DavideGalilei""" 66 | 67 | help_text: str = """ 68 | **GpyTranslate Bot** 69 | GpyTranslate is a word 'G+Py+Translate' which means 'Google Python Translate'. A bot to help you translate text (with emojis) to few Languages from any other language in world. 70 | GpyTranslator Bot is able to detect a wide variety of languages because he is a grand son of Google Translate API. 71 | You can use GpyTranslator Bot in private chat, groups and inline mode. Also you can use /ocr command to get a text from an image. 72 | **How To** 73 | Just send copied text or forward message with other language to GpyTranslator Bot and you'll receive a translation of the message in the language of your choice. You can also translate quiz and polls. Send /language command to know which language is available. 74 | **- More help -** 75 | **Groups & Privat Chat Commands** 76 | **Translate** 77 | - /tr (language code (ISO 639-1)) - Translate replied message 78 | - /tr (language code (ISO 639-1)) (text) - Translate to specific language without changing main language 79 | - /poll (language code (ISO 639-1)) by reply to poll - translate the poll and send new poll 80 | **ocr** 81 | - /ocr - To get text from image. (the text of in the image must to be in english, else see /ocrlang) (First you should send a image and then send /ocr as a reply.) 82 | - /ocrlang (language code (ISO 639-2)) - To get text from image. (the language parameter is the language of the text in the image) (First you should send a image and then send /ocrlang (language) as a reply.) 83 | **Translate in inline mode** 84 | - @GpyTranslatorBot (language code (ISO 639-1)) (text) 85 | **set your default language (private only)** 86 | - /lang (language code (ISO 639-1)) - set your default language 87 | 88 | __If you do not specify any language code, the given text will be translated to English.__ 89 | [for information about the language codes format click here](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) 90 | --- 91 | Find a problem? Send to @MrCentimetre 92 | coded by @MrCentimetreLK and @itayki by using @DavideGalilei's Library with 💚 93 | """ 94 | 95 | donate_text: str = """ 96 | It's just a command \ud83d\ude09 But you can contact me - @MrCentimetre 97 | 98 | For donations for server maintenance: 99 | https://www.paypal.com/paypalme/itayki 100 | """ 101 | 102 | language_text: str = """ 103 | **Languages** 104 | 105 | __Select the language you want to translate.__ 106 | 107 | •/lang (language code) 108 | 109 | Example: `/lang en` 110 | 111 | List of language codes: https://cloud.google.com/translate/docs/languages 112 | 113 | 114 | """ 115 | 116 | error_ocr_no_reply: str = """reply to a photo to get the text""" 117 | 118 | lang_saved_message: str = """{} has been set as your main language.""" 119 | 120 | ocr_message_text: str = """`the text in the image:` \n\n {}""" 121 | 122 | translate_string_one: str = """**\ud83c\udf10 Translation**:\n\n`{}`\n\n**🔍 Detected language:** {} \n\n **Translated to**: {}""" 123 | 124 | translate_string_two: str = ( 125 | """**\ud83c\udf10 Translation**:\n\n`{}`\n\n**🔍 Detected language:** {}""" 126 | ) 127 | 128 | inline_text_string_one: str = """Translate from {} to {}""" 129 | 130 | error_msg_string: str = """**Error:** \n\n `{}` \n\n **forward this message to https://t.me/TDICSupport if you see this error again, try to forward your message too for better help**""" 131 | 132 | help_group_string: str = """To get help click on the button below""" 133 | 134 | 135 | google_tr_api_err_msg: str = """this is not text or this is google translate api limit, please try again later.""" 136 | 137 | ocr_err_msg_lang: str = """the language code is not supported in the ocr try to found the language code by click on the link {}""" 138 | 139 | err_must_specify_lang = """you must specify the language to translate.""" 140 | 141 | err_must_specify_text = """you must specify the text to translate.""" 142 | -------------------------------------------------------------------------------- /db/__init__.py: -------------------------------------------------------------------------------- 1 | from .functions import * 2 | -------------------------------------------------------------------------------- /db/db.py: -------------------------------------------------------------------------------- 1 | from tortoise import Tortoise, fields 2 | from tortoise.models import Model 3 | from config import db_url 4 | 5 | 6 | class users(Model): 7 | user_id = fields.BigIntField(pk=True) 8 | chat_lang = fields.TextField() 9 | 10 | 11 | async def init_db(): 12 | await Tortoise.init(db_url=db_url, modules={"models": [__name__]}) 13 | await Tortoise.generate_schemas() 14 | -------------------------------------------------------------------------------- /db/functions.py: -------------------------------------------------------------------------------- 1 | from .db import users 2 | 3 | 4 | async def add_chat(chat_id): 5 | await users.create(user_id=chat_id, chat_lang="en") 6 | 7 | 8 | async def chat_exists(chat_id): 9 | return await users.exists(user_id=chat_id) 10 | 11 | 12 | async def get_lang(chat_id: int) -> str: 13 | return (await users.get(user_id=chat_id)).chat_lang 14 | 15 | 16 | async def set_lang(chat_id: int, lang_code: str): 17 | await users.filter(user_id=chat_id).update(chat_lang=lang_code) 18 | 19 | 20 | async def get_users_count(): 21 | return await users.all().count() 22 | -------------------------------------------------------------------------------- /downloads/readme.md: -------------------------------------------------------------------------------- 1 | Folder of OCR Plugin. 2 | -------------------------------------------------------------------------------- /exporter_db_to_sqlite/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /exporter_db_to_sqlite/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Based on the codes written by Simon Willison, licensed under Apache-2.0 3 | https://github.com/simonw/db-to-sqlite 4 | """ 5 | 6 | from sqlalchemy import create_engine, inspect 7 | from sqlite_utils import Database 8 | import toposort 9 | 10 | 11 | def db_to_sqlite_func(connection, path): 12 | if connection.startswith("postgres://"): 13 | connection = connection.replace("postgres://", "postgresql://") 14 | db = Database(path) 15 | db_conn = create_engine(connection).connect() 16 | inspector = inspect(db_conn) 17 | tables = toposort.toposort_flatten( 18 | { 19 | table: {fk["referred_table"] for fk in inspector.get_foreign_keys(table)} 20 | for table in inspector.get_table_names() 21 | } 22 | ) 23 | for table in tables: 24 | pks = inspector.get_pk_constraint(table)["constrained_columns"] 25 | if len(pks) > 1: 26 | raise Exception("Multiple primary keys not currently supported") 27 | pk = None 28 | if pks: 29 | pk = pks[0] 30 | fks = inspector.get_foreign_keys(table) 31 | foreign_keys = [ 32 | ( 33 | # column, type, other_table, other_column 34 | fk["constrained_columns"][0], 35 | "INTEGER", 36 | fk["referred_table"], 37 | fk["referred_columns"][0], 38 | ) 39 | for fk in fks 40 | ] 41 | results = db_conn.execute("SELECT * FROM {}".format(table)) 42 | rows = (dict(r) for r in results) 43 | db[table].upsert_all(rows, pk=pk, foreign_keys=foreign_keys) 44 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from hydrogram import Client, filters, idle 2 | from pkg_resources import get_distribution 3 | from config import API_ID, API_HASH, TOKEN, sudofilter, db_url 4 | import os, sys 5 | from tortoise import run_async 6 | from threading import Thread 7 | from datetime import datetime 8 | from db.db import init_db 9 | from db.functions import get_users_count, chat_exists, get_lang 10 | from exporter_db_to_sqlite import db_to_sqlite_func 11 | 12 | bot = Client( 13 | "gpytranslatorbot", 14 | in_memory=True, 15 | api_id=API_ID, 16 | api_hash=API_HASH, 17 | bot_token=TOKEN, 18 | plugins=dict(root="plugins"), 19 | ) 20 | 21 | 22 | def stop_and_restart(): 23 | bot.stop() 24 | os.system("git pull") 25 | os.execl(sys.executable, sys.executable, *sys.argv) 26 | 27 | 28 | @bot.on_message( 29 | filters.command("r") 30 | & sudofilter 31 | & ~filters.forwarded 32 | & ~filters.group 33 | & ~filters.via_bot 34 | ) 35 | async def restart(bot, message): 36 | msgtxt = await message.reply("wait") 37 | Thread(target=stop_and_restart).start() 38 | await msgtxt.edit_text("done") 39 | 40 | 41 | @bot.on_message(filters.command("ping") & sudofilter & filters.private) 42 | async def ping(bot, message): 43 | a = datetime.now() 44 | m = await message.reply_text("pong") 45 | b = datetime.now() 46 | await m.edit_text(f"pong {(b - a).microseconds / 1000} ms") 47 | 48 | 49 | @bot.on_message(filters.command("bot_stats") & sudofilter) 50 | async def get_bot_stats(bot, message): 51 | await message.reply(f"the bot has {await get_users_count()} users") 52 | 53 | 54 | @bot.on_message(filters.command("get_user_lang") & sudofilter) 55 | async def get_lang_by_user_db(bot, message): 56 | if len(message.text.split()) > 1: 57 | chat_exists_check = await chat_exists(chat_id=message.command[1]) 58 | if chat_exists_check == True: 59 | await message.reply(await get_lang(chat_id=message.command[1])) 60 | else: 61 | await message.reply("¯\_(ツ)_/¯") 62 | else: 63 | await message.reply("¯\_(ツ)_/¯") 64 | 65 | 66 | @bot.on_message(filters.command("gpytranslate_version") & sudofilter) 67 | async def get_gpytranslate_lib_version(bot, message): 68 | gpytranslate_version = get_distribution("gpytranslate") 69 | await message.reply_text(f"gpytranslate version: {gpytranslate_version.version}") 70 | 71 | 72 | @bot.on_message(filters.command("backup_db") & sudofilter & filters.private) 73 | async def backup_db_cmd(bot, message): 74 | sqlite_output_file_name = "gpytranslatorbotdb_backup.sqlite3" 75 | db_to_sqlite_func(connection=db_url, path=sqlite_output_file_name) 76 | await message.reply_document(sqlite_output_file_name) 77 | os.remove(sqlite_output_file_name) 78 | 79 | 80 | async def startbot(): 81 | await init_db() 82 | await bot.start() 83 | await idle() 84 | 85 | 86 | run_async(startbot()) 87 | -------------------------------------------------------------------------------- /plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrcentimetre/gpytranslator-bot/f28488bc0a4696fd9b564237661b4fada5ec8b56/plugins/__init__.py -------------------------------------------------------------------------------- /plugins/addtodb.py: -------------------------------------------------------------------------------- 1 | from hydrogram import Client, filters 2 | from hydrogram.types import Message 3 | from db import functions as db 4 | 5 | 6 | @Client.on_message(filters.private, group=-1) 7 | async def check_chat(bot: Client, message: Message): 8 | chat_id = message.chat.id 9 | check_if_chat_exists = await db.chat_exists(chat_id) 10 | if not check_if_chat_exists: 11 | await db.add_chat(chat_id) 12 | await db.set_lang(chat_id, "en") 13 | -------------------------------------------------------------------------------- /plugins/callback.py: -------------------------------------------------------------------------------- 1 | from hydrogram import Client, filters 2 | from hydrogram.types import CallbackQuery 3 | import constants 4 | 5 | 6 | @Client.on_callback_query(filters.regex(r"^back$")) 7 | async def backtostart(bot: Client, query: CallbackQuery): 8 | await query.message.edit( 9 | constants.start_message_text.format(query.from_user.mention()), 10 | reply_markup=constants.start_message_reply_markup, 11 | ) 12 | 13 | 14 | @Client.on_callback_query(filters.regex(r"^help$")) 15 | async def helpbutton(bot: Client, query: CallbackQuery): 16 | await query.message.edit(constants.help_text, reply_markup=constants.help_markup) 17 | 18 | 19 | @Client.on_callback_query(filters.regex(r"^Credits$")) 20 | async def credits(bot: Client, query: CallbackQuery): 21 | await query.answer(constants.credits, show_alert=True) 22 | 23 | 24 | @Client.on_callback_query(filters.regex(r"^closethismsg$")) 25 | async def close_error_message_callback(bot: Client, query: CallbackQuery): 26 | await query.message.delete() 27 | -------------------------------------------------------------------------------- /plugins/groupchat.py: -------------------------------------------------------------------------------- 1 | from hydrogram import Client, filters, enums 2 | from hydrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton 3 | import constants 4 | from tr import tr 5 | from bot_errors_logger import logging_errors 6 | from bot_custom_exceptions import google_api_error 7 | from gpytranslate import TranslationError 8 | 9 | prefix = constants.prefix 10 | 11 | 12 | @Client.on_message(filters.command("poll", prefix) & filters.group & filters.reply) 13 | @logging_errors 14 | async def gen_poll_tr_group_chat(bot, message: Message): 15 | try: 16 | if message.reply_to_message.poll: 17 | replymsg = message.reply_to_message 18 | poll_options = "\n".join(i.text for i in replymsg.poll.options) 19 | txt_to_tr = f"{replymsg.poll.question}\n{poll_options}" 20 | if len(message.text.split()) > 1: 21 | tolanguage = message.command[1] 22 | else: 23 | tolanguage = "en" 24 | translation = await tr(txt_to_tr, targetlang=[tolanguage, "utf-16"]) 25 | translation_text = translation.text 26 | poll_que = translation_text.split("\n", 1)[0] 27 | poll_opt = translation_text.splitlines()[1:] 28 | return await bot.send_poll( 29 | chat_id=message.chat.id, 30 | question=poll_que, 31 | options=poll_opt, 32 | is_anonymous=replymsg.poll.is_anonymous, 33 | allows_multiple_answers=replymsg.poll.allows_multiple_answers, 34 | ) 35 | else: 36 | return 37 | except TranslationError: 38 | raise google_api_error(constants.google_tr_api_err_msg) 39 | 40 | 41 | @Client.on_message( 42 | filters.command(["tr", "tl", "translate"], prefix) & filters.group & filters.reply 43 | ) 44 | @logging_errors 45 | async def translategroup(bot, message: Message) -> None: 46 | try: 47 | if message.reply_to_message.poll is None: 48 | if message.reply_to_message.caption: 49 | to_translate = message.reply_to_message.caption 50 | elif message.reply_to_message.text: 51 | to_translate = message.reply_to_message.text 52 | else: 53 | return 54 | try: 55 | args = message.text.split()[1].lower() 56 | if "//" in args: 57 | language = args.split("//")[0] 58 | tolanguage = args.split("//")[1] 59 | else: 60 | language = await tr.detect(to_translate) 61 | tolanguage = args 62 | except IndexError: 63 | language = await tr.detect(to_translate) 64 | tolanguage = "en" 65 | translation = await tr( 66 | to_translate, sourcelang=language, targetlang=tolanguage 67 | ) 68 | await message.reply( 69 | constants.translate_string_one.format( 70 | translation.text, language, tolanguage 71 | ), 72 | parse_mode=enums.ParseMode.MARKDOWN, 73 | ) 74 | elif message.reply_to_message.poll is not None: 75 | options = "\n".join( 76 | x.text for x in message.reply_to_message.poll.options 77 | ) 78 | to_translate = f"{message.reply_to_message.poll.question}\n\n\n{options}" 79 | language = await tr.detect(to_translate) 80 | if len(message.text.split()) > 1: 81 | tolanguage = message.command[1] 82 | else: 83 | tolanguage = "en" 84 | translation = await tr( 85 | to_translate, sourcelang=language, targetlang=tolanguage 86 | ) 87 | await message.reply( 88 | constants.translate_string_one.format( 89 | translation.text, language, tolanguage 90 | ), 91 | parse_mode=enums.ParseMode.MARKDOWN, 92 | ) 93 | except TranslationError: 94 | raise google_api_error(constants.google_tr_api_err_msg) 95 | 96 | 97 | @Client.on_message(filters.command("tr", prefix) & filters.group & ~filters.reply) 98 | @logging_errors 99 | async def translategrouptwo(bot, message: Message): 100 | try: 101 | if len(message.text.split()) > 1: 102 | tolanguage = message.command[1] 103 | else: 104 | return await message.reply_text(constants.err_must_specify_lang) 105 | if len(message.text.split()) > 2: 106 | to_translate = message.text.split(None, 2)[2] 107 | else: 108 | return await message.reply_text(constants.err_must_specify_text) 109 | language = await tr.detect(message.text.split(None, 2)[2]) 110 | translation = await tr(to_translate, sourcelang=language, targetlang=tolanguage) 111 | await message.reply_text( 112 | constants.translate_string_one.format( 113 | translation.text, language, tolanguage 114 | ), 115 | parse_mode=enums.ParseMode.MARKDOWN, 116 | ) 117 | except TranslationError: 118 | raise google_api_error(constants.google_tr_api_err_msg) 119 | 120 | 121 | @Client.on_message( 122 | filters.command(["help", "help@gpytranslatorbot"], prefix) & filters.group 123 | ) 124 | @logging_errors 125 | async def helpgroupcmd(bot, message: Message): 126 | getmebot = await bot.get_me() 127 | await message.reply( 128 | constants.help_group_string, 129 | reply_markup=InlineKeyboardMarkup( 130 | [ 131 | [ 132 | InlineKeyboardButton( 133 | "🆘 Help", url=f"https://t.me/{getmebot.username}?start=help" 134 | ), 135 | InlineKeyboardButton( 136 | "🗑 Delete this message", callback_data="closethismsg" 137 | ), 138 | ], 139 | ] 140 | ), 141 | ) 142 | -------------------------------------------------------------------------------- /plugins/inline.py: -------------------------------------------------------------------------------- 1 | from hydrogram import Client 2 | from hydrogram.types import ( 3 | InlineQuery, 4 | InlineQueryResultArticle, 5 | InputTextMessageContent, 6 | ) 7 | import constants 8 | from tr import tr 9 | 10 | 11 | @Client.on_inline_query() 12 | async def translateinline(bot: Client, query: InlineQuery) -> None: 13 | try: 14 | to_translate = query.query.lower().split(None, 1)[1] 15 | language = await tr.detect(query.query.lower().split(None, 1)[1]) 16 | tolanguage = query.query.lower().split()[0] 17 | translation = await tr(to_translate, sourcelang=language, targetlang=tolanguage) 18 | trmsgtext = f"{translation.text}" 19 | await query.answer( 20 | [ 21 | InlineQueryResultArticle( 22 | title=constants.inline_text_string_one.format(language, tolanguage), 23 | description=f"{translation.text}", 24 | input_message_content=InputTextMessageContent(trmsgtext), 25 | ) 26 | ] 27 | ) 28 | except IndexError: 29 | return 30 | -------------------------------------------------------------------------------- /plugins/ocr.py: -------------------------------------------------------------------------------- 1 | from hydrogram import Client, filters 2 | from hydrogram.types import Message 3 | import pytesseract, PIL, os 4 | import constants 5 | from bot_errors_logger import logging_errors 6 | from py_multiapi import multiapi 7 | 8 | theapi = multiapi() 9 | 10 | prefix = constants.prefix 11 | 12 | 13 | def getocrlangsasalist(): 14 | a = "" 15 | for i in pytesseract.get_languages(): 16 | a += f"{i}\n" 17 | return a 18 | 19 | 20 | @Client.on_message(filters.command("ocr", prefix)) 21 | @logging_errors 22 | async def ocrcmd(bot, message: Message): 23 | if not message.reply_to_message: 24 | await message.reply(constants.error_ocr_no_reply) 25 | return 26 | if not message.reply_to_message.photo: 27 | await message.reply(constants.error_ocr_no_reply) 28 | return 29 | if message.reply_to_message.photo: 30 | await message.reply_to_message.download(file_name="ocr.jpg") 31 | await message.reply( 32 | constants.ocr_message_text.format( 33 | pytesseract.image_to_string(PIL.Image.open("downloads/ocr.jpg")) 34 | ) 35 | ) 36 | os.remove("downloads/ocr.jpg") 37 | 38 | 39 | @Client.on_message(filters.command("ocrlang", prefix)) 40 | @logging_errors 41 | async def ocrlangcmd(bot, message: Message): 42 | if len(message.text.split()) > 1: 43 | msg_arg = message.command[1] 44 | if not message.reply_to_message: 45 | return await message.reply(constants.error_ocr_no_reply) 46 | if not message.reply_to_message.photo: 47 | return await message.reply(constants.error_ocr_no_reply) 48 | if message.reply_to_message.photo: 49 | if msg_arg in pytesseract.get_languages(): 50 | await message.reply_to_message.download(file_name="ocr.jpg") 51 | await message.reply( 52 | constants.ocr_message_text.format( 53 | pytesseract.image_to_string( 54 | PIL.Image.open("downloads/ocr.jpg"), lang=msg_arg 55 | ) 56 | ) 57 | ) 58 | os.remove("downloads/ocr.jpg") 59 | else: 60 | ocrlangslist = getocrlangsasalist() 61 | thepaste = await theapi.paste(content=ocrlangslist) 62 | thepaste = thepaste["paste_url"] 63 | await message.reply(constants.ocr_err_msg_lang.format(thepaste)) 64 | else: 65 | await ocrcmd(bot, message) 66 | -------------------------------------------------------------------------------- /plugins/privatechat.py: -------------------------------------------------------------------------------- 1 | from hydrogram import Client, filters, enums 2 | from hydrogram.types import Message 3 | from bot_errors_logger import logging_errors 4 | import constants 5 | import db 6 | from tr import tr 7 | from gpytranslate import TranslationError 8 | import html 9 | from bot_custom_exceptions import google_api_error 10 | 11 | prefix = constants.prefix 12 | 13 | 14 | @Client.on_message(filters.command("start", prefix) & filters.private) 15 | @logging_errors 16 | async def start(bot, message: Message): 17 | if len(message.text.split()) > 1: 18 | if message.command[1] == "help": 19 | await message.reply_text(constants.help_text) 20 | else: 21 | await message.reply_text( 22 | constants.start_message_text.format(message.from_user.mention()), 23 | reply_markup=constants.start_message_reply_markup, 24 | ) 25 | 26 | 27 | @Client.on_message(filters.command("help", prefix) & filters.private) 28 | @logging_errors 29 | async def help(bot, message: Message): 30 | await message.reply_text(constants.help_text) 31 | 32 | 33 | @Client.on_message(filters.command("donate", prefix) & filters.private) 34 | @logging_errors 35 | async def donate(bot, message: Message): 36 | await message.reply_text(constants.donate_text) 37 | 38 | 39 | @Client.on_message(filters.command("language", prefix)) 40 | @logging_errors 41 | async def language(bot, message: Message): 42 | await message.reply_text(constants.language_text) 43 | 44 | 45 | @Client.on_message(filters.command("lang", prefix) & filters.private) 46 | @logging_errors 47 | async def setmylang(bot, message: Message): 48 | if len(message.text.split()) > 1: 49 | thelang = message.command[1] 50 | await message.reply(constants.lang_saved_message.format(thelang)) 51 | await db.set_lang(message.chat.id, thelang) 52 | else: 53 | await message.reply(constants.language_text) 54 | 55 | 56 | @Client.on_message(filters.command("poll", prefix) & filters.private & filters.reply) 57 | @logging_errors 58 | async def gen_poll_tr_private_chat(bot, message: Message): 59 | try: 60 | if message.reply_to_message.poll: 61 | replymsg = message.reply_to_message 62 | poll_options = "\n".join(i.text for i in replymsg.poll.options) 63 | txt_to_tr = f"{replymsg.poll.question}\n{poll_options}" 64 | if len(message.text.split()) > 1: 65 | tolanguage = message.command[1] 66 | else: 67 | tolanguage = await db.get_lang(message.chat.id) 68 | translation = await tr(txt_to_tr, targetlang=[tolanguage, "utf-16"]) 69 | translation_text = translation.text 70 | poll_que = translation_text.split("\n", 1)[0] 71 | poll_opt = translation_text.splitlines()[1:] 72 | return await bot.send_poll( 73 | chat_id=message.chat.id, 74 | question=poll_que, 75 | options=poll_opt, 76 | is_anonymous=replymsg.poll.is_anonymous, 77 | allows_multiple_answers=replymsg.poll.allows_multiple_answers, 78 | ) 79 | else: 80 | return 81 | except TranslationError: 82 | raise google_api_error(constants.google_tr_api_err_msg) 83 | 84 | 85 | @Client.on_message( 86 | filters.private & ~filters.command("tr", prefix) & ~filters.command("start") 87 | ) 88 | @logging_errors 89 | async def main(bot, message: Message): 90 | try: 91 | if message.poll is None: 92 | textorcaption = message.text or message.caption 93 | userlang = await db.get_lang(message.chat.id) 94 | translation = await tr(textorcaption, targetlang=[userlang, "utf-16"]) 95 | language = await tr.detect(textorcaption or message.caption) 96 | await message.reply( 97 | constants.translate_string_two.format(translation.text, language) 98 | ) 99 | elif message.poll is not None: 100 | userlang = await db.get_lang(message.chat.id) 101 | options = "\n".join(x.text for x in message.poll.options) 102 | to_translate = f"{message.poll.question}\n\n\n{options}" 103 | fromlang = await tr.detect(to_translate) 104 | translation = await tr(to_translate, targetlang=[userlang, "utf-16"]) 105 | await message.reply( 106 | constants.translate_string_two.format(translation.text, fromlang), 107 | parse_mode=enums.ParseMode.MARKDOWN, 108 | ) 109 | except TranslationError: 110 | raise google_api_error(constants.google_tr_api_err_msg) 111 | 112 | 113 | @Client.on_message(filters.command("tr", prefix) & filters.private & ~filters.reply) 114 | @logging_errors 115 | async def translateprivatetwo(bot, message: Message): 116 | try: 117 | if len(message.text.split()) > 1: 118 | msgcmdone = message.command[1] 119 | if len(msgcmdone.split("-")) > 1: 120 | tolanguage = msgcmdone.split("-")[0] 121 | language = msgcmdone.split("-")[1] 122 | if (tolanguage == "") or (language == ""): 123 | tolanguage = message.command[1] 124 | language = await tr.detect(message.text.split(None, 2)[2]) 125 | else: 126 | tolanguage = message.command[1] 127 | language = await tr.detect(message.text.split(None, 2)[2]) 128 | else: 129 | return await message.reply_text(constants.err_must_specify_lang) 130 | if len(message.text.split()) > 2: 131 | to_translate = message.text.split(None, 2)[2] 132 | else: 133 | return await message.reply_text(constants.err_must_specify_text) 134 | translation = await tr(to_translate, sourcelang=language, targetlang=tolanguage) 135 | await message.reply_text( 136 | constants.translate_string_one.format( 137 | translation.text, language, tolanguage 138 | ), 139 | parse_mode=enums.ParseMode.MARKDOWN, 140 | ) 141 | except TranslationError: 142 | raise google_api_error(constants.google_tr_api_err_msg) 143 | 144 | 145 | @Client.on_message(filters.command("tr", prefix) & filters.private & filters.reply) 146 | @logging_errors 147 | async def translateprivate_reply(bot, message: Message): 148 | try: 149 | if message.reply_to_message.poll is None: 150 | if message.reply_to_message.caption: 151 | to_translate = message.reply_to_message.caption 152 | elif message.reply_to_message.text: 153 | to_translate = message.reply_to_message.text 154 | else: 155 | return 156 | language = await tr.detect(to_translate) 157 | if len(message.text.split()) > 1: 158 | tolanguage = message.command[1] 159 | else: 160 | tolanguage = "en" 161 | translation = await tr( 162 | to_translate, sourcelang=language, targetlang=tolanguage 163 | ) 164 | await message.reply( 165 | constants.translate_string_one.format( 166 | translation.text, language, tolanguage 167 | ), 168 | parse_mode=enums.ParseMode.MARKDOWN, 169 | ) 170 | elif message.reply_to_message.poll is not None: 171 | options = "\n".join( 172 | x.text for x in message.reply_to_message.poll.options 173 | ) 174 | to_translate = f"{message.reply_to_message.poll.question}\n\n\n{options}" 175 | language = await tr.detect(to_translate) 176 | if len(message.text.split()) > 1: 177 | tolanguage = message.command[1] 178 | else: 179 | tolanguage = "en" 180 | translation = await tr( 181 | to_translate, sourcelang=language, targetlang=tolanguage 182 | ) 183 | await message.reply( 184 | constants.translate_string_one.format( 185 | translation.text, language, tolanguage 186 | ), 187 | parse_mode=enums.ParseMode.MARKDOWN, 188 | ) 189 | except TranslationError: 190 | raise google_api_error(constants.google_tr_api_err_msg) 191 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | gpytranslate 2 | hydrogram 3 | TgCrypto 4 | pytesseract 5 | Pillow 6 | py-multiapi 7 | tortoise-orm 8 | sqlite-utils 9 | sqlalchemy 10 | toposort 11 | 12 | # optional for postgresql 13 | # for db backup 14 | 15 | psycopg2-binary 16 | 17 | # for tortoise-orm 18 | 19 | asyncpg 20 | -------------------------------------------------------------------------------- /requirements_mysql.txt: -------------------------------------------------------------------------------- 1 | # optional for mysql 2 | 3 | # for db backup 4 | 5 | mysqlclient 6 | 7 | # for tortoise-orm 8 | 9 | aiomysql 10 | 11 | # or 12 | 13 | asyncmy 14 | -------------------------------------------------------------------------------- /tr.py: -------------------------------------------------------------------------------- 1 | from gpytranslate import Translator 2 | 3 | tr = Translator() 4 | --------------------------------------------------------------------------------