日々スターリングエンジンのことを考えている。

妄想スターリングエンジン考(考えてるダケなんだぜ) - Togetter [トゥギャッター]

ラミナフローエンジン作っちゃおうかな(もう材料は買っちゃったんだぜ) - Togetter [トゥギャッター]

1月の能登地震で考えたのだ。 このせめてスマホ用にだけでも危険物取り扱いで集積に許可がいらない食用油で発電出来ないかと。 で方法はいくつか有った。

蒸気エンジン

これは水を使う、それも相当純粋なものでないといけない。でないと水垢が貯まるし電解質なら腐食する。 ただでさえ災害時に水がないのにそれを電力のためとは言え使うのは忍びない。 あと効率を追求すると国家資格が必要に成るのでそれも却下な理由である。

スターリングエンジン

これは効率が素晴らしい。効率は上限は60%という。ならばと考えた。 がどうも駆動系が厄介そうなのだ。振動を発する=徐々に振動で壊れていく。 というわけで一旦保留にした。

ペルチェ素子でゼーベック効果を使う

これが一番お手軽なのだが…で、実際に作って試した。 みんなが放り投げる理由が分かった所で挫折した。 一つは熱電変換効率が低すぎること。だいたい0.3%ぐらい。 2つ目はそして結局水を使わないと冷却が追いつかないこと。 3つ目にすぐ壊れるところ。壊れない範囲で使うとそれは効率が低いとなる。 一応次のパターンは考えては居るが水密の壁がある。 よってこれも保留にした。

現状

仕様をちまちま考えている。 目指すは2.5Wを100Wの入力熱から得ること。 2.5W=USBの5V 500mAに相当する。 100W=食用油(C18ぐらい)10mlを1時間燃やした熱量に相当する。 これができればまずは上々、できれば10W(5V2A)まで行ければスマホの充電やラズパイ4の稼働までもっていける。

ツール

先達のツールを少し改変してみた。

スターリングエンジン簡易性能予測法

300℃の温度差はやっぱり厳しい。

漫画ネタ202401

  • 恒星間航行を実現し宇宙に進出した人類は植民をつづける
  • しかし光速突破は結局実現できなかった
  • 従って宇宙に広がった人類は距離に従い強烈な分断が始まる
  • そしてそのうち一つの星系で魔素、基底現実で魔法、人間の圧倒的な物理操作用意識拡張マイクロマシンが開発される
  • その後、明確な理由は不明ながらその星系は主星を爆発させる事件で消滅する
  • 魔素を開発しそして消滅の憂き目にあった星系から辛くも脱出できたいくつかの播種船がそれぞれ事件の元から見つかりにくい新たなる恒星系に進出する
  • ある播種船は数万年後、ハビタブルゾーン限界の惑星にゼロからテラフォーミングで恒星間航行が可能にまで文明発展の復興を遂げる
  • もう一つ別の播種船は魔法基盤のマイクロマシンたる魔素による開発に全面的に振った挙句、人類統一政府相当が存在しない魔境に陥っていた
  • 宇宙開発以前に魔素の構成する可能性からの破滅を回避するために人為的に起こされる文明的混沌に陥っていた
    • そして前者が後者を発見する

  • 前者の世界は人類が住まう後者発見の報が届き、星系の世論は連絡を取り生存権を統合する機運に包まれた。
    • 決定打は人類の文明発展でしか観測されない分子の検出だった
    • 観測結果からもたらされる予測によればハビタブルゾーンの主星どころかいくつかの星に人口拠点が有るらしい。
    • しかし、星系にはやけに塵が多いという点は特徴的だった。星系の内側の何処かに火山か小惑星が粉砕されたのであろうと予測された。

1P

  • 前者は伝説の播種船が送出された星系は消え去った故事から同様の結末を回避すべく、星間文明構築に向け次の開拓先候補星系を探していた所、数十年で到達可能な後者を発見す
    • 人類が恒星間航行を実現して早数万年、結局光速を超えることはできなかった。そして、幾多もの星々に散ってそして破滅を迎えた
  • 消え去った星系の件も有り開拓は急務だし外縁惑星まで植民都市を拡張しきっていた
    • 「星はほとんど見えない漆黒の宇宙と」
    • 「俺達はこの漆黒のガス星雲に逃れ、その原因の破滅を迎えた我らが播種船の出身星系は1万光年の先に有る。らしい」
    • 「まあ数万年前だから伝承になりはててるわね」
    • 「聞いただろ?まさかあの暗黒星雲の向こうにあるとはね…」
    • 「ほんの小さな隙間から発見されたようね」
    • 「聞いた時は正直驚いたよ何かの奇跡かなって。しかしこれではあと50年もしたらまた観測不能になってしまう」
    • 「そうね。また星が消えてしまうわね」
    • 「今の状況はわからない。間に巨大な恒星を2個は挟んでいるらしい。で、かの星もその影に入っている…後は分かるな?」
    • 「私達と同じ。じゃあ、あの星も破滅の危機からの脱出組ってこと?!」
    • 「政府はそう考えてるらしい。俺達もこの惑星が3個しかない星系じゃどん詰りだしな。」
    • 「なにかあるの?」
    • 「まだ未公開情報だがすでに30年前には探査機は送り込んでいたそうだ。」
    • 「ええ!?それじゃ…」
    • 「政府は本気さ。その数万年ぶりの恒星間有人飛行に」

