From 04bcc113af5f4da989475d3c4c5ae012a222240b Mon Sep 17 00:00:00 2001 From: LittleCoder Date: Tue, 28 Mar 2017 13:06:27 +0800 Subject: [PATCH] Fix contact double bug --- itchat/components/contact.py | 13 +----- itchat/components/messages.py | 8 ++-- itchat/config.py | 2 +- itchat/storage/__init__.py | 16 ++++++-- itchat/storage/templates.py | 74 +++++++++++++++++++++++------------ itchat/utils.py | 10 +++++ 6 files changed, 79 insertions(+), 44 deletions(-) diff --git a/itchat/components/contact.py b/itchat/components/contact.py index 8de97b00..08b8e51b 100644 --- a/itchat/components/contact.py +++ b/itchat/components/contact.py @@ -7,6 +7,7 @@ from .. import config, utils from ..returnvalues import ReturnValue from ..storage import contact_change, templates +from ..utils import update_info_dict logger = logging.getLogger('itchat') @@ -98,16 +99,6 @@ def update_friend(self, userName): for f in friendList] return r if len(r) != 1 else r[0] -def update_info_dict(oldInfoDict, newInfoDict): - ''' only normal values will be updated here - because newInfoDict is normal dict, so it's not necessary to consider templates - ''' - for k, v in newInfoDict.items(): - if any((isinstance(v, t) for t in (tuple, list, dict))): - pass # these values will be updated somewhere else - elif oldInfoDict.get(k) is None or v not in (None, '', '0', 0): - oldInfoDict[k] = v - @contact_change def update_local_chatrooms(core, l): ''' @@ -131,7 +122,7 @@ def update_local_chatrooms(core, l): update_info_dict(oldChatroom, chatroom) # - update other values memberList = chatroom.get('MemberList', []) - oldMemberList = oldChatroom.memberList + oldMemberList = oldChatroom['MemberList'] if memberList: for member in memberList: oldMember = utils.search_dict_list( diff --git a/itchat/components/messages.py b/itchat/components/messages.py index d36fb97d..7c60e06c 100644 --- a/itchat/components/messages.py +++ b/itchat/components/messages.py @@ -369,11 +369,11 @@ def send_file(self, fileDir, toUserName=None, mediaId=None, file_=None): 'Ret': -1005, }}) if toUserName is None: toUserName = self.storageClass.userName + preparedFile = _prepare_file(fileDir, file_) + if not preparedFile: + return preparedFile + fileSize = preparedFile['fileSize'] if mediaId is None: - preparedFile = _prepare_file(fileDir, file_) - if not preparedFile: - return preparedFile - fileSize = preparedFile['fileSize'] r = self.upload_file(fileDir, preparedFile=preparedFile) if r: mediaId = r['MediaId'] diff --git a/itchat/config.py b/itchat/config.py index 09d3bfa5..2c05f324 100644 --- a/itchat/config.py +++ b/itchat/config.py @@ -1,6 +1,6 @@ import os, platform -VERSION = '1.3.2' +VERSION = '1.3.3' BASE_URL = 'https://login.weixin.qq.com' OS = platform.system() #Windows, Linux, Darwin DIR = os.getcwd() diff --git a/itchat/storage/__init__.py b/itchat/storage/__init__.py index 8010c9a1..ec6c8ba7 100644 --- a/itchat/storage/__init__.py +++ b/itchat/storage/__init__.py @@ -32,9 +32,9 @@ def dumps(self): return { 'userName' : self.userName, 'nickName' : self.nickName, - 'memberList' : [dict(member) for member in self.memberList], - 'mpList' : [dict(mp) for mp in self.mpList], - 'chatroomList' : [dict(chatroom) for chatroom in self.chatroomList], + 'memberList' : self.memberList, + 'mpList' : self.mpList, + 'chatroomList' : self.chatroomList, 'lastInputUserName' : self.lastInputUserName, } def loads(self, j): self.userName = j.get('userName', None) @@ -48,6 +48,16 @@ def loads(self, j): del self.chatroomList[:] for i in j.get('chatroomList', []): self.chatroomList.append(i) + # I tried to solve everything in pickle + # but this way is easier and more storage-saving + for chatroom in self.chatroomList: + if 'MemberList' in chatroom: + for member in chatroom['MemberList']: + member.core = chatroom.core + member.chatroom = chatroom + if 'Self' in chatroom: + chatroom['Self'].core = chatroom.core + chatroom['Self'].chatroom = chatroom self.lastInputUserName = j.get('lastInputUserName', None) def search_friends(self, name=None, userName=None, remarkName=None, nickName=None, wechatAccount=None): diff --git a/itchat/storage/templates.py b/itchat/storage/templates.py index 987b3233..8ec40e7e 100644 --- a/itchat/storage/templates.py +++ b/itchat/storage/templates.py @@ -1,6 +1,7 @@ import logging, copy, pickle from ..returnvalues import ReturnValue +from ..utils import update_info_dict logger = logging.getLogger('itchat') @@ -17,9 +18,7 @@ class ContactList(list): ''' def __init__(self, *args, **kwargs): super(ContactList, self).__init__(*args, **kwargs) - self.contactInitFn = None - self.contactClass = User - self.core = fakeItchat + self.__setstate__(None) def set_default_value(self, initFunction=None, contactClass=None): if hasattr(initFunction, '__call__'): self.contactInitFn = initFunction @@ -32,12 +31,17 @@ def append(self, value): contact = self.contactInitFn(contact) or contact super(ContactList, self).append(contact) def __deepcopy__(self, memo): - return self.__class__([copy.deepcopy(v) for v in self]) + r = self.__class__([copy.deepcopy(v) for v in self]) + r.contactInitFn = self.contactInitFn + r.contactClass = self.contactClass + r.core = self.core + return r def __getstate__(self): - return [pickle.dumps(v) for v in self] + return 1 def __setstate__(self, state): - for v in state: - super(ContactList, self).append(pickle.loads(v)) + self.contactInitFn = None + self.contactClass = User + self.core = fakeItchat def __str__(self): return '[%s]' % ', '.join([repr(v) for v in self]) def __repr__(self): @@ -49,7 +53,7 @@ def __repr__(self): class AbstractUserDict(dict): def __init__(self, *args, **kwargs): super(AbstractUserDict, self).__init__(*args, **kwargs) - self.core = fakeItchat + self.__setstate__(None) def update(self): return ReturnValue({'BaseResponse': { 'Ret': -1006, @@ -104,30 +108,31 @@ def __getattr__(self, value): value = value[0].upper() + value[1:] return self.get(value, '') def __deepcopy__(self, memo): - r = self.__class__({ - copy.deepcopy(k, memo): copy.deepcopy(v, memo) - for k, v in self.items()}) + r = self.__class__() + for k, v in self.items(): + r[copy.deepcopy(k)] = copy.deepcopy(v) r.core = self.core return r - def __getstate__(self): - return dict(self) - def __setstate__(self, state): - for k, v in state.items(): - self[k] = v def __str__(self): return '{%s}' % ', '.join( ['%s: %s' % (repr(k),repr(v)) for k,v in self.items()]) def __repr__(self): return '<%s: %s>' % (self.__class__.__name__.split('.')[-1], self.__str__()) + def __getstate__(self): + return 1 + def __setstate__(self, state): + self.core = fakeItchat class User(AbstractUserDict): def __init__(self, *args, **kwargs): super(User, self).__init__(*args, **kwargs) - self.verifyDict = {} - self.memberList = fakeContactList + self.__setstate__(None) def update(self): - return self.core.update_friend(self.userName) + r = self.core.update_friend(self.userName) + if r: + update_info_dict(self, r) + return r def set_alias(self, alias): return self.core.set_alias(self.userName, alias) def set_pinned(self, isPinned=True): @@ -138,10 +143,17 @@ def __deepcopy__(self, memo): r = super(User, self).__deepcopy__(memo) r.verifyDict = copy.deepcopy(self.verifyDict) return r + def __setstate__(self, state): + super(User, self).__setstate__(state) + self.verifyDict = {} + self.memberList = fakeContactList class MassivePlatform(AbstractUserDict): def __init__(self, *args, **kwargs): super(MassivePlatform, self).__init__(*args, **kwargs) + self.__setstate__(None) + def __setstate__(self, state): + super(MassivePlatform, self).__setstate__(state) self.memberList = fakeContactList class Chatroom(AbstractUserDict): @@ -151,11 +163,21 @@ def __init__(self, *args, **kwargs): def init_fn(d): d.chatroom = self memberList.set_default_value(init_fn, ChatroomMember) - for rawMember in self.memberList: - memberList.append(rawMember) - self['MemberList'] = memberList + if 'MemberList' in self: + if not isinstance(self.memberList, ContactList): + for member in self.memberList: + memberList.append(member) + self['MemberList'] = memberList + else: + for member in self.memberList: + memberList.append(member) + self['MemberList'] = memberList def update(self, detailedMember=False): - return self.core.update_chatroom(self.userName, detailedMember) + r = self.core.update_chatroom(self.userName, detailedMember) + if r: + update_info_dict(self, r) + self['MemberList'] = r['MemberList'] + return r def set_alias(self, alias): return self.core.set_chatroom_name(self.userName, alias) def set_pinned(self, isPinned=True): @@ -200,8 +222,7 @@ def search_member(self, name=None, userName=None, remarkName=None, nickName=None class ChatroomMember(AbstractUserDict): def __init__(self, *args, **kwargs): super(AbstractUserDict, self).__init__(*args, **kwargs) - self.core = fakeItchat - self.chatroom = self.fakeChatroom + self.__setstate__(None) def get_head_image(self, imageDir=None): return self.core.get_head_img(self.userName, self.chatroom.userName, picDir=imageDir) def delete_member(self, userName): @@ -240,6 +261,9 @@ def __deepcopy__(self, memo): r = super(ChatroomMember, self).__deepcopy__(memo) r.core = self.core return r + def __setstate__(self, state): + super(ChatroomMember, self).__setstate__(state) + self.chatroom = self.fakeChatroom ChatroomMember.fakeChatroom = Chatroom() diff --git a/itchat/utils.py b/itchat/utils.py index 202de6e7..89456532 100644 --- a/itchat/utils.py +++ b/itchat/utils.py @@ -142,3 +142,13 @@ def get_image_postfix(data): elif b'JFIF' in data: return 'jpg' return '' + +def update_info_dict(oldInfoDict, newInfoDict): + ''' only normal values will be updated here + because newInfoDict is normal dict, so it's not necessary to consider templates + ''' + for k, v in newInfoDict.items(): + if any((isinstance(v, t) for t in (tuple, list, dict))): + pass # these values will be updated somewhere else + elif oldInfoDict.get(k) is None or v not in (None, '', '0', 0): + oldInfoDict[k] = v