←︎ dickserv :: 11ceb7f


1
commit 11ceb7fba6a96a8e6e308107858fd82eac0941d3 (HEAD -> master)
2
Author: acidvegas <acid.vegas@acid.vegas>
3
Date:   Mon Jun 24 22:13:57 2019 -0400
4
5
    Initial commit
6
---
7
 LICENSE                            |  15 ++
8
 README.md                          |  51 ++++
9
 dickserv/core/commands.py          |  16 ++
10
 dickserv/core/config.py            |  42 ++++
11
 dickserv/core/constants.py         | 213 +++++++++++++++++
12
 dickserv/core/database.py          |  83 +++++++
13
 dickserv/core/debug.py             |  90 +++++++
14
 dickserv/core/functions.py         |  53 ++++
15
 dickserv/core/httplib.py           |  63 +++++
16
 dickserv/core/irc.py               | 479 +++++++++++++++++++++++++++++++++++++
17
 dickserv/data/cert/.gitignore      |   4 +
18
 dickserv/data/logs/.gitignore      |   4 +
19
 dickserv/dickserv.py               |  24 ++
20
 dickserv/modules/cryptocurrency.py |  13 +
21
 dickserv/modules/dictionary.py     |  22 ++
22
 dickserv/modules/google.py         |  12 +
23
 dickserv/modules/imdb.py           |  31 +++
24
 dickserv/modules/isup.py           |  16 ++
25
 dickserv/modules/netsplit.py       |  24 ++
26
 dickserv/modules/reddit.py         |  34 +++
27
 dickserv/modules/tpb.py            |  23 ++
28
 dickserv/modules/tripsit.py        |  12 +
29
 dickserv/modules/weather.py        |  19 ++
30
 dickserv/modules/wolfram.py        |  23 ++
31
 dickserv/modules/youtube.py        |  40 ++++
32
 25 files changed, 1406 insertions(+)
33
34
diff --git a/LICENSE b/LICENSE
35
new file mode 100644
36
index 0000000..69997e8
37
--- /dev/null
38
+++ b/LICENSE
39
@@ -0,0 +1,15 @@
40
+ISC License
41
+
42
+Copyright (c) 2019, acidvegas <acid.vegas@acid.vegas>
43
+
44
+Permission to use, copy, modify, and/or distribute this software for any
45
+purpose with or without fee is hereby granted, provided that the above
46
+copyright notice and this permission notice appear in all copies.
47
+
48
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
49
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
50
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
51
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
52
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
53
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
54
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
55
diff --git a/README.md b/README.md
56
new file mode 100644
57
index 0000000..06b5053
58
--- /dev/null
59
+++ b/README.md
60
@@ -0,0 +1,51 @@
61
+###### Requirments
62
+* [Python](https://www.python.org/downloads/) *(**Note:** This script was developed to be used with the latest version of Python.)*
63
+* [PySocks](https://pypi.python.org/pypi/PySocks) *(**Optional:** For using the `proxy` setting.)*
64
+* [beautifulsoup4](https://pypi.python.org/pypi/beautifulsoup4)
65
+* [google-api-python-client](https://pypi.python.org/pypi/google-api-python-client)
66
+
67
+###### Commands
68
+| Command | Description |
69
+| --- | --- |
70
+| @dickserv | Information about the bot. |
71
+| @dickserv help | Information about the commands. |
72
+| coin \<cryptocurrency> | Get the USD value for \<cryptocurrency>. |
73
+| date | Get the current date and time. |
74
+| define \<word> | Get the definition of \<word>. |
75
+| drug \<query> | Lookup information on \<drug> on tripsit. |
76
+| g \<query> | Search Google for \<query>. |
77
+| imdb \<query/ttid> [year] | Search \<query/ttid> on IMDb. |
78
+| isup \<url> | Check if \<url> is up or not. |
79
+| netsplit \<query> | Search for \<query> on NetSplit. |
80
+| r \<subreddit> | Read top posts from \<subreddit> |
81
+| talent | RIP DITTLE DIP DIP DIP DIP IT\'S YA BIRTHDAY!!1@11! |
82
+| todo | Read your todo list. |
83
+| todo add \<data> | Add \<data> to your todo list. |
84
+| todo del \<num>| | Delete the \<num> todo result. |
85
+| tpb \<query> | Searc \<query on ThePirateBay. |
86
+| ud \<word> | Get the urban dictionary definition of \<word>. |
87
+| uptime | Get the amount of time DickServ has been running. |
88
+| w \<zip_code> | Get the weather for \<zip>. |
89
+| wolfram \<ask> | Get the results of \<query> from WolframAlpha. |
90
+| yt \<query> | Search \<query> on YouTube. |
91
+
92
+###### Admin Commands (Private Message)
93
+| Command | Description |
94
+| --- | --- |
95
+| config | View the config settings. |
96
+| config \<setting> \<value> | Change \<setting> to \<value>. |
97
+| ignore | View the ignore list. |
98
+| ignore add \<ident> | Add a user to the ignore list. |
99
+| ignore del \<ident> | Remove a user from the ignore list. |
100
+| ignore reset | Remove all ignores. |
101
+| off | Toggle the usage of the bot commands. |
102
+| on | Toggle the usage of the bot commands. |
103
+| todo | List all todos. |
104
+| todo expire | Remove all expired todos. |
105
+| todo reset | Remove all todos. |
106
+
107
+###### Mirrors
108
+- [acid.vegas](https://acid.vegas/dickserv) *(main)*
109
+- [SuperNETs](https://git.supernets.org/acidvegas/dickserv)
110
+- [GitHub](https://github.com/acidvegas/dickserv)
111
+- [GitLab](https://gitlab.com/acidvegas/dickserv)
112
diff --git a/dickserv/core/commands.py b/dickserv/core/commands.py
113
new file mode 100644
114
index 0000000..6903dfe
115
--- /dev/null
116
+++ b/dickserv/core/commands.py
117
@@ -0,0 +1,16 @@
118
+#!/usr/bin/env python
119
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
120
+# commands.py
121
+
122
+import cryptocurrency
123
+import dictionary
124
+import google
125
+import imdb
126
+import isup
127
+import netsplit
128
+import reddit
129
+import tpb
130
+import tripsit
131
+import weather
132
+import wolfram
133
+import youtube
134
diff --git a/dickserv/core/config.py b/dickserv/core/config.py
135
new file mode 100644
136
index 0000000..cd663c2
137
--- /dev/null
138
+++ b/dickserv/core/config.py
139
@@ -0,0 +1,42 @@
140
+#!/usr/bin/env python
141
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
142
+# config.py
143
+
144
+class connection:
145
+	server     = 'irc.server.com'
146
+	port       = 6697
147
+	ipv6       = False
148
+	ssl        = True
149
+	ssl_verify = False
150
+	proxy      = None
151
+	vhost      = None
152
+	channel    = '#chats'
153
+	key        = None
154
+
155
+class cert:
156
+	key      = None
157
+	file     = None
158
+	password = None
159
+
160
+class ident:
161
+	nickname = 'DickServ'
162
+	username = 'dickserv'
163
+	realname = 'acid.vegas/dickserv'
164
+
165
+class login:
166
+	network  = None
167
+	nickserv = None
168
+	operator = None
169
+
170
+class settings:
171
+	admin    = 'user@host.name'
172
+	cmd_char = '.'
173
+	log      = False
174
+	modes    = None
175
+
176
+class api:
177
+	google_api_key       = 'CHANGEME' # https://console.developers.google.com/
178
+	google_cse_id        = 'CHANGEME' # https://cse.google.com/
179
+	omdbapi_key          = 'CHANGEME' # http://www.omdbapi.com/apikey.aspx
180
+	wolfram_api_key      = 'CHANGEME' # http://products.wolframalpha.com/api/
181
+	wunderground_api_key = 'CHANGEME' # https://www.wunderground.com/weather/api/
182
diff --git a/dickserv/core/constants.py b/dickserv/core/constants.py
183
new file mode 100644
184
index 0000000..2f1a041
185
--- /dev/null
186
+++ b/dickserv/core/constants.py
187
@@ -0,0 +1,213 @@
188
+#!/usr/bin/env python
189
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
190
+# constants.py
191
+
192
+# Control Characters
193
+bold      = '\x02'
194
+color     = '\x03'
195
+italic    = '\x1D'
196
+underline = '\x1F'
197
+reverse   = '\x16'
198
+reset     = '\x0f'
199
+
200
+# Color Codes
201
+white       = '00'
202
+black       = '01'
203
+blue        = '02'
204
+green       = '03'
205
+red         = '04'
206
+brown       = '05'
207
+purple      = '06'
208
+orange      = '07'
209
+yellow      = '08'
210
+light_green = '09'
211
+cyan        = '10'
212
+light_cyan  = '11'
213
+light_blue  = '12'
214
+pink        = '13'
215
+grey        = '14'
216
+light_grey  = '15'
217
+
218
+# Events
219
+PASS     = 'PASS'
220
+NICK     = 'NICK'
221
+USER     = 'USER'
222
+OPER     = 'OPER'
223
+MODE     = 'MODE'
224
+SERVICE  = 'SERVICE'
225
+QUIT     = 'QUIT'
226
+SQUIT    = 'SQUIT'
227
+JOIN     = 'JOIN'
228
+PART     = 'PART'
229
+TOPIC    = 'TOPIC'
230
+NAMES    = 'NAMES'
231
+LIST     = 'LIST'
232
+INVITE   = 'INVITE'
233
+KICK     = 'KICK'
234
+PRIVMSG  = 'PRIVMSG'
235
+NOTICE   = 'NOTICE'
236
+MOTD     = 'MOTD'
237
+LUSERS   = 'LUSERS'
238
+VERSION  = 'VERSION'
239
+STATS    = 'STATS'
240
+LINKS    = 'LINKS'
241
+TIME     = 'TIME'
242
+CONNECT  = 'CONNECT'
243
+TRACE    = 'TRACE'
244
+ADMIN    = 'ADMIN'
245
+INFO     = 'INFO'
246
+SERVLIST = 'SERVLIST'
247
+SQUERY   = 'SQUERY'
248
+WHO      = 'WHO'
249
+WHOIS    = 'WHOIS'
250
+WHOWAS   = 'WHOWAS'
251
+KILL     = 'KILL'
252
+PING     = 'PING'
253
+PONG     = 'PONG'
254
+ERROR    = 'ERROR'
255
+AWAY     = 'AWAY'
256
+REHASH   = 'REHASH'
257
+DIE      = 'DIE'
258
+RESTART  = 'RESTART'
259
+SUMMON   = 'SUMMON'
260
+USERS    = 'USERS'
261
+WALLOPS  = 'WALLOPS'
262
+USERHOST = 'USERHOST'
263
+ISON     = 'ISON'
264
+
265
+# Event Numerics
266
+RPL_WELCOME          = '001'
267
+RPL_YOURHOST         = '002'
268
+RPL_CREATED          = '003'
269
+RPL_MYINFO           = '004'
270
+RPL_ISUPPORT         = '005'
271
+RPL_TRACELINK        = '200'
272
+RPL_TRACECONNECTING  = '201'
273
+RPL_TRACEHANDSHAKE   = '202'
274
+RPL_TRACEUNKNOWN     = '203'
275
+RPL_TRACEOPERATOR    = '204'
276
+RPL_TRACEUSER        = '205'
277
+RPL_TRACESERVER      = '206'
278
+RPL_TRACESERVICE     = '207'
279
+RPL_TRACENEWTYPE     = '208'
280
+RPL_TRACECLASS       = '209'
281
+RPL_STATSLINKINFO    = '211'
282
+RPL_STATSCOMMANDS    = '212'
283
+RPL_STATSCLINE       = '213'
284
+RPL_STATSILINE       = '215'
285
+RPL_STATSKLINE       = '216'
286
+RPL_STATSYLINE       = '218'
287
+RPL_ENDOFSTATS       = '219'
288
+RPL_UMODEIS          = '221'
289
+RPL_SERVLIST         = '234'
290
+RPL_SERVLISTEND      = '235'
291
+RPL_STATSLLINE       = '241'
292
+RPL_STATSUPTIME      = '242'
293
+RPL_STATSOLINE       = '243'
294
+RPL_STATSHLINE       = '244'
295
+RPL_LUSERCLIENT      = '251'
296
+RPL_LUSEROP          = '252'
297
+RPL_LUSERUNKNOWN     = '253'
298
+RPL_LUSERCHANNELS    = '254'
299
+RPL_LUSERME          = '255'
300
+RPL_ADMINME          = '256'
301
+RPL_ADMINLOC1        = '257'
302
+RPL_ADMINLOC2        = '258'
303
+RPL_ADMINEMAIL       = '259'
304
+RPL_TRACELOG         = '261'
305
+RPL_TRYAGAIN         = '263'
306
+RPL_NONE             = '300'
307
+RPL_AWAY             = '301'
308
+RPL_USERHOST         = '302'
309
+RPL_ISON             = '303'
310
+RPL_UNAWAY           = '305'
311
+RPL_NOWAWAY          = '306'
312
+RPL_WHOISUSER        = '311'
313
+RPL_WHOISSERVER      = '312'
314
+RPL_WHOISOPERATOR    = '313'
315
+RPL_WHOWASUSER       = '314'
316
+RPL_ENDOFWHO         = '315'
317
+RPL_WHOISIDLE        = '317'
318
+RPL_ENDOFWHOIS       = '318'
319
+RPL_WHOISCHANNELS    = '319'
320
+RPL_LIST             = '322'
321
+RPL_LISTEND          = '323'
322
+RPL_CHANNELMODEIS    = '324'
323
+RPL_NOTOPIC          = '331'
324
+RPL_TOPIC            = '332'
325
+RPL_INVITING         = '341'
326
+RPL_INVITELIST       = '346'
327
+RPL_ENDOFINVITELIST  = '347'
328
+RPL_EXCEPTLIST       = '348'
329
+RPL_ENDOFEXCEPTLIST  = '349'
330
+RPL_VERSION          = '351'
331
+RPL_WHOREPLY         = '352'
332
+RPL_NAMREPLY         = '353'
333
+RPL_LINKS            = '364'
334
+RPL_ENDOFLINKS       = '365'
335
+RPL_ENDOFNAMES       = '366'
336
+RPL_BANLIST          = '367'
337
+RPL_ENDOFBANLIST     = '368'
338
+RPL_ENDOFWHOWAS      = '369'
339
+RPL_INFO             = '371'
340
+RPL_MOTD             = '372'
341
+RPL_ENDOFINFO        = '374'
342
+RPL_MOTDSTART        = '375'
343
+RPL_ENDOFMOTD        = '376'
344
+RPL_YOUREOPER        = '381'
345
+RPL_REHASHING        = '382'
346
+RPL_YOURESERVICE     = '383'
347
+RPL_TIME             = '391'
348
+RPL_USERSSTART       = '392'
349
+RPL_USERS            = '393'
350
+RPL_ENDOFUSERS       = '394'
351
+RPL_NOUSERS          = '395'
352
+ERR_NOSUCHNICK       = '401'
353
+ERR_NOSUCHSERVER     = '402'
354
+ERR_NOSUCHCHANNEL    = '403'
355
+ERR_CANNOTSENDTOCHAN = '404'
356
+ERR_TOOMANYCHANNELS  = '405'
357
+ERR_WASNOSUCHNICK    = '406'
358
+ERR_TOOMANYTARGETS   = '407'
359
+ERR_NOSUCHSERVICE    = '408'
360
+ERR_NOORIGIN         = '409'
361
+ERR_NORECIPIENT      = '411'
362
+ERR_NOTEXTTOSEND     = '412'
363
+ERR_NOTOPLEVEL       = '413'
364
+ERR_WILDTOPLEVEL     = '414'
365
+ERR_BADMASK          = '415'
366
+ERR_UNKNOWNCOMMAND   = '421'
367
+ERR_NOMOTD           = '422'
368
+ERR_NOADMININFO      = '423'
369
+ERR_FILEERROR        = '424'
370
+ERR_NONICKNAMEGIVEN  = '431'
371
+ERR_ERRONEUSNICKNAME = '432'
372
+ERR_NICKNAMEINUSE    = '433'
373
+ERR_NICKCOLLISION    = '436'
374
+ERR_USERNOTINCHANNEL = '441'
375
+ERR_NOTONCHANNEL     = '442'
376
+ERR_USERONCHANNEL    = '443'
377
+ERR_NOLOGIN          = '444'
378
+ERR_SUMMONDISABLED   = '445'
379
+ERR_USERSDISABLED    = '446'
380
+ERR_NOTREGISTERED    = '451'
381
+ERR_NEEDMOREPARAMS   = '461'
382
+ERR_ALREADYREGISTRED = '462'
383
+ERR_NOPERMFORHOST    = '463'
384
+ERR_PASSWDMISMATCH   = '464'
385
+ERR_YOUREBANNEDCREEP = '465'
386
+ERR_KEYSET           = '467'
387
+ERR_CHANNELISFULL    = '471'
388
+ERR_UNKNOWNMODE      = '472'
389
+ERR_INVITEONLYCHAN   = '473'
390
+ERR_BANNEDFROMCHAN   = '474'
391
+ERR_BADCHANNELKEY    = '475'
392
+ERR_BADCHANMASK      = '476'
393
+ERR_BANLISTFULL      = '478'
394
+ERR_NOPRIVILEGES     = '481'
395
+ERR_CHANOPRIVSNEEDED = '482'
396
+ERR_CANTKILLSERVER   = '483'
397
+ERR_UNIQOPRIVSNEEDED = '485'
398
+ERR_NOOPERHOST       = '491'
399
+ERR_UMODEUNKNOWNFLAG = '501'
400
+ERR_USERSDONTMATCH   = '502'
401
diff --git a/dickserv/core/database.py b/dickserv/core/database.py
402
new file mode 100644
403
index 0000000..87e4593
404
--- /dev/null
405
+++ b/dickserv/core/database.py
406
@@ -0,0 +1,83 @@
407
+#!/usr/bin/env python
408
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
409
+# database.py
410
+
411
+import os
412
+import sqlite3
413
+
414
+# Globals
415
+db  = sqlite3.connect(os.path.join('data', 'bot.db'), check_same_thread=False)
416
+sql = db.cursor()
417
+
418
+def check():
419
+	tables = sql.execute('SELECT name FROM sqlite_master WHERE type=\'table\'').fetchall()
420
+	if not len(tables):
421
+		sql.execute('CREATE TABLE IGNORE (IDENT TEXT NOT NULL);')
422
+		sql.execute('CREATE TABLE SETTINGS (SETTING TEXT NOT NULL, VALUE INTEGER NOT NULL);')
423
+		sql.execute('INSERT INTO SETTINGS (SETTING,VALUE) VALUES (?, ?)', ('max_results',  5))
424
+		sql.execute('INSERT INTO SETTINGS (SETTING,VALUE) VALUES (?, ?)', ('max_todo',   100))
425
+		sql.execute('INSERT INTO SETTINGS (SETTING,VALUE) VALUES (?, ?)', ('max_todo_per', 5))
426
+		sql.execute('INSERT INTO SETTINGS (SETTING,VALUE) VALUES (?, ?)', ('todo_expire',  7))
427
+		sql.execute('CREATE TABLE TODO (DATE TEXT NOT NULL, IDENT TEXT NOT NULL, DATA TEXT NOT NULL);')
428
+		db.commit()
429
+
430
+class Ignore:
431
+	def add(ident):
432
+		sql.execute('INSERT INTO IGNORE (IDENT) VALUES (?)', (ident,))
433
+		db.commit()
434
+
435
+	def idents():
436
+		return list(item[0] for item in sql.execute('SELECT IDENT FROM IGNORE ORDER BY IDENT ASC').fetchall())
437
+
438
+	def remove(ident):
439
+		sql.execute('DELETE FROM IGNORE WHERE IDENT=?', (ident,))
440
+		db.commit()
441
+
442
+	def reset():
443
+		sql.execute('DROP TABLE IGNORE')
444
+		sql.execute('CREATE TABLE IGNORE (IDENT TEXT NOT NULL);')
445
+		db.commit()
446
+
447
+class Settings:
448
+	def get(setting):
449
+		return sql.execute('SELECT VALUE FROM SETTINGS WHERE SETTING=?', (setting,)).fetchone()[0]
450
+
451
+	def read():
452
+		return sql.execute('SELECT SETTING,VALUE FROM SETTINGS ORDER BY SETTING ASC').fetchall()
453
+
454
+	def settings():
455
+		return list(item[0] for item in sql.execute('SELECT SETTING FROM SETTINGS').fetchall())
456
+
457
+	def update(setting, value):
458
+		sql.execute('UPDATE SETTINGS SET VALUE=? WHERE SETTING=?', (value, setting))
459
+		db.commit()
460
+
461
+class Todo:
462
+	def add(date, ident, data):
463
+		sql.execute('INSERT INTO TODO (DATE,IDENT,DATA) VALUES (?,?,?)', (date, ident, data))
464
+		db.commit()
465
+
466
+	def expire_check():
467
+		todos = set(list(item[0] for item in sql.execute('SELECT DATE FROM TODO').fetchall()))
468
+		for date in todos:
469
+			if functions.timespan(date) > Settings.get('todo_expire'):
470
+				sql.execute('DELETE FROM TODO WHERE DATE=?', (date,))
471
+				db.commit()
472
+
473
+	def idents():
474
+		return list(item[0] for item in sql.execute('SELECT IDENT FROM TODO').fetchall())
475
+
476
+	def read(ident=None):
477
+		if ident:
478
+			return list(item[0] for item in sql.execute('SELECT DATA FROM TODO WHERE IDENT=?', (ident,)).fetchall())
479
+		else:
480
+			return sql.execute('SELECT DATE,IDENT,DATA FROM TODO ORDER BY DATE ASC, IDENT ASC, DATA ASC').fetchall()
481
+
482
+	def remove(ident, data):
483
+		sql.execute('DELETE FROM TODO WHERE IDENT=? AND DATA=?', (ident, data))
484
+		db.commit()
485
+
486
+	def reset():
487
+		sql.execute('DROP TABLE TODO')
488
+		sql.execute('CREATE TABLE TODO (DATE TEXT NOT NULL, IDENT TEXT NOT NULL, DATA TEXT NOT NULL);')
489
+		db.commit()
490
diff --git a/dickserv/core/debug.py b/dickserv/core/debug.py
491
new file mode 100644
492
index 0000000..a178193
493
--- /dev/null
494
+++ b/dickserv/core/debug.py
495
@@ -0,0 +1,90 @@
496
+#!/usr/bin/env python
497
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
498
+# debug.py
499
+
500
+import ctypes
501
+import logging
502
+import os
503
+import sys
504
+import time
505
+
506
+from logging.handlers import RotatingFileHandler
507
+
508
+import config
509
+
510
+def check_libs():
511
+	if config.connection.proxy:
512
+		try:
513
+			import socks
514
+		except ImportError:
515
+			error_exit('Missing \'socks\' module! (https://pypi.python.org/pypi/PySocks)')
516
+	try:
517
+		import bs4
518
+	except ImportError:
519
+		error_exit('Missing \'bs4\' module. (https://pypi.python.org/pypi/beautifulsoup4)')
520
+	try:
521
+		import googleapiclient.discovery
522
+	except ImportError:
523
+		error_exit('Missing \'google-api-python-client\' module. (https://pypi.python.org/pypi/google-api-python-client/)')
524
+
525
+
526
+def check_privileges():
527
+	if check_windows():
528
+		if ctypes.windll.shell32.IsUserAnAdmin() != 0:
529
+			return True
530
+		else:
531
+			return False
532
+	else:
533
+		if os.getuid() == 0 or os.geteuid() == 0:
534
+			return True
535
+		else:
536
+			return False
537
+
538
+def check_version(major):
539
+	if sys.version_info.major == major:
540
+		return True
541
+	else:
542
+		return False
543
+
544
+def check_windows():
545
+	if os.name == 'nt':
546
+		return True
547
+	else:
548
+		return False
549
+
550
+def clear():
551
+	if check_windows():
552
+		os.system('cls')
553
+	else:
554
+		os.system('clear')
555
+
556
+def error(msg, reason=None):
557
+	if reason:
558
+		logging.debug(f'[!] - {msg} ({reason})')
559
+	else:
560
+		logging.debug('[!] - ' + msg)
561
+
562
+def error_exit(msg):
563
+	raise SystemExit('[!] - ' + msg)
564
+
565
+def info():
566
+	clear()
567
+	logging.debug('#'*56)
568
+	logging.debug('#{0}#'.format(''.center(54)))
569
+	logging.debug('#{0}#'.format('DickServ IRC'.center(54)))
570
+	logging.debug('#{0}#'.format('Developed by acidvegas in Python'.center(54)))
571
+	logging.debug('#{0}#'.format('https://acid.vegas/dickserv'.center(54)))
572
+	logging.debug('#{0}#'.format(''.center(54)))
573
+	logging.debug('#'*56)
574
+
575
+def irc(msg):
576
+	logging.debug('[~] - ' + msg)
577
+
578
+def setup_logger():
579
+	stream_handler = logging.StreamHandler(sys.stdout)
580
+	if config.settings.log:
581
+		log_file	 = os.path.join(os.path.join('data','logs'), 'bot.log')
582
+		file_handler = RotatingFileHandler(log_file, maxBytes=256000, backupCount=3)
583
+		logging.basicConfig(level=logging.NOTSET, format='%(asctime)s | %(message)s', datefmt='%I:%M:%S', handlers=(file_handler,stream_handler))
584
+	else:
585
+		logging.basicConfig(level=logging.NOTSET, format='%(asctime)s | %(message)s', datefmt='%I:%M:%S', handlers=(stream_handler,))
586
diff --git a/dickserv/core/functions.py b/dickserv/core/functions.py
587
new file mode 100644
588
index 0000000..c041c03
589
--- /dev/null
590
+++ b/dickserv/core/functions.py
591
@@ -0,0 +1,53 @@
592
+#!/usr/bin/env python
593
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
594
+# functions.py
595
+
596
+import datetime
597
+import random
598
+import re
599
+import time
600
+
601
+def between(source, start, stop):
602
+	data = re.compile(start + '(.*?)' + stop, re.IGNORECASE|re.MULTILINE).search(source)
603
+	if data:
604
+		return data.group(1)
605
+	else:
606
+		return False
607
+
608
+def current_date():
609
+	return time.strftime('%A, %B %d, %Y - %I:%M %p')
610
+
611
+def floatint(data):
612
+    if data.isdigit():
613
+        return int(data)
614
+    else:
615
+        return float(data)
616
+
617
+def get_date():
618
+	return datetime.date.today().strftime('%m/%d/%Y')
619
+
620
+def get_datetime(data):
621
+	return datetime.datetime.strptime(data, '%m/%d/%Y')
622
+
623
+def luck(odds):
624
+	if random_int(1,odds) == 1:
625
+		return True
626
+	else:
627
+		return False
628
+
629
+def random_int(min, max):
630
+	return random.randint(min, max)
631
+
632
+def timespan(date):
633
+	delta = datetime.date(get_date()) - datetime.date(get_datetime(date))
634
+	return delta.days
635
+
636
+def trim(data, max_length):
637
+	if len(data) > max_length:
638
+		return data[:max_length] + '...'
639
+	else:
640
+		return data
641
+
642
+def uptime(start_time):
643
+	uptime = datetime.datetime(1,1,1) + datetime.timedelta(seconds=time.time() - start_time)
644
+	return f'{uptime.day-1} Days, {uptime.hour} Hours, {uptime.minute} Minutes, {uptime.second} Seconds'
645
diff --git a/dickserv/core/httplib.py b/dickserv/core/httplib.py
646
new file mode 100644
647
index 0000000..4c27ad1
648
--- /dev/null
649
+++ b/dickserv/core/httplib.py
650
@@ -0,0 +1,63 @@
651
+#!/usr/bin/env python
652
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
653
+# httplib.py
654
+
655
+import json
656
+import os
657
+import re
658
+import urllib.parse
659
+import urllib.request
660
+
661
+from bs4 import BeautifulSoup
662
+
663
+def clean_url(url):
664
+    for prefix in ('https://', 'http://', 'www.'):
665
+        if url.startswith(prefix):
666
+            url = url[len(prefix):]
667
+    if url[-1:] == '/':
668
+        url = url[:-1]
669
+    return url
670
+
671
+def data_quote(data):
672
+    return urllib.parse.quote(data)
673
+
674
+def data_encode(data):
675
+    return urllib.parse.urlencode(data)
676
+
677
+def get_file(url):
678
+    return os.path.basename(url)
679
+
680
+def get_json(url):
681
+    return json.loads(get_source(url))
682
+
683
+def get_size(url):
684
+    content_length = int(get_url(url).getheader('content-length'))
685
+    for unit in ('B','KB','MB','GB','TB','PB','EB','ZB'):
686
+        if abs(content_length) < 1024.0:
687
+            return '{0:.2f}'.format(content_length) + unit
688
+        content_length /= 1024.0
689
+    return '{0:.2f}'.format(content_length) + 'YB'
690
+
691
+def get_source(url):
692
+    source  = get_url(url)
693
+    charset = source.headers.get_content_charset()
694
+    if charset:
695
+        return source.read().decode(charset)
696
+    else:
697
+        return source.read().decode()
698
+
699
+def get_title(url):
700
+    source = get_source(url)
701
+    soup   = BeautifulSoup(source, 'html.parser')
702
+    return ' '.join(soup.title.string.split())
703
+
704
+def get_type(url):
705
+    return get_url(url).info().get_content_type()
706
+
707
+def get_url(url):
708
+    req = urllib.request.Request(url)
709
+    req.add_header('User-Agent', 'DickServ/1.0')
710
+    return urllib.request.urlopen(req, timeout=10)
711
+
712
+def parse_urls(data):
713
+    return re.compile('(?:http[s]?:\/\/|www.)(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', re.IGNORECASE).findall(data)
714
diff --git a/dickserv/core/irc.py b/dickserv/core/irc.py
715
new file mode 100644
716
index 0000000..c7d4bb3
717
--- /dev/null
718
+++ b/dickserv/core/irc.py
719
@@ -0,0 +1,479 @@
720
+#!/usr/bin/env python
721
+# -*- coding: utf-8 -*-
722
+# DickServ IRC Bot - Developed by acidvegas in Python (https://acid.vegas/dickserv)
723
+# irc.py
724
+
725
+import socket
726
+import time
727
+
728
+import config
729
+import constants
730
+import database
731
+import debug
732
+import functions
733
+import httplib
734
+
735
+from commands import *
736
+
737
+# Load optional modules
738
+if config.connection.ssl:
739
+	import ssl
740
+if config.connection.proxy:
741
+	import sock
742
+
743
+def color(msg, foreground, background=None):
744
+	if foreground == 'random':
745
+		foreground = '{0:0>2}'.format(functions.random_int(2,13))
746
+	if background == 'random':
747
+		background = '{0:0>2}'.format(functions.random_int(2,13))
748
+	if background:
749
+		return f'\x03{foreground},{background}{msg}{constants.reset}'
750
+	else:
751
+		return f'\x03{foreground}{msg}{constants.reset}'
752
+
753
+class IRC(object):
754
+	def __init__(self):
755
+		self.last   = 0
756
+		self.slow	= False
757
+		self.sock	= None
758
+		self.start  = 0
759
+		self.status = True
760
+
761
+	def connect(self):
762
+		try:
763
+			self.create_socket()
764
+			self.sock.connect((config.connection.server, config.connection.port))
765
+			self.register()
766
+		except socket.error as ex:
767
+			debug.error('Failed to connect to IRC server.', ex)
768
+			Events.disconnect()
769
+		else:
770
+			self.listen()
771
+
772
+	def create_socket(self):
773
+		family = socket.AF_INET6 if config.connection.ipv6 else socket.AF_INET
774
+		if config.connection.proxy:
775
+			proxy_server, proxy_port = config.connection.proxy.split(':')
776
+			self.sock = socks.socksocket(family, socket.SOCK_STREAM)
777
+			self.sock.setblocking(0)
778
+			self.sock.settimeout(15)
779
+			self.sock.setproxy(socks.PROXY_TYPE_SOCKS5, proxy_server, int(proxy_port))
780
+		else:
781
+			self.sock = socket.socket(family, socket.SOCK_STREAM)
782
+		if config.connection.vhost:
783
+			self.sock.bind((config.connection.vhost, 0))
784
+		if config.connection.ssl:
785
+			ctx = ssl.SSLContext()
786
+			if config.cert.file:
787
+				ctx.load_cert_chain(config.cert.file, config.cert.key, config.cert.password)
788
+			if config.connection.ssl_verify:
789
+				ctx.verify_mode = ssl.CERT_REQUIRED
790
+				ctx.load_default_certs()
791
+			else:
792
+				ctx.check_hostname = False
793
+				ctx.verify_mode	= ssl.CERT_NONE
794
+			self.sock = ctx.wrap_socket(self.sock)
795
+
796
+	def listen(self):
797
+		while True:
798
+			try:
799
+				data = self.sock.recv(1024).decode('utf-8')
800
+				for line in (line for line in data.split('\r\n') if line):
801
+					debug.irc(line)
802
+					if len(line.split()) >= 2:
803
+						Events.handle(line)
804
+			except (UnicodeDecodeError,UnicodeEncodeError):
805
+				pass
806
+			except Exception as ex:
807
+				debug.error('Unexpected error occured.', ex)
808
+				break
809
+		Events.disconnect()
810
+
811
+	def register(self):
812
+		if config.login.network:
813
+			Commands.raw('PASS ' + config.login.network)
814
+		Commands.raw(f'USER {config.ident.username} 0 * :{config.ident.realname}')
815
+		Commands.nick(config.ident.nickname)
816
+
817
+
818
+
819
+class Commands:
820
+	def action(chan, msg):
821
+		Commands.sendmsg(chan, f'\x01ACTION {msg}\x01')
822
+
823
+	def error(target, data, reason=None):
824
+		if reason:
825
+			Commands.sendmsg(target, '[{0}] {1} {2}'.format(color('!', constants.red), data, color('({0})'.format(reason), constants.grey)))
826
+		else:
827
+			Commands.sendmsg(target, '[{0}] {1}'.format(color('!', constants.red), data))
828
+
829
+	def identify(nick, password):
830
+		Commands.sendmsg('nickserv', f'identify {nick} {password}')
831
+
832
+	def join_channel(chan, key=None):
833
+		Commands.raw(f'JOIN {chan} {key}') if key else Commands.raw('JOIN ' + chan)
834
+
835
+	def mode(target, mode):
836
+		Commands.raw(f'MODE {target} {mode}')
837
+
838
+	def nick(nick):
839
+		Commands.raw('NICK ' + nick)
840
+
841
+	def notice(target, msg):
842
+		Commands.raw(f'NOTICE {target} :{msg}')
843
+
844
+	def oper(user, password):
845
+		Commands.raw(f'OPER {user} {password}')
846
+
847
+	def raw(msg):
848
+		msg = msg.replace('\r','').replace('\n','')[:450]
849
+		DickServ.sock.send(bytes(msg + '\r\n', 'utf-8'))
850
+
851
+	def sendmsg(target, msg):
852
+		Commands.raw(f'PRIVMSG {target} :{msg}')
853
+
854
+
855
+
856
+class Events:
857
+	def connect():
858
+		DickServ.start = time.time()
859
+		if config.settings.modes:
860
+			Commands.mode(config.ident.nickname, '+' + config.settings.modes)
861
+		if config.login.nickserv:
862
+			Commands.identify(config.ident.nickname, config.login.nickserv)
863
+		if config.login.operator:
864
+			Commands.oper(config.ident.username, config.login.operator)
865
+		Commands.join_channel(config.connection.channel, config.connection.key)
866
+
867
+	def disconnect():
868
+		DickServ.sock.close()
869
+		time.sleep(10)
870
+		DickServ.connect()
871
+
872
+	def kick(nick, chan, kicked):
873
+		if kicked == config.ident.nickname and chan == config.connection.channel:
874
+			time.sleep(3)
875
+			Commands.join_channel(chan, config.connection.key)
876
+
877
+	def message(nick, ident, chan, msg):
878
+		try:
879
+			if chan == config.connection.channel and (DickServ.status or ident == config.settings.admin):
880
+				if not msg.startswith(config.settings.cmd_char):
881
+					urls = httplib.parse_urls(msg)
882
+					if urls:
883
+						if time.time() - DickServ.last > 3:
884
+							DickServ.last = time.time()
885
+							Events.url(chan, urls[0])
886
+					elif msg == '@dickserv':
887
+						Commands.sendmsg(chan, constants.bold + 'DickServ IRC Bot - Developed by acidvegas in Python - https://acid.vegas/dickserv')
888
+					elif msg == '@dickserv help':
889
+						Commands.sendmsg(chan, 'https://git.supernets.org/acidvegas/dickserv#commands')
890
+					elif msg == 'h' and functions.luck(4):
891
+						Commands.sendmsg(chan, 'h')
892
+					elif 'qhat' in msg:
893
+						Commands.sendmsg(chan, 'Q:)')
894
+				elif ident not in database.Ignore.idents():
895
+					if time.time() - DickServ.last < 3 and ident != config.settings.admin:
896
+						if not DickServ.slow:
897
+							Commands.sendmsg(chan, color('Slow down nerd!', constants.red))
898
+							DickServ.slow = True
899
+					else:
900
+						DickServ.slow = False
901
+						args = msg.split()
902
+						argz = msg[len(args[0])+1:]
903
+						cmd  = args[0][1:]
904
+						if len(args) == 1:
905
+							if cmd == 'date':
906
+								Commands.sendmsg(chan, functions.current_date())
907
+							elif cmd == 'talent':
908
+								if functions.luck(1000):
909
+									Commands.sendmsg(chan, color(f' !!! HOLY FUCKING SHIT {nick} ACHIEVED TALENT !!! ',               'random', 'random'))
910
+									Commands.sendmsg(chan, color(' !!! RIP DITTLE DIP DIP DIP DIP IT\'S YOUR BIRTHDAY !!! ',          'random', 'random'))
911
+									Commands.sendmsg(chan, color(f' !!! CAN WE HAVE A GOT DAMN MOMENT OF SILENCE FOR {nick} :) !!! ', 'random', 'random'))
912
+									Commands.sendmsg(chan, color(' !!! GOT DAMN XD THIS IS TOO CRAZY LIKE...DAMN HAHA. DAMN. !!! ',   'random', 'random'))
913
+								else:
914
+									Commands.sendmsg(chan, color('(^)', 'random'))
915
+							elif cmd == 'todo':
916
+								todos = database.Todo.read(ident)
917
+								if todos:
918
+									for item in todos:
919
+										Commands.notice(nick, '[{0}] {1}'.format(color(todos.index(item)+1, constants.pink), item))
920
+								else:
921
+									Commands.notice(nick, 'You have no saved todos.')
922
+							elif cmd == 'uptime':
923
+								Commands.sendmsg(chan, functions.uptime(DickServ.start))
924
+						elif len(args) == 2:
925
+							if cmd == 'coin':
926
+								api = cryptocurrency.get(args[1])
927
+								if api:
928
+									Commands.sendmsg(chan, '{0} {1} - ${2:,.2f}'.format(color(api['name'], constants.white), color('({0})'.format(api['symbol']), constants.grey), float(api['price_usd'])))
929
+								else:
930
+									Commands.error(chan, 'Invalid cryptocurrency name!')
931
+							elif cmd == 'drug':
932
+								api = tripsit.drug(args[1])
933
+								if api:
934
+									Commands.sendmsg(chan, '{0} - {1}'.format(color(api['name'], constants.yellow), api['desc']))
935
+								else:
936
+									Commands.error(chan, 'No results found.')
937
+							elif cmd == 'define':
938
+								definition = dictionary.define(args[1])
939
+								if definition:
940
+									Commands.sendmsg(chan, '{0} - {1}: {2}'.format(color('Definition', constants.white, constants.blue), args[1].lower(), definition))
941
+								else:
942
+									Commands.error(chan, 'No results found.')
943
+							elif cmd == 'isup':
944
+								Commands.sendmsg(chan, '{0} is {1}'.format(args[1], isup.check(args[1])))
945
+							elif cmd == 'r':
946
+								api = reddit.read(args[1])
947
+								if api:
948
+									data = list(api.keys())
949
+									for i in data:
950
+										count = str(data.index(i)+1)
951
+										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)))
952
+										Commands.sendmsg(chan, ' - ' + color(api[i]['url'], constants.grey))
953
+								else:
954
+									Commands.error(chan, 'No results found.')
955
+							elif cmd == 'w':
956
+								if args[1].isdigit():
957
+									api = weather.lookup(args[1])
958
+									if api:
959
+										Commands.sendmsg(chan, api)
960
+									else:
961
+										Commands.error(chan, 'No results found.')
962
+								else:
963
+									Commands.error(chan, 'Invalid arguments.')
964
+						if len(args) >= 2:
965
+							if cmd == 'g':
966
+								api = google.search(argz, database.Settings.get('max_results'))
967
+								if api:
968
+									for result in api:
969
+										count = api.index(result)+1
970
+										Commands.sendmsg(chan, '[{0}] {1}'.format(color(count, constants.pink), result['title']))
971
+										Commands.sendmsg(chan, ' - ' + color(result['link'], constants.grey))
972
+								else:
973
+									Commands.error(chan, 'No results found.')
974
+							elif cmd == 'imdb':
975
+								api = imdb.search(argz)
976
+								if api:
977
+									Commands.sendmsg(chan, '{0} {1} {2} {3}'.format(color('Title  :', constants.white), api['Title'], api['Year'], color(api['Rated'], constants.grey)))
978
+									Commands.sendmsg(chan, '{0} {1}{2}'.format(color('Link   :', constants.white), constants.underline, color('https://imdb.com/title/' +  api['imdbID'], constants.light_blue)))
979
+									Commands.sendmsg(chan, '{0} {1}'.format(color('Genre  :', constants.white), api['Genre']))
980
+									if api['imdbRating'] == 'N/A':
981
+										Commands.sendmsg(chan, '{0} {1} 0/10'.format(color('Rating :', constants.white), color('★★★★★★★★★★', constants.grey)))
982
+									else:
983
+										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']))
984
+									Commands.sendmsg(chan, '{0} {1}'.format(color('Plot   :', constants.white), api['Plot']))
985
+								else:
986
+									Commands.error(chan, 'No results found.')
987
+							elif cmd == 'netsplit':
988
+								api = netsplit.search(argz)
989
+								if api:
990
+									data = list(api.keys())
991
+									for i in data:
992
+										count = str(data.index(i)+1)
993
+										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)))
994
+										Commands.sendmsg(chan, color(' - ' + api[i]['topic'], constants.grey))
995
+								else:
996
+									Commands.error(chan, 'No results found.')
997
+							elif cmd == 'todo' and len(args) >= 3:
998
+								if len(args) >= 3 and args[1] == 'add':
999
+									todos = database.Todo.read(ident)