diff --git a/efb_qq_slave/Clients/CoolQ/CoolQ.py b/efb_qq_slave/Clients/CoolQ/CoolQ.py index 8fe6375..172c0b8 100644 --- a/efb_qq_slave/Clients/CoolQ/CoolQ.py +++ b/efb_qq_slave/Clients/CoolQ/CoolQ.py @@ -4,6 +4,7 @@ import threading import time import uuid +import requests from typing import IO, Any, Dict, Optional, List, Tuple import cqhttp @@ -23,7 +24,8 @@ CoolQUnknownException from .MsgDecorator import QQMsgProcessor from .Utils import qq_emoji_list, async_send_messages_to_master, process_quote_text, coolq_text_encode, \ - upload_image_smms, download_file_from_qzone, download_user_avatar, download_group_avatar + upload_image_smms, download_file_from_qzone, download_user_avatar, download_group_avatar, \ + get_friend_list_via_qq_show from ..BaseClient import BaseClient from ... import QQMessengerChannel @@ -112,7 +114,7 @@ def handle_msg(context): if str(my_uid) == str(msg_data['qq']) or str(msg_data['qq']) == 'all': at_list[(substitution_begin, substitution_end)] = EFBChat(self.channel).self() else: - messages.append(self.call_msg_decorator(msg_type, msg_data)) + messages.extend(self.call_msg_decorator(msg_type, msg_data)) if main_text != "": messages.append(self.msg_decorator.qq_text_simple_wrapper(main_text, at_list)) uid: str = str(uuid.uuid4()) @@ -355,8 +357,21 @@ def get_friends(self) -> List: 'Only groups are shown.')) return [] res = self.friend_list - # res = self.coolq_bot._get_friend_list() users = [] + for i in range(len(res)): # friend group + for j in range(len(res[i]['friend'])): + current_user = res[i]['friend'][j] + txt = '[{}] {}' + txt = txt.format(res[i]['name'], current_user['name']) + # nickname = self.get_stranger_info(current_user['uin'])['nickname'] + + # Disable nickname & remark comparsion for it's too time-consuming + context = {'user_id': str(current_user['uin']), + 'nickname': txt, + 'alias': None} + efb_chat = self.chat_manager.build_efb_chat_as_user(context, True) + users.append(efb_chat) + ''' for i in range(len(res)): # friend group for j in range(len(res[i]['friends'])): current_user = res[i]['friends'][j] @@ -372,6 +387,7 @@ def get_friends(self) -> List: 'alias': txt} efb_chat = self.chat_manager.build_efb_chat_as_user(context, True) users.append(efb_chat) + ''' return users def receive_message(self): @@ -576,7 +592,14 @@ def deliver_alert_to_master(self, message: str): def update_friend_list(self): # Warning: Experimental API - self.friend_list = self.coolq_api_query('_get_friend_list') + # self.friend_list = self.coolq_api_query('_get_friend_list') + try: + cred = self.coolq_api_query('get_credentials') + cookies = cred['cookies'] + csrf_token = cred['csrf_token'] + self.friend_list = get_friend_list_via_qq_show(cookies, csrf_token) + except Exception: + self.logger.warning('Failed to update friend list') if self.friend_list: self.logger.debug('Update friend list completed. Entries: %s', len(self.friend_list)) else: @@ -617,6 +640,14 @@ def get_friend_remark(self, uid): except CoolQAPIFailureException: self.deliver_alert_to_master(self._('Failed to obtain friend remark name')) return '' + for i in range(len(self.friend_list)): # friend group + for j in range(len(self.friend_list[i]['friend'])): + current_user = self.friend_list[i]['friend'][j] + if current_user['uin'] != str(uid): + continue + return current_user['name'] + return None # I don't think you've got such a friend + ''' for i in range(len(self.friend_list)): # friend group for j in range(len(self.friend_list[i]['friends'])): current_user = self.friend_list[i]['friends'][j] @@ -628,6 +659,8 @@ def get_friend_remark(self, uid): else: return current_user['remark'] return None # I don't think you've got such a friend + ''' + def send_efb_group_notice(self, context): context['message_type'] = 'group' diff --git a/efb_qq_slave/Clients/CoolQ/MsgDecorator.py b/efb_qq_slave/Clients/CoolQ/MsgDecorator.py index 3689b4f..e746676 100644 --- a/efb_qq_slave/Clients/CoolQ/MsgDecorator.py +++ b/efb_qq_slave/Clients/CoolQ/MsgDecorator.py @@ -4,6 +4,8 @@ import threading import uuid import base64 +import json +import html import magic from typing import Callable @@ -50,7 +52,7 @@ def __init__(self, instance: CoolQ): self._ = instance._ pass - def qq_image_wrapper(self, data) -> EFBMsg: + def qq_image_wrapper(self, data): efb_msg = EFBMsg() if 'url' not in data: efb_msg.type = MsgType.Text @@ -69,15 +71,15 @@ def qq_image_wrapper(self, data) -> EFBMsg: mime = mime.decode() efb_msg.path = efb_msg.file.name efb_msg.mime = mime - return efb_msg + return [efb_msg] - def qq_record_wrapper(self, data) -> EFBMsg: + def qq_record_wrapper(self, data): efb_msg = EFBMsg() efb_msg.type = MsgType.Unsupported efb_msg.text = self._('[Voice Message] Please check it on your QQ') - return efb_msg + return [efb_msg] - def qq_share_wrapper(self, data) -> EFBMsg: + def qq_share_wrapper(self, data): efb_msg = EFBMsg() efb_msg.type = MsgType.Link efb_msg.text = '' @@ -87,23 +89,23 @@ def qq_share_wrapper(self, data) -> EFBMsg: image='' if 'image' not in data else data['image'], url=data['url'] ) - return efb_msg + return [efb_msg] - def qq_location_wrapper(self, data) -> EFBMsg: + def qq_location_wrapper(self, data): efb_msg = EFBMsg() efb_msg.text = data['content'] efb_msg.attributes = EFBMsgLocationAttribute(longitude=float(data['lon']), latitude=float(data['lat'])) efb_msg.type = MsgType.Location - return efb_msg + return [efb_msg] - def qq_shake_wrapper(self, data) -> EFBMsg: + def qq_shake_wrapper(self, data): efb_msg = EFBMsg() efb_msg.type = MsgType.Text efb_msg.text += self._('[Your friend shakes you!]') - return efb_msg + return [efb_msg] - def qq_contact_wrapper(self, data) -> EFBMsg: + def qq_contact_wrapper(self, data): efb_msg = EFBMsg() efb_msg.type = MsgType.Text uid = data['id'] @@ -113,36 +115,44 @@ def qq_contact_wrapper(self, data) -> EFBMsg: txt = txt.format(uid, contact_type) efb_msg.text = txt efb_msg.type = MsgType.Text - return efb_msg + return [efb_msg] - def qq_bface_wrapper(self, data) -> EFBMsg: + def qq_bface_wrapper(self, data): efb_msg = EFBMsg() efb_msg.type = MsgType.Unsupported efb_msg.text += self._('[Here comes the BigFace Emoji, please check it on your phone]') - return efb_msg + return [efb_msg] - def qq_small_face_wrapper(self, data, merged_msg_id) -> EFBMsg: + def qq_small_face_wrapper(self, data, merged_msg_id): # todo this function's maybe not necessary? pass - def qq_sign_wrapper(self, data) -> EFBMsg: + def qq_sign_wrapper(self, data): location = self._('at {}').format(data['location']) if 'location' in data else self._('at Unknown Place') title = '' if 'title' not in data else (self._('with title {}').format(data['title'])) efb_msg = EFBMsg() efb_msg.type = MsgType.Text efb_msg.text = self._('signed in {location} {title}').format(title=title, location=location) - return efb_msg + return [efb_msg] - def qq_rich_wrapper(self, data: dict) -> EFBMsg: # Buggy, Help needed + def qq_rich_wrapper(self, data: dict): # Buggy, Help needed + efb_messages = list() efb_msg = EFBMsg() efb_msg.type = MsgType.Unsupported efb_msg.text = self._('[Here comes the Rich Text, dumping...] \n') for key, value in data.items(): efb_msg.text += key + ':' + value + '\n' - return efb_msg + efb_messages.append(efb_msg) + # Optimizations for rich messages + # Group Broadcast + _ = self.qq_group_broadcast_wrapper(data) + if _ is not None: + efb_messages.append(_) - def qq_music_wrapper(self, data) -> EFBMsg: + return efb_messages + + def qq_music_wrapper(self, data): efb_msg = EFBMsg() if data['type'] == '163': # Netease Cloud Music efb_msg.type = MsgType.Text @@ -150,8 +160,7 @@ def qq_music_wrapper(self, data) -> EFBMsg: else: efb_msg.type = MsgType.Text efb_msg.text = data['text'] - return efb_msg # todo Port for other music platform - pass + return [efb_msg] # todo Port for other music platform def qq_text_simple_wrapper(self, text: str, ats: dict): # This cute function only accepts string! efb_msg = EFBMsg() @@ -173,7 +182,7 @@ def coolq_code_image_wrapper(self, file, file_path): # there's no need to escape the special characters return '[CQ:image,file=base64://{}]'.format(encoded_string.decode()) - def qq_file_after_wrapper(self, data) -> EFBMsg: + def qq_file_after_wrapper(self, data): efb_msg = EFBMsg() efb_msg.file = data['file'] efb_msg.type = MsgType.File @@ -183,4 +192,69 @@ def qq_file_after_wrapper(self, data) -> EFBMsg: efb_msg.path = efb_msg.file.name efb_msg.mime = mime efb_msg.filename = quote(data['filename']) - return efb_msg + return [efb_msg] + + def qq_group_broadcast_wrapper(self, data): + try: + at_list = {} + content_data = json.loads(data['content']) + text_data = base64.b64decode(content_data['mannounce']['text']).decode("UTF-8") + title_data = base64.b64decode(content_data['mannounce']['title']).decode("UTF-8") + text = "[群公告] 【{title}】\n{text}".format(title=title_data, text=text_data) + + if text == '': + substitution_begin = len(text) + substitution_end = len(text) + len('@all') + 1 + text += '@all ' + else: + substitution_begin = len(text) + 1 + substitution_end = len(text) + len('@all') + 2 + text += ' @all ' + at_list[(substitution_begin, substitution_end)] = EFBChat(self.inst.channel).self() + + if 'pic' in content_data['mannounce']: # Picture Attached + # Assuming there's only one picture + data['url'] = "http://gdynamic.qpic.cn/gdynamic/{}/628".format( + content_data['mannounce']['pic'][0]['url']) + efb_message = self.qq_image_wrapper(data)[0] + efb_message.text = text + efb_message.substitutions = EFBMsgSubstitutions(at_list) + return efb_message + else: + return self.qq_text_simple_wrapper(text, at_list) + except Exception: + return self.qq_group_broadcast_alternative_wrapper(data) + + def qq_group_broadcast_alternative_wrapper(self, data): + try: + at_list = {} + content_data = json.loads(data['content']) + group_id = content_data['mannounce']['gc'] + notice_raw_data = self.inst.coolq_api_query("_get_group_notice", + group_id=group_id) + notice_data = json.loads(notice_raw_data) + title_data = html.unescape(notice_data[0]['msg']['title']) + text_data = html.unescape(notice_data[0]['msg']['text']) + text = "[群公告] 【{title}】\n{text}".format(title=title_data, text=text_data) + + if text == '': + substitution_begin = len(text) + substitution_end = len(text) + len('@all') + 1 + text += '@all ' + else: + substitution_begin = len(text) + 1 + substitution_end = len(text) + len('@all') + 2 + text += ' @all ' + at_list[(substitution_begin, substitution_end)] = EFBChat(self.inst.channel).self() + + if 'pics' in html.unescape(notice_data[0]['msg']): # Picture Attached + # Assuming there's only one picture + data['url'] = "http://gdynamic.qpic.cn/gdynamic/{}/628".format(notice_data[0]['msg']['pics'][0]['id']) + efb_message = self.qq_image_wrapper(data)[0] + efb_message.text = text + efb_message.substitutions = EFBMsgSubstitutions(at_list) + return efb_message + else: + return self.qq_text_simple_wrapper(text, at_list) + except Exception: + return None diff --git a/efb_qq_slave/Clients/CoolQ/Utils.py b/efb_qq_slave/Clients/CoolQ/Utils.py index b75685b..c4f632e 100644 --- a/efb_qq_slave/Clients/CoolQ/Utils.py +++ b/efb_qq_slave/Clients/CoolQ/Utils.py @@ -304,3 +304,13 @@ def download_group_avatar(uid: str): raise EOFError('File downloaded is Empty') file.seek(0) return file + + +def get_friend_list_via_qq_show(cookie: str, csrf_token: str): + # This function won't check before execute, instead all the exceptions will be thrown + cookie_arr = param_spliter(cookie) + url = "http://show.qq.com/cgi-bin/qqshow_user_friendgroup?g_tk={csrf_token}&omode=4" \ + .format(csrf_token=csrf_token) + ret = requests.get(url, cookies=cookie_arr) + data = json.loads(ret.text) + return data['data']['group']