sunflower 0.4.5 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -1
- data/example-bot.rb +7 -8
- data/lib/sunflower/commontasks.rb +3 -77
- data/lib/sunflower/core.rb +257 -78
- data/lib/sunflower/list.rb +255 -0
- data/lib/sunflower.rb +1 -1
- metadata +3 -12
- data/lib/sunflower/listmaker.rb +0 -160
- data/scripts/ZDBOT.rb +0 -62
- data/scripts/aktualizacjapilkarzy.rb +0 -339
- data/scripts/author-list.rb +0 -36
- data/scripts/changeimage.rb +0 -42
- data/scripts/fix-multiple-same-refs.rb +0 -102
- data/scripts/insight.rb +0 -133
- data/scripts/make-id2team-list.rb +0 -32
- data/scripts/wanted.rb +0 -72
- data/use-easy-bot.rb +0 -54
data/README
CHANGED
data/example-bot.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
# This is the most basic bot possible.
|
2
3
|
|
3
|
-
require 'sunflower
|
4
|
+
require 'sunflower'
|
4
5
|
|
5
|
-
s=Sunflower.new
|
6
|
-
s.
|
6
|
+
s = Sunflower.new.login
|
7
|
+
s.summary = 'test summary'
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
p
|
11
|
-
p.write p.text+"\n\ntest"
|
12
|
-
p.save
|
9
|
+
p = Sunflower::Page.new 'Test'
|
10
|
+
p.text += "\n\ntest"
|
11
|
+
p.save
|
@@ -1,80 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
# extends Page with some methods letting easily perform common tasks
|
3
2
|
|
4
|
-
class Page
|
5
|
-
# executes methods on self
|
6
|
-
# "commands" is array of arrays
|
7
|
-
# page.execute([
|
8
|
-
# [:replace, 'something', 'whatever'],
|
9
|
-
# [:append, 'some things']
|
10
|
-
# ])
|
11
|
-
# equals to
|
12
|
-
# page.replace('something', 'whatever')
|
13
|
-
# page.append('some things')
|
14
|
-
|
15
|
-
# allowed modifiers:
|
16
|
-
# r, required
|
17
|
-
# oi:module, only-if:module
|
18
|
-
# !oi:module, only-if-not:module
|
19
|
-
# s:append to summary, summary:append to summary
|
20
|
-
def execute commands
|
21
|
-
originalText = self.text.dup
|
22
|
-
|
23
|
-
commands.each do |cmd|
|
24
|
-
f=cmd.shift
|
25
|
-
if f.class==Array
|
26
|
-
methodName=f.shift
|
27
|
-
modifiers=f.map{|i|
|
28
|
-
i+=':' if !i.include? ':'
|
29
|
-
i=i.split(':',-1)
|
30
|
-
i[0]=i[0].downcase.gsub(/[^a-z!]/,'')
|
31
|
-
|
32
|
-
i[0]='r' if i[0]=='required'
|
33
|
-
i[0]='oi' if i[0]=='onlyif'
|
34
|
-
i[0]='!oi' if i[0]=='onlyifnot'
|
35
|
-
i[0]='s' if i[0]=='summary'
|
36
|
-
|
37
|
-
type=i.shift
|
38
|
-
i=i.join(':')
|
39
|
-
|
40
|
-
[type,i]
|
41
|
-
}
|
42
|
-
modifiers=Hash[*(modifiers.flatten)]
|
43
|
-
else
|
44
|
-
methodName=f
|
45
|
-
modifiers={}
|
46
|
-
end
|
47
|
-
|
48
|
-
if modifiers['oi']
|
49
|
-
if !@modulesExecd.index(modifiers['oi'].strip)
|
50
|
-
next #skip this command
|
51
|
-
end
|
52
|
-
end
|
53
|
-
if modifiers['!oi']
|
54
|
-
if @modulesExecd.index(modifiers['oi'].strip)
|
55
|
-
next #skip this command
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
oldText=self.text
|
60
|
-
self.method(methodName).call(*cmd)
|
61
|
-
newText=self.text
|
62
|
-
|
63
|
-
@modulesExecd<<methodName if oldText!=newText
|
64
|
-
|
65
|
-
if modifiers['s'] && oldText!=newText
|
66
|
-
@summaryAppend<<modifiers['s'].strip
|
67
|
-
end
|
68
|
-
|
69
|
-
if modifiers['r'] && oldText==newText
|
70
|
-
self.text = originalText
|
71
|
-
break #reset text and stop executing commands
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
|
77
|
-
|
3
|
+
class Sunflower::Page
|
78
4
|
# replaces "from" with "to" in page text
|
79
5
|
# "from" may be regex
|
80
6
|
def replace from, to, once=false
|
@@ -152,7 +78,7 @@ class Page
|
|
152
78
|
|
153
79
|
str.gsub!(/\[\[([^\|\]]+)(\||\]\])/){
|
154
80
|
name, rest = $1, $2
|
155
|
-
"[[#{self.sunflower.cleanup_title name}#{rest}"
|
81
|
+
"[[#{self.sunflower.cleanup_title name, true, true}#{rest}"
|
156
82
|
}
|
157
83
|
|
158
84
|
# headings
|
@@ -164,7 +90,7 @@ class Page
|
|
164
90
|
|
165
91
|
if wikiid = self.sunflower.siteinfo['general']['wikiid']
|
166
92
|
if self.respond_to? :"code_cleanup_#{wikiid}"
|
167
|
-
str = self.
|
93
|
+
str = self.send :"code_cleanup_#{wikiid}", str
|
168
94
|
end
|
169
95
|
end
|
170
96
|
|
data/lib/sunflower/core.rb
CHANGED
@@ -3,8 +3,6 @@ require 'rest-client'
|
|
3
3
|
require 'json'
|
4
4
|
require 'cgi'
|
5
5
|
|
6
|
-
class SunflowerError < StandardError; end
|
7
|
-
|
8
6
|
# Main class. To start working, you have to create new Sunflower:
|
9
7
|
# s = Sunflower.new('en.wikipedia.org')
|
10
8
|
# And then log in:
|
@@ -19,7 +17,7 @@ class SunflowerError < StandardError; end
|
|
19
17
|
#
|
20
18
|
# You can use multiple Sunflowers at once, to work on multiple wikis.
|
21
19
|
class Sunflower
|
22
|
-
VERSION = '0.
|
20
|
+
VERSION = '0.5'
|
23
21
|
|
24
22
|
INVALID_CHARS = %w(# < > [ ] | { })
|
25
23
|
INVALID_CHARS_REGEX = Regexp.union *INVALID_CHARS
|
@@ -28,7 +26,19 @@ class Sunflower
|
|
28
26
|
def self.path
|
29
27
|
File.join(ENV['HOME'], 'sunflower-userdata')
|
30
28
|
end
|
31
|
-
|
29
|
+
|
30
|
+
# Returns array of [url, username, password], or nil if userdata is unavailable or invalid.
|
31
|
+
def self.read_userdata
|
32
|
+
data = nil
|
33
|
+
data = File.read(Sunflower.path).split(/\r?\n/).map{|i| i.strip} rescue nil
|
34
|
+
|
35
|
+
if data && data.length==3 && data.all?{|a| a and a != ''}
|
36
|
+
return data
|
37
|
+
else
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
32
42
|
# Summary used when saving edits with this Sunflower.
|
33
43
|
attr_accessor :summary
|
34
44
|
# Whether to run #code_cleanup when calling #save.
|
@@ -38,6 +48,11 @@ class Sunflower
|
|
38
48
|
# Siteinfo, as returned by API call.
|
39
49
|
attr_accessor :siteinfo
|
40
50
|
|
51
|
+
# Whether we are logged in.
|
52
|
+
def logged_in?; @loggedin; end
|
53
|
+
# Username if logged in; nil otherwise.
|
54
|
+
attr_reader :username
|
55
|
+
|
41
56
|
# Whether this user (if logged in) has bot rights.
|
42
57
|
def is_bot?; @is_bot; end
|
43
58
|
|
@@ -49,29 +64,72 @@ class Sunflower
|
|
49
64
|
attr_writer :log
|
50
65
|
def log?; @log; end
|
51
66
|
|
52
|
-
#
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
67
|
+
# Used by #initialize to convert short identifiers such as "b:pl" to domains such as "pl.wikibooks.org".
|
68
|
+
# Identifier is of the format "type:lang" or "lang:type" (see below for valid values).
|
69
|
+
#
|
70
|
+
# Either or both parts can be ommitted; default type is "w", default lang is "en".
|
71
|
+
# (Since clashes are impossible, the colon can be ommitted in such cases as well.)
|
72
|
+
#
|
73
|
+
# lang can be any valid language code. It is ignored for type == "meta" or "commons".
|
74
|
+
#
|
75
|
+
# Valid values for type are the same as used for inter-wiki links, that is:
|
76
|
+
# [w] Wikipedia
|
77
|
+
# [b] Wikibooks
|
78
|
+
# [n] Wikinews
|
79
|
+
# [q] Wikiquote
|
80
|
+
# [s] Wikisource
|
81
|
+
# [v] Wikiversity
|
82
|
+
# [wikt] Wiktionary
|
83
|
+
# [species] Wikispecies
|
84
|
+
# [commons] Wikimedia Commons
|
85
|
+
# [meta] Wikimedia Meta-Wiki
|
86
|
+
def self.resolve_wikimedia_id id
|
87
|
+
keys = id.split(':').select{|a| a and !a.empty? }
|
88
|
+
|
89
|
+
raise ArgumentError, 'invalid format' if keys.length > 2
|
90
|
+
|
91
|
+
type_map = {
|
92
|
+
'b' => 'XX.wikibooks.org',
|
93
|
+
'q' => 'XX.wikiquote.org',
|
94
|
+
'n' => 'XX.wikinews.org',
|
95
|
+
'w' => 'XX.wikipedia.org',
|
96
|
+
'wikt' => 'XX.wiktionary.org',
|
97
|
+
'species' => 'XX.wikispecies.org',
|
98
|
+
'v' => 'XX.wikiversity.org',
|
99
|
+
's' => 'XX.wikisource.org',
|
100
|
+
'commons' => 'commons.wikimedia.org',
|
101
|
+
'meta' => 'meta.wikimedia.org',
|
102
|
+
}
|
103
|
+
|
104
|
+
types, langs = keys.partition{|a| type_map.keys.include? a }
|
105
|
+
type = types.first || 'w'
|
106
|
+
lang = langs.first || 'en'
|
60
107
|
|
108
|
+
return type_map[type].sub 'XX', lang
|
109
|
+
end
|
110
|
+
|
111
|
+
# Initialize a new Sunflower working on a wiki with given URL, for ex. "pl.wikipedia.org". url can also be a shorthand identifier such as "b:pl" - see Sunflower.resolve_wikimedia_id for details.
|
112
|
+
def initialize url=nil
|
61
113
|
if !url
|
62
|
-
|
63
|
-
|
114
|
+
userdata = Sunflower.read_userdata()
|
115
|
+
|
116
|
+
if userdata
|
117
|
+
url = userdata[0]
|
64
118
|
else
|
65
|
-
raise
|
119
|
+
raise Sunflower::Error, 'initialize: no URL supplied and no userdata found!'
|
66
120
|
end
|
67
121
|
end
|
68
122
|
|
69
|
-
@
|
70
|
-
|
123
|
+
@wikiURL = (url.include?('.') ? url : Sunflower.resolve_wikimedia_id(url))
|
124
|
+
|
125
|
+
@warnings = true
|
126
|
+
@log = false
|
71
127
|
|
72
|
-
@
|
128
|
+
@loggedin = false
|
129
|
+
@username = nil
|
130
|
+
@is_bot = false
|
73
131
|
|
74
|
-
@
|
132
|
+
@cookies = {}
|
75
133
|
|
76
134
|
siprop = 'general|namespaces|namespacealiases|specialpagealiases|magicwords|interwikimap|dbrepllag|statistics|usergroups|extensions|fileextensions|rightsinfo|languages|skins|extensiontags|functionhooks|showhooks|variables'
|
77
135
|
@siteinfo = self.API(action: 'query', meta: 'siteinfo', siprop: siprop)['query']
|
@@ -79,6 +137,10 @@ class Sunflower
|
|
79
137
|
_build_ns_map
|
80
138
|
end
|
81
139
|
|
140
|
+
def inspect
|
141
|
+
"#<Sunflower #{@loggedin ? @username : "[anon]"}@#{@wikiURL}#{@is_bot ? ' [bot]' : ''}>"
|
142
|
+
end
|
143
|
+
|
82
144
|
# Private. Massages data from siteinfo to be used for recognizing namespaces.
|
83
145
|
def _build_ns_map
|
84
146
|
@namespace_to_id = {} # all keys lowercase
|
@@ -99,6 +161,7 @@ class Sunflower
|
|
99
161
|
@namespace_to_id[ h['*'].downcase ] = h['id'].to_i
|
100
162
|
end
|
101
163
|
end
|
164
|
+
private :_build_ns_map
|
102
165
|
|
103
166
|
# Call the API. Returns a hash of JSON response. Request can be a HTTP request string or a hash.
|
104
167
|
def API request
|
@@ -116,25 +179,69 @@ class Sunflower
|
|
116
179
|
JSON.parse resp.to_str
|
117
180
|
end
|
118
181
|
|
182
|
+
# Call the API. While more results are available via the xxcontinue parameter, call it again.
|
183
|
+
#
|
184
|
+
# Assumes action=query.
|
185
|
+
#
|
186
|
+
# By default returns an array of all API responses. Attempts to merge the responses
|
187
|
+
# into a response that would have been returned if the limit was infinite. merge_on is
|
188
|
+
# the key of response['query'] to merge consecutive responses on.
|
189
|
+
#
|
190
|
+
# If limit given, will perform no more than this many API calls before returning.
|
191
|
+
# If limit is 1, behaves exactly like #API.
|
192
|
+
#
|
193
|
+
# Example: get list of all pages linking to Main Page:
|
194
|
+
#
|
195
|
+
# sunflower.API_continued "action=query&list=backlinks&bllimit=max&bltitle=Main_Page", 'backlinks', 'blcontinue'
|
196
|
+
def API_continued request, merge_on, xxcontinue, limit=nil
|
197
|
+
out = []
|
198
|
+
|
199
|
+
# gather
|
200
|
+
res = self.API(request)
|
201
|
+
out << res
|
202
|
+
while res['query-continue'] and (!limit || out.length < limit)
|
203
|
+
api_endpoint = if request.is_a? String
|
204
|
+
request + "&#{xxcontinue}=#{res["query-continue"][merge_on][xxcontinue]}"
|
205
|
+
elsif request.is_a? Hash
|
206
|
+
request.merge({xxcontinue => res["query-continue"][merge_on][xxcontinue]})
|
207
|
+
end
|
208
|
+
|
209
|
+
res = self.API(api_endpoint)
|
210
|
+
out << res
|
211
|
+
end
|
212
|
+
|
213
|
+
# merge
|
214
|
+
merged = out[0]
|
215
|
+
meth = (merged['query'][merge_on].is_a?(Hash) ? :merge! : :concat)
|
216
|
+
|
217
|
+
out.drop(1).each do |cur|
|
218
|
+
merged['query'][merge_on].send meth, cur['query'][merge_on]
|
219
|
+
end
|
220
|
+
|
221
|
+
return merged
|
222
|
+
end
|
223
|
+
|
119
224
|
# Log in using given info.
|
120
225
|
def login user='', password=''
|
121
226
|
if user=='' || password==''
|
122
|
-
|
123
|
-
|
124
|
-
|
227
|
+
userdata = Sunflower.read_userdata()
|
228
|
+
|
229
|
+
if userdata
|
230
|
+
user = userdata[1] if user==''
|
231
|
+
password = userdata[2] if password==''
|
125
232
|
else
|
126
|
-
raise
|
233
|
+
raise Sunflower::Error, 'login: no user/pass supplied and no userdata found!'
|
127
234
|
end
|
128
235
|
end
|
129
236
|
|
130
|
-
raise
|
237
|
+
raise Sunflower::Error, 'bad username!' if user =~ INVALID_CHARS_REGEX
|
131
238
|
|
132
239
|
|
133
240
|
# 1. get the login token
|
134
241
|
response = RestClient.post(
|
135
242
|
'http://'+@wikiURL+'/w/api.php?'+"action=login&lgname=#{CGI.escape user}&lgpassword=#{CGI.escape password}"+'&format=json',
|
136
243
|
nil,
|
137
|
-
{:user_agent =>
|
244
|
+
{:user_agent => "Sunflower #{VERSION} alpha"}
|
138
245
|
)
|
139
246
|
|
140
247
|
@cookies = response.cookies
|
@@ -146,7 +253,7 @@ class Sunflower
|
|
146
253
|
response = RestClient.post(
|
147
254
|
'http://'+@wikiURL+'/w/api.php?'+"action=login&lgname=#{CGI.escape user}&lgpassword=#{CGI.escape password}&lgtoken=#{token}"+'&format=json',
|
148
255
|
nil,
|
149
|
-
{:user_agent =>
|
256
|
+
{:user_agent => "Sunflower #{VERSION} alpha", :cookies => @cookies}
|
150
257
|
)
|
151
258
|
|
152
259
|
json = JSON.parse response.to_str
|
@@ -158,7 +265,7 @@ class Sunflower
|
|
158
265
|
})
|
159
266
|
|
160
267
|
|
161
|
-
raise
|
268
|
+
raise Sunflower::Error, 'unable to log in (no cookies received)!' if !@cookies
|
162
269
|
|
163
270
|
|
164
271
|
# 3. confirm you did log in by checking the watchlist.
|
@@ -166,9 +273,12 @@ class Sunflower
|
|
166
273
|
r=self.API('action=query&list=watchlistraw')
|
167
274
|
if r['error'] && r['error']['code']=='wrnotloggedin'
|
168
275
|
@loggedin=false
|
169
|
-
raise
|
276
|
+
raise Sunflower::Error, 'unable to log in!'
|
170
277
|
end
|
171
278
|
|
279
|
+
# set the username
|
280
|
+
@username = user
|
281
|
+
|
172
282
|
# 4. check bot rights
|
173
283
|
r=self.API('action=query&list=allusers&aulimit=1&augroup=bot&aufrom='+(CGI.escape user))
|
174
284
|
unless r['query']['allusers'][0]['name']==user
|
@@ -187,7 +297,9 @@ class Sunflower
|
|
187
297
|
end
|
188
298
|
|
189
299
|
# Cleans up underscores, percent-encoding and title-casing in title (with optional anchor).
|
190
|
-
def cleanup_title title
|
300
|
+
def cleanup_title title, preserve_case=false, preserve_colon=false
|
301
|
+
return '' if title.strip == ''
|
302
|
+
|
191
303
|
name, anchor = title.split '#', 2
|
192
304
|
|
193
305
|
# CGI.unescape also changes pluses to spaces; code borrowed from there
|
@@ -197,6 +309,10 @@ class Sunflower
|
|
197
309
|
name = unescape.call(name).gsub(/[ _]+/, ' ').strip
|
198
310
|
anchor = unescape.call(anchor.gsub(/\.([0-9a-fA-F]{2})/, '%\1')).gsub(/[ _]+/, ' ').strip if anchor
|
199
311
|
|
312
|
+
leading_colon = name[0]==':'
|
313
|
+
name = name.sub(/^:\s*/, '') if leading_colon
|
314
|
+
leading_colon = false if !preserve_colon
|
315
|
+
|
200
316
|
# FIXME unicode? downcase, upcase
|
201
317
|
|
202
318
|
if name.include? ':'
|
@@ -206,9 +322,9 @@ class Sunflower
|
|
206
322
|
end
|
207
323
|
end
|
208
324
|
|
209
|
-
name[0] = name[0].upcase if @siteinfo["general"]["case"] == "first-letter"
|
325
|
+
name[0] = name[0].upcase if !preserve_case and @siteinfo["general"]["case"] == "first-letter"
|
210
326
|
|
211
|
-
return [ns ? "#{ns}:" : nil, name, anchor ? "##{anchor}" : nil].join ''
|
327
|
+
return [leading_colon ? ':' : nil, ns ? "#{ns}:" : nil, name, anchor ? "##{anchor}" : nil].join ''
|
212
328
|
end
|
213
329
|
|
214
330
|
# Returns the localized namespace name for ns, which may be namespace number, canonical name, or any namespace alias.
|
@@ -245,76 +361,103 @@ class Sunflower
|
|
245
361
|
end
|
246
362
|
|
247
363
|
# Class representing a single Wiki page. To load specified page, use #new. To save it back, use #save.
|
248
|
-
class Page
|
364
|
+
class Sunflower::Page
|
249
365
|
# Characters which MediaWiki does not permit in page title.
|
250
366
|
INVALID_CHARS = %w(# < > [ ] | { })
|
251
367
|
# Regex matching characters which MediaWiki does not permit in page title.
|
252
368
|
INVALID_CHARS_REGEX = Regexp.union *INVALID_CHARS
|
253
369
|
|
254
|
-
# The
|
370
|
+
# The Sunflower instance this page belongs to.
|
371
|
+
attr_reader :sunflower
|
372
|
+
|
373
|
+
# The current text of the page. Lazy-loaded.
|
255
374
|
attr_accessor :text
|
256
|
-
# The text of the page, as of when it was loaded.
|
375
|
+
# The text of the page, as of when it was loaded. Lazy-loaded.
|
257
376
|
attr_reader :orig_text
|
258
377
|
|
259
|
-
#
|
260
|
-
|
378
|
+
# Page title, as passed to #initialize and cleaned by Sunflower#cleanup_title.
|
379
|
+
# Real page title as canonicalized by MediaWiki software can be accessed via #real_title
|
380
|
+
# (but it should always be the same).
|
381
|
+
attr_reader :title
|
261
382
|
|
262
|
-
#
|
263
|
-
|
264
|
-
#
|
265
|
-
attr_reader :
|
266
|
-
|
383
|
+
# Value of given attribute, as returned by API call prop=info for this page. Lazy-loaded.
|
384
|
+
attr_reader :pageid, :ns, :touched, :lastrevid, :counter, :length, :starttimestamp, :edittoken, :protection
|
385
|
+
# Value of `title` attribute, as returned by API call prop=info for this page. Lazy-loaded. See #title.
|
386
|
+
attr_reader :real_title
|
387
|
+
|
388
|
+
# Whether this datum is already loaded. Can be set to true to suppress loading
|
389
|
+
# (used e.g. by Sunflower::List#pages_preloaded)
|
390
|
+
attr_accessor :preloaded_text, :preloaded_attrs
|
267
391
|
|
268
392
|
# calling any of these accessors will fetch the data.
|
269
|
-
|
393
|
+
# getters...
|
394
|
+
[:pageid, :ns, :real_title, :touched, :lastrevid, :counter, :length, :starttimestamp, :edittoken, :protection].each do |meth|
|
395
|
+
remove_method meth # to avoid warnings when running with ruby -w
|
270
396
|
define_method meth do
|
271
397
|
preload_attrs unless @preloaded_attrs
|
272
398
|
instance_variable_get "@#{meth}"
|
273
399
|
end
|
274
400
|
end
|
401
|
+
[:text, :orig_text].each do |meth|
|
402
|
+
remove_method meth # to avoid warnings when running with ruby -w
|
403
|
+
define_method meth do
|
404
|
+
preload_text unless @preloaded_text
|
405
|
+
instance_variable_get "@#{meth}"
|
406
|
+
end
|
407
|
+
end
|
408
|
+
# setters...
|
409
|
+
[:text=].each do |meth|
|
410
|
+
remove_method meth # to avoid warnings when running with ruby -w
|
411
|
+
define_method meth do |a|
|
412
|
+
preload_text unless @preloaded_text
|
413
|
+
instance_variable_set "@#{meth.to_s.chop}", a
|
414
|
+
end
|
415
|
+
end
|
275
416
|
|
276
|
-
# Load the specified page.
|
277
|
-
#
|
278
|
-
#
|
279
|
-
|
280
|
-
|
417
|
+
# Load the specified page.
|
418
|
+
# Only the text will be immediately loaded - attributes and edit token will be loaded when needed, or when you call #preload_attrs.
|
419
|
+
#
|
420
|
+
# If you are using multiple Sunflowers, you have to specify which one this page belongs to using the second argument of function.
|
421
|
+
# You can pass either a Sunflower object, wiki URL, or a shorthand id as specified in Sunflower.resolve_wikimedia_id.
|
422
|
+
def initialize title='', url=''
|
423
|
+
raise Sunflower::Error, 'title invalid: '+title if title =~ INVALID_CHARS_REGEX
|
281
424
|
|
282
425
|
@modulesExecd=[] #used by sunflower-commontasks.rb
|
283
426
|
@summaryAppend=[] #used by sunflower-commontasks.rb
|
284
427
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
raise
|
428
|
+
case url
|
429
|
+
when Sunflower
|
430
|
+
@sunflower = url
|
431
|
+
when '', nil
|
432
|
+
count = ObjectSpace.each_object(Sunflower){|o| @sunflower=o}
|
433
|
+
raise Sunflower::Error, 'no Sunflowers present' if count==0
|
434
|
+
raise Sunflower::Error, 'you must pass wiki name if using multiple Sunflowers at once' if count>1
|
291
435
|
else
|
292
|
-
|
436
|
+
url = (url.include?('.') ? url : Sunflower.resolve_wikimedia_id(url))
|
437
|
+
ObjectSpace.each_object(Sunflower){|o| @sunflower=o if o.wikiURL==url}
|
438
|
+
raise Sunflower::Error, "no Sunflower for #{url}" if !@sunflower
|
293
439
|
end
|
294
440
|
|
295
|
-
raise SunflowerError, "no Sunflower for #{wiki}" if !@sunflower
|
296
|
-
|
297
441
|
@title = @sunflower.cleanup_title title
|
298
442
|
|
299
|
-
|
300
|
-
|
301
|
-
@orig_text=''
|
302
|
-
return
|
303
|
-
end
|
304
|
-
|
305
|
-
preload_text
|
443
|
+
@preloaded_text = false
|
444
|
+
@preloaded_attrs = false
|
306
445
|
end
|
307
446
|
|
308
447
|
# Load the text of this page. Semi-private.
|
309
448
|
def preload_text
|
310
|
-
|
311
|
-
r = r['query']['pages'].first
|
312
|
-
if r['missing']
|
449
|
+
if title == ''
|
313
450
|
@text = ''
|
314
|
-
elsif r['invalid']
|
315
|
-
raise SunflowerError, 'title invalid: '+@title
|
316
451
|
else
|
317
|
-
|
452
|
+
r = @sunflower.API('action=query&prop=revisions&rvprop=content&titles='+CGI.escape(@title))
|
453
|
+
r = r['query']['pages'].values.first
|
454
|
+
if r['missing']
|
455
|
+
@text = ''
|
456
|
+
elsif r['invalid']
|
457
|
+
raise Sunflower::Error, 'title invalid: '+@title
|
458
|
+
else
|
459
|
+
@text = r['revisions'][0]['*']
|
460
|
+
end
|
318
461
|
end
|
319
462
|
|
320
463
|
@orig_text = @text.dup
|
@@ -325,8 +468,9 @@ class Page
|
|
325
468
|
# Load the metadata associated with this page. Semi-private.
|
326
469
|
def preload_attrs
|
327
470
|
r = @sunflower.API('action=query&prop=info&inprop=protection&intoken=edit&titles='+CGI.escape(@title))
|
328
|
-
r = r['query']['pages'].first
|
471
|
+
r = r['query']['pages'].values.first
|
329
472
|
r.each{|key, value|
|
473
|
+
key = 'real_title' if key == 'title'
|
330
474
|
self.instance_variable_set('@'+key, value)
|
331
475
|
}
|
332
476
|
|
@@ -359,8 +503,8 @@ class Page
|
|
359
503
|
|
360
504
|
summary = @sunflower.summary if !summary
|
361
505
|
|
362
|
-
raise
|
363
|
-
raise
|
506
|
+
raise Sunflower::Error, 'title invalid: '+title if title =~ INVALID_CHARS_REGEX
|
507
|
+
raise Sunflower::Error, 'no summary!' if (!summary or summary=='') && @summaryAppend.empty?
|
364
508
|
|
365
509
|
unless @summaryAppend.empty?
|
366
510
|
if !summary or summary==''
|
@@ -383,18 +527,53 @@ class Page
|
|
383
527
|
alias :put :save
|
384
528
|
|
385
529
|
def self.get title, wiki=''
|
386
|
-
|
530
|
+
self.new(title, wiki)
|
387
531
|
end
|
388
532
|
|
389
533
|
def self.load title, wiki=''
|
390
|
-
|
534
|
+
self.new(title, wiki)
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
# For backwards compatibility. Deprecated.
|
539
|
+
class Page # :nodoc:
|
540
|
+
class << self
|
541
|
+
def new *a
|
542
|
+
warn "warning: toplevel Page class has been renamed to Sunflower::Page, this alias will be removed in v0.6"
|
543
|
+
Sunflower::Page.new *a
|
544
|
+
end
|
545
|
+
alias get new
|
546
|
+
alias load new
|
391
547
|
end
|
392
548
|
end
|
393
549
|
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
550
|
+
# For backwards compatibility. Deprecated.
|
551
|
+
#
|
552
|
+
# We use inheritance shenanigans to keep the usage in "begin ... rescue ... end" working.
|
553
|
+
class SunflowerError < StandardError # :nodoc:
|
554
|
+
%w[== backtrace exception inspect message set_backtrace to_s].each do |meth|
|
555
|
+
define_method meth.to_sym do |*a, &b|
|
556
|
+
if self.class == Sunflower::Error and !@warned
|
557
|
+
warn "warning: toplevel SunflowerError class has been renamed to Sunflower::Error, this alias will be removed in v0.6"
|
558
|
+
@warned = true
|
559
|
+
end
|
560
|
+
|
561
|
+
super *a, &b
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
class << self
|
566
|
+
def new *a
|
567
|
+
warn "warning: toplevel SunflowerError class has been renamed to Sunflower::Error, this alias will be removed in v0.6" unless self == Sunflower::Error
|
568
|
+
super
|
569
|
+
end
|
570
|
+
def exception *a
|
571
|
+
warn "warning: toplevel SunflowerError class has been renamed to Sunflower::Error, this alias will be removed in v0.6" unless self == Sunflower::Error
|
572
|
+
super
|
573
|
+
end
|
398
574
|
end
|
399
575
|
end
|
400
576
|
|
577
|
+
|
578
|
+
# Represents an error raised by Sunflower.
|
579
|
+
class Sunflower::Error < SunflowerError; end
|