【electron】アーキテクチャ図解

はじめに、electronプロジェクトの作成手順はelectron getting startedを参照。 簡単にまとめる。

npm init
npm install --save-dev electron
touch index.js
vim package.json # scriptsに"start": "electron ."を追加
npm start

さて、本題。
※この記事の目的は概念の整理であり、具体的なプログラムの書き方を示すものでありません。

electronアーキテクチャ

electron.jpg

electronはchromiumアーキテクチャをベースとしている。なので、chromiumに詳しい方はelectronの理解もしやすいと思う。

electronには2つのプロセスが存在する。

  • メインプロセス
  • レンダラープロセス

メインプロセス

メインプロセスはWebでいうバックエンド、サーバサイドにあたる。npm startを行うとデフォルトではindex.jsが実行されるが、そのプロセスがメインプロセスになる。メインプロセスはOS機能にアクセスできる。index.jsはメインプロセスで実行されることは大切なので意識したい。

メインプロセスの役割は下記の通り。

  • アプリケーションウィンドウの作成
  • アプリのライフサイクルの管理・ネイティブインターフェースの表示
  • レンダラープロセスとイベントの管理
  • プリロードスクリプトのアタッチ
  • MessagePortの設定・割り当て
    • レンダラープロセス間通信の際に有用
    • 別途、レンダラープロセスからMessagePortを利用可能にするためのAPIを公開するコードをプリロードスクリプトに記載する必要がある

MessagePortの設定・割り当ては少し難しい(私のあまり把握していない)ので、必要なときに勉強すればいいと思う。 個人的によく使うのは、上4つの機能。

レンダラープロセス

レンダラープロセスの役割は下記の通り。

  • 1ウィンドウに1プロセス
  • 典型的なフロントエンド機能と同等
  • OS、Node.js、electronAPIへのアクセスはできない

レンダラープロセスはchromium(Webでいうブラウザ)上で動作するので、OS機能やNode.js、elctronAPIにはアクセスできない。 つまり、レンダラープロセスでできること = Webブラウザの実行環境でできること、ということである。

プリロードスクリプトについて

基本的に、メインプロセスとレンダラープロセス間の通信はできない。これはつまり、アプリ画面のとあるボタンをクリックしたら、ファイルへ保存したい(もしくはファイルからデータを取得したい)、といったことができないことを意味する。これでは困る。

そこで、プリロードスクリプトというものを利用して、メインプロセスとレンダラープロセス間で通信できるようにする。

プリロードスクリプトで記述することは、レンダラープロセスからアクセスでき、かつメインプロセスへ信号を送るAPI(関数)を定義することである。プリロードスクリプトの実行環境は、一部のelctronAPIを利用できるため、そこで定義するAPIはelectronAPIを拝借してメインプロセスへの通信機能をもつことができる。

つまり、プリロードスクリプトで定義したメインプロセスへの通信機能をもつAPIをレンダラープロセスから呼び出すことで、メインプロセスとレンダラープロセス間で通信(片方向通信、双方向通信)ができるようになる。レンダラープロセスを起点にした通信、メインプロセスを起点にした通信の両方に対応している。

プリロードスクリプトのアタッチはメインプロセス側のスクリプトで行う。

まとめ

  1. npm startを実行する
  2. メインプロセスでindex.jsが実行され、アプリケーション(ウィンドウ)が立ち上がる。
  3. このとき、プリロードスクリプトも実行される
  4. その後、ウィンドウ(chromium)上でレンダラープロセスが走り、HTML・CSS・JSから画面が描画される。
  5. 必要に応じて(例えばボタンをクリックすると)、プリロードスクリプトで公開したAPIを利用してレンダラープロセスがメインプロセスへ通信を行いデータをやりとりする

以上。

参考

https://www.electronjs.org/ja/docs/latest/

【Jinja】Includeブロック内で変数を展開する

Goal

  • JinjaのIncludeブロックで指定するファイル名を変数で指定する

