@wenbin_wb/dsh-bridge 2.8.7 → 2.9.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/CHANGELOG.md +295 -250
- package/README.en.md +408 -572
- package/README.md +430 -570
- package/client/client.js +3844 -3764
- package/client/index.js +140 -876
- package/client/mobile-styles.js +802 -0
- package/docs/fix-plan-202608.md +108 -0
- package/lib/auth/login-template.js +381 -381
- package/lib/auth/manager.js +531 -469
- package/lib/bridge-rpc.js +436 -511
- package/lib/cloudflared-manager.mjs +361 -345
- package/lib/compat.js +129 -0
- package/lib/feishu/index.js +225 -222
- package/lib/feishu/node.js +433 -409
- package/lib/index.js +1765 -1804
- package/lib/platform/base.js +147 -156
- package/lib/platform/commands.js +221 -0
- package/lib/platform/conversation-bridge.js +816 -1570
- package/lib/platform/dsh-storage.js +117 -0
- package/lib/platform/index.js +10 -10
- package/lib/platform/message-split.js +191 -0
- package/lib/platform/session-catalog.js +372 -0
- package/lib/platform/stream-slices.js +21 -0
- package/lib/qq/index.js +312 -309
- package/lib/qq/node.js +532 -533
- package/lib/telegram/index.js +215 -212
- package/lib/telegram/node.js +348 -350
- package/lib/tunnel-client.mjs +39 -15
- package/lib/wechat/gateway.js +973 -960
- package/lib/wechat/index.js +244 -241
- package/lib/wechat/media.js +285 -281
- package/lib/wechat/node.js +352 -350
- package/package.json +106 -102
package/lib/telegram/index.js
CHANGED
|
@@ -1,213 +1,216 @@
|
|
|
1
|
-
// dsh-bridge Telegram platform adapter
|
|
2
|
-
// 编排 TelegramGateway(长轮询 + 代理支持)+ TelegramConversationNode(Telegram⇄DSH 会话桥)。
|
|
3
|
-
// 作为 Platform 子类,注册进 PlatformManager 统一管理。
|
|
4
|
-
|
|
5
|
-
import QRCode from 'qrcode'
|
|
6
|
-
import { Platform } from '../platform/base.js'
|
|
7
|
-
import { TelegramGateway } from './gateway.js'
|
|
8
|
-
import { TelegramConversationNode } from './node.js'
|
|
9
|
-
|
|
10
|
-
export class TelegramService extends Platform {
|
|
11
|
-
/**
|
|
12
|
-
* @param {object} opts
|
|
13
|
-
* @param {object} opts.ctx Cordis 上下文
|
|
14
|
-
* @param {object} opts.logger 日志器
|
|
15
|
-
* @param {object} [opts.config] 已持久化的 telegram 配置(凭证 + allowFrom + 间隔)
|
|
16
|
-
* @param {(patch: object) => (void|Promise<void>)} opts.onPersist 主插件保存回调
|
|
17
|
-
*/
|
|
18
|
-
constructor({ ctx, logger, config = {}, onPersist }) {
|
|
19
|
-
super({ ctx, logger, config, onPersist })
|
|
20
|
-
this.id = 'telegram'
|
|
21
|
-
this.name = 'Telegram'
|
|
22
|
-
this._botQrCache = { username: '', qr: '' }
|
|
23
|
-
|
|
24
|
-
this.gateway = new TelegramGateway(ctx, {
|
|
25
|
-
botToken: config.botToken ?? '',
|
|
26
|
-
proxy: config.proxy ?? '',
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
// 挂到 ctx 供会话节点读取
|
|
30
|
-
try { ctx.telegram = this.gateway } catch {}
|
|
31
|
-
|
|
32
|
-
this.node = new TelegramConversationNode(ctx, {
|
|
33
|
-
allowFrom: Array.isArray(config.allowFrom) ? config.allowFrom : [],
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
get
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
this.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
*
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (creds.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
this.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
this.
|
|
121
|
-
this.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
this.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (
|
|
178
|
-
if (
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
1
|
+
// dsh-bridge Telegram platform adapter
|
|
2
|
+
// 编排 TelegramGateway(长轮询 + 代理支持)+ TelegramConversationNode(Telegram⇄DSH 会话桥)。
|
|
3
|
+
// 作为 Platform 子类,注册进 PlatformManager 统一管理。
|
|
4
|
+
|
|
5
|
+
import QRCode from 'qrcode'
|
|
6
|
+
import { Platform } from '../platform/base.js'
|
|
7
|
+
import { TelegramGateway } from './gateway.js'
|
|
8
|
+
import { TelegramConversationNode } from './node.js'
|
|
9
|
+
|
|
10
|
+
export class TelegramService extends Platform {
|
|
11
|
+
/**
|
|
12
|
+
* @param {object} opts
|
|
13
|
+
* @param {object} opts.ctx Cordis 上下文
|
|
14
|
+
* @param {object} opts.logger 日志器
|
|
15
|
+
* @param {object} [opts.config] 已持久化的 telegram 配置(凭证 + allowFrom + 间隔)
|
|
16
|
+
* @param {(patch: object) => (void|Promise<void>)} opts.onPersist 主插件保存回调
|
|
17
|
+
*/
|
|
18
|
+
constructor({ ctx, logger, config = {}, onPersist }) {
|
|
19
|
+
super({ ctx, logger, config, onPersist })
|
|
20
|
+
this.id = 'telegram'
|
|
21
|
+
this.name = 'Telegram'
|
|
22
|
+
this._botQrCache = { username: '', qr: '' }
|
|
23
|
+
|
|
24
|
+
this.gateway = new TelegramGateway(ctx, {
|
|
25
|
+
botToken: config.botToken ?? '',
|
|
26
|
+
proxy: config.proxy ?? '',
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
// 挂到 ctx 供会话节点读取
|
|
30
|
+
try { ctx.telegram = this.gateway } catch {}
|
|
31
|
+
|
|
32
|
+
this.node = new TelegramConversationNode(ctx, {
|
|
33
|
+
allowFrom: Array.isArray(config.allowFrom) ? config.allowFrom : [],
|
|
34
|
+
groupAutoApprove: config.groupAutoApprove,
|
|
35
|
+
digestIntervalSec: config.digestIntervalSec,
|
|
36
|
+
approvalTimeoutSec: config.approvalTimeoutSec,
|
|
37
|
+
maxMessageChars: config.maxMessageChars || 4096,
|
|
38
|
+
sendChunkDelayMs: config.sendChunkDelayMs,
|
|
39
|
+
activeSessionId: config.activeSessionId,
|
|
40
|
+
}, logger, {
|
|
41
|
+
onFirstSender: () => this.persist({ allowFrom: [...(this.node?.config?.allowFrom ?? [])] }),
|
|
42
|
+
onActiveSessionChange: (sessionId) => this.persist({ activeSessionId: sessionId }),
|
|
43
|
+
})
|
|
44
|
+
this.bridge = this.node
|
|
45
|
+
|
|
46
|
+
if (this.gateway.configured) {
|
|
47
|
+
void this.start().catch((err) => {
|
|
48
|
+
this.logger.error?.('[dsh-bridge telegram] start failed:', err?.message ?? err)
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---- Platform 接口 ----
|
|
54
|
+
|
|
55
|
+
get configured() { return this.gateway.configured }
|
|
56
|
+
get accountId() { return this.gateway.accountId }
|
|
57
|
+
|
|
58
|
+
get capabilities() {
|
|
59
|
+
return {
|
|
60
|
+
group: true,
|
|
61
|
+
media: true,
|
|
62
|
+
approvals: true,
|
|
63
|
+
maxMessageChars: this.node.config.maxMessageChars || 4096,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async sendText(peerId, text, opts = {}) {
|
|
68
|
+
return this.gateway.sendText(peerId, text, opts)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async sendTyping(peerId, opts = {}) {
|
|
72
|
+
return this.gateway.sendTyping?.(peerId, opts)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ---- 生命周期控制 ----
|
|
76
|
+
|
|
77
|
+
async start() {
|
|
78
|
+
if (!this.gateway.configured) {
|
|
79
|
+
this.setStatus('idle')
|
|
80
|
+
return { success: false, error: 'Telegram Bot Token 未配置' }
|
|
81
|
+
}
|
|
82
|
+
this.setStatus('starting')
|
|
83
|
+
const ok = await this.gateway.start()
|
|
84
|
+
if (ok) {
|
|
85
|
+
this.setStatus('connected')
|
|
86
|
+
return { success: true }
|
|
87
|
+
} else {
|
|
88
|
+
this.setStatus('error', '连接 Telegram API 失败,请检查 Bot Token 或网络代理')
|
|
89
|
+
return { success: false, error: '连接失败' }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async stop() {
|
|
94
|
+
await this.gateway.stop()
|
|
95
|
+
this.setStatus('offline')
|
|
96
|
+
return { success: true }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 配置或登录
|
|
101
|
+
* @param {object} creds - { botToken, proxy }
|
|
102
|
+
*/
|
|
103
|
+
async login(creds = {}) {
|
|
104
|
+
const patch = {}
|
|
105
|
+
if (creds.botToken !== undefined) patch.botToken = String(creds.botToken).trim()
|
|
106
|
+
if (creds.proxy !== undefined) patch.proxy = String(creds.proxy).trim()
|
|
107
|
+
|
|
108
|
+
this.gateway.setCredentials(patch)
|
|
109
|
+
this.persist(patch)
|
|
110
|
+
|
|
111
|
+
if (!this.gateway.configured) {
|
|
112
|
+
await this.stop()
|
|
113
|
+
return { success: false, error: '请填写完整的 Telegram Bot Token' }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return this.start()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async unbind() {
|
|
120
|
+
await this.stop()
|
|
121
|
+
this.gateway.setCredentials({ botToken: '', proxy: '' })
|
|
122
|
+
this.persist({ botToken: '', proxy: '', allowFrom: [] })
|
|
123
|
+
if (this.node) this.node.config.allowFrom = []
|
|
124
|
+
return { success: true }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
getStatus() {
|
|
128
|
+
const allowFrom = [...(this.node?.config?.allowFrom ?? [])]
|
|
129
|
+
const username = this.gateway.botInfo?.username || ''
|
|
130
|
+
const botLink = username ? `https://t.me/${encodeURIComponent(username)}` : null
|
|
131
|
+
if (botLink && this._botQrCache.username !== username) {
|
|
132
|
+
this._botQrCache.username = username
|
|
133
|
+
void QRCode.toDataURL(botLink, {
|
|
134
|
+
width: 260,
|
|
135
|
+
margin: 2,
|
|
136
|
+
color: { dark: '#1F2421', light: '#FFFFFF' },
|
|
137
|
+
}).then((qr) => {
|
|
138
|
+
this._botQrCache.qr = qr
|
|
139
|
+
}).catch(() => {})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
id: this.id,
|
|
144
|
+
name: this.name,
|
|
145
|
+
status: this.status === 'connected' ? 'connected' : this.gateway.status,
|
|
146
|
+
configured: this.gateway.configured,
|
|
147
|
+
accountId: this.gateway.accountId || (this.gateway.configured ? '已配置 Token' : ''),
|
|
148
|
+
allowFrom,
|
|
149
|
+
peerId: this.node?.peerId,
|
|
150
|
+
sessionId: this.node?.activeSessionId,
|
|
151
|
+
login: {
|
|
152
|
+
phase: this.status === 'connected' ? 'done' : this.status === 'error' ? 'error' : 'idle',
|
|
153
|
+
},
|
|
154
|
+
capabilities: { ...this.capabilities },
|
|
155
|
+
config: {
|
|
156
|
+
digestIntervalSec: this.node?.config?.digestIntervalSec,
|
|
157
|
+
approvalTimeoutSec: this.node?.config?.approvalTimeoutSec,
|
|
158
|
+
maxMessageChars: this.node?.config?.maxMessageChars,
|
|
159
|
+
sendChunkDelayMs: this.node?.config?.sendChunkDelayMs,
|
|
160
|
+
botToken: this.gateway.config.botToken ? '******' : '',
|
|
161
|
+
proxy: this.gateway.config.proxy || '',
|
|
162
|
+
},
|
|
163
|
+
botInfo: this.gateway.botInfo,
|
|
164
|
+
botLink,
|
|
165
|
+
botQr: this._botQrCache.qr,
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
setAllowFrom(allowFrom) {
|
|
170
|
+
const list = Array.isArray(allowFrom) ? allowFrom.map((s) => String(s).trim()).filter(Boolean) : []
|
|
171
|
+
if (this.node) this.node.config.allowFrom = list
|
|
172
|
+
this.persist({ allowFrom: list })
|
|
173
|
+
return { success: true, allowFrom: list }
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async setConfig({ digestIntervalSec, approvalTimeoutSec, maxMessageChars, sendChunkDelayMs, groupAutoApprove, botToken, proxy } = {}) {
|
|
177
|
+
if (digestIntervalSec != null) this.node.config.digestIntervalSec = Number(digestIntervalSec)
|
|
178
|
+
if (approvalTimeoutSec != null) this.node.config.approvalTimeoutSec = Number(approvalTimeoutSec)
|
|
179
|
+
if (maxMessageChars != null) {
|
|
180
|
+
const val = Number(maxMessageChars)
|
|
181
|
+
this.node.config.maxMessageChars = (val >= 200) ? val : 4096
|
|
182
|
+
}
|
|
183
|
+
if (sendChunkDelayMs != null) this.node.config.sendChunkDelayMs = Number(sendChunkDelayMs)
|
|
184
|
+
if (groupAutoApprove != null) this.node.config.groupAutoApprove = groupAutoApprove === true
|
|
185
|
+
|
|
186
|
+
if (botToken !== undefined || proxy !== undefined) {
|
|
187
|
+
const patch = {}
|
|
188
|
+
if (botToken !== undefined) patch.botToken = botToken.trim()
|
|
189
|
+
if (proxy !== undefined) patch.proxy = proxy.trim()
|
|
190
|
+
this.gateway.setCredentials(patch)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const patch = {
|
|
194
|
+
digestIntervalSec: this.node.config.digestIntervalSec,
|
|
195
|
+
approvalTimeoutSec: this.node.config.approvalTimeoutSec,
|
|
196
|
+
maxMessageChars: this.node.config.maxMessageChars,
|
|
197
|
+
sendChunkDelayMs: this.node.config.sendChunkDelayMs,
|
|
198
|
+
groupAutoApprove: this.node.config.groupAutoApprove === true,
|
|
199
|
+
}
|
|
200
|
+
if (botToken !== undefined) patch.botToken = this.gateway.config.botToken
|
|
201
|
+
if (proxy !== undefined) patch.proxy = this.gateway.config.proxy
|
|
202
|
+
await this.persist(patch)
|
|
203
|
+
|
|
204
|
+
if (this.gateway.configured && (botToken !== undefined || proxy !== undefined)) {
|
|
205
|
+
await this.start().catch((err) => {
|
|
206
|
+
this.logger?.warn?.('[dsh-bridge telegram] auto-start failed: %s', err?.message ?? err)
|
|
207
|
+
})
|
|
208
|
+
}
|
|
209
|
+
return { success: true }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
dispose() {
|
|
213
|
+
super.dispose()
|
|
214
|
+
this.gateway?.dispose?.()
|
|
215
|
+
}
|
|
213
216
|
}
|