romegle 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/omegle.rb +333 -0
  2. metadata +45 -0
data/lib/omegle.rb ADDED
@@ -0,0 +1,333 @@
1
+ # Ruby Omegle interface. Written by Stephen Wattam, http://www.stephenwattam.com
2
+ # Based on the ruby-omegle gem, with major updates.
3
+ # Thanks to the original author, Mikhail Slyusarev
4
+ #
5
+ # Many thanks to the various protocol descriptions people did that helped with this.
6
+ #
7
+
8
+ # TODO:
9
+ # 1. Support for recaptcha, when challenged.
10
+ #
11
+
12
+
13
+
14
+ #http://code.google.com/p/omegle-api/wiki/Home#IDs_and_events
15
+ #https://github.com/nikkiii/omegle-api-java/blob/master/src/org/nikki/omegle/Omegle.java
16
+ #//Omegle events
17
+ # waiting, connected, gotMessage, strangerDisconnected, typing, stoppedTyping, recaptchaRequired, recaptchaRejected, count,
18
+ #
19
+ # //Spy mode events
20
+ # spyMessage, spyTyping, spyStoppedTyping, spyDisconnected, question, error, commonLikes,
21
+ #
22
+ # //Misc events
23
+ # antinudeBanned,
24
+ ##
25
+ #http://code.google.com/p/saf-omegle/wiki/Events
26
+
27
+ require 'uri'
28
+ require 'net/http'
29
+ require 'json'
30
+ require 'thread'
31
+
32
+ ##
33
+ # Open, manage and use a Session with Omegle.
34
+ #
35
+ # This class is capable of supporting all Omegle features, except webcam support, and constitutes a fairly thin layer over Omegle's event system.
36
+ #
37
+ # Each time the code interacts with Omegle's web services, it receives, and queues up, some events. These events may then be accessed in a thread-safe manner using the various functions of the class.
38
+ # These events are documented, but only loosely. They fit into rough categories:
39
+ #
40
+ # * General events: waiting, connected, gotMessage, strangerDisconnected, typing, stoppedTyping, recaptchaRequired, recaptchaRejected, count, antinudeBanned
41
+ # * Spy Mode Events: spyMessage, spyTyping, spyStoppedTyping, spyDisconnected, question, error, commonLikes
42
+ #
43
+ # You'll have to do some testing to verify the precise pattern you receive when doing things, as Omegle seem to change some of their events around from time to time.
44
+ class Omegle
45
+
46
+ ##
47
+ # Passed to omegle in every call.
48
+ #
49
+ # By default the static headers simply set the referer.
50
+ STATIC_HEADERS = {"referer" => "http://omegle.com"}
51
+
52
+ ##
53
+ # Default options. These run to:
54
+ # * :host --- String, the host to connect to (don't change from omegle.com unless you wish to defy their load balancer)
55
+ # * :question --- String, the question to ask in spy mode
56
+ # * :topics --- Array of Strings, the list of topics you're interested in for Omegle's topic matching
57
+ # * :answer --- Boolean, tell Omegle that you wish to be watched (i.e. take part in spy mode for someone else's question)
58
+ # * :headers --- Some HTTP headers to send with every call, handy for things like user agent spoofing.
59
+ #
60
+ # Setting :question, :topics, or :answer will set the 'mode' of the session, and will cause errors if two are
61
+ # set together.
62
+ DEFAULT_OPTIONS = {:host => 'omegle.com',
63
+ :question => nil,
64
+ :topics => nil,
65
+ :answer => false,
66
+ :headers => STATIC_HEADERS}
67
+
68
+ # The ID of this session
69
+ attr_accessor :id
70
+
71
+ # Construct the Omegle object and set options
72
+ # See #DEFAULT_OPTIONS for a list of valid options for the hash.
73
+ def initialize(options = {})
74
+ # mutex for multiple access to send/events
75
+ @mx = Mutex.new
76
+ @options = DEFAULT_OPTIONS
77
+
78
+ # Load and validate config options
79
+ integrate_configs(options)
80
+
81
+ # FIFO for events
82
+ @events = []
83
+ end
84
+
85
+ # :category: Control
86
+ #
87
+ # Static construct/use method.
88
+ #
89
+ # The code below:
90
+ #
91
+ # Omegle.start(options){
92
+ # whatever
93
+ # }
94
+ #
95
+ # is equivalent to calling
96
+ # o = Omegle.new(options)
97
+ # o.start
98
+ # whatever
99
+ # o.disconnect
100
+ #
101
+ def self.start(options = {})
102
+ s = Omegle.new(options)
103
+ s.start
104
+ yield s
105
+ s.disconnect
106
+ end
107
+
108
+ # :category: Control
109
+ #
110
+ # Make the initial request to omegle to start the session.
111
+ #
112
+ # This will, like all other calls to Omegle, cause some events to pile up.
113
+ # See #DEFAULT_OPTIONS for a list of valid options for the hash.
114
+ def start(options = {})
115
+ integrate_configs(options)
116
+
117
+ # Connect to start a session in one of three modes
118
+ if(@options[:question]) then
119
+ resp = req("start?rcs=1&firstevents=1&spid=&randid=#{get_randID}&cansavequestion=1&ask=#{URI::encode(@options[:question])}", :get)
120
+ elsif(@options[:answer]) then
121
+ resp = req("start?firstevents=1&wantsspy=1", :get) #previously ended at 6
122
+ else
123
+ topicstring = ""
124
+ topicstring = "&topics=#{ URI::encode(@options[:topics].to_s) }" if @options[:topics].is_a?(Array)
125
+ resp = req("start?firstevents=1#{topicstring}", :get) #previously ended at 6
126
+ end
127
+
128
+ # Was the response JSON?
129
+ if resp =~ /^"[\w]+:\w+"$/ then
130
+ # not json, simply strip quotes
131
+ @id = resp[1..-2]
132
+ else
133
+ #json
134
+ # parse, find ID, add first events
135
+ resp = JSON.parse(resp)
136
+ raise "No ID in connection response!" if not resp["clientID"]
137
+ @id = resp["clientID"]
138
+
139
+ # Add events if we requested it.
140
+ add_events(resp["events"]) if resp["events"]
141
+ end
142
+ end
143
+
144
+ # :category: Control
145
+ #
146
+ # Check omegle to see if any events have come through.
147
+ def poll_events
148
+ ret = req('events', "id=#{@id}")
149
+ parse_response(ret)
150
+ end
151
+
152
+ # :category: Control
153
+ #
154
+ # Send a message to whoever is connected (if, indeed, they are)
155
+ def send(msg)
156
+ ret = req('send', "id=#{@id}&msg=#{URI::encode(msg)}")
157
+ parse_response(ret)
158
+ end
159
+
160
+ # :category: Control
161
+ #
162
+ # Let them know you're typing.
163
+ def typing
164
+ ret = req('typing', "id=#{@id}")
165
+ parse_response(ret)
166
+ end
167
+
168
+ # :category: Control
169
+ #
170
+ # Disconnect from Omegle.
171
+ #
172
+ # This merely requests a disconnect. Omegle will then stop issuing events,
173
+ # which will cause any calls to get_event to return nil, which will in turn
174
+ # cause listen to quit.
175
+ def disconnect
176
+ ret = req('disconnect', "id=#{@id}")
177
+ @id = nil if ret != nil
178
+ parse_response(ret)
179
+ end
180
+
181
+ # :category: Status
182
+ #
183
+ # Is this object in a session?
184
+ def connected?
185
+ @id != nil
186
+ end
187
+
188
+ # :category: Status
189
+ #
190
+ # Is spy mode on?
191
+ def spy_mode?
192
+ @options[:question] != nil
193
+ end
194
+
195
+ # :category: Status
196
+ #
197
+ # Does this session have any topics associated?
198
+ def topics
199
+ @options[:topics]
200
+ end
201
+
202
+ # :category: Events
203
+ #
204
+ # Pass a code block to deal with each events as they come.
205
+ #
206
+ # This continually returns events until the omegle session is
207
+ # disconnected, and is the main way of interacting with the thing.
208
+ def listen
209
+ # Get any events since last call
210
+ poll_events
211
+
212
+ # repeatedly yield any incoming events,
213
+ # and keep polling.
214
+ #
215
+ # This drops out when no events are
216
+ # available, and automatically polls for more
217
+ while (e = get_event) != nil
218
+ yield e
219
+ poll_events if @events.length == 0
220
+ end
221
+ end
222
+
223
+ # :category: Events
224
+ #
225
+ # Returns the oldest event from the FIFO
226
+ #
227
+ # This, unlike #peek_event removes the event from the list.
228
+ def get_event
229
+ @mx.synchronize{
230
+ return @events.pop
231
+ }
232
+ end
233
+
234
+ # :category: Events
235
+ #
236
+ # Returns a reference to the oldest event on the FIFO.
237
+ #
238
+ # Unlike #get_event this does not remove it from the list.
239
+ def peek_event
240
+ @mx.synchronize{
241
+ return @events.last
242
+ }
243
+ end
244
+
245
+ private
246
+
247
+ # Merges configs with the 'global config'
248
+ # can only work when not connected
249
+ def integrate_configs(options = {})
250
+ @mx.synchronize{
251
+ raise "Cannot alter session settings whilse connected." if @id != nil
252
+ raise "Topics cannot be specified along with a question." if options[:question] and options[:topics]
253
+ raise "Topics cannot be specified along with answer mode" if options[:answer] and options[:topics]
254
+ raise "Answer mode cannot be enabled along with a question" if options[:answer] and options[:question]
255
+ }
256
+ @options.merge!(options)
257
+ end
258
+
259
+
260
+ # Make a request to omegle. Synchronous.
261
+ # set args = :get to make a get request, else it's post.
262
+ #
263
+ # Returns the body if it worked, or nil if it failed.
264
+ #
265
+ # Arguments:
266
+ # path: The page to request, without trailing or preceeding slash (i.e. 'status' for 'omegle.com/status')
267
+ # args: A list of URI-formatted arguments, or the :get symbol
268
+ def req(path, args="")
269
+ omegle = Net::HTTP.start(@options[:host])
270
+
271
+ # get a return and ignore the errors that
272
+ # occasionally (and seemingly meaninglessly) crop up.
273
+ ret = nil
274
+ begin
275
+ ret = omegle.post("/#{path}", args, STATIC_HEADERS) if args != :get
276
+ ret = omegle.get("/#{path}", STATIC_HEADERS) if args == :get
277
+ rescue EOFError
278
+ rescue TimeoutError
279
+ end
280
+
281
+ # return nil or the content if the call worked
282
+ return ret.body if ret and ret.code == "200"
283
+ return nil
284
+ end
285
+
286
+ # Add an event to the FIFO in-order
287
+ def add_events(evts)
288
+ # Accept one event or many
289
+ evts = [evts] if not evts.is_a? Array
290
+
291
+ @mx.synchronize{
292
+ # add to front of array, pop off back
293
+ evts.each{|e|
294
+ @events = [e] + @events
295
+ }
296
+ }
297
+ end
298
+
299
+ # Returns an 8-character random ID used when connecting.
300
+ # This seems to have no bearing on functionality, but
301
+ # might come in handy, possibly.
302
+ def get_randID()
303
+ # The JS in the omegle page says:
304
+ # if(!randID||8!==randID.length)
305
+ # randID=function(){
306
+ # for(var a="",b=0;8>b;b++)
307
+ # var c=Math.floor(32*Math.random()),
308
+ # a=a+"23456789ABCDEFGHJKLMNPQRSTUVWXYZ".charAt(c);
309
+ # return a
310
+ # }();
311
+ str = "";
312
+ 8.times{ str += "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"[ (rand() * 32).to_i ] }
313
+
314
+ return str;
315
+ end
316
+
317
+ # Parse a JSON response from omegle,
318
+ # and add its events to the FIFO
319
+ def parse_response(str)
320
+ # win or null don't contain any events, so skip.
321
+ return if str == nil or (%w{win null}.include?(str.to_s.strip))
322
+
323
+ # try to parse
324
+ evts = JSON.parse(str)
325
+
326
+ # check it's events
327
+ return if not evts.is_a? Array
328
+
329
+ # add in order
330
+ add_events(evts)
331
+ end
332
+
333
+ end
metadata ADDED
@@ -0,0 +1,45 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: romegle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Stephen Wattam
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-05 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: A simple hello world gem
15
+ email: stephenwattam@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/omegle.rb
21
+ homepage: http://stephenwattam.com/projects/romegle
22
+ licenses: []
23
+ post_install_message:
24
+ rdoc_options: []
25
+ require_paths:
26
+ - lib
27
+ required_ruby_version: !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ requirements: []
40
+ rubyforge_project:
41
+ rubygems_version: 1.8.23
42
+ signing_key:
43
+ specification_version: 3
44
+ summary: A pure ruby interface to Omegle.com
45
+ test_files: []