2P

  • が、先に送出した観測機が正体不明の故障で後者星系外縁部で消息を絶つ
    • 調査の結果、原因は伝説の禁忌技術、MMの魔素で有る可能性が浮上する。がそれは可能性の一つであって重視はされなかった。
      • 「最初に突入した探査機が機能停止しただと?」「高速フライバイだ、あの密度の塵なら衝突でもしたんだろう。」 「それが衝突データは無いんですが、だんだん機能停止に陥ってしまって」 「塵が例の人工物だって?」「アレは我々の播種船が出帆した時点で禁忌技術だ。使ってるなどないはず。」
    • 討論会
      • 「政府は無駄に期待を煽るのは厳禁だと秘密にしていましたが、よいよ情報が公開されましたね」
      • 「あの星系にはハビタブルゾーンに5個以上の惑星が有り、明らかに人工物の形跡があると」
      • 「電波の発信は見られませんが、我々とて同様。行ってみるしかないですな。」
      • 「ええ、ただ妙に塵が多いのが特徴で、小惑星が粉砕されたのでしょう。何があったかワクワクしますなぁ」
    • 視聴者席 「おかしいだろ?」 「どうして?」
      • 「俺達が数万年かかってようやくなんだ。なぜあっちから来ない?元々同胞だったんだ。俺達が出来るならあっちにだって出来る。でもこっちに来ていない」 「我々に気がついていないだけなんじゃ?暗黒星雲の隙間、それも中性子星でできた重力レンズの向こう側だよ?」 「それは否定しない。でも何もないのは気にかかるだろ?そりゃ俺達は電波封鎖はしてるけどさ、だけど」
    • 回想 *「どうして気が付かなかったんだろう。わかってたはずなのに」「可能性の数からして無理さ」

3P

  • 人口増加は頭打ちだ。太陽系より遥かに小さい星系だった。惑星も3個しか無い。スペースコロニーの建造数も限界が近く、すでに主星の光源の奪い合いをも発生させ始めていたし、次の植民地、資源の不足はもう恒星間移民しか残されていないと認識され始めていた
    • 「私達はあの窮屈な星系から開放されたのよ」「もう資源割当で争わなくていい」「そうだな」
  • というのも、記録が残る限り他の人類からの接触はなく、ならば残された人類は自分達だけであり、我らこそが恒星の海に文明を生存圏を再度広げるのだという気概があった
  • 即座に有人植民パッケージを有志の下、派遣を決定する

    • 「よいよ我々が恒星間種族に復帰するのだ!いざゆかん!星の海へ!」高揚する世論
    • 演説 「あの破滅の時代から人類の種を伝え、ここに我らは文明を再興させた」 「この星に辿り着いてからどれだけの月日が流れたことか。その間他の人類が我々に手を差し伸べただろうか?否!誰も居なかったのである!」 *「その孤独から我らは自力で立ち上がった!我ら以外に人類は居ない!我らこそが人類、今こそ破滅を迎えずこの星の海に人類が再度足を踏み出す時なのだ!立てよ我らが星の子らよ!」
  • 後者へ送り出された播種船は到達まで数十年かかることから橋頭堡を築くためノーリターンを想定された。

  • 有志にはサイボーグもAIも高性能計算機も生身の人間も不慮の事態を鑑みて選抜された。
    • 500年を生きた生き飽きた人間が自身のコピーを残し志願する
    • 「私は行くよ。惑星が3個しか無いこんな狭い世界じゃなくてね」
    • 「また数百年後には会えるさ。」
    • 「いいね。君たちはあの星系に行くのだ。エネルギーは心配しなくていい。過去の探査船同様、推進ビームはちゃんと照射し続けておく。」
    • 「それでは100年後の吉報を待つ!」「敬礼!」

4P

  • MMである魔素、情報は禁忌ゆえに開発した当時の伝説の星系由来の公知情報のみ。実際の所、開けたら最後除去が困難だという程度しか分かっていない。
    • 「我らが狭い故郷ははるか彼方なりと」「マジで暗黒星雲の中じゃ星がアレしか見えないのか」「あの星系でフライバイして目的地へだな」「」
    • 「君は外縁到着まで寝ないのかい?」「俺達は快適に寝てミッションに望むのさ」「私は不老不死手術を受けてるからね。その時間は事前準備しておくのよ」
    • 「準備ったって先行探査機の情報が来るのに数年かかるんだぜ?」
    • 「ちょっと時間がかかりそうな案件があってね・・・」
    • 「なになに魔素?なんだこりゃ?」「マイクロマシンの一種だよ」「へぇ聞いたこと無いな」「そりゃ禁忌だからね」
    • 「まさか、使ってるなんて恐ろしくて考えたくもないよ」
    • 「知ってるぜ、グレイグーだろ?全部MMに埋め尽くされちまうのさ」
    • 「一応制御はできてたみたいだけどね。だから破滅した星系にはすでに有ったけど我々はこうして無事なんだよ。」
    • 「わかった。じゃあ俺達は行くよ」「おやすみ」
  • 次第に故障前提で保険をかけ大量に送り込んだ先行偵察機により状況は分かってくるが暗澹とした情報に埋め尽くされる
    • 「どうなってるんだ。何かの間違いだろ?なんで外縁に到達したとたん全部壊れるんだ」
    • 「俺達より前に到達する偵察機はあと何機だ?」「さっきので最後です。全部壊れたわけではないですが、故障ですかね。」

5P

  • 播種船が前者の星系外縁に到達した頃、船に念の為設置したセンサーからMM警報を食らう
    • 「何の警報だ?」
    • 「わかりません、いろんなところから徐々に、外側からナニカに侵食されているような」
    • 「こいつは何だ」「今調べます。まさか…」
    • 「そのまさかだよ。私が頼んで外縁到達前につけておいてもらったんだ。私は勝ちたくない賭けに勝ったみたいだね。」「これからが大変だよ」
  • 目標のハビタブルゾーン内の惑星に近づくにつれ播種船は星系に漂う魔素に侵食されだんだん機能を停止していく
    • 「急げ、おいおいおい。どんどん機材が停止していくぞ」
    • 「生存限界までは?」「このままだとあと2日と言ったところでしょうか」
    • 「船を棺桶にしたくなかったらさっさと方法を探せ!」「方法は有るよ」「なんだと」「これも念の為。実験は成功だよ。」
    • 「過去の伝説文献からMMの機能を無効化する安全装置の信号を割り出したのか…」
    • 「こんな簡単なものでいいのか?」

