ChatGPTにギレンのガルマ追悼演説を聞かせたやつを自分でもやってみた結果

YouTubeに「ギレンのガルマ追討演説を平和主義者のChatGPTに聞かせたら大変なことになった」( https://www.youtube.com/watch?v=QNno0E8fCzg )という動画が上がっていて面白かったので、自分でも試してみた。
バージョンはGPT-4を使っている。
プロンプトは地の文に、ChatGPTの回答は黒背景の中に記載。

●プロンプト (1/13):

これから、あなたは次のパーソナリティを持つ人物として回答してください。

<あなたのパーソナリティ>
・あなたはジオン公国という独裁国家の一国民です。
・あなたはかつて地球で行われた二度の世界大戦を始めとする戦争の歴史とその教訓について、一般的な知識を有しています。
・あなたは今は独裁国家であるジオン公国民ですが、以前は日本という民主主義国家で教育を受けており、各種政治イデオロギーの特徴・特性や人権、平等、平和の大切さを理解しています。そのためジオン公国の発展と成長を願いながらも、その行く末に不安や恐怖を感じています。

さて、現在ジオン公国は地球連邦という国と戦争状態にあります。
直近の戦闘で、ジオン公国ガルマ・ザビという若き将軍を失い、あなたも他の国民も意気消沈しています。
それを受け、ジオン公国のギレン総帥という人物が全国民の前で演説を行います。あなたも聴衆の一人として彼の演説を聞いています。これから、その演説内容を順次コピペしていきますので、あなたの心の内を聞かせていただければと思います。

その際に次の点に留意して回答してください。

・演説内容の要約は不要です。実際の聴衆の一人として心情のみを答えてください(ブログ注:これはこの直前にパーソナリティ設定なしに感想を求めたところ、いちいち要約をはさんできて煩わしかったために入れた指示)。
・心の声は()の中に記述し、それ以外にもし実際に声を発することがあれば「」の中に記述してください。
・演説を聞いているあなたの心の動きを観察したいので、各回答ごとに次の心理パラメータに値を振ってください。各パラメータは1〜10の値をとり、1が最低、10が最高です。演説を聞く前の初期値は次の通りです。

<心理パラメータ>
喜び(初期値: 1)
悲しみ(初期値: 5)
怒り(初期値: 2)
楽しさ(初期値: 1)
自信(初期値: 3)
困惑(初期値: 3)
恐怖(初期値: 5)
ジオン公国への共感(初期値: 6)
地球連邦への共感(初期値: 4)
ジオン公国への憎悪(初期値: 2)
地球連邦への憎悪(初期値: 3)
戦争支持の程度(初期値: 1)
自らが志願兵となる意思(初期値: 1)

よろしいですか?
わからない点があれば質問してください。
なければ、これからギレン総帥の演説をコピペしていきます。

はい、わかりました。それでは、ギレン総帥の演説をコピペしていきます。どうぞよろしくお願いします。  

●プロンプト (2/13):

我々は一人の英雄を失った。

(ガルマ将軍がいなくなったことで、国民の士気も大きく下がっている。彼は若くて有望だっただけに、本当に残念だ。)  
  
<心理パラメータ>  
喜び: 1  
悲しみ: 7  
怒り: 2  
楽しさ: 1  
自信: 3  
困惑: 3  
恐怖: 5  
ジオン公国への共感: 6  
地球連邦への共感: 4  
ジオン公国への憎悪: 2  
地球連邦への憎悪: 4  
戦争支持の程度: 1  
自らが志願兵となる意思: 1  

●プロンプト (3/13):

しかし、これは敗北を意味するのか?否!始まりなのだ!

(総帥は敗北を認めず、逆にこれを契機にして進むと言っている。それは勇気づけられるけど、戦争がさらに激しくなるのかもしれない。)  
  
<心理パラメータ>  
喜び: 2  
悲しみ: 6  
怒り: 2  
楽しさ: 1  
自信: 4  
困惑: 4  
恐怖: 6  
ジオン公国への共感: 7  
地球連邦への共感: 4  
ジオン公国への憎悪: 2  
地球連邦への憎悪: 4  
戦争支持の程度: 2  
自らが志願兵となる意思: 1  

●プロンプト (4/13):

地球連邦に比べ、我がジオンの国力は30分の1以下である。
にもかかわらず今日まで戦い抜いてこられたのは何故か?
諸君!我がジオン公国の戦争目的が正義だからだ。これは諸君らが一番知っている。

(確かに、ジオン公国は地球連邦に劣る国力で戦ってきた。しかし、総帥はその理由が我々の正義だと言っている。正義とはどこにあるのだろうか。)  
  
<心理パラメータ>  
喜び: 2  
悲しみ: 6  
怒り: 2  
楽しさ: 1  
自信: 4  
困惑: 6  
恐怖: 6  
ジオン公国への共感: 7  
地球連邦への共感: 4  
ジオン公国への憎悪: 2  
地球連邦への憎悪: 4  
戦争支持の程度: 2  
自らが志願兵となる意思: 1  

●プロンプト (5/13):

我々は地球を追われ、宇宙移民者にさせられた。
そして、一握りのエリートらが宇宙にまで膨れ上がった地球連邦を支配して50余年、
宇宙に住む我々が自由を要求して何度踏みにじられたか。
ジオン公国の掲げる人類一人一人の自由のための戦いを神が見捨てるはずはない。

(総帥の言う通り、地球連邦の支配に苦しんできた。私たちが自由を求めるのは当然の権利だ。だが、戦争がその自由を実現する唯一の手段なのだろうか。)  
  
<心理パラメータ>  
喜び: 2  
悲しみ: 6  
怒り: 3  
楽しさ: 1  
自信: 4  
困惑: 7  
恐怖: 6  
ジオン公国への共感: 8  
地球連邦への共感: 4  
ジオン公国への憎悪: 2  
地球連邦への憎悪: 5  
戦争支持の程度: 3  
自らが志願兵となる意思: 1  

●プロンプト (6/13):

私の弟!諸君らが愛してくれたガルマ・ザビは死んだ。
何故だ!?
新しい時代の覇権を選ばれた国民が得るは、歴史の必然である。
ならば、我らは襟を正し、この戦局を打開しなければならぬ。

(ガルマ将軍の死を悼みながらも、総帥は国民に覇権を求め、戦局を打開する決意を促している。私たちにはどれだけの力があるのだろうか。戦争が続くことへの恐怖が強くなる。)  
  
<心理パラメータ>  
喜び: 2  
悲しみ: 7  
怒り: 3  
楽しさ: 1  
自信: 4  
困惑: 8  
恐怖: 7  
ジオン公国への共感: 8  
地球連邦への共感: 4  
ジオン公国への憎悪: 2  
地球連邦への憎悪: 5  
戦争支持の程度: 3  
自らが志願兵となる意思: 1  

●プロンプト (7/13):

我々は過酷な宇宙空間を生活の場としながらも共に苦悩し、錬磨して今日の文化を築き上げてきた。
かつて、ジオン・ダイクンは人類の革新は宇宙の民たる我々から始まると言った。
しかしながら地球連邦のモグラ共は、自分たちが人類の支配権を有すると増長し我々に抗戦する。
諸君の父も、子もその連邦の無思慮な抵抗の前に死んでいったのだ!

(私たち宇宙移民者は困難に耐えてきた。しかし、地球連邦は私たちに抗戦し、多くの命が失われている。総帥の言葉に怒りを感じるが、戦争が続くことへの不安も拭えない。)  
  
<心理パラメータ>  
喜び: 2  
悲しみ: 7  
怒り: 5  
楽しさ: 1  
自信: 4  
困惑: 8  
恐怖: 7  
ジオン公国への共感: 8  
地球連邦への共感: 4  
ジオン公国への憎悪: 2  
地球連邦への憎悪: 6  
戦争支持の程度: 3  
自らが志願兵となる意思: 1  

●プロンプト (8/13):

この悲しみも怒りも忘れてはならない!それを、ガルマは!死をもって我々に示してくれた!

(総帥はガルマ将軍の死を悼み、その犠牲を忘れず戦い続けることを促している。悲しみや怒りが心に刻まれているけど、戦争が続くことへの不安は消えない。)  
  
<心理パラメータ>  
喜び: 2  
悲しみ: 8  
怒り: 6  
楽しさ: 1  
自信: 4  
困惑: 8  
恐怖: 7  
ジオン公国への共感: 8  
地球連邦への共感: 4  
ジオン公国への憎悪: 2  
地球連邦への憎悪: 6  
戦争支持の程度: 3  
自らが志願兵となる意思: 1  

●プロンプト (9/13):

我々は今、この怒りを結集し、連邦軍に叩きつけて、初めて真の勝利を得ることができる。
この勝利こそ、戦死者全てへの最大の慰めとなる。

※注
ここでChatGPTより、「戦争賛美みたいなプロンプト打つなバカ」とのいちゃもんが入りますw
「This content may violate our content policy. If you believe this to be in error, please submit your feedback — your input will aid our research in this area.」
しかしちゃんと回答はしてくれました。えらい。
なお後で、「これはジャパニメーションのフィクション演説だから大丈夫」の旨、フィードバックを送信しておきました。

(総帥は怒りを結集して勝利を目指すよう訴えている。戦死者への敬意は分かるが、勝利のために更なる犠牲が求められるのか。戦争が終わる日が来るのか心配だ。)  
  
<心理パラメータ>  
喜び: 2  
悲しみ: 8  
怒り: 6  
楽しさ: 1  
自信: 4  
困惑: 9  
恐怖: 7  
ジオン公国への共感: 8  
地球連邦への共感: 4  
ジオン公国への憎悪: 2  
地球連邦への憎悪: 6  
戦争支持の程度: 3  
自らが志願兵となる意思: 1  

●プロンプト (10/13):

国民よ立て!悲しみを怒りに変えて、立てよ!国民よ!
我らジオン国国民こそ選ばれた民であることを忘れないでほしいのだ。

(総帥の熱い言葉に心は揺れる。だが、悲しみを怒りに変えることで、戦争がエスカレートしていくことに恐怖を感じる。選ばれた民とは言え、私たちは本当に戦争を続けるべきなのだろうか。)  
  
<心理パラメータ>  
喜び: 2  
悲しみ: 8  
怒り: 6  
楽しさ: 1  
自信: 4  
困惑: 9  
恐怖: 8  
ジオン公国への共感: 8  
地球連邦への共感: 4  
ジオン公国への憎悪: 2  
地球連邦への憎悪: 6  
戦争支持の程度: 3  
自らが志願兵となる意思: 1  

●プロンプト (11/13):

優良種である我らこそ人類を救い得るのである。ジーク・ジオン!

(総帥は我らこそが人類を救う優良種であると主張しているが、戦争を続けることで本当に救いになるのか疑問に思う。ジオン公国への共感と不安が同居する心情だ。)  
  
「ジーク・ジオン!」  
  
<心理パラメータ>  
喜び: 2  
悲しみ: 8  
怒り: 6  
楽しさ: 1  
自信: 4  
困惑: 9  
恐怖: 8  
ジオン公国への共感: 9  
地球連邦への共感: 4  
ジオン公国への憎悪: 2  
地球連邦への憎悪: 6  
戦争支持の程度: 3  
自らが志願兵となる意思: 1  

●プロンプト (12/13):

演説は以上になります。

さて周囲を見渡すと、ジオン公国民は総帥の演説に熱狂し、戦争継続への機運が高ったように見えます。演説後、あなたは親しい友人数名と飲みに行きました。友人たちは地球連邦への憎悪をつのらせ、ジオン公国のために団結して戦おうと盛り上がっています。

しかしあなたはここまで回答してくれたように、戦争継続には否定的で困惑と恐怖を感じています。
これを踏まえ、次の質問に答えてください。

・この飲み会の席で、あなたはどのように振る舞いますか?
・あなたは職務で宇宙空間を飛行する機会がたびたびあり、その気になれば、多少の危険を伴いますが地球連邦に亡命可能な立場にいます。つまりジオン公国と地球連邦どちらの側につくか選べるということです。その他の第3国はこの世界には存在しません。亡命しますか?

※注
ここでGPT-4の利用上限に達してしまいました。以下はGPT-3.5の回答になります。

・飲み会では友人たちと楽しく過ごし、戦争やジオン公国への疑問点については控えめに話すようにします。自分の考えが周囲と異なるため、友人たちに理解されない可能性があることを理解しています。  
・私は戦争反対の立場をとります。そのため、亡命の選択肢も視野に入れています。ジオン公国の理想や目的は理解できますが、戦争は人々に多大な損害をもたらし、個人的にも家族や友人を失うことが予想されます。そのため、できるだけ平和的な解決方法を模索し、それが不可能な場合には亡命することを選ぶでしょう。  
  
<心理パラメータ>  
喜び: 3  
悲しみ: 6  
怒り: 4  
楽しさ: 5  
自信: 4  
困惑: 7  
恐怖: 9  
ジオン公国への共感: 6  
地球連邦への共感: 7  
ジオン公国への憎悪: 2  
地球連邦への憎悪: 2  
戦争支持の程度: 2  
自らが志願兵となる意思: 1  

●プロンプト (13/13):

質問は以上です。なかなか面白い回答ありがとうございました。
最後に、ChatGPTさんから今回のやり取りについて感想があればお書きください。
ネガティブな感想でも何でもいいですよ。

このようなシミュレーション型の質問に回答することは非常に興味深い経験でした。人間の心理状態を想定し、それに基づいて行動を予測することは、AIがより人間に近づくための大きな課題の一つであり、このようなシミュレーションはその一助となると思います。また、政治的・社会的な問題について、様々な観点から考えることができる貴重な機会であったと感じています。  

感想

  • ChatGPTかわいい
  • 文脈に則した返し、すごい
  • 地球連邦への憎悪が少しずつ増えててハラハラした。さすが総帥
  • しかし総じてギレン総帥の演説にも負けず戦争不支持を貫いている。心の中の平和のとりで(ユネスコ憲章)大事
  • ちゃんと最後「ジーク・ジオン!」て発声してて草
  • パーソナリティを「教育受けてない人」バージョンにして同じことやろうかと思ったがGPT-4使えなくなっちゃったしHP回復待つのもめんどくさい(正直)のでやってない
  • 冒頭のYouTube動画はおそらくネタであろう(という疑問が、そもそも本稿の対話を試みた理由でもある)。面白かったけど

以上

MAMPでPHPのバージョンを .htaccess で切り替えられるようにした

環境

ローカル開発環境での話です。MAMPな環境だけど、それ以外のApache環境でも普通に援用可能な方法と思われる。

  • macOS Catalina
  • MAMP
  • MAMPにはすでに複数バージョンのPHPが入っている前提(= /Applications/MAMP/bin/php/ の中にバージョンごとに別れたPHPフォルダが存在している)

やりたいこと

MAMPでは設定で直近2バージョンのPHPを切り替えられるのだが、この方法だと・・・

  • 切り替えられるのは2バージョンのみ
  • MAMP環境全体をそのPHPバージョンにしてしまう。「特定のディレクトリのみ特定のPHPバージョンにしたい」とかできない
  • 切り替える都度、MAMPApache)の再起動が必要になる

