toxygen/toxygen/callbacks.py

357 lines
14 KiB
Python
Raw Normal View History

2017-04-13 18:22:46 +02:00
from PyQt5 import QtCore, QtGui, QtWidgets
2016-02-24 19:38:36 +01:00
from notifications import *
2016-02-24 21:01:25 +01:00
from settings import Settings
2016-07-06 15:25:04 +02:00
from profile import Profile
from toxcore_enums_and_consts import *
2016-04-24 12:45:11 +02:00
from toxav_enums import *
2016-03-09 19:11:36 +01:00
from tox import bin_to_string
2016-05-28 12:06:13 +02:00
from plugin_support import PluginLoader
2016-08-01 14:26:11 +02:00
import queue
import threading
2016-08-01 17:00:03 +02:00
import util
2016-08-01 14:26:11 +02:00
# -----------------------------------------------------------------------------------------------------------------
# Threads
# -----------------------------------------------------------------------------------------------------------------
2016-02-22 16:55:04 +01:00
class InvokeEvent(QtCore.QEvent):
EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
def __init__(self, fn, *args, **kwargs):
QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
self.fn = fn
self.args = args
self.kwargs = kwargs
class Invoker(QtCore.QObject):
def event(self, event):
event.fn(*event.args, **event.kwargs)
return True
_invoker = Invoker()
def invoke_in_main_thread(fn, *args, **kwargs):
QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))
2016-08-01 14:26:11 +02:00
class FileTransfersThread(threading.Thread):
def __init__(self):
self._queue = queue.Queue()
self._timeout = 0.01
self._continue = True
super().__init__()
def execute(self, function, *args, **kwargs):
self._queue.put((function, args, kwargs))
def stop(self):
self._continue = False
def run(self):
while self._continue:
try:
function, args, kwargs = self._queue.get(timeout=self._timeout)
function(*args, **kwargs)
except queue.Empty:
pass
except queue.Full:
2016-08-01 17:00:03 +02:00
util.log('Queue is Full in _thread')
except Exception as ex:
util.log('Exception in _thread: ' + str(ex))
2016-08-01 14:26:11 +02:00
_thread = FileTransfersThread()
2016-08-01 17:00:03 +02:00
def start():
_thread.start()
2016-08-01 14:26:11 +02:00
def stop():
_thread.stop()
_thread.join()
# -----------------------------------------------------------------------------------------------------------------
# Callbacks - current user
# -----------------------------------------------------------------------------------------------------------------
def self_connection_status(tox_link):
2016-02-24 21:01:25 +01:00
"""
2016-04-02 20:31:59 +02:00
Current user changed connection status (offline, UDP, TCP)
2016-02-24 21:01:25 +01:00
"""
def wrapped(tox, connection, user_data):
2016-06-21 13:58:11 +02:00
print('Connection status: ', str(connection))
profile = Profile.get_instance()
if profile.status is None:
status = tox_link.self_get_status()
invoke_in_main_thread(profile.set_status, status)
elif connection == TOX_CONNECTION['NONE']:
invoke_in_main_thread(profile.set_status, None)
2016-02-23 13:07:15 +01:00
return wrapped
2016-02-22 16:55:04 +01:00
# -----------------------------------------------------------------------------------------------------------------
# Callbacks - friends
# -----------------------------------------------------------------------------------------------------------------
def friend_status(tox, friend_num, new_status, user_data):
"""
Check friend's status (none, busy, away)
"""
2016-07-02 14:40:06 +02:00
print("Friend's #{} status changed!".format(friend_num))
profile = Profile.get_instance()
friend = profile.get_friend_by_number(friend_num)
2016-04-14 21:09:23 +02:00
if friend.status is None and Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
invoke_in_main_thread(friend.set_status, new_status)
2016-08-05 15:38:08 +02:00
invoke_in_main_thread(QtCore.QTimer.singleShot, 5000, lambda: profile.send_files(friend_num))
2016-03-29 20:08:15 +02:00
invoke_in_main_thread(profile.update_filtration)
def friend_connection_status(tox, friend_num, new_status, user_data):
"""
Check friend's connection status (offline, udp, tcp)
"""
2016-06-21 13:58:11 +02:00
print("Friend #{} connection status: {}".format(friend_num, new_status))
profile = Profile.get_instance()
friend = profile.get_friend_by_number(friend_num)
if new_status == TOX_CONNECTION['NONE']:
invoke_in_main_thread(profile.friend_exit, friend_num)
2016-03-29 20:08:15 +02:00
invoke_in_main_thread(profile.update_filtration)
2016-04-14 21:09:23 +02:00
if Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
2016-03-18 14:20:07 +01:00
elif friend.status is None:
invoke_in_main_thread(profile.send_avatar, friend_num)
2016-06-04 14:19:15 +02:00
invoke_in_main_thread(PluginLoader.get_instance().friend_online, friend_num)
2016-03-13 13:06:06 +01:00
def friend_name(tox, friend_num, name, size, user_data):
2016-04-02 20:31:59 +02:00
"""
Friend changed his name
"""
2016-03-13 13:06:06 +01:00
profile = Profile.get_instance()
2016-07-02 14:40:06 +02:00
print('New name friend #' + str(friend_num))
2016-06-17 21:35:05 +02:00
invoke_in_main_thread(profile.new_name, friend_num, name)
2016-03-13 13:06:06 +01:00
def friend_status_message(tox, friend_num, status_message, size, user_data):
"""
:return: function for callback friend_status_message. It updates friend's status message
and calls window repaint
"""
2016-03-13 13:06:06 +01:00
profile = Profile.get_instance()
friend = profile.get_friend_by_number(friend_num)
invoke_in_main_thread(friend.set_status_message, status_message)
2016-07-02 14:40:06 +02:00
print('User #{} has new status'.format(friend_num))
invoke_in_main_thread(profile.send_messages, friend_num)
2016-03-13 13:06:06 +01:00
if profile.get_active_number() == friend_num:
invoke_in_main_thread(profile.set_active)
2016-02-22 16:55:04 +01:00
2016-03-15 20:12:37 +01:00
def friend_message(window, tray):
2016-02-24 21:01:25 +01:00
"""
2016-04-02 20:31:59 +02:00
New message from friend
2016-02-24 21:01:25 +01:00
"""
def wrapped(tox, friend_number, message_type, message, size, user_data):
profile = Profile.get_instance()
2016-03-11 12:37:45 +01:00
settings = Settings.get_instance()
2016-06-21 13:58:11 +02:00
message = str(message, 'utf-8')
invoke_in_main_thread(profile.new_message, friend_number, message_type, message)
2016-03-11 12:37:45 +01:00
if not window.isActiveWindow():
friend = profile.get_friend_by_number(friend_number)
2016-07-02 14:40:06 +02:00
if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked:
2016-06-21 13:58:11 +02:00
invoke_in_main_thread(tray_notification, friend.name, message, tray, window)
2016-04-14 21:09:23 +02:00
if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
sound_notification(SOUND_NOTIFICATION['MESSAGE'])
invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png'))
2016-02-24 21:01:25 +01:00
return wrapped
2016-02-22 22:18:58 +01:00
2016-03-09 19:11:36 +01:00
def friend_request(tox, public_key, message, message_size, user_data):
2016-03-09 19:45:38 +01:00
"""
Called when user get new friend request
"""
2016-06-21 13:58:11 +02:00
print('Friend request')
2016-03-09 19:11:36 +01:00
profile = Profile.get_instance()
key = ''.join(chr(x) for x in public_key[:TOX_PUBLIC_KEY_SIZE])
tox_id = bin_to_string(key, TOX_PUBLIC_KEY_SIZE)
if tox_id not in Settings.get_instance()['blocked']:
invoke_in_main_thread(profile.process_friend_request, tox_id, str(message, 'utf-8'))
2016-03-09 19:11:36 +01:00
2016-04-27 20:10:53 +02:00
def friend_typing(tox, friend_number, typing, user_data):
invoke_in_main_thread(Profile.get_instance().friend_typing, friend_number, typing)
def friend_read_receipt(tox, friend_number, message_id, user_data):
2016-06-15 22:27:57 +02:00
profile = Profile.get_instance()
profile.get_friend_by_number(friend_number).dec_receipt()
if friend_number == profile.get_active_number():
2016-06-15 23:12:27 +02:00
invoke_in_main_thread(profile.receipt)
# -----------------------------------------------------------------------------------------------------------------
# Callbacks - file transfers
# -----------------------------------------------------------------------------------------------------------------
def tox_file_recv(window, tray):
2016-04-02 20:31:59 +02:00
"""
New incoming file
"""
def wrapped(tox, friend_number, file_number, file_type, size, file_name, file_name_size, user_data):
profile = Profile.get_instance()
settings = Settings.get_instance()
if file_type == TOX_FILE_KIND['DATA']:
2016-06-21 13:58:11 +02:00
print('File')
2016-04-20 11:32:28 +02:00
try:
2016-06-21 13:58:11 +02:00
file_name = str(file_name[:file_name_size], 'utf-8')
2016-04-20 11:32:28 +02:00
except:
2016-06-21 13:58:11 +02:00
file_name = 'toxygen_file'
invoke_in_main_thread(profile.incoming_file_transfer,
friend_number,
file_number,
size,
file_name)
if not window.isActiveWindow():
friend = profile.get_friend_by_number(friend_number)
2016-07-02 14:40:06 +02:00
if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked:
2017-04-11 20:10:03 +02:00
file_from = QtWidgets.QApplication.translate("Callback", "File from")
invoke_in_main_thread(tray_notification, file_from + ' ' + friend.name, file_name, tray, window)
2016-04-14 21:09:23 +02:00
if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
sound_notification(SOUND_NOTIFICATION['FILE_TRANSFER'])
2016-06-04 14:19:15 +02:00
invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png'))
else: # AVATAR
2016-07-02 14:40:06 +02:00
print('Avatar')
invoke_in_main_thread(profile.incoming_avatar,
friend_number,
file_number,
size)
return wrapped
2016-03-16 21:56:35 +01:00
def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, user_data):
2016-04-02 20:31:59 +02:00
"""
Incoming chunk
"""
2016-08-03 22:32:11 +02:00
_thread.execute(Profile.get_instance().incoming_chunk, friend_number, file_number, position,
chunk[:length] if length else None)
2016-03-17 21:49:27 +01:00
def file_chunk_request(tox, friend_number, file_number, position, size, user_data):
2016-04-02 20:31:59 +02:00
"""
Outgoing chunk
"""
2016-08-03 22:32:11 +02:00
Profile.get_instance().outgoing_chunk(friend_number, file_number, position, size)
2016-03-17 21:49:27 +01:00
def file_recv_control(tox, friend_number, file_number, file_control, user_data):
2016-04-02 20:31:59 +02:00
"""
Friend cancelled, paused or resumed file transfer
"""
if file_control == TOX_FILE_CONTROL['CANCEL']:
invoke_in_main_thread(Profile.get_instance().cancel_transfer, friend_number, file_number, True)
elif file_control == TOX_FILE_CONTROL['PAUSE']:
invoke_in_main_thread(Profile.get_instance().pause_transfer, friend_number, file_number, True)
elif file_control == TOX_FILE_CONTROL['RESUME']:
invoke_in_main_thread(Profile.get_instance().resume_transfer, friend_number, file_number, True)
2016-03-17 21:49:27 +01:00
2016-05-28 12:06:13 +02:00
# -----------------------------------------------------------------------------------------------------------------
# Callbacks - custom packets
# -----------------------------------------------------------------------------------------------------------------
def lossless_packet(tox, friend_number, data, length, user_data):
"""
Incoming lossless packet
"""
2016-10-15 18:03:33 +02:00
data = data[:length]
2016-05-28 12:06:13 +02:00
plugin = PluginLoader.get_instance()
2016-10-15 18:03:33 +02:00
invoke_in_main_thread(plugin.callback_lossless, friend_number, data)
2016-05-28 12:06:13 +02:00
def lossy_packet(tox, friend_number, data, length, user_data):
"""
Incoming lossy packet
"""
2016-10-15 18:03:33 +02:00
data = data[:length]
2016-05-28 12:06:13 +02:00
plugin = PluginLoader.get_instance()
2016-10-15 18:03:33 +02:00
invoke_in_main_thread(plugin.callback_lossy, friend_number, data)
2016-05-28 12:06:13 +02:00
2016-04-24 12:45:11 +02:00
# -----------------------------------------------------------------------------------------------------------------
# Callbacks - audio
# -----------------------------------------------------------------------------------------------------------------
def call_state(toxav, friend_number, mask, user_data):
2016-05-28 12:06:13 +02:00
"""
New call state
"""
2016-06-21 13:58:11 +02:00
print(friend_number, mask)
2016-04-24 12:45:11 +02:00
if mask == TOXAV_FRIEND_CALL_STATE['FINISHED'] or mask == TOXAV_FRIEND_CALL_STATE['ERROR']:
invoke_in_main_thread(Profile.get_instance().stop_call, friend_number, True)
else:
Profile.get_instance().call.toxav_call_state_cb(friend_number, mask)
def call(toxav, friend_number, audio, video, user_data):
2016-05-28 12:06:13 +02:00
"""
Incoming call from friend
"""
2016-06-21 13:58:11 +02:00
print(friend_number, audio, video)
2016-04-24 12:45:11 +02:00
invoke_in_main_thread(Profile.get_instance().incoming_call, audio, video, friend_number)
def callback_audio(toxav, friend_number, samples, audio_samples_per_channel, audio_channels_count, rate, user_data):
2016-05-28 12:06:13 +02:00
"""
New audio chunk
"""
2016-04-24 12:45:11 +02:00
Profile.get_instance().call.chunk(
2016-06-23 10:18:18 +02:00
bytes(samples[:audio_samples_per_channel * 2 * audio_channels_count]),
2016-04-24 12:45:11 +02:00
audio_channels_count,
rate)
# -----------------------------------------------------------------------------------------------------------------
# Callbacks - initialization
# -----------------------------------------------------------------------------------------------------------------
2016-03-09 19:11:36 +01:00
2016-03-15 20:12:37 +01:00
def init_callbacks(tox, window, tray):
2016-02-23 12:11:00 +01:00
"""
Initialization of all callbacks.
2016-02-23 12:11:00 +01:00
:param tox: tox instance
:param window: main window
2016-03-16 16:15:55 +01:00
:param tray: tray (for notifications)
2016-02-23 12:11:00 +01:00
"""
tox.callback_self_connection_status(self_connection_status(tox), 0)
2016-02-22 22:18:58 +01:00
tox.callback_friend_status(friend_status, 0)
2016-03-15 20:12:37 +01:00
tox.callback_friend_message(friend_message(window, tray), 0)
tox.callback_friend_connection_status(friend_connection_status, 0)
2016-03-13 13:06:06 +01:00
tox.callback_friend_name(friend_name, 0)
tox.callback_friend_status_message(friend_status_message, 0)
2016-03-09 19:11:36 +01:00
tox.callback_friend_request(friend_request, 0)
2016-04-27 20:10:53 +02:00
tox.callback_friend_typing(friend_typing, 0)
tox.callback_friend_read_receipt(friend_read_receipt, 0)
tox.callback_file_recv(tox_file_recv(window, tray), 0)
2016-03-17 10:54:05 +01:00
tox.callback_file_recv_chunk(file_recv_chunk, 0)
2016-03-17 21:49:27 +01:00
tox.callback_file_chunk_request(file_chunk_request, 0)
tox.callback_file_recv_control(file_recv_control, 0)
2016-04-24 12:45:11 +02:00
toxav = tox.AV
toxav.callback_call_state(call_state, 0)
toxav.callback_call(call, 0)
toxav.callback_audio_receive_frame(callback_audio, 0)
2016-05-28 12:06:13 +02:00
tox.callback_friend_lossless_packet(lossless_packet, 0)
tox.callback_friend_lossy_packet(lossy_packet, 0)