« ^ »

Gnusの簡単なバックエンドを実装する

所要時間: 約 5分

外部とのコミュニケーションのツールはプロジェクト毎に色々なものを採用するようになっている。そうであっても、文章を誰から伝えてもらい、また誰かに伝えるという部分は変わらない。コミュニケーションツールなのだから、それはそうだろう。そうであるなら、何故こうも色々なツールを使い分けないといけないのだろうか。そして、そのツールの使用を強制されなければならないのだろうかと思えてくる。

その結果、どのツールにも対応可能な柔軟なコミュニケーションのためのインターフェースが必要になる。Emacsはその選択肢の一つであり、外部のやり取りをどのようにすればストレスを感じることなく迅速に行なえるかということを工夫している。

Emacsには幾つかのメールリーダーの実装があり、それらを用いることもできる。また代表的なニュースリーダーの実装としてGnusがあり、ELPAに登録されている。さまざまなバックエンドの実装によりNNTPだけでなく、他のデータソースからの情報を管理できる。

今回はGnusバックエンドを学ぶため、簡単なバックエンドnnsimpleを実装した。実装したといっても、実態はnnnilバックエンドとほぼ同様であり、docstringに解説を加えたものとなっている。

;;; nnsimple --- A simple backend for Gnus  -*- lexical-binding: t; -*-

;;                               __
;;                             .d$$b
;;                           .' TO$;\
;;                          /  : TP._;
;;                         / _.;  :Tb|
;;                        /   /   ;j$j
;;                    _.-"       d$$$$
;;                  .' ..       d$$$$;
;;                 /  /P'      d$$$$P. |\
;;                /   "      .d$$$P' |\^"l
;;              .'           `T$P^"""""  :
;;          ._.'      _.'                ;
;;       `-.-".-'-' ._.       _.-"    .-"
;;     `.-" _____  ._              .-"
;;    -(.g$$$$$$$b.              .'
;;      ""^^T$$$P^)            .(:
;;        _/  -"  /.'         /:/;
;;     ._.'-'`-'  ")/         /;/;
;;  `-.-"..--""   " /         /  ;
;; .-" ..--""        -'          :
;; ..--""--.-"         (\      .-(\
;;   ..--""              `-\(\/;`
;;     _.                      :
;;                             ;`-
;;                            :\
;;                            ;

;; Copyright (C) 2022 TakesxiSximada

;; Author: TakesxiSximada <[email protected]>
;; Version: 1
;; Package-Version: 20220623.0000
;; Package-Requires: ((emacs "28.1"))
;; Date: 2022-06-23

;; This file is part of nnsimple.

;; nnsimple is free software: you can redistribute it and/or
;; modify it under the terms of the GNU Affero General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.

;; nnsimple is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;; Affero General Public License for more details.

;; You should have received a copy of the GNU Affero General Public
;; License along with this program.  If not, see
;; <https://www.gnu.org/licenses/>.

;;; Commentary:

;; nnnil is a Gnus backend that provides no groups or articles.  It's useful
;; as a primary select method when you want all your real select methods to
;; be secondary or foreign.

;;; Code:

(defvar nnsimple-status-string "")


(defun nnsimple-open-server (_server &optional _definitions)
  "サーバーへ接続する。

_serverは仮想サーバー名を指定する。_definitionsは(VARIABLE VALUE)ペア
のリストで、仮想サーバーを指定する。サーバーへの接続に失敗した場合であっ
てもエラーは通知しない。サーバーへの接続を再試行するかどうかを選択でき
る。サーバーへの接続が既に確立されている場合、nil以外の値を返す。"
  t)

(defun nnsimple-close-server (&optional _server)
  "サーバーへの接続を閉じる。

サーバーへの接続を閉じ、サーバーに接続されているすべてのリソースを解放
する。何らかの理由でサーバーを閉じることができなかった場合は、nilを返
す。"
  t)

(defun nnsimple-request-close ()
  "すべてのサーバーへの接続を閉じ、バックエンドが予約したすべてのリソー
  スを解放する。

そのバックエンドによって作成されたすべてのバッファを強制終了する必要が
ある。ただし、nntp-server-bufferではありません。値は返さなくても良い。"
  t)


(defun nnsimple-server-opened (&optional _server)
  "サーバーが現在の仮想サーバーであり、物理サーバーへの接続が有効であ
  る場合、nil以外の値を返す。

この関数では接続を失ったサーバーへの再接続を試みてはいけない。
値は返さなくても良い。"
  t)

(defun nnsimple-status-message (&optional _server)
  "常にサーバーからの最後のメッセージを返す。

値は返さなくても良い。"
  nnsimple-status-string)

(defun nnsimple-request-article (_article &optional _group _server _to-buffer)
  "articleで指定された記事を返す。

_articleはメッセージIDまたは番号を指定する。Message-IDによる取得を実装
するかどうかは任意である。to-bufferがnil以外の場合、通常のデータバッファ
ではなく、このバッファーに返す。これは、あるバッファから別のバッファに
大量のデータをコピーすることを回避できるようにするため。一方、Gnusは主
に、記事を記事バッファに直接挿入するように要求する。可能であれば、この
関数はconsセルを返す必要がある。ここで、carは記事の取得元のグループ名
であり、cdrは記事番号となる。これにより、Gnusは、Message-IDで記事を取
得するときに、実際のグループ番号と記事番号を確認できる。これが不可能な
場合は、記事の取得が成功したときにtを返す必要がある。"
  (setq nnsimple-status-string "No such groupaaa")
  nil)

(defun nnsimple-request-group (_group &optional _server _fast _info)
  "グループのデータを取得する。

グループのデータを取得する。また取得したグループをカレントグループとし
てセットする。
_fastを指定している場合、グループをカレントグループにするだけでよい。
_infoを指定している場合、バックエンドがグループ情報構造を更新できるよ
うにする。"
  (let (deactivate-mark)
    (with-current-buffer nntp-server-buffer
      (erase-buffer)
      (insert "411 no such news group\n" )))
  (setq nnsimple-status-string "No such group")
  ni)


(defun nnsimple-close-group (_group &optional _server)
  "グループを閉じる。

グループを閉じ、それに接続されているすべてのリソースを解放する。ほとん
どのバックエンドでノーオペレーションとなる。この関数は値を返さなくてよ
い。"
  t)

(defun nnsimple-request-list (&optional _server)
  "サーバーで使用可能なすべてのグループのリストを返す。"
  t)

(defun nnsimple-request-post (&optional _server)
  "この関数は現在のバッファをポストする。

現在のバッファをポストする。投稿が成功したかどうかを返が、必須ではない。
投稿処理が非同期で行われる場合、この関数が終了したからといって、投稿処
理が完了しているとは限らない。投稿処理に失敗した場合、ユーザーにビープ
音を鳴らしクリアするために、センチネルを設定する必要がある。この関数は
値を返さなくてよい。"

  (setq nnsimple-status-string "Read-only server")
  nil)

(provide 'nnsimple)
;;; nnsimple.el ends here