424 lines
18 KiB
Python
424 lines
18 KiB
Python
|
#!/usr/bin/env python3
|
||
|
|
||
|
# Initially 'jupiter' by acidvegas
|
||
|
# Furthered by delorean
|
||
|
#
|
||
|
# irc.supernets.org #superbowl
|
||
|
|
||
|
import random
|
||
|
import re
|
||
|
import socket
|
||
|
import ssl
|
||
|
import time
|
||
|
import threading
|
||
|
|
||
|
# Servers
|
||
|
servers = (
|
||
|
{'server':'efnet.deic.eu', 'ssl':6697, 'ipv6': True},
|
||
|
{'server':'efnet.port80.se', 'ssl':6697, 'ipv6': True},
|
||
|
#{'server':'efnet.portlane.se', 'ssl':6697, 'ipv6': True}, # Removed (efnet.portlane.se is an alias for irc.efnet.org)
|
||
|
{'server':'irc.choopa.net', 'ssl':9000, 'ipv6': True},
|
||
|
{'server':'irc.colosolutions.net', 'ssl':None, 'ipv6':False}, # error: SSL handshake failed: unsafe legacy renegotiation disabled
|
||
|
{'server':'irc.du.se', 'ssl':None, 'ipv6':False}, # error: handshake failed: dh key too small
|
||
|
#{'server':'irc.efnet.fr', 'ssl':6697, 'ipv6': True}, # Removed (irc.efnet.fr is an alias for irc.efnet.nl)
|
||
|
{'server':'irc.efnet.nl', 'ssl':6697, 'ipv6': True},
|
||
|
{'server':'irc.homelien.no', 'ssl':6697, 'ipv6': True},
|
||
|
{'server':'irc.mzima.net', 'ssl':6697, 'ipv6': True},
|
||
|
#{'server':'irc.nordunet.se', 'ssl':6697, 'ipv6': True}, # Removed (irc.nordunet.se is an alias for irc.swepipe.se)
|
||
|
{'server':'irc.prison.net', 'ssl':None, 'ipv6':False},
|
||
|
{'server':'irc.swepipe.se', 'ssl':6697, 'ipv6': True},
|
||
|
{'server':'irc.underworld.no', 'ssl':6697, 'ipv6': True},
|
||
|
{'server':'irc.servercentral.net', 'ssl':9999, 'ipv6':False}
|
||
|
)
|
||
|
|
||
|
ipv6 = True # Set to False if your system does not have an IPv6 address
|
||
|
channel = "#nword"
|
||
|
key = "$$$"
|
||
|
|
||
|
# Settings
|
||
|
admin = ["*rean!*@*"] # Can use wildcards (Must be in nick!user@host format)
|
||
|
concurrency = 4 # Number of clones to load per server
|
||
|
id = "dev" # Unique ID so you can tell which bots belong what server
|
||
|
|
||
|
# Formatting Control Characters / Color Codes
|
||
|
bold = "\x02"
|
||
|
reset = "\x0f"
|
||
|
green = "03"
|
||
|
red = "04"
|
||
|
purple = "06"
|
||
|
orange = "07"
|
||
|
yellow = "08"
|
||
|
light_green = "09"
|
||
|
cyan = "10"
|
||
|
light_cyan = "11"
|
||
|
light_blue = "12"
|
||
|
pink = "13"
|
||
|
grey = "14"
|
||
|
|
||
|
# Globals
|
||
|
bots = list()
|
||
|
|
||
|
def botlist(nick):
|
||
|
global bots
|
||
|
if nick[:1] == "+":
|
||
|
bots.append(nick[1:])
|
||
|
elif nick[:1] == "-":
|
||
|
bots.remove(nick[1:])
|
||
|
|
||
|
def color(msg, foreground, background=None):
|
||
|
return (f"\x03{foreground},{background}{msg}{reset}"
|
||
|
if background
|
||
|
else f"\x03{foreground}{msg}{reset}"
|
||
|
)
|
||
|
|
||
|
def debug(msg):
|
||
|
print(f"{get_time()} | [~] - {msg}")
|
||
|
|
||
|
def error(msg, reason=None):
|
||
|
print(f"{get_time()} | [!] - {msg} ({reason})") if reason else print(f"{get_time()} | [!] - {msg}")
|
||
|
|
||
|
def get_time():
|
||
|
return time.strftime("%I:%M:%S")
|
||
|
|
||
|
def is_admin(ident):
|
||
|
for i in admin:
|
||
|
if re.compile(i.replace('*','.*')).search(ident):
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def random_nick():
|
||
|
prefix = random.choice(['st','sn','cr','pl','pr','fr','fl','qu','br','gr','sh','sk','tr','kl','wr','bl']+list('bcdfgklmnprstvwz'))
|
||
|
midfix = random.choice(('aeiou'))+random.choice(('aeiou'))+random.choice(('bcdfgklmnprstvwz'))
|
||
|
suffix = random.choice(['ed','est','er','le','ly','y','ies','iest','ian','ion','est','ing','led','inger']+list('abcdfgklmnprstvwz'))
|
||
|
return prefix+midfix+suffix
|
||
|
|
||
|
def unicode():
|
||
|
msg='\u202e\u0007\x03' + str(random.randint(2,14))
|
||
|
for i in range(random.randint(150, 200)):
|
||
|
msg += chr(random.randint(0x1000,0x3000))
|
||
|
return msg
|
||
|
|
||
|
class clone(threading.Thread):
|
||
|
def __init__(self, server, addr_type):
|
||
|
self.addr_type = addr_type
|
||
|
self.landmine = None
|
||
|
self.nickname = random_nick()
|
||
|
self.monlist = list()
|
||
|
self.relaylist = list()
|
||
|
self.coverlist = list()
|
||
|
self.autoop = False
|
||
|
self.server = server
|
||
|
self.port = 6667
|
||
|
self.relay = None
|
||
|
self.sock = None
|
||
|
self.ssl_status = None
|
||
|
threading.Thread.__init__(self)
|
||
|
|
||
|
def run(self):
|
||
|
time.sleep(random.randint(300, 900))
|
||
|
self.connect()
|
||
|
|
||
|
def connect(self):
|
||
|
try:
|
||
|
self.create_socket()
|
||
|
self.sock.connect((self.server["server"], self.port))
|
||
|
self.raw(f"USER {random_nick()} 0 * :{random_nick()}")
|
||
|
self.nick(self.nickname)
|
||
|
except socket.error as ex:
|
||
|
error("Failed to connect to '{0}' IRC server.".format(self.server["server"]), ex)
|
||
|
self.event_disconnect()
|
||
|
except ssl.SSLError as ex:
|
||
|
error("Failed to connect to '{0}' IRC server using SSL/TLS.".format(self.server["server"]), ex)
|
||
|
self.ssl_status = False
|
||
|
self.event_disconnect()
|
||
|
else:
|
||
|
self.listen()
|
||
|
|
||
|
def create_socket(self):
|
||
|
self.sock = socket.socket(self.addr_type)
|
||
|
if self.server["ssl"]:
|
||
|
if self.ssl_status:
|
||
|
self.port = self.server["ssl"]
|
||
|
self.sock = ssl.wrap_socket(self.sock)
|
||
|
else:
|
||
|
self.port = 6667
|
||
|
self.ssl_status = True
|
||
|
|
||
|
def event_connect(self):
|
||
|
if self.nickname not in bots:
|
||
|
botlist("+" + self.nickname)
|
||
|
if self.monlist:
|
||
|
self.monitor("+", self.monlist)
|
||
|
self.join_channel(channel, key)
|
||
|
|
||
|
def event_ctcp(self, nick, target, msg):
|
||
|
if target == self.nickname:
|
||
|
if msg == 'VERSION':
|
||
|
version = random.choice('Wal-Mart IRC Client','mIRC v6.35 Khaled Mardam-Bey','xchat 0.24.1 Linux 2.6.27-8-eeepc i686','rZNC Version 1.0 [02/01/11] - Built from ZNC','thelounge v3.0.0 -- https://thelounge.chat/')
|
||
|
self.raw(f'NOTICE {nick} \001VERSION {version}\001')
|
||
|
else:
|
||
|
self.sendmsg(channel, '[{0}] {1}{2}{3} {4}'.format(color('CTCP', green), color('<', grey), color(nick, yellow), color('>', grey), msg))
|
||
|
|
||
|
def event_disconnect(self):
|
||
|
if self.nickname in bots:
|
||
|
botlist("-" + self.nickname)
|
||
|
self.sock.close()
|
||
|
time.sleep(86400 + random.randint(1800, 3600))
|
||
|
self.connect()
|
||
|
|
||
|
def event_join(self, ident, nick, chan):
|
||
|
if is_admin(ident) and self.autoop:
|
||
|
self.raw("MODE {0} +o {1}".format(chan, nick))
|
||
|
if chan == self.landmine:
|
||
|
for x in range(0, 6):
|
||
|
self.sendmsg(chan, f"{unicode()} oh god {nick} what is happening {unicode()}")
|
||
|
self.sendmsg(nick, f"{unicode()} oh god {nick} what is happening {unicode()}")
|
||
|
|
||
|
def event_nick(self, nick, new_nick):
|
||
|
if nick == self.nickname:
|
||
|
self.nickname = new_nick
|
||
|
if new_nick in self.monlist:
|
||
|
self.monitor("C")
|
||
|
self.monlist = list()
|
||
|
elif nick in self.monlist:
|
||
|
self.nick(nick)
|
||
|
|
||
|
def event_nick_in_use(self, nick, target_nick):
|
||
|
if nick == "*":
|
||
|
self.nickname = random_nick()
|
||
|
self.nick(self.nickname)
|
||
|
|
||
|
def event_notice(self, nick, target, msg):
|
||
|
if target == self.nickname:
|
||
|
self.sendmsg(channel, '[{0}] {1}{2}{3} {4}'.format(color('NOTICE', purple), color('<', grey), color(nick, yellow), color('>', grey), msg))
|
||
|
|
||
|
def event_message(self, ident, nick, target, msg):
|
||
|
if target.lower() in self.relaylist:
|
||
|
self.sendmsg(channel, "[{0}] <{1}> {2}".format(color(target, light_cyan), color(nick, purple), msg))
|
||
|
if is_admin(ident):
|
||
|
args = msg.split()
|
||
|
if args[0] in ("@all", self.nickname) and len(args) >= 2:
|
||
|
if len(args) == 2:
|
||
|
if args[1] == "id":
|
||
|
self.sendmsg(target, id)
|
||
|
elif len(args) == 3:
|
||
|
if args[1] == "5000":
|
||
|
chan = args[2]
|
||
|
if chan == "stop":
|
||
|
self.landmine = None
|
||
|
self.sendmsg(target, "5000 mode turned off")
|
||
|
elif chan[:1] == "#":
|
||
|
self.landmine = chan
|
||
|
self.sendmsg(target, "5000 mode actived on " + color(chan, cyan))
|
||
|
elif args[1] == 'migrate':
|
||
|
if args[2][:1] == '#':
|
||
|
channel = args[2]
|
||
|
self.sendmsg(target, 'hub migrated to {0}'.format(color(args[2], light_green)))
|
||
|
elif args[1] == 'auto-op':
|
||
|
if args[2][:2].lower() == 'on':
|
||
|
self.autoop = True
|
||
|
self.sendmsg(channel, 'auto opping {0}'.format(color("enabled", light_green)))
|
||
|
elif args[2][:3].lower() == 'off':
|
||
|
self.autoop = False
|
||
|
self.sendmsg(channel, 'auto opping {0}'.format(color(args[2][1:], red)))
|
||
|
elif args[1] == 'admin':
|
||
|
adstr = args[2][1:] + "!*@*"
|
||
|
if args[2][:1] == '+':
|
||
|
if adstr in admin:
|
||
|
self.sendmsg(target, '{0}'.format(color('user is already an admin', red)))
|
||
|
else:
|
||
|
admin.append(adstr)
|
||
|
self.sendmsg(target, '{0} promoted to {1}'.format(color(args[2][1:], light_green), color("admin", light_green)))
|
||
|
elif args[2][:1] == '-':
|
||
|
if adstr not in admin:
|
||
|
self.sendmsg(target, '{0}'.format(color('user was not an admin', red)))
|
||
|
else:
|
||
|
admin.remove(adstr)
|
||
|
self.sendmsg(target, '{0} admin {1}'.format(color('removed', red), color(args[2][1:], red)))
|
||
|
elif args[1] == 'monitor':
|
||
|
if args[2] == 'list' and self.monlist:
|
||
|
self.sendmsg(target, '[{0}] {1}'.format(color('Monitor', light_blue), ', '.join(self.monlist)))
|
||
|
elif args[2] == 'reset' and self.monlist:
|
||
|
self.monitor('C')
|
||
|
self.monlist = list()
|
||
|
self.sendmsg(target, '{0} nick(s) have been {1} from the monitor list.'.format(color(str(len(self.monlist)), cyan), color('removed', red)))
|
||
|
elif args[2][:1] == '+':
|
||
|
nicks = [mon_nick for mon_nick in set(args[2][1:].split(',')) if mon_nick not in self.monlist]
|
||
|
if nicks:
|
||
|
self.monitor('+', nicks)
|
||
|
self.monlist += nicks
|
||
|
self.sendmsg(target, '{0} nick(s) have been {1} to the monitor list.'.format(color(str(len(nicks)), cyan), color('added', green)))
|
||
|
elif args[2][:1] == '-':
|
||
|
nicks = [mon_nick for mon_nick in set(args[2][1:].split(',')) if mon_nick in self.monlist]
|
||
|
if nicks:
|
||
|
self.monitor('-', nicks)
|
||
|
for mon_nick in nicks:
|
||
|
self.monlist.remove(mon_nick)
|
||
|
self.sendmsg(target, '{0} nick(s) have been {1} from the monitor list.'.format(color(str(len(nicks)), cyan), color('removed', red)))
|
||
|
elif args[1] == 'relay':
|
||
|
if args[2][:1] == '+':
|
||
|
if args[2][1:].lower() in self.relaylist:
|
||
|
self.sendmsg(channel, '{0}'.format(color('already snitching on that channel', red)))
|
||
|
else:
|
||
|
self.relaylist.append(args[2][1:].lower())
|
||
|
self.sendmsg(channel, '{0} on {1}'.format(color('snitching', red), color(args[2][1:], red)))
|
||
|
elif args[2][:1] == '-':
|
||
|
if args[2][1:].lower() == 'all':
|
||
|
self.sendmsg(channel, '{0} on {1} {2}'.format(color('ceased snitching', red), color('all', yellow), color('channels', red)))
|
||
|
self.relaylist.clear()
|
||
|
elif args[2][1:].lower() not in self.relaylist:
|
||
|
self.sendmsg(channel, '{0}'.format(color('channel was not being snitched on', red)))
|
||
|
else:
|
||
|
self.relaylist.remove(args[2][1:].lower())
|
||
|
self.sendmsg(channel, '{0} on {1}'.format(color('ceased all snitching', red), color(args[2][1:], red)))
|
||
|
elif args[1] == 'cover':
|
||
|
if args[2][:1] == '+':
|
||
|
if args[2][1:].lower() in self.coverlist:
|
||
|
self.sendmsg(channel, '{0}'.format(color('already watching that nigga\'s back', red)))
|
||
|
else:
|
||
|
self.coverlist.append(args[2][1:].lower())
|
||
|
self.sendmsg(channel, 'got your back, {0}'.format(color(args[2][1:], light_green)))
|
||
|
elif args[2][:1] == '-':
|
||
|
if args[2][1:].lower() == 'all':
|
||
|
self.sendmsg(channel, '{0} {1} {2}'.format(color('ceased covering', red), color('all', grey), color('muhfuckas', red)))
|
||
|
self.coverlist.clear()
|
||
|
elif args[2][1:].lower() not in self.coverlist:
|
||
|
self.sendmsg(channel, '{0}'.format(color('didnt have their back in the first place', red)))
|
||
|
else:
|
||
|
self.coverlist.remove(args[2][1:].lower())
|
||
|
self.sendmsg(channel, 'no longer watching {0}\'s back'.format(color(args[2][1:], light_green)))
|
||
|
elif len(args) >= 4 and args[1] == "raw":
|
||
|
if args[2] == "-d":
|
||
|
data = " ".join(args[3:])
|
||
|
threading.Thread(target=self.raw, args=(data, True)).start()
|
||
|
else:
|
||
|
data = " ".join(args[2:])
|
||
|
self.raw(data)
|
||
|
|
||
|
elif target == self.nickname:
|
||
|
if msg.startswith('\x01ACTION'):
|
||
|
self.sendmsg(channel, '[{0}] {1}{2}{3} * {4}'.format(color('PM', red), color('<', grey), color(nick, yellow), color('>', grey), msg[8:][:-1]))
|
||
|
else:
|
||
|
self.sendmsg(channel, '[{0}] {1}{2}{3} {4}'.format(color('PM', red), color('<', grey), color(nick, yellow), color('>', grey), msg))
|
||
|
|
||
|
def event_kick(self, ident, nick, chan, target):
|
||
|
if target == self.nickname:
|
||
|
self.sendmsg(channel, "{0} from {1} by {2} {3}".format(color("kicked", yellow), color(chan, red), color(nick, yellow), color(";(", white)))
|
||
|
elif target.lower() in self.coverlist and not is_admin(ident):
|
||
|
self.raw("KICK {0} {1} :that shit outta here".format(chan, nick))
|
||
|
|
||
|
def event_mode(self, nick, chan, modes):
|
||
|
# Todo: takeover mode to automatically jupe a channel upon +o
|
||
|
pass
|
||
|
|
||
|
def event_quit(self, nick):
|
||
|
if nick in self.monlist:
|
||
|
self.nick(nick)
|
||
|
|
||
|
def handle_events(self, data):
|
||
|
args = data.split()
|
||
|
if data.startswith("ERROR :Closing Link:"):
|
||
|
raise Exception("Connection has closed.")
|
||
|
elif data.startswith("ERROR :Reconnecting too fast"):
|
||
|
raise Exception("Connection has closed. (throttled)")
|
||
|
elif args[0] == "PING":
|
||
|
self.raw("PONG " + args[1][1:])
|
||
|
elif args[1] == "001": # RPL_WELCOME
|
||
|
self.event_connect()
|
||
|
elif args[1] == "433" and len(args) >= 4: # ERR_NICKNAMEINUSE
|
||
|
nick = args[2]
|
||
|
target_nick = args[3]
|
||
|
self.event_nick_in_use(nick, target_nick)
|
||
|
elif args[1] == "731" and len(args) >= 4: # RPL_MONOFFLINE
|
||
|
nick = args[3][1:]
|
||
|
self.nick(nick)
|
||
|
elif args[1] == "JOIN" and len(args) == 3:
|
||
|
ident = args[0][1:]
|
||
|
nick = args[0].split("!")[0][1:]
|
||
|
chan = args[2][1:]
|
||
|
self.event_join(ident, nick, chan)
|
||
|
elif args[1] == "MODE" and len(args) >= 4:
|
||
|
nick = args[0].split("!")[0][1:]
|
||
|
chan = args[2]
|
||
|
modes = " ".join(args[:3])
|
||
|
self.event_mode(nick, chan, modes)
|
||
|
elif args[1] == "NICK" and len(args) == 3:
|
||
|
nick = args[0].split("!")[0][1:]
|
||
|
new_nick = args[2][1:]
|
||
|
self.event_nick(nick, new_nick)
|
||
|
elif args[1] == "NOTICE":
|
||
|
nick = args[0].split("!")[0][1:]
|
||
|
target = args[2]
|
||
|
msg = " ".join(args[3:])[1:]
|
||
|
self.event_notice(nick, target, msg)
|
||
|
elif args[1] == "PRIVMSG" and len(args) >= 4:
|
||
|
ident = args[0][1:]
|
||
|
nick = args[0].split("!")[0][1:]
|
||
|
target = args[2]
|
||
|
msg = " ".join(args[3:])[1:]
|
||
|
if msg[:1] == "\001":
|
||
|
msg = msg[1:]
|
||
|
self.event_ctcp(nick, target, msg)
|
||
|
else:
|
||
|
self.event_message(ident, nick, target, msg)
|
||
|
elif args[1] == "QUIT":
|
||
|
nick = args[0].split("!")[0][1:]
|
||
|
self.event_quit(nick)
|
||
|
elif args[1] == "KICK":
|
||
|
ident = args[0][1:]
|
||
|
nick = args[0].split("!")[0][1:]
|
||
|
chan = args[2]
|
||
|
target = args[3]
|
||
|
self.event_kick(ident, nick, chan, target)
|
||
|
|
||
|
def join_channel(self, chan, key=None):
|
||
|
self.raw(f"JOIN {chan} {key}") if key else self.raw("JOIN " + chan)
|
||
|
|
||
|
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 len(line.split()) >= 2):
|
||
|
# debug(line)
|
||
|
self.handle_events(line)
|
||
|
except (UnicodeDecodeError, UnicodeEncodeError):
|
||
|
pass
|
||
|
except Exception as ex:
|
||
|
error("Unexpected error occured on '{0}' server.".format(self.server["server"]),ex)
|
||
|
break
|
||
|
self.event_disconnect()
|
||
|
|
||
|
def mode(self, target, mode):
|
||
|
self.raw(f"MODE {target} {mode}")
|
||
|
|
||
|
def monitor(self, action, nicks=list()):
|
||
|
self.raw(f"MONITOR {action} " + ",".join(nicks))
|
||
|
|
||
|
def nick(self, nick):
|
||
|
self.raw("NICK " + nick)
|
||
|
|
||
|
def raw(self, data, delay=False):
|
||
|
if delay:
|
||
|
time.sleep(random.randint(300, 900))
|
||
|
self.sock.send(bytes(data + "\r\n", "utf-8"))
|
||
|
|
||
|
def sendmsg(self, target, msg):
|
||
|
self.raw(f"PRIVMSG {target} :{msg}")
|
||
|
|
||
|
|
||
|
# Main
|
||
|
for i in range(concurrency):
|
||
|
for server in servers:
|
||
|
clone(server, socket.AF_INET).start()
|
||
|
if ipv6:
|
||
|
if set(
|
||
|
[
|
||
|
ip[4][0]
|
||
|
for ip in socket.getaddrinfo(server["server"], 6667)
|
||
|
if ":" in ip[4][0]
|
||
|
]
|
||
|
):
|
||
|
clone(server, socket.AF_INET6).start()
|
||
|
while True:
|
||
|
input("")
|