sunflower 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +4 -0
- data/README +21 -0
- data/bin/sunflower-setup +68 -0
- data/example-bot.rb +12 -0
- data/lib/sunflower/commontasks.rb +251 -0
- data/lib/sunflower/core.rb +253 -0
- data/lib/sunflower/listmaker.rb +152 -0
- data/lib/sunflower.rb +4 -0
- data/scripts/ZDBOT.rb +62 -0
- data/scripts/aktualizacjapilkarzy.rb +339 -0
- data/scripts/author-list.rb +36 -0
- data/scripts/changeimage.rb +42 -0
- data/scripts/fix-bold-in-headers.rb +53 -0
- data/scripts/fix-double-pipes.rb +49 -0
- data/scripts/fix-langs.rb +43 -0
- data/scripts/fix-multiple-same-refs.rb +102 -0
- data/scripts/fix-some-entities.rb +43 -0
- data/scripts/fix-unicode-control-chars.rb +51 -0
- data/scripts/insight.rb +133 -0
- data/scripts/lekkoatl-portal.rb +51 -0
- data/scripts/make-id2team-list.rb +32 -0
- data/scripts/recat.rb +32 -0
- data/scripts/wanted.rb +72 -0
- data/use-easy-bot.rb +54 -0
- metadata +115 -0
data/LICENSE
ADDED
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ą.
|
data/bin/sunflower-setup
ADDED
@@ -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,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
|
+
|