En el artículo anterior, describí la implementación de la elección del lugar de residencia del usuario al registrarse en mi bot de telegramas , que creé inspirado en la idea de "Telefonía" . En el mismo artículo, describiré la integración del bot con Asterisk .
¿Para qué?
A muchas personas no les gusta el hecho de que no se puedan realizar llamadas grupales en Telegram.
Bueno, ¿no usas Viber?
También hay varios casos para tal implementación, por ejemplo:
- Para conferencias de audio anónimas, cuando no desea "iluminar" su número o identificación entre los participantes de la conferencia (me viene a la mente el sábado de los hackers o el club de alcohólicos anónimos). No es necesario estar en ningún grupo, comunidad, canal
- Cuando no sabe quién se conectará a la conferencia, pero necesita restringir el acceso con una contraseña
- Todas las delicias de Asterisk: gestión de conferencias (mute / umute, kick), audioconferencia híbrida con clientes registrados en asterisk, telegram y PSTN. Puedes ahorrar mucho en llamadas internacionales
- Organización de callback corporativo vía telegrama, etc.
Me vienen a la mente un montón de opciones, hay muchas, limitadas solo por la imaginación. Después de muchos años de trabajar con Asterisk, creo que lo principal es llamarlo, y luego puedes hacer lo que sea adecuado con él, incluso enviarlo al espacio.
Paquete Asterisk VoIP-Telegram VoIP
El paquete de VoIP en sí se implementa gracias a la biblioteca tg2sip . Su uso se describe en el propio repositorio en la sección Uso. Hay algunos artículos más sobre personalización. Incluso hay una imagen de Docker .
Una descripción de este paquete está fuera del alcance de este artículo.
El único matiz que me gustaría expresar es que no puedes llamar a telegram_id, cuyo número no está en tu agenda de contactos. Por lo tanto, debe llamar al número de teléfono en el que está registrado el telegrama.
En mi botimplementado como audioconferencias públicas (Ethers), a las que cualquiera puede conectarse, y audioconferencias privadas con contraseña. Las salas privadas / contraseñas son creadas por los propios usuarios y pueden usar el bot como plataforma para conferencias de audio, reuniones, etc.
Bot de telegrama de interacción - Asterisk
El esquema de interacción en mi bot se ve así.
El usuario selecciona la habitación deseada en el menú del bot, el bot llama a la función de interactuar con Asterisk a través de la API pasando los parámetros de conexión en la solicitud POST:
- número de teléfono del suscriptor
- ID de sala de conferencias
- callerid para presentación en una sala de conferencias
- idioma para verbalizar notificaciones al usuario en el sistema Asterisk en su idioma nativo
Además, Asterisk realiza una llamada saliente a través de canales de telegramas al número especificado en los parámetros de solicitud. Después de que el usuario responde la llamada, Asterisk lo conecta a la habitación apropiada.
Sería posible usar una conexión directa desde el bot a la AMI de Astersik , pero prefiero trabajar a través de la API, cuanto más simple, mejor.
API en el lado del servidor Asterisk
Código API simple en Python. Los archivos .Call se utilizan para iniciar una llamada
#!/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)
En este caso, el plan de marcado de Asterisk en una forma simple se ve así:
[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
Esta API se puede utilizar en otros casos, por ejemplo, para organizar el mismo botón de devolución de llamada "Devolver la llamada", etc.
Función de llamada 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, ". . ."
Estas dos herramientas ya son suficientes para la integración en tu bot, envuélvelas en tu lógica y úsalas.
Un ejemplo de un bot para iniciar una llamada a una sala de conferencias
#!/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)
En este ejemplo, el número de teléfono se establece estáticamente, pero en realidad, por ejemplo, puede realizar solicitudes a la base de datos para que coincida message.chat.id - el número de teléfono.
Espero que mi artículo ayude a alguien a crear proyectos interesantes y, a su vez, compartirlos con la comunidad.