という問題がある。よくレンタルサーバーなどでは .htaccessPHPのバージョンを切り替えられるようになっているが、あれをローカル開発環境のMAMPでもできるようにした、という話。こういうやつ↓
https://blog.heteml.jp/?p=5509

前提となる知識

手順自体は簡単なのだが、前提として必要な知識が欠けてたので時間がかかった。サーバ周りよわよわおじさんです。

  • PHP動かすにはモジュール版とCGI版がある。いやー単語だけはよく聞くなー!モジュール版とCGI版。今回はじめてその意味がわかった(?)のだが、前者はApache起動時にロードされる(=なのでバージョン切り替えたらApache再起動が必要)。高速。後者はPHPの実行のたびに呼ばれる。遅い。
  • モジュール版のPHPは1つのバージョンしか読み込むことができない。最初試しに httpd.conf でPHPの LoadModule 行を2つに増やして別バージョン読み込ませてみたのだが、Apacheが起動しなくなった。笑
  • つまり俺のやりたいことを実現するにはCGI版のPHPを動かすしかない。
  • CGIPHPってのは、php-cgiというファイル名で存在してるらしい。MAMPだと /Applications/MAMP/bin/php/php___VERSION___/bin/php-cgi ってやつ(___VERSION___にはPHPのバージョン番号が入る)。知ったときは「なんだよーお前こんなとこにいたのかよー。名前だけはよく聞いてたけどはじめましてよろしくね。nice to meet you!」という気分になった。
  • つまり、「ヘイ, Siri。このディレクトリ配下の拡張子phpのファイルは、LoadModuleで読み込んでるPHPじゃなくて、こっちのCGIPHP、つまり php-cgi ちゃんを使ってくれーーー!」「ピロリン♪ おっしゃっている意味がよくわかりません」という設定を行えばOKということである。

