Skip to content

Commit

Permalink
feat: add oppo client
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinGong2013 committed Aug 31, 2023
1 parent 481823b commit 9c01430
Show file tree
Hide file tree
Showing 6 changed files with 581 additions and 110 deletions.
189 changes: 152 additions & 37 deletions cmd/oppo/client.go
Original file line number Diff line number Diff line change
@@ -1,55 +1,170 @@
package oppo

import (
"errors"
"fmt"
"net/url"
"strconv"
"time"

"github.com/KevinGong2013/apkgo/cmd/shared"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/proto"
"github.com/go-resty/resty/v2"
)

type OppoClient string
// https://open.oppomobile.com/new/developmentDoc/info?id=10998

var DefaultClient = OppoClient("oppo")
// 1300699476073808945
// 2ebf891579110cfdd7d1095adab056f716c1ecda029132f98bde73b00d220b31

func (oc OppoClient) Identifier() string {
return string(oc)
type Client struct {
restyClient *resty.Client
accessToken string
clientSecret string
}

func (oc OppoClient) Name() string {
return "oppo应用商店"
func (c *Client) Name() string {
return "oppo开放平台"
}

func (oc OppoClient) CheckAuth(browser *rod.Browser, reAuth bool) (*rod.Page, error) {
page, err := browser.Page(proto.TargetCreateTarget{
URL: "https://open.oppomobile.com/new/ecological/app",
})
func NewClient(clientId, clientSecret string) (*Client, error) {

restyClient := resty.New()

restyClient.SetDebugBodyLimit(10000)

restyClient.SetBaseURL("https://oop-openapi-cn.heytapmobi.com")
restyClient.SetHeader("Content-Type", "application/json")
restyClient.SetDebug(true)

// 获取token
var result struct {
Errno int `json:"errno"`
Data struct {
AccessToken string `json:"access_token"`
ExpireIn int `json:"expire_in"`
} `json:"data"`
}
_, err := restyClient.R().SetResult(&result).SetQueryParams(map[string]string{
"client_id": clientId,
"client_secret": clientSecret,
}).Get("/developer/v1/token")
if err != nil {
return nil, err
}
// oppo 环境检测非常慢
time.Sleep(time.Second * 15)
_, err = page.Race().ElementR("h1", "Sign in").Handle(func(e *rod.Element) error {
if !reAuth {
return errors.New("登陆态失效")
}
fmt.Println("show login alert")
if _, err := page.Eval("(msg) => { alert(msg) }", "登录完成以后会自动同步到apkgo"); err != nil {
return err
}
return page.WaitElementsMoreThan(".service-item-open", 0)
}).Element(".service-item-open").MustHandle(func(e *rod.Element) {
// Click this element
//

fmt.Println("oppo login succeed")
}).Do()

return page, err
}

func (oc OppoClient) Do(page *rod.Page, req shared.PublishRequest) error {
return do(page, req)

token := result.Data.AccessToken

c := &Client{restyClient: restyClient, accessToken: token, clientSecret: clientSecret}

return c, nil
}

// 签名
func (c *Client) sign(data url.Values) url.Values {
// 公共参数
data.Set("access_token", c.accessToken)
data.Set("timestamp", strconv.FormatInt(time.Now().Unix(), 10))
// 计算签名
api_sign := calSign(c.clientSecret, data)
data.Set("api_sign", api_sign)

return data
}

// 查询普通包详情
func (c *Client) query(pkgName string) (*app, error) {
data := make(url.Values)
data.Set("pkg_name", pkgName)
var result struct {
Errno int `json:"errno"`
Data *app `json:"data"`
}
if _, err := c.restyClient.R().SetResult(&result).
SetQueryParamsFromValues(c.sign(data)).Get("/resource/v1/app/info"); err != nil {
return nil, err
}
if result.Errno != 0 {
return nil, fmt.Errorf("查询失败基础信息失败 code: %d", result.Errno)
}

return result.Data, nil
}

// 上传文件
func (c *Client) uploadAPK(file string) (*uploadResult, error) {
var result struct {
Errno int `json:"errno"`
Data struct {
UploadUrl string `json:"upload_url"`
Sign string `json:"sign"`
} `json:"data"`
}
// 1. 获取上传url
if _, err := c.restyClient.R().
SetResult(&result).
SetQueryParamsFromValues(c.sign(make(url.Values))).
Get("/resource/v1/upload/get-upload-url"); err != nil {
return nil, err
}

var uploadResult struct {
Errno int `json:"errno"`
Data uploadResult `json:"data"`
}

if _, err := c.restyClient.R().SetResult(&uploadResult).
SetFormData(map[string]string{
"sign": result.Data.Sign,
"type": "apk",
}).
SetFile("file", file).
Post(result.Data.UploadUrl); err != nil {
return nil, err
}

return &uploadResult.Data, nil
}

func (c *Client) publish(req publishRequestParameter) error {

var result struct {
Errno int `json:"errno"`
Data struct {
Success bool `json:"success"`
Message string `json:"message"`
LogId int `json:"logid"`
} `json:"data"`
}

if _, err := c.restyClient.R().
SetResult(&result).
SetFormDataFromValues(c.sign(req.toValues())).
Post("/resource/v1/app/upd"); err != nil {
return err
}
if result.Errno != 0 {
return fmt.Errorf("发布失败 message: %s logid: %d", result.Data.Message, result.Data.LogId)
}

return nil
}

// 获取任务状态

func (c *Client) taskState(packageName string, versionCode string) (*taskBody, error) {
data := make(url.Values)
data.Set("pkg_name", packageName)
data.Set("version_code", versionCode)
var result struct {
Errno int `json:"errno"`
Data *taskBody `json:"data"`
}
if _, err := c.restyClient.R().SetResult(&result).
SetFormDataFromValues(c.sign(data)).
Post("/resource/v1/app/task-state"); err != nil {
return nil, err
}
if result.Errno != 0 {
return nil, fmt.Errorf("查询任务状态失败 code: %d", result.Errno)
}

return result.Data, nil
}
153 changes: 82 additions & 71 deletions cmd/oppo/do.go
Original file line number Diff line number Diff line change
@@ -1,95 +1,106 @@
package oppo

import (
"fmt"
"strings"
"errors"
"strconv"
"time"

"github.com/KevinGong2013/apkgo/cmd/shared"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/proto"
"github.com/ysmood/gson"
)

func do(page *rod.Page, req shared.PublishRequest) error {
func (c *Client) Do(req shared.PublishRequest) error {

js := `async function postData() {
var formData = new FormData()
formData.append('type', 0)
formData.append('limit', 100)
formData.append('offset', 0)
const response = await fetch('https://open.oppomobile.com/resource/list/index.json', {
method: 'POST',
body: formData
})
return response.text()
}`

obj, err := page.Evaluate(&rod.EvalOptions{
ByValue: true,
AwaitPromise: true,
JS: js,
UserGesture: true,
})
param := publishRequestParameter{}

param.PkgName = req.PackageName
param.VersionCode = strconv.Itoa(int(req.VersionCode))

// 1. 获取一批基础信息
app, err := c.query(req.PackageName)
if err != nil {
fmt.Println(err)
return err
}
param.AppName = app.AppName

r := gson.NewFrom(obj.Value.Str())
secondCategoryId, _ := strconv.Atoi(app.SecondCategoryID)
param.SecondCategoryId = secondCategoryId

errno := r.Get("errno").Int()
if errno != 0 {
return fmt.Errorf("auth failed. %s", r.Raw())
}
thirdCategoryId, _ := strconv.Atoi(app.ThirdCategoryID)
param.ThirdCategoryId = thirdCategoryId

var appid string
for _, row := range r.Get("data").Get("rows").Arr() {
if row.Get("pkg_name").Str() == req.PackageName {
appid = row.Get("app_id").Str()
break
}
param.Summary = "[寓小二]公寓系统定制专家" //app.Summary
param.DetailDesc = app.DetailDesc
param.UpdateDesc = req.UpdateDesc
param.PrivacySourceUrl = app.PrivacySourceURL
param.IconUrl = app.IconURL
param.PicUrl = app.PicURL
param.OnlineType = 1
param.TestDesc = "submit by apkgo tool."
param.CopyrightUrl = app.CopyrightURL
param.BusinessUsername = app.BusinessUsername
param.BusinessMobile = app.BusinessMobile
param.BusinessEmail = app.BusinessEmail

ageLevel, _ := strconv.Atoi(app.AgeLevel)
param.AgeLevel = ageLevel

param.AdaptiveType = app.AdaptiveType
param.AdaptiveEquipment = app.AdaptiveEquipment

param.CustomerContact = app.CustomerContact

// 2. 上传apk包
uploadResult, err := c.uploadAPK(req.ApkFile)
if err != nil {
return err
}

if len(appid) == 0 {
return fmt.Errorf("unsupported package. %s", req.PackageName)
cpucode := 0

if len(req.SecondApkFile) > 0 {
cpucode = 32
}

return rod.Try(func() {
page.MustNavigate(fmt.Sprintf("https://open.oppomobile.com/new/mcom#/home/management/app-admin#/resource/update/index?app_id=%s&is_gray=2", appid))
iframe := page.MustElement(`iframe[id="menu_service_main_iframe"]`).MustFrame()
// 判断一下如果有弹窗,先关掉弹窗
time.Sleep(time.Second * 3)
exist, noButton, _ := iframe.Has(`#save-the-draft > div > div > div.modal-body > div:nth-child(2) > button.btn.no`)
if exist {
noButton.MustClick()
}
param.ApkUrl = append(param.ApkUrl, apkInfo{
Url: uploadResult.URL,
Md5: uploadResult.MD5,
CpuCode: cpucode,
})

iframe.MustElement(`textarea[name="update_desc"`).MustSelectAllText().MustInput(req.UpdateDesc)

wait := page.EachEvent(func(e *proto.NetworkResponseReceived) bool {
url := e.Response.URL
if strings.Contains(url, "verify-info.json") {
m := proto.NetworkGetResponseBody{RequestID: e.RequestID}
if r, err := m.Call(page); err == nil {
body := gson.NewFrom(r.Body)
errno := body.Get("errono").Int()
if errno == 0 {
return true
}

if errno == 911046 {
return true
}

}
return false
}
return false
if len(req.SecondApkFile) > 0 {
secondResult, err := c.uploadAPK(req.SecondApkFile)
if err != nil {
return err
}
param.ApkUrl = append(param.ApkUrl, apkInfo{
Url: secondResult.URL,
Md5: secondResult.MD5,
CpuCode: 64,
})
}

go iframe.MustElement(`#auditphasedbuttonclick`).MustClick()
// 3. 发布
if err := c.publish(param); err != nil {
return err
}

wait()
})
times := 0
for {
if times > 10 {
return errors.New("发布失败")
}
time.Sleep(time.Second * 10)
state, err := c.taskState(req.PackageName, strconv.Itoa(int(req.VersionCode)))
if err != nil {
return err
}
// 成功
if state.TaskState == "2" {
return nil
}
if state.TaskState == "3" {
return errors.New(state.ErrMsg)
}
times += 1
}
}
Loading

0 comments on commit 9c01430

Please sign in to comment.