Compare commits

...

56 Commits
master ... new

Author SHA1 Message Date
dd615896bb
Support group filter 2023-01-13 14:25:45 +08:00
9dc07ec55f
chore: async msg decorator 2023-01-13 14:24:14 +08:00
pre-commit-ci[bot]
56363f091f [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/PyCQA/isort: v5.11.3 → 5.11.4](https://github.com/PyCQA/isort/compare/v5.11.3...5.11.4)
2022-12-27 11:16:32 +08:00
pre-commit-ci[bot]
5ed4996905 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/PyCQA/isort: 5.11.1 → v5.11.3](https://github.com/PyCQA/isort/compare/5.11.1...v5.11.3)
2022-12-20 10:22:45 +08:00
pre-commit-ci[bot]
3fc56b93f5 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 22.10.0 → 22.12.0](https://github.com/psf/black/compare/22.10.0...22.12.0)
- [github.com/PyCQA/isort: 5.10.1 → 5.11.1](https://github.com/PyCQA/isort/compare/5.10.1...5.11.1)
2022-12-13 11:53:11 +08:00
pre-commit-ci[bot]
2a145d2659 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/PyCQA/flake8: 5.0.4 → 6.0.0](https://github.com/PyCQA/flake8/compare/5.0.4...6.0.0)
2022-11-29 09:40:44 +08:00
shejialuo
39f628a99f fix(docs): change the http bind address
It is dangerous to use `0.0.0.0` in the docs. So this commit changes the
address to localhost.
2022-11-08 17:08:35 +08:00
pre-commit-ci[bot]
0f8b06b129 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 22.8.0 → 22.10.0](https://github.com/psf/black/compare/22.8.0...22.10.0)
2022-10-17 10:40:28 +08:00
pre-commit-ci[bot]
bf0a04e5fe [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 22.6.0 → 22.8.0](https://github.com/psf/black/compare/22.6.0...22.8.0)
2022-09-06 13:05:13 +08:00
pre-commit-ci[bot]
f94353f469 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-09-06 13:05:01 +08:00
8024f4e416 feat: group album 2022-09-06 13:05:01 +08:00
pre-commit-ci[bot]
838e759e07 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-08-21 15:34:44 +08:00
147f66cd91 fix: ActionFailed 2022-08-21 15:34:44 +08:00
pre-commit-ci[bot]
32547397c3 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-08-21 15:34:44 +08:00
7f14a3cb9e fix: join request 2022-08-21 15:34:44 +08:00
XYenon
a1781add55 fix receive file 2022-08-14 14:46:56 +08:00
pre-commit-ci[bot]
31c757344c [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/PyCQA/flake8: 5.0.2 → 5.0.4](https://github.com/PyCQA/flake8/compare/5.0.2...5.0.4)
2022-08-09 12:17:49 +08:00
7a3bccf8c0 fix: await async_download_group_file 2022-08-02 12:20:29 +08:00
a4da2927fa fix: Ignore qq guild message 2022-08-02 12:20:29 +08:00
pre-commit-ci[bot]
da6e06d1c0 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/PyCQA/flake8: 4.0.1 → 5.0.2](https://github.com/PyCQA/flake8/compare/4.0.1...5.0.2)
2022-08-02 12:20:04 +08:00
XYenon
a114d91fee upgrade pdm to 2.0 2022-07-27 21:35:41 +08:00
XYenon
a1b7f88015 fix: add await 2022-07-27 21:35:41 +08:00
0honus0
10acc91fdb 使用新版配置格式 2022-07-27 21:32:28 +08:00
pre-commit-ci[bot]
0db46937ac [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 22.3.0 → 22.6.0](https://github.com/psf/black/compare/22.3.0...22.6.0)
2022-07-05 12:05:21 +08:00
XYenon
09072071ca
Merge pull request #36 from ehForwarderBot/dev/async
refactor to async
2022-06-03 14:04:42 +08:00
XYenon
1d826405ac fix: close file when error occurred 2022-05-28 23:12:50 +08:00
XYenon
7bf80e1787 fix: catch NetworkError 2022-05-28 18:51:54 +08:00
XYenon
3202ccd85c set quart.serving log level to warning 2022-05-28 17:04:15 +08:00
XYenon
d97a2ab2bf async process msg 2022-05-28 16:56:46 +08:00
XYenon
b86e558312 use request to download file 2022-05-28 16:05:26 +08:00
XYenon
265959ddda fix typo 2022-05-22 19:47:13 +08:00
XYenon
cd3fdc7d86 fix: RuntimeError: asyncio.run() cannot be called from a running event loop 2022-05-22 19:21:13 +08:00
XYenon
3818b6ffb4 fix: KeyError: 'in_group_info' 2022-05-22 18:36:42 +08:00
XYenon
b93a613e1b fix: TypeError: 'coroutine' object is not subscriptable 2022-05-09 17:20:27 +08:00
XYenon
4b23b770fb refactor to async 2022-05-02 18:34:48 +08:00
XYenon
d1e79d8ed0
Merge pull request #33 from ehForwarderBot/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2022-04-05 04:35:29 +08:00
pre-commit-ci[bot]
2b400f7ed5
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 22.1.0 → 22.3.0](https://github.com/psf/black/compare/22.1.0...22.3.0)
2022-04-04 19:41:00 +00:00
XYenon
d01b459fe8
Merge pull request #32 from ehForwarderBot/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2022-03-29 23:19:35 +08:00
pre-commit-ci[bot]
fb73069820
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v0.941 → v0.942](https://github.com/pre-commit/mirrors-mypy/compare/v0.941...v0.942)
2022-03-28 18:58:25 +00:00
XYenon
1478cb7b56
Merge pull request #30 from ehForwarderBot/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2022-03-22 09:52:16 +08:00
pre-commit-ci[bot]
4370e98d30
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v0.940 → v0.941](https://github.com/pre-commit/mirrors-mypy/compare/v0.940...v0.941)
2022-03-21 22:03:38 +00:00
XYenon
c8d30db88b
Merge pull request #29 from ehForwarderBot/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2022-03-15 09:50:13 +08:00
pre-commit-ci[bot]
2874307dcf
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v0.931 → v0.940](https://github.com/pre-commit/mirrors-mypy/compare/v0.931...v0.940)
2022-03-14 21:53:46 +00:00
XYenon
f6c508560c
Merge pull request #28 from omg-xtao/dev
feat(more notice): Group Admin Change and Member Restrict
2022-03-14 13:23:04 +08:00
omg-xtao
3f89606ab6
refactor: clean up 2022-03-14 13:13:26 +08:00
pre-commit-ci[bot]
7d4d3c4a0b [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-03-12 11:52:26 +00:00
1dc82dcb9e
feat(more notice): Group Admin Change and Member Restrict 2022-03-12 19:49:03 +08:00
XYenon
b07c89d06b chore(lint): add mypy 2022-03-09 22:15:00 +08:00
XYenon
2c2ff9de1e chore(version): bump version to 2.2.3 2022-03-09 21:05:29 +08:00
XYenon
661dc91104
Merge pull request #27 from omg-xtao:omg-xtao-patch-1
feat(recall notice): support msg recall notice
2022-03-09 21:03:11 +08:00
omg-xtao
4c6880b342
refactor: clean up 2022-03-09 20:56:45 +08:00
omg-xtao
aeee9738a4
feat(recall notice): support msg recall notice 2022-03-09 19:52:21 +08:00
XYenon
d9bf0da5a4
Update __init__.py 2022-03-08 21:18:43 +08:00
XYenon
4d5dffcd7a
Merge pull request #26 from omg-xtao/omg-xtao-patch-1
Support flash picture and Fix GroupJoinRequest
2022-03-08 21:17:45 +08:00
omg-xtao
a08633715c
fix(GroupJoinRequest): fix build msg author error 2022-03-08 20:10:35 +08:00
omg-xtao
dfb1421f84
feat(flash picture): support flash picture 2022-03-08 20:06:10 +08:00
9 changed files with 1303 additions and 1073 deletions

View File

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

View File

@ -38,12 +38,8 @@ efb-qq-plugin-go-cqhttp 是 efb-qq-slave 的插件,需要配合 efb-qq-slave
servers:
# HTTP 通信设置
- http:
# 是否关闭正向 HTTP 服务器
disabled: false
# 服务端监听地址
host: 127.0.0.1
# 服务端监听端口
port: 5700
# HTTP监听地址
address: 127.0.0.1:5700
# 反向 HTTP 超时时间, 单位秒
# 最小值为 5小于 5 将会忽略本项设置
timeout: 5

View File

@ -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
is_discuss = False if context["message_type"] == "group" else True
async def build_efb_chat_as_group(self, context, update_member=False): # Should be cached
is_discuss = context["message_type"] != "group"
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(

File diff suppressed because it is too large Load Diff

View File

@ -1,37 +1,46 @@
import asyncio
import base64
import html
import json
import logging
import sys
from typing import TYPE_CHECKING
import magic
from ehforwarderbot import Chat, Message, MsgType
from ehforwarderbot.message import LinkAttribute, LocationAttribute, Substitutions
from . import GoCQHttp
from .Utils import cq_get_image, download_file, download_voice
if TYPE_CHECKING:
from .GoCQHttp import GoCQHttp
class QQMsgProcessor:
inst: GoCQHttp
inst: "GoCQHttp"
logger: logging.Logger = logging.getLogger(__name__)
def __init__(self, instance: GoCQHttp):
def __init__(self, instance: "GoCQHttp"):
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()
if "url" not in data:
efb_msg.type = MsgType.Text
efb_msg.text = self._("[Image Source missing]")
efb_msg.text = "[Image Source missing]"
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:
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]
efb_msg.type = MsgType.Image
@ -46,11 +55,11 @@ class QQMsgProcessor:
efb_msg.type = MsgType.Animation
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()
try:
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)
if isinstance(mime, bytes):
mime = mime.decode()
@ -58,11 +67,11 @@ class QQMsgProcessor:
efb_msg.mime = mime
except Exception:
efb_msg.type = MsgType.Unsupported
efb_msg.text = self._("[Voice Message] Please check it on your QQ")
efb_msg.text = "[Voice Message] Please check it on your QQ"
logging.getLogger(__name__).exception("Failed to download voice")
return [efb_msg]
def qq_share_wrapper(self, data, chat: Chat = None):
def qq_share_wrapper(self, data, _: Chat = None):
efb_msg = Message(
type=MsgType.Link,
text="",
@ -75,7 +84,7 @@ class QQMsgProcessor:
)
return [efb_msg]
def qq_location_wrapper(self, data, chat: Chat = None):
def qq_location_wrapper(self, data, _: Chat = None):
efb_msg = Message(
text=data["content"],
type=MsgType.Location,
@ -83,23 +92,23 @@ class QQMsgProcessor:
)
return [efb_msg]
def qq_shake_wrapper(self, data, chat: Chat = None):
efb_msg = Message(type=MsgType.Text, text=self._("[Your friend shakes you!]"))
def qq_shake_wrapper(self, _, __: Chat = None):
efb_msg = Message(type=MsgType.Text, text=("[Your friend shakes you!]"))
return [efb_msg]
def qq_contact_wrapper(self, data, chat: Chat = None):
def qq_contact_wrapper(self, data, _: Chat = None):
uid = data["id"]
contact_type = data["type"]
efb_msg = Message(
type=MsgType.Text,
text=self._("Chat Recommendation Received\nID: {}\nType: {}").format(uid, contact_type),
text=("Chat Recommendation Received\nID: {}\nType: {}").format(uid, contact_type),
)
return [efb_msg]
def qq_bface_wrapper(self, data, chat: Chat = None):
def qq_bface_wrapper(self, _, __: Chat = None):
efb_msg = Message(
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]
@ -107,33 +116,33 @@ class QQMsgProcessor:
# todo this function's maybe not necessary?
pass
def qq_sign_wrapper(self, data, chat: Chat = None):
location = self._("at {}").format(data["location"]) if "location" in data else self._("at Unknown Place")
title = "" if "title" not in data else (self._("with title {}").format(data["title"]))
def qq_sign_wrapper(self, data, _: Chat = None):
location = ("at {}").format(data["location"]) if "location" in data else ("at Unknown Place")
title = "" if "title" not in data else (("with title {}").format(data["title"]))
efb_msg = Message(
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]
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_msg = Message(
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():
efb_msg.text += key + ": " + value + "\n"
efb_messages.append(efb_msg)
# Optimizations for rich messages
# Group Broadcast
_ = self.qq_group_broadcast_wrapper(data, chat)
_ = await self.qq_group_broadcast_wrapper(data, chat)
if _ is not None:
efb_messages.append(_)
return efb_messages
def qq_music_wrapper(self, data, chat: Chat = None):
def qq_music_wrapper(self, data, _: Chat = None):
efb_msg = Message()
if data["type"] == "163": # Netease Cloud Music
efb_msg.type = MsgType.Text
@ -183,7 +192,7 @@ class QQMsgProcessor:
efb_msg.filename = data["filename"]
return efb_msg
def qq_group_broadcast_wrapper(self, data, chat: Chat = None):
async def qq_group_broadcast_wrapper(self, data, chat: Chat):
try:
at_list = {}
content_data = json.loads(data["content"])
@ -202,21 +211,21 @@ class QQMsgProcessor:
data["url"] = "http://gdynamic.qpic.cn/gdynamic/{}/628".format(
content_data["mannounce"]["pic"][0]["url"]
)
efb_message = self.qq_image_wrapper(data)[0]
efb_message = (await self.qq_image_wrapper(data))[0]
efb_message.text = text
efb_message.substitutions = Substitutions(at_list)
return [efb_message]
else:
return self.qq_text_simple_wrapper(text, at_list)
except Exception:
return self.qq_group_broadcast_alternative_wrapper(data)
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:
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"])
@ -231,7 +240,7 @@ class QQMsgProcessor:
if "pics" in html.unescape(notice_data[0]["msg"]): # Picture Attached
# Assuming there's only one picture
data["url"] = "http://gdynamic.qpic.cn/gdynamic/{}/628".format(notice_data[0]["msg"]["pics"][0]["id"])
efb_message = self.qq_image_wrapper(data)[0]
efb_message = (await self.qq_image_wrapper(data))[0]
efb_message.text = text
efb_message.substitutions = Substitutions(at_list)
return [efb_message]
@ -240,13 +249,13 @@ class QQMsgProcessor:
except Exception:
return None
def qq_xml_wrapper(self, data, chat: Chat = None):
def qq_xml_wrapper(self, data, _: Chat = None):
efb_msg = Message()
efb_msg.type = MsgType.Text
efb_msg.text = data["data"]
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.type = MsgType.Text
efb_msg.text = data["data"]
@ -289,6 +298,17 @@ class QQMsgProcessor:
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
elif dict_data["app"] == "com.tencent.structmsg":
meta_view = dict_data["meta"][dict_data["view"]]
@ -317,14 +337,14 @@ class QQMsgProcessor:
return [efb_msg]
def qq_video_wrapper(self, data, chat: Chat = None):
res = download_file(data["url"])
async def qq_video_wrapper(self, data, _: Chat = None):
res = await download_file(data["url"])
mime = magic.from_file(res.name, mime=True)
if isinstance(mime, bytes):
mime = mime.decode()
efb_msg = Message(type=MsgType.Video, file=res, filename=res.name, mime=mime)
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)
return [efb_msg]

View File

@ -1,13 +1,13 @@
import logging
import tempfile
import urllib.request
from gettext import translation
from urllib.error import ContentTooShortError, HTTPError, URLError
from typing import IO, Optional, Union
import httpx
import pilk
import pydub
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
qq_emoji_list = {
@ -639,32 +639,50 @@ 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) -> tempfile: # Download image from QQ
file = tempfile.NamedTemporaryFile()
async def async_get_file(url: str) -> IO:
temp_file = tempfile.NamedTemporaryFile()
try:
urllib.request.urlretrieve(image_link, file.name)
except (URLError, HTTPError, ContentTooShortError) as e:
logging.getLogger(__name__).warning("Image download failed.")
logging.getLogger(__name__).warning(str(e))
return None
else:
if file.seek(0, 2) <= 0:
async with httpx.AsyncClient() as client:
resp = await client.get(url)
temp_file.write(resp.content)
if temp_file.seek(0, 2) <= 0:
raise EOFError("File downloaded is Empty")
file.seek(0)
return file
temp_file.seek(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")
temp_file.seek(0)
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):
try:
coordinator.send_message(msg)
finally:
if msg.file:
msg.file.close()
@ -706,80 +724,64 @@ def param_spliter(str_param):
return param
def download_file(download_url):
file = tempfile.NamedTemporaryFile()
async def download_file(download_url: str) -> Union[IO, str]:
try:
opener = urllib.request.build_opener()
urllib.request.install_opener(opener)
urllib.request.urlretrieve(download_url, file.name)
except (URLError, HTTPError, ContentTooShortError) as e:
logging.getLogger(__name__).warning("Error occurs when downloading files: " + str(e))
return _("Error occurs when downloading files: ") + str(e)
else:
if file.seek(0, 2) <= 0:
raise EOFError("File downloaded is Empty")
file.seek(0)
return file
return await async_get_file(download_url)
except Exception as e:
logger.warning("Error occurs when downloading files: " + str(e))
return "Error occurs when downloading files: " + str(e)
def download_user_avatar(uid: str):
file = tempfile.NamedTemporaryFile()
url = "https://q1.qlogo.cn/g?b=qq&nk={}&s=0".format(uid)
try:
opener = urllib.request.build_opener()
urllib.request.install_opener(opener)
urllib.request.urlretrieve(url, file.name)
except (URLError, HTTPError, ContentTooShortError) as e:
logging.getLogger(__name__).warning("Error occurs when downloading files: " + str(e))
return _("Error occurs when downloading files: ") + str(e)
if file.seek(0, 2) <= 0:
raise EOFError("File downloaded is Empty")
file.seek(0)
return file
return sync_get_file(url)
except Exception as e:
logger.warning("Error occurs when downloading files: " + str(e))
raise
def download_group_avatar(uid: str):
file = tempfile.NamedTemporaryFile()
url = "https://p.qlogo.cn/gh/{}/{}/".format(uid, uid)
try:
opener = urllib.request.build_opener()
urllib.request.install_opener(opener)
urllib.request.urlretrieve(url, file.name)
except (URLError, HTTPError, ContentTooShortError) as e:
logging.getLogger(__name__).warning("Error occurs when downloading files: " + str(e))
return _("Error occurs when downloading files: ") + str(e)
if file.seek(0, 2) <= 0:
raise EOFError("File downloaded is Empty")
file.seek(0)
return file
def 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)
return sync_get_file(url)
except Exception as e:
logging.getLogger(__name__).warning("Error occurs when downloading files: " + str(e))
origin_file.close()
raise e
finally:
opener.close()
if origin_file.seek(0, 2) <= 0:
origin_file.close()
raise EOFError("File downloaded is Empty")
origin_file.seek(0)
logger.warning("Error occurs when downloading files: " + str(e))
raise
async def download_voice(voice_url: str):
origin_file, audio_file = None, None
try:
origin_file = await async_get_file(voice_url)
silk_header = origin_file.read(10)
origin_file.seek(0)
if b"#!SILK_V3" in silk_header:
with tempfile.NamedTemporaryFile() as pcm_file:
pilk.decode(origin_file.name, pcm_file.name)
origin_file.close()
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()
if audio_file:
audio_file.close()
logger.warning("Error occurs when downloading files: " + str(e))
raise
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

View File

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

1116
pdm.lock

File diff suppressed because it is too large Load Diff

View File

@ -6,13 +6,14 @@ dependencies = [
"efb-qq-slave @ git+https://github.com/milkice233/efb-qq-slave@master",
"ehforwarderbot~=2.1.1",
"PyYAML~=6.0",
"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",
"httpx>=0.23.3",
]
requires-python = ">=3.7"
license = { text = "AGPL-3.0-only" }
@ -56,7 +57,7 @@ build-backend = "pdm.pep517.api"
version = { from = "efb_qq_plugin_go_cqhttp/__init__.py" }
[tool.pdm.dev-dependencies]
dev = ["pre-commit", "efb-telegram-master~=2.2.4"]
dev = ["efb-telegram-master~=2.2.4"]
[tool.black]
line-length = 120