2024年3月18日

今天试着完善交错战线机器人,两个目标:

  1. 通过星火大模型 API ,补全空白标题
  2. 上传图片并使用

补全空白标题

使用讯飞星火提供的官方 API 例程即可。

1
2
3
4
5
6
def generate_title(content=""):
text.clear
question = getText("user", content)
SparkApi.answer = ""
SparkApi.main(appid, api_key, api_secret, Spark_url, domain, question)
return str(SparkApi.answer)

content 为 “向大模型提问的内容”,在这个情况下,则为 “请你为以下内容想一个标题”;这个函数会返回一段文本,即答案,即给出的标题。

上传文件/图片

我如果要通过 python 上传图片,一是需要注意在上传之前需要发送一个 OPTIONS 请求,二是上传后不会返回任何内容,包括图片 url。

查看了原js,使用的是 XHR(XMLHttpRequest)提交数据。

可以使用 Object.keys() 得到一个 Object 对象的键,例如,对于 变量 asd,可以使用 Object.keys(asd) 得到键,但注意不是通过 asd.keys()

在原本的js中,构造了一个 postfunc.thumbBinCache 方便生成图片代码,其中第七个键是完整大小的图片代码。

对于函数 postfunc.add1Attach = function (opt, time, attach,checkSum,url,isimg,thumb,utf8oname,tid,pid,aid),有以下注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 添加一个上传好的附件
* @param {*} opt bit选项 &1不复用当前上传elm
* @param {*} time 时间戳(毫秒)
* @param {*} attach 附件服务器返回的attach字符串
* @param {*} checkSum 附件服务器返回的checkSum字符串
* @param {*} url 路径 mon_\d+开头
* @param {*} isimg 是否是图片
* @param {*} thumb thumb值 bit
* @param {*} utf8oname
* @param {*} tid
* @param {*} pid
* @param {*} aid
*/

我一直在纳闷 url 参数从何而来。

url 参数被保存在 y.data.url,其中 y 是 window.script_muti_get_var_store,即 nga 的网页版页面数据。可以向控制台输入 window.script_muti_get_var_store.data.url 查询 url。

(但是少了个 ./,需要自己加上去)

那么,问题来了,应该如何在没有编辑页面的 python 程序中获取到 url?

根据 api 文档(二哥写的),返回的数据一种形式就是开头有个 window.script_muti_get_var_store =,得到的数据就这些个格式,但内容肯定是不同的。

但是发帖后获取的返回数据中,有已上传附件、已上传附件验证码(auth)。

地精文档

之前没注意到,光顾着自己尝试了。实际上二哥有详细描述应该如何上传附件。见 https://g.nga.cn/read.php?tid=6406100 的 6.1 和 6.2 节。

值得注意的是,文档是挺久之前写的,实际情况可能有变化。但也可能没变化,只是我搞错了,误以为没有返回数据。

文档中的描述是:

1
2
3
4
使用post(enctype=multipart/form-data)方法将如下数据上传至 6.1 中提到的数据中的附件上传地址,上传成功后服务器会返回数据。

将attachments连接至发帖表单的attachments参数末尾(如有多个附件用\t分隔)
将attachments_check连接至发帖表单的attachments_check参数末尾 (如有多个附件用\t分隔)

而发贴的时候,应当附带 attachments 附件、attachments_check 附件验证码。

我猜测 6.1 中的附件上传地址指的是一个 url,例如 http://img8.nga.cn/attach.php 之类的。

试了一下

fid=823,nga 的上线游戏讨论区。

我向 https://ngabbs.com/post.php?fid=823&__output=1 发送请求,返回的数据如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
"data": {
"action": "new",
"__T": {
"topic_misc_var": ""
},
"fid": 823,
"auth": "【字符串】",
"if_moderator": 0,
"cost_info": "匿名主题50银币 匿名回复1银币 贴条回复1银币 投票10银币 版主免费 付费会员免费(每月限量)",
"tid": "",
"__CU": {
"uid": 65112288,
"group_bit": 98528,
"admincheck": 0,
"rvrc": 10
},
"__GLOBAL": {
"_ATTACH_BASE_VIEW": "img.nga.178.com/attachments"
},
"__F": {
"bit_data": 167772160,
"fid": 823,
"name": "上线游戏讨论区"
},
"attach_url": "https://img8.nga.cn/attach.php"
},
"encode": "gbk",
"time": 1710754883,
"debug": null
}

得到的结果中, "attach_url": "https://img8.nga.cn/attach.php" 意思就是我要向这边上传文件,"auth": "【字符串】", 就是我需要附带的附件验证码,否则会被视为违规请求。需要的就是这个。

注意这是需要登录的(废话)。发送请求和其他基本一样,带上 Cookie 即可。

又试了一下

在网页段上传文件时,会发送这么一个 POST 请求:

1
2
3
4
5
6
7
8
9
window.script_muti_get_var_store = {
data: {
attachments: 'aid~mvQ2t-64jz~ext~webp~url_utf8_org_name~RJ01078257%5fimg%5fmain%2ewebp~path~mon_202403/18~url_dscp~~size~28538~name~mvQ2t-64jzKsT1kSfk-bo.webp~tmp_file~./temp_attachments/mvQ2t-64jz.webp.tmp~tmp_shoot~./temp_attachments/mvQ2t-64jz.webp.tmp~tmp_shoot_size~28538~tmp_shoot_ext~webp~isimg~1~w~560~h~420~thumb~56',
attachments_check: 'e7f78df7942303341cfc34c0947d092a',
url: 'mon_202403/18/mvQ2t-64jzKsT1kSfk-bo.webp',
isImg: '1',
thumb: '56'
}
}

很显然,这是 javascript 生成的表单数据。也就是说,正常流程中发送数据,不会向服务器请求得到附件的 url,反之,是本地生成 url,再发送给服务器。

那么问题来了,本地是如何生成附件 url 的?

按照我的理解,应该是上传文件-图片服务器响应-返回数据中提取对应 url,但我并没有看到相应的响应。

总之就是又试了一次

使用 postman 制造一个请求,成功上传了,并且返回了有效数据:

1
2
3
4
5
6
7
8
9
window.script_muti_get_var_store = {
data: {
attachments: 'aid~mvQ2t-3za5~ext~jpg~url_utf8_org_name~image.png~path~mon_202403/18~url_dscp~~size~124647~name~mvQ2t-3za5ZcT3cSlc-w0.jpg~tmp_file~./temp_attachments/mvQ2t-3za5.jpg.tmp~w~768~h~1152~tmp_shoot~./temp_attachments/mvQ2t-3za5.jpg.tmp~tmp_shoot_size~124647~tmp_shoot_ext~jpg~isimg~1~thumb~120',
attachments_check: 'c745093860ce606f3fe12c0c3b66e638',
url: 'mon_202403/18/mvQ2t-3za5ZcT3cSlc-w0.jpg',
isImg: '1',
thumb: '120'
}
}

是的,正是之前那些;我理解有误,但无误;这些数据不是本地提交的,是远端发来的。

破案了

在最终提交 post 的时候,需要在 attachments 添加对应的 attachments,而不是 attachments_check,或者 url

这是单张图片提交的情况:

1
2
3
4
5
6
7
8
9
10
11
12

action: reply
fid: 275
tid: 38719308
post_content:
[img]./mon_202403/18/7nQ2t-de3sZlT3cSlc-w0.jpg[/img]
attachments: aid~7nQ2t-de3s~ext~jpg~url_utf8_org_name~00737%2d2622116886%2dfeaturelessMix%5fv30202312%2epng~path~mon_202403/18~url_dscp~~size~218093~name~7nQ2t-de3sZlT3cSlc-w0.jpg~tmp_file~./temp_attachments/7nQ2t-de3s.jpg.tmp~w~768~h~1152~tmp_shoot~./temp_attachments/7nQ2t-de3s.jpg.tmp~tmp_shoot_size~124647~tmp_shoot_ext~jpg~isimg~1~thumb~120
attachments_check: fd9a4034fe53664a801d79a2dbd9f5c4
nojump: 1
lite: htmljs
step: 2


