Included currency conversion

This commit is contained in:
ben
2023-01-06 15:28:32 +00:00
parent d0094427eb
commit 85b5e96ea7
8 changed files with 245 additions and 122 deletions

View File

@@ -1,4 +1,5 @@
import asyncio import asyncio
from fastapi import APIRouter from fastapi import APIRouter
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
@@ -22,10 +23,12 @@ example_static_files = [
def example_renderer(): def example_renderer():
return template_renderer(["lnbits/extensions/example/templates"]) return template_renderer(["lnbits/extensions/example/templates"])
from .tasks import wait_for_paid_invoices from .tasks import wait_for_paid_invoices
from .views import * from .views import *
from .views_api import * from .views_api import *
def tpos_start(): def tpos_start():
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@@ -3,11 +3,14 @@
# add your dependencies here # add your dependencies here
import asyncio import asyncio
from loguru import logger from loguru import logger
from lnbits.core.models import Payment from lnbits.core.models import Payment
from lnbits.helpers import get_current_extension_name from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener from lnbits.tasks import register_invoice_listener
async def wait_for_paid_invoices(): async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue() invoice_queue = asyncio.Queue()
register_invoice_listener(invoice_queue, get_current_extension_name()) register_invoice_listener(invoice_queue, get_current_extension_name())
@@ -16,8 +19,11 @@ async def wait_for_paid_invoices():
payment = await invoice_queue.get() payment = await invoice_queue.get()
await on_invoice_paid(payment) await on_invoice_paid(payment)
async def on_invoice_paid(payment: Payment) -> None: async def on_invoice_paid(payment: Payment) -> None:
if payment.extra.get("tag") != "example": # Will grab any payment with the tag "example" if (
payment.extra.get("tag") != "example"
): # Will grab any payment with the tag "example"
logger.debug(payment) logger.debug(payment)
# Do something # Do something
return return

View File

