Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
arcbtc authored Feb 11, 2023
0 parents commit bcde392
Show file tree
Hide file tree
Showing 16 changed files with 1,714 additions and 0 deletions.
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Events

## Sell tickets for events and use the built-in scanner for registering attendants

Events alows you to make tickets for an event. Each ticket is in the form of a uniqque QR code. After registering, and paying for ticket, the user gets a QR code to present at registration/entrance.

Events includes a shareable ticket scanner, which can be used to register attendees.

## Usage

1. Create an event\
![create event](https://i.imgur.com/dadK1dp.jpg)
2. Fill out the event information:

- event name
- wallet (normally there's only one)
- event information
- closing date for event registration
- begin and end date of the event

![event info](https://imgur.com/KAv68Yr.jpg)

3. Share the event registration link\
![event ticket](https://imgur.com/AQWUOBY.jpg)

- ticket example\
![ticket example](https://i.imgur.com/trAVSLd.jpg)

- QR code ticket, presented after invoice paid, to present at registration\
![event ticket](https://i.imgur.com/M0ROM82.jpg)

4. Use the built-in ticket scanner to validate registered, and paid, attendees\
![ticket scanner](https://i.imgur.com/zrm9202.jpg)
35 changes: 35 additions & 0 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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_events")


events_ext: APIRouter = APIRouter(prefix="/events", tags=["Events"])

events_static_files = [
{
"path": "/events/static",
"app": StaticFiles(packages=[("lnbits", "extensions/events/static")]),
"name": "events_static",
}
]


def events_renderer():
return template_renderer(["lnbits/extensions/events/templates"])


from .tasks import wait_for_paid_invoices
from .views import * # noqa: F401,F403
from .views_api import * # noqa: F401,F403


def events_start():
loop = asyncio.get_event_loop()
loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))
6 changes: 6 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "Events",
"short_description": "Sell and register event tickets",
"tile": "/events/static/image/events.png",
"contributors": ["benarc"]
}
144 changes: 144 additions & 0 deletions crud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
from typing import List, Optional, Union

from lnbits.helpers import urlsafe_short_hash

from . import db
from .models import CreateEvent, Events, Tickets

# TICKETS


async def create_ticket(
payment_hash: str, wallet: str, event: str, name: str, email: str
) -> Tickets:
await db.execute(
"""
INSERT INTO events.ticket (id, wallet, event, name, email, registered, paid)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(payment_hash, wallet, event, name, email, False, True),
)

# UPDATE EVENT DATA ON SOLD TICKET
eventdata = await get_event(event)
assert eventdata, "Couldn't get event from ticket being paid"
sold = eventdata.sold + 1
amount_tickets = eventdata.amount_tickets - 1
await db.execute(
"""
UPDATE events.events
SET sold = ?, amount_tickets = ?
WHERE id = ?
""",
(sold, amount_tickets, event),
)

ticket = await get_ticket(payment_hash)
assert ticket, "Newly created ticket couldn't be retrieved"
return ticket


async def get_ticket(payment_hash: str) -> Optional[Tickets]:
row = await db.fetchone("SELECT * FROM events.ticket WHERE id = ?", (payment_hash,))
return Tickets(**row) if row else None


async def get_tickets(wallet_ids: Union[str, List[str]]) -> List[Tickets]:
if isinstance(wallet_ids, str):
wallet_ids = [wallet_ids]

q = ",".join(["?"] * len(wallet_ids))
rows = await db.fetchall(
f"SELECT * FROM events.ticket WHERE wallet IN ({q})", (*wallet_ids,)
)
return [Tickets(**row) for row in rows]


async def delete_ticket(payment_hash: str) -> None:
await db.execute("DELETE FROM events.ticket WHERE id = ?", (payment_hash,))


async def delete_event_tickets(event_id: str) -> None:
await db.execute("DELETE FROM events.ticket WHERE event = ?", (event_id,))


# EVENTS


async def create_event(data: CreateEvent) -> Events:
event_id = urlsafe_short_hash()
await db.execute(
"""
INSERT INTO events.events (id, wallet, name, info, closing_date, event_start_date, event_end_date, amount_tickets, price_per_ticket, sold)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
event_id,
data.wallet,
data.name,
data.info,
data.closing_date,
data.event_start_date,
data.event_end_date,
data.amount_tickets,
data.price_per_ticket,
0,
),
)

event = await get_event(event_id)
assert event, "Newly created event couldn't be retrieved"
return event


async def update_event(event_id: str, **kwargs) -> Events:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
await db.execute(
f"UPDATE events.events SET {q} WHERE id = ?", (*kwargs.values(), event_id)
)
event = await get_event(event_id)
assert event, "Newly updated event couldn't be retrieved"
return event


async def get_event(event_id: str) -> Optional[Events]:
row = await db.fetchone("SELECT * FROM events.events WHERE id = ?", (event_id,))
return Events(**row) if row else None


async def get_events(wallet_ids: Union[str, List[str]]) -> List[Events]:
if isinstance(wallet_ids, str):
wallet_ids = [wallet_ids]

q = ",".join(["?"] * len(wallet_ids))
rows = await db.fetchall(
f"SELECT * FROM events.events WHERE wallet IN ({q})", (*wallet_ids,)
)

return [Events(**row) for row in rows]


async def delete_event(event_id: str) -> None:
await db.execute("DELETE FROM events.events WHERE id = ?", (event_id,))


# EVENTTICKETS


async def get_event_tickets(event_id: str, wallet_id: str) -> List[Tickets]:
rows = await db.fetchall(
"SELECT * FROM events.ticket WHERE wallet = ? AND event = ?",
(wallet_id, event_id),
)
return [Tickets(**row) for row in rows]


async def reg_ticket(ticket_id: str) -> List[Tickets]:
await db.execute(
"UPDATE events.ticket SET registered = ? WHERE id = ?", (True, ticket_id)
)
ticket = await db.fetchone("SELECT * FROM events.ticket WHERE id = ?", (ticket_id,))
rows = await db.fetchall(
"SELECT * FROM events.ticket WHERE event = ?", (ticket[1],)
)
return [Tickets(**row) for row in rows]
83 changes: 83 additions & 0 deletions migrations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
async def m001_initial(db):

await db.execute(
"""
CREATE TABLE events.events (
id TEXT PRIMARY KEY,
wallet TEXT NOT NULL,
name TEXT NOT NULL,
info TEXT NOT NULL,
closing_date TEXT NOT NULL,
event_start_date TEXT NOT NULL,
event_end_date TEXT NOT NULL,
amount_tickets INTEGER NOT NULL,
price_per_ticket INTEGER NOT NULL,
sold INTEGER NOT NULL,
time TIMESTAMP NOT NULL DEFAULT """
+ db.timestamp_now
+ """
);
"""
)

await db.execute(
"""
CREATE TABLE events.tickets (
id TEXT PRIMARY KEY,
wallet TEXT NOT NULL,
event TEXT NOT NULL,
name TEXT NOT NULL,
email TEXT NOT NULL,
registered BOOLEAN NOT NULL,
time TIMESTAMP NOT NULL DEFAULT """
+ db.timestamp_now
+ """
);
"""
)


async def m002_changed(db):

await db.execute(
"""
CREATE TABLE events.ticket (
id TEXT PRIMARY KEY,
wallet TEXT NOT NULL,
event TEXT NOT NULL,
name TEXT NOT NULL,
email TEXT NOT NULL,
registered BOOLEAN NOT NULL,
paid BOOLEAN NOT NULL,
time TIMESTAMP NOT NULL DEFAULT """
+ db.timestamp_now
+ """
);
"""
)

for row in [list(row) for row in await db.fetchall("SELECT * FROM events.tickets")]:
usescsv = ""

for i in range(row[5]):
if row[7]:
usescsv += "," + str(i + 1)
else:
usescsv += "," + str(1)
usescsv = usescsv[1:]
await db.execute(
"""
INSERT INTO events.ticket (
id,
wallet,
event,
name,
email,
registered,
paid
)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(row[0], row[1], row[2], row[3], row[4], row[5], True),
)
await db.execute("DROP TABLE events.tickets")
43 changes: 43 additions & 0 deletions models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from fastapi.param_functions import Query
from pydantic import BaseModel


class CreateEvent(BaseModel):
wallet: str
name: str
info: str
closing_date: str
event_start_date: str
event_end_date: str
amount_tickets: int = Query(..., ge=0)
price_per_ticket: int = Query(..., ge=0)


class CreateTicket(BaseModel):
name: str
email: str


class Events(BaseModel):
id: str
wallet: str
name: str
info: str
closing_date: str
event_start_date: str
event_end_date: str
amount_tickets: int
price_per_ticket: int
sold: int
time: int


class Tickets(BaseModel):
id: str
wallet: str
event: str
name: str
email: str
registered: bool
paid: bool
time: int
Binary file added static/image/events.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import asyncio

from lnbits.core.models import Payment
from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener

from .models import CreateTicket
from .views_api import api_ticket_send_ticket


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:
# (avoid loops)
if (
payment.extra
and "events" == payment.extra.get("tag")
and payment.extra.get("name")
and payment.extra.get("email")
):
await api_ticket_send_ticket(
payment.memo,
payment.payment_hash,
CreateTicket(
name=str(payment.extra.get("name")),
email=str(payment.extra.get("email")),
),
)
return
Loading

0 comments on commit bcde392

Please sign in to comment.