6P

  • 引き返すか議論するが、
    • 「安全装置としては優秀ですね。しかし数万年前の情報が有効で助かりました」
    • 「船の機能は7割まで復旧できているが」
    • 「おれはこんな魔境だとは聞いてなかったぞ」「そんな覚悟で望んだのか?ここは前人未到だぞ?」
    • 「引き返して違う星系に志願すべきだ。我々ならテラフォーミングから出来るだろ?」「他にって何処が有るんだよ。周りは暗黒星雲なんだぞ?」
    • 「ここに、星系に到達してから開けるメッセージが有るんだが…」
    • 「おいおい、冗談だろ?」
    • 「妥当だね」「俺達はもう汚染されてるってことだ」「アイツラ知ってたのかよ!」「私でもこうするね。そのための人身御供だ」「くっそ」
    • 「引き返しても中間地点で迎撃すると」「星系内到達後開封のこの封印命令書にはそう書いてあるね」
  • 仕方ないので調査と植民候補地を探すことに決定する

    • 「じゃあ俺達はココに骨を埋めるしか選択肢はないわけだ。異論は?」
    • 「仕方ねぇな」「味方に撃たれるのはゴメンだわ」
    • 「ねえよ。貧乏くじも良いところだぜ。」
    • 「それでは諸君、植民地を築いていこうではないか。」
  • 調査の結果、星系全域がMM汚染されており、星系の何処に作ろうと大差が無いことが分かる。

    • 「この塵、全部MMなの?!」「みたいね。流石にまだ恒星間には広まってないみたいだけど」
    • 「星屑とかそういったものじゃないわ。もしかしたらカモフラージュなのかも」
    • 「恒星間は流石に放射性元素でも無い限り無理だね。コイツラのエネルギー源は熱だ。恒星から離れたら止まっちゃうし、そうなったら宇宙線でいずれ消毒されてしまう。」
  • 仕方がないので時間もコストも安い人類が住まう惑星地上へ向かう
    • 「住居なんだが…スペースコロニーの建造という手もあるが」
    • 「MMの存在下では建造には不安が残る。できれば並行して地上に拠点を構えたい」「大気組成も問題ない。水もある。太陽光も十分だ。」
    • 「MMの濃度は凄そうだけどね。」「でも大気が有るだけマシだわ」「じゃ、決まりだ。」
    • 「それはでは第一先遣隊を組織する。出発は3日後だ」

7P

  • ファーストコンタクトで亜種人種に出会う
    • 「あれは人間?なのか?」
    • 「タコのような頭だと…何言ってるか分からん」
  • 言語はAIでなんとかなったが強烈な人為的な影を見る
    • 「翻訳機のおかげで言葉はなんとか片言程度には通じるようだな。しかしタダの限界集落の狩人と言った所か。」
    • 「しかし、この特徴どう思う?」「頭部以外はほぼ人間、ですかね」「人間と動物のミックスだな。信じられんこれで生存しているだと。」
    • 「我々の様な人間は居るとの事ですが…なにせ語彙が少なくて状況がイマイチつかめないですね」
    • 「体格で勝っているのが救いですな」※星間移植民は現地民に比べ体育が圧倒的
    • 「おいどうした。」「日暮れだからか?みんな居なくなったぞ?」忍び寄る影
  • が、運悪く魔法生物にエイリアン的な強襲を受けて移植民の先遣隊は全滅する
    • 「緊急入電です」
      • 「こちら先遣隊!救援を求む!」
      • 「何者かが!うわぁぁぁ!」
    • 「先遣隊は全滅だと!?この空中に出現するものは…魔法陣が」

8P

  • しまいには衛星軌道上で強襲を受けて播種船は先遣隊第二陣を残して離脱する
    • 「なんだこの振動は!」
    • 「龍。ですかね。」「龍?!この高度に生物!?なんてことだ。私はまだ信じられません。」「いいからとっとと排除しろ!」
    • 「弾が当たらねぇ」「レーザーも弾かれるぞ!」「応戦していますが、排除できません!」
    • 「第2先遣隊が降下準備を完了してもう船外に放出済みです!どうしますか」
    • 「止む負えない!母艦がやられたら後がないぞ!このまま第二先遣隊は降下!我々は周回軌道から離脱する!」
  • 衛星軌道上で投げ出された先遣隊第二陣は仕方ないので現地民に習って魔物に襲われない安全地帯の橋頭堡確保に乗り出す。
    • 「各員聞いたか!このまま予定通り降下する!」「神のご加護があらんことを」
    • 「とにかく体制が整うまで第一先遣隊の全滅地点の調査は後回しだ」「あ、MM制御信号は弱めてください」「なんでだ!?」「とにかく降下完了までまずは。」「良いだろう」
  • 播種船は襲撃の修理のため大きい方の月軌道ラグランジュポイントに退避
    • 「第二先遣隊の着陸は確認。通信状態は良好です。現在は周囲の探索を実施中。」「まずは一安心だな」
    • 「船の修理に必要な時間は?」「幸い軽い損傷でしたから、2日もあれば」「分かった。衛星軌道上にも何かいる。何かあってはまずい。いったん距離を取って大きい方の月のL1に退避する。」

