bot: Added bot files
This commit is contained in:
parent
b3655f2f25
commit
13f8133094
30
README.md
30
README.md
@ -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
167
bot.py
Normal 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
33
delete.sh
Normal 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
93
git.sh
Normal 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
14
gitpull.sh
Normal 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
69
proxy.sh
Normal 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
3
requirements.txt
Normal file
@ -0,0 +1,3 @@
|
||||
discord
|
||||
discord.py
|
||||
python-dotenv
|
12
tlsa.sh
Normal file
12
tlsa.sh
Normal 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
|
Loading…
Reference in New Issue
Block a user