hotcoin.test.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. const {
  2. describe,
  3. test,
  4. expect,
  5. } = require('@jest/globals')
  6. const { WebDriver } = require('../common/web_driver')
  7. const {
  8. inflate,
  9. deflate,
  10. } = require('pako')
  11. const fs = require('fs')
  12. const path = require('node:path')
  13. // Static code + Reflection + .proto parser
  14. const protobuf = require('protobufjs')
  15. const desc = require('./hotconi.proto')
  16. const timeout = 9999999
  17. async function sleep (ms) {
  18. return new Promise(resolve => setTimeout(resolve, ms))
  19. }
  20. describe('hotcoin站点测试', () => {
  21. let targetInfo = {
  22. 'targetId': '',
  23. 'type': '',
  24. 'title': '',
  25. 'url': '',
  26. 'attached': true,
  27. 'canAccessOpener': false,
  28. 'browserContextId': '',
  29. }
  30. const driver = new WebDriver({
  31. // port: 23035,
  32. })
  33. const init = async () => {
  34. // await driver.setProxy('socks://localhost:8880')
  35. await driver.setProxy('http://172.16.1.132:3128')
  36. await driver.builder()
  37. let target = await driver.cdp.Target
  38. targetInfo = await target.getTargetInfo()
  39. console.info('info>>>>', targetInfo)
  40. }
  41. const baseUrl = 'https://www.hotcoin.com/'
  42. const prefix = 'hotcoin'
  43. let cookiesPath = path.join(__dirname, 'tmp', prefix + '_cookies')
  44. let storagePath = path.join(__dirname, 'tmp', prefix + '_storage')
  45. test('测试打开', async (done) => {
  46. await init()
  47. const page = driver.cdp.Page
  48. page.navigate({ url: baseUrl })
  49. done()
  50. }, timeout)
  51. const login = async (Storage, Runtime) => {
  52. let cookie = '', localStorage = ''
  53. if (fs.existsSync(cookiesPath)) {
  54. cookie = fs.readFileSync(cookiesPath, 'utf8')
  55. }
  56. // 借助localStorage中的jwt信息登录
  57. if (fs.existsSync(storagePath)) {
  58. localStorage = fs.readFileSync(storagePath, 'utf8')
  59. }
  60. if (cookie) {
  61. await Storage.setCookies(JSON.parse(cookie))
  62. }
  63. if (localStorage) {
  64. // let script = `
  65. let storage = JSON.parse(`${localStorage}`) || {}
  66. // for (const k in storage) {
  67. // localStorage.setItem(k,storage[k]);
  68. // }
  69. // `
  70. // script=`
  71. // localStorage.setItem('aaa','bbb')
  72. // `
  73. // await Runtime.evaluate({
  74. // expression: `localStorage.setItem('aaa','>>>>>')`,
  75. // // awaitPromise: true,
  76. // });
  77. for (const k in storage) {
  78. await Runtime.evaluate({
  79. expression: `localStorage.setItem(\`${k}\`, \`${storage[k]}\`)`,
  80. // awaitPromise: true,
  81. })
  82. }
  83. }
  84. }
  85. test('自动登录', async (done) => {
  86. await init()
  87. const {
  88. Storage,
  89. Page,
  90. Runtime,
  91. } = driver.cdp
  92. await Runtime.enable()
  93. try {
  94. Page.navigate({ url: baseUrl })
  95. await login(Storage, Runtime)
  96. let t = Date.now()
  97. let s = setInterval(async () => {
  98. let cookies = await Storage.getCookies()
  99. fs.writeFileSync(cookiesPath, JSON.stringify(cookies), 'utf8')
  100. let localStorage = await Runtime.evaluate({
  101. expression: 'JSON.stringify(localStorage)',
  102. })
  103. fs.writeFileSync(storagePath, localStorage?.result?.value, 'utf8')
  104. if (Date.now() - t > 5 * 60 * 1000) {
  105. clearInterval(s)
  106. }
  107. }, 1000)
  108. } catch (e) {
  109. console.error(e)
  110. }
  111. await sleep(timeout)
  112. done()
  113. }, timeout)
  114. test('监听wss', async (done) => {
  115. await init()
  116. const {
  117. Storage,
  118. Page,
  119. Network,
  120. Runtime,
  121. } = driver.cdp
  122. Network.enable()
  123. Page.navigate({ url: baseUrl })
  124. await login(Storage, Runtime)
  125. const decode = (data) => {
  126. let res = inflate(data/* , { to: 'string' } */)
  127. console.info('解压缩结果:', res)
  128. switch (res) {
  129. case 'pong':
  130. return res
  131. default: {
  132. // function decodeProtobufToJson(buffer) {
  133. // try {
  134. // // 创建一个通用的解码器
  135. // const Reader = protobuf.Reader;
  136. // const reader = Reader.create(buffer);
  137. //
  138. // const result = {};
  139. //
  140. // while (reader.pos < reader.len) {
  141. // const tag = reader.uint32();
  142. // const fieldId = tag >>> 3;
  143. // const wireType = tag & 7;
  144. //
  145. // // 根据 wireType 解析数据
  146. // let value;
  147. // switch (wireType) {
  148. // case 0: // varint
  149. // value = reader.uint64();
  150. // break;
  151. // case 1: // 64-bit
  152. // value = reader.fixed64();
  153. // break;
  154. // case 2: // length-delimited (string, bytes, embedded messages)
  155. // value = reader.bytes();
  156. // // 尝试将 bytes 转换为字符串
  157. // try {
  158. // value = new TextDecoder().decode(value);
  159. // } catch(e) {}
  160. // break;
  161. // case 5: // 32-bit
  162. // value = reader.fixed32();
  163. // break;
  164. // default:
  165. // reader.skipType(wireType);
  166. // continue;
  167. // }
  168. //
  169. // result[`field${fieldId}`] = value;
  170. // }
  171. //
  172. // return result;
  173. //
  174. // } catch (error) {
  175. // console.error("解析错误:", error);
  176. // return null;
  177. // }
  178. // }
  179. //
  180. // let object = decodeProtobufToJson(res)
  181. // let root = protobuf.Root.fromJSON(desc.common)
  182. //
  183. // // 查找 Response 消息类型
  184. // let ResponseType = root.lookupType('Response')
  185. //
  186. // // 解码数据
  187. // let message = ResponseType.decode(res)
  188. //
  189. // // 转换为普通的 JavaScript 对象
  190. // let object = ResponseType.toObject(message, {
  191. // longs: String, // 将 long 转换为字符串
  192. // enums: String, // 将枚举转换为字符串
  193. // bytes: String, // 将 bytes 转换为字符串
  194. // defaults: true, // 包含默认值
  195. // objects: true,
  196. // arrays: true,
  197. // json: true,
  198. // oneofs: true,
  199. // })
  200. // console.info('=00==>', object)
  201. // let key = (object.ch || '') + '.json'
  202. // let key = (object.ch || '') + '.js'
  203. //root = protobuf.Root.fromJSON(require('./hotconi/proto/' + key))
  204. // root = protobuf.loadSync(path.resolve('./hotconi/proto/' + key))
  205. // root = require('./hotconi/proto/' + key)
  206. let root = require('./hotconi/proto/market.v2.trade.area.tickers')
  207. // Response = root.lookupType(object.ch + '.Response')
  208. // message = Response.decode(res)
  209. //
  210. // object = Response.toObject(message, {
  211. // longs: String,
  212. // enums: String,
  213. // bytes: String,
  214. // defaults: true,
  215. // arrays: true,
  216. // objects: true,
  217. // oneofs: true,
  218. // })
  219. console.info('=11==>', 'object')
  220. // return JSON.parse(res)
  221. }
  222. }
  223. }
  224. // 一个二进制数据流还原
  225. // opcode number WebSocket message opcode.
  226. // mask boolean WebSocket message mask.
  227. // payloadData string WebSocket message payload data. If the opcode is 1, this is a text message and payloadData is a UTF-8 string.
  228. // If the opcode isn't 1, then payloadData is a base64 encoded string representing binary data.
  229. const formatResponse = (params = {
  230. opcode: 1,
  231. mask: false,
  232. payloadData: '',
  233. }) => {
  234. try {
  235. if (!params.response?.opcode) {
  236. return params.response
  237. }
  238. if (params.response?.opcode === 1) {
  239. return params.response.payloadData
  240. }
  241. const binaryData = Buffer.from(params.response.payloadData, 'base64')
  242. console.info('结果解析:', decode(binaryData))
  243. } catch (e) {
  244. console.error(e)
  245. }
  246. }
  247. Network.webSocketCreated(params => {
  248. console.log('WebSocket created:', params.url)
  249. })
  250. // 订阅 Network.webSocketFrameSent 和 Network.webSocketFrameReceived 事件,监听 WebSocket 帧的发送和接收:
  251. Network.webSocketFrameSent(params => {
  252. console.log('WebSocket frame sent:', params.response)
  253. // formatResponse(params)
  254. })
  255. Network.webSocketFrameReceived(params => {
  256. console.log('WebSocket frame received:', params.response)
  257. // formatResponse(params)
  258. })
  259. // 订阅 Network.webSocketClosed 事件,监听 WebSocket 连接的关闭:
  260. Network.webSocketClosed(params => {
  261. console.log('WebSocket closed:', params.closeCode, params.closeReason)
  262. // formatResponse(params)
  263. })
  264. // 订阅Network.webSocketWillSendHandshakeRequest事件,拦截WebSocket连接的握手请求:
  265. Network.webSocketWillSendHandshakeRequest(params => {
  266. console.log('WebSocket handshake request:', params.request)
  267. // formatResponse(params)
  268. })
  269. // 订阅Network.webSocketHandshakeResponseReceived事件,拦截WebSocket连接的握手响应:
  270. Network.webSocketHandshakeResponseReceived(params => {
  271. console.log('WebSocket handshake response:', params.response)
  272. let data = formatResponse(params)
  273. console.info('webSocketHandshakeResponseReceived解析结果:', data)
  274. })
  275. // 订阅Network.webSocketFrameSent和Network.webSocketFrameReceived事件,拦截WebSocket数据帧的发送和接收:
  276. Network.webSocketFrameSent(params => {
  277. console.log('WebSocket frame sent:', params.response)
  278. // formatResponse(params)
  279. })
  280. Network.webSocketFrameReceived(params => {
  281. // console.log('WebSocket frame received:', params.response)
  282. console.info('webSocketFrameReceived 回复', params.response)
  283. let data = formatResponse(params)
  284. console.info('webSocketFrameReceived解析结果:', data)
  285. })
  286. Network.webSocketFrameError(params => {
  287. console.log('WebSocket frame error:', params.response)
  288. let data = formatResponse(params)
  289. console.info('webSocketFrameError解析结果:', data)
  290. })
  291. await sleep(timeout)
  292. done()
  293. }, timeout)
  294. test('拆包', done => {
  295. let str = `0{"sid":"b6535f35-3047-4f12-9cd3-c526b39cd019","upgrades":["websocket"],"pingInterval":30000,"pingTimeout":60000}`
  296. while (/^\d/.test(str)) {
  297. str = str.substring(1)
  298. }
  299. try {
  300. JSON.parse(str)
  301. } catch (e) {
  302. console.error(e)
  303. }
  304. console.info(str)
  305. done()
  306. })
  307. })