Initial commit

This commit is contained in:
Stanislav Mykhailenko 2023-01-31 15:27:10 +02:00
commit 9feb845d3c
GPG key ID: 1E95E66A9C9D6A36
7 changed files with 333 additions and 0 deletions

131
.gitignore vendored Normal file
View file

@ -0,0 +1,131 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
token.txt

23
README.md Normal file
View file

@ -0,0 +1,23 @@
# Computer kill switch
This program is a PC kill switch Telegram bot. It is a project for New Generation.
## Security notice
This program provides no access control, so anyone knowing the bot username is able to use it.
## Features
- disabling and re-enabling keyboard and mouse input
- locking the screen
- formatting partitions
- playing text-to-speech messages
## Limitations
- input lock resets if Control-Alt-Delete is pressed
- volumes used by some software at the time of request cannot be formatted
- works on Windows only
## Usage
Before starting the bot, its token must be put into token.txt file with no newlines. Administrator privileges are required so that the bot is able to disable input and format partitions. Once the bot is started, send /start to its account on Telegram to see what can be done.
## Licensing
All code in this repository is Unlicensed, see UNLICENSE.

24
UNLICENSE Normal file
View file

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

15
main.py Normal file
View file

@ -0,0 +1,15 @@
# Project: Telegram PC kill switch bot
# Author: Stanislav Mykhailenko
# License: Unlicense
import ctypes, sys
from tg import startBot
def main() -> None:
if ctypes.windll.shell32.IsUserAnAdmin():
startBot()
else:
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
if __name__ == '__main__':
main()

25
payload.py Normal file
View file

@ -0,0 +1,25 @@
# Project: Telegram PC kill switch bot
# Author: Stanislav Mykhailenko
# License: Unlicense
# This file contains the payloads
import ctypes, os, pyttsx3
def enableInput():
ctypes.windll.user32.BlockInput(False)
def disableInput():
ctypes.windll.user32.BlockInput(True)
def lockScreen():
ctypes.windll.user32.LockWorkStation()
def formatVolumes(volumes):
for volume in volumes:
os.system('format ' + volume + ' /y')
def playMessage(message):
engine = pyttsx3.init()
engine.say(message)
engine.runAndWait()

10
requirements.txt Normal file
View file

@ -0,0 +1,10 @@
anyio==3.6.2
certifi==2022.12.7
h11==0.14.0
httpcore==0.16.3
httpx==0.23.3
idna==3.4
python-telegram-bot==20.0
pyttsx3==2.90
rfc3986==1.5.0
sniffio==1.3.0

105
tg.py Normal file
View file

@ -0,0 +1,105 @@
# Project: Telegram PC kill switch bot
# Author: Stanislav Mykhailenko
# License: Unlicense
# This file contains Telegram interactions
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
from telegram.ext import Application, CommandHandler, ContextTypes, ConversationHandler, filters, MessageHandler
from payload import *
import logging
from threading import Thread
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO
)
logger = logging.getLogger(__name__)
ACTION, FORMAT, MESSAGE = range(3)
def getToken():
with open(os.path.realpath(os.path.dirname(__file__)) + '/' + 'token.txt', 'r') as file:
return file.read()
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
reply_keyboard = [["Enable input", "Disable input", "Lock screen", "Format volumes", "Play message"]]
await update.message.reply_text(
"Choose your action:",
reply_markup=ReplyKeyboardMarkup(
reply_keyboard, one_time_keyboard=True, input_field_placeholder="Choose your action"
),
)
return ACTION
async def action(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
user = update.message.from_user
match update.message.text:
case "Enable input":
logger.info("Got a request from %s to enable input.", user.first_name)
enableInput()
await update.message.reply_text("Input enabled.", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
case "Disable input":
logger.info("Got a request from %s to disable input.", user.first_name)
disableInput()
await update.message.reply_text("Input disabled.", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
case "Lock screen":
logger.info("Got a request from %s to lock screen.", user.first_name)
lockScreen()
await update.message.reply_text("Screen locked.", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
case "Format volumes":
logger.info("Got a request from %s to format volumes.", user.first_name)
await update.message.reply_text("Please type a space-separated list of the volumes you want to format.", reply_markup=ReplyKeyboardRemove())
return FORMAT
case "Play message":
logger.info("Got a request from %s to play a message.", user.first_name)
await update.message.reply_text("Please type the message you want to play.", reply_markup=ReplyKeyboardRemove())
return MESSAGE
async def format(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
user = update.message.from_user
logger.info("Got a request from %s to format volumes %s.", user.first_name, update.message.text)
Thread(target=formatVolumes,args=(update.message.text.split(),)).start()
await update.message.reply_text("Command to format the volumes sent.")
return ConversationHandler.END
async def message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
user = update.message.from_user
logger.info("Got a request from %s to play message %s.", user.first_name, update.message.text)
Thread(target=playMessage,args=(update.message.text,)).start()
await update.message.reply_text("Command to play the message sent.")
return ConversationHandler.END
async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
user = update.message.from_user
logger.info("User %s canceled the conversation.", user.first_name)
await update.message.reply_text("Request cancelled.", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
def startBot():
application = Application.builder().token(getToken()).build()
conv_handler = ConversationHandler(
entry_points=[CommandHandler("start", start)],
states={
ACTION: [MessageHandler(filters.Regex("^(Enable input|Disable input|Lock screen|Format volumes|Play message)$"), action)],
FORMAT: [MessageHandler(filters.TEXT & ~filters.COMMAND, format)],
MESSAGE: [MessageHandler(filters.TEXT & ~filters.COMMAND, message)],
},
fallbacks=[CommandHandler("cancel", cancel)],
)
application.add_handler(conv_handler)
application.run_polling()