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智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

高效录音转文字:2024年四大工具精选!

在快节奏的工作生活中,能够快速将录音转换成文字是一项非常实用的能力。特别是在需要记录会议纪要、讲座内容或者是采访素材的时候,一款优秀的在线录音转文字工具能派上大用场。以下推荐几个好用的录音转文字工具! 365在线转文字 直达链接:https://www.pdf365.cn/ 365在线转文字是一款提供在线录音转文字服务的工具,它以其高效、便捷的特点受到用户的青睐。用户无需下载安装任何软件,只

金融业开源技术 术语

金融业开源技术  术语 1  范围 本文件界定了金融业开源技术的常用术语。 本文件适用于金融业中涉及开源技术的相关标准及规范性文件制定和信息沟通等活动。

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

EMLOG程序单页友链和标签增加美化

单页友联效果图: 标签页面效果图: 源码介绍 EMLOG单页友情链接和TAG标签,友链单页文件代码main{width: 58%;是设置宽度 自己把设置成与您的网站宽度一样,如果自适应就填写100%,TAG文件不用修改 安装方法:把Links.php和tag.php上传到网站根目录即可,访问 域名/Links.php、域名/tag.php 所有模板适用,代码就不粘贴出来,已经打

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机

跨系统环境下LabVIEW程序稳定运行

在LabVIEW开发中,不同电脑的配置和操作系统(如Win11与Win7)可能对程序的稳定运行产生影响。为了确保程序在不同平台上都能正常且稳定运行,需要从兼容性、驱动、以及性能优化等多个方面入手。本文将详细介绍如何在不同系统环境下,使LabVIEW开发的程序保持稳定运行的有效策略。 LabVIEW版本兼容性 LabVIEW各版本对不同操作系统的支持存在差异。因此,在开发程序时,尽量使用