Includeブロック内では、Jinjaの構文では変数が展開されず、文字列として認識されてしまう。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        {% include "{{ variable }}" %} <!-- これだと Template not found error となってしまう-->
  </body>
</html>

Environment

  • Jinja 3.1.2

Resolve

Jinja2 2.7.1 以降では、下記で展開可能。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        {% include "%s" % variable %}
        {% include "/files/%s" % variable %}
  </body>
</html>

Jinja2 2.7.1 以前でも、理論上は下記で回避可能。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        {% include "templates/case/"+variable+"/intro.html" %}
        {% include "/files/"+cid %}
  </body>
</html>

Reference

https://stackoverflow.com/questions/12233971/using-include-to-dynamically-point-to-html

【HTML】初期テンプレート

<!DOCTYPE html>
<html lang="ja">
   <head>
     <meta charset="utf-8">
     <title>サイトタイトル</title>
     <meta name="description" content="ディスクリプションを入力">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <link rel="stylesheet" href="style.css">
     <!--<script src="index.js"></script>-->
  </head>
  <body>
  
  </body>
</html>

【ngrok】LINE botとflaskアプリを連携する

Goal

LINE botflaskアプリngrok経由で連携する。 LINE botがいるスペースでメッセージ送信するとflaskアプリにHTTPリクエストが届くことを目指す。

Environment

  • LINE側

    • LINE Messaging API v2
  • flaskアプリ側

    • OS: Raspbian GNU/Linux 10 (buster)
    • ngrok version 3.1.0
    • Python 3.8.13
    • Flask 2.2.2

What ngrok

ngrokはインターネット上の通信をローカルサーバへもってくることができるトンネリングサービス。

通常ローカルのWebサーバを公開するには、色々と手順が必要。最近はHerokuやAWS Lightsailで公開がだいぶしやすくなってきた。ただ、どちらも費用が発生するので手軽に検証したいという時は少し不便。

ngrokは柔軟性は下がるが、Webサーバを公開するには必要十分なものである。なにより手軽。HTTPをはじめSSHも可能なので、自宅のサーバのSSHを公開することも可能。

ただし、無料版ではネックな制限がかかるので本番環境での運用はおすすめしない。(根気があればできる??)

image.png

1. Create ngrok account.

ブラウザにて、ngrokのアカウントを作成する。

2. Install ngrok command to your computer.

flaskアプリがあるサーバで、ngrokコマンドをインストールする。OS問わずどれでもいけそう。

公式に手順がある。

  • 手動インストール
  • apt
  • snap

たぶん、yumでも方法はある。

tokenは「Getting Started」の「Your Authtoken」に記載がある。

3. Start a tunnel

flaskアプリがあるサーバにて、ngrok http 80を実行すると、下記のような情報が表示される。

ngrok                                                                                                                                                                                                                         (Ctrl+C to quit)
                                                                                                                                                                                                                                              
Add Single Sign-On to your ngrok dashboard via your Identity Provider: https://ngrok.com/dashSSO                                                                                                                                              
                                                                                                                                                                                                                                              
Session Status                online                                                                                                                                                                                                          
Account                       Your Account (Plan: Free)                                                                                                                                                                                          
Update                        update available (version 3.1.1, Ctrl-U to update)                                                                                                                                                              
Version                       3.1.0                                                                                                                                                                                                           
Region                        Japan (jp)                                                                                                                                                                                                      
Latency                       8ms                                                                                                                                                                                                             
Web Interface                 http://127.0.0.1:4040                                                                                                                                                                                           
Forwarding                    https://xxxxxxxxx.jp.ngrok.io -> http://localhost:80                                                                                                                          
                                                                                                                                                                                                                                              
Connections                   ttl     opn     rt1     rt5     p50     p90                                                                                                                                                                     
                              0       0       0.00    0.00    0.00    0.00

このうち、Forwardinghttps://xxxxxxxxxxx.jp.ngrok.ioを控える。

