From 91c9bdc69052d03eda605638db214115a055a3df Mon Sep 17 00:00:00 2001 From: musnow Date: Wed, 22 Feb 2023 11:20:38 +0800 Subject: [PATCH 01/30] docs(api): update api docs --- docs/valorant-shop-img-api.md | 15 +++++++++------ screenshot/log.png | Bin 7686 -> 7278 bytes 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/valorant-shop-img-api.md b/docs/valorant-shop-img-api.md index 156a451..ef7d430 100644 --- a/docs/valorant-shop-img-api.md +++ b/docs/valorant-shop-img-api.md @@ -4,18 +4,21 @@ > > Api根连接 https://val.musnow.top/api/ -Api是一个网页链接,能够方便的被用户使用或被开发者调用,以实现特定功能 +Api是一个网页链接,能够方便的被用户使用或被开发者调用,以实现特定功能。 -和阿狸机器人一样,**此api后台不会打印任何params参数**。只要链接不被攻击,你的账户密码只有你自己使用的时候看得到!很是安全 +和阿狸机器人一样,**此api后台不会打印任何params参数**。只要链接不被攻击,你的账户密码只有你自己使用的时候看得到!很是安全。 -> 阿狸帮助频道有一个免费的token,加入帮助频道即可领取;有任何问题,也欢迎加入帮助频道与我交流🎶 -> [kook帮助服务器邀请链接](https://kook.top/gpbTwZ) +> 阿狸帮助频道有一个免费的token,加入帮助频道即可领取; +> +> 有任何问题,也欢迎加入帮助频道与我交流🎶 [kook帮助服务器邀请链接](https://kook.top/gpbTwZ) +> +> 独立Api token获取🎉 [爱发电](https://afdian.net/item/31f60e884a1c11ed9cff52540025c377) | 接口 | 说明 | 状态 | | ---------- | --------------------- | -------- | | /shop-img | 登录,直接返回并跳转商店的图片 | 正常 | | /shop-url | 登录,以`json`格式返回商店图片url | 正常 | -| /shop-draw | 无需登录,提供4个皮肤uuid 返回图片url | 开放测试 | +| /shop-draw | 无需登录,提供4个皮肤uuid 返回图片url | 正常 | | /tfa | 邮箱验证接口,需和`/shop-url`接口配合使用 | 正常 | @@ -75,7 +78,7 @@ https://val.musnow.top/api/shop-img?token=API的密钥&account=账户&passwd=密 好处就是后台包装了**图片处理+riot登录**,你只需要传入账户密码,剩下的交给api解决! -注:只有`code 0`才是获取正常,`200/400` 都是有错误,会返回错误的原因。 +>注:只有`code 0`才是获取正常,`200/400` 都是有错误,会返回错误的原因。 ### 3.1 shop-url diff --git a/screenshot/log.png b/screenshot/log.png index cdb1c7849abe516e38e2197261f742d70b543dd9..c123b4d764295dee5be4ac482ad11c9c4e5f5a84 100644 GIT binary patch literal 7278 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 re2o6(nHtB;}4*4I?cfjWaaTCEb!E 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 mV3+>SZO*~}! Date: Wed, 22 Feb 2023 15:41:52 +0800 Subject: [PATCH 02/30] feat(api): add raw params for returning raw api response --- code/api.py | 6 +++--- code/utils/api/ApiHandler.py | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/code/api.py b/code/api.py index aa80d9a..29e1148 100644 --- a/code/api.py +++ b/code/api.py @@ -81,9 +81,9 @@ async def get_dailshop_img(request): # 获取图片url -@routes.post('/shop-url') +@routes.post('/shop') async def get_dailshop_img(request): - print(f"[{GetTime()}] [request] /shop-url") + print(f"[{GetTime()}] [request] /shop") try: ret = await login_img_request(request,"POST") return web.Response(body=json.dumps(ret, indent=2, sort_keys=True, ensure_ascii=False), @@ -155,6 +155,6 @@ async def aifadian_webhook(request): if __name__ == '__main__': try: # host需要设置成0.0.0.0,否则只有本地才能访问 print(f"[API Start] starting at {GetTime()}") - web.run_app(app, host='0.0.0.0', port=14725) + web.run_app(app, host='0.0.0.0', port=14726) except: print(traceback.format_exc()) \ No newline at end of file diff --git a/code/utils/api/ApiHandler.py b/code/utils/api/ApiHandler.py index 7c5f3c4..cdac6d5 100644 --- a/code/utils/api/ApiHandler.py +++ b/code/utils/api/ApiHandler.py @@ -73,7 +73,7 @@ async def check_token_rate(token: str): # 基本画图操作 -async def base_img_request(params, list_shop, vp1='0', rp1='0'): +async def base_img_request(params, list_shop, vp1=0, rp1=0): # 自定义背景 if 'img_src' in params and 'http' in params['img_src']: img_src = params['img_src'] @@ -140,11 +140,11 @@ async def img_draw_request(request): if 'vp' not in params or 'rp' not in params: return await base_img_request(params, list_shop) else: - return await base_img_request(params, list_shop, params['vp'], params['rp']) + return await base_img_request(params, list_shop, int(params['vp']), int(params['rp'])) # 登录+画图 -async def login_img_request(request,method:str="GET"): +async def login_img_request(request,method = "GET"): params = request.rel_url.query if method=="POST": body = await request.content.read() @@ -162,6 +162,9 @@ async def login_img_request(request,method:str="GET"): account = params['account'] passwd = params['passwd'] token = params['token'] + isRaw = ('raw' in params) # 用户需要原始uuid + isimgRatio = ( 'img_ratio' not in params or str(params['img_ratio']) != '1') # 判断是否有指定图片比例 + # 检测token速率,避免撞墙 ck_ret = await check_token_rate(token) if not ck_ret['status']: return {'code': 200, 'message': ck_ret['message'], 'info': ck_ret['info']} @@ -191,11 +194,14 @@ async def login_img_request(request,method:str="GET"): resp = await fetch_daily_shop(userdict) #获取每日商店 print(f'[{GetTime()}] [Api] fetch_daily_shop success') list_shop = resp["SkinsPanelLayout"]["SingleItemOffers"] # 商店刷出来的4把枪 - res_vprp = {'vp': '0', 'rp': '0'} # 先初始化为0 - if 'img_ratio' not in params or params['img_ratio'] != '1': + res_vprp = {'vp': 0, 'rp': 0} # 先初始化为0 + if isimgRatio or isRaw: res_vprp = await fetch_vp_rp_dict(userdict) # 只有16-9的图片需获取vp和r点 - # 不管什么情况,都请求这个 - return await base_img_request(params, list_shop, res_vprp['vp'], res_vprp['rp']) + # 如果用户需要raw,则返回皮肤uuid和vp rp的dict + if isRaw: + return { "code":0,"message":"获取原始接口返回值成功","info":"get raw response success","storefront":resp,"wallet":res_vprp} + else: + return await base_img_request(params, list_shop, res_vprp['vp'], res_vprp['rp']) # 邮箱验证的post From ff465ac7b36600815e8c651122beb233d5836d5b Mon Sep 17 00:00:00 2001 From: musnow Date: Wed, 22 Feb 2023 15:42:20 +0800 Subject: [PATCH 03/30] fix(shopimg): set vp rp as int --- code/utils/ShopImg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/utils/ShopImg.py b/code/utils/ShopImg.py index ee055d2..2f1419c 100644 --- a/code/utils/ShopImg.py +++ b/code/utils/ShopImg.py @@ -279,7 +279,7 @@ def skin_uuid_to_comp(skinuuid, ran, is_169=False): # 获取16比9的每日商店的图片 -async def get_shop_img_169(list_shop: dict, vp: str, rp: str, bg_img_src="err"): +async def get_shop_img_169(list_shop: dict, vp: int, rp: int, bg_img_src="err"): """ args: - list_shop: user daily shop skin dict - bg_img_src: background img url From 45615041a578032cbff45d6a9b1ee5aa76e8918b Mon Sep 17 00:00:00 2001 From: musnow Date: Wed, 22 Feb 2023 16:03:19 +0800 Subject: [PATCH 04/30] docs(api): update api docs --- docs/shop-raw-resp.json | 552 ++++++++++++++++++++++++++++++++++ docs/valorant-shop-img-api.md | 31 +- 2 files changed, 573 insertions(+), 10 deletions(-) create mode 100644 docs/shop-raw-resp.json diff --git a/docs/shop-raw-resp.json b/docs/shop-raw-resp.json new file mode 100644 index 0000000..65d0715 --- /dev/null +++ b/docs/shop-raw-resp.json @@ -0,0 +1,552 @@ +{ + "code": 0, + "info": "get raw response success", + "message": "获取原始接口返回值成功", + "storefront": { + "BonusStore": { + "BonusStoreOffers": [ + { + "BonusOfferID": "89b198a4-c374-4077-9e4b-f7c08e8c20bb", + "DiscountCosts": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 578 + }, + "DiscountPercent": 34, + "IsSeen": true, + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 875 + }, + "IsDirectPurchase": true, + "OfferID": "eb35e4e1-4138-9ece-08e2-69bd414cf598", + "Rewards": [ + { + "ItemID": "eb35e4e1-4138-9ece-08e2-69bd414cf598", + "ItemTypeID": "e7c63390-eda7-46e0-bb7a-a6abdacd2433", + "Quantity": 1 + } + ], + "StartDate": "2023-02-21T07:39:37.394337555Z" + } + }, + { + "BonusOfferID": "28f844b0-d87a-4547-8564-5cdaf607212f", + "DiscountCosts": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 2592 + }, + "DiscountPercent": 27, + "IsSeen": true, + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 3550 + }, + "IsDirectPurchase": true, + "OfferID": "6e37a33a-416e-fcc0-ceb8-7784e18fbfe9", + "Rewards": [ + { + "ItemID": "6e37a33a-416e-fcc0-ceb8-7784e18fbfe9", + "ItemTypeID": "e7c63390-eda7-46e0-bb7a-a6abdacd2433", + "Quantity": 1 + } + ], + "StartDate": "2023-02-21T07:39:37.394334172Z" + } + }, + { + "BonusOfferID": "57453b07-506a-4b81-9e96-104bda19da8b", + "DiscountCosts": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 1190 + }, + "DiscountPercent": 33, + "IsSeen": true, + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 1775 + }, + "IsDirectPurchase": true, + "OfferID": "5be0b43b-4e66-ab8a-91d9-be9137e2e1c2", + "Rewards": [ + { + "ItemID": "5be0b43b-4e66-ab8a-91d9-be9137e2e1c2", + "ItemTypeID": "e7c63390-eda7-46e0-bb7a-a6abdacd2433", + "Quantity": 1 + } + ], + "StartDate": "2023-02-21T07:39:37.394347741Z" + } + }, + { + "BonusOfferID": "4755ee10-030d-4e77-867b-c82fec80c148", + "DiscountCosts": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 455 + }, + "DiscountPercent": 48, + "IsSeen": true, + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 875 + }, + "IsDirectPurchase": true, + "OfferID": "a14fa0b5-4b03-2ad9-3b0e-a3ac6a0b033b", + "Rewards": [ + { + "ItemID": "a14fa0b5-4b03-2ad9-3b0e-a3ac6a0b033b", + "ItemTypeID": "e7c63390-eda7-46e0-bb7a-a6abdacd2433", + "Quantity": 1 + } + ], + "StartDate": "2023-02-21T07:39:37.394308751Z" + } + }, + { + "BonusOfferID": "24e7b3c9-7378-43e6-82ff-865a3b28bbe3", + "DiscountCosts": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 630 + }, + "DiscountPercent": 28, + "IsSeen": true, + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 875 + }, + "IsDirectPurchase": true, + "OfferID": "68564c51-4f7e-67d1-fd0d-4e9d216356e8", + "Rewards": [ + { + "ItemID": "68564c51-4f7e-67d1-fd0d-4e9d216356e8", + "ItemTypeID": "e7c63390-eda7-46e0-bb7a-a6abdacd2433", + "Quantity": 1 + } + ], + "StartDate": "2023-02-21T07:39:37.394329492Z" + } + }, + { + "BonusOfferID": "8b3a7d35-1f51-4538-ac53-235aea806336", + "DiscountCosts": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 1420 + }, + "DiscountPercent": 20, + "IsSeen": true, + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 1775 + }, + "IsDirectPurchase": true, + "OfferID": "291cb44a-410d-b035-4d0b-608a92c2cd91", + "Rewards": [ + { + "ItemID": "291cb44a-410d-b035-4d0b-608a92c2cd91", + "ItemTypeID": "e7c63390-eda7-46e0-bb7a-a6abdacd2433", + "Quantity": 1 + } + ], + "StartDate": "2023-02-21T07:39:37.394303818Z" + } + } + ], + "BonusStoreRemainingDurationInSeconds": 1182022 + }, + "FeaturedBundle": { + "Bundle": { + "CurrencyID": "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741", + "DataAssetID": "2654d506-4d05-e7e9-c996-63ac6fdaf767", + "DurationRemainingInSeconds": 1174822, + "ID": "2618261c-12aa-42c7-85bd-411e8daa684a", + "ItemOffers": null, + "Items": [ + { + "BasePrice": 375, + "CurrencyID": "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741", + "DiscountPercent": 0.2, + "DiscountedPrice": 300, + "IsPromoItem": false, + "Item": { + "Amount": 1, + "ItemID": "d3c65faf-49c2-9861-cccd-f3b5428c64da", + "ItemTypeID": "3f296c07-64c3-494c-923b-fe692a4fa1bd" + } + }, + { + "BasePrice": 375, + "CurrencyID": "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741", + "DiscountPercent": 0.2, + "DiscountedPrice": 300, + "IsPromoItem": false, + "Item": { + "Amount": 1, + "ItemID": "d824f9c8-48fd-f56c-5669-5680a7a10031", + "ItemTypeID": "3f296c07-64c3-494c-923b-fe692a4fa1bd" + } + }, + { + "BasePrice": 375, + "CurrencyID": "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741", + "DiscountPercent": 0.2, + "DiscountedPrice": 300, + "IsPromoItem": false, + "Item": { + "Amount": 1, + "ItemID": "068e78f6-4d12-7e0a-76ec-3cb80e157d76", + "ItemTypeID": "3f296c07-64c3-494c-923b-fe692a4fa1bd" + } + }, + { + "BasePrice": 325, + "CurrencyID": "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741", + "DiscountPercent": 0.2, + "DiscountedPrice": 260, + "IsPromoItem": false, + "Item": { + "Amount": 1, + "ItemID": "506bdf48-4d1b-87a7-6f02-eeb9ee9dadff", + "ItemTypeID": "d5f120f8-ff8c-4aac-92ea-f2b5acbe9475" + } + }, + { + "BasePrice": 5350, + "CurrencyID": "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741", + "DiscountPercent": 0.2, + "DiscountedPrice": 4280, + "IsPromoItem": false, + "Item": { + "Amount": 1, + "ItemID": "e98461ab-4e29-3bbe-513b-0b82f3013050", + "ItemTypeID": "e7c63390-eda7-46e0-bb7a-a6abdacd2433" + } + } + ], + "TotalBaseCost": null, + "TotalDiscountPercent": 0, + "TotalDiscountedCost": null, + "WholesaleOnly": true + }, + "BundleRemainingDurationInSeconds": 51622, + "Bundles": [ + { + "CurrencyID": "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741", + "DataAssetID": "2654d506-4d05-e7e9-c996-63ac6fdaf767", + "DurationRemainingInSeconds": 1174822, + "ID": "2618261c-12aa-42c7-85bd-411e8daa684a", + "ItemOffers": [ + { + "BundleItemOfferID": "d3c65faf-49c2-9861-cccd-f3b5428c64da", + "DiscountPercent": 0.2, + "DiscountedCost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 300 + }, + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 375 + }, + "IsDirectPurchase": true, + "OfferID": "d3c65faf-49c2-9861-cccd-f3b5428c64da", + "Rewards": [ + { + "ItemID": "d3c65faf-49c2-9861-cccd-f3b5428c64da", + "ItemTypeID": "3f296c07-64c3-494c-923b-fe692a4fa1bd", + "Quantity": 1 + } + ], + "StartDate": "0001-01-01T00:00:00Z" + } + }, + { + "BundleItemOfferID": "d824f9c8-48fd-f56c-5669-5680a7a10031", + "DiscountPercent": 0.2, + "DiscountedCost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 300 + }, + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 375 + }, + "IsDirectPurchase": true, + "OfferID": "d824f9c8-48fd-f56c-5669-5680a7a10031", + "Rewards": [ + { + "ItemID": "d824f9c8-48fd-f56c-5669-5680a7a10031", + "ItemTypeID": "3f296c07-64c3-494c-923b-fe692a4fa1bd", + "Quantity": 1 + } + ], + "StartDate": "0001-01-01T00:00:00Z" + } + }, + { + "BundleItemOfferID": "068e78f6-4d12-7e0a-76ec-3cb80e157d76", + "DiscountPercent": 0.2, + "DiscountedCost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 300 + }, + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 375 + }, + "IsDirectPurchase": true, + "OfferID": "068e78f6-4d12-7e0a-76ec-3cb80e157d76", + "Rewards": [ + { + "ItemID": "068e78f6-4d12-7e0a-76ec-3cb80e157d76", + "ItemTypeID": "3f296c07-64c3-494c-923b-fe692a4fa1bd", + "Quantity": 1 + } + ], + "StartDate": "0001-01-01T00:00:00Z" + } + }, + { + "BundleItemOfferID": "506bdf48-4d1b-87a7-6f02-eeb9ee9dadff", + "DiscountPercent": 0.2, + "DiscountedCost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 260 + }, + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 325 + }, + "IsDirectPurchase": true, + "OfferID": "506bdf48-4d1b-87a7-6f02-eeb9ee9dadff", + "Rewards": [ + { + "ItemID": "506bdf48-4d1b-87a7-6f02-eeb9ee9dadff", + "ItemTypeID": "d5f120f8-ff8c-4aac-92ea-f2b5acbe9475", + "Quantity": 1 + } + ], + "StartDate": "0001-01-01T00:00:00Z" + } + }, + { + "BundleItemOfferID": "e98461ab-4e29-3bbe-513b-0b82f3013050", + "DiscountPercent": 0.2, + "DiscountedCost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 4280 + }, + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 5350 + }, + "IsDirectPurchase": true, + "OfferID": "e98461ab-4e29-3bbe-513b-0b82f3013050", + "Rewards": [ + { + "ItemID": "e98461ab-4e29-3bbe-513b-0b82f3013050", + "ItemTypeID": "e7c63390-eda7-46e0-bb7a-a6abdacd2433", + "Quantity": 1 + } + ], + "StartDate": "0001-01-01T00:00:00Z" + } + } + ], + "Items": [ + { + "BasePrice": 375, + "CurrencyID": "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741", + "DiscountPercent": 0.2, + "DiscountedPrice": 300, + "IsPromoItem": false, + "Item": { + "Amount": 1, + "ItemID": "d3c65faf-49c2-9861-cccd-f3b5428c64da", + "ItemTypeID": "3f296c07-64c3-494c-923b-fe692a4fa1bd" + } + }, + { + "BasePrice": 375, + "CurrencyID": "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741", + "DiscountPercent": 0.2, + "DiscountedPrice": 300, + "IsPromoItem": false, + "Item": { + "Amount": 1, + "ItemID": "d824f9c8-48fd-f56c-5669-5680a7a10031", + "ItemTypeID": "3f296c07-64c3-494c-923b-fe692a4fa1bd" + } + }, + { + "BasePrice": 375, + "CurrencyID": "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741", + "DiscountPercent": 0.2, + "DiscountedPrice": 300, + "IsPromoItem": false, + "Item": { + "Amount": 1, + "ItemID": "068e78f6-4d12-7e0a-76ec-3cb80e157d76", + "ItemTypeID": "3f296c07-64c3-494c-923b-fe692a4fa1bd" + } + }, + { + "BasePrice": 325, + "CurrencyID": "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741", + "DiscountPercent": 0.2, + "DiscountedPrice": 260, + "IsPromoItem": false, + "Item": { + "Amount": 1, + "ItemID": "506bdf48-4d1b-87a7-6f02-eeb9ee9dadff", + "ItemTypeID": "d5f120f8-ff8c-4aac-92ea-f2b5acbe9475" + } + }, + { + "BasePrice": 5350, + "CurrencyID": "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741", + "DiscountPercent": 0.2, + "DiscountedPrice": 4280, + "IsPromoItem": false, + "Item": { + "Amount": 1, + "ItemID": "e98461ab-4e29-3bbe-513b-0b82f3013050", + "ItemTypeID": "e7c63390-eda7-46e0-bb7a-a6abdacd2433" + } + } + ], + "TotalBaseCost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 6800 + }, + "TotalDiscountPercent": 0.2, + "TotalDiscountedCost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 5440 + }, + "WholesaleOnly": true + } + ] + }, + "SkinsPanelLayout": { + "SingleItemOffers": [ + "155ba654-4afa-1029-9e71-e0b6962d5410", + "72586d88-46c4-1532-7507-d6bb5c2d201c", + "3800390c-4192-92d1-1703-33ba231fc226", + "5a27fbea-4f1d-62f9-8e5f-f494b4f0d322" + ], + "SingleItemOffersRemainingDurationInSeconds": 58822, + "SingleItemStoreOffers": [ + { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 2550 + }, + "IsDirectPurchase": true, + "OfferID": "155ba654-4afa-1029-9e71-e0b6962d5410", + "Rewards": [ + { + "ItemID": "155ba654-4afa-1029-9e71-e0b6962d5410", + "ItemTypeID": "e7c63390-eda7-46e0-bb7a-a6abdacd2433", + "Quantity": 1 + } + ], + "StartDate": "2023-02-21T07:39:37.394778084Z" + }, + { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 1775 + }, + "IsDirectPurchase": true, + "OfferID": "72586d88-46c4-1532-7507-d6bb5c2d201c", + "Rewards": [ + { + "ItemID": "72586d88-46c4-1532-7507-d6bb5c2d201c", + "ItemTypeID": "e7c63390-eda7-46e0-bb7a-a6abdacd2433", + "Quantity": 1 + } + ], + "StartDate": "2023-02-21T07:39:37.394799216Z" + }, + { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 1275 + }, + "IsDirectPurchase": true, + "OfferID": "3800390c-4192-92d1-1703-33ba231fc226", + "Rewards": [ + { + "ItemID": "3800390c-4192-92d1-1703-33ba231fc226", + "ItemTypeID": "e7c63390-eda7-46e0-bb7a-a6abdacd2433", + "Quantity": 1 + } + ], + "StartDate": "2023-02-21T07:39:37.394758105Z" + }, + { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 3550 + }, + "IsDirectPurchase": true, + "OfferID": "5a27fbea-4f1d-62f9-8e5f-f494b4f0d322", + "Rewards": [ + { + "ItemID": "5a27fbea-4f1d-62f9-8e5f-f494b4f0d322", + "ItemTypeID": "e7c63390-eda7-46e0-bb7a-a6abdacd2433", + "Quantity": 1 + } + ], + "StartDate": "2023-02-21T07:39:37.39479954Z" + } + ] + }, + "UpgradeCurrencyStore": { + "UpgradeCurrencyOffers": [ + { + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 1600 + }, + "IsDirectPurchase": true, + "OfferID": "f9cfa034-c7e1-4995-904c-1a296e7b1760", + "Rewards": [ + { + "ItemID": "e59aa87c-4cbf-517a-5983-6e81511be9b7", + "ItemTypeID": "ea6fcd2e-8373-4137-b1c0-b458947aa86d", + "Quantity": 20 + } + ], + "StartDate": "2020-01-01T00:00:00Z" + }, + "OfferID": "f9cfa034-c7e1-4995-904c-1a296e7b1760", + "StorefrontItemID": "187c8a5e-47de-f4ca-b02b-7697611cff5b" + }, + { + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 2800 + }, + "IsDirectPurchase": true, + "OfferID": "da0edbc8-31fb-468e-95a8-27ac25cd76ed", + "Rewards": [ + { + "ItemID": "e59aa87c-4cbf-517a-5983-6e81511be9b7", + "ItemTypeID": "ea6fcd2e-8373-4137-b1c0-b458947aa86d", + "Quantity": 40 + } + ], + "StartDate": "2020-01-01T00:00:00Z" + }, + "OfferID": "da0edbc8-31fb-468e-95a8-27ac25cd76ed", + "StorefrontItemID": "9483b151-4f66-298b-fb32-b58224324e67" + }, + { + "Offer": { + "Cost": { + "85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741": 4800 + }, + "IsDirectPurchase": true, + "OfferID": "a61e8526-bb1f-4135-b7df-95e67b416efe", + "Rewards": [ + { + "ItemID": "e59aa87c-4cbf-517a-5983-6e81511be9b7", + "ItemTypeID": "ea6fcd2e-8373-4137-b1c0-b458947aa86d", + "Quantity": 80 + } + ], + "StartDate": "2020-01-01T00:00:00Z" + }, + "OfferID": "a61e8526-bb1f-4135-b7df-95e67b416efe", + "StorefrontItemID": "f86f9999-452b-3d4c-788a-cda895ddf923" + } + ] + } + }, + "wallet": { + "rp": 275, + "vp": 247 + } +} \ No newline at end of file diff --git a/docs/valorant-shop-img-api.md b/docs/valorant-shop-img-api.md index ef7d430..9c21277 100644 --- a/docs/valorant-shop-img-api.md +++ b/docs/valorant-shop-img-api.md @@ -17,7 +17,7 @@ Api是一个网页链接,能够方便的被用户使用或被开发者调用 | 接口 | 说明 | 状态 | | ---------- | --------------------- | -------- | | /shop-img | 登录,直接返回并跳转商店的图片 | 正常 | -| /shop-url | 登录,以`json`格式返回商店图片url | 正常 | +| /shop | 登录,以`json`格式返回商店图片url或者riot原始响应 | 正常 | | /shop-draw | 无需登录,提供4个皮肤uuid 返回图片url | 正常 | | /tfa | 邮箱验证接口,需和`/shop-url`接口配合使用 | 正常 | @@ -80,12 +80,12 @@ https://val.musnow.top/api/shop-img?token=API的密钥&account=账户&passwd=密 >注:只有`code 0`才是获取正常,`200/400` 都是有错误,会返回错误的原因。 -### 3.1 shop-url +### 3.1 shop -如果你是开发者,请使用`/shop-url`来获取`json`格式的结果 +如果你是开发者,请使用`/shop`来获取`json`格式的结果 ~~~ -https://val.musnow.top/api/shop-url +https://val.musnow.top/api/shop ~~~ 请求方法: `POST` @@ -98,7 +98,8 @@ https://val.musnow.top/api/shop-url | account | 拳头账户 | string |是 | | passwd | 拳头账户密码 | string|是 | | img_src | 自定义背景图的url链接 | string | 否 | -| img_ratio | 自定义背景图比例,值为1代表正方形 | string |否 | +| img_ratio | 自定义返回图比例,值为1代表正方形 | int |否 | +| raw | 获取Riot接口的原始响应(不会返回画图结果) | int | 否 | 返回示例 @@ -110,6 +111,14 @@ https://val.musnow.top/api/shop-url } ~~~ +指定了raw参数的返回示例过长,参考 [shop-raw-resp.json](./shop-raw-resp.json) + +* `["storefront"]["SkinsPanelLayout"]` 是每日商店的结果,内部包含了4个皮肤uuid(注意是level 0 的uuid)、皮肤价格、商店剩余刷新时间 +* `["storefront"]["BonusStore"]` 是夜市,包含了皮肤uuid,折扣力度 +* `["wallet"]` 是用户的钱包,直接提供了vp和rp字段 + +获取到uuid后,您可以根据 [valorant-api](https://valorant-api.com/) 提供的接口,获取到皮肤的图片、各语言皮肤名字、和皮肤的等级(蓝紫橙) + ### 3.2 tfa 此接口用于两步验证,适用于开启了邮箱验证的用户; @@ -147,14 +156,16 @@ https://val.musnow.top/api/tfa 请求方法:`GET` +速率限制:`10r/m` + | params参数 | 说明 | 参数类型 |是否必填 | | ---------- | --------------------- | -------- | -------- | | token | API token | string|是 | | list_shop | 4个皮肤uuid | list |是 | -| vp | vp | string | 否 | -| rp | rp | string | 否 | +| vp | vp | int | 否 | +| rp | rp | int | 否 | | img_src | 自定义背景图的url链接 | string |否 | -| img_ratio | 自定义背景图比例,值为1代表正方形 | string |否 | +| img_ratio | 自定义返回图比例,值为1代表正方形 | int |否 | 其中 `list_shop` 为riot商店返回值中的以下字段,传入 `["SkinsPanelLayout"]["SingleItemOffers"]` 即可 @@ -185,12 +196,12 @@ vp/rp只有16-9的图片需要,如果设置了`img_ratio`为`'1'`,则无需 ## 4.Python示例代码 -### 示例代码1:shop-url +### 示例代码1:shop ~~~python import requests -url = "https://val.musnow.top/api/shop-url" +url = "https://val.musnow.top/api/shop" params = { "token":"api-token", "account": "拳头账户", From 4bd53fbcd0e27b33ffde82df0686a01a1a174679 Mon Sep 17 00:00:00 2001 From: musnow Date: Wed, 22 Feb 2023 16:21:25 +0800 Subject: [PATCH 05/30] docs(api): update api docs --- docs/valorant-shop-img-api.md | 42 ++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/docs/valorant-shop-img-api.md b/docs/valorant-shop-img-api.md index 9c21277..f3a3676 100644 --- a/docs/valorant-shop-img-api.md +++ b/docs/valorant-shop-img-api.md @@ -113,11 +113,47 @@ https://val.musnow.top/api/shop 指定了raw参数的返回示例过长,参考 [shop-raw-resp.json](./shop-raw-resp.json) -* `["storefront"]["SkinsPanelLayout"]` 是每日商店的结果,内部包含了4个皮肤uuid(注意是level 0 的uuid)、皮肤价格、商店剩余刷新时间 -* `["storefront"]["BonusStore"]` 是夜市,包含了皮肤uuid,折扣力度 +* `["storefront"]["SkinsPanelLayout"]` 是每日商店的结果,内部包含了4个皮肤uuid、皮肤价格、商店剩余刷新时间 +* `["storefront"]["BonusStore"]` 是夜市,包含了皮肤uuid,折扣力度;如果夜市没有开启,则不会有该字段 * `["wallet"]` 是用户的钱包,直接提供了vp和rp字段 -获取到uuid后,您可以根据 [valorant-api](https://valorant-api.com/) 提供的接口,获取到皮肤的图片、各语言皮肤名字、和皮肤的等级(蓝紫橙) +获取到uuid后,您可以根据 [valorant-api](https://valorant-api.com/) 提供的接口,获取到皮肤的图片、各语言皮肤名字、和皮肤的等级(蓝紫 +橙) + +其中需要注意的是,商店返回的结果是level0的uuid,并不是皮肤的主uuid。也就是说,您需要使用 `weapons/skinlevels` 接口来查询皮肤的情况 + +``` +https://valorant-api.com/v1/weapons/skinlevels/ +``` +skinlevels 接口的返回值如下,提供了皮肤的名字和图片 + +```json +{ + "status":200, + "data":{ + "uuid":"155ba654-4afa-1029-9e71-e0b6962d5410","displayName":"Snowfall Wand", + "levelItem":null, + "displayIcon":"https://media.valorant-api.com/weaponskinlevels/155ba654-4afa-1029-9e71-e0b6962d5410/displayicon.png", + "streamedVideo":null, + "assetPath":"ShooterGame/Content/Equippables/Melee/Snowglobe/Levels/Melee_Snowglobe_Lv1_PrimaryAsset" + } +} +``` +个人更加建议,使用 `weapons/skins` 将所有皮肤的键值缓存到本地,直接在本地遍历查找 +``` +https://valorant-api.com/v1/weapons/skins +``` + +因为 `weapons/skinlevels` 接口返回的内容中,不包含皮肤的等级uuid,这需要你在本地遍历后找到 `"contentTierUuid"`, 再调用如下接口获取到皮肤的等级 + +``` +https://valorant-api.com/v1/competitivetiers/{competitivetierUuid} +``` + +![resp-exp](https://img.kookapp.cn/assets/2023-02/LHAo1meQAP1jo0rj.png) + +还有一件事!部分皮肤返回结果中,是不带皮肤的图片的(我真的不理解为什么会这样)这也需要你遍历本地找皮肤图片! + ### 3.2 tfa From 5eb49f845277110de968cb642a4168db83f5a4a0 Mon Sep 17 00:00:00 2001 From: musnow Date: Wed, 22 Feb 2023 20:00:34 +0800 Subject: [PATCH 06/30] fix(ezauth): add multifactor_attempt_failed check in ezauth --- code/utils/valorant/EzAuth.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/utils/valorant/EzAuth.py b/code/utils/valorant/EzAuth.py index fc51ce1..081e52c 100644 --- a/code/utils/valorant/EzAuth.py +++ b/code/utils/valorant/EzAuth.py @@ -124,8 +124,11 @@ def authorize(self, username, password, key): elif "auth_failure" in r.text: User2faCode[key]['err'] = f"[{key}] 2fa auth_failue" raise EzAuthExp.MultifactorError(User2faCode[key]['err']) - else: # 大概率是验证码错了 - User2faCode[key]['err'] = f"[{key}] auth_failue, maybe wrong 2fa code" + elif "multifactor_attempt_failed" in r.text: # 大概率是验证码错了 + User2faCode[key]['err'] = f"[{key}] 2fa auth_failue, multifactor_attempt_failed" + raise EzAuthExp.MultifactorError(User2faCode[key]['err']) + else: + User2faCode[key]['err'] = f"[{key}] 2fa auth_failue, unkown err" raise EzAuthExp.MultifactorError(User2faCode[key]['err']) else: # 2fa等待超出600s User2faCode[key]['err']=f"[{key}] 2fa wait overtime, wait failed" From dd04d619d8ab25b27cbdfc9747312a337e24a3dc Mon Sep 17 00:00:00 2001 From: musnow Date: Thu, 23 Feb 2023 09:03:12 +0800 Subject: [PATCH 07/30] refactor(config): refactor config file --- code/main.py | 8 ++++---- code/utils/BotVip.py | 4 ++-- code/utils/KookApi.py | 4 ++-- code/utils/api/ApiHandler.py | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/code/main.py b/code/main.py index f379e0e..0be1491 100644 --- a/code/main.py +++ b/code/main.py @@ -38,9 +38,9 @@ # bot的token文件 from utils.FileManage import config, Save_All_File # 用读取来的 config 初始化 bot,字段对应即可 -bot = Bot(token=config['token']) +bot = Bot(token=config['token']['bot']) # 只用来上传图片的bot -bot_upimg = Bot(token=config['img_upload_token']) +bot_upimg = Bot(token=config['token']['img_upload_token']) # 设置全局变量:机器人开发者id/报错频道 master_id = config['master_id'] @@ -2285,8 +2285,8 @@ async def proc_check(msg:Message,*arg): async def loading_channel_cookie(): try: global debug_ch, cm_send_test - cm_send_test = await bot_upimg.client.fetch_public_channel(config["img_upload_channel"]) - debug_ch = await bot.client.fetch_public_channel(config['debug_ch']) + cm_send_test = await bot_upimg.client.fetch_public_channel(config['channel']["img_upload_ch"]) + debug_ch = await bot.client.fetch_public_channel(config['channel']['debug_ch']) print("[BOT.TASK] fetch_public_channel success") except: print("[BOT.TASK] fetch_public_channel failed") diff --git a/code/utils/BotVip.py b/code/utils/BotVip.py index 1acd1d1..540eab9 100644 --- a/code/utils/BotVip.py +++ b/code/utils/BotVip.py @@ -252,7 +252,7 @@ async def replace_illegal_img(user_id: str, num: int): except Exception as result: err_str = f"ERR! [{GetTime()}] replace_illegal_img\n```\n{traceback.format_exc()}\n```" print(err_str) - debug_ch = await bot.fetch_public_channel(config['debug_ch']) + debug_ch = await bot.fetch_public_channel(config['channel']['debug_ch']) await bot.client.send(debug_ch, err_str) #发送消息到debug频道 #计算用户背景图的list大小,避免出现空list的情况 @@ -301,7 +301,7 @@ async def get_vip_shop_bg_cm(msg: Message): #把被ban的图片替换成默认的图片,打印url便于日后排错 err_str += f"[UnidentifiedImageError] url={VipShopBgDict['bg'][msg.author_id]['background'][i]}\n```" await replace_illegal_img(msg.author_id, i) #替换图片 - debug_ch = await bot.fetch_public_channel(config['debug_ch']) + debug_ch = await bot.fetch_public_channel(config['channel']['debug_ch']) await bot.client.send(debug_ch, err_str) # 发送消息到debug频道 print(err_str) return f"您上传的图片违规!请慎重选择图片。多次上传违规图片会导致阿狸被封!下方有违规图片的url\n{err_str}" diff --git a/code/utils/KookApi.py b/code/utils/KookApi.py index 25e69ce..5656389 100644 --- a/code/utils/KookApi.py +++ b/code/utils/KookApi.py @@ -6,10 +6,10 @@ from utils.FileManage import config # 下方更新卡片消息需要bot -bot = Bot(token=config['token']) +bot = Bot(token=config['token']['bot']) # kook的base_url和headers kook_base_url = "https://www.kookapp.cn" -kook_headers = {f'Authorization': f"Bot {config['token']}"} +kook_headers = {f'Authorization': f"Bot {config['token']['bot']}"} #################################机器人在玩状态#################################### diff --git a/code/utils/api/ApiHandler.py b/code/utils/api/ApiHandler.py index cdac6d5..ce3ef6a 100644 --- a/code/utils/api/ApiHandler.py +++ b/code/utils/api/ApiHandler.py @@ -15,7 +15,7 @@ # bot的token文件 from utils.FileManage import config # 用来给kook上传文件的bot token -api_bot_token = config['api_bot_token'] +api_bot_token = config['token']['api_bot_token'] Api2faDict = {'data': {}} # 保存2fa用户登录的过程信息 # 默认的背景图 img_bak_169 = 'https://img.kookapp.cn/assets/2022-10/KcN5YoR5hC0zk0k0.jpg' @@ -30,9 +30,9 @@ # img = open(path,'rb') # gLock.release() # 释放锁 # # lsky的连接和token写入配置文件,方便修改 -# url = f"{config['lsky_url']}/api/v1/upload" +# url = f"{config['lsky']['url']}/api/v1/upload" # header = { -# "Authorization": f"Bearer {config['lsky_token']}", +# "Authorization": f"Bearer {config['lsky']['token']}", # "Accept": "application/json" # } # params = {'strategy_id':3} @@ -272,7 +272,7 @@ async def afd_request(request, bot): Module.Section(Element.Text(text, Types.Text.KMD))) cm.append(c) #print(json.dumps(cm)) - debug_ch = await bot.client.fetch_public_channel(config['debug_ch']) + debug_ch = await bot.client.fetch_public_channel(config['channel']['debug_ch']) await bot.client.send(debug_ch, cm) print(f"[{GetTime()}] trno:{params['data']['order']['out_trade_no']} afd-cm-send") return {"ec": 200, "em": "success"} From 8b85266e29e6f6cb0881e1e9029cc6890573cef0 Mon Sep 17 00:00:00 2001 From: musnow Date: Thu, 23 Feb 2023 09:07:04 +0800 Subject: [PATCH 08/30] docs(config): add config.exp.json --- code/config/config.exp.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 code/config/config.exp.json diff --git a/code/config/config.exp.json b/code/config/config.exp.json new file mode 100644 index 0000000..88b7e10 --- /dev/null +++ b/code/config/config.exp.json @@ -0,0 +1,21 @@ +{ + "token":{ + "bot": "机器人websocket token", + "api_bot_token": "api用来上传图片的机器人websocket token", + "img_upload_token": "用来进行vip商店图片测试的机器人websocket token(发图片让kook的内容识别来判断有么有问题)" + }, + "master_id": "机器人开发者userid", + "caiyun": "彩云小译的token", + "channel":{ + "debug_ch": "发送错误日志的频道", + "img_upload_ch": "用来测试vip商店图片的频道" + }, + "lsky":{ + "url":"lsky图床的url(该功能未启用)", + "token":"lsky图床的token(该功能未启用)" + }, + "leancloud":{ + "appid":"leancloud数据库的appid(可联系作者,共用皮肤评价数据库)", + "master_key":"leancloud数据库的masterkey" + } +} \ No newline at end of file From e8a655165741d844f5fe915618cc851010c31acf Mon Sep 17 00:00:00 2001 From: musnow Date: Thu, 23 Feb 2023 09:30:17 +0800 Subject: [PATCH 09/30] refactor(shoprate): add def for geting shop-rate only,rename rate_sum as rate_avg --- code/utils/ShopRate.py | 74 +++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/code/utils/ShopRate.py b/code/utils/ShopRate.py index 2d4a1ed..8be029d 100644 --- a/code/utils/ShopRate.py +++ b/code/utils/ShopRate.py @@ -1,17 +1,23 @@ import json import random +import leancloud from khl.card import Card, CardMessage, Module, Element, Types # 皮肤的评价 -from utils.FileManage import SkinRateDict +from utils.FileManage import config,SkinRateDict +leancloud.init(config["leancloud"]["appid"], master_key=config["leancloud"]["master_key"]) - -async def get_shop_rate_cm(list_shop: dict, kook_user_id: str, cm: CardMessage): +# 获取皮肤评价的信息 +async def get_shop_rate(list_shop: dict, kook_user_id: str): + """ + { "sum":rate_avg,"lv":rate_lv,"text_list":rate_text,"count":rate_count } + """ #皮肤评分和评价,用户不在err_user里面才显示 global SkinRateDict rate_text = [] rate_count = 0 rate_total = 0 + rate_avg = 0 # 平均分 for sk in list_shop: if sk in SkinRateDict['rate']: rate_count += 1 @@ -25,42 +31,50 @@ async def get_shop_rate_cm(list_shop: dict, kook_user_id: str, cm: CardMessage): text += f"「随机评论」 {SkinRateDict['rate'][sk]['cmt'][ran]}\n" rate_text.append(text) - if rate_count == 0: - rate_lv = "皮肤评价数据仍待收集…" - c1 = Card(Module.Header(f"{rate_lv}"), Module.Context(f"你可以使用「/rate 皮肤名」参与评分哦!"), color='#fb4b57') - else: - rate_sum = rate_total // rate_count + rate_lv = "皮肤评价数据仍待收集…" + if rate_count > 0: + rate_avg = rate_total // rate_count #记录当日冠军和屌丝 - if rate_sum > SkinRateDict["cmp"]["best"]["pit"]: - SkinRateDict["cmp"]["best"]["pit"] = rate_sum + if rate_avg > SkinRateDict["cmp"]["best"]["pit"]: + SkinRateDict["cmp"]["best"]["pit"] = rate_avg SkinRateDict["cmp"]["best"]["skin"] = list_shop SkinRateDict["cmp"]["best"]["kook_id"] = kook_user_id - print(f"[shop] update rate-best Au:{kook_user_id} = {rate_sum}") - elif rate_sum < SkinRateDict["cmp"]["worse"]["pit"]: - SkinRateDict["cmp"]["worse"]["pit"] = rate_sum + print(f"[shop] update rate-best Au:{kook_user_id} = {rate_avg}") + elif rate_avg < SkinRateDict["cmp"]["worse"]["pit"]: + SkinRateDict["cmp"]["worse"]["pit"] = rate_avg SkinRateDict["cmp"]["worse"]["skin"] = list_shop SkinRateDict["cmp"]["worse"]["kook_id"] = kook_user_id - print(f"[shop] update rate-worse Au:{kook_user_id} = {rate_sum}") + print(f"[shop] update rate-worse Au:{kook_user_id} = {rate_avg}") - if rate_sum >= 0 and rate_sum <= 20: + if rate_avg >= 0 and rate_avg <= 20: rate_lv = "丐帮帮主" - elif rate_sum > 20 and rate_sum <= 40: + elif rate_avg > 20 and rate_avg <= 40: rate_lv = "省钱能手" - elif rate_sum > 40 and rate_sum <= 60: + elif rate_avg > 40 and rate_avg <= 60: rate_lv = "差强人意" - elif rate_sum > 60 and rate_sum <= 80: + elif rate_avg > 60 and rate_avg <= 80: rate_lv = "芜湖起飞" - elif rate_sum > 80 and rate_sum <= 100: + elif rate_avg > 80 and rate_avg <= 100: rate_lv = "天选之人" - c1 = Card(Module.Header(f"综合评分 {rate_sum},{rate_lv}"), - Module.Context(f"以下评论来自其他用户,仅供图一乐"), - Module.Divider(), - color='#fb4b57') - for text in rate_text: + + return { "sum":rate_avg,"lv":rate_lv,"text_list":rate_text,"count":rate_count } + +# 获取皮肤评价的卡片 +async def get_shop_rate_cm(list_shop: dict, kook_user_id: str, cm: CardMessage): + ret = await get_shop_rate(list_shop, kook_user_id) + if ret['count'] == 0: + c1 = Card(Module.Header(f"{ret['lv']}"), Module.Context(f"你可以使用「/rate 皮肤名」参与评分哦!"), color='#fb4b57') + else: + c1 = Card(Module.Header(f"综合评分 {ret['sum']},{ret['lv']}"), + Module.Context(f"以下评论来自其他用户,仅供图一乐"), + Module.Divider(), + color='#fb4b57') + # 插入单个皮肤评价 + for text in ret['text_list']: c1.append(Module.Section(Element.Text(text, Types.Text.KMD))) c1.append(Module.Divider()) c1.append(Module.Context(Element.Text(f"可以使用「/rate 皮肤名」参与评分\n或用「/kkn」查看昨日天选之子/丐帮帮主", Types.Text.KMD))) - + # 插入卡片 cm.append(c1) return cm @@ -76,14 +90,14 @@ async def check_shop_rate(kook_user_id: str, list_shop: list): rate_total += SkinRateDict['rate'][sk]['pit'] if rate_count != 0: - rate_sum = rate_total // rate_count #平均分 + rate_avg = rate_total // rate_count #平均分 #记录冠军和屌丝 - if rate_sum > SkinRateDict["cmp"]["best"]["pit"]: - SkinRateDict["cmp"]["best"]["pit"] = rate_sum + if rate_avg > SkinRateDict["cmp"]["best"]["pit"]: + SkinRateDict["cmp"]["best"]["pit"] = rate_avg SkinRateDict["cmp"]["best"]["skin"] = list_shop SkinRateDict["cmp"]["best"]["kook_id"] = kook_user_id - elif rate_sum < SkinRateDict["cmp"]["worse"]["pit"]: - SkinRateDict["cmp"]["worse"]["pit"] = rate_sum + elif rate_avg < SkinRateDict["cmp"]["worse"]["pit"]: + SkinRateDict["cmp"]["worse"]["pit"] = rate_avg SkinRateDict["cmp"]["worse"]["skin"] = list_shop SkinRateDict["cmp"]["worse"]["kook_id"] = kook_user_id return True From b013a64c84af65110e22183893f2f591cd11f18a Mon Sep 17 00:00:00 2001 From: musnow Date: Thu, 23 Feb 2023 09:55:21 +0800 Subject: [PATCH 10/30] feat(shoprate): using leancloud to get skin rate --- code/utils/ShopRate.py | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/code/utils/ShopRate.py b/code/utils/ShopRate.py index 8be029d..2a670dc 100644 --- a/code/utils/ShopRate.py +++ b/code/utils/ShopRate.py @@ -1,6 +1,7 @@ import json import random import leancloud +import traceback from khl.card import Card, CardMessage, Module, Element, Types # 皮肤的评价 @@ -9,29 +10,38 @@ # 获取皮肤评价的信息 async def get_shop_rate(list_shop: dict, kook_user_id: str): - """ + """皮肤评分和评价,用户不在err_user里面才显示\n { "sum":rate_avg,"lv":rate_lv,"text_list":rate_text,"count":rate_count } """ - #皮肤评分和评价,用户不在err_user里面才显示 - global SkinRateDict + # 初始化相关值 rate_text = [] rate_count = 0 rate_total = 0 rate_avg = 0 # 平均分 - for sk in list_shop: - if sk in SkinRateDict['rate']: - rate_count += 1 - rate_total += SkinRateDict['rate'][sk]['pit'] - skin_name = f"「{SkinRateDict['rate'][sk]['name']}」" - text = f"%-50s\t\t评分: {SkinRateDict['rate'][sk]['pit']}\n" % skin_name - if len(SkinRateDict['rate'][sk]['cmt']) == 1: - ran = 0 #元素内只有1个评论,直接选定该评论 - else: - ran = random.randint(0, len(SkinRateDict['rate'][sk]['cmt']) - 1) - text += f"「随机评论」 {SkinRateDict['rate'][sk]['cmt'][ran]}\n" + # leancould中搜索 + query = leancloud.Query('SkinRate') + # 遍历用户的4个商店皮肤 + for skin in list_shop: + # 查找数据库中和skin uuid相同的评价 + query.equal_to('skinUuid',skin) + objlist = query.find() + if len(objlist) > 0: #找到了 + obj = objlist[0] + rate_total += obj.get('rating') # 获取评分 + skin_name = f"「{obj.get('skinName')}」" # 获取皮肤名 + text = f"%-50s\t\t评分: {obj.get('rating')}\n" % skin_name + # 获取皮肤评价的list + cmt = obj.get('comment') + if len(cmt) == 0:# 元素内没有评论,出现错误 + text += f"「随机评论」 NO CMT ERR\n" + else:# 有评论,生成随机数 + ran = random.randint(0, len(cmt) - 1) + text += f"「随机评论」 {cmt[ran]}\n" + # 插入text rate_text.append(text) rate_lv = "皮肤评价数据仍待收集…" + rate_count = len(rate_text) # 直接计算长度 if rate_count > 0: rate_avg = rate_total // rate_count #记录当日冠军和屌丝 From 5026e8ba31065f07ee1046bf21e173d757df1a9a Mon Sep 17 00:00:00 2001 From: musnow Date: Thu, 23 Feb 2023 10:25:10 +0800 Subject: [PATCH 11/30] feat(main): add update_shop_cmp in notify task --- code/main.py | 9 +++++---- code/utils/ShopRate.py | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/code/main.py b/code/main.py index 0be1491..ee4faf0 100644 --- a/code/main.py +++ b/code/main.py @@ -31,7 +31,7 @@ from utils.BotVip import (VipUserDict, create_vip_uuid, fetch_vip_user, roll_vip_start, using_vip_uuid, vip_ck, vip_time_remain, vip_time_remain_cm, vip_time_stamp,get_vip_shop_bg_cm,replace_illegal_img,illegal_img_169) from utils.Translate import ListTL, translate_main, Shutdown_TL, checkTL, Open_TL, Close_TL -from utils.ShopRate import SkinRateDict, get_shop_rate_cm, check_shop_rate +from utils.ShopRate import SkinRateDict, get_shop_rate_cm, check_shop_rate,update_shop_cmp from utils.ShopImg import get_shop_img_11, get_shop_img_169, img_requestor from utils.valorant.ValFileUpd import update_bundle_url, update_price, update_skins @@ -2021,9 +2021,10 @@ async def auto_skin_notify(): SkinRateDict["cmp"]["best"]["pit"] = 0 SkinRateDict["cmp"]["worse"]["skin"] = list() SkinRateDict["cmp"]["worse"]["pit"] = 100 - print("[BOT.TASK.NOTIFY] SkinRateDict/UserShopDict clear, sleep(15)") - #睡15s再开始遍历(避免时间不准) - await asyncio.sleep(15) + update_shop_cmp() # 更新数据库中的记录 + print("[BOT.TASK.NOTIFY] SkinRateDict/UserShopDict clear, sleep(10)") + #睡10s再开始遍历(避免时间不准) + await asyncio.sleep(10) print("[BOT.TASK.NOTIFY] auto_skin_notify Start") #加载vip用户列表 VipUserD = copy.deepcopy(VipUserDict) diff --git a/code/utils/ShopRate.py b/code/utils/ShopRate.py index 2a670dc..1eaf94e 100644 --- a/code/utils/ShopRate.py +++ b/code/utils/ShopRate.py @@ -112,4 +112,43 @@ async def check_shop_rate(kook_user_id: str, list_shop: list): SkinRateDict["cmp"]["worse"]["kook_id"] = kook_user_id return True else: - return False \ No newline at end of file + return False + +# 每日早八,更新leancloud中的ShopCmp(被kill掉的时候也执行此函数) +def update_shop_cmp(): + """update shop rate in leancloud + """ + try: + # 获取对象 + ShopCmp = leancloud.Object.extend('ShopCmp') + query = ShopCmp.query + # 只更新昨日的 + query.equal_to('today',False) + objlist = query.find() + if len(objlist) == 0: + raise Exception("leancloud find today err!") + # 开始更新,先设置为最差 + platfrom = 'kook' + rate_avg = SkinRateDict["kkn"]["worse"]["pit"] + list_shop = SkinRateDict["kkn"]["worse"]["skin"] + kook_user_id = SkinRateDict["kkn"]["worse"]["kook_id"] + for i in objlist: + if(i.get('best')): # 是最佳 + if SkinRateDict["kkn"]["best"]["pit"] <= i.get('rating'): + continue # 当前用户分数小于数据库中的,不更新 + # 设置值 + rate_avg = SkinRateDict["kkn"]["best"]["pit"] + list_shop = SkinRateDict["kkn"]["best"]["skin"] + kook_user_id = SkinRateDict["kkn"]["best"]["kook_id"] + elif(SkinRateDict["kkn"]["worse"]["pit"] >= i.get('rating')): # 是最差,判断分数 + continue # 如果本地用户好于数据库记录,不更新 + + # 更新对象并保存 + i.set('userId',kook_user_id) + i.set('skinList',list_shop) + i.set('rating',rate_avg) + i.set('platfrom',platfrom) + i.save() + print(f"[update_shop_cmp] saving best:{i.get('best')}") + except: + print(f"ERR! [update_shop_cmp]\n{traceback.format_exc()}") \ No newline at end of file From 2cb1d74310fafff6c3c1d7385b5fd3ea8076b750 Mon Sep 17 00:00:00 2001 From: musnow Date: Thu, 23 Feb 2023 12:03:37 +0800 Subject: [PATCH 12/30] feat(api): add raw params check in /shop --- code/utils/api/ApiHandler.py | 2 +- docs/valorant-shop-img-api.md | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/code/utils/api/ApiHandler.py b/code/utils/api/ApiHandler.py index ce3ef6a..e2f328c 100644 --- a/code/utils/api/ApiHandler.py +++ b/code/utils/api/ApiHandler.py @@ -162,7 +162,7 @@ async def login_img_request(request,method = "GET"): account = params['account'] passwd = params['passwd'] token = params['token'] - isRaw = ('raw' in params) # 用户需要原始uuid + isRaw = ('raw' in params and str(params['raw']) != '0') # 用户需要原始uuid isimgRatio = ( 'img_ratio' not in params or str(params['img_ratio']) != '1') # 判断是否有指定图片比例 # 检测token速率,避免撞墙 ck_ret = await check_token_rate(token) diff --git a/docs/valorant-shop-img-api.md b/docs/valorant-shop-img-api.md index f3a3676..d33e894 100644 --- a/docs/valorant-shop-img-api.md +++ b/docs/valorant-shop-img-api.md @@ -24,15 +24,15 @@ Api是一个网页链接,能够方便的被用户使用或被开发者调用 ## 1.使用示例 -查询每日商店的链接如下,调用`/shop-img`接口,浏览器会直接跳转图片,适合普通用户快捷查看当日每日商店 +查询每日商店的链接如下,调用 `/shop-img` 接口,浏览器会直接跳转图片,适合普通用户快捷查看当日每日商店 -由于该接口会直接跳转到图片,所以不支持开启了邮箱验证的用户 +由于该接口会直接跳转到图片,所以不支持开启了邮箱验证的用户,也不建议开发者调用 ~~~ https://val.musnow.top/api/shop-img?token=API的密钥&account=账户&passwd=密码 ~~~ -直接丢浏览器里面打开就行,想看商店直接用这个链接看就行辣,可以浏览器收藏一下哦! +补充好上面的链接后,直接丢浏览器里面打开就OK。可以浏览器收藏一下,方便后续查看! * 添加第四个参数`&img_src=图片url`,此参数用于自定义背景图 * 添加第五个参数`&img_ratio=图片比例`,将此参数设置为`1`,代表背景图是 `1-1` 的正方形,最终的成果图也将是正方形;默认比例为 `16-9` @@ -44,7 +44,7 @@ https://val.musnow.top/api/shop-img?token=API的密钥&account=账户&passwd=密 https://val.musnow.top/api/shop-img?token=API的密钥&account=账户&passwd=密码&img_src=背景图片链接 ~~~ -如果背景图是1-1的正方形 +如果背景图是正方形(1-1) ~~~ https://val.musnow.top/api/shop-img?token=API的密钥&account=账户&passwd=密码&img_src=背景图片链接&img_ratio=1 ~~~ @@ -99,7 +99,16 @@ https://val.musnow.top/api/shop | passwd | 拳头账户密码 | string|是 | | img_src | 自定义背景图的url链接 | string | 否 | | img_ratio | 自定义返回图比例,值为1代表正方形 | int |否 | -| raw | 获取Riot接口的原始响应(不会返回画图结果) | int | 否 | +| raw | 设置为1,获取Riot接口的原始响应(不画图) | int | 否 | + +```python +# 用户需要原始uuid +# raw参数在params中,且该参数不为0 +isRaw = ('raw' in params and str(params['raw']) != '0') +# 判断是否有指定图片比例 +# img_ratio在params中,且该参数不为非1 +isimgRatio = ( 'img_ratio' not in params or str(params['img_ratio']) != '1') +``` 返回示例 From 4e1ef8187c45efadbaa265aced36d56a0319d96f Mon Sep 17 00:00:00 2001 From: musnow Date: Thu, 23 Feb 2023 21:06:29 +0800 Subject: [PATCH 13/30] docs(init-bot): update docs --- docs/init-bot.md | 63 +++++++++++++---------- docs/log.example/cookie/.empty | 0 docs/log.example/img_temp/comp/.empty | 0 docs/log.example/img_temp/weapon/.empty | 0 docs/log.example/img_temp_vip/comp/.empty | 0 docs/log.example/img_temp_vip/shop/.empty | 0 6 files changed, 35 insertions(+), 28 deletions(-) create mode 100644 docs/log.example/cookie/.empty create mode 100644 docs/log.example/img_temp/comp/.empty create mode 100644 docs/log.example/img_temp/weapon/.empty create mode 100644 docs/log.example/img_temp_vip/comp/.empty create mode 100644 docs/log.example/img_temp_vip/shop/.empty diff --git a/docs/init-bot.md b/docs/init-bot.md index 8acdee3..7b1ae13 100644 --- a/docs/init-bot.md +++ b/docs/init-bot.md @@ -2,58 +2,64 @@ # 如何开始? -保证你的Windows/Linux中`Python`版本高于`3.8`,执行下面的安装库命令 +建议根据[khl.py](https://github.com/TWT233/khl.py)的`example`教程,学习KOOK机器人的基本搭建(很简单的,相信我) -更新Python的方法可以参考本wiki的 [01-How_to_update_Python](https://github.com/Aewait/Valorant-Kook-Bot/wiki/01-How_to_update_Python) +如果你已经学习完了所有example,那么本仓库的内容对于你来说就是不难的! + +我会添加一些只有本仓库中包含的内容的代码解释,方便你来搭建你自己的kook机器人!有任何问题,都可以加入我的[kook频道](https://kook.top/gpbTwZ)交流! + +### 1.克隆本仓库 + +准备好Linux下的git,克隆本仓库 + +~~~ +git clone https://github.com/musnows/Valorant-kaiheila-bot.git +~~~ + +如果想使用最新的commit和新功能,请切换到develop分支。 + +保证你的Windows/Linux中`Python`版本高于`3.8`,执行下面的安装库命令 ~~~ pip3 install -r requirements.txt ~~~ + +更新Python的方法可以参考本wiki的 [01-How_to_update_Python](https://github.com/Aewait/Valorant-Kook-Bot/wiki/01-How_to_update_Python) + > Q:为何`khl.py`只需要3.6.8以上版本,而本仓库需要3.8+? > > A:因为valorant的第三方`Python Api`需要3.8+版本 -建议根据[khl.py](https://github.com/TWT233/khl.py)的`example`教程,学习KOOK机器人的基本搭建(很简单的,相信我) - -如果你已经学习完了所有example,那么本仓库的内容对于你来说就是不难的! +如果安装及其缓慢,则可以使用清华镜像源 -我会添加一些只有本仓库中包含的内容的代码解释,方便你来搭建你自己的kook机器人! +``` +pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple +``` -### 1.克隆本仓库 +若无法安装 riot-auth,请前往 [floxay/python-riot-auth](https://github.com/floxay/python-riot-auth) 下载仓库zip,解压后,cd进入目录,执行如下命令 -准备好Linux下的git,克隆本仓库 +``` +pip3 install -e . -i https://pypi.tuna.tsinghua.edu.cn/simple +``` -~~~ -git clone https://github.com/Aewait/Valorant-kaiheila-bot.git -~~~ +![install-riot-auth](https://img.kookapp.cn/assets/2023-02/NSEuH87KrO1y90y8.png) -如果想使用最新的commit和新功能,请切换到develop分支 ### 2.配置config文件 在`code`路径下创建`config`文件夹,并在其中创建`config.json`,将你的`token`写入其中 -~~~json -{ - "token": "bot websocket token", - "img_upload_token": "用来上传图片进行测试的bot websocket token,建议新开bot", - "api_bot_token":"用来上传Api图片的bot websocket token,建议新开bot", - "caiyun":"彩云小译的token", - "master_id":"bot作者的user_id,用于控制只有bot作者才能用的命令", - "debug_ch":"用于发送debug消息的文字频道id" - "img_upload_channel":"用于发送商店自定义背景图测试消息的文字频道id" -} -~~~ +>示例文件参考 [config.exp.json](../code/config/config.exp.json) 总结一下,本仓库需要手动配置的文件有下面两个,其中`color_emoji.json`在仓库中有示例文件 | 文件路径 | 功能 | | --------------------------- | ------------------------------------------------------------ | | code/config/config.json | 存放本机器人的基本配置:[KOOK开发者页面](https://developer.kaiheila.cn/doc/intro) | +| [code/config/color_emoji.json](https://github.com/Aewait/Kook-Valorant-Bot/blob/develop/code/config/color_emoji.json) | 存放`code/main.py`中自动给用户上角色功能相关的emoji以及`msg_id/guild_id` | | ~code/config/valorant.json~ | ~存放拳头开发者api-key:[Roit Develop](https://developer.riotgames.com/)~ 目前需要使用该api的代码都已被删除 | | ~code/config/caiyun.json~ | ~存放彩云小译的api-key:[彩云](https://docs.caiyunapp.com/blog/2018/09/03/lingocloud-api/#python-%E8%B0%83%E7%94%A8)~ 已使用`config.json`代替 | -| [code/config/color_emoji.json](https://github.com/Aewait/Kook-Valorant-Bot/blob/develop/code/config/color_emoji.json) | 存放`code/main.py`中自动给用户上角色功能相关的emoji以及`msg_id/guild_id` | 这些config文件已经被我写入了`.gitignore`,所以在本仓库中你是看不到该文件夹的。这也保证当我们把代码托管到 `gitee/github/gitlab` 等平台上时,自己的`token`不会泄漏。 @@ -65,10 +71,11 @@ git clone https://github.com/Aewait/Valorant-kaiheila-bot.git ### 3.让机器人跑起来! -当你完成上面两步后,即可`cd`进入`code`目录,执行运行命令 +当你完成上面两步后,即可`cd`进入`code`目录,执行运行命令; -~~~ -python3 main.py +~~~bash +python3 start.py #同时运行api和机器人 +python3 main.py #只运行机器人 ~~~ 但是这样,你会发现程序在shell中挂起,且关闭终端后机器人会停止运行 @@ -99,7 +106,7 @@ kill -9 进程号 注意:如果你需要修改代码并测试新功能,请一定要先**终止先前在后台运行的程序**。否则你修改后的代码,即使用 `python3 main.py` 运行,其新功能也是无法生效的! -本仓库code路径下有`makefile`文件,你可以使用`make`快速运行bot,`make ps`快速查找后台运行的程序 +本仓库code路径下有`makefile`文件,你可以使用`make`快速运行bot,`make ps`快速查找后台运行的程序。使用之前,你需要将make中的py3修改为你本地的python命令。 # 起飞! diff --git a/docs/log.example/cookie/.empty b/docs/log.example/cookie/.empty new file mode 100644 index 0000000..e69de29 diff --git a/docs/log.example/img_temp/comp/.empty b/docs/log.example/img_temp/comp/.empty new file mode 100644 index 0000000..e69de29 diff --git a/docs/log.example/img_temp/weapon/.empty b/docs/log.example/img_temp/weapon/.empty new file mode 100644 index 0000000..e69de29 diff --git a/docs/log.example/img_temp_vip/comp/.empty b/docs/log.example/img_temp_vip/comp/.empty new file mode 100644 index 0000000..e69de29 diff --git a/docs/log.example/img_temp_vip/shop/.empty b/docs/log.example/img_temp_vip/shop/.empty new file mode 100644 index 0000000..e69de29 From 1543261ee9de92eeefd8bcbed75f73f6dcfd14db Mon Sep 17 00:00:00 2001 From: musnow Date: Thu, 23 Feb 2023 21:13:31 +0800 Subject: [PATCH 14/30] docs(requirements): update requirements.txt --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bc86238..373313c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ -aiofiles==22.1.0 +aiofiles==23.1.0 aiohttp==3.8.1 khl.py==0.3.7 +leancloud==2.9.12 pandas==1.5.2 Pillow==9.4.0 psutil==5.9.4 From ed3bef7009bf27a93df3b139f9324ad08d277223 Mon Sep 17 00:00:00 2001 From: musnow Date: Fri, 24 Feb 2023 08:50:00 +0800 Subject: [PATCH 15/30] fix(shopRate): fix key err using leancloud --- code/utils/ShopRate.py | 104 ++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/code/utils/ShopRate.py b/code/utils/ShopRate.py index 1eaf94e..426ec68 100644 --- a/code/utils/ShopRate.py +++ b/code/utils/ShopRate.py @@ -13,61 +13,59 @@ async def get_shop_rate(list_shop: dict, kook_user_id: str): """皮肤评分和评价,用户不在err_user里面才显示\n { "sum":rate_avg,"lv":rate_lv,"text_list":rate_text,"count":rate_count } """ - # 初始化相关值 - rate_text = [] - rate_count = 0 - rate_total = 0 - rate_avg = 0 # 平均分 - # leancould中搜索 - query = leancloud.Query('SkinRate') - # 遍历用户的4个商店皮肤 - for skin in list_shop: - # 查找数据库中和skin uuid相同的评价 - query.equal_to('skinUuid',skin) - objlist = query.find() - if len(objlist) > 0: #找到了 - obj = objlist[0] - rate_total += obj.get('rating') # 获取评分 - skin_name = f"「{obj.get('skinName')}」" # 获取皮肤名 - text = f"%-50s\t\t评分: {obj.get('rating')}\n" % skin_name - # 获取皮肤评价的list - cmt = obj.get('comment') - if len(cmt) == 0:# 元素内没有评论,出现错误 - text += f"「随机评论」 NO CMT ERR\n" - else:# 有评论,生成随机数 - ran = random.randint(0, len(cmt) - 1) - text += f"「随机评论」 {cmt[ran]}\n" - # 插入text - rate_text.append(text) - - rate_lv = "皮肤评价数据仍待收集…" - rate_count = len(rate_text) # 直接计算长度 - if rate_count > 0: - rate_avg = rate_total // rate_count - #记录当日冠军和屌丝 - if rate_avg > SkinRateDict["cmp"]["best"]["pit"]: - SkinRateDict["cmp"]["best"]["pit"] = rate_avg - SkinRateDict["cmp"]["best"]["skin"] = list_shop - SkinRateDict["cmp"]["best"]["kook_id"] = kook_user_id - print(f"[shop] update rate-best Au:{kook_user_id} = {rate_avg}") - elif rate_avg < SkinRateDict["cmp"]["worse"]["pit"]: - SkinRateDict["cmp"]["worse"]["pit"] = rate_avg - SkinRateDict["cmp"]["worse"]["skin"] = list_shop - SkinRateDict["cmp"]["worse"]["kook_id"] = kook_user_id - print(f"[shop] update rate-worse Au:{kook_user_id} = {rate_avg}") + try: + # 初始化相关值 + rate_text = [] + rate_total = 0 + rate_avg = 0 # 平均分 + # leancould中搜索,用户评分列表 + query = leancloud.Query('UserRate') + # 遍历用户的4个商店皮肤 + for skin in list_shop: + # 查找数据库中和skinUuid相同的评价 + query.equal_to('skinUuid',skin) + objlist = query.find() + if len(objlist) > 0: # 找到了评论,生成随机数 + ran = random.randint(0, len(objlist) - 1) + obj = objlist[ran] # 随机获取一个对象 + rate_total += obj.get('rating') # 获取评分 + skin_name = f"「{obj.get('skinName')}」" # 获取皮肤名 + text = f"%-50s\t\t评分: {obj.get('rating')}\n" % skin_name + text += f"「随机评论」 {obj.get('comment')}\n" + # 插入text + rate_text.append(text) + + rate_lv = "皮肤评价数据仍待收集…" + rate_count = len(rate_text) # 直接计算长度 + if rate_count > 0: + rate_avg = rate_total // rate_count + #记录当日冠军和屌丝 + if rate_avg > SkinRateDict["cmp"]["best"]["pit"]: + SkinRateDict["cmp"]["best"]["pit"] = rate_avg + SkinRateDict["cmp"]["best"]["skin"] = list_shop + SkinRateDict["cmp"]["best"]["kook_id"] = kook_user_id + print(f"[shop] update rate-best Au:{kook_user_id} = {rate_avg}") + elif rate_avg < SkinRateDict["cmp"]["worse"]["pit"]: + SkinRateDict["cmp"]["worse"]["pit"] = rate_avg + SkinRateDict["cmp"]["worse"]["skin"] = list_shop + SkinRateDict["cmp"]["worse"]["kook_id"] = kook_user_id + print(f"[shop] update rate-worse Au:{kook_user_id} = {rate_avg}") - if rate_avg >= 0 and rate_avg <= 20: - rate_lv = "丐帮帮主" - elif rate_avg > 20 and rate_avg <= 40: - rate_lv = "省钱能手" - elif rate_avg > 40 and rate_avg <= 60: - rate_lv = "差强人意" - elif rate_avg > 60 and rate_avg <= 80: - rate_lv = "芜湖起飞" - elif rate_avg > 80 and rate_avg <= 100: - rate_lv = "天选之人" + if rate_avg >= 0 and rate_avg <= 20: + rate_lv = "丐帮帮主" + elif rate_avg > 20 and rate_avg <= 40: + rate_lv = "省钱能手" + elif rate_avg > 40 and rate_avg <= 60: + rate_lv = "差强人意" + elif rate_avg > 60 and rate_avg <= 80: + rate_lv = "芜湖起飞" + elif rate_avg > 80 and rate_avg <= 100: + rate_lv = "天选之人" - return { "sum":rate_avg,"lv":rate_lv,"text_list":rate_text,"count":rate_count } + return { "sum":rate_avg,"lv":rate_lv,"text_list":rate_text,"count":rate_count } + except Exception as result: + print(f"ERR! [get_shop_rate]\n{traceback.format_exc()}") + return { "sum":0,"lv":"皮肤评价数据仍待收集…","text_list":[],"count":0 } # 获取皮肤评价的卡片 async def get_shop_rate_cm(list_shop: dict, kook_user_id: str, cm: CardMessage): From 3650d4f354d742b136812f73667bfd7c33e0d828 Mon Sep 17 00:00:00 2001 From: musnow Date: Fri, 24 Feb 2023 09:12:22 +0800 Subject: [PATCH 16/30] docs(leancloud): add leancluod docs --- docs/leancloud.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 docs/leancloud.md diff --git a/docs/leancloud.md b/docs/leancloud.md new file mode 100644 index 0000000..8a2fd6f --- /dev/null +++ b/docs/leancloud.md @@ -0,0 +1,58 @@ +## 介绍 + +在一位社区开发者的建议下,再加上本身就有在 kook-valorant-bot和 qq-valorant-bot 之间同步皮肤评论数据库的需求,在此设立一个valorant国内社区的公共皮肤评论数据库! + +您可以选择将您的项目接入数据库;也可只读数据库,获取公共皮肤评论! + +>数据库基于leancloud的结构化数据存储。leancloud提供了多语言sdk,参考 [官方文档](https://docs.leancloud.cn/sdk/storage/overview/) + +所有皮肤uuid都需要统一为拳头商店返回值的皮肤uuid,即 [valorant-api.com/v1/weapons/skins](https://valorant-api.com/v1/weapons/skins) 皮肤数据列表中的 `["level"][0]["uuid"]` ; + +皮肤评分采用**100分制**。 + +---- + +目前设置了3个class,字段如下: + +## 1.SkinRate + +皮肤评分数据 + +| 字段 | 类型 | 含义 | 是否必填 | +| --- | --- | --- | ------- | +| rating | NUMBER | 该皮肤的平均分 | 是 | +| skinUuid | STRING | 皮肤 uuid | 是 | +| skinName | STRING | 皮肤名字(繁体中文) | 是 | + +更新皮肤评分时,直接和原有rating加起来计算平均数即可;为保险起见,请先搜索数据库中是否已有该皮肤uuid,避免出现冗余键值。 + +## 2.UserRate + +用户发送的皮肤评论 + +| 字段 | 类型 | 含义 | 是否必填 | +| --- | --- | --- | ------- | +| rating | NUMBER | 皮肤评论的用户评分 | 是 | +| skinUuid | STRING | 皮肤 uuid | 是 | +| skinName | STRING | 皮肤名字(繁体中文) | 是 | +| comment | STRING | 用户皮肤评论 | 是 | +| platform | STRING | 平台 | 是 | +| rateAt | NUMBER | 用户评论时间戳 | 是 | +| userId | STRING | 用户平台id | 否 | +| msgId | STRING | 评论消息id | 否 | + +* 若平台有用户id和消息id(如kook、qq频道),则需要设置; +* 强烈建议设置用户评论时间戳(秒级)方便后续查看日志; +* `platform` 字段指代该评论来自什么平台,比如kook和qq频道;若您是小程序/app开发者,请给您的平台起一个名字,并将来自您平台的所有用户评价的`platform` 字段设置成该名字。 + +## 3.ShopCmp + +昨日商店最高分/最低分,请修改原有object(先搜索获取到原有obj,在其基础上修改),不要新建object + +| 字段 | 类型 | 含义 | 是否必填 | +| --- | --- | --- | ------- | +| best | BOOLEAN | 当日最佳True/最差False | 是 | +| rating | NUMBER | 商店4个皮肤的平均分 | 是 | +| skinList | ARRAY | 商店4个皮肤 uuid | 是 | +| platform | STRING | 平台 | 是 | +| userId | STRING | 用户平台id | 否 | \ No newline at end of file From 06aa63e3e551564ced60bc9be36967fd8d213b28 Mon Sep 17 00:00:00 2001 From: musnow Date: Fri, 24 Feb 2023 13:17:01 +0800 Subject: [PATCH 17/30] feat(ApiToken): update ApiToken rate check --- code/utils/api/ApiHandler.py | 31 ++-------------- code/utils/api/ApiToken.py | 68 ++++++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 40 deletions(-) diff --git a/code/utils/api/ApiHandler.py b/code/utils/api/ApiHandler.py index e2f328c..bb196c3 100644 --- a/code/utils/api/ApiHandler.py +++ b/code/utils/api/ApiHandler.py @@ -5,14 +5,13 @@ # import requests # import asyncio from utils.valorant.EzAuth import EzAuthExp, auth2fa, auth2faWait, Get2faWait_Key, User2faCode -from utils.api.ApiToken import token_ck, ApiTokenDict, save_token_files +from utils.api.ApiToken import check_token_rate from utils.Gtime import GetTime from utils.KookApi import kook_create_asset from utils.valorant.Val import fetch_daily_shop, fetch_vp_rp_dict from utils.ShopImg import get_shop_img_11, get_shop_img_169 -TOKEN_RATE_LIMITED = 10 -# bot的token文件 +# bot的配置文件 from utils.FileManage import config # 用来给kook上传文件的bot token api_bot_token = config['token']['api_bot_token'] @@ -45,32 +44,6 @@ # # 上传失败 # return {'code':200,'data':ret['data'],'message':ret['message']} -# 检测速率(一分钟只允许10次) -async def check_token_rate(token: str): - ret = await token_ck(token) - if ret: - cur_time = time.time() - time_diff = cur_time - ApiTokenDict['data'][token]['rate_time'] - ApiTokenDict['data'][token]['sum'] += 1 - if ApiTokenDict['data'][token]['rate_nums'] == 0: #初次使用 - ApiTokenDict['data'][token]['rate_time'] = cur_time - ApiTokenDict['data'][token]['rate_nums'] = 1 - save_token_files("token init use") - return {'status': True, 'message': 'first use', 'info': '一切正常'} - elif time_diff <= 60: #时间在60s以内 - if ApiTokenDict['data'][token]['rate_nums'] > TOKEN_RATE_LIMITED: - return {'status': False, 'message': 'token rate limited!', 'info': '速率限制,请稍后再试'} - else: #没有引发速率限制 - ApiTokenDict['data'][token]['rate_nums'] += 1 - return {'status': True, 'message': 'time_diff <= 60, in rate', 'info': '一切正常'} - else: #时间超过60 - save_token_files("rate check") - ApiTokenDict['data'][token]['rate_time'] = cur_time - ApiTokenDict['data'][token]['rate_nums'] = 0 - return {'status': True, 'message': 'time_diff > 60', 'info': '一切正常'} - else: - return {'status': False, 'message': 'token not in dict', 'info': '无效token'} - # 基本画图操作 async def base_img_request(params, list_shop, vp1=0, rp1=0): diff --git a/code/utils/api/ApiToken.py b/code/utils/api/ApiToken.py index ee6d126..8e07758 100644 --- a/code/utils/api/ApiToken.py +++ b/code/utils/api/ApiToken.py @@ -5,7 +5,9 @@ # 所有token from utils.FileManage import ApiTokenDict - +# token速率限制为10,检测周期为60s +TOKEN_RATE_LIMITED = 10 +TOKEN_RATE_TIME = 60 #获取uuid def get_uuid(): @@ -27,6 +29,15 @@ def create_token_uuid(num: int = 10, day: int = 30): Returns: str: text for uuid + + Keys: + - days: token expiration date + - prime: is token public? False(public)/True(private) + - od_time: token overdue timestamp (init in first use) + - last_uesed: token last_used time + - rate_time: token rate_limit start time + - rate_nums: token rate_limit count (use-count in 60s) + - sum: token post/get count """ global ApiTokenDict i = num @@ -35,19 +46,17 @@ def create_token_uuid(num: int = 10, day: int = 30): uuid = str(get_uuid()) ApiTokenDict['data'][uuid] = { 'days': day, - 'prime': False, - 'od_time': time.time() + day * 86400, + 'prime': True, + 'od_time': 0, 'last_used': time.time(), 'rate_time': time.time(), 'rate_nums': 0, 'sum': 0 } - if day > 3000: #永久会员 - ApiTokenDict['data'][uuid]['prime'] = True NewUuid.append(uuid) i -= 1 - # 更新uuid + # 更新uuid到文件 save_token_files("token create") text = "" @@ -65,24 +74,61 @@ async def token_ck(token: str): * True: is token * False: not token """ - # 检查 global ApiTokenDict if token in ApiTokenDict['data']: - #用户的token是否过期? + # 判断token是否为第一次使用(是的话,根据days更新过期时间) + if ApiTokenDict['data'][token]['od_time'] == 0: + od_time = time.time() + ApiTokenDict['data'][token]['days'] * 86400 + ApiTokenDict['data'][token]['od_time'] = od_time + # 用户的token是否过期? if time.time() > ApiTokenDict['data'][token]['od_time']: + # 过期了,删除该token del ApiTokenDict['data'][token] - # 更新uuid + # 更新本地文件 save_token_files("token expire") print(f"[{GetTime()}] [token-ck] T:{token} out of date") return False - else: #没有过期,返回真 + else: # 没有过期,返回真 print(f"[{GetTime()}] [token-ck] T:{token} is token") return True - else: #token不在 + else: # token不在 print(f"[{GetTime()}] [token-ck] T:{token} not token") return False +# 检测速率(一分钟只允许10次) +async def check_token_rate(token: str): + global ApiTokenDict + ret = await token_ck(token) # 判断token合法性 + if ret: + cur_time = time.time() + # 当前时间和rate计数开始时间的差值 + time_diff = cur_time - ApiTokenDict['data'][token]['rate_time'] + # token的总使用次数+1 + ApiTokenDict['data'][token]['sum'] += 1 + # rate_nums为0,代表是当前检测周期的初次使用 + if ApiTokenDict['data'][token]['rate_nums'] == 0: + ApiTokenDict['data'][token]['rate_time'] = cur_time + ApiTokenDict['data'][token]['rate_nums'] = 1 + save_token_files("token init use") + return {'status': True, 'message': 'first use', 'info': '一切正常'} + # rate_nums不为0,代表检测周期内有调用 + elif time_diff <= TOKEN_RATE_TIME: # 和上次调用的时间差在60s以内 + # 调用的次数超过10次,触发速率限制 + if ApiTokenDict['data'][token]['rate_nums'] > TOKEN_RATE_LIMITED: + return {'status': False, 'message': 'token rate limited!', 'info': '速率限制,请稍后再试'} + else: # 没有引发速率限制 + ApiTokenDict['data'][token]['rate_nums'] += 1 + return {'status': True, 'message': 'time_diff <= 60, in rate', 'info': '一切正常'} + else: # 时间超过60s + save_token_files("rate check") + # 重置rate_time为当前时间,重置计数器(开始新一轮速率检测) + ApiTokenDict['data'][token]['rate_time'] = cur_time + ApiTokenDict['data'][token]['rate_nums'] = 0 + return {'status': True, 'message': 'time_diff > 60', 'info': '一切正常'} + else: # 压根不是一个合法的token + return {'status': False, 'message': 'token not in dict', 'info': '无效token'} + # 在此处手动添加token # text = create_token_uuid(1,5) # print(text) \ No newline at end of file From 2b3c3ed53308320ef457127fe66c9b2cbd7b540d Mon Sep 17 00:00:00 2001 From: musnow Date: Fri, 24 Feb 2023 13:25:31 +0800 Subject: [PATCH 18/30] feat(main): add local cookie file remove in /logout refactor(main): rename login,tfa,logout def --- code/main.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/code/main.py b/code/main.py index ee4faf0..3934236 100644 --- a/code/main.py +++ b/code/main.py @@ -915,7 +915,7 @@ async def check_UserAuthDict_len(msg: Message): # 登录,保存用户的token @bot.command(name='login') -async def login_authtoken(msg: Message, user: str = 'err', passwd: str = 'err', apSave='', *arg): +async def login(msg: Message, user: str = 'err', passwd: str = 'err', apSave='', *arg): print(f"[{GetTime()}] Au:{msg.author_id}_{msg.author.username}#{msg.author.identify_num} = /login {apSave}") log_bot_user(msg.author_id) #这个操作只是用来记录用户和cmd总数的 global Login_Forbidden, login_rate_limit, UserTokenDict, UserAuthDict @@ -1029,7 +1029,7 @@ async def login_authtoken(msg: Message, user: str = 'err', passwd: str = 'err', @bot.command(name='tfa') -async def auth_2fa(msg: Message, key: str, tfa: str, *arg): +async def tfa_verify(msg: Message, key: str, tfa: str, *arg): print(f"[{GetTime()}] Au:{msg.author_id}_{msg.author.username}#{msg.author.identify_num} = /2fa") if len(tfa) != 6: await msg.reply(f"邮箱验证码长度错误,请确认您输入了正确的6位验证码\n当前参数:{tfa}") @@ -1051,7 +1051,7 @@ async def auth_2fa(msg: Message, key: str, tfa: str, *arg): # 退出登录 @bot.command(name='logout') -async def logout_authtoken(msg: Message, *arg): +async def logout(msg: Message, *arg): logging(msg) try: global UserTokenDict, UserAuthDict @@ -1060,14 +1060,21 @@ async def logout_authtoken(msg: Message, *arg): await msg.reply(cm) return - #如果id存在, 删除id - del UserAuthDict[msg.author_id] #先删除auth对象 + log_text = f"[Logout] Au:{msg.author_id} - {UserTokenDict[msg.author_id]['GameName']}#{UserTokenDict[msg.author_id]['TagLine']}" + # 如果id存在,删除auth对象 + # 因为UserTokenDict里面只存放了用户游戏名/uuid,且不作为是否登录的判断,所以不需要删除 + del UserAuthDict[msg.author_id] + # 如果是vip用户,删除本地保存的cookie + cookie_path = f"./log/cookie/{msg.author_id}.cke" + # 判断路径是否存在,存在直接删除 + if os.path.exists(cookie_path): + os.remove(cookie_path) # 删除文件 + log_text+= " - rm cookie file" + text = f"已退出登录!下次再见,{UserTokenDict[msg.author_id]['GameName']}#{UserTokenDict[msg.author_id]['TagLine']}" cm = await get_card(text, "你会回来的,对吗?", icon_cm.crying_crab) await msg.reply(cm) - print( - f"[Logout] Au:{msg.author_id} - {UserTokenDict[msg.author_id]['GameName']}#{UserTokenDict[msg.author_id]['TagLine']}" - ) + print(log_text) except Exception as result: # 其他错误 await BaseException_Handler("logout", traceback.format_exc(), msg, bot) From 6f04f597ff2a6e1d5e9ca1d29dbf75cb2216fc46 Mon Sep 17 00:00:00 2001 From: musnow Date: Fri, 24 Feb 2023 13:45:57 +0800 Subject: [PATCH 19/30] feat(main): add params of NO for killbot --- code/main.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/main.py b/code/main.py index 3934236..bb0c128 100644 --- a/code/main.py +++ b/code/main.py @@ -77,15 +77,17 @@ async def Save_File_Task(): @bot.command(name='kill') -async def KillBot(msg: Message, *arg): +async def KillBot(msg: Message,num:str, *arg): logging(msg) - if msg.author_id == master_id: + if msg.author_id == master_id and int(num)==config['no']: # 保存所有文件 await Save_All_File(False) await msg.reply(f"[KILL] 保存全局变量成功,bot下线") res = await bot_offline() # 调用接口下线bot print(f"[KILL] [{GetTime()}] bot-off: {res}\n") os._exit(0) # 退出程序 + else: + await msg.reply(f"您没有权限或参数错误!\n本Bot编号为:{config['no']}") ########################################################################################## From ae4bbab7ecc4172b8dde2d67c35b08be6b6a8d98 Mon Sep 17 00:00:00 2001 From: musnow Date: Fri, 24 Feb 2023 13:45:57 +0800 Subject: [PATCH 20/30] feat(main): add params of NO for killbot docs(config): update config.exp.json --- code/config/config.exp.json | 3 ++- code/main.py | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/code/config/config.exp.json b/code/config/config.exp.json index 88b7e10..423caac 100644 --- a/code/config/config.exp.json +++ b/code/config/config.exp.json @@ -17,5 +17,6 @@ "leancloud":{ "appid":"leancloud数据库的appid(可联系作者,共用皮肤评价数据库)", "master_key":"leancloud数据库的masterkey" - } + }, + "no":1000 } \ No newline at end of file diff --git a/code/main.py b/code/main.py index 3934236..bb0c128 100644 --- a/code/main.py +++ b/code/main.py @@ -77,15 +77,17 @@ async def Save_File_Task(): @bot.command(name='kill') -async def KillBot(msg: Message, *arg): +async def KillBot(msg: Message,num:str, *arg): logging(msg) - if msg.author_id == master_id: + if msg.author_id == master_id and int(num)==config['no']: # 保存所有文件 await Save_All_File(False) await msg.reply(f"[KILL] 保存全局变量成功,bot下线") res = await bot_offline() # 调用接口下线bot print(f"[KILL] [{GetTime()}] bot-off: {res}\n") os._exit(0) # 退出程序 + else: + await msg.reply(f"您没有权限或参数错误!\n本Bot编号为:{config['no']}") ########################################################################################## From 1aef4d0fb0d3776d0e015fe406873aceaa5b2900 Mon Sep 17 00:00:00 2001 From: musnows Date: Fri, 24 Feb 2023 08:49:22 +0000 Subject: [PATCH 21/30] feat(ShopRate): add def to get shoprate from leancloud --- code/main.py | 55 ++++++++--------------------- code/utils/ShopRate.py | 80 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 93 insertions(+), 42 deletions(-) diff --git a/code/main.py b/code/main.py index bb0c128..32c9957 100644 --- a/code/main.py +++ b/code/main.py @@ -19,6 +19,7 @@ from PIL import Image, UnidentifiedImageError # 用于合成图片 from riot_auth import RiotAuth, auth_exceptions +from utils import ShopRate from utils.Help import help_main, help_val, help_develop from utils.BotLog import logging, log_bot_list, log_bot_user, log_bot_list_text, APIRequestFailed_Handler, BaseException_Handler,get_proc_info from utils.Other import weather @@ -31,7 +32,6 @@ from utils.BotVip import (VipUserDict, create_vip_uuid, fetch_vip_user, roll_vip_start, using_vip_uuid, vip_ck, vip_time_remain, vip_time_remain_cm, vip_time_stamp,get_vip_shop_bg_cm,replace_illegal_img,illegal_img_169) from utils.Translate import ListTL, translate_main, Shutdown_TL, checkTL, Open_TL, Close_TL -from utils.ShopRate import SkinRateDict, get_shop_rate_cm, check_shop_rate,update_shop_cmp from utils.ShopImg import get_shop_img_11, get_shop_img_169, img_requestor from utils.valorant.ValFileUpd import update_bundle_url, update_price, update_skins @@ -864,7 +864,7 @@ async def vip_time_add(msg: Message, vday: int = 1, *arg): ##################################################################################### # 预加载用户的riot游戏id和玩家uuid(登录后Api获取) -from utils.FileManage import UserTokenDict,SkinNotifyDict,EmojiDict +from utils.FileManage import UserTokenDict,SkinNotifyDict,EmojiDict,SkinRateDict # 用来存放auth对象(无法直接保存到文件) UserAuthDict = {'AP': {}} @@ -1325,7 +1325,7 @@ async def get_daily_shop(msg: Message, *arg): #皮肤评分和评价,用户不在rate_err_user里面才显示(在评论中发表违规言论的用户) if not check_rate_err_user(msg.author_id): - cm = await get_shop_rate_cm(list_shop, msg.author_id, cm=cm) + cm = await ShopRate.get_shop_rate_cm(list_shop, msg.author_id, cm=cm) end = time.perf_counter() #计算获取评分的时间 # 更新消息 await upd_card(send_msg['msg_id'], cm, channel_type=msg.channel_type) @@ -1653,51 +1653,26 @@ async def rate_skin_add(msg: Message, *arg): return try: name = " ".join(arg) - name = zhconv.convert(name, 'zh-tw') #将名字繁体化 - sklist = fetch_skin_list_byname(name) - if sklist == []: #空list代表这个皮肤不在里面 - await msg.reply(f"该皮肤不在列表中,请重新查询!") - return - - retlist = list() #用于返回的list,因为不是所有搜到的皮肤都有价格,没有价格的皮肤就是商店不刷的 - for s in sklist: - res_price = fetch_item_price_bylist(s['lv_uuid']) - if res_price != None: # 有可能出现返回值里面找不到这个皮肤的价格的情况,比如冠军套 - price = res_price['Cost']['85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741'] - data = {'skin': s, 'price': price} - retlist.append(data) - - if retlist == []: #空list代表这个皮肤没有价格 - await msg.reply(f"该皮肤不在列表中 [没有价格],请重新查询!") + retlist = await ShopRate.get_available_skinlist(name) + if retlist == []: # 空list,有问题 + await msg.reply(f"该皮肤不在列表中[或没有价格],请重新查询!") return + # 将皮肤list插入到选择列表中,用户使用/rts命令选择 UserRtsDict[msg.author_id] = retlist - sum = 0 - usrin = msg.author_id in SkinRateDict['data'] - if usrin: - sum = len(SkinRateDict['data'][msg.author_id]) - i = 0 - text = "```\n" #模拟一个选择表 - for w in retlist: - text += f"[{i}] - {w['skin']['displayName']} - VP {w['price']}" - i += 1 - if usrin and w['skin']['lv_uuid'] in SkinRateDict['data'][msg.author_id]: - text += " √\n" - elif w['skin']['lv_uuid'] in SkinRateDict['rate']: - text += " +\n" - else: - text += " -\n" + # 获取选择列表的text + ret = await ShopRate.get_skinlist_rate_text(retlist,msg.author_id) + text = f"```\n{ret['text']}```" - text += "```" cm = CardMessage() c = Card(Module.Header(f"查询到 {name} 相关皮肤如下"), Module.Section(Element.Text(text, Types.Text.KMD)), - Module.Section(Element.Text("请使用以下命令对皮肤进行评分; √代表您已评价过该皮肤,+代表已有玩家评价,-代表无人评价\n", Types.Text.KMD))) + Module.Section(Element.Text("请使用以下命令对皮肤进行评分;\n√代表您已评价过该皮肤,+已有玩家评价,-无人评价\n", Types.Text.KMD))) text1 = "```\n/rts 序号 评分 吐槽\n" text1 += "序号:上面列表中的皮肤序号\n" text1 += "评分:给皮肤打分,范围0~100\n" text1 += "吐槽:说说你对这个皮肤的看法\n" text1 += "吐槽的时候请注意文明用语!\n```\n" - text1 += f"您已经评价过了 {sum} 个皮肤" + text1 += f"您已经评价过了 {ret['sum']} 个皮肤" c.append(Module.Section(Element.Text(text1, Types.Text.KMD))) cm.append(c) await msg.reply(cm) @@ -2030,7 +2005,7 @@ async def auto_skin_notify(): SkinRateDict["cmp"]["best"]["pit"] = 0 SkinRateDict["cmp"]["worse"]["skin"] = list() SkinRateDict["cmp"]["worse"]["pit"] = 100 - update_shop_cmp() # 更新数据库中的记录 + ShopRate.update_shop_cmp() # 更新数据库中的记录 print("[BOT.TASK.NOTIFY] SkinRateDict/UserShopDict clear, sleep(10)") #睡10s再开始遍历(避免时间不准) await asyncio.sleep(10) @@ -2066,7 +2041,7 @@ async def auto_skin_notify(): timeout = resp["SkinsPanelLayout"]["SingleItemOffersRemainingDurationInSeconds"] #剩余时间 timeout = time.strftime("%H:%M:%S", time.gmtime(timeout)) #将秒数转为标准时间 log_time = f"[Api_shop] {format(time.time()-a_time,'.4f')} " - await check_shop_rate(vip, list_shop) #计算用户商店得分 + await ShopRate.check_shop_rate(vip, list_shop) #计算用户商店得分 #vip用户会提前缓存当日商店,需要设置uuid来保证是同一个游戏用户 UserShopDict[vip] = {} UserShopDict[vip]["auth_user_id"] = UserTokenDict[vip]["auth_user_id"] @@ -2165,7 +2140,7 @@ async def auto_skin_notify(): else: resp = await fetch_daily_shop(userdict) # 获取每日商店 list_shop = resp["SkinsPanelLayout"]["SingleItemOffers"] # 商店刷出来的4把枪 - await check_shop_rate(vip, list_shop) #计算非vip用户商店得分 + await ShopRate.check_shop_rate(vip, list_shop) #计算非vip用户商店得分 # 然后再遍历列表查看是否有提醒皮肤 # 关于下面这一行参考 https://img.kookapp.cn/assets/2022-08/oYbf8PM6Z70ae04s.png diff --git a/code/utils/ShopRate.py b/code/utils/ShopRate.py index 426ec68..c036407 100644 --- a/code/utils/ShopRate.py +++ b/code/utils/ShopRate.py @@ -1,10 +1,11 @@ -import json +import zhconv import random import leancloud import traceback from khl.card import Card, CardMessage, Module, Element, Types # 皮肤的评价 +from utils.valorant import Val from utils.FileManage import config,SkinRateDict leancloud.init(config["leancloud"]["appid"], master_key=config["leancloud"]["master_key"]) @@ -149,4 +150,79 @@ def update_shop_cmp(): i.save() print(f"[update_shop_cmp] saving best:{i.get('best')}") except: - print(f"ERR! [update_shop_cmp]\n{traceback.format_exc()}") \ No newline at end of file + print(f"ERR! [update_shop_cmp]\n{traceback.format_exc()}") + +# 通过名字查找可以购买的皮肤,并返回一个list +async def get_available_skinlist(name:str): + """name must be zh-CN + Return: [{'skin': [{'displayName': skin['displayName'], 'lv_uuid': skin['levels'][0]['uuid']}], 'price': price}] + """ + name = zhconv.convert(name, 'zh-tw') #将名字繁体化 + sklist = Val.fetch_skin_list_byname(name) + if sklist == []: #空list代表这个皮肤不在里面 + return [] + + retlist = list() # 用于返回的list + for s in sklist: + # 查找皮肤价格 + # 因为不是所有搜到的皮肤都有价格,没有价格的皮肤就是商店不刷的 + res_price = Val.fetch_item_price_bylist(s['lv_uuid']) + if res_price != None: # 有可能出现返回值里面找不到这个皮肤的价格的情况,比如冠军套 + price = res_price['Cost']['85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741'] + data = {'skin': s, 'price': price} + retlist.append(data) + + return retlist + +# 获取可以购买皮肤的相关信息 +async def query_UserCmt(user_id:str): + """Return + A list containing the skin evaluated by the user, + """ + query = leancloud.Query('UserCmt') + query.equal_to('platform', 'kook') + query.equal_to('userId', user_id) # 查找usercmt中有没有该用户 + objlist = query.find() + print(objlist) + if len(objlist) > 0 : # 存在 + # 获取这个用户评价过的皮肤列表 + return objlist[0].get('skinList') + else: # 不存在 + return [] + +# 获取可以购买皮肤的相关信息的text +async def get_skinlist_rate_text(skinlist:list,user_id:str): + """Args: + - skinlist: return of get_available_skinlist() + - user_id: kook user_id + + Return { "text":text,"sum":len(get_skinlist_rate_text)}\n + - text: rating info of the skin in list\n + - sum: the total skin rating by user\n + `√ rate by user_id`,`+ rate by other_user`,`- no one rate` + """ + # 获取该用户已评价的皮肤列表 + userCmtList = await query_UserCmt(user_id) + print(userCmtList) + i=0 + query = leancloud.Query('UserRate') + query.equal_to('platform', 'kook') + text = "" # 模拟一个选择表 + for w in skinlist: + # 先插入皮肤名字和皮肤价格 + text += f"[{i}] - {w['skin']['displayName']} - VP {w['price']}" + # 情况1,当前用户评价过这个皮肤 + if w['skin']['lv_uuid'] in userCmtList: + text += " √\n" + else: + query.equal_to('skinUuid', w['skin']['lv_uuid']) + objlist = query.find() + # 情况2,其他用户评价过这个皮肤 + if len(objlist)>0: # 数据库中找到了其他用户的评价 + text += " +\n" + else: # 情况3,无人问津 + text += " -\n" + # 标号+1 + i += 1 + # 返回结果 + return { "text":text,"sum":len(userCmtList)} \ No newline at end of file From fba7b24cad1a606a0d83e211e737fd9eac24e3aa Mon Sep 17 00:00:00 2001 From: musnows Date: Fri, 24 Feb 2023 11:20:42 +0000 Subject: [PATCH 22/30] feat(ShopRate): add def to modify db in leancloud --- code/utils/ShopRate.py | 128 +++++++++++++++++++++++++++++++---------- 1 file changed, 99 insertions(+), 29 deletions(-) diff --git a/code/utils/ShopRate.py b/code/utils/ShopRate.py index c036407..1e4b3ba 100644 --- a/code/utils/ShopRate.py +++ b/code/utils/ShopRate.py @@ -113,16 +113,38 @@ async def check_shop_rate(kook_user_id: str, list_shop: list): else: return False -# 每日早八,更新leancloud中的ShopCmp(被kill掉的时候也执行此函数) -def update_shop_cmp(): +# 通过名字查找可以购买的皮肤,并返回一个list +async def get_available_skinlist(name:str): + """name must be zh-CN + Return: [{'skin': [{'displayName': skin['displayName'], 'lv_uuid': skin['levels'][0]['uuid']}], 'price': price}] + """ + name = zhconv.convert(name, 'zh-tw') #将名字繁体化 + sklist = Val.fetch_skin_list_byname(name) + if sklist == []: #空list代表这个皮肤不在里面 + return [] + + retlist = list() # 用于返回的list + for s in sklist: + # 查找皮肤价格 + # 因为不是所有搜到的皮肤都有价格,没有价格的皮肤就是商店不刷的 + res_price = Val.fetch_item_price_bylist(s['lv_uuid']) + if res_price != None: # 有可能出现返回值里面找不到这个皮肤的价格的情况,比如冠军套 + price = res_price['Cost']['85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741'] + data = {'skin': s, 'price': price} + retlist.append(data) + + return retlist + +# 每日早八,更新leancloud中的ShopCmp +async def update_ShopCmp(): """update shop rate in leancloud """ try: # 获取对象 ShopCmp = leancloud.Object.extend('ShopCmp') query = ShopCmp.query - # 只更新昨日的 - query.equal_to('today',False) + # 获取到两个已有值 + query.exists('rating') # 通过键值是否存在,进行查找 objlist = query.find() if len(objlist) == 0: raise Exception("leancloud find today err!") @@ -152,28 +174,6 @@ def update_shop_cmp(): except: print(f"ERR! [update_shop_cmp]\n{traceback.format_exc()}") -# 通过名字查找可以购买的皮肤,并返回一个list -async def get_available_skinlist(name:str): - """name must be zh-CN - Return: [{'skin': [{'displayName': skin['displayName'], 'lv_uuid': skin['levels'][0]['uuid']}], 'price': price}] - """ - name = zhconv.convert(name, 'zh-tw') #将名字繁体化 - sklist = Val.fetch_skin_list_byname(name) - if sklist == []: #空list代表这个皮肤不在里面 - return [] - - retlist = list() # 用于返回的list - for s in sklist: - # 查找皮肤价格 - # 因为不是所有搜到的皮肤都有价格,没有价格的皮肤就是商店不刷的 - res_price = Val.fetch_item_price_bylist(s['lv_uuid']) - if res_price != None: # 有可能出现返回值里面找不到这个皮肤的价格的情况,比如冠军套 - price = res_price['Cost']['85ad13f7-3d1b-5128-9eb2-7cd8ee0b5741'] - data = {'skin': s, 'price': price} - retlist.append(data) - - return retlist - # 获取可以购买皮肤的相关信息 async def query_UserCmt(user_id:str): """Return @@ -183,7 +183,6 @@ async def query_UserCmt(user_id:str): query.equal_to('platform', 'kook') query.equal_to('userId', user_id) # 查找usercmt中有没有该用户 objlist = query.find() - print(objlist) if len(objlist) > 0 : # 存在 # 获取这个用户评价过的皮肤列表 return objlist[0].get('skinList') @@ -203,7 +202,6 @@ async def get_skinlist_rate_text(skinlist:list,user_id:str): """ # 获取该用户已评价的皮肤列表 userCmtList = await query_UserCmt(user_id) - print(userCmtList) i=0 query = leancloud.Query('UserRate') query.equal_to('platform', 'kook') @@ -225,4 +223,76 @@ async def get_skinlist_rate_text(skinlist:list,user_id:str): # 标号+1 i += 1 # 返回结果 - return { "text":text,"sum":len(userCmtList)} \ No newline at end of file + return { "text":text,"sum":len(userCmtList)} + +# 获取一个皮肤的评分信息 +async def query_SkinRate(skin_uuid:str): + """return: { + "status":True/False, + "skin_uuid":objlist[0].get('skinUuid'), + "skin_name":objlist[0].get('skinName'), + "rating":objlist[0].get('rating') + } + """ + query = leancloud.Query('SkinRate') + query.equal_to('skinUuid', skin_uuid) + objlist = query.find() + ret = {"status":False} + if len(objlist) > 0: # 找到了 + ret = { + "status":True, + "skin_uuid":objlist[0].get('skinUuid'), + "skin_name":objlist[0].get('skinName'), + "rating":objlist[0].get('rating') + } + + return ret + +# 更新数据库中的评价 +async def update_UserRate(skin_uuid:str,rate_info:dict,user_id:str): + """Args: + - rate_info:{ + "name": skin_name, + "cmt": user comment, + "pit": user rating, + "time": time stamp, + "msg_id: message id + } + """ + UserRate = leancloud.Object.extend('UserRate') + query = UserRate.query + # 先查找是否有userid和用户id都相同的obj(如有,直接更新评论、评分、时间) + query.equal_to('userId', user_id) + query.equal_to('skinUuid', skin_uuid) + objlist = query.find() + if len(objlist)>0: # 有,更新 + obj = objlist[0] + else: # 没有,新建 + obj = UserRate() + obj.set('skinUuid',skin_uuid) + obj.set('skinName',rate_info['name']) + obj.set('platform','kook') + obj.set('userId',user_id) + + obj.set('comment',rate_info['cmt']) + obj.set('rating',rate_info['pit']) + obj.set('rateAt',rate_info['time']) + obj.set('msgId',rate_info['msg_id']) + obj.save() # 保存 + + +# 更新皮肤评分 +async def update_SkinRate(skin_uuid:str,rating:float): + """ + - True: update rating success + - False: skin_uuid not found + """ + query = leancloud.Query('SkinRate') + query.equal_to('skinUuid', skin_uuid) + objlist = query.find() + if len(objlist) > 0: # 找到了 + # 更新评分 + objlist[0].set('rating',rating) + objlist[0].save() # 保存 + return True + return False \ No newline at end of file From aa42bf1baab2c55b437624fdc776bd141d5d03ab Mon Sep 17 00:00:00 2001 From: musnows Date: Fri, 24 Feb 2023 11:21:08 +0000 Subject: [PATCH 23/30] feat(main): using leancloud db in /rts --- code/main.py | 53 ++++++++++++++++++---------------------------------- 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/code/main.py b/code/main.py index 32c9957..68af63d 100644 --- a/code/main.py +++ b/code/main.py @@ -1715,49 +1715,32 @@ async def rate_skin_select(msg: Message, index: str = "err", rating: str = "err" comment = " ".join(arg) #用户对此皮肤的评论 text1 = "" text2 = "" - # 如果rate里面没有,先创立键值 - if skin_uuid not in SkinRateDict['rate']: - SkinRateDict['rate'][skin_uuid] = {} - SkinRateDict['rate'][skin_uuid]['pit'] = 0 - SkinRateDict['rate'][skin_uuid]['cmt'] = list() - if SkinRateDict['rate'][skin_uuid]['pit'] == 0: - point = float(_rating) - elif abs(float(_rating) - SkinRateDict['rate'][skin_uuid]['pit']) <= 32: + # 先从leancloud获取该皮肤的分数 + skin_rate = await ShopRate.query_SkinRate(skin_uuid) + if skin_rate['status']: # 找到了 #用户的评分和皮肤平均分差值不能超过32,避免有人乱刷分 - point = (SkinRateDict['rate'][skin_uuid]['pit'] + float(_rating)) / 2 - else: #差值过大,不计入皮肤平均值 - point = SkinRateDict['rate'][skin_uuid]['pit'] - text2 += f"由于您的评分和皮肤平均分差值大于32,所以您的评分不会计入皮肤平均分,但您的评论会进行保留\n" - # 设置皮肤的评分和评论 - SkinRateDict['rate'][skin_uuid]['pit'] = point - SkinRateDict['rate'][skin_uuid]['name'] = S_skin['skin']['displayName'] - SkinRateDict['rate'][skin_uuid]['cmt'].append(comment) - # data内是记录xx用户评论了xx皮肤 - if msg.author_id in SkinRateDict['data']: - #如果用户之前已经评论过这个皮肤,则需要删除之前的评论 - if skin_uuid in SkinRateDict['data'][msg.author_id]: - i = 0 - while (i < len(SkinRateDict['rate'][skin_uuid]['cmt'])): - #找到这条评论,将其删除 - if SkinRateDict['data'][ - msg.author_id][skin_uuid]['cmt'] == SkinRateDict['rate'][skin_uuid]['cmt'][i]: - SkinRateDict['rate'][skin_uuid]['cmt'].pop(i) - text1 += "更新" - break - i += 1 - else: #用户不存在,创建用户的dict - SkinRateDict['data'][msg.author_id] = {} - #无论用户在不在,都设置键值 + if abs(float(_rating) - skin_rate['rating']) <= 32: + # 计算分数 + point = (skin_rate['rating'] + float(_rating)) / 2 + # 更新数据库中皮肤评分 + await ShopRate.update_SkinRate(skin_uuid,point) + else: # 差值过大,不计入皮肤平均值 + point = skin_rate['rating'] + text2 += f"由于您的评分和皮肤平均分差值大于32,所以您的评分不会计入皮肤平均分,但您的评论会进行保留\n" + + # 无论用户在不在,都设置键值 SkinRateDict['data'][msg.author_id][skin_uuid] = {} SkinRateDict['data'][msg.author_id][skin_uuid]['name'] = S_skin['skin']['displayName'] SkinRateDict['data'][msg.author_id][skin_uuid]['cmt'] = comment SkinRateDict['data'][msg.author_id][skin_uuid]['pit'] = point - SkinRateDict['data'][msg.author_id][skin_uuid]['time'] = GetTime() + SkinRateDict['data'][msg.author_id][skin_uuid]['time'] = int(time.time()) # 秒级 SkinRateDict['data'][msg.author_id][skin_uuid]['msg_id'] = msg.id + # 数据库添加该评论 + await ShopRate.update_UserRate(skin_uuid,SkinRateDict['data'][msg.author_id][skin_uuid],msg.author_id) text1 += f"评价成功!{S_skin['skin']['displayName']}" text2 += f"您的评分:{_rating}\n" - text2 += f"皮肤平均分:{SkinRateDict['rate'][skin_uuid]['pit']}\n" + text2 += f"皮肤平均分:{point}\n" text2 += f"您的评语:{comment}" cm = CardMessage() c = Card(Module.Header(text1), Module.Divider(), Module.Section(Element.Text(text2, Types.Text.KMD))) @@ -2005,7 +1988,7 @@ async def auto_skin_notify(): SkinRateDict["cmp"]["best"]["pit"] = 0 SkinRateDict["cmp"]["worse"]["skin"] = list() SkinRateDict["cmp"]["worse"]["pit"] = 100 - ShopRate.update_shop_cmp() # 更新数据库中的记录 + await ShopRate.update_ShopCmp() # 更新数据库中的记录 print("[BOT.TASK.NOTIFY] SkinRateDict/UserShopDict clear, sleep(10)") #睡10s再开始遍历(避免时间不准) await asyncio.sleep(10) From fb6eafdfd86375dbb7789bcda7fbc6aa551d3819 Mon Sep 17 00:00:00 2001 From: musnows Date: Fri, 24 Feb 2023 11:44:06 +0000 Subject: [PATCH 24/30] feat(ShopRate): add get_ShopCmp for /kkn command --- code/main.py | 36 ++++++++++++++++++++++++------------ code/utils/ShopRate.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/code/main.py b/code/main.py index 68af63d..38fc087 100644 --- a/code/main.py +++ b/code/main.py @@ -1747,7 +1747,7 @@ async def rate_skin_select(msg: Message, index: str = "err", rating: str = "err" cm.append(c) # 设置成功并删除list后,再发送提醒事项设置成功的消息 await msg.reply(cm) - print(f"[rts] Au:{msg.author_id} ", text1) + print(f"[{GetTime()}] [rts] Au:{msg.author_id} ", text1) else: await msg.reply(f"您需要执行 `/rate 皮肤名` 来查找皮肤\n再使用 `/rts` 进行选择") @@ -1765,29 +1765,41 @@ async def rate_skin_select(msg: Message): await msg.reply(f"您有过不良评论记录,阿狸现已不允许您使用相关功能\n后台存放了所有用户的评论内容和评论时间。在此提醒,请不要在评论的时候发送不雅言论!") return try: + # 从数据库中获取 + cmpRet = await ShopRate.get_ShopCmp() + if not cmpRet['status']: + await msg.reply(f"获取昨日天选之子和丐帮帮主出错!请重试或联系开发者") + return + cm = CardMessage() c = Card(Module.Header(f"来看看昨日天选之子和丐帮帮主吧!"), Module.Divider()) # best text = "" - c.append(Module.Section(Element.Text(f"**天选之子** 综合评分 {SkinRateDict['kkn']['best']['pit']}", Types.Text.KMD))) - for sk in SkinRateDict['kkn']['best']['skin']: - if sk in SkinRateDict['rate']: - skin_name = f"「{SkinRateDict['rate'][sk]['name']}」" - text += f"%-50s\t\t评分: {SkinRateDict['rate'][sk]['pit']}\n" % skin_name + c.append(Module.Section(Element.Text(f"**天选之子** 综合评分 {cmpRet['best']['rating']}", Types.Text.KMD))) + c.append(Module.Context(f"来自 {cmpRet['best']['platform']} 平台")) + for sk in cmpRet['best']['skin_list']: + # 数据库中获取一个皮肤的评分情况 + skinRet = await ShopRate.query_SkinRate(sk) + if skinRet['status']: + skin_name = f"「{skinRet['skin_name']}」" + text += f"%-50s\t\t评分: {skinRet['rating']}\n" % skin_name c.append(Module.Section(Element.Text(text, Types.Text.KMD))) c.append(Module.Divider()) # worse text = "" - c.append(Module.Section(Element.Text(f"**丐帮帮主** 综合评分 {SkinRateDict['kkn']['worse']['pit']}", Types.Text.KMD))) - for sk in SkinRateDict['kkn']['worse']['skin']: - if sk in SkinRateDict['rate']: - skin_name = f"「{SkinRateDict['rate'][sk]['name']}」" - text += f"%-50s\t\t评分: {SkinRateDict['rate'][sk]['pit']}\n" % skin_name + c.append(Module.Section(Element.Text(f"**丐帮帮主** 综合评分 {cmpRet['worse']['rating']}", Types.Text.KMD))) + c.append(Module.Context(f"来自 {cmpRet['worse']['platform']} 平台")) + for sk in cmpRet['worse']['skin_list']: + # 数据库中获取一个皮肤的评分情况 + skinRet = await ShopRate.query_SkinRate(sk) + if skinRet['status']: + skin_name = f"「{skinRet['skin_name']}」" + text += f"%-50s\t\t评分: {skinRet['rating']}\n" % skin_name c.append(Module.Section(Element.Text(text, Types.Text.KMD))) cm.append(c) await msg.reply(cm) - print(f"[kkn] SkinRateDict save success!") + print(f"[{GetTime()}] [kkn] reply success") except requester.HTTPRequester.APIRequestFailed as result: #卡片消息发送失败 await APIRequestFailed_Handler("rts", traceback.format_exc(), msg, bot, None, cm) except Exception as result: # 其他错误 diff --git a/code/utils/ShopRate.py b/code/utils/ShopRate.py index 1e4b3ba..fb607f8 100644 --- a/code/utils/ShopRate.py +++ b/code/utils/ShopRate.py @@ -174,6 +174,43 @@ async def update_ShopCmp(): except: print(f"ERR! [update_shop_cmp]\n{traceback.format_exc()}") +# 获取昨日最好/最差用户 +async def get_ShopCmp(): + """Return:{ + "status": True/False + "best":{ + "skin_list": list of 4 skin uuid, + "rating": avg_rating, + "platform": str + } + "worse":{ + "skin_list": list of 4 skin uuid, + "rating": avg_rating, + "platform": str + } + } + """ + query = leancloud.Query('ShopCmp') + query.exists('rating') # 通过键值是否存在,进行查找 + objlist = query.find() + ret = {"status":False} + if len(objlist) == 2: # 应该是有两个的 + ret['status'] = True + for i in objlist: + infoDict = { + 'skin_list':i.get('skinList'), + 'rating': i.get('rating'), + 'platform':i.get('platform') + } + # 是最好 + if i.get('best'): + ret['best'] = infoDict + else: # 是最差 + ret['worse'] = infoDict + # 返回 + return ret + + # 获取可以购买皮肤的相关信息 async def query_UserCmt(user_id:str): """Return From 73ea0be7f02a06faf2758da5c8c9263d52e3570e Mon Sep 17 00:00:00 2001 From: musnows Date: Fri, 24 Feb 2023 11:56:24 +0000 Subject: [PATCH 25/30] feat(ShopRate): add remove UserRate for /ban-r cmd --- code/main.py | 18 +++++------------- code/utils/ShopRate.py | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/code/main.py b/code/main.py index 38fc087..cdcec48 100644 --- a/code/main.py +++ b/code/main.py @@ -1611,19 +1611,11 @@ async def set_rate_err_user(msg: Message, user_id: str): await msg.reply(f"该用户已在SkinRateDict['err_user']列表中") elif user_id in SkinRateDict['data']: for skin, info in SkinRateDict['data'][user_id].items(): - i = 0 - while (i < len(SkinRateDict['rate'][skin]['cmt'])): - #找到这条评论,将其删除 - if info['cmt'] == SkinRateDict['rate'][skin]['cmt'][i]: - SkinRateDict['rate'][skin]['cmt'].pop(i) - break - i += 1 - #如果删除评论之后,链表为空,说明该链表中只有这一个评论 - if not SkinRateDict['rate'][skin]['cmt']: #空列表视为false - #删除掉这个皮肤的rate - del SkinRateDict['rate'][skin] - - #删除完该用户的所有评论之后,将其放入err_user + # 找到这条评论,将其删除 + if not await ShopRate.remove_UserRate(skin,user_id): + await msg.reply(f"Au:{user_id} 删除 {skin} [{info['name']}] 错误") + + # 删除完该用户的所有评论之后,将其放入err_user temp_user = copy.deepcopy(SkinRateDict['data'][user_id]) del SkinRateDict['data'][user_id] SkinRateDict['err_user'][user_id] = temp_user diff --git a/code/utils/ShopRate.py b/code/utils/ShopRate.py index fb607f8..a2bfad8 100644 --- a/code/utils/ShopRate.py +++ b/code/utils/ShopRate.py @@ -332,4 +332,21 @@ async def update_SkinRate(skin_uuid:str,rating:float): objlist[0].set('rating',rating) objlist[0].save() # 保存 return True + return False + +# 删除皮肤评价(违规言论) +async def remove_UserRate(skin_uuid:str,user_id:str): + """ + - True: remove success + - False: skin_uuid or user_id not found + """ + query = leancloud.Query('UserRate') + query.equal_to('skinUuid', skin_uuid) + query.equal_to('userId', user_id) + objlist = query.find() + # 找到了,直接删除 + if len(objlist) > 0: + objlist[0].destroy() + return True + return False \ No newline at end of file From f8cb273f3847e6e58e08e22989475c0ff489194a Mon Sep 17 00:00:00 2001 From: musnows Date: Fri, 24 Feb 2023 11:59:58 +0000 Subject: [PATCH 26/30] fix(ShopRate): using leancloud to replace SkinRateDict in check_shop_rate --- code/utils/ShopRate.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/code/utils/ShopRate.py b/code/utils/ShopRate.py index a2bfad8..b302d50 100644 --- a/code/utils/ShopRate.py +++ b/code/utils/ShopRate.py @@ -94,9 +94,14 @@ async def check_shop_rate(kook_user_id: str, list_shop: list): rate_count = 0 rate_total = 0 for sk in list_shop: - if sk in SkinRateDict['rate']: + # 在云端查找并获取分数 + query = leancloud.Query('SkinRate') + query.equal_to('skinUuid',sk) + objlist = query.find() + if len(objlist) > 0: # 找到了 rate_count += 1 - rate_total += SkinRateDict['rate'][sk]['pit'] + rate_total += objlist[0].get('rating') # 获取得分 + if rate_count != 0: rate_avg = rate_total // rate_count #平均分 From abb6120d336c57927e86aa57e506e5f3078fe62b Mon Sep 17 00:00:00 2001 From: musnows Date: Fri, 24 Feb 2023 12:07:17 +0000 Subject: [PATCH 27/30] docs(leancloud): update leancloud docs --- docs/leancloud.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/docs/leancloud.md b/docs/leancloud.md index 8a2fd6f..7f1c633 100644 --- a/docs/leancloud.md +++ b/docs/leancloud.md @@ -12,7 +12,7 @@ ---- -目前设置了3个class,字段如下: +目前设置了多个class,字段如下: ## 1.SkinRate @@ -55,4 +55,22 @@ | rating | NUMBER | 商店4个皮肤的平均分 | 是 | | skinList | ARRAY | 商店4个皮肤 uuid | 是 | | platform | STRING | 平台 | 是 | -| userId | STRING | 用户平台id | 否 | \ No newline at end of file +| userId | STRING | 用户平台id | 否 | + +建议在本地维护一个当天(8am之后)的用户最好/最差商店信息,并在次日8am更新到数据库。数据库中维护的是**昨日商店**的最高分/最低分。 + +由于leancloud的结构化数据存储并不是[线程安全](https://docs.leancloud.cn/sdk/storage/guide/python/#%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8)的,为了避免多个项目同时在8am访问并更新ShopCmp,请加入开发者频道和我确认,约定一个时间(不同项目错开)来更新数据! + +## 4.UserCmt + +用户user_id和其评论过的皮肤uuid的对照表 + +| 字段 | 类型 | 含义 | 是否必填 | +| --- | --- | --- | ------- | +| skinList | ARRAY | 用户评价过的所有皮肤uuid | 是 | +| platform | STRING | 平台 | 是 | +| userId | STRING | 用户平台id | 是 | + +此字段是采用“以空间换时间的”思路,如果不维护此表,则需要**遍历**UserRate中的所有评论,找到该用户已评论皮肤,并不是很友好。 + +如果你的平台并没有用户id,也不需要获取某一用户已评论皮肤列表,则可以不维护此class。 \ No newline at end of file From 556893508822d7c08e2736ed4928152f56132fa1 Mon Sep 17 00:00:00 2001 From: musnow Date: Fri, 24 Feb 2023 21:26:31 +0800 Subject: [PATCH 28/30] fix(ShopRate): add update_UserCmt for /rts --- code/main.py | 9 +++++++-- code/utils/ShopRate.py | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/code/main.py b/code/main.py index cdcec48..3060e93 100644 --- a/code/main.py +++ b/code/main.py @@ -1719,8 +1719,11 @@ async def rate_skin_select(msg: Message, index: str = "err", rating: str = "err" else: # 差值过大,不计入皮肤平均值 point = skin_rate['rating'] text2 += f"由于您的评分和皮肤平均分差值大于32,所以您的评分不会计入皮肤平均分,但您的评论会进行保留\n" - - # 无论用户在不在,都设置键值 + + # 用户之前没有评价过,新建键值 + if msg.author_id not in SkinRateDict['data']: + SkinRateDict['data'][msg.author_id] = {} + # 设置uuid的键值 SkinRateDict['data'][msg.author_id][skin_uuid] = {} SkinRateDict['data'][msg.author_id][skin_uuid]['name'] = S_skin['skin']['displayName'] SkinRateDict['data'][msg.author_id][skin_uuid]['cmt'] = comment @@ -1729,6 +1732,8 @@ async def rate_skin_select(msg: Message, index: str = "err", rating: str = "err" SkinRateDict['data'][msg.author_id][skin_uuid]['msg_id'] = msg.id # 数据库添加该评论 await ShopRate.update_UserRate(skin_uuid,SkinRateDict['data'][msg.author_id][skin_uuid],msg.author_id) + # 更新用户已评价的皮肤 + await ShopRate.update_UserCmt(msg.author_id,skin_uuid) text1 += f"评价成功!{S_skin['skin']['displayName']}" text2 += f"您的评分:{_rating}\n" diff --git a/code/utils/ShopRate.py b/code/utils/ShopRate.py index b302d50..9816fa5 100644 --- a/code/utils/ShopRate.py +++ b/code/utils/ShopRate.py @@ -230,6 +230,29 @@ async def query_UserCmt(user_id:str): return objlist[0].get('skinList') else: # 不存在 return [] + +# 更新用户已评价皮肤 +async def update_UserCmt(user_id:str,skin_uuid:str): + # 初始化为只有当前皮肤uuid的list + skinList = [ skin_uuid ] + UserCmt = leancloud.Object.extend('UserCmt') + query = UserCmt.query + # 先查找是否有userid和平台都相同的obj(如有,直接更新评论、评分、时间) + query.equal_to('userId', user_id) + query.equal_to('platform','kook') + objlist = query.find() + if len(objlist)>0: # 有,更新 + obj = objlist[0] + skinList = obj.get('skinList') + skinList.append(skin_uuid) + else: # 没有,新建 + obj = UserCmt() + obj.set('platform','kook') + obj.set('userId',user_id) + + obj.set('skinList',skinList) + obj.save() # 保存 + # 获取可以购买皮肤的相关信息的text async def get_skinlist_rate_text(skinlist:list,user_id:str): From a829aa8f1c0cde930e5cd4b30f9d7569548d92d9 Mon Sep 17 00:00:00 2001 From: musnow Date: Fri, 24 Feb 2023 21:34:45 +0800 Subject: [PATCH 29/30] fix(main): fix err of un-update new skin in rate --- code/main.py | 7 ++++--- code/utils/ShopRate.py | 24 +++++++++++++----------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/code/main.py b/code/main.py index 3060e93..6b4a319 100644 --- a/code/main.py +++ b/code/main.py @@ -1705,6 +1705,7 @@ async def rate_skin_select(msg: Message, index: str = "err", rating: str = "err" S_skin = UserRtsDict[msg.author_id][_index] skin_uuid = S_skin['skin']['lv_uuid'] comment = " ".join(arg) #用户对此皮肤的评论 + point = _rating # 初始化分数 text1 = "" text2 = "" # 先从leancloud获取该皮肤的分数 @@ -1714,12 +1715,12 @@ async def rate_skin_select(msg: Message, index: str = "err", rating: str = "err" if abs(float(_rating) - skin_rate['rating']) <= 32: # 计算分数 point = (skin_rate['rating'] + float(_rating)) / 2 - # 更新数据库中皮肤评分 - await ShopRate.update_SkinRate(skin_uuid,point) else: # 差值过大,不计入皮肤平均值 point = skin_rate['rating'] text2 += f"由于您的评分和皮肤平均分差值大于32,所以您的评分不会计入皮肤平均分,但您的评论会进行保留\n" + # 更新数据库中皮肤评分 + await ShopRate.update_SkinRate(skin_uuid,S_skin['skin']['displayName'],point) # 用户之前没有评价过,新建键值 if msg.author_id not in SkinRateDict['data']: SkinRateDict['data'][msg.author_id] = {} @@ -1744,7 +1745,7 @@ async def rate_skin_select(msg: Message, index: str = "err", rating: str = "err" cm.append(c) # 设置成功并删除list后,再发送提醒事项设置成功的消息 await msg.reply(cm) - print(f"[{GetTime()}] [rts] Au:{msg.author_id} ", text1) + print(f"[{GetTime()}] [rts] Au:{msg.author_id} {text1} {skin_uuid}") else: await msg.reply(f"您需要执行 `/rate 皮肤名` 来查找皮肤\n再使用 `/rts` 进行选择") diff --git a/code/utils/ShopRate.py b/code/utils/ShopRate.py index 9816fa5..ac956d6 100644 --- a/code/utils/ShopRate.py +++ b/code/utils/ShopRate.py @@ -347,20 +347,22 @@ async def update_UserRate(skin_uuid:str,rate_info:dict,user_id:str): # 更新皮肤评分 -async def update_SkinRate(skin_uuid:str,rating:float): - """ - - True: update rating success - - False: skin_uuid not found - """ - query = leancloud.Query('SkinRate') +async def update_SkinRate(skin_uuid:str,skin_name:str,rating:float): + SkinRate = leancloud.Object.extend('SkinRate') + query = SkinRate.query query.equal_to('skinUuid', skin_uuid) objlist = query.find() if len(objlist) > 0: # 找到了 - # 更新评分 - objlist[0].set('rating',rating) - objlist[0].save() # 保存 - return True - return False + obj = objlist[0] + else: + obj = SkinRate() + obj.set('skinUuid',skin_uuid) + obj.set('skinName',skin_name) + + # 更新评分 + obj.set('rating',rating) + obj.save() # 保存 + # 删除皮肤评价(违规言论) async def remove_UserRate(skin_uuid:str,user_id:str): From 139fb3c3667574cd9d996904b829ffe1c7c671cf Mon Sep 17 00:00:00 2001 From: musnow Date: Fri, 24 Feb 2023 21:47:14 +0800 Subject: [PATCH 30/30] fix(ShopRate): using global PLATFORM, fix keyerr in update_ShopCmp --- code/utils/ShopRate.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/code/utils/ShopRate.py b/code/utils/ShopRate.py index ac956d6..18f01a8 100644 --- a/code/utils/ShopRate.py +++ b/code/utils/ShopRate.py @@ -8,6 +8,7 @@ from utils.valorant import Val from utils.FileManage import config,SkinRateDict leancloud.init(config["leancloud"]["appid"], master_key=config["leancloud"]["master_key"]) +PLATFORM = "kook" # 获取皮肤评价的信息 async def get_shop_rate(list_shop: dict, kook_user_id: str): @@ -154,7 +155,6 @@ async def update_ShopCmp(): if len(objlist) == 0: raise Exception("leancloud find today err!") # 开始更新,先设置为最差 - platfrom = 'kook' rate_avg = SkinRateDict["kkn"]["worse"]["pit"] list_shop = SkinRateDict["kkn"]["worse"]["skin"] kook_user_id = SkinRateDict["kkn"]["worse"]["kook_id"] @@ -173,7 +173,7 @@ async def update_ShopCmp(): i.set('userId',kook_user_id) i.set('skinList',list_shop) i.set('rating',rate_avg) - i.set('platfrom',platfrom) + i.set('platform',PLATFORM) i.save() print(f"[update_shop_cmp] saving best:{i.get('best')}") except: @@ -222,7 +222,7 @@ async def query_UserCmt(user_id:str): A list containing the skin evaluated by the user, """ query = leancloud.Query('UserCmt') - query.equal_to('platform', 'kook') + query.equal_to('platform', PLATFORM) query.equal_to('userId', user_id) # 查找usercmt中有没有该用户 objlist = query.find() if len(objlist) > 0 : # 存在 @@ -239,7 +239,7 @@ async def update_UserCmt(user_id:str,skin_uuid:str): query = UserCmt.query # 先查找是否有userid和平台都相同的obj(如有,直接更新评论、评分、时间) query.equal_to('userId', user_id) - query.equal_to('platform','kook') + query.equal_to('platform',PLATFORM) objlist = query.find() if len(objlist)>0: # 有,更新 obj = objlist[0] @@ -247,7 +247,7 @@ async def update_UserCmt(user_id:str,skin_uuid:str): skinList.append(skin_uuid) else: # 没有,新建 obj = UserCmt() - obj.set('platform','kook') + obj.set('platform',PLATFORM) obj.set('userId',user_id) obj.set('skinList',skinList) @@ -269,7 +269,7 @@ async def get_skinlist_rate_text(skinlist:list,user_id:str): userCmtList = await query_UserCmt(user_id) i=0 query = leancloud.Query('UserRate') - query.equal_to('platform', 'kook') + query.equal_to('platform', PLATFORM) text = "" # 模拟一个选择表 for w in skinlist: # 先插入皮肤名字和皮肤价格 @@ -336,7 +336,7 @@ async def update_UserRate(skin_uuid:str,rate_info:dict,user_id:str): obj = UserRate() obj.set('skinUuid',skin_uuid) obj.set('skinName',rate_info['name']) - obj.set('platform','kook') + obj.set('platform',PLATFORM) obj.set('userId',user_id) obj.set('comment',rate_info['cmt'])