Telegram + Asteriskでの音声会議の実装





では前の記事、私の電報に登録するとき、私は、居住地のユーザーの選択の実装を説明したボット、私はのアイデアに触発作成「電話エーテル」同じ記事で、ボットとAsteriskの統合について説明します。



何のために?



多くの人は、テレグラムでグループ通話を発信できないという事実を嫌います。



さて、Viberを使用しないのですか?



このような実装には、次のような多くのケースもあります。



  • 匿名の音声会議の場合、会議の参加者の間で自分の番号やIDを「ライトアップ」したくない場合(ハッカーの安息日または匿名のアルコール依存症のクラブがすぐに思い浮かびます)。グループ、コミュニティ、チャネルに参加する必要はありません
  • 誰が会議に接続するかまったくわからないが、パスワードでアクセスを制限する必要がある場合
  • アスタリスクのすべての楽しみ:会議管理(ミュート/ umute、キック)、アスタリスク、テレグラム、PSTNに登録されたクライアントとのハイブリッド音声会議。あなたは国際電話で多くを節約することができます
  • 電報などによる企業コールバックの編成。


多くのオプションが思い浮かびますが、それらの多くは想像力によってのみ制限されます。アスタリスクと長年一緒に仕事をした後、私はそれを呼び出すことが主なことだと信じています。そうすれば、それを宇宙に送ることさえできます。



アスタリスクVoIP-テレグラムVoIPバンドル



VoIPバンドル自体は、tg2sipライブラリのおかげで実装されてますその使用法は、リポジトリ自体の「使用法」セクションで説明されています。カスタマイズに関する記事が他にもいくつかありますDockerイメージもあります

このバンドルの説明は、この記事の範囲を超えています。



私が言いたい唯一のニュアンスは、連絡先帳にない番号であるtelegram_idを呼び出すことができないということです。したがって、テレグラムが登録されている電話番号に電話をかける必要があります。



私のボットで誰でも接続できるパブリックオーディオ会議(Ethers)、およびパスワードアクセス付きのプライベートオーディオ会議として実装されます。個室/パスワードはユーザー自身が作成し、電話会議や会議などのプラットフォームとしてボットを使用できます。



インタラクションテレグラムボット-アスタリスク



私のボットのインタラクションスキームは次のようになります。



ユーザーがボットメニューで目的の部屋を選択すると、ボットはPOSTリクエストで接続パラメーターを渡すことにより、APIを介してAsteriskと対話する関数を呼び出します。



  • 加入者の電話番号
  • 会議室ID
  • 会議室でのプレゼンテーションのためのcallerid
  • Asteriskシステムのユーザーへの通知を母国語で言語化するための言語


さらに、Asteriskは、テレグラムチャネルを介して、要求パラメーターで指定された番号に発信します。ユーザーが電話に応答した後、Asteriskはユーザーを適切な部屋に接続します。



ボットからAstersikAMIへの直接接続を使用することは可能ですが、私はAPIを介して作業することを好みます。



Asteriskサーバー側のAPI



pythonの単純なAPIコード。.Callファイルは通話を開始するために使用されます



#!/usr/bin/python3
from flask import Flask, request, jsonify
import codecs
import json
import glob
import shutil

api_key = "s0m3_v3ry_str0ng_k3y"
app = Flask(__name__)

@app.route('/api/conf', methods= ['POST'])
def go_conf():
    content = request.get_json()
    ##  
    if not "api_key" in content:
        return jsonify({'error': 'Authentication required', 'message': 'Please specify api key'}), 401
    if not content["api_key"] == api_key:
        return jsonify({'error': 'Authentication failure', 'message': 'Wrong api key'}), 401
    ##      
    if not "phone_number" in content or not "room_name" in content or not "caller_id" in content:
        return jsonify({'error': 'not all parameters are specified'}), 400

    if not "lang" in content:
        lang = "ru"
    else:
        lang = content["lang"]

    phone_number = content["phone_number"]
    room_name = content["room_name"]
    caller_id = content["caller_id"]
    calls = glob.glob(f"/var/spool/asterisk/outgoing/*-{phone_number}*")
    callfile = "cb_conf-" + phone_number + "-" + room_name + ".call"
    filename = "/var/spool/asterisk/" + callfile
    if calls:
        return jsonify({'message': 'error', "text": "call already in progress"})
    with codecs.open(filename, "w", encoding='utf8') as f:
        f.write("Channel: LOCAL/" + phone_number + "@telegram-out\n")
        f.write("CallerID: <" + caller_id + ">\n")
        f.write("MaxRetries: 0\nRetryTime: 5\nWaitTime: 30\n")
        f.write("Set: LANG=" + lang + "\nContext: conf-in\n")
        f.write("Extension: " + room_name + "\nArchive: Yes\n")
    shutil.chown(filename, user="asterisk", group="asterisk")
    shutil.move(filename, "/var/spool/asterisk/outgoing/" + callfile)
    return jsonify({'message': 'ok'})


if __name__ == '__main__':
    app.run(debug=True,host='0.0.0.0', port=8080)


この場合、単純な形式のAsteriskダイヤルプランは次のようになります。



[telegram-out]
exten => _+.!,1,NoOp()
same => n,Dial(SIP/${EXTEN}@telegram)

exten => _X!,1,NoOp()
same => n,Dial(SIP/+${EXTEN}@telegram)

[conf-in]
exten => _.!,1,NoOp()
same => n,Answer()
same => n,Wait(3)
same => n,Playback(beep)
same => n,Set(CHANNEL(language)=${LANG})
same => n,ConfBridge(${EXTEN})
same => n,Hangup


このAPIは、他の場合にも使用できます。たとえば、同じコールバックボタン「Callmeback」を整理する場合などです。



API呼び出し関数



telephony_api.py



import requests, json

#  example.com   url
url = "http://example.com:8080/api/conf"
api_key = "s0m3_v3ry_str0ng_k3y"

def go_to_conf(phone_number, room_name, caller_id, lang="ru"):
    payload = {}
    payload["phone_number"] = phone_number
    payload["room_name"] = room_name
    payload["caller_id"] = caller_id
    payload["lang"] = lang
    payload["api_key"] = api_key

    headers = {
        'content-type': "application/json",
        'cache-control': "no-cache",
        }
    try:
        response = requests.request("POST", url, data=json.dumps(payload), headers=headers, timeout=2, verify=False)
        if "call already in progress" in response.text:
            return False, ".    ."
        elif "error" in response.text:
            print(response.text)
            return False, ".  .  ."
        else:
            return True, response.text
    except:
        return False, ".  .  ."


これらの2つのツールは、ボットに統合し、ロジックでラップして使用するのにすでに十分です。



会議室への呼び出しを開始するためのボットの例



#!/usr/bin/python3.6
import telebot
from telephony_api import go_to_conf
bot = telebot.TeleBot("TOKEN")
pnone_number = "799999999999"#   ,    telegram 

@bot.message_handler(content_types=['text'])
def main_text_handler(message):
    if message.text == "   ":
        bot.send_message(message.chat.id, "Ok.   telegram   ,        ")
        func_result, func_message = go_to_conf(pnone_number, "ROOM1", "Bob", "ru")
        if not func_result:
            bot.send_message(chat_id=message.chat.id, text=func_message)

if __name__ == "__main__":)
   print("bot started")
   bot.polling(none_stop=True)


この例では、電話番号は静的に設定されていますが、実際には、たとえば、message.chat.id(電話番号)と一致するようにデータベースに要求を行うことができます。



うまくいけば、私の記事が誰かがクールなプロジェクトを作成し、それをコミュニティと共有するのに役立つことを願っています。



All Articles