sunflower 0.2
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/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
|
+
|