1 """
2 Handles the Campfire room.
3 """
4 from datetime import datetime
5 import re
6 import time
7 import urlparse
8
9 from BeautifulSoup import BeautifulSoup
10
12 "A Campfire room."
13 - def __init__(self, campfire, id, name=None):
14 self._campfire = campfire
15 self._verify_response = campfire._verify_response
16 self._post = campfire._post
17 self._get = campfire._get
18 self._room = None
19
20 self.membership_key = self.user_id = None
21 self.last_cache_id = self.timestamp = None
22 self.idle_since = datetime.now()
23
24
25 self.id = id
26
27 self.name = name
28
29 self.uri = urlparse.urlparse("%s/room/%s" % (urlparse.urlunparse(self._campfire.uri), self.id))
30
32 return "<Room: %s>" % self.name
33
35 return self.id == other.id
36
37 - def join(self, force=False):
38 """Joins the room; if 'force' is True join even if already joined.
39
40 Returns True if successfully joined, False otherwise. """
41 if not self._room or force:
42 self._room = self._get("room/%s" % self.id)
43 if not self._verify_response(self._room, success=True):
44 self._room = None
45 return False
46 self._get_room_data()
47 self.ping()
48 return True
49
51 """Leaves the room.
52
53 Returns True if successfully left, False otherwise."""
54 has_left = self._verify_response(
55 self._post('room/%s/leave' % self.id), redirect=True)
56 self._room = self.membership_key = self.user_id = None
57 self.last_cache_id = self.timestamp = self.idle_since = None
58 return has_left
59
61 """Toggles guest access on and off.
62
63 Returns True if successfully toggled, False otherwise."""
64 response_result = self._verify_response(
65 self._post(
66 'room/%s/toggle_guest_access' % self.id), success=True)
67
68
69 return response_result and self.join(True)
70
72 """Checks if the guest access is enabled.
73
74 Returns True if the guest access is enabled, False otherwise."""
75 return self.guest_url() is not None
76
78 """Gets the URL for guest access.
79
80 Returns None if the guest access is not enabled."""
81 self.join()
82 soup = BeautifulSoup(self._room.body)
83 try:
84 return soup.find('div', {'id': 'guest_access_control'}).h4.string
85 except AttributeError:
86 return None
87
89 """Gets the invite code for guest access.
90
91 Returns None if the guest access is not enabled."""
92 url = self.guest_url()
93 if url:
94 return re.search(r'\/(\w*)$', url).groups(0)[0]
95
97 """Changes the name of the room.
98
99 Returns the new name if successfully changed, None otherwise."""
100 response = self._post('account/edit/room/%s' % self.id,
101 {'room[name]': name}, ajax=True)
102 if self._verify_response(response, success=True):
103 self.name = name
104 return self.name
105 rename = change_name
106
108 """Changes the topic of the room.
109
110 Returns the new topic if successfully changed, None otherwise."""
111 response = self._post('room/%s/change_topic' % self.id,
112 {'room[topic]': topic}, ajax=True)
113 if self._verify_response(response, success=True):
114 return topic
115
117 """Gets the current topic, if any."""
118 self.join()
119 soup = BeautifulSoup(self._room.body)
120 h = soup.find(attrs={'id': 'topic'})
121 if h:
122 def _is_navigable_string(tag):
123 return not hasattr(tag, 'attrs')
124 topic = filter(_is_navigable_string, h.contents)
125 return repr("".join(topic).strip())
126
128 """Locks the room to prevent new users from entering.
129
130 Returns True if successfully locked, False otherwise."""
131 return self._verify_response(self._post(
132 'room/%s/lock' % self.id, {}, ajax=True), success=True)
133
135 """Unlocks the room.
136
137 Returns True if successfully unlocked, False otherwise."""
138 return self._verify_response(self._post(
139 'room/%s/unlock' % self.id, {}, ajax=True), success=True)
140
141 - def ping(self, force=False):
142 """Pings the server updating the last time we have been seen there.
143
144 Returns True if successfully pinged, False otherwise."""
145 now = datetime.now()
146 delta = self.idle_since - now
147 if delta.seconds < 60 or force:
148 self.idle_since = datetime.now()
149 return self._verify_response(self._post(
150 'room/%s/tabs' % self.id, {}, ajax=True), success=True)
151 return False
152
154 """Destroys the room.
155
156 Returns True if successfully destroyed, False otherwise."""
157 return self._verify_response(self._post(
158 'account/delete/room/%s' % self.id), success=True)
159
161 """Lists the users chatting in the room.
162
163 Returns a set of the users."""
164 return self._campfire.users(self.name)
165
166 - def speak(self, message):
167 """Send a message to the room.
168
169 Returns the message if successfully sent it, None otherwise."""
170 self.join()
171 return self._send(message)
172
173 - def paste(self, message):
174 """Paste a message to the room.
175
176 Returns the message if successfully pasted it, None otherwise."""
177 self.join()
178 return self._send(message, {'paste': True})
179
181 """Gets the dates of transcripts of the room.
182
183 Returns a list of dates."""
184 return self._campfire.transcripts(self.id)
185
187 """Get the transcript for the given date (a datetime.date instance).
188
189 Returns a list of message data:
190 * id: the id of the message
191 * person: the name of the person who wrote the message if any
192 * user_id: the user id of the person if any
193 * message: the message itself if any"""
194 uri = 'room/%s/transcript/%s' % (self.id, date.strftime('%Y/%m/%d'))
195
196 soup = BeautifulSoup(self._get(uri).body)
197
198 def _filter_messages(tag):
199 return tag.has_key('class') and 'message' in tag['class'].split()
200 messages = soup.findAll(_filter_messages)
201
202 all_transcript = []
203 for message in messages:
204 t = {}
205
206 person = message.find(True, attrs={'class': 'person'})
207 try:
208 t['person'] = person.span.string
209 except AttributeError:
210 try:
211 t['person'] = person.string
212 except AttributeError:
213 t['person'] = None
214
215 body = message.find('td', attrs={'class': 'body'})
216 try:
217 t['message'] = body.div.string
218 except AttributeError:
219 t['message'] = None
220
221 t['id'] = re.search(r'message_(\d+)', message['id']).groups()[0]
222 match = re.search(r'user_(\d+)', message['class'])
223 if match:
224 t['user_id'] = match.groups()[0]
225 else:
226 t['user_id'] = None
227
228 all_transcript.append(t)
229
230 return all_transcript
231
232 - def _send(self, message, options={}):
233 data = {'message': message, 't': int(time.time())}
234 data.update(options)
235 response = self._post('room/%s/speak' % self.id, data, ajax=True)
236 if self._verify_response(response, success=True):
237 return message
238
240 self.membership_key = re.search(r'\"membershipKey\": \"([a-z0-9]+)\"',
241 self._room.body).groups(0)[0]
242 self.user_id = re.search(r'\"userID\": (\d+)',
243 self._room.body).groups(0)[0]
244 self.last_cache_id = re.search(r'\"lastCacheID\": (\d+)',
245 self._room.body).groups(0)[0]
246 self.timestamp = re.search(r'\"timestamp\": (\d+)',
247 self._room.body).groups(0)[0]
248
249
250
251 __all__ = ['Room']
252