livejournaller 0.04
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.
- data/bin/ljpost +51 -0
- data/bin/ljsend +55 -0
- data/lib/livejournaller.rb +205 -0
- data/share/template +8 -0
- metadata +59 -0
data/bin/ljpost
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/opt/bin/ruby
|
|
2
|
+
|
|
3
|
+
LJPOSTDIR=File.join(ENV["HOME"],"/.ljpost")
|
|
4
|
+
|
|
5
|
+
if not File.exists?(LJPOSTDIR)
|
|
6
|
+
Dir.mkdir(LJPOSTDIR)
|
|
7
|
+
Dir.mkdir File.join(LJPOSTDIR,"outgoing")
|
|
8
|
+
Dir.mkdir File.join(LJPOSTDIR,"sent")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
require "tempfile"
|
|
12
|
+
require "yaml"
|
|
13
|
+
require "time"
|
|
14
|
+
|
|
15
|
+
template=File.join(File.dirname(File.dirname(__FILE__)),"share","template")
|
|
16
|
+
|
|
17
|
+
t=Tempfile.new("ljpost")
|
|
18
|
+
t.write(File.read(template))
|
|
19
|
+
t.close
|
|
20
|
+
|
|
21
|
+
system(ENV["VISUAL"],t.path)
|
|
22
|
+
|
|
23
|
+
fh=File.open(t.path,File::RDONLY)
|
|
24
|
+
|
|
25
|
+
text=fh.read
|
|
26
|
+
(headertext,bodytext)=text.split(/^--text follows this line--$/,2);
|
|
27
|
+
headers=Hash.new
|
|
28
|
+
headertext.split(/\n/).each do |line|
|
|
29
|
+
header,value=line.split(/: ?/,2)
|
|
30
|
+
headers[header]=value
|
|
31
|
+
end
|
|
32
|
+
headers["Body"]=bodytext
|
|
33
|
+
|
|
34
|
+
if headers["Date"] != "" then
|
|
35
|
+
headers["Date"]=Time.parse(headers["Date"])
|
|
36
|
+
headers["Backdate"] = true
|
|
37
|
+
else
|
|
38
|
+
headers["Date"]=Time.now
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# convert "Preformatted" boolean tag to actual boolean
|
|
42
|
+
headers["Preformatted"] = {
|
|
43
|
+
?t => true,
|
|
44
|
+
?y => true,
|
|
45
|
+
?f => false,
|
|
46
|
+
?n => false } [ headers["Preformatted"].downcase[0] ]
|
|
47
|
+
|
|
48
|
+
File.open("#{LJPOSTDIR}/outgoing/"+headers["Date"].strftime("%Y%m%d%H%M%S"),
|
|
49
|
+
"w") do |f|
|
|
50
|
+
f.write(headers.to_yaml)
|
|
51
|
+
end
|
data/bin/ljsend
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/ruby
|
|
2
|
+
|
|
3
|
+
LJPOSTDIR = File.join(ENV["HOME"],"/.ljpost")
|
|
4
|
+
LIVEJOURNALRC = File.join(ENV["HOME"],".livejournal.rc")
|
|
5
|
+
|
|
6
|
+
require "livejournaller"
|
|
7
|
+
require "tempfile"
|
|
8
|
+
require "yaml"
|
|
9
|
+
require "open3"
|
|
10
|
+
|
|
11
|
+
outgoingdir = File.join(LJPOSTDIR,"/outgoing")
|
|
12
|
+
sentdir = File.join(LJPOSTDIR,"sent")
|
|
13
|
+
ljparams = YAML.load(File.read(LIVEJOURNALRC))
|
|
14
|
+
|
|
15
|
+
Dir.open("#{outgoingdir}") do |d|
|
|
16
|
+
d.each do |filename|
|
|
17
|
+
pathname="#{outgoingdir}/#{filename}"
|
|
18
|
+
if File.stat(pathname).file?
|
|
19
|
+
posting=Hash.new("")
|
|
20
|
+
YAML.load(File.read(pathname)).each_pair do |key,value|
|
|
21
|
+
posting[key]=value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
opts = {}
|
|
25
|
+
|
|
26
|
+
{
|
|
27
|
+
"Mood" => :mood,
|
|
28
|
+
"Music" => :current_music,
|
|
29
|
+
"Userpic" => :picture,
|
|
30
|
+
"Security" => :security,
|
|
31
|
+
"Preformatted" => :preformatted,
|
|
32
|
+
"Backdate" => :backdated
|
|
33
|
+
}.each do |header, option|
|
|
34
|
+
if posting[header] != "" then
|
|
35
|
+
opts[option] = posting[header]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if posting["Date"] != "" then
|
|
40
|
+
unless posting["Date"].respond_to? :strftime
|
|
41
|
+
posting["Date"] = DateTime.parse(posting["Date"])
|
|
42
|
+
end
|
|
43
|
+
opts[:date] = posting["Date"]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
lj=LiveJournaller.new ljparams["user"], ljparams["password"]
|
|
47
|
+
retval = lj.post posting["Subject"], posting["Body"], opts
|
|
48
|
+
|
|
49
|
+
if retval["anum"] then
|
|
50
|
+
puts "Successfully posted item #{retval["anum"]} to LiveJournal."
|
|
51
|
+
File.rename pathname, File.join(sentdir,filename)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/ruby
|
|
2
|
+
|
|
3
|
+
require "rubygems"
|
|
4
|
+
gem "hpricot"
|
|
5
|
+
|
|
6
|
+
require "xmlrpc/client"
|
|
7
|
+
require "md5"
|
|
8
|
+
require "yaml"
|
|
9
|
+
require "hpricot"
|
|
10
|
+
|
|
11
|
+
class LiveJournaller
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
# Handles the challenge-response sequence before LiveJournal lets
|
|
15
|
+
# you call an API method.
|
|
16
|
+
def get_challenge
|
|
17
|
+
result = @client.call("LJ.XMLRPC.getchallenge")
|
|
18
|
+
challenge = result["challenge"]
|
|
19
|
+
response = MD5.md5(challenge + @password).to_s
|
|
20
|
+
|
|
21
|
+
@paramhash["auth_challenge"] = challenge
|
|
22
|
+
@paramhash["auth_response"] = response
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Calls a LiveJournal function name after handling the challenge
|
|
26
|
+
# that LJ goes through before you're allowed to use the API.
|
|
27
|
+
#
|
|
28
|
+
# This is a low-level support method.
|
|
29
|
+
def ljcall(ljfnname,params = {})
|
|
30
|
+
get_challenge
|
|
31
|
+
paramhash = @paramhash.merge Hash[*(params.map do |a,b|
|
|
32
|
+
[a.to_s,b]
|
|
33
|
+
end.flatten)]
|
|
34
|
+
@client.call "LJ.XMLRPC.#{ljfnname.to_s}", paramhash
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
public
|
|
38
|
+
|
|
39
|
+
# Creates a new LiveJournaller object.
|
|
40
|
+
#
|
|
41
|
+
# The parameters are your username, your password, and optionally
|
|
42
|
+
# a server. So, for instance, you could say:
|
|
43
|
+
#
|
|
44
|
+
# lj = LiveJournaller.new("myusername", "mypassword")
|
|
45
|
+
#
|
|
46
|
+
# to log into LiveJournal, or alternatively you could say
|
|
47
|
+
#
|
|
48
|
+
# gj = LiveJournaller.new("myusername, "mypassword",
|
|
49
|
+
# "www.greatestjournal.com")
|
|
50
|
+
#
|
|
51
|
+
# to log into Greatest Journal.
|
|
52
|
+
def initialize(user, password, server="www.livejournal.com")
|
|
53
|
+
@client = XMLRPC::Client.new server, "/interface/xmlrpc"
|
|
54
|
+
@user = user
|
|
55
|
+
@password = MD5.md5(password).to_s
|
|
56
|
+
@paramhash = { "username" => user,
|
|
57
|
+
"auth_method" => "challenge",
|
|
58
|
+
"ver" => 1 }
|
|
59
|
+
@restful_ish_client = Net::HTTP.new("www.livejournal.com")
|
|
60
|
+
@restful_ish_client_headers = {
|
|
61
|
+
"User-Agent" => "RubyLJ",
|
|
62
|
+
"Content-Type" => "text/xml; charset=UTF-8",
|
|
63
|
+
"Connection" => "keep-alive"
|
|
64
|
+
}
|
|
65
|
+
@comment_cache_dir = "db/ljcomments"
|
|
66
|
+
@comment_cache = File.join(@comment_cache_dir, "allcomments.xml")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
attr_reader :logindetails
|
|
70
|
+
|
|
71
|
+
# Defines a LiveJournal API function as a method, for those
|
|
72
|
+
# LiveJournal APIs that I figure it's safe to just define
|
|
73
|
+
# explicitly.
|
|
74
|
+
def self.lj_api *meths
|
|
75
|
+
meths.each do |meth|
|
|
76
|
+
eval %[
|
|
77
|
+
def #{meth.to_s} params = {}
|
|
78
|
+
ljcall :#{meth}, params
|
|
79
|
+
end
|
|
80
|
+
]
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
lj_api :checkfriends, :consolecommand, :editevent, :editfriendgroups,
|
|
85
|
+
:editfriends, :friendof, :getdaycounts, :getevents, :getfriends,
|
|
86
|
+
:getfriendgroups, :login, :postevent, :sessionexpire,
|
|
87
|
+
:sessiongenerate, :syncitems
|
|
88
|
+
|
|
89
|
+
# Returns a hash of your user details
|
|
90
|
+
def user_details; @user_details ||= login end
|
|
91
|
+
|
|
92
|
+
# Returns an array of your friend groups.
|
|
93
|
+
def friendgroups; user_details["friendgroups"] end
|
|
94
|
+
|
|
95
|
+
# Returns a list of all items you posted ever.
|
|
96
|
+
def all_items
|
|
97
|
+
@allitems ||= syncitems :lastsync => "1970-01-01 00:00:00"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def friends; @friends ||= getfriends["friends"] end
|
|
101
|
+
|
|
102
|
+
# Returns a livejournal entry with id +id+.
|
|
103
|
+
def item id
|
|
104
|
+
ljcall :getevents, :selecttype => "one",
|
|
105
|
+
:lastsync => "1970-01-01 00:00:00",
|
|
106
|
+
:itemid => id
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
def sessioncookie
|
|
111
|
+
@sessioncookie ||= sessiongenerate
|
|
112
|
+
"ljsession=#{@sessioncookie["ljsession"]}"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
public
|
|
116
|
+
def comment_summaries start_id = 0
|
|
117
|
+
@restful_ish_client_headers["Cookie"] ||= sessioncookie
|
|
118
|
+
unless @comments
|
|
119
|
+
@restful_ish_client.start do
|
|
120
|
+
response = @restful_ish_client.get("/export_comments.bml?get=comment_meta&startid=#{start_id}", @restful_ish_client_headers)
|
|
121
|
+
rexml = REXML::Document.new(response.body)
|
|
122
|
+
@max_comment_id = rexml.elements["//maxid"].text.to_i
|
|
123
|
+
@comment_summaries = rexml.elements.each("//comment") { }.sort_by do |c|
|
|
124
|
+
c.attributes["id"].to_i
|
|
125
|
+
end
|
|
126
|
+
@usermap = {}
|
|
127
|
+
rexml.elements.each("//usermap") do |elem|
|
|
128
|
+
@usermap[elem.attribute("id").value] =
|
|
129
|
+
elem.attribute("user").value
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
@comment_summaries
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def export_comments start_id=0
|
|
137
|
+
@restful_ish_client_headers["Cookie"] ||= sessioncookie
|
|
138
|
+
|
|
139
|
+
unless @comment_bodies
|
|
140
|
+
if File.exists?(@comment_cache)
|
|
141
|
+
@comment_bodies = File.read(@comment_cache)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
@restful_ish_client.start do
|
|
145
|
+
response = @restful_ish_client.get("/export_comments.bml?get=comment_body&startid=#{start_id}", @restful_ish_client_headers)
|
|
146
|
+
@comment_bodies = response.body
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
@comment_bodies
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Post an item to livejournal
|
|
153
|
+
# Required fields are +subject+ and +text+
|
|
154
|
+
# Optional fields: date, mood, music, preformatted, nocomments, picture,
|
|
155
|
+
# noemail
|
|
156
|
+
def post subject, text, options = {}
|
|
157
|
+
date = if options[:date] then
|
|
158
|
+
if String === options[:date] then
|
|
159
|
+
DateTime.parse(options[:date])
|
|
160
|
+
else
|
|
161
|
+
options[:date]
|
|
162
|
+
end
|
|
163
|
+
else
|
|
164
|
+
DateTime.now
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
callhash = {
|
|
169
|
+
:event => text,
|
|
170
|
+
:subject => subject,
|
|
171
|
+
:year => date.year,
|
|
172
|
+
:mon => date.month,
|
|
173
|
+
:day => date.day,
|
|
174
|
+
:hour => date.hour,
|
|
175
|
+
:min => date.min,
|
|
176
|
+
:lineendings => "unix",
|
|
177
|
+
:props => {}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if options[:security] then
|
|
181
|
+
callhash[:security]=options[:security]
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
{
|
|
185
|
+
:mood => :current_mood,
|
|
186
|
+
:music => :current_music,
|
|
187
|
+
:preformatted => :opt_preformatted,
|
|
188
|
+
:nocomments => :opt_nocomments,
|
|
189
|
+
:picture => :picture_keyword,
|
|
190
|
+
:noemail => :opt_noemail,
|
|
191
|
+
:backdated => :opt_backdated
|
|
192
|
+
}.each do |option_name, lj_option_name|
|
|
193
|
+
if options[option_name] then
|
|
194
|
+
callhash[:props][lj_option_name] = options[option_name]
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
postevent callhash
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def poster posterid, start_id = 0
|
|
202
|
+
comment_summaries start_id
|
|
203
|
+
@usermap[posterid.to_s]
|
|
204
|
+
end
|
|
205
|
+
end
|
data/share/template
ADDED
metadata
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: livejournaller
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: "0.04"
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Dave Brown
|
|
8
|
+
autorequire: livejournaller
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-10-27 00:00:00 +09:00
|
|
13
|
+
default_executable: ljpost
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
description: " This is a library to access the LiveJournal API from Ruby, and a\n script or two to let you write and submit LiveJournal entries from\n the command line.\n"
|
|
17
|
+
email: livejournaller@dagbrown.com
|
|
18
|
+
executables:
|
|
19
|
+
- ljpost
|
|
20
|
+
- ljsend
|
|
21
|
+
extensions: []
|
|
22
|
+
|
|
23
|
+
extra_rdoc_files: []
|
|
24
|
+
|
|
25
|
+
files:
|
|
26
|
+
- lib/livejournaller.rb
|
|
27
|
+
- bin/ljpost
|
|
28
|
+
- bin/ljsend
|
|
29
|
+
- share/template
|
|
30
|
+
has_rdoc: true
|
|
31
|
+
homepage:
|
|
32
|
+
licenses: []
|
|
33
|
+
|
|
34
|
+
post_install_message:
|
|
35
|
+
rdoc_options: []
|
|
36
|
+
|
|
37
|
+
require_paths:
|
|
38
|
+
- lib
|
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
40
|
+
requirements:
|
|
41
|
+
- - ">="
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: "0"
|
|
44
|
+
version:
|
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: "0"
|
|
50
|
+
version:
|
|
51
|
+
requirements: []
|
|
52
|
+
|
|
53
|
+
rubyforge_project:
|
|
54
|
+
rubygems_version: 1.3.5
|
|
55
|
+
signing_key:
|
|
56
|
+
specification_version: 3
|
|
57
|
+
summary: A LiveJournal access library.
|
|
58
|
+
test_files: []
|
|
59
|
+
|