https://xxxxxxxxxxx.jp.ngrok.io -> http://localhost:80ngork.ioにきたリクエストをlocalhost:80に飛ばすことを意味する。

4. Setting Webhook in LINE Messaging API

LINE botがメッセージ受信時に、LINE Messaging APIのWebhookが飛ばすリクエストの送信先を設定する。設定はブラウザで行う。

LINE Developersにログイン後、LINE Developers Consoleに移動してボットを選択する。その後、Messaging API設定で下記の設定を行う。

  • 「Webhookの利用」: ON
  • 「Webhook URL」: https://xxxxxxxxxxx.jp.ngrok.io

5. Build and run Flask App

flaskアプリがあるサーバにて、Flaskpip installして、アプリを立ち上げる。ポートは80を指定。 LINE botがメッセージを受信すると、LINE Messaging APIは指定URLへPOSTリクエストを送信するためPOSTで公開しておく。

python -m venv venv
source venv/bin/activate
pip install flask
vim hello.py
# Setting flask app file and run.
FLASK_APP=hello.py flask run -p 80
from flask import Flask

app = Flask(__name__)

@app.route("/", methods=["POST"])
def reply():
    print("Hello ngrok")
    return "Hello World"

6. Check Received HTTP request in ngrok and flask app

LINE botのスぺースでメッセージを送信する。そうすると、raspiの例のngrokでは下記のように表示される。

ngrok                                                                                                                                                                                                                         (Ctrl+C to quit)
                                                                                                                                                                                                                                              
Add Single Sign-On to your ngrok dashboard via your Identity Provider: https://ngrok.com/dashSSO                                                                                                                                              
                                                                                                                                                                                                                                              
Session Status                online                                                                                                                                                                                                          
Account                       sagaziiin (Plan: Free)                                                                                                                                                                                          
Update                        update available (version 3.1.1, Ctrl-U to update)                                                                                                                                                              
Version                       3.1.0                                                                                                                                                                                                           
Region                        Japan (jp)                                                                                                                                                                                                      
Latency                       7ms                                                                                                                                                                                                             
Web Interface                 http://127.0.0.1:4040                                                                                                                                                                                           
Forwarding                    https://6c45-240b-11-10c3-3900-e65f-1ff-fe1f-16ce.jp.ngrok.io -> http://localhost:8888                                                                                                                          
                                                                                                                                                                                                                                              
Connections                   ttl     opn     rt1     rt5     p50     p90                                                                                                                                                                     
                              2       0       0.02    0.01    0.01    0.02                                                                                                                                                                    
                                                                                                                                                                                                                                              
HTTP Requests                                                                                                                                                                                                                                 
-------------                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                              
POST /                         200 OK

また、flask appでもPOSTリクエストが来たことを確認できる。

あとはPOSTリクエストの中身を処理するプログラムを書けば、LINEとflaskアプリの連携が可能になる。

7. 注意点

  • ngrokのフリープランの場合、ngrokコマンドを終了して、再実行すると公開URLが変化する。その場合、再起動時にLINE Webhookも変更する必要がでてくる。

以上。

【Almalinux】djbdnsをインストールしてSystemdで管理する

Goal

Almalinuxにて

  • djbdnsパッケージのインストール
  • djbdnsをsystemdでサービス化

Environment

  • Almalinux 8.6 (Stone Smilodon)
  • ndjbdns 1.06
  • systemd 239 (239-68.el8_7.2)

ndjbdnsパッケージはdjbdnsの堅牢性を低下させる代わりにインストールを容易にしたもの。djbdnsとの互換性はある模様。 この記事ではndjbdnsパッケージを利用してDNSサーバを構築する。

補足だが、djbdnsは下記が含まれている。

  • tinydns(権威DNSサーバ)
  • dnscache(DNSキャッシュサーバ)

1. Download ndjbdns from Web.

cd /usr/local/src
wget "http://pjp.dgplug.org/ndjbdns/ndjbdns-1.06.tar.gz"