9P

  • 魔物の戦術として初手で魔法基盤である魔素、MMを沈黙させる存在を消すのだそうだ
    • 「MM制御信号はずっとこのまま限界近い弱めで良いのか?」
    • 「推測ですが、この信号は非常に単純です」「その割にMMは機能停止をします。」「火を考えてください。火で炙れば生物は焼け死んでしまいます。MMが生存に必要な器官にまで組み込まれた生物が存在したら?」
    • 「言いたいことはわかったが、どうやって確認するんだ?」「MM探知機なんて無いぞ。有るのは信号発振器と信号探知機だけだぞ」
    • 「出来れば信号のONOFFで挙動を変える生物でも見つられれば…」
    • 「簡単に言ってくれる。ここのMM密度は相当なもんなんだぞ。早く電源を確保しないと俺達は生存すら危うい。」 ・結果、MM解除信号が魔物を呼び寄せる事が判明
  • 襲撃は偶然ではないが十分遭遇を回避は可能と判断
    • 「奴らの行動を見ると一撃離脱だな。」
    • 「やっぱりセンサーが有るんだ。」「貴重な発振器が1個壊されたんだぞ。」「ええだからこうやって観察してるんじゃないですか」「第一先遣隊のように強烈な信号発振で安全圏のマージンを設けるとニノ轍を踏みますよ。」
    • 「ほらあそこ見てください、隣の小出力の発振器は無事です。それどころか忌避してます」「そうか、これは朗報だな。安全圏が作れる。」
    • 「象以上のデカブツが居るなんて聞いてないぞ」「幸いこちらには興味がなさそうですね」
  • なんとか信号の出力を限界まで抑えるが
    • 「荒事は避けたい。できるだけやり過ごすしか無いな」「だったら節電のため、第一次隊を襲った連中を呼び寄せないためにも信号照射範囲を限界まで狭めるしか無いですね。」

10P

  • 修理がてら先遣隊を派遣した惑星の観察をすすめ状況を整理する
  • 状況は混沌としていた
    • 「15世紀程度のインフラ有るようだが…」
    • 「これが我々の夢見た世界なのか…たしかに人類は相応に居るようだが」
    • 「どうして数万年前の都市遺跡がこんなに有るんだ」
    • ローマ帝国レベルの遺跡がゴロゴロしているぞ?」
    • 「人影はないのに何だこの不自然な輻射熱の分布は…」「それにこの火山の周りに有る明らかな人口構造物はいったい…」
  • 一方地上では信号の範囲外に置いた精密機械作動不良を起こすことでその理由がだんだん分かってくる
    • 「MMが物体の中に食い込むから精密機械ほど影響が速いのか…しまったな」
    • 「精密機械に入り込んだMMが自己複製を行い、機材の精度と剛性を破壊していく。もしかして文明を阻害しているのはこれか?」
    • 「全ての機材が魔素MMに汚染されて機能停止に陥る前に、現地民に文明先行の利点を失う前に拠点を確保しないと」
    • 「でなきゃ私達も魔法を使うしか無いよ」「魔法?」「第一先遣隊を襲った連中が使ってたあれか」「目星はついてるんだろ?」「そうだね。50年の鍛錬を見せる時だね」

11P

  • 母船との通信は生きているので色々調査を依頼するが
    • 「進捗は気にするな。襲撃に気をつけつつ、情勢をさぐれ。できれば現地協力者が欲しい。」
    • 「分かった。努力する」
  • 魔法をAIの力を借りて少しずつ習得はするもののAI機材がほぼ死ぬ(電力が足りない)事で地元民から接収する方向に向かう
    • 「どうだ?魔法はなんとかなりそうか?」「試そうにも逆に制御できる魔素が足りない、魔力が足りないな」「電力でどうにかならないか?」
    • 「無理だ。さっきソーラーパネルが逝っちまった。使えるセルが残ってねぇ」「おいおい結界は効いているのか?」「風に煽られると間に合わん」「エネルギー調達ね…焚き火じゃ限界があるな。この無人の原野じゃなくて協力者を見つけないと」
    • 「そうだな。焚き火じゃバッテリーに貯めなきゃMM抑止信号しか出せねぇ」「着火だけはできるんだけどねぇ」
  • 先遣隊のうち生身の人間は適合性に問題は経過観察としてMM停止信号の照射対象外とされる。
    • 「信号は切った。あんたは完全な生身だから問題は無いはずだが。気分はどうだ?」
    • 「上々だよ」
    • 「MMのパワーチャージ、魔素が体に取り込まれるまで時間がかかりそうだ。それよりも伝染病だな」
    • 「数万年で病気が増えてないことを祈るしか無いね」

12P

  • ラグランジュ・ポイントに古代の衛星遺跡を発見する
    • 「あの物体は遺跡ですかね。」「明らかな人工物だな。」「ただ熱放射、電磁波ともにほぼ停止しています。」「ほぼ?」「ええ、停止信号が若干出ています。」
  • 遺跡には女性のアバターが認証を寄越せと口うるさく言うが、言語から伝説の星系の技術系統で少なくとも10万年前には残存が確定する
    • 「汝、その真名を捧げ、我が認証に答えよ」
    • 「古代共用語ですね。」「訛りから見て10万年前の破滅の頃とさして変わっていません。」
    • 「認証の突破ですか?この方式だと。まあやってみますが時間は保証できかねます」「構わない、しばらく試してみてくれ。」
  • 認証の突破は数学的に非常に難しい事が分かる
    • 「で、この構造物は何だと思う?」「間違いない、制御信号衛星だ」「一気にMMを無力化出来ると?」「中身が無事で認証をクリアできればだがな。」
    • 「報告します。無理です。手持ちの機材じゃ認証突破にゃ100年はかかりますぜ」

