RaspberryPiでPBXとFAXの構築 - 迷惑電話対策

投稿 2024/11/09 (土) 午後 05:33 | パソコン | hotall

昔は良かった。
年配の人が(私もその一人ですが)、よく口にする言葉です。
最近、殺伐としたニュースを見るにつけ、そう思うことが増えました。


情報通信の世界でも、迷惑メール、迷惑広告、迷惑電話、フィッシング詐欺、ランサムウェア、嫌ななことばかり増えているように思えます。

インターネットが出来た当時は、性善説が前提で、自由に安心して利用できるものでした。それが今では、相手を疑う姿勢でないと、危なくて使えない時代になりました。

いっそのこと、情報を遮断してしまえば、悩まされずにすむのではと思いながら、この情報化社会で生きていくには、やはり利用するしかないようです。

先日、家の固定電話に迷惑電話が掛かってきました。過去にも掛かってきた業者です。断れば、それで済むのですが、嫌な思いは残ります。
その時、改めて、一度断った相手には、自動的に着信を拒否したいと思いました。
嫌な思いをしないためには、迷惑電話の対策が必用なのです。

過日、我家にPBXシステムを構築しましたが、そのソフトウェア:Asteriskにはblacklistの機能があります。これを利用して電話番号を登録し、着信時の動作を制御できます。

迷惑電話対策にはこの機能でも良いのですが、コンソールからのコマンドで管理しなければならないのが残念です。

個人的には、テキストファイルで、もっと直感的に、簡単に管理がしたいのです。
つまり、SSHやSMBなどを経由して、エディタを使って電話番号を登録したり、更には着信履歴を反映して、入力作業を省力化できるようにしたいのです。

そこで、AGIと呼ばれるAsteriskと外部プログラムの連携機能を利用して、これを実現することにしました。

この文書は個人的なものであり、内容について一切の責任を負いかねます。






今回作成するプログラムの仕様は次の通り
  • 着信があれば、このプログラムが呼び出される。
  • 渡された電話番号で着信電話番号リストファイルを検索し、事前に登録された判定区分をAsteriskに返す。
  • もし見つからなければ、新たにデータを判定区分を不明としてリストファイルに追加する。
  • 検索または追加したデータについて、最新着信日時と着信回数を更新し、ファイルの先頭に配置/移動する。これによりデータは新着順に並びます。
  • 迷惑電話の判定は、人が行い、リストファイルに追記する。
以下が今回作成したプログラムです。今回はPython3で作成しました。
#!/usr/bin/env python3
""" 迷惑電話判定 AGIスクリプト

着信電話番号が迷惑電話か否かを判定する。
判定結果は指定された変数に設定(クォートで囲まない)
'OK':迷惑電話ではない
'NG':迷惑電話
'' :不明

着信電話番号の判定は、着信電話番号リストファイルを検索し、
その判定区分(OK/NG/)で判断する。
ファイルに無い場合は、判定区分を不明:''としてファイルの先頭に追加する。
検索された着信電話番号のデータは日付時刻(yyyy/mm/dd hh:mm:ss)と回数を設定して
ファイルの先頭に移動する。
着信電話番号リストファイルのフォーマット:テキストファイル(utf-8)
<着信電話番号>,<判定区分>,<最新着信日時>,<回数>,<コメント>LF
<着信電話番号>,<判定区分>,<最新着信日時>,<回数>,<コメント>LF
...

Arguments
----------
resvar : str
判定結果を格納する変数名。デフォルト:'MEIWAKU_CHK'

Returns
-------
int
0 : 正常終了 その他 : 処理失敗

Example
-------
exten => s,n,AGI(meiwaku_check.py,meiwaku_chk)
same => n,GotoIf($["${meiwaku_chk}" = "NG"]?meiwaku)

Notes
-----
判定区分はテキストエディタで着信電話番号リストファイルに追記する。
着信電話番号リストファイルのパスは変数:CALLER_LIST_FILEに格納。

"""
__author__ = "Yasuaki Nojiri"
__version__ = "1.0.0"
__date__ = "2024/10/30"

import sys
import os
import stat
from datetime import datetime

# パッケージのインストールディレクトリと参照ディレクトリが一致しない場合
# pyst2パッケージインストールディレクトリを追加
# "pip show pyst2"コマンドでインストール先を確認する
sys.path.append('/home/pi/.local/lib/python3.9/site-packages')
from asterisk.agi import *

# 定数の設定
CALLER_LIST_DIR = '/usr/local/share/meiwakuchk'
CALLER_LIST_FILE = CALLER_LIST_DIR + '/' + 'caller-list.txt'
CALLER_LIST_FILE_NEW = CALLER_LIST_FILE + '.new'
CALLER_LIST_FILE_PRM = stat.S_IRWXU | \
stat.S_IRGRP | stat.S_IWGRP | \
stat.S_IROTH | stat.S_IWOTH
DEFAULT_RECORD = ['','','','0','']
RECORD_LEN = len(DEFAULT_RECORD)


def main(args):
"""メイン関数

Parameters
----------
args : array
コマンドライン引数

Returns
-------
int
終了コード

"""
resvar_val = ''

# コマンドライン引数
resvar = args[1] if len(args) > 1 else 'MEIWAKU_CHK'

# AGIの生成
agi = AGI()

# 着信情報の取得
caller_id = str(agi.env['agi_callerid']) # 着信電話番号

agi.verbose("meiwaku_check agi start : " + caller_id)

