Compare commits

..

1 Commits

Author SHA1 Message Date
Chris Watson 72178efcf0 check env vars before starting 2024-06-13 11:01:56 -06:00
31 changed files with 166 additions and 1399 deletions

8
.env.example Executable file → Normal file
View File

@ -1,9 +1,3 @@
APP_ID= APP_ID=
APP_HASH= APP_HASH=
TG_PHONE=+15555555555 TG_PHONE=+15555555555
DB_HOST=
DB_USER=
DB_PASS=
DB_PORT=27017
DB_NAME=fenneko

0
.gitignore vendored Executable file → Normal file
View File

0
README.md Executable file → Normal file
View File

28
cyber_fenneko/__init__.py Executable file → Normal file
View File

@ -1,40 +1,26 @@
import os import os
import jsonpickle from telethon import TelegramClient
from mongoengine import connect from telethon.sessions import SQLiteSession
from telemongo import MongoSession
from telethon import Client as TelegramClient
from dotenv import load_dotenv from dotenv import load_dotenv
from cyber_fenneko.bot import Bot from cyber_fenneko.bot import Bot
load_dotenv() load_dotenv()
jsonpickle.set_encoder_options('json', ensure_ascii=False, indent=4)
# Remember to use your own values from my.telegram.org! # Remember to use your own values from my.telegram.org!
api_id = os.environ.get('APP_ID') api_id = os.environ.get('APP_ID')
api_hash = os.environ.get('APP_HASH') api_hash = os.environ.get('APP_HASH')
phone_number = os.environ.get('TG_PHONE') phone_number = os.environ.get('TG_PHONE')
db_host = os.environ.setdefault('DB_HOST', 'localhost') for var in [api_id, api_hash, phone_number]:
db_port = os.environ.setdefault('DB_PORT', 27017) if not var:
db_user = os.environ.get('DB_USER') raise ValueError('Environment variable not set')
db_pass = os.environ.get('DB_PASS')
db_name = os.environ.get('DB_NAME')
connect(db=db_name, host=db_host, port=int(db_port), username=db_user, password=db_pass) session = SQLiteSession('fenneko-' + phone_number)
session = MongoSession() client = TelegramClient(session, api_id, api_hash)
client = TelegramClient(session, int(api_id), api_hash)
bot = Bot(client) bot = Bot(client)
# Import all middlewares from the middleware directory
# We just need the files to be imported, nothing else
# This is a bit hacky, but it works
for filename in os.listdir('cyber_fenneko/middleware'):
if filename.endswith('.py'):
__import__('cyber_fenneko.middleware.' + filename[:-3])
# Import all commands from the commands directory # Import all commands from the commands directory
# We just need the files to be imported, nothing else # We just need the files to be imported, nothing else
# This is a bit hacky, but it works # This is a bit hacky, but it works

34
cyber_fenneko/__main__.py Executable file → Normal file
View File

@ -1,30 +1,12 @@
import asyncio from telethon.events import NewMessage
from datetime import datetime
from telethon.events import NewMessage, Event
from cyber_fenneko.models.chat import Chat, ChatType
from . import client, bot, phone_number from . import client, bot, phone_number
client.add_event_handler(bot._on_raw, Event) async def new_message_handler(event: NewMessage):
# client.add_event_handler(bot._on_new_message, NewMessage) print(event)
async def main(): client.add_event_handler(bot.on_new_message, NewMessage)
async with client: # client.add_event_handler(new_message_handler, NewMessage)
me = await client.interactive_login(phone_number)
Chat.objects(id=me.id).update_one(
set__id=me.id,
set__name=me.first_name,
set__username=me.username,
set__chat_type=ChatType.USER,
set__phone=me.phone,
set__packed=me.pack().__bytes__(),
set__updated_at=datetime.utcnow(),
upsert=True
)
client.add_event_handler(bot._on_raw, Event) with client:
client.add_event_handler(bot._on_new_message, NewMessage) client.start(phone_number)
client.run_until_disconnected()
await client.run_until_disconnected()
asyncio.run(main())

54
cyber_fenneko/bot.py Executable file → Normal file
View File

@ -1,28 +1,15 @@
import logging import logging
from pprint import pprint from telethon import TelegramClient
from typing import Dict from telethon.tl.custom.message import Message
from telethon import Client as TelegramClient
from telethon._impl.client.types import Message
from telethon._impl.tl.abcs import Update
from telethon._impl.client.events import NewMessage, Event
from cyber_fenneko.internal.constants import GLOBAL_ARGS from cyber_fenneko.internal.constants import GLOBAL_ARGS
from cyber_fenneko.internal.command import Command from cyber_fenneko.internal.command import Command
from cyber_fenneko.internal.command_context import CommandContext from cyber_fenneko.internal.command_context import CommandContext
from cyber_fenneko.internal.arg_parser import parse_args from cyber_fenneko.internal.arg_parser import parse_args
from cyber_fenneko.utils import to_dict
class Bot: class Bot:
commands: Dict[str, Command]
command_aliases: Dict[str, str]
update_handlers: Dict[Update, list]
client: TelegramClient
logger: logging.Logger
default_prefix: str
def __init__(self, client: TelegramClient): def __init__(self, client: TelegramClient):
self.commands = {} self.commands = {}
self.command_aliases = {} self.command_aliases = {}
self.update_handlers = {}
self.client = client self.client = client
self.logger = logging.getLogger('cyber_fenneko.bot') self.logger = logging.getLogger('cyber_fenneko.bot')
self.default_prefix = '.' self.default_prefix = '.'
@ -30,22 +17,13 @@ class Bot:
def set_default_prefix(self, prefix: str): def set_default_prefix(self, prefix: str):
self.default_prefix = prefix self.default_prefix = prefix
def on(self, *events: Event):
def decorator(func):
for event in events:
if event not in self.update_handlers:
self.update_handlers[event] = []
self.update_handlers[event].append(func)
return func
return decorator
def command(self, cmd: str, **kwargs: dict): def command(self, cmd: str, **kwargs: dict):
def decorator(func): def decorator(func):
# Set some defaults # Set some defaults
kwargs.setdefault('prefix', self.default_prefix) kwargs.setdefault('prefix', self.default_prefix)
command = Command(func, cmd, **kwargs) command = Command(func, cmd, **kwargs)
command.args = GLOBAL_ARGS + command.args
# Check if a command with the same name already exists # Check if a command with the same name already exists
if command.command in self.commands: if command.command in self.commands:
@ -64,17 +42,8 @@ class Bot:
return func # we still want the function to be normally accessible return func # we still want the function to be normally accessible
return decorator return decorator
async def _on_raw(self, event: Update): async def on_new_message(self, event: Message):
pass message_text = event.message.message.strip()
# if event.__class__ not in self.update_handlers:
# return
# for handler in self.update_handlers[event.__class__]:
# await handler(self, event)
async def _on_new_message(self, event: NewMessage):
# pprint(to_dict(event))
message_text = event.text.strip()
if not message_text: if not message_text:
return return
@ -87,27 +56,28 @@ class Bot:
if cmd.prefix != prefix: if cmd.prefix != prefix:
return return
if event._raw.out and not cmd.outgoing: if event.out and not cmd.outgoing:
return return
elif not event._raw.out and not cmd.incoming: elif not event.out and not cmd.incoming:
return return
text = message_text[len(command_text) + 1:].strip() text = message_text[len(command_text) + 1:].strip()
try: try:
args, text = parse_args(self.client, text, cmd.args) args, text = parse_args(text, cmd.args)
except ValueError as e: except ValueError as e:
await event.respond(f'Error: {e}') await event.respond(f'Error: {e}')
return return
reply_message = await event.get_replied_message() reply_message = await event.message.get_reply_message()
# create the command context # create the command context
ctx = CommandContext( ctx = CommandContext(
client=self.client,
bot=self, bot=self,
client=event.client,
raw_text=message_text, raw_text=message_text,
text=text, text=text,
event=event, event=event,
message=event.message,
reply_to=reply_message, reply_to=reply_message,
args=args args=args
) )
@ -117,4 +87,4 @@ class Bot:
# if the command is marked as 'delete', delete the message that triggered it # if the command is marked as 'delete', delete the message that triggered it
if ctx.args['delete']: if ctx.args['delete']:
await event.delete() await event.message.delete()

View File

@ -1,80 +0,0 @@
import openai
from cyber_fenneko.internal.constants import OPENAI_API_KEY
from .. import bot
from ..internal.command_context import CommandContext
from ..internal.arg_parser import Arg, ArgType
MODELS = {
'3.5': 'gpt-3.5-turbo',
'4': 'gpt-4',
}
@bot.command(
'ask',
args=[
Arg('model', type=ArgType.enum, aliases=['m'], description='The model to use', enum_values=list(MODELS.keys()), default='3.5'),
Arg('max-tokens', type=ArgType.int, aliases=['t'], description='The maximum number of tokens to generate', default=750),
Arg('temperature', type=ArgType.int, aliases=['temp'], description='The temperature to use', default=1),
Arg('top-p', type=ArgType.int, aliases=['top'], description='The top-p value to use', default=1),
Arg('presence-penalty', type=ArgType.int, aliases=['pp'], description='The presence penalty to use', default=0),
Arg('frequency-penalty', type=ArgType.int, aliases=['fp'], description='The frequency penalty to use', default=0),
],
description='Ask a question using ChatGPT',
)
async def ask(bot, ctx: CommandContext):
if not OPENAI_API_KEY:
return
# Get the message. If this command was sent in response to a message, use that message.
# Otherwise, use the message that invoked the command.
message = ctx.reply_to or ctx.event
# Get the prompt
prompt = ctx.reply_to.text.strip() if ctx.reply_to else ctx.text.strip()
# If this message was sent in response to another message, delete the sent message
if ctx.reply_to:
await ctx.event.delete()
# Get the arguments to be passed to the API
model = MODELS[ctx.args['model']]
max_tokens = ctx.args['max-tokens']
temperature = ctx.args['temperature']
top_p = ctx.args['top-p']
presence_penalty = ctx.args['presence-penalty']
frequency_penalty = ctx.args['frequency-penalty']
# Set the scene
messages = [{
"role": "system",
"content": """You are a chatbot within Telegram masquerading as a real person.
Your name is watzon, and you should act and respond like watzon would.
Watzon is a web developer of 13 years, father of 2, and husband of 1.
You are having a conversation with other humans in a Telegram group chat. Assume you have been a part of the chat for a while. No need for greetings or introductions.
The person you're responding to is either asking a question, or making a statement.
You should respond to the human in a way that makes sense.
The human is not trying to trick you. You are responding as a user, so act natural. Use slang if necessary, acronyms, etc.
Do not be too formal, and keep responses short unless the question requires a larger response.
You may use markdown formatting in your responses.
Markdown code blocks should not include the language, and should be delimited by triple backticks.
"""
}, {
"role": "user",
"content": prompt
}]
# Generate the completion
openai.api_key = OPENAI_API_KEY
completion = openai.chat.completions.create(
messages=messages,
model=model,
top_p=top_p,
temperature=temperature,
max_tokens=max_tokens,
presence_penalty=presence_penalty,
frequency_penalty=frequency_penalty,
)
# Send the response
await message.reply(markdown=completion.choices[0].message.content, link_preview=False)

View File

@ -1,24 +0,0 @@
import time
from telethon._impl.tl.types import ChatParticipant, ChatParticipantCreator, ChatParticipantAdmin
from cyber_fenneko.utils import format_time, to_dict
from .. import bot, Bot
from ..internal.command_context import CommandContext
@bot.command(
'cache',
description='Cache as many users in the current chat as possible',
)
async def cache(bot: Bot, ctx: CommandContext):
start = time.time()
msg = await ctx.event.respond('Caching...')
users = []
async for participant in ctx.client.get_participants(ctx.event.chat):
id = participant._raw.user_id
user = participant._chat_map[id]
users.append(user)
print(users)
end = time.time()
await msg.edit(f'Cached {len(users)} users in {format_time(end - start)}')

4
cyber_fenneko/commands/help.py Executable file → Normal file
View File

@ -9,7 +9,7 @@ async def help(bot, ctx: CommandContext):
cmd = ctx.text cmd = ctx.text
if cmd: if cmd:
if cmd in bot.commands: if cmd in bot.commands:
await ctx.respond(markdown=bot.commands[cmd].long_help()) await ctx.respond(bot.commands[cmd].long_help(), parse_mode='Markdown')
else: else:
await ctx.respond(f'Command `{cmd}` not found') await ctx.respond(f'Command `{cmd}` not found')
else: else:
@ -19,4 +19,4 @@ async def help(bot, ctx: CommandContext):
continue continue
commands.append(command.help()) commands.append(command.help())
commands.sort() commands.sort()
await ctx.respond(markdown='\n'.join(commands)) await ctx.respond('\n'.join(commands), parse_mode='Markdown')