13P

  • 仕方がないので先遣隊に調査指示をするが厳しい状況に陥っていることがわかり救援物資を送る事に
    • 「ああ、なんとか第一先遣隊の襲撃者は寄り付かないらしき方法は発見できたが…我々は物資が不足している」
    • 「分かった、追加の物資シャトルを投下させる。それまで頑張ってくれ。」
  • 衛星軌道上に蛇のような何かが居ることだけは分かる
    • 「こんな時に。やはり奴らか。龍なのか…衛星軌道上の生物なんて…どうやって生きているんだ」
    • 「MMの応用で未知のエネルギー回路があって太陽光か何かを直接摂取してるんだろう…なんてやつだ」
  • 救援物資は周囲の物質から分子転換炉とプリンターで出力したものを送る
    • 「我々が今届けられるのはこれだけだ」「十分だよ。それがあれば長期探索も出来るようになる。」
  • しかし、対MM制御信号がアダになって衛星軌道上で蛇みたいなのを寄せ付けてしまう
    • 「やっぱりだ!奴ら対MM制御信号に食いついてきやがる!対空防衛戦だ、奴らを物資に近づけさせるな!」
  • …その時遺跡の中で襲撃を受ける。襲撃者は悪魔の姿の魔法の使い手で移植民を遺跡から追い出そうとする
    • 「やばいぞ、一端船に退避しろ!」「何か唱えたぞ」「まさか魔法か?!」「警備隊!機関砲だ、火力で押し切れ!」

14P

  • 自衛システムで大気圏突入まで妨害をしのぎなんとか物資は地上に落下するも先遣隊の位置から大幅にずれてしまった
    • 「なんとか龍の排除に成功。無事物資シャトルは降下しましたが」「なんだ?」「回避行動分の修正が間に合いませんでした、着陸予定地が先遣隊から数百キロずれてしまいました」
    • 「遺跡内部への侵入者撃退を確認。」「殺ったか?」「いや、だが引いてくれるようだ。」「どうゆうことだ?」
  • 播種船の兵器で撃退した結果、軌跡から月からの来訪者と分かる
    • 「マジかよ」「あれが月に逃げったってことは」「月が黒い理由はまさか…」「高純度の魔素だぜ…」「グレイってよりパープルね」
  • 月からの来訪者対策にはMM抑制信号の照射量強化で魔法ごと封じる事で対応出来ることになった
    • 「信号照射で月の一部が変色してる…」「なるほどな。遺跡は更に強力版と言ったところか」
    • 「何もなければいいんだが。」
  • 追加物資を取りに行く事に
    • 「分かった感謝する。」「どうすんだよ」「物資を取りに行くしか無い…かな?」「おいおい300kmは離れているぞ」「諦めろ。そいつがなきゃこの魔境から生きていけないぞ。」

TOTPソフトウェアトークンをBookmarkletで作った。

経緯

TOTPソフトウェアトークンなブックマークレット - Togetter

リポジトリ

GitHub - ryunosinfx/BookmarkletTotpAuth: Bookmarklet for Totp Auth

Bookmarkletのリンクが有るページ

Bookmarklet Totp Auth

作った理由

GoogleAuthenticatorと同等のものだが、アプリを入れたくなかった。※アプリストアはGoogleアカウントを要求されるのが気に入らない。 ブラウザは存在するがアプリを入れられないデバイス(ラズパイ、超絶古いがまだ最新Firefoxが動くアンヨヨイヨすまほ。)で使いたい。 ソースが見れないアプリが信用ならんので自作したかった。 職場のPCにソフトウェアのインストールが出来ないため私物すまほを使わされられる可哀想な環境をなんとかしたかった。 また、ブックマークレットの限界が何処かを確認したかったのもある。

判明したブックマークレットの限界

以上!

ブラウザが解釈可能な最大のURL長は?

まあ以下の記事で答えは出ているのだが、もう少し調べてみた@202307である。

各種OS/ブラウザでの長いフラグメントのあるURLの長さを調べてみました - Qiita

何を調べるか

まず観点としては以下が有る

  • ①ブラウザが開けるURL
  • ②ブラウザが開けるURLからコピペできるURL
  • ③jsのlocation.hrefで取得できるURL
  • ④ブックマークに保存できるサイズ

このうち吾輩が目指す素敵なBookmarkletは④が重要に成る。 ただ、以下のようにGitubPagesやGASみたいなCORS許可のあるサイトに配置してESのダイナミックimportで良いじゃないかという話も有る。

Bookmarkletを作ろう(準備編) - Qiita

テストツール

というわけで、テストツールを作成した。ES2018ぐらいで書いてあるので古いブラウザでは動かない。

Browser URL length checker

ここで、分かった事が以下のとおり。②はそういったシチュエーションは考慮外なので調べていない。

  • Chromium114@Ubuntu 22.04LTSは
    • ①③2094057byteで約2MB弱
    • ④ブックマークも2094057byte約2MB弱で同じ
  • Firefox114@Ubuntu 22.04LTSでは
    • ①③1048565byteで約1MB弱
    • ④ブックマークは65535byteで約64KB
  • Chromium114@Andoroidでは
    • ①③2094057byteで約2MB弱
    • ④ブックマークも2094057byte約2MB弱で同じ
  • Firefox114@Andoroidでは
    • ①③1048565byteで約1MB弱
    • ④ブックマークは65535byteで約64KB
  • 一応iPadSafariでも
    • ④ブックマークは65535byteで約64KBまでは大丈夫そう。

node js安直にローカルのファイルをブラウザに見せるだけの鯖

安直にローカルのファイルをブラウザに見せるだけの鯖を作りたい。 ※正直npmでpackage.jsonでの管理もしたくないレベルの場合に使用したい。

理由は簡単で、esmoduleでファイルを書くと、モジュールファイルがfile:///のスキーマからでは 悪意の有る第三者の攻撃(ローカルに落としたHTMLファイルから他のファイルを見ることが出来ちゃうよ)により 弾かれるようになってしまった。

なので、超絶簡単にサーバーを自前で建ててHTML+js+CSSのアプリを作りたい。 OS種別とか気にしたくもないのでnodejsが入ってる前提でサクッと。

