Browse Source

Big refactoring pour prépa a connection multiple.

Jean-Francois Burdet 6 years ago
parent
commit
ff4559ba13
1 changed files with 140 additions and 119 deletions
  1. 140 119
      lenny.py

+ 140 - 119
lenny.py

@@ -13,9 +13,12 @@ import threading
 import urllib2
 import wave
 from datetime import datetime
+import tempfile
 
 import linphone
 from enum import Enum
+import signal
+import sys
 
 VOLUME_THRESHOLD = 100
 
@@ -58,12 +61,6 @@ replies_seq.sort()
 replies_generic = glob.glob(current_dir + "/replies/generic/*.wav")
 replies_generic.sort()
 
-incoming_stream_file = "/tmp/lenny"
-
-conversation = Conversation()
-
-replies_pos = 0
-
 THREADS_MUST_QUIT = False
 
 KTIP_LOOKUP_URL = "https://www.ktipp.ch/service/warnlisten/detail/?warnliste_id=7&ajax=ajax-search-form&keyword={" \
@@ -132,161 +129,185 @@ def sleep(duration):
     dummy_event.wait(timeout=duration)
 
 
-def say(core):
-    global conversation
-    global replies_pos
-    if conversation.status is not ConversationStatus.IMTALKING:
-        conversation.status = ConversationStatus.IMTALKING
+class SipConnection(object):
+    def say(self, core):
+        if self._conversation.status is not ConversationStatus.IMTALKING:
+            self._conversation.status = ConversationStatus.IMTALKING
 
-        # On joue les repliques en sequence, puis quand
-        # on arrive au bout, on en joue une au hasard
-        # du groupe 'generic'
+            # On joue les repliques en sequence, puis quand
+            # on arrive au bout, on en joue une au hasard
+            # du groupe 'generic'
 
-        voice_filename = replies_seq[replies_pos]
-        replies_pos = (replies_pos + 1) % len(replies_seq)
-        if replies_pos == 0:
-            # On ne rejoue jamais la première réplique "allo"
-            replies_pos = 1
+            voice_filename = replies_seq[self._replies_pos]
+            self._replies_pos = (self._replies_pos + 1) % len(replies_seq)
+            if self._replies_pos == 0:
+                # On ne rejoue jamais la première réplique "allo"
+                self._replies_pos = 1
 
-        duration = get_wav_duration(voice_filename)
-        log("Saying : " + voice_filename + "(duration : " + str(duration) + ")")
+            duration = get_wav_duration(voice_filename)
+            log("Saying : " + voice_filename + "(duration : " + str(duration) + ")")
 
-        core.play_file = voice_filename
+            core.play_file = voice_filename
 
-        sleep(duration)
-        core.play_file = ""
+            sleep(duration)
+            core.play_file = ""
 
-        # On laisse l'autre l'occassion de reparler
-        conversation.status = ConversationStatus.WAITFORANSWER
+            # On laisse l'autre l'occassion de reparler
+            self._conversation.status = ConversationStatus.WAITFORANSWER
 
+    def incoming_stream_worker(self, core, call):
+        log("Worker is starting")
 
-def call_state_changed(core, call, state, message):
-    global conversation
-    global replies_pos
-    log("state changed : " + message)
+        f = open(self._incoming_stream_file, "rb")
+        f.seek(0, io.SEEK_END)
+        p = f.tell()
+        buf = ''
 
-    if state == linphone.CallState.Released:
-        # Let's convert wav to mp3
-        log("Converting output from wav to mp3")
-        if call.current_params.record_file is not None and os.path.isfile(call.current_params.record_file):
-            subprocess.call('lame --quiet --preset insane %s' % call.current_params.record_file, shell=True)
-            os.remove(call.current_params.record_file)
+        previous_status = self._conversation.status
 
-    if state == linphone.CallState.IncomingReceived:
-        log("Incoming call : {}".format(call.remote_address.username))
+        while call.state is not linphone.CallState.End and not self._is_quitting:
+            if self._conversation.status is ConversationStatus.IMTALKING:
+                f.seek(0, io.SEEK_END)
+                p = f.tell()
+            else:
 
-        replies_pos = 0
+                if previous_status != self._conversation.status:
+                    f.seek(0, io.SEEK_END)
+                    p = f.tell()
 
-        if is_in_blacklists(call.remote_address.username):
-            log("telemarketer calling : " + call.remote_address.username)
+                f.seek(p)
+                buf += f.read(4096)
+                p = f.tell()
 
-            call_params = core.create_call_params(call)
-            a_file = current_dir + "/out/call_from_" + slugify(call.remote_address.username) + \
-                     "_" + datetime.now().strftime(
-                '%Y-%m-%d_%Hh%Mmn%Ss') + ".wav"
+                if len(buf) >= 20000:
+                    volume = audioop.rms(buf, 2)
+                    print("Detected volume : " + str(volume))
+                    # print("State : " + str(conversation.status))
+                    buf = ''
+                    if volume < self._volume_threshold:
+                        if self._conversation.status is ConversationStatus.READY_TO_TALK:
+                            threading.Thread(target=self.say, args=[core]).start()
+                    else:
+                        self._conversation.status = ConversationStatus.READY_TO_TALK
 
-            log(a_file)
+            # We must sleep a bit to avoid cpu hog
+            sleep(0.01)
+            previous_status = self._conversation.status
 
-            call_params.record_file = a_file
+        log("Worker is quitting")
 
-            # Let ring some time
-            sleep(5)
+    def call_state_changed(self, core, call, state, message):
+        log("state changed : " + message)
 
-            core.accept_call_with_params(call, call_params)
-            call.start_recording()
+        if state == linphone.CallState.Released:
+            # Let's convert wav to mp3
+            if call.current_params.record_file is not None and os.path.isfile(call.current_params.record_file):
+                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)
 
-            sleep(2)
+        if state == linphone.CallState.IncomingReceived:
+            log("Incoming call : {}".format(call.remote_address.username))
 
-            t = threading.Thread(target=incoming_stream_worker, args=[core, call])
-            t.start()
+            self._replies_pos = 0
 
-            say(core)
+            if is_in_blacklists(call.remote_address.username):
+                log("telemarketer calling : " + call.remote_address.username)
 
+                call_params = core.create_call_params(call)
+                a_file = current_dir + "/out/call_from_" + slugify(call.remote_address.username) + \
+                         "_" + datetime.now().strftime(
+                    '%Y-%m-%d_%Hh%Mmn%Ss') + ".wav"
 
-def incoming_stream_worker(core, call):
-    global conversation
-    log("Worker is starting")
+                log(a_file)
 
-    f = open(incoming_stream_file, "rb")
-    f.seek(0, io.SEEK_END)
-    p = f.tell()
-    buf = ''
+                call_params.record_file = a_file
 
-    previous_status = conversation.status
+                # Let ring some time
+                sleep(4)
 
-    while call.state is not linphone.CallState.End and not THREADS_MUST_QUIT:
-        if conversation.status is ConversationStatus.IMTALKING:
-            f.seek(0, io.SEEK_END)
-            p = f.tell()
-        else:
+                core.accept_call_with_params(call, call_params)
+                call.start_recording()
 
-            if previous_status != conversation.status:
-                f.seek(0, io.SEEK_END)
-                p = f.tell()
+                sleep(2)
+
+                t = threading.Thread(target=self.incoming_stream_worker, args=[core, call])
+                t.start()
 
-            f.seek(p)
-            buf += f.read(4096)
-            p = f.tell()
+                self.say(core)
 
-            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 ConversationStatus.READY_TO_TALK:
-                        threading.Thread(target=say, args=[core]).start()
-                else:
-                    conversation.status = ConversationStatus.READY_TO_TALK
+    def __enter__(self):
+        return self
 
-        # We must sleep a bit to avoid cpu hog
-        sleep(0.01)
-        previous_status = conversation.status
+    def __exit__(self, exc_type, exc_value, traceback):
+        log("Cleaning on exit ...")
+        os.unlink(self._incoming_stream_file)
 
-    log("Worker is quitting")
+    def start(self):
+        print("starting")
+        self._core.use_files = True
+        self._core.record_file = self._incoming_stream_file
 
+        proxy_cfg = self._core.create_proxy_config()
+        proxy_cfg.identity_address = self._core.create_address('sip:' + self._username + '@' + self._domain + ':5060')
+        proxy_cfg.server_addr = 'sip:' + self._domain + ':5060'
+        proxy_cfg.register_enabled = True
+        self._core.add_proxy_config(proxy_cfg)
+        auth_info = self._core.create_auth_info(self._username, None, self._password, None, None, self._domain)
+        self._core.add_auth_info(auth_info)
 
-def main():
-    log("lenny is starting ...")
-    callbacks = {
-        'call_state_changed': call_state_changed
-    }
+        while not self._is_quitting:
+            sleep(0.03)
+            self._core.iterate()
 
-    username = "621"
-    password = "toto"
-    port = "5060"
-    domain = "192.168.1.1"
+    def request_quit(self):
+        self._is_quitting = True
+        self.__exit__(None, None, None)
 
-    core = linphone.Core.new(callbacks, None, None)
+    def __init__(self, domain, username, password):
 
-    # 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
+        print("init")
+        callbacks = {
+            'call_state_changed': self.call_state_changed
+        }
 
-    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)
+        self._core = linphone.Core.new(callbacks, None, None)
+        self._domain = domain
+        self._username = username
+        self._password = password
+        self._is_quitting = False
 
-    while True:
-        sleep(0.03)
-        core.iterate()
+        self._conversation = Conversation()
+        self._replies_pos = 0
+        self._volume_threshold = VOLUME_THRESHOLD
+        self._incoming_stream_file = tempfile.NamedTemporaryFile(delete=False).name
 
 
 if __name__ == "__main__":
-    try:
-        main()
-    except KeyboardInterrupt:
-        THREADS_MUST_QUIT = True
-        print "Bye"
+
+    connections = []
+
+    def signal_handler(signal, frame):
+        print('External stop request!')
+        for conn in connections:
+            conn.request_quit()
+
+
+    conn1 = SipConnection("192.168.1.1", "621", "toto")
+    connections.append(conn1)
+    threading.Thread(target=conn1.start).start()
+
+    signal.signal(signal.SIGINT, signal_handler)
+    signal.signal(signal.SIGTERM, signal_handler)
+    signal.pause()
+
+
+
+
+
+
 
 # TODO : créer un fichier de config
 # TODO : pouvoir s'enregistrer sur plusieurs comptes SIP
-# TODO : Que le stream entrant utilise un fichier temporaire
 # TODO : De pouvoir utiliser des mp3 pour gagner de la place, qu'on convertit au vol en wav, puis qu'on efface.
 # TODO : Ecrire le readme