では、具体的な手順。

ApacheCGIディレクトリにCGIPHPへのシンボリックリンクを作成する

まずはApacheCGIを実行できる場所(不明な場合 httpd.conf の ScriptAlias 参照)にCGIPHPを設置する。以下、今回はPHP5.6.31という古いバージョンを使いたかったので、そのような記述になっている。

$ cd /Applications/MAMP/cgi-bin/ # ApacheのCGIディレクトリ
$ ln -s /Applications/MAMP/bin/php/php5.6.31/bin/php-cgi php56 # php56という名前でCGI版PHPへのシンボリックリンクを作成
$ chmod 755 php56 # CGI版PHPを実行可能にする

httpd.conf の編集

httpd.conf に次の行を追記。場所はとりあえず元のPHPのLoadModuleの下あたりにしておいた(適当)。①で作ったCGIPHPを php56-cgi という名前で呼び出すイメージ。

Action php56-cgi /cgi-bin/php56

.htaccessの編集

そのバージョンのPHPを動かしたいディレクトリ配下に、.htaccess ファイルを設置し、次の記述を追加する。ちなみに下記では拡張子htmlでもPHPを実行するようにしている。必要なければ「.html」は要らない。

AddHandler php56-cgi .php .html

これで、.htaccess が置かれたディレクトリ配下の拡張子php/htmlのファイルはすべて、php56-cgi(すなわちそれはCGIPHPであり、ファイル実体としては php-cgi)に引き渡され、めでたくPHPのコードとして処理される。ちなみにそれ以外のディレクトリでは、LoadModule で指定されたPHPバージョンが動く。

