Compare commits
56 Commits
Author | SHA1 | Date | |
---|---|---|---|
dd615896bb | |||
9dc07ec55f | |||
|
56363f091f | ||
|
5ed4996905 | ||
|
3fc56b93f5 | ||
|
2a145d2659 | ||
|
39f628a99f | ||
|
0f8b06b129 | ||
|
bf0a04e5fe | ||
|
f94353f469 | ||
8024f4e416 | |||
|
838e759e07 | ||
147f66cd91 | |||
|
32547397c3 | ||
7f14a3cb9e | |||
|
a1781add55 | ||
|
31c757344c | ||
7a3bccf8c0 | |||
a4da2927fa | |||
|
da6e06d1c0 | ||
|
a114d91fee | ||
|
a1b7f88015 | ||
|
10acc91fdb | ||
|
0db46937ac | ||
|
09072071ca | ||
|
1d826405ac | ||
|
7bf80e1787 | ||
|
3202ccd85c | ||
|
d97a2ab2bf | ||
|
b86e558312 | ||
|
265959ddda | ||
|
cd3fdc7d86 | ||
|
3818b6ffb4 | ||
|
b93a613e1b | ||
|
4b23b770fb | ||
|
d1e79d8ed0 | ||
|
2b400f7ed5 | ||
|
d01b459fe8 | ||
|
fb73069820 | ||
|
1478cb7b56 | ||
|
4370e98d30 | ||
|
c8d30db88b | ||
|
2874307dcf | ||
|
f6c508560c | ||
|
3f89606ab6 | ||
|
7d4d3c4a0b | ||
1dc82dcb9e | |||
|
b07c89d06b | ||
|
2c2ff9de1e | ||
|
661dc91104 | ||
|
4c6880b342 | ||
|
aeee9738a4 | ||
|
d9bf0da5a4 | ||
|
4d5dffcd7a | ||
|
a08633715c | ||
|
dfb1421f84 |
@ -1,17 +1,17 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/psf/black
|
||||||
rev: 22.1.0
|
rev: 22.12.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
|
|
||||||
- repo: https://github.com/PyCQA/flake8
|
- repo: https://github.com/PyCQA/flake8
|
||||||
rev: 4.0.1
|
rev: 6.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- flake8-bugbear
|
- flake8-bugbear
|
||||||
|
|
||||||
- repo: https://github.com/PyCQA/isort
|
- repo: https://github.com/PyCQA/isort
|
||||||
rev: 5.10.1
|
rev: 5.11.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: isort
|
- id: isort
|
||||||
|
@ -38,12 +38,8 @@ efb-qq-plugin-go-cqhttp 是 efb-qq-slave 的插件,需要配合 efb-qq-slave
|
|||||||
servers:
|
servers:
|
||||||
# HTTP 通信设置
|
# HTTP 通信设置
|
||||||
- http:
|
- http:
|
||||||
# 是否关闭正向 HTTP 服务器
|
# HTTP监听地址
|
||||||
disabled: false
|
address: 127.0.0.1:5700
|
||||||
# 服务端监听地址
|
|
||||||
host: 127.0.0.1
|
|
||||||
# 服务端监听端口
|
|
||||||
port: 5700
|
|
||||||
# 反向 HTTP 超时时间, 单位秒
|
# 反向 HTTP 超时时间, 单位秒
|
||||||
# 最小值为 5,小于 5 将会忽略本项设置
|
# 最小值为 5,小于 5 将会忽略本项设置
|
||||||
timeout: 5
|
timeout: 5
|
||||||
|
@ -20,10 +20,10 @@ class ChatManager:
|
|||||||
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):
|
async def build_efb_chat_as_private(self, context):
|
||||||
uid = context["user_id"]
|
uid = context["user_id"]
|
||||||
if "sender" not in context or "nickname" not in context["sender"]:
|
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 = ""
|
chat_name = ""
|
||||||
if i:
|
if i:
|
||||||
chat_name = i["nickname"]
|
chat_name = i["nickname"]
|
||||||
@ -37,13 +37,13 @@ class ChatManager:
|
|||||||
)
|
)
|
||||||
return efb_chat
|
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"]
|
member_uid = context["user_id"]
|
||||||
with contextlib.suppress(KeyError):
|
with contextlib.suppress(KeyError):
|
||||||
return chat.get_member(str(member_uid))
|
return chat.get_member(str(member_uid))
|
||||||
chat_name = ""
|
chat_name = ""
|
||||||
if "nickname" not in context:
|
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 = ""
|
chat_name = ""
|
||||||
if i:
|
if i:
|
||||||
chat_name = i["nickname"]
|
chat_name = i["nickname"]
|
||||||
@ -55,20 +55,20 @@ class ChatManager:
|
|||||||
uid=str(member_uid),
|
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
|
is_discuss = context["message_type"] != "group"
|
||||||
chat_uid = context["discuss_id"] if is_discuss else context["group_id"]
|
chat_uid = context["discuss_id"] if is_discuss else context["group_id"]
|
||||||
efb_chat = GroupChat(channel=self.channel, uid=str(chat_uid))
|
efb_chat = GroupChat(channel=self.channel, uid=str(chat_uid))
|
||||||
if not is_discuss:
|
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)
|
i = await self.channel.QQClient.get_group_info(chat_uid)
|
||||||
if i is not None:
|
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:
|
else:
|
||||||
efb_chat.name = str(chat_uid)
|
efb_chat.name = str(chat_uid)
|
||||||
efb_chat.vendor_specific = {"is_discuss": False}
|
efb_chat.vendor_specific = {"is_discuss": False}
|
||||||
if update_member:
|
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:
|
if members:
|
||||||
for member in members:
|
for member in members:
|
||||||
efb_chat.add_member(
|
efb_chat.add_member(
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,37 +1,46 @@
|
|||||||
|
import asyncio
|
||||||
import base64
|
import base64
|
||||||
import html
|
import html
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import magic
|
import magic
|
||||||
from ehforwarderbot import Chat, Message, MsgType
|
from ehforwarderbot import Chat, Message, MsgType
|
||||||
from ehforwarderbot.message import LinkAttribute, LocationAttribute, Substitutions
|
from ehforwarderbot.message import LinkAttribute, LocationAttribute, Substitutions
|
||||||
|
|
||||||
from . import GoCQHttp
|
|
||||||
from .Utils import cq_get_image, download_file, download_voice
|
from .Utils import cq_get_image, download_file, download_voice
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .GoCQHttp import GoCQHttp
|
||||||
|
|
||||||
|
|
||||||
class QQMsgProcessor:
|
class QQMsgProcessor:
|
||||||
inst: GoCQHttp
|
inst: "GoCQHttp"
|
||||||
logger: logging.Logger = logging.getLogger(__name__)
|
logger: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def __init__(self, instance: GoCQHttp):
|
def __init__(self, instance: "GoCQHttp"):
|
||||||
self.inst = instance
|
self.inst = instance
|
||||||
self._ = instance._
|
|
||||||
pass
|
|
||||||
|
|
||||||
def qq_image_wrapper(self, data, chat: Chat = None):
|
async def qq_image_wrapper(self, data, _: Chat = None):
|
||||||
efb_msg = Message()
|
efb_msg = Message()
|
||||||
if "url" not in data:
|
if "url" not in data:
|
||||||
efb_msg.type = MsgType.Text
|
efb_msg.type = MsgType.Text
|
||||||
efb_msg.text = self._("[Image Source missing]")
|
efb_msg.text = "[Image Source missing]"
|
||||||
return [efb_msg]
|
return [efb_msg]
|
||||||
|
|
||||||
efb_msg.file = cq_get_image(data["url"])
|
# flash picture
|
||||||
|
if data.get("type", "") == "flash":
|
||||||
|
data["url"] = (
|
||||||
|
f"https://gchat.qpic.cn/gchatpic_new/1/1-1-" f'{data["file"].replace(".image", "").upper()}/0?term=3%27'
|
||||||
|
)
|
||||||
|
efb_msg.text = "Send a flash picture."
|
||||||
|
|
||||||
|
efb_msg.file = await cq_get_image(data["url"])
|
||||||
if efb_msg.file is None:
|
if efb_msg.file is None:
|
||||||
efb_msg.type = MsgType.Text
|
efb_msg.type = MsgType.Text
|
||||||
efb_msg.text = self._("[Download image failed, please check on your QQ client]")
|
efb_msg.text = "[Download image failed, please check on your QQ client]"
|
||||||
return [efb_msg]
|
return [efb_msg]
|
||||||
|
|
||||||
efb_msg.type = MsgType.Image
|
efb_msg.type = MsgType.Image
|
||||||
@ -46,11 +55,11 @@ class QQMsgProcessor:
|
|||||||
efb_msg.type = MsgType.Animation
|
efb_msg.type = MsgType.Animation
|
||||||
return [efb_msg]
|
return [efb_msg]
|
||||||
|
|
||||||
def qq_record_wrapper(self, data, chat: Chat = None): # Experimental!
|
async def qq_record_wrapper(self, data, _: Chat = None): # Experimental!
|
||||||
efb_msg = Message()
|
efb_msg = Message()
|
||||||
try:
|
try:
|
||||||
efb_msg.type = MsgType.Audio
|
efb_msg.type = MsgType.Audio
|
||||||
efb_msg.file = download_voice(data["url"])
|
efb_msg.file = await download_voice(data["url"])
|
||||||
mime = magic.from_file(efb_msg.file.name, mime=True)
|
mime = magic.from_file(efb_msg.file.name, mime=True)
|
||||||
if isinstance(mime, bytes):
|
if isinstance(mime, bytes):
|
||||||
mime = mime.decode()
|
mime = mime.decode()
|
||||||
@ -58,11 +67,11 @@ class QQMsgProcessor:
|
|||||||
efb_msg.mime = mime
|
efb_msg.mime = mime
|
||||||
except Exception:
|
except Exception:
|
||||||
efb_msg.type = MsgType.Unsupported
|
efb_msg.type = MsgType.Unsupported
|
||||||
efb_msg.text = self._("[Voice Message] Please check it on your QQ")
|
efb_msg.text = "[Voice Message] Please check it on your QQ"
|
||||||
logging.getLogger(__name__).exception("Failed to download voice")
|
logging.getLogger(__name__).exception("Failed to download voice")
|
||||||
return [efb_msg]
|
return [efb_msg]
|
||||||
|
|
||||||
def qq_share_wrapper(self, data, chat: Chat = None):
|
def qq_share_wrapper(self, data, _: Chat = None):
|
||||||
efb_msg = Message(
|
efb_msg = Message(
|
||||||
type=MsgType.Link,
|
type=MsgType.Link,
|
||||||
text="",
|
text="",
|
||||||
@ -75,7 +84,7 @@ class QQMsgProcessor:
|
|||||||
)
|
)
|
||||||
return [efb_msg]
|
return [efb_msg]
|
||||||
|
|
||||||
def qq_location_wrapper(self, data, chat: Chat = None):
|
def qq_location_wrapper(self, data, _: Chat = None):
|
||||||
efb_msg = Message(
|
efb_msg = Message(
|
||||||
text=data["content"],
|
text=data["content"],
|
||||||
type=MsgType.Location,
|
type=MsgType.Location,
|
||||||
@ -83,23 +92,23 @@ class QQMsgProcessor:
|
|||||||
)
|
)
|
||||||
return [efb_msg]
|
return [efb_msg]
|
||||||
|
|
||||||
def qq_shake_wrapper(self, data, chat: Chat = None):
|
def qq_shake_wrapper(self, _, __: Chat = None):
|
||||||
efb_msg = Message(type=MsgType.Text, text=self._("[Your friend shakes you!]"))
|
efb_msg = Message(type=MsgType.Text, text=("[Your friend shakes you!]"))
|
||||||
return [efb_msg]
|
return [efb_msg]
|
||||||
|
|
||||||
def qq_contact_wrapper(self, data, chat: Chat = None):
|
def qq_contact_wrapper(self, data, _: Chat = None):
|
||||||
uid = data["id"]
|
uid = data["id"]
|
||||||
contact_type = data["type"]
|
contact_type = data["type"]
|
||||||
efb_msg = Message(
|
efb_msg = Message(
|
||||||
type=MsgType.Text,
|
type=MsgType.Text,
|
||||||
text=self._("Chat Recommendation Received\nID: {}\nType: {}").format(uid, contact_type),
|
text=("Chat Recommendation Received\nID: {}\nType: {}").format(uid, contact_type),
|
||||||
)
|
)
|
||||||
return [efb_msg]
|
return [efb_msg]
|
||||||
|
|
||||||
def qq_bface_wrapper(self, data, chat: Chat = None):
|
def qq_bface_wrapper(self, _, __: Chat = None):
|
||||||
efb_msg = Message(
|
efb_msg = Message(
|
||||||
type=MsgType.Unsupported,
|
type=MsgType.Unsupported,
|
||||||
text=self._("[Here comes the BigFace Emoji, please check it on your phone]"),
|
text=("[Here comes the BigFace Emoji, please check it on your phone]"),
|
||||||
)
|
)
|
||||||
return [efb_msg]
|
return [efb_msg]
|
||||||
|
|
||||||
@ -107,33 +116,33 @@ class QQMsgProcessor:
|
|||||||
# todo this function's maybe not necessary?
|
# todo this function's maybe not necessary?
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def qq_sign_wrapper(self, data, chat: Chat = None):
|
def qq_sign_wrapper(self, data, _: Chat = None):
|
||||||
location = self._("at {}").format(data["location"]) if "location" in data else self._("at Unknown Place")
|
location = ("at {}").format(data["location"]) if "location" in data else ("at Unknown Place")
|
||||||
title = "" if "title" not in data else (self._("with title {}").format(data["title"]))
|
title = "" if "title" not in data else (("with title {}").format(data["title"]))
|
||||||
efb_msg = Message(
|
efb_msg = Message(
|
||||||
type=MsgType.Text,
|
type=MsgType.Text,
|
||||||
text=self._("signed in {location} {title}").format(title=title, location=location),
|
text=("signed in {location} {title}").format(title=title, location=location),
|
||||||
)
|
)
|
||||||
return [efb_msg]
|
return [efb_msg]
|
||||||
|
|
||||||
def qq_rich_wrapper(self, data: dict, chat: Chat = None): # Buggy, Help needed
|
async def qq_rich_wrapper(self, data: dict, chat: Chat = None): # Buggy, Help needed
|
||||||
efb_messages = list()
|
efb_messages = list()
|
||||||
efb_msg = Message(
|
efb_msg = Message(
|
||||||
type=MsgType.Unsupported,
|
type=MsgType.Unsupported,
|
||||||
text=self._("[Here comes the Rich Text, dumping...] \n"),
|
text=("[Here comes the Rich Text, dumping...] \n"),
|
||||||
)
|
)
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
efb_msg.text += key + ": " + value + "\n"
|
efb_msg.text += key + ": " + value + "\n"
|
||||||
efb_messages.append(efb_msg)
|
efb_messages.append(efb_msg)
|
||||||
# Optimizations for rich messages
|
# Optimizations for rich messages
|
||||||
# Group Broadcast
|
# Group Broadcast
|
||||||
_ = self.qq_group_broadcast_wrapper(data, chat)
|
_ = await self.qq_group_broadcast_wrapper(data, chat)
|
||||||
if _ is not None:
|
if _ is not None:
|
||||||
efb_messages.append(_)
|
efb_messages.append(_)
|
||||||
|
|
||||||
return efb_messages
|
return efb_messages
|
||||||
|
|
||||||
def qq_music_wrapper(self, data, chat: Chat = None):
|
def qq_music_wrapper(self, data, _: Chat = None):
|
||||||
efb_msg = Message()
|
efb_msg = Message()
|
||||||
if data["type"] == "163": # Netease Cloud Music
|
if data["type"] == "163": # Netease Cloud Music
|
||||||
efb_msg.type = MsgType.Text
|
efb_msg.type = MsgType.Text
|
||||||
@ -183,7 +192,7 @@ class QQMsgProcessor:
|
|||||||
efb_msg.filename = data["filename"]
|
efb_msg.filename = data["filename"]
|
||||||
return efb_msg
|
return efb_msg
|
||||||
|
|
||||||
def qq_group_broadcast_wrapper(self, data, chat: Chat = None):
|
async def qq_group_broadcast_wrapper(self, data, chat: Chat):
|
||||||
try:
|
try:
|
||||||
at_list = {}
|
at_list = {}
|
||||||
content_data = json.loads(data["content"])
|
content_data = json.loads(data["content"])
|
||||||
@ -202,21 +211,21 @@ class QQMsgProcessor:
|
|||||||
data["url"] = "http://gdynamic.qpic.cn/gdynamic/{}/628".format(
|
data["url"] = "http://gdynamic.qpic.cn/gdynamic/{}/628".format(
|
||||||
content_data["mannounce"]["pic"][0]["url"]
|
content_data["mannounce"]["pic"][0]["url"]
|
||||||
)
|
)
|
||||||
efb_message = self.qq_image_wrapper(data)[0]
|
efb_message = (await self.qq_image_wrapper(data))[0]
|
||||||
efb_message.text = text
|
efb_message.text = text
|
||||||
efb_message.substitutions = Substitutions(at_list)
|
efb_message.substitutions = Substitutions(at_list)
|
||||||
return [efb_message]
|
return [efb_message]
|
||||||
else:
|
else:
|
||||||
return self.qq_text_simple_wrapper(text, at_list)
|
return self.qq_text_simple_wrapper(text, at_list)
|
||||||
except Exception:
|
except Exception:
|
||||||
return self.qq_group_broadcast_alternative_wrapper(data)
|
return asyncio.run(self.qq_group_broadcast_alternative_wrapper(data, chat))
|
||||||
|
|
||||||
def qq_group_broadcast_alternative_wrapper(self, data, chat: Chat = None):
|
async def qq_group_broadcast_alternative_wrapper(self, data, chat: Chat):
|
||||||
try:
|
try:
|
||||||
at_list = {}
|
at_list = {}
|
||||||
content_data = json.loads(data["content"])
|
content_data = json.loads(data["content"])
|
||||||
group_id = content_data["mannounce"]["gc"]
|
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)
|
notice_data = json.loads(notice_raw_data)
|
||||||
title_data = html.unescape(notice_data[0]["msg"]["title"])
|
title_data = html.unescape(notice_data[0]["msg"]["title"])
|
||||||
text_data = html.unescape(notice_data[0]["msg"]["text"])
|
text_data = html.unescape(notice_data[0]["msg"]["text"])
|
||||||
@ -231,7 +240,7 @@ class QQMsgProcessor:
|
|||||||
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
|
# 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 = (await self.qq_image_wrapper(data))[0]
|
||||||
efb_message.text = text
|
efb_message.text = text
|
||||||
efb_message.substitutions = Substitutions(at_list)
|
efb_message.substitutions = Substitutions(at_list)
|
||||||
return [efb_message]
|
return [efb_message]
|
||||||
@ -240,13 +249,13 @@ class QQMsgProcessor:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def qq_xml_wrapper(self, data, chat: Chat = None):
|
def qq_xml_wrapper(self, data, _: Chat = None):
|
||||||
efb_msg = Message()
|
efb_msg = Message()
|
||||||
efb_msg.type = MsgType.Text
|
efb_msg.type = MsgType.Text
|
||||||
efb_msg.text = data["data"]
|
efb_msg.text = data["data"]
|
||||||
return [efb_msg]
|
return [efb_msg]
|
||||||
|
|
||||||
def qq_json_wrapper(self, data, chat: Chat = None):
|
def qq_json_wrapper(self, data, _: Chat = None):
|
||||||
efb_msg = Message()
|
efb_msg = Message()
|
||||||
efb_msg.type = MsgType.Text
|
efb_msg.type = MsgType.Text
|
||||||
efb_msg.text = data["data"]
|
efb_msg.text = data["data"]
|
||||||
@ -289,6 +298,17 @@ class QQMsgProcessor:
|
|||||||
preview=meta_detail1["preview"],
|
preview=meta_detail1["preview"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Tencent group photo upload
|
||||||
|
elif dict_data["app"] == "com.tencent.groupphoto":
|
||||||
|
album_name = dict_data["meta"]["albumData"]["title"]
|
||||||
|
photo_urls = ["https://" + i["url"] for i in dict_data["meta"]["albumData"]["pics"]]
|
||||||
|
efb_msg.text = "【群相册】\n\n{}\n\n{}".format(album_name, "\n".join(photo_urls))
|
||||||
|
|
||||||
|
# Tencent group photo album create
|
||||||
|
elif dict_data["app"] == "com.tencent.qzone.albumShare":
|
||||||
|
album_name = dict_data["meta"]["albumData"]["title"]
|
||||||
|
efb_msg.text = "【群相册】\n\n{}".format(album_name)
|
||||||
|
|
||||||
# Shared third-party Apps
|
# Shared third-party Apps
|
||||||
elif dict_data["app"] == "com.tencent.structmsg":
|
elif dict_data["app"] == "com.tencent.structmsg":
|
||||||
meta_view = dict_data["meta"][dict_data["view"]]
|
meta_view = dict_data["meta"][dict_data["view"]]
|
||||||
@ -317,14 +337,14 @@ class QQMsgProcessor:
|
|||||||
|
|
||||||
return [efb_msg]
|
return [efb_msg]
|
||||||
|
|
||||||
def qq_video_wrapper(self, data, chat: Chat = None):
|
async def qq_video_wrapper(self, data, _: Chat = None):
|
||||||
res = download_file(data["url"])
|
res = await download_file(data["url"])
|
||||||
mime = magic.from_file(res.name, mime=True)
|
mime = magic.from_file(res.name, mime=True)
|
||||||
if isinstance(mime, bytes):
|
if isinstance(mime, bytes):
|
||||||
mime = mime.decode()
|
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]
|
return [efb_msg]
|
||||||
|
|
||||||
def qq_unsupported_wrapper(self, data, chat: Chat = None):
|
def qq_unsupported_wrapper(self, data, _: Chat = None):
|
||||||
efb_msg = Message(type=MsgType.Unsupported, text=data)
|
efb_msg = Message(type=MsgType.Unsupported, text=data)
|
||||||
return [efb_msg]
|
return [efb_msg]
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import logging
|
import logging
|
||||||
import tempfile
|
import tempfile
|
||||||
import urllib.request
|
from typing import IO, Optional, Union
|
||||||
from gettext import translation
|
|
||||||
from urllib.error import ContentTooShortError, HTTPError, URLError
|
|
||||||
|
|
||||||
|
import httpx
|
||||||
import pilk
|
import pilk
|
||||||
import pydub
|
import pydub
|
||||||
from ehforwarderbot import Message, coordinator
|
from ehforwarderbot import Message, coordinator
|
||||||
from pkg_resources import resource_filename
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# created by JogleLew and jqqqqqqqqqq, optimized based on Tim's emoji support, updated by xzsk2 to mobileqq v8.8.11
|
# created by JogleLew and jqqqqqqqqqq, optimized based on Tim's emoji support, updated by xzsk2 to mobileqq v8.8.11
|
||||||
qq_emoji_list = {
|
qq_emoji_list = {
|
||||||
@ -639,34 +639,52 @@ qq_sface_list = {
|
|||||||
39: "[赞]",
|
39: "[赞]",
|
||||||
40: "[眨眼]",
|
40: "[眨眼]",
|
||||||
}
|
}
|
||||||
translator = translation(
|
|
||||||
"efb_qq_slave",
|
|
||||||
resource_filename("efb_qq_slave", "Clients/CoolQ/locale"),
|
|
||||||
fallback=True,
|
|
||||||
)
|
|
||||||
_ = translator.gettext
|
|
||||||
ngettext = translator.ngettext
|
|
||||||
|
|
||||||
|
|
||||||
def cq_get_image(image_link: str) -> tempfile: # Download image from QQ
|
async def async_get_file(url: str) -> IO:
|
||||||
file = tempfile.NamedTemporaryFile()
|
temp_file = tempfile.NamedTemporaryFile()
|
||||||
try:
|
try:
|
||||||
urllib.request.urlretrieve(image_link, file.name)
|
async with httpx.AsyncClient() as client:
|
||||||
except (URLError, HTTPError, ContentTooShortError) as e:
|
resp = await client.get(url)
|
||||||
logging.getLogger(__name__).warning("Image download failed.")
|
temp_file.write(resp.content)
|
||||||
logging.getLogger(__name__).warning(str(e))
|
if temp_file.seek(0, 2) <= 0:
|
||||||
return None
|
raise EOFError("File downloaded is Empty")
|
||||||
else:
|
temp_file.seek(0)
|
||||||
if file.seek(0, 2) <= 0:
|
except Exception as e:
|
||||||
|
temp_file.close()
|
||||||
|
raise e
|
||||||
|
return temp_file
|
||||||
|
|
||||||
|
|
||||||
|
def sync_get_file(url: str) -> IO:
|
||||||
|
temp_file = tempfile.NamedTemporaryFile()
|
||||||
|
try:
|
||||||
|
resp = httpx.get(url)
|
||||||
|
temp_file.write(resp.content)
|
||||||
|
if temp_file.seek(0, 2) <= 0:
|
||||||
raise EOFError("File downloaded is Empty")
|
raise EOFError("File downloaded is Empty")
|
||||||
file.seek(0)
|
temp_file.seek(0)
|
||||||
return file
|
except Exception as e:
|
||||||
|
temp_file.close()
|
||||||
|
raise e
|
||||||
|
return temp_file
|
||||||
|
|
||||||
|
|
||||||
|
async def cq_get_image(image_link: str) -> Optional[IO]: # Download image from QQ
|
||||||
|
try:
|
||||||
|
return await async_get_file(image_link)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("File download failed.")
|
||||||
|
logger.warning(str(e))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def async_send_messages_to_master(msg: Message):
|
def async_send_messages_to_master(msg: Message):
|
||||||
coordinator.send_message(msg)
|
try:
|
||||||
if msg.file:
|
coordinator.send_message(msg)
|
||||||
msg.file.close()
|
finally:
|
||||||
|
if msg.file:
|
||||||
|
msg.file.close()
|
||||||
|
|
||||||
|
|
||||||
def process_quote_text(text, max_length): # Simple wrapper for processing quoted text
|
def process_quote_text(text, max_length): # Simple wrapper for processing quoted text
|
||||||
@ -706,80 +724,64 @@ def param_spliter(str_param):
|
|||||||
return param
|
return param
|
||||||
|
|
||||||
|
|
||||||
def download_file(download_url):
|
async def download_file(download_url: str) -> Union[IO, str]:
|
||||||
file = tempfile.NamedTemporaryFile()
|
|
||||||
try:
|
try:
|
||||||
opener = urllib.request.build_opener()
|
return await async_get_file(download_url)
|
||||||
urllib.request.install_opener(opener)
|
except Exception as e:
|
||||||
urllib.request.urlretrieve(download_url, file.name)
|
logger.warning("Error occurs when downloading files: " + str(e))
|
||||||
except (URLError, HTTPError, ContentTooShortError) as e:
|
return "Error occurs when downloading files: " + str(e)
|
||||||
logging.getLogger(__name__).warning("Error occurs when downloading files: " + str(e))
|
|
||||||
return _("Error occurs when downloading files: ") + str(e)
|
|
||||||
else:
|
|
||||||
if file.seek(0, 2) <= 0:
|
|
||||||
raise EOFError("File downloaded is Empty")
|
|
||||||
file.seek(0)
|
|
||||||
return file
|
|
||||||
|
|
||||||
|
|
||||||
def download_user_avatar(uid: str):
|
def download_user_avatar(uid: str):
|
||||||
file = tempfile.NamedTemporaryFile()
|
|
||||||
url = "https://q1.qlogo.cn/g?b=qq&nk={}&s=0".format(uid)
|
url = "https://q1.qlogo.cn/g?b=qq&nk={}&s=0".format(uid)
|
||||||
try:
|
try:
|
||||||
opener = urllib.request.build_opener()
|
return sync_get_file(url)
|
||||||
urllib.request.install_opener(opener)
|
except Exception as e:
|
||||||
urllib.request.urlretrieve(url, file.name)
|
logger.warning("Error occurs when downloading files: " + str(e))
|
||||||
except (URLError, HTTPError, ContentTooShortError) as e:
|
raise
|
||||||
logging.getLogger(__name__).warning("Error occurs when downloading files: " + str(e))
|
|
||||||
return _("Error occurs when downloading files: ") + str(e)
|
|
||||||
if file.seek(0, 2) <= 0:
|
|
||||||
raise EOFError("File downloaded is Empty")
|
|
||||||
file.seek(0)
|
|
||||||
return file
|
|
||||||
|
|
||||||
|
|
||||||
def download_group_avatar(uid: str):
|
def download_group_avatar(uid: str):
|
||||||
file = tempfile.NamedTemporaryFile()
|
|
||||||
url = "https://p.qlogo.cn/gh/{}/{}/".format(uid, uid)
|
url = "https://p.qlogo.cn/gh/{}/{}/".format(uid, uid)
|
||||||
try:
|
try:
|
||||||
opener = urllib.request.build_opener()
|
return sync_get_file(url)
|
||||||
urllib.request.install_opener(opener)
|
|
||||||
urllib.request.urlretrieve(url, file.name)
|
|
||||||
except (URLError, HTTPError, ContentTooShortError) as e:
|
|
||||||
logging.getLogger(__name__).warning("Error occurs when downloading files: " + str(e))
|
|
||||||
return _("Error occurs when downloading files: ") + str(e)
|
|
||||||
if file.seek(0, 2) <= 0:
|
|
||||||
raise EOFError("File downloaded is Empty")
|
|
||||||
file.seek(0)
|
|
||||||
return file
|
|
||||||
|
|
||||||
|
|
||||||
def download_voice(voice_url: str):
|
|
||||||
origin_file = tempfile.NamedTemporaryFile()
|
|
||||||
try:
|
|
||||||
opener = urllib.request.build_opener()
|
|
||||||
urllib.request.install_opener(opener)
|
|
||||||
urllib.request.urlretrieve(voice_url, origin_file.name)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.getLogger(__name__).warning("Error occurs when downloading files: " + str(e))
|
logger.warning("Error occurs when downloading files: " + str(e))
|
||||||
origin_file.close()
|
raise
|
||||||
raise e
|
|
||||||
finally:
|
|
||||||
opener.close()
|
async def download_voice(voice_url: str):
|
||||||
if origin_file.seek(0, 2) <= 0:
|
origin_file, audio_file = None, None
|
||||||
origin_file.close()
|
try:
|
||||||
raise EOFError("File downloaded is Empty")
|
origin_file = await async_get_file(voice_url)
|
||||||
origin_file.seek(0)
|
silk_header = origin_file.read(10)
|
||||||
silk_header = origin_file.read(10)
|
origin_file.seek(0)
|
||||||
origin_file.seek(0)
|
if b"#!SILK_V3" in silk_header:
|
||||||
if b"#!SILK_V3" in silk_header:
|
with tempfile.NamedTemporaryFile() as pcm_file:
|
||||||
with tempfile.NamedTemporaryFile() as pcm_file:
|
pilk.decode(origin_file.name, pcm_file.name)
|
||||||
pilk.decode(origin_file.name, pcm_file.name)
|
audio_file = tempfile.NamedTemporaryFile()
|
||||||
|
pydub.AudioSegment.from_raw(file=pcm_file, sample_width=2, frame_rate=24000, channels=1).export(
|
||||||
|
audio_file, format="ogg", codec="libopus", parameters=["-vbr", "on"]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
audio_file = origin_file
|
||||||
|
except Exception as e:
|
||||||
|
if origin_file:
|
||||||
origin_file.close()
|
origin_file.close()
|
||||||
audio_file = tempfile.NamedTemporaryFile()
|
if audio_file:
|
||||||
pydub.AudioSegment.from_raw(file=pcm_file, sample_width=2, frame_rate=24000, channels=1).export(
|
audio_file.close()
|
||||||
audio_file, format="ogg", codec="libopus", parameters=["-vbr", "on"]
|
logger.warning("Error occurs when downloading files: " + str(e))
|
||||||
)
|
raise
|
||||||
else:
|
|
||||||
audio_file = origin_file
|
|
||||||
return audio_file
|
return audio_file
|
||||||
|
|
||||||
|
|
||||||
|
def strf_time(seconds: int) -> str:
|
||||||
|
minutes, seconds = divmod(seconds, 60)
|
||||||
|
hours, minutes = divmod(minutes, 60)
|
||||||
|
days, hours = divmod(hours, 24)
|
||||||
|
text = ""
|
||||||
|
text += f"{days}d" if days else ""
|
||||||
|
text += f"{hours}h" if hours else ""
|
||||||
|
text += f"{minutes}m" if minutes else ""
|
||||||
|
text += f"{seconds}s" if seconds else ""
|
||||||
|
return text
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
from . import GoCQHttp # noqa: F401
|
from . import GoCQHttp # noqa: F401
|
||||||
|
|
||||||
__version__ = "2.2.1"
|
__version__ = "3.0.2"
|
||||||
|
@ -6,13 +6,14 @@ dependencies = [
|
|||||||
"efb-qq-slave @ git+https://github.com/milkice233/efb-qq-slave@master",
|
"efb-qq-slave @ git+https://github.com/milkice233/efb-qq-slave@master",
|
||||||
"ehforwarderbot~=2.1.1",
|
"ehforwarderbot~=2.1.1",
|
||||||
"PyYAML~=6.0",
|
"PyYAML~=6.0",
|
||||||
"requests~=2.27.1",
|
|
||||||
"python-magic~=0.4.25",
|
"python-magic~=0.4.25",
|
||||||
"Pillow~=9.0.1",
|
"Pillow~=9.0.1",
|
||||||
"cqhttp~=1.3.1",
|
"aiocqhttp~=1.4.3",
|
||||||
"CherryPy~=18.6.1",
|
"quart~=0.17.0",
|
||||||
|
"hypercorn~=0.13.2",
|
||||||
"pilk~=0.0.2",
|
"pilk~=0.0.2",
|
||||||
"pydub~=0.25.1",
|
"pydub~=0.25.1",
|
||||||
|
"httpx>=0.23.3",
|
||||||
]
|
]
|
||||||
requires-python = ">=3.7"
|
requires-python = ">=3.7"
|
||||||
license = { text = "AGPL-3.0-only" }
|
license = { text = "AGPL-3.0-only" }
|
||||||
@ -56,7 +57,7 @@ build-backend = "pdm.pep517.api"
|
|||||||
version = { from = "efb_qq_plugin_go_cqhttp/__init__.py" }
|
version = { from = "efb_qq_plugin_go_cqhttp/__init__.py" }
|
||||||
|
|
||||||
[tool.pdm.dev-dependencies]
|
[tool.pdm.dev-dependencies]
|
||||||
dev = ["pre-commit", "efb-telegram-master~=2.2.4"]
|
dev = ["efb-telegram-master~=2.2.4"]
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 120
|
line-length = 120
|
||||||
|
Loading…
Reference in New Issue
Block a user