polycopy 0.0.9 → 0.1.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/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const child_process = require("child_process");
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
|
-
const paths = require("./paths-
|
|
6
|
+
const paths = require("./paths-CEjGES8j.js");
|
|
7
7
|
function _interopNamespace(e) {
|
|
8
8
|
if (e && e.__esModule) return e;
|
|
9
9
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
@@ -35,6 +35,10 @@ function getRunningPid() {
|
|
|
35
35
|
try {
|
|
36
36
|
const pid = parseInt(fs__namespace.readFileSync(paths.PID_FILE, "utf-8").trim());
|
|
37
37
|
process.kill(pid, 0);
|
|
38
|
+
if (!isPolycopyProcess(pid)) {
|
|
39
|
+
fs__namespace.unlinkSync(paths.PID_FILE);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
38
42
|
return pid;
|
|
39
43
|
} catch {
|
|
40
44
|
fs__namespace.unlinkSync(paths.PID_FILE);
|
|
@@ -111,10 +115,65 @@ async function startDaemon() {
|
|
|
111
115
|
const mainPath = path__namespace.join(__dirname, "index.js");
|
|
112
116
|
await import(mainPath);
|
|
113
117
|
}
|
|
118
|
+
function isPolycopyProcess(pid) {
|
|
119
|
+
if (process.platform === "win32") {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
const result = child_process.spawnSync("ps", ["-p", String(pid), "-o", "command="], {
|
|
123
|
+
encoding: "utf-8"
|
|
124
|
+
});
|
|
125
|
+
if (result.status !== 0) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
const command = result.stdout.trim();
|
|
129
|
+
if (!command) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return command.includes(paths.DIST_INDEX) || command.includes(paths.DIST_CLI);
|
|
133
|
+
}
|
|
134
|
+
function findPolycopyPids() {
|
|
135
|
+
if (process.platform === "win32") {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
const result = child_process.spawnSync("ps", ["-ax", "-o", "pid=,command="], {
|
|
139
|
+
encoding: "utf-8"
|
|
140
|
+
});
|
|
141
|
+
if (result.status !== 0) {
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
return result.stdout.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
145
|
+
const firstSpace = line.indexOf(" ");
|
|
146
|
+
if (firstSpace === -1) return null;
|
|
147
|
+
const pidText = line.slice(0, firstSpace).trim();
|
|
148
|
+
const command = line.slice(firstSpace + 1).trim();
|
|
149
|
+
const pid = parseInt(pidText, 10);
|
|
150
|
+
if (Number.isNaN(pid)) return null;
|
|
151
|
+
if (pid === process.pid) return null;
|
|
152
|
+
if (!(command.includes(paths.DIST_INDEX) || command.includes(paths.DIST_CLI))) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
return pid;
|
|
156
|
+
}).filter((pid) => pid !== null);
|
|
157
|
+
}
|
|
114
158
|
function stopDaemon() {
|
|
115
159
|
const pid = getRunningPid();
|
|
116
160
|
if (!pid) {
|
|
117
|
-
|
|
161
|
+
const fallbackPids = findPolycopyPids();
|
|
162
|
+
if (fallbackPids.length === 0) {
|
|
163
|
+
console.log("ℹ️ 没有运行中的前台/后台进程");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
console.log(
|
|
167
|
+
`⚠️ 未找到 PID 文件,尝试停止前台/后台进程 (${fallbackPids.length} 个)`
|
|
168
|
+
);
|
|
169
|
+
fallbackPids.forEach((fallbackPid) => {
|
|
170
|
+
try {
|
|
171
|
+
process.kill(fallbackPid, "SIGTERM");
|
|
172
|
+
console.log(`✅ 已发送停止信号 (PID: ${fallbackPid})`);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error(`❌ 停止失败 (PID: ${fallbackPid}):`, error);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
118
177
|
return;
|
|
119
178
|
}
|
|
120
179
|
try {
|
package/dist/config.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const init = require("./init-
|
|
2
|
+
const init = require("./init-C5cwUXyD.js");
|
|
3
3
|
const grammy = require("grammy");
|
|
4
4
|
const wallet = require("@ethersproject/wallet");
|
|
5
5
|
const clobClient = require("@polymarket/clob-client");
|
|
@@ -11,7 +11,7 @@ const builderRelayerClient = require("@polymarket/builder-relayer-client");
|
|
|
11
11
|
const setPromiseInterval = require("set-promise-interval");
|
|
12
12
|
const child_process = require("child_process");
|
|
13
13
|
const fs = require("fs");
|
|
14
|
-
const paths = require("./paths-
|
|
14
|
+
const paths = require("./paths-CEjGES8j.js");
|
|
15
15
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
16
16
|
function _interopNamespace(e) {
|
|
17
17
|
if (e && e.__esModule) return e;
|
|
@@ -175,14 +175,14 @@ class ClobClientWrapper {
|
|
|
175
175
|
*/
|
|
176
176
|
async getTickSize(tokenId) {
|
|
177
177
|
const client = this.ensureInitialized();
|
|
178
|
-
return
|
|
178
|
+
return client.getTickSize(tokenId);
|
|
179
179
|
}
|
|
180
180
|
/**
|
|
181
181
|
* 获取 token 是否为 negRisk
|
|
182
182
|
*/
|
|
183
183
|
async getNegRisk(tokenId) {
|
|
184
184
|
const client = this.ensureInitialized();
|
|
185
|
-
return
|
|
185
|
+
return client.getNegRisk(tokenId);
|
|
186
186
|
}
|
|
187
187
|
/**
|
|
188
188
|
* 创建买单(GTC 限价单)
|
|
@@ -204,13 +204,13 @@ class ClobClientWrapper {
|
|
|
204
204
|
clobClient.OrderType.GTC
|
|
205
205
|
);
|
|
206
206
|
if (response.success) {
|
|
207
|
-
init.logger.success(
|
|
207
|
+
init.logger.success(`买单已提交: ${response.orderID}`);
|
|
208
208
|
return {
|
|
209
209
|
success: true,
|
|
210
210
|
orderId: response.orderID
|
|
211
211
|
};
|
|
212
212
|
} else {
|
|
213
|
-
init.logger.error(
|
|
213
|
+
init.logger.error(`买单提交失败: ${response.errorMsg}`);
|
|
214
214
|
return {
|
|
215
215
|
success: false,
|
|
216
216
|
orderId: "",
|
|
@@ -219,7 +219,7 @@ class ClobClientWrapper {
|
|
|
219
219
|
}
|
|
220
220
|
} catch (error) {
|
|
221
221
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
222
|
-
init.logger.error(
|
|
222
|
+
init.logger.error(`买单提交失败: ${errorMsg}`);
|
|
223
223
|
return {
|
|
224
224
|
success: false,
|
|
225
225
|
orderId: "",
|
|
@@ -272,7 +272,7 @@ class ClobClientWrapper {
|
|
|
272
272
|
*/
|
|
273
273
|
async getOrder(orderId) {
|
|
274
274
|
const client = this.ensureInitialized();
|
|
275
|
-
return
|
|
275
|
+
return client.getOrder(orderId);
|
|
276
276
|
}
|
|
277
277
|
/**
|
|
278
278
|
* 取消订单
|
|
@@ -410,7 +410,7 @@ class AssetFilter {
|
|
|
410
410
|
this.traded.set(endTime, /* @__PURE__ */ new Set());
|
|
411
411
|
}
|
|
412
412
|
this.traded.get(endTime).add(assetId);
|
|
413
|
-
init.logger.info(`已标记 assetId
|
|
413
|
+
init.logger.info(`已标记 assetId: ${assetId.slice(0, 10)}...`);
|
|
414
414
|
}
|
|
415
415
|
/**
|
|
416
416
|
* 清理过期记录
|
|
@@ -530,10 +530,20 @@ class Redeemer {
|
|
|
530
530
|
records = /* @__PURE__ */ new Map();
|
|
531
531
|
intervalId;
|
|
532
532
|
delayTimeoutId;
|
|
533
|
-
running
|
|
533
|
+
// stopped: 未启动; running: 正常轮询; paused: 限流等待恢复
|
|
534
|
+
state = "stopped";
|
|
535
|
+
scheduler;
|
|
534
536
|
redeemFn = null;
|
|
535
537
|
funderAddress = "";
|
|
536
538
|
onRedeemSuccess = null;
|
|
539
|
+
constructor(scheduler = {
|
|
540
|
+
setInterval: setPromiseInterval__default.default,
|
|
541
|
+
clearInterval: setPromiseInterval.clearPromiseInterval,
|
|
542
|
+
setTimeout,
|
|
543
|
+
clearTimeout
|
|
544
|
+
}) {
|
|
545
|
+
this.scheduler = scheduler;
|
|
546
|
+
}
|
|
537
547
|
/**
|
|
538
548
|
* 启动 Redeemer
|
|
539
549
|
* @param redeemFn 执行 Redeem 的函数(返回交易哈希)
|
|
@@ -541,26 +551,24 @@ class Redeemer {
|
|
|
541
551
|
* @param onRedeemSuccess Redeem 成功后的回调(用于更新余额缓存)
|
|
542
552
|
*/
|
|
543
553
|
start(redeemFn, funderAddress, onRedeemSuccess) {
|
|
544
|
-
if (this.
|
|
554
|
+
if (this.state !== "stopped") {
|
|
545
555
|
return;
|
|
546
556
|
}
|
|
547
557
|
this.redeemFn = redeemFn;
|
|
548
558
|
this.funderAddress = funderAddress;
|
|
549
559
|
this.onRedeemSuccess = onRedeemSuccess || null;
|
|
550
|
-
this.
|
|
551
|
-
init.logger.
|
|
552
|
-
this.
|
|
553
|
-
await this.runOnce();
|
|
554
|
-
}, BASE_INTERVAL);
|
|
560
|
+
this.state = "running";
|
|
561
|
+
init.logger.success("自动 redeem 已启动");
|
|
562
|
+
this.startInterval();
|
|
555
563
|
}
|
|
556
564
|
/**
|
|
557
|
-
*
|
|
565
|
+
* 启动定时任务(仅在 running 状态生效)
|
|
558
566
|
*/
|
|
559
567
|
startInterval() {
|
|
560
|
-
if (
|
|
568
|
+
if (this.state !== "running" || this.intervalId !== void 0) {
|
|
561
569
|
return;
|
|
562
570
|
}
|
|
563
|
-
this.intervalId =
|
|
571
|
+
this.intervalId = this.scheduler.setInterval(async () => {
|
|
564
572
|
await this.runOnce();
|
|
565
573
|
}, BASE_INTERVAL);
|
|
566
574
|
}
|
|
@@ -569,15 +577,19 @@ class Redeemer {
|
|
|
569
577
|
*/
|
|
570
578
|
pauseAndRestart(delayMs) {
|
|
571
579
|
if (this.intervalId !== void 0) {
|
|
572
|
-
|
|
580
|
+
this.scheduler.clearInterval(this.intervalId);
|
|
573
581
|
this.intervalId = void 0;
|
|
574
582
|
}
|
|
575
|
-
this.delayTimeoutId
|
|
583
|
+
if (this.delayTimeoutId) {
|
|
584
|
+
this.scheduler.clearTimeout(this.delayTimeoutId);
|
|
585
|
+
this.delayTimeoutId = void 0;
|
|
586
|
+
}
|
|
587
|
+
this.state = "paused";
|
|
588
|
+
this.delayTimeoutId = this.scheduler.setTimeout(() => {
|
|
576
589
|
this.delayTimeoutId = void 0;
|
|
577
|
-
if (this.
|
|
578
|
-
this.
|
|
579
|
-
|
|
580
|
-
});
|
|
590
|
+
if (this.state === "paused") {
|
|
591
|
+
this.state = "running";
|
|
592
|
+
this.startInterval();
|
|
581
593
|
}
|
|
582
594
|
}, delayMs);
|
|
583
595
|
}
|
|
@@ -586,14 +598,14 @@ class Redeemer {
|
|
|
586
598
|
*/
|
|
587
599
|
stop() {
|
|
588
600
|
if (this.intervalId !== void 0) {
|
|
589
|
-
|
|
601
|
+
this.scheduler.clearInterval(this.intervalId);
|
|
590
602
|
this.intervalId = void 0;
|
|
591
603
|
}
|
|
592
604
|
if (this.delayTimeoutId) {
|
|
593
|
-
clearTimeout(this.delayTimeoutId);
|
|
605
|
+
this.scheduler.clearTimeout(this.delayTimeoutId);
|
|
594
606
|
this.delayTimeoutId = void 0;
|
|
595
607
|
}
|
|
596
|
-
this.
|
|
608
|
+
this.state = "stopped";
|
|
597
609
|
init.logger.info("Redeemer 已停止");
|
|
598
610
|
}
|
|
599
611
|
/**
|
|
@@ -621,7 +633,9 @@ class Redeemer {
|
|
|
621
633
|
positions.push({
|
|
622
634
|
conditionId: item.conditionId,
|
|
623
635
|
assetId: item.asset,
|
|
624
|
-
currentValue
|
|
636
|
+
currentValue,
|
|
637
|
+
slug: item.slug,
|
|
638
|
+
outcome: item.outcome
|
|
625
639
|
});
|
|
626
640
|
} else {
|
|
627
641
|
break;
|
|
@@ -633,7 +647,7 @@ class Redeemer {
|
|
|
633
647
|
* 执行一轮 Redeem
|
|
634
648
|
*/
|
|
635
649
|
async runOnce() {
|
|
636
|
-
if (
|
|
650
|
+
if (this.state !== "running" || !this.redeemFn) {
|
|
637
651
|
return;
|
|
638
652
|
}
|
|
639
653
|
let positions;
|
|
@@ -651,7 +665,16 @@ class Redeemer {
|
|
|
651
665
|
init.logger.error(`获取仓位失败: ${errorMsg}`);
|
|
652
666
|
return;
|
|
653
667
|
}
|
|
654
|
-
|
|
668
|
+
const pendingRedeemablePositions = positions.filter((position) => {
|
|
669
|
+
const record = this.records.get(position.conditionId);
|
|
670
|
+
return !record?.success;
|
|
671
|
+
});
|
|
672
|
+
if (pendingRedeemablePositions.length > 0) {
|
|
673
|
+
this.logPositions(
|
|
674
|
+
`自动 redeem: 发现 ${pendingRedeemablePositions.length} 个可 Redeem 仓位`,
|
|
675
|
+
pendingRedeemablePositions.map((position) => ({ position }))
|
|
676
|
+
);
|
|
677
|
+
}
|
|
655
678
|
for (const position of positions) {
|
|
656
679
|
let record = this.records.get(position.conditionId);
|
|
657
680
|
if (!record) {
|
|
@@ -679,11 +702,10 @@ conditionId: ${position.conditionId}
|
|
|
679
702
|
}
|
|
680
703
|
} else if (record.failedCount < MAX_MATCH_TIMES) {
|
|
681
704
|
try {
|
|
682
|
-
const txHash = await this.redeemFn(position.conditionId);
|
|
705
|
+
const txHash = await this.redeemFn?.(position.conditionId);
|
|
683
706
|
record.success = true;
|
|
684
|
-
init.logger.info(`Redeem
|
|
707
|
+
init.logger.info(`Redeem 已执行: ${txHash}`);
|
|
685
708
|
} catch (error) {
|
|
686
|
-
record.failedCount++;
|
|
687
709
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
688
710
|
if (errorMsg.includes("429") || errorMsg.includes("Too Many Requests")) {
|
|
689
711
|
const resetSeconds = this.parseResetSeconds(errorMsg);
|
|
@@ -692,6 +714,7 @@ conditionId: ${position.conditionId}
|
|
|
692
714
|
this.pauseAndRestart((resetSeconds + 60) * 1e3);
|
|
693
715
|
return;
|
|
694
716
|
}
|
|
717
|
+
record.failedCount++;
|
|
695
718
|
init.logger.error(
|
|
696
719
|
`Redeem 异常 (第 ${record.failedCount} 次): ${errorMsg}`
|
|
697
720
|
);
|
|
@@ -706,24 +729,49 @@ conditionId: ${position.conditionId}
|
|
|
706
729
|
}
|
|
707
730
|
}
|
|
708
731
|
}
|
|
709
|
-
const
|
|
732
|
+
const successItems = [];
|
|
710
733
|
for (const [conditionId, record] of this.records.entries()) {
|
|
711
734
|
const stillExists = positions.some((p) => p.conditionId === conditionId);
|
|
712
735
|
if (!stillExists) {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
736
|
+
successItems.push({
|
|
737
|
+
position: record.position,
|
|
738
|
+
matchedCount: record.matchedCount
|
|
739
|
+
});
|
|
716
740
|
this.records.delete(conditionId);
|
|
717
741
|
}
|
|
718
742
|
}
|
|
719
|
-
if (
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
}
|
|
743
|
+
if (successItems.length > 0) {
|
|
744
|
+
const successTitle = `自动 redeem: 成功 ${successItems.length} 个`;
|
|
745
|
+
const successLines = this.logPositions(successTitle, successItems);
|
|
746
|
+
notifier.success(successTitle, successLines.join("\n"));
|
|
724
747
|
this.onRedeemSuccess?.();
|
|
725
748
|
}
|
|
726
|
-
init.logger.info(`
|
|
749
|
+
init.logger.info(`redeem 状态: ${this.records.size} 个待确认`);
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* 统一格式化仓位信息
|
|
753
|
+
*/
|
|
754
|
+
formatPositionLine(position, matchedCount) {
|
|
755
|
+
const label = position.slug || `${position.assetId.slice(0, 10)}...`;
|
|
756
|
+
const outcomeLabel = position.outcome ? ` ${position.outcome}` : "";
|
|
757
|
+
const matchedLabel = matchedCount !== void 0 ? ` (matched: ${matchedCount})` : "";
|
|
758
|
+
return `${label}${outcomeLabel}: ${position.currentValue} USDC${matchedLabel}`;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* 统一输出 Redeem 仓位列表
|
|
762
|
+
*/
|
|
763
|
+
logPositions(title, items) {
|
|
764
|
+
init.logger.info(title);
|
|
765
|
+
const lines = [];
|
|
766
|
+
for (const item of items) {
|
|
767
|
+
const line = this.formatPositionLine(
|
|
768
|
+
item.position,
|
|
769
|
+
item.matchedCount
|
|
770
|
+
);
|
|
771
|
+
lines.push(line);
|
|
772
|
+
init.logger.line("", `- ${line}`);
|
|
773
|
+
}
|
|
774
|
+
return lines;
|
|
727
775
|
}
|
|
728
776
|
/**
|
|
729
777
|
* 解析 429 错误中的 resets in 时间
|
|
@@ -862,8 +910,7 @@ class Trader {
|
|
|
862
910
|
}
|
|
863
911
|
this.assetFilter.markTraded(assetId, endTime);
|
|
864
912
|
if (sellPrice === 1) {
|
|
865
|
-
init.logger.
|
|
866
|
-
init.logger.line("", "sellPrice = 1,成交后将自动 redeem");
|
|
913
|
+
init.logger.info("sellPrice = 1,成交后将自动 redeem");
|
|
867
914
|
return;
|
|
868
915
|
}
|
|
869
916
|
this.watchAndHandle(
|
|
@@ -990,10 +1037,10 @@ class Trader {
|
|
|
990
1037
|
init.logger.info(`创建卖单: ${size.toFixed(4)} 份 @ ${price}`);
|
|
991
1038
|
const sellResult = await this.client.createSellOrder(assetId, price, size);
|
|
992
1039
|
if (sellResult.success) {
|
|
993
|
-
init.logger.success(
|
|
1040
|
+
init.logger.success(`卖单已提交: ${sellResult.orderId}`);
|
|
994
1041
|
} else {
|
|
995
|
-
init.logger.error(
|
|
996
|
-
notifier.error("
|
|
1042
|
+
init.logger.error(`卖单提交失败: ${sellResult.errorMsg}`);
|
|
1043
|
+
notifier.error("卖单提交失败", `错误: ${sellResult.errorMsg}`);
|
|
997
1044
|
}
|
|
998
1045
|
}
|
|
999
1046
|
/**
|
|
@@ -1003,7 +1050,7 @@ class Trader {
|
|
|
1003
1050
|
if (!this.relayClient.isInitialized()) {
|
|
1004
1051
|
throw new Error("RelayClient 未初始化");
|
|
1005
1052
|
}
|
|
1006
|
-
return
|
|
1053
|
+
return this.relayClient.redeem(conditionId);
|
|
1007
1054
|
}
|
|
1008
1055
|
/**
|
|
1009
1056
|
* 停止交易模块
|
|
@@ -1021,7 +1068,11 @@ function enterDaemonMode() {
|
|
|
1021
1068
|
const child = child_process.spawn(process.execPath, [__filename], {
|
|
1022
1069
|
detached: true,
|
|
1023
1070
|
stdio: "ignore",
|
|
1024
|
-
env: {
|
|
1071
|
+
env: {
|
|
1072
|
+
...process.env,
|
|
1073
|
+
POLYCOPY_DAEMON: void 0,
|
|
1074
|
+
POLYCOPY_DAEMON_CHILD: "1"
|
|
1075
|
+
}
|
|
1025
1076
|
});
|
|
1026
1077
|
if (child.pid) {
|
|
1027
1078
|
fs__namespace.writeFileSync(paths.PID_FILE, child.pid.toString());
|
|
@@ -1075,7 +1126,7 @@ async function main() {
|
|
|
1075
1126
|
config.telegram.botToken,
|
|
1076
1127
|
config.telegram.targetChatIds || []
|
|
1077
1128
|
);
|
|
1078
|
-
listener.onChatIdsChanged(
|
|
1129
|
+
listener.onChatIdsChanged((chatIds) => {
|
|
1079
1130
|
const telegram = init.configLocal.getItem("telegram");
|
|
1080
1131
|
if (!telegram) {
|
|
1081
1132
|
init.logger.error("警告: 无法保存频道 ID,telegram 配置不存在");
|
|
@@ -1106,6 +1157,13 @@ async function main() {
|
|
|
1106
1157
|
}
|
|
1107
1158
|
}
|
|
1108
1159
|
});
|
|
1160
|
+
if (process.env.POLYCOPY_DAEMON === "1") {
|
|
1161
|
+
enterDaemonMode();
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
const runMode = process.env.POLYCOPY_DAEMON_CHILD === "1" ? "后台模式" : "前台模式";
|
|
1165
|
+
notifier.success("程序已启动", `模式: ${runMode}
|
|
1166
|
+
PID: ${process.pid}`);
|
|
1109
1167
|
let isShuttingDown = false;
|
|
1110
1168
|
const shutdown = () => {
|
|
1111
1169
|
if (isShuttingDown) return;
|
|
@@ -1114,6 +1172,8 @@ async function main() {
|
|
|
1114
1172
|
process.removeAllListeners("SIGTERM");
|
|
1115
1173
|
init.logger.blank();
|
|
1116
1174
|
init.logger.info("正在退出...");
|
|
1175
|
+
notifier.warning("程序已停止", `模式: ${runMode}
|
|
1176
|
+
PID: ${process.pid}`);
|
|
1117
1177
|
setTimeout(() => {
|
|
1118
1178
|
process.exit(0);
|
|
1119
1179
|
}, 1e3);
|
|
@@ -1123,10 +1183,6 @@ async function main() {
|
|
|
1123
1183
|
};
|
|
1124
1184
|
process.on("SIGINT", shutdown);
|
|
1125
1185
|
process.on("SIGTERM", shutdown);
|
|
1126
|
-
if (process.env.POLYCOPY_DAEMON === "1") {
|
|
1127
|
-
enterDaemonMode();
|
|
1128
|
-
return;
|
|
1129
|
-
}
|
|
1130
1186
|
await listener.start(() => {
|
|
1131
1187
|
if (trader.isInitialized()) {
|
|
1132
1188
|
trader.startRedeemer();
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const readline = require("readline");
|
|
3
3
|
const fsExtra = require("fs-extra");
|
|
4
4
|
const path = require("path");
|
|
5
|
-
const paths = require("./paths-
|
|
5
|
+
const paths = require("./paths-CEjGES8j.js");
|
|
6
6
|
const grammy = require("grammy");
|
|
7
7
|
const winston = require("winston");
|
|
8
8
|
const DailyRotateFile = require("winston-daily-rotate-file");
|
|
@@ -462,7 +462,7 @@ class TelegramListener {
|
|
|
462
462
|
logger.info("正在启动 Bot...");
|
|
463
463
|
const botInfo = await this.bot.api.getMe();
|
|
464
464
|
const botId = botInfo.id;
|
|
465
|
-
logger.
|
|
465
|
+
logger.info(`Bot 信息: @${botInfo.username} (ID: ${botId})`);
|
|
466
466
|
await this.checkAndDisplayChannels(botId);
|
|
467
467
|
if (onReady) {
|
|
468
468
|
await onReady();
|
|
@@ -578,7 +578,7 @@ class TelegramListener {
|
|
|
578
578
|
*/
|
|
579
579
|
async getBotInfo() {
|
|
580
580
|
try {
|
|
581
|
-
return
|
|
581
|
+
return this.bot.api.getMe();
|
|
582
582
|
} catch (error) {
|
|
583
583
|
throw new Error(`获取 Bot 信息失败: ${error}`);
|
|
584
584
|
}
|
|
@@ -776,7 +776,7 @@ async function runConfigWizard(existingConfig) {
|
|
|
776
776
|
return config;
|
|
777
777
|
}
|
|
778
778
|
async function initConfig() {
|
|
779
|
-
return
|
|
779
|
+
return runConfigWizard();
|
|
780
780
|
}
|
|
781
781
|
async function configureTrading(existingConfig) {
|
|
782
782
|
logger.section("交易配置");
|
|
@@ -917,7 +917,7 @@ async function ensureConfig() {
|
|
|
917
917
|
const trading = configLocal.getItem("trading");
|
|
918
918
|
if (!telegram && !polymarket && !trading) {
|
|
919
919
|
logger.info("未找到配置文件,开始初始化配置...");
|
|
920
|
-
return
|
|
920
|
+
return initConfig();
|
|
921
921
|
}
|
|
922
922
|
const config = {
|
|
923
923
|
telegram: telegram || { botToken: "" },
|
|
@@ -929,7 +929,7 @@ async function ensureConfig() {
|
|
|
929
929
|
logger.warning("配置文件不完整,缺少以下字段:");
|
|
930
930
|
validation.missingFields.forEach((field) => logger.item(`- ${field}`, 1));
|
|
931
931
|
logger.info("开始重新配置...");
|
|
932
|
-
return
|
|
932
|
+
return initConfig();
|
|
933
933
|
}
|
|
934
934
|
if (!hasPolyMarketConfig(config)) {
|
|
935
935
|
logger.info("未检测到 PolyMarket 配置");
|
|
@@ -937,7 +937,7 @@ async function ensureConfig() {
|
|
|
937
937
|
"是否要运行完整配置向导?(y/n,默认 n,选择 n 将跳过配置): "
|
|
938
938
|
);
|
|
939
939
|
if (useFullWizard.trim().toLowerCase() === "y") {
|
|
940
|
-
return
|
|
940
|
+
return runConfigWizard(config);
|
|
941
941
|
} else {
|
|
942
942
|
logger.info("ℹ️ 已跳过 PolyMarket 配置");
|
|
943
943
|
}
|
|
@@ -9,6 +9,11 @@ const PID_DIR = USER_DATA_DIR;
|
|
|
9
9
|
const LOGS_DIR = path__default.default.join(USER_DATA_DIR, "logs");
|
|
10
10
|
const LOCALSTORAGE_DIR = path__default.default.join(USER_DATA_DIR, "localstorage");
|
|
11
11
|
const PID_FILE = path__default.default.join(USER_DATA_DIR, "polycopy.pid");
|
|
12
|
+
const INSTALL_ROOT = path__default.default.resolve(__dirname, "..");
|
|
13
|
+
const DIST_INDEX = path__default.default.join(INSTALL_ROOT, "dist", "index.js");
|
|
14
|
+
const DIST_CLI = path__default.default.join(INSTALL_ROOT, "dist", "cli.js");
|
|
15
|
+
exports.DIST_CLI = DIST_CLI;
|
|
16
|
+
exports.DIST_INDEX = DIST_INDEX;
|
|
12
17
|
exports.LOCALSTORAGE_DIR = LOCALSTORAGE_DIR;
|
|
13
18
|
exports.LOGS_DIR = LOGS_DIR;
|
|
14
19
|
exports.PID_DIR = PID_DIR;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polycopy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "polycopy test",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"cli": "node dist/cli.js",
|
|
16
16
|
"preuninstall": "node dist/cli.js stop 2>/dev/null || true",
|
|
17
17
|
"prepublishOnly": "npm run build",
|
|
18
|
-
"test": "
|
|
18
|
+
"test": "HOME=$PWD node --import tsx tests/redeemer.test.ts",
|
|
19
|
+
"test:redeemer": "HOME=$PWD node --import tsx tests/redeemer.test.ts"
|
|
19
20
|
},
|
|
20
21
|
"dependencies": {
|
|
21
22
|
"@polymarket/builder-relayer-client": "^0.0.8",
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
"@types/lodash": "^4.17.23",
|
|
34
35
|
"@types/node": "^20.0.0",
|
|
35
36
|
"prettier": "^3.7.4",
|
|
37
|
+
"tsx": "^4.19.2",
|
|
36
38
|
"typescript": "^5.9.3",
|
|
37
39
|
"vite": "^7.3.1",
|
|
38
40
|
"vite-tsconfig-paths": "^6.0.4"
|