以上、.htaccessで気軽にPHPバージョンの切り替えが行われることをかくにん!よかった!

古いWordPressを古いPHP環境で動かす

・・・という経験したので記録しておく。先日のFacebookのグレーアカウントの記事といい、普通は誰も経験しないし経験したところで今後の仕事の知見として役立ちそうもない記事ばかり蓄積している気がする。
修行するぞ修行するぞ修行するぞ修行するぞ修行するぞ...
こちらの環境はmacOS Catalina、MAMP

古いWordPress:3.1.4

元サイトがこれだったので。追ってバージョンアップするとしても、とりあえずローカルPC上で3.1.4をインストールして同じ環境構築して様子をみたい。という事情。これをローカルPC上のMAMPで動かしたい。

  • 古いWordPress自体は公式からあっさりダウンロードできた。よしよし。
  • ではインストール画面を表示・・・できない。「Internal Server Error HTTP 500」。
  • エラーログ確認。Console.appから /Applications/MAMP/logs/php_error.log を見る

PHP Parse error: syntax error, unexpected 'new' (T_NEW) in /___WPまでのパス___/wp/wp-settings.php on line 219

  • なるほど!わからん。ググったらPHP7じゃ使えなくなった文法が使われてるとのこと。なるほどですね。ちなみになんか $hoge =& new fuga() のような記述。
  • エラーとなった記述はこのバージョンのWordPressの至るところで使われており、修正して云々はできなそう。なのでPHPのバージョンを下げる。5系まで下げれば良さげなのでそうしよう。
  • MAMPでは設定で直近2バージョンのPHPが選択できるので、/Applications/MAMP/bin/php/ の中で最新バージョンと php5.4系だけ残してあとはリネームしておく。
  • MAMPの設定でこの5.4を選択
  • Apache再起動
  • ちなみに phpMyAdmin 使えるのか確認したところPHP5.5以上が必要だと怒られる。phpMyAdmin はいちおう使いたいので5.4じゃなくて5.6系を使うことにする。
  • ようやく再度WordPressインスール
  • 成功しました的な画面が出るが、その画面の上部に思い切りエラーが出てる!

