archive

- Random tools & helpful resources for IRC
git clone git://git.acid.vegas/archive.git
Log | Files | Refs | Archive

irc.py (20620B)

      1 #!/usr/bin/env python
      2 # IRC Services (IRCS) - Developed by acidvegas in Python (https://acid.vegas/ircs)
      3 # irc.py
      4 
      5 import socket
      6 import ssl
      7 import time
      8 
      9 import config
     10 import debug
     11 from functions import Database, ChanServ, HostServ
     12 
     13 # Formatting Control Characters / Color Codes
     14 bold        = '\x02'
     15 italic      = '\x1D'
     16 underline   = '\x1F'
     17 reverse     = '\x16'
     18 reset       = '\x0f'
     19 white       = '00'
     20 black       = '01'
     21 blue        = '02'
     22 green       = '03'
     23 red         = '04'
     24 brown       = '05'
     25 purple      = '06'
     26 orange      = '07'
     27 yellow      = '08'
     28 light_green = '09'
     29 cyan        = '10'
     30 light_cyan  = '11'
     31 light_blue  = '12'
     32 pink        = '13'
     33 grey        = '14'
     34 light_grey  = '15'
     35 
     36 class IRC(object):
     37     server       = config.server
     38     port         = config.port
     39     use_ipv6     = config.use_ipv6
     40     use_ssl      = config.use_ssl
     41     vhost        = config.vhost
     42     password     = config.password
     43     nickname     = config.nickname
     44     username     = config.username
     45     realname     = config.realname
     46     oper_passwd  = config.oper_passwd
     47     admin_host   = config.admin_host
     48 
     49     def __init__(self):
     50         self.husers = list()
     51         self.last   = dict()
     52         self.sock   = None
     53 
     54     def action(self, chan, msg):
     55         self.sendmsg(chan, '\x01ACTION {0}\x01'.format(msg))
     56 
     57     def chghost(self, nick, host):
     58         self.raw('CHGHOST {0} {1}'.format(nick, host))
     59 
     60     def color(self, msg, foreground, background=None):
     61         if background:
     62             return '\x03{0},{1}{2}{3}'.format(foreground, background, msg, reset)
     63         else:
     64             return '\x03{0}{1}{2}'.format(foreground, msg, reset)
     65 
     66     def connect(self):
     67         try:
     68             self.create_socket()
     69             self.sock.connect((self.server, self.port))
     70             if self.password:
     71                 self.raw('PASS ' + self.password)
     72             self.raw('USER {0} 0 * :{1}'.format(self.username, self.realname))
     73             self.raw('NICK ' + self.nickname)
     74         except socket.error as ex:
     75             debug.error('Failed to connect to IRC server.', ex)
     76             self.event_disconnect()
     77         else:
     78             self.listen()
     79 
     80     def create_socket(self):
     81         if self.use_ipv6:
     82             self.sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
     83         else:
     84             self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     85         if self.vhost:
     86             self.sock.bind((self.vhost, 0))
     87         if self.use_ssl:
     88             self.sock = ssl.wrap_socket(self.sock)
     89 
     90     def error(self, target, msg, reason=None):
     91         if reason:
     92             self.sendmsg(target, '[{0}] {1} {2}'.format(self.color('ERROR', red), msg, self.color('({0})'.format(str(reason)), grey)))
     93         else:
     94             self.sendmsg(target, '[{0}] {1}'.format(self.color('ERROR', red), msg))
     95 
     96     def event_connect(self):
     97         self.mode(self.nickname, '+Bd')
     98         self.oper(self.username, self.oper_passwd)
     99         if Database.check():
    100             for channel in ChanServ.channels():
    101                 self.join(channel)
    102         else:
    103             Database.create()
    104 
    105     def event_connection(self, nick, ident):
    106         vhost = HostServ.get_vhost(ident, True)
    107         if vhost:
    108             self.chghost(nick, vhost)
    109 
    110     def event_disconnect(self):
    111         self.sock.close()
    112         time.sleep(10)
    113         self.connect()
    114 
    115     def event_end_of_who(self):
    116         if self.last['cmd'] == 'husers':
    117             if self.husers:
    118                 self.sendmsg(self.last['nick'], '{0} {1}'.format(self.color('Total:', light_blue), self.color(len(self.husers), grey)))
    119             else:
    120                 self.error(self.last['nick'], 'No hidden users found.')
    121 
    122     def event_join(self, nick, ident, chan):
    123         mode = ChanServ.get_mode(chan, ident)
    124         if mode:
    125             self.mode(chan, '+{0} {1}'.format(mode, nick))
    126 
    127     def event_kick(self, chan, kicked):
    128         if kicked == self.nickname:
    129             if chan in Database.channels():
    130                 self.join(chan)
    131 
    132     def event_nick_in_use(self):
    133         debug.error_exit('IRCS is already running.')
    134 
    135     def event_notice(self, nick, data):
    136         if '.' in nick or nick == self.server:
    137             args = data.split()
    138             if 'Client connecting' in data:
    139                 nick  = args[6]
    140                 ident = args[7][1:][:-1]
    141                 self.event_connection(nick, ident)
    142 
    143     def event_private(self, nick, ident, msg):
    144         try:
    145             args = msg.split()
    146             cmd  = args[0][1:]
    147             host = ident.split('@')[1]
    148             if cmd == 'husers' and host == self.admin_host:
    149                 if len(args) == 1:
    150                     self.husers = list()
    151                     self.last   = {'nick':nick,'cmd':'husers'}
    152                     self.who('I', '*')
    153                 elif len(args) == 2:
    154                     if args[1] == 'kill':
    155                         if self.husers:
    156                             self.action(nick, 'Killing all hidden users...')
    157                             for item in self.husers:
    158                                 self.kill(item['nick'], 'Killed by IRCS anti-bot protection.')
    159                         else:
    160                             self.error(nick, 'Hidden users list is empty.', 'Make sure you run !husers first')
    161                     elif args[1] == 'gzline':
    162                         if self.husers:
    163                             self.action(nick, 'Z:Lining all hidden users...')
    164                             for item in self.husers:
    165                                 self.gzline(item['host'], '1d', 'Banned by IRCS anti-bot protection.')
    166                         else:
    167                             self.error(nick, 'Hidden users list is empty.', 'Make sure you run !husers first')
    168                 elif len(args) == 3:
    169                     if args [1] == 'join':
    170                         channel = args[2]
    171                         if channel.startswith('#') and len(channel) <= 20:
    172                             if self.husers:
    173                                 self.action(nick, 'Joining all hidden users to {0}...'.format(channel))
    174                                 for item in self.husers:
    175                                     self.sajoin(item['nick'], channel)
    176                             else:
    177                                 self.error(nick, 'Hidden users list is empty.', 'Make sure you run !husers first')
    178                         else:
    179                             self.error(nick, 'Invalid arguments.')
    180                     else:
    181                         self.error(nick, 'Invalid arguments.')
    182                 else:
    183                     self.error(nick, 'Invalid arguments.')
    184             elif cmd == 'mode':
    185                 if len(args) > 1:
    186                     channel = args[1]
    187                     if channel[:1] == '#' and len(channel) <= 20 and debug.check_data(channel):
    188                         if ChanServ.get_mode(channel, ident) == 'q' or host == self.admin_host:
    189                             if len(args) == 2:
    190                                 if channel in ChanServ.channels():
    191                                     data = ChanServ.read(channel)
    192                                     self.sendmsg(nick, '[{0}]'.format(self.color(channel, purple)))
    193                                     for row in data:
    194                                         self.sendmsg(nick, '{0} | {1}'.format(self.color('+' + row[1], grey), self.color(row[0], yellow)))
    195                                     self.sendmsg(nick, '{0} {1}'.format(self.color('Total:', light_blue), self.color(len(data), grey)))
    196                                 else:
    197                                     self.error(nick, self.color(channel, purple) + ' does not exist.')
    198                             elif len(args) == 3:
    199                                 if args[2] in ('a','h','o','v','q'):
    200                                     if channel in ChanServ.channels():
    201                                         mode = args[2]
    202                                         data = ChanServ.read(channel, mode)
    203                                         if data:
    204                                             self.sendmsg(nick, '[{0}] {1}'.format(self.color(channel, purple) , self.color('(+{0})'.format(mode), grey)))
    205                                             for row in data:
    206                                                 self.sendmsg(nick, self.color(row[0], yellow))
    207                                             self.sendmsg(nick, '{0} {1}'.format(self.color('Total:', light_blue), self.color(len(data), grey)))
    208                                         else:
    209                                             self.error(nick, self.color('+{0}'.format(mode), grey) + ' is empty.')
    210                                     else:
    211                                         self.error(nick, self.color(channel, purple) + ' does not exist.')
    212                                 else:
    213                                     self.error(nick, 'Invalid arguments.')
    214                             elif len(args) == 4:
    215                                 if args[2] in ('a','h','o','v','q') and args[3][:1] in '+-' and len(args[3]) <= 63 and debug.check_data(args[3]):
    216                                     mode = args[2]
    217                                     if mode == 'q' and host != self.admin_host:
    218                                         self.error(nick, 'You do not have permission to change this mode.')
    219                                     else:
    220                                         action = args[3][:1]
    221                                         ident  = args[3][1:]
    222                                         if action == '+':
    223                                             if not ChanServ.get_mode(channel, ident):
    224                                                 ChanServ.add_mode(channel, ident, mode)
    225                                                 self.sendmsg(nick, '{0} {1} has been {2} to the {3} database.'.format(self.color(ident, light_blue), self.color('(+{0})'.format(mode), grey), self.color('added', green), self.color(channel, purple)))
    226                                             else:
    227                                                 self.error(nick, '{0} already exists in the {1} database.'.format(self.color(ident, light_blue), self.color(channel, purple)))
    228                                         elif action == '-':
    229                                             if ChanServ.get_mode(channel, ident):
    230                                                 ChanServ.del_mode(channel, ident)
    231                                                 self.sendmsg(nick, '{0} {1} has been {2} from the {3} database.'.format(self.color(ident, light_blue), self.color('(+{0})'.format(mode), grey), self.color('removed', red), self.color(channel, purple)))
    232                                             else:
    233                                                 self.error(nick, '{0} does not exist in the {1} database.'.format(self.color(ident, light_blue), self.color(channel, purple)))
    234                                 else:
    235                                     self.error(nick, 'Invalid arguments.')
    236                             else:
    237                                 self.error(nick, 'Invalid arguments.')
    238                         else:
    239                             self.error(nick, 'You do not have permission to use this command.')
    240                     else:
    241                         self.error(nick, 'Invalid arguments.')
    242                 else:
    243                     self.error(nick, 'Invalid arguments.')
    244             elif cmd == 'sync':
    245                 if len(args) == 2:
    246                     channel = args[1]
    247                     if channel[:1] == '#' and len(channel) <= 20 and debug.check_data(channel):
    248                         if channel in ChanServ.channels():
    249                             if ChanServ.get_mode(channel, ident) == 'q' or host == self.admin_host:
    250                                 self.action(nick, 'Syncing all modes in {0}...'.format(color(channel, purple)))
    251                                 self.last['cmd'] = 'sync ' + channel
    252                                 self.who('h', '*')
    253                             else:
    254                                 self.error(nick, 'You do not have permission to use this command.')
    255                         else:
    256                             self.error(nick, '{0} does not exist.'.format(color(channel, purple)))
    257                     else:
    258                         self.error(nick, 'Invalid arguments.')
    259                 else:
    260                     self.error(nick, 'Invalid arguments.')
    261             elif cmd == 'vhost':
    262                 if len(args) == 2:
    263                     if args[1] == 'list':
    264                         if host == self.admin_host:
    265                             vhosts = HostServ.read()
    266                             if vhosts:
    267                                 self.sendmsg(nick, '[{0}]'.format(self.color('Registered Vhosts', purple)))
    268                                 for vhost in vhosts:
    269                                     self.sendmsg(nick, '{0} {1}'.format(self.color(vhost[0], yellow), self.color('({0})'.format(vhost[1]), grey)))
    270                                 self.sendmsg(nick, '{0} {1}'.format(self.color('Total:', light_blue), self.color(len(vhosts), grey)))
    271                             else:
    272                                 self.error(nick, 'Vhost list is empty.')
    273                         else:
    274                             self.error(nick, 'You do not have permission to use this command.')
    275                     elif args[1] == 'off':
    276                         status = HostServ.get_status(ident)
    277                         if status == 'off':
    278                             self.error(nick, 'VHOST is already turned off.')
    279                         elif status == 'on':
    280                             HostServ.set_status(ident, 'off')
    281                             self.sendmsg(nick, 'VHOST has been turned ' + color('off', red))
    282                         else:
    283                             self.error(nick, 'You do not have a registered VHOST.')
    284                     elif args[1] == 'on':
    285                         status = HostServ.get_status(ident)
    286                         if status == 'off':
    287                             HostServ.set_status(ident, 'on')
    288                             self.sendmsg(nick, 'VHOST has been turned ' + color('on', green))
    289                         elif status == 'on':
    290                             self.error(nick, 'Your VHOST is already turned on.')
    291                         else:
    292                             self.error(nick, 'You do not have a registered VHOST.')
    293                     elif args[1] == 'sync':
    294                         vhost = HostServ.get_vhost(ident)
    295                         if host == vhost:
    296                             self.error(nick, 'Your VHOST is already synced and working.')
    297                         elif vhost:
    298                             self.action(nick, 'Syncing VHOST...')
    299                             self.chghost(nick, vhost)
    300                         else:
    301                             self.error(nick, 'You do not have a registered VHOST.')
    302                 elif len(args) == 3:
    303                     if args[1] == 'drop':
    304                         if host == self.admin_host:
    305                             ident = args[2]
    306                             if ident in HostServ.idents():
    307                                 HostServ.delete(ident)
    308                                 self.sendmsg(nick, '{0} has been {1} from the vhost database.'.format(self.color(ident, light_blue), self.color('removed', red)))
    309                             else:
    310                                 self.error(nick, '{0} does not have a vhost.'.format(self.color(ident, light_blue)))
    311                         else:
    312                             self.error(nick, 'You do not have permission to use this command.')
    313                 elif len(args) == 4:
    314                     if args[1] == 'add':
    315                         if host == self.admin_host:
    316                             ident = args[2]
    317                             vhost = args[3]
    318                             if ident not in HostServ.idents():
    319                                 HostServ.add(ident, vhost)
    320                                 self.sendmsg(nick, '{0} has been {1} from the database.'.format(self.color(ident, light_blue), self.color('added', green)))
    321                             else:
    322                                 self.error(nick, '{0} is already registered.'.format(color(ident, light_blue)))
    323                         else:
    324                             self.error(nick, 'You do not have permission to use this command.')
    325                     else:
    326                         self.error(nick, 'Invalid arguments.')
    327                 else:
    328                     self.error(nick, 'Invalid arguments.')
    329         except Exception as ex:
    330             self.error(nick, 'Unexpected error has occured.', ex)
    331 
    332     def event_who(self, chan, user, host, nick):
    333         if self.last:
    334             if self.last['cmd'] == 'husers':
    335                 if chan == '*':
    336                     self.husers.append({'user':user,'host':host,'nick':nick})
    337                     self.sendmsg(self.last['nick'], '{0} {1}'.format(self.color(nick, yellow), self.color('({0}@{1})'.format(user, host), grey)))
    338             elif self.last['cmd'].startswith('sync'):
    339                 channel = self.last['cmd'].split()[1]
    340                 if chan == channel:
    341                     mode = ChanServ.mode(chan, '{0}@{1]'.format(user, host))
    342                     if mode:
    343                         self.mode(chan, '+{0} {1}'.format(mode, nick))
    344 
    345     def gzline(self, host, duration, msg):
    346         self.raw('gzline *@{1} {2} {3}'.format(user, host, duration, msg))
    347 
    348     def handle_events(self, data):
    349         args = data.split()
    350         if args[0] == 'PING':
    351             self.raw('PONG ' + args[1][1:])
    352         elif args[1] == '001':
    353             self.event_connect()
    354         elif args[1] == '315':
    355            self.event_end_of_who()
    356         elif args[1] == '352':
    357             chan = args[3]
    358             user = args[4]
    359             host = args[5]
    360             nick = args[7]
    361             self.event_who(chan, user, host, nick)
    362         elif args[1] == '433':
    363             self.event_nick_in_use()
    364         elif args[1] == 'NOTICE':
    365             nick = args[0][1:]
    366             self.event_notice(nick, data)
    367         elif args[1] in ('JOIN','KICK','PRIVMSG'):
    368             nick = args[0].split('!')[0][1:]
    369             if nick != self.nickname:
    370                 chan = args[2]
    371                 if args[1] == 'JOIN':
    372                     host = args[0].split('!')[1]
    373                     self.event_join(nick, host, chan[1:])
    374                 elif args[1] == 'KICK':
    375                     kicked = args[3]
    376                     self.event_kick(chan, kicked)
    377                 elif args[1] == 'PRIVMSG':
    378                     ident = args[0].split('!')[1]
    379                     msg   = data.split('{0} PRIVMSG {1} :'.format(args[0], chan))[1]
    380                     if msg.startswith('!'):
    381                         if chan == self.nickname:
    382                             self.event_private(nick, ident, msg)
    383 
    384     def join(self, chan):
    385         self.raw('JOIN ' + chan)
    386         self.mode(chan, '+q ' + self.nickname)
    387 
    388     def kill(self, nick, reason):
    389         self.raw('KILL {0} {1}'.format(nick, reason))
    390 
    391     def listen(self):
    392         while True:
    393             try:
    394                 data = self.sock.recv(1024).decode('utf-8')
    395                 if data:
    396                     for line in (line for line in data.split('\r\n') if line):
    397                         debug.irc(line)
    398                         if line.startswith('ERROR :Closing Link:'):
    399                             raise Exception('Connection has closed.')
    400                         elif len(line.split()) >= 2:
    401                             self.handle_events(line)
    402                 else:
    403                     debug.error('No data recieved from server.')
    404                     break
    405             except (UnicodeDecodeError,UnicodeEncodeError):
    406                 debug.error('Unicode error has occured.')
    407             except Exception as ex:
    408                 debug.error('Unexpected error occured.', ex)
    409                 break
    410         self.event_disconnect()
    411 
    412     def mode(self, target, mode):
    413         self.raw('MODE {0} {1}'.format(target, mode))
    414 
    415     def oper(self, nick, password):
    416         self.raw('OPER {0} {1}'.format(nick, password))
    417 
    418     def part(self, chan, msg):
    419         self.raw('PART {0} {1}'.format(chan, msg))
    420 
    421     def raw(self, msg):
    422         self.sock.send(bytes(msg + '\r\n', 'utf-8'))
    423 
    424     def sajoin(self, nick, chan):
    425         self.raw('SAJOIN {0} {1}'.format(nick, chan))
    426 
    427     def sendmsg(self, target, msg):
    428         self.raw('PRIVMSG {0} :{1}'.format(target, msg))
    429 
    430     def who(self, flag, args):
    431         self.raw('who +{0} {1}'.format(flag, args))
    432 
    433 IRCS = IRC()