2. Install ndjbdns

インストール場所は/usr/local/src配下

tar zxvf ndjbdns-1.06.tar.gz
cd ndjbdns-1.06
./configure --prefix=/usr/local
make install
which tinydns
# Output: /usr/local/sbin/tinydns
which dnscache
# Output: /usr/local/sbin/dnscache

GCCがない環境で./configure --prefix=/usr/localをするとエラーがでる。 その場合は下記を実行。aptでいうbuild-essentialをインストール。

yum groupinstall "Development Tools"
yum install kernel-devel kernel-headers
# その後、./configure --prefix=/usr/local

3. Setting tinydns and dnscache

tinydnsとdnscacheは別のサービスと起動するがどちらもポート53を使用する。そのため、同じサーバ上で両方のサービスを動作させる場合、IPアドレスが2つ以上必要になる。 tinydnsとdnscacheが使用するIPアドレスは下記のファイルでそれぞれ指定できる。

  • /usr/local/etc/ndjbdns/tinydns.conf
  • /usr/local/etc/ndjbdns/dnscache.conf
cd /usr/local/src/ndjbdns-1.06

# 編集内容は下記を参照
vim /etc/init.d/tinydns.sh
vim /etc/init.d/dnscache.sh
vim /usr/local/etc/ndjbdns/tinydns.conf
vim /usr/local/etc/ndjbdns/dnscache.conf
### BEGIN INIT INFO
# Provides:          tinydns
# Required-Start:    $network
# Required-Stop:     $network
# Default-Stop:      0 1 2 3 4 5 6
# Short-Description: start and stop tinydns daemon at boot time.
# Description:       tinydns is a DNS server program.
### END INIT INFO

# Source function library.
. /etc/init.d/functions

# Source networking configuration
. /etc/sysconfig/network

prog=PREFIX/sbin/tinydns
config=/etc/ndjbdns/tinydns.conf
-config=/etc/ndjbdns/tinydns.conf
+config=/usr/local/etc/ndjbdns/tinydns.conf
### BEGIN INIT INFO
# Provides:          dnscache
# Required-Start:    $network
# Required-Stop:     $network
# Default-Stop:      0 1 2 3 4 5 6
# Short-Description: start and stop dnscache daemon at boot time.
# Description:       dnscache is an iterative DNS resolver daemon.
### END INIT INFO

# Source function library.
. /etc/init.d/functions

# Source networking configuration
. /etc/sysconfig/network

prog=PREFIX/sbin/dnscache
-config=/etc/ndjbdns/dnscache.conf
+config=/usr/local/etc/ndjbdns/dnscache.conf
# Address to listen on for incoming connections. It could be comma separated
# list of IP addresses.
#
# IP=127.0.0.1[,x.x.x.x,...]
-IP=127.0.0.1
+IP=Your IP Address 1

---

# ROOT: is the new root & working directory for tinydns.
# Obviously, the user whose ID is mentioned above MUST be able to read from
# this directory.
#
# Also, this is where `data' and `data.cdb' files should reside.
-ROOT=PREFIX
+ROOT=/usr/local/etc/ndjbdns
# Address to listen on for incoming connections.
-IP=127.0.0.1
+IP=Your IP Address 2

---

# ROOT: is the new root & working directory for dnscache.
# Obviously, the user whose ID is mentioned above MUST be able to read from
# this directory.
#
# Also, this is where `ip/' and `servers/' directories should reside.
-ROOT=PREFIX
+ROOT=/usr/local/etc/ndjbdns

4. Enable starting tinydns and dnscache with Systemd

cd /usr/local/src/ndjbdns-1.06

cp ./etc/init.d/tinydns.sh /etc/init.d/
chmod u+x /etc/init.d/tinydns.sh
cp ./etc/init.d/dnscache.sh /etc/init.d/
chmod u+x /etc/init.d/dnscache.sh

systemctl start tinydns
systemctl start dnscache