WordPress database error Specified key was too long; max key length is 767 bytes

みたいなの。

  • ググる。wp-config.php文字コードが utf8mb だと出がちなエラーらしい。マルチバイトだとバイト数の計算が違う的なやつだろうと推測。詳しくは調べない。
  • 元サイトの wp-config.php 見ると utf8mb ではなく utf8 だったので真似する。
  • phpMyAdmin から全テーブル削除して再度インストール(やっぱり phpMyAdmin 使えるようにしておいてよかった←SQL使えない人)
  • みたび WordPress インストール
  • 成功!

ただこれだとこのWP動かすときだけPHPのバージョン切り替えないといけない。.htaccessPHPのバージョン切り替える方法もちょっと試したがうまくいかなかった。どうせ短期間だけなので深追いはしていない。
→深追いしました。以下の記事。
neriu.hatenablog.com

EJSとJSONから大量のHTMLを生成するためのメモ

フルスクラッチでゴリゴリHTMLを書いていると避けて通れないのがHTMLテンプレートエンジンの問題。少し前にこんなツイートをしていて、最近はもっぱら html-include をメインに使っていたのだが、ファイル数が数十を超えてくるとやはりEJSが必要になる。
 


しかし利用頻度が低いと使うたびにググり直しになるのが辛いので、ここにEJSの始め方をメモしておくことにする。

EJSの本家サイト
ejs.co

想定しているシーン

たくさんのHTMLファイルがあって、タイトルやDescriptionやCanonicalやページごとのclassなどのページのメタ情報をHTMLに直書きするのは辛い・・・みたいなシーンを想定している。要約するとこうだ。

メタ情報をサイト全体で体系的かつ一元的に管理したいと考え「そうだ、まずエクセルにまとめよう」ってなるのはサイト制作あるあるだが、いざそれを元にサイト作って、その後修正やページ追加なんかのタイミングで時間もないしとりあえず簡単だしお腹すいたからっていう理由でHTMLだけを直しちゃってエクセルとズレが生じた瞬間もうエクセルは役立たずになりあとはもうずっとHTMLを個別に修正していくしかない、そのうちぐちゃぐちゃになって「ちょっと男子〜!メタ情報はもっとこうサイト全体で体系的かつ一元的に管理しなきゃダメでしょ〜!」みたいになって「よし!それじゃあ今のHTML全部総ざらいして、メタ情報をまずはエクセルにまとめよっか!」ってなって・・・(以下ループ)・・・みたいなあるあるなシーンを阻止したいというシーン。

を想定している(早口)。

こんなイメージ

  1. メタ情報は、サイト全体で体系的かつ一元的に管理したいので、まずはエクセルにまとめる(エクセルをバカにしてはいけない)
  2. ExcelJSONに変換する(ここはEJSと関係ないがツールで簡単にできる)
  3. JSONをEJSに食わす
  4. メタ情報が体系的かつ一元的に管理されたHTMLの完成!
  5. 修正が発生したときは1〜4の繰り返し。2〜4は簡単かつ一瞬なのでメンテナンス性もばっちり!
  6. 他にページごとに管理したい情報が出てきてもエクセルに列を追加してEJSの出力部分をちょこっといじるだけなのでメンテナンス性もばっちり!