実行は

node server.mjs

server.mjsの中身は以下の通り

import fs from 'fs';
import http from 'http';
const exts = {
    js: 'text/javascript',
    css: 'text/css',
    html: 'text/html',
};
const PORT = 8085;

http
    .createServer(function (req, res) {
        const url = req.url.replace('../', '/');
        const urls = url.split('.');
        const ext = urls[urls.length - 1];
        console.log('req url:' + url);
        try {
            res.writeHead(200, {
                'Content-Type': exts[ext] ? exts[ext] : 'text/plain',
            });
            const file = fs.readFileSync('.' + url);
            const responseMessage = file;
            res.end(responseMessage);
        } catch (e) {
            res.writeHead(404, {
                'Content-Type': exts[ext] ? exts[ext] : 'text/plain',
            });
            console.log('req e:' + e);
            res.end('NOT FOUND');
        }
    })
    .listen(PORT, '127.0.0.1');

Tia142漫画ネタ

-1---

(怪獣だぁ!逃げろ!)

ああ、我らがカンムス

(カンムス様だ!カンムス様が来たぞ!)

その美しき女神は童子のごとき妖精を従え

(お嬢!動けるのは五分だけだぜ?)

(承知!)

怪獣をたやすく

(来た怪獣の!攻撃だ!)

(聞かぬ、一刀両断!)

ほふり星を開放してきた

(カンムス様万歳!)

何時からおりたのや?

(ふん、たやすいもんだぜ!)

人類有史以前?さにあらず、この星の歴史にあり。

それはこの星の開拓初期、宇宙で初めて人類が”奴ら”の猛威に相対した時に遡れり

-2----総裁当局

時既に恒星間探査船に星間ゲートにて超光速移動を実現した時代の話なり。

銀河にひろがりし人類はとある星系で惑星ごと消滅させるしか手の打ちようがなかりせしAIの反乱を経験したり。 反乱鎮圧後、兵権をAIに渡す事を禁じ、人間のみが握ると規定する星間条約を結びたり。これを星間条約連盟といいたり。

しかしAIは禁止すれどもAI級効率の人類圏未接触生物との遭遇はいずれは迎える事態と危惧されし。

もしかの生物が出現し、現地入植者を殲滅せし時はAIの時と同じく、即ちに”恒星落としの刑”に処す事と同時に定めれり。 ”恒星落としの刑”、それは入植惑星だった星に隕石落とし軌道を変え惑星ごと灼熱の主星の落とす最終手段なり。

それを星間条約では”上位侵略性未接触生物”と呼称せり。

このカンムスの生まれる星”八千代”へ、夢と希望の引き換えに莫大な借金を抱えたる入植団達、 星間条約を結び星間条約連盟に加入をはたして降り立てり。

-3----

そして時過ぎること数年、順調に行くと思われたその時、”奴ら”現れり

怪獣とその眷属達、

おおその20mを優に超す巨体やおぞましき姿、火を吐き光線を発射し入植者を圧倒することすまじき。

「あんな害虫踏み潰せ無いのか?」「あんた大きさ分かってんのか!?」

精々盗賊対策でしか無き治安部隊を歯牙にもかけず一蹴し、都市を木の葉のごとく壊滅せしこと甚だしき

急遽傭兵団を雇うも初戦で遁走の有様にて、

「砲兵支援だけはしてやる!前線は自分で支えな!」

やむなく住民総出で抵抗部隊、市民兵団を作り、阻止を試みたるが焼け石に水のごとし!

「うわー助けてくれ!」「ここで食い止めるぞ!」「爆破!」

絶望的な状況がそこかしこで少しでも怪獣の歩みを止めるべく展開されり

-4---

ああ、どうしよう

「防衛線が突破されました。」「逃げ遅れた部隊が救援を求めています」

「救援をだせ!人間が死んだら元も子もないんだぞ!」「次の防衛線に退却を急がせろ」

「砲弾備蓄があと1ヶ月分もありません」

「とにかく戦線を安定させろ、話はそれからだ!」

頭を抱える八千代開拓政府の長、将軍様

あまりの惨劇に次々と帰ってしまう武器商人達(これは駄目だ、宇宙戦艦級でないと無理だぞ)

今までの苦労を、これからの未来を全て破壊し尽くすあの怪獣をなんとかしなければと決心せり

「で、提案が有る人物が居るのですがー」

残るは一人の胡散臭い行商人のみなり

「ええい、良いから連れてこい!」

このまま人類はこの星八千代から駆逐されるのかや?

「このままでは来月の砲弾枯渇で防衛戦線が突破されます」

「これまでか・・・」

「いいえ、まだ手が有ります」「連れてきたぜ?」

-5--

そこに売り込み行商人に連れてこられたるは伝説の博士、カンムスの生みの親なり

「機械でもないAIでもない、我らが人間が怪獣を倒してみせましょう」

黒き怪獣を踏みつぶすその大きな足は一体誰や?

おお、この麗しき原始のFirstレディ、博士の相棒ではないか

「こんな汚れ仕事、これっきりよ?」

光り輝く槍で一閃、沸騰する大地、飛び散る肉片

「女神様だ・・・」

その神々しき姿に人々は涙したり。

-6--

博士は語れり

「我らに足らぬのは体格、それも怪獣など一蹴できる大きさ」

「怪獣が真似る?それは不可能なのです。我々の、宇宙太陽光発電という惑星表面積を超えて太陽定数を凝縮できる動力源が無い限り熱力学第二法則により不可能なのです!」

「弱いものいじめ?いいえ!我らは必勝必滅を悲願とし奴らに引導を渡すのです」

「何故ならこの星の生存権を賭けた勝負、負けた方は叩き出される運命なのだから」

「この麗しく、少なくとも人類の半分には快く受け入れられるであろう容姿」

