pax_global_header00006660000000000000000000000064144463327220014521gustar00rootroot0000000000000052 comment=9078463ff5dd231f3e9f86637559cfea7c3e1b4f spamcheck-v1.10.1/000077500000000000000000000000001444633272200137255ustar00rootroot00000000000000spamcheck-v1.10.1/.coveragerc000066400000000000000000000001061444633272200160430ustar00rootroot00000000000000# .coveragerc to control coverage.py [run] branch = True source = app spamcheck-v1.10.1/.dockerignore000066400000000000000000000001421444633272200163760ustar00rootroot00000000000000* !*.py !api !app !classifiers !server !Makefile !Pipfile !Pipfile.lock !tests/integration/run.rb spamcheck-v1.10.1/.gitignore000066400000000000000000000003021444633272200157100ustar00rootroot00000000000000# Directories __pycache__/ tmp/ ssl/ # ML model files **/*.tflite **/*.pickle classifiers/ # Files *.pyc api/v1/*.py config/config.yml docs/api/v1/* ruby/spamcheck/*_pb.rb .DS_Store .coverage spamcheck-v1.10.1/.gitlab-ci.yml000066400000000000000000000103131444633272200163570ustar00rootroot00000000000000stages: - test - build - deploy - release variables: DOCKER_TLS_CERTDIR: "/certs" CONTAINER_IMAGE_COMMIT: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA CONTAINER_IMAGE_LATEST: $CI_REGISTRY_IMAGE:latest CONTAINER_IMAGE_TEST: $CI_REGISTRY_IMAGE:integration-tests IMAGE_TAG: $CI_COMMIT_SHORT_SHA # Image tag for deployment pipeline # GitLab Secure # https://docs.gitlab.com/ee/user/application_security/ include: - template: Security/Dependency-Scanning.gitlab-ci.yml - template: Security/License-Scanning.gitlab-ci.yml - template: Security/SAST.gitlab-ci.yml - template: Security/Secret-Detection.gitlab-ci.yml workflow: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Templates # Identify if source code changes. This is used to avoid triggering # a new build or deployment if only documentation or CI files change. .source_changes: &source_changes changes: - "Dockerfile" - "**/*.py" - "**/*.proto" - "Pipfile*" .docker_job: image: docker:dind services: - docker:dind before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY # Only test/build if source code has changed. # Unfortunately, due to limitations of "changes" this will always run when a branch # is first pushed so we only build once an MR is opened or running on $CI_DEFAULT_BRANCH. # # see: https://docs.gitlab.com/ee/ci/jobs/job_control.html#jobs-or-pipelines-run-unexpectedly-when-using-changes .test: image: python:3.9 stage: test rules: - if: $FORCE_DEPLOY != null - if: $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH <<: *source_changes .build: extends: .docker_job stage: build rules: - if: $FORCE_DEPLOY != null - !reference [.test, rules] # Only deploy if on default branch .deploy: stage: deploy rules: - if: $DEPLOY_STOP != null when: never - if: $FORCE_DEPLOY != null - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH <<: *source_changes # Same rules as deploy but in different stage .release: stage: release extends: .deploy # Jobs # Ensure VERSION file is updated if source code changes check version: stage: test image: name: registry.gitlab.com/gitlab-com/gl-security/engineering-and-research/automation-team/docker/check-version:latest entrypoint: [''] script: - git fetch origin $CI_DEFAULT_BRANCH:$CI_DEFAULT_BRANCH - check-version rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" <<: *source_changes lint: extends: .test script: - make lint test: extends: .test script: - make test coverage: '/^TOTAL.+?(\d+\%)$/' build: extends: .build script: - docker context create spamcheck - docker buildx create --use spamcheck - if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then PUSH="--push"; else PUSH="${FORCE_DEPLOY:+--push}"; fi - docker buildx build --provenance=false $PUSH --pull --platform linux/arm64/v8,linux/amd64 --tag $CONTAINER_IMAGE_COMMIT . build test image: extends: .build script: - docker build --pull --tag $CONTAINER_IMAGE_TEST -f Dockerfile.test . - docker push $CONTAINER_IMAGE_TEST rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH changes: - tests/integration/run.rb - Dockerfile.test deploy: extends: .deploy trigger: project: gitlab-private/gl-security/engineering-and-research/automation-team/kubernetes/spamcheck/spamcheck-py strategy: depend release image: extends: - .docker_job - .release script: - VERSION="$(head -n1 VERSION | xargs)" - docker context create spamcheck - docker buildx create --use spamcheck - docker buildx build --provenance=false --cache-from $CONTAINER_IMAGE_COMMIT --push --platform linux/arm64/v8,linux/amd64 --tag $CONTAINER_IMAGE_LATEST --tag $CI_REGISTRY_IMAGE:$VERSION . tag branch: extends: .release script: - VERSION="v$(head -n1 VERSION | xargs)" - git config user.name "gitlab-securitybot" - git config user.email "securitybot@gitlab.com" - git remote set-url origin "https://oauth2:${GITLAB_REPOSITORY_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" - git tag "$VERSION" - git push --tags spamcheck-v1.10.1/.pylintrc000066400000000000000000000436171444633272200156050ustar00rootroot00000000000000[MASTER] # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. extension-pkg-allow-list= # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. (This is an alternative name to extension-pkg-allow-list # for backward compatibility.) extension-pkg-whitelist= # Return non-zero exit code if any of these messages/categories are detected, # even if score is above --fail-under value. Syntax same as enable. Messages # specified are enabled, while categories only check already-enabled messages. fail-on= # Specify a score threshold to be exceeded before program exits with error. fail-under=10.0 # Files or directories to be skipped. They should be base names, not paths. ignore=CVS,client.py # Add files or directories matching the regex patterns to the ignore-list. The # regex matches against paths and can be in Posix or Windows format. ignore-paths= # Files or directories matching the regex patterns are skipped. The regex # matches against base names, not paths. ignore-patterns=(.)*_test\.py,test_(.)*\.py # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). #init-hook= # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the # number of processors available to use. jobs=1 # Control the amount of potential inferred values when inferring a single # object. This can help the performance when dealing with large functions or # complex, nested conditions. limit-inference-results=100 # List of plugins (as comma separated values of python module names) to load, # usually to register additional checkers. load-plugins= # Pickle collected data for later comparisons. persistent=yes # Minimum Python version to use for version dependent checks. Will default to # the version used to run pylint. py-version=3.9 # When enabled, pylint would attempt to guess common misconfiguration and emit # user-friendly hints instead of false-positive error messages. suggestion-mode=yes # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. confidence= # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this # option multiple times (only on the command line, not in the configuration # file where it should appear only once). You can also use "--disable=all" to # disable everything first and then reenable specific checks. For example, if # you want to run only the similarities checker, you can use "--disable=all # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". disable=raw-checker-failed, bad-inline-option, locally-disabled, file-ignored, suppressed-message, useless-suppression, deprecated-pragma, use-symbolic-message-instead # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). See also the "--disable" option for examples. enable=c-extension-no-member [REPORTS] # Python expression which should return a score less than or equal to 10. You # have access to the variables 'error', 'warning', 'refactor', and 'convention' # which contain the number of messages in each category, as well as 'statement' # which is the total number of statements analyzed. This score is used by the # global evaluation report (RP0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) # Template used to display messages. This is a python new-style format string # used to format the message information. See doc for all details. #msg-template= # Set the output format. Available formats are text, parseable, colorized, json # and msvs (visual studio). You can also give a reporter class, e.g. # mypackage.mymodule.MyReporterClass. output-format=text # Tells whether to display a full report or only the messages. reports=no # Activate the evaluation score. score=yes [REFACTORING] # Maximum number of nested blocks for function / method body max-nested-blocks=5 # Complete name of functions that never returns. When checking for # inconsistent-return-statements if a never returning function is called then # it will be considered as an explicit return statement and no message will be # printed. never-returning-functions=sys.exit,argparse.parse_error [LOGGING] # The type of string formatting that logging methods do. `old` means using % # formatting, `new` is for `{}` formatting. logging-format-style=old # Logging modules to check that the string format arguments are in logging # function parameter format. logging-modules=logging [SIMILARITIES] # Comments are removed from the similarity computation ignore-comments=yes # Docstrings are removed from the similarity computation ignore-docstrings=yes # Imports are removed from the similarity computation ignore-imports=no # Signatures are removed from the similarity computation ignore-signatures=no # Minimum lines number of a similarity. min-similarity-lines=4 [TYPECHECK] # List of decorators that produce context managers, such as # contextlib.contextmanager. Add to this list to register other decorators that # produce valid context managers. contextmanager-decorators=contextlib.contextmanager # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. generated-members= # Tells whether missing members accessed in mixin class should be ignored. A # class is considered mixin if its name matches the mixin-class-rgx option. ignore-mixin-members=yes # Tells whether to warn about missing members when the owner of the attribute # is inferred to be None. ignore-none=yes # This flag controls whether pylint should warn about no-member and similar # checks whenever an opaque object is returned when inferring. The inference # can return multiple potential results while evaluating a Python object, but # some branches might not be evaluated, which results in partial inference. In # that case, it might be useful to still emit no-member and other checks for # the rest of the inferred objects. ignore-on-opaque-inference=yes # List of class names for which member attributes should not be checked (useful # for classes with dynamically set attributes). This supports the use of # qualified names. ignored-classes=optparse.Values,thread._local,_thread._local # List of module names for which member attributes should not be checked # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis). It # supports qualified module names, as well as Unix pattern matching. ignored-modules=api.v1.health_pb2, api.v1.health_pb2_grpc, api.v1.spamcheck_pb2, api.v1.spamcheck_pb2_grpc # Show a hint with possible names when a member name was not found. The aspect # of finding the hint is based on edit distance. missing-member-hint=yes # The minimum edit distance a name should have in order to be considered a # similar match for a missing member name. missing-member-hint-distance=1 # The total number of similar names that should be taken in consideration when # showing a hint for a missing member. missing-member-max-choices=1 # Regex pattern to define which classes are considered mixins ignore-mixin- # members is set to 'yes' mixin-class-rgx=.*[Mm]ixin # List of decorators that change the signature of a decorated function. signature-mutators= [VARIABLES] # List of additional names supposed to be defined in builtins. Remember that # you should avoid defining new builtins when possible. additional-builtins= # Tells whether unused global variables should be treated as a violation. allow-global-unused-variables=yes # List of names allowed to shadow builtins allowed-redefined-builtins= # List of strings which can identify a callback function by name. A callback # name must start or end with one of those strings. callbacks=cb_, _cb # A regular expression matching the name of dummy variables (i.e. expected to # not be used). dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ # Argument names that match this expression will be ignored. Default to name # with leading underscore. ignored-argument-names=_.*|^ignored_|^unused_ # Tells whether we should check for unused import in __init__ files. init-import=no # List of qualified module names which can have objects that can redefine # builtins. redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io [BASIC] # Naming style matching correct argument names. argument-naming-style=snake_case # Regular expression matching correct argument names. Overrides argument- # naming-style. #argument-rgx= # Naming style matching correct attribute names. attr-naming-style=snake_case # Regular expression matching correct attribute names. Overrides attr-naming- # style. #attr-rgx= # Bad variable names which should always be refused, separated by a comma. bad-names=foo, bar, baz, toto, tutu, tata # Bad variable names regexes, separated by a comma. If names match any regex, # they will always be refused bad-names-rgxs= # Naming style matching correct class attribute names. class-attribute-naming-style=any # Regular expression matching correct class attribute names. Overrides class- # attribute-naming-style. #class-attribute-rgx= # Naming style matching correct class constant names. class-const-naming-style=UPPER_CASE # Regular expression matching correct class constant names. Overrides class- # const-naming-style. #class-const-rgx= # Naming style matching correct class names. class-naming-style=PascalCase # Regular expression matching correct class names. Overrides class-naming- # style. #class-rgx= # Naming style matching correct constant names. const-naming-style=UPPER_CASE # Regular expression matching correct constant names. Overrides const-naming- # style. #const-rgx= # Minimum line length for functions/classes that require docstrings, shorter # ones are exempt. docstring-min-length=-1 # Naming style matching correct function names. function-naming-style=snake_case # Regular expression matching correct function names. Overrides function- # naming-style. #function-rgx= # Good variable names which should always be accepted, separated by a comma. good-names=i, j, k, ex, Run, _ # Good variable names regexes, separated by a comma. If names match any regex, # they will always be accepted good-names-rgxs= # Include a hint for the correct naming format with invalid-name. include-naming-hint=no # Naming style matching correct inline iteration names. inlinevar-naming-style=any # Regular expression matching correct inline iteration names. Overrides # inlinevar-naming-style. #inlinevar-rgx= # Naming style matching correct method names. method-naming-style=snake_case # Regular expression matching correct method names. Overrides method-naming- # style. #method-rgx= # Naming style matching correct module names. module-naming-style=snake_case # Regular expression matching correct module names. Overrides module-naming- # style. #module-rgx= # Colon-delimited sets of names that determine each other's naming style when # the name regexes allow several styles. name-group= # Regular expression which should only match function or class names that do # not require a docstring. no-docstring-rgx=^_ # List of decorators that produce properties, such as abc.abstractproperty. Add # to this list to register other decorators that produce valid properties. # These decorators are taken in consideration only for invalid-name. property-classes=abc.abstractproperty # Naming style matching correct variable names. variable-naming-style=snake_case # Regular expression matching correct variable names. Overrides variable- # naming-style. #variable-rgx= [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes=FIXME, XXX, TODO # Regular expression of note tags to take in consideration. #notes-rgx= [FORMAT] # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. expected-line-ending-format= # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ # Number of spaces of indent required inside a hanging or continued line. indent-after-paren=4 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). indent-string=' ' # Maximum number of characters on a single line. max-line-length=100 # Maximum number of lines in a module. max-module-lines=1000 # Allow the body of a class to be on the same line as the declaration if body # contains single statement. single-line-class-stmt=no # Allow the body of an if to be on the same line as the test if there is no # else. single-line-if-stmt=no [STRING] # This flag controls whether inconsistent-quotes generates a warning when the # character used as a quote delimiter is used inconsistently within a module. check-quote-consistency=no # This flag controls whether the implicit-str-concat should generate a warning # on implicit string concatenation in sequences defined over several lines. check-str-concat-over-line-jumps=no [SPELLING] # Limits count of emitted suggestions for spelling mistakes. max-spelling-suggestions=4 # Spelling dictionary name. Available dictionaries: none. To make it work, # install the 'python-enchant' package. spelling-dict= # List of comma separated words that should be considered directives if they # appear and the beginning of a comment and should not be checked. spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: # List of comma separated words that should not be checked. spelling-ignore-words= # A path to a file that contains the private dictionary; one word per line. spelling-private-dict-file= # Tells whether to store unknown words to the private dictionary (see the # --spelling-private-dict-file option) instead of raising a message. spelling-store-unknown-words=no [IMPORTS] # List of modules that can be imported at any level, not just the top level # one. allow-any-import-level= # Allow wildcard imports from modules that define __all__. allow-wildcard-with-all=no # Analyse import fallback blocks. This can be used to support both Python 2 and # 3 compatible code, which means that the block might have code that exists # only in one or another interpreter, leading to false positives when analysed. analyse-fallback-blocks=no # Deprecated modules which should not be used, separated by a comma. deprecated-modules= # Output a graph (.gv or any supported image format) of external dependencies # to the given file (report RP0402 must not be disabled). ext-import-graph= # Output a graph (.gv or any supported image format) of all (i.e. internal and # external) dependencies to the given file (report RP0402 must not be # disabled). import-graph= # Output a graph (.gv or any supported image format) of internal dependencies # to the given file (report RP0402 must not be disabled). int-import-graph= # Force import order to recognize a module as part of the standard # compatibility libraries. known-standard-library= # Force import order to recognize a module as part of a third party library. known-third-party=enchant # Couples of modules and preferred modules, separated by a comma. preferred-modules= [DESIGN] # List of regular expressions of class ancestor names to ignore when counting # public methods (see R0903) exclude-too-few-public-methods= # List of qualified class names to ignore when counting class parents (see # R0901) ignored-parents= # Maximum number of arguments for function / method. max-args=5 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Maximum number of boolean expressions in an if statement (see R0916). max-bool-expr=5 # Maximum number of branch for function / method body. max-branches=12 # Maximum number of locals for function / method body. max-locals=15 # Maximum number of parents for a class (see R0901). max-parents=7 # Maximum number of public methods for a class (see R0904). max-public-methods=20 # Maximum number of return / yield for function / method body. max-returns=6 # Maximum number of statements in function / method body. max-statements=50 # Minimum number of public methods for a class (see R0903). min-public-methods=2 [CLASSES] # Warn about protected attribute access inside special methods check-protected-access-in-special-methods=no # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__, __new__, setUp, __post_init__ # List of member names, which should be excluded from the protected access # warning. exclude-protected=_asdict, _fields, _replace, _source, _make # List of valid names for the first argument in a class method. valid-classmethod-first-arg=cls # List of valid names for the first argument in a metaclass class method. valid-metaclass-classmethod-first-arg=cls [EXCEPTIONS] # Exceptions that will emit a warning when being caught. Defaults to # "BaseException, Exception". overgeneral-exceptions=BaseException, Exception spamcheck-v1.10.1/.ruby-version000066400000000000000000000000061444633272200163660ustar00rootroot000000000000002.7.4 spamcheck-v1.10.1/.tool-versions000066400000000000000000000000311444633272200165430ustar00rootroot00000000000000ruby 2.7.4 python 3.9.10 spamcheck-v1.10.1/CODEOWNERS000066400000000000000000000001721444633272200153200ustar00rootroot00000000000000# https://gitlab.com/help/user/project/code_owners.md * @gitlab-com/gl-security/engineering-and-research/automation-team spamcheck-v1.10.1/Dockerfile000066400000000000000000000040021444633272200157130ustar00rootroot00000000000000############################## # Build Image ############################## FROM python:3.9 as builder ENV PIPENV_VENV_IN_PROJECT 1 # For some reason pip thinks uname is in /usr/local/bin when running in arm but it is in /bin ENV PATH /bin:/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin # Install pipenv and other dependencies RUN pip install pipenv # Install python dependencies in /.venv and generate gRPC code COPY . . RUN pipenv install --deploy && make proto # Make a src directory to avoid multiple copy layers in the runtime image RUN mkdir ./src && \ mv api app server *.py ./src ############################## # Runtime Image ############################## FROM python:3.9-slim as runtime ENV LANG C.UTF-8 ENV LC_ALL C.UTF-8 ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONFAULTHANDLER 1 # Set minimum log level in tensorflow to error # This will cause GPU warnings to be omitted ENV TF_CPP_MIN_LOG_LEVEL 2 ARG UID=1000 ARG GID=1000 ARG CLASSIFIER_VERSION=2.3.1 # Copy python environment from builder COPY --from=builder /.venv /.venv ENV PATH="/.venv/bin:$PATH" WORKDIR /spamcheck # Copy code into runtime image COPY --from=builder /src . ADD "https://storage.googleapis.com/glsec-spamcheck-ml-artifacts/spam-classifier/${CLASSIFIER_VERSION}/gl-spam-classifier-${CLASSIFIER_VERSION}.tar.gz" ./classifiers.tar.gz RUN tar xvf classifiers.tar.gz && rm classifiers.tar.gz # Add non-root user to run spamchecklications # Run chmod to ensure everything works if container is run with different uid RUN groupadd -g $GID spamcheck && \ adduser \ --gecos "" \ --disabled-password \ --shell "/sbin/nologin" \ --no-create-home \ --gid $GID \ spamcheck && \ chown -R spamcheck:spamcheck . && \ chmod -R u=rw,g=rw,o=r,a+X . # Add spamcheck and health-check to path RUN ln -s /spamcheck/main.py /usr/local/bin/spamcheck && \ ln -s /spamcheck/health_check.py /usr/local/bin/health-check && \ chmod 755 main.py health_check.py USER spamcheck # Export gRPC port EXPOSE 8001 ENTRYPOINT ["spamcheck"] spamcheck-v1.10.1/Dockerfile.test000066400000000000000000000001671444633272200167010ustar00rootroot00000000000000FROM ruby:latest RUN gem install spamcheck COPY tests/integration/run.rb /tests/run.rb CMD ["ruby", "/tests/run.rb"] spamcheck-v1.10.1/Gemfile000066400000000000000000000001311444633272200152130ustar00rootroot00000000000000source 'https://rubygems.org' group :development do gem 'grpc-tools', '~> 1.35.0' end spamcheck-v1.10.1/Gemfile.lock000066400000000000000000000002251444633272200161460ustar00rootroot00000000000000GEM remote: https://rubygems.org/ specs: grpc-tools (1.35.0) PLATFORMS ruby DEPENDENCIES grpc-tools (~> 1.35.0) BUNDLED WITH 2.1.4 spamcheck-v1.10.1/LICENSE000066400000000000000000000023761444633272200147420ustar00rootroot00000000000000With the exception of the software in the tools/preprocessor_helper subdirectory which is subject to the licenese stated therein, all software in this directory is subject to the following license: MIT License Copyright (c) 2021 - present GitLab, B.V. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. spamcheck-v1.10.1/Makefile000066400000000000000000000023211444633272200153630ustar00rootroot00000000000000# Set python virtual environment export PIPENV_VENV_IN_PROJECT=1 VENV := ./.venv SHELL = /usr/bin/env bash -eo pipefail SPAMCHECK_PROTO_PATH := ${PWD}/api/v1 SPAMCHECK_PROTO := ${SPAMCHECK_PROTO_PATH}/*.proto RUBY_PROTO_PATH := ${PWD}/ruby/spamcheck deps: ${VENV} ${VENV}: pip install pipenv pipenv install --dev # Generate Python protobuf files proto: pipenv run python -m grpc_tools.protoc \ --proto_path=${PWD} \ --python_out=${PWD} \ --grpc_python_out=${PWD} \ ${SPAMCHECK_PROTO} # Generate Ruby protobuf files proto_ruby: gem install bundler bundle install bundle exec grpc_tools_ruby_protoc \ --proto_path=${SPAMCHECK_PROTO_PATH} \ --ruby_out=${RUBY_PROTO_PATH} \ --grpc_out=${RUBY_PROTO_PATH} \ ${SPAMCHECK_PROTO} gem: proto proto_ruby gem build spamcheck.gemspec ./tests/gem/test.sh || (rm -f spamcheck*.gem && exit 1) lint: ${VENV} proto pipenv run pylint --rcfile=.pylintrc *.py app server test: ${VENV} proto ENV=test PYTHONDONTWRITEBYTECODE=1 pipenv run coverage run -m pytest -W ignore::DeprecationWarning pipenv run coverage report -m run: ${VENV} proto pipenv run python main.py clean: rm -rf ${SPAMCHECK_PROTO_PATH}/*.py .PHONY: proto proto_ruby gem test run clean spamcheck-v1.10.1/Pipfile000066400000000000000000000006311444633272200152400ustar00rootroot00000000000000[[source]] url = "https://pypi.org/simple" verify_ssl = true name = "pypi" [packages] numpy = "*" grpcio-reflection = "*" grpcio-tools = "*" python-json-logger = "*" ulid-py = "*" tflite-runtime = "*" vyper-config = "*" google-cloud-pubsub = "*" cloudevents = "*" google-cloud-storage = "*" [dev-packages] coverage = "*" pytest = "*" pylint = "*" ipython = "*" rope = "*" [requires] python_version = "3.9" spamcheck-v1.10.1/Pipfile.lock000066400000000000000000002152171444633272200161770ustar00rootroot00000000000000{ "_meta": { "hash": { "sha256": "b8755b0fc6c95f60683738c378c1db883b0c0e7a9dd996408d86e3fb61383d20" }, "pipfile-spec": 6, "requires": { "python_version": "3.9" }, "sources": [ { "name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": true } ] }, "default": { "cachetools": { "hashes": [ "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14", "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4" ], "markers": "python_version ~= '3.7'", "version": "==5.3.0" }, "certifi": { "hashes": [ "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" ], "markers": "python_version >= '3.6'", "version": "==2022.12.7" }, "charset-normalizer": { "hashes": [ "sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b", "sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42", "sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d", "sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b", "sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a", "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59", "sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154", "sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1", "sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c", "sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a", "sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d", "sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6", "sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b", "sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b", "sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783", "sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5", "sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918", "sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555", "sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639", "sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786", "sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e", "sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed", "sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820", "sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8", "sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3", "sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541", "sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14", "sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be", "sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e", "sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76", "sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b", "sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c", "sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b", "sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3", "sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc", "sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6", "sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59", "sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4", "sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d", "sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d", "sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3", "sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a", "sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea", "sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6", "sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e", "sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603", "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24", "sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a", "sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58", "sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678", "sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a", "sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c", "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6", "sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18", "sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174", "sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317", "sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f", "sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc", "sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837", "sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41", "sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c", "sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579", "sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753", "sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8", "sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291", "sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087", "sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866", "sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3", "sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d", "sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1", "sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca", "sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e", "sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db", "sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72", "sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d", "sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc", "sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539", "sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d", "sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af", "sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b", "sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602", "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f", "sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478", "sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c", "sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e", "sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479", "sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7", "sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8" ], "markers": "python_version >= '3.6'", "version": "==3.0.1" }, "cloudevents": { "hashes": [ "sha256:1011459d56d8f0184a46456f5d72632a2565f18171e51b33e06f643e723d30c9", "sha256:8beb27503f97e215f886f73c17671012e96bb6268137fb3b2f9ef552727ab5b1" ], "index": "pypi", "version": "==1.9.0" }, "deprecation": { "hashes": [ "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a" ], "version": "==2.1.0" }, "distconfig3": { "hashes": [ "sha256:7d2c7f30a57ef494c5683270587ba7593318746c6e22b9b8953e288c9c303c65", "sha256:823e35ae044677e8aa77bed8d9be0780862a2500c63cf95ce85544b9d3d9fc89" ], "version": "==1.0.1" }, "google-api-core": { "hashes": [ "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22", "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e" ], "markers": "python_version >= '3.7'", "version": "==2.11.0" }, "google-auth": { "hashes": [ "sha256:5045648c821fb72384cdc0e82cc326df195f113a33049d9b62b74589243d2acc", "sha256:ed7057a101af1146f0554a769930ac9de506aeca4fd5af6543ebe791851a9fbd" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", "version": "==2.16.0" }, "google-cloud-core": { "hashes": [ "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe", "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a" ], "markers": "python_version >= '3.7'", "version": "==2.3.2" }, "google-cloud-pubsub": { "hashes": [ "sha256:a035d63ef209a28edd54c8c1fcf66cc25b207ed6edb1259a973b909c723ecf80", "sha256:e2714f07b750458beaf5b07b670ea7b6058ee155c021c987d0b8e6a40bf3446f" ], "index": "pypi", "version": "==2.14.0" }, "google-cloud-storage": { "hashes": [ "sha256:1ac2d58d2d693cb1341ebc48659a3527be778d9e2d8989697a2746025928ff17", "sha256:f78a63525e72dd46406b255bbdf858a22c43d6bad8dc5bdeb7851a42967e95a1" ], "index": "pypi", "version": "==2.7.0" }, "google-crc32c": { "hashes": [ "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a", "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876", "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c", "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289", "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298", "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02", "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f", "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2", "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a", "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb", "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210", "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5", "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee", "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c", "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a", "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314", "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd", "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65", "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37", "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4", "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13", "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894", "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31", "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e", "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709", "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740", "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc", "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d", "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c", "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c", "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d", "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906", "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61", "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57", "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c", "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a", "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438", "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946", "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7", "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96", "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091", "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae", "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d", "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88", "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2", "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd", "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541", "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728", "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178", "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968", "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346", "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8", "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93", "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7", "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273", "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462", "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94", "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd", "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e", "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57", "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b", "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9", "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a", "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100", "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325", "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183", "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556", "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4" ], "markers": "python_version >= '3.7'", "version": "==1.5.0" }, "google-resumable-media": { "hashes": [ "sha256:15b8a2e75df42dc6502d1306db0bce2647ba6013f9cd03b6e17368c0886ee90a", "sha256:831e86fd78d302c1a034730a0c6e5369dd11d37bad73fa69ca8998460d5bae8d" ], "markers": "python_version >= '3.7'", "version": "==2.4.1" }, "googleapis-common-protos": { "hashes": [ "sha256:c727251ec025947d545184ba17e3578840fc3a24a0516a020479edab660457df", "sha256:ca3befcd4580dab6ad49356b46bf165bb68ff4b32389f028f1abd7c10ab9519a" ], "markers": "python_version >= '3.7'", "version": "==1.58.0" }, "grpc-google-iam-v1": { "hashes": [ "sha256:2bc4b8fdf22115a65d751c9317329322602c39b7c86a289c9b72d228d960ef5f", "sha256:5c10f3d8dc2d88678ab1a9b0cb5482735c5efee71e6c0cd59f872eef22913f5c" ], "markers": "python_version >= '3.7'", "version": "==0.12.6" }, "grpcio": { "hashes": [ "sha256:094e64236253590d9d4075665c77b329d707b6fca864dd62b144255e199b4f87", "sha256:0dc5354e38e5adf2498312f7241b14c7ce3484eefa0082db4297189dcbe272e6", "sha256:0e1a9e1b4a23808f1132aa35f968cd8e659f60af3ffd6fb00bcf9a65e7db279f", "sha256:0fb93051331acbb75b49a2a0fd9239c6ba9528f6bdc1dd400ad1cb66cf864292", "sha256:16c71740640ba3a882f50b01bf58154681d44b51f09a5728180a8fdc66c67bd5", "sha256:172405ca6bdfedd6054c74c62085946e45ad4d9cec9f3c42b4c9a02546c4c7e9", "sha256:17ec9b13cec4a286b9e606b48191e560ca2f3bbdf3986f91e480a95d1582e1a7", "sha256:22b011674090594f1f3245960ced7386f6af35485a38901f8afee8ad01541dbd", "sha256:24ac1154c4b2ab4a0c5326a76161547e70664cd2c39ba75f00fc8a2170964ea2", "sha256:257478300735ce3c98d65a930bbda3db172bd4e00968ba743e6a1154ea6edf10", "sha256:29cb97d41a4ead83b7bcad23bdb25bdd170b1e2cba16db6d3acbb090bc2de43c", "sha256:2b170eaf51518275c9b6b22ccb59450537c5a8555326fd96ff7391b5dd75303c", "sha256:31bb6bc7ff145e2771c9baf612f4b9ebbc9605ccdc5f3ff3d5553de7fc0e0d79", "sha256:3c2b3842dcf870912da31a503454a33a697392f60c5e2697c91d133130c2c85d", "sha256:3f9b0023c2c92bebd1be72cdfca23004ea748be1813a66d684d49d67d836adde", "sha256:471d39d3370ca923a316d49c8aac66356cea708a11e647e3bdc3d0b5de4f0a40", "sha256:49d680356a975d9c66a678eb2dde192d5dc427a7994fb977363634e781614f7c", "sha256:4c4423ea38a7825b8fed8934d6d9aeebdf646c97e3c608c3b0bcf23616f33877", "sha256:506b9b7a4cede87d7219bfb31014d7b471cfc77157da9e820a737ec1ea4b0663", "sha256:538d981818e49b6ed1e9c8d5e5adf29f71c4e334e7d459bf47e9b7abb3c30e09", "sha256:59dffade859f157bcc55243714d57b286da6ae16469bf1ac0614d281b5f49b67", "sha256:5a6ebcdef0ef12005d56d38be30f5156d1cb3373b52e96f147f4a24b0ddb3a9d", "sha256:5dca372268c6ab6372d37d6b9f9343e7e5b4bc09779f819f9470cd88b2ece3c3", "sha256:6df3b63538c362312bc5fa95fb965069c65c3ea91d7ce78ad9c47cab57226f54", "sha256:6f0b89967ee11f2b654c23b27086d88ad7bf08c0b3c2a280362f28c3698b2896", "sha256:75e29a90dc319f0ad4d87ba6d20083615a00d8276b51512e04ad7452b5c23b04", "sha256:7942b32a291421460d6a07883033e392167d30724aa84987e6956cd15f1a21b9", "sha256:9235dcd5144a83f9ca6f431bd0eccc46b90e2c22fe27b7f7d77cabb2fb515595", "sha256:97d67983189e2e45550eac194d6234fc38b8c3b5396c153821f2d906ed46e0ce", "sha256:9ff42c5620b4e4530609e11afefa4a62ca91fa0abb045a8957e509ef84e54d30", "sha256:a8a0b77e992c64880e6efbe0086fe54dfc0bbd56f72a92d9e48264dcd2a3db98", "sha256:aacb54f7789ede5cbf1d007637f792d3e87f1c9841f57dd51abf89337d1b8472", "sha256:bc59f7ba87972ab236f8669d8ca7400f02a0eadf273ca00e02af64d588046f02", "sha256:cc2bece1737b44d878cc1510ea04469a8073dbbcdd762175168937ae4742dfb3", "sha256:cd3baccea2bc5c38aeb14e5b00167bd4e2373a373a5e4d8d850bd193edad150c", "sha256:dad6533411d033b77f5369eafe87af8583178efd4039c41d7515d3336c53b4f1", "sha256:e223a9793522680beae44671b9ed8f6d25bbe5ddf8887e66aebad5e0686049ef", "sha256:e473525c28251558337b5c1ad3fa969511e42304524a4e404065e165b084c9e4", "sha256:e4ef09f8997c4be5f3504cefa6b5c6cc3cf648274ce3cede84d4342a35d76db6", "sha256:e6dfc2b6567b1c261739b43d9c59d201c1b89e017afd9e684d85aa7a186c9f7a", "sha256:eacad297ea60c72dd280d3353d93fb1dcca952ec11de6bb3c49d12a572ba31dd", "sha256:f1158bccbb919da42544a4d3af5d9296a3358539ffa01018307337365a9a0c64", "sha256:f1fec3abaf274cdb85bf3878167cfde5ad4a4d97c68421afda95174de85ba813", "sha256:f96ace1540223f26fbe7c4ebbf8a98e3929a6aa0290c8033d12526847b291c0f", "sha256:fbdbe9a849854fe484c00823f45b7baab159bdd4a46075302281998cb8719df5" ], "markers": "python_version >= '3.7'", "version": "==1.51.1" }, "grpcio-reflection": { "hashes": [ "sha256:b70af764a83e42a44f65df1edb232e972ab69e72bc7fbbad481e66c29a9d8cb8", "sha256:c07a93c0c36ef88fe475744289863b4787005eff4de0cc04213ecad718b01aae" ], "index": "pypi", "version": "==1.51.1" }, "grpcio-status": { "hashes": [ "sha256:a52cbdc4b18f325bfc13d319ae7c7ae7a0fee07f3d9a005504d6097896d7a495", "sha256:ac2617a3095935ebd785e2228958f24b10a0d527a0c9eb5a0863c784f648a816" ], "markers": "python_version >= '3.6'", "version": "==1.51.1" }, "grpcio-tools": { "hashes": [ "sha256:048793747339f327ea091d8f022c6756d89713d8080dffde5ce7380cc348ea8e", "sha256:055819992ddd30c642a7fd6f344a03747be3afa95cb910f8a2e5efaabd41cde5", "sha256:0a218f64e667f3332b74080bdc5440aaf0fa6700ae07a0b54ecf085aaef2aa9f", "sha256:14e82c2b3ee7e300611c2c729d411b3b911e4cca5f4ec14787457a2fb72ff9d4", "sha256:15b8acf4eaa0ebe37e2f69108de49efd935b7abe9c7e58ba737490b99906aa76", "sha256:16b8b915625dc6eb2ea7efdfb06f1fae44a9066c9016453a2ca120c034f33090", "sha256:1c44b57a6770b78a1eafe355878ff1ec59a2fa07455a2cbd522c071eedae04d4", "sha256:2281180490c475d09b7aa05dabafa5e09de9902176931e7295113f636c2b5360", "sha256:27113b354f7587684eb55125733e6e5be1f489458abfe12344dabd918d8dcc54", "sha256:331a897306adeec3c67470431ea8d8b4972b689d32966f94506d91f4dac20952", "sha256:392ad4cd004f7b843cf7d916d9a15b2d6585965bfef235be1c88d8f8649777e5", "sha256:3a671466158ed74c07ee070fb940ed783acf59ba6e6e53cb4de8fd63819c6c7f", "sha256:40ef70e8c5d0310dedff9af502b520b4c7e215bce94094527fb959150a0c594a", "sha256:4957f1ffa16598aa5379505fcbaeb47d65693a46b0817f4ee61db76707092aeb", "sha256:49624394805568acd7d767dea5a00d970fca5ad8f395fe0161eeea0de5133eba", "sha256:4e3249a2ec435b3b972610c66c8a714c188844500d564c910f57a2771dc61978", "sha256:531586c5598a99658249f3c5e92826d6d2bb117abd6ffc88527d1e1d9eaef924", "sha256:566809d9942e78821b279af70f3cf159a328127f9f3d5fee8d83ad8b2d27b2fe", "sha256:64d8ad369417759f5fdb8ffb7cbd6374fecc06ab51c9a226dee9bbd7d311c3b5", "sha256:674b340f2f7bb2adbc3f15144bd37ce5ea83239f78b68dbbd0ea3cba00107e2b", "sha256:67b304282cad38642587ebae68617e450e1ad4fa1c0c8b19e9e30274dbb32716", "sha256:6b83d7fc2597c6d392c225177d1fbbcff74900f8cc40b33236987fd1ff841330", "sha256:6d6626a6e4dbe843df96dc8c08dd244d2191a75324f54bfa4ebaa3e76b0b1958", "sha256:6e72a30be1746ea0749a8486d0ca0120c0b2757fe84fc246a5144b1ef66d7b89", "sha256:794f26a09b70f4f101df5cf54c6c12dc1b65747ab1dee5bda02c2991389ade56", "sha256:79c06d2577cb4d977922bbf01234de3b20f73d1784d3cbe3179deee1bdb9a60b", "sha256:87bc5f3e3698c65907d397003c64d25c3ea84e3d6aa46dac133bd98bf66835ee", "sha256:8e62d23d3fed9d4f81738f98dd193dbd2e21aed4a8f0dd715e75b5439e649727", "sha256:98777b5031f1b3c58b688815ffa83435c103b2152c26eb144f80f4a4bb34addb", "sha256:9906fb6bf6d9c30c23d85153f12d130f44325afe8f9ebe58aa7a6c82ecade9d8", "sha256:9dfe6c12b0e2c07f6a4a91a9912ef4e5bd007672533891a44e6f433ffbf7c3b1", "sha256:a66b3a5d18a7615f0f828b72e2d2935751459c89cc4725e56bdfb3d2cd93281f", "sha256:aab24a342642329de38139cb26f8492882ca0d8551bb87f6530bcc613945a0d0", "sha256:b4fb8ed6d29f2d6cf03ef99ffaad635bbc132a59be77013691392fe557e67144", "sha256:c4649af7f5d9553975ee66b6bfae20a84be779f13e163fa835e782961895e63c", "sha256:ccd37165d7a3e93f460096a2eb62b7a9c1ebe5c424eaee42d8e92740d0c8f6bc", "sha256:d5e033c04b416afcddd5231b3ff94a34fb5d26fba2416eb940e69b05f22cfd25", "sha256:d7b186183515ad6b8584ffe4bd820b72b00f6e7d121fb1c36294edeea9092313", "sha256:d8cc862a1ad30f94528d66cc6f95fb9e659005e568313e54a23550535b649573", "sha256:de51a0a71845b854f6a5967756c893c96bd03e37f39e5dce87b4f409dac36ee2", "sha256:e9abc03d67793b1bf33dc766caa69a3333f9db029869ba6e8fc6cd9c251c0080", "sha256:ecf1494cb695afead36995534f787761ee33fb9e116b23030113a37fe6057a83", "sha256:f06bb0753b7cecbff154b523cfb8f45dee2c31b0a4c72bed7da44c57f1cba113", "sha256:f336ad9be661d92fa45940e74e8ff3d78e67ebe9b4f7ea8774b2d680c17aeb6c", "sha256:f6caf36e7752728329a28f93afec7c4ec9015fc1c6e4460bd1eb0f3737e1c55a" ], "index": "pypi", "version": "==1.51.1" }, "idna": { "hashes": [ "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" ], "markers": "python_version >= '3.5'", "version": "==3.4" }, "numpy": { "hashes": [ "sha256:0044f7d944ee882400890f9ae955220d29b33d809a038923d88e4e01d652acd9", "sha256:0e3463e6ac25313462e04aea3fb8a0a30fb906d5d300f58b3bc2c23da6a15398", "sha256:179a7ef0889ab769cc03573b6217f54c8bd8e16cef80aad369e1e8185f994cd7", "sha256:2386da9a471cc00a1f47845e27d916d5ec5346ae9696e01a8a34760858fe9dd2", "sha256:26089487086f2648944f17adaa1a97ca6aee57f513ba5f1c0b7ebdabbe2b9954", "sha256:28bc9750ae1f75264ee0f10561709b1462d450a4808cd97c013046073ae64ab6", "sha256:28e418681372520c992805bb723e29d69d6b7aa411065f48216d8329d02ba032", "sha256:442feb5e5bada8408e8fcd43f3360b78683ff12a4444670a7d9e9824c1817d36", "sha256:6ec0c021cd9fe732e5bab6401adea5a409214ca5592cd92a114f7067febcba0c", "sha256:7094891dcf79ccc6bc2a1f30428fa5edb1e6fb955411ffff3401fb4ea93780a8", "sha256:84e789a085aabef2f36c0515f45e459f02f570c4b4c4c108ac1179c34d475ed7", "sha256:87a118968fba001b248aac90e502c0b13606721b1343cdaddbc6e552e8dfb56f", "sha256:8e669fbdcdd1e945691079c2cae335f3e3a56554e06bbd45d7609a6cf568c700", "sha256:ad2925567f43643f51255220424c23d204024ed428afc5aad0f86f3ffc080086", "sha256:b0677a52f5d896e84414761531947c7a330d1adc07c3a4372262f25d84af7bf7", "sha256:b07b40f5fb4fa034120a5796288f24c1fe0e0580bbfff99897ba6267af42def2", "sha256:b09804ff570b907da323b3d762e74432fb07955701b17b08ff1b5ebaa8cfe6a9", "sha256:b162ac10ca38850510caf8ea33f89edcb7b0bb0dfa5592d59909419986b72407", "sha256:b31da69ed0c18be8b77bfce48d234e55d040793cebb25398e2a7d84199fbc7e2", "sha256:caf65a396c0d1f9809596be2e444e3bd4190d86d5c1ce21f5fc4be60a3bc5b36", "sha256:cfa1161c6ac8f92dea03d625c2d0c05e084668f4a06568b77a25a89111621566", "sha256:dae46bed2cb79a58d6496ff6d8da1e3b95ba09afeca2e277628171ca99b99db1", "sha256:ddc7ab52b322eb1e40521eb422c4e0a20716c271a306860979d450decbb51b8e", "sha256:de92efa737875329b052982e37bd4371d52cabf469f83e7b8be9bb7752d67e51", "sha256:e274f0f6c7efd0d577744f52032fdd24344f11c5ae668fe8d01aac0422611df1", "sha256:ed5fb71d79e771ec930566fae9c02626b939e37271ec285e9efaf1b5d4370e7d", "sha256:ef85cf1f693c88c1fd229ccd1055570cb41cdf4875873b7728b6301f12cd05bf", "sha256:f1b739841821968798947d3afcefd386fa56da0caf97722a5de53e07c4ccedc7" ], "index": "pypi", "version": "==1.24.1" }, "packaging": { "hashes": [ "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2", "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97" ], "markers": "python_version >= '3.7'", "version": "==23.0" }, "proto-plus": { "hashes": [ "sha256:0e8cda3d5a634d9895b75c573c9352c16486cb75deb0e078b5fda34db4243165", "sha256:de34e52d6c9c6fcd704192f09767cb561bb4ee64e70eede20b0834d841f0be4d" ], "markers": "python_version >= '3.6'", "version": "==1.22.2" }, "protobuf": { "hashes": [ "sha256:1f22ac0ca65bb70a876060d96d914dae09ac98d114294f77584b0d2644fa9c30", "sha256:237216c3326d46808a9f7c26fd1bd4b20015fb6867dc5d263a493ef9a539293b", "sha256:27f4d15021da6d2b706ddc3860fac0a5ddaba34ab679dc182b60a8bb4e1121cc", "sha256:299ea899484ee6f44604deb71f424234f654606b983cb496ea2a53e3c63ab791", "sha256:3d164928ff0727d97022957c2b849250ca0e64777ee31efd7d6de2e07c494717", "sha256:6ab80df09e3208f742c98443b6166bcb70d65f52cfeb67357d52032ea1ae9bec", "sha256:78a28c9fa223998472886c77042e9b9afb6fe4242bd2a2a5aced88e3f4422aa7", "sha256:7cd532c4566d0e6feafecc1059d04c7915aec8e182d1cf7adee8b24ef1e2e6ab", "sha256:89f9149e4a0169cddfc44c74f230d7743002e3aa0b9472d8c28f0388102fc4c2", "sha256:a53fd3f03e578553623272dc46ac2f189de23862e68565e83dde203d41b76fc5", "sha256:b135410244ebe777db80298297a97fbb4c862c881b4403b71bac9d4107d61fd1", "sha256:b98d0148f84e3a3c569e19f52103ca1feacdac0d2df8d6533cf983d1fda28462", "sha256:d1736130bce8cf131ac7957fa26880ca19227d4ad68b4888b3be0dea1f95df97", "sha256:f45460f9ee70a0ec1b6694c6e4e348ad2019275680bd68a1d9314b8c7e01e574" ], "markers": "python_version >= '3.7'", "version": "==4.21.12" }, "pyasn1": { "hashes": [ "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359", "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576", "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf", "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7", "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d", "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00", "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8", "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86", "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12", "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776", "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba", "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2", "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3" ], "version": "==0.4.8" }, "pyasn1-modules": { "hashes": [ "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8", "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199", "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811", "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed", "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4", "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e", "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74", "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb", "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45", "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd", "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0", "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d", "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405" ], "version": "==0.2.8" }, "python-json-logger": { "hashes": [ "sha256:3b03487b14eb9e4f77e4fc2a023358b5394b82fd89cecf5586259baed57d8c6f", "sha256:764d762175f99fcc4630bd4853b09632acb60a6224acb27ce08cd70f0b1b81bd" ], "index": "pypi", "version": "==2.0.4" }, "pyyaml": { "hashes": [ "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" ], "markers": "python_version >= '3.6'", "version": "==6.0" }, "requests": { "hashes": [ "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa", "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf" ], "markers": "python_version >= '3.7' and python_version < '4'", "version": "==2.28.2" }, "rsa": { "hashes": [ "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" ], "markers": "python_version >= '3.6'", "version": "==4.9" }, "setuptools": { "hashes": [ "sha256:6f590d76b713d5de4e49fe4fbca24474469f53c83632d5d0fd056f7ff7e8112b", "sha256:ac4008d396bc9cd983ea483cb7139c0240a07bbc74ffb6232fceffedc6cf03a8" ], "markers": "python_version >= '3.7'", "version": "==66.1.1" }, "six": { "hashes": [ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.16.0" }, "tflite-runtime": { "hashes": [ "sha256:086f5ac54e6b6a73b75fa91ea63a7c8a71274272c787f39ed1ebeab2d570595c", "sha256:1cf50a6c51147d70299f4469f6cfab732f38f6ee7999df287155513e742360e6", "sha256:50832befa76d69de082394716846e12d85fa8257f3ddb0a4755479bdb0a47083", "sha256:6a784dd07df27316d5a800b60ce2f448572b6ad7ea5372b93c931818d6bda482", "sha256:7ec1652408e33d0d4958fb830be00fd9829e2bf3b901e6a02b0b9186b2d2e61c", "sha256:a3e53e625626312f9384aefa72458fd2d756a57e0a98394096300c3c1183caf8", "sha256:aad98db64962a515a90deaf38110181b3eb4cafbe9654673a71bda147e1c2576", "sha256:cf018122630db1584ecd7645b77e87814edf4b9b6f29a1bbde188009e644737f", "sha256:e15ebb86a1039cbbe9132cb3497466e248d401b03b963b6d4045f732d1a66e2a" ], "index": "pypi", "version": "==2.11.0" }, "toml": { "hashes": [ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.10.2" }, "ulid-py": { "hashes": [ "sha256:b56a0f809ef90d6020b21b89a87a48edc7c03aea80e5ed5174172e82d76e3987", "sha256:dc6884be91558df077c3011b9fb0c87d1097cb8fc6534b11f310161afd5738f0" ], "index": "pypi", "version": "==1.1.0" }, "urllib3": { "hashes": [ "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72", "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", "version": "==1.26.14" }, "vyper-config": { "hashes": [ "sha256:0aa3615b5d33ca9c1a1b1947a3311e08a5742dc549c866b5e6558da6528c5cff" ], "index": "pypi", "version": "==1.1.1" }, "watchdog": { "hashes": [ "sha256:102a60093090fc3ff76c983367b19849b7cc24ec414a43c0333680106e62aae1", "sha256:17f1708f7410af92ddf591e94ae71a27a13974559e72f7e9fde3ec174b26ba2e", "sha256:195ab1d9d611a4c1e5311cbf42273bc541e18ea8c32712f2fb703cfc6ff006f9", "sha256:4cb5ecc332112017fbdb19ede78d92e29a8165c46b68a0b8ccbd0a154f196d5e", "sha256:5100eae58133355d3ca6c1083a33b81355c4f452afa474c2633bd2fbbba398b3", "sha256:61fdb8e9c57baf625e27e1420e7ca17f7d2023929cd0065eb79c83da1dfbeacd", "sha256:6ccd8d84b9490a82b51b230740468116b8205822ea5fdc700a553d92661253a3", "sha256:6e01d699cd260d59b84da6bda019dce0a3353e3fcc774408ae767fe88ee096b7", "sha256:748ca797ff59962e83cc8e4b233f87113f3cf247c23e6be58b8a2885c7337aa3", "sha256:83a7cead445008e880dbde833cb9e5cc7b9a0958edb697a96b936621975f15b9", "sha256:8586d98c494690482c963ffb24c49bf9c8c2fe0589cec4dc2f753b78d1ec301d", "sha256:8b5cde14e5c72b2df5d074774bdff69e9b55da77e102a91f36ef26ca35f9819c", "sha256:8c28c23972ec9c524967895ccb1954bc6f6d4a557d36e681a36e84368660c4ce", "sha256:967636031fa4c4955f0f3f22da3c5c418aa65d50908d31b73b3b3ffd66d60640", "sha256:96cbeb494e6cbe3ae6aacc430e678ce4b4dd3ae5125035f72b6eb4e5e9eb4f4e", "sha256:978a1aed55de0b807913b7482d09943b23a2d634040b112bdf31811a422f6344", "sha256:a09483249d25cbdb4c268e020cb861c51baab2d1affd9a6affc68ffe6a231260", "sha256:a480d122740debf0afac4ddd583c6c0bb519c24f817b42ed6f850e2f6f9d64a8", "sha256:adaf2ece15f3afa33a6b45f76b333a7da9256e1360003032524d61bdb4c422ae", "sha256:bc43c1b24d2f86b6e1cc15f68635a959388219426109233e606517ff7d0a5a73", "sha256:c27d8c1535fd4474e40a4b5e01f4ba6720bac58e6751c667895cbc5c8a7af33c", "sha256:cdcc23c9528601a8a293eb4369cbd14f6b4f34f07ae8769421252e9c22718b6f", "sha256:cece1aa596027ff56369f0b50a9de209920e1df9ac6d02c7f9e5d8162eb4f02b", "sha256:d0f29fd9f3f149a5277929de33b4f121a04cf84bb494634707cfa8ea8ae106a8", "sha256:d6b87477752bd86ac5392ecb9eeed92b416898c30bd40c7e2dd03c3146105646", "sha256:e038be858425c4f621900b8ff1a3a1330d9edcfeaa1c0468aeb7e330fb87693e", "sha256:e618a4863726bc7a3c64f95c218437f3349fb9d909eb9ea3a1ed3b567417c661", "sha256:f8ac23ff2c2df4471a61af6490f847633024e5aa120567e08d07af5718c9d092" ], "markers": "python_version >= '3.6'", "version": "==2.2.1" } }, "develop": { "astroid": { "hashes": [ "sha256:14c1603c41cc61aae731cad1884a073c4645e26f126d13ac8346113c95577f3b", "sha256:6afc22718a48a689ca24a97981ad377ba7fb78c133f40335dfd16772f29bcfb1" ], "markers": "python_full_version >= '3.7.2'", "version": "==2.13.3" }, "asttokens": { "hashes": [ "sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3", "sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c" ], "version": "==2.2.1" }, "attrs": { "hashes": [ "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836", "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99" ], "markers": "python_version >= '3.6'", "version": "==22.2.0" }, "backcall": { "hashes": [ "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e", "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255" ], "version": "==0.2.0" }, "coverage": { "hashes": [ "sha256:051afcbd6d2ac39298d62d340f94dbb6a1f31de06dfaf6fcef7b759dd3860c45", "sha256:0a1890fca2962c4f1ad16551d660b46ea77291fba2cc21c024cd527b9d9c8809", "sha256:0ee30375b409d9a7ea0f30c50645d436b6f5dfee254edffd27e45a980ad2c7f4", "sha256:13250b1f0bd023e0c9f11838bdeb60214dd5b6aaf8e8d2f110c7e232a1bff83b", "sha256:17e01dd8666c445025c29684d4aabf5a90dc6ef1ab25328aa52bedaa95b65ad7", "sha256:19245c249aa711d954623d94f23cc94c0fd65865661f20b7781210cb97c471c0", "sha256:1caed2367b32cc80a2b7f58a9f46658218a19c6cfe5bc234021966dc3daa01f0", "sha256:1f66862d3a41674ebd8d1a7b6f5387fe5ce353f8719040a986551a545d7d83ea", "sha256:220e3fa77d14c8a507b2d951e463b57a1f7810a6443a26f9b7591ef39047b1b2", "sha256:276f4cd0001cd83b00817c8db76730938b1ee40f4993b6a905f40a7278103b3a", "sha256:29de916ba1099ba2aab76aca101580006adfac5646de9b7c010a0f13867cba45", "sha256:2a7f23bbaeb2a87f90f607730b45564076d870f1fb07b9318d0c21f36871932b", "sha256:2c407b1950b2d2ffa091f4e225ca19a66a9bd81222f27c56bd12658fc5ca1209", "sha256:30b5fec1d34cc932c1bc04017b538ce16bf84e239378b8f75220478645d11fca", "sha256:3c2155943896ac78b9b0fd910fb381186d0c345911f5333ee46ac44c8f0e43ab", "sha256:411d4ff9d041be08fdfc02adf62e89c735b9468f6d8f6427f8a14b6bb0a85095", "sha256:436e103950d05b7d7f55e39beeb4d5be298ca3e119e0589c0227e6d0b01ee8c7", "sha256:49640bda9bda35b057b0e65b7c43ba706fa2335c9a9896652aebe0fa399e80e6", "sha256:4a950f83fd3f9bca23b77442f3a2b2ea4ac900944d8af9993743774c4fdc57af", "sha256:50a6adc2be8edd7ee67d1abc3cd20678987c7b9d79cd265de55941e3d0d56499", "sha256:52ab14b9e09ce052237dfe12d6892dd39b0401690856bcfe75d5baba4bfe2831", "sha256:54f7e9705e14b2c9f6abdeb127c390f679f6dbe64ba732788d3015f7f76ef637", "sha256:66e50680e888840c0995f2ad766e726ce71ca682e3c5f4eee82272c7671d38a2", "sha256:790e4433962c9f454e213b21b0fd4b42310ade9c077e8edcb5113db0818450cb", "sha256:7a38362528a9115a4e276e65eeabf67dcfaf57698e17ae388599568a78dcb029", "sha256:7b05ed4b35bf6ee790832f68932baf1f00caa32283d66cc4d455c9e9d115aafc", "sha256:7e109f1c9a3ece676597831874126555997c48f62bddbcace6ed17be3e372de8", "sha256:949844af60ee96a376aac1ded2a27e134b8c8d35cc006a52903fc06c24a3296f", "sha256:95304068686545aa368b35dfda1cdfbbdbe2f6fe43de4a2e9baa8ebd71be46e2", "sha256:9e662e6fc4f513b79da5d10a23edd2b87685815b337b1a30cd11307a6679148d", "sha256:a9fed35ca8c6e946e877893bbac022e8563b94404a605af1d1e6accc7eb73289", "sha256:b69522b168a6b64edf0c33ba53eac491c0a8f5cc94fa4337f9c6f4c8f2f5296c", "sha256:b78729038abea6a5df0d2708dce21e82073463b2d79d10884d7d591e0f385ded", "sha256:b8c56bec53d6e3154eaff6ea941226e7bd7cc0d99f9b3756c2520fc7a94e6d96", "sha256:b9727ac4f5cf2cbf87880a63870b5b9730a8ae3a4a360241a0fdaa2f71240ff0", "sha256:ba3027deb7abf02859aca49c865ece538aee56dcb4871b4cced23ba4d5088904", "sha256:be9fcf32c010da0ba40bf4ee01889d6c737658f4ddff160bd7eb9cac8f094b21", "sha256:c18d47f314b950dbf24a41787ced1474e01ca816011925976d90a88b27c22b89", "sha256:c76a3075e96b9c9ff00df8b5f7f560f5634dffd1658bafb79eb2682867e94f78", "sha256:cbfcba14a3225b055a28b3199c3d81cd0ab37d2353ffd7f6fd64844cebab31ad", "sha256:d254666d29540a72d17cc0175746cfb03d5123db33e67d1020e42dae611dc196", "sha256:d66187792bfe56f8c18ba986a0e4ae44856b1c645336bd2c776e3386da91e1dd", "sha256:d8d04e755934195bdc1db45ba9e040b8d20d046d04d6d77e71b3b34a8cc002d0", "sha256:d8f3e2e0a1d6777e58e834fd5a04657f66affa615dae61dd67c35d1568c38882", "sha256:e057e74e53db78122a3979f908973e171909a58ac20df05c33998d52e6d35757", "sha256:e4ce984133b888cc3a46867c8b4372c7dee9cee300335e2925e197bcd45b9e16", "sha256:ea76dbcad0b7b0deb265d8c36e0801abcddf6cc1395940a24e3595288b405ca0", "sha256:ecb0f73954892f98611e183f50acdc9e21a4653f294dfbe079da73c6378a6f47", "sha256:ef14d75d86f104f03dea66c13188487151760ef25dd6b2dbd541885185f05f40", "sha256:f26648e1b3b03b6022b48a9b910d0ae209e2d51f50441db5dce5b530fad6d9b1", "sha256:f67472c09a0c7486e27f3275f617c964d25e35727af952869dd496b9b5b7f6a3" ], "index": "pypi", "version": "==7.0.5" }, "decorator": { "hashes": [ "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186" ], "markers": "python_version >= '3.5'", "version": "==5.1.1" }, "dill": { "hashes": [ "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0", "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373" ], "markers": "python_version < '3.11'", "version": "==0.3.6" }, "exceptiongroup": { "hashes": [ "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e", "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23" ], "markers": "python_version < '3.11'", "version": "==1.1.0" }, "executing": { "hashes": [ "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc", "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107" ], "version": "==1.2.0" }, "iniconfig": { "hashes": [ "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" ], "markers": "python_full_version >= '3.7.0'", "version": "==2.0.0" }, "ipython": { "hashes": [ "sha256:da01e6df1501e6e7c32b5084212ddadd4ee2471602e2cf3e0190f4de6b0ea481", "sha256:f3bf2c08505ad2c3f4ed5c46ae0331a8547d36bf4b21a451e8ae80c0791db95b" ], "index": "pypi", "version": "==8.8.0" }, "isort": { "hashes": [ "sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6", "sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b" ], "markers": "python_full_version >= '3.7.0'", "version": "==5.11.4" }, "jedi": { "hashes": [ "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e", "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612" ], "markers": "python_version >= '3.6'", "version": "==0.18.2" }, "lazy-object-proxy": { "hashes": [ "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382", "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82", "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9", "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494", "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46", "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30", "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63", "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4", "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae", "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be", "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701", "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd", "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006", "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a", "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586", "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8", "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821", "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07", "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b", "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171", "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b", "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2", "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7", "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4", "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8", "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e", "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f", "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda", "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4", "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e", "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671", "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11", "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455", "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734", "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb", "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59" ], "markers": "python_full_version >= '3.7.0'", "version": "==1.9.0" }, "matplotlib-inline": { "hashes": [ "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311", "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304" ], "markers": "python_version >= '3.5'", "version": "==0.1.6" }, "mccabe": { "hashes": [ "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" ], "markers": "python_version >= '3.6'", "version": "==0.7.0" }, "packaging": { "hashes": [ "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2", "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97" ], "markers": "python_version >= '3.7'", "version": "==23.0" }, "parso": { "hashes": [ "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0", "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75" ], "markers": "python_version >= '3.6'", "version": "==0.8.3" }, "pexpect": { "hashes": [ "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937", "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c" ], "markers": "sys_platform != 'win32'", "version": "==4.8.0" }, "pickleshare": { "hashes": [ "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56" ], "version": "==0.7.5" }, "platformdirs": { "hashes": [ "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490", "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2" ], "markers": "python_full_version >= '3.7.0'", "version": "==2.6.2" }, "pluggy": { "hashes": [ "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" ], "markers": "python_version >= '3.6'", "version": "==1.0.0" }, "prompt-toolkit": { "hashes": [ "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63", "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305" ], "markers": "python_full_version >= '3.6.2'", "version": "==3.0.36" }, "ptyprocess": { "hashes": [ "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220" ], "version": "==0.7.0" }, "pure-eval": { "hashes": [ "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350", "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3" ], "version": "==0.2.2" }, "pygments": { "hashes": [ "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297", "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717" ], "markers": "python_version >= '3.6'", "version": "==2.14.0" }, "pylint": { "hashes": [ "sha256:9df0d07e8948a1c3ffa3b6e2d7e6e63d9fb457c5da5b961ed63106594780cc7e", "sha256:b3dc5ef7d33858f297ac0d06cc73862f01e4f2e74025ec3eff347ce0bc60baf5" ], "index": "pypi", "version": "==2.15.10" }, "pytest": { "hashes": [ "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5", "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42" ], "index": "pypi", "version": "==7.2.1" }, "pytoolconfig": { "extras": [ "global" ], "hashes": [ "sha256:5e1a246f77970ddb5f3d8d06fbf162424b8a1adfc22c2eb51826b383bf293d1e", "sha256:7befe396f91b2593345b829a7745c7f459f04fc6c53fc86c0b771945446a7bd1" ], "markers": "python_full_version >= '3.7.0'", "version": "==1.2.4" }, "rope": { "hashes": [ "sha256:893dd80ba7077fc9f6f42b0a849372076b70f1d6e405b9f0cc52781ffa0e6890", "sha256:ba39581d0f8dee4ae8b5b5e82e35d03cebad965ccb127b7eaab9755cdc85e85a" ], "index": "pypi", "version": "==1.7.0" }, "six": { "hashes": [ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.16.0" }, "stack-data": { "hashes": [ "sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815", "sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8" ], "version": "==0.6.2" }, "tomli": { "hashes": [ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" ], "markers": "python_version < '3.11'", "version": "==2.0.1" }, "tomlkit": { "hashes": [ "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b", "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73" ], "markers": "python_version >= '3.6'", "version": "==0.11.6" }, "traitlets": { "hashes": [ "sha256:32500888f5ff7bbf3b9267ea31748fa657aaf34d56d85e60f91dda7dc7f5785b", "sha256:a1ca5df6414f8b5760f7c5f256e326ee21b581742114545b462b35ffe3f04861" ], "markers": "python_full_version >= '3.7.0'", "version": "==5.8.1" }, "typing-extensions": { "hashes": [ "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e" ], "markers": "python_version < '3.10'", "version": "==4.4.0" }, "wcwidth": { "hashes": [ "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e", "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0" ], "version": "==0.2.6" }, "wrapt": { "hashes": [ "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3", "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b", "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4", "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2", "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656", "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3", "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff", "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310", "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a", "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57", "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069", "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383", "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe", "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87", "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d", "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b", "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907", "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f", "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0", "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28", "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1", "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853", "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc", "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3", "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3", "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164", "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1", "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c", "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1", "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7", "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1", "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320", "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed", "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1", "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248", "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c", "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456", "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77", "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef", "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1", "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7", "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86", "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4", "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d", "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d", "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8", "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5", "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471", "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00", "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68", "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3", "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d", "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735", "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d", "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569", "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7", "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59", "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5", "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb", "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b", "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f", "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462", "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015", "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af" ], "markers": "python_version < '3.11'", "version": "==1.14.1" } } } spamcheck-v1.10.1/README.md000066400000000000000000000222521444633272200152070ustar00rootroot00000000000000# Spamcheck ## Project Structure Spamcheck started as an internal project by and for GitLab, over time it became increasingly clear the community could profit from these efforts and the decision was made to strive towards making much of it public. At the moment, management and development (issues, merge requests, etc) happen in [the gitlab-com Spamcheck project](https://gitlab.com/gitlab-com/gl-security/engineering-and-research/automation-team/spam/spamcheck) with [the gitlab-org project](https://gitlab.com/gitlab-org/spamcheck) being a code and registry mirror. ## Architecture Diagram The following diagram gives a high-level overview on how the various components of the anti-spam engine interact with each other: ![](docs/architecture.drawio.png) The basic spamcheck workflow is as follows. If issue metadata meets certain pre-defined conditions then a spam verdict is determined based on custom business logic. If spam status cannot be determined via issue metadata then ML inference is performed on the issue and the confidence ratio of the classification is used to determine the spam verdict. ![](docs/workflow.drawio.png) ## Development ### Running spamcheck with GDK 1. [Login to the GitLab Container Registry](https://docs.gitlab.com/ee/user/packages/container_registry/authenticate_with_container_registry.html) ```bash docker login registry.gitlab.com -u -p ``` 1. Run the service locally with docker ```bash docker pull registry.gitlab.com/gitlab-org/gl-security/security-engineering/security-automation/spam/spamcheck docker run --rm -p 8001:8001 registry.gitlab.com/gitlab-org/gl-security/security-engineering/security-automation/spam/spamcheck ``` 1. [Configure recaptcha](https://docs.gitlab.com/ee/development/spam_protection_and_captcha/exploratory_testing.html) in GDK 1. Enable Spamcheck in GDK 1. Go to Admin -> Settings -> Reporting 1. Under the Spam and Anti-bot Protection section 1. Check `Enable reCAPTCHA` 1. Check `Enable Spam Check via external API endpoint` 1. Set URL of the external Spam Check endpoint to `grpc://localhost:8001` 1. To change the maximum verdict values of spamcheck use the `--max-[TYPE]-verdict` options ```bash docker run --rm -p 8001:8001 registry.gitlab.com/gitlab-org/gl-security/security-engineering/security-automation/spam/spamcheck --max-generic-verdict block ``` ### Development Environment Clone this repo, install dependencies, and run the service. In order to perform ML inference, ensure a classifier is available in the `./classifiers` directory. See the classifiers and configuration section for details. ```bash git clone git@gitlab.com:gitlab-com/gl-security/engineering-and-research/automation-team/spam/spamcheck.git cd spamcheck make deps cp config/config.example.yml config/config.yml # Customize config/config.yml if necessary make run ``` #### Generating gRPC protobuf files To build the protobuf files when you've made a change: ```bash make proto ``` To build the Ruby protobuf files ```bash make proto_ruby ``` ### Local development using JupyterLab As an alternative, we've created a development environment (using a [Jupyter Docker Stacks](https://jupyter-docker-stacks.readthedocs.io/en/latest/index.html) image) that offers a containerized JupyterLab interface to enable one to code and, run Python files and Jupyter notebooks. To set this up, follow the instructions [here](https://gitlab.com/gitlab-com/gl-security/engineering-and-research/automation-team/docker/jupyter/-/blob/main/README.md). ### Running in Docker Build the `spamcheck` docker image. ```bash docker build -t spamcheck-py . ``` Run the docker image and ensure model files are mounted. ```bash docker run --rm -p 8001:8001 -v "$(pwd)/classifiers:/spamcheck/classifiers" spamcheck-py ``` ## ML Classifiers ML classifiers are python modules that are used to classify spam and return a score between 0.0-1.0 which corresponds to a probability a given issue is spam. All classifiers should exist as a module associated with the type of data that is to be classified. For example, code to classify issues should live in the `./classifiers/issue` directory. Each classifier must contain, at a minimum, a `classifier` module that includes a `score` method. The `score` method will receive a dictionary representation of the object to classify and return a float. The directory structure of an issue classifier may look like this. ``` classifiers/ |-- issue/ | |--model/ | | |-- model.tflite | | |-- tokenizer.pickle | |--classifier.py | |--pre_processor.py ``` The ML classifiers are constrained by dependencies included in this repositories `Pipfile` and care should be taken to not build classifiers that need additional dependencies. This implies that the model format used by the classifier it in the `tflite` format. The `docker build` command will automatically install the latest classifier. ``` docker build -t spamcheck-py . docker run --rm -v "$(pwd)/config:/spamcheck/config" -p8001:8001 spamcheck-py ``` ## Configuration Configuration options can be loaded via a `yaml` config file, environment variables, or CLI arguments. Preference for config options are: 1. CLI arguments 2. Environment variables 3. Config file options 4. Application defaults |Option|Default Value|Description| |------|--------------|------------| |`-c`, `--config`
`SPAMCHECK_CONFIG` | `./config/config.yml` | The path to the spamcheck configuration file. Must be in `yaml` format. | |`--env`
`SPAMCHECK_ENV` | `development`| The environment spamcheck is running in. When running in production gRPC reflection is disabled. | |`--gcs-bucket`
`SPAMCHECK_GCS_BUCKET ` | ``None`` | The GCS bucket to store unlabeled spam for future labeling and training. | |`--google-pubsub-project`
`SPAMCHECK_GOOGLE_PUBSUB_PROJECT ` | ``None`` | The GCP project where the spamcheck PubSub topic resides for publishing spam events. | |`--google-pubsub-topic`
`SPAMCHECK_GOOGLE_PUBSUB_TOPIC ` | ``spamcheck`` | The GCP PubSub topic to publish spam events to. | |`--grpc-addr`
`SPAMCHECK_GRPC_ADDR ` | `0.0.0.0:8001` | The `HOST:PORT` to bind the spamcheck service to. | |`--log-level`
`SPAMCHECK_LOG_LEVEL ` | `info` | The application log level. | |`--max-generic-verdict`
`SPAMCHECK_MAX_GENERIC_VERDICT ` | `ALLOW` | Maximum verdict to return for generic spammables. | |`--max-issue-verdict`
`SPAMCHECK_MAX_ISSUE_VERDICT ` | `CONDITIONAL_ALLOW` | Maximum verdict to return for issue spammables. | |`--max-snippet-verdict`
`SPAMCHECK_MAX_SNIPPET_VERDICT ` | `CONDITIONAL_ALLOW` | Maximum verdict to return for snippet spammables. | |`--ml-classifiers`
`SPAMCHECK_ML_CLASSIFIERS ` | `./classifiers` | Directory location for ML classifiers. | |`--tls-certificate`
`SPAMCHECK_TLS_CERTIFICATE ` | `./ssl/cert.pem` | The path to the TLS certificate to use for secure connections to spamcheck service. | |`--tls-private-key`
`SPAMCHECK_TLS_PRIVATE_KEY ` | `./ssl/key.pem` | The path to the TLS private key to use for secure connections to spamcheck service. | `config/config.yml` has more configuration options. View the [example file](./config/config.example.yml) for details. ## Concept overview ## Linting Run pylint against spamcheck source code: ```bash make lint ``` ## Testing Tests are located in the `./tests` directory and mirror the python module layout. ### Test suite Run the test suite: ```bash make test ``` ### Manually Start the service via `make run`. Test the gRPC endpoint with a python gRPC client. ```bash python client.py ``` Test the gRPC endpoint with [grpcurl](https://github.com/fullstorydev/grpcurl): ```bash grpcurl -plaintext -d "$(cat examples/checkforspamissue.json)" localhost:8001 spamcheck.SpamcheckService/CheckForSpamIssue ``` By default, `grpcurl` will return an empty object, e.g. `{}`, because Protobufs don't encode 0-value fields and `SpamVerdict.Verdict` is an enum whose default value is `ALLOW` which is 0. There is a `/healthz` endpoint used for Kubernetes probes and uptime checks: ```bash grpcurl -plaintext localhost:8001 spamcheck.SpamcheckService/Healthz ``` ## Publishing a new gem version 1. Build new Ruby protobuf files: ```bash make proto_ruby ``` 2. Make sure to update `ruby/spamcheck/version.rb` with the new version number. Keep in mind that `bundle update` uses `--major` by default, so all minor version updates should not break backward or cross-protocol compatibility. 3. Sign up for a [RubyGems account](RubyGems.org) if you already don't have one. You should also be an owner of the [spamcheck gem](https://rubygems.org/gems/spamcheck) 4. Create a tag for spamcheck with the version number e.g. if the version number is 0.1.0, do: ```bash git tag v0.1.0 ``` 5. Check that the tag has been correctly created alongside your latest commit: ```bash git show v0.1.0 ``` 6. Push the branch with the tag: ```bash git push --tags origin ``` 7. Run `bundle exec ruby _support/publish-gem v0.1.0` locally. It should ask you for your rubygems email and password. 8. After a successful push, the new gem version should now be publicly available on [RubyGems.org](https://rubygems.org/gems/spamcheck) and ready to use. spamcheck-v1.10.1/VERSION000066400000000000000000000000071444633272200147720ustar00rootroot000000000000001.10.1 spamcheck-v1.10.1/_support/000077500000000000000000000000001444633272200156005ustar00rootroot00000000000000spamcheck-v1.10.1/_support/changelog000077500000000000000000000126701444633272200174630ustar00rootroot00000000000000#!/usr/bin/env ruby # # Generate a changelog entry file in the correct location. # # Automatically stages the file and amends the previous commit if the `--amend` # argument is used. # # Lifted from gitlab-org/gitlab-ce require 'optparse' require 'yaml' Options = Struct.new( :amend, :author, :dry_run, :force, :merge_request, :title, :type ) INVALID_TYPE = -1 class ChangelogOptionParser Type = Struct.new(:name, :description) TYPES = [ Type.new('added', 'New feature'), Type.new('fixed', 'Bug fix'), Type.new('changed', 'Feature change'), Type.new('deprecated', 'New deprecation'), Type.new('removed', 'Feature removal'), Type.new('security', 'Security fix'), Type.new('performance', 'Performance improvement'), Type.new('other', 'Other') ].freeze TYPES_OFFSET = 1 class << self def parse(argv) options = Options.new parser = OptionParser.new do |opts| opts.banner = "Usage: #{__FILE__} [options] [title]\n\n" # Note: We do not provide a shorthand for this in order to match the `git # commit` interface opts.on('--amend', 'Amend the previous commit') do |value| options.amend = value end opts.on('-f', '--force', 'Overwrite an existing entry') do |value| options.force = value end opts.on('-m', '--merge-request [integer]', Integer, 'Merge Request ID') do |value| options.merge_request = value end opts.on('-n', '--dry-run', "Don't actually write anything, just print") do |value| options.dry_run = value end opts.on('-u', '--git-username', 'Use Git user.name configuration as the author') do |value| options.author = git_user_name if value end opts.on('-t', '--type [string]', String, "The category of the change, valid options are: #{TYPES.map(&:name).join(', ')}") do |value| options.type = parse_type(value) end opts.on('-h', '--help', 'Print help message') do $stdout.puts opts exit end end parser.parse!(argv) # Title is everything that remains, but let's clean it up a bit options.title = argv.join(' ').strip.squeeze(' ').tr("\r\n", '') options end def read_type read_type_message type = TYPES[$stdin.getc.to_i - TYPES_OFFSET] assert_valid_type!(type) type.name end private def parse_type(name) type_found = TYPES.find do |type| type.name == name end type_found ? type_found.name : INVALID_TYPE end def read_type_message $stdout.puts "\n>> Please specify the index for the category of your change:" TYPES.each_with_index do |type, index| $stdout.puts "#{index + TYPES_OFFSET}. #{type.description}" end $stdout.print "\n?> " end def assert_valid_type!(type) unless type $stderr.puts "Invalid category index, please select an index between 1 and #{TYPES.length}" exit 1 end end def git_user_name %x{git config user.name}.strip end end end class ChangelogEntry attr_reader :options def initialize(options) @options = options assert_feature_branch! assert_title! assert_new_file! # Read type from $stdin unless is already set options.type ||= ChangelogOptionParser.read_type assert_valid_type! $stdout.puts "\e[32mcreate\e[0m #{file_path}" $stdout.puts contents unless options.dry_run write amend_commit if options.amend end end private def contents yaml_content = YAML.dump( 'title' => title, 'merge_request' => options.merge_request, 'author' => options.author, 'type' => options.type ) remove_trailing_whitespace(yaml_content) end def write File.write(file_path, contents) end def amend_commit %x{git add #{file_path}} exec("git commit --amend") end def fail_with(message) $stderr.puts "\e[31merror\e[0m #{message}" exit 1 end def assert_feature_branch! return unless branch_name == 'master' fail_with "Create a branch first!" end def assert_new_file! return unless File.exist?(file_path) return if options.force fail_with "#{file_path} already exists! Use `--force` to overwrite." end def assert_title! return if options.title.length > 0 || options.amend fail_with "Provide a title for the changelog entry or use `--amend`" \ " to use the title from the previous commit." end def assert_valid_type! return unless options.type && options.type == INVALID_TYPE fail_with 'Invalid category given!' end def title if options.title.empty? last_commit_subject else options.title end end def last_commit_subject %x{git log --format="%s" -1}.strip end def file_path File.join( unreleased_path, branch_name.gsub(/[^\w-]/, '-') << '.yml' ) end def unreleased_path path = File.join('changelogs', 'unreleased') path = File.join('ee', path) if ee? path end def ee? @ee ||= File.exist?(File.expand_path('../CHANGELOG-EE.md', __dir__)) end def branch_name @branch_name ||= %x{git symbolic-ref --short HEAD}.strip end def remove_trailing_whitespace(yaml_content) yaml_content.gsub(/ +$/, '') end end if $0 == __FILE__ options = ChangelogOptionParser.parse(ARGV) ChangelogEntry.new(options) end # vim: ft=ruby spamcheck-v1.10.1/_support/generate-proto-ruby000077500000000000000000000032741444633272200214460ustar00rootroot00000000000000#!/usr/bin/env ruby require 'erb' require 'fileutils' require_relative 'run.rb' PROTO_INCLUDE = 'api/v1' PROTO_FILES = Dir[File.join(PROTO_INCLUDE, '*.proto')].sort.map { |f| File.absolute_path(f) } RUBY_PREFIX = File.join("ruby") RUBY_VERSION_FILE = 'spamcheck/version.rb' GOPATH = ENV["GOPATH"] GO_PROTO_LIBS = [ "#{GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis", "#{GOPATH}/pkg/mod/github.com/grpc-ecosystem/grpc-gateway/v2@v2.1.0/third_party/googleapis" ] def main ruby_lib_spamcheck = File.join(RUBY_PREFIX, 'spamcheck') FileUtils.rm(Dir[File.join(ruby_lib_spamcheck, '**/*_pb.rb')]) FileUtils.mkdir_p(ruby_lib_spamcheck) Dir.chdir(File.join(Dir.pwd, 'ruby')) do # Using an absolute path make sure the prefixes match, or the passed in file # locations. `protoc` requires this. GO_PROTO_LIBS << File.absolute_path(File.join('..', PROTO_INCLUDE)) includes = GO_PROTO_LIBS.join(":") run!(%W[bundle exec grpc_tools_ruby_protoc --proto_path=#{includes} --ruby_out=../#{ruby_lib_spamcheck} --grpc_out=../#{ruby_lib_spamcheck}] + PROTO_FILES) end write_ruby_requires end def write_ruby_requires requires = Dir.chdir(RUBY_PREFIX) { Dir['**/*_pb.rb'] }.sort abort "No auto-generated Ruby service files found" if requires.empty? requires.unshift(RUBY_VERSION_FILE) gem_root = File.join(RUBY_PREFIX, 'spamcheck.rb') gem_root_template = ERB.new <<~EOT # This file is generated by #{File.basename($0)}. Do not edit. $:.unshift(File.expand_path('../spamcheck', __FILE__)) <% requires.each do |f| %> require '<%= f.chomp('.rb') %>' <% end %> EOT open(gem_root, 'w') { |f| f.write(gem_root_template.result(binding)) } end main spamcheck-v1.10.1/_support/generate_changelog000077500000000000000000000031061444633272200213270ustar00rootroot00000000000000#!/usr/bin/env ruby # Generates the changelog from the yaml entries in changelogs/unreleased require 'yaml' require 'fileutils' class ChangelogEntry attr_reader :title, :merge_request, :type, :author def initialize(file_path) yaml = YAML.safe_load(File.read(file_path)) @title = yaml['title'] @merge_request = yaml['merge_request'] @type = yaml['type'] @author = yaml['author'] end def to_s str = "" str << "- #{title}\n" str << " https://gitlab.com/gitlab-org/gitaly/merge_requests/#{merge_request}\n" str << " Contributed by #{author}\n" if author str end end ROOT_DIR = File.expand_path('../..', __FILE__) UNRELEASED_ENTRIES = File.join(ROOT_DIR, 'changelogs', 'unreleased') CHANGELOG_FILE = File.join(ROOT_DIR, 'CHANGELOG.md') def main(version) entries = [] Dir["#{UNRELEASED_ENTRIES}/*.yml"].each do |yml| entries << ChangelogEntry.new(yml) FileUtils.rm(yml) end sections = [] types = entries.map(&:type).uniq.sort types.each do |type| text = '' text << "#### #{type.capitalize}\n" entries.each do |e| next unless e.type == type text << e.to_s end sections << text end new_version_entry = ["## v#{version}\n\n", sections.join("\n"), "\n"].join current_changelog = File.read(CHANGELOG_FILE).lines header = current_changelog.shift(2) new_changelog = [header, new_version_entry, current_changelog.join] File.write(CHANGELOG_FILE, new_changelog.join) end unless ARGV.count == 1 warn "Usage: #{$0} VERSION" warn "Specify version as x.y.z" abort end main(ARGV.first) spamcheck-v1.10.1/_support/publish-gem000077500000000000000000000017761444633272200177550ustar00rootroot00000000000000#!/usr/bin/env ruby #Borrowed from Gitaly require_relative 'run.rb' def main(tag) version = tag.sub(/^v/, '') unless version.match?(/\d+\.\d+\.\d+(-rc\d+)?/) abort "Version string #{version.inspect} does not look like a Spamcheck Release tag (e.g. \"v1.0.2\"). Aborting." end ref = capture!(%w[git describe --tag]).chomp if ref != "v#{version}" abort "Checkout tag v#{version} to publish.\n\t git checkout v#{version}" end puts 'Testing for changed files' run!(%w[git diff --quiet --exit-code]) puts 'Testing for staged changes' run!(%w[git diff --quiet --cached --exit-code]) gem = "spamcheck-#{version}.gem" run!(['gem', 'build', 'spamcheck.gemspec', '--output', gem]) abort "gem not found: #{gem}" unless File.exist?(gem) puts "Proceed to publish version #{tag}? Enter 'Yes' to continue; Ctrl-C to abort" $stdout.flush abort unless $stdin.gets.chomp == 'Yes' run!(%W[gem push #{gem}]) end unless ARGV.count == 1 warn "Usage: #{$0} TAG" abort end main(ARGV[0]) spamcheck-v1.10.1/_support/run.rb000066400000000000000000000013551444633272200167350ustar00rootroot00000000000000def run!(cmd, chdir='.') SpamCheckSupport.print_cmd(cmd) unless system(*cmd, chdir: chdir) SpamCheckSupport.fail_cmd!(cmd) end end def run2!(cmd, chdir: '.', out: 1) SpamCheckSupport.print_cmd(cmd) unless system(*cmd, chdir: chdir, out: out) SpamCheckSupport.fail_cmd!(cmd) end end def capture!(cmd, chdir='.') SpamCheckSupport.print_cmd(cmd) output = IO.popen(cmd, chdir: chdir) { |io| io.read } SpamCheckSupport.fail_cmd!(cmd) unless $?.success? output end module SpamCheckSupport class << self def print_cmd(cmd) puts '-> ' + printable_cmd(cmd) end def fail_cmd!(cmd) abort "command failed: #{printable_cmd(cmd)}" end def printable_cmd(cmd) cmd.join(' ') end end end spamcheck-v1.10.1/api/000077500000000000000000000000001444633272200144765ustar00rootroot00000000000000spamcheck-v1.10.1/api/README.md000066400000000000000000000002541444633272200157560ustar00rootroot00000000000000# /api Protobuf files and artifacts - protobuf definitions `*.proto` - gRPC server and client interfaces `*.pb.go` - Swagger `*.swagger.json` - grpc-gateway `*.pb.gw.go` spamcheck-v1.10.1/api/v1/000077500000000000000000000000001444633272200150245ustar00rootroot00000000000000spamcheck-v1.10.1/api/v1/health.proto000066400000000000000000000006441444633272200173620ustar00rootroot00000000000000syntax = "proto3"; package grpc.health.v1; message HealthCheckResponse { enum ServingStatus { UNKNOWN = 0; SERVING = 1; NOT_SERVING = 2; } ServingStatus status = 1; } message HealthCheckRequest { string service = 1; } service Health { rpc Check(HealthCheckRequest) returns (HealthCheckResponse); rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse); } spamcheck-v1.10.1/api/v1/spamcheck.proto000066400000000000000000000041621444633272200200520ustar00rootroot00000000000000syntax = "proto3"; package spamcheck; import "google/protobuf/timestamp.proto"; message Generic { User user = 1; string text = 2; string type = 3; google.protobuf.Timestamp created_at = 4; google.protobuf.Timestamp updated_at = 5; Action action = 6; bool user_in_project = 7; // Whether or not the user is an authorized project member Project project = 8; } message Issue { User user = 1; string title = 2; string description = 3; google.protobuf.Timestamp created_at = 4; google.protobuf.Timestamp updated_at = 5; Action action = 6; bool user_in_project = 7; // Whether or not the user is an authorized project member Project project = 8; } message Snippet { User user = 1; string title = 2; string description = 3; string visibility = 4; google.protobuf.Timestamp created_at = 5; google.protobuf.Timestamp updated_at = 6; Action action = 7; bool user_in_project = 9; // Whether or not the user is an authorized project member Project project = 10; repeated File files = 11; } message User { repeated Email emails = 1; string org = 2; string username = 3; google.protobuf.Timestamp created_at = 4; map abuse_metadata = 5; int32 id = 6; message Email { string email = 1; bool verified = 2; } } message Project { int32 project_id = 1; string project_path = 2; } message File { string path = 1; } message SpamVerdict { enum Verdict { ALLOW = 0; CONDITIONAL_ALLOW = 1; DISALLOW = 2; BLOCK = 3; NOOP = 4; } Verdict verdict = 1; string error = 2 [deprecated=true]; map extra_attributes = 3 [deprecated=true]; // introduced in v1.5.0 float score = 4; string reason = 5; // introduced in v1.6.0 bool evaluated = 6; } enum Action { CREATE = 0; UPDATE = 1; } service SpamcheckService { rpc CheckForSpamGeneric(Generic) returns (SpamVerdict) { } rpc CheckForSpamIssue(Issue) returns (SpamVerdict) { } rpc CheckForSpamSnippet(Snippet) returns (SpamVerdict) { } } spamcheck-v1.10.1/app/000077500000000000000000000000001444633272200145055ustar00rootroot00000000000000spamcheck-v1.10.1/app/__init__.py000066400000000000000000000002761444633272200166230ustar00rootroot00000000000000"""Top level module for spamcheck service.""" from app import config config.load() class ValidationError(Exception): """An exception indicating an object failed validation checks""" spamcheck-v1.10.1/app/config.py000066400000000000000000000065141444633272200163320ustar00rootroot00000000000000"""Spamcheck service configuration.""" import argparse import os from vyper import v # pylint: disable=too-many-statements def load() -> None: """Load the configuration options. Precedence for config: CLI arguments Environment variables Config file Defaults """ if v.get("config_loaded"): return # Set CLI config options parser = argparse.ArgumentParser(description="Application settings") parser.add_argument( "--env", type=str, choices=["test", "dev", "prod"], help="Application env" ) parser.add_argument("--grpc-addr", type=str, help="Application bind address") parser.add_argument( "--log-level", type=str, choices=["debug", "info", "warning", "error", "fatal"], help="Application log level", ) parser.add_argument( "--ml-classifiers", type=str, help="Directory location for ML classifiers" ) parser.add_argument("--gcs-bucket", type=str, help="Bucket to save spammable data for labeling") parser.add_argument("--google-pubsub-project", type=str, help="Google PubSub project") parser.add_argument("--google-pubsub-topic", type=str, help="Google PubSub topic") parser.add_argument("--tls-certificate", type=str, help="TLS certificate path") parser.add_argument("--tls-private-key", type=str, help="TLS private key path") parser.add_argument("-c", "--config", type=str, help="Path to config file") parser.add_argument("--max-generic-verdict", type=str, help="Maximum verdict for generics") parser.add_argument("--max-issue-verdict", type=str, help="Maximum verdict for issues") parser.add_argument("--max-snippet-verdict", type=str, help="Maximum verdict for snippets") # ignore unknown args. These will be present when running unit tests args, _ = parser.parse_known_args() v.bind_args(args.__dict__) # Set config file options v.set_config_type("yaml") v.set_config_name("config") v.add_config_path("./config") # Set environment variable options v.set_env_prefix("spamcheck") v.automatic_env() # Set defaults v.set_default("env", "development") v.set_default("grpc_addr", "0.0.0.0:8001") v.set_default("log_level", "info") v.set_default("google_pubsub_topic", "spamcheck") v.set_default("tls_certificate", "./ssl/cert.pem") v.set_default("tls_private_key", "./ssl/key.pem") v.set_default("ml_classifiers", "./classifiers") v.set_default("filter.allow_list", {}) v.set_default("filter.deny_list", {}) v.set_default("filter.allowed_domains", []) v.set_default("max_generic_verdict", "ALLOW") v.set_default("max_issue_verdict", "CONDITIONAL_ALLOW") v.set_default("max_snippet_verdict", "CONDITIONAL_ALLOW") # Load config file config_file = None if args.config: config_file = args.config elif v.is_set("config"): config_file = v.get_string("config") if config_file: with open(config_file, encoding="utf-8") as handle: config_data = handle.read() v.read_config(config_data) else: try: v.read_in_config() except FileNotFoundError: pass if os.path.exists(v.get("tls_private_key")) and os.path.exists( v.get("tls_certificate") ): v.set("tls_enabled", True) v.set("config_loaded", True) spamcheck-v1.10.1/app/data_store.py000066400000000000000000000145301444633272200172070ustar00rootroot00000000000000"""Store ham and spam for labeling and training.""" import json import time from threading import Thread from google.cloud import storage from vyper import v from app import logger log = logger.logger bucket = None # pylint: disable=invalid-name _BUCKET_KEY = "gcs_bucket" _stores = {} # dict containing the DataStore store objects for each spammable type if v.is_set(_BUCKET_KEY): bucket_str = v.get_string(_BUCKET_KEY) _storage_client = storage.Client() try: bucket = _storage_client.get_bucket(bucket_str) log.debug("GCS storage enabled") except Exception as exp: # pylint: disable=broad-except log.error(f"Failed to initialize GCS bucket: {exp}") else: log.debug("GCS storage disabled") class DataStore: # pylint: disable=too-few-public-methods """The data store is used to capture ham and spam and write to GCS for labeling and training. All issues classified as spam are saved and ham is saved in a balanced manner. Ham storage is biased to ensure that spammables that are closest to the SPAM_HAM_THRESHOLD are saved more often than other ham. Args: spammable_type (str): The type of spammable (i.e. issue, snippet). """ MAX_HAM_SIZE = 50 SPAM_HAM_THRESHOLD = 0.5 QUESTIONABLE_HAM_BIAS = 3 def __init__(self, spammable_type: str): self.spammable_type = spammable_type.lower() self.gcs_file_path = f"spamcheck-unlabeled/{spammable_type}" self._questionable_ham = {} self._random_ham = {} self._questionable_count = 1 self._labeled_count = 0 self._ham_spam_count = 0 self._monitor_objects() def save(self, spammable: dict, confidence: float) -> None: """Save a spammable for future labeling and training. Args: spammable (dict): The dictionary representation of a spammable object. confidence (float): The confidence value returned from ML inference. """ spammable["type"] = self.spammable_type spammable["ml_inference_score"] = confidence if confidence < self.SPAM_HAM_THRESHOLD: self._add(spammable, confidence) else: _write_to_gcs(self.gcs_file_path, spammable) self._ham_spam_count -= 1 self._save_ham() def _save_ham(self) -> None: """Save ham messages until the saved ham is equal to the number of saved spam.""" while self._ham_spam_count + self._labeled_count < 0: ham = self._get() if not ham: break _write_to_gcs(self.gcs_file_path, ham) # If _ham_spam_count is less than 0 then we are imbalanced in the unlabeled data we # have saved so balance that first. Otherwise, we are here because of an imbalance # in the labeled data so decrement that counter instead. if self._ham_spam_count < 0: self._ham_spam_count += 1 else: self._labeled_count += 1 def _add(self, spammable: dict, confidence: float) -> None: # First fill up the "questionable ham". This dictionary contains spammables # That are closest to the threshold of being classified as spam. if len(self._questionable_ham) == 0 or confidence > min(self._questionable_ham): self._questionable_ham[confidence] = spammable # If the "questionsable ham" is full then remove the lowest confidence spammable if len(self._questionable_ham) > self.MAX_HAM_SIZE: self._questionable_ham.pop(min(self._questionable_ham)) # If the "random ham" is not full then add the spammable to that dictionary elif len(self._random_ham) < self.MAX_HAM_SIZE: self._random_ham[confidence] = spammable def _get(self) -> dict: ham = None # Only get random ham if the modulo of the questionable count is 0. # Doing this will create a bias to return the ham messages that are closest # to the threshold of marking a spammable as spam versus ham. if ( self._questionable_count % self.QUESTIONABLE_HAM_BIAS == 0 and len(self._random_ham) > 0 ): self._questionable_count = 1 # popitem returns a key and value tuple but we have no use for the key _, ham = self._random_ham.popitem() elif len(self._questionable_ham) > 0: self._questionable_count += 1 ham = self._questionable_ham.pop(max(self._questionable_ham)) return ham def _monitor_objects(self): def update_counts(): prefix = self.gcs_file_path.replace('unlabeled', 'labeled') while True: count = 0 log.debug("counting labeled objects for {self.spammable_type}") for blob in bucket.list_blobs(prefix=prefix): if '/spam/' in blob.name: count -= 1 else: count += 1 log.debug(f"setting labeled count for {self.spammable_type}: {count}") self._labeled_count = count time.sleep(900) Thread(target=update_counts, daemon=True).start() def save(spammable_type: str, spammable: dict, confidence: float): """Save a spammable for future labeling and training. Args: spammable_type (str): The type of spammable (i.e. issue, snippet). spammable (dict): The dictionary representation of the spammable object. confidence (float): The value confidence value returned from ML inference. """ if bucket is None: return if spammable_type not in _stores: _stores[spammable_type] = DataStore(spammable_type) func = _stores[spammable_type].save thread = Thread(target=func, args=((spammable, confidence))) thread.start() def _write_to_gcs(upload_path: str, data_dict: dict) -> None: """Convert a dict to JSON and write to GCS. Args: upload_path (str): The bucket path to write the blob. spammable_dict (dict): The dictionary to save. """ json_str = json.dumps(data_dict) filename = data_dict["correlation_id"] try: blob = bucket.blob(f"{upload_path}/{filename}.json") blob.upload_from_string(json_str) # pylint: disable=broad-except except Exception as ex: log.error(f"failed to write to GCS: {ex}") spamcheck-v1.10.1/app/event.py000066400000000000000000000013041444633272200161760ustar00rootroot00000000000000"""Event module used to implement cloudevents""" from cloudevents.http import CloudEvent from cloudevents.conversion import to_json VERDICT = "verdict" class Event(CloudEvent): """The Event object which wraps cloudevent functionality. Args: data [dict]: The data to build the event from. """ SOURCE = "/spamcheck" NAMESPACE = "com.gitlab.spamcheck" def __init__(self, _type: str, data: dict): attributes = { "type": f"{self.NAMESPACE}.{_type}", "source": self.SOURCE, } super().__init__(attributes=attributes, data=data) def json(self) -> bytes: """Convert Event to a JSON byte array.""" return to_json(self) spamcheck-v1.10.1/app/logger.py000066400000000000000000000045201444633272200163370ustar00rootroot00000000000000"""Common logging package for spamcheck""" import logging import sys from datetime import datetime from typing import Any, Optional from pythonjsonlogger import jsonlogger from vyper import v def _log_wrapper(level: str) -> Any: def inner(func: Any) -> Any: def wrapper(*args, **kwargs) -> Any: if kwargs.get("extra") is None: kwargs["extra"] = {} kwargs["extra"]["time"] = datetime.utcnow().strftime( "%Y-%m-%dT%H:%M:%S.%fZ" ) kwargs["extra"]["level"] = level kwargs["extra"]["service_name"] = "spamcheck" return func(*args, **kwargs) return wrapper return inner def _exc_info() -> bool: if sys.exc_info()[0]: return True return False class Logger: """Common JSON logger for spamcheck service.""" def __init__(self) -> None: self.logger = logging.getLogger("spamcheck") self.logger.propagate = False handler = logging.StreamHandler() formatter = jsonlogger.JsonFormatter() handler.setFormatter(formatter) self.logger.addHandler(handler) lvl = v.get_string("log_level").upper() level = logging.getLevelName(lvl) self.logger.setLevel(level) @_log_wrapper("FATAL") def fatal(self, msg: str, extra: Optional[dict] = None): """Logs a message with level FATAL on the spamcheck logger.""" self.logger.fatal(msg, exc_info=_exc_info(), extra=extra) sys.exit(1) @_log_wrapper("ERROR") def error(self, msg: str, extra: Optional[dict] = None): """Logs a message with level ERROR on the spamcheck logger.""" self.logger.error(msg, exc_info=_exc_info(), extra=extra) @_log_wrapper("WARNING") def warning(self, msg: str, extra: Optional[dict] = None): """Logs a message with level WARNING on the spamcheck logger.""" self.logger.warning(msg, extra=extra) @_log_wrapper("INFO") def info(self, msg: str, extra: Optional[dict] = None): """Logs a message with level INFO on the spamcheck logger.""" self.logger.info(msg, extra=extra) @_log_wrapper("DEBUG") def debug(self, msg: str, extra: Optional[dict] = None): """Logs a message with level DEBUG on the spamcheck logger.""" self.logger.debug(msg, extra=extra) logger = Logger() spamcheck-v1.10.1/app/queue.py000066400000000000000000000044341444633272200162100ustar00rootroot00000000000000"""Spamcheck queue used to emit spam verdicts""" from threading import Thread from google.cloud import pubsub_v1 from google.api_core.exceptions import AlreadyExists from vyper import v from app import logger from app.event import Event log = logger.logger queues = set() class Queue: """Base class for Queue implementation. Specific queues should inherit from this class.""" def enabled(self) -> bool: """Determine if a queue is enabled.""" raise NotImplementedError("Queue not implemented") def publish(self, event: Event) -> None: """Publish an event to the queue. Args: event (Event): The Event to publish. """ raise NotImplementedError("Queue not implemented") class GooglePubSub(Queue): """Implementation of the Gooogle PubSub message queue.""" project_key = "google_pubsub_project" topic_key = "google_pubsub_topic" def __init__(self): self._enabled = False if not v.is_set(self.project_key): log.debug("Google PubSub disabled") return project = v.get_string(self.project_key) topic = v.get_string(self.topic_key) self.publisher = pubsub_v1.PublisherClient() self.topic = self.publisher.topic_path(project, topic) try: self.publisher.create_topic(name=self.topic) except AlreadyExists: pass except Exception as exp: # pylint: disable=broad-except log.error(f"Failed to initialize Google PubSub: {exp}") return log.debug("Google PubSub enabled") self._enabled = True def enabled(self) -> bool: return self._enabled def publish(self, event: Event) -> None: if self.enabled(): try: future = self.publisher.publish(self.topic, event.json()) future.result() except Exception as exp: # pylint: disable=broad-except log.error(f"Google PubSub error: {exp}") google_pubsub = GooglePubSub() if google_pubsub.enabled(): queues.add(google_pubsub) def publish(event: Event) -> None: """Publish an event to all configured queues.""" for queue in queues: thread = Thread(target=queue.publish, args=(event,)) thread.daemon = True thread.start() spamcheck-v1.10.1/app/spammable/000077500000000000000000000000001444633272200164465ustar00rootroot00000000000000spamcheck-v1.10.1/app/spammable/__init__.py000066400000000000000000000153711444633272200205660ustar00rootroot00000000000000"""Logic to perform spam/ham classification""" import sys from typing import Any, List from google.protobuf.json_format import MessageToDict from vyper import v from api.v1.spamcheck_pb2 import SpamVerdict from app import event, logger, queue, data_store from server.interceptors import SpamCheckContext log = logger.logger classifiers = v.get_string("ml_classifiers") if classifiers: sys.path.append(classifiers) # pylint: disable=too-few-public-methods class Spammable: """Base class for spammable types.""" allow_list = v.get("filter.allow_list") deny_list = v.get("filter.deny_list") allowed_domains = set(v.get("filter.allowed_domains")) # Currently maximum allowed value is conditional allow to limit false positives. max_verdict = SpamVerdict.CONDITIONAL_ALLOW _inference_scores = { 0.9: SpamVerdict.BLOCK, 0.5: SpamVerdict.DISALLOW, 0.4: SpamVerdict.CONDITIONAL_ALLOW, 0.0: SpamVerdict.ALLOW, } _verdict_rankings = { SpamVerdict.ALLOW: 1, SpamVerdict.CONDITIONAL_ALLOW: 2, SpamVerdict.DISALLOW: 3, SpamVerdict.BLOCK: 4, } _verdict_mapping = { "ALLOW": SpamVerdict.ALLOW, "CONDITIONAL_ALLOW": SpamVerdict.CONDITIONAL_ALLOW, "DISALLOW": SpamVerdict.DISALLOW, "BLOCK": SpamVerdict.BLOCK, } def __init__( self, spammable: Any, context: SpamCheckContext, classifier: None ) -> None: self.context = context self.spammable = spammable self.classifier = classifier @classmethod def set_max_verdict(cls): """Set the maximum verdict for the spammable class.""" key = f"max_{cls.__name__}_verdict".lower() verdict_value = v.get_string(key).upper() try: cls.max_verdict = cls._verdict_mapping[verdict_value] except KeyError: valid_args = ', '.join(cls._verdict_mapping.keys()) log.fatal(f"Max verdict must be in [{valid_args}]. Got: {verdict_value}") @property def spammable(self) -> Any: """spam.Spammable: The spammable to analyze for spam""" return self._spammable @spammable.setter def spammable(self, spammable: Any): self._spammable = spammable self._email_allowed = self.email_allowed(spammable.user.emails) if spammable.project: self._project_allowed = self.project_allowed(spammable.project.project_id) else: self._project_allowed = True def verdict(self) -> SpamVerdict: """Analyze the spammable and determine if spam. Returns: SpamVerdict """ # If the project is not allowed then this may be an indication that the model # does not generalize well to the spammables in that project. In this case we will # circumvent evaluating the spammable. if not self._project_allowed: return self._verdict(SpamVerdict.NOOP, 0.0, "project not allowed", False) if not self.classifier: return self._verdict(SpamVerdict.NOOP, 0.0, "classifier not loaded", False) spammable_dict = self.to_dict() confidence = self.classifier.score(spammable_dict) data_store.save(self.type(), spammable_dict, confidence) if self._email_allowed: return self._verdict(SpamVerdict.ALLOW, confidence, "email allowed", True) verdict = self.calculate_verdict(confidence) return self._verdict(verdict, confidence, "ml inference score", True) def calculate_verdict(self, confidence: float) -> SpamVerdict: """Convert an ML confidence value to a spam verdict. Args: confidence (float): The ML confidence value Returns: SpamVerdict """ for threshold, vdict in self._inference_scores.items(): if confidence >= threshold: return self._maximum_verdict(vdict) return SpamVerdict.NOOP def _verdict( self, verdict: int, confidence: float, reason: str, evaluated: bool ) -> SpamVerdict: fields = { "correlation_id": str(self.context.correlation_id), "metric": "spamcheck_verdicts", "spammable_type": self.type(), "email_allowlisted": self._email_allowed, "project_allowed": self._project_allowed, "project_path": self._spammable.project.project_path, "project_id": self._spammable.project.project_id, "user_name": self._spammable.user.username, "user_in_project": self._spammable.user_in_project, "verdict": SpamVerdict.Verdict.Name(verdict), "reason": reason, "confidence": confidence, "evaluated": evaluated, } log.info("Verdict calculated", extra=fields) if verdict not in (SpamVerdict.ALLOW, SpamVerdict.NOOP): evnt = event.Event(event.VERDICT, fields) queue.publish(evnt) return SpamVerdict( verdict=verdict, score=confidence, reason=reason, evaluated=evaluated ) def project_allowed(self, project_id: int) -> bool: """Determine if a project should be tested for spam. Args: project_id (int): The GitLab project ID Returns: bool """ if len(self.allow_list) != 0: if self.allow_list.get(project_id) is not None: return True return False if len(self.deny_list) != 0: if self.deny_list.get(project_id) is not None: return False return True return True def email_allowed(self, emails: List) -> bool: """Determine if a user email should be exempt from spam checking. Args: emails (list): A list of Emails represented by protobuf objects Returns: bool """ for email in emails: if not "@" in email.email: continue domain = email.email.split("@")[-1] if email.verified and domain in self.allowed_domains: return True return False def type(self) -> str: """Get the string representation of the spammable type.""" return type(self).__name__.lower() def to_dict(self) -> dict: """Return the dictionary representation of the spammable.""" spammable_dict = MessageToDict(self._spammable) spammable_dict["correlation_id"] = str(self.context.correlation_id) return spammable_dict def _maximum_verdict(self, verdict: SpamVerdict) -> SpamVerdict: max_verdict = self._verdict_rankings[self.max_verdict] current_verdict = self._verdict_rankings[verdict] if max_verdict < current_verdict: return self.max_verdict return verdict spamcheck-v1.10.1/app/spammable/generic.py000066400000000000000000000025541444633272200204420ustar00rootroot00000000000000"""Process an snippet to determine if it is spam or not.""" from google.protobuf.json_format import MessageToDict import api.v1.spamcheck_pb2 as spam from app import logger from app.spammable import Spammable from server.interceptors import SpamCheckContext log = logger.logger # Expecting a module to exist in the directory specified by the ml_classifiers config option. # i.e {ml_classifiers}/snippet/ml try: from issue import classifier except ModuleNotFoundError as exp: log.warning("generic ML classifier not loaded", extra={"error": exp}) classifier = None # pylint: disable=invalid-name class Generic(Spammable): """Analyze a generic spammable to determine if it is spam.""" def __init__(self, spammable: spam.Generic, context: SpamCheckContext) -> None: super().__init__(spammable, context, classifier) def to_dict(self) -> dict: """Return the dictionary representation of the spammable.""" spammable_dict = MessageToDict(self._spammable) spammable_dict["correlation_id"] = str(self.context.correlation_id) spammable_dict["title"] = "" spammable_dict["description"] = spammable_dict.pop("text") return spammable_dict def type(self) -> str: s_type = self._spammable.type if s_type == "": s_type = "Generic" return s_type Generic.set_max_verdict() spamcheck-v1.10.1/app/spammable/issue.py000066400000000000000000000017401444633272200201520ustar00rootroot00000000000000"""Process an issue to determine if it is spam or not.""" import api.v1.spamcheck_pb2 as spam from app import logger, ValidationError from app.spammable import Spammable log = logger.logger # Expecting a module to exist in the directory specified by the ml_classifiers config option. # i.e {ml_classifiers}/issue/ml try: from issue import classifier except ModuleNotFoundError as exp: log.warning("issue ML classifier not loaded", extra={"error": exp}) classifier = None # pylint: disable=invalid-name def validate(issue: spam.Issue) -> None: """Validate that issue contains required fields. Raises: ValidationError """ if not issue.title: raise ValidationError("Issue title is required ") class Issue(Spammable): """Analyze a GitLab issue to determine if it is spam.""" def __init__(self, issue: spam.Issue, context) -> None: validate(issue) super().__init__(issue, context, classifier) Issue.set_max_verdict() spamcheck-v1.10.1/app/spammable/snippet.py000066400000000000000000000021031444633272200204760ustar00rootroot00000000000000"""Process an snippet to determine if it is spam or not.""" import api.v1.spamcheck_pb2 as spam from app import logger, ValidationError from app.spammable import Spammable from server.interceptors import SpamCheckContext log = logger.logger # Expecting a module to exist in the directory specified by the ml_classifiers config option. # i.e {ml_classifiers}/snippet/ml try: from snippet import classifier except ModuleNotFoundError as exp: log.warning("snippet ML classifier not loaded", extra={"error": exp}) classifier = None # pylint: disable=invalid-name def validate(snippet: spam.Snippet) -> None: """Validate that snippet contains required fields. Raises: ValidationError """ if not snippet.title: raise ValidationError("Snippet title is required ") class Snippet(Spammable): """Analyze a GitLab snippet to determine if it is spam.""" def __init__(self, snippet: spam.Snippet, context: SpamCheckContext) -> None: validate(snippet) super().__init__(snippet, context, classifier) Snippet.set_max_verdict() spamcheck-v1.10.1/client.py000066400000000000000000000132601444633272200155570ustar00rootroot00000000000000#!/usr/bin/env python import grpc import api.v1.spamcheck_pb2 as spam import api.v1.spamcheck_pb2_grpc as spam_grpc generic_ham = { "text": "Dependency update needed The dependencies for this application are outdated and need to be updated.", "user_in_project": False, "project": { "project_id": 5841855, "project_path": "gitlab-com/gl-security/engineering-and-research/automation-team/spam/spamcheck-trigger", }, "user": { "emails": [{"email": "integrationtest@example.com", "verified": True}], "username": "IntegrationTest", "org": "IntegrationTest", "id": 123, "abuse_metadata": { "account_age": 30, "spam_score": 0.12, }, }, } generic_spam = { "text": "watch fifa live stream best live streaming [here](https://livestream.com)", "user_in_project": False, "project": { "project_id": 5841855, "project_path": "gitlab-com/gl-security/engineering-and-research/automation-team/spam/spamcheck-trigger", }, "user": { "emails": [{"email": "integrationtest@example.com", "verified": True}], "username": "IntegrationTest", "org": "IntegrationTest", "id": 23, "abuse_metadata": { "account_age": 3, "spam_score": 0.32, }, }, } issue_ham = { "title": "Dependency update needed", "description": "The dependencies for this application are outdated and need to be updated.", "user_in_project": False, "project": { "project_id": 5841855, "project_path": "gitlab-com/gl-security/engineering-and-research/automation-team/spam/spamcheck-trigger", }, "user": { "emails": [{"email": "integrationtest@example.com", "verified": True}], "username": "IntegrationTest", "org": "IntegrationTest", "id": 23, "abuse_metadata": { "account_age": 3, "spam_score": 0.32, }, }, } issue_spam = { "title": "watch fifa live stream", "description": "best live streaming [here](https://livestream.com)", "user_in_project": False, "project": { "project_id": 5841855, "project_path": "gitlab-com/gl-security/engineering-and-research/automation-team/spam/spamcheck-trigger", }, "user": { "emails": [{"email": "integrationtest@example.com", "verified": True}], "username": "IntegrationTest", "org": "IntegrationTest", "id": 23, "abuse_metadata": { "account_age": 3, "spam_score": 0.32, }, }, } snippet_ham = { "title": " internet time", "description": "Swatch Internet Time (or .beat time) is a decimal time concept introduced in 1998 by the Swatch corporation where instead of hours and minutes, the mean solar day is divided into 1000 parts called .beats, with the same time around the globe.", "user": { "emails": [{"email": "integrationtest@example.com", "verified": True}], "username": "IntegrationTest", "org": "IntegrationTest", "id": 23, "abuse_metadata": { "account_age": 3, "spam_score": 0.32, }, }, "files": [ { "path": "beats.py", } ], } snippet_spam = { "title": "Deep sea fishing Dubai | deep sea fishing", "description": "Beach Riders can be easily accessed from a variety of easily accessible tourist destinations and look to provide you the best [deep sea fishing Dubai](https://www.beachridersdubai.com/activities/sport-deep-sea-fishing-in-dubai/) has to offer.[ Deep sea fishing](https://www.beachridersdubai.com/activities/sport-deep-sea-fishing-in-dubai/) is one of the best activities that you can undertake with your loved ones. ", "user": { "emails": [{"email": "integrationtest@example.com", "verified": True}], "username": "IntegrationTest", "org": "IntegrationTest", "id": 23, "abuse_metadata": { "account_age": 3, "spam_score": 0.32, }, }, "files": [ { "path": "snippetfile1.txt", } ], } def generic_request(generic): generic = spam.Generic(**generic) with grpc.insecure_channel("localhost:8001") as channel: stub = spam_grpc.SpamcheckServiceStub(channel) response = stub.CheckForSpamGeneric(generic) return response def issue_request(issue): issue = spam.Issue(**issue) with grpc.insecure_channel("localhost:8001") as channel: stub = spam_grpc.SpamcheckServiceStub(channel) response = stub.CheckForSpamIssue(issue) return response def snippet_request(snippet): issue = spam.Snippet(**snippet) with grpc.insecure_channel("localhost:8001") as channel: stub = spam_grpc.SpamcheckServiceStub(channel) response = stub.CheckForSpamSnippet(issue) return response def print_result(response): print(f"Verdict: {response.Verdict.Name(response.verdict)}") print(f"Score: {response.score}") print(f"Reason: {response.reason}") def main(): print("Checking ham generic") response = generic_request(generic_ham) print_result(response) print("\nChecking spam generic") response = generic_request(generic_spam) print_result(response) print("\nChecking ham issue") response = issue_request(issue_ham) print_result(response) print("\nChecking spam issue") response = issue_request(issue_spam) print_result(response) print("\nChecking ham snippet") response = snippet_request(snippet_ham) print_result(response) print("\nChecking spam snippet") response = snippet_request(snippet_spam) print_result(response) if __name__ == "__main__": main() spamcheck-v1.10.1/config/000077500000000000000000000000001444633272200151725ustar00rootroot00000000000000spamcheck-v1.10.1/config/README.md000066400000000000000000000001441444633272200164500ustar00rootroot00000000000000# /config This directory should store your configuration files and additional resources if needed. spamcheck-v1.10.1/config/config.example.yml000066400000000000000000000007631444633272200206220ustar00rootroot00000000000000grpc_addr: "0.0.0.0:8001" log_level: "info" filter: allow_list: 1234: "spamtest/hello" deny_list: 2345: "spamtest/goodbye" allowed_domains: - gitlab.com max_generic_verdict: "ALLOW" max_issue_verdict: "CONDITIONAL_ALLOW" max_snippet_verdict: "CONDITIONAL_ALLOW" gcs_bucket: "gcs-bucket-name" google_pubsub_project: "gcp-project" google_pubsub_topic: "topic" ml_classifiers: "/path/to/classifiers" tls_certificate: "/path/to/server.crt" tls_private-key: "/path/to/server.key" spamcheck-v1.10.1/docker-compose.yml000066400000000000000000000004311444633272200173600ustar00rootroot00000000000000version: "3.8" services: spamcheck: build: . #image: registry.gitlab.com/gitlab-com/gl-security/engineering-and-research/automation-team/spam/spamcheck:latest ports: - "8080:8080" - "8001:8001" - "8002:8002" - "8003:8003" - "8004:8004" spamcheck-v1.10.1/docs/000077500000000000000000000000001444633272200146555ustar00rootroot00000000000000spamcheck-v1.10.1/docs/api/000077500000000000000000000000001444633272200154265ustar00rootroot00000000000000spamcheck-v1.10.1/docs/api/v1/000077500000000000000000000000001444633272200157545ustar00rootroot00000000000000spamcheck-v1.10.1/docs/api/v1/spamcheck.swagger.json000066400000000000000000000076131444633272200222520ustar00rootroot00000000000000{ "swagger": "2.0", "info": { "title": "spamcheck.proto", "version": "version not set" }, "tags": [ { "name": "SpamcheckService" } ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": { "/v1/issue_spam": { "post": { "operationId": "SpamcheckService_CheckForSpamIssue", "responses": { "200": { "description": "A successful response.", "schema": { "$ref": "#/definitions/spamcheckSpamVerdict" } }, "default": { "description": "An unexpected error response.", "schema": { "$ref": "#/definitions/rpcStatus" } } }, "parameters": [ { "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/spamcheckIssue" } } ], "tags": [ "SpamcheckService" ] } } }, "definitions": { "SpamVerdictVerdict": { "type": "string", "enum": [ "ALLOW", "CONDITIONAL_ALLOW", "DISALLOW", "BLOCK", "NOOP" ], "default": "ALLOW" }, "UserEmail": { "type": "object", "properties": { "email": { "type": "string" }, "verified": { "type": "boolean" } } }, "protobufAny": { "type": "object", "properties": { "typeUrl": { "type": "string" }, "value": { "type": "string", "format": "byte" } } }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "$ref": "#/definitions/protobufAny" } } } }, "spamcheckAction": { "type": "string", "enum": [ "CREATE", "UPDATE" ], "default": "CREATE" }, "spamcheckIssue": { "type": "object", "properties": { "user": { "$ref": "#/definitions/spamcheckUser" }, "title": { "type": "string" }, "description": { "type": "string" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" }, "action": { "$ref": "#/definitions/spamcheckAction" }, "userInProject": { "type": "boolean" }, "project": { "$ref": "#/definitions/spamcheckProject" } } }, "spamcheckProject": { "type": "object", "properties": { "projectId": { "type": "integer", "format": "int32" }, "projectPath": { "type": "string" } } }, "spamcheckSpamVerdict": { "type": "object", "properties": { "verdict": { "$ref": "#/definitions/SpamVerdictVerdict" }, "error": { "type": "string" }, "extraAttributes": { "type": "object", "additionalProperties": { "type": "string" } } } }, "spamcheckUser": { "type": "object", "properties": { "emails": { "type": "array", "items": { "$ref": "#/definitions/UserEmail" } }, "org": { "type": "string" }, "username": { "type": "string" }, "createdAt": { "type": "string", "format": "date-time" } } } } } spamcheck-v1.10.1/docs/architecture.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-v1.10.1/docs/architecture.drawio.png000066400000000000000000004056721444633272200213470ustar00rootroot00000000000000PNG  IHDR^ sRGB"tEXtmxfile%3Cmxfile%20host%3D%22Electron%22%20modified%3D%222022-02-28T19%3A45%3A22.236Z%22%20agent%3D%225.0%20(X11%3B%20Linux%20x86_64)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20draw.io%2F16.5.1%20Chrome%2F96.0.4664.110%20Electron%2F16.0.7%20Safari%2F537.36%22%20etag%3D%22Dn4b99SdA6W9HpK675Nk%22%20version%3D%2216.5.1%22%20type%3D%22device%22%3E%3Cdiagram%20id%3D%22a2aPqvSgi-SdpDl3ol0m%22%20name%3D%22Page-1%22%3E7LzZlqs8ti74NHl5amADBi5F37em8x2m7216ePqSvNa%2FM%2Fc4dUZV3e8YmSsiCCOkqTm%2FRhL%2Fv3CuP6Qp%2BVTGmOXdv%2B5YdvwL5%2F91v99oCoff0JXz7xX8cftzpZzq7O%2B1f1%2Fw6iv%2FexH7e3Wts3z%2Bbx9cxrFb6s9%2Fv5iOw5Cny3%2B7lkzTuP%2F3jxVj99%2Bf%2BknK%2FH%2B74KVJ98%2FV%2F4v89%2FWwzpbqz3WaxP59Xc7rsvrn2Tfs71%2F65J8P%2F70wV0k27v9xCRf%2BhXPTOC5%2FfuoPLu9Q%2FP6JzJ%2F7xP%2FDX%2F%2Brw1M%2BLP9fbhj%2F1%2FOM87TnZT5i%2Br7fh%2Bj7v%2F62siXd%2BnfIfzu7nP%2FEYK%2FqJfc%2BSYp%2B3%2BFE%2Fwtnq6Xv4G83%2BGMyf%2F5EvqiPHD6K%2FdtiPi358X%2Fs6u2%2FAgCTJx%2F7fJlO%2BJG%2FN9B%2FQ%2FY3bfA79c887P%2Beg9s%2Fn6r%2BM%2F7%2FXEz%2Bznz5X43%2FOzTwh7%2FR%2Bf8Rqfv%2Fe6TgDH%2FQj3X%2FS6v%2FPTR%2F%2F8BnyZL8Cwd%2Ffr2Ln6H8152rA9Zyd0yTyhHAL9PzK8EvAWB19KtLciCG3%2Fns2M4LXZEiVwxl9%2Fm%2Bv7DsLp4vh2VfElO%2FPFZ9h%2BLwCtQuDl0yTbvOhp%2BPio%2FqCqKfm9OMZ3crMvNAskBjJn14vtinKY%2FO3j4VoZTDJ4D9YhMeY2v4HfCzHEYSpnuvhJxtXxZUiu%2B59GBtAtx42Bt6f7qVI1CiBBxeUkfCkGLa4zj5XQiDgT3UggyyWxHAxrIZjhjf03S587wybMF1IwfqHd2%2F3m%2Fc%2F%2FP1P1%2F%2F8%2FU%2FX%2F%2Fz9T9f%2F89fSmNamAJ85TFkkEvygYkRiUqI3%2B%2FiDfILu%2F35WV03F6QgOUowB5BGZ8izIvGEHxgL%2BNM5b2s8CRtdvEv%2B%2B7iJDLwYmowCv3HdFwjO6Y73d5bs%2B80OZ46bo4Lxp7tguw5WPvdPDOR2gB9%2BwxZncQynZ%2BxllAGvfHuClceyAZ60xR%2BbOiCPitPjZdjw%2B4M0e%2F5tvAAJXqx3AptFV5%2FkJBSMXkfwZ6vcSpmIeIeV5NLsmRReK5bg5KMlnu5SUdcvYFfeDmxVh3%2BC4oP9iJ9bFEWABwL3TW7NqOnkc7rbtsvGZZHTGoh5AX728Iobepx1YX5YAjmcFcu6UfxOwYureAK9dZw0dPiHCi9QKFg0%2Bueb1UakUkPz5HaQdxevzlhirngVHai1w%2F5wHZC9T2noCsssF7U%2Froh%2BRiX7YGzrC8PE6kF9hbP2AmzLA1FXgGGat6i6oEtghcLufREDD3k0OTrsUFgjwiXcPzNJM6JUswvXlSAvqpc60BKwfFW6WCbDKkY3dpAENpph9CAkqt6RCh9tSAwoHkYSmYoNONWfAeeJ57MVaBl%2BTjN65sXcNEsBmV5LVfqYxiwOo6AEHGqtbYoWDKSdC9IoWHJfkC8guwZwFkCjCSXZO5idO3wKZwieWnJiy3NstRcRr0qcwEVkiUKdp7ECXA32PXSWRvDS0BKA%2FgzfSmWwG9mQp2K3XAwjwmGy%2Bz5fu%2BYMl3B3wSPHl3vBNTeQo9lmldu%2BNWXbiBzQP3to1gWHAR1F5fH6wkexUvpWdrdMTsvkT4Jk97l0HJinVpsYu4Tn%2Bw1Id3Ot2LJhhdoB1QuUn0nI6UMu4%2FTYpy4CI1hgL2Bj5G7tigNjpqDZWMbdBRN3tGkqdwT8uXah2pvL51No8SHZQFZJPDuXR1mywtcC7IuLOSOWu8fbbA%2FAxqx9eTaRm70ChK%2BG%2Fp73rLoqsDonDX%2FBB6qVRC%2FpwcEyZR0B%2BHNpP4WA907U72%2B%2FoTq3fkUf9xHLARONnIu7sZB%2FAQf91WDMLpYSqk2pf6HsqXCjjZVdbWvpYFL0cwvKX6wPtRrvFCbFLHa8hzKVajiLigFnEUrz0qGOXnMNXwG3QR6pPQILmgMOFUP0ZOGUc6pSYX%2FmWoU6HVZyZnhpDOufZWC2pcqamHwP5xp0zg5UxTGeTYnN8uUaHgi5wMFp2D9gfuBfNQcVwYbUdl6ZMy0cTXFuSbw7iqxoIWA%2FHicdWUmEQNviCkilCWemNMrO4B%2FES9mdUgy9u8WNt4tFo%2FvddEioGjCTCWgEfYAnv2YTmwgHpknigFB%2FRiAqmrz%2Bckokbw%2BL1hw2hZMO2puInS7EsO9NT%2B5qCYPoKd2uhI7hJa789e4i86ZLBiSrxXs0zHEP5bhftvkjQXErWxM%2B6LVzjxE9RZXXHj1FUXzz8gzBUQWvFHRF1NSmpNfrfYcdCrc4M75bIbMqqhJW9FBsHHulAMPd8hSBUnbc4n%2F%2FpTgznHlkEETEWnWcMZjHtxRKRmoRHavUOsrxypoEKz6yj0iAeTRM34oTc%2B5KPmNVnv2DwpYV7SqPWMPGuBe%2FYRasdRPVOrFcb%2BoOS1xjFSs8yqe6rAkA9RgDUVDbPn6RPb1ucK7iMdlxPY6O%2FRFamyYAWoS0oxoHg%2FLOV2Gz%2BwyKgAfgA3ZEN18KYYcmfCzpKH4zgmIlqLJxlHlESYDvdEkinyg9VglGjQMeynphDeEVvEhOULnVsMrbizUkhUXTDrzXm34CEVfo7OBjwgEteuJYCYPH2l%2F41EtiCROF8j1%2F4lJudaZAqTf1kdiz9K%2F9ja5ydqwcI0ma%2BJPKzFTJ3Ak4roJTXDvVA%2FDcyW5WyPgy72ZYycGkhrMJVDP%2BXJfkbnA08pmtDQ4erP2nz1aFRnuKfE8Qpo6Qwv0msHLAIY9TlhCA7t4sG6NISg4aSaNB%2BvkVrc%2Fl%2FHbPgf1nTpxmb9HIJK9r%2F1S%2FDGr2V5%2BCQ0F%2F%2BuKi%2Bx4x0wzz0k9a2Ol9bEaXn7aQhdfPXjJoA0w8yZkORW9AMH9VyAEYFZMDkKQ%2Bo1mHXYW0xGuZrgtYqH3jFRCfzsm%2BNEt3fF3NboVdmLkbEK4AK2H1E6ju6%2Fqg577V%2FWGnUsyVv1kheAHWkKuEmgxxYM2%2FYN5BiUpt2CXryMHDW0gFU9C4lBucsapi9%2FxvbWXqHbYlx8KftnDqT1u%2BhdpCsRakXy3bQlafSu6on9lBKM%2FWKajewFP6TfihfuNHoJ5Jh%2BV9GnCS2TxXOMLZi%2BYWfne0z3TXEcbFhVk5z%2FS2K%2BCHBhvMrl3%2FzLa%2BY%2BKuAcJDmdDATx7406GsmG%2FBe4goDMMC3zTYHTE8a8b96tyYAiVbK3uSYfa3t8TC1Gfb9qhKBUCkZvVszgaTxWqed9IDuLUJqgS4cjajHMF6nidC%2FDdGVB0GyzflI%2FcQotnCFr9MDTZn8SojZvwTITFMg5AwKGvkD%2BMmlUwJGmFkOYzjpN1sKgiM895dxg%2Bb0bwrsaPicTUJOkFYA4rDzh96L1VFMOmVNvgrgHqo5cQZ7I1RocGUoveK7eotGxWw6gj1l0Q6LCGRInuRTlrqTRxfzWtZvxj7UFqkDux%2BdO9apU5%2BXvK1NJs9Yqg1%2BkBEIUCOMNuTKlRwhcI9tOCuiJAHfyDyDrwDFYLA%2Bm8OCc4LYrOj7JAFWOIlM6dWVgyQpTWmU9l8vEfp98yRFczmdYc37HI9ymX3KnnXNwDXGpUkXr9ouLMm7RbCW%2F7m7kAfvRcOnjWUGsziIUqy9%2Bi1f%2FEsBfYPu3jioOvVpjllWKhsIV4vFDLph10YS1Mzd%2FeKO2xPs872zTFJZom8z8loIPkC2mmmQCKlKwu4BvP9toBA4KewT3ojnSuBGgN1QvtiNlv4uJfvsQdKWBrOLRi%2FAEL1XmZ8Xj5FrpQImEAidtlV%2BTpjS3DiuMqZPHU8pSnhPYpze01oZenVFjr7XCeBtb4eDDNbNa%2BuhnS%2Fv9kDO46ycpQf7z0CLv%2FGWZEgPcXisGQ43ljwaSg5V2hRFtVSs2WwST6XP3hxg9GHHz2TqT9BU8OqiFx9TgKqHqF2mFgfwfZDQeqQlftNZI%2FX%2BwflkvLe7gNlVA5X2j%2Bd1SHnMTgwrZ8jpDQmlakm4Lw5drgDaddW4%2FqYI6B6vJ1Bh9iV%2F8zr22ETNKMPhT%2F2jEQ5xRIA2J%2BKd82i1EoUIeUpWpW1cyVLxzL7EHXpATpUKXXYfkXQsvCmXuIh0kABAWPBzzAZ%2BawHC0shtbc9eXYUCIC5NnDW8pPA7mEml3TZTdJH2KNPthO7zbc5brZ%2FNKmwaP5dxyRavydv7TQh48%2FPXdAdWysEVHvm%2FeQ8c4CkxyGW5mDP3LsE6ZB7m%2BGRIP8BPoZ2vZ2S22Wkd%2BT3HCGEtqkmBNTNONizNn5YwTs37PGagMLeM92x3oyKvf%2Fq15LDXvJxBiQaY4lDTzfFu920EW7OIPwVlwY%2BWOMDTHmYQBdkTxlhrfN3slX2vkQ1wi13UMbNBTOOwxDxz6NP0DvTiAHnSmrRyneJMFFdrhGdiscjV6S7CmAr7J0SDwpaOe%2Fl2CDZOUcGv7kevkhR99ewVJxxVWW1KzHKv%2FtL3nft8wMZA4f%2BbNiuvhxVoM4OZNhYNqUbtUMd2HmbrSaAc1XEiPunPRFz9LhI5PzZP4MUB9IC0U2PS1tmNa4dwffEq6otGoWx4hHcfnjDu09yh6bB9sdo6EwYDORU9v7dOoda0OYNE4F6Wb9qoFE1TKgK7qx5BZ3ezjtxTwXJkjHTuIhYZ%2FDsz1PVeKAd5OKmUQPaUUXVChqUFsll8%2F0gnG%2F6cEb2qRVP%2BYTVt7MjHD0fxnD0XJTswCegWdfGu%2BfwFlLlM8xaoag%2F1AxiJfzZgIKXvi%2FdDMlLcQAxOEq4y9PcGG8b6YGknGj6uYzrhLGWIiD0fjWA7t0kTUuLkaYD0sxP03u%2BlYkdsIEGdYmKQcWcghGp6EuLv61DCe7OfkMN5ayWIb4g3MjqsW5v22EyRV0A0aZlD9vnTkX5Ya7R5A%2FeAV%2FIJypC7RQosD3%2BqcVJ6zL%2F0R73pz3niWzDl629YWxhXEBUsNkDd7gTJZuiyEabEPwoVTaFqrsIoLPy7yEEESAi9dqePfcRtkPw6kxp6wf%2FGdcUsPkvVRX92HMHmgRXI6%2FNecBfxD9400zD0wF%2B8IVqc1B6wYTGVkX1dPBiVH9Y8IWV8po4CeFVrmJoFr7pKSpRVTYMP3JqoAPmGrmXkLIo%2B4F5ZZ2aNfg1qoalSSgryJF3bKX9g8bHC0%2B2MlNimT5G%2FmlKnM15SKHUo%2BtyunK836OL8iXnMegjoTosfrPywkYMdLomPT3IS9Tgig7l%2FHiMa%2FprJW6EPVvT2QHiz4xqK2pbdbnj3kv9Su6lpSBTSA81SGTcfbIkJOAmWYqzCUmswy2B%2B7kXxzt5PNwAcxx5SHACqpGNd6Ky31EEzw9jYNGBMAEgJbaZRmh%2B%2FZpYHRPOMqY%2FyLQuMhKrHVtECisU%2BfQ0mZRTWOORUgd0bu0PBSU1rker93ahNK9XRfc5VPd1DpHQsS2Ld18yDah%2BiUOZMRoyZmG2OKaAamSO5HUBmXITCbW%2BKw5b%2F1HRla7A3KkBvg66JqSxQtIK6JDyOSGbpkKtBSW3JkclMMznCKSdVb6o1v3nqhYybL%2FkfWO6OhDDGJam%2Fjq90zZ8HUht8v20OIwRe4g6B%2BKhO31GnmNgQbkoQ1xYu%2FYoRzTHPq%2BpyEw4Bcxx6XYQJXA%2BsWO0vpF%2BOihIlQG6u7RqG%2Fh0pMv9syjLBaINUPo1TmN5dlpZRPl01sHBIsPHoX0mi2ZLwYEJLXXzYKtxZkNc4pI7pjq2BGMjw9gocE7RHa2IECRjspylgQp9JEfyu%2F9Um0pgPaRZ%2BPsaU500x%2F6fEQlVz8SoUF8gcZ01tEUh0hALdMB%2F3smazWl5l9MQL2nopmzo%2FtxHJxA4IIEAa3wmNAFz%2BF8PPE187%2BovmqERsL9xIqns7wjRyw%2FieFQp8hEREcxxXoAAwnHsTkERoQ6Fyj6RS6q%2FHGBfT%2BEB%2FMHPdiH%2BFs7V9sg3iyjva%2BjUWt40%2BpWQSrEhm1mnAJxnUMXlHBuiHH1Ap1I5%2Fme9EztE0MPh8hCHNOC7NU1AnzAI3DLIshSuXvEFU%2FeEuK2Nz4YNY8hNS7xvAg2ULyyB8Gm35BaXENoUPb5ddlneGmhGoYNvTZ79Ih%2BQA0o47LlJErKEecgOXFcBwekzQQd9THhQa18btOC%2FtYnwh3gn%2B8IoIB2TBLOIkiTwRMDEqbJ%2BWVTjO9xP2b%2F184dA6a8nXgHxjpXIEYscGsTkrINIKUqkkzn2fZTZx8lS3rChx%2F%2FeYLQ1Lbn4DB8cFfKrwqW4G3NL%2F8QlWOSuNvVOybfIu7b2xd8fb4lTJPfhuF1IPkbujth1g5Ca4kQeu55c%2FmV5pfksaJ5mtNaVuxodS5DpCX5Di2W7XGpKBTWrIkv8DcWytw7ON2HvapZCHjprD3us3x7HV6Z3zhExDZkhoPwLoCorXWK0MiNazZylkGyhYZ2WMPcFz2zql%2BGrZcK%2FqocyonqfUJyDcbex0lbhIHyIa58G6SRPh6re4dG4%2BVTj49%2FKWj7HOWDYEK3fbUoTKnwEWZLvfGlWErcfOzfnQITaNJuTrF3qtTvvRzQb%2FdZvYx3ZEEV21BcSCvfmnxY51KKCWqw9hS%2BeIkT2J5SoLMcytO9mCoCKUgAhZOAT8FvsiMzU2%2BJPdyZPHkPIlzgcgdzXb1UdSOU7jzGuR%2FUZceWK8Rmq3%2BLPyEzoLyCtoJG1ksu0jowqnl4%2BxqeXSYCx3gEINF7oRECLigAtcIe05ei7NDuOoJ%2BGuCghgIr4Vp07mxlPSvEhxIMW1YHmkXNnNgZP8QUk6S%2FKE6795bvd8K%2FB1YF6G2G23bKn0o6wcRbMP9x0PmrDYc79DwK4cl%2FrHzCjWQ0hukkvGZdzjmItmoIa%2BYPm1DDrdCr7UoRuvzFKlAUt%2BvwrHpXG8KHcAaZyi2BmCu1Psz0VNDDg0T%2BrWihRgwLmHAJUgsot2BVJHVYuBNM%2F466uwBDPrEEQpyJsxeqAgLBhYLBxw1Q66KVgIOEMcd9FJ54lBfHYjPdJElC0EeJ99Ob0b4LLpuJstUnWocWyXhkFhAMXWg1wlfWuEivXs8%2BrIuq%2BiQyNPx2hhJNk9IK318qhPT8QNfZqlRnOBA0apQajUr84y5H4zEwvQoHPhSr7Fxk9bKAHCnd9hYyo5z8U8L8SDIEXJTBILcozseFpnRB%2BDv3DGdCT4SQZHU6utQZMgmSH9nAsb7yGtNIByjJa7m9MKgSUu5S8QbyVHLQSA%2FPJplyI2AZZC3tMij8nAzFsPHLCiv1%2Fqn%2BLSigU5mzhgOFaKOaVl%2BLCeyUFAAbMvHPMWcFRfmLoWbW1ERer9Fm8d57VUr4qJ68%2FBBBdtFNEKBLplfEzmnVUClr4eIKvvTtoeVcBvga%2BDjfB51m0uIkpmyNW8KHR5jeUDfg9dioRMn2Z%2FFyea1wPpb%2F5JfH050lEDd6%2FT0HiBEdHDcqvS%2FHAgVav8D0jrl3FnhD5avW2ip3aQHaO1xoTVCOOFYj%2Fv5xt482DCa3dGl79BKu4qDBA3E%2FPCbyl2syDgr1jJfMFNQ6IUX4akJud7xdylfYIdU6Cjhgiw6q00P0aDYhAXd35H%2F571mdWSv3n%2FuM6OtCKvWKHhg3yoylZ2OfdwOA1xrLKh%2BcIZ2%2FSrYm0rFenGKFAc0bTb7RDhOzAE%2F1TLCnWPs9hYNAmX3Iiri3KoY3ZkxJJWm4oZu0yBZ%2FQSZR8kn47D4ECdJFw9xrc2b24no9P0W3MvH2focrd0RogRQ75aG%2BMPfNcSBpylz8j6tx8ztk2JrWfAx4daEVXYVmgS8%2FAUa41eBdouZXZA85U0QGkDR9uKUmmMedtMtq1XD14%2F2e7MS6S%2F49Cdp9CSgsAQ6D3zRINy2X9%2BdzuxUI7vElZ4Lp2I0F0S4qfQ%2BVal0eRvdLlck7Sr48b8MQyVnddG%2BUKZcC4hEV2Iyi0pfOCwO1wOwLNZLOp4cME8PMKAVmklqHyVEP8Z7up644fRCrT974sbQH5ztqIbajDN65ZWZH25Iy75175bp%2BlpH%2BUcGWYaGjw71etUJXtpfqdnx%2FTp0qIB%2BLYfNEe5mPN4o8fJeumnQVacKYpaDG0sgSRQJq%2FHUdsi4XIycWenEtEVyJJcu3JhHUp%2B9Dfun8a5%2BrU4iniWy%2BD2jUlWb13Kc5ulYNzzIt6vJUdymvoVipOhxTf%2BAb30EWk0SN0oGm4prl21QfauXTj%2F%2FhsfsxdSM1cVS2polK2eTqDbNyRcGvNE3nf8QaRsNeOol8fDnM72n0j3oZ9Zz7fabrt5ghSLTT4sRmZvj8zIU6LvVqaSq3S%2BPabNPzlt%2BrqOvcLi9Gqk8GNaK4lhno%2Fivsdz%2Ff6B%2Fl59FgVd9D17b3I0EDhjhBX0OEKir2EjIJbFKxha%2BfsxfKTW3Kz%2FcKHdb5LEMdi8ce8Ekfhz2uIX5LDnA%2FiHgfpIP%2FZUM3fwRVVh%2BLKtUc%2FohAF%2BkWBsjdcyIySuFEu18yqQQht8xxvI0tHakPYqqVoaC7MKB8hY8TcjkYbr30nboz7iss3o087VXe1w5FM11YArTz25O9fGnPL2mixVAdPLnzOsRwdvhaKhSA%2Bc8ZB%2B5cGT2oCmcgL7j5uv5lRfAjvXE9VVjkbFqFRvPCEPcT6jG65Z%2Fq1P8RdwQ6nBz7XNaDXP5uTM8PnkB5RdEK%2FpJ%2BFkqh4R16fl86D1xk4%2B8wayts69qwQozh5E%2BXxHh5sUZWFKo%2BVSE2vB8mUDQuNDtRwhvp%2Bb8WvfpjbrTKhmuL47nrQsaDLTvWmUCqT8P812Od%2F7kDbeejaTuMVOyodBp4vKBeH8%2B4F3%2Fn%2BdcbDS97DFxCFOigOJwV1SiOqDu2Hh773trXXZrO6z4Jt1XG3qkriOFFDWbRMKyDZmHiCEVUDL9K2WReiSioWa%2B7E19MBfqzj04Q5tH1HtDxZ8%2FVR%2BuAT72wtPaOGe67uzfFSpK%2BbtrTdtLcnGzGikaAjFM0DK16hipSmh%2B7SgCS7DB1ktbtxOlLAbCst32J8qsd2SR93Y6fhBG5TGWyJEnqMMPqOzM7sy2n9Yne1EB9V567QqqQPCECsBmmc9JwHgcLn7OsX%2F8SE5SmJ5SXK05ov3N2wN9LYLNLYztEvtsyi8aM7vnNgqe%2FywzZJeRcOusbS4Dz6Ails57c3YT8sBcGvm9WfzZogMlNSO%2FODyMfeOg840mzD%2BwG9FIsUxct7EYqToQXvrBLShakNwqrNh61vqyF8RKhsp8FTnN1tGnwsWQa5e7aSEcZK0OMjBBeRnhK8wmCC6cDLsPA9xSXPJDV8rfxTeUHMKRx7aTYlEVF9S3fdOa4cOwQF%2B6Pq%2BGdtO29YvLJjQaSOubJArNjjKxRpaQm%2BlTM3lST5glLbELPd2l5Y07T6%2B%2FEoTag5HAttLFbSC0E5UeJfp6X6IKefU6WyXdyeF5rgHnNKyILFJCm%2FjsEJbfTj6TzIEZYHAERPTzzWV4JHSmS1zzX6WC0Wmt%2FYCqNIUCsawCEWeKfr3c2hpfeXQjwJOKvHOjiPI8ZcMN086CgfYoEThLZx1%2Foyp6Nbu1Ho%2FWeM%2FJ9ZrsGjz24hdLZWtUXQm41S9lleliynjylqnlJM7A7xFYkjqbgAeeglRwEBSIFgeoFlqw6zOev042qQhaIDHsTdkwrijywdsIzlbwMZBlO%2Fw7E%2Fi%2Bz7OeXqtfS02Pow211mNjuyI8EIaKR%2FC6zHoKAMp8KjS7QX0bivZ2Ea%2FFvUsOS63a5PWhIhBmbopFmbQRs%2B7J3IswtVZu4qbxwfo0HfHzY7J01pBOOa5iaJqlAvH7EuP9gnUpXovkl4BXqHCUrOD8%2BCxqearDqa%2FYrDidlvmmlAqpYUxreiLvB1%2Bkl2AZ9Yjvi%2BX2fzoR%2Bp3s6SmD6YO1Iz3YI4akBqffucOrkVLy1TtQU3CBAn9TbQe8KttywPJploEJuSB8QPg7hBZieX%2BCHz3%2FMddVvqKrYtD885nfwxtiIZZaMG1ZhjaOc0gStfQr0ml%2FG0YTiQwXD32HP3YHv9igpqU9FCSrB41xEV0uHQQpx2VkO6DyjJI0Mu6WHA7Ffwp8c6AM03IIum%2FPbudbuvZn8f9j42wn0RECo9abRdWGEsigRXz9%2Bhix8Uez3d9lXk9dSm0rxkCM7e23I4ye%2FMQWbdcgFBcecw2KuGyIzmYK8qrInjzMp4hktA9XZ6T5fYGxNcK%2FF5fyf2td8fvhRHyyVyI2tooYKMGvYOnGFAh5i5XppU%2FD%2F0XMIO82MS0uu55bnE6mgXKMC%2BFHnypP%2F4Gjd7bCF3M6gyucPKjaG7TxO2IEBY%2F0YLQtDgxaNbUGHsFUEeNNgaNJsmqSyOX9LDDIOkk0FbG9xbpZ9oR6TKrSb0K%2BNcbllLNjB3vt4rOp1oGUYO7S58%2F7io9vEQwPLq3vvzgVaE0XEzFFRPolPzjZao2BGx6oPbD1hAvZdhATt84lnBXKdn6GWtzYrCFTGWV7oXKmCO32K49iD0YStyNeP3F5gJwsZeP2Ua6uPCCuCNslwkdnurp3nhp6msiDEWBDtiBoklBZ0XfFlcOA2tWSl5z0dBAf2Ci4sLVjtykRsMKd4wBWcmYp949rNuMbvMq1g57IhEWQO9o259j7mxvq83q49Pt0yDKh%2FSSVbH37IJRjnmGyG1yqkMvTqV1SeGIJ855FZEJdPY8lrhlE4wowi0SoNpz9NNudjIhELmZFd2TytbO7UIiKjiDeS1UFTF9%2FewnDwMaFo6flvCSWJYMDPuWi0JJx7RUGkwW%2FTB5%2BKOVhJrwEq%2BMsYi5rLTp6JARzdjpVjcVL7WSCnt6LMD7Y7y1hM4Sfjo7yiUyhKfu8Gznb4lJWuwGnrhQrT1y%2BpJDZNs%2FvgEza9j4ZNy7Jj1K0tK6j2RFVr4Yho35B7C%2FKDvlSXpeVOzV9Kjux53o7oLejy%2BsYrX3d96S0cXY1U%2B3%2FTKi2vC7dKOKj4Wl2Sj8lvA9MvnHTkOdPmxbDwkCd2kBgdSP7Tu7NPzQ5ZZ3O9KOPNyUx6YQt4FUiesMFNlIM7D1kD%2FZ1WcFhCEkAgzblP8S6oJoVTcRjUofqyDTNUBWQVMwt0t3vxKj1FF8Sth%2FPhKEhZCWUVDMqv8wSkH1JNvP%2BXqLgLCLgbVQymREmyQEnTH3c7WwyMdNsaVi%2FcV%2BPyZNvSodLyDwaDgnCbYdZMDQom51euefmkF3Lb8ebRNpfufaJJuho1mdr70B37d0JZTyidI3TzTuwi1vpWFmQ5FfFU4nzjU8gZxVajOnDjB%2Fs9SfGvoVoru%2B0ys73Qt8ccTVcypvzBvy8wfZex4N66QcaVFHvAza5O3vYXIAbxuGh3Xa6TdMxh8Gn3m8Xt2VlYPch0kShAhhu8mVPxmBRGvp9V9B1Zfk78DCgbK0sl4EsSo888HuZvm5FJYipGD2uxSxbTozOwaYAVVPWxFvjrKrhzOCYU68ZArfqdX88qPjLaX2nGYVf7Mc2GpBpwL7qhFYYzmu1AWoQiWhKo%2F5E6sKX%2Bb3lEELlXixF2FPSOES2Kx7PxG0JNKlLeHOa9EfXl%2BKgyN3hp%2BC75Q3kELz1Yrrrn8A%2F0oUu0znJsTw4ul0%2FWJKQPjh2b1Ag3fbuCPhf%2BmuzhNVE7tDaiRHv66kM9E4yUTF0D2U8Ou8lXdaHVt5MWf5PQ40HKCY%2Fx9qvLh2FtsEj6LDNO6P%2FKMDU51WLt9gVkkOCbyrSkEaN0C80L4pdCL6IypNu6EEmfmBnWPNnCnBZY4rhSJs1IOQOUOtUmqKX2Zcaz4svfA0PV95j%2F4tJVw4BzrXRZp8nfJaJny5aN9X7WSnDfqxd0h8wyYpzbQH84PTewJerQqfADwkZJUsO76fqbFalifbGUesg20IkmgvM%2FQCcYAcgTo4tOuanKWTDpySyKO5MJPHW7YRSvyDUbignva8SXlHhEHw%2BA8dK7MyXuDyxff0%2BQfpUpdyZrTls5yD0m9J%2FKsPQEE9Rtsv3Ys7b24FuJWvoMx18xerAsUKxVWb32mttAsyAu8UcYuR1k8hSb23uTLyrxsOTIiJhz%2FkteiS6jgdgaVp1SpKJfMdvgcVBmOJWUkwcV5gfY51HANFFhVdXvwDke7aBWW59v2UxWi88o%2FBCvW8W7T0KgYfd2omH8PFDlCnCqMr7TSUi%2BqYinqhrZxRU%2BxGDKaDHox7yOUSFZzf53%2BynbvehZKwamUU1Szq1wmpmYeUpKn0BmSHJZnH%2BXh7zLgoWoCSsc9mz0f%2BGJ0v0ILw09eAXm1T%2BX9pYGNzhXUvnrPcC6YL4pwcujfAuzjKzqemZ9STy%2FzkB0wax6USWfpuaBXPgK70g86r9AqhUb4nGa%2BT3q%2BOehU%2BMa1rPwEKVqThGTCidG13Ut0Wo7CLc34zun9Q0Ry0AAOrTwCpwrIB33Y2jLylgjWEiOVaHdNUt8%2F4IWqBX1uB5vEXkYAIzPyLJ%2FiqkG6wSdfRcR4fuU%2FYgEAqPwk1XEFhIZPt9Je1K5RyNWf5MLfcWsAoPrFhBV8hX1Qyt4Jp8w1wzMOmod4O2%2BaSLz6lHn9KmS3fMS20O7H9QDy25fYO%2BtgSFpzlDR42brXSlosjjKA2osTRrWmNugxD02Nj%2B%2B1b3VMEek52aEwVlLLaurOiZCbqwQ4V1l%2BwrXyuAz7tmYU9ZvMzkuOPWjVaa3hwPIDSBqbQm3irFzsoqA7RfxdGV%2FEHjcuGIe5gffEY7gSw63STF52Sgik6Aw0B1iFVbzKeRjqGbW1m2mrI%2BzRqxyjuvPtt3f3DAF8EccqrPC5oUNqaqH0lkAKDvu9sdXbBP1Tg27H11lYG2VsxDJ35LnHl0%2B2KSAol96KXDkTac4OBiaQwGfIS1aLilUEnANE64D6EHtWixIrpyPXXdoVUJje9lu1287fHV%2F4nSP8WYOeFoNPe0HDnVxcpcp4T0ZutqtNJf6WD8bOFtmoMlduSMQ9RfCoQhKWKnpOydtJVf52xT7gugudVYMJi87fsLXLrBXVRiQW%2BkgJP1ydTiAmpvs0pBpNfQjHkVv4fDZdiBDcrur94ZotQjsV7DjOov%2F1QPDi9VuufZA15h6ry4IcYskoXha%2FQitF30Wl5IV%2B2hP5vOS5meNgAJZYeTHYS1aU5i%2BrXe2girUG9LLwN5%2Bp14ZKq650aag0JZYSq0Nds0oMDAUFIZl1QtvDvU%2F1F6mbjgQzWzNNgeOrIOf06slWGlQcInXr65jcs5JF2MzGFHRphHiJQGnQccnkMxmykcOnAg72GPD1yyXTiRPYlrql54JYK0o9wM1Ibu%2FWmjsa%2BALm%2FPDTE7JVyQO0YO9rr8GZ%2BST7fN8wODVkjZ3eFn%2B589LU%2BY%2BiFIgO%2BgbRan40lBW1o7xT76XemitoXGdcpPcfj2aI%2Fb%2FHkz6VEDRE3Dmogs%2B7W7q4BJBU7%2B1X%2FTV4k%2FDY6VWPMKvOWw9HNd1c0MShXH7Hwtrnr0M41rU9f7uvfaSO1m1%2BZH9o4VBuEpihw3HeiAjXg9u7CMOlq%2FN2Q6daWiQ90LyNi1BY6OvKQWU0x51V19tes%2BZEvlo2KqdQAYXh82VFg81mFaEC14unfWQ7rSVP%2B656Ohd4TFAPKjdDHHfRao292dDpXuPCB60e3fxbNapoRQAOYGyg7uwELBKXWWepD0XKXQjrS8HiUGXuQQjN0uYwy4ck2iFprReqvbdNl6z%2BRO9yqBrdluh8wFPJL%2BWKHZcJjtrZdvVNah9I6f5%2Fjb%2BlxxeYHDdb1C0oydtFVDV4Cof9qV8J7DHL3u%2FMA%2B07yOXId%2Bi0FF7%2BnqOr1mxx5%2Furk%2FlvF7NXiDONDkMKt1X%2BTXFhw980pLEqb31CVxREyfmwdImEioPkWGwlvFUkaraZWaNSRVY67pi4f6IIJlUnzCy3l79jqAp3F0lCW8UuTmt2qrIanawSaAjXxD3BmMewIq9WvsEH%2Bga%2FBWi1UmW1O%2BmlPYc%2BP6pcUrtrUOG4UyEgmwozes4PcUf6msMDj9bB6bXVG4LfTd8vdSMu6GV74PSjxHxG%2Fb71360hArYF6HSQ2LBrsdr2%2Fe6xUMHfJXT6VS%2FRfecjOipX05REpo9Sj%2FhVzKGBcaSmpBHfNQCdaiT9uqsx0SExtOy05qaJbTlaNz7M7E5knOFP%2BSNFBcrEUfmlRwWIm%2FZ%2BEYEujA0%2BDF8XbdHcewMy6QcIF%2FSJSq702CcqxQ3tQeGbqgn5WiU6d0tt88Swm3FWjitZ4YROswnYO5TQ%2BxOqsmPCOIZ1l4OQIdHOEXbsxRWR%2BZ80qUWYwTebeUc3yGrj%2BmeRLewQk9OTD6fYeqDysodb9nvBKyBKf%2FC6sDlNcYqjoXmOF7eUqvPT0LJLWeh88Zxvj37%2BSv3VJdGNUfb9w5Xh7Bs1NxbXcuXMLdb8sVFOOYXti%2F5mdpYmVsyt2QPpDjoF8n7%2FGkOqpCeFCCBBnH7EJpLId0Z%2FCi8Zu25VshqNaUtp7zpD2ademYazKjcj0w63UGuhHk7eloeOom2fW1HcRE3Uz9v1Z7MZOhwRr1C1JJSkvI%2Fd90Ti4w8XpWU6TWW%2Fd%2B9eBuCnVoVG997doVWEJsi43f0r7qP07sGiN7FrrEehymUWlF8SBjqbFWI3iq9LsOHBAWeDlX2bba3ffOP5cJpnTFkPLFwvvz3cpgIX%2B9sDqdH7ZQOSf6adfLffy3Ip3a4V4F9VxbIjWVSj%2BaVDltrfWugy22PdiiOUArZsXqBGWuw%2BncIPC0%2BnyhYnD36xjW%2BKeTG%2FXT%2BYxcDsvcbhlpuwaQisoNddomY2kmtwpyhevVQ9yvJVtujWQ25qRV1IH5purSPYN1qxTvDgTGsUxe2LWmiLQdK5nIy6%2BD08vPFdgWkfPs2Wxf4MvbuAKbcqvVtPZh760Zx5H%2BIXN6C9pzjhyw5iEeQui6ELlGndGPs9Z7Q8Aef4UCydz8I4mdXjQhPWDRfgOHDrvdrR8gR%2F%2BxTjBS8iPKLHuiZQkJcNi14n%2Bt4ta1jRNprXPsd1Giteuzr3%2BRhzEGCek4aYusEudNWSN1I6AwqtDy4LzBJVknScsostWJDBa6%2FJmQIQb8UHx6FepASFtciDekWeM%2BO3g9rI5sBGgOep4ZQ8LNKWpsciiKvUelZva2fGoFqaTzvL1LeHykrkj3qxxDaVbBG9EgA2tA73vn9bshwYN9RCwAjlYSw87g%2FJV3aQfLIXKIcs8ru4DVl9DOb7pe1%2Bvuml%2BIAqA0gpvmN%2F38Ds%2FJeWO%2BiAyu1TbIcl9WzbfASonw9zX9%2BoHKKqJKCf%2FK%2BsOACF97wAWP77st7ub3%2BHf0wiQcIRRJvpvl0MMQXPBCyfza9r9WBVf%2BVS5AWf5Y7KH3KH%2FqL5sx8Dg9mlNllh2iVzrDwylRKhExmr2HoXGfmi%2FIgk9%2FuTpCVJcV5AgpCE5Vd5n47OUHgsB7fi0W8Rk0dnooySZ7UaBCNfEDC%2FFy7HT2eNTJBO2KYZt3GZhw5EguSvw5auhMqG4uuxtvSghSfXP3nhXMooet9htOn69Trft4Bq3%2FEn2sVqHnbVtfhHIgeJ%2Fq53ebNtxCF3Yh03Gx1dh2JoW9LK3dfngB8F0tBVGw%2BNDN5y7i4ReaJXUpnm4GluzKCT88MyRzB6VEvWZCURQImU4y49vdy317Unw6E1czFhCC3%2BixOpC9DuuPvBUjtBOOE%2BFHaiFfXi53o25kCkRwun0EZwXVK3Fdo9lNl2X94lR5ReUMTWpn5g4fEX1avglXgOYW5oRlocSv5cqWyZRfsO9wuFTtLECYZSJxL%2BrhMOWyutpXOslmU69rWpw3kHxJO3Ou6enzjLSVDCKnJwi98GQ6mXFmm0P3Ufyr8RuRZgHFouSlIH6NK3fWQDUwQoiX5vQkYp%2B5w9reUM6U4dW1MegqoQFl4pQiR3iU58ostTBDjPgtAOLG3y6yi29MdmOXPf7o9w3H2j%2BTSVdPG%2F091t0Y3L2u2f8FmPNoyhXTmaIsWPWQGtqa9sBjOXqkFHE96cq4oSE%2B4cF3nNYDu2f%2BUFkmGHeH4IKvhwB3ywGMiq9fyPLFCLL%2F7YxDF%2FGNSyDNTtaRc9OrzR2GGKmIEhmXeKb0euSIrnUurG%2BXq5XQs6yUeppaSw7fmsAXnlVmo%2FdmjSEv8hqkzk5pJkNSCuTIm9m6TiTeKX0iAaCBTkDpCgs1ncM%2FCyHMna8iNOIK%2F9pJ6YKBAl4WWrcmJUOsJ5NqWVihXnikoiShSZIgM05bal5sdKmTuZTOP9Hx4ck6roVxhJz1qgYkSv%2B%2BI48%2Fjte9I4MzsEOu3qWUdUn2QIjaOVbAwHB%2BGfz4Ql%2BZCqNeH9wDZXE65R1auXlbnMS%2F98Q6U4dqdKv4e3nZvXhtXlT0MpqgLGcnTFDS%2BH%2BQcXaIO8WlogJY8ap%2BlBxHsjDWh5VtMotn00GN%2BfhqwyT7fUGJ1NfuiYSvvYXntZtSqO8Jpd3PKXG2VzLhPy9kLsQT3H9yadSi6VvaPnuWXf3ld7vKteNldBV0i2Usvdw%2B4uoQrxu9ubbEdvEDrCqSZ5nFYBO8pMXvgTiwrpq3IC0msdf3BzbfL1XiZ0Js%2B0%2BcVi7CbEGFeYeUtltAop%2FLewvOBf6q92CicWHaKhmiln8kYdz3JgLUIldtHy9D%2FquThjw1FImHfi60taRAq9gGXxTBuWvgIB%2F1LcStc%2Fc8iFBn9%2B8ib0Nn%2Fav%2B5RvjbNJNgcvd%2B9KdEHNxBiGCKsYd7RBPWxZwEWH4hBxSg3mJFqKh7vadWpheLG%2BPIVSAl4W8PFEp%2Bz4D1Y56u7f7vxZVHTj9MbA1ymxIkbN%2BZ4jk3dLih40tRYEaQjVwErGYpevQFBDirgv6bsfLJoO1oLxhdHYvLa0JJ5UR3twRLUR4jLAWco45KrAfWB2piwExG9tQRixHtp%2BIFm9D8RoxoU%2BL%2BPPg2RddMklaSXBG2PIv%2Fz5E5D0JZWhYafCevcDgMfgA7LDVu0cCKRX47uxxqPOY1%2FlwxaWioJvXcnFvrtWRRZlKY8QVjcejPTJU6r%2BC47ukIgCFHG1iHmUWpMqvcJQsT0YibwTMzF2K8f77I2%2BJ0yxvxO%2FrBD%2B2GH0cE5FDCkZVB%2FpPtcS9XweEc447m1JADy4Imc9KlEt%2FGf5k7xgNqDmpNEP5%2BYk%2Bp%2BahTq%2FJP6i93Q1bGaSs1CYj7V%2BNnEtnv9zvaUmDF4U1pRoSPo0TNHHDCGe6RO340drMafLs3%2FJ0PeHDqvtNfNRy1EtYiT516GVfta%2BmrnOEP4vTuf%2Fk63Weq%2BKxM28Gyr9fexMAXGZr%2FgdMpqH1erub%2BPpGFdy%2FWMBmeekX%2BXx%2F6SublqC1loxnX7fgzJGWXzub6FBzDpcgY%2B5VDyV2pTZUP3rZUvxfG5v%2FU5ypAZvj277NN8YnnxlhwkJ%2BxtAHv7hb19FTIT%2FSkmsb9ZysrJfLm9Kl7iwGLqQ04zoxUHKXU76Y1PBjm%2FBRkH3lwi%2BBowXay4nMf3gDITjfgfFrwxhZf6RTFcG8WgI3gZOi83d1nYD7%2BjGuiY5QBRAELILclUyyspQDL9cEEsB89amlixes7nnPBDE%2F3OjIhR9GJw15Ps0V8HObvV7JNcFwtWkAbTPdTJtC6JKgLS4JYgvJBGl6p7yN0fs5fU7RvG157xfeGiF4WU2ILs672PvAZalTIvom3ho7taPfOd6iJuNshc5pG4gPa0csSyvA6eAwD9h8rYM6xKO5eJTiJmZbx6tM%2B0rLJG47LTiV9yDYgueuxzU8qpP5XGb3dw%2Fvpo6FB2RhLX0Qr7VddbUKy07rB3SyDfeM5wDkysg1YgXSI9vPJkPchAOpTnxQCWHHl8YILoZrue6BA3G4%2F2BciQczuDp%2BIR5%2FtbFFSeeQzknzMxYjbGvaMuqbOX4N7PVg7opnLI%2BvkKeDxaG40upidYqkMcDq2P9BIq6GlnE7QX8hDqbIDSdEINfX8eDQs%2Fx%2BGEMG7jYkmeMZN42KNTG%2BKVYnMsbMkOP4EzrwDqE28YDsSrLH1T4qjSiFgcVe5lOPa9R0oDq7qmsludIAytc3EqyaP0iyFJSK%2FThLaCElbfWczlYhXTbOQ1tweKiygSA3LcWpAMStFZaAnaqiw10CTN%2FTAsw6AlAJHRbyDjeoMPiKAqPfH9Xe19NPMKaGV4qWrJUgGXH4PykpaG9Xr1ebqY3MXDU83JLejAXfLDQvi6zp8CsfEF74%2Bt6xLMzIFpnbBIJEC6vmaApXVv6DiMzA%2BllAPnAv6nAX40S0zJ5sfTHD%2FsCKRYk1uWIZ%2FXR3K6VyJnguYtpyl9pJ5ST5SW%2FGEpbE1t60auHPn5YqMKHQJ1fl%2Ff23uKLlRI63RWOfBPCb0lVam%2BW84hvaw3qQku60F9Fsmc9WawNFnA%2B9m9NzU6ngEgmx6MdyTnxAczaxhy1FIxiG1d%2B%2F3H3RAj5d8eT2pXk9hsLqEGmBsXN4qA0ubDcqfkjQ6VT6QNvct2oJ7oyQLUcjYoAD7fPaVtgphuy33cXzXfvQ0EpdMrGK%2Bvgs45dDPSEIyB%2Fk2qJ51%2B7FY80v6oIW%2B9oIsWGPEmvmRWR%2F1jsQdMwn6CEclPPxiYj5fFj0NGb4rrfCNz7qbFTdthBffJb35EW9TjC8Oyk1nuLSWLFr3mjxI7B16QpZFrf3CnF7rmay9wJmIM0YDgy8fJ19z3vQZp5L%2Fd5PeyuwF6TnNByb6%2BpyHaRDfdQ%2BimRJvGP6uExZW7lDMxmo2g5Tne4n3UBUVhNtjMnzEbgxd6I%2BerVDnCGVPctvoakproleKGNYlBzIigBv%2B%2BRUOpYuUTgfBUb%2BC%2B%2BP77TJYdCxbyrhFLuE4OF8uuyJXKpOjEFdFYJHCtr33pXY0946ncT4m%2FCE8%2BjperGsH0GFd2Cje0inCpBCe2nNiZknAELn1v58DNziE%2BFkigCjrLbir8SIzfrRu%2Fn7t%2FyVMymyEQPR69gXu8HB7viuvm3yk%2FEKKWkHXlanAnkxiHwj8fdmEzEc9gdgViRffT7yBWTWUL67M3SMqsL8ldaRdynsFuHhGmonko3sYvOTa86Yun%2FbpI%2BfuYR0d%2BnNDXNVUlHSdZUVlGhoHIIgHOGecq%2BRPQJlmDtpWrV%2FsRkOqRFGxg3D3zIfPYdHsiptV4dKCoIbZAdGkU5uW7KoEj%2BwiF%2FYhtP40lQ2g1WIrxvQFyyopyBc9CTViSGIouT5GRVsB41%2FfdZAvo1mgA99EqlitdmFMEYtUznOK3WCj09MjvccDWle23dlO356Mdc9Ho6ER20CAVIDrge30XqYSY%2FySKq27rqhaswBJus6Z%2FiEfb0SkoXSk4T94NryH%2BsBR0zYAPK7Cv5WeYsmu7%2BZlkLHfljiIIK7CjXpXA07T9WnJUjOr5ERDDbG%2FkjCQgdc9DOLaPpbV7%2Fqmrxt964Q2sD3rhHDxfYSEhWdMTndl%2BZ0BPivwhv31UA%2Foblfj46jT6O1JZYJyP%2BwQCYIcNVA4OwvsQMExAZ9ltDQReqPjvPXZBkff2RofjSefKt3YYRmk04u3N4NEOtt2yfAcga6LTlLfzThl33aK3yUDOv5jkFjcRz1MB81WMGF8p7ORJEZUJu76TGkimk7e%2BdCm7%2BH2WKdRlln%2BgfSa5tDM5621p%2Bkb1wcuP%2BFNRtu9ivi19cfqmPatzrrYKT4Po4L7ghfZhpa9niDxlBHd7ub%2BEF5w8%2BdDwR1ySoe2VGASE%2BrCyZScOEQt%2FL43JQC5B7CiOO37FB56ec9CmardeQpN6emXiw3InpmfNiiEyWlU53GEilOszjFv7fTxClsQAxgGIaiDRJobAaWxSxrAWKMOTgxJ%2F%2B%2Bj1j8zO%2FP7P%2FafymOTy5jHrRzK9NQeR4%2FDfHYjf8v9m772WXcexbNGvqceuoDePpOhEURRFUnQvN%2BhF7%2B3XH0Brpds7q6vu7aruPnFLkZF7CaIFJsYcYwKYgCwEjy3khaOdljRX0acM%2Bq73Z8WG00n5aWvU6Tjks5dVVeadyyZaqZqvLmxNSP7fQfEyFjl68p02MC%2FOGXJDCJi76WZ2qc86qZLuLb3SPd0xgxyel4o2TWuue61A3XPqB%2B0LJ4CqgThhkd844bn0%2FXmn7ghRmwBbAEKyxa3YrutrMiVz20Mey5WvWuwcChcJI7miKnFg5B3x55egeK0r30TUgvltgBn5qecJerUHgtxnLbpGqfscOX0UAFPgpAIGRUef4GmUZmxWlyLcNMQacocAd3vAW8kLPax4s6bnZ5LHrrHZANQE98BK2LNID%2FSsJSOJbq42Y0WMezuq7Ly1Gr6eVOilKJU64usF9bHExgkrK5f0NsazqisFNS%2BRm0x2ziUjtUxPl9M%2BVN%2BqsUR%2BBFf7Ot%2FTM1jVvWx8A%2BBBodugTzU9dRUE8rlyaqfv7M6tl9EbYxuQPghWb91mhKvD0Bwy0ydQBFmLdBI7%2BM%2Bx3ZHOfyOt3xledH%2B8oltapJrT3jYhhKPnuZYhS4%2FSg1luWn0lQlMsM%2FkaZRX0aBM%2FijDPilSGxUDd1EY%2BowZtn3jE3YV3uOvg30vE9ItEe8feXHtjDZrqQCpvPyXk09PiW7EU6wn6xW5P5AhUIedxDZz7oLwVU%2FAOd0K901yC%2BZoXXnTVI2K8FMn9M%2BtaJJhrQNgFUccU09DM%2FFWr9%2BBpgwcfT85%2BmyO7qDHtF6%2B21KJqB9IUyl1rCd7rS77HWiTyBLs%2FdiepkHPaT2plMzUXn6AURlJV7Snk6sNtCUW6dXbsgEskipdMGzl%2FFg1BAmN1TQfJjuaod7jM4U4tN2e7xLuYRpxhwbgSjzIt1MBFL14FvMjerkNnmO%2BX%2BBcVXEc9nB4KvgmmAkOFgT43twswh1cCOARnDy%2FhQsB1YTWcNfUwz9TFLMsWJSR1MFwQn69XerLn6%2FVgI3Nq1MtNEHZr%2Fcob43McU696G51Fobn1jPm5UL4jncB4YIngeid5iinuXMCrYng1ky2rreMT4LOpcc%2BF6wIra6ZEfXvgVOKIE8Mf36ZNG7W%2BP4d3wRTmtBcNHifDPD75XIQZd7htXjclON%2BpNmQ50nqyqjOoNhfSY6v1e2OTdNt1HX0Cw5NDOg3xKjtjLOJe3JNGsBn8W16g3zV91oBVrTsxcspUdMURUdyDbKSzKJpxjzWfGt0vNiBiphPf4PugvH95P66vZ64pfWKv2qfrr6mFR6WtkCEKeVq4fMCRfRtU0uBMb9vTelHyE%2BoF5HErfPt06CVA8VZ%2F25drs0hEynFX7srKPB8Lfq1CTwJbQj%2FWMh7XJKIi6XJLR9Zb2Jq31n3JNnwzJNCbZPMZuxxvmBQeLaC7RiEjLeXjXXuQF8Dhowe9tq%2Ba%2Fkz5vUfJrUgVr54IA4smAY1L8QEnFju8snGbXhM6V3ImmSndjWoJSSXNWTm6ZSPgtPtz32sSoKr3FgH1dZjnwFkwp07DF6Av%2Bnh0ADNweh0JkNnZ50yTDm3wTmm3dnXAUD48roL%2BmXkDBQif1JRGpg3CCTx27Z6cLborRgNptxNtFWtvOs1Q2%2BqKt4WfnzC30StQazJ0G%2FWixTMau84lRF9ZNCLyRBTzbQnYmEzcdrFJ9ZVzkm5mxfAZrB5LlK%2FlVuWxhd4n2f4sZD8nU4xRZ%2BVHKaqJsahjzob5OnLermA3reiHh14lBlUwxyfR%2B0hicgJl9aUWThK%2B1st5AyX5PjUl0JXBeqeNdweO37qK3HUTaXQJzsrp9%2BeCEQgETQ2NNUuVH3xtysZeDa63b2lBLb0eYjFC8KXcGdZNKLFN4nSjciyOu6OwI4cQCMICjuDssmPFjRL2npcEpcIBApqE88DM5XJ7PLMz9WqZ4RrOhSMPdwX03AU%2B9G0cAOnIM5PbIUs4h7Vl57PGWSItQzZbvPo432zaQoZ2v2lAa0pkkiil0VkfvGBchgsqX5Fy7qxZ5rW7wJECpr5UX2OnZdmr7mCi%2BOtqSyswkLe7dq1uMm7%2FFM06dqLt4sMVSNylextnEwopnCEORBjC75cjk3IUlY57zLCP1n56PGSnviBf6WhzD1SvOPfSsA9OeAkNfKN0bYvY5EaaPa0VRhEwYsBuIsE%2BPtmPLjHeQrEzoQ0P%2BeQrAmL6BtgL54g0GWZsAXuIFJY2KUlswMAkMfQAm%2Bie0q3U2d75RIyAp0PAP7S1kJ4XB85vfl68WkdYOrVx4PrQhHISDR%2BsKD9fqUI7tTTbOqqOBf3OGQs4pKPcL4CRw7VrJ8LVSTYmfWDHr%2FE9zQix%2BwuzNnAuCp6dvzJLV0l3K9N4JE4aEcVm7zW86%2F1KvHnaFINMBrzilg%2Bfd5HoQIguYbEQwCmpiW4hviNjkPhiefGYS4KoPaBBc%2F%2BZwRCH5wDLGIThMyZ1Wf5oGZ%2Bx7QeTNeOC%2B9ObLpV7m0rx8snUyq9l%2BPSM1RfN3sdKOoVRWfQWQccciTMndzxcs8NwtwJqDtkf%2BXFZ%2B5s6AtYbhGsYsax%2Fv0Jse%2B4TY1KHLQ2kLv2CJA8493PgMYAky69IwlIlb6VrYYqweRKzS9tzM4uhmLXT%2BOCH26ycxEUQP1Bp%2BD1%2BlAYMq4%2BhbazNu3zHfJco9BEX03Wm98c3TvAoKb0yTnPc5C1O5OrWuF3KqHdFK%2F5zTqScFnQSlgW5ymcU4yl%2FomRPKNhqHXPIFK4m15wGqP8AgfUZUEAlsSznd5YQujvDSCdpwIPau03LHfp8jcymA%2FnnQt0eqmY604%2B4qO%2B3D2jLMk5%2FJlgsDNvP6IxS7EPVvzqZuhowyDUCOfDOP2MhSwqDV5JKjKTALIK2XXJ2PAuO4s2joAQ02sUgIV4qraJNm%2BJdbozjjYrWFff6Z4B5KKoVdbYWcFqKcIOMU7BUodiA8lg9OtkN005dLmWVdvFQLzIWQPbwgDU0w2J7%2B%2F2Q1gZGDiIp17zKLIHUnjIs%2BRC27fQ5RCu9%2B%2Bhz%2Bc0flo11X600GReZciwCpw4qyFnPyvzxsZxTip7Jr56lr4H4GQQT2AOJR1sBOgzwLJ0Ox%2FG7xZwhPzJK52bKV5au0sLVWL%2Bduht3xSp8pzh0eaMwlJNYotUwaDbqqdp6OlXtTxaXLjAq5y5GmZeOiYi75pJsgJ3GJnRGswbYxnMyaiZFIvY6D1dS8SW3zE28HLqM3TS28OGIYfYOpQtNvF%2FcGMK17RfQBAmd4hV7SJn3OsWzH5HQ4fLn9dzZbB%2Fevu95or3f9ugYR4jxMvZGP0%2Fbow9m6Qv%2Bucx4V64VKfWa0OxcYaAk6MW68mTX2jXI3UwU1ldqjuceKGW7d42Ti3Fqpzc%2FHviDUE%2F1dArZCBUGoOGO1XcW9QThzfhbLVUvQ7zlUnTcrRfX3%2FVlNABpuAdh%2BHjJq1hKLyMOH9%2FMJe4gY4H27J4QGzpvsrQhHQxH%2BIwz4%2Fr4eDfxCd6h4baJQxoRd9onugMiSjvBst0dyJk2lTcfr3uMsx6dZdWr6U8GePX8i8vmOeBjK9OvYgujDSVQexN%2B1fpYmzMarkXSXRiZ6AZlSZoJmGkxaGTY9pvKUUT15CZuT2f8sTqmeIHTrFCqo5URosVVnANk0CafSCZIntvLE%2FjJwbtbVMN2FIv1LLTR47z7nEKV8A3KWsKMl1VNEiJUlIFNpDk8WDMpDX6eE0orOAwb8Teq394J8%2By4bGWoc%2Fe5zVCOhinYlacrODFt6RvRs4VaRNzbuJ5OBgfXGyQ75xCVU3S4FbeZzq6dlD2WddUm3ufxwuQuT54Fcp08wO%2FSQWEkAjlIzYYQuXV0uU%2BSAFWbzAE6DkcvsNbnhUK%2BEdtnwsbx5HIDrgd6cZlV0NdG41J8KQxTeS%2B1LUhnUryvybA8E2hM0YveqCLfBi9XuJtn5lx84WBdgmuBPuEfcDsYaaKXGjI2OCeEn2GGcjgcxRJki5xYBjt%2BsC1FnXhGuXT4hhnE7NVoud8%2F5rFDUOlAW2X41%2FWPhlJl4%2FLxdzktk8gG7avHJ3ZNc8eDfgZn2Dl0iDlYbq4TR1k%2FXZ4GA2ey5hej1UmMTEWrjSPjSMqgFiEu9mjFsGylXxWFdvFCcmmuDkQH8gCcGERSUyivJ7cL4FJAz1ucmLlU68HeZqfh%2BkyyYPRgtH6McQ0u2oXZF%2FiLx7IMEu7PzCDxF657%2BOI896FkytzJr9x7uAqXjZcdJbDNm7Vh%2FsOPqLCQqanB3l056jgbrfSTne7CFXB2OKMFZum6c4CqpnXqfdwt6qc9pQVdW%2BSHdvNCiV9DuXhMzlMcP8k8z9IjiK4pULcni0t%2BgS%2Fw5O7hBLUfMvWHai36%2BQlav20PnjHEjhFJxeOez0hrna4qoKRuPuWxCW%2BQd0ccw3AieJy7pHEXv5IonEHc7wHHe0mzO57aGJwyPuFxRuKWQVJxyKAnvegjPKi91GaP%2BKLnOcG%2BYPGhBOXk7OGcrNuL95Xt01b57yKB%2BoOGD2i7a4bC8HGw0hsr73AyWdgB%2BlT9FgtUsWm%2BAImfCBhDoMxFViHBJ6IsA8IvawJgRZwIuHOxAxsFNa8Jn8UvT1MqskFMYhoAZl2SgPV0%2FcV3Rzyw7%2Bk8bBjDcmp%2Bh1kjG40%2FNmMe1nEMBmFyW9vCB5w5Abaun4TwMB8%2BReF1Eumna5IS1duHLoG6EnnpwqlFEU8CTdD7oqfB45tlRM7qTydftLVvOxGkow%2Bhdq7Lhdz44qXMIj9mTJA%2FuCN58PyTtz8pMKesf61OrZlzIt4dZnZ8k00iJbxPhrZvfg4QehW3LCe9GpDvTujhPM%2F8Uo1X4mUws6sMaQkAEdwtGux3MA630MjTEVkzwKzHsAXsv8OA7bzDkvM5IJpEMeer4Uo4u7VIBw0pzP3Ea7ZNYD%2FUXTatsIfYHluiKMHbcVQE9OZ5FwJlyup3anMCUGv1Y%2BQ4%2FxoWxfFYQR9yDaJ%2FMcHbf4FzRRObIkMe%2BFWxjAYgVP3lIc9r%2FgD21hUwInkyDu7k2hwRZXaw3h5BhEusiGU0k2xZNPkd58ycxRXNG30GRYHp39bwAtbgnsPG3bjiWKgHtIaUtT022hYnZRvHo%2BGRATc84HDgED5IZ02xxp8j5z1DFcHTn5x5l0%2FM38kU90TbuCreWTvg6dm8%2FXKUd2aSDH3HGZjp7XE8LhiOdMBfIcWb458vQItYJiHxcKwVrwgK4KOk7JbVWGYxLN5kCukdGWAY0bGgXqD5KKDvXIiDJnjebPjsl8KfeHrtch7GDV6xwiNndekw1ICbMvGQR1uYZ%2BipT740YNSPxCVTHk55jD7ek3%2FaMEO%2BxL%2BD0N6eEU3deS%2B5Zo61OUbXvK%2FlKIF3uF6lY5NEGB3W%2BE4ARKV6Ar%2BrO5J8WR6vbfFCPCuu4khNe4WSbKrCCQCrtLIZcx9RmIHxPSzk43atQ9aUFML3Gg4Q16f%2FuTt3ri7dFvi5k%2BiYUCprv2I7HJlIT7NoXjxr8764a7tZicazXr9dx8dALSjGaPPKk2JNGdkNXC%2BzbiK8HtQIOJ6O52637k26h2qigi6qHtS%2BvcuyTmqg1HcGTlXi93DyUe0znsbp3sj6wI93Cn07dL24jf2VWqTCm9MRoOLgTZfhfGAGaEndbfAEan4YwUlagA8m0FQexIfhVn%2FjQx80%2B5Tw4alNNBO8npeBwRqxtt65EmPs0DFwne5A%2BChHcKz7lIEyRp4C3BJL8qR0PqCY8zEDc%2B6fWWptg%2BmYARMM0kZPOjTr5b1EpHioHNO2XUCL7sk4uS9TRI%2FMZWeSFGKJ0y0PsvHwejkG7FnDB4YRDet7R4G1Jp%2Bno78Cv0imZIol6hOrLEhMdSBHoSoGKnbEhqWQPdh7touNdCEh5ncQvc60HCMYyRcg32D91cRLGCEKpFEqfGO2tCgYhvPcOI7vOB71OZ3bgihRE1kHnS0yUNs%2BYSR7HdDu5TCxHOCL72MtdTLz9KpiEa7zAe5vg3l5n0%2FgxaE%2BJVNoJ8P61MsXBUdPkhqSDLpzJjmgUhjqcUo0RRs2xBi0m%2BBgZyVyfC9yzwmok3N6do19WeMhPfT2wbEa507c%2FSAwthFPe3HvyNmyJHhY5oA5NQSBtDMEzsKqi7vpJbfEOT8p5R%2BlRFzbcDZEd%2FloGSf0%2BnhcTvcTPYRLChu5xX0uvBDAZ%2FJdnr5vRUq9Ms0NY0IpTG39ZGRq3cSq88z%2BsGXQfjfTbYUaeCnO8Y4aJjLg5Hjr3pxm3kd2hjFMg6F7ZzFX%2FGu0sV%2BnGQrFwE7XYGZfTAmIlfAM%2BqxRrgAEg%2FrhODtdeyKvZxrkrDkffRif5ujYwFgcrMIB%2Fg9mM%2BDbm0vt3LuN3%2BvaXzr3E00mF6on%2BKcwdzlQD9Iw%2FRZHhv79kJf3vcyGwKEzUoNos0NTIZMjmuE%2BaBJc%2Biu5v8QR%2B2x%2BXAWHGnEpmOpnbHMcN3fb15Vp%2BaZGwzmc%2FOUG8zNASom5DvQ7XLOO80IOgzMet%2BMBFyZOoIaZTw0vkJonGNN1BeoEFYCS18Wanlc4W6LolsqdBjPcgprnjLuDed1KPrKhblHYRoz5zIXcodGFRImBH3eg5jUigZFEx9h%2BH0kcWLJN0YcuMCEz0DPZwgjGHYeuMHbZNWDUu8o9Soh10DtGKVAEyCrVhsnaKv3aN4AsL59wzNXZTnNE4JSodJXi18g9LlcYH2fUkJwDjF2l6EST1pCJsXDc5AYDs4MAZ0L3aMQtPe2RrzEm%2FRxAltUGzyun8Nc3zwnpwGazdouLV%2Fse%2BzKrYHzfzujIuaDyQwPk8OZ%2BpqDa57IvAjZnWr4B76hTMKMP95lLcFwOX%2F4sHgipJ6bmKWlTHDW43WcG2fXV4yp1FfRPH3QQNlOJ3stFDngTwHCFzqJ%2BQ45MlRo95VmDYldkspQFBgt%2F43KhRoq%2FcTkZiwe%2BDA0o1e2FrtCZDk%2B6iO%2BcUHwiDN2FrRNkR3wGbfRauW9oDyQr9NfGyD5LC1Dpe4wycI7gI5sAhpiyszsrd4V6Cua9eQpI5IIKPr8iiWf6lE1NqckGKlfTcYo3f197CZlRbJ9I2vHbtlM5hRvE15XzL8UnjnjLQ8Y%2BWUvmjKRF8OVA47dZluaX0uLggsaq8NJzfcX3EMLDIW5PThCvEFe3WlPpoBQd3LbtxLo7MMCV6Mc1PdtnhWIZjNJAdV%2B9Plw0%2FFKOMtxxB5w9azB8hjHOajhUzOxNw5kBeJ%2FpElGfWusfSr0tb6ru2hKtAyYExN2H0a5NKpGlx%2BvMkL1VDVu2wkvzfls%2FU4zqL5mmVN4noGgEC9tKb4ZRBrIvYnmGysjjN7gl1a7zQMHe%2BQC0BIr4JxZiZaoPEKH1wzt794meLQrPvYXJKg5tznzi5JtQdhtoiX2gv6NEmZtwIWMwOAQZmKxMCrh9LzFlgb0%2B2mdfR4h48AsMtGN0RH3Z4mEDRyazxN6u3P1CwHk03BTMEUTp6vCoo%2BryuWUaqune81hefMoyENHfmdxgsTeMhnoSYMa5IMNRRcDNr3Cg8yt%2B%2BCIZl3H3oKl1wy%2FOyCzfkrgVjBa4IpYvRpzCMal6SGwO6Kr6mb8BK9whl%2FFGkmgvfnLLtn4EFA7qSM0Gp8uJwBVvWT9105XWuNGeKRq1S8VtN%2FtTG%2BpAT0fd3ilmUbPh7jsKOWcSu2ClAkWv%2F9qBk5NQ1qsKNP01osiJxwuixZ13zvCcv2LLR0wrR%2F1Qow1ZCxYO30wq4nfAMp9MekZYOIppf5NIlkwqHVwhvFxE7q64bNTEpBTGL%2B2TjhTGy47Wb0%2BIEss7o2ZXg%2FSuVVYEotAdLv3Y9zxbScXZ%2FTJG%2BRFoWuNiBidsk8%2FcJm6wFjoBngyJX2c2y3wix4x3dPICRw2TLYdxS5%2BYDDPPQvLae0xUzDeJ4Dn98YkNNbc6nXE4a0BKUJ%2BAffyTBOVYewoKoSb6HsUmdRoz7X17sWtJSYkmh5xtEdQiuPg8GosMPP95vQIlwec9bOrmtkEh3A1Lu8oUNpDm20xc9Y7Uw%2BeFQyC9vsceODlQIXNotAZABp6st9t1tltMw4lTit1EfG0UsJEjE%2FgFQ1Xy4ZCmJ22TTYoi0I4BxIxOUoF8odjMEInS1uGQr6KnHpWaAJ2NW%2BX770gas33bjXcpDdRcrli1Dwqu3fcrN3DXxOV4rg%2FsxOiWnjKIAIKj3pNKmfTyTbzCdk5CrI33cCmfKQZZPhfAFVbPi3OnoQfKvBzf0Ux5eP4kALb%2BlRaPH8zifbk70%2BUapu5j0j8B89sSJFnqBJ%2FMm9DCJb5fXbJ9AGYiXSiDfEI6qoefCX1Z4kGxBGwrs4c9A%2BSkQ17KuXv0BFOidvtgoO%2BHw2XcjYezn3hOBb4sf8z9cKVDxcJfu9gr9xvMj1BmEfqQ62ynivyAa4I4yXI%2FmC8EoAZp1lGAJdsRpErua3iPnUHBZb5S72ZZWbBK%2BvhEHXn55vJitAqh29AcCT0IEVTwasnX1aKbNsiOozx1wWIbc0N2VEAKGMAxhu09UiHjcewsa87%2BK1YMzxxgxa1wfo8VyDsFIONTq8vGfHi814p44Q0F4xY3rLxob79d%2FStQJxpE36eMQ1CHfSdVd7kMLNkw7BWppV7jDYLoTOG9ZaXMkuruSONDnk8KnyOlBcKJRz%2Fs6FbMUzd3d9NJTP%2BhdbMw64BXs4oiXq4ZJb8lUtFIIrGtzsvEG1Annz0vOj36chnt8BwpqvXHyPYfqrhNZJ5h3oubIy472carEejP54m1JpcZKIZxQRdY3pBVcGH3YRVZUd81QFihT8OZ1XmjUvnmoIt7iOV5wRF3suiaDVNQG8iL2SkXtgjDc3L%2F8RoI%2F64BnA1epytkSLXruMRmh5jsiuLlcXvk53ITLn0KbFfvuE%2BkuoLTWIzdS1wDMVI4DP%2BGs92lFnSpHj6InRkkCZ0YvsG4owz0bnTcuJRkl2CRG29BH5CHgZ%2FEALgvpHgSE1DS5rFQZR0d2JfUJAE33lGF9oXhi9TCbnXcdffDNS8DjEggItynied8BgOQezZSoFzO6KPE35Ye9R9gZMivuOKGpvtjGHPlhUq%2BAdnQnknL5B2O6HP53TdhWM4dFOgLNROocQeKGYlw6xRKI9hBr4QhXwxQI4jP0y96GLYFkH0K4lotPn2uqTbAKITnpXFwoNAhc9vnae3NR5lxulzcGTZmDFGW7uuGDg4NmpPvDYho4RX7Ypm%2FxZ%2F5Gp3qx83ywT1FZvZFz6SVPYNKvP8FFUzAn8JPPFEuam8EjOXaX4nspsJ24CUPBm8hiEJMwuXjzug2SQCq2xaUFm24yhlPngs%2FYyNwGGtI97TIlVVFsk0bfPuCrOyevyFlB1gFt1uSJPh06MvkfGcEj48CT8I%2FL4gHVVj08SW3aDg%2Bm1lSbxVv2cQ4vypvUXqAShQaV%2BtwAvLpMf7D2xWTi9Z%2BNhqvwR9jik7p7cnlWmRBRTRLdICaTwEfMwgHRPKQH5CaILFrrzh9P%2BwBS3nn0Y4PtCH6wNmnVH1e8hNmReAEyzelUYfDcFdJJmBf2tLi0KxXWSBYa%2FplK7EEfDrm6wlDW9goNUy9ruDEywjj%2BZyxP0Z2IWsC1GuIwnpcsnNVqS%2BbxD%2Fpp1IzxloWvrIe03pJErr5kkYlkzojvAoy6AzAzC%2FlANU3bD1oFhQcKVk2w%2F7k7kboBPIlhiaWUTbKHBCMJDN8AycIV6L3C1BDEYzUcILSSGuExev4jAB0AKcJeZ4UWw9W%2FET1PRE%2F2xWJRSYrSAeVEbdADROwXeIT8%2BI4ePf3ryNVXyyzy1hwkEbDyC5GMrRWRYTdbltRpx585a58mTvtcpTFb0IuC583eby8QNp9AccplQVexkmz9WsrUUhYRBLwzZHKMml47K6Avd0VnvOFIRwHOVpWDNQEgBO%2B%2BONcNeKT5OQvn6HRzBI%2BQyewjs%2BNwCaFim%2FONOTOO1yjCMfgLMUPHYt5TqRayFB8jq9n9HlPdeYLLjhmRzR8Gtae3Vl3VZy2RcipFRbqO4qIiNwDZrH0ue0qAEwOMAa2cxq9M6IsHKGF0jl8d5LvThumuRhiRAIxZ6TVKKAtEMuHPY8TP4yzA2fGM3LDMRtp2bQVWyNhgG3kcfls3z6u9dTjlrNXAP9JvnG%2B8pmdyZk1j70oDAbQM7Ne%2BtaCMxhgpfSa0b76%2FjOS8bo5tPjgGW6Euk4QYURI4BrDoloY%2F7GjdSz27MOXcGEnK%2BmWbKyh%2BedjODGagzpB5GpWGxISQsNnU9Vkh0jJJG0NvOK%2FP%2F%2F%2B%2FPvz78%2B%2FP%2F%2F%2B%2FOlH4CaDNHLgRrjZlu3A1Ev9%2BfnFejkP80Ze%2FOv1L7jwFxy4XkBmkDUd53SHJRhcyAiL%2BnBM2%2Fl3Rbj4F%2FzS7HLaNek8HuCQ7xMY5K%2FI7z%2FfFzi%2BfsVR7K%2FkV8lWJPP7%2B4oM%2Flfi%2B8A34LHvX%2B5Efx8bTl8F%2Ba%2B3g6Tr6yFgBGO%2FpHX9yzN9%2FsaQIvk6p%2FsP%2B%2FDTuBEUwWObptlab%2FgP%2FPtNw3pJvw77C0bV4LZ81oH3xJBpPuqvX6hh6X754T%2Bm4gSlQCsjKNHvv%2F0I%2Fsrhv3Ixa2H0y7Wi8Zdyc4mOX0rBA3%2Fd5Ounryf57X4YqHtY%2Fp4b8EoCCv6c5rGr0ktXdyMoabs2hc9T1PUPRWFd5C34GoO2SkE5D1uyiMOa%2B%2F6hKZIE3obf3sWcWn0Yw3tuY9iDsrFb2iSFtYb8c0wBJdC%2F0n9ofoKi%2F6T5KeTnpseRf1HLY9RPFZ4meWp9f%2B3G%2Bd3lXRvW4m%2BlP1TNb8doXdd%2Ft1GZzvNhfcxDCJe5%2B2MLghobDw%2BeD97%2F%2B6v%2FfbnPF2H%2Fw7fj%2Bxs0lO%2BLosSvrQIf%2BD9vE%2FB%2B3TLG6X9SEczXcXM45un8nxyHIn%2FeyGNah3Ox%2FvFB%2Fqy5Pqdy4wi3VPn1gL4r2nn63ZUNWPCb7ZAk8gfLQVHyh6b%2FuuJvhvDro%2F1%2Ftw3mT1DhB1uZ3mEP%2F2z2HPSa91%2FzuMf%2BmnRLVKdmGs8%2Fdda%2FYHjy%2BcBf3mHSbX%2Fo0%2B53L0D%2FZlv%2Fl3ogxf6xFmnkZ%2Bxl%2F6T3sf%2Bq3sf%2B13GX%2FjPcvdQdnE2N%2FIy81tyNIegu%2FyD4TlU6x%2B%2Fv3peE0%2FvXXh93bQtaOIw%2BhyJ%2F7N%2B%2FB2PQ5CTKEBL%2Bt5D7T23one5XcAvwcz9%2B%2FSHE8KX%2Bn%2Bn7BcAP4Th%2F368Oo7Q2uqmYi8%2Bh41fb%2FQr42g%2B%2F%2Fwr8v7iIOs1%2Bf%2FxPDmICvqFoc%2B1zmEB%2Bt8PvXpH9fH4wW%2BwfNdu%2F3f1%2BtuVv20X%2BzHPQf0Xxn82XZH859v8dSP2MQn%2FoPf%2Fxo1PqsmyCS7d%2F6BT%2FBCRCkb8PRX90Sn%2Fm0X9voP98cCHQP4ILoH4%2FNdCvZb9vnl8d%2Fj8dXlD0p2q7LVE6tumcTv9Lida%2FoGVw5o8tQ6J%2FgvvkfyfrQrGfGuY6zXBh7v82oyb%2BWHUEg%2F5s1Nh%2Fa9X9rFVu7Reo%2Fa%2BvPOJ%2FvPKI%2Fwtw9Ida%2B5V9%2Fb1aQ4l%2FlTz%2BxfH9D4mkX798JNJvium%2FXyR9q46%2Fr5L%2BBnf5h%2FnHf83KyZ%2Bs%2FN4lKTzPHsOiLeC02P9lVs%2Fgf7R6nPizyNB%2Fs92j9N%2BHiz8ohN%2FV0R%2FEwqc%2Bpz%2FW5w8aAWNoUsT%2BTDBmnw8k%2FF8iGRSSPPgPRtbAm1%2BQv1I4%2FOcvJDj68vkB%2FRRjP5Syf1r6ucSPR7J%2F48L05%2By%2FsuyfXAT9oQxjfjiWFP5E7VS%2FErK%2FFj8Inr5L%2FkUQi%2F99qvpnfIhg%2FlWG9g%2BEGtI24cbxEzCAiXunIv4YCxCBPxf%2Fzg7%2Fc3aZ7sX8FYdiWez7%2BzfKkuj3999gFn45fvfFSMcCVACkvL%2FhsPf7L%2F8YYv8TERr7BwH6b4Wx%2Fo4F%2FFL2XxSSDPIDIyd%2FYDxf7%2Fl91m%2FG9fOFfoiLkcgPF%2FqqiJ8u9E8Tpj9HcHITLir8%2F422%2BqElCexnKPnvDWj%2FLK3%2BOWMZoGab%2BJ3GFTjiV8WBWOm4FvE%2FHE%2F732cH9L%2FGpRDIn7AX9L%2FVEH4mL8YS1cX0BoXNNx8M2wT8P36HYxjDagXICVqjBXUz%2Fl%2FQdv%2BkPsz%2BqLiQn%2Fvwn8ZHmH9Z2%2F3MB7QuTP7dcD90uh%2FAFyX%2Bxxvuz0Y0flQM3wy4aL7i%2BOHUf4aKhKzYYW3x3z8ISTiHAJG%2FvmJSD2XbpXD4h7khNznv4Ji9br3e4isHf9UV%2BJ%2BMXjgfDvLf6kUsOY5v3KoWn45JYMuR8HjuoAlcWaUeclXN%2FOWiIdJFw3G8Xdf16YhTyPuiKPZObVHWW%2BxvjcaX%2BUsSTe45XS7X3HgOysuqJzgjFyWrc5EOAW3VioSLBCUGXxISptBJvibufU38Y4lgY%2B%2F4Mt%2FxdZ3tp%2F7bZAR%2Bu4hSdd824fP1eiGFS6T47%2Fzr5%2FtW3unGaK%2FD%2B%2FuEraTk6son79t3wXmRfUPmKjdgy8cvZbJMVO5ll08u%2FiqBKz5E5AosVK3oOHx%2BT4Ugzi6vLmZA89X98ilSeZg%2FjiFR4aBkVEVz5BDvd%2FHrN27vi4yEE02zrG64uskNgf%2B6%2FjObB1MlPrklRT%2B1DkW9ddSb%2B7wIf1XoDVnUlPHXMxOo8OlrInp3tq%2FHeNSlp16rW7mSztGrznu6S5%2BfeA57P%2BHqHWSxTAFzrE6T2Cl9cp%2Fnv1Snn4Wt%2Bwpf1XIok%2FVrub3eEXEYq%2FZqvrmvK12b%2BKzsz34roxTvyXdN3bJHJ20VXFQI3lf4qvY8zW6mRKP3aaQlmKDg8%2FJ3xtL2%2B8S%2B47uyfU0kaW17u8ZSIWDi9eve%2FKL6ZUw2D4v9ygb2KRVy501v91i66ZwpXL7LrnZCwl3Fb8fnyO9rbtvNFosDU66%2B%2FbmxKNhhL%2BL26zjkRv26z4PHuKnJbPHYsDvhO5%2FnvhBtKjYqWk4vbhs%2F5%2Br73V5vd%2F7VuiG3zV9l5z3iJkszAkm4IJ8iXOwcpgskK%2FEjWSI%2BZYsqcnGnSxczoi%2B%2B738KZbKqri9VGif3uX213kzKjiFLezW%2BhO2XslNJiNzUZLH6sr8rPxKKj6MYep0G8j186lTI1TqKKhnY0dsThC%2BbxJH7KvMwGF6IyqcxdEN9vin0ETdD0fxSppNb6osFhvH%2BE97iwlVHrXqrI1TzK3puzFebwQmoqLJfJ7Yk7saXdcRW8dlg0ymnlIMrf74O3a%2By0nIl0rq2tH31BaEwLx55n%2FQCj5%2FVp4gQLMKcX7VxJAGcwC4eX%2F2U77OH1vLXNSnv03eXy0y3u4oFiynW9mVD%2Fc7DdUdFrVbti6%2B3%2B6fUbuS7ytVuV8mE%2Binhn%2B1TqwzyWru2TGi%2FlGVxZdGC%2Fxrzr1tIl2B%2FXxtX3hOP%2F6AhN3Dj%2BsHKBCmskTsI4%2BtIu76%2BKltT%2FE3Lv%2BrGuZguQ5q3SvDUNLe8r1fmhqkuC%2F46s0Iff02mvfUmMN4%2BDa6oRMZfD3gZzCLbJSa3NM8PNP6rZp1FTHlwblK%2Bpq9aldyrOjFD1ZgzXBv1%2Btwkv4x3x7J5H84YL%2BIc1g3P8eFg6bt5mwq6rL7Lkly7Krtzm%2Fibvt1eH4wEAN%2B6fj09N6JuD62t4CpHs2GqpGniFYFuC%2B7HS2Omd0tIvfns0n2nn4bkWY8dru9QyanA8WUDVzFi5ZIX1%2B4O2DzAq3ckwZ6kXozXHbUFrINJg3ylwVK9NC11w6vy6hUuXdFVLW%2FJ45NkOWQ9VCmprjv6TPed1HKeDX7%2F6qVwGrNB2i%2F8diPrKlyokYzIdgReULqiU5c1IVzEXNpY6LMRfpA%2BAtOmk5uvPUa3GJ1RUddizj8ejXkeQvA0Hoxpa3PkI1I8q3iNRRBxB%2BMgFfdcp8wyX5j4Wazih%2Frj6CS8RN8Y%2FvEMMreicduTgqSPPh7jKk%2BqdRv2rQRuH7QRmq8jSeZThFF7UqFem5VO%2Fuld05tlQ8aPTiHoP6kOTGtoykeEdYd7mmkWn9QoSnzualH%2F2WfEorVFrrXbWIphASAb3n66Es97p8RUIFRUGxrV%2FtkV%2BhI%2B3NBsiPqzutUyYHIAuGaW98FNp6uafLIL2JVb6FdlMTXbNlxmI05Pt3T%2BngoQci5CaaqyEQLFdvG6kSZxqrrt0zYXbKhH7fFGbHUiMhl%2Fa9XqIRXrVPKoJ6S0hP2RlKplfp7weR%2B3aeE3ZjS1algjwGJIQDRJl7iPXmnswcO7PJ7oakWCL8NndOq2ZbT%2BOh6vurqdV1opqo%2FlItjwdNEj9HRkWRllJKaXnzWGG1Bv50qnROopvgJQuRsr3XnN4bzXqrNVjJ9%2FUFvWjYYSzYvp68jgkKNRM%2BROy5q77pMvX%2BDiC3fOyBL0P%2FAUaZGQxWCoaLaTJ5o%2BMsWHXiIWxhhf%2FKkaKsQ62e3hPzjn7u%2BtsHfoyJDGTUKmvHJhvQ%2FaXBH%2BCyXhW%2FnvqXjFT4iqMJUCx5o7U%2BTx8MTFdHe4sQRYPGVH0pY1v5PrhZPUx6S5E%2F3A0Dg7Qjrsafbjkq6CxejUCNcxFeFqgF7Q1JMSmJVxXcyawSkdC4yRTIp88hZ8HoXBNTQ64GRcCLEPVvDaFr9jPDKC1kPgXuk8dw984hwDLGLxuHM6xtVXIgUE1EowuITEoMA1jI2zGNnX4Dvclidy39sNrS%2BHVjZPRTQeqpqaFFOxpEQe8KRKWQI8tgailkijPJ5SZXyyVCj3llOKe3B%2FeCtMcfK4nVMlD5fdISgjfs5FujdT0aUe7O988%2B7lYIULwm4HyUHk4e%2F9JRDZ4zE26ip2t%2Bo8H3agA%2BL4Do6RkR4vnHQ3g66r%2FNxeHRJ%2FMsXMhxnqzDsIpOAuQ%2FvWVQGudlCV07lTmzsOoRBmSredeK3Oui2%2FrQ4uMpFftScfFN9J%2BVHZL1CZBzrCjApURZ1de5MWl6Tqg1rxfPs8sKV6slpkEy%2BN2oZjNbSmJlHE%2By4Y1QifRdGLOtDoI86uoehahdzYIRXTm3vTpA5cCqWIE8Op%2BAmchcibdFVZ%2BpWySOskYL0WL7R0xxwlmAdcUsVcq%2FpIosU77JRMx1EsXkaAlK04Z3dVfg3HrXavyfp4czjZyA0XQjuu%2BM3A3z5y9gpqGyctYJPrYHBtvw28M94%2FkeU1PlqmEu%2F1cfQPmdbYubndpUJmbR2mKIzsNtq8qD9kkYB2yS8ELVX5BNzIcFczfInrWJ6NSywBeeEeXiPSl7c6zZczpMW7ir6nhQ0lWmk1syQ5tnCiKvFUv7Guxr1yderx2IMn9G35%2BLKuGYkYbKqsoPsNjfwqA1LwpE%2F6uWoaapj3SqPuL%2FlacLll4hHOWKNKzcu5XLBselXDQQvjwWdXdqXhkhkkIKfD0ejPgz%2Br0fQngj6JhkTQE5fw16b7nND4xqPJycvQk0xDlw%2B2T3ANkpu7PXiF49MCKljsesjW3PiXHZfXFaa26XWT7ZjWe40TsoEWvPG9Rj3LUDuK%2FjtTjuXh%2BsTuXbhcblcnJnStofAImE1L0VV3tWKnlerornpFEnRI4swP55RW%2FjAM5jyaBlE7Y5LRZImSp45sgLhoRTSk1V2GsGNfXYCfDtE83ATb5F5UWl3Newe5pLsKnLL8flzGEp3pFqiZE8G6qFoLsaEvi0rkHn1brfLrKc8IMV6QfWqF55zXpy6F5stqmfZ5C8mzoMOXfCtwuGTUTEjg%2BW8OekVc%2BlQG7EZt9pcCYMbXMoZsEs0VMr4PuEeLBGvBwlIDpjABV9lOyFvtJajvssB0M0rmxXCSQxVGaA86HbbErXTUjr20aPnKFy6T3n7FwtQUKaT5cUVROFm6qDZ1VGhoYbxprFaPcDeviWDtUKSvvQeYAge4oXExAc6IpYJWnvrJ%2Bh1795w3W11O5Lz1xnudSNVmfKlMQXIykdqg95eyEBh9tbqRmRqairptVTcwK0x24lZJeG9%2BVQmgaApQYT5Hautz8yUjRW%2BNSd833VRDY1X22%2FjY0Zwoc%2B9mbr3WI6lfDdacDWOy%2Bo5FOwTcfJe32%2FCELl86Xg4Tpm%2F86IJQaG5wCyO%2BpY0i8msveh02rDnFhOspgaSBaxkhpLm2fuZZrqiOBrNshYrav2GOS08v43pzIUFwE3j1ubBHjlzhkjz0UfKUL%2BGEElm061BGvXZtg3EXe96ZcosbKatW9TE%2BsQZoQo650gb9HJ5D0koPSVbej%2F6xOOhTNw1ladyTG%2FzafZxpZKPzJz9Eg2m7iLHAkTFibS8j1PqgNm2LNIArdoD4iNWU2g2RkP2gA1RVWN2HsvK3RdioAYe5HxFGZGLqIbyftDGgb6pW0aGBtM%2FFaIek0Seh0YLafgQKe7yLE0Yl%2BZjVzzpicBNZDATK7NflUwrNByMW%2Fwp48abM0FZPgx%2Ba16tAnkcd7a1t0e%2Ft6gjnCyjC29qKzUF7O435hORf2O6zqdwhY6sVv0mpPPylz1RiPDK4rUEdiy%2BoSfHRSL2tdq3sCtRz%2FVJcpQBkwpAOuP3Ew1Z89mJpwhPXAyCD70xhQNdyfbpxxNC0%2FSSWNNLpl4EIOz2cGoXZQZnm4MLQERCHyahOcZACUr%2BGlRyYUnOQytE1sdkwfdv9KIphogCczUT8UWUeVEoojbuGOelssOCJlb5eSf7rFf2gTS975F6z6mrVYYDa5nm%2BV%2B9w%2ByCfYlcLIq%2FGh6q04qXUPit6R2oJA%2BZBuoFtZbA1ntuIeDHwjzCKgbPvOSE1VH3fOl3BbdDAj0vevbAzwafTVgF291gf0CpKWRTzcIi3AZ%2FRJWldZPVPvtzrFfJ5u4KLP19trWQbCx3jpxUZ4cHLUy4OObSxZK3kCilN4Ch16KlO4F%2BZlRUPw0xSVRyIlMShj6Nqhtmiz25ezxK4mTceyPW9%2FMRuSpkWC5iuDooe0ajAlRXXaQSm8O%2BKAg1KFLTq2BddxhXDNKtacp%2Bk0yeqswTuDrtSlIepZxE%2BVn9AtNbpcL3FFVfbV4ANMCcHESzkbXJInuvT6nEfH6pPk5tdGZp4HNhn2bA1j4j%2B2ZY00LPVwA30XAgEf2GtaRmPV67xRXlJShe2nPIgQinZSC1%2BFiR%2F7d3OvmPLE43zOaqJOKgWC%2Bb%2Fk7ZpBL3SvmICjKZRQQXtgSeAlmrrlGBaQ%2BPpEulzl3sKnHIjB32hVWIl6YqU7mq60mpHnoAqf%2FYSrZMAhRGPUDWPNUhydga9%2BbOt801usnsSkYIT4fMjJuUVcNf0jYYP%2BgjuxRPiI8EnReBbHnqdRrh%2BR1qDbOouKnFOTBeZM4yQbMMznGeE9pCeUcw0YE%2Fck2Omc4hnZjvUFnrnuo%2FQFoRGfNxW9brOIbbFZn%2BrC1tsymZ9KDelOB8iPxdBMFGZB3kkNIE0mVgnG7Ir9Odwjbu4H1F4Tg3vAzLMxdcVCwJNnOOH%2BtlbGxAnoWhaQ%2FAb4DWt3UfmBUsbSyHZ9JkJoweporSTVLT4IkQU4QB3LOOBfLSeulezHeyaVnXUFQaD3%2ByQBhc0Q%2Bcb9rVW%2FNXzId37tgrcrzVKYQDF%2BlbSx2EbQqwZA3fZH5PNIIzDA3X9xMbHPKS%2BJo0rVq8YjR7joXnJQIYXMxj8TeTye%2FWqm6boiR06s0cIIxA0wb%2FQiYRqE9%2BhyIeqazQXGmvDjJW0Ti4TR2Vqikvub9DHfAtpA3WdJ5IfPFWqbNs88FFSeOKTri1rSW3K6YmSgZhI1LhzFwbHiutYXYBiH%2B1rxilBnHcCp2%2B3h1O1Bi8cPhO3Dbg%2BTIDQVAuFHJHH1s1lDwjmPj0%2BqbNuoz8HxJQRN1R07m%2BGz2LJw97k0GwPVBAYJ8qBDr49Vqm9ryHyEDyVas7CVV9z%2B9LjB8zplqOHDel2576BFUWmaU256QM0uqKKBK4ppEbYE8MqfXLUHtPxzB7xyalc4PaQs4POYytqno%2FeBah7GOB2XvR6BH7xCkRerw8befbJDTcqjvGNvauy2F%2BqpNqk5zUIRTm1PfUEDM4Dd7ktkegrA5eX3pH0l8U9qQaPQW8XFX7f9%2BlhRRfiXd%2BYwjXsCZv2m3md9IqdFhlX83SXIlhbT22vaeECF%2BJfo%2FsLOS7PwtWqpMBJr9bHw%2Fhkh%2BLzptM5gweklXIzxdkzrS6f9tWSA41DZNw%2Fud4brCCFWS7ay2Y2sWviHlZZkVZYmHVqdkvA5fMq1Jg3Wu0Rf54Lt3laXOTG73pIDkRnWpS8lHqMPFNOqj%2FJNCaYkChTTz17cQcyu9ld049OeDbGHhFPexzuVXsGVOZKWMhfl9krPXle7DzZmlVDcKrrg3COlK6uzFm7cC0uvfFRlZ699yoj1mcEmy7v8s7dx5F61IqRaS1cCU9o07gvlKuRlSO2x7wXPZasYmzlZo068SOYcifQnFmCIRxsv4mzqSbACcoNGugfDqpI3LvqKcydUroDkKLK7mBxfNvR6SwaXgJzJzrThY7oeX3Zu19Xs%2FCkpUPAbqpTZs25yR6Ox%2B7QiTAYjlud%2F54vsaaI9WlDxDGFQLuAnulH5VE9B47zEv3s9%2Bdn%2F46z5hkWFE5cJhgq2tUEAfunmGxZVOUZVYSLE9d5eMUftlEUpo8y7aG2NF4uyM0iEyC5DKqOnvMzKFeSuWsvpoqpIvBoLrhnCFcvstD3Jx0z2noNmPYmi31%2BxTPyUQM2m3AE5vM0Ez9zJNz3rJoE07LIVydWIRGq%2BFlNmlhOn%2BifV4utC5P4pK%2FZQtXmcXwy6lW8HCZY3HudB9Md8bk8bsaAqe46YmHoNcAspamLdn2Tt2KdWcBtyUCWbs8sG5anbQ7oEvexWGBCqavucklirYIy4Sx31DLvk1S0%2BMFMDapuzR3zmFQPeLRVpbQrn%2B6rP3USCS2FMDmyDg%2Ftzc3%2Bdof5s%2FHhka0Jc6AxeD9IJLa7p4qP1yKYBUda7LUK6UtQx4RmEoCKE8m1wSyWlXcdWInE8zMN6YxfceRh%2B5qy%2Bo9OGKY7C0vhYB8fi4RZXyfE7IeH4mvAz6jfAk%2BTIDVqou2O1fuyPdriXlsnjzXvIzZIIvT0Xjx2DB9Oai5et9aCbiBZMtVk8mdwOF4RRRzKpTFnxBDL7kMBqcezevjJZObBe7jKHvAMqDftV4uMpWlfbt2N5w6W3Tw1zreof%2BvCFaPwBY2kgKNyS8u8ZH%2Bxk%2FV%2B%2Bm7UjK3hPckWvP%2F0yq5PcWe3S7ud2XHZocfT5eyqT%2BUrKC%2BiIqcc6f2aaIe1Jjq45E8oNUjegrsVNZixbHSl2kzresYdV2JXubxhHJVH3lmgoeQrnSNxco%2B6306b%2Fmz3ZjzskLCyJ20rLVcYMUxtEXkFd%2FYPzfK70y0KWQltXoJBnxWSTSBbYhI0biRqYoaJ4pK9vbF62AxQTNFpx2X3Mt3apjlrpObkeF1TETISLxghfDLkYzTFucBy%2FdE%2FbyTOhEphZxnggorWPIn3zDqB%2FmLI14WqsMddJ%2FH4VDZt8e9MpzRuC%2BUilfbsjLG2MxACNCoyWLoK8ilNhbtyQGrk3p0ywFS0hqpFyhNmSbXP1tKxfNvFanTY%2BARdu3NykQIUP7oBVR%2BisM8HlH3FOSp9InmPJ8JnZ3czWzj6VpNpwVxIhoNJWY9VkdY2sk98VWs5XbexpIpE2QF2s9nVu7Xls3VPJuiB8vxuMlWyA%2BhS3sIDX6PHwCascQ299ILnmNWl5G00r8l7yc0lFxnZXr0yZ3z0VRJQeO31alz5kBFgZQJjb2n2Td3ZTyqX9MYrEcpepr0VcuY5u2s1r8%2FT7CGNx3o%2Bxy1zhPVQkDLrGQNwWkB%2FE8YsuDDnDpt%2Fst0aenZ17gAE4KbykkrMrbh6qTNlBjQyCDmn4XEwLO8f4k4ysDZ351jSoB%2FJq1Aj5rAI5ZwYN8KGkvfxyenXU7gO%2BR1MZPPydRlzXpJmYPFnhMGDI3OFeDJry2gVaadOVRwLgNenDyUTW5MyZsFGsRjzymh0UzwZd7p53ieu1rVUYijQvjFk2t7PK%2F6qvVPiD%2BfqCpZwovM7k24PE8gk03qpUGqYhoRwvXRcCCExg9zshLJ3WZrWbI3sX4DKWehlV1vS1ZbL8OpuTiiZ1STjOF5m%2FXKBMbfqidSpSF6e50Pdd2qAqcGlmcClcKJuLA4x5%2F0IA5uQguIQXIv%2FJC8nL%2BOhlFdD65VZv8C9ElDT9klGBK2G3JH2sM5pNA5yiT3azw9vBJePvLsLE89dmHA4zBWloxGvh%2BAwnhSMQVi2j97JlW17vNzv8ED6jboRSn72L8KynAiF2Za5hZKzxhxHJA8taC3PkKmUwzgIR434CuPw07tfXmqrz%2FqRIY%2BdM1z0iOLFa01iZ%2BtLULn8DLOtv1gRavdoKyu9BnrcvU3GvbBRoKyXHWqbSpapSovlc4sXvmETZGlAD27x7Ehpg8mPxtk4RtBF2GcpsxLCMf4M8pTkUpPOXnjH%2FjzkJGFnZFSd9LYmt%2BXCBlepfHnNqQcuxF92ja4RpsegJh5qS1R3btUSFQWMvZ5cRVav1D2uUOICidPEVP4loDSxlYL9la29voFmNaKthUDWv29%2BkCtEhoHuVFLSLTfDbJmOpjgeMKblO57SvxtsVOsgrMSxcbhgpa9NqB7y2SfYG6r1ZRby1V0aH1u4zRDPk0WcMuL9egLsiwmim7kLQWj3lkMordZc%2BNfpDUNCzWuC4%2FQULuubM66Krd05nGvXnB1yVxcS6%2F9wdR3bkiIx9mtmjzdLIPHek%2BzwnsS7rx%2FiVc9mNl19XtXLhAhJ915Jofhh7d%2Fkw3vyOyebhvkOwqYLuju8COgN9oKHOMDWPPTVYhBG%2BiFH21TEPdGVnZelqjBUrQFCwJ6%2FHfvxyXu%2B2rNK4VyvEJ2OjUDuIzQWkpAmSA1TFH%2FRi3ASBjeBa9aW7n8yoL9BNYtaGn2jOMF%2BiTMII0Dnf%2FPfkT8rZ1wvqQlkDNq0g99pAU0iyXNeTQLhYBLZZvJVKSFakGur8BSAV68gnqDR3iUDOC8m0NunDLYFE9MOCWp3iGzWxRHSeE5%2FLGMPFVtkKfKZ2pUxwQUMsA4FXyEv7Y%2FTWgh0IWpbm4sevYty%2FXa09UbedDbhBgZXb9L7M9GxY61GI5HNcTT7MuXhITQ1HeXUYyPMs4aUtgPT7pCzyRgi0Ysik9n3zaMCnRzq8sKP9FG2wjTIQoc9uNOdM0zOn9%2BkCfVDF7aIur8Ah8Z35M2vePyCcDttgdqFmohSVu67rhHhuuBnY4AKwo9ybyWDbosjqe23ao%2BxUEW81jnzUrKlps7hK3lBndWSn4RB%2FqQVFKHjDqNzx9YQ7SAOyms4FcvLu3uiNtHn71F%2F7tiLLP09VBfC4kB6%2F9Ub0ryIaPtbWmmQovzaR%2BrK2x1oBYjRObCCOIL1LyfLJt%2F91tyUxyvJpjjmrfb5Uh7BA4ZMFk5JF5GKW6wmmwVqjfkL2N5Pqb2ng4ZTmwTBsy60YEZFtbvQ%2FMvdCgH7IwgwibTjyeRV0tj2OCR0fMYDekiSwMcmBcL48DglYwqdlpUbxFixVG%2BCfdogKuxqK8QeEv9GZ2WYM6lwqy4RTzXoLaNdtGOoq2DtCcFffNQIaHOf9%2B1iHnooBB7aav1LtZZQ1ocOTUnf56IuAfXWSCh30NQlKLjHEtSGA5D6G6OsEtWnkiFUfg345bVNIReFt3iRlfhrgP55JQ4yuVfTfMkusGTNj69r8d5v6ZMpPjUQW37kbJlilmxWasBHIJdliso9VXQHPMZ%2BNQVfBHrg4lseCWmGGU8VBHA27sQswPMrcuFZjNWZsqBIK8C%2BffjbC2PKNcRp5Vak7w%2B0pUnc3y0RSnd807NlQCcGovDoFdX9EoKiER84e414XVDFJdBMMLbJzVORfH6zSWW%2Besk9CiqPy5yMLVW2H6FEr8iRbbHizw9B0LSKa%2FwM8wWQGJ6J2BHYnYMUb5LZgagySZwIDvmg0LUsCjwHVId7wGzcENNIYkZTgprWmX4AAYrmdxPMs7TQPEpbUhkbmPckQG76pb7h7e5Asi6%2FHD9KwCWlfxNXCf5dDS48TXhLl2pF%2FDkLDGwscAUs8%2Bez51n9BCNtLbsEyePITmoq4hCKeSacCLg8n6BrIn%2B%2FvWtsBMj2idxgFzU7uw%2BxJj6C4muimwfEChGZ0iIOnPnwaxDRVIoWdgOWYkoQRzqV%2BLvMEa3TTfgg7ad4F1cLRmLUd4ej5kkBEVp5vzkVLtVOv9XfCHd511x%2BsbIfjcjUuuzWFzfX5n5pqCdEuQl%2FA%2FOJ4c1ys4K7PyiJpR1kv%2FLtFFo8WFdLaIKkCai2xznmrs9XrxujWn%2B%2BY3DyKN1hCvdc6zmUKxqgpvGoFJLWSR9cLjplh6cuNcLYC%2BTfMTO7%2BUsGw%2FYChZTFvP6KRrVeLrNokWQZ2gi82txZ%2B23bfglm30p5ofo65jDvWoos3iTFm0kr52gcnrVO3UQaTP4Urkuk3ALs%2FNez8voutNgDyIjKhBWv64waGE5GJEHuarf9uzJVcAmXb3Lo%2Fr7fPCY%2F0FLGmPeHeEJDQajmTidhAfoEaV7a9JiWsFvykENbIn38riR1bPcX6HOBLyXJDzsltkn3dz6AofzIHWlHWcJKQmVjKN511Q%2BdGBlghwWc2OZkuwtUB2ntAwNs%2BjBtuvqcEkYkFXFYJvEpBbbSyoHBhz7t2eQa5jyv0y7N9CEqvXMb%2FuSQUQgP%2BhTUYtn%2FeiLZWDiu8HPogUnaOym%2Fqnn3buGgMvJf%2FZvKGDrOwvg8jb%2Fc3anhf31Tzw25lkIbcr17XxtoMOZVCUn0Brh58nEPJMciM58FocMGj9ch9%2F5VVwBC56C0QPUrtf7njlnqu9FJwRiYGJl%2BQwHoxV9MbbcT5%2BEC%2BN8RxOQG%2BE3zBvgNx%2F9pyoJ84i2MUpDnn8VR1fF5mDiGOzvx6XqoGaAdCp9AD2UK%2Bx5YAPDt2j838Ci6E7kreS0fhG69mBPHpwQKmwhe5qoZVAUCIqF4J4Bv55uaa8iW6P0IySE2VlhuVpGCQEz%2ByxMWneA1Ec2XXYkot25%2FXl9gWxyn2tNVVSoJlQOhT9XAfal24OJVRAiWE%2BzMZZ9RIl1oyyAlJduhzdkdFKDzBh7ALhvxLnBdIeY1j8IT8ocKwOdRvA3C%2BLnxeXyFOO0wLpNyovRSwobLXFXZ%2BxH%2FAI0yvrErilvHkno6v10HjFXOfua2%2BaAFbPdyYcbVVQBuxYuShEiqUN5EkFmjznMjIDpTaD4E2YyWLZnfj15rjFxpvS9bZUq5L9yRx6j0XkQjP57VKvTMljGOaRZI3udbA0h9n0KHex%2Fi8%2BPx0VI27hlJBJTNqxUU4vVF5fD4ALn0%2FCMQvkVP5WhK5JN7VoQ3ZbnSXXizXw7HOSdCDt4OCCRzr6eA%2FSxDaCN6lMl1QF6Fijk8SbSvcFuIqwjtut8xpTzEvIzyfVp5OwRaeEho1TcI%2Be%2BOsjiW4SS%2BYDK0ia1Z1%2FyAZpSsw7n%2BbL3WzwrdpzYseAx%2Fto1yzdf5MvV70xfh73bFa%2FOATzyHAnpn7iIxMvLoT1TJF%2FINBIZTgTvm2CTT0KHdn%2BOQlRcetxmGWjBFdwSWaImSSv5eaaKMh%2BCltCV13EGW5Q2DSaEs2t%2Fwj8hyslkaMLQcICwQI3mkKcSviMBHAG1mzuguOXoA6pMzYKvI71N%2BBwE9ZSxOtcYrcPoG%2ByETL4aGpQHUqoi%2F2pRS3AzEhulMlychjX1PcQoK5RLC9HxA3KsshDC0lPrdvXI2rzd%2BvMb43ThcgZrgHori92EqVv9xE47IhGS86FfKUzDoVC651Qs8a4cs2aTwCqe3%2FXD%2Bllrdx%2Fct1WR8kSV4vdDq1tCQYv27fiTyLFENGdU8TZbyu6%2Fl0nR7Rrci68fvO5ehgBId3i09mcMn%2BpXIX4cxJu8wkiUv0TCzPRheTlnyYRsvjcx%2FHX3kJTB9p0Ln4MMHu4X22%2FaZDVMA2uAG7Q%2BqpIh7wuV0jcTK0HarXSij1T%2FC9Hev4TFf%2BhpduoW23%2FPbV3FWTFV5g91VYfp4qZaBOci%2BwpUtOzKDUr1%2BDKlU4SvqkS1I8QiolPMw6LPEsUwzQHpuJsYflITfm1UdxBO9AE7uEZYSLFYMN%2F9qyvjsD40fRFZ133LWthQi2GxVziM10OAuYxEuIGoA1QpsLKO%2FobdTurmTz2a9yPR3gyZ1Y%2FDMwJj2E1jJGWsSfBX87tEhO245iMYXtCdNCqhqUyxQNkFX04X2QVWmNlOCpe%2Fisxytv9vgyE7dO0cmvsyA327wgP8w5KYaLEg6X0tJcm2gGSGOHrkmQj6VilsiBUO%2F1G6VReDHfzUW1saywaTR%2BFIKGuCQvyTB4Lmf1VKW3XGpWVPcPP7yD1kSAK8T05v%2BLiJPHmP%2Fu1TgmlMTvjLzg9muk%2BJ%2F4%2FhegT4%2FVRXezScv09uDpyMBeSFQc7yciF7s4sYom%2Ft%2BDpwDcL%2BZFpsGDNo%2BNX5aBy0qd%2FeHwo8A%2BdojCw46D0y39S%2Fz6AUMSyHVpZPqlfBD5NL3SSt%2FySisspLmAxAJOJ839L9koGfc1F7H%2FiYYlwL%2FxH8q2X4mlKe6PdaArsFvkAdiAkz7s5izqwW6vbDXbiOL6BM9UdYgKDc2Qj8csh1If7qrHHjat0bvj1UofWeFHw5%2FPq04d5Vw%2FV1cPFZT61svbAryMhCeSwa%2F5n0YjxrLDfWe8C%2F1xkjakYI8xu2VMpq1vyh5oqIyDmqPau12Od5lPjrmkfew%2BVba19%2FOwM%2FXOBUqS0uQ5g242SrLKEgOYNquX3O7nOqEYGeDJ%2FrNTbZpjdIE8hKXpavAv2ED4GWpjJHXjyOF4yZ%2B9urO9KEPhRVuKJUj6xTPOZT%2BSz26MkUfT9RSf9cKpaVTg4NIbBe78W69XLrTbfnluVKWdHO5FhIroQFdlLfR560VfZKVIEaK3LzMn6dBcc%2FGXvCPBOGNXOPtSZLDlmYP%2Fj0N4chnkApiz9YRFHiCfNWBphI8CY08C5HnyJdZAfnQY0J6hkITlzJGr48NrQXD3zVvDY9kU%2FtGQ3iGNswr3Md%2BCfOrqB3BDzLIraUgno%2BY9j7m9Oi4cUQ1yH21p0wWS7cWBUs5Bx7VJEWTW1q%2FylSdAfXzr26B3f3JTNH5HBuSEecv%2B%2BifAis%2BbAxNpLXXnCJUn%2BHkc6ynq%2Bt07I0HNy0Da6QlAnd7YKH1SIZi4MyDjVy3kx2iY3foObmxNZOv76Oprd7eFJSLpUr2eofjl593pB5TxEPDbehPQzetD7ekUNhl4V9V59Y6sVY%2BeIwIhexQy%2FIXdV%2FtcTjNJMV7pqCnIwTO0p2mcyH0Mmgu9ROD9MoDSOyPJPzM1KaeFgiEKQmdK5Yp2xFgYwYf5OBkLZTKOZBawo70qpPynanwhQdRIGkCRLBF1Ur8aUT3RpIEx4oAjy1j%2BONyR6wV5GmqD0bs2l2HrQ8cTqJMVbHBIRYWYv0CpbeollNiweiZL3T%2Fl%2FdCk1Ff1P2CNzSvQ87f7X2t2RQGi%2BTJwnuIpShTJkv3Nnocy3wVcTSmq3Fgoq84Df75grInotnHLOISrRfSK9hxzZMIop5rR8viwyH7njJnnGWcimB%2BL6JyVASCFzzd175SPh0w2%2FfvhtfSRmq9bz6Gj5k3tw2ixIObEewMa6z7E3XR8WI0grFtKavOPNOfndNM%2BzbvpPjLHjogqAN5XZLWD5k02nYBn96bpcb%2BIA9jjhwze1CYKtFEfSLG5IJf7tGedkAnqs03%2BKuCUJl4AN%2Bsw889UtHufnDmEK3FD0AuX%2BjcUh28srS9F%2Bx228dS3srNz7bLMxiQLxQ9qSXuNHnO4taoswp4cVzks3XWTmAZ8zHQajdEwOLLeTVSd0qiu5FwIX4sqoiE%2FWoy%2BdLiarWFb4vg1ufmSiWAukQfoM9fMxGlGDxVZgaG5VK%2BgrbOQXUWbXwQ9sOH86991QOzIYcQxqaWzAG7O%2F%2BPt63zGJATEk5yMLj%2Bw7QsdBi3pfegw0t4kipC6qdbg30lkn%2BVgbiN9dXZ%2Fcq9VkXmsvpHVVGbwh2Kay%2F46OZA0jiGrsLElaac41gUjviTm1MZ8pOBWE9W6SUNOHZwyIBisGulfO7p1RGvnupIdEgKkI0sfRx7cvWfCK3q218y4a%2Fnaqi4Ei7U74nQv13GWy%2BVWzO%2BM1vHaot%2B1yN6cSnGEVskU5daU9w17b6trHIGfB%2FwT31Qe30GLwNUFel62yvAFTXl0vzwoYavRcelB8kY7iyzX3NFRtr61KljlmgaS0Q1Xuo6qX5BtoCFFihSx%2BXDyyHN3n4YyvgOvC6rpTZDs0jdrj6WmDEyamyyC%2B1qyRPjPthLbBJv22fzie3ViSSyCMta%2B0csCJr9rgN3zQrokL3lxvpc8vwtQuTia57XQSGfjfB3YcEm9SlpZr1KS9Jz0Gvr9E8VdrDQftF4zDpnmAsRep4WaNUupH71YJN0xpKHIopWxS8z6JqCa%2Fp7%2BiL7qyJlC2yizx4BXjbA0iKIuqcAqGLk2zMfBZ1xz%2FjQWgH6kMZHbPrJF6ktHZ8P%2Bg%2FDxHqWN7ni7uBCq3tw%2Bgl891EFJE6SaesU43ebmuycj7%2FUNtC3OT4rf%2B3AcpPA7tkz1BlQgqTLsUnnwZSX4KNbGYlUnXcdIGJ43q%2BzwOJ5xFanejlq%2F4IWUmtrdUqtOTwf7N5GnvJepWn7oCS2h%2FxnuGuq9YY5dw1ZvnLVi56mkO2XrIOSJFoS0uvETbnHUQPUvtxn3oqN4yMh38jviWuiP41pb2rVXXSUuR%2Be6bCeur9Y2j7FuRxzs5R%2FXacvJG9s%2FGGpUIsM8JJozKY7FO%2Bs6P1cttTfUIraxiiD3jlvYpbRQwwT7pVLtYYvNIQ42zd5WwirwNfyWMQB5fFWG%2BOMHgH22VnEdtgzHhmgAuRnfLWvUf2bg5ve1QJLQmERcTKnyLMKIj7maiAU3WLWnhIyKDaD%2BbtxDMGXxC8K7phjnRXrFVxDOwVVwi6xzofpRASHJLj7zLYjt9nS2a%2B93oxAu3tLjtNrLkayJiAXuh%2Fkvr4s6TinRNrHAawA06qQBJIYK8m9mGgM3GdwSX5FlOy4B7RdkA1m6ABWnUYUGHNgCYJbt90SF%2BHb909DzSBH0VaVEx1%2FNGWe9W8evbIgu7mgsKJl9GtWjH67WEgf1K4S3hBwPXVsnEEq%2BeHGrB%2BRSPTwH6aAOskfDwNx%2BXdqAu652VX0jsC%2FROXe3Iug1Zpj5CdX7FJDEPNk4CBzt5jbO46SIxYXwyWlU32wLVj6GTvH%2Fd0gulfN1eJhmLnRbabPbkW79CuZDwv3cDbj%2FIvC3KFo3MdB2TlljqQwkVFqP%2FWWuQCQI0xJGOrYw3TQOB4O6ZcN1V0ty4%2FBTNkvk1mR%2Bu35%2Fdsby331DFJkLZxvDx8p4LIo9u9GlqVazx0lt59XcktwXmZnKOr3fRd5gZM1axrejCNfD%2B3Pj1k45Qvw0b3X4xqeKUPt48b22VgqCi1SDxVwNV8acwq%2BsPxXQtrFHgrO%2BkeC3Mnf5Yfyt%2F6EKcx6G0MZrs%2BuykUaIFXJGuFu%2BXLeQdX6GgmKSdbdbnhL2A3sTEiyckyD5d8HozmZMySXe5xldB5O0tfZWJ21IhmTnJMc8IJWicvJepBr3tcfzhj8CZh7ah6D1d9SLlZpTYAcYpl8nXuqI%2FVzU%2F22sJKFFuVuOdVI1%2FqtZMD1KTahjZSU2ngvVXzOTLrLGXdX%2FzJLKNSW%2BpcjGiIy39f5vvRYxoqiNHkC3TSEtFdV%2FuoNm1uWMP1dxSRhZ34YtCTTJkrOWg1qEiCAoHgEbpkSONAqeKw29uN1ipXCv%2FtdMSrtY1L%2F4yZ8bFcbWys%2BYo96fcBzXfmpghkH9DcUk92vzJP5IuKpy1waKHiAH810pOU7qYJqKWVapoCMwQd0H%2B9Ts76Q3lTtZn%2B%2BhTzToPJvc3xth4pir4n81CzBcmTuzqqix1Y2wlxaSeKWjVjqyujhUOi%2BKFgcTjlViC4%2BaPRd4Q3IAMcFttBe9dcI%2FEyl9WFFy4IvJ9Z%2FvvwywYMXRFk5P2E4NEU4y6GFnZZ1MUUK4Tom%2B7W5ENffS9Ppa3VSqxvRjry%2FNMef1JmllxOFn78WmiKAIWrRcDJypecJnf1KAyMqy1XYHu5OKDonynbVQSbhjaUyare1XblqMtEho2WI81xYXImM8Es0hID9zA144mNjpsD4WmxI2r44L7q3mZG4fiFOulBKyaZNhEqoBor6PoumELxR3ztSaA2HCrl%2BF8SX3jBVpLcoy6jPAAkuRUvx6npZD1XHBDT99fBTFtxDmJ7uzIsQQn08w1xVn19Ffn8N6q8pxPoeC5B4fVksyVCIgId8SeEugoPO9D0GzrXQg%2Bn2zkZ%2BlFLC0NsplY%2FexowRFMHzCJL40KKk8JMQW2TInUxYm81Xb1%2FqR9V2MnSdr9X2lPSJxTtoKjgxquBeiCClMQSCVVpTwZES6NGAyAbSyaIdzvo6drSQepcagkoDNTWfafjElCWStCbh5pGIzKeW4SQo6rCzsHptS3NzT5QP7RFt9Gngf%2F3X2MZLKlZND126gNUZFI0EaQka0%2BI90T9RtOUwSnpRbR99HyEJOXAtH%2FzYjqjHGLJ8YwvZLBSwU3dujvMR6TjUxjAgBnrJjyI0kDdZgf4SWqpxqnOR78SY72V4kt%2Fxd4l0NeXNvS06ti6rxTXqiC3UyGKrRDZ%2Fx8jofQHJVuddzw%2Bnj4yb0wetWwAZKgXQ0VwvO5YQ%2Beww0KgjXp5KVl%2BFIpXu8lUl9zaxSaLzWUNI1aU%2BcDxly8L7ymHeOzqqMpBS%2FDxSHpV0YKLXCr4z35cytI365VkOl2IYIapNdYYjiZb8pFe1oikiudFIRMXB46iwCgXqy8Y1dUZRaRnmcAL1%2B1ccXGxWbpuqCYBZptBV9lH4nONlvrtQuNziiaE%2FDY5bid7u93%2FJ8hozwJ%2BImgk%2F5y6szSkd3ubRUs2oavxYmlT%2FelVIDJFwfP7aJVo53HJslXwGX1qWybwc1B%2FFezlyg%2F16VuQNJbICIYTFLlO99c6BSIgZhdMl5u%2BIZUt%2BC4MWpqaSS0ArwbkF%2FjxGg3XKMr%2BSlCi8sf2u%2Ft8V70Cv7Lao4Hb4C9EAvpfV8TgNZT4UEmCEVkfwhG%2BWJzh21dQCqvaFiZUzBRI%2B%2FASWApGzhmZUlNK%2B6wfQGHR4GdY0Vaf8tc%2By1GrGxKeZZ%2B88TtdGYTRU%2F76MFOdRkLNirfQ8C1%2BWv8VyrwMByCs%2FU0cwaULHYWyId%2Fv3tOKE3rMkhvpfZtszQCdhq5m%2FGMsxaxfkl1cDO7pjXpzZEO4m4rTUCR%2Bwn%2FsbAruqaa2mQn93NBBGea4aQz6VWbu5mK6Jruqacx12jhqTFMHXfvH6RuGEQ7lN8xPJgtS48zDHZ7zy4JEznxQwlqQQ2Y%2F9U2HxuL%2BqKvDa4ng0vv3ybPZvDt%2B7p2hJRgVHs9jnMlapOQjpW5vxQ3ylQvjrF9m1e3DFb86J%2FHJ8dMwWGw%2BjsCDHC9K1F3m2uYzbniNCfoRE%2FQjg2PJBVYTRorKRLEKjAklGrz7%2F4QRoY6k9SUg0h%2B3uDCbGabiMGZnRJy96Vb9f14nJPn20qcoIYyywEPQ13MwdN5wqtZYUnsCR23iLq%2Fl%2BHSn%2Bu%2FzMt1k3nlX0Qze8wo8hAH3nJcxGD1F6LrHnD1%2BRuYW62N6r7%2B7kncfhPdrjm%2FJb65pRbfMHZimyL4mQcQoc7RIe7GdQz6a5WNFgLP%2B4vC%2F0lrRXxz7689xNr70YVJfdOsX0DNI4oIuAZATX0mwYUC7PQeiuobrEnpvEvTIY8o9AVZRqdF9sGk6uk4hELZVzume0397I1OpDajc1fxDwAizoB2vE6R7WaiD2ajdfRkxMnsfl1WjO1dVO%2BHj4IWOaGrZkqncNsuGmh3iC22AHJnuf6qo6GN6awCNljjkN%2Fka%2BwqXxtGrxjAyScdORbPfQQctNjMSt6C99TK8rMtNBVa9fP74%2FHNqXZ5%2FUkL3%2B1XOvxIfZ7rmSBtoeQpu6AHN4BhT7pCazLZkjfn1Q885LKyr00pr%2F44NfW5V4flm6HKYdZQ%2FZIEyif%2F4dWEzRMAxb%2BNNGRaXud0B5J5Onw6lekiG7S3XDc%2FH5WoB%2FHELC4Dys9k1vll%2F01bKcwjCxArZsmL8D7JMRrwI4LbKPQAR8%2BPL2rBi0qq4NhUzE%2FPi70%2FH3Ha1tpr5FZLOMuQtNFqPyQ7WL2qJnZIh0Qr37%2FlutEBsXAYvf%2F3%2FfoRHQ369RA3PZ4KBrvYphoK6Pn9z0KNWZUwKae32dpg7ZNR8riR%2BliJD6a5kcodnUEPc8Igs0CBeZAfuZxKZxCMdDK4ZLdNLIP1lbT75jEfBHD3j5FyzF8an145Nsy6%2FVu37oi7IQ8b1Ph3OoeYzzsUI5vWxAb39Pvm24DW2paDQeI7yTWa3Od0%2FI%2B58RTQuqF9vypX5LOt9GZ2OOxQjqoH%2BGwlI%2FSUisTe1ar30IwQUaOoXipRqvc%2Fy7D1SIZ3iyiaLp4qtIK%2B81CE7lGZTvvsj3DShuH%2F5iNEVLbwmCGzDUWd2stq%2B7d93mfQbnFoWKnPCA33dtslAZO9%2FIUQZ3PnZFHhXhFv6ExhoetRq1%2Biwb%2FfXWuYH%2BjuUV6gyaVkJh5%2Bv7QOnfZPV9zqlMBIpXWFc7uJQG%2Fr5ma8E3gYT2f52%2BgE0NYuO8dsvU6L%2FeaWJL1CCNYIQZ%2BODdPu1brx6K81EfTbKNqdzmpq%2FdU5s2YHmLvcau8tBHOBYwrxc014Aw4UsOK690fbzAwp9T1mp4j%2FyslBhcJPO6CdllAB4pfGK2LkeuDw7hCJM2LMGtwnc2RZDB8czSqK9bTLtvgs9cxBePhOZEFnOQm3flCW%2FHztOjChvSH9OV2G9F1Tgjoy%2BX3sSJ0wxQBo0yxC%2BgH8N4nru%2BknIdtg2QZWm7dQ7ETb5FSwF0HQgo03amO0MeChKBJ6fgZlnKWvajF9Db%2Bv5MaCVkzaaGJJNikJm6lsFbg7WMstr8NaDjELrkNa8PgOjJGdoo1R0GROLMYBeugndF9J2V5cH3BUA%2FcTF%2FK3AjTwwLgb%2FgqNKAHgfjbJWT631ZLbwJDkie24KG%2FT4RgbVPfRaV3EX2%2FnfK9v1KCKJDnpLYysfNVXYvx%2FaZOOvNtvkrku1ofeNdGxig9JzD4wVOjP72QbLrWsUvgkr236gJiZqNwR%2FXrj4vZeZMO%2BXBfv7u8Ttofb%2BeWnN4Rdo02%2BjnuvqiRbHguMablhSwYqyfBaFkQuPcSsfA%2Fs%2FjsepDLMhP94PW0V71FxiSQQYsrm%2B1wcJUfZDc%2Fm751P3iMtpKmvr8nEP2uDYOYfPUI7MZPPnFSCx%2FAwwa%2FWG%2FFbwcoi8VYK%2BP1CbEmhOxnZUAVAbiaUCjG%2FtrH8aMZTtNFlwdR8udEcPcteZlbQVozbd0tBcThv8s0Ui%2FK79HKi6wL33CTa0xjw4R%2F7l1gjacmTNwhW5lVqO3vZkRcLURbhvLChxAoTTuZqAfYjNVDtheOjd%2BtqjlgcwVjnd%2FySzQ8%2FOApruCFoDWQ7JeN5pg7JgiG8ZP31RYAFx1Dv56WGgIfqjsVAPUFXli6XHPzZ2pd5C4KGtXkx3zq2bul7zIgsodqxLVXi0kftv9MSyDfrpRggw1e1aR3Fqa5B%2FDiLqPK%2FrIuo31r3V%2B9%2FKqKxmi8TPz9M3iVYIl9tcecLFMsDnIoOZAtd1Wb8WShA3k2LUfa6R%2BVUsqTqU7SKBu35duBajJ8LXYNkqmyaAN3o7xc%2B6sdBa5MHPpHPcd9cG3JS%2Fy031BsHSlHsIsHhT39EXm0ODuyfG4i5EHfT2u9m0a5JXCP4pRGbS8tOglHW05lj%2FbgXzxDzRfe5hRvM3CGHhAZTEuD7FkBCTDWWHdUZFxCAC0AwzebbD%2BwnX6%2Fk6dyL8yVAmaC3yMQlL14o0XkI6uY53ohF%2BxNyZYRyMwUzCd8jsAocrF5hJBlZCECTbWe6gAquwqD0XLmgkHojaZ1iv88Azun3XUy%2F4ixXSG%2FmZcKuxK%2FtkDSJ6RCa2yYiRkg10%2Bl7a0BXI%2Bl7ptOmWk1S0n9os20s0boDqZDpgl23o6AqLZZwUGBK%2BnMhQr%2B%2Fc%2FbsCZRkl%2B6vdd%2FagVkYs72%2FFOya6PFFd2UJnTg1uSunhFWdx9tToHmN79UzBys6NO5kSJ4GeERtBZxbMtIWpo4atUbMgZ%2FnYB9PRMZx4KUVtPWMjdWQ0Xh3fmlyVVO0ANtvjWcxCpiluYczND6X6r39cLHvK%2FylDYX0Zezi5VgFrV%2B5PG3EOF%2F42f2f%2B76twQzkG7PaGLSRkSv%2FcJfu0UNiiWNYa%2B%2FNE4i29L1lMWoOjzUh2%2FlmXgS8Ld5ts8LAXg5tt0%2FKw9J3MM8vpm4xu5keXmS2TULJ5RPI%2BN%2BdNw7mNkEEh9mLDMfLk8k2ZwNJb1bvwkOktSCokwxH6QWvITu1LFcglOIdpJufWYonA4Uk80%2Fzayw36aswTPfdyX%2FG2hWIoOqg1w8boVY3A13hZlzmeC2pCswcsqowC5V5m9CrspWLX4M1fZ5l%2B5cUgs9bPqK%2FeLKxHxz19Xqh9J%2BS%2BCi%2F5%2B42nWQLGI4lt24J%2Bj8xEHfXLrlXXuxKQcJynLmOs5Ka7Tx355rhpg5HiD48bCpuSldkN7BE4RfEf%2FWkVK8VODaPUmNJxGaaenkK3CifB3BVQwKUeOoMqphOVW1k%2F3N0LIgL5ovr478Acix9wB%2BFBtiJHJMsXQhWx2YIhpsxUD7PgWKhjhn8an6fKucrIzL5lw3icCRVeYGsR7SfmmVqby5Aos%2FartDZl%2FObZthtRm6R4Qw1dPOl9oNdiS%2Bv2rbYx%2Ftz5ryndNuLt43%2BtGf%2BpaGl1W1F2qfCa3mMbMWXDL22TKycxFZEqL0Ss7YgkuvL%2BvQsBWB0%2BkWcBgp32e4BPRNcsP5asn2g8A4roi1D%2B6PV%2FYlEFJiyul5GLwaZDfkOlRLkR%2Fffb7vE7Ju845SX%2FtJXdw0wdggPd6Ir4ux6X1akCpqXE%2F%2FyvEPg0oyOrXFQiPC1yg8JenV%2BRf09qvCgmjmqhbVPi7ZNmKaixD5iN9VWGUhUYtje2J8jiD1b1GDqBYlBpoZkvpU3oJ%2BsYihx%2Fn3lWFvmt0MN9oey2IMTrZr5TKCE6GZh3cKhKb2j6UIa2v5nga0JVdp39DaYAjcQ7MzYorgceNHO5prVIbzF3Mf0kuYnZAOMZJz%2BUxnBq2e4vadxopf6GKrQT4LH5%2Ft4erJXtMD97h2dLxPEWF3XD0mSFwj6jYZx0s5QAT5WZ%2BD5FyBk2XX5YL8uKJ4CSDO1uvwE%2BpWr0liiGPmqqZzeBpvOENs9SqBNahFh5JT5p0%2BLWKy0j%2Fap%2F59CktZQfBUeImdmp8oIXdX58YKKeQW810QUkd9HnvTcBYsSxp9nMpaS%2FhZ6G2sK%2BLl7yVBuqxMXPWuQLWlyzWMQAHKQJvvYsIXIOtS4JgbuIV2qy9C%2BYQDj2dIUMVo%2F3JvjguLb03YdlcapBMmSRBmNJLde844TiRekra%2FDiAMCwAL5sR%2Fa1h46mN6yf9xPa8RrDquKPOpEd%2FowgxwL%2BFv4NqiGP3ksvxOgvAF31FEPopqEWvEjypEHvLIFCJHhq%2BalObidSu1CGPMszEAU7UKaxebNS1rK5%2BOwbHKRZKvNuzRo7Do%2FBfUswqIgGqY9wU%2BDRhSYk7lBscqGIndP1c3zIpUkJvMuvLd7Zd1R61ciZ6TNMFJn34%2BBvAKuGUnNY9V8afzLyUbUq29s%2BV5MNuViDY3suHZX51rQ%2Bg%2FLjziAVftdV%2B7CyP%2BqI41PgD8tCFPaZaJ1lKOYCnnzGTZaCEjrIU1Jly%2Fcey3CDLI31kHpggtExUBSsXGh4chQ8jNLJmLVKVvtHL9OP689sySE5WXwukEKoz5u%2BG57n0oMGwE5R%2FEFLY%2FiaGfX7n7k1G24SPPJ6fIRuGIlhmUrHoXzIVhp%2B8yAxsfJfk0MVk255ql43q%2FQjMIZCSEV5G4qZbsaui9wlqPSnll9f%2BDxi6ddf7JxfJAn%2F%2Buvhg6QXv9%2FcbrlQUglBZjHd4Bze8jqhq3LoGkWLgLqqVdUJHwuBH2rZEpb%2Bzcrlvk3w1Ouc4YB8vaO%2Fq7y5m9ayIm1ONAdKWHk304NwQ4zL9Iaa0rPURXRVzOAVZMRuexvSvQUo3uZHJ6wdQhA6OTO6Ar12fXa4K1wblgDeY30Huh6y2wi5spkIjKD4vy%2F5lkn1zpa9eBpMNrLlEReMy6VYJo4qz24q7P6%2BX48PAqwX6ERR98eQzjYIO1JwcQ6vqdweVS8qPzPEwZPrVeZKPbHOVuJ63cSMyH5f3gWCzrb%2Fj6JThT4EKhR8X%2FaU1FVfV4L9GeOhI%2Bgpe5e%2B886sZ0US4klivfMxKx%2BSrnV%2BGZ1V3kh8j61LDzgT2%2B%2FvTXBIcIydnq%2FLXUwpr%2BjtNLDX6EwiRwf%2BmOw9Jcb5VX8Y6KVBWNsKn4tKqBZzD9L0WTt0I7Vv%2B59tnu%2BXeA5rK9mgTErEajNuJDiXspfX9bUWQqRa%2FAV07LX8P35fQ2JQ7lffx66zHKN1v9bHHrYBrIuhEFKFcp9CBa0mv5SxDvaC8DiTO%2BB3NdnWLaeQiPPU1PWU3ES9ABcs3tIaq1iFH7NASwOhZYeucr3PJpul%2Bme3ewZNvPG5T4de5Z%2BUk91Nh3q%2FXSi0HL16AftxZWBe4gY8lIbwUGYqnkV%2FzidDvNbHk5NF2Mn2tOccw0heLLag91bbtsQEkm0xR%2FkfRgSCVQC%2FOLm7%2B5r%2FpcLp9V9Lai%2FrIT0UnwcoqHtJv0AyuASvQ1mQAUl66AJqM2B7ToGTCGYf%2FiiYXvp8C%2BuXcKX51eXbz34dqrbte33XMsOwXatAsL3%2BVl4F%2F38ZikP9Gh23qX2%2F%2FOv1%2BIcdp4SUvNEhyt7jPFrUkV%2FwldWhhm4hDNdYx%2Fh0JOODVtWVgD7s5a9uspBOhEMXDREqQU5y54XqVgl3h%2FVLpqeXRPxcbiSbp%2Fr4tyKjtGzLw9t9zvC6Vq2psLA3auIjz%2BIgmjI0%2Bdp%2FOfn6CbnAgZ9RFsNJ9doK6U6NC6cp12NcraZTCJ%2FM7AvbbNTJl6fV25%2F6U%2FZ4hySmxYl3%2BfXjnoIAYYRR%2F6SBLH%2BDgz4TzWedil%2BME%2FV%2B95sCX52sH7jFm%2FdZcDjY6V59GDGdXq9YB2ePEf9ZVjr8cxEPk8oFXuCBCZ6WFZv6Hxu0ASWVxf0Tc7LzCxb7NxBhxwoPwrFi7%2B7XiVS5Up3MivnLgL%2F%2Bz7XN2CivOKnTcnzmsfYugUv7eEO0Z%2FA8fy%2Bwj2%2FF22wgCZHOhz%2BHTSMOjNiPIBi1iDJDCoo6e%2BAJLguuej1cSVVFFcECOhewoUU7Oj1hwHLh491Wa4JPYTHuZ3pojdfaq%2BKBya%2FbDTQZBmTyom7IBv0ApbsgRStb87s2AkcnSYFdnO9uVnXb6ino9NM5KJkrSXR3%2FJr1xuD7VLDNQxV%2FqZ9fwJg8EyFWctENWhIM7h0I9%2BcqY46aUTyOi90FWPTcEUXYXbROAraXQNCo3tqB3qlAAJqcdsZcNm%2F69h1szVlcU%2FfhahUCjYDs3XITul0Wp34t59QE2xSAmLr8CVvJRaK6ZasXXMhl2nx2SAqJS0Vvpb1gDsZBPgTuI96owkOFaqH8qkQZtNJCv3XJTbS%2BreX0S7oQveClCTDBel8G5OLAlCT8FnpoWUpBEpWRx7r%2BY9esvvcik9cUbAnYaVIG%2F6A0UsDJX7Xb%2BAakCMvKOxUGfScDc1CiITmivJNzAInKFcDiQwrf33kllD57hRY%2BxVKj%2F%2B31ff6Nv8oC8myH8nWR8bfDrFWQKXyKwTkexrGmMniz6%2BNCcInDs6d92%2BxsixneyKYMWISBaEql%2BduLwVGjRJuxb6x2ADa%2BT9j1esYww3h1ofnh73y%2FekpQb4Dc%2F9i5eZYPMM14a%2FEJBACYC9b%2Bj%2B2UKrBFcrZTWx%2BA9pDXfPQHVPfqIvBk0GD8IZBT11hBrcnwPNfH%2B1WQquKVfErkivvwbgmtFwkCr41IO42VkPydTznr4V8BeXUxxukwgeBq8PeDl%2B5SrLxMLb26cX%2FpVKErwEmOisRB%2FVm8KL3dsyd7%2FzCU8%2BqEICoGLCjqJVNpq%2BgugZ26h4a%2By0AEb0H78OtydIPCfBu8muvw7TEWz%2BdLgRbG4w7vhdO5JTTOPbQP5b4S1s4Ww1h3UiX84lYhbg9YC%2BUz9Pv7GCbQJ09%2BN%2B01DW5oa%2B%2B5LY%2Fzg7LfPfWQqIKdHVJmA%2FSax%2BcrC%2BqsokHiOMdwus%2Bdz%2FlRqORQK%2B6dBau4VGu7u7hF6%2FB5y%2FHQEaE%2B741KxmPY4Bfr0KYCmuJjGEt64V2oqA9hrwJiBuna%2FotqccyaaAY%2Bv4CfeLHEK2%2BH%2BNqAAbiAXHORmp%2Bt3aMFFoLAjRjEPuG6zjnUhP7BTuLDy%2Fm32Nw%2B6OUfk5cVSbQVENjMvk%2BEnK7AW0ARm0XwNvqPLEs0FPalWIE7BGnqvjjIfAre%2Bsq8WrxonlBv45YhdXu9y1fag80Qc%2FCm%2FoKIpKaHhDv794Pip4ZrRaT8HwzgKW3GaGftPfxY8qk73r5YLUocFJww8v4ZZoCPsDPg3WkO3cD39eKuPjNkAPS7hUypgHE3FV%2ByvgS9BrHjDHsf%2Bb44r9cBRXRSx8hKNWjrqxU2ZY7c7FjSVT59oihlZNux07FI2oXz1s1g%2Fhfmtr8Nn7YMLp0zTavaHU47Ev%2F%2BSSYuVdJ8br%2Fooi7G4UhiLFE63ZDat6pnw31SM5mTEn6vdTKKoQqIEA1IUIG5XUtBQCt2AIOUInLSuPbwZpz2%2B7ObdO9duSnkiv%2BTGISvUXk%2Bl33rAsN%2FV9eTpHkwZrz9hEd5ih1nuaIzwBlAioQ2QvM1ZnPlM3N8oEPTWK1VdITjCecdldkYMbLSv3EOW6w8ShHgmXsBKzCbHl8xP9oBIksz81lSSy3HmwlwdtBd1Mi9j%2BLIRpR42ujsiKxK%2Fbrv2i%2BjYhjvuGosHd0nK10tCN5FAdChIbWsUKR%2BChCrOobnZnjG73sBfwMymHaUfo%2FuKEnLR%2FX3NGXZk%2B7%2FhLr%2FL0AkO%2F68q09SlNq8zBVLI4i1vstSxS3B%2Fikth6oCmMyNyzihjyJVQkB2kTchsDj8ejUP2JKBNmVla88pwA01Xxm72Ii9%2BZU8EyL5CTvNj055yST7YbYkTvUl4gaSaIAs4J0iWE%2F2Qky9n8hvQ3t4O6xPLdv9UkW%2BoRJ5R4GzrXzcAdJvAp9tjNwOK6huO%2F2B9GANcq3onKjrhx0Ws1m3nh%2BfjooJK%2BMmhl0QixBa12XiKjC39HeLspYmTTWMFR0bZJc82BH%2FuH9r%2FNyaVQJO%2FM8zYYfWGxzJfcf4wEup%2BDhtfam%2BpglOUV87%2FQzbKUj8%2FEv%2BLFHdoi8eFCZasE2kAME9pZCt%2FdajX%2FFlPpICskjZMcjpJN71pjdz04ihQ9%2BV%2BxmzjElxi5HjvAJ2rZgfXwcRD5VhgdSDV4gN2aPV%2BLv%2FLQTIrO%2F8as%2BAg2YryA4G2BApfwyLVXxB7%2F8a6PjEOPirC7JmieU6v6qn5mIqcFY5zvSE03jLaD1LctpTzb%2Fy%2BgQb6rjetf%2BkGeth02Z1BVpJfW8iGcI80vGsjy2Hsnzz%2B1QC0B18nt7zUkR7TCQ62SN6DIunw0Mb8Wht9OcgcARdPOdgDKzDQm6JD2nQzZ8wOwsXKQfxhuKrHODHToEXFQLFjNCIl79rwTLFOR8%2FtchKL780XUSJDgn1YL5wBcrGsmdfr1RzVqtCyt72ss0AUhecVo7R6R0X8z4Xvh0FrpSlUsDGhqUCL0aDc7ldiWPj4nCA3V%2F5gdc1nOvMzaHeNwuyRniw%2FJNA9Sd5W%2Fjqbfydq%2F1jiUzfzonveodhdHcGm0z%2BB1SYthg%2F4xT4N6DqFR47jtUV%2FHvo3hx6DmZ7djt%2BmdY4u%2F8uun7hXxBaYrjTFL2%2F5ddqF%2F01S1FoEhJRkQNnMIPavQwL8DQLhJBeYNJZv8Ij7b0zq8DuiLem1Q6eZP9f%2BpK4lQeDUA2QxdUj43s1NQuNipOzmE5FkkEmfV1Js4AjxXRSh9qFDNfc64jZFHllJhz2W0%2FluKjbvGjCZkXzF4dN8l1N9BGm87VgOaLuxniFE5bXiaGRPjrDOD9KNfG%2FcPhSWgfb4z0keviaQCu5Ctz9zcDlB1HdbB%2FzG6niwV6gBnf1isaTrvxOICkTkRYmxXFTC56L75DXu1eYKaCjrjrpV0%2FzyED8Lv%2FLqxN%2F%2Bxs0AdLO%2BBIOdOi1V985Hn%2BGvQBcz%2By3kQ0jyLKIsu2t5jgXiFUBnnGFv%2FTP4h7xTwDV1P4p2ppRrqpZuXVIN3GsTU7kL98E%2FVoebxpTB0zVpg0qlN95e7RyZ9Vo8uLEMr2SjXxPG9bIDYsKOcKHszF3uCy%2FBEDoBUdYdqKG8E7QzRlnavwfV0ZckNa4l8vtPoVoPF0YDOklw6Iq1NonO7B3DOydA%2F0abDQl70v8GXr0R68NqEpbO2Mt39loZh0lh46K%2FzRrNS9n9zvSexWmhoLfb4mUXGK114Qc%2BPKk6QYsDLbtzVByqkfTdxrqt%2Fx7dHr79ilvOMOd32WPtWfhG64cGq2VdmE9yxu127wVWCtVmJu4e%2FT15IHq3zHBfmjujSj44kkxJW1xfA8a4qPQMtiTK3a5bCu8nnInLWey62I%2FqRoOn4BLCTwUZimpsMkzDZa%2FlXVQZu%2FmPdMiUAC7EEczMNSOent%2BFO79GNbNrNHmvZLW7uBwaURi%2Bw1U%2BemXHYH5I2kjjlh1YoeM6gsZcE4OZ3Gw8PCx%2BlIgorttHYm7sqwJKp%2Fw9P5QI6uZqDdwCKNdUVMQrMvpcFaZmA0inSrYB6DMWlLtOgC70rOQstFf%2FMItaXOTEJ5nx%2BPDHZgxd0KTYLQIjSzi8nbPbn%2B2fXCAKiVDLKlbGn0wanbDR9OzH9OrgeTON9xjDEHdI6UMf32KhTwMTwKwDrc%2BO%2FhiK4ehZrQ%2F%2BmsFFHeUp2WJ0cgwToB7wWItK%2F77x6V2jFL3CypwUlNiDPLyiUJpexLAh%2FnGla6nsxRjKIS%2Fs8anGRVO1WyxM7%2FkbP5YboGcXm8IpT3x6zV6GnvbTXqkK3JRiU3XMqfMQhYTAgfQUnEVmudgMSAslQE2jR5MGfzKfQ75oL32Vmb8nUompFMa5QEPs2ftLLi%2FCq0EkIGyWQ3OX8W%2FS2CEuB0j22KfvLlEk5rXNVLxV0xVdGBdTEuCGpOSr8HTwR4wnVXwXtk0wQBN8z7oCtixnYshFSxKyGZD6naBqvWLDzO8avDxiDdjrX0%2FOg9MLFmJUmlGS%2FTfnbMutQKWSH6Q29IsdjMrLjFnc8LYqVVtAoLyr13fw0svGBnfPsj7hVzEb9ZpjSIGZfHF8UmRQw3rfFgge79vnf6CLtd9rPhwO317eTsPcXz1nGq%2FimZxeQzz06YKgsDCzQ3y7qdhTARNacL0TKMJ%2F3znG3nd%2Bg2ca9hzGZm6wO5%2B0QW%2BugGKpSv8y1zmIoeBgdc5FIIfpKYqwU3R3AD3PVavGd%2F8qEPBYOL86tLjhD919Gv95P9k5GexvQA8QMwzxKfnmtlqnznZZvEllgytMuRNnRT1gyWBYotAvFp0scRsYa5wSdKIKKb0TnEJ3Y7B6MNWT8yt%2FQ0swzz0lmAAsvdcXKNMzVVG4yZMjNl9xpzSulBQ3mAVuDxDQOY4luDlXDwLzkaHK%2Fpdx24MdftSoL%2BViQGnzY6Wam50qHilq5n6qY5du7ZZu5MkDsun3ovT8gZuIbG8Oqpzq2C7o%2F23tO5ok5JVsf80s5wbeLIGCwvsqzA7vvefXD6r%2B7ot5MauJmI5edKsoBFIq85zMVMod7yagfGEJkkpmxELVYlyrRY2qVdnrSG40Xe1Dew0n1uS6545LIGPfMa0360RtwGSEgrhZ%2Fznr4xnM70MdZScJG%2BL0bmsZUpg%2BwSZuJ%2FODTQb1qFmEptc7Wr3xG%2Bo03%2FEyY6QWKieyFBAU9vEcOv7IDS3%2BKuKI24VtDzWAnFpcncfOas5aGEdw4KD%2BHDusixcYrZ8rTmL15y%2Fnwwdi9nWu%2FEOC3QxOj57o1ZIoPl59bHlubswaH%2B1NwEvuM7%2FF9QtAM0WehUIzu3Yp9fNFJYDAv3w7aVVBGb1apIdg8bkSqEezjV1qFxP%2F5yt89B1zUSXar%2F47huimOmWhTsWUkui20slKLJuQ8Wk1NFKH1upy%2FNqBf2E9Qg1HqTHEbjzUU2d9oKadWfeU9iSLV6YoupLDWdpr16hXf%2F6FSU%2FjT8cvQa8IhPVNwm%2FJ4YrLO3kS9lS5wuQEBMOcXHiOdPWi7eSQAGlPS8pFwlfFyEwcFwXawLk8aLmGl88b6yXTz8sQgDc2ztnIoA3Uam67%2BjN5yhkGOeAT7woLh0yJD%2BBjaFZ7aF6e%2FA1z9SHx3w8ISi9xsKoh9yeZSvXlitTLHyQkfQ52eAVzHjaPmFrHlY7DsVk3sZNXXrQbypimanVeC9Qgy6fPK%2FdaglPXX2ATo9jF8nv1wk%2B5AuGsERd91EwAOOH%2B9ojRO%2BHF4LiSHOXh%2FD2mlMkQDmWVLqOU6q9mAfeaG5qBei8sta4fHzS5Ng8MJuKPDsNcGHmj1l8%2FdBQD7JeZLgCgj3l8gxCzXB6exW5fgzDlxPlVdev6bU8Cg8IWrb1zn%2F4KQRoo7Vci8wVDuWiWWosNuIiVOTKXG97rPsWVz0LJd6J%2BmfE7tbBOHeAjVRxa2WIqVPc2j0QZtxFYLY%2FCJPGSIh6N2UIA8z6wAdSfAm0jyFKCdRHYPDXEIgld0ToERveVUw3CCQkzaX26Nxin0EoYvqE7SBS8OxTYt8QVGIY0Jjr6QmW8iZwoIc1z9L2k9QC3aWWf6v0VIzLKyd6dRubbvCH7ZipkYRKCzsUC6V65ffY5y3%2FTxgzgo2avp9OIybDMCv0rYqitW7oHLJRa24EoG0KMAoWDsEyGXv3maPOonTtNq3084aJYyomLzzAY6scyiSylvhIQcPUmNqpVxIgRW56ISOqk2gaquw2o4OQuRuDMED1BrplAmzauwAhmy%2BW2zF4IF1QUxvPmvUZuxDO%2FaSUSHVHVba6laXbSu6kd%2FwAHLGvPzgf57X%2Bz7xBNMoiqJGUFghxNkTAoFqs0QfbM9PYSoS1zHPOBZznH6j%2FLcrQqQ9el78N8kZ8hr72cRfTUYusmVIN6yi2srWuXjOnslvRWr1Eb%2FNMnBejIqKMlKoEX9%2FsheypHsV%2FmmR03l%2BEBUHAD57ItWaBMX%2F2KBQrQeOExqUInuuKkBq3HnVGeO0va2%2FRLscwtXLUhTAy9XTHcTDXc2gNEQz4bcGy%2BUrBQic8iQ9U90vk9ZT2OopTOd3AkjO5jqrBqODniGdmXZ0q5D66nXrAsg1NWNFWECEHVmsJRUWp%2BdL78zGJetwlU1hut7wBdlPAAp9%2Bo2uIEREoGB57v78o9dERCxXW8sG8skhfa6%2Bk69MC6docKUz4GfytwTN58tP%2BUr0WXxlUcO5ShK%2FiqoOqgTBSPZOaSPCDEUDCj4QvV4jxim2%2F9BAc3oDtOGFjbw8P3C4IHEnbx5cDocATR%2FNQuAIYOhNOUWzWGf9kjdbSq09XfaubiIfML7TB5pUoBCbQezcKuo6pG9%2BLK9H36iGk1EfzbddAMb%2BBZu0Fs9VlFNy71entTdKf7Xzq9nY1ysPkM94oqv9Ng1bkIUUXOAn4exiXoGhtNgJ%2B3dayA5ounXIqKsmS00izKBnhZErlppvuW9zPNN70JBwkGOFsOdEFUex8xUs%2FDo0aFN%2BkfjzdJ5GWobazpfeYv%2BV2vL292nprkdqYO2hmL3OihWDAaHxBk8m2n%2FcGqkqT8yGz%2FetflXonng6Y6XXg%2Fou7QASXzB2DcdJ3MRzggWxw5TKQq8We7YLI8dYB8VYdVAARkh7JR0p3L35sUeZTXKIkefYxzZRqCzmWJxsmFAEArdrg1ybKJwqW9QE0b6jEpOF6S6yy3uJcU%2FuCzK2FbQLTxbqFi%2FMEvx8%2BXbgbKTfVRq17B0atLCx8fiPBYDYwr98g0aSRE2sV8a3OEVx2u7JtmW9uce7YfELJj0fmXMZgZHPY7SUpAtw9wRRc18y70iwqp6eLEZrzs5Velic7%2BbE%2F%2BIY5H%2By%2B1VRi%2FmmT1LxH9csqx1faCDw%2FDZ5NsO2cIbq8L7G5l5Spr93ciNpxMBmPTvDPggpih6iEL3%2F6r0fZAUbAFI7%2Bsfzf9AM3uNSzDSo4bYgeGiHvvWLJmmN9HnmRQwYB9XbZie3EHyNohXmE9kraA%2FQpbCVfL7gQSimSMrrHxKIR62ie%2BUnfhM5t8J1OgoOBaYBWCvkr3TFpf%2BEx2sAnJOSmAkcniov9TqTG71xWPrMMq%2B0c2ZJnUsoepvHntbjBAskqvfRh1WyYqlAvEZpqja%2BDbCu9XehgvTDHHn%2BIBY6TnOj8pkzrxxvvhIzHsfaAZ1c68W3Ptxt34TZcuzrW9GkYp0L8ho32xfyp7uvOBQLn8QkfKgoKqZ%2Fjfhjc2pmOwiSmMT5wyvnvyFsT4lBZ1LAB8SLrCgDehYTbKVZnfmXzrgq0vpr2yn4V5BmP%2F60DmcG3KSvS3c5uuofwhRBUH%2FHCmd5%2FK9zbri0szOeQ7X374sS%2FnD7i%2BySxcSKQXvqpMP9rmzzfK402MWQfQJ0lVdl%2BGVGhRk8G50OyNVM9tRMzSm%2F54rSYRvoFA4mUh%2B%2FOLk5xQZONDGxWmr4xcUrVhGywsFTgKZDWBcURROpnb2ENpUe7d9xsoTNc9HSzyo1qYwJ7U81odAtsU%2BZdiEcs0UICu8w6BWtGypSy5d%2FG6oG%2FkVYHijsHReH%2FxNfQiFij7PkCUjmJQhyqN%2FQrMIQch1Bg8y7R6idVIIO%2F6lMoJvVuRLjn6l7pV8ZWEDmpMavBp4jjJzBQQoqqK4LdPvoSzW4CxrkEZhs%2BvNGM6AFnnWbx7IDVJGmsxPutPvLXGezuKsBA2FhI16q%2Be4IxcuJpgexxySJqg0JwbQsWbjSPExCiutTOybRBLyffsFwET4ZR0Z%2ByrlvZIkD%2FuecSANYB5v8QwXTojc8v2rPBRR4EmruXl4Rf1RTi3%2FXIeQ6Cak%2FjoVKrnXrjdplhD4EQ5hl40aptVXrC2sM8oeVf3vf2BdtlTfXAnZ00Ga%2BVQKYnn0IipqQImvREp9X4vaBzWPa6m7XJIV0t3mLOHnMfojNqpEsB1RCJKg3%2Fh08a%2B9wpRRsVNRvhSNUQWStmP0sKPmkO6%2BSu7bqQx4eSzx5wAkFuo6%2FiG3Mw%2BfS%2BI5t%2B8br2aYVI7IhTbFutKxxAlndWWeSRWqHN9tCpwyTwKsLMb3iaaDer4i5nP7K7j0qiPqkrlRACOIgJ55ZpjtO0L%2BkrMqCgq5ewSXXRlIPHL9BhdkURd%2F0V3czLw%2BuWkHdatdPdx3R6PnpvudGHjBEE3xvEwMIBsvkctoAgW4tf31MLHtjEQk%2FW7%2FZgpyO07hVu9UMhOPuqaLqwySA6w9phififY9hp%2BBSfUnUA5aKH8Yac1DsCoZoGsAoh4z4772%2FTF12OEqG%2FsujNVSOUrYHCJfZYtsJXZpUqibXixpD64kU7tOFoOb%2F6YjrrD90GztdE%2B%2F9jtyYEM5%2Bo9YuLoPHo4AVuHxqztjuO6%2FQQqWtYTz54iwfyV9RtFILhRBwtlDIdJCndV3aVnPAxN%2Bo1LPRb4dQRIf97GfQMoIcOfus%2FJhMoSwXSSj3YlQzzrObUTrjIvNUP7RuXTBQYv%2Byksa05WkSqpB4tpMl%2FRtCd%2FkMMt0UWFBu8mYh5n%2FeqMsT4e2hv%2BUPJpR1fHuDXcbg7LbWOPyuVjpN7Y%2B4P8ygvn0iNeoZbl4ppQJX6A86mVVZX50MTzYvmuqjL69VfoqvQV%2F2k4iDGvAEBULlq3XxUo7kQ8TWuV%2BZUj4pz6yVc88mTr5Gy5HPFBMiy4bLZF7n0wmzPbWHAvvSfkppzVQAGvx4V5KJxA6vkG7mhaJEn0g0udx%2FctOrK7s6uP3Zp7z728lioJ5403VDGHYsbrjNg4c%2FL1zN1Npk3tTu9d%2BSCeWM3jDCas6SzXBLtrBVtC8i2nzqq5h6D2a85pBsncj%2FNRgcTSieQQWBJWwJceUk1H6PpIc2%2BKuZh3boxX431tsaI3uVYB2NiNR42spJp%2BHM42pApJsg4omwuxitb7bF2%2F46WM92KR5xVXZhqC6jeQpweoU7BsJ%2BWF889ihG9CpR4KZYou0E9CEaIDwCNKy1casJ6BivjflEBbHJf0cCl%2FV1EPWoehF0QckXJkIL6c3kmlfOXQwydnwL%2B5vpBLnDS1mEuSZqEFZwEpVZu8OTV8rH532Sr5LZFeOikr%2Fn3z31fJm%2F2K0jqlrQFL%2FyAdhwcSeauoBmMXPqJQ3NHD3PdgCL4oL8r6Zl75i08xpmFMQgJbltZjsZu61RrnYrGbtgpAbkDKFoV%2BtXVKj2jASRUKptyk6MCQMN%2BJyM6kQjB%2B8kMTlWaIMtsS7pcvG92bqpJ%2F5uH53B%2BJd5W0KHcW%2FfK2VVWtW%2FdjRUK6u8CkQJWst2Tbgmt1ObyjKxpLO%2BxVEHpaAYACrDrDRqpjTpcysTjs5LHh4y4niVbeseOUYnw1kZcquQc7MBSl7Hh%2B8%2FgVN%2FvZpui79SeAnXdtJqK7X63mDKUZrVMkvifPhDfnRB4BPrGevA0FVsaLxPuz3fl9GVUc2EXY1FaFM6wlxlsTVYTGGKxHCeaFxOPmzrqD6YQj5L%2FULF2BL6vLtTAmV7BiYewBnmiljN%2BFkDOWOMPX3Nw%2FyeNgkR%2Feiv%2Fa0%2Fx73ir2sGVgan31yqdB0pMzQW7usZLxrx5ZuTDJzhhy5eek0%2BLMLhtav5n2yH8%2Fe2s455esbj%2B6KBB2bGAJM%2BJedPSBk2NyR%2FoC%2F64%2FwfMKL1ez%2BSzXy3gsHdDPG%2B2K4TtjQAhPIC%2BZS5oEhh6MZZ44QkrWTnRhv8joFOlBsXz06TbxxICjd7%2Fdzb7H%2BVmt9dte6vXmK5InghZ6VgApAEezYFJng9w6Ha2mvZWvAvShrZzM152nNRhT0be2SP3FB2AZJSd8ebNMCn00rPV4ZFLnq3TiPVaPUXsu73eZnPvo%2FT2N3ps%2B4iwO1H1MSHq9H8uXyY8BZjLQhd9kXCjjt%2FBpNfMhwjEQaoJ0F%2B1rHCs8Q51Pad4VnozLzf7rvWwfT8n7WojGb5%2FhYhfhpvFMwAN4b8%2F7Wjyj11p8heLG7bL8YxqGO3wVcTtutHs0IX5E55abzXKNBt6mUcpfYkl3mS5gFJ47O4m8Y%2BMFxOmX4lznPO6aEQiB9%2BaxLxhTZgG1IgQwj5ilWVTxOgQ8Sx7unCbfQG1g4nkqYQaZh%2BhgqWo5tJls09pSPLqWl2S48AFA2GIzZ8Y7LGcZrlO5aR1SDV0uvEcJ7ehzuvwXfIKkJAtXsMAnTnzEdEAHd4lkSvJ5TTF94PiXPqPKTEacbztTmyz4AbP6FUzr5iLvtNWhRZkUt5fOfunMpPHqRz91fw6BttOBIj8O%2FLJkiZvZmOwl81Hr6doBjZr%2BUtcsFmPo3bicx4iA1vg6vzLjLehNA7pSPUIzCBzaTVQLVi%2Bd6nSrL0Me82vrCpRPGl2J2JddTjIVpM0vsdWwTGk8iiQ1uYwIEpMKYgk6SoqBazMcQk1iMTW%2F3rnklE3SnPdjI4CIMhfd9l%2BP%2BpU31fpacUQwwXkAe9pDkMQ1s2I1ptvVVE6q1nhkpckmWwva94F306ibR%2Faok%2F6yHOMy3ojDl2SfpWLltD4OS82HeGZg6Dt%2Fgw6mBWGXCslRkeCT4c9n%2BKYfDguicVHsTU7bwBgaRHRwh%2FIBfJ1wsgsJkVj6X5Yia4s282ZizqiCZ3jcUY4rEPuXyiSIiXqT083JHCgQEAJEqeIlBNs0KFvmsy5O5X1u6gObCSsPkIkYzwZ66N2j8NxjCbYU2ZFVG20membLSOiJ10%2FPeh%2Flo8TzHcQQ5N2wCx%2BLul9gMg%2Bz0ZFlihoCerSKRadfQcsSBbzlHV8gdsdN7vlpYnzJy6SQC7MBqXvsAuZLovpHyXIaeeoFzdD8AY7CJAyjA0qUshlSFOR261UWdNt%2B%2FujfVAiHzFPy1uw4wspnlfyO0U1C9NoDRSbkkWvruvGuXwn39gimQHxWe%2FJIg1p4v9qx0iXIicWIWiCnzevuPTJUluy212zGZ4A8Htjm4ww2puuIl1luW8qX3klxsyePpZDMRGsQUnh3AjmY%2B%2FV0lYLTSZtKFrfbjvu6bmGUfbcgHpioIp7hOdE4EOTeQiQLNroN3lI4va2VEnqDp98dTTVl%2F3Vh8yOi7I46O3NQWGxbofKrGUO73rv30gOrzYdTt5FMfbiXhIr7Mlyu39TJvBNV%2BNH7XApJcMrvvrC5aO5FRio6%2FtrzpDXrzOBfbwx9enihvz2Hv3wbYsWvmhsYsAYnwFCRrwlnQo8ZPFHGY%2FhwRvCMgriZp%2Bi%2BVL%2FB9s%2B8isnuWhgAlvqCgOgrW1BZTpxe1pgguIG53S5XmMvoDzU00hxSP583mBEk3H%2B8DuiAxukT00uIiNs8HSKrbpecG%2FJ8GfbSRvhFK6DlDTwncBY5%2Fsq4OXaiTOSzGKdpVGmCxASoXEanCpCQiH%2B0Eec%2FPgj4NnSv7VS6NSp0JQIcRzhOOx9cn%2BvyUGmLclOORc2XowLK7ZvOC6zlHJhIMoTJSgi%2FguahLV9GXdq%2FxD38OI826Y5BhWbpUh2kA1Ufw8Cig0DNcvwBQIroFOcDwEpwRoOtfdax1qplJJ2v%2FY4sXZv4VhfG8PMttQgVN0OnoLw2wW8Cw0DLeUaRi5fao725Pz%2FL7gfZVU%2F8xR73Z60WHwa%2B2pF6mTW6poaRbl%2FvxQqfxECz3exNk%2FBbMuaOyUMi6VcT6OBItbWCD%2FqZzl6JV6R1LpnHU6vZecG3Bk1Z3uQiCIMDtO50IOKC0DzFMGnoET8YTtN9qn3vGiMEHO8mgirLJLfINOpmQT2G1yBshXjLR3zDXTRa1lBJm%2BOzz5vX4QCxM0zT%2FtlpCS3%2BEqdZi2JOtCEBAQLSA5Pl2q7nsGTk5zLOVTg%2F9wvyJ0gIh%2FvWfYB8w73a88gJkwTyhlr7jKhV9gdy7djn5QfvzBdgsWS%2Bp52N321ZTcpfx%2B9Y5Au80NhnK3CC2xeY%2BfCfuAJqtr516DATgguICr8Xx2sNIJp4%2Bsq24uTadxox5X4WgbJjoWi5JOmR0GvgvJKsB2OeCVa%2BguPluJMFCK53OhSq58L6K0qEmPrYg3xGRcLJwr5t6FlCkrEVNS5LKRsxB2rLNlvh0H1uZhdcMzY24oe6u6ywLUQkes3zTUjy7RUveuD9hFTK3b3aKTd9MExzt6baQ%2BViEEfTNfPFn4hT6sPepgmGeBbTuKY%2FNA07pgLzrAyZpkcqG9WgVsI9kytD%2BEmDYmLsvRNHQcmvwafvPXnN1hhxJgIKH08AGBZGpNGNuDN5Z%2B9Nb6VYq3bkZr2pm5f0koliDVq%2F32bmuexHta8Lt%2FoGqeSrCe2skk6NfgxnPovL61KUyyQtogqSiMHLWastcbFb4s1OKSmIAnE3ygJKdB8xLEDazK8V0EhmdCek31izdZtbqJ%2BVcfOHnH%2BQoGmrIIhUxyhYgu9KyZiFqYI%2BXP8HQgyCWCY4V6Ox%2Fl5SXzNKVg9K2V8RupDWp9hLg0Pfd%2BxnqPwOQWl%2B1uSuZ%2FpGqh7k1hJjaww303qlzBVxGCRkU%2Bw2iRvhW0eUgTusuWVgCJxvC5UXbjp2H%2B%2FYrk%2F3Cr61RZmQKCNYj6tbAtytX9h8WWpW2tHr9WUcQngTjD7osdVN0zcCZg3FuQFo09xYoYi7P%2BYJMNTHrHbEqtNvo%2FJMtzxTp%2B8%2FT7KBiH0Hw7JQaacb5gKUbkg7OrcqfMMwxPvPmo06%2Ff15Z99yILD2Ovu%2F4oR9Do6rFepU6o333kj8FZZpbr7pQFTpOXBfQ558FdOPElNH0I%2FMyYzUe9%2Bzv%2FuoaSryehFCDvI1%2BvR3dAXDP%2Bpp6qw6NurywqjM4gfhexDqCrJOIVKiuysEJQrLhXRmR%2BeVmTmnmeBq1ru9jo5gNLCVqrEtX9ZW5h3ddpNFpULmVBjpTXSFApI8xGaIy6T8DDnZmF7bEQhFubYq7QSHDsAnuJqq%2FgmnWQkUTp0fdn%2F7jtFF8DW0uS6kM%2BIH4Wv5na1sXmaQdHXGn%2BfaYbbkSqIDGVspRF8k3tQ77nXo2ux84Oc51yyCQu2SCCzLZw6dIwEeQ8R0NwFIKbVHOJePIoaKr5CvSAvTfVx60hkK6lCiEafOdoZmNAk0HAREgyhBYvbWh3sjfd0oR286TobVgKPVjw%2FGgyZOs3%2B7nwhmZwrkVzaYfdjwdGqN2ryO8KOy9KiuvZvLESzm%2Fs7SkKQGWjSAQNi4Tir%2BftAlJ%2FXpNR%2F85rfvqO8o6EoJQjlMWa0rzZJ0EEMW7PFZpbzxW2fd80a%2FCljufucxRijMLPlD7HdX9Z1W36pk9ZEsoFdEwaZ67obbuKpxrNoqQoEQe0yjin%2F4l%2Bk0W%2F7Mbw2kbAoGTm3n2Hy705fN2EUVGzupqvMjRM3qDiFFSqv%2BVj4OcLCx4bjHy8Twioq9pIctVuSE1blW8Ahzr%2FZV2eywnGEQTNjL7axXN%2FZZeKOotqzcbnsmO0Wh%2B94ETgZj%2FXGZ1ja5g%2FQjVm%2BcToSfVeuONxri4Us5B4oSHgHTdXWhOY4T%2BVfn2g1L%2Fao%2FKcozbrVu0ioFVXaRfpSIDSBPTt8c9%2BZ53uOkB232ScV9KMxFwKExrFEo8f32Jnys79Wdv63KO1ZRlPab0TQ%2Foy5DdW3qCllUGWhqtyI63vxEM3owb%2BX9SMqZll%2BnTXujrl3gKB6vR7%2BNbkNJWAAQPGMuKKIvoV4AjcGAH74V3MbZrI4DB4WImezgG9mwu3Zyr8B6d7eufr%2BhcjL9PvnLQ3JlXB%2BqZHx%2F%2FKHSXNO7EJQ4SYaqqgWESSFj6MR3YQTRLUEsyYdZWUR9dtpntuDcDM4s0N60nT4DqAb1z9wtqAHjvAJpN9m5SVAvp8b5sN7JctC93xVUD0xWyoPzms6H0wsMiFHs97bNyupfyBTASSVjB9cPBPfIQ7eSBiRIKuYW67kgMLpeduezeX0saxkQuURw4YdFwMmENcSgltYVm876UwfhIo9s8fiAa55Qfei16atPzqaI8WgUtsKiLq1cIEIAmS%2BavdLtd8I62evimbWpe0CPIvl4xQFb%2FFbaW%2FGKXh4kT1PrlTlwrVkTWJboPafvYoFfVGy2L2ymGwn9htzJyDPLpa8Yr%2FJ7LXF8TR07eJAK4p%2BpGK9j9Fkefut5XhbCqj%2B5%2Bde39rLyWOsTRzAomyfIKAA0qCPf5YxzrNYFvn%2Bfu8f%2BUrcR%2FiBEVHBqGOlfd7rCnFVZnPygxgfLo4%2FFe5QxtGfzmp2gBUHgv6YxmrN%2B%2FW9NKP8fKNed72zoshUEuqB%2FvoBD0L9ApBB86%2FprQmHkX%2Fhfy1Gla%2FnXStP%2Fgv65e5lVRfnP3Unkry1a%2Fv4v%2Fl8PwE789QvW1sllbfvvx%2Fj9jUBV%2Bved4T%2FdK8iS7iW%2BfLrruqP3p%2F8EJfB%2Bbxe1W%2FZ33V%2FDsl7tPw3Lw8cT8HzQMxjl2j0dvODnz2gZswQ8YF6d2dMJu6zz0GTc0A7z09oPfQYayygdjn%2B%2BHbVV0T9%2FJ8%2B4Zc9FLBjVKola5p8P1mF8WvOqbf99m%2F9AUJSlXgL8d68RPFR3FnM0lv8qkhH5V5FF4E750K9OdYOPYez%2FZtYoGPr%2Fpwz5nxNGQf9ztv7d9r%2BYLRAVGIb1v332Bm%2BoDWkGrvgv%3C%2Fdiagram%3E%3C%2Fmxfile%3Ek;/ IDATx^ xE߄pr# "7*" "*xc odUPnA@PP@+ 9H[0L=5d}SWU\vvv6TD@D@D@D@"N`ǎطo_U"& *s -" Nb^ׁxC`hذ7UpkZC@b^xD@#jU]ūE ļ.DGլx]oڴ 5kDӦM]mK@,e" " " w@@̳38åVTļļGլ$]ūE ļ.ļGլ$]ūE@b^׀xM@bk}7H̻AUuU!" " " u 6:vxL[ڇU<HǐU?d3Eu;YļDGլ$]ūE ļ.ļGլBڇU! 1#V7E@D@D@G@b>E'gKڇUC]߫" " " j`U"p y]" " " "yYW H̻W@y] " " " "yYW H̻WļļPnwc (2BD@D@D@<" 1x5*yWrPd^׀xM@bk}7H̻AUu"D@D@D@D7$}  ya*(H{^ͺJ@bU\rHbH{^ͺJ@bU\$u $wļTUK@y]" " " "yYW H̻W"D@D@D@Dk^{@A@b S5 " " " !I1|0/}d?;QʖC& 3YEH@b^ׁDGZcDJSZ*6NQWw}J3Yl'y;&E@D@D@@_a W'چ*ļ~߇"! )!4ihTr7eX۶mQT)TZ't4hs~M6GDlܹs'{=s9G=lDbCj:HǔY?HJ-}KE@#K.ŢEPzu4ie˖EJJ ֮][e˖ܹ9\b>2 g"D@D@D@D#nyxHoQDD?'iӦ83iʕ+qvEH"-OIq-]A()#QfΜ]v+@BB1Mfffg˱zj߿QfMtUT13ǩ _"o;w.6n[N4k }L}$!@@b>>XIM1?{y&| }pV_|7ƙgR[y… ԩիg3w^\r%(SLb2BO7/8~0uirA"|PD:@D@D@D@!Vtç(Ldxndh۶J-WШQS|O6D[ngTu5oYx\:F% 1.1/" " " pKO6;deѪq{ThӦ ڵk1}tg܅œk><Ϙ1ݻwlj'SU`Mļ|H0 Ḣ LS3*輗l8L _xp ѣGHM'>c{_Q32_B;$1z2% %%C@uI@b>L`:\D@D@D@"AwyS&;5=UGa'3gcۡTwf852?uTԭ[׼$((އ~g |7l0dWcwКyn Uc$c8-7Ũ|:\CFټy3ѾAFHμybŊ|;tY~zw;c־s 6/4 *Wls^/(L]ZBǎsh_/1ݠ:c|,z]}<~*&஋Kfҏ? \呒kך95E6Kl/[dT{&c6{Oh?O>dwlSU]`k?ߵkWwqfO?={(^K̻v;# 1cWwE@D@D@CI1OO1}[\҈wn/WT)ԨQ-Z0J^1ϿskUV^dI?<33(k֬ҥK/WN=T#w܉0}7md~UIxy߻/>K?dļ=$D@D@D@b~ȄcļO"aB!OA`%RO@b USb~ L(-,njY[LB͒xc>=' '7 g*˃P q!L9gHL<ÇMdļ_=#D@D@D@b[ Vt{^؆'gU0b{PLȂI\[ڵbE<ƌ%J 5՟KF,rL1duQD@D@DӾMǴ9aw0\1·&+OG3Ep+A ѣGcذaV]F@$ HG\6qh8pWKeo^Jkmy&|{kTZ;v찭˲W\1h|Z%ѯsB<)Tq%b*Gs :K4g׸3*w3" " ^C}GЗIsC˸-ݻ?b֭HIIA||<ʗ/orr)w(Xk\KIǒW uQՀSbPݢLbUI+R҂!- ta>ժUC-PBdddS/_gϞٴ4L:LVdIMQ}GG#H UIb~" " QB@mQuI1SR}oy'燌V ]_1`$$$e²e@{FҥO-泲rH HBPct," "=6kvR̓Qynn [V+ pUWDnpɒ%@Wzev`f6_ԬY:t/f[~m')/rʕ+͹3st[zvڅ۶mCbb"5k+:TgOػw9nݺԩʔ) ;7+wR# 1AD@D@$ 1 i1OdWVXk[jeK8oܸ};8mu9&B9EŋqW .4h?xK.1Bzݺuꫯ?QR%^@wLg?O߈Caڴi&駛.]j=۸꫍ۢEЮ];4nɘ;wypE"^:Vb^b>RZ#1W/" "(yGqF}en} AРjh}H$ЮU5jdDwPϟ??g=+b dTnL՟5kVmV\9 F}]{F^6s9hР9w={gDv8s1czalHKtlļޓ" " # 1; 7<;|﫩i㡰?^"F6 df߲ef:<3"ΒWo߾ӧOǙg&M,uڵ}WXaS3 O;u_yGM<_I{s_~DvZ,#1/1c Kǘ]w H̻7jwKoۛ!R)A?ex-Ԭ|tR"T:kךl'p1b?(zfgf|& /8g=]`۷7Qwf?p}9SsJ;c&{Rg:F /3S3q1!_Psk:F>k毹暜r3U6mt/Tq" " "=IgLLlSx'3#YYPr ti^mtE X] nƅtDH| MF8"-ñMNJ@Q >/j~=O_=c]P=="1odb`ݠOиsYlļΓ" " #6I;`]q.y}| LI21lzn'h\,6ObbtC|g$VTzϋڮ_ӸWoļ>TD@D@|D@m>rH[$6`b=a74.A'1od>E~l+*`b=Emׯi\gKb~" " >"69S$-pL @0 | ͓y2]D@D?"y?{G@04.3%1oCa)8I&M Xy  >wI[<." "?zhOlļ#ۊJ XyQy풘߇Gΰy $&L<}~;b$-vL='~HbޑmE%L< ~vIC@D@DG#gX`ļNa&փ}v>?Ady;O?[$1gȶ&փ}^vzz~$z " "#zh3,0Eb'İ >AqD|dyG{k|l]vļLU@ auyJ H{%$đ?Hb|xRM@b^׃8C@bED@D@ y]N>NT]~!lz"z5!" " C0UU"@~[ާcW\[H 2AD@D zHG/y?yC8I@bI+Hǚ_W H̻7f+/hGƅh#1_( (p " и LUuy]" " " =9SU@иNi$}%" E!믿OĖ-[鋑,=c \{`Hu'e]zszhsh\]V+1odmn݊ZjaѢEh׮#6&%杦D@D@J@bޯ.y}|ΑE+D@D@% 1(NUļ.p@(bgq5jk4!C`iI&As?p@|W\uUNq뭷رc17tz;7nz7`/̶`m~nUbEwy3f *T`bW_#<~ 5jwalg ql۶zxM6lMRJǃ>'xB]U@$ueE@b-WD@¦ٿG}\`ڶm+7o[o$\s5HKK̙3 ߋ.od#+?3\xFR8sq%7l/2.-X;vDΝ#55Fa  5k{ 0 /9z)cw˖-̋{ _}j׮ /5-E@D@"M@b>c=z*"`y&M`ܸq91ޥKI޷ IDATo;#,nfgtE@^%f{ʕ9k׮F>[mG.M6Vbb/(7 {((ޡCL/AbVj0`ʖ-g}ݻw_$<泝;wZjfƁļ7' 1ݗw`biӦf8j0bDʕ+1rHXˆw pFك?Ҿj*fqZ*P{i:g3<ӴYmO?tmsvW\a,[@1mϿؽ{7h#_N0;<_X0"(|1@>3?_FP Eĉ%uKD|đL1juTD yN@zwyǬZsN'g_~=5j't9}ҥhӦMbi(|7o:uꔯ/6:nᨶr/l/hpm<ճOw@_|8p@b>;ok{9y/n)" 1N@b>//1"\U-" |>}Lһ;t耳:D)L׮]ka89˗770͞lF f/6QXۜ5tLwI͜^(ж-Zm/% `ʔ)(QIPnjf*T?&13u]gf ?oYd~Cbރ@M@ K̻WU@@Gb8L;FmtyF{aNa#Fi0Oo-=… ͔|Kћ;"h6<+6m jkj [1}ay~&cT%n; de l[Qs+>Ng>&E@D.MJ-*" "{zhdDƅ⎩$c6a/" vи`lVb&oVC]$E@D 4.DwL5&1SVgE@D@&6 ~e6yK|"(" %q!c1r:+" "6=MX]4./[UDmʕ9s56emf^NnWreX/Em~и?DUIDs~)))xp;ƅ_{HLL Z[b>33sEQl٠vD=E;h\O6Z)1od 8):%JϳfYxquP0'$$u_TT vBr閘pç衭tD% QV_tJbn" xG1~xݻݺuäIP~}#׬YSN9|ѣ;v!C0{lP0vi `߾}X"NÇ@AꫯGo5j;ޚ.?[֮]>}6J.EᤓN„ O;4izwM6+/iӦ>}zNiiifj{jj?S_ԳN߸qc5 }5<|M̛7UT)FݯJ|f:}FFx \z=&BZ ͚5n |a˾3*T~h8_wu][.%"Ӊ" "4.D[})y_AFHǍ7h'aÆyFS _2PDXt7o[o$\s5P{ 2esO?ڵkc)Jf޽M={w}gDla;LD? <شE[>S#));vh2_.PR.˖-é>wd>wF9]ԩ|Јlaru^:^x7n0{bSُ;t耳: LG(׭[odזɓ v @Ydy)>2ϒlfTV qSD@$ 1olZb/F_`{s*yVp]w᧟~2 s<3ZMεBuLu݁<ײ&33?llygD AK.1v3QT3!בd|| oРAfIg 05-Z8*os=pl)H̓_sr/+Lb^"p&zyy_^2JD@|ԸwKdMsoM)ygr#F\p&{f8en;2Oϗ|Q2םS~f8nSW-HnB8şk̙YYYիɤϗ Dwsf3",y/ǖ%mڬpи11א|̹\pܤE@D>-K))" "`=Y&)" #q!bc!s:," "&=IWu}4.3[,SSD@D zhM2RD@"F@BP\C1ruXD@DMzhsh\gX,1odfdDƅ$c&]-" и`lXbON+ 7H Cs Iǜa7 M[D@#q>bļ-" " VCn" "1":9" " nCtUG@}>by[<%;E@D@ 6+$#E@D b4.D u5$1s.WE@D@$67n|fxJvXA@mVIF@h\kHb>\I@mnU" "` %mڬpи11א|̹\pܤE@D>-K))" "`=Y&)" #q!bc!s:," "&=IWu}4.3[,SSD@D zhM2RD@"F@BP\C1ruXD@DMzhsh\gX,1odfdDƅ$c&]-" и`lXbON+ 7H Cs Iǜa7 M[D@#q>bļ-" " VCn" "1":9" " nCtUG@}>by[<%;E@D@ 6+$#E@D b4.D u5$1s.WE@D@$67n|fxJvXA@mVIF@h\kHb>\I@mnU" "` %mڬpӸpm?ロo[nkCEc}Xr%fΜ15tW\r4iRP{r7< BmJb>TR:ND@D@B ে!" " .pj\x뭷pXzԩ>}`Ѩ\rО44o޼z%b7ḢIGgzhjXD@DQN ƚ5k}2?6 'tv#VvHİIGgzhjXD@DQN >}:d{;T@Qn];L?/"ʗ/oS4~ƍ2d{dggNɓqǛsRSNM=_-_i߾=.2|X|9V ͞=ۼ2e >ls:ۦl;)) {.bŊg}>(ك *L0a|IرM4C=;浕dzϴ)33S4h`Xs*|`yfӏW^y-Z.'.2Mw#zhP ԸPOpOaL̿K9rs5~F$$/R)S'NDVV]v|1 7x#XtyKNw^#Wݻ;c~u|jjyqQR%̘1+V06x7xzd \z>S/E7,f…^cܹ3on",_|zm"O=siժUN"vx3Pp7ߌ[y .|ӪUP%"zD@D@D@b^׀@Ny&pgabF_xԮ];$1OgX9`">S3 }Ygq q @aԛ끑yo|FQ=o<|w837|D)eݺu&6/1Zj&2ߴiS/0p@˅yfgŧ-d<_"" .4/ X^{5so뮻PI$"zD@D@D@b^׀bSG_PswkךiBaᜢ^tNilQ{DnMLs<<# y<m۶-^f?~Ǎ6mZ|PV-#ni+YPd6k'cN狏b~ѢEh׮1bfS7ļS$UHpQ/wsBϨpnq.]r<2\Ns _eַ ۡ`w7<2m=͞3gD5}GMo֬S͙ „z\_`d~ӦMW/&X>3 1O&M"1I#" " D@D@<B'cɒ%8SMS-Ao5 <3<QL$ID٦MfBSׯof0sM:{ɖHޝ\ vZ,*U |ٱxbܹ&Z_Pd>]N$NT=" " " 1k@D@DC1Ϧ)f)ؙ9dɒmr ؍[q7q&;=KAyVl2C7nzfL9g43q_1\Zy/\f\ϩގ/ eg6pk|(v9qH;AQuN%:P q!:^H+ID@DZzhu2\D@\!qOED@D@Dzhs*!q!j\黎H%2HD@Dfzh{]D@'qy0y] " " " =9SU@иNi$}%" "`'=7Y-" nиY+1k@D@D@$6a*n m Ȋԅn }"_I۾rXbZhhŭLbu"C@GqOb>%%];gy^{-閘ܹsѾ}{-[6<@b>Ֆ@p-ᩃ" "B%Yf,^8:(:/*U]v\rAtKmpC9/;EbXtM͇6£>c޽֭&Mfr)sFGm~ޱc ٳgN36hCŊ1uT > W_}<~7ԨQwqnٲvZǴQti,Z't&L'|ѤICojݺu.hb" " " 1k@D@D`>nF#OM[O?5bBcǎF(E-{l2zؿs-<G}dl!. ,@۶mӦM3/ (6l`t [nn^,L8{/vi^^3rW_~ofO;w?l^uU/lϗd0rHPvoIGD@D@$P܇6MQU" " > Pq͛H1 EW_}e,3fիszZn] ߿02{o6u0(:jm6ԪU+-[s/FsyF)! (饗R^1s7n\iХK& K϶XBf7_52$x|%|qеkW! 2UDw#&" " E"hh IDAT*#y #˗/75PfQ+W gDyFb(Y7w ھe}@ =z^zf}=>>wd>wF9]ԩ|Јlaru^:^x6n.`p<ʳwgu1QЯ[~-ɓ'@6dR}dT%99V$VoD@D@@qڢ$" 1MkS[j ?IVXdkŝjtz&cb7dD湖6ѝg3=fs=gD?#LX \rI$/y͆>F 81.0;:3ޚS \3r#F0S/M\Vq>-wd"/e;6pܦ [fݬ繅gp?ט3m $ &泲ЫW/I/8NgD3XnM_|1A-yr[_NxnO(^b>{FG'xhp5*" "  `Uǐ,=D@D@D1zhs * q!*NH-2JD@DVzhs[D@!qUy]" " "(=9S4.XBv@yߺFH@m6zM6{4.6k+@pũD@Dzwo; 1[0 Ffpz~" " C8UXO@.m$}&" "`#=5," иXYb>֯_D@DQzhs* h\ޅļo]#D@D@l$6&E@D=c5K8J@mTe" "`= ֻзkdfdG@{lcfXG QLD@'qzu llh\pm,1W/" "(=9S4.XBv@b޷a" " 6C^" " %c PE@D@%6Gq2]H52LD@Dcƌȑ#裏bذa<7w}7z! >܆FH̻UUD@D@DߏU"!!eʔΝ;QJ"33^|bSE@D@l& 1omļ#D@D@, p=੧Bzzz;0zh z E@D@" 1Y+1k@D@D@Ijժ!---$l߾]Qb" "`;y=_%Y&" "`yE-rL H̻ 8a" " WT9ID@l' 1okļ}#D@D@,#<3{[<+" .w U6{]" " "F ^zIk坂zD@Dr;+2c4 lS кuk,[ |MVI1ouZb|x:;u*'`9ec݅M/(DtA'1od@d}ޟWԹ ەŀPt|d%N B >yȪ^!A8 ]KG͕D~D[AB@_<}vHGO# mzF/N7QveT|;jY=j_D2SOrNNgz__4[A@ythX$¥E@b׃">%cV@?0 "tGļwղ%KY{#U(CH5vD xy0CIǐU 셈Kb+JY"2HlpsP<$uI@GkخC$h^ڬ~JiEJG,?ri7vj_$_"95("`>ׅyȪ^!A()jD/lj<GNo% V>StXG Ry^0^kdXL@>w@apg|DWS+&!ՃEj%歾LdW/E2V'YE@b*wX/Dj>_{ .f*}AQUtO|E Rļ.cD "~HԈ$=ůE@l AbzsێA>[y0:vᬀszCu%sSWz [@}c`w%#[MI /b vgYO_T|a?u8A޺4)$yZ7_lܪ"qK"y"_\ꎪy9C@$Sw_8f<.wVz&;ynY![%ìS Vo0΂o^1„Dž܎D>" _^ļ#D@|A 0Ԭcwz~QQ{FsG [}u|!n}.1TW/'# K@bu@p{f±[XX9pA3E>w|nQ?h|=Nv~AD|UQQA H^NS'D-qfJ[4,"Yn…/jyF)MM?,ޓz{J)H?ri\خl(Uļu xXTo H52LD/ 3$P|H {VcAb~@_ "`sy(ŝިV?7d/ ==z -1ϩoK((.szH狀~,|t@pst`)2O/1{Vp>/ WZ$-}n,0[b'Do 93;=+MOY"F\b>ڮG#KxVh H5,"Qn£?܃WFs >⫈@p>Ex:F6zM6DIێV.oo0 QjL"A\b>T"_^},sļS$U@ps.(s|<*MQ{qcQC\b>j.uDD"" 1od'ڥg eF{fWh"}WK8G@s,U$uE@n »G1OJЭ*n yn0st;R5I53=&{4/  p>o f>psX&y]" "aܠEUn`vs>wj5 " apk8#U-3N)Κ`57t-ܺσ߫v٥E@#9Ib^׀EA8|FIfown J)_> \Ϥ|-bɇ@FFRRRP\9(QB|DnzծzϣŞuP ء<Xn 4K7BP%}.1TW/~"O@b U@p{^9B"(nPT:1϶^sDC)lk5P'ȋPc(׬Yq!99_rXD>}q#dD!QTtIb'" %A8 1:(nL̿2wsV@EC%uqC='G,\|!|_[nxаaU%<^(l/?L„v. o&~{͢s8x ^y1vކOԩSѶm[qH>rL%]Sj^D"=s+xnd»ń|r <K,ի^{ ]t1@@jeg/z)1u)"`/a>mq~[M4]ҳmo=-- F2빷|wqx7pgK^WL'<]Y%=CE@l! \xN0}*9S/`һٳgcС_Z@Pz߯_?y]^^#2Ewg pQKQv9pT`<c[oŌ3qTJ2/r^.}U. T" }K-nU" QCAI~U0iVo9ZW {5k0|?*UiӦܹ3ڴiudɒF;gnԫ[aS0f¾DnSuIܫvģE@A@y1B H +Wݻw}Nw^D?w\L4 ,0)9eޤ3BL2f / 2q+9sqM-`?>>7FFp 'k׮޽;Uf>SRq%qOd|b0S;g!=3P&1$Ю^B h?~h'՘'$=FE@l" ݯ~J#ݙ]9#UBӶ8g}(;o6݋< lz|<D*]Ѷq9#o>UvuҥM#GDNt|}X"pÛ)iKcGf 2%b}%bTCM E=#B߽MmO$!jX߻w/nf^Æ 3♠.39]O]uG|8@x'&lْ㖖s)/noYYEsDsXWBZje^&wyf}}Y5jP իͫvƧ|L`&pؕ]53~xtU9$*}8RUx\l8oi;,~& U~Ӷy b2WT HMNyx@#&+a z'|]ǡcx3Ѹaddda+kp(+ۼ[G^/![رcѥK#V޼yxQfM<#X8b>7ڍigX7USg/+5+($Xa< X:4,FKL,}M]]v܉믿ެs$yDP2Hu `q@нpMXYs|<3'd:,0K3Y?cv%&97 8̙f˻oFBұ4"lGpWW톀DD g_3qV)5VI**G%qBռsB#} ^pW.ЗK`cZ+ݹ_,(lsXy#k k<"KOǕf{#}p1P%:|ַGq=_00駟7xì HUO4l?. KbdR(\Џ pXt+M+:4L>혃xI8`s]>.7~l)6}Y*ȇWוWzup_O=Y#ޯ_?t ˗Gɒ%][lW_ٳBzژޝRP#x U~V2 m<+o*? x`(ׁLVwJ|$Z?by'Ŗ+W$,7ujsYx4Z+mOKr wea܃k軠0^*%РjVO@ (k{مG/hUu/%lp_}ODӘ,cuޫʫv]b k UVaĉꫯLB:N5ر#ڴiƍ#)),v֙~ڵ;plQtwi6G٬lp7V=I6ǡsihղjNT:_^4l1 [ohヒN:)"j^o^kb2{Dߢv ۫4T:^=L- v¡“EY61 O))pY%}zE# 1_4n:˧eSXnWוWz.N+^ƍ3}:}gl IDATQ[D[.ڵkN:C8gO\`7|K.Şݻn/՝֩^~R.%%'~,Z@'#vd9fO-~}c:L= ox F۹~; `X:Tk{0eFʕ+àA  xuyծUFW=#QwXH46tE(1odx~e ^]W^âIM=g1˖-3kbʔ)zUV8sLľVZ([lTtF kM۶Tv6jdg9Gӻ Ϟ *}P#G-e;e]vdq@卐x$$5SVV6>.0 ^-}]Fpe'N@R䋍Xu kAQ+xuyn~? ~ }yYD =#~It;ISu& 1!2xu]yծosl#9{8x>11Lů]h=5m{ȑ#hmuPVX A$CqN<FS33֕,?'gsۂ~~r84:<.wήyHL*f[Lga."?4{R7ujtaN>d#L/#%}M6(p qx<3+=zk& r#_/f_[ƟvVW%qE@jѽ[m\}etl_%?7'cxozdf^uA.]лwoqfK;wzuyծsT[.lCgcX:ߨ)>*fy;$+C$/A鰰xu]ynXp"x0];'Ć ˠ9uQb~X~i}Vvq&s= J+CaS]QvczDv~ۇ#^+J*G'lױnON]jNJPM0d\ܕk)%PB+`ӧYf AEUPS-npy:Upn_:w)Źtn($Cc!"k\e^]W^gx? 33$ƖЮv8oۂ9moc΃:P>L@DŽv>>Lwi]L߸OZ2[ؤwOLaE8Z}N=Ԯ5q9|kwn}ޛܛ=|k|U6 oLW2s +w`u{ۺ>{#hkˡAc:O}n|mļ#%/pPxu]yn(L<&~ʕf=3gr$ G>%ʗ+={ӱ]Xd-f{5% e&\.n7߽; 3L@d0&_*K!)51pgYB Z#~GŪUqo7Yb߾}fgݻw;\yf~/h۫ͫv`:'<#6}ٻ*{(M]QDł+(g wQPPQDzK'!; /!}o朜(ٝo7{k+BϵS܌l1+2ouR(uW+YrG7nĔ)SDw֦OJඛcЀ Akkغ?yX ըvw.z`Fg"ZڜB4b8 ? j '@NN"_^^F?555i&B״ÐP%擵ȉ137p'91 $^/F#~nlҽo_n uC{ud&N(yWdˣ-<ཕd,fZY&#a}wD;wj90aEr<6ZkjصbpQ>~[)@QqPdBM:V硨7d} J 1'}Rq˸A8^HNj\6ṕyUXK_XG:n~ RSHCxp 1uj%@Hvz~罦Vh8Ouekǁ{#oѣyCmjfIʎR' fvc=ah&8y%d z+>"0*|e8~md ,˓|8-b"W35#|Uod=e5j2;qYgaŊ '%E{`uѿ_:bCC=†7sE|RbRcY=]jJ0HHB||⭈F!*JC^Av3[,(X[nD;[,{'d|&Kn86j̲Gul_-廒2;['kHKА:*j ;V Wb2wթB|VdޗU3l:Â"J\3I8,^XĺGY-8vx0qh{nV]mRc=6ZG-unw;bU1/fA{ ?l0a?wp0d7Yry$Ʈcċp3NkeE ~\mCYu{eYp5ʫ2q[w9p 7P4;y$19sV2ϊ ) ,%<?#UE8fo~ FiTO7+b.3Swnƍ^dU K]x ?g(s_Kvn?EgޭK ;#:k$a7S#aߺ~o\eb]YF k,z\`YM\/QgT!=0x$> "-w:WxaO;"ֱpQQH9J\x ZzYw] k@' ǡńɧ6]^Yz%KlܸQ$#_h6oތ (.11}{␃aq0xP&:tHh߾?ۀ3Vc]^Ӗ|&Lj! kɒ:jXŎC⑙ZC֕ dNpI%*ݪ>!`sؓҷ&ébAT}n{b#+F(|&Uu$U؁6OOuTd,RnE8|aapVx1K!` p-Jљ1^3bK 7QQ'6K<@TL<\p!woz__/=-OǠ'>ъ{OGJE %㮪y·}|3YZ>v~p[s4 &"qa`M,%+I?`Xp!֬Y#.RO|zZ :wJ2ѣ{22mDp<((sEUkvȭE+Guڷot񓒒Fxld 6ÊM wǩy*}qxĆLBBE|[2/ڵ+Ppp7vJ:_u dxtBt!y58>tX3l;&"Kd5Y˱|r' cvM7߱CfcYاO*RScgJf9ߍ?RlVy(.uf2t"i\쭂,3wEOǎ~jj}|YM\i}xu۟[Кw‹8On w<`113XƐ"`sy'?]? &soQ!w` BH^'gey!>O|ęaI%/d,~%fR5X.]ףv]d"33=% =훆.m8ROBO^Y@E vUc˶rlܴ6Ɩ(,FɮU嵰:QWuHzfA׮]ѻwo4>&Kn$̹I} ãA52 ߍKFbOUeKG %͆;mƧDd=:bVjĮןA'@{f"G0joM'ɤ5J\2 yf[bɒ%X`~\R:+<]큌XyX{\9v_\'8A\=E'ODڎjlQ+m{n+Ǻ2ol-oﲾ&M״ TiC1dJp(Rqʉ]pٽЭK2`19ge7f}}+}ݫMC 1c;=I=3Փ2,J"dgU4{Ngo:x2Ӏ)aG杕(JԮXڦ.X3`jf@ؿvFCaF"cڼaC5WMh]YOVs"776 䨙q<(#wqGw碢ȫ-7uekX>-5VqJ$q+V⇹۰r.8sСC1zhQn*&3d uUO>; };nķE{+i1Xz3<Î֯F1 o1^|m^j %ȿ"طoi{ 8Yjy3lD]a4d,Fl뢜۱pBL:Uez6164 1QVV@)jw!jufnw!h>HQޒ?0bchu;-",#Owoz:C?<ɐ%˷V 0CiY >8՟09ȼD[bHm4 ;wCWgb-w^զ}װ-!_Q9LMͰR#l,%Wa•ܮ*C;`h6fs eQKd7Yr}I !soJAAxַ~-HT7>!`sXyƆ?7?~r&KiG">aИ7w=Ɓ2< ژay0 uWʂ$T9s&fϞ 2mؐ,|=YQv\<~l'{t"6VKS۴e()wAеK2b-H93V'ƻîҚBO=IHHm&̫xE~%{@O?5VbߏKFB,;ʜ8M,M1plb́G09ȼ`ovT4үI2_U{EͲ~lz~4æ3dj8 Kd\sNo@]DwsA]墺>vC8{5&"́Y-[cŹgOTDE5> =$o\lgoGK>Izcx5%ʹ`I'v"@tt=dW zg^xe9nh0-&L@׮]RKe7YrG]U6d IDAT$>mMEϾ2 Rg۠%x>10ٟn5fx>uT|;SlŊa!kҋТ칲vȿ ^j9aIH26/k,%Wڌ'g<hw8~[k4cǎe 84d?2b<I'a4x,%[|r=\pϛsuB&7(}5Lp;pc0~ݽXb,AI7l,u7/s{!k$\xno\ra_t=!MsKE;WKNƠ8qxg2JARR(I˯;pߤEgijj<0rHL^5FUQ'֗#חsO_pH=y_ slzEzfدCՇ@=+uNkEӄgddƅ^E(&Kns#xG͚ϛ%av(jǸsu)5^CNى;">0N3 '- `6n!%- =z͛QVVgoJq7cܸqHMMU@Z/Xy5Nnw*>bsae۳s\@$C gSy~0{{ugþ}W%S-* i7=GD4_?CRi$sYB̰Yud,rPԚ|;vZa' orMu\[jϼE xn /Ȥwnt"-My -dO &ছn¢E_%Q^^z<"˸nvUzd7YrC"WbEKޮ@^gM H` ):OO2kplbN_3fx>(r2wci#AYt¹;/gnM8`d?aәE8 KdɍgB<Ϛ5K`F]?,FK6܉LoQu#iu `[5 םT?@[L>36%|b"?|<c rJ|Q\\ X7'$$`xgn,P2oۧ~6{L+޺8Q%m-{]n"Nǖ75(0*4 Efšnlݎogd~%k'D̾yOү5æ+Yr7^}՘9sf#2O;uCQZZ*ګW@N;5=&g-,v}rdl`&|MCjZƎwy $}Ν~ŋengSN9Ez&ȋtB/kɒkmvCxF6n"gl賾z9 }6.NԄϦ&^pۥmEb~D$Z vGze '݌ſP!CwqͰ6YQW Xb^w}W]6nݺgG-"? אַI'jB蛛$y_CbAaҥ(߽]G2]tL #5 ǻH==h[o Y +kO6 H#oF:ql!]9 tսW_Zi7s*{0T3]\aÆG!H{#I޾};&N(8Fqs}zژxN:I<-_x1򚆬vp뭷b̘15?Ɵ)1a}w}šE-_UU0o<_Ov`:tےooaM"eŕ:y5vՉ g$ c+0:ٕXcרw%vJ{ e0kKQ:tF33`a1 7U FC7ͿLI:bdfMg Kdɍ$ g9ZIx: [|󺢢"QNZ:Fѽ kUbAbRnULJuOJJͤuyIТD`͚5B>eCᨣz#kɒk ?S5>8;Gcb7_wo-q9~yyG50ٴdYZ/}snu(JpI^]ESc#??oN*,$41JZ}xKM7Ii&8z̝;~ݾ}{q0rHqIM~%7 sq Ug\4psP,,|ݯyu?7Ҩ1F0fx>W/Z‰7ޥV+1|}f?β]j=z˳YLAͰP`# Kd 6F|ժUxDb;YO-$ôr[O< ޽NcOxƻGG~h+)(pbxv Hj,fU6Lڿy2ܘbpPhcSsVĻq e8 $NJzeNzsieša\?`wO=!;p5ۓ6]!PYz%Kn 4l$ݞ$M2>֯_/m۶Mc_ɂmٗxP*ߞjr|%kɒL,U߾! ׆> w"1FV ;[.łĽ'w9ԉXŁ5%%k/=&0ٔd 5Mkhd4nXn `iZ2:^p5`,ftASZqW HձBox)"rh6pQR_ts(=Li @΋K10U:.Y j,Ihgf7ui{bU߈C@\܈[`5a JzȒ8?4΀$kIQ"%!`糩-6hjH,`9?MxCu6]Pr,%7pȩA@~%<+#}|WdseO;O0~gxk]Kl.2;\6 js,p،DF=  ˈKftAUXyPWDթBo|9"fx+w:Usϟ)*uP[F gsy.^r9HwE5VAމtZ08v:3C5̰錉Uk+Yr6("YMH\cvA˪l !7u:^^ۢ'G'Q!3Ir,G< XeM# KdɕB oa%5qUp1T(V )Vu#,y7ڽH#pP IP!N;S\6p˳$f!-j(dL3z:28kO["-.Rc63l:c"FJ\ HD@~%7s~ iBEn:6cN^ 7/aݣpq蓽7;wT,? ˡƧVCUpλ΅}]I2y+bAM|2ON$@C/^y75oeo,dתAQd,j zd7YrϖԜƄad$j x$n=2h{Ix׬kÞۉA@3æ DU'!E@^ɒRp0Ad5j^"В s?>}kM<̈́#ZZ筍ۧ7& ̯~ON/Y9 }!43l:@J\/Q*YMܰYd~dh7ҳ7~?jZDQ8wh [5@8"`糱ɼΟWNdx/IWkCK`sHǛĄwL@T4 GN:,q7æ .B,%7*q C kɒk F%}Y_ĜU6ʆ-μ4 ]3< 3Q)q`PG|y A!fx>;~ P[.~,_CyQ\bwLg8s1V"/yˡVNx26:1æ JF`W=՛BocU(";1oq`v+=-Ϝ]x*;?V*L'MM/7jq" nPSx'g_&b;` W<>NޟUM9L+(x1sLL0!(cPU0f-Kn0TYM\_/% iwOB e!y qpnYI֐#UonAZR9pj|OEzT {ajηmۆ'x/"{7$ᢋ.Bn IDAT.]C\\ HD@~%78%VQ,a3mOl垕kK+aϗlP;-Ӂ@oz'mߵ²xdonmڴIW_}111O?+Yq3n8s=СC)z˒hYM\D@9Mڜa̳8|68^gBMXj#~ s4,L,Զk&V\{oPSS{Xf ,=E6o&Ə]ap7 R-0%7A>Rdɍu6}̈́A<{ T-}K¸y֟;@& lA$DJ;?]Iihl RhK -7's確zOZv;V+Naszo6d@'bNO-667t^dddfH1QB@~%7X8~җ;w,<8?Vڵ 1pn݊>MY?Jqa:t֘_6els?\:Uw}N %[Eqzshz;{)a=5 qM[}3Gs၎;Foۗ=Ĥ9\+**U޽ԏ=|"~LuBh%+FJ_}7nĎ;vo'H8y(d3g}֢#j|֭^ hK vԩkq1M7kb\2Y 蹳]'k`oY>R -#2\]OxZZhtiͽ䪪DŽ1t 2j5 geeL Kwuz$a>dkIoFچ\ԑb|p{wY_~e Ewޘ4iHxϐ:ZI|A=SO ?C5o|ƒ}W|Gzf?}tvm(,,l+1cnVIwyGyЮ];!y|ov!33_~_'|R\fֵpKoW^O<7onO8%O`fl2BD޹t2c>a|1&\wXP+d9 ^ѳ] Dx͓MWRR9ss1ԈB=7B< ^MO=ѫ` \`E0:,F_5PRG>a}7l$̕ăxRyƗ_}/I?0l0q+" ;Ĵi;ĵȟ>zj*ay#!!A)S۷os%N'p~W1vw2OARL:U|.[ C ŋw'm];rHqy8gp+r9ȼcw%p~};@M- Z)jú/QgbouUencnY Z΁@݂4 .T}^z 7pC+YrA +B ֑@O?T$"rxs J,F+5PRGNޭ[Ht𬣕xobZ'Oa?\Xߏ=X|?6ZYf̙8ҵ<8cOc|JiVc6Dq $OڽW_}%Z׮]Ŝ8dkO?tJȿ`cRd^gO:F ՝\9(\ 8h$H_q~[ɭ%HU8w֦{@L*#;Zrz{[C N Kҗ=dJ]z \rI՞ntGٳѧONjt]!G-,5Z_2g@>FwzЋksVyypW=2ߔ̷t-l&l+VI͓\d53З}qOz3!<_(ުieӚH^{MXfdA6OšwY-Z$z-xnlC͑n=#V< o6ByED#ƯD ;ows̫ @:PYa׶}0Gֹ[u7Kбk @p5A_pUw ֑$I^%u,;Ӵm^^(sEd$|xˑ,\G!7| t8BV ~`<Ώ"Ѳ%EKKKW_}Ux Z,XF& dk-N'L@LM? ?\]wyBZBN9Č''ù*f#-5$>,rrrɚ8ZmW'1$\#Z1ŵ%>e9צ>דg}ZLuK%]~/0^qŌ%|X{mR7mm`Y`G\!|WϢN$uޝNfOw;Nr!…fwN`?t|WayJ+Dw}WL,2\RĄ?Eӌ3^w@Jpx2!{ќ ?Zh?YS{bO% 6Za" #3QS/9-*KڒKɏf% F/")ɵxheĉ3o63 qu$np.z<0{h`!"&<Вk~g5JX=H[K4Ĥ$B&5kxs:r~ 3uO?tx!GA=3̀!Pu L>CƑÚGqkkiT[-X,Yr5opPҘ%w ;6KVn&y#i|ҋ:>ޣ7lH>N㻎ҕ|fn;Y^VS~>y[%~|ptWd^>{e/C_n< '"g W|>GpM1HH@Ƴ  ;,Cr\5?-${KWK\Ah ''u50_yOH!Yg%>\;G?pH\eM`g) ?-'|Yq;$:k\$dk1@ 6-ȮC t2~DįF2O!GF\%׋·0kh`#1'\|Ms+6J1<0`kO0Y]]{/K1V-3ykt%2sÓ1˒>җdYǰys0zq}.(mwpFZeE&ysωmyX̃efҥx7򀕞s|aCsakٓPpP}uƔ`fΘ֛V}\8WLJ4{3OW{{z;]wi(i4gp 6 i@LCp} 䮵YaJ+o.]TXEwYQBjttկŁo˖-LXA~ܰ/~\8.n$bz躏X\h M냊 $tFϹَiaA{#')&Qf?Hz]kF6ɏ#UؑOo_}a n-ZC zC 8䚰=.׉L룎*.2Ol^<DZ{s67^>4e wIfrr!%tXG$=J=xxa;2 -|ȼXapؠkw +/aGdxfk VL#sv3N޵B>@{!d$wkM~{ K|K~{>]xHLI I$Y.㋛$h8.G)o9؝$HZ!/_Zě6k;-&./4dDdZ&jc ]l΅$srOx?b0 }sՙg $YYYy3SbsWyZspYyK W`H-K[d$tg:1b#&AbMg"%a/w^j+ꈾyZmm`:oO-.IGzg]V8yOk4$  95C#&3l:)(~! KHVnw2p=xp yp5΋Vuzd LRH f'~LVbgKd$zC+;C0\͝PAkm_2H7{߂!Z`E|c%A!@4M?'tù +xvo7WNħh!="]SQZBoVOo6 7zəg4RYz(L*B@c%q덁cEd `d^z't _ |g¶wh]h؛ߵ>ֶ\Yz 2<>WԎ5k%̾5Ͱ̎q$_^J"󑨵j"fގS]o d1fF-foCסh%p2fhnJO ֝_[GߊjV qӀ֌$t$]];кPW;^dzz6TpWȼOˮn0߼M\oǩ7J_j[dӡז~7ֹAB_UOkZV`jՂr]Vމ4ewvGk|716>*ZÀWCKnMW .25KYz%KnxB3d7Yr=CE]e4mEx3%jC/Y W1}}+I'o/]ל@y׊cDf|K- @8VwZb;hΆ 65"H 3lp~d,Ẏj^ dU`NsԨ͉[dy-j8W}ջL׷HYIZU@&kݸ#wUHC9u5<06k|b3dthX/jLJ{9O6s{RR+ag{1vV 'wY⛒y~.^XXoy­~ӫlM}+P=ªi37E[SK7~ԖmU~\P^liS_d@4{c:ɳ]ӳ\Ⱥ䮵%3æiER޳gOOf@VX4"w6&%$7@vvw7|S$c5g<<]?xf2B!2:$S? IDATPw)B"mMJA_"ݛgQ~ƄRcud6utоI<3Y㠵;ڐ[i#/]\[7۪UЯ_Fd~ ~W7N Hy(믋"g{~ѢEBlfy$B PϗpXAKVfo{$Nk ߀cA$$LWCVHHs5hUZhOh9zL 4AUk6u)))3g;8q{g1cƴfO|̝;K]2b? _|QF5+"1^GZ 'SU|1 k|J_j4ፀ"Hw\7 .S?8! Γgvs"&-^n(_.}_hsP[^?wy{7kMfaDaz"p J_Ba)2ﵚmUh{3 x j_"⻷Yݝul1 =kk]G2$wws̰Yؓ/[ O:$䴘~ƌ |O,LJׅ+>>w}7:(oV׆^(Lz|C<|/!\h̰UEi/ͅc[ іo 6Zm욜DRV==Z\eLBKm D%*kdMTeLhG+qcrϒs.zoS@o( ` /@5|Tkff<̰GoDr*>st* ~UZC"2:.j| ` `ȼ+_O/\ }_I[Kgj+WY]uQt"ѝСv|&gḬ鼙鯾j̛7OdU~ʔ)jC@^ɒ:d$qd5j$ Ե 0~Sd޿5>9^Y 'kWyNCہت5adOt"9[' -}B2#y6דR7HG@^ɒ+p5d7Yr%@D/Qu37E=\L.Q }\8>m69(aAE& ]Mց = wCKj{ta9Eud, V"dɕO(} 6uB'̰ii۸Q s+7 Fv`6 :z9afeC|Q;q,0b]3l:=J#$*# kɒ+q5_P joa)2ڶ}ޙIQ%a8Ft gy;FK,Ъ7-. d$4Po6ݸ ERE$Jn$lLmS%hΝTf͠_i۶mGQcrҥK4|ѣ_ӕ+WJ*1C@޴Ƈ"z7K  1S}V^iCX wy6oL%Kƍik׮B'UoZvOA'`BA#{=Ep8WZv֬bW^ɓ'/_ x"*T/_N/28q+Fou]f޼yK/Ç[oP~|V(-- ( +O.x j۶-R7,5k&c3fT9ZlШUoZv@pL/1  b> PtngV^iCݻwSrr2mڴ*Wt"|Ay=!!ڷoOW^e˖W_}Ewy'-]ZhA|ݲe ժU&L 3wz-3fQ… !?0X~=rUVQjhڵԡCڷoUXQQ޴@ј _bF@2 br8& 1Jˮ==ӈ#2,{/t:y{8VJ{={^Ss<;xf)SdFuw~>j՛]'–}/DO  1.(&Q@1X!WZvP̈_nAb~ǎ"!CȲ 5jLٳg%O;?I< ylcǎ05kP ;YG׮]iܹ~ZeW2L3Bt PoÉ&0L1f&WZv>y}ܸqn{>T,=opzدQu%gmѢE|?r<IIIh՛]’/vD_ 3 bY)&Ҳ뇰f}vuV*\0^;j>ΝRJrp3ܾ{o~WhQD[h֬YP/]4ʕQQ޴@ј _bF@2 br8& 1Jˮ⟝pႼΛw,;E:3/^X6ߟ|Iϩuֲwgag<ϻ2{>x;gΜ6x=oǟ  |nS~}?GGzӲFc&|!:L7yDCPt&p!潞ىydW^4tӧG۷S2ehر"OÇ'oܸ1q+W8Ԟ7+R =&N(ye˖ y#D$|'] $B[?r+-F  h՛qt%%0K&ļ/SӻNPtޥ]ϴJˮw# @M.rL3QIz730k0<h啖]"@MnЃu+ bޯQM(:[Zyes ಇsd$/B[ 1[qPt.CX WZv- AzӲ!O 7]AzwE`v08G@+:G@=Mˮ{c$@DB mA 6&|l1.#`Bѹ cV^iٵM@sMˮ/> 4t bAEWҲYXЪ7-!DB -Po8suޝccD˖-~SSSm׋Pt^uJˮ @ ;ZeY`&䋙qè$`BA[ȭt4itʕ+UXuF}%4v%JPRҥK4|ѣֳgO:vZʂM #3{n!WZv' h՛]'–}/DO  1&Ԯ];ڰa :ׯOM6QJJ /ɓ'7EĊ+~7=9E@+ v@|~ž=/pD/ ` 1&<+#֭[nݺZ_~= >rMewy'jՊҨ@sNzWs6={ 7@?<$5j%''ŋ)11M&mqJC#2/f1BS"q"_L70إK:r}a6 &2{+3<PlYzd5mTEwQ*?N>M͛7{ÎO L(:?+"I#b!_kL70YנAR̂;S(!!Afw.\(bC\rawߥ{~7|8H /. |qqp040 ä]ƍe3f[6k֌,X@:t.#VXAGGJgϞ%BY˗/mΞ=[fϜ9‰!.pwX?QG},#`BȦ"Folyape˖.;hgyU̙3eC=-|N]1gyĉWPt GE^y$p\HCB88PoaҎߕիWpz/_^[Eϛ7y /p4jԈ-TdIi׮]K;v\ E8WJ;(#> 0 EoRz4/ϓ'߿_ޣ߷olWrL3ӧO#F gy}hsÁ-[裏iӦ)7Ej啖]'Viu w@@ zI?Ӵgڴi%1?g2dC{6mgovR }TB!yG)WV+:v~*Z(۷.kZe0 Ϣ'&&Rrr[_|9:t(8FQjhĉTNjժ :T`#D8b5M픷Zye)קfΜ)+k֬+RJ(--M֪U ,TL@޴*y@G1 (@r咳^JW^yrmIu#ҥ ]p.]JSN۷S-d}޼y#_v[ҲWSD"ׯ/K=CqB@޴"  01M7mF;v ٞf۶mKeʔ 3AYV8v*,X@gϞ]yVgt96mZ_s/NxWZv`jb^c҉'dɒ4`I&]#eҤI9rDmݺG͚5Ӎ7hώU޴z6p @|Db>`gŋ";uD#Gy O>I'O Zॖ k{e~ԨQW:tw~mz׉g|pL7EȐx+-`hrb>OYM>Sj޼RjU٠t23<¯Q,GzGeL㙱k՛] @Gkmajٲ;_vmyU^]f?O<f͢ѣGSn݈w<}ɨˎ;η~k)\H]V^iut "T'ԸqcƗ-[VF.Wq-1}֭KNbŊIuE?.zӲKA@ 1!ŜvgQ7zwe?{BB=O<+Kgqxs$^G7J#FD8R6M?oJny?.+6l(b; kyѾ_~"7|#ݻWfqЪ7-a@@vPSD\r'd33t#ZyeU]06WVXA-Gʞ)V8UpSpxf0Zye3ɑ'''^'"ޓ| 6L66mڴ)͝;7eO=l3Ѫ7-1  . 1 `M},ӯJ.b~$UoZvc  ^#1﵈<V^iٍFt &UoZv] @@yK)2%RfS+!UoZv^@@4 @k҇m vB/heA  qM"Zye"UoZvE|4pk ȵ1z`Zye`a %zӲ%&  ""S:^;hhN9.! /b`xhZ7Zv=: h՛]`@@KII^Y$ѱL&Ҿ1X1Xm!bOG~!ue?Ъ7-~1|.Ә^)Ͷ];0Vi5"($@  Ͷ]$Vic3xļ" @l'u@ D IDAT/> )OtxpmE \_   l&p'a@Zr8@7pPQp/#A yτċnE|po8y}=PݩO> GA3|/bTlٓ;FV ߻w/5jԈFI{;.5kPŊRJaǎ z}g  u^0՛boO?M4;|zz:Νےk5z衘 z}?Jb9 aX}իW{1z饗ͅ h+^8͜9~ab1߶m[ڶm}TlY0a5kL^zѦMo-[رc)!!Ν;'ӳ>Ktq:}4+VLV Թsg5jwe0`-^~'UM4If4iB|ϟڵkGիW_~tR:z͛W~g!Ҩ@rn׮]w^"zjڷo_p~;=:t 3+W_~<ϳwb~Ν9K,͛78ź?p9E(0XG`#a@dVoYO>H;vYršbgw̙3LbgsX1|Ajݺ<0ȓ'A ^_:t  `$y#ÆA8I6 ÇӺuɓtqy|N:9<3III{vbgk֬J toѢ<ؽ{,_hQ ڲe{7~X2I>a  w3LvYT7hЀNJݺuMx9>x3<^Λ|ڵ_XfϿ,ҏ9"Kx"]tIf#<ζ_{5y>prCz};Jb9 aX;gϦzJv x8ݞgYH?ꫯ;1*<8qBfY.]Zv;ƍiŊTB1c, %J&y0}NyI=p7o.<>8@   %z(X<#O:ԳeN=/g1b}"|V*mذA6r?ߧO4/]ӟ$}43<ϟ{e@jhk>Æ 6mJ˖->Ͼ+11ѣ@ vV/[B  ^#1﵈ fv@ @@%1-9 M(8NǑ xļgB G@E7"~Ap}ADKb>Zr8@7pPQp/#A yτċnE|pof7 8\_G   %lNj,9  -h<ͶoB GAq8A@3 =J8 /َY  r@@ Zђy !m߄p}q9 g@{&p@ ^fċ@FFFF  &6Kj$IENDB`spamcheck-v1.10.1/docs/workflow.drawio000066400000000000000000000030501444633272200177340ustar00rootroot000000000000005VjLdts2EP0aLZXDh0Q5y8qxlbROTlKncb2EyBGJGMSwIGhJ+foORFAkTVqP1LKTdCXMcPC8984AGvjn6WqmWJa8xwjEwHOi1cB/M/A892zi04/xrK3HD9zSEyseWV/tuObfwDod6y14BHkrUCMKzbO2M0QpIdQtH1MKl+2wBYr2rBmLoeO4Dpnoem94pJPSe+ZNav9b4HFSzewGr8svKauC7U7yhEW4bLj8i4F/rhB12UpX5yDM6VXncvNufSOu7oLZ75/yf9hf0z8+f/gyLAe7PKbLdgsKpH7aob1y6HsmCntedq96XR2gwkJGYAZxBv400amgpkvNr6D12gLOCo3kQqUTjFEycYWY2bgFSm3DXGODjH4zwJI9Fxjela5LLoSdgywbf0ZWrhXebbEzA2yBMMGCzUFMWXgXbxZ6jgIVfZIowQwVERnsXurFXdTe6YFnazHIsVAh7IizctFMxbBrvKCMM+tr8NQiNwNMQas1BSgQTPP7NpmZ1US8jatxp4aF/gga+B0a/AkhB5rWc97lOTkfZ4VBZJlwDdcZ2xzNkjJJmylNBtAmp7FgeW7x2wPvcfDcg9Kw2nmg1Vffqtrmtcpc1jnCHVtf0sgPle/JIRh1IPiAp9Wi98NpEVZc/23GejW21q2dxrTfrKo1GWO9XSCdfqOTMW+b3+puG2v9XcTaq/vgQN273oHCt7ysis/BecCO9BE57asOwcUip4W1CkQVs6qmGrcl4T7gerk526seiAjE1o2wzATkPWt5bB7POW5d7XhqlCuotbc9xu+X47gjx1vIO3rcT+mfWrEvpJHJgRKx/Bg6r7wWQU6nmadiV9Bh10cFESy4JKrQwgthrsyXAy9gqamlcp5nWzya9SDBdF7k+ytwC3RDqUuWcmHO6y2Ie9A8ZD1cY4LHkoyQ8AfVzyaaksuYrKC2Pm/YSyXthPV71K7f7ijoFnC/p4C7zuhEFXzSc4nShZLlviIeGm7NWb6BGI07UzB8iPqvdtHyHwJVvSif46aFMycdnq0+zSbfhvIrfpmnudOT2v9zXu/N3D0ZfgdUe/N6b1J8wuxc3Uya6bn3/F70pVKtsp06h5nCEIjq1Lt8rjhET/3LianzanFfv7CYupXs/yamxzVygJa8Z9LSrkU2C1ZhytL7K9NTLoAOI3zpp39HHIeidLCIRt4zvv0fZ8FRN4cNRCHKBY9KjBxFhNn1h8FpsercFJ8LvLE/Phl4ZNb/7pZX//pPcv/iXw==spamcheck-v1.10.1/docs/workflow.drawio.png000066400000000000000000000562551444633272200205360ustar00rootroot00000000000000PNG  IHDRi,0X sRGBtEXtmxfile%3Cmxfile%20host%3D%22Electron%22%20modified%3D%222022-02-28T19%3A57%3A21.798Z%22%20agent%3D%225.0%20(X11%3B%20Linux%20x86_64)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20draw.io%2F16.5.1%20Chrome%2F96.0.4664.110%20Electron%2F16.0.7%20Safari%2F537.36%22%20etag%3D%22hKz9YfOOZad5k7PoCM6y%22%20version%3D%2216.5.1%22%20type%3D%22device%22%3E%3Cdiagram%20id%3D%22C5RBs43oDa-KdzZeNtuy%22%20name%3D%22Page-1%22%3E5VjLdts2EP0aLZXDh0Q5y8qxlbROTlKncb2EyBGJGMSwIGhJ%2BfoORFAkTVqP1LKTdCXMcPC8984AGvjn6WqmWJa8xwjEwHOi1cB%2FM%2FA892zi04%2FxrK3HD9zSEyseWV%2FtuObfwDod6y14BHkrUCMKzbO2M0QpIdQtH1MKl%2B2wBYr2rBmLoeO4Dpnoem94pJPSe%2BZNav9b4HFSzewGr8svKauC7U7yhEW4bLj8i4F%2FrhB12UpX5yDM6VXncvNufSOu7oLZ75%2Fyf9hf0z8%2Bf%2FgyLAe7PKbLdgsKpH7aob1y6HsmCntedq96XR2gwkJGYAZxBv400amgpkvNr6D12gLOCo3kQqUTjFEycYWY2bgFSm3DXGODjH4zwJI9Fxjela5LLoSdgywbf0ZWrhXebbEzA2yBMMGCzUFMWXgXbxZ6jgIVfZIowQwVERnsXurFXdTe6YFnazHIsVAh7IizctFMxbBrvKCMM%2Btr8NQiNwNMQas1BSgQTPP7NpmZ1US8jatxp4aF%2Fgga%2BB0a%2FAkhB5rWc97lOTkfZ4VBZJlwDdcZ2xzNkjJJmylNBtAmp7FgeW7x2wPvcfDcg9Kw2nmg1Vffqtrmtcpc1jnCHVtf0sgPle%2FJIRh1IPiAp9Wi98NpEVZc%2F23GejW21q2dxrTfrKo1GWO9XSCdfqOTMW%2Bb3%2BpuG2v9XcTaq%2FvgQN273oHCt7ysis%2FBecCO9BE57asOwcUip4W1CkQVs6qmGrcl4T7gerk526seiAjE1o2wzATkPWt5bB7POW5d7XhqlCuotbc9xu%2BX47gjx1vIO3rcT%2BmfWrEvpJHJgRKx%2FBg6r7wWQU6nmadiV9Bh10cFESy4JKrQwgthrsyXAy9gqamlcp5nWzya9SDBdF7k%2BytwC3RDqUuWcmHO6y2Ie9A8ZD1cY4LHkoyQ8AfVzyaaksuYrKC2Pm%2FYSyXthPV71K7f7ijoFnC%2Fp4C7zuhEFXzSc4nShZLlviIeGm7NWb6BGI07UzB8iPqvdtHyHwJVvSif46aFMycdnq0%2BzSbfhvIrfpmnudOT2v9zXu%2FN3D0ZfgdUe%2FN6b1J8wuxc3Uya6bn3%2FF70pVKtsp06h5nCEIjq1Lt8rjhET%2F3LianzanFfv7CYupXs%2FyamxzVygJa8Z9LSrkU2C1ZhytL7K9NTLoAOI3zpp39HHIeidLCIRt4zvv0fZ8FRN4cNRCHKBY9KjBxFhNn1h8FpsercFJ8LvLE%2FPhl4ZNb%2F7pZX%2F%2FpPcv%2FiXw%3D%3D%3C%2Fdiagram%3E%3C%2Fmxfile%3E]) IDATx^U D1Ă*VAEQbAEQXE$*`/Q5(V,Qtw~ܳ{g{wΔs杪zٳgĉm̙6|[|y?TWW[ΝGV[[kݻw @U\>h>} 2zm]tVZʅ~kͳiӦ٘1cW^6nܸr@Qh={ZlرsQ Mi5B=x`[h͘1Ok!PH˂lETV h+2u) m, Ufͪ۷X%1dHYԛmM2uq+UuuuZ9SK4 \r%nt+U555SNڪKK/l}q$@ ;U_~%8ru\k_ *3K}U:fVUU @ Kt<=D00)%"͔@tH3 tNR(4sa a8R 4s9!H+bI3 "#`I3 tNR(4sa a8R 4s9!H+bI3 "#`I3 E+һᄏ=SJ+ꪶN;W_m]vm.^{5[t}oSV*5aIWZubW_gm?=-h:Xwj^H`)ӹB>4GdDZ1eO?}7pmF.Ygez;lС6l0[ү 4{=kݺ]uU뮻:k]etA=c_|͙3'TqV^{}-ښjf ,py⟓ X҅NHdDZVga>=cN\%X0rH{'6bp饗̙36]we?xcc9t&O?mp[vWثj'Nt][[kn?$b-.\hmڴnԏC=4:5IUGRqb"8* H˭ѪU+>sW^iTof}ٲe_mGuO<5\ |ҲrKWf-V'.& \!OOSdIE]drmeӍ#sC=q0bPZ>wٹ'$Js9[oqOmܹ?>`;#QPݺusb/®wvm:Y/Л/rMDex|;]wu馛:O}%-MRS/H(Іr P2"裏~;{7Y%ƽ޻8H]x;i„ ΒE' .MVNOR Ip(>|Kv7\,~[];#?}z뭝;dy.Z8+U<"N>dׯ}NeE;̹CYv횬/IM'gO4ɉ\4wqyizر^{6'dPbxt1 m*E%%=Nh;vbM֛BSN9e;lѢE/}I' Qb^ՓG)$"h!S~kYǏ7-(^~rH7dݻ]~EDDahC9(Z.DG$HIY i&@p4Sa a8R "@N 9JX!HH)X"%@t%@N 9JX!HH)X"%@t%@N 9JX!HH)X@UuuuB}FT (:@,_ z;I%$ /[>}l޼y! T4Ν;75U4|:f6nܸ Q*@լYQS!LйSLݻ)R PulN$dC`0l(r-~ DZ{ !XL_Ό1c4.z6dwj.]lN" Mfcƌ^zaA3_ @#Vٳg϶'̙3WIqBڴܣG].K? "]T+ư@ H{"+ "EV@ D#"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ "EV@ D#"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ "EV@ D#"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ "EV@ D#"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ "EV@ D#"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ "EV@ D#"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ "EV@ D#"@FDY! i,BA M`\pav,X軎;  "0/&L`Cm6XlnݺҥKmԨQ6`f @~֮];{'k׮+;w.dkӦMNB! ḾÇŋm+4h>Y@ g&ʚn߾͙35H@ D)ΚƊw" TkZVtnݜ_t)@r }ܚ8psY  I&Y3`F@ D:C'OlÊΐ 25}ѣEgȌl@ tM6qD#8"ë@ ;t?}gÆ #Go#FJ@Ȏ"O>Ď#q{vYMM oӦ/tI7Ə_P8p΢.*.0ԟ:z!ӧme)>|Kv@288uT!A _~=aTb0BQLSENrkƽvi~;0˜4ijg}w<wYZT{y$\_c5 'ҥl¬*Tc'x‰,+-|joFnn-,TUUY}}}A@G@ tOKe*9#Hu%T.5 'O,AD:?+tJaR>Hq%֐7ƚ+SLJ.9/"_㑛wݞz)]iUWv,QRovK$W{fM?ImoQ9۷k UoE-(xU["sᇻ:+;mi*履~zClLz&ITOHkE[/)MPbYzs&@sSȾ|({i-岢D=NYoS3?ce^8{\st>_~ۉk?ƟW*cǎy~m[Cu Ga Vjߞ{icUVZ˵U/^+1Ygezyz Ԩ Y*gyel2:ީW;j/dM\;j?}]WbJIfUo~{]Q:c>xM4V<3O;a [F38Þ}YI8rHwvb^ΒrHK;WUW]n : $`N^pTW\bԤd;DҏKJ6ew ii!-Ozwv:@InWm2%ʊwm2Y,BK:^xE ӣժ7`\T]:iGv!J_Yq FbƟ҉nnѼVpK$waKO?Ɛݼ(a\x ˂S!9D}|ĔFCz, QJZ|HX"`%/PR'_ZH7xHQcn~ib4eeVJlEGu7xÉTbک).bO?[|*bҜ(=^{m'p }jHKK7Y%Y,itȁ4Ū*6lCzBLi\4 %#&]j:KZ7:/n"E!ҾWH \BÒ%Ku&pzQRJ=G?r>*=DtIk6MȒc&B)[ŠO>&-NhHS-+ #:GF"To"]_%= ?ȧRΫWԗlEZ`kxZ |m뭷vK֩X a ]y#U RrAH5ULlEZ'Mꫯ6$l{Z4Be\p'\%TiNi-;U*Fݨ!>Vzˉ~ʊ_hn(wߪ;nI폋>UggyXOv\[L'ҺS9kBzH$ֺqK'yh0G8iqCJ&N$Қ`wƒZ _8AmJś(kiPN7iD7xCVidKƷEEzՏPsQ֚rIbBc=ܼԍ?Qz5b'-q[d1fcIm qcdʇ-! 9)_\WC`2Y膣v,S7:!)"ve}:GWHHe.CJJGƑA(kD)?IxPwBBRywwҊE%mtG/~hA2PF2$g-D jEōDZԲΣUmC=J&JH.d"zKWק[OY8r}臛OJE-jDZRsMnzIb+-jUot>ԅCt_|b Y#?Y{JH#njq)iQ;YwG=)*`:/d"z("H4{u C!vKWt&@b~5"]#]mVU:wGOIqzԌ$ԲEG+tS">'-('8E=rD:.1W;S?KaQG>iToC|$ZHMސOO!ېRE:_b6S'*DZ+z-'W|DZ%,W|VzOZE%^↯Hx9|Zݕ`Ѹ9[zJ/:=1EZ&՛E%; 1uH+%'c$r{HWʬ_?D:ߖ0ȥ!WV嗖բWǴ`$cibHu^ կ~V#L47Z_W_ҽݑh$N"Tu3-d’&tșEYMiTJJ<|ҩnm|ҕ mE:jN%ZU,=Sq,VH稤rL6Znv9Fo\eF\nXkL6hk2Z,jц+5g!WI{ě\{2i3^{M"7:DcʼnV3J)/OJ?VvZڈ6PU A u6k%LmOS:F-^kH;$U6h1_'HպSKvG mXTѴ%)tk\UXXZD_2W%5}\Xn0DUB@"۵ݸKBu66n,l*DjKv*]oHij*tk\az=V4" $.D "Qñ 1(;iY~ S< ſЦxҦB{TѦvJ0h".DdWqnӡ72iT ; IDAT)EB8 _TSr(svtu!EZ]U+:5i7):1D¤I: I".D6dWqHruD.ze7^_ƌ(,p*D .$EһM*n<BިІ*A!;ei6N1i7%)ĕ&mj7lSt"-}hɮTGk*tkPȅzeD.yYVcmtHwcu!EZ vpe*ԧOnԍTMƍh*hSa}EZmmɮDZo$nB"P`%2Lg_D:jVZC|.H\a:V;tAvi*Tg.ܖtKwGM4)tkv3k<zE>nEޔpGф9MsV;=Mxf< ) p`[#6,<'̙3NL!A IE=zXmmOE/j,W@( t?5O(rt4l% И@IYQ+Ų(I.w?5>x~@ɼPY)O ͍+@Z,m @YpZnO(%A Hse=#PV"uTR\Ȟ@YۗԢ@ bSbQ bSnr5*@ًt?Wϋ>C {#҅S~R*@Et!+E!@ʼntLPJ@Hεs?h΅sII@Xv CY R T%Ƈ͵LI@M@SZáp) t4T2+g@rYȾwK &H71|+C!ot#yذa6rHmĈ\I@@3'W^i'pq^E6@@=UUUY}}d Dڃ"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ "EV@ D#"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ "EV@ D#"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ "EV@ D#"@FDY! i,BA iXd@=0Dy{<N8:ud@ "17Z* VZ٫jOVT0Dc[*#GuYǶzk;3V>Ȏ9{mVYg2BJٖ_l&Lv^xxYG}۵^k}vWX^uu\sε]vŖ,YbmڴP( D90|p[x?~\ r"~g3 "ZY۷9s4s6)H!H73%YX |@!jMˊ֭s4T.D:[t.|#  5D:5=i$߿?Vt!HgQɓ_~X2# =D:C=X=z4 Ȟ "={l8q͜9ϟo˗/ϾJ(KֹsgѣZ˲t $H>h>} 2zm]tVZ}][7oM6ƌczqqiJ@H:t`cǎEKo b mѢE6cƌ@\8wߥ\.!WW^ye,খ P5k֬}ڛo].ʢllʔ)s +@U]]]N=Ԋ0=K.-:=kj(U555SNڪ{KB_>}EE /WGv:F@.5X/ Ȏ@i0R)8)00)f@ '`P,i@p,irB V Œf@ D: GJf@ '`P,i@p,irB V Œf@ D: GJf@ '`P,i@p,irB k~G׿*u•.2{/Ho&;#Z\wGcIxiCQ3ϴܞzF2lڧ޵Z+bZtemF{xt ">[wuO?uo{m} .w-~{]~ xmڴqyO<ў{90`dwwiÇw17pCkm-P?;묳[ouG :Ԇ Ija]߻V}qKڷV[,XT~OoƵnpOxw}[+4h֭[UW]e9Jz:ntμliB[J И@NDzҥn&L;̙EpgجYp|֣G;WuYǎ;8wkoNw~۶v[{駝s{'M\zN%Zlu]mG.\hI_{NG]NH]c=nL'xFu־}{os1sFB[ow."DmUy}Q Z@ H/[̉NxGm4 - K.viVB)MMi}v۹?,Q]+{գ$Z}՝E '8agٚk+];MfoC9ڶm뮓>͑%}W{$=M+E5i$'7|sh3%Hu3-G7=^rGiAV./k1S $Z+M7~BU|rIo{JLW,lq-~ wV^xsMȒ0iqQ$R?ɴP'A),M6ĉ*V7=$Tr*];fM\jǏ龗7"Vf ߻wo֋]vw;nD zYO7b-Z'tuIKn(*+pozD҄HA1E:@%2)Ri@Kkhm(z_*^,Nf>@ D: GJI!H3% @Qt.RJ! ҅NH.Q->!E04, e1 DƄ&D4ǭ[H!H@Z3R1[bo"]H"]4CQ^ Ak]J"4saTUWW+eV”H)O@!hu%AWdA@\اO7o^(M^*B@'$q)B Pf͚U߷o_w,.J a.02e;0dG^G*?E@6ÊΆ"BNQgvE% ZO<3f^N~@ @HﲨOnC qvf$0kqPގ3z|@`DZeϞ=۝=sLSIHGڝޣG4@ 9ldF %@@= EV@ D#"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ "EV@ D#"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ "EV@ D#"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ "EV@ D#"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ "EV@ D#"@FDY! i,BA iXd@=0" !H{`D=`BH{"+ " ƅ 6ԩ-Xsǎ @H"H717&L`Cmۺ\˖-֭[_t5  3th?sk׮=ֵk׆sε]vŖ,YbmڴP0 D90|p[x?!Ax} @9%H7WtmΜ9Κݭ[7'X9"4[t8@Y@3@Yӓ&McEg,@teMO<!3A@3d(kcѣGΐ ҳg϶'̙3m|k$P]]m;w=zXmmu޽,I PHD裏ӧې!Cw֥KkժU!GELoyٴil̘1֫W/7n\A4tϞ=C6vXƱ-`<-Zd3f(x{hʅiY}] A_zEqWƢ/vj+cUfͪ۷Xe<,6̦L:_Щ TkSO-ҹKܢ31%PUSSS?uTjʷ,^~eӧ[T$A/qudǑcXc5L T+!BSlCÑRirB V Œf@ D: GJf@ '`P,i@p,irB V Œf@ D: GJf@ '`P,i@p,irB V Œf@ D: GJf@ '.ҏ<׿UVYClO?=H'u.ҹ{7tS2k|:M}u?3n7|wqv1؈#T .ՑF "-A!Qz饗x:kϚDz׶6w\[m\k׮dwOlkm _ u&￷VZ)%.!EJ(DZl?pb-3t`p {1{wꫯ}٧^j֭C_%*k:c[ңF2Tt'}m9Y7 .[n$X{]~ brO xbw4h{W]u/&W :,[gCaÆ>%՛9H6u3i\ &Mrb$~g?xtphH?+ʐxxv8bgbIEzv9&m:aT|w}vgجY=zK%޹Ht>O?y뭷_Ov饗̙3m|t#K7'm*giYmڴq\,hYq9ՒHn駟|h… ӊ3ر꫶onolDv"$g'6ip.]Ns|A˜mMn߶-ҹ_"N;J̓{G:a>]~5tBTo>'2"OU "qK֥|bbS,hd?G%пw~9ae-F̙ JqKZ.}/+ZI. -*HϞ=4Yb_/kq:XSSs"#M]L"-?ϝOvejaq6o ;c}EZ_l묳s1ȭ>ꨣܹ'p G"-שں(&SSM(YEa#9|NdD: Ho+P XMM{cr 3+b+r^Ds=׹7kzR8#\#a _~E@iM;?-Jq+]To>'2"OUBX=}pwůM6z휜tHIDATފЂmIl ߷VJ%@dqG)u0w Zx&L`o "B>餓܂dSn>!a?S\DFIʙ@Eaҷ !HH))i@pD9@sBCÑR D:'X)K900)K9@sBCÑR D:'X)K900)K9@sBCÑR D:'X)K90u̔#A f7!ʤ T*zj쳙ArJnۜ9s5-+[nNs:7)03D:iD+:pd&Hg0'MdNJ΀Y 0 9ʚ7xuE"'|b뭷^bS_IJ9.W"kۏcwTj暢u|wts!=DV*7|c|A']7pn~ӟN>#^{͵NgF/rD}uBK/_mhcMǎz&h߶_}?tP[}mĈv-ڷ{XeUlrm7G㏻xк1*YgezeztlN:$ov›lkX-X?ػQ U)?Ҷ?Պ_l?'*7:osu)i}iC_%ES.7^t?>J@&EZ\8 {gݏZb&kwȑ#'pzgIE9U% w#Ws9ǹdJ\ڶm%C#<}ݩS'WYf9a9]l?ޕ3`'Ҳ%&Lpt58[pa#wn^z'mM"/DZB&MMhڣ_yW t#ViխJ?Snx?GciScŝ wuWwg?xP54xHKo{Y78$6`Ig##HeFH{AٕW^|t|M+j\wuUu=d$eDRN\W]u)Ёx:s9 JNse_veNd5:[VU6-i$'x'kfiYzP(ʒ CD'Dr7![7wܱTiX AI?O~mMAIO0uJ'ҺIW_u6|s{7#>-w=uQ<޸V?x-JĒ(n͕ϲ^H9ҥKHP/B"]jUl=3K"K[%,޸H#q,(oI!H馛? YK$waKdn21$G7EZ"h\x՞<Љ,h=(%9WdI_a4~n2D'F^dG Q}Q绕$Jxq{c\GqD9:{o;Ul{}$pԍDedI)@n:KZ.("M1(Ģ)wۻ&Z6ls7QDO[|墹݁ TYҺyFo—[E7__/+׿5"!MɅCo}z뭝S֩X a",V-H!ԏ[Y^$zns=׹7$ kP q'\6*Y{YqK;t}*+B*k7_FH {뭷#ΊZ78/""׋^m{wG7ˤEZ _x _[7-Ym-bI{ƸYhRc=ꭄ+%Zx:Yl6YzA>=n+,l,i;qcdʇ-!qiՍDL| IKdu|zSNYq a\ڧc^|?#ӿ袋D=cLnYkD)?IxP?BBR;TnTSJ(Hdq+&C~sDc&YrA2+"]>#yLAa'3$HWt DLغHۈОR%Hy" W2R v5ccWSb.;ŞRЪDF6iWR"mF3DPBDahC9( N"T(DCPIuZaHIon")Nv"jWv,im(&Ţ@Ţ,I=[P[u\% pBjvj7vM mPsdI7ŧГ.P( N mۊmڊ r[(cm9JTۯ/_qG$ҊHu L-iK yTYEi6ڍ]ۡ-I!]F;OD;;031)i<6JݡpI| =AB <R6s%жk=K<isu0 $NMaV*Kq3 y_.uTkK Bmي0'+tG˅@QD8]P8_:T bi|ZeKLCEZd5+\-/)k|Ҥ8ڴ)K:O'("]r!P""TN'AԑNJWqN{ UHg4.Ҋ0=gUhVY;찃B6%Ҋ24 V7h;HS Hz\H'7@~ۨQܑV"G)2~xwS`i-<+e/_^{!]BЄ(ҽݑʧ.P( .4a a8RJ D)00)f@ '`P,i@p,irB V Œf@ z*֪U0%RJ튡I@vjjjHHA@)c Qe@ T+\i OcǍ< @%5kV} G%O0}c63QU(V zm 2M@6BbEgCk!'سgO^'`Q3E| Ȃْ 5c $hi]*~޽]t7D@¬Ak3f ʞ={ d3OIHG[&~P!IENDB`spamcheck-v1.10.1/examples/000077500000000000000000000000001444633272200155435ustar00rootroot00000000000000spamcheck-v1.10.1/examples/checkforspamissue.json000066400000000000000000000006031444633272200221530ustar00rootroot00000000000000{ "title": "My Title", "description": "lorem ipsum", "user_in_project": true, "project": { "project_id": 14, "project_path": "spamtest/hello" }, "user": { "emails": [{"email": "mr_stupendous@hotmail.com", "verified": true}], "username": "MrStupendous", "org": "GitLab" }, "created_at": "2021-01-01T10:00:00Z", "updated_at": "2021-01-01T11:00:00Z" } spamcheck-v1.10.1/examples/spam.json000066400000000000000000000012061444633272200173750ustar00rootroot00000000000000{ "title": "*livestreaming*! >swansea u23 vs west brom u23, <live'stream> (free)™", "description": "watch @free' online swansea u23 vs west brom u23 premier league 2™ live stream. \nlive tv channel [live-free]** swansea u23 vs west brom u23 live 2/3/2020 broadcast today uk. premier league 2 live tv", "user_in_project": false, "project": { "project_id": 14, "project_path": "spamtest/hello" }, "user": { "emails": [{"email": "mr_stupendous@hotmail.com", "verified": false}], "username": "MrStupendousaaaaa" }, "created_at": "2021-01-01T10:00:00Z", "updated_at": "2021-01-01T11:00:00Z" } spamcheck-v1.10.1/health_check.py000066400000000000000000000024631444633272200167060ustar00rootroot00000000000000#!/usr/bin/env python """ Health check CLI for spam service """ import argparse import os import grpc # from api.v1.spamcheck_pb2 import HealthCheckRequest, HealthCheckResponse # import api.v1.spamcheck_pb2_grpc as spam_grpc from api.v1.health_pb2 import HealthCheckRequest, HealthCheckResponse import api.v1.health_pb2_grpc as health_grpc SPAMCHECK_GRPC_ADDR = os.environ.get("SPAMCHECK_GRPC_ADDR", "0.0.0.0:8001") def check_liveness(): """Check to use for Kubernetes liveness probes""" with grpc.insecure_channel(SPAMCHECK_GRPC_ADDR) as channel: stub = health_grpc.HealthStub(channel) response = stub.Check(HealthCheckRequest()) if not isinstance(response, HealthCheckResponse): raise AttributeError(f"Unexpected response type: {type(response)}") if response.status == HealthCheckResponse.NOT_SERVING: raise RuntimeError("gRPC service not serving") def check_readiness(): """Check to use for Kubernetes readiness probes""" if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--liveness", action="store_true", default=False) parser.add_argument("--readiness", action="store_true", default=False) args = parser.parse_args() if args.liveness: check_liveness() if args.readiness: check_readiness() spamcheck-v1.10.1/main.py000077500000000000000000000001541444633272200152260ustar00rootroot00000000000000#!/usr/bin/env python """ Entry point for spamcheck service. """ from server import server server.serve() spamcheck-v1.10.1/ruby/000077500000000000000000000000001444633272200147065ustar00rootroot00000000000000spamcheck-v1.10.1/ruby/spamcheck.rb000066400000000000000000000003401444633272200171660ustar00rootroot00000000000000# This file is generated by generate-proto-ruby. Do not edit. $:.unshift(File.expand_path('../spamcheck', __FILE__)) require 'spamcheck/version' require 'spamcheck/spamcheck_pb' require 'spamcheck/spamcheck_services_pb' spamcheck-v1.10.1/ruby/spamcheck/000077500000000000000000000000001444633272200166445ustar00rootroot00000000000000spamcheck-v1.10.1/ruby/spamcheck/version.rb000066400000000000000000000005141444633272200206560ustar00rootroot00000000000000# Upgrade strategy # Version number is in the form Major.Minor.Patch # Increment the patch version when making backard compatible bug fixes # Increment the minor version when adding functionality (that is backward compatible) # Increment the major version when making incompatible changes module Spamcheck VERSION = '1.3.0' end spamcheck-v1.10.1/server/000077500000000000000000000000001444633272200152335ustar00rootroot00000000000000spamcheck-v1.10.1/server/__init__.py000066400000000000000000000000001444633272200173320ustar00rootroot00000000000000spamcheck-v1.10.1/server/interceptors.py000066400000000000000000000146611444633272200203360ustar00rootroot00000000000000"""Middleware for gRPC server""" import abc import time from typing import Any, Callable import grpc import ulid from app import logger log = logger.logger CORRELATION_ID_KEY = "x-gitlab-correlation-id" METRIC_API_CALLS = "spamcheck_api_calls" METRIC_API_FAILURES = "spamcheck_api_failures" # Metadata fields to include in log messages, if present _metadata_log_fields = {"user-agent", "retry"} # pylint: disable=abstract-method class SpamCheckContext(grpc.ServicerContext): """Custom implementation of a context object passed to method implementations.""" # there is no init in the super class # pylint: disable=super-init-not-called def __init__(self, context: grpc.ServicerContext): self._original_context = context self.correlation_id = ulid.new() def abort(self, code, details): self._original_context.abort(code, details) def abort_with_status(self, status): self._original_context.abort_with_status(status) def add_callback(self, callback): self._original_context.add_callback(callback) def auth_context(self): return self._original_context.auth_context() def cancel(self): self._original_context.cancel() def invocation_metadata(self): return self._original_context.invocation_metadata() def is_active(self): return self._original_context.is_active() def peer(self): return self._original_context.peer() def peer_identities(self): return self._original_context.peer_identities() def peer_identity_key(self): return self._original_context.peer_identity_key() def send_initial_metadata(self, initial_metadata): self._original_context.send_initial_metadata(initial_metadata) def set_code(self, code): self._original_context.set_code(code) def set_details(self, details): self._original_context.set_details(details) def set_trailing_metadata(self, trailing_metadata): self._original_context.abort(trailing_metadata) def time_remaining(self): return self._original_context.time_remaining() # ServerInterceptor code copied from: # https://github.com/d5h-foss/grpc-interceptor/blob/master/src/grpc_interceptor/server.py#L9 class ServerInterceptor(grpc.ServerInterceptor, metaclass=abc.ABCMeta): """Base class for server-side interceptors. To implement an interceptor, subclass this class and override the intercept method. """ @abc.abstractmethod def intercept( self, method: Callable, request: Any, context: grpc.ServicerContext, method_name: str, ) -> Any: """Override this method to implement a custom interceptor. You should call method(request, context) to invoke the next handler (either the RPC method implementation, or the next interceptor in the list). Args: method: Either the RPC method implementation, or the next interceptor in the chain. request: The RPC request, as a protobuf message. context: The ServicerContext pass by gRPC to the service. method_name: A string of the form "/protobuf.package.Service/Method" Returns: This should generally return the result of method(request, context), which is typically the RPC method response, as a protobuf message. The interceptor is free to modify this in some way, however. """ return method(request, context) # Implementation of grpc.ServerInterceptor, do not override. def intercept_service(self, continuation, handler_call_details): """Implementation of grpc.ServerInterceptor. This is not part of the grpc_interceptor.ServerInterceptor API, but must have a public name. Do not override it, unless you know what you're doing. """ next_handler = continuation(handler_call_details) # Only intercept if it's unary: if next_handler.request_streaming or next_handler.response_streaming: return next_handler def invoke_intercept_method(request, context): next_interceptor_or_implementation = next_handler.unary_unary method_name = handler_call_details.method return self.intercept( next_interceptor_or_implementation, request, context, method_name, ) return grpc.unary_unary_rpc_method_handler( invoke_intercept_method, request_deserializer=next_handler.request_deserializer, response_serializer=next_handler.response_serializer, ) # pylint: disable=too-few-public-methods class CorrelationIDInterceptor(ServerInterceptor): """Add correlation_id to gRPC requests.""" def intercept(self, method, request, context, method_name): ctx = SpamCheckContext(context) for item in context.invocation_metadata(): item = item._asdict() if item["key"].lower() == CORRELATION_ID_KEY: ctx.correlation_id = item["value"] break return method(request, ctx) # pylint: disable=too-few-public-methods class LoggingInterceptor(ServerInterceptor): """Logging interceptor for gRPC requests.""" def intercept(self, method, request, context, method_name): metadata = { "correlation_id": context.correlation_id, "failed": False, "method": method_name, "metric": METRIC_API_CALLS, "peer": context.peer(), } for item in context.invocation_metadata(): item = item._asdict() if item["key"] in _metadata_log_fields: metadata[item["key"]] = item["value"] start_time = time.time() try: out = method(request, context) # Catch and log any unhandled excecptions processing requests # pylint: disable=broad-except except Exception as ex: metadata["failed"] = True fields = { "metric": METRIC_API_FAILURES, "correlation_id": context.correlation_id, } log.error(str(ex), extra=fields) context.set_code(grpc.StatusCode.INTERNAL) context.set_details("server error") raise finally: metadata["request_latency_milliseconds"] = (time.time() - start_time) * 1000 log.info("Processed Request", extra=metadata) return out spamcheck-v1.10.1/server/server.py000066400000000000000000000107761444633272200171260ustar00rootroot00000000000000""" gRPC server for spamcheck service. """ from concurrent import futures import grpc from grpc_reflection.v1alpha import reflection from vyper import v import api.v1.health_pb2 as health import api.v1.health_pb2_grpc as health_grpc import api.v1.spamcheck_pb2 as spam import api.v1.spamcheck_pb2_grpc as spam_grpc from app import logger, ValidationError from app.spammable import generic, issue, snippet from server.interceptors import LoggingInterceptor, CorrelationIDInterceptor log = logger.logger # The method names can't be camel case due to generated gRPC code. # # pylint: disable=invalid-name class SpamCheckServicer(spam_grpc.SpamcheckServiceServicer): """Handler for gRPC routes.""" def CheckForSpamGeneric( self, request: spam.Generic, context: grpc.ServicerContext ) -> spam.SpamVerdict: """Route for generic spam.""" return generic.Generic(request, context).verdict() def CheckForSpamIssue( self, request: spam.Issue, context: grpc.ServicerContext ) -> spam.SpamVerdict: """Route for issue spam.""" try: return issue.Issue(request, context).verdict() except ValidationError as ex: fields = { "metric": "spamcheck_validation_errors", "correlation_id": context.correlation_id, } log.warning("Invalid issue", extra=fields) context.set_code(grpc.StatusCode.INVALID_ARGUMENT) context.set_details(str(ex)) return spam.SpamVerdict() def CheckForSpamSnippet( self, request: spam.Snippet, context: grpc.ServicerContext ) -> spam.SpamVerdict: """Route for snippet spam.""" try: return snippet.Snippet(request, context).verdict() except ValidationError as ex: fields = { "metric": "spamcheck_validation_errors", "correlation_id": context.correlation_id, } log.warning("Invalid snippet", extra=fields) context.set_code(grpc.StatusCode.INVALID_ARGUMENT) context.set_details(str(ex)) return spam.SpamVerdict() class HealthServicer(health_grpc.HealthServicer): """Handler for gRPC health check.""" def Check(self, request: health.HealthCheckRequest, context: grpc.ServicerContext): """Urnary health check method""" return health.HealthCheckResponse(status=health.HealthCheckResponse.SERVING) def Watch(self, request, context): """Streaming health check method""" yield health.HealthCheckResponse(status=health.HealthCheckResponse.SERVING) def _fetch_certificate(): tls_certificate = v.get_string("tls_certificate") tls_private_key = v.get_string("tls_private_key") with open(tls_private_key, "rb") as file: private_key = file.read() with open(tls_certificate, "rb") as file: certificate_chain = file.read() return (private_key, certificate_chain) def _server(addr: str, tls: bool) -> grpc.Server: interceptors = [CorrelationIDInterceptor(), LoggingInterceptor()] server = grpc.server( futures.ThreadPoolExecutor(max_workers=50), interceptors=interceptors ) if tls: creds = grpc.ssl_server_credentials( ((_fetch_certificate()),), root_certificates=None, require_client_auth=False, ) server.add_secure_port(addr, creds) else: log.warning("TLS certificates not found. Defaulting to insecure channel") server.add_insecure_port(addr) return server def serve(): """Start the gRPC server.""" env = v.get_string("env") tls_enabled = v.get_bool("tls_enabled") addr = v.get_string("grpc_addr") if addr.isdecimal(): addr = f"0.0.0.0:{addr}" server = _server(addr, tls_enabled) spam_grpc.add_SpamcheckServiceServicer_to_server(SpamCheckServicer(), server) health_grpc.add_HealthServicer_to_server(HealthServicer(), server) # Disable gRPC reflection in production environments if env != "production": service_names = ( spam.DESCRIPTOR.services_by_name["SpamcheckService"].full_name, reflection.SERVICE_NAME, ) reflection.enable_server_reflection(service_names, server) server.start() log.info("gRPC server started", extra={"tls_enabled": tls_enabled, "addr": addr}) try: server.wait_for_termination() except KeyboardInterrupt: log.info("shutting down gRPC server") server.stop(None) if __name__ == "__main__": serve() spamcheck-v1.10.1/spamcheck.gemspec000066400000000000000000000014761444633272200172400ustar00rootroot00000000000000# coding: utf-8 prefix = 'ruby' $LOAD_PATH.unshift(File.expand_path(File.join(prefix), __dir__)) require 'spamcheck/version' Gem::Specification.new do |spec| spec.name = "spamcheck" spec.version = Spamcheck::VERSION spec.authors = ["GitLab Security Automation"] spec.email = ["security-automation@gitlab.com"] spec.summary = %q{Auto-generated gRPC client for Spamcheck} spec.description = %q{Auto-generated gRPC client for Spamcheck.} spec.homepage = "https://gitlab.com/gitlab-org/gl-security/security-engineering/security-automation/spam/spamcheck" spec.license = "MIT" spec.files = `find ./#{prefix}`.split("\n").reject { |f| f.match(%r{^#{prefix}/(test|spec|features)/}) } spec.require_paths = [prefix] spec.add_dependency "grpc", "~> 1.0" end spamcheck-v1.10.1/tests/000077500000000000000000000000001444633272200150675ustar00rootroot00000000000000spamcheck-v1.10.1/tests/README.md000066400000000000000000000003321444633272200163440ustar00rootroot00000000000000# /tests This directory should store tests that are not defined as Unit Tests. Examples - Test REST API layer logic, by running a "real" service. - Everything that can be classified as Integration/Functional tests. spamcheck-v1.10.1/tests/__init__.py000066400000000000000000000000001444633272200171660ustar00rootroot00000000000000spamcheck-v1.10.1/tests/app/000077500000000000000000000000001444633272200156475ustar00rootroot00000000000000spamcheck-v1.10.1/tests/app/__init__.py000066400000000000000000000000001444633272200177460ustar00rootroot00000000000000spamcheck-v1.10.1/tests/app/data_store_test.py000066400000000000000000000035571444633272200214170ustar00rootroot00000000000000import unittest from unittest.mock import patch, PropertyMock from app import data_store class TestDataStore(unittest.TestCase): @patch.object(data_store.DataStore, "_monitor_objects") @patch('app.data_store._write_to_gcs') def test_save(self, mock_write, mock_monitor): def add_ham(): # Adding two seen "ham" object to the internal store ds._questionable_ham[0.1] = {"a": 1} ds._questionable_ham[0.2] = {"b": 2} ds = data_store.DataStore(spammable_type="foobar") # save a spam object ds.save(spammable={}, confidence=ds.SPAM_HAM_THRESHOLD + 0.1) self.assertEqual(-1, ds._ham_spam_count, "The spam ham count was not decremented") # Save another spam, this should save the two ham objects add_ham() ds.save(spammable={}, confidence=ds.SPAM_HAM_THRESHOLD + 0.1) self.assertEqual(0, ds._ham_spam_count, "The spam ham count should be 0") mock_write.assert_any_call(ds.gcs_file_path, {"a": 1}) mock_write.assert_any_call(ds.gcs_file_path, {"b": 2}) # Set the counts so there is an imbalance of ham in the labeled dataset ds._ham_spam_count = -2 ds._labeled_count = 10 add_ham() ds.save(spammable={}, confidence=ds.SPAM_HAM_THRESHOLD + 0.1) self.assertEqual(-3, ds._ham_spam_count, "We should not have saved any ham") # Set the counts so there is an imbalance of spam in the labeled dataset ds._ham_spam_count = 1 ds._labeled_count = -5 add_ham() ds.save(spammable={}, confidence=ds.SPAM_HAM_THRESHOLD + 0.1) self.assertEqual(0, ds._ham_spam_count, "Score should have decremented by one when _ham_spam_count is positive and spam was saved") self.assertEqual(-3, ds._labeled_count, "We should have saved two ham issues and incremented the _labeled_count") spamcheck-v1.10.1/tests/app/helpers.py000066400000000000000000000004171444633272200176650ustar00rootroot00000000000000class MockContext: def __init__(self): self.correlation_id = "test" class MockML: def __init__(self, score): self._score = score def score(self, args): return self._score def set_score(self, score): self._score = score spamcheck-v1.10.1/tests/app/spammable/000077500000000000000000000000001444633272200176105ustar00rootroot00000000000000spamcheck-v1.10.1/tests/app/spammable/__init__.py000066400000000000000000000000001444633272200217070ustar00rootroot00000000000000spamcheck-v1.10.1/tests/app/spammable/generic_test.py000066400000000000000000000071001444633272200226330ustar00rootroot00000000000000import unittest from unittest.mock import patch, PropertyMock import api.v1.spamcheck_pb2 as spam from app import config from app.spammable import generic from tests.app.helpers import MockContext, MockML class TestGeneric(unittest.TestCase): def test_generic_attrs(self): obj = spam.Generic(text="test", type="random_generic") g = generic.Generic(obj, MockContext()) to_dict = g.to_dict() self.assertEqual( "test", to_dict["description"], "Generic description not set correctly" ) self.assertEqual( "random_generic", g.type(), "Generic type not set correctly" ) def test_verdict_project_not_allowed(self): g = generic.Generic(spam.Generic(text="test"), MockContext()) g._project_allowed = False v = g.verdict() self.assertEqual( spam.SpamVerdict.NOOP, v.verdict, "Disallowed project should return NOOP" ) def test_score(self): g = generic.Generic(spam.Generic(text="test"), MockContext()) self.assertEqual( spam.SpamVerdict.ALLOW, g.calculate_verdict(0.39), "Confidence less than 0.4 should be allowed", ) self.assertEqual( spam.SpamVerdict.ALLOW, g.calculate_verdict(0.41), "Confidence between 0.4 and 0,5 should be allowed", ) self.assertEqual( spam.SpamVerdict.ALLOW, g.calculate_verdict(0.55), "Confidence between 0.5 and 0.9 should be allowed", ) self.assertEqual( spam.SpamVerdict.ALLOW, g.calculate_verdict(0.9), "Confidence of 0.9 or greater should be allowed", ) def test_verdict(self): generic.classifier = MockML(1.0) g = generic.Generic(spam.Generic(text="test"), MockContext()) g._project_allowed = False self.assertEqual( spam.SpamVerdict.NOOP, g.verdict().verdict, "Disallowed project should return NOOP", ) g._project_allowed = True g._email_allowed = True self.assertEqual( spam.SpamVerdict.ALLOW, g.verdict().verdict, "Allowed email should return ALLOW", ) g._email_allowed = False self.assertEqual( spam.SpamVerdict.ALLOW, g.verdict().verdict, "ML inference of 1.0 should be allowed", ) generic.classifier.set_score(0.1) self.assertEqual( spam.SpamVerdict.ALLOW, g.verdict().verdict, "ML inference of 0.1 should be allowed", ) def test_verdict_no_ml(self): generic.classifier = None g = generic.Generic(spam.Generic(text="test"), MockContext()) g.project_allowed = True self.assertEqual( spam.SpamVerdict.NOOP, g.verdict().verdict, "Generic ML not loaded should return NOOP", ) def test_generic_property(self): g = generic.Generic(spam.Generic(text="spam"), MockContext()) g.allowed_domains = {"gitlab.com"} self.assertEqual("spam", g.spammable.text, "Generic text should have been set") self.assertEqual(False, g._email_allowed, "Blank email should not be allowed") args = {"user": {"emails": [{"email": "test@gitlab.com", "verified": True}]}} new_generic = spam.Generic(**args) g.spammable = new_generic self.assertEqual( True, g._email_allowed, "Email should be allowed after updating issue", ) spamcheck-v1.10.1/tests/app/spammable/issue_test.py000066400000000000000000000065171444633272200223620ustar00rootroot00000000000000import unittest from unittest.mock import patch, PropertyMock import api.v1.spamcheck_pb2 as spam from app import config, ValidationError from app.spammable import issue from tests.app.helpers import MockContext, MockML class TestIssue(unittest.TestCase): def test_verdict_project_not_allowed(self): i = issue.Issue(spam.Issue(title="test"), MockContext()) i._project_allowed = False v = i.verdict() self.assertEqual( spam.SpamVerdict.NOOP, v.verdict, "Disallowed project should return NOOP" ) def test_score(self): s = issue.Issue(spam.Issue(title="test"), MockContext()) self.assertEqual( spam.SpamVerdict.ALLOW, s.calculate_verdict(0.39), "Confidence less than 0.4 should be allowed", ) self.assertEqual( spam.SpamVerdict.CONDITIONAL_ALLOW, s.calculate_verdict(0.41), "Confidence between 0.4 and 0,5 should be conditionally allowed", ) self.assertEqual( spam.SpamVerdict.CONDITIONAL_ALLOW, s.calculate_verdict(0.55), "Confidence between 0.5 and 0.9 should be disallowed", ) self.assertEqual( spam.SpamVerdict.CONDITIONAL_ALLOW, s.calculate_verdict(0.9), "Confidence of 0.9 or greater should be blocked", ) def test_verdict(self): issue.classifier = MockML(1.0) i = issue.Issue(spam.Issue(title="test"), MockContext()) i._project_allowed = False self.assertEqual( spam.SpamVerdict.NOOP, i.verdict().verdict, "Disallowed project should return NOOP", ) i._project_allowed = True i._email_allowed = True self.assertEqual( spam.SpamVerdict.ALLOW, i.verdict().verdict, "Allowed email should return ALLOW", ) i._email_allowed = False self.assertEqual( spam.SpamVerdict.CONDITIONAL_ALLOW, i.verdict().verdict, "ML inference of 1.0 should be conditionally allowed", ) issue.classifier.set_score(0.1) self.assertEqual( spam.SpamVerdict.ALLOW, i.verdict().verdict, "ML inference of 0.1 should be allowed", ) def test_verdict_no_ml(self): issue.classifier = None i = issue.Issue(spam.Issue(title="test"), MockContext()) i.project_allowed = True self.assertEqual( spam.SpamVerdict.NOOP, i.verdict().verdict, "Issue ML not loaded should return NOOP", ) def test_issue_property(self): i = issue.Issue(spam.Issue(title="spam"), MockContext()) i.allowed_domains = {"gitlab.com"} self.assertEqual("spam", i.spammable.title, "Issue should have been set") self.assertEqual(False, i._email_allowed, "Blank email should not be allowed") args = {"user": {"emails": [{"email": "test@gitlab.com", "verified": True}]}} new_issue = spam.Issue(**args) i.spammable = new_issue self.assertEqual( True, i._email_allowed, "Email should not be allowed after updating issue", ) def test_validation(self): self.assertRaises(ValidationError, issue.Issue, spam.Issue(), MockContext()) spamcheck-v1.10.1/tests/app/spammable/snippet_test.py000066400000000000000000000060251444633272200227060ustar00rootroot00000000000000import unittest from unittest.mock import patch, PropertyMock import api.v1.spamcheck_pb2 as spam from app import config, ValidationError from app.spammable import snippet from tests.app.helpers import MockContext, MockML class TestSnippet(unittest.TestCase): def test_verdict_project_not_allowed(self): s = snippet.Snippet(spam.Snippet(title="test"), MockContext()) s._project_allowed = False v = s.verdict() self.assertEqual( spam.SpamVerdict.NOOP, v.verdict, "Disallowed project should return NOOP" ) def test_score(self): s = snippet.Snippet(spam.Snippet(title="test"), MockContext()) self.assertEqual( spam.SpamVerdict.ALLOW, s.calculate_verdict(0.39), "Confidence less than 0.4 should be allowed", ) self.assertEqual( spam.SpamVerdict.CONDITIONAL_ALLOW, s.calculate_verdict(0.41), "Confidence between 0.4 and 0,5 should be conditionally allowed", ) # Due to valse positives all non allow verdicts were converted to `CONDITIONAL_ALLOW` # https://gitlab.com/gitlab-com/gl-security/engineering-and-research/automation-team/spam/spamcheck/-/issues/191 self.assertEqual( spam.SpamVerdict.CONDITIONAL_ALLOW, s.calculate_verdict(0.55), "Confidence between 0.5 and 0.9 should be conditionally allowed", ) self.assertEqual( spam.SpamVerdict.CONDITIONAL_ALLOW, s.calculate_verdict(0.9), "Confidence of 0.9 or greater should be conditionally allowed", ) def test_verdict(self): snippet.classifier = MockML(1.0) s = snippet.Snippet(spam.Snippet(title="test"), MockContext()) s._project_allowed = False self.assertEqual( spam.SpamVerdict.NOOP, s.verdict().verdict, "Disallowed project should return NOOP", ) s._project_allowed = True s._email_allowed = True self.assertEqual( spam.SpamVerdict.ALLOW, s.verdict().verdict, "Allowed email should return ALLOW", ) s._email_allowed = False self.assertEqual( spam.SpamVerdict.CONDITIONAL_ALLOW, s.verdict().verdict, "ML inference of 1.0 should be conditionally allowed", ) snippet.classifier.set_score(0.1) self.assertEqual( spam.SpamVerdict.ALLOW, s.verdict().verdict, "ML inference of 0.1 should be allowed", ) def test_verdict_no_ml(self): snippet.classifier = None s = snippet.Snippet(spam.Snippet(title="test"), MockContext()) s.project_allowed = True self.assertEqual( spam.SpamVerdict.NOOP, s.verdict().verdict, "Snippet ML not loaded should return NOOP", ) def test_validation(self): self.assertRaises( ValidationError, snippet.Snippet, spam.Snippet(), MockContext() ) spamcheck-v1.10.1/tests/app/spammable/spammable_test.py000066400000000000000000000077751444633272200232020ustar00rootroot00000000000000import unittest from unittest.mock import patch import api.v1.spamcheck_pb2 as spam from vyper import v from app.spammable import Spammable from app.spammable.issue import Issue from tests.app.helpers import MockContext class TestSpam(unittest.TestCase): mock_issue = { "title": "mock spam", "description": "mock spam", "user_in_project": True, "project": { "project_id": 555, "project_path": "mock/spmmable", }, "user": { "emails": [{"email": "test@example.com", "verified": True}], "username": "test", "org": "test", "id": 23, "abuse_metadata": { "account_age": 3, "spam_score": 0.32, }, }, } def test_project_allowed(self): mock_project = {123: "spamtest/hello"} s = Issue(spam.Issue(**self.mock_issue), None) allowed = s.project_allowed(555) self.assertTrue(allowed, "Empty allow and deny list should return True") s.allow_list = mock_project allowed = s.project_allowed(555) self.assertFalse(allowed, "Project not in allow_list should return False") allowed = s.project_allowed(123) self.assertTrue(allowed, "Project in allow_list should return True") s.allow_list = {} s.deny_list = mock_project allowed = s.project_allowed(555) self.assertTrue(allowed, "Project not in deny_list should return True") allowed = s.project_allowed(123) self.assertFalse(allowed, "Project in deny_list should return False") def test_email_allowed(self): def set_emails(email, verified): args = {"user": {"emails": [{"email": email, "verified": verified}]}} i = spam.Issue(**args) return i.user.emails s = Issue(spam.Issue(**self.mock_issue), None) Spammable.allowed_domains = {"gitlab.com"} emails = set_emails("integrationtest@example.com", True) allowed = s.email_allowed(emails) self.assertFalse(allowed, "Non gitlab email should not be allowed") emails = set_emails("integrationtest@gitlab.com", True) allowed = s.email_allowed(emails) self.assertTrue(allowed, "Verified gitlab email should be allowed") emails = set_emails("integrationtest@gitlab.com", False) allowed = s.email_allowed(emails) self.assertFalse(allowed, "Non-verified gitlab email should not be allowed") def test_user_has_id(self): s = spam.Issue(**self.mock_issue) self.assertEqual(23, s.user.id, "User ID does not match") def test_abuse_metadata(self): s = spam.Issue(**self.mock_issue) error_msg = "Abuse metadata has unepected value" self.assertEqual(3, s.user.abuse_metadata["account_age"], error_msg) self.assertAlmostEqual(0.32, s.user.abuse_metadata["spam_score"], msg=error_msg) def test_to_dict(self): spammable = Issue(spam.Issue(**self.mock_issue), MockContext()).to_dict() error_msg = "Expected field not seen in spammable dictionary" self.assertTrue("user" in spammable, error_msg) self.assertTrue("title" in spammable, error_msg) self.assertTrue("description" in spammable, error_msg) self.assertTrue("project" in spammable, error_msg) self.assertTrue("userInProject" in spammable, error_msg) user = spammable["user"] self.assertTrue("username" in user, error_msg) self.assertTrue("id" in user, error_msg) self.assertTrue("abuseMetadata" in user, error_msg) self.assertTrue("emails" in user, error_msg) self.assertTrue("org" in user, error_msg) def test_max_verdict(self): self.assertEqual(spam.SpamVerdict.CONDITIONAL_ALLOW, Issue.max_verdict, "Unexpected maximum verdict for Issue") v.set("max_issue_verdict", "ALLOW") Issue.set_max_verdict() self.assertEqual(spam.SpamVerdict.ALLOW, Issue.max_verdict, "Unexpected maximum verdict for Issue") spamcheck-v1.10.1/tests/gem/000077500000000000000000000000001444633272200156375ustar00rootroot00000000000000spamcheck-v1.10.1/tests/gem/test.sh000077500000000000000000000004661444633272200171630ustar00rootroot00000000000000#!/bin/bash set -euo pipefail IFS=$'\n\t' kill_server() { kill $SPAMCHECK_PID } gem uninstall spamcheck gem install ./spamcheck*.gem python main.py --log-level debug & SPAMCHECK_PID=$! trap kill_server EXIT # Wait for service to start sleep 3 LOCAL_TESTING=TRUE ruby ./tests/integration/run.rb exit 0 spamcheck-v1.10.1/tests/integration/000077500000000000000000000000001444633272200174125ustar00rootroot00000000000000spamcheck-v1.10.1/tests/integration/run.rb000066400000000000000000000155661444633272200205600ustar00rootroot00000000000000# frozen_string_literal: true # Tests here are meant to run against the dev deployment of spamcheck # see https://gitlab.com/gitlab-private/gl-security/engineering-and-research/automation-team/kubernetes/spamcheck/spamcheck-py/-/blob/main/dev/config/config.yaml # for the filters that are applied. require 'test/unit' require 'grpc' require 'spamcheck' LOCAL_TEST = ENV['LOCAL_TESTING'].freeze unless LOCAL_TEST abort 'SPAMCHECK_HOSTNAME not set' unless ENV['SPAMCHECK_HOSTNAME'] abort 'SPAMCHECK_API_TOKEN not set' unless ENV['SPAMCHECK_API_TOKEN'] end class TestSpamcheck < Test::Unit::TestCase def setup host = ENV['SPAMCHECK_HOSTNAME'] jwt = ENV['SPAMCHECK_API_TOKEN'] tls_creds = GRPC::Core::ChannelCredentials.new(File.read('/etc/ssl/certs/ca-certificates.crt')) auth_proc = proc { { 'authorization' => jwt } } jwt_creds = GRPC::Core::CallCredentials.new(auth_proc) credentials = tls_creds.compose(jwt_creds) if LOCAL_TEST testing_stub = Spamcheck::SpamcheckService::Stub.new('localhost:8001', :this_channel_is_insecure) @auth_stub = testing_stub @unauth_stub = testing_stub else @auth_stub = Spamcheck::SpamcheckService::Stub.new(host, credentials) @unauth_stub = Spamcheck::SpamcheckService::Stub.new(host, tls_creds) end end # Check that authentication is required def test_no_authentication return if LOCAL_TEST issue = Spamcheck::Issue.new assert_raise(GRPC::PermissionDenied) { @unauth_stub.check_for_spam_issue(issue) } end # Check that an invalid issue returns an error def test_invalid_issue issue = Spamcheck::Issue.new(description: 'test issue') exception = assert_raise(GRPC::InvalidArgument) { @auth_stub.check_for_spam_issue(issue) } assert_match(/Issue title is required/, exception.message) end # Check that project not allowed is NOOP def test_project_not_allowed issue = Spamcheck::Issue.new( title: 'test issue', description: 'test issue', project: { project_id: 1, project_path: 'test/project' } ) resp = @auth_stub.check_for_spam_issue(issue) verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict) if LOCAL_TEST assert_equal(::Spamcheck::SpamVerdict::Verdict::ALLOW, verdict, 'Ham issue should be ALLOWED') assert_equal('ml inference score', resp.reason) assert_equal(true, resp.evaluated) else assert_equal(::Spamcheck::SpamVerdict::Verdict::NOOP, verdict, 'Issue with project not in allow list should be NOOP') assert_equal('project not allowed', resp.reason) assert_equal(false, resp.evaluated) end end # Check that ham is allowed def test_generic_ham issue = Spamcheck::Generic.new( text: 'Dependency update needed. The dependencies for this application are outdated and need to be updated.', project: { project_id: 278_964, project_path: 'gitlab-org/gitlab' }, type: 'ham_type', user: { id: 17, abuse_metadata: { account_age: 50, spam_score: 0.02 } } ) resp = @auth_stub.check_for_spam_issue(issue) verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict) assert_equal(::Spamcheck::SpamVerdict::Verdict::ALLOW, verdict, 'Ham issue verdict not "ALLOW"') assert_equal('ml inference score', resp.reason) assert_equal(Float, resp.score.class) assert_equal(true, resp.evaluated) end # Check that spam is blocked def test_generic_spam issue = Spamcheck::Generic.new( text: 'watch fifa live stream best live streaming [here](https://livestream.com)', project: { project_id: 278_964, project_path: 'gitlab-org/gitlab' }, type: 'spam_type', user: { id: 23, abuse_metadata: { account_age: 3, spam_score: 0.62 } } ) resp = @auth_stub.check_for_spam_issue(issue) verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict) assert_equal(::Spamcheck::SpamVerdict::Verdict::CONDITIONAL_ALLOW, verdict, 'Spam issue verdict not "CONDITIONAL_ALLOW"') assert_equal('ml inference score', resp.reason) assert_equal(Float, resp.score.class) assert_equal(true, resp.evaluated) end # Check that ham is allowed def test_issue_ham issue = Spamcheck::Issue.new( title: 'Dependency update needed', description: 'The dependencies for this application are outdated and need to be updated.', project: { project_id: 278_964, project_path: 'gitlab-org/gitlab' } ) resp = @auth_stub.check_for_spam_issue(issue) verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict) assert_equal(::Spamcheck::SpamVerdict::Verdict::ALLOW, verdict, 'Ham issue verdict not "ALLOW"') assert_equal('ml inference score', resp.reason) assert_equal(Float, resp.score.class) assert_equal(true, resp.evaluated) end # Check that spam is blocked def test_issue_spam issue = Spamcheck::Issue.new( title: 'watch fifa live stream', description: 'best live streaming [here](https://livestream.com)', project: { project_id: 278_964, project_path: 'gitlab-org/gitlab' } ) resp = @auth_stub.check_for_spam_issue(issue) verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict) assert_equal(::Spamcheck::SpamVerdict::Verdict::CONDITIONAL_ALLOW, verdict, 'Spam issue verdict not "CONDITIONAL_ALLOW"') assert_equal('ml inference score', resp.reason) assert_equal(Float, resp.score.class) assert_equal(true, resp.evaluated) end # Check that snippet ham is allowed def test_snippet_ham issue = Spamcheck::Snippet.new( title: 'Example SQL queries', description: '', project: { project_id: 278_964, project_path: 'gitlab-org/gitlab' }, files: [{ path: 'snippetfile1.txt' }] ) resp = @auth_stub.check_for_spam_snippet(issue) verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict) assert_equal(::Spamcheck::SpamVerdict::Verdict::ALLOW, verdict, 'Ham snippet verdict not "ALLOW"') assert_equal('ml inference score', resp.reason) assert_equal(Float, resp.score.class) assert_equal(true, resp.evaluated) end # Check that snippet spam is blocked def test_snippet_spam issue = Spamcheck::Snippet.new( title: 'Slot Online', description: '', project: { project_id: 278_964, project_path: 'gitlab-org/gitlab' }, files: [{ path: 'snippetfile1.txt' }] ) issue.files << Spamcheck::File.new(path: 'snippetfile1.txt') resp = @auth_stub.check_for_spam_snippet(issue) verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict) assert_equal(::Spamcheck::SpamVerdict::Verdict::CONDITIONAL_ALLOW, verdict, 'Spam snippet verdict not "CONDITIONAL_ALLOW"') assert_equal('ml inference score', resp.reason) assert_equal(Float, resp.score.class) assert_equal(true, resp.evaluated) end end