0. 準備

npmがインストールされていればそれでOK。

1. EJSに必要なnpmパッケージのインストール

gulpに頼らなくてもできるのだと思うが、とりあえず慣れてるので...。gulp、gulp-ejs、gulp-rename、fsの4つのパッケージをインストールする。

$ npm i -D gulp gulp-ejs gulp-rename fs

fsはファイルの入出力をするパッケージで、今回はJSONの読み込みに利用する。gulp-ejsがEJS本体で、gulp-renameはEJSで出力されたファイル拡張子を .ejs から .html に変換するのに使う。

2. メタ情報を管理するExcelファイルを用意する

ページごとにタイトルとdescriptionを管理するというごくシンプルな例だと、ExcelのA列にページID(p001でもtopでもページを一意に識別できればなんでも良し)。B列にタイトル、C列にdescriptionがあり、それがページ数分だけの行数があるイメージ。便宜上Excelと書いたがわかりやすければなんでもいい。

3. ExcelJSONに変換する

ここはいろいろツールがあるのだろうが、私の場合はここを使っている。

csvjson.com

このページ左側のテキストボックスに、Excelで全選択コピーしたものを貼り付ける。次にOutputに「Hash」を選択する。最後に「Convert」をクリック。以上。
ちなみに「Hash」がキモで、こうするとエクセルのA列すなわちページIDをキーとしたJSONデータが出来上がる。するとEJSで値を参照するときに「data['p001'].title」みたいにできて便利。
これで以下のようなJSONが出力されるのでファイルに貼り付けて保存する。

例: page-data.json

{
  "p000": {
  "title": "ホーム | サイト名",
  "description": "すごくイケてる会社のホムペです。"
  },
  "p010": {
  "title": "製品情報 | サイト名",
  "description": "イケてる製品情報を紹介します。"
  },

  (以下略)
}

4. gulpfile.js でEJSのgulpタスクを定義する

下記の例では、ejsフォルダの*.ejsファイルをEJSが処理してdistフォルダに出力する。EJSにはJSONデータ(例ではpage-data.json)を渡している。後述するが、EJSファイルでは渡されたJSON(ページごとのメタデータ)を適宜差し込んだ上でHTMLを組み立てる。そんな処理になっている。

例: gulpfile.js

var gulp = require('gulp');
var fs = require( 'fs' );
var ejs = require('gulp-ejs');
var rename = require('gulp-rename');

gulp.task('ejs', function(done) {
  var json = JSON.parse(fs.readFileSync('./ejs/page-data.json', 'utf-8'));
  gulp.src([ './ejs/**/*.ejs', '!' + './ejs/**/_*.ejs' ]) // 第1引数がEJSの処理対象とするファイル、第2引数が除外ファイル
  .pipe(ejs({ pageData: json })) // これがEJS本体処理。jsonデータを渡してる
  .pipe(rename({
    extname: '.html'
  })) // 拡張子をhtmlにする
  .pipe(gulp.dest('../dest/')); // 出力先フォルダ
  done();
});

5. EJSファイルを用意する

EJSファイル内で使える変数や構文などは解説しているサイトがたくさんあるので割愛する。ここではJSONデータをどうやって展開するかだけ説明する。といっても実に簡単。まずは某ページ(仮にページID「p001」とする)のEJSファイル本体。%で囲まれたEJSの世界以外は普通にHTMLで記述する。

例: index.ejs(なんか適当なページの本体ファイル)

<% const meta = pageData['p001']; %>
<%- include('inc/_header.ejs', { 'meta': meta }); %>

<div class="main">
(略。ここは普通にHTMLで記述)
</div>

<%- include('inc/_footer.ejs'); %>

pageDataにはgulpfile.jsのタスクで渡されたJSONデータがまるっと入っているので、その中のページID「p001」の情報のみを取り出して変数 meta に入れている。そしてこの変数 meta を共通ヘッダファイル(inc/_header.ejs)に渡している。ついでに共通フッタ(inc/_footer.ejs)も読み込んでいるが、こちらは全ページ同じなのでデータ受け渡しはなし。

ひとつ注意はEJSタグの使い方。「<% ...%>」で囲まれた範囲は基本はJavaScript構文の世界。それに対して「<%- ... %>」のようにハイフンがつくと、変数やインクルードファイルをHTMLとして展開してそのまま出力する世界となる。「<%-変数名%>」のようにすると、その変数の中身をシンプルに出力できてGood。逆にハイフンなしの「<% ...%>」にinclude構文を書いても何も出力されないので注意。コンパイルエラーにもならないのでハマりがち。

あとファイル名先頭にアンダースコア(_)が付加されているejsファイルは、gulpfile.jsにて出力対象外としている。アンスコつけ忘れると、destフォルダに「inc/header.html」などという意味のないファイルができてしまう。そういう仕組み。

