年更人又来了(
weverse是一个韩国开发的DM私聊系统,集成了很多fan club的功能,提供了xox向群组发送消息这么一个平台,上面可以用来订阅一些xox的message,但是有些时候我不想用它的app看DM,而且它的自带翻译系统也比较,emmmmmm,垃圾;又或者有时候我想做一个转发的bot,这个时候就需要一些其他方法来获取这些DM信息。
首先是登录系统,登录系统使用了一套Google reCAPTCHA v2 (Invisible/Enterprise) 验证请求,由它生成了验证用的Token,这一段不太能越过去所以我放弃了在代码端模拟登录的行为(懒,自己登录抓一下token吧)
登录以后我们需要获取两个token,一个是accessToken,一个是refreshToken,这个refreshToken后面再说,之后所有的操作都必须携带这个accessToken。
在有了accessToken以后,第一个需要获取的就是订阅了的rooms信息,这一个由接口/weverse/wevweb/dm/v2.0/my/rooms来实现,这是一个Get请求,用来获取订阅的rooms,这里就会遇到wvs这个系统最麻烦的地方之一,就是请求的param,param一共需要8个,一个都不能缺,分别是
GET /weverse/wevweb/dm/v2.0/my/rooms?appId={appId}&language=ja&os=WEB&platform=WEB&transLang=ja&wpf=pc&wmd=7prwCTCA1x%2fpFIH1BNn8IKW%2bHfc%3d&wmsgpad=1770964579326
而它的headers为
host: global.apis.naver.com
accept: */*
accept-encoding: gzip, deflate, br
accept-language: ja-JP,ja;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0 RestSharp/113.1.0.0
wev-device-id: {wev-device-id}
wev-open-community: A
wev-wdm-v2: on
wev-timezone-id: Asia/Tokyo
wev-use-sphere: on
origin: https://weverse.io
referer: https://weverse.io/
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
te: trailers
authorization: Bearer {accessToken}
其中有一些是很好懂的,param中,appId是一个固定值,language、os、platform、wpf都很好懂,transLang是需要翻译的目标语言,wmsgpad一眼可以看出是请求的时间戳,那么wmd是什么?
结论是这是一个加密签名,使用这个请求的所有param(除了wmd和wmsgpad)和时间戳生成,它引用了一个加密密钥来生成这个加密签名从而达到保护请求不被篡改的作用。
从前端分析中可以得到,这个密钥是一个硬编码的值,wmd就是基于这个密钥生成的HMAC-SHA1 算法签名,它依赖于三个部分
- 请求的完整相对路径,但是这个相对路径并非是完整的相对路径
/weverse/wevweb/dm/v2.0/my/rooms,而是相对于 API 基础路径的/dm/v2.0/my/rooms,而且在计算签名前,需要先将param给全部按照首字母排序,这样计算出来的签名才是正确的 - 时间戳
- 密钥
当解析完了这一步,剩下的步骤就很简单了,我们只需要把获取到的rooms里面的roomId取出来,这就是需要获取信息的关键。
获取到了roomId之后,就需要获取room的message,由接口获取
GET /weverse/wevweb/dm/v2.0/messages?appId={appId}&language=zh-cn&os=WEB&platform=WEB&roomId={roomId}&transLang=ja&wpf=pc&wmd=m8vpnCeHntspAsuYQRePRrzFRh4%3D&wmsgpad=1770702447473
headers与之前保持一致
这个请求比较重要的param就是roomId了,还有两个隐藏参数,一个是prev,一个是after,这两个的值都是时间戳,很好理解,就是获取某个时间戳之前或者之后的消息,如果不加这两个参数,那么大约会获取从现在往前推的100条消息记录
获取的消息有两大类,一类是普通的文字,一类是媒体
媒体类型又分为,photo(图片),audio(语言),video(视频),snippet(超链接),它们由一个类似xml格式的段落包裹,举个栗子
<dm:photo imageUrl="https://phinf.wevpstatic.net/MjAyNjAyMDNfMjA1/MDAxNzcwMTI0MjA1NTQ5.9fzXNeyFH-doO1rXm9pIvDHsWFVI0wpj7_xD3Y6OQegg.WMkNQ-Sl2QPyO--YMVE31gtJHtEB-OlvUuO0OFY87hIg.JPEG/image.jpg" width="3024" height="4032" />
<dm:snippet linkUrl="https://www.tiktok.com/@17_miyuu_1112/video/7602955010942684437?_r=1&_d=secCgYIASAHKAESPgo832bPcSZV8SlhQfpZs4tHtMkB5v2aJUqv1QWDVxXbZUt1e3xzDkWfg6ZcBhvefKMBB%2Byq6WYia1EttbLcGgA%3D&_svg=1&checksum=af333a05ceb2901e119e3609a32d84f3a939e11b0c6ebfcde853a4e85f82873f&enable_card_refactor=1&enable_did_fallback=1&item_author_type=1&link_reflow_popup_iteration_sharer=%7B%22click_empty_to_play%22%3A1%2C%22follow_to_play_duration%22%3A-1%2C%22profile_clickable%22%3A1%2C%22dynamic_cover%22%3A1%7D&mid=7186909165783419649&preview_pb=0®ion=JP&sec_user_id=MS4wLjABAAAAmNhndHUaZ_8suzwbiPy7ZATC2woP5GFZxKYwhbVq17KC8D3KFyv1XQbwtMcfgAOC&share_app_id=1180&share_item_id=7602955010942684437&share_link_id=97C4527F-4ED8-4312-9216-B081C2660BCD&share_region=JP&share_scene=2&sharer_language=ja&sharing_card_exp_vid=v2&social_share_type=0&source=h5_t×tamp=1770201579&tt_from=copy&u_code=e50ac0c9gjcd2d&ug_btm=b8727%2Cb2878&user_id=7166518110513398785&utm_campaign=client_share&utm_medium=ios&utm_source=copy" title="TikTok · 水島美結" description="「いいね」が66件、コメントが7件。「あたしを彼女にしたいなら\ud83d\udc8f」" thumbnailUrl="https://phinf.wevpstatic.net/MjAyNjAyMDRfMTky/MDAxNzcwMjAxNjA1MDYy.AJ-tWl_JknBu66576Clm-9s489YPk063CRuiW1MDwDUg.F0VUR2ZR4E6PXiwTt_jJvbRHDez5AqQyyYW8Mm4bdLAg.JPEG/play-icon-large.png%3A%3A%3A%3A%3A.jpeg" />
<dm:audio videoId="49DD31B6D541470576EAEEB18A5855CBC17C" thumbnailUrl="https://phinf.wevpstatic.net/MjAyNjAyMDdfNzUg/MDAxNzcwMzkzMzMwNTc0.NZsP0ApXxHwxwWWzKjugEzwXD0anlD2dV6y5C2tTMBUg.VflyC0fd9KX43p4CqOUvn2zvCCy81KyIc3Cl3daPJ4Yg.JPEG/dxlw01OCMM_01.jpg" duration="9" audioLevels="[0, 0, 0, 3, 1, 14, 12, 14, 14, 12, 13, 14, 13, 10, 11, 13, 15, 12, 11, 12, 10, 7, 4, 3, 2, 2, 12, 15, 14, 12, 13, 13, 11, 12, 10, 6, 3, 2, 7, 16, 15, 14, 12, 8, 5, 14, 14, 14, 12, 8]" />'
<dm:video videoId="A5E6B7F5BF3B2C0DC74E343B1734C0A7D68A" duration="6" thumbnailUrl="https://phinf.wevpstatic.net/MjAyNjAyMDdfMjk5/MDAxNzcwNDQ1NTM2NTI0.5_G_tFn0aTEekV5hICvSGyzpLD_85WdxIZV7hJ3Befog.5YH1X5FksF0TzKpVnc8hmu0FJn-HBY6TuxBBQOpweeAg.JPEG/Ye1jlSzoXX_01.jpg" width="1080.0" height="1920.0" />
解析出来以后,图片和超链接格式只需要获取url就可以了,audio和video其实是同一类型,在wvs的网页端,audio也是以视频形式放出来的的,只是它给视频加了一层透明,所以解析上算是同一个流程
获取到videoId以后,需要通过一个新的接口/weverse/wevweb/dm/v2.0/video/{videoId}/play-info来获取视频的url,它的param和获取message基本是一样的,你需要给它roomId以及发出这条媒体的messageId,这样你就可以获取到这个视频的url
到这里好像就结束了?只需要循环访问message接口获取新的message就好了
但实际上如果你把它当成一个长期服务运行以后就会发现,token会有规律的过期,如果每次都重新抓一次重启一下也太麻烦了,但是实际上cookies过期必没有那么频繁,抓包后发现,accessToken有一个固定的有效期,就是72h,72h以后会自动失效,但是失效以后,它会自动检测你的浏览器中有没有保存refreshToken,如果有,那么它会访问
POST /api/v1/token/refresh HTTP/2
host: accountapi.weverse.io
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0
accept: */*
accept-language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
accept-encoding: gzip, deflate, br, zstd
referer: https://weverse.io/
content-type: application/json
authorization: Bearer {accessToken}
x-acc-service-id: weverse
x-acc-trace-id: 7c7f4867-6ae9-4ee9-b5e2-730b324288c3
content-length: 440
origin: https://weverse.io
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-site
priority: u=4
te: trailers
{"refreshToken":"{refreshToken}"}
这是一个官方的Token刷新接口,能够让你在检测到Token过期时自动获取一个新的Token,refreshToken是一次性的,在调用这个接口后,之前的refreshToken会自动过期,这个接口会返回给你一个全新的refreshToken。
到这里流程应该就结束了,你已经拥有了一个全自动转发Bot,接下来就可以用这些数据全部喂给LLM来虚拟化一个只属于自己的DM啦(x)
Be First to Comment