bot: Added bot files

This commit is contained in:
Nathan Woodburn 2023-08-07 14:47:26 +10:00
parent b3655f2f25
commit 13f8133094
Signed by: nathanwoodburn
GPG Key ID: 203B000478AD0EF1
8 changed files with 420 additions and 1 deletions

View File

@ -1 +1,29 @@
# hnshosting-bot
# HNS Hosting Discord Bot
## Setup
Install the python requirements with `pip install -r requirements.txt`
Add your discord token to a .env file in the root directory of the project
```sh
DISCORD_TOKEN=your_token_here
```
Install nginx
```sh
sudo apt update
sudo apt install nginx -y
```
## Run
You can run the bot with
```sh
python3 main.py
```
To run the bot in the background, you can use `screen`
```sh
screen -S bot
python3 main.py
```
Close the screen with `Ctrl + A + D` (keeps the process running)
Resume the screen with `screen -r bot`

167
bot.py Normal file
View File

@ -0,0 +1,167 @@
import subprocess
import os
from dotenv import load_dotenv
import discord
from discord import app_commands
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
intents = discord.Intents.default()
client = discord.Client(intents=intents)
tree = app_commands.CommandTree(client)
@tree.command(name = "mirror", description = "Create a mirror of an ICANN site on a Handshake domain")
async def mirror(interaction, handshakedomain: str, icannurl: str):
print("Creating mirror to " + icannurl + " from " + handshakedomain + "...")
if not icannurl.startswith("https://"):
await interaction.response.send_message("Please use https:// for the ICANN URL", ephemeral=True)
return
await interaction.response.send_message("Creating mirror to " + icannurl + " from " + handshakedomain + "..." + "\nCheck your DM for the status of your mirror.", ephemeral=True)
# Get user from interaction
user = interaction.user
handshakedomain_str = str(handshakedomain)
icannurl_str = str(icannurl)
user_str = str(user.id)
command = ['./proxy.sh', handshakedomain_str, icannurl_str, user_str]
try:
output = subprocess.check_output(command, stderr=subprocess.STDOUT, text=True)
print("TLSA output:")
tlsa = get_tlsa(output)
if tlsa is not None:
print(tlsa)
message = "Mirror setup! Add this TLSA record\n`_443._tcp." + handshakedomain_str + "` : `" + tlsa + "`\nAdd this A\n`" + handshakedomain_str + "` : `152.69.188.246`"
await user.send(message)
else:
error = get_error(output)
await user.send(error)
except subprocess.CalledProcessError as e:
print(f"Command execution failed with exit code {e.returncode}.")
await user.send(f"Command execution failed.")
updateStatus()
@tree.command(name = "delete", description = "Delete a Handshake domain from the system")
async def delete(interaction, handshakedomain: str):
print("Deleting " + handshakedomain + "...")
await interaction.response.send_message("Deleting " + handshakedomain + "..." + "\nCheck your DM for status.", ephemeral=True)
# Get user from interaction
user = interaction.user
handshakedomain_str = str(handshakedomain)
user_str = str(user.id)
# Construct the command
command = ['./delete.sh', handshakedomain_str, user_str]
try:
output = subprocess.check_output(command, stderr=subprocess.STDOUT, text=True)
print("Delete output:")
out=output.split("\n")[0]
print(out)
await user.send(out)
except subprocess.CalledProcessError as e:
print(f"Command execution failed with exit code {e.returncode}.")
await user.send(f"Command execution failed.")
updateStatus()
@tree.command(name = "list", description = "List all Handshake mirrors")
async def list(interaction):
print("Listing mirrors...")
# Get user from interaction
user = interaction.user
if user.id != 892672018917519370:
await interaction.response.send_message("You don't have permission to do that.", ephemeral=True)
print(user + " tried to list mirrors.")
return
# List all files in the directory using os
files = os.listdir("/etc/nginx/sites-available")
# Remove default
files.remove("default")
await interaction.response.send_message("Here are all the mirrors:\n" + "\n".join(files))
@tree.command(name = "tlsa", description = "Get the TLSA record for an existing Handshake domain")
async def tlsa(interaction, handshakedomain: str):
print("Getting TLSA record for " + handshakedomain + "...")
# Get user from interaction
output = subprocess.check_output(['./tlsa.sh', handshakedomain], stderr=subprocess.STDOUT, text=True)
await interaction.response.send_message(output, ephemeral=True)
@tree.command(name = "git", description = "Create a website from a git repo of html files")
async def git(interaction, handshakedomain: str, giturl: str):
print("Creating website from " + giturl + " on " + handshakedomain + "...")
if not giturl.startswith("https://"):
await interaction.response.send_message("Please use https:// for the git URL", ephemeral=True)
return
await interaction.response.send_message("Creating website from " + giturl + " on " + handshakedomain + "..." + "\nCheck your DM for the status of your website.", ephemeral=True)
user = interaction.user
handshakedomain_str = str(handshakedomain)
user_str = str(user.id)
giturl_str = str(giturl)
output = subprocess.check_output(['./git.sh', handshakedomain_str, giturl_str, user_str], stderr=subprocess.STDOUT, text=True)
# Check if output contains any errors
lowercase_string = output.lower()
# Check if the lowercase string contains the word "error"
if "error" in lowercase_string:
await user.send("Failed with error:\n" + output)
return
output = subprocess.check_output(['./tlsa.sh', handshakedomain_str], stderr=subprocess.STDOUT, text=True)
# Get only second line
output = output.split("\n")[1]
await user.send("Website created! Add this TLSA record\n`_443._tcp." + handshakedomain_str + "` : `" + output + "`\nAdd this A\n`" + handshakedomain_str + "` : `152.69.188.246`")
@tree.command(name = "gitpull", description = "Get the latest changes from a git repo of html files")
async def gitpull(interaction, handshakedomain: str):
print("Pulling latest changes from " + handshakedomain + "...")
await interaction.response.send_message("Pulling changes for " + handshakedomain + "...", ephemeral=True)
user = interaction.user
handshakedomain_str = str(handshakedomain)
user_str = str(user.id)
output = subprocess.check_output(['./gitpull.sh', handshakedomain_str, user_str], stderr=subprocess.STDOUT, text=True)
# Check if output contains any errors
lowercase_string = output.lower()
# Check if the lowercase string contains the word "error"
if "error" in lowercase_string:
await user.send("Failed with error:\n" + output)
return
await user.send("Changes pulled for " + handshakedomain + "!")
def get_tlsa(input_string):
lines = input_string.split("\n")
for line in lines:
if line.strip().startswith("TLSA:"):
return line[6:]
return None
def get_error(input_string):
lines = input_string.split("\n")
for line in lines:
if line.strip().startswith("ERROR:"):
return line
return None
def updateStatus():
# List all files in the directory using os
files = os.listdir("/etc/nginx/sites-available")
# Count the number of files - 1 (default)
count = len(files) - 1
# Set the status
activity = discord.Activity(name=str(count) + " mirrors", type=discord.ActivityType.watching)
# Update the status
client.loop.create_task(client.change_presence(activity=activity))
@client.event
async def on_ready():
await tree.sync()
print("Ready!")
updateStatus()
client.run(TOKEN)

