Initial Commit
This commit is contained in:
commit
da15b9bae4
0
README.rst
Normal file
0
README.rst
Normal file
148
efb_qq_plugin_coolq/ChatMgr.py
Normal file
148
efb_qq_plugin_coolq/ChatMgr.py
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
import contextlib
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from ehforwarderbot import Chat
|
||||||
|
from ehforwarderbot.chat import GroupChat, PrivateChat, SystemChat
|
||||||
|
from ehforwarderbot.types import ChatID
|
||||||
|
|
||||||
|
from efb_qq_slave import QQMessengerChannel
|
||||||
|
|
||||||
|
|
||||||
|
class ChatManager:
|
||||||
|
|
||||||
|
def __init__(self, channel: 'QQMessengerChannel'):
|
||||||
|
self.channel: 'QQMessengerChannel' = channel
|
||||||
|
self.logger: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
self.MISSING_GROUP: GroupChat = GroupChat(
|
||||||
|
channel=self.channel,
|
||||||
|
uid=ChatID("__error_group__"),
|
||||||
|
name="Group Missing"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.MISSING_CHAT: PrivateChat = PrivateChat(
|
||||||
|
channel=self.channel,
|
||||||
|
uid=ChatID("__error_chat__"),
|
||||||
|
name="Chat Missing"
|
||||||
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
def build_efb_chat_as_user(self, uid, is_chat, in_group=None, is_discuss=False):
|
||||||
|
efb_chat = EFBChat(self.channel)
|
||||||
|
efb_chat.chat_uid = 'user' + str(uid)
|
||||||
|
i: dict = self.channel.QQClient.get_stranger_info(uid)
|
||||||
|
efb_chat.chat_name = i['nickname']
|
||||||
|
efb_chat.chat_alias = None
|
||||||
|
efb_chat.chat_type = ChatType.User
|
||||||
|
efb_chat.is_chat = is_chat
|
||||||
|
efb_chat.vendor_specific = {'is_anonymous': False}
|
||||||
|
if in_group is not None:
|
||||||
|
efb_chat.group = self.build_efb_chat_as_group(in_group, is_discuss)
|
||||||
|
return efb_chat
|
||||||
|
|
||||||
|
def build_efb_chat_as_group(self, uid, discuss=False):
|
||||||
|
efb_chat = EFBChat(self.channel)
|
||||||
|
if not discuss:
|
||||||
|
efb_chat.chat_uid = 'group' + str(uid)
|
||||||
|
i = self.channel.QQClient.get_group_info(uid)
|
||||||
|
efb_chat.chat_name = i['group_name']
|
||||||
|
efb_chat.chat_type = ChatType.Group
|
||||||
|
efb_chat.vendor_specific = {'is_discuss': False}
|
||||||
|
# todo Add user to efb_chat.member
|
||||||
|
else:
|
||||||
|
efb_chat.chat_uid = 'discuss' + str(uid)
|
||||||
|
efb_chat.chat_name = 'Discuss Group' # todo Find a way to distinguish from different discuss group
|
||||||
|
efb_chat.chat_type = ChatType.Group
|
||||||
|
efb_chat.vendor_specific = {'is_discuss': True}
|
||||||
|
return efb_chat
|
||||||
|
|
||||||
|
def build_efb_chat_as_anonymous_user(self, nickname, flag, anonymous_id, group_id, is_discuss):
|
||||||
|
efb_chat = EFBChat(self.channel)
|
||||||
|
efb_chat.chat_uid = flag
|
||||||
|
efb_chat.chat_name = nickname
|
||||||
|
efb_chat.chat_type = ChatType.User
|
||||||
|
efb_chat.is_chat = False
|
||||||
|
efb_chat.vendor_specific = {'is_anonymous': True,
|
||||||
|
'anonymous_id': anonymous_id}
|
||||||
|
efb_chat.group = self.build_efb_chat_as_group(group_id, is_discuss)
|
||||||
|
return efb_chat
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def build_efb_chat_as_private(self, context):
|
||||||
|
uid = context['user_id']
|
||||||
|
if 'sender' not in context or 'nickname' not in context['sender']:
|
||||||
|
i: dict = self.channel.QQClient.get_stranger_info(uid)
|
||||||
|
chat_name = ""
|
||||||
|
if i:
|
||||||
|
chat_name = i['nickname']
|
||||||
|
else:
|
||||||
|
chat_name = context['sender']['nickname']
|
||||||
|
efb_chat = PrivateChat(channel=self.channel,
|
||||||
|
uid='private' + '_' + str(uid),
|
||||||
|
name=str(chat_name),
|
||||||
|
alias=None if 'alias' not in context else str(context['alias']))
|
||||||
|
return efb_chat
|
||||||
|
|
||||||
|
def build_or_get_efb_member(self, chat: Chat, context):
|
||||||
|
member_uid = context['user_id']
|
||||||
|
with contextlib.suppress(KeyError):
|
||||||
|
return chat.get_member(str(member_uid))
|
||||||
|
chat_name = ''
|
||||||
|
if 'nickname' not in context:
|
||||||
|
i: dict = self.channel.QQClient.get_stranger_info(member_uid)
|
||||||
|
chat_name = ""
|
||||||
|
if i:
|
||||||
|
chat_name = i['nickname']
|
||||||
|
else:
|
||||||
|
chat_name = context['nickname']
|
||||||
|
return chat.add_member(name=str(chat_name),
|
||||||
|
alias=None if 'alias' not in context else str(context['alias']),
|
||||||
|
uid=str(member_uid))
|
||||||
|
|
||||||
|
def build_efb_chat_as_group(self, context, update_member=False): # Should be cached
|
||||||
|
is_discuss = False if context['message_type'] == 'group' else True
|
||||||
|
chat_uid = context['discuss_id'] if is_discuss else context['group_id']
|
||||||
|
efb_chat = GroupChat(
|
||||||
|
channel=self.channel,
|
||||||
|
uid=str(chat_uid)
|
||||||
|
)
|
||||||
|
if not is_discuss:
|
||||||
|
efb_chat.uid = 'group' + '_' + str(chat_uid)
|
||||||
|
i = self.channel.QQClient.get_group_info(chat_uid)
|
||||||
|
if i is not None:
|
||||||
|
efb_chat.name = str(i['group_name']) if 'group_name' not in context else str(context['group_name'])
|
||||||
|
else:
|
||||||
|
efb_chat.name = str(chat_uid)
|
||||||
|
efb_chat.vendor_specific = {'is_discuss': False}
|
||||||
|
if update_member:
|
||||||
|
members = self.channel.QQClient.get_group_member_list(chat_uid, False)
|
||||||
|
if members:
|
||||||
|
for member in members:
|
||||||
|
efb_chat.add_member(name=str(member['card']),
|
||||||
|
alias=str(member['nickname']),
|
||||||
|
uid=str(member['user_id']))
|
||||||
|
else:
|
||||||
|
efb_chat.uid = 'discuss' + '_' + str(chat_uid)
|
||||||
|
efb_chat.name = 'Discuss Group' + '_' + str(chat_uid)
|
||||||
|
# todo Find a way to distinguish from different discuss group
|
||||||
|
efb_chat.vendor_specific = {'is_discuss': True}
|
||||||
|
return efb_chat
|
||||||
|
|
||||||
|
def build_efb_chat_as_anonymous_user(self, chat: Chat, context):
|
||||||
|
anonymous_data = context['anonymous']
|
||||||
|
member_uid = 'anonymous' + '_' + anonymous_data['flag']
|
||||||
|
with contextlib.suppress(KeyError):
|
||||||
|
return chat.get_member(member_uid)
|
||||||
|
chat_name = '[Anonymous] ' + anonymous_data['name']
|
||||||
|
return chat.add_member(name=str(chat_name),
|
||||||
|
alias=None if 'alias' not in context else str(context['alias']),
|
||||||
|
uid=str(member_uid),
|
||||||
|
vendor_specific={'is_anonymous': True,
|
||||||
|
'anonymous_id': anonymous_data['id']})
|
||||||
|
|
||||||
|
def build_efb_chat_as_system_user(self, context): # System user only!
|
||||||
|
return SystemChat(channel=self.channel,
|
||||||
|
name=str(context['event_description']),
|
||||||
|
uid=ChatID("__{context[uid_prefix]}__".format(context=context)))
|
1059
efb_qq_plugin_coolq/CoolQ.py
Normal file
1059
efb_qq_plugin_coolq/CoolQ.py
Normal file
File diff suppressed because it is too large
Load Diff
22
efb_qq_plugin_coolq/Exceptions.py
Normal file
22
efb_qq_plugin_coolq/Exceptions.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
class CoolQClientException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CoolQAPIFailureException(CoolQClientException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CoolQCookieExpiredException(CoolQAPIFailureException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CoolQOfflineException(CoolQClientException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CoolQDisconnectedException(CoolQClientException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CoolQUnknownException(CoolQClientException):
|
||||||
|
pass
|
251
efb_qq_plugin_coolq/MsgDecorator.py
Normal file
251
efb_qq_plugin_coolq/MsgDecorator.py
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import html
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
import magic
|
||||||
|
from ehforwarderbot import Message, MsgType, Chat
|
||||||
|
from ehforwarderbot.message import LocationAttribute, LinkAttribute, Substitutions
|
||||||
|
|
||||||
|
from . import CoolQ
|
||||||
|
from .Utils import cq_get_image, download_voice
|
||||||
|
|
||||||
|
|
||||||
|
class QQMsgProcessor:
|
||||||
|
inst: CoolQ
|
||||||
|
logger: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def __init__(self, instance: CoolQ):
|
||||||
|
self.inst = instance
|
||||||
|
self._ = instance._
|
||||||
|
pass
|
||||||
|
|
||||||
|
def qq_image_wrapper(self, data, chat: Chat = None):
|
||||||
|
efb_msg = Message()
|
||||||
|
if 'url' not in data:
|
||||||
|
efb_msg.type = MsgType.Text
|
||||||
|
efb_msg.text = self._('[Image Source missing]')
|
||||||
|
return [efb_msg]
|
||||||
|
|
||||||
|
efb_msg.file = cq_get_image(data['url'])
|
||||||
|
if efb_msg.file is None:
|
||||||
|
efb_msg.type = MsgType.Text
|
||||||
|
efb_msg.text = self._('[Download image failed, please check on your QQ client]')
|
||||||
|
return [efb_msg]
|
||||||
|
|
||||||
|
efb_msg.type = MsgType.Image
|
||||||
|
mime = magic.from_file(efb_msg.file.name, mime=True)
|
||||||
|
if isinstance(mime, bytes):
|
||||||
|
mime = mime.decode()
|
||||||
|
efb_msg.filename = data['file'] if 'file' in data else efb_msg.file.name
|
||||||
|
efb_msg.filename += '.' + str(mime).split('/')[1]
|
||||||
|
efb_msg.path = efb_msg.file.name
|
||||||
|
efb_msg.mime = mime
|
||||||
|
if "gif" in mime:
|
||||||
|
efb_msg.type = MsgType.Animation
|
||||||
|
return [efb_msg]
|
||||||
|
|
||||||
|
def qq_record_wrapper(self, data, chat: Chat = None): # Experimental!
|
||||||
|
efb_msg = Message()
|
||||||
|
try:
|
||||||
|
transformed_file = self.inst.coolq_api_query("get_record", file=data['file'], out_format='mp3')
|
||||||
|
efb_msg.type = MsgType.Audio
|
||||||
|
efb_msg.file = download_voice(transformed_file['file'],
|
||||||
|
self.inst.client_config['api_root'].rstrip("/"),
|
||||||
|
self.inst.client_config['access_token'])
|
||||||
|
mime = magic.from_file(efb_msg.file.name, mime=True)
|
||||||
|
if isinstance(mime, bytes):
|
||||||
|
mime = mime.decode()
|
||||||
|
efb_msg.path = efb_msg.file.name
|
||||||
|
efb_msg.mime = mime
|
||||||
|
except Exception:
|
||||||
|
efb_msg.type = MsgType.Unsupported
|
||||||
|
efb_msg.text = self._('[Voice Message] Please check it on your QQ')
|
||||||
|
logging.getLogger(__name__).exception("Failed to download voice")
|
||||||
|
return [efb_msg]
|
||||||
|
|
||||||
|
def qq_share_wrapper(self, data, chat: Chat = None):
|
||||||
|
efb_msg = Message(
|
||||||
|
type=MsgType.Link,
|
||||||
|
text='',
|
||||||
|
attributes=LinkAttribute(
|
||||||
|
title='' if 'title' not in data else data['title'],
|
||||||
|
description='' if 'content' not in data else data['content'],
|
||||||
|
image='' if 'image' not in data else data['image'],
|
||||||
|
url=data['url']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return [efb_msg]
|
||||||
|
|
||||||
|
def qq_location_wrapper(self, data, chat: Chat = None):
|
||||||
|
efb_msg = Message(
|
||||||
|
text=data['content'],
|
||||||
|
type=MsgType.Location,
|
||||||
|
attributes=LocationAttribute(longitude=float(data['lon']),
|
||||||
|
latitude=float(data['lat']))
|
||||||
|
)
|
||||||
|
return [efb_msg]
|
||||||
|
|
||||||
|
def qq_shake_wrapper(self, data, chat: Chat = None):
|
||||||
|
efb_msg = Message(
|
||||||
|
type=MsgType.Text,
|
||||||
|
text=self._('[Your friend shakes you!]')
|
||||||
|
)
|
||||||
|
return [efb_msg]
|
||||||
|
|
||||||
|
def qq_contact_wrapper(self, data, chat: Chat = None):
|
||||||
|
uid = data['id']
|
||||||
|
contact_type = data['type']
|
||||||
|
efb_msg = Message(
|
||||||
|
type=MsgType.Text,
|
||||||
|
text=self._("Chat Recommendation Received\nID: {}\nType: {}").format(uid, contact_type)
|
||||||
|
)
|
||||||
|
return [efb_msg]
|
||||||
|
|
||||||
|
def qq_bface_wrapper(self, data, chat: Chat = None):
|
||||||
|
efb_msg = Message(
|
||||||
|
type=MsgType.Unsupported,
|
||||||
|
text=self._('[Here comes the BigFace Emoji, please check it on your phone]')
|
||||||
|
)
|
||||||
|
return [efb_msg]
|
||||||
|
|
||||||
|
def qq_small_face_wrapper(self, data, chat: Chat = None):
|
||||||
|
# todo this function's maybe not necessary?
|
||||||
|
pass
|
||||||
|
|
||||||
|
def qq_sign_wrapper(self, data, chat: Chat = None):
|
||||||
|
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 = Message(
|
||||||
|
type=MsgType.Text,
|
||||||
|
text=self._('signed in {location} {title}').format(title=title,
|
||||||
|
location=location)
|
||||||
|
)
|
||||||
|
return [efb_msg]
|
||||||
|
|
||||||
|
def qq_rich_wrapper(self, data: dict, chat: Chat = None): # Buggy, Help needed
|
||||||
|
efb_messages = list()
|
||||||
|
efb_msg = Message(
|
||||||
|
type=MsgType.Unsupported,
|
||||||
|
text=self._('[Here comes the Rich Text, dumping...] \n')
|
||||||
|
)
|
||||||
|
for key, value in data.items():
|
||||||
|
efb_msg.text += key + ': ' + value + '\n'
|
||||||
|
efb_messages.append(efb_msg)
|
||||||
|
# Optimizations for rich messages
|
||||||
|
# Group Broadcast
|
||||||
|
_ = self.qq_group_broadcast_wrapper(data, chat)
|
||||||
|
if _ is not None:
|
||||||
|
efb_messages.append(_)
|
||||||
|
|
||||||
|
return efb_messages
|
||||||
|
|
||||||
|
def qq_music_wrapper(self, data, chat: Chat = None):
|
||||||
|
efb_msg = Message()
|
||||||
|
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
|
||||||
|
|
||||||
|
def qq_text_simple_wrapper(self, text: str, ats: dict): # This cute function only accepts string!
|
||||||
|
efb_msg = Message()
|
||||||
|
efb_msg.type = MsgType.Text
|
||||||
|
efb_msg.text = text
|
||||||
|
if ats: # This is used to replace specific text with @blahblah
|
||||||
|
# And Milkice really requires a brain check
|
||||||
|
efb_msg.substitutions = Substitutions(ats)
|
||||||
|
return efb_msg
|
||||||
|
|
||||||
|
def coolq_code_at_wrapper(self, uid):
|
||||||
|
return '[CQ:at,qq={}]'.format(uid)
|
||||||
|
|
||||||
|
def coolq_code_image_wrapper(self, file, file_path):
|
||||||
|
if file.closed:
|
||||||
|
file = open(file.name)
|
||||||
|
encoded_string = base64.b64encode(file.read())
|
||||||
|
# Since base64 doesn't contain characters which isn't allowed in CQ Code,
|
||||||
|
# there's no need to escape the special characters
|
||||||
|
return '[CQ:image,file=base64://{}]'.format(encoded_string.decode())
|
||||||
|
|
||||||
|
def coolq_voice_image_wrapper(self, file, file_path):
|
||||||
|
if file.closed:
|
||||||
|
file = open(file.name)
|
||||||
|
encoded_string = base64.b64encode(file.read())
|
||||||
|
# Since base64 doesn't contain characters which isn't allowed in CQ Code,
|
||||||
|
# there's no need to escape the special characters
|
||||||
|
return '[CQ:record,file=base64://{}]'.format(encoded_string.decode())
|
||||||
|
|
||||||
|
def qq_file_after_wrapper(self, data):
|
||||||
|
efb_msg = Message()
|
||||||
|
efb_msg.file = data['file']
|
||||||
|
efb_msg.type = MsgType.File
|
||||||
|
mime = magic.from_file(efb_msg.file.name, mime=True)
|
||||||
|
if isinstance(mime, bytes):
|
||||||
|
mime = mime.decode()
|
||||||
|
efb_msg.path = efb_msg.file.name
|
||||||
|
efb_msg.mime = mime
|
||||||
|
efb_msg.filename = quote(data['filename'])
|
||||||
|
return efb_msg
|
||||||
|
|
||||||
|
def qq_group_broadcast_wrapper(self, data, chat: Chat = None):
|
||||||
|
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)
|
||||||
|
|
||||||
|
substitution_begin = len(text) + 1
|
||||||
|
substitution_end = len(text) + len('@all') + 2
|
||||||
|
text += ' @all '
|
||||||
|
|
||||||
|
at_list[(substitution_begin, substitution_end)] = chat.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 = Substitutions(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, chat: Chat = None):
|
||||||
|
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)
|
||||||
|
|
||||||
|
substitution_begin = len(text) + 1
|
||||||
|
substitution_end = len(text) + len('@all') + 2
|
||||||
|
text += ' @all '
|
||||||
|
|
||||||
|
at_list[(substitution_begin, substitution_end)] = chat.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 = Substitutions(at_list)
|
||||||
|
return [efb_message]
|
||||||
|
else:
|
||||||
|
return self.qq_text_simple_wrapper(text, at_list)
|
||||||
|
except Exception:
|
||||||
|
return None
|
830
efb_qq_plugin_coolq/Utils.py
Normal file
830
efb_qq_plugin_coolq/Utils.py
Normal file
@ -0,0 +1,830 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import ntpath
|
||||||
|
import tempfile
|
||||||
|
import urllib.request
|
||||||
|
from gettext import translation
|
||||||
|
from typing import *
|
||||||
|
from urllib.error import URLError, HTTPError, ContentTooShortError
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from ehforwarderbot import Message, coordinator
|
||||||
|
from pkg_resources import resource_filename
|
||||||
|
|
||||||
|
from .Exceptions import CoolQUnknownException
|
||||||
|
|
||||||
|
qq_emoji_list = { # created by JogleLew and jqqqqqqqqqq, optimized based on Tim's emoji support
|
||||||
|
0: '😮',
|
||||||
|
1: '😣',
|
||||||
|
2: '😍',
|
||||||
|
3: '😳',
|
||||||
|
4: '😎',
|
||||||
|
5: '😭',
|
||||||
|
6: '☺️',
|
||||||
|
7: '😷',
|
||||||
|
8: '😴',
|
||||||
|
9: '😭',
|
||||||
|
10: '😰',
|
||||||
|
11: '😡',
|
||||||
|
12: '😝',
|
||||||
|
13: '😃',
|
||||||
|
14: '🙂',
|
||||||
|
15: '🙁',
|
||||||
|
16: '🤓',
|
||||||
|
17: '[Empty]',
|
||||||
|
18: '😤',
|
||||||
|
19: '😨',
|
||||||
|
20: '😏',
|
||||||
|
21: '😊',
|
||||||
|
22: '🙄',
|
||||||
|
23: '😕',
|
||||||
|
24: '🤤',
|
||||||
|
25: '😪',
|
||||||
|
26: '😨',
|
||||||
|
27: '😓',
|
||||||
|
28: '😬',
|
||||||
|
29: '🤑',
|
||||||
|
30: '✊',
|
||||||
|
31: '😤',
|
||||||
|
32: '🤔',
|
||||||
|
33: '🤐',
|
||||||
|
34: '😵',
|
||||||
|
35: '😩',
|
||||||
|
36: '💣',
|
||||||
|
37: '💀',
|
||||||
|
38: '🔨',
|
||||||
|
39: '👋',
|
||||||
|
40: '[Empty]',
|
||||||
|
41: '😮',
|
||||||
|
42: '💑',
|
||||||
|
43: '🕺',
|
||||||
|
44: '[Empty]',
|
||||||
|
45: '[Empty]',
|
||||||
|
46: '🐷',
|
||||||
|
47: '[Empty]',
|
||||||
|
48: '[Empty]',
|
||||||
|
49: '🤷',
|
||||||
|
50: '[Empty]',
|
||||||
|
51: '[Empty]',
|
||||||
|
52: '[Empty]',
|
||||||
|
53: '🎂',
|
||||||
|
54: '⚡',
|
||||||
|
55: '💣',
|
||||||
|
56: '🔪',
|
||||||
|
57: '⚽️',
|
||||||
|
58: '[Empty]',
|
||||||
|
59: '💩',
|
||||||
|
60: '☕️',
|
||||||
|
61: '🍚',
|
||||||
|
62: '[Empty]',
|
||||||
|
63: '🌹',
|
||||||
|
64: '🥀',
|
||||||
|
65: '[Empty]',
|
||||||
|
66: '❤️',
|
||||||
|
67: '💔️',
|
||||||
|
68: '[Empty]',
|
||||||
|
69: '🎁',
|
||||||
|
70: '[Empty]',
|
||||||
|
71: '[Empty]',
|
||||||
|
72: '[Empty]',
|
||||||
|
73: '[Empty]',
|
||||||
|
74: '🌞️',
|
||||||
|
75: '🌃',
|
||||||
|
76: '👍',
|
||||||
|
77: '👎',
|
||||||
|
78: '🤝',
|
||||||
|
79: '✌️',
|
||||||
|
80: '[Empty]',
|
||||||
|
81: '[Empty]',
|
||||||
|
82: '[Empty]',
|
||||||
|
83: '[Empty]',
|
||||||
|
84: '[Empty]',
|
||||||
|
85: '🥰',
|
||||||
|
86: '[怄火]',
|
||||||
|
87: '[Empty]',
|
||||||
|
88: '[Empty]',
|
||||||
|
89: '🍉',
|
||||||
|
90: '[Empty]',
|
||||||
|
91: '[Empty]',
|
||||||
|
92: '[Empty]',
|
||||||
|
93: '[Empty]',
|
||||||
|
94: '[Empty]',
|
||||||
|
95: '[Empty]',
|
||||||
|
96: '😅',
|
||||||
|
97: '[擦汗]',
|
||||||
|
98: '[抠鼻]',
|
||||||
|
99: '👏',
|
||||||
|
100: '[糗大了]',
|
||||||
|
101: '😏',
|
||||||
|
102: '😏',
|
||||||
|
103: '😏',
|
||||||
|
104: '🥱',
|
||||||
|
105: '[鄙视]',
|
||||||
|
106: '😭',
|
||||||
|
107: '😭',
|
||||||
|
108: '[阴险]',
|
||||||
|
109: '😚',
|
||||||
|
110: '🙀',
|
||||||
|
111: '[可怜]',
|
||||||
|
112: '🔪',
|
||||||
|
113: '🍺',
|
||||||
|
114: '🏀',
|
||||||
|
115: '🏓',
|
||||||
|
116: '❤️',
|
||||||
|
117: '🐞',
|
||||||
|
118: '[抱拳]',
|
||||||
|
119: '[勾引]',
|
||||||
|
120: '✊',
|
||||||
|
121: '[差劲]',
|
||||||
|
122: '🤟',
|
||||||
|
123: '🚫',
|
||||||
|
124: '👌',
|
||||||
|
125: '[转圈]',
|
||||||
|
126: '[磕头]',
|
||||||
|
127: '[回头]',
|
||||||
|
128: '[跳绳]',
|
||||||
|
129: '👋',
|
||||||
|
130: '[激动]',
|
||||||
|
131: '[街舞]',
|
||||||
|
132: '😘',
|
||||||
|
133: '[左太极]',
|
||||||
|
134: '[右太极]',
|
||||||
|
135: '[Empty]',
|
||||||
|
136: '[双喜]',
|
||||||
|
137: '🧨',
|
||||||
|
138: '🏮',
|
||||||
|
139: '💰',
|
||||||
|
140: '[K歌]',
|
||||||
|
141: '🛍️',
|
||||||
|
142: '📧',
|
||||||
|
143: '[帅]',
|
||||||
|
144: '👏',
|
||||||
|
145: '🙏',
|
||||||
|
146: '[爆筋]',
|
||||||
|
147: '🍭',
|
||||||
|
148: '🍼',
|
||||||
|
149: '[下面]',
|
||||||
|
150: '🍌',
|
||||||
|
151: '🛩',
|
||||||
|
152: '🚗',
|
||||||
|
153: '🚅',
|
||||||
|
154: '[车厢]',
|
||||||
|
155: '[高铁右车头]',
|
||||||
|
156: '🌥',
|
||||||
|
157: '下雨',
|
||||||
|
158: '💵',
|
||||||
|
159: '🐼',
|
||||||
|
160: '💡',
|
||||||
|
161: '[风车]',
|
||||||
|
162: '⏰',
|
||||||
|
163: '🌂',
|
||||||
|
164: '[彩球]',
|
||||||
|
165: '💍',
|
||||||
|
166: '🛋',
|
||||||
|
167: '[纸巾]',
|
||||||
|
168: '💊',
|
||||||
|
169: '🔫',
|
||||||
|
170: '🐸',
|
||||||
|
171: '🍵',
|
||||||
|
172: '[眨眼睛]',
|
||||||
|
173: '😭',
|
||||||
|
174: '[无奈]',
|
||||||
|
175: '[卖萌]',
|
||||||
|
176: '[小纠结]',
|
||||||
|
177: '[喷血]',
|
||||||
|
178: '[斜眼笑]',
|
||||||
|
179: '[doge]',
|
||||||
|
180: '[惊喜]',
|
||||||
|
181: '[骚扰]',
|
||||||
|
182: '😹',
|
||||||
|
183: '[我最美]',
|
||||||
|
184: '🦀',
|
||||||
|
185: '[羊驼]',
|
||||||
|
186: '[Empty]',
|
||||||
|
187: '👻',
|
||||||
|
188: '🥚',
|
||||||
|
189: '[Empty]',
|
||||||
|
190: '🌼',
|
||||||
|
191: '[Empty]',
|
||||||
|
192: '🧧',
|
||||||
|
193: '😄',
|
||||||
|
194: '😞',
|
||||||
|
195: '[Empty]',
|
||||||
|
196: '[Empty]',
|
||||||
|
197: '[冷漠]',
|
||||||
|
198: '[呃]',
|
||||||
|
199: '👍',
|
||||||
|
200: '👋',
|
||||||
|
201: '👍',
|
||||||
|
202: '[无聊]',
|
||||||
|
203: '[托脸]',
|
||||||
|
204: '[吃]',
|
||||||
|
205: '💐',
|
||||||
|
206: '😨',
|
||||||
|
207: '[花痴]',
|
||||||
|
208: '[小样儿]',
|
||||||
|
209: '[Empty]',
|
||||||
|
210: '😭',
|
||||||
|
211: '[我不看]',
|
||||||
|
212: '[托腮]',
|
||||||
|
213: '[Empty]',
|
||||||
|
214: '😙',
|
||||||
|
215: '[糊脸]',
|
||||||
|
216: '[拍头]',
|
||||||
|
217: '[扯一扯]',
|
||||||
|
218: '[舔一舔]',
|
||||||
|
219: '[蹭一蹭]',
|
||||||
|
220: '[拽炸天]',
|
||||||
|
221: '[顶呱呱]',
|
||||||
|
222: '🤗',
|
||||||
|
223: '[暴击]',
|
||||||
|
224: '🔫',
|
||||||
|
225: '[撩一撩]',
|
||||||
|
226: '[拍桌]',
|
||||||
|
227: '👏',
|
||||||
|
228: '[恭喜]',
|
||||||
|
229: '🍻',
|
||||||
|
230: '[嘲讽]',
|
||||||
|
231: '[哼]',
|
||||||
|
232: '[佛系]',
|
||||||
|
233: '[掐一掐]',
|
||||||
|
234: '😮',
|
||||||
|
235: '[颤抖]',
|
||||||
|
236: '[啃头]',
|
||||||
|
237: '[偷看]',
|
||||||
|
238: '[扇脸]',
|
||||||
|
239: '[原谅]',
|
||||||
|
240: '[喷脸]',
|
||||||
|
241: '🎂',
|
||||||
|
242: '[Empty]',
|
||||||
|
243: '[Empty]',
|
||||||
|
244: '[Empty]',
|
||||||
|
245: '[Empty]',
|
||||||
|
246: '[Empty]',
|
||||||
|
247: '[Empty]',
|
||||||
|
248: '[Empty]',
|
||||||
|
249: '[Empty]',
|
||||||
|
250: '[Empty]',
|
||||||
|
251: '[Empty]',
|
||||||
|
252: '[Empty]',
|
||||||
|
253: '[Empty]',
|
||||||
|
254: '[Empty]',
|
||||||
|
255: '[Empty]',
|
||||||
|
}
|
||||||
|
|
||||||
|
# original text copied from Tim
|
||||||
|
qq_emoji_text_list = {
|
||||||
|
0: '[惊讶]',
|
||||||
|
1: '[撇嘴]',
|
||||||
|
2: '[色]',
|
||||||
|
3: '[发呆]',
|
||||||
|
4: '[得意]',
|
||||||
|
5: '[流泪]',
|
||||||
|
6: '[害羞]',
|
||||||
|
7: '[闭嘴]',
|
||||||
|
8: '[睡]',
|
||||||
|
9: '[大哭]',
|
||||||
|
10: '[尴尬]',
|
||||||
|
11: '[发怒]',
|
||||||
|
12: '[调皮]',
|
||||||
|
13: '[呲牙]',
|
||||||
|
14: '[微笑]',
|
||||||
|
15: '[难过]',
|
||||||
|
16: '[酷]',
|
||||||
|
17: '[Empty]',
|
||||||
|
18: '[抓狂]',
|
||||||
|
19: '[吐]',
|
||||||
|
20: '[偷笑]',
|
||||||
|
21: '[可爱]',
|
||||||
|
22: '[白眼]',
|
||||||
|
23: '[傲慢]',
|
||||||
|
24: '[饥饿]',
|
||||||
|
25: '[困]',
|
||||||
|
26: '[惊恐]',
|
||||||
|
27: '[流汗]',
|
||||||
|
28: '[憨笑]',
|
||||||
|
29: '[悠闲]',
|
||||||
|
30: '[奋斗]',
|
||||||
|
31: '[咒骂]',
|
||||||
|
32: '[疑问]',
|
||||||
|
33: '[嘘]',
|
||||||
|
34: '[晕]',
|
||||||
|
35: '[折磨]',
|
||||||
|
36: '[衰]',
|
||||||
|
37: '[骷髅]',
|
||||||
|
38: '[敲打]',
|
||||||
|
39: '[再见]',
|
||||||
|
40: '[Empty]',
|
||||||
|
41: '[发抖]',
|
||||||
|
42: '[爱情]',
|
||||||
|
43: '[跳跳]',
|
||||||
|
44: '[Empty]',
|
||||||
|
45: '[Empty]',
|
||||||
|
46: '[猪头]',
|
||||||
|
47: '[Empty]',
|
||||||
|
48: '[Empty]',
|
||||||
|
49: '[拥抱]',
|
||||||
|
50: '[Empty]',
|
||||||
|
51: '[Empty]',
|
||||||
|
52: '[Empty]',
|
||||||
|
53: '[蛋糕]',
|
||||||
|
54: '[闪电]',
|
||||||
|
55: '[炸弹]',
|
||||||
|
56: '[刀]',
|
||||||
|
57: '[足球]',
|
||||||
|
58: '[Empty]',
|
||||||
|
59: '[便便]',
|
||||||
|
60: '[咖啡]',
|
||||||
|
61: '[饭]',
|
||||||
|
62: '[Empty]',
|
||||||
|
63: '[玫瑰]',
|
||||||
|
64: '[凋谢]',
|
||||||
|
65: '[Empty]',
|
||||||
|
66: '[爱心]',
|
||||||
|
67: '[心碎]',
|
||||||
|
68: '[Empty]',
|
||||||
|
69: '[礼物]',
|
||||||
|
70: '[Empty]',
|
||||||
|
71: '[Empty]',
|
||||||
|
72: '[Empty]',
|
||||||
|
73: '[Empty]',
|
||||||
|
74: '[太阳]',
|
||||||
|
75: '[月亮]',
|
||||||
|
76: '[赞]',
|
||||||
|
77: '[踩]',
|
||||||
|
78: '[握手]',
|
||||||
|
79: '[胜利]',
|
||||||
|
80: '[Empty]',
|
||||||
|
81: '[Empty]',
|
||||||
|
82: '[Empty]',
|
||||||
|
83: '[Empty]',
|
||||||
|
84: '[Empty]',
|
||||||
|
85: '[飞吻]',
|
||||||
|
86: '[怄火]',
|
||||||
|
87: '[Empty]',
|
||||||
|
88: '[Empty]',
|
||||||
|
89: '[西瓜]',
|
||||||
|
90: '[Empty]',
|
||||||
|
91: '[Empty]',
|
||||||
|
92: '[Empty]',
|
||||||
|
93: '[Empty]',
|
||||||
|
94: '[Empty]',
|
||||||
|
95: '[Empty]',
|
||||||
|
96: '[冷汗]',
|
||||||
|
97: '[擦汗]',
|
||||||
|
98: '[抠鼻]',
|
||||||
|
99: '[鼓掌]',
|
||||||
|
100: '[糗大了]',
|
||||||
|
101: '[坏笑]',
|
||||||
|
102: '[左哼哼]',
|
||||||
|
103: '[右哼哼]',
|
||||||
|
104: '[哈欠]',
|
||||||
|
105: '[鄙视]',
|
||||||
|
106: '[委屈]',
|
||||||
|
107: '[快哭了]',
|
||||||
|
108: '[阴险]',
|
||||||
|
109: '[亲亲]',
|
||||||
|
110: '[吓]',
|
||||||
|
111: '[可怜]',
|
||||||
|
112: '[菜刀]',
|
||||||
|
113: '[啤酒]',
|
||||||
|
114: '[篮球]',
|
||||||
|
115: '[乒乓]',
|
||||||
|
116: '[示爱]',
|
||||||
|
117: '[瓢虫]',
|
||||||
|
118: '[抱拳]',
|
||||||
|
119: '[勾引]',
|
||||||
|
120: '[拳头]',
|
||||||
|
121: '[差劲]',
|
||||||
|
122: '[爱你]',
|
||||||
|
123: '[NO]',
|
||||||
|
124: '[OK]',
|
||||||
|
125: '[转圈]',
|
||||||
|
126: '[磕头]',
|
||||||
|
127: '[回头]',
|
||||||
|
128: '[跳绳]',
|
||||||
|
129: '[挥手]',
|
||||||
|
130: '[激动]',
|
||||||
|
131: '[街舞]',
|
||||||
|
132: '[献吻]',
|
||||||
|
133: '[左太极]',
|
||||||
|
134: '[右太极]',
|
||||||
|
135: '[Empty]',
|
||||||
|
136: '[双喜]',
|
||||||
|
137: '[鞭炮]',
|
||||||
|
138: '[灯笼]',
|
||||||
|
139: '[发财]',
|
||||||
|
140: '[K歌]',
|
||||||
|
141: '[购物]',
|
||||||
|
142: '[邮件]',
|
||||||
|
143: '[帅]',
|
||||||
|
144: '[喝彩]',
|
||||||
|
145: '[祈祷]',
|
||||||
|
146: '[爆筋]',
|
||||||
|
147: '[棒棒糖]',
|
||||||
|
148: '[喝奶]',
|
||||||
|
149: '[下面]',
|
||||||
|
150: '[香蕉]',
|
||||||
|
151: '[飞机]',
|
||||||
|
152: '[开车]',
|
||||||
|
153: '[高铁左车头]',
|
||||||
|
154: '[车厢]',
|
||||||
|
155: '[高铁右车头]',
|
||||||
|
156: '[多云]',
|
||||||
|
157: '[下雨]',
|
||||||
|
158: '[钞票]',
|
||||||
|
159: '[熊猫]',
|
||||||
|
160: '[灯泡]',
|
||||||
|
161: '[风车]',
|
||||||
|
162: '[闹钟]',
|
||||||
|
163: '[打伞]',
|
||||||
|
164: '[彩球]',
|
||||||
|
165: '[钻戒]',
|
||||||
|
166: '[沙发]',
|
||||||
|
167: '[纸巾]',
|
||||||
|
168: '[药]',
|
||||||
|
169: '[手枪]',
|
||||||
|
170: '[青蛙]',
|
||||||
|
171: '[茶]',
|
||||||
|
172: '[眨眼睛]',
|
||||||
|
173: '[泪奔]',
|
||||||
|
174: '[无奈]',
|
||||||
|
175: '[卖萌]',
|
||||||
|
176: '[小纠结]',
|
||||||
|
177: '[喷血]',
|
||||||
|
178: '[斜眼笑]',
|
||||||
|
179: '[doge]',
|
||||||
|
180: '[惊喜]',
|
||||||
|
181: '[骚扰]',
|
||||||
|
182: '[笑哭]',
|
||||||
|
183: '[我最美]',
|
||||||
|
184: '[河蟹]',
|
||||||
|
185: '[羊驼]',
|
||||||
|
186: '[Empty]',
|
||||||
|
187: '[幽灵]',
|
||||||
|
188: '[蛋]',
|
||||||
|
189: '[Empty]',
|
||||||
|
190: '[菊花]',
|
||||||
|
191: '[Empty]',
|
||||||
|
192: '[红包]',
|
||||||
|
193: '[大笑]',
|
||||||
|
194: '[不开心]',
|
||||||
|
195: '[Empty]',
|
||||||
|
196: '[Empty]',
|
||||||
|
197: '[冷漠]',
|
||||||
|
198: '[呃]',
|
||||||
|
199: '[好棒]',
|
||||||
|
200: '[拜托]',
|
||||||
|
201: '[点赞]',
|
||||||
|
202: '[无聊]',
|
||||||
|
203: '[托脸]',
|
||||||
|
204: '[吃]',
|
||||||
|
205: '[送花]',
|
||||||
|
206: '[害怕]',
|
||||||
|
207: '[花痴]',
|
||||||
|
208: '[小样儿]',
|
||||||
|
209: '[Empty]',
|
||||||
|
210: '[飙泪]',
|
||||||
|
211: '[我不看]',
|
||||||
|
212: '[托腮]',
|
||||||
|
213: '[Empty]',
|
||||||
|
214: '[啵啵]',
|
||||||
|
215: '[糊脸]',
|
||||||
|
216: '[拍头]',
|
||||||
|
217: '[扯一扯]',
|
||||||
|
218: '[舔一舔]',
|
||||||
|
219: '[蹭一蹭]',
|
||||||
|
220: '[拽炸天]',
|
||||||
|
221: '[顶呱呱]',
|
||||||
|
222: '[抱抱]',
|
||||||
|
223: '[暴击]',
|
||||||
|
224: '[开枪]',
|
||||||
|
225: '[撩一撩]',
|
||||||
|
226: '[拍桌]',
|
||||||
|
227: '[拍手]',
|
||||||
|
228: '[恭喜]',
|
||||||
|
229: '[干杯]',
|
||||||
|
230: '[嘲讽]',
|
||||||
|
231: '[哼]',
|
||||||
|
232: '[佛系]',
|
||||||
|
233: '[掐一掐]',
|
||||||
|
234: '[惊呆]',
|
||||||
|
235: '[颤抖]',
|
||||||
|
236: '[啃头]',
|
||||||
|
237: '[偷看]',
|
||||||
|
238: '[扇脸]',
|
||||||
|
239: '[原谅]',
|
||||||
|
240: '[喷脸]',
|
||||||
|
241: '[生日快乐]',
|
||||||
|
242: '[Empty]',
|
||||||
|
243: '[Empty]',
|
||||||
|
244: '[Empty]',
|
||||||
|
245: '[Empty]',
|
||||||
|
246: '[Empty]',
|
||||||
|
247: '[Empty]',
|
||||||
|
248: '[Empty]',
|
||||||
|
249: '[Empty]',
|
||||||
|
250: '[Empty]',
|
||||||
|
251: '[Empty]',
|
||||||
|
252: '[Empty]',
|
||||||
|
253: '[Empty]',
|
||||||
|
254: '[Empty]',
|
||||||
|
255: '[Empty]',
|
||||||
|
}
|
||||||
|
|
||||||
|
qq_sface_list = {
|
||||||
|
1: '[拜拜]',
|
||||||
|
2: '[鄙视]',
|
||||||
|
3: '[菜刀]',
|
||||||
|
4: '[沧桑]',
|
||||||
|
5: '[馋了]',
|
||||||
|
6: '[吃惊]',
|
||||||
|
7: '[微笑]',
|
||||||
|
8: '[得意]',
|
||||||
|
9: '[嘚瑟]',
|
||||||
|
10: '[瞪眼]',
|
||||||
|
11: '[震惊]',
|
||||||
|
12: '[鼓掌]',
|
||||||
|
13: '[害羞]',
|
||||||
|
14: '[好的]',
|
||||||
|
15: '[惊呆了]',
|
||||||
|
16: '[静静看]',
|
||||||
|
17: '[可爱]',
|
||||||
|
18: '[困]',
|
||||||
|
19: '[脸红]',
|
||||||
|
20: '[你懂的]',
|
||||||
|
21: '[期待]',
|
||||||
|
22: '[亲亲]',
|
||||||
|
23: '[伤心]',
|
||||||
|
24: '[生气]',
|
||||||
|
25: '[摇摆]',
|
||||||
|
26: '[帅]',
|
||||||
|
27: '[思考]',
|
||||||
|
28: '[震惊哭]',
|
||||||
|
29: '[痛心]',
|
||||||
|
30: '[偷笑]',
|
||||||
|
31: '[挖鼻孔]',
|
||||||
|
32: '[抓狂]',
|
||||||
|
33: '[笑着哭]',
|
||||||
|
34: '[无语]',
|
||||||
|
35: '[捂脸]',
|
||||||
|
36: '[喜欢]',
|
||||||
|
37: '[笑哭]',
|
||||||
|
38: '[疑惑]',
|
||||||
|
39: '[赞]',
|
||||||
|
40: '[眨眼]'
|
||||||
|
}
|
||||||
|
translator = translation("efb_qq_slave",
|
||||||
|
resource_filename('efb_qq_slave', 'Clients/CoolQ/locale'),
|
||||||
|
fallback=True)
|
||||||
|
_ = translator.gettext
|
||||||
|
ngettext = translator.ngettext
|
||||||
|
|
||||||
|
|
||||||
|
def cq_get_image(image_link: str) -> tempfile: # Download image from QQ
|
||||||
|
file = tempfile.NamedTemporaryFile()
|
||||||
|
try:
|
||||||
|
urllib.request.urlretrieve(image_link, file.name)
|
||||||
|
except (URLError, HTTPError, ContentTooShortError) as e:
|
||||||
|
logging.getLogger(__name__).warning('Image download failed.')
|
||||||
|
logging.getLogger(__name__).warning(str(e))
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
if file.seek(0, 2) <= 0:
|
||||||
|
raise EOFError('File downloaded is Empty')
|
||||||
|
file.seek(0)
|
||||||
|
return file
|
||||||
|
|
||||||
|
|
||||||
|
def async_send_messages_to_master(msg: Message):
|
||||||
|
coordinator.send_message(msg)
|
||||||
|
if msg.file:
|
||||||
|
msg.file.close()
|
||||||
|
|
||||||
|
|
||||||
|
def process_quote_text(text, max_length): # Simple wrapper for processing quoted text
|
||||||
|
qt_txt = "%s" % text
|
||||||
|
if max_length > 0:
|
||||||
|
tgt_text = qt_txt[:max_length]
|
||||||
|
if len(qt_txt) >= max_length:
|
||||||
|
tgt_text += "…"
|
||||||
|
tgt_text = "「%s」" % tgt_text
|
||||||
|
elif max_length < 0:
|
||||||
|
tgt_text = "「%s」" % qt_txt
|
||||||
|
else:
|
||||||
|
tgt_text = ""
|
||||||
|
return tgt_text
|
||||||
|
|
||||||
|
|
||||||
|
def coolq_text_encode(text: str): # Escape special characters for CQ Code text
|
||||||
|
expr = (('&', '&'), ('[', '['), (']', ']'))
|
||||||
|
for r in expr:
|
||||||
|
text = text.replace(*r)
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def coolq_para_encode(text: str): # Escape special characters for CQ Code parameters
|
||||||
|
expr = (('&', '&'), ('[', '['), (']', ']'), (',', ','))
|
||||||
|
for r in expr:
|
||||||
|
text = text.replace(*r)
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def upload_image_smms(file, path, email, password): # Upload image to sm.ms and return the link
|
||||||
|
UPLOAD_URL_TOKEN = 'https://sm.ms/api/v2/token'
|
||||||
|
UPLOAD_URL_IMAGE = 'https://sm.ms/api/v2/upload'
|
||||||
|
UPLOAD_LOGIN = {'username': email,
|
||||||
|
'password': password}
|
||||||
|
UPLOAD_PARAMS = {'format': 'json', 'ssl': True}
|
||||||
|
resp = requests.post(UPLOAD_URL_TOKEN, params=UPLOAD_LOGIN)
|
||||||
|
status = json.loads(resp.text)
|
||||||
|
if status['code'] == 'success':
|
||||||
|
token = status['data']['token']
|
||||||
|
UPLOAD_HEADER = {'Authorization': token}
|
||||||
|
else:
|
||||||
|
logging.getLogger(__name__).warning(
|
||||||
|
'WARNING: {}'.format(status['msg']))
|
||||||
|
raise CoolQUnknownException(status['msg'])
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
files = {'smfile': f.read()}
|
||||||
|
resp = requests.post(UPLOAD_URL_IMAGE, files=files, headers=UPLOAD_HEADER,
|
||||||
|
params=UPLOAD_PARAMS)
|
||||||
|
status = json.loads(resp.text)
|
||||||
|
if status['code'] == 'success':
|
||||||
|
logging.getLogger(__name__).debug('INFO: upload success! url at {}'.format(status['data']['url']))
|
||||||
|
return status['data']
|
||||||
|
else:
|
||||||
|
logging.getLogger(__name__).warning('WARNING: {}'.format(status['msg']))
|
||||||
|
raise CoolQUnknownException(status['msg'])
|
||||||
|
|
||||||
|
|
||||||
|
def upload_image_vim_cn(file, path): # Upload image to img.vim-cn.com and return the link
|
||||||
|
UPLOAD_URL = 'https://img.vim-cn.com/'
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
files = {'name': f.read()}
|
||||||
|
resp = requests.post(UPLOAD_URL, files=files)
|
||||||
|
if resp.status_code != 200:
|
||||||
|
raise CoolQUnknownException("Failed to upload images to vim-cn.com")
|
||||||
|
return resp.text
|
||||||
|
|
||||||
|
|
||||||
|
def upload_image_sogou(file, path): # Upload image to pic.sogou.com and return the link
|
||||||
|
UPLOAD_URL = 'https://pic.sogou.com/pic/upload_pic.jsp'
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
files = {'pic_path': f.read()}
|
||||||
|
resp = requests.post(UPLOAD_URL, files=files)
|
||||||
|
if resp.status_code != 200:
|
||||||
|
raise CoolQUnknownException("Failed to upload images to sogou.com")
|
||||||
|
return "https" + resp.text[4:] # Replace http with https
|
||||||
|
|
||||||
|
|
||||||
|
def upload_image_mi(file, path): # Upload image to shopapi.io.mi.com and return the link
|
||||||
|
UPLOAD_URL = 'https://shopapi.io.mi.com/homemanage/shop/uploadpic'
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
files = {'pic': (ntpath.basename(path), f.read(), "image/jpeg")}
|
||||||
|
resp = requests.post(UPLOAD_URL, files=files)
|
||||||
|
if resp.status_code != 200:
|
||||||
|
raise CoolQUnknownException("Failed to upload images to mi.com")
|
||||||
|
status = json.loads(resp.text)
|
||||||
|
print(status)
|
||||||
|
if status['message'] != "ok":
|
||||||
|
raise CoolQUnknownException("Failed to upload images to mi.com")
|
||||||
|
return status['result']
|
||||||
|
|
||||||
|
|
||||||
|
def param_spliter(str_param):
|
||||||
|
params = str_param.split(";")
|
||||||
|
param = {}
|
||||||
|
for _k in params:
|
||||||
|
key, value = _k.strip().split("=")
|
||||||
|
param[key] = value
|
||||||
|
return param
|
||||||
|
|
||||||
|
|
||||||
|
def download_file_from_qzone(cookie: str, csrf_token: str, uin, group_id, file_id, filename, file_size):
|
||||||
|
cookie_arr = param_spliter(cookie)
|
||||||
|
url = "http://qun.qzone.qq.com/cgi-bin/group_share_get_downurl?uin=" + str(uin) + "&pa=/104/" + \
|
||||||
|
str(file_id) + "&groupid=" + str(group_id) + "&bussinessid=0&charset=utf-8&g_tk=" + str(csrf_token) + "&r=888"
|
||||||
|
ret = requests.get(url, cookies=cookie_arr)
|
||||||
|
data = json.loads(ret.text.split("(")[1].split(")")[0])['data']
|
||||||
|
cookie += "; FTN5K=" + str(data['cookie'])
|
||||||
|
download_url = data['url']
|
||||||
|
download_url += "/" + quote(filename)
|
||||||
|
if file_size >= 50*1024*1024: # File size is bigger than 50MiB
|
||||||
|
return _("File is too big to be downloaded")
|
||||||
|
file = tempfile.NamedTemporaryFile()
|
||||||
|
try:
|
||||||
|
opener = urllib.request.build_opener()
|
||||||
|
opener.addheaders.append(('Cookie', cookie))
|
||||||
|
urllib.request.install_opener(opener)
|
||||||
|
urllib.request.urlretrieve(download_url, file.name)
|
||||||
|
except (URLError, HTTPError, ContentTooShortError) as e:
|
||||||
|
logging.getLogger(__name__).warning("Error occurs when downloading files: " + str(e))
|
||||||
|
return _("Error occurs when downloading files: ") + str(e)
|
||||||
|
else:
|
||||||
|
if file.seek(0, 2) <= 0:
|
||||||
|
raise EOFError('File downloaded is Empty')
|
||||||
|
file.seek(0)
|
||||||
|
return file
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
opener = urllib.request.build_opener()
|
||||||
|
opener.addheaders.append(('Cookie', cookie))
|
||||||
|
with opener.open(download_url) as response, tempfile.NamedTemporaryFile() as f:
|
||||||
|
shutil.copyfileobj(response, f)
|
||||||
|
if f.seek(0, 2) <= 0:
|
||||||
|
raise EOFError('File downloaded is Empty')
|
||||||
|
f.seek(0)
|
||||||
|
return f
|
||||||
|
except Exception as e:
|
||||||
|
logging.getLogger(__name__).warning("Error occurs when downloading files" + str(e))
|
||||||
|
return url
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def download_user_avatar(uid: str):
|
||||||
|
file = tempfile.NamedTemporaryFile()
|
||||||
|
url = "https://q1.qlogo.cn/g?b=qq&nk={}&s=0".format(uid)
|
||||||
|
try:
|
||||||
|
opener = urllib.request.build_opener()
|
||||||
|
urllib.request.install_opener(opener)
|
||||||
|
urllib.request.urlretrieve(url, file.name)
|
||||||
|
except (URLError, HTTPError, ContentTooShortError) as e:
|
||||||
|
logging.getLogger(__name__).warning("Error occurs when downloading files: " + str(e))
|
||||||
|
return _("Error occurs when downloading files: ") + str(e)
|
||||||
|
if file.seek(0, 2) <= 0:
|
||||||
|
raise EOFError('File downloaded is Empty')
|
||||||
|
file.seek(0)
|
||||||
|
return file
|
||||||
|
|
||||||
|
|
||||||
|
def download_group_avatar(uid: str):
|
||||||
|
file = tempfile.NamedTemporaryFile()
|
||||||
|
url = "https://p.qlogo.cn/gh/{}/{}/".format(uid, uid)
|
||||||
|
try:
|
||||||
|
opener = urllib.request.build_opener()
|
||||||
|
urllib.request.install_opener(opener)
|
||||||
|
urllib.request.urlretrieve(url, file.name)
|
||||||
|
except (URLError, HTTPError, ContentTooShortError) as e:
|
||||||
|
logging.getLogger(__name__).warning("Error occurs when downloading files: " + str(e))
|
||||||
|
return _("Error occurs when downloading files: ") + str(e)
|
||||||
|
if file.seek(0, 2) <= 0:
|
||||||
|
raise EOFError('File downloaded is Empty')
|
||||||
|
file.seek(0)
|
||||||
|
return file
|
||||||
|
|
||||||
|
|
||||||
|
def get_friend_group_via_qq_show(cookie: str, csrf_token: str) -> Dict[str, str]:
|
||||||
|
# This function won't check before execute, instead all the exceptions will be thrown
|
||||||
|
cookie_arr = param_spliter(cookie)
|
||||||
|
url = "https://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)
|
||||||
|
friend_group = {}
|
||||||
|
for i in range(len(data['data']['group'])): # friend group
|
||||||
|
for j in range(len(data['data']['group'][i]['friend'])):
|
||||||
|
current_user = str(data['data']['group'][i]['friend'][j]['uin'])
|
||||||
|
current_group = data['data']['group'][i]['name']
|
||||||
|
friend_group[current_user] = current_group
|
||||||
|
return friend_group
|
||||||
|
|
||||||
|
|
||||||
|
def download_voice(filename: str, api_root: str, access_token: str):
|
||||||
|
file = tempfile.NamedTemporaryFile()
|
||||||
|
url = '{url}/data/record/{file}'.format(url=api_root, file=filename)
|
||||||
|
try:
|
||||||
|
opener = urllib.request.build_opener()
|
||||||
|
opener.addheaders = [("Authorization", "Bearer {at}".format(at=access_token))]
|
||||||
|
|
||||||
|
urllib.request.install_opener(opener)
|
||||||
|
urllib.request.urlretrieve(url, file.name)
|
||||||
|
except (URLError, HTTPError, ContentTooShortError) as e:
|
||||||
|
logging.getLogger(__name__).warning("Error occurs when downloading files: " + str(e))
|
||||||
|
return _("Error occurs when downloading files: ") + str(e)
|
||||||
|
if file.seek(0, 2) <= 0:
|
||||||
|
raise EOFError('File downloaded is Empty')
|
||||||
|
file.seek(0)
|
||||||
|
return file
|
||||||
|
|
||||||
|
|
||||||
|
def get_stranger_info_via_qzone(uin: str):
|
||||||
|
pattern = re.compile(r"\((.*)\)")
|
||||||
|
resp = requests.get("https://users.qzone.qq.com/fcg-bin/cgi_get_portrait.fcg?uins={id}".format(id=uin))
|
||||||
|
# Assume that this API is always available
|
||||||
|
data = pattern.findall(resp.text)
|
||||||
|
if not data:
|
||||||
|
return ""
|
||||||
|
try:
|
||||||
|
data = json.loads(data[0])
|
||||||
|
ret = {
|
||||||
|
"uin": uin,
|
||||||
|
"nickname": data[uin][6],
|
||||||
|
"avatar_url": data[uin][0]
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
except:
|
||||||
|
return ""
|
1
efb_qq_plugin_coolq/__init__.py
Normal file
1
efb_qq_plugin_coolq/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from . import CoolQ
|
3
efb_qq_plugin_coolq/__version__.py
Normal file
3
efb_qq_plugin_coolq/__version__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
__version__ = '2.0.0a0'
|
45
setup.py
Normal file
45
setup.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import sys
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
if sys.version_info < (3, 6):
|
||||||
|
raise Exception("Python 3.6 or higher is required. Your version is %s." % sys.version)
|
||||||
|
|
||||||
|
__version__ = ""
|
||||||
|
exec(open('efb_qq_plugin_coolq/__version__.py').read())
|
||||||
|
|
||||||
|
long_description = open('README.rst').read()
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='efb-qq-plugin-coolq',
|
||||||
|
packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]),
|
||||||
|
version=__version__,
|
||||||
|
description='EQS plugin for CoolQ API Compatible Client.',
|
||||||
|
long_description=long_description,
|
||||||
|
include_package_data=True,
|
||||||
|
author='Milkice',
|
||||||
|
author_email='milkice@milkice.me',
|
||||||
|
url='https://github.com/milkice233/efb-qq-plugin-coolq',
|
||||||
|
license='GPLv3',
|
||||||
|
python_requires='>=3.6',
|
||||||
|
keywords=['ehforwarderbot', 'EH Forwarder Bot', 'EH Forwarder Bot Slave Channel',
|
||||||
|
'qq', 'chatbot', 'EQS', 'CoolQ'],
|
||||||
|
classifiers=[
|
||||||
|
"Development Status :: 3 - Alpha",
|
||||||
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"Intended Audience :: End Users/Desktop",
|
||||||
|
"Programming Language :: Python :: 3 :: Only",
|
||||||
|
"Programming Language :: Python :: 3.6",
|
||||||
|
"Programming Language :: Python :: 3.7",
|
||||||
|
"Topic :: Communications :: Chat",
|
||||||
|
"Topic :: Utilities"
|
||||||
|
],
|
||||||
|
install_requires=[
|
||||||
|
"efb-qq-slave", "ehforwarderbot",
|
||||||
|
"PyYaml",
|
||||||
|
'requests', 'python-magic', 'Pillow', 'cqhttp>=1.3.0', 'cherrypy>=18.5.0'
|
||||||
|
],
|
||||||
|
entry_points={
|
||||||
|
'ehforwarderbot.qq.plugin': 'CoolQ = efb_qq_plugin_coolq:CoolQ'
|
||||||
|
}
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user