#!/usr/bin/env python
# IRC Class
# Developed by acidvegas in Python 3
# https://github.com/acidvegas/irc
# irc.py

'''
ISC License

Copyright (c) 2016, acidvegas (https://github.com/acidvegas)

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.
'''

'''
IRC RCF 2812 Reference:
 - https://tools.ietf.org/html/rfc2812
'''

import socket
import ssl
import time
import threading
import os
import random

# IRC Config (EDIT)
server   = 'irc.nordunet.se'
#server   = 'irc.supernets.org'
port     = 6667
use_ipv6 = False
#use_ipv6 = False
use_ssl  = False
#vhost = False
vhost    = '217.182.113.30'
password = None
channel  = '#efnetnews'
#channel  = '#dev'
key      = None

# Identity (EDIT)
nickname = 'HUGECOCK'
username = 'HUGECOCK'
realname = 'HUGECOCK'

# Login (EDIT)
nickserv = None
operserv = None

# Globals
ascii_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ascii.txt')
ascii_lines = []

# Formatting Control Characters / Color Codes
bold        = '\x02'
colour      = '\x03'
italic      = '\x1D'
underline   = '\x1F'
reverse     = '\x16'
reset       = '\x0f'
white       = '00'
black       = '01'
blue        = '02'
green       = '03'
red         = '04'
brown       = '05'
purple      = '06'
orange      = '07'
yellow      = '08'
light_green = '09'
cyan        = '10'
light_cyan  = '11'
light_blue  = '12'
pink        = '13'
grey        = '14'
light_grey  = '15'

def color(msg, foreground, background=None):
    if background:
    	return '%s%s,%s%s%s' % (colour, foreground, background, msg, reset)
    else:
    	return '%s%s%s%s' % (colour, foreground, msg, reset)

def debug(msg):
    print('%s | [~] - %s' % (get_time(), msg))

def error(msg, reason=None):
    if reason:
        print('%s | [!] - %s (%s)' % (get_time(), msg, str(reason)))
    else:
    	print('%s | [!] - %s' % (get_time(), msg))

def error_exit(msg):
    raise SystemExit('%s | [!] - %s' % (get_time(), msg))    

def get_time():
    return time.strftime('%I:%M:%S')

def random_int(min, max):
    return random.randint(min, max)

class IRC(object):
    def __init__(self, server, port, use_ipv6, use_ssl, vhost, password, channel, key, nickname, username, realname, nickserv, operserv):
        self.server   = server
        self.port     = port
        self.use_ipv6 = use_ipv6
        self.use_ssl  = use_ssl
        self.vhost    = vhost
        self.password = password
        self.channel  = channel
        self.key      = key
        self.nickname = nickname
        self.username = username
        self.realname = realname
        self.nickserv = nickserv
        self.operserv = operserv
        self.connected = False
        self.sock     = None
        self.last_time = 0
        
    def random_output(self):
        time.sleep(60)
        while self.connected:
            self.sendmsg(self.channel, random.choice(ascii_lines))
            time.sleep(60*random_int(20,300))

    def action(self, chan, msg):
        self.sendmsg(chan, '\x01ACTION %s\x01' % msg)
        
    def connect(self):
        try:
            self.create_socket()
            self.sock.connect((self.server, self.port))
            if self.password:
            	self.raw('PASS ' + self.password)
            self.raw('USER %s 0 * :%s' % (self.username, self.realname))
            self.nick(self.nickname)
        except Exception as ex:
            error('Failed to connect to IRC server.', ex)
            self.event_disconnect()
        else:
            self.listen()

    def create_socket(self):
        if self.use_ipv6:
            family = socket.AF_INET6
        else:
            family = socket.AF_INET
        self.sock = socket.socket(family, socket.SOCK_STREAM)
        if self.vhost:
            self.sock.bind((self.vhost, 0))
        if self.use_ssl:
            self.sock = ssl.wrap_socket(self.sock)

    def ctcp(self, target, data):
        self.sendmsg(target, '\001%s\001' % data)

    def disconnect(self):
        if self.sock:
            try:
            	self.sock.shutdown(socket.SHUT_RDWR)
            except:
                pass
            self.sock.close()
            self.sock = None

    def event_connect(self):
        self.connected = True
        if self.nickserv:
            self.identify(self.username, self.nickserv)
        if self.operserv:
            self.oper(self.username, self.operserv)
        self.join(self.channel, self.key)
        threading.Thread(target=self.random_output).start()

    def event_ctcp(self, nick, chan, msg):
    	pass

    def event_disconnect(self):
        self_connected = False
        self.disconnect()
        time.sleep(5)
        self.connect()

    def event_invite(self, nick, chan):
        pass

    def event_join(self, nick, chan): 
        print('%s joins %s' % (nick, chan))
#        if chan == '#dev':
#            self.sendmsg(chan, 'hey %s ur:' % (nick))
#            time.sleep(2) # delay 10 seconds
#            self.sendmsg(chan, 'G')
#            time.sleep(2) # delay 10 seconds
#            self.sendmsg(chan, 'A')
#            time.sleep(2) # delay 10 seconds
#            self.sendmsg(chan, 'Y')
#            time.sleep(2) # delay 10 seconds
#            self.sendmsg(chan, 'and all of %s hates u' % (chan))
#        elif chan == '#dev':
#            self.sendmsg(nick, 'yo real talk part %s they will just ruin ur life eventually' % (chan))
        if nick == 'ZARDOZ':
            self.sendmsg(chan, '%s' % (nick))
        self.notice(nick, 'Thank you for joining EFNETNEWS, you have \x03043 {1}memos waiting. Please type {2}/server MemoServ read {1}to check your messages.'.format(red, reset, bold))

    def event_kick(self, nick, chan, kicked):
        print('%s kicked someone in %s amd that person was: %s' % (nick, chan, kicked)) 
        if kicked == self.nickname:
            if chan == self.channel:
                self.join(self.channel, self.key)

    def event_message(self, nick, chan, msg):
        print('%s in "%s" just said "%s"' % (nick, chan, msg))
        if 'http' in msg:
            if random.choice((True, False, False, False)):
                self.sendmsg(chan, '\x1Fnot clicking')
        elif msg == 'h':
            if random.choice((True, False, False, False)):
                self.sendmsg(chan, 'h')
        elif msg == 'pump':
            if random.choice((True, False, False, False)):
                self.sendmsg(chan, 'penis')
        elif msg == 'penis':
            if random.choice((True, False, False, False)):
                self.sendmsg(chan, 'pump')
        elif 'ddos' in msg:
            if random.choice((True, False, False, False)):
                self.sendmsg(chan, 'Dudes Drink Owl Sperm')
        elif 'fag' in msg:
            if random.choice((True, False, False, False)):
                self.sendmsg(chan, 'i hate faggots more than i hate war in this world')
        elif self.nickname in msg:
            if random.choice((True, False, False, False)):
                self.action(chan, '8=================================================================D')
        elif msg == 'lol':
            if random.choice((True, False, False, False)):
                self.sendmsg(chan, 'lol')
        elif msg == 'Lol':
            if random.choice((True, False, False, False)):
                self.sendmsg(chan, 'Lol')
        elif msg == 'LOL':
            if random.choice((True, False, False, False)):
                self.sendmsg(chan, 'LOL')
        elif msg == '%%':
            if random.choice((True, False, False, False)):
                self.sendmsg(chan, '%%')

    def event_nick_in_use(self):
        self.nickname = self.nickname + '_'
        self.nick(self.nickname)

    def event_part(self, nick, chan):
        self.sendmsg(nick, 'bet u wont come back pussy')
        self.sendmsg(chan, '\x0304,08EMOPART DETECTED')

    def event_private(self, nick, host, msg):
        print('%s %s %s' % (nick, host, msg))
        self.sendmsg('vapor', '%s is msging HUGECOCK: %s' % (nick, msg))

    def event_quit(self, nick, reason):
        print('%s quit with reason "%s"' % (nick, reason))
        if time.time() - self.last_time > 15:
            time.sleep(2)
            self.nick(nick)
            self.nickname = nick        
            self.sendmsg(self.channel, 'GOT EEEEEm')
            self.last_time = time.time()

    def handle_events(self, data):
        args = data.split()
        if args[0] == 'PING':
            self.raw('PONG ' + args[1][1:])
        elif args[1] == '001': # Use 002 or 003 if you run into issues.
            self.event_connect() 
        elif args[1] == '433':
            self.event_nick_in_use()
        elif args[1] in ('INVITE','JOIN','KICK','PART','PRIVMSG','QUIT'):
            name = args[0].split('!')[0][1:]
            if name != self.nickname:
                if args[1] == 'INVITE':
                    chan = args[3][1:]
                    self.event_invite(name, chan)
                elif args[1] == 'JOIN':
                    chan = args[2][1:]
                    self.event_join(name, chan)
                elif args[1] == 'KICK':
                    chan   = args[2]
                    kicked = args[3]
                    self.event_kick(name, chan, kicked)
                elif args[1] == 'PART':
                    chan   = args[2]
                    self.event_part(name, chan)
                elif args[1] == 'PRIVMSG':
                    chan = args[2]
                    msg  = data.split(args[1] + ' ' + chan + ' :')[1]
                    host = args[0].split('!')[1].split('@')[1]
                    if msg.startswith('\001'):
                    	self.event_ctcp(name, chan, msg)
                    elif chan == self.nickname:
                        self.event_private(name, host, msg)
                    else:
                    	self.event_message(name, chan, msg)
                elif args[1] == 'QUIT':
                    reason = data.split('QUIT :')[1]
                    self.event_quit(name, reason)

    def identify(self, username, password):
        self.sendmsg('nickserv', 'identify %s %s' % (username, password))

    def invite(self, nick, chan):
        self.raw('INVITE %s %s' % (nick, chan))

    def join(self, chan, key=None):
        if key:
            self.raw('JOIN %s %s' % (chan, key))
        else:
            self.raw('JOIN ' + chan)

    def listen(self):
        while True:
            try:
                data = self.sock.recv(1024)
                for line in data.split(b'\r\n'):
                    if line:
                        try:
                            line = line.decode('utf-8')
                        except:
                            pass
                        debug(line)
                        if len(line.split()) >= 2:
                            self.handle_events(line)
                if b'Closing Link' in data and bytes(self.nickname, 'utf-8') in data:
                    break
            except (UnicodeDecodeError,UnicodeEncodeError):
                pass
            except Exception as ex:
                error('Unexpected error occured.', ex)
                break
        self.event_disconnect()

    def mode(self, target, mode):
        self.raw('MODE %s %s' % (target, mode))

    def nick(self, nick):
        self.raw('NICK ' + nick)

    def notice(self, target, msg):
        self.raw('NOTICE %s :%s' % (target, msg))

    def oper(self, nick, password):
        self.raw('OPER %s %s' % (nick, password))

    def part(self, chan, msg=None):
        if msg:
            self.raw('PART %s %s' % (chan, msg))
        else:
            self.raw('PART ' + chan)

    def quit(self, msg=None):
        if msg:
            self.raw('QUIT :' + msg)
        else:
            self.raw('QUIT')
	
    def raw(self, msg):
        self.sock.send(bytes(msg + '\r\n', 'utf-8'))
        
    def sendmsg(self, target, msg):
        self.raw('PRIVMSG %s :%s' % (target, msg))
        
    def topic(self, chan, text):
        self.raw('TOPIC %s :%s' % (chan, text))

if os.path.isfile(ascii_file):
    with open(ascii_file, mode='r', encoding='utf8', errors='replace') as ascii__file:
        lines = ascii__file.read().splitlines()
        for line in [x for x in lines if x]:
            ascii_lines.append(line)
else:
    error_exit('Missing ascii file!')

DevBot = IRC(server, port, use_ipv6, use_ssl, vhost, password, channel, key, nickname, username, realname, nickserv, operserv)
DevBot.connect()
