line.py 14 KB

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