license_service

This commit is contained in:
user 2024-04-05 21:05:45 +03:00
parent 752f42ba02
commit 839655b163
4 changed files with 170 additions and 11 deletions

View File

@ -8,6 +8,7 @@ from app.core._keyboards import get_inline_keyboard
from app.core.models.node_storage import StoredContent
import json
from app.core.logger import make_log
from app.core.models.content.user_content import UserAction
from app.client_bot.routers.home import router as home_router
from app.client_bot.routers.tonconnect import router as tonconnect_router
@ -15,10 +16,63 @@ from app.core._config import CLIENT_TELEGRAM_BOT_USERNAME
from app.core.logger import logger
from app.core.content.content_id import ContentId
import base58
from datetime import datetime, timedelta
import asyncio
from app.core._blockchain.ton.connect import TonConnect, wallet_obj_by_name
router = Router()
async def t_callback_purchase_node_content(query: types.CallbackQuery, memory=None, user=None, db_session=None, chat_wrap=None, **extra):
content_oid = int(query.data.split('_')[1])
make_log("OwnedContent", f"{user} Try to purchase content: {content_oid}", level='info')
content = db_session.query(StoredContent).filter_by(id=content_oid).first()
if not content:
return await query.answer(user.translated('error_contentNotFound'), show_alert=True)
license_price = content.meta.get('license', {}).get('listen', {}).get('price')
license_price_num = int(license_price)
if license_price_num < 1:
return await query.answer(user.translated('error_contentPrice'), show_alert=True)
ton_connect, ton_connection = TonConnect.by_user(db_session, user, callback_fn=())
await ton_connect.restore_connection()
assert ton_connect.connected, "No connected wallet"
user_wallet_address = user.wallet_address(db_session)
memory._app.add_task(ton_connect._sdk_client.send_transaction({
'valid_until': int(datetime.now().timestamp() + 300),
'messages': [
{
'address': content.meta['item_address'],
'amount': license_price
}
]
}))
new_action = UserAction(
type='purchase',
user_id=user.id,
content_id=content.id,
telegram_message_id=query.message.message_id,
from_address=user_wallet_address,
to_address=content.meta['item_address'],
status='requested',
meta={
'confirmation_url': wallet_obj_by_name(ton_connection.wallet_key.split('==')[0])['universal_url']
},
created_at=datetime.now()
)
db_session.add(new_action)
db_session.commit()
await user.send_content(db_session, content, message_id=query.message.message_id)
router.callback_query.register(t_callback_purchase_node_content, F.data.startswith('PC_'))
async def t_inline_query_node_content(query: types.InlineQuery, memory=None, user=None, db_session=None, chat_wrap=None, **extra):
make_log("OwnedContent", f"Inline query: {query.query}", level='info')
try:

View File

@ -1,8 +1,9 @@
import asyncio
from base64 import b64decode
from datetime import datetime
from datetime import datetime, timedelta
from base58 import b58encode
from sqlalchemy import and_
from tonsdk.boc import Cell
from tonsdk.utils import Address
@ -11,11 +12,14 @@ from app.core._blockchain.ton.toncenter import toncenter
from app.core._utils.send_status import send_status
from app.core.logger import make_log
from app.core.models.node_storage import StoredContent
from app.core.models.content.user_content import UserContent, UserAction
from app.core._utils.resolve_content import resolve_content
from app.core.models.wallet_connection import WalletConnection
from app.core._keyboards import get_inline_keyboard
from app.core.models._telegram import Wrapped_CBotChat
from app.core.storage import db_session
from app.core._config import CLIENT_TELEGRAM_API_KEY
from app.core.models.user import User
import os
import traceback
@ -23,7 +27,54 @@ import traceback
async def license_index_loop(memory, platform_found: bool, seqno: int) -> [bool, int]:
make_log("LicenseIndex", "Service running", level="debug")
with db_session() as session:
pass
for user in session.query(User).filter(
User.last_use > datetime.now() - timedelta(minutes=10)
).all():
try:
await user.scan_owned_user_content(session)
except BaseException as e:
make_log("LicenseIndex", f"Error: {e}" + '\n' + traceback.format_exc(), level="error")
for content in session.query(UserContent).filter(
and_(
UserContent.startswith('nft/'),
UserContent.updated < (datetime.now() - timedelta(minutes=15)),
)
):
try:
await content.sync_with_chain(session)
except BaseException as e:
make_log("LicenseIndex", f"Error: {e}" + '\n' + traceback.format_exc(), level="error")
for action in session.query(UserAction).filter(
and_(
UserAction.type == 'purchase',
# UserAction.updated < (datetime.now() - timedelta(minutes=5)),
UserAction.status == 'requested',
)
):
try:
user = session.query(User).filter_by(id=action.user_id).first()
chat_wrap = Wrapped_CBotChat(CLIENT_TELEGRAM_API_KEY, chat_id=user.telegram_id, db_session=session, user=user)
content = session.query(StoredContent).filter_by(id=action.content_id).first()
if (datetime.now() - action.updated) > timedelta(minutes=5):
if action.telegram_message_id:
await chat_wrap.delete_message(action.telegram_message_id)
make_log("LicenseIndex", f"Action timeout: {action.id}", level="info")
action.status = 'canceled'
else:
user_wallet_address = user.wallet_address(session)
user_content = session.query(UserContent).filter_by(content_id=action.content_id, status='active', owner_address=user_wallet_address).first()
if user_content:
make_log("LicenseIndex", f"User already has content: {user_content.content_id}", level="info")
action.status = 'success'
session.commit()
await chat_wrap.send_content(session, content)
except BaseException as e:
make_log("LicenseIndex", f"Error: {e}" + '\n' + traceback.format_exc(), level="error")
return platform_found, seqno

