tamiyo 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/LICENCE.txt +22 -0
- data/Rakefile +3 -0
- data/Readme.md +4 -0
- data/bin/tamiyo +3 -0
- data/lib/tamiyo.rb +22 -0
- data/lib/tamiyo/card.rb +26 -0
- data/lib/tamiyo/dummy_scrape.rb +57 -0
- data/lib/tamiyo/dummy_scrape_chain.rb +11 -0
- data/lib/tamiyo/gatherer_scrape.rb +26 -0
- data/lib/tamiyo/gatherer_scrape_chain.rb +11 -0
- data/lib/tamiyo/process.rb +13 -0
- data/lib/tamiyo/puts_sink.rb +11 -0
- data/lib/tamiyo/scrape_parser.rb +129 -0
- data/lib/tamiyo/value.rb +50 -0
- data/lib/tamiyo/version.rb +3 -0
- data/tamiyo.gemspec +23 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bbf3ec46f9860183fa06e9c767262c3c214f5562
|
4
|
+
data.tar.gz: 955834fbde1e9d80fad20ebf9715a360683de6a4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ea01d30d6f7c33ae1ab5ec49536a669515b61fa750f0d847ad0428f7295c1a4ec550596ff763c4a8a4dc2d60d35b54ecad819485ced4d96e9a214a4281536db9
|
7
|
+
data.tar.gz: 9606a0a8b306362f15c54d946ca416a1567071ed1e3c3618b65c85fd02fd5e9846a8f640b6429f9d54d5053cf596ae8fb201a87b6a84f026270821bb8aa08172
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENCE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
The MIT License (MIT)
|
3
|
+
|
4
|
+
Copyright (c) 2013 Roger Norling
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
14
|
+
copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
SOFTWARE.
|
data/Rakefile
ADDED
data/Readme.md
ADDED
data/bin/tamiyo
ADDED
data/lib/tamiyo.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'slop'
|
2
|
+
|
3
|
+
opts = Slop.new help: true do
|
4
|
+
banner "Usage: #{File.basename __FILE__} <command>"
|
5
|
+
|
6
|
+
command 'dummy-scrape' do
|
7
|
+
run do |opts, args|
|
8
|
+
require_relative 'tamiyo/dummy_scrape_chain'
|
9
|
+
Tamiyo::DummyScrapeChain.create.pump
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
command 'gatherer-scrape' do
|
14
|
+
run do |opts, args|
|
15
|
+
require_relative 'tamiyo/gatherer_scrape_chain'
|
16
|
+
Tamiyo::GathererScrapeChain.create(args.first || "Dragon's Maze").pump
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.parse
|
22
|
+
puts opts if ARGV.empty?
|
data/lib/tamiyo/card.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative 'value'
|
2
|
+
|
3
|
+
module Tamiyo
|
4
|
+
Card = Value.new(:name, :cost, :colors, :type, :text, :pt, :loyalty,
|
5
|
+
:table_row, :played_tapped, :picture_url) do
|
6
|
+
def with_split(new_text)
|
7
|
+
Card.new(name, cost, colors, type, "#{text}\n---\n#{new_text}",
|
8
|
+
pt, loyalty, table_row, played_tapped, picture_url)
|
9
|
+
end
|
10
|
+
|
11
|
+
def inspect
|
12
|
+
["Name: #{name}",
|
13
|
+
"Cost: #{cost}",
|
14
|
+
"Color identity: #{colors.join}",
|
15
|
+
"Card type: #{type}",
|
16
|
+
'Rules text:',
|
17
|
+
text,
|
18
|
+
pt && "P/T: #{pt}",
|
19
|
+
loyalty && "Loyalty: #{loyalty}",
|
20
|
+
"Row: #{table_row}",
|
21
|
+
"Played tapped: #{played_tapped}",
|
22
|
+
"Picture url: #{picture_url}"
|
23
|
+
].compact.join "\n"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Tamiyo
|
4
|
+
class DummyScrape
|
5
|
+
def xml_rows
|
6
|
+
Nokogiri::HTML(<<TestXml).css 'tr'
|
7
|
+
<div class="textspoiler">
|
8
|
+
<table>
|
9
|
+
<tr>
|
10
|
+
<td>Name</td>
|
11
|
+
<td><a id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_cardEntries_ctl00_cardLink" class="nameLink" onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=369036">Advent of the Wurm</a></td></tr>
|
12
|
+
<tr id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_cardEntries_ctl00_costRow">
|
13
|
+
<td>Cost:</td>
|
14
|
+
<td>1GGW</td></tr>
|
15
|
+
<tr>
|
16
|
+
<td>Type:</td>
|
17
|
+
<td>Instant</td></tr>
|
18
|
+
<tr>
|
19
|
+
<td>Pow/Tgh:</td>
|
20
|
+
<td></td></tr>
|
21
|
+
<tr>
|
22
|
+
<td>Rules Text:</td>
|
23
|
+
<td>Put a 5/5 green Wurm creature token with trample onto the battlefield.</td></tr>
|
24
|
+
<tr>
|
25
|
+
<td>Set/Rarity:</td>
|
26
|
+
<td>Dragon's Maze Rare</td></tr>
|
27
|
+
<tr>
|
28
|
+
<td colspan="2"><br /></td></tr>
|
29
|
+
<tr>
|
30
|
+
<td>Name</td>
|
31
|
+
<td><a id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_cardEntries_ctl01_cardLink" class="nameLink" onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=368961">Ætherling</a></td></tr>
|
32
|
+
<tr id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_cardEntries_ctl01_costRow">
|
33
|
+
<td>Cost:</td>
|
34
|
+
<td>4UU</td></tr>
|
35
|
+
<tr>
|
36
|
+
<td>Type:</td>
|
37
|
+
<td>Creature — Shapeshifter</td></tr>
|
38
|
+
<tr>
|
39
|
+
<td>Pow/Tgh:</td>
|
40
|
+
<td>(4/5)</td></tr>
|
41
|
+
<tr>
|
42
|
+
<td>Rules Text:</td>
|
43
|
+
<td>{U}: Exile Ætherling. Return it to the battlefield under its owner's control at the beginning of the next end step.<br />
|
44
|
+
{U}: Ætherling can't be blocked this turn.<br />
|
45
|
+
{1}: Ætherling gets +1/-1 until end of turn.<br />
|
46
|
+
{1}: Ætherling gets -1/+1 until end of turn.</td></tr>
|
47
|
+
<tr>
|
48
|
+
<td>Set/Rarity:</td>
|
49
|
+
<td>Dragon's Maze Rare</td></tr>
|
50
|
+
<tr>
|
51
|
+
<td colspan="2"><br /></td></tr>
|
52
|
+
</table>
|
53
|
+
</div>
|
54
|
+
TestXml
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'httpclient'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Tamiyo
|
5
|
+
class GathererScrape
|
6
|
+
def initialize(set_name)
|
7
|
+
@set_name = set_name
|
8
|
+
end
|
9
|
+
|
10
|
+
def xml_rows
|
11
|
+
host = 'http://gatherer.wizards.com/'
|
12
|
+
path = 'Pages/Search/Default.aspx'
|
13
|
+
query = '?output=spoiler&method=text&action=advanced&set=["%s"]&special=true' % @set_name.tr(' ', '+')
|
14
|
+
url = URI.escape "#{host}#{path}#{query}"
|
15
|
+
response = HTTPClient.new.get url
|
16
|
+
html = Nokogiri::HTML ampersand_fixed(response.body)
|
17
|
+
html.css 'div.textspoiler tr'
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def ampersand_fixed(text)
|
23
|
+
text.gsub /&(?![^;]{,4};)/, '&'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative 'gatherer_scrape'
|
2
|
+
require_relative 'scrape_parser'
|
3
|
+
require_relative 'puts_sink'
|
4
|
+
|
5
|
+
module Tamiyo
|
6
|
+
class GathererScrapeChain
|
7
|
+
def self.create(set)
|
8
|
+
Process.chain GathererScrape.new(set), ScrapeParser.new, PutsSink.new
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require_relative 'process'
|
2
|
+
require_relative 'card'
|
3
|
+
|
4
|
+
module Tamiyo
|
5
|
+
class ScrapeParser
|
6
|
+
include Process::Node
|
7
|
+
|
8
|
+
def pull
|
9
|
+
parse_cards_from @chain.xml_rows
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse_cards_from(xml)
|
13
|
+
xml.each_with_object({}) do |tr, collection|
|
14
|
+
tds = tr.css 'td'
|
15
|
+
if tds.length == 2
|
16
|
+
left, right = *tds
|
17
|
+
right_text = right.content.tr '—', '-'
|
18
|
+
case fold left.content
|
19
|
+
when 'Name'
|
20
|
+
href = right.at_css('a')['href']
|
21
|
+
@id = href.match(/multiverseid=(.*)/)[1]
|
22
|
+
@name = fold right_text
|
23
|
+
when 'Cost:'
|
24
|
+
@cost = fold right_text
|
25
|
+
when 'Type:'
|
26
|
+
@type = fold right_text
|
27
|
+
when 'Pow/Tgh:'
|
28
|
+
@pt = fold(right_text).tr '()', ''
|
29
|
+
@pt = nil if @pt.empty?
|
30
|
+
when 'Loyalty:'
|
31
|
+
@loyalty = right_text.strip.tr '()', ''
|
32
|
+
when 'Rules Text:'
|
33
|
+
@text = right_text.strip.each_line.map(&:strip).join "\n"
|
34
|
+
end
|
35
|
+
else
|
36
|
+
card = parse_card collection
|
37
|
+
collection[card.name] = card
|
38
|
+
@pt = @loyalty = nil
|
39
|
+
end
|
40
|
+
end.values
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_card(collection)
|
44
|
+
split_card = @name[match_split_card]
|
45
|
+
@name = beautify @name
|
46
|
+
|
47
|
+
if collection.include? @name
|
48
|
+
card = collection[@name]
|
49
|
+
if split_card && !card.text.include?(@text)
|
50
|
+
card.with_split @text
|
51
|
+
else
|
52
|
+
card
|
53
|
+
end
|
54
|
+
else
|
55
|
+
Card.with name: @name,
|
56
|
+
cost: @cost,
|
57
|
+
type: @type,
|
58
|
+
text: @text,
|
59
|
+
pt: @pt,
|
60
|
+
loyalty: @loyalty,
|
61
|
+
colors: colors,
|
62
|
+
table_row: table_row,
|
63
|
+
played_tapped: played_tapped,
|
64
|
+
picture_url: picture_url
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def beautify(name)
|
69
|
+
name.gsub(match_split_card, '')
|
70
|
+
.gsub('XX', '')
|
71
|
+
.gsub('Æ', 'AE')
|
72
|
+
.tr('’', "'")
|
73
|
+
end
|
74
|
+
|
75
|
+
def colors
|
76
|
+
{'W' => ' is white.',
|
77
|
+
'U' => ' is blue.',
|
78
|
+
'B' => ' is black.',
|
79
|
+
'R' => ' is red.',
|
80
|
+
'G' => ' is green.'
|
81
|
+
}.each_with_object [] do |(color, identity), colors|
|
82
|
+
if @cost.include?(color) || @text.include?(@name + identity)
|
83
|
+
colors << color
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def table_row
|
89
|
+
mana_source = @type.end_with?('Artifact') &&
|
90
|
+
@text.include?('{T}') && @text.include?('to your mana pool')
|
91
|
+
case main_type_based_on @type
|
92
|
+
when /Land/, mana_source
|
93
|
+
:first_row
|
94
|
+
when /Sorcery|Instant/
|
95
|
+
:stack
|
96
|
+
when /Creature/
|
97
|
+
:third_row
|
98
|
+
else
|
99
|
+
:second_row
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def main_type_based_on(type)
|
104
|
+
# The last word before a dash or $
|
105
|
+
type.match(/(\w+)\s*(-|$)/)[1]
|
106
|
+
end
|
107
|
+
|
108
|
+
def played_tapped
|
109
|
+
@text.include?(@name + ' enters the battlefield tapped.')
|
110
|
+
end
|
111
|
+
|
112
|
+
def picture_url
|
113
|
+
host = 'http://gatherer.wizards.com/'
|
114
|
+
path = 'Handlers/Image.ashx'
|
115
|
+
query = '?multiverseid=%s&type=card' % @id
|
116
|
+
"#{host}#{path}#{query}"
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def fold(text)
|
122
|
+
text.strip.gsub /\s+/, ' '
|
123
|
+
end
|
124
|
+
|
125
|
+
def match_split_card
|
126
|
+
/ \(.*\)/
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/tamiyo/value.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
# Copyright (c) 2011 Tom Crayford
|
3
|
+
# Refer to https://rubygems.org/gems/values
|
4
|
+
|
5
|
+
module Tamiyo
|
6
|
+
class Value
|
7
|
+
def self.new(*fields, &block)
|
8
|
+
Class.new do
|
9
|
+
attr_reader(:hash, *fields)
|
10
|
+
|
11
|
+
define_method(:initialize) do |*values|
|
12
|
+
raise ArgumentError.new("wrong number of arguments, #{values.size} for #{fields.size}") if fields.size != values.size
|
13
|
+
|
14
|
+
fields.zip(values) do |field, value|
|
15
|
+
instance_variable_set(:"@#{field}", value)
|
16
|
+
end
|
17
|
+
|
18
|
+
@hash = self.class.hash ^ values.hash
|
19
|
+
|
20
|
+
freeze
|
21
|
+
end
|
22
|
+
|
23
|
+
const_set :VALUE_ATTRS, fields
|
24
|
+
|
25
|
+
def self.with(hash)
|
26
|
+
unexpected_keys = hash.keys - self::VALUE_ATTRS
|
27
|
+
if unexpected_keys.any?
|
28
|
+
raise ArgumentError.new("Unexpected hash keys: #{unexpected_keys}")
|
29
|
+
end
|
30
|
+
|
31
|
+
new(*hash.values_at(*self::VALUE_ATTRS))
|
32
|
+
end
|
33
|
+
|
34
|
+
def ==(other)
|
35
|
+
eql?(other)
|
36
|
+
end
|
37
|
+
|
38
|
+
def eql?(other)
|
39
|
+
self.class == other.class && values == other.values
|
40
|
+
end
|
41
|
+
|
42
|
+
def values
|
43
|
+
self.class::VALUE_ATTRS.map { |field| send(field) }
|
44
|
+
end
|
45
|
+
|
46
|
+
class_eval &block if block_given?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/tamiyo.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tamiyo/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'tamiyo'
|
8
|
+
spec.version = Tamiyo::VERSION
|
9
|
+
spec.authors = ['Roger Norling']
|
10
|
+
spec.email = ['roger.norling.se@gmail.com']
|
11
|
+
spec.summary = %q{Import/manage Cockatrice's card database}
|
12
|
+
spec.description = %q{A utility that import cards from Wizard's Gatherer. Allows for incremental imports and selective export to Cockatrice's card database format.}
|
13
|
+
spec.homepage = 'https://github.com/rogernorling/tamiyo#readme'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tamiyo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Roger Norling
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: A utility that import cards from Wizard's Gatherer. Allows for incremental
|
42
|
+
imports and selective export to Cockatrice's card database format.
|
43
|
+
email:
|
44
|
+
- roger.norling.se@gmail.com
|
45
|
+
executables:
|
46
|
+
- tamiyo
|
47
|
+
extensions: []
|
48
|
+
extra_rdoc_files: []
|
49
|
+
files:
|
50
|
+
- ".gitignore"
|
51
|
+
- Gemfile
|
52
|
+
- LICENCE.txt
|
53
|
+
- Rakefile
|
54
|
+
- Readme.md
|
55
|
+
- bin/tamiyo
|
56
|
+
- lib/tamiyo.rb
|
57
|
+
- lib/tamiyo/card.rb
|
58
|
+
- lib/tamiyo/dummy_scrape.rb
|
59
|
+
- lib/tamiyo/dummy_scrape_chain.rb
|
60
|
+
- lib/tamiyo/gatherer_scrape.rb
|
61
|
+
- lib/tamiyo/gatherer_scrape_chain.rb
|
62
|
+
- lib/tamiyo/process.rb
|
63
|
+
- lib/tamiyo/puts_sink.rb
|
64
|
+
- lib/tamiyo/scrape_parser.rb
|
65
|
+
- lib/tamiyo/value.rb
|
66
|
+
- lib/tamiyo/version.rb
|
67
|
+
- tamiyo.gemspec
|
68
|
+
homepage: https://github.com/rogernorling/tamiyo#readme
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 2.1.11
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: Import/manage Cockatrice's card database
|
92
|
+
test_files: []
|