本文主要是介绍AutoUU--有品租赁自动上架的开源轻量化工具--程序篇、(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
接前文的API篇。
如果你只是想使用,可直接跳到(三)实战篇
AutoUU--悠悠有品租赁自动上架的开源轻量化工具--实战篇、(三)_worldofgoo9的博客-CSDN博客
二、程序篇
1、配置文件结构
(暂时的)以一个configIndex.json文件作为顶层配置,在其中规定了其他配置文件的路径。总体结构如下:
<1>configIndex.json文件
configIndex.json格式如下:
其中configPath是一个列表,每一个对应了一个配置文件,之所以这里采用列表是为了控制多个配置文件的使能,使得配置更加灵活,例如:如果你有的时间只想上架一部分饰品(往往有这种需求的话会提前分类到不同的配置里),可以把不想上架的饰品对应的配置json去掉。
其他的字段为:
"userName" : "你的用户名(手机)",
"userPwd" : "登录密码",
"retryTimes" : 3, 代表了当出现错误时,重试的次数,不建议更改
"retryInterval" : 300.0, 代表了当出现错误时,重试前等待的时间(秒),不建议更改
"runTime" : "17:00" 代表了上架流程每天的运行时间,根据自己的需求更改
"maxInvPage" : 2 程序只会检测你的前X页库存,也就是这里的最大检测库存页数设定。库存页数根据自己需要上架库存量而定,建议够用即可,不要太大,否则会很慢还可能出问题。一般来说,新获得的物品都是在前面的,所以到cd的物品也是在前面,加上平均下来每一天的物品不会太多,所以一般2-3页就很多很多了。
<2>example.json文件
具体的配置文件格式如下,这里以“example.json”文件为例:
该文件是一个字典的列表,每一个字典({})代表了一项配置,用逗号隔开(注意最后一项后面没有逗号),对应一个物品,其中各个字段含义如下:
{
"float":"0.01342727987876540", 代表了物品的磨损值,是标识一个物品的关键(请使用有品上的磨损值而不是某buff的,因为某buff的显示不全)
"strategy": "long", 代表了物品的上架策略,共有四种,会在下面解释。
"shortPrice":3.0, 物品的短租租金,注意在不同策略下含义不同。
"longPrice":2.5, 物品的长租租金,注意在不同策略下含义不同。
"valuePrice":14999.0, 物品的押金设置,售价会设置为同值
"maxDay":22, 物品的最长租赁时间
"message":"长租好价 001", 物品的上架描述
"name":"M9刺刀(★) | 虎牙" ,物品的名称,也可以看做备忘录,可以填写任意字符,不会影响上架流程,但是如果你填写了之后以后就知道这个是什么物品,不会忘了。
},
这里具体解释一下"strategy"字段,一共有四种设置:
"short": 顾名思义,代表了优先短租,短租价格会设置为(市场短租底价*0.97-0.01),长租价格会设置为(市场长租底价*1.015+0.01)
"long": 代表了优先短租,短租价格会设置为(市场短租底价*1.015+0.01),长租价格会设置为(市场长租底价*0.97-0.01)
"auto": 代表了无偏向性的自动定价,长短租价格会分别设置为(底价租金*0.985)。
"fix": 代表了固定的价格设置,长短租价格会固定设置为"shortPrice"和"longPrice"中的值。
此外,在自动定价的模式下(short/long/auto),"shortPrice"和"longPrice"代表了定价的下限,也就是说如果自动定价获取的价格小于你设置的下限,那么价格就会被设置为该下限值。
2、程序设计
<1>程序执行框架
程序执行过程的框架如下图:
<2>读取配置信息
读取配置信息函数接收地址路径列表参数pathList,调用loadConfigs会依次读取参数地址路径列表中的所有配置文件并添加进当前配置中。
def loadConfigs(self, pathList, encoding = "utf-8"):
for path in pathList:
self.__loadConfig__(path)
def __loadConfig__(self, path="cfg.json", encoding="utf-8"):
with open(path, "r", encoding=encoding) as f:
config = json.load(f)
num = len(config)
result = {}
for item in config:
result[str(item['float'])] = item
self.printLog(f"Successfully Load {num} Configurations.")
self.config.update(result)
return result
<3>登录、获取用户信息
登录函数接收用户名密码作为参数,获取会话的Token以及用户id,昵称并保存。
def login(self, userName, userPwd):
url = "https://api.youpin898.com/api/user/Auth/PwdSignIn"
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36",
"accept": "application/json, text/plain, */*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "zh-CN,zh;q=0.9",
"apptype": '1',
"origin": "https://www.youpin898.com",
"referer": "https://www.youpin898.com/",
"sec-ch-ua": '" Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"',
"sec-ch=-ua=mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site"
}
data = {
"code": "",
"SessionId": "",
"UserName": userName,
"UserPwd": userPwd
}
res = requests.post(url, headers=headers, json=data).json()
if(res["Code"] == 0):
token = res['Data']['Token']
self.printLog(f"Get Session Token:{token}")
self.token = token
self.isLogin = True
else:
raise MyError(f"Get Session Failed. Res code:{res['Code']},res:{res}")
url = "https://api.youpin898.com/api/user/Account/GetUserInfo"
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36",
"accept": "application/json, text/plain, */*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "zh-CN,zh;q=0.9",
"apptype": '1',
"authorization": f"Bearer {token}",
"origin": "https://www.youpin898.com",
"referer": "https://www.youpin898.com/",
"sec-ch-ua": '" Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"',
"sec-ch=-ua=mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site"
}
res = requests.get(url, headers=headers).json()
if(res["Code"] == 0):
self.userId = res['Data']['UserId']
self.name = res['Data']['NickName']
#self.printLog(
# f"Get User Info. Id:{res['Data']['UserId']},Name:{res['Data']['NickName']}")
self.isLogin = True
#return True
else:
raise MyError(f"Get User Info Failed. Response code:{res['Code']}, body:{res}")
最主要的步骤为发送两个请求,分别为登录获取Token,在获取Token后请求获得用户信息,用来获得用户的Id,之后会用到。
<4>获取库存信息
获取库存信息不需要额外的参数,会根据设置的最大页数来获取所要操作的库存信息
def getInv(self):
if(not self.isLogin):
raise MyError("Please Login first.")
headers = {
"app-version": "4.1.3",
"version": "4.1.3",
"platform": "ios",
"accept": "*/*",
"accept-encoding": "br;q=1.0, gzip;q=0.9, deflate;q=0.8",
"accept-language": "zh-Hans-CN;q=1.0, en-CN;q=0.9",
"AppType": '3',
"authorization": f"Bearer {self.token}",
} # 这里实际采用的是IOS客户端的API,因为网页端获取库存有时会崩溃
self.invData = []
pageIndex = 1
while(pageIndex <= self.maxInvPageIndex):
url = f'''https://api.youpin898.com/api/commodity/Inventory/GetUserInventoryDataList?AppType=3&GameID=730&IsRefresh=false&ItemStatus=0&PageIndex={pageIndex}&PageSize=30&Platform=ios&Sort=0&Version=4.1.3'''
res = requests.get(url, headers=headers).json()
pageIndex += 1
if(res["Code"] == 0):
self.invData += res['Data']['ItemsInfos']
self.operateSleep()
else:
raise MyError(f"Get Inv Info Failed. Response code:{res['Code']}, body:{res}")
self.printLog(f"Get Inv Info Success. Total Num : {len(self.invData)}")
<5>获取市场价格
获取市场价格是一个私有函数,参数为物品的ID,会返回当前市场的短长租底价。
def __getMarketPrice__(self, itemId):
if(not self.isLogin):
raise MyError("Please Login first.")
url = "https://api.youpin898.com/api/homepage/es/commodity/GetCsGoPagedList"
itemId = str(itemId)
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36",
"accept": "application/json, text/plain, */*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "zh-CN,zh;q=0.9",
"apptype": '1',
"authorization": f"Bearer {self.token}",
'content-type': "application/json;charset=UTF-8",
"origin": "https://www.youpin898.com",
"referer": "https://www.youpin898.com/",
"sec-ch-ua": '" Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"',
"sec-ch=-ua=mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site"
# "Connection": "keep-alive",
}
queryShort = {"templateId": itemId, "pageSize": 30, "pageIndex": 1,
"sortType": 1, "listSortType": 2, "listType": 30, "stickersIsSort": False}
queryLong = {"templateId": itemId, "pageSize": 30, "pageIndex": 1,
"sortType": 1, "listSortType": 3, "listType": 30, "stickersIsSort": False}
res = requests.post(url, headers=headers, json=queryShort).json()
if(res["Code"] == 0):
shortPrice = float(res["Data"]['CommodityList'][0]['LeaseUnitPrice'])
else:
raise MyError(f"Get Short-Lease Price Failed. Response code:{res['Code']}, body:{res}")
res = requests.post(url, headers=headers, json=queryLong).json()
if(res["Code"] == 0):
longPrice = float(res["Data"]['CommodityList'][0]['LeaseUnitPrice'])
else:
raise MyError(f"Get Long-Lease Price Failed. Response code:{res['Code']}, body:{res}")
return {
"shortPrice": shortPrice,
"longPrice": longPrice,
}
# Query data sort by short / long lease price.
这里参数itemId对应了该物品在uu中的ID号,在获取库存信息时会得到。
<6>定价
定价函数不需要额外参数,会根据前面获取到的库存数据逐一进行判断是否要上架以及定价多少。
def doPricing(self):
if(not self.isLogin):
raise MyError("Please Login first.")
invNum = len(self.invData)
price = [{} for i in range(invNum)] # Responding to each item in INV
#itemTemp = {}
for i in range(invNum):
if(self.invData[i]['AssetInfo'] is None):
price[i] = {"flag": False}
continue
itemFloat = str(self.invData[i]['AssetInfo']['Abrade'])
assetId = self.invData[i]['SteamAssetID']
itemId = self.invData[i]['TemplateInfo']['Id']
if((itemFloat not in self.config.keys()) or (self.invData[i]['Tradable'] == False) ): #or (itemId in itemTemp.keys())
price[i] = {"flag": False}
continue
#print(f"{i} {itemFloat} {self.invData[i]['OnSale']} {self.invData[i]}")
time.sleep(self.timeSleep)
marketPrice = self.__getMarketPrice__(itemId)
shortMarketPrice = marketPrice['shortPrice']
longMarketPrice = marketPrice['longPrice']
# Do pricing.
strategy = self.config[itemFloat]['strategy']
if(strategy == "auto"):
shortPrice = max(
self.config[itemFloat]['shortPrice'], shortMarketPrice*0.985-0.01)
longPrice = max(
self.config[itemFloat]['longPrice'], longMarketPrice*0.985-0.01)
elif(strategy == "short"):
shortPrice = max(
self.config[itemFloat]['shortPrice'], shortMarketPrice*0.97-0.01)
longPrice = max(
self.config[itemFloat]['longPrice'], longMarketPrice*1.015+0.01)
elif(strategy == "long"):
shortPrice = max(
self.config[itemFloat]['shortPrice'], shortMarketPrice*1.015+0.01)
longPrice = max(
self.config[itemFloat]['longPrice'], longMarketPrice*0.97-0.01)
elif(strategy == "fix"):
# Fixed.
shortPrice = self.config[itemFloat]['shortPrice']
longPrice = self.config[itemFloat]['longPrice']
else:
self.printLog(
f"Unvalid strategy {strategy} ------ item float {itemFloat}", 2)
price[i] = {"flag": False}
continue
shortPrice = round(shortPrice, 2)
longPrice = round(longPrice, 2)
valuePrice = self.config[itemFloat]['valuePrice']
maxDay = max(self.config[itemFloat]['maxDay'], 8)
message = self.config[itemFloat]['message']
#itemTemp[itemId] = True
price[i] = {"flag": True, "assetId": int(assetId), "templateId": int(
itemId), "itemFloat": itemFloat, "shortPrice": shortPrice, "longPrice": longPrice, "valuePrice": valuePrice, "maxDay": maxDay, "message": message}
self.price = price
<7>上架
上架函数不需要额外参数,其信息来源于上面的定价函数的执行结果,其具体执行过程为执行两个请求,包括获取AppKey以及上架操作,对应了API篇的分析结果。
def putOnSale(self):
if(not self.isLogin):
raise MyError("Please Login first.")
itemInfos = []
for item in self.price:
if(item['flag'] == True):
itemInfos.append(
{
"assetId": item['assetId'], "commodityTemplateId": item['templateId'], "remark": item['message'], "charge": item['valuePrice'], "price": item['valuePrice'],
"IsCanLease": True, "IsCanSold": True,
"LeaseMaxDays": item['maxDay'], "LeaseUnitPrice": item['shortPrice'], "LongLeaseUnitPrice": item['longPrice'], "LongLeaseDays": item['maxDay'], "LeaseDeposit": item['valuePrice']
}
)
num = len(itemInfos)
if(num == 0):
self.printLog(f"Nothing to be put onto sell.")
return True
# Apply App for Key
url = f"https://api.youpin898.com/api/youpin/detect/detection/1/{self.userId}/0/app"
#itemId = str(itemId)
headers = {
# "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:72.0) Gecko/20100101 Firefox/72.0 micromessenger",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36",
"accept": "application/json, text/plain, */*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "zh-CN,zh;q=0.9",
"apptype": '1',
"authorization": f"Bearer {self.token}",
'content-type': "application/json;charset=UTF-8",
"origin": "https://www.youpin898.com",
"referer": "https://www.youpin898.com/",
"sec-ch-ua": '" Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"',
"sec-ch=-ua=mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site"
# "Connection": "keep-alive",
}
res = requests.get(url, headers=headers).json()
if(res["code"] == 0):
key = res['data']['key']
self.printLog(f"Get App Key Succ. Key:{key}")
else:
raise MyError(f"Get App Key Failed. Response code:{res['Code']}, body:{res}")
#return False
# Sell items
url = f"https://api.youpin898.com/api/commodity/Inventory/SellInventoryWithLease"
payload = {
"gameId": "730", # Csgo
"itemInfos": itemInfos,
"key": key
}
res = requests.post(url, headers=headers, json=payload).json()
if(res["Code"] == 0):
self.printLog(f"Sell {num} items Succ.")
return num
else:
raise MyError(f"Put on Sale Failed. Response code:{res['Code']}, body:{res}")
<8>高层执行代码
其中uu.operateSleep()仅起到暂停作用,用于降低服务器瞬时压力,默认为暂停3s
def operateSleep(self):
time.sleep(self.timeSleep)
def run():
# Try for muiltiple times
configIndex = loadConfigIndex()
uu = AutoUU(maxInvPageIndex = configIndex['maxInvPage'])
uu.printLog("Start !")
retryTimes = configIndex["retryTimes"]
retryInterval = configIndex["retryInterval"]
userName = configIndex["userName"]
userPwd = configIndex["userPwd"]
flag = 0 # tried times
while(flag < retryTimes and flag >= 0):
if(flag > 0):
uu.printLog("Retrying , time : " + str(flag) , 1)
flag += 1
uu.loadConfigs(pathList = configIndex["configPath"])
try:
uu.login(userName, userPwd)
uu.printLog("Login Finished !")
uu.operateSleep()
uu.getInv()
uu.printLog("Get INV Finished !")
uu.operateSleep()
uu.doPricing()
uu.printLog("Do Pricing Finished !")
uu.operateSleep()
uu.putOnSale()
uu.printLog("Put On Sale Finished !")
uu.operateSleep()
except MyError as e:
uu.printLog(e.value , 1)
time.sleep(retryInterval)
except Exception as e:
uu.printLog(e , 2)
time.sleep(retryInterval)
else:
flag = -1 # Finish
uu.printLog(f"Routine Finished !")
break
这篇关于AutoUU--有品租赁自动上架的开源轻量化工具--程序篇、(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!