romegle 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []