refactor to async
This commit is contained in:
parent
d1e79d8ed0
commit
4b23b770fb
@ -15,12 +15,3 @@ repos:
|
||||
rev: 5.10.1
|
||||
hooks:
|
||||
- id: isort
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.942
|
||||
hooks:
|
||||
- id: mypy
|
||||
additional_dependencies:
|
||||
- types-requests
|
||||
- types-setuptools
|
||||
- types-toml
|
||||
|
@ -20,10 +20,10 @@ class ChatManager:
|
||||
channel=self.channel, uid=ChatID("__error_chat__"), name="Chat Missing"
|
||||
)
|
||||
|
||||
def build_efb_chat_as_private(self, context):
|
||||
async 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)
|
||||
i: dict = await self.channel.QQClient.get_stranger_info(uid)
|
||||
chat_name = ""
|
||||
if i:
|
||||
chat_name = i["nickname"]
|
||||
@ -37,13 +37,13 @@ class ChatManager:
|
||||
)
|
||||
return efb_chat
|
||||
|
||||
def build_or_get_efb_member(self, chat: Chat, context):
|
||||
async 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)
|
||||
i: dict = await self.channel.QQClient.get_stranger_info(member_uid)
|
||||
chat_name = ""
|
||||
if i:
|
||||
chat_name = i["nickname"]
|
||||
@ -55,20 +55,20 @@ class ChatManager:
|
||||
uid=str(member_uid),
|
||||
)
|
||||
|
||||
def build_efb_chat_as_group(self, context, update_member=False): # Should be cached
|
||||
async 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)
|
||||
i = await 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)
|
||||
members = await self.channel.QQClient.get_group_member_list(chat_uid, False)
|
||||
if members:
|
||||
for member in members:
|
||||
efb_chat.add_member(
|
||||
|
@ -1,3 +1,4 @@
|
||||
import asyncio
|
||||
import copy
|
||||
import logging
|
||||
import tempfile
|
||||
@ -5,14 +6,10 @@ import threading
|
||||
import time
|
||||
import uuid
|
||||
from datetime import datetime, timedelta
|
||||
from gettext import translation
|
||||
from typing import Any, BinaryIO, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import cherrypy
|
||||
import cqhttp
|
||||
from cherrypy._cpserver import Server
|
||||
from cherrypy.process.wspbus import states
|
||||
from cqhttp import CQHttp
|
||||
import aiocqhttp
|
||||
from aiocqhttp import CQHttp, Event
|
||||
from efb_qq_slave import BaseClient, QQMessengerChannel
|
||||
from ehforwarderbot import Chat, Message, MsgType, Status, coordinator
|
||||
from ehforwarderbot.chat import (
|
||||
@ -31,8 +28,10 @@ from ehforwarderbot.message import MessageCommand, MessageCommands
|
||||
from ehforwarderbot.status import MessageRemoval
|
||||
from ehforwarderbot.types import ChatID, MessageID
|
||||
from ehforwarderbot.utils import extra
|
||||
from hypercorn.asyncio import serve
|
||||
from hypercorn.config import Config as HyperConfig
|
||||
from PIL import Image
|
||||
from pkg_resources import resource_filename
|
||||
from quart.logging import create_serving_logger
|
||||
from requests import RequestException
|
||||
|
||||
from .ChatMgr import ChatManager
|
||||
@ -63,12 +62,6 @@ class GoCQHttp(BaseClient):
|
||||
logger: logging.Logger = logging.getLogger(__name__)
|
||||
channel: QQMessengerChannel
|
||||
|
||||
translator = translation(
|
||||
"efb_qq_slave",
|
||||
resource_filename("efb_qq_slave", "Clients/CoolQ/locale"),
|
||||
fallback=True,
|
||||
)
|
||||
|
||||
friend_list: List[Dict] = []
|
||||
friend_dict: Dict[int, dict] = {}
|
||||
stranger_dict: Dict[int, dict] = {}
|
||||
@ -80,14 +73,6 @@ class GoCQHttp(BaseClient):
|
||||
extra_group_list: List[Dict] = []
|
||||
repeat_counter = 0
|
||||
update_repeat_counter = 0
|
||||
event = threading.Event()
|
||||
update_contacts_timer: threading.Timer
|
||||
self_update_timer: threading.Timer
|
||||
check_status_timer: threading.Timer
|
||||
cherryServer: Server
|
||||
|
||||
can_send_image: bool = False
|
||||
can_send_voice: bool = False
|
||||
|
||||
def __init__(self, client_id: str, config: Dict[str, Any], channel):
|
||||
super().__init__(client_id, config)
|
||||
@ -103,10 +88,15 @@ class GoCQHttp(BaseClient):
|
||||
self.is_logged_in = False
|
||||
self.msg_decorator = QQMsgProcessor(instance=self)
|
||||
|
||||
def forward_msgs_wrapper(msg_elements: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||||
self.loop = asyncio.get_event_loop()
|
||||
self.shutdown_event = asyncio.Event()
|
||||
|
||||
asyncio.set_event_loop(self.loop)
|
||||
|
||||
async def forward_msgs_wrapper(msg_elements: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||||
fmt_msgs: List[Dict] = []
|
||||
for msg in msg_elements:
|
||||
from_user = self.get_user_info(msg["sender"]["user_id"])
|
||||
from_user = await self.get_user_info(msg["sender"]["user_id"])
|
||||
header_text = {"data": {"text": f'{from_user["remark"]}({from_user["nickname"]}):\n'}, "type": "text"}
|
||||
footer_text = {"data": {"text": "\n- - - - - - - - - - - - - - -\n"}, "type": "text"}
|
||||
msg["content"].insert(0, header_text)
|
||||
@ -116,12 +106,12 @@ class GoCQHttp(BaseClient):
|
||||
if i == 1:
|
||||
fmt_msgs.pop()
|
||||
msg["content"].pop()
|
||||
fmt_msgs += forward_msgs_wrapper([inner_msg])
|
||||
fmt_msgs += await forward_msgs_wrapper([inner_msg])
|
||||
else:
|
||||
fmt_msgs.append(inner_msg)
|
||||
return fmt_msgs
|
||||
|
||||
def message_element_wrapper(
|
||||
async def message_element_wrapper(
|
||||
context: Dict[str, Any], msg_element: Dict[str, Any], chat: Chat
|
||||
) -> Tuple[str, List[Message], List[Tuple[Tuple[int, int], Union[Chat, ChatMember]]]]:
|
||||
msg_type = msg_element["type"]
|
||||
@ -142,12 +132,12 @@ class GoCQHttp(BaseClient):
|
||||
elif msg_type == "at":
|
||||
# todo Recheck if bug exists
|
||||
g_id = context["group_id"]
|
||||
my_uid = self.get_qq_uid()
|
||||
my_uid = await self.get_qq_uid()
|
||||
self.logger.debug("My QQ uid: %s\n" "QQ mentioned: %s\n", my_uid, msg_data["qq"])
|
||||
if str(msg_data["qq"]) == "all":
|
||||
group_card = "all"
|
||||
else:
|
||||
member_info = self.get_user_info(msg_data["qq"], group_id=g_id)["in_group_info"]
|
||||
member_info = await self.get_user_info(msg_data["qq"], group_id=g_id)["in_group_info"]
|
||||
group_card = member_info["card"] if member_info["card"] != "" else member_info["nickname"]
|
||||
self.logger.debug("Group card: {}".format(group_card))
|
||||
substitution_begin = len(main_text)
|
||||
@ -157,34 +147,34 @@ class GoCQHttp(BaseClient):
|
||||
at_dict = ((substitution_begin, substitution_end), chat.self)
|
||||
at_list.append(at_dict)
|
||||
elif msg_type == "reply":
|
||||
ref_user = self.get_user_info(msg_data["qq"])
|
||||
ref_user = await self.get_user_info(msg_data["qq"])
|
||||
main_text = (
|
||||
f'「{ref_user["remark"]}({ref_user["nickname"]}):{msg_data["text"]}」\n'
|
||||
"- - - - - - - - - - - - - - -\n"
|
||||
)
|
||||
elif msg_type == "forward":
|
||||
forward_msgs = self.coolq_api_query("get_forward_msg", message_id=msg_data["id"])["messages"]
|
||||
forward_msgs = await self.coolq_api_query("get_forward_msg", message_id=msg_data["id"])["messages"]
|
||||
logging.debug(f"Forwarded message: {forward_msgs}")
|
||||
fmt_forward_msgs = forward_msgs_wrapper(forward_msgs)
|
||||
fmt_forward_msgs = await forward_msgs_wrapper(forward_msgs)
|
||||
logging.debug(f"Formated forwarded message: {forward_msgs}")
|
||||
header_msg = {"data": {"text": "合并转发消息开始\n- - - - - - - - - - - - - - -\n"}, "type": "text"}
|
||||
footer_msg = {"data": {"text": "合并转发消息结束"}, "type": "text"}
|
||||
fmt_forward_msgs.insert(0, header_msg)
|
||||
fmt_forward_msgs.append(footer_msg)
|
||||
main_text, messages, _ = message_elements_wrapper(context, fmt_forward_msgs, chat)
|
||||
main_text, messages, _ = await message_elements_wrapper(context, fmt_forward_msgs, chat)
|
||||
return main_text, messages, []
|
||||
else:
|
||||
messages.extend(self.call_msg_decorator(msg_type, msg_data, chat))
|
||||
return main_text, messages, at_list
|
||||
|
||||
def message_elements_wrapper(
|
||||
async def message_elements_wrapper(
|
||||
context: Dict[str, Any], msg_elements: List[Dict[str, Any]], chat: Chat
|
||||
) -> Tuple[str, List[Message], Dict[Tuple[int, int], Union[Chat, ChatMember]]]:
|
||||
messages: List[Message] = []
|
||||
main_text: str = ""
|
||||
at_dict: Dict[Tuple[int, int], Union[Chat, ChatMember]] = {}
|
||||
for msg_element in msg_elements:
|
||||
sub_main_text, sub_messages, sub_at_list = message_element_wrapper(context, msg_element, chat)
|
||||
sub_main_text, sub_messages, sub_at_list = await message_element_wrapper(context, msg_element, chat)
|
||||
main_text_len = len(main_text)
|
||||
for at_tuple in sub_at_list:
|
||||
pos = (
|
||||
@ -197,19 +187,19 @@ class GoCQHttp(BaseClient):
|
||||
return main_text, messages, at_dict
|
||||
|
||||
@self.coolq_bot.on_message
|
||||
def handle_msg(context):
|
||||
async def handle_msg(context: Event):
|
||||
self.logger.debug(repr(context))
|
||||
msg_elements = context["message"]
|
||||
qq_uid = context["user_id"]
|
||||
chat: Chat
|
||||
author: ChatMember
|
||||
|
||||
user = self.get_user_info(qq_uid)
|
||||
user = await self.get_user_info(qq_uid)
|
||||
if context["message_type"] == "private":
|
||||
context["alias"] = user["remark"]
|
||||
chat: PrivateChat = self.chat_manager.build_efb_chat_as_private(context)
|
||||
chat: PrivateChat = await self.chat_manager.build_efb_chat_as_private(context)
|
||||
else:
|
||||
chat = self.chat_manager.build_efb_chat_as_group(context)
|
||||
chat = await self.chat_manager.build_efb_chat_as_group(context)
|
||||
|
||||
if "anonymous" not in context or context["anonymous"] is None:
|
||||
if context["message_type"] == "group":
|
||||
@ -221,18 +211,18 @@ class GoCQHttp(BaseClient):
|
||||
uid=ChatID("__{context[uid_prefix]}__".format(context=context)),
|
||||
)
|
||||
else:
|
||||
user = self.get_user_info(qq_uid, group_id=context["group_id"])
|
||||
user = await self.get_user_info(qq_uid, group_id=context["group_id"])
|
||||
context["nickname"] = user["remark"]
|
||||
context["alias"] = user["in_group_info"]["card"]
|
||||
author = self.chat_manager.build_or_get_efb_member(chat, context)
|
||||
author = await self.chat_manager.build_or_get_efb_member(chat, context)
|
||||
elif context["message_type"] == "private":
|
||||
author = chat.other
|
||||
else:
|
||||
author = self.chat_manager.build_or_get_efb_member(chat, context)
|
||||
author = await self.chat_manager.build_or_get_efb_member(chat, context)
|
||||
else: # anonymous user in group
|
||||
author = self.chat_manager.build_efb_chat_as_anonymous_user(chat, context)
|
||||
|
||||
main_text, messages, at_dict = message_elements_wrapper(context, msg_elements, chat)
|
||||
main_text, messages, at_dict = await message_elements_wrapper(context, msg_elements, chat)
|
||||
|
||||
if main_text != "":
|
||||
messages.append(self.msg_decorator.qq_text_simple_wrapper(main_text, at_dict))
|
||||
@ -262,19 +252,19 @@ class GoCQHttp(BaseClient):
|
||||
send_message_wrapper(efb_msg)
|
||||
|
||||
@self.coolq_bot.on_notice("group_increase")
|
||||
def handle_group_increase_msg(context):
|
||||
async def handle_group_increase_msg(context: Event):
|
||||
context["event_description"] = "\u2139 Group Member Increase Event"
|
||||
if (context["sub_type"]) == "invite":
|
||||
text = "{nickname}({context[user_id]}) joined the group({group_name}) via invitation"
|
||||
else:
|
||||
text = "{nickname}({context[user_id]}) joined the group({group_name})"
|
||||
|
||||
original_group = self.get_group_info(context["group_id"], False)
|
||||
original_group = await self.get_group_info(context["group_id"], False)
|
||||
group_name = context["group_id"]
|
||||
if original_group is not None and "group_name" in original_group:
|
||||
group_name = original_group["group_name"]
|
||||
text = text.format(
|
||||
nickname=self.get_stranger_info(context["user_id"])["nickname"],
|
||||
nickname=await self.get_stranger_info(context["user_id"])["nickname"],
|
||||
context=context,
|
||||
group_name=group_name,
|
||||
)
|
||||
@ -283,9 +273,9 @@ class GoCQHttp(BaseClient):
|
||||
self.send_efb_group_notice(context)
|
||||
|
||||
@self.coolq_bot.on_notice("group_decrease")
|
||||
def handle_group_decrease_msg(context):
|
||||
async def handle_group_decrease_msg(context: Event):
|
||||
context["event_description"] = "\u2139 Group Member Decrease Event"
|
||||
original_group = self.get_group_info(context["group_id"], False)
|
||||
original_group = await self.get_group_info(context["group_id"], False)
|
||||
group_name = context["group_id"]
|
||||
if original_group is not None and "group_name" in original_group:
|
||||
group_name = original_group["group_name"]
|
||||
@ -298,7 +288,7 @@ class GoCQHttp(BaseClient):
|
||||
else:
|
||||
text = "{nickname}({context[user_id]}) was kicked from the group({group_name})"
|
||||
text = text.format(
|
||||
nickname=self.get_stranger_info(context["user_id"])["nickname"],
|
||||
nickname=await self.get_stranger_info(context["user_id"])["nickname"],
|
||||
context=context,
|
||||
group_name=group_name,
|
||||
)
|
||||
@ -306,19 +296,19 @@ class GoCQHttp(BaseClient):
|
||||
self.send_efb_group_notice(context)
|
||||
|
||||
@self.coolq_bot.on_notice("group_admin")
|
||||
def handle_group_admin_msg(context):
|
||||
async def handle_group_admin_msg(context: Event):
|
||||
context["event_description"] = "\u2139 Group Admin Change Event"
|
||||
if (context["sub_type"]) == "set":
|
||||
text = "{nickname}({context[user_id]}) has been appointed as the group({group_name}) administrator"
|
||||
else:
|
||||
text = "{nickname}({context[user_id]}) has been de-appointed as the group({group_name}) administrator"
|
||||
|
||||
original_group = self.get_group_info(context["group_id"], False)
|
||||
original_group = await self.get_group_info(context["group_id"], False)
|
||||
group_name = context["group_id"]
|
||||
if original_group is not None and "group_name" in original_group:
|
||||
group_name = original_group["group_name"]
|
||||
text = text.format(
|
||||
nickname=self.get_stranger_info(context["user_id"])["nickname"],
|
||||
nickname=await self.get_stranger_info(context["user_id"])["nickname"],
|
||||
context=context,
|
||||
group_name=group_name,
|
||||
)
|
||||
@ -327,7 +317,7 @@ class GoCQHttp(BaseClient):
|
||||
self.send_efb_group_notice(context)
|
||||
|
||||
@self.coolq_bot.on_notice("group_ban")
|
||||
def handle_group_ban_msg(context):
|
||||
async def handle_group_ban_msg(context: Event):
|
||||
context["event_description"] = "\u2139 Group Member Restrict Event"
|
||||
if (context["sub_type"]) == "ban":
|
||||
text = (
|
||||
@ -344,27 +334,27 @@ class GoCQHttp(BaseClient):
|
||||
)
|
||||
time_text = ""
|
||||
|
||||
original_group = self.get_group_info(context["group_id"], False)
|
||||
original_group = await self.get_group_info(context["group_id"], False)
|
||||
group_name = context["group_id"]
|
||||
if original_group is not None and "group_name" in original_group:
|
||||
group_name = original_group["group_name"]
|
||||
text = text.format(
|
||||
nickname=self.get_stranger_info(context["user_id"])["nickname"],
|
||||
nickname=await self.get_stranger_info(context["user_id"])["nickname"],
|
||||
context=context,
|
||||
time=time_text,
|
||||
group_name=group_name,
|
||||
nickname_=self.get_stranger_info(context["operator_id"])["nickname"],
|
||||
nickname_=await self.get_stranger_info(context["operator_id"])["nickname"],
|
||||
)
|
||||
|
||||
context["message"] = text
|
||||
self.send_efb_group_notice(context)
|
||||
|
||||
@self.coolq_bot.on_notice("offline_file")
|
||||
def handle_offline_file_upload_msg(context):
|
||||
async def handle_offline_file_upload_msg(context: Event):
|
||||
context["event_description"] = "\u2139 Offline File Upload Event"
|
||||
context["uid_prefix"] = "offline_file"
|
||||
file_info_msg = ("Filename: {file[name]}\n" "File size: {file[size]}").format(file=context["file"])
|
||||
user = self.get_user_info(context["user_id"])
|
||||
user = await self.get_user_info(context["user_id"])
|
||||
text = "{remark}({nickname}) uploaded a file to you\n"
|
||||
text = text.format(remark=user["remark"], nickname=user["nickname"]) + file_info_msg
|
||||
context["message"] = text
|
||||
@ -376,10 +366,10 @@ class GoCQHttp(BaseClient):
|
||||
threading.Thread(target=self.async_download_file, args=[], kwargs=param_dict).start()
|
||||
|
||||
@self.coolq_bot.on_notice("group_upload")
|
||||
def handle_group_file_upload_msg(context):
|
||||
async def handle_group_file_upload_msg(context: Event):
|
||||
context["event_description"] = "\u2139 Group File Upload Event"
|
||||
context["uid_prefix"] = "group_upload"
|
||||
original_group = self.get_group_info(context["group_id"], False)
|
||||
original_group = await self.get_group_info(context["group_id"], False)
|
||||
group_name = context["group_id"]
|
||||
if original_group is not None and "group_name" in original_group:
|
||||
group_name = original_group["group_name"]
|
||||
@ -387,7 +377,7 @@ class GoCQHttp(BaseClient):
|
||||
file_info_msg = ("File ID: {file[id]}\n" "Filename: {file[name]}\n" "File size: {file[size]}").format(
|
||||
file=context["file"]
|
||||
)
|
||||
member_info = self.get_user_info(context["user_id"], group_id=context["group_id"])["in_group_info"]
|
||||
member_info = await self.get_user_info(context["user_id"], group_id=context["group_id"])["in_group_info"]
|
||||
group_card = member_info["card"] if member_info["card"] != "" else member_info["nickname"]
|
||||
text = "{member_card}({context[user_id]}) uploaded a file to group({group_name})\n"
|
||||
text = text.format(member_card=group_card, context=context, group_name=group_name) + file_info_msg
|
||||
@ -404,19 +394,19 @@ class GoCQHttp(BaseClient):
|
||||
threading.Thread(target=self.async_download_group_file, args=[], kwargs=param_dict).start()
|
||||
|
||||
@self.coolq_bot.on_notice("friend_add")
|
||||
def handle_friend_add_msg(context):
|
||||
async def handle_friend_add_msg(context: Event):
|
||||
context["event_description"] = "\u2139 New Friend Event"
|
||||
context["uid_prefix"] = "friend_add"
|
||||
text = "{nickname}({context[user_id]}) has become your friend!"
|
||||
text = text.format(
|
||||
nickname=self.get_stranger_info(context["user_id"])["nickname"],
|
||||
nickname=await self.get_stranger_info(context["user_id"])["nickname"],
|
||||
context=context,
|
||||
)
|
||||
context["message"] = text
|
||||
self.send_msg_to_master(context)
|
||||
|
||||
@self.coolq_bot.on_notice("group_recall")
|
||||
def handle_group_recall_msg(context):
|
||||
async def handle_group_recall_msg(context: Event):
|
||||
coolq_msg_id = context["message_id"]
|
||||
chat = GroupChat(channel=self.channel, uid=f"group_{context['group_id']}")
|
||||
|
||||
@ -426,11 +416,11 @@ class GoCQHttp(BaseClient):
|
||||
)
|
||||
|
||||
@self.coolq_bot.on_notice("friend_recall")
|
||||
def handle_friend_recall_msg(context):
|
||||
async def handle_friend_recall_msg(context: Event):
|
||||
coolq_msg_id = context["message_id"]
|
||||
|
||||
try:
|
||||
chat: PrivateChat = self.chat_manager.build_efb_chat_as_private(context)
|
||||
chat: PrivateChat = await self.chat_manager.build_efb_chat_as_private(context)
|
||||
except Exception:
|
||||
return
|
||||
efb_msg = Message(chat=chat, uid=MessageID(f"{chat.uid.split('_')[-1]}_{coolq_msg_id}"))
|
||||
@ -439,7 +429,7 @@ class GoCQHttp(BaseClient):
|
||||
)
|
||||
|
||||
@self.coolq_bot.on_request("friend") # Add friend request
|
||||
def handle_add_friend_request(context):
|
||||
async def handle_add_friend_request(context: Event):
|
||||
self.logger.debug(repr(context))
|
||||
context["event_description"] = "\u2139 New Friend Request"
|
||||
context["uid_prefix"] = "friend_request"
|
||||
@ -449,7 +439,7 @@ class GoCQHttp(BaseClient):
|
||||
"{context[comment]}"
|
||||
)
|
||||
text = text.format(
|
||||
nickname=self.get_stranger_info(context["user_id"])["nickname"],
|
||||
nickname=await self.get_stranger_info(context["user_id"])["nickname"],
|
||||
context=context,
|
||||
)
|
||||
context["message"] = text
|
||||
@ -469,34 +459,34 @@ class GoCQHttp(BaseClient):
|
||||
self.send_msg_to_master(context)
|
||||
|
||||
@self.coolq_bot.on_request("group")
|
||||
def handle_group_request(context):
|
||||
async def handle_group_request(context: Event):
|
||||
self.logger.debug(repr(context))
|
||||
context["uid_prefix"] = "group_request"
|
||||
context["group_name"] = ("[Request]") + self.get_group_info(context["group_id"])["group_name"]
|
||||
context["group_name"] = ("[Request]") + await self.get_group_info(context["group_id"])["group_name"]
|
||||
context["group_id_orig"] = context["group_id"]
|
||||
context["group_id"] = str(context["group_id"]) + "_notification"
|
||||
context["message_type"] = "group"
|
||||
context["event_description"] = "\u2139 New Group Join Request"
|
||||
original_group = self.get_group_info(context["group_id_orig"], False)
|
||||
original_group = await self.get_group_info(context["group_id_orig"], False)
|
||||
group_name = context["group_id"]
|
||||
if original_group is not None and "group_name" in original_group:
|
||||
group_name = original_group["group_name"]
|
||||
msg = Message()
|
||||
msg.uid = "group" + "_" + str(context["group_id"])
|
||||
msg.author = (self.chat_manager.build_efb_chat_as_system_user(context)).other
|
||||
msg.chat = self.chat_manager.build_efb_chat_as_group(context)
|
||||
msg.chat = await self.chat_manager.build_efb_chat_as_group(context)
|
||||
msg.deliver_to = coordinator.master
|
||||
msg.type = MsgType.Text
|
||||
name = ""
|
||||
if not self.get_friend_remark(context["user_id"]):
|
||||
if not await self.get_friend_remark(context["user_id"]):
|
||||
name = "{}({})[{}] ".format(
|
||||
self.get_stranger_info(context["user_id"])["nickname"],
|
||||
self.get_friend_remark(context["user_id"]),
|
||||
await self.get_stranger_info(context["user_id"])["nickname"],
|
||||
await self.get_friend_remark(context["user_id"]),
|
||||
context["user_id"],
|
||||
)
|
||||
else:
|
||||
name = "{}[{}] ".format(
|
||||
self.get_stranger_info(context["user_id"])["nickname"],
|
||||
await self.get_stranger_info(context["user_id"])["nickname"],
|
||||
context["user_id"],
|
||||
)
|
||||
msg.text = "{} wants to join the group {}({}). \nHere is the comment: {}".format(
|
||||
@ -526,50 +516,30 @@ class GoCQHttp(BaseClient):
|
||||
)
|
||||
coordinator.send_message(msg)
|
||||
|
||||
self.check_status_periodically(threading.Event())
|
||||
self.update_contacts_timer = threading.Timer(1800, self.update_contacts_periodically, [threading.Event()])
|
||||
self.update_contacts_timer.start()
|
||||
# threading.Thread(target=self.check_running_status).start()
|
||||
asyncio.run(self.check_status_periodically(run_once=True))
|
||||
|
||||
def run_instance(self, *args, **kwargs):
|
||||
# threading.Thread(target=self.coolq_bot.run, args=args, kwargs=kwargs, daemon=True).start()
|
||||
cherrypy.tree.graft(self.coolq_bot.wsgi, "/")
|
||||
cherrypy.server.unsubscribe()
|
||||
self.cherryServer = Server()
|
||||
self.cherryServer.socket_host = self.client_config["host"]
|
||||
self.cherryServer.socket_port = self.client_config["port"]
|
||||
self.cherryServer.subscribe()
|
||||
cherrypy.engine.start()
|
||||
cherrypy.engine.wait(states.EXITING)
|
||||
def run_instance(self, host: str, port: int, debug: bool = False):
|
||||
def _run():
|
||||
config = HyperConfig()
|
||||
config.access_log_format = "%(h)s %(r)s %(s)s %(b)s %(D)s"
|
||||
config.accesslog = create_serving_logger()
|
||||
config.bind = [f"{host}:{port}"]
|
||||
if debug is not None:
|
||||
self.debug = debug
|
||||
config.use_reloader = debug
|
||||
config.errorlog = config.accesslog
|
||||
|
||||
@extra(
|
||||
name=("Restart CoolQ Client"),
|
||||
desc=(
|
||||
"Force CoolQ to restart\n"
|
||||
"Usage: {function_name} [-l] [-c] [-e]\n"
|
||||
" -l: Restart and clean log\n"
|
||||
" -c: Restart and clean cache\n"
|
||||
" -e: Restart and clean event\n"
|
||||
),
|
||||
)
|
||||
def relogin(self, param: str = ""):
|
||||
param_dict = dict()
|
||||
if param:
|
||||
params = param.split(" ")
|
||||
for each_param in params:
|
||||
if each_param == " ":
|
||||
continue
|
||||
if each_param == "-l":
|
||||
param_dict["clean_log"] = "true"
|
||||
elif each_param == "-c":
|
||||
param_dict["clean_cache"] = "true"
|
||||
elif each_param == "-e":
|
||||
param_dict["clean_event"] = "true"
|
||||
else:
|
||||
return ("Unknown parameter: {}.").format(param)
|
||||
self.logger.debug(repr(param_dict))
|
||||
self.coolq_api_query("_set_restart", **param_dict)
|
||||
return "Done. Please wait for a while."
|
||||
self.loop.create_task(serve(self.coolq_bot.server_app, config, shutdown_trigger=self.shutdown_event.wait))
|
||||
self.loop.create_task(self.check_status_periodically())
|
||||
self.loop.create_task(self.update_contacts_periodically())
|
||||
self.loop.run_forever()
|
||||
|
||||
self.t = threading.Thread(target=_run)
|
||||
self.t.daemon = True
|
||||
self.t.start()
|
||||
|
||||
def relogin(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def logout(self):
|
||||
raise NotImplementedError
|
||||
@ -579,17 +549,17 @@ class GoCQHttp(BaseClient):
|
||||
desc=("Force efb-qq-slave to refresh status from CoolQ Client.\n" "Usage: {function_name}"),
|
||||
)
|
||||
def login(self, param: str = ""):
|
||||
self.check_status_periodically(None)
|
||||
asyncio.run(self.check_status_periodically(run_once=True))
|
||||
return "Done"
|
||||
|
||||
def get_stranger_info(self, user_id: int, no_cache: bool = False) -> Dict[str, Any]:
|
||||
async def get_stranger_info(self, user_id: int, no_cache: bool = False) -> Dict[str, Any]:
|
||||
user_id = int(user_id)
|
||||
return self.get_user_info(user_id, no_cache=no_cache)
|
||||
return await self.get_user_info(user_id, no_cache=no_cache)
|
||||
|
||||
def get_login_info(self) -> Dict[Any, Any]:
|
||||
res = self.coolq_bot.get_status()
|
||||
async def get_login_info(self) -> Dict[Any, Any]:
|
||||
res = await self.coolq_bot.get_status()
|
||||
if "good" in res or "online" in res:
|
||||
data = self.coolq_bot.get_login_info()
|
||||
data = await self.coolq_bot.get_login_info()
|
||||
return {
|
||||
"status": 0,
|
||||
"data": {"uid": data["user_id"], "nickname": data["nickname"]},
|
||||
@ -597,15 +567,15 @@ class GoCQHttp(BaseClient):
|
||||
else:
|
||||
return {"status": 1}
|
||||
|
||||
def get_groups(self) -> List:
|
||||
async def get_groups(self) -> List:
|
||||
# todo Add support for discuss group iteration
|
||||
self.update_group_list() # Force update group list
|
||||
await self.update_group_list() # Force update group list
|
||||
res = self.group_list
|
||||
# res = self.coolq_bot.get_group_list()
|
||||
groups = []
|
||||
for i in range(len(res)):
|
||||
context = {"message_type": "group", "group_id": res[i]["group_id"]}
|
||||
efb_chat = self.chat_manager.build_efb_chat_as_group(context)
|
||||
efb_chat = await self.chat_manager.build_efb_chat_as_group(context)
|
||||
groups.append(efb_chat)
|
||||
for i in range(len(self.extra_group_list)):
|
||||
does_exist = False
|
||||
@ -619,13 +589,13 @@ class GoCQHttp(BaseClient):
|
||||
"message_type": "group",
|
||||
"group_id": self.extra_group_list[i]["group_id"],
|
||||
}
|
||||
efb_chat = self.chat_manager.build_efb_chat_as_group(context)
|
||||
efb_chat = await self.chat_manager.build_efb_chat_as_group(context)
|
||||
groups.append(efb_chat)
|
||||
return groups + self.discuss_list
|
||||
|
||||
def get_friends(self) -> List:
|
||||
async def get_friends(self) -> List:
|
||||
try:
|
||||
self.update_friend_list() # Force update friend list
|
||||
await self.update_friend_list() # Force update friend list
|
||||
except CoolQAPIFailureException:
|
||||
self.deliver_alert_to_master(("Failed to retrieve the friend list.\n" "Only groups are shown."))
|
||||
return []
|
||||
@ -636,7 +606,7 @@ class GoCQHttp(BaseClient):
|
||||
"nickname": current_user["nickname"],
|
||||
"alias": current_user["remark"],
|
||||
}
|
||||
efb_chat = self.chat_manager.build_efb_chat_as_private(context)
|
||||
efb_chat = await self.chat_manager.build_efb_chat_as_private(context)
|
||||
users.append(efb_chat)
|
||||
return users
|
||||
|
||||
@ -663,7 +633,7 @@ class GoCQHttp(BaseClient):
|
||||
if msg.edit:
|
||||
try:
|
||||
uid_type = msg.uid.split("_")
|
||||
self.recall_message(uid_type[1])
|
||||
asyncio.run(self.recall_message(uid_type[1]))
|
||||
except CoolQAPIFailureException:
|
||||
raise EFBOperationNotSupported(
|
||||
("Failed to recall the message!\n" "This message may have already expired.")
|
||||
@ -673,7 +643,7 @@ class GoCQHttp(BaseClient):
|
||||
if msg.text == "kick`":
|
||||
group_id = chat_type[1]
|
||||
user_id = msg.target.author.uid
|
||||
self.coolq_api_query("set_group_kick", group_id=group_id, user_id=user_id)
|
||||
asyncio.run(self.coolq_api_query("set_group_kick", group_id=group_id, user_id=user_id))
|
||||
else:
|
||||
if isinstance(msg.target, Message):
|
||||
max_length = 50
|
||||
@ -688,16 +658,11 @@ class GoCQHttp(BaseClient):
|
||||
tgt_text,
|
||||
coolq_text_encode(msg.text),
|
||||
)
|
||||
msg.uid = self.coolq_send_message(chat_type[0], chat_type[1], msg.text)
|
||||
msg.uid = asyncio.run(self.coolq_send_message(chat_type[0], chat_type[1], msg.text))
|
||||
self.logger.debug("[%s] Sent as a text message. %s", msg.uid, msg.text)
|
||||
elif msg.type in (MsgType.Image, MsgType.Sticker, MsgType.Animation):
|
||||
self.logger.info("[%s] Image/Sticker/Animation %s", msg.uid, msg.type)
|
||||
text = ""
|
||||
if not self.can_send_image:
|
||||
self.check_features() # Force checking features
|
||||
raise EFBOperationNotSupported(
|
||||
("Unable to send image now. Please check your CoolQ version " "or retry later")
|
||||
)
|
||||
if msg.type != MsgType.Sticker:
|
||||
text += m.coolq_code_image_wrapper(msg.file, msg.path)
|
||||
else:
|
||||
@ -715,23 +680,17 @@ class GoCQHttp(BaseClient):
|
||||
f.seek(0)
|
||||
text += m.coolq_code_image_wrapper(f, f.name)
|
||||
if msg.text:
|
||||
msg.uid = self.coolq_send_message(chat_type[0], chat_type[1], text + coolq_text_encode(msg.text))
|
||||
msg.uid = asyncio.run(
|
||||
self.coolq_send_message(chat_type[0], chat_type[1], text + coolq_text_encode(msg.text))
|
||||
)
|
||||
else:
|
||||
msg.uid = self.coolq_send_message(chat_type[0], chat_type[1], text)
|
||||
msg.uid = asyncio.run(self.coolq_send_message(chat_type[0], chat_type[1], text))
|
||||
# todo More MsgType Support
|
||||
elif msg.type is MsgType.Voice:
|
||||
if not self.can_send_voice:
|
||||
self.check_features() # Force checking features
|
||||
raise EFBOperationNotSupported(
|
||||
(
|
||||
"Unable to send voice now. Please check your CoolQ version "
|
||||
" and install CoolQ audio library or retry later"
|
||||
)
|
||||
)
|
||||
text = m.coolq_voice_image_wrapper(msg.file, msg.path)
|
||||
msg.uid = self.coolq_send_message(chat_type[0], chat_type[1], text)
|
||||
msg.uid = asyncio.run(self.coolq_send_message(chat_type[0], chat_type[1], text))
|
||||
if msg.text:
|
||||
self.coolq_send_message(chat_type[0], chat_type[1], msg.text)
|
||||
asyncio.run(self.coolq_send_message(chat_type[0], chat_type[1], msg.text))
|
||||
return msg
|
||||
|
||||
def call_msg_decorator(self, msg_type: str, *args) -> List[Message]:
|
||||
@ -744,21 +703,21 @@ class GoCQHttp(BaseClient):
|
||||
else:
|
||||
return func(*args)
|
||||
|
||||
def get_qq_uid(self):
|
||||
res = self.get_login_info()
|
||||
async def get_qq_uid(self):
|
||||
res = await self.get_login_info()
|
||||
if res["status"] == 0:
|
||||
return res["data"]["uid"]
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_group_member_list(self, group_id, no_cache=False) -> List[Dict[str, Any]]:
|
||||
async def get_group_member_list(self, group_id, no_cache=False) -> List[Dict[str, Any]]:
|
||||
if (
|
||||
no_cache
|
||||
or (group_id not in self.group_member_dict)
|
||||
or (datetime.now() - self.group_member_dict[group_id]["time"] > timedelta(hours=1))
|
||||
): # Force Update
|
||||
try:
|
||||
member_list = self.coolq_api_query("get_group_member_list", group_id=group_id, no_cache=no_cache)
|
||||
member_list = await self.coolq_api_query("get_group_member_list", group_id=group_id, no_cache=no_cache)
|
||||
except CoolQAPIFailureException as e:
|
||||
self.deliver_alert_to_master(("Failed the get group member detail.") + "{}".format(e))
|
||||
return []
|
||||
@ -768,27 +727,27 @@ class GoCQHttp(BaseClient):
|
||||
}
|
||||
return self.group_member_dict[group_id]["members"]
|
||||
|
||||
def get_user_info(self, user_id: int, group_id: Optional[str] = None, no_cache=False):
|
||||
async def get_user_info(self, user_id: int, group_id: Optional[str] = None, no_cache=False):
|
||||
user_id = int(user_id)
|
||||
if no_cache or (not self.friend_list) or (user_id not in self.friend_dict):
|
||||
self.update_friend_list()
|
||||
await self.update_friend_list()
|
||||
friend = self.friend_dict.get(user_id)
|
||||
if friend:
|
||||
user = copy.deepcopy(friend)
|
||||
user["is_friend"] = True
|
||||
else:
|
||||
if no_cache:
|
||||
stranger = self.coolq_api_query("get_stranger_info", user_id=user_id)
|
||||
stranger = await self.coolq_api_query("get_stranger_info", user_id=user_id)
|
||||
self.stranger_dict[user_id] = stranger
|
||||
stranger = self.stranger_dict.get(user_id)
|
||||
if stranger is None:
|
||||
stranger = self.coolq_api_query("get_stranger_info", user_id=user_id)
|
||||
stranger = await self.coolq_api_query("get_stranger_info", user_id=user_id)
|
||||
self.stranger_dict[user_id] = stranger
|
||||
user = copy.deepcopy(stranger)
|
||||
user["is_friend"] = False
|
||||
if group_id is not None:
|
||||
user["is_in_group"] = False
|
||||
for member in self.get_group_member_list(group_id):
|
||||
for member in await self.get_group_member_list(group_id):
|
||||
if member["user_id"] == user_id:
|
||||
user["is_in_group"] = True
|
||||
user["in_group_info"] = member
|
||||
@ -798,9 +757,9 @@ class GoCQHttp(BaseClient):
|
||||
user["remark"] = user["nickname"]
|
||||
return user
|
||||
|
||||
def get_group_info(self, group_id, no_cache=False):
|
||||
async def get_group_info(self, group_id, no_cache=False):
|
||||
if no_cache or not self.group_list:
|
||||
self.update_group_list()
|
||||
await self.update_group_list()
|
||||
group = self.group_dict.get(group_id)
|
||||
if group:
|
||||
return group
|
||||
@ -809,7 +768,7 @@ class GoCQHttp(BaseClient):
|
||||
if extra_group["group_id"] == group_id:
|
||||
return extra_group
|
||||
try:
|
||||
external_group = self.get_external_group_info(group_id)
|
||||
external_group = await self.get_external_group_info(group_id)
|
||||
except CoolQAPIFailureException:
|
||||
self.logger.error(f"Get external group({group_id}) info failed.")
|
||||
return None
|
||||
@ -817,18 +776,17 @@ class GoCQHttp(BaseClient):
|
||||
self.extra_group_list.append(external_group)
|
||||
return external_group
|
||||
|
||||
def coolq_send_message(self, msg_type, uid, message):
|
||||
async def coolq_send_message(self, msg_type, uid, message):
|
||||
keyword = msg_type if msg_type != "private" else "user"
|
||||
res = self.coolq_api_query("send_msg", message_type=msg_type, **{keyword + "_id": uid}, message=message)
|
||||
res = await self.coolq_api_query("send_msg", message_type=msg_type, **{keyword + "_id": uid}, message=message)
|
||||
return str(uid) + "_" + str(res["message_id"])
|
||||
|
||||
def _coolq_api_wrapper(self, func_name, **kwargs):
|
||||
async def _coolq_api_wrapper(self, func_name, **kwargs):
|
||||
try:
|
||||
func = getattr(self.coolq_bot, func_name)
|
||||
res = func(**kwargs)
|
||||
res = await self.coolq_bot.call_action(func_name, **kwargs)
|
||||
except RequestException as e:
|
||||
raise CoolQDisconnectedException(("Unable to connect to CoolQ Client!" "Error Message:\n{}").format(str(e)))
|
||||
except cqhttp.Error as ex:
|
||||
except aiocqhttp.Error as ex:
|
||||
api_ex = CoolQAPIFailureException(
|
||||
("CoolQ HTTP API encountered an error!\n" "Status Code:{} " "Return Code:{}").format(
|
||||
ex.status_code, ex.retcode
|
||||
@ -840,30 +798,31 @@ class GoCQHttp(BaseClient):
|
||||
else:
|
||||
return res
|
||||
|
||||
def check_running_status(self):
|
||||
res = self._coolq_api_wrapper("get_status")
|
||||
async def check_running_status(self):
|
||||
res = await self._coolq_api_wrapper("get_status")
|
||||
if res["good"] or res["online"]:
|
||||
return True
|
||||
else:
|
||||
raise CoolQOfflineException(("CoolQ Client isn't working correctly!"))
|
||||
|
||||
def coolq_api_query(self, func_name, **kwargs) -> Any:
|
||||
async def coolq_api_query(self, func_name, **kwargs) -> Any:
|
||||
"""# Do not call get_status too frequently
|
||||
if self.check_running_status():
|
||||
return self._coolq_api_wrapper(func_name, **kwargs)
|
||||
"""
|
||||
if self.is_logged_in and self.is_connected:
|
||||
return self._coolq_api_wrapper(func_name, **kwargs)
|
||||
return await self._coolq_api_wrapper(func_name, **kwargs)
|
||||
elif self.repeat_counter < 3:
|
||||
self.deliver_alert_to_master(("Your status is offline.\n" "You may try login with /0_login"))
|
||||
self.repeat_counter += 1
|
||||
|
||||
def check_status_periodically(self, t_event):
|
||||
async def check_status_periodically(self, run_once: bool = False):
|
||||
interval = 300
|
||||
while True:
|
||||
self.logger.debug("Start checking status...")
|
||||
flag = True
|
||||
interval = 300
|
||||
try:
|
||||
flag = self.check_running_status()
|
||||
flag = await self.check_running_status()
|
||||
except CoolQDisconnectedException as e:
|
||||
if self.repeat_counter < 3:
|
||||
self.deliver_alert_to_master(
|
||||
@ -909,10 +868,9 @@ class GoCQHttp(BaseClient):
|
||||
self.is_connected = True
|
||||
self.is_logged_in = True
|
||||
self.repeat_counter = 0
|
||||
self.check_features()
|
||||
if t_event is not None and not t_event.is_set():
|
||||
self.check_status_timer = threading.Timer(interval, self.check_status_periodically, [t_event])
|
||||
self.check_status_timer.start()
|
||||
if run_once:
|
||||
return
|
||||
await asyncio.sleep(interval)
|
||||
|
||||
def deliver_alert_to_master(self, message: str):
|
||||
context = {
|
||||
@ -922,8 +880,8 @@ class GoCQHttp(BaseClient):
|
||||
}
|
||||
self.send_msg_to_master(context)
|
||||
|
||||
def update_friend_list(self):
|
||||
self.friend_list = self.coolq_api_query("get_friend_list")
|
||||
async def update_friend_list(self):
|
||||
self.friend_list = await self.coolq_api_query("get_friend_list")
|
||||
if self.friend_list:
|
||||
self.logger.debug("Update friend list completed. Entries: %s", len(self.friend_list))
|
||||
for friend in self.friend_list:
|
||||
@ -933,8 +891,8 @@ class GoCQHttp(BaseClient):
|
||||
else:
|
||||
self.logger.warning("Failed to update friend list")
|
||||
|
||||
def update_group_list(self):
|
||||
self.group_list = self.coolq_api_query("get_group_list")
|
||||
async def update_group_list(self):
|
||||
self.group_list = await self.coolq_api_query("get_group_list")
|
||||
if self.group_list:
|
||||
self.logger.debug("Update group list completed. Entries: %s", len(self.group_list))
|
||||
for group in self.group_list:
|
||||
@ -942,13 +900,14 @@ class GoCQHttp(BaseClient):
|
||||
else:
|
||||
self.logger.warning("Failed to update group list")
|
||||
|
||||
def update_contacts_periodically(self, t_event):
|
||||
self.logger.debug("Start updating friend & group list")
|
||||
async def update_contacts_periodically(self, run_once: bool = False):
|
||||
interval = 1800
|
||||
while True:
|
||||
self.logger.debug("Start updating friend & group list")
|
||||
if self.is_connected and self.is_logged_in:
|
||||
try:
|
||||
self.update_friend_list()
|
||||
self.update_group_list()
|
||||
await self.update_friend_list()
|
||||
await self.update_group_list()
|
||||
except CoolQAPIFailureException as ex:
|
||||
if (ex.status_code) == 200 and (ex.retcode) == 104 and self.update_repeat_counter < 3:
|
||||
self.send_cookie_expired_alarm()
|
||||
@ -958,13 +917,13 @@ class GoCQHttp(BaseClient):
|
||||
else:
|
||||
self.update_repeat_counter = 0
|
||||
self.logger.debug("Update completed")
|
||||
if t_event is not None and not t_event.is_set():
|
||||
self.update_contacts_timer = threading.Timer(interval, self.update_contacts_periodically, [t_event])
|
||||
self.update_contacts_timer.start()
|
||||
if run_once:
|
||||
return
|
||||
await asyncio.sleep(interval)
|
||||
|
||||
def get_friend_remark(self, uid):
|
||||
async def get_friend_remark(self, uid):
|
||||
if (not self.friend_list) or (uid not in self.friend_dict):
|
||||
self.update_friend_list()
|
||||
await self.update_friend_list()
|
||||
if uid not in self.friend_dict:
|
||||
return None # I don't think you have such a friend
|
||||
return self.friend_dict[uid]["remark"]
|
||||
@ -972,7 +931,7 @@ class GoCQHttp(BaseClient):
|
||||
def send_efb_group_notice(self, context):
|
||||
context["message_type"] = "group"
|
||||
self.logger.debug(repr(context))
|
||||
chat = self.chat_manager.build_efb_chat_as_group(context)
|
||||
chat = asyncio.run(self.chat_manager.build_efb_chat_as_group(context))
|
||||
try:
|
||||
author = chat.get_member(SystemChatMember.SYSTEM_ID)
|
||||
except KeyError:
|
||||
@ -1014,8 +973,8 @@ class GoCQHttp(BaseClient):
|
||||
# As the old saying goes
|
||||
# A programmer spent 20% of time on coding
|
||||
# while the rest 80% on considering a variable/function/class name
|
||||
def get_external_group_info(self, group_id): # Special thanks to @lwl12 for thinking of this name
|
||||
res = self.coolq_api_query("get_group_info", group_id=group_id)
|
||||
async def get_external_group_info(self, group_id): # Special thanks to @lwl12 for thinking of this name
|
||||
res = await self.coolq_api_query("get_group_info", group_id=group_id)
|
||||
return res
|
||||
|
||||
def send_status(self, status: "Status"):
|
||||
@ -1024,15 +983,15 @@ class GoCQHttp(BaseClient):
|
||||
raise EFBMessageError(("You can only recall your own messages."))
|
||||
try:
|
||||
uid_type = status.message.uid.split("_")
|
||||
self.recall_message(uid_type[1])
|
||||
asyncio.run(self.recall_message(uid_type[1]))
|
||||
except CoolQAPIFailureException:
|
||||
raise EFBMessageError(("Failed to recall the message. This message may have already expired."))
|
||||
else:
|
||||
raise EFBOperationNotSupported()
|
||||
# todo
|
||||
|
||||
def recall_message(self, message_id):
|
||||
self.coolq_api_query("delete_msg", message_id=message_id)
|
||||
async def recall_message(self, message_id):
|
||||
await self.coolq_api_query("delete_msg", message_id=message_id)
|
||||
|
||||
def send_cookie_expired_alarm(self):
|
||||
self.deliver_alert_to_master(
|
||||
@ -1046,18 +1005,18 @@ class GoCQHttp(BaseClient):
|
||||
)
|
||||
)
|
||||
|
||||
def process_friend_request(self, result, flag):
|
||||
async def process_friend_request(self, result, flag):
|
||||
res = "true" if result == "accept" else "false"
|
||||
try:
|
||||
self.coolq_api_query("set_friend_add_request", approve=res, flag=flag)
|
||||
await self.coolq_api_query("set_friend_add_request", approve=res, flag=flag)
|
||||
except CoolQAPIFailureException as e:
|
||||
return ("Failed to process request! Error Message:\n") + getattr(e, "message", repr(e))
|
||||
return "Done"
|
||||
|
||||
def process_group_request(self, result, flag, sub_type):
|
||||
async def process_group_request(self, result, flag, sub_type):
|
||||
res = "true" if result == "accept" else "false"
|
||||
try:
|
||||
self.coolq_api_query("set_group_add_request", approve=res, flag=flag, sub_type=sub_type)
|
||||
await self.coolq_api_query("set_group_add_request", approve=res, flag=flag, sub_type=sub_type)
|
||||
except CoolQAPIFailureException as e:
|
||||
return ("Failed to process request! Error Message:\n") + getattr(e, "message", repr(e))
|
||||
return "Done"
|
||||
@ -1076,15 +1035,15 @@ class GoCQHttp(BaseClient):
|
||||
efb_msg.uid = str(context["user_id"]) + "_" + str(uuid.uuid4()) + "_" + str(1)
|
||||
efb_msg.text = "Sent a file\n{}".format(context["file"]["name"])
|
||||
if context["uid_prefix"] == "offline_file":
|
||||
efb_msg.chat = self.chat_manager.build_efb_chat_as_private(context)
|
||||
efb_msg.chat = asyncio.run(self.chat_manager.build_efb_chat_as_private(context))
|
||||
elif context["uid_prefix"] == "group_upload":
|
||||
efb_msg.chat = self.chat_manager.build_efb_chat_as_group(context)
|
||||
efb_msg.author = self.chat_manager.build_or_get_efb_member(efb_msg.chat, context)
|
||||
efb_msg.chat = asyncio.run(self.chat_manager.build_efb_chat_as_group(context))
|
||||
efb_msg.author = asyncio.run(self.chat_manager.build_or_get_efb_member(efb_msg.chat, context))
|
||||
efb_msg.deliver_to = coordinator.master
|
||||
async_send_messages_to_master(efb_msg)
|
||||
|
||||
def async_download_group_file(self, context, group_id, file_id, busid):
|
||||
file = self.coolq_api_query("get_group_file_url", group_id=group_id, file_id=file_id, busid=busid)
|
||||
async def async_download_group_file(self, context, group_id, file_id, busid):
|
||||
file = await self.coolq_api_query("get_group_file_url", group_id=group_id, file_id=file_id, busid=busid)
|
||||
download_url = file["url"]
|
||||
self.async_download_file(context, download_url)
|
||||
|
||||
@ -1098,32 +1057,35 @@ class GoCQHttp(BaseClient):
|
||||
return download_group_avatar("")
|
||||
|
||||
def get_chats(self):
|
||||
qq_chats = self.get_friends()
|
||||
group_chats = self.get_groups()
|
||||
return qq_chats + group_chats
|
||||
async def _get_chats():
|
||||
return await asyncio.gather(self.get_friends(), self.get_groups())
|
||||
|
||||
friend_chats, group_chats = asyncio.run(_get_chats())
|
||||
return friend_chats + group_chats
|
||||
|
||||
def get_chat(self, chat_uid: ChatID) -> "Chat":
|
||||
# todo what is member_uid used for?
|
||||
chat_type = chat_uid.split("_")
|
||||
if chat_type[0] == "private":
|
||||
qq_uid = int(chat_type[1])
|
||||
remark = self.get_friend_remark(qq_uid)
|
||||
remark = asyncio.run(self.get_friend_remark(qq_uid))
|
||||
context: Dict[str, Any] = {"user_id": qq_uid}
|
||||
if remark is not None:
|
||||
context["alias"] = remark
|
||||
return self.chat_manager.build_efb_chat_as_private(context)
|
||||
return asyncio.run(self.chat_manager.build_efb_chat_as_private(context))
|
||||
elif chat_type[0] == "group":
|
||||
group_id = int(chat_type[1])
|
||||
context = {"message_type": "group", "group_id": group_id}
|
||||
return self.chat_manager.build_efb_chat_as_group(context, update_member=True)
|
||||
return asyncio.run(self.chat_manager.build_efb_chat_as_group(context, update_member=True))
|
||||
elif chat_type[0] == "discuss":
|
||||
discuss_id = int(chat_type[1])
|
||||
context = {"message_type": "discuss", "discuss_id": discuss_id}
|
||||
return self.chat_manager.build_efb_chat_as_group(context)
|
||||
return asyncio.run(self.chat_manager.build_efb_chat_as_group(context))
|
||||
raise EFBChatNotFound()
|
||||
|
||||
def check_self_update(self, t_event):
|
||||
async def check_self_update(self, run_once: bool = False):
|
||||
interval = 60 * 60 * 24
|
||||
while True:
|
||||
latest_version = self.channel.check_updates()
|
||||
if latest_version is not None:
|
||||
self.deliver_alert_to_master(
|
||||
@ -1132,25 +1094,21 @@ class GoCQHttp(BaseClient):
|
||||
"<code>pip3 install --upgrade efb-qq-slave</code>".format(version=latest_version)
|
||||
)
|
||||
else:
|
||||
if t_event is not None and not t_event.is_set():
|
||||
self.self_update_timer = threading.Timer(interval, self.check_self_update, [t_event])
|
||||
self.self_update_timer.start()
|
||||
pass
|
||||
if run_once:
|
||||
return
|
||||
await asyncio.sleep(interval)
|
||||
|
||||
def poll(self):
|
||||
self.check_self_update(threading.Event())
|
||||
asyncio.run(self.check_self_update(run_once=True))
|
||||
self.run_instance(
|
||||
host=self.client_config["host"],
|
||||
port=self.client_config["port"],
|
||||
debug=False,
|
||||
)
|
||||
self.logger.debug("EQS gracefully shut down")
|
||||
|
||||
def stop_polling(self):
|
||||
self.update_contacts_timer.cancel()
|
||||
self.check_status_timer.cancel()
|
||||
self.self_update_timer.cancel()
|
||||
cherrypy.engine.exit()
|
||||
|
||||
def check_features(self):
|
||||
self.can_send_image = self.coolq_api_query("can_send_image")["yes"]
|
||||
self.can_send_voice = self.coolq_api_query("can_send_record")["yes"]
|
||||
self.logger.debug("Gracefully stopping QQ Slave")
|
||||
self.shutdown_event.set()
|
||||
self.loop.stop()
|
||||
self.t.join()
|
||||
|
@ -1,3 +1,4 @@
|
||||
import asyncio
|
||||
import base64
|
||||
import html
|
||||
import json
|
||||
@ -218,14 +219,14 @@ class QQMsgProcessor:
|
||||
else:
|
||||
return self.qq_text_simple_wrapper(text, at_list)
|
||||
except Exception:
|
||||
return self.qq_group_broadcast_alternative_wrapper(data, chat)
|
||||
return asyncio.run(self.qq_group_broadcast_alternative_wrapper(data, chat))
|
||||
|
||||
def qq_group_broadcast_alternative_wrapper(self, data, chat: Chat):
|
||||
async def qq_group_broadcast_alternative_wrapper(self, data, chat: Chat):
|
||||
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_raw_data = await 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"])
|
||||
|
@ -1,14 +1,12 @@
|
||||
import logging
|
||||
import tempfile
|
||||
import urllib.request
|
||||
from gettext import translation
|
||||
from typing import IO, Optional
|
||||
from urllib.error import ContentTooShortError, HTTPError, URLError
|
||||
|
||||
import pilk
|
||||
import pydub
|
||||
from ehforwarderbot import Message, coordinator
|
||||
from pkg_resources import resource_filename
|
||||
|
||||
# created by JogleLew and jqqqqqqqqqq, optimized based on Tim's emoji support, updated by xzsk2 to mobileqq v8.8.11
|
||||
qq_emoji_list = {
|
||||
@ -640,13 +638,6 @@ qq_sface_list = {
|
||||
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) -> Optional[IO]: # Download image from QQ
|
||||
@ -715,7 +706,7 @@ def download_file(download_url):
|
||||
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)
|
||||
return ("Error occurs when downloading files: ") + str(e)
|
||||
else:
|
||||
if file.seek(0, 2) <= 0:
|
||||
raise EOFError("File downloaded is Empty")
|
||||
@ -732,7 +723,7 @@ def download_user_avatar(uid: str):
|
||||
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)
|
||||
return ("Error occurs when downloading files: ") + str(e)
|
||||
if file.seek(0, 2) <= 0:
|
||||
raise EOFError("File downloaded is Empty")
|
||||
file.seek(0)
|
||||
@ -748,7 +739,7 @@ def download_group_avatar(uid: str):
|
||||
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)
|
||||
return ("Error occurs when downloading files: ") + str(e)
|
||||
if file.seek(0, 2) <= 0:
|
||||
raise EOFError("File downloaded is Empty")
|
||||
file.seek(0)
|
||||
|
@ -1,3 +1,3 @@
|
||||
from . import GoCQHttp # noqa: F401
|
||||
|
||||
__version__ = "2.2.3"
|
||||
__version__ = "3.0.0"
|
||||
|
228
pdm.lock
228
pdm.lock
@ -1,3 +1,30 @@
|
||||
[[package]]
|
||||
name = "aiocqhttp"
|
||||
version = "1.4.3"
|
||||
requires_python = ">=3.7"
|
||||
summary = "A Python SDK with async I/O for OneBot (CQHTTP)."
|
||||
dependencies = [
|
||||
"Quart<1.0,>=0.17",
|
||||
"httpx<1.0,>=0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aiofiles"
|
||||
version = "0.8.0"
|
||||
requires_python = ">=3.6,<4.0"
|
||||
summary = "File support for asyncio."
|
||||
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "3.5.0"
|
||||
requires_python = ">=3.6.2"
|
||||
summary = "High level compatibility layer for multiple asynchronous event loop implementations"
|
||||
dependencies = [
|
||||
"idna>=2.8",
|
||||
"sniffio>=1.1",
|
||||
"typing-extensions; python_version < \"3.8\"",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "apscheduler"
|
||||
version = "3.6.3"
|
||||
@ -15,6 +42,11 @@ version = "0.2.1"
|
||||
requires_python = ">=3.6"
|
||||
summary = "Backport of the standard library zoneinfo module"
|
||||
|
||||
[[package]]
|
||||
name = "blinker"
|
||||
version = "1.4"
|
||||
summary = "Fast, simple object-to-object and broadcast signaling"
|
||||
|
||||
[[package]]
|
||||
name = "bullet"
|
||||
version = "2.2.0"
|
||||
@ -171,6 +203,53 @@ version = "0.18.2"
|
||||
requires_python = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
summary = "Clean single-source support for Python 3 and 2"
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.12.0"
|
||||
requires_python = ">=3.6"
|
||||
summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "4.1.0"
|
||||
requires_python = ">=3.6.1"
|
||||
summary = "HTTP/2 State-Machine based protocol implementation"
|
||||
dependencies = [
|
||||
"hpack<5,>=4.0",
|
||||
"hyperframe<7,>=6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hpack"
|
||||
version = "4.0.0"
|
||||
requires_python = ">=3.6.1"
|
||||
summary = "Pure-Python HPACK header compression"
|
||||
|
||||
[[package]]
|
||||
name = "httpcore"
|
||||
version = "0.14.7"
|
||||
requires_python = ">=3.6"
|
||||
summary = "A minimal low-level HTTP client."
|
||||
dependencies = [
|
||||
"anyio==3.*",
|
||||
"certifi",
|
||||
"h11<0.13,>=0.11",
|
||||
"sniffio==1.*",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.22.0"
|
||||
requires_python = ">=3.6"
|
||||
summary = "The next generation HTTP client."
|
||||
dependencies = [
|
||||
"certifi",
|
||||
"charset-normalizer",
|
||||
"httpcore<0.15.0,>=0.14.5",
|
||||
"rfc3986[idna2008]<2,>=1.3",
|
||||
"sniffio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humanize"
|
||||
version = "4.0.0"
|
||||
@ -180,6 +259,26 @@ dependencies = [
|
||||
"importlib-metadata; python_version < \"3.8\"",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hypercorn"
|
||||
version = "0.13.2"
|
||||
requires_python = ">=3.7"
|
||||
summary = "A ASGI Server based on Hyper libraries and inspired by Gunicorn"
|
||||
dependencies = [
|
||||
"h11",
|
||||
"h2>=3.1.0",
|
||||
"priority",
|
||||
"toml",
|
||||
"typing-extensions>=3.7.4; python_version < \"3.8\"",
|
||||
"wsproto>=0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyperframe"
|
||||
version = "6.0.1"
|
||||
requires_python = ">=3.6.1"
|
||||
summary = "HTTP/2 framing layer for Python"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.3"
|
||||
@ -308,6 +407,12 @@ dependencies = [
|
||||
"tempora>=1.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "priority"
|
||||
version = "2.0.0"
|
||||
requires_python = ">=3.6.1"
|
||||
summary = "A pure-Python implementation of the HTTP/2 priority tree"
|
||||
|
||||
[[package]]
|
||||
name = "pydub"
|
||||
version = "0.25.1"
|
||||
@ -358,6 +463,25 @@ version = "6.0"
|
||||
requires_python = ">=3.6"
|
||||
summary = "YAML parser and emitter for Python"
|
||||
|
||||
[[package]]
|
||||
name = "quart"
|
||||
version = "0.17.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "A Python ASGI web microframework with the same API as Flask"
|
||||
dependencies = [
|
||||
"aiofiles",
|
||||
"blinker",
|
||||
"click",
|
||||
"hypercorn>=0.11.2",
|
||||
"importlib-metadata; python_version < \"3.8\"",
|
||||
"itsdangerous",
|
||||
"jinja2",
|
||||
"markupsafe",
|
||||
"toml",
|
||||
"typing-extensions; python_version < \"3.8\"",
|
||||
"werkzeug>=2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.27.1"
|
||||
@ -378,6 +502,21 @@ dependencies = [
|
||||
"six>=1.7.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfc3986"
|
||||
version = "1.5.0"
|
||||
summary = "Validating URI References per RFC 3986"
|
||||
|
||||
[[package]]
|
||||
name = "rfc3986"
|
||||
version = "1.5.0"
|
||||
extras = ["idna2008"]
|
||||
summary = "Validating URI References per RFC 3986"
|
||||
dependencies = [
|
||||
"idna",
|
||||
"rfc3986<2,>=1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruamel.yaml"
|
||||
version = "0.17.20"
|
||||
@ -405,6 +544,12 @@ version = "1.16.0"
|
||||
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
summary = "Python 2 and 3 compatibility utilities"
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.2.0"
|
||||
requires_python = ">=3.5"
|
||||
summary = "Sniff out which async library your code is running under"
|
||||
|
||||
[[package]]
|
||||
name = "tempora"
|
||||
version = "5.0.1"
|
||||
@ -415,6 +560,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 = "tornado"
|
||||
version = "6.1"
|
||||
@ -456,6 +607,15 @@ version = "2.0.2"
|
||||
requires_python = ">=3.6"
|
||||
summary = "The comprehensive WSGI web application library."
|
||||
|
||||
[[package]]
|
||||
name = "wsproto"
|
||||
version = "1.1.0"
|
||||
requires_python = ">=3.7.0"
|
||||
summary = "WebSockets state-machine based protocol implementation"
|
||||
dependencies = [
|
||||
"h11<1,>=0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zc.lockfile"
|
||||
version = "2.0"
|
||||
@ -472,9 +632,20 @@ summary = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
|
||||
[metadata]
|
||||
lock_version = "3.1"
|
||||
content_hash = "sha256:978b5cbd8b5efd554ae429512f6909ca9e5be239ca11531f954c51c274b984cc"
|
||||
content_hash = "sha256:31a92e98cb57c2c24915316aca43a1756ad1d0ad8d5ffdd78631463724d61bd5"
|
||||
|
||||
[metadata.files]
|
||||
"aiocqhttp 1.4.3" = [
|
||||
{file = "aiocqhttp-1.4.3.tar.gz", hash = "sha256:197de394f97b798515d643814660a41d5ec470bd0b0608682d1e4b553e486768"},
|
||||
]
|
||||
"aiofiles 0.8.0" = [
|
||||
{file = "aiofiles-0.8.0-py3-none-any.whl", hash = "sha256:7a973fc22b29e9962d0897805ace5856e6a566ab1f0c8e5c91ff6c866519c937"},
|
||||
{file = "aiofiles-0.8.0.tar.gz", hash = "sha256:8334f23235248a3b2e83b2c3a78a22674f39969b96397126cc93664d9a901e59"},
|
||||
]
|
||||
"anyio 3.5.0" = [
|
||||
{file = "anyio-3.5.0-py3-none-any.whl", hash = "sha256:b5fa16c5ff93fa1046f2eeb5bbff2dad4d3514d6cda61d02816dba34fa8c3c2e"},
|
||||
{file = "anyio-3.5.0.tar.gz", hash = "sha256:a0aeffe2fb1fdf374a8e4b471444f0f3ac4fb9f5a5b542b48824475e0042a5a6"},
|
||||
]
|
||||
"apscheduler 3.6.3" = [
|
||||
{file = "APScheduler-3.6.3-py2.py3-none-any.whl", hash = "sha256:e8b1ecdb4c7cb2818913f766d5898183c7cb8936680710a4d3a966e02262e526"},
|
||||
{file = "APScheduler-3.6.3.tar.gz", hash = "sha256:3bb5229eed6fbbdafc13ce962712ae66e175aa214c69bed35a06bffcf0c5e244"},
|
||||
@ -497,6 +668,9 @@ content_hash = "sha256:978b5cbd8b5efd554ae429512f6909ca9e5be239ca11531f954c51c27
|
||||
{file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"},
|
||||
{file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"},
|
||||
]
|
||||
"blinker 1.4" = [
|
||||
{file = "blinker-1.4.tar.gz", hash = "sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6"},
|
||||
]
|
||||
"bullet 2.2.0" = [
|
||||
{file = "bullet-2.2.0-py3-none-any.whl", hash = "sha256:d22528deb914ce3ff20a4a000fa5ba37179697f384a0748f90691de8a74cf006"},
|
||||
{file = "bullet-2.2.0.tar.gz", hash = "sha256:dfa0fa81810ad1a9e688815ca04f24af87ff5cdbe803b42fa634b1f50fc9d887"},
|
||||
@ -555,10 +729,38 @@ content_hash = "sha256:978b5cbd8b5efd554ae429512f6909ca9e5be239ca11531f954c51c27
|
||||
"future 0.18.2" = [
|
||||
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
|
||||
]
|
||||
"h11 0.12.0" = [
|
||||
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
|
||||
{file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
|
||||
]
|
||||
"h2 4.1.0" = [
|
||||
{file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"},
|
||||
{file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"},
|
||||
]
|
||||
"hpack 4.0.0" = [
|
||||
{file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"},
|
||||
{file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"},
|
||||
]
|
||||
"httpcore 0.14.7" = [
|
||||
{file = "httpcore-0.14.7-py3-none-any.whl", hash = "sha256:47d772f754359e56dd9d892d9593b6f9870a37aeb8ba51e9a88b09b3d68cfade"},
|
||||
{file = "httpcore-0.14.7.tar.gz", hash = "sha256:7503ec1c0f559066e7e39bc4003fd2ce023d01cf51793e3c173b864eb456ead1"},
|
||||
]
|
||||
"httpx 0.22.0" = [
|
||||
{file = "httpx-0.22.0-py3-none-any.whl", hash = "sha256:e35e83d1d2b9b2a609ef367cc4c1e66fd80b750348b20cc9e19d1952fc2ca3f6"},
|
||||
{file = "httpx-0.22.0.tar.gz", hash = "sha256:d8e778f76d9bbd46af49e7f062467e3157a5a3d2ae4876a4bbfd8a51ed9c9cb4"},
|
||||
]
|
||||
"humanize 4.0.0" = [
|
||||
{file = "humanize-4.0.0-py3-none-any.whl", hash = "sha256:8d86333b8557dacffd4dce1dbe09c81c189e2caf7bb17a970b2212f0f58f10f2"},
|
||||
{file = "humanize-4.0.0.tar.gz", hash = "sha256:ee1f872fdfc7d2ef4a28d4f80ddde9f96d36955b5d6b0dac4bdeb99502bddb00"},
|
||||
]
|
||||
"hypercorn 0.13.2" = [
|
||||
{file = "Hypercorn-0.13.2-py3-none-any.whl", hash = "sha256:ca18f91ab3fa823cbe9e949738f9f2cc07027cd647c80d8f93e4b1a2a175f112"},
|
||||
{file = "Hypercorn-0.13.2.tar.gz", hash = "sha256:6307be5cbdf6ba411967d4661202dc4f79bd511b5d318bc4eed88b09418427f8"},
|
||||
]
|
||||
"hyperframe 6.0.1" = [
|
||||
{file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"},
|
||||
{file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"},
|
||||
]
|
||||
"idna 3.3" = [
|
||||
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
|
||||
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
|
||||
@ -730,6 +932,10 @@ content_hash = "sha256:978b5cbd8b5efd554ae429512f6909ca9e5be239ca11531f954c51c27
|
||||
{file = "portend-3.1.0-py3-none-any.whl", hash = "sha256:9e735cee3a5c1961f09e3f3ba6dc498198c2d70b473d98d0d1504b8d1e7a3d61"},
|
||||
{file = "portend-3.1.0.tar.gz", hash = "sha256:239e3116045ea823f6df87d6168107ad75ccc0590e37242af0cc1e98c5d224e4"},
|
||||
]
|
||||
"priority 2.0.0" = [
|
||||
{file = "priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa"},
|
||||
{file = "priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0"},
|
||||
]
|
||||
"pydub 0.25.1" = [
|
||||
{file = "pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6"},
|
||||
{file = "pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f"},
|
||||
@ -799,6 +1005,10 @@ content_hash = "sha256:978b5cbd8b5efd554ae429512f6909ca9e5be239ca11531f954c51c27
|
||||
{file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
|
||||
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
|
||||
]
|
||||
"quart 0.17.0" = [
|
||||
{file = "Quart-0.17.0-py3-none-any.whl", hash = "sha256:69480e7384935feff1f50293a8cb70c5d31f568af94ed792d043bb368b50bd50"},
|
||||
{file = "Quart-0.17.0.tar.gz", hash = "sha256:2cf213d8b83fa701a83e3b3125e9102a937cefd1e97e9583f22ee2fa79139640"},
|
||||
]
|
||||
"requests 2.27.1" = [
|
||||
{file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
|
||||
{file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
|
||||
@ -806,6 +1016,10 @@ content_hash = "sha256:978b5cbd8b5efd554ae429512f6909ca9e5be239ca11531f954c51c27
|
||||
"retrying 1.3.3" = [
|
||||
{file = "retrying-1.3.3.tar.gz", hash = "sha256:08c039560a6da2fe4f2c426d0766e284d3b736e355f8dd24b37367b0bb41973b"},
|
||||
]
|
||||
"rfc3986 1.5.0" = [
|
||||
{file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
|
||||
{file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
|
||||
]
|
||||
"ruamel.yaml 0.17.20" = [
|
||||
{file = "ruamel.yaml-0.17.20-py3-none-any.whl", hash = "sha256:810eef9c46523a3f77479c66267a4708255ebe806a2d540078408c2227f011af"},
|
||||
{file = "ruamel.yaml-0.17.20.tar.gz", hash = "sha256:4b8a33c1efb2b443a93fcaafcfa4d2e445f8e8c29c528d9f5cdafb7cc9e4004c"},
|
||||
@ -845,10 +1059,18 @@ content_hash = "sha256:978b5cbd8b5efd554ae429512f6909ca9e5be239ca11531f954c51c27
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
"sniffio 1.2.0" = [
|
||||
{file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
|
||||
{file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
|
||||
]
|
||||
"tempora 5.0.1" = [
|
||||
{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"},
|
||||
]
|
||||
"tornado 6.1" = [
|
||||
{file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"},
|
||||
{file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"},
|
||||
@ -912,6 +1134,10 @@ content_hash = "sha256:978b5cbd8b5efd554ae429512f6909ca9e5be239ca11531f954c51c27
|
||||
{file = "Werkzeug-2.0.2-py3-none-any.whl", hash = "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f"},
|
||||
{file = "Werkzeug-2.0.2.tar.gz", hash = "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a"},
|
||||
]
|
||||
"wsproto 1.1.0" = [
|
||||
{file = "wsproto-1.1.0-py3-none-any.whl", hash = "sha256:2218cb57952d90b9fca325c0dcfb08c3bda93e8fd8070b0a17f048e2e47a521b"},
|
||||
{file = "wsproto-1.1.0.tar.gz", hash = "sha256:a2e56bfd5c7cd83c1369d83b5feccd6d37798b74872866e62616e0ecf111bda8"},
|
||||
]
|
||||
"zc.lockfile 2.0" = [
|
||||
{file = "zc.lockfile-2.0-py2.py3-none-any.whl", hash = "sha256:cc33599b549f0c8a248cb72f3bf32d77712de1ff7ee8814312eb6456b42c015f"},
|
||||
{file = "zc.lockfile-2.0.tar.gz", hash = "sha256:307ad78227e48be260e64896ec8886edc7eae22d8ec53e4d528ab5537a83203b"},
|
||||
|
@ -9,8 +9,9 @@ dependencies = [
|
||||
"requests~=2.27.1",
|
||||
"python-magic~=0.4.25",
|
||||
"Pillow~=9.0.1",
|
||||
"cqhttp~=1.3.1",
|
||||
"CherryPy~=18.6.1",
|
||||
"aiocqhttp~=1.4.3",
|
||||
"quart~=0.17.0",
|
||||
"hypercorn~=0.13.2",
|
||||
"pilk~=0.0.2",
|
||||
"pydub~=0.25.1",
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user