sunflower 0.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/sunflower/commontasks.rb +1 -1
- data/lib/sunflower/core.rb +98 -64
- data/lib/sunflower/listmaker.rb +14 -14
- metadata +4 -4
@@ -101,7 +101,7 @@ class Page
|
|
101
101
|
|
102
102
|
def code_cleanup
|
103
103
|
# simple, safe code cleanup
|
104
|
-
# use
|
104
|
+
# use Sunflower.always_do_code_cleanup=true to do it automatically just before saving page
|
105
105
|
# based on Nux's cleaner: http://pl.wikipedia.org/wiki/Wikipedysta:Nux/wp_sk.js
|
106
106
|
str=self.text.gsub(/\r\n/,"\n")
|
107
107
|
|
data/lib/sunflower/core.rb
CHANGED
@@ -12,18 +12,31 @@ require 'cgi'
|
|
12
12
|
# s = Sunflower.new.login
|
13
13
|
#
|
14
14
|
# Then you can request data from API using #API method.
|
15
|
-
#
|
15
|
+
#
|
16
|
+
# To log data to file, use #log method (works like puts). Use RestClient.log=<io> to log all requests.
|
17
|
+
#
|
16
18
|
# You can use multiple Sunflowers at once, to work on multiple wikis.
|
19
|
+
|
20
|
+
class SunflowerError < StandardError; end
|
21
|
+
|
17
22
|
class Sunflower
|
23
|
+
VERSION = '0.3'
|
24
|
+
|
25
|
+
INVALID_CHARS = %w(# < > [ ] | { })
|
26
|
+
INVALID_CHARS_REGEX = Regexp.union *INVALID_CHARS
|
27
|
+
|
18
28
|
# Path to user data file.
|
19
29
|
def self.path
|
20
30
|
File.join(ENV['HOME'], 'sunflower-userdata')
|
21
31
|
end
|
22
32
|
|
33
|
+
# Options for this Sunflower.
|
34
|
+
attr_accessor :summary, :always_do_code_cleanup
|
35
|
+
|
23
36
|
attr_accessor :cookie, :headers, :wikiURL, :warnings, :log
|
24
37
|
|
25
38
|
# Initialize a new Sunflower working on a wiki with given URL, for ex. "pl.wikipedia.org".
|
26
|
-
def initialize url=
|
39
|
+
def initialize url=nil
|
27
40
|
begin
|
28
41
|
r=File.read(Sunflower.path)
|
29
42
|
@userdata=r.split(/\r?\n/).map{|i| i.strip}
|
@@ -31,30 +44,34 @@ class Sunflower
|
|
31
44
|
@userdata=[]
|
32
45
|
end
|
33
46
|
|
34
|
-
if url
|
47
|
+
if !url
|
35
48
|
if !@userdata.empty?
|
36
49
|
url=@userdata[0]
|
37
50
|
else
|
38
|
-
raise
|
51
|
+
raise SunflowerError, 'initialize: no URL supplied and no userdata found!'
|
39
52
|
end
|
40
53
|
end
|
41
54
|
|
42
55
|
@warnings=true
|
43
|
-
@log=
|
56
|
+
@log=false
|
44
57
|
|
45
58
|
@wikiURL=url
|
46
|
-
@logData=''
|
47
59
|
|
48
60
|
@loggedin=false
|
49
61
|
end
|
50
62
|
|
51
|
-
# Call the API. Returns a hash of JSON response.
|
63
|
+
# Call the API. Returns a hash of JSON response. Request can be a HTTP request string or a hash.
|
52
64
|
def API request
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
65
|
+
if request.is_a? String
|
66
|
+
request += '&format=json'
|
67
|
+
elsif request.is_a? Hash
|
68
|
+
request.merge!({format:'json'})
|
69
|
+
end
|
70
|
+
|
71
|
+
resp = RestClient.post(
|
72
|
+
'http://'+@wikiURL+'/w/api.php',
|
73
|
+
request,
|
74
|
+
{:user_agent => "Sunflower #{VERSION} alpha", :cookies => @cookies}
|
58
75
|
)
|
59
76
|
JSON.parse resp.to_str
|
60
77
|
end
|
@@ -66,13 +83,11 @@ class Sunflower
|
|
66
83
|
user=@userdata[1] if user==''
|
67
84
|
password=@userdata[2] if password==''
|
68
85
|
else
|
69
|
-
raise
|
86
|
+
raise SunflowerError, 'login: no user/pass supplied and no userdata found!'
|
70
87
|
end
|
71
88
|
end
|
72
89
|
|
73
|
-
if user
|
74
|
-
raise RuntimeError, 'Sunflower - bad username!'
|
75
|
-
end
|
90
|
+
raise SunflowerError, 'bad username!' if user =~ INVALID_CHARS_REGEX
|
76
91
|
|
77
92
|
|
78
93
|
# 1. get the login token
|
@@ -103,55 +118,52 @@ class Sunflower
|
|
103
118
|
})
|
104
119
|
|
105
120
|
|
106
|
-
raise
|
121
|
+
raise SunflowerError, 'unable to log in (no cookies received)!' if !@cookies
|
107
122
|
|
108
123
|
|
109
|
-
|
124
|
+
# 3. confirm you did log in by checking the watchlist.
|
125
|
+
@loggedin=true
|
110
126
|
r=self.API('action=query&list=watchlistraw')
|
111
127
|
if r['error'] && r['error']['code']=='wrnotloggedin'
|
112
128
|
@loggedin=false
|
113
|
-
raise
|
129
|
+
raise SunflowerError, 'unable to log in!'
|
114
130
|
end
|
115
131
|
|
132
|
+
# 4. check bot rights
|
116
133
|
r=self.API('action=query&list=allusers&aulimit=1&augroup=bot&aufrom='+user)
|
117
134
|
unless r['query']['allusers'][0]['name']==user
|
118
|
-
|
119
|
-
@
|
135
|
+
warn 'Sunflower - this user does not have bot rights!' if @warnings
|
136
|
+
@is_bot=false
|
120
137
|
else
|
121
|
-
@
|
138
|
+
@is_bot=true
|
122
139
|
end
|
123
140
|
|
124
141
|
return self
|
125
142
|
end
|
126
143
|
|
127
|
-
def
|
128
|
-
|
129
|
-
if @log
|
130
|
-
f=File.open('log.txt','a')
|
131
|
-
f.write t
|
132
|
-
f.close
|
133
|
-
end
|
144
|
+
def log t
|
145
|
+
File.open('log.txt','a'){|f| f.puts t}
|
134
146
|
end
|
135
147
|
|
136
|
-
def
|
137
|
-
|
138
|
-
end
|
139
|
-
|
140
|
-
def isBot?
|
141
|
-
@haveBotRights
|
148
|
+
def is_bot?
|
149
|
+
@is_bot
|
142
150
|
end
|
143
151
|
end
|
144
152
|
|
145
153
|
# Class representng single Wiki page. To load specified page, use #new/#get/#load method.
|
146
154
|
#
|
155
|
+
# When calling Page.new, at first only the text will be loaded - attributes and edit token will be loaded when needed, or when you call #preload_attrs.
|
156
|
+
#
|
147
157
|
# If you are using multiple Sunflowers, you have to specify which wiki this page belongs to using second argument of function; you can pass whole URL (same as when creating new Sunflower) or just language code.
|
148
158
|
#
|
149
|
-
# To save page, use #save/#put method. Optional argument is new title page, if ommited, page is saved at old title. Summary can be passed as second parameter. If it's ommited,
|
159
|
+
# To save page, use #save/#put method. Optional argument is new title page, if ommited, page is saved at old title. Summary can be passed as second parameter. If it's ommited, s.summary is used. If it's empty too, error is raised.
|
150
160
|
#
|
151
161
|
# To get Sunflower instance which this page belongs to, use #sunflower of #belongs_to.
|
152
162
|
class Page
|
153
|
-
|
163
|
+
INVALID_CHARS = %w(# < > [ ] | { })
|
164
|
+
INVALID_CHARS_REGEX = Regexp.union *INVALID_CHARS
|
154
165
|
|
166
|
+
attr_accessor :text
|
155
167
|
attr_reader :orig_text
|
156
168
|
|
157
169
|
attr_reader :sunflower
|
@@ -159,8 +171,16 @@ class Page
|
|
159
171
|
|
160
172
|
attr_reader :pageid, :ns, :title, :touched, :lastrevid, :counter, :length, :starttimestamp, :edittoken, :protection #prop=info
|
161
173
|
|
174
|
+
# calling any of these accessors will fetch the data.
|
175
|
+
[:pageid, :ns, :title, :touched, :lastrevid, :counter, :length, :starttimestamp, :edittoken, :protection].each do |meth|
|
176
|
+
define_method meth do
|
177
|
+
preload_attrs unless @preloaded_attrs
|
178
|
+
instance_variable_get "@#{meth}"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
162
182
|
def initialize title='', wiki=''
|
163
|
-
raise
|
183
|
+
raise SunflowerError, 'title invalid: '+title if title =~ INVALID_CHARS_REGEX
|
164
184
|
|
165
185
|
@modulesExecd=[] #used by sunflower-commontasks.rb
|
166
186
|
@summaryAppend=[] #used by sunflower-commontasks.rb
|
@@ -170,7 +190,7 @@ class Page
|
|
170
190
|
|
171
191
|
if wiki==''
|
172
192
|
count=ObjectSpace.each_object(Sunflower){|o| @sunflower=o}
|
173
|
-
raise
|
193
|
+
raise SunflowerError, 'you must pass wiki name if using multiple Sunflowers at once!' if count>1
|
174
194
|
else
|
175
195
|
ObjectSpace.each_object(Sunflower){|o| @sunflower=o if o.wikiURL==wiki}
|
176
196
|
end
|
@@ -181,46 +201,60 @@ class Page
|
|
181
201
|
return
|
182
202
|
end
|
183
203
|
|
184
|
-
|
185
|
-
|
186
|
-
|
204
|
+
preload_text
|
205
|
+
end
|
206
|
+
|
207
|
+
def preload_text
|
208
|
+
r = @sunflower.API('action=query&prop=revisions&rvprop=content&titles='+CGI.escape(@title))
|
209
|
+
r = r['query']['pages'].first
|
210
|
+
if r['missing']
|
211
|
+
@text = ''
|
212
|
+
else
|
213
|
+
@text = r['revisions'][0]['*']
|
214
|
+
end
|
215
|
+
|
216
|
+
@orig_text = @text
|
217
|
+
|
218
|
+
@preloaded_text = true
|
219
|
+
end
|
220
|
+
|
221
|
+
def preload_attrs
|
222
|
+
r = @sunflower.API('action=query&prop=info&inprop=protection&intoken=edit&titles='+CGI.escape(@title))
|
223
|
+
r = r['query']['pages'].first
|
224
|
+
r.each{|key, value|
|
187
225
|
self.instance_variable_set('@'+key, value)
|
188
226
|
}
|
189
227
|
|
190
|
-
|
191
|
-
|
192
|
-
begin
|
193
|
-
@text=r['query']['pages'][@pageid.to_s]['revisions'][0]['*']
|
194
|
-
rescue Exception
|
195
|
-
@text=''
|
196
|
-
end
|
197
|
-
@orig_text=@text
|
228
|
+
@preloaded_attrs = true
|
198
229
|
end
|
199
230
|
|
200
|
-
def
|
231
|
+
def dump_to file
|
201
232
|
if file.respond_to? :write #probably file or IO
|
202
233
|
file.write @text
|
203
234
|
else #filename?
|
204
|
-
|
205
|
-
f.write @text
|
206
|
-
f.close
|
235
|
+
File.open(file.to_s, 'w'){|f| f.write @text}
|
207
236
|
end
|
208
237
|
end
|
209
238
|
|
210
239
|
def dump
|
211
|
-
self.
|
240
|
+
self.dump_to @title.gsub(/[^a-zA-Z0-9\-]/,'_')+'.txt'
|
212
241
|
end
|
213
242
|
|
214
|
-
def save title=@title, summary
|
215
|
-
|
216
|
-
raise RuntimeError, 'Sunflower - no summary!' if (summary==nil || summary=='') && @summaryAppend==[]
|
243
|
+
def save title=@title, summary=nil
|
244
|
+
preload_attrs unless @preloaded_attrs
|
217
245
|
|
218
|
-
summary=
|
219
|
-
for i in @summaryAppend.uniq
|
220
|
-
summary+=', '+i
|
221
|
-
end
|
222
|
-
summary.sub!(/^, /,'')
|
246
|
+
summary = @sunflower.summary if !summary
|
223
247
|
|
248
|
+
raise SunflowerError, 'title invalid: '+title if title =~ INVALID_CHARS_REGEX
|
249
|
+
raise SunflowerError, 'no summary!' if (!summary or summary=='') && @summaryAppend.empty?
|
250
|
+
|
251
|
+
unless @summaryAppend.empty?
|
252
|
+
if !summary or summary==''
|
253
|
+
summary = @summaryAppend.uniq.join(', ')
|
254
|
+
else
|
255
|
+
summary = summary.sub(/,\s*\Z/, '') + ', ' + @summaryAppend.uniq.join(', ')
|
256
|
+
end
|
257
|
+
end
|
224
258
|
|
225
259
|
if @orig_text==@text && title==@title
|
226
260
|
@sunflower.log('Page '+title+' not saved - no changes.')
|
@@ -229,7 +263,7 @@ class Page
|
|
229
263
|
|
230
264
|
|
231
265
|
|
232
|
-
self.code_cleanup if
|
266
|
+
self.code_cleanup if @sunflower.always_do_code_cleanup && self.respond_to?('code_cleanup')
|
233
267
|
|
234
268
|
r=@sunflower.API("action=edit&bot=1&title=#{CGI.escape(title)}&text=#{CGI.escape(@text)}&summary=#{CGI.escape(summary)}&token=#{CGI.escape(@edittoken)}")# if @sunflower.isBot?
|
235
269
|
end
|
data/lib/sunflower/listmaker.rb
CHANGED
@@ -4,7 +4,7 @@ class Sunflower
|
|
4
4
|
def make_list type, *parameters
|
5
5
|
type=type.downcase.gsub(/[^a-z]/, '')
|
6
6
|
first=parameters[0]
|
7
|
-
firstE=CGI.escape first
|
7
|
+
firstE=CGI.escape first.to_s
|
8
8
|
|
9
9
|
case type
|
10
10
|
when 'file'
|
@@ -29,11 +29,11 @@ class Sunflower
|
|
29
29
|
|
30
30
|
when 'categorieson'
|
31
31
|
r=self.API('action=query&prop=categories&cllimit=500&titles='+firstE)
|
32
|
-
list=r['query']['pages'].first['categories'].map{|v| v['title']}
|
32
|
+
list=r['query']['pages'].first['categories'].map{|v| v['title']}
|
33
33
|
|
34
34
|
when 'category'
|
35
35
|
r=self.API('action=query&list=categorymembers&cmprop=title&cmlimit=5000&cmtitle='+firstE)
|
36
|
-
list=r['query']['categorymembers'].map{|v| v['title']}
|
36
|
+
list=r['query']['categorymembers'].map{|v| v['title']}
|
37
37
|
|
38
38
|
when 'categoryr', 'categoryrecursive'
|
39
39
|
list=[] #list of articles
|
@@ -48,43 +48,43 @@ class Sunflower
|
|
48
48
|
|
49
49
|
when 'linkson'
|
50
50
|
r=self.API('action=query&prop=links&pllimit=5000&titles='+firstE)
|
51
|
-
list=r['query']['pages'].first['links'].map{|v| v['title']}
|
51
|
+
list=r['query']['pages'].first['links'].map{|v| v['title']}
|
52
52
|
|
53
53
|
when 'transclusionson', 'templateson'
|
54
54
|
r=self.API('action=query&prop=templates&tllimit=5000&titles='+firstE)
|
55
|
-
list=r['query']['pages'].first['templates'].map{|v| v['title']}
|
55
|
+
list=r['query']['pages'].first['templates'].map{|v| v['title']}
|
56
56
|
|
57
57
|
when 'usercontribs', 'contribs'
|
58
58
|
r=self.API('action=query&list=usercontribs&uclimit=5000&ucprop=title&ucuser='+firstE)
|
59
|
-
list=r['query']['usercontribs'].map{|v| v['title']}
|
59
|
+
list=r['query']['usercontribs'].map{|v| v['title']}
|
60
60
|
|
61
61
|
when 'whatlinksto', 'whatlinkshere'
|
62
62
|
r=self.API('action=query&list=backlinks&bllimit=5000&bltitle='+firstE)
|
63
|
-
list=r['query']['backlinks'].map{|v| v['title']}
|
63
|
+
list=r['query']['backlinks'].map{|v| v['title']}
|
64
64
|
|
65
65
|
when 'whattranscludes', 'whatembeds'
|
66
66
|
r=self.API('action=query&list=embeddedin&eilimit=5000&eititle='+firstE)
|
67
|
-
list=r['query']['embeddedin'].map{|v| v['title']}
|
67
|
+
list=r['query']['embeddedin'].map{|v| v['title']}
|
68
68
|
|
69
69
|
when 'image', 'imageusage'
|
70
70
|
r=self.API('action=query&list=imageusage&iulimit=5000&iutitle='+firstE)
|
71
|
-
list=r['query']['imageusage'].map{|v| v['title']}
|
71
|
+
list=r['query']['imageusage'].map{|v| v['title']}
|
72
72
|
|
73
73
|
when 'search'
|
74
74
|
r=self.API('action=query&list=search&srwhat=text&srlimit=5000&srnamespace='+(parameters[1]=='allns' ? CGI.escape('0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|100|101|102|103') : '0')+'&srsearch='+firstE)
|
75
|
-
list=r['query']['search'].map{|v| v['title']}
|
75
|
+
list=r['query']['search'].map{|v| v['title']}
|
76
76
|
|
77
77
|
when 'searchtitles'
|
78
78
|
r=self.API('action=query&list=search&srwhat=title&srlimit=5000&srnamespace='+(parameters[1]=='allns' ? CGI.escape('0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|100|101|102|103') : '0')+'&srsearch='+firstE)
|
79
|
-
list=r['query']['search'].map{|v| v['title']}
|
79
|
+
list=r['query']['search'].map{|v| v['title']}
|
80
80
|
|
81
81
|
when 'random'
|
82
|
-
r=self.API('action=query&list=random&rnnamespace=0&rnlimit='+
|
83
|
-
list=r['query']['random'].map{|v| v['title']}
|
82
|
+
r=self.API('action=query&list=random&rnnamespace=0&rnlimit='+firstE)
|
83
|
+
list=r['query']['random'].map{|v| v['title']}
|
84
84
|
|
85
85
|
when 'external', 'linksearch'
|
86
86
|
r=self.API('action=query&euprop=title&list=exturlusage&eulimit=5000&euquery='+firstE)
|
87
|
-
list=r['query']['exturlusage'].map{|v| v['title']}
|
87
|
+
list=r['query']['exturlusage'].map{|v| v['title']}
|
88
88
|
|
89
89
|
when 'google'
|
90
90
|
limit=[parameters[1].to_i,999].min
|
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sunflower
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 13
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: "0.
|
8
|
+
- 3
|
9
|
+
version: "0.3"
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Matma Rex
|
@@ -106,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
106
|
requirements: []
|
107
107
|
|
108
108
|
rubyforge_project:
|
109
|
-
rubygems_version: 1.7.
|
109
|
+
rubygems_version: 1.7.1
|
110
110
|
signing_key:
|
111
111
|
specification_version: 3
|
112
112
|
summary: Sunflower is a lightweight library to provide access to MediaWiki API from Ruby.
|