Skip to content
This repository was archived by the owner on Dec 22, 2023. It is now read-only.

Added a telegram bot which runs python code #624

Merged
merged 6 commits into from
Oct 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Scripts/Bots/runPython_bot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
token.txt
__pycache__
*.pyc
11 changes: 11 additions & 0 deletions Scripts/Bots/runPython_bot/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
hi:
@echo hi

requirements:
rm -rf venv
python3.8 -m venv venv
. venv/bin/activate && python3.8 -m pip install -r requirements.txt -U
clear

start:
. venv/bin/activate && python3.8 start.py
95 changes: 95 additions & 0 deletions Scripts/Bots/runPython_bot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# runPython_bot

Run python code from your telegram chat !

[![GitHub license](https://img.shields.io/github/license/aahnik/lovely-telegram)](https://github.com/aahnik/lovely-telegram/blob/main/LICENSE)
[![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org/)
[![Maintenance Yes](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://gitHub.com/aahnik/REPO/graphs/commit-activity)

![RunPythonBot](https://user-images.githubusercontent.com/66209958/97750350-9d773d80-1b16-11eb-89a5-7626f5547a10.png)

<!-- A simple bot that runs python code. Free and Open Source. For more info visit http://bit.ly/runPython -->

## Find on Telegram

You can find this bot on Telegram: click on this [link](https://t.me/@runPython_bot) or search `@runPython_bot` on telegram. The spelling is case sensitive.

This bot is deployed on [Python Anywhere](https://www.pythonanywhere.com/) free Beginner Account.
You may check whether the bot is alive or not, by clicking on the start command. If the bot responds, it is alive.

## Example Use

You may use pythonic expressions to easily calculate any complex problem. Or you may test your algorithms on the go.

> If you are viewing from a smartphone, click on the gif to view full screen ...

![runPython_bot](https://user-images.githubusercontent.com/66209958/97753037-1e383880-1b1b-11eb-863e-bcf82006820b.gif)

## Deploy

You can easily *deploy this bot* on [Python Anywhere](https://www.pythonanywhere.com/) or your **local machine** by following the below steps:

> Note: While pasting on your machine terminal you should use `Ctrl+Shift+V` but make sure to use `Ctrl+V` to paste in the Python Anywhere bash console from the browser.

Create a free Python Anywhere account and open a Bash Console, which has everything pre-loaded.

If you are planning to deploy on your **own machine**, make sure to have `Python3+`, `pip`, and `make`.

The following instructions will work smoothly on *Linux* and *Mac*. If you are on Windows, you may have to make slight modifications. Google is your best friend here.

- Create a `projects` directory and move into it

mkdir projects && cd projects

- Clone this repository containing the collection of bots

git clone https://github.com/aahnik/lovely-telegram.git

- Delete all other folders except the folder containing this bot

find ./lovely-telegram -mindepth 1 ! -regex '^./lovely-telegram/runPython_bot\(/.*\)?' -delete

- Now move into the directory which contains this README file, you are reading now.

cd lovely-telegram/runPython_bot

- Now add the token in the first line of `token.txt`.Run `cat > token.txt` -> Paste the token -> Press `Ctrl+D`

- To install all dependencies. Simply run `make requirements`

- Activate the bot by running `make start`

- You may now close the Python Anywhere bash console window from your browser, but the bot will continue running.

Your bot is now up and running, Enjoy ! 😊

To stop the bot, Press `Ctrl+C`. To restart the bot run `make start`

## Limitations

Currently, the bot is deployed on a Free Tier account of Python Anywhere.

For security and performance reasons, you **cannot** do the following with the bot:

- import any package
- run the `input()` function
- run the `open()` function
- Execute a piece of code which takes longer than 30 seconds to execute.

You may overcome these limitations by tweaking the code a little bit and running the bot on your own server.

## The Shameless Plug

Authored by **Aahnik Daw**.

You may connect with me by clicking on any of the icons below !

<a href = "https://medium.com/@aahnikdaw" > <img src = "https://github.com/aahnik/aahnik/blob/master/social_media_logos/medium.png?raw=true" alt = "medium" > </a >
<a href = "https://www.facebook.com/aahnik.daw" > <img src = "https://github.com/aahnik/aahnik/blob/master/social_media_logos/facebook.png?raw=true" alt = "facebook" > </a >
<a href = "https://www.linkedin.com/in/aahnik-daw-067a011b3/" > <img src = "https://github.com/aahnik/aahnik/blob/master/social_media_logos/linkedin.png?raw=true" alt = "linkedin" > </a >
<a href = "https://t.me/AahniKDaw" > <img src = "https://github.com/aahnik/aahnik/blob/master/social_media_logos/telegram.png?raw=true" alt = "telegram" > </a >
<a href = "https://twitter.com/AahnikD" > <img src = "https://github.com/aahnik/aahnik/blob/master/social_media_logos/twitter.png?raw=true" alt = "twitter" > </a >
<a href = "https://www.quora.com/profile/Aahnik-Daw" > <img src = "https://github.com/aahnik/aahnik/blob/master/social_media_logos/quora.png?raw=true" alt = "quora" > </a >
<a href = "https://www.youtube.com/channel/UCcEbN0d8iLTB6ZWBE_IDugg" > <img src = "https://github.com/aahnik/aahnik/blob/master/social_media_logos/youtube.png?raw=true" alt = "youtube" > </a >
<a href = "https://stackoverflow.com/users/13523305/aahnik-daw" > <img src = "https://github.com/aahnik/aahnik/blob/master/social_media_logos/stackOverflow.png?raw=true" alt = "stackOverflow" > </a >
<a href = "https://dev.to/aahnik" > <img src = "https://github.com/aahnik/aahnik/blob/master/social_media_logos/dev.png?raw=true" alt = "dev" > </a >
1 change: 1 addition & 0 deletions Scripts/Bots/runPython_bot/bot/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
'''Run Python Code on Telegram [BOT]'''
59 changes: 59 additions & 0 deletions Scripts/Bots/runPython_bot/bot/execute_code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'''
This module is responsible for handling the execution of python code given by the telegram user.
'''


import subprocess
from subprocess import TimeoutExpired


def run(update):
'''
This function takes an Telegram `update` object, passed by the `reply` function in runPython_bot module, and returns the result after executing update.message.text.
'''

def execute_py(code):
'''
This function takes a string of python code and executes it in a subprocess of timeout 30s, and returns the standard out and standard error.

Learn more about subprocesses from the official docs of Python
https://docs.python.org/3/library/subprocess.html

For a shorter intro read this stack overflow answer
https://stackoverflow.com/questions/64606880/how-to-get-the-python-interactive-shell-output-of-a-python-code-in-a-variable
'''

proc = subprocess.Popen(['/usr/bin/python3.8', '-c', code],
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try:
stdout, stderr = proc.communicate(timeout=30)
except TimeoutExpired:
return '', '😢 Timeout is 30s. I have limited resources. You may increase the timeout and run this bot on your own server if required.'
return stdout.decode('utf-8'), stderr.decode('utf-8')

def func(input_text):
'''
This function is a helper function which does some validation job before passing the code to execute_py.
'''

restricted_words = ['quit', 'input', 'open', 'import']
if any(word in input_text for word in restricted_words):
# block usage of this words for security and performance issues
out = f'☹️ SECURITY ISSUE: You have used a restricted word \n {restricted_words}'
return out
stdout, stderr = execute_py(input_text)
if str(stdout) or str(stderr):
out = f'{stdout} \n{stderr}'
return out
return None

input_text = update.message.text
out = func(input_text)
if not out:
try:
out = str(eval(input_text))
# if there is no output, it may be due to the fact that the code does not have print and is a python expression.
# so the bot evaluates it using eval()
except Exception as e:
out = f'''😔 If you are planning to write lines after this line then try writing them in a single message. \n**To print something, try using print function**\n Executing your code gave no stdout or stderr. \nSo I tried to evaluate it by using eval(). That raised the following error \n {e}'''
return out
75 changes: 75 additions & 0 deletions Scripts/Bots/runPython_bot/bot/runPython_bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'''
I'm the *Python Runner* bot 😀

I have been created by Aahnik Daw and you may see my [source code](http://bit.ly/runPython) on GitHub.

I am currently *alive and kicking* on a free tier server of Python Anywhere.

😁 Sometimes I behave quite differently from a regular python interactive shell.
Certain things are not allowed to prevent horrible consequences.

_Start using me to figure out more about me_.

Every message is executed freshly. Suppose you define a variable in one message, you wont be able to access it in the next message.

But you can write _multiline code in one message_
->Tab is 4 spaces
-> Newline is a newline in the same message body

If I am being used by too many people, there may be delay in response as I am deployed on a free tier server.
'''

# the module docstring will be sent to the user if the bot is alive

from telegram.ext import CommandHandler, MessageHandler, Filters, Updater
# python-telegram-bot is a Pythonic Wrapper to the core Telegram API
# it helps us to be DRY by giving us convinient wrapper functions to deal with Telegram API
# you can install it by pip install python-telegram-bot --upgrade
# learn more about it here https://github.com/python-telegram-bot/python-telegram-bot


from .execute_code import run

# read the token for authenticating our bot
with open('token.txt') as f:
tok = f.readline().strip()


def bot():
'''
Running this function runs the bot
You may learn more from this tutorial
https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-Your-first-Bot
'''

updater = Updater(token=tok)

dispatcher = updater.dispatcher

def start(update, context):
'''
This fuction replies to the start command
'''

context.bot.send_message(
chat_id=update.effective_chat.id, text=f'{__doc__}\n', parse_mode='Markdown')
# for more info on parse modes see https://python-telegram-bot.readthedocs.io/en/stable/telegram.parsemode.html

def reply(update, context):
'''
This function replies to any non-command messages
'''
returned_val = run(update)
context.bot.send_message(
chat_id=update.effective_chat.id, text=returned_val)

start_handler = CommandHandler('start', start)
message_handler = MessageHandler(Filters.text & (~Filters.command), reply)

dispatcher.add_handler(start_handler)
dispatcher.add_handler(message_handler)

updater.start_polling()
# Search google to know what is polling...
# i came to know while building this project
# this can be stopped by interrupting the terminal by `Ctrl+C`
15 changes: 15 additions & 0 deletions Scripts/Bots/runPython_bot/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
APScheduler==3.6.3
autopep8==1.5.4
certifi==2020.6.20
cffi==1.14.3
cryptography==3.2.1
decorator==4.4.2
pycodestyle==2.6.0
pycparser==2.20
python-telegram-bot==13.0
pytz==2020.1
PyYAML==5.3.1
six==1.15.0
toml==0.10.1
tornado==6.0.4
tzlocal==2.1
6 changes: 6 additions & 0 deletions Scripts/Bots/runPython_bot/start.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from bot import runPython_bot
import logging

logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
runPython_bot.bot()