koishi-plugin-p-draw 1.3.1 → 1.3.3
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 +121 -93
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -461,43 +461,56 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
461
461
|
return { ok: true, tasks, firstPosition }
|
|
462
462
|
}
|
|
463
463
|
|
|
464
|
-
//
|
|
465
|
-
function feedbackBase(session, { firstPosition, count, totalPrice, isAdmin }) {
|
|
466
|
-
const
|
|
467
|
-
if (cfg.queueEnabled) {
|
|
468
|
-
|
|
469
|
-
if (count > 1)
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
464
|
+
// 即时反馈的公共部分。扣费提醒单独返回,随后以引用消息发送。
|
|
465
|
+
function feedbackBase(session, { firstPosition, count, totalPrice, isAdmin }) {
|
|
466
|
+
const notices = []
|
|
467
|
+
if (cfg.queueEnabled) {
|
|
468
|
+
notices.push(session.text('.queued', [firstPosition, cfg.queueMaxRequests || '∞']))
|
|
469
|
+
if (count > 1) notices.push(session.text('.batch-count', [count]))
|
|
470
|
+
} else {
|
|
471
|
+
notices.push(session.text('.generating'))
|
|
472
|
+
if (count > 1) notices.push(session.text('.batch-count', [count]))
|
|
473
|
+
}
|
|
474
|
+
return {
|
|
475
|
+
notices,
|
|
476
|
+
chargeNotice: isAdmin ? '' : session.text('.charged', [totalPrice]),
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
async function sendNotices(session, notices, opts = {}) {
|
|
481
|
+
const content = notices.filter(Boolean).join('\n')
|
|
482
|
+
if (!content) return
|
|
483
|
+
try {
|
|
484
|
+
const message = opts.quote && session.messageId ? h.quote(session.messageId) + content : content
|
|
485
|
+
await session.send(message)
|
|
486
|
+
} catch (e) {
|
|
487
|
+
logger.warn(`发送反馈消息失败:${e.message}`)
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// 生成图片使用合并转发;每张图后紧跟实际使用的最终提示词,不附原消息引用。
|
|
492
|
+
async function sendImagesAsForward(session, outputs) {
|
|
493
|
+
const nodes = []
|
|
494
|
+
const fallback = []
|
|
495
|
+
for (const output of outputs) {
|
|
496
|
+
const src = typeof output === 'string' ? output : output.src
|
|
497
|
+
const prompt = typeof output === 'string' ? '' : String(output.prompt || '')
|
|
491
498
|
const materialized = await materializeImageSource(src)
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
|
|
499
|
+
const image = Buffer.isBuffer(materialized) ? h.image(materialized) : h.image(src)
|
|
500
|
+
nodes.push(h('message', image))
|
|
501
|
+
fallback.push(image)
|
|
502
|
+
if (prompt) {
|
|
503
|
+
nodes.push(h('message', prompt))
|
|
504
|
+
fallback.push(prompt)
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
try {
|
|
508
|
+
await session.send(h('figure', nodes))
|
|
509
|
+
} catch (e) {
|
|
510
|
+
logger.warn(`发送转发图片失败:${e.message}`)
|
|
511
|
+
await session.send(fallback)
|
|
512
|
+
}
|
|
513
|
+
}
|
|
501
514
|
|
|
502
515
|
// P 点读改写按用户串行化,避免并发指令互相覆盖余额
|
|
503
516
|
const userLocks = new Map()
|
|
@@ -1186,7 +1199,7 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1186
1199
|
const verifyBaseUrl = String(cfg.verifyLlmBaseUrl || '').trim()
|
|
1187
1200
|
const verifyModel = String(cfg.verifyLlmModel || '').trim()
|
|
1188
1201
|
if (!verifyBaseUrl || !verifyModel) {
|
|
1189
|
-
return { ok: true, degraded: true, message: '', verdict: null, outputs: images }
|
|
1202
|
+
return { ok: true, degraded: true, message: '', verdict: null, outputs: images, prompt }
|
|
1190
1203
|
}
|
|
1191
1204
|
const passScore = Math.max(0, Math.min(10, parseInt(cfg.multiVerifyPassScore) || 6))
|
|
1192
1205
|
const candidateCount = Math.max(1, Math.min(3, parseInt(cfg.multiCandidateCount) || 2))
|
|
@@ -1294,11 +1307,11 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1294
1307
|
data = extractVerifyJson(reply)
|
|
1295
1308
|
} catch (e) {
|
|
1296
1309
|
logger.warn(`多人视觉校验失败:${e.message}`)
|
|
1297
|
-
return { ok: true, degraded: true, message: session.text('.multi-verify-error', [String(e && e.message || e)]), verdict: null, outputs: images }
|
|
1310
|
+
return { ok: true, degraded: true, message: session.text('.multi-verify-error', [String(e && e.message || e)]), verdict: null, outputs: images, prompt: currentPrompt }
|
|
1298
1311
|
}
|
|
1299
1312
|
if (!data) {
|
|
1300
1313
|
logger.warn(`多人视觉校验返回无法解析:${reply.slice(0, 200)}`)
|
|
1301
|
-
return { ok: true, degraded: true, message: '', verdict: null, outputs: images }
|
|
1314
|
+
return { ok: true, degraded: true, message: '', verdict: null, outputs: images, prompt: currentPrompt }
|
|
1302
1315
|
}
|
|
1303
1316
|
const verdict = verdictFromData(data)
|
|
1304
1317
|
verdict.skipped = false
|
|
@@ -1332,7 +1345,7 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1332
1345
|
selectedOutputs = best.outputs
|
|
1333
1346
|
selectedVerdict = best.verdict
|
|
1334
1347
|
if (!multiAccepted && !cfg.multiSendDegradedCandidate) {
|
|
1335
|
-
return { ok: false, discarded: true, message: session.text('.multi-verify-discarded'), verdict: selectedVerdict, outputs: [] }
|
|
1348
|
+
return { ok: false, discarded: true, message: session.text('.multi-verify-discarded'), verdict: selectedVerdict, outputs: [], prompt: best.prompt }
|
|
1336
1349
|
}
|
|
1337
1350
|
const noteParts = []
|
|
1338
1351
|
if (multiAccepted) {
|
|
@@ -1341,7 +1354,7 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1341
1354
|
noteParts.push(session.text('.multi-verify-degraded', selectedVerdict.issues.length ? '(' + selectedVerdict.issues.join(';').slice(0, 80) + ')' : ''))
|
|
1342
1355
|
}
|
|
1343
1356
|
if (retries) noteParts.push(session.text('.multi-verify-failed', [selectedVerdict.issues.length ? ':' + selectedVerdict.issues.join(';').slice(0, 80) : '', retries]))
|
|
1344
|
-
return { ok: true, degraded: false, message: noteParts.join('\n'), verdict: selectedVerdict, outputs: selectedOutputs }
|
|
1357
|
+
return { ok: true, degraded: false, message: noteParts.join('\n'), verdict: selectedVerdict, outputs: selectedOutputs, prompt: best.prompt }
|
|
1345
1358
|
}
|
|
1346
1359
|
|
|
1347
1360
|
function buildVerifySystemPrompt(multiPerson, planCount) {
|
|
@@ -1354,7 +1367,7 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1354
1367
|
}
|
|
1355
1368
|
|
|
1356
1369
|
// 共享批量执行器:一次性扣除总价,逐张生成,单张失败只退该张单价。
|
|
1357
|
-
// runOne(i) 需返回 { ok, outputs, seed, message? };返回数组为多张输出(如视觉校验候选)。
|
|
1370
|
+
// runOne(i) 需返回 { ok, outputs, seed, prompt, message? };返回数组为多张输出(如视觉校验候选)。
|
|
1358
1371
|
async function executeBatch(USERID, isAdmin, count, unitPrice, runOne) {
|
|
1359
1372
|
const results = []
|
|
1360
1373
|
let successCount = 0
|
|
@@ -1368,7 +1381,7 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1368
1381
|
const outputs = Array.isArray(item.outputs) ? item.outputs : (item.outputs ? [item.outputs] : [])
|
|
1369
1382
|
if (item.ok && outputs.length) {
|
|
1370
1383
|
successCount += 1
|
|
1371
|
-
results.push({ i, ok: true, outputs, seed: item.seed, note: item.note || '' })
|
|
1384
|
+
results.push({ i, ok: true, outputs, seed: item.seed, prompt: item.prompt || '', note: item.note || '' })
|
|
1372
1385
|
} else {
|
|
1373
1386
|
if (!isAdmin) await refundP(USERID, unitPrice)
|
|
1374
1387
|
if (cfg.outputLogs) logger.warn(`批量第 ${i + 1} 张生成失败(${USERID}):${item.message || '无输出'}`)
|
|
@@ -1449,8 +1462,10 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1449
1462
|
// 即时反馈
|
|
1450
1463
|
const notice = []
|
|
1451
1464
|
if (parsedBatch.clamped) notice.push(session.text('.batch-limit', [count]))
|
|
1452
|
-
|
|
1453
|
-
|
|
1465
|
+
const feedback = feedbackBase(session, { firstPosition, count, totalPrice: count * price, isAdmin })
|
|
1466
|
+
notice.push(...feedback.notices)
|
|
1467
|
+
await sendNotices(session, notice)
|
|
1468
|
+
await sendNotices(session, [feedback.chargeNotice], { quote: true })
|
|
1454
1469
|
|
|
1455
1470
|
// 单张生成 +(可选)视觉校验
|
|
1456
1471
|
const runOne = async (i) => {
|
|
@@ -1460,25 +1475,27 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1460
1475
|
} else {
|
|
1461
1476
|
try { result = await runComfyGenerate(finalPrompt, size, { negativePrompt: multiNegative, unet }) } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
1462
1477
|
}
|
|
1463
|
-
if (!result.ok || !result.outputs || !result.outputs.length) return result
|
|
1464
|
-
if (!cfg.multiVerifyEnabled) {
|
|
1465
|
-
return { ok: true, outputs: result.outputs, seed: result.seed, note: session.text('.multi-degraded', ['(未启用校验或未配置视觉模型)']) }
|
|
1466
|
-
}
|
|
1467
|
-
const verified = await verifyGeneratedImages(session, result.outputs, text, finalPrompt, size, plan.characters.length, unet)
|
|
1468
|
-
if (!verified.ok) return { ok: false, message: verified.message }
|
|
1469
|
-
return { ok: true, outputs: verified.outputs, seed: result.seed, note: verified.message || '' }
|
|
1478
|
+
if (!result.ok || !result.outputs || !result.outputs.length) return result
|
|
1479
|
+
if (!cfg.multiVerifyEnabled) {
|
|
1480
|
+
return { ok: true, outputs: result.outputs, seed: result.seed, prompt: finalPrompt, note: session.text('.multi-degraded', ['(未启用校验或未配置视觉模型)']) }
|
|
1481
|
+
}
|
|
1482
|
+
const verified = await verifyGeneratedImages(session, result.outputs, text, finalPrompt, size, plan.characters.length, unet)
|
|
1483
|
+
if (!verified.ok) return { ok: false, message: verified.message }
|
|
1484
|
+
return { ok: true, outputs: verified.outputs, seed: result.seed, prompt: verified.prompt || finalPrompt, note: verified.message || '' }
|
|
1470
1485
|
}
|
|
1471
1486
|
|
|
1472
1487
|
const { results, successCount } = await executeBatch(USERID, isAdmin, count, price, runOne)
|
|
1473
1488
|
|
|
1474
|
-
// 汇总
|
|
1475
|
-
const allOutputs = []
|
|
1476
|
-
const
|
|
1489
|
+
// 汇总
|
|
1490
|
+
const allOutputs = []
|
|
1491
|
+
const forwardOutputs = []
|
|
1492
|
+
const notes = []
|
|
1477
1493
|
const seeds = []
|
|
1478
1494
|
const failures = []
|
|
1479
1495
|
for (const item of results) {
|
|
1480
|
-
if (item.ok) {
|
|
1481
|
-
allOutputs.push(...item.outputs)
|
|
1496
|
+
if (item.ok) {
|
|
1497
|
+
allOutputs.push(...item.outputs)
|
|
1498
|
+
forwardOutputs.push(...item.outputs.map(src => ({ src, prompt: item.prompt || finalPrompt })))
|
|
1482
1499
|
if (item.seed != null) seeds.push(item.seed)
|
|
1483
1500
|
if (item.note) notes.push(item.note)
|
|
1484
1501
|
} else {
|
|
@@ -1493,8 +1510,8 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1493
1510
|
|
|
1494
1511
|
if (cfg.outputLogs) logger.success(`${USERID} 多人生成成功 ${successCount}/${count} 张`)
|
|
1495
1512
|
|
|
1496
|
-
//
|
|
1497
|
-
await
|
|
1513
|
+
// 发图:合并转发,不引用原指令
|
|
1514
|
+
await sendImagesAsForward(session, forwardOutputs)
|
|
1498
1515
|
|
|
1499
1516
|
const reply = []
|
|
1500
1517
|
if (count > 1) {
|
|
@@ -1625,29 +1642,34 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1625
1642
|
if (identity && fixedChars[identity]) notice.push(`已固定角色「${identity}」的身份 tags,各阶段外观将保持一致。`)
|
|
1626
1643
|
if (!useLLM) notice.push(session.text('.series-no-llm'))
|
|
1627
1644
|
if (degradedStages) notice.push(session.text('.prompt-degraded', ['(连续图阶段优化失败,已使用原始描述)']))
|
|
1628
|
-
|
|
1629
|
-
|
|
1645
|
+
const feedback = feedbackBase(session, { firstPosition, count, totalPrice: count * price, isAdmin })
|
|
1646
|
+
notice.push(...feedback.notices)
|
|
1647
|
+
await sendNotices(session, notice)
|
|
1648
|
+
await sendNotices(session, [feedback.chargeNotice], { quote: true })
|
|
1630
1649
|
|
|
1631
1650
|
// 单阶段生成(共用 seed)
|
|
1632
1651
|
const runOne = async (i) => {
|
|
1633
1652
|
let result
|
|
1634
|
-
if (cfg.queueEnabled) {
|
|
1635
|
-
try { result = await queuedTasks[i] } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
1636
|
-
} else {
|
|
1637
|
-
try { result = await runComfyGenerate(stagePrompts[i], size, { seed, unet }) } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1653
|
+
if (cfg.queueEnabled) {
|
|
1654
|
+
try { result = await queuedTasks[i] } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
1655
|
+
} else {
|
|
1656
|
+
try { result = await runComfyGenerate(stagePrompts[i], size, { seed, unet }) } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
1657
|
+
}
|
|
1658
|
+
result.prompt = stagePrompts[i]
|
|
1659
|
+
return result
|
|
1640
1660
|
}
|
|
1641
1661
|
|
|
1642
1662
|
const { results, successCount } = await executeBatch(USERID, isAdmin, count, price, runOne)
|
|
1643
1663
|
|
|
1644
|
-
// 汇总
|
|
1645
|
-
const allOutputs = []
|
|
1646
|
-
const
|
|
1664
|
+
// 汇总
|
|
1665
|
+
const allOutputs = []
|
|
1666
|
+
const forwardOutputs = []
|
|
1667
|
+
const seeds = []
|
|
1647
1668
|
const failures = []
|
|
1648
1669
|
for (const item of results) {
|
|
1649
|
-
if (item.ok) {
|
|
1650
|
-
allOutputs.push(...item.outputs)
|
|
1670
|
+
if (item.ok) {
|
|
1671
|
+
allOutputs.push(...item.outputs)
|
|
1672
|
+
forwardOutputs.push(...item.outputs.map(src => ({ src, prompt: item.prompt || stagePrompts[item.i] || '' })))
|
|
1651
1673
|
if (item.seed != null) seeds.push(item.seed)
|
|
1652
1674
|
} else {
|
|
1653
1675
|
failures.push(`第 ${item.i + 1} 阶段:${item.message}`)
|
|
@@ -1661,8 +1683,8 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1661
1683
|
|
|
1662
1684
|
if (cfg.outputLogs) logger.success(`${USERID} 连续图生成成功 ${successCount}/${count} 阶段(seed=${seed})`)
|
|
1663
1685
|
|
|
1664
|
-
//
|
|
1665
|
-
await
|
|
1686
|
+
// 发图:合并转发,不引用原指令
|
|
1687
|
+
await sendImagesAsForward(session, forwardOutputs)
|
|
1666
1688
|
|
|
1667
1689
|
const reply = []
|
|
1668
1690
|
reply.push(session.text('.series-ok', [count * price, successCount, seed]))
|
|
@@ -2307,7 +2329,8 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
2307
2329
|
const optimized = await optimizePrompt(session, userPrompt, true, i2iRule, searchCache)
|
|
2308
2330
|
p = appendInlineProtectedTags(composePrompt(optimized.prompt || userPrompt, raw).prompt, userPrompt, raw)
|
|
2309
2331
|
}
|
|
2310
|
-
|
|
2332
|
+
const generated = await runComfyGenerate(p, parsedSize.size, Object.assign({ unet, seed }, i2iRun))
|
|
2333
|
+
return { ...generated, prompt: p }
|
|
2311
2334
|
}, { USERID, isAdmin, totalPrice: count * cfg.price })
|
|
2312
2335
|
if (!queued.ok) return queued.message
|
|
2313
2336
|
const queuedTasks = queued.tasks
|
|
@@ -2330,8 +2353,10 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
2330
2353
|
notice.push(session.text('.no-optimize', [reasons[noOptimizeReason] || noOptimizeReason]))
|
|
2331
2354
|
}
|
|
2332
2355
|
if (parsedBatch.clamped) notice.push(session.text('.batch-limit', [count]))
|
|
2333
|
-
|
|
2334
|
-
|
|
2356
|
+
const feedback = feedbackBase(session, { firstPosition, count, totalPrice: count * cfg.price, isAdmin })
|
|
2357
|
+
notice.push(...feedback.notices)
|
|
2358
|
+
await sendNotices(session, notice)
|
|
2359
|
+
await sendNotices(session, [feedback.chargeNotice], { quote: true })
|
|
2335
2360
|
|
|
2336
2361
|
// 单张生成
|
|
2337
2362
|
const runOne = async (i) => {
|
|
@@ -2343,22 +2368,25 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
2343
2368
|
let result
|
|
2344
2369
|
if (cfg.queueEnabled) {
|
|
2345
2370
|
try { result = await queuedTasks[i] } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
2346
|
-
} else {
|
|
2347
|
-
try { result = await runComfyGenerate(p, parsedSize.size, Object.assign({ unet, seed }, i2iRun)) } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
2348
|
-
}
|
|
2349
|
-
|
|
2371
|
+
} else {
|
|
2372
|
+
try { result = await runComfyGenerate(p, parsedSize.size, Object.assign({ unet, seed }, i2iRun)) } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
2373
|
+
}
|
|
2374
|
+
if (!result.prompt) result.prompt = p
|
|
2375
|
+
return result
|
|
2350
2376
|
}
|
|
2351
2377
|
|
|
2352
2378
|
const { results, successCount } = await executeBatch(USERID, isAdmin, count, cfg.price, runOne)
|
|
2353
2379
|
|
|
2354
|
-
// 汇总
|
|
2355
|
-
const allOutputs = []
|
|
2356
|
-
const
|
|
2357
|
-
const
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2380
|
+
// 汇总
|
|
2381
|
+
const allOutputs = []
|
|
2382
|
+
const forwardOutputs = []
|
|
2383
|
+
const seeds = []
|
|
2384
|
+
const failures = []
|
|
2385
|
+
for (const item of results) {
|
|
2386
|
+
if (item.ok) {
|
|
2387
|
+
allOutputs.push(...item.outputs)
|
|
2388
|
+
forwardOutputs.push(...item.outputs.map(src => ({ src, prompt: item.prompt || finalPrompt })))
|
|
2389
|
+
if (item.seed != null) seeds.push(item.seed)
|
|
2362
2390
|
} else {
|
|
2363
2391
|
failures.push(`第 ${item.i + 1} 张:${item.message}`)
|
|
2364
2392
|
}
|
|
@@ -2371,8 +2399,8 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
2371
2399
|
|
|
2372
2400
|
if (cfg.outputLogs) logger.success(`${USERID} 生成成功 ${successCount}/${count} 张`)
|
|
2373
2401
|
|
|
2374
|
-
//
|
|
2375
|
-
await
|
|
2402
|
+
// 发图:合并转发,不引用原指令
|
|
2403
|
+
await sendImagesAsForward(session, forwardOutputs)
|
|
2376
2404
|
|
|
2377
2405
|
const reply = []
|
|
2378
2406
|
if (count > 1) {
|
|
@@ -2525,5 +2553,5 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
2525
2553
|
})
|
|
2526
2554
|
|
|
2527
2555
|
// 暴露内部接口供自动化测试调用(Koishi 忽略 apply 返回值,不影响生产行为)
|
|
2528
|
-
return { couponConfirmFlow, buyCouponsAndConsume, normalizeConfirm, resolveCouponPrice, handleGenerateI2I, extractImageFromSession, uploadImageToComfyui, taggerImage, animaI2IWorkflow, animaStyleI2IWorkflow, animaOotdI2IWorkflow, buildI2IWorkflow, detectI2ICapabilities, parseDenoise }
|
|
2529
|
-
}
|
|
2556
|
+
return { couponConfirmFlow, buyCouponsAndConsume, normalizeConfirm, resolveCouponPrice, handleGenerateI2I, extractImageFromSession, uploadImageToComfyui, taggerImage, animaI2IWorkflow, animaStyleI2IWorkflow, animaOotdI2IWorkflow, buildI2IWorkflow, detectI2ICapabilities, parseDenoise, sendNotices, sendImagesAsForward, executeBatch }
|
|
2557
|
+
}
|