From 432ca98663da6043ca0afa7d25ea6a2a9692107e Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 27 Jan 2023 13:03:21 +0000 Subject: [PATCH] Pulled tpos --- lnbits/extensions/tpos/README.md | 15 - lnbits/extensions/tpos/__init__.py | 34 - lnbits/extensions/tpos/config.json | 6 - lnbits/extensions/tpos/crud.py | 49 -- lnbits/extensions/tpos/migrations.py | 36 - lnbits/extensions/tpos/models.py | 29 - lnbits/extensions/tpos/static/image/tpos.png | Bin 14172 -> 0 bytes lnbits/extensions/tpos/tasks.py | 64 -- .../tpos/templates/tpos/_api_docs.html | 79 -- .../extensions/tpos/templates/tpos/_tpos.html | 21 - .../extensions/tpos/templates/tpos/index.html | 471 ------------ .../extensions/tpos/templates/tpos/tpos.html | 686 ------------------ lnbits/extensions/tpos/views.py | 77 -- lnbits/extensions/tpos/views_api.py | 193 ----- tests/data/mock_data.zip | Bin 44893 -> 44350 bytes 15 files changed, 1760 deletions(-) delete mode 100644 lnbits/extensions/tpos/README.md delete mode 100644 lnbits/extensions/tpos/__init__.py delete mode 100644 lnbits/extensions/tpos/config.json delete mode 100644 lnbits/extensions/tpos/crud.py delete mode 100644 lnbits/extensions/tpos/migrations.py delete mode 100644 lnbits/extensions/tpos/models.py delete mode 100644 lnbits/extensions/tpos/static/image/tpos.png delete mode 100644 lnbits/extensions/tpos/tasks.py delete mode 100644 lnbits/extensions/tpos/templates/tpos/_api_docs.html delete mode 100644 lnbits/extensions/tpos/templates/tpos/_tpos.html delete mode 100644 lnbits/extensions/tpos/templates/tpos/index.html delete mode 100644 lnbits/extensions/tpos/templates/tpos/tpos.html delete mode 100644 lnbits/extensions/tpos/views.py delete mode 100644 lnbits/extensions/tpos/views_api.py diff --git a/lnbits/extensions/tpos/README.md b/lnbits/extensions/tpos/README.md deleted file mode 100644 index c7e3481d..00000000 --- a/lnbits/extensions/tpos/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# TPoS - -## A Shareable PoS (Point of Sale) that doesn't need to be installed and can run in the browser! - -An easy, fast and secure way to accept Bitcoin, over Lightning Network, at your business. The PoS is isolated from the wallet, so it's safe for any employee to use. You can create as many TPOS's as you need, for example one for each employee, or one for each branch of your business. - -### Usage - -1. Enable extension -2. Create a TPOS\ - ![create](https://imgur.com/8jNj8Zq.jpg) -3. Open TPOS on the browser\ - ![open](https://imgur.com/LZuoWzb.jpg) -4. Present invoice QR to customer\ - ![pay](https://imgur.com/tOwxn77.jpg) diff --git a/lnbits/extensions/tpos/__init__.py b/lnbits/extensions/tpos/__init__.py deleted file mode 100644 index c1b5a7dd..00000000 --- a/lnbits/extensions/tpos/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -import asyncio - -from fastapi import APIRouter -from fastapi.staticfiles import StaticFiles - -from lnbits.db import Database -from lnbits.helpers import template_renderer -from lnbits.tasks import catch_everything_and_restart - -db = Database("ext_tpos") - -tpos_ext: APIRouter = APIRouter(prefix="/tpos", tags=["TPoS"]) - -tpos_static_files = [ - { - "path": "/tpos/static", - "app": StaticFiles(directory="lnbits/extensions/tpos/static"), - "name": "tpos_static", - } -] - - -def tpos_renderer(): - return template_renderer(["lnbits/extensions/tpos/templates"]) - - -from .tasks import wait_for_paid_invoices -from .views import * # noqa -from .views_api import * # noqa - - -def tpos_start(): - loop = asyncio.get_event_loop() - loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) diff --git a/lnbits/extensions/tpos/config.json b/lnbits/extensions/tpos/config.json deleted file mode 100644 index 0c118e1a..00000000 --- a/lnbits/extensions/tpos/config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "TPoS", - "short_description": "A shareable PoS terminal!", - "tile": "/tpos/static/image/tpos.png", - "contributors": ["talvasconcelos", "arcbtc", "leesalminen"] -} diff --git a/lnbits/extensions/tpos/crud.py b/lnbits/extensions/tpos/crud.py deleted file mode 100644 index 94e2c006..00000000 --- a/lnbits/extensions/tpos/crud.py +++ /dev/null @@ -1,49 +0,0 @@ -from typing import List, Optional, Union - -from lnbits.helpers import urlsafe_short_hash - -from . import db -from .models import CreateTposData, TPoS - - -async def create_tpos(wallet_id: str, data: CreateTposData) -> TPoS: - tpos_id = urlsafe_short_hash() - await db.execute( - """ - INSERT INTO tpos.tposs (id, wallet, name, currency, tip_options, tip_wallet) - VALUES (?, ?, ?, ?, ?, ?) - """, - ( - tpos_id, - wallet_id, - data.name, - data.currency, - data.tip_options, - data.tip_wallet, - ), - ) - - tpos = await get_tpos(tpos_id) - assert tpos, "Newly created tpos couldn't be retrieved" - return tpos - - -async def get_tpos(tpos_id: str) -> Optional[TPoS]: - row = await db.fetchone("SELECT * FROM tpos.tposs WHERE id = ?", (tpos_id,)) - return TPoS(**row) if row else None - - -async def get_tposs(wallet_ids: Union[str, List[str]]) -> List[TPoS]: - if isinstance(wallet_ids, str): - wallet_ids = [wallet_ids] - - q = ",".join(["?"] * len(wallet_ids)) - rows = await db.fetchall( - f"SELECT * FROM tpos.tposs WHERE wallet IN ({q})", (*wallet_ids,) - ) - - return [TPoS(**row) for row in rows] - - -async def delete_tpos(tpos_id: str) -> None: - await db.execute("DELETE FROM tpos.tposs WHERE id = ?", (tpos_id,)) diff --git a/lnbits/extensions/tpos/migrations.py b/lnbits/extensions/tpos/migrations.py deleted file mode 100644 index 565c05ab..00000000 --- a/lnbits/extensions/tpos/migrations.py +++ /dev/null @@ -1,36 +0,0 @@ -async def m001_initial(db): - """ - Initial tposs table. - """ - await db.execute( - """ - CREATE TABLE tpos.tposs ( - id TEXT PRIMARY KEY, - wallet TEXT NOT NULL, - name TEXT NOT NULL, - currency TEXT NOT NULL - ); - """ - ) - - -async def m002_addtip_wallet(db): - """ - Add tips to tposs table - """ - await db.execute( - """ - ALTER TABLE tpos.tposs ADD tip_wallet TEXT NULL; - """ - ) - - -async def m003_addtip_options(db): - """ - Add tips to tposs table - """ - await db.execute( - """ - ALTER TABLE tpos.tposs ADD tip_options TEXT NULL; - """ - ) diff --git a/lnbits/extensions/tpos/models.py b/lnbits/extensions/tpos/models.py deleted file mode 100644 index f6522add..00000000 --- a/lnbits/extensions/tpos/models.py +++ /dev/null @@ -1,29 +0,0 @@ -from sqlite3 import Row -from typing import Optional - -from fastapi import Query -from pydantic import BaseModel - - -class CreateTposData(BaseModel): - name: str - currency: str - tip_options: str = Query(None) - tip_wallet: str = Query(None) - - -class TPoS(BaseModel): - id: str - wallet: str - name: str - currency: str - tip_options: Optional[str] - tip_wallet: Optional[str] - - @classmethod - def from_row(cls, row: Row) -> "TPoS": - return cls(**dict(row)) - - -class PayLnurlWData(BaseModel): - lnurl: str diff --git a/lnbits/extensions/tpos/static/image/tpos.png b/lnbits/extensions/tpos/static/image/tpos.png deleted file mode 100644 index c663032d954da04dad0c4b4573f1b135c0a00020..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14172 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 P7oe`JtyHaG6aN1JY^|z2 diff --git a/lnbits/extensions/tpos/tasks.py b/lnbits/extensions/tpos/tasks.py deleted file mode 100644 index 4b7bd9f9..00000000 --- a/lnbits/extensions/tpos/tasks.py +++ /dev/null @@ -1,64 +0,0 @@ -import asyncio - -from loguru import logger - -from lnbits.core.models import Payment -from lnbits.core.services import create_invoice, pay_invoice, websocketUpdater -from lnbits.helpers import get_current_extension_name -from lnbits.tasks import register_invoice_listener - -from .crud import get_tpos - - -async def wait_for_paid_invoices(): - invoice_queue = asyncio.Queue() - register_invoice_listener(invoice_queue, get_current_extension_name()) - - while True: - payment = await invoice_queue.get() - await on_invoice_paid(payment) - - -async def on_invoice_paid(payment: Payment) -> None: - if payment.extra.get("tag") != "tpos": - return - - tipAmount = payment.extra.get("tipAmount") - - strippedPayment = { - "amount": payment.amount, - "fee": payment.fee, - "checking_id": payment.checking_id, - "payment_hash": payment.payment_hash, - "bolt11": payment.bolt11, - } - - tpos_id = payment.extra.get("tposId") - assert tpos_id - - tpos = await get_tpos(tpos_id) - assert tpos - - await websocketUpdater(tpos_id, str(strippedPayment)) - - if not tipAmount: - # no tip amount - return - - wallet_id = tpos.tip_wallet - assert wallet_id - - payment_hash, payment_request = await create_invoice( - wallet_id=wallet_id, - amount=int(tipAmount), - internal=True, - memo=f"tpos tip", - ) - logger.debug(f"tpos: tip invoice created: {payment_hash}") - - checking_id = await pay_invoice( - payment_request=payment_request, - wallet_id=payment.wallet_id, - extra={**payment.extra, "tipSplitted": True}, - ) - logger.debug(f"tpos: tip invoice paid: {checking_id}") diff --git a/lnbits/extensions/tpos/templates/tpos/_api_docs.html b/lnbits/extensions/tpos/templates/tpos/_api_docs.html deleted file mode 100644 index cbb21be1..00000000 --- a/lnbits/extensions/tpos/templates/tpos/_api_docs.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - GET /tpos/api/v1/tposs -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
-
- Returns 200 OK (application/json) -
- [<tpos_object>, ...] -
Curl example
- curl -X GET {{ request.base_url }}tpos/api/v1/tposs -H "X-Api-Key: - <invoice_key>" - -
-
-
- - - - POST /tpos/api/v1/tposs -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
- {"name": <string>, "currency": <string*ie USD*>} -
- Returns 201 CREATED (application/json) -
- {"currency": <string>, "id": <string>, "name": - <string>, "wallet": <string>} -
Curl example
- curl -X POST {{ request.base_url }}tpos/api/v1/tposs -d '{"name": - <string>, "currency": <string>}' -H "Content-type: - application/json" -H "X-Api-Key: <admin_key>" - -
-
-
- - - - - DELETE - /tpos/api/v1/tposs/<tpos_id> -
Headers
- {"X-Api-Key": <admin_key>}
-
Returns 204 NO CONTENT
- -
Curl example
- curl -X DELETE {{ request.base_url - }}tpos/api/v1/tposs/<tpos_id> -H "X-Api-Key: <admin_key>" - -
-
-
-
diff --git a/lnbits/extensions/tpos/templates/tpos/_tpos.html b/lnbits/extensions/tpos/templates/tpos/_tpos.html deleted file mode 100644 index 97f4d52c..00000000 --- a/lnbits/extensions/tpos/templates/tpos/_tpos.html +++ /dev/null @@ -1,21 +0,0 @@ - - - -

- Thiago's Point of Sale is a secure, mobile-ready, instant and shareable - point of sale terminal (PoS) for merchants. The PoS is linked to your - LNbits wallet but completely air-gapped so users can ONLY create - invoices. To share the TPoS hit the hash on the terminal. -

- Created by -
Tiago Vasconcelos. - - - diff --git a/lnbits/extensions/tpos/templates/tpos/index.html b/lnbits/extensions/tpos/templates/tpos/index.html deleted file mode 100644 index 1aa75fcf..00000000 --- a/lnbits/extensions/tpos/templates/tpos/index.html +++ /dev/null @@ -1,471 +0,0 @@ -{% extends "base.html" %} {% from "macros.jinja" import window_vars with context -%} {% block page %} -
-
- - - New TPoS - - - - - -
-
-
TPoS
-
-
- Export to CSV -
-
- - {% raw %} - - - - {% endraw %} - -
-
-
- -
- - -
{{SITE_TITLE}} TPoS extension
-
- - - - {% include "tpos/_api_docs.html" %} - - {% include "tpos/_tpos.html" %} - - -
-
- - - - - - - - - Hit enter to add values - - -
- Create TPoS - Cancel -
-
-
-
-
-{% endblock %} {% block scripts %} {{ window_vars(user) }} - -{% endblock %} diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html deleted file mode 100644 index 438fc22e..00000000 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ /dev/null @@ -1,686 +0,0 @@ -{% extends "public.html" %} {% block toolbar_title %} {{ tpos.name }} - -{% endblock %} {% block footer %}{% endblock %} {% block page_container %} - - - -
-
-

