refactoring - correct namespaces, logic from profile.py moved to different files, calbacks partially fixed

This commit is contained in:
ingvar1995 2018-03-10 18:42:53 +03:00
parent 593e25efe5
commit 20bb694c7e
23 changed files with 1071 additions and 966 deletions

View file

@ -1,6 +1,6 @@
from contacts.profile import * from contacts.profile import *
from network.tox_dns import tox_dns from network.tox_dns import tox_dns
from db.history import History from db.database import History
from toxygen.smileys import SmileyLoader from toxygen.smileys import SmileyLoader
from messenger.messages import * from messenger.messages import *
import user_data.toxes as encr import user_data.toxes as encr

View file

@ -1,3 +1,7 @@
import threads
class App: class App:
def __init__(self, path_or_uri=None): def __init__(self, path_or_uri=None):
@ -247,14 +251,14 @@ class App:
# create new tox instance # create new tox instance
self.tox = profile.tox_factory(data, Settings.get_instance()) self.tox = profile.tox_factory(data, Settings.get_instance())
# init thread # init thread
self.init = self.InitThread(self.tox, self.ms, self.tray) self.init = threads.InitThread(self.tox, self.ms, self.tray)
self.init.start() self.init.start()
# starting threads for tox iterate and toxav iterate # starting threads for tox iterate and toxav iterate
self.mainloop = self.ToxIterateThread(self.tox) self.mainloop = threads.ToxIterateThread(self.tox)
self.mainloop.start() self.mainloop.start()
self.avloop = self.ToxAVIterateThread(self.tox.AV) self.avloop = threads.ToxAVIterateThread(self.tox.AV)
self.avloop.start() self.avloop.start()
plugin_helper = PluginLoader.get_instance() plugin_helper = PluginLoader.get_instance()

104
toxygen/av/calls_manager.py Normal file
View file

@ -0,0 +1,104 @@
import threading
import cv2
import av.calls
from PyQt5 import QtWidgets
from messenger.messages import *
import time
class CallsManager:
def __init__(self, tox):
self._call = av.calls.AV(tox.AV) # object with data about calls
self._call_widgets = {} # dict of incoming call widgets
self._incoming_calls = set()
# -----------------------------------------------------------------------------------------------------------------
# AV support
# -----------------------------------------------------------------------------------------------------------------
def get_call(self):
return self._call
call = property(get_call)
def call_click(self, audio=True, video=False):
"""User clicked audio button in main window"""
num = self.get_active_number()
if not self.is_active_a_friend():
return
if num not in self._call and self.is_active_online(): # start call
if not Settings.get_instance().audio['enabled']:
return
self._call(num, audio, video)
self._screen.active_call()
if video:
text = QtWidgets.QApplication.translate("incoming_call", "Outgoing video call")
else:
text = QtWidgets.QApplication.translate("incoming_call", "Outgoing audio call")
self.get_curr_friend().append_message(InfoMessage(text, time.time()))
self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
self._messages.scrollToBottom()
elif num in self._call: # finish or cancel call if you call with active friend
self.stop_call(num, False)
def incoming_call(self, audio, video, friend_number):
"""
Incoming call from friend.
"""
if not Settings.get_instance().audio['enabled']:
return
friend = self.get_friend_by_number(friend_number)
if video:
text = QtWidgets.QApplication.translate("incoming_call", "Incoming video call")
else:
text = QtWidgets.QApplication.translate("incoming_call", "Incoming audio call")
friend.append_message(InfoMessage(text, time.time()))
self._incoming_calls.add(friend_number)
if friend_number == self.get_active_number():
self._screen.incoming_call()
self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
self._messages.scrollToBottom()
else:
friend.actions = True
self._call_widgets[friend_number] = avwidgets.IncomingCallWidget(friend_number, text, friend.name)
self._call_widgets[friend_number].set_pixmap(friend.get_pixmap())
self._call_widgets[friend_number].show()
def accept_call(self, friend_number, audio, video):
"""
Accept incoming call with audio or video
"""
self._call.accept_call(friend_number, audio, video)
self._screen.active_call()
if friend_number in self._incoming_calls:
self._incoming_calls.remove(friend_number)
del self._call_widgets[friend_number]
def stop_call(self, friend_number, by_friend):
"""
Stop call with friend
"""
if friend_number in self._incoming_calls:
self._incoming_calls.remove(friend_number)
text = QtWidgets.QApplication.translate("incoming_call", "Call declined")
else:
text = QtWidgets.QApplication.translate("incoming_call", "Call finished")
self._screen.call_finished()
is_video = self._call.is_video_call(friend_number)
self._call.finish_call(friend_number, by_friend) # finish or decline call
if hasattr(self, '_call_widget'):
self._call_widget[friend_number].close()
del self._call_widget[friend_number]
def destroy_window():
if is_video:
cv2.destroyWindow(str(friend_number))
threading.Timer(2.0, destroy_window).start()
friend = self.get_friend_by_number(friend_number)
friend.append_message(InfoMessage(text, time.time()))
if friend_number == self.get_active_number():
self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
self._messages.scrollToBottom()

View file

@ -1,6 +1,6 @@
import random import random
import urllib.request import urllib.request
from util import log, curr_directory from util.util import log, curr_directory
from user_data import settings from user_data import settings
from PyQt5 import QtNetwork, QtCore from PyQt5 import QtNetwork, QtCore
import json import json

View file

