diff --git a/toxygen/callbacks.py b/toxygen/callbacks.py index 7028f97..6cc88c0 100644 --- a/toxygen/callbacks.py +++ b/toxygen/callbacks.py @@ -380,17 +380,18 @@ def video_receive_frame(toxav, friend_number, width, height, y, u, v, ystride, u def group_invite(tox, friend_number, gc_type, data, length, user_data): - invoke_in_main_thread(Profile.get_instance().group_invite, friend_number, gc_type, data[:length]) + invoke_in_main_thread(Profile.get_instance().group_invite, friend_number, gc_type, + 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'], message[:length]) + peer_number, TOX_MESSAGE_TYPE['NORMAL'], str(message, 'utf-8')) 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'], message[:length]) + peer_number, TOX_MESSAGE_TYPE['ACTION'], str(message, 'utf-8')) def group_title(tox, group_number, peer_number, title, length, user_data): diff --git a/toxygen/group_chat.py b/toxygen/group_chat.py index 563d494..fe009b7 100644 --- a/toxygen/group_chat.py +++ b/toxygen/group_chat.py @@ -1,4 +1,7 @@ import contact +import util +from PyQt5 import QtGui, QtCore +import toxcore_enums_and_consts as cnst class GroupChat(contact.Contact): @@ -6,6 +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'] def set_name(self, name): self._tox.group_set_title(self._number, name) @@ -16,3 +20,14 @@ class GroupChat(contact.Contact): def new_title(self, title): super().set_name(title) + + def load_avatar(self): + path = util.curr_directory() + '/images/group.png' + width = self._widget.avatar_label.width() + pixmap = QtGui.QPixmap(path) + self._widget.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation)) + self._widget.avatar_label.repaint() + + def remove_invalid_unsent_files(self): + pass diff --git a/toxygen/images/group.png b/toxygen/images/group.png new file mode 100644 index 0000000..22adab0 Binary files /dev/null and b/toxygen/images/group.png differ diff --git a/toxygen/mainscreen.py b/toxygen/mainscreen.py index 27b884f..3643b39 100644 --- a/toxygen/mainscreen.py +++ b/toxygen/mainscreen.py @@ -41,6 +41,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): self.menuAbout.setObjectName("menuAbout") self.actionAdd_friend = QtWidgets.QAction(window) + self.actionAdd_gc = QtWidgets.QAction(window) self.actionAdd_friend.setObjectName("actionAdd_friend") self.actionprofilesettings = QtWidgets.QAction(window) self.actionprofilesettings.setObjectName("actionprofilesettings") @@ -64,6 +65,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): self.reloadPlugins = QtWidgets.QAction(window) self.lockApp = QtWidgets.QAction(window) self.menuProfile.addAction(self.actionAdd_friend) + self.menuProfile.addAction(self.actionAdd_gc) self.menuProfile.addAction(self.actionSettings) self.menuProfile.addAction(self.lockApp) self.menuSettings.addAction(self.actionPrivacy_settings) @@ -86,6 +88,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): self.actionAbout_program.triggered.connect(self.about_program) self.actionNetwork.triggered.connect(self.network_settings) self.actionAdd_friend.triggered.connect(self.add_contact) + self.actionAdd_gc.triggered.connect(self.create_gc) self.actionSettings.triggered.connect(self.profile_settings) self.actionPrivacy_settings.triggered.connect(self.privacy_settings) self.actionInterface_settings.triggered.connect(self.interface_settings) @@ -115,6 +118,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): self.menuSettings.setTitle(QtWidgets.QApplication.translate("MainWindow", "Settings")) self.menuAbout.setTitle(QtWidgets.QApplication.translate("MainWindow", "About")) self.actionAdd_friend.setText(QtWidgets.QApplication.translate("MainWindow", "Add contact")) + self.actionAdd_gc.setText(QtWidgets.QApplication.translate("MainWindow", "Create group chat")) self.actionprofilesettings.setText(QtWidgets.QApplication.translate("MainWindow", "Profile")) self.actionPrivacy_settings.setText(QtWidgets.QApplication.translate("MainWindow", "Privacy")) self.actionInterface_settings.setText(QtWidgets.QApplication.translate("MainWindow", "Interface")) @@ -431,6 +435,9 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): self.a_c = AddContact(link or '') self.a_c.show() + def create_gc(self): + self.profile.create_group_chat() + def profile_settings(self, *args): self.p_s = ProfileSettings() self.p_s.show() diff --git a/toxygen/profile.py b/toxygen/profile.py index a0870d8..49139c6 100644 --- a/toxygen/profile.py +++ b/toxygen/profile.py @@ -252,12 +252,15 @@ class Profile(basecontact.BaseContact, Singleton): print('Incoming not started transfer - no info found') elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline self.create_inline_item(message.get_data()) - else: # info message + 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: @@ -271,7 +274,10 @@ class Profile(basecontact.BaseContact, Singleton): self._screen.account_name.setText(friend.name) self._screen.account_status.setText(friend.status_message) - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + if friend.tox_id is None: + avatar_path = curr_directory() + '/images/group.png' + else: + avatar_path = (ProfileHelper.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)) @@ -466,6 +472,9 @@ class Profile(basecontact.BaseContact, Singleton): :param text: message text :param friend_num: num of friend """ + if not self.is_active_a_friend(): + self.send_gc_message(text) + return if friend_num is None: friend_num = self.get_active_number() if text.startswith('/plugin '): @@ -481,8 +490,8 @@ class Profile(basecontact.BaseContact, Singleton): friend.inc_receipts() if friend.status is not None: self.split_and_send(friend.number, message_type, text.encode('utf-8')) - if friend.number == self.get_active_number(): - t = time.time() + t = time.time() + if friend.number == self.get_active_number() and self.is_active_a_friend(): self.create_message_item(text, t, MESSAGE_OWNER['NOT_SENT'], message_type) self._screen.messageEdit.clear() self._messages.scrollToBottom() @@ -505,7 +514,7 @@ class Profile(basecontact.BaseContact, Singleton): s = Settings.get_instance() if hasattr(self, '_history'): if s['save_history']: - for friend in self._contacts: + for friend in filter(lambda x: type(x) is Friend, self._contacts): if not self._history.friend_exists_in_db(friend.tox_id): self._history.add_friend_to_db(friend.tox_id) if not s['save_unsent_only']: @@ -645,7 +654,7 @@ class Profile(basecontact.BaseContact, Singleton): else: pixmap = self.get_pixmap() return self._factory.message_item(text, time, name, True, - message_type, append, pixmap) + message_type - 5, append, pixmap) def create_file_transfer_item(self, tm, append=True): data = list(tm.get_data()) @@ -1289,7 +1298,7 @@ class Profile(basecontact.BaseContact, Singleton): def add_gc(self, number): widget = self.create_friend_item() - gc = GroupChat('', '', widget, self._tox, number) + gc = GroupChat('Group chat #' + str(number), '', widget, self._tox, number) self._contacts.append(gc) def create_group_chat(self): @@ -1299,7 +1308,8 @@ class Profile(basecontact.BaseContact, Singleton): def group_invite(self, friend_number, gc_type, data): text = QtWidgets.QApplication.translate('MainWindow', 'User {} invites you to group chat. Accept?') title = QtWidgets.QApplication.translate('MainWindow', 'Group chat invite') - reply = QtWidgets.QMessageBox.question(None, title, text, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) + friend = self.get_friend_by_number(friend_number) + reply = QtWidgets.QMessageBox.question(None, title, text.format(friend.name), QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.Yes: # accepted if gc_type == TOX_GROUPCHAT_TYPE['TEXT']: number = self._tox.join_groupchat(friend_number, data) @@ -1309,12 +1319,13 @@ class Profile(basecontact.BaseContact, Singleton): def new_gc_message(self, group_number, peer_number, message_type, message): name = self._tox.group_peername(group_number, peer_number) + message_type += 5 if group_number == self.get_active_number() and not self.is_active_a_friend(): # add message to list t = time.time() self.create_gc_message_item(message, t, MESSAGE_OWNER['FRIEND'], name, message_type) self._messages.scrollToBottom() self.get_curr_friend().append_message( - GroupChatMessage(message, MESSAGE_OWNER['FRIEND'], t, message_type. name)) + GroupChatMessage(message, MESSAGE_OWNER['FRIEND'], t, message_type, name)) else: gc = self.get_group_by_number(group_number) gc.inc_messages() @@ -1328,7 +1339,19 @@ class Profile(basecontact.BaseContact, Singleton): gc.new_title(title) def update_gc(self, group_number): - pass + count = self._tox.group_number_peers(group_number) + gc = self.get_group_by_number(group_number) + text = QtWidgets.QApplication.translate('MainWindow', '{} users in chat') + gc.status_message = text.format(str(count)).encode('utf-8') + + def send_gc_message(self, text): + group_number = self.get_active_number() + if text.startswith('/me '): + text = text[4:] + self._tox.group_action_send(group_number, text.encode('utf-8')) + else: + self._tox.group_message_send(group_number, text.encode('utf-8')) + self._screen.messageEdit.clear() def tox_factory(data=None, settings=None): diff --git a/toxygen/tox.py b/toxygen/tox.py index 0d06f5b..c7760bc 100644 --- a/toxygen/tox.py +++ b/toxygen/tox.py @@ -90,6 +90,11 @@ class Tox: self.file_recv_chunk_cb = None self.friend_lossy_packet_cb = None self.friend_lossless_packet_cb = None + self.group_namelist_change_cb = None + self.group_title_cb = None + self.group_action_cb = None + self.group_message_cb = None + self.group_invite_cb = None self.AV = ToxAV(self._tox_pointer) @@ -1521,7 +1526,7 @@ class Tox: buffer = create_string_buffer(TOX_MAX_NAME_LENGTH) result = Tox.libtoxcore.tox_group_peername(self._tox_pointer, c_int(groupnumber), c_int(peernumber), buffer, None) - return buffer[:] + return str(buffer[:result], 'utf-8') def invite_friend(self, friendnumber, groupnumber): result = Tox.libtoxcore.tox_invite_friend(self._tox_pointer, c_int(friendnumber), @@ -1554,24 +1559,30 @@ class Tox: result = Tox.libtoxcore.tox_group_get_title(self._tox_pointer, c_int(groupnumber), buffer, c_uint32(TOX_MAX_NAME_LENGTH), None) - return buffer[:] + return str(buffer[:result], 'utf-8') def group_number_peers(self, groupnumber): result = Tox.libtoxcore.tox_group_number_peers(self._tox_pointer, c_int(groupnumber), None) return result - # def group_get_names(self): - # result = Tox.libtoxcore.tox_group_get_names(self._tox_pointer, c_int(groupnumber), - # c_char_p(names), None, c_uint16(length), error) - # 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 = Tox.libtoxcore.tox_add_av_groupchat(self._tox_pointer, None, None, None) + result = self.AV.libtoxav.tox_add_av_groupchat(self._tox_pointer, None, None, None) return result def join_av_groupchat(self, friendnumber, data): - result = Tox.libtoxcore.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.tox_join_av_groupchat(self._tox_pointer, c_int(friendnumber), + c_char_p(data), c_uint16(len(data)), + None, None, None) return result def callback_group_invite(self, callback, user_data=None):