这是同时提交两张图片的情况:

1
2
3
4
5
6
7
8
9
10
11
action: reply
fid: 275
tid: 38719308
post_content:
[img]./mon_202403/18/7nQ2t-529pZlT3cSlc-w0.jpg[/img]
[img]./mon_202403/18/7nQ2t-7v3oZlT3cSlc-w0.jpg[/img]
attachments: aid~7nQ2t-529p~ext~jpg~url_utf8_org_name~00737%2d2622116886%2dfeaturelessMix%5fv30202312%2epng~path~mon_202403/18~url_dscp~~size~218093~name~7nQ2t-529pZlT3cSlc-w0.jpg~tmp_file~./temp_attachments/7nQ2t-529p.jpg.tmp~w~768~h~1152~tmp_shoot~./temp_attachments/7nQ2t-529p.jpg.tmp~tmp_shoot_size~124647~tmp_shoot_ext~jpg~isimg~1~thumb~120 aid~7nQ2t-7v3o~ext~jpg~url_utf8_org_name~00737%2d2622116886%2dfeaturelessMix%5fv30202312%2epng~path~mon_202403/18~url_dscp~~size~218093~name~7nQ2t-7v3oZlT3cSlc-w0.jpg~tmp_file~./temp_attachments/7nQ2t-7v3o.jpg.tmp~w~768~h~1152~tmp_shoot~./temp_attachments/7nQ2t-7v3o.jpg.tmp~tmp_shoot_size~124647~tmp_shoot_ext~jpg~isimg~1~thumb~120
attachments_check: 11e98d076d3a6793a4128a1ac62baa51 c593cf08e0b0e1765b727342fcf3f7de
nojump: 1
lite: htmljs
step: 2

可以看到 图片的 attachments 之间并没有使用分号啊逗号啊什么的隔开,只是换行和空格。

2024-03-19 更正:应该是使用 \t 进行分割,而不是使用回车或者空格,这在文档里面写出来了。

总结

完成了,不复杂的一套流程。

整体来说:

  1. 调用 data_dict_get() 函数,从 api 获取 bilibili 的动态信息,筛选没有转发过的。在 github action 可以表现为每五分钟检测上一个五分钟内的新动态(由于github action每个月份额有限,改成半小时)
  2. 从调到的字典数据中获取 data_list,即存放动态内容数据的列表
  3. 考虑到半小时内可能会有多条动态,而 nga 会有 10 秒发帖时间间隔,使用一个循环依次发送帖子(绝大多数情况下只有一个)
  4. 对于每一个的动态,根据时间戳判断是否是已经发送过;也可以将文件保存到本地,对比新旧文件的差别,判断哪些动态没有被发送过
  5. 调用 generate_post(data),其中 data 是单条动态的数据。
  6. 根据 api 回传的信息结构,分析出网页直观看到的内容、tag、图片、视频,并转化为 nga 对应的 bbscode。
    1. 标题使用讯飞星火的 api 进行生成(因为原动态总是不写标题)
    2. 图片使用 upload_img() 上传,这个函数接受的参数是一个列表,里面记录每张图的网址,因为一条动态可以有很多图片。使用 upload_one_img() 上传单个图片,使用 post_img() 上传图片,回传生成的图片 url、attachments、attachments_check
    3. 视频使用 flash=video 标签,不过不同客户端有的可能看不到。
    4. 一大问题是,有的动态是专栏文章的缩略,只会有一小段内容,然后是 “展开”,对应一个全新的页面。这种情况下的转发我还没弄明白应该怎么做。
  7. 结束之后,根据发送的帖子数量输出对应的内容。

二哥的代码、文档其实写得很清晰,井井有条。拜他所赐,我这个半吊子也能大致理解上传图片所需要的东西。遇见过的几个问题全是可以通过看文档快速解决的。


2024年3月18日
http://petertan303.github.io/2024/03/18/2024年3月18日/
作者
peter?
发布于
2024年3月18日
许可协议