AutoUU--有品租赁自动上架的开源轻量化工具--程序篇、(二)

2024-02-08 04:50

本文主要是介绍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--有品租赁自动上架的开源轻量化工具--程序篇、(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/689948

相关文章

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注

pytorch自动求梯度autograd的实现

《pytorch自动求梯度autograd的实现》autograd是一个自动微分引擎,它可以自动计算张量的梯度,本文主要介绍了pytorch自动求梯度autograd的实现,具有一定的参考价值,感兴趣... autograd是pytorch构建神经网络的核心。在 PyTorch 中,结合以下代码例子,当你

使用Python构建一个Hexo博客发布工具

《使用Python构建一个Hexo博客发布工具》虽然Hexo的命令行工具非常强大,但对于日常的博客撰写和发布过程,我总觉得缺少一个直观的图形界面来简化操作,下面我们就来看看如何使用Python构建一个... 目录引言Hexo博客系统简介设计需求技术选择代码实现主框架界面设计核心功能实现1. 发布文章2. 加

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

Python如何自动生成环境依赖包requirements

《Python如何自动生成环境依赖包requirements》:本文主要介绍Python如何自动生成环境依赖包requirements问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录生成当前 python 环境 安装的所有依赖包1、命令2、常见问题只生成当前 项目 的所有依赖包1、

基于Python打造一个全能文本处理工具

《基于Python打造一个全能文本处理工具》:本文主要介绍一个基于Python+Tkinter开发的全功能本地化文本处理工具,它不仅具备基础的格式转换功能,更集成了中文特色处理等实用功能,有需要的... 目录1. 概述:当文本处理遇上python图形界面2. 功能全景图:六大核心模块解析3.运行效果4. 相

springboot项目中常用的工具类和api详解

《springboot项目中常用的工具类和api详解》在SpringBoot项目中,开发者通常会依赖一些工具类和API来简化开发、提高效率,以下是一些常用的工具类及其典型应用场景,涵盖Spring原生... 目录1. Spring Framework 自带工具类(1) StringUtils(2) Coll

SpringBoot实现微信小程序支付功能

《SpringBoot实现微信小程序支付功能》小程序支付功能已成为众多应用的核心需求之一,本文主要介绍了SpringBoot实现微信小程序支付功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作... 目录一、引言二、准备工作(一)微信支付商户平台配置(二)Spring Boot项目搭建(三)配置文件

基于Python实现高效PPT转图片工具

《基于Python实现高效PPT转图片工具》在日常工作中,PPT是我们常用的演示工具,但有时候我们需要将PPT的内容提取为图片格式以便于展示或保存,所以本文将用Python实现PPT转PNG工具,希望... 目录1. 概述2. 功能使用2.1 安装依赖2.2 使用步骤2.3 代码实现2.4 GUI界面3.效