diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2a44415 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +**/__pycache__/ +venv/ +guide/site diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..be0236f --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,61 @@ +name: Build docker image +on: + workflow_call: {} + +env: + DOCKERHUB_REPO: tgpy/tgpy + +jobs: + Build-docker-image: + name: Build docker image + concurrency: release-docker + if: github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Setup QEMU + uses: docker/setup-qemu-action@v2 + - name: Setup Buildx + uses: docker/setup-buildx-action@v2 + - name: Setup Buildx caching + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Set release flag + if: github.ref == 'refs/heads/master' + run: sed -i "s/\(IS_DEV_BUILD *= *\).*/\1False/" tgpy/version.py + + - name: Set branch tag + if: github.ref != 'refs/heads/master' + run: | + BRANCH_TAG=$DOCKERHUB_REPO:$(git rev-parse --abbrev-ref HEAD) + echo "BRANCH_TAG=$BRANCH_TAG" >> $GITHUB_ENV + echo "IMAGE_TAGS=$IMAGE_TAGS -t $BRANCH_TAG" >> $GITHUB_ENV + - name: Set latest tag + if: github.ref == 'refs/heads/master' + run: | + BRANCH_TAG=$DOCKERHUB_REPO:latest + echo "BRANCH_TAG=$BRANCH_TAG" >> $GITHUB_ENV + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USER }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + - name: Build and push image + run: | + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + -t $DOCKERHUB_REPO:$(git rev-parse --short HEAD) \ + -t $BRANCH_TAG \ + --cache-from "type=local,src=/tmp/.buildx-cache" \ + --cache-to "type=local,dest=/tmp/.buildx-cache-new,mode=max" \ + --push . + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 32d7b0a..13eec06 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,12 +2,18 @@ name: Lint & release project on: push: paths: + - 'pyproject.toml' + - 'poetry.lock' - 'tgpy/**' - '.github/workflows/main.yml' + - '.github/workflows/docker.yml' pull_request: paths: + - 'pyproject.toml' + - 'poetry.lock' - 'tgpy/**' - '.github/workflows/main.yml' + - '.github/workflows/docker.yml' jobs: Lint: @@ -94,3 +100,17 @@ jobs: export REPOSITORY_USERNAME="__token__" export REPOSITORY_PASSWORD="${PYPI_TOKEN}" python -m semantic_release publish -D commit_author="github-actions " + + Build-dev-docker: + name: Build dev docker image + if: github.event_name == 'push' && github.ref != 'refs/heads/master' + uses: ./.github/workflows/docker.yml + secrets: inherit + + Build-release-docker: + name: Build release docker image + needs: Release + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + uses: ./.github/workflows/docker.yml + secrets: inherit + diff --git a/.gitignore b/.gitignore index 0b698da..1c4448f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ dist/ data/ config.py config.yaml + +guide/site diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..692ac57 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# syntax=docker/dockerfile:1.3 +FROM python:3.10-alpine as base +WORKDIR /app + +FROM base as builder +RUN apk add --no-cache gcc musl-dev libffi-dev git rust cargo + +ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \ + POETRY_VERSION=1.1.13 + +RUN --mount=type=cache,target=/root/.cache/pip \ + pip install poetry==$POETRY_VERSION +RUN python -m venv /venv + +COPY pyproject.toml poetry.lock ./ +RUN --mount=type=cache,target=/root/.cache/pip \ + poetry export -f requirements.txt | /venv/bin/pip install -r /dev/stdin + +COPY . . +RUN git status +RUN sed -i "s/\(COMMIT_HASH *= *\).*/\1'$(git rev-parse HEAD)'/" tgpy/version.py + +FROM base as runner +COPY --from=builder /venv /venv +COPY --from=builder /app/tgpy tgpy + +ENV TGPY_DATA=/data +VOLUME /data + +ENTRYPOINT ["/venv/bin/python", "-m", "tgpy"] diff --git a/README.md b/README.md index a03a3a2..fd87e97 100644 --- a/README.md +++ b/README.md @@ -1,88 +1,51 @@ # TGPy -### Run Python code right in your Telegram messages +_Run Python code right in your Telegram messages_ -TGPy is a tool for evaluating expressions and Telegram API scripts, built on top of [Telethon](https://github.com/LonamiWebs/Telethon). +![PyPI](https://img.shields.io/pypi/v/tgpy) +![Docker Image Version (latest semver)](https://img.shields.io/docker/v/tgpy/tgpy?label=docker&sort=semver) +![Open issues](https://img.shields.io/github/issues-raw/tm-a-t/TGPy) +![Docs](https://img.shields.io/website?label=docs&url=https%3A%2F%2Ftgpy.tmat.me) + -- Do Python calculations in dialogs -- Interact with your messages and chats -- Automate sending messages and more - -**Learn basics:** https://tgpy.tmat.me/basics/ +TGPy is a tool for running Python expressions and Telegram API scripts, built on top of [Telethon](https://github.com/LonamiWebs/Telethon). +- Do Python calculations in dialogs +- Use code to send messages, save files, analyze chats and more +- Set up functions to automate your Telegram actions ## Getting started -Python 3.9+ required. Host TGPy and connect it to your Telegram account: +Python 3.9+ required. Install TGPy and connect it to your Telegram account: ```shell > pip install tgpy > tgpy ``` -You’re ready now. Send Python code to any chat to run it. Change your message to change the result. - - - -## Examples - -Send any of these examples to any chat to evaluate: - -🐍 Do Python calculations - -```python -for i in range(5): - print(i) -``` - -⏳ Delete the current message in 5 seconds +You’re ready now. Send Python code to any chat, and it will run. Change your message to change the result. -```python -import asyncio +Details on installation: http://tgpy.tmat.me/installation/ -await asyncio.sleep(5) -await msg.delete() -``` +https://user-images.githubusercontent.com/38432588/181266550-c4640ff1-71f2-4868-ab83-6ea3690c01b6.mp4 -↪️ Forward the message you replied to to another chat +## New TGPy docs -```python -orig.forward_to('Chat title') -``` +**[Basics Guide:](http://tgpy.tmat.me/basics/code/)** All you need to know to start using TGPy. -🖼 Send all chat profile photos to the same chat - -```python -photos = await client.get_profile_photos(msg.chat) -msg.reply(file=photos) -``` +**[Extensibility Guide:](http://tgpy.tmat.me/extensibility/context/)** Special features for advanced usage. -🔖 Define a function which forwards messages to Saved Messages with reply +**[Reference:](http://tgpy.tmat.me/reference/builtins/)** List of TGPy objects and settings. -```python -def save(): - message = ctx.msg - original = await message.get_reply_message() - await original.forward_to('me') - return 'Saved!' -``` +## Inspiration -🗑 Define a function which deletes messages with reply - -```python -async def delete(): - message = ctx.msg - original = await message.get_reply_message() - await original.delete() - await message.delete() -``` - -## [TGPy Guide](https://tgpy.tmat.me/) +TGPy is inspired by [FTG](https://gitlab.com/friendly-telegram/friendly-telegram) and similar userbots. However, the key concept is different: TGPy is totally based on usage of code in Telegram rather than plugging extra modules. This leads both to convenience of single-use scripts and reusage flexibility. ## Credits -- Thanks to [penn5](https://github.com/penn5) for [meval](https://github.com/penn5/meval) -- Thanks to [Lonami](https://github.com/LonamiWebs) for [Telethon](https://github.com/LonamiWebs/Telethon) +TGPy is built on [Telethon](https://github.com/LonamiWebs/Telethon), which allows to integrate Telegram features in Python code. + +Basic code transformation (such as auto-return of values) is based on [meval](https://github.com/penn5/meval). ## License diff --git a/guide/docs/api.md b/guide/docs/api.md deleted file mode 100644 index 39545c2..0000000 --- a/guide/docs/api.md +++ /dev/null @@ -1,14 +0,0 @@ -# API - -## Code transformers - -`tgpy.add_code_transformer` - -`code_transformers` - -TODO - -## Available variables - -- `tgpy.variables` - dictionary of variables -- `tgpy.constants` - dictionary of [TGPy constants](builtins.md#tgpy-objects) diff --git a/guide/docs/assets/example.gif b/guide/docs/assets/example.gif deleted file mode 100644 index f4a5c4d..0000000 Binary files a/guide/docs/assets/example.gif and /dev/null differ diff --git a/guide/docs/assets/example.mp4 b/guide/docs/assets/example.mp4 new file mode 100644 index 0000000..9054a0d Binary files /dev/null and b/guide/docs/assets/example.mp4 differ diff --git a/guide/docs/basics.md b/guide/docs/basics.md deleted file mode 100644 index 75fa00b..0000000 --- a/guide/docs/basics.md +++ /dev/null @@ -1,118 +0,0 @@ -# Basics - -Just send any Python code to any chat - and it'll be evaluated. -```python -2 + 2 - -TGPy> 4 -``` - -```python -s = 0 -for i in range(100): - s += i -s - -TGPy> 4950 -``` - -Change your message to change the result. - -If you want to keep the original message, send `cancel` after your message. -[Learn more about code detection](code_detection.md) - -!!! tip - - You can experiment with TGPy in Saved Messages. Nobody else will see your messages. - -## Do anything - -All Python possibilities are available, including module imports and function definitions. - -TGPy uses [Telethon library](https://github.com/LonamiWebs/Telethon/) to interact with Telegram. You can use -messages, chats and users in your code. Check out [Telethon documentation](https://docs.telethon.dev/en/latest/). - -For example, to programmatically reply to the current message: - -```python -msg.reply('Hello!') -``` - -You can use variables such as `msg` for current message object and `client` for Telethon client object. -[Learn more about TGPy builtins](builtins.md) - -Variables defined by you are saved automatically, so you can use them in following messages. - -```python -def f(): - return 'TGPy is awesome' - -TGPy> None -``` - -```python -f() - -TGPy> 'TGPy is awesome' -``` - -## Code result - -You can explicitly return values in messages: -```python -x = 10 * 10 -return x - -TGPy> 100 -``` - -Otherwise, all calculated values will be automatically returned: -```python -10 * 10 -x = 2 -x + 2 - -TGPy> [100, 4] -``` - -!!! tip - - Use `_` variable for the previous result: - - ```python - 2 + 2 - - TGPy> 4 - ``` - - ```python - _ * 100 - - TGPy> 400 - ``` - -You can also print values. `print` function is redefined, so the output is added to messages. - -```python -print('Hello World!') -return 'ok' - -TGPy> 'ok' - -Hello World! -``` - -Exceptions are also shown right in messages. - -Long messages might be truncated because of Telegram limit of 4096 symbols per message. - -## Asyncio - -You can use `async`/`await` in your code. Also, all returned values are automatically awaited (if needed). - -```python -import asyncio -asyncio.sleep(30) - -TGPy> Running... -``` \ No newline at end of file diff --git a/guide/docs/basics/asyncio.md b/guide/docs/basics/asyncio.md new file mode 100644 index 0000000..722fcca --- /dev/null +++ b/guide/docs/basics/asyncio.md @@ -0,0 +1,49 @@ +# Asyncio + +## Not familiar with asyncio? + +To use TGPy features for Telegram (such as sending messages, getting chats, and so on), you should understand Python +asynchronous functions. + +Modern Python versions support asynchronous functions. Basically, asynchronous function is a function that runs until +completion while not blocking other code parts. + +Let’s say you need to use such function in your TGPy message. To do that, you should place the `await` keyword before: +otherwise, the function won’t run. + +```python +result = await some_function() +``` + +This way the code snippet will be suspended until `some_function()` ends, but **TGPy itself won’t stop**. For instance, +the code from another message may run at the same time. + +If you declare some function which uses asynchronous functions, your function must be asynchronous too. For that +use `async def` instead of `def`. + +!!! note + + If you wish to learn more about Python `async`/`await`, you may: + + - Read the [explanation of how asyncio works](https://fastapi.tiangolo.com/async/#technical-details) by Tiangolo written for FastAPI + - Or google anything else about it :) + +## Asyncio in TGPy + +TGPy allows using top-level `async`/`await` in the code: + +```python +import asyncio + +await asyncio.sleep(10) +print('Done!') +``` + +In addition, TGPy automatically awaits all returned values (if needed). Therefore, you may omit top-level `await`: + +```python hl_lines="3" +import asyncio + +asyncio.sleep(10) +print('Done!') +``` diff --git a/guide/docs/basics/code.md b/guide/docs/basics/code.md new file mode 100644 index 0000000..85b1092 --- /dev/null +++ b/guide/docs/basics/code.md @@ -0,0 +1,94 @@ +# Running code + +## How to use TGPy + +Open any chat, type some Python code and send it. It’s that simple. + +
+```python +2 + 2 +``` +
+``` +TGPy> 4 +``` +
+ +
+```python +s = 0 +for i in range(100): + s += i +s +``` +
+``` +TGPy> 4950 +``` +
+ +If you edit your message, TGPy will recalculate the result. + +If TGPy mistakes your plain-text message for code, reply `cancel` to fix that. If you haven't sent anything since, you can just send `cancel` without replying. + +!!! tip + + You can experiment with TGPy in [Saved Messages](tg://resolve?domain=TelegramTips&post=242). Nobody else will see that ;) + +## Power of Python + +All Python features are available, including **module imports** and **function definitions**. Moreover, you can use +most of Telegram features, such as sending messages. You’ll learn more about them later in the guide. + +## Code result + +You can explicitly return values in messages: + +
+```python +x = 2 * 2 +return x +``` +
+``` +TGPy> 4 +``` +
+ +Otherwise, all computed values will be returned automatically: + +
+```python +x = 10 +x * 7 +x + 20 +``` +
+``` +TGPy> [70, 30] +``` +
+ +You can also print values. The `print` function is redefined, so that the output is added to the message. + +
+```python +print('Hello World!') +``` +
+``` +TGPy> Hello World! +``` +
+ +Exceptions are also shown right in the message. + +!!! note + + Long messages might be truncated because of Telegram limit of 4096 symbols per message. + +## Other tips + +- TGPy saves the defined variables, so you use them in further messages +- The `_` variable contains the result of the previous message +- Edit the message to rerun it diff --git a/guide/docs/basics/examples.md b/guide/docs/basics/examples.md new file mode 100644 index 0000000..68b3cf6 --- /dev/null +++ b/guide/docs/basics/examples.md @@ -0,0 +1,161 @@ +# Examples + +## Sending messages + +### Auto-laugh + +```python +text = 'ha' * 20 +await msg.respond(text) +``` + +!!! Tip + + If you want to delete the code snippet message in advance, start your message with + `#!python await msg.delete()` + +### Countdown + +Send numbers from 10 to 1, at a one second interval: + +```python +import asyncio + +for i in range(10): + await msg.respond(str(10 - i)) + await asyncio.sleep(1) +``` + +### Message typing animation + +Send a message and edit it several times, adding letters one by one: + +```python +import asyncio + +text = 'Hello World' +message = await msg.respond('...') +for i in range(len(text)): + await message.edit(text[:i+1] + '|') + await asyncio.sleep(0.5) +``` + +### Send a copy + +Send a copy of the message to another chat: + +```python +message = orig +await client.send_message('Example Chat', message) +return 'Sent the message' +``` + +## Other Telegram features + +### Download a picture or file + +Download a picture from a message to the TGPy directory. Reply to the message with the following: + +```python +await orig.download_media('example.jpg') +``` + +### Send a picture or file + +```python +await msg.respond(file='example.jpg') # You can also pass URL here +return +``` + +### Delete recent messages from the chat + +Delete all messages starting with the message you‘re replying to and ending with the current message: + +=== "From all users" + + ```python + messages = await client.get_messages( + msg.chat, + min_id=orig.id - 1, + max_id=msg.id + ) + await client.delete_messages(msg.chat, messages) + ``` + +=== "From a specified user" + + ```python hl_lines="5" + messages = await client.get_messages( + msg.chat, + min_id=orig.id - 1, + max_id=msg.id, + from_user='John Doe' + ) + await client.delete_messages(msg.chat, messages) + ``` + +!!! note + + TGPy can only delete messages if you have rights for that, for instance if you’re a group admin. + +### List your drafts + +Print all chats where you have any drafts: + +```python +async for draft in client.iter_drafts(): + title = getattr(draft.entity, 'title', None) # if this is a group or a channel + name = getattr(draft.entity, 'first_name', None) # if this is a user + print(name or title) +``` + +### Kick a user from the chat + +This works only if you’re a chat admin. Ban a user and remove them from the blacklist, so that they can join the chat +again: + +```python +await client.kick_participant(msg.chat, 'John Doe') +return 'Bye!' +``` + +Use `#!python 'me'` instead of the name to leave. + +## Integrations + +### Run shell commands on the host + +```python +import subprocess + +command = 'echo Hello World' +process = subprocess.run(command, shell=True, capture_output=True) +print(process.stdout.decode()) +print(process.stderr.decode()) +``` + +### Send a plot rendered by matplotlib + +[Example taken from matplotlib docs](https://matplotlib.org/stable/gallery/lines_bars_and_markers/simple_plot.html) + +```python +import matplotlib.pyplot as plt +import numpy as np + +# Data for plotting +t = np.arange(0.0, 2.0, 0.01) +s = 1 + np.sin(2 * np.pi * t) + +fig, ax = plt.subplots() +ax.plot(t, s) + +ax.set(xlabel='time (s)', ylabel='voltage (mV)', + title='About as simple as it gets, folks') +ax.grid() + +fig.savefig('test.png') + +# Send the plot +await msg.reply(file='test.png') +return +``` diff --git a/guide/docs/basics/messages.md b/guide/docs/basics/messages.md new file mode 100644 index 0000000..b42f869 --- /dev/null +++ b/guide/docs/basics/messages.md @@ -0,0 +1,149 @@ +# Messages + +## Telegram objects + +TGPy is based on **Telethon**, a Telegram API client library. You can +use [Telethon objects and methods](https://docs.telethon.dev/en/stable/quick-references/objects-reference.html) +for messages, users and chats. This page explains how to perform basic message actions, such as sending and editing. + +??? tldr "Already familiar with Telethon?" + + Already familiar with Telethon? + + All you need to know is that in TGPy you can use the following objects: + + - `client` for the Telethon client + - `msg` for the current message + - `orig` for the message you’re replying to + + See the [Builtin reference](/reference/builtins/#telethon-objects) for details. + + Now you can skip the rest of the page and go to the [examples](/basics/examples) :) + +TGPy provides some global Telegram objects. The `client` object is useful for general functionality, such as sending +messages, listing chats and so on. The `msg` object always refers to the current message. + +## Sending a message + +The simplest Telegram action is sending a message. There is a method for that: + +```python +await client.send_message(chat, text) +``` + +`chat` can be either a chat name, a username, or an ID. + +For example, to send a «Hello World» to the current chat you can use: + +```python +await client.send_message(msg.chat_id, "Hello World") +``` + +Or use a shortcut for this exact action: + +```python +await msg.respond("Hello World") +``` + +You can also use `msg.reply` instead of `msg.respond` to send the message as a reply, rather than just send it to the +chat. + +!!! note + + The code above returns the new message. For now, TGPy shows the full info for the returned message, which may be + very long to display. You can add a `#!python return` to suppress it: + + ```python + await msg.respond("Hello World") + return + ``` + +## Reusing messages + +The new message object can be used later: + +```python +hello = await msg.respond("Hello") # (1) +await hello.edit("Hiiiiiiiiii") +``` + +1. `hello` is now the new message object + +You can use message properties such as `message.text`, `message.chat`, `message.sender` and others. + +There are also message methods for common actions, such as `message.edit()`, `message.delete()`, `message.forward_to()` +, `message.pin()` and so on. + +Well, have fun :) + +!!! note + + Check out Telethon reference for details: + + - [Message attributes](https://docs.telethon.dev/en/stable/quick-references/objects-reference.html#message) + + - [Client attributes](https://docs.telethon.dev/en/stable/quick-references/client-reference.html) + +??? example "Example: show full message data" + + ```python + return msg + + TGPy> Message( + id=77305, + peer_id=PeerChannel( + channel_id=1544471292 + ), + date=datetime.datetime(2021, 10, 31, 11, 20, 28, tzinfo=datetime.timezone.utc), + message='return msg', + out=True, + mentioned=False, + media_unread=False, + silent=False, + post=False, + from_scheduled=False, + legacy=False, + edit_hide=False, + pinned=False, + from_id=PeerUser( + user_id=254210206 + ), + fwd_from=None, + via_bot_id=None, + reply_to=None, + media=None, + reply_markup=None, + entities=[ + ], + views=None, + forwards=None, + replies=MessageReplies( + replies=0, + replies_pts=87625, + comments=False, + recent_repliers=[ + ], + channel_id=None, + max_id=None, + read_max_id=None + ), + edit_date=None, + post_author=None, + grouped_id=None, + restriction_reason=[ + ], + ttl_period=None + ) + ``` + +## Getting the original message + +The `orig` variable is a shortcut for the message you are replying to. + +For example, you can reply with this to get the uppercased text of the message: + +```python +orig.text.upper() +``` + +When your message is not a reply, the `orig` object is None. diff --git a/guide/docs/builtins.md b/guide/docs/builtins.md deleted file mode 100644 index dbf4b46..0000000 --- a/guide/docs/builtins.md +++ /dev/null @@ -1,78 +0,0 @@ -# TGPy builtins - -## Control functions - -- `ping()` - use it to check if TGPy is running -- `restart()` - restart TGPy -- `update()` - download the latest version from GitHub and restart TGPy - -## Telethon objects - - -- `msg` - current [message](https://docs.telethon.dev/en/latest/quick-references/objects-reference.html#message) -- `orig` - original message, if your message is a reply -- `client` - Telethon [client](https://docs.telethon.dev/en/latest/quick-references/client-reference.html) - -??? example "Example: show current message data" - - ```python - return msg - - TGPy> Message( - id=77305, - peer_id=PeerChannel( - channel_id=1544471292 - ), - date=datetime.datetime(2021, 10, 31, 11, 20, 28, tzinfo=datetime.timezone.utc), - message='return msg', - out=True, - mentioned=False, - media_unread=False, - silent=False, - post=False, - from_scheduled=False, - legacy=False, - edit_hide=False, - pinned=False, - from_id=PeerUser( - user_id=254210206 - ), - fwd_from=None, - via_bot_id=None, - reply_to=None, - media=None, - reply_markup=None, - entities=[ - ], - views=None, - forwards=None, - replies=MessageReplies( - replies=0, - replies_pts=87625, - comments=False, - recent_repliers=[ - ], - channel_id=None, - max_id=None, - read_max_id=None - ), - edit_date=None, - post_author=None, - grouped_id=None, - restriction_reason=[ - ], - ttl_period=None - ) - ``` - -TGPy fetches `orig` message only if your code uses `orig` variable (because it requires an additional request -to Telegram API). - -## TGPy objects - -- [`modules`](modules.md) -- [`ctx`](context.md) - -## TGPy API object - -- [`tgpy`](api.md) diff --git a/guide/docs/code_detection.md b/guide/docs/code_detection.md deleted file mode 100644 index 5132d0a..0000000 --- a/guide/docs/code_detection.md +++ /dev/null @@ -1,40 +0,0 @@ -# Code detection - -## Why use auto-detection? - -TGPy is designed for running code snippets sequentially and frequently. Bot-like commands -(such as `/run print('Hello World')`) would break the workflow. That's why TGPy automatically detects your messages with syntactically correct Python code and evaluates it. - -It turns out that regular text messages aren't often identified as code. TGPy ignores too simple expressions. - -Although, optional disabling of auto-detection might be added in the future. - -## What is ignored? - -TL;DR: Some simple expressions, which could be email addresses, URLs or several comma- or hyphen-separated words -(as described in [issue 4](https://github.com/tm-a-t/TGPy/issues/4)) - -??? note "More details" - In this section, an **unknown** variable is one not present in `locals` — that is, which were not saved in previous messages and which are not built in TGPy (as `ctx`, `orig`, `msg` and `print` are). - Unknown variables' attributes are also considered unknown - - **Ignored** expressions are expressions in the list below: - - * Constants like `1` or `"abcd"` and unknown variables - * Binary operations on unknown variables (recursively, i.e., `a - b -c` is also ignored in case `a`, `b`, `c` are unknown) - * Unary operations on constants or unknown variables - * Tuples of ignored expressions - * Multiple ignored expressions (i.e. separated by `;` or newline)**** - - -## Cancel evaluation - -You can change the message with evaluated code to the original with the `cancel` command. - -`cancel` edits back your latest TGPy message in current chat (if it's in 10 latest messages). - -`cancel` can be also used in reply to a specific TGPy message. - -## Prevent evaluation - -If you write `//` in the beginning in your message, the code won't be evaluated. The `//` prefix will be deleted. diff --git a/guide/docs/context.md b/guide/docs/context.md deleted file mode 100644 index 31ddf5f..0000000 --- a/guide/docs/context.md +++ /dev/null @@ -1,34 +0,0 @@ -# Context - -[`msg`](builtins.md#telethon-objects) and [`orig`](builtins.md#telethon-objects) objects are always related -to the message where they were used. Although, sometimes you need to define functions that use current message and reuse -them later. For example, you might want to delete the messages you reply to with `delete()` function. - -Instead of passing `msg` and `orig` as arguments, you can use `ctx` variable. - -`ctx` contains current context objects: - -- `ctx.msg` - the latest TGPy message - -- `ctx.orig` - the message which the latest TGPy message was reply to - - !!! caution "Not implemented yet" - - `ctx.orig` is not implemented yet. Instead, use: - ```python - await ctx.msg.get_reply_message() - ``` - - -Thus, you can define `delete()` function as following: - -```python -async def delete(): - original_message = await ctx.msg.get_reply_message() - await original_message.delete() - await ctx.msg.delete() - -TGPy> None -``` - -Now you can send `delete()` in reply to some message. The original message and your '`delete()`' message will be deleted. diff --git a/guide/docs/explanation/how.md b/guide/docs/explanation/how.md new file mode 100644 index 0000000..81ed71f --- /dev/null +++ b/guide/docs/explanation/how.md @@ -0,0 +1,5 @@ +# How it works + +## So... how does it work?? + +Just magic. diff --git a/guide/docs/extensibility/code_transformers.md b/guide/docs/extensibility/code_transformers.md new file mode 100644 index 0000000..342c516 --- /dev/null +++ b/guide/docs/extensibility/code_transformers.md @@ -0,0 +1,3 @@ +# Code transformers + +_To be documented_ diff --git a/guide/docs/extensibility/context.md b/guide/docs/extensibility/context.md new file mode 100644 index 0000000..a2efe1d --- /dev/null +++ b/guide/docs/extensibility/context.md @@ -0,0 +1,30 @@ +# Context data + +The [`msg`](../reference/builtins.md#telethon-objects) object always refers to the message where it was used. For +instance, if you use `msg` in a function, it will refer to the message that defined this function, even if it is +called from another message later. However, sometimes you need to define reusable functions that use the current +message. + +Let’s say we want to define a `cat()` function which sends a cat picture to the chat it was used at. Somehow the +function must use the current chat. We could pass `msg` as an argument, but it wouldn’t be handy enough to reuse the +function. Instead, we will use `ctx.msg` variable. + +`ctx.msg` always contains your latest TGPy message. With it, we can define `cat()` function as follows: + +```python +async def cat(): + cat_url = 'https://cataas.com/cat' # URL for a cat image + await ctx.msg.respond(file=cat_url) +``` + +## Getting the original message + +To get the message which `ctx.msg` replies to, use: + +```python +original = await ctx.msg.get_reply_message() +``` + +!!! info + + The shortcut `ctx.orig` is planned but not implemented yet. diff --git a/guide/docs/extensibility/module_examples.md b/guide/docs/extensibility/module_examples.md new file mode 100644 index 0000000..125f315 --- /dev/null +++ b/guide/docs/extensibility/module_examples.md @@ -0,0 +1,68 @@ +# Module examples + +## Shortcut for deleting messages + +Send `d()` in reply to any message to delete it. + +```python +async def d(): + original = await ctx.msg.get_reply_message() + await msg.delete() + await original.delete() +``` + +## Shortcut for saving messages + +Send `save()` in reply to any message to forward to Saved Messages. + +```python +async def save(): + original = await ctx.msg.get_reply_message() + await msg.delete() + await original.forward_to('me') +``` + +## Stats for chat member IDs + +Sort chat members by their IDs. In average, the lower the ID of a user is, the earlier they registered in Telegram. + +```python +def fullname(user): + return ((user.first_name or '') + ' ' + (user.last_name or '')).strip() or 'Deleted account' + +def idstat(users): + users.sort(key=lambda x: x.id) + return '\n'.join([f'{x.id:>10} {fullname(x)}' for x in users]) + +async def idstatgrp(): + return idstat(await client.get_participants(ctx.msg.chat)) +``` + +## Send the source of all your modules + +```python +from html import escape + +for name in modules: + code = escape(modules[name].code) + await ctx.msg.respond(f'
Module "{name}":\n\n{code}
') +``` + +## Process a message with sed + +Use in reply to a message. + +```python +import subprocess + +async def sed(s): + orig = await ctx.msg.get_reply_message() + text = subprocess.run(["sed", s], input=orig.text, capture_output=True, check=True, encoding="utf-8").stdout + if text == orig.text: + return "(no changes)" + if orig.from_id == ctx.msg.from_id: + await orig.edit(text) + await ctx.msg.delete() + else: + return text +``` diff --git a/guide/docs/extensibility/modules.md b/guide/docs/extensibility/modules.md new file mode 100644 index 0000000..9ef49bc --- /dev/null +++ b/guide/docs/extensibility/modules.md @@ -0,0 +1,67 @@ +# Using modules + +All variables from your messages will be lost when TGPy stops. Yet, you can use modules to define some variables every +time TGPy starts. + +Modules are code snippets executed at every startup. For example, modules can help you define some shortcut +functions, classes, or constants for future use. + +## Add a module + +After running a code snippet with TGPy, you can add it to modules by replying with the `modules.add` function: + +```python +modules.add(module_name) +``` + +You can add a module from a code string instead using `#!python modules.add(module_name, code)`. + +!!! Info + + If a module with this name already exists, its code will be replaced. + +!!! example + + 1. Define a square function: + + ```python + def square(x): + return x * x + + TGPy> None + ``` + + 2. Save the definition to modules: + + ```python + # in reply to the previous message + modules.add('square') + + TGPy> Added module 'square'. + The module will be executed every time TGPy starts. + ``` + +## Remove a module + +Remove a module by name: + +```python +modules.remove(module_name) +``` + +## Manage your modules + +Use the string value of `modules` to list all of your modules: + +```python +modules +``` + +Modules are stored as separate Python files in `data/modules` directory. You can safely edit them manually. + +Modules run each time TGPy starts. By default, they run in the order they were added. + +## Module settings + +Module metadata is stored as a comment in the module file. You can edit it +manually. [Module metadata reference](/reference/module_metadata) diff --git a/guide/docs/index.md b/guide/docs/index.md index 8a0e49b..64eb5a7 100644 --- a/guide/docs/index.md +++ b/guide/docs/index.md @@ -1,88 +1,28 @@ -# TGPy +# Introduction -### Run Python code right in your Telegram messages +## What is TGPy? -Made with Telethon library, TGPy is a tool for evaluating expressions and Telegram API scripts. +TGPy is a tool for running Python code snippets right in your Telegram +messages. [Check it out at GitHub](https://github.com/tm-a-t/TGPy/). + -- Do Python calculations in dialogs -- Interact with your messages and chats -- Automate sending messages and more +## How to use the docs? -## Installation +These docs consist of: -Python 3.9+ is required. +**[Basics Guide:](/basics/code/)** All you need to know to start using TGPy. -```shell -> pip install tgpy -> tgpy -``` +**[Extensibility Guide:](/extensibility/context/)** Special features for advanced usage. -## Getting started +**[Reference:](/reference/builtins/)** List of TGPy objects and settings. -Just send Python code to any chat, and it will be executed. Change your message to change the result. +Learning for the first time? Read the pages one by one by clicking the «Next» button in the bottom. -[📒 TGPy Basics](https://tgpy.tmat.me/basics/) +You can also skip pages using the menu on the left. -![Example](assets/example.gif) -## Examples +## Haven't installed TGPy yet? -Send any of these examples to any chat to evaluate: - -🐍 Do Python calculations - -```python -for i in range(5): - print(i) -``` - -⏳ Delete the current message in 5 seconds - -```python -import asyncio - -await asyncio.sleep(5) -await msg.delete() -``` - -↪️ Forward the message you replied to to another chat - -```python -orig.forward_to('Chat title') -``` - -🖼 Send all chat profile photos to the same chat - -```python -photos = await client.get_profile_photos(msg.chat) -msg.reply(file=photos) -``` - -🔖 Define a function which forwards messages to Saved Messages with reply - -```python -def save(): - message = ctx.msg - original = await message.get_reply_message() - await original.forward_to('me') - return 'Saved!' -``` - -🗑 Define a function which deletes messages with reply - -```python -async def delete(): - message = ctx.msg - original = await message.get_reply_message() - await original.delete() - await message.delete() -``` - -## Credits - -- Thanks to [penn5](https://github.com/penn5) for [meval](https://github.com/penn5/meval) -- Thanks to [Lonami](https://github.com/LonamiWebs) for [Telethon](https://github.com/LonamiWebs/Telethon) - -## License - -This project is licensed under the terms of the MIT license. +Start with [Installation](/installation/) and move on to the Basics Guide. diff --git a/guide/docs/installation.md b/guide/docs/installation.md index af168d4..4480c37 100644 --- a/guide/docs/installation.md +++ b/guide/docs/installation.md @@ -1,24 +1,52 @@ # Installation -Python 3.9+ is required. +You can install and run TGPy on your computer, but you might have to use a remote server to have TGPy available 24/7. + +!!! warning + + **Make sure you run TGPy on a trusted machine** — that is, no one except you can read TGPy files on the computer. + Anyone with access to TGPy files can steal your Telegram account. + + And the other way round: anyone with access to your Telegram account has access to the machine TGPy is running on. + +Make sure you have [Python 3.9 or above](https://www.python.org/) installed. + +Install TGPy: ```shell -> pip install tgpy -> tgpy +pip install tgpy ``` -Send `ping()` to any chat to check if TGPy is running. +And start it: + +=== "With a simple command" + + ```shell + tgpy + ``` + +=== "If the simple command doesn’t work" + + ```shell + python -m tgpy + ``` + +Follow the instructions to connect your Telegram account for the first time. + +When it’s ready, try to send `ping()` to any chat to check if TGPy is running. ## Updating -Update to the newest version with: +Update to the latest version: -```shell -> pip install -U tgpy -``` +=== "From Telegram message" -Or right from Telegram: + ```python + update() + ``` -```python -update() -``` +=== "From shell" + + ```shell + pip install -U tgpy + ``` diff --git a/guide/docs/modules.md b/guide/docs/modules.md index a4fdb67..e8427b2 100644 --- a/guide/docs/modules.md +++ b/guide/docs/modules.md @@ -1,57 +1,3 @@ -# Modules +# TGPy modules -Modules are executed when TGPy starts. For example, with modules you can define shortcut functions for future using. - -## Add modules - -Add one of previous TGPy messages to modules by replying with `modules.add` function. - -```python -modules.add(module_name) -``` - -You can also use `#!python modules.add(module_name, code)` to add any other code. - -If the module with this name already exists, its code will be replaced. - -!!! example - - 1. Define a square function: - - ```python - def square(x): - return x * x - - TGPy> None - ``` - - 2. Save the definition to modules: - - ```python - # in reply to the previous message - modules.add('square') - - TGPy> Added module 'square'. - The module will be executed every time TGPy starts. - ``` - -## Remove modules - -Remove a module by name: -```python -modules.remove(module_name) -``` - -## Manage your modules - -Use the string value of `modules` to list all of your modules: - -```python -modules -``` - -Modules are executed when TGPy starts. By default, modules are executed in order of addition. - -Modules are stored as separate Python files in `data/modules` directory. You can safely edit them manually. - -TODO: metadata explanation \ No newline at end of file +[Learn about modules](/extensibility/modules/) \ No newline at end of file diff --git a/guide/docs/reference/builtins.md b/guide/docs/reference/builtins.md new file mode 100644 index 0000000..c5565b8 --- /dev/null +++ b/guide/docs/reference/builtins.md @@ -0,0 +1,49 @@ +# TGPy builtins + +## Control functions + +| Function | Description | +| ----------------- | ------------------------------------------------------------------------------------- | +| `#!python ping()` | Return basic info about your TGPy instance. Use `ping()` to check if TGPy is running. | +| `#!python restart()` | Restart TGPy. | +| `#!python update()` | Download the latest version of TGPy, update, and restart the instance. | + +## Telethon objects + +| Object | Description | +|--------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `#!python client` | The Telethon client. [See Telethon Client reference](https://docs.telethon.dev/en/latest/quick-references/client-reference.html) | +| `#!python msg` | The current message. [See Telethon Message reference](https://docs.telethon.dev/en/latest/quick-references/objects-reference.html#message) | +| `#!python orig` | Original message: the message you replied to. [See Telethon Message reference](https://docs.telethon.dev/en/latest/quick-references/objects-reference.html#message) | + +!!! note + + TGPy fetches `orig` message only if your code uses the `orig` variable. That’s because it requires an additional + request to Telegram API. + +## Modules + +[Read on modules](/extensibility/modules/) + +| Object | Description | +| --- | --- | +| `#!python modules` | Object for module management. `str(modules)` lists saved modules. | +| `#!python modules.add(name: str, code: str)` | Add the given code as a module. If `code` isn’t specified, the code from the `orig` message will be added. | +| `#!python modules.remove(name: str)` | Remove the module named `name`. | + +## Context + +[Read on the `ctx` object](../extensibility/context.md) + +| Object | Description | +| --- | --- | +| `#!python ctx.msg` | The message containing the code TGPy is evaluating at the moment | + +## TGPy API object + +| Object | Description | +| --- | --- | +| `#!python tgpy.add_code_transformer(name: str, transformer: Callable[[str], str])` | _To be documented_ | +| `#!python tgpy.code_transformers` | _To be documented_ | +| `#!python tgpy.variables` | Dictionary of saved variables | +| `#!python tgpy.constants` | Dictionary of constants
(`tgpy`, `ctx`, `client`) | diff --git a/guide/docs/reference/code_detection.md b/guide/docs/reference/code_detection.md new file mode 100644 index 0000000..0c3980e --- /dev/null +++ b/guide/docs/reference/code_detection.md @@ -0,0 +1,43 @@ +# Code detection + +## Why use auto-detection? + +TGPy is designed for running code snippets sequentially and frequently. Bot-like commands +(such as `/run print('Hello World')`) would break the workflow. That's why TGPy automatically detects your messages with +syntactically correct Python code and evaluates it. + +It turns out that regular text messages are identified as code pretty rarely. In fact, TGPy ignores too simple +expressions. + +However, optional disabling of auto-detection might be added in the future. + +## What is ignored? + +TL;DR: Some simple expressions, which could be email addresses, URLs or several comma- or hyphen-separated words +(as described in [issue 4](https://github.com/tm-a-t/TGPy/issues/4)) + +??? note "More details" + + In this section, an **unknown** variable is one not present in `locals` — that is, one that was not saved in previous + messages and which is not built into TGPy (as `ctx`, `orig`, `msg` and `print` are). Unknown variables' attributes are + also considered unknown. + + **Ignored** expressions are the expressions from the list below: + + * Constants like `1` or `"abcd"` and unknown variables + * Binary operations on unknown variables (recursively, i.e., `a - b -c` is also ignored in case `a`, `b`, or `c` are unknown) + * Unary operations on constants or unknown variables + * Tuples of ignored expressions + * Multiple ignored expressions (i.e. separated by `;` or newline) + +## Cancelling evaluation + +You can restore the message with evaluated code to its original contents with the `cancel` command. + +`cancel` edits back your latest TGPy message in the current chat (if it’s in 10 latest messages). + +`cancel` can be also used in reply to a specific TGPy message. + +## Preventing evaluation + +If you begin your message with `//`, the code won’t run. The `//` prefix will be deleted. diff --git a/guide/docs/reference/module_metadata.md b/guide/docs/reference/module_metadata.md new file mode 100644 index 0000000..1cc3ecc --- /dev/null +++ b/guide/docs/reference/module_metadata.md @@ -0,0 +1,31 @@ +# Module metadata + +Modules are stored as separate Python files in `data/modules` directory. Module metadata is a YAML comment at the start of the module. + +You can safely edit it manually. The changes will apply after a restart. + +[Read on using modules](/extensibility/modules/) + +## Metadata example + +```python +""" + name: MyModule + once: false + origin: tgpy://module/MyModule + priority: 1655584820 + save_locals: true +""" + +# module code goes here +``` + +## Fields + +| Key | Description | Default value | +| --- | ----------- | ------------- | +| `name` | The name of the module | | +| `once` | If `true`, the module will be deleted after running | `false` | +| `origin` | The string specifying the origin of the module (used for logs) | `tgpy://module/` | +| `priority` | A number that defines the order in which modules are run at startup. The module with the lowest priority will run first | timestamp for the time the module was created | +| `save_locals` | If `true`, the module variables can be later used in TGPy code snippets | `true` | diff --git a/guide/docs/stylesheets/extra.css b/guide/docs/stylesheets/extra.css new file mode 100644 index 0000000..3082eb4 --- /dev/null +++ b/guide/docs/stylesheets/extra.css @@ -0,0 +1,7 @@ +.tgpy-code-block { + background-color: var(--md-code-bg-color); +} + +.tgpy-code-block > hr { + margin: -1em 0; +} \ No newline at end of file diff --git a/guide/mkdocs.yml b/guide/mkdocs.yml index 3cbf48b..683ae75 100644 --- a/guide/mkdocs.yml +++ b/guide/mkdocs.yml @@ -1,19 +1,25 @@ site_name: TGPy Guide +site_description: 'TGPy, a tool for running Python code snippets right in your Telegram messages' +site_url: https://tgpy.tmat.me/ repo_url: https://github.com/tm-a-t/TGPy repo_name: tm-a-t/TGPy edit_uri: edit/master/guide/docs/ theme: name: material features: + - content.code.annotate - navigation.instant - navigation.sections + - search.suggest + - search.highlight + - search.share icon: repo: fontawesome/brands/github admonition: - note: octicons/tag-16 + note: octicons/pin-16 abstract: octicons/checklist-16 info: octicons/info-16 - tip: octicons/squirrel-16 + tip: octicons/flame-16 success: octicons/check-16 question: octicons/question-16 warning: octicons/alert-16 @@ -22,27 +28,65 @@ theme: bug: octicons/bug-16 example: octicons/beaker-16 quote: octicons/quote-16 + font: + text: Albert Sans + code: Source Code Pro + palette: + - scheme: default + primary: cyan + accent: cyan + toggle: + icon: material/weather-sunny + name: Switch to dark mode + - scheme: slate + primary: cyan + accent: cyan + toggle: + icon: material/weather-night + name: Switch to light mode plugins: - search + - git-revision-date-localized: + type: timeago + enable_creation_date: true markdown_extensions: - toc: permalink: true - admonition - attr_list + - tables - pymdownx.highlight - pymdownx.inlinehilite - pymdownx.superfences - pymdownx.details - pymdownx.superfences - pymdownx.mark + - pymdownx.tabbed: + alternate_style: true - pymdownx.tilde +extra: + social: + - icon: material/youtube + link: https://www.youtube.com/watch?v=dQw4w9WgXcQ + name: TGPy on YouTube +extra_css: + - stylesheets/extra.css nav: - - Home: index.md + - index.md - installation.md - - basics.md - - builtins.md - - code_detection.md - - Advanced: - - modules.md - - context.md - - api.md + - Basics: + - basics/code.md + - basics/asyncio.md + - basics/messages.md + - basics/examples.md + - Extensibility: + - extensibility/context.md + - extensibility/modules.md + - extensibility/module_examples.md + - extensibility/code_transformers.md + - Reference: + - reference/builtins.md + - reference/module_metadata.md + - reference/code_detection.md +# - Explanation: +# - explanation/how.md diff --git a/guide/requirements.txt b/guide/requirements.txt index 4c8f017..f522c59 100644 --- a/guide/requirements.txt +++ b/guide/requirements.txt @@ -1 +1,2 @@ mkdocs-material +mkdocs-git-revision-date-localized-plugin \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 512bbb0..414dae2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,6 +1,6 @@ [[package]] name = "aiorun" -version = "2021.10.1" +version = "2022.4.1" description = "Boilerplate for asyncio applications" category = "main" optional = false @@ -19,56 +19,55 @@ python-versions = "*" [[package]] name = "black" -version = "21.12b0" +version = "22.6.0" description = "The uncompromising code formatter." category = "dev" optional = false python-versions = ">=3.6.2" [package.dependencies] -click = ">=7.1.2" +click = ">=8.0.0" mypy-extensions = ">=0.4.3" -pathspec = ">=0.9.0,<1" +pathspec = ">=0.9.0" platformdirs = ">=2" -tomli = ">=0.2.6,<2.0.0" -typing-extensions = [ - {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}, - {version = "!=3.10.0.1", markers = "python_version >= \"3.10\""}, -] +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] colorama = ["colorama (>=0.4.3)"] d = ["aiohttp (>=3.7.4)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -python2 = ["typed-ast (>=1.4.3)"] uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "bleach" -version = "4.1.0" +version = "5.0.1" description = "An easy safelist-based HTML-sanitizing tool." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] -packaging = "*" six = ">=1.9.0" webencodings = "*" +[package.extras] +css = ["tinycss2 (>=1.1.0,<1.2)"] +dev = ["build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "Sphinx (==4.3.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)", "black (==22.3.0)", "mypy (==0.961)"] + [[package]] name = "certifi" -version = "2021.10.8" +version = "2022.6.15" description = "Python package for providing Mozilla's CA Bundle." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" [[package]] name = "cffi" -version = "1.15.0" +version = "1.15.1" description = "Foreign Function Interface for Python calling C code." -category = "main" +category = "dev" optional = false python-versions = "*" @@ -77,29 +76,29 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "2.0.9" +version = "2.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "dev" optional = false -python-versions = ">=3.5.0" +python-versions = ">=3.6.0" [package.extras] unicode_backport = ["unicodedata2"] [[package]] name = "click" -version = "8.0.3" +version = "8.1.3" description = "Composable command line interface toolkit" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "click-log" -version = "0.3.2" +version = "0.4.0" description = "Logging integration for Click" category = "dev" optional = false @@ -110,9 +109,9 @@ click = "*" [[package]] name = "colorama" -version = "0.4.4" +version = "0.4.5" description = "Cross-platform colored terminal text." -category = "main" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" @@ -125,23 +124,19 @@ optional = false python-versions = "*" [package.extras] -test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] +test = ["hypothesis (==3.55.3)", "flake8 (==3.7.8)"] [[package]] name = "cryptg" -version = "0.2.post4" +version = "0.3.1" description = "Cryptographic utilities for Telegram." category = "main" optional = false -python-versions = ">=3.6" - -[package.dependencies] -cffi = ">=1.0.0" -pycparser = "*" +python-versions = ">=3.3" [[package]] name = "cryptography" -version = "36.0.1" +version = "37.0.4" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "dev" optional = false @@ -156,26 +151,23 @@ docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] sdist = ["setuptools_rust (>=0.11.4)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] +test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] [[package]] name = "docutils" -version = "0.18.1" +version = "0.19" description = "Docutils -- Python Documentation Utilities" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" [[package]] name = "dotty-dict" -version = "1.3.0" +version = "1.3.1" description = "Dictionary wrapper for quick access to deeply nested keys." category = "dev" optional = false -python-versions = "*" - -[package.dependencies] -setuptools_scm = "*" +python-versions = ">=3.5,<4.0" [[package]] name = "gitdb" @@ -190,7 +182,7 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.24" +version = "3.1.27" description = "GitPython is a python library used to interact with Git repositories" category = "dev" optional = false @@ -198,7 +190,6 @@ python-versions = ">=3.7" [package.dependencies] gitdb = ">=4.0.1,<5" -typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""} [[package]] name = "idna" @@ -210,7 +201,7 @@ python-versions = ">=3.5" [[package]] name = "importlib-metadata" -version = "4.10.0" +version = "4.12.0" description = "Read metadata from Python packages" category = "dev" optional = false @@ -220,13 +211,13 @@ python-versions = ">=3.7" zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] perf = ["ipython"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl-flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] [[package]] name = "invoke" -version = "1.6.0" +version = "1.7.1" description = "Pythonic task execution" category = "dev" optional = false @@ -248,33 +239,33 @@ plugins = ["setuptools"] [[package]] name = "jeepney" -version = "0.7.1" +version = "0.8.0" description = "Low-level, pure Python DBus protocol wrapper." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] -test = ["pytest", "pytest-trio", "pytest-asyncio", "testpath", "trio", "async-timeout"] -trio = ["trio", "async-generator"] +trio = ["async-generator", "trio"] +test = ["async-timeout", "trio", "testpath", "pytest-asyncio (>=0.17)", "pytest-trio", "pytest"] [[package]] name = "keyring" -version = "23.4.0" +version = "23.7.0" description = "Store and access your passwords safely." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] -importlib-metadata = ">=3.6" +importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""} SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [[package]] name = "mypy-extensions" @@ -305,26 +296,26 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "pkginfo" -version = "1.8.2" +version = "1.8.3" description = "Query metadatdata from sdists / bdists / installed packages." category = "dev" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [package.extras] -testing = ["coverage", "nose"] +testing = ["nose", "coverage"] [[package]] name = "platformdirs" -version = "2.4.1" +version = "2.5.2" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" [package.extras] -docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] +test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] [[package]] name = "pyaes" @@ -346,14 +337,14 @@ python-versions = "*" name = "pycparser" version = "2.21" description = "C parser in Python" -category = "main" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pydantic" -version = "1.8.2" -description = "Data validation and settings management using python 3.6 type hinting" +version = "1.9.1" +description = "Data validation and settings management using python type hints" category = "main" optional = false python-versions = ">=3.6.1" @@ -367,42 +358,42 @@ email = ["email-validator (>=1.0.3)"] [[package]] name = "pygments" -version = "2.10.0" +version = "2.12.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [[package]] name = "pyparsing" -version = "3.0.6" -description = "Python parsing module" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.8" [package.extras] -diagrams = ["jinja2", "railroad-diagrams"] +diagrams = ["railroad-diagrams", "jinja2"] [[package]] name = "python-gitlab" -version = "2.10.1" +version = "3.7.0" description = "Interact with GitLab API" category = "dev" optional = false -python-versions = ">=3.6.0" +python-versions = ">=3.7.0" [package.dependencies] requests = ">=2.25.0" requests-toolbelt = ">=0.9.1" [package.extras] -autocompletion = ["argcomplete (>=1.10.0,<2)"] +autocompletion = ["argcomplete (>=1.10.0,<3)"] yaml = ["PyYaml (>=5.2)"] [[package]] name = "python-semantic-release" -version = "7.23.0" +version = "7.31.2" description = "Automatic Semantic Versioning for Python projects" category = "dev" optional = false @@ -414,15 +405,17 @@ click-log = ">=0.3,<1" dotty-dict = ">=1.3.0,<2" gitpython = ">=3.0.8,<4" invoke = ">=1.4.1,<2" -python-gitlab = ">=1.10,<3" +packaging = "*" +python-gitlab = ">=2,<4" requests = ">=2.25,<3" semver = ">=2.10,<3" -tomlkit = "0.7.0" +tomlkit = ">=0.10.0,<0.11.0" twine = ">=3,<4" +wheel = "*" [package.extras] dev = ["tox", "isort", "black"] -docs = ["Sphinx (==1.3.6)"] +docs = ["Sphinx (==1.3.6)", "Jinja2 (==3.0.3)"] mypy = ["mypy", "types-requests"] test = ["coverage (>=5,<6)", "pytest (>=5,<6)", "pytest-xdist (>=1,<2)", "pytest-mock (>=2,<3)", "responses (==0.13.3)", "mock (==1.3.0)"] @@ -444,11 +437,11 @@ python-versions = ">=3.6" [[package]] name = "readme-renderer" -version = "32.0" +version = "35.0" description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] bleach = ">=2.1.0" @@ -456,25 +449,25 @@ docutils = ">=0.13.1" Pygments = ">=2.5.1" [package.extras] -md = ["cmarkgfm (>=0.5.0,<0.7.0)"] +md = ["cmarkgfm (>=0.8.0)"] [[package]] name = "requests" -version = "2.26.0" +version = "2.28.1" description = "Python HTTP for Humans." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.7, <4" [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} -idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +charset-normalizer = ">=2,<3" +idna = ">=2.5,<4" urllib3 = ">=1.21.1,<1.27" [package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-toolbelt" @@ -489,25 +482,24 @@ requests = ">=2.0.1,<3.0.0" [[package]] name = "rfc3986" -version = "1.5.0" +version = "2.0.0" description = "Validating URI References per RFC 3986" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" [package.extras] idna2008 = ["idna"] [[package]] name = "rich" -version = "10.16.1" +version = "12.5.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" category = "main" optional = false -python-versions = ">=3.6.2,<4.0.0" +python-versions = ">=3.6.3,<4.0.0" [package.dependencies] -colorama = ">=0.4.0,<0.5.0" commonmark = ">=0.9.0,<0.10.0" pygments = ">=2.6.0,<3.0.0" @@ -516,7 +508,7 @@ jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] [[package]] name = "rsa" -version = "4.8" +version = "4.9" description = "Pure-Python RSA implementation" category = "main" optional = false @@ -527,7 +519,7 @@ pyasn1 = ">=0.1.3" [[package]] name = "secretstorage" -version = "3.3.1" +version = "3.3.2" description = "Python bindings to FreeDesktop.org Secret Service API" category = "dev" optional = false @@ -545,21 +537,6 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -[[package]] -name = "setuptools-scm" -version = "6.3.2" -description = "the blessed package to manage your versions by scm tags" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -packaging = ">=20.0" -tomli = ">=1.0.0" - -[package.extras] -toml = ["setuptools (>=42)", "tomli (>=1.0.0)"] - [[package]] name = "six" version = "1.16.0" @@ -577,8 +554,8 @@ optional = false python-versions = ">=3.6" [[package]] -name = "telethon" -version = "1.24.0" +name = "telethon-v1-24" +version = "1.24.2" description = "Full-featured Telegram client library for Python 3" category = "main" optional = false @@ -593,23 +570,23 @@ cryptg = ["cryptg"] [[package]] name = "tomli" -version = "1.2.3" +version = "2.0.1" description = "A lil' TOML parser" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] name = "tomlkit" -version = "0.7.0" +version = "0.10.2" description = "Style preserving TOML library" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6,<4.0" [[package]] name = "tqdm" -version = "4.62.3" +version = "4.64.0" description = "Fast, Extensible Progress Meter" category = "dev" optional = false @@ -621,11 +598,12 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] dev = ["py-make (>=0.1.0)", "twine", "wheel"] notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] telegram = ["requests"] [[package]] name = "twine" -version = "3.7.1" +version = "3.8.0" description = "Collection of utilities for publishing packages on PyPI" category = "dev" optional = false @@ -641,25 +619,26 @@ requests = ">=2.20" requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" rfc3986 = ">=1.4.0" tqdm = ">=4.14" +urllib3 = ">=1.26.0" [[package]] name = "typing-extensions" -version = "4.0.1" -description = "Backported and Experimental Type Hints for Python 3.6+" +version = "4.3.0" +description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] name = "urllib3" -version = "1.26.7" +version = "1.26.11" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" [package.extras] -brotli = ["brotlipy (>=0.6.0)"] +brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] @@ -671,213 +650,263 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "wheel" +version = "0.37.1" +description = "A built-package format for Python" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.extras] +test = ["pytest (>=3.0.0)", "pytest-cov"] + [[package]] name = "zipp" -version = "3.6.0" +version = "3.8.1" description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco-itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "16b3ecaa3dfbcb4b23bdc8a5aa1cbf8a5ca9a4a865402c16301c8796b72a1764" +content-hash = "1ad95884b5f10bb43a992bb0744d0cab8e3313523073a8eec3d5a36dc81e3497" [metadata.files] aiorun = [ - {file = "aiorun-2021.10.1-py3-none-any.whl", hash = "sha256:49c3b0ca9aa4c8ace443691d31d3114ad7a3abe7af19abb13725309dec2e3c2c"}, - {file = "aiorun-2021.10.1.tar.gz", hash = "sha256:bb621493e008cd072a9b19de09a615513be2f48b594ee16ee726da5255fa4267"}, + {file = "aiorun-2022.4.1-py3-none-any.whl", hash = "sha256:3a7925d66c394f6812a0150495fda8d359b4cdf24cf0c223f005b61ad02baaa7"}, + {file = "aiorun-2022.4.1.tar.gz", hash = "sha256:6aafa65c4004c1697088d8f09a9a68e8d486a5403f2e35956f5f0173ce7501f5"}, ] appdirs = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] black = [ - {file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"}, - {file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"}, + {file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"}, + {file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"}, + {file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"}, + {file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"}, + {file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"}, + {file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"}, + {file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"}, + {file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"}, + {file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"}, + {file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"}, + {file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"}, + {file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"}, + {file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"}, + {file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"}, + {file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"}, + {file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"}, + {file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"}, + {file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"}, + {file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"}, + {file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"}, + {file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"}, + {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"}, + {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"}, ] bleach = [ - {file = "bleach-4.1.0-py2.py3-none-any.whl", hash = "sha256:4d2651ab93271d1129ac9cbc679f524565cc8a1b791909c4a51eac4446a15994"}, - {file = "bleach-4.1.0.tar.gz", hash = "sha256:0900d8b37eba61a802ee40ac0061f8c2b5dee29c1927dd1d233e075ebf5a71da"}, + {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"}, + {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"}, ] certifi = [ - {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, - {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, + {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, + {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, ] cffi = [ - {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, - {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, - {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, - {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, - {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, - {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, - {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, - {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, - {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, - {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, - {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, - {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, - {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, - {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, - {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, - {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, - {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, - {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, - {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, - {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, - {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, - {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, - {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, - {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, - {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, ] charset-normalizer = [ - {file = "charset-normalizer-2.0.9.tar.gz", hash = "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c"}, - {file = "charset_normalizer-2.0.9-py3-none-any.whl", hash = "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721"}, + {file = "charset-normalizer-2.1.0.tar.gz", hash = "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"}, + {file = "charset_normalizer-2.1.0-py3-none-any.whl", hash = "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5"}, ] click = [ - {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, - {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] click-log = [ - {file = "click-log-0.3.2.tar.gz", hash = "sha256:16fd1ca3fc6b16c98cea63acf1ab474ea8e676849dc669d86afafb0ed7003124"}, - {file = "click_log-0.3.2-py2.py3-none-any.whl", hash = "sha256:eee14dc37cdf3072158570f00406572f9e03e414accdccfccd4c538df9ae322c"}, + {file = "click-log-0.4.0.tar.gz", hash = "sha256:3970f8570ac54491237bcdb3d8ab5e3eef6c057df29f8c3d1151a51a9c23b975"}, + {file = "click_log-0.4.0-py2.py3-none-any.whl", hash = "sha256:a43e394b528d52112af599f2fc9e4b7cf3c15f94e53581f74fa6867e68c91756"}, ] colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, + {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, + {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, ] commonmark = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] cryptg = [ - {file = "cryptg-0.2.post4-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:b6352555e47f389ed502269bdb537233d0a928b12d9f4caa57e8c707151acd30"}, - {file = "cryptg-0.2.post4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:7fc8e1893775c6f53dceda1959f19833cc27a67a80492c10e2415dc601b36650"}, - {file = "cryptg-0.2.post4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2516557e89803637fa7342de43dbcc5f84bf68ae05b1064a354a62d423447d9f"}, - {file = "cryptg-0.2.post4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:c09a5b14494532fc3226f5c5f57ef2a651c935ed6a1d2d0f9eff110046725524"}, - {file = "cryptg-0.2.post4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:eff15f0a1eee678dd9ec747b58ce86edb78b608036ac4e02d8349f5f35202495"}, - {file = "cryptg-0.2.post4-cp36-cp36m-win32.whl", hash = "sha256:72a5485ece10a70160170ceb658b1836db82dccab08a1f7029c54d81cf6b1d43"}, - {file = "cryptg-0.2.post4-cp36-cp36m-win_amd64.whl", hash = "sha256:29001dafd3d6a054365222b1f89b12876723c89cdd10aa0e5885a05dfd034eeb"}, - {file = "cryptg-0.2.post4-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:307bf96a6ac9c87b44531d8da5fe3a6c5d856e1dc69b68136ef9c4fb66ad17ac"}, - {file = "cryptg-0.2.post4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:47ad5916be4558f4d674c12800e8d9663ce938b0046f19cdc869ba3a7ca280ec"}, - {file = "cryptg-0.2.post4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6c5d66975fc59adca203fa91e2a104240457114468162d30e9213661239ac1d6"}, - {file = "cryptg-0.2.post4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:bf00943924cddb0838f8a65f5aae31f6fe2ad64a5d7e6f10a6b900b3f01b0ae0"}, - {file = "cryptg-0.2.post4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:135688c6fbda90748924c2cb047f63785ebf4397d81acc4a05357950653c5096"}, - {file = "cryptg-0.2.post4-cp37-cp37m-win32.whl", hash = "sha256:46960979542155c9d903656a3a39770061b09a3691a23296f06dc168fe4ff962"}, - {file = "cryptg-0.2.post4-cp37-cp37m-win_amd64.whl", hash = "sha256:ce08c04ebb06ce1ac417597c1bb514a3c1b36cf5c286b8c60f23df2e65703bf3"}, - {file = "cryptg-0.2.post4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:695636cca0ee938bd7113658ee60bfaf89afa19708c40ecae5f4a222c2ec544a"}, - {file = "cryptg-0.2.post4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1fb6c6d4561a54406593197c1f5f23662ab320f4af4ab11834e1583e9d27a49a"}, - {file = "cryptg-0.2.post4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bbd05b52d09e78bdc595f229c0481f4f2e1daf3959847322a6b2c1f76119305f"}, - {file = "cryptg-0.2.post4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c4812802ce4cd6f08189ce0fa8b79e9a96ac941e69e6b3032bb6908baefde2ba"}, - {file = "cryptg-0.2.post4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c69c1e19884108e508697919de0cd43e2ca4e9af418962aa235273b3c51a0e37"}, - {file = "cryptg-0.2.post4-cp38-cp38-win32.whl", hash = "sha256:e29b0d944176cf88fe52d1c58f46017b5bddc9cc54ec0fc6fac20043febefc32"}, - {file = "cryptg-0.2.post4-cp38-cp38-win_amd64.whl", hash = "sha256:5faed49d972c7f44ce4d6fa1a64169c85a11209fa1fbe1c8a333fb1454888725"}, - {file = "cryptg-0.2.post4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:b8896394b72ff7dbf38072ad4c2cd59abdd9e388bb55e1c369102beb8e569f9d"}, - {file = "cryptg-0.2.post4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:e48ab84e0ed364436d5e449c59762c5963f08ad87f6508f4cb7644745b5559a8"}, - {file = "cryptg-0.2.post4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:0da1b367056e57a5c01d22608da0cd50e597b917c1b2d9631767aa3c0640a99a"}, - {file = "cryptg-0.2.post4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:31cf7682de69022c9a77739cdcf7116b06522b128b9b51c7593f277f38c38dbf"}, - {file = "cryptg-0.2.post4-cp39-cp39-win32.whl", hash = "sha256:02b31622a75a49a5dcd25e589c85faae54575f018e055bd21a17df97c8bb9095"}, - {file = "cryptg-0.2.post4-cp39-cp39-win_amd64.whl", hash = "sha256:3bc2f372dec3a7753c0c0d72c69fcbe44af5473f870a3406978e07e8560a1aa6"}, - {file = "cryptg-0.2.post4-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:a1fb178702730b59267f1e6c6dfe16c7bb9c1350cee4183221982ad2dba4e7f5"}, - {file = "cryptg-0.2.post4-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:2cc8115960e49a038091ffb2d09de59e0acbdc76de10d7d415b7671a06bae0a9"}, - {file = "cryptg-0.2.post4-pp36-pypy36_pp73-win32.whl", hash = "sha256:bf15aae0fa01aeec728ab16b920cf4c6b2793099c71f62f30ff100d6fe8c9859"}, - {file = "cryptg-0.2.post4-pp37-pypy37_pp73-manylinux1_x86_64.whl", hash = "sha256:890584db41c8e1e046ae40dee0074614470d36ebd6b7e57bb91303300066601f"}, - {file = "cryptg-0.2.post4-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:fdd62c2be23eeabb9ebd2ad41bf153f5ec48b968885ef14e676515407cd56339"}, - {file = "cryptg-0.2.post4-pp37-pypy37_pp73-win32.whl", hash = "sha256:2cd8224eb64af756f45cdceab16d048494313db8acec1e38d75d97716082267b"}, - {file = "cryptg-0.2.post4.tar.gz", hash = "sha256:a4de1730ca56aa8a945f176c25586901ed5e9f15ffb70c6459eedf466eb6299b"}, + {file = "cryptg-0.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:28866fd8b265129a246a4317ff27af21cbfb90850b57567bcb2df04f86118779"}, + {file = "cryptg-0.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e678f128857394592850db653dfe9aebd63db9dca325cb212844e5a3a5b1f5be"}, + {file = "cryptg-0.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:77c06b69cdb7d435131a2ebc7a2cfa9373ed51d0f0da6560ea9b90e11e56175f"}, + {file = "cryptg-0.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:75bc0833ab7e6b25a3feb1984c6e05b3c4b8a59ad5a6b488ead9919f5d886f9c"}, + {file = "cryptg-0.3.1-cp310-cp310-win32.whl", hash = "sha256:6abb6dcaf57968d8e17646385b337cea1629cbe674c225daad1da406e2b387f3"}, + {file = "cryptg-0.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:3d99c20ef4d84fc8d49e69a40fb61eeb25520ccdc7b49fc2dae63d7eaf128876"}, + {file = "cryptg-0.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2fbf0a7fa0de58e6680293c21de2bdafea070d0e8ed2d1ceac62890dde99a419"}, + {file = "cryptg-0.3.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80cf880381025831537a3a9ab2277474136628b030258d55e755e102390c05b3"}, + {file = "cryptg-0.3.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f754cd98799d8b5f552af83f3da3886ae161ba29f9bf0a81278a789fce49407a"}, + {file = "cryptg-0.3.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c50a2d8fe56c662ad4c8d773f5506fda522de6b06eb4d40a4a63d0fe119a4ed"}, + {file = "cryptg-0.3.1-cp36-cp36m-win32.whl", hash = "sha256:e341cda3d34ccdad56772e1de331741e454cc2803edffd27434c090ebe6afbaf"}, + {file = "cryptg-0.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:074fc5cd51519ea87fad9a4fdab47434ca60374b638459bf96368a7300b4c6a1"}, + {file = "cryptg-0.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95a15bce5897934199b7c6bbc6744f1145c6b280ec7dfb6d2eff2c22f49fba1b"}, + {file = "cryptg-0.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd98da66a47a6932e4c3f1aae066a9902bc33e4152d113977b54b8a8b4ddd02a"}, + {file = "cryptg-0.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:369d23e1e29365b9bb24a798dce49c2d638afbd0dcfefe9cb7fcdfcc2723e92d"}, + {file = "cryptg-0.3.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3fc10c2274faa78f4ae00f38546f4e6890ad1a31313b61c80252de6857e5ae7d"}, + {file = "cryptg-0.3.1-cp37-cp37m-win32.whl", hash = "sha256:21811c3abe7ccba8f7fca4fbdbc96eb5586d1768c78e077f79dfffd651cd84ce"}, + {file = "cryptg-0.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2c81f4f35557c0adb4339fc1da399ac1f4cc2d48ef099ca3371a48507f81b836"}, + {file = "cryptg-0.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9523236d9712c85f1b9b70072c8411b0ee705e9513c6115560fb033e86d38bd9"}, + {file = "cryptg-0.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d65ce0731a2b52cf61718cf8fd5fdf54b0e188faefa606f05ec0fefeeedefe6d"}, + {file = "cryptg-0.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dce2ce150db5622b991af5fa45e6dda53fd0b39463143267233f56eaa466cb7b"}, + {file = "cryptg-0.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:de9f9efabc9d58d2d843bec86aac4eaa3b56acf938b7cc904ff4cddda6de1d48"}, + {file = "cryptg-0.3.1-cp38-cp38-win32.whl", hash = "sha256:d43c33b2c4cb8f9c0bf90fc1cbc72000042a95feef92877e2bce625976ac6831"}, + {file = "cryptg-0.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:8495b70e65b0614447311d41c2cbe3ae23b3b9341cfb701d906b91a0d27d685c"}, + {file = "cryptg-0.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:88adb6eb55dbac30259812c208260315df9fa68aac7a07c917e649a55bec61c9"}, + {file = "cryptg-0.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab0d85f210cdb5cc8ec9ad2c1e3f37352679d58d76027f812f07808620775acf"}, + {file = "cryptg-0.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:027a49e01136d2e8d48ded9556b314eeae5cc0d0f29dca2ca3bbdff547403c73"}, + {file = "cryptg-0.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:21e144f5811989e82f198b827b9d9b5f4afbda22e265947b662bc5eb5416c49d"}, + {file = "cryptg-0.3.1-cp39-cp39-win32.whl", hash = "sha256:52f18c9a6bd8462f1047ccbc3e477b84781d5da24ee899e281482ea82b67ce11"}, + {file = "cryptg-0.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:016be3d4e04b91b2a251c281a02357d97fc5a8e9d68633dd3bbcf831e29a8b14"}, + {file = "cryptg-0.3.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:08f25386494b3a038699c119b87959c920ea281ca68d8de30f7f3562b15866d1"}, + {file = "cryptg-0.3.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc0dc5cedb27a53953014c42c5cd98b5eeb7109145f7b39c8d235cd9dd7e21ff"}, + {file = "cryptg-0.3.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6931cf71047ad582c78d877ab83e064f27783abb59e926552f7b73bedf686daa"}, + {file = "cryptg-0.3.1-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:170be19b8c2f90f5108c3bd35a45f3f5a3674130730d52f200160097ab18baa8"}, + {file = "cryptg-0.3.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:7eb7f93900321a0c3f585877719c8b95af0d9c440fdefd7b011dfc6e3e2efb25"}, + {file = "cryptg-0.3.1.tar.gz", hash = "sha256:d309bd387b7e8257390e611ea44f983199e16812e82bea9a0b1d50e74e6ec8dc"}, ] cryptography = [ - {file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:73bc2d3f2444bcfeac67dd130ff2ea598ea5f20b40e36d19821b4df8c9c5037b"}, - {file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:2d87cdcb378d3cfed944dac30596da1968f88fb96d7fc34fdae30a99054b2e31"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74d6c7e80609c0f4c2434b97b80c7f8fdfaa072ca4baab7e239a15d6d70ed73a"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:6c0c021f35b421ebf5976abf2daacc47e235f8b6082d3396a2fe3ccd537ab173"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59a9d55027a8b88fd9fd2826c4392bd487d74bf628bb9d39beecc62a644c12"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a817b961b46894c5ca8a66b599c745b9a3d9f822725221f0e0fe49dc043a3a3"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:94ae132f0e40fe48f310bba63f477f14a43116f05ddb69d6fa31e93f05848ae2"}, - {file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7be0eec337359c155df191d6ae00a5e8bbb63933883f4f5dffc439dac5348c3f"}, - {file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e0344c14c9cb89e76eb6a060e67980c9e35b3f36691e15e1b7a9e58a0a6c6dc3"}, - {file = "cryptography-36.0.1-cp36-abi3-win32.whl", hash = "sha256:4caa4b893d8fad33cf1964d3e51842cd78ba87401ab1d2e44556826df849a8ca"}, - {file = "cryptography-36.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:391432971a66cfaf94b21c24ab465a4cc3e8bf4a939c1ca5c3e3a6e0abebdbcf"}, - {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bb5829d027ff82aa872d76158919045a7c1e91fbf241aec32cb07956e9ebd3c9"}, - {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc15b1c22e55c4d5566e3ca4db8689470a0ca2babef8e3a9ee057a8b82ce4b1"}, - {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:596f3cd67e1b950bc372c33f1a28a0692080625592ea6392987dba7f09f17a94"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:30ee1eb3ebe1644d1c3f183d115a8c04e4e603ed6ce8e394ed39eea4a98469ac"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec63da4e7e4a5f924b90af42eddf20b698a70e58d86a72d943857c4c6045b3ee"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca238ceb7ba0bdf6ce88c1b74a87bffcee5afbfa1e41e173b1ceb095b39add46"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:ca28641954f767f9822c24e927ad894d45d5a1e501767599647259cbf030b903"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:39bdf8e70eee6b1c7b289ec6e5d84d49a6bfa11f8b8646b5b3dfe41219153316"}, - {file = "cryptography-36.0.1.tar.gz", hash = "sha256:53e5c1dc3d7a953de055d77bef2ff607ceef7a2aac0353b5d630ab67f7423638"}, + {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884"}, + {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280"}, + {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3"}, + {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59"}, + {file = "cryptography-37.0.4-cp36-abi3-win32.whl", hash = "sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157"}, + {file = "cryptography-37.0.4-cp36-abi3-win_amd64.whl", hash = "sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327"}, + {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b"}, + {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab"}, + {file = "cryptography-37.0.4.tar.gz", hash = "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82"}, ] docutils = [ - {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"}, - {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, + {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, + {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, ] dotty-dict = [ - {file = "dotty_dict-1.3.0.tar.gz", hash = "sha256:eb0035a3629ecd84397a68f1f42f1e94abd1c34577a19cd3eacad331ee7cbaf0"}, + {file = "dotty_dict-1.3.1-py3-none-any.whl", hash = "sha256:5022d234d9922f13aa711b4950372a06a6d64cb6d6db9ba43d0ba133ebfce31f"}, + {file = "dotty_dict-1.3.1.tar.gz", hash = "sha256:4b016e03b8ae265539757a53eba24b9bfda506fb94fbce0bee843c6f05541a15"}, ] gitdb = [ {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, ] gitpython = [ - {file = "GitPython-3.1.24-py3-none-any.whl", hash = "sha256:dc0a7f2f697657acc8d7f89033e8b1ea94dd90356b2983bca89dc8d2ab3cc647"}, - {file = "GitPython-3.1.24.tar.gz", hash = "sha256:df83fdf5e684fef7c6ee2c02fc68a5ceb7e7e759d08b694088d0cacb4eba59e5"}, + {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, + {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, ] idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.10.0-py3-none-any.whl", hash = "sha256:b7cf7d3fef75f1e4c80a96ca660efbd51473d7e8f39b5ab9210febc7809012a4"}, - {file = "importlib_metadata-4.10.0.tar.gz", hash = "sha256:92a8b58ce734b2a4494878e0ecf7d79ccd7a128b5fc6014c401e0b61f006f0f6"}, + {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, + {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, ] invoke = [ - {file = "invoke-1.6.0-py2-none-any.whl", hash = "sha256:e6c9917a1e3e73e7ea91fdf82d5f151ccfe85bf30cc65cdb892444c02dbb5f74"}, - {file = "invoke-1.6.0-py3-none-any.whl", hash = "sha256:769e90caeb1bd07d484821732f931f1ad8916a38e3f3e618644687fc09cb6317"}, - {file = "invoke-1.6.0.tar.gz", hash = "sha256:374d1e2ecf78981da94bfaf95366216aaec27c2d6a7b7d5818d92da55aa258d3"}, + {file = "invoke-1.7.1-py3-none-any.whl", hash = "sha256:2dc975b4f92be0c0a174ad2d063010c8a1fdb5e9389d69871001118b4fcac4fb"}, + {file = "invoke-1.7.1.tar.gz", hash = "sha256:7b6deaf585eee0a848205d0b8c0014b9bf6f287a8eb798818a642dff1df14b19"}, ] isort = [ {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, ] jeepney = [ - {file = "jeepney-0.7.1-py3-none-any.whl", hash = "sha256:1b5a0ea5c0e7b166b2f5895b91a08c14de8915afda4407fb5022a195224958ac"}, - {file = "jeepney-0.7.1.tar.gz", hash = "sha256:fa9e232dfa0c498bd0b8a3a73b8d8a31978304dcef0515adc859d4e096f96f4f"}, + {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, + {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, ] keyring = [ - {file = "keyring-23.4.0-py3-none-any.whl", hash = "sha256:3dc0f66062a4f8f6f2ce30d6a516e6e623e6c3c2e76864204ceaf64695408f07"}, - {file = "keyring-23.4.0.tar.gz", hash = "sha256:88f206024295e3c6fb16bb0a60fb4bb7ec1185629dc5a729f12aa7c236d01387"}, + {file = "keyring-23.7.0-py3-none-any.whl", hash = "sha256:e67fc91a7955785fd2efcbccdd72d7dacf136dbc381d27de305b2b660b3de886"}, + {file = "keyring-23.7.0.tar.gz", hash = "sha256:782e1cd1132e91bf459fcd243bcf25b326015c1ac0b198e4408f91fa6791062b"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, @@ -892,12 +921,12 @@ pathspec = [ {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] pkginfo = [ - {file = "pkginfo-1.8.2-py2.py3-none-any.whl", hash = "sha256:c24c487c6a7f72c66e816ab1796b96ac6c3d14d49338293d2141664330b55ffc"}, - {file = "pkginfo-1.8.2.tar.gz", hash = "sha256:542e0d0b6750e2e21c20179803e40ab50598d8066d51097a0e382cba9eb02bff"}, + {file = "pkginfo-1.8.3-py2.py3-none-any.whl", hash = "sha256:848865108ec99d4901b2f7e84058b6e7660aae8ae10164e015a6dcf5b242a594"}, + {file = "pkginfo-1.8.3.tar.gz", hash = "sha256:a84da4318dd86f870a9447a8c98340aa06216bfc6f2b7bdc4b8766984ae1867c"}, ] platformdirs = [ - {file = "platformdirs-2.4.1-py3-none-any.whl", hash = "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca"}, - {file = "platformdirs-2.4.1.tar.gz", hash = "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda"}, + {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, + {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, ] pyaes = [ {file = "pyaes-1.6.1.tar.gz", hash = "sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f"}, @@ -922,44 +951,57 @@ pycparser = [ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] pydantic = [ - {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"}, - {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"}, - {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"}, - {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"}, - {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"}, - {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"}, - {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"}, - {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"}, - {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"}, - {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"}, - {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"}, - {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"}, - {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"}, - {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"}, - {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"}, - {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"}, - {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"}, - {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"}, - {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"}, - {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"}, - {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"}, - {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"}, + {file = "pydantic-1.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8098a724c2784bf03e8070993f6d46aa2eeca031f8d8a048dff277703e6e193"}, + {file = "pydantic-1.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c320c64dd876e45254bdd350f0179da737463eea41c43bacbee9d8c9d1021f11"}, + {file = "pydantic-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18f3e912f9ad1bdec27fb06b8198a2ccc32f201e24174cec1b3424dda605a310"}, + {file = "pydantic-1.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11951b404e08b01b151222a1cb1a9f0a860a8153ce8334149ab9199cd198131"}, + {file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8bc541a405423ce0e51c19f637050acdbdf8feca34150e0d17f675e72d119580"}, + {file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e565a785233c2d03724c4dc55464559639b1ba9ecf091288dd47ad9c629433bd"}, + {file = "pydantic-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a4a88dcd6ff8fd47c18b3a3709a89adb39a6373f4482e04c1b765045c7e282fd"}, + {file = "pydantic-1.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:447d5521575f18e18240906beadc58551e97ec98142266e521c34968c76c8761"}, + {file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:985ceb5d0a86fcaa61e45781e567a59baa0da292d5ed2e490d612d0de5796918"}, + {file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059b6c1795170809103a1538255883e1983e5b831faea6558ef873d4955b4a74"}, + {file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d12f96b5b64bec3f43c8e82b4aab7599d0157f11c798c9f9c528a72b9e0b339a"}, + {file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ae72f8098acb368d877b210ebe02ba12585e77bd0db78ac04a1ee9b9f5dd2166"}, + {file = "pydantic-1.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:79b485767c13788ee314669008d01f9ef3bc05db9ea3298f6a50d3ef596a154b"}, + {file = "pydantic-1.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:494f7c8537f0c02b740c229af4cb47c0d39840b829ecdcfc93d91dcbb0779892"}, + {file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e"}, + {file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:969dd06110cb780da01336b281f53e2e7eb3a482831df441fb65dd30403f4608"}, + {file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:177071dfc0df6248fd22b43036f936cfe2508077a72af0933d0c1fa269b18537"}, + {file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9bcf8b6e011be08fb729d110f3e22e654a50f8a826b0575c7196616780683380"}, + {file = "pydantic-1.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a955260d47f03df08acf45689bd163ed9df82c0e0124beb4251b1290fa7ae728"}, + {file = "pydantic-1.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9ce157d979f742a915b75f792dbd6aa63b8eccaf46a1005ba03aa8a986bde34a"}, + {file = "pydantic-1.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0bf07cab5b279859c253d26a9194a8906e6f4a210063b84b433cf90a569de0c1"}, + {file = "pydantic-1.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d93d4e95eacd313d2c765ebe40d49ca9dd2ed90e5b37d0d421c597af830c195"}, + {file = "pydantic-1.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1542636a39c4892c4f4fa6270696902acb186a9aaeac6f6cf92ce6ae2e88564b"}, + {file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a9af62e9b5b9bc67b2a195ebc2c2662fdf498a822d62f902bf27cccb52dbbf49"}, + {file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6"}, + {file = "pydantic-1.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:9f659a5ee95c8baa2436d392267988fd0f43eb774e5eb8739252e5a7e9cf07e0"}, + {file = "pydantic-1.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b83ba3825bc91dfa989d4eed76865e71aea3a6ca1388b59fc801ee04c4d8d0d6"}, + {file = "pydantic-1.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1dd8fecbad028cd89d04a46688d2fcc14423e8a196d5b0a5c65105664901f810"}, + {file = "pydantic-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02eefd7087268b711a3ff4db528e9916ac9aa18616da7bca69c1871d0b7a091f"}, + {file = "pydantic-1.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb57ba90929bac0b6cc2af2373893d80ac559adda6933e562dcfb375029acee"}, + {file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4ce9ae9e91f46c344bec3b03d6ee9612802682c1551aaf627ad24045ce090761"}, + {file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72ccb318bf0c9ab97fc04c10c37683d9eea952ed526707fabf9ac5ae59b701fd"}, + {file = "pydantic-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:61b6760b08b7c395975d893e0b814a11cf011ebb24f7d869e7118f5a339a82e1"}, + {file = "pydantic-1.9.1-py3-none-any.whl", hash = "sha256:4988c0f13c42bfa9ddd2fe2f569c9d54646ce84adc5de84228cfe83396f3bd58"}, + {file = "pydantic-1.9.1.tar.gz", hash = "sha256:1ed987c3ff29fff7fd8c3ea3a3ea877ad310aae2ef9889a119e22d3f2db0691a"}, ] pygments = [ - {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"}, - {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"}, + {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, + {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, ] pyparsing = [ - {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, - {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, ] python-gitlab = [ - {file = "python-gitlab-2.10.1.tar.gz", hash = "sha256:7afa7d7c062fa62c173190452265a30feefb844428efc58ea5244f3b9fc0d40f"}, - {file = "python_gitlab-2.10.1-py3-none-any.whl", hash = "sha256:581a219759515513ea9399e936ed7137437cfb681f52d2641626685c492c999d"}, + {file = "python-gitlab-3.7.0.tar.gz", hash = "sha256:b3d57cd5469a4ef903df6c55314bb74aeefe26dffc23ff7e267d22f9c674c262"}, + {file = "python_gitlab-3.7.0-py3-none-any.whl", hash = "sha256:2ebdbf08127e89d4967b70861f19f61072a0cab697979d36917ba027ee2810b2"}, ] python-semantic-release = [ - {file = "python-semantic-release-7.23.0.tar.gz", hash = "sha256:48c33bf671dafa1257e7d955543856eb98486a3f976f586053556ae180d725da"}, - {file = "python_semantic_release-7.23.0-py3-none-any.whl", hash = "sha256:5bf7fcdb28e5e9888c9a15a1168afe53302116a6874d818580d4c58db60283ab"}, + {file = "python-semantic-release-7.31.2.tar.gz", hash = "sha256:4d6a135ebbdfdb78999ccb0e6d58774e5b2145898a0faf74ca38a0919ff07440"}, + {file = "python_semantic_release-7.31.2-py3-none-any.whl", hash = "sha256:a0c10dc5f02de270cd7efbed89a7326d40ca9024bde00f2fcf1262c979bb3dee"}, ] pywin32-ctypes = [ {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, @@ -1001,41 +1043,37 @@ pyyaml = [ {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] readme-renderer = [ - {file = "readme_renderer-32.0-py3-none-any.whl", hash = "sha256:a50a0f2123a4c1145ac6f420e1a348aafefcc9211c846e3d51df05fe3d865b7d"}, - {file = "readme_renderer-32.0.tar.gz", hash = "sha256:b512beafa6798260c7d5af3e1b1f097e58bfcd9a575da7c4ddd5e037490a5b85"}, + {file = "readme_renderer-35.0-py3-none-any.whl", hash = "sha256:73b84905d091c31f36e50b4ae05ae2acead661f6a09a9abb4df7d2ddcdb6a698"}, + {file = "readme_renderer-35.0.tar.gz", hash = "sha256:a727999acfc222fc21d82a12ed48c957c4989785e5865807c65a487d21677497"}, ] requests = [ - {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, - {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, + {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, + {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, ] requests-toolbelt = [ {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, ] rfc3986 = [ - {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, - {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, + {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, + {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, ] rich = [ - {file = "rich-10.16.1-py3-none-any.whl", hash = "sha256:bbe04dd6ac09e4b00d22cb1051aa127beaf6e16c3d8687b026e96d3fca6aad52"}, - {file = "rich-10.16.1.tar.gz", hash = "sha256:4949e73de321784ef6664ebbc854ac82b20ff60b2865097b93f3b9b41e30da27"}, + {file = "rich-12.5.1-py3-none-any.whl", hash = "sha256:2eb4e6894cde1e017976d2975ac210ef515d7548bc595ba20e195fb9628acdeb"}, + {file = "rich-12.5.1.tar.gz", hash = "sha256:63a5c5ce3673d3d5fbbf23cd87e11ab84b6b451436f1b7f19ec54b6bc36ed7ca"}, ] rsa = [ - {file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"}, - {file = "rsa-4.8.tar.gz", hash = "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17"}, + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, ] secretstorage = [ - {file = "SecretStorage-3.3.1-py3-none-any.whl", hash = "sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f"}, - {file = "SecretStorage-3.3.1.tar.gz", hash = "sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195"}, + {file = "SecretStorage-3.3.2-py3-none-any.whl", hash = "sha256:755dc845b6ad76dcbcbc07ea3da75ae54bb1ea529eb72d15f83d26499a5df319"}, + {file = "SecretStorage-3.3.2.tar.gz", hash = "sha256:0a8eb9645b320881c222e827c26f4cfcf55363e8b374a021981ef886657a912f"}, ] semver = [ {file = "semver-2.13.0-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4"}, {file = "semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"}, ] -setuptools-scm = [ - {file = "setuptools_scm-6.3.2-py3-none-any.whl", hash = "sha256:4c64444b1d49c4063ae60bfe1680f611c8b13833d556fd1d6050c0023162a119"}, - {file = "setuptools_scm-6.3.2.tar.gz", hash = "sha256:a49aa8081eeb3514eb9728fa5040f2eaa962d6c6f4ec9c32f6c1fba88f88a0f2"}, -] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -1044,39 +1082,43 @@ smmap = [ {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, ] -telethon = [ - {file = "Telethon-1.24.0-py3-none-any.whl", hash = "sha256:04fdc5fa4ed3e886e6ecf4bad79205ab8880c6aefbd42c29c89c689a502aa816"}, - {file = "Telethon-1.24.0.tar.gz", hash = "sha256:818cb61281ed3f75ba4da9b68cb69486bed9474d2db4e0aa16e482053117452c"}, +telethon-v1-24 = [ + {file = "Telethon-v1.24-1.24.2.tar.gz", hash = "sha256:709b147945d2320d6fc5ec71895d8147133c02e78f12049793984836e39334c1"}, + {file = "Telethon_v1.24-1.24.2-py3-none-any.whl", hash = "sha256:79f19b4e049148e279ac55386700ed398210e41ed366b8c966b937ffa9ae840c"}, ] tomli = [ - {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, - {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] tomlkit = [ - {file = "tomlkit-0.7.0-py2.py3-none-any.whl", hash = "sha256:6babbd33b17d5c9691896b0e68159215a9387ebfa938aa3ac42f4a4beeb2b831"}, - {file = "tomlkit-0.7.0.tar.gz", hash = "sha256:ac57f29693fab3e309ea789252fcce3061e19110085aa31af5446ca749325618"}, + {file = "tomlkit-0.10.2-py3-none-any.whl", hash = "sha256:905cf92c2111ef80d355708f47ac24ad1b6fc2adc5107455940088c9bbecaedb"}, + {file = "tomlkit-0.10.2.tar.gz", hash = "sha256:30d54c0b914e595f3d10a87888599eab5321a2a69abc773bbefff51599b72db6"}, ] tqdm = [ - {file = "tqdm-4.62.3-py2.py3-none-any.whl", hash = "sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c"}, - {file = "tqdm-4.62.3.tar.gz", hash = "sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d"}, + {file = "tqdm-4.64.0-py2.py3-none-any.whl", hash = "sha256:74a2cdefe14d11442cedf3ba4e21a3b84ff9a2dbdc6cfae2c34addb2a14a5ea6"}, + {file = "tqdm-4.64.0.tar.gz", hash = "sha256:40be55d30e200777a307a7585aee69e4eabb46b4ec6a4b4a5f2d9f11e7d5408d"}, ] twine = [ - {file = "twine-3.7.1-py3-none-any.whl", hash = "sha256:8c120845fc05270f9ee3e9d7ebbed29ea840e41f48cd059e04733f7e1d401345"}, - {file = "twine-3.7.1.tar.gz", hash = "sha256:28460a3db6b4532bde6a5db6755cf2dce6c5020bada8a641bb2c5c7a9b1f35b8"}, + {file = "twine-3.8.0-py3-none-any.whl", hash = "sha256:d0550fca9dc19f3d5e8eadfce0c227294df0a2a951251a4385797c8a6198b7c8"}, + {file = "twine-3.8.0.tar.gz", hash = "sha256:8efa52658e0ae770686a13b675569328f1fba9837e5de1867bfe5f46a9aefe19"}, ] typing-extensions = [ - {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, - {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, + {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, + {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, ] urllib3 = [ - {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, - {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, + {file = "urllib3-1.26.11-py2.py3-none-any.whl", hash = "sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc"}, + {file = "urllib3-1.26.11.tar.gz", hash = "sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a"}, ] webencodings = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] +wheel = [ + {file = "wheel-0.37.1-py2.py3-none-any.whl", hash = "sha256:4bdcd7d840138086126cd09254dc6195fb4fc6f01c050a1d7236f2630db1d22a"}, + {file = "wheel-0.37.1.tar.gz", hash = "sha256:e9a504e793efbca1b8e0e9cb979a249cf4a0a7b5b8c9e8b65a5e39d49529c1c4"}, +] zipp = [ - {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, - {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, + {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"}, + {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"}, ] diff --git a/pyproject.toml b/pyproject.toml index f37aa3b..8ca7a15 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,13 +16,13 @@ tgpy = 'tgpy.main:main' [tool.semantic_release] version_variable = [ - "tgpy/__init__.py:__version__", + "tgpy/version.py:__version__", "pyproject.toml:version" ] branch = "master" upload_to_repository = true upload_to_release = true -build_command = "poetry build" +build_command = """sed -i "s/\\(IS_DEV_BUILD *= *\\).*/\\1False/" tgpy/version.py && poetry build""" commit_subject = 'chore(release): v{version} [skip ci]' commit_message = '' @@ -36,17 +36,17 @@ src_paths = ["tgpy"] [tool.poetry.dependencies] python = "^3.9" -Telethon = "^1.24.0" -cryptg = "^0.2.post4" +cryptg = "^0.3.1" PyYAML = "^6.0" -pydantic = "^1.8.2" -aiorun = "^2021.10.1" -rich = "^10.16.1" +pydantic = "^1.9.1" +aiorun = "^2022.4.1" +rich = "^12.5.1" appdirs = "^1.4.4" +telethon-v1-24 = "^1.24.2" [tool.poetry.dev-dependencies] -python-semantic-release = "^7.23.0" -black = "^21.12b0" +python-semantic-release = "^7.31.2" +black = "^22.6.0" isort = "^5.10.1" [build-system] diff --git a/readme_assets/2plus2.png b/readme_assets/2plus2.png deleted file mode 100644 index 74ea3dc..0000000 Binary files a/readme_assets/2plus2.png and /dev/null differ diff --git a/readme_assets/TGPy.png b/readme_assets/TGPy.png deleted file mode 100644 index a9c5dc9..0000000 Binary files a/readme_assets/TGPy.png and /dev/null differ diff --git a/readme_assets/auto_await.png b/readme_assets/auto_await.png deleted file mode 100644 index f4691c6..0000000 Binary files a/readme_assets/auto_await.png and /dev/null differ diff --git a/readme_assets/example.gif b/readme_assets/example.gif deleted file mode 100644 index f4a5c4d..0000000 Binary files a/readme_assets/example.gif and /dev/null differ diff --git a/readme_assets/multiple_return.png b/readme_assets/multiple_return.png deleted file mode 100644 index 4ce18c3..0000000 Binary files a/readme_assets/multiple_return.png and /dev/null differ diff --git a/readme_assets/saving_variables.png b/readme_assets/saving_variables.png deleted file mode 100644 index 33b6ab5..0000000 Binary files a/readme_assets/saving_variables.png and /dev/null differ diff --git a/readme_assets/send_hello_world.png b/readme_assets/send_hello_world.png deleted file mode 100644 index cc56842..0000000 Binary files a/readme_assets/send_hello_world.png and /dev/null differ diff --git a/tgpy/__init__.py b/tgpy/__init__.py index 7bdfdda..72f7ae2 100644 --- a/tgpy/__init__.py +++ b/tgpy/__init__.py @@ -7,12 +7,12 @@ from tgpy.app_config import Config from tgpy.console import console from tgpy.context import Context - -__version__ = "0.4.1" +from tgpy.version import __version__ logging.basicConfig( level=logging.INFO, format='%(message)s', datefmt="[%X]", handlers=[RichHandler()] ) +logging.getLogger('telethon').setLevel(logging.WARNING) class App: diff --git a/tgpy/api.py b/tgpy/api.py index d71bec4..30d3fe3 100644 --- a/tgpy/api.py +++ b/tgpy/api.py @@ -1,4 +1,3 @@ -import logging from typing import Any, Callable @@ -15,18 +14,5 @@ def __init__(self): def add_code_transformer(self, name: str, transformer: Callable[[str], str]): self.code_transformers.append((name, transformer)) - def _apply_code_transformers(self, code: str) -> str: - for _, transformer in self.code_transformers: - try: - code = transformer(code) - except Exception: - logger = logging.getLogger(__name__) - logger.exception( - f'Error while applying code transformer {transformer}', - exc_info=True, - ) - raise - return code - api = API() diff --git a/tgpy/builtin_functions.py b/tgpy/builtin_functions.py index 05e242a..a862c35 100644 --- a/tgpy/builtin_functions.py +++ b/tgpy/builtin_functions.py @@ -1,6 +1,4 @@ -import getpass import os -import socket import sys from datetime import datetime from textwrap import dedent @@ -9,7 +7,7 @@ from telethon.tl.custom import Message from tgpy import app -from tgpy.message_design import get_code +from tgpy.message_design import parse_message from tgpy.modules import ( Module, delete_module_file, @@ -17,11 +15,16 @@ get_sorted_modules, ) from tgpy.utils import ( + REPO_ROOT, RunCmdException, + execute_in_repo_root, filename_prefix, + get_hostname, + get_user, get_version, installed_as_package, run_cmd, + running_in_docker, ) @@ -29,7 +32,7 @@ def ping(): return dedent( f''' Pong! - Running on {getpass.getuser()}@{socket.gethostname()} + Running on {get_user()}@{get_hostname()} Version: {get_version()} ''' ) @@ -57,14 +60,25 @@ def restart(msg: Optional[str] = 'Restarted successfully'): def update(): old_version = get_version() + + if running_in_docker(): + return 'Can\'t update a docker container' + if installed_as_package(): update_args = [sys.executable, '-m', 'pip', 'install', '-U', 'tgpy'] try: run_cmd(update_args) except RunCmdException: run_cmd(update_args + ['--user']) + elif REPO_ROOT: + with execute_in_repo_root(): + try: + run_cmd(['git', 'pull']) + except FileNotFoundError: + return 'Git is not installed' else: - run_cmd(['git', 'pull']) + return 'Could not find suitable update method' + new_version = get_version() if old_version == new_version: return 'Already up to date' @@ -80,9 +94,10 @@ async def add(self, name: str, code: Optional[str] = None) -> str: original: Message = await app.ctx.msg.get_reply_message() if original is None: return 'Use this function in reply to a message' - code = get_code(original) - if not code: + message_data = parse_message(original) + if not message_data.is_tgpy_message: return 'No code found in reply message' + code = message_data.code origin = f'{filename_prefix}module/{name}' diff --git a/tgpy/handlers/__init__.py b/tgpy/handlers/__init__.py index 657c031..ce9c117 100644 --- a/tgpy/handlers/__init__.py +++ b/tgpy/handlers/__init__.py @@ -2,48 +2,79 @@ from telethon.tl.custom import Message from telethon.tl.types import Channel -from tgpy import app, message_design +from tgpy import app, message_design, reactions_fix from tgpy.handlers.utils import _handle_errors, outgoing_messages_filter +from tgpy.reactions_fix import ReactionsFixResult from tgpy.run_code import eval_message, get_variable_names, parse_code -async def handle_message(message: Message) -> None: - raw_text = message.raw_text - - if not raw_text: - return +async def handle_message( + message: Message, *, only_show_warning: bool = False +) -> Message: + if not message.raw_text: + return message if message.text.startswith('//') and message.text[2:].strip(): - await message.edit(message.text[2:]) - return + return await message.edit(message.text[2:]) locals_ = get_variable_names() - res = parse_code(raw_text, locals_) + res = parse_code(message.raw_text, locals_) if not res.is_code: - return - - await eval_message(raw_text, message, uses_orig=res.uses_orig) + return message + + if only_show_warning: + return await message_design.edit_message( + message, + message.raw_text, + 'Edit message again to evaluate', + ) + else: + return await eval_message(message.raw_text, message, res.uses_orig) @events.register(events.NewMessage(func=outgoing_messages_filter)) @_handle_errors async def on_new_message(event: events.NewMessage.Event) -> None: - await handle_message(event.message) + message = await handle_message(event.message) + reactions_fix.update_hash(message) @events.register(events.MessageEdited(func=outgoing_messages_filter)) @_handle_errors -async def on_message_edited(event: events.NewMessage.Event) -> None: - if isinstance(event.message.chat, Channel) and event.message.chat.broadcast: - return - code = message_design.get_code(event.message) - if not code: - await handle_message(event.message) +async def on_message_edited(event: events.MessageEdited.Event) -> None: + message: Message = event.message + if isinstance(message.chat, Channel) and message.chat.broadcast: return - await eval_message( - code, event.message, uses_orig=parse_code(code, get_variable_names()).uses_orig - ) + message_data = message_design.parse_message(message) + reactions_fix_result = reactions_fix.check_hash(message) + try: + match reactions_fix_result: + case ReactionsFixResult.ignore: + return + case ReactionsFixResult.show_warning: + if message_data.is_tgpy_message: + message = await message_design.edit_message( + message, message_data.code, 'Edit message again to evaluate' + ) + else: + message = await handle_message(message, only_show_warning=True) + return + case ReactionsFixResult.evaluate: + pass + case _: + raise ValueError(f'Bad reactions fix result: {reactions_fix_result}') + + if not message_data.is_tgpy_message: + message = await handle_message(message) + return + message = await eval_message( + message_data.code, + message, + parse_code(message_data.code, get_variable_names()).uses_orig, + ) + finally: + reactions_fix.update_hash(message) @events.register( @@ -55,14 +86,14 @@ async def cancel(message: Message): async for msg in app.client.iter_messages( message.chat_id, max_id=message.id, limit=10 ): - if msg.out and message_design.get_code(msg): + if msg.out and message_design.parse_message(msg).is_tgpy_message: prev = msg break else: return # noinspection PyBroadException try: - await prev.edit(message_design.get_code(prev)) + await prev.edit(message_design.parse_message(prev).code) except Exception: pass else: diff --git a/tgpy/message_design.py b/tgpy/message_design.py index 4f1d24e..8db597d 100644 --- a/tgpy/message_design.py +++ b/tgpy/message_design.py @@ -1,5 +1,6 @@ import sys import traceback as tb +from dataclasses import dataclass from telethon.tl.custom import Message from telethon.tl.types import MessageEntityBold, MessageEntityCode, MessageEntityTextUrl @@ -11,16 +12,47 @@ FORMATTED_ERROR_HEADER = f'TGPy error>' +class Utf16CodepointsWrapper(str): + def __len__(self): + return len(self.encode('utf-16-le')) // 2 + + def __getitem__(self, item): + s = self.encode('utf-16-le') + if isinstance(item, slice): + item = slice( + item.start * 2 if item.start else None, + item.stop * 2 if item.stop else None, + item.step * 2 if item.step else None, + ) + s = s[item] + elif isinstance(item, int): + s = s[item * 2 : item * 2 + 2] + else: + raise TypeError(f'{type(item)} is not supported') + return s.decode('utf-16-le') + + async def edit_message( - message: Message, code: str, result, traceback: str = '', output: str = '' -) -> None: + message: Message, + code: str, + result: str, + traceback: str = '', + output: str = '', +) -> Message: if result is None and output: result = output output = '' - parts = [code.strip(), f'{TITLE} {str(result).strip()}'] - parts += [part for part in (output.strip(), traceback.strip()) if part] - text = '\n\n'.join(parts) + title = Utf16CodepointsWrapper(TITLE) + parts = [ + Utf16CodepointsWrapper(code.strip()), + Utf16CodepointsWrapper(f'{title} {str(result).strip()}'), + ] + parts += [ + Utf16CodepointsWrapper(part) + for part in (output.strip(), traceback.strip()) + if part + ] entities = [] offset = 0 @@ -28,23 +60,48 @@ async def edit_message( entities.append(MessageEntityCode(offset, len(p))) offset += len(p) + 2 - entities[1].offset += len(TITLE) + 1 - entities[1].length -= len(TITLE) + 1 + entities[1].offset += len(title) + 1 + entities[1].length -= len(title) + 1 entities[1:1] = [ - MessageEntityBold(len(parts[0]) + 2, len(TITLE)), - MessageEntityTextUrl(len(parts[0]) + 2, len(TITLE), TITLE_URL), + MessageEntityBold( + len(parts[0]) + 2, + len(title), + ), + MessageEntityTextUrl( + len(parts[0]) + 2, + len(title), + TITLE_URL, + ), ] + text = str('\n\n'.join(parts)) if len(text) > 4096: text = text[:4095] + '…' - await message.edit(text, formatting_entities=entities, link_preview=False) + return await message.edit(text, formatting_entities=entities, link_preview=False) + +@dataclass +class MessageParseResult: + is_tgpy_message: bool + code: str | None + result: str | None -def get_code(message: Message) -> str: + +def get_title_entity(message: Message) -> MessageEntityTextUrl | None: for e in message.entities or []: if isinstance(e, MessageEntityTextUrl) and e.url == TITLE_URL: - return message.raw_text[: e.offset].strip() - return '' + return e + return None + + +def parse_message(message: Message) -> MessageParseResult: + e = get_title_entity(message) + if not e: + return MessageParseResult(False, None, None) + msg_text = Utf16CodepointsWrapper(message.raw_text) + code = msg_text[: e.offset].strip() + result = msg_text[e.offset + e.length :].strip() + return MessageParseResult(True, code, result) async def send_error(chat) -> None: @@ -54,3 +111,11 @@ async def send_error(chat) -> None: await app.client.send_message( chat, f'{FORMATTED_ERROR_HEADER}\n\n{exc}', link_preview=False ) + + +__all__ = [ + 'edit_message', + 'MessageParseResult', + 'parse_message', + 'send_error', +] diff --git a/tgpy/modules.py b/tgpy/modules.py index 071af96..cd52b9e 100644 --- a/tgpy/modules.py +++ b/tgpy/modules.py @@ -66,7 +66,7 @@ async def run_modules(): continue -DOCSTRING_RGX = re.compile(r'^\s*(?:\'\'\'(.*)\'\'\'|"""(.*)""")', re.DOTALL) +DOCSTRING_RGX = re.compile(r'^\s*(?:\'\'\'(.*?)\'\'\'|"""(.*?)""")', re.DOTALL) MODULE_TEMPLATE = ''' """ {metadata} diff --git a/tgpy/reactions_fix.py b/tgpy/reactions_fix.py new file mode 100644 index 0000000..0522403 --- /dev/null +++ b/tgpy/reactions_fix.py @@ -0,0 +1,43 @@ +""" +This module tries to fix Telegram bug/undocumented feature where +setting/removing reaction sometimes triggers message edit event. +This bug/feature introduces a security vulnerability in TGPy, +because message reevaluation can be triggered by other users. +""" +import json +from enum import Enum +from hashlib import sha256 + +from telethon.tl.custom import Message + +content_hashes: dict[tuple[int, int], bytes] = {} + + +def get_content_hash(message: Message) -> bytes: + entities = [json.dumps(e.to_dict()) for e in message.entities or []] + data = str(len(entities)) + '\n' + '\n'.join(entities) + message.raw_text + return sha256(data.encode('utf-8')).digest() + + +class ReactionsFixResult(Enum): + ignore = 1 + evaluate = 2 + show_warning = 3 + + +def check_hash(message: Message) -> ReactionsFixResult: + message_uid = (message.chat_id, message.id) + content_hash = get_content_hash(message) + if message_uid not in content_hashes: + return ReactionsFixResult.show_warning + if content_hashes[message_uid] == content_hash: + return ReactionsFixResult.ignore + return ReactionsFixResult.evaluate + + +def update_hash(message: Message) -> None: + message_uid = (message.chat_id, message.id) + content_hashes[message_uid] = get_content_hash(message) + + +__all__ = ['ReactionsFixResult', 'check_hash', 'update_hash'] diff --git a/tgpy/run_code/__init__.py b/tgpy/run_code/__init__.py index 6fab24a..613fbc9 100644 --- a/tgpy/run_code/__init__.py +++ b/tgpy/run_code/__init__.py @@ -1,4 +1,3 @@ -from telethon.errors import MessageIdInvalidError from telethon.tl.custom import Message from tgpy import app, message_design @@ -17,7 +16,7 @@ def get_variable_names(include_orig=True): # fmt: on -async def eval_message(code: str, message: Message, uses_orig=False) -> None: +async def eval_message(code: str, message: Message, uses_orig: bool) -> Message: await message_design.edit_message(message, code, 'Running...') app.ctx.msg = message @@ -48,10 +47,11 @@ async def eval_message(code: str, message: Message, uses_orig=False) -> None: result = convert_result(result) exc = '' - try: - # noinspection PyProtectedMember - await message_design.edit_message( - message, code, result, traceback=exc, output=app.ctx._print_output - ) - except MessageIdInvalidError: - pass + # noinspection PyProtectedMember + return await message_design.edit_message( + message, + code, + result, + traceback=exc, + output=app.ctx._print_output, + ) diff --git a/tgpy/run_code/autoawait.py b/tgpy/run_code/autoawait.py new file mode 100644 index 0000000..6ee6416 --- /dev/null +++ b/tgpy/run_code/autoawait.py @@ -0,0 +1,43 @@ +import ast +import tokenize +from io import BytesIO + +AWAIT_REPLACEMENT_ATTRIBUTE = '__tgpy_await__' + + +def tokenize_string(s: str) -> list[tokenize.TokenInfo]: + return list(tokenize.tokenize(BytesIO(s.encode('utf-8')).readline)) + + +def untokenize_to_string(tokens: list[tokenize.TokenInfo]) -> str: + return tokenize.untokenize(tokens).decode('utf-8') + + +def pre_transform(code: str) -> str: + tokens = tokenize_string(code) + for i, tok in enumerate(tokens): + if i == 0: + continue + prev_tok = tokens[i - 1] + if ( + tok.type == tokenize.NAME + and tok.string == 'await' + and prev_tok.type == tokenize.OP + and prev_tok.string == '.' + ): + tokens[i] = tok._replace(string=AWAIT_REPLACEMENT_ATTRIBUTE) + return untokenize_to_string(tokens) + + +class AwaitTransformer(ast.NodeTransformer): + def visit_Attribute(self, node: ast.Attribute): + node = self.generic_visit(node) + if node.attr == AWAIT_REPLACEMENT_ATTRIBUTE: + return ast.Await(value=node.value) + else: + return node + + +transformer = AwaitTransformer() + +__all__ = ['pre_transform', 'transformer'] diff --git a/tgpy/run_code/meval.py b/tgpy/run_code/meval.py index 18c8d1f..81413c5 100644 --- a/tgpy/run_code/meval.py +++ b/tgpy/run_code/meval.py @@ -9,6 +9,8 @@ from typing import Any, Iterator from tgpy import app +from tgpy.run_code import autoawait +from tgpy.run_code.utils import apply_code_transformers def shallow_walk(node) -> Iterator: @@ -65,9 +67,9 @@ async def meval( # Copy data to args we are sending kwargs[global_args][glob] = globs[glob] - # noinspection PyProtectedMember - str_code = app.api._apply_code_transformers(str_code) + str_code = apply_code_transformers(app, str_code) root = ast.parse(str_code, '', 'exec') + autoawait.transformer.visit(root) ret_name = '_ret' ok = False diff --git a/tgpy/run_code/parse_code.py b/tgpy/run_code/parse_code.py index 6d333ad..9d1f117 100644 --- a/tgpy/run_code/parse_code.py +++ b/tgpy/run_code/parse_code.py @@ -1,6 +1,7 @@ import ast from tgpy import app +from tgpy.run_code.utils import apply_code_transformers class _Result: @@ -23,10 +24,10 @@ def _is_node_suspicious_binop(node: ast.AST, locs: dict) -> bool: if not isinstance(node, (ast.BoolOp, ast.BinOp, ast.Compare)): return False if isinstance(node, ast.Compare): - return _is_node_unknown_variable(node.left, locs) and all( + return _is_node_unknown_variable(node.left, locs) or any( _is_node_unknown_variable(x, locs) for x in node.comparators ) - return all( + return any( _is_node_suspicious_binop(operand, locs) for operand in ( (node.left, node.right) if isinstance(node, ast.BinOp) else node.values @@ -61,16 +62,16 @@ def _ignore_node(node: ast.AST, locs: dict) -> bool: or _is_node_suspicious_binop(node, locs) # Messages like "yes, understood" or isinstance(node, ast.Tuple) - and all(_ignore_node_simple(elt, locs) for elt in node.elts) + and any(_ignore_node(elt, locs) for elt in node.elts) # Messages like "cat (no)" or isinstance(node, ast.Call) - and _ignore_node_simple(node.func, locs) - and all(_ignore_node_simple(arg, locs) for arg in node.args) + and _ignore_node(node.func, locs) + and any(_ignore_node(arg, locs) for arg in node.args) # Messages like "fix: fix" or isinstance(node, ast.AnnAssign) and node.value is None - and _ignore_node_simple(node.target, locs) - and _ignore_node_simple(node.annotation, locs) + and _ignore_node(node.target, locs) + and _ignore_node(node.annotation, locs) ) @@ -78,8 +79,7 @@ def parse_code(text: str, locs: dict) -> _Result: """Parse given text and decide should it be evaluated as Python code""" result = _Result() - # noinspection PyProtectedMember - text = app.api._apply_code_transformers(text) + text = apply_code_transformers(app, text) try: root = ast.parse(text, '', 'exec') diff --git a/tgpy/run_code/utils.py b/tgpy/run_code/utils.py index 7e2b7f8..a7938c6 100644 --- a/tgpy/run_code/utils.py +++ b/tgpy/run_code/utils.py @@ -1,8 +1,13 @@ +import logging import sys +import tokenize import traceback from telethon.tl import TLObject +from tgpy import App +from tgpy.run_code import autoawait + def convert_result(result): if isinstance(result, TLObject): @@ -15,3 +20,21 @@ def format_traceback(): exc_type, exc_value, exc_traceback = sys.exc_info() exc_traceback = exc_traceback.tb_next.tb_next return traceback.format_exception(exc_type, exc_value, exc_traceback) + + +def apply_code_transformers(app: App, code: str) -> str: + for _, transformer in app.api.code_transformers: + try: + code = transformer(code) + except Exception: + logger = logging.getLogger(__name__) + logger.exception( + f'Error while applying code transformer {transformer}', + exc_info=True, + ) + raise + try: + code = autoawait.pre_transform(code) + except tokenize.TokenError: + pass + return code diff --git a/tgpy/utils.py b/tgpy/utils.py index 7d9683f..6bd9e77 100644 --- a/tgpy/utils.py +++ b/tgpy/utils.py @@ -1,20 +1,47 @@ +import getpass import importlib.metadata +import os +import re import shlex +import socket +from contextlib import contextmanager from pathlib import Path from subprocess import PIPE, Popen import appdirs +from telethon.tl import types -# noinspection PyTypeChecker -DATA_DIR = Path(appdirs.user_config_dir('tgpy', appauthor=False)) +from tgpy import version + +ENV_TGPY_DATA = os.getenv('TGPY_DATA') +if ENV_TGPY_DATA: + DATA_DIR = Path(os.getenv('TGPY_DATA')) +else: + # noinspection PyTypeChecker + DATA_DIR = Path(appdirs.user_config_dir('tgpy', appauthor=False)) MODULES_DIR = DATA_DIR / 'modules' WORKDIR = DATA_DIR / 'workdir' CONFIG_FILENAME = DATA_DIR / 'config.yml' SESSION_FILENAME = DATA_DIR / 'TGPy.session' +REPO_ROOT = Path(__file__).parent.parent +if not os.path.exists(REPO_ROOT / '.git'): + REPO_ROOT = None filename_prefix = 'tgpy://' +@contextmanager +def execute_in_repo_root(): + if not REPO_ROOT: + raise ValueError('No repository found') + old_cwd = os.getcwd() + os.chdir(REPO_ROOT) + try: + yield + finally: + os.chdir(old_cwd) + + def create_config_dirs(): DATA_DIR.mkdir(exist_ok=True) MODULES_DIR.mkdir(exist_ok=True) @@ -45,28 +72,71 @@ def installed_as_package(): return False -def get_version(): - if installed_as_package(): - return importlib.metadata.version('tgpy') +def running_in_docker(): + return os.path.exists('/.dockerenv') + +def get_user(): try: - return 'git@' + run_cmd(['git', 'rev-parse', '--short', 'HEAD']) - except RunCmdException: - pass + return getpass.getuser() + except KeyError: + return str(os.getuid()) + + +DOCKER_DEFAULT_HOSTNAME_RGX = re.compile(r'[0-9a-f]{12}') + + +def get_hostname(): + real_hostname = socket.gethostname() + if running_in_docker() and DOCKER_DEFAULT_HOSTNAME_RGX.fullmatch(real_hostname): + return 'docker' + return real_hostname + + +def get_version(): + if not version.IS_DEV_BUILD: + return version.__version__ + + if REPO_ROOT: + with execute_in_repo_root(): + try: + return 'git@' + run_cmd(['git', 'rev-parse', '--short', 'HEAD']) + except (RunCmdException, FileNotFoundError): + pass + + if version.COMMIT_HASH: + return 'git@' + version.COMMIT_HASH[:7] return 'unknown' +def peer_to_id(peer: types.TypePeer): + if isinstance(peer, types.PeerUser): + return peer.user_id + elif isinstance(peer, types.PeerChat): + return peer.chat_id + elif isinstance(peer, types.PeerChannel): + return peer.channel_id + else: + raise TypeError(f'Unknown peer type: {type(peer)}') + + __all__ = [ 'DATA_DIR', 'MODULES_DIR', 'WORKDIR', 'CONFIG_FILENAME', 'SESSION_FILENAME', + 'REPO_ROOT', 'run_cmd', 'get_version', 'create_config_dirs', 'installed_as_package', 'RunCmdException', 'filename_prefix', + 'execute_in_repo_root', + 'get_user', + 'get_hostname', + 'running_in_docker', + 'peer_to_id', ] diff --git a/tgpy/version.py b/tgpy/version.py new file mode 100644 index 0000000..6d780d9 --- /dev/null +++ b/tgpy/version.py @@ -0,0 +1,3 @@ +__version__ = '0.4.1' +IS_DEV_BUILD = True +COMMIT_HASH = None