line.py 14 KB

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