sj-tinder 1.9.3

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.
@@ -0,0 +1,307 @@
1
+ # encoding: UTF-8
2
+ require 'time'
3
+
4
+ module Tinder
5
+ # A campfire room
6
+ class Room
7
+ attr_reader :id, :name
8
+
9
+ def initialize(connection, attributes = {})
10
+ @connection = connection
11
+ @id = attributes['id']
12
+ @name = attributes['name']
13
+ @loaded = false
14
+ end
15
+
16
+ # Join the room
17
+ # POST /room/#{id}/join.xml
18
+ # For whatever reason, #join() and #leave() are still xml endpoints
19
+ # whereas elsewhere in this API we're assuming json :\
20
+ def join
21
+ post 'join', 'xml'
22
+ end
23
+
24
+ # Leave a room
25
+ # POST /room/#{id}/leave.xml
26
+ def leave
27
+ post 'leave', 'xml'
28
+ stop_listening
29
+ end
30
+
31
+ # Get the url for guest access
32
+ def guest_url
33
+ "#{@connection.uri}/#{guest_invite_code}" if guest_access_enabled?
34
+ end
35
+
36
+ def guest_access_enabled?
37
+ load
38
+ @open_to_guests ? true : false
39
+ end
40
+
41
+ # The invite code use for guest
42
+ def guest_invite_code
43
+ load
44
+ @active_token_value
45
+ end
46
+
47
+ # Change the name of the room
48
+ def name=(name)
49
+ update :name => name
50
+ end
51
+ alias_method :rename, :name=
52
+
53
+ # Change the topic
54
+ def topic=(topic)
55
+ update :topic => topic
56
+ end
57
+
58
+ def update(attrs)
59
+ connection.put("/room/#{@id}.json", {:room => attrs})
60
+ end
61
+
62
+ # Get the current topic
63
+ def topic
64
+ reload!
65
+ @topic
66
+ end
67
+
68
+ # Lock the room to prevent new users from entering and to disable logging
69
+ def lock
70
+ post 'lock'
71
+ end
72
+
73
+ # Unlock the room
74
+ def unlock
75
+ post 'unlock'
76
+ end
77
+
78
+ # Post a new message to the chat room
79
+ def speak(message, options = {})
80
+ send_message(message)
81
+ end
82
+
83
+ def paste(message)
84
+ send_message(message, 'PasteMessage')
85
+ end
86
+
87
+ def play(sound)
88
+ send_message(sound, 'SoundMessage')
89
+ end
90
+
91
+ def tweet(url)
92
+ send_message(url, 'TweetMessage')
93
+ end
94
+
95
+ # Get the list of users currently chatting for this room
96
+ def users
97
+ @users ||= current_users
98
+ end
99
+
100
+ def current_users
101
+ reload!
102
+ @current_users
103
+ end
104
+
105
+ # return the user with the given id; if it isn't in our room cache,
106
+ # do a request to get it
107
+ def user(id)
108
+ if id
109
+ cached_user = users.detect {|u| u[:id] == id }
110
+ user = cached_user || fetch_user(id)
111
+ self.users << user
112
+ user
113
+ end
114
+ end
115
+
116
+ # Perform a request for the user with the given ID
117
+ def fetch_user(id)
118
+ user_data = connection.get("/users/#{id}.json")
119
+ user = user_data && user_data[:user]
120
+ user[:created_at] = Time.parse(user[:created_at])
121
+ user
122
+ end
123
+
124
+ # Modifies a hash representation of a Campfire message. Expands +:user_id+
125
+ # to a full hash at +:user+, generates Timestamp from +:created_at+.
126
+ #
127
+ # Full returned hash:
128
+ # * +:body+: the body of the message
129
+ # * +:user+: Campfire user, which is itself a hash, of:
130
+ # * +:id+: User id
131
+ # * +:name+: User name
132
+ # * +:email_address+: Email address
133
+ # * +:admin+: Boolean admin flag
134
+ # * +:created_at+: User creation timestamp
135
+ # * +:type+: User type (e.g. Member)
136
+ # * +:id+: Campfire message id
137
+ # * +:type+: Campfire message type
138
+ # * +:room_id+: Campfire room id
139
+ # * +:created_at+: Message creation timestamp
140
+ def parse_message(message)
141
+ message[:user] = user(message.delete(:user_id))
142
+ message[:created_at] = Time.parse(message[:created_at])
143
+ message
144
+ end
145
+
146
+ # Listen for new messages in the room, parsing them with #parse_message
147
+ # and then yielding them to the provided block as they arrive.
148
+ #
149
+ # room.listen do |m|
150
+ # room.speak "Go away!" if m[:body] =~ /Java/i
151
+ # end
152
+ def listen(options = {})
153
+ raise ArgumentError, "no block provided" unless block_given?
154
+
155
+ Tinder.logger.info "Joining #{@name}…"
156
+ join # you have to be in the room to listen
157
+
158
+ require 'json'
159
+ require 'hashie'
160
+ require 'multi_json'
161
+ require 'twitter/json_stream'
162
+
163
+ auth = connection.basic_auth_settings
164
+ options = {
165
+ :host => "streaming.#{Connection::HOST}",
166
+ :path => room_url_for('live'),
167
+ :auth => "#{auth[:username]}:#{auth[:password]}",
168
+ :timeout => 6,
169
+ :ssl => connection.options[:ssl]
170
+ }.merge(options)
171
+
172
+ Tinder.logger.info "Starting EventMachine server…"
173
+ EventMachine::run do
174
+ @stream = Twitter::JSONStream.connect(options)
175
+ Tinder.logger.info "Listening to #{@name}…"
176
+ @stream.each_item do |message|
177
+ message = Hashie::Mash.new(MultiJson.decode(message))
178
+ message = parse_message(message)
179
+ yield(message)
180
+ end
181
+
182
+ @stream.on_error do |message|
183
+ raise ListenFailed.new("got an error! #{message.inspect}!")
184
+ end
185
+
186
+ @stream.on_max_reconnects do |timeout, retries|
187
+ raise ListenFailed.new("Tried #{retries} times to connect. Got disconnected from #{@name}!")
188
+ end
189
+
190
+ # if we really get disconnected
191
+ raise ListenFailed.new("got disconnected from #{@name}!") if !EventMachine.reactor_running?
192
+ end
193
+ end
194
+
195
+ def listening?
196
+ @stream != nil
197
+ end
198
+
199
+ def stop_listening
200
+ return unless listening?
201
+
202
+ Tinder.logger.info "Stopped listening to #{@name}…"
203
+ @stream.stop
204
+ @stream = nil
205
+ end
206
+
207
+ # Get the transcript for the given date (returns an array of messages parsed
208
+ # via #parse_message, see #parse_message for format of returned message)
209
+ #
210
+ def transcript(transcript_date = Date.today)
211
+ unless transcript_date.is_a?(Date)
212
+ transcript_date = transcript_date.to_date
213
+ end
214
+ url = "/room/#{@id}/transcript/#{transcript_date.strftime('%Y/%m/%d')}.json"
215
+ connection.get(url)['messages'].map do |message|
216
+ parse_message(message)
217
+ end
218
+ end
219
+
220
+ # Search transcripts for the given term (returns an array of messages parsed
221
+ # via #parse_message, see #parse_message for format of returned message)
222
+ #
223
+ def search(term)
224
+ encoded_term = URI.encode(term)
225
+
226
+ room_messages = connection.get("/search/#{encoded_term}.json")["messages"].select do |message|
227
+ message[:room_id] == id
228
+ end
229
+
230
+ room_messages.map do |message|
231
+ parse_message(message)
232
+ end
233
+ end
234
+
235
+ def upload(file, content_type = nil, filename = nil)
236
+ require 'mime/types'
237
+ content_type ||= MIME::Types.type_for(filename || file)
238
+ raw_post(:uploads, { :upload => Faraday::UploadIO.new(file, content_type, filename) })
239
+ end
240
+
241
+ # Get the list of latest files for this room
242
+ def files(count = 5)
243
+ get(:uploads)['uploads'].map { |u| u['full_url'] }
244
+ end
245
+
246
+ # Get a list of recent messages
247
+ # Accepts a hash for options:
248
+ # * +:limit+: Restrict the number of messages returned
249
+ # * +:since_message_id+: Get messages created after the specified message id
250
+ def recent(options = {})
251
+ options = { :limit => 10, :since_message_id => nil }.merge(options)
252
+ # Build url manually, faraday has to be 8.0 to do this
253
+ url = "#{room_url_for(:recent)}?limit=#{options[:limit]}&since_message_id=#{options[:since_message_id]}"
254
+
255
+ connection.get(url)['messages'].map do |msg|
256
+ parse_message(msg)
257
+ end
258
+ end
259
+
260
+ protected
261
+
262
+ def load
263
+ reload! unless @loaded
264
+ end
265
+
266
+ def reload!
267
+ attributes = connection.get("/room/#{@id}.json")['room']
268
+
269
+ @id = attributes['id']
270
+ @name = attributes['name']
271
+ @topic = attributes['topic']
272
+ @full = attributes['full']
273
+ @open_to_guests = attributes['open_to_guests']
274
+ @active_token_value = attributes['active_token_value']
275
+ @current_users = attributes['users'].map do |user|
276
+ user[:created_at] = Time.parse(user[:created_at])
277
+ user
278
+ end
279
+
280
+ @loaded = true
281
+ end
282
+
283
+ def send_message(message, type = 'TextMessage')
284
+ post 'speak', {:message => {:body => message, :type => type}}
285
+ end
286
+
287
+ def get(action)
288
+ connection.get(room_url_for(action))
289
+ end
290
+
291
+ def post(action, body = nil)
292
+ connection.post(room_url_for(action), body)
293
+ end
294
+
295
+ def raw_post(action, body = nil)
296
+ connection.raw_post(room_url_for(action), body)
297
+ end
298
+
299
+ def room_url_for(action, format="json")
300
+ "/room/#{@id}/#{action}.#{format}"
301
+ end
302
+
303
+ def connection
304
+ @connection
305
+ end
306
+ end
307
+ end
@@ -0,0 +1,4 @@
1
+ # encoding: UTF-8
2
+ module Tinder
3
+ VERSION = '1.9.3' unless defined?(::Tinder::VERSION)
4
+ end
@@ -0,0 +1,101 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
7
+ <title>Tinder</title>
8
+ <link rel="stylesheet" type="text/css" href="http://opensoul.org/stylesheets/code.css" />
9
+ <link rel="stylesheet" type="text/css" href="stylesheets/style.css" />
10
+ <link href="http://opensoul.org/stylesheets/ci.css" rel="stylesheet" type="text/css" />
11
+ <script src="http://opensoul.org/javascripts/code_highlighter.js" type="text/javascript"></script>
12
+ <script src="http://opensoul.org/javascripts/ruby.js" type="text/javascript"></script>
13
+ </head>
14
+
15
+ <body>
16
+ <div id="collectiveidea">
17
+ <a href="http://collectiveidea.com"><img src="http://opensoul.org/images/header_logo.gif" alt="Collective Idea" class="logo" width="17" height="22" /></a>
18
+ <ul class="links">
19
+ <li><a href="http://daniel.collectiveidea.com/blog">Daniel</a></li>
20
+ <li><a href="http://opensoul.org">Brandon</a></li>
21
+ <li class="name"><a href="http://collectiveidea.com"><img src="http://opensoul.org/images/header_collectiveidea.gif" alt="Collective Idea" width="123" height="21" /></a></li>
22
+ </ul>
23
+ </div>
24
+ <div id="main">
25
+ <div id="header">
26
+ <h1><a href="/">Tinder</a></h1>
27
+ <p>Getting the campfire started</p>
28
+ <ul id="nav">
29
+ <li><a href="tinder">API Docs</a></li>
30
+ <li><a href="http://rubyforge.org/projects/tinder">RubyForge</a></li>
31
+ <li><a href="http://opensoul.org/tags/tinder">Blog</a></li>
32
+ </ul>
33
+ </div>
34
+ <div id="content">
35
+ <p>Tinder is an API for interfacing with <a href="http://campfirenow.com">Campfire</a>, the 37Signals chat application.</p>
36
+ <h2>Example</h2>
37
+
38
+ <pre><code class="ruby">campfire = Tinder::Campfire.new 'mysubdomain'
39
+ campfire.login 'myemail@example.com', 'mypassword'</code></pre>
40
+
41
+ <h3>Create, find and destroy rooms</h3>
42
+ <pre><code class="ruby">room = campfire.create_room 'New Room', 'My new campfire room to test tinder'
43
+ room = campfire.find_room_by_name 'Other Room'
44
+ room.destroy</code></pre>
45
+
46
+ <h3>Speak and Paste</h3>
47
+ <pre><code class="ruby">room.speak 'Hello world!'
48
+ room.paste File.read(&quot;path/to/your/file.txt&quot;)</code></pre>
49
+
50
+ <h3>Listening</h3>
51
+ <pre><code class="ruby">room.listen
52
+ #=&gt; [{:person=&gt;&quot;Brandon&quot;, :message=&gt;&quot;I'm getting very sleepy&quot;, :user_id=&gt;&quot;148583&quot;, :id=&gt;&quot;16434003&quot;}]
53
+
54
+ # or in block form
55
+ room.listen do |m|
56
+ room.speak 'Welcome!' if m[:message] == /hello/
57
+ end</code></pre>
58
+
59
+ <h3>Guest Access</h3>
60
+ <pre><code class="ruby">room.toggle_guest_access
61
+ room.guest_url #=> http://mysubdomain.campfirenow.com/11111
62
+ room.guest_invite_code #=> 11111</code></pre>
63
+
64
+ <h3>Change the name and topic</h3>
65
+ <pre><code class="ruby">room.name = 'Tinder Demo'
66
+ room.topic = 'Showing how to change the room name and topic with tinder…'</code></pre>
67
+
68
+ <h3>Users</h3>
69
+ <pre><code class="ruby">room.users
70
+ campfire.users # users in all rooms</code></pre>
71
+
72
+ <h3>Transcripts</h3>
73
+ <pre><code class="ruby">transcript = room.transcript(room.available_transcripts.first)
74
+ #=&gt; [{:message=&gt;&quot;foobar!&quot;, :user_id=&gt;&quot;99999&quot;, :person=&gt;&quot;Brandon&quot;, :id=&gt;&quot;18659245&quot;, :timestamp=&gt;Tue May 05 07:15:00 -0700 2009}]
75
+ </code></pre>
76
+
77
+ <p>See the <a href="tinder">API documentation</a> for more details.</p>
78
+
79
+ <h2>Installation</h2>
80
+
81
+ <p>Tinder can be installed as a gem or a Rails plugin. Install the gem by executing:</p>
82
+
83
+ <pre>gem install tinder</pre>
84
+
85
+ <p>Or, download it from <a href="http://rubyforge.org/frs/?group_id=2922">RubyForge</a>.</p>
86
+
87
+ <h2>Source</h2>
88
+
89
+ <p>Contributions are welcome and appreciated! The source is available from:</p>
90
+
91
+ <pre>http://github.com/collectiveidea/tinder</pre>
92
+ </div>
93
+ </div>
94
+ <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
95
+ </script>
96
+ <script type="text/javascript">
97
+ _uacct = "UA-194397-8";
98
+ urchinTracker();
99
+ </script>
100
+ </body>
101
+ </html>
@@ -0,0 +1,77 @@
1
+ body {
2
+ font-family: "Lucida Grande", Helvetica, Arial, sans-serif;
3
+ font-size: 76%;
4
+ background: #2A2A2A;
5
+ margin: 0;
6
+ padding: 0;
7
+ }
8
+
9
+ #collectiveidea {
10
+ border-bottom: 1px solid #444;
11
+ }
12
+
13
+ a {
14
+ color: #2D5385;
15
+ }
16
+
17
+ #main {
18
+ background-color: #FFF;
19
+ width: 700px;
20
+ margin: 0 auto;
21
+ border: 5px #CCC;
22
+ border-left-style: solid;
23
+ border-right-style: solid;
24
+ padding: 0 1em;
25
+ }
26
+
27
+ #header {
28
+ position: relative;
29
+ border-bottom: 1px solid #999;
30
+ padding: 1em;
31
+ }
32
+
33
+ #header h1 {
34
+ margin: 0;
35
+ padding: 0;
36
+ color: #2D5385;
37
+ }
38
+
39
+ #header h1 a {
40
+ text-decoration: none;
41
+ }
42
+
43
+ #header p {
44
+ margin: 0;
45
+ padding: 0;
46
+ font-size: 0.8em;
47
+ color: #999;
48
+ }
49
+
50
+ #nav {
51
+ list-style: none;
52
+ position: absolute;
53
+ right: 0;
54
+ top: 0.6em;
55
+ }
56
+ #nav li {
57
+ display: inline;
58
+ padding: 0 0.5em;
59
+ }
60
+
61
+ #content {
62
+ padding: 1em 0;
63
+ }
64
+
65
+ dl {
66
+ background-color: #DDD;
67
+ padding: 1em;
68
+ border: 1px solid #CCC;
69
+ }
70
+ dl .pronunciation {
71
+ color: #C00;
72
+ }
73
+ dl .description {
74
+ text-transform: uppercase;
75
+ font-size: 0.8em;
76
+ font-family: fixed;
77
+ }