group creation, invites and message sending (untested)
This commit is contained in:
parent
c8bdb32e86
commit
f13274882a
8 changed files with 165 additions and 105 deletions
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,6 +543,7 @@ 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()
|
||||
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))
|
||||
|
@ -556,6 +567,8 @@ class MainWindow(QtGui.QMainWindow):
|
|||
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()
|
||||
|
|
|
@ -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
|
||||
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:]
|
||||
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():
|
||||
|
||||
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()
|
||||
friend.append_message(TextMessage(text, MESSAGE_OWNER['NOT_SENT'], t, message_type))
|
||||
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):
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue