diff --git a/lnbits/extensions/twitchalerts/crud.py b/lnbits/extensions/twitchalerts/crud.py index a3d1578c..a5a9aacf 100644 --- a/lnbits/extensions/twitchalerts/crud.py +++ b/lnbits/extensions/twitchalerts/crud.py @@ -31,6 +31,7 @@ async def get_charge_details(service_id): async def create_donation( id: str, + wallet: str, cur_code: str, sats: int, amount: float, @@ -43,6 +44,7 @@ async def create_donation( """ INSERT INTO Donations ( id, + wallet, name, message, cur_code, @@ -51,10 +53,11 @@ async def create_donation( service, posted ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( id, + wallet, name, message, cur_code, @@ -223,14 +226,10 @@ async def get_donation(donation_id: str) -> Optional[Donation]: async def get_donations(wallet_id: str) -> Optional[list]: - services = await get_services(wallet_id) - service_ids = [service.id for service in services] - rows = [] - for service_id in service_ids: - rows.append(await db.fetchall( - "SELECT * FROM Donations WHERE service = ?", - (service_id,) - )) + rows = await db.fetchall( + "SELECT * FROM Donations WHERE wallet = ?", + (wallet_id,) + ) return [Donation.from_row(row) for row in rows] if rows else None @@ -240,3 +239,23 @@ async def delete_donation(donation_id: str) -> None: (donation_id,) ) await delete_charge(donation_id) + + +async def update_donation(donation_id: str, **kwargs) -> Donation: + q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) + await db.execute(f"UPDATE form SET {q} WHERE id = ?", (*kwargs.values(), + donation_id)) + row = await db.fetchone("SELECT * FROM Donations WHERE id = ?", + (donation_id,)) + assert row, "Newly updated donation couldn't be retrieved" + return Donation(**row) + + +async def update_service(service_id: str, **kwargs) -> Donation: + q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) + await db.execute(f"UPDATE form SET {q} WHERE id = ?", (*kwargs.values(), + service_id)) + row = await db.fetchone("SELECT * FROM Services WHERE id = ?", + (service_id,)) + assert row, "Newly updated service couldn't be retrieved" + return Service(**row) diff --git a/lnbits/extensions/twitchalerts/migrations.py b/lnbits/extensions/twitchalerts/migrations.py index 6677d5d2..64d75a8d 100644 --- a/lnbits/extensions/twitchalerts/migrations.py +++ b/lnbits/extensions/twitchalerts/migrations.py @@ -21,6 +21,7 @@ async def m001_initial(db): """ CREATE TABLE IF NOT EXISTS Donations ( id TEXT PRIMARY KEY, + wallet TEXT NOT NULL, name TEXT NOT NULL, message TEXT NOT NULL, cur_code TEXT NOT NULL, diff --git a/lnbits/extensions/twitchalerts/models.py b/lnbits/extensions/twitchalerts/models.py index 32f795ff..c1451672 100644 --- a/lnbits/extensions/twitchalerts/models.py +++ b/lnbits/extensions/twitchalerts/models.py @@ -4,6 +4,7 @@ from typing import NamedTuple, Optional class Donation(NamedTuple): id: str + wallet: str name: str message: str cur_code: str diff --git a/lnbits/extensions/twitchalerts/templates/twitchalerts/_api_docs.html b/lnbits/extensions/twitchalerts/templates/twitchalerts/_api_docs.html index 9dd12cc0..d062589f 100644 --- a/lnbits/extensions/twitchalerts/templates/twitchalerts/_api_docs.html +++ b/lnbits/extensions/twitchalerts/templates/twitchalerts/_api_docs.html @@ -1,22 +1,17 @@ - - - -
- Twitch Alerts: Integrate Bitcoin into your stream alerts! -
-

- Accept Bitcoin donations on Twitch, and integrate them into your alerts. - Present your viewers with a simple donation page, and add those donations - to Streamlabs to play alerts on your stream!
- - Created by, Fitti -

-
-
-
+ + +

+ Twitch Alerts: Integrate Bitcoin into your stream alerts! +

+

+ Accept Bitcoin donations on Twitch, and integrate them into your alerts. + Present your viewers with a simple donation page, and add those donations + to Streamlabs to play alerts on your stream!
+ For detailed setup instructions, check out + this guide!
+ + Created by, Fitti +

+
+
diff --git a/lnbits/extensions/twitchalerts/templates/twitchalerts/index.html b/lnbits/extensions/twitchalerts/templates/twitchalerts/index.html index 2abb6c65..b39679dd 100644 --- a/lnbits/extensions/twitchalerts/templates/twitchalerts/index.html +++ b/lnbits/extensions/twitchalerts/templates/twitchalerts/index.html @@ -177,31 +177,30 @@ - + > @@ -218,7 +217,7 @@ v-else unelevated color="deep-purple" - :disable="formDialog.data.clientid == null || formDialog.data.clientsecret == 0 || formDialog.data.username == null" + :disable="formDialog.data.client_id == null || formDialog.data.client_secret == 0 || formDialog.data.twitchuser == null" type="submit" >Create Service @@ -247,30 +246,37 @@ mixins: [windowMixin], data: function() { return { + servicenames: ["Streamlabs"], services: [], donations: [], servicesTable: { columns: [ {name: 'id', align: 'left', label: 'ID', field: 'id'}, - {name: 'username', align: 'left', label: 'Twitch Username', field: 'username'}, + {name: 'twitchuser', align: 'left', label: 'Twitch Username', field: 'twitchuser'}, {name: 'wallet', align: 'left', label: 'Wallet', field: 'wallet'}, { - name: 'service', + name: 'servicename', align: 'left', label: 'Service', - field: 'service' + field: 'servicename' }, { - name: 'clientid', + name: 'client_id', align: 'left', label: 'Client ID', - field: 'clientid' + field: 'client_id' }, { - name: 'clientsecret', + name: 'client_secret', align: 'left', label: 'Client Secret', - field: 'clientsecret' + field: 'client_secret' + }, + { + name: 'authenticated', + align: 'left', + label: 'Authenticated', + field: 'authenticated' } ], pagination: { @@ -310,17 +316,17 @@ }) }) }, - deleteDonation: function(ticketId) { + deleteDonation: function(donationId) { var self = this - var donations = _.findWhere(this.donations, {id: ticketId}) + var donations = _.findWhere(this.donations, {id: donationId}) LNbits.utils - .confirmDialog('Are you sure you want to delete this ticket') + .confirmDialog('Are you sure you want to delete this donation?') .onOk(function() { LNbits.api .request( 'DELETE', - '/twitchalerts/api/v1/donations/' + ticketId, + '/twitchalerts/api/v1/donations/' + donationId, _.findWhere(self.g.user.wallets, {id: donations.wallet}).inkey ) .then(function(response) { @@ -343,7 +349,7 @@ LNbits.api .request( 'GET', - '/twitchalerts/api/v1/services?all_wallets', + '/twitchalerts/api/v1/services', this.g.user.wallets[0].inkey ) .then(function(response) { diff --git a/lnbits/extensions/twitchalerts/views_api.py b/lnbits/extensions/twitchalerts/views_api.py index aeebd469..90afa7a5 100644 --- a/lnbits/extensions/twitchalerts/views_api.py +++ b/lnbits/extensions/twitchalerts/views_api.py @@ -9,15 +9,20 @@ from .crud import ( get_charge_details, create_donation, post_donation, + get_donation, get_donations, + delete_donation, create_service, get_service, - authenticate_service + get_services, + authenticate_service, + update_donation, + update_service ) from ..satspay.crud import create_charge, get_charge -@twitchalerts_ext.route("/api/v1/createservice", methods=["POST"]) +@twitchalerts_ext.route("/api/v1/services", methods=["POST"]) @api_check_wallet_key("invoice") @api_validate_post_request( schema={ @@ -88,7 +93,7 @@ async def api_authenticate_service(service_id): ) -@twitchalerts_ext.route("/api/v1/createdonation", methods=["POST"]) +@twitchalerts_ext.route("/api/v1/donations", methods=["POST"]) @api_check_wallet_key("invoice") @api_validate_post_request( schema={ @@ -114,6 +119,7 @@ async def api_create_donation(): **charge_details) await create_donation( id=charge.id, + wallet=service.wallet, name=name, cur_code=g.data["cur_code"], sats=g.data["sats"], @@ -145,13 +151,104 @@ async def api_post_donation(): ) +@twitchalerts_ext.route("/api/v1/services", methods=["GET"]) +@api_check_wallet_key("invoice") +async def api_get_services(): + wallet_ids = (await get_user(g.wallet.user)).wallet_ids + services = [] + for wallet_id in wallet_ids: + services += await get_services(wallet_id) + return ( + jsonify([ + service._asdict() for service in services + ] if services else []), + HTTPStatus.OK, + ) + + @twitchalerts_ext.route("/api/v1/donations", methods=["GET"]) @api_check_wallet_key("invoice") async def api_get_donations(): wallet_ids = (await get_user(g.wallet.user)).wallet_ids + donations = [] + for wallet_id in wallet_ids: + donations += await get_donations(wallet_id) return ( jsonify([ - donation._asdict() for donation in await get_donations(wallet_ids) - ]), + donation._asdict() for donation in donations + ] if donations else []), HTTPStatus.OK, ) + + +@twitchalerts_ext.route("/api/v1/donations/", methods=["PUT"]) +@api_check_wallet_key("invoice") +async def api_update_donation(donation_id=None): + if donation_id: + donation = await get_donation(donation_id) + + if not donation: + return ( + jsonify({"message": "Donation does not exist."}), + HTTPStatus.NOT_FOUND + ) + + if donation.wallet != g.wallet.id: + return ( + jsonify({"message": "Not your donation."}), + HTTPStatus.FORBIDDEN + ) + + donation = await update_donation(donation_id, **g.data) + else: + return ( + jsonify({"message": "No donation ID specified"}), + HTTPStatus.BAD_REQUEST + ) + return jsonify(donation._asdict()), HTTPStatus.CREATED + + +@twitchalerts_ext.route("/api/v1/services/", methods=["PUT"]) +@api_check_wallet_key("invoice") +async def api_update_service(service_id=None): + if service_id: + service = await get_service(service_id) + + if not service: + return ( + jsonify({"message": "Service does not exist."}), + HTTPStatus.NOT_FOUND + ) + + if service.wallet != g.wallet.id: + return ( + jsonify({"message": "Not your service."}), + HTTPStatus.FORBIDDEN + ) + + service = await update_service(service_id, **g.data) + else: + return ( + jsonify({"message": "No service ID specified"}), + HTTPStatus.BAD_REQUEST + ) + return jsonify(service._asdict()), HTTPStatus.CREATED + + +@twitchalerts_ext.route("/api/v1/donations/", methods=["DELETE"]) +@api_check_wallet_key("invoice") +async def api_delete_donation(donation_id): + donation = await get_donation(donation_id) + if not donation: + return ( + jsonify({"message": "No donation with this ID!"}), + HTTPStatus.NOT_FOUND + ) + if donation.wallet != g.wallet.id: + return ( + jsonify({"message": "Not authorized to delete this donation!"}), + HTTPStatus.FORBIDDEN + ) + await delete_donation(donation_id) + + return "", HTTPStatus.NO_CONTENT