33
delete.sh Normal file
View File

@ -0,0 +1,33 @@
#!/bin/bash
domain=$1
user=$2
# Set all to lowercase
domain=${domain,,}
user=${user,,}
file_path="/etc/nginx/sites-available/$domain"
# Check if domain already exists
if [ -f $file_path ]; then
# Verify owner
if grep -q "$user" "$file_path"; then
rm $file_path
rm /etc/nginx/sites-enabled/$domain
systemctl restart nginx
echo "Domain deleted!"
else
echo "ERROR: You do not own this domain"
exit 0;
fi
else
echo "ERROR: Domain doesn't exists"
exit 0;
fi
# Check if website files exist
if [ -d "/var/www/$domain" ]; then
rm -rf /var/www/$domain
echo "Website files deleted!"
fi

93
git.sh Normal file
View File

@ -0,0 +1,93 @@
#!/bin/bash
# This script is used to setup nginx for a static website using files from a git repository.
# Make sure the git repo has an `index.html` and `404.html` file.
# Usage ./git.sh [domain] [git repo url] [optional: user]
# Example ./git.sh nathan.woodburn https://github.com/Nathanwoodburn/Nathanwoodburn.github.io.git
# Variables
domain=$1
git_repo=$2
user=$3
# Check if domain name is set
if [ -z "$1" ]
then
echo "Domain name:"
read domain
fi
# Check if git repo is set
if [ -z "$2" ]
then
echo "Git repo:"
read git_repo
fi
# Check if nginx is installed
if ! [ -x "$(command -v nginx)" ]; then
sudo apt update
sudo apt install nginx -y
fi
# Clone git repo
git clone $git_repo /var/www/$domain
# Setup NGINX config
printf "# $user
server {
listen 80;
listen [::]:80;
root /var/www/$domain;
index index.html;
server_name $domain *.$domain;
location / {
try_files \$uri \$uri/ @htmlext;
}
location ~ \.html$ {
try_files \$uri =404;
}
location @htmlext {
rewrite ^(.*)$ \$1.html last;
}
error_page 404 /404.html;
location = /404.html {
internal;
}
location = /.well-known/wallets/HNS {
add_header Cache-Control 'must-revalidate';
add_header Content-Type text/plain;
}
listen 443 ssl;
ssl_certificate /etc/ssl/$domain.crt;
ssl_certificate_key /etc/ssl/$domain.key;
}
" > /etc/nginx/sites-available/$domain
sudo ln -s /etc/nginx/sites-available/$domain /etc/nginx/sites-enabled/$domain
#generate ssl certificate
openssl req -x509 -newkey rsa:4096 -sha256 -days 365 -nodes \
-keyout cert.key -out cert.crt -extensions ext -config \
<(echo "[req]";
echo distinguished_name=req;
echo "[ext]";
echo "keyUsage=critical,digitalSignature,keyEncipherment";
echo "extendedKeyUsage=serverAuth";
echo "basicConstraints=critical,CA:FALSE";
echo "subjectAltName=DNS:$domain,DNS:*.$domain";
) -subj "/CN=*.$domain"
TLSA=$(echo -n "3 1 1 " && openssl x509 -in cert.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | xxd -p -u -c 32)
echo "TLSA: $TLSA"
sudo mv cert.key /etc/ssl/$domain.key
sudo mv cert.crt /etc/ssl/$domain.crt
# Restart to apply config file
sudo systemctl restart nginx

