group creation, invites and message sending (untested)

This commit is contained in:
ingvar1995 2016-07-11 22:19:35 +03:00
parent c8bdb32e86
commit f13274882a
8 changed files with 165 additions and 105 deletions

View file

@ -91,10 +91,11 @@ class BaseContact:
if not os.path.isfile(avatar_path): # load default image
avatar_path = default_path
os.chdir(curr_directory() + '/images/')
pixmap = QtGui.QPixmap(QtCore.QSize(64, 64))
width = self._widget.avatar_label.width()
pixmap = QtGui.QPixmap(QtCore.QSize(width, width))
pixmap.load(avatar_path)
self._widget.avatar_label.setScaledContents(False)
self._widget.avatar_label.setPixmap(pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio))
self._widget.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio))
self._widget.avatar_label.repaint()
def reset_avatar(self):

View file

@ -287,6 +287,20 @@ def callback_audio(toxav, friend_number, samples, audio_samples_per_channel, aud
rate)
# -----------------------------------------------------------------------------------------------------------------
# Callbacks - group chats
# -----------------------------------------------------------------------------------------------------------------
def group_message(tox, group_number, peer_id, message, length, user_data):
pass
def group_invite(tox, friend_number, invite_data, length, user_data):
invoke_in_main_thread(Profile.get_instance().process_group_invite,
friend_number,
invite_data[:length])
# -----------------------------------------------------------------------------------------------------------------
# Callbacks - initialization
# -----------------------------------------------------------------------------------------------------------------

View file

@ -5,9 +5,6 @@ except ImportError:
import basecontact
from messages import *
from history import *
from settings import ProfileHelper
from toxcore_enums_and_consts import *
from util import curr_directory
class Contact(basecontact.BaseContact):
@ -17,7 +14,7 @@ class Contact(basecontact.BaseContact):
widget - widget for update
"""
def __init__(self, message_getter, name, status_message, widget, tox_id):
def __init__(self, number, message_getter, name, status_message, widget, tox_id):
"""
:param name: name, example: 'Toxygen user'
:param status_message: status message, example: 'Toxing on Toxygen'
@ -28,6 +25,7 @@ class Contact(basecontact.BaseContact):
self._message_getter = message_getter
self._new_messages = False
self._visible = True
self._number = number
self._corr = []
self._unsaved_messages = 0
self._history_loaded = self._new_actions = False
@ -150,40 +148,5 @@ class Contact(basecontact.BaseContact):
self._widget.messages.update(self._new_messages)
self._widget.connection_status.update(self.status, False)
# -----------------------------------------------------------------------------------------------------------------
# Avatars
# -----------------------------------------------------------------------------------------------------------------
def load_avatar(self):
"""
Tries to load avatar of contact or uses default avatar
"""
avatar_path = '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
os.chdir(ProfileHelper.get_path() + 'avatars/')
if not os.path.isfile(avatar_path): # load default image
avatar_path = 'avatar.png'
os.chdir(curr_directory() + '/images/')
width = self._widget.avatar_label.width()
pixmap = QtGui.QPixmap(QtCore.QSize(width, width))
pixmap.load(avatar_path)
self._widget.avatar_label.setScaledContents(False)
self._widget.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio))
self._widget.avatar_label.repaint()
def reset_avatar(self):
avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
if os.path.isfile(avatar_path):
os.remove(avatar_path)
self.load_avatar()
def set_avatar(self, avatar):
avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
with open(avatar_path, 'wb') as f:
f.write(avatar)
self.load_avatar()
def get_pixmap(self):
return self._widget.avatar_label.pixmap()
messages = property(get_messages)

View file

