Skip to content

Commit

Permalink
Merge pull request #31 from milkice233/enh-group_broadcast
Browse files Browse the repository at this point in the history
Implement Group Broadcast & New API for retrieving friend list
  • Loading branch information
milkice233 authored Jul 9, 2019
2 parents c3adfab + 1a48135 commit 956d5fd
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 28 deletions.
41 changes: 37 additions & 4 deletions efb_qq_slave/Clients/CoolQ/CoolQ.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import threading
import time
import uuid
import requests
from typing import IO, Any, Dict, Optional, List, Tuple

import cqhttp
Expand All @@ -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

Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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]
Expand All @@ -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):
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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]
Expand All @@ -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'
Expand Down
122 changes: 98 additions & 24 deletions efb_qq_slave/Clients/CoolQ/MsgDecorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import threading
import uuid
import base64
import json
import html

import magic
from typing import Callable
Expand Down Expand Up @@ -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
Expand All @@ -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 = ''
Expand All @@ -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']
Expand All @@ -113,45 +115,52 @@ 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
efb_msg.text = 'https://music.163.com/#/song?id=' + data['id']
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()
Expand All @@ -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
Expand All @@ -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
10 changes: 10 additions & 0 deletions efb_qq_slave/Clients/CoolQ/Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']

0 comments on commit 956d5fd

Please sign in to comment.