lenny.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. #!/usr/bin/env python2
  2. # -*- coding: utf-8 -*-
  3. import audioop
  4. import io
  5. import os
  6. import syslog
  7. import threading
  8. import time
  9. import glob
  10. import subprocess
  11. import linphone
  12. from enum import Enum
  13. from datetime import datetime
  14. import wave
  15. import contextlib
  16. VOLUME_THRESHOLD = 50
  17. class ConversationStatus(Enum):
  18. READY_TO_TALK = 0
  19. IMTALKING = 1
  20. WAITFORANSWER = 2
  21. current_dir = os.path.dirname(os.path.realpath(__file__))
  22. replies = glob.glob(current_dir + "/replies/*.wav")
  23. incoming_stream_file = "/tmp/lenny"
  24. conversation_status = ConversationStatus.READY_TO_TALK
  25. replies_pos = 0
  26. THREADS_MUST_QUIT = False
  27. def log(msg):
  28. syslog.syslog(msg)
  29. print(msg)
  30. def get_wav_duration(fname):
  31. with contextlib.closing(wave.open(fname, 'r')) as f:
  32. frames = f.getnframes()
  33. rate = f.getframerate()
  34. return frames / float(rate)
  35. def say(core):
  36. global conversation_status
  37. global replies_pos
  38. if conversation_status is not ConversationStatus.IMTALKING:
  39. voice_filename = replies[replies_pos]
  40. replies_pos = (replies_pos + 1) % len(replies)
  41. if replies_pos == 0:
  42. # On ne rejoue jamais la première réplique "allo"
  43. replies_pos = 1
  44. print("Saying : " + voice_filename)
  45. conversation_status = ConversationStatus.IMTALKING
  46. duration = get_wav_duration(voice_filename)
  47. core.play_file = voice_filename
  48. time.sleep(duration)
  49. core.play_file = ""
  50. # On laisse l'autre l'occassion de reparler
  51. conversation_status = ConversationStatus.WAITFORANSWER
  52. def call_state_changed(core, call, state, message):
  53. global conversation_status
  54. log("state changed : " + message)
  55. if state == linphone.CallState.Released:
  56. # Let's convert wav to mp3
  57. log("Converting output from wav to mp3")
  58. subprocess.call('lame --quiet --preset insane %s' % call.current_params.record_file, shell=True)
  59. os.remove(call.current_params.record_file)
  60. if state == linphone.CallState.IncomingReceived:
  61. log("Incoming call : {}".format(call.remote_address.username))
  62. if call.remote_address.username == "**620":
  63. print(" .. from JF !")
  64. call_params = core.create_call_params(call)
  65. call_params.record_file = current_dir + "/out/call_"+datetime.now().strftime('%Y-%m-%d_%Hh%Mmn%Ss')+".wav"
  66. # Let ring some time
  67. time.sleep(5)
  68. core.accept_call_with_params(call, call_params)
  69. call.start_recording()
  70. time.sleep(2)
  71. t = threading.Thread(target=incoming_stream_worker, args=[silence_call_bak, core, call])
  72. t.start()
  73. say(core)
  74. def global_state_changed(*args, **kwargs):
  75. log("global_state_changed: %r %r" % (args, kwargs))
  76. def registration_state_changed(core, call, state, message):
  77. log("registration_state_changed: " + str(state) + ", " + message)
  78. def silence_call_bak(core, call):
  79. global conversation_status
  80. global replies_pos
  81. if conversation_status is not ConversationStatus.WAITFORANSWER:
  82. say(core)
  83. def incoming_stream_worker(sil_cb, core, call):
  84. global conversation_status
  85. print("Worker is starting")
  86. f = open(incoming_stream_file, "rb")
  87. f.seek(0, io.SEEK_END)
  88. p = f.tell()
  89. buf = ''
  90. while call.state is not linphone.CallState.End and not THREADS_MUST_QUIT:
  91. f.seek(p)
  92. buf += f.read(4096)
  93. p = f.tell()
  94. # We must sleep a bit to avoid cpu hog
  95. time.sleep(0.01)
  96. if len(buf) >= 20000:
  97. volume = audioop.rms(buf, 2)
  98. # print("Detected volume : " + str(volume))
  99. # print("State : " + str(conversation_status))
  100. buf = ''
  101. if volume < VOLUME_THRESHOLD:
  102. if conversation_status is not ConversationStatus.IMTALKING:
  103. t = threading.Thread(target=sil_cb, args=[core, call])
  104. t.start()
  105. else:
  106. conversation_status = ConversationStatus.READY_TO_TALK
  107. print("Worker is quitting")
  108. def main():
  109. log("lenny is starting ...")
  110. callbacks = {
  111. 'call_state_changed': call_state_changed,
  112. 'registration_state_changed': registration_state_changed,
  113. 'global_state_changed': global_state_changed,
  114. }
  115. username = "621"
  116. password = "toto"
  117. port = "5060"
  118. domain = "192.168.1.1"
  119. core = linphone.Core.new(callbacks, None, None)
  120. # On fait le setup pour la capture et analyse du stream entrant
  121. os.system("rm -rf " + incoming_stream_file)
  122. os.system("touch " + incoming_stream_file)
  123. core.use_files = True
  124. core.record_file = incoming_stream_file
  125. proxy_cfg = core.create_proxy_config()
  126. proxy_cfg.identity_address = core.create_address('sip:' + username + '@' + domain + ':' + port)
  127. proxy_cfg.server_addr = 'sip:' + domain + ':' + port
  128. proxy_cfg.register_enabled = True
  129. core.add_proxy_config(proxy_cfg)
  130. auth_info = core.create_auth_info(username, None, password, None, None, domain)
  131. core.add_auth_info(auth_info)
  132. while True:
  133. time.sleep(0.03)
  134. core.iterate()
  135. log("callblocker quitting.")
  136. if __name__ == "__main__":
  137. try:
  138. main()
  139. except KeyboardInterrupt:
  140. THREADS_MUST_QUIT = True
  141. print "Bye"
  142. # - Hi its lenny
  143. # - Hmmm, sorry, I can barely here you there
  144. # - Yes, yes, yes
  145. # - Oh good, yes, yes, yes
  146. # - Hmm, mais il me semble que quelqu'un a appelé pour ça la semaine passée ... c'était vous ?
  147. # - Yeah, pouvez-vous me rappeler votre nom ?
  148. # - Ca tombe bien que vous m'appeliez, car justement ma nièce me parlait de la même chose l'autre jour.
  149. # j'écoute d'ailleurs toujours attentivement son avis, car vous savez, c'est la première de la famille
  150. # qui est allé à l'Université. Et elle l'a terminé avec mention, nous en sommes très fière.
  151. # et du coup elle m'a dit que je devrais jeter un oeil à ce dont vous me parlez.
  152. # - Hmm, désolé, j'ai pas tout à fait compris, vous pouvez répéter ?
  153. # - Could you say that again please ?
  154. # - Heu, oui, vous pouvez me rappeler de la part de quelle entreprise vous appelez
  155. # - Oui, en fait y'a une chose ... heuu, car j'avais eu un appel comme le votre, j'ai eu pas mal
  156. # de problème avec le personnel ici, qui m'on fait la morale, car je me suis retrouvé avec des affaires
  157. # dont je n'avais pas besoin, et ma nièce justement m'a fait la morale également ... vous savez ce que c'est
  158. # la famille ....
  159. #
  160. # Idées :
  161. # - En tout cas vous me semblez une personne fort sympathique, c'est toujours agréable de discute
  162. # des personnes sympathique, quand on est une personne seule comme moi ... heuu, en fait on parlait
  163. # de quoi déjà ?