@zhin.js/plugin-ledger 1.0.0 → 1.0.1
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/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +34 -3
- package/lib/service.js.map +1 -1
- package/lib/store.d.ts +6 -0
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +25 -0
- package/lib/store.js.map +1 -1
- package/package.json +7 -7
- package/src/index.ts +1 -1
- package/src/service.ts +34 -4
- package/src/store.ts +25 -0
package/lib/index.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export { describePeriodRange, isInPeriod, localDateKey, localTimeKey, parsePerio
|
|
|
6
6
|
export { formatCategoryStatistics, formatEntryList, formatPeriodSummary, summarize, totalsByCategory, MAX_LISTED_ENTRIES, type CategoryTotal, type LedgerReply, type PeriodSummary, } from './report-format.js';
|
|
7
7
|
export { ledgerServiceToken } from './runtime.js';
|
|
8
8
|
export { LedgerService, type DailyReportAction, type LedgerServiceOptions } from './service.js';
|
|
9
|
-
export { defineLedgerTables, LedgerStore, ENTRY_TABLE, USER_TABLE, type LedgerEntry, type LedgerUserRecord } from './store.js';
|
|
9
|
+
export { defineLedgerTables, LedgerStore, ENTRY_TABLE, USER_TABLE, PRIVATE_TARGET_TABLE, type LedgerEntry, type LedgerUserRecord } from './store.js';
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACrJ,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAClJ,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC/G,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACrH,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,WAAW,EACX,UAAU,EACV,YAAY,EACZ,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,mBAAmB,EACnB,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACrJ,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAClJ,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC/G,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACrH,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,WAAW,EACX,UAAU,EACV,YAAY,EACZ,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,mBAAmB,EACnB,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -6,5 +6,5 @@ export { describePeriodRange, isInPeriod, localDateKey, localTimeKey, parsePerio
|
|
|
6
6
|
export { formatCategoryStatistics, formatEntryList, formatPeriodSummary, summarize, totalsByCategory, MAX_LISTED_ENTRIES, } from './report-format.js';
|
|
7
7
|
export { ledgerServiceToken } from './runtime.js';
|
|
8
8
|
export { LedgerService } from './service.js';
|
|
9
|
-
export { defineLedgerTables, LedgerStore, ENTRY_TABLE, USER_TABLE } from './store.js';
|
|
9
|
+
export { defineLedgerTables, LedgerStore, ENTRY_TABLE, USER_TABLE, PRIVATE_TARGET_TABLE } from './store.js';
|
|
10
10
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,gBAAgB,EAAgD,MAAM,aAAa,CAAC;AACrJ,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAgD,MAAM,kBAAkB,CAAC;AAClJ,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,gBAAgB,EAAqB,MAAM,eAAe,CAAC;AAC/G,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,eAAe,EAA0B,MAAM,YAAY,CAAC;AACrH,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,WAAW,EACX,UAAU,EACV,YAAY,GAEb,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,mBAAmB,EACnB,SAAS,EACT,gBAAgB,EAChB,kBAAkB,GAInB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,aAAa,EAAqD,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAA2C,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,gBAAgB,EAAgD,MAAM,aAAa,CAAC;AACrJ,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAgD,MAAM,kBAAkB,CAAC;AAClJ,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,gBAAgB,EAAqB,MAAM,eAAe,CAAC;AAC/G,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,eAAe,EAA0B,MAAM,YAAY,CAAC;AACrH,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,WAAW,EACX,UAAU,EACV,YAAY,GAEb,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,mBAAmB,EACnB,SAAS,EACT,gBAAgB,EAChB,kBAAkB,GAInB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,aAAa,EAAqD,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,oBAAoB,EAA2C,MAAM,YAAY,CAAC"}
|
package/lib/service.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,SAAS,CAAC;AACrG,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAuD,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAYrG,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,KAAK,GAAG,QAAQ,CAAC;AAExD,MAAM,WAAW,oBAAoB;IACnC,qCAAqC;IACrC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,qBAAa,aAAa;;gBAUZ,QAAQ,EAAE,kBAAkB,EAAE,QAAQ,EAAE,YAAY,GAAG,SAAS,EAAE,MAAM,EAAE,oBAAoB,EAAE,OAAO,GAAE,oBAAyB;IAwCxI,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAsB7D,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiB/D,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAM1E,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAM/E,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IA8BxE,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;IA0BnF;;;;OAIG;IACG,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;CAuBzC"}
|
package/lib/service.js
CHANGED
|
@@ -19,6 +19,8 @@ export class LedgerService {
|
|
|
19
19
|
#now;
|
|
20
20
|
/** Serialises writes per user so two quick `记` commands never share an `#id`. */
|
|
21
21
|
#userQueues = new Map();
|
|
22
|
+
/** Private targets already saved this process, to skip redundant writes. */
|
|
23
|
+
#knownPrivate = new Map();
|
|
22
24
|
constructor(database, outbound, config, options = {}) {
|
|
23
25
|
this.#store = new LedgerStore(database);
|
|
24
26
|
this.#outbound = outbound;
|
|
@@ -36,7 +38,32 @@ export class LedgerService {
|
|
|
36
38
|
});
|
|
37
39
|
return next;
|
|
38
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Some platforms (Discord, Lark, Slack, ...) address a private chat by a conversation id that is
|
|
43
|
+
* not the user id. When the user talks to the bot in private we save that real conversation and
|
|
44
|
+
* use it for later private deliveries; until then the user id is the best available guess.
|
|
45
|
+
*/
|
|
46
|
+
async #rememberPrivate(caller) {
|
|
47
|
+
if (!caller.isPrivate)
|
|
48
|
+
return;
|
|
49
|
+
const raw = JSON.stringify(caller.conversation);
|
|
50
|
+
if (this.#knownPrivate.get(caller.ownerKey) === raw)
|
|
51
|
+
return;
|
|
52
|
+
try {
|
|
53
|
+
await this.#store.setPrivateTarget(caller.ownerKey, caller.conversation);
|
|
54
|
+
this.#knownPrivate.set(caller.ownerKey, raw);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
logger.warn(`saving private target failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async #privateTargetOf(caller) {
|
|
61
|
+
if (caller.isPrivate)
|
|
62
|
+
return caller.conversation;
|
|
63
|
+
return (await this.#store.getPrivateTarget(caller.ownerKey)) ?? caller.privateTarget;
|
|
64
|
+
}
|
|
39
65
|
async addEntry(caller, text) {
|
|
66
|
+
await this.#rememberPrivate(caller);
|
|
40
67
|
const parsed = parseEntryInput(text);
|
|
41
68
|
if (!parsed.ok)
|
|
42
69
|
return parsed.reason;
|
|
@@ -58,6 +85,7 @@ export class LedgerService {
|
|
|
58
85
|
return `✅ 已记账 #${seq} · ${category} ${amount}${noteText}`;
|
|
59
86
|
}
|
|
60
87
|
async deleteEntry(caller, seq) {
|
|
88
|
+
await this.#rememberPrivate(caller);
|
|
61
89
|
return this.#withUserLock(caller.ownerKey, async () => {
|
|
62
90
|
const entry = await this.#store.findEntry(caller.ownerKey, seq);
|
|
63
91
|
if (!entry)
|
|
@@ -89,8 +117,10 @@ export class LedgerService {
|
|
|
89
117
|
}
|
|
90
118
|
/** Private chat: full reply. Group/channel: try a private message, else the masked reply. */
|
|
91
119
|
async #deliver(caller, reply, title) {
|
|
92
|
-
if (caller.isPrivate)
|
|
120
|
+
if (caller.isPrivate) {
|
|
121
|
+
await this.#rememberPrivate(caller);
|
|
93
122
|
return reply.full;
|
|
123
|
+
}
|
|
94
124
|
if (reply.full === reply.masked)
|
|
95
125
|
return reply.full;
|
|
96
126
|
const delivered = await this.#sendPrivately(caller, reply.full);
|
|
@@ -103,7 +133,7 @@ export class LedgerService {
|
|
|
103
133
|
if (!sendTarget)
|
|
104
134
|
return false;
|
|
105
135
|
try {
|
|
106
|
-
await sendTarget.call(this.#outbound, caller
|
|
136
|
+
await sendTarget.call(this.#outbound, await this.#privateTargetOf(caller), content);
|
|
107
137
|
return true;
|
|
108
138
|
}
|
|
109
139
|
catch (error) {
|
|
@@ -112,6 +142,7 @@ export class LedgerService {
|
|
|
112
142
|
}
|
|
113
143
|
}
|
|
114
144
|
async dailyReport(caller, action) {
|
|
145
|
+
await this.#rememberPrivate(caller);
|
|
115
146
|
const reportTime = this.#config.reportTime;
|
|
116
147
|
const user = await this.#store.getUser(caller.ownerKey);
|
|
117
148
|
if (action === 'status') {
|
|
@@ -135,7 +166,7 @@ export class LedgerService {
|
|
|
135
166
|
const today = localDateKey(nowMs, this.#config.timezone);
|
|
136
167
|
const alreadyPastReportTime = localTimeKey(nowMs, this.#config.timezone) >= reportTime;
|
|
137
168
|
const lastReportDate = alreadyPastReportTime ? today : '';
|
|
138
|
-
await this.#store.setReport(caller.ownerKey, true, caller
|
|
169
|
+
await this.#store.setReport(caller.ownerKey, true, await this.#privateTargetOf(caller), lastReportDate);
|
|
139
170
|
return caller.isPrivate ? confirmation : '🔒 已开启记账日报,确认消息已私信发送给你';
|
|
140
171
|
}
|
|
141
172
|
/**
|
package/lib/service.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
package/lib/store.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { DeliveryTarget, PluginDatabaseHost } from 'zhin.js';
|
|
2
2
|
export declare const ENTRY_TABLE = "ledger_entries";
|
|
3
3
|
export declare const USER_TABLE = "ledger_users";
|
|
4
|
+
/** Separate table: existing databases only run CREATE TABLE IF NOT EXISTS, so new columns on `ledger_users` would never appear. */
|
|
5
|
+
export declare const PRIVATE_TARGET_TABLE = "ledger_private_targets";
|
|
4
6
|
/** One expense. `seq` is the per-user number shown as `#id`; amounts are integer cents. */
|
|
5
7
|
export interface LedgerEntry {
|
|
6
8
|
readonly ownerKey: string;
|
|
@@ -36,6 +38,10 @@ export declare class LedgerStore {
|
|
|
36
38
|
findEntry(ownerKey: string, seq: number): Promise<LedgerEntry | undefined>;
|
|
37
39
|
deleteEntry(ownerKey: string, seq: number): Promise<void>;
|
|
38
40
|
setReport(ownerKey: string, enabled: boolean, target: DeliveryTarget | undefined, lastReportDate: string): Promise<void>;
|
|
41
|
+
/** The real private conversation saved when the user talked to the bot in private. */
|
|
42
|
+
getPrivateTarget(ownerKey: string): Promise<DeliveryTarget | undefined>;
|
|
43
|
+
/** Saves the real private conversation; an enabled daily report follows it. */
|
|
44
|
+
setPrivateTarget(ownerKey: string, target: DeliveryTarget): Promise<void>;
|
|
39
45
|
listReportSubscribers(): Promise<LedgerUserRecord[]>;
|
|
40
46
|
markReported(ownerKey: string, dateKey: string): Promise<void>;
|
|
41
47
|
}
|
package/lib/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,cAAc,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAErF,eAAO,MAAM,WAAW,mBAAmB,CAAC;AAC5C,eAAO,MAAM,UAAU,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,cAAc,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAErF,eAAO,MAAM,WAAW,mBAAmB,CAAC;AAC5C,eAAO,MAAM,UAAU,iBAAiB,CAAC;AACzC,mIAAmI;AACnI,eAAO,MAAM,oBAAoB,2BAA2B,CAAC;AAE7D,2FAA2F;AAC3F,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,cAAc,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,GAAG,IAAI,CAoBrF;AAgCD;;;;GAIG;AACH,qBAAa,WAAW;;gBAGV,QAAQ,EAAE,kBAAkB;IAUlC,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAqBtE,+FAA+F;IACzF,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOlD,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAW9C,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAOrD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAQ1E,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,GAAG,SAAS,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS9H,sFAAsF;IAChF,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAK7E,+EAA+E;IACzE,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAYzE,qBAAqB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAOpD,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGrE"}
|
package/lib/store.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export const ENTRY_TABLE = 'ledger_entries';
|
|
2
2
|
export const USER_TABLE = 'ledger_users';
|
|
3
|
+
/** Separate table: existing databases only run CREATE TABLE IF NOT EXISTS, so new columns on `ledger_users` would never appear. */
|
|
4
|
+
export const PRIVATE_TARGET_TABLE = 'ledger_private_targets';
|
|
3
5
|
/** Registers both tables; called from `setup()` before the database starts. */
|
|
4
6
|
export function defineLedgerTables(database) {
|
|
5
7
|
database.define(ENTRY_TABLE, {
|
|
@@ -17,6 +19,10 @@ export function defineLedgerTables(database) {
|
|
|
17
19
|
report_target: { type: 'text', default: '' },
|
|
18
20
|
last_report_date: { type: 'text', default: '' },
|
|
19
21
|
});
|
|
22
|
+
database.define(PRIVATE_TARGET_TABLE, {
|
|
23
|
+
owner_key: { type: 'text', nullable: false },
|
|
24
|
+
target: { type: 'text', nullable: false },
|
|
25
|
+
});
|
|
20
26
|
}
|
|
21
27
|
function toEntry(row) {
|
|
22
28
|
return {
|
|
@@ -124,6 +130,25 @@ export class LedgerStore {
|
|
|
124
130
|
last_report_date: lastReportDate,
|
|
125
131
|
}).where({ owner_key: ownerKey });
|
|
126
132
|
}
|
|
133
|
+
/** The real private conversation saved when the user talked to the bot in private. */
|
|
134
|
+
async getPrivateTarget(ownerKey) {
|
|
135
|
+
const rows = await this.#model(PRIVATE_TARGET_TABLE).select('owner_key', 'target').where({ owner_key: ownerKey });
|
|
136
|
+
return parseTarget(rows[0]?.target);
|
|
137
|
+
}
|
|
138
|
+
/** Saves the real private conversation; an enabled daily report follows it. */
|
|
139
|
+
async setPrivateTarget(ownerKey, target) {
|
|
140
|
+
const raw = JSON.stringify(target);
|
|
141
|
+
const model = this.#model(PRIVATE_TARGET_TABLE);
|
|
142
|
+
const existing = await model.select('owner_key', 'target').where({ owner_key: ownerKey });
|
|
143
|
+
if (existing.length > 0)
|
|
144
|
+
await model.update({ target: raw }).where({ owner_key: ownerKey });
|
|
145
|
+
else
|
|
146
|
+
await model.insert({ owner_key: ownerKey, target: raw });
|
|
147
|
+
const user = await this.getUser(ownerKey);
|
|
148
|
+
if (user?.reportEnabled) {
|
|
149
|
+
await this.#model(USER_TABLE).update({ report_target: raw }).where({ owner_key: ownerKey });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
127
152
|
async listReportSubscribers() {
|
|
128
153
|
const rows = await this.#model(USER_TABLE)
|
|
129
154
|
.select('owner_key', 'next_seq', 'report_enabled', 'report_target', 'last_report_date')
|
package/lib/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,WAAW,GAAG,gBAAgB,CAAC;AAC5C,MAAM,CAAC,MAAM,UAAU,GAAG,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/plugin-ledger",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Personal expense ledger for Zhin.js (Plugin Runtime): per-user entries in integer cents, period and category summaries, group privacy and an opt-in daily report",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"url": "https://github.com/lc-cn"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"
|
|
39
|
-
"zhin.js": "^1.1.
|
|
40
|
-
"@zhin.js/schedule-feature": "^1.1.
|
|
38
|
+
"zhin.js": "^1.1.13",
|
|
39
|
+
"@zhin.js/command": "^1.1.9",
|
|
40
|
+
"@zhin.js/schedule-feature": "^1.1.7"
|
|
41
41
|
},
|
|
42
42
|
"peerDependenciesMeta": {
|
|
43
43
|
"@zhin.js/command": {
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"typescript": "^6.0.3",
|
|
52
52
|
"vitest": "^4.1.10",
|
|
53
|
-
"@zhin.js/schedule-feature": "1.1.
|
|
54
|
-
"zhin.js": "1.
|
|
55
|
-
"
|
|
53
|
+
"@zhin.js/schedule-feature": "1.1.7",
|
|
54
|
+
"@zhin.js/testing": "1.0.5",
|
|
55
|
+
"zhin.js": "1.1.13"
|
|
56
56
|
},
|
|
57
57
|
"engines": {
|
|
58
58
|
"node": ">=22.19.0"
|
package/src/index.ts
CHANGED
|
@@ -26,4 +26,4 @@ export {
|
|
|
26
26
|
} from './report-format.js';
|
|
27
27
|
export { ledgerServiceToken } from './runtime.js';
|
|
28
28
|
export { LedgerService, type DailyReportAction, type LedgerServiceOptions } from './service.js';
|
|
29
|
-
export { defineLedgerTables, LedgerStore, ENTRY_TABLE, USER_TABLE, type LedgerEntry, type LedgerUserRecord } from './store.js';
|
|
29
|
+
export { defineLedgerTables, LedgerStore, ENTRY_TABLE, USER_TABLE, PRIVATE_TARGET_TABLE, type LedgerEntry, type LedgerUserRecord } from './store.js';
|
package/src/service.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getLogger, type OutboundHost, type PluginDatabaseHost } from 'zhin.js';
|
|
1
|
+
import { getLogger, type DeliveryTarget, type OutboundHost, type PluginDatabaseHost } from 'zhin.js';
|
|
2
2
|
import type { ResolvedLedgerConfig } from './config.js';
|
|
3
3
|
import { parseEntryInput } from './entry-input.js';
|
|
4
4
|
import type { LedgerCaller } from './identity.js';
|
|
@@ -36,6 +36,8 @@ export class LedgerService {
|
|
|
36
36
|
readonly #now: () => number;
|
|
37
37
|
/** Serialises writes per user so two quick `记` commands never share an `#id`. */
|
|
38
38
|
readonly #userQueues = new Map<string, Promise<unknown>>();
|
|
39
|
+
/** Private targets already saved this process, to skip redundant writes. */
|
|
40
|
+
readonly #knownPrivate = new Map<string, string>();
|
|
39
41
|
|
|
40
42
|
constructor(database: PluginDatabaseHost, outbound: OutboundHost | undefined, config: ResolvedLedgerConfig, options: LedgerServiceOptions = {}) {
|
|
41
43
|
this.#store = new LedgerStore(database);
|
|
@@ -55,7 +57,30 @@ export class LedgerService {
|
|
|
55
57
|
return next;
|
|
56
58
|
}
|
|
57
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Some platforms (Discord, Lark, Slack, ...) address a private chat by a conversation id that is
|
|
62
|
+
* not the user id. When the user talks to the bot in private we save that real conversation and
|
|
63
|
+
* use it for later private deliveries; until then the user id is the best available guess.
|
|
64
|
+
*/
|
|
65
|
+
async #rememberPrivate(caller: LedgerCaller): Promise<void> {
|
|
66
|
+
if (!caller.isPrivate) return;
|
|
67
|
+
const raw = JSON.stringify(caller.conversation);
|
|
68
|
+
if (this.#knownPrivate.get(caller.ownerKey) === raw) return;
|
|
69
|
+
try {
|
|
70
|
+
await this.#store.setPrivateTarget(caller.ownerKey, caller.conversation);
|
|
71
|
+
this.#knownPrivate.set(caller.ownerKey, raw);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
logger.warn(`saving private target failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async #privateTargetOf(caller: LedgerCaller): Promise<DeliveryTarget> {
|
|
78
|
+
if (caller.isPrivate) return caller.conversation;
|
|
79
|
+
return (await this.#store.getPrivateTarget(caller.ownerKey)) ?? caller.privateTarget;
|
|
80
|
+
}
|
|
81
|
+
|
|
58
82
|
async addEntry(caller: LedgerCaller, text: string): Promise<string> {
|
|
83
|
+
await this.#rememberPrivate(caller);
|
|
59
84
|
const parsed = parseEntryInput(text);
|
|
60
85
|
if (!parsed.ok) return parsed.reason;
|
|
61
86
|
const { category, amountCents, note } = parsed.entry;
|
|
@@ -77,6 +102,7 @@ export class LedgerService {
|
|
|
77
102
|
}
|
|
78
103
|
|
|
79
104
|
async deleteEntry(caller: LedgerCaller, seq: number): Promise<string> {
|
|
105
|
+
await this.#rememberPrivate(caller);
|
|
80
106
|
return this.#withUserLock(caller.ownerKey, async () => {
|
|
81
107
|
const entry = await this.#store.findEntry(caller.ownerKey, seq);
|
|
82
108
|
if (!entry) return `没有找到你的账目 #${seq}(只能删除自己的记账,可用「账单」查看编号)`;
|
|
@@ -112,7 +138,10 @@ export class LedgerService {
|
|
|
112
138
|
|
|
113
139
|
/** Private chat: full reply. Group/channel: try a private message, else the masked reply. */
|
|
114
140
|
async #deliver(caller: LedgerCaller, reply: LedgerReply, title: string): Promise<string> {
|
|
115
|
-
if (caller.isPrivate)
|
|
141
|
+
if (caller.isPrivate) {
|
|
142
|
+
await this.#rememberPrivate(caller);
|
|
143
|
+
return reply.full;
|
|
144
|
+
}
|
|
116
145
|
if (reply.full === reply.masked) return reply.full;
|
|
117
146
|
const delivered = await this.#sendPrivately(caller, reply.full);
|
|
118
147
|
if (delivered) return `🔒 ${title}已私信发送给你`;
|
|
@@ -123,7 +152,7 @@ export class LedgerService {
|
|
|
123
152
|
const sendTarget = this.#outbound?.sendTarget;
|
|
124
153
|
if (!sendTarget) return false;
|
|
125
154
|
try {
|
|
126
|
-
await sendTarget.call(this.#outbound, caller
|
|
155
|
+
await sendTarget.call(this.#outbound, await this.#privateTargetOf(caller), content);
|
|
127
156
|
return true;
|
|
128
157
|
} catch (error) {
|
|
129
158
|
logger.debug(`private delivery unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -132,6 +161,7 @@ export class LedgerService {
|
|
|
132
161
|
}
|
|
133
162
|
|
|
134
163
|
async dailyReport(caller: LedgerCaller, action: DailyReportAction): Promise<string> {
|
|
164
|
+
await this.#rememberPrivate(caller);
|
|
135
165
|
const reportTime = this.#config.reportTime;
|
|
136
166
|
const user = await this.#store.getUser(caller.ownerKey);
|
|
137
167
|
if (action === 'status') {
|
|
@@ -152,7 +182,7 @@ export class LedgerService {
|
|
|
152
182
|
const today = localDateKey(nowMs, this.#config.timezone);
|
|
153
183
|
const alreadyPastReportTime = localTimeKey(nowMs, this.#config.timezone) >= reportTime;
|
|
154
184
|
const lastReportDate = alreadyPastReportTime ? today : '';
|
|
155
|
-
await this.#store.setReport(caller.ownerKey, true, caller
|
|
185
|
+
await this.#store.setReport(caller.ownerKey, true, await this.#privateTargetOf(caller), lastReportDate);
|
|
156
186
|
return caller.isPrivate ? confirmation : '🔒 已开启记账日报,确认消息已私信发送给你';
|
|
157
187
|
}
|
|
158
188
|
|
package/src/store.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type { DatabaseHostModel, DeliveryTarget, PluginDatabaseHost } from 'zhin
|
|
|
2
2
|
|
|
3
3
|
export const ENTRY_TABLE = 'ledger_entries';
|
|
4
4
|
export const USER_TABLE = 'ledger_users';
|
|
5
|
+
/** Separate table: existing databases only run CREATE TABLE IF NOT EXISTS, so new columns on `ledger_users` would never appear. */
|
|
6
|
+
export const PRIVATE_TARGET_TABLE = 'ledger_private_targets';
|
|
5
7
|
|
|
6
8
|
/** One expense. `seq` is the per-user number shown as `#id`; amounts are integer cents. */
|
|
7
9
|
export interface LedgerEntry {
|
|
@@ -39,6 +41,10 @@ export function defineLedgerTables(database: Pick<PluginDatabaseHost, 'define'>)
|
|
|
39
41
|
report_target: { type: 'text', default: '' },
|
|
40
42
|
last_report_date: { type: 'text', default: '' },
|
|
41
43
|
});
|
|
44
|
+
database.define(PRIVATE_TARGET_TABLE, {
|
|
45
|
+
owner_key: { type: 'text', nullable: false },
|
|
46
|
+
target: { type: 'text', nullable: false },
|
|
47
|
+
});
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
function toEntry(row: Record<string, unknown>): LedgerEntry {
|
|
@@ -157,6 +163,25 @@ export class LedgerStore {
|
|
|
157
163
|
}).where({ owner_key: ownerKey });
|
|
158
164
|
}
|
|
159
165
|
|
|
166
|
+
/** The real private conversation saved when the user talked to the bot in private. */
|
|
167
|
+
async getPrivateTarget(ownerKey: string): Promise<DeliveryTarget | undefined> {
|
|
168
|
+
const rows = await this.#model(PRIVATE_TARGET_TABLE).select('owner_key', 'target').where({ owner_key: ownerKey });
|
|
169
|
+
return parseTarget(rows[0]?.target);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Saves the real private conversation; an enabled daily report follows it. */
|
|
173
|
+
async setPrivateTarget(ownerKey: string, target: DeliveryTarget): Promise<void> {
|
|
174
|
+
const raw = JSON.stringify(target);
|
|
175
|
+
const model = this.#model(PRIVATE_TARGET_TABLE);
|
|
176
|
+
const existing = await model.select('owner_key', 'target').where({ owner_key: ownerKey });
|
|
177
|
+
if (existing.length > 0) await model.update({ target: raw }).where({ owner_key: ownerKey });
|
|
178
|
+
else await model.insert({ owner_key: ownerKey, target: raw });
|
|
179
|
+
const user = await this.getUser(ownerKey);
|
|
180
|
+
if (user?.reportEnabled) {
|
|
181
|
+
await this.#model(USER_TABLE).update({ report_target: raw }).where({ owner_key: ownerKey });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
160
185
|
async listReportSubscribers(): Promise<LedgerUserRecord[]> {
|
|
161
186
|
const rows = await this.#model(USER_TABLE)
|
|
162
187
|
.select('owner_key', 'next_seq', 'report_enabled', 'report_target', 'last_report_date')
|