Browse Source

First working commit

Jean-Francois Burdet 6 years ago
parent
commit
931d01cc55
5 changed files with 228 additions and 0 deletions
  1. 2 0
      .gitignore
  2. 221 0
      lenny.py
  3. BIN
      replies/00_allo.wav
  4. BIN
      replies/01_quoi_j'ai_pas_compris.wav
  5. 5 0
      requirements.txt

+ 2 - 0
.gitignore

@@ -58,3 +58,5 @@ docs/_build/
 # PyBuilder
 target/
 
+/out/
+/venv2/

+ 221 - 0
lenny.py

@@ -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à ?
+

BIN
replies/00_allo.wav


BIN
replies/01_quoi_j'ai_pas_compris.wav


+ 5 - 0
requirements.txt

@@ -0,0 +1,5 @@
+--find-links https://linphone.org/releases/linphone-python/linux-x86_64/
+
+linphone
+lxml
+enum34