14
gitpull.sh Normal file
View File

@ -0,0 +1,14 @@
#!/bin/bash
# Variables
domain=$1
user=$2
# Verify owner
file_path="/etc/nginx/sites-available/$domain"
if grep -q "$user" "$file_path"; then
git -C /var/www/$domain pull
echo "Git pull complete!"
else
echo "ERROR: You do not own this domain"
fi

69
proxy.sh Normal file
View File

@ -0,0 +1,69 @@
#!/bin/bash
domain=$1
url=$2
user=$3
# Set all to lowercase
domain=${domain,,}
url=${url,,}
user=${user,,}
# Check if domain already exists
if [ -f /etc/nginx/sites-available/$domain ]; then
echo "ERROR: Domain already exists"
exit 0;
fi
# Verify url is valid timeout 5 seconds
valid=$(timeout 5 curl -s -o /dev/null -w "%{http_code}" $url | grep 200)
if [ -n "$valid" ]; then
echo "URL is valid: $url"
else
echo "ERROR: URL is not valid or unreachable: $url"
exit 0;
fi
printf "# $user
server {
listen 80;
listen [::]:80;
server_name $domain;
proxy_ssl_server_name on;
location / {
proxy_set_header X-Real-IP \$remote_addr;
proxy_pass $url;
}
listen 443 ssl;
ssl_certificate /etc/ssl/$domain.crt;
ssl_certificate_key /etc/ssl/$domain.key;
}" > /etc/nginx/sites-available/$domain
sudo ln -s /etc/nginx/sites-available/$domain /etc/nginx/sites-enabled/$domain
#generate ssl certificate
openssl req -x509 -newkey rsa:4096 -sha256 -days 365 -nodes \
-keyout cert.key -out cert.crt -extensions ext -config \
<(echo "[req]";
echo distinguished_name=req;
echo "[ext]";
echo "keyUsage=critical,digitalSignature,keyEncipherment";
echo "extendedKeyUsage=serverAuth";
echo "basicConstraints=critical,CA:FALSE";
echo "subjectAltName=DNS:$domain,DNS:*.$domain";
) -subj "/CN=*.$domain"
# Respond with TLSA
TLSA=$(echo -n "3 1 1 " && openssl x509 -in cert.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | xxd -p -u -c 32)
echo "TLSA: $TLSA"
sudo mv cert.key /etc/ssl/$domain.key
sudo mv cert.crt /etc/ssl/$domain.crt
# Restart to apply config file
sudo systemctl restart nginx

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
discord
discord.py
python-dotenv

12
tlsa.sh Normal file
View File

@ -0,0 +1,12 @@
#!/bin/bash
domain=$1
# Check if args passed
if [ -z "$1" ]
then
# Ask for domain name
echo "Domain name:"
read domain
fi
echo "TLSA record:"
echo -n "3 1 1 " && openssl x509 -in /etc/ssl/$domain.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | xxd -p -u -c 32