|
2 | 2 | import math |
3 | 3 | from datetime import datetime, timezone |
4 | 4 |
|
5 | | -from lnbits.core.crud.wallets import get_wallets |
6 | | -from lnbits.core.models import Payment |
7 | 5 | from lnbits.core.crud.users import get_user |
8 | 6 | from lnbits.core.crud.wallets import get_wallets |
| 7 | +from lnbits.core.models import Payment |
9 | 8 | from lnbits.core.services import create_invoice, pay_invoice, websocket_manager |
10 | 9 | from lnbits.core.services.notifications import send_notification |
11 | 10 | from lnbits.helpers import urlsafe_short_hash |
@@ -83,9 +82,7 @@ async def _broadcast_claim(chat_id: str, claimed_by_id: str | None, claimed_by_n |
83 | 82 | await _broadcast_chat(chat_id, payload) |
84 | 83 |
|
85 | 84 |
|
86 | | -async def _maybe_pay_claim_split( |
87 | | - category: Categories, chat: ChatSession, amount: int |
88 | | -) -> None: |
| 85 | +async def _maybe_pay_claim_split(category: Categories, chat: ChatSession, amount: int) -> None: |
89 | 86 | if not chat.claimed_by_id: |
90 | 87 | return |
91 | 88 | split = float(category.claim_split or 0) |
@@ -239,6 +236,103 @@ async def _calculate_amount(category: Categories, message: str) -> int: |
239 | 236 | return math.ceil(raw_amount) |
240 | 237 |
|
241 | 238 |
|
| 239 | +async def _handle_lnurlp_drawdown( |
| 240 | + category: Categories, |
| 241 | + chat: ChatSession, |
| 242 | + amount: int, |
| 243 | + data: CreateChatMessage, |
| 244 | + sender_name: str, |
| 245 | + base_url: str | None, |
| 246 | +) -> ChatPaymentRequest: |
| 247 | + if chat.balance < amount: |
| 248 | + raise ValueError("Insufficient balance. Fund the chat to continue.") |
| 249 | + chat.balance = max(0, chat.balance - amount) |
| 250 | + await _maybe_pay_claim_split(category, chat, amount) |
| 251 | + message = ChatMessage( |
| 252 | + id=urlsafe_short_hash(), |
| 253 | + sender_id=data.sender_id, |
| 254 | + sender_name=sender_name, |
| 255 | + sender_role=data.sender_role, |
| 256 | + message=data.message, |
| 257 | + created_at=datetime.now(timezone.utc), |
| 258 | + amount=amount, |
| 259 | + message_type="message", |
| 260 | + ) |
| 261 | + if not chat.messages: |
| 262 | + await _notify_new_chat(category, chat, base_url, data.message) |
| 263 | + await _append_message(chat, message, unread=True) |
| 264 | + await _broadcast_balance(chat.id, chat.balance) |
| 265 | + return ChatPaymentRequest(chat_id=chat.id, pending=False, message_id=message.id) |
| 266 | + |
| 267 | + |
| 268 | +async def _create_payg_payment_request( |
| 269 | + category: Categories, |
| 270 | + chat: ChatSession, |
| 271 | + amount: int, |
| 272 | + data: CreateChatMessage, |
| 273 | + sender_name: str, |
| 274 | +) -> ChatPaymentRequest: |
| 275 | + wallet_id = await _resolve_category_wallet(category) |
| 276 | + if not wallet_id: |
| 277 | + raise ValueError("Category wallet not configured.") |
| 278 | + payment = await create_invoice( |
| 279 | + wallet_id=wallet_id, |
| 280 | + amount=amount, |
| 281 | + memo=f"Chat message for {category.name}", |
| 282 | + extra={ |
| 283 | + "tag": "chat", |
| 284 | + "chat_id": chat.id, |
| 285 | + "categories_id": chat.categories_id, |
| 286 | + "sender_id": data.sender_id, |
| 287 | + "sender_name": sender_name, |
| 288 | + "sender_role": data.sender_role, |
| 289 | + "message": data.message, |
| 290 | + "payment_type": "message", |
| 291 | + }, |
| 292 | + ) |
| 293 | + await create_chat_payment( |
| 294 | + ChatPayment( |
| 295 | + payment_hash=payment.payment_hash, |
| 296 | + chat_id=chat.id, |
| 297 | + categories_id=chat.categories_id, |
| 298 | + sender_id=data.sender_id, |
| 299 | + sender_name=sender_name, |
| 300 | + sender_role=data.sender_role, |
| 301 | + message=data.message, |
| 302 | + amount=amount, |
| 303 | + payment_type="message", |
| 304 | + ) |
| 305 | + ) |
| 306 | + return ChatPaymentRequest( |
| 307 | + chat_id=chat.id, |
| 308 | + payment_hash=payment.payment_hash, |
| 309 | + payment_request=payment.bolt11, |
| 310 | + amount=amount, |
| 311 | + pending=True, |
| 312 | + ) |
| 313 | + |
| 314 | + |
| 315 | +async def _send_free_message( |
| 316 | + category: Categories, |
| 317 | + chat: ChatSession, |
| 318 | + data: CreateChatMessage, |
| 319 | + sender_name: str, |
| 320 | + base_url: str | None, |
| 321 | +) -> ChatPaymentRequest: |
| 322 | + message = ChatMessage( |
| 323 | + id=urlsafe_short_hash(), |
| 324 | + sender_id=data.sender_id, |
| 325 | + sender_name=sender_name, |
| 326 | + sender_role=data.sender_role, |
| 327 | + message=data.message, |
| 328 | + created_at=datetime.now(timezone.utc), |
| 329 | + ) |
| 330 | + if not chat.messages: |
| 331 | + await _notify_new_chat(category, chat, base_url, data.message) |
| 332 | + await _append_message(chat, message, unread=True) |
| 333 | + return ChatPaymentRequest(chat_id=chat.id, pending=False, message_id=message.id) |
| 334 | + |
| 335 | + |
242 | 336 | async def send_public_message( |
243 | 337 | categories_id: str, |
244 | 338 | chat_id: str, |
@@ -267,78 +361,12 @@ async def send_public_message( |
267 | 361 | amount = await _calculate_amount(category, data.message) |
268 | 362 |
|
269 | 363 | if category.paid and category.lnurlp and amount > 0 and not user_id: |
270 | | - if chat.balance < amount: |
271 | | - raise ValueError("Insufficient balance. Fund the chat to continue.") |
272 | | - chat.balance = max(0, chat.balance - amount) |
273 | | - await _maybe_pay_claim_split(category, chat, amount) |
274 | | - message = ChatMessage( |
275 | | - id=urlsafe_short_hash(), |
276 | | - sender_id=data.sender_id, |
277 | | - sender_name=sender_name, |
278 | | - sender_role=data.sender_role, |
279 | | - message=data.message, |
280 | | - created_at=datetime.now(timezone.utc), |
281 | | - amount=amount, |
282 | | - message_type="message", |
283 | | - ) |
284 | | - if not chat.messages: |
285 | | - await _notify_new_chat(category, chat, base_url, data.message) |
286 | | - await _append_message(chat, message, unread=True) |
287 | | - await _broadcast_balance(chat.id, chat.balance) |
288 | | - return ChatPaymentRequest(chat_id=chat.id, pending=False, message_id=message.id) |
| 364 | + return await _handle_lnurlp_drawdown(category, chat, amount, data, sender_name, base_url) |
289 | 365 |
|
290 | 366 | if category.paid and amount > 0 and not user_id: |
291 | | - wallet_id = await _resolve_category_wallet(category) |
292 | | - if not wallet_id: |
293 | | - raise ValueError("Category wallet not configured.") |
294 | | - payment = await create_invoice( |
295 | | - wallet_id=wallet_id, |
296 | | - amount=amount, |
297 | | - memo=f"Chat message for {category.name}", |
298 | | - extra={ |
299 | | - "tag": "chat", |
300 | | - "chat_id": chat.id, |
301 | | - "categories_id": categories_id, |
302 | | - "sender_id": data.sender_id, |
303 | | - "sender_name": sender_name, |
304 | | - "sender_role": data.sender_role, |
305 | | - "message": data.message, |
306 | | - "payment_type": "message", |
307 | | - }, |
308 | | - ) |
309 | | - await create_chat_payment( |
310 | | - ChatPayment( |
311 | | - payment_hash=payment.payment_hash, |
312 | | - chat_id=chat.id, |
313 | | - categories_id=categories_id, |
314 | | - sender_id=data.sender_id, |
315 | | - sender_name=sender_name, |
316 | | - sender_role=data.sender_role, |
317 | | - message=data.message, |
318 | | - amount=amount, |
319 | | - payment_type="message", |
320 | | - ) |
321 | | - ) |
322 | | - return ChatPaymentRequest( |
323 | | - chat_id=chat.id, |
324 | | - payment_hash=payment.payment_hash, |
325 | | - payment_request=payment.bolt11, |
326 | | - amount=amount, |
327 | | - pending=True, |
328 | | - ) |
| 367 | + return await _create_payg_payment_request(category, chat, amount, data, sender_name) |
329 | 368 |
|
330 | | - message = ChatMessage( |
331 | | - id=urlsafe_short_hash(), |
332 | | - sender_id=data.sender_id, |
333 | | - sender_name=sender_name, |
334 | | - sender_role=data.sender_role, |
335 | | - message=data.message, |
336 | | - created_at=datetime.now(timezone.utc), |
337 | | - ) |
338 | | - if not chat.messages: |
339 | | - await _notify_new_chat(category, chat, base_url, data.message) |
340 | | - await _append_message(chat, message, unread=True) |
341 | | - return ChatPaymentRequest(chat_id=chat.id, pending=False, message_id=message.id) |
| 369 | + return await _send_free_message(category, chat, data, sender_name, base_url) |
342 | 370 |
|
343 | 371 |
|
344 | 372 | async def send_admin_message( |
@@ -439,30 +467,22 @@ async def request_tip( |
439 | 467 | ) |
440 | 468 |
|
441 | 469 |
|
442 | | -async def payment_received_for_client_data(payment: Payment) -> bool: |
443 | | - if payment.extra.get("tag") != "chat": |
| 470 | +async def _apply_balance_payment(chat_id: str | None, amount_sat: int) -> bool: |
| 471 | + if not chat_id: |
| 472 | + logger.warning("Chat balance payment missing chat_id.") |
444 | 473 | return False |
445 | | - |
446 | | - if payment.extra.get("payment_type") == "balance": |
447 | | - chat_id = payment.extra.get("chat_id") |
448 | | - if not chat_id: |
449 | | - logger.warning("Chat balance payment missing chat_id.") |
450 | | - return False |
451 | | - chat = await get_chat(chat_id) |
452 | | - if not chat: |
453 | | - logger.warning("Chat not found for balance payment.") |
454 | | - return False |
455 | | - chat.balance = max(0, chat.balance + payment.sat) |
456 | | - chat.updated_at = datetime.now(timezone.utc) |
457 | | - await update_chat(chat) |
458 | | - await _broadcast_balance(chat.id, chat.balance) |
459 | | - return True |
460 | | - |
461 | | - chat_payment = await get_chat_payment(payment.payment_hash) |
462 | | - if not chat_payment: |
463 | | - logger.warning("Chat payment not found.") |
| 474 | + chat = await get_chat(chat_id) |
| 475 | + if not chat: |
| 476 | + logger.warning("Chat not found for balance payment.") |
464 | 477 | return False |
| 478 | + chat.balance = max(0, chat.balance + amount_sat) |
| 479 | + chat.updated_at = datetime.now(timezone.utc) |
| 480 | + await update_chat(chat) |
| 481 | + await _broadcast_balance(chat.id, chat.balance) |
| 482 | + return True |
| 483 | + |
465 | 484 |
|
| 485 | +async def _finalize_chat_payment(chat_payment: ChatPayment) -> bool: |
466 | 486 | if chat_payment.paid: |
467 | 487 | return True |
468 | 488 |
|
@@ -496,14 +516,29 @@ async def payment_received_for_client_data(payment: Payment) -> bool: |
496 | 516 | amount=chat_payment.amount, |
497 | 517 | message_type=message_type, |
498 | 518 | ) |
499 | | - if not chat.messages or len(chat.messages) == 0: |
| 519 | + if not chat.messages: |
500 | 520 | category = await get_categories_by_id(chat.categories_id) |
501 | 521 | if category: |
502 | 522 | await _notify_new_chat(category, chat, None, chat_payment.message) |
503 | 523 | await _append_message(chat, message, unread=True) |
504 | 524 | return True |
505 | 525 |
|
506 | 526 |
|
| 527 | +async def payment_received_for_client_data(payment: Payment) -> bool: |
| 528 | + if payment.extra.get("tag") != "chat": |
| 529 | + return False |
| 530 | + |
| 531 | + if payment.extra.get("payment_type") == "balance": |
| 532 | + return await _apply_balance_payment(payment.extra.get("chat_id"), payment.sat) |
| 533 | + |
| 534 | + chat_payment = await get_chat_payment(payment.payment_hash) |
| 535 | + if not chat_payment: |
| 536 | + logger.warning("Chat payment not found.") |
| 537 | + return False |
| 538 | + |
| 539 | + return await _finalize_chat_payment(chat_payment) |
| 540 | + |
| 541 | + |
507 | 542 | async def toggle_chat_claim(chat_id: str, user_id: str) -> ChatSession: |
508 | 543 | chat = await get_chat(chat_id) |
509 | 544 | if not chat: |
|
0 commit comments