koishi-plugin-ll-mc 1.0.7 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +341 -0
- package/package.json +26 -45
- package/assets/fallbackBase64.json +0 -3
- package/assets/fonts/HarmonyOS_Sans_Medium.ttf +0 -0
- package/lib/index.d.ts +0 -136
- package/lib/index.js +0 -2838
package/index.js
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// koishi-plugin-ll-mc v2.0.0(重做版)
|
|
4
|
+
// 消息统计插件:群成员发言排行 + 跨群发言排行(群名正确)
|
|
5
|
+
// 官方QQ机器人(官机)原生适配:
|
|
6
|
+
// - 昵称:优先取消息自带昵称(官机消息自带 member.nick / author.username,零API)
|
|
7
|
+
// - 群名:getGuild(需在QQ开放平台申请"获取群信息"权限)+ 配置手动指定兜底
|
|
8
|
+
// 纯文本排行,不依赖 puppeteer/canvas/图片生成,任何环境都能跑
|
|
9
|
+
|
|
10
|
+
const { Schema } = require('koishi')
|
|
11
|
+
|
|
12
|
+
exports.name = 'll-mc'
|
|
13
|
+
|
|
14
|
+
exports.usage = `消息统计插件:群成员发言排行 + 跨群发言排行。
|
|
15
|
+
|
|
16
|
+
## 命令
|
|
17
|
+
|
|
18
|
+
- \`mc.排行 [周期] [人数]\`:本群成员发言排行。周期:今日/本周/本月/总榜(默认总榜),如 \`mc.排行 今日 10\`
|
|
19
|
+
- \`mc.群排行 [周期] [人数]\`:所有群的发言排行(群名正确显示),当前群带 ★
|
|
20
|
+
- \`mc.我的\`:自己的发言统计(今日/本周/本月/总榜)
|
|
21
|
+
|
|
22
|
+
## 官机(官方QQ机器人)说明
|
|
23
|
+
|
|
24
|
+
- **群名**:官机消息里不带群名,插件通过 \`getGuild\` 接口获取。需要在 QQ 开放平台申请「获取群信息」权限(否则群名显示为 openid);也可以在配置 \`groupNameMap\` 里手动指定(键=群openid,值=群名),一次填好永久正确
|
|
25
|
+
- **昵称**:官机消息自带昵称,无需接口权限;申请「获取群成员信息」后更完整
|
|
26
|
+
- **群ID**:官机显示的是 openid,非数字群号,属正常现象
|
|
27
|
+
|
|
28
|
+
## 配置
|
|
29
|
+
|
|
30
|
+
- \`rankLimit\`:排行默认显示人数
|
|
31
|
+
- \`ignoredGroups\`:不统计的群(填群ID)
|
|
32
|
+
- \`groupNameMap\`:手动指定群名(群openid → 群名)
|
|
33
|
+
- \`refreshGroupNameInterval\`:群名刷新间隔(秒,默认3600)
|
|
34
|
+
- \`countBots\`:是否统计机器人自己的发言(默认否)
|
|
35
|
+
`
|
|
36
|
+
|
|
37
|
+
const Config = Schema.object({
|
|
38
|
+
rankLimit: Schema.number()
|
|
39
|
+
.min(1).max(100).default(20)
|
|
40
|
+
.description('排行默认显示人数'),
|
|
41
|
+
ignoredGroups: Schema.array(String)
|
|
42
|
+
.default([])
|
|
43
|
+
.description('不统计的群(填群ID,官机填 openid)'),
|
|
44
|
+
groupNameMap: Schema.dict(String)
|
|
45
|
+
.default({})
|
|
46
|
+
.description('手动指定群名:键=群ID(官机填 openid),值=群名。用于官机无法自动获取群名时保证"群排行"群名正确'),
|
|
47
|
+
refreshGroupNameInterval: Schema.number()
|
|
48
|
+
.min(60).default(3600)
|
|
49
|
+
.description('群名自动刷新间隔(秒)。官机获取群名需要"获取群信息"权限,无权限时只会尝试一次并降级'),
|
|
50
|
+
countBots: Schema.boolean()
|
|
51
|
+
.default(false)
|
|
52
|
+
.description('是否统计机器人自己的发言'),
|
|
53
|
+
verbose: Schema.boolean()
|
|
54
|
+
.default(false)
|
|
55
|
+
.description('调试:控制台打印统计详情'),
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
exports.Config = Config
|
|
59
|
+
|
|
60
|
+
exports.inject = { required: ['database'] }
|
|
61
|
+
|
|
62
|
+
function apply(ctx, config) {
|
|
63
|
+
const logger = ctx.logger('ll-mc')
|
|
64
|
+
|
|
65
|
+
// ===== 数据表 =====
|
|
66
|
+
// ll_mc_stats: 每群每人每天计数
|
|
67
|
+
ctx.model.extend('ll_mc_stats', {
|
|
68
|
+
channelId: 'string',
|
|
69
|
+
userId: 'string',
|
|
70
|
+
nick: 'string',
|
|
71
|
+
date: 'string',
|
|
72
|
+
count: 'unsigned',
|
|
73
|
+
}, { primary: ['channelId', 'userId', 'date'] })
|
|
74
|
+
// ll_mc_groups: 群名缓存(channelId -> 群名)
|
|
75
|
+
ctx.model.extend('ll_mc_groups', {
|
|
76
|
+
channelId: 'string',
|
|
77
|
+
name: 'string',
|
|
78
|
+
platform: 'string',
|
|
79
|
+
updatedAt: 'timestamp',
|
|
80
|
+
}, { primary: ['channelId'] })
|
|
81
|
+
|
|
82
|
+
// ===== 内存缓存 =====
|
|
83
|
+
const nameCache = new Map() // channelId -> { name, t }
|
|
84
|
+
const lastNameRefresh = new Map() // channelId -> timestamp
|
|
85
|
+
const warnThrottle = new Map() // key -> timestamp(同类警告10分钟一次)
|
|
86
|
+
|
|
87
|
+
function throttledWarn(key, message) {
|
|
88
|
+
const now = Date.now()
|
|
89
|
+
if ((warnThrottle.get(key) || 0) > now - 10 * 60 * 1000) return
|
|
90
|
+
warnThrottle.set(key, now)
|
|
91
|
+
logger.warn(message)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ===== 日期工具 =====
|
|
95
|
+
function dateStr(d) {
|
|
96
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
|
|
97
|
+
}
|
|
98
|
+
function todayStr() {
|
|
99
|
+
return dateStr(new Date())
|
|
100
|
+
}
|
|
101
|
+
// 解析周期:返回 { key, label, start },start 为日期范围起点(含当天),null=全部
|
|
102
|
+
function parsePeriod(period) {
|
|
103
|
+
const p = String(period || '').trim()
|
|
104
|
+
const now = new Date()
|
|
105
|
+
if (!p || /总|全部|all/i.test(p)) return { key: 'all', label: '总榜', start: null }
|
|
106
|
+
if (/周/.test(p)) {
|
|
107
|
+
const d = new Date(now)
|
|
108
|
+
d.setDate(d.getDate() - ((d.getDay() + 6) % 7)) // 周一
|
|
109
|
+
return { key: 'week', label: '本周', start: dateStr(d) }
|
|
110
|
+
}
|
|
111
|
+
if (/月/.test(p)) {
|
|
112
|
+
return { key: 'month', label: '本月', start: `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-01` }
|
|
113
|
+
}
|
|
114
|
+
if (/日|天/.test(p)) return { key: 'day', label: '今日', start: todayStr() }
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ===== 昵称解析(官机优先取消息自带) =====
|
|
119
|
+
async function resolveNick(session, userId) {
|
|
120
|
+
const author = session.author
|
|
121
|
+
if (author?.nick) return String(author.nick)
|
|
122
|
+
if (author?.name) return String(author.name)
|
|
123
|
+
// 官机:原始事件自带 member.nick / author.username,零 API
|
|
124
|
+
const raw = session.event?._data?.d
|
|
125
|
+
if (raw?.member?.nick) return String(raw.member.nick)
|
|
126
|
+
if (raw?.author?.username) return String(raw.author.username)
|
|
127
|
+
// 兜底:getGuildMember(需"获取群成员信息"权限,失败降级为 ID)
|
|
128
|
+
if (session.guildId && !session.isDirect && typeof session.bot?.getGuildMember === 'function') {
|
|
129
|
+
try {
|
|
130
|
+
const member = await session.bot.getGuildMember(session.guildId, userId)
|
|
131
|
+
const name = member?.name || member?.user?.name
|
|
132
|
+
if (name) return String(name)
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
throttledWarn(`member:${session.guildId}:${userId}`, `[ll-mc] 获取成员昵称失败 ${userId}: ${e?.message}(官机需申请"获取群成员信息"权限)`)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return String(userId)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ===== 群名 =====
|
|
142
|
+
async function setGroupName(guildId, name) {
|
|
143
|
+
if (!name || name === guildId) return
|
|
144
|
+
nameCache.set(guildId, { name, t: Date.now() })
|
|
145
|
+
try {
|
|
146
|
+
await ctx.database.upsert('ll_mc_groups', [{
|
|
147
|
+
channelId: guildId, name, platform: '', updatedAt: Date.now(),
|
|
148
|
+
}], ['channelId'])
|
|
149
|
+
}
|
|
150
|
+
catch { /* 忽略写入失败 */ }
|
|
151
|
+
}
|
|
152
|
+
async function getGroupName(guildId) {
|
|
153
|
+
if (config.groupNameMap[guildId]) return config.groupNameMap[guildId]
|
|
154
|
+
const cached = nameCache.get(guildId)
|
|
155
|
+
if (cached?.name) return cached.name
|
|
156
|
+
try {
|
|
157
|
+
const rows = await ctx.database.get('ll_mc_groups', { channelId: guildId })
|
|
158
|
+
if (rows[0]?.name) {
|
|
159
|
+
nameCache.set(guildId, { name: rows[0].name, t: Date.now() })
|
|
160
|
+
return rows[0].name
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch { /* 忽略 */ }
|
|
164
|
+
return guildId
|
|
165
|
+
}
|
|
166
|
+
// 收到消息时刷新群名(节流;官机走 getGuild)
|
|
167
|
+
async function refreshGroupName(session) {
|
|
168
|
+
const guildId = session.guildId
|
|
169
|
+
const now = Date.now()
|
|
170
|
+
if ((lastNameRefresh.get(guildId) || 0) > now - config.refreshGroupNameInterval * 1000) return
|
|
171
|
+
lastNameRefresh.set(guildId, now)
|
|
172
|
+
// 1. OneBot:消息自带群名
|
|
173
|
+
if (session.event?.channel?.name) {
|
|
174
|
+
await setGroupName(guildId, session.event.channel.name)
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
// 2. 手动指定
|
|
178
|
+
if (config.groupNameMap[guildId]) {
|
|
179
|
+
await setGroupName(guildId, config.groupNameMap[guildId])
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
// 3. 官机:getGuild(内部 getGuildInfo,需"获取群信息"权限)
|
|
183
|
+
if (typeof session.bot?.getGuild === 'function') {
|
|
184
|
+
try {
|
|
185
|
+
const g = await session.bot.getGuild(guildId)
|
|
186
|
+
if (g?.name) {
|
|
187
|
+
await setGroupName(guildId, g.name)
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (e) {
|
|
192
|
+
throttledWarn(`guild:${guildId}`, `[ll-mc] 获取群名失败 ${guildId}: ${e?.message}(官机需在QQ开放平台申请"获取群信息"权限,或在配置 groupNameMap 手动指定群名)`)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ===== 消息统计 =====
|
|
198
|
+
ctx.on('message', async (session) => {
|
|
199
|
+
try {
|
|
200
|
+
if (session.isDirect || !session.guildId || !session.userId) return
|
|
201
|
+
const guildId = session.guildId
|
|
202
|
+
const userId = session.userId
|
|
203
|
+
if (!config.countBots && session.selfId === session.userId) return
|
|
204
|
+
if (config.ignoredGroups.includes(guildId)) return
|
|
205
|
+
|
|
206
|
+
const date = todayStr()
|
|
207
|
+
const rows = await ctx.database.get('ll_mc_stats', { channelId: guildId, userId, date })
|
|
208
|
+
const count = (rows[0]?.count || 0) + 1
|
|
209
|
+
const nick = await resolveNick(session, userId)
|
|
210
|
+
await ctx.database.upsert('ll_mc_stats', [{
|
|
211
|
+
channelId: guildId, userId, date, nick, count,
|
|
212
|
+
}], ['channelId', 'userId', 'date'])
|
|
213
|
+
if (config.verbose) logger.info(`[ll-mc] ${guildId} ${userId} +1 (${count})`)
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
logger.warn('[ll-mc] 消息统计失败:', e?.message)
|
|
217
|
+
}
|
|
218
|
+
// 群名刷新独立于计数,失败不影响统计
|
|
219
|
+
refreshGroupName(session).catch(() => {})
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
// ===== 排行聚合 =====
|
|
223
|
+
// 返回 [{ key, nick/name, count }] 按 count 降序
|
|
224
|
+
async function aggregateStats(range, groupBy) {
|
|
225
|
+
const query = range.start ? { date: { $gte: range.start } } : {}
|
|
226
|
+
const rows = await ctx.database.get('ll_mc_stats', query)
|
|
227
|
+
const map = new Map()
|
|
228
|
+
for (const row of rows) {
|
|
229
|
+
const key = groupBy === 'user' ? `${row.channelId}:${row.userId}` : row.channelId
|
|
230
|
+
const item = map.get(key) || { count: 0, nick: row.nick }
|
|
231
|
+
item.count += row.count || 0
|
|
232
|
+
if (row.nick) item.nick = row.nick
|
|
233
|
+
map.set(key, item)
|
|
234
|
+
}
|
|
235
|
+
return [...map.entries()].map(([key, item]) => ({ key, ...item }))
|
|
236
|
+
.sort((a, b) => b.count - a.count)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ===== 命令:本群成员排行 =====
|
|
240
|
+
async function memberRanking(session, period, limit) {
|
|
241
|
+
if (session.isDirect || !session.guildId) return '❌ 请在群聊中使用'
|
|
242
|
+
const range = parsePeriod(period)
|
|
243
|
+
if (!range) return '❌ 周期参数无效,可用:今日 / 本周 / 本月 / 总榜'
|
|
244
|
+
const n = Math.min(Math.max(limit || config.rankLimit, 1), 100)
|
|
245
|
+
const list = await aggregateStats(range, 'user')
|
|
246
|
+
const inRange = list.filter(x => x.key.startsWith(`${session.guildId}:`))
|
|
247
|
+
const mine = inRange.find(x => x.key === `${session.guildId}:${session.userId}`)
|
|
248
|
+
const mineRank = mine ? inRange.indexOf(mine) + 1 : 0
|
|
249
|
+
const groupRows = await ctx.database.get('ll_mc_stats', { channelId: session.guildId })
|
|
250
|
+
const total = groupRows.reduce((s, r) => s + (r.count || 0), 0)
|
|
251
|
+
const totalInRange = inRange.reduce((s, x) => s + x.count, 0)
|
|
252
|
+
|
|
253
|
+
const lines = [`📊 ${range.label}发言排行 TOP ${Math.min(n, inRange.length)}(本群)`]
|
|
254
|
+
inRange.slice(0, n).forEach((x, i) => {
|
|
255
|
+
const mark = x.key === `${session.guildId}:${session.userId}` ? ' ★' : ''
|
|
256
|
+
lines.push(`${i + 1}. ${x.nick || x.key.split(':')[1]} · ${x.count}条${mark}`)
|
|
257
|
+
})
|
|
258
|
+
lines.push(`—— 本群${range.label}共 ${totalInRange} 条发言,历史累计 ${total} 条`)
|
|
259
|
+
if (mineRank) lines.push(`我的排名:第 ${mineRank} 名(${mine.count}条)`)
|
|
260
|
+
return lines.join('\n')
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ===== 命令:跨群排行(群名正确) =====
|
|
264
|
+
async function groupRanking(session, period, limit) {
|
|
265
|
+
const range = parsePeriod(period)
|
|
266
|
+
if (!range) return '❌ 周期参数无效,可用:今日 / 本周 / 本月 / 总榜'
|
|
267
|
+
const n = Math.min(Math.max(limit || config.rankLimit, 1), 100)
|
|
268
|
+
const list = await aggregateStats(range, 'group')
|
|
269
|
+
const current = session.guildId
|
|
270
|
+
const lines = [`🏆 群${range.label}发言排行 TOP ${Math.min(n, list.length)}`]
|
|
271
|
+
for (let i = 0; i < Math.min(n, list.length); i++) {
|
|
272
|
+
const x = list[i]
|
|
273
|
+
const name = await getGroupName(x.key)
|
|
274
|
+
const mark = x.key === current ? ' ★当前群' : ''
|
|
275
|
+
lines.push(`${i + 1}. ${name} · ${x.count}条${mark}`)
|
|
276
|
+
}
|
|
277
|
+
lines.push('(群名来自 getGuild 接口,需申请"获取群信息"权限;或用 groupNameMap 手动指定)')
|
|
278
|
+
return lines.join('\n')
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ===== 命令:我的统计 =====
|
|
282
|
+
async function myStats(session) {
|
|
283
|
+
if (session.isDirect || !session.guildId) return '❌ 请在群聊中使用'
|
|
284
|
+
const guildId = session.guildId
|
|
285
|
+
const userId = session.userId
|
|
286
|
+
const periods = [
|
|
287
|
+
{ key: 'day', label: '今日' },
|
|
288
|
+
{ key: 'week', label: '本周' },
|
|
289
|
+
{ key: 'month', label: '本月' },
|
|
290
|
+
{ key: 'all', label: '总榜' },
|
|
291
|
+
]
|
|
292
|
+
const lines = ['👤 我的发言统计(本群)']
|
|
293
|
+
for (const p of periods) {
|
|
294
|
+
const range = parsePeriod(p.label)
|
|
295
|
+
const query = range.start
|
|
296
|
+
? { channelId: guildId, userId, date: { $gte: range.start } }
|
|
297
|
+
: { channelId: guildId, userId }
|
|
298
|
+
const rows = await ctx.database.get('ll_mc_stats', query)
|
|
299
|
+
const total = rows.reduce((s, r) => s + (r.count || 0), 0)
|
|
300
|
+
lines.push(`${p.label}:${total} 条`)
|
|
301
|
+
}
|
|
302
|
+
return lines.join('\n')
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ===== 命令注册 =====
|
|
306
|
+
// 解析 "周期 [人数]" 或 "[人数]",两种顺序都支持
|
|
307
|
+
function parseArgs(text) {
|
|
308
|
+
const parts = String(text || '').trim().split(/\s+/).filter(Boolean)
|
|
309
|
+
let period = ''
|
|
310
|
+
let limit = 0
|
|
311
|
+
for (const part of parts) {
|
|
312
|
+
if (/^\d+$/.test(part)) limit = parseInt(part, 10)
|
|
313
|
+
else period = part
|
|
314
|
+
}
|
|
315
|
+
return { period, limit }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
ctx.command('mc.排行 [args:text]', '本群成员发言排行(今日/本周/本月/总榜)')
|
|
319
|
+
.alias('排行')
|
|
320
|
+
.action(async ({ session }, args) => {
|
|
321
|
+
const { period, limit } = parseArgs(args)
|
|
322
|
+
return await memberRanking(session, period, limit)
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
ctx.command('mc.群排行 [args:text]', '跨群发言排行(群名正确)')
|
|
326
|
+
.alias('群排行')
|
|
327
|
+
.action(async ({ session }, args) => {
|
|
328
|
+
const { period, limit } = parseArgs(args)
|
|
329
|
+
return await groupRanking(session, period, limit)
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
ctx.command('mc.我的', '我的发言统计')
|
|
333
|
+
.alias('我的发言')
|
|
334
|
+
.action(async ({ session }) => {
|
|
335
|
+
return await myStats(session)
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
logger.info('[ll-mc] 已启动:mc.排行 / mc.群排行 / mc.我的')
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
exports.apply = apply
|
package/package.json
CHANGED
|
@@ -1,45 +1,26 @@
|
|
|
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
|
-
"author": "lee",
|
|
28
|
-
"license": "MIT",
|
|
29
|
-
"keywords": [
|
|
30
|
-
"koishi",
|
|
31
|
-
"koishi-plugin",
|
|
32
|
-
"message-counter",
|
|
33
|
-
"rank"
|
|
34
|
-
],
|
|
35
|
-
"peerDependencies": {
|
|
36
|
-
"koishi": "^4.18.9"
|
|
37
|
-
},
|
|
38
|
-
"devDependencies": {
|
|
39
|
-
"koishi": "^4.18.11",
|
|
40
|
-
"koishi-plugin-cron": "^3.1.0",
|
|
41
|
-
"koishi-plugin-markdown-to-image-service": "^1.3.6",
|
|
42
|
-
"koishi-plugin-puppeteer": "^3.9.0",
|
|
43
|
-
"typescript": "^5.4.0"
|
|
44
|
-
}
|
|
45
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "koishi-plugin-ll-mc",
|
|
3
|
+
"description": "消息统计插件(重做版):群成员发言排行 + 跨群发言排行,官方QQ机器人原生适配",
|
|
4
|
+
"version": "2.0.0",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"index.js"
|
|
8
|
+
],
|
|
9
|
+
"koishi": {
|
|
10
|
+
"description": {
|
|
11
|
+
"zh": "消息统计插件:群成员发言排行 + 跨群发言排行(群名正确),官机原生适配,纯文本无需图片依赖"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"author": "lee",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"koishi",
|
|
18
|
+
"koishi-plugin",
|
|
19
|
+
"message-counter",
|
|
20
|
+
"rank",
|
|
21
|
+
"stats"
|
|
22
|
+
],
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"koishi": "^4.18.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
"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"
|
|
3
|
-
]
|
|
Binary file
|
package/lib/index.d.ts
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { Context, Schema } from "koishi";
|
|
2
|
-
export declare const name = "mc";
|
|
3
|
-
export declare const inject: {
|
|
4
|
-
required: string[];
|
|
5
|
-
optional: string[];
|
|
6
|
-
};
|
|
7
|
-
export interface Config {
|
|
8
|
-
/** 是否统计 Bot 自己发送的消息。 */
|
|
9
|
-
isBotMessageTrackingEnabled: boolean;
|
|
10
|
-
/** 排行榜默认显示的人数。 */
|
|
11
|
-
defaultMaxDisplayCount: number;
|
|
12
|
-
/** 是否在显示排行榜时补充时间信息。 */
|
|
13
|
-
isTimeInfoSupplementEnabled: boolean;
|
|
14
|
-
/** 是否在排行榜中显示用户消息占比。 */
|
|
15
|
-
isUserMessagePercentageVisible: boolean;
|
|
16
|
-
/** 在排行榜中全局隐藏的用户 ID 列表。 */
|
|
17
|
-
hiddenUserIdsInLeaderboard: string[];
|
|
18
|
-
/** 在群排行榜中全局隐藏的频道 ID 列表。 */
|
|
19
|
-
hiddenChannelIdsInLeaderboard: string[];
|
|
20
|
-
/** 排除统计的频道 ID 列表,这些群的消息不会被计数。 */
|
|
21
|
-
excludedChannelIds: string[];
|
|
22
|
-
/** 排除统计的用户 ID 列表,这些用户的消息不会被计数。 */
|
|
23
|
-
excludedUserIds: string[];
|
|
24
|
-
/** 是否将文本排行榜转为 Markdown 图片。 */
|
|
25
|
-
isTextToImageConversionEnabled: boolean;
|
|
26
|
-
/** 是否将排行榜渲染为水平柱状图。 */
|
|
27
|
-
isLeaderboardToHorizontalBarChartConversionEnabled: boolean;
|
|
28
|
-
/** 生成的柱状图图片类型。 */
|
|
29
|
-
imageType: "png" | "jpeg" | "webp";
|
|
30
|
-
/** 头像缓存的有效期(秒)。设置为 0 可禁用缓存刷新。 */
|
|
31
|
-
avatarCacheTTL: number;
|
|
32
|
-
/** 头像获取失败后的重试间隔(秒)。 */
|
|
33
|
-
avatarFailureCacheTTL: number;
|
|
34
|
-
/** 页面加载等待事件,影响图片生成速度和稳定性。 */
|
|
35
|
-
waitUntil: "load" | "domcontentloaded" | "networkidle0" | "networkidle2";
|
|
36
|
-
/**
|
|
37
|
-
* 生成水平柱状图时,渲染页面的视口宽度(像素)。
|
|
38
|
-
* @default 1080
|
|
39
|
-
*/
|
|
40
|
-
chartViewportWidth: number;
|
|
41
|
-
/**
|
|
42
|
-
* 渲染时的设备像素比 (DPR)。
|
|
43
|
-
* 更高的值可以生成更清晰的图片(例如,设置为 2 相当于 2x 图),但也会增加图片文件体积。
|
|
44
|
-
* @default 1
|
|
45
|
-
*/
|
|
46
|
-
deviceScaleFactor: number;
|
|
47
|
-
/** 是否将自定义图标显示在柱状条的末端。 */
|
|
48
|
-
shouldMoveIconToBarEndLeft: boolean;
|
|
49
|
-
/** 是否在生成水平柱状图时,在当前用户/群聊名称前显示★以高亮。 */
|
|
50
|
-
showStarInChart: boolean;
|
|
51
|
-
/** 自定义背景图在进度条区域的不透明度。 */
|
|
52
|
-
horizontalBarBackgroundOpacity: number;
|
|
53
|
-
/** 自定义背景图在整行背景的不透明度。 */
|
|
54
|
-
horizontalBarBackgroundFullOpacity: number;
|
|
55
|
-
/** 允许上传的背景图最大宽度(像素)。 */
|
|
56
|
-
maxBarBgWidth: number;
|
|
57
|
-
/** 允许上传的背景图最大高度(像素)。 */
|
|
58
|
-
maxBarBgHeight: number;
|
|
59
|
-
/** 允许上传的背景图最大体积(MB)。 */
|
|
60
|
-
maxBarBgSize: number;
|
|
61
|
-
/** 图片整体背景的类型。 */
|
|
62
|
-
backgroundType: string;
|
|
63
|
-
/** API 背景配置。 */
|
|
64
|
-
apiBackgroundConfig: apiBackgroundConfig;
|
|
65
|
-
/** 自定义背景的 CSS 代码。 */
|
|
66
|
-
backgroundValue: string;
|
|
67
|
-
/** 水平柱状图 - 标题的字体。 */
|
|
68
|
-
chartTitleFont: string;
|
|
69
|
-
/** 水平柱状图 - 成员昵称和发言次数的字体。 */
|
|
70
|
-
chartNicknameFont: string;
|
|
71
|
-
/** 是否启用定时自动推送排行榜功能。 */
|
|
72
|
-
autoPush: boolean;
|
|
73
|
-
/** 是否在每日 0 点自动发送昨日排行榜。 */
|
|
74
|
-
shouldSendDailyLeaderboardAtMidnight: boolean;
|
|
75
|
-
/** 是否在每周一 0 点自动发送上周排行榜。 */
|
|
76
|
-
shouldSendWeeklyLeaderboard: boolean;
|
|
77
|
-
/** 是否在每月第一天 0 点自动发送上月排行榜。 */
|
|
78
|
-
shouldSendMonthlyLeaderboard: boolean;
|
|
79
|
-
/** 是否在每年第一天 0 点自动发送去年排行榜。 */
|
|
80
|
-
shouldSendYearlyLeaderboard: boolean;
|
|
81
|
-
/** 其他定时发送今日排行榜的时间点(24小时制)。 */
|
|
82
|
-
dailyScheduledTimers: string[];
|
|
83
|
-
/** 发送排行榜前是否发送提示消息。 */
|
|
84
|
-
isGeneratingRankingListPromptVisible: boolean;
|
|
85
|
-
/** 发送提示后等待多少秒再发送图片。 */
|
|
86
|
-
leaderboardGenerationWaitTime: number;
|
|
87
|
-
/** 需要接收自动推送的频道 ID 列表。 */
|
|
88
|
-
pushChannelIds: string[];
|
|
89
|
-
/** 是否向机器人所在的所有群聊推送。 */
|
|
90
|
-
shouldSendLeaderboardNotificationsToAllChannels: boolean;
|
|
91
|
-
/** "向所有群聊推送" 开启时的排除列表。 */
|
|
92
|
-
excludedLeaderboardChannels: string[];
|
|
93
|
-
/** 批量推送时,每个群之间的发送延迟(秒)。 */
|
|
94
|
-
delayBetweenGroupPushesInSeconds: number;
|
|
95
|
-
/** 延迟时间的随机波动范围(秒)。 */
|
|
96
|
-
groupPushDelayRandomizationSeconds: number;
|
|
97
|
-
/** 是否在每日 0 点自动禁言昨日发言最多的人。 */
|
|
98
|
-
enableMostActiveUserMuting: boolean;
|
|
99
|
-
/** 0 点后,等待多少秒再执行禁言操作。 */
|
|
100
|
-
dragonKingDetainmentTime: number;
|
|
101
|
-
/** 禁言时长(天)。 */
|
|
102
|
-
detentionDuration: number;
|
|
103
|
-
/** 在哪些频道中执行“抓龙王”操作。 */
|
|
104
|
-
muteChannelIds: string[];
|
|
105
|
-
}
|
|
106
|
-
export declare const Config: Schema<Config>;
|
|
107
|
-
declare module "koishi" {
|
|
108
|
-
interface Tables {
|
|
109
|
-
message_counter_records: MessageCounterRecord;
|
|
110
|
-
message_counter_state: MessageCounterState;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
interface apiBackgroundConfig {
|
|
114
|
-
apiUrl: string;
|
|
115
|
-
apiKey: string;
|
|
116
|
-
responseType: string;
|
|
117
|
-
}
|
|
118
|
-
interface MessageCounterRecord {
|
|
119
|
-
channelId: string;
|
|
120
|
-
channelName: string;
|
|
121
|
-
userId: string;
|
|
122
|
-
username: string;
|
|
123
|
-
userAvatar: string;
|
|
124
|
-
todayPostCount: number;
|
|
125
|
-
thisWeekPostCount: number;
|
|
126
|
-
thisMonthPostCount: number;
|
|
127
|
-
thisYearPostCount: number;
|
|
128
|
-
totalPostCount: number;
|
|
129
|
-
yesterdayPostCount: number;
|
|
130
|
-
}
|
|
131
|
-
interface MessageCounterState {
|
|
132
|
-
key: string;
|
|
133
|
-
value: Date;
|
|
134
|
-
}
|
|
135
|
-
export declare function apply(ctx: Context, config: Config): Promise<void>;
|
|
136
|
-
export {};
|