View File

@ -1,5 +1,5 @@
from app.core.models.node_storage import StoredContent
from app.core.models.content.user_content import UserContent
from app.core.models.content.user_content import UserContent, UserAction
from app.core.logger import make_log
from app.core._utils.tg_process_template import tg_process_template
from app.core._config import PROJECT_HOST, CLIENT_TELEGRAM_BOT_USERNAME
@ -96,11 +96,24 @@ class PlayerTemplates:
or bool(self.db_session.query(UserContent).filter_by(owner_address=user_wallet_address, status='active', content_id=content.id).first())
)
if not have_access:
template_kwargs['audio'] = URLInputFile(local_content_url + '?seconds_limit=30')
purchase_action = self.db_session.query(UserAction).filter_by(
type='purchase',
from_address=user_wallet_address,
content_id=content.id,
status='requested'
).first()
if purchase_action:
inline_keyboard_array.append([{
'text': self.user.translated('gotoWallet_button'),
'url': purchase_action.meta['confirmation_url']
}])
text = self.user.translated('p_playerContext_purchaseRequested')
else:
inline_keyboard_array.append([{
'text': self.user.translated('buyTrackListenLicense_button').format(price=str(round(0.15, 3))),
'callback_data': f'PC_{content.id}'
}])
template_kwargs['audio'] = URLInputFile(local_content_url + '?seconds_limit=30')
text = self.user.translated('p_playerContext_preview')
make_log("TG-Player", f"Send content {content_type} ({content_encoding}) to chat {self._chat_id}. {cd_log}")

View File

@ -1,3 +1,10 @@
import traceback
import base58
from sqlalchemy import and_
from app.core.logger import make_log
from app.core.models import StoredContent
from app.core.models.wallet_connection import WalletConnection
from app.core._blockchain.ton.toncenter import toncenter
from tonsdk.boc import Cell
@ -35,10 +42,44 @@ class NodeStorageIndexationMixin:
pass # async def fetch_onchain_metadata(self):
class UserContentIndexationMixin:
pass
async def sync_with_chain(self, db_session):
errored = False
cc_indexator_result = await toncenter.run_get_method(self.onchain_address, 'indexator_data')
if cc_indexator_result.get('exit_code', -1) != 0:
errored = True
if not errored:
try:
cc_indexator_data = unpack_item_indexator_data(cc_indexator_result)
assert cc_indexator_data['type'] == 1, "Type is not a content"
assert cc_indexator_data['address'] == self.onchain_address, "Address is not equal"
values_slice = cc_indexator_data['values'].begin_parse()
content_hash_b58 = base58.b58encode(bytes.fromhex(values_slice.read_uint(256).hex()[2:])).decode()
stored_content = db_session.query(StoredContent).filter(
and_(
StoredContent.type == 'onchain/content',
StoredContent.hash == content_hash_b58,
)
).first()
trusted_cop_address_result = await toncenter.run_get_method(stored_content.meta['item_address'], 'get_nft_address_by_index', [['num', cc_indexator_data['index']]])
assert trusted_cop_address_result.get('exit_code', -1) == 0, "Trusted cop address error"
trusted_cop_address = Cell.one_from_boc(b64decode(trusted_cop_address_result['stack'][0][1]['bytes'])).begin_parse().read_msg_addr().to_string(1, 1, 1)
make_log("UserContent", f"Trusted cop address: {trusted_cop_address} / Contract address: {trusted_cop_address}", level="info")
assert trusted_cop_address == cc_indexator_data['address'], "Trusted cop address is not equal"
self.owner_address = cc_indexator_data['owner_address']
self.type = 'nft/listen'
self.content_id = stored_content.id
db_session.commit()
except BaseException as e:
errored = True
make_log("UserContent", f"Error: {e}" + '\n' + traceback.format_exc(), level="error")
if errored is True:
self.type = 'nft/unknown'
self.content_id = None
db_session.commit()