discord botをdaemonにする流れ

# 前提
なぜかこの記事は準備編と別れています．[前編はこちら](https://qiita.com/halglobe0108/items/da65a7bc4956fc500d39)

さて，私の環境は
- WSL, Ubuntu22.04.1

となっています．他の環境だとどうなるかは正直わかりません．

daemonにする流れは以下を参考にしています．
- [pythonをデーモン化するメモ](https://qiita.com/croquisdukke/items/9c5d8933496ba6729c78)
- [[Linux] systemdのユニットファイルを自分で書いてプログラムをdaemon動作させてみる](https://zaki-hmkc.hatenablog.com/entry/2021/04/11/003202#Description)
- [Systemdを使ってさくっと自作コマンドをサービス化してみる](https://qiita.com/DQNEO/items/0b5d0bc5d3cf407cb7ff)

# discord botのプログラムにShebangを仕込む

とりあえずプログラムにShebangを仕込んで，実行権限を与えておきます．

```python: defaultbot.py
#!/usr/bin/env python3
import discord
import os

class MyClient(discord.Client):
    def __init__(self, *, intents: discord.Intents):
        super().__init__(intents=intents)

    async def on_ready(self):
        print(f"Logged in as {client.user} (ID: {client.user.id})")
        print("------")


intents = discord.Intents.default()
client = MyClient(intents=intents)
client.run(os.getenv("TOKEN"))
```

```bash: bash
$ chmod +x defaultbot.py
```

続いて，daemonの設定ファイルであるUnitファイルを書いていきます

# Unitファイル

[pythonプログラムをデーモン化する｜Unitに処理を記述しsystemdで実行する](https://super-vitality.com/python-daemon-unit-systemd/)という記事を参考にしています．

`testbot.service`というUnitファイルを`/etc/systemd/system/`に作成し，daemonを作っていきます．

```text: testbot.service
[Unit]
Description = test discord bot daemon

[Install]
WantedBy = multi-user.target

[Service]
EnvironmentFile = /home/user/directory/.env
ExecStart = /home/user/directory/defaultbot.py
```

[Service]のExecStartに実行したいファイルを**絶対パス**で指定します．また，環境変数を使用するので，それが書かれたファイルをEnvironmentFileに指定します[^env]．

<details><summary>multi-user.target</summary>

ちなみに，WantedByに指定したmulti-user.targetですが，[OS起動時にsystemdで行われていること](https://qiita.com/ledmonster/items/5f2e1633d4124cb978fe)という記事によると，GUIなしのアプリ起動に紐づいたものらしいです．
蛇足ですが中身も一応見れます．~~まあ見ても私にはなにもわかんないんですけど~~

```bash: bash
$ systemctl cat multi-user.target
# /lib/systemd/system/multi-user.target
#  SPDX-License-Identifier: LGPL-2.1-or-later
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes
```

</details>

これで準備が整いました．~~準備編は終わったんじゃなかった？~~

次にdaemonとして認識させましょう．

# systemctl
daemonとして認識されているか，および起動や停止，自動実行の設定には`systemctl`コマンドを利用します．
systemctlのコマンドの詳細については以下の記事を参考にしています．
- [システムサービスの管理](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/system_administrators_guide/sect-managing_services_with_systemd-services#sect-Managing_Services_with_systemd-Services-List)
- [debian systemdによるサービスの管理](https://hungry-bros.com/archives/1063)


まずは`systemctl list-unit-files --type service`を使って認識されているか確認しましょう．
というところで，WSLだとおそらくsystemctlが使えねえっていうエラーが出ると思います．PID1にsystemdが設定されてないよ，というエラーです．これの解決方法は[こちらの記事](https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/)に載っています．WSLのUbuntu上の`/etc/wsl.conf`に
```
[boot]
systemd=true
```

を記述し，WSLを再起動させるとできるようになります．[wsl.confについてはこちら](https://learn.microsoft.com/ja-jp/windows/wsl/wsl-config#wslconf)を見てください．
ただし，ここでWSLをシャットダウンさせて再起動させる際，8秒ほど待たないといけません．詳しくは[公式のドキュメント](https://learn.microsoft.com/ja-jp/windows/wsl/wsl-config#the-8-second-rule)を見てください．

というわけでこれで`systemctl`が使えると思います．試してみて`testbot.service`が表示されるか確認しましょう．

```bash: bash
$ systemctl list-unit-files --type service | grep testbot
testbot.service                            disabled        enabled
```

出てきましたね．次はこれを有効にしましょう．
```bash: bash
$ sudo systemctl enable testbot.service
Created symlink /etc/systemd/system/multi-user.target.wants/testbot.service → /etc/systemd/system/testbot.service.
```

できたら後は起動するだけです．
```bash: bash
$ sudo systemctl daemon-reload
$ sudo systemctl start testbot.service
$ sudo systemctl status testbot.service
● testbot.service - test discord bot daemon
     Loaded: loaded (/etc/systemd/system/testbot.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2022-12-18 00:40:04 JST; 18s ago
   Main PID: 1701 (python3)
      Tasks: 3 (limit: 9421)
     Memory: 22.8M
...
```

動きました．やったね．
停止するには，stopさせればいいです．
```bash: bash
$ sudo systemctl stop testbot.service
$ sudo systemctl status testbot.service
○ testbot.service - test discord bot daemon
     Loaded: loaded (/etc/systemd/system/testbot.service; enabled; vendor preset: enabled)
     Active: inactive (dead) since Sun 2022-12-18 00:41:48 JST; 3s ago
    Process: 1701 ExecStart=/home/......./defaultbot.py (code=killed, signal=TERM)
   Main PID: 1701 (code=killed, signal=TERM)

Dec 18 00:40:04 DESKTOP-CN6AQ4U systemd[1]: Started test discord bot daemon.
Dec 18 00:40:04 DESKTOP-CN6AQ4U defaultbot.py[1701]: [2022-12-18 00:40:04] [INFO    ] discord.client: logging in using static token
Dec 18 00:40:05 DESKTOP-CN6AQ4U defaultbot.py[1701]: [2022-12-18 00:40:05] [INFO    ] discord.gateway: Shard ID None has connected to Gateway (>
Dec 18 00:41:48 DESKTOP-CN6AQ4U systemd[1]: Stopping test discord bot daemon...
Dec 18 00:41:48 DESKTOP-CN6AQ4U systemd[1]: testbot.service: Deactivated successfully.
Dec 18 00:41:48 DESKTOP-CN6AQ4U systemd[1]: Stopped test discord bot daemon.
```

これでPCが起動しっぱなしであればdiscord botも起動しっぱなしになりました．

ちなみに蛇足ですが，私の環境だと，
```
$ explorer.exe .
run-detectors: unable to find an interpreter for /mnt/c/WINDOWS/explorer.exe
```

windowsのアプリがWSLでが使えなくなりました．

<details><summary>windowsのアプリがWSLで使えない</summary>

[こちらに載っている解決方法](https://github.com/microsoft/WSL/issues/4567)を試したところ，
- `wsl --shutdown`して再起動
    - 変わらず
- `export DISPLAY="$(/sbin/ip route | awk '/default/ { print $3 }'):0"` to ~/.bashrc
and‘ sourced it and `$ source ~/.bashrc`
- `sudo update-binfmts --disable cli`
    - 上2つを同時に実行
    - 使えるようになったけど，再起動で再び使えなくなる
- `[interop]`で`appendWindowsPath = true`にする
    - 変わらず

で変わりませんでした．`[automount]`の設定が怪しい気がしたので消してみましたが変わらずです．

解決策は`wsl.conf`で`[boot]`の`systemd=true`を削除すると使えるようになります．本末転倒ですね．

~~調べた結果．．．．わかりませんでした！~~
~~いかがでしたか？~~

</details>

以上となります．daemon起動の流れを紹介しました．



[^env]: [itohalってやつが書いた環境変数の記事](https://qiita.com/halglobe0108/items/735433531e7b17a20b54#%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%E3%81%AE%E8%A8%AD%E5%AE%9A)では`.profile`に環境変数を設定することができるらしいので，`.profile`に環境変数ファイルに指定して起動してみたらうまくいきませんでした．環境変数を認識してくれません．どうして．
`.bash_profile`ではうまくいきます．なんで`.profile`に環境変数を入れることをおすすめしてるんだ．
ちなみに，`.env`で動くのかなと思って試してみたらうまくいったのでそうやってます．
`.env`や`.bash_profile`と`.profile`の違いを考えたとき，性質の違いは置いておいて，exportかなと思います．前者では`token="000"`と記述(`.bash_profile`ではそこから`export token`)して，`.profile`では`export token="000"`としています．`echo`させるとどちらもうまくいきますが，daemonだと前者がうまくいきます．なんでだろ