livejournal 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/ruby
2
+ #--
3
+ # ljrb -- LiveJournal Ruby module
4
+ # Copyright (c) 2005 Evan Martin <martine@danga.com>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+ #++
24
+
25
+ require 'livejournal/request'
26
+
27
+ module LiveJournal
28
+ # LiveJournal times have no time zone, as they are for display only:
29
+ # "I wrote this post at midnight". However, it's convenient to represent
30
+ # times with a Ruby time object. But when time zones get involved,
31
+ # everything gets confused; we'd like to store a Unix time in the database
32
+ # and those only make sense as GMT. To reduce confusion, then, we imagine
33
+ # all LiveJournal times are in GMT.
34
+ # This function takes a time in any time zone and just switches the
35
+ # timezone to GMT. That is, coerce_gmt of 11:12pm PST is 11:12 pm GMT.
36
+ # The entry time-setting functions require this GMT part to verify you're
37
+ # thinking carefully about time when you use Entry#time. If I want an
38
+ # entry that has a time that corresponds to what I feel is "now", I'd use
39
+ # LiveJournal::coerce_gmt Time.now
40
+ def self.coerce_gmt time
41
+ expanded = time.to_a
42
+ expanded[8] = false # dst flag
43
+ expanded[9] = 'GMT'
44
+ return Time.gm(*expanded)
45
+ end
46
+
47
+ class Entry
48
+ attr_accessor :itemid, :anum, :subject, :event, :moodid, :mood
49
+ attr_accessor :music, :taglist, :pickeyword, :preformatted, :backdated
50
+ attr_accessor :comments # values: {:normal, :none, :noemail}
51
+ attr_reader :time # a Ruby Time object
52
+ attr_accessor :security # values: {:public, :private, :friends, :custom}
53
+ attr_accessor :allowmask
54
+ attr_accessor :screening # values {:default, :all, :anonymous, :nonfriends, :none}
55
+ attr_accessor :props
56
+
57
+ def initialize
58
+ @subject = nil
59
+ @event = nil
60
+ @moodid = nil
61
+ @mood = nil
62
+ @music = nil
63
+ @taglist = []
64
+ @pickeyword = nil
65
+ @preformatted = false
66
+ @backdated = false
67
+ @comments = :normal
68
+ @time = nil
69
+ @security = :public
70
+ @allowmask = nil
71
+ @screening = :default
72
+ @props = {}
73
+ end
74
+
75
+ def ==(other)
76
+ [:subject, :event, :moodid, :mood, :music, :taglist, :pickeyword,
77
+ :preformatted, :backdated, :comments, :time, :security, :allowmask,
78
+ :screening, :props].each do |attr|
79
+ return false if send(attr) != other.send(attr)
80
+ end
81
+ return true
82
+ end
83
+
84
+ def time=(time)
85
+ raise RuntimeError, "Must use GMT times everywhere to reduce confusion. See LiveJournal::coerce_gmt for details." unless time.gmt?
86
+ @time = time
87
+ end
88
+
89
+ def from_request(req)
90
+ @itemid, @anum = req['itemid'].to_i, req['anum'].to_i
91
+ @subject, @event = req['subject'], CGI.unescape(req['event'])
92
+
93
+ case req['security']
94
+ when 'public'
95
+ @security = :public
96
+ when 'private'
97
+ @security = :private
98
+ when 'usemask'
99
+ if req['allowmask'] == '1'
100
+ @security = :friends
101
+ else
102
+ @security = :custom
103
+ @allowmask = req['allowmask'].to_i
104
+ end
105
+ end
106
+
107
+ @time = LiveJournal::Request::ljtime_to_time req['eventtime']
108
+
109
+ # further metadata is loaded via #load_prop
110
+
111
+ self
112
+ end
113
+ def load_prop(name, value) #:nodoc:#
114
+ case name
115
+ when 'current_mood'
116
+ @mood = value.to_i
117
+ when 'current_moodid'
118
+ @moodid = value.to_i
119
+ when 'current_music'
120
+ @music = value
121
+ when 'taglist'
122
+ @taglist = value.split(/, /).sort
123
+ when 'picture_keyword'
124
+ @pickeyword = value
125
+ when 'opt_preformatted'
126
+ @preformatted = value == '1'
127
+ when 'opt_nocomments'
128
+ @comments = :none
129
+ when 'opt_noemail'
130
+ @comments = :noemail
131
+ when 'opt_backdated'
132
+ @backdated = value == '1'
133
+ when 'opt_screening'
134
+ case value
135
+ when 'A'; @screening = :all
136
+ when 'R'; @screening = :anonymous
137
+ when 'F'; @screening = :nonfriends
138
+ when 'N'; @screening = :none
139
+ else
140
+ raise LiveJournalException,
141
+ "unknown opt_screening value #{value.inspect}"
142
+ end
143
+ when 'hasscreened'
144
+ @screened = value == '1'
145
+ # and then some props we just store by name
146
+ when 'revnum'; @props[name] = value
147
+ when 'revtime'; @props[name] = value
148
+ when 'commentalter'; @props[name] = value
149
+ when 'unknown8bit'; @props[name] = value
150
+ else
151
+ @props[name] = value
152
+ raise Request::ProtocolException, "unknown prop (#{name}, #{value})"
153
+ end
154
+ end
155
+
156
+ # Get the numeric id used in URLs (it's a function of the itemid and the
157
+ # anum).
158
+ def display_itemid
159
+ (@itemid << 8) + @anum
160
+ end
161
+
162
+ # Render LJ markup to an HTML simulation.
163
+ # (The server to use is for rendering links to other LJ users.)
164
+ def event_as_html server=LiveJournal::DEFAULT_SERVER
165
+ # I'd like to use REXML but the content isn't XML, so REs it is!
166
+ html = @event.dup
167
+ html.gsub!(/\n/, "<br/>\n") unless @preformatted
168
+ html.gsub!(%r{< \s* lj \s+ user \s* = \s*
169
+ ['"]? ([^\s'"]+) ['"]?
170
+ \s* /? \s* >}ix) do
171
+ user = $1
172
+ url = "#{server.url}/~#{user}/"
173
+ "<a href='#{url}'><b>#{user}</b></a>"
174
+ end
175
+ html
176
+ end
177
+ end
178
+
179
+ module Request
180
+ class GetEvents < Req
181
+ # We support three different types of GetEvents:
182
+ # * <tt>GetEvents.new(user, :itemid => itemid)</tt> (fetch a single item)
183
+ # * <tt>GetEvents.new(user, :recent => n)</tt> (fetch most recent n itemds)
184
+ # * <tt>GetEvents.new(user, :lastsync => lastsync)</tt> (for syncing)
185
+ def initialize(user, opts)
186
+ super(user, 'getevents')
187
+ self['lineendings'] = 'unix'
188
+
189
+ if opts.has_key? :itemid
190
+ self['selecttype'] = 'one'
191
+ self['itemid'] = opts[:itemid]
192
+ elsif opts.has_key? :recent
193
+ self['selecttype'] = 'lastn'
194
+ self['howmany'] = opts[:recent]
195
+ elsif opts.has_key? :lastsync
196
+ self['selecttype'] = 'syncitems'
197
+ self['lastsync'] = opts[:lastsync] if opts[:lastsync]
198
+ end
199
+ end
200
+
201
+ # Returns either a single #Entry or an array of #Entry, depending on
202
+ # the mode this was constructed with.
203
+ def run
204
+ super
205
+
206
+ entries = {}
207
+ each_in_array('events') do |req|
208
+ entry = Entry.new.from_request(req)
209
+ entries[entry.itemid] = entry
210
+ end
211
+
212
+ each_in_array('prop') do |prop|
213
+ itemid = prop['itemid'].to_i
214
+ entries[itemid].load_prop(prop['name'], prop['value'])
215
+ end
216
+
217
+ if @reqparams.has_key? 'itemid'
218
+ return entries[@reqparams['itemid']]
219
+ else
220
+ return entries
221
+ end
222
+ end
223
+ end
224
+ class EditEvent < Req
225
+ def initialize(user, entry, opts={})
226
+ super(user, 'editevent')
227
+ self['itemid'] = entry.itemid
228
+ if entry.event
229
+ self['event'] = entry.event
230
+ elsif entry.entry.nil? and opts.has_key? :delete
231
+ self['event'] = ''
232
+ else
233
+ raise AccidentalDeleteError
234
+ end
235
+ self['lineendings'] = 'unix'
236
+ self['subject'] = entry.subject
237
+
238
+ case entry.security
239
+ when :public
240
+ self['security'] = 'public'
241
+ when :friends
242
+ self['security'] = 'usemask'
243
+ self['allowmask'] = 1
244
+ when :private
245
+ self['security'] = 'private'
246
+ when :custom
247
+ self['security'] = 'usemask'
248
+ self['allowmask'] = entry.allowmask
249
+ end
250
+
251
+ self['year'], self['mon'], self['day'] =
252
+ entry.time.year, entry.time.mon, entry.time.day
253
+ self['hour'], self['min'] = entry.time.hour, entry.time.min
254
+
255
+ { 'current_mood' => entry.mood,
256
+ 'current_moodid' => entry.moodid,
257
+ 'current_music' => entry.music,
258
+ 'picture_keyword' => entry.pickeyword,
259
+ 'taglist' => entry.taglist.join(', '),
260
+ 'opt_preformatted' => entry.preformatted ? 1 : 0,
261
+ 'opt_nocomments' => entry.comments == :none ? 1 : 0,
262
+ 'opt_noemail' => entry.comments == :noemail ? 1 : 0,
263
+ 'opt_backdated' => entry.backdated ? 1 : 0,
264
+ 'opt_screening' =>
265
+ case entry.screening
266
+ when :all; 'A'
267
+ when :anonymous; 'R'
268
+ when :nonfriends; 'F'
269
+ when :none; 'N'
270
+ when :default; ''
271
+ end
272
+ }.each do |name, value|
273
+ self["prop_#{name}"] = value
274
+ end
275
+ end
276
+ end
277
+ end
278
+ end
279
+
280
+ # vim: ts=2 sw=2 et :
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/ruby
2
+ #--
3
+ # ljrb -- LiveJournal Ruby module
4
+ # Copyright (c) 2005 Evan Martin <martine@danga.com>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+ #++
24
+
25
+ require 'livejournal/request'
26
+
27
+ module LiveJournal
28
+ # Represents a LiveJournal friend relationship.
29
+ # See LiveJournal::Request::Friends to get an array of these.
30
+ class Friend
31
+ attr_accessor :username, :fullname
32
+ attr_accessor :background, :foreground, :groupmask, :type
33
+ def initialize
34
+ @username = nil
35
+ @fullname = nil
36
+ @background = nil
37
+ @foreground = nil
38
+ @groupmask = nil
39
+ @type = nil
40
+ end
41
+ def from_request(req)
42
+ @username = req['user']
43
+ @fullname = req['name']
44
+ @foreground = req['fg']
45
+ @background = req['bg']
46
+ @groupmask = req['groupmask']
47
+ @type = req['type']
48
+ self
49
+ end
50
+ def to_s
51
+ "#{@username}: #{@fullname}"
52
+ end
53
+ end
54
+
55
+ module Request
56
+ class Friends < Req
57
+ attr_reader :friends
58
+ def initialize(user)
59
+ super(user, 'getfriends')
60
+ @friends = nil
61
+ end
62
+ # Returns an array of LiveJournal::Friend.
63
+ def run
64
+ super
65
+ @friends = build_array('friend') { |r| Friend.new.from_request(r) }
66
+ @friends
67
+ end
68
+ end
69
+ class FriendOfs < Req
70
+ attr_reader :friendofs
71
+ def initialize(user)
72
+ super(user, 'friendof')
73
+ @friendofs = nil
74
+ end
75
+ # Returns an array of LiveJournal::Friend.
76
+ def run
77
+ super
78
+ @friends = build_array('friendof') { |r| Friend.new.from_request(r) }
79
+ @friends
80
+ end
81
+ end
82
+
83
+ class CheckFriends < Req
84
+ attr_reader :interval
85
+ def initialize(user, lastupdate=nil)
86
+ super(user, 'checkfriends')
87
+ @lastupdate = lastupdate
88
+ @interval = 90 # reasonable default?
89
+ end
90
+ # Returns true if there are new posts available.
91
+ def run
92
+ self['lastupdate'] = @lastupdate if @lastupdate
93
+ super
94
+ @lastupdate = self['lastupdate']
95
+ @interval = self['interval']
96
+ self['new'] == '1'
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ # vim: ts=2 sw=2 et :
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/ruby
2
+ #--
3
+ # ljrb -- LiveJournal Ruby module
4
+ # Copyright (c) 2005 Evan Martin <martine@danga.com>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+ #++
24
+
25
+ require 'livejournal/request'
26
+
27
+ module LiveJournal
28
+ module Request
29
+ class Login < Req
30
+ def initialize(user)
31
+ super(user, 'login')
32
+ end
33
+ def run
34
+ super
35
+ u = @user # should we clone here?
36
+ u.fullname = @result['name']
37
+ u
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ # vim: ts=2 sw=2 et :
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/ruby
2
+ #--
3
+ # ljrb -- LiveJournal Ruby module
4
+ # Copyright (c) 2005 Evan Martin <martine@danga.com>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+ #++
24
+ #
25
+ # This module extends the LiveJournal module to work with LogJam's data.
26
+
27
+ require 'rexml/document' # parsing logjam conf
28
+
29
+ module LiveJournal
30
+ module LogJam
31
+ # Path to LogJam data.
32
+ def self.logjam_path
33
+ File.expand_path '~/.logjam'
34
+ end
35
+
36
+ def self.xml_fetch(file, path) #:nodoc:
37
+ doc = REXML::Document.new(File.open(file))
38
+ doc.elements.each(path) { |element| return element.text }
39
+ return nil
40
+ end
41
+
42
+ # Name of LogJam's current server.
43
+ def self.current_server
44
+ xml_fetch(logjam_path + '/conf.xml', '/configuration/currentserver')
45
+ end
46
+
47
+ # Path to LogJam's data for a given server.
48
+ def self.server_path servername
49
+ logjam_path + '/servers/' + servername # is escaping needed here?
50
+ end
51
+
52
+ # Username for a given server's current user.
53
+ def self.current_user servername
54
+ xml_fetch(server_path(servername) + '/conf.xml',
55
+ '/server/currentuser')
56
+ end
57
+
58
+ # Path to a given user's data.
59
+ def self.user_path servername, username
60
+ server_path(servername) + "/users/#{username}"
61
+ end
62
+
63
+ # Return [current_server, current_user].
64
+ def self.current_server_user
65
+ server = current_server
66
+ user = current_user server
67
+ [server, user]
68
+ end
69
+
70
+ def self.database_from_server_user servername, username
71
+ Database.new(LogJam::user_path(servername, username) + "/journal.db")
72
+ end
73
+ end
74
+ end
75
+
76
+ # vim: ts=2 sw=2 et :