@ -11,6 +11,7 @@ import threading
import util import util
import cv2 import cv2
import numpy as np import numpy as np
from threads import invoke_in_main_thread, execute
# TODO: use closures # TODO: use closures
@ -19,15 +20,14 @@ import numpy as np
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
def self_connection_status(tox_link): def self_connection_status(tox, profile):
""" """
Current user changed connection status (offline, UDP, TCP) Current user changed connection status (offline, UDP, TCP)
""" """
def wrapped(tox, connection, user_data): def wrapped(tox_link, connection, user_data):
print('Connection status: ', str(connection)) print('Connection status: ', str(connection))
profile = Profile.get_instance()
if profile.status is None: if profile.status is None:
status = tox_link.self_get_status() status = tox.self_get_status()
invoke_in_main_thread(profile.set_status, status) invoke_in_main_thread(profile.set_status, status)
elif connection == TOX_CONNECTION['NONE']: elif connection == TOX_CONNECTION['NONE']:
invoke_in_main_thread(profile.set_status, None) invoke_in_main_thread(profile.set_status, None)
@ -39,67 +39,73 @@ def self_connection_status(tox_link):
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
def friend_status(tox, friend_num, new_status, user_data): def friend_status(profile, settings):
""" def wrapped(tox, friend_num, new_status, user_data):
Check friend's status (none, busy, away) """
""" Check friend's status (none, busy, away)
print("Friend's #{} status changed!".format(friend_num)) """
profile = Profile.get_instance() print("Friend's #{} status changed!".format(friend_num))
friend = profile.get_friend_by_number(friend_num) friend = profile.get_friend_by_number(friend_num)
if friend.status is None and Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: if friend.status is None and settings['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)
invoke_in_main_thread(QtCore.QTimer.singleShot, 5000, lambda: profile.send_files(friend_num))
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)
"""
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)
invoke_in_main_thread(profile.update_filtration)
if Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
elif friend.status is None: invoke_in_main_thread(friend.set_status, new_status)
invoke_in_main_thread(profile.send_avatar, friend_num) invoke_in_main_thread(QtCore.QTimer.singleShot, 5000, lambda: profile.send_files(friend_num))
invoke_in_main_thread(PluginLoader.get_instance().friend_online, friend_num) invoke_in_main_thread(profile.update_filtration)
return wrapped
def friend_name(tox, friend_num, name, size, user_data): def friend_connection_status(profile, settings, plugin_loader):
""" def wrapped(tox, friend_num, new_status, user_data):
Friend changed his name """
""" Check friend's connection status (offline, udp, tcp)
profile = Profile.get_instance() """
print('New name friend #' + str(friend_num)) print("Friend #{} connection status: {}".format(friend_num, new_status))
invoke_in_main_thread(profile.new_name, friend_num, name) friend = profile.get_friend_by_number(friend_num)
if new_status == TOX_CONNECTION['NONE']:
invoke_in_main_thread(profile.friend_exit, friend_num)
invoke_in_main_thread(profile.update_filtration)
if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
elif friend.status is None:
invoke_in_main_thread(profile.send_avatar, friend_num)
invoke_in_main_thread(plugin_loader.friend_online, friend_num)
return wrapped
def friend_status_message(tox, friend_num, status_message, size, user_data): def friend_name(profile):
""" def wrapped(tox, friend_num, name, size, user_data):
:return: function for callback friend_status_message. It updates friend's status message """
and calls window repaint Friend changed his name
""" """
profile = Profile.get_instance() print('New name friend #' + str(friend_num))
friend = profile.get_friend_by_number(friend_num) invoke_in_main_thread(profile.new_name, friend_num, name)
invoke_in_main_thread(friend.set_status_message, status_message)
print('User #{} has new status'.format(friend_num)) return wrapped
invoke_in_main_thread(profile.send_messages, friend_num)
if profile.get_active_number() == friend_num:
invoke_in_main_thread(profile.set_active)
def friend_message(window, tray): def friend_status_message(profile):
def wrapped(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
"""
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))
invoke_in_main_thread(profile.send_messages, friend_num)
if profile.get_active_number() == friend_num:
invoke_in_main_thread(profile.set_active)
return wrapped
def friend_message(profile, settings, window, tray):
""" """
New message from friend New message from friend
""" """
def wrapped(tox, friend_number, message_type, message, size, user_data): def wrapped(tox, friend_number, message_type, message, size, user_data):
profile = Profile.get_instance()
settings = Settings.get_instance()
message = str(message, 'utf-8') message = str(message, 'utf-8')
invoke_in_main_thread(profile.new_message, friend_number, message_type, message) invoke_in_main_thread(profile.new_message, friend_number, message_type, message)
if not window.isActiveWindow(): if not window.isActiveWindow():
@ -109,6 +115,7 @@ def friend_message(window, tray):
if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
sound_notification(SOUND_NOTIFICATION['MESSAGE']) sound_notification(SOUND_NOTIFICATION['MESSAGE'])
invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png'))
return wrapped return wrapped
@ -174,31 +181,40 @@ def tox_file_recv(window, tray):
return wrapped return wrapped
def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, user_data): def file_recv_chunk(file_transfer_handler):
""" """
Incoming chunk Incoming chunk
""" """
_thread.execute(Profile.get_instance().incoming_chunk, friend_number, file_number, position, def wrapped(tox, friend_number, file_number, position, chunk, length, user_data):
chunk[:length] if length else None) execute(file_transfer_handler.incoming_chunk, friend_number, file_number, position,
chunk[:length] if length else None)
return wrapped
def file_chunk_request(tox, friend_number, file_number, position, size, user_data): def file_chunk_request(file_transfer_handler):
""" """
Outgoing chunk Outgoing chunk
""" """
Profile.get_instance().outgoing_chunk(friend_number, file_number, position, size) def wrapped(tox, friend_number, file_number, position, size, user_data):
execute(file_transfer_handler.outgoing_chunk, friend_number, file_number, position, size)
return wrapped
def file_recv_control(tox, friend_number, file_number, file_control, user_data): def file_recv_control(file_transfer_handler):
""" """
Friend cancelled, paused or resumed file transfer Friend cancelled, paused or resumed file transfer
""" """
if file_control == TOX_FILE_CONTROL['CANCEL']: def wrapped(tox, friend_number, file_number, file_control, user_data):
invoke_in_main_thread(Profile.get_instance().cancel_transfer, friend_number, file_number, True) if file_control == TOX_FILE_CONTROL['CANCEL']:
elif file_control == TOX_FILE_CONTROL['PAUSE']: file_transfer_handler.cancel_transfer(friend_number, file_number, True)
invoke_in_main_thread(Profile.get_instance().pause_transfer, friend_number, file_number, True) elif file_control == TOX_FILE_CONTROL['PAUSE']:
elif file_control == TOX_FILE_CONTROL['RESUME']: file_transfer_handler.pause_transfer(friend_number, file_number, True)
invoke_in_main_thread(Profile.get_instance().resume_transfer, friend_number, file_number, True) elif file_control == TOX_FILE_CONTROL['RESUME']:
file_transfer_handler.resume_transfer(friend_number, file_number, True)
return wrapped
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# Callbacks - custom packets # Callbacks - custom packets

View file

@ -1,3 +1,7 @@
import user_data.settings
import wrapper.tox
import wrapper.toxcore_enums_and_consts as enums
import ctypes
def tox_factory(data=None, settings=None): def tox_factory(data=None, settings=None):
@ -7,8 +11,9 @@ def tox_factory(data=None, settings=None):
:return: new tox instance :return: new tox instance
""" """
if settings is None: if settings is None:
settings = Settings.get_default_settings() settings = user_data.settings.Settings.get_default_settings()
tox_options = Tox.options_new()
tox_options = wrapper.tox.Tox.options_new()
tox_options.contents.udp_enabled = settings['udp_enabled'] tox_options.contents.udp_enabled = settings['udp_enabled']
tox_options.contents.proxy_type = settings['proxy_type'] tox_options.contents.proxy_type = settings['proxy_type']
tox_options.contents.proxy_host = bytes(settings['proxy_host'], 'UTF-8') tox_options.contents.proxy_host = bytes(settings['proxy_host'], 'UTF-8')
@ -17,11 +22,12 @@ def tox_factory(data=None, settings=None):
tox_options.contents.end_port = settings['end_port'] tox_options.contents.end_port = settings['end_port']
tox_options.contents.tcp_port = settings['tcp_port'] tox_options.contents.tcp_port = settings['tcp_port']
if data: # load existing profile if data: # load existing profile
tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['TOX_SAVE'] tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['TOX_SAVE']
tox_options.contents.savedata_data = c_char_p(data) tox_options.contents.savedata_data = ctypes.c_char_p(data)
tox_options.contents.savedata_length = len(data) tox_options.contents.savedata_length = len(data)
else: # create new profile else: # create new profile
tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['NONE'] tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['NONE']
tox_options.contents.savedata_data = None tox_options.contents.savedata_data = None
tox_options.contents.savedata_length = 0 tox_options.contents.savedata_length = 0
return Tox(tox_options)
return wrapper.tox.Tox(tox_options)

View file

@ -1,8 +1,8 @@
from db.history import * from db.database import *
from contacts import basecontact from contacts import basecontact
import util import util
from messenger.messages import * from messenger.messages import *
from file_tansfers import file_transfers as ft from file_transfers import file_transfers as ft
import re import re

View file

@ -0,0 +1,402 @@
class ContactsManager:
def __init__(self, tox, settings, screen):
self._tox = tox
self._settings = settings
self._contacts, self._active_friend = [], -1
self._sorting = settings['sorting']
data = tox.self_get_friend_list()
self._filter_string = ''
self._friend_item_height = 40 if settings['compact_mode'] else 70
screen.online_contacts.setCurrentIndex(int(self._sorting))
aliases = settings['friends_aliases']
for i in data: # creates list of friends
tox_id = tox.friend_get_public_key(i)
try:
alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1]
except:
alias = ''
item = self.create_friend_item()
name = alias or tox.friend_get_name(i) or tox_id
status_message = tox.friend_get_status_message(i)
if not self._history.friend_exists_in_db(tox_id):
self._history.add_friend_to_db(tox_id)
message_getter = self._history.messages_getter(tox_id)
friend = Friend(message_getter, i, name, status_message, item, tox_id)
friend.set_alias(alias)
self._contacts.append(friend)
if len(self._contacts):
self.set_active(0)
self.filtration_and_sorting(self._sorting)
def get_friend(self, num):
if num < 0 or num >= len(self._contacts):
return None
return self._contacts[num]
def get_curr_friend(self):
return self._contacts[self._active_friend] if self._active_friend + 1 else None
# -----------------------------------------------------------------------------------------------------------------
# Work with active friend
# -----------------------------------------------------------------------------------------------------------------
def get_active(self):
return self._active_friend
def set_active(self, value=None):
"""
Change current active friend or update info
:param value: number of new active friend in friend's list or None to update active user's data
"""
if value is None and self._active_friend == -1: # nothing to update
return
if value == -1: # all friends were deleted
self._screen.account_name.setText('')
self._screen.account_status.setText('')
self._screen.account_status.setToolTip('')
self._active_friend = -1
self._screen.account_avatar.setHidden(True)
self._messages.clear()
self._screen.messageEdit.clear()
return
try:
self.send_typing(False)
self._screen.typing.setVisible(False)
if value is not None:
if self._active_friend + 1 and self._active_friend != value:
try:
self.get_curr_friend().curr_text = self._screen.messageEdit.toPlainText()
except:
pass
friend = self._contacts[value]
friend.remove_invalid_unsent_files()
if self._active_friend != value:
self._screen.messageEdit.setPlainText(friend.curr_text)
self._active_friend = value
friend.reset_messages()
if not Settings.get_instance()['save_history']:
friend.delete_old_messages()
self._messages.clear()
friend.load_corr()
messages = friend.get_corr()[-PAGE_SIZE:]
self._load_history = False
for message in messages:
if message.get_type() <= 1:
data = message.get_data()
self.create_message_item(data[0],
data[2],
data[1],
data[3])
elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']:
if message.get_status() is None:
self.create_unsent_file_item(message)
continue
item = self.create_file_transfer_item(message)
if message.get_status() in ACTIVE_FILE_TRANSFERS: # active file transfer
try:
ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())]
ft.set_state_changed_handler(item.update_transfer_state)
ft.signal()
except:
print('Incoming not started transfer - no info found')
elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline
self.create_inline_item(message.get_data())
elif message.get_type() < 5: # info message
data = message.get_data()
self.create_message_item(data[0],
data[2],
'',
data[3])
else:
data = message.get_data()
self.create_gc_message_item(data[0], data[2], data[1], data[4], data[3])
self._messages.scrollToBottom()
self._load_history = True
if value in self._call:
self._screen.active_call()
elif value in self._incoming_calls:
self._screen.incoming_call()
else:
self._screen.call_finished()
else:
friend = self.get_curr_friend()
self._screen.account_name.setText(friend.name)
self._screen.account_status.setText(friend.status_message)
self._screen.account_status.setToolTip(friend.get_full_status())
if friend.tox_id is None:
avatar_path = curr_directory() + '/images/group.png'
else:
avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
if not os.path.isfile(avatar_path): # load default image
avatar_path = curr_directory() + '/images/avatar.png'
os.chdir(os.path.dirname(avatar_path))
pixmap = QtGui.QPixmap(avatar_path)
self._screen.account_avatar.setPixmap(pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation))
except Exception as ex: # no friend found. ignore
log('Friend value: ' + str(value))
log('Error in set active: ' + str(ex))
raise
def set_active_by_number_and_type(self, number, is_friend):
for i in range(len(self._contacts)):
c = self._contacts[i]
if c.number == number and (type(c) is Friend == is_friend):
self._active_friend = i
break
active_friend = property(get_active, set_active)
# -----------------------------------------------------------------------------------------------------------------
# Filtration
# -----------------------------------------------------------------------------------------------------------------
def filtration_and_sorting(self, sorting=0, filter_str=''):
"""
Filtration of friends list
:param sorting: 0 - no sort, 1 - online only, 2 - online first, 4 - by name
:param filter_str: show contacts which name contains this substring
"""
filter_str = filter_str.lower()
settings = Settings.get_instance()
number = self.get_active_number()
is_friend = self.is_active_a_friend()
if sorting > 1:
if sorting & 2:
self._contacts = sorted(self._contacts, key=lambda x: int(x.status is not None), reverse=True)
if sorting & 4:
if not sorting & 2:
self._contacts = sorted(self._contacts, key=lambda x: x.name.lower())
else: # save results of prev sorting
online_friends = filter(lambda x: x.status is not None, self._contacts)
count = len(list(online_friends))
part1 = self._contacts[:count]
part2 = self._contacts[count:]
part1 = sorted(part1, key=lambda x: x.name.lower())
part2 = sorted(part2, key=lambda x: x.name.lower())
self._contacts = part1 + part2
else: # sort by number
online_friends = filter(lambda x: x.status is not None, self._contacts)
count = len(list(online_friends))
part1 = self._contacts[:count]
part2 = self._contacts[count:]
part1 = sorted(part1, key=lambda x: x.number)
part2 = sorted(part2, key=lambda x: x.number)
self._contacts = part1 + part2
self._screen.friends_list.clear()
for contact in self._contacts:
contact.set_widget(self.create_friend_item())
for index, friend in enumerate(self._contacts):
friend.visibility = (friend.status is not None or not (sorting & 1)) and (filter_str in friend.name.lower())
friend.visibility = friend.visibility or friend.messages or friend.actions
if friend.visibility:
self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, self._friend_item_height))
else:
self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 0))
self._sorting, self._filter_string = sorting, filter_str
settings['sorting'] = self._sorting
settings.save()
self.set_active_by_number_and_type(number, is_friend)
def update_filtration(self):
"""
Update list of contacts when 1 of friends change connection status
"""
self.filtration_and_sorting(self._sorting, self._filter_string)
def create_friend_item(self):
"""
Method-factory
:return: new widget for friend instance
"""
return self._factory.friend_item()
# -----------------------------------------------------------------------------------------------------------------
# Work with friends (remove, block, set alias, get public key)
# -----------------------------------------------------------------------------------------------------------------
def set_alias(self, num):
"""
Set new alias for friend
"""
friend = self._contacts[num]
name = friend.name
dialog = QtWidgets.QApplication.translate('MainWindow',
"Enter new alias for friend {} or leave empty to use friend's name:")
dialog = dialog.format(name)
title = QtWidgets.QApplication.translate('MainWindow',
'Set alias')
text, ok = QtWidgets.QInputDialog.getText(None,
title,
dialog,
QtWidgets.QLineEdit.Normal,
name)
if ok:
settings = Settings.get_instance()
aliases = settings['friends_aliases']
if text:
friend.name = bytes(text, 'utf-8')
try:
index = list(map(lambda x: x[0], aliases)).index(friend.tox_id)
aliases[index] = (friend.tox_id, text)
except:
aliases.append((friend.tox_id, text))
friend.set_alias(text)
else: # use default name
friend.name = bytes(self._tox.friend_get_name(friend.number), 'utf-8')
friend.set_alias('')
try:
index = list(map(lambda x: x[0], aliases)).index(friend.tox_id)
del aliases[index]
except:
pass
settings.save()
if num == self.get_active_number() and self.is_active_a_friend():
self.update()
def friend_public_key(self, num):
return self._contacts[num].tox_id
def delete_friend(self, num):
"""
Removes friend from contact list
:param num: number of friend in list
"""
friend = self._contacts[num]
settings = Settings.get_instance()
try:
index = list(map(lambda x: x[0], settings['friends_aliases'])).index(friend.tox_id)
del settings['friends_aliases'][index]
except:
pass
if friend.tox_id in settings['notes']:
del settings['notes'][friend.tox_id]
settings.save()
self.clear_history(num)
if self._history.friend_exists_in_db(friend.tox_id):
self._history.delete_friend_from_db(friend.tox_id)
self._tox.friend_delete(friend.number)
del self._contacts[num]
self._screen.friends_list.takeItem(num)
if num == self._active_friend: # active friend was deleted
if not len(self._contacts): # last friend was deleted
self.set_active(-1)
else:
self.set_active(0)
data = self._tox.get_savedata()
ProfileManager.get_instance().save_profile(data)
def add_friend(self, tox_id):
"""
Adds friend to list
"""
num = self._tox.friend_add_norequest(tox_id) # num - friend number
item = self.create_friend_item()
try:
if not self._history.friend_exists_in_db(tox_id):
self._history.add_friend_to_db(tox_id)
message_getter = self._history.messages_getter(tox_id)
except Exception as ex: # something is wrong
log('Accept friend request failed! ' + str(ex))
message_getter = None
friend = Friend(message_getter, num, tox_id, '', item, tox_id)
self._contacts.append(friend)
def block_user(self, tox_id):
"""
Block user with specified tox id (or public key) - delete from friends list and ignore friend requests
"""
tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2]
if tox_id == self.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]:
return
settings = Settings.get_instance()
if tox_id not in settings['blocked']:
settings['blocked'].append(tox_id)
settings.save()
try:
num = self._tox.friend_by_public_key(tox_id)
self.delete_friend(num)
data = self._tox.get_savedata()
ProfileManager.get_instance().save_profile(data)
except: # not in friend list
pass
def unblock_user(self, tox_id, add_to_friend_list):
"""
Unblock user
:param tox_id: tox id of contact
:param add_to_friend_list: add this contact to friend list or not
"""
s = Settings.get_instance()
s['blocked'].remove(tox_id)
s.save()
if add_to_friend_list:
self.add_friend(tox_id)
data = self._tox.get_savedata()
ProfileManager.get_instance().save_profile(data)
# -----------------------------------------------------------------------------------------------------------------
# Friend requests
# -----------------------------------------------------------------------------------------------------------------
def send_friend_request(self, tox_id, message):
"""
Function tries to send request to contact with specified id
:param tox_id: id of new contact or tox dns 4 value
:param message: additional message
:return: True on success else error string
"""
try:
message = message or 'Hello! Add me to your contact list please'
if '@' in tox_id: # value like groupbot@toxme.io
tox_id = tox_dns(tox_id)
if tox_id is None:
raise Exception('TOX DNS lookup failed')
if len(tox_id) == TOX_PUBLIC_KEY_SIZE * 2: # public key
self.add_friend(tox_id)
msgBox = QtWidgets.QMessageBox()
msgBox.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "Friend added"))
text = (QtWidgets.QApplication.translate("MainWindow", 'Friend added without sending friend request'))
msgBox.setText(text)
msgBox.exec_()
else:
result = self._tox.friend_add(tox_id, message.encode('utf-8'))
tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2]
item = self.create_friend_item()
if not self._history.friend_exists_in_db(tox_id):
self._history.add_friend_to_db(tox_id)
message_getter = self._history.messages_getter(tox_id)
friend = Friend(message_getter, result, tox_id, '', item, tox_id)
self._contacts.append(friend)
data = self._tox.get_savedata()
ProfileManager.get_instance().save_profile(data)
return True
except Exception as ex: # wrong data
log('Friend request failed with ' + str(ex))
return str(ex)
def process_friend_request(self, tox_id, message):
"""
Accept or ignore friend request
:param tox_id: tox id of contact
:param message: message
"""
try:
text = QtWidgets.QApplication.translate('MainWindow', 'User {} wants to add you to contact list. Message:\n{}')
info = text.format(tox_id, message)
fr_req = QtWidgets.QApplication.translate('MainWindow', 'Friend request')
reply = QtWidgets.QMessageBox.question(None, fr_req, info, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.Yes: # accepted
self.add_friend(tox_id)
data = self._tox.get_savedata()
ProfileManager.get_instance().save_profile(data)
except Exception as ex: # something is wrong
log('Accept friend request failed! ' + str(ex))

View file

@ -6,8 +6,8 @@ from wrapper.toxcore_enums_and_consts import *
from ctypes import * from ctypes import *
from util import log, Singleton, curr_directory from util import log, Singleton, curr_directory
from network.tox_dns import tox_dns from network.tox_dns import tox_dns
from db.history import * from db.database import *
from file_tansfers.file_transfers import * from file_transfers.file_transfers import *
import time import time
from av import calls from av import calls
import plugin_support import plugin_support
@ -38,42 +38,15 @@ class Profile(basecontact.BaseContact, Singleton):
self._messages = screen.messages self._messages = screen.messages
self._tox = tox self._tox = tox
self._file_transfers = {} # dict of file transfers. key - tuple (friend_number, file_number) self._file_transfers = {} # dict of file transfers. key - tuple (friend_number, file_number)
self._call = calls.AV(tox.AV) # object with data about calls
self._call_widgets = {} # dict of incoming call widgets
self._incoming_calls = set()
self._load_history = True self._load_history = True
self._waiting_for_reconnection = False self._waiting_for_reconnection = False
self._factory = items_factory.ItemsFactory(self._screen.friends_list, self._messages) self._factory = items_factory.ItemsFactory(self._screen.friends_list, self._messages)
settings = Settings.get_instance() settings = Settings.get_instance()
self._sorting = settings['sorting']
self._show_avatars = settings['show_avatars'] self._show_avatars = settings['show_avatars']
self._filter_string = ''
self._friend_item_height = 40 if settings['compact_mode'] else 70
self._paused_file_transfers = dict(settings['paused_file_transfers']) self._paused_file_transfers = dict(settings['paused_file_transfers'])
# key - file id, value: [path, friend number, is incoming, start position] # key - file id, value: [path, friend number, is incoming, start position]
screen.online_contacts.setCurrentIndex(int(self._sorting))
aliases = settings['friends_aliases']
data = tox.self_get_friend_list()
self._history = History(tox.self_get_public_key()) # connection to db self._history = History(tox.self_get_public_key()) # connection to db
self._contacts, self._active_friend = [], -1
for i in data: # creates list of friends
tox_id = tox.friend_get_public_key(i)
try:
alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1]
except:
alias = ''
item = self.create_friend_item()
name = alias or tox.friend_get_name(i) or tox_id
status_message = tox.friend_get_status_message(i)
if not self._history.friend_exists_in_db(tox_id):
self._history.add_friend_to_db(tox_id)
message_getter = self._history.messages_getter(tox_id)
friend = Friend(message_getter, i, name, status_message, item, tox_id)
friend.set_alias(alias)
self._contacts.append(friend)
if len(self._contacts):
self.set_active(0)
self.filtration_and_sorting(self._sorting)
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# Edit current user's data # Edit current user's data
@ -119,190 +92,12 @@ class Profile(basecontact.BaseContact, Singleton):
self._tox_id = self._tox.self_get_address() self._tox_id = self._tox.self_get_address()
return self._tox_id return self._tox_id
# -----------------------------------------------------------------------------------------------------------------
# Filtration
# -----------------------------------------------------------------------------------------------------------------
def filtration_and_sorting(self, sorting=0, filter_str=''):
"""
Filtration of friends list
:param sorting: 0 - no sort, 1 - online only, 2 - online first, 4 - by name
:param filter_str: show contacts which name contains this substring
"""
filter_str = filter_str.lower()
settings = Settings.get_instance()
number = self.get_active_number()
is_friend = self.is_active_a_friend()
if sorting > 1:
if sorting & 2:
self._contacts = sorted(self._contacts, key=lambda x: int(x.status is not None), reverse=True)
if sorting & 4:
if not sorting & 2:
self._contacts = sorted(self._contacts, key=lambda x: x.name.lower())
else: # save results of prev sorting
online_friends = filter(lambda x: x.status is not None, self._contacts)
count = len(list(online_friends))
part1 = self._contacts[:count]
part2 = self._contacts[count:]
part1 = sorted(part1, key=lambda x: x.name.lower())
part2 = sorted(part2, key=lambda x: x.name.lower())
self._contacts = part1 + part2
else: # sort by number
online_friends = filter(lambda x: x.status is not None, self._contacts)
count = len(list(online_friends))
part1 = self._contacts[:count]
part2 = self._contacts[count:]
part1 = sorted(part1, key=lambda x: x.number)
part2 = sorted(part2, key=lambda x: x.number)
self._contacts = part1 + part2
self._screen.friends_list.clear()
for contact in self._contacts:
contact.set_widget(self.create_friend_item())
for index, friend in enumerate(self._contacts):
friend.visibility = (friend.status is not None or not (sorting & 1)) and (filter_str in friend.name.lower())
friend.visibility = friend.visibility or friend.messages or friend.actions
if friend.visibility:
self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, self._friend_item_height))
else:
self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 0))
self._sorting, self._filter_string = sorting, filter_str
settings['sorting'] = self._sorting
settings.save()
self.set_active_by_number_and_type(number, is_friend)
def update_filtration(self):
"""
Update list of contacts when 1 of friends change connection status
"""
self.filtration_and_sorting(self._sorting, self._filter_string)
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# Friend getters # Friend getters
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
def get_friend_by_number(self, num): def get_friend_by_number(self, num):
return list(filter(lambda x: x.number == num and type(x) is Friend, self._contacts))[0] return list(filter(lambda x: x.number == num and type(x) is Friend, self._contacts))[0]
def get_friend(self, num):
if num < 0 or num >= len(self._contacts):
return None
return self._contacts[num]
def get_curr_friend(self):
return self._contacts[self._active_friend] if self._active_friend + 1 else None
# -----------------------------------------------------------------------------------------------------------------
# Work with active friend
# -----------------------------------------------------------------------------------------------------------------
def get_active(self):
return self._active_friend
def set_active(self, value=None):
"""
Change current active friend or update info
:param value: number of new active friend in friend's list or None to update active user's data
"""
if value is None and self._active_friend == -1: # nothing to update
return
if value == -1: # all friends were deleted
self._screen.account_name.setText('')
self._screen.account_status.setText('')
self._screen.account_status.setToolTip('')
self._active_friend = -1
self._screen.account_avatar.setHidden(True)
self._messages.clear()
self._screen.messageEdit.clear()
return
try:
self.send_typing(False)
self._screen.typing.setVisible(False)
if value is not None:
if self._active_friend + 1 and self._active_friend != value:
try:
self.get_curr_friend().curr_text = self._screen.messageEdit.toPlainText()
except:
pass
friend = self._contacts[value]
friend.remove_invalid_unsent_files()
if self._active_friend != value:
self._screen.messageEdit.setPlainText(friend.curr_text)
self._active_friend = value
friend.reset_messages()
if not Settings.get_instance()['save_history']:
friend.delete_old_messages()
self._messages.clear()
friend.load_corr()
messages = friend.get_corr()[-PAGE_SIZE:]
self._load_history = False
for message in messages:
if message.get_type() <= 1:
data = message.get_data()
self.create_message_item(data[0],
data[2],
data[1],
data[3])
elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']:
if message.get_status() is None:
self.create_unsent_file_item(message)
continue
item = self.create_file_transfer_item(message)
if message.get_status() in ACTIVE_FILE_TRANSFERS: # active file transfer
try:
ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())]
ft.set_state_changed_handler(item.update_transfer_state)
ft.signal()
except:
print('Incoming not started transfer - no info found')
elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline
self.create_inline_item(message.get_data())
elif message.get_type() < 5: # info message
data = message.get_data()
self.create_message_item(data[0],
data[2],
'',
data[3])
else:
data = message.get_data()
self.create_gc_message_item(data[0], data[2], data[1], data[4], data[3])
self._messages.scrollToBottom()
self._load_history = True
if value in self._call:
self._screen.active_call()
elif value in self._incoming_calls:
self._screen.incoming_call()
else:
self._screen.call_finished()
else:
friend = self.get_curr_friend()
self._screen.account_name.setText(friend.name)
self._screen.account_status.setText(friend.status_message)
self._screen.account_status.setToolTip(friend.get_full_status())
if friend.tox_id is None:
avatar_path = curr_directory() + '/images/group.png'
else:
avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
if not os.path.isfile(avatar_path): # load default image
avatar_path = curr_directory() + '/images/avatar.png'
os.chdir(os.path.dirname(avatar_path))
pixmap = QtGui.QPixmap(avatar_path)
self._screen.account_avatar.setPixmap(pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation))
except Exception as ex: # no friend found. ignore
log('Friend value: ' + str(value))
log('Error in set active: ' + str(ex))
raise
def set_active_by_number_and_type(self, number, is_friend):
for i in range(len(self._contacts)):
c = self._contacts[i]
if c.number == number and (type(c) is Friend == is_friend):
self._active_friend = i
break
active_friend = property(get_active, set_active)
def get_last_message(self): def get_last_message(self):
if self._active_friend + 1: if self._active_friend + 1:
return self.get_curr_friend().get_last_message_text() return self.get_curr_friend().get_last_message_text()
@ -428,6 +223,25 @@ class Profile(basecontact.BaseContact, Singleton):
except Exception as ex: except Exception as ex:
log('Sending pending messages failed with ' + str(ex)) log('Sending pending messages failed with ' + str(ex))
def split_message(self, message):
messages = []
while len(message) > TOX_MAX_MESSAGE_LENGTH:
size = TOX_MAX_MESSAGE_LENGTH * 4 / 5
last_part = message[size:TOX_MAX_MESSAGE_LENGTH]
if ' ' in last_part:
index = last_part.index(' ')
elif ',' in last_part:
index = last_part.index(',')
elif '.' in last_part:
index = last_part.index('.')
else:
index = TOX_MAX_MESSAGE_LENGTH - size - 1
index += size + 1
messages.append(message[:index])
message = message[index:]
return messages
def split_and_send(self, number, message_type, message): def split_and_send(self, number, message_type, message):
""" """
Message splitting. Message length cannot be > TOX_MAX_MESSAGE_LENGTH Message splitting. Message length cannot be > TOX_MAX_MESSAGE_LENGTH
@ -629,12 +443,6 @@ class Profile(basecontact.BaseContact, Singleton):
# Friend, message and file transfer items creation # Friend, message and file transfer items creation
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
def create_friend_item(self):
"""
Method-factory
:return: new widget for friend instance
"""
return self._factory.friend_item()
def create_message_item(self, text, time, owner, message_type, append=True): def create_message_item(self, text, time, owner, message_type, append=True):
if message_type == MESSAGE_TYPE['INFO_MESSAGE']: if message_type == MESSAGE_TYPE['INFO_MESSAGE']:
@ -678,188 +486,6 @@ class Profile(basecontact.BaseContact, Singleton):
def create_inline_item(self, data, append=True): def create_inline_item(self, data, append=True):
return self._factory.inline_item(data, append) return self._factory.inline_item(data, append)
# -----------------------------------------------------------------------------------------------------------------
# Work with friends (remove, block, set alias, get public key)
# -----------------------------------------------------------------------------------------------------------------
def set_alias(self, num):
"""
Set new alias for friend
"""
friend = self._contacts[num]
name = friend.name
dialog = QtWidgets.QApplication.translate('MainWindow',
"Enter new alias for friend {} or leave empty to use friend's name:")
dialog = dialog.format(name)
title = QtWidgets.QApplication.translate('MainWindow',
'Set alias')
text, ok = QtWidgets.QInputDialog.getText(None,
title,
dialog,
QtWidgets.QLineEdit.Normal,
name)
if ok:
settings = Settings.get_instance()
aliases = settings['friends_aliases']
if text:
friend.name = bytes(text, 'utf-8')
try:
index = list(map(lambda x: x[0], aliases)).index(friend.tox_id)
aliases[index] = (friend.tox_id, text)
except:
aliases.append((friend.tox_id, text))
friend.set_alias(text)
else: # use default name
friend.name = bytes(self._tox.friend_get_name(friend.number), 'utf-8')
friend.set_alias('')
try:
index = list(map(lambda x: x[0], aliases)).index(friend.tox_id)
del aliases[index]
except:
pass
settings.save()
if num == self.get_active_number() and self.is_active_a_friend():
self.update()
def friend_public_key(self, num):
return self._contacts[num].tox_id
def delete_friend(self, num):
"""
Removes friend from contact list
:param num: number of friend in list
"""
friend = self._contacts[num]
settings = Settings.get_instance()
try:
index = list(map(lambda x: x[0], settings['friends_aliases'])).index(friend.tox_id)
del settings['friends_aliases'][index]
except:
pass
if friend.tox_id in settings['notes']:
del settings['notes'][friend.tox_id]
settings.save()
self.clear_history(num)
if self._history.friend_exists_in_db(friend.tox_id):
self._history.delete_friend_from_db(friend.tox_id)
self._tox.friend_delete(friend.number)
del self._contacts[num]
self._screen.friends_list.takeItem(num)
if num == self._active_friend: # active friend was deleted
if not len(self._contacts): # last friend was deleted
self.set_active(-1)
else:
self.set_active(0)
data = self._tox.get_savedata()
ProfileManager.get_instance().save_profile(data)
def add_friend(self, tox_id):
"""
Adds friend to list
"""
num = self._tox.friend_add_norequest(tox_id) # num - friend number
item = self.create_friend_item()
try:
if not self._history.friend_exists_in_db(tox_id):
self._history.add_friend_to_db(tox_id)
message_getter = self._history.messages_getter(tox_id)
except Exception as ex: # something is wrong
log('Accept friend request failed! ' + str(ex))
message_getter = None
friend = Friend(message_getter, num, tox_id, '', item, tox_id)
self._contacts.append(friend)
def block_user(self, tox_id):
"""
Block user with specified tox id (or public key) - delete from friends list and ignore friend requests
"""
tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2]
if tox_id == self.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]:
return
settings = Settings.get_instance()
if tox_id not in settings['blocked']:
settings['blocked'].append(tox_id)
settings.save()
try:
num = self._tox.friend_by_public_key(tox_id)
self.delete_friend(num)
data = self._tox.get_savedata()
ProfileManager.get_instance().save_profile(data)
except: # not in friend list
pass
def unblock_user(self, tox_id, add_to_friend_list):
"""
Unblock user
:param tox_id: tox id of contact
:param add_to_friend_list: add this contact to friend list or not
"""
s = Settings.get_instance()
s['blocked'].remove(tox_id)
s.save()
if add_to_friend_list:
self.add_friend(tox_id)
data = self._tox.get_savedata()
ProfileManager.get_instance().save_profile(data)
# -----------------------------------------------------------------------------------------------------------------
# Friend requests
# -----------------------------------------------------------------------------------------------------------------
def send_friend_request(self, tox_id, message):
"""
Function tries to send request to contact with specified id
:param tox_id: id of new contact or tox dns 4 value
:param message: additional message
:return: True on success else error string
"""
try:
message = message or 'Hello! Add me to your contact list please'
if '@' in tox_id: # value like groupbot@toxme.io
tox_id = tox_dns(tox_id)
if tox_id is None:
raise Exception('TOX DNS lookup failed')
if len(tox_id) == TOX_PUBLIC_KEY_SIZE * 2: # public key
self.add_friend(tox_id)
msgBox = QtWidgets.QMessageBox()
msgBox.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "Friend added"))
text = (QtWidgets.QApplication.translate("MainWindow", 'Friend added without sending friend request'))
msgBox.setText(text)
msgBox.exec_()
else:
result = self._tox.friend_add(tox_id, message.encode('utf-8'))
tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2]
item = self.create_friend_item()
if not self._history.friend_exists_in_db(tox_id):
self._history.add_friend_to_db(tox_id)
message_getter = self._history.messages_getter(tox_id)
friend = Friend(message_getter, result, tox_id, '', item, tox_id)
self._contacts.append(friend)
data = self._tox.get_savedata()
ProfileManager.get_instance().save_profile(data)
return True
except Exception as ex: # wrong data
log('Friend request failed with ' + str(ex))
return str(ex)
def process_friend_request(self, tox_id, message):
"""
Accept or ignore friend request
:param tox_id: tox id of contact
:param message: message
"""
try:
text = QtWidgets.QApplication.translate('MainWindow', 'User {} wants to add you to contact list. Message:\n{}')
info = text.format(tox_id, message)
fr_req = QtWidgets.QApplication.translate('MainWindow', 'Friend request')
reply = QtWidgets.QMessageBox.question(None, fr_req, info, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.Yes: # accepted
self.add_friend(tox_id)
data = self._tox.get_savedata()
ProfileManager.get_instance().save_profile(data)
except Exception as ex: # something is wrong
log('Accept friend request failed! ' + str(ex))
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# Reset # Reset
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
@ -900,307 +526,6 @@ class Profile(basecontact.BaseContact, Singleton):
s['paused_file_transfers'] = dict(self._paused_file_transfers) if s['resend_files'] else {} s['paused_file_transfers'] = dict(self._paused_file_transfers) if s['resend_files'] else {}
s.save() s.save()
# -----------------------------------------------------------------------------------------------------------------
# File transfers support
# -----------------------------------------------------------------------------------------------------------------
def incoming_file_transfer(self, friend_number, file_number, size, file_name):
"""
New transfer
:param friend_number: number of friend who sent file
:param file_number: file number
:param size: file size in bytes
:param file_name: file name without path
"""
settings = Settings.get_instance()
friend = self.get_friend_by_number(friend_number)
auto = settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends']
inline = is_inline(file_name) and settings['allow_inline']
file_id = self._tox.file_get_file_id(friend_number, file_number)
accepted = True
if file_id in self._paused_file_transfers:
data = self._paused_file_transfers[file_id]
pos = data[-1] if os.path.exists(data[0]) else 0
if pos >= size:
self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL'])
return
self._tox.file_seek(friend_number, file_number, pos)
self.accept_transfer(None, data[0], friend_number, file_number, size, False, pos)
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
time.time(),
TOX_FILE_TRANSFER_STATE['RUNNING'],
size,
file_name,
friend_number,
file_number)
elif inline and size < 1024 * 1024:
self.accept_transfer(None, '', friend_number, file_number, size, True)
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
time.time(),
TOX_FILE_TRANSFER_STATE['RUNNING'],
size,
file_name,
friend_number,
file_number)
elif auto:
path = settings['auto_accept_path'] or curr_directory()
self.accept_transfer(None, path + '/' + file_name, friend_number, file_number, size)
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
time.time(),
TOX_FILE_TRANSFER_STATE['RUNNING'],
size,
file_name,
friend_number,
file_number)
else:
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
time.time(),
TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'],
size,
file_name,
friend_number,
file_number)
accepted = False
if friend_number == self.get_active_number() and self.is_active_a_friend():
item = self.create_file_transfer_item(tm)
if accepted:
self._file_transfers[(friend_number, file_number)].set_state_changed_handler(item.update_transfer_state)
self._messages.scrollToBottom()
else:
friend.actions = True
friend.append_message(tm)
def cancel_transfer(self, friend_number, file_number, already_cancelled=False):
"""
Stop transfer
:param friend_number: number of friend
:param file_number: file number
:param already_cancelled: was cancelled by friend
"""
i = self.get_friend_by_number(friend_number).update_transfer_data(file_number,
TOX_FILE_TRANSFER_STATE['CANCELLED'])
if (friend_number, file_number) in self._file_transfers:
tr = self._file_transfers[(friend_number, file_number)]
if not already_cancelled:
tr.cancel()
else:
tr.cancelled()
if (friend_number, file_number) in self._file_transfers:
del tr
del self._file_transfers[(friend_number, file_number)]
else:
if not already_cancelled:
self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL'])
if friend_number == self.get_active_number() and self.is_active_a_friend():
tmp = self._messages.count() + i
if tmp >= 0:
self._messages.itemWidget(
self._messages.item(tmp)).update_transfer_state(TOX_FILE_TRANSFER_STATE['CANCELLED'],
0, -1)
def cancel_not_started_transfer(self, cancel_time):
self.get_curr_friend().delete_one_unsent_file(cancel_time)
self.update()
def pause_transfer(self, friend_number, file_number, by_friend=False):
"""
Pause transfer with specified data
"""
tr = self._file_transfers[(friend_number, file_number)]
tr.pause(by_friend)
t = TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] if by_friend else TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']
self.get_friend_by_number(friend_number).update_transfer_data(file_number, t)
def resume_transfer(self, friend_number, file_number, by_friend=False):
"""
Resume transfer with specified data
"""
self.get_friend_by_number(friend_number).update_transfer_data(file_number,
TOX_FILE_TRANSFER_STATE['RUNNING'])
tr = self._file_transfers[(friend_number, file_number)]
if by_friend:
tr.state = TOX_FILE_TRANSFER_STATE['RUNNING']
tr.signal()
else:
tr.send_control(TOX_FILE_CONTROL['RESUME'])
def accept_transfer(self, item, path, friend_number, file_number, size, inline=False, from_position=0):
"""
:param item: transfer item.
:param path: path for saving
:param friend_number: friend number
:param file_number: file number
:param size: file size
:param inline: is inline image
:param from_position: position for start
"""
path, file_name = os.path.split(path)
new_file_name, i = file_name, 1
if not from_position:
while os.path.isfile(path + '/' + new_file_name): # file with same name already exists
if '.' in file_name: # has extension
d = file_name.rindex('.')
else: # no extension
d = len(file_name)
new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:]
i += 1
path = os.path.join(path, new_file_name)
if not inline:
rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number, from_position)
else:
rt = ReceiveToBuffer(self._tox, friend_number, size, file_number)
rt.set_transfer_finished_handler(self.transfer_finished)
self._file_transfers[(friend_number, file_number)] = rt
self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME'])
if item is not None:
rt.set_state_changed_handler(item.update_transfer_state)
self.get_friend_by_number(friend_number).update_transfer_data(file_number,
TOX_FILE_TRANSFER_STATE['RUNNING'])
def send_screenshot(self, data):
"""
Send screenshot to current active friend
:param data: raw data - png
"""
self.send_inline(data, 'toxygen_inline.png')
self._messages.repaint()
def send_sticker(self, path):
with open(path, 'rb') as fl:
data = fl.read()
self.send_inline(data, 'sticker.png')
def send_inline(self, data, file_name, friend_number=None, is_resend=False):
friend_number = friend_number or self.get_active_number()
friend = self.get_friend_by_number(friend_number)
if friend.status is None and not is_resend:
m = UnsentFile(file_name, data, time.time())
friend.append_message(m)
self.update()
return
elif friend.status is None and is_resend:
raise RuntimeError()
st = SendFromBuffer(self._tox, friend.number, data, file_name)
st.set_transfer_finished_handler(self.transfer_finished)
self._file_transfers[(friend.number, st.get_file_number())] = st
tm = TransferMessage(MESSAGE_OWNER['ME'],
time.time(),
TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'],
len(data),
file_name,
friend.number,
st.get_file_number())
item = self.create_file_transfer_item(tm)
friend.append_message(tm)
st.set_state_changed_handler(item.update_transfer_state)
self._messages.scrollToBottom()
def send_file(self, path, number=None, is_resend=False, file_id=None):
"""
Send file to current active friend
:param path: file path
:param number: friend_number
:param is_resend: is 'offline' message
:param file_id: file id of transfer
"""
friend_number = self.get_active_number() if number is None else number
friend = self.get_friend_by_number(friend_number)
if friend.status is None and not is_resend:
m = UnsentFile(path, None, time.time())
friend.append_message(m)
self.update()
return
elif friend.status is None and is_resend:
print('Error in sending')
raise RuntimeError()
st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id)
st.set_transfer_finished_handler(self.transfer_finished)
self._file_transfers[(friend_number, st.get_file_number())] = st
tm = TransferMessage(MESSAGE_OWNER['ME'],
time.time(),
TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'],
os.path.getsize(path),
os.path.basename(path),
friend_number,
st.get_file_number())
if friend_number == self.get_active_number():
item = self.create_file_transfer_item(tm)
st.set_state_changed_handler(item.update_transfer_state)
self._messages.scrollToBottom()
self._contacts[friend_number].append_message(tm)
def incoming_chunk(self, friend_number, file_number, position, data):
"""
Incoming chunk
"""
self._file_transfers[(friend_number, file_number)].write_chunk(position, data)
def outgoing_chunk(self, friend_number, file_number, position, size):
"""
Outgoing chunk
"""
self._file_transfers[(friend_number, file_number)].send_chunk(position, size)
def transfer_finished(self, friend_number, file_number):
transfer = self._file_transfers[(friend_number, file_number)]
t = type(transfer)
if t is ReceiveAvatar:
self.get_friend_by_number(friend_number).load_avatar()
if friend_number == self.get_active_number() and self.is_active_a_friend():
self.set_active(None)
elif t is ReceiveToBuffer or (t is SendFromBuffer and Settings.get_instance()['allow_inline']): # inline image
print('inline')
inline = InlineImage(transfer.get_data())
i = self.get_friend_by_number(friend_number).update_transfer_data(file_number,
TOX_FILE_TRANSFER_STATE['FINISHED'],
inline)
if friend_number == self.get_active_number() and self.is_active_a_friend():
count = self._messages.count()
if count + i + 1 >= 0:
elem = QtWidgets.QListWidgetItem()
item = InlineImageItem(transfer.get_data(), self._messages.width(), elem)
elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
self._messages.insertItem(count + i + 1, elem)
self._messages.setItemWidget(elem, item)
self._messages.scrollToBottom()
elif t is not SendAvatar:
self.get_friend_by_number(friend_number).update_transfer_data(file_number,
TOX_FILE_TRANSFER_STATE['FINISHED'])
del self._file_transfers[(friend_number, file_number)]
del transfer
# -----------------------------------------------------------------------------------------------------------------
# Avatars support
# -----------------------------------------------------------------------------------------------------------------
def send_avatar(self, friend_number):
"""
:param friend_number: number of friend who should get new avatar
"""
avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
if not os.path.isfile(avatar_path): # reset image
avatar_path = None
sa = SendAvatar(avatar_path, self._tox, friend_number)
self._file_transfers[(friend_number, sa.get_file_number())] = sa
def incoming_avatar(self, friend_number, file_number, size):
"""
Friend changed avatar
:param friend_number: friend number
:param file_number: file number
:param size: size of avatar or 0 (default avatar)
"""
ra = ReceiveAvatar(self._tox, friend_number, size, file_number)
if ra.state != TOX_FILE_TRANSFER_STATE['CANCELLED']:
self._file_transfers[(friend_number, file_number)] = ra
ra.set_transfer_finished_handler(self.transfer_finished)
else:
self.get_friend_by_number(friend_number).load_avatar()
if self.get_active_number() == friend_number and self.is_active_a_friend():
self.set_active(None)
def reset_avatar(self): def reset_avatar(self):
super(Profile, self).reset_avatar() super(Profile, self).reset_avatar()
for friend in filter(lambda x: x.status is not None, self._contacts): for friend in filter(lambda x: x.status is not None, self._contacts):
@ -1210,92 +535,3 @@ class Profile(basecontact.BaseContact, Singleton):
super(Profile, self).set_avatar(data) super(Profile, self).set_avatar(data)
for friend in filter(lambda x: x.status is not None, self._contacts): for friend in filter(lambda x: x.status is not None, self._contacts):
self.send_avatar(friend.number) self.send_avatar(friend.number)
# -----------------------------------------------------------------------------------------------------------------
# AV support
# -----------------------------------------------------------------------------------------------------------------
def get_call(self):
return self._call
call = property(get_call)
def call_click(self, audio=True, video=False):
"""User clicked audio button in main window"""
num = self.get_active_number()
if not self.is_active_a_friend():
return
if num not in self._call and self.is_active_online(): # start call
if not Settings.get_instance().audio['enabled']:
return
self._call(num, audio, video)
self._screen.active_call()
if video:
text = QtWidgets.QApplication.translate("incoming_call", "Outgoing video call")
else:
text = QtWidgets.QApplication.translate("incoming_call", "Outgoing audio call")
self.get_curr_friend().append_message(InfoMessage(text, time.time()))
self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
self._messages.scrollToBottom()
elif num in self._call: # finish or cancel call if you call with active friend
self.stop_call(num, False)
def incoming_call(self, audio, video, friend_number):
"""
Incoming call from friend.
"""
if not Settings.get_instance().audio['enabled']:
return
friend = self.get_friend_by_number(friend_number)
if video:
text = QtWidgets.QApplication.translate("incoming_call", "Incoming video call")
else:
text = QtWidgets.QApplication.translate("incoming_call", "Incoming audio call")
friend.append_message(InfoMessage(text, time.time()))
self._incoming_calls.add(friend_number)
if friend_number == self.get_active_number():
self._screen.incoming_call()
self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
self._messages.scrollToBottom()
else:
friend.actions = True
self._call_widgets[friend_number] = avwidgets.IncomingCallWidget(friend_number, text, friend.name)
self._call_widgets[friend_number].set_pixmap(friend.get_pixmap())
self._call_widgets[friend_number].show()
def accept_call(self, friend_number, audio, video):
"""
Accept incoming call with audio or video
"""
self._call.accept_call(friend_number, audio, video)
self._screen.active_call()
if friend_number in self._incoming_calls:
self._incoming_calls.remove(friend_number)
del self._call_widgets[friend_number]
def stop_call(self, friend_number, by_friend):
"""
Stop call with friend
"""
if friend_number in self._incoming_calls:
self._incoming_calls.remove(friend_number)
text = QtWidgets.QApplication.translate("incoming_call", "Call declined")
else:
text = QtWidgets.QApplication.translate("incoming_call", "Call finished")
self._screen.call_finished()
is_video = self._call.is_video_call(friend_number)
self._call.finish_call(friend_number, by_friend) # finish or decline call
if hasattr(self, '_call_widget'):
self._call_widget[friend_number].close()
del self._call_widget[friend_number]
def destroy_window():
if is_video:
cv2.destroyWindow(str(friend_number))
threading.Timer(2.0, destroy_window).start()
friend = self.get_friend_by_number(friend_number)
friend.append_message(InfoMessage(text, time.time()))
if friend_number == self.get_active_number():
self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
self._messages.scrollToBottom()

View file

@ -17,22 +17,22 @@ MESSAGE_OWNER = {
'NOT_SENT': 2 'NOT_SENT': 2
} }
# TODO: unique message id and ngc support # TODO: unique message id and ngc support, db name as profile name
class History: class Database:
def __init__(self, name): def __init__(self, name, toxes):
self._name = name self._name = name
self._toxes = toxes
chdir(settings.ProfileManager.get_path()) chdir(settings.ProfileManager.get_path())
path = settings.ProfileManager.get_path() + self._name + '.hstr' path = settings.ProfileManager.get_path() + self._name + '.hstr'
if os.path.exists(path): if os.path.exists(path):
decr = ToxES.get_instance()
try: try:
with open(path, 'rb') as fin: with open(path, 'rb') as fin:
data = fin.read() data = fin.read()
if decr.is_data_encrypted(data): if toxes.is_data_encrypted(data):
data = decr.pass_decrypt(data) data = toxes.pass_decrypt(data)
with open(path, 'wb') as fout: with open(path, 'wb') as fout:
fout.write(data) fout.write(data)
except: except:
@ -45,12 +45,11 @@ class History:
db.close() db.close()
def save(self): def save(self):
encr = ToxES.get_instance() if self._toxes.has_password():
if encr.has_password():
path = settings.ProfileManager.get_path() + self._name + '.hstr' path = settings.ProfileManager.get_path() + self._name + '.hstr'
with open(path, 'rb') as fin: with open(path, 'rb') as fin:
data = fin.read() data = fin.read()
data = encr.pass_encrypt(bytes(data)) data = self._toxes.pass_encrypt(bytes(data))
with open(path, 'wb') as fout: with open(path, 'wb') as fout:
fout.write(data) fout.write(data)
@ -136,8 +135,7 @@ class History:
finally: finally:
db.close() db.close()
def delete_message(self, tox_id, time): def delete_message(self, tox_id, message_id):
start, end = str(time - 0.01), str(time + 0.01)
chdir(settings.ProfileManager.get_path()) chdir(settings.ProfileManager.get_path())
db = connect(self._name + '.hstr', timeout=TIMEOUT) db = connect(self._name + '.hstr', timeout=TIMEOUT)
try: try:
@ -165,7 +163,7 @@ class History:
db.close() db.close()
def messages_getter(self, tox_id): def messages_getter(self, tox_id):
return History.MessageGetter(self._name, tox_id) return Database.MessageGetter(self._name, tox_id)
class MessageGetter: class MessageGetter:

View file

@ -0,0 +1,311 @@
from file_transfers.file_transfers import *
from messenger.messages import *
import os
class FileTransfersHandler:
def __init__(self, tox, settings):
self._tox = tox
self._settings = settings
self._file_transfers = {}
# -----------------------------------------------------------------------------------------------------------------
# File transfers support
# -----------------------------------------------------------------------------------------------------------------
def incoming_file_transfer(self, friend_number, file_number, size, file_name):
"""
New transfer
:param friend_number: number of friend who sent file
:param file_number: file number
:param size: file size in bytes
:param file_name: file name without path
"""
friend = self.get_friend_by_number(friend_number)
auto = self._settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends']
inline = is_inline(file_name) and settings['allow_inline']
file_id = self._tox.file_get_file_id(friend_number, file_number)
accepted = True
if file_id in self._paused_file_transfers:
data = self._paused_file_transfers[file_id]
pos = data[-1] if os.path.exists(data[0]) else 0
if pos >= size:
self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL'])
return
self._tox.file_seek(friend_number, file_number, pos)
self.accept_transfer(None, data[0], friend_number, file_number, size, False, pos)
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
time.time(),
TOX_FILE_TRANSFER_STATE['RUNNING'],
size,
file_name,
friend_number,
file_number)
elif inline and size < 1024 * 1024:
self.accept_transfer(None, '', friend_number, file_number, size, True)
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
time.time(),
TOX_FILE_TRANSFER_STATE['RUNNING'],
size,
file_name,
friend_number,
file_number)
elif auto:
path = settings['auto_accept_path'] or curr_directory()
self.accept_transfer(None, path + '/' + file_name, friend_number, file_number, size)
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
time.time(),
TOX_FILE_TRANSFER_STATE['RUNNING'],
size,
file_name,
friend_number,
file_number)
else:
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
time.time(),
TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'],
size,
file_name,
friend_number,
file_number)
accepted = False
if friend_number == self.get_active_number() and self.is_active_a_friend():
item = self.create_file_transfer_item(tm)
if accepted:
self._file_transfers[(friend_number, file_number)].set_state_changed_handler(item.update_transfer_state)
self._messages.scrollToBottom()
else:
friend.actions = True
friend.append_message(tm)
def cancel_transfer(self, friend_number, file_number, already_cancelled=False):
"""
Stop transfer
:param friend_number: number of friend
:param file_number: file number
:param already_cancelled: was cancelled by friend
"""
i = self.get_friend_by_number(friend_number).update_transfer_data(file_number,
TOX_FILE_TRANSFER_STATE['CANCELLED'])
if (friend_number, file_number) in self._file_transfers:
tr = self._file_transfers[(friend_number, file_number)]
if not already_cancelled:
tr.cancel()
else:
tr.cancelled()
if (friend_number, file_number) in self._file_transfers:
del tr
del self._file_transfers[(friend_number, file_number)]
else:
if not already_cancelled:
self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL'])
if friend_number == self.get_active_number() and self.is_active_a_friend():
tmp = self._messages.count() + i
if tmp >= 0:
self._messages.itemWidget(
self._messages.item(tmp)).update_transfer_state(TOX_FILE_TRANSFER_STATE['CANCELLED'],
0, -1)
def cancel_not_started_transfer(self, cancel_time):
self.get_curr_friend().delete_one_unsent_file(cancel_time)
self.update()
def pause_transfer(self, friend_number, file_number, by_friend=False):
"""
Pause transfer with specified data
"""
tr = self._file_transfers[(friend_number, file_number)]
tr.pause(by_friend)
t = TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] if by_friend else TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']
self.get_friend_by_number(friend_number).update_transfer_data(file_number, t)
def resume_transfer(self, friend_number, file_number, by_friend=False):
"""
Resume transfer with specified data
"""
self.get_friend_by_number(friend_number).update_transfer_data(file_number,
TOX_FILE_TRANSFER_STATE['RUNNING'])
tr = self._file_transfers[(friend_number, file_number)]
if by_friend:
tr.state = TOX_FILE_TRANSFER_STATE['RUNNING']
tr.signal()
else:
tr.send_control(TOX_FILE_CONTROL['RESUME'])
def accept_transfer(self, item, path, friend_number, file_number, size, inline=False, from_position=0):
"""
:param item: transfer item.
:param path: path for saving
:param friend_number: friend number
:param file_number: file number
:param size: file size
:param inline: is inline image
:param from_position: position for start
"""
path, file_name = os.path.split(path)
new_file_name, i = file_name, 1
if not from_position:
while os.path.isfile(path + '/' + new_file_name): # file with same name already exists
if '.' in file_name: # has extension
d = file_name.rindex('.')
else: # no extension
d = len(file_name)
new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:]
i += 1
path = os.path.join(path, new_file_name)
if not inline:
rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number, from_position)
else:
rt = ReceiveToBuffer(self._tox, friend_number, size, file_number)
rt.set_transfer_finished_handler(self.transfer_finished)
self._file_transfers[(friend_number, file_number)] = rt
self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME'])
if item is not None:
rt.set_state_changed_handler(item.update_transfer_state)
self.get_friend_by_number(friend_number).update_transfer_data(file_number,
TOX_FILE_TRANSFER_STATE['RUNNING'])
def send_screenshot(self, data):
"""
Send screenshot to current active friend
:param data: raw data - png
"""
self.send_inline(data, 'toxygen_inline.png')
self._messages.repaint()
def send_sticker(self, path):
with open(path, 'rb') as fl:
data = fl.read()
self.send_inline(data, 'sticker.png')
def send_inline(self, data, file_name, friend_number=None, is_resend=False):
friend_number = friend_number or self.get_active_number()
friend = self.get_friend_by_number(friend_number)
if friend.status is None and not is_resend:
m = UnsentFile(file_name, data, time.time())
friend.append_message(m)
self.update()
return
elif friend.status is None and is_resend:
raise RuntimeError()
st = SendFromBuffer(self._tox, friend.number, data, file_name)
st.set_transfer_finished_handler(self.transfer_finished)
self._file_transfers[(friend.number, st.get_file_number())] = st
tm = TransferMessage(MESSAGE_OWNER['ME'],
time.time(),
TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'],
len(data),
file_name,
friend.number,
st.get_file_number())
item = self.create_file_transfer_item(tm)
friend.append_message(tm)
st.set_state_changed_handler(item.update_transfer_state)
self._messages.scrollToBottom()
def send_file(self, path, number=None, is_resend=False, file_id=None):
"""
Send file to current active friend
:param path: file path
:param number: friend_number
:param is_resend: is 'offline' message
:param file_id: file id of transfer
"""
friend_number = self.get_active_number() if number is None else number
friend = self.get_friend_by_number(friend_number)
if friend.status is None and not is_resend:
m = UnsentFile(path, None, time.time())
friend.append_message(m)
self.update()
return
elif friend.status is None and is_resend:
print('Error in sending')
raise RuntimeError()
st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id)
st.set_transfer_finished_handler(self.transfer_finished)
self._file_transfers[(friend_number, st.get_file_number())] = st
tm = TransferMessage(MESSAGE_OWNER['ME'],
time.time(),
TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'],
os.path.getsize(path),
os.path.basename(path),
friend_number,
st.get_file_number())
if friend_number == self.get_active_number():
item = self.create_file_transfer_item(tm)
st.set_state_changed_handler(item.update_transfer_state)
self._messages.scrollToBottom()
self._contacts[friend_number].append_message(tm)
def incoming_chunk(self, friend_number, file_number, position, data):
"""
Incoming chunk
"""
self._file_transfers[(friend_number, file_number)].write_chunk(position, data)
def outgoing_chunk(self, friend_number, file_number, position, size):
"""
Outgoing chunk
"""
self._file_transfers[(friend_number, file_number)].send_chunk(position, size)
def transfer_finished(self, friend_number, file_number):
transfer = self._file_transfers[(friend_number, file_number)]
t = type(transfer)
if t is ReceiveAvatar:
self.get_friend_by_number(friend_number).load_avatar()
if friend_number == self.get_active_number() and self.is_active_a_friend():
self.set_active(None)
elif t is ReceiveToBuffer or (t is SendFromBuffer and Settings.get_instance()['allow_inline']): # inline image
print('inline')
inline = InlineImage(transfer.get_data())
i = self.get_friend_by_number(friend_number).update_transfer_data(file_number,
TOX_FILE_TRANSFER_STATE['FINISHED'],
inline)
if friend_number == self.get_active_number() and self.is_active_a_friend():
count = self._messages.count()
if count + i + 1 >= 0:
elem = QtWidgets.QListWidgetItem()
item = InlineImageItem(transfer.get_data(), self._messages.width(), elem)
elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
self._messages.insertItem(count + i + 1, elem)
self._messages.setItemWidget(elem, item)
self._messages.scrollToBottom()
elif t is not SendAvatar:
self.get_friend_by_number(friend_number).update_transfer_data(file_number,
TOX_FILE_TRANSFER_STATE['FINISHED'])
del self._file_transfers[(friend_number, file_number)]
del transfer
# -----------------------------------------------------------------------------------------------------------------
# Avatars support
# -----------------------------------------------------------------------------------------------------------------
def send_avatar(self, friend_number):
"""
:param friend_number: number of friend who should get new avatar
"""
avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
if not os.path.isfile(avatar_path): # reset image
avatar_path = None
sa = SendAvatar(avatar_path, self._tox, friend_number)
self._file_transfers[(friend_number, sa.get_file_number())] = sa
def incoming_avatar(self, friend_number, file_number, size):
"""
Friend changed avatar
:param friend_number: friend number
:param file_number: file number
:param size: size of avatar or 0 (default avatar)
"""
ra = ReceiveAvatar(self._tox, friend_number, size, file_number)
if ra.state != TOX_FILE_TRANSFER_STATE['CANCELLED']:
self._file_transfers[(friend_number, file_number)] = ra
ra.set_transfer_finished_handler(self.transfer_finished)
else:
self.get_friend_by_number(friend_number).load_avatar()
if self.get_active_number() == friend_number and self.is_active_a_friend():
self.set_active(None)

View file

@ -1,12 +1,10 @@
import sys import sys
import app
from user_data.settings import * from user_data.settings import *
from util import curr_directory, program_version, remove from util.util import curr_directory, program_version, remove
import argparse import argparse
def clean(): def clean():
"""Removes all windows libs from libs folder""" """Removes all windows libs from libs folder"""
d = curr_directory() + '/libs/' d = curr_directory() + '/libs/'
@ -24,7 +22,7 @@ def main():
parser.add_argument('--reset') parser.add_argument('--reset')
args = parser.parse_args() args = parser.parse_args()
if not len(args): if not len(args):
toxygen = Toxygen() toxygen = app.App()
else: # started with argument(s) else: # started with argument(s)
arg = sys.argv[1] arg = sys.argv[1]
if arg == '--version': if arg == '--version':
@ -40,7 +38,7 @@ def main():
reset() reset()
return return
else: else:
toxygen = Toxygen(arg) toxygen = app.App(arg)
toxygen.main() toxygen.main()

View file

@ -1,5 +1,5 @@
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from util import curr_directory from util.util import curr_directory
import wave import wave
import pyaudio import pyaudio

View file

@ -1,4 +1,4 @@
import util from util import util
import json import json
import os import os
from collections import OrderedDict from collections import OrderedDict

View file

@ -1,3 +1,11 @@
from PyQt5 import QtCore
from communication.callbacks import init_callbacks
from bootstrap.bootstrap import *
import threading
import queue
from util import util
class InitThread(QtCore.QThread): class InitThread(QtCore.QThread):
def __init__(self, tox, ms, tray): def __init__(self, tox, ms, tray):
@ -70,8 +78,8 @@ class FileTransfersThread(threading.Thread):
self._continue = True self._continue = True
super().__init__() super().__init__()
def execute(self, function, *args, **kwargs): def execute(self, func, *args, **kwargs):
self._queue.put((function, args, kwargs)) self._queue.put((func, args, kwargs))
def stop(self): def stop(self):
self._continue = False self._continue = False
@ -79,12 +87,12 @@ class FileTransfersThread(threading.Thread):
def run(self): def run(self):
while self._continue: while self._continue:
try: try:
function, args, kwargs = self._queue.get(timeout=self._timeout) func, args, kwargs = self._queue.get(timeout=self._timeout)
function(*args, **kwargs) func(*args, **kwargs)
except queue.Empty: except queue.Empty:
pass pass
except queue.Full: except queue.Full:
util.log('Queue is Full in _thread') util.log('Queue is full in _thread')
except Exception as ex: except Exception as ex:
util.log('Exception in _thread: ' + str(ex)) util.log('Exception in _thread: ' + str(ex))
@ -101,6 +109,8 @@ def stop():
_thread.join() _thread.join()
def execute(func, *args, **kwargs):
_thread.execute(func, *args, **kwargs)
class InvokeEvent(QtCore.QEvent): class InvokeEvent(QtCore.QEvent):
@ -125,3 +135,4 @@ _invoker = Invoker()
def invoke_in_main_thread(fn, *args, **kwargs): def invoke_in_main_thread(fn, *args, **kwargs):
QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs)) QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))

View file

@ -1,83 +1,95 @@
self.tray = QtWidgets.QSystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) from PyQt5 import QtWidgets, QtGui, QtCore
self.tray.setObjectName('tray') from util.ui import tr
from util.util import curr_directory
class Menu(QtWidgets.QMenu):
def newStatus(self, status): class Menu(QtWidgets.QMenu):
if not Settings.get_instance().locked:
profile.Profile.get_instance().set_status(status)
self.aboutToShowHandler()
self.hide()
def aboutToShowHandler(self): def __init__(self, settings, profile, *args):
status = profile.Profile.get_instance().status super().__init__(*args)
act = self.act self._settings = settings
if status is None or Settings.get_instance().locked: self._profile = profile
self.actions()[1].setVisible(False)
else:
self.actions()[1].setVisible(True)
act.actions()[0].setChecked(False)
act.actions()[1].setChecked(False)
act.actions()[2].setChecked(False)
act.actions()[status].setChecked(True)
self.actions()[2].setVisible(not Settings.get_instance().locked)
def languageChange(self, *args, **kwargs): def newStatus(self, status):
self.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) if not self._settings.locked:
self.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Set status')) self._profile.Profile.get_instance().set_status(status)
self.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Exit')) self.aboutToShowHandler()
self.act.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Online')) self.hide()
self.act.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Away'))
self.act.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Busy'))
m = Menu() def aboutToShowHandler(self):
show = m.addAction(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) status = self._profile.status
sub = m.addMenu(QtWidgets.QApplication.translate('tray', 'Set status')) act = self.act
onl = sub.addAction(QtWidgets.QApplication.translate('tray', 'Online')) if status is None or self._ettings.get_instance().locked:
away = sub.addAction(QtWidgets.QApplication.translate('tray', 'Away')) self.actions()[1].setVisible(False)
busy = sub.addAction(QtWidgets.QApplication.translate('tray', 'Busy')) else:
onl.setCheckable(True) self.actions()[1].setVisible(True)
away.setCheckable(True) act.actions()[0].setChecked(False)
busy.setCheckable(True) act.actions()[1].setChecked(False)
m.act = sub act.actions()[2].setChecked(False)
exit = m.addAction(QtWidgets.QApplication.translate('tray', 'Exit')) act.actions()[status].setChecked(True)
self.actions()[2].setVisible(not self._settings.locked)
def show_window(): def languageChange(self, *args, **kwargs):
s = Settings.get_instance() self.actions()[0].setText(tr('Open Toxygen'))
self.actions()[1].setText(tr('Set status'))
self.actions()[2].setText(tr('Exit'))
self.act.actions()[0].setText(tr('Online'))
self.act.actions()[1].setText(tr('Away'))
self.act.actions()[2].setText(tr('Busy'))
def show():
if not self.ms.isActiveWindow(): def init_tray(profile, settings, main_screen):
self.ms.setWindowState(self.ms.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) tray = QtWidgets.QSystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png'))
self.ms.activateWindow() tray.setObjectName('tray')
self.ms.show()
if not s.locked: m = Menu(settings, profile)
show = m.addAction(tr('Open Toxygen'))
sub = m.addMenu(tr('Set status'))
online = sub.addAction(tr('Online'))
away = sub.addAction(tr('Away'))
busy = sub.addAction(tr('Busy'))
online.setCheckable(True)
away.setCheckable(True)
busy.setCheckable(True)
m.act = sub
exit = m.addAction(tr('Exit'))
def show_window():
def show():
if not main_screen.isActiveWindow():
main_screen.setWindowState(main_screen.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
main_screen.activateWindow()
main_screen.show()
if not settings.locked:
show()
else:
def correct_pass():
show() show()
else: settings.locked = False
def correct_pass(): settings.unlockScreen = False
show() if not settings.unlockScreen:
s.locked = False settings.unlockScreen = True
s.unlockScreen = False self.p = UnlockAppScreen(toxes.ToxES.get_instance(), correct_pass)
if not s.unlockScreen: self.p.show()
s.unlockScreen = True
self.p = UnlockAppScreen(toxes.ToxES.get_instance(), correct_pass)
self.p.show()
def tray_activated(reason): def tray_activated(reason):
if reason == QtWidgets.QSystemTrayIcon.DoubleClick: if reason == QtWidgets.QSystemTrayIcon.DoubleClick:
show_window() show_window()
def close_app(): def close_app():
if not Settings.get_instance().locked: if not settings.locked:
settings.closing = True settings.closing = True
self.ms.close() main_screen.close()
show.triggered.connect(show_window) show.triggered.connect(show_window)
exit.triggered.connect(close_app) exit.triggered.connect(close_app)
m.aboutToShow.connect(lambda: m.aboutToShowHandler()) m.aboutToShow.connect(lambda: m.aboutToShowHandler())
onl.triggered.connect(lambda: m.newStatus(0)) online.triggered.connect(lambda: m.newStatus(0))
away.triggered.connect(lambda: m.newStatus(1)) away.triggered.connect(lambda: m.newStatus(1))
busy.triggered.connect(lambda: m.newStatus(2)) busy.triggered.connect(lambda: m.newStatus(2))
self.tray.setContextMenu(m) tray.setContextMenu(m)
self.tray.show() tray.show()
self.tray.activated.connect(tray_activated) tray.activated.connect(tray_activated)
return tray

View file

@ -1,7 +1,7 @@
from wrapper.toxcore_enums_and_consts import * from wrapper.toxcore_enums_and_consts import *
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
from contacts import profile from contacts import profile
from file_tansfers.file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR from file_transfers.file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR
from util import curr_directory, convert_time, curr_time from util import curr_directory, convert_time, curr_time
from ui.widgets import DataLabel, create_menu from ui.widgets import DataLabel, create_menu
import html as h import html as h

View file

@ -1,4 +1,4 @@
class ProfileManager(): class ProfileManager:
""" """
Class with methods for search, load and save profiles Class with methods for search, load and save profiles
""" """

8
toxygen/util/ui.py Normal file
View file

@ -0,0 +1,8 @@
import PyQt5
def tr(s):
return PyQt5.QtWidgets.QApplication.translate('Toxygen', s)
# TODO: move all dialogs here

View file

@ -29,9 +29,8 @@ def log(data):
pass pass
@cached def curr_directory(current_file=None):
def curr_directory(): return os.path.dirname(os.path.realpath(current_file or __file__))
return os.path.dirname(os.path.realpath(__file__))
def curr_time(): def curr_time():