Skip to content

Instantly share code, notes, and snippets.

@e000
Created February 16, 2011 23:03
Show Gist options
  • Select an option

  • Save e000/830484 to your computer and use it in GitHub Desktop.

Select an option

Save e000/830484 to your computer and use it in GitHub Desktop.
an omegle wrapper for a connection using twisted, not really following their protocol spec, maybe I should o.O
"""
Omegle Twisted Client version 1.0
"""
__author__ = "e"
__version___ = "1.0"
DISCONNECTED = 0
CONNECTING = 1
WAITING = 2
CONNECTED = 3
from random import choice
from twisted.internet.defer import DeferredLock, inlineCallbacks, CancelledError, returnValue
from urllib import urlencode
from twisted.internet import reactor
import re
# zalgo dependancies. you can just drop in twisted.getPage, and simplejson's json_decode, utf8 is simply conversion of utf8 to byte array.
from Core.ZalgoUtil.CancellableClient import getPage
from Core.ZalgoUtil.ModUtil import json_decode, utf8
_userAgents = [
'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.10) Gecko/20100915 Ubuntu/10.04 (lucid) Firefox/3.6.10',
'Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.45 Safari/534.16'
]
def getRandomUserAgent():
"Gives us a random user-agent to spoof with!"
return choice(_userAgents)
class AlreadyRunningError(Exception):
pass
class NotConnectedError(Exception):
pass
class CaptchaNotRequired(Exception):
pass
class SendError(Exception):
pass
class OmegleBot():
DISCONNECTED = 0
CONNECTING = 1
WAITING = 2
CONNECTED = 3
_serverRegex = re.compile('\<i?frame src="(.*?)"\>')
_captchaImageRegex = re.compile('\<center\>\<img width="\d+" height="\d+" alt="" src="image\?c\=(.*?)"\>\<\/center\>')
def __init__(self, typingCallback = None, stoppedTypingCallback = None,
disconnectCallback = None, messageCallback = None,
recaptchaFailedCallback = None, recaptchaRequiredCallback = None,
connectCallback = None, waitingCallback = None):
"""
Initializes an L{OmegleBot}
@param typingCallback: a callable that will fire when the user connected is typing
@param stoppedTypingCallback: a callable that will fire when the user connected is no longer typing
@param disconnectCallback: a callable that will fire when the user has disconnected, or the bot has disconnected from the user
@param messageCallback: a callable that will fire when the user has sent us a message
@param recaptchaFailedCallback: a callable that will fire when our submitted captcha fails
@param recaptchaRequiredCallback: a callable that will fire when Omegle requires us to submit a captcha to connect
@param connectCallback: a callable that will fire when we have found a partner
@param waitingCallback: a callable that will fire when we are waiting for a partner
"""
self.typingCallback = typingCallback
self.stoppedTypingCallback = stoppedTypingCallback
self.disconnectCallback = disconnectCallback
self.messageCallback = messageCallback
self.recaptchaFailedCallback = recaptchaFailedCallback
self.recaptchaRequiredCallback = recaptchaRequiredCallback
self.connectCallback = connectCallback
self.waitingCallback = waitingCallback
self.status = DISCONNECTED
self.server = None
self.id = None
self.lock = DeferredLock()
self.activeRequests = set()
self.challenge = None
self.image = None
def disconnect(self):
""" Disconnects from the Omegle Server if we're connected """
oldStatus = self.status
if self.status in (WAITING, CONNECTED):
self.getPage('disconnect', addToActive = False, data = {'id': self.id}).addErrback(lambda r: None) ## /dev/null it lol
if self.status == DISCONNECTED:
return
self.status = DISCONNECTED
self.id = None
self.challenge = None
self.server = None
self._cancelAllRequests()
self.onDisconnect()
def _cancelAllRequests(self):
"""
kills all active connetions, i/o and empties the message queue
"""
self.lock.waiting[:] = []
for d in list(self.activeRequests):
d.cancel()
self.activeRequests.clear()
def getPage(self, url, addToActive = True, data = None, *args, **kwargs):
"""
retrieves a page using the twisted getPage function, and if addToActive is true, will add to the tracked requests that will cancel if we disconnect
"""
def removeFromActive(r):
self.activeRequests.discard(d)
return r
if not url.startswith('http://') and self.server:
url = self.server + url
if data is not None:
data = urlencode(data)
kwargs.update({
'method': 'POST',
'postdata': data,
'headers': {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': '%i' % len(data)
}
})
d = getPage(url, agent = self.userAgent, *args, **kwargs)
if addToActive:
self.activeRequests.add(d)
d.addBoth(removeFromActive)
return d
def say(self, message):
"""
send a message to the connected user
raises NotConnectedError if we're not connected
@param message: the message to send
@type message: string, unicode
"""
if self.status != CONNECTED:
raise NotConnectedError()
def sentMessage(response):
if response == 'win':
return True
else:
raise SendError("Couldn't send message.")
return self._doLockedCommand(
'send', data = {'id': self.id, 'msg': message}
).addCallback(sentMessage)
def typing(self):
"""
tells the connected user that we're typing
raises NotConnectedError if we're not connected
"""
if self.status != CONNECTED:
raise NotConnectedError()
self._doLockedCommand(
'typing', data = {'id': self.id}
)
def stoppedTyping(self):
"""
tells the connected user that we're not typing anymore
raises NotConnectedError if we're not connected
"""
if self.status != CONNECTED:
raise NotConnectedError()
self._doLockedCommand(
'stoppedtyping', data = {'id': self.id}
)
def solveCaptcha(self, solution):
"""
attempts to solve the captcha that omegle sent to us.
@param solution: the solution to the captcha
@type solution: string
"""
if not self.challenge and self.image:
raise CaptchaNotRequired()
self.getPage('recaptcha', data = {
'id': self.id,
'response': solution,
'challenge': self.image
})
self.image, self.challenge = None, None
def _doLockedCommand(self, url, data):
"""
internal command that adds it to our DeferredLock queue, which will fire sequentially as they finish, allowing only one request to be processed at once
"""
l = self.lock.acquire()
def gotLock(lock):
if self.status == CONNECTED:
def releaseLock(r):
lock.release()
return r
d = self.getPage(url, data = data)
d.addBoth(releaseLock)
return d
else:
lock.release()
return l.addCallback(gotLock)
@inlineCallbacks
def connect(self):
"""
attempts to connect to the Omegle server.
returns a deferred that will fire when we've established a connection
"""
if self.status != DISCONNECTED:
raise AlreadyRunningError()
self.userAgent = getRandomUserAgent()
self.status = CONNECTING
homePage = yield self.getPage('http://omegle.com/')
match = self._serverRegex.search(homePage)
if not match:
raise ValueError("Could not find a server to connect to!")
else:
self.server = match.group(1)
id = yield self.getPage('start?rcs=1&spid=')
self.id = json_decode(id)
self.status = WAITING
self.doEvents()
returnValue((self.id, self.server))
def doEvents(self):
"""
main asynchronous io loop that handles events, and asks for more
"""
if self.status not in (CONNECTED, WAITING):
return
def gotEvents(response):
events = json_decode(response)
if events is None:
self.disconnect()
else:
for event in events:
event, params = event[0], event[1:]
callback = getattr(self, 'EVENT_%s' % event, None)
if callback:
callback(params)
self.doEvents()
def gotError(error):
if not isinstance(error.value, CancelledError):
self.disconnect()
self.onError(error)
return self.getPage('events', data = {
'id': self.id
}).addCallbacks(gotEvents, gotError)
def EVENT_waiting(self, params):
""" we received a waiting event """
self.status = WAITING
self.runCallback(self.waitingCallback)
def EVENT_connected(self, params):
""" we're connected to a partner """
self.status = CONNECTED
self.runCallback(self.connectCallback)
def EVENT_gotMessage(self, params):
""" partner sent us a message! """
self.runCallback(self.messageCallback, params)
def EVENT_typing(self, params):
""" partner is typing """
self.runCallback(self.typingCallback)
def EVENT_stoppedTyping(self, params):
""" partner stopped typing """
self.runCallback(self.stoppedTypingCallback)
def EVENT_strangerDisconnected(self, params):
""" partner disconnected """
self.disconnect()
def doCaptcha(self, challenge):
""" returns a deferred that will fire when we have the location of the captcha image """
def gotImage(r):
self.image = r
return r
def error(error):
self.onError(error)
self.disconnect()
return None
d = self.getRecaptchaImage(challenge)
d.addCallback(gotImage).addErrback(error)
return d
@inlineCallbacks
def getRecaptchaImage(self, key):
""" try and find the image to solve """
pg = yield self.getPage('http://www.google.com/recaptcha/api/noscript?%s' % urlencode({'k': key}), headers = {
'referer': 'http://www.omegle.com/'
})
match = self._captchaImageRegex.search(pg)
if match:
returnValue(match.group(1))
else:
raise ValueError("Could not find the image!")
def EVENT_recaptchaRequired(self, params):
""" omegle says we need a captcha to connect """
#params = challenge,
self.challenge = params[0]
params.append(self.doCaptcha(self.challenge))
self.runCallback(self.recaptchaRequiredCallback, params)
def EVENT_recaptchaRejected(self, params):
""" omegle says that our captcha was wrong! """
self.challenge = params[0]
params.append(self.doCaptcha(self.challenge))
self.runCallback(self.recaptchaFailedCallback, params)
def onDisconnect(self):
""" we've disconnected """
self.runCallback(self.disconnectCallback)
def onError(self, error):
""" an error has happened! """
error.printBriefTraceback()
def runCallback(self, callback, params = None):
""" run our callback if it's set """
if callback is None:
return
try:
callback(self, params)
except:
from twisted.python import failure
failure.Failure().printBriefTraceback()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment