├── .gitignore ├── Screenshots ├── A0D7D8D2E1C81F69439D4263E7559058E0ED1F14.png ├── AFEF4592952FF90CBBE8E0D15301F966BBEEF546.png └── F288684AD35F9D3E9B619706736874511C6DB4AA.png ├── go.mod ├── go.sum ├── main.go └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | -------------------------------------------------------------------------------- /Screenshots/A0D7D8D2E1C81F69439D4263E7559058E0ED1F14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WankkoRee/BilibiliSuitRushBuy/35a054a17b672272bdea15012abf95395ab45770/Screenshots/A0D7D8D2E1C81F69439D4263E7559058E0ED1F14.png -------------------------------------------------------------------------------- /Screenshots/AFEF4592952FF90CBBE8E0D15301F966BBEEF546.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WankkoRee/BilibiliSuitRushBuy/35a054a17b672272bdea15012abf95395ab45770/Screenshots/AFEF4592952FF90CBBE8E0D15301F966BBEEF546.png -------------------------------------------------------------------------------- /Screenshots/F288684AD35F9D3E9B619706736874511C6DB4AA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WankkoRee/BilibiliSuitRushBuy/35a054a17b672272bdea15012abf95395ab45770/Screenshots/F288684AD35F9D3E9B619706736874511C6DB4AA.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module BilibiliSuitRushBuy 2 | 3 | go 1.17 4 | 5 | require github.com/buger/jsonparser v1.1.1 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= 2 | github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= 3 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/buger/jsonparser" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "strconv" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | func init() { 15 | //proxyUrl, err := url.Parse("http://127.0.0.1:10801") 16 | //if err != nil { 17 | // log.Fatal(err) 18 | // return 19 | //} 20 | //http.DefaultTransport = &http.Transport{Proxy: http.ProxyURL(proxyUrl)} 21 | } 22 | 23 | func orderCreate(itemId int, addMonth int, buyNum int64, csrf string, apiCookie string, userAgent string, accessKey string) string { 24 | req, err := http.NewRequest("POST", "https://api.bilibili.com/x/garb/trade/create", strings.NewReader(fmt.Sprintf("item_id=%d&platform=android¤cy=bp&add_month=%d&buy_num=%d&coupon_token=&hasBiliapp=true&csrf=%s", itemId, addMonth, buyNum, csrf))) 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | req.Header.Set("native_api_from", "h5") 29 | req.Header.Set("Referer", fmt.Sprintf("https://www.bilibili.com/h5/mall/suit/detail?id=%d&navhide=1", itemId)) 30 | req.Header.Set("User-Agent", userAgent) 31 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8") 32 | 33 | req.Header.Set("Cookie", apiCookie) 34 | req.Header.Set("X-CSRF-TOKEN", csrf) 35 | resp, err := (&http.Client{}).Do(req) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | respb, err := ioutil.ReadAll(resp.Body) 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | log.Println(string(respb)) 44 | 45 | payData0, err := jsonparser.GetString(respb, "data", "pay_data") 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | payData1, err := jsonparser.Set([]byte(payData0), []byte(`"`+accessKey+`"`), "accessKey") 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | payData2, err := jsonparser.Set(payData1, []byte(`"tv.danmaku.bili"`), "appName") 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | payData1, err = jsonparser.Set(payData2, []byte("6560300"), "appVersion") 58 | if err != nil { 59 | log.Fatal(err) 60 | } 61 | payData2, err = jsonparser.Set(payData1, []byte(`"ANDROID"`), "device") 62 | if err != nil { 63 | log.Fatal(err) 64 | } 65 | payData1, err = jsonparser.Set(payData2, []byte(`"3"`), "deviceType") 66 | if err != nil { 67 | log.Fatal(err) 68 | } 69 | payData2, err = jsonparser.Set(payData1, []byte(`"WiFi"`), "network") 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | payData1, err = jsonparser.Set(payData2, []byte(`"bp"`), "payChannel") 74 | if err != nil { 75 | log.Fatal(err) 76 | } 77 | payData2, err = jsonparser.Set(payData1, []byte("99"), "payChannelId") 78 | if err != nil { 79 | log.Fatal(err) 80 | } 81 | payData1, err = jsonparser.Set(payData2, []byte(`"bp"`), "realChannel") 82 | if err != nil { 83 | log.Fatal(err) 84 | } 85 | payData2, err = jsonparser.Set(payData1, []byte(`"1.4.9"`), "sdkVersion") 86 | if err != nil { 87 | log.Fatal(err) 88 | } 89 | return string(payData2) 90 | } 91 | 92 | func pay(payData string, userAgent string, Buvid string, DeviceID string, fpLocal string, fpRemote string, sessionId string, deviceFingerprint string) (string, error) { 93 | req, err := http.NewRequest("POST", "https://pay.bilibili.com/payplatform/pay/pay", strings.NewReader(payData)) 94 | if err != nil { 95 | return "", err 96 | } 97 | req.Header.Set("cLocale", "zh_CN") 98 | req.Header.Set("sLocale", "zh_CN") 99 | req.Header.Set("Buvid", Buvid) 100 | req.Header.Set("Device-ID", DeviceID) 101 | req.Header.Set("fp_local", fpLocal) 102 | req.Header.Set("fp_remote", fpRemote) 103 | req.Header.Set("session_id", sessionId) 104 | req.Header.Set("deviceFingerprint", deviceFingerprint) 105 | req.Header.Set("buildId", "6560300") 106 | req.Header.Set("env", "prod") 107 | req.Header.Set("APP-KEY", "android64") 108 | req.Header.Set("User-Agent", userAgent) 109 | req.Header.Set("APP-KEY", "android64") 110 | req.Header.Set("bili-bridge-engine", "cronet") 111 | req.Header.Set("Content-Type", "application/json") 112 | 113 | resp, err := (&http.Client{}).Do(req) 114 | if err != nil { 115 | return "", err 116 | } 117 | respb, err := ioutil.ReadAll(resp.Body) 118 | if err != nil { 119 | return "", err 120 | } 121 | log.Println(string(respb)) 122 | payChannelParam, err := jsonparser.GetString(respb, "data", "payChannelParam") 123 | if err != nil { 124 | return "", err 125 | } 126 | return payChannelParam, nil 127 | } 128 | 129 | func payBp(payChannelParam string, userAgent string, Buvid string, DeviceID string, fpLocal string, fpRemote string, sessionId string, deviceFingerprint string) (string, error) { 130 | req, err := http.NewRequest("POST", "https://pay.bilibili.com/paywallet/pay/payBp", strings.NewReader(payChannelParam)) 131 | if err != nil { 132 | return "", err 133 | } 134 | req.Header.Set("cLocale", "zh_CN") 135 | req.Header.Set("sLocale", "zh_CN") 136 | req.Header.Set("Buvid", Buvid) 137 | req.Header.Set("Device-ID", DeviceID) 138 | req.Header.Set("fp_local", fpLocal) 139 | req.Header.Set("fp_remote", fpRemote) 140 | req.Header.Set("session_id", sessionId) 141 | req.Header.Set("deviceFingerprint", deviceFingerprint) 142 | req.Header.Set("buildId", "6560300") 143 | req.Header.Set("env", "prod") 144 | req.Header.Set("APP-KEY", "android64") 145 | req.Header.Set("User-Agent", userAgent) 146 | req.Header.Set("APP-KEY", "android64") 147 | req.Header.Set("bili-bridge-engine", "cronet") 148 | req.Header.Set("Content-Type", "application/json") 149 | 150 | resp, err := (&http.Client{}).Do(req) 151 | if err != nil { 152 | return "", err 153 | } 154 | respb, err := ioutil.ReadAll(resp.Body) 155 | if err != nil { 156 | return "", err 157 | } 158 | return string(respb), err 159 | } 160 | 161 | func buy(payData string, userAgent string, Buvid string, DeviceID string, fpLocal string, fpRemote string, sessionId string, deviceFingerprint string) (string, error) { 162 | payChannelParam, err := pay(payData, userAgent, Buvid, DeviceID, fpLocal, fpRemote, sessionId, deviceFingerprint) 163 | if err != nil { 164 | return "", err 165 | } 166 | 167 | payResult, err := payBp(payChannelParam, userAgent, Buvid, DeviceID, fpLocal, fpRemote, sessionId, deviceFingerprint) 168 | if err != nil { 169 | return "", err 170 | } 171 | return payResult, nil 172 | } 173 | 174 | func watch(itemId int, targetId int64, limitV int64, buyNum *int64) bool { 175 | log.Println("开始观望") 176 | for { 177 | resp, err := http.Get(fmt.Sprintf("https://api.bilibili.com/x/garb/mall/item/suit/v2?item_id=%d&part=suit", itemId)) 178 | if err != nil { 179 | log.Fatal(err) 180 | return false 181 | } 182 | respb, err := ioutil.ReadAll(resp.Body) 183 | if err != nil { 184 | log.Fatal(err) 185 | return false 186 | } 187 | SuitRecentResultCode, err := jsonparser.GetInt(respb, "code") 188 | if err != nil { 189 | log.Fatal(err) 190 | return false 191 | } 192 | if SuitRecentResultCode != 0 { 193 | log.Fatal(respb) 194 | return false 195 | } 196 | 197 | saleQuantity, err := jsonparser.GetString(respb, "data", "item", "properties", "sale_quantity") 198 | if err != nil { 199 | log.Fatal(err) 200 | return false 201 | } 202 | saleQuantityI, err := strconv.ParseInt(saleQuantity, 10, 64) 203 | if err != nil { 204 | log.Fatal(err) 205 | return false 206 | } 207 | saleSurplus, err := jsonparser.GetInt(respb, "data", "sale_surplus") 208 | if err != nil { 209 | log.Fatal(err) 210 | return false 211 | } 212 | nowId := saleQuantityI - saleSurplus 213 | log.Println(nowId) 214 | 215 | if nowId < targetId && nowId+*buyNum >= targetId { 216 | log.Println("开始抢购") 217 | *buyNum = targetId - nowId 218 | return true 219 | } else if nowId >= targetId { 220 | log.Println("已经错过") 221 | return false 222 | } 223 | 224 | if nowId < targetId-limitV { 225 | time.Sleep(time.Duration(2) * time.Second) 226 | } 227 | } 228 | } 229 | 230 | func main() { 231 | // 请务必确保脚本运行时号里的B币充足,>=永久价格*buyNum 232 | 233 | var limitV int64 = 20 // 观望时无CD刷新的临界值,当前id与目标id相差小于等于临界值时,脚本观望将不再sleep,开启暴风刷新状态 234 | // 个人推荐:装扮热门时,id冷门10, id中等70, id热门140;过小容易错过,过大容易ban IP,请自行合理预估 235 | 236 | itemId := 33998 // 2022拜年纪 33998 237 | addMonth := -1 // -1:永久, 1:一个月,应该不会有人买一个月吧,这个我功能性测试时候用的,就是为了测试能省点钱 238 | var targetId int64 = 18168 // 目标粉丝编号,豹子号和炸弹号基本上都有人x10、x10地抢,所以如果有这类需求的话就把下面的参数改大点,大于等于其他人即可,就是比较烧钱 239 | var buyNum int64 = 1 // 一次性买几个,当需要抢热门号时建议多买几个,因为号贩子就是这样的,由于是一次性购入,脚本无法比其快,所以只能以暴制暴;如果是冷门id的话那就无所谓了 240 | 241 | // 以下参数在余额不足时抓取下单请求即可获取,在https://api.bilibili.com/x/garb/trade/create数据包中可找到 242 | // 以下参数在余额充足时抓取下单请求亦可获取,在https://api.bilibili.com/x/garb/trade/create数据包中可找到 243 | // 余额充足时务必开启发送断点,切勿放行https://pay.bilibili.com/paywallet/pay/payBp,否则将会购买成功 244 | apiCsrf := "" 245 | apiCookie := "" 246 | apiUserAgent := "" 247 | // 以下参数在余额不足时抓取下单请求即可获取,在https://pay.bilibili.com/paywallet/recharge/getRechargePanel/v2数据包中可找到 248 | // 以下参数在余额充足时抓取下单请求亦可获取,在https://pay.bilibili.com/payplatform/pay/pay数据包中可找到 249 | // 余额充足时务必开启发送断点,切勿放行https://pay.bilibili.com/paywallet/pay/payBp,否则将会购买成功 250 | apiAccessKey := "" 251 | payUserAgent := "" 252 | payBuvid := "" 253 | payDeviceID := "" 254 | payFpLocal := "" 255 | payFpRemote := "" 256 | paySessionId := "" 257 | payDeviceFingerprint := "" 258 | 259 | if watch(itemId, targetId, limitV, &buyNum) { // 观望,并且传递最多需要买几个,不使用Cookie,防止被临时ban号 260 | payData := orderCreate(itemId, addMonth, buyNum, apiCsrf, apiCookie, apiUserAgent, apiAccessKey) // 下单 261 | payResult, err := buy(payData, payUserAgent, payBuvid, payDeviceID, payFpLocal, payFpRemote, paySessionId, payDeviceFingerprint) // 购买 262 | if err != nil { 263 | log.Fatal(err) 264 | } 265 | log.Println(payResult) 266 | } 267 | 268 | log.Println("End.") 269 | } 270 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # BilibiliSuitRushBuy[哔哩哔哩装扮抢购脚本] 2 | 3 | ## 这个脚本为啥有两个分支? 4 | 5 | - [`master`](https://github.com/WankkoRee/BilibiliSuitRushBuy/tree/master) 分支为蹲号抢Id模式 6 | ```shell 7 | git clone -b master https://github.com/WankkoRee/BilibiliSuitRushBuy.git 8 | ``` 9 | - [`withoutIDRequirement`](https://github.com/WankkoRee/BilibiliSuitRushBuy/tree/withoutIDRequirement) 分支为定时抢购模式 10 | ```shell 11 | git clone -b withoutIDRequirement https://github.com/WankkoRee/BilibiliSuitRushBuy.git 12 | ``` 13 | 14 | ## 这个脚本和其他仓库有什么不同? 15 | 16 | 1. 本项目采用`Golang`编写,比其他采用`Python`编写的脚本更加快速 17 | 2. 本脚本功能齐全,可以提前开启并在合理配置参数时保证不 ban IP 18 | 3. 本脚本功能齐全,可以实现抢指定装扮Id 19 | 4. 本脚本功能齐全,可以实现单个订单购买多个同一装扮,使得抢指定装扮Id的成功率大大增加 20 | 5. 还有其他细节部分请自行阅读源码发现,不予列出... 21 | 22 | ## 关于数据包参数的获取? 23 | 24 | - 如果B币余额不足,那么使用任意抓包工具皆可在环境配置正确的情况下获取到所有的所需参数 25 | - 如果B币余额充足,请务必使用诸如`Fiddler`等带有 发送断点 功能的抓包工具进行抓取所需参数,若不开启发送断点那么真的会在抓包时直接购买成功 26 | - 当然如果家里有矿不在意这些小钱的话,那么随便用啥抓都行,不就是一个装扮钱吗 27 | 28 | ## 能不能来点抓包的demo图? 29 | 30 | > 以下图片**蓝色涂描部分**包含脚本中的`itemId`、`apiCsrf`、`apiCookie`、`apiUserAgent` 31 | > ![api](./Screenshots/AFEF4592952FF90CBBE8E0D15301F966BBEEF546.png) 32 | 33 | > 以下图片分别为余额不足时和余额充足时的数据包,**蓝色涂描部分**皆包含脚本中的`apiAccessKey`、`payUserAgent`、`payBuvid`、`payDeviceID`、`payFpLocal`、`payFpRemote`、`paySessionId`、`payDeviceFingerprint` 34 | > ![api](./Screenshots/A0D7D8D2E1C81F69439D4263E7559058E0ED1F14.png) 35 | > ![api](./Screenshots/F288684AD35F9D3E9B619706736874511C6DB4AA.png) 36 | 37 | ## 这个仓库的代码为什么那么烂? 38 | 39 | 这是我刚开始学习`Golang`并且第一次用于实践用途,所以代码质量难以保证,如果觉得有任何可以改进的部分欢迎提交pr。 40 | 41 | ## 脚本用途声明 42 | 43 | ### 仅限用于个人自身对B站装扮的需求,不可对本脚本及本脚本的产出进行贩卖 44 | ### 严禁装扮贩子使用本脚本,否则任何法律问题请自行解决,与本人及本脚本无关 45 | --------------------------------------------------------------------------------