sunflower 0.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,4 @@
1
+ Sunflower is licensed under CC-BY-SA 3.0.
2
+
3
+ Author: matma.rex@gmail.com
4
+
data/README ADDED
@@ -0,0 +1,21 @@
1
+ Version: 0.2 alpha
2
+
3
+ >>> English:
4
+
5
+
6
+
7
+ >>> Polski:
8
+
9
+ ==============================
10
+ Nie edytuj żadnych plików .rb Notatnikiem - na początku plików zapisanych w Unikodzie dodaje on tzw. BOM, który powoduje wywalanie się Rubiego. Edytuj jest WordPadem lub, najlepiej, jakimś edytorem z kolorowaniem składni (np. Notepad++).
11
+ ==============================
12
+ Aby bot zawsze automatycznie się logował bez konieczności podawania hasła w pliku .rb, utwórz plik o nazwie "userdata" (bez rozszerzenia) i w treści wpisz (oddzielając enterem) kolejno URL do wiki, login i hasło. Wtedy Sunflower.new() i s.login() można wywoływać bez parametrów - z pustymi nawiasami.
13
+ ==============================
14
+
15
+ sunflower-core.rb to podstawowy plik z ramą bota. sunflower-commontasks.rb zawiera parę funkcji, które ułatwiający wykonywanie różnych zadań.
16
+
17
+ example-bot.rb to przykład bota; jest to niemal najkrótszy kod, który cokolwiek może zrobić, ten pobiera stronę, dodaje do niej "test" i zapisuje ją.
18
+
19
+ use-easy-bot.rb to skrypt pozwalający w zamierzeniu na uzycie bota laikom. Pobiera listę stron z pliku (należy ją stworzyć np. AWB i zapisać jako "Plaintext list" - tytuły stron oddzielone enterami), po czym w każdej wykonuje określone zmiany i zapisuje. Dokładniejszy opis jest w samym pliku, pamiętaj też, żeby zmienić oznaczone fragmenty. Komentarze zaczynają się od znaku "#".
20
+
21
+ W /scripts jest parę moich skryptów, niektóre moga się przydać, niektóre nie albo w ogóle nie działają.
@@ -0,0 +1,68 @@
1
+ require 'sunflower'
2
+
3
+
4
+ path = Sunflower.path
5
+
6
+
7
+
8
+ puts "Welcome to Sunflower's setup script."
9
+ puts ""
10
+
11
+ puts "If you set your home wiki and userdata, you will not have to enter it in every script."
12
+ puts "Your userdata will be saved (IN PLAINTEXT!) in this file:"
13
+ puts " #{path}"
14
+
15
+ puts ""
16
+
17
+ print "Enter your home wiki (for ex. en.wikipedia.org): "
18
+ home=gets.strip
19
+
20
+ print "Enter your bot's nick on the home wiki: "
21
+ nick=gets.strip
22
+
23
+ print "Enter your bot's password on home wiki (WILL BE SHOWN IN PLAINTEXT): "
24
+ pass=gets.strip
25
+
26
+ puts ""
27
+
28
+ worked = true
29
+ puts "Trying to connect with the data provided..."
30
+ begin
31
+ s=Sunflower.new home
32
+ s.login nick, pass
33
+ rescue
34
+ worked = false
35
+ error = $!.message
36
+ end
37
+
38
+ if worked
39
+ puts "It seems to work!"
40
+ puts "WARNING! USER DOES NOT HAVE BOT RIGHTS!" if !s.isBot?
41
+ else
42
+ puts "Whoops, it didn't work. The error message is:"
43
+ puts error
44
+ end
45
+
46
+ save = worked
47
+
48
+ if !worked
49
+ begin
50
+ print "Do you want to save the data anyway? [yn] "
51
+ ans = gets.strip
52
+ end until ans=~/[yn]/i
53
+
54
+ save = (ans.downcase=='y')
55
+ end
56
+
57
+ if save
58
+ f=File.open(path, "w")
59
+ f.write [home, nick, pass].join "\n"
60
+ f.close
61
+
62
+ puts "User data has been saved. Remember that your password is saved in plaintext!"
63
+ puts ""
64
+
65
+ puts "If you ever want to erase your login data, simply delete the file."
66
+ else
67
+ puts "User data has not been saved. You can run this setup again anytime."
68
+ end
data/example-bot.rb ADDED
@@ -0,0 +1,12 @@
1
+ # This is the most basic bot possible.
2
+
3
+ require 'sunflower-commontasks.rb'
4
+
5
+ s=Sunflower.new
6
+ s.login
7
+
8
+ $summary='Sunflower: test'
9
+
10
+ p=Page.get('Test')
11
+ p.write p.text+"\n\ntest"
12
+ p.save
@@ -0,0 +1,251 @@
1
+ # coding: utf-8
2
+ # extends Page with some methods letting easily perform common tasks
3
+
4
+ class Page
5
+ def execute commands
6
+ # executes methods on self
7
+ # "commands" is array of arrays
8
+ # page.execute([
9
+ # [:replace, 'something', 'whatever'],
10
+ # [:append, 'some things']
11
+ # ])
12
+ # equals to
13
+ # page.replace('something', 'whatever')
14
+ # page.append('some things')
15
+
16
+ # allowed modifiers:
17
+ # r, required
18
+ # oi:module, only-if:module
19
+ # !oi:module, only-if-not:module
20
+ # s:append to summary, summary:append to summary
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
+
78
+ def replace from, to, once=false
79
+ # replaces "from" with "to" in page text
80
+ # "from" may be regex
81
+ self.text = self.text.send( (once ? 'sub' : 'gsub'), from, to )
82
+ end
83
+ def gsub from, to
84
+ self.replace from, to
85
+ end
86
+ def sub from, to
87
+ self.replace from, to, true
88
+ end
89
+
90
+ def append txt, newlines=2
91
+ # appends newlines and text
92
+ # by default - 2 newlines
93
+ self.text = self.text.rstrip + ("\n"*newlines) + txt
94
+ end
95
+
96
+ def prepend txt, newlines=2
97
+ # prepends text and newlines
98
+ # by default - 2 newlines
99
+ self.text = txt + ("\n"*newlines) + self.text.lstrip
100
+ end
101
+
102
+ def code_cleanup
103
+ # simple, safe code cleanup
104
+ # use $alwaysDoCodeCleanup=true to do it automatically just before saving page
105
+ # based on Nux's cleaner: http://pl.wikipedia.org/wiki/Wikipedysta:Nux/wp_sk.js
106
+ str=self.text.gsub(/\r\n/,"\n")
107
+
108
+ str.gsub!(/\{\{\s*([^|{}]+ |uni|)stub2?(\|[^{}]+)?\}\}/i){
109
+ if $1=='sekcja '
110
+ '{{sekcja stub}}'
111
+ else
112
+ '{{stub}}'
113
+ end
114
+ }
115
+ str.gsub!(/\{\{\{(?:poprzednik|następca|pop|nast|lata|info|lang)\|(.+?)\}\}\}/i,'\1')
116
+ str.gsub!(/(={1,5})\s*Przypisy\s*\1\s*<references\s?\/>/){
117
+ if $1=='=' || $1=='=='
118
+ '{{Przypisy}}'
119
+ else
120
+ '{{Przypisy|stopień= '+$1+'}}'
121
+ end
122
+ }
123
+
124
+ # sklejanie skrótów linkowych
125
+ str.gsub!(/m\.? ?\[\[n\.? ?p\.? ?m\.?\]\]/, 'm [[n.p.m.]]');
126
+
127
+ # korekty dat - niepotrzebny przecinek
128
+ str.gsub!(/(\[\[[0-9]+ (stycznia|lutego|marca|kwietnia|maja|czerwca|lipca|sierpnia|września|października|listopada|grudnia)\]\]), (\[\[[0-9]{4}\]\])/i, '\1 \3');
129
+
130
+ # linkowanie do wieków
131
+ str.gsub!(/\[\[([XVI]{1,5}) [wW]\.?\]\]/, '[[\1 wiek|\1 w.]]');
132
+ str.gsub!(/\[\[([XVI]{1,5}) [wW]\.?\|/, '[[\1 wiek|');
133
+ str.gsub!(/\[\[(III|II|IV|VIII|VII|VI|IX|XIII|XII|XI|XIV|XV|XVIII|XVII|XVI|XIX|XXI|XX)\]\]/, '[[\1 wiek|\1]]');
134
+ str.gsub!(/\[\[(III|II|IV|VIII|VII|VI|IX|XIII|XII|XI|XIV|XV|XVIII|XVII|XVI|XIX|XXI|XX)\|/, '[[\1 wiek|');
135
+
136
+ # rozwijanie typowych linków
137
+ str.gsub!(/\[\[ang\.\]\]/, '[[język angielski|ang.]]');
138
+ str.gsub!(/\[\[cz\.\]\]/, '[[język czeski|cz.]]');
139
+ str.gsub!(/\[\[fr\.\]\]/, '[[język francuski|fr.]]');
140
+ str.gsub!(/\[\[łac\.\]\]/, '[[łacina|łac.]]');
141
+ str.gsub!(/\[\[niem\.\]\]/, '[[język niemiecki|niem.]]');
142
+ str.gsub!(/\[\[pol\.\]\]/, '[[język polski|pol.]]');
143
+ str.gsub!(/\[\[pl\.\]\]/, '[[język polski|pol.]]');
144
+ str.gsub!(/\[\[ros\.\]\]/, '[[język rosyjski|ros.]]');
145
+ str.gsub!(/\[\[(((G|g)iga|(M|m)ega|(K|k)ilo)herc|[GMk]Hz)\|/, '[[herc|');
146
+
147
+ # unifikacja nagłówkowa
148
+ str.gsub!(/[ \n\t]*\n'''? *(Zobacz|Patrz) (też|także):* *'''?[ \n\t]*/i, "\n\n== Zobacz też ==\n");
149
+ str.gsub!(/[ \n\t]*\n(=+) *(Zobacz|Patrz) (też|także):* *=+[ \n\t]*/i, "\n\n\\1 Zobacz też \\1\n");
150
+ str.gsub!(/[ \n\t]*\n'''? *((Zewnętrzn[ey] )?(Linki?|Łącza|Stron[ay]|Zobacz w (internecie|sieci))( zewn[eę]trzn[aey])?):* *'''?[ \n\t]*/i, "\n\n== Linki zewnętrzne ==\n");
151
+ str.gsub!(/[ \n\t]*\n(=+) *((Zewnętrzn[ey] )?(Linki?|Łącza|Stron[ay]|Zobacz w (internecie|sieci))( zewn[eę]trzn[aey])?):* *=+[ \n\t]*/i, "\n\n\\1 Linki zewnętrzne \\1\n");
152
+
153
+ # nagłówki
154
+ str.gsub!(/(^|\n)(=+) *([^=\n]*[^ :=\n])[ :]*=/, '\1\2 \3 ='); # =a= > = a =, =a:= > = a =
155
+ str.gsub!(/(^|\n)(=+[^=\n]+=+)[\n]{2,}/, "\\1\\2\n"); # jeden \n
156
+
157
+ # listy ze spacjami
158
+ str.gsub!(/(\n[#*:;]+)([^ \t\n#*:;{])/, '\1 \2');
159
+
160
+ # poprawa nazw przestrzeni i drobne okoliczne
161
+ str.gsub!(/\[\[(:?) *(image|grafika|file|plik) *: *([^ ])/i){'[['+$1+'Plik:'+$3.upcase}
162
+ str.gsub!(/\[\[(:?) *(category|kategoria) *: *([^ ])/i){'[['+$1+'Kategoria:'+$3.upcase}
163
+ str.gsub!(/\[\[ *(:?) *(template|szablon) *: *([^ ])/i){'[['+'Szablon:'+$3.upcase}
164
+ str.gsub!(/\[\[ *(:?) *(special|specjalna) *: *([^ ])/i){'[['+'Specjalna:'+$3.upcase}
165
+
166
+ 3.times { str.gsub!('{{stub}}{{stub}}', '{{stub}}') }
167
+
168
+ self.text = str
169
+ end
170
+
171
+ def friendly_infobox
172
+ # cleans up infoboxes
173
+ # might make mistakes! use at your own risk!
174
+ def makeFriendly(nazwa,zaw)
175
+ zaw.gsub!(/<!--.+?-->/,'')
176
+ nazwa=nazwa.gsub('_',' ').strip
177
+
178
+ #escapowanie parametrów
179
+ zaw.gsub!(/<<<(#+)>>>/,"<<<#\\1>>>")
180
+ #wewnętrzne szablony
181
+ while zaw=~/\{\{[^}]+\|[^}]+\}\}/
182
+ zaw.gsub!($&,$&.gsub(/\|/,'<<<#>>>'))
183
+ end
184
+ #wewnętrzne linki
185
+ while zaw=~/\[\[[^\]]+\|[^\]]+\]\]/
186
+ zaw.gsub!($&,$&.gsub(/\|/,'<<<#>>>'))
187
+ end
188
+
189
+ zaw.sub!(/\A\s*\|\s*/,'') #usunięcie pierwszego pipe'a
190
+ lines=zaw.split('|')
191
+
192
+ # te tablice przechowują odpowiednio nazwy i wartości kolejnych parametrów
193
+ names=[]
194
+ values=[]
195
+
196
+ for line in lines
197
+ line.gsub!(/<<<#>>>/,'|')
198
+ line.gsub!(/<<<#(#+)>>>/,"<<<\\1>>>") #odescapowanie
199
+
200
+ line=~/\A\s*(.+?)\s*=\s*([\s\S]*?)\s*\Z/
201
+ if $&==nil
202
+ next
203
+ end
204
+ name=$1.strip
205
+ value=$2.strip
206
+
207
+ names<<name
208
+ values<<value
209
+ end
210
+
211
+ zaw=''
212
+ names.each_index{|i|
213
+ zaw+=' | '+names[i]+' = '+values[i]+"\n"
214
+ }
215
+
216
+ # grupowane koordynaty
217
+ zaw.gsub!(/\s*\| minut/, ' | minut')
218
+ zaw.gsub!(/\s*\| sekund/, ' | sekund')
219
+
220
+ return '{{'+nazwa[0,1].upcase+nazwa[1,999]+"\n"+zaw+'}}'+"\n"
221
+ end
222
+
223
+ nstr=''
224
+ while str!=''
225
+ str=~/(\s*)\{\{([^|}]+[ _]infobo[^|}]+|[wW]ładca)((?:[^{}]|[^{}][{}][^{}]|\{\{(?:[^{}]|[^{}][{}][^{}]|\{\{[^{}]+\}\})+\}\})+)\}\}(?:\s*)/
226
+
227
+ spaces=($1!='' ? "\n" : '')
228
+ before=($`==nil ? '' : $`)
229
+ name=$2
230
+ inner=$3
231
+ match=$&
232
+ if match!=nil
233
+ result=makeFriendly(name,inner)
234
+ nstr+=before+spaces+result
235
+ else
236
+ nstr+=str
237
+ break
238
+ end
239
+
240
+ str=str.sub(before+match,'')
241
+ end
242
+
243
+ self.text = nstr
244
+ end
245
+
246
+ def change_category from, to
247
+ from=from.sub(/\A\s*([cC]ategory|[kK]ategoria):/, '').strip
248
+ to=to.sub(/\A\s*([cC]ategory|[kK]ategoria):/, '').strip
249
+ self.text = self.text.gsub!(/\[\[ *(?:[cC]ategory|[kK]ategoria) *: *#{Regexp.escape from} *(\|[^\]]+ *|)\]\]/){'[[Kategoria:'+to+$1.rstrip+']]'}
250
+ end
251
+ end
@@ -0,0 +1,253 @@
1
+ # coding: utf-8
2
+ require 'rest-client'
3
+ require 'json'
4
+ require 'cgi'
5
+
6
+ # Main class. To start working, you have to create new Sunflower:
7
+ # s = Sunflower.new('en.wikipedia.org')
8
+ # And then log in:
9
+ # s.login('Username','password')
10
+ #
11
+ # If you have ran setup, you can just use
12
+ # s = Sunflower.new.login
13
+ #
14
+ # Then you can request data from API using #API method.
15
+ # To log data to file, use #log method (works like puts, append new line if needed) of #log2 (like print).
16
+ # You can use multiple Sunflowers at once, to work on multiple wikis.
17
+ class Sunflower
18
+ # Path to user data file.
19
+ def self.path
20
+ File.join(ENV['HOME'], 'sunflower-userdata')
21
+ end
22
+
23
+ attr_accessor :cookie, :headers, :wikiURL, :warnings, :log
24
+
25
+ # Initialize a new Sunflower working on a wiki with given URL, for ex. "pl.wikipedia.org".
26
+ def initialize url=''
27
+ begin
28
+ r=File.read(Sunflower.path)
29
+ @userdata=r.split(/\r?\n/).map{|i| i.strip}
30
+ rescue
31
+ @userdata=[]
32
+ end
33
+
34
+ if url==''
35
+ if !@userdata.empty?
36
+ url=@userdata[0]
37
+ else
38
+ raise RuntimeError, 'Sunflower - initialize: no URL supplied and no userdata found!'
39
+ end
40
+ end
41
+
42
+ @warnings=true
43
+ @log=true
44
+
45
+ @wikiURL=url
46
+ @logData=''
47
+
48
+ @loggedin=false
49
+ end
50
+
51
+ # Call the API. Returns a hash of JSON response.
52
+ def API request
53
+ #$stderr.puts 'Warning: Sunflower: API request before logging in! ('+request+')' unless @loggedin || !@warnings
54
+ self.log 'http://'+@wikiURL+'/w/api.php?'+request+'&format=jsonfm'
55
+ resp = RestClient.get(
56
+ 'http://'+@wikiURL+'/w/api.php?'+request+'&format=json',
57
+ {:user_agent => 'Sunflower alpha', :cookies => @cookies}
58
+ )
59
+ JSON.parse resp.to_str
60
+ end
61
+
62
+ # Log in using given info.
63
+ def login user='', password=''
64
+ if user=='' || password==''
65
+ if !@userdata.empty?
66
+ user=@userdata[1] if user==''
67
+ password=@userdata[2] if password==''
68
+ else
69
+ raise RuntimeError, 'Sunflower - login: no user/pass supplied and no userdata found!'
70
+ end
71
+ end
72
+
73
+ if user.index(/[#<>\[\]\|\{\}]/)
74
+ raise RuntimeError, 'Sunflower - bad username!'
75
+ end
76
+
77
+
78
+ # 1. get the login token
79
+ response = RestClient.post(
80
+ 'http://'+@wikiURL+'/w/api.php?'+"action=login&lgname=#{user}&lgpassword=#{password}"+'&format=json',
81
+ nil,
82
+ {:user_agent => 'Sunflower alpha'}
83
+ )
84
+
85
+ @cookies = response.cookies
86
+ json = JSON.parse response.to_str
87
+ token, prefix = json['login']['token'], json['login']['cookieprefix']
88
+
89
+
90
+ # 2. actually log in
91
+ response = RestClient.post(
92
+ 'http://'+@wikiURL+'/w/api.php?'+"action=login&lgname=#{user}&lgpassword=#{password}&lgtoken=#{token}"+'&format=json',
93
+ nil,
94
+ {:user_agent => 'Sunflower alpha', :cookies => @cookies}
95
+ )
96
+
97
+ json = JSON.parse response.to_str
98
+
99
+ @cookies = @cookies.merge(response.cookies).merge({
100
+ "#{prefix}UserName" => json['login']['lgusername'].to_s,
101
+ "#{prefix}UserID" => json['login']['lguserid'].to_s,
102
+ "#{prefix}Token" => json['login']['lgtoken'].to_s
103
+ })
104
+
105
+
106
+ raise RuntimeError, 'Sunflower - unable to log in (no cookies received)!' if !@cookies
107
+
108
+
109
+ @loggedin=true
110
+ r=self.API('action=query&list=watchlistraw')
111
+ if r['error'] && r['error']['code']=='wrnotloggedin'
112
+ @loggedin=false
113
+ raise RuntimeError, 'Sunflower - unable to log in!'
114
+ end
115
+
116
+ r=self.API('action=query&list=allusers&aulimit=1&augroup=bot&aufrom='+user)
117
+ unless r['query']['allusers'][0]['name']==user
118
+ $stderr.puts 'Warning: Sunflower - this user does not have bot rights!' if @warnings
119
+ @haveBotRights=false
120
+ else
121
+ @haveBotRights=true
122
+ end
123
+
124
+ return self
125
+ end
126
+
127
+ def log2(t)
128
+ @logData+=t
129
+ if @log
130
+ f=File.open('log.txt','a')
131
+ f.write t
132
+ f.close
133
+ end
134
+ end
135
+
136
+ def log(t)
137
+ self.log2(t.to_s.chomp+"\n")
138
+ end
139
+
140
+ def isBot?
141
+ @haveBotRights
142
+ end
143
+ end
144
+
145
+ # Class representng single Wiki page. To load specified page, use #new/#get/#load method.
146
+ #
147
+ # 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
+ #
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, global variable $summary is used. If it's empty too, error is raised.
150
+ #
151
+ # To get Sunflower instance which this page belongs to, use #sunflower of #belongs_to.
152
+ class Page
153
+ attr_accessor :text
154
+
155
+ attr_reader :orig_text
156
+
157
+ attr_reader :sunflower
158
+ alias :belongs_to :sunflower
159
+
160
+ attr_reader :pageid, :ns, :title, :touched, :lastrevid, :counter, :length, :starttimestamp, :edittoken, :protection #prop=info
161
+
162
+ def initialize title='', wiki=''
163
+ raise RuntimeError, 'Sunflower - title invalid: '+title if title.index(/[#<>\[\]\|\{\}]/)
164
+
165
+ @modulesExecd=[] #used by sunflower-commontasks.rb
166
+ @summaryAppend=[] #used by sunflower-commontasks.rb
167
+
168
+ @title=title
169
+ wiki=wiki+'.wikipedia.org' if wiki.index('.')==nil && wiki!=''
170
+
171
+ if wiki==''
172
+ count=ObjectSpace.each_object(Sunflower){|o| @sunflower=o}
173
+ raise RuntimeError, 'Sunflower - you must pass wiki name if using multiple Sunflowers at once!' if count>1
174
+ else
175
+ ObjectSpace.each_object(Sunflower){|o| @sunflower=o if o.wikiURL==wiki}
176
+ end
177
+
178
+ if title==''
179
+ @text=''
180
+ @orig_text=''
181
+ return
182
+ end
183
+
184
+ r=@sunflower.API('action=query&prop=info&inprop=protection&intoken=edit&titles='+CGI.escape(@title))
185
+ r=r['query']['pages'][r['query']['pages'].keys[0] ]
186
+ r.each{|key,value|
187
+ self.instance_variable_set('@'+key, value)
188
+ }
189
+
190
+ r=@sunflower.API('action=query&prop=revisions&rvprop=content&titles='+CGI.escape(@title))
191
+
192
+ begin
193
+ @text=r['query']['pages'][@pageid.to_s]['revisions'][0]['*']
194
+ rescue Exception
195
+ @text=''
196
+ end
197
+ @orig_text=@text
198
+ end
199
+
200
+ def dumpto file
201
+ if file.respond_to? :write #probably file or IO
202
+ file.write @text
203
+ else #filename?
204
+ f=File.open(file.to_s, 'w')
205
+ f.write @text
206
+ f.close
207
+ end
208
+ end
209
+
210
+ def dump
211
+ self.dumpto @title.gsub(/[^a-zA-Z0-9\-]/,'_')+'.txt'
212
+ end
213
+
214
+ def save title=@title, summary=$summary
215
+ raise RuntimeError, 'Sunflower - title invalid: '+title if title.index(/[#<>\[\]\|\{\}]/)
216
+ raise RuntimeError, 'Sunflower - no summary!' if (summary==nil || summary=='') && @summaryAppend==[]
217
+
218
+ summary='' if summary==nil
219
+ for i in @summaryAppend.uniq
220
+ summary+=', '+i
221
+ end
222
+ summary.sub!(/^, /,'')
223
+
224
+
225
+ if @orig_text==@text && title==@title
226
+ @sunflower.log('Page '+title+' not saved - no changes.')
227
+ return
228
+ end
229
+
230
+
231
+
232
+ self.code_cleanup if $alwaysDoCodeCleanup && self.respond_to?('code_cleanup')
233
+
234
+ 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
+ end
236
+ alias :put :save
237
+
238
+ def self.get title, wiki=''
239
+ Page.new(title, wiki)
240
+ end
241
+
242
+ def self.load title, wiki=''
243
+ Page.new(title, wiki)
244
+ end
245
+ end
246
+
247
+ class Hash
248
+ # just a lil patch
249
+ def first
250
+ self.values[0]
251
+ end
252
+ end
253
+