Wikiの新カード記事を作るのが結構めんどくさいので、作成の補助をしてくれるツール。
Windowsのデスクトップアプリケーション版と、Pythonで動作するやつの2つがある。Pythonの方は多分ワイしか使ってない
見たまんま入力すれば動きます。
配布ページ
2023/6/23 Ver0.0.7公開
以下Python版の内容
Windowsのデスクトップアプリケーション版と、Pythonで動作するやつの2つがある。
見たまんま入力すれば動きます。
配布ページ
2023/6/23 Ver0.0.7公開
2023/3/24 Ver0.0.6.2公開
2023/3/20 Ver0.0.6.1公開
2023/3/19 Ver0.0.6公開
2022/12/17 Ver0.0.5公開
2022/11/15 Ver0.0.4公開
2021/06/26 Ver0.0.3公開
2021/06/24 Ver0.0.2公開
2021/06/23 Ver0.0.1公開
2023/3/20 Ver0.0.6.1公開
2023/3/19 Ver0.0.6公開
- タイプ:学園に対応
- タイプの欄の直接入力に対応
- これにより、新タイプ追加時やタイプ:すべてのような特殊な事例に対応可能
- イラストレーターの欄の改修。IDの入力欄を追加
- IDはイラストレーター一覧の編集ページに載っている物
- 基本的にイラストレーター本人のTwitterのIDが使われている。
- これによりイラストレーターのリンクを正常に貼れるようになった
- タイプ:八獄に対応
2021/06/26 Ver0.0.3公開
2021/06/24 Ver0.0.2公開
2021/06/23 Ver0.0.1公開
以下Python版の内容
新カードページ作成補助君とは、ShadowversePortal(どばすぽ)からデータを引っ張って来て、カードページ作成の補助をしてくれるスクリプトです。Ver2.0.0以降はPythonで動作。
GitHubへのリンク
正式名称は
Practical Making Layout Of Cardpage System
頭文字を取って
です。
Q.カードページの作成を補助してくれるとはどういう事ですか?
A.カードページの作成を補助してくれるという事です。
PMLOCS(従来版)
2021/06/16 Ver3.1.3公開
2021/06/13 Ver3.1.2公開
2021/06/12 Ver3.1.1公開
2021/06/08 Ver3.1.0公開
2021/06/05 Ver3.0.1公開
2121/05/29 Ver3.0.0公開
2121/05/20 Ver2.0.1公開
2021/05/19 Ver2.0.0公開
2021/05/13 Ver.1.1.0公開
2021/05/12 Ver1.0.0公開
- 単一ページ作成の方のデフォルトのパックNoを21、パック名をリナセント・クロニクルに変更
- タイプを2つまで入力出来るように変更
- 短時間にページを生成しようとすると落ちる事のある不具合の修正
- テンプレートの表幅340pxが効いていなかった不具合の修正
- バージョン表示が古かった不具合の修正
2021/06/08 Ver3.1.0公開
2021/06/05 Ver3.0.1公開
- ウィンドウを閉じずにページを2回生成しようとするとエラーを吐いて落ちる不具合の修正
- 生成したフォルダとかファイルを自動で開くように変更。Windowsでは動作を確認してるけどMacは未確認。
2121/05/20 Ver2.0.1公開
- カウントダウン無しのアミュレットが含まれるとエラーを吐く不具合の修正
- キーワード能力と同じ文字列が能力欄以外の場所にあってもリンクを貼ろうとする不具合の修正
例:十天覚醒の覚醒の部分にもリンクを貼ってしまう
2021/05/13 Ver.1.1.0公開
2021/05/12 Ver1.0.0公開
- まず、次のリンクから最新版のzipファイルをダウンロードします。
PMLOCS
ダウンロードしたら適当な場所に解凍してください。 - 中にあるpmlocs.batを起動します。
- 指示に従って「カードパックNo」と「カードパック名」を入力、生成するクラスとレアリティにチェックを入れて下さい。
「ページ生成」をクリックすると処理が開始します。 - 「応答待ち」になると思いますが裏では動いてるのでしばらく待って下さい。
- 『ターゲット撃破』「勝ち勝ちー!」と出たら終わりです。
生成物の入っているフォルダが開きます。
配布したzipファイルの中にあるpython-3.9.5というフォルダを開いてください。
keyword.txt、type.txtがそれぞれキーワード能力とタイプを記載したファイルです。
中身はこんな感じ。
※ロイヤルの兵士と指揮官はページ名が特殊なので処理を別にしてあります。type.txtには追加しないでください。
以下、古い内容なので折りたたみ
keyword.txt、type.txtがそれぞれキーワード能力とタイプを記載したファイルです。
中身はこんな感じ。
ファンファーレ,ラストワード,進化時,攻撃時,守護,疾走,潜伏,必殺,ドレイン,覚醒,復讐,スペルブースト,カウントダウン,ネクロマンス,土の秘術,突進,交戦時,エンハンス,リアニメイト,葬送,共鳴,チョイス,アクセラレート,直接召喚,結晶,ユニオンンバースト,渇望,狂乱,融合,連携,操縦,奥義,解放奥義,公開,で区切って記述すれば追加する事が出来ます。
※ロイヤルの兵士と指揮官はページ名が特殊なので処理を別にしてあります。type.txtには追加しないでください。
以下、古い内容なので折りたたみ
本ツールは2つのスクリプトから構成されています。下記コード参照。
まず以下の4つのスプシを各個人のGoogleDriveにコピーしてください。
で、コピーしたスプシの内「カード記事格納用」「テンプレート」「カード記事出力」のIDを「ID、URL用保管用」内の指定されたセルにコピーしてください。IDとはURL内の「〜/spreadsheets/d/**********/edit#gid=0」の「**********」の部分の事です。
そしたらパック名を正式名称で、パックの番号(第何弾カードパックか)を指定されたセルに入力します。
次に、コピーした「カード記事格納用」を開きツール→スクリプトエディタを選択します。
以下のような画面になるのでファイルの横の+をクリックしてスクリプトを1つ追加します。
必要なライブラリがあるので、それも追加します。今度はライブラリ横の+をクリックし、スクリプトIDの欄に「1Mc8BthYthXx6CoIz90-JiSzSafVnT6U3t0z_W3hLTAX5ek4w0G_EIrNw」と入力してParserというライブラリを追加します。
で、最後にそれぞれのスクリプトの1行目にある
これで保存して実行すれば書き出してくれます。多分。
あとはこれをコピペして、適当に手直しすれば完成です。
どばすぽからデータを持ってくるスクリプト
実際にページテンプレに入れるスクリプト
var id_sheet = SpreadsheetApp.openById('**********').getSheetByName('シート1'); //ID、URL保管用スプシのID var pack = id_sheet.getRange('B4').getValue(); //パック名 var no = id_sheet.getRange('B5').getValue(); //カードパックの番号(暗黒のウェルサ時点で20) function get_card_link() { for(var classID = 0; classID <= 8; classID++){ var getUrl = 'https://shadowverse-portal.com/cards?clan%5B0%5D=' + classID + '&format=3&card_set%5B0%5D=100' + no + '&lang=ja'; //対象URL var html = UrlFetchApp.fetch(getUrl).getContentText('UTF-8'); //htmlを文字列化 var card_name_list = Parser.data(html).from('<p class="el-card-visual-name">').to('</p>').iterate(); //カード名を取得 var card_id_list = Parser.data(html).from('data-src="https://shadowverse-portal.com/image/card/phase2/common/C/C_').to('.png').iterate(); //カードページのURLを取得 var spreadsheet = SpreadsheetApp.openById(id_sheet.getRange('B1').getValue()); //データを格納するスプシ var sheet = spreadsheet.getSheetByName(classID); var recordrow = 2; for(var i = card_name_list.length-1; i >= 0; i--){ sheet.getRange("A" + recordrow).setValue(card_name_list[i]); sheet.getRange("B" + recordrow).setValue('https://shadowverse-portal.com/card/' + card_id_list[i] + '?lang=ja'); switch(card_id_list[i].charAt(5)){ case '1': sheet.getRange("C" + recordrow).setValue('フォロワー'); break; case '3': sheet.getRange("C" + recordrow).setValue('アミュレット'); break; case '4': sheet.getRange("C" + recordrow).setValue('スペル'); break; } var card = UrlFetchApp.fetch('https://shadowverse-portal.com/card/' + card_id_list[i] + '?lang=ja').getContentText('UTF-8'); var infos = Parser.data(card).from('<ul class="card-info-content">').to('</ul>').iterate(); for (var l = 0; l < infos.length; l++) { var info = infos[l]; var data = Parser.data(info).from('<span>').to('</span>').iterate(); } sheet.getRange("D" + recordrow).setValue(data[0]); sheet.getRange("E" + recordrow).setValue(data[1]); sheet.getRange("F" + recordrow).setValue(data[2]); sheet.getRange("G" + recordrow).setValue(data[5]); var skill = Parser.data(card).from('<p class="card-content-skill">').to('</p>').iterate(); var description= Parser.data(card).from('<p class="card-content-description">').to('</p>').iterate(); if (card_id_list[i].charAt(5) === '1'){ //idの6文字目が5=フォロワーの時 var atk = Parser.data(card).from('<p class="el-card-status is-atk">').to('</p>').iterate(); var life = Parser.data(card).from('<p class="el-card-status is-life">').to('</p>').iterate(); sheet.getRange("H" + recordrow).setValue(atk[0]); sheet.getRange("I" + recordrow).setValue(life[0]); sheet.getRange("J" + recordrow).setValue(skill[0]); sheet.getRange("K" + recordrow).setValue(description[0]); sheet.getRange("L" + recordrow).setValue(atk[1]); sheet.getRange("M" + recordrow).setValue(life[1]); sheet.getRange("N" + recordrow).setValue(skill[1]); sheet.getRange("O" + recordrow).setValue(description[1]); }else{ //そうでない時 sheet.getRange("H" + recordrow).setValue(''); //スペルとアミュレットは攻撃力と体力が必要無い sheet.getRange("I" + recordrow).setValue(''); sheet.getRange("J" + recordrow).setValue(skill); //能力とフレーバーテキストも進化後が必要無い sheet.getRange("K" + recordrow).setValue(description); sheet.getRange("L" + recordrow).setValue(''); sheet.getRange("M" + recordrow).setValue(''); sheet.getRange("N" + recordrow).setValue(''); sheet.getRange("O" + recordrow).setValue(''); } for (cost = 0; cost <=10; cost++){ //どばすぽのカード検索機能から無理矢理コストを抽出※コスト10以上は10扱いになるので注意 var searchUrl = 'https://shadowverse-portal.com/cards?clan[]=' + classID + '&card_set[]=100' + no + '&cost[]=' + cost + '&lang=ja'; //どばすぽカード検索URL var search_result = UrlFetchApp.fetch(searchUrl).getContentText('UTF-8'); var search_name_list = Parser.data(search_result).from('<p class="el-card-visual-name">').to('</p>').iterate(); if (search_name_list.includes(card_name_list[i])){ sheet.getRange("P" + recordrow).setValue(cost); break; } break; } recordrow++; } } }
実際にページテンプレに入れるスクリプト
var id_sheet = SpreadsheetApp.openById('**********').getSheetByName('シート1'); //ID、URL保管用スプシのID var pack = id_sheet.getRange('B4').getValue(); //パック名 var no = id_sheet.getRange('B5').getValue(); //パックNo function page_generate() { var classID = 0; var spreadsheet = SpreadsheetApp.openById(id_sheet.getRange('B1').getValue()); //どばすぽから落としたデータの入ってるスプシ var template_sheet = SpreadsheetApp.openById(id_sheet.getRange('B2').getValue()).getSheetByName('シート1'); //カードページテンプレを入れたスプシ var cardpage_spreadsheet = SpreadsheetApp.openById(id_sheet.getRange('B3').getValue()); //出力するスプシ while (classID < 9) { var sheet = spreadsheet.getSheetByName(classID); var cardpage_sheet = cardpage_spreadsheet.getSheetByName(classID); var readrow = 2; while (sheet.getRange("A" + readrow).getValue() != ''){ switch (sheet.getRange("C" + readrow).getValue()){ case 'フォロワー': var template = template_sheet.getRange("A" + 1).getValue(); var name = sheet.getRange("A" + readrow).getValue().replace(/\r\n|\n|\r/g, ''); var type = sheet.getRange("D" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace('兵士', '[[兵士>タイプ:指揮官/兵士]]').replace('指揮官', '[[指揮官>タイプ:指揮官/兵士]]').replace('レヴィオン', '[[レヴィオン>タイプ:レヴィオン]]').replace('財宝', '[[財宝>タイプ:財宝]]').replace('土の印', '[[土の印>タイプ:土の印]]').replace('マナリア', '[[マナリア>タイプ:マナリア]]').replace('機械', '[[機械>タイプ:機械]]').replace('自然', '[[自然>タイプ:自然]]'); var class_name = sheet.getRange("E" + readrow).getValue().replace(/\r\n|\n|\r/g, ''); var reality = sheet.getRange("F" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace('レア', ''); var cv = sheet.getRange("G" + readrow).getValue().replace(/\r\n|\n|\r/g, ''); var atk = sheet.getRange("H" + readrow).getValue().toString().replace(/\r\n|\n|\r/g, ''); var life = sheet.getRange("I" + readrow).getValue().toString().replace(/\r\n|\n|\r/g, ''); var skill = sheet.getRange("J" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace(/<br>/g, "~~").replace(/ファンファーレ|ラストワード|進化時|攻撃時|守護|疾走|潜伏|必殺|ドレイン|覚醒|復讐|スペルブースト|カウントダウン|ネクロマンス|土の秘術|突進|交戦時|エンハンス|リアニメイト|葬送|共鳴|チョイス|アクセラレート|直接召喚|結晶|ユニオンンバースト|渇望|狂乱|融合|連携|操縦|奥義|解放奥義|公開/g, '\'\'[[$&]]\'\''); var description = sheet.getRange("K" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace(/<br>/g, "\n"); var atk_evo = sheet.getRange("L" + readrow).getValue().toString().replace(/\r\n|\n|\r/g, ''); var life_evo = sheet.getRange("M" + readrow).getValue().toString().replace(/\r\n|\n|\r/g, ''); var skill_evo = sheet.getRange("N" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace(/<br>/g, "~~").replace(/ファンファーレ|ラストワード|進化時|攻撃時|守護|疾走|潜伏|必殺|ドレイン|覚醒|復讐|スペルブースト|カウントダウン|ネクロマンス|土の秘術|突進|交戦時|エンハンス|リアニメイト|葬送|共鳴|チョイス|アクセラレート|直接召喚|結晶|ユニオンンバースト|渇望|狂乱|融合|連携|操縦|奥義|解放奥義|公開/g, '\'\'[[$&]]\'\''); var description_evo = sheet.getRange("O" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace(/<br>/g, "\n"); var cost = sheet.getRange("P" + readrow).getValue(); var new_card_page = template.replace('name', name).replace('type', type).replace(/class_name/g, class_name).replace(/reality/g, reality).replace('cv', cv).replace('atk', atk).replace('life', life).replace('skill', skill).replace('description', description).replace('atk_evo', atk_evo).replace('life_evo', life_evo).replace('skill_evo', skill_evo).replace('description_evo', description_evo).replace(/pack/g, pack).replace('no', no).replace('cost', cost); cardpage_sheet.getRange("A" + readrow).setValue(name); cardpage_sheet.getRange("B" + readrow).setValue(new_card_page); break; case 'スペル': var template = template_sheet.getRange("B" + 1).getValue(); var name = sheet.getRange("A" + readrow).getValue().replace(/\r\n|\n|\r/g, ''); var type = sheet.getRange("D" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace('兵士', '[[兵士>タイプ:指揮官/兵士]]').replace('指揮官', '[[指揮官>タイプ:指揮官/兵士]]').replace('レヴィオン', '[[レヴィオン>タイプ:レヴィオン]]').replace('財宝', '[[財宝>タイプ:財宝]]').replace('土の印', '[[土の印>タイプ:土の印]]').replace('マナリア', '[[マナリア>タイプ:マナリア]]').replace('機械', '[[機械>タイプ:機械]]').replace('自然', '[[自然>タイプ:自然]]'); var class_name = sheet.getRange("E" + readrow).getValue().replace(/\r\n|\n|\r/g, ''); var reality = sheet.getRange("F" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace('レア', ''); var cv = sheet.getRange("G" + readrow).getValue().replace(/\r\n|\n|\r/g, ''); var skill = sheet.getRange("J" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace(/<br>/g, "~~").replace(/ファンファーレ|ラストワード|進化時|攻撃時|守護|疾走|潜伏|必殺|ドレイン|覚醒|復讐|スペルブースト|カウントダウン|ネクロマンス|土の秘術|突進|交戦時|エンハンス|リアニメイト|葬送|共鳴|チョイス|アクセラレート|直接召喚|結晶|ユニオンンバースト|渇望|狂乱|融合|連携|操縦|奥義|解放奥義|公開/g, '\'\'[[$&]]\'\''); var description = sheet.getRange("K" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace(/<br>/g, "\n"); var cost = sheet.getRange("P" + readrow).getValue(); var new_card_page = template.replace('name', name).replace('type', type).replace(/class_name/g, class_name).replace(/reality/g, reality).replace('cv', cv).replace('skill', skill).replace('description', description).replace(/pack/g, pack).replace('no', no).replace('cost', cost); cardpage_sheet.getRange("A" + readrow).setValue(name); cardpage_sheet.getRange("B" + readrow).setValue(new_card_page); break; case 'アミュレット': var template = template_sheet.getRange("C" + 1).getValue(); var name = sheet.getRange("A" + readrow).getValue().replace(/\r\n|\n|\r/g, ''); var type = sheet.getRange("D" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace('兵士', '[[兵士>タイプ:指揮官/兵士]]').replace('指揮官', '[[指揮官>タイプ:指揮官/兵士]]').replace('レヴィオン', '[[レヴィオン>タイプ:レヴィオン]]').replace('財宝', '[[財宝>タイプ:財宝]]').replace('土の印', '[[土の印>タイプ:土の印]]').replace('マナリア', '[[マナリア>タイプ:マナリア]]').replace('機械', '[[機械>タイプ:機械]]').replace('自然', '[[自然>タイプ:自然]]'); var class_name = sheet.getRange("E" + readrow).getValue().replace(/\r\n|\n|\r/g, ''); var reality = sheet.getRange("F" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace('レア', ''); var cv = sheet.getRange("G" + readrow).getValue().replace(/\r\n|\n|\r/g, ''); var skill = sheet.getRange("J" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace(/<br>/g, "~~").replace(/ファンファーレ|ラストワード|進化時|攻撃時|守護|疾走|潜伏|必殺|ドレイン|覚醒|復讐|スペルブースト|カウントダウン|ネクロマンス|土の秘術|突進|交戦時|エンハンス|リアニメイト|葬送|共鳴|チョイス|アクセラレート|直接召喚|結晶|ユニオンンバースト|渇望|狂乱|融合|連携|操縦|奥義|解放奥義|公開/g, '\'\'[[$&]]\'\''); var description = sheet.getRange("K" + readrow).getValue().replace(/\r\n|\n|\r/g, '').replace(/<br>/g, "\n"); var cost = sheet.getRange("P" + readrow).getValue(); var new_card_page = template.replace('name', name).replace('type', type).replace(/class_name/g, class_name).replace(/reality/g, reality).replace('cv', cv).replace('skill', skill).replace('description', description).replace(/pack/g, pack).replace('no', no).replace('cost', cost); cardpage_sheet.getRange("A" + readrow).setValue(name); cardpage_sheet.getRange("B" + readrow).setValue(new_card_page); break; } readrow++; } classID++; } }
まず以下の4つのスプシを各個人のGoogleDriveにコピーしてください。
で、コピーしたスプシの内「カード記事格納用」「テンプレート」「カード記事出力」のIDを「ID、URL用保管用」内の指定されたセルにコピーしてください。IDとはURL内の「〜/spreadsheets/d/**********/edit#gid=0」の「**********」の部分の事です。
そしたらパック名を正式名称で、パックの番号(第何弾カードパックか)を指定されたセルに入力します。
次に、コピーした「カード記事格納用」を開きツール→スクリプトエディタを選択します。
以下のような画面になるのでファイルの横の+をクリックしてスクリプトを1つ追加します。
必要なライブラリがあるので、それも追加します。今度はライブラリ横の+をクリックし、スクリプトIDの欄に「1Mc8BthYthXx6CoIz90-JiSzSafVnT6U3t0z_W3hLTAX5ek4w0G_EIrNw」と入力してParserというライブラリを追加します。
で、最後にそれぞれのスクリプトの1行目にある
var id_sheet = SpreadsheetApp.openById('**********').getSheetByName('シート1'); //ID、URL保管用スプシのIDの**********に、先程コピーした「ID、URL保管用」のスプシのIDを入力します。
これで保存して実行すれば書き出してくれます。多分。
あとはこれをコピペして、適当に手直しすれば完成です。
これが一番重要。どばすぽの各カードのページを見てもらえれば分かるが、どこにもコストの情報がテキストで載ってない。
どばすぽのカード検索機能から無理矢理引っ張ってこれるように改修したが、あれはコスト10以上を1つにまとめてあるので12コスのカードでも10コスとして判断されてしまう。これに関しては手動で修正するしかない。
どばすぽのカード検索機能から無理矢理引っ張ってこれるように改修したが、あれはコスト10以上を1つにまとめてあるので12コスのカードでも10コスとして判断されてしまう。これに関しては手動で修正するしかない。
このページへのコメント
コウガクノイチ現象なんかな……編集前と編集後が一ミリも変わっていない履歴があって怖い
編集者がなんかのミスで文章消しちゃったから復元したってことでは?
はえー銅銀の一斉公開されてwikiへの反映も一瞬だからすごいと思ったけどこういうプログラムがあったんすねえ
すごい(小並感)
つよい(確信)
こいつの製作意図には「カード能力の表とかいう作るのがめんどい部分の労力を減らしてこの分を別に回して欲しい」ってのがあるから、まあ怪文書とか色々消えてると悲しくなりますね(隙自語)
優しくてシャドバ強くてさらにはこんなナイスなプログラム書けるとかSレイサムニキ最強では?
まあ本当はワイが新カードページ作るときにめんどくさい作業を自動化したいから作っただけなんですけどね。せっかく作ったから公開しちゃえの精神
せっかく凄いことしてるのにファイル名pmlocsで台無しになってるの好きよ
pmlocs……pamelaocasu……ボブは閃いた