livejournal 0.0.1

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,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 :