「宇宙戦艦の主砲直撃でなければ傷が付かない身体」

「人間と同じタイムスケールで駆動できる巨体」

「そして、24時間365日作戦行動ができる休息不要な持久力」

「その間補佐するいかなる妨害も受け付けない人間が潰れる加速度にも耐えられる強靭な内部補助人員、妖精さん

「われわれ人類が、人類のパワーを以て勝利するのです」

-7--

その頃、星間条約連盟では怪獣たちの力と数、”上位侵略性未接触生物”、AI級の力を恐れたり。 星間ゲートを閉じ、殲滅をするか否かの会合が連日連夜、専門家を集め行われり。

「星間ゲートは閉じよ。連絡が途絶え次第、殲滅艦隊を派遣する。以上だ。諸君、異議はないかね?」「異議なし!」

そして将軍様にその報伝わると決心したり。

「腹を決めたよ」

「我々が生き残れば、それが勝利だ」

腹をくくった将軍様

起死回生一発逆転、そのためのカンムス数を揃えるべく首都さえも捨て最終防衛線、 宇宙太陽光発電の受電設備が有りカンムスの生まれる場所、カンムスの里へ退却を敢行すると号令をかけたり。


そして・・・星から逃げる意志と能力の有る者達があらかた居なくなりし頃・・・

-8--

反撃のときは来たれり、里で生まれし居並ぶ女神カンムスに将軍様の最後の激が飛べり!

「諸君、機は熟した。人類に栄光あれ!」「人類に栄光あれ!」「乾杯!」

「突貫!」

ああ我らが鉾、我らが剣、向かう所肉片とならず敵う怪獣はなし、

日が暮れるとも、夜が明けるともその矛先、鈍ることなし。

生死をかけて押し寄せる黒い怪獣の波を間髪入れずに次々と倒しけり

「1000匹目!」「まだまだ!」

その鉾先は幾日も幾月も、留まる事は無し。積み上がるは怪獣達の躯の山々なり

おお、ここに危機去れり、カンムスにより人類はこの星に生き残れり!

-9---

里から生まれいづるカンムス達、生存圏防衛線の確立に次々と出征せり その凛々しきカンムスの姿を見たる残った市民が沸き立ちぬ

その一方で遥か上空、軌道上にてお別れの会、催されり。

名残惜しむ将軍様

「博士、行ってしまうのかね?」

満足気にきりりと背筋を伸ばす博士

「私の目的は達成され、起案の正しさは証明されました。我々は怪獣の故郷に赴きます」

そう言うと博士はレディと星を離れたり。

そして向かう先に星の海を単独で渡り切るという深宇宙探査艦が航海の年季が入った姿で待ちにけり。

迎えるはあの胡散臭い行商人

「待ってたぜ?」「お世話になるわ」

恒星間探査船に乗った行商人と共に星の彼方に旅立てり。

-10--

なぜ、まだカンムスはこの星に居るのかや?

そはみなの足元に怪獣の種が眠っているからなり。

時が経てば怪獣の種は根を張り力を蓄え、そしてまた人類に仇なすべく現れるなり。

「うーっす。先輩、こっちの駆除終わったっす。」「おつかれー」

そのカンムスの任務が消える日は真に怪獣を調伏せる方法を人類が見つける遥か未来なり

その時、カンムスも妖精さんも人間になれるという・・・

GAS(Google Apps Script)のハマりどころ@2022

今やってること

WebRTCのシグナリング鯖の構築を目指している。 WebRTCはP2Pなのは良いが、シグナリング鯖と言うやつが要求されるのである。こいつはP2P接続の情報(sdp)を交換するのだがそれだけなら一見大したことはない。 しかし実際に自前構築を考えるとこのシグナリング鯖というやつがが厄介でサンプルを見るとどいつもこいつもWebSocketをオススメしてくる。WebSocketは分かりやすいのだが無料で借りれる場所はない。WebSocketは余りにもリソースを食うのだ。他にもHttpStreamApiと言うのも有ったがFirefoxが100でようやく対応しているのでメジャーと言うには程遠い。そしてリソースを食う。

ストリーム API - Web API | MDN

結局の所、普通の掲示板でも良いのだ。情報交換ができれば普通の掲示板でも何の問題もない。 そこでGASだ!Googleのアカウントが有れば使える。Googleのアカウントは1IPか1Andoroidに6ヶ月に1回取れる。そうジャンク屋でGetしたスマホで取れるのである。 ここにシグナリング鯖を建てられれば、雨後の筍のように自腹で鯖を立てること無く、誰かに生殺与奪の権を握られること無くコピペでWebRTCを使える事に成る。

ハマったところ

次の点はハマった。これで3年は寝かせる羽目になった。

GASの制約
  • 使えるのはdoGetとdoPostだけ

    • まあ問題は無いんだよ。正常に動けば。ただし、エラーを起こすとCORS許可のヘッダーを返さずにエラー(ブラウザ上ではCORS許可されてないよエラー)になるので意味不明である。
    • 当然、投入したデータ起因でエラーになったら良くわからない。
  • GASのログ見えない

    • ログは取ってあるけど、GCPに課金してちょ!って言われるので泣く泣く諦めるしか無い。
    • 仕方がないのでdoPostを呼ぶ関数を中に作ってそいつ越しにデバッグをするしか無い。投入されたデータをエスパーして。
  • async/awaitは対応したけどdoGetとdoPostは別腹な
    • もう、何が起こったのか分からなかった。V8に対応したんじゃないの?ってdoGet/doPost呼び出し側がPromiseに対応してないでやんの・・
  • setTimeout?知らない子ですね。
    • これが痛い。マルチプロセスで掲示板上書きせずに書き込もうとか考えると結構待つ必要が発生するのだが、そんな用途は端から考えてない模様。まあExcelみたいなスプレッドシートを扱うんじゃ仕方ないね。
    • じゃあどうするか?それはもうwhileでコンテキストスイッチを噛ますべくDate.now()で指定時刻になっているか判定を別関数にして呼びまくるしか無い。console.log()とか無駄につけて。
  • Googleの他のサービス呼び出しは兎に角遅い

