Skip to content

Commit

Permalink
feat(twitter): tip bot fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
nikicat committed Feb 26, 2023
1 parent ba892a8 commit 30d1c41
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 16 deletions.
2 changes: 1 addition & 1 deletion donate4fun/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ async def payment_callback(
db_session=Depends(get_db_session),
):
"""
This callback is needed for lightning address support.
This callback is needed for lightning address support. It automatically creates a Donation.
"""
provider = SocialProvider.create(provider_id)
receiver: SocialAccount | Donator = await provider.wrap_db(db_session).query_account(id=receiver_id)
Expand Down
15 changes: 15 additions & 0 deletions donate4fun/api_donation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from datetime import datetime

from fastapi import Request, Depends, HTTPException, APIRouter
from fastapi.responses import RedirectResponse
from furl import furl
from sqlalchemy import select

Expand Down Expand Up @@ -96,6 +97,20 @@ async def get_donation(donation_id: UUID, db_session=Depends(get_donations_db)):
return DonateResponse(donation=donation, payment_request=payment_request)


@router.get("/donation/{donation_id}/invoice")
async def get_invoice(donation_id: UUID, db_session=Depends(get_donations_db)):
donation: Donation = await db_session.query_donation(id=donation_id)
if donation.paid_at is None:
invoice: Invoice = await lnd.lookup_invoice(donation.r_hash)
if invoice is None or invoice.state == 'CANCELED':
logger.debug(f"Invoice {invoice} cancelled, recreating")
invoice = await lnd.create_invoice(memo=make_memo(donation), value=donation.amount)
await db_session.update_donation(donation_id=donation_id, r_hash=invoice.r_hash)
return RedirectResponse(f'lightning:{invoice.payment_request}', status_code=307)
else:
return RedirectResponse(f'/donation/{donation.id}', status_code=303)


@router.post("/donation/{donation_id}/paid", response_model=Donation)
async def donation_paid(donation_id: UUID, request: DonationPaidRequest, db=Depends(get_donations_db)):
donation: Donation = await db.query_donation(id=donation_id)
Expand Down
27 changes: 19 additions & 8 deletions donate4fun/twitter_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import qrcode
from furl import furl
from qrcode.image.styledpil import StyledPilImage
from qrcode.constants import ERROR_CORRECT_H, ERROR_CORRECT_M
from lnurl.core import _url_encode as lnurl_encode
from starlette.datastructures import URL

Expand Down Expand Up @@ -329,7 +330,7 @@ async def handle_mention(self, mention: dict):
# Long expiry because this will be posted in Twitter
pay_req, donation = await donate(donation, db_session, expiry=3600)
if pay_req:
await self.send_payreq(tweet_id, receiver_account.handle, pay_req)
await self.send_payreq(tweet_id, receiver_account.handle, donation, pay_req)
elif donation.paid_at:
await self.share_donation_preview(tweet_id, donation)
else:
Expand All @@ -344,11 +345,11 @@ async def do_not_understand(self, tweet_id: int):
async def share_donation_preview(self, tweet_id: int, donation: Donation):
await self.send_tweet(reply_to=tweet_id, text=make_absolute_uri(f'/donation/{donation.id}'))

async def send_payreq(self, tweet_id: int, handle: TwitterHandle, pay_req: PaymentRequest):
qrcode: bytes = make_qr_code(pay_req)
async def send_payreq(self, tweet_id: int, handle: TwitterHandle, donation: Donation, pay_req: PaymentRequest):
qrcode: bytes = make_qr_code(pay_req, ERROR_CORRECT_M)
media_id: int = await self.client.upload_media(qrcode, 'image/png', category='tweet_image')
url: str = make_absolute_uri(f'/lnurlp/twitter/{handle}')
await self.send_tweet(text=f"Invoice: {url}", reply_to=tweet_id, media_id=media_id)
invoice_url = make_absolute_uri(f'/api/v1/donation/{donation.id}/invoice')
await self.send_tweet(text=f"Invoice: {invoice_url}", reply_to=tweet_id, media_id=media_id)

async def fetch_mentions(self, handle: TwitterHandle) -> AsyncIterator[dict]:
logger.info("fetching new mentions for @%s", handle)
Expand All @@ -370,7 +371,11 @@ async def fetch_mentions(self, handle: TwitterHandle) -> AsyncIterator[dict]:
'expansions': 'author_id,referenced_tweets.id,in_reply_to_user_id,referenced_tweets.id.author_id',
}
async with self.apponly_client.stream('GET', '/tweets/search/stream', params=params, timeout=3600) as response:
response.raise_for_status()
try:
response.raise_for_status()
except httpx.HTTPStatusError:
await response.aread()
raise
async for chunk in response.aiter_text():
chunk = chunk.strip()
if chunk:
Expand Down Expand Up @@ -416,8 +421,14 @@ async def create_withdrawal(db_session, twitter_account):
return lnurl_encode(str(withdraw_url))


def make_qr_code(data: str) -> bytes:
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H)
def make_qr_code(data: str, error_correction: int = ERROR_CORRECT_H) -> bytes:
"""
L - 7%
M - 15%
Q - 25%
H - 30%
"""
qr = qrcode.QRCode(error_correction=error_correction)
qr.add_data(data)
image: StyledPilImage = qr.make_image()
image_data = io.BytesIO()
Expand Down
7 changes: 0 additions & 7 deletions donate4fun/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from xml.etree import ElementTree as ET

import httpx
from bech32 import bech32_encode
from mako.lookup import TemplateLookup
from fastapi import Request, Response, FastAPI, Depends
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse
Expand Down Expand Up @@ -164,12 +163,6 @@ async def lnurlp_redirect(provider: str, username: str):
return RedirectResponse(url, status_code=302)


@app.get('/lightning/{provider}/{username}')
async def lightning_redirect(provider: str, username: str):
encoded_url = bech32_encode(make_absolute_uri(f'/.well-known/lnurlp/{provider}/{username}'))
return RedirectResponse(f'lightning:{encoded_url}', status_code=302)


@app.get("/.well-known/openid-configuration", response_class=JSONResponse)
async def openid_configuration():
return dict(
Expand Down

0 comments on commit 30d1c41

Please sign in to comment.