feat: intergrate itchat to lib

This commit is contained in:
lanvent
2023-03-26 13:35:46 +08:00
parent 3c91575ebe
commit 92caeed7ab
23 changed files with 4649 additions and 2 deletions

View File

@@ -0,0 +1,117 @@
import os, time, copy
from threading import Lock
from .messagequeue import Queue
from .templates import (
ContactList, AbstractUserDict, User,
MassivePlatform, Chatroom, ChatroomMember)
def contact_change(fn):
def _contact_change(core, *args, **kwargs):
with core.storageClass.updateLock:
return fn(core, *args, **kwargs)
return _contact_change
class Storage(object):
def __init__(self, core):
self.userName = None
self.nickName = None
self.updateLock = Lock()
self.memberList = ContactList()
self.mpList = ContactList()
self.chatroomList = ContactList()
self.msgList = Queue(-1)
self.lastInputUserName = None
self.memberList.set_default_value(contactClass=User)
self.memberList.core = core
self.mpList.set_default_value(contactClass=MassivePlatform)
self.mpList.core = core
self.chatroomList.set_default_value(contactClass=Chatroom)
self.chatroomList.core = core
def dumps(self):
return {
'userName' : self.userName,
'nickName' : self.nickName,
'memberList' : self.memberList,
'mpList' : self.mpList,
'chatroomList' : self.chatroomList,
'lastInputUserName' : self.lastInputUserName, }
def loads(self, j):
self.userName = j.get('userName', None)
self.nickName = j.get('nickName', None)
del self.memberList[:]
for i in j.get('memberList', []):
self.memberList.append(i)
del self.mpList[:]
for i in j.get('mpList', []):
self.mpList.append(i)
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):
with self.updateLock:
if (name or userName or remarkName or nickName or wechatAccount) is None:
return copy.deepcopy(self.memberList[0]) # my own account
elif userName: # return the only userName match
for m in self.memberList:
if m['UserName'] == userName:
return copy.deepcopy(m)
else:
matchDict = {
'RemarkName' : remarkName,
'NickName' : nickName,
'Alias' : wechatAccount, }
for k in ('RemarkName', 'NickName', 'Alias'):
if matchDict[k] is None:
del matchDict[k]
if name: # select based on name
contact = []
for m in self.memberList:
if any([m.get(k) == name for k in ('RemarkName', 'NickName', 'Alias')]):
contact.append(m)
else:
contact = self.memberList[:]
if matchDict: # select again based on matchDict
friendList = []
for m in contact:
if all([m.get(k) == v for k, v in matchDict.items()]):
friendList.append(m)
return copy.deepcopy(friendList)
else:
return copy.deepcopy(contact)
def search_chatrooms(self, name=None, userName=None):
with self.updateLock:
if userName is not None:
for m in self.chatroomList:
if m['UserName'] == userName:
return copy.deepcopy(m)
elif name is not None:
matchList = []
for m in self.chatroomList:
if name in m['NickName']:
matchList.append(copy.deepcopy(m))
return matchList
def search_mps(self, name=None, userName=None):
with self.updateLock:
if userName is not None:
for m in self.mpList:
if m['UserName'] == userName:
return copy.deepcopy(m)
elif name is not None:
matchList = []
for m in self.mpList:
if name in m['NickName']:
matchList.append(copy.deepcopy(m))
return matchList

View File

@@ -0,0 +1,32 @@
import logging
try:
import Queue as queue
except ImportError:
import queue
from .templates import AttributeDict
logger = logging.getLogger('itchat')
class Queue(queue.Queue):
def put(self, message):
queue.Queue.put(self, Message(message))
class Message(AttributeDict):
def download(self, fileName):
if hasattr(self.text, '__call__'):
return self.text(fileName)
else:
return b''
def __getitem__(self, value):
if value in ('isAdmin', 'isAt'):
v = value[0].upper() + value[1:] # ''[1:] == ''
logger.debug('%s is expired in 1.3.0, use %s instead.' % (value, v))
value = v
return super(Message, self).__getitem__(value)
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__())

View File

