Open18

令和にもなって VoIP 電話網を作ろうとしているお前らへ

ピン留めされたアイテム
KusaReMKNKusaReMKN

注意

このスクラップの内容は、後に修正・更新されて記事として投稿されるかもしれません。

投稿者へのお願い

このスクラップの内容を記事にする場合、元となったスクラップの投稿を削除しないでください。 該当するスクラップの冒頭部分に message 構文で記事として公開された旨を記述し、スクラップのうち記事として公開された部分を details 構文で折り畳むか、あるいはスクラップそのものを非表示にしてください。 元の投稿を削除した場合、その投稿を参照していた読者がいた場合に混乱を招きます。

<!-- 以下を追加する -->
:::message
このスクラップは記事[○○の解説](https://example.com/hogehoge)として公開されました。
:::

<!-- details で折り畳むには以下のように記述する -->
:::details もともとのスクラップの内容
<!-- ここにスクラップの内容 -->
:::
Hidden comment
yudeyude

カンファレンスルームや Echo-back service などを外線から受け付けられるようにする

  • カンファレンスルームについては、作成時に内線番号 / 直通番号 (DID) を割り当てるため、着信ルールでこれを受け付けるようにします。

  • Echo-back service などのアプリケーションを受け付けるには、着信ルールでどの内線番号 / 直通番号 (DID) を受け取ったらそのアプリケーションへルーティングするかを定義します。

優先度設定において、上記の例外的な番号をまずフィルターするようにし、最後に、リアルな宛先 (ユーザー) が存在する内線番号 / 直通番号 (DID) へルーティングするようにします。
KusaReMKN 氏の環境では、直通番号へルーティングするためのルールを新しく作成していますが、以下のように「着信デフォルトルート」として定義することもできます。

Hidden comment
KusaReMKNKusaReMKN

Linux 用 FAX ユーティリティ

ハードウェアモデムなどが接続されている場合、efax が便利です。GUI のフロントエンドを持つ efax-gtk はもっと便利です。これは USB モデムの上で動作することを確認しました。シリアルケーブル越しの物理外付けモデムではうまく動かないようです(たぶんコマンドが異なる?)

Hidden comment
Hidden comment
Hidden comment
yudeyude

Discord の音声チャンネルと MikoPBX (Asterisk) のカンファレンスをブリッジする

用意するもの

  • Microsoft Windows の VM
    • 検証環境では Proxmox VE 上に Windows の VM を用意しています
  • MikoPBX
    • ブリッジに使用するカンファレンス
    • ブリッジに使用する内線番号 (内線アカウント)
  • あなたが管理者権限を持っている Discord サーバ
    • このブリッジに使用する音声チャンネルを事前に作成してください
  • Discord ボットのトークン
    • https://discord.com/developers から取得できます
    • Discord ボットは、ブリッジに使用する音声チャンネルが作成されている Discord サーバに参加させてください

手順

  1. Windows に以下のアプリケーションをインストールします

VB-Audio Virtual Audio Cable, VB-Audio VoiceMeeter 両方のインストールが完了したら、一度 Windows を再起動してください。

  1. Discord Audio Stream Bot を設定する

Discord Audio Stream Bot は、Windows のサウンド デバイスを介して他のアプリケーションと音声を送受信し、Discord の音声チャンネルにブリッジするものです。

  • Settings タブ
    • General
      • Bot token: Discord から取得したボットのトークンを入力します
      • Auto login: オン
    • Audio
      • Mute/Unmute: マイクとスピーカーが、どちらもミュートされていない状態にします。
      • Input device: VoiceMeeter Output (VB-Audio Vo
      • Output device: CABLE Input (VB-Audio Virtual Cable)
    • Voice activity: オフ
    • Speak threshold: 変更しない (この値は使用されない)
  1. MicroSIP を設定する

MicroSIP は、Windows 向け SIP (VoIP) クライアント アプリケーションです。

  • 設定
    • スピーカー: VoiceMeeter Input (VB-Audio Voi
    • マイク: Cable Output (VB-Audio Virtual
    • システム起動時に実行: オン
  • アカウントの追加
    • アカウント名: ブリッジに使用する内線番号 (内線アカウント)
    • SIP サーバ: MikoPBX の IP アドレス / ホスト名
    • ユーザ名: ブリッジに使用する内線番号 (内線アカウント)
    • ドメイン: MikoPBX の IP アドレス / ホスト名
    • ログイン: ブリッジに使用する内線番号 (内線アカウント)
    • パスワード: ブリッジに使用する内線番号 (内線アカウント) のパスワード
  1. MikoPBX からカンファレンスの番号をコールする
  2. Discord Audio Stream Bot を起動する
    • Home タブの 電源 ボタンを押下する。
    • Discord サーバ内で、以下のボット コマンドを実行する
      • /autojoin <op: set> <channel: ブリッジに使用する音声チャンネル>
      • /join <channel: ブリッジに使用する音声チャンネル> <public: True>
JiminyJiminy

DIALSの卵

DIALS

概要

このダイアルプランアプリケーションのコードは、MikoPBX環境で通話中にユーザーが入力したDTMF(プッシュボタンの数字)を受け取り、「**#」が押されたら終了し、入力内容を一桁ずつ音声で再生する処理を行います。音声が再生されない場合は、MikoPBX環境にて音声ファイルが適切に配置されているか確認してください。

コード

<?php
    require_once 'Globals.php';
    
    use \MikoPBX\Core\Asterisk\AGI;
    $agi = new AGI();
    
    $agi->answer();
    
    $agi->exec('Playback', 'beep');

    // DTMF入力を待機(タイムアウト: 10秒)
    $digits = '';
    $end_sequence = '**#'; // 終了シーケンス
    $end_sequence_length = strlen($end_sequence);
    while (true) {
        $result = $agi->wait_for_digit(-1); // -1 無制限に繰り返し
        if ($result['result'] == -1) {
            // タイムアウトまたはエラー
            $agi->verbose("エラーが発生");
            break;
        } else {
            // 入力されたキーASCIIで取得
            $digit = chr($result['result']);
            $agi->verbose("押したキー: $digits");
            
            // 入力されたキーを保存
            $digits .= $digit;
            
            // 保存された入力が終了シーケンスで終わるか確認
            if (substr($digits, -$end_sequence_length) === $end_sequence) {
                $agi->verbose("終了シーケンス '$end_sequence' が入力されました。");
                // 終了シーケンスが入力された場合、ループを終了
                break;
            }
        }
    }
    
    // 入力されたDTMF全体をログに記録
    $agi->verbose("DTMF: $digits");   
    
    // ここに計算するようにしてほしいな
    // もし、外部のコードを実行するなら
    // $agi->exec("AGI", '外部コードパス,$digits');
    // でできるはず(わからない時は聞いて一緒に考えるよ)。

    // 入力された各DTMFを一桁ずつ再生
    for ($i = 0; $i < strlen($digits); $i++) {
        $digit = $digits[$i];
        if ($digit == '#') {
            $agi->exec("Playback", 'digits/pound');
        } elseif ($digit == '*') {
            $agi->exec("Playback", 'digits/star');
        } else {
            $agi->exec('SayNumber', $digit);
        }
    }
    
    // 通話を終了
    $agi->hangup();
?>

その他

MikoPBX環境では、デフォルトで「**」が内部転送の処理呼び出しに使用されています。そのため、「**#」と競合し、終了操作を行おうとすると転送が実行されてしまいます。この問題を回避するためには、MikoPBXの設定を変更する必要があります。WEB UIの「General settings」にある「Call Transfers」セクションで、以下の2つの設定を変更してください。(これ以外もあるかもしれません)

  • 「This key combination activates an attended transfer」(アテンド転送を起動するキー)
  • 「This key combination activates a blind transfer」(ブラインド転送を起動するキー)

設定例として、「####」や「##**」などのキー組み合わせを指定できます。以下に設定画面の例を示します。

参考

https://docs.asterisk.org/Asterisk_18_Documentation/API_Documentation/AGI_Commands/wait_for_digit/

yudeyude

IPv4 / IPv6 両方で SIP ポートをリッスンする

MikoPBX の既定の設定では、IPv4 のみがリッスンされています。先進的な考えを持つ方が IPv6 上で MikoPBX を利用するには、以下のようにします。

  • /etc/asterisk/pjsip.conf を「ファイルの最後に追加」モードで「システムファイルのカスタマイズ」を行います。
  • 「変更を加える編集者」欄に以下を追記します。
[transport-udp-v6]
type = transport
protocol = udp
bind=[::]:5060

[transport-tcp-v6]
type = transport
protocol = tcp
bind=[::]:5060
yudeyude

発信者番号を通知する

  • 着信用のテレフォニープロバイダーに、以下の 追加オプション を追記します
[endpoint]
trust_id_inbound=yes
  • 発信用のテレフォニープロバイダーに、以下の 追加オプション を追記します
    • こちらに関しては、テレフォニープロバイダーが複数存在することが想定されますが、そのすべてに追記します。
    • 発信者番号を意図的に秘匿したい場合、特定のテレフォニープロバイダーに追記しないこともできます。
[endpoint]
send_pai=yes
trust_id_outbound=yes
yudeyude

MikoPBX のダイヤルプランアプリケーションで Fax を受信する

着信用テレフォニープロバイダーの修正

テレフォニープロバイダー from-any の Advanced Options に以下を追加します。既に設定が存在する場合はよしなにしてください。

[endpoint]
fax_detect=yes
fax_detect_timeout=30

Fax 関連モジュールの追加

システムファイルのカスタマイズから、/etc/asterisk/modules.conf の「ファイルの最後に追加」します

load => res_fax.so
load => res_fax_spandsp.so

ダイヤルプラン アプリケーションを追加

コードタイプが PHP-AGI スクリプト のダイヤルプラン アプリケーションを作成します。
ソースコードは以下に示します。

<?php
require_once 'Globals.php';

use \MikoPBX\Core\Asterisk\AGI;

$agi     = new AGI();
$faxFile = "/tmp/" .$agi->get_variable("CDR(linkedid)", true).'.tiff'; // 一時ファイル
$caller  = $agi->get_variable("CALLERID(num)", true); // 発信者番号

// 保存先ディレクトリ
$saveDir = "/mikopbx/fax_files/";
if (!file_exists($saveDir)) {
    mkdir($saveDir, 0777, true); // 保存ディレクトリがない場合は作成
}

// FAX受信の実行
$agi->exec("ReceiveFax", "{$faxFile},d"); 
$result  = $agi->get_variable("FAXOPT(status)", true); // 受信結果を取得

if ($result === 'SUCCESS' && file_exists($faxFile)) {
    // 保存用ファイル名を設定
    $savedFile = $saveDir . date("Ymd_His") . "_from_{$caller}.tiff";

    // 一時ファイルを保存先に移動
    rename($faxFile, $savedFile);

    $agi->verbose("FAX successfully saved to {$savedFile}");
} else {
    $agi->verbose("FAX reception failed or file not found.");
}

sleep(1);
  • compose.yaml の編集
    compose.yaml を編集し、受信した Fax のデータを永続化するようにします。
    上記のソースコードでは、コンテナ内のパスであるところの /mikopbx/fax_files に保存するようにしていますから、これを Docker Engine を稼働させているホスト側に volumes で露出させてください。
KusaReMKNKusaReMKN

MikoPBX と SIP クライアントとが同一のホスト上にあると正常に動作しない

標記の通りです。別のホストで動作させましょう。

Keito TobichiKeito Tobichi

テスト番号の実装方法

  1. 再生させたい音声を録音
  2. MikoPBXへ音声をアップロード
    1. 左メニュー「サウンドファイル」を選択
    2. 「新しいサウンドファイルを追加」からファイルをアップロード
  3. アップロードしたファイルパスをメモ帳などに控える
  4. 確認方法はアップロードしたファイルの右側アイコン編集を開く
  5. ダイヤルプランアプリケーションの追加
    1. 「モジュール」項目の「ダイヤルプランアプリケーション」を開く
    2. アプリケーション設定を下記のように設定
    3. プログラムコードを下記のように記述。このときに先ほど控えたファイルパスを記述。※拡張子は不要

アプリケーション設定

  • タイトル:好きなものを入力,
  • 解説:好きなものを入力,
  • アプリケーション呼出番号:音声を流したい内線番号を入力。(東京広域電話網では1234が多い),
  • コードタイプ:Dialplanアスタリスク,

プログラムコード

1,Answer()
n,Playback(/storage/usbdisk1/mikopbx/media/custom/2e6c16e41a101c47f86d349eb3227686-20240110)
n,goto(2)

※2行目のカッコ内はご自身のファイルパス・ファイル名に変更してください。
※3行目はループ再生用なので、1度の再生のみにしたい場合は3行目は消してください。

アドバイスしていただいた8yazakiさんに感謝。