Pythonでマイクラ鯖のログイン通知をwebhookで行う

##  2024/04/06 更新
ログ取得ロジックを変更

## Minecraftサーバーのログイン通知をwebhookで送る方法
Minecraftサーバーのlosgsフォルダ内のlatest.logにログイン情報などが記載される。
そのため、logsフォルダのlatest.logファイルが更新されたタイミングでファイルの中身を送信すれば、ログイン通知になるはずである。
今回は例としてdiscordのwebhookを使用するが、Slackなどでも同じ事が出来るので、
使用する場合は下記のソースの一部を書き換えて欲しい

## なぜPythonなのか
多くのマイクラサーバはWindowsもしくはLinuxで運用されていると思う。
その中でLinuxで一番使われているubuntuはデフォルトでPythonがインストールされている為、採用した。
そしてPythonのフォルダ監視ライブラリであるwatchdog使用して簡易的なログイン通知システムを作成した。


## 動作環境
Python 3.11.0
watchdog 4.0.0

## ソース
```python:main.py
import os
import re
import time

import requests
from watchdog.events import FileSystemEventHandler
from watchdog.observers.polling import PollingObserver

from config.app import TARGET_DIR, TARGET_FILE, WEBHOOK_URL
from utils.log_message_parser import message_creation


class MinecraftLogMonitor:
    def __init__(self, target_dir, target_file, webhook_url):
        self.target_dir = target_dir
        self.target_file = target_file
        self.webhook_url = webhook_url
        self.log_position = 0

    def send_message(self, message: str) -> None:
        main_content = {"content": message}
        response = requests.post(self.webhook_url, json=main_content)
        response.raise_for_status()

    def get_log(self, filepath: str):
        with open(filepath, "r", errors="ignore") as f:
            f.seek(self.log_position)
            logs = f.readlines()
            self.log_position = f.tell()

        for log in logs:
            text = message_creation(log)
            if text:
                self.send_message(text)


class ChangeHandler(FileSystemEventHandler):
    def __init__(self, monitor: MinecraftLogMonitor):
        self.monitor = monitor

    def on_modified(self, event):
        filepath = event.src_path
        if (
            os.path.isfile(filepath)
            and os.path.basename(filepath) == self.monitor.target_file
        ):
            self.monitor.get_log(filepath)


if __name__ == "__main__":
    monitor = MinecraftLogMonitor(TARGET_DIR, TARGET_FILE, WEBHOOK_URL)
    event_handler = ChangeHandler(monitor)
    observer = PollingObserver()
    observer.schedule(event_handler, monitor.target_dir, recursive=True)
    observer.start()
    try:
        while True:
            time.sleep(0.1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()
```

```python:utils/log_message_parser.py
import re

MESSAGE_PATTERNS = [
    (": (.*) joined the game", "{0} が参加しました"),
    (": (.*) left the game", "{0} が退出しました"),
    # イベントを追加したい場合はここに追加する
]


def message_creation(text: str):
    for pattern, message_format in MESSAGE_PATTERNS:
        match = re.search(pattern, text)
        if match:
            return message_format.format(*match.groups())
    return None
```

```python:config/__init__.py
from dotenv import load_dotenv

load_dotenv()
```

```python:config/app.py
import os

WEBHOOK_URL = os.getenv("WEBHOOK_URL")
TARGET_DIR = os.getenv("TARGET_DIR")
TARGET_FILE = os.getenv("TARGET_FILE")
```

```env:.env
WEBHOOK_URL=discordのwebhookのURL
TARGET_DIR=監視対象のフォルダ
TARGET_FILE=latest.log
```
\_\_init\_\_.pyを使ってload_dotenv()を使うのは綺麗なのでかなり気に入っている

Githubにもソースをおいています。
https://github.com/somakai-sumasi/Send-Minecraft-notifications