@@ -4,12 +4,44 @@
<q-dialog v-model="thingDialog.show" position="top"> <q-dialog v-model="thingDialog.show" position="top">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card"> <q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<q-form @submit="sendThingDialog" class="q-gutter-md"> <q-form @submit="sendThingDialog" class="q-gutter-md">
<q-input filled dense v-model.trim="thingDialog.data.name" type="text" label="Name *"></q-input> <q-input
<q-input filled dense v-model.trim="thingDialog.data.email" type="email" label="Name *"></q-input> filled
<q-select filled dense emit-value v-model="thingDialog.data.wallet" :options="g.user.walletOptions" label="Wallet *"></q-select> dense
v-model.trim="thingDialog.data.name"
type="text"
label="Name *"
></q-input>
<q-input
filled
dense
v-model.trim="thingDialog.data.email"
type="email"
label="Name *"
></q-input>
<q-select
filled
dense
emit-value
v-model="thingDialog.data.wallet"
:options="g.user.walletOptions"
label="Wallet *"
></q-select>
<div class="row q-mt-lg"> <div class="row q-mt-lg">
<q-btn v-if="thingDialog.data.id" unelevated color="primary" type="submit">Update thing</q-btn> <q-btn
<q-btn v-else unelevated color="primary" :disable="thingDialog.data.name == null" type="submit">Create thing</q-btn> v-if="thingDialog.data.id"
unelevated
color="primary"
type="submit"
>Update thing</q-btn
>
<q-btn
v-else
unelevated
color="primary"
:disable="thingDialog.data.name == null"
type="submit"
>Create thing</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn> <q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
</div> </div>
</q-form> </q-form>
@@ -19,7 +51,8 @@
<q-card flat> <q-card flat>
<q-card-section> <q-card-section>
<div class="text-h5 q-mb-md"> <div class="text-h5 q-mb-md">
{{SITE_TITLE}} Extension Development Guide <small>(Collection of resources for extension developers)</small> {{SITE_TITLE}} Extension Development Guide
<small>(Collection of resources for extension developers)</small>
</div> </div>
<q-card unelevated flat> <q-card unelevated flat>
@@ -46,58 +79,129 @@
<div class="text-h6">Frameworks</div> <div class="text-h6">Frameworks</div>
<div> <div>
<q-splitter <q-splitter v-model="splitterModel">
v-model="splitterModel"
>
<template v-slot:before> <template v-slot:before>
<q-tabs <q-tabs v-model="framworktab" vertical>
v-model="framworktab" <q-tab name="fastapi"
vertical ><img src="./static/fastapi-framework.png" />FASTAPI</q-tab
>
<q-tab name="quasar"
><img src="./static/quasar-framework.png" />QUASAR</q-tab
>
<q-tab name="vuejs"
><img src="./static/vuejs-framework.png" />VUE-JS</q-tab
> >
<q-tab name="fastapi"><img src="./static/fastapi-framework.png">FASTAPI</q-tab>
<q-tab name="quasar"><img src="./static/quasar-framework.png">QUASAR</q-tab>
<q-tab name="vuejs"><img src="./static/vuejs-framework.png">VUE-JS</q-tab>
</q-tabs> </q-tabs>
</template> </template>
<template v-slot:after> <template v-slot:after>
<q-tab-panels <q-tab-panels v-model="framworktab">
v-model="framworktab"
>
<q-tab-panel name="fastapi" class="text-body1"> <q-tab-panel name="fastapi" class="text-body1">
<img src="./static/fastapilogo.png"> <a href="https://fastapi.tiangolo.com/"
<p>LNbits API is built using FastAPI, a high-performance, easy to code API framework.<br/><br/> ><img src="./static/fastapilogo.png"
FastAPI auto-generates swagger UI docs for testing endpoints <a class="text-primary" href="../docs">/docs</a></p> /></a>
<code class="bg-grey-3 text-black">views.py</code> is used for setting application routes: <p>
<img src="./static/fastapi-example.png"> LNbits API is built using
<code class="bg-grey-3 text-black">views_api.py</code> is used for setting application API endpoints: <a
<img src="./static/fastapi-example2.png"> href="https://fastapi.tiangolo.com/"
class="text-primary"
>FastAPI</a
>, a high-performance, easy to code API framework.<br /><br />
FastAPI auto-generates swagger UI docs for testing
endpoints <a class="text-primary" href="../docs">/docs</a>
</p>
<code class="bg-grey-3 text-black">views.py</code> is used
for setting application routes:
<img src="./static/fastapi-example.png" />
<code class="bg-grey-3 text-black">views_api.py</code> is
used for setting application API endpoints:
<img src="./static/fastapi-example2.png" />
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="quasar" class="text-body1"> <q-tab-panel name="quasar" class="text-body1">
<img src="./static/quasarlogo.png"> <a href="https://quasar.dev/"
<p>LNbits uses Quasar for frontend deisgn elements. Quasar Framework is an open-source Vue.js based framework for building apps.</p> ><img src="./static/quasarlogo.png"
/></a>
<p>
LNbits uses
<a class="text-primary" href="https://quasar.dev/"
>Quasar Framework</a
>
for frontend deisgn elements. Quasar Framework is an
open-source Vue.js based framework for building apps.
</p>
<p class="text-weight-bold">TIP: Look through <code class="bg-grey-3 text-black">/template</code> files in other extensions for examples.</p> <p class="text-weight-bold">
TIP: Look through
<code class="bg-grey-3 text-black">/template</code> files
in other extensions for examples.
</p>
<p>In the below example we make a dialogue popup box (box can be triggered <q-btn size="sm" color="primary" @click="thingDialog.show = true">here</q-btn>): <p>
<q-tooltip>Exmple of a tooltip!</q-tooltip></p> In the below example we make a dialogue popup box (box can
<img src="./static/quasar-example.png"><br/><br/> be triggered
<q-btn
size="sm"
color="primary"
@click="thingDialog.show = true"
>here</q-btn
>): <q-tooltip>Exmple of a tooltip!</q-tooltip>
</p>
<img src="./static/quasar-example.png" /><br /><br />
<div class="text-h6">Useful links:</div> <div class="text-h6">Useful links:</div>
<q-btn color="primary" type="a" href="https://quasar.dev/style/">Style (typography, spacing, etc)</q-btn> <q-btn
<q-btn color="primary" type="a" href="https://quasar.dev/vue-components/">Genral components (cards, buttons, popup dialogs, etc)</q-btn> color="primary"
<q-btn color="primary" type="a" href="https://quasar.dev/layout/grid">Layouts (rows/columns, flexbox)</q-btn> type="a"
href="https://quasar.dev/style/"
>Style (typography, spacing, etc)</q-btn
>
<q-btn
color="primary"
type="a"
href="https://quasar.dev/vue-components/"
>Genral components (cards, buttons, popup dialogs,
etc)</q-btn
>
<q-btn
color="primary"
type="a"
href="https://quasar.dev/layout/grid"
>Layouts (rows/columns, flexbox)</q-btn
>
</q-tab-panel> </q-tab-panel>
<q-tab-panel v-if="someBool == true" name="vuejs" class="text-body1"> <q-tab-panel
<img src="./static/vuejslogo.png"> v-if="someBool == true"
<p>Quasar is built on Vue-JS, so LNbits uses Vue-JS for rendering page elements, organising JS functions and declaring modles and elements.</p> name="vuejs"
<p><code class="bg-grey-3 text-black">v-if</code> can be used to render elements:</p> class="text-body1"
<img src="./static/vif-example.png" style="max-width:800px"> >
<p>Typical example of a frontend page JS:</p> <a href="https://vuejs.org/">
<img src="./static/script-example.png" style="max-width:800px"> <img src="./static/vuejslogo.png"
/></a>
<p>
LNbits uses
<a href="https://vuejs.org/" class="text-primary">Vue</a>
components for best-in-class high-performance and
responsive performance.
</p>
<p>
Typical example of Vue components in a frontend script:
</p>
<img
src="./static/script-example.png"
style="max-width: 800px"
/><br /><br />
<p>
In a page body, models can be called. <br />Content can be
conditionally rendered using Vue's
<code class="bg-grey-3 text-black">v-if</code>:
</p>
<img
src="./static/vif-example.png"
style="max-width: 800px"
/>
</q-tab-panel> </q-tab-panel>
</q-tab-panels> </q-tab-panels>
</template> </template>
@@ -105,7 +209,56 @@
</div> </div>
</q-tab-panel> </q-tab-panel>
<!--FILE STRUCTURE--> <q-tab-panel name="tools">
<div class="text-h6">Useful Tools</div>
<div>
<q-splitter v-model="splitterModel">
<template v-slot:before>
<q-tabs v-model="usefultab" vertical>
<q-tab name="magicalg">MAGICAL G</q-tab>
<q-tab name="exchange">EXCHANGE RATES</q-tab>
</q-tabs>
</template>
<template v-slot:after>
<q-tab-panels v-model="usefultab">
<q-tab-panel name="magicalg" class="text-body1">
<div class="text-h5 q-mb-md">Magical G</div>
<p>
A magical "g" (ie
<code class="bg-grey-3 text-black"
>this.g.user.wallets[0].inkey</code
>) is always available, with info about the user, wallets
and extensions:
</p>
<code class="text-caption"
>{% raw %}{{ g }}{% endraw %}</code
>
</q-tab-panel>
<q-tab-panel name="exchange">
<div class="text-h6">Exchange rates</div>
<p>
LNbits includes a handy
<a
href="../docs#/default/api_fiat_as_sats_api_v1_conversion_post"
class="text-primary"
>exchange rate function</a
>, that streams rates from 6 different sources.
</p>
Exchange rate API:<br />
<img src="./static/conversion-example.png" /><br /><br />
Exchange rate functions, included using
<code class="bg-grey-3 text-black"
>from lnbits.utils.exchange_rates import
fiat_amount_as_satoshis</code
>:<br />
<img src="./static/conversion-example2.png" />
</q-tab-panel>
</q-tab-panels>
</template>
</q-splitter>
</div>
</q-tab-panel>
<q-tab-panel name="structure"> <q-tab-panel name="structure">
<div class="text-h6">File Structure</div> <div class="text-h6">File Structure</div>
@@ -121,46 +274,6 @@
<div class="text-h6">Submission</div> <div class="text-h6">Submission</div>
Coming soon... Coming soon...
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="tools">
<div class="text-h6">Useful Tools</div>
<div>
<q-splitter
v-model="splitterModel"
>
<template v-slot:before>
<q-tabs
v-model="usefultab"
vertical
>
<q-tab name="magicalg">MAGICAL G</q-tab>
<q-tab name="conversion">CONVERSION</q-tab>
</q-tabs>
</template>
<template v-slot:after>
<q-tab-panels
v-model="usefultab"
>
<q-tab-panel name="magicalg" class="text-body1">
<div class="text-h5 q-mb-md">Magical G</div>
<p>
A magical "g" (ie <code class="bg-grey-3 text-black">this.g.user.wallets[0].inkey</code>) is always available, with info about the user, wallets and
extensions:
</p>
<code class="text-caption">{% raw %}{{ g }}{% endraw %}</code>
</q-tab-panel>
<q-tab-panel name="conversion">
<div class="text-h6">Conversion </div>
Coming soon...
</q-tab-panel>
</q-tab-panels>
</template>
</q-splitter>
</div>
</q-tab-panel>
</q-tab-panels> </q-tab-panels>
</q-card> </q-card>
</q-card-section> </q-card-section>
@@ -190,9 +303,9 @@ var someMapObject = obj => {
someBool: true, someBool: true,
splitterModel: 20, splitterModel: 20,
exampleData: [], exampleData: [],
tab: "frameworks", tab: 'frameworks',
framworktab: "fastapi", framworktab: 'fastapi',
usefultab: "magicalg" usefultab: 'magicalg'
} }
}, },
///// Where functions live ///// ///// Where functions live /////
@@ -219,7 +332,7 @@ var someMapObject = obj => {
///// To run on startup ///// ///// To run on startup /////
created: function () { created: function () {
self = this // Often used to run a real object, rather than the event (all a bit confusing really) self = this // Often used to run a real object, rather than the event (all a bit confusing really)
self.exampleFunction("lorum") self.exampleFunction('lorum')
} }
}) })
</script> </script>

View File

@@ -6,6 +6,7 @@ from . import example_ext
# add your endpoints here # add your endpoints here
@example_ext.get("/api/v1/test/{test_data}") @example_ext.get("/api/v1/test/{test_data}")
async def api_example(test_data): async def api_example(test_data):
# Do some python things and return the data # Do some python things and return the data