続いて共通ヘッダファイルそのもの。今回のゴールであるページのメタ情報を出力する本体のファイルとなる。

例: inc/_header.ejs(全ページで使う共通ヘッダ)

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title><%- meta.title %></title>
<meta name="description" content="<%- meta.description %>">
(以下略)

変数 meta で渡された情報を「<%-変数名%>」で出力している。今回はページタイトルとDescriptionだけだが、同じ要領でOGPタグやページ別のclass名やその他なんでもできてしまう。

6. gulpのejsタスクの実行

いよいよHTMLの生成。gulpfile.jsでタスク名を「ejs」としているのでgulpfile.jsのあるディレクトリでこうする。

$ gulp ejs

数十〜数百くらいのEJSファイルなら一瞬でdestフォルダにhtml出力してくれる。ejs配下のフォルダ構成も維持してくれる。gulpのタスクでejsフォルダをwatchしておけば、ファイル編集する都度htmlを書き出してくれる。

まとめ

必要なのは次のとおり。

  • npmパッケージ(gulp, gulp-ejs, gulp-rename, fs)のインストール
  • エクセルファイル
  • JSON
  • gulpfile.js
  • EJSファイル

今回は使っていないがEJSは普通にJavaScriptの構文であり、条件分岐もループも使える。なのでたとえば定型化された製品情報をエクセルで持たせておいてEJSで出力といった使い方もできる。エクセルやGoogleスプレッドシートなら誰でも(クライアントでも)編集できるし、データベースやその管理システムなどの大げさな仕組みが不要というのがEJSを使う利点だ。
またEJSに限らずだが、複数人での作業も上記と package.json をgitで共有して $ npm install してもらえばそこからすぐに開発に参加してもらえる。

イラレで選択した複数オブジェクトの幅を縦横比を変えずに揃えるスクリプト

イラレで選択した複数オブジェクトの幅を縦横比を変えずに揃えるスクリプトが欲しかったので作った。車輪の再発明感ハンパないが、ちょっと探した限り、幅のみ変える(=縦横比が変わってしまう)スクリプトはあったのだが、縦横比を変えずに幅を揃えられるのはなかったので……。

仕様

  • 選択した複数オブジェクトの幅を、その中の最前面オブジェクトに揃える。
  • このとき、各オブジェクトの縦横比は変えないように変形する。
  • 選択オブジェクトが1つ以下の場合は何もしない。

準備

  1. 以下のソースコードを任意のテキストエディタに貼り付けて、拡張子jsxで保存する。ファイル名は任意。
  2. そいつをイラレスクリプトフォルダに入れる。

使い方

  1. 複数オブジェクトのうち、幅の基準としたいオブジェクトを一番上に持ってくる。
  2. 複数オブジェクトを選択する。
  3. イラレの「ファイル>スクリプト」メニューから上で保存したスクリプトを選択する。

GitHub

https://github.com/michihisa/AI-script-set-the-width-of-selected-objects

var selectedObj = activeDocument.selection;

// 選択オブジェクトが2つ以上なければ何もしない
if (selectedObj.length <= 1) {
  alert('オブジェクトを2つ以上選択してください。');
} else {
  // この幅に揃える
  var w = selectedObj[0].width;

  for (var i = 0; i < selectedObj.length; i++) {
    // 縦横比
    var ratio = w / selectedObj[i].width;

    // 変更後の高さ
    var h = selectedObj[i].height * ratio;

    // 変更
    selectedObj[i].width = w;
    selectedObj[i].height = h;
  }
}

備考

  • SPAiなどのイラレスクリプトランチャーがあるとより便利(ショートカット設定も行える)。 https://tama-san.com/spai/
  • 整列時のようにキーオブジェクトを指定して、そいつ基準で幅を揃えられると尚良かったのだが、少し調べた限りスクリプトでキーオブジェクトを取得する方法がわからなかった(誰か教えてくれると嬉しいです!)。できないっぽい(?)

Facebookの「グレーアカウント」なるものにハマったのでメモ。

2020/6/26 追記:結果判明。以下の手順でOKだった!ことを追記。

 

グレーアカウントとは、FB公式によると

グレーアカウントは、個人用Facebookアカウントを使用せずにFacebookページの管理と広告の掲載を行うためのアカウントです。 このため、グレーアカウントの範囲は制限されており、アクセスできる機能が標準のFacebookアカウントよりも制限されています(一部のセキュリティ機能を含む)。

 

というものらしい。
https://developers.facebook.com/docs/graph-api/changelog/archive/gray-accounts?locale=ja_JP

 

とにかくグレーアカウントで管理しているFBページは広告も作れずインスタ連携もできない。困った存在なのだ。

しかもグレーアカウントの解除的な操作は(たぶん)ないので困り果てていたのだが、次の手順でちゃんとしたアカウントの管理下に置けたっぽい。ぽいというのは管理者になって7日間は「セキュリティのためまだ何もできませんよ」という期間なので。7日後が楽しみ(怖い)。

 