# 着信電話番号リストファイルの準備
os.makedirs(CALLER_LIST_DIR, exist_ok=True)
if not os.path.isfile(CALLER_LIST_FILE):
# 存在しない場合は、空ファイルを作成
with open(CALLER_LIST_FILE,'w'):pass

# 着信電話番号リストファイルの検索
resrec = None
resrec_index = -1
with open(CALLER_LIST_FILE, mode='r') as file_in:
# 1行毎に読込む
for i, rec_i in enumerate(file_in):
record = rec_i.split(',')
if len(record) > 0 and record[0] == caller_id:
# 見つかった場合、必要な情報を退避
resrec_index = i
resrec = record
resrec[-1] = resrec[-1].removesuffix('\n')
# 不足項目があれば、デフォルト値で埋める
for i in range(len(resrec),RECORD_LEN): resrec += [DEFAULT_RECORD[i]]
break

if resrec is None:
# 見つからなかった場合、新規に作成
resrec = DEFAULT_RECORD.copy()
resrec[0] = caller_id

# 最新着信日時の更新
resrec[2] = datetime.now().strftime("%Y/%m/%d %H:%M:%S")

# 回数の更新
resrec[3] = str(int(resrec[3]) + 1) if resrec[3].isdecimal() else '1'

# 着信電話番号リストファイルの更新
with open(CALLER_LIST_FILE, mode='r') as file_in, \
open(CALLER_LIST_FILE_NEW, mode='w') as file_out:
# 今回のデータを先頭にする
print(*resrec, sep=',', end='\n', file=file_out)

# 元のファイルの内容を出力
for i, rec_i in enumerate(file_in):
if i != resrec_index:
# 今回の検索データ以外を出力
print(rec_i, end='', file=file_out)

# 権限の設定
os.chmod(CALLER_LIST_FILE_NEW, CALLER_LIST_FILE_PRM)

# 更新ファイルに置換
os.remove(CALLER_LIST_FILE)
os.rename(CALLER_LIST_FILE_NEW, CALLER_LIST_FILE)

# 判定結果を変数に格納
agi.set_variable(resvar,resrec[1])

agi.verbose("meiwaku_check agi results : " + resvar + " = " + resrec[1])

return 0

if __name__ == "__main__":
# トップレベル環境で実行された場合、メイン関数を呼ぶ
sys.exit(main(sys.argv))

このプログラムをインストールしていきます。

Pythonとパッケージ管理ソフト(pip3)をインストールします。
RaspberryPi OSでは、これらのソフトウェアはデフォルトでインストールされていますので、特に作業は必用ありません。

Python用のAGIライブラリをインストールします。
pip3を使ってAGIライブラリ:pyst2をインストールします。
pip3 install pyst2

作成したユーザープログラムを配置します。
場所:/usr/share/asterisk/agi-bin
ファイル名:meiwaku_check.py

ユーザープログラムの権限を設定します。
パーミッションの設定:
chmod 755 meiwaku_check.py

ダイヤルプランを変更します。
処理内容は次の通り
  • 電話番号をユーザープログラムに渡し、判定結果を受取る。
  • 判定結果がNGであれば、子機への呼出は行わず、留守番電話に切り替える。
  • NGでなければ、通常の処理を行う。
ファイル:/etc/asterisk/extensions.conf
変更内容:外線着信の処理の変更
[from-hikari]
exten => s,1,AGI(meiwaku_check.py,meiwaku_chk)
same => n,GotoIf($["${meiwaku_chk}" = "NG"]?meiwaku)
same => n,Dial(PJSIP/1001&PJSIP/1002,20,Tt)
same => n,Answer()
same => n,Wait(1)
same => n,Voicemail(5000)
same => n,Hangup
; Meiwaku denwa
same => n(meiwaku),Ringing()
same => n,Wait(8)
same => n,Answer()
same => n,Wait(1)
same => n,Voicemail(5000)
same => n,Hangup
5000は留守電、1001と1002は子機の内線番号です。実際の運用に合わせてください。

着信電話番号リストファイルのディレクトリを作成
場所:/usr/local/share/meiwakuchk
パーミッションの設定:
chmod 766 meiwakuchk

テストと確認
再起動して、外線から着信テストをします。
着信電話番号リストファイルが作成され、電話番号が登録されていれば、正常です。

テキストエディターでこの電話番号の判定をNGと追記し、再度、テストしてみます。
子機が着信せずに、留守番電話になれば、正常です。

着信電話番号リストファイルには、最新着信日時と着信回数が更新されているはずです。

トラブルと対応
今回のシステム構築でのトラブルは、AGIライブラリのインポートができなかったことです。
現象としては、プログラム中のAGIライブラリ(asterisk.agi)のインポート行がNot foundエラーになりました。
これは、pipでインストールしたpyst2のインストール先がpythonから参照できなかったためです。

プログラムを見ていただければ分かると思いますが、インポートする前に、参照先リストにインストール先のディレクトリを追加しています。
ただ、この現象は当方の環境のみかもしれません。

これで、少しは迷惑電話に悩まされることが減るでしょうか。
業者さん、一度断られた相手には掛けないでくださいね。



■ RaspberryPiでPBXとFAXの構築 - 目次 -
  1. 構成
  2. ひかり電話RV-230SEの設定
  3. OSのセットアップ
  4. LANアダプタの接続
  5. ユーティリティのセットアップ
  6. PBXのセットアップ
  7. ソフトウェアモデムのセットアップ
  8. FAXのセットアップ
  9. 自動再起動
  10. ステータスLEDの接続
  11. ケースに収納・設置
  12. トラブルと対応
  13. ▶迷惑電話対策
« Prev • Next »

コメント

コメントはありません。

コメントする