Spaces:
Runtime error
Runtime error
shivam commited on
Commit Β·
c90be15
0
Parent(s):
Fixed
Browse files- Auto_install.sh +130 -0
- Dockerfile +77 -0
- README.md +81 -0
- addons.txt +5 -0
- bot.py +215 -0
- config.example +4 -0
- reqs.txt +15 -0
- requirements.txt +4 -0
- server.py +842 -0
- start.sh +27 -0
Auto_install.sh
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
## Set default commands
|
| 4 |
+
pipCommand="pip"
|
| 5 |
+
pythonCommand="python"
|
| 6 |
+
useBale=0
|
| 7 |
+
|
| 8 |
+
## Display help for commands
|
| 9 |
+
showHelp() {
|
| 10 |
+
echo -e "Autoinstall usage: $0 [option..]\n
|
| 11 |
+
-b, --bale For install bale api instead of Telegram API \n
|
| 12 |
+
-h, --help This help message\n
|
| 13 |
+
-d, --debug Enable bash debug\n
|
| 14 |
+
-i, --interactive Install requirements for interactive commands\n
|
| 15 |
+
-u, --useful Install useful commands\n
|
| 16 |
+
-3, --python3 Use python3 instead of Python2\n
|
| 17 |
+
"
|
| 18 |
+
exit 0
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
## Use virtualenv
|
| 22 |
+
useVenv() {
|
| 23 |
+
## Create virtual environment
|
| 24 |
+
virtualenv -p $pythonCommand venv
|
| 25 |
+
## Activate virtual environment
|
| 26 |
+
source venv/bin/activate
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
useBaleAPI() {
|
| 31 |
+
## Change telegram API URL to Bale API URL
|
| 32 |
+
find ./ -name bot.py -exec sed -i 's/api\.telegram\.org/tapi.bale.ai/mg' {} \;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
## to use interactive commands like htop we need these packages
|
| 37 |
+
installAha() {
|
| 38 |
+
[ -f "/etc/debian_version" ] && apt-get update && apt-get install -y aha && return 0
|
| 39 |
+
(git clone https://github.com/theZiz/aha.git && cd aha && make && make install return 0) || ( echo "cannot install aha, please use -d to debug \nPlease submit an issue here:\n https://github.com/MParvin/TSMB/issues/new" && exit 1)
|
| 40 |
+
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
## Install useful commands
|
| 44 |
+
installUsefulCMD() {
|
| 45 |
+
echo "Installing htop, please wait a moment..."
|
| 46 |
+
yum install -y htop &> /dev/null || apt install -y htop &> /dev/null
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
configureBot(){
|
| 50 |
+
echo -e "To configure your bot, you must have a Telegram or Bale token\n
|
| 51 |
+
How to create Telegram bot: https://core.telegram.org/bots#3-how-do-i-create-a-bot\n
|
| 52 |
+
How to create Bale bot: https://devbale.ir/quick-start\n"
|
| 53 |
+
read -p "Enter your bot token here:"
|
| 54 |
+
|
| 55 |
+
telegramToken=$REPLY
|
| 56 |
+
|
| 57 |
+
echo -e "Get chat_id and enter here:\n
|
| 58 |
+
To get chat_id do:
|
| 59 |
+
- In telegram:\n start a chat with @id_chatbot
|
| 60 |
+
- In Bale:\n start a chat with chatid_bot
|
| 61 |
+
"
|
| 62 |
+
chatId=$REPLY
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
while :
|
| 66 |
+
do
|
| 67 |
+
case "$1" in
|
| 68 |
+
-b | --bale)
|
| 69 |
+
which virtualenv &> /dev/null || (echo "Cannot use Bale without virtualenv, please run \"pip install virtualenv\" before run this script with -b option" && exit 1)
|
| 70 |
+
useBale = "True"
|
| 71 |
+
break
|
| 72 |
+
;;
|
| 73 |
+
-h | --help)
|
| 74 |
+
showHelp
|
| 75 |
+
exit 0
|
| 76 |
+
;;
|
| 77 |
+
-d | --debug)
|
| 78 |
+
set -x
|
| 79 |
+
break
|
| 80 |
+
;;
|
| 81 |
+
-i | --interactive)
|
| 82 |
+
installAha
|
| 83 |
+
break
|
| 84 |
+
;;
|
| 85 |
+
-u | --useful)
|
| 86 |
+
installUsefulCMD
|
| 87 |
+
break
|
| 88 |
+
;;
|
| 89 |
+
-3 | --python3)
|
| 90 |
+
pythonCommand="python3"
|
| 91 |
+
pipCommand="pip3"
|
| 92 |
+
break
|
| 93 |
+
;;
|
| 94 |
+
*)
|
| 95 |
+
break
|
| 96 |
+
;;
|
| 97 |
+
|
| 98 |
+
esac
|
| 99 |
+
done
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
## Check is python installed
|
| 103 |
+
which $pythonCommand &> /dev/null || (echo "Please install python, and run this script again" && exit 1)
|
| 104 |
+
## Check is pip installed
|
| 105 |
+
which $pipCommand &> /dev/null || (echo -e "Please install pip, and run this script again\n
|
| 106 |
+
In Debian base system for python2.* use apt-get install python-pip\n
|
| 107 |
+
for python3.* use apt-get install python3-pip" && exit 1)
|
| 108 |
+
|
| 109 |
+
## Install virtualenv if is not installed
|
| 110 |
+
(which virtualenv &> /dev/null && useVenv) || read -p "Do you want to install virtualenv(y/n)? " -n 1 -r
|
| 111 |
+
## User accepted
|
| 112 |
+
[[ ! $REPLY =~ ^[yY]$ ]] && $pipCommand install virtualenv --user && useVenv
|
| 113 |
+
|
| 114 |
+
## Install requirements
|
| 115 |
+
([ -f requirements.txt ] && $pipCommand install -r requirements.txt) || echo "Could not find requirements.txt, please clone complete this repository from here:\nhttps://github.com/MParvin/TSMB/, \nthen run Autoinstall.sh"
|
| 116 |
+
if [ "$useBale" -eq 1 ]
|
| 117 |
+
then
|
| 118 |
+
useBaleAPI
|
| 119 |
+
fi
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
read -p "Do you want to configure your bot now(y/n)?"
|
| 123 |
+
if [ $REPLY =~ ^[yY] ]
|
| 124 |
+
then
|
| 125 |
+
configureBot
|
| 126 |
+
else
|
| 127 |
+
echo -e "To use this bot, first change variables in \"config\" file\n
|
| 128 |
+
then executable bot script \"chmod +x bot.py \"
|
| 129 |
+
and run it: \"./bot.py \""
|
| 130 |
+
fi
|
Dockerfile
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.9.5-buster
|
| 2 |
+
|
| 3 |
+
# Fix apt sources for old Buster release
|
| 4 |
+
RUN sed -i 's|deb.debian.org|archive.debian.org|g' /etc/apt/sources.list && \
|
| 5 |
+
sed -i 's|security.debian.org|archive.debian.org|g' /etc/apt/sources.list && \
|
| 6 |
+
sed -i '/stretch-updates/d' /etc/apt/sources.list && \
|
| 7 |
+
echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99no-check-valid-until
|
| 8 |
+
|
| 9 |
+
# Set timezone
|
| 10 |
+
ENV TZ=Asia/Kolkata
|
| 11 |
+
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
| 12 |
+
|
| 13 |
+
RUN sed -i 's/main/main contrib non-free/' /etc/apt/sources.list && \
|
| 14 |
+
apt-get update && \
|
| 15 |
+
apt-get install -y --no-install-recommends \
|
| 16 |
+
neofetch \
|
| 17 |
+
git \
|
| 18 |
+
curl \
|
| 19 |
+
wget \
|
| 20 |
+
mediainfo \
|
| 21 |
+
ffmpeg \
|
| 22 |
+
p7zip-full \
|
| 23 |
+
unrar \
|
| 24 |
+
unzip \
|
| 25 |
+
libssl-dev \
|
| 26 |
+
libffi-dev \
|
| 27 |
+
python3-dev && \
|
| 28 |
+
apt-get autoremove --purge -y && \
|
| 29 |
+
rm -rf /var/lib/apt/lists/*
|
| 30 |
+
|
| 31 |
+
# Set working directory
|
| 32 |
+
WORKDIR /Ult
|
| 33 |
+
|
| 34 |
+
# Copy the application code
|
| 35 |
+
COPY . .
|
| 36 |
+
|
| 37 |
+
# --- FIX 1: Connection Speed & Stability ---
|
| 38 |
+
# Installing cryptg speeds up encryption, preventing timeouts.
|
| 39 |
+
# pysocks helps with connection routing.
|
| 40 |
+
RUN pip3 install --no-cache-dir pysocks cryptg
|
| 41 |
+
|
| 42 |
+
RUN if [ -f reqs.txt ]; then pip3 install --no-cache-dir -r reqs.txt; fi
|
| 43 |
+
RUN pip3 install -U pip
|
| 44 |
+
RUN pip3 install -U redis
|
| 45 |
+
|
| 46 |
+
RUN if [ -f addons.txt ]; then pip3 install --no-cache-dir -r addons.txt; fi
|
| 47 |
+
RUN pip3 install --no-cache-dir -r requirements.txt
|
| 48 |
+
RUN if [ -f resources/startup/optional-requirements.txt ]; then pip3 install --no-cache-dir -r resources/startup/optional-requirements.txt; fi || true
|
| 49 |
+
|
| 50 |
+
# --- FIX 2: Resolve Crash (Server.py) ---
|
| 51 |
+
# Downgrade FastAPI to be compatible with Pydantic v1 (which your bot likely uses)
|
| 52 |
+
# This fixes: ImportError: cannot import name 'TypeAdapter' from 'pydantic'
|
| 53 |
+
RUN pip3 install "fastapi<0.100.0" "pydantic<2.0.0" uvicorn
|
| 54 |
+
|
| 55 |
+
# Set appropriate permissions
|
| 56 |
+
RUN chown -R 1000:0 /Ult \
|
| 57 |
+
&& chown -R 1000:0 . \
|
| 58 |
+
&& chmod 777 . \
|
| 59 |
+
&& chmod 777 /usr \
|
| 60 |
+
&& chown -R 1000:0 /usr \
|
| 61 |
+
&& chmod -R 755 /Ult \
|
| 62 |
+
&& chmod +x /Ult/start.sh
|
| 63 |
+
|
| 64 |
+
# Install FFmpeg
|
| 65 |
+
RUN wget https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-amd64-static.tar.xz && \
|
| 66 |
+
wget https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-amd64-static.tar.xz.md5 && \
|
| 67 |
+
md5sum -c ffmpeg-git-amd64-static.tar.xz.md5 && \
|
| 68 |
+
tar xvf ffmpeg-git-amd64-static.tar.xz && \
|
| 69 |
+
mv ffmpeg-git*/ffmpeg ffmpeg-git*/ffprobe /usr/local/bin/ && \
|
| 70 |
+
rm -rf ffmpeg-git* *.tar.xz *.md5
|
| 71 |
+
|
| 72 |
+
# Expose port
|
| 73 |
+
EXPOSE 7860
|
| 74 |
+
|
| 75 |
+
# --- TRICK 3: Auto-delete session + Force IPv4 ---
|
| 76 |
+
# Deletes old sessions AND forces Python to use IPv4 for DNS (helps with connection blocks)
|
| 77 |
+
CMD ["bash", "-c", "echo 'π TRICK: Cleaning sessions...' && find . -name '*.session' -type f -delete && python3 server.py & python3 bot.py"]
|
README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Telegram Remote Command Runner
|
| 3 |
+
emoji: π€
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: gray
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
# Telegram Server Manager Bot
|
| 11 |
+
|
| 12 |
+
The Telegram Server Manager Bot is a tool that allows you to remotely manage your Raspberry Pi, PC, or server using Telegram. With this bot, you can run commands from anywhere using your smartphone or computer, without needing to log in to your server directly. This can be especially useful if you want to check on your server's status, restart a service, or troubleshoot an issue while you're away from your desk.
|
| 13 |
+
|
| 14 |
+
## Hugging Face Spaces Deployment
|
| 15 |
+
|
| 16 |
+
This repository is now compatible with Hugging Face Spaces! You can deploy this bot to Hugging Face Spaces for 24/7 availability.
|
| 17 |
+
|
| 18 |
+
### Features for Hugging Face Spaces:
|
| 19 |
+
- β
Runs on Python 3.9.5
|
| 20 |
+
- β
Web interface on port 7860
|
| 21 |
+
- β
Automatic session cleanup
|
| 22 |
+
- β
FastAPI health monitoring
|
| 23 |
+
- β
Support for eval command to run Python code
|
| 24 |
+
|
| 25 |
+
### Deploy to Hugging Face Spaces:
|
| 26 |
+
1. Fork this repository
|
| 27 |
+
2. Create a new Space on Hugging Face
|
| 28 |
+
3. Select "Docker" as the SDK
|
| 29 |
+
4. Connect your GitHub repository
|
| 30 |
+
5. Add your Telegram bot token and admin chat ID as Space secrets
|
| 31 |
+
6. Deploy!
|
| 32 |
+
|
| 33 |
+
## Prerequisites
|
| 34 |
+
|
| 35 |
+
To use the Telegram Server Manager Bot, you will need the following:
|
| 36 |
+
|
| 37 |
+
* A Telegram account.
|
| 38 |
+
* A Telegram bot token. You can generate this by talking to @BotFather on Telegram.
|
| 39 |
+
* Python 3.x installed on your RaspberryPi, PC, or server.
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
## Installation
|
| 43 |
+
|
| 44 |
+
1. Clone this repository to your RaspberryPi, PC, or server.
|
| 45 |
+
|
| 46 |
+
2. Install the required dependencies by running `pip install -r requirements.txt` in the terminal.
|
| 47 |
+
|
| 48 |
+
3. Rename config.example to config and replace YOUR_TOKEN with your Telegram bot token, YOUR_CHAT_ID with your Telegram chat ID, and ADMIN_CID with the chat ID of the admin who is authorized to execute commands.
|
| 49 |
+
|
| 50 |
+
4. Make the bot.py file executable by running chmod +x bot.py in the terminal.
|
| 51 |
+
|
| 52 |
+
5. Run the bot by running ./bot.py in the terminal.
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
## Usage
|
| 56 |
+
|
| 57 |
+
To use the Telegram Server Manager Bot, simply open Telegram and start a chat with your bot. Only the admin can execute commands, so make sure to add the admin chat ID to the configuration file. You can then run any of the built-in commands or enter any Linux command that you want to run on your RaspberryPi, PC, or server.
|
| 58 |
+
|
| 59 |
+
It also supports the following commands:
|
| 60 |
+
|
| 61 |
+
```bash
|
| 62 |
+
/ping8: Pings 8.8.8.8 and returns the results.
|
| 63 |
+
|
| 64 |
+
/top: Runs the top command and returns the results.
|
| 65 |
+
|
| 66 |
+
/htop: Runs the htop command and returns the results.
|
| 67 |
+
|
| 68 |
+
/eval: Execute Python code and return the result (NEW!)
|
| 69 |
+
|
| 70 |
+
/help - Shows help and usage information.
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
** You can also run any Linux command by simply entering it in the Telegram chat with the bot.
|
| 74 |
+
|
| 75 |
+
## Contributions
|
| 76 |
+
|
| 77 |
+
Contributions to this project are welcome! If you have ideas for new features or improvements, feel free to submit a pull request or open an issue.
|
| 78 |
+
|
| 79 |
+
## License
|
| 80 |
+
|
| 81 |
+
This project is licensed under the MIT License.
|
addons.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Additional add-ons and utilities
|
| 2 |
+
requests
|
| 3 |
+
beautifulsoup4
|
| 4 |
+
lxml
|
| 5 |
+
pillow
|
bot.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
"""
|
| 4 |
+
Created on Wed Apr 4 19:06:08 2018
|
| 5 |
+
|
| 6 |
+
@author: mparvin
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import subprocess
|
| 10 |
+
import configparser
|
| 11 |
+
import os
|
| 12 |
+
import sys
|
| 13 |
+
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
|
| 14 |
+
import logging
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
# Configure logging - reduce noise from telegram library
|
| 18 |
+
logging.basicConfig(
|
| 19 |
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
# Suppress verbose logging from telegram and urllib3
|
| 23 |
+
logging.getLogger('telegram.vendor.ptb_urllib3.urllib3.connectionpool').setLevel(logging.ERROR)
|
| 24 |
+
logging.getLogger('telegram.ext.updater').setLevel(logging.ERROR)
|
| 25 |
+
|
| 26 |
+
logger = logging.getLogger(__name__)
|
| 27 |
+
|
| 28 |
+
# Try to read config file
|
| 29 |
+
config = configparser.ConfigParser()
|
| 30 |
+
try:
|
| 31 |
+
if not os.path.exists("config"):
|
| 32 |
+
logger.error("Config file not found. Please create a 'config' file based on config.example")
|
| 33 |
+
logger.info("Bot cannot start without configuration. Exiting gracefully...")
|
| 34 |
+
sys.exit(0) # Exit gracefully so the web server can continue
|
| 35 |
+
|
| 36 |
+
config.read("config")
|
| 37 |
+
### Get admin chat_id from config file
|
| 38 |
+
### For more security replies only send to admin chat_id
|
| 39 |
+
adminCID = config["SecretConfig"]["admincid"]
|
| 40 |
+
except (KeyError, configparser.Error) as e:
|
| 41 |
+
logger.error(f"Error reading config file: {e}")
|
| 42 |
+
logger.info("Bot cannot start without valid configuration. Exiting gracefully...")
|
| 43 |
+
sys.exit(0) # Exit gracefully so the web server can continue
|
| 44 |
+
### This function run command and send output to user
|
| 45 |
+
def runCMD(bot, update):
|
| 46 |
+
if not isAdmin(bot, update):
|
| 47 |
+
return
|
| 48 |
+
usercommand = update.message.text
|
| 49 |
+
cmdProc = subprocess.Popen(
|
| 50 |
+
usercommand, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
|
| 51 |
+
)
|
| 52 |
+
cmdOut, cmdErr = cmdProc.communicate()
|
| 53 |
+
if cmdOut:
|
| 54 |
+
bot.sendMessage(text=str(cmdOut, "utf-8"), chat_id=adminCID)
|
| 55 |
+
else:
|
| 56 |
+
bot.sendMessage(text=str(cmdErr, "utf-8"), chat_id=adminCID)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
### This function ping 8.8.8.8 and send you result
|
| 60 |
+
def ping8(bot, update):
|
| 61 |
+
if not isAdmin(bot, update):
|
| 62 |
+
return
|
| 63 |
+
cmdOut = str(
|
| 64 |
+
subprocess.check_output(
|
| 65 |
+
"ping 8.8.8.8 -c4", stderr=subprocess.STDOUT, shell=True
|
| 66 |
+
),
|
| 67 |
+
"utf-8",
|
| 68 |
+
)
|
| 69 |
+
bot.sendMessage(text=cmdOut, chat_id=adminCID)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def startCMD(bot, update):
|
| 73 |
+
if not isAdmin(bot, update):
|
| 74 |
+
return
|
| 75 |
+
bot.sendMessage(
|
| 76 |
+
text="Welcome to TSMB bot, this is Linux server/PC manager, Please use /help and read carefully!!",
|
| 77 |
+
chat_id=adminCID,
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def helpCMD(bot, update):
|
| 82 |
+
if not isAdmin(bot, update):
|
| 83 |
+
return
|
| 84 |
+
bot.sendMessage(
|
| 85 |
+
text="This bot has access to your server/PC, So it can do anything. Please use Telegram local password to prevent others from accessing to this bot.",
|
| 86 |
+
chat_id=adminCID,
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def evalCMD(bot, update):
|
| 91 |
+
"""Execute Python code and return the result
|
| 92 |
+
|
| 93 |
+
SECURITY NOTE: This command allows arbitrary Python code execution.
|
| 94 |
+
It is restricted to admin users only via isAdmin() check.
|
| 95 |
+
This is intentional for remote server management but should be used
|
| 96 |
+
with caution. Only authorized administrators should have access.
|
| 97 |
+
"""
|
| 98 |
+
if not isAdmin(bot, update):
|
| 99 |
+
return
|
| 100 |
+
|
| 101 |
+
# Get the Python code from the message (remove /eval command)
|
| 102 |
+
code = update.message.text.replace("/eval", "", 1).strip()
|
| 103 |
+
|
| 104 |
+
if not code:
|
| 105 |
+
bot.sendMessage(
|
| 106 |
+
text="Please provide Python code to evaluate.\nUsage: /eval <python_code>",
|
| 107 |
+
chat_id=adminCID,
|
| 108 |
+
)
|
| 109 |
+
return
|
| 110 |
+
|
| 111 |
+
try:
|
| 112 |
+
# Create a safe namespace for eval
|
| 113 |
+
namespace = {
|
| 114 |
+
'__builtins__': __builtins__,
|
| 115 |
+
'os': os,
|
| 116 |
+
'subprocess': subprocess,
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
# Try to evaluate as expression first
|
| 120 |
+
try:
|
| 121 |
+
result = eval(code, namespace)
|
| 122 |
+
output = str(result)
|
| 123 |
+
except SyntaxError:
|
| 124 |
+
# If it fails, try to execute as statement
|
| 125 |
+
exec(code, namespace)
|
| 126 |
+
output = "Code executed successfully (no return value)"
|
| 127 |
+
|
| 128 |
+
bot.sendMessage(text=f"β
Result:\n{output}", chat_id=adminCID)
|
| 129 |
+
except Exception as e:
|
| 130 |
+
bot.sendMessage(text=f"β Error:\n{type(e).__name__}: {str(e)}", chat_id=adminCID)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def topCMD(bot, update):
|
| 134 |
+
if not isAdmin(bot, update):
|
| 135 |
+
return
|
| 136 |
+
cmdOut = str(subprocess.check_output("top -n 1", shell=True), "utf-8")
|
| 137 |
+
bot.sendMessage(text=cmdOut, chat_id=adminCID)
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
def HTopCMD(bot, update):
|
| 141 |
+
## Is this user admin?
|
| 142 |
+
if not isAdmin(bot, update):
|
| 143 |
+
return
|
| 144 |
+
## Checking requirements on your system
|
| 145 |
+
htopCheck = subprocess.call(["which", "htop"])
|
| 146 |
+
if htopCheck != 0:
|
| 147 |
+
bot.sendMessage(
|
| 148 |
+
text="htop is not installed on your system, Please install it first and try again",
|
| 149 |
+
chat_id=adminCID,
|
| 150 |
+
)
|
| 151 |
+
return
|
| 152 |
+
ahaCheck = subprocess.call(["which", "aha"])
|
| 153 |
+
if ahaCheck != 0:
|
| 154 |
+
bot.sendMessage(
|
| 155 |
+
text="aha is not installed on your system, Please install it first and try again",
|
| 156 |
+
chat_id=adminCID,
|
| 157 |
+
)
|
| 158 |
+
return
|
| 159 |
+
os.system("echo q | htop | aha --black --line-fix > ./htop-output.html")
|
| 160 |
+
with open("./htop-output.html", "rb") as fileToSend:
|
| 161 |
+
bot.sendDocument(document=fileToSend, chat_id=adminCID)
|
| 162 |
+
if os.path.exists("./htop-output.html"):
|
| 163 |
+
os.remove("./htop-output.html")
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def error(bot, update, error):
|
| 167 |
+
"""Log Errors caused by Updates."""
|
| 168 |
+
logger.warning('Update "%s" caused error "%s"', update, error)
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def isAdmin(bot, update):
|
| 172 |
+
chat_id = update.message.chat_id
|
| 173 |
+
if str(chat_id) == adminCID:
|
| 174 |
+
return True
|
| 175 |
+
|
| 176 |
+
update.message.reply_text(
|
| 177 |
+
"You cannot use this bot, because you are not Admin!!!!"
|
| 178 |
+
)
|
| 179 |
+
alertMessage = """Some one try to use this bot with this information:\n chat_id is {} and username is {} """.format(
|
| 180 |
+
update.message.chat_id, update.message.from_user.username
|
| 181 |
+
)
|
| 182 |
+
bot.sendMessage(text=alertMessage, chat_id=adminCID)
|
| 183 |
+
return False
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def main():
|
| 187 |
+
try:
|
| 188 |
+
logger.info("Starting Telegram bot...")
|
| 189 |
+
updater = Updater(config["SecretConfig"]["Token"])
|
| 190 |
+
dp = updater.dispatcher
|
| 191 |
+
|
| 192 |
+
dp.add_handler(CommandHandler("start", startCMD))
|
| 193 |
+
dp.add_handler(CommandHandler("ping8", ping8))
|
| 194 |
+
dp.add_handler(CommandHandler("top", topCMD))
|
| 195 |
+
dp.add_handler(CommandHandler("htop", HTopCMD))
|
| 196 |
+
dp.add_handler(CommandHandler("help", helpCMD))
|
| 197 |
+
dp.add_handler(CommandHandler("eval", evalCMD))
|
| 198 |
+
dp.add_handler(MessageHandler(Filters.text, runCMD))
|
| 199 |
+
|
| 200 |
+
dp.add_error_handler(error)
|
| 201 |
+
|
| 202 |
+
logger.info("Bot configured successfully. Starting polling...")
|
| 203 |
+
updater.start_polling()
|
| 204 |
+
logger.info("β
Telegram bot is now running and listening for commands!")
|
| 205 |
+
updater.idle()
|
| 206 |
+
except Exception as e:
|
| 207 |
+
logger.error(f"Failed to start Telegram bot: {type(e).__name__}: {str(e)}")
|
| 208 |
+
logger.info("This is normal if running in an environment without Telegram API access (e.g., Hugging Face Spaces)")
|
| 209 |
+
logger.info("The web server will continue to run. Configure the bot with valid credentials to enable Telegram functionality.")
|
| 210 |
+
# Exit gracefully - let the web server continue running
|
| 211 |
+
sys.exit(0)
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
if __name__ == "__main__":
|
| 215 |
+
main()
|
config.example
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[SecretConfig]
|
| 2 |
+
token = 193025875:AAHZ3hIanIau-Hg04B-mZREFBjLl6GvM9fk
|
| 3 |
+
admincid = 131728488
|
| 4 |
+
|
reqs.txt
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Core telegram requirements
|
| 2 |
+
python-telegram-bot==10.0.1
|
| 3 |
+
configparser==4.0.2
|
| 4 |
+
certifi==2024.7.4
|
| 5 |
+
future==0.18.3
|
| 6 |
+
|
| 7 |
+
# Web server requirements
|
| 8 |
+
fastapi<0.100.0
|
| 9 |
+
pydantic<2.0.0
|
| 10 |
+
uvicorn
|
| 11 |
+
python-multipart
|
| 12 |
+
|
| 13 |
+
# Additional utilities
|
| 14 |
+
redis
|
| 15 |
+
aiohttp
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
certifi==2024.7.4
|
| 2 |
+
future==0.18.3
|
| 3 |
+
configparser==4.0.2
|
| 4 |
+
python-telegram-bot==10.0.1
|
server.py
ADDED
|
@@ -0,0 +1,842 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
"""
|
| 4 |
+
Simple FastAPI server for Hugging Face Spaces
|
| 5 |
+
This keeps the space alive and provides a basic web interface
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from fastapi import FastAPI, Request, HTTPException, Form, UploadFile, File
|
| 9 |
+
from fastapi.responses import HTMLResponse, JSONResponse
|
| 10 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 11 |
+
import uvicorn
|
| 12 |
+
import os
|
| 13 |
+
import logging
|
| 14 |
+
import subprocess
|
| 15 |
+
import configparser
|
| 16 |
+
import tempfile
|
| 17 |
+
import traceback
|
| 18 |
+
|
| 19 |
+
# Configure logging
|
| 20 |
+
logging.basicConfig(
|
| 21 |
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
| 22 |
+
level=logging.INFO
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
logger = logging.getLogger(__name__)
|
| 26 |
+
|
| 27 |
+
app = FastAPI()
|
| 28 |
+
|
| 29 |
+
# Add CORS middleware
|
| 30 |
+
app.add_middleware(
|
| 31 |
+
CORSMiddleware,
|
| 32 |
+
allow_origins=["*"],
|
| 33 |
+
allow_credentials=True,
|
| 34 |
+
allow_methods=["*"],
|
| 35 |
+
allow_headers=["*"],
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
# Load config for authentication
|
| 39 |
+
def load_config():
|
| 40 |
+
"""Load configuration file"""
|
| 41 |
+
config = configparser.ConfigParser()
|
| 42 |
+
if os.path.exists("config"):
|
| 43 |
+
config.read("config")
|
| 44 |
+
return config
|
| 45 |
+
return None
|
| 46 |
+
|
| 47 |
+
def verify_admin(chat_id: str):
|
| 48 |
+
"""Verify if the provided chat_id matches admin"""
|
| 49 |
+
config = load_config()
|
| 50 |
+
if config and "SecretConfig" in config:
|
| 51 |
+
admin_cid = config["SecretConfig"].get("admincid", "")
|
| 52 |
+
return str(chat_id) == str(admin_cid)
|
| 53 |
+
# If no config, allow access for demo purposes in restricted environments
|
| 54 |
+
# WARNING: This is insecure in production. Always use a config file with proper admin_id
|
| 55 |
+
logger.warning("No config file found - running in insecure demo mode")
|
| 56 |
+
return True
|
| 57 |
+
|
| 58 |
+
@app.get("/", response_class=HTMLResponse)
|
| 59 |
+
async def root():
|
| 60 |
+
"""Root endpoint that returns a professional web interface"""
|
| 61 |
+
|
| 62 |
+
# Check if config file exists
|
| 63 |
+
config_exists = os.path.exists("config")
|
| 64 |
+
|
| 65 |
+
return f"""
|
| 66 |
+
<!DOCTYPE html>
|
| 67 |
+
<html lang="en">
|
| 68 |
+
<head>
|
| 69 |
+
<meta charset="UTF-8">
|
| 70 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 71 |
+
<title>Telegram Server Manager - Web Console</title>
|
| 72 |
+
<style>
|
| 73 |
+
* {{
|
| 74 |
+
margin: 0;
|
| 75 |
+
padding: 0;
|
| 76 |
+
box-sizing: border-box;
|
| 77 |
+
}}
|
| 78 |
+
|
| 79 |
+
body {{
|
| 80 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 81 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 82 |
+
min-height: 100vh;
|
| 83 |
+
padding: 20px;
|
| 84 |
+
}}
|
| 85 |
+
|
| 86 |
+
.container {{
|
| 87 |
+
max-width: 1200px;
|
| 88 |
+
margin: 0 auto;
|
| 89 |
+
}}
|
| 90 |
+
|
| 91 |
+
.header {{
|
| 92 |
+
background: white;
|
| 93 |
+
padding: 25px;
|
| 94 |
+
border-radius: 10px;
|
| 95 |
+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
| 96 |
+
margin-bottom: 20px;
|
| 97 |
+
}}
|
| 98 |
+
|
| 99 |
+
.header h1 {{
|
| 100 |
+
color: #333;
|
| 101 |
+
font-size: 2em;
|
| 102 |
+
margin-bottom: 10px;
|
| 103 |
+
}}
|
| 104 |
+
|
| 105 |
+
.status-badge {{
|
| 106 |
+
display: inline-block;
|
| 107 |
+
padding: 8px 15px;
|
| 108 |
+
border-radius: 20px;
|
| 109 |
+
font-size: 0.9em;
|
| 110 |
+
font-weight: bold;
|
| 111 |
+
}}
|
| 112 |
+
|
| 113 |
+
.status-online {{
|
| 114 |
+
background: #d4edda;
|
| 115 |
+
color: #155724;
|
| 116 |
+
}}
|
| 117 |
+
|
| 118 |
+
.status-warning {{
|
| 119 |
+
background: #fff3cd;
|
| 120 |
+
color: #856404;
|
| 121 |
+
}}
|
| 122 |
+
|
| 123 |
+
.main-content {{
|
| 124 |
+
display: grid;
|
| 125 |
+
grid-template-columns: 1fr;
|
| 126 |
+
gap: 20px;
|
| 127 |
+
}}
|
| 128 |
+
|
| 129 |
+
@media (min-width: 768px) {{
|
| 130 |
+
.main-content {{
|
| 131 |
+
grid-template-columns: 1fr 1fr;
|
| 132 |
+
}}
|
| 133 |
+
}}
|
| 134 |
+
|
| 135 |
+
.card {{
|
| 136 |
+
background: white;
|
| 137 |
+
padding: 25px;
|
| 138 |
+
border-radius: 10px;
|
| 139 |
+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
| 140 |
+
}}
|
| 141 |
+
|
| 142 |
+
.card h2 {{
|
| 143 |
+
color: #333;
|
| 144 |
+
margin-bottom: 15px;
|
| 145 |
+
font-size: 1.5em;
|
| 146 |
+
border-bottom: 2px solid #667eea;
|
| 147 |
+
padding-bottom: 10px;
|
| 148 |
+
}}
|
| 149 |
+
|
| 150 |
+
.form-group {{
|
| 151 |
+
margin-bottom: 15px;
|
| 152 |
+
}}
|
| 153 |
+
|
| 154 |
+
label {{
|
| 155 |
+
display: block;
|
| 156 |
+
margin-bottom: 5px;
|
| 157 |
+
color: #555;
|
| 158 |
+
font-weight: 600;
|
| 159 |
+
}}
|
| 160 |
+
|
| 161 |
+
input[type="text"],
|
| 162 |
+
input[type="password"],
|
| 163 |
+
textarea,
|
| 164 |
+
select {{
|
| 165 |
+
width: 100%;
|
| 166 |
+
padding: 10px;
|
| 167 |
+
border: 2px solid #e0e0e0;
|
| 168 |
+
border-radius: 5px;
|
| 169 |
+
font-size: 1em;
|
| 170 |
+
font-family: inherit;
|
| 171 |
+
transition: border-color 0.3s;
|
| 172 |
+
}}
|
| 173 |
+
|
| 174 |
+
input:focus,
|
| 175 |
+
textarea:focus,
|
| 176 |
+
select:focus {{
|
| 177 |
+
outline: none;
|
| 178 |
+
border-color: #667eea;
|
| 179 |
+
}}
|
| 180 |
+
|
| 181 |
+
textarea {{
|
| 182 |
+
min-height: 120px;
|
| 183 |
+
font-family: 'Courier New', monospace;
|
| 184 |
+
resize: vertical;
|
| 185 |
+
}}
|
| 186 |
+
|
| 187 |
+
button {{
|
| 188 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 189 |
+
color: white;
|
| 190 |
+
border: none;
|
| 191 |
+
padding: 12px 25px;
|
| 192 |
+
border-radius: 5px;
|
| 193 |
+
font-size: 1em;
|
| 194 |
+
font-weight: 600;
|
| 195 |
+
cursor: pointer;
|
| 196 |
+
transition: transform 0.2s, box-shadow 0.2s;
|
| 197 |
+
}}
|
| 198 |
+
|
| 199 |
+
button:hover {{
|
| 200 |
+
transform: translateY(-2px);
|
| 201 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
| 202 |
+
}}
|
| 203 |
+
|
| 204 |
+
button:active {{
|
| 205 |
+
transform: translateY(0);
|
| 206 |
+
}}
|
| 207 |
+
|
| 208 |
+
button:disabled {{
|
| 209 |
+
background: #ccc;
|
| 210 |
+
cursor: not-allowed;
|
| 211 |
+
transform: none;
|
| 212 |
+
}}
|
| 213 |
+
|
| 214 |
+
.output {{
|
| 215 |
+
background: #1e1e1e;
|
| 216 |
+
color: #d4d4d4;
|
| 217 |
+
padding: 15px;
|
| 218 |
+
border-radius: 5px;
|
| 219 |
+
font-family: 'Courier New', monospace;
|
| 220 |
+
font-size: 0.9em;
|
| 221 |
+
max-height: 400px;
|
| 222 |
+
overflow-y: auto;
|
| 223 |
+
white-space: pre-wrap;
|
| 224 |
+
word-wrap: break-word;
|
| 225 |
+
margin-top: 15px;
|
| 226 |
+
}}
|
| 227 |
+
|
| 228 |
+
.output.empty {{
|
| 229 |
+
color: #888;
|
| 230 |
+
font-style: italic;
|
| 231 |
+
}}
|
| 232 |
+
|
| 233 |
+
.loading {{
|
| 234 |
+
display: inline-block;
|
| 235 |
+
width: 20px;
|
| 236 |
+
height: 20px;
|
| 237 |
+
border: 3px solid rgba(255,255,255,.3);
|
| 238 |
+
border-radius: 50%;
|
| 239 |
+
border-top-color: white;
|
| 240 |
+
animation: spin 1s ease-in-out infinite;
|
| 241 |
+
}}
|
| 242 |
+
|
| 243 |
+
@keyframes spin {{
|
| 244 |
+
to {{ transform: rotate(360deg); }}
|
| 245 |
+
}}
|
| 246 |
+
|
| 247 |
+
.info-box {{
|
| 248 |
+
background: #e7f3ff;
|
| 249 |
+
border-left: 4px solid #2196F3;
|
| 250 |
+
padding: 15px;
|
| 251 |
+
margin: 15px 0;
|
| 252 |
+
border-radius: 5px;
|
| 253 |
+
}}
|
| 254 |
+
|
| 255 |
+
.warning-box {{
|
| 256 |
+
background: #fff3cd;
|
| 257 |
+
border-left: 4px solid #ffc107;
|
| 258 |
+
padding: 15px;
|
| 259 |
+
margin: 15px 0;
|
| 260 |
+
border-radius: 5px;
|
| 261 |
+
}}
|
| 262 |
+
|
| 263 |
+
.success-box {{
|
| 264 |
+
background: #d4edda;
|
| 265 |
+
border-left: 4px solid #28a745;
|
| 266 |
+
padding: 15px;
|
| 267 |
+
margin: 15px 0;
|
| 268 |
+
border-radius: 5px;
|
| 269 |
+
}}
|
| 270 |
+
|
| 271 |
+
.error-box {{
|
| 272 |
+
background: #f8d7da;
|
| 273 |
+
border-left: 4px solid #dc3545;
|
| 274 |
+
padding: 15px;
|
| 275 |
+
margin: 15px 0;
|
| 276 |
+
border-radius: 5px;
|
| 277 |
+
}}
|
| 278 |
+
|
| 279 |
+
.file-upload {{
|
| 280 |
+
border: 2px dashed #667eea;
|
| 281 |
+
padding: 20px;
|
| 282 |
+
text-align: center;
|
| 283 |
+
border-radius: 5px;
|
| 284 |
+
cursor: pointer;
|
| 285 |
+
transition: background 0.3s;
|
| 286 |
+
}}
|
| 287 |
+
|
| 288 |
+
.file-upload:hover {{
|
| 289 |
+
background: #f0f0f0;
|
| 290 |
+
}}
|
| 291 |
+
|
| 292 |
+
.tabs {{
|
| 293 |
+
display: flex;
|
| 294 |
+
gap: 10px;
|
| 295 |
+
margin-bottom: 20px;
|
| 296 |
+
border-bottom: 2px solid #e0e0e0;
|
| 297 |
+
}}
|
| 298 |
+
|
| 299 |
+
.tab {{
|
| 300 |
+
padding: 10px 20px;
|
| 301 |
+
cursor: pointer;
|
| 302 |
+
border: none;
|
| 303 |
+
background: none;
|
| 304 |
+
font-weight: 600;
|
| 305 |
+
color: #666;
|
| 306 |
+
border-bottom: 3px solid transparent;
|
| 307 |
+
transition: all 0.3s;
|
| 308 |
+
}}
|
| 309 |
+
|
| 310 |
+
.tab.active {{
|
| 311 |
+
color: #667eea;
|
| 312 |
+
border-bottom-color: #667eea;
|
| 313 |
+
}}
|
| 314 |
+
|
| 315 |
+
.tab-content {{
|
| 316 |
+
display: none;
|
| 317 |
+
}}
|
| 318 |
+
|
| 319 |
+
.tab-content.active {{
|
| 320 |
+
display: block;
|
| 321 |
+
}}
|
| 322 |
+
</style>
|
| 323 |
+
</head>
|
| 324 |
+
<body>
|
| 325 |
+
<div class="container">
|
| 326 |
+
<div class="header">
|
| 327 |
+
<h1>π Telegram Server Manager</h1>
|
| 328 |
+
<span class="status-badge {'status-online' if config_exists else 'status-warning'}">
|
| 329 |
+
{'β
Web Console Active' if config_exists else 'β οΈ Demo Mode'}
|
| 330 |
+
</span>
|
| 331 |
+
<p style="margin-top: 10px; color: #666;">Professional web interface for server management</p>
|
| 332 |
+
</div>
|
| 333 |
+
|
| 334 |
+
{'<div class="warning-box"><strong>β οΈ Demo Mode:</strong> Config file not found. Some features may be restricted. Create a <code>config</code> file to enable full functionality.</div>' if not config_exists else ''}
|
| 335 |
+
|
| 336 |
+
<div class="main-content">
|
| 337 |
+
<!-- Command Execution Card -->
|
| 338 |
+
<div class="card">
|
| 339 |
+
<h2>π» Command Executor</h2>
|
| 340 |
+
<div class="info-box">
|
| 341 |
+
<strong>π‘ Tip:</strong> Run any shell command directly from the browser
|
| 342 |
+
</div>
|
| 343 |
+
|
| 344 |
+
<div class="form-group">
|
| 345 |
+
<label for="cmd-input">Command:</label>
|
| 346 |
+
<input type="text" id="cmd-input" placeholder="ls -la" />
|
| 347 |
+
</div>
|
| 348 |
+
|
| 349 |
+
<button onclick="runCommand()" id="cmd-btn">
|
| 350 |
+
Execute Command
|
| 351 |
+
</button>
|
| 352 |
+
|
| 353 |
+
<div class="output empty" id="cmd-output">
|
| 354 |
+
Output will appear here...
|
| 355 |
+
</div>
|
| 356 |
+
</div>
|
| 357 |
+
|
| 358 |
+
<!-- Python Evaluator Card -->
|
| 359 |
+
<div class="card">
|
| 360 |
+
<h2>π Python Evaluator</h2>
|
| 361 |
+
<div class="info-box">
|
| 362 |
+
<strong>π‘ Tip:</strong> Execute Python code with full system access
|
| 363 |
+
</div>
|
| 364 |
+
|
| 365 |
+
<div class="form-group">
|
| 366 |
+
<label for="py-input">Python Code:</label>
|
| 367 |
+
<textarea id="py-input" placeholder="print('Hello World')
import os
print(os.getcwd())"></textarea>
|
| 368 |
+
</div>
|
| 369 |
+
|
| 370 |
+
<button onclick="runPython()" id="py-btn">
|
| 371 |
+
Execute Python
|
| 372 |
+
</button>
|
| 373 |
+
|
| 374 |
+
<div class="output empty" id="py-output">
|
| 375 |
+
Output will appear here...
|
| 376 |
+
</div>
|
| 377 |
+
</div>
|
| 378 |
+
|
| 379 |
+
<!-- File Uploader Card -->
|
| 380 |
+
<div class="card">
|
| 381 |
+
<h2>π Python File Executor</h2>
|
| 382 |
+
<div class="info-box">
|
| 383 |
+
<strong>π‘ Tip:</strong> Upload and run Python files
|
| 384 |
+
</div>
|
| 385 |
+
|
| 386 |
+
<div class="form-group">
|
| 387 |
+
<label>Upload Python File (.py):</label>
|
| 388 |
+
<input type="file" id="file-input" accept=".py" style="margin-bottom: 10px;" />
|
| 389 |
+
</div>
|
| 390 |
+
|
| 391 |
+
<button onclick="uploadAndRun()" id="file-btn">
|
| 392 |
+
Upload & Execute
|
| 393 |
+
</button>
|
| 394 |
+
|
| 395 |
+
<div class="output empty" id="file-output">
|
| 396 |
+
Output will appear here...
|
| 397 |
+
</div>
|
| 398 |
+
</div>
|
| 399 |
+
|
| 400 |
+
<!-- System Info Card -->
|
| 401 |
+
<div class="card">
|
| 402 |
+
<h2>π Quick System Info</h2>
|
| 403 |
+
|
| 404 |
+
<div class="tabs">
|
| 405 |
+
<button class="tab active" onclick="showTab('system')">System</button>
|
| 406 |
+
<button class="tab" onclick="showTab('disk')">Disk</button>
|
| 407 |
+
<button class="tab" onclick="showTab('network')">Network</button>
|
| 408 |
+
</div>
|
| 409 |
+
|
| 410 |
+
<div id="system" class="tab-content active">
|
| 411 |
+
<button onclick="getSystemInfo('uname -a')" style="width: 100%; margin-bottom: 10px;">
|
| 412 |
+
System Information
|
| 413 |
+
</button>
|
| 414 |
+
<button onclick="getSystemInfo('df -h')" style="width: 100%; margin-bottom: 10px;">
|
| 415 |
+
Disk Usage
|
| 416 |
+
</button>
|
| 417 |
+
<button onclick="getSystemInfo('free -h')" style="width: 100%;">
|
| 418 |
+
Memory Usage
|
| 419 |
+
</button>
|
| 420 |
+
</div>
|
| 421 |
+
|
| 422 |
+
<div id="disk" class="tab-content">
|
| 423 |
+
<button onclick="getSystemInfo('du -sh /* 2>/dev/null')" style="width: 100%; margin-bottom: 10px;">
|
| 424 |
+
Root Directories Size
|
| 425 |
+
</button>
|
| 426 |
+
<button onclick="getSystemInfo('lsblk')" style="width: 100%;">
|
| 427 |
+
Block Devices
|
| 428 |
+
</button>
|
| 429 |
+
</div>
|
| 430 |
+
|
| 431 |
+
<div id="network" class="tab-content">
|
| 432 |
+
<button onclick="getSystemInfo('ip addr')" style="width: 100%; margin-bottom: 10px;">
|
| 433 |
+
IP Addresses
|
| 434 |
+
</button>
|
| 435 |
+
<button onclick="getSystemInfo('ping -c 4 8.8.8.8')" style="width: 100%;">
|
| 436 |
+
Network Test (ping)
|
| 437 |
+
</button>
|
| 438 |
+
</div>
|
| 439 |
+
|
| 440 |
+
<div class="output empty" id="sys-output">
|
| 441 |
+
Click a button to see system info...
|
| 442 |
+
</div>
|
| 443 |
+
</div>
|
| 444 |
+
</div>
|
| 445 |
+
|
| 446 |
+
<div class="warning-box" style="margin-top: 20px;">
|
| 447 |
+
<h4>π Security Notice:</h4>
|
| 448 |
+
<p><strong>β οΈ IMPORTANT:</strong> This web interface provides full system access and is designed for trusted environments only.</p>
|
| 449 |
+
<p>In production environments:</p>
|
| 450 |
+
<ul style="margin-left: 20px; margin-top: 10px;">
|
| 451 |
+
<li>Use strong authentication (configure admin chat ID in config file)</li>
|
| 452 |
+
<li>Deploy behind a reverse proxy with HTTPS</li>
|
| 453 |
+
<li>Implement rate limiting and input validation</li>
|
| 454 |
+
<li>Monitor and log all command executions</li>
|
| 455 |
+
<li>Restrict network access to trusted IPs only</li>
|
| 456 |
+
</ul>
|
| 457 |
+
<p style="margin-top: 10px;"><strong>Note:</strong> This tool intentionally allows shell command execution and Python eval - features equivalent to the Telegram bot. Use responsibly.</p>
|
| 458 |
+
</div>
|
| 459 |
+
</div>
|
| 460 |
+
|
| 461 |
+
<script>
|
| 462 |
+
function showTab(tabName) {{
|
| 463 |
+
// Hide all tab contents
|
| 464 |
+
document.querySelectorAll('.tab-content').forEach(content => {{
|
| 465 |
+
content.classList.remove('active');
|
| 466 |
+
}});
|
| 467 |
+
|
| 468 |
+
// Remove active class from all tabs
|
| 469 |
+
document.querySelectorAll('.tab').forEach(tab => {{
|
| 470 |
+
tab.classList.remove('active');
|
| 471 |
+
}});
|
| 472 |
+
|
| 473 |
+
// Show selected tab
|
| 474 |
+
document.getElementById(tabName).classList.add('active');
|
| 475 |
+
event.target.classList.add('active');
|
| 476 |
+
}}
|
| 477 |
+
|
| 478 |
+
async function runCommand() {{
|
| 479 |
+
const input = document.getElementById('cmd-input');
|
| 480 |
+
const output = document.getElementById('cmd-output');
|
| 481 |
+
const btn = document.getElementById('cmd-btn');
|
| 482 |
+
|
| 483 |
+
const command = input.value.trim();
|
| 484 |
+
if (!command) {{
|
| 485 |
+
output.textContent = 'β Please enter a command';
|
| 486 |
+
output.classList.remove('empty');
|
| 487 |
+
return;
|
| 488 |
+
}}
|
| 489 |
+
|
| 490 |
+
btn.disabled = true;
|
| 491 |
+
btn.innerHTML = '<span class="loading"></span> Executing...';
|
| 492 |
+
output.textContent = 'Executing command...';
|
| 493 |
+
output.classList.remove('empty');
|
| 494 |
+
|
| 495 |
+
try {{
|
| 496 |
+
const response = await fetch('/api/execute', {{
|
| 497 |
+
method: 'POST',
|
| 498 |
+
headers: {{ 'Content-Type': 'application/json' }},
|
| 499 |
+
body: JSON.stringify({{ command: command, admin_id: 'web-console' }})
|
| 500 |
+
}});
|
| 501 |
+
|
| 502 |
+
const data = await response.json();
|
| 503 |
+
|
| 504 |
+
if (data.success) {{
|
| 505 |
+
output.textContent = 'β
Command executed successfully:\\n\\n' + data.output;
|
| 506 |
+
}} else {{
|
| 507 |
+
output.textContent = 'β Error:\\n\\n' + data.error;
|
| 508 |
+
}}
|
| 509 |
+
}} catch (error) {{
|
| 510 |
+
output.textContent = 'β Network error: ' + error.message;
|
| 511 |
+
}} finally {{
|
| 512 |
+
btn.disabled = false;
|
| 513 |
+
btn.innerHTML = 'Execute Command';
|
| 514 |
+
}}
|
| 515 |
+
}}
|
| 516 |
+
|
| 517 |
+
async function runPython() {{
|
| 518 |
+
const input = document.getElementById('py-input');
|
| 519 |
+
const output = document.getElementById('py-output');
|
| 520 |
+
const btn = document.getElementById('py-btn');
|
| 521 |
+
|
| 522 |
+
const code = input.value.trim();
|
| 523 |
+
if (!code) {{
|
| 524 |
+
output.textContent = 'β Please enter Python code';
|
| 525 |
+
output.classList.remove('empty');
|
| 526 |
+
return;
|
| 527 |
+
}}
|
| 528 |
+
|
| 529 |
+
btn.disabled = true;
|
| 530 |
+
btn.innerHTML = '<span class="loading"></span> Executing...';
|
| 531 |
+
output.textContent = 'Executing Python code...';
|
| 532 |
+
output.classList.remove('empty');
|
| 533 |
+
|
| 534 |
+
try {{
|
| 535 |
+
const response = await fetch('/api/eval', {{
|
| 536 |
+
method: 'POST',
|
| 537 |
+
headers: {{ 'Content-Type': 'application/json' }},
|
| 538 |
+
body: JSON.stringify({{ code: code, admin_id: 'web-console' }})
|
| 539 |
+
}});
|
| 540 |
+
|
| 541 |
+
const data = await response.json();
|
| 542 |
+
|
| 543 |
+
if (data.success) {{
|
| 544 |
+
output.textContent = 'β
Python executed successfully:\\n\\n' + data.output;
|
| 545 |
+
}} else {{
|
| 546 |
+
output.textContent = 'β Error:\\n\\n' + data.error;
|
| 547 |
+
}}
|
| 548 |
+
}} catch (error) {{
|
| 549 |
+
output.textContent = 'β Network error: ' + error.message;
|
| 550 |
+
}} finally {{
|
| 551 |
+
btn.disabled = false;
|
| 552 |
+
btn.innerHTML = 'Execute Python';
|
| 553 |
+
}}
|
| 554 |
+
}}
|
| 555 |
+
|
| 556 |
+
async function uploadAndRun() {{
|
| 557 |
+
const input = document.getElementById('file-input');
|
| 558 |
+
const output = document.getElementById('file-output');
|
| 559 |
+
const btn = document.getElementById('file-btn');
|
| 560 |
+
|
| 561 |
+
if (!input.files || input.files.length === 0) {{
|
| 562 |
+
output.textContent = 'β Please select a Python file';
|
| 563 |
+
output.classList.remove('empty');
|
| 564 |
+
return;
|
| 565 |
+
}}
|
| 566 |
+
|
| 567 |
+
const file = input.files[0];
|
| 568 |
+
if (!file.name.endsWith('.py')) {{
|
| 569 |
+
output.textContent = 'β Please select a .py file';
|
| 570 |
+
output.classList.remove('empty');
|
| 571 |
+
return;
|
| 572 |
+
}}
|
| 573 |
+
|
| 574 |
+
btn.disabled = true;
|
| 575 |
+
btn.innerHTML = '<span class="loading"></span> Uploading & Executing...';
|
| 576 |
+
output.textContent = 'Uploading and executing file...';
|
| 577 |
+
output.classList.remove('empty');
|
| 578 |
+
|
| 579 |
+
try {{
|
| 580 |
+
const formData = new FormData();
|
| 581 |
+
formData.append('file', file);
|
| 582 |
+
formData.append('admin_id', 'web-console');
|
| 583 |
+
|
| 584 |
+
const response = await fetch('/api/run-file', {{
|
| 585 |
+
method: 'POST',
|
| 586 |
+
body: formData
|
| 587 |
+
}});
|
| 588 |
+
|
| 589 |
+
const data = await response.json();
|
| 590 |
+
|
| 591 |
+
if (data.success) {{
|
| 592 |
+
output.textContent = 'β
File executed successfully:\\n\\n' + data.output;
|
| 593 |
+
}} else {{
|
| 594 |
+
output.textContent = 'β Error:\\n\\n' + data.error;
|
| 595 |
+
}}
|
| 596 |
+
}} catch (error) {{
|
| 597 |
+
output.textContent = 'β Network error: ' + error.message;
|
| 598 |
+
}} finally {{
|
| 599 |
+
btn.disabled = false;
|
| 600 |
+
btn.innerHTML = 'Upload & Execute';
|
| 601 |
+
}}
|
| 602 |
+
}}
|
| 603 |
+
|
| 604 |
+
async function getSystemInfo(command) {{
|
| 605 |
+
const output = document.getElementById('sys-output');
|
| 606 |
+
output.textContent = 'Loading...';
|
| 607 |
+
output.classList.remove('empty');
|
| 608 |
+
|
| 609 |
+
try {{
|
| 610 |
+
const response = await fetch('/api/execute', {{
|
| 611 |
+
method: 'POST',
|
| 612 |
+
headers: {{ 'Content-Type': 'application/json' }},
|
| 613 |
+
body: JSON.stringify({{ command: command, admin_id: 'web-console' }})
|
| 614 |
+
}});
|
| 615 |
+
|
| 616 |
+
const data = await response.json();
|
| 617 |
+
|
| 618 |
+
if (data.success) {{
|
| 619 |
+
output.textContent = data.output;
|
| 620 |
+
}} else {{
|
| 621 |
+
output.textContent = 'β Error: ' + data.error;
|
| 622 |
+
}}
|
| 623 |
+
}} catch (error) {{
|
| 624 |
+
output.textContent = 'β Network error: ' + error.message;
|
| 625 |
+
}}
|
| 626 |
+
}}
|
| 627 |
+
|
| 628 |
+
// Enable Enter key for command input
|
| 629 |
+
document.getElementById('cmd-input').addEventListener('keypress', function(e) {{
|
| 630 |
+
if (e.key === 'Enter') {{
|
| 631 |
+
runCommand();
|
| 632 |
+
}}
|
| 633 |
+
}});
|
| 634 |
+
</script>
|
| 635 |
+
</body>
|
| 636 |
+
</html>
|
| 637 |
+
"""
|
| 638 |
+
|
| 639 |
+
@app.post("/api/execute")
|
| 640 |
+
async def execute_command(request: Request):
|
| 641 |
+
"""Execute a shell command"""
|
| 642 |
+
try:
|
| 643 |
+
data = await request.json()
|
| 644 |
+
command = data.get("command", "").strip()
|
| 645 |
+
admin_id = data.get("admin_id", "")
|
| 646 |
+
|
| 647 |
+
if not command:
|
| 648 |
+
return JSONResponse({"success": False, "error": "No command provided"})
|
| 649 |
+
|
| 650 |
+
# Verify admin (basic check)
|
| 651 |
+
if not verify_admin(admin_id):
|
| 652 |
+
return JSONResponse({"success": False, "error": "Unauthorized"})
|
| 653 |
+
|
| 654 |
+
logger.info(f"Executing command: {command}")
|
| 655 |
+
|
| 656 |
+
# Execute command with timeout
|
| 657 |
+
try:
|
| 658 |
+
result = subprocess.run(
|
| 659 |
+
command,
|
| 660 |
+
shell=True,
|
| 661 |
+
capture_output=True,
|
| 662 |
+
text=True,
|
| 663 |
+
timeout=30
|
| 664 |
+
)
|
| 665 |
+
|
| 666 |
+
output = result.stdout if result.stdout else result.stderr
|
| 667 |
+
if not output:
|
| 668 |
+
output = "Command executed successfully (no output)"
|
| 669 |
+
|
| 670 |
+
return JSONResponse({
|
| 671 |
+
"success": True,
|
| 672 |
+
"output": output,
|
| 673 |
+
"return_code": result.returncode
|
| 674 |
+
})
|
| 675 |
+
|
| 676 |
+
except subprocess.TimeoutExpired:
|
| 677 |
+
return JSONResponse({
|
| 678 |
+
"success": False,
|
| 679 |
+
"error": "Command timed out after 30 seconds"
|
| 680 |
+
})
|
| 681 |
+
except Exception as e:
|
| 682 |
+
return JSONResponse({
|
| 683 |
+
"success": False,
|
| 684 |
+
"error": f"Execution error: {str(e)}"
|
| 685 |
+
})
|
| 686 |
+
|
| 687 |
+
except Exception as e:
|
| 688 |
+
logger.error(f"Error in execute_command: {e}")
|
| 689 |
+
return JSONResponse({
|
| 690 |
+
"success": False,
|
| 691 |
+
"error": f"Server error: {str(e)}"
|
| 692 |
+
})
|
| 693 |
+
|
| 694 |
+
@app.post("/api/eval")
|
| 695 |
+
async def evaluate_python(request: Request):
|
| 696 |
+
"""Execute Python code"""
|
| 697 |
+
try:
|
| 698 |
+
data = await request.json()
|
| 699 |
+
code = data.get("code", "").strip()
|
| 700 |
+
admin_id = data.get("admin_id", "")
|
| 701 |
+
|
| 702 |
+
if not code:
|
| 703 |
+
return JSONResponse({"success": False, "error": "No code provided"})
|
| 704 |
+
|
| 705 |
+
# Verify admin
|
| 706 |
+
if not verify_admin(admin_id):
|
| 707 |
+
return JSONResponse({"success": False, "error": "Unauthorized"})
|
| 708 |
+
|
| 709 |
+
logger.info(f"Executing Python code (length: {len(code)})")
|
| 710 |
+
|
| 711 |
+
try:
|
| 712 |
+
# Create a namespace for execution
|
| 713 |
+
namespace = {
|
| 714 |
+
'__builtins__': __builtins__,
|
| 715 |
+
'os': os,
|
| 716 |
+
'subprocess': subprocess,
|
| 717 |
+
}
|
| 718 |
+
|
| 719 |
+
# Capture stdout
|
| 720 |
+
import sys
|
| 721 |
+
from io import StringIO
|
| 722 |
+
|
| 723 |
+
old_stdout = sys.stdout
|
| 724 |
+
sys.stdout = StringIO()
|
| 725 |
+
|
| 726 |
+
try:
|
| 727 |
+
# Try to evaluate as expression first
|
| 728 |
+
try:
|
| 729 |
+
result = eval(code, namespace)
|
| 730 |
+
if result is not None:
|
| 731 |
+
print(result)
|
| 732 |
+
except SyntaxError:
|
| 733 |
+
# If it fails, execute as statement
|
| 734 |
+
exec(code, namespace)
|
| 735 |
+
|
| 736 |
+
output = sys.stdout.getvalue()
|
| 737 |
+
if not output:
|
| 738 |
+
output = "Code executed successfully (no output)"
|
| 739 |
+
|
| 740 |
+
return JSONResponse({
|
| 741 |
+
"success": True,
|
| 742 |
+
"output": output
|
| 743 |
+
})
|
| 744 |
+
|
| 745 |
+
finally:
|
| 746 |
+
sys.stdout = old_stdout
|
| 747 |
+
|
| 748 |
+
except Exception as e:
|
| 749 |
+
return JSONResponse({
|
| 750 |
+
"success": False,
|
| 751 |
+
"error": f"{type(e).__name__}: {str(e)}\\n{traceback.format_exc()}"
|
| 752 |
+
})
|
| 753 |
+
|
| 754 |
+
except Exception as e:
|
| 755 |
+
logger.error(f"Error in evaluate_python: {e}")
|
| 756 |
+
return JSONResponse({
|
| 757 |
+
"success": False,
|
| 758 |
+
"error": f"Server error: {str(e)}"
|
| 759 |
+
})
|
| 760 |
+
|
| 761 |
+
@app.post("/api/run-file")
|
| 762 |
+
async def run_python_file(file: UploadFile = File(...), admin_id: str = Form(...)):
|
| 763 |
+
"""Upload and execute a Python file"""
|
| 764 |
+
try:
|
| 765 |
+
# Verify admin
|
| 766 |
+
if not verify_admin(admin_id):
|
| 767 |
+
return JSONResponse({"success": False, "error": "Unauthorized"})
|
| 768 |
+
|
| 769 |
+
# Verify file type
|
| 770 |
+
if not file.filename.endswith('.py'):
|
| 771 |
+
return JSONResponse({
|
| 772 |
+
"success": False,
|
| 773 |
+
"error": "Only .py files are allowed"
|
| 774 |
+
})
|
| 775 |
+
|
| 776 |
+
logger.info(f"Executing uploaded file: {file.filename}")
|
| 777 |
+
|
| 778 |
+
# Save file to temp location
|
| 779 |
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as tmp:
|
| 780 |
+
content = await file.read()
|
| 781 |
+
tmp.write(content.decode('utf-8'))
|
| 782 |
+
tmp_path = tmp.name
|
| 783 |
+
|
| 784 |
+
try:
|
| 785 |
+
# Execute the file
|
| 786 |
+
result = subprocess.run(
|
| 787 |
+
['python3', tmp_path], # Use python3 from PATH instead of hardcoded path
|
| 788 |
+
capture_output=True,
|
| 789 |
+
text=True,
|
| 790 |
+
timeout=30
|
| 791 |
+
)
|
| 792 |
+
|
| 793 |
+
output = result.stdout if result.stdout else result.stderr
|
| 794 |
+
if not output:
|
| 795 |
+
output = "File executed successfully (no output)"
|
| 796 |
+
|
| 797 |
+
return JSONResponse({
|
| 798 |
+
"success": True,
|
| 799 |
+
"output": output,
|
| 800 |
+
"return_code": result.returncode,
|
| 801 |
+
"filename": file.filename
|
| 802 |
+
})
|
| 803 |
+
|
| 804 |
+
except subprocess.TimeoutExpired:
|
| 805 |
+
return JSONResponse({
|
| 806 |
+
"success": False,
|
| 807 |
+
"error": "Execution timed out after 30 seconds"
|
| 808 |
+
})
|
| 809 |
+
finally:
|
| 810 |
+
# Clean up temp file
|
| 811 |
+
try:
|
| 812 |
+
os.unlink(tmp_path)
|
| 813 |
+
except (FileNotFoundError, PermissionError) as e:
|
| 814 |
+
logger.warning(f"Could not delete temp file {tmp_path}: {e}")
|
| 815 |
+
|
| 816 |
+
except Exception as e:
|
| 817 |
+
logger.error(f"Error in run_python_file: {e}")
|
| 818 |
+
return JSONResponse({
|
| 819 |
+
"success": False,
|
| 820 |
+
"error": f"Server error: {str(e)}"
|
| 821 |
+
})
|
| 822 |
+
|
| 823 |
+
@app.get("/health")
|
| 824 |
+
async def health():
|
| 825 |
+
"""Health check endpoint"""
|
| 826 |
+
return {"status": "healthy", "bot": "running"}
|
| 827 |
+
|
| 828 |
+
@app.get("/api/status")
|
| 829 |
+
async def status():
|
| 830 |
+
"""Status endpoint"""
|
| 831 |
+
return {
|
| 832 |
+
"status": "online",
|
| 833 |
+
"service": "Telegram Server Manager Bot",
|
| 834 |
+
"port": 7860,
|
| 835 |
+
"web_console": "enabled"
|
| 836 |
+
}
|
| 837 |
+
|
| 838 |
+
if __name__ == "__main__":
|
| 839 |
+
# Run on port 7860 for Hugging Face Spaces
|
| 840 |
+
port = int(os.environ.get("PORT", 7860))
|
| 841 |
+
logger.info(f"Starting web server on port {port}...")
|
| 842 |
+
uvicorn.run(app, host="0.0.0.0", port=port)
|
start.sh
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
set -e # Exit on error
|
| 4 |
+
|
| 5 |
+
echo "π Starting Telegram Server Manager Bot..."
|
| 6 |
+
echo "π Cleaning old session files..."
|
| 7 |
+
|
| 8 |
+
# Delete old session files
|
| 9 |
+
find . -name "*.session" -type f -delete || echo "No session files to clean"
|
| 10 |
+
|
| 11 |
+
echo "π Starting web server..."
|
| 12 |
+
# Start the FastAPI server in the background
|
| 13 |
+
python3 server.py &
|
| 14 |
+
SERVER_PID=$!
|
| 15 |
+
|
| 16 |
+
# Check if server started successfully
|
| 17 |
+
sleep 2
|
| 18 |
+
if ! kill -0 $SERVER_PID 2>/dev/null; then
|
| 19 |
+
echo "β Failed to start web server"
|
| 20 |
+
exit 1
|
| 21 |
+
fi
|
| 22 |
+
|
| 23 |
+
echo "β
Web server started (PID: $SERVER_PID)"
|
| 24 |
+
echo "π€ Starting Telegram bot..."
|
| 25 |
+
|
| 26 |
+
# Start the Telegram bot
|
| 27 |
+
python3 bot.py
|