@@ -0,0 +1,318 @@
import logging, copy, pickle
from weakref import ref
from ..returnvalues import ReturnValue
from ..utils import update_info_dict
logger = logging.getLogger('itchat')
class AttributeDict(dict):
def __getattr__(self, value):
keyName = value[0].upper() + value[1:]
try:
return self[keyName]
except KeyError:
raise AttributeError("'%s' object has no attribute '%s'" % (
self.__class__.__name__.split('.')[-1], keyName))
def get(self, v, d=None):
try:
return self[v]
except KeyError:
return d
class UnInitializedItchat(object):
def _raise_error(self, *args, **kwargs):
logger.warning('An itchat instance is called before initialized')
def __getattr__(self, value):
return self._raise_error
class ContactList(list):
''' when a dict is append, init function will be called to format that dict '''
def __init__(self, *args, **kwargs):
super(ContactList, self).__init__(*args, **kwargs)
self.__setstate__(None)
@property
def core(self):
return getattr(self, '_core', lambda: fakeItchat)() or fakeItchat
@core.setter
def core(self, value):
self._core = ref(value)
def set_default_value(self, initFunction=None, contactClass=None):
if hasattr(initFunction, '__call__'):
self.contactInitFn = initFunction
if hasattr(contactClass, '__call__'):
self.contactClass = contactClass
def append(self, value):
contact = self.contactClass(value)
contact.core = self.core
if self.contactInitFn is not None:
contact = self.contactInitFn(self, contact) or contact
super(ContactList, self).append(contact)
def __deepcopy__(self, memo):
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 1
def __setstate__(self, state):
self.contactInitFn = None
self.contactClass = User
def __str__(self):
return '[%s]' % ', '.join([repr(v) for v in self])
def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__.split('.')[-1],
self.__str__())
class AbstractUserDict(AttributeDict):
def __init__(self, *args, **kwargs):
super(AbstractUserDict, self).__init__(*args, **kwargs)
@property
def core(self):
return getattr(self, '_core', lambda: fakeItchat)() or fakeItchat
@core.setter
def core(self, value):
self._core = ref(value)
def update(self):
return ReturnValue({'BaseResponse': {
'Ret': -1006,
'ErrMsg': '%s can not be updated' % \
self.__class__.__name__, }, })
def set_alias(self, alias):
return ReturnValue({'BaseResponse': {
'Ret': -1006,
'ErrMsg': '%s can not set alias' % \
self.__class__.__name__, }, })
def set_pinned(self, isPinned=True):
return ReturnValue({'BaseResponse': {
'Ret': -1006,
'ErrMsg': '%s can not be pinned' % \
self.__class__.__name__, }, })
def verify(self):
return ReturnValue({'BaseResponse': {
'Ret': -1006,
'ErrMsg': '%s do not need verify' % \
self.__class__.__name__, }, })
def get_head_image(self, imageDir=None):
return self.core.get_head_img(self.userName, picDir=imageDir)
def delete_member(self, userName):
return ReturnValue({'BaseResponse': {
'Ret': -1006,
'ErrMsg': '%s can not delete member' % \
self.__class__.__name__, }, })
def add_member(self, userName):
return ReturnValue({'BaseResponse': {
'Ret': -1006,
'ErrMsg': '%s can not add member' % \
self.__class__.__name__, }, })
def send_raw_msg(self, msgType, content):
return self.core.send_raw_msg(msgType, content, self.userName)
def send_msg(self, msg='Test Message'):
return self.core.send_msg(msg, self.userName)
def send_file(self, fileDir, mediaId=None):
return self.core.send_file(fileDir, self.userName, mediaId)
def send_image(self, fileDir, mediaId=None):
return self.core.send_image(fileDir, self.userName, mediaId)
def send_video(self, fileDir=None, mediaId=None):
return self.core.send_video(fileDir, self.userName, mediaId)
def send(self, msg, mediaId=None):
return self.core.send(msg, self.userName, mediaId)
def search_member(self, name=None, userName=None, remarkName=None, nickName=None,
wechatAccount=None):
return ReturnValue({'BaseResponse': {
'Ret': -1006,
'ErrMsg': '%s do not have members' % \
self.__class__.__name__, }, })
def __deepcopy__(self, memo):
r = self.__class__()
for k, v in self.items():
r[copy.deepcopy(k)] = copy.deepcopy(v)
r.core = self.core
return r
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):
pass
class User(AbstractUserDict):
def __init__(self, *args, **kwargs):
super(User, self).__init__(*args, **kwargs)
self.__setstate__(None)
def update(self):
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):
return self.core.set_pinned(self.userName, isPinned)
def verify(self):
return self.core.add_friend(**self.verifyDict)
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):
def __init__(self, *args, **kwargs):
super(Chatroom, self).__init__(*args, **kwargs)
memberList = ContactList()
userName = self.get('UserName', '')
refSelf = ref(self)
def init_fn(parentList, d):
d.chatroom = refSelf() or \
parentList.core.search_chatrooms(userName=userName)
memberList.set_default_value(init_fn, ChatroomMember)
if 'MemberList' in self:
for member in self.memberList:
memberList.append(member)
self['MemberList'] = memberList
@property
def core(self):
return getattr(self, '_core', lambda: fakeItchat)() or fakeItchat
@core.setter
def core(self, value):
self._core = ref(value)
self.memberList.core = value
for member in self.memberList:
member.core = value
def update(self, detailedMember=False):
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):
return self.core.set_pinned(self.userName, isPinned)
def delete_member(self, userName):
return self.core.delete_member_from_chatroom(self.userName, userName)
def add_member(self, userName):
return self.core.add_member_into_chatroom(self.userName, userName)
def search_member(self, name=None, userName=None, remarkName=None, nickName=None,
wechatAccount=None):
with self.core.storageClass.updateLock:
if (name or userName or remarkName or nickName or wechatAccount) is None:
return None
elif userName: # return the only userName match
for m in self.memberList:
if m.userName == userName:
return copy.deepcopy(m)
else:
matchDict = {
'RemarkName' : remarkName,
'NickName' : nickName,
'Alias' : wechatAccount, }
for k in ('RemarkName', 'NickName', 'Alias'):
if matchDict[k] is None:
del matchDict[k]
if name: # select based on name
contact = []
for m in self.memberList:
if any([m.get(k) == name for k in ('RemarkName', 'NickName', 'Alias')]):
contact.append(m)
else:
contact = self.memberList[:]
if matchDict: # select again based on matchDict
friendList = []
for m in contact:
if all([m.get(k) == v for k, v in matchDict.items()]):
friendList.append(m)
return copy.deepcopy(friendList)
else:
return copy.deepcopy(contact)
def __setstate__(self, state):
super(Chatroom, self).__setstate__(state)
if not 'MemberList' in self:
self['MemberList'] = fakeContactList
class ChatroomMember(AbstractUserDict):
def __init__(self, *args, **kwargs):
super(AbstractUserDict, self).__init__(*args, **kwargs)
self.__setstate__(None)
@property
def chatroom(self):
r = getattr(self, '_chatroom', lambda: fakeChatroom)()
if r is None:
userName = getattr(self, '_chatroomUserName', '')
r = self.core.search_chatrooms(userName=userName)
if isinstance(r, dict):
self.chatroom = r
return r or fakeChatroom
@chatroom.setter
def chatroom(self, value):
if isinstance(value, dict) and 'UserName' in value:
self._chatroom = ref(value)
self._chatroomUserName = value['UserName']
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):
return self.core.delete_member_from_chatroom(self.chatroom.userName, self.userName)
def send_raw_msg(self, msgType, content):
return ReturnValue({'BaseResponse': {
'Ret': -1006,
'ErrMsg': '%s can not send message directly' % \
self.__class__.__name__, }, })
def send_msg(self, msg='Test Message'):
return ReturnValue({'BaseResponse': {
'Ret': -1006,
'ErrMsg': '%s can not send message directly' % \
self.__class__.__name__, }, })
def send_file(self, fileDir, mediaId=None):
return ReturnValue({'BaseResponse': {
'Ret': -1006,
'ErrMsg': '%s can not send message directly' % \
self.__class__.__name__, }, })
def send_image(self, fileDir, mediaId=None):
return ReturnValue({'BaseResponse': {
'Ret': -1006,
'ErrMsg': '%s can not send message directly' % \
self.__class__.__name__, }, })
def send_video(self, fileDir=None, mediaId=None):
return ReturnValue({'BaseResponse': {
'Ret': -1006,
'ErrMsg': '%s can not send message directly' % \
self.__class__.__name__, }, })
def send(self, msg, mediaId=None):
return ReturnValue({'BaseResponse': {
'Ret': -1006,
'ErrMsg': '%s can not send message directly' % \
self.__class__.__name__, }, })
def __setstate__(self, state):
super(ChatroomMember, self).__setstate__(state)
self['MemberList'] = fakeContactList
def wrap_user_dict(d):
userName = d.get('UserName')
if '@@' in userName:
r = Chatroom(d)
elif d.get('VerifyFlag', 8) & 8 == 0:
r = User(d)
else:
r = MassivePlatform(d)
return r
fakeItchat = UnInitializedItchat()
fakeContactList = ContactList()
fakeChatroom = Chatroom()