dickserv

- irc bot with many useful commands
git clone git://git.acid.vegas/dickserv.git
Log | Files | Refs | Archive | README | LICENSE

commit 11ceb7fba6a96a8e6e308107858fd82eac0941d3
Author: acidvegas <acid.vegas@acid.vegas>
Date: Mon, 24 Jun 2019 22:13:57 -0400

Initial commit

Diffstat:
ALICENSE | 15+++++++++++++++
AREADME.md | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Adickserv/core/commands.py | 17+++++++++++++++++
Adickserv/core/config.py | 42++++++++++++++++++++++++++++++++++++++++++
Adickserv/core/constants.py | 214+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adickserv/core/database.py | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adickserv/core/debug.py | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adickserv/core/functions.py | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adickserv/core/httplib.py | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adickserv/core/irc.py | 479+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adickserv/data/cert/.gitignore | 5+++++
Adickserv/data/logs/.gitignore | 5+++++
Adickserv/dickserv.py | 25+++++++++++++++++++++++++
Adickserv/modules/cryptocurrency.py | 14++++++++++++++
Adickserv/modules/dictionary.py | 22++++++++++++++++++++++
Adickserv/modules/google.py | 13+++++++++++++
Adickserv/modules/imdb.py | 32++++++++++++++++++++++++++++++++
Adickserv/modules/isup.py | 16++++++++++++++++
Adickserv/modules/netsplit.py | 25+++++++++++++++++++++++++
Adickserv/modules/reddit.py | 34++++++++++++++++++++++++++++++++++
Adickserv/modules/tpb.py | 24++++++++++++++++++++++++
Adickserv/modules/tripsit.py | 13+++++++++++++
Adickserv/modules/weather.py | 19+++++++++++++++++++
Adickserv/modules/wolfram.py | 24++++++++++++++++++++++++
Adickserv/modules/youtube.py | 40++++++++++++++++++++++++++++++++++++++++

25 files changed, 1421 insertions(+), 0 deletions(-)

diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2019, acidvegas <acid.vegas@acid.vegas>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/README.md b/README.md
@@ -0,0 +1,51 @@
+###### Requirments
+* [Python](https://www.python.org/downloads/) *(**Note:** This script was developed to be used with the latest version of Python.)*
+* [PySocks](https://pypi.python.org/pypi/PySocks) *(**Optional:** For using the `proxy` setting.)*
+* [beautifulsoup4](https://pypi.python.org/pypi/beautifulsoup4)
+* [google-api-python-client](https://pypi.python.org/pypi/google-api-python-client)
+
+###### Commands
+| Command | Description |
+| --- | --- |
+| @dickserv | Information about the bot. |
+| @dickserv help | Information about the commands. |
+| coin \<cryptocurrency> | Get the USD value for \<cryptocurrency>. |
+| date | Get the current date and time. |
+| define \<word> | Get the definition of \<word>. |
+| drug \<query> | Lookup information on \<drug> on tripsit. |
+| g \<query> | Search Google for \<query>. |
+| imdb \<query/ttid> [year] | Search \<query/ttid> on IMDb. |
+| isup \<url> | Check if \<url> is up or not. |
+| netsplit \<query> | Search for \<query> on NetSplit. |
+| r \<subreddit> | Read top posts from \<subreddit> |
+| talent | RIP DITTLE DIP DIP DIP DIP IT\'S YA BIRTHDAY!!1@11! |
+| todo | Read your todo list. |
+| todo add \<data> | Add \<data> to your todo list. |
+| todo del \<num>| | Delete the \<num> todo result. |
+| tpb \<query> | Searc \<query on ThePirateBay. |
+| ud \<word> | Get the urban dictionary definition of \<word>. |
+| uptime | Get the amount of time DickServ has been running. |
+| w \<zip_code> | Get the weather for \<zip>. |
+| wolfram \<ask> | Get the results of \<query> from WolframAlpha. |
+| yt \<query> | Search \<query> on YouTube. |
+
+###### Admin Commands (Private Message)
+| Command | Description |
+| --- | --- |
+| config | View the config settings. |
+| config \<setting> \<value> | Change \<setting> to \<value>. |
+| ignore | View the ignore list. |
+| ignore add \<ident> | Add a user to the ignore list. |
+| ignore del \<ident> | Remove a user from the ignore list. |
+| ignore reset | Remove all ignores. |
+| off | Toggle the usage of the bot commands. |
+| on | Toggle the usage of the bot commands. |
+| todo | List all todos. |
+| todo expire | Remove all expired todos. |
+| todo reset | Remove all todos. |
+
+###### Mirrors
+- [acid.vegas](https://acid.vegas/dickserv) *(main)*
+- [SuperNETs](https://git.supernets.org/acidvegas/dickserv)
+- [GitHub](https://github.com/acidvegas/dickserv)
+- [GitLab](https://gitlab.com/acidvegas/dickserv)
diff --git a/dickserv/core/commands.py b/dickserv/core/commands.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# commands.py
+
+import cryptocurrency
+import dictionary
+import google
+import imdb
+import isup
+import netsplit
+import reddit
+import tpb
+import tripsit
+import weather
+import wolfram
+import youtube
+\ No newline at end of file
diff --git a/dickserv/core/config.py b/dickserv/core/config.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# config.py
+
+class connection:
+	server     = 'irc.server.com'
+	port       = 6697
+	ipv6       = False
+	ssl        = True
+	ssl_verify = False
+	proxy      = None
+	vhost      = None
+	channel    = '#chats'
+	key        = None
+
+class cert:
+	key      = None
+	file     = None
+	password = None
+
+class ident:
+	nickname = 'DickServ'
+	username = 'dickserv'
+	realname = 'acid.vegas/dickserv'
+
+class login:
+	network  = None
+	nickserv = None
+	operator = None
+
+class settings:
+	admin    = 'user@host.name'
+	cmd_char = '.'
+	log      = False
+	modes    = None
+
+class api:
+	google_api_key       = 'CHANGEME' # https://console.developers.google.com/
+	google_cse_id        = 'CHANGEME' # https://cse.google.com/
+	omdbapi_key          = 'CHANGEME' # http://www.omdbapi.com/apikey.aspx
+	wolfram_api_key      = 'CHANGEME' # http://products.wolframalpha.com/api/
+	wunderground_api_key = 'CHANGEME' # https://www.wunderground.com/weather/api/
diff --git a/dickserv/core/constants.py b/dickserv/core/constants.py
@@ -0,0 +1,213 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# constants.py
+
+# Control Characters
+bold      = '\x02'
+color     = '\x03'
+italic    = '\x1D'
+underline = '\x1F'
+reverse   = '\x16'
+reset     = '\x0f'
+
+# Color Codes
+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'
+
+# Events
+PASS     = 'PASS'
+NICK     = 'NICK'
+USER     = 'USER'
+OPER     = 'OPER'
+MODE     = 'MODE'
+SERVICE  = 'SERVICE'
+QUIT     = 'QUIT'
+SQUIT    = 'SQUIT'
+JOIN     = 'JOIN'
+PART     = 'PART'
+TOPIC    = 'TOPIC'
+NAMES    = 'NAMES'
+LIST     = 'LIST'
+INVITE   = 'INVITE'
+KICK     = 'KICK'
+PRIVMSG  = 'PRIVMSG'
+NOTICE   = 'NOTICE'
+MOTD     = 'MOTD'
+LUSERS   = 'LUSERS'
+VERSION  = 'VERSION'
+STATS    = 'STATS'
+LINKS    = 'LINKS'
+TIME     = 'TIME'
+CONNECT  = 'CONNECT'
+TRACE    = 'TRACE'
+ADMIN    = 'ADMIN'
+INFO     = 'INFO'
+SERVLIST = 'SERVLIST'
+SQUERY   = 'SQUERY'
+WHO      = 'WHO'
+WHOIS    = 'WHOIS'
+WHOWAS   = 'WHOWAS'
+KILL     = 'KILL'
+PING     = 'PING'
+PONG     = 'PONG'
+ERROR    = 'ERROR'
+AWAY     = 'AWAY'
+REHASH   = 'REHASH'
+DIE      = 'DIE'
+RESTART  = 'RESTART'
+SUMMON   = 'SUMMON'
+USERS    = 'USERS'
+WALLOPS  = 'WALLOPS'
+USERHOST = 'USERHOST'
+ISON     = 'ISON'
+
+# Event Numerics
+RPL_WELCOME          = '001'
+RPL_YOURHOST         = '002'
+RPL_CREATED          = '003'
+RPL_MYINFO           = '004'
+RPL_ISUPPORT         = '005'
+RPL_TRACELINK        = '200'
+RPL_TRACECONNECTING  = '201'
+RPL_TRACEHANDSHAKE   = '202'
+RPL_TRACEUNKNOWN     = '203'
+RPL_TRACEOPERATOR    = '204'
+RPL_TRACEUSER        = '205'
+RPL_TRACESERVER      = '206'
+RPL_TRACESERVICE     = '207'
+RPL_TRACENEWTYPE     = '208'
+RPL_TRACECLASS       = '209'
+RPL_STATSLINKINFO    = '211'
+RPL_STATSCOMMANDS    = '212'
+RPL_STATSCLINE       = '213'
+RPL_STATSILINE       = '215'
+RPL_STATSKLINE       = '216'
+RPL_STATSYLINE       = '218'
+RPL_ENDOFSTATS       = '219'
+RPL_UMODEIS          = '221'
+RPL_SERVLIST         = '234'
+RPL_SERVLISTEND      = '235'
+RPL_STATSLLINE       = '241'
+RPL_STATSUPTIME      = '242'
+RPL_STATSOLINE       = '243'
+RPL_STATSHLINE       = '244'
+RPL_LUSERCLIENT      = '251'
+RPL_LUSEROP          = '252'
+RPL_LUSERUNKNOWN     = '253'
+RPL_LUSERCHANNELS    = '254'
+RPL_LUSERME          = '255'
+RPL_ADMINME          = '256'
+RPL_ADMINLOC1        = '257'
+RPL_ADMINLOC2        = '258'
+RPL_ADMINEMAIL       = '259'
+RPL_TRACELOG         = '261'
+RPL_TRYAGAIN         = '263'
+RPL_NONE             = '300'
+RPL_AWAY             = '301'
+RPL_USERHOST         = '302'
+RPL_ISON             = '303'
+RPL_UNAWAY           = '305'
+RPL_NOWAWAY          = '306'
+RPL_WHOISUSER        = '311'
+RPL_WHOISSERVER      = '312'
+RPL_WHOISOPERATOR    = '313'
+RPL_WHOWASUSER       = '314'
+RPL_ENDOFWHO         = '315'
+RPL_WHOISIDLE        = '317'
+RPL_ENDOFWHOIS       = '318'
+RPL_WHOISCHANNELS    = '319'
+RPL_LIST             = '322'
+RPL_LISTEND          = '323'
+RPL_CHANNELMODEIS    = '324'
+RPL_NOTOPIC          = '331'
+RPL_TOPIC            = '332'
+RPL_INVITING         = '341'
+RPL_INVITELIST       = '346'
+RPL_ENDOFINVITELIST  = '347'
+RPL_EXCEPTLIST       = '348'
+RPL_ENDOFEXCEPTLIST  = '349'
+RPL_VERSION          = '351'
+RPL_WHOREPLY         = '352'
+RPL_NAMREPLY         = '353'
+RPL_LINKS            = '364'
+RPL_ENDOFLINKS       = '365'
+RPL_ENDOFNAMES       = '366'
+RPL_BANLIST          = '367'
+RPL_ENDOFBANLIST     = '368'
+RPL_ENDOFWHOWAS      = '369'
+RPL_INFO             = '371'
+RPL_MOTD             = '372'
+RPL_ENDOFINFO        = '374'
+RPL_MOTDSTART        = '375'
+RPL_ENDOFMOTD        = '376'
+RPL_YOUREOPER        = '381'
+RPL_REHASHING        = '382'
+RPL_YOURESERVICE     = '383'
+RPL_TIME             = '391'
+RPL_USERSSTART       = '392'
+RPL_USERS            = '393'
+RPL_ENDOFUSERS       = '394'
+RPL_NOUSERS          = '395'
+ERR_NOSUCHNICK       = '401'
+ERR_NOSUCHSERVER     = '402'
+ERR_NOSUCHCHANNEL    = '403'
+ERR_CANNOTSENDTOCHAN = '404'
+ERR_TOOMANYCHANNELS  = '405'
+ERR_WASNOSUCHNICK    = '406'
+ERR_TOOMANYTARGETS   = '407'
+ERR_NOSUCHSERVICE    = '408'
+ERR_NOORIGIN         = '409'
+ERR_NORECIPIENT      = '411'
+ERR_NOTEXTTOSEND     = '412'
+ERR_NOTOPLEVEL       = '413'
+ERR_WILDTOPLEVEL     = '414'
+ERR_BADMASK          = '415'
+ERR_UNKNOWNCOMMAND   = '421'
+ERR_NOMOTD           = '422'
+ERR_NOADMININFO      = '423'
+ERR_FILEERROR        = '424'
+ERR_NONICKNAMEGIVEN  = '431'
+ERR_ERRONEUSNICKNAME = '432'
+ERR_NICKNAMEINUSE    = '433'
+ERR_NICKCOLLISION    = '436'
+ERR_USERNOTINCHANNEL = '441'
+ERR_NOTONCHANNEL     = '442'
+ERR_USERONCHANNEL    = '443'
+ERR_NOLOGIN          = '444'
+ERR_SUMMONDISABLED   = '445'
+ERR_USERSDISABLED    = '446'
+ERR_NOTREGISTERED    = '451'
+ERR_NEEDMOREPARAMS   = '461'
+ERR_ALREADYREGISTRED = '462'
+ERR_NOPERMFORHOST    = '463'
+ERR_PASSWDMISMATCH   = '464'
+ERR_YOUREBANNEDCREEP = '465'
+ERR_KEYSET           = '467'
+ERR_CHANNELISFULL    = '471'
+ERR_UNKNOWNMODE      = '472'
+ERR_INVITEONLYCHAN   = '473'
+ERR_BANNEDFROMCHAN   = '474'
+ERR_BADCHANNELKEY    = '475'
+ERR_BADCHANMASK      = '476'
+ERR_BANLISTFULL      = '478'
+ERR_NOPRIVILEGES     = '481'
+ERR_CHANOPRIVSNEEDED = '482'
+ERR_CANTKILLSERVER   = '483'
+ERR_UNIQOPRIVSNEEDED = '485'
+ERR_NOOPERHOST       = '491'
+ERR_UMODEUNKNOWNFLAG = '501'
+ERR_USERSDONTMATCH   = '502'
+\ No newline at end of file
diff --git a/dickserv/core/database.py b/dickserv/core/database.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# database.py
+
+import os
+import sqlite3
+
+# Globals
+db  = sqlite3.connect(os.path.join('data', 'bot.db'), check_same_thread=False)
+sql = db.cursor()
+
+def check():
+	tables = sql.execute('SELECT name FROM sqlite_master WHERE type=\'table\'').fetchall()
+	if not len(tables):
+		sql.execute('CREATE TABLE IGNORE (IDENT TEXT NOT NULL);')
+		sql.execute('CREATE TABLE SETTINGS (SETTING TEXT NOT NULL, VALUE INTEGER NOT NULL);')
+		sql.execute('INSERT INTO SETTINGS (SETTING,VALUE) VALUES (?, ?)', ('max_results',  5))
+		sql.execute('INSERT INTO SETTINGS (SETTING,VALUE) VALUES (?, ?)', ('max_todo',   100))
+		sql.execute('INSERT INTO SETTINGS (SETTING,VALUE) VALUES (?, ?)', ('max_todo_per', 5))
+		sql.execute('INSERT INTO SETTINGS (SETTING,VALUE) VALUES (?, ?)', ('todo_expire',  7))
+		sql.execute('CREATE TABLE TODO (DATE TEXT NOT NULL, IDENT TEXT NOT NULL, DATA TEXT NOT NULL);')
+		db.commit()
+
+class Ignore:
+	def add(ident):
+		sql.execute('INSERT INTO IGNORE (IDENT) VALUES (?)', (ident,))
+		db.commit()
+
+	def idents():
+		return list(item[0] for item in sql.execute('SELECT IDENT FROM IGNORE ORDER BY IDENT ASC').fetchall())
+
+	def remove(ident):
+		sql.execute('DELETE FROM IGNORE WHERE IDENT=?', (ident,))
+		db.commit()
+
+	def reset():
+		sql.execute('DROP TABLE IGNORE')
+		sql.execute('CREATE TABLE IGNORE (IDENT TEXT NOT NULL);')
+		db.commit()
+
+class Settings:
+	def get(setting):
+		return sql.execute('SELECT VALUE FROM SETTINGS WHERE SETTING=?', (setting,)).fetchone()[0]
+
+	def read():
+		return sql.execute('SELECT SETTING,VALUE FROM SETTINGS ORDER BY SETTING ASC').fetchall()
+
+	def settings():
+		return list(item[0] for item in sql.execute('SELECT SETTING FROM SETTINGS').fetchall())
+
+	def update(setting, value):
+		sql.execute('UPDATE SETTINGS SET VALUE=? WHERE SETTING=?', (value, setting))
+		db.commit()
+
+class Todo:
+	def add(date, ident, data):
+		sql.execute('INSERT INTO TODO (DATE,IDENT,DATA) VALUES (?,?,?)', (date, ident, data))
+		db.commit()
+
+	def expire_check():
+		todos = set(list(item[0] for item in sql.execute('SELECT DATE FROM TODO').fetchall()))
+		for date in todos:
+			if functions.timespan(date) > Settings.get('todo_expire'):
+				sql.execute('DELETE FROM TODO WHERE DATE=?', (date,))
+				db.commit()
+
+	def idents():
+		return list(item[0] for item in sql.execute('SELECT IDENT FROM TODO').fetchall())
+
+	def read(ident=None):
+		if ident:
+			return list(item[0] for item in sql.execute('SELECT DATA FROM TODO WHERE IDENT=?', (ident,)).fetchall())
+		else:
+			return sql.execute('SELECT DATE,IDENT,DATA FROM TODO ORDER BY DATE ASC, IDENT ASC, DATA ASC').fetchall()
+
+	def remove(ident, data):
+		sql.execute('DELETE FROM TODO WHERE IDENT=? AND DATA=?', (ident, data))
+		db.commit()
+
+	def reset():
+		sql.execute('DROP TABLE TODO')
+		sql.execute('CREATE TABLE TODO (DATE TEXT NOT NULL, IDENT TEXT NOT NULL, DATA TEXT NOT NULL);')
+		db.commit()
+\ No newline at end of file
diff --git a/dickserv/core/debug.py b/dickserv/core/debug.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# debug.py
+
+import ctypes
+import logging
+import os
+import sys
+import time
+
+from logging.handlers import RotatingFileHandler
+
+import config
+
+def check_libs():
+	if config.connection.proxy:
+		try:
+			import socks
+		except ImportError:
+			error_exit('Missing \'socks\' module! (https://pypi.python.org/pypi/PySocks)')
+	try:
+		import bs4
+	except ImportError:
+		error_exit('Missing \'bs4\' module. (https://pypi.python.org/pypi/beautifulsoup4)')
+	try:
+		import googleapiclient.discovery
+	except ImportError:
+		error_exit('Missing \'google-api-python-client\' module. (https://pypi.python.org/pypi/google-api-python-client/)')
+
+
+def check_privileges():
+	if check_windows():
+		if ctypes.windll.shell32.IsUserAnAdmin() != 0:
+			return True
+		else:
+			return False
+	else:
+		if os.getuid() == 0 or os.geteuid() == 0:
+			return True
+		else:
+			return False
+
+def check_version(major):
+	if sys.version_info.major == major:
+		return True
+	else:
+		return False
+
+def check_windows():
+	if os.name == 'nt':
+		return True
+	else:
+		return False
+
+def clear():
+	if check_windows():
+		os.system('cls')
+	else:
+		os.system('clear')
+
+def error(msg, reason=None):
+	if reason:
+		logging.debug(f'[!] - {msg} ({reason})')
+	else:
+		logging.debug('[!] - ' + msg)
+
+def error_exit(msg):
+	raise SystemExit('[!] - ' + msg)
+
+def info():
+	clear()
+	logging.debug('#'*56)
+	logging.debug('#{0}#'.format(''.center(54)))
+	logging.debug('#{0}#'.format('DickServ IRC'.center(54)))
+	logging.debug('#{0}#'.format('Developed by acidvegas in Python'.center(54)))
+	logging.debug('#{0}#'.format('https://acid.vegas/dickserv'.center(54)))
+	logging.debug('#{0}#'.format(''.center(54)))
+	logging.debug('#'*56)
+
+def irc(msg):
+	logging.debug('[~] - ' + msg)
+
+def setup_logger():
+	stream_handler = logging.StreamHandler(sys.stdout)
+	if config.settings.log:
+		log_file	 = os.path.join(os.path.join('data','logs'), 'bot.log')
+		file_handler = RotatingFileHandler(log_file, maxBytes=256000, backupCount=3)
+		logging.basicConfig(level=logging.NOTSET, format='%(asctime)s | %(message)s', datefmt='%I:%M:%S', handlers=(file_handler,stream_handler))
+	else:
+		logging.basicConfig(level=logging.NOTSET, format='%(asctime)s | %(message)s', datefmt='%I:%M:%S', handlers=(stream_handler,))
+\ No newline at end of file
diff --git a/dickserv/core/functions.py b/dickserv/core/functions.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# functions.py
+
+import datetime
+import random
+import re
+import time
+
+def between(source, start, stop):
+	data = re.compile(start + '(.*?)' + stop, re.IGNORECASE|re.MULTILINE).search(source)
+	if data:
+		return data.group(1)
+	else:
+		return False
+
+def current_date():
+	return time.strftime('%A, %B %d, %Y - %I:%M %p')
+
+def floatint(data):
+    if data.isdigit():
+        return int(data)
+    else:
+        return float(data)
+
+def get_date():
+	return datetime.date.today().strftime('%m/%d/%Y')
+
+def get_datetime(data):
+	return datetime.datetime.strptime(data, '%m/%d/%Y')
+
+def luck(odds):
+	if random_int(1,odds) == 1:
+		return True
+	else:
+		return False
+
+def random_int(min, max):
+	return random.randint(min, max)
+
+def timespan(date):
+	delta = datetime.date(get_date()) - datetime.date(get_datetime(date))
+	return delta.days
+
+def trim(data, max_length):
+	if len(data) > max_length:
+		return data[:max_length] + '...'
+	else:
+		return data
+
+def uptime(start_time):
+	uptime = datetime.datetime(1,1,1) + datetime.timedelta(seconds=time.time() - start_time)
+	return f'{uptime.day-1} Days, {uptime.hour} Hours, {uptime.minute} Minutes, {uptime.second} Seconds'
+\ No newline at end of file
diff --git a/dickserv/core/httplib.py b/dickserv/core/httplib.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# httplib.py
+
+import json
+import os
+import re
+import urllib.parse
+import urllib.request
+
+from bs4 import BeautifulSoup
+
+def clean_url(url):
+    for prefix in ('https://', 'http://', 'www.'):
+        if url.startswith(prefix):
+            url = url[len(prefix):]
+    if url[-1:] == '/':
+        url = url[:-1]
+    return url
+
+def data_quote(data):
+    return urllib.parse.quote(data)
+
+def data_encode(data):
+    return urllib.parse.urlencode(data)
+
+def get_file(url):
+    return os.path.basename(url)
+
+def get_json(url):
+    return json.loads(get_source(url))
+
+def get_size(url):
+    content_length = int(get_url(url).getheader('content-length'))
+    for unit in ('B','KB','MB','GB','TB','PB','EB','ZB'):
+        if abs(content_length) < 1024.0:
+            return '{0:.2f}'.format(content_length) + unit
+        content_length /= 1024.0
+    return '{0:.2f}'.format(content_length) + 'YB'
+
+def get_source(url):
+    source  = get_url(url)
+    charset = source.headers.get_content_charset()
+    if charset:
+        return source.read().decode(charset)
+    else:
+        return source.read().decode()
+
+def get_title(url):
+    source = get_source(url)
+    soup   = BeautifulSoup(source, 'html.parser')
+    return ' '.join(soup.title.string.split())
+
+def get_type(url):
+    return get_url(url).info().get_content_type()
+
+def get_url(url):
+    req = urllib.request.Request(url)
+    req.add_header('User-Agent', 'DickServ/1.0')
+    return urllib.request.urlopen(req, timeout=10)
+
+def parse_urls(data):
+    return re.compile('(?:http[s]?:\/\/|www.)(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', re.IGNORECASE).findall(data)
diff --git a/dickserv/core/irc.py b/dickserv/core/irc.py
@@ -0,0 +1,479 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# irc.py
+
+import socket
+import time
+
+import config
+import constants
+import database
+import debug
+import functions
+import httplib
+
+from commands import *
+
+# Load optional modules
+if config.connection.ssl:
+	import ssl
+if config.connection.proxy:
+	import sock
+
+def color(msg, foreground, background=None):
+	if foreground == 'random':
+		foreground = '{0:0>2}'.format(functions.random_int(2,13))
+	if background == 'random':
+		background = '{0:0>2}'.format(functions.random_int(2,13))
+	if background:
+		return f'\x03{foreground},{background}{msg}{constants.reset}'
+	else:
+		return f'\x03{foreground}{msg}{constants.reset}'
+
+class IRC(object):
+	def __init__(self):
+		self.last   = 0
+		self.slow	= False
+		self.sock	= None
+		self.start  = 0
+		self.status = True
+
+	def connect(self):
+		try:
+			self.create_socket()
+			self.sock.connect((config.connection.server, config.connection.port))
+			self.register()
+		except socket.error as ex:
+			debug.error('Failed to connect to IRC server.', ex)
+			Events.disconnect()
+		else:
+			self.listen()
+
+	def create_socket(self):
+		family = socket.AF_INET6 if config.connection.ipv6 else socket.AF_INET
+		if config.connection.proxy:
+			proxy_server, proxy_port = config.connection.proxy.split(':')
+			self.sock = socks.socksocket(family, socket.SOCK_STREAM)
+			self.sock.setblocking(0)
+			self.sock.settimeout(15)
+			self.sock.setproxy(socks.PROXY_TYPE_SOCKS5, proxy_server, int(proxy_port))
+		else:
+			self.sock = socket.socket(family, socket.SOCK_STREAM)
+		if config.connection.vhost:
+			self.sock.bind((config.connection.vhost, 0))
+		if config.connection.ssl:
+			ctx = ssl.SSLContext()
+			if config.cert.file:
+				ctx.load_cert_chain(config.cert.file, config.cert.key, config.cert.password)
+			if config.connection.ssl_verify:
+				ctx.verify_mode = ssl.CERT_REQUIRED
+				ctx.load_default_certs()
+			else:
+				ctx.check_hostname = False
+				ctx.verify_mode	= ssl.CERT_NONE
+			self.sock = ctx.wrap_socket(self.sock)
+
+	def listen(self):
+		while True:
+			try:
+				data = self.sock.recv(1024).decode('utf-8')
+				for line in (line for line in data.split('\r\n') if line):
+					debug.irc(line)
+					if len(line.split()) >= 2:
+						Events.handle(line)
+			except (UnicodeDecodeError,UnicodeEncodeError):
+				pass
+			except Exception as ex:
+				debug.error('Unexpected error occured.', ex)
+				break
+		Events.disconnect()
+
+	def register(self):
+		if config.login.network:
+			Commands.raw('PASS ' + config.login.network)
+		Commands.raw(f'USER {config.ident.username} 0 * :{config.ident.realname}')
+		Commands.nick(config.ident.nickname)
+
+
+
+class Commands:
+	def action(chan, msg):
+		Commands.sendmsg(chan, f'\x01ACTION {msg}\x01')
+
+	def error(target, data, reason=None):
+		if reason:
+			Commands.sendmsg(target, '[{0}] {1} {2}'.format(color('!', constants.red), data, color('({0})'.format(reason), constants.grey)))
+		else:
+			Commands.sendmsg(target, '[{0}] {1}'.format(color('!', constants.red), data))
+
+	def identify(nick, password):
+		Commands.sendmsg('nickserv', f'identify {nick} {password}')
+
+	def join_channel(chan, key=None):
+		Commands.raw(f'JOIN {chan} {key}') if key else Commands.raw('JOIN ' + chan)
+
+	def mode(target, mode):
+		Commands.raw(f'MODE {target} {mode}')
+
+	def nick(nick):
+		Commands.raw('NICK ' + nick)
+
+	def notice(target, msg):
+		Commands.raw(f'NOTICE {target} :{msg}')
+
+	def oper(user, password):
+		Commands.raw(f'OPER {user} {password}')
+
+	def raw(msg):
+		msg = msg.replace('\r','').replace('\n','')[:450]
+		DickServ.sock.send(bytes(msg + '\r\n', 'utf-8'))
+
+	def sendmsg(target, msg):
+		Commands.raw(f'PRIVMSG {target} :{msg}')
+
+
+
+class Events:
+	def connect():
+		DickServ.start = time.time()
+		if config.settings.modes:
+			Commands.mode(config.ident.nickname, '+' + config.settings.modes)
+		if config.login.nickserv:
+			Commands.identify(config.ident.nickname, config.login.nickserv)
+		if config.login.operator:
+			Commands.oper(config.ident.username, config.login.operator)
+		Commands.join_channel(config.connection.channel, config.connection.key)
+
+	def disconnect():
+		DickServ.sock.close()
+		time.sleep(10)
+		DickServ.connect()
+
+	def kick(nick, chan, kicked):
+		if kicked == config.ident.nickname and chan == config.connection.channel:
+			time.sleep(3)
+			Commands.join_channel(chan, config.connection.key)
+
+	def message(nick, ident, chan, msg):
+		try:
+			if chan == config.connection.channel and (DickServ.status or ident == config.settings.admin):
+				if not msg.startswith(config.settings.cmd_char):
+					urls = httplib.parse_urls(msg)
+					if urls:
+						if time.time() - DickServ.last > 3:
+							DickServ.last = time.time()
+							Events.url(chan, urls[0])
+					elif msg == '@dickserv':
+						Commands.sendmsg(chan, constants.bold + 'DickServ IRC Bot - Developed by acidvegas in Python - https://acid.vegas/dickserv')
+					elif msg == '@dickserv help':
+						Commands.sendmsg(chan, 'https://git.supernets.org/acidvegas/dickserv#commands')
+					elif msg == 'h' and functions.luck(4):
+						Commands.sendmsg(chan, 'h')
+					elif 'qhat' in msg:
+						Commands.sendmsg(chan, 'Q:)')
+				elif ident not in database.Ignore.idents():
+					if time.time() - DickServ.last < 3 and ident != config.settings.admin:
+						if not DickServ.slow:
+							Commands.sendmsg(chan, color('Slow down nerd!', constants.red))
+							DickServ.slow = True
+					else:
+						DickServ.slow = False
+						args = msg.split()
+						argz = msg[len(args[0])+1:]
+						cmd  = args[0][1:]
+						if len(args) == 1:
+							if cmd == 'date':
+								Commands.sendmsg(chan, functions.current_date())
+							elif cmd == 'talent':
+								if functions.luck(1000):
+									Commands.sendmsg(chan, color(f' !!! HOLY FUCKING SHIT {nick} ACHIEVED TALENT !!! ',               'random', 'random'))
+									Commands.sendmsg(chan, color(' !!! RIP DITTLE DIP DIP DIP DIP IT\'S YOUR BIRTHDAY !!! ',          'random', 'random'))
+									Commands.sendmsg(chan, color(f' !!! CAN WE HAVE A GOT DAMN MOMENT OF SILENCE FOR {nick} :) !!! ', 'random', 'random'))
+									Commands.sendmsg(chan, color(' !!! GOT DAMN XD THIS IS TOO CRAZY LIKE...DAMN HAHA. DAMN. !!! ',   'random', 'random'))
+								else:
+									Commands.sendmsg(chan, color('(^)', 'random'))
+							elif cmd == 'todo':
+								todos = database.Todo.read(ident)
+								if todos:
+									for item in todos:
+										Commands.notice(nick, '[{0}] {1}'.format(color(todos.index(item)+1, constants.pink), item))
+								else:
+									Commands.notice(nick, 'You have no saved todos.')
+							elif cmd == 'uptime':
+								Commands.sendmsg(chan, functions.uptime(DickServ.start))
+						elif len(args) == 2:
+							if cmd == 'coin':
+								api = cryptocurrency.get(args[1])
+								if api:
+									Commands.sendmsg(chan, '{0} {1} - ${2:,.2f}'.format(color(api['name'], constants.white), color('({0})'.format(api['symbol']), constants.grey), float(api['price_usd'])))
+								else:
+									Commands.error(chan, 'Invalid cryptocurrency name!')
+							elif cmd == 'drug':
+								api = tripsit.drug(args[1])
+								if api:
+									Commands.sendmsg(chan, '{0} - {1}'.format(color(api['name'], constants.yellow), api['desc']))
+								else:
+									Commands.error(chan, 'No results found.')
+							elif cmd == 'define':
+								definition = dictionary.define(args[1])
+								if definition:
+									Commands.sendmsg(chan, '{0} - {1}: {2}'.format(color('Definition', constants.white, constants.blue), args[1].lower(), definition))
+								else:
+									Commands.error(chan, 'No results found.')
+							elif cmd == 'isup':
+								Commands.sendmsg(chan, '{0} is {1}'.format(args[1], isup.check(args[1])))
+							elif cmd == 'r':
+								api = reddit.read(args[1])
+								if api:
+									data = list(api.keys())
+									for i in data:
+										count = str(data.index(i)+1)
+										Commands.sendmsg(chan, '[{0}] {1} [{2}|{3}/{4}|{5}]'.format(color(count, constants.pink), functions.trim(i, 100), color(str(api[i]['score']), constants.white), color('+' + str(api[i]['ups']), constants.green), color('-' + str(api[i]['downs']), constants.red), color(api[i]['comments'], constants.white)))
+										Commands.sendmsg(chan, ' - ' + color(api[i]['url'], constants.grey))
+								else:
+									Commands.error(chan, 'No results found.')
+							elif cmd == 'w':
+								if args[1].isdigit():
+									api = weather.lookup(args[1])
+									if api:
+										Commands.sendmsg(chan, api)
+									else:
+										Commands.error(chan, 'No results found.')
+								else:
+									Commands.error(chan, 'Invalid arguments.')
+						if len(args) >= 2:
+							if cmd == 'g':
+								api = google.search(argz, database.Settings.get('max_results'))
+								if api:
+									for result in api:
+										count = api.index(result)+1
+										Commands.sendmsg(chan, '[{0}] {1}'.format(color(count, constants.pink), result['title']))
+										Commands.sendmsg(chan, ' - ' + color(result['link'], constants.grey))
+								else:
+									Commands.error(chan, 'No results found.')
+							elif cmd == 'imdb':
+								api = imdb.search(argz)
+								if api:
+									Commands.sendmsg(chan, '{0} {1} {2} {3}'.format(color('Title  :', constants.white), api['Title'], api['Year'], color(api['Rated'], constants.grey)))
+									Commands.sendmsg(chan, '{0} {1}{2}'.format(color('Link   :', constants.white), constants.underline, color('https://imdb.com/title/' +  api['imdbID'], constants.light_blue)))
+									Commands.sendmsg(chan, '{0} {1}'.format(color('Genre  :', constants.white), api['Genre']))
+									if api['imdbRating'] == 'N/A':
+										Commands.sendmsg(chan, '{0} {1} 0/10'.format(color('Rating :', constants.white), color('★★★★★★★★★★', constants.grey)))
+									else:
+										Commands.sendmsg(chan, '{0} {1}{2} {3}/10'.format(color('Rating :', constants.white), color('★'*round(float(api['imdbRating'])), constants.yellow), color('★'*(10-round(float(api['imdbRating']))), constants.grey), api['imdbRating']))
+									Commands.sendmsg(chan, '{0} {1}'.format(color('Plot   :', constants.white), api['Plot']))
+								else:
+									Commands.error(chan, 'No results found.')
+							elif cmd == 'netsplit':
+								api = netsplit.search(argz)
+								if api:
+									data = list(api.keys())
+									for i in data:
+										count = str(data.index(i)+1)
+										Commands.sendmsg(chan, '[{0}] {1} {2} / {3}'.format(color(count, constants.pink), color(i, constants.light_blue), color('({0})'.format(api[i]['users']), constants.grey), color(api[i]['network'], constants.red)))
+										Commands.sendmsg(chan, color(' - ' + api[i]['topic'], constants.grey))
+								else:
+									Commands.error(chan, 'No results found.')
+							elif cmd == 'todo' and len(args) >= 3:
+								if len(args) >= 3 and args[1] == 'add':
+									todos = database.Todo.read(ident)
+									if len(todos) <= database.Settings.get('max_todo_per') and len(database.Todo.read()) <= database.Settings.get('max_todo'):
+										argz = argz[4:]
+										if argz not in todos:
+											database.Todo.add(functions.get_date(), ident, argz)
+											Commands.notice(nick, 'Todo added to database!')
+										else:
+											Commands.notice(nick, 'Todo already in database!')
+									else:
+										Commands.notice(nick, 'Maximum todos reached!')
+								elif len(args) == 3 and args[1] == 'del':
+									num = args[2]
+									if num.isdigit():
+										num   = int(num)
+										todos = database.Todo.read(ident)
+										if todos:
+											if num <= len(todos):
+												for item in todos:
+													count = todos.index(item)+1
+													if count == num:
+														database.Todo.remove(ident, item)
+														break
+												Commands.notice(nick, 'Todo removed from database!')
+											else:
+												Commands.notice(nick, 'Invalid number.')
+										else:
+											Commands.notice(nick, 'No todos found.')
+									else:
+										Commands.notice(nick, 'Invalid number.')
+								else:
+									Commands.notice(nick, 'Invalid arguments.')
+							elif cmd == 'tpb':
+								api = tpb.search(argz, database.Settings.get('max_results'))
+								if api:
+									data = list(api.keys())
+									for i in data:
+										count = str(data.index(i)+1)
+										Commands.sendmsg(chan, '[{0}] {1} [{2}/{3}]'.format(color(count, constants.pink), i, color(api[i]['seeders'], constants.green), color(api[i]['leechers'], constants.red)))
+										Commands.sendmsg(chan, ' - ' + color('http://thepiratebay.org' + api[i]['url'], constants.grey))
+								else:
+									Commands.error(chan, 'No results found.')
+							elif cmd == 'ud':
+								definition = dictionary.urban(argz)
+								if definition:
+									Commands.sendmsg(chan, '{0}{1} - {2}: {3}'.format(color('urban', constants.white, constants.blue), color('DICTIONARY', constants.yellow, constants.black), argz, definition))
+								else:
+									Commands.error(chan, 'No results found.')
+							elif cmd == 'wolfram':
+								results = wolfram.ask(argz)
+								if results:
+									Commands.sendmsg(chan, '{0}{1} - {2}'.format(color('Wolfram', constants.red), color('Alpha', constants.orange), results))
+								else:
+									Commands.error(chan, 'No results found.')
+							elif cmd == 'yt':
+								api  = youtube.search(argz, database.Settings.get('max_results'))
+								if api:
+									data = list(api.keys())
+									for i in api.keys():
+										count = str(data.index(i)+1)
+										Commands.sendmsg(chan, '[{0}] {1}'.format(color(count, constants.pink), functions.trim(i, 75)))
+										Commands.sendmsg(chan, ' - ' + color(api[i], constants.grey))
+								else:
+									Commands.error(chan, 'No results found.')
+						DickServ.last = time.time()
+		except Exception as ex:
+			Commands.error(chan, 'Command threw an exception.', ex)
+
+	def nick_in_use():
+		debug.error('DickServ is already running or nick is in use.')
+
+	def private(nick, ident, msg):
+		try:
+			if ident == config.settings.admin_host:
+				args = msg.split()
+				cmd  = args[0][1:]
+				if len(args) == 1:
+					if cmd == 'config':
+						settings = database.Settings.read()
+						Commands.sendmsg(nick, '[{0}]'.format(color('Settings', constants.purple)))
+						for setting in settings:
+							Commands.sendmsg(nick, '{0} = {1}'.format(color(setting[0], constants.yellow), color(setting[1], constants.grey)))
+					elif cmd == 'ignore':
+						ignores = database.Ignore.idents()
+						if ignores:
+							Commands.sendmsg(nick, '[{0}]'.format(color('Ignore List', constants.purple)))
+							for user in ignores:
+								Commands.sendmsg(nick, color(user, constants.yellow))
+							Commands.sendmsg(nick, '{0} {1}'.format(color('Total:', constants.light_blue), color(len(ignores), constants.grey)))
+						else:
+							Commands.error(nick, 'Ignore list is empty!')
+					elif cmd == 'off':
+						DickServ.status = False
+						Commands.sendmsg(nick, color('OFF', constants.red))
+					elif cmd == 'on':
+						DickServ.status = True
+						Commands.sendmsg(nick, color('ON', constants.green))
+				elif len(args) == 2:
+					if cmd == 'ignore' and args[1] == 'reset':
+						database.Ignore.reset()
+					elif cmd == 'todo' and args[1] == 'expire':
+						database.Todo.expire_check()
+					elif cmd == 'todo' and args[1] == 'reset':
+						database.Todo.reset()
+				elif len(args) == 3:
+					if cmd == 'config':
+						setting, value = args[1], args[2]
+						if functions.CheckString.number(value):
+							value = functions.floatint(value)
+							if value >= 0:
+								if setting in database.Settings.settings():
+									database.Settings.update(setting, value)
+									Commands.sendmsg(nick, 'Change setting for {0} to {1}.'.format(color(setting, constants.yellow), color(value, constants.grey)))
+								else:
+									Commands.error(nick, 'Invalid config variable.')
+							else:
+								Commands.error(nick, 'Value must be greater than or equal to zero.')
+						else:
+							Commands.error(nick, 'Value must be an integer or float.')
+					elif cmd == 'ignore':
+						if args[1] == 'add':
+							user_ident = args[2]
+							if user_ident not in database.Ignore.idents():
+								database.Ignore.add(nickname, user_ident)
+								Commands.sendmsg(nick, 'Ident {0} to the ignore list.'.format(color('added', constants.green)))
+							else:
+								Commands.error(nick, 'Ident is already on the ignore list.')
+						elif cmd == 'del':
+							user_ident = args[2]
+							if user_ident in database.Ignore.idents():
+								database.Ignore.remove(user_ident)
+								Commands.sendmsg(nick, 'Ident {0} from the ignore list.'.format(color('removed', constants.red)))
+							else:
+								Commands.error(nick, 'Ident does not exist in the ignore list.')
+		except Exception as ex:
+			Commands.error(nick, 'Command threw an exception.', ex)
+
+	def url(chan, url):
+		try:
+			if imdb.check(url):
+				id  = imdb.check(url)
+				api = imdb.search(id)
+				if api:
+					Commands.sendmsg(chan, '{0} {1} {2} {3}'.format(color('Title  :', constants.white), api['Title'], api['Year'], color(api['Rated'], constants.grey)))
+					Commands.sendmsg(chan, '{0} {1}{2}'.format(color('Link   :', constants.white), constants.underline, color('https://imdb.com/title/' +  api['imdbID'], constants.light_blue)))
+					Commands.sendmsg(chan, '{0} {1}'.format(color('Genre  :', constants.white), api['Genre']))
+					if api['imdbRating'] == 'N/A':
+						Commands.sendmsg(chan, '{0} {1} 0/10'.format(color('Rating :', constants.white), color('★★★★★★★★★★', constants.grey)))
+					else:
+						Commands.sendmsg(chan, '{0} {1}{2} {3}/10'.format(color('Rating :', constants.white), color('★'*round(float(api['imdbRating'])), constants.yellow), color('★'*(10-round(float(api['imdbRating']))), constants.grey), api['imdbRating']))
+					Commands.sendmsg(chan, '{0} {1}'.format(color('Plot   :', constants.white), api['Plot']))
+			elif reddit.check(url):
+				subreddit = reddit.check(url)[0]
+				post_id   = reddit.check(url)[1]
+				api       = reddit.post_info(subreddit, post_id)
+				if api:
+					Commands.sendmsg(chan, '[{0}] - {1} [{2}|{3}/{4}|{5}]'.format(color('reddit', constants.cyan), color(functions.trim(api['title'], 75), constants.white), color(api['score'], constants.white), color('+' + api['ups'], constants.green), color('-' + api['downs'], constants.red), color(api['num_comments'], constants.white)))
+			elif youtube.check(url):
+				api = youtube.video_info(youtube.check(url))
+				if api:
+					Commands.sendmsg(chan, '{0}{1} - {2} [{3}|{4}/{5}]'.format(color('You', constants.black, constants.white), color('Tube', constants.white, constants.red), functions.trim(api['title'], 75), color(api['views'], constants.white), color('+' + api['likes'], constants.green), color('-' + api['dislikes'], constants.red)))
+					Commands.sendmsg(chan, color(api['description'], constants.grey))
+			else:
+				url_type = httplib.get_type(url)
+				if url_type == 'text/html':
+					title = httplib.get_title(url)
+					Commands.sendmsg(chan, '[{0}] {1}'.format(color(url_type, constants.pink), color(title, constants.white)))
+				else:
+					file_name = httplib.get_file(url)
+					if file_name:
+						file_size = httplib.get_size(url)
+						Commands.sendmsg(chan, '[{0}] {1} [{2}]'.format(color(url_type, constants.pink), color(file_name, constants.white), color(file_size, constants.blue)))
+		except Exception as ex:
+			debug.error('Title Error', ex)
+
+	def handle(data):
+		args = data.split()
+		if data.startswith('ERROR :Closing Link:'):
+			raise Exception('Connection has closed.')
+		elif args[0] == 'PING':
+			Commands.raw('PONG ' + args[1][1:])
+		elif args[1] == constants.RPL_WELCOME:
+			Events.connect()
+		elif args[1] == constants.ERR_NICKNAMEINUSE:
+			Events.nick_in_use()
+		elif args[1] == constants.KICK:
+			nick   = args[0].split('!')[0][1:]
+			chan   = args[2]
+			kicked = args[3]
+			Events.kick(nick, chan, kicked)
+		elif args[1] == constants.PRIVMSG:
+			nick  = args[0].split('!')[0][1:]
+			ident = args[0].split('!')[1]
+			chan  = args[2]
+			msg   = ' '.join(args[3:])[1:]
+			if chan == config.ident.nickname:
+				Events.private(nick, ident, msg)
+			else:
+				Events.message(nick, ident, chan, msg)
+
+DickServ = IRC()
diff --git a/dickserv/data/cert/.gitignore b/dickserv/data/cert/.gitignore
@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
+\ No newline at end of file
diff --git a/dickserv/data/logs/.gitignore b/dickserv/data/logs/.gitignore
@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
+\ No newline at end of file
diff --git a/dickserv/dickserv.py b/dickserv/dickserv.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# dickserv.py
+
+import os
+import sys
+
+sys.dont_write_bytecode = True
+os.chdir(sys.path[0] or '.')
+sys.path += ('core','modules')
+
+import debug
+
+debug.setup_logger()
+debug.info()
+if not debug.check_version(3):
+	debug.error_exit('Python 3 is required!')
+if debug.check_privileges():
+	debug.error_exit('Do not run as admin/root!')
+debug.check_libs()
+import database
+database.check()
+import irc
+irc.DickServ.connect()
+\ No newline at end of file
diff --git a/dickserv/modules/cryptocurrency.py b/dickserv/modules/cryptocurrency.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# cryptocurrency.py
+
+import httplib
+
+def get(coin):
+	api  = httplib.get_json('https://api.coinmarketcap.com/v1/ticker/?limit=500')
+	data = [item for item in api if (coin.lower() == item['id'] or coin.upper() == item['symbol'])]
+	if data:
+		return data[0]
+	else:
+		return False
+\ No newline at end of file
diff --git a/dickserv/modules/dictionary.py b/dickserv/modules/dictionary.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# dictionary.py
+
+import httplib
+import functions
+
+def define(word):
+	source  = httplib.get_source('http://www.merriam-webster.com/dictionary/' + word.replace(' ', '%20'))
+	results = functions.between(source, f'<meta name="description" content="Define {word}: ', '">')
+	if results:
+		return results
+	else:
+		return False
+
+def urban(word):
+	api = httplib.get_json('http://api.urbandictionary.com/v0/define?term=' + word.replace(' ', '+'))
+	if api['result_type'] != 'no_results':
+		definition = api['list'][0]['definition']
+		return definition
+	else:
+		return False
diff --git a/dickserv/modules/google.py b/dickserv/modules/google.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# google.py
+
+from googleapiclient.discovery import build
+
+import config
+
+def search(query, results):
+	service = build('customsearch', 'v1', developerKey=config.api.google_api_key, cache_discovery=False)
+	results = service.cse().list(q=query, cx=config.api.google_cse_id, num=results).execute()
+	return results['items']
+\ No newline at end of file
diff --git a/dickserv/modules/imdb.py b/dickserv/modules/imdb.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# imdb.py
+
+import re
+
+import config
+import httplib
+
+def check(url):
+	found = re.match('^.*?imdb.com\/title\/tt([0-9A-Za-z]+).*?$', url, re.IGNORECASE)
+	if found:
+		return found.group(1)
+	else:
+		return False
+
+def search(query):
+	if query.startswith('tt') and len(query) == 9:
+		api = httplib.get_json(f'http://omdbapi.com/?i={query}&apikey={config.api.omdbapi_key}')
+	else:
+		year = query.split()[-1]
+		if len(year) == 4 and year.isdigit():
+			query = query[:-5].replace(' ', '%20')
+			api = httplib.get_json(f'http://omdbapi.com/?t={query}&y={year}&apikey={config.api.omdbapi_key}')
+		else:
+			query = query.replace(' ', '%20')
+			api = httplib.get_json(f'http://omdbapi.com/?t={query}&apikey={config.api.omdbapi_key}')
+	if api['Response'] == 'True':
+		return api
+	else:
+		return False
+\ No newline at end of file
diff --git a/dickserv/modules/isup.py b/dickserv/modules/isup.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# isup.py
+
+import httplib
+
+def check(url):
+    source = httplib.get_source('http://isup.me/' + url)
+    if source.find('It\'s just you.') != -1:
+        return 'UP'
+    elif source.find('It\'s not just you!') != -1:
+        return 'DOWN'
+    elif source.find('Huh?') != -1:
+        return 'INVALID'
+    else:
+        return 'UNKNOWN'
diff --git a/dickserv/modules/netsplit.py b/dickserv/modules/netsplit.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# netsplit.py
+
+import re
+
+import functions
+import httplib
+
+def search(query):
+	channels = {}
+	source   = httplib.get_source('http://irc.netsplit.de/channels/?chat=' + query.replace(' ','+'))
+	for i in ('&#8203;','<b>','</b>','<span style="color:#000000;">','<strong>','</strong>'):
+		source = source.replace(i, '')
+	channel_objects = re.findall('<div style="margin: 4px; padding: 0 0 15 0; text-align: left;">(.*?)</a></span></div>', source, re.IGNORECASE|re.MULTILINE)
+	for data in channel_objects:
+		channel = functions.between(data, '<span class="cs-channel">', '</span>')
+		network = functions.between(data, '<span class="cs-network">', '</span>')
+		users   = functions.between(data, '<span class="cs-users">', ' &ndash; </span>')
+		topic   = functions.between(data, '<span class="cs-topic">', '</span><br>')
+		if not topic:
+			topic = 'No channel topic set.'
+		channels[channel] = {'network':network,'users':users,'topic':topic}
+	return channels
+\ No newline at end of file
diff --git a/dickserv/modules/reddit.py b/dickserv/modules/reddit.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# reddit.py
+
+import re
+
+import httplib
+from database import Settings
+
+def check(url):
+	found = re.match('^.*?reddit.com\/r\/(.*?)\/comments\/([0-9A-Za-z]+).*$', url, re.IGNORECASE)
+	if found:
+		return (found.group(1), found.group(2))
+	else:
+		return False
+
+def post_info(subreddit, id):
+	api = httplib.get_json(f'https://www.reddit.com/r/{subreddit}/comments/{id}.json')
+	if 'error' not in api:
+		return api[0]['data']['children'][0]['data']
+	else:
+		return False
+
+def read(subreddit):
+	api  = httplib.get_json('https://www.reddit.com/r/{0}.json?limit={1}'.format(subreddit, Settings.get('max_results')))
+	data = [x['data'] for x in api['data']['children']]
+	if data:
+		results = {}
+		for item in data:
+			if not item['stickied']:
+				results[item['title']] = {'url':item['url'], 'score':item['score'], 'ups':item['ups'], 'downs':item['downs'], 'comments':item['num_comments']}
+		return results
+	else:
+		return False
diff --git a/dickserv/modules/tpb.py b/dickserv/modules/tpb.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# tpb.py
+
+import re
+
+import httplib
+from database import Settings
+
+def search(query, results):
+	url      = 'https://thepiratebay.org/search/{0}/0/99/0'.format(query.replace(' ', '+'))
+	source   = httplib.get_source(url)
+	torrents = re.findall('<a href="(.*?)" class="detLink".*>(.*?)</a>', source)
+	seeders  = re.findall('\t\t</td>\n\t\t<td align="right">(.*?)</td>', source)
+	leechers = re.findall('<td align="right">(.*?)</td>\n\t</tr>',       source)
+	if torrents:
+		data     = {}
+		torrents = torrents[:results]
+		for i in range(len(torrents)):
+			data[torrents[i][1]] = {'seeders':seeders[i], 'leechers':leechers[i], 'url':torrents[i][0]}
+		return data
+	else:
+	   return False
+\ No newline at end of file
diff --git a/dickserv/modules/tripsit.py b/dickserv/modules/tripsit.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# tripsit.py
+
+import httplib
+
+def drug(query):
+	api = httplib.get_json('http://tripbot.tripsit.me/api/tripsit/getDrug?name=' + query.replace(' ','%20'))
+	if api['err'] != True:
+		return {'name':api['data'][0]['name'],'desc':api['data'][0]['properties']['summary']}
+	else:
+		return False
+\ No newline at end of file
diff --git a/dickserv/modules/weather.py b/dickserv/modules/weather.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# weather.py
+
+import httplib
+
+import config
+
+def lookup(zip_code):
+	api = httplib.get_json('http://api.wunderground.com/api/{0}/conditions/q/{1}.json'.format(config.api.wunderground_api_key, zip_code))
+	if 'error' not in api:
+		city    = api['current_observation']['display_location']['city']
+		state   = api['current_observation']['display_location']['state']
+		country = api['current_observation']['display_location']['country']
+		weather = api['current_observation']['weather']
+		temp    = api['current_observation']['temp_f']
+		return 'The weather for {0}, {1}, {2} is {3} at {4} F'.format(city, state, country, weather, temp)
+	else:
+		return False
diff --git a/dickserv/modules/wolfram.py b/dickserv/modules/wolfram.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# wolfram.py
+
+from xml.etree import ElementTree as etree
+
+import config
+import httplib
+
+def ask(query):
+	params  = httplib.data_encode({'input':query, 'appid':config.api.wolfram_api_key})
+	data    = httplib.get_source('http://api.wolframalpha.com/v2/query?' + params)
+	results = {}
+	tree	= etree.fromstring(data)
+	for e in tree.findall('pod'):
+		for item in [ef for ef in list(e) if ef.tag=='subpod']:
+			for it in [i for i in list(item) if i.tag=='plaintext']:
+				if it.tag=='plaintext':
+					results[e.get('title')] = it.text
+	if 'Result' in results:
+		return results['Result']
+	else:
+		return False
+\ No newline at end of file
diff --git a/dickserv/modules/youtube.py b/dickserv/modules/youtube.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
+# youtube.py
+
+import re
+
+import config
+import httplib
+
+def check(url):
+	found = re.match('^.*?youtu(be)?\.([a-z])+\/(watch(.*?)(\?|\&)v=)?(.*?)(&(.)*)*$', url, re.IGNORECASE)
+	if found:
+		return found.group(6)
+	else:
+		return False
+
+def video_info(id):
+	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
+
+def search(query, results):
+	url	    = 'https://www.googleapis.com/youtube/v3/search?part=snippet&key={0}&q={1}&maxResults={2}&type=video&regionCode=US&relevanceLanguage=en&safeSearch=none'.format(config.api.google_api_key, httplib.data_quote(query), results)
+	api     = httplib.get_json(url)
+	results = {}
+	for item in api['items']:
+		title = item['snippet']['title']
+		url   = 'https://www.youtube.com/watch?v=' + item['id']['videoId']
+		results[title] = url
+	return results