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 from app.core.models.node_storage import StoredContent
import json import json
from app.core.logger import make_log 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.home import router as home_router
from app.client_bot.routers.tonconnect import router as tonconnect_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.logger import logger
from app.core.content.content_id import ContentId from app.core.content.content_id import ContentId
import base58 import base58
from datetime import datetime, timedelta
import asyncio
from app.core._blockchain.ton.connect import TonConnect, wallet_obj_by_name
router = Router() 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): 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') make_log("OwnedContent", f"Inline query: {query.query}", level='info')
try: try:

View File

@ -1,8 +1,9 @@
import asyncio import asyncio
from base64 import b64decode from base64 import b64decode
from datetime import datetime from datetime import datetime, timedelta
from base58 import b58encode from base58 import b58encode
from sqlalchemy import and_
from tonsdk.boc import Cell from tonsdk.boc import Cell
from tonsdk.utils import Address 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._utils.send_status import send_status
from app.core.logger import make_log from app.core.logger import make_log
from app.core.models.node_storage import StoredContent 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._utils.resolve_content import resolve_content
from app.core.models.wallet_connection import WalletConnection from app.core.models.wallet_connection import WalletConnection
from app.core._keyboards import get_inline_keyboard from app.core._keyboards import get_inline_keyboard
from app.core.models._telegram import Wrapped_CBotChat from app.core.models._telegram import Wrapped_CBotChat
from app.core.storage import db_session 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 os
import traceback import traceback
@ -23,7 +27,54 @@ import traceback
async def license_index_loop(memory, platform_found: bool, seqno: int) -> [bool, int]: async def license_index_loop(memory, platform_found: bool, seqno: int) -> [bool, int]:
make_log("LicenseIndex", "Service running", level="debug") make_log("LicenseIndex", "Service running", level="debug")
with db_session() as session: 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 return platform_found, seqno

View File

@ -1,5 +1,5 @@
from app.core.models.node_storage import StoredContent 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.logger import make_log
from app.core._utils.tg_process_template import tg_process_template from app.core._utils.tg_process_template import tg_process_template
from app.core._config import PROJECT_HOST, CLIENT_TELEGRAM_BOT_USERNAME 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()) 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: 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([{ inline_keyboard_array.append([{
'text': self.user.translated('buyTrackListenLicense_button').format(price=str(round(0.15 , 3))), '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}' 'callback_data': f'PC_{content.id}'
}]) }])
template_kwargs['audio'] = URLInputFile(local_content_url + '?seconds_limit=30')
text = self.user.translated('p_playerContext_preview') 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}") 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.models.wallet_connection import WalletConnection
from app.core._blockchain.ton.toncenter import toncenter from app.core._blockchain.ton.toncenter import toncenter
from tonsdk.boc import Cell from tonsdk.boc import Cell
@ -35,10 +42,44 @@ class NodeStorageIndexationMixin:
pass # async def fetch_onchain_metadata(self): pass # async def fetch_onchain_metadata(self):
class UserContentIndexationMixin: 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()