Files
tangguo_server/scripts/dramacmt
rootandClaude Opus 5 96caa5cda0 [AI] feat: dramacmt 文案池扩到 637 条以支撑单剧 200 条
其余四站要求每部在线短剧加铺 50~200 条,原来 179 条的池子装不下:
main.go 的硬校验会直接拒绝 --max 200,而且就算刚好够,每部剧都会抽到
九成文案,几千部剧的评论区长得一模一样。

新增 458 条(催更、剧情编剧、角色与 CP、演技、制作配乐、吐槽批评、
平台体验、追剧场景、讨论打卡、短评、长评),全池 637 条互不重复,
抽 200 条约占三成。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-30 11:33:11 +08:00
..

dramacmt

给在线短剧铺机器人评论。一次性运营工具,不进 cron。

为什么不用后台已有的评论导入

web/api/commentctrl.AddContentExcel 有三个限制:

  1. 只认视频(按 vidmod.GetVideoInfo 查对象),短剧的 objID 是 media._id,走不通
  2. 要上传 Excel
  3. 它的 userID 取自 static.json 的 sysUser——那些 uid 在 user 表里并不存在

第 3 点是实际踩到的坑:评论列表填充用户信息时(app/service/commentser/cmtcoverter.go) 查不到用户不会丢弃评论,只是把昵称和头像留空。线上已经有 5962 条这样的 「无名无头像」评论(抽查 400 个引用 uid,0 命中)。

所以本工具自己建一批机器人用户,评论挂在它们身上。

机器人用户

  • uid 从 1001 开始往上发,--users 控制池子大小。该区间原本完全空闲 (uid 1~17 是库里已有的 17 个机器人,1000 以下整段不碰)
  • 池子要够大:同一部剧内用户不重复,所以 --users 必须大于 --max; 而且池子太小的话同一个昵称头像会在整个评论区刷屏。实测取值: tangguo 5,000(和 scripts/vidcmt 共用),其余四站 3,000
  • uid 必须 <= 111999(common/constant.RobotUIDLimit)—— commentser.getTrackInfo 靠这个阈值判定 isRobot,后台可按机器人筛选和撤回
  • uid 池远低于 auto_id 的 uid 计数器(当时是 3111989,只增不减), 不会和后续真实注册撞号
  • 昵称按「姓 + 名」随机拼,风格对齐库里现有那 17 个机器人(郏哲、溪焱这种)
  • 头像从库里现有用户采样,不写死路径(各站 static.json 里是同一批 90 张)

user 表的唯一索引要当心:

索引 类型 处理
devID 非 sparse 唯一 必须给唯一非空值,用 ROBOT%07d
token / promotionCode / mobile 部分唯一({$gt: ""}) 留空串即豁免

已存在的 uid 不覆盖(昵称头像可能被运营改过),所以可以重复跑。

评论

要能在 App 里显示,必须满足 models/v/cmtmod.GetParentCmtList 的全部条件:

level = 1        status = 1       isGodComment = false
isDelete = false isAdvertiser 非 true
createdAt <= now

注意 status:App 发评论走的 getTrackInfo 写的是 status: 2(未审核), 而列表只认 1。所以铺数据必须直接写 1,否则一条都不显示。

其余字段整体对齐线上真实短剧评论的形状,两个容易写错的地方:

  • type 是 "VIDEO",不是 "drama"——objType 才是 "drama"。 getTrackInfo 对所有 objType 都传 cmtmod.CmtTypeVideo
  • tousername 落库键名是小写的:结构体那个字段只有 form/json tag, 没写 bson tag,driver 默认把字段名小写
  • city 必须写 "-":发评论路径里 GetLocationByIP 的调用是注释掉的 (app/service/commentser/cmtsend.go:114-116),线上真实评论没有一条带 归属地。City 会原样出参给 App(cmtcoverter.go:46),给真城市等于在评论区 挂个「我是机器人」的标。第一版写了随机城市,已改掉,线上那 19,697 条也已回刷

media.countComment 必须同步 $inc。App 的「总评论条数」在第一页直接读这个 字段(app/api/commentctrl),不补的话前端显示 0 条、但列表里有评论。

点赞数与时间

  • createdAt 随机铺在该剧 latestPublishedAt(没有就退 createdAt)到现在之间, 再按时间排序写入
  • likeCount 走长尾:55% 落 05、30% 落 630、12% 落 31120、3% 落 121400。 热门评论区要求 likeCount > 0 且按它倒序,所以不能全给 0
  • 再按文案长短压上限:≤4 字的短评最多 30 赞,≤8 字最多 120。 不压的话会出现「可以」两个字拿 164 赞这种明显假的数据
  • 不写 like 集合的记录。likeCount 是展示值,isLike 只反映当前请求用户, 不建 like 文档不影响功能,也省掉几万条垃圾数据

文案池在 content.go,637 条互不重复。单剧内文案和用户都不重复, 所以 --max 不能超过池子容量(main.go 里有硬校验)。

容量还要留足余量,不能刚好够:铺 200 条时池子只有 220 条的话,每部剧都会抽到 九成文案,几千部剧的评论区长得一模一样。现在抽 200 条约占池子三成。

用法

go build -o /tmp/dramacmt ./scripts/dramacmt/

# 干跑,看会写多少
/tmp/dramacmt --dst "$DST_TANGGUO" --db tangguo --dry-run

# 只给评论数为 0 的剧铺,每部 20~50 条(默认)
/tmp/dramacmt --dst "$DST_TANGGUO" --db tangguo

# 所有在线短剧都加铺 50~200 条(其余四站 2026-10-30 实际用的参数)
/tmp/dramacmt --dst "$DST_HUANGGUO" --db huangguo --users 3000 \
  --min 50 --max 200 --only-empty=false

# 调试:只跑 3 部,固定种子便于复现
/tmp/dramacmt --dst "$DST_TANGGUO" --db tangguo --limit 3 --seed 42

--db 必须显式传:URI 里的 authSource 通常是 admin,推不出业务库名。

撤回

机器人用户和评论都带标记,撤回是两条命令:

// 撤回评论(软删,保守做法)。uid 上限按当站实际的 --users 取
db.comment.updateMany({isRobot: true, objType: "drama", userID: {$gte: 1001, $lte: 5000}},
                      {$set: {isDelete: true}})

硬删同理,把 $set 换成 deleteMany。两种都做完后 countComment 要重算, 口径和 App 列表第 1 页保持一致(app/service/commentser.GetTotalComments):

countDocuments({objID, objType: "drama", level: 1, status: 1,
                isDelete: false, isAdvertiser: {$in: [null, false]}})

短剧和影视不一样:第 1 页的总条数直接读 media.countComment,第 2 页之后才 实时查,所以这个字段不重算前端就会出现「显示 N 条但列表是空的」。