From anonymous, 6 Years ago, written in Python.
This paste is a reply to Untitled from Your mom
- view diff
Embed
  1. from twisted import __version__ as twistedversion
  2. from twisted.words.protocols import irc
  3. from twisted.internet import protocol, reactor, task
  4. from Queue import Queue
  5. import omegle
  6. import re
  7. import sys
  8. import os
  9. import logging
  10. import platform
  11. import random
  12. import optparse
  13. import time
  14. import datetime
  15. import binascii
  16. try: import json
  17. except: import simplejson as json
  18.  
  19. from twisted.python import log
  20. log.startLogging(sys.stdout)
  21.  
  22. _contcodes = re.compile(r"(?:\x02|\x03(?:\d{1,2}(?:,\d{1,2})?)?|\x0f|\x1f|\x1d)") # control codes
  23. _places = ['amsterdam', 'athens', 'atlanta', 'austin', 'baltimore', 'bangkok', 'barcelona', 'beijing', 'berlin', 'birmingham', 'boston', 'brighton', 'brisbane', 'bristol', 'brussels', 'buenos aires', 'calgary', 'charlotte', 'chicago', 'cleveland', 'cologne', 'columbus', 'copenhagen', 'dallas/fw', 'denver', 'detroit', 'dubai', 'dublin', 'edinburgh', 'edmonton', 'frankfurt', 'geneva', 'hamburg', 'helsinki', 'hong kong', 'honolulu', 'houston', 'indianapolis', 'johannesburg', 'kansas city', 'kuala lumpur', 'las vegas', 'lisbon', 'london', 'la', 'louisville', 'madrid', 'manchester', 'melbourne', 'memphis', 'mexico city', 'miami', 'milan', 'milwaukee', 'twin cities', 'montreal', 'mumbai', 'munich', 'nashville', 'new haven', 'new orleans', 'nyc', 'oklahoma city', 'omaha', 'orlando', 'oslo', 'ottawa', 'paris', 'park city', 'philly', 'phoenix', 'pittsburgh', 'portland', 'prague', 'providence', 'raleigh-durham', 'reykjavik', 'richmond', 'rio de janeiro', 'rome', 'sacramento', 'salt lake city', 'san antonio', 'san diego', 'sf', 'sao paulo', 'seattle', 'seoul', 'shanghai', 'singapore', 'st. louis', 'stockholm', 'sydney', 'tampa', 'tel aviv', 'tokyo', 'toronto', 'tucson', 'vancouver', 'vienna', 'd.c.', 'wellington', 'zurich']
  24.  
  25. def connectedonly(fn):
  26.     def wrapper(self, *args):
  27.         if not self.ctx["clients"]:
  28.             self.msg(self.context, "\x01ACTION \x035is not connected to Omegle.\x01")
  29.         else:
  30.             fn(self, *args)
  31.     return wrapper
  32.  
  33. class OmegleWebClient(omegle.OmegleChat):
  34.     ctx = property(lambda self: self.target.contexts[self.context])
  35.    
  36.     def __init__(self, target, context):
  37.         self.target = target
  38.         self.context = context
  39.         self.q = Queue()
  40.         self.silent = False
  41.        
  42.         self.lc = task.LoopingCall(self.on_loop)
  43.        
  44.         omegle.OmegleChat.__init__(self, config["pollinterval"], "", "omegle.com")
  45.    
  46.     def on_message(self, message):
  47.         while True:
  48.             if False not in map(lambda x: hasattr(x, "id") and x.is_confirmed, self.ctx["clients"]):
  49.                 reactor.callFromThread(self.target.emitmsg, self, message)
  50.                 break
  51.    
  52.     def on_connect(self):
  53.         self.lc.start(config["pollinterval"])
  54.         reactor.callFromThread(self.target.log, self.context, self, "E", "connect", None)
  55.    
  56.     def on_loop(self):
  57.         if hasattr(self, "is_confirmed") and self.is_confirmed and not self.q.empty():
  58.             try:
  59.                 omegle.OmegleChat.send(self, self.q.get_nowait())
  60.             except:
  61.                 reactor.callFromThread(self.target.loseclient, self, "Transport error")
  62.    
  63.     def on_disconnect(self):
  64.         reactor.callFromThread(self.target.loseclient, self)
  65.    
  66.     def on_recaptcha(self):
  67.         reactor.callFromThread(self.target.msg, self.context, "\x01ACTION \x035requires the following CAPTCHA to be solved: http://www.google.com/recaptcha/api/image?c=%s\x01" % self.captcha)
  68.         reactor.callFromThread(self.target.msg, self.context, "\x01ACTION \x035requests that you transmit your answer with: %sr %s <your response>\x01" % (config["prefix"].encode("utf-8"), self.id))
  69.    
  70.     def on_recaptcha_rejected(self):
  71.         reactor.callFromThread(self.target.loseclient, self, "Failed CAPTCHA")
  72.    
  73.     def send(self, message):
  74.         self.q.put_nowait(message)
  75.        
  76. class OmegleIRCBot(irc.IRCClient):
  77.    
  78.     # CONTEXT
  79.     ctx = property(lambda self: self.contexts[self.context])
  80.    
  81.     # ALIASES
  82.     cmd_c = property(lambda self: self.cmd_connect)
  83.     cmd_co = property(lambda self: self.cmd_connect)
  84.     cmd_d = property(lambda self: self.cmd_disconnect)
  85.     cmd_dc = property(lambda self: self.cmd_disconnect)
  86.     cmd_r = property(lambda self: self.cmd_captcha)
  87.     cmd_stfu = property(lambda self: self.cmd_silence)
  88.     cmd_s = property(lambda self: self.cmd_sayas)
  89.     cmd_sa = property(lambda self: self.cmd_sayas)
  90.     cmd_say = property(lambda self: self.cmd_sayas)
  91.     cmd_st = property(lambda self: self.cmd_sayto)
  92.    
  93.     # COMMANDS
  94.     @connectedonly
  95.     def cmd_asl(self, horny = "horny", *args):
  96.         age = random.randrange(16, 26)
  97.         sex = (lambda: "m", lambda: "f")[horny == "horny"]()
  98.         location = _places[random.randrange(0, len(_places))]
  99.         self.sendannounce("%d/%s/%s" % (age, sex, location))
  100.    
  101.     def cmd_help(self, *args):
  102.         self.msg(self.context, "Nothing of your buisness you faggot gtfo!")
  103.    
  104.     def cmd_connect(self, numclients = 1, *args):
  105.         if self.ctx["pair"] and len(self.ctx["clients"]) == 2:
  106.             self.msg(self.context, "\x01ACTION \x035cannot add clients manually in pairing mode.\x01")
  107.             return
  108.         if not self.ctx["clients"]:
  109.             self.ctx["confid"] = "%08x" % (binascii.crc32("%f%f%f" % (random.random(), time.time(), random.random())) & 0xffffffff)
  110.         cids = []
  111.         try:
  112.             numclients = int(numclients)
  113.         except: numclients = 1
  114.         for i in xrange(0, numclients):
  115.             if len(self.ctx["clients"]) >= config["maxclients"]:
  116.                 break
  117.             client = OmegleWebClient(self, self.context)
  118.             self.ctx["clients"].append(client)
  119.             client.start()
  120.             cids.append(client.id)
  121.         self.cids = cids    
  122.         if cids:
  123.             self.msg(self.context, "3[09%s3]3 Connected!" % "".join(cids))
  124.             if self.ctx["aware"]:
  125.                 map(lambda x: x.send("[Info] %d client(s) joined, ID(s) are %s." % (len(cids), ", ".join(cids))), filter(lambda x: x.id not in cids, self.ctx["clients"]))
  126.         else:
  127.             self.msg(self.context, "\x01ACTION \x035cannot add any more clients.\x01")
  128.    
  129.     def cmd_count(self, *args):
  130.         if len(self.ctx["clients"]):
  131.             self.msg(self.context, "\x01ACTION \x035currently has %d client(s) (ID(s) are: %s).\x01" % (len(self.ctx["clients"]), ", ".join(map(lambda x: x.id, self.ctx["clients"]))))
  132.         else:
  133.             self.msg(self.context, "\x01ACTION \x035currently has no clients.\x01")
  134.    
  135.     @connectedonly
  136.     def cmd_kill(self, cid = None, *args):
  137.         if not cid:
  138.             self.msg(self.context, "\x01ACTION \x035requires a client ID argument.\x01")
  139.             return
  140.         pclients = filter(lambda x: re.match(cid, x.id), self.ctx["clients"])
  141.         if pclients:
  142.             for client in pclients:
  143.                 self.loseclient(client, "Killed by host")
  144.         else:
  145.             self.msg(self.context, "\x01ACTION \x035found no such clients to kill.\x01")
  146.    
  147.     @connectedonly
  148.     def cmd_disconnect(self, numclients = 0, *args):
  149.         cids = []
  150.         try:
  151.             numclients = int(numclients)
  152.         except: numclients = config["maxclients"]
  153.         if numclients == 0: numclients = config["maxclients"]
  154.         if self.ctx["pair"]:
  155.             numclients = config["maxclients"]
  156.         for i in xrange(0, numclients):
  157.             if not self.ctx["clients"]:
  158.                 break
  159.             cids.append(self.ctx["clients"][0].id)
  160.             self.log(self.context, self.ctx["clients"][0], "E", "disconnect", None)
  161.             try:
  162.                 self.ctx["clients"][0].disconnect()
  163.             except: pass
  164.             try:
  165.                 del self.ctx["clients"][0]
  166.             except: pass
  167.         if cids:
  168.             self.msg(self.context, "\x01ACTION \x035has removed from %d client(s) from conference %s (ID(s) were: %s).\x01" % (len(cids), self.ctx["confid"], ", ".join(cids)))
  169.             if self.ctx["aware"]:
  170.                 map(lambda x: x.send("[Info] %d client(s) forcequit IRC-side, ID(s) were %s." % (len(cids), ", ".join(cids))), filter(lambda x: x.id not in cids, self.ctx["clients"]))
  171.             if not self.ctx["clients"]:
  172.                 self.ctx["confid"] = None
  173.             if self.ctx["pair"]:
  174.                 self.cmd_connect(2)
  175.         else:
  176.             self.msg(self.context, "\x01ACTION \x035cannot disconnect non-existent clients.\x01")
  177.    
  178.     @connectedonly
  179.     def cmd_sayas(self, *message):
  180.         cid = self.cids[-1]
  181.         if not cid:
  182.             self.msg(self.context, "\x01ACTION \x035requires a client ID argument.\x01")
  183.             return            
  184.         message = " ".join(message).strip()
  185.         if not message:
  186.             self.msg(self.context, "\x01ACTION \x035requires a message to send. %s\x01" % self.cid)
  187.             return
  188.         pclients = filter(lambda x: re.match(cid, x.id), self.ctx["clients"])
  189.         if not pclients:
  190.             self.msg(self.context, "\x01ACTION \x035found no such client to say as.\x01")
  191.             return
  192.         for client in pclients:
  193.             self.log(self.context, client, "M", "sayas", "".join(message))
  194.             self.emitmsg(client, message)
  195.    
  196.     @connectedonly
  197.     def cmd_sayto(self, cid = None, *message):
  198.         if not cid:
  199.             self.msg(self.context, "\x01ACTION \x035requires a client ID argument.\x01")
  200.             return
  201.         message = " ".join(message).strip()
  202.         if not message:
  203.             self.msg(self.context, "\x01ACTION \x035requires a message to send.\x01")
  204.             return
  205.         if self.ctx["aware"]:
  206.             self.msg(self.context, "\x01ACTION \x035cannot use say to in aware mode.\x01") # awwz
  207.             return        
  208.         pclients = filter(lambda x: re.match(cid, x.id), self.ctx["clients"])
  209.         if pclients:    
  210.             for client in pclients:
  211.                 self.log(self.context, None, "M", "sayto", "%s:%s" % (client.id, message))
  212.                 self.msg(self.context, "\x02OUTGOING TO %s:\x02 %s" % (client.id, message))
  213.                 client.send(message)
  214.         else:
  215.             self.msg(self.context, "\x01ACTION \x035found no such client to say to.\x01")
  216.    
  217.     @connectedonly
  218.     def cmd_captcha(self, cid = None, *response):
  219.         if not cid:
  220.             self.msg(self.context, "\x01ACTION \x035requires a client ID argument.\x01")
  221.             return
  222.         captcha = " ".join(response)
  223.         for client in filter(lambda x: re.match(cid, x.id), self.ctx["clients"]):
  224.             client.captchaConfirm(captcha)
  225.             self.msg(self.context, "\x01ACTION \x035is confirming CAPTCHA for %s with \"%s\".\x01" % (client.id, captcha))
  226.  
  227.     def cmd_aware(self, *args):
  228.         if self.ctx["aware"]:
  229.             self.ctx["aware"] = False
  230.             self.log(self.context, None, "S", "aware", "")
  231.             self.msg(self.context, "\x01ACTION \x035will no longer report multiple clients Omegle-side.\x01")
  232.         else:
  233.             self.ctx["aware"] = True
  234.             self.log(self.context, None, "S", "aware", "")
  235.             self.msg(self.context, "\x01ACTION \x035will now report multiple clients Omegle-side.\x01")
  236.  
  237.     def cmd_mute(self, *args):
  238.         if self.ctx["mute"]:
  239.             self.ctx["mute"] = False
  240.             self.log(self.context, None, "S", "mute", "")
  241.             self.msg(self.context, "\x01ACTION \x035will now accept messages IRC-side.\x01")
  242.         else:
  243.             self.ctx["mute"] = True
  244.             self.log(self.context, None, "S", "mute", "")
  245.             self.msg(self.context, "\x01ACTION \x035will no longer accept messages IRC-side.\x01")
  246.    
  247.     def cmd_pair(self, *args):
  248.         if self.ctx["pair"]:
  249.             self.ctx["pair"] = False
  250.             self.log(self.context, None, "S", "pair", "")
  251.             self.msg(self.context, "\x01ACTION \x035will no longer use pairing.\x01")
  252.         else:
  253.             self.ctx["pair"] = True
  254.             self.log(self.context, None, "S", "pair", "")
  255.             self.msg(self.context, "\x01ACTION \x035will now use pairing.\x01")
  256.             self.cmd_connect(2) # pair 2 clients to start off with
  257.    
  258.     @connectedonly
  259.     def cmd_silence(self, cid = None, *args):
  260.         if not cid:
  261.             self.msg(self.context, "\x01ACTION \x035requires a client ID argument.\x01")
  262.             return
  263.         pclients = filter(lambda x: re.match(cid, x.id), self.ctx["clients"])
  264.         if pclients:
  265.             silenced = []
  266.             unsilenced = []
  267.             for client in pclients:
  268.                 if client.silent:
  269.                     client.silent = False
  270.                     self.log(self.context, client, "S", "silence", "")
  271.                     unsilenced.append(client.id)
  272.                 else:
  273.                     client.silent = True
  274.                     self.log(self.context, client, "S", "silence", "")
  275.                     silenced.append(client.id)
  276.             if silenced:
  277.                 self.msg(self.context, "\x01ACTION \x035has silenced %d client(s) (ID(s) are: %s).\x01" % (len(silenced), ", ".join(silenced)))
  278.             if unsilenced:
  279.                 self.msg(self.context, "\x01ACTION \x035has unsilenced %d client(s) (ID(s) are: %s).\x01" % (len(unsilenced), ", ".join(unsilenced)))
  280.         else:
  281.             self.msg(self.context, "\x01ACTION \x035found no such client to mode change.\x01")
  282.    
  283.     # FUNCTIONS
  284.     def loseclient(self, client, reason = "Remote disconnect", repair = True):
  285.         if not hasattr(client, "lost") or not client.lost:
  286.             self.log(client.context, client, "E", "loseclient", reason)
  287.             client.lost = True
  288.             try:
  289.                 client.lc.stop()
  290.             except AssertionError: pass
  291.             self.msg(client.context, "\x01ACTION \x035has lost client %s from Omegle (%s).\x01" % (client.id, reason))
  292.             if client.ctx["aware"]:
  293.                 for c in filter(lambda x: x is not client, client.ctx["clients"]):
  294.                     c.send("[Info] Client %s has been lost (%s)." % (client.id, reason))
  295.             try:
  296.                 try:
  297.                     client.disconnect()
  298.                 except:
  299.                     pass
  300.                 del client.ctx["clients"][client.ctx["clients"].index(client)]
  301.             except ValueError:
  302.                 pass
  303.             if not client.ctx["clients"]:
  304.                 client.ctx["confid"] = None
  305.             if client.ctx["pair"]:
  306.                 for c in client.ctx["clients"]:
  307.                     self.loseclient(c, "Pairing ended", False)
  308.                 if repair:
  309.                     tempcontext = self.context
  310.                     self.context = client.context
  311.                     self.cmd_connect(2)
  312.                     self.context = tempcontext
  313.    
  314.     def emitmsg(self, client, msg):
  315.         if not client.id: return # none of the None nastiness
  316.         msg = msg.strip()
  317.         if not client.silent:
  318.             for c in filter(lambda x: x is not client, client.ctx["clients"]):
  319.                 if self.ctx["aware"]:
  320.                     c.send("[%s:Omegle] %s" % (client.id, msg))
  321.                 else:
  322.                     c.send(msg)
  323.         lines = msg.encode("utf-8").replace("\r\n", "\n").replace("\r", "\n").split("\n")
  324.         if len(lines) > config["omegleflood"]: # flooding
  325.             self.loseclient(client, "Excess flood")
  326.         else:
  327.             for m in lines:
  328.                 self.log(client.context, client, "M", "msg", m)    
  329.                 self.msg(client.context, "\x02[%s]\x02 %s" % (client.id, m))
  330.    
  331.     def sendannounce(self, msg):
  332.         self.log(self.context, None, "M", "announce", msg)
  333.         self.msg(self.context, "\x02OUTBOUND:\x02 %s" % msg)
  334.         map(lambda x: x.send(msg), self.ctx["clients"])
  335.    
  336.     def log(self, context, client, rtype, method, msg):
  337.         if self.contexts[context]["confid"] and self.factory.dbconn:
  338.             self.factory.dbconn.runQuery("INSERT INTO `%s` (timestamp, confid, mute, aware, pair, silent, clientid, medium, type, method, message) VALUES(NOW(), %%s, %%s, %%s, %%s, %%s, %%s, %%s, %%s, %%s, %%s)" % _mysql.escape_string(context[1:]), (self.contexts[context]["confid"], (lambda: "N", lambda: "Y")[self.contexts[context]["mute"]](), (lambda: "N", lambda: "Y")[self.contexts[context]["aware"]](), (lambda: "N", lambda: "Y")[self.contexts[context]["pair"]](), (lambda: "N", lambda: "Y")[(lambda: False, lambda: client.silent)[isinstance(client, OmegleWebClient)]()](), (lambda: client, lambda: client.id)[isinstance(client, OmegleWebClient)](), (lambda: "IRC", lambda: "Omegle")[isinstance(client, OmegleWebClient)](), rtype, method, msg))
  339.    
  340.     # EVENTS
  341.     def __init__(self):
  342.         self.contexts = {}
  343.         self.context = None
  344.         self.lineRate = config["linerate"]
  345.         self.versionName = "OmegleIRC"
  346.         self.versionNum = "0.1"
  347.         self.versionEnv = "%s/%s+Twisted/%s" % (platform.uname()[0], platform.uname()[2], twistedversion)
  348.         self.realname = config["realname"].encode("utf-8")
  349.         self.username = config["username"].encode("utf-8")
  350.    
  351.     def connectionMade(self):
  352.         self.nickname = config["nickname"].encode("utf-8")
  353.         irc.IRCClient.connectionMade(self)
  354.    
  355.     def irc_RPL_WELCOME(self, prefix, params):
  356.         if config["nspass"]:
  357.             self.msg("NickServ", "GHOST %s %s" % (config["nickname"].encode("utf-8"), config["nspass"].encode("utf-8")))
  358.             self.msg("NickServ", "IDENTIFY %s" % config["nspass"].encode("utf-8"))
  359.         for c in config["channels"]:
  360.             self.join(c.encode("utf-8"))
  361.    
  362.     def connectionLost(self, reason):
  363.         for c in map(lambda x: x["clients"], self.contexts.values()):
  364.             try:
  365.                 c.disconnect()
  366.             except: pass
  367.    
  368.     def joined(self, channel):
  369.         self.contexts[channel.lower()] = {
  370.             "clients"    :    [],
  371.             "aware"        :    False,
  372.             "mute"        :    False,
  373.             "pair"        :    False,
  374.             "confid"    :    None
  375.         }
  376.         self.context = channel.lower()
  377.         if self.factory.dbconn:
  378.             self.factory.dbconn.runQuery("""\
  379. CREATE TABLE IF NOT EXISTS `%s`
  380. (
  381.    timestamp DATETIME,
  382.    confid VARCHAR(8),
  383.    mute ENUM("N", "Y"),
  384.    aware ENUM("N", "Y"),
  385.    pair ENUM("N", "Y"),
  386.    silent ENUM("N", "Y"),
  387.    clientid VARCHAR(32),
  388.    medium ENUM("IRC", "Omegle"),
  389.    type ENUM("E", "M", "S"),
  390.    method VARCHAR(32),
  391.    message TEXT
  392. );\
  393. """ % _mysql.escape_string(self.context[1:]))
  394.         self.log(self.context, "", "E", "joined", channel)
  395.    
  396.     def privmsg(self, user, channel, msg):
  397.         nick = user.split("!")[0] # hostname and ident are superfluous
  398.         self.context = channel.lower()
  399.         if msg[:len(config["invisprefix"])] == config["invisprefix"]: return # gtfo
  400.         if not channel.lower() in map(lambda x: x.lower(), self.contexts.keys()): return # no idea how you're here but gtfo as well
  401.         msg = _contcodes.sub("", msg)
  402.         if msg.lower()[:len(config["prefix"])] == config["prefix"]:
  403.             argv = msg[len(config["prefix"]):].split(" ")
  404.             if hasattr(self, "cmd_%s" % argv[0].lower()):
  405.                 getattr(self, "cmd_%s" %  argv[0].lower())(*argv[1:])
  406.         elif not self.ctx["mute"]:
  407.             if self.ctx["clients"]:
  408.                 if msg[:8].lower() == "\x01action " and msg[-1] == "\x01":
  409.                     msg = "*%s*" % msg[8:-1]
  410.                 self.log(self.context, nick, "M", "msg", msg)
  411.                 if self.ctx["aware"]:
  412.                     map(lambda x: x.send("[%s:IRC] %s" % (nick, msg)), self.ctx["clients"])
  413.                 else:
  414.                     map(lambda x: x.send(msg), self.ctx["clients"])
  415.    
  416.     def action(self, user, channel, data):
  417.         self.privmsg(user, channel, "\x01ACTION %s\x01" % data)
  418.    
  419. class OmegleBotFactory(protocol.ClientFactory):
  420.     protocol = OmegleIRCBot
  421.    
  422.     def __init__(self):
  423.         if config["mysqlconns"]:
  424.             self.dbconn = adbapi.ConnectionPool("MySQLdb", \
  425.                 host = (lambda: None, lambda: config["mysqlconns"]["host"])["host" in config["mysqlconns"].keys()](), \
  426.                 user = (lambda: None, lambda: config["mysqlconns"]["username"])["username" in config["mysqlconns"].keys()](), \
  427.                 passwd = (lambda: None, lambda: config["mysqlconns"]["password"])["password" in config["mysqlconns"].keys()](), \
  428.                 db = (lambda: None, lambda: config["mysqlconns"]["database"])["database" in config["mysqlconns"].keys()](), \
  429.                 port = (lambda: None, lambda: config["mysqlconns"]["port"])["port" in config["mysqlconns"].keys()](), \
  430.             )
  431.        
  432.         else:
  433.             self.dbconn = None
  434.    
  435.     def clientConnectionLost(self, connector, reason):
  436.         print "Lost connection (%s), reconnecting." % (reason,)
  437.         connector.connect()
  438.  
  439.     def clientConnectionFailed(self, connector, reason):
  440.         print "Could not connect: %s" % (reason,)
  441.  
  442. if __name__ == "__main__":
  443.     arghelp = {
  444.         "nickname"        :    "nickname to use",
  445.         "realname"        :    "real name in whois",
  446.         "username"        :    "fake ident",
  447.         "nspass"        :    "nickserv password",
  448.         "server"        :    "irc server",
  449.         "port"            :    "server port",
  450.         "channels"        :    "list of channels, separated by commas",
  451.         "prefix"        :    "command prefix",
  452.         "invisprefix"    :    "invisible prefix",
  453.         "maxclients"    :    "maximum clients per chan",
  454.         "pollinterval"    :    "interval to poll",
  455.         "linerate"        :    "twisted line rate",
  456.         "omegleflood"    :    "maximum lines for an omegle client",
  457.         "mysqlconns"    :    "mysql connection string, in json: \"{'host':<hostname>,'port':<port>,'database':<database>,'username':<username>, 'password':<password>}\"",
  458.         "pidfile"        :    "pidfile to save the pid to",
  459.         "foreground"    :    "run in foreground (accepts true/false)"
  460.     }
  461.     parser = optparse.OptionParser()
  462.     parser.add_option("-c", "--config", help="configuration file", metavar="FILE", dest="config")
  463.     for v in arghelp.keys():
  464.         parser.add_option("", "--%s" % v, help=arghelp[v], metavar="VALUE", dest=v)
  465.     (options, args) = parser.parse_args()
  466.     config = json.load(open((lambda: "bot.json", lambda: options.config)[bool(hasattr(options, "config") and options.config)]()))
  467.    
  468.     for o in options.__dict__.iteritems(): # doin it rong
  469.         if o[0] == "config" or o[1] is None: continue
  470.         if o[0] == "channels":
  471.             config[o[0]] = o[1].split(",")
  472.         elif o[0] == "foreground":
  473.             config[o[0]] = (o[1].lower() == "true")
  474.         elif o[0] == "mysqlconns":
  475.             config[o[0]].update(json.loads(o[1]))
  476.         else:
  477.             config[o[0]] = o[1]
  478.    
  479.     if config["mysqlconns"]:
  480.         from twisted.enterprise import adbapi
  481.         import _mysql
  482.    
  483.     if not config["foreground"]:
  484.         try:
  485.             pid = os.fork()
  486.             if pid > 0:
  487.                 sys.exit(0)
  488.         except OSError, e:
  489.             print >>sys.stderr, "On fork #1: %s" % str(e)
  490.             sys.exit(1)
  491.  
  492.         try:
  493.             pid = os.fork()
  494.             if pid > 0:
  495.                 pidfile = open(config["pidfile"], "w")
  496.                 pidfile.write(str(pid))
  497.                 pidfile.close()
  498.                 sys.exit(0)
  499.         except OSError, e:
  500.             print >>sys.stderr, "On fork #2: %s" % str(e)
  501.             sys.exit(1)    
  502.    
  503.     reactor.connectTCP(config["server"], config["port"], OmegleBotFactory())
  504.     reactor.run()
  505.