line.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. import uuid
  2. import fastapi
  3. import pymysql
  4. pymysql.install_as_MySQLdb()
  5. from linebot.models import (
  6. MessageEvent, TextMessage, TextSendMessage, FollowEvent,
  7. TemplateSendMessage, ButtonsTemplate, URITemplateAction,
  8. )
  9. from linebot.exceptions import LineBotApiError
  10. import dataset
  11. import requests
  12. import json
  13. import qrcode
  14. from fastapi.encoders import jsonable_encoder
  15. from random import randrange
  16. from app.schemas import line
  17. from app.core.config import settings
  18. import datetime as dt
  19. from fastapi import APIRouter, FastAPI, Request, Response, Body,Depends
  20. from fastapi.routing import APIRoute
  21. from app.api import deps
  22. from app import crud, models, schemas
  23. from typing import Callable, List
  24. from uuid import uuid4
  25. from sqlalchemy.orm import Session
  26. from datetime import datetime
  27. import httpx
  28. import pymysql
  29. pymysql.install_as_MySQLdb()
  30. async def request_get(url, headers):
  31. async with httpx.AsyncClient() as client:
  32. return await client.get(url, headers = headers)
  33. class LineRouter(APIRoute):
  34. def get_route_handler(self) -> Callable:
  35. original_route_handler = super().get_route_handler()
  36. async def custom_route_handler(request: Request) -> Response:
  37. try:
  38. the_body = await request.json()
  39. except:
  40. the_body = ""
  41. response: Response = await original_route_handler(request)
  42. print(f"request payload: {the_body}")
  43. # print(f"route response: {response.body}")
  44. print(f"route response headers: {response.headers}")
  45. return response
  46. return custom_route_handler
  47. router = APIRouter(route_class=LineRouter)
  48. baseUrl = "https://nft-api-staging.joyso.io/api/v1/"
  49. headers = {
  50. 'Authorization':
  51. 'Basic %s' % 'bmZ0OmMxOTEzOWMzYjM3YjdjZWU3ZmY3OTFiZGU3NzdjZWNl'
  52. }
  53. # callback event
  54. @router.post("/callback")
  55. async def callback(request: fastapi.Request):
  56. signature = request.headers['X-Line-Signature']
  57. body = await request.body()
  58. settings.handler.handle(body.decode('utf-8'), signature)
  59. return 'OK'
  60. # follow event
  61. @settings.handler.add(FollowEvent)
  62. def handle_follow(event):
  63. # get user id when follow
  64. real_user_id = event.source.user_id
  65. # db connect and search
  66. db = dataset.connect(
  67. 'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
  68. )
  69. table = db['users']
  70. result1 = table.find_one(userid=real_user_id)
  71. # 都存在db的話
  72. if result1:
  73. db.close()
  74. settings.line_bot_api.reply_message(
  75. event.reply_token,
  76. TextSendMessage(text='很高興再見到您!'))
  77. # 建立全新使用者
  78. else:
  79. # create user account api
  80. url = 'https://nft-api-staging.joyso.io/api/v1/accounts'
  81. headers = {
  82. 'Authorization':
  83. 'Basic bmZ0OmMxOTEzOWMzYjM3YjdjZWU3ZmY3OTFiZGU3NzdjZWNl'
  84. }
  85. try:
  86. profile = settings.line_bot_api.get_profile(real_user_id)
  87. print(profile['displayName'])
  88. except LineBotApiError as e:
  89. pass
  90. # setup for temp use (unique id)
  91. rand_num = str(randrange(99999))
  92. # user_id = event.source.user_id + rand_num
  93. user_id = event.source.user_id
  94. data = 'uid=' + user_id
  95. r = requests.post(url=url, headers=headers, data=data)
  96. # extract the account address
  97. dict_str = json.loads(r.text)
  98. user_account = dict_str['account']
  99. user_address = user_account['address']
  100. # generate qr code from user id
  101. qr = qrcode.QRCode(
  102. version=1,
  103. box_size=10,
  104. border=5)
  105. qr.add_data(user_address)
  106. qr.make(fit=True)
  107. img_qr = qr.make_image(fill='black', back_color='white')
  108. filename = "/var/www/ArkCard-Linebot/ArkCard-web/qrcode/" + \
  109. real_user_id + '.png'
  110. img_qr.save(filename)
  111. # add to db
  112. data = dict(userid=real_user_id, useraddress=user_address)
  113. table.insert(data)
  114. db.close()
  115. settings.line_bot_api.reply_message(
  116. event.reply_token,
  117. TextSendMessage(text='歡迎加入好友'))
  118. # message handler
  119. @settings.handler.add(MessageEvent, message=TextMessage)
  120. def message(event):
  121. if '我要發送' in event.message.text:
  122. button_template_message = ButtonsTemplate(
  123. title=' ',
  124. text='點擊並打開收藏的NFT,可以選擇想要發送的NFT給對方!',
  125. actions=[
  126. URITemplateAction(
  127. label='打開發送頁',
  128. uri='https://ark.cards/collect.html?'
  129. + event.source.user_id)])
  130. settings.line_bot_api.reply_message(
  131. event.reply_token,
  132. TemplateSendMessage(
  133. alt_text="Receive",
  134. template=button_template_message))
  135. elif '我要接收' in event.message.text:
  136. button_template_message = ButtonsTemplate(
  137. title=' ',
  138. text='點擊並打開接收頁面,即可分享接收地址給對方!',
  139. actions=[
  140. URITemplateAction(
  141. label='打開接收頁',
  142. uri='https://ark.cards/qr-code.html?' +
  143. event.source.user_id)])
  144. settings.line_bot_api.reply_message(
  145. event.reply_token,
  146. TemplateSendMessage(
  147. alt_text="Receive",
  148. template=button_template_message))
  149. elif 'NFT商店' in event.message.text:
  150. button_template_message = ButtonsTemplate(
  151. title=' ',
  152. text='點擊並打開NFT商品頁,就可以購買您所想要的NFT商品哦!',
  153. actions=[
  154. URITemplateAction(
  155. label='打開NFT商品頁',
  156. uri='https://ark.cards/shop.html?' +
  157. event.source.user_id)])
  158. settings.line_bot_api.reply_message(
  159. event.reply_token,
  160. TemplateSendMessage(
  161. alt_text="Receive",
  162. template=button_template_message))
  163. elif 'NFT收藏' in event.message.text:
  164. button_template_message = ButtonsTemplate(
  165. title=' ',
  166. text='點擊並打開收藏的NFT,可以查看收到的NFT!',
  167. actions=[
  168. URITemplateAction(
  169. label='打開收藏頁',
  170. uri='https://ark.cards/collect.html?' +
  171. event.source.user_id), ])
  172. settings.line_bot_api.reply_message(
  173. event.reply_token,
  174. TemplateSendMessage(
  175. alt_text="Receive",
  176. template=button_template_message))
  177. else:
  178. button_template_message = ButtonsTemplate(
  179. title=' ',
  180. text='更多的服務內容,歡迎請上我們的官網!',
  181. actions=[
  182. URITemplateAction(
  183. label='ArkCard的官網',
  184. uri='https://ark.cards')])
  185. settings.line_bot_api.reply_message(
  186. event.reply_token,
  187. TemplateSendMessage(
  188. alt_text="Receive",
  189. template=button_template_message))
  190. @router.post("/push/")
  191. def push_text(user, message):
  192. settings.line_bot_api.push_message(
  193. user, TextSendMessage(text=message)
  194. )
  195. # nft collection api
  196. @router.get("/collection/{userid}")
  197. async def collection(userid, db: Session = Depends(deps.get_db)):
  198. # db connect
  199. url = 'accounts/' + str(userid) + '/nft_balances'
  200. r = await request_get(baseUrl + url, headers)
  201. nft_all = {}
  202. outcome = r.json()['nft_balances']
  203. ii = 0
  204. for i in range(len(outcome)):
  205. nft_ = crud.nft.get_by_uid(db, uid=outcome[i]['uid'])
  206. if nft_:
  207. nft_all[ii] = jsonable_encoder(nft_)
  208. nft_all[ii]['amount'] = outcome[i]['amount']
  209. ii+=1
  210. return nft_all
  211. db = dataset.connect(
  212. 'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
  213. )
  214. table3 = db['nftdrops']
  215. table2 = db['nft']
  216. nftdrops = {}
  217. nft = {}
  218. nfts_all = {}
  219. i = 0
  220. j = 0
  221. if not table3.find_one(userid=userid) and not table2.find_one(userid=userid):
  222. db.close()
  223. return "error: user don't have any nft"
  224. else:
  225. results1 = table3.find(userid=userid)
  226. for item in results1:
  227. nft_id = item['nftid']
  228. nftdrops[i] = table2.find_one(id=nft_id)
  229. i += 1
  230. results2 = table2.find(userid=userid)
  231. for item in results2:
  232. nft[j] = item
  233. j += 1
  234. nfts_all[0] = nftdrops
  235. nfts_all[1] = nft
  236. return nfts_all
  237. db.close()
  238. # receive handler
  239. @router.get("/receive/{userid}")
  240. def receive(userid):
  241. # db connect
  242. db = dataset.connect(
  243. 'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
  244. )
  245. table = db['users']
  246. table.find_one(userid=userid)
  247. if not table.find_one(userid=userid):
  248. db.close()
  249. return "ERROR: User Not Found"
  250. else:
  251. result = table.find_one(userid=userid)
  252. return {
  253. "userid": result['userid'], "useraddress": result['useraddress']
  254. }
  255. db.close()
  256. # send handler
  257. @router.post("/send")
  258. async def send(
  259. userid: str,
  260. to: str,
  261. nftuid: int,
  262. amount: int,
  263. db_: Session = Depends(deps.get_db),
  264. ):
  265. to_userid = crud.user.get_by_address(db_, address=to)
  266. db = dataset.connect(
  267. 'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
  268. )
  269. transaction_table = db['transaction']
  270. txid = str(uuid.uuid4())
  271. payload = {
  272. "txid": txid,
  273. # "contract": "0xe0d9102c88b09369df99b1c126fb2eebc13804f8",
  274. "contract": "0x8d5a7d8ca33fd9edca4b871cf5fb2a9cb5091505",
  275. "to": to,
  276. "uid": nftuid,
  277. "value": amount
  278. }
  279. url = "https://nft-api-staging.joyso.io/api/v1/accounts/Uba38e8903d243cd1bd15d5c27cc6653e/erc1155/safe_transfer_to"
  280. # r = requests.post(baseUrl + "accounts/" + userid+ "/erc1155/safe_transfer_to" , headers=headers, data=payload)
  281. r = requests.post(baseUrl + "accounts/" + userid+ "/erc1155/transfer_by_admin" , headers=headers, data=payload)
  282. # r = requests.post(url , headers=headers, data=payload)
  283. if r.status_code == 200:
  284. title = crud.nft.get_by_uid(db_, uid=nftuid).__dict__['title']
  285. message = "您的NFT : " + title + ", 已劃轉!"
  286. push_text(userid, message)
  287. transaction_table.insert({'txid':txid,'tfrom':userid,'to':to_userid,'nft':nftuid,'transaction_at':datetime.now()})
  288. from_name = settings.line_bot_api.get_profile(userid).as_json_dict()['displayName']
  289. try:
  290. to_name = settings.line_bot_api.get_profile(to_userid).as_json_dict()['displayName']
  291. except:
  292. to_name = "外部"
  293. fr = "您給"+ to_name +"的NFT(" + title + "), 已發送成功!"
  294. to_message = from_name +"給您的NFT("+title+"), 已收到!"
  295. push_text(userid, fr)
  296. push_text(to_userid, to_message)
  297. else:
  298. # push訊息
  299. message = "交易失敗!如果有疑問,請洽網站的服務信箱!"
  300. push_text(userid, message)
  301. return {'msg': r.content}
  302. return {'msg': 'OK'}
  303. # shop handler
  304. @router.get("/shop/{userid}")
  305. def shop(userid):
  306. # db connect
  307. db = dataset.connect(
  308. 'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
  309. )
  310. sql = 'SELECT title, id, imgurl, userid FROM arkcard.nft ' \
  311. 'GROUP BY uid'
  312. result = db.query(sql)
  313. rows = {}
  314. i = 0
  315. for row in result:
  316. rows[i] = row
  317. i += 1
  318. return rows
  319. db.close()
  320. @router.post("/buy")
  321. async def buy(userModel: line.BuyNft):
  322. # db connect
  323. db = dataset.connect(
  324. 'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
  325. )
  326. table2 = db['nft']
  327. # input
  328. nftid = userModel.nftid
  329. userid = userModel.userid
  330. if not table2.find_one(id=nftid):
  331. db.close()
  332. # push訊息
  333. message = "購買失敗!如果有疑問,請洽網站的服務信箱!"
  334. push_text(userid, message)
  335. return "該NFT商品不存在!如果有疑問,請洽網站的服務信箱!"
  336. else:
  337. user_obj = table2.find_one(id=nftid)
  338. user_obj['userid'] = userid
  339. table2.update(dict(user_obj), ['id'])
  340. # push訊息
  341. result3 = table2.find_one(id=nftid)
  342. title = result3['title']
  343. message = "您的NFT : " + title + ", 已購買成功!"
  344. push_text(userid, message)
  345. db.close()
  346. return "您已購買成功!"
  347. @router.post("/event")
  348. async def nftdrops(userModel: line.NftDrops):
  349. # db connect
  350. db = dataset.connect(
  351. 'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
  352. )
  353. table3 = db['nftdrops']
  354. # input對應
  355. eventid = '1'
  356. nftid = '1001'
  357. nftid2 = '1002'
  358. userid = userModel.userid
  359. email = userModel.email
  360. now = dt.datetime.now()
  361. # 如果userid不在db, 寫入到一個空值nft
  362. if not table3.find_one(userid=userid):
  363. # 新增資料
  364. table3.insert(
  365. dict(
  366. eventid=eventid, nftid=nftid,
  367. userid=userid, email=email, time=now
  368. ))
  369. table3.insert(
  370. dict(
  371. eventid=eventid, nftid=nftid2,
  372. userid=userid, email=email, time=now
  373. ))
  374. db.close()
  375. # push訊息
  376. message = "已為您登記活動!"
  377. push_text(userid, message)
  378. return "新增成功"
  379. # 如果userid存在,回傳通知
  380. else:
  381. db.close()
  382. # push訊息
  383. message = "您已登記過活動了!"
  384. push_text(userid, message)
  385. return "已有資料"
  386. @router.get("/transactions")
  387. def transactions(
  388. skip: int = 0,
  389. limit: int = 100,
  390. current_user: models.users = Depends(deps.get_current_active_superuser),
  391. ):
  392. # db connect
  393. db = dataset.connect(
  394. 'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
  395. )
  396. sql = 'SELECT * FROM transaction ORDER BY id Desc ' \
  397. 'limit '+str(skip)+', '+str(limit)+''
  398. nft_table = db['nft']
  399. result = db.query(sql)
  400. rows = []
  401. for row in result:
  402. nft_item = nft_table.find_one(uid=row['nft'])
  403. try:
  404. row['nft'] = nft_item['title']
  405. except:
  406. pass
  407. rows.append(row)
  408. db.close()
  409. return rows