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