|
@@ -0,0 +1,221 @@
|
|
|
+#!/usr/bin/env python2
|
|
|
+# -*- coding: utf-8 -*-
|
|
|
+
|
|
|
+import audioop
|
|
|
+import io
|
|
|
+import os
|
|
|
+import syslog
|
|
|
+import threading
|
|
|
+import time
|
|
|
+import glob
|
|
|
+import subprocess
|
|
|
+
|
|
|
+import linphone
|
|
|
+from enum import Enum
|
|
|
+from datetime import datetime
|
|
|
+
|
|
|
+import wave
|
|
|
+import contextlib
|
|
|
+
|
|
|
+VOLUME_THRESHOLD = 50
|
|
|
+
|
|
|
+
|
|
|
+class ConversationStatus(Enum):
|
|
|
+ READY_TO_TALK = 0
|
|
|
+ IMTALKING = 1
|
|
|
+ WAITFORANSWER = 2
|
|
|
+
|
|
|
+
|
|
|
+current_dir = os.path.dirname(os.path.realpath(__file__))
|
|
|
+replies = glob.glob(current_dir + "/replies/*.wav")
|
|
|
+
|
|
|
+incoming_stream_file = "/tmp/lenny"
|
|
|
+
|
|
|
+conversation_status = ConversationStatus.READY_TO_TALK
|
|
|
+
|
|
|
+replies_pos = 0
|
|
|
+
|
|
|
+THREADS_MUST_QUIT = False
|
|
|
+
|
|
|
+
|
|
|
+def log(msg):
|
|
|
+ syslog.syslog(msg)
|
|
|
+ print(msg)
|
|
|
+
|
|
|
+
|
|
|
+def get_wav_duration(fname):
|
|
|
+ with contextlib.closing(wave.open(fname, 'r')) as f:
|
|
|
+ frames = f.getnframes()
|
|
|
+ rate = f.getframerate()
|
|
|
+ return frames / float(rate)
|
|
|
+
|
|
|
+
|
|
|
+def say(core):
|
|
|
+ global conversation_status
|
|
|
+ global replies_pos
|
|
|
+ if conversation_status is not ConversationStatus.IMTALKING:
|
|
|
+ voice_filename = replies[replies_pos]
|
|
|
+ replies_pos = (replies_pos + 1) % len(replies)
|
|
|
+ if replies_pos == 0:
|
|
|
+ # On ne rejoue jamais la première réplique "allo"
|
|
|
+ replies_pos = 1
|
|
|
+
|
|
|
+ print("Saying : " + voice_filename)
|
|
|
+ conversation_status = ConversationStatus.IMTALKING
|
|
|
+
|
|
|
+ duration = get_wav_duration(voice_filename)
|
|
|
+ core.play_file = voice_filename
|
|
|
+ time.sleep(duration)
|
|
|
+ core.play_file = ""
|
|
|
+
|
|
|
+ # On laisse l'autre l'occassion de reparler
|
|
|
+ conversation_status = ConversationStatus.WAITFORANSWER
|
|
|
+
|
|
|
+
|
|
|
+def call_state_changed(core, call, state, message):
|
|
|
+ global conversation_status
|
|
|
+ log("state changed : " + message)
|
|
|
+
|
|
|
+ if state == linphone.CallState.Released:
|
|
|
+ # Let's convert wav to mp3
|
|
|
+ log("Converting output from wav to mp3")
|
|
|
+ subprocess.call('lame --quiet --preset insane %s' % call.current_params.record_file, shell=True)
|
|
|
+ os.remove(call.current_params.record_file)
|
|
|
+
|
|
|
+ if state == linphone.CallState.IncomingReceived:
|
|
|
+ log("Incoming call : {}".format(call.remote_address.username))
|
|
|
+ if call.remote_address.username == "**620":
|
|
|
+ print(" .. from JF !")
|
|
|
+
|
|
|
+ call_params = core.create_call_params(call)
|
|
|
+ call_params.record_file = current_dir + "/out/call_"+datetime.now().strftime('%Y-%m-%d_%Hh%Mmn%Ss')+".wav"
|
|
|
+
|
|
|
+ # Let ring some time
|
|
|
+ time.sleep(5)
|
|
|
+
|
|
|
+ core.accept_call_with_params(call, call_params)
|
|
|
+ call.start_recording()
|
|
|
+
|
|
|
+ time.sleep(2)
|
|
|
+
|
|
|
+ t = threading.Thread(target=incoming_stream_worker, args=[silence_call_bak, core, call])
|
|
|
+ t.start()
|
|
|
+
|
|
|
+ say(core)
|
|
|
+
|
|
|
+
|
|
|
+def global_state_changed(*args, **kwargs):
|
|
|
+ log("global_state_changed: %r %r" % (args, kwargs))
|
|
|
+
|
|
|
+
|
|
|
+def registration_state_changed(core, call, state, message):
|
|
|
+ log("registration_state_changed: " + str(state) + ", " + message)
|
|
|
+
|
|
|
+
|
|
|
+def silence_call_bak(core, call):
|
|
|
+ global conversation_status
|
|
|
+ global replies_pos
|
|
|
+ if conversation_status is not ConversationStatus.WAITFORANSWER:
|
|
|
+ say(core)
|
|
|
+
|
|
|
+
|
|
|
+def incoming_stream_worker(sil_cb, core, call):
|
|
|
+ global conversation_status
|
|
|
+ print("Worker is starting")
|
|
|
+
|
|
|
+ f = open(incoming_stream_file, "rb")
|
|
|
+ f.seek(0, io.SEEK_END)
|
|
|
+ p = f.tell()
|
|
|
+ buf = ''
|
|
|
+
|
|
|
+ while call.state is not linphone.CallState.End and not THREADS_MUST_QUIT:
|
|
|
+ f.seek(p)
|
|
|
+ buf += f.read(4096)
|
|
|
+ p = f.tell()
|
|
|
+
|
|
|
+ # We must sleep a bit to avoid cpu hog
|
|
|
+ time.sleep(0.01)
|
|
|
+
|
|
|
+ if len(buf) >= 20000:
|
|
|
+ volume = audioop.rms(buf, 2)
|
|
|
+ # print("Detected volume : " + str(volume))
|
|
|
+ # print("State : " + str(conversation_status))
|
|
|
+ buf = ''
|
|
|
+ if volume < VOLUME_THRESHOLD:
|
|
|
+ if conversation_status is not ConversationStatus.IMTALKING:
|
|
|
+ t = threading.Thread(target=sil_cb, args=[core, call])
|
|
|
+ t.start()
|
|
|
+ else:
|
|
|
+ conversation_status = ConversationStatus.READY_TO_TALK
|
|
|
+
|
|
|
+ print("Worker is quitting")
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ log("lenny is starting ...")
|
|
|
+ callbacks = {
|
|
|
+ 'call_state_changed': call_state_changed,
|
|
|
+ 'registration_state_changed': registration_state_changed,
|
|
|
+ 'global_state_changed': global_state_changed,
|
|
|
+ }
|
|
|
+
|
|
|
+ username = "621"
|
|
|
+ password = "toto"
|
|
|
+ port = "5060"
|
|
|
+ domain = "192.168.1.1"
|
|
|
+
|
|
|
+ core = linphone.Core.new(callbacks, None, None)
|
|
|
+
|
|
|
+ # On fait le setup pour la capture et analyse du stream entrant
|
|
|
+ os.system("rm -rf " + incoming_stream_file)
|
|
|
+ os.system("touch " + incoming_stream_file)
|
|
|
+ core.use_files = True
|
|
|
+ core.record_file = incoming_stream_file
|
|
|
+
|
|
|
+ proxy_cfg = core.create_proxy_config()
|
|
|
+ proxy_cfg.identity_address = core.create_address('sip:' + username + '@' + domain + ':' + port)
|
|
|
+ proxy_cfg.server_addr = 'sip:' + domain + ':' + port
|
|
|
+ proxy_cfg.register_enabled = True
|
|
|
+ core.add_proxy_config(proxy_cfg)
|
|
|
+ auth_info = core.create_auth_info(username, None, password, None, None, domain)
|
|
|
+ core.add_auth_info(auth_info)
|
|
|
+
|
|
|
+ while True:
|
|
|
+ time.sleep(0.03)
|
|
|
+ core.iterate()
|
|
|
+
|
|
|
+ log("callblocker quitting.")
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+
|
|
|
+ try:
|
|
|
+ main()
|
|
|
+ except KeyboardInterrupt:
|
|
|
+ THREADS_MUST_QUIT = True
|
|
|
+ print "Bye"
|
|
|
+
|
|
|
+# - Hi its lenny
|
|
|
+# - Hmmm, sorry, I can barely here you there
|
|
|
+# - Yes, yes, yes
|
|
|
+# - Oh good, yes, yes, yes
|
|
|
+# - Hmm, mais il me semble que quelqu'un a appelé pour ça la semaine passée ... c'était vous ?
|
|
|
+# - Yeah, pouvez-vous me rappeler votre nom ?
|
|
|
+# - Ca tombe bien que vous m'appeliez, car justement ma nièce me parlait de la même chose l'autre jour.
|
|
|
+# j'écoute d'ailleurs toujours attentivement son avis, car vous savez, c'est la première de la famille
|
|
|
+# qui est allé à l'Université. Et elle l'a terminé avec mention, nous en sommes très fière.
|
|
|
+# et du coup elle m'a dit que je devrais jeter un oeil à ce dont vous me parlez.
|
|
|
+# - Hmm, désolé, j'ai pas tout à fait compris, vous pouvez répéter ?
|
|
|
+# - Could you say that again please ?
|
|
|
+# - Heu, oui, vous pouvez me rappeler de la part de quelle entreprise vous appelez
|
|
|
+# - Oui, en fait y'a une chose ... heuu, car j'avais eu un appel comme le votre, j'ai eu pas mal
|
|
|
+# de problème avec le personnel ici, qui m'on fait la morale, car je me suis retrouvé avec des affaires
|
|
|
+# dont je n'avais pas besoin, et ma nièce justement m'a fait la morale également ... vous savez ce que c'est
|
|
|
+# la famille ....
|
|
|
+#
|
|
|
+
|
|
|
+# Idées :
|
|
|
+# - En tout cas vous me semblez une personne fort sympathique, c'est toujours agréable de discute
|
|
|
+# des personnes sympathique, quand on est une personne seule comme moi ... heuu, en fait on parlait
|
|
|
+# de quoi déjà ?
|
|
|
+
|