これは,Fediverse (4) Advent Calendar 2023 の 12 月 22 日の記事です。21日目の記事は 💙mostrで近づいた、Fediverseとの距離 でした。
本稿の趣旨
おひとりさまインスタンスで自分宛にダイレクトメッセージを送ったら,自分だけが見られるロガーとして使えるんじゃない?
おひとりさまインスタンス
Mastodon はジャック・ドーシーの横暴に耐えかね,古き良き Twitter を実現するために生まれました。Twitter が抱える問題の原因は,ただひとりの支配者が SNS のすべてを掌握できてしまうからだと考えたのです。そのことは後にカレン氏によって実証され,人類は再び邪悪な支配者の前に破れることになります。Fediverse は分散化することによって,この問題を解決しようとしています。
新参者の私は Mastodon 以外の Fediverse プラットフォームについてはよくわかりませんが,Misskey や Pleroma など,Open Source 実装が公開されているシステムの場合,自前のサーバにインストールすることができます。自分だけがアカウントを作成できるように設定したインスタンスのことは,おひとりさまインスタンスと呼ばれます。
Logging モジュール
処理状況を追跡し動作検証するには記録を取ることが大切です。Python にはこの目的で作られた Logging モジュールがあります。例えば,以下のように使います。
import logging
logging.debug('足袋に玉を入れもうして,くるまを打ち付けるで候')
実際には getLogger
を使うべきとか作法がありますが,設定ファイルから書式を読み込めるなど機能も十分にあり,いざ実装するとめんどうなロガーを手軽に使うことができます。
PPAP
おひとりさまインスタンスには自分しかいません。その自分にダイレクトメッセージを送ると,他のサーバへ通知されることはありません。Mastodon は End-to-End Encryption ではないため,送受信者とその経路は暗号化されていません。しかし,おひとりさまインスタンス内であれば,自分以外にアクセスできないため,Slack に通知していたような機密性の水準の投稿はできそうです。
準備するもの
- おひとりさまインスタンス
- 投稿用 Access Token
おひとりさまインスタンスはいくつかの方法で準備できます。本稿をここまで読んだ方であれば,VPS をいくつか運用しているでしょうから,Docker Compose を使って稼働させるのがお手軽かもしれません。日本語のサポートがあるホスティングサービス(Hostdon 等)を利用する方法もあります。直近の Advent Calendar 19日目 おひとり様 Mastodon サーバーを支える技術 などをご参照ください。私は固定 IP アドレスを振った自宅 Linux サーバ上で,他のサービスとともに Docker コンテナで動作させています。
投稿用 Access Token は,Preferences | Development | New application から,write
にだけチェックを入れて作成します。
すると,このような雰囲気のものが生成されます。必要になるのは Your access token
とあるものだけです。
Client key
と Client secret
は API を利用するツールそれぞれに対応する,いわゆる ID/パスワードに相当する情報です。これらによりクライアントが認証されると,API リクエストに使うための Access Token
を生成することができます。今回は Mastodon のウェブインターフェイスを利用したため,これら 3 つが一度に生成されました。
ログの投稿方法
cURL を使う
以下のように curl
からインスタンスへアクセスすることで,Mastodon API を利用できます。
curl -X POST \
-H 'Authorization: Bearer {ACCESS_TOKEN}' \
-d 'visibility=direct' \
-d 'status={MESSAGE}'
https://{INSTANCE_NAME}/api/v1/statuses
ただし,
-
{INSTANCE_NAME}
: おひとりさまインスタンス 例:mstdn.jp -
{ACCESS_TOKEN}
: 上記の準備で得たアクセストークン -
{MESSAGE}
: 投稿するメッセージ@自分のID
を前置する
例えば,以下のようなリクエストを投げると,こんな感じで Private mention が自分に飛んできます。
curl -X POST \
-H 'Authorization: Bearer vvjPKgFtlbCsVVZqKzRWMk_jTOBEuc4urvX2on41tyw' \
-d 'visibility=direct' \
-d 'status=@rino 裏金がバレてしまった!' \
https://hello.world/api/v1/statuses
ダイレクトメッセージは Private mentions のページで一覧することができます。
私が使用している Ivory では,目立った感じの音と色で知らせてくれます。
Mastodon.py を使う
Python から投稿するのであれば,次のような雑ロガーを実装しておくと便利です。
mastodon.py
python-dotenv
ACCESS_TOKEN=
BASE_URL=https://
DESTINATION=@
#!/usr/bin/env python3
# logging for mastodon
import datetime
from mastodon import Mastodon
import time
class MLogging:
# Logging levels
Levels = {
'EMERG' : 80, 'EMERGENCY' : 80,
'ALERT' : 70,
'CRIT' : 60, 'CRITICAL' : 60,
'FATAL' : 60,
'ERROR' : 50,
'WARN' : 40, 'WARNING' : 40,
'NOTE' : 30, 'NOTICE' : 30,
'INFO' : 20,
'DEBUG' : 10,
}
# Constructer
# api_base_url url of the mastodon instance
# access_token api access token
# destination username to be sent (@user for local, @user@instance for remote)
# level minimum logging level
def __init__(self, api_base_url, access_token, destination, level='WARN', time_format='%Y-%m-%d %H:%M:%S'):
# Store given information to local variables
self.api_base_url = api_base_url
self.access_token = access_token
self.destination = destination
self.level = level if level.upper() in self.Levels else 'WARN'
self.time_format = time_format
self.retry_count = 10
self.retry_interval = 2
# Create mastodon object
self.mastodon = Mastodon(
api_base_url = self.api_base_url,
access_token = self.access_token,
)
# Send status directly to a user
# level logging level
# status message
# media list of images (specify empty list if no image)
def __send_direct(self, level, status, media=[]):
# Set level to WARNING if the given level is not in the predefined list
if not level.upper() in self.Levels:
level = 'WARN'
# Send status only if the given logging level is above than criteria
if self.Levels[self.level] <= self.Levels[level.upper()]:
# Obtain current time and format
now = datetime.datetime.now()
dt = now.strftime(self.time_format)
# Upload media if media files are specified
media_files = []
if media:
for m in media:
count = 0
d = self.mastodon.media_post(m)
while count < self.retry_count and d['url'] is None:
count += 1
d = self.mastodon.media_update(id=d['id'])
time.sleep(self.retry_interval)
media_files.append(d)
# Send status directly to the user
self.mastodon.status_post(
'%s [%s][%s] %s' % (self.destination, level, dt, status),
visibility = 'direct',
media_ids = media_files if media else None,
)
# Wait
time.sleep(1)
def basicConfig(self, level='WARN', time_format='%Y-%m-%d %H:%M:%S'):
if level in self.Levels:
self.level = level
self.time_format = time_format
def emergency(self, status, media=[]):
self.__send_direct(level='EMERG', status=status, media=media)
def alert(self, status, media=[]):
self.__send_direct(level='ALERT', status=status, media=media)
def critical(self, status, media=[]):
self.__send_direct(level='CRIT', status=status, media=media)
def fatal(self, status, media=[]):
self.__send_direct(level='FATAL', status=status, media=media)
def error(self, status, media=[]):
self.__send_direct(level='ERROR', status=status, media=media)
def warning(self, status, media=[]):
self.__send_direct(level='WARN', status=status, media=media)
def notice(self, status, media=[]):
self.__send_direct(level='NOTE', status=status, media=media)
def info(self, status, media=[]):
self.__send_direct(level='INFO', status=status, media=media)
def debug(self, status, media=[]):
self.__send_direct(level='DEBUG', status=status, media=media)
def main():
from dotenv import load_dotenv
import os
# Load the environment variables from .env file
load_dotenv()
mlog = MLogging(
api_base_url = os.getenv('BASE_URL'),
access_token = os.getenv('ACCESS_TOKEN'),
destination = os.getenv('DESTINATION'),
)
mlog.basicConfig(level='DEBUG')
mlog.critical('CRITICAL MESSAGE')
mlog.error('file not found', media=['./image1.png', './image2.png'])
mlog.warning('WARNING MESSAGE')
mlog.info('INFO MESSAGE')
mlog.debug('DEBUG MESSAGE')
return 0
if __name__ == "__main__":
main()
まとめ
いくつか Bot を稼働させているのですが(例えば 今年の残り日数),失敗したときなどに,ローカルのログへエラーを書き出しただけではいまいち気づかず,ホームタイムラインへ通知されたら便利だなあと思って試してみた次第です。
うちには爬虫類のペットがおり,一年中 28 ℃くらいに維持しているんですが,空調の停止によりかわいい爬虫類が苦しんでしまうなど耐えがたいため,室温変動を警告するといった用途でも使えそうです。
参考になれば幸いです。