├── .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 |
4 |
5 |
6 |
7 |
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 |
--------------------------------------------------------------------------------