From 65167de1fed4e547d83a9439d9d8d15975aa10e8 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 18 Jul 2017 21:36:14 +0300 Subject: [PATCH] group notifications and bug fixes --- toxygen/callbacks.py | 36 ++++++++++++++++++++++++++++-------- toxygen/friend.py | 7 +++++++ toxygen/group_chat.py | 13 +++++++++++-- toxygen/mainscreen.py | 20 +++++++++++++++----- toxygen/menu.py | 14 ++++++++++---- toxygen/profile.py | 31 ++++++++++++++++++++++--------- toxygen/settings.py | 3 ++- toxygen/tox.py | 18 ++++-------------- 8 files changed, 99 insertions(+), 43 deletions(-) diff --git a/toxygen/callbacks.py b/toxygen/callbacks.py index 6cc88c0..94e954e 100644 --- a/toxygen/callbacks.py +++ b/toxygen/callbacks.py @@ -384,14 +384,34 @@ def group_invite(tox, friend_number, gc_type, data, length, user_data): bytes(data[:length])) -def group_message(tox, group_number, peer_number, message, length, user_data): - invoke_in_main_thread(Profile.get_instance().new_gc_message, group_number, - peer_number, TOX_MESSAGE_TYPE['NORMAL'], str(message, 'utf-8')) +def show_gc_notification(window, tray, message, group_number): + profile = Profile.get_instance() + settings = Settings.get_instance() + chat = profile.get_group_by_number(group_number) + if not window.isActiveWindow() and (profile.name in message or settings['group_notifications']): + if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: + invoke_in_main_thread(tray_notification, chat.name, message, tray, window) + if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: + sound_notification(SOUND_NOTIFICATION['MESSAGE']) + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) -def group_action(tox, group_number, peer_number, message, length, user_data): - invoke_in_main_thread(Profile.get_instance().new_gc_message, group_number, - peer_number, TOX_MESSAGE_TYPE['ACTION'], str(message, 'utf-8')) +def group_message(window, tray): + def wrapped(tox, group_number, peer_number, message, length, user_data): + message = str(message[:length], 'utf-8') + invoke_in_main_thread(Profile.get_instance().new_gc_message, group_number, + peer_number, TOX_MESSAGE_TYPE['NORMAL'], message) + show_gc_notification(window, tray, message, group_number) + return wrapped + + +def group_action(window, tray): + def wrapped(tox, group_number, peer_number, message, length, user_data): + message = str(message[:length], 'utf-8') + invoke_in_main_thread(Profile.get_instance().new_gc_message, group_number, + peer_number, TOX_MESSAGE_TYPE['ACTION'], message) + show_gc_notification(window, tray, message, group_number) + return wrapped def group_title(tox, group_number, peer_number, title, length, user_data): @@ -440,7 +460,7 @@ def init_callbacks(tox, window, tray): tox.callback_friend_lossy_packet(lossy_packet, 0) tox.callback_group_invite(group_invite) - tox.callback_group_message(group_message) - tox.callback_group_action(group_action) + tox.callback_group_message(group_message(window, tray)) + tox.callback_group_action(group_action(window, tray)) tox.callback_group_title(group_title) tox.callback_group_namelist_change(group_namelist_change) diff --git a/toxygen/friend.py b/toxygen/friend.py index f560e5c..d912708 100644 --- a/toxygen/friend.py +++ b/toxygen/friend.py @@ -66,3 +66,10 @@ class Friend(contact.Contact): if self._receipts: self._receipts -= 1 self.mark_as_sent() + + # ----------------------------------------------------------------------------------------------------------------- + # Full status + # ----------------------------------------------------------------------------------------------------------------- + + def get_full_status(self): + return self._status_message diff --git a/toxygen/group_chat.py b/toxygen/group_chat.py index fe009b7..9b505ce 100644 --- a/toxygen/group_chat.py +++ b/toxygen/group_chat.py @@ -1,7 +1,7 @@ import contact import util from PyQt5 import QtGui, QtCore -import toxcore_enums_and_consts as cnst +import toxcore_enums_and_consts as constants class GroupChat(contact.Contact): @@ -9,7 +9,7 @@ class GroupChat(contact.Contact): def __init__(self, name, status_message, widget, tox, group_number): super().__init__(None, group_number, name, status_message, widget, None) self._tox = tox - self._status = cnst.TOX_USER_STATUS['NONE'] + self.set_status(constants.TOX_USER_STATUS['NONE']) def set_name(self, name): self._tox.group_set_title(self._number, name) @@ -31,3 +31,12 @@ class GroupChat(contact.Contact): def remove_invalid_unsent_files(self): pass + + def get_full_status(self): + peers_count = self._tox.group_number_peers(self._number) + names = [] + for i in range(peers_count): + name = self._tox.group_peername(self._number, i) + names.append(name) + names = sorted(names, key=lambda n: n.lower()) + return '\n'.join(names) diff --git a/toxygen/mainscreen.py b/toxygen/mainscreen.py index ac753d5..c76f19b 100644 --- a/toxygen/mainscreen.py +++ b/toxygen/mainscreen.py @@ -5,7 +5,6 @@ from widgets import MultilineEdit, ComboBox import plugin_support from mainscreen_widgets import * import settings -import platform import toxes @@ -519,7 +518,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): def send_file(self): self.menu.hide() - if self.profile.active_friend + 1: + if self.profile.active_friend + 1and self.profile.is_active_a_friend(): choose = QtWidgets.QApplication.translate("MainWindow", 'Choose file') name = QtWidgets.QFileDialog.getOpenFileName(self, choose, options=QtWidgets.QFileDialog.DontUseNativeDialog) if name[0]: @@ -527,7 +526,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): def send_screenshot(self, hide=False): self.menu.hide() - if self.profile.active_friend + 1: + if self.profile.active_friend + 1 and self.profile.is_active_a_friend(): self.sw = ScreenShotWindow(self) self.sw.show() if hide: @@ -545,7 +544,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): def send_sticker(self): self.menu.hide() - if self.profile.active_friend + 1: + if self.profile.active_friend + 1 and self.profile.is_active_a_friend(): self.sticker = StickerWindow(self) self.sticker.setGeometry(QtCore.QRect(self.x() if Settings.get_instance()['mirror_mode'] else 270 + self.x(), self.y() + self.height() - 200, @@ -593,6 +592,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): is_friend = type(friend) is Friend if is_friend: set_alias_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Set alias')) + set_alias_item.triggered.connect(lambda: self.set_alias(num)) history_menu = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Chat history')) clear_history_item = history_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Clear history')) @@ -610,13 +610,20 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): block_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Block friend')) notes_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Notes')) + chats = self.profile.get_group_chats() + if len(chats) and self.profile.is_active_online(): + invite_menu = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Invite to group chat')) + for i in range(len(chats)): + name, number = chats[i] + item = invite_menu.addAction(name) + item.triggered.connect(lambda: self.invite_friend_to_gc(num, number)) + plugins_loader = plugin_support.PluginLoader.get_instance() if plugins_loader is not None: submenu = plugins_loader.get_menu(self.listMenu, num) if len(submenu): plug = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Plugins')) plug.addActions(submenu) - set_alias_item.triggered.connect(lambda: self.set_alias(num)) copy_key_item.triggered.connect(lambda: self.copy_friend_key(num)) remove_item.triggered.connect(lambda: self.remove_friend(num)) block_item.triggered.connect(lambda: self.block_friend(num)) @@ -705,6 +712,9 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): settings['auto_accept_from_friends'].remove(tox_id) settings.save() + def invite_friend_to_gc(self, friend_number, group_number): + self.profile.invite_friend(friend_number, group_number) + # ----------------------------------------------------------------------------------------------------------------- # Functions which called when user click somewhere else # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/menu.py b/toxygen/menu.py index 6724b75..37f7bb6 100644 --- a/toxygen/menu.py +++ b/toxygen/menu.py @@ -508,15 +508,17 @@ class NotificationsSettings(CenteredWidget): def initUI(self): self.setObjectName("notificationsForm") - self.resize(350, 180) - self.setMinimumSize(QtCore.QSize(350, 180)) - self.setMaximumSize(QtCore.QSize(350, 180)) + self.resize(350, 210) + self.setMinimumSize(QtCore.QSize(350, 210)) + self.setMaximumSize(QtCore.QSize(350, 210)) self.enableNotifications = QtWidgets.QCheckBox(self) self.enableNotifications.setGeometry(QtCore.QRect(10, 20, 340, 18)) self.callsSound = QtWidgets.QCheckBox(self) - self.callsSound.setGeometry(QtCore.QRect(10, 120, 340, 18)) + self.callsSound.setGeometry(QtCore.QRect(10, 170, 340, 18)) self.soundNotifications = QtWidgets.QCheckBox(self) self.soundNotifications.setGeometry(QtCore.QRect(10, 70, 340, 18)) + self.groupNotifications = QtWidgets.QCheckBox(self) + self.groupNotifications.setGeometry(QtCore.QRect(10, 120, 340, 18)) font = QtGui.QFont() s = Settings.get_instance() font.setFamily(s['font']) @@ -524,8 +526,10 @@ class NotificationsSettings(CenteredWidget): self.callsSound.setFont(font) self.soundNotifications.setFont(font) self.enableNotifications.setFont(font) + self.groupNotifications.setFont(font) self.enableNotifications.setChecked(s['notifications']) self.soundNotifications.setChecked(s['sound_notifications']) + self.groupNotifications.setChecked(s['group_notifications']) self.callsSound.setChecked(s['calls_sound']) self.retranslateUi() QtCore.QMetaObject.connectSlotsByName(self) @@ -533,6 +537,7 @@ class NotificationsSettings(CenteredWidget): def retranslateUi(self): self.setWindowTitle(QtWidgets.QApplication.translate("notificationsForm", "Notification settings")) self.enableNotifications.setText(QtWidgets.QApplication.translate("notificationsForm", "Enable notifications")) + self.groupNotifications.setText(QtWidgets.QApplication.translate("notificationsForm", "Notify about all messages in groups")) self.callsSound.setText(QtWidgets.QApplication.translate("notificationsForm", "Enable call\'s sound")) self.soundNotifications.setText(QtWidgets.QApplication.translate("notificationsForm", "Enable sound notifications")) @@ -540,6 +545,7 @@ class NotificationsSettings(CenteredWidget): settings = Settings.get_instance() settings['notifications'] = self.enableNotifications.isChecked() settings['sound_notifications'] = self.soundNotifications.isChecked() + settings['group_notifications'] = self.groupNotifications.isChecked() settings['calls_sound'] = self.callsSound.isChecked() settings.save() diff --git a/toxygen/profile.py b/toxygen/profile.py index 14e7ca0..63cf09b 100644 --- a/toxygen/profile.py +++ b/toxygen/profile.py @@ -205,6 +205,7 @@ class Profile(basecontact.BaseContact, Singleton): 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() @@ -274,6 +275,7 @@ class Profile(basecontact.BaseContact, Singleton): 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: @@ -354,7 +356,7 @@ class Profile(basecontact.BaseContact, Singleton): elif data[1] == friend_number and not data[2]: self.send_file(data[0], friend_number, True, key) del self._paused_file_transfers[key] - if friend_number == self.get_active_number(): + if friend_number == self.get_active_number() and self.is_active_a_friend(): self.update() except Exception as ex: print('Exception in file sending: ' + str(ex)) @@ -396,7 +398,7 @@ class Profile(basecontact.BaseContact, Singleton): """ Display incoming typing notification """ - if friend_number == self.get_active_number(): + if friend_number == self.get_active_number() and self.is_active_a_friend(): self._screen.typing.setVisible(typing) # ----------------------------------------------------------------------------------------------------------------- @@ -452,7 +454,7 @@ class Profile(basecontact.BaseContact, Singleton): :param message_type: message type - plain text or action message (/me) :param message: text of message """ - if friend_num == self.get_active_number(): # add message to list + if friend_num == self.get_active_number()and self.is_active_a_friend(): # add message to list t = time.time() self.create_message_item(message, t, MESSAGE_OWNER['FRIEND'], message_type) self._messages.scrollToBottom() @@ -712,7 +714,7 @@ class Profile(basecontact.BaseContact, Singleton): except: pass settings.save() - if num == self.get_active_number(): + if num == self.get_active_number() and self.is_active_a_friend(): self.update() def friend_public_key(self, num): @@ -956,7 +958,7 @@ class Profile(basecontact.BaseContact, Singleton): friend_number, file_number) accepted = False - if friend_number == self.get_active_number(): + 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) @@ -987,7 +989,7 @@ class Profile(basecontact.BaseContact, Singleton): else: if not already_cancelled: self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) - if friend_number == self.get_active_number(): + 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(TOX_FILE_TRANSFER_STATE['CANCELLED'], @@ -1141,7 +1143,7 @@ class Profile(basecontact.BaseContact, Singleton): t = type(transfer) if t is ReceiveAvatar: self.get_friend_by_number(friend_number).load_avatar() - if friend_number == self.get_active_number(): + 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') @@ -1149,7 +1151,7 @@ class Profile(basecontact.BaseContact, Singleton): 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(): + 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() @@ -1191,7 +1193,7 @@ class Profile(basecontact.BaseContact, Singleton): 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: + if self.get_active_number() == friend_number and self.is_active_a_friend(): self.set_active(None) def reset_avatar(self): @@ -1216,6 +1218,8 @@ class Profile(basecontact.BaseContact, Singleton): 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 @@ -1389,6 +1393,15 @@ class Profile(basecontact.BaseContact, Singleton): self._tox.group_set_title(gc.number, text) self.new_gc_title(gc.number, text) + def get_group_chats(self): + chats = filter(lambda x: type(x) is GroupChat, self._contacts) + chats = map(lambda c: (c.name, c.number), chats) + return list(chats) + + def invite_friend(self, friend_num, group_number): + friend = self._contacts[friend_num] + self._tox.invite_friend(friend.number, group_number) + def tox_factory(data=None, settings=None): """ diff --git a/toxygen/settings.py b/toxygen/settings.py index b0d0502..21c8bec 100644 --- a/toxygen/settings.py +++ b/toxygen/settings.py @@ -144,7 +144,8 @@ class Settings(dict, Singleton): 'show_welcome_screen': True, 'close_to_tray': False, 'font': 'Times New Roman', - 'update': 1 + 'update': 1, + 'group_notifications': True } @staticmethod diff --git a/toxygen/tox.py b/toxygen/tox.py index c7760bc..ef4e44c 100644 --- a/toxygen/tox.py +++ b/toxygen/tox.py @@ -1565,24 +1565,14 @@ class Tox: result = Tox.libtoxcore.tox_group_number_peers(self._tox_pointer, c_int(groupnumber), None) return result - def group_get_names(self, groupnumber): - peers_count = self.group_number_peers(groupnumber) - arr = (c_char_p * peers_count)() - for i in range(peers_count): - arr[i] = create_string_buffer(TOX_MAX_NAME_LENGTH) - result = Tox.libtoxcore.tox_group_get_names(self._tox_pointer, c_int(groupnumber), - arr, None, c_uint16(peers_count), None) - arr = map(lambda x: str(x, 'utf-8'), arr) - return list(arr) - def add_av_groupchat(self): - result = self.AV.libtoxav.tox_add_av_groupchat(self._tox_pointer, None, None, None) + result = self.AV.libtoxav.toxav_add_av_groupchat(self._tox_pointer, None, None) return result def join_av_groupchat(self, friendnumber, data): - result = self.AV.libtoxav.tox_join_av_groupchat(self._tox_pointer, c_int(friendnumber), - c_char_p(data), c_uint16(len(data)), - None, None, None) + result = self.AV.libtoxav.toxav_join_av_groupchat(self._tox_pointer, c_int32(friendnumber), + c_char_p(data), c_uint16(len(data)), + None, None) return result def callback_group_invite(self, callback, user_data=None):