@ -10,13 +10,11 @@ class Friend(contact.Contact):
Friend in list of friends. Can be hidden, properties 'has unread messages' and 'has alias' added
"""
def __init__(self, message_getter, number, *args):
def __init__(self, *args):
"""
:param message_getter: gets messages from db
:param number: number of friend.
"""
super(Friend, self).__init__(message_getter, *args)
self._number = number
super(Friend, self).__init__(*args)
self._alias = False
self._receipts = 0

View file

@ -3,10 +3,11 @@ import contact
class GroupChat(contact.Contact):
def __init__(self, group_id, tox, *args):
def __init__(self, tox, *args):
super().__init__(*args)
self._id = group_id
self._tox = tox
def load_avatar(self, default_path='group.png'):
super().load_avatar(default_path)
# TODO: get peers list and add other methods

View file

@ -82,7 +82,7 @@ class MainWindow(QtGui.QMainWindow):
self.actionAbout_program.triggered.connect(self.about_program)
self.actionNetwork.triggered.connect(self.network_settings)
self.actionAdd_friend.triggered.connect(self.add_contact)
self.actionSettings.triggered.connect(self.profilesettings)
self.actionSettings.triggered.connect(self.profile_settings)
self.actionPrivacy_settings.triggered.connect(self.privacy_settings)
self.actionInterface_settings.triggered.connect(self.interface_settings)
self.actionNotifications.triggered.connect(self.notification_settings)
@ -205,9 +205,9 @@ class MainWindow(QtGui.QMainWindow):
Form.status_message.setObjectName("status_message")
self.connection_status = Form.connection_status = StatusCircle(Form)
Form.connection_status.setGeometry(QtCore.QRect(230, 35, 32, 32))
self.avatar_label.mouseReleaseEvent = self.profilesettings
self.status_message.mouseReleaseEvent = self.profilesettings
self.name.mouseReleaseEvent = self.profilesettings
self.avatar_label.mouseReleaseEvent = self.profile_settings
self.status_message.mouseReleaseEvent = self.profile_settings
self.name.mouseReleaseEvent = self.profile_settings
self.connection_status.raise_()
Form.connection_status.setObjectName("connection_status")
@ -232,6 +232,11 @@ class MainWindow(QtGui.QMainWindow):
font.setBold(False)
self.account_status.setFont(font)
self.account_status.setObjectName("account_status")
self.account_status.mouseReleaseEvent = self.show_chat_menu
self.account_name.mouseReleaseEvent = self.show_chat_menu
self.account_avatar.mouseReleaseEvent = self.show_chat_menu
self.callButton = QtGui.QPushButton(Form)
self.callButton.setGeometry(QtCore.QRect(550, 30, 50, 50))
self.callButton.setObjectName("callButton")
@ -389,7 +394,7 @@ class MainWindow(QtGui.QMainWindow):
self.a_c = AddContact(link)
self.a_c.show()
def profilesettings(self, *args):
def profile_settings(self, *args):
self.p_s = ProfileSettings()
self.p_s.show()
@ -455,6 +460,11 @@ class MainWindow(QtGui.QMainWindow):
self.gc = AddGroupchat()
self.gc.show()
def show_chat_menu(self):
pr = Profile.get_instance()
if not pr.is_active_a_friend():
pass # TODO: show list of users in chat
# -----------------------------------------------------------------------------------------------------------------
# Messages, calls and file transfers
# -----------------------------------------------------------------------------------------------------------------
@ -533,29 +543,32 @@ class MainWindow(QtGui.QMainWindow):
auto = QtGui.QApplication.translate("MainWindow", 'Disallow auto accept', None, QtGui.QApplication.UnicodeUTF8) if allowed else QtGui.QApplication.translate("MainWindow", 'Allow auto accept', None, QtGui.QApplication.UnicodeUTF8)
if item is not None:
self.listMenu = QtGui.QMenu()
set_alias_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Set alias', None, QtGui.QApplication.UnicodeUTF8))
clear_history_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Clear history', None, QtGui.QApplication.UnicodeUTF8))
copy_menu = self.listMenu.addMenu(QtGui.QApplication.translate("MainWindow", 'Copy', None, QtGui.QApplication.UnicodeUTF8))
copy_name_item = copy_menu.addAction(QtGui.QApplication.translate("MainWindow", 'Name', None, QtGui.QApplication.UnicodeUTF8))
copy_status_item = copy_menu.addAction(QtGui.QApplication.translate("MainWindow", 'Status message', None, QtGui.QApplication.UnicodeUTF8))
copy_key_item = copy_menu.addAction(QtGui.QApplication.translate("MainWindow", 'Public key', None, QtGui.QApplication.UnicodeUTF8))
if type(friend) is Friend: # TODO: add `invite to gc` submenu
set_alias_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Set alias', None, QtGui.QApplication.UnicodeUTF8))
clear_history_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Clear history', None, QtGui.QApplication.UnicodeUTF8))
copy_menu = self.listMenu.addMenu(QtGui.QApplication.translate("MainWindow", 'Copy', None, QtGui.QApplication.UnicodeUTF8))
copy_name_item = copy_menu.addAction(QtGui.QApplication.translate("MainWindow", 'Name', None, QtGui.QApplication.UnicodeUTF8))
copy_status_item = copy_menu.addAction(QtGui.QApplication.translate("MainWindow", 'Status message', None, QtGui.QApplication.UnicodeUTF8))
copy_key_item = copy_menu.addAction(QtGui.QApplication.translate("MainWindow", 'Public key', None, QtGui.QApplication.UnicodeUTF8))
auto_accept_item = self.listMenu.addAction(auto)
remove_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Remove friend', None, QtGui.QApplication.UnicodeUTF8))
notes_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Notes', None, QtGui.QApplication.UnicodeUTF8))
auto_accept_item = self.listMenu.addAction(auto)
remove_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Remove friend', None, QtGui.QApplication.UnicodeUTF8))
notes_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Notes', None, QtGui.QApplication.UnicodeUTF8))
submenu = plugin_support.PluginLoader.get_instance().get_menu(self.listMenu, num)
if len(submenu):
plug = self.listMenu.addMenu(QtGui.QApplication.translate("MainWindow", 'Plugins', None, QtGui.QApplication.UnicodeUTF8))
plug.addActions(submenu)
self.connect(set_alias_item, QtCore.SIGNAL("triggered()"), lambda: self.set_alias(num))
self.connect(remove_item, QtCore.SIGNAL("triggered()"), lambda: self.remove_friend(num))
self.connect(copy_key_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_friend_key(num))
self.connect(clear_history_item, QtCore.SIGNAL("triggered()"), lambda: self.clear_history(num))
self.connect(auto_accept_item, QtCore.SIGNAL("triggered()"), lambda: self.auto_accept(num, not allowed))
self.connect(notes_item, QtCore.SIGNAL("triggered()"), lambda: self.show_note(friend))
self.connect(copy_name_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_name(friend))
self.connect(copy_status_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_status(friend))
submenu = plugin_support.PluginLoader.get_instance().get_menu(self.listMenu, num)
if len(submenu):
plug = self.listMenu.addMenu(QtGui.QApplication.translate("MainWindow", 'Plugins', None, QtGui.QApplication.UnicodeUTF8))
plug.addActions(submenu)
self.connect(set_alias_item, QtCore.SIGNAL("triggered()"), lambda: self.set_alias(num))
self.connect(remove_item, QtCore.SIGNAL("triggered()"), lambda: self.remove_friend(num))
self.connect(copy_key_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_friend_key(num))
self.connect(clear_history_item, QtCore.SIGNAL("triggered()"), lambda: self.clear_history(num))
self.connect(auto_accept_item, QtCore.SIGNAL("triggered()"), lambda: self.auto_accept(num, not allowed))
self.connect(notes_item, QtCore.SIGNAL("triggered()"), lambda: self.show_note(friend))
self.connect(copy_name_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_name(friend))
self.connect(copy_status_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_status(friend))
else:
pass # TODO: add menu for gc
parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0))
self.listMenu.move(parent_position + pos)
self.listMenu.show()

View file

@ -60,7 +60,7 @@ class Profile(basecontact.BaseContact, Singleton):
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 = Friend(i, message_getter, name, status_message, item, tox_id)
friend.set_alias(alias)
self._friends_and_gc.append(friend)
self.filtration(self._show_online)
@ -138,7 +138,10 @@ class Profile(basecontact.BaseContact, Singleton):
self.filtration(self._show_online, self._filter_string)
def get_friend_by_number(self, num):
return list(filter(lambda x: x.number == num, self._friends_and_gc))[0]
return list(filter(lambda x: x.number == num and type(x) is Friend, self._friends_and_gc))[0]
def get_gc_by_number(self, num):
return list(filter(lambda x: x.number == num and type(x) is not Friend, self._friends_and_gc))[0]
def get_friend_or_gc(self, num):
return self._friends_and_gc[num]
@ -350,12 +353,13 @@ class Profile(basecontact.BaseContact, Singleton):
except:
pass
def split_and_send(self, number, message_type, message):
def split_and_send(self, number, message_type, message, is_group=False):
"""
Message splitting
:param number: friend's number
:param number: friend or gc number
:param message_type: type of message
:param message: message text
:param is_group: send to group
"""
while len(message) > TOX_MAX_MESSAGE_LENGTH:
size = TOX_MAX_MESSAGE_LENGTH * 4 / 5
@ -369,9 +373,15 @@ class Profile(basecontact.BaseContact, Singleton):
else:
index = TOX_MAX_MESSAGE_LENGTH - size - 1
index += size + 1
self._tox.friend_send_message(number, message_type, message[:index])
if not is_group:
self._tox.friend_send_message(number, message_type, message[:index])
else:
self._tox.group_send_message(number, message_type, message[:index])
message = message[index:]
self._tox.friend_send_message(number, message_type, message)
if not is_group:
self._tox.friend_send_message(number, message_type, message)
else:
self._tox.group_send_message(number, message_type, message)
def new_message(self, friend_num, message_type, message):
"""
@ -394,18 +404,20 @@ class Profile(basecontact.BaseContact, Singleton):
if not friend.visibility:
self.update_filtration()
def send_message(self, text, friend_num=None):
def send_message(self, text, number=None, is_gc=False):
"""
Send message
:param text: message text
:param friend_num: num of friend
:param number: num of friend or gc
:param is_gc: is group chat
"""
if friend_num is None:
friend_num = self.get_active_number()
if number is None:
number = self.get_active_number()
is_gc = not self.is_active_a_friend()
if text.startswith('/plugin '):
plugin_support.PluginLoader.get_instance().command(text[8:])
self._screen.messageEdit.clear()
elif text and friend_num + 1:
elif text and number + 1:
text = ''.join(c if c <= '\u10FFFF' else '\u25AF' for c in text)
if text.startswith('/me '):
@ -414,22 +426,30 @@ class Profile(basecontact.BaseContact, Singleton):
else:
message_type = TOX_MESSAGE_TYPE['NORMAL']
friend = self.get_friend_by_number(friend_num)
# TODO: send to gc
# friend = self._friends_and_gc[self._active_friend_or_gc]
friend.inc_receipts()
if friend.status is not None:
self.split_and_send(friend.number, message_type, text.encode('utf-8'))
if not is_gc:
friend_or_gc = self.get_friend_by_number(number)
else:
friend_or_gc = self.get_gc_by_number(number)
t = time.time()
if friend.number == self.get_active_number():
self.create_message_item(text, t, MESSAGE_OWNER['NOT_SENT'], message_type)
self._screen.messageEdit.clear()
self._messages.scrollToBottom()
friend.append_message(TextMessage(text, MESSAGE_OWNER['NOT_SENT'], t, message_type))
if not is_gc:
friend_or_gc.inc_receipts()
if friend_or_gc.status is not None:
self.split_and_send(friend_or_gc.number, message_type, text.encode('utf-8'))
if friend_or_gc.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()
else:
self.split_and_send(friend_or_gc.number, message_type, text.encode('utf-8'), True)
if friend_or_gc.number == self.get_active_number() and not self.is_active_a_friend():
self.create_message_item(text, t, MESSAGE_OWNER['ME'], message_type)
self._screen.messageEdit.clear()
self._messages.scrollToBottom()
friend_or_gc.append_message(TextMessage(text, MESSAGE_OWNER['NOT_SENT'], t, message_type))
def delete_message(self, time):
friend = self._friends[self._active_friend]
friend = self._friends_and_gc[self._active_friend_or_gc]
friend.delete_message(time)
self._history.delete_message(friend.tox_id, time)
self.update()
@ -1198,12 +1218,43 @@ class Profile(basecontact.BaseContact, Singleton):
# Group chats support
# -----------------------------------------------------------------------------------------------------------------
def add_gc(self, num):
tox_id = self._tox.group_get_chat_id(num)
name = self._tox.group_get_name(num)
topic = self._tox.group_get_topic(num)
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
gc = GroupChat(self._tox, message_getter, num, name, topic, item, tox_id)
self._friends_and_gc.append(gc)
def create_gc(self, name, is_public, password):
privacy_state = TOX_GROUP_PRIVACY_STATE['TOX_GROUP_PRIVACY_STATE_PUBLIC'] if is_public else TOX_GROUP_PRIVACY_STATE['TOX_GROUP_PRIVACY_STATE_PRIVATE']
num = self._tox.group_new(privacy_state, bytes(name, 'utf-8'))
if password:
self._tox.group_founder_set_password(num, password)
# self._friends_and_gc.append(Groupchat(num, self._tox, ))
self._tox.group_founder_set_password(num, bytes(password, 'utf-8'))
self.add_gc(num)
def process_group_invite(self, friend_num, data):
# TODO: add info to list and support password
try:
text = QtGui.QApplication.translate('MainWindow', 'User {} invites you to group',
None, QtGui.QApplication.UnicodeUTF8)
info = text.format(self.get_friend_by_number(friend_num).name)
fr_req = QtGui.QApplication.translate('MainWindow', 'Group chat invite', None, QtGui.QApplication.UnicodeUTF8)
reply = QtGui.QMessageBox.question(None, fr_req, info, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
if reply == QtGui.QMessageBox.Yes: # accepted
num = self._tox.group_invite_accept(data)
data = self._tox.get_savedata()
ProfileHelper.get_instance().save_profile(data)
self.add_gc(num)
except Exception as ex: # something is wrong
log('Accept group chat invite failed! ' + str(ex))
def tox_factory(data=None, settings=None):

View file

@ -1893,13 +1893,14 @@ class Tox:
"""
Write the Chat ID designated by the given group number to a byte array.
`chat_id` should have room for at least TOX_GROUP_CHAT_ID_SIZE bytes.
:return true on success.
:return chat id.
"""
error = c_int()
buff = create_string_buffer(TOX_GROUP_CHAT_ID_SIZE)
result = Tox.libtoxcore.tox_group_get_chat_id(self._tox_pointer, groupnumber,
create_string_buffer(TOX_GROUP_CHAT_ID_SIZE), byref(error))
return result
buff, byref(error))
return bin_to_string(buff[:TOX_GROUP_CHAT_ID_SIZE], TOX_GROUP_CHAT_ID_SIZE)
def group_get_number_groups(self):
"""
@ -2093,6 +2094,15 @@ class Tox:
"""
Set the callback for the `group_message` event. Pass NULL to unset.
This event is triggered when the client receives a group message.
Callback: python function with params:
tox Tox* instance
groupnumber The group number of the group the message is intended for.
peer_id The ID of the peer who sent the message.
type The type of message (normal, action, ...).
message The message data.
length The length of the message.
user_data - user data
"""
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_char_p, c_size_t, c_void_p)
@ -2140,7 +2150,7 @@ class Tox:
result = Tox.libtoxcore.tox_group_invite_friend(self._tox_pointer, groupnumber, friend_number, byref(error))
return result
def group_invite_accept(self, invite_data, password):
def group_invite_accept(self, invite_data, password=None):
"""
Accept an invite to a group chat that the client previously received from a friend. The invite
is only valid while the inviter is present in the group.
@ -2151,8 +2161,10 @@ class Tox:
"""
error = c_int()
result = Tox.libtoxcore.tox_group_invite_accept(self._tox_pointer, invite_data, len(invite_data), password,
len(password), byref(error))
result = Tox.libtoxcore.tox_group_invite_accept(self._tox_pointer, invite_data, len(invite_data),
password,
len(password) if password is not None else 0,
byref(error))
return result
def callback_group_invite(self, callback, user_data):
@ -2161,6 +2173,13 @@ class Tox:
This event is triggered when the client receives a group invite from a friend. The client must store
invite_data which is used to join the group via tox_group_invite_accept.
Callback: python function with params:
tox - Tox*
friend_number The friend number of the contact who sent the invite.
invite_data The invite data.
length The length of invite_data.
user_data - user data
"""
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p)