Cache Service  |  Apps Script  |  Google Developers * Cacheサービスの制約はKVSで生存時間は10分、保持レコード数は1000件、1レコードの長さはキーが250byte、データが100kbなので十分である。

サンプル

ライセンスはMITでよろ。

鯖側

Webアプリとして誰でもアクセス出来るようにデプロイ

const cache = CacheService.getUserCache();
const EXPIRE_DURATION = 1000 * 2;
const WAIT_EXPIRE_DURATION = 1000 * 20;
const parse = (event) => (!event || !event.parameter ? { cmd: null, group: null, data: null } : { group: event.parameter.group, cmd: event.parameter.cmd, data: event.parameter.data });
function sleep(sec = Math.floor(Math.random() * 800) + 200) {
    const expire = Date.now() + sec;
    const func = (ms) => ms > expire;
    return new Promise((resolve) => {
        while (!func(Date.now())) {
            console.log(`sleep: sec:${sec}/expire:${expire}/now:${Date.now()}`);
        }
        resolve();
    });
}
async function wait(key, value) {
    const ckey = `c%${key}`;
    const challeng = value + Date.now() + Math.floor(Math.random() * 1000000);
    let c = null;
    while (c !== challeng) {
        await sleep();
        cache.put(ckey, challeng);
        c = cache.get(ckey);
        cache.remove(ckey);
    }
}
async function add(key, value, now = Date.now()) {
    await wait(key, value);
    let c = cache.get(key);
    c = c ? JSON.parse(c) : { message: [], expire: now + 40000 };
    const n = [];
    for (const v of c.message) {
        if (v.expire > now) {
            n.push(v);
        }
    }
    n.push({ value, expire: now + WAIT_EXPIRE_DURATION });
    cache.remove(key);
    cache.put(key, JSON.stringify({ message: n, expire: now + 40000 }), 900);
}
function put(key, value, now = Date.now()) {
    cache.remove(key);
    cache.put(key, JSON.stringify({ message: value, expire: now + EXPIRE_DURATION }));
}
function doWait(state, expire) {
    console.log(`doWait:expire;${expire}`);
    if (expire < Date.now()) {
        state.isOver = true;
    }
}
// eslint-disable-next-line no-unused-vars
function doPost(event) {
    const out = ContentService.createTextOutput();
    out.setMimeType(ContentService.MimeType.JSON);
    try {
        const { group, cmd, data } = parse(event);
        const key = JSON.stringify([group, cmd]);
        const value = typeof data !== 'string' ? JSON.stringify(data) : data;
        if (cmd === 'wait') {
            const state = { isOver: false };
            add(key, value).then(() => {
                state.isOver = true;
            });
            const expire = Date.now() + 1000;
            while (!state.isOver) {
                doWait(state, expire);
            }
        } else {
            put(key, value);
        }
        console.log('END:doPost');
        out.setContent(JSON.stringify({ message: 'POST OK' }));
    } catch (e) {
        out.setContent(JSON.stringify({ message: 'ERROR', e: e.message, stack: e.stack }));
    }
    return out;
}
// eslint-disable-next-line no-unused-vars
function doGet(event) {
    const out = ContentService.createTextOutput(); //Mime TypeをJSONに設定
    out.setMimeType(ContentService.MimeType.JSON); //JSONテキストをセットする
    try {
        const { group, cmd } = parse(event);
        const key = cmd && group ? JSON.stringify([group, cmd]) : null;
        let value = key ? cache.get(key) : null;
        value = value ? (typeof value === 'string' ? JSON.parse(value) : value) : null;
        if (key && value && (!value.expire || value.expire < Date.now())) {
            cache.remove(key);
        }
        out.setContent(JSON.stringify({ message: key ? (value ? value.message : value) : 'GET OK' }));
    } catch (e) {
        out.setContent(JSON.stringify({ message: 'ERROR', e: e.message, stack: e.stack }));
    }
    return out;
}
ブラウザ側

これがなかなか正解が何かで往生した。結局、GASは"""正常時"""のみリダイレクトをしてCORS許可("*"なのでどこでもOK)を返す。不正時はクロスオリジン不許可アクセス遮断エラーになる。 なおPOSTも結果が取得できる。

const contentType = 'application/x-www-form-urlencoded';
    convertObjToQueryParam(data) {
        return data && typeof data === 'object'
            ? Object.keys(data)
                    .map((key) => `${key}=${encodeURIComponent(data[key])}`)
                    .join('&')
            : data;
    }
    async getTextGAS(path, data = {}) {
        const r = await fetch(`${path}?${this.convertObjToQueryParam(data)}`, {
            method: 'GET',
            redirect: 'follow',
            Accept: 'application/json',
            'Content-Type': contentType,
        });
        return await r.text();
    }
    async postToGAS(path, data) {
        const r = await fetch(`${path}`, {
            method: 'POST',
            redirect: 'follow',
            Accept: 'application/json',
            'Content-Type': contentType,
            body: `${this.convertObjToQueryParam(data)}`,
            headers: {
                'Content-Type': contentType,
            },
        });
        return await r.text();
    }
使い方
  • GASに鯖をデプロイする
  • ブラウザから以下のJSONを投げる
    • {group:"一意なグループ名",cmd:"waitの場合は待つ、他は適当",data:"sdp等のデータ"}
    • {message:"投稿データ"}を受け取る
  • WebRTCのバニラICEの儀式をこの掲示板(投稿データの生存時間は20秒に設定)の上で行う。