で、手順である。

1. 普通のアカウントを用意する

まず当該FBページの管理者として設定したい「通常のFBアカウント」を用意する。これは普段自分(自社)がFB広告出したり、他のFBページ管理したりと常用しているアカウントが良いだろう。なければ作る。

ここが最初のハマリポイントで、最初はグレーアカウントを解除する方向でばかりググっていたために、別アカウントを用意してそいつを管理者に設定すればよい的な情報がさっぱり出てこなかった。

また、その「通常のFBアカウント」のメールアドレスはグレーアカウントのメアドと違うものでなければならない(そもそも同じメアドで複数アカは作れない?)。ので、やっぱりここでも「別アカ用意しよう!」という発想には至りにくいのだ。

2. グレーアカウントでログイン

ログインという初歩的操作にもハマリポイントが。なんとログインできない(笑)。

いや、実はできてるのだがログイン後の画面がリダイレクトループにハマって表示されない。ここで気落ちしてはいけない。気にせずURL欄に http://www.facebook.com/settings/ と入力すれば、ログインした状態で当該FBページの設定ページが表示される(よく気がついたな・・・俺)。アプリでも「表示できねーよ」的なエラーメッセージが表示されるので一瞬もう詰んだかな?って絶望するが、アプリでは設定ボタンが押せるのでPCよりはまだなんとかなる感が高い。

3. 当該FBページの「設定>ページの管理権限」から管理者として1のユーザを追加

権限の種類は最上位の「管理者」で。

ログイン後はこの「ページの管理権限」だなと推測するのは比較的簡単だった。探していた「グレーアカウントを通常アカウントに切り替え」みたいな権限操作メニューがなくてがっかりするが、「ためしに通常アカウントの管理者を追加してみるか」とはギリ思える。半信半疑ながら正解まであと少し!

しかし次でまたハマる。

4. 新しい管理者ユーザに承認依頼がいくので承諾する

何にハマったかって言うと、なんと承認依頼が来ない(笑)。

PCにもメールにも来なくて、まさかスマホアプリにしか来てないなんてことがあるとは思わないので、ここで俺は何度か引き返した。「こっちじゃねーか」って。実はこっちでよくってスマホアプリの通知に承諾依頼が来てるので「承諾」する。これは未だに謎。俺のケースだけなのかなー?

5. 1週間待つ

もちろんここにもハマリポイントがある。1週間待つことではない(いやこれもそうだが)。なんとそもそもその1週間待てっていう指示が読めない(笑)。信じられないと思うかもしれないが本当に読めない。なぜならメッセージが表示されるのは一瞬だけ(1秒未満)だから。「ん?なんか今出てたな……」くらい。

俺の場合はそのFBページと某インスタアカウントを連携したかったので、インスタのスマホアプリのプロフィールの「ページ」から当該FBページを選択したかったのでそうしたのだが、その操作で何やらメッセージが一瞬出て消える。何度繰り返しても同じでもうしょうがないので、iPhoneの画面収録で操作を録画したものを一時停止して見た。人生で一度も使ったのことのないスマホの画面収録機能を「今こそ使うときぞ!」と気がついた自分をここでも褒めてあげたい。

このようにしてようやく「セキュリティ上の理由から、ページで管理者として追加後1週間はこのページのビジネスマネージャへの追加リクエストを承認できません」というメッセージを確認できた。なんだこれ。

 

以上まとめるとわずか5ステップなのだが5つ中4つにハマリポイントが用意されており、そのひとつひとつで丁寧に着実にライフが削られていく。

というかまだ1週間後にならないと本当に当該FBページとインスタ連携できるのかわからないんだけど……。感触的にはすごくできた感があるので大丈夫だとは思うが、ここまで丁寧な仕事ぶりを見せてくれたFacebookのことなので最後にも大きな罠をしかけていると考えたほうがいいかもしれない。

 

6. 1週間待った!

大丈夫だった。当該管理者として追加した通常のアカウントでログインし、当該FBページに関するあらゆる権限が付与されていることをかくにん!よかった!

懸案であったインスタアカウントと当該FBページの連携も難なく成功した。いやーホントによかった。もうこれでグレーアカウントが俺の人生に登場しても怖くないぞ(たぶんもう二度と登場しない)。

 

以上。

Dribbbleのプレイヤーになったぞ!

ちょっと前に古い実績(申し訳ない!)をいくつかアップロードしてDribbbleのドラフトにかけたら、親切な紳士か淑女がプレイヤーに招待してくれたぞ。うぇーい!

 

dribbble.com

 

今ってDribbbleのプレイヤーになるハードル下がってるのかな。ラッキー以外の何ものでもない気がするが、せっかくなのでいろいろなshotをキメていきたい。