Archived
1
0
Fork 0

kook support

This commit is contained in:
yzhh 2023-06-30 01:32:35 +08:00
parent 5c70ef69d5
commit 07910322a9
7 changed files with 389 additions and 1 deletions

0
bots/kook/__init__.py Normal file
View file

40
bots/kook/bot.py Normal file
View file

@ -0,0 +1,40 @@
import os
from bots.kook.client import bot
from khl import Message, MessageTypes
from core.builtins import PrivateAssets, Url
from core.logger import Logger
from core.parser.message import parser
from core.types import MsgInfo, Session
from core.utils.bot import load_prompt, init_async
from bots.kook.message import MessageSession, FetchTarget
PrivateAssets.set(os.path.abspath(os.path.dirname(__file__) + '/assets'))
@bot.on_message((MessageTypes.TEXT, MessageTypes.IMG))
async def msg_handler(message: Message):
targetId = f'Kook|{message.channel_type.name}|{message.target_id}'
replyId = None
if 'quote' in message.extra:
replyId = message.extra['quote']['rong_id']
msg = MessageSession(MsgInfo(targetId=targetId,
senderId=f'Kook|User|{message.author_id}',
targetFrom=f'Kook|{message.channel_type.name}',
senderFrom='Kook|User', senderName=message.author.nickname,
clientName='Kook',
messageId=message.id,
replyId=replyId),
Session(message=message, target=message.target_id, sender=message.author_id))
await parser(msg)
@bot.on_startup
async def _(b: bot):
await init_async()
await load_prompt(FetchTarget)
if bot:
bot.run()

11
bots/kook/client.py Normal file
View file

@ -0,0 +1,11 @@
from khl import Bot
from config import Config
token = Config('kook_token')
if token:
bot = Bot(token=token)
else:
bot = False

241
bots/kook/message.py Normal file
View file

