├── inviter ├── __init__.py ├── utils.py ├── config.py ├── ldap.py ├── matrix_utils.py └── bot.py ├── renovate.json ├── maubot.yaml ├── Pipfile ├── LICENSE ├── base-config.yaml ├── README.md ├── .gitignore └── Pipfile.lock /inviter/__init__.py: -------------------------------------------------------------------------------- 1 | from .bot import LDAPInviterBot 2 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base", 4 | ":dependencyDashboard", 5 | ":maintainLockFilesWeekly", 6 | ":automergePatch" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /maubot.yaml: -------------------------------------------------------------------------------- 1 | maubot: 0.1.0 2 | id: de.herrmehren.ldap-inviter 3 | version: 0.0.1 4 | license: MIT 5 | modules: 6 | - inviter 7 | main_class: LDAPInviterBot 8 | config: true 9 | extra_files: 10 | - base-config.yaml -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | maubot = "0.1.2" 8 | mautrix = "0.9.10" 9 | python-ldap = "3.3.1" 10 | 11 | [dev-packages] 12 | black = {version = "21.7b0", extras = ["d"]} 13 | pylint = "2.9.6" 14 | 15 | [requires] 16 | python_version = "3.9" 17 | 18 | [pipenv] 19 | allow_prereleases = true 20 | -------------------------------------------------------------------------------- /inviter/utils.py: -------------------------------------------------------------------------------- 1 | from inviter.config import MemberConfig 2 | from inviter.matrix_utils import UserInfoMap, UserInfo 3 | 4 | 5 | def process_template(template: str, arg1: str) -> str: 6 | """Replaces placeholder in a template with argument""" 7 | if "<1>" in template and arg1 == "": 8 | raise Exception( 9 | f'Template string "{template}" includes a placeholder, but no argument was provided' 10 | ) 11 | return template.replace("<1>", arg1) 12 | 13 | 14 | def to_user_info_map(member_config: [MemberConfig]) -> UserInfoMap: 15 | user_info_map = {} 16 | user: dict 17 | for user in member_config: 18 | user_info_map[user["mxid"]] = UserInfo(power_level=user.get("power_level", 0)) 19 | return user_info_map 20 | -------------------------------------------------------------------------------- /inviter/config.py: -------------------------------------------------------------------------------- 1 | from typing import TypedDict, Optional, List 2 | 3 | from mautrix.util.config import BaseProxyConfig, ConfigUpdateHelper 4 | 5 | 6 | class LDAPMemberConfig(TypedDict): 7 | ldap_group: str 8 | power_level: Optional[int] 9 | 10 | 11 | class MemberConfig(TypedDict): 12 | mxid: str 13 | power_level: Optional[int] 14 | 15 | 16 | class SyncRoomConfig(TypedDict): 17 | alias: str 18 | visibility: str 19 | name: str 20 | ldap_members: List[LDAPMemberConfig] 21 | members: List[MemberConfig] 22 | 23 | 24 | class LDAPConfig(TypedDict): 25 | uri: str 26 | base_dn: str 27 | connect_dn: str 28 | connect_password: str 29 | user_filter: str 30 | mxid_homeserver: str 31 | 32 | 33 | class LDAPInviterConfig(BaseProxyConfig): 34 | sync_rooms: List[SyncRoomConfig] 35 | admin_users: List[str] 36 | ldap: LDAPConfig 37 | 38 | def do_update(self, helper: ConfigUpdateHelper) -> None: 39 | helper.copy("sync_rooms") 40 | helper.copy("admin_users") 41 | helper.copy("ldap") 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 David Mehren 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /base-config.yaml: -------------------------------------------------------------------------------- 1 | # LDAP config 2 | ldap: 3 | uri: 'ldap://foo.bar.tld:389' # URI of your LDAP server 4 | base_dn: 'cn=users,dc=foo,dc=bar,dc=tld' # base-DN of your user objects 5 | connect_dn: 'uid=ldap-bot,cn=users,dc=foo,dc=bar,dc=tld' # DN of the user used to bind 6 | connect_password: 'verySecure' # password of the user used to bind 7 | user_filter: '(objectClass=inetOrgPerson)' 8 | mxid_homeserver: 'matrix.server.tld' # Homeserver used to generate MXIDs from LDAP uids 9 | 10 | # Rooms that should be synced 11 | sync_rooms: 12 | - alias: '#event-<1>-group-1:matrix.server.tld' # Aliases can include '<1>' placeholders 13 | # Should the room be visible in the room list? 14 | # Can be 'private' or 'public' 15 | visibility: 'private' 16 | # Names can include '<1>' placeholders 17 | name: 'Foo <1>' 18 | # LDAP members for this room 19 | ldap_members: 20 | - ldap_group: 'cn=event-<1>-group1,cn=groups,dc=foo,dc=bar,dc=tld' 21 | power_level: 0 22 | # Groups can include '<1>' placeholders 23 | - ldap_group: 'cn=event-<1>-tutors1,cn=groups,dc=foo,dc=bar,dc=tld' 24 | power_level: 100 25 | # Hardcoded members for this room 26 | members: 27 | - mxid: '@super.admin:matrix.server.tld' 28 | power_level: 100 29 | 30 | # Users that are allowed to run a sync 31 | admin_users: 32 | - '@super.admin:matrix.server.tld' -------------------------------------------------------------------------------- /inviter/ldap.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | import ldap 4 | from mautrix.util.logging import TraceLogger 5 | 6 | from .config import LDAPMemberConfig 7 | from .matrix_utils import UserInfoMap, UserInfo 8 | from .utils import process_template 9 | 10 | 11 | class LDAPManager: 12 | connection = None 13 | base_dn = None 14 | user_filter = None 15 | mxid_default_homeserver = None 16 | 17 | def __init__( 18 | self, 19 | server_uri: str, 20 | user_dn: str, 21 | user_pass: str, 22 | base_dn: str, 23 | user_filter: str, 24 | default_homeserver: str, 25 | logger: TraceLogger, 26 | ): 27 | self.logger = logger 28 | # Create LDAP connection 29 | self.connection = ldap.initialize(server_uri) 30 | self.connection.simple_bind_s( 31 | user_dn, 32 | user_pass, 33 | ) 34 | self.base_dn = base_dn 35 | self.user_filter = user_filter 36 | self.mxid_default_homeserver = default_homeserver 37 | 38 | async def get_matrix_users_of_ldap_group( 39 | self, 40 | ldap_group: str, 41 | power_level: int, 42 | ) -> UserInfoMap: 43 | self.logger.debug(f"Getting users for LDAP group `{ldap_group}`") 44 | # Piece together LDAP filter from config and memberOf statement 45 | ldap_filter = f"(&{self.user_filter}(memberOf={ldap_group}))" 46 | # Search for group members in LDAP 47 | group_members = self.connection.search_s( 48 | self.base_dn, ldap.SCOPE_SUBTREE, ldap_filter, ["uid"] 49 | ) 50 | # UTF-8-decode uids and create MXIDs 51 | mxids = map( 52 | lambda member: f'@{member[1]["uid"][0].decode("utf-8")}:{self.mxid_default_homeserver}', 53 | group_members, 54 | ) 55 | # Build UserInfoMap from MXIDs and power level 56 | user_map = {} 57 | for mxid in mxids: 58 | user_map[mxid] = UserInfo(power_level=power_level) 59 | return user_map 60 | 61 | async def get_all_matrix_users_of_sync_room( 62 | self, ldap_members: List[LDAPMemberConfig], template_arg: str 63 | ) -> UserInfoMap: 64 | user_info_map = {} 65 | for member_config in ldap_members: 66 | user_info_map.update( 67 | await self.get_matrix_users_of_ldap_group( 68 | process_template(member_config["ldap_group"], template_arg), 69 | member_config["power_level"], 70 | ) 71 | ) 72 | return user_info_map 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LDAP Inviter Bot 2 | 3 | This is a [maubot](https://github.com/maubot/maubot) plugin that invites users to Matrix rooms 4 | according to their membership in LDAP groups. 5 | It was built in an educational context, where groups of students work on software projects. 6 | The bot ensures that participating students are invited to all rooms 7 | (general chat, announcement-only, helpdesk & their group chat) and that tutors have correct power levels in the corresponding rooms. 8 | 9 | **Features:** 10 | - Ensure that a room with the configured alias exists and has the correct name 11 | - Invite users from LDAP and from the config and give them the configured power levels 12 | - Set the room visibility 13 | - Room aliases, room names and LDAP DNs are templateable. 14 | - Matrix IDs of LDAP users are generated using the `uid` attribute from LDAP and a configurable homeserver. 15 | - The bot does not remove or uninvite users from rooms. This is intentional, to allow students to join with their own Matrix accounts. 16 | 17 | ## Notes 18 | ### Dependencies 19 | This Bot requires the `python-ldap` library. 20 | It must be installed manually in the python environment used by your Maubot instance. 21 | If you run Maubot via the official Docker image, run `apk add py3-pyldap` in the container. 22 | 23 | ### Rate Limiting 24 | The bot will quickly run into rate limits. 25 | You can use the Synapse Admin API to remove rate limits for the bot user. 26 | See https://matrix-org.github.io/synapse/latest/admin_api/user_admin_api.html#override-ratelimiting-for-users 27 | for more details. 28 | 29 | ## Config 30 | ```yaml 31 | # LDAP config 32 | ldap: 33 | uri: 'ldap://foo.bar.tld:389' # URI of your LDAP server 34 | base_dn: 'cn=users,dc=foo,dc=bar,dc=tld' # base-DN of your user objects 35 | connect_dn: 'uid=ldap-bot,cn=users,dc=foo,dc=bar,dc=tld' # DN of the user used to bind 36 | connect_password: 'verySecure' # password of the user used to bind 37 | user_filter: '(objectClass=inetOrgPerson)' 38 | mxid_homeserver: 'matrix.server.tld' # Homeserver used to generate MXIDs from LDAP uids 39 | 40 | # Rooms that should be synced 41 | sync_rooms: 42 | - alias: '#event-<1>-group-1:matrix.server.tld' # Aliases can include '<1>' placeholders 43 | # Should the room be visible in the room list? 44 | # Can be 'private' or 'public' 45 | visibility: 'private' 46 | # Names can include '<1>' placeholders 47 | name: 'Foo <1>' 48 | # LDAP members for this room 49 | ldap_members: 50 | - ldap_group: 'cn=event-<1>-group1,cn=groups,dc=foo,dc=bar,dc=tld' 51 | power_level: 0 52 | # Groups can include '<1>' placeholders 53 | - ldap_group: 'cn=event-<1>-tutors1,cn=groups,dc=foo,dc=bar,dc=tld' 54 | power_level: 100 55 | # Hardcoded members for this room 56 | members: 57 | - mxid: '@super.admin:matrix.server.tld' 58 | power_level: 100 59 | 60 | # Users that are allowed to run a sync 61 | admin_users: 62 | - '@super.admin:matrix.server.tld' 63 | ``` 64 | 65 | ## Usage 66 | To check the connection to your LDAP server, write `!ldap-check ` in a room with the bot. 67 | It will print out the computed members for all configured rooms. 68 | If you used the `<1>` placeholder in the config file, you will need to provide a value for `arg`. 69 | 70 | To run the actual invite process, write `!ldap-sync ` in a room with the bot. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/pycharm+all,python 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=pycharm+all,python 4 | 5 | ### PyCharm+all ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # User-specific stuff 10 | .idea/**/workspace.xml 11 | .idea/**/tasks.xml 12 | .idea/**/usage.statistics.xml 13 | .idea/**/dictionaries 14 | .idea/**/shelf 15 | 16 | # AWS User-specific 17 | .idea/**/aws.xml 18 | 19 | # Generated files 20 | .idea/**/contentModel.xml 21 | 22 | # Sensitive or high-churn files 23 | .idea/**/dataSources/ 24 | .idea/**/dataSources.ids 25 | .idea/**/dataSources.local.xml 26 | .idea/**/sqlDataSources.xml 27 | .idea/**/dynamic.xml 28 | .idea/**/uiDesigner.xml 29 | .idea/**/dbnavigator.xml 30 | 31 | # Gradle 32 | .idea/**/gradle.xml 33 | .idea/**/libraries 34 | 35 | # Gradle and Maven with auto-import 36 | # When using Gradle or Maven with auto-import, you should exclude module files, 37 | # since they will be recreated, and may cause churn. Uncomment if using 38 | # auto-import. 39 | # .idea/artifacts 40 | # .idea/compiler.xml 41 | # .idea/jarRepositories.xml 42 | # .idea/modules.xml 43 | # .idea/*.iml 44 | # .idea/modules 45 | # *.iml 46 | # *.ipr 47 | 48 | # CMake 49 | cmake-build-*/ 50 | 51 | # Mongo Explorer plugin 52 | .idea/**/mongoSettings.xml 53 | 54 | # File-based project format 55 | *.iws 56 | 57 | # IntelliJ 58 | out/ 59 | 60 | # mpeltonen/sbt-idea plugin 61 | .idea_modules/ 62 | 63 | # JIRA plugin 64 | atlassian-ide-plugin.xml 65 | 66 | # Cursive Clojure plugin 67 | .idea/replstate.xml 68 | 69 | # Crashlytics plugin (for Android Studio and IntelliJ) 70 | com_crashlytics_export_strings.xml 71 | crashlytics.properties 72 | crashlytics-build.properties 73 | fabric.properties 74 | 75 | # Editor-based Rest Client 76 | .idea/httpRequests 77 | 78 | # Android studio 3.1+ serialized cache file 79 | .idea/caches/build_file_checksums.ser 80 | 81 | ### PyCharm+all Patch ### 82 | # Ignores the whole .idea folder and all .iml files 83 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 84 | 85 | .idea/ 86 | 87 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 88 | 89 | *.iml 90 | modules.xml 91 | .idea/misc.xml 92 | *.ipr 93 | 94 | # Sonarlint plugin 95 | .idea/sonarlint 96 | 97 | ### Python ### 98 | # Byte-compiled / optimized / DLL files 99 | __pycache__/ 100 | *.py[cod] 101 | *$py.class 102 | 103 | # C extensions 104 | *.so 105 | 106 | # Distribution / packaging 107 | .Python 108 | build/ 109 | develop-eggs/ 110 | dist/ 111 | downloads/ 112 | eggs/ 113 | .eggs/ 114 | lib/ 115 | lib64/ 116 | parts/ 117 | sdist/ 118 | var/ 119 | wheels/ 120 | share/python-wheels/ 121 | *.egg-info/ 122 | .installed.cfg 123 | *.egg 124 | MANIFEST 125 | 126 | # PyInstaller 127 | # Usually these files are written by a python script from a template 128 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 129 | *.manifest 130 | *.spec 131 | 132 | # Installer logs 133 | pip-log.txt 134 | pip-delete-this-directory.txt 135 | 136 | # Unit test / coverage reports 137 | htmlcov/ 138 | .tox/ 139 | .nox/ 140 | .coverage 141 | .coverage.* 142 | .cache 143 | nosetests.xml 144 | coverage.xml 145 | *.cover 146 | *.py,cover 147 | .hypothesis/ 148 | .pytest_cache/ 149 | cover/ 150 | 151 | # Translations 152 | *.mo 153 | *.pot 154 | 155 | # Django stuff: 156 | *.log 157 | local_settings.py 158 | db.sqlite3 159 | db.sqlite3-journal 160 | 161 | # Flask stuff: 162 | instance/ 163 | .webassets-cache 164 | 165 | # Scrapy stuff: 166 | .scrapy 167 | 168 | # Sphinx documentation 169 | docs/_build/ 170 | 171 | # PyBuilder 172 | .pybuilder/ 173 | target/ 174 | 175 | # Jupyter Notebook 176 | .ipynb_checkpoints 177 | 178 | # IPython 179 | profile_default/ 180 | ipython_config.py 181 | 182 | # pyenv 183 | # For a library or package, you might want to ignore these files since the code is 184 | # intended to run in multiple environments; otherwise, check them in: 185 | # .python-version 186 | 187 | # pipenv 188 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 189 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 190 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 191 | # install all needed dependencies. 192 | #Pipfile.lock 193 | 194 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 195 | __pypackages__/ 196 | 197 | # Celery stuff 198 | celerybeat-schedule 199 | celerybeat.pid 200 | 201 | # SageMath parsed files 202 | *.sage.py 203 | 204 | # Environments 205 | .env 206 | .venv 207 | env/ 208 | venv/ 209 | ENV/ 210 | env.bak/ 211 | venv.bak/ 212 | 213 | # Spyder project settings 214 | .spyderproject 215 | .spyproject 216 | 217 | # Rope project settings 218 | .ropeproject 219 | 220 | # mkdocs documentation 221 | /site 222 | 223 | # mypy 224 | .mypy_cache/ 225 | .dmypy.json 226 | dmypy.json 227 | 228 | # Pyre type checker 229 | .pyre/ 230 | 231 | # pytype static type analyzer 232 | .pytype/ 233 | 234 | # Cython debug symbols 235 | cython_debug/ 236 | 237 | # End of https://www.toptal.com/developers/gitignore/api/pycharm+all,python 238 | 239 | # Maubot data dir 240 | maubot -------------------------------------------------------------------------------- /inviter/matrix_utils.py: -------------------------------------------------------------------------------- 1 | from typing import Mapping, Optional, TypedDict 2 | 3 | from mautrix.api import HTTPAPI 4 | from mautrix.client.api.events import EventMethods 5 | from mautrix.client.api.rooms import RoomMethods 6 | from mautrix.errors import MNotFound 7 | from mautrix.types import ( 8 | RoomID, 9 | RoomDirectoryVisibility, 10 | EventType, 11 | RoomNameStateEventContent, 12 | UserID, 13 | PowerLevelStateEventContent, 14 | StateEvent, 15 | Membership, 16 | ) 17 | from mautrix.util.logging import TraceLogger 18 | 19 | 20 | class UserInfo(TypedDict): 21 | power_level: Optional[int] 22 | 23 | 24 | UserInfoMap = Mapping[str, UserInfo] 25 | 26 | 27 | class MatrixUtils: 28 | room_methods = None 29 | event_methods = None 30 | logger = None 31 | 32 | def __init__(self, mautrix_api: HTTPAPI, log: TraceLogger): 33 | self.room_methods = RoomMethods(api=mautrix_api) 34 | self.event_methods = EventMethods(api=mautrix_api) 35 | self.logger = log 36 | 37 | async def ensure_room_visibility(self, room_id: RoomID, visibility: str): 38 | self.logger.debug(f"Ensuring visibility for {room_id}...") 39 | current_visibility = await self.room_methods.get_room_directory_visibility( 40 | room_id 41 | ) 42 | if current_visibility != visibility: 43 | await self.room_methods.set_room_directory_visibility( 44 | room_id, RoomDirectoryVisibility(visibility) 45 | ) 46 | 47 | async def ensure_room_name(self, room_id: RoomID, name: str) -> None: 48 | try: 49 | current_name = ( 50 | await self.room_methods.get_state_event(room_id, EventType.ROOM_NAME) 51 | )["name"] 52 | except MNotFound: 53 | current_name = "" 54 | if not current_name == name: 55 | self.logger.debug(f"Setting name '{name}' for room {room_id}") 56 | await self.event_methods.send_state_event( 57 | room_id, EventType.ROOM_NAME, RoomNameStateEventContent(name) 58 | ) 59 | 60 | async def create_room_with_alias(self, alias: str) -> RoomID: 61 | self.logger.debug(f"Creating room {alias}...") 62 | alias_local_part = alias[1:-1].split(":")[0] 63 | new_room_id = await self.room_methods.create_room(alias_local_part) 64 | self.logger.debug(f"Created room: {new_room_id}") 65 | return new_room_id 66 | 67 | async def ensure_room_with_alias(self, alias) -> RoomID: 68 | self.logger.debug(f"Ensuring {alias} exists...") 69 | try: 70 | room = await self.room_methods.get_room_alias(alias) 71 | except MNotFound: 72 | self.logger.debug(f"Alias {alias} not found.") 73 | return await self.create_room_with_alias(alias) 74 | if room is None: 75 | raise Exception(f"Could not find nor create room for alias {alias}") 76 | else: 77 | self.logger.debug(f"Found room: {room.room_id}") 78 | return room.room_id 79 | 80 | @staticmethod 81 | def state_events_to_member_list(state_events: [StateEvent]): 82 | member_mxids = [] 83 | invite_mxids = [] 84 | member_event_type = EventType.find("m.room.member", EventType.Class.STATE) 85 | for event in state_events: 86 | if ( 87 | event.type == member_event_type 88 | and event.content.membership == Membership.JOIN 89 | ): 90 | member_mxids.append(event.state_key) 91 | if ( 92 | event.type == member_event_type 93 | and event.content.membership == Membership.INVITE 94 | ): 95 | invite_mxids.append(event.state_key) 96 | return member_mxids, invite_mxids 97 | 98 | async def ensure_room_invitees(self, room_id: RoomID, user_info_map: UserInfoMap): 99 | room_member_events = await self.event_methods.get_members(room_id) 100 | room_members, room_invitees = self.state_events_to_member_list( 101 | room_member_events 102 | ) 103 | self.logger.debug(f"Room {room_id} has members:{str(room_members)}") 104 | self.logger.debug(f"Room {room_id} has invitees:{str(room_invitees)}") 105 | for mxid in user_info_map: 106 | if mxid not in room_members and mxid not in room_invitees: 107 | self.logger.debug( 108 | f"User {mxid} not invited or in the room, inviting..." 109 | ) 110 | await self.room_methods.invite_user(room_id, mxid) 111 | self.logger.debug(f"Successfully ensured invitees for {room_id}") 112 | 113 | async def ensure_room_power_levels( 114 | self, room_id: RoomID, user_info_map: UserInfoMap 115 | ): 116 | current_state = await self.room_methods.get_state_event( 117 | room_id, EventType.ROOM_POWER_LEVELS 118 | ) 119 | current_power_levels: dict[UserID, int] = current_state["users"] 120 | self.logger.debug(f"Current power levels: {str(current_power_levels)}") 121 | for mxid in user_info_map: 122 | current_power_levels[UserID(mxid)] = user_info_map[mxid]["power_level"] 123 | await self.room_methods.send_state_event( 124 | room_id, 125 | EventType.ROOM_POWER_LEVELS, 126 | PowerLevelStateEventContent(users=current_power_levels), 127 | ) 128 | self.logger.debug(f"Successfully ensured power levels") 129 | -------------------------------------------------------------------------------- /inviter/bot.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from pprint import pformat 3 | from traceback import format_exc 4 | from typing import Type 5 | 6 | from maubot import Plugin, MessageEvent 7 | from maubot.handlers import command 8 | from mautrix.client.api.events import EventMethods 9 | from mautrix.client.api.rooms import RoomMethods 10 | from mautrix.util.config import BaseProxyConfig 11 | 12 | from .config import SyncRoomConfig, LDAPInviterConfig 13 | from .ldap import LDAPManager 14 | from .matrix_utils import MatrixUtils 15 | from .utils import process_template, to_user_info_map 16 | 17 | 18 | class LDAPInviterBot(Plugin): 19 | room_methods = None 20 | event_methods = None 21 | matrix_utils = None 22 | config: LDAPInviterConfig 23 | 24 | async def start(self) -> None: 25 | await super().start() 26 | self.config.load_and_update() 27 | self.room_methods = RoomMethods(api=self.client.api) 28 | self.event_methods = EventMethods(api=self.client.api) 29 | self.matrix_utils = MatrixUtils(self.client.api, self.log) 30 | 31 | @classmethod 32 | def get_config_class(cls) -> Type[BaseProxyConfig]: 33 | return LDAPInviterConfig 34 | 35 | async def sync_room( 36 | self, evt: MessageEvent, room: SyncRoomConfig, template_arg: str 37 | ): 38 | """Sync a single Matrix room""" 39 | # Setup LDAP connection 40 | ldap_manager = LDAPManager( 41 | self.config["ldap"]["uri"], 42 | self.config["ldap"]["connect_dn"], 43 | self.config["ldap"]["connect_password"], 44 | self.config["ldap"]["base_dn"], 45 | self.config["ldap"]["user_filter"], 46 | self.config["ldap"]["mxid_homeserver"], 47 | self.log, 48 | ) 49 | 50 | # Generate the final room alias 51 | alias = process_template(room["alias"], template_arg) 52 | await evt.respond(f"Syncing room: {alias}") 53 | self.log.debug(f"Syncing room: {alias}") 54 | 55 | # Ensure room exists 56 | room_id = await self.matrix_utils.ensure_room_with_alias(alias) 57 | 58 | # Ensure room has the correct name 59 | await self.matrix_utils.ensure_room_name( 60 | room_id, process_template(room["name"], template_arg) 61 | ) 62 | 63 | # Generate map of users 64 | all_users = {} 65 | all_users.update( 66 | await ldap_manager.get_all_matrix_users_of_sync_room( 67 | room["ldap_members"], template_arg 68 | ) 69 | ) 70 | # Hardcoded users are added last to allow overriding LDAP 71 | all_users.update(to_user_info_map(room.get("members", []))) 72 | 73 | # Ensure users are invited 74 | await self.matrix_utils.ensure_room_invitees(room_id, all_users) 75 | 76 | # Ensure users have correct power levels 77 | await self.matrix_utils.ensure_room_power_levels(room_id, all_users) 78 | 79 | # Ensure room is (in) visible in Room Directory 80 | await self.matrix_utils.ensure_room_visibility(room_id, room["visibility"]) 81 | 82 | await evt.respond(f"Successfully synced room.") 83 | self.log.debug(f"Successfully synced room.") 84 | 85 | async def sync_rooms( 86 | self, evt: MessageEvent, rooms: [SyncRoomConfig], template_arg1: str 87 | ): 88 | """Loops through a list of rooms to sync them""" 89 | for room in rooms: 90 | await self.sync_room(evt, room, template_arg1) 91 | 92 | @command.new(name="ldap-sync") 93 | @command.argument( 94 | "template_arg", "Template argument", pass_raw=True, required=False 95 | ) 96 | async def ldap_sync(self, evt: MessageEvent, template_arg: str) -> None: 97 | # Check if the user is allowed to sync 98 | if evt.sender not in self.config["admin_users"]: 99 | await evt.respond("You are not allowed to run a sync.") 100 | return None 101 | 102 | await evt.respond(f"Starting sync.") 103 | try: 104 | await self.sync_rooms(evt, self.config["sync_rooms"], template_arg) 105 | except Exception as e: 106 | # Wait a bit to hopefully clear too many requests 107 | await asyncio.sleep(5) 108 | await evt.respond(f"Encountered fatal error: {e}\n```\n{format_exc()}\n```") 109 | raise e 110 | 111 | @command.new(name="debug-map") 112 | async def ldap_check(self, evt: MessageEvent): 113 | await evt.respond( 114 | f'User map: {to_user_info_map(self.config["sync_rooms"][0]["members"])}' 115 | ) 116 | 117 | @command.new(name="ldap-check") 118 | @command.argument( 119 | "template_arg", "Template argument", pass_raw=True, required=False 120 | ) 121 | async def ldap_check(self, evt: MessageEvent, template_arg: str): 122 | await evt.respond("Checking LDAP connection...") 123 | try: 124 | ldap_manager = LDAPManager( 125 | self.config["ldap"]["uri"], 126 | self.config["ldap"]["connect_dn"], 127 | self.config["ldap"]["connect_password"], 128 | self.config["ldap"]["base_dn"], 129 | self.config["ldap"]["user_filter"], 130 | self.config["ldap"]["mxid_homeserver"], 131 | self.log, 132 | ) 133 | await evt.respond( 134 | f"Successfully connected. I am `{ldap_manager.connection.whoami_s()}`" 135 | ) 136 | room: SyncRoomConfig 137 | for room in self.config["sync_rooms"]: 138 | uids = await ldap_manager.get_all_matrix_users_of_sync_room( 139 | room["ldap_members"], template_arg 140 | ) 141 | await evt.respond( 142 | f'Members of room `{process_template(room["alias"], template_arg)}`:\n```\n{pformat(uids)}\n```' 143 | ) 144 | except Exception as e: 145 | await evt.respond(f"Encountered fatal error: {e}\n```\n{format_exc()}\n```") 146 | raise e 147 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "23f335db8fec845ca510ad49d580c769bd724085807498ac9d6ec3c92b45658a" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.9" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "aiohttp": { 20 | "hashes": [ 21 | "sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe", 22 | "sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe", 23 | "sha256:114b281e4d68302a324dd33abb04778e8557d88947875cbf4e842c2c01a030c5", 24 | "sha256:14762875b22d0055f05d12abc7f7d61d5fd4fe4642ce1a249abdf8c700bf1fd8", 25 | "sha256:15492a6368d985b76a2a5fdd2166cddfea5d24e69eefed4630cbaae5c81d89bd", 26 | "sha256:17c073de315745a1510393a96e680d20af8e67e324f70b42accbd4cb3315c9fb", 27 | "sha256:209b4a8ee987eccc91e2bd3ac36adee0e53a5970b8ac52c273f7f8fd4872c94c", 28 | "sha256:230a8f7e24298dea47659251abc0fd8b3c4e38a664c59d4b89cca7f6c09c9e87", 29 | "sha256:2e19413bf84934d651344783c9f5e22dee452e251cfd220ebadbed2d9931dbf0", 30 | "sha256:393f389841e8f2dfc86f774ad22f00923fdee66d238af89b70ea314c4aefd290", 31 | "sha256:3cf75f7cdc2397ed4442594b935a11ed5569961333d49b7539ea741be2cc79d5", 32 | "sha256:3d78619672183be860b96ed96f533046ec97ca067fd46ac1f6a09cd9b7484287", 33 | "sha256:40eced07f07a9e60e825554a31f923e8d3997cfc7fb31dbc1328c70826e04cde", 34 | "sha256:493d3299ebe5f5a7c66b9819eacdcfbbaaf1a8e84911ddffcdc48888497afecf", 35 | "sha256:4b302b45040890cea949ad092479e01ba25911a15e648429c7c5aae9650c67a8", 36 | "sha256:515dfef7f869a0feb2afee66b957cc7bbe9ad0cdee45aec7fdc623f4ecd4fb16", 37 | "sha256:547da6cacac20666422d4882cfcd51298d45f7ccb60a04ec27424d2f36ba3eaf", 38 | "sha256:5df68496d19f849921f05f14f31bd6ef53ad4b00245da3195048c69934521809", 39 | "sha256:64322071e046020e8797117b3658b9c2f80e3267daec409b350b6a7a05041213", 40 | "sha256:7615dab56bb07bff74bc865307aeb89a8bfd9941d2ef9d817b9436da3a0ea54f", 41 | "sha256:79ebfc238612123a713a457d92afb4096e2148be17df6c50fb9bf7a81c2f8013", 42 | "sha256:7b18b97cf8ee5452fa5f4e3af95d01d84d86d32c5e2bfa260cf041749d66360b", 43 | "sha256:932bb1ea39a54e9ea27fc9232163059a0b8855256f4052e776357ad9add6f1c9", 44 | "sha256:a00bb73540af068ca7390e636c01cbc4f644961896fa9363154ff43fd37af2f5", 45 | "sha256:a5ca29ee66f8343ed336816c553e82d6cade48a3ad702b9ffa6125d187e2dedb", 46 | "sha256:af9aa9ef5ba1fd5b8c948bb11f44891968ab30356d65fd0cc6707d989cd521df", 47 | "sha256:bb437315738aa441251214dad17428cafda9cdc9729499f1d6001748e1d432f4", 48 | "sha256:bdb230b4943891321e06fc7def63c7aace16095be7d9cf3b1e01be2f10fba439", 49 | "sha256:c6e9dcb4cb338d91a73f178d866d051efe7c62a7166653a91e7d9fb18274058f", 50 | "sha256:cffe3ab27871bc3ea47df5d8f7013945712c46a3cc5a95b6bee15887f1675c22", 51 | "sha256:d012ad7911653a906425d8473a1465caa9f8dea7fcf07b6d870397b774ea7c0f", 52 | "sha256:d9e13b33afd39ddeb377eff2c1c4f00544e191e1d1dee5b6c51ddee8ea6f0cf5", 53 | "sha256:e4b2b334e68b18ac9817d828ba44d8fcb391f6acb398bcc5062b14b2cbeac970", 54 | "sha256:e54962802d4b8b18b6207d4a927032826af39395a3bd9196a5af43fc4e60b009", 55 | "sha256:f705e12750171c0ab4ef2a3c76b9a4024a62c4103e3a55dd6f99265b9bc6fcfc", 56 | "sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a", 57 | "sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95" 58 | ], 59 | "markers": "python_version >= '3.6'", 60 | "version": "==3.7.4.post0" 61 | }, 62 | "alembic": { 63 | "hashes": [ 64 | "sha256:a21fedebb3fb8f6bbbba51a11114f08c78709377051384c9c5ead5705ee93a51", 65 | "sha256:e78be5b919f5bb184e3e0e2dd1ca986f2362e29a2bc933c446fe89f39dbe4e9c" 66 | ], 67 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", 68 | "version": "==1.6.5" 69 | }, 70 | "async-timeout": { 71 | "hashes": [ 72 | "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", 73 | "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" 74 | ], 75 | "markers": "python_full_version >= '3.5.3'", 76 | "version": "==3.0.1" 77 | }, 78 | "attrs": { 79 | "hashes": [ 80 | "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", 81 | "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb" 82 | ], 83 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 84 | "version": "==21.2.0" 85 | }, 86 | "bcrypt": { 87 | "hashes": [ 88 | "sha256:5b93c1726e50a93a033c36e5ca7fdcd29a5c7395af50a6892f5d9e7c6cfbfb29", 89 | "sha256:63d4e3ff96188e5898779b6057878fecf3f11cfe6ec3b313ea09955d587ec7a7", 90 | "sha256:81fec756feff5b6818ea7ab031205e1d323d8943d237303baca2c5f9c7846f34", 91 | "sha256:a67fb841b35c28a59cebed05fbd3e80eea26e6d75851f0574a9273c80f3e9b55", 92 | "sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6", 93 | "sha256:cd1ea2ff3038509ea95f687256c46b79f5fc382ad0aa3664d200047546d511d1", 94 | "sha256:cdcdcb3972027f83fe24a48b1e90ea4b584d35f1cc279d76de6fc4b13376239d" 95 | ], 96 | "markers": "python_version >= '3.6'", 97 | "version": "==3.2.0" 98 | }, 99 | "cffi": { 100 | "hashes": [ 101 | "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d", 102 | "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771", 103 | "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872", 104 | "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c", 105 | "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc", 106 | "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762", 107 | "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202", 108 | "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5", 109 | "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548", 110 | "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a", 111 | "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f", 112 | "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20", 113 | "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218", 114 | "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c", 115 | "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e", 116 | "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56", 117 | "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224", 118 | "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a", 119 | "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2", 120 | "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a", 121 | "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819", 122 | "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346", 123 | "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b", 124 | "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e", 125 | "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534", 126 | "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb", 127 | "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0", 128 | "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156", 129 | "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd", 130 | "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87", 131 | "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc", 132 | "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195", 133 | "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33", 134 | "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f", 135 | "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d", 136 | "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd", 137 | "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728", 138 | "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7", 139 | "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca", 140 | "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99", 141 | "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf", 142 | "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e", 143 | "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c", 144 | "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5", 145 | "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69" 146 | ], 147 | "version": "==1.14.6" 148 | }, 149 | "chardet": { 150 | "hashes": [ 151 | "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", 152 | "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" 153 | ], 154 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 155 | "version": "==4.0.0" 156 | }, 157 | "click": { 158 | "hashes": [ 159 | "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", 160 | "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" 161 | ], 162 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 163 | "version": "==7.1.2" 164 | }, 165 | "colorama": { 166 | "hashes": [ 167 | "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b", 168 | "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2" 169 | ], 170 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 171 | "version": "==0.4.4" 172 | }, 173 | "commonmark": { 174 | "hashes": [ 175 | "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60", 176 | "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9" 177 | ], 178 | "version": "==0.9.1" 179 | }, 180 | "idna": { 181 | "hashes": [ 182 | "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a", 183 | "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3" 184 | ], 185 | "markers": "python_version >= '3.5'", 186 | "version": "==3.2" 187 | }, 188 | "jinja2": { 189 | "hashes": [ 190 | "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", 191 | "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" 192 | ], 193 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 194 | "version": "==2.11.3" 195 | }, 196 | "mako": { 197 | "hashes": [ 198 | "sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab", 199 | "sha256:aea166356da44b9b830c8023cd9b557fa856bd8b4035d6de771ca027dfc5cc6e" 200 | ], 201 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 202 | "version": "==1.1.4" 203 | }, 204 | "markupsafe": { 205 | "hashes": [ 206 | "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298", 207 | "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64", 208 | "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b", 209 | "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567", 210 | "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff", 211 | "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74", 212 | "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35", 213 | "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26", 214 | "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7", 215 | "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75", 216 | "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f", 217 | "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135", 218 | "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8", 219 | "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a", 220 | "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914", 221 | "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18", 222 | "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8", 223 | "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2", 224 | "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d", 225 | "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b", 226 | "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f", 227 | "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb", 228 | "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833", 229 | "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415", 230 | "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902", 231 | "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9", 232 | "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d", 233 | "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066", 234 | "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f", 235 | "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5", 236 | "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94", 237 | "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509", 238 | "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51", 239 | "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872" 240 | ], 241 | "markers": "python_version >= '3.6'", 242 | "version": "==2.0.1" 243 | }, 244 | "maubot": { 245 | "hashes": [ 246 | "sha256:0432105825d4737ec81ca56aced96c17a66072aac9df415d8f89e99f9641d628", 247 | "sha256:5ecaba5390b883c324404095adce9a029fe4985a93765d6d9f25b441fb05672f" 248 | ], 249 | "index": "pypi", 250 | "version": "==0.1.2" 251 | }, 252 | "mautrix": { 253 | "hashes": [ 254 | "sha256:0583fc2ee9723c5a032d4375d54bf4f21fd11fa33be2f6ad5cdd4017fbbe4f08", 255 | "sha256:bacf1f7ae125855ac403dbec38d4bb01e46732e88782f2e39b84d5b8af69d5c5" 256 | ], 257 | "index": "pypi", 258 | "version": "==0.9.10" 259 | }, 260 | "multidict": { 261 | "hashes": [ 262 | "sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a", 263 | "sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93", 264 | "sha256:05c20b68e512166fddba59a918773ba002fdd77800cad9f55b59790030bab632", 265 | "sha256:07b42215124aedecc6083f1ce6b7e5ec5b50047afa701f3442054373a6deb656", 266 | "sha256:0e3c84e6c67eba89c2dbcee08504ba8644ab4284863452450520dad8f1e89b79", 267 | "sha256:0e929169f9c090dae0646a011c8b058e5e5fb391466016b39d21745b48817fd7", 268 | "sha256:1ab820665e67373de5802acae069a6a05567ae234ddb129f31d290fc3d1aa56d", 269 | "sha256:25b4e5f22d3a37ddf3effc0710ba692cfc792c2b9edfb9c05aefe823256e84d5", 270 | "sha256:2e68965192c4ea61fff1b81c14ff712fc7dc15d2bd120602e4a3494ea6584224", 271 | "sha256:2f1a132f1c88724674271d636e6b7351477c27722f2ed789f719f9e3545a3d26", 272 | "sha256:37e5438e1c78931df5d3c0c78ae049092877e5e9c02dd1ff5abb9cf27a5914ea", 273 | "sha256:3a041b76d13706b7fff23b9fc83117c7b8fe8d5fe9e6be45eee72b9baa75f348", 274 | "sha256:3a4f32116f8f72ecf2a29dabfb27b23ab7cdc0ba807e8459e59a93a9be9506f6", 275 | "sha256:46c73e09ad374a6d876c599f2328161bcd95e280f84d2060cf57991dec5cfe76", 276 | "sha256:46dd362c2f045095c920162e9307de5ffd0a1bfbba0a6e990b344366f55a30c1", 277 | "sha256:4b186eb7d6ae7c06eb4392411189469e6a820da81447f46c0072a41c748ab73f", 278 | "sha256:54fd1e83a184e19c598d5e70ba508196fd0bbdd676ce159feb412a4a6664f952", 279 | "sha256:585fd452dd7782130d112f7ddf3473ffdd521414674c33876187e101b588738a", 280 | "sha256:5cf3443199b83ed9e955f511b5b241fd3ae004e3cb81c58ec10f4fe47c7dce37", 281 | "sha256:6a4d5ce640e37b0efcc8441caeea8f43a06addace2335bd11151bc02d2ee31f9", 282 | "sha256:7df80d07818b385f3129180369079bd6934cf70469f99daaebfac89dca288359", 283 | "sha256:806068d4f86cb06af37cd65821554f98240a19ce646d3cd24e1c33587f313eb8", 284 | "sha256:830f57206cc96ed0ccf68304141fec9481a096c4d2e2831f311bde1c404401da", 285 | "sha256:929006d3c2d923788ba153ad0de8ed2e5ed39fdbe8e7be21e2f22ed06c6783d3", 286 | "sha256:9436dc58c123f07b230383083855593550c4d301d2532045a17ccf6eca505f6d", 287 | "sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf", 288 | "sha256:ace010325c787c378afd7f7c1ac66b26313b3344628652eacd149bdd23c68841", 289 | "sha256:b47a43177a5e65b771b80db71e7be76c0ba23cc8aa73eeeb089ed5219cdbe27d", 290 | "sha256:b797515be8743b771aa868f83563f789bbd4b236659ba52243b735d80b29ed93", 291 | "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f", 292 | "sha256:d5c65bdf4484872c4af3150aeebe101ba560dcfb34488d9a8ff8dbcd21079647", 293 | "sha256:d81eddcb12d608cc08081fa88d046c78afb1bf8107e6feab5d43503fea74a635", 294 | "sha256:dc862056f76443a0db4509116c5cd480fe1b6a2d45512a653f9a855cc0517456", 295 | "sha256:ecc771ab628ea281517e24fd2c52e8f31c41e66652d07599ad8818abaad38cda", 296 | "sha256:f200755768dc19c6f4e2b672421e0ebb3dd54c38d5a4f262b872d8cfcc9e93b5", 297 | "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281", 298 | "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80" 299 | ], 300 | "markers": "python_version >= '3.6'", 301 | "version": "==5.1.0" 302 | }, 303 | "packaging": { 304 | "hashes": [ 305 | "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7", 306 | "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14" 307 | ], 308 | "markers": "python_version >= '3.6'", 309 | "version": "==21.0" 310 | }, 311 | "prompt-toolkit": { 312 | "hashes": [ 313 | "sha256:7281b5199235adaef6980942840c43753e4ab20dfe41338da634fb41c194f9d8", 314 | "sha256:82c7f8e07d7a0411ff5367a5a8ff520f0112b9179f3e599ee8ad2ad9b943d911", 315 | "sha256:cc66413b1b4b17021675d9f2d15d57e640b06ddfd99bb724c73484126d22622f" 316 | ], 317 | "version": "==1.0.14" 318 | }, 319 | "pyasn1": { 320 | "hashes": [ 321 | "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359", 322 | "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576", 323 | "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf", 324 | "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7", 325 | "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d", 326 | "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00", 327 | "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8", 328 | "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86", 329 | "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12", 330 | "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776", 331 | "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba", 332 | "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2", 333 | "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3" 334 | ], 335 | "version": "==0.4.8" 336 | }, 337 | "pyasn1-modules": { 338 | "hashes": [ 339 | "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8", 340 | "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199", 341 | "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811", 342 | "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed", 343 | "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4", 344 | "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e", 345 | "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74", 346 | "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb", 347 | "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45", 348 | "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd", 349 | "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0", 350 | "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d", 351 | "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405" 352 | ], 353 | "version": "==0.2.8" 354 | }, 355 | "pycparser": { 356 | "hashes": [ 357 | "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", 358 | "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" 359 | ], 360 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 361 | "version": "==2.20" 362 | }, 363 | "pygments": { 364 | "hashes": [ 365 | "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f", 366 | "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e" 367 | ], 368 | "markers": "python_version >= '3.5'", 369 | "version": "==2.9.0" 370 | }, 371 | "pyinquirer": { 372 | "hashes": [ 373 | "sha256:c9a92d68d7727fbd886a7908c08fd9e9773e5dc211bf5cbf836ba90d366dee51" 374 | ], 375 | "version": "==1.0.3" 376 | }, 377 | "pyparsing": { 378 | "hashes": [ 379 | "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", 380 | "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" 381 | ], 382 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", 383 | "version": "==2.4.7" 384 | }, 385 | "python-dateutil": { 386 | "hashes": [ 387 | "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", 388 | "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" 389 | ], 390 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 391 | "version": "==2.8.2" 392 | }, 393 | "python-editor": { 394 | "hashes": [ 395 | "sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d", 396 | "sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b", 397 | "sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8", 398 | "sha256:c3da2053dbab6b29c94e43c486ff67206eafbe7eb52dbec7390b5e2fb05aac77", 399 | "sha256:ea87e17f6ec459e780e4221f295411462e0d0810858e055fc514684350a2f522" 400 | ], 401 | "version": "==1.0.4" 402 | }, 403 | "python-ldap": { 404 | "hashes": [ 405 | "sha256:4711cacf013e298754abd70058ccc995758177fb425f1c2d30e71adfc1d00aa5" 406 | ], 407 | "index": "pypi", 408 | "version": "==3.3.1" 409 | }, 410 | "regex": { 411 | "hashes": [ 412 | "sha256:026beb631097a4a3def7299aa5825e05e057de3c6d72b139c37813bfa351274b", 413 | "sha256:14caacd1853e40103f59571f169704367e79fb78fac3d6d09ac84d9197cadd16", 414 | "sha256:16d9eaa8c7e91537516c20da37db975f09ac2e7772a0694b245076c6d68f85da", 415 | "sha256:18fdc51458abc0a974822333bd3a932d4e06ba2a3243e9a1da305668bd62ec6d", 416 | "sha256:28e8af338240b6f39713a34e337c3813047896ace09d51593d6907c66c0708ba", 417 | "sha256:3835de96524a7b6869a6c710b26c90e94558c31006e96ca3cf6af6751b27dca1", 418 | "sha256:3905c86cc4ab6d71635d6419a6f8d972cab7c634539bba6053c47354fd04452c", 419 | "sha256:3c09d88a07483231119f5017904db8f60ad67906efac3f1baa31b9b7f7cca281", 420 | "sha256:4551728b767f35f86b8e5ec19a363df87450c7376d7419c3cac5b9ceb4bce576", 421 | "sha256:459bbe342c5b2dec5c5223e7c363f291558bc27982ef39ffd6569e8c082bdc83", 422 | "sha256:4f421e3cdd3a273bace013751c345f4ebeef08f05e8c10757533ada360b51a39", 423 | "sha256:577737ec3d4c195c4aef01b757905779a9e9aee608fa1cf0aec16b5576c893d3", 424 | "sha256:57fece29f7cc55d882fe282d9de52f2f522bb85290555b49394102f3621751ee", 425 | "sha256:7976d410e42be9ae7458c1816a416218364e06e162b82e42f7060737e711d9ce", 426 | "sha256:85f568892422a0e96235eb8ea6c5a41c8ccbf55576a2260c0160800dbd7c4f20", 427 | "sha256:8764a78c5464ac6bde91a8c87dd718c27c1cabb7ed2b4beaf36d3e8e390567f9", 428 | "sha256:8935937dad2c9b369c3d932b0edbc52a62647c2afb2fafc0c280f14a8bf56a6a", 429 | "sha256:8fe58d9f6e3d1abf690174fd75800fda9bdc23d2a287e77758dc0e8567e38ce6", 430 | "sha256:937b20955806381e08e54bd9d71f83276d1f883264808521b70b33d98e4dec5d", 431 | "sha256:9569da9e78f0947b249370cb8fadf1015a193c359e7e442ac9ecc585d937f08d", 432 | "sha256:a3b73390511edd2db2d34ff09aa0b2c08be974c71b4c0505b4a048d5dc128c2b", 433 | "sha256:a4eddbe2a715b2dd3849afbdeacf1cc283160b24e09baf64fa5675f51940419d", 434 | "sha256:a5c6dbe09aff091adfa8c7cfc1a0e83fdb8021ddb2c183512775a14f1435fe16", 435 | "sha256:b63e3571b24a7959017573b6455e05b675050bbbea69408f35f3cb984ec54363", 436 | "sha256:bb350eb1060591d8e89d6bac4713d41006cd4d479f5e11db334a48ff8999512f", 437 | "sha256:bf6d987edd4a44dd2fa2723fca2790f9442ae4de2c8438e53fcb1befdf5d823a", 438 | "sha256:bfa6a679410b394600eafd16336b2ce8de43e9b13f7fb9247d84ef5ad2b45e91", 439 | "sha256:c856ec9b42e5af4fe2d8e75970fcc3a2c15925cbcc6e7a9bcb44583b10b95e80", 440 | "sha256:cea56288eeda8b7511d507bbe7790d89ae7049daa5f51ae31a35ae3c05408531", 441 | "sha256:ea212df6e5d3f60341aef46401d32fcfded85593af1d82b8b4a7a68cd67fdd6b", 442 | "sha256:f35567470ee6dbfb946f069ed5f5615b40edcbb5f1e6e1d3d2b114468d505fc6", 443 | "sha256:fbc20975eee093efa2071de80df7f972b7b35e560b213aafabcec7c0bd00bd8c", 444 | "sha256:ff4a8ad9638b7ca52313d8732f37ecd5fd3c8e3aff10a8ccb93176fd5b3812f6" 445 | ], 446 | "version": "==2021.8.3" 447 | }, 448 | "ruamel.yaml": { 449 | "hashes": [ 450 | "sha256:106bc8d6dc6a0ff7c9196a47570432036f41d556b779c6b4e618085f57e39e67", 451 | "sha256:ffb9b703853e9e8b7861606dfdab1026cf02505bade0653d1880f4b2db47f815" 452 | ], 453 | "markers": "python_version >= '3'", 454 | "version": "==0.17.10" 455 | }, 456 | "ruamel.yaml.clib": { 457 | "hashes": [ 458 | "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd", 459 | "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0", 460 | "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277", 461 | "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104", 462 | "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd", 463 | "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78", 464 | "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99", 465 | "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527", 466 | "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84", 467 | "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7", 468 | "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468", 469 | "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b", 470 | "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94", 471 | "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233", 472 | "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb", 473 | "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5", 474 | "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe", 475 | "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751", 476 | "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502", 477 | "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed", 478 | "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c" 479 | ], 480 | "markers": "python_version < '3.10' and platform_python_implementation == 'CPython'", 481 | "version": "==0.2.6" 482 | }, 483 | "six": { 484 | "hashes": [ 485 | "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", 486 | "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" 487 | ], 488 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 489 | "version": "==1.16.0" 490 | }, 491 | "sqlalchemy": { 492 | "hashes": [ 493 | "sha256:014ea143572fee1c18322b7908140ad23b3994036ef4c0d630110faf942652f8", 494 | "sha256:0172423a27fbcae3751ef016663b72e1a516777de324a76e30efa170dbd3dd2d", 495 | "sha256:01aa5f803db724447c1d423ed583e42bf5264c597fd55e4add4301f163b0be48", 496 | "sha256:0352db1befcbed2f9282e72843f1963860bf0e0472a4fa5cf8ee084318e0e6ab", 497 | "sha256:09083c2487ca3c0865dc588e07aeaa25416da3d95f7482c07e92f47e080aa17b", 498 | "sha256:0d5d862b1cfbec5028ce1ecac06a3b42bc7703eb80e4b53fceb2738724311443", 499 | "sha256:14f0eb5db872c231b20c18b1e5806352723a3a89fb4254af3b3e14f22eaaec75", 500 | "sha256:1e2f89d2e5e3c7a88e25a3b0e43626dba8db2aa700253023b82e630d12b37109", 501 | "sha256:26155ea7a243cbf23287f390dba13d7927ffa1586d3208e0e8d615d0c506f996", 502 | "sha256:2ed6343b625b16bcb63c5b10523fd15ed8934e1ed0f772c534985e9f5e73d894", 503 | "sha256:34fcec18f6e4b24b4a5f6185205a04f1eab1e56f8f1d028a2a03694ebcc2ddd4", 504 | "sha256:4d0e3515ef98aa4f0dc289ff2eebb0ece6260bbf37c2ea2022aad63797eacf60", 505 | "sha256:5de2464c254380d8a6c20a2746614d5a436260be1507491442cf1088e59430d2", 506 | "sha256:6607ae6cd3a07f8a4c3198ffbf256c261661965742e2b5265a77cd5c679c9bba", 507 | "sha256:8110e6c414d3efc574543109ee618fe2c1f96fa31833a1ff36cc34e968c4f233", 508 | "sha256:816de75418ea0953b5eb7b8a74933ee5a46719491cd2b16f718afc4b291a9658", 509 | "sha256:861e459b0e97673af6cc5e7f597035c2e3acdfb2608132665406cded25ba64c7", 510 | "sha256:87a2725ad7d41cd7376373c15fd8bf674e9c33ca56d0b8036add2d634dba372e", 511 | "sha256:a006d05d9aa052657ee3e4dc92544faae5fcbaafc6128217310945610d862d39", 512 | "sha256:bce28277f308db43a6b4965734366f533b3ff009571ec7ffa583cb77539b84d6", 513 | "sha256:c10ff6112d119f82b1618b6dc28126798481b9355d8748b64b9b55051eb4f01b", 514 | "sha256:d375d8ccd3cebae8d90270f7aa8532fe05908f79e78ae489068f3b4eee5994e8", 515 | "sha256:d37843fb8df90376e9e91336724d78a32b988d3d20ab6656da4eb8ee3a45b63c", 516 | "sha256:e47e257ba5934550d7235665eee6c911dc7178419b614ba9e1fbb1ce6325b14f", 517 | "sha256:e98d09f487267f1e8d1179bf3b9d7709b30a916491997137dd24d6ae44d18d79", 518 | "sha256:ebbb777cbf9312359b897bf81ba00dae0f5cb69fba2a18265dcc18a6f5ef7519", 519 | "sha256:ee5f5188edb20a29c1cc4a039b074fdc5575337c9a68f3063449ab47757bb064", 520 | "sha256:f03bd97650d2e42710fbe4cf8a59fae657f191df851fc9fc683ecef10746a375", 521 | "sha256:f1149d6e5c49d069163e58a3196865e4321bad1803d7886e07d8710de392c548", 522 | "sha256:f3c5c52f7cb8b84bfaaf22d82cb9e6e9a8297f7c2ed14d806a0f5e4d22e83fb7", 523 | "sha256:f597a243b8550a3a0b15122b14e49d8a7e622ba1c9d29776af741f1845478d79", 524 | "sha256:fc1f2a5a5963e2e73bac4926bdaf7790c4d7d77e8fc0590817880e22dd9d0b8b", 525 | "sha256:fc4cddb0b474b12ed7bdce6be1b9edc65352e8ce66bc10ff8cbbfb3d4047dbf4", 526 | "sha256:fcb251305fa24a490b6a9ee2180e5f8252915fb778d3dafc70f9cc3f863827b9" 527 | ], 528 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 529 | "version": "==1.3.24" 530 | }, 531 | "typing-extensions": { 532 | "hashes": [ 533 | "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", 534 | "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", 535 | "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" 536 | ], 537 | "version": "==3.10.0.0" 538 | }, 539 | "wcwidth": { 540 | "hashes": [ 541 | "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", 542 | "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83" 543 | ], 544 | "version": "==0.2.5" 545 | }, 546 | "yarl": { 547 | "hashes": [ 548 | "sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e", 549 | "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434", 550 | "sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366", 551 | "sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3", 552 | "sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec", 553 | "sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959", 554 | "sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e", 555 | "sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c", 556 | "sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6", 557 | "sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a", 558 | "sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6", 559 | "sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424", 560 | "sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e", 561 | "sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f", 562 | "sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50", 563 | "sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2", 564 | "sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc", 565 | "sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4", 566 | "sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970", 567 | "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10", 568 | "sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0", 569 | "sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406", 570 | "sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896", 571 | "sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643", 572 | "sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721", 573 | "sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478", 574 | "sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724", 575 | "sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e", 576 | "sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8", 577 | "sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96", 578 | "sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25", 579 | "sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76", 580 | "sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2", 581 | "sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2", 582 | "sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c", 583 | "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a", 584 | "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71" 585 | ], 586 | "markers": "python_version >= '3.6'", 587 | "version": "==1.6.3" 588 | } 589 | }, 590 | "develop": { 591 | "aiohttp": { 592 | "hashes": [ 593 | "sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe", 594 | "sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe", 595 | "sha256:114b281e4d68302a324dd33abb04778e8557d88947875cbf4e842c2c01a030c5", 596 | "sha256:14762875b22d0055f05d12abc7f7d61d5fd4fe4642ce1a249abdf8c700bf1fd8", 597 | "sha256:15492a6368d985b76a2a5fdd2166cddfea5d24e69eefed4630cbaae5c81d89bd", 598 | "sha256:17c073de315745a1510393a96e680d20af8e67e324f70b42accbd4cb3315c9fb", 599 | "sha256:209b4a8ee987eccc91e2bd3ac36adee0e53a5970b8ac52c273f7f8fd4872c94c", 600 | "sha256:230a8f7e24298dea47659251abc0fd8b3c4e38a664c59d4b89cca7f6c09c9e87", 601 | "sha256:2e19413bf84934d651344783c9f5e22dee452e251cfd220ebadbed2d9931dbf0", 602 | "sha256:393f389841e8f2dfc86f774ad22f00923fdee66d238af89b70ea314c4aefd290", 603 | "sha256:3cf75f7cdc2397ed4442594b935a11ed5569961333d49b7539ea741be2cc79d5", 604 | "sha256:3d78619672183be860b96ed96f533046ec97ca067fd46ac1f6a09cd9b7484287", 605 | "sha256:40eced07f07a9e60e825554a31f923e8d3997cfc7fb31dbc1328c70826e04cde", 606 | "sha256:493d3299ebe5f5a7c66b9819eacdcfbbaaf1a8e84911ddffcdc48888497afecf", 607 | "sha256:4b302b45040890cea949ad092479e01ba25911a15e648429c7c5aae9650c67a8", 608 | "sha256:515dfef7f869a0feb2afee66b957cc7bbe9ad0cdee45aec7fdc623f4ecd4fb16", 609 | "sha256:547da6cacac20666422d4882cfcd51298d45f7ccb60a04ec27424d2f36ba3eaf", 610 | "sha256:5df68496d19f849921f05f14f31bd6ef53ad4b00245da3195048c69934521809", 611 | "sha256:64322071e046020e8797117b3658b9c2f80e3267daec409b350b6a7a05041213", 612 | "sha256:7615dab56bb07bff74bc865307aeb89a8bfd9941d2ef9d817b9436da3a0ea54f", 613 | "sha256:79ebfc238612123a713a457d92afb4096e2148be17df6c50fb9bf7a81c2f8013", 614 | "sha256:7b18b97cf8ee5452fa5f4e3af95d01d84d86d32c5e2bfa260cf041749d66360b", 615 | "sha256:932bb1ea39a54e9ea27fc9232163059a0b8855256f4052e776357ad9add6f1c9", 616 | "sha256:a00bb73540af068ca7390e636c01cbc4f644961896fa9363154ff43fd37af2f5", 617 | "sha256:a5ca29ee66f8343ed336816c553e82d6cade48a3ad702b9ffa6125d187e2dedb", 618 | "sha256:af9aa9ef5ba1fd5b8c948bb11f44891968ab30356d65fd0cc6707d989cd521df", 619 | "sha256:bb437315738aa441251214dad17428cafda9cdc9729499f1d6001748e1d432f4", 620 | "sha256:bdb230b4943891321e06fc7def63c7aace16095be7d9cf3b1e01be2f10fba439", 621 | "sha256:c6e9dcb4cb338d91a73f178d866d051efe7c62a7166653a91e7d9fb18274058f", 622 | "sha256:cffe3ab27871bc3ea47df5d8f7013945712c46a3cc5a95b6bee15887f1675c22", 623 | "sha256:d012ad7911653a906425d8473a1465caa9f8dea7fcf07b6d870397b774ea7c0f", 624 | "sha256:d9e13b33afd39ddeb377eff2c1c4f00544e191e1d1dee5b6c51ddee8ea6f0cf5", 625 | "sha256:e4b2b334e68b18ac9817d828ba44d8fcb391f6acb398bcc5062b14b2cbeac970", 626 | "sha256:e54962802d4b8b18b6207d4a927032826af39395a3bd9196a5af43fc4e60b009", 627 | "sha256:f705e12750171c0ab4ef2a3c76b9a4024a62c4103e3a55dd6f99265b9bc6fcfc", 628 | "sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a", 629 | "sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95" 630 | ], 631 | "markers": "python_version >= '3.6'", 632 | "version": "==3.7.4.post0" 633 | }, 634 | "aiohttp-cors": { 635 | "hashes": [ 636 | "sha256:0451ba59fdf6909d0e2cd21e4c0a43752bc0703d33fc78ae94d9d9321710193e", 637 | "sha256:4d39c6d7100fd9764ed1caf8cebf0eb01bf5e3f24e2e073fda6234bc48b19f5d" 638 | ], 639 | "version": "==0.7.0" 640 | }, 641 | "appdirs": { 642 | "hashes": [ 643 | "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", 644 | "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128" 645 | ], 646 | "version": "==1.4.4" 647 | }, 648 | "astroid": { 649 | "hashes": [ 650 | "sha256:3975a0bd5373bdce166e60c851cfcbaf21ee96de80ec518c1f4cb3e94c3fb334", 651 | "sha256:ab7f36e8a78b8e54a62028ba6beef7561db4cdb6f2a5009ecc44a6f42b5697ef" 652 | ], 653 | "markers": "python_version ~= '3.6'", 654 | "version": "==2.6.6" 655 | }, 656 | "async-timeout": { 657 | "hashes": [ 658 | "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", 659 | "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" 660 | ], 661 | "markers": "python_full_version >= '3.5.3'", 662 | "version": "==3.0.1" 663 | }, 664 | "attrs": { 665 | "hashes": [ 666 | "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", 667 | "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb" 668 | ], 669 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 670 | "version": "==21.2.0" 671 | }, 672 | "black": { 673 | "extras": [ 674 | "d" 675 | ], 676 | "hashes": [ 677 | "sha256:1c7aa6ada8ee864db745b22790a32f94b2795c253a75d6d9b5e439ff10d23116", 678 | "sha256:c8373c6491de9362e39271630b65b964607bc5c79c83783547d76c839b3aa219" 679 | ], 680 | "index": "pypi", 681 | "version": "==21.7b0" 682 | }, 683 | "chardet": { 684 | "hashes": [ 685 | "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", 686 | "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" 687 | ], 688 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 689 | "version": "==4.0.0" 690 | }, 691 | "click": { 692 | "hashes": [ 693 | "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", 694 | "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" 695 | ], 696 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 697 | "version": "==7.1.2" 698 | }, 699 | "idna": { 700 | "hashes": [ 701 | "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a", 702 | "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3" 703 | ], 704 | "markers": "python_version >= '3.5'", 705 | "version": "==3.2" 706 | }, 707 | "isort": { 708 | "hashes": [ 709 | "sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899", 710 | "sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2" 711 | ], 712 | "markers": "python_version < '4.0' and python_full_version >= '3.6.1'", 713 | "version": "==5.9.3" 714 | }, 715 | "lazy-object-proxy": { 716 | "hashes": [ 717 | "sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653", 718 | "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61", 719 | "sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2", 720 | "sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837", 721 | "sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3", 722 | "sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43", 723 | "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726", 724 | "sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3", 725 | "sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587", 726 | "sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8", 727 | "sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a", 728 | "sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd", 729 | "sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f", 730 | "sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad", 731 | "sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4", 732 | "sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b", 733 | "sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf", 734 | "sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981", 735 | "sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741", 736 | "sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e", 737 | "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93", 738 | "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b" 739 | ], 740 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", 741 | "version": "==1.6.0" 742 | }, 743 | "mccabe": { 744 | "hashes": [ 745 | "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", 746 | "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" 747 | ], 748 | "version": "==0.6.1" 749 | }, 750 | "multidict": { 751 | "hashes": [ 752 | "sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a", 753 | "sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93", 754 | "sha256:05c20b68e512166fddba59a918773ba002fdd77800cad9f55b59790030bab632", 755 | "sha256:07b42215124aedecc6083f1ce6b7e5ec5b50047afa701f3442054373a6deb656", 756 | "sha256:0e3c84e6c67eba89c2dbcee08504ba8644ab4284863452450520dad8f1e89b79", 757 | "sha256:0e929169f9c090dae0646a011c8b058e5e5fb391466016b39d21745b48817fd7", 758 | "sha256:1ab820665e67373de5802acae069a6a05567ae234ddb129f31d290fc3d1aa56d", 759 | "sha256:25b4e5f22d3a37ddf3effc0710ba692cfc792c2b9edfb9c05aefe823256e84d5", 760 | "sha256:2e68965192c4ea61fff1b81c14ff712fc7dc15d2bd120602e4a3494ea6584224", 761 | "sha256:2f1a132f1c88724674271d636e6b7351477c27722f2ed789f719f9e3545a3d26", 762 | "sha256:37e5438e1c78931df5d3c0c78ae049092877e5e9c02dd1ff5abb9cf27a5914ea", 763 | "sha256:3a041b76d13706b7fff23b9fc83117c7b8fe8d5fe9e6be45eee72b9baa75f348", 764 | "sha256:3a4f32116f8f72ecf2a29dabfb27b23ab7cdc0ba807e8459e59a93a9be9506f6", 765 | "sha256:46c73e09ad374a6d876c599f2328161bcd95e280f84d2060cf57991dec5cfe76", 766 | "sha256:46dd362c2f045095c920162e9307de5ffd0a1bfbba0a6e990b344366f55a30c1", 767 | "sha256:4b186eb7d6ae7c06eb4392411189469e6a820da81447f46c0072a41c748ab73f", 768 | "sha256:54fd1e83a184e19c598d5e70ba508196fd0bbdd676ce159feb412a4a6664f952", 769 | "sha256:585fd452dd7782130d112f7ddf3473ffdd521414674c33876187e101b588738a", 770 | "sha256:5cf3443199b83ed9e955f511b5b241fd3ae004e3cb81c58ec10f4fe47c7dce37", 771 | "sha256:6a4d5ce640e37b0efcc8441caeea8f43a06addace2335bd11151bc02d2ee31f9", 772 | "sha256:7df80d07818b385f3129180369079bd6934cf70469f99daaebfac89dca288359", 773 | "sha256:806068d4f86cb06af37cd65821554f98240a19ce646d3cd24e1c33587f313eb8", 774 | "sha256:830f57206cc96ed0ccf68304141fec9481a096c4d2e2831f311bde1c404401da", 775 | "sha256:929006d3c2d923788ba153ad0de8ed2e5ed39fdbe8e7be21e2f22ed06c6783d3", 776 | "sha256:9436dc58c123f07b230383083855593550c4d301d2532045a17ccf6eca505f6d", 777 | "sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf", 778 | "sha256:ace010325c787c378afd7f7c1ac66b26313b3344628652eacd149bdd23c68841", 779 | "sha256:b47a43177a5e65b771b80db71e7be76c0ba23cc8aa73eeeb089ed5219cdbe27d", 780 | "sha256:b797515be8743b771aa868f83563f789bbd4b236659ba52243b735d80b29ed93", 781 | "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f", 782 | "sha256:d5c65bdf4484872c4af3150aeebe101ba560dcfb34488d9a8ff8dbcd21079647", 783 | "sha256:d81eddcb12d608cc08081fa88d046c78afb1bf8107e6feab5d43503fea74a635", 784 | "sha256:dc862056f76443a0db4509116c5cd480fe1b6a2d45512a653f9a855cc0517456", 785 | "sha256:ecc771ab628ea281517e24fd2c52e8f31c41e66652d07599ad8818abaad38cda", 786 | "sha256:f200755768dc19c6f4e2b672421e0ebb3dd54c38d5a4f262b872d8cfcc9e93b5", 787 | "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281", 788 | "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80" 789 | ], 790 | "markers": "python_version >= '3.6'", 791 | "version": "==5.1.0" 792 | }, 793 | "mypy-extensions": { 794 | "hashes": [ 795 | "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", 796 | "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" 797 | ], 798 | "version": "==0.4.3" 799 | }, 800 | "pathspec": { 801 | "hashes": [ 802 | "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a", 803 | "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1" 804 | ], 805 | "version": "==0.9.0" 806 | }, 807 | "pylint": { 808 | "hashes": [ 809 | "sha256:2e1a0eb2e8ab41d6b5dbada87f066492bb1557b12b76c47c2ee8aa8a11186594", 810 | "sha256:8b838c8983ee1904b2de66cce9d0b96649a91901350e956d78f289c3bc87b48e" 811 | ], 812 | "index": "pypi", 813 | "version": "==2.9.6" 814 | }, 815 | "regex": { 816 | "hashes": [ 817 | "sha256:026beb631097a4a3def7299aa5825e05e057de3c6d72b139c37813bfa351274b", 818 | "sha256:14caacd1853e40103f59571f169704367e79fb78fac3d6d09ac84d9197cadd16", 819 | "sha256:16d9eaa8c7e91537516c20da37db975f09ac2e7772a0694b245076c6d68f85da", 820 | "sha256:18fdc51458abc0a974822333bd3a932d4e06ba2a3243e9a1da305668bd62ec6d", 821 | "sha256:28e8af338240b6f39713a34e337c3813047896ace09d51593d6907c66c0708ba", 822 | "sha256:3835de96524a7b6869a6c710b26c90e94558c31006e96ca3cf6af6751b27dca1", 823 | "sha256:3905c86cc4ab6d71635d6419a6f8d972cab7c634539bba6053c47354fd04452c", 824 | "sha256:3c09d88a07483231119f5017904db8f60ad67906efac3f1baa31b9b7f7cca281", 825 | "sha256:4551728b767f35f86b8e5ec19a363df87450c7376d7419c3cac5b9ceb4bce576", 826 | "sha256:459bbe342c5b2dec5c5223e7c363f291558bc27982ef39ffd6569e8c082bdc83", 827 | "sha256:4f421e3cdd3a273bace013751c345f4ebeef08f05e8c10757533ada360b51a39", 828 | "sha256:577737ec3d4c195c4aef01b757905779a9e9aee608fa1cf0aec16b5576c893d3", 829 | "sha256:57fece29f7cc55d882fe282d9de52f2f522bb85290555b49394102f3621751ee", 830 | "sha256:7976d410e42be9ae7458c1816a416218364e06e162b82e42f7060737e711d9ce", 831 | "sha256:85f568892422a0e96235eb8ea6c5a41c8ccbf55576a2260c0160800dbd7c4f20", 832 | "sha256:8764a78c5464ac6bde91a8c87dd718c27c1cabb7ed2b4beaf36d3e8e390567f9", 833 | "sha256:8935937dad2c9b369c3d932b0edbc52a62647c2afb2fafc0c280f14a8bf56a6a", 834 | "sha256:8fe58d9f6e3d1abf690174fd75800fda9bdc23d2a287e77758dc0e8567e38ce6", 835 | "sha256:937b20955806381e08e54bd9d71f83276d1f883264808521b70b33d98e4dec5d", 836 | "sha256:9569da9e78f0947b249370cb8fadf1015a193c359e7e442ac9ecc585d937f08d", 837 | "sha256:a3b73390511edd2db2d34ff09aa0b2c08be974c71b4c0505b4a048d5dc128c2b", 838 | "sha256:a4eddbe2a715b2dd3849afbdeacf1cc283160b24e09baf64fa5675f51940419d", 839 | "sha256:a5c6dbe09aff091adfa8c7cfc1a0e83fdb8021ddb2c183512775a14f1435fe16", 840 | "sha256:b63e3571b24a7959017573b6455e05b675050bbbea69408f35f3cb984ec54363", 841 | "sha256:bb350eb1060591d8e89d6bac4713d41006cd4d479f5e11db334a48ff8999512f", 842 | "sha256:bf6d987edd4a44dd2fa2723fca2790f9442ae4de2c8438e53fcb1befdf5d823a", 843 | "sha256:bfa6a679410b394600eafd16336b2ce8de43e9b13f7fb9247d84ef5ad2b45e91", 844 | "sha256:c856ec9b42e5af4fe2d8e75970fcc3a2c15925cbcc6e7a9bcb44583b10b95e80", 845 | "sha256:cea56288eeda8b7511d507bbe7790d89ae7049daa5f51ae31a35ae3c05408531", 846 | "sha256:ea212df6e5d3f60341aef46401d32fcfded85593af1d82b8b4a7a68cd67fdd6b", 847 | "sha256:f35567470ee6dbfb946f069ed5f5615b40edcbb5f1e6e1d3d2b114468d505fc6", 848 | "sha256:fbc20975eee093efa2071de80df7f972b7b35e560b213aafabcec7c0bd00bd8c", 849 | "sha256:ff4a8ad9638b7ca52313d8732f37ecd5fd3c8e3aff10a8ccb93176fd5b3812f6" 850 | ], 851 | "version": "==2021.8.3" 852 | }, 853 | "toml": { 854 | "hashes": [ 855 | "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", 856 | "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" 857 | ], 858 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", 859 | "version": "==0.10.2" 860 | }, 861 | "tomli": { 862 | "hashes": [ 863 | "sha256:8dd0e9524d6f386271a36b41dbf6c57d8e32fd96fd22b6584679dc569d20899f", 864 | "sha256:a5b75cb6f3968abb47af1b40c1819dc519ea82bcc065776a866e8d74c5ca9442" 865 | ], 866 | "markers": "python_version >= '3.6'", 867 | "version": "==1.2.1" 868 | }, 869 | "typing-extensions": { 870 | "hashes": [ 871 | "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", 872 | "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", 873 | "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" 874 | ], 875 | "version": "==3.10.0.0" 876 | }, 877 | "wrapt": { 878 | "hashes": [ 879 | "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7" 880 | ], 881 | "version": "==1.12.1" 882 | }, 883 | "yarl": { 884 | "hashes": [ 885 | "sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e", 886 | "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434", 887 | "sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366", 888 | "sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3", 889 | "sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec", 890 | "sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959", 891 | "sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e", 892 | "sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c", 893 | "sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6", 894 | "sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a", 895 | "sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6", 896 | "sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424", 897 | "sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e", 898 | "sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f", 899 | "sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50", 900 | "sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2", 901 | "sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc", 902 | "sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4", 903 | "sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970", 904 | "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10", 905 | "sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0", 906 | "sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406", 907 | "sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896", 908 | "sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643", 909 | "sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721", 910 | "sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478", 911 | "sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724", 912 | "sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e", 913 | "sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8", 914 | "sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96", 915 | "sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25", 916 | "sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76", 917 | "sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2", 918 | "sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2", 919 | "sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c", 920 | "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a", 921 | "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71" 922 | ], 923 | "markers": "python_version >= '3.6'", 924 | "version": "==1.6.3" 925 | } 926 | } 927 | } 928 | --------------------------------------------------------------------------------