From 8fb1f900364600fc19fbebe861b695fae44870dd Mon Sep 17 00:00:00 2001 From: Luxferre Date: Sun, 26 Feb 2023 00:19:35 +0200 Subject: [PATCH] init --- .dockerignore | 6 + .gitignore | 3 + COPYING | 26 +++ Dockerfile | 12 ++ README.md | 279 ++++++++++++++++++++++++++ example-config/actions/echobeep.py | 38 ++++ example-config/clips/beep.wav | Bin 0 -> 4044 bytes example-config/config.yaml | 46 +++++ fvx.py | 277 +++++++++++++++++++++++++ pyVoIP-1.6.4.patched-py3-none-any.whl | Bin 0 -> 74034 bytes requirements.txt | 3 + 11 files changed, 690 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 COPYING create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 example-config/actions/echobeep.py create mode 100644 example-config/clips/beep.wav create mode 100644 example-config/config.yaml create mode 100644 fvx.py create mode 100644 pyVoIP-1.6.4.patched-py3-none-any.whl create mode 100644 requirements.txt diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0291af6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +__pycache__ +*.pyc +private-config +example-config +compose.yaml +README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dea2bd4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +__pycache__ +*.pyc +private-config diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..23274a6 --- /dev/null +++ b/COPYING @@ -0,0 +1,26 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 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. + +For more information, please refer to + + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..455eb70 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.10-slim-bullseye +USER root +WORKDIR /usr/src/app +RUN sed -i -e's/ main/ main contrib non-free/g' /etc/apt/sources.list +RUN apt-get update -y && apt-get install -y libttspico-utils espeak-ng espeak-ng-data espeak-ng-espeak flite sox +COPY requirements.txt ./ +COPY pyVoIP-1.6.4.patched-py3-none-any.whl ./ +RUN pip install --no-cache-dir -r requirements.txt +COPY fvx.py ./ +VOLUME /opt/config +WORKDIR /opt/config +ENTRYPOINT ["python", "-u", "/usr/src/app/fvx.py", "/opt/config/config.yaml"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..c5c5782 --- /dev/null +++ b/README.md @@ -0,0 +1,279 @@ +# FrugalVox + +A tiny VoIP IVR framework by hackers and for hackers. + +## Features + +- Small and nimble: the kernel is a single Python 3 file (~250 SLOC), and the configuration is a single YAML file +- Hackable: the kernel is well-commented and not so big, all the actions are full-featured Python scripts +- Written with plain telephony in mind, supporting both out-of-band and in-band DTMF command detection +- Comes with PIN-based authentication and action access control out of the box (optional but recommended) +- Comes with TTS integration out of the box, configured for eSpeakNG by default (optional) +- Container-ready +- Released into public domain + +## Limitations + +- Only UDP transport is supported for now (pyVoIP limitation) +- Only a single SIP server registration per instance is supported by design (if you need to receive incoming calls via multiple SIP accounts, you must spin up multiple FrugalVox instances) +- The format for top-level action commands is hardcoded (see the workflow section of this README) + +## Running FrugalVox as a host application + +### Dependencies + +- Python 3.8 or higher (3.10 recommended) +- pyVoIP 1.6.4, patched according to [this comment](https://github.com/tayler6000/pyVoIP/issues/107#issuecomment-1440231926) (also available in a `.whl` file in this repo) +- NumPy (mandatory, required for DTMF detection) +- eSpeakNG and SoX(optional but required for the default TTS engine configuration) + +### Usage + +Just run `python /path/to/fvx.py [your-config.yaml]`. Press Ctrl+C or otherwise terminate the process when you don't need it. + +Make sure your `python` command is pointing to Python 3.8 or higher. + +## Running FrugalVox in Docker + +The Docker image encapsulates all the dependencies (including Python 3.10, three different TTS engines (see the FAQ section), SoX and the patched pyVoIP package) but requires you to provide all the configuration and action scripts in a volume mounted from the host. In addition to this, the configuration file itself must be called `config.yaml` since the container is only going to be looking for this name. + +### Building + +From the source directory, run: `docker build -t frugalvox .` and the image called `frugalvox` will be built. + +### Running from the command line + +The command to run the `frugalvox` image locally is: + +``` +docker run -d --rm -v /path/to/configdir:/opt/config --name frugalvox frugalvox +``` + +Note that the `/path/to/configdir` must be absolute. Use `$(pwd)` command to get the current working directory if your configuration directory path is relative to it. + +### Running from Docker Compose + +Add this to your `compose.yaml`, replacing `$PWD/example-config` with your configuration direcory path: + +```yaml +services: + # ... other services here ... + frugalvox: + image: frugalvox:latest + container_name: frugalvox + restart: on-failure:10 + volumes: + - "$PWD/example-config:/opt/config" +``` + +Then, on the next `docker compose up -d` run, the FrugalVox container should be up. Note that you can attach this service to any network you already created in Compose file, as long as it allows the containers to have Internet access. + +## Typical FrugalVox workflow + +Without auth: + +1. User calls the IVR. +2. User is prompted for the command. +3. User enters the DTMF command in the form `[action ID]*[param1]*[param2]*...#`. +4. The IVR checks the action ID. If it exists, the corresponding action script is run. If it doesn't, an error message is played back to the user. +5. User is prompted for the next command, and so on. + +With auth (recommended): + +1. User calls the IVR. +2. User is prompted for the PIN (internal user ID) followed by `#` key. +3. The IVR checks the PIN. If it doesn't exist in the user list, the caller is warned and the call is terminated. Otherwise, go to the next step. +4. User is prompted for the command. +5. User enters the DTMF command in the form `[action ID]*[param1]*[param2]*...#`. +6. The IVR checks the action ID. If it exists in the list of actions allowed for the user, the corresponding action script is run. If it doesn't, an error message is played back to the user. +7. User is prompted for the next command, and so on. + +## Configuration + +All FrugalVox configuration is done in a single YAML file that's passed to the kernel on the start. If no file is passed, FrugalVox will look for `config.yaml` file in the current working directory. + +The entire config is done in four different sections of the YAML file: `sip`, `tts`, `clips` and `ivr`. + +### SIP client configuration: `sip` + +This section lets you configure which SIP server FrugalVox will connect to in order to start receiving incoming calls. The fields are: + +- `sip.host`: your SIP provider hostname or IP address +- `sip.port`: your SIP provider port number (usually 5060) +- `sip.transport`: optional, not used for now, added here for the forward compatibility with future versions (the only supported transport is now `udp`) +- `sip.username`: your SIP account auth username (only the username itself, no domain or URI parts) +- `sip.password`: your SIP account auth password +- `sip.rtpPortLow` and `sip.rtpPortHigh`: your UDP port range for RTP communication, must match the port range opened in Docker if using the image, usually the default of 10000 and 20000 is fine + +All the fields in this section, except `transport`, are currently mandatory. If unsure about `rtpPortLow` and `rtpPortHigh`, just leave the values provided in the example config. + +### Text-to-speech engine settings: `tts` + +This section allows you to configure your TTS engine, for FrugalVox to be able to generate audio clips from your text. The fields are: + +- `tts.voice`: the name of the voice supported by your TTS engine (eSpeakNG by default) +- `tts.rate`: words per minute speech rate of the voice +- `tts.volume`: voice volume (0 to 200 for eSpeakNG) +- `tts.pitch`: voice pitch (check with your TTS program which one is optimal for you, 60 is the default in the example config) +- `tts.cmd`: a dictionary with the TTS synth and transcoder command templates, please just leave the default values there unless you want to switch to a different TTS engine other than eSpeakNG or a different encoder other than SoX +- `tts.phrases`: a dictionary where every key is the clip name and the value is the phrase text to be rendered to that clip on the kernel start + +### Static audio clips list: `clips` + +This section determines which static audio clips are to be loaded into memory in addition to the synthesized voice. The clips must be in the unsigned 8-bit 8KHz PCM WAV format. The fields are: + +- `clips.dir`: path to the directory (relative to the configuration file directory) containing the audio clips to load +- `clips.files`: a dictionary mapping the clip names to the `.wav` file names in the `clips.dir` directory + +Both fields are required to fill, but if you're not planning on using any static audio, just set `clips.dir` value to `'.'` and `clips.files` to `{}`. + +### IVR configuration: `ivr` + +This section lets you set up PIN-based authentication, access control and, most importantly, IVR actions themselves and the scripts that implement them. + +These two fields are mandatory to fill (although can be left as empty arrays): + +- `ivr.cmdpromptclips`: an array with a sequence of the clip names to play back to prompt the caller for a command +- `ivr.cmdfailclips`: an array with a sequence of the clip names to play back to alert the caller about an invalid command + +Note that any clip name in this section can refer to both static and synthesized voice audio clips, they all are populated at the same place at this point. + +To turn the authentication part on and off, use the `ivr.auth` field. If and only if this field is set to `true`, the following fields are required: + +- `ivr.authpromptclips`: an array with a sequence of the clip names to play back to prompt for the caller's PIN +- `ivr.authfailclips`: an array with a sequence of the clip names to play back to alert the caller about the invalid PIN before hanging up the call +- `ivr.users`: a dictionary that maps valid user IDs (PINs) to the lists (arrays) of action IDs they are authorized to run, or `'*'` string if the user is authorized to run all registered actions on this instance + +Also, if the user is authenticated but not authorized to run a particular action, the same "invalid command" message sequence from `ivr.cmdfailclips` will be played back as if the command was not found. This is implemented by design as a security measure. FrugalVox itself will log a different message to the console though. + +Finally, all the action mapping is done in the mandatory `ivr.actions` dictionary. The key is the action ID (without parameters, i.e. commands `22*5#` and `22*44*99#` both mean an action with ID 22, just with different parameter lists) and the value is the path to the action script file, relative to the configuration file directory. + +## Action scripts + +An action script is a regular Python module file referenced in the `ivr.actions` section of the configuration YAML file. A single script may implement one or more actions based on the action ID. The module must implement the `run_action` call in order to work as an action script, as follows: + +``` +def run_action(action_id, params, call_obj, user_id, config, clips, calls): + ... +``` + +where: + +- `action_id` is a string action identifier, +- `params` is an array of string parameters to the action, +- `call_obj` is an instance of `pyVoIP.VoIP.VoIPCall` class passed from the main FrugalVox call processing loop, +- `user_id` is the ID (PIN) string of the user running the action (if authentication is turned off, it's always `0000`), +- `config` is the dictionary containing the entire FrugalVox configuration object (as specified in the YAML), +- `clips` is the object containing all in-memory audio clips (in the unsigned 8-bit 8KHz PCM format) ready to be fed into the `write_audio` method of the `call_obj`, +- `calls` is a dictionary with all currently active calls on the instance (keyed with the `call_obj.call_id` value). + +_Protip:_ if we use `*` as parameter separator and `#` as command terminator, why are `action_id`, `user_id` and all the action parameters still treated as strings as opposed to numbers? Because `A`, `B`, `C` and `D` still are valid DTMF digits and can be legitimately used in the actions or their parameters. Of course, if you target normal phone users, you should avoid using the "extended" digits, but there still is a possibility to do so. If you need to treat your action parameters or any IDs as numbers only, please do this yourself in your action script code. + +The action script may import any other Python modules at your disposal, including the main `fvx.py` kernel to use its helper methods, and all the modules available in the configuration file directory (in case it differs from the default one). An example action script that implements three demonstration actions, `32` for echo test, `24` for caller ID readback and `22*[times]` for beep, is shipped in this repo at `actions/echobeep.py`. + +### Useful methods, variables and objects exposed by the `fvx` kernel module + +- `fvx.load_yaml(filename)`: a wrapper method to read a YAML file contents into a Python variable (useful if your action scripts have their own configuration files) +- `fvx.load_audio(filename)`: a method to read a WAV PCM file into the audio buffer in memory (note that it must be unsigned 8-bit 8Khz in order to work with pyVoIP calls) +- `fvx.logevent(msg)`: a drop-in replacement for Python's `print` function that outputs a formatted log message with the timestamp +- `fvx.audio_buf_len`: the recommended length (in bytes) of a raw audio buffer to be sent to or received from the call object the action is operating on +- `fvx.emptybuf`: a buffer of empty audio data, `fvx.audio_buf_len` bytes long +- `fvx.detect_dtmf(buf)`: a method to detect a DTMF digit in the audio data buffer (see `actions/echobeep.py` for an example of how to use it correctly) +- `fvx.tts_to_buf(text, ttsconfig)`: a method to directly render your text into an audio data buffer (pass `config['tts']` as the second parameter if you don't want to change anything in the TTS settings) +- `fvx.tts_to_file(text, filename, ttsconfig)`: same as `fvx.tts_to_buf` method but writes the result to a WAV PCM file +- `fvx.get_caller_addr(call_obj)`: a method to extract the caller's SIP address from a `VoIPCall` object (e.g. the one passed to the action) +- `fvx.get_callee_addr(call_obj)`: a method to extract the destination SIP address from a `VoIPCall` object (e.g. the one passed to the action) +- `fvx.flush_input_audio(call_obj)`: a method to ensure any excessive audio is not collected in the call audio buffer, recommended to use at the start of any actions that perform incoming audio processing +- `fvx.playbuf(buf, call_obj)`: a method to properly play back any audio buffer to the call's line +- `fvx.kernelroot`: a string that contains the FrugalVox kernel directory path +- `fvx.configroot`: a string that contains the config file directory path + +## FAQ + +**How was this created?** + +Initially, FrugalVox was created to answer a simple question: "given a VoIP provider with a DID number I pay for monthly anyway, and a cheap privacy-centric VPS already filled with other stuff, how can I combine these two things together to control various things on the Internet from a Nokia 1280 class feature phone without the Internet access?" + +**Why not Asterisk/FreeSWITCH/etc then? What's wrong with existing solutions?** + +Nothing wrong at the first glance, but... Asterisk's code base was, as of November 2016, as large as 1139039 SLOC. If you don't see a problem with that, I envy your innocence. Anyway, I doubt that any of those would be able to comfortably run on that cheap VPS or on my Orange Pi with 256 MB RAM. For my goals, it would be like hunting sparrows with ballistic missiles. + +FrugalVox kernel, on the other hand, is around 250 SLOC in 2023. Despite being written in Python, it is really frugal in terms of resource consumption, both while running and while writing and debugging its code. Yet, thanks to full exposure of the action scripts to the Python runtime, it can be as flexible as you want it to be. Not to mention that such a small piece of code is much easier to audit and discover and promptly mitigate any subtle errors or security vulnerabilities. + +**So, is it a PBX or just a scriptable IVR system?** + +I'd rather think of FrugalVox not as a turnkey solution, but as a framework. If you look at the `fvx.py` kernel alone, you'll see nothing but a scriptable IVR with user authentication and TTS integration. However, its the action scripts that give such a system its meaning. FrugalVox, along with the underlying pyVoIP library, exposes all the tooling you need to create your own interactive menus, connect and bridge calls, dial into other FrugalVox instances or other services, and so on. It's a relatively simple building block that, while being useful alone, can also be used to build VoIP systems of arbitrary complexity when properly combined with other similar blocks. + +**If it's meant to be flexible, why hardcode the top level command format?** + +Because such a format is the only format that allows to run actions with any amount of parameters more or less efficiently using a simple phone keypad. The goal here isn't to replace something like VoiceXML, but to give the caller ability to get to the action as quickly as possible. The `PIN#` and then `action_id*param1*param2*..#` sequence is as complex as it should be. Multi-level voice menus waste the caller's time, but you can implement them as well if you really need to. + +**Does FrugalVox offer a way to fully replace DTMF commands with speech recognition?** + +Currently, there is no such way, but you surely can integrate speech recognition into your action scripts. It is not an easy thing to do even in Python, and in no way frugal on computing resource consumption, but definitely is possible, see [SpeechRecognition](https://pypi.org/project/SpeechRecognition/) module reference for more information. + +**Why do you need a patched pyVoIP version instead of a vanilla one?** + +Because vanilla pyVoIP 1.6.4 has a bug its maintainers don't even seem to recognize as a bug. Its `RTPClient` instance creates two sockets to communicate with the same host and port. As a result, when the client is behind a NAT and tries exchanging audio data using both `read_audio` and `write_audio` methods, only the latter works correctly because it's sending datagrams out to the server. Patching `RTPClient` to only use the single socket made things work the way they should. + +**I understand the importance of eSpeakNG but it sounds terrible even with MBROLA. Which else open source TTS engines can you recommend to use with FrugalVox?** + +The first obvious choice would be ~~Festival~~ [Flite](https://github.com/festvox/flite). With an externally downloaded `.flitevox` voice, of course. It has a number of limitations: only English and Indic languages support, no way to adjust the volume, but the output quality is definitely a bit better. If you use the Docker image of FrugalVox, Flite is also included but you have to ship your own `.flitevox` files located somewhere inside your config directory. Also, current Flite versions already generate 16 KHz PCM files instead of 8 KHz, so the transcoder command still needs to be in place. + +The second obvious choice would be [Pico TTS](https://github.com/naggety/picotts) which is (or was) used as a built-in offline TTS engine in Android. It supports more European languages (besides two variants of English, there also are Spanish, German, French and Italian) but has a single voice per language and absolutely no parameters to configure. Also, it requires autotools to build but the process looks straightforward: `./autogen.sh && ./configure && make && sudo make install`. After this, we're interested in the `pico2wave` command. Please note that its current version has some bug retrieving the text from the command line, so we use an "echo to the pipe" approach. For your convenience, this engine also comes pre-installed in the FrugalVox Docker image. + +The third (not so obvious) choice **might** be [Mimic 1](https://github.com/MycroftAI/mimic1) which is basically Flite on steroids. That's why, unlike Mimic 2 and 3, it still is pretty lightweight and suitable for our IVR purposes. It supports all the `.flitevox` voice files as well as the `.htsvoice` format. However, there is a "small" issue: currently, Mimic 1 still only supports sideloading `.flitevox` and not `.htsvoice` files by specifying the arbitrary path into the `-voice` option, all HTS voices must be either compiled in or put into the `$prefix/share/mimic/voices` (where `$prefix` usually is `/usr` or `/usr/local`) or the current working directory, and then referenced in the `-voice` option without the `.htsvoice` suffix. For me, this inconsistency kinda rules Mimic 1 out of the recommended options. The [Mycroft 4.0](https://github.com/MycroftAI/mimic1/blob/development/voices/mycroft_voice_4.0.flitevox) voice though, which is shipped in the same Mimic 1 repo, still can be used with the vanilla Flite with no issues. + +Another approach to the same problem would be to build the HTS Engine API and then a version of Flite 2.0 with its support, both sources taken from [this project page](https://hts-engine.sourceforge.net/). The build process is not so straightforward but you should be left with a `flite_hts_engine` binary with a set of command line options totally different from the usual Flite or Mimic 1. If you understand how FrugalVox is configured to use Pico TTS, then you'll have no issues configuring it for `flite_hts_engine`. The voice output quality is debatable compared to the usual `.flitevox` packages, so I wouldn't include this into my recommended list either. + +Alas, that looks like it. The great triad of lightweight and FOSS TTS engines consists of eSpeakNG, Flite with variations and Pico TTS. All other engines, not counting the online APIs, are too heavy to fit into the scenario. Of course, nothing prevents you from integrating them as well if you have enough resources. In that case, I'd recommend [Mimic 3](https://github.com/MycroftAI/mimic3) but that definitely is out of this FAQ's scope. + +To recap, here are all the example TTS configurations for all the reviewed engines: + +eSpeakNG + MBROLA: + +```yaml +tts: + voice: 'us-mbrola-2' + rate: 130 # words per minute + volume: 70 # from 0 to 200 + pitch: 60 + cmd: + synth: 'espeak -v %s -a %d -p %d -s %d -w %s "%s"' # parameter order: voice, volume, pitch, rate, filename, text + transcode: 'sox %s -r 8000 -b 8 -c 1 -D %s' # parameter order: inputfile, outputfile + ... +``` + +Flite/Mimic 1: + +```yaml +tts: + voice: 'tts/mycroft_voice_4.0.flitevox' + rate: 1 # Flite uses a factor instead of absolute value + volume: 0 # Flite doesn't support volume adjustment + pitch: 100 # Flite uses slightly different pitch scale + cmd: + synth: 'flite -voice %s --setf vol=%d --setf int_f0_target_mean=%d --setf duration_stretch=%d -o %s -t "%s"' # parameter order: voice, volume, pitch, rate, filename, text + transcode: 'sox %s -r 8000 -b 8 -c 1 -D %s' # parameter order: inputfile, outputfile + ... +``` + +Pico TTS: + +```yaml +tts: + voice: 'en-US' + rate: 0 # Pico doesn't support it + volume: 0 # Pico doesn't support it + pitch: 0 # Pico doesn't support it + cmd: + synth: VOICE=%s UU=%d%d%d OUTF=%s sh -c 'echo "%s" | pico2wave -l $VOICE -w $OUTF' # parameter order: voice, volume, pitch, rate, filename, text + transcode: 'sox %s -r 8000 -b 8 -c 1 -D %s' # parameter order: inputfile, outputfile + ... +``` + +## Credits + +Created by Luxferre in 2023. + +Made in Ukraine. diff --git a/example-config/actions/echobeep.py b/example-config/actions/echobeep.py new file mode 100644 index 0000000..b7133ef --- /dev/null +++ b/example-config/actions/echobeep.py @@ -0,0 +1,38 @@ +# An example action script for FrugalVox +# Implements three actions: echo test, caller address readback and parameterized beep + +import os, sys # example of using commonly available Python modules +from pyVoIP.VoIP import CallState # example of using an installation-specific module +from fvx import tts_to_buf, detect_dtmf, audio_buf_len, emptybuf, get_caller_addr, flush_input_audio, playbuf # example of using the FrugalVox kernel module (fvx) + +def run_action(action_id, params, call_obj, user_id, config, clips, calls): + if action_id == '32': # echo test: just enter 32# + playbuf(tts_to_buf('Entering the echo test, press pound to return', config['tts']), call_obj) + flush_input_audio(call_obj) + cache_digit = None # in-band digit cache + while call_obj.state == CallState.ANSWERED: # main event loop + audiobuf = call_obj.read_audio(audio_buf_len, True) # blocking audio buffer read + call_obj.write_audio(audiobuf) # echo the audio + digit = call_obj.get_dtmf() # get a single out-of-band DTMF digit + if digit == '' and audiobuf != emptybuf: # no out-of-band digit, try in-band detection + ib_digit = detect_dtmf(audiobuf) + if ib_digit != cache_digit: + if ib_digit == None: # digit transmission ended + digit = cache_digit # save the digit + cache_digit = None # reset the cache + else: # digit transmission started + cache_digit = ib_digit + if digit == '#': + playbuf(tts_to_buf('Echo test ended', config['tts']), call_obj) + return + elif action_id == '24': # Caller ID readback: enter 24# + playbuf(tts_to_buf('Your caller ID is %s' % get_caller_addr(call_obj), config['tts']), call_obj) # demonstration of the on-the-fly TTS + else: # beep command: 22*3# tells to beep 3 times + times = 1 # how many times we should beep + if len(params) > 0: + times = int(params[0]) + if times > 10: # limit beeps to 10 + times = 10 + # send the beeps + playbuf((clips['beep']+(emptybuf*10)) * times, call_obj) + return diff --git a/example-config/clips/beep.wav b/example-config/clips/beep.wav new file mode 100644 index 0000000000000000000000000000000000000000..51d4b46e3bdcc0a1bf05c3ab85e23687bc692bd6 GIT binary patch literal 4044 zcmY*c*-j%@6!g61E%}4wQ<8^_6p8M(u`&BP5C{we2Z9B`0S3ef1Yv+-3;S-2jqUFF zgnU@4>fAOSS^Z||=$u+_>o0HL{`YMn(f(8R=U*phfA}tuNW8)C$L|yR-Xy+B3=hr@ zUg1A0*J0)Jbeq>(xTwDz&bIqK3#YZ}@m#0Zkw2-`&gQ%P?99PS{c^F-%S`WAqO0YB zAU*Y|9NnzG3tGpwpThf(BSCU(<1u>N7)u7j>-S-4YdjSUeYlODKTW2C{^hH%vX^V~ zdkg3FmxGyXOZUQQy?Qd&+0vOmuGLN#y1ne|A!ZbM{kGrt%Hj3WfR~>9^c>yVj8cRd z$<)|pF?!sz8PU`Bc*-AIy^YG4;rB0NkG<)P_NW}pwtL<4XZ7lFzSHZRJE>|$`^?cx z{i4w8WvBPc;nm7OkeS>mW5y8o;Eds9YSi{fCAkM?v<5?K+9QJ*S7CWShg&UPguIo9 zJ!;&;>zF&K)wRdWVKuy1#2(Xo73|UPr<-OxMfd9?LCS75no5qWKSU+W&>p;%_9*X9 zxA}dA%MiEXjI(;}WUj-)tjiiGnw%R$X(26;L*}OPdtb@_NblCV+OeEZbdxY3~(2>n%EK^ z+T+XP#%MA*{1KRIkF}eqL_D+yc!fQn58*NI_HZ+R2lZiie6EDlM~2+JBd^?y2Ifh_ z9C#4(keGYou3;_>W?y;LB|M1v%t00U=<}(;`qfImpAjA(40GxuAa}n!ZrUEeoOpb& z8GirLbse|LdVScV8sqUI?&gnQbgS$PGzK*Ct z-!wSGTS4azb7*kBN4Kgueb@})0nEi+XmB{09Nj2}@Dgh9j$ZPW2I*I(j~cn__c&hB zucQwTx2gj3KCdlDUabuH@RE{lm5TR}Ulqw+X>fZY-S9;6Dl+aCIIj#uzq$6c3$E5tmP@%k32bLaz@Thmb_Aaz}sWrHNOJpE(euSwg+dV z{J}MNJ3J9t3vV~yMb`3GrVnM5eAoF^j5+h%PrtXUg(oTpT}Bn5LFfZ`IKOiFVR&S` z9(4xloO_%gKhPOWAF(IW+l7ZG4O-TUSB?C@9`Ie`uJXfj5Vs=cebyP2wdxGkOOUlK z^ebkqdP&_hh&`zDl)Du?akCf-b8=UB3?e_2wF66*AM}zqqqqk#SAJkdhh{hp3J>a> zok5th`^c}TLEVabq%F^tQOrSfAMz@!s+Xu=4|)mqfIjGnj=N=e3HF#k zzoI_WOW-BYpl*dcAHp7`ohft%@QRpgM%)?5UF9GT? z`4u~ybsy%SyOqOS9WKsV=ZP`q@-LB+;6iz)P3Y7ZU+1c8Z0uS8hBt2ddW_l=S@0q_vu$IKY&N0mz*mH zc`NG-+M}ua$V-$T#Do2cd0yfS_1%4S(Q|e<%X8sjJ#oIfrTq{1RjfhwUF3&41HHt$ zkHdpG=zJHvqRyochdFx*c!e3Sd!X;Ci=xB5SPtf-L39RqyW{RxUcy`140&QaqbZ~6 zFGuRT&UeLK@CrSVo(R9niDfPD0C%k?=FqPeajPTO z84M5GqsyZPowu|5EIaPrAfv!5_QYZ7oZcSi;BUnIM*XUpSMo&jtD1PFw*hldy&XDl z?1Ao6ia5hM9J`PDuG>StOMQgsqS#}_-UjN4=vVUHT0Fya&fc#4&Q_TSTE5G- zLG$|qS*za8?lYBX^h9+y^%8r3;kUqNoEIT!LQ}(SGD^K2 zc`iKI8T5`qe*D#=_n_(AJh9(HMpa3WXWmW?LLXCUPo04}r(da;SVk!a z?LCNhZ6j;#ZD5{gJu%L6!^1og?^@{t?+=$zcz=ky_8z2{#JvQ!qL*~&{h?kG_uVP> Ut2jUS#(CuxJGJ#)c4~Ra|350&8vp script filename + '32': './actions/echobeep.py' + '22': './actions/echobeep.py' + '24': './actions/echobeep.py' diff --git a/fvx.py b/fvx.py new file mode 100644 index 0000000..eb2d635 --- /dev/null +++ b/fvx.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python3 + +# FrugalVox: experimental, straightforward, no-nonsense IVR framework on top of pyVoIP (patched) and TTS engines +# Created by Luxferre in 2023, released into public domain +# Deps: PyYAML, NumPy, espeak-ng/flite/libttspico, SoX, patched pyVoIP (see https://github.com/tayler6000/pyVoIP/issues/107#issuecomment-1440231926) +# All configuration is in config.yaml + +import sys +import os +import signal +import tempfile +import yaml +import wave +import time +from datetime import datetime # for logging +import traceback # for logging +import socket # for local IP detection +import numpy as np # for in-band DTMF detection +import importlib.util # for action modules import +from pyVoIP.VoIP import VoIPPhone, InvalidStateError, CallState + +# global parameters +progname = 'FrugalVox v0.0.1' +config = {} # placeholder for config object +configfile = './config.yaml' # default config yaml path (relative to the workdir) +if len(sys.argv) > 1: + configfile = sys.argv[1] +configfile = os.path.realpath(configfile) +kernelroot = os.path.realpath(os.path.dirname(__file__)) # absolute path to the kernel directory +configroot = os.path.dirname(configfile) +sys.path.append(kernelroot) # make the kernel module findable +if configroot != kernelroot: + sys.path.append(configroot) # make the modules in configuration directory findable +audio_buf_len = 160 # analyze this amount of raw audio data bytes +emptybuf = b'\x80' * audio_buf_len +DTMF_TABLE = { + '1': [1209, 697], + '2': [1336, 697], + '3': [1477, 697], + 'A': [1633, 697], + '4': [1209, 770], + '5': [1336, 770], + '6': [1477, 770], + 'B': [1633, 770], + '7': [1209, 852], + '8': [1336, 852], + '9': [1477, 852], + 'C': [1633, 852], + '*': [1209, 941], + '0': [1336, 941], + '#': [1477, 941], + 'D': [1633, 941] +} +ivrconfig = None # placeholder for IVR auth config +calls = {} # placeholder for all realtime call instances + +# helper methods + +def logevent(msg): + dts = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + print('[%s] %s' % (dts, msg)) + +def load_audio(fname): # load audio data from a WAV PCM file + f = wave.open(fname, 'rb') + frames = f.getnframes() + data = f.readframes(frames) + f.close() + return data + +def load_yaml(fname): # load an object from a YAML file + yf = open(fname, 'r') + yc = yf.read() + yf.close() + return yaml.safe_load(yc) + +def tts_to_file(text, fname, conf): # render the text to a file + fh, tname = tempfile.mkstemp('.wav', 'fvx-') + os.close(fh) + rate = int(conf['rate']) + volume = int(conf['volume']) + pitch = int(conf['pitch']) + ecmd = conf['cmd']['synth'] % (conf['voice'], volume, pitch, rate, tname, text) + os.system(ecmd) # render to the temporary file + # now, resample the synthesized file to Unsigned 8-bit 8Khz mono PCM + smpcmd = conf['cmd']['transcode'] % (tname, fname) + os.system(smpcmd) + os.remove(tname) + +def tts_to_buf(text, conf): # render the text directly to a buffer + fh, fname = tempfile.mkstemp('.wav', 'fvx-') + os.close(fh) + tts_to_file(text, fname, conf) + buf = load_audio(fname) + os.remove(fname) + return buf + +def get_caller_addr(call): # extract caller's SIP address from the call request headers + return call.request.headers['From']['address'] + +def get_callee_addr(call): # extract destination SIP address from the call request headers + return call.request.headers['To']['address'] + +def flush_input_audio(call): # clear the call's RTP input buffer + abuf = None + for i in range(625): # because 625 * 160 = 100000 (pyVoIP's internal buffer size) + abuf = call.read_audio(audio_buf_len, False) + +def playbuf(buf, call): # properly play audio buffer on the call + blen = len(buf) / 8000 + call.write_audio(buf) + time.sleep(blen) + +def playclips(clipset, call): # properly play clips on the call + for clipname in clipset: + playbuf(clips[clipname], call) + +def hangup(call): # call hangup wrapper + global calls + if call.call_id in calls: + del calls[call.call_id] + try: + call.hangup() + except InvalidStateError: + pass + logevent('Call with %s terminated' % get_caller_addr(call)) + +# in-band DTMF detector + +def isNumberInArray(array, number): + offset = 5 + for i in range(number - offset, number + offset): + if i in array: + return True + return False + +def detect_dtmf(buf): # Detect a DTMF digit in the audio buffer using FFT + data = np.frombuffer(buf, dtype=np.uint8) + ftdata = np.fft.fft(data) + ftlen = len(ftdata) + for i in range(ftlen): + ftdata[i] = int(np.absolute(ftdata[i])) + lb = 20 * np.average(ftdata) # lower bound for filtering + freqs = [] + for i in range(ftlen): + if ftdata[i] > lb: + freqs.append(i) + for d, fpair in DTMF_TABLE.items(): # Detect and return the digit + if isNumberInArray(freqs, fpair[0]) and isNumberInArray(freqs, fpair[1]): + return d + +# IVR command handler (for authenticated and authorized action runs) + +def command_handler(act, modulefile, call, userid): + global clips + global calls + global config + actid = act[0] + params = act[1:] + logevent('Running action %s from the module %s with params (%s)' % (actid, modulefile, ', '.join(params))) + (modname, ext) = os.path.splitext(os.path.basename(modulefile)) + spec = importlib.util.spec_from_file_location(modname, modulefile) + actionmodule = importlib.util.module_from_spec(spec) + sys.modules[modname] = actionmodule + spec.loader.exec_module(actionmodule) + actionmodule.run_action(actid, params, call, userid, config, clips, calls) + +# main call handler + +def main_call_handler(call): # call object as the argument + global clips + global ivrconfig + global calls + calls[call.call_id] = call # register the call in the list + logevent('New incoming call from %s' % get_caller_addr(call)) + try: + call.answer() + authdone = True + userid = '0000' # default for the unauthorized + actionsallowed = '*' + if ivrconfig['auth'] == True: # drop all permissions and prompt for the PIN + authdone = False + actionsallowed = {} + playclips(ivrconfig['authpromptclips'], call) + else: # prompt for the first command + playclips(ivrconfig['cmdpromptclips'], call) + cmdbuf = '' # command buffer + cache_digit = None # in-band digit cache + while call.state == CallState.ANSWERED: # main event loop + audiobuf = call.read_audio(audio_buf_len, False) # nonblocking audio buffer read + digit = call.get_dtmf() # get a single out-of-band DTMF digit + if digit == '' and audiobuf != emptybuf: # no out-of-band digit, try in-band detection + ib_digit = detect_dtmf(audiobuf) + if ib_digit != cache_digit: + if ib_digit == None: # digit transmission ended + digit = cache_digit # save the digit + cache_digit = None # reset the cache + else: # digit transmission started + cache_digit = ib_digit + if digit == '#': # end of the command + if authdone: # we're authenticated, let's authorize the action + actionparts = cmdbuf.split('*') + actionroot = actionparts[0] + letthrough = False + if actionsallowed == '*' or (actionroot in actionsallowed): + letthrough = True + if letthrough: # authorized + if actionroot in ivrconfig['actions']: # command exists + actionmod = os.path.realpath(os.path.join(configroot, ivrconfig['actions'][actionroot])) # resolve the action module file + command_handler(actionparts, actionmod, call, userid) # pass control to the command handler along with the call instance + else: # command doesn't exist, notify the caller + playclips(ivrconfig['cmdfailclips'], call) + logevent('Attempt to execute a non-existing action %s with the user ID %s' % (cmdbuf, userid)) + else: # notify the caller that the command doesn't exist and log the event + playclips(ivrconfig['cmdfailclips'], call) + logevent('Attempt to execute an unauthorized action %s with the user ID %s' % (cmdbuf, userid)) + playclips(ivrconfig['cmdpromptclips'], call) # prompt for the next command + flush_input_audio(call) + else: # we expect the first command to be our user PIN + if cmdbuf in ivrconfig['users']: # PIN found, confirm auth and prompt for the command + authdone = True + userid = cmdbuf + actionsallowed = ivrconfig['users'][userid] + playclips(ivrconfig['cmdpromptclips'], call) # prompt for the next command + else: # PIN not found, alert the caller, log the failed entry and hang up + playclips(ivrconfig['authfailclips'], call) + logevent('Attempt to enter with invalid PIN %s' % cmdbuf) + hangup(call) + cmdbuf = '' # clear command buffer + elif digit != '': # append the digit to the command buffer + cmdbuf += digit + hangup(call) + except InvalidStateError: # usually this means the call was hung up mid-action + hangup(call) + except SystemExit: # in case the service has been stopped or restarted + hangup(call) + except Exception as e: + print('Unknown error: ', sys.exc_info()) + traceback.print_exc() + hangup(call) + +# signal handler for graceful process termination + +def sighandler(signum, frame): + global phone + logevent('Stopping the SIP client...') + phone.stop() + logevent('SIP client stopped, bye!') + +# entry point + +if __name__ == '__main__': + logevent('Starting %s' % progname) + config = load_yaml(configfile) + ivrconfig = config['ivr'] + logevent('Configuration loaded from %s' % configfile) + clipDir = os.path.realpath(os.path.join(configroot, config['clips']['dir'])) + logevent('Loading clips and compiling TTS phrases') + clips = config['clips']['files'] + for k, fname in clips.items(): + clips[k] = load_audio(os.path.join(clipDir, fname)) + for pname, phrase in config['tts']['phrases'].items(): + clips[pname] = tts_to_buf(phrase, config['tts']) + logevent('All clips loaded to memory buffers from %s' % clipDir) + logevent('Initializing SIP phone part') + sip = config['sip'] + sipport = int(sip['port']) + localname = socket.gethostname() + localip = (([ip for ip in socket.gethostbyname_ex(localname)[2] if not ip.startswith('127.')] or [[(s.connect((sip['host'], sipport)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) + [None])[0] + if localip == None: + localip = socket.gethostbyname(localname) + logevent('Local IP detected: %s' % localip) + phone = VoIPPhone(sip['host'], sipport, sip['username'], sip['password'], myIP=localip, rtpPortLow=int(sip['rtpPortLow']), rtpPortHigh=int(sip['rtpPortHigh']), callCallback=main_call_handler) + # register the SIGINT and SIGTERM handlers to gracefully stop the phone instance + signal.signal(signal.SIGINT, sighandler) + signal.signal(signal.SIGTERM, sighandler) + phone.start() + logevent('SIP client started') diff --git a/pyVoIP-1.6.4.patched-py3-none-any.whl b/pyVoIP-1.6.4.patched-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..be905e44a9d3b9e7c786e29b7e473c498b3d44e9 GIT binary patch literal 74034 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

4`L=i;3!-r`pTB4y86F zL$^M>)NWN+)&Um#YE7kz;Q;jG`6P0#sy=TlG-sm3@e|X04)ZKlwsd8G843)sf{$Ll z@!ZH4r-?en1WR^!^~=_z=?PFk9;L9O;&$QAMJsK@*&n!yE{c1}rJci%2+A;~vFpxL z8<8tS9W|YsO*@s%$EKH+<6-*Hez2Z??(`p32KAw#;9!)y=zN!^$K(?xzkYZyc%_F= z<}+McW$OrIpkbZn5bue}{uJ9%-rT!Os=~i=oVy;$?BvZ-8d#4yG$uYDm3HgZN{#SL&&h@uC${pMgBuwl!_<+(PA;Wts zbk8kPSGI&qu=cvwHv4%O8KJB(LS{_iZ@z|#u?Xbu6rgx*Ez#SyqQF=J_-hk|^;9>R zw!}d0dRaHxHYcc?p-wj*9P_Nc&&&8!PS@;8HUKundq8`}Ade@x%Qmyc#l>u(rl{<08rvK7; zYuN_@nQb4{!WP}I>=#UZ&)jdkW+gkJile4Y6q%g zS(dIIE?>s5kT(0Dz6i{9e_{-|+n;rNqNj17J$Cr~X>G{j5W}e;s)*~VnAK730zGf> z5uv&)@~^sZ0nW!?n}laC{>qRyPyDE`rrf@TaoB_qk zjHDVLl!krCB$jv7@xxR5qzc1Phl=svAv-9~2;?ki4l2~4hMIz+4PQEcLq)w$MNnz< z>m~ExP@kY_v+)cx6u#Klog2i=WC+g6_|fSpVNe2G!P1S#3sr&xoS@06IttQ+N1DzG z$6MpM2@j>wBV{1YvB&wtAry`F{LylZw}P2qdmhcHEVX@`U%Q2#*_Ku8Dwv+NuqJ($ z`OHg4qpY`9u8Tb7Lrk8nc`^fP;q*v8@R}{Iy=GRoHzpbc1W_u3&}`?Q)Ns-cFYt(w z`_G=`le*13oK}zNQOO)q%HrOfUk0w{ra=WsXkj!i|H> zg+o*bSsZz6U)XYaiWTCBE+J3Aj~bN>33SF;jJKX8CUISoyncHd{+zRgI~;G*ilV-I z{ojJpjA!Cs*Td-R+8(dn7^Bnt=bA@vpW(rDi)*ga9coko1q9!50t+@6m)%+?pVuv? zTV7RJ@@K@aRK7bCa7M@9^nM)jp?vsG3j>H)17D{E{`XSRolg)PqEK5$emD ze{u}*f!w0b_g2Qe5w{#tfZPsptnonr3B`TM4Wv-Kus8H65N;a@{oY_(+q9rJutV&9 z%Y&Z%%P=*iX}?h>ZaQPVlaqDW02+EOBq(T*dVM53TAIz)fNmw7w)E(>33HRd=oqxg z;{&gC$L0Hl08szpj6*Ss)hwlEC0gN(XErFAnVqx=v=RE_5qkL}hLgsGN|{+XLGt+e z3(sMH?N18o*2rUI936Fm-0%rQHnUCD&FEj+Hzx zdwI0$jAa?NL)7f&4IyqUi2&GdMqmUQV<}qW37LSBNmmT_V}7+Q^Lz!W8qMHst8VoZB7)GXzlfRm+-7og06?|!@pEUXTuLR(@I$$y#rM)iNTYUjm-9pL zVyPwBD7G#Q{Z`P&3gMoni{S<8(*4l}1lk9x8q_aVY$dXX^~RkSK5T+c$V zv%#e7fTa91;=Tj*i#d6UXo#M*?Ll=l?FyhYd(UqgWXRwR81&Tp$ocK#uJVhHSf0s zq{k;>${XtKS?RrZnXt)#NX1}UE(C(Clw^g9yk~R%TnhC+hTCd^N^tcYd($@iKM11_ zx({E9d$>(-Iof5-W5Mc0nTovov3_5TA!%(Q+q{w@6x#&fO1|INK!*)zW_Lijv887ecZc{jE!f**8xQMU?RdLY(R`8db zqQy|*!jCAlf(3AS5>?8CinI=xlrgq~Rhp>g84HL3pSPyszFghW=LPS0ltw)o-h(2r zzH%frQ1R*QuuP-vL%7y`N*dN|?sM9&6)W;}zBGsredsgDHv*tD0qY zU!ulXJoe9ZBqu+t6T&|eTeBfkpjQ@;gxr?+hO=p*>y0N!u2ZaQ4t@t!Tthi5xu&z3 z;_JU2vDmHoCgn0l*Qg#jxou3>VNRT`L?1o=Q6&Sqt>GI1Q;AoqkEnm9d8Tza^z`l0 z7)FGwhrGhGneI!sCNnYCi#)<*(^0M#PfFLCJkqDr0Bcnq$kZ?st6iwh&opd%8euS2SwqtiH*w8%7Ar}rRYs(dnH0Uxi zNCM86@B+d`0;1?w$`awF|Bj#LYd6Xlgx+3K$2DZz$Nz}&3@nq$E*Qgk^lawZ%0)O@ z5AE=_FNj^drhOOlav|Tt?p^VG@F2vlI8s^zkbD?Szz+Y#1fq8VGCvj-q!n)*NafGC z-8H`GDsHF3n1^4Yg%xQqpV%i~%9MnEtZivP8APhrQ2gb&DG`sV&+Pjk-vOFouRqzZ zBEEKUDnOT#^ZWfpHYHeDz0q;JT*)1{ML|!k#xo%nzu5Ea{^1lp_|(Cq1}WMxUm$^U zW;2IWa`DIdmdyZ zCT8Cc7c%`Xt|@wY2y5ErTbrtrR5J0(x_an5vw({KSWB~hvaK)?%DWXxV>^mi{Xmq` zG-V3iX8!}Ft;;-6d1+Si?GKNuD1SsS<|U8nxzKWta#MZ@;`eL$uUy^MmGO53Mo-Uw zW)WJqi5mL#J9yw;srRD@SC2tV2DHg#GNY9SKz&G@Li-OxoXqj~_}h3h>1^~9h>i1XMX_!J2{pwT|*#&&79M^T4EZ8aam z52TLc))upw%?h~V*8{aGtU86grDMg?b#n%=j>;nrj0by}ZlMVxlf8B{)?^P{wu-Nn z>pF1&GkPT1#m>76CN~no7}D7+L_-f;e%r^I&i1EXV3pg!Y2*}Qpelb1{3c0CbcAx3 zm5Pl~x004#pzB~9P9?g;m4wa@Xpt+5Q}V%Y_qF2RadS=r;A@j)auo$43?OzEHU{gO z&GvV_a)JD+Tk4|Z1Jp^@W5!S{NIArz!N&#BxR?@OfVwA4`)RLmrb;FL>&48|;Bc+T z9#H2n5p!CtW45uw+68;yb&%I;fcjqrUBolXdH(==T3VxD9Ss595Wjl-e=!5fA>+29 z6-kZD9`B;~E4qB+>ZQHfL)bRhDtzHO`9w7kTlb-sbNlQ2KiDAQ%)!!Z=Kl`>kU($0 z0@0E(f|^0s|8=a#A)+y36q*QKCHSt{jj#E%S8T?xpbwm7UK>u1^A_Z4aZa}wG#)DV z1DJi6*P*zit*$#wjz(-`26&w@9D4&Wun4s*L*_Q5{Xra;=vcL~Scb)JJ?$l05Izv) zhcJqIv0Aa}ukaQd z+dv`+a-c^BrLA2#U@0@iBlW(qVg+fb26Rzd-0-*>l5JDt<|;R6@abWazie)7cRcIzgSg5~=P7nkT zB%SI)wcHFtoa7S)v>0-zL4Pr0euFp>CQ+CQPC~AKi}E0;Kcp@?m;_c-g= z)!QY8)3jXrNi1VQ_gbowtboI7oA>O|u7P?Y}ijP|kh$58x?~pH&+0|lY1DgdB zVd-~?F-93f2@&eKP+bMNiaP1vLy#F8U7as2tykA^3jGf>{heR0VOQA}Gy&=Yvn{~X zJa|Hy|47;S6G2n9luQ4KXts3%qii7TKF=7L9_CJH!~NfDnNg-dB*&RXF8zO4sni^> z`a)?9$VPO{?_=}>GuFa7p~)x+yjF{l0CV6UVVr%$a0uao@kzB=KPX&FP`#3anlj>Y zySk3~E2Te{xX9`l6WBkPHIVzWA{E0CAn4?T!KceAOhg{T{t2-PHt-^J$DQIv{dtf2 zpySt&5`Z!6>MHOhX9v#1r)bWF4E_uCo)G1NbWwjj!0`(l!G#n~DCNm!Y~AAAxLg93 zD_4s+YyBw^qG*LTZJ!8a$e@Clqgb#&a-7R)V&%1lOuj`xf$)3{@E0i(`^iT~e_j!C zy+5zWB}pb>o@<=_sSg64$ADu~>$T;UHG(I~wiW>K$1#5I6x7n2qY07LiMZauz%HU% zpe}pk&N3&u&&aq`8@Cq9r3$(~i}Ctfxycx<`0?L`a66A{M!8hLw9&cBonnR8;Pd1$ zZLJ7)vr&UdkL){bYCZb|E~6A!?33!leia|D)6xnC4v5CtptZHD)hq{ym1|D4+36b- z`HOC38lLdK8M`n!eSLI#JU0$2zq#BT{HAAeGcyyD`NGun z&hgdT*jR1~oMu;kvXGygowzVDHaZK5-1ODCE3*?*S8z{Za$+nu<91)2yp|g;OhF!_ z`CNYX$_-O^&E~F7O-_$aPh7d-#^z>nSmHYnI(h{P#NrAwx#??=i5ovZI$oI0K@DeS zLHN&)&dpw$oSraq<8FFt^u|@lmVzgy!3W!Peqww)mv>`Ox597`mA;S0s<}!N4tAF9b=)@JsZNPJ5QL{G+vy+pBE2GmFb8fE}0&*4i zo`f;$o6FD4O+kl2zZb6N#wSJ#xOZc=_+yHhu}itDIdfnxe|hpcR)1!4Zh8!RcLMrs z_J*0n-psu-o6F-kn;HK#P;rk4Ch{;A*P!-TkD05`b9;oHpPtBFFb6Q}E0dz|Y4k0WD3 z@ne%$f!CmYvC*sNCoaxS&dr#y^K&!Uyy={o@hR5lV6&%{=y}8#k;FBqz3Ef=R1Rnt zFd_w=ksF(sf{0Z812eg?IV7aQDBwe`mHGA&Q6RY^@4|q>uue|9U0CP@))k{eJmTn! zP@(IiH{2e<=fPfbyF{qRZ6?kR1;~^4 ztDj%#(DfK|ToQYQ35oU%`dW9f{mlc~enh2o9xB^mN~X-*h@eLo#(ZW9VWH!gV`Il8 zvEvvGrC>auCo`RT(%)-)PTIRCtuVN7zYTZTAdIOjvZ#tZw+Pd%F!+YaSEHi|su^wb z#Ul!;8Ef;!g9@stxB2361=WmO3D#pJEoCsodaaZdw!%v?gv;RO@%FJk+LP;onEg^2cGX;f(;Hf%cCf`H1#+0Q{3Ev_m42jY+MtkMD6W>ZObsOK4 zjnGyK$WxMN2R$3k{~;D?Pm|PktyHWJ9{=o}=dmhaD zsiJME#sjHz{_Dgbo_v#?9_52LV$@??e^49IcjOVn zmGruH9)@g0uZO3LcSiJV+-`W-VLLwTY=*>UnyYbDu>D2!`4T>8pX$$XBy0|0r*jjL z+WP8h(cbtBjwPP*P+{zbq>LPeu{^z{{q5J#+*3X;b~8S_$*t zJJOv{LxwoOcbnlxcuQdlylIZw(7-p$Q!?$IycucWz=dza`peN^*nxY2OJhwgrEnd> z--YnF2v0P_og|B3zL;Bo=$`atDI3kx*`TDJ)azN&0vPKRJWYi9e8ij+MzsPf-F4c{ z;!FtRjucNjo=N==@!fFMb!YMQIr1Y(R$b$Rz0pb`NHA`ixEOMq7OQT%j$t(n>+-_HVA83iY&1YEJ~g@WZOe@d zI4J;b*k1_IiJN6>;buE7d<7mAq$NChB1;*dnv}F(ioPd*qN}>pE9o*)l1}s_%Lz$+ zK>N%dO;NR6jhD8dwBbZp5Y`_a6!6U?hi1d?S0p^ZqaeR-gIthR^YzP=7iTWiH8FoX z(bH;7<)&UYNOe^zRu-(22C7CWRbg2qtJEBLJ9!_6?qkW-0bLhvmaW@XIjg$SJ4L$! z!wevcZ*Iohu>^#@&V$@UFu#ulJO1Dvu+E9tp!tkpOqJJ2<31}XDBe9SpH?(^a zQ*WyFa4-(ORRCr;qm3}|Q4|D`$cOS~EEZ~@022BNz$KJ6xT&*P19@z^p+m|TPuYn1 zE-2@0=+-$#e)tAFsW1u3Q5!Mj#9JEiD!}YbxwZnoacIl000u}bfu~Mbax)IKgz14r zZ-(tJ@LF!hMavS@+EgO}txYzP=(iPSUE2~o(=c~~&)E<>Rr2qK98-{63ese@CCXE_ zf7)|Xj$OVB%Sl&MMt+;=MxqgKq#9kklt!BMe5he-8q(R2XhSdL_HU`~`*?2tJLJXs zK9t+y_d}b17k&pI-S1P}L6kRI;_Qd8Z#!CpB$k$t12^TbNwqM%9mF^L1mqB5hgg!t zXqeOh4P|I?cPX@)X>`MQB><&+AWY_c)W|e?&W64w-P#9lB5!;v=u|>WZL^2-_8mlg z#b}%%#3SK(M;bBJS5CirIxBNriWIrt*#3Y*&$;RaySnPiv(=sO8K<~(j!&A}*|%z? zwUHfZXh%A>BfYsJy`9x)R&y1IW&aq>eUuWhJE_)C*js~}P>yh!&66!`_V*WDb{WYpm z*{Tp`x>|8<&8jdwa%q>=xY{maTWjUw0x&hX7E2Wd^|jx_8uqqff-5msEman)Zn%JQ zof|EP4s(@4Y0V55u=1u_xLqppIsNZ>)4dtN;}gXNhdRgpX?*+)`bX{RodV8IrT}ts zxXSIK-Hzbf@c4d2@N$Stb_v4jC_vDVikO{Nqtao(@r1_~bB86ls4*x!OOB?|%ayTR19uGrs*J9iThr*WuTp_v{}<67Fqp5eBz3 zr#mPvy`zQr0kIIkWz3Oq3G!uflSrci3oG_BDD48n9|n-x&F~t8i~{q*x(L`>X(%iT z5TmddJ#~7<>6xHs67o&$0;w*@Qiwpm_iU(Hcean$82Vy|w}ACQZa>fSLa^T5I!KF93cd^{uAm>p0Wk0XFzgF| zfzVAA;1eeho#4512`awhL?iNwwdD3#Y3NPgRg;Ibh=tn9p{W3R?>P0;VEZVu{kx_R zngr~(J5vQaxGVl4ivRATsfZo^P;EAFOb;q0 zBv@!2bNF^6&Yq_p`)MZu=}riNOVk8vHn5oj!3f77g>|Je-gjib)yYN*1+L95DO7=c zZmk0KB!PMolX6`l4Iy#AR;O`HYF+U5g8%lS|Mn7% z*g>L%R3pwsK+J)?EGT%FIMybdy^_bv8C=rIRTiqKI0`O5X?`*+41tMrfyX?FR~vX5 zIxf@&vQ1g&kWSUunk(IQr7B5UO@WVQ(fc^d-N&W!eVnednj6If9e82Kl}pTx+~&G1 zH&L;iJ5~E8mvs_C)FBra@uZq7m0W4vl`L0+5*K!)Q>HR8^=dY3|8H0v9t`DC6~`%? zNiPe!^x;bL_TS=yAPJwb`C^6LDKTf+l^pOyorkN|oGRpO!_3Mj$kY6H3xU}hjDM))p(#Q-GofPN|m{3ZSXf~6$Do*T_w>?$ETF?-TRK@(#AcQ|Fsh>c( zDyrx*GL)`yQ98v%b{WNMUBLxCON6LN}#a}{z< zDd?{$yk1AYqet{Ws0OKzev%3Ec0>DA$p1m~6A<_xKw9V@I)&dV(3AB1*J+is418F& zT6BkzfNWpGwGoq0_l_%7faWafEdWa8d$dN0j2X9sq5a5F8jW=r>mgWZdDhwem4RJy6!&o)RUw2hsON}~PH*(g6N)Y+=& z?C_FwlU5+`E|GMrflPzlmGtJIY$VV7===SC`|*%C)57~rA(Y_#Hp@HmOI}yGQKS%J z=4OAj^-`+YQjGjfSzNG5sGMPS=q33*_1mgU$BTCN52o6+Gvuol_lNdUEpAI(v3RXr zulmG`&RS`$VEc(5WAT~UozDLA9q{X~!sGYkpxzJ`;o7(L$Zk>z)PHX(!Teg)_oRB4 zIX!ST#H};hpAkxVTC0Zd;g%XoV=AfOC41P>7_Ku_+)w#P!Idqn1EPU+QIwBq(=7%a zt~W4cd|RgOPc*QErniGUvSCHa33(k3aT^H3ev!kOqjQ z_sc1mW9o-?5o>U`_as>Rk3(DuGTWxo|1(kLs+aaLh_ZWEvW_(Q4J1Wnmo8h^>iav; zq;T&dN&hgw*Z(Xe8Rk1e9Y``FNHQu&vYn85Du}feC4!6zg4Bs1Y(KcVgzEr12(Gkn9b$*+NCbBsVFn$C;I60G z5FLf!uBX{iItK9!*1^I$Jj;%O&%S`qah3(2KH<|3KF_h|!Dk@gbAk>)H zSit8h%Y)Bx@|gsRn6k2#ihu0WND*|9Wtz=UY@N-rIf(t-W5&M5u0!nS**g$37+YzJ zeFJ<<_`_5w_8InBNOj@~Q+CAI=-Ugo(FpWI|+@OcGdz6#W`$|~S`O}MIT z4P37SzuaOryuHC{emaM(gU^VdB0L6ko81AQGYs!mH|#X<`cCBZN&0vHR{&M>cs3dj zsPmN~U0aQ=6>GISRhzj9+(N>u3G>B;n+wqZa^SmvatP-_Iw{2MWk?M=I|S>75Z!sl z83yKw0`7^13K}X53lS#c{ISi?5ycuB?Vn-XIK#LJCwUL!rNnQS_)Ux7Zt4^eqti-#PCxk%h?7SbTv*;O;jJV4J}$uTqmm;#=qmY{aa+RO32eWsu5L_BQ7-_l z!b73^a!zrn=4wk;WwyBF>fpLknzxJghO4b^Oujds8M0vhQD>x z9>Lq(9C)X^TxXZ@CXF4YF-3I-nZ^+Z8vHf~S{ubj!fSV@^G%0wMn%IJn%-Rc!(tj1 z!5S^9qjFU8{^2q^A<_95emU?VN1o`^p+`@69SOI1?B7Dm09*2Qh^uwCR7U4e>aUS2 zOcD+rHd0^-HeT;`V6}P=mTOJ~%50>RAb+W3Yz z%X5N7oHTBv+y+4yYST@-%Xl~|v(?iGZ}qY$sV?`yU*DpP(o-b?HH$g@4Su{!r;|Wx zppJO<=QZ!_54Gu6MQO(%WxO>dOF;Rsl(;8DE*j?I46F>=|EP0Iz8u=)d{M{PI7&6e zU+5VzaiTADTO zCE*Psv_^z9JYv}YV~8&03lGD>DB%4)$kF@l2$F8UM4RdhbY;o@SI7bJn&r%wt8^;K zRWT*WME2J`X-=F=@k}_y@ctQHxOBVDi*yr{{H#&Lw#pV-1W`%R8LpKrSXK0R!l63| zoC)<4P0Cjte|z0k01S|WE6l078rLlcn30aP-6e^0Ja>NXVpikqN_+efA)dOjb=Uqs zLVOdQ4n?)|>H)|whrEjRA5f*10I3l7q-EN@_D>)^P~XEnXi~X>A1UQPM%Eq-sJ-u{ zq@#3h5fyZa@8WqSRHmiASx+@Nn)>mW)TUPHxEQ+{?A|9{i9N|GEZLI8vypVH1Ew7W zzh&qr`#VTeea&_S>)O&ZO-z0SRCqtIA&GODihUj46-Qmcz36^CeTn-(b{{ak#t+a{ zUKcSU5aYNX<5(cZF%big*u3G#cs&rKPsE7blxsQhj(fBhDlsPx(=f4Se+ZR=#GnXG zN^`Gtr3-h>?M;z)$i5AEr!j9j{?fgb0r2cWMh6$}M(^XG#CK)ATKIY4?)pD(`omiv z<)zdQLU=!mfY5_`&#}P}EKOAcJ}g5KWvI>o?tAhwu2a4MXoDNS!r>NLq;nW=N!3!b z8v~65#s1PPKbjess(+&XV`MgUhLW{q*eQ_~eK&%9!!W>C=XfYSVO` z56?9shI!hfk)&Q9exek!rp7Ip5dN9U`5QT|W~FsA?&Q>81V3Z6QoDm!eCg&d{FKX> z6N+cZKjN-ov1T}UFWxbg(Xq>>Ha40c%U!9TY}V-IV2vu(ig(eGcD}r8h5G}CZ1IF7 z*Z7bE2yeh~A)$zGxA1WtAMdn4Dj~1EUlNOVHv+hb*9YyV(d#Hao{@sWh#Q6&lQ+5A zh_plYwV zmPsc=%V_nxiLfdOoj$;0^g&AH`vd@t5G(pXQ*g&X|0JPCDTe+){zT_#AeI5K=qOJR zTs(N%Wd+Vx>nw+a7!+|NCZQ?@h2}!Yk+)<=2ELb9)NPpr>(EMgOQUn#p_S;OEGFr& zr#IZ@oP<=LxF>H)AofUwBp^i!LxhN2KQZc{jTOXWq$RYDXB5jQLvC=FMYaQYBW6iw z$fs@~>5_FDx56T1m}DB{RhSp~T&dMy?z9{?GJ2sfkCvlvW>)7* z6&4Uai8xexcypQkAMuT3c7f)<>5pu4crpAZad>-a*5gCbEAtauIFr@8nuEZt{{=f= z3I*)np^z)qG$8HRT|r3{cTeb7^}A2bU&w~J?9qpP0J1tvU}aYaQWWC2n<^}ntEgzA z4ykpt+4NiR_(esS@S@!?87m*0a&cpJZ@XG=hxjH25R7amsqV>U-6;en2Kyk&bM|49 z>SV7Kz8t6IIp^X?WK}n^T&&^7As?L;pH9|)`l zTDoIbll<+6fl%k|2Q*EHg2g4kj4@goJ%Ln#YbRV!6-w-}WQuqoK}VZpF~1hSwp+6s z)#&PnXt*D9*R2nKm*ISf-|yn*ERfLd;o)4EyZDl4OWO|Nz7+PySNJUqRK-T%7ttUJ zqJEff*8OE~7P)WW+fU(xcK>5EuFK`pJn-mp@x@nO<(kb54lG$nL~M3r%hp{+r#qW0 zDh_SCDrR7Ym+_~a4BmivzpjW2Wgu(<3exUpvk$!P+;imZ*Cj4G=F7#J^Jf}jO!!3O zoNiCmaj^Afg?R+9+Dr4BmT)FYFp(CJpdmmUj)a!c;NLVKK~FV3;GqB&a{u z_F1WQtnG8?@unnJD14Bg^9I>|t9AMRb_Y@UJox>#H=c+2c6;P$U$Lv$^h{32ifTzfM+cJ!oLYu*oqi%&!xWqBT z3y5@pO{OD9k=v4e6o0p(To-{CHE&5UZhRUod*@%(z!=_-@{ywzrDhi^thx#dz9Jg% zaQLv{pt{N`dxZ~R3?I1fF77y)D#qq#q1$=@uX_Gx98J_q$#S36M~g&2tZ$Pg#lWay zCuSdOg}1WjviAuG8Mk7@VW>i->J*yRXC7zh4?Q#s?+RbG1C0-X-?xx1{tLM3Pq*|# z=Z5XNq3h3DNc2qN1h9p-bny-xAvUZ`_(qz_8Tuo|_tD5F@{!yh60xHc7Hf$e!YR8g zo=)-NBE2Gf6XZ)Lnkl49l!ZR&U1fDsSf(wH9!K$ZsD)3j_}$0)w!#pqZA&fT{rnez zX&_%e1|4_m#gnID74(X+r)`s@zN6ulhY`d62^-b`eY9y2!S`hYHI9c#T}&) z7L)CaOUB&iSj zl%`Lg3ab}s0RB{&Mkc`LdveU98GjDaw_FH$LVji?YolMpW59yG@G|n>;Uu> zSN62dT`iIJK7oY`aa8!=Q6Ey5z>=&V&>Yn*{kT$q=ceMoY~Dcqw6ZPcgG~jOTq_a2 z>pqC;nFz&?!W;Z;s+2};NuyFpF}jJSQ2t?2j@lpMF*h|zr|}e?Ujxku=y^8`Yq#<$ zNdSCWp%X_x>#;>WJc750>Zvnyq7euC+}!w-8?HbXSTOUvhcTPm10+IR@)zwi?mhYS~#WpRyMhUV8D>7oM(JWK(ST^^-5a zzt{X+>K9Q5B1(E9Ch@BGI2Uu8@x^-C)yf1+omMTpHm~A1(5fXIIFHi0YT+^Wwn|}y zxJbJVjDVU*CJ*p){2(gft?`fO9;%JIlU>sq`yAdk1v5QES$RqQ$Rg>@+?hd29S?-g^JD(%n;8KpT|R!a4=yXj=F|F3UWgj&=f$wlQh~3;g?lm$vUkD)zHBmz6+1W;cF0| zZ0-FSj^SZZikWj1Iwk9fNaGNI>kOxm@eMqBDcgxVFKtd%1Yo)1>~fzhonnm zI$xd&AuLmBPd3W%WdQm1G$pE*d|GP{69wA`e|>n=3ViwlEf`>fTe=fpNst8AEM>p= zJs^G$LY!nvj6)5nwhcL^DiS-~*1jX+*YM@Ir}8_=>D?1b$b$hKaIX|!>bB-PovMFmI>_5GtWb&Cp|Vjw-X;a>v8DFj`}7@Z}v6J zjU=}W+ME<7z-uGMYuKQpB-;&>2}SS&fIs=t(%Ra|${pOlCY2uKF^fgVa(v5Q7&O@F z-X#d`s3;0%ySSFSxyW9WyxBF5ZzK3f`?{BX`1To3I^<>wOmrrtrVR_^Fjt4#On2S} zT!rw@AhMvz6)qX3gk+e&x%NQ{=h+8xn7h;my=otwi9aaj-~`OU>Vp9-N*7ddR^W1E zR3!=WU@KC!!T`A!;`KPo9P~BTd*aGhf*En5;|Go*Xcc9WNZayKf>Pl zbg2cO1ZOKBg^9a-4oCG5Ds%v+B3!A=4`2HH7!x_@fH(2B+yqW-g(+1sz{RPym|bKW z21C*ySZb_$kf(SQG6hR)t?xsTN&F5v`bxZ|oc*{9!4CAcmVVH~CGmT>6^j6ajC?&c zGk1PwYkf+@1WWp*dQB_62NPaq=D zZT41$win*g#%K@_{AD}1E+a%54eA%$(ixt$i*JXv(VCFP zdK(eSOA$HYIi|!`l4{oH`}Viw8f?h`!N?%|Auy!C=YW6_Om~nS+Uj!BE8QgEG)p-w zevgP>1L9;_Vm#H5YrgtqOL^}B9Q$Bd=Mzv z9gxWvZa7B0*9uf5rWh>>j}b%I@9^%UoViT zsyKiIx;Obl#O|RJ#qtswrC(k>cXj*~fYBzf_44)6yHvluAg(DKkrfeT$#Q60>9NJ6 zXggEWWZxVmv?t_7yp5hVMEna7_%o`o`jHkEIb^cZ@h}l8qo$>Y3Q>~mtLGAUbB&Ny z7br^D*|q@mFzV5=)R;J5@uZpsO7a+x8TDd!QnuO8gWorOVX1W&0c*9VuH3$igEf$+ zNrQC7FTeh(;af)s!nuOQbx;g*HbJX}>dnU*N{z?uIFR^hASvt(WLHboCvWaK zjJ1Ck`tNVcW7p&Ywp9rz zf?Jw|`a<8zCeA$kbC;VkBkLZXkOsq!wmxJ}IBOwcxdRE4We^GrkTAEINh>f6*C{+m zy6BrA-*(cKJCH8M?xwIzTOO^ub?DQ}_U$5Q^zWlz+Mf1mgO+80!29zg*b;%i@cL;( z^xl6{9B!p|n7L_vbHiWcG*>k=+tWmkq5;rLbB?E~yx#%Q@N-kg!{S(1RBB}kL+z7V zVH;T@Edx`u9as@m=tl4c28axE@wO7u?OO>vW=ntQp@8A9Psucwgd1UCJ!BrVbReVE#eVM;jKq}?uZ1X91dl|L zk0N}cejLvyM!yOfwb8(>H#ENH;rZL(sU6^in==Cra!bNMTfD&tShjN3g4Q(bp`yoMZGiPx2K^7+7nvQ zXiu>h?_zF7*}lzKBSyD!Ic$N8^-+v|0FVKy(;z_#_t{TAnt&9HeSpFaHe)xUTrnlW z4|(PJwLss)#5s2w_w{$8tLKXAtW-519dP)m(Q8vXvEn&yg=WP5bR23m92_Z?qvJ0%QT>n|{+fK|Ov2ZxR9MT?C-$vvP(^HzT0)t_V%S>%MjT|z`#-yw9wZ4rXdR&MEP!8Yy3nWJ$ zf%<5BWm{_^{{-D1d_v?eiA0Z!{5-z5p-&bw+r|RFXhtpTGzaiHey=|V#JoA6w|x%a zFh{N zjQ9=C0THeP+|(PS_d}C*tha&f-N#Rdvc8YPoAE}R^^1NO5ZF8TQJC~z9x`K`{o?lk zJ17u&Ut6j}0--V1;g)g^(Hw#PN7~XF4c%`yhCfAfF0VhjhtOsOUReU)r+wd}?3t}3 zjAkl;_UQkt_-&mZ(C4@VGbrJI*7IwKGJ$`d%iH+fN*g~w*~EPtKQ_r_SNwpajo*c| z@zahreyGv@5q$hjd?2LT_(4S*zkg`s#|iBpCqO3fmGCA6zrVBh#>Dp#ib*YSOd$L{ zl$`I1@(l@61NvxQkBP4v2lp-QzwP7q-@ymraJmx?+kcm0qLXB5!eUalC44OI!Z%W= zwE=SCaX_F0G@Cl0$R1k___f0yM0hX}9&|!z>Cp%sm366?6mU_o2k9x<)CU8qA)(rB zKp4{Pgw7n+9=)o?BZ22Po)l!~Hy#gRBO-Fgm8`~#{y+UyZ0n*Nf?BE{3k9|o{+h&Z z3z4!zB9V}*U1T%?h6lsosgiZha6_=pCSAu08@qF%Xb3R$|3ThT!rFBlo{CF zRH70 z6D252`sr|sLI>YCK~>ZLF#iScIg;mc9IkeEq_g%<;?SyiosVNgQ>y#e7i~z~BO#CA zY1o(aCR^n@I`>Z~p&5{)w^#XSVUMq~69LieANukcp~%1Rp(N3YCq6`EW9@72e@(0p zqCn@z|45~;4rDK zaQ`taHCf+RV6`1vpWk>)ULRd}QWY!SwtQ?}hsb;KIuyOL2d_iWE`v|LvH%sWd+|CM z^CwP+XPEe1BYux`;CGe2*Mh&XV0wPmz(0!+5Aa=6y(;1n2gZF4Oa(YF?(;$y`KBQi zKXs7tLwcO>iq;ah*XNskf^Rphvw9LE59^XOC1(Pz$%bhoZ#Vn z5b4MVdJEyV=t79xf0M`^M%epgNE>0JAqxdYt#*(Idp{92UcyH$tO2CFpJu+@#9MsW z=>K9!sM+y&F#n{)42&}B^w9KUm@m17i&PP~dASK=-B9ps=51&KjX2rh6#}jbBk6^X zd3LtfCHpPHT7@Gpk69r)pfpK8%`}a@!cWWbcsTC#D>v}_K=Q2!sn65=3qbsOGl@rJ znP2k3iXe@o3ly2mFdZDLNB9A)Jo%!6)7iX-ykw6S<;JKzA zvE1q9r=ehkbf%)I6+HX^KK(GB$vsCevULE@a5j=8Whiu{;vwVs@E*t>$lJ@3V5R%? zH9j&X5aH`=W)EQCW%Pd&A8+9U0k+;VE%>Kt!N4#RFa=ercghx9g0#&|^S_8GqC+S6 z*#P_RU@m`&mM}vn{IdacE}=fyc0QnaI>5&7Y1F&g4hYoKNxfCW#L@ickdY7aBa%J- zQswXY>l!roVh`eDFT(=dCIK0Ip_|Qgsk~{=fZ&2|Z=*#J`v`##`r-G_1E&;7Ci;aC zsl1vCqfTog6vthSuQ}-0fvEtfn^C;6K-Xpc4s})ZYsVq{aQbn?p~Vma5=Waf8Yxx| zl1q;+uMdTpi6q#X`2LQ*?viymR7b_m95zI|h&}!~M zRhN5Pwg5XUmRh!Yp9}H*GT&|p;1rq_#v95FVNSAXO(QGw12S37{+Wl3Qp+i<4)$7H z1*JMo9QM~GL#`*Ab;d9$P+=`NX?|TSk8rvoMv@tbJKxVgtcQ1KBz}8_wCccuKEo+n zQDiegu!B7*K;2vud?o<;|dc0%l9|lYJ%M6zEHKzw*6!PIm%{5P~gZF(Og!q@-M2ZBZ z_yCQiU|%&`Bf^su{`1tE{RBn>TL6>7z{-BSKfzFPhnNYxmB3@U1HY@S7 zIY=NExEO#yRexsOH=Tf6#Z|Ha<$Y`()#S`T!)kOa?xl!UuI+F65g?aTAMyixYGK zjBTK`tJ)er`_xHej+rcL;*>bv0zL>}(I<0z>8N!{$tf94Y2fn9|M(Go%18$|(dg(> zN{fc&_o5I}r%%}>WCSb{kpg;{rZtQfPK^XP^~dB^P9*^-$feRATq-}5OJTWUza;oX z^|>^>7nkDK--y~ZqV}s0mZAEnzF;HUF8KCP3Qg2c>r}rzc^B*S8^Jp5i5I-QUjy!q z3NH4!ng62YJ95W`n*;^nmk@qhTO2hIhv(Zz=QFhr#Dx%e?{7l~-XH(Q>yKtHitsK9 z|M!q%n#3wU6>kNPvB*Cj(ieF%_ z$MoskSZ?B4ZoF>j=wloopEUB5v&PJ&(dnEqdud|E!0)!_X0!G$O5W;g606lSWmoTR z7+4IMgDTk|0{S>f+c<`QMw?%}o~=LiNc`pIf&>C*k3A9E+P{R4R;acAC&`oQ{!yzy z_diQ5sQP^^(AtiosxSDFk|E~{gJgdQPm+>RAAanV^xREzL}HihA7juz!N)(v2MK(s zw)-%huosKoLhSva5dXSHJNCj7RE~xLRof6)Ck*xu+$RFRd*O0aQ59~mGy2`M#!*^V z5T|vSf6Oicf@4BO;Zdb6J3Q#B(ec*c`NfV1j_l$Mf%o3gczcbZzXPhSjqcYH|$CF6NB z0Q_^@uOSn>hHr-L!;VIGpHQPN!>U=~py@=m$V!<%8HQmp6uK*#prf#^4b%7MbTng% zW^lYxYC3&GS}P(9_?K;GssJ?{RBA@WLi1&SwDbWdZq?3kmMwogPWtJtFIyD|D%Ff- z`aWgt1VtpSZ4Tc?b=_F990L{E6fWSaRBKjXVv2vw`sD2Njfwollg2FOdS|&*wv2MM zx@I_b$*LJ7_0$ zFH)$dU=tUN(5f1VxdAk6V|9e;!x5nsjYbl5C_X`E>jB@w9K{lUqQ>ioo0Qo}fQIGb z1I!T7C!dwcs&F1a2JHZ3r3=}!ZC3m__t55sP8jcV*-^hqG!3AgK!SI&$3_;-xblH+ zAB^Dx%^`99@Hov1_&`$}_IEJ_X$;W10l&zA_mt_HH=pNLecu0yKTY9n13E!Zhs@~+ zHXY=(zkv@tzi6Y{(EcHO!!s^i{egR_d`*p;C|q5ET4%1mj$;bcB-pVK*q%RHTl>1Xw% z9!*74u~eA<(I*H7GO=_folHg3T3XLMlZo{n>5XMhWnvhT*_VmQ174mPk*BQbJ91Y? z-#2#q$~#cEZ^=*(x%*+Enb)B_1OB?@UW}I!tw@CRZ)eUxeP7pCGLgO{wLClIs#axv z)kZY56#`R-tZL&~WRecZy0+$Qtf7zQQx%O>%%?OO2n1ffn?RN{PT}JMKIoIVB#xAP zCZnw+{7RS`DHQO6N`X%RXb+Q1GGuNz<#TGrC+2jh^I*Esv#V9MUbfH-nAmI!rKO`% zbk{#r3q=(zo>tOoN=>IjUkZIGok;2U8~Rda=Klv!O9KQH0000802@(QR$l1>*4H2a z0CGqG04D$d0C0I$Z%I%uUte%}V_{=xWnW(}R&PmAE@NC1Ofh9Qle0hd?|8rP?Se3_ zhu96(L~WySoOaSWirhoov|-Y;X*<(2Y2CE0>vq~sCex(TXxpal^v9T;v@;z~-I!6t zzW2V}y}biKi=mg#%HCb)YXDo*9sby)tX*63PwFuNKsjn$G3jNI%vh;VfFiL;N3S(;$Gk#7kjJrxTSIAwISc;i%O3ZX8?x=+W zP);)o$`+L;p*+NFDBDy%2<2gxfpW%`p*#iU5tfB=mdevm9%W-t9=oFy4nb+0<)D;< z(qX1ANi!4o5ECN859E-hXS)N9LE;aOj<)LkBEmzw2UGkT!THMRote5qC| zt<>B=o3E68XsmdCU|wnYm1d(<%g-1=8oMm{CEpE37aO-rwFdj`uEe{e=4vF|38-ObnWF`T!W!Rd+hoLHe zR}IW!u~DkK#bRI;i}fbksNu6+EUs^qYNF+?#3!LQf%|7q-)z?1)3uGybe%)6Fg&oP zk8RtwZQHhO+qP|e$F^E?x&>4}T=R5+@S z&ub?0zn-W&z8$Jm`_xq(cq|9ce~8nqt>-n$?L^1)%8MqI8}0`_FpvfJFm!40;?WWT z!4UNy5PUMCrFYy%<3r}dr$-Ml#PHb*+}q*XL6a##qsu|F%|U`bCJw{?30e9%UCkMN z7$voU!{E+zeb1wVEukL*`r~u-(`V*NJmK^?0uBHJEJ4B$_pIOJe$b=rIm0U&ghmfz zDHVQyrQGnkUW6ks8h%t5M6W_++xI}ujQs9?o)4o!3@)|@hlh)gKz8KyML^eP9)5%y zCOR3~-637f;O_1)fOJ8~jq(H@Hhr{sp!X=Xn{qaZY@RN4vS!7yz4UKvnQ2Q<-mw)j z#+_A6Lo1bB(j8#qDJariW!Y}9Yn5;{RLcGTs@R&==d-;Az%r9842R~qfs2RV5j`fE zFujU-7NCak8`&qZVM`7`nvG*e;r`R%Vj6;PEN3H)59v*@aD&hmU-L-REmsc4)&G}} z8rpqgI2G{1k$O2F+#aUlnvfaz+U&oKuN^J&A=W5%J(G9=UBTCRxJUSaJK=|BxL&RN25!#w6Bt_s5|>`k%MKtl6xz19yeO zPK!NgmI#Kd@Fb5>+}GH35H`}zyzs`ajY2`Se4$HvmEI=Jg1QpI6}>QuzREtK|HcuFrOg2=0qfjEx0+#QDN z^DpAA_m$mX4=9E*#~|X6r&a6yii+RCE0fz&^8X`9k>~E>ihO$@wb1Y{Z`aQyJ;$CTR>qpX{59eK) ze~MmaPCw7QD)Hw1xws@moBfV^t_{liv<=mQbD!pBQns`FMmHoqD)N!o#IvH1*}w~O z;$7bU|0dok{%weJ72i#4Ys$4DCzdiVWiNTLw@$kIF6nL|lYU>y z>)&PIgHCA$pfA4XW+T19+ZYcgMjsyEF>V#N>^aI^aPKGbR5wL7+WB>P%x#!`*QBWPhvV%08jfquy^`2bR-jj%k%OBZX2=HWJ{S1g zBbFjK^9$j4yTT2Y#OBzd|6;cJgzIiF25{$xG|mrcGdrj`Quw7vyRcPqCGy$Z8|b)q zKUMhkgNiNwhknLM)g?fO9wr^*$gw)&D6yhjpPYfNLArOAnhLx30u=mJ<5hubVPU(2ADTD2vZz+0X#__% z-@$*(lEpXOxM0xq=42wp;NH@#_!PYS@_qF|ZGXHq#BuHF9$hU=K7T)Rb4Ir`IAU~Z z2fV6uSR1TVJE(hVv+IH_SE~3xP1?7p;VcC=IhgqmDEF99JeWwKM=CnMsBPCpdiq2! zVbG2OTXpX2o47p3ynYDb8YFfFzq)z{rn=stqJxrz9wVKOy*_{9`;!W5Hc-hD>=db> zsG-$^biN*sCi0}}%o9nSqAwoQW!FLYPdspoV2b z%^*dK*2CApF3kg2?-&$|57cVL)**Hk_s|>jL$C$_T!USzC3b$fwMVK))mfdIRY1}} zOacz!L|u9tx(jn~nKtI=G5ZCr043vq$ENcd)U8}IN+O*=`;>0KtxcZk3Fr%p0pA0a z1>Ya3%@M9}{D0yeNi=~1UBcR<%an0-=g5TpN0#RR4R@hW)VxDg&Is=~gC zV865$sthm-SWA3Q@Sng`n>|cppz?#jWOKWAnAK@LY!azDdTsB1N?@^7s#5XwEY}+E z8lrHt!)Vlrg~QO(LBQ;zd4dkU@a)1ns!wl}HbyqQd05kP7zgCQ`mJTZ_6JMcz>hQg zDez(Y3UBozP=G&z=1CUhxM~2$T|DMZ1x7{i*R?R7ma1AvH+Be*{;h>7C~X<{BQ9pi zZ-ea+I5w3~p~Y3f4d5#Ey5cCwC1Cx(N-zSR7T8y1))iK6%0|A$669tc|ELm;a&R+~ z-UtpUa4vE9oIeJ;GbSJus8E+~Jua=l*g?p)CLo~vKZMt*Baf$pcObmV`Dk!Nb-?>N zhdB){1FI;Z97rE2yV!>w8Lh9q8)0AxnIx+D^L8polfx!Fq9^X9FQ>ye8jgD>C;9k( zUyf>xxG~<8hj-cGL5DpAhki)QSl)Xj;QRa%28+T8L}5)aZ=z9Arwvoi27ezlVlyX; zKitR(ca*%y7^kvsT4>;eb)lJLoe>InN`BO3oeAL=n&mwPcWXKvzohM5T}>Jv0h+QX z(bLtak-xY)(bHB5y_P1(4k6snh*0R2q7fqD0i@E}?qIY+71d5E4LK;@z_VXm9Fyc2CiF{^m8z7V$hG zFH>XJm6H}T&jck}fInugPl^v_0$b+$CBAv_ij3Z#}`6MzmU~PL)JC?k|DML@OL?+(vnD4&cdaln_Di*h7ykU z_>y(Rpr)V6tgZHMic82(^s7C2Qb;R|BKp1cBb|$9y?bP^(m=~*=Extxf4ws=xirnm z{nmnYI6qw26a^tFiUI@yaut>4rLG*R^_m?sH00mz3%2bzt(VKWr5{ytHjT-G`c@v< zbelDD$?B_Auo~lNDz9S3TDiaKgS=7=mP2f>PcjkqG@3`1VDfMj{v=n1?g9D3jYv=wnO^9f&~FC=BiOK2aLb}9zIZq^|08u z#9a_Z87?eaIsnzMxN!WK8MeVLB?u=AllH=TOA6?f_t-Ez+Ug}H@gCP@8eNW=M=8E)ru5;1Zu=PgoEzAZyfXZV_-m zu~nc~;nkTz#6bcLM`^;EgQoKLv&OZmOPQye+luw8V&H!ni(C;7q)83&eon^!Jsk{Q4c3_tKS=PjBmlY>Vkg@lL89)v& z1+kaR_aW%rcAQ5%;)~x~O{)&mP8dspBb!qFAvYk<-V6wq?L(&X#`CzOd05PUH6!nm zJ7qiq_)LFkVOgaxK6A4+zmyf~3mu6)`T1*`1IjId#$59RIfW9As4|`kpRc)W|I{Y+ zFAz`_x%w!@#pLd~6}9TF$~EKOQU4fOK#d>}+XBH<_EyOSO^><7{QO5igdnab2#KIo zBu>8R^usWdP$c196U%2Tnyz+NWdpHqAir2mH_CBi z9};sLbe}PKx;aa$o7VSS@VuSI3^$^Y4-BH~O5F;$Q=%UQOVxvJb}c{4{gN;Ek-A9@ zJK6(z`=?S-c>0fGqxy&-Nzdh$TAuUGHYv%)|Ep+-Kgxs0i4Tje44io2L01nv8<4_x zPcIA;8~X({o%`RrKEVW8)LW{u+}E_(g!J$qj(P83PR%J;=%oc~b~mIoqOhK0&g;W_ z)W(Ajeg{QuMyd6v=C9E(-8U(KyEs^!NddFFsd4O_C^_6VNZ$rhTYQO>YbOh0jtF?e zn9G`@80&zg_XdMMq5vfP4h=4siw_`QJ<16%%(e!U7OU}nRiInQD!9R`Z$bI!SEh?M z%|v=o)0_Ep{>NV!d9h*?-7ok@($VfaH-sM=AmbLJQR{KJ zpWD%$k@_)HoO#-xDbBOwal>#%LMeV|xCi=x{)TWP3@Mo%@kIXjJPG^nf9wmGqG6FP zn4*mRw$dM%A}T8V3afz5QY6ucV>>Pu|9~8jo0ah;yf{q)VHyXdQyfUmexXne*2-cF zsGQ^?h$Z=h*1f25z|l*+G{lYCeH}Kvt^3wtOwV>s5vjW~gn7tsE0;?mgB@>tzDuEjZAZGAm88?Jc4%M|b9YB5JMuM(Pg9atA zZ-d#6P0Jcv7QUd2>}W=EARICVa>M$A^(ek?W81MLrzm;C^3+*^JU>2OqG<5 z>M=%O4lKyfIrXH0#H94~hOkD)0mgDSyL^3J-h1EZ0N+4%T*BF-v}4$ylq+lY2Bl`c9Y-;wbeMs4yM-RPTHDBQuWx_^4 zfXYM(1VN5hR7fHIf<@OvLjK&2LjIQca!3BS{dE(L%LuqbA;Z$wS9<7NH0djWp(9T4 z6TN#aLvcid9O#VI&*j7YjPU}{Rcn3pZl<-%|Gm1LGs7W};?iGQJGsOnjCtA+7v3Jl z`D&a;+ia(ajJH8GMou_vDo9OHXi|&{W@sdf_y9{-1Ty~vies_aK@35W%i3g>pbzoS z>@E&^3OQrzu~mTYF^<@LS;Ey7(cx~VOab1H9R>vb{fDrxDwsgj%%UBrWkjQ(J)?Hq zN)Yv4_v|QDz@kCWBpCLQ4bY1@Q3}IF#>7bgrRQep6qJh`0>BygFpim-29abB`+FRy z_y@+cGJO>6sYxQCLUguxxH4tf9n90T_Md^C1o8QYEexQiwMUbnp- zR6D?E{d+pXw+@Lk&KY@Tg{mK9rx&I=3Wzo!S}bX%#r9U9(Xs8qRZ!T*M+%KY zN^!CZL0XM{5?oWyD~3Q=WJRP`B2Zebb+-)MLY9^joyFG}%dw%ZaMu7=u?(QLIleqH zY*Pct5{%gf{|x+zZvioW5ZQz)%;tnNgHes8GW9sd^Ni((e&J;#546oTEX(tQIA`n4 zDF$Fdx5+8~UHM&3)BGIY`5Nma-I@yi?KnS5W>Yb+^Q0S(iW;;^<+W`b)6_3iMb^DX z*r)}WyywJ`z*4QnZAk7dwqHmW(wZo}qHA$OnVMDfigD_2nc883D5nscDfMb(IrY;_ zB4J+!5GyrPZkevnC6s2GQknV?;uO==Ic5=0zYqGJm&+uE7MyL7 z%ablL$#K5G&bh1UoSit&R_H~IRzQPFEds~!A=|GA3LTi8U3oS6pW_J4E0TK}? zJV`$hMP|r}LhXR%<{VehY*21p=6tPRmXL!77QyKT3-ttPQ&=434W; zEc8V#PD9%~I8n&q*D5%o)(Q> zt1~wTo(x>`!H|U-Lr<&I)!^Y*=)ueHZfy6kxNV(|7IYD~yXGxN{wMA2FvbsFKwW5> zp-Mw4><1+F%Ix$EET$Xuxd8pz6My7RV#uej^>Xu00;-%enA8Ta#||Z?7f1pB!W+z<$o?MI?TIn+shwNza#1NI|ANoMkXoqE*fGzcv)CJ>$(cC{M}ZkBs)TDWdOaM zvUb|zxd^YuKmT>UECqV+h$UUR8YEmS6zIXALT%m0w85cbfF`pc;=*}GMPy7eTkK(8 zF>31D8w$E&$f6gEh|#YYu83N|oNs9WPts4X&_g(nGor@)*G*)K;9giu$cqbByxu~b z=88xO`*udh-$Gev(oixa+lW6_D2oc+d$vwED+TBaT{1kMW+LCoAAx|R=^mv6c(;A1+MIY+G|64i!j5SSvaeRfOFTu zdkVpJ(0ld{;KlC(A$lNH;EFW-DWYO|6>}!r@gO0wdAtVFJ8R;jW<0-)PO7R0?@4688?KbNzq(+&E!! zjd>%(dnDgZG>Cq_!X-(5@n83U_kikw&VOeEK{7?stkj?-U48VGPTo3mq>*C&@M%btt=LUwWlOL8e;6156o@aPkg>VDpPyQH)9 z?*!L+=i}ca7txz@F`gJ^thf>-Ykq6Jq%>zXlNzx=zZL zABbU!+%pxdj}yL^AX=Cwj(+r9M!_B5?E$|fT)Js_o!C$&@n?3?H9Zj3yrJyU3(<=2 zEB|ZQ&ih)$8RTdWm-tRrgXE)W#cXIwTIE9bG7PLB{cb{t7fT4|yI99r6rQHg`%#JI z=LGL#Gi)XL7SsLzFb3|33g-SOoO;rIBZzlLX`_pa<`og65SBU&pYEc^yVu!F)o-Gv z$N0WmhX2!9%}VD_*iPAgI#mK!liafGCaf;i8yjCmtUvrK=%$RMIYJN~yo!(Ab{Hc+ z#7!oyO3kjJMO=xh3mUe&P#*6wdR+fJ$wL^k@tDl}*Tt-j=IOBDmQCBax@Af@rY)At zFZEM7&nZ}!R4{s+KcW!iqz2U#b_~~AQh6HpY3Zsx#6pMPdU2$h85S5-DT_%ypW=|qF@8T1Lc8H9kW#KX*nMncWJ90!ClkoQ1&qmc&M`ifP zKs5z?SOwPHaJWQ@<25C9h-W(bn8$=cn)XHuz)x2*>GAblb|GyKxHV8WxGV{|bbLF$ z6$RhkN6h?upYKC0gILP=`D@6w8G7NT$#dlXO)4Mk40F$etN_P~CI- zn)kG%TUA`J81`C7=K%15L?OTQp=%DC7@)7I%)~WX%a=UrO^-npSX1C~Oiy_=nN-lP zn|Ubio+^;ZOROR;4;BHP)6^1mRy_3eSXVSC;=GW6GLn=E4^#SM5|1-+XKOaM7=F1s z)zov)qw&)#efmf0_u(&kzvo6D@n1HuFTm=0`!g!D7h|r8tTILCUG~zTv%gUxD8n(ugB8RMj3Qx?2F-drVW-(qzR9?B` z)W&1zzMz{lu;YacqalR^-?t%{g6cw~ML2_{0qE?v?%xm53x+Y*;pYKJ{Qhi&JAv5TZEuyMT+ioFfDR z;zrmQ_5H1);Z6L=EBC^8Yf|q%4Rkicl^K;cMCC#u<#Xk+1;Zc5vnCh0>U^`&Ge%~- z;j0{SaO->{qo420OeRVU)C3gXs0MgxtRut#YK92`jJpy1SGTK4SMp<~*v-x`?Bku4 z{0H2BM{O7+C-XDknU(f{RQ+aC(T`5l=t*K}c58Hrnfuon@>dtz7k}4JKl1)xOF%`Z zpod0&G`|l@vct6KORUR!*no~6Q!ewdCmax{ZfN`i2NZ*&WD^<$dHk7VESd+FIXSpZ zVoZ&li)I-5zVQ0iEj5f?x;Og%S49$#qJp#WSDVz1H4H75BwohaZupnpQK>lNtqz-XnF0AW8#i6p8{6V|sfn57Z}+v(e4 z1oLAHf==8q`yfmL^?dkGay(vi0Ncc9Nfa(KzXakMBXPr`%lm+kVJ_;AcY4ZWQ*w4o z32k`9aI!1TUxheVwF&E|gsJS=^Ba@t_ z7k&Nz8(CzVG-bCWfD(2VL+@y3OCd;QFMzfVnx>+vJXR2~DiVuaK}>H%!9rv8|wsuuu?&5?}sLL|o3cf9B9)B7Lxt2M))-f=Z-tVG!u+q-LmI2t<7xrVS zhYwhF&$?X6AZM-NTHTU3xawIUuN9j%nBvzqmLgm?wrsd~(~<&8FesExQ~|{x+%78h zx3HPi+EKzwe)mkDknG$ZQ1?uHlT5uBBsB5ZEDCL9hw6}K)$U5!=U$U+LR0PGW|rxQ z1lE8mKDO$Tmrs(6e%eG!9qshsf)#=vlsJALdfe#4pC!_9m%ZevxDi)O1E2ihBJRhS zEF+sV8KEDmR$wP_XSjs=iky;DA_jMcL20n<8**j>b^+#9|7fBc7#%*(HM_g-S}40@ zk#xjt0veIcTxyz`GCJ~L^DH2jKtZ+K->>vn0-ISj+^EM zD7ED`l+r^Se&M#&lKV49+KqYD(b1ByZK>+U9+h+l?s)wrdXp^CM3HGgbC)c{D_bO~ z`0|r_^^euYAbZzv>jvM2x9{k>E&f~hA9H=rt+spQFxA>YmdDRU7)ut%(x^@_4_Dks zU_bW(rnk!&u3o$V_SUa)74Mfp{W9B9PT`T)^+9f~+n{L1j?^8Qkg<2py(W#@F@3si z5r#;qY^9QvYE98HOz>sF64^KC|BlEp{PaPv{~{6OzeVtWB9fMoj*X6m&cxE$h1Sx} z%${CSPFY1zMn+VLdYon-2;hJ30l;gXu0Rg}AR7q)fcO93Lq<|qR8CoR#8=xHTRiF3 z`zH#jJ0d2)Vk)n}y&6(n0LA10M$}1i600W{tW)5*av}Bx+VZ-N|HITww-aza&X+T8 z2P5&ic5-s^tEPtc`>yS3?zQdX<=f89)SkbuOG`)V^Y7#3XB)>8>+k0_tF@f^>uCA+ z9{$V!b8-W}_wU!-_}a8*i+$v%tjRq6>|6S)&~bKm?dq2EKks^lH`P4X=htPr%9YTc zv$89<8~Q!pHoG-+*QfWLy3yHuou)d=m7*5zHJj*K=Z+hzuA&oLwG%e_&05Md-!mHC zt`g9;cjE&s~!1o(jjGdY;p_dRO`mY-B;<&8NG|RW;?Srms?6OY*O`e;u?=1ea z4?8}B5|sD(jfqp{XWMvvvDUJHp5MyTE4;GWW}`l*q(OItm^Mf(vbNn@lmhugALJq6 zll6Wk$z!0;p)ANL7^M7;vj>!J-AWa)ZFbf^($Q>L+XDE6SY9-xO#qYvxlH#CW4P*) zoni2+@$Q5-2;GQK-kYbl;_YtTCKGd*b3ns z`9MLjbA!!|eC;kgiO+YFV08=lv`cnvmuQ;E3lTv=lHyRrxCh|#E?e8-f%;g2($=nS zDDkD+ocUm}5j5B6IE18sn}L!(on7PwP>ioNE;~V=VO~K0$>@$UM`D88TDoet4YDPj zH%y#VyWL>OLEH;qrg^KW%pdCXjAhL!Y=LbONt{Lf}t zsexv)XvmZzD}Glgs+6*oezib==(AfRn8cq`C%%91&fz=-p`XY{Y;+o2Yw9P_)}6HG z3kk%wZ&M_WeAyO9jqzDook1+4>t^%9u#NZOh|c=L9@W5bxLs8cS}jC~PqEci?7#{h zf?9p{C{&C@&i*d*qU_f)IU&!0T7k5Nnb-+Zpaq)KzvY@AjOH6N=VQ18BP6RiXxW{sKHwSqmVf#vBE#eW>2`n9YlJDyv z$3c!r=o~soGINgyO+N2d>P=!Wu*GglN^j+srAJWfmOCv@isE3k!5UBQCF)Hz@of@6 z{DdAqIj)_Eb0gu~+5cJ!!u`m;$gwC-(t~ZD)SC=6%!@#m&g6(RF+ak8cR55>yYr}B z_vp8a9_!z`**mn+N$Q=dy5SPm>@_A+{1!`{t#b`ySG=g#5EeYiGvAfPITEd=YXdIgUaBk;%9cBlQ?+B z!`mK(TL^bvTB+K*59fpc zl8S1PP!j;g^ywFo^hAot<0MWKP1$sjEA1|3Q43Qe@2M!V&^F!-4oQzfW)CH{QrZ0Gg6}@akc1EWI zhERm@1_>(BoL<33N+cp$#d4OeWvx#IOL5aYn*>HQh6xn!(OyVZ)hf_b(F(Q$D>JaX zd&`03oDfhOVPt3nq0+vpc$l5g{$y)d?HsuQJ7z#|F9=O_PFhnVvJ(%`iJ>}sC<69Y zxH8Q5tb;Li!2*f_EC`t}KPpPKdICY--kXp2e8v5)2S1pO-zsr3q{tmzV&rK8$N+{@=7;W8sSrQEAKE5ZlH&utzw zE!``(Z1f%(GpG<&kN2(Gb<<0G)*W_n#0JZ3$34{dY zQ8Ypho#^MI=++-uCHS*$H2H&qH#&Nz{O_KTQ)8_Wo!fUu77e9FF*vNHx~@pirJ1Y3 zZYh4A1Dx2uy-r+zL73xAhV+Ik!q=oWIsUi*ualfjzW!~RDFHNMeca^+AqPbKN+7EQ z_k66;z$*AoMa6nM%s7z(WbDy$M1Gi?$c%vT2iyhwCtmv{ca*39X%t!#JBH&MHO%87 zB{DRQyKBWc2tnd0otJWmKzv#tF6Y6}l9RVgHWZ4}LkeRXe=3O27wIin<8&j{v>kGAAsIWF`yGX+PECjdXbMc-&|7yi;C-d$Ks!LR z6tCQaG9*;qhFK+~HYQfRV=d-|2nFAUG8uG47^(%B!Re^pfV)v&dwmCI7r|IdSRRPq zP|Tt=>WhbSBfwt}Eh~7N4BJy)$hZ~xC#b5^$or==Y+^w!=2OH57vdzct13NxB2M*y zm5R2RmTK%0iwy)w$6D!RfHKG*(JjqEbe%g%d*ln8f5^o}@-U7vO^@wTH7SpBfXcQc zd>$Q&HF4vRuxTV5t~dm=v9Q0b1uR(% zJNhwc{go%=+S1jx9Zm5j#Y$9c5F!Pz{ zg7MLIWK2d(K_RV6TGjaM0(m^T)(o;Xbd?=pV?%8T-u35)H^8| z5dlqIPWY6oYa7n7Cxg1$h4j>OgsfB>HuF6^!?>V8MEXa&NKH?xtMkT7GnWbnz<47^WeuV&% z5)q0tr?nqrZp!F88(`St!W=iT%D0RXPX#jfPNLaiJhtG_TST{V672@8`!n{5sdt5P z$xxSz(0Rt$cU`p_vyqy}<;K+2iAJpRCxC+hnYe_^SAK2Trf5&c$OTy?f-FE5el1QA zOfBo-~_2i6@X=r3S!^S3{A`u8)Ug|XVuLm$`+d81Wipc$7L30 ztM5skwxdfJ%4!5H%`}m-q!1u!DA8yUvf~z@H%BmbPeu5f5*D;^C)^=B1=up`$tFP3 zd@6vBuh88v)C0bSxp8Z%5mwEW=8cq32pBW&!LtF6{;Ip0WYP%>7ulZ3{0Pmu{ynbQ z{T%@~B69I}9n10GOs6dz&_Wwu|J;LtMxaa7Kdl@z`zc6WLavl-61Tvyw%R8`$v%Fd zQ4{KZyP0t-f%)K}V_#LYEg)-{|4^*0I9|S&IY~=GuU*?vygChyEEVt*a8bIV!1+TItOPMZ6y$z?1f`UL7iMI#p0f(f=UV+ny$D4Ch^Wrd0*`@N}Yj&OY<6D9rRA2$v#=;Fc2Cm$#wkB4Cn=T3=skgy+u zy%O3Y2I{#ypLoGNwb$W7oQ@Nt{#eGdroKrAI$PVLEBSY$`7ol8vd8GMRTk zqAR*kF>N4viUZbW9a8YUG;7eOVB`GRR86S87Dhx5<_X-68$A36YAp)~jT4 zIRV55Pd9@zMz*)RM~Bq=v$JNdD9Ar%lVy@a`No}V@#+EOgV2SumShrRm1Gfxc;*0<7^j>Eu6xgtio{SKxh<1 z$ib3j@T=k-3=zdHpC>d+rvAsPU2{2_eMlAL7bgizSo#eLfa1TA#Iu%)ywL#9*wzL! zO3V!3VK4bH{D6VMtNLZ}%7KMPr&Q@(54(mHSX-&}2WyJ5kfjF&19^lAcqQR0v~}mk z8lr~`9NOSPuo}Vu;V&O~liYT*%=F7E+Z_i8^zp|+$Wx5VO8Xj?h%mk6WlHal7+@>6iF9#Zd%WWpnrQaBl|FfxUL zwYDIX5CgrQr(h(yo4RDP|LEU>Zoc*Lqk!T*or^Fl!P;^k%v05VXzKS`*60qXC2ZFJ4F=~qL0jTK!TKR@_XOQpK=U?C|z=9KpllG z=cCwGW9>^Sh~{ik4B9?e@+1mL1XM=JAE)A*fF^|9QIIsujJ&ff4Ijp(ihr45iAw5S ztlZN$Ry`rToZG*eMpV`lvd>vE4yp<~1{wtFhSJCXJfNiGWzR~PGyR&?6px5SI2)yt z4HQz|g6ybBOLrs{yNb9^H0Rrp4)3g)onQP+WYaydiLOK&LYby+#4G(6tj?L~;R(HM z;bU3QJ<5F%C}g5Fg|vRVoGW)n#Fd@CuN;B_!p&zBE&UNUd_Zb|VG~*iOZrTl@s_bf zqjT#UZ0sBvIXQ6-I@Pkh$5J1~WWzSr~S3`U{mX?Wno}7@2 zKXeGf9KGV=U*~mO(Tc?4GAttDFPnt8mokAQqqH# z@#ufY#9~ZJt*Qyq-|tWfzv^H59|ZA2f&_pR>nF<7*VStzQSh?DDE(&r#W5}KcP^h| zltn!=1(A#fk~2jMp79OS7PJpxX;!{Mu7}K6bmaoPGY7fT1%rYUi2)8vAtW&ptDbZM z=J@ZYgh&R-OWR1MD0ht_#3<99g@|p_#%2+MSCx)R4T&#Ost4vPHt50zBVkK;Gt8D% z?%ej3cB^0ObR;@w-3U~UR@%4WRRTWw0>Dr3XJywkHpDJj zT*R5|f!to^Rz#as$%r;XGDZfHMpA(oCv+OJ#64k1fqco@QFX`4vLs_sh7A39k`%=F zv$Va&;{DkP2$Vv^N<0^aPcD8b7$*erckROPlDWjDqY7jNT|)vasiL@Y1vC;ZXVIBRH+cy*DCZlS*#`wC}Yz>Gg4_aW+uAmMUWH{Wvxnewb7L9mO|-r+<>5-05$Diu^}p+ z`XA}_*Z?{%RtEWdq7#y*(FN>qe%b;Z*TB2<%VgQsw~%ud-!i$>K)4nf~pO zj*RqxL5vc4+wWm$l{jf;G>iaSp-ZMeWi~VDJ=Ilj;(38WFh!*Sd4(~91?D@o3;SaL zGK-`nJ$W7`(hI|PlCa_?-u&$|2_@u_ zM|r*V67{@LEy-ii^x`GG0 z1M@o5qOhGre=~hApXx3^GhaKU7}mt7!YE45Jc;6z6>)h?Mw^}BM3Su?_^wHn&XJsx z?4093uJg=QtzYb4>SLY4;~C@h6~JRrl1Cs35H5Nu#BH3(V0echO(67seka0oUGp4l-*9c4*kk#HL$L%E+1sx z+J^FsX|#=}Nqp}XSxv#?4y+=tV~v`U5onva6*;g7>}92F+g?MBhBPW}qB;dJ5s_}H z<`x+({*t1`;92a~{1(IJ_9S73Ad_8P1Sos4IINc;Pl}W1Ej-ABW)mal9SO2PfOB@F z$_ArBIRlj0v0`&P!CQK2^#EXS3GRPdO#mt0J40FMN(50jY>0N>5$Pt{fW3J?t=e%+ zh*mNIW1Qub`phLDv3Np%wAv7TiJdr(ap*LaZ+m^bltv9D!uC?*6;EzZ+iIP-;eM5` z{$`^cKWA^jI*4AP+8a>2yk;&(1i{PD?Znoaq4Ti1F>C+`NR!6kw^H(EUfYEZK#vHh z%vSM=BJ3(mSvEJ(6=95L)G0+)A7cSbnpvSK56Z}}Wi2W67FChqfRLzT9i^gXrEEgZ{L@W|wm^7U#}r=D zfwg5)Ql|jCfmzY~+9**(90HjSe1TbC9Y^)!Pot{fk`xeB>b1gzq4MUZ38r#Ug&Nuo z?eP`HiF|&wOTPX`SjN`r>1y0@a1wzd!r!Wn*&(sX}bZVEqb2> z-4|Q40!>=+#JH%7AmcA#smW#eqn<73%690d90-lhWtbE*tRIb#6OkV-;Ms{Juq9?9 zS=&ZkjF~fwGRbIF{{pK8X~oV#(?gPnKjFOc6jnyF=(wS}635%>l;o`~gm=}pc(Z4n z39=s<^G&)1!o8ELX)h{?HSUw8@C=hAzJfR@-~}IYClq;ql$kVNz1vqm~~XV6#Xpul>-$>q$| zBK{t(;W;XwdpY7Kz~CA(koSe<$`x{~ZZ(dGt{GvSm4-UGxTHb!o9+?+KW&QykVUibWV&FLCxBhXoB*)ZRceu9)uHzGz}M`VpT~qJWtzt z+amG87-9gDKp&+aDn2@Q;`G??Y+SLBD5kc1;12;XsJ5p1OL*m*>z1%1`nbS5Y>(#_ z$JC>k5N!Y%VXLmN9#8LkD2P+^x8@NX) z-269Jahj4;SbP;`X3UgeRco;3!05*j6)=xWpk${q%vQ^0m>YW!vf-X!jSM@JtpGK7 z3Oiey6Y(B6hZs7!g<8b&c;e3gK0(g~0c3B73|*h*7^0I;iHt(dl&CMfG-^-22qmthns!pY;A zlp_CSnY`gSG|jWi0mk~xR%>YQZPZOpyUq#fk5z2oFfz>x@CmvXFlTK-gHj!=5P}Z1 zzGzlPnI3}x^$x~T?jX=Ugo!kW@X6H`CnYydQ=3sVE!b#mKmm^J@1~HDdN0p=T3*3k z-fW_aJk)T)M(Od$rjE%p80$}KjZD5@lZXKz!7^ig?#LP^AQgP@6A$gC5G8epXt@La zRva3a$N_ET;=DOO!ytg!Y9a$J#YF>rT&TsMdlBZQyU0!?=eIr+GNHULA>)Xs9(#2X zrmL2V*AH7}@v#~Zagaei#9gd!%^eVKo9 zT-dEIJ}V+uYcLj)nV9r0E2MG^tl$I#aSVMJkdJ);A~mE@ER3^2Y*xy1IS$7~RkcHN zsy{zs*w9%18We2Bj7iJ@%euqk447i5XoN`Xg<$k)LM%or5j@me^kOk)N zxo2jhQq$cFc{?6a;u-m)mDSjHKIAz)!2*ed%$$oO$v;?Lshy28`s1ld=42GQ0eMw4 zcrwlC>X4_eU|VH%evzDV^ld51*9-55gBgsS4Y%4$mSI#C*w0_)!Mgb{xPJq5Ofw*9 zUoQpz{R%K17MPQ;8odJv16n3*zfSHrfOW)AQy`Rn4+8sM&@oHcuJ_)HlxN!7QH1U~ z*ZM*>mGw1xXzGuyz*=wJDX8qRo zyu@TlG`mz(fE)sva747=Kkf_S8xj zl773!L^~!VxTYXvT*?-Xo4(Fzz4GQq8d5?AM4hZ~QpN6s+;Ul^^E%*df!iKM19*jA z0AZ~&n!;9ufS!ssO{I?dk_n{Y}{?ai!7iT=`yuC#Dd6%6; z0TJPvi2bf_XP&2Nli272i{`r9MNWYsJX~L7zS&k1(yx|(qzy6h8m&*&LYOyx(2eCS zQIwQoN7z%(v@^&?{9=%os56maHhw3%O5c*{)oO=#M&ir6LRU5a0^-=VLVr5v`5RI#Sb1eC7NtkFU9s78aw z(p^K6Y2hhu3Kw~i73;wyzWE%&6LL!{$q!a)Y(o(RGZRRUE=C`g-0(2@k7>-&X$7Bt zedoEVY*xJ2rEapth0gu(y|fvq)T*$mI-#%1x?n_X(ZxMUrez|}X_kpl)K<4Ylb`5( zIzE`LCNeo}{9h%MJgXLzQMRS~OP1CIV=*7&_L zD8_GdP4M^&)xuAF^TI8TEy%-?-af0*f-Ng)Or{~sp<3une>r8bV#{X0V%ch2uR#~? zfE=l%?vBWmd zq{~ziwexBO^2-O7>0+^r`Qq4 z2$-FpIJqZ+_o|&z2evg1MhrRFDEnGJYef<%;eCCE9T3S$AHUHgroO{22@db^aqk%~ z>OYRBr7vy4jwAM&vUO9KI07-9@E-uBoWe7#vXcEwVGIjCRg841Dl2LElR@ zyNlhwST9u~6}U9VZ0Ut=M7W)=H$>OHITlgvq`p|eba4*RBQg~)95GBaIKwXzd6LQ$ zQ=9%~Mnq?Wm)n?2CNy`mqFux8co(3xqamc9l|hOTAlF?Pou&z398%cg>L|`U^&h{? zsi)UBp3f#Ro{YESe(sH;kWq$lOrM%Wz}0Qi7_SC$;(g?QhH1t$qF^mPggcqw4$&)) zv{-)kgc7SpvPX6$m;f|Bp-mqzY3DADV+!`g3q?&D90|Y>QqqjDxDA#TWTQiYR6)0G zH47bJYiXFV0vIW8UnsE!IGMg0vSDPv*s9`Qco^9c4v@*WbVpOrsJ6rEoHv8;ngw2_ zMFALNLi&7IhmV!VMLSHD6-Cf$T@y1B@Ej<3-^;7NL!TXUEpjH(tCqyFT*)?M>r|`A zq{W;)PbaX5_^Q-b{>?;*1-5JuTPdALArX5udpc6Q;OG6Oyx`i4Rzi`y?BmmR_sw(n z5h}_gDLcaaFC7)yTW7O?Hzv8EobSmKrHyoe1p1pLJo`U z!#HM`S>!rN;!Amnf=&3C&5V>v>bh6F^+GOLLxC@)0MQIgm*D5}aN zl8n}+(i|j*4yrY1c4a(sMT|QimhzI9fv(MLy%mlJQ$P9Cqpl!ab~8I21nRn=>pnfU zpL?Ggk@ZHTvpqu&&Mwv*p<&2P4L-(f7~DHSqmo^-ETU>Y1r5Ke%&87@K&MiMoPjbs z<5HA61M)OO!u$FBKcAEcy-D7!42g!9)1n01+AVre&(MHvkz9M~I72O-awe!ZX6dMe zRr+S^oFaee&~Xg-)b3PveM~~vctln{+qw#L-8h?`cYNm^i1+rhyGptU!Ei# z%9INLxY_OR`Nppat)9dClsIn{`s8ttB)V0l7hZP3N4a)8{MU{$1!;2yZ*$6$^i0Ib z0ykxK(Wn}UO&)z(&a%A6t)tPSJH#;Y2m$rbc>*iUR6hfeS; zRC+gRZ86Zg8->8r7Ccj!f#8S!4%341|06$#ZX7HNUUjloe2AOzIf zNTT;tsfR3iscp|@g=G1aobC=M9Dy;NbmEgGj<^bzWmwcN-1wmFgxt}#9cg0cB|t*% z9GW)|+IIrI+i6z3DzQew(d@-4u<;W;K$>6&qxS|F>Oc<%Kmh^nYA87c14E#ctf+P8 zQ*azpNvA>h($PL)M}prmZax=aglTEG?sG((Wc^r=75vtkJ0N8P5B@IjiVGnd&n(wX zoS$)G*woap@++chrAIjNS>d8BUgU;(nessmK>%*77~+p1t{-@f=cOW{$II{$X1BA0 zM1(s)Cmub<&rD@QNn`<|`-l^Lcm|)2_AaYV%XC5>bya^b)!=*ki2YK|?9^xhRl&>s zF6`n`lh37xXm|L%6N<^1x;dk5oy9`;u3^$Nl<7=LTf~cwi$G=*uQ8<#&?u_0(wPz@ zce1Qf{- zc@P5q?i^t6Sb3A$1O2urHgw#%Iv3FJRG6(Ia19aY=+L&v9&r7`M0C-xC1>e`sI>Ln z!X?O8sL5Av=m@UwDCz=2z$lcyfIo|WZFzEhvI&s_EPnIL1+~rvvR5pr9{6>j58wIw z^0&1y0c;L>n1a1j0u2p~jr;>tE0qp}3|6i-JB--3Ely{G7^GY+L9!iCMFgH1y@3vbY% zGv*_Uj@l)(VK&X$%O{|xBk zHdW>azNFBB^Qpz95g^eIWayji1e9e3HFtki>z*$YKsQ{WcS5~r)G7_KkaS`=1g`_s z@blgy4U^H;#c*lc4-SVCL7UKhQ!5g)t0hlYVSoNMMrp3NVGf|!5^mcHYO}=Hw?IZL zL&HJ)<>sN)sHTfv9+Js}@UN@8kcG74N(g2ijv_Z4BR`Gmw;WYWjwKG!F`ZPZv$_43 zuiNJ0+#Vinp3d$=kI=1T7s6i}GOKL6H3zS%7awh*U)H0DM&L3Z-{#6QXd7LMZ->Sb z3+rK_2&629(@<&HlC^C!g{kVB&j#^+_-tYN-cje#H|M70WE8SA@S#3@u87R1S9d_9 z(u!n}ve8&)rLyW26*b=an~o(p8%#5U!FrByvC)vEo|~63M#2v*;ja_YDIZ-&0hM#;~|^)*sn#%(7aU3L?ptOZhSG^y~&BqY0v{Fs0}R`#B; zC}#oKCrV5lT~z{)$=ek62T4woCNpTpU4>n8%oQ`5rf(FNVto{gGDvKN(oMmgEts-X zQY6KjJ&_|AVsF?taRMFi^VBc1yN{)sZwE_h+SdKe?-F<`a5LW)3r?Dt@EZH?X;t3% zD=!DHjoH`tjssvT$n8-vMkzG1e|p9Is3!HOt~LZ{Mq1y-n~|=>M~=m?`PvH=D$G2; z7;r__B$i|{skF@YLa+PqxyLSj>vGXn`8DVKYcWgod98@O9C4<$BkN{{t-voXLJ1r# zc4!}EkQmUrB6!6EPRdjmzUxeX-DrR^6;47GgpTp*9G9-O&;7@a>aMC|I^6H@TKPpk zt-pob<`$CYJ8pZ)oPbybULfCwKEBDSUnyFQ5kO(@7DTfZ6C|t8%%<{rOw@;qGfL9> ztWLtGgMX)zpCd)&S&>pfsMzi>pAC*M^z(Uc zp;)QU2cGS|dY zCcJAo3zzn#=HN=#?MSRV@W&mGGTleiU_Wrm<7oSCJ;Jt_Zce=* zHdALdikE?Jjg=z3Mg6<6)2Es2#&9zFUVZ29OD~p0fvZ2vp|KAhOu1HesC+h~ZPc3% zRFYiD4<70%EUsIBM#1ILA}b$)RK8}7bHfBRbR7Xbb8?b*`Ft_`NW}|tx!jJ8~@OUjM?pVJ$fy;{gG@BoxjbDPAPIl~9H6UAtELB}Z7Ro4Ba_^rC zazuAa`$%vL*%7rV9o&sEF5pF2jHuC0Pkpu!gjUWN%^z4C13f*0NZ?B0m9iC2X+=%^|i@&?sHC+Wu<}X480I2>w7hf5q zB_S=LCGlFv-f>k1=hmVB&eZ2Jo;s+XuVowDSt5|gSlmF4ww}MMmC!?JLgwq)1jMbiZmHYx7p0jTjpz_%BflXb>sr5S|d^d;EgU?U1|V+LoIo z)Yautf(v!l>xbG5U(hPR28gS0NJFOi(TlMjn60F>oi$gr9+a#)mx7^HEMwFn$xC0v zx-e7boV+;CI+6PbsdD=&qT+p7OmnaZ84gKfUt2fBt4PtPZ*Y|p#<1KWZIGnMvFS+k z8&*>FnIdi3Ly`8X44yv)2}usjD5UzF4IC?M7Lz|Fk6xJ~F5?GFwE-&}SKuEeXm`@H z!O8{OI>s(9o4M2p?C!sPhA&ipTaC5#TwCGeqIQ<8l#`p_+_1w~zS%>cG_}j)VJoHA zG_X=*NOxEhZ}<4vIbW)Fz1x4HJTkVSL;83phL>m4w`Oi^%FFNApS0y#(os93*7iIg ziGji>Ms{mv62dE=)KWAKHO5EdM4L`*sy+-pT@GHUa^WQL7dZG1sYjQ5NFbYpa9*sK zJUY8&r>tT?&&(^RHYYV%CQga?gkrl+n+BQ0N^K9&poUXyAo-<9aCxS)Z;j8JJz*U! z)c&=XJs));;gkw#En+Nky_uB84~p@ZTH$$z6c3!H=M3XJR*!?xO$} z@!8>_1I?!;8*~XHymZGoMG}fac_U%%9|ad|o!nRHI@gpo6*^OSMw%{ZOCea4t&?GI zuWEgxGxtYBsE(>eR58(gkM*DI?m>_x`puty2PJPbiq9%Uc5v~ay#6G6C~%lq7<;0H z@WFe^742hTWE1;=`}c>6&A}$sDVc?;lk^kQg^RT=J)u#ULEQOqSr)v8XGP&X%_1O$wDsEd?`+4U>~xH zN$R{;UPHH@AM8!ADizy%$G^x#EyW66ngsh;i|V?ek8W|2cQ##SaXjYBX?x*rF5Jb0 zhoxeZVjAfoy+@fC^Ur_h$yEint2cR9JD?LKbHJybTlcEAk~D zaV~liGvU1iK)GbAxH;~?jpri5`jZgkxi>^gma)heRahG8>&Vymb1ks|;g7jtl7g*d zbXvS~fuXtPH1lw4&S|6uyBUt3d3QQ4UR0rQ;;9H@PkA&A6GXTs_Ie*mE8o%~6uCW9 z(;GM{{cx$8=p`HlNoBl)|1;Xb0vrG)r~A)F4SRR;{#jc1ztl?_Aa!k!rWQ#0!9C?t zUIEvkAMfi|ViH3uv$Y-p`VQ- z0*G9qKE^r5Q$NNtzK%993LHOUo5o~jR~YVBs@7nB#G<@`Ca?5SgSnV@dU$YD@r&|M z+4|~zJ(YDdX*n!>1CU`m{l9zV=ICT>XX#;S$KmGX##)Rqc!BYUG>cYUPiHKNB5EJj zLY9aC0L8zfG(b|S8qx)mgNbkD2#}Wj#n5dBDF|!SL~{t{DxNRS9xH-bH^(E z!JF~SPoIUb)P~|R$#W2c*S!)9iFL0Bn7<0*L}8bh>~#wsQCvM~B*}ad-2`DEMO-H2 zS&{eHM8zZTOYH2t@o9b1f8e1z({uQ4<^kP9^-{~Qu|CgB&Y9^?8ypd?j(yY-Q4qLHa;mU4ot^g=kz~+(7vvp2Mm879_?%@3CMWfS>H!^amDGN#} zU{3qN=ssh=5(YV$+}5)|<<*!rRb*k70$bgTvN=nYJxY9wCh8y-_`(dkD(5=cx|8~N z6K0K@={xfAOaMQSd7&D?d6#N-43=fSIc*Pteo5;fIfZ%~GX1bM@{@~)A9Pwmyvbh3 zjHh?1VGQIJeJ;(Lf=YVG=qbtk;_w?z-&-K-Zbpk{?Hx6O$JentN9)%F_Q;XDMt4(6 zGxOwFWBT73j;@ovwa)S%1Xh+);~(zqe`hK|b!+;ncaxO?J=WNdHd!n|Rt_#iCxQ@@ zueuzlA8J>QNr$4c7+;GXL=t6`gF)-~`-KQY;~SD=Z2hl}3?KNAd9T?M5}OcD%1p~B zwFXjdSa`gbd;D5IMVUFoZUDh$X0?K|mQRA~W}cAT23B=7_lEx(8t$V*Bg{t2mp@Bg zhD_I_RJyF_$|!PV3tI}Nxy(BN1D%lTq)Pl_22P2mh3;CVlCf>WZm&b0sl#{gV0xzj zi<`j`t-LYC6TBa1G}Y5Y?*baHlfEQoeZ#xCM$3OkhuM)+JB?KRXKS z3p05#?R$y7<*azNuD0N`u| z0AT%bRTt22)BhA+|L@CA6oWr6zySaxFj+F*AGXH^ziodDlmFZPr^&A{jQxi_6c$w;KeI3fPofVg0I)6q05JYx%=YZJv9Ym}x4Eggjis^if12)I@%Z~p zGpjp76z5h*Yh8lKil;GVcxH6|I5|aG7li)>O~{f%~-kw*$X``9aLY|4RGM&B1-r{UQ7>QtQ8y{ut2z oPW-2l{1>qn4wd9zef!^WT}=`3r^Fk83w!Ou=7yZ)u)YrXAFHxnjQ{`u literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0e49426 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +numpy>=1.24.2 +PyYAML>=6.0 +./pyVoIP-1.6.4.patched-py3-none-any.whl