通过分析源码可以得到以下MatchPlayInfo序列的生成代码
const protobuf = require("protobufjs");var i = protobuf.Reader, a = protobuf.Writer, r = protobuf.util;MatchStepInfo = function () { function t(t) { if (t) for (var e = Object.keys(t), o = 0; o < e.length; ++o) null != t[e[o]] && (this[e[o]] = t[e[o]]) } return t.prototype.chessIndex = 0, t.prototype.timeTag = 0, t.create = function ( e) { return new t(e) }, t.encode = function (t, e) { return e || (e = a.create()), null != t.chessIndex && Object.hasOwnProperty .call(t, "chessIndex") && e.uint32(8).int32(t.chessIndex), null != t.timeTag && Object.hasOwnProperty.call(t, "timeTag") && e.uint32( 16).int32(t.timeTag), e }, t.decode = function (t, e) { t instanceof i || (t = i.create(t)); for (var o = void 0 === e ? t.len : t.pos + e, n = new MatchStepInfo; t .pos < o;) { var a = t.uint32(); switch (a > 3) { case 1: n.chessIndex = t.int32(); break; case 2: n.timeTag = t.int32(); break; default: t.skipType(7 & a) } } return n }, t}(), MatchPlayInfo = function () { function t(t) { if (this.stepInfoList = [], t) for (var e = Object.keys(t), o = 0; o < e.length; ++o) null != t[e[o]] && (this[e[o]] = t[e[o]]) } return t.prototype.gameType = 0, t.prototype.mapId = 0, t.prototype.mapSeed = 0, t.prototype.stepInfoList = r.emptyArray, t.create = function (e) { return new t(e) }, t.encode = function (t, e) { if (e || (e = a.create()), null != t.gameType && Object.hasOwnProperty.call( t, "gameType") && e.uint32(8).int32(t.gameType), null != t.mapId && Object.hasOwnProperty.call(t, "mapId") && e.uint32(16).int32(t.mapId), null != t.mapSeed && Object.hasOwnProperty.call(t, "mapSeed") && e.uint32( 24).int32(t.mapSeed), null != t.stepInfoList && t.stepInfoList.length ) for (var o = 0; o < t.stepInfoList.length; ++o) MatchStepInfo .encode(t.stepInfoList[o], e.uint32(34).fork()).ldelim(); return e }, t.decode = function (t, e) { t instanceof i || (t = i.create(t)); for (var o = void 0 === e ? t.len : t.pos + e, n = new MatchPlayInfo; t .pos < o;) { var a = t.uint32(); switch (a > 3) { case 1: n.gameType = t.int32(); break; case 2: n.mapId = t.int32(); break; case 3: n.mapSeed = t.int32(); break; case 4: n.stepInfoList && n.stepInfoList.length || (n.stepInfoList = []), n.stepInfoList.push(MatchStepInfo.decode(t, t.uint32())); break; default: t.skipType(7 & a) } } return n }, t}()var operationList = []function addOp(t, e) { //增加操作 void 0 === e && (e = -100); var o = { id: t, // 操作卡片的id,从levelData第一层开始按顺序编号 time: Date.now() // 操作时间 }; operationList.push(o)}function sleep(delay) { for (var t = Date.now(); Date.now() - t <= delay;);}let range = n => [...Array(n).keys()]for (let i of range(50)) { // 生成了50次操作 addOp(i); sleep(Math.random() * 10); // 模拟操作过程中的等待}console.log(operationList)for (var u = operationList, p = [], d = 0, h = 0; h < u.length; h++) // 把时间戳转换为两次操作的间隔 p.push({ chessIndex: u[h].id, timeTag: 0 == d ? 0 : u[h].time - d }), d = u[h].time;console.log(p)GAMEDAILY = 3GAMETOPIC = 4for (var f = { gameType: GAMEDAILY, stepInfoList: p }, y = MatchPlayInfo.create(f), v = MatchPlayInfo.encode(y).finish(), b = "", _ = 0; _ < v.length; _++) b += String.fromCharCode(v[_]); // 序列化var data = Buffer.from(b).toString('base64');console.log(data);data = Buffer.from(data, 'base64');console.log(data);console.log(MatchPlayInfo.decode(data));
分析一下MatchPlayInfo的生成操作
首先可以得知MatchPlayInfo是由stepInfoList和gameType组成的,stepInfoList里有两个参数分别是chessIndex和timeTag,分别记录点击卡片id和两次操作间隔
观察MatchPlayInfo.encode可以看到[JavaScript] 纯文本查看 复制代码
e = a.create()
会创建一个protobuf.Writer对象
[JavaScript] 纯文本查看 复制代码e.uint32(8).int32(t.gameType)
[JavaScript] 纯文本查看 复制代码
MatchStepInfo.encode(t.stepInfoList[o], e.uint32(34).fork()).ldelim()
1.png
然而生成的序列无法增加通关次数,研究了一下发现stepInfoList里的值大于127的数值是错误的,不知道是什么原因,于是过滤掉大于127的数可以成功增加通关次数
python代码如下
[Python] 纯文本查看 复制代码
import structimport base64import requestsheaders = { 't': '', 'User-Agent': '', 'Referer': 'https://servicewechat.com/wx141bfb9b73c970a9/23/page-frame.html'}url = 'https://cat-match.easygame2021.com/sheep/v1/game/personal_info?'r = requests.get(url, headers=headers)print(r.json())url = 'https://cat-match.easygame2021.com/sheep/v1/game/map_info_ex?matchType=3'r = requests.get(url, headers=headers)map_md5 = r.json()['data']['map_md5'][1]url = f'https://cat-match-static.easygame2021.com/maps/{map_md5}.txt' # 由于每天获取的地图不一样,需要计算地图大小r = requests.get(url)levelData = r.json()['levelData']p = []for h in range(len(sum(levelData.values(), []))): # 生成操作序列 p.append({'chessIndex': 127 if h > 127 else h, 'timeTag': 127 if h > 127 else h})GAME_DAILY = 3GAME_TOPIC = 4data = struct.pack('BB', 8, GAME_DAILY)for i in p: c, t = i.values() data += struct.pack('BBBBBB', 34, 4, 8, c, 16, t)MatchPlayInfo = base64.b64encode(data)print(MatchPlayInfo)url = 'https://cat-match.easygame2021.com/sheep/v1/game/game_over_ex?'r = requests.post(url, headers=headers, json={'rank_score': 1, 'rank_state': 1, 'rank_time': 1, 'rank_role': 1, 'skin': 1, 'MatchPlayInfo': MatchPlayInfo})print(r.json())url = 'https://cat-match.easygame2021.com/sheep/v1/game/personal_info?'r = requests.get(url, headers=headers)print(r.json())