├── .deepsource.toml ├── .gitignore ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── bot.py ├── docker-compose.yml ├── requirements.txt ├── settings.json └── src ├── api.py └── logger.py /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "python" 5 | enabled = true 6 | 7 | [analyzers.meta] 8 | runtime_version = "3.x.x" 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Pycharm Settings 132 | .idea/ 133 | 134 | # Log folder 135 | log/* -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to CONTRIBUTING PyCompiler ? 2 | 3 | 1. Fork project into your repo 4 | 2. Change your branch to *devlop* then 5 | 3. Update or add somethings to project and push on your forked repo 6 | 4. Pull Request for code review and update -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | RUN apt-get update && apt-get install -y locales python3 python3-pip && rm -rf /var/lib/apt/lists/* 4 | 5 | COPY . /app 6 | 7 | WORKDIR /app 8 | 9 | RUN pip3 install -r requirements.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Javad Rajabzade 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 | # PyCompiler 2 |

3 | GitHub repo size 4 | GitHub contributors 5 | GitHub last commit 6 | GitHub pull requests 7 | Code Factor 8 |

9 | 10 | PyFarsi Telegram Bot Compiler, Used Rextester.com api. 11 | 12 | ## Contributing 13 | If you want to contribute to this project, read [Contributing Guide](CONTRIBUTING.md). 14 | 15 | ### How to runing bot? 16 | 17 | 1. First clone project `git clone https://github.com/PyFarsi/PyCompiler.git` 18 | 2. Create python virtual enviroment `python3 -m venv .env` 19 | 3. Install Python library `pip install -r requirements.txt` 20 | 4. Set your token into *settings.json* 21 | 5. Run bot `python3 bot.py` 22 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import os 4 | import re 5 | 6 | import telegram 7 | from telegram.ext import CommandHandler 8 | from telegram.ext import Filters 9 | from telegram.ext import MessageHandler 10 | from telegram.ext import Updater 11 | 12 | from src.api import RextesterApi 13 | from src.logger import Logger 14 | 15 | rex = RextesterApi() 16 | log = Logger() 17 | 18 | 19 | def clang(bot, update): 20 | msg = "اوکی, لطفا کدی که به زبان CLang هست را برایم بفرستید" 21 | log.info("CLang Request.") 22 | bot.message.reply_text(msg) 23 | 24 | 25 | def cpplang(bot, update): 26 | msg = "اوکی, لطفا کدی که به زبان CPlusPlus هست را برایم بفرستید" 27 | log.info("C++ Request.") 28 | bot.message.reply_text(msg) 29 | 30 | 31 | def mysql(bot, update): 32 | msg = "اوکی, لطفا کدی که به زبان MySQL هست را برایم بفرستید" 33 | log.info("MySQL Request.") 34 | bot.message.reply_text(msg) 35 | 36 | 37 | def sql_server(bot, update): 38 | msg = "اوکی, لطفا کدی که به زبان SQL Server هست را برایم بفرستید" 39 | log.info("SQL Server Request.") 40 | bot.message.reply_text(msg) 41 | 42 | 43 | def psql(bot, update): 44 | msg = "اوکی, لطفا کدی که به زبان PostgreSQL هست را برایم بفرستید" 45 | log.info("PostgreSQL Request.") 46 | bot.message.reply_text(msg) 47 | 48 | 49 | def python3(bot, update): 50 | msg = "اوکی, لطفا کدی که به زبان Python 3 هست را برایم بفرستید" 51 | log.info("Python 3 Request.") 52 | bot.message.reply_text(msg) 53 | 54 | def golang(bot, update): 55 | msg = "اوکی, لطفا کدی که به زبان Go هست را برایم بفرستید" 56 | log.info("Golang Request.") 57 | bot.message.reply_text(msg) 58 | 59 | def get_code(bot, update): 60 | msg = bot.message or bot.edited_message 61 | code = msg.text 62 | msg_reply = msg.reply_to_message.text 63 | cid = msg.from_user.first_name 64 | really_cid = msg.from_user.id 65 | callback_result(msg, code, msg_reply, cid, really_cid) 66 | 67 | 68 | def callback_result(message, code, msg_reply, cid, really_cid): 69 | if (securityCheck(code) and ("اوکی, لطفا کدی که به زبان" in msg_reply) 70 | and "." not in msg_reply): 71 | message.reply_text(f"کتابخانه یا تابع استفاده شده در کد شما مجاز نیست!") 72 | return 73 | 74 | if msg_reply: 75 | if ("CLang" in msg_reply and ("اوکی, لطفا کدی که به زبان" in msg_reply) 76 | and "." not in msg_reply): 77 | message.reply_text( 78 | rex.rextester_api(6, code, cid, really_cid), 79 | parse_mode=telegram.ParseMode.MARKDOWN, 80 | ) 81 | elif ("CPlusPlus" in msg_reply and ("اوکی, لطفا کدی که به زبان" in msg_reply) 82 | and "." not in msg_reply): 83 | message.reply_text( 84 | rex.rextester_api(7, code, cid, really_cid), 85 | parse_mode=telegram.ParseMode.MARKDOWN, 86 | ) 87 | elif ("MySQL" in msg_reply 88 | and ("اوکی, لطفا کدی که به زبان" in msg_reply) 89 | and "." not in msg_reply): 90 | message.reply_text( 91 | rex.rextester_api(33, code, cid, really_cid), 92 | parse_mode=telegram.ParseMode.MARKDOWN, 93 | ) 94 | elif ("SQL Server" in msg_reply 95 | and ("اوکی, لطفا کدی که به زبان" in msg_reply) 96 | and "." not in msg_reply): 97 | message.reply_text( 98 | rex.rextester_api(16, code, cid, really_cid), 99 | parse_mode=telegram.ParseMode.MARKDOWN, 100 | ) 101 | elif ("PostgreSQL" in msg_reply 102 | and ("اوکی, لطفا کدی که به زبان" in msg_reply) 103 | and "." not in msg_reply): 104 | message.reply_text( 105 | rex.rextester_api(34, code, cid, really_cid), 106 | parse_mode=telegram.ParseMode.MARKDOWN, 107 | ) 108 | elif ("Python 3" in msg_reply 109 | and ("اوکی, لطفا کدی که به زبان" in msg_reply) 110 | and "." not in msg_reply): 111 | message.reply_text( 112 | rex.rextester_api(24, code, cid, really_cid), 113 | parse_mode=telegram.ParseMode.MARKDOWN, 114 | ) 115 | 116 | elif ("Go" in msg_reply 117 | and ("اوکی, لطفا کدی که به زبان" in msg_reply) 118 | and "." not in msg_reply): 119 | message.reply_text( 120 | rex.rextester_api(20, code, cid, really_cid), 121 | parse_mode=telegram.ParseMode.MARKDOWN, 122 | ) 123 | 124 | 125 | def get_settings(): 126 | if os.path.exists("./settings.json"): 127 | with open("./settings.json") as setting_file: 128 | setting = json.load(setting_file) 129 | 130 | return { 131 | "token": setting["token"], 132 | "proxy": setting["proxy"], 133 | "proxy_address": setting["proxy_address"], 134 | } 135 | raise Exception("settings.json not found.") 136 | 137 | def securityCheck(code): 138 | if re.findall(r"(exec\(.*\))|(eval\(.*\))|(__import__\(.*\))", code): 139 | return True 140 | 141 | filter_modules = ("os", "sys", "platform", "subprocess") 142 | filter_codes = [f"import {m}" for m in filter_modules] + [f"from {m}" for m in filter_modules] 143 | 144 | for fm in filter_codes: 145 | if fm in code: 146 | return True 147 | 148 | if "subprocess" in code or "create_subprocess_shell" in code: 149 | return True 150 | 151 | return False 152 | 153 | if __name__ == "__main__": 154 | settings = get_settings() 155 | if settings["proxy"]: 156 | updater = Updater( 157 | settings["token"], 158 | use_context=True, 159 | request_kwargs={ 160 | "proxy_url": f"socks5h://{settings['proxy_address']}" 161 | }, 162 | ) 163 | else: 164 | updater = Updater(settings["token"], use_context=True) 165 | dp = updater.dispatcher 166 | dp.add_handler( 167 | MessageHandler( 168 | filters=(Filters.chat_type.groups & Filters.text & Filters.reply), 169 | callback=get_code, 170 | )) 171 | dp.add_handler(CommandHandler("py", python3, Filters.chat_type.groups)) 172 | dp.add_handler(CommandHandler("c", clang, Filters.chat_type.groups)) 173 | dp.add_handler(CommandHandler("cpp", cpplang, Filters.chat_type.groups)) 174 | dp.add_handler(CommandHandler("mysql", mysql, Filters.chat_type.groups)) 175 | dp.add_handler( 176 | CommandHandler("sqlsv", sql_server, Filters.chat_type.groups)) 177 | dp.add_handler(CommandHandler("psql", psql, Filters.chat_type.groups)) 178 | dp.add_handler(CommandHandler("go", golang, Filters.chat_type.groups)) 179 | updater.start_polling() 180 | updater.idle() 181 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | bot: 4 | build: . 5 | restart: always 6 | command: python3 bot.py 7 | volumes: 8 | - .:/app -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | telegram 2 | python-telegram-bot[socks] 3 | requests -------------------------------------------------------------------------------- /settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "token": "", 3 | "proxy": false, 4 | "proxy_address": "127.0.0.1:9050" 5 | } 6 | -------------------------------------------------------------------------------- /src/api.py: -------------------------------------------------------------------------------- 1 | from requests import get, post 2 | import json 3 | 4 | STATS_ITEMS = ( 5 | ("Absolute running time", "مدت زمان اجرا"), 6 | ("cpu time", "مدت پردازش"), 7 | ("memory peak", "مقدار رم مصرف شده"), 8 | ("absolute service time", "مدت استفاده از سرویس"), 9 | ("Compilation time", "مدت زمان کامپایل"), 10 | ("Execution time", "مدت زمان اجرا"), 11 | ("rows selected", "ردیف های انتخاب شده"), 12 | ("rows affected", "ردیف های تحت تاثیر"), 13 | ("sec", "ثانیه"), 14 | ("absolute running time", "مدت زمان در حال اجرا"), 15 | (", ", "\n> "), 16 | ) 17 | 18 | COMPILER_ARGS = { 19 | "c": "-Wall -std=gnu99 -O2 -o a.out source_file.c", 20 | "cpp": "-Wall -std=c++14 -O2 -o a.out source_file.cpp", 21 | "go": "-o a.out source_file.go" 22 | } 23 | 24 | DATA_TO_SEND = { 25 | 'LanguageChoiceWrapper': '', 26 | 'EditorChoiceWrapper': '1', 27 | 'LayoutChoiceWrapper': '1', 28 | 'Program': '', 29 | 'CompilerArgs': '', 30 | 'Input': '', 31 | 'ShowWarnings': 'false', 32 | 'Privacy': '', 33 | 'PrivacyUsers': '', 34 | 'Title': '', 35 | 'SavedOutput': '', 36 | 'WholeError': '', 37 | 'WholeWarning': '', 38 | 'StatsToSave': '', 39 | 'CodeGuid': '', 40 | 'IsInEditMode': 'False', 41 | 'IsLive': 'False' 42 | } 43 | class RextesterApi: 44 | @staticmethod 45 | def __replace(code: str): 46 | for rep in STATS_ITEMS: 47 | code = code.replace(*rep) 48 | return code 49 | 50 | @staticmethod 51 | def __message(interpreter: str, user: str, code: str, result: str, 52 | stats: str) -> str: 53 | return f""" 54 | *زبان :* _{interpreter}_ 55 | *کاربر :* {user}\n 56 | *کد ارسال شده :* \n 57 | `{code}`\n 58 | *نتیجه :* \n 59 | `{result}`\n 60 | *منابع مصرف شده :* \n 61 | `{stats}` 62 | 63 | . 64 | """ 65 | 66 | 67 | def rextester_api(self, lang_id: int, code: str, uid: str, username: str): 68 | if lang_id == 6: 69 | data = DATA_TO_SEND 70 | data['Program'] = code 71 | data['LanguageChoiceWrapper'] = lang_id 72 | data['CompilerArgs'] = COMPILER_ARGS['c'] 73 | resp = post( 74 | f"https://rextester.com/rundotnet/Run", 75 | data=data 76 | ).json() 77 | elif lang_id == 7: 78 | data = DATA_TO_SEND 79 | data['Program'] = code 80 | data['LanguageChoiceWrapper'] = lang_id 81 | data['CompilerArgs'] = COMPILER_ARGS['cpp'] 82 | resp = post( 83 | f"https://rextester.com/rundotnet/Run", 84 | data=data 85 | ).json() 86 | elif lang_id == 20: 87 | data = DATA_TO_SEND 88 | data['Program'] = code 89 | data['LanguageChoiceWrapper'] = lang_id 90 | data['CompilerArgs'] = COMPILER_ARGS['go'] 91 | resp = post( 92 | f"https://rextester.com/rundotnet/Run", 93 | data=data 94 | ).json() 95 | else: 96 | data = DATA_TO_SEND 97 | data['Program'] = code 98 | data['LanguageChoiceWrapper'] = lang_id 99 | resp = post( 100 | f"https://rextester.com/rundotnet/Run", 101 | data=data 102 | ).json() 103 | 104 | """Response List""" 105 | errors = resp["Errors"] 106 | result = resp["Result"] 107 | stats = f"> {self.__replace(resp['Stats'])}" 108 | 109 | lang = None 110 | if lang_id == 6: 111 | lang = "C" 112 | elif lang_id == 7: 113 | lang = "++C" 114 | elif lang_id == 33: 115 | lang = "MySQL" 116 | elif lang_id == 16: 117 | lang = "SQL Server" 118 | elif lang_id == 34: 119 | lang = "PostgreSQL" 120 | elif lang_id == 24: 121 | lang = "Python 3" 122 | elif lang_id == 20: 123 | lang = "Go" 124 | 125 | if errors is not None: 126 | return self.__message( 127 | lang, 128 | f"[{uid}](tg://user?id={username})", 129 | code if len(code) < 500 else "Telegram limited character size", 130 | errors, 131 | stats, 132 | ) 133 | return self.__message( 134 | lang, 135 | f"[{uid}](tg://user?id={username})", 136 | code if len(code) < 500 else "Telegram limited character size", 137 | result, 138 | stats, 139 | ) 140 | -------------------------------------------------------------------------------- /src/logger.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import os 3 | 4 | 5 | class MetaSingleton(type): 6 | _instance = {} 7 | 8 | def __call__(cls, *args, **kwargs): 9 | if cls not in cls._instance: 10 | cls._instance[cls] = super().__call__(*args, **kwargs) 11 | return cls._instance[cls] 12 | 13 | 14 | class Logger(metaclass=MetaSingleton): 15 | @staticmethod 16 | def _write_file(type_log, msg: str): 17 | if not os.path.exists('log'): 18 | os.mkdir('log') 19 | log_write = f'[{type_log}] [{str(datetime.datetime.now())}]: {msg}\n' 20 | print(log_write) 21 | with open(f'log/{datetime.date.today()}.log', 'a') as file_log: 22 | file_log.write(log_write) 23 | 24 | def info(self, msg): 25 | self._write_file('Info', msg) 26 | 27 | def error(self, msg): 28 | self._write_file('Error', msg) 29 | 30 | def warning(self, msg): 31 | self._write_file('Warning', msg) 32 | 33 | def debug(self, msg): 34 | self._write_file('Debug', msg) 35 | --------------------------------------------------------------------------------