line.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. import uvicorn
  2. import fastapi
  3. from fastapi.middleware.cors import CORSMiddleware
  4. from fastapi.responses import HTMLResponse
  5. from linebot import LineBotApi, WebhookHandler
  6. from linebot.models import (
  7. MessageEvent, TextMessage, TextSendMessage, FollowEvent, TemplateSendMessage, ButtonsTemplate, URITemplateAction,
  8. )
  9. import dataset
  10. import requests
  11. import json
  12. import qrcode
  13. # from PIL import Image
  14. # import base64, io
  15. from random import randrange
  16. from app.api.api_v1.endpoints import models
  17. import datetime as dt
  18. from fastapi import APIRouter
  19. router = APIRouter()
  20. # bot config
  21. line_bot_api = LineBotApi("SJT7VPT4RMQFLcS27jQBy3FcC24gtDrkcwJWZ5Xzqesr5T78LOKudHEJzt0k3b2S7n4KPwf27J7DVz2c8NQ4plSaaQylEeB1cYrfejaE/RPG/lCIQBYe4iBTzo26s4i2PcmT89837per/lTyvhVIKAdB04t89/1O/w1cDnyilFU=")
  22. handler = WebhookHandler("411ae3ef7e766739ed2c2c27b249d010")
  23. # callback event
  24. @router.post("/callback")
  25. async def callback(request: fastapi.Request):
  26. signature = request.headers['X-Line-Signature']
  27. body = await request.body()
  28. handler.handle(body.decode('utf-8'), signature)
  29. return 'OK'
  30. # follow event
  31. @handler.add(FollowEvent)
  32. def handle_follow(event):
  33. # get user id when follow
  34. real_user_id = event.source.user_id
  35. # db connect and search
  36. db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
  37. table = db['users']
  38. result1 = table.find_one(userid=real_user_id)
  39. # 都存在db的話
  40. if result1:
  41. db.close()
  42. line_bot_api.reply_message(
  43. event.reply_token,
  44. TextSendMessage(text='很高興再見到您!'))
  45. # 建立全新使用者
  46. else:
  47. # create user account api
  48. url = 'https://nft-api-staging.joyso.io/api/v1/accounts'
  49. headers = {'Authorization': 'Basic bmZ0OmMxOTEzOWMzYjM3YjdjZWU3ZmY3OTFiZGU3NzdjZWNl'}
  50. # setup for temp use (unique id)
  51. rand_num = str(randrange(99999))
  52. user_id = event.source.user_id + rand_num
  53. data = 'uid=' + user_id
  54. r = requests.post(url=url, headers=headers, data=data)
  55. # extract the account address
  56. dict_str = json.loads(r.text)
  57. user_account = dict_str['account']
  58. user_address = user_account['address']
  59. # generate qr code from user id
  60. qr = qrcode.QRCode(
  61. version=1,
  62. box_size=10,
  63. border=5)
  64. qr.add_data(user_address)
  65. qr.make(fit=True)
  66. img_qr = qr.make_image(fill='black', back_color='white')
  67. filename = "/var/www/ArkCard-Linebot/ArkCard-web/qrcode/" + real_user_id + '.png'
  68. img_save = img_qr.save(filename)
  69. # add to db
  70. data = dict(userid=real_user_id, useraddress=user_address)
  71. table.insert(data)
  72. db.close()
  73. line_bot_api.reply_message(
  74. event.reply_token,
  75. TextSendMessage(text='歡迎加入好友'))
  76. # message handler
  77. @handler.add(MessageEvent, message=TextMessage)
  78. def message(event):
  79. if '我要發送' in event.message.text:
  80. button_template_message = ButtonsTemplate(
  81. title=' ',
  82. text='點擊並打開收藏的NFT,可以選擇想要發送的NFT給對方!',
  83. actions=[
  84. URITemplateAction(
  85. label='打開發送頁',
  86. uri='https://ark.cards/collect.html?' + event.source.user_id),])
  87. line_bot_api.reply_message(
  88. event.reply_token,
  89. TemplateSendMessage(
  90. alt_text="Receive",
  91. template=button_template_message))
  92. elif '我要接收' in event.message.text:
  93. button_template_message = ButtonsTemplate(
  94. title=' ',
  95. text='點擊並打開接收頁面,即可分享接收地址給對方!',
  96. actions=[
  97. URITemplateAction(
  98. label='打開接收頁',
  99. uri='https://ark.cards/qr-code.html?' + event.source.user_id),])
  100. line_bot_api.reply_message(
  101. event.reply_token,
  102. TemplateSendMessage(
  103. alt_text="Receive",
  104. template=button_template_message))
  105. elif 'NFT商店' in event.message.text:
  106. button_template_message = ButtonsTemplate(
  107. title=' ',
  108. text='點擊並打開NFT商品頁,就可以購買您所想要的NFT商品哦!',
  109. actions=[
  110. URITemplateAction(
  111. label='打開NFT商品頁',
  112. uri='https://ark.cards/shop.html?' + event.source.user_id),])
  113. line_bot_api.reply_message(
  114. event.reply_token,
  115. TemplateSendMessage(
  116. alt_text="Receive",
  117. template=button_template_message))
  118. elif 'NFT收藏' in event.message.text:
  119. button_template_message = ButtonsTemplate(
  120. title=' ',
  121. text='點擊並打開收藏的NFT,可以查看收到的NFT!',
  122. actions=[
  123. URITemplateAction(
  124. label='打開收藏頁',
  125. uri='https://ark.cards/collect.html?' + event.source.user_id), ])
  126. line_bot_api.reply_message(
  127. event.reply_token,
  128. TemplateSendMessage(
  129. alt_text="Receive",
  130. template=button_template_message))
  131. else:
  132. button_template_message = ButtonsTemplate(
  133. title=' ',
  134. text='更多的服務內容,歡迎請上我們的官網!',
  135. actions=[
  136. URITemplateAction(
  137. label='ArkCard的官網',
  138. uri='https://ark.cards'),])
  139. line_bot_api.reply_message(
  140. event.reply_token,
  141. TemplateSendMessage(
  142. alt_text="Receive",
  143. template=button_template_message))
  144. # nft collection api
  145. @router.get("/collection/{userid}")
  146. def collection(userid):
  147. # 連到ownership表單去找,並回傳json
  148. # db connect
  149. db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
  150. table1 = db['ownership']
  151. nft = {}
  152. i = 0
  153. if not table1.find_one(userid=userid):
  154. db.close()
  155. return "error: user don't have any nft"
  156. else:
  157. results = db.query("SELECT nftid, amount FROM arkcard.ownership WHERE userid IN (SELECT userid FROM arkcard.ownership WHERE userid = '"+userid+"');")
  158. for item in results:
  159. nft[i] = item
  160. i += 1
  161. db.close()
  162. return nft
  163. # receive handler
  164. @router.get("/receive/{userid}")
  165. def receive(userid):
  166. # 確定要傳送的對象,到user去找到它
  167. # db connect
  168. db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
  169. table = db['users']
  170. if not table.find_one(userid=userid):
  171. db.close()
  172. return "ERROR: User Not Found"
  173. else:
  174. result = table.find_one(userid=userid)
  175. db.close()
  176. return {"userid": result['userid'], "address": result['address']}
  177. # send handler
  178. @router.post("/send")
  179. async def receive(userModel : models.TransactionNft):
  180. # 從網頁上選定好商品,在選定好數量後輸入收方地址;將把該商品轉到收方,並存下交易記錄
  181. # 從ownership找到該userid的擁有人,再把擁有人userid改為收方,並在trans留下記錄
  182. # db connect
  183. db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
  184. table1 = db['users']
  185. table2 = db['ownership']
  186. table3 = db['transactions']
  187. # find the userid from the selected nft
  188. fromuser = userModel.fromuser
  189. address = userModel.address
  190. result1 = table1.find_one(address=address)
  191. result2 = table1.find_one(userid=fromuser)
  192. # 例:
  193. # {
  194. # "fromuser": "a01",
  195. # "address": "0x4cd0ea8b1bdb5ab9249d96ccf3d8a0d3ada2bc76",
  196. # "dic": {"10":"2", "20":"3"}
  197. # }
  198. # 第一個為nftid, 第二個為amount,可以同時輸入多組nft給同一人
  199. the_list = userModel.dic
  200. fr_token = "from token"
  201. _hash = "hash"
  202. # confirm if the user exist
  203. # 如果沒有這個接受者,回傳錯誤
  204. if not result1:
  205. db.close()
  206. return {'msg': 'user not found'}
  207. # 確認發送方的nft夠不夠發放
  208. else:
  209. for nftid, amount in the_list.items():
  210. print("nft: "+nftid+",數量:"+ amount)
  211. # 直接針對取到的值去做確認數量和更新表單,最後再記錄
  212. # 確認sender的總數
  213. result3 = db.query('SELECT SUM(amount) AS total FROM arkcard.ownership WHERE userid="' + fromuser + '" AND nftid ="' + nftid + '";')
  214. # 把總數撈出來,存到total
  215. for row in result3:
  216. rows = row
  217. total = rows['total']
  218. # 如果總數不夠,就報錯回傳
  219. if total == None or total < int(amount):
  220. db.close()
  221. return {"messge: NFT not enough"}
  222. # 總數夠,在ownership刪去數量,並再新增ownership一筆
  223. else:
  224. # 今天到這,需要找出怎麼扣除總數的方式,目前找到的總數,會更新到資料欄位,導致原本才100個的,會被更新成499個,因為全加到一個
  225. # 找接收者的userid
  226. to_user = result1['userid']
  227. old_address = result2['address']
  228. print(total)
  229. new_amount = total - int(amount)
  230. # 更新原user總數
  231. result4 = db.query('DELETE FROM arkcard.ownership WHERE userid="' + fromuser + '" AND nftid ="' + nftid + '";')
  232. result5 = db.query('UPDATE arkcard.ownership SET amount = amount - "' + amount + '" WHERE userid = "' + fromuser + '" AND nftid = "' + nftid + '" LIMIT 1;')
  233. new_data1 = dict(userid=fromuser, address=old_address, nftid=nftid, amount=new_amount)
  234. table2.insert(new_data1)
  235. # 增加新user數量
  236. new_data2 = dict(userid=to_user, address=address, nftid=nftid, amount=int(amount))
  237. table2.insert(new_data2)
  238. # 在transactions記錄from和to, 以及時間等等
  239. now = dt.datetime.now()
  240. trans_data = dict(to_token=address, time=now, amount=int(amount), userid=fromuser, from_token=fr_token, hash=_hash)
  241. table3.insert(trans_data)
  242. db.close()
  243. return {"messge: NFT 夠"}
  244. # shop handler
  245. @router.get("/shop")
  246. def shop():
  247. # 為了顯示正確的nft網頁和數量,直接顯示nft表出來
  248. # db connect
  249. db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
  250. result1 = db.query('SELECT nftid, launched, amount, type, title, context FROM arkcard.nft WHERE launched="y" AND nftid NOT IN(SELECT nftid FROM arkcard.nft_to_event);')
  251. nfts = {}
  252. i = 0
  253. for row in result1:
  254. nfts[i] = row
  255. i += 1
  256. return nfts
  257. db.close()
  258. @router.post("/buy")
  259. async def buy(userModel : models.BuyNft):
  260. # db connect
  261. db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
  262. table1 = db['users']
  263. table2 = db['ownership']
  264. table3 = db['nft']
  265. table4 = db['transactions']
  266. shops = "shop"
  267. hash = "hash"
  268. # input
  269. the_list = userModel.dic
  270. address = userModel.address
  271. # 例:
  272. # {
  273. # "address": "d04",
  274. # "dic": {"1":"2","2":"3"}
  275. # }
  276. result1 = table1.find_one(address=address)
  277. # 找不到該使用者就回報錯誤
  278. if not result1:
  279. db.close()
  280. return "該用戶不存在!如果有疑問,請洽網站的服務信箱!"
  281. else:
  282. for nftid, amount in the_list.items():
  283. # 先user找到該使用者的userid留著備用
  284. userid = result1['userid']
  285. # 更新到ownership
  286. owner_data = [dict(userid=userid, address=address, nftid=nftid, amount=int(amount))]
  287. table2.insert_many(owner_data)
  288. # 更新nft數量
  289. result3 = table3.find_one(nftid=nftid)
  290. nft_total = result3['amount']
  291. total = int(nft_total) - int(amount)
  292. nft_data = dict(nftid=nftid, amount=int(total))
  293. table3.update(nft_data, ['nftid'])
  294. # 最後再來處理transactions
  295. now = dt.datetime.now()
  296. trans_data = dict(to_token=address, time=now, amount=int(amount), userid=userid, from_token=shops, hash=hash)
  297. table4.insert(trans_data)
  298. db.close()
  299. return "您已購買成功!"
  300. @router.post("/event")
  301. async def nftdrops(userModel : models.NftDrops):
  302. # db connect
  303. db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
  304. table1 = db['nft_drop_list']
  305. # input
  306. userid = userModel.userid
  307. email = userModel.email
  308. the_current_event = eventPeriod()
  309. if the_current_event == 0:
  310. return "目前沒有任何進行中的活動"
  311. else:
  312. # 再針對有上架的 event 填寫客戶到 nftdroplist
  313. now = dt.datetime.now()
  314. result1 = table1.find_one(userid=userid)
  315. if not result1:
  316. for key, item in the_current_event.items():
  317. table1.insert(dict(eventid=item, userid=userid, email=email, time=now))
  318. db.close()
  319. return "完成加入"
  320. else:
  321. db.close()
  322. return "該資料已存在"
  323. @router.post("/eventPeriod")
  324. def eventPeriod():
  325. # db connect
  326. db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
  327. # 設定event始終期間,與上架與否
  328. table3 = db['event']
  329. now = dt.datetime.now()
  330. # 先去找 event 表,確認在期間之內
  331. result = db.query("SELECT * FROM arkcard.event WHERE start < NOW() and NOW() < end and launched='yes';")
  332. rows = {}
  333. i = 0
  334. for row in result:
  335. rows[i] = row['eventid']
  336. i += 1
  337. return rows
  338. db.close()