systemctl enable tinydns
systemctl enable dnscache

Almalinuxにおいて、sysVinitは内部的にはsystemdであったのでinit.dに登録することで、systemdで管理可能。 https://qiita.com/KEINOS/items/f3e6b3064b0cbe35fd03

djbdnsにはsystemd用のファイルも含まれているので、/etc/systemd/system/に配置してもいいかもしれない。

  • ${djbdnsフォルダ}/etc/systemd/tinydns.sysd
  • ${djbdnsフォルダ}/etc/systemd/dnscache.sysd

Reference

https://qiita.com/metheglin/items/2c4f798eded252b2b61e https://blog.64p.org/entry/2013/07/22/142457

以上。

【python】OpenAIのDALL・E 2を利用する

TODO

Pythonにて

  1. DALL-E 2を利用して画像を自動生成する
  2. 作成した画像をローカルにダウンロードする
  3. 複数の画像を作成&ダウンロード

Environmet

Pythonバージョンは3.7.1が必須
MSYS2 MinGWWindowsBash環境を整えるためのソフトウェア

Using Library

pip install openai
urllibはPythonに内蔵されているライブラリ

1. Write code creating a image with dall・E 2

import openai
openai.api_key = 'Your API Key'
def generate_image_with_dalle2(prompt):
    # Official API Reference: https://beta.openai.com/docs/api-reference/images
    response = openai.Image.create(
        prompt=prompt,
        n=1,
        size='{}x{}'.format(str(256), str(256))
    )
    image_url = response['data'][0]['url']
    return image_url

image_url = generate_image_with_dalle2('a white cat')
print(image_url)

openai.Image.create()でDALL・E 2のAPIを利用して画像を生成する。responseには画像URLがある。画像そのものはローカルには保存されないため注意。

パラメタについては下記の通り。

  • prompt: 画像の説明文
  • n: 生成する画像数
  • size: 画像サイズ(256x256, 512x512, 1024x1024)

生成する画像数と画像サイズにより、AIモデル利用料が変化するので注意。

そのほか、response_formaturlb64_jsonで取得するか選択可能。

2. Download a image with urllib.request

import openai
import urllib.request
openai.api_key = 'Your API Key'

def generate_image_with_dalle2(prompt):
    # Official API Reference: https://beta.openai.com/docs/api-reference/images
    response = openai.Image.create(
        prompt=prompt,
        n=1,
        size='{}x{}'.format(str(256), str(256))
    )
    image_url = response['data'][0]['url']
    return image_url

def download_dall2_image(image):
    with urllib.request.urlopen(image) as web_file:
        data = web_file.read()
        with open('./dall2.png', mode='wb') as local_file:
            local_file.write(data)

image = generate_image_with_dalle2('a white cat')
download_dall2_image(image)

DALL・E 2で作成した画像をローカルへDLするコード。urllibでWeb上の画像をopenできるので、openしたファイルをローカルファイルに書き込めばDLできる。

3. Create some images, and download.

import openai
import urllib.request
openai.api_key = 'Your API Key'

def generate_image_with_dalle2(prompt, n, size):
    size = str(size)
    response = openai.Image.create(
        prompt=prompt,
        n=n,
        size='{}x{}'.format(size, size)
    )
    datas = response['data']
    images = []
    for data in datas:
        print(data['url'])
        images.append(data['url'])
    return images

def download_dall2_image(image, n):
    with urllib.request.urlopen(image) as web_file:
        data = web_file.read()
        with open('./dall2_{}.png'.format(n), mode='wb') as local_file:
            local_file.write(data)

images = generate_image_with_dalle2('a white cat', 3, 256)
i = 0
for image in images:
    download_dall2_image(image, i)
    i = i + 1

一回の実行で複数の画像を生成するコード。ベースは#2のコード。generate_image_with_dalle2で生成した画像URLの配列を返し、download_dall2_imageで各画像URLをDLする。

以上。