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()