Add pre-commit

This commit is contained in:
XYenon 2022-02-05 20:58:20 +08:00 committed by XYenon
parent ccf10a1f90
commit 93b6bc887d
9 changed files with 1388 additions and 1156 deletions

2
.flake8 Normal file
View File

@ -0,0 +1,2 @@
[flake8]
max-line-length = 120

17
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,17 @@
repos:
- repo: https://github.com/psf/black
rev: 22.1.0
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
rev: 4.0.1
hooks:
- id: flake8
additional_dependencies:
- flake8-bugbear
- repo: https://github.com/PyCQA/isort
rev: 5.10.1
hooks:
- id: isort

View File

@ -8,95 +8,100 @@ from ehforwarderbot.types import ChatID
class ChatManager:
def __init__(self, channel: 'QQMessengerChannel'):
self.channel: 'QQMessengerChannel' = channel
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"
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"
channel=self.channel, uid=ChatID("__error_chat__"), name="Chat Missing"
)
def build_efb_chat_as_private(self, context):
uid = context['user_id']
if 'sender' not in context or 'nickname' not in context['sender']:
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']
chat_name = i["nickname"]
else:
chat_name = context['sender']['nickname']
efb_chat = PrivateChat(channel=self.channel,
uid='private' + '_' + str(uid),
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']))
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']
member_uid = context["user_id"]
with contextlib.suppress(KeyError):
return chat.get_member(str(member_uid))
chat_name = ''
if 'nickname' not in context:
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']
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))
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)
)
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)
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'])
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}
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']))
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)
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}
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']
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']),
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']})
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)))
return SystemChat(
channel=self.channel,
name=str(context["event_description"]),
uid=ChatID("__{context[uid_prefix]}__".format(context=context)),
)

File diff suppressed because it is too large Load Diff

View File

@ -2,13 +2,14 @@ import base64
import html
import json
import logging
import sys
import magic
from ehforwarderbot import Message, MsgType, Chat
from ehforwarderbot.message import LocationAttribute, LinkAttribute, Substitutions
from ehforwarderbot import Chat, Message, MsgType
from ehforwarderbot.message import LinkAttribute, LocationAttribute, Substitutions
from . import GoCQHttp
from .Utils import cq_get_image, download_voice, download_file
from .Utils import cq_get_image, download_file, download_voice
class QQMsgProcessor:
@ -22,23 +23,23 @@ class QQMsgProcessor:
def qq_image_wrapper(self, data, chat: Chat = None):
efb_msg = Message()
if 'url' not in data:
if "url" not in data:
efb_msg.type = MsgType.Text
efb_msg.text = self._('[Image Source missing]')
efb_msg.text = self._("[Image Source missing]")
return [efb_msg]
efb_msg.file = cq_get_image(data['url'])
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]')
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.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:
@ -48,11 +49,13 @@ class QQMsgProcessor:
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')
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'])
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()
@ -60,52 +63,48 @@ class QQMsgProcessor:
efb_msg.mime = mime
except Exception:
efb_msg.type = MsgType.Unsupported
efb_msg.text = self._('[Voice Message] Please check it on your QQ')
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='',
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']
)
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'],
text=data["content"],
type=MsgType.Location,
attributes=LocationAttribute(longitude=float(data['lon']),
latitude=float(data['lat']))
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!]')
)
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']
uid = data["id"]
contact_type = data["type"]
efb_msg = Message(
type=MsgType.Text,
text=self._("Chat Recommendation Received\nID: {}\nType: {}").format(uid, contact_type)
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]')
text=self._("[Here comes the BigFace Emoji, please check it on your phone]"),
)
return [efb_msg]
@ -114,12 +113,11 @@ class QQMsgProcessor:
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']))
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)
text=self._("signed in {location} {title}").format(title=title, location=location),
)
return [efb_msg]
@ -127,10 +125,10 @@ class QQMsgProcessor:
efb_messages = list()
efb_msg = Message(
type=MsgType.Unsupported,
text=self._('[Here comes the Rich Text, dumping...] \n')
text=self._("[Here comes the Rich Text, dumping...] \n"),
)
for key, value in data.items():
efb_msg.text += key + ': ' + value + '\n'
efb_msg.text += key + ": " + value + "\n"
efb_messages.append(efb_msg)
# Optimizations for rich messages
# Group Broadcast
@ -142,12 +140,12 @@ class QQMsgProcessor:
def qq_music_wrapper(self, data, chat: Chat = None):
efb_msg = Message()
if data['type'] == '163': # Netease Cloud Music
if data["type"] == "163": # Netease Cloud Music
efb_msg.type = MsgType.Text
efb_msg.text = 'https://music.163.com/#/song?id=' + data['id']
efb_msg.text = "https://music.163.com/#/song?id=" + data["id"]
else:
efb_msg.type = MsgType.Text
efb_msg.text = data['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!
@ -160,7 +158,7 @@ class QQMsgProcessor:
return efb_msg
def coolq_code_at_wrapper(self, uid):
return '[CQ:at,qq={}]'.format(uid)
return "[CQ:at,qq={}]".format(uid)
def coolq_code_image_wrapper(self, file, file_path):
if file.closed:
@ -168,7 +166,7 @@ class QQMsgProcessor:
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())
return "[CQ:image,file=base64://{}]".format(encoded_string.decode())
def coolq_voice_image_wrapper(self, file, file_path):
if file.closed:
@ -176,38 +174,39 @@ class QQMsgProcessor:
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())
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.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 = data['filename']
efb_msg.filename = 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")
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 '
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
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'])
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)
@ -220,24 +219,23 @@ class QQMsgProcessor:
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)
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'])
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 '
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
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'])
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)
@ -250,66 +248,75 @@ class QQMsgProcessor:
def qq_xml_wrapper(self, data, chat: Chat = None):
efb_msg = Message()
efb_msg.type = MsgType.Text
efb_msg.text = data['data']
efb_msg.text = data["data"]
return [efb_msg]
def qq_json_wrapper(self, data, chat: Chat = None):
efb_msg = Message()
efb_msg.type = MsgType.Text
efb_msg.text = data['data']
efb_msg.text = data["data"]
try:
# In general, data['data'] is a JSON string
dict_data = json.loads(efb_msg.text)
if type(dict_data) != dict or 'app' not in dict_data:
if type(dict_data) != dict or "app" not in dict_data:
return [efb_msg]
# Group of announcement
if dict_data['app'] == 'com.tencent.mannounce':
meta_mannounce = dict_data['meta']['mannounce']
efb_msg.text = "[{prompt}]\n\n{text}".format(prompt=str(base64.b64decode(meta_mannounce['title']), 'UTF-8'), text=str(base64.b64decode(meta_mannounce['text']), 'UTF-8'))
if dict_data["app"] == "com.tencent.mannounce":
meta_mannounce = dict_data["meta"]["mannounce"]
efb_msg.text = "[{prompt}]\n\n{text}".format(
prompt=str(base64.b64decode(meta_mannounce["title"]), "UTF-8"),
text=str(base64.b64decode(meta_mannounce["text"]), "UTF-8"),
)
# Watch, listen and play together
elif dict_data['app'] == 'com.tencent.together':
meta_invite = dict_data['meta']['invite']
efb_msg.text = "[{prompt}]\n\n{text}\n\n{cover}".format(prompt=meta_invite['title'], text=meta_invite['summary'], cover=meta_invite['cover'])
elif dict_data["app"] == "com.tencent.together":
meta_invite = dict_data["meta"]["invite"]
efb_msg.text = "[{prompt}]\n\n{text}\n\n{cover}".format(
prompt=meta_invite["title"],
text=meta_invite["summary"],
cover=meta_invite["cover"],
)
# QQ vip card
elif dict_data['app'] == 'com.tencent.qqvip_singlepic':
elif dict_data["app"] == "com.tencent.qqvip_singlepic":
efb_msg.text = efb_msg.text
# Tencent mini App (01 unknown)
elif dict_data['app'] == 'com.tencent.miniapp_01':
meta_detail1 = dict_data['meta']['detail_1']
url = meta_detail1['qqdocurl'] if 'qqdocurl' in meta_detail1 else meta_detail1['url']
efb_msg.text = "{prompt}\n\n{desc}\n\n{url}\n\n{preview}".format(prompt=dict_data['prompt'], desc=meta_detail1['desc'], url=url, preview=meta_detail1['preview'])
elif dict_data["app"] == "com.tencent.miniapp_01":
meta_detail1 = dict_data["meta"]["detail_1"]
url = meta_detail1["qqdocurl"] if "qqdocurl" in meta_detail1 else meta_detail1["url"]
efb_msg.text = "{prompt}\n\n{desc}\n\n{url}\n\n{preview}".format(
prompt=dict_data["prompt"],
desc=meta_detail1["desc"],
url=url,
preview=meta_detail1["preview"],
)
# Shared third-party Apps
elif dict_data['app'] == 'com.tencent.structmsg':
meta_view = dict_data['meta'][dict_data['view']]
efb_msg.text = "{prompt}\n\n{desc}\n\n{url}\n\n{preview}".format(prompt=dict_data['prompt'], desc=meta_view['desc'], url=meta_view['jumpUrl'], preview=meta_view['preview'])
elif dict_data["app"] == "com.tencent.structmsg":
meta_view = dict_data["meta"][dict_data["view"]]
efb_msg.text = "{prompt}\n\n{desc}\n\n{url}\n\n{preview}".format(
prompt=dict_data["prompt"],
desc=meta_view["desc"],
url=meta_view["jumpUrl"],
preview=meta_view["preview"],
)
except:
except Exception:
self.logger.error(f"json_wrapper_info: {data}\nexc_info:{sys.exc_info()[0]}")
return [efb_msg]
def qq_video_wrapper(self, data, chat: Chat = None):
res = download_file(data['url'])
res = download_file(data["url"])
mime = magic.from_file(res.name, mime=True)
if isinstance(mime, bytes):
mime = mime.decode()
efb_msg = Message(
type=MsgType.Video,
file=res,
filename=res.name,
mime=mime
)
efb_msg = Message(type=MsgType.Video, file=res, filename=res.name, mime=mime)
return [efb_msg]
def qq_unsupported_wrapper(self, data, chat: Chat = None):
efb_msg = Message(
type=MsgType.Unsupported,
text=data
)
efb_msg = Message(type=MsgType.Unsupported, text=data)
return [efb_msg]

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,3 @@
from . import GoCQHttp
from . import GoCQHttp # noqa: F401
__version__ = '2.0.9'
__version__ = "2.0.9"

106
pdm.lock
View File

@ -8,6 +8,12 @@ name = "certifi"
version = "2021.10.8"
summary = "Python package for providing Mozilla's CA Bundle."
[[package]]
name = "cfgv"
version = "3.3.1"
requires_python = ">=3.6.1"
summary = "Validate configuration and produce human readable error messages."
[[package]]
name = "charset-normalizer"
version = "2.0.11"
@ -70,6 +76,11 @@ dependencies = [
"requests",
]
[[package]]
name = "distlib"
version = "0.3.4"
summary = "Distribution utilities"
[[package]]
name = "efb-qq-slave"
version = "2.0.1.dev0"
@ -99,6 +110,12 @@ dependencies = [
"typing-extensions",
]
[[package]]
name = "filelock"
version = "3.4.2"
requires_python = ">=3.7"
summary = "A platform independent file lock."
[[package]]
name = "flask"
version = "2.0.2"
@ -111,6 +128,12 @@ dependencies = [
"itsdangerous>=2.0",
]
[[package]]
name = "identify"
version = "2.4.8"
requires_python = ">=3.7"
summary = "File identification library for Python"
[[package]]
name = "idna"
version = "3.3"
@ -208,12 +231,23 @@ version = "8.12.0"
requires_python = ">=3.5"
summary = "More routines for operating on iterables, beyond itertools"
[[package]]
name = "nodeenv"
version = "1.6.0"
summary = "Node.js virtual environment builder"
[[package]]
name = "pillow"
version = "9.0.1"
requires_python = ">=3.7"
summary = "Python Imaging Library (Fork)"
[[package]]
name = "platformdirs"
version = "2.4.1"
requires_python = ">=3.7"
summary = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
[[package]]
name = "portend"
version = "3.1.0"
@ -223,6 +257,21 @@ dependencies = [
"tempora>=1.8",
]
[[package]]
name = "pre-commit"
version = "2.17.0"
requires_python = ">=3.6.1"
summary = "A framework for managing and maintaining multi-language pre-commit hooks."
dependencies = [
"cfgv>=2.0.0",
"identify>=1.0.0",
"importlib-metadata; python_version < \"3.8\"",
"nodeenv>=0.11.1",
"pyyaml>=5.1",
"toml",
"virtualenv>=20.0.8",
]
[[package]]
name = "python-magic"
version = "0.4.25"
@ -294,6 +343,12 @@ dependencies = [
"pytz",
]
[[package]]
name = "toml"
version = "0.10.2"
requires_python = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
summary = "Python Library for Tom's Obvious, Minimal Language"
[[package]]
name = "typing-extensions"
version = "4.0.1"
@ -306,6 +361,19 @@ version = "1.26.8"
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
summary = "HTTP library with thread-safe connection pooling, file post, and more."
[[package]]
name = "virtualenv"
version = "20.13.0"
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
summary = "Virtual Python Environment builder"
dependencies = [
"distlib<1,>=0.3.1",
"filelock<4,>=3.2",
"importlib-metadata>=0.12; python_version < \"3.8\"",
"platformdirs<3,>=2",
"six<2,>=1.9.0",
]
[[package]]
name = "werkzeug"
version = "2.0.2"
@ -328,7 +396,7 @@ summary = "Backport of pathlib-compatible object wrapper for zip files"
[metadata]
lock_version = "3.1"
content_hash = "sha256:0e2a42ddc2fd648d8001d7be1ebfedbae88d34a2a78de0d941a23637dd7199dd"
content_hash = "sha256:63656839bd77b534cd7236e11ac80dcbb103f86659970673c7bba62c02f393c3"
[metadata.files]
"bullet 2.2.0" = [
@ -339,6 +407,10 @@ content_hash = "sha256:0e2a42ddc2fd648d8001d7be1ebfedbae88d34a2a78de0d941a23637d
{file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
{file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
]
"cfgv 3.3.1" = [
{file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
{file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
]
"charset-normalizer 2.0.11" = [
{file = "charset_normalizer-2.0.11-py3-none-any.whl", hash = "sha256:2842d8f5e82a1f6aa437380934d5e1cd4fcf2003b06fed6940769c164a480a45"},
{file = "charset-normalizer-2.0.11.tar.gz", hash = "sha256:98398a9d69ee80548c762ba991a4728bfc3836768ed226b3945908d1a688371c"},
@ -366,14 +438,26 @@ content_hash = "sha256:0e2a42ddc2fd648d8001d7be1ebfedbae88d34a2a78de0d941a23637d
"cqhttp 1.3.1" = [
{file = "cqhttp-1.3.1.tar.gz", hash = "sha256:4cb0dae03872162df395ef49f3bb2ec69501cc0d686193dfb1b413097b062821"},
]
"distlib 0.3.4" = [
{file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"},
{file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"},
]
"ehforwarderbot 2.1.1" = [
{file = "ehforwarderbot-2.1.1-py3-none-any.whl", hash = "sha256:33e02015cc7dabde9d7e719a6486ccedd681e33246ab746d5ed6de4ec9b96dac"},
{file = "ehforwarderbot-2.1.1.tar.gz", hash = "sha256:414c3de4e9ad151d3ad38d37d89f0b626a5fa7fb04d9d30ff152b5230d34099b"},
]
"filelock 3.4.2" = [
{file = "filelock-3.4.2-py3-none-any.whl", hash = "sha256:cf0fc6a2f8d26bd900f19bf33915ca70ba4dd8c56903eeb14e1e7a2fd7590146"},
{file = "filelock-3.4.2.tar.gz", hash = "sha256:38b4f4c989f9d06d44524df1b24bd19e167d851f19b50bf3e3559952dddc5b80"},
]
"flask 2.0.2" = [
{file = "Flask-2.0.2-py3-none-any.whl", hash = "sha256:cb90f62f1d8e4dc4621f52106613488b5ba826b2e1e10a33eac92f723093ab6a"},
{file = "Flask-2.0.2.tar.gz", hash = "sha256:7b2fb8e934ddd50731893bdcdb00fc8c0315916f9fcd50d22c7cc1a95ab634e2"},
]
"identify 2.4.8" = [
{file = "identify-2.4.8-py2.py3-none-any.whl", hash = "sha256:a55bdd671b6063eb837af938c250ec00bba6e610454265133b0d2db7ae718d0f"},
{file = "identify-2.4.8.tar.gz", hash = "sha256:97e839c1779f07011b84c92af183e1883d9745d532d83412cca1ca76d3808c1c"},
]
"idna 3.3" = [
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
@ -489,6 +573,10 @@ content_hash = "sha256:0e2a42ddc2fd648d8001d7be1ebfedbae88d34a2a78de0d941a23637d
{file = "more_itertools-8.12.0-py3-none-any.whl", hash = "sha256:43e6dd9942dffd72661a2c4ef383ad7da1e6a3e968a927ad7a6083ab410a688b"},
{file = "more-itertools-8.12.0.tar.gz", hash = "sha256:7dc6ad46f05f545f900dd59e8dfb4e84a4827b97b3cfecb175ea0c7d247f6064"},
]
"nodeenv 1.6.0" = [
{file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"},
{file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"},
]
"pillow 9.0.1" = [
{file = "Pillow-9.0.1-1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a5d24e1d674dd9d72c66ad3ea9131322819ff86250b30dc5821cbafcfa0b96b4"},
{file = "Pillow-9.0.1-1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2632d0f846b7c7600edf53c48f8f9f1e13e62f66a6dbc15191029d950bfed976"},
@ -526,10 +614,18 @@ content_hash = "sha256:0e2a42ddc2fd648d8001d7be1ebfedbae88d34a2a78de0d941a23637d
{file = "Pillow-9.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a9f44cd7e162ac6191491d7249cceb02b8116b0f7e847ee33f739d7cb1ea1f70"},
{file = "Pillow-9.0.1.tar.gz", hash = "sha256:6c8bc8238a7dfdaf7a75f5ec5a663f4173f8c367e5a39f87e720495e1eed75fa"},
]
"platformdirs 2.4.1" = [
{file = "platformdirs-2.4.1-py3-none-any.whl", hash = "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca"},
{file = "platformdirs-2.4.1.tar.gz", hash = "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda"},
]
"portend 3.1.0" = [
{file = "portend-3.1.0-py3-none-any.whl", hash = "sha256:9e735cee3a5c1961f09e3f3ba6dc498198c2d70b473d98d0d1504b8d1e7a3d61"},
{file = "portend-3.1.0.tar.gz", hash = "sha256:239e3116045ea823f6df87d6168107ad75ccc0590e37242af0cc1e98c5d224e4"},
]
"pre-commit 2.17.0" = [
{file = "pre_commit-2.17.0-py2.py3-none-any.whl", hash = "sha256:725fa7459782d7bec5ead072810e47351de01709be838c2ce1726b9591dad616"},
{file = "pre_commit-2.17.0.tar.gz", hash = "sha256:c1a8040ff15ad3d648c70cc3e55b93e4d2d5b687320955505587fd79bbaed06a"},
]
"python-magic 0.4.25" = [
{file = "python_magic-0.4.25-py2.py3-none-any.whl", hash = "sha256:1a2c81e8f395c744536369790bd75094665e9644110a6623bcc3bbea30f03973"},
{file = "python-magic-0.4.25.tar.gz", hash = "sha256:21f5f542aa0330f5c8a64442528542f6215c8e18d2466b399b0d9d39356d83fc"},
@ -634,6 +730,10 @@ content_hash = "sha256:0e2a42ddc2fd648d8001d7be1ebfedbae88d34a2a78de0d941a23637d
{file = "tempora-5.0.1-py3-none-any.whl", hash = "sha256:fbca6a229af666ea4ea8b2f9f80ac9a074f7cf53a97987855b1d15b6e93fd63b"},
{file = "tempora-5.0.1.tar.gz", hash = "sha256:cba0f197a64883bf3e73657efbc0324d5bf17179e7769b1385b4d75d26cd9127"},
]
"toml 0.10.2" = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
"typing-extensions 4.0.1" = [
{file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"},
{file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"},
@ -642,6 +742,10 @@ content_hash = "sha256:0e2a42ddc2fd648d8001d7be1ebfedbae88d34a2a78de0d941a23637d
{file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"},
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
]
"virtualenv 20.13.0" = [
{file = "virtualenv-20.13.0-py2.py3-none-any.whl", hash = "sha256:339f16c4a86b44240ba7223d0f93a7887c3ca04b5f9c8129da7958447d079b09"},
{file = "virtualenv-20.13.0.tar.gz", hash = "sha256:d8458cf8d59d0ea495ad9b34c2599487f8a7772d796f9910858376d1600dd2dd"},
]
"werkzeug 2.0.2" = [
{file = "Werkzeug-2.0.2-py3-none-any.whl", hash = "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f"},
{file = "Werkzeug-2.0.2.tar.gz", hash = "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a"},

View File

@ -46,10 +46,20 @@ homepage = "https://github.com/ehForwarderBot/efb-qq-plugin-go-cqhttp"
[project.entry-points."ehforwarderbot.qq.plugin"]
GoCQHttp = "efb_qq_plugin_go_cqhttp:GoCQHttp"
[tool]
[tool.pdm]
version = { from = "efb_qq_plugin_go_cqhttp/__init__.py" }
[build-system]
requires = ["pdm-pep517"]
build-backend = "pdm.pep517.api"
[tool.pdm]
version = { from = "efb_qq_plugin_go_cqhttp/__init__.py" }
[tool.pdm.dev-dependencies]
dev = ["pre-commit"]
[tool.black]
line-length = 120
[tool.isort]
profile = "black"
atomic = true
filter_files = true