wikian 0.1.0 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -18
- data/Gemfile.lock +21 -0
- data/README.md +45 -19
- data/exe/wi +9 -0
- data/exe/wikian +2 -3
- data/lib/.gitignore +0 -0
- data/lib/wikian.rb +125 -4
- data/lib/wikian/get.rb +99 -0
- data/lib/wikian/post.rb +140 -0
- data/lib/wikian/search.rb +40 -0
- data/lib/wikian/subcommand.rb +74 -0
- data/lib/wikian/version.rb +2 -2
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7641f6124dd8e9db31fa2dc7688146107db693d65d8347af57d45ee40205519
|
4
|
+
data.tar.gz: a9def9f5d7d2cc6a00c375f9e690e96aaf193a601359ae98fb1f4abea1f31402
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7887e82d7f20332717314772cba3ca4bbb4d7cb54d5a1dab3235f1c248654949c3427feaa6915324b437117d0e87147c219a2acbed7654ddeb22c89541abc50
|
7
|
+
data.tar.gz: 37998e9226c13ddfa504921859e4da1f9f8e96a19edcc617faaf30a7a387d80fff4768e29c868f9ebfb363c168baaada1f3280cbc241c3f91c10c732e378c790
|
data/.gitignore
CHANGED
@@ -1,15 +1,6 @@
|
|
1
|
-
# files without extensions except directories
|
2
|
-
*
|
3
|
-
!/**/
|
4
|
-
!?*.*
|
5
|
-
|
6
1
|
# directories
|
7
|
-
.bundle/
|
8
|
-
_yardoc/
|
9
|
-
coverage/
|
10
2
|
doc/
|
11
3
|
pkg/
|
12
|
-
spec/reports/
|
13
4
|
tmp/
|
14
5
|
vendor/
|
15
6
|
|
@@ -22,6 +13,7 @@ vendor/
|
|
22
13
|
.*.swp
|
23
14
|
.DS_Store
|
24
15
|
.byebug
|
16
|
+
.byebugrc
|
25
17
|
.idea
|
26
18
|
.project
|
27
19
|
.rake*
|
@@ -46,12 +38,3 @@ secrets.yml
|
|
46
38
|
![MR]akefile
|
47
39
|
!bin/**/[^.]*
|
48
40
|
!exe/**/[^.]*
|
49
|
-
|
50
|
-
/.bundle/
|
51
|
-
/.yardoc
|
52
|
-
/_yardoc/
|
53
|
-
/coverage/
|
54
|
-
/doc/
|
55
|
-
/pkg/
|
56
|
-
/spec/reports/
|
57
|
-
/tmp/
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
wikian (0.1.5)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
minitest (5.14.2)
|
10
|
+
rake (12.3.3)
|
11
|
+
|
12
|
+
PLATFORMS
|
13
|
+
ruby
|
14
|
+
|
15
|
+
DEPENDENCIES
|
16
|
+
minitest (~> 5.0)
|
17
|
+
rake (~> 12.0)
|
18
|
+
wikian!
|
19
|
+
|
20
|
+
BUNDLED WITH
|
21
|
+
2.1.4
|
data/README.md
CHANGED
@@ -1,40 +1,66 @@
|
|
1
1
|
# Wikian
|
2
2
|
|
3
|
-
|
3
|
+
Want to be happier while editing wiki files?
|
4
|
+
Me too, that's why I use [Wikian](https://rubygems.org/gems/wikian):
|
4
5
|
|
5
|
-
|
6
|
+
```
|
7
|
+
$ gem install wikian
|
8
|
+
```
|
9
|
+
|
10
|
+
To use it create a Wikipedia account, then define and export the `WIKI_USER` and `SECRET_WIKI_PASS` variables in your `.bashrc`:
|
6
11
|
|
7
|
-
|
12
|
+
```bash
|
13
|
+
export WIKI_USER='Example_wiki_user_name'
|
14
|
+
export SECRET_WIKI_PASS='example_wiki_password'
|
15
|
+
```
|
8
16
|
|
9
|
-
|
17
|
+
Wikian works across [Wikimedia sites](https://meta.wikimedia.org/wiki/Our_projects) and follows the file naming convention:
|
10
18
|
|
11
|
-
```ruby
|
12
|
-
gem 'wikian'
|
13
19
|
```
|
20
|
+
<article_name>.<site>.wiki
|
21
|
+
```
|
22
|
+
|
23
|
+
Some valid file names:
|
14
24
|
|
15
|
-
|
25
|
+
```
|
26
|
+
Spider_Man.en.wikipedia.org.wiki
|
27
|
+
Spider_Man.es.wikipedia.org.wiki
|
28
|
+
excelsior.es.wiktionary.org.wiki
|
29
|
+
User:Example_User.www.mediawiki.org.wiki
|
30
|
+
```
|
16
31
|
|
17
|
-
|
32
|
+
#### Examples
|
18
33
|
|
19
|
-
|
34
|
+
To append some text to your Wiktionary user profile:
|
20
35
|
|
21
|
-
|
36
|
+
```bash
|
37
|
+
$ cat User:$WIKI_USER.en.wikitionary.org.wiki
|
22
38
|
|
23
|
-
|
39
|
+
== Last section==
|
40
|
+
testing
|
24
41
|
|
25
|
-
|
42
|
+
$ wi post -p User:$WIKI_USER.en.wikitionary.org.wiki
|
43
|
+
Article uploaded
|
44
|
+
```
|
26
45
|
|
27
|
-
|
46
|
+
To get an article's wikitext:
|
28
47
|
|
29
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
48
|
|
31
|
-
|
49
|
+
```
|
50
|
+
$ wi get -t
|
51
|
+
Creating template wiki.yml
|
32
52
|
|
33
|
-
|
53
|
+
$ wi g https://en.wikipedia.org/wiki/Wikipedia:Sandbox
|
54
|
+
Writing to Wikipedia:Sandbox.en.wikipedia.org.json
|
55
|
+
Writing to Wikipedia:Sandbox.en.wikipedia.org.wiki
|
56
|
+
```
|
34
57
|
|
35
|
-
|
58
|
+
You can then edit the the article in your favorite text editor and uploaded:
|
36
59
|
|
60
|
+
```bash
|
61
|
+
$ wi post Wikipedia:Sandbox.en.wikipedia.org.wiki
|
62
|
+
Article uploaded
|
63
|
+
```
|
37
64
|
|
38
|
-
|
65
|
+
Vim users should try out [mediawiki.vim](https://en.wikipedia.org/wiki/Help:Text_editor_support#Vim) which defines syntax highlighting and abbreviations for wikitext files.
|
39
66
|
|
40
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/exe/wi
ADDED
data/exe/wikian
CHANGED
data/lib/.gitignore
ADDED
File without changes
|
data/lib/wikian.rb
CHANGED
@@ -1,8 +1,129 @@
|
|
1
1
|
$LOAD_PATH.unshift __dir__
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'wikian/subcommand'
|
4
|
+
require 'wikian/get'
|
5
|
+
require 'wikian/post'
|
6
|
+
require 'wikian/search'
|
7
|
+
require 'wikian/version'
|
4
8
|
|
5
|
-
|
6
|
-
|
7
|
-
|
9
|
+
# stdlib
|
10
|
+
require 'fileutils'
|
11
|
+
require 'json'
|
12
|
+
require 'net/http'
|
13
|
+
require 'open-uri'
|
14
|
+
require 'yaml'
|
15
|
+
|
16
|
+
# external libraries
|
17
|
+
require 'byebug'
|
18
|
+
|
19
|
+
class Array
|
20
|
+
# return true if `self` and `elms` have any element in common
|
21
|
+
def have?(elms)
|
22
|
+
(self & elms).length > 0 ? true : false
|
23
|
+
end
|
24
|
+
alias_method :has?, :have?
|
25
|
+
end
|
26
|
+
|
27
|
+
class Hash
|
28
|
+
# return a query string representation of a hash
|
29
|
+
def to_query
|
30
|
+
URI.decode(URI.encode_www_form(self))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Wikian
|
35
|
+
class WikianError < StandardError; end
|
36
|
+
class UnknownSubcommandError < WikianError; end
|
37
|
+
|
38
|
+
attr_accessor :subcommand, :args
|
39
|
+
|
40
|
+
CONFIG_FILE = 'wiki.yml'
|
41
|
+
RESPONSE_FORMAT = 'json'
|
42
|
+
|
43
|
+
def initialize(*args)
|
44
|
+
@args = args
|
45
|
+
end
|
46
|
+
|
47
|
+
def run
|
48
|
+
if args.have?(%w(-h --help))
|
49
|
+
help
|
50
|
+
elsif args.have?(%w(-v --version))
|
51
|
+
version
|
52
|
+
end
|
53
|
+
|
54
|
+
@subcommand = args.shift
|
55
|
+
|
56
|
+
raise(UnknownSubcommandError, "Unkown Subcommand") unless %w(g p s get post search).include?(subcommand)
|
57
|
+
|
58
|
+
if subcommand[0] == 'g'
|
59
|
+
api = Wikian::Get.new(args)
|
60
|
+
api.doit
|
61
|
+
api.extract_wikitext
|
62
|
+
elsif subcommand[0] == 's'
|
63
|
+
api = Wikian::Search.new(args)
|
64
|
+
api.doit
|
65
|
+
else
|
66
|
+
api = Wikian::Post.new(args)
|
67
|
+
api.post
|
68
|
+
end
|
69
|
+
|
70
|
+
rescue UnknownSubcommandError => e
|
71
|
+
puts "#{e.class} #{e.message}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def help
|
75
|
+
puts <<~eos
|
76
|
+
Usage:
|
77
|
+
wiki [options] <subcommand> [url|file]
|
78
|
+
|
79
|
+
Options:
|
80
|
+
-a, --append append the input file
|
81
|
+
-c, --captcha ID:MESSAGE captcha info
|
82
|
+
-d, --debug debug
|
83
|
+
-m, --message MESSAGE add a commit message (HIGHLY recommended)
|
84
|
+
-p, --prepend prepend the input file
|
85
|
+
-r, --remove-cookie remove API cookie
|
86
|
+
-t, --template create template configuration file
|
87
|
+
-v, --version
|
88
|
+
|
89
|
+
Subcommands:
|
90
|
+
g, get get wikitext file from a wikipedia article
|
91
|
+
p, post post wikitext file to a wikipedia article
|
92
|
+
s, search search wikitext file to a wikipedia article
|
93
|
+
|
94
|
+
Examples:
|
95
|
+
# create wiki.yml template
|
96
|
+
wiki -t
|
97
|
+
|
98
|
+
# download article and create response and wikitext files
|
99
|
+
wiki get https://en.wikipedia.org/wiki/Spider-Man
|
100
|
+
|
101
|
+
# upload file to English Wikipedia
|
102
|
+
wiki post Spider-Man.en.wikipedia.org.wiki
|
103
|
+
|
104
|
+
# upload file to Spanish Wikipedia
|
105
|
+
wiki post Spider-Man.es.wikipedia.org.wiki
|
106
|
+
|
107
|
+
# upload file to English Wiktionary
|
108
|
+
wiki file to Spider-Man.es.wiktionary.org.wiki
|
109
|
+
|
110
|
+
# append new section to article
|
111
|
+
wiki post -a Spider-Man-new-section.wiki
|
112
|
+
|
113
|
+
# heavy use of the API may require cache validation
|
114
|
+
wiki post -c 1234:someMessage spider-Man.wiki
|
115
|
+
|
116
|
+
Comments:
|
117
|
+
Posted files must follow the convention:
|
118
|
+
<article_name>.<host>.wiki
|
119
|
+
where <host> is a wikimedia site.
|
120
|
+
More info at: https://meta.wikimedia.org/wiki/Our_projects
|
121
|
+
eos
|
122
|
+
exit
|
123
|
+
end
|
124
|
+
|
125
|
+
def version
|
126
|
+
puts "wikian #{VERSION}"
|
127
|
+
exit
|
128
|
+
end
|
8
129
|
end
|
data/lib/wikian/get.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
class Wikian
|
2
|
+
class WikianGetError < StandardError; end
|
3
|
+
class ExtractWikiError < WikianGetError; end
|
4
|
+
class ArgumentRequiredError < WikianGetError; end
|
5
|
+
class BadUrlError < WikianGetError; end
|
6
|
+
|
7
|
+
class Get < Subcommand
|
8
|
+
attr_accessor :title
|
9
|
+
|
10
|
+
def initialize(args)
|
11
|
+
raise ArgumentRequiredError if args.empty?
|
12
|
+
|
13
|
+
super
|
14
|
+
|
15
|
+
url = URI(args.find{|arg| arg =~ URI.regexp})
|
16
|
+
|
17
|
+
raise BadUrlError unless url.path
|
18
|
+
|
19
|
+
@title = File.basename(url.path)
|
20
|
+
|
21
|
+
@output_file = title + '.' + url.host
|
22
|
+
|
23
|
+
@params.merge!('titles' => title, 'format' => Wikian::RESPONSE_FORMAT)
|
24
|
+
|
25
|
+
@query = @params.to_query
|
26
|
+
|
27
|
+
@api_url = URI("https://#{url.host}/w/api.php?#{query}")
|
28
|
+
rescue => e
|
29
|
+
puts "#{e.class} #{e.message} in #{__FILE__}"
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
|
33
|
+
# extract wikitext from the response file and save it into a `.wiki` file
|
34
|
+
#
|
35
|
+
# return: nil
|
36
|
+
def extract_wikitext
|
37
|
+
if !res['content-type'].match?('json') || !(pages = JSON.parse(res.body).dig('query','pages'))
|
38
|
+
raise ExtractWikiError, 'JSON response has no pages'
|
39
|
+
end
|
40
|
+
|
41
|
+
create_wiki = -> (title, revisions) do
|
42
|
+
revisions.each do |revision|
|
43
|
+
wiki_file= File.basename(response_file, File.extname(response_file)) + '.wiki'
|
44
|
+
if revision['revid'].nil? && revisions.size > 1
|
45
|
+
STDERR.puts "Warning: you should specify 'revid' in #{Wikian::CONFIG_FILE} to prevent overriding different revisions"
|
46
|
+
end
|
47
|
+
File.open(wiki_file,'w') do |f|
|
48
|
+
content = revision.dig('slots', 'main', 'content') ||
|
49
|
+
revision.dig('slots', '*') ||
|
50
|
+
revision.dig('*')
|
51
|
+
STDERR.puts "Warning: nil 'content' in #{Wikian::CONFIG_FILE}" unless content
|
52
|
+
STDERR.puts "Writing to #{wiki_file}"
|
53
|
+
f.puts content
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# this is ugly, but Wikipedia is inconsistent in their JSON value for 'pages'. Sometimes it's a hash, sometimes it's an array.
|
59
|
+
if pages.respond_to? :keys
|
60
|
+
byebug
|
61
|
+
create_wiki.call(pages.values.first['title'], pages.values.first['revisions'])
|
62
|
+
else
|
63
|
+
pages.each do |page|
|
64
|
+
create_wiki.call(page['title'], page['revisions'])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
rescue => e
|
69
|
+
puts "An error occurred while extracting the wikitext",
|
70
|
+
"Try using a new config file by pasing the '-t' option.",
|
71
|
+
"Or pass '-d' option for debugging"
|
72
|
+
exit
|
73
|
+
end
|
74
|
+
|
75
|
+
def template
|
76
|
+
<<~eos
|
77
|
+
meta:
|
78
|
+
headers:
|
79
|
+
user-agent: Wikian
|
80
|
+
api:
|
81
|
+
action:
|
82
|
+
- query
|
83
|
+
prop:
|
84
|
+
- revisions
|
85
|
+
rvprop:
|
86
|
+
- content
|
87
|
+
#rvsection:
|
88
|
+
# - 0
|
89
|
+
# - 2
|
90
|
+
rvslots:
|
91
|
+
- main
|
92
|
+
formatversion:
|
93
|
+
- 2
|
94
|
+
format:
|
95
|
+
- json
|
96
|
+
eos
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/wikian/post.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
class Wikian
|
2
|
+
class WikianPostError < StandardError; end
|
3
|
+
class WikiFileError < WikianPostError; end
|
4
|
+
|
5
|
+
class Post
|
6
|
+
attr_accessor :args, :baseurl, :header, :input_file, :debug, :login_token,
|
7
|
+
:login_cookie, :csrf_token, :csrf_cookie, :query, :body_text, :username
|
8
|
+
|
9
|
+
def initialize(args)
|
10
|
+
@args = args
|
11
|
+
|
12
|
+
# input wikitext file
|
13
|
+
@input_file = args.find{|f| File.exist? f}
|
14
|
+
raise WikiFileError unless input_file
|
15
|
+
|
16
|
+
site = input_file.match(/\.(.*)\.wiki/)[1]
|
17
|
+
|
18
|
+
@baseurl = "https://#{site}/w/api.php"
|
19
|
+
|
20
|
+
@header = {}
|
21
|
+
|
22
|
+
@username = ENV['WIKI_USER']
|
23
|
+
|
24
|
+
@debug = (args & %w(-d --debug)).length > 0 ? true : false
|
25
|
+
rescue => e
|
26
|
+
puts "#{e.class} in #{__FILE__}"
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
def post
|
31
|
+
# remove expired cookie
|
32
|
+
if expired_cookie? || args.have?(%w(-r --remove-cookie))
|
33
|
+
FileUtils.rm_f(csrf_cookie_file)
|
34
|
+
end
|
35
|
+
|
36
|
+
# csrf_cookie can be reused among multiple requests. But csrf_token must be updated on each request
|
37
|
+
if File.exist?(csrf_cookie_file)
|
38
|
+
@csrf_cookie = File.read(csrf_cookie_file)
|
39
|
+
else
|
40
|
+
get_login_token
|
41
|
+
|
42
|
+
get_csrf_cookie
|
43
|
+
end
|
44
|
+
get_csrf_token
|
45
|
+
|
46
|
+
build_query_string
|
47
|
+
|
48
|
+
upload_article
|
49
|
+
end
|
50
|
+
|
51
|
+
# check if the cookie is expired (older than an hour)
|
52
|
+
def expired_cookie?
|
53
|
+
File.exist?(csrf_cookie_file) && ((Time.now - File.open(csrf_cookie_file).stat.ctime)/3600 > 1)
|
54
|
+
end
|
55
|
+
|
56
|
+
def csrf_cookie_file
|
57
|
+
'csrf_cookie'
|
58
|
+
end
|
59
|
+
|
60
|
+
def remove_cookie_metadata(cookie)
|
61
|
+
cookie.gsub(/secure;|path=.*?[,;]|httponly[;,]|samesite=.*?[;,]|expires=.....*?[,;]|domain=.*?;|max-age=.*?[;,]/i,'').squeeze(' ')
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_login_token
|
65
|
+
puts("Getting login token/cookie") if debug
|
66
|
+
url = URI("#{baseurl}?action=query&meta=tokens&format=json&type=login")
|
67
|
+
res = URI.open(url)
|
68
|
+
json = JSON.parse(res.read)
|
69
|
+
@login_token = json.dig('query','tokens','logintoken')
|
70
|
+
@login_cookie = remove_cookie_metadata(res.meta['set-cookie'])
|
71
|
+
puts(json) if debug
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_csrf_cookie
|
75
|
+
puts("\nGetting csrf cookie using token #{login_token}") if debug
|
76
|
+
url = URI("#{baseurl}?action=login&lgname=#{username}&format=json")
|
77
|
+
req = Net::HTTP::Post.new(url, header.merge('cookie' => login_cookie, 'content-type' => 'application/x-www-form-urlencoded'))
|
78
|
+
req.set_form_data('lgpassword' => ENV['SECRET_WIKI_PASS'], 'lgtoken' => login_token)
|
79
|
+
http = Net::HTTP.new(url.host, url.port)
|
80
|
+
http.use_ssl = true
|
81
|
+
res=http.request(req)
|
82
|
+
@csrf_cookie = remove_cookie_metadata(res['set-cookie'])
|
83
|
+
File.write(csrf_cookie_file, @csrf_cookie)
|
84
|
+
puts(res.body) if debug
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_csrf_token
|
88
|
+
puts("\nGetting csrf token using csrf cookies") if debug
|
89
|
+
url = URI("#{baseurl}?action=query&meta=tokens&format=json&type=csrf")
|
90
|
+
res = URI.open(url, header.merge('cookie' => csrf_cookie))
|
91
|
+
json = JSON.parse(res.read)
|
92
|
+
@csrf_token = json.dig('query','tokens','csrftoken')
|
93
|
+
puts(json) if debug
|
94
|
+
end
|
95
|
+
|
96
|
+
def build_query_string
|
97
|
+
params={}
|
98
|
+
params['action'] = 'edit'
|
99
|
+
params['format'] = Wikian::RESPONSE_FORMAT
|
100
|
+
params['title'] = input_file.sub(/\..*/,'')
|
101
|
+
wikitext = File.read(input_file)
|
102
|
+
if args.have?(%w(-a --append))
|
103
|
+
params['appendtext'] = wikitext
|
104
|
+
elsif args.have?(%w(-p --prepend))
|
105
|
+
params['prependtext'] = wikitext
|
106
|
+
else
|
107
|
+
# pass the wikitext in request body
|
108
|
+
@body_text = wikitext
|
109
|
+
end
|
110
|
+
if args.include?('-c')
|
111
|
+
params['captchaid'], params['captchaword'] = args[args.index('-c')+1].split(':')
|
112
|
+
end
|
113
|
+
if args.include?('-m')
|
114
|
+
params['summary'] = args[args.index('-m')+1]
|
115
|
+
end
|
116
|
+
@query = URI.encode_www_form(params)
|
117
|
+
end
|
118
|
+
|
119
|
+
def upload_article
|
120
|
+
puts("\nUploading the wiki article using csrf token #{csrf_token}") if debug
|
121
|
+
url = URI("#{baseurl}?#{query}")
|
122
|
+
req = Net::HTTP::Post.new(url, header.merge('cookie' => csrf_cookie, 'content-type' => 'application/x-www-form-urlencoded'))
|
123
|
+
http = Net::HTTP.new(url.host, url.port)
|
124
|
+
req_body = body_text.nil? ? {token: csrf_token} : {token: csrf_token, text: body_text}
|
125
|
+
req.set_form_data(req_body)
|
126
|
+
http = Net::HTTP.new(url.host, url.port)
|
127
|
+
http.use_ssl = true
|
128
|
+
res=http.request(req)
|
129
|
+
json = JSON.parse(res.body)
|
130
|
+
puts(json) if debug
|
131
|
+
if json.dig('error')
|
132
|
+
puts "An error occurred while uploding the file",
|
133
|
+
"Try pasing the '-r' option to remove '#{csrf_cookie_file}'",
|
134
|
+
"Or pass '-d' option for debugging"
|
135
|
+
else
|
136
|
+
puts "Article uploaded"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Wikian
|
2
|
+
class WikianGetError < StandardError; end
|
3
|
+
class ArgumentRequiredError < WikianGetError; end
|
4
|
+
|
5
|
+
class Search < Subcommand
|
6
|
+
def initialize(args)
|
7
|
+
super
|
8
|
+
|
9
|
+
@output_file = yaml['api']['srsearch'].first
|
10
|
+
|
11
|
+
@params.merge!('format' => Wikian::RESPONSE_FORMAT)
|
12
|
+
|
13
|
+
@query = @params.to_query
|
14
|
+
|
15
|
+
@api_url = URI("https://#{yaml['meta']['site']}/w/api.php?#{query}")
|
16
|
+
rescue => e
|
17
|
+
puts "#{e.class} #{e.message} in #{__FILE__}"
|
18
|
+
exit
|
19
|
+
end
|
20
|
+
|
21
|
+
def template
|
22
|
+
<<~eos
|
23
|
+
# Get last 5 revisions of the Main Page.
|
24
|
+
meta:
|
25
|
+
site: en.wikipedia.org
|
26
|
+
headers:
|
27
|
+
user-agent: Wikian
|
28
|
+
api:
|
29
|
+
action:
|
30
|
+
- query
|
31
|
+
list:
|
32
|
+
- search
|
33
|
+
srsearch:
|
34
|
+
- Craig Noone
|
35
|
+
format:
|
36
|
+
- json
|
37
|
+
eos
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env -S ruby -W0
|
2
|
+
class Wikian
|
3
|
+
# class to be inherited by other Wikian classes
|
4
|
+
class Subcommand
|
5
|
+
attr_accessor :args, :res, :yaml, :query, :title, :api_url, :debug, :output_file
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
@args = args
|
9
|
+
|
10
|
+
if args.have?(%w(-t --template))
|
11
|
+
puts "Creating template #{Wikian::CONFIG_FILE}"
|
12
|
+
make_template
|
13
|
+
exit
|
14
|
+
end
|
15
|
+
|
16
|
+
@debug = (args & %w(-d --debug)).length > 0 ? true : false
|
17
|
+
|
18
|
+
@yaml=YAML.load(File.open(Wikian::CONFIG_FILE))
|
19
|
+
|
20
|
+
# some params like 'titles' can contain multiple entries joined by '|'. More info in Wikipedia API docs
|
21
|
+
@params = Hash[yaml['api'].keys.zip(yaml['api'].values.map{|arr| arr.join("|")})]
|
22
|
+
end
|
23
|
+
|
24
|
+
def make_template
|
25
|
+
if File.exist?(CONFIG_FILE)
|
26
|
+
puts "Overwrite existing '#{CONFIG_FILE}'? [yn]"
|
27
|
+
answer = STDIN.gets.chomp
|
28
|
+
(puts 'Bye'; exit) if answer != 'y'
|
29
|
+
end
|
30
|
+
|
31
|
+
File.open(CONFIG_FILE, 'w') do |f|
|
32
|
+
f.write template
|
33
|
+
end
|
34
|
+
exit
|
35
|
+
end
|
36
|
+
|
37
|
+
# HTTP response file name. Its extension depends on the 'content-type' header
|
38
|
+
def response_file
|
39
|
+
output_file + '.' + res['content-type'].split('/').last.sub(/;.*/,'')
|
40
|
+
end
|
41
|
+
|
42
|
+
# write response in to `response_file`
|
43
|
+
def write_response
|
44
|
+
STDERR.puts "Writing to #{response_file}"
|
45
|
+
File.open(response_file, 'w') do |f|
|
46
|
+
f.puts prettify(res.body)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def doit
|
51
|
+
puts api_url if debug
|
52
|
+
|
53
|
+
req = Net::HTTP::Get.new(api_url, yaml['meta']['headers'])
|
54
|
+
|
55
|
+
http = Net::HTTP.new(api_url.host, api_url.port)
|
56
|
+
|
57
|
+
http.use_ssl = true
|
58
|
+
|
59
|
+
@res=http.request(req)
|
60
|
+
|
61
|
+
write_response
|
62
|
+
rescue => e
|
63
|
+
puts "#{e.class} #{e.message} in #{__FILE__}"
|
64
|
+
exit
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# if response is JSON prettify it, otherwise return it unchanged
|
70
|
+
def prettify(str)
|
71
|
+
res['content-type'].match?('json') ? JSON.pretty_generate(JSON.parse(str)) : str
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/wikian/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "0.1.
|
1
|
+
class Wikian
|
2
|
+
VERSION = "0.1.5"
|
3
3
|
end
|
metadata
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wikian
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sergioro
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-09-
|
11
|
+
date: 2020-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Get and edit wikipedia articles
|
14
14
|
email:
|
15
15
|
- yo@sergioro.com
|
16
16
|
executables:
|
17
|
+
- wi
|
17
18
|
- wikian
|
18
19
|
extensions: []
|
19
20
|
extra_rdoc_files: []
|
@@ -21,13 +22,20 @@ files:
|
|
21
22
|
- ".gitignore"
|
22
23
|
- ".travis.yml"
|
23
24
|
- Gemfile
|
25
|
+
- Gemfile.lock
|
24
26
|
- LICENSE.txt
|
25
27
|
- README.md
|
26
28
|
- Rakefile
|
27
29
|
- bin/console
|
28
30
|
- bin/setup
|
31
|
+
- exe/wi
|
29
32
|
- exe/wikian
|
33
|
+
- lib/.gitignore
|
30
34
|
- lib/wikian.rb
|
35
|
+
- lib/wikian/get.rb
|
36
|
+
- lib/wikian/post.rb
|
37
|
+
- lib/wikian/search.rb
|
38
|
+
- lib/wikian/subcommand.rb
|
31
39
|
- lib/wikian/version.rb
|
32
40
|
- wikian.gemspec
|
33
41
|
homepage:
|