@ -0,0 +1,241 @@
import re
import traceback
import ujson as json
from typing import List, Union
import aiohttp
from khl import MessageTypes, Message
from bots.kook.client import bot
from config import Config
from core.builtins import Bot, Plain, Image, Voice, MessageSession as MS, ErrorMessage
from core.builtins.message.chain import MessageChain
from core.logger import Logger
from core.types import MsgInfo, Session, FetchTarget as FT, FetchedSession as FS, \
FinishedSession as FinS
from core.utils.image import image_split
from database import BotDBUtil
enable_analytics = Config('enable_analytics')
kook_base = "https://www.kookapp.cn"
kook_headers = {f'Authorization': f"Bot {Config('kook_token')}"}
async def direct_msg_delete(msg_id: str):
"""删除私聊消息"""
url = kook_base + "/api/v3/direct-message/delete"
params = {"msg_id": msg_id}
async with aiohttp.ClientSession() as session:
async with session.post(url, data=params, headers=kook_headers) as response:
res = json.loads(await response.text())
return res
async def channel_msg_delete(msg_id: str):
"""删除普通消息"""
url = kook_base + "/api/v3/message/delete"
params = {"msg_id": msg_id}
async with aiohttp.ClientSession() as session:
async with session.post(url, data=params, headers=kook_headers) as response:
res = json.loads(await response.text())
return res
class FinishedSession(FinS):
async def delete(self):
"""
用于删除这条消息
"""
try:
for x in self.result:
if x['type'] == 'PERSON':
await direct_msg_delete(x)
else:
await channel_msg_delete(x)
except Exception:
Logger.error(traceback.format_exc())
class MessageSession(MS):
class Feature:
image = True
voice = True
embed = False
forward = False
delete = True
quote = True
wait = True
async def sendMessage(self, msgchain, quote=True, disable_secret_check=False,
allow_split_image=True) -> FinishedSession:
self.session.message: Message
msgchain = MessageChain(msgchain)
if not msgchain.is_safe and not disable_secret_check:
return await self.sendMessage(Plain(ErrorMessage(self.locale.t("error.message.chain.unsafe"))))
self.sent.append(msgchain)
count = 0
send = []
for x in msgchain.asSendable(embed=False):
if isinstance(x, Plain):
send_ = await self.session.message.reply(x.text, quote=quote if quote
and count == 0 and self.session.message else None)
Logger.info(f'[Bot] -> [{self.target.targetId}]: {x.text}')
send.append(send_)
count += 1
elif isinstance(x, Image):
url = await bot.create_asset(open(await x.get(), 'rb'))
send_ = await self.session.message.reply(url, type=MessageTypes.IMG, quote=quote if quote
and count == 0 and self.session.message else None)
Logger.info(f'[Bot] -> [{self.target.targetId}]: Image: {str(x.__dict__)}')
send.append(send_)
count += 1
elif isinstance(x, Voice):
url = await bot.create_asset(open(x.path), 'rb')
send_ = await self.session.message.reply(url, type=MessageTypes.AUDIO, quote=quote if quote
and count == 0 and self.session.message else None)
Logger.info(f'[Bot] -> [{self.target.targetId}]: Voice: {str(x.__dict__)}')
send.append(send_)
count += 1
msgIds = []
for x in send:
msgIds.append({x['msg_id']: self.session.message.channel_type.name})
return FinishedSession(self, msgIds, send)
async def checkPermission(self):
self.session.message: Message
if self.session.message.channel_type.name == 'PERSON' or self.target.senderId in self.custom_admins \
or self.target.senderInfo.query.isSuperUser:
return True
return await self.checkNativePermission()
async def checkNativePermission(self):
self.session.message: Message
if self.session.message.channel_type.name == 'PERSON':
return True
guild = await bot.client.fetch_guild(self.session.message.ctx.guild.id)
user_roles = (await guild.fetch_user(self.session.message.author.id)).roles
guild_roles = await guild.fetch_roles()
for i in guild_roles: # 遍历服务器身分组
if i.id in user_roles and i.has_permission(0):
return True
if self.session.message.author.id == guild.master_id:
return True
return False
def asDisplay(self, text_only=False):
if self.session.message.content:
msg = re.sub(r'\[.*]\((.*)\)', '\\1', self.session.message.content)
return msg
return ''
async def toMessageChain(self):
lst = []
if self.session.message.type == MessageTypes.TEXT:
lst.append(Plain(self.session.message.content))
elif self.session.message.type == MessageTypes.IMG:
lst.append(Image(self.session.message.content))
elif self.session.message.type == MessageTypes.AUDIO:
lst.append(Voice(self.session.message.content))
return MessageChain(lst)
async def delete(self):
self.session.message: Message
try:
if self.session.message.channel_type.name == 'PERSON':
await direct_msg_delete(self.session.message.id)
else:
await channel_msg_delete(self.session.message.id)
except Exception:
Logger.error(traceback.format_exc())
class Typing:
def __init__(self, msg: MS):
self.msg = msg
async def __aenter__(self):
# await bot.answer_chat_action(self.msg.session.target, 'typing')
pass
async def __aexit__(self, exc_type, exc_val, exc_tb):
pass
class FetchedSession(FS):
def __init__(self, targetFrom, targetId):
self.target = MsgInfo(targetId=f'{targetFrom}|{targetId}',
senderId=f'{targetFrom}|{targetId}',
targetFrom=targetFrom,
senderFrom=targetFrom,
senderName='',
clientName='Kook', messageId=0, replyId=None)
self.session = Session(message=False, target=targetId, sender=targetId)
self.parent = MessageSession(self.target, self.session)
class FetchTarget(FT):
name = 'Telegram'
@staticmethod
async def fetch_target(targetId) -> Union[FetchedSession, bool]:
matchChannel = re.match(r'^(Kook\|.*?)\|(.*)', targetId)
if matchChannel:
return FetchedSession(matchChannel.group(1), matchChannel.group(2))
else:
return False
@staticmethod
async def fetch_target_list(targetList: list) -> List[FetchedSession]:
lst = []
for x in targetList:
fet = await FetchTarget.fetch_target(x)
if fet:
lst.append(fet)
return lst
@staticmethod
async def post_message(module_name, message, user_list: List[FetchedSession] = None, i18n=False, **kwargs):
if user_list is not None:
for x in user_list:
try:
if i18n:
if isinstance(message, dict):
if (gm := message.get(x.parent.locale.locale)) is not None:
await x.sendDirectMessage(gm)
else:
await x.sendDirectMessage(message.get('fallback'))
else:
await x.sendDirectMessage(x.parent.locale.t(message, **kwargs))
else:
await x.sendDirectMessage(message)
if enable_analytics:
BotDBUtil.Analytics(x).add('', module_name, 'schedule')
except Exception:
Logger.error(traceback.format_exc())
else:
get_target_id = BotDBUtil.TargetInfo.get_enabled_this(module_name, "Telegram")
for x in get_target_id:
fetch = await FetchTarget.fetch_target(x.targetId)
if fetch:
try:
if i18n:
if isinstance(message, dict):
if (gm := message.get(fetch.parent.locale.locale)) is not None:
await fetch.sendDirectMessage(gm)
else:
await fetch.sendDirectMessage(message.get('fallback'))
else:
await fetch.sendDirectMessage(fetch.parent.locale.t(message, **kwargs))
else:
await fetch.sendDirectMessage(message)
if enable_analytics:
BotDBUtil.Analytics(fetch).add('', module_name, 'schedule')
except Exception:
Logger.error(traceback.format_exc())
Bot.MessageSession = MessageSession
Bot.FetchTarget = FetchTarget

61
poetry.lock generated
View file

@ -1821,6 +1821,23 @@ MarkupSafe = ">=2.0"
[package.extras] [package.extras]
i18n = ["Babel (>=2.7)"] i18n = ["Babel (>=2.7)"]
[[package]]
name = "khl-py"
version = "0.3.16"
description = "Python SDK for kaiheila.cn API"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
{file = "khl.py-0.3.16-py3-none-any.whl", hash = "sha256:c55a7caca054ac4678b4ff8f35f8555cc295d5ec9bd4db15857dcbec9cd0a42e"},
{file = "khl.py-0.3.16.tar.gz", hash = "sha256:d4e608d560cc809bd3b657d2ddb2ca3e9370f6b84d299215b87d0768d4133d76"},
]
[package.dependencies]
aiohttp = "*"
apscheduler = "*"
pycryptodomex = "*"
[[package]] [[package]]
name = "kiwisolver" name = "kiwisolver"
version = "1.4.4" version = "1.4.4"
@ -3079,6 +3096,48 @@ files = [
{file = "pycryptodome-3.18.0.tar.gz", hash = "sha256:c9adee653fc882d98956e33ca2c1fb582e23a8af7ac82fee75bd6113c55a0413"}, {file = "pycryptodome-3.18.0.tar.gz", hash = "sha256:c9adee653fc882d98956e33ca2c1fb582e23a8af7ac82fee75bd6113c55a0413"},
] ]
[[package]]
name = "pycryptodomex"
version = "3.18.0"
description = "Cryptographic library for Python"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "pycryptodomex-3.18.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:160a39a708c36fa0b168ab79386dede588e62aec06eb505add870739329aecc6"},
{file = "pycryptodomex-3.18.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c2953afebf282a444c51bf4effe751706b4d0d63d7ca2cc51db21f902aa5b84e"},
{file = "pycryptodomex-3.18.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:ba95abd563b0d1b88401658665a260852a8e6c647026ee6a0a65589287681df8"},
{file = "pycryptodomex-3.18.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:192306cf881fe3467dda0e174a4f47bb3a8bb24b90c9cdfbdc248eec5fc0578c"},
{file = "pycryptodomex-3.18.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:f9ab5ef0718f6a8716695dea16d83b671b22c45e9c0c78fd807c32c0192e54b5"},
{file = "pycryptodomex-3.18.0-cp27-cp27m-win32.whl", hash = "sha256:50308fcdbf8345e5ec224a5502b4215178bdb5e95456ead8ab1a69ffd94779cb"},
{file = "pycryptodomex-3.18.0-cp27-cp27m-win_amd64.whl", hash = "sha256:4d9379c684efea80fdab02a3eb0169372bca7db13f9332cb67483b8dc8b67c37"},
{file = "pycryptodomex-3.18.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5594a125dae30d60e94f37797fc67ce3c744522de7992c7c360d02fdb34918f8"},
{file = "pycryptodomex-3.18.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:8ff129a5a0eb5ff16e45ca4fa70a6051da7f3de303c33b259063c19be0c43d35"},
{file = "pycryptodomex-3.18.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:3d9314ac785a5b75d5aaf924c5f21d6ca7e8df442e5cf4f0fefad4f6e284d422"},
{file = "pycryptodomex-3.18.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:f237278836dda412a325e9340ba2e6a84cb0f56b9244781e5b61f10b3905de88"},
{file = "pycryptodomex-3.18.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac614363a86cc53d8ba44b6c469831d1555947e69ab3276ae8d6edc219f570f7"},
{file = "pycryptodomex-3.18.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:302a8f37c224e7b5d72017d462a2be058e28f7be627bdd854066e16722d0fc0c"},
{file = "pycryptodomex-3.18.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:6421d23d6a648e83ba2670a352bcd978542dad86829209f59d17a3f087f4afef"},
{file = "pycryptodomex-3.18.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84e105787f5e5d36ec6a581ff37a1048d12e638688074b2a00bcf402f9aa1c2"},
{file = "pycryptodomex-3.18.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6875eb8666f68ddbd39097867325bd22771f595b4e2b0149739b5623c8bf899b"},
{file = "pycryptodomex-3.18.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:27072a494ce621cc7a9096bbf60ed66826bb94db24b49b7359509e7951033e74"},
{file = "pycryptodomex-3.18.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:1949e09ea49b09c36d11a951b16ff2a05a0ffe969dda1846e4686ee342fe8646"},
{file = "pycryptodomex-3.18.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6ed3606832987018615f68e8ed716a7065c09a0fe94afd7c9ca1b6777f0ac6eb"},
{file = "pycryptodomex-3.18.0-cp35-abi3-win32.whl", hash = "sha256:d56c9ec41258fd3734db9f5e4d2faeabe48644ba9ca23b18e1839b3bdf093222"},
{file = "pycryptodomex-3.18.0-cp35-abi3-win_amd64.whl", hash = "sha256:e00a4bacb83a2627e8210cb353a2e31f04befc1155db2976e5e239dd66482278"},
{file = "pycryptodomex-3.18.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:2dc4eab20f4f04a2d00220fdc9258717b82d31913552e766d5f00282c031b70a"},
{file = "pycryptodomex-3.18.0-pp27-pypy_73-win32.whl", hash = "sha256:75672205148bdea34669173366df005dbd52be05115e919551ee97171083423d"},
{file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bec6c80994d4e7a38312072f89458903b65ec99bed2d65aa4de96d997a53ea7a"},
{file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d35a8ffdc8b05e4b353ba281217c8437f02c57d7233363824e9d794cf753c419"},
{file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f0a46bee539dae4b3dfe37216f678769349576b0080fdbe431d19a02da42ff"},
{file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:71687eed47df7e965f6e0bf3cadef98f368d5221f0fb89d2132effe1a3e6a194"},
{file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:73d64b32d84cf48d9ec62106aa277dbe99ab5fbfd38c5100bc7bddd3beb569f7"},
{file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbdcce0a226d9205560a5936b05208c709b01d493ed8307792075dedfaaffa5f"},
{file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58fc0aceb9c961b9897facec9da24c6a94c5db04597ec832060f53d4d6a07196"},
{file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:215be2980a6b70704c10796dd7003eb4390e7be138ac6fb8344bf47e71a8d470"},
{file = "pycryptodomex-3.18.0.tar.gz", hash = "sha256:3e3ecb5fe979e7c1bb0027e518340acf7ee60415d79295e5251d13c68dde576e"},
]
[[package]] [[package]]
name = "pydantic" name = "pydantic"
version = "1.10.7" version = "1.10.7"
@ -4433,4 +4492,4 @@ cffi = ["cffi (>=1.11)"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.8.1" python-versions = "^3.8.1"
content-hash = "21c1f768442311a5d4fa657d4f07b5290eb7f1b8fd09b8be89cae12c15904c70" content-hash = "255986b7eedf346049cf3eb592530a3fde64114dfca510e14e4efb7fc6d3b922"

View file

@ -53,6 +53,7 @@ tiktoken = "^0.3.3"
pycryptodome = "^3.18.0" pycryptodome = "^3.18.0"
langconv = "^0.2.0" langconv = "^0.2.0"
toml = "^0.10.2" toml = "^0.10.2"
khl-py = "^0.3.16"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]

View file

@ -908,6 +908,9 @@ jaraco-context==4.3.0 ; python_full_version >= "3.8.1" and python_full_version <
jinja2==3.1.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" \ jinja2==3.1.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" \
--hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \ --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \
--hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61
khl-py==0.3.16 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" \
--hash=sha256:c55a7caca054ac4678b4ff8f35f8555cc295d5ec9bd4db15857dcbec9cd0a42e \
--hash=sha256:d4e608d560cc809bd3b657d2ddb2ca3e9370f6b84d299215b87d0768d4133d76
kiwisolver==1.4.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" \ kiwisolver==1.4.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" \
--hash=sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b \ --hash=sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b \
--hash=sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166 \ --hash=sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166 \
@ -1602,6 +1605,39 @@ pycryptodome==3.18.0 ; python_full_version >= "3.8.1" and python_full_version <
--hash=sha256:f022a4fd2a5263a5c483a2bb165f9cb27f2be06f2f477113783efe3fe2ad887b \ --hash=sha256:f022a4fd2a5263a5c483a2bb165f9cb27f2be06f2f477113783efe3fe2ad887b \
--hash=sha256:f21efb8438971aa16924790e1c3dba3a33164eb4000106a55baaed522c261acf \ --hash=sha256:f21efb8438971aa16924790e1c3dba3a33164eb4000106a55baaed522c261acf \
--hash=sha256:fc0a73f4db1e31d4a6d71b672a48f3af458f548059aa05e83022d5f61aac9c08 --hash=sha256:fc0a73f4db1e31d4a6d71b672a48f3af458f548059aa05e83022d5f61aac9c08
pycryptodomex==3.18.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" \
--hash=sha256:160a39a708c36fa0b168ab79386dede588e62aec06eb505add870739329aecc6 \
--hash=sha256:192306cf881fe3467dda0e174a4f47bb3a8bb24b90c9cdfbdc248eec5fc0578c \
--hash=sha256:1949e09ea49b09c36d11a951b16ff2a05a0ffe969dda1846e4686ee342fe8646 \
--hash=sha256:215be2980a6b70704c10796dd7003eb4390e7be138ac6fb8344bf47e71a8d470 \
--hash=sha256:27072a494ce621cc7a9096bbf60ed66826bb94db24b49b7359509e7951033e74 \
--hash=sha256:2dc4eab20f4f04a2d00220fdc9258717b82d31913552e766d5f00282c031b70a \
--hash=sha256:302a8f37c224e7b5d72017d462a2be058e28f7be627bdd854066e16722d0fc0c \
--hash=sha256:3d9314ac785a5b75d5aaf924c5f21d6ca7e8df442e5cf4f0fefad4f6e284d422 \
--hash=sha256:3e3ecb5fe979e7c1bb0027e518340acf7ee60415d79295e5251d13c68dde576e \
--hash=sha256:4d9379c684efea80fdab02a3eb0169372bca7db13f9332cb67483b8dc8b67c37 \
--hash=sha256:50308fcdbf8345e5ec224a5502b4215178bdb5e95456ead8ab1a69ffd94779cb \
--hash=sha256:5594a125dae30d60e94f37797fc67ce3c744522de7992c7c360d02fdb34918f8 \
--hash=sha256:58fc0aceb9c961b9897facec9da24c6a94c5db04597ec832060f53d4d6a07196 \
--hash=sha256:6421d23d6a648e83ba2670a352bcd978542dad86829209f59d17a3f087f4afef \
--hash=sha256:6875eb8666f68ddbd39097867325bd22771f595b4e2b0149739b5623c8bf899b \
--hash=sha256:6ed3606832987018615f68e8ed716a7065c09a0fe94afd7c9ca1b6777f0ac6eb \
--hash=sha256:71687eed47df7e965f6e0bf3cadef98f368d5221f0fb89d2132effe1a3e6a194 \
--hash=sha256:73d64b32d84cf48d9ec62106aa277dbe99ab5fbfd38c5100bc7bddd3beb569f7 \
--hash=sha256:75672205148bdea34669173366df005dbd52be05115e919551ee97171083423d \
--hash=sha256:76f0a46bee539dae4b3dfe37216f678769349576b0080fdbe431d19a02da42ff \
--hash=sha256:8ff129a5a0eb5ff16e45ca4fa70a6051da7f3de303c33b259063c19be0c43d35 \
--hash=sha256:ac614363a86cc53d8ba44b6c469831d1555947e69ab3276ae8d6edc219f570f7 \
--hash=sha256:ba95abd563b0d1b88401658665a260852a8e6c647026ee6a0a65589287681df8 \
--hash=sha256:bbdcce0a226d9205560a5936b05208c709b01d493ed8307792075dedfaaffa5f \
--hash=sha256:bec6c80994d4e7a38312072f89458903b65ec99bed2d65aa4de96d997a53ea7a \
--hash=sha256:c2953afebf282a444c51bf4effe751706b4d0d63d7ca2cc51db21f902aa5b84e \
--hash=sha256:d35a8ffdc8b05e4b353ba281217c8437f02c57d7233363824e9d794cf753c419 \
--hash=sha256:d56c9ec41258fd3734db9f5e4d2faeabe48644ba9ca23b18e1839b3bdf093222 \
--hash=sha256:d84e105787f5e5d36ec6a581ff37a1048d12e638688074b2a00bcf402f9aa1c2 \
--hash=sha256:e00a4bacb83a2627e8210cb353a2e31f04befc1155db2976e5e239dd66482278 \
--hash=sha256:f237278836dda412a325e9340ba2e6a84cb0f56b9244781e5b61f10b3905de88 \
--hash=sha256:f9ab5ef0718f6a8716695dea16d83b671b22c45e9c0c78fd807c32c0192e54b5
pydantic==1.10.7 ; python_full_version >= "3.8.1" and python_version < "4.0" \ pydantic==1.10.7 ; python_full_version >= "3.8.1" and python_version < "4.0" \
--hash=sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e \ --hash=sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e \
--hash=sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6 \ --hash=sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6 \