tools

- collection of tools for supernets sysadmins
git clone git://git.acid.vegas/tools.git
Log | Files | Refs | Archive

commit c5342532e4e16d40b8992eff82a86eea31cf8e39
Author: acidvegas <acid.vegas@acid.vegas>
Date: Thu, 25 Jan 2024 23:06:20 -0500

Initial commit

Diffstat:
A.screenrc | 30++++++++++++++++++++++++++++++
Abots/5000.go | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abots/5000.py | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abots/hateserv/api.py | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abots/hateserv/commands.py | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abots/hateserv/hateserv.py | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adeploy/httpd | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adeploy/ircd | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adeploy/jitsi | 11+++++++++++
Adeploy/services | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adeploy/tor_ircd_helper | 14++++++++++++++
Ahelp/altdns.py | 20++++++++++++++++++++
Ahelp/irclxc | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahelp/ircwall | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahelp/monitor | 9+++++++++
Ahelp/namecheap.py | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahelp/startbots | 10++++++++++

17 files changed, 1305 insertions(+), 0 deletions(-)

diff --git a/.screenrc b/.screenrc
@@ -0,0 +1,29 @@
+altscreen on
+defscrollback 1000
+defutf8 on
+startup_message off
+term screen-256color
+vbell off
+zombie kr
+
+screen -t irc       weechat
+screen -t supernets ssh supernets
+screen -t contra    bash
+screen -t omega     ssh omega
+screen -t omni      ssh omni
+screen -t cowboy    ssh cowboy
+screen -t phish     ssh phish
+
+caption always "%{= kw}%=%-w%{= wk}%n %t%{-}%+W%="
+
+unbindall
+bindkey ^[[1;3D prev
+bindkey ^[[1;3C next
+bind : colon
+bind c screen
+bind d detach
+bind k kill
+bind n number
+bind r redisplay
+bind s source $HOME/.screenrc
+bind t title
+\ No newline at end of file
diff --git a/bots/5000.go b/bots/5000.go
@@ -0,0 +1,158 @@
+package main
+
+// Original 5000 bot was written in Python. This is a Go port of it by acidvegas.
+
+import (
+	"bufio"
+	"crypto/tls"
+	"fmt"
+	"log"
+	"math/rand"
+	"strings"
+	"time"
+	"unicode/utf16"
+)
+
+var sendChannel chan<- string
+
+const (
+	nickname = "FUCKYOU"
+	username = "5000"
+	realname = "\x0304THROWN INTO THE FUCKING WALL\x0f"
+)
+
+func randomASCIIString(n int) string {
+	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+	b := make([]byte, n)
+	for i := range b {
+		b[i] = charset[rand.Intn(len(charset))]
+	}
+	return string(b)
+}
+
+func unicodeString() string {
+	msg := "\u202e\u0007\x03" + fmt.Sprintf("%d", rand.Intn(12)+2)
+	for i := 0; i < rand.Intn(101)+200; i++ {
+		r := rand.Intn(0x2001) + 0x1000
+		if r < 0x10000 {
+			msg += string(rune(r))
+		} else {
+			r -= 0x10000
+			msg += string(utf16.DecodeRune(rune(r>>10+0xD800), rune(r&0x3FF+0xDC00)))
+		}
+	}
+	return msg
+}
+
+func attack(target string) {
+	sendChannel <- fmt.Sprintf("PRIVMSG #superbowl :I am fucking the shit out of %s right now...", target)
+	for i := 0; i < 200; i++ {
+		var channels []string
+		for i := 0; i < 25; i++ {
+			channelName := "#" + randomASCIIString(5)
+			channels = append(channels, channelName)
+		}
+		result := strings.Join(channels, ",")
+		sendChannel <- fmt.Sprintf("SAJOIN %s #%s", target, result)
+		sendChannel <- fmt.Sprintf("PRIVMSG #5000 :%s oh god %s what is happening %s", unicodeString(), target, unicodeString())
+		sendChannel <- fmt.Sprintf("PRIVMSG %s :%s oh god %s what is happening %s", target, unicodeString(), target, unicodeString())
+	}
+}
+
+func main() {
+	rand.Seed(time.Now().UnixNano())
+
+	for {
+		conn, err := tls.Dial("tcp", "localhost:6697", &tls.Config{InsecureSkipVerify: true})
+		if err != nil {
+			log.Println("Failed to connect: %v", err)
+			time.Sleep(15 * time.Second)
+			continue
+		}
+
+		messageChannel := make(chan string, 100) // INCOMING
+		sendChannel := make(chan string, 100)    // OUTGOING
+		quit := make(chan struct{})
+
+		timeoutDuration := 300 * time.Second
+		timeoutTimer := time.NewTimer(timeoutDuration)
+
+		go func() {
+			reader := bufio.NewReader(conn)
+			for {
+				line, err := reader.ReadString('\n')
+				if err != nil {
+					log.Println("Error reading from server:", err)
+					conn.Close()
+					close(quit)
+					return
+				}
+				select {
+				case messageChannel <- line:
+					timeoutTimer.Reset(timeoutDuration)
+				case <-quit:
+					return
+				case <-timeoutTimer.C:
+					log.Println("No data received for 300 seconds. Reconnecting...")
+					conn.Close()
+					close(quit)
+					return
+				}
+			}
+		}()
+
+		go func() {
+			for {
+				select {
+				case message := <-sendChannel:
+					data := []byte(message)
+					if len(data) > 510 {
+						data = data[:510]
+					}
+					_, err := conn.Write(append(data, '\r', '\n'))
+					if err != nil {
+						log.Println("Error writing to server:", err)
+						conn.Close()
+						close(quit)
+						return
+					}
+				case <-quit:
+					return
+				}
+			}
+		}()
+
+		sendChannel <- fmt.Sprintf("NICK FUCKYOU")
+		sendChannel <- fmt.Sprintf("USER 5000 0 * :THROW INTO THE FUCKING WALL")
+
+		for {
+			handleMessage(<-messageChannel, sendChannel)
+		}
+
+		conn.Close()
+		//close(quit)
+		time.Sleep(15 * time.Second)
+	}
+}
+
+func handleMessage(message string, sendChannel chan<- string) {
+	fmt.Println(message)
+	parts := strings.Split(message, " ")
+	if len(parts) > 1 && parts[0] == "PING" {
+		sendChannel <- fmt.Sprintf("PONG %s", parts[1])
+	} else if len(parts) > 2 && parts[1] == "001" {
+		time.Sleep(5 * time.Second)
+		sendChannel <- fmt.Sprintf("MODE FUCKYOU +BDd")
+		sendChannel <- fmt.Sprintf("PRIVMSG NickServ IDENTIFY FUCKYOU simps0nsfan420")
+		sendChannel <- fmt.Sprintf("PRIVMSG OPER FUCKYOU fartsimps0n420")
+		sendChannel <- fmt.Sprintf("JOIN #5000")
+		sendChannel <- fmt.Sprintf("JOIN #superbowl")
+	} else if len(parts) == 3 {
+		if parts[1] == "JOIN" && parts[2] == "#5000" {
+			nick := strings.Split(parts[0], "!")[0][1:]
+			if nick != "acidvegas" && nick != "ChanServ" && nick != "FUCKYOU" {
+				go attack(nick)
+			}
+		}
+	}
+}
diff --git a/bots/5000.py b/bots/5000.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# 5000 IRC Bot - Developed by acidvegas in Python (https://git.acid.vegas/supertools)
+
+'''
+This bot requires network operator privledges in order to use the SAJOIN command.
+The bot will idle in the #5000 channel and #superbowl.
+Anyone who joins the #5000 channel will be force joined into 5000 random channels.
+It will also spam corrupting unicode that lags some IRC clients.
+It will announce in #superbowl who joins the #5000 channel.
+The command .kills can be used to see how many people have been 5000'd.
+Join #5000 on irc.supernets.org for an example of what this does to an IRC client.
+Modify your IRCd to not send a NOTICE on SAJOIN so people will not be able to mitigate it.
+Bot is setup to handle abuse, aka people clone flooding in #5000 to try and break the bot.
+'''
+
+import os
+import random
+import socket
+import string
+import ssl
+import time
+import threading
+
+nickserv_password='CHANGEME'
+operator_password='CHANGEME'
+
+def rnd():
+	return ''.join(random.choice(string.ascii_letters) for _ in range(random.randint(4, 8)))
+
+def unicode():
+	msg='\u202e\u0007\x03' + str(random.randint(2,13))
+	for i in range(random.randint(200, 300)):
+		msg += chr(random.randint(0x1000,0x3000))
+	return msg
+
+def attack(nick):
+	try:
+		raw(f'PRIVMSG #superbowl :I am fucking the shit out of {nick} right now...')
+		count += 1
+		if not count % 10:
+			with open(log, 'w') as log_file:
+				log_file.write(count)
+		for i in range(200):
+			if nick not in nicks:
+				break
+			else:
+				channels = ','.join(('#' + rnd() for x in range(25)))
+				raw(f'SAJOIN {nick} {channels}')
+				raw(f'PRIVMSG #5000 :{unicode()} oh god {nick} what is happening {unicode()}')
+				raw(f'PRIVMSG {nick} :{unicode()} oh god {nick} what is happening {unicode()}')
+				time.sleep(0.3)
+	except:
+		pass
+	finally:
+		if nick in nicks:
+			nicks.remove(nick)
+
+def raw(msg):
+	sock.send(bytes(msg + '\r\n', 'utf-8'))
+
+# Main
+log   = os.path.join(os.path.dirname(os.path.realpath(__file__)), '5000.log')
+last  = 0
+nicks = list()
+if not os.path.isfile(log):
+	open(log, 'w').write('0')
+	count = 0
+else:
+	count = open(log).read()
+while True:
+	try:
+		sock = ssl.wrap_socket(socket.socket())
+		sock.connect(('localhost', 6697))
+		raw('USER 5000 0 * :THROWN INTO THE FUCKING WALL')
+		raw('NICK FUCKYOU')
+		while True:
+			try:
+				data = sock.recv(1024).decode('utf-8')
+				for line in (line for line in data.split('\r\n') if len(line.split()) >= 2):
+					print('{0} | [~] - {1}'.format(time.strftime('%I:%M:%S'), line))
+					args = line.split()
+					if line.startswith('ERROR :Closing Link:'):
+						raise Exception('Connection has closed.')
+					elif args[0] == 'PING':
+						raw('PONG ' + args[1][1:])
+					elif args[1] == '001':
+						raw('MODE FUCKYOU +BDd')
+						raw('PRIVMSG NickServ IDENTIFY FUCKYOU ' + nickserv_password)
+						raw('OPER 5000 ' + operator_password)
+						raw('JOIN #superbowl')
+						raw('JOIN #5000')
+					elif args[1] == '401' and len(args) >= 4:
+						nick = args[3]
+						if nick in nicks:
+							nicks.remove(nick)
+					elif args[1] == 'JOIN' and len(args) == 3:
+						nick = args[0].split('!')[0][1:]
+						chan = args[2][1:]
+						if chan == '#5000' and nick not in ('acidvegas', 'ChanServ', 'FUCKYOU') and len(nicks) < 3 and nick not in nicks:
+							nicks.append(nick)
+							threading.Thread(target=attack, args=(nick,)).start()
+					elif args[1] == 'PRIVMSG' and len(args) == 4:
+						chan = args[2][1:]
+						msg  = ' '.join(args[3:])[1:]
+						if chan == '#superbowl' and msg == '.kills' and time.time() - last > 3:
+							raw('PRIVMSG #superbowl :' + str(count))
+							last = time.time()
+			except (UnicodeDecodeError, UnicodeEncodeError):
+				pass
+	except:
+		sock.close()
+	finally:
+		time.sleep(15)
+\ No newline at end of file
diff --git a/bots/hateserv/api.py b/bots/hateserv/api.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# hateserv irc bot - developed by acidvegas in python (https://git.acid.vegas/hateserv)
+
+import http.client
+import json
+import re
+
+def between(source, start, stop):
+	data = re.compile(start + '(.*?)' + stop, re.IGNORECASE|re.MULTILINE).search(source)
+	return data.group(1) if data else False
+
+def geoip(ip: str):
+	api = geturl('api.ipapi.is', '?q='+ip)
+	data = json.loads(api)
+	LOCATION = '{0}, {1}, {2}'.format(data['location']['city'], data['location']['state'], data['location']['country_code'])
+	ASN = 'AS{0} {1}'.format(data['asn']['asn'], data['asn']['descr'])
+	RIR = data['rir']
+	return {'location': LOCATION, 'asn': ASN, 'rir': RIR}
+
+def geturl(url, endpoint, headers={}):
+	conn = http.client.HTTPSConnection(url, timeout=15)
+	conn.request('GET', endpoint, headers=headers)
+	response = conn.getresponse().read()
+	conn.close()
+	return response
+
+def google(query):
+	service = build('customsearch', 'v1', developerKey=google_api_key, cache_discovery=False).cse()
+	results = service.list(q=query, cx=google_cse_id, num=10).execute()
+	return results['items'] if results else False
+
+def github(option, query):
+	if option == 'search':
+		data = json.loads(geturl('api.github.com', '/search/repositories?q='+query, headers={'Accept':'application/vnd.github.v3+json','User-Agent':'HateServ/1.0'}))
+		return data['items'] if data['items'] else False
+	elif option == 'repo':
+		return json.loads(geturl('api.github.com', '/repos/'+query, headers={'Accept':'application/vnd.github.v3+json','User-Agent':'HateServ/1.0'}))
+	elif option == 'user':
+		return json.loads(geturl('api.github.com', '/users/'+query, headers={'Accept':'application/vnd.github.v3+json','User-Agent':'HateServ/1.0'}))
+
+def imdb(query):
+	''' https://www.omdbapi.com '''
+	year = query.split()[-1]
+	query = query.replace(' ','%20')
+	search = 'i' if query.startswith('tt') else 't'
+	if search == 't' and len(year) == 4 and year.isdigit():
+		endpoint = f'/?{search}={query[:-5]}&y={year}&apikey={api_key}'
+	else:
+		endpoint = f'/?{search}={query}&apikey={api_key}'
+	conn = http.client.HTTPSConnection('omdbapi.com', timeout=15)
+	conn.request('GET', endpoint, headers={'Accept':'application/json'})
+	response = json.loads(conn.getresponse().read())
+	conn.close()
+	return response if response['Response'] == 'True' else False
+
+def reddit(option, subreddit, id=None):
+	if option == 'post':
+		data = json.loads(geturl('reddit.com', f'/r/{subreddit}/comments/{id}.json', headers={'Accept':'application/json','User-Agent':'HateServ/1.0'}))
+		return data[0]['data']['children'][0]['data'] if 'error' not in data else False
+	elif option == 'subreddit':
+		data = json.loads(geturl('reddit.com', f'/r/{subreddit}.json?limit=20', headers={'Accept':'application/json','User-Agent':'HateServ/1.0'}))
+		posts = [item['data'] for item in data['data']['children'] if not item['data']['stickied']]
+		return posts if posts else None
+
+def youtube(option, query, api_key):
+	if option == 'video':
+		api = httplib.get_json(f'https://www.googleapis.com/youtube/v3/videos?key={config.api.google_api_key}&part=snippet,statistics&id={id}')
+		if api['items']:
+			api                 = api['items'][0]
+			data                = {}
+			data['channel']     = api['snippet']['channelTitle']
+			data['description'] = ' '.join(api['snippet']['description'].split())
+			data['dislikes']    = api['statistics']['dislikeCount']
+			data['likes']       = api['statistics']['likeCount']
+			data['title']       = api['snippet']['title']
+			data['views']       = api['statistics']['viewCount']
+			return data
+		else:
+			return False
+	elif option == 'search':
+		service = build('youtube', 'v3', developerKey=api_key).search()
+		results = service.list(part='id', type='video', q=query, maxResults=10).execute()
+		return results['items'] if results else False
+
+def twitter(data):
+#	auth = tweepy.OAuthHandler(twitter_consumer_key, twitter_consumer_secret)
+#	auth.set_access_token(twitter_access_token, twitter_access_secret)
+#	api = tweepy.API(auth)
+#	api.update_status(data)
+	pass
+
+def unreal():
+	pass
+
+def anope():
+	pass
+
+
+'''
+elif args[0] == '.imdb' and len(args) >= 2:
+                        query = ' '.join(args[1:])
+                        api   = imdb.search(query, config.api.omdbapi_key)
+                        if api:
+                            Commands.sendmsg(chan, '{0} {1} {2} {3}'.format(color('Title  :', white), api['Title'], api['Year'], color(api['Rated'], grey)))
+                            Commands.sendmsg(chan, '{0} {1}{2}'.format(color('Link   :', white), underline, color('https://imdb.com/title/' +  api['imdbID'], light_blue)))
+                            Commands.sendmsg(chan, '{0} {1}'.format(color('Genre  :', white), api['Genre']))
+                            if api['imdbRating'] == 'N/A':
+                                Commands.sendmsg(chan, '{0} {1} N/A'.format(color('Rating :', white), color('★★★★★★★★★★', grey)))
+                            else:
+                                Commands.sendmsg(chan, '{0} {1}{2} {3}'.format(color('Rating :', white), color('★'*round(float(api['imdbRating'])), yellow), color('★'*(10-round(float(api['imdbRating']))), grey), a
+pi['imdbRating']))
+                            Commands.sendmsg(chan, '{0} {1}'.format(color('Plot   :', white), api['Plot']))
+                        else:
+                            Commands.error(chan, 'no results found')
+'''
+\ No newline at end of file
diff --git a/bots/hateserv/commands.py b/bots/hateserv/commands.py
@@ -0,0 +1,143 @@
+import csv
+import io
+import json
+import urllib.request
+import sys
+import time
+
+def download_file(url: str, dest_filename: str, chunk_size: int = 1024*1024):
+    '''
+    Download a file from a given URL in chunks and save to a destination filename.
+
+    :param url: The URL of the file to download
+    :param dest_filename: The destination filename to save the downloaded file
+    :param chunk_size: Size of chunks to download. Default is set to 1MB.
+    '''
+    with urllib.request.urlopen(url) as response:
+        total_size = int(response.getheader('Content-Length').strip())
+        downloaded_size = 0
+        with open(dest_filename, 'wb') as out_file:
+            while True:
+                start_time = time.time()
+                chunk = response.read(chunk_size)
+                if not chunk:
+                    break
+                downloaded_size += len(chunk)
+                out_file.write(chunk)
+                end_time = time.time()
+                speed = len(chunk) / (end_time - start_time)
+                progress = (downloaded_size / total_size) * 100
+                sys.stdout.write(f'\rDownloaded {downloaded_size} of {total_size} bytes ({progress:.2f}%) at {speed/1024:.2f} KB/s\r')
+                sys.stdout.flush()
+            print()
+
+def get_url(url: str, sent_headers: dict = {}, reader: bool = True):
+	'''
+	Retrieve a URL with custom headers.
+	
+	:param url: The URL to retrieve
+	:param data: The headers to send
+	:param reader: Return the reader object instead of the decoded data
+	'''
+	req = urllib.request.Request(url, headers=sent_headers)
+	if reader:
+		return urllib.request.urlopen(req, timeout=10).read().decode()
+	else:
+		return urllib.request.urlopen(req, timeout=10)
+      
+def setup_user_agent(user_agent: str = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'):
+	'''
+	Set up urllib.request user agent.
+	
+	:param user_agent: The user agent to use
+	'''
+	handler = urllib.request.HTTPHandler()
+	opener = urllib.request.build_opener(handler)
+	opener.addheaders = [('User-agent', user_agent)]
+	urllib.request.install_opener(opener)
+
+
+# -------------------------------------------------------------------------------- #
+
+def asn_seach(query: str):
+    '''
+    Search for an ASN by string.
+      
+    :param query: The string to search
+    '''
+    return json.loads(get_url('https://api.bgpview.io/search?query_term='+query))
+
+def cve_search(query: str, limit: str = '25'):
+	'''
+    Search for a CVE by string.
+    
+    :param query: The string to search
+    :param limit: The number of results to return
+    '''
+	return json.loads(get_url(f'https://services.nvd.nist.gov/rest/json/cves/2.0?keywordSearch={query}&resultsPerPage={limit}'))
+
+def geoip(ip: str):
+	'''
+	Get the geolocation of an IP address.
+	
+	:param ip: The IP address to geolocate
+	'''
+	return json.loads(get_url('https://api.ipapi.is/?q='+ip))
+
+def github(option: str, query: str):
+    '''
+    Search for a GitHub repository or user.
+    
+    :param option: The option to search for (search, repo, user)
+    :param query: The query to search
+    '''
+    header = {'Accept': 'application/vnd.github.v3+json'}
+    if option == 'search':
+        url = 'https://api.github.com/search/repositories?q=' + query
+        data = json.loads(get_url(url, header))  # Changed this line
+        return data['items'] if data['items'] else False
+    elif option == 'repo':
+        url = 'https://api.github.com/repos/' + query
+        return json.loads(get_url(url, header))  # And this one
+    elif option == 'user':
+        url = 'https://api.github.com/users/' + query
+        return json.loads(get_url(url, header))  # And this one
+
+def librex(query: str):
+	'''
+	Search on the SuperNETs running LibreX.
+	
+	:param query: The query to search
+	'''
+	return json.loads(get_url(f'https://librex.supernets.org/api.php?q={query}&t=0'))
+
+def reddit(option, subreddit, id=None):
+	'''
+	Search for a Reddit post or subreddit.
+	
+	:param option: The option to search for (post, subreddit)
+	:param subreddit: The subreddit to search
+	:param id: The post ID to search
+    '''
+	header = {'Accept':'application/json'}
+	if option == 'post':
+		data = json.loads(get_url('https://reddit.com', f'/r/{subreddit}/comments/{id}.json', header))
+		return data[0]['data']['children'][0]['data'] if 'error' not in data else False
+	elif option == 'subreddit':
+		data = json.loads(get_url('https://reddit.com', f'/r/{subreddit}.json?limit=20', header))
+		posts = [item['data'] for item in data['data']['children'] if not item['data']['stickied']]
+		return posts if posts else None
+	
+def exploitdb(query: str):
+	'''
+	Search for an exploit or shellcode on ExploitDB.
+	
+	:param query: The query to search
+	'''
+	exploits = get_url('https://git.supernets.org/mirrors/exploitdb/raw/branch/main/files_exploits.csv')
+	shellcodes = get_url('https://git.supernets.org/mirrors/exploitdb/raw/branch/main/files_shellcodes.csv')
+	results = []
+	for database in (exploits, shellcodes):
+		reader = csv.DictReader(io.StringIO(database))
+		results += [row for row in reader if query.lower() in row['description'].lower()]
+	return results
+\ No newline at end of file
diff --git a/bots/hateserv/hateserv.py b/bots/hateserv/hateserv.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+# hateserv irc bot - developed by acidvegas in python (https://git.acid.vegas/hateserv)
+
+import re
+import socket
+import ssl
+import time
+import urllib.request
+
+import commands
+
+# Config
+admin             = 'acidvegas!~stillfree@most.dangerous.motherfuck'
+server            = 'irc.supernets.org'
+channel           = '#dev'
+nickname          = '[dev]HateServ'
+username          = 'H'
+realname          = 'SuperNETs HATE Services'
+nickserv_password = 'simps0nsfan22'
+operator_password = 'EatMYsh0rts39'
+
+# Colors & Control Characters
+bold        = '\x02'
+underline   = '\x1F'
+reset       = '\x0f'
+white       = '00'
+black       = '01'
+blue        = '02'
+green       = '03'
+red         = '04'
+brown       = '05'
+purple      = '06'
+orange      = '07'
+yellow      = '08'
+light_green = '09'
+cyan        = '10'
+light_cyan  = '11'
+light_blue  = '12'
+pink        = '13'
+grey        = '14'
+light_grey  = '15'
+
+def color(msg, foreground, background=None):
+    return f'\x03{foreground},{background}{msg}{reset}' if background else f'\x03{foreground}{msg}{reset}'
+
+def debug(data):
+	print('{0} | [~] - {1}'.format(time.strftime('%I:%M:%S'), data))
+
+def error(data, reason=None):
+	print('{0} | [!] - {1} ({2})'.format(time.strftime('%I:%M:%S'), data, str(reason))) if reason else print('{0} | [!] - {1}'.format(time.strftime('%I:%M:%S'), data))
+
+def irc_error(chan, data, reason=None):
+	sendmsg(chan, '[{0}] {1}'.format(color('error', red), data, color(f'({reason})', grey))) if reason else sendmsg(chan, '[{0}] {1}'.format(color('error', red), data))
+
+def raw(msg):
+	msg = msg.replace('\r\n',' ')
+	sock.send(bytes(msg[:510] + '\r\n', 'utf-8'))
+
+def sendmsg(target, msg):
+	raw(f'PRIVMSG {target} :{msg}')
+
+def trim(data, max_length):
+	return data[:max_length] + '...' if len(data) > max_length else data
+
+def urlcheck(msg):
+	url = re.compile('(?:http[s]?:\/\/|http[s]?:\/\/www.)(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', re.IGNORECASE).findall(msg)
+	if url:
+		url = url[0]
+		try:
+			if (check := re.match('^.*?github.com\/([0-9A-Za-z]+\/[0-9A-Za-z]+).*?', url, re.IGNORECASE)):
+				data = commands.github('repo', check.group(1))
+				if data:
+					if not data['description']:
+						data['description'] = 'no description available'
+					sendmsg(channel, '{0} {1} {2} [{3}:{4}|{5}:{6}|{7}:{8}]'.format(color(' GitHub ', black, grey), data['full_name'], color('('+data['description']+')', grey), color('Stars', purple), data['stargazers_count'], color('Watch', purple), data['watchers'], color('Forks', purple), data['forks']))
+			elif (check := re.match('^.*?github.com\/([0-9A-Za-z]+)', url, re.IGNORECASE)):
+				data = commands.github('user', check.group(1))
+				if data:
+					data['bio'] = data['bio'].replace('\r\n','') if data['bio'] else ''
+					sendmsg(channel, '{0} {1} {2} {3} [{4}:{5}|{6}:{7}]'.format(color(' GitHub ', black, grey), data['login'], color('('+data['name']+')', grey), data['bio'], color('Repos', purple), data['public_repos'], color('Followers', purple), data['followers']))
+			elif (check := re.match('^.*?reddit.com\/r\/(.*?)\/comments\/([0-9A-Za-z]+).*$', url, re.IGNORECASE)):
+				data = commands.reddit('post', check.group(1), check.group(2))
+				sendmsg(channel, '[{0}] - {1} [{2}/{3}|{4}]'.format(color('reddit', cyan), color(trim(data['title'], 300), white), color('+' + str(data['ups']), green), color('-' + str(data['downs']), red), color(str(data['num_comments']), white)))
+			elif (check := re.match('^.*?youtu(be)?\.([a-z])+\/(watch(.*?)(\?|\&)v=)?(.*?)(&(.)*)*$', url, re.IGNORECASE)):
+				pass
+			else:
+				source = urllib.request.urlopen(url, timeout=10)
+				title = re.compile(r'<title.*?>(.+?)</title>', re.I | re.M | re.S | re.U).search(source.read().decode('utf-8'))
+				if title:
+					title = title.group(1).replace('\n',' ')
+					if len(title) > 100:
+						title = title[:100] + '...'
+					type = source.info().get_content_type()
+					sendmsg(channel, f'[{type}] {title}')
+		except Exception as ex:
+			error('failed to get parse url title', ex)
+
+def event_message(chan, nick, ident, msg):
+	args = msg.split()
+	if not msg.startswith('.'):
+		urlcheck(msg)
+		if msg == '@hateserv':
+			sendmsg(channel, 'hateserv irc bot for supernets - developed by acidvegas in python (https://git.acid.vegas/hateserv)')
+		elif msg in ('melp','.melp','melp?','.melp?'):
+			sendmsg(chan, '\x01ACTION explodes\x01')
+	else:
+		if ident == admin:
+			if msg == '.massjoin':
+				raw('WHO * n%nc')
+		if args[0] in ('.g','.s'):
+			query   = ' '.join(args[1:])
+			results = commands.librex(query)
+			if results:
+				for item in results:
+					sendmsg(chan, '[{0}] {1}'.format(color(str(results.index(item)+1).zfill(2), pink), trim(item['title'], 300)))
+					sendmsg(chan, ' '*5 + underline + color(item['link'], light_blue))
+			else:
+				irc_error(chan, 'no results found')
+		elif args[0] == '.cve':
+			data = commands.cve_search(' '.join(args[1:]))
+			for item in data['vulnerabilities']:
+				id = item['cve']['id']
+				desc = item['cve']['descriptions'][0]['value']
+				sendmsg(chan, '[{0}] {1} - {2}'.format(color(str(data['vulnerabilities'].index(item)+1).zfill(2), pink), color(id, cyan), trim(desc, 300)))
+		elif args[0] == '.ip':
+			data = commands.geoip(args[1])
+			location = '{0}, {1}, {2}'.format(data['location']['city'], data['location']['state'], data['location']['country_code'])
+			asn = 'AS{0} ({1})'.format(data['asn']['asn'], data['asn']['descr'])
+			sendmsg(chan, '[{0}] {1} under {2} controlled by {3}'.format(color('geoip', light_blue), color(location, yellow), color(asn, cyan), color(data['rir'], pink)))
+		elif args[0] == '.gh':
+			query = ' '.join(args[1:]).replace(' ','%20')
+			results = commands.github('search',query)
+			if results:
+				for item in results:
+					if not item['description']:
+						item['description'] = 'no description'
+					sendmsg(chan, '[{0}] {1}/{2}{3}{4} {5}'.format(color(str(results.index(item)+1).zfill(2), pink), item['owner']['login'], bold, item['name'], reset, color('('+item['description']+')', grey)))
+					sendmsg(chan, ' '*5 + underline + color(item['html_url'], light_blue))
+		elif args[0] == '.r' and len(args) == 2:
+			query   = args[1]
+			results = commands.reddit('subreddit', query)
+			if results:
+				for item in results:
+					sendmsg(chan, '[{0}] {1} [{2}/{3}|{4}]'.format(color(str(results.index(item)+1).zfill(2), pink), trim(item['title'], 300), color('+' + str(item['ups']), green), color('-' + str(item['downs']), red), color(item['num_comments'], white)))
+					sendmsg(chan, ' '*5 + underline + color(item['url'], light_blue))
+			else:
+				irc_error(chan, 'no results found')
+
+while True:
+	#try:
+		#sock = ssl.wrap_socket(socket.socket())
+		sock = socket.socket()
+		sock.connect((server, 6667))
+		raw(f'USER {username} 0 * :{realname}')
+		raw('NICK ' + nickname)
+		while True:
+			try:
+				data = sock.recv(1024).decode('utf-8')
+				for line in (line for line in data.split('\r\n') if len(line.split()) >= 2):
+					debug(line)
+					args = line.split()
+					if line.startswith('ERROR :Closing Link:'):
+						raise Exception('Connection has closed.')
+					elif args[0] == 'PING':
+						raw('PONG ' + args[1][1:])
+					elif args[1] == '001': #RPL_WELCOME
+						raw(f'MODE {nickname} +B')
+						raw(f'PRIVMSG NickServ IDENTIFY {nickname} {nickserv_password}')
+						raw(f'OPER hates {operator_password}')
+						raw('JOIN ' + channel)
+						last = 5
+					elif args[1] == '354' and len(args) == 5: #RPL_WHOSPCRPL
+						nick = args[4]
+						if nick not in (nickname,'AI','BLACKHOLE','BotServ','ChanServ','EliManning','fraud','Global','HostServ','IRCCEX','NickServ','OperServ','THEGAME'):
+							raw(f'SAJOIN {nick} {channel}')
+					elif args[1] == 'JOIN' and len(args) == 3:
+						nick = args[0].split('!')[0][1:]
+						chan = args[2][1:]
+					elif args[1] == 'PRIVMSG' and len(args) >= 4:
+						ident  = args[0][1:]
+						chan   = args[2]
+						nick   = args[0].split('!')[0][1:].lower()
+						msg    = ' '.join(args[3:])[1:]
+						if chan == channel:
+							#try:
+								event_message(chan, nick, ident, msg)
+							#except Exception as ex:
+							#	irc_error(chan, 'unknown error occured', ex)
+						elif chan == nickname and ident == admin and msg.startswith('.raw '):
+							raw(msg[5:])
+			except (UnicodeDecodeError, UnicodeEncodeError):
+				pass
+	#except Exception as ex:
+	#	error('fatal error occured', ex)
+	#	sock.close()
+	#finally:
+	#	time.sleep(15)
+\ No newline at end of file
diff --git a/deploy/httpd b/deploy/httpd
@@ -0,0 +1,59 @@
+#!/bin/sh
+# SuperNETs tool for nginx deployment - Developed by acidvegas (https://git.acid.vegas/supertools)
+HUB="changeme"
+USERNAME=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
+PASSWORD=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
+PORT="changeme"
+
+setup_home() {
+	mkdir $HOME/www
+	wget -O $HOME/www/badwords.conf   https://raw.githubusercontent.com/supernets/unrealircd/master/doc/conf/badwords.conf
+	wget -O $HOME/www/except.conf     https://raw.githubusercontent.com/supernets/unrealircd/master/doc/conf/except.conf
+	wget -O $HOME/www/ircd.conf       https://raw.githubusercontent.com/supernets/unrealircd/master/doc/conf/unrealircd.remote.conf # edit this
+	wget -O $HOME/www/modules.conf    https://raw.githubusercontent.com/supernets/unrealircd/master/doc/conf/modules.conf
+	wget -O $HOME/www/opers.conf      https://raw.githubusercontent.com/supernets/unrealircd/master/doc/conf/opers.conf             # edit this
+	wget -O $HOME/www/spamfilter.conf https://raw.githubusercontent.com/supernets/unrealircd/master/doc/conf/spamfilter.conf
+	chown -R acidvegas:acidvegas $HOME/www && chmod -R 755 $HOME/www
+}
+
+setup_nginx() {
+	}
+		echo "http {"
+		echo "\taccess_log off;"
+		echo "\tsendfile on;"
+		echo "\tsendfile_max_chunk 1m;"
+		echo "\tserver_tokens off;"
+		echo "\tserver {"
+		echo "\t\tlisten [::]:$PORT ssl http2;"
+		echo "\t\tlisten $PORT ssl http2;"
+		echo "\t\troot /home/acidvegas/www;"
+		echo "\t\tserver_name $HUB;"
+		echo "\t\tcharset UTF-8;"
+		echo "\t\tsatisfy all;"
+		echo "\t\tallow 1.1.1.1; # link 1"
+		echo "\t\tallow 1.1.1.2; # link 2"
+		echo "\t\tallow 1.1.1.3; # link 3"
+		echo "\t\tdeny all;"
+		echo "\t\tauth_basic \"Restricted\";"
+		echo "\t\tauth_basic_user_file /etc/nginx/.htpasswd;"
+		echo "\t\tssl_certificate /etc/letsencrypt/live/$HUB/fullchain.pem;"
+		echo "\t\tssl_certificate_key /etc/letsencrypt/live/$HUB/privkey.pem;"
+		echo "\t\tssl_session_tickets off;"
+		echo "\t}"
+		echo "}"
+	} > /etc/nginx/nginx.conf
+	echo "$USERNAME:$(openssl passwd -apr1 $PASSWORD)\n" > /etc/nginx/.htpasswd
+	systemctl enable nginx && systemctl start nginx
+}
+
+setup_certbot() {
+	certbot certonly --standalone -d $HUB -m admin@supernets.org # fix this
+	echo -e "[Unit]\nDescription=cerbot renewal\n\n[Service]\nType=oneshot\nExecStart=/usr/bin/certbot renew -n --quiet --agree-tos --deploy-hook systemctl restart nginx" > /etc/systemd/system/certbot.service
+	echo -e "[Unit]\nDescription=cerbot renewal timer\n\n[Timer]\nOnCalendar=0/12:00:00\nRandomizedDelaySec=1h\nPersistent=true\n\n[Install]\nWantedBy=timers.target" > /etc/systemd/system/certbot.timer
+	systemctl enable certbot.timer && systemctl start certbot.timer
+}
+
+setup_home
+setup_nginx
+setup_certbot
+echo "Remote include: https://$USERNAME:$PASSWORD@$HUB:$PORT/"
+\ No newline at end of file
diff --git a/deploy/ircd b/deploy/ircd
@@ -0,0 +1,78 @@
+#!/bin/sh
+# SuperNETs tool for UnrealIRCd deployment - Developed by acidvegas (https://git.acid.vegas/supertools)
+# debian deployment: apt-get install build-essential pkg-config gdb libssl-dev libpcre2-dev libargon2-0-dev libsodium-dev libc-ares-dev libcurl4-openssl-dev
+
+UNREAL=$HOME/unrealircd
+SOURCE=$UNREAL.source
+
+for pkg in curl git jq make; do
+    if ! command -v $pkg > /dev/null; then
+        echo "error: missing required package '$pkg'"
+        exit 1
+    fi
+done
+
+case "$1" in
+	check)
+		[ ! $(command -v   jq) ] && echo "error: missing required package 'jq'"   && exit 1
+		CURRENT=$($UNREAL/unrealircd version | cut -d'-' -f2)
+		LATEST=$(curl -s https://www.unrealircd.org/downloads/list.json | jq '[.[]][1].Stable.version')
+		[ ! $CURRENT = $LATEST ] && echo "new version available: $LATEST"
+		;;
+
+	distcert)
+		for link in cowboy contra omega omni phish; do # Make this an arguement instead of hardcoded
+    		scp irc.* $link:unrealircd/conf/tls
+    		ssh $1 unrealircd/unrealircd rehash && unrealircd/unrealircd reloadtls
+		done
+		;;
+
+	deploy)
+		git clone --depth 1 https://github.com/supernets/unrealircd.git $SOURCE
+		cd $SOURCE && echo -e "\n" | ./Config -nointro && make && make install && cd $HOME && rm -rf $SOURCE
+		rm $UNREAL/conf/*.conf
+		read -p "Link Name: " NAME
+		SID=$(cat /dev/urandom | tr -dc '0-9' | fold -w 256 | head -n 1 | head --bytes 1)$(cat /dev/urandom | tr -dc 'A-Z0-9' | fold -w 2 | head -n 1)
+		read -p "Remote Include: " REMOTE
+		for item in badwords except ircd modules opers snomasks spamfilter; do echo "include \"$REMOTE/$item.conf\";" >> $UNREAL/conf/unrealircd.conf; done
+		echo "me { name \"$NAME.supernets.org\"; info \"SuperNETs IRC Network\"; sid $SID; }" >> $UNREAL/conf/unrealircd.conf
+		$UNREAL/unrealircd start &
+		if [ $(command -v crontab) ]; then
+			crontab -l | { cat; echo "*/5 * * * * $HOME/unrealircd/unrealircd croncheck"; } | crontab -
+			crontab -l | { cat; echo "@reboot     $HOME/unrealircd/unrealircd croncheck"; } | crontab -
+		elif [ $(command -v systemctl) ]; then
+			echo -e "[Unit]\nDescription=UnrealIRCd Cron Check Timer\n\n[Timer]\nOnBootSec=1min\nOnUnitActiveSec=5min\n\n[Install]\nWantedBy=timers.target" > $HOME/.config/systemd/user/unreal.timer
+			echo -e "[Unit]\nDescription=UnrealIRCd Cron Check Service\n\n[Service]\nType=oneshot\nExecStart=$HOME/unrealircd/unrealircd croncheck"         > $HOME/.config/systemd/user/unreal.service
+			systemctl --user enable unreal.timer && systemctl --user start unreal.timer
+		else
+			echo "warning: cron/systemd not found on system! (reboot/restart timers not set)"
+		fi
+		$UNREAL/unrealircd spkifp | tail -n2 | head -1
+		curl -4 icanhazip.com && curl -6 icanhazip.com
+		;;
+
+	source)
+		wget -O $SOURCE.tar.gz https://www.unrealircd.org/downloads/unrealircd-latest.tar.gz
+		tar -xvf $SOURCE.tar.gz --one-top-level --strip-components=1 && rm $SOURCE.tar.gz
+		sed -i 's/NICKNAMEHISTORYLENGTH="2000"/NICKNAMEHISTORYLENGTH="100"/g' $SOURCE/Config
+		sed -i 's/REMOTEINC=""/REMOTEINC="1"/g' $SOURCE/Config
+		sed -i 's/*.default.conf/*.conf/g'  $SOURCE/Makefile.in
+		sed -i 's/*.optional.conf/*.motd/g' $SOURCE/Makefile.in
+		sed -i '/modules.sources.list/,/doc\/conf\/example/d' $SOURCE/Makefile.in
+		sed -i 's/sendnotice(target, "\*\*\* You were forced to join %s", jbuf);//g' $SOURCE/src/modules/sajoin.c
+		sed -i 's/0.organizationName_default      = IRC geeks/0.organizationName_default      = SuperNETs/g' $SOURCE/extras/tls.cnf
+		sed -i 's;//#undef FAKELAG_CONFIGURABLE;#define FAKELAG_CONFIGURABLE;g' $SOURCE/include/config.h
+		rm $SOURCE/doc/conf/* && rm $SOURCE/doc/conf/aliases && rm $SOURCE/doc/conf/examples && rm $SOURCE/doc/conf/help
+		cp $HOME/dev/git/supernets/unrealircd/doc/conf/* $SOURCE/doc/conf/
+		;;
+
+	update)
+		BACKUP=$UNREAL.backup
+		mkdir $BACKUP && cp $UNREAL/conf/unrealircd.conf $BACKUP && cp $UNREAL/conf/tls/*.pem $BACKUP && cp $UNREAL/data/*.db $BACKUP
+		git clone --depth 1 https://github.com/supernets/unrealircd.git $SOURCE
+		$UNREAL/unrealircd stop && rm -rf $UNREAL
+		cd $SOURCE && (echo -e "\n" | ./Config -nointro) && make && make install && cd $HOME && rm -rf $SOURCE
+		rm $UNREAL/conf/*.conf && mv $BACKUP/unrealircd.conf $UNREAL/conf && mv $BACKUP/*.pem $UNREAL/conf/tls && mv $BACKUP/*.db $UNREAL/data && rm -r $BACKUP
+		$UNREAL/unrealircd start &
+		;;
+esac
diff --git a/deploy/jitsi b/deploy/jitsi
@@ -0,0 +1,11 @@
+#!/bin/sh
+# https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-quickstart/
+sudo curl -sL https://prosody.im/files/prosody-debian-packages.key -o /etc/apt/keyrings/prosody-debian-packages.key
+echo "deb [signed-by=/etc/apt/keyrings/prosody-debian-packages.key] http://packages.prosody.im/debian $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/prosody-debian-packages.list
+sudo apt install lua5.2
+
+curl -sL https://download.jitsi.org/jitsi-key.gpg.key | sudo sh -c 'gpg --dearmor > /usr/share/keyrings/jitsi-keyring.gpg'
+echo "deb [signed-by=/usr/share/keyrings/jitsi-keyring.gpg] https://download.jitsi.org stable/" | sudo tee /etc/apt/sources.list.d/jitsi-stable.list
+
+sudo apt update
+sudo apt install jitsi-meet
diff --git a/deploy/services b/deploy/services
@@ -0,0 +1,58 @@
+#!/bin/sh
+# SuperNETs tool for Anope services - Developed by acidvegas (https://git.acid.vegas/supertools)
+# requires cmake
+
+ANOPE=$HOME/services
+SOURCE=$HOME/services.source
+
+for pkg in curl git jq make; do
+    if ! command -v $pkg > /dev/null; then
+        echo "error: missing required package '$pkg'"
+        exit 1
+    fi
+done
+
+case "$1" in
+    check)
+        CURRENT=$($ANOPE/bin/services -v | cut -d' ' -f1 | cut -d'-' -f2)
+        LATEST=$(curl -s https://api.github.com/repos/anope/anope/releases/latest | jq -r '.tag_name')
+        if [ "$CURRENT" != "$LATEST" ]; then
+            echo "new version available: $LATEST"
+        fi
+        ;;
+
+    deploy)
+        git clone --depth 1 https://github.com/supernets/anope.git "$SOURCE"
+        cd "$SOURCE" && ./Config -nointro -quick && cd build && make && make install && cd $HOME && rm -rf "$SOURCE"
+        if command -v crontab > /dev/null; then
+            (crontab -l; echo "*/5 * * * * $HOME/services/data/services.chk >/dev/null 2>&1") | crontab -
+            (crontab -l; echo "@reboot     $HOME/services/bin/services") | crontab -
+        elif command -v systemctl > /dev/null; then
+            printf "[Unit]\nDescription=Anope Check Timer\n\n[Timer]\nOnBootSec=1min\nOnUnitActiveSec=5min\n\n[Install]\nWantedBy=timers.target" > "$HOME/.config/systemd/user/anope.timer"
+            printf "[Unit]\nDescription=Anope Check Service\n\n[Service]\nType=oneshot\nExecStart=$HOME/services/data/services.chk >/dev/null 2>&1" > "$HOME/.config/systemd/user/anope.service"
+			systemctl --user enable anope.timer && systemctl --user start anope.timer
+        else
+            echo "warning: cron/systemd not found on system! (reboot/restart timers not set)"
+        fi
+        for param in host port password seed; do
+            read -p "$param = " VALUE
+            sed -i "s/$param = \"REDACTED\"/$param = \"$VALUE\"/g" "$ANOPE/conf/services.conf"
+        done
+        $ANOPE/bin/services
+        ;;
+
+    update)
+        BACKUP="$ANOPE.backup"
+        mkdir "$BACKUP" && cp "$ANOPE/conf/services.conf" "$BACKUP" && cp "$ANOPE/data/anope.db" "$BACKUP"
+        pkill -9 services && rm -rf "$ANOPE"
+        git clone --depth 1 https://github.com/supernets/anope.git "$SOURCE"
+        cd "$SOURCE" && ./Config -nointro -quick && cd build && make && make install && cd $HOME && rm -rf "$SOURCE"
+        mv "$BACKUP/services.conf" "$ANOPE/conf/"
+        mv "$BACKUP/anope.db" "$ANOPE/data"
+        $ANOPE/bin/services
+        ;;
+
+    *)
+        echo "Usage: $0 {check|deploy|update}"
+        ;;
+esac
diff --git a/deploy/tor_ircd_helper b/deploy/tor_ircd_helper
@@ -0,0 +1,14 @@
+#!/bin/sh
+apt-get install tor
+{
+	echo "HiddenServiceDir /var/lib/tor/ircd"
+	echo "HiddenServicePort 6667 unix:/etc/tor/unrealircd/tor_ircd.socket"
+	echo "HiddenServicePort 6697 unix:/etc/tor/unrealircd/tor_tls_ircd.socket"
+	echo "#MapAddress irc.supernets.org changeme.onion"
+} > /etc/tor/torrc
+mkdir /etc/tor/unrealircd
+chown unrealircd:debian-tor /etc/tor/unrealircd
+chmod 750 /etc/tor/unrealircd
+systemctl restart tor.service && systemctl enable tor.service
+cat /var/lib/tor/ircd/hostname
+echo "MapAddress irc1.example.net xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion" >> /etc/tor/torrc
diff --git a/help/altdns.py b/help/altdns.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+import socket
+
+dns = (
+	'irc.hardchats.net',
+	'irc.ngr.bz',
+	'irc.wepump.in',
+	'irc.autist.life'
+)
+
+servers = set([i[4][0] for i in socket.getaddrinfo('irc.supernets.org', 6667)])
+for hostname in dns:
+	try:
+		if socket.gethostbyname(hostname) in servers:
+			print('GOOD\t' + hostname)
+		else:
+			print('BAD \t' + hostname)
+	except:
+		print('BAD \t' + hostname)
+\ No newline at end of file
diff --git a/help/irclxc b/help/irclxc
@@ -0,0 +1,138 @@
+#!/bin/sh
+# LXC Container Setup - developed by acidvegas (https://git.acid.vegas/supertools)
+
+# Configuration
+SSH_PORT=1337
+USER_NAME="supernets"
+CONTAINER_NAME="ircd"
+
+setup_root() {
+	# Secure DNS (TEMP)
+	printf "nameserver 208.67.222.222\nnameserver 208.67.220.220\nnameserver 2620:119:35::35\nnameserver 2620:119:53::53\n" > /etc/resolv.conf
+	chattr +i /etc/resolv.conf
+
+	# Update & Install Packages
+	apt-get update && apt-get upgrade
+	apt-get install bridge-utils dirmngr htop gpg lxc man net-tools uidmap screen unattended-upgrades
+
+	# Wipe the journal and only use RAM storage
+	journalctl --vacuum-time=1d
+	printf "[Journal]\nStorage=volatile\nSplitMode=none\nRuntimeMaxUse=500K\n" > /etc/systemd/journald.conf
+	systemctl restart systemd-journald
+
+	# Install & setup dropbear
+	apt-get install -y dropbear
+	{
+		echo "NO_START=0"
+		echo "DROPBEAR_PORT=$SSH_PORT"
+		echo "DROPBEAR_EXTRA_ARGS=\"-K 0\""
+		echo "DROPBEAR_BANNER=\"\""
+		echo "DROPBEAR_ED25519KEY=\"/etc/dropbear/dropbear_ed25519_host_key\""
+		echo "DROPBEAR_RECEIVE_WINDOW=65536"
+	} > /etc/default/dropbear
+	systemctl restart dropbear && systemctl enable dropbear
+
+	# Remove OpenSSH
+	apt remove openssh-server && apt remove openssh-client
+	apt purge openssh-server && apt purge openssh-client
+	apt autoremove && apt autoclean
+	systemctl stop ssh && systemctl disable ssh
+
+	# Disable history, logs, & IPv6
+	printf "\nHISTSIZE=0\nHISTFILESIZE=0\nunset HISTFILE\n" >> /etc/bash.bashrc
+	>/var/log/lastlog && chattr +i /var/log/lastlog
+	sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="ipv6.disable=1"/' /etc/default/grub && update-grub
+
+	# Set locales
+	echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen
+
+	# Add a new user
+	useradd -m -s /bin/bash $USER_NAME && passwd $USER_NAME
+
+	# Change hostname
+	nano /etc/hostname
+
+	# Enable user-level services
+	loginctl enable-linger $USER_NAME
+
+	# Configure NAT
+	iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
+	echo "1" > /proc/sys/net/ipv4/ip_forward
+	printf "\nnet.ipv4.ip_forward=1\n" > /etc/sysctl.conf
+
+	# Create a runtime directory with the correct permissions
+	mkdir -p /run/user/$(id -u $USER_NAME)
+    chown $USER_NAME:$USER_NAME /run/user/$(id -u $USER_NAME)
+    chmod 700 /run/user/$(id -u $USER_NAME)
+
+	# Set the subordinate UID/GID
+    echo "$USER_NAME:100000:65536" > /etc/subuid
+    echo "$USER_NAME:100000:65536" > /etc/subgid
+
+	# Create bridge (usually done automatically, see `ip addr` output for lxcbr0)
+	#brctl addbr lxcbr0
+	#ip addr add 192.168.1.10/24 dev lxcbr0
+	#ip link set dev lxcbr0 up
+
+	# Restart the LXC service
+	systemctl restart lxc
+}
+
+setup_user() {
+	# Add dropbear public key
+	mkdir -p $HOME/.ssh
+	printf "ssh-ed25519 loldongs acidvegas@blackhole" > $HOME/.ssh/authorized_keys
+	chmod 700 $HOME/.ssh
+	chown -R $USER $HOME/.ssh
+	chmod 400 $HOME/.ssh/authorized_keys
+	chattr +i $HOME/.ssh
+	chattr +i $HOME/.ssh/authorized_keys
+
+	# Setup LXC configuration
+	mkdir -p ~/.config/lxc
+	{
+		echo "lxc.idmap = u 0 100000 65536"
+		echo "lxc.idmap = g 0 100000 65536"
+		echo "lxc.net.0.type = veth"
+		echo "lxc.net.0.link = lxcbr0"
+		echo "lxc.net.0.flags = up"
+		echo "lxc.start.auto = 1"
+		echo "lxc.start.delay = 5"
+	} > $HOME/.config/lxc/default.conf
+
+	# Setup runtime directory
+	echo 'export XDG_RUNTIME_DIR=/run/user/$(id -u $USER)' >> ~/.bashrc
+	export XDG_RUNTIME_DIR=/run/user/$(id -u $USER)
+
+	# Create a systemd user service
+	mkdir -p $HOME/.config/systemd/user
+	{
+		echo "[Unit]"
+		echo "Description=LXC Container %I"
+		echo "After=network.target"
+		echo ""
+		echo "[Service]"
+		echo "Type=forking"
+		echo "ExecStart=/usr/bin/lxc-start -n %i"
+		echo "ExecStop=/usr/bin/lxc-stop -n %i"
+		echo "Restart=on-failure"
+		echo ""
+		echo "[Install]"
+		echo "WantedBy=default.target"
+	} > $HOME/.config/systemd/user/lxc-container@.service
+
+	# Create a container
+	lxc-create -n $container -t download -- --dist debian --release bullseye --arch amd64
+
+	# Start & enable the service
+	systemctl --user enable lxc-container@${container}.service
+	systemctl --user start  lxc-container@${container}.service
+}
+
+setup_container() {
+	# TODO: Provision container for services
+	return
+}
+
+#setup_root
+#setup_user
diff --git a/help/ircwall b/help/ircwall
@@ -0,0 +1,70 @@
+#!/bin/sh
+# IRCd Firewall - Developed by acidvegas (https://git.acid.vegas/supertools)
+
+# nano /etc/default/grub
+# Add ipv6.disable=1 to GRUB_CMDLINE_LINUX_DEFAULT then run update-grub
+
+# Configuration
+IP_MAIN="10.0.0.1" # Change this to your IP
+IP_HUB="10.0.0.2"  # Change this to your hub IP
+PORT_SSH=22        # Default 22
+PORT_HUB=5900      # Default 5900
+
+# Kernel hardening settings
+mkdir -p /etc/sysctl.d
+{
+  printf "net.ipv4.conf.all.accept_source_route = 0\n"
+  printf "net.ipv6.conf.all.accept_source_route = 0\n"
+  printf "net.ipv4.conf.all.rp_filter = 1\n"
+  printf "net.ipv4.conf.default.rp_filter = 1\n"
+  printf "net.ipv4.conf.all.accept_redirects = 0\n"
+  printf "net.ipv6.conf.all.accept_redirects = 0\n"
+  printf "net.ipv4.conf.default.accept_redirects = 0\n"
+  printf "net.ipv6.conf.default.accept_redirects = 0\n"
+  printf "net.ipv4.conf.all.log_martians = 1\n"
+  printf "kernel.randomize_va_space = 2\n"
+  printf "fs.suid_dumpable = 0\n"
+} > /etc/sysctl.d/99-custom-hardening.conf
+
+# Apply hardening settings
+sysctl -p /etc/sysctl.d/99-custom-hardening.conf
+
+# Flush existing rules
+iptables -F
+iptables -X
+iptables -t nat -F
+iptables -t nat -X
+iptables -t mangle -F
+iptables -t mangle -X
+
+# Default chain policies
+iptables -P INPUT DROP
+iptables -P FORWARD DROP
+iptables -P OUTPUT ACCEPT
+
+# Common Firewall rules
+iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
+iptables -A INPUT -p icmp --icmp-type echo-request -j DROP
+iptables -A INPUT -i lo -j ACCEPT
+
+# Allow SSH
+iptables -A INPUT -p tcp -s $IP_MAIN --dport $PORT_SSH -j ACCEPT
+
+# Allow IRCd Hub
+iptables -A INPUT -p tcp -s $IP_HUB  --dport $PORT_HUB -j ACCEPT
+
+# Allow IRCd Ports
+iptables -A INPUT -p tcp --dport 6660:6669 -j ACCEPT
+iptables -A INPUT -p tcp --dport 7000 -j ACCEPT
+
+# Allow IRCd TLS Ports
+iptables -A INPUT -p tcp --dport 6697 -j ACCEPT
+iptables -A INPUT -p tcp --dport 9999 -j ACCEPT
+
+# Save rules
+apt-get install -y iptables-persistent
+netfilter-persistent save
+systemctl enable netfilter-persistent && systemctl start netfilter-persistent
+
+# Show rules
+iptables -L -v -n
diff --git a/help/monitor b/help/monitor
@@ -0,0 +1,8 @@
+#!/bin/sh
+while true; do
+	for i in $(dig irc.supernets.org A irc.supernets.org AAAA +short); do
+		ping -c 1 $i 2>&1 >/dev/null
+		[ $? -ne 0 ] && echo "`date`: ping failed! ($i)"
+	done
+	sleep 300
+done
+\ No newline at end of file
diff --git a/help/namecheap.py b/help/namecheap.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+# supernets namecheap api tool - developed by acidvegas in python (https://git.acid.vegas/supertools)
+
+import re
+import requests
+import xml.etree.ElementTree as et
+
+# Config
+username = 'changeme'
+api_key  = 'changeme'
+ip_addr  = 'changeme'
+
+def api(cmd, extra=False):
+	payload = {
+		'ApiKey'   : api_key,
+		'ApiUser'  : username,
+		'UserName' : username,
+		'ClientIP' : ip_addr,
+		'Command'  : cmd
+	}
+	if extra:
+		payload.update(extra)
+	r = requests.post('https://api.namecheap.com/xml.response', params=payload)
+	return r.content
+
+class domains:
+	class dns:
+		def getHosts():
+			data = api('namecheap.domains.dns.getHosts', extra={'TLD': 'supernets','SLD':'org'})
+			for child in et.fromstring(data).findall('.//{http://api.namecheap.com/xml.response}host'):
+				print(child.attrib)
+
+	def setHosts(type, address):
+		payload = {
+			'SLD'        : 'supernets',
+			'TLD'        : 'org',
+			'HostName'   : 'irc',
+			'RecordType' : type,
+			'Address'    : address,
+			'TTL'        : '60'
+		}
+		data = api('namecheap.domains.dns.setHosts', payload)
+
+class ssl:
+	def getInfo(id):
+		'''https://www.namecheap.com/support/api/methods/ssl/get-info/'''
+		data = api('namecheap.ssl.getInfo', extra={'CertificateID':id})
+
+	def getList():
+		'''https://www.namecheap.com/support/api/methods/ssl/get-list/'''
+		data = api('namecheap.ssl.getList')
+
+	def activate(id, csr, mail):
+		'''https://www.namecheap.com/support/api/methods/ssl/activate/'''
+		payload = {
+			'CertificateID': id,
+			'CSR':csr,
+			'AdminEmailAddress':mail
+		}
+		data = api('namecheap.ssl.activate', payload)
+
+	def parseCSR(csr):
+		payload = {
+			'csr': csr,
+			'CertificateType': 'PositiveSSL'
+		}
+		data = api('namecheap.ssl.parseCSR', payload)
+
+	def renew(id):
+		'''https://www.namecheap.com/support/api/methods/ssl/renew/'''
+		payload = {
+			'CertificateID':id,
+			'SSLType': 'PositiveSSL',
+			'years': '1' # or 5
+		}
+		data = api('namecheap.ssl.renew')
diff --git a/help/startbots b/help/startbots
@@ -0,0 +1,9 @@
+#!/bin/sh
+pkill -9 -u $UID abduco
+cd $HOME/bots                   && abduco -fnr 5000         python3 5000.py         && sleep 1
+cd $HOME/bots/amber             && abduco -fnr amber        python3 amber.py        && sleep 1
+cd $HOME/bots/anythinggoes      && abduco -fnr anythinggoes python3 anythinggoes.py && sleep 1
+cd $HOME/bots                   && abduco -fnr cancer       python3 cancer.py       && sleep 1
+cd $HOME/bots/dickserv/dickserv && abduco -fnr dickserv     python3 dickserv.py     && sleep 1
+cd $HOME/bots/irccex/irccex     && abduco -fnr irccex       python3 irccex.py       && sleep 1
+cd $HOME/bots/scroll/scroll     && abduco -fnr scroll       python3 scroll.py
+\ No newline at end of file