{% raw %}{{ amountFormatted }}{% endraw %}

-
- {% raw %}{{ fsat }}{% endraw %} sat -
-
-
-
- -
-
-
- 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - DEL - 0 - C - OK -
-
-
-
- - - - - - - - -
-

- {% raw %}{{ amountWithTipFormatted }}{% endraw %} -

-
- {% raw %}{{ fsat }} - sat - ( + {{ tipAmountFormatted }} tip) - {% endraw %} -
- -
-
- Copy invoice - Close -
-
-
- - - -
- Would you like to leave a tip? -
-
- {% raw %}{{ tip }}{% endraw %}% - -
- - - Ok -
-
-
- No, thanks - Close -
-
-
- - - - - - -
-

- {{ tpos.name }}
{{ request.url }} -

-
-
- Copy URL - Close -
-
-
- - - - - - - - - - - - - - - No paid invoices - - - - {%raw%} - - {{payment.amount / 1000}} sats - Hash: {{payment.checking_id.slice(0, 30)}}... - - - {{payment.dateFrom}} - - - {%endraw%} - - - - -
-
-{% endblock %} {% block styles %} - -{% endblock %} {% block scripts %} - - -{% endblock %} diff --git a/lnbits/extensions/tpos/views.py b/lnbits/extensions/tpos/views.py deleted file mode 100644 index fee5914f..00000000 --- a/lnbits/extensions/tpos/views.py +++ /dev/null @@ -1,77 +0,0 @@ -from http import HTTPStatus - -from fastapi import Depends, Request -from fastapi.templating import Jinja2Templates -from starlette.exceptions import HTTPException -from starlette.responses import HTMLResponse - -from lnbits.core.models import User -from lnbits.decorators import check_user_exists -from lnbits.settings import settings - -from . import tpos_ext, tpos_renderer -from .crud import get_tpos - -templates = Jinja2Templates(directory="templates") - - -@tpos_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): - return tpos_renderer().TemplateResponse( - "tpos/index.html", {"request": request, "user": user.dict()} - ) - - -@tpos_ext.get("/{tpos_id}") -async def tpos(request: Request, tpos_id): - tpos = await get_tpos(tpos_id) - if not tpos: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist." - ) - - return tpos_renderer().TemplateResponse( - "tpos/tpos.html", - { - "request": request, - "tpos": tpos, - "web_manifest": f"/tpos/manifest/{tpos_id}.webmanifest", - }, - ) - - -@tpos_ext.get("/manifest/{tpos_id}.webmanifest") -async def manifest(tpos_id: str): - tpos = await get_tpos(tpos_id) - if not tpos: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist." - ) - - return { - "short_name": settings.lnbits_site_title, - "name": tpos.name + " - " + settings.lnbits_site_title, - "icons": [ - { - "src": settings.lnbits_custom_logo - if settings.lnbits_custom_logo - else "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png", - "type": "image/png", - "sizes": "900x900", - } - ], - "start_url": "/tpos/" + tpos_id, - "background_color": "#1F2234", - "description": "Bitcoin Lightning tPOS", - "display": "standalone", - "scope": "/tpos/" + tpos_id, - "theme_color": "#1F2234", - "shortcuts": [ - { - "name": tpos.name + " - " + settings.lnbits_site_title, - "short_name": tpos.name, - "description": tpos.name + " - " + settings.lnbits_site_title, - "url": "/tpos/" + tpos_id, - } - ], - } diff --git a/lnbits/extensions/tpos/views_api.py b/lnbits/extensions/tpos/views_api.py deleted file mode 100644 index 1be1428d..00000000 --- a/lnbits/extensions/tpos/views_api.py +++ /dev/null @@ -1,193 +0,0 @@ -from http import HTTPStatus - -import httpx -from fastapi import Depends, Query -from lnurl import decode as decode_lnurl -from loguru import logger -from starlette.exceptions import HTTPException - -from lnbits.core.crud import get_latest_payments_by_extension, get_user -from lnbits.core.models import Payment -from lnbits.core.services import create_invoice -from lnbits.core.views.api import api_payment -from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key -from lnbits.settings import settings - -from . import tpos_ext -from .crud import create_tpos, delete_tpos, get_tpos, get_tposs -from .models import CreateTposData, PayLnurlWData - - -@tpos_ext.get("/api/v1/tposs", status_code=HTTPStatus.OK) -async def api_tposs( - all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type) -): - wallet_ids = [wallet.wallet.id] - if all_wallets: - user = await get_user(wallet.wallet.user) - wallet_ids = user.wallet_ids if user else [] - - return [tpos.dict() for tpos in await get_tposs(wallet_ids)] - - -@tpos_ext.post("/api/v1/tposs", status_code=HTTPStatus.CREATED) -async def api_tpos_create( - data: CreateTposData, wallet: WalletTypeInfo = Depends(get_key_type) -): - tpos = await create_tpos(wallet_id=wallet.wallet.id, data=data) - return tpos.dict() - - -@tpos_ext.delete("/api/v1/tposs/{tpos_id}") -async def api_tpos_delete( - tpos_id: str, wallet: WalletTypeInfo = Depends(require_admin_key) -): - tpos = await get_tpos(tpos_id) - - if not tpos: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist." - ) - - if tpos.wallet != wallet.wallet.id: - raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your TPoS.") - - await delete_tpos(tpos_id) - return "", HTTPStatus.NO_CONTENT - - -@tpos_ext.post("/api/v1/tposs/{tpos_id}/invoices", status_code=HTTPStatus.CREATED) -async def api_tpos_create_invoice( - tpos_id: str, amount: int = Query(..., ge=1), memo: str = "", tipAmount: int = 0 -) -> dict: - - tpos = await get_tpos(tpos_id) - - if not tpos: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist." - ) - - if tipAmount > 0: - amount += tipAmount - - try: - payment_hash, payment_request = await create_invoice( - wallet_id=tpos.wallet, - amount=amount, - memo=f"{memo} to {tpos.name}" if memo else f"{tpos.name}", - extra={ - "tag": "tpos", - "tipAmount": tipAmount, - "tposId": tpos_id, - "amount": amount - tipAmount if tipAmount else False, - }, - ) - except Exception as e: - raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e)) - - return {"payment_hash": payment_hash, "payment_request": payment_request} - - -@tpos_ext.get("/api/v1/tposs/{tpos_id}/invoices") -async def api_tpos_get_latest_invoices(tpos_id: str): - try: - payments = [ - Payment.from_row(row) - for row in await get_latest_payments_by_extension( - ext_name="tpos", ext_id=tpos_id - ) - ] - - except Exception as e: - raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e)) - - return [ - { - "checking_id": payment.checking_id, - "amount": payment.amount, - "time": payment.time, - "pending": payment.pending, - } - for payment in payments - ] - - -@tpos_ext.post( - "/api/v1/tposs/{tpos_id}/invoices/{payment_request}/pay", status_code=HTTPStatus.OK -) -async def api_tpos_pay_invoice( - lnurl_data: PayLnurlWData, payment_request: str, tpos_id: str -): - tpos = await get_tpos(tpos_id) - - if not tpos: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist." - ) - - lnurl = ( - lnurl_data.lnurl.replace("lnurlw://", "") - .replace("lightning://", "") - .replace("LIGHTNING://", "") - .replace("lightning:", "") - .replace("LIGHTNING:", "") - ) - - if lnurl.lower().startswith("lnurl"): - lnurl = decode_lnurl(lnurl) - else: - lnurl = "https://" + lnurl - - async with httpx.AsyncClient() as client: - try: - headers = {"user-agent": f"lnbits/tpos commit {settings.lnbits_commit[:7]}"} - r = await client.get(lnurl, follow_redirects=True, headers=headers) - if r.is_error: - lnurl_response = {"success": False, "detail": "Error loading"} - else: - resp = r.json() - if resp["tag"] != "withdrawRequest": - lnurl_response = {"success": False, "detail": "Wrong tag type"} - else: - r2 = await client.get( - resp["callback"], - follow_redirects=True, - headers=headers, - params={ - "k1": resp["k1"], - "pr": payment_request, - }, - ) - resp2 = r2.json() - if r2.is_error: - lnurl_response = { - "success": False, - "detail": "Error loading callback", - } - elif resp2["status"] == "ERROR": - lnurl_response = {"success": False, "detail": resp2["reason"]} - else: - lnurl_response = {"success": True, "detail": resp2} - except (httpx.ConnectError, httpx.RequestError): - lnurl_response = {"success": False, "detail": "Unexpected error occurred"} - - return lnurl_response - - -@tpos_ext.get( - "/api/v1/tposs/{tpos_id}/invoices/{payment_hash}", status_code=HTTPStatus.OK -) -async def api_tpos_check_invoice(tpos_id: str, payment_hash: str): - tpos = await get_tpos(tpos_id) - if not tpos: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist." - ) - try: - status = await api_payment(payment_hash) - - except Exception as exc: - logger.error(exc) - return {"paid": False} - return status diff --git a/tests/data/mock_data.zip b/tests/data/mock_data.zip index 90c1393b24ba8b0816054e799d23e58b1c46ba16..154c4528a6d15cc2789e74ed5fab32666f1dc3c5 100644 GIT binary patch delta 84 zcmcb6k7?g6rVX;4o9}fsC{5n8R&H|5T8_!zx{N0uSj00~ah)7fZ}()!b%`tmGZ`2r lC(cuxTrgW0EcXm3S3YMl+j>tvO$JSdAN&jq26GvJ004GtA)WvL delta 605 zcmdmYi|Ot?rVX;4`s>&J2&w;Z??Evm0|SEr1A_pA0z+y=Nqk8`ez9J0VNPaAs&Qxt zF9Unj!n;XP3nw33CR+dY#(l3t1tRPp?uTq*+4>^M#eDy17R}qs&UP!DZk!tR>WGe$ z+4hSL=B1O@?0z9xwI=RR_q>ItpZ!X9N+}U!xzGPR=gpsUb;-M}SBG{jTOE8@u0(YD zr<9onJU16l5fJ)hDG)tGg*?7g`|(m*B9^u-N?XD&u~EhPOM?# zwA7iub;AR#cirABd)%IdZ&i@rv=wu1>w8_%zu3$1_sEqMI&)8c*-{m8Ds=Xjh%B{q zsdZEPwm;4e{(0nd)kcfkiJGy0cYpm>U~pmWx3f9t-MuI6N?ULGdB#bxx~uPYF3YvN zT%;rzkx(AlHe=ed{yBBWXTOO)bz$q@`q$I`x2?_nZ!+_#xO%+$D^=log*dRw7#I%3 z8_g`(wq;*b)XGm&Cx#l#a{u|+ZU2P_b8i>jy%J=-N+E?O?r})q0~fz5mz%OW=1e%Z z#Awf?P!adfi{eh7f4yf%&xDhI{=4jf8rM*tt2-n1*RS|P+yUOqEFwT*4u;LLoefI1 z*dh+9i(yHla3=$SXjGg$ceNbzl`e+K2Avj@4Oa3@p586Oi!i~jmtpd%ZZn{az~sy| tQY?nE85kxnn4>t^KV29sczTT#lf}Hr&)0bJX)$Oqgb6S(e4Gc;4*;B{2txn>