mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-21 22:27:13 +08:00
feat: intergrate itchat to lib
This commit is contained in:
488
lib/itchat/async_components/contact.py
Normal file
488
lib/itchat/async_components/contact.py
Normal file
@@ -0,0 +1,488 @@
|
||||
import time, re, io
|
||||
import json, copy
|
||||
import logging
|
||||
|
||||
from .. import config, utils
|
||||
from ..components.contact import accept_friend
|
||||
from ..returnvalues import ReturnValue
|
||||
from ..storage import contact_change
|
||||
from ..utils import update_info_dict
|
||||
|
||||
logger = logging.getLogger('itchat')
|
||||
|
||||
def load_contact(core):
|
||||
core.update_chatroom = update_chatroom
|
||||
core.update_friend = update_friend
|
||||
core.get_contact = get_contact
|
||||
core.get_friends = get_friends
|
||||
core.get_chatrooms = get_chatrooms
|
||||
core.get_mps = get_mps
|
||||
core.set_alias = set_alias
|
||||
core.set_pinned = set_pinned
|
||||
core.accept_friend = accept_friend
|
||||
core.get_head_img = get_head_img
|
||||
core.create_chatroom = create_chatroom
|
||||
core.set_chatroom_name = set_chatroom_name
|
||||
core.delete_member_from_chatroom = delete_member_from_chatroom
|
||||
core.add_member_into_chatroom = add_member_into_chatroom
|
||||
|
||||
def update_chatroom(self, userName, detailedMember=False):
|
||||
if not isinstance(userName, list):
|
||||
userName = [userName]
|
||||
url = '%s/webwxbatchgetcontact?type=ex&r=%s' % (
|
||||
self.loginInfo['url'], int(time.time()))
|
||||
headers = {
|
||||
'ContentType': 'application/json; charset=UTF-8',
|
||||
'User-Agent' : config.USER_AGENT }
|
||||
data = {
|
||||
'BaseRequest': self.loginInfo['BaseRequest'],
|
||||
'Count': len(userName),
|
||||
'List': [{
|
||||
'UserName': u,
|
||||
'ChatRoomId': '', } for u in userName], }
|
||||
chatroomList = json.loads(self.s.post(url, data=json.dumps(data), headers=headers
|
||||
).content.decode('utf8', 'replace')).get('ContactList')
|
||||
if not chatroomList:
|
||||
return ReturnValue({'BaseResponse': {
|
||||
'ErrMsg': 'No chatroom found',
|
||||
'Ret': -1001, }})
|
||||
|
||||
if detailedMember:
|
||||
def get_detailed_member_info(encryChatroomId, memberList):
|
||||
url = '%s/webwxbatchgetcontact?type=ex&r=%s' % (
|
||||
self.loginInfo['url'], int(time.time()))
|
||||
headers = {
|
||||
'ContentType': 'application/json; charset=UTF-8',
|
||||
'User-Agent' : config.USER_AGENT, }
|
||||
data = {
|
||||
'BaseRequest': self.loginInfo['BaseRequest'],
|
||||
'Count': len(memberList),
|
||||
'List': [{
|
||||
'UserName': member['UserName'],
|
||||
'EncryChatRoomId': encryChatroomId} \
|
||||
for member in memberList], }
|
||||
return json.loads(self.s.post(url, data=json.dumps(data), headers=headers
|
||||
).content.decode('utf8', 'replace'))['ContactList']
|
||||
MAX_GET_NUMBER = 50
|
||||
for chatroom in chatroomList:
|
||||
totalMemberList = []
|
||||
for i in range(int(len(chatroom['MemberList']) / MAX_GET_NUMBER + 1)):
|
||||
memberList = chatroom['MemberList'][i*MAX_GET_NUMBER: (i+1)*MAX_GET_NUMBER]
|
||||
totalMemberList += get_detailed_member_info(chatroom['EncryChatRoomId'], memberList)
|
||||
chatroom['MemberList'] = totalMemberList
|
||||
|
||||
update_local_chatrooms(self, chatroomList)
|
||||
r = [self.storageClass.search_chatrooms(userName=c['UserName'])
|
||||
for c in chatroomList]
|
||||
return r if 1 < len(r) else r[0]
|
||||
|
||||
def update_friend(self, userName):
|
||||
if not isinstance(userName, list):
|
||||
userName = [userName]
|
||||
url = '%s/webwxbatchgetcontact?type=ex&r=%s' % (
|
||||
self.loginInfo['url'], int(time.time()))
|
||||
headers = {
|
||||
'ContentType': 'application/json; charset=UTF-8',
|
||||
'User-Agent' : config.USER_AGENT }
|
||||
data = {
|
||||
'BaseRequest': self.loginInfo['BaseRequest'],
|
||||
'Count': len(userName),
|
||||
'List': [{
|
||||
'UserName': u,
|
||||
'EncryChatRoomId': '', } for u in userName], }
|
||||
friendList = json.loads(self.s.post(url, data=json.dumps(data), headers=headers
|
||||
).content.decode('utf8', 'replace')).get('ContactList')
|
||||
|
||||
update_local_friends(self, friendList)
|
||||
r = [self.storageClass.search_friends(userName=f['UserName'])
|
||||
for f in friendList]
|
||||
return r if len(r) != 1 else r[0]
|
||||
|
||||
@contact_change
|
||||
def update_local_chatrooms(core, l):
|
||||
'''
|
||||
get a list of chatrooms for updating local chatrooms
|
||||
return a list of given chatrooms with updated info
|
||||
'''
|
||||
for chatroom in l:
|
||||
# format new chatrooms
|
||||
utils.emoji_formatter(chatroom, 'NickName')
|
||||
for member in chatroom['MemberList']:
|
||||
if 'NickName' in member:
|
||||
utils.emoji_formatter(member, 'NickName')
|
||||
if 'DisplayName' in member:
|
||||
utils.emoji_formatter(member, 'DisplayName')
|
||||
if 'RemarkName' in member:
|
||||
utils.emoji_formatter(member, 'RemarkName')
|
||||
# update it to old chatrooms
|
||||
oldChatroom = utils.search_dict_list(
|
||||
core.chatroomList, 'UserName', chatroom['UserName'])
|
||||
if oldChatroom:
|
||||
update_info_dict(oldChatroom, chatroom)
|
||||
# - update other values
|
||||
memberList = chatroom.get('MemberList', [])
|
||||
oldMemberList = oldChatroom['MemberList']
|
||||
if memberList:
|
||||
for member in memberList:
|
||||
oldMember = utils.search_dict_list(
|
||||
oldMemberList, 'UserName', member['UserName'])
|
||||
if oldMember:
|
||||
update_info_dict(oldMember, member)
|
||||
else:
|
||||
oldMemberList.append(member)
|
||||
else:
|
||||
core.chatroomList.append(chatroom)
|
||||
oldChatroom = utils.search_dict_list(
|
||||
core.chatroomList, 'UserName', chatroom['UserName'])
|
||||
# delete useless members
|
||||
if len(chatroom['MemberList']) != len(oldChatroom['MemberList']) and \
|
||||
chatroom['MemberList']:
|
||||
existsUserNames = [member['UserName'] for member in chatroom['MemberList']]
|
||||
delList = []
|
||||
for i, member in enumerate(oldChatroom['MemberList']):
|
||||
if member['UserName'] not in existsUserNames:
|
||||
delList.append(i)
|
||||
delList.sort(reverse=True)
|
||||
for i in delList:
|
||||
del oldChatroom['MemberList'][i]
|
||||
# - update OwnerUin
|
||||
if oldChatroom.get('ChatRoomOwner') and oldChatroom.get('MemberList'):
|
||||
owner = utils.search_dict_list(oldChatroom['MemberList'],
|
||||
'UserName', oldChatroom['ChatRoomOwner'])
|
||||
oldChatroom['OwnerUin'] = (owner or {}).get('Uin', 0)
|
||||
# - update IsAdmin
|
||||
if 'OwnerUin' in oldChatroom and oldChatroom['OwnerUin'] != 0:
|
||||
oldChatroom['IsAdmin'] = \
|
||||
oldChatroom['OwnerUin'] == int(core.loginInfo['wxuin'])
|
||||
else:
|
||||
oldChatroom['IsAdmin'] = None
|
||||
# - update Self
|
||||
newSelf = utils.search_dict_list(oldChatroom['MemberList'],
|
||||
'UserName', core.storageClass.userName)
|
||||
oldChatroom['Self'] = newSelf or copy.deepcopy(core.loginInfo['User'])
|
||||
return {
|
||||
'Type' : 'System',
|
||||
'Text' : [chatroom['UserName'] for chatroom in l],
|
||||
'SystemInfo' : 'chatrooms',
|
||||
'FromUserName' : core.storageClass.userName,
|
||||
'ToUserName' : core.storageClass.userName, }
|
||||
|
||||
@contact_change
|
||||
def update_local_friends(core, l):
|
||||
'''
|
||||
get a list of friends or mps for updating local contact
|
||||
'''
|
||||
fullList = core.memberList + core.mpList
|
||||
for friend in l:
|
||||
if 'NickName' in friend:
|
||||
utils.emoji_formatter(friend, 'NickName')
|
||||
if 'DisplayName' in friend:
|
||||
utils.emoji_formatter(friend, 'DisplayName')
|
||||
if 'RemarkName' in friend:
|
||||
utils.emoji_formatter(friend, 'RemarkName')
|
||||
oldInfoDict = utils.search_dict_list(
|
||||
fullList, 'UserName', friend['UserName'])
|
||||
if oldInfoDict is None:
|
||||
oldInfoDict = copy.deepcopy(friend)
|
||||
if oldInfoDict['VerifyFlag'] & 8 == 0:
|
||||
core.memberList.append(oldInfoDict)
|
||||
else:
|
||||
core.mpList.append(oldInfoDict)
|
||||
else:
|
||||
update_info_dict(oldInfoDict, friend)
|
||||
|
||||
@contact_change
|
||||
def update_local_uin(core, msg):
|
||||
'''
|
||||
content contains uins and StatusNotifyUserName contains username
|
||||
they are in same order, so what I do is to pair them together
|
||||
|
||||
I caught an exception in this method while not knowing why
|
||||
but don't worry, it won't cause any problem
|
||||
'''
|
||||
uins = re.search('<username>([^<]*?)<', msg['Content'])
|
||||
usernameChangedList = []
|
||||
r = {
|
||||
'Type': 'System',
|
||||
'Text': usernameChangedList,
|
||||
'SystemInfo': 'uins', }
|
||||
if uins:
|
||||
uins = uins.group(1).split(',')
|
||||
usernames = msg['StatusNotifyUserName'].split(',')
|
||||
if 0 < len(uins) == len(usernames):
|
||||
for uin, username in zip(uins, usernames):
|
||||
if not '@' in username: continue
|
||||
fullContact = core.memberList + core.chatroomList + core.mpList
|
||||
userDicts = utils.search_dict_list(fullContact,
|
||||
'UserName', username)
|
||||
if userDicts:
|
||||
if userDicts.get('Uin', 0) == 0:
|
||||
userDicts['Uin'] = uin
|
||||
usernameChangedList.append(username)
|
||||
logger.debug('Uin fetched: %s, %s' % (username, uin))
|
||||
else:
|
||||
if userDicts['Uin'] != uin:
|
||||
logger.debug('Uin changed: %s, %s' % (
|
||||
userDicts['Uin'], uin))
|
||||
else:
|
||||
if '@@' in username:
|
||||
core.storageClass.updateLock.release()
|
||||
update_chatroom(core, username)
|
||||
core.storageClass.updateLock.acquire()
|
||||
newChatroomDict = utils.search_dict_list(
|
||||
core.chatroomList, 'UserName', username)
|
||||
if newChatroomDict is None:
|
||||
newChatroomDict = utils.struct_friend_info({
|
||||
'UserName': username,
|
||||
'Uin': uin,
|
||||
'Self': copy.deepcopy(core.loginInfo['User'])})
|
||||
core.chatroomList.append(newChatroomDict)
|
||||
else:
|
||||
newChatroomDict['Uin'] = uin
|
||||
elif '@' in username:
|
||||
core.storageClass.updateLock.release()
|
||||
update_friend(core, username)
|
||||
core.storageClass.updateLock.acquire()
|
||||
newFriendDict = utils.search_dict_list(
|
||||
core.memberList, 'UserName', username)
|
||||
if newFriendDict is None:
|
||||
newFriendDict = utils.struct_friend_info({
|
||||
'UserName': username,
|
||||
'Uin': uin, })
|
||||
core.memberList.append(newFriendDict)
|
||||
else:
|
||||
newFriendDict['Uin'] = uin
|
||||
usernameChangedList.append(username)
|
||||
logger.debug('Uin fetched: %s, %s' % (username, uin))
|
||||
else:
|
||||
logger.debug('Wrong length of uins & usernames: %s, %s' % (
|
||||
len(uins), len(usernames)))
|
||||
else:
|
||||
logger.debug('No uins in 51 message')
|
||||
logger.debug(msg['Content'])
|
||||
return r
|
||||
|
||||
def get_contact(self, update=False):
|
||||
if not update:
|
||||
return utils.contact_deep_copy(self, self.chatroomList)
|
||||
def _get_contact(seq=0):
|
||||
url = '%s/webwxgetcontact?r=%s&seq=%s&skey=%s' % (self.loginInfo['url'],
|
||||
int(time.time()), seq, self.loginInfo['skey'])
|
||||
headers = {
|
||||
'ContentType': 'application/json; charset=UTF-8',
|
||||
'User-Agent' : config.USER_AGENT, }
|
||||
try:
|
||||
r = self.s.get(url, headers=headers)
|
||||
except:
|
||||
logger.info('Failed to fetch contact, that may because of the amount of your chatrooms')
|
||||
for chatroom in self.get_chatrooms():
|
||||
self.update_chatroom(chatroom['UserName'], detailedMember=True)
|
||||
return 0, []
|
||||
j = json.loads(r.content.decode('utf-8', 'replace'))
|
||||
return j.get('Seq', 0), j.get('MemberList')
|
||||
seq, memberList = 0, []
|
||||
while 1:
|
||||
seq, batchMemberList = _get_contact(seq)
|
||||
memberList.extend(batchMemberList)
|
||||
if seq == 0:
|
||||
break
|
||||
chatroomList, otherList = [], []
|
||||
for m in memberList:
|
||||
if m['Sex'] != 0:
|
||||
otherList.append(m)
|
||||
elif '@@' in m['UserName']:
|
||||
chatroomList.append(m)
|
||||
elif '@' in m['UserName']:
|
||||
# mp will be dealt in update_local_friends as well
|
||||
otherList.append(m)
|
||||
if chatroomList:
|
||||
update_local_chatrooms(self, chatroomList)
|
||||
if otherList:
|
||||
update_local_friends(self, otherList)
|
||||
return utils.contact_deep_copy(self, chatroomList)
|
||||
|
||||
def get_friends(self, update=False):
|
||||
if update:
|
||||
self.get_contact(update=True)
|
||||
return utils.contact_deep_copy(self, self.memberList)
|
||||
|
||||
def get_chatrooms(self, update=False, contactOnly=False):
|
||||
if contactOnly:
|
||||
return self.get_contact(update=True)
|
||||
else:
|
||||
if update:
|
||||
self.get_contact(True)
|
||||
return utils.contact_deep_copy(self, self.chatroomList)
|
||||
|
||||
def get_mps(self, update=False):
|
||||
if update: self.get_contact(update=True)
|
||||
return utils.contact_deep_copy(self, self.mpList)
|
||||
|
||||
def set_alias(self, userName, alias):
|
||||
oldFriendInfo = utils.search_dict_list(
|
||||
self.memberList, 'UserName', userName)
|
||||
if oldFriendInfo is None:
|
||||
return ReturnValue({'BaseResponse': {
|
||||
'Ret': -1001, }})
|
||||
url = '%s/webwxoplog?lang=%s&pass_ticket=%s' % (
|
||||
self.loginInfo['url'], 'zh_CN', self.loginInfo['pass_ticket'])
|
||||
data = {
|
||||
'UserName' : userName,
|
||||
'CmdId' : 2,
|
||||
'RemarkName' : alias,
|
||||
'BaseRequest' : self.loginInfo['BaseRequest'], }
|
||||
headers = { 'User-Agent' : config.USER_AGENT}
|
||||
r = self.s.post(url, json.dumps(data, ensure_ascii=False).encode('utf8'),
|
||||
headers=headers)
|
||||
r = ReturnValue(rawResponse=r)
|
||||
if r:
|
||||
oldFriendInfo['RemarkName'] = alias
|
||||
return r
|
||||
|
||||
def set_pinned(self, userName, isPinned=True):
|
||||
url = '%s/webwxoplog?pass_ticket=%s' % (
|
||||
self.loginInfo['url'], self.loginInfo['pass_ticket'])
|
||||
data = {
|
||||
'UserName' : userName,
|
||||
'CmdId' : 3,
|
||||
'OP' : int(isPinned),
|
||||
'BaseRequest' : self.loginInfo['BaseRequest'], }
|
||||
headers = { 'User-Agent' : config.USER_AGENT}
|
||||
r = self.s.post(url, json=data, headers=headers)
|
||||
return ReturnValue(rawResponse=r)
|
||||
|
||||
def accept_friend(self, userName, v4= '', autoUpdate=True):
|
||||
url = f"{self.loginInfo['url']}/webwxverifyuser?r={int(time.time())}&pass_ticket={self.loginInfo['pass_ticket']}"
|
||||
data = {
|
||||
'BaseRequest': self.loginInfo['BaseRequest'],
|
||||
'Opcode': 3, # 3
|
||||
'VerifyUserListSize': 1,
|
||||
'VerifyUserList': [{
|
||||
'Value': userName,
|
||||
'VerifyUserTicket': v4, }],
|
||||
'VerifyContent': '',
|
||||
'SceneListCount': 1,
|
||||
'SceneList': [33],
|
||||
'skey': self.loginInfo['skey'], }
|
||||
headers = {
|
||||
'ContentType': 'application/json; charset=UTF-8',
|
||||
'User-Agent' : config.USER_AGENT }
|
||||
r = self.s.post(url, headers=headers,
|
||||
data=json.dumps(data, ensure_ascii=False).encode('utf8', 'replace'))
|
||||
if autoUpdate:
|
||||
self.update_friend(userName)
|
||||
return ReturnValue(rawResponse=r)
|
||||
|
||||
def get_head_img(self, userName=None, chatroomUserName=None, picDir=None):
|
||||
''' get head image
|
||||
* if you want to get chatroom header: only set chatroomUserName
|
||||
* if you want to get friend header: only set userName
|
||||
* if you want to get chatroom member header: set both
|
||||
'''
|
||||
params = {
|
||||
'userName': userName or chatroomUserName or self.storageClass.userName,
|
||||
'skey': self.loginInfo['skey'],
|
||||
'type': 'big', }
|
||||
url = '%s/webwxgeticon' % self.loginInfo['url']
|
||||
if chatroomUserName is None:
|
||||
infoDict = self.storageClass.search_friends(userName=userName)
|
||||
if infoDict is None:
|
||||
return ReturnValue({'BaseResponse': {
|
||||
'ErrMsg': 'No friend found',
|
||||
'Ret': -1001, }})
|
||||
else:
|
||||
if userName is None:
|
||||
url = '%s/webwxgetheadimg' % self.loginInfo['url']
|
||||
else:
|
||||
chatroom = self.storageClass.search_chatrooms(userName=chatroomUserName)
|
||||
if chatroomUserName is None:
|
||||
return ReturnValue({'BaseResponse': {
|
||||
'ErrMsg': 'No chatroom found',
|
||||
'Ret': -1001, }})
|
||||
if 'EncryChatRoomId' in chatroom:
|
||||
params['chatroomid'] = chatroom['EncryChatRoomId']
|
||||
params['chatroomid'] = params.get('chatroomid') or chatroom['UserName']
|
||||
headers = { 'User-Agent' : config.USER_AGENT}
|
||||
r = self.s.get(url, params=params, stream=True, headers=headers)
|
||||
tempStorage = io.BytesIO()
|
||||
for block in r.iter_content(1024):
|
||||
tempStorage.write(block)
|
||||
if picDir is None:
|
||||
return tempStorage.getvalue()
|
||||
with open(picDir, 'wb') as f:
|
||||
f.write(tempStorage.getvalue())
|
||||
tempStorage.seek(0)
|
||||
return ReturnValue({'BaseResponse': {
|
||||
'ErrMsg': 'Successfully downloaded',
|
||||
'Ret': 0, },
|
||||
'PostFix': utils.get_image_postfix(tempStorage.read(20)), })
|
||||
|
||||
def create_chatroom(self, memberList, topic=''):
|
||||
url = '%s/webwxcreatechatroom?pass_ticket=%s&r=%s' % (
|
||||
self.loginInfo['url'], self.loginInfo['pass_ticket'], int(time.time()))
|
||||
data = {
|
||||
'BaseRequest': self.loginInfo['BaseRequest'],
|
||||
'MemberCount': len(memberList.split(',')),
|
||||
'MemberList': [{'UserName': member} for member in memberList.split(',')],
|
||||
'Topic': topic, }
|
||||
headers = {
|
||||
'content-type': 'application/json; charset=UTF-8',
|
||||
'User-Agent' : config.USER_AGENT }
|
||||
r = self.s.post(url, headers=headers,
|
||||
data=json.dumps(data, ensure_ascii=False).encode('utf8', 'ignore'))
|
||||
return ReturnValue(rawResponse=r)
|
||||
|
||||
def set_chatroom_name(self, chatroomUserName, name):
|
||||
url = '%s/webwxupdatechatroom?fun=modtopic&pass_ticket=%s' % (
|
||||
self.loginInfo['url'], self.loginInfo['pass_ticket'])
|
||||
data = {
|
||||
'BaseRequest': self.loginInfo['BaseRequest'],
|
||||
'ChatRoomName': chatroomUserName,
|
||||
'NewTopic': name, }
|
||||
headers = {
|
||||
'content-type': 'application/json; charset=UTF-8',
|
||||
'User-Agent' : config.USER_AGENT }
|
||||
r = self.s.post(url, headers=headers,
|
||||
data=json.dumps(data, ensure_ascii=False).encode('utf8', 'ignore'))
|
||||
return ReturnValue(rawResponse=r)
|
||||
|
||||
def delete_member_from_chatroom(self, chatroomUserName, memberList):
|
||||
url = '%s/webwxupdatechatroom?fun=delmember&pass_ticket=%s' % (
|
||||
self.loginInfo['url'], self.loginInfo['pass_ticket'])
|
||||
data = {
|
||||
'BaseRequest': self.loginInfo['BaseRequest'],
|
||||
'ChatRoomName': chatroomUserName,
|
||||
'DelMemberList': ','.join([member['UserName'] for member in memberList]), }
|
||||
headers = {
|
||||
'content-type': 'application/json; charset=UTF-8',
|
||||
'User-Agent' : config.USER_AGENT}
|
||||
r = self.s.post(url, data=json.dumps(data),headers=headers)
|
||||
return ReturnValue(rawResponse=r)
|
||||
|
||||
def add_member_into_chatroom(self, chatroomUserName, memberList,
|
||||
useInvitation=False):
|
||||
''' add or invite member into chatroom
|
||||
* there are two ways to get members into chatroom: invite or directly add
|
||||
* but for chatrooms with more than 40 users, you can only use invite
|
||||
* but don't worry we will auto-force userInvitation for you when necessary
|
||||
'''
|
||||
if not useInvitation:
|
||||
chatroom = self.storageClass.search_chatrooms(userName=chatroomUserName)
|
||||
if not chatroom: chatroom = self.update_chatroom(chatroomUserName)
|
||||
if len(chatroom['MemberList']) > self.loginInfo['InviteStartCount']:
|
||||
useInvitation = True
|
||||
if useInvitation:
|
||||
fun, memberKeyName = 'invitemember', 'InviteMemberList'
|
||||
else:
|
||||
fun, memberKeyName = 'addmember', 'AddMemberList'
|
||||
url = '%s/webwxupdatechatroom?fun=%s&pass_ticket=%s' % (
|
||||
self.loginInfo['url'], fun, self.loginInfo['pass_ticket'])
|
||||
params = {
|
||||
'BaseRequest' : self.loginInfo['BaseRequest'],
|
||||
'ChatRoomName' : chatroomUserName,
|
||||
memberKeyName : memberList, }
|
||||
headers = {
|
||||
'content-type': 'application/json; charset=UTF-8',
|
||||
'User-Agent' : config.USER_AGENT}
|
||||
r = self.s.post(url, data=json.dumps(params),headers=headers)
|
||||
return ReturnValue(rawResponse=r)
|
||||
Reference in New Issue
Block a user