2016-05-24 18:22:21 +00:00
|
|
|
try:
|
|
|
|
from PySide import QtCore
|
|
|
|
except ImportError:
|
|
|
|
from PyQt4 import QtCore
|
2016-02-24 18:38:36 +00:00
|
|
|
from notifications import *
|
2016-02-24 20:01:25 +00:00
|
|
|
from settings import Settings
|
2016-02-26 18:54:15 +00:00
|
|
|
from profile import Profile
|
2016-02-28 21:33:35 +00:00
|
|
|
from toxcore_enums_and_consts import *
|
2016-04-24 10:45:11 +00:00
|
|
|
from toxav_enums import *
|
2016-03-09 18:11:36 +00:00
|
|
|
from tox import bin_to_string
|
2016-05-28 10:06:13 +00:00
|
|
|
from plugin_support import PluginLoader
|
2016-02-22 15:55:04 +00:00
|
|
|
|
|
|
|
|
2016-02-24 08:33:49 +00: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-03-16 18:16:58 +00:00
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
# Callbacks - current user
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
|
2016-02-24 08:33:49 +00:00
|
|
|
|
2016-02-29 16:39:43 +00:00
|
|
|
def self_connection_status(tox_link):
|
2016-02-24 20:01:25 +00:00
|
|
|
"""
|
2016-04-02 18:31:59 +00:00
|
|
|
Current user changed connection status (offline, UDP, TCP)
|
2016-02-24 20:01:25 +00:00
|
|
|
"""
|
2016-02-24 08:33:49 +00:00
|
|
|
def wrapped(tox, connection, user_data):
|
|
|
|
print 'Connection status: ', str(connection)
|
2016-02-29 15:40:49 +00:00
|
|
|
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 12:07:15 +00:00
|
|
|
return wrapped
|
2016-02-22 15:55:04 +00:00
|
|
|
|
|
|
|
|
2016-03-16 18:16:58 +00:00
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
# Callbacks - friends
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
2016-02-28 21:33:35 +00:00
|
|
|
def friend_status(tox, friend_num, new_status, user_data):
|
|
|
|
"""
|
|
|
|
Check friend's status (none, busy, away)
|
|
|
|
"""
|
2016-03-04 17:52:52 +00:00
|
|
|
print "Friend's #{} status changed! New status: {}".format(friend_num, new_status)
|
2016-02-28 21:33:35 +00:00
|
|
|
profile = Profile.get_instance()
|
2016-02-29 15:40:49 +00:00
|
|
|
friend = profile.get_friend_by_number(friend_num)
|
2016-04-14 19:09:23 +00:00
|
|
|
if friend.status is None and Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
2016-04-01 14:38:24 +00:00
|
|
|
sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
|
2016-02-28 21:33:35 +00:00
|
|
|
invoke_in_main_thread(friend.set_status, new_status)
|
2016-03-29 18:08:15 +00:00
|
|
|
invoke_in_main_thread(profile.update_filtration)
|
2016-02-28 21:33:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
def friend_connection_status(tox, friend_num, new_status, user_data):
|
|
|
|
"""
|
|
|
|
Check friend's connection status (offline, udp, tcp)
|
|
|
|
"""
|
2016-06-03 18:53:02 +00:00
|
|
|
print "Friend #{} connection status: {}".format(friend_num, new_status)
|
2016-02-28 21:33:35 +00:00
|
|
|
profile = Profile.get_instance()
|
2016-02-29 15:40:49 +00:00
|
|
|
friend = profile.get_friend_by_number(friend_num)
|
2016-02-28 21:33:35 +00:00
|
|
|
if new_status == TOX_CONNECTION['NONE']:
|
2016-05-05 23:00:10 +00:00
|
|
|
invoke_in_main_thread(profile.friend_exit, friend_num)
|
2016-03-29 18:08:15 +00:00
|
|
|
invoke_in_main_thread(profile.update_filtration)
|
2016-04-14 19:09:23 +00:00
|
|
|
if Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
2016-04-01 14:38:24 +00:00
|
|
|
sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
|
2016-03-18 13:20:07 +00:00
|
|
|
elif friend.status is None:
|
|
|
|
invoke_in_main_thread(profile.send_avatar, friend_num)
|
2016-06-04 12:19:15 +00:00
|
|
|
profile.friend_online(friend_num)
|
|
|
|
invoke_in_main_thread(PluginLoader.get_instance().friend_online, friend_num)
|
2016-02-28 21:33:35 +00:00
|
|
|
|
|
|
|
|
2016-03-13 12:06:06 +00:00
|
|
|
def friend_name(tox, friend_num, name, size, user_data):
|
2016-04-02 18:31:59 +00:00
|
|
|
"""
|
|
|
|
Friend changed his name
|
|
|
|
"""
|
2016-03-13 12:06:06 +00:00
|
|
|
profile = Profile.get_instance()
|
|
|
|
friend = profile.get_friend_by_number(friend_num)
|
|
|
|
print 'New name: ', str(friend_num), str(name)
|
|
|
|
invoke_in_main_thread(friend.set_name, name)
|
|
|
|
if profile.get_active_number() == friend_num:
|
|
|
|
invoke_in_main_thread(profile.set_active)
|
2016-02-28 21:33:35 +00:00
|
|
|
|
|
|
|
|
2016-03-13 12:06:06 +00:00
|
|
|
def friend_status_message(tox, friend_num, status_message, size, user_data):
|
2016-02-28 21:33:35 +00:00
|
|
|
"""
|
|
|
|
:return: function for callback friend_status_message. It updates friend's status message
|
|
|
|
and calls window repaint
|
|
|
|
"""
|
2016-03-13 12:06:06 +00:00
|
|
|
profile = Profile.get_instance()
|
|
|
|
friend = profile.get_friend_by_number(friend_num)
|
|
|
|
invoke_in_main_thread(friend.set_status_message, status_message)
|
|
|
|
print 'User #{} has new status: {}'.format(friend_num, status_message)
|
|
|
|
if profile.get_active_number() == friend_num:
|
|
|
|
invoke_in_main_thread(profile.set_active)
|
2016-02-22 15:55:04 +00:00
|
|
|
|
|
|
|
|
2016-03-15 19:12:37 +00:00
|
|
|
def friend_message(window, tray):
|
2016-02-24 20:01:25 +00:00
|
|
|
"""
|
2016-04-02 18:31:59 +00:00
|
|
|
New message from friend
|
2016-02-24 20:01:25 +00:00
|
|
|
"""
|
|
|
|
def wrapped(tox, friend_number, message_type, message, size, user_data):
|
2016-02-28 21:33:35 +00:00
|
|
|
profile = Profile.get_instance()
|
2016-03-11 11:37:45 +00:00
|
|
|
settings = Settings.get_instance()
|
2016-02-28 21:33:35 +00:00
|
|
|
invoke_in_main_thread(profile.new_message, friend_number, message_type, message)
|
2016-03-11 11:37:45 +00:00
|
|
|
if not window.isActiveWindow():
|
|
|
|
friend = profile.get_friend_by_number(friend_number)
|
2016-04-14 19:09:23 +00:00
|
|
|
if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
2016-04-01 16:13:01 +00:00
|
|
|
invoke_in_main_thread(tray_notification, friend.name, message.decode('utf8'), tray, window)
|
2016-04-14 19:09:23 +00:00
|
|
|
if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
2016-03-11 16:46:30 +00:00
|
|
|
sound_notification(SOUND_NOTIFICATION['MESSAGE'])
|
2016-06-03 18:53:02 +00:00
|
|
|
invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png'))
|
2016-02-24 20:01:25 +00:00
|
|
|
return wrapped
|
2016-02-22 21:18:58 +00:00
|
|
|
|
|
|
|
|
2016-03-09 18:11:36 +00:00
|
|
|
def friend_request(tox, public_key, message, message_size, user_data):
|
2016-03-09 18:45:38 +00:00
|
|
|
"""
|
|
|
|
Called when user get new friend request
|
|
|
|
"""
|
2016-04-25 12:48:56 +00:00
|
|
|
print 'Friend request'
|
2016-03-09 18:11:36 +00:00
|
|
|
profile = Profile.get_instance()
|
2016-04-25 12:48:56 +00:00
|
|
|
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, message.decode('utf-8'))
|
2016-03-09 18:11:36 +00:00
|
|
|
|
2016-04-27 18:10:53 +00:00
|
|
|
|
|
|
|
def friend_typing(tox, friend_number, typing, user_data):
|
|
|
|
invoke_in_main_thread(Profile.get_instance().friend_typing, friend_number, typing)
|
|
|
|
|
2016-03-16 18:16:58 +00:00
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
# Callbacks - file transfers
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def tox_file_recv(window, tray):
|
2016-04-02 18:31:59 +00:00
|
|
|
"""
|
|
|
|
New incoming file
|
|
|
|
"""
|
2016-03-16 18:16:58 +00:00
|
|
|
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']:
|
|
|
|
print 'file'
|
2016-04-20 09:32:28 +00:00
|
|
|
try:
|
|
|
|
file_name = unicode(file_name[:file_name_size].decode('utf-8'))
|
|
|
|
except:
|
|
|
|
file_name = u'toxygen_file'
|
2016-03-16 18:16:58 +00:00
|
|
|
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-04-14 19:09:23 +00:00
|
|
|
if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
2016-05-29 13:49:00 +00:00
|
|
|
file_from = QtGui.QApplication.translate("Callback", "File from", None, QtGui.QApplication.UnicodeUTF8)
|
|
|
|
invoke_in_main_thread(tray_notification, file_from + ' ' + friend.name, file_name, tray, window)
|
2016-04-14 19:09:23 +00:00
|
|
|
if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
2016-03-16 18:16:58 +00:00
|
|
|
sound_notification(SOUND_NOTIFICATION['FILE_TRANSFER'])
|
2016-06-04 12:19:15 +00:00
|
|
|
invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png'))
|
2016-03-16 18:16:58 +00:00
|
|
|
else: # AVATAR
|
|
|
|
print 'Avatar'
|
|
|
|
invoke_in_main_thread(profile.incoming_avatar,
|
|
|
|
friend_number,
|
2016-03-16 19:59:15 +00:00
|
|
|
file_number,
|
|
|
|
size)
|
2016-03-16 18:16:58 +00:00
|
|
|
return wrapped
|
|
|
|
|
2016-03-16 20:56:35 +00:00
|
|
|
|
|
|
|
def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, user_data):
|
2016-04-02 18:31:59 +00:00
|
|
|
"""
|
|
|
|
Incoming chunk
|
|
|
|
"""
|
2016-05-08 19:29:50 +00:00
|
|
|
if not length:
|
|
|
|
invoke_in_main_thread(Profile.get_instance().incoming_chunk,
|
|
|
|
friend_number,
|
|
|
|
file_number,
|
|
|
|
position,
|
|
|
|
None)
|
|
|
|
else:
|
2016-06-04 12:19:15 +00:00
|
|
|
Profile.get_instance().incoming_chunk(friend_number, file_number, position, chunk[:length])
|
2016-03-17 20:49:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
def file_chunk_request(tox, friend_number, file_number, position, size, user_data):
|
2016-04-02 18:31:59 +00:00
|
|
|
"""
|
|
|
|
Outgoing chunk
|
|
|
|
"""
|
2016-04-29 21:03:53 +00:00
|
|
|
if size:
|
|
|
|
Profile.get_instance().outgoing_chunk(friend_number, file_number, position, size)
|
|
|
|
else:
|
|
|
|
invoke_in_main_thread(Profile.get_instance().outgoing_chunk,
|
|
|
|
friend_number,
|
|
|
|
file_number,
|
|
|
|
position,
|
|
|
|
size)
|
2016-03-17 20:49:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
def file_recv_control(tox, friend_number, file_number, file_control, user_data):
|
2016-04-02 18:31:59 +00:00
|
|
|
"""
|
|
|
|
Friend cancelled, paused or resumed file transfer
|
|
|
|
"""
|
2016-03-23 14:27:05 +00:00
|
|
|
if file_control == TOX_FILE_CONTROL['CANCEL']:
|
|
|
|
Profile.get_instance().cancel_transfer(friend_number, file_number, True)
|
2016-05-08 19:29:50 +00:00
|
|
|
elif file_control == TOX_FILE_CONTROL['PAUSE']:
|
|
|
|
Profile.get_instance().pause_transfer(friend_number, file_number, True)
|
|
|
|
elif file_control == TOX_FILE_CONTROL['RESUME']:
|
|
|
|
Profile.get_instance().resume_transfer(friend_number, file_number, True)
|
2016-03-17 20:49:27 +00:00
|
|
|
|
2016-05-28 10:06:13 +00:00
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
# Callbacks - custom packets
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def lossless_packet(tox, friend_number, data, length, user_data):
|
|
|
|
"""
|
|
|
|
Incoming lossless packet
|
|
|
|
"""
|
|
|
|
plugin = PluginLoader.get_instance()
|
|
|
|
invoke_in_main_thread(plugin.callback_lossless, friend_number, data, length)
|
|
|
|
|
|
|
|
|
|
|
|
def lossy_packet(tox, friend_number, data, length, user_data):
|
|
|
|
"""
|
|
|
|
Incoming lossy packet
|
|
|
|
"""
|
|
|
|
plugin = PluginLoader.get_instance()
|
|
|
|
invoke_in_main_thread(plugin.callback_lossy, friend_number, data, length)
|
|
|
|
|
2016-04-24 10:45:11 +00:00
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
# Callbacks - audio
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
def call_state(toxav, friend_number, mask, user_data):
|
2016-05-28 10:06:13 +00:00
|
|
|
"""
|
|
|
|
New call state
|
|
|
|
"""
|
2016-04-24 10:45:11 +00:00
|
|
|
print friend_number, mask
|
|
|
|
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 10:06:13 +00:00
|
|
|
"""
|
|
|
|
Incoming call from friend
|
|
|
|
"""
|
2016-04-24 10:45:11 +00:00
|
|
|
print friend_number, audio, video
|
|
|
|
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 10:06:13 +00:00
|
|
|
"""
|
|
|
|
New audio chunk
|
|
|
|
"""
|
2016-04-24 10:45:11 +00:00
|
|
|
print audio_samples_per_channel, audio_channels_count, rate
|
|
|
|
Profile.get_instance().call.chunk(
|
|
|
|
''.join(chr(x) for x in samples[:audio_samples_per_channel * 2 * audio_channels_count]),
|
|
|
|
audio_channels_count,
|
|
|
|
rate)
|
|
|
|
|
|
|
|
|
2016-03-16 18:16:58 +00:00
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
# Callbacks - initialization
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
|
2016-03-09 18:11:36 +00:00
|
|
|
|
2016-03-15 19:12:37 +00:00
|
|
|
def init_callbacks(tox, window, tray):
|
2016-02-23 11:11:00 +00:00
|
|
|
"""
|
2016-02-28 21:33:35 +00:00
|
|
|
Initialization of all callbacks.
|
2016-02-23 11:11:00 +00:00
|
|
|
:param tox: tox instance
|
|
|
|
:param window: main window
|
2016-03-16 15:15:55 +00:00
|
|
|
:param tray: tray (for notifications)
|
2016-02-23 11:11:00 +00:00
|
|
|
"""
|
2016-03-16 18:16:58 +00:00
|
|
|
tox.callback_self_connection_status(self_connection_status(tox), 0)
|
|
|
|
|
2016-02-22 21:18:58 +00:00
|
|
|
tox.callback_friend_status(friend_status, 0)
|
2016-03-15 19:12:37 +00:00
|
|
|
tox.callback_friend_message(friend_message(window, tray), 0)
|
2016-02-28 21:33:35 +00:00
|
|
|
tox.callback_friend_connection_status(friend_connection_status, 0)
|
2016-03-13 12:06:06 +00:00
|
|
|
tox.callback_friend_name(friend_name, 0)
|
|
|
|
tox.callback_friend_status_message(friend_status_message, 0)
|
2016-03-09 18:11:36 +00:00
|
|
|
tox.callback_friend_request(friend_request, 0)
|
2016-04-27 18:10:53 +00:00
|
|
|
tox.callback_friend_typing(friend_typing, 0)
|
2016-03-16 18:16:58 +00:00
|
|
|
|
|
|
|
tox.callback_file_recv(tox_file_recv(window, tray), 0)
|
2016-03-17 09:54:05 +00:00
|
|
|
tox.callback_file_recv_chunk(file_recv_chunk, 0)
|
2016-03-17 20:49:27 +00:00
|
|
|
tox.callback_file_chunk_request(file_chunk_request, 0)
|
|
|
|
tox.callback_file_recv_control(file_recv_control, 0)
|
2016-04-24 10:45:11 +00: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 10:06:13 +00:00
|
|
|
tox.callback_friend_lossless_packet(lossless_packet, 0)
|
|
|
|
tox.callback_friend_lossy_packet(lossy_packet, 0)
|
|
|
|
|