147
cyber_fenneko/commands/highlight.py Executable file → Normal file
View File

@ -1,11 +1,9 @@
import time
import httpx import httpx
from io import BytesIO from io import BytesIO
from tempfile import NamedTemporaryFile
from urllib.parse import urlencode from urllib.parse import urlencode
from collections import OrderedDict from collections import OrderedDict
from telethon._impl.tl.types import MessageEntityPre from telethon.tl.types import TypeMessageEntity, MessageEntityPre
from cyber_fenneko.models.settings import Settings, HighlightSettings
from .. import bot from .. import bot
from ..internal.command_context import CommandContext from ..internal.command_context import CommandContext
from ..internal.arg_parser import Arg, ArgType from ..internal.arg_parser import Arg, ArgType
@ -22,9 +20,9 @@ ENDPOINT_URL = 'https://inkify.0x45.st'
Arg('shadow_color', type=ArgType.str, description='The color of the shadow'), Arg('shadow_color', type=ArgType.str, description='The color of the shadow'),
Arg('background', type=ArgType.str, description='The background color'), Arg('background', type=ArgType.str, description='The background color'),
Arg('tab_width', type=ArgType.int, description='The tab width'), Arg('tab_width', type=ArgType.int, description='The tab width'),
Arg('line_pad', type=ArgType.int, aliases=['pad'], description='The line padding'), Arg('line_pad', type=ArgType.int, description='The line padding'),
Arg('line_offset', type=ArgType.int, aliases=['offset'], description='The line offset'), Arg('line_offset', type=ArgType.int, description='The line offset'),
Arg('window_title', type=ArgType.str, aliases=['title'], description='The window title'), Arg('window_title', type=ArgType.str, description='The window title'),
Arg('no_line_number', type=ArgType.bool, description='Whether to hide the line numbers'), Arg('no_line_number', type=ArgType.bool, description='Whether to hide the line numbers'),
Arg('no_round_corner', type=ArgType.bool, description='Whether to round the corners'), Arg('no_round_corner', type=ArgType.bool, description='Whether to round the corners'),
Arg('no_window_controls', type=ArgType.bool, description='Whether to hide the window controls'), Arg('no_window_controls', type=ArgType.bool, description='Whether to hide the window controls'),
@ -46,17 +44,17 @@ async def highlight(bot, ctx: CommandContext):
# Check if the user wants to list available themes # Check if the user wants to list available themes
if ctx.args['themes']: if ctx.args['themes']:
themes = httpx.get(f'{ENDPOINT_URL}/themes').json() themes = httpx.get(f'{ENDPOINT_URL}/themes').json()
themes_list = [f'- `{theme}`' for theme in themes] themes_list = ', '.join(themes)
message = f'Available themes ({len(themes)}):\n' + '\n'.join(themes_list) message = f'Available themes ({len(themes)}):\n{themes_list}'
await ctx.event.respond(markdown=message) await ctx.event.respond(message)
return return
# Check if the user wants to list available fonts # Check if the user wants to list available fonts
if ctx.args['fonts']: if ctx.args['fonts']:
fonts = httpx.get(f'{ENDPOINT_URL}/fonts').json() fonts = httpx.get(f'{ENDPOINT_URL}/fonts').json()
fonts_list = [f'- `{font}`' for font in fonts] fonts_list = ', '.join(fonts)
message = f'Available fonts ({len(fonts)}):\n' + '\n'.join(fonts_list) message = f'Available fonts ({len(fonts)}):\n{fonts_list}'
await ctx.event.respond(markdown=message) await ctx.event.respond(message)
return return
# Check if the user wants to list available languages # Check if the user wants to list available languages
@ -69,82 +67,63 @@ async def highlight(bot, ctx: CommandContext):
# Get the message that triggered this command # Get the message that triggered this command
message = ctx.reply_to message = ctx.reply_to
if not message: if not message or not message.message:
await ctx.event.respond('Reply to a message to highlight it') await ctx.event.respond('Reply to a message to highlight it')
return return
language = ctx.args['language'] # Check if there is a code block in the message
code = None if not message.entities:
await ctx.event.respond('No code block found')
return
# Filter through entities in the message to find the code block
code_blocks = []
for entity in message.entities:
if isinstance(entity, MessageEntityPre):
code_blocks.append(message.message[entity.offset:entity.offset + entity.length])
break
else:
code_blocks.append(ctx.text)
# First check if the message contains a file for code_block in code_blocks:
if message.file: # Inkify returns an image, we just need to build the URL
mime = message.file._mime query = OrderedDict(
if mime and (mime.startswith('text/') or mime.startswith('application/')): code=code_block,
with NamedTemporaryFile(mode='wb', suffix=message.file.ext) as file: language=ctx.args['language'],
await message.file.download(file) theme=ctx.args['theme'],
file.seek(0) font=ctx.args['font'],
code = file.read().decode('utf-8') shadow_color=ctx.args['shadow_color'],
language = language or message.file.ext[1:] background=ctx.args['background'],
tab_width=ctx.args['tab_width'],
line_pad=ctx.args['line_pad'],
line_offset=ctx.args['line_offset'],
window_title=ctx.args['window_title'],
no_line_number=ctx.args['no_line_number'],
no_round_corner=ctx.args['no_round_corner'],
no_window_controls=ctx.args['no_window_controls'],
shadow_blur_radius=ctx.args['shadow_blur_radius'],
shadow_offset_x=ctx.args['shadow_offset_x'],
shadow_offset_y=ctx.args['shadow_offset_y'],
pad_horiz=ctx.args['pad_horiz'],
pad_vert=ctx.args['pad_vert'],
highlight_lines=ctx.args['highlight_lines'],
background_image=ctx.args['background_image'],
)
# Next check if the message contains any code blocks # Remove any arguments that are None, and transform booleans into lowercase strings
if not code and message._raw.entities: query = {k: v for k, v in query.items() if v is not None}
for entity in message._raw.entities: query = {k: str(v).lower() if isinstance(v, bool) else v for k, v in query.items()}
if isinstance(entity, MessageEntityPre):
code = message.text[entity.offset:entity.offset + entity.length]
language = language or entity.language
break
# If there are no code blocks (or more than one), we'll just use the entire message # Get the image from the URL
if not code: url = f'{ENDPOINT_URL}/generate?{urlencode(query)}'
code = message.text print(url)
res = httpx.get(url)
if res.status_code != 200:
await ctx.event.respond(f'Error: {res.status_code}')
return
img = BytesIO(res.content)
settings = Settings.objects.first() # Send the image
if settings.highlight is None: await ctx.event.respond(file=img)
settings.highlight = HighlightSettings()
settings.save()
# Inkify returns an image, we just need to build the URL
query = OrderedDict(
code=code,
language=language,
theme=ctx.args['theme'] or settings.highlight.theme,
font=ctx.args['font'] or settings.highlight.font,
shadow_color=ctx.args['shadow_color'] or settings.highlight.shadow_color,
background=ctx.args['background'] or settings.highlight.background,
tab_width=ctx.args['tab_width'] or settings.highlight.tab_width,
line_pad=ctx.args['line_pad'] or settings.highlight.line_pad,
line_offset=ctx.args['line_offset'] or settings.highlight.line_offset,
window_title=ctx.args['window_title'] or settings.highlight.window_title,
no_line_number=ctx.args['no_line_number'] or settings.highlight.no_line_number,
no_round_corner=ctx.args['no_round_corner'] or settings.highlight.no_round_corner,
no_window_controls=ctx.args['no_window_controls'] or settings.highlight.no_window_controls,
shadow_blur_radius=ctx.args['shadow_blur_radius'] or settings.highlight.shadow_blur_radius,
shadow_offset_x=ctx.args['shadow_offset_x'] or settings.highlight.shadow_offset_x,
shadow_offset_y=ctx.args['shadow_offset_y'] or settings.highlight.shadow_offset_y,
pad_horiz=ctx.args['pad_horiz'] or settings.highlight.pad_horiz,
pad_vert=ctx.args['pad_vert'] or settings.highlight.pad_vert,
highlight_lines=ctx.args['highlight_lines'] or settings.highlight.highlight_lines,
background_image=ctx.args['background_image'] or settings.highlight.background_image,
)
# Remove any arguments that are None, and transform booleans into lowercase strings
query = {k: v for k, v in query.items() if v is not None}
query = {k: str(v).lower() if isinstance(v, bool) else v for k, v in query.items()}
url = f'{ENDPOINT_URL}/generate?{urlencode(query)}'
async with httpx.AsyncClient() as client:
response = await client.get(url)
file = BytesIO(response.content)
try:
await ctx.client.send_photo(
ctx.event.chat,
file=file,
size=int(response.headers['content-length']),
compress=True,
name='highlight.png',
)
except Exception as e:
await ctx.event.respond(f'Failed to highlight code: {e}')
return

11
cyber_fenneko/commands/jsondump.py Executable file → Normal file
View File

@ -1,5 +1,5 @@
import jsonpickle import time
from cyber_fenneko.utils import to_dict import json
from .. import bot from .. import bot
from ..internal.command_context import CommandContext from ..internal.command_context import CommandContext
from ..internal.arg_parser import Arg, ArgType from ..internal.arg_parser import Arg, ArgType
@ -16,10 +16,10 @@ from ..utils.paste import paste as paste_util
async def jsondump(bot, ctx: CommandContext): async def jsondump(bot, ctx: CommandContext):
# Get the message to paste # Get the message to paste
message = ctx.reply_to or ctx.message message = ctx.reply_to or ctx.message
dict = to_dict(message) dict = message.to_dict()
# Prettify the JSON # Prettify the JSON
raw = jsonpickle.encode(dict, unpicklable=False, indent=4) raw = json.dumps(dict, indent=4, sort_keys=True, default=str)
# Check if the JSON is too long # Check if the JSON is too long
if len(raw) > 4000 or ctx.args['paste']: if len(raw) > 4000 or ctx.args['paste']:
@ -28,5 +28,4 @@ async def jsondump(bot, ctx: CommandContext):
await ctx.event.respond(f'Created paste: {url}', link_preview=False) await ctx.event.respond(f'Created paste: {url}', link_preview=False)
else: else:
# Respond with the JSON # Respond with the JSON
response = f'<pre><code class="language-json">{raw}</code></pre>' await ctx.event.respond(f'```\n{raw}\n```', link_preview=False)
await ctx.event.respond(html=response)

59
cyber_fenneko/commands/paste.py Executable file → Normal file
View File

@ -1,11 +1,7 @@
import re
from io import BytesIO
from tempfile import NamedTemporaryFile
import time import time
import httpx import httpx
from telethon._impl.tl.types import MessageEntityPre from telethon.tl.types import MessageEntityPre
from telethon.tl.custom.message import Message
from cyber_fenneko.models.paste import Paste
from .. import bot from .. import bot
from ..internal.command_context import CommandContext from ..internal.command_context import CommandContext
from ..internal.arg_parser import Arg, ArgType from ..internal.arg_parser import Arg, ArgType
@ -13,53 +9,44 @@ from ..utils.paste import paste as paste_util
ENDPOINT = 'https://0x45.st/api/pastes' ENDPOINT = 'https://0x45.st/api/pastes'
@bot.command( @bot.command(
'paste', 'paste',
description='Send the contents of the replied to message to a pastebin', description='Send the contents of the replied to message to a pastebin',
args=[ args=[
Arg('delete', default=True), Arg('language', type=ArgType.str, aliases=['lang'], default='txt', description='The language to use for syntax highlighting'),
Arg('list', type=ArgType.bool, default=False, aliases=[
'l'], description='List all pastes made by the bot'),
Arg('raw', type=ArgType.bool, default=True,
description='Paste the raw markdown instead of the rendered text'),
Arg('language', type=ArgType.str, aliases=[
'lang'], default=None, description='The language to use for syntax highlighting'),
] ]
) )
async def paste(bot, ctx: CommandContext): async def paste(bot, ctx: CommandContext):
# Get the message to paste # Get the message to paste
message = ctx.reply_to message: Message = ctx.reply_to
if message is None: if message is None:
await ctx.event.respond('You must reply to a message to use this command') await ctx.event.respond('You must reply to a message to use this command')
return return
# Get the message text
text = message.text
if text is None:
await ctx.event.respond('You must reply to a text message to use this command')
return
# Get the language
language = ctx.args['language'] language = ctx.args['language']
code = None
# First check if the message contains a file contents = ''
if message.file:
mime = message.file._mime
if mime and (mime.startswith('text/') or mime.startswith('application/')):
buff = BytesIO()
await message.file.download(buff)
code = buff.getvalue().decode('utf-8')
language = language or message.file.ext[1:]
# Next check if the message contains any code blocks # Check if the message has entities; any pre entities will be used as the contents
if not code and message._raw.entities: if message.entities is not None:
for entity in message._raw.entities: for entity in message.entities:
if isinstance(entity, MessageEntityPre): if isinstance(entity, MessageEntityPre):
code = message.text[entity.offset:entity.offset + entity.length] contents += text[entity.offset:entity.offset + entity.length]
language = language or entity.language contents += '\n\n'
break break
if not contents:
contents = text
# If there are no code blocks (or more than one), we'll just use the entire message json = paste_util(contents, language)
if not code:
code = message.text_markdown if ctx.args['raw'] else message.text
json = paste_util(code, language)
url = json['url'] url = json['url']
# Respond with the paste URL # Respond with the paste URL
await ctx.event.respond(f'Created new paste at:\n{url}', link_preview=False) await ctx.event.respond(f'Created paste: {url}', link_preview=False)

1
cyber_fenneko/commands/ping.py Executable file → Normal file
View File

@ -1,6 +1,7 @@
import time import time
from .. import bot from .. import bot
from ..internal.command_context import CommandContext from ..internal.command_context import CommandContext
from ..internal.arg_parser import Arg, ArgType
@bot.command( @bot.command(
'ping', 'ping',

View File

@ -1,50 +0,0 @@
from urllib import parse
from cyber_fenneko.internal.arg_parser import Arg, ArgType
from .. import bot
from ..internal.command_context import CommandContext
from cyber_fenneko.models.settings import Settings
engine_map = {
'google': 'https://www.google.com/search?q=%s',
'bing': 'https://www.bing.com/search?q=%s',
'duckduckgo': 'https://duckduckgo.com/?q=%s',
'qwant': 'https://www.qwant.com/?q=%s',
'ecosia': 'https://www.ecosia.org/search?q=%s',
'kagi': 'https://kagi.com/search?q=%s',
}
@bot.command(
'search',
aliases=['google', 'lmgtfy'],
args=[
# Arg('delete', type=ArgType.bool, default=True),
Arg('google', type=ArgType.bool, default=False, description='Use Google as the search engine'),
Arg('bing', type=ArgType.bool, default=False, description='Use Bing as the search engine'),
Arg('duckduckgo', type=ArgType.bool, aliases=['ddg'], default=False, description='Use DuckDuckGo as the search engine'),
Arg('qwant', type=ArgType.bool, default=False, description='Use Qwant as the search engine'),
Arg('ecosia', type=ArgType.bool, default=False, description='Use Ecosia as the search engine'),
Arg('kagi', type=ArgType.bool, default=False, description='Use Kagi as the search engine'),
],
description='"Help people out by posting a link to a search query for their question"',
)
async def search(bot, ctx: CommandContext):
print(ctx)
# Get the search query from the reply message or the sent message
query = ctx.reply_to.text if ctx.reply_to else ctx.text
if not query:
await ctx.event.respond('You must specify a search query')
return
settings = Settings.objects.first()
# Get the search engine
search_engine = None
for engine in engine_map:
if ctx.args[engine]:
search_engine = engine
search_engine = search_engine or settings.search_engine or 'google'
url = engine_map[search_engine] % parse.quote_plus(query)
# Send the search query
await ctx.event.respond(html=f'<a href="{url}">{query}</a>', link_preview=False)

View File

@ -1,64 +0,0 @@
import time
from typing import List
from mongoengine import EmbeddedDocumentField, StringField, IntField, BooleanField, EnumField
from cyber_fenneko.internal.arg_parser import Arg, ArgType
from cyber_fenneko.models.settings import Settings
from .. import bot
from ..internal.command_context import CommandContext
def mongo_field_to_arg_type(field):
if isinstance(field, StringField):
return ArgType.str
elif isinstance(field, IntField):
return ArgType.int
elif isinstance(field, BooleanField):
return ArgType.bool
elif isinstance(field, EnumField):
return ArgType.enum
else:
return ArgType.str
# Build the args object for the settings command using the fields from the Settings model.
# Keys are the field names, values are the field types. With EmbeddedDocuments, the key
# is the Settings object property name and the embedded document's field name,
# concaternated with a period. Ex. 'highlight.theme'.
setings_args: List[Arg] = []
for field in Settings._fields:
if isinstance(Settings._fields[field], EmbeddedDocumentField):
for embedded_field in Settings._fields[field].document_type._fields:
if embedded_field == 'id':
continue
name = f'{field}.{embedded_field}'
kind = mongo_field_to_arg_type(Settings._fields[field].document_type._fields[embedded_field])
setings_args.append(Arg(name, type=kind))
else:
if field == 'id':
continue
kind = mongo_field_to_arg_type(Settings._fields[field])
setings_args.append(Arg(field, type=kind))
@bot.command(
'settings',
aliases=['s', 'set'],
args=setings_args,
description='Change the bot\'s settings',
)
async def settings(bot, ctx: CommandContext):
"""Change the bot's settings"""
# Get the settings object
settings = Settings.objects.first()
# Find all args that have a value, and set them on the settings object
for arg in ctx.args:
if ctx.args[arg] is not None:
if '.' in arg:
parts = arg.split('.')
setattr(getattr(settings, parts[0]), parts[1], ctx.args[arg])
else:
setattr(settings, arg, ctx.args[arg])
# Save the settings object
settings.save()
await ctx.reply('Settings updated')

View File

@ -1,116 +0,0 @@
# import time
# from telethon._impl.tl.types import User
# from telethon._impl.tl.functions import messages
# from cyber_fenneko.internal.arg_parser import Arg, ArgType
# from cyber_fenneko.models.chat import Chat
# from cyber_fenneko.utils import format_bool, format_user_status
# from cyber_fenneko.bot import Bot
# from .. import bot
# from ..internal.command_context import CommandContext
# @bot.command(
# 'user',
# aliases=['u'],
# args = [
# Arg('user', type=ArgType.peer, aliases=['u'], description='The user to get information about'),
# Arg('id', type=ArgType.bool, aliases=['i'], default=True, description='Return only the user ID'),
# Arg('mention', type=ArgType.bool, aliases=['m'], default=False, description='Whether to mention the user in the response'),
# Arg('check_forward', type=ArgType.bool, aliases=['fwd'], default=True, description='Whether to check the forward from the reply message'),
# Arg('full', type=ArgType.bool, aliases=['f'], default=False, description='Whether to get full information about the user'),
# Arg('general', type=ArgType.bool, aliases=['g'], default=False, description='Whether to show general information about the user'),
# Arg('common_chats', type=ArgType.bool, aliases=['chats'], default=False, description='Whether to get the user\'s common chats'),
# Arg('meta', type=ArgType.bool, default=False, description='Extra infomation about the user that didn\'t fit in the other categories'),
# ],
# description='Gather and return information about the given user',
# )
# async def user(bot: Bot, ctx: CommandContext):
# peer: Chat = ctx.args['user']
# user: User = peer._to_input_peer()
# if not user:
# # Attempt to get the user from the reply message
# message = await ctx.event.get_replied_message()
# if message:
# try:
# if ctx.args['check_forward'] and message._raw.fwd_from:
# # user = message.forward_info
# # TODO: Forwarded messages
# raise NotImplementedError('Forwarded messages are not supported yet')
# else:
# user = message.sender
# except:
# await ctx.event.respond('Failed to get user from reply message')
# return
# if not user:
# await ctx.event.respond('You must specify a user to get information about')
# return
# mention = ctx.args['mention']
# show_full = ctx.args['full']
# id = ctx.args['id']
# show_general = show_full or ctx.args['general']
# show_meta = show_full or ctx.args['meta']
# show_common_chats = show_full or ctx.args['common_chats']
# if show_general or show_meta or show_common_chats:
# id = False
# # Screen name (first name + last name if available)
# screen_name = user.first_name
# if user.last_name:
# screen_name += f' {user.last_name}'
# # Start building a response
# response = ''
# if id:
# if mention:
# response += f'[{screen_name}](tg://user?id={user.id}) (`{user.id}`)'
# else:
# response += f'**{screen_name}**: {user.id}'
# else:
# if mention:
# response += f'[{screen_name}](tg://user?id={user.id}) (`{user.id}`):\n'
# else:
# response += f'**{screen_name}** ({user.id})\n'
# # General
# if show_general:
# response += f'**general**\n'
# response += f' username: `{user.username}`\n'
# response += f' phone: `{user.phone}`\n'
# response += f' bot: {format_bool(user.bot)}\n'
# response += f' verified: {format_bool(user.verified)}\n'
# response += f' restricted: {format_bool(user.restricted)}\n'
# response += f' support: {format_bool(user.support)}\n'
# response += f' scam: {format_bool(user.scam)}\n'
# response += f' fake: {format_bool(user.fake)}\n'
# # Common chats
# if show_common_chats:
# response += f'**common chats**\n'
# common_chats = await bot.client(messages.get_common_chats(user.id, 0, 200))
# for chat in common_chats.chats:
# response += f' {chat.title} ({chat.id})\n'
# # Meta
# if show_meta:
# response += f'**meta**\n'
# response += f' access_hash: `{user.access_hash}`\n'
# response += f' bot_chat_history: {format_bool(user.bot_chat_history)}\n'
# response += f' bot_nochats: {format_bool(user.bot_nochats)}\n'
# response += f' bot_inline_geo: {format_bool(user.bot_inline_geo)}\n'
# response += f' min: {format_bool(user.min)}\n'
# response += f' bot_inline_placeholder: {format_bool(user.bot_inline_placeholder)}\n'
# response += f' lang_code: `{user.lang_code}`\n'
# response += f' status: `{format_user_status(user.status)}`\n'
# response += f' bot_info_version: `{user.bot_info_version}`\n'
# response += f' restriction_reason: `{user.restriction_reason}`\n'
# response += f' bot_inline_geo: {format_bool(user.bot_inline_geo)}\n'
# # Send the response
# await ctx.reply(markdown=response, link_preview=False)

54
cyber_fenneko/internal/arg_parser.py Executable file → Normal file
View File

@ -2,12 +2,8 @@ import logging
from enum import Enum from enum import Enum
from typing import Dict, List, Union from typing import Dict, List, Union
from telethon import Client as TelegramClient
from cyber_fenneko.utils import get_chat_by_id, get_chat_by_username ArgType = Enum('ArgType', ['str', 'int', 'bool', 'peer_id'])
ArgType = Enum('ArgType', ['str', 'int', 'bool', 'enum', 'peer'])
logger = logging.getLogger('cyber_fenneko.internal.arg_parser') logger = logging.getLogger('cyber_fenneko.internal.arg_parser')
@ -33,9 +29,6 @@ class Arg:
# Special designation for global args # Special designation for global args
global_arg: bool = False, global_arg: bool = False,
# If the type is enum, this is the list of valid values
enum_values: List[str] = None,
): ):
self.name = name self.name = name
self.type = type self.type = type
@ -44,25 +37,8 @@ class Arg:
self.default = default self.default = default
self.description = description self.description = description
self.global_arg = global_arg self.global_arg = global_arg
self.enum_values = enum_values
def merge(self, other: 'Arg'): def parse_args(text: str, cmd_args: List[Arg]) -> (Dict[str, Arg], str):
"""
Merge the values of another arg into this one.
"""
self.name = self.name if self.name is not None else other.name
self.type = self.type if self.type is not None else other.type
self.aliases = self.aliases if self.aliases is not None else other.aliases
self.required = self.required if self.required is not None else other.required
self.default = self.default if self.default is not None else other.default
self.description = self.description if self.description is not None else other.description
self.global_arg = self.global_arg if self.global_arg is not None else other.global_arg
self.enum_values = self.enum_values if self.enum_values is not None else other.enum_values
def __repr__(self) -> str:
return f'Arg({self.name}, type={self.type}, aliases={self.aliases}, required={self.required}, default={self.default}, description={self.description}, global_arg={self.global_arg}, enum_values={self.enum_values})'
def parse_args(client: TelegramClient, text: str, cmd_args: List[Arg]) -> (Dict[str, Arg], str):
""" """
Take an incoming string and parse args from it. Take an incoming string and parse args from it.
Args are defined one of three ways: Args are defined one of three ways:
@ -120,31 +96,6 @@ def parse_args(client: TelegramClient, text: str, cmd_args: List[Arg]) -> (Dict[
raise ValueError(f'Unterminated string for arg "{arg.name}"') raise ValueError(f'Unterminated string for arg "{arg.name}"')
arg_value = arg_value[1:-1] arg_value = arg_value[1:-1]
parsed_args[arg.name] = arg_value parsed_args[arg.name] = arg_value
elif arg.type == ArgType.enum:
# if the arg is an enum, we need to check if the value is valid
if arg_value in arg.enum_values:
parsed_args[arg.name] = arg_value
else:
raise ValueError(f'Invalid enum value {arg_value} for arg "{arg.name}"')
elif arg.type == ArgType.peer:
# peer is a special type that can be a username, user id, or chat id.
# we need to fetch the user or chat that the id belongs to.
chat = None
try:
int_value = int(arg_value)
chat = get_chat_by_id(int_value)
except ValueError:
chat = get_chat_by_username(arg_value)
if chat is None:
raise ValueError(f'Invalid peer value {arg_value} for arg "{arg.name}"')
peer = chat.unpack()
if peer is None:
raise ValueError(f'Invalid peer value {arg_value} for arg "{arg.name}"')
parsed_args[arg.name] = peer
elif arg.type == ArgType.bool: elif arg.type == ArgType.bool:
# if the arg is a boolean, we need to check if the value is true or false # if the arg is a boolean, we need to check if the value is true or false
if arg_value.lower() == 'true': if arg_value.lower() == 'true':
@ -167,7 +118,6 @@ def parse_args(client: TelegramClient, text: str, cmd_args: List[Arg]) -> (Dict[
# If we get here, we've encountered a non-arg token # If we get here, we've encountered a non-arg token
# so we stop parsing # so we stop parsing
tokens.insert(0, token)
break break
# Check if any required args are missing # Check if any required args are missing

29
cyber_fenneko/internal/command.py Executable file → Normal file
View File

@ -51,14 +51,6 @@ class Command:
self.description = description self.description = description
self.args = args self.args = args
for arg in GLOBAL_ARGS:
if arg.name not in [a.name for a in self.args]:
self.args.append(arg)
else:
for a in self.args:
if a.name == arg.name:
a.merge(arg)
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs) return self.func(*args, **kwargs)
@ -93,27 +85,14 @@ class Command:
help_string += f'`{arg.name}' help_string += f'`{arg.name}'
if arg.aliases: if arg.aliases:
help_string += f' ({", ".join(arg.aliases)})' help_string += f' ({", ".join(arg.aliases)})'
help_string += f': {arg.type.name}' help_string += f': {arg.type.name}`\n {arg.description}\n'
if arg.default is not None:
help_string += f' = {arg.default}'
help_string += '`\n'
if arg.description:
help_string += f' {arg.description}\n'
help_string += '\n' help_string += '\n'
for arg in command_args: for arg in args:
help_string += f'`{arg.name}' help_string += f'`{arg.name}'
if arg.aliases: if arg.aliases:
help_string += f' ({", ".join(arg.aliases)})' help_string += f' ({", ".join(arg.aliases)})'
help_string += f': {arg.type.name}' help_string += f': {arg.type.name}`\n {arg.description}\n'
if arg.default is not None:
help_string += f' = {arg.default}'
help_string += '`\n'
if arg.description:
help_string += f' {arg.description}\n'
help_string += '\n' help_string += '\n'
return help_string return help_string
def __repr__(self) -> str:
return f'Command({self.command}, prefix={self.prefix}, incoming={self.incoming}, outgoing={self.outgoing}, aliases={self.aliases}, silent={self.silent}, hidden={self.hidden}, usage={self.usage}, description={self.description}, args={self.args})'

15
cyber_fenneko/internal/command_context.py Executable file → Normal file
View File

@ -1,7 +1,7 @@
from typing import Dict, Union from typing import Dict, Union
from telethon import Client as TelegramClient from telethon import TelegramClient
from telethon.events import NewMessage from telethon.events import NewMessage
from telethon._impl.client.types import Message from telethon.tl.custom.message import Message
from .. import bot from .. import bot
from .arg_parser import Arg from .arg_parser import Arg
@ -26,6 +26,9 @@ class CommandContext:
# The event that triggered this command # The event that triggered this command
event: NewMessage, event: NewMessage,
# The message from the event
message: Message,
# The message that was replied to # The message that was replied to
reply_to: Union[Message, None], reply_to: Union[Message, None],
@ -37,6 +40,7 @@ class CommandContext:
self.raw_text = raw_text self.raw_text = raw_text
self.text = text self.text = text
self.event = event self.event = event
self.message = message
self.reply_to = reply_to self.reply_to = reply_to
self.args = args self.args = args
@ -45,11 +49,8 @@ class CommandContext:
async def respond(self, *args, **kwargs): async def respond(self, *args, **kwargs):
"""Respond to the message that triggered this command.""" """Respond to the message that triggered this command."""
await self.event.respond(*args, **kwargs) await self.message.respond(*args, **kwargs)
async def reply(self, *args, **kwargs): async def reply(self, *args, **kwargs):
"""Reply to the message that triggered this command.""" """Reply to the message that triggered this command."""
await self.event.reply(*args, **kwargs) await self.message.reply(*args, **kwargs)
def __repr__(self) -> str:
return f'CommandContext(raw_text={self.raw_text}, text={self.text}, args={self.args})'

3
cyber_fenneko/internal/constants.py Executable file → Normal file
View File

@ -1,8 +1,5 @@
import os
from .arg_parser import Arg, ArgType from .arg_parser import Arg, ArgType
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
# Args that exist on every command # Args that exist on every command
GLOBAL_ARGS = [ GLOBAL_ARGS = [
Arg("delete", type=ArgType.bool, aliases=['d', 'del'], default=False, description="Delete the message that triggered this command", global_arg=True) Arg("delete", type=ArgType.bool, aliases=['d', 'del'], default=False, description="Delete the message that triggered this command", global_arg=True)

0
cyber_fenneko/internal/utils.py Executable file → Normal file
View File

View File

@ -1,53 +0,0 @@
from datetime import datetime
from telethon import events
from telethon._impl.tl.types import User, Chat, Channel
from .. import bot
from cyber_fenneko.models.chat import Chat as Entity, ChatType
def is_incoming(event):
return not event._raw.out
@bot.client.on(events.NewMessage, filter=is_incoming)
async def caching(event: events.NewMessage):
chat = event.sender
if chat is not None:
ent = Entity.objects(id=chat.id).first() or Entity()
ent.id = chat.id
ent.name = chat.name
ent.username = chat.username
if isinstance(chat, User):
ent.phone = chat.phone
ent.chat_type = ChatType.USER
elif isinstance(chat, Chat):
ent.chat_type = ChatType.GROUP
elif isinstance(chat, Channel):
ent.chat_type = ChatType.CHANNEL
if not ent.packed:
ent.packed = chat.pack().__bytes__()
ent.updated_at = datetime.utcnow()
ent.save()
channel = event.chat
if channel is not None:
ent = Entity.objects(id=channel.id).first() or Entity()
ent.id = channel.id
ent.name = channel.name
ent.username = channel.username
if isinstance(chat, User):
ent.phone = chat.phone
ent.chat_type = ChatType.USER
elif isinstance(chat, Chat):
ent.chat_type = ChatType.GROUP
elif isinstance(chat, Channel):
ent.chat_type = ChatType.CHANNEL
if not ent.packed:
ent.packed = channel.pack().__bytes__()
ent.updated_at = datetime.utcnow()
ent.save()

View File

@ -1,30 +0,0 @@
from enum import Enum
from mongoengine import Document, IntField, StringField, DateTimeField, BinaryField, EnumField
from telethon._impl.session import PackedChat
class ChatType(Enum):
USER = 1
GROUP = 2
CHANNEL = 3
class Chat(Document):
id = IntField(primary_key=True)
name = StringField()
username = StringField()
phone = StringField()
name = StringField()
packed = BinaryField()
chat_type = EnumField(ChatType)
created_at = DateTimeField()
updated_at = DateTimeField(required=True)
meta = {
'collection': 'entities',
'indexes': [
'username',
'phone',
]
}
def unpack(self):
if self.packed:
return PackedChat.from_bytes(self.packed)

View File

@ -1,12 +0,0 @@
from .chat import Chat
from mongoengine import Document, StringField, DateTimeField, IntField, BooleanField
from datetime import datetime
class Paste(Document):
"""Contains a record of a paste creation on Paste69"""
url = StringField(required=True)
language = StringField(required=True)
chat = Chat()
author = Chat()
message_id = IntField()
created_at = DateTimeField(default=datetime.utcnow)

View File

@ -1,32 +0,0 @@
from mongoengine import Document, StringField, IntField, BooleanField, EmbeddedDocument, EmbeddedDocumentField
class HighlightSettings(EmbeddedDocument):
"""Stores settings for the highlight command"""
theme = StringField()
font = StringField()
shadow_color = StringField()
background = StringField()
tab_width = IntField()
line_pad = IntField()
line_offset = IntField()
window_title = StringField()
no_line_number = BooleanField()
no_round_corner = BooleanField()
no_window_controls = BooleanField()
shadow_blur_radius = IntField()
shadow_offset_x = IntField()
shadow_offset_y = IntField()
pad_horiz = IntField()
pad_vert = IntField()
highlight_lines = StringField()
background_image = StringField()
class Settings(Document):
"""Stores configuration settings for the bot"""
highlight = EmbeddedDocumentField(HighlightSettings)
search_engine = StringField(default='google')
meta = {
'collection': 'settings'
}

View File

@ -1,65 +0,0 @@
import jsonpickle
from typing import Optional
from telethon._impl.tl.abcs import UserStatus
from telethon._impl.tl.core import Serializable
from telethon._impl.tl.types import UserStatusRecently, UserStatusOnline, UserStatusOffline, UserStatusLastWeek, UserStatusLastMonth, UserStatusEmpty
from cyber_fenneko.models.chat import Chat
# Format time (in ms) to a human readable format.
def format_time(ms: float) -> str:
time = str('%.2f', ms)
unit = 'millis'
if time > 1000000:
mins = time / 60000
secs = time % 60000
time = '%d.%.2f' % (mins, secs)
unit = 'mins'
if time > 1000:
secs = time / 1000
millis = time % 1000
time = '%d.%.2f' % (secs, millis)
unit = 'secs'
return f'{time} {unit}'
# Format a boolean as a simple yes/no string.
def format_bool(value: bool) -> str:
return 'Yes' if value else 'No'
# Convert a TypeUserStatus to a string.
def format_user_status(status: UserStatus) -> str:
if isinstance(status, UserStatusRecently):
return 'Recently'
elif isinstance(status, UserStatusOnline):
return 'Online'
elif isinstance(status, UserStatusOffline):
return 'Offline'
elif isinstance(status, UserStatusLastWeek):
return 'Last week'
elif isinstance(status, UserStatusLastMonth):
return 'Last month'
elif isinstance(status, UserStatusEmpty):
return 'Empty'
else:
return 'Unknown'
def to_dict(obj: object) -> dict:
if obj is None or isinstance(obj, (bool, int, bytes, str)): return obj
if isinstance(obj, (list, tuple)): return [to_dict(v) for v in obj]
if isinstance(obj, dict): return {k: to_dict(v) for k, v in obj.items()}
if hasattr(obj, '_from_raw') or isinstance(obj, Serializable):
props = dir(obj)
return {k: to_dict(getattr(obj, k)) for k in props if not k.startswith('_') and not callable(getattr(obj, k))}
return obj.__repr__()
def get_chat_by_id(id: int) -> Optional[Chat]:
return Chat.objects(id=id).first()
def get_chat_by_username(username: str) -> Optional[Chat]:
username = username.lstrip('@')
return Chat.objects(username=username).first()
def get_chat_by_phone(phone: str) -> Optional[Chat]:
return Chat.objects(phone=phone).first()

1
cyber_fenneko/utils/paste.py Executable file → Normal file
View File

@ -9,7 +9,6 @@ def paste(text: str, language: str = 'txt', password = None, burn = False) -> st
'contents': text, 'contents': text,
'password': password, 'password': password,
'burnAfterReading': burn, 'burnAfterReading': burn,
'raw': True,
} }
response = httpx.post(ENDPOINT, json=data) response = httpx.post(ENDPOINT, json=data)

488
poetry.lock generated
View File

@ -1,25 +1,14 @@
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
[[package]] [[package]]
name = "annotated-types" name = "anyio"
version = "0.6.0" version = "4.0.0"
description = "Reusable constraint types to use with typing.Annotated" description = "High level compatibility layer for multiple asynchronous event loop implementations"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, {file = "anyio-4.0.0-py3-none-any.whl", hash = "sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f"},
{file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, {file = "anyio-4.0.0.tar.gz", hash = "sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a"},
]
[[package]]
name = "anyio"
version = "3.7.1"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
optional = false
python-versions = ">=3.7"
files = [
{file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"},
{file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"},
] ]
[package.dependencies] [package.dependencies]
@ -27,9 +16,9 @@ idna = ">=2.8"
sniffio = ">=1.1" sniffio = ">=1.1"
[package.extras] [package.extras]
doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"]
test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] test = ["anyio[trio]", "coverage[toml] (>=7)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
trio = ["trio (<0.22)"] trio = ["trio (>=0.22)"]
[[package]] [[package]]
name = "certifi" name = "certifi"
@ -42,47 +31,6 @@ files = [
{file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"},
] ]
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "distro"
version = "1.8.0"
description = "Distro - an OS platform information API"
optional = false
python-versions = ">=3.6"
files = [
{file = "distro-1.8.0-py3-none-any.whl", hash = "sha256:99522ca3e365cac527b44bde033f64c6945d90eb9f769703caaec52b09bbd3ff"},
{file = "distro-1.8.0.tar.gz", hash = "sha256:02e111d1dc6a50abb8eed6bf31c3e48ed8b0830d1ea2a1b78c61765c2513fdd8"},
]
[[package]]
name = "dnspython"
version = "2.4.2"
description = "DNS toolkit"
optional = false
python-versions = ">=3.8,<4.0"
files = [
{file = "dnspython-2.4.2-py3-none-any.whl", hash = "sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8"},
{file = "dnspython-2.4.2.tar.gz", hash = "sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984"},
]
[package.extras]
dnssec = ["cryptography (>=2.6,<42.0)"]
doh = ["h2 (>=4.1.0)", "httpcore (>=0.17.3)", "httpx (>=0.24.1)"]
doq = ["aioquic (>=0.9.20)"]
idna = ["idna (>=2.1,<4.0)"]
trio = ["trio (>=0.14,<0.23)"]
wmi = ["wmi (>=1.5.1,<2.0.0)"]
[[package]] [[package]]
name = "h11" name = "h11"
version = "0.14.0" version = "0.14.0"
@ -96,40 +44,39 @@ files = [
[[package]] [[package]]
name = "httpcore" name = "httpcore"
version = "1.0.1" version = "0.18.0"
description = "A minimal low-level HTTP client." description = "A minimal low-level HTTP client."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "httpcore-1.0.1-py3-none-any.whl", hash = "sha256:c5e97ef177dca2023d0b9aad98e49507ef5423e9f1d94ffe2cfe250aa28e63b0"}, {file = "httpcore-0.18.0-py3-none-any.whl", hash = "sha256:adc5398ee0a476567bf87467063ee63584a8bce86078bf748e48754f60202ced"},
{file = "httpcore-1.0.1.tar.gz", hash = "sha256:fce1ddf9b606cfb98132ab58865c3728c52c8e4c3c46e2aabb3674464a186e92"}, {file = "httpcore-0.18.0.tar.gz", hash = "sha256:13b5e5cd1dca1a6636a6aaea212b19f4f85cd88c366a2b82304181b769aab3c9"},
] ]
[package.dependencies] [package.dependencies]
anyio = ">=3.0,<5.0"
certifi = "*" certifi = "*"
h11 = ">=0.13,<0.15" h11 = ">=0.13,<0.15"
sniffio = "==1.*"
[package.extras] [package.extras]
asyncio = ["anyio (>=4.0,<5.0)"]
http2 = ["h2 (>=3,<5)"] http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"] socks = ["socksio (==1.*)"]
trio = ["trio (>=0.22.0,<0.23.0)"]
[[package]] [[package]]
name = "httpx" name = "httpx"
version = "0.25.1" version = "0.25.0"
description = "The next generation HTTP client." description = "The next generation HTTP client."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "httpx-0.25.1-py3-none-any.whl", hash = "sha256:fec7d6cc5c27c578a391f7e87b9aa7d3d8fbcd034f6399f9f79b45bcc12a866a"}, {file = "httpx-0.25.0-py3-none-any.whl", hash = "sha256:181ea7f8ba3a82578be86ef4171554dd45fec26a02556a744db029a0a27b7100"},
{file = "httpx-0.25.1.tar.gz", hash = "sha256:ffd96d5cf901e63863d9f1b4b6807861dbea4d301613415d9e6e57ead15fc5d0"}, {file = "httpx-0.25.0.tar.gz", hash = "sha256:47ecda285389cb32bb2691cc6e069e3ab0205956f681c5b2ad2325719751d875"},
] ]
[package.dependencies] [package.dependencies]
anyio = "*"
certifi = "*" certifi = "*"
httpcore = "*" httpcore = ">=0.18.0,<0.19.0"
idna = "*" idna = "*"
sniffio = "*" sniffio = "*"
@ -150,93 +97,6 @@ files = [
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
] ]
[[package]]
name = "jsonpickle"
version = "3.0.2"
description = "Python library for serializing any arbitrary object graph into JSON"
optional = false
python-versions = ">=3.7"
files = [
{file = "jsonpickle-3.0.2-py3-none-any.whl", hash = "sha256:4a8442d97ca3f77978afa58068768dba7bff2dbabe79a9647bc3cdafd4ef019f"},
{file = "jsonpickle-3.0.2.tar.gz", hash = "sha256:e37abba4bfb3ca4a4647d28bb9f4706436f7b46c8a8333b4a718abafa8e46b37"},
]
[package.extras]
docs = ["jaraco.packaging (>=3.2)", "rst.linker (>=1.9)", "sphinx"]
testing = ["ecdsa", "feedparser", "gmpy2", "numpy", "pandas", "pymongo", "pytest (>=3.5,!=3.7.3)", "pytest-black-multipy", "pytest-checkdocs (>=1.2.3)", "pytest-cov", "pytest-flake8 (>=1.1.1)", "scikit-learn", "sqlalchemy"]
testing-libs = ["simplejson", "ujson"]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
description = "Python port of markdown-it. Markdown parsing, done right!"
optional = false
python-versions = ">=3.8"
files = [
{file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
{file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
]
[package.dependencies]
mdurl = ">=0.1,<1.0"
[package.extras]
benchmarking = ["psutil", "pytest", "pytest-benchmark"]
code-style = ["pre-commit (>=3.0,<4.0)"]
compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
linkify = ["linkify-it-py (>=1,<3)"]
plugins = ["mdit-py-plugins"]
profiling = ["gprof2dot"]
rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
[[package]]
name = "mdurl"
version = "0.1.2"
description = "Markdown URL utilities"
optional = false
python-versions = ">=3.7"
files = [
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
]
[[package]]
name = "mongoengine"
version = "0.27.0"
description = "MongoEngine is a Python Object-Document Mapper for working with MongoDB."
optional = false
python-versions = ">=3.7"
files = [
{file = "mongoengine-0.27.0-py3-none-any.whl", hash = "sha256:c3523b8f886052f3deb200b3218bcc13e4b781661e3bea38587cc936c80ea358"},
{file = "mongoengine-0.27.0.tar.gz", hash = "sha256:8f38df7834dc4b192d89f2668dcf3091748d12f74d55648ce77b919167a4a49b"},
]
[package.dependencies]
pymongo = ">=3.4,<5.0"
[[package]]
name = "openai"
version = "1.1.1"
description = "Client library for the openai API"
optional = false
python-versions = ">=3.7.1"
files = [
{file = "openai-1.1.1-py3-none-any.whl", hash = "sha256:1496418b132c88352bcfffa8c24e83a69f0e01b1484cbb7bb48f722aad8fd6e1"},
{file = "openai-1.1.1.tar.gz", hash = "sha256:80e49cb21d8445f6d51339b8af7376fc83302c78ab78578b78133ef89634869d"},
]
[package.dependencies]
anyio = ">=3.5.0,<4"
distro = ">=1.7.0,<2"
httpx = ">=0.23.0,<1"
pydantic = ">=1.9.0,<3"
tqdm = ">4"
typing-extensions = ">=4.5,<5"
[package.extras]
datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"]
[[package]] [[package]]
name = "pyaes" name = "pyaes"
version = "1.6.1" version = "1.6.1"
@ -258,245 +118,6 @@ files = [
{file = "pyasn1-0.5.0.tar.gz", hash = "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde"}, {file = "pyasn1-0.5.0.tar.gz", hash = "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde"},
] ]
[[package]]
name = "pydantic"
version = "2.4.2"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.7"
files = [
{file = "pydantic-2.4.2-py3-none-any.whl", hash = "sha256:bc3ddf669d234f4220e6e1c4d96b061abe0998185a8d7855c0126782b7abc8c1"},
{file = "pydantic-2.4.2.tar.gz", hash = "sha256:94f336138093a5d7f426aac732dcfe7ab4eb4da243c88f891d65deb4a2556ee7"},
]
[package.dependencies]
annotated-types = ">=0.4.0"
pydantic-core = "2.10.1"
typing-extensions = ">=4.6.1"
[package.extras]
email = ["email-validator (>=2.0.0)"]
[[package]]
name = "pydantic-core"
version = "2.10.1"
description = ""
optional = false
python-versions = ">=3.7"
files = [
{file = "pydantic_core-2.10.1-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:d64728ee14e667ba27c66314b7d880b8eeb050e58ffc5fec3b7a109f8cddbd63"},
{file = "pydantic_core-2.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:48525933fea744a3e7464c19bfede85df4aba79ce90c60b94d8b6e1eddd67096"},
{file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef337945bbd76cce390d1b2496ccf9f90b1c1242a3a7bc242ca4a9fc5993427a"},
{file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1392e0638af203cee360495fd2cfdd6054711f2db5175b6e9c3c461b76f5175"},
{file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0675ba5d22de54d07bccde38997e780044dcfa9a71aac9fd7d4d7a1d2e3e65f7"},
{file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:128552af70a64660f21cb0eb4876cbdadf1a1f9d5de820fed6421fa8de07c893"},
{file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f6e6aed5818c264412ac0598b581a002a9f050cb2637a84979859e70197aa9e"},
{file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ecaac27da855b8d73f92123e5f03612b04c5632fd0a476e469dfc47cd37d6b2e"},
{file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b3c01c2fb081fced3bbb3da78510693dc7121bb893a1f0f5f4b48013201f362e"},
{file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:92f675fefa977625105708492850bcbc1182bfc3e997f8eecb866d1927c98ae6"},
{file = "pydantic_core-2.10.1-cp310-none-win32.whl", hash = "sha256:420a692b547736a8d8703c39ea935ab5d8f0d2573f8f123b0a294e49a73f214b"},
{file = "pydantic_core-2.10.1-cp310-none-win_amd64.whl", hash = "sha256:0880e239827b4b5b3e2ce05e6b766a7414e5f5aedc4523be6b68cfbc7f61c5d0"},
{file = "pydantic_core-2.10.1-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:073d4a470b195d2b2245d0343569aac7e979d3a0dcce6c7d2af6d8a920ad0bea"},
{file = "pydantic_core-2.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:600d04a7b342363058b9190d4e929a8e2e715c5682a70cc37d5ded1e0dd370b4"},
{file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39215d809470f4c8d1881758575b2abfb80174a9e8daf8f33b1d4379357e417c"},
{file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eeb3d3d6b399ffe55f9a04e09e635554012f1980696d6b0aca3e6cf42a17a03b"},
{file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a7902bf75779bc12ccfc508bfb7a4c47063f748ea3de87135d433a4cca7a2f"},
{file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3625578b6010c65964d177626fde80cf60d7f2e297d56b925cb5cdeda6e9925a"},
{file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa48fc31fc7243e50188197b5f0c4228956f97b954f76da157aae7f67269ae8"},
{file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:07ec6d7d929ae9c68f716195ce15e745b3e8fa122fc67698ac6498d802ed0fa4"},
{file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6f31a17acede6a8cd1ae2d123ce04d8cca74056c9d456075f4f6f85de055607"},
{file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d8f1ebca515a03e5654f88411420fea6380fc841d1bea08effb28184e3d4899f"},
{file = "pydantic_core-2.10.1-cp311-none-win32.whl", hash = "sha256:6db2eb9654a85ada248afa5a6db5ff1cf0f7b16043a6b070adc4a5be68c716d6"},
{file = "pydantic_core-2.10.1-cp311-none-win_amd64.whl", hash = "sha256:4a5be350f922430997f240d25f8219f93b0c81e15f7b30b868b2fddfc2d05f27"},
{file = "pydantic_core-2.10.1-cp311-none-win_arm64.whl", hash = "sha256:5fdb39f67c779b183b0c853cd6b45f7db84b84e0571b3ef1c89cdb1dfc367325"},
{file = "pydantic_core-2.10.1-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:b1f22a9ab44de5f082216270552aa54259db20189e68fc12484873d926426921"},
{file = "pydantic_core-2.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8572cadbf4cfa95fb4187775b5ade2eaa93511f07947b38f4cd67cf10783b118"},
{file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db9a28c063c7c00844ae42a80203eb6d2d6bbb97070cfa00194dff40e6f545ab"},
{file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e2a35baa428181cb2270a15864ec6286822d3576f2ed0f4cd7f0c1708472aff"},
{file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05560ab976012bf40f25d5225a58bfa649bb897b87192a36c6fef1ab132540d7"},
{file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6495008733c7521a89422d7a68efa0a0122c99a5861f06020ef5b1f51f9ba7c"},
{file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ac492c686defc8e6133e3a2d9eaf5261b3df26b8ae97450c1647286750b901"},
{file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8282bab177a9a3081fd3d0a0175a07a1e2bfb7fcbbd949519ea0980f8a07144d"},
{file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:aafdb89fdeb5fe165043896817eccd6434aee124d5ee9b354f92cd574ba5e78f"},
{file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f6defd966ca3b187ec6c366604e9296f585021d922e666b99c47e78738b5666c"},
{file = "pydantic_core-2.10.1-cp312-none-win32.whl", hash = "sha256:7c4d1894fe112b0864c1fa75dffa045720a194b227bed12f4be7f6045b25209f"},
{file = "pydantic_core-2.10.1-cp312-none-win_amd64.whl", hash = "sha256:5994985da903d0b8a08e4935c46ed8daf5be1cf217489e673910951dc533d430"},
{file = "pydantic_core-2.10.1-cp312-none-win_arm64.whl", hash = "sha256:0d8a8adef23d86d8eceed3e32e9cca8879c7481c183f84ed1a8edc7df073af94"},
{file = "pydantic_core-2.10.1-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9badf8d45171d92387410b04639d73811b785b5161ecadabf056ea14d62d4ede"},
{file = "pydantic_core-2.10.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:ebedb45b9feb7258fac0a268a3f6bec0a2ea4d9558f3d6f813f02ff3a6dc6698"},
{file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfe1090245c078720d250d19cb05d67e21a9cd7c257698ef139bc41cf6c27b4f"},
{file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e357571bb0efd65fd55f18db0a2fb0ed89d0bb1d41d906b138f088933ae618bb"},
{file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3dcd587b69bbf54fc04ca157c2323b8911033e827fffaecf0cafa5a892a0904"},
{file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c120c9ce3b163b985a3b966bb701114beb1da4b0468b9b236fc754783d85aa3"},
{file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15d6bca84ffc966cc9976b09a18cf9543ed4d4ecbd97e7086f9ce9327ea48891"},
{file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cabb9710f09d5d2e9e2748c3e3e20d991a4c5f96ed8f1132518f54ab2967221"},
{file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:82f55187a5bebae7d81d35b1e9aaea5e169d44819789837cdd4720d768c55d15"},
{file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1d40f55222b233e98e3921df7811c27567f0e1a4411b93d4c5c0f4ce131bc42f"},
{file = "pydantic_core-2.10.1-cp37-none-win32.whl", hash = "sha256:14e09ff0b8fe6e46b93d36a878f6e4a3a98ba5303c76bb8e716f4878a3bee92c"},
{file = "pydantic_core-2.10.1-cp37-none-win_amd64.whl", hash = "sha256:1396e81b83516b9d5c9e26a924fa69164156c148c717131f54f586485ac3c15e"},
{file = "pydantic_core-2.10.1-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:6835451b57c1b467b95ffb03a38bb75b52fb4dc2762bb1d9dbed8de31ea7d0fc"},
{file = "pydantic_core-2.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b00bc4619f60c853556b35f83731bd817f989cba3e97dc792bb8c97941b8053a"},
{file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fa467fd300a6f046bdb248d40cd015b21b7576c168a6bb20aa22e595c8ffcdd"},
{file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d99277877daf2efe074eae6338453a4ed54a2d93fb4678ddfe1209a0c93a2468"},
{file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa7db7558607afeccb33c0e4bf1c9a9a835e26599e76af6fe2fcea45904083a6"},
{file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aad7bd686363d1ce4ee930ad39f14e1673248373f4a9d74d2b9554f06199fb58"},
{file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:443fed67d33aa85357464f297e3d26e570267d1af6fef1c21ca50921d2976302"},
{file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:042462d8d6ba707fd3ce9649e7bf268633a41018d6a998fb5fbacb7e928a183e"},
{file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ecdbde46235f3d560b18be0cb706c8e8ad1b965e5c13bbba7450c86064e96561"},
{file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ed550ed05540c03f0e69e6d74ad58d026de61b9eaebebbaaf8873e585cbb18de"},
{file = "pydantic_core-2.10.1-cp38-none-win32.whl", hash = "sha256:8cdbbd92154db2fec4ec973d45c565e767ddc20aa6dbaf50142676484cbff8ee"},
{file = "pydantic_core-2.10.1-cp38-none-win_amd64.whl", hash = "sha256:9f6f3e2598604956480f6c8aa24a3384dbf6509fe995d97f6ca6103bb8c2534e"},
{file = "pydantic_core-2.10.1-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:655f8f4c8d6a5963c9a0687793da37b9b681d9ad06f29438a3b2326d4e6b7970"},
{file = "pydantic_core-2.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e570ffeb2170e116a5b17e83f19911020ac79d19c96f320cbfa1fa96b470185b"},
{file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64322bfa13e44c6c30c518729ef08fda6026b96d5c0be724b3c4ae4da939f875"},
{file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:485a91abe3a07c3a8d1e082ba29254eea3e2bb13cbbd4351ea4e5a21912cc9b0"},
{file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7c2b8eb9fc872e68b46eeaf835e86bccc3a58ba57d0eedc109cbb14177be531"},
{file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5cb87bdc2e5f620693148b5f8f842d293cae46c5f15a1b1bf7ceeed324a740c"},
{file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25bd966103890ccfa028841a8f30cebcf5875eeac8c4bde4fe221364c92f0c9a"},
{file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f323306d0556351735b54acbf82904fe30a27b6a7147153cbe6e19aaaa2aa429"},
{file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0c27f38dc4fbf07b358b2bc90edf35e82d1703e22ff2efa4af4ad5de1b3833e7"},
{file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f1365e032a477c1430cfe0cf2856679529a2331426f8081172c4a74186f1d595"},
{file = "pydantic_core-2.10.1-cp39-none-win32.whl", hash = "sha256:a1c311fd06ab3b10805abb72109f01a134019739bd3286b8ae1bc2fc4e50c07a"},
{file = "pydantic_core-2.10.1-cp39-none-win_amd64.whl", hash = "sha256:ae8a8843b11dc0b03b57b52793e391f0122e740de3df1474814c700d2622950a"},
{file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d43002441932f9a9ea5d6f9efaa2e21458221a3a4b417a14027a1d530201ef1b"},
{file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fcb83175cc4936a5425dde3356f079ae03c0802bbdf8ff82c035f8a54b333521"},
{file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:962ed72424bf1f72334e2f1e61b68f16c0e596f024ca7ac5daf229f7c26e4208"},
{file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cf5bb4dd67f20f3bbc1209ef572a259027c49e5ff694fa56bed62959b41e1f9"},
{file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e544246b859f17373bed915182ab841b80849ed9cf23f1f07b73b7c58baee5fb"},
{file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c0877239307b7e69d025b73774e88e86ce82f6ba6adf98f41069d5b0b78bd1bf"},
{file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:53df009d1e1ba40f696f8995683e067e3967101d4bb4ea6f667931b7d4a01357"},
{file = "pydantic_core-2.10.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a1254357f7e4c82e77c348dabf2d55f1d14d19d91ff025004775e70a6ef40ada"},
{file = "pydantic_core-2.10.1-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:524ff0ca3baea164d6d93a32c58ac79eca9f6cf713586fdc0adb66a8cdeab96a"},
{file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f0ac9fb8608dbc6eaf17956bf623c9119b4db7dbb511650910a82e261e6600f"},
{file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:320f14bd4542a04ab23747ff2c8a778bde727158b606e2661349557f0770711e"},
{file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63974d168b6233b4ed6a0046296803cb13c56637a7b8106564ab575926572a55"},
{file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:417243bf599ba1f1fef2bb8c543ceb918676954734e2dcb82bf162ae9d7bd514"},
{file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dda81e5ec82485155a19d9624cfcca9be88a405e2857354e5b089c2a982144b2"},
{file = "pydantic_core-2.10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:14cfbb00959259e15d684505263d5a21732b31248a5dd4941f73a3be233865b9"},
{file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:631cb7415225954fdcc2a024119101946793e5923f6c4d73a5914d27eb3d3a05"},
{file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:bec7dd208a4182e99c5b6c501ce0b1f49de2802448d4056091f8e630b28e9a52"},
{file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:149b8a07712f45b332faee1a2258d8ef1fb4a36f88c0c17cb687f205c5dc6e7d"},
{file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d966c47f9dd73c2d32a809d2be529112d509321c5310ebf54076812e6ecd884"},
{file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7eb037106f5c6b3b0b864ad226b0b7ab58157124161d48e4b30c4a43fef8bc4b"},
{file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:154ea7c52e32dce13065dbb20a4a6f0cc012b4f667ac90d648d36b12007fa9f7"},
{file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e562617a45b5a9da5be4abe72b971d4f00bf8555eb29bb91ec2ef2be348cd132"},
{file = "pydantic_core-2.10.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f23b55eb5464468f9e0e9a9935ce3ed2a870608d5f534025cd5536bca25b1402"},
{file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:e9121b4009339b0f751955baf4543a0bfd6bc3f8188f8056b1a25a2d45099934"},
{file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0523aeb76e03f753b58be33b26540880bac5aa54422e4462404c432230543f33"},
{file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0e2959ef5d5b8dc9ef21e1a305a21a36e254e6a34432d00c72a92fdc5ecda5"},
{file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da01bec0a26befab4898ed83b362993c844b9a607a86add78604186297eb047e"},
{file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2e9072d71c1f6cfc79a36d4484c82823c560e6f5599c43c1ca6b5cdbd54f881"},
{file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f36a3489d9e28fe4b67be9992a23029c3cec0babc3bd9afb39f49844a8c721c5"},
{file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f64f82cc3443149292b32387086d02a6c7fb39b8781563e0ca7b8d7d9cf72bd7"},
{file = "pydantic_core-2.10.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b4a6db486ac8e99ae696e09efc8b2b9fea67b63c8f88ba7a1a16c24a057a0776"},
{file = "pydantic_core-2.10.1.tar.gz", hash = "sha256:0f8682dbdd2f67f8e1edddcbffcc29f60a6182b4901c367fc8c1c40d30bb0a82"},
]
[package.dependencies]
typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
[[package]]
name = "pymongo"
version = "4.6.0"
description = "Python driver for MongoDB <http://www.mongodb.org>"
optional = false
python-versions = ">=3.7"
files = [
{file = "pymongo-4.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c011bd5ad03cc096f99ffcfdd18a1817354132c1331bed7a837a25226659845f"},
{file = "pymongo-4.6.0-cp310-cp310-manylinux1_i686.whl", hash = "sha256:5e63146dbdb1eac207464f6e0cfcdb640c9c5ff0f57b754fa96fe252314a1dc6"},
{file = "pymongo-4.6.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:2972dd1f1285866aba027eff2f4a2bbf8aa98563c2ced14cb34ee5602b36afdf"},
{file = "pymongo-4.6.0-cp310-cp310-manylinux2014_i686.whl", hash = "sha256:a0be99b599da95b7a90a918dd927b20c434bea5e1c9b3efc6a3c6cd67c23f813"},
{file = "pymongo-4.6.0-cp310-cp310-manylinux2014_ppc64le.whl", hash = "sha256:9b0f98481ad5dc4cb430a60bbb8869f05505283b9ae1c62bdb65eb5e020ee8e3"},
{file = "pymongo-4.6.0-cp310-cp310-manylinux2014_s390x.whl", hash = "sha256:256c503a75bd71cf7fb9ebf889e7e222d49c6036a48aad5a619f98a0adf0e0d7"},
{file = "pymongo-4.6.0-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:b4ad70d7cac4ca0c7b31444a0148bd3af01a2662fa12b1ad6f57cd4a04e21766"},
{file = "pymongo-4.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5717a308a703dda2886a5796a07489c698b442f5e409cf7dc2ac93de8d61d764"},
{file = "pymongo-4.6.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f7f9feecae53fa18d6a3ea7c75f9e9a1d4d20e5c3f9ce3fba83f07bcc4eee2"},
{file = "pymongo-4.6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:128b1485753106c54af481789cdfea12b90a228afca0b11fb3828309a907e10e"},
{file = "pymongo-4.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3077a31633beef77d057c6523f5de7271ddef7bde5e019285b00c0cc9cac1e3"},
{file = "pymongo-4.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebf02c32afa6b67e5861a27183dd98ed88419a94a2ab843cc145fb0bafcc5b28"},
{file = "pymongo-4.6.0-cp310-cp310-win32.whl", hash = "sha256:b14dd73f595199f4275bed4fb509277470d9b9059310537e3b3daba12b30c157"},
{file = "pymongo-4.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:8adf014f2779992eba3b513e060d06f075f0ab2fb3ad956f413a102312f65cdf"},
{file = "pymongo-4.6.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ba51129fcc510824b6ca6e2ce1c27e3e4d048b6e35d3ae6f7e517bed1b8b25ce"},
{file = "pymongo-4.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2973f113e079fb98515722cd728e1820282721ec9fd52830e4b73cabdbf1eb28"},
{file = "pymongo-4.6.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af425f323fce1b07755edd783581e7283557296946212f5b1a934441718e7528"},
{file = "pymongo-4.6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ec71ac633b126c0775ed4604ca8f56c3540f5c21a1220639f299e7a544b55f9"},
{file = "pymongo-4.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ec6c20385c5a58e16b1ea60c5e4993ea060540671d7d12664f385f2fb32fe79"},
{file = "pymongo-4.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:85f2cdc400ee87f5952ebf2a117488f2525a3fb2e23863a8efe3e4ee9e54e4d1"},
{file = "pymongo-4.6.0-cp311-cp311-win32.whl", hash = "sha256:7fc2bb8a74dcfcdd32f89528e38dcbf70a3a6594963d60dc9595e3b35b66e414"},
{file = "pymongo-4.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:6695d7136a435c1305b261a9ddb9b3ecec9863e05aab3935b96038145fd3a977"},
{file = "pymongo-4.6.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d603edea1ff7408638b2504905c032193b7dcee7af269802dbb35bc8c3310ed5"},
{file = "pymongo-4.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79f41576b3022c2fe9780ae3e44202b2438128a25284a8ddfa038f0785d87019"},
{file = "pymongo-4.6.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49f2af6cf82509b15093ce3569229e0d53c90ad8ae2eef940652d4cf1f81e045"},
{file = "pymongo-4.6.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecd9e1fa97aa11bf67472220285775fa15e896da108f425e55d23d7540a712ce"},
{file = "pymongo-4.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d2be5c9c3488fa8a70f83ed925940f488eac2837a996708d98a0e54a861f212"},
{file = "pymongo-4.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ab6bcc8e424e07c1d4ba6df96f7fb963bcb48f590b9456de9ebd03b88084fe8"},
{file = "pymongo-4.6.0-cp312-cp312-win32.whl", hash = "sha256:47aa128be2e66abd9d1a9b0437c62499d812d291f17b55185cb4aa33a5f710a4"},
{file = "pymongo-4.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:014e7049dd019a6663747ca7dae328943e14f7261f7c1381045dfc26a04fa330"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:288c21ab9531b037f7efa4e467b33176bc73a0c27223c141b822ab4a0e66ff2a"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:747c84f4e690fbe6999c90ac97246c95d31460d890510e4a3fa61b7d2b87aa34"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:055f5c266e2767a88bb585d01137d9c7f778b0195d3dbf4a487ef0638be9b651"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:82e620842e12e8cb4050d2643a81c8149361cd82c0a920fa5a15dc4ca8a4000f"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:6b18276f14b4b6d92e707ab6db19b938e112bd2f1dc3f9f1a628df58e4fd3f0d"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:680fa0fc719e1a3dcb81130858368f51d83667d431924d0bcf249644bce8f303"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:3919708594b86d0f5cdc713eb6fccd3f9b9532af09ea7a5d843c933825ef56c4"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db082f728160369d9a6ed2e722438291558fc15ce06d0a7d696a8dad735c236b"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e4ed21029d80c4f62605ab16398fe1ce093fff4b5f22d114055e7d9fbc4adb0"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bea9138b0fc6e2218147e9c6ce1ff76ff8e29dc00bb1b64842bd1ca107aee9f"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a0269811661ba93c472c8a60ea82640e838c2eb148d252720a09b5123f2c2fe"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d6a1b1361f118e7fefa17ae3114e77f10ee1b228b20d50c47c9f351346180c8"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e3b0127b260d4abae7b62203c4c7ef0874c901b55155692353db19de4b18bc4"},
{file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a49aca4d961823b2846b739380c847e8964ff7ae0f0a683992b9d926054f0d6d"},
{file = "pymongo-4.6.0-cp37-cp37m-win32.whl", hash = "sha256:09c7de516b08c57647176b9fc21d929d628e35bcebc7422220c89ae40b62126a"},
{file = "pymongo-4.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:81dd1308bd5630d2bb5980f00aa163b986b133f1e9ed66c66ce2a5bc3572e891"},
{file = "pymongo-4.6.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:2f8c04277d879146eacda920476e93d520eff8bec6c022ac108cfa6280d84348"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5802acc012bbb4bce4dff92973dff76482f30ef35dd4cb8ab5b0e06aa8f08c80"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ccd785fafa1c931deff6a7116e9a0d402d59fabe51644b0d0c268295ff847b25"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fe03bf25fae4b95d8afe40004a321df644400fdcba4c8e5e1a19c1085b740888"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:2ca0ba501898b2ec31e6c3acf90c31910944f01d454ad8e489213a156ccf1bda"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:10a379fb60f1b2406ae57b8899bacfe20567918c8e9d2d545e1b93628fcf2050"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:a4dc1319d0c162919ee7f4ee6face076becae2abbd351cc14f1fe70af5fb20d9"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ddef295aaf80cefb0c1606f1995899efcb17edc6b327eb6589e234e614b87756"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:518c90bdd6e842c446d01a766b9136fec5ec6cc94f3b8c3f8b4a332786ee6b64"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b80a4ee19b3442c57c38afa978adca546521a8822d663310b63ae2a7d7b13f3a"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb438a8bf6b695bf50d57e6a059ff09652a07968b2041178b3744ea785fcef9b"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3db7d833a7c38c317dc95b54e27f1d27012e031b45a7c24e360b53197d5f6e7"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3729b8db02063da50eeb3db88a27670d85953afb9a7f14c213ac9e3dca93034b"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:39a1cd5d383b37285641d5a7a86be85274466ae336a61b51117155936529f9b3"},
{file = "pymongo-4.6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7b0e6361754ac596cd16bfc6ed49f69ffcd9b60b7bc4bcd3ea65c6a83475e4ff"},
{file = "pymongo-4.6.0-cp38-cp38-win32.whl", hash = "sha256:806e094e9e85d8badc978af8c95b69c556077f11844655cb8cd2d1758769e521"},
{file = "pymongo-4.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1394c4737b325166a65ae7c145af1ebdb9fb153ebedd37cf91d676313e4a67b8"},
{file = "pymongo-4.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a8273e1abbcff1d7d29cbbb1ea7e57d38be72f1af3c597c854168508b91516c2"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:e16ade71c93f6814d095d25cd6d28a90d63511ea396bd96e9ffcb886b278baaa"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:325701ae7b56daa5b0692305b7cb505ca50f80a1288abb32ff420a8a209b01ca"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:cc94f9fea17a5af8cf1a343597711a26b0117c0b812550d99934acb89d526ed2"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:21812453354b151200034750cd30b0140e82ec2a01fd4357390f67714a1bfbde"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:0634994b026336195778e5693583c060418d4ab453eff21530422690a97e1ee8"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:ad4f66fbb893b55f96f03020e67dcab49ffde0177c6565ccf9dec4fdf974eb61"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:2703a9f8f5767986b4f51c259ff452cc837c5a83c8ed5f5361f6e49933743b2f"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bafea6061d63059d8bc2ffc545e2f049221c8a4457d236c5cd6a66678673eab"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f28ae33dc5a0b9cee06e95fd420e42155d83271ab75964baf747ce959cac5f52"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16a534da0e39785687b7295e2fcf9a339f4a20689024983d11afaa4657f8507"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef67fedd863ffffd4adfd46d9d992b0f929c7f61a8307366d664d93517f2c78e"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05c30fd35cc97f14f354916b45feea535d59060ef867446b5c3c7f9b609dd5dc"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1c63e3a2e8fb815c4b1f738c284a4579897e37c3cfd95fdb199229a1ccfb638a"},
{file = "pymongo-4.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e5e193f89f4f8c1fe273f9a6e6df915092c9f2af6db2d1afb8bd53855025c11f"},
{file = "pymongo-4.6.0-cp39-cp39-win32.whl", hash = "sha256:a09bfb51953930e7e838972ddf646c5d5f984992a66d79da6ba7f6a8d8a890cd"},
{file = "pymongo-4.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:107a234dc55affc5802acb3b6d83cbb8c87355b38a9457fcd8806bdeb8bce161"},
{file = "pymongo-4.6.0.tar.gz", hash = "sha256:fb1c56d891f9e34303c451998ef62ba52659648bb0d75b03c5e4ac223a3342c2"},
]
[package.dependencies]
dnspython = ">=1.16.0,<3.0.0"
[package.extras]
aws = ["pymongo-auth-aws (<2.0.0)"]
encryption = ["certifi", "pymongo[aws]", "pymongocrypt (>=1.6.0,<2.0.0)"]
gssapi = ["pykerberos", "winkerberos (>=0.5.0)"]
ocsp = ["certifi", "cryptography (>=2.5)", "pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)"]
snappy = ["python-snappy"]
test = ["pytest (>=7)"]
zstd = ["zstandard"]
[[package]] [[package]]
name = "python-dotenv" name = "python-dotenv"
version = "1.0.0" version = "1.0.0"
@ -537,80 +158,23 @@ files = [
] ]
[[package]] [[package]]
name = "telemongo" name = "telethon"
version = "0.2.2" version = "1.30.3"
description = "MongoDB backend for Telethon session storage" description = "Full-featured Telegram client library for Python 3"
optional = false optional = false
python-versions = "^3.9" python-versions = ">=3.5"
files = []
develop = true
[package.dependencies]
mongoengine = "^0.27.0"
telethon = {git = "https://github.com/LonamiWebs/Telethon", rev = "v2", subdirectory = "client"}
[package.source]
type = "directory"
url = "../telethon-session-mongo"
[[package]]
name = "Telethon"
version = "2.0.0a0"
description = "Full-featured Telegram client library"
optional = false
python-versions = ">=3.8"
files = []
develop = false
[package.dependencies]
markdown-it-py = ">=3.0,<4.0"
pyaes = ">=1.6,<2.0"
rsa = ">=4.9,<5.0"
[package.extras]
cryptg = ["cryptg (>=0.4,<1.0)"]
dev = ["black (>=23.3.0,<23.4.0)", "isort (>=5.12,<6.0)", "mypy (>=1.3,<2.0)", "pytest (>=7.3,<8.0)", "pytest-asyncio (>=0.21,<1.0)", "ruff (>=0.0.292,<0.1.0)"]
doc = ["sphinx_rtd_theme (>=1.2,<2.0)", "types-docutils (>=0.20,<1.0)"]
[package.source]
type = "git"
url = "https://github.com/LonamiWebs/Telethon"
reference = "v2"
resolved_reference = "ae44426a78d553cd1a3ea9f536c0f2c90751d657"
subdirectory = "client"
[[package]]
name = "tqdm"
version = "4.66.1"
description = "Fast, Extensible Progress Meter"
optional = false
python-versions = ">=3.7"
files = [ files = [
{file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"}, {file = "Telethon-1.30.3.tar.gz", hash = "sha256:313e40fa06667b19ced13b379d9988167a8319bc0eb90bf39347cff46919a351"},
{file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"},
] ]
[package.dependencies] [package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""} pyaes = "*"
rsa = "*"
[package.extras] [package.extras]
dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] cryptg = ["cryptg"]
notebook = ["ipywidgets (>=6)"]
slack = ["slack-sdk"]
telegram = ["requests"]
[[package]]
name = "typing-extensions"
version = "4.8.0"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
{file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"},
{file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"},
]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.11" python-versions = "^3.11"
content-hash = "4dcc24786204463583f6e954cb90106d79ec504ffa7f3f9ad321444e55cc5e9a" content-hash = "9ef099a5cfddbb3a84d27506f528e2edca10562fad5dabeb22bd64d49d9c1b04"

8
pyproject.toml Executable file → Normal file
View File

@ -10,14 +10,10 @@ fenneko = "cyber_fenneko.__main__:main"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.11" python = "^3.11"
# telethon = "^1.30.3" telethon = "^1.30.3"
python-dotenv = "^1.0.0" python-dotenv = "^1.0.0"
httpx = "^0.25.0" httpx = "^0.25.0"
openai = {version = "^1.0.0b3", allow-prereleases = true}
telemongo = {path = "../telethon-session-mongo", develop=true}
mongoengine = "^0.27.0"
telethon = {git = "https://github.com/LonamiWebs/Telethon", rev = "v2", subdirectory = "client"}
jsonpickle = "^3.0.2"
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]

95
requirements.txt Executable file → Normal file
View File

@ -4,9 +4,6 @@ anyio==4.0.0 ; python_version >= "3.11" and python_version < "4.0" \
certifi==2023.7.22 ; python_version >= "3.11" and python_version < "4.0" \ certifi==2023.7.22 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \
--hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9 --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9
dnspython==2.4.2 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8 \
--hash=sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984
h11==0.14.0 ; python_version >= "3.11" and python_version < "4.0" \ h11==0.14.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \ --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \
--hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761 --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761
@ -19,96 +16,11 @@ httpx==0.25.0 ; python_version >= "3.11" and python_version < "4.0" \
idna==3.4 ; python_version >= "3.11" and python_version < "4.0" \ idna==3.4 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \
--hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2
mongoengine==0.27.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:8f38df7834dc4b192d89f2668dcf3091748d12f74d55648ce77b919167a4a49b \
--hash=sha256:c3523b8f886052f3deb200b3218bcc13e4b781661e3bea38587cc936c80ea358
pyaes==1.6.1 ; python_version >= "3.11" and python_version < "4.0" \ pyaes==1.6.1 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f --hash=sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f
pyasn1==0.5.0 ; python_version >= "3.11" and python_version < "4" \ pyasn1==0.5.0 ; python_version >= "3.11" and python_version < "4" \
--hash=sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57 \ --hash=sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57 \
--hash=sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde --hash=sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde
pymongo==4.5.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:076afa0a4a96ca9f77fec0e4a0d241200b3b3a1766f8d7be9a905ecf59a7416b \
--hash=sha256:08819da7864f9b8d4a95729b2bea5fffed08b63d3b9c15b4fea47de655766cf5 \
--hash=sha256:0a1f26bc1f5ce774d99725773901820dfdfd24e875028da4a0252a5b48dcab5c \
--hash=sha256:0f4b125b46fe377984fbaecf2af40ed48b05a4b7676a2ff98999f2016d66b3ec \
--hash=sha256:1240edc1a448d4ada4bf1a0e55550b6292420915292408e59159fd8bbdaf8f63 \
--hash=sha256:152259f0f1a60f560323aacf463a3642a65a25557683f49cfa08c8f1ecb2395a \
--hash=sha256:168172ef7856e20ec024fe2a746bfa895c88b32720138e6438fd765ebd2b62dd \
--hash=sha256:1b1d7d9aabd8629a31d63cd106d56cca0e6420f38e50563278b520f385c0d86e \
--hash=sha256:1d40ad09d9f5e719bc6f729cc6b17f31c0b055029719406bd31dde2f72fca7e7 \
--hash=sha256:21b953da14549ff62ea4ae20889c71564328958cbdf880c64a92a48dda4c9c53 \
--hash=sha256:23cc6d7eb009c688d70da186b8f362d61d5dd1a2c14a45b890bd1e91e9c451f2 \
--hash=sha256:2988ef5e6b360b3ff1c6d55c53515499de5f48df31afd9f785d788cdacfbe2d3 \
--hash=sha256:2a0aade2b11dc0c326ccd429ee4134d2d47459ff68d449c6d7e01e74651bd255 \
--hash=sha256:2b0176f9233a5927084c79ff80b51bd70bfd57e4f3d564f50f80238e797f0c8a \
--hash=sha256:2d4fa1b01fa7e5b7bb8d312e3542e211b320eb7a4e3d8dc884327039d93cb9e0 \
--hash=sha256:3236cf89d69679eaeb9119c840f5c7eb388a2110b57af6bb6baf01a1da387c18 \
--hash=sha256:33faa786cc907de63f745f587e9879429b46033d7d97a7b84b37f4f8f47b9b32 \
--hash=sha256:37df8f6006286a5896d1cbc3efb8471ced42e3568d38e6cb00857277047b0d63 \
--hash=sha256:3a7166d57dc74d679caa7743b8ecf7dc3a1235a9fd178654dddb2b2a627ae229 \
--hash=sha256:3d79ae3bb1ff041c0db56f138c88ce1dfb0209f3546d8d6e7c3f74944ecd2439 \
--hash=sha256:3e33064f1984db412b34d51496f4ea785a9cff621c67de58e09fb28da6468a52 \
--hash=sha256:3fa3648e4f1e63ddfe53563ee111079ea3ab35c3b09cd25bc22dadc8269a495f \
--hash=sha256:40d5f6e853ece9bfc01e9129b228df446f49316a4252bb1fbfae5c3c9dedebad \
--hash=sha256:41771b22dd2822540f79a877c391283d4e6368125999a5ec8beee1ce566f3f82 \
--hash=sha256:435228d3c16a375274ac8ab9c4f9aef40c5e57ddb8296e20ecec9e2461da1017 \
--hash=sha256:44ee985194c426ddf781fa784f31ffa29cb59657b2dba09250a4245431847d73 \
--hash=sha256:465fd5b040206f8bce7016b01d7e7f79d2fcd7c2b8e41791be9632a9df1b4999 \
--hash=sha256:496c9cbcb4951183d4503a9d7d2c1e3694aab1304262f831d5e1917e60386036 \
--hash=sha256:49dce6957598975d8b8d506329d2a3a6c4aee911fa4bbcf5e52ffc6897122950 \
--hash=sha256:4c42748ccc451dfcd9cef6c5447a7ab727351fd9747ad431db5ebb18a9b78a4d \
--hash=sha256:505f8519c4c782a61d94a17b0da50be639ec462128fbd10ab0a34889218fdee3 \
--hash=sha256:53f2dda54d76a98b43a410498bd12f6034b2a14b6844ca08513733b2b20b7ad8 \
--hash=sha256:56320c401f544d762fc35766936178fbceb1d9261cd7b24fbfbc8fb6f67aa8a5 \
--hash=sha256:58a63a26a1e3dc481dd3a18d6d9f8bd1d576cd1ffe0d479ba7dd38b0aeb20066 \
--hash=sha256:5caee7bd08c3d36ec54617832b44985bd70c4cbd77c5b313de6f7fce0bb34f93 \
--hash=sha256:631492573a1bef2f74f9ac0f9d84e0ce422c251644cd81207530af4aa2ee1980 \
--hash=sha256:63d8019eee119df308a075b8a7bdb06d4720bf791e2b73d5ab0e7473c115d79c \
--hash=sha256:6422b6763b016f2ef2beedded0e546d6aa6ba87910f9244d86e0ac7690f75c96 \
--hash=sha256:681f252e43b3ef054ca9161635f81b730f4d8cadd28b3f2b2004f5a72f853982 \
--hash=sha256:6d64878d1659d2a5bdfd0f0a4d79bafe68653c573681495e424ab40d7b6d6d41 \
--hash=sha256:74c0da07c04d0781490b2915e7514b1adb265ef22af039a947988c331ee7455b \
--hash=sha256:7591a3beea6a9a4fa3080d27d193b41f631130e3ffa76b88c9ccea123f26dc59 \
--hash=sha256:76a262c41c1a7cbb84a3b11976578a7eb8e788c4b7bfbd15c005fb6ca88e6e50 \
--hash=sha256:77cfff95c1fafd09e940b3fdcb7b65f11442662fad611d0e69b4dd5d17a81c60 \
--hash=sha256:8027c9063579083746147cf401a7072a9fb6829678076cd3deff28bb0e0f50c8 \
--hash=sha256:80a167081c75cf66b32f30e2f1eaee9365af935a86dbd76788169911bed9b5d5 \
--hash=sha256:840eaf30ccac122df260b6005f9dfae4ac287c498ee91e3e90c56781614ca238 \
--hash=sha256:8543253adfaa0b802bfa88386db1009c6ebb7d5684d093ee4edc725007553d21 \
--hash=sha256:89b3f2da57a27913d15d2a07d58482f33d0a5b28abd20b8e643ab4d625e36257 \
--hash=sha256:8e559116e4128630ad3b7e788e2e5da81cbc2344dee246af44471fa650486a70 \
--hash=sha256:9aff6279e405dc953eeb540ab061e72c03cf38119613fce183a8e94f31be608f \
--hash=sha256:9c04b9560872fa9a91251030c488e0a73bce9321a70f991f830c72b3f8115d0d \
--hash=sha256:9d2346b00af524757576cc2406414562cced1d4349c92166a0ee377a2a483a80 \
--hash=sha256:a253b765b7cbc4209f1d8ee16c7287c4268d3243070bf72d7eec5aa9dfe2a2c2 \
--hash=sha256:a8127437ebc196a6f5e8fddd746bd0903a400dc6b5ae35df672dd1ccc7170a2a \
--hash=sha256:b25f7bea162b3dbec6d33c522097ef81df7c19a9300722fa6853f5b495aecb77 \
--hash=sha256:b33c17d9e694b66d7e96977e9e56df19d662031483efe121a24772a44ccbbc7e \
--hash=sha256:b4fe46b58010115514b842c669a0ed9b6a342017b15905653a5b1724ab80917f \
--hash=sha256:b520aafc6cb148bac09ccf532f52cbd31d83acf4d3e5070d84efe3c019a1adbf \
--hash=sha256:b5bbb87fa0511bd313d9a2c90294c88db837667c2bda2ea3fa7a35b59fd93b1f \
--hash=sha256:b6d2a56fc2354bb6378f3634402eec788a8f3facf0b3e7d468db5f2b5a78d763 \
--hash=sha256:bbd705d5f3c3d1ff2d169e418bb789ff07ab3c70d567cc6ba6b72b04b9143481 \
--hash=sha256:bc5d8c3647b8ae28e4312f1492b8f29deebd31479cd3abaa989090fb1d66db83 \
--hash=sha256:c3c3525ea8658ee1192cdddf5faf99b07ebe1eeaa61bf32821126df6d1b8072b \
--hash=sha256:c9a9a39b7cac81dca79fca8c2a6479ef4c7b1aab95fad7544cc0e8fd943595a2 \
--hash=sha256:cd4c8d6aa91d3e35016847cbe8d73106e3d1c9a4e6578d38e2c346bfe8edb3ca \
--hash=sha256:cf62da7a4cdec9a4b2981fcbd5e08053edffccf20e845c0b6ec1e77eb7fab61d \
--hash=sha256:d67225f05f6ea27c8dc57f3fa6397c96d09c42af69d46629f71e82e66d33fa4f \
--hash=sha256:dfcd2b9f510411de615ccedd47462dae80e82fdc09fe9ab0f0f32f11cf57eeb5 \
--hash=sha256:e1f61355c821e870fb4c17cdb318669cfbcf245a291ce5053b41140870c3e5cc \
--hash=sha256:e249190b018d63c901678053b4a43e797ca78b93fb6d17633e3567d4b3ec6107 \
--hash=sha256:e2654d1278384cff75952682d17c718ecc1ad1d6227bb0068fd826ba47d426a5 \
--hash=sha256:e57d859b972c75ee44ea2ef4758f12821243e99de814030f69a3decb2aa86807 \
--hash=sha256:e5a27f348909235a106a3903fc8e70f573d89b41d723a500869c6569a391cff7 \
--hash=sha256:ead4f19d0257a756b21ac2e0e85a37a7245ddec36d3b6008d5bfe416525967dc \
--hash=sha256:f076b779aa3dc179aa3ed861be063a313ed4e48ae9f6a8370a9b1295d4502111 \
--hash=sha256:f1bb3a62395ffe835dbef3a1cbff48fbcce709c78bd1f52e896aee990928432b \
--hash=sha256:f2227a08b091bd41df5aadee0a5037673f691e2aa000e1968b1ea2342afc6880 \
--hash=sha256:f3754acbd7efc7f1b529039fcffc092a15e1cf045e31f22f6c9c5950c613ec4d \
--hash=sha256:fe48f50fb6348511a3268a893bfd4ab5f263f5ac220782449d03cd05964d1ae7 \
--hash=sha256:fff7d17d30b2cd45afd654b3fc117755c5d84506ed25fda386494e4e0a3416e1
python-dotenv==1.0.0 ; python_version >= "3.11" and python_version < "4.0" \ python-dotenv==1.0.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba \ --hash=sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba \
--hash=sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a --hash=sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a
@ -118,8 +30,5 @@ rsa==4.9 ; python_version >= "3.11" and python_version < "4" \
sniffio==1.3.0 ; python_version >= "3.11" and python_version < "4.0" \ sniffio==1.3.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \ --hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
--hash=sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384 --hash=sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384
telemongo==0.2.2 ; python_version >= "3.11" and python_version < "4.0" \ telethon==1.30.3 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:02dce5c4918a305f6b75766107382fd2af4a477d05ddb512e617eacac8130a48 \ --hash=sha256:313e40fa06667b19ced13b379d9988167a8319bc0eb90bf39347cff46919a351
--hash=sha256:ef4a383b8c25c62a3b3f640fe4cd9a4d3c1fa0ab0b22d51e047f9f6c924c6795
telethon==1.31.1 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:299567c307818e0ecd1ecb208c2f4269be4ea84fdea49b5061c36362dc92abbd

0
tests/__init__.py Executable file → Normal file
View File