@@ -84,16 +84,27 @@ export async function NCoreInitFramework (
8484
8585 // 初始化 FFmpeg 服务
8686 await FFmpegService . init ( pathWrapper . binaryPath , logger ) ;
87+
88+ // 提前启动 WebUI,使其在登录前可用,支持通过 WebUI 操控登录过程
89+ WebUiDataRuntime . setWorkingEnv ( NapCatCoreWorkingEnv . Framework ) ;
90+ InitWebUi ( logger , pathWrapper , logSubscription , statusHelperSubscription ) . then ( ) . catch ( e => logger . logError ( e ) ) ;
91+
8792 // 直到登录成功后,执行下一步
88- // const selfInfo = {
89- // uid: 'u_FUSS0_x06S_9Tf4na_WpUg',
90- // uin: '3684714082',
91- // nick: '',
92- // online: true
93- // }
9493 const selfInfo = await new Promise < SelfInfo > ( ( resolve ) => {
94+ const loginContext = { isLogined : false } ;
95+
9596 const loginListener = new NodeIKernelLoginListener ( ) ;
97+
98+ loginListener . onUserLoggedIn = ( userid : string ) => {
99+ const tips = `当前账号(${ userid } )已登录,无法重复登录` ;
100+ logger . logError ( tips ) ;
101+ WebUiDataRuntime . setQQLoginError ( tips ) ;
102+ } ;
103+
96104 loginListener . onQRCodeLoginSucceed = async ( loginResult ) => {
105+ loginContext . isLogined = true ;
106+ WebUiDataRuntime . setQQLoginStatus ( true ) ;
107+ WebUiDataRuntime . setQQLoginError ( '' ) ;
97108 await new Promise < void > ( resolve => {
98109 registerInitCallback ( ( ) => resolve ( ) ) ;
99110 } ) ;
@@ -104,6 +115,47 @@ export async function NCoreInitFramework (
104115 online : true ,
105116 } ) ;
106117 } ;
118+
119+ loginListener . onQRCodeGetPicture = ( { qrcodeUrl } ) => {
120+ WebUiDataRuntime . setQQLoginQrcodeURL ( qrcodeUrl ) ;
121+ logger . log ( '[NapCat] [Framework] 二维码已更新, URL:' , qrcodeUrl ) ;
122+ } ;
123+
124+ loginListener . onQRCodeSessionFailed = ( errType : number , errCode : number ) => {
125+ if ( ! loginContext . isLogined ) {
126+ logger . logError ( '[NapCat] [Framework] [Login] QRCode Session Failed, ErrType:' , errType , 'ErrCode:' , errCode ) ;
127+ if ( errType === 1 && errCode === 3 ) {
128+ WebUiDataRuntime . setQQLoginError ( '二维码已过期,请刷新' ) ;
129+ }
130+ }
131+ } ;
132+
133+ loginListener . onLoginFailed = ( ...args ) => {
134+ const errInfo = JSON . stringify ( args ) ;
135+ logger . logError ( '[NapCat] [Framework] [Login] Login Failed, ErrInfo:' , errInfo ) ;
136+ WebUiDataRuntime . setQQLoginError ( `登录失败: ${ errInfo } ` ) ;
137+ } ;
138+
139+ loginListener . onLoginConnected = ( ) => {
140+ logger . log ( '[NapCat] [Framework] 登录服务已连接' ) ;
141+ // 注册 WebUI 登录操作回调
142+ registerWebUiLoginCallbacks ( loginService , loginContext , logger ) ;
143+ // 获取历史登录列表供 WebUI 使用
144+ loginService . getLoginList ( ) . then ( ( res ) => {
145+ const list = res . LocalLoginInfoList . filter ( ( item ) => item . isQuickLogin ) ;
146+ WebUiDataRuntime . setQQQuickLoginList ( list . map ( ( item ) => item . uin . toString ( ) ) ) ;
147+ WebUiDataRuntime . setQQNewLoginList ( list ) ;
148+ } ) . catch ( e => logger . logError ( '[NapCat] [Framework] 获取登录列表失败:' , e ) ) ;
149+ } ;
150+
151+ loginListener . onQRCodeSessionUserScaned = ( ) => {
152+ logger . log ( '[NapCat] [Framework] 二维码已被扫描,等待确认...' ) ;
153+ } ;
154+
155+ loginListener . onPasswordLoginFailed = ( ...args ) => {
156+ logger . logError ( '[NapCat] [Framework] 密码登录失败:' , ...args ) ;
157+ } ;
158+
107159 loginService . addKernelLoginListener ( proxiedListenerOf ( loginListener , logger ) ) ;
108160 } ) ;
109161 // 过早进入会导致addKernelMsgListener等Listener添加失败
@@ -116,10 +168,8 @@ export async function NCoreInitFramework (
116168 }
117169 await loaderObject . core . initCore ( ) ;
118170
119- // 启动WebUi
120- WebUiDataRuntime . setWorkingEnv ( NapCatCoreWorkingEnv . Framework ) ;
171+ // 登录成功后设置数据路径
121172 WebUiDataRuntime . setQQDataPath ( loaderObject . core . dataPath ) ;
122- InitWebUi ( logger , pathWrapper , logSubscription , statusHelperSubscription ) . then ( ) . catch ( e => logger . logError ( e ) ) ;
123173
124174 // // 测试 DatabaseApi
125175 // if (dbPassphrase) {
@@ -147,6 +197,164 @@ export async function NCoreInitFramework (
147197 }
148198}
149199
200+ function registerWebUiLoginCallbacks (
201+ loginService : NodeIKernelLoginService ,
202+ loginContext : { isLogined : boolean ; } ,
203+ logger : LogWrapper
204+ ) {
205+ // 刷新二维码
206+ WebUiDataRuntime . setRefreshQRCodeCallback ( async ( ) => {
207+ loginService . getQRCodePicture ( ) ;
208+ } ) ;
209+
210+ // 快速登录
211+ WebUiDataRuntime . setQuickLoginCall ( async ( uin : string ) => {
212+ return await new Promise ( ( resolve ) => {
213+ if ( ! uin ) {
214+ return resolve ( { result : false , message : '快速登录失败' } ) ;
215+ }
216+ logger . log ( '[NapCat] [Framework] 正在快速登录' , uin ) ;
217+ loginService . quickLoginWithUin ( uin ) . then ( res => {
218+ const success = res . result === '0' && ! res . loginErrorInfo ?. errMsg ;
219+ if ( ! success ) {
220+ const errMsg = res . loginErrorInfo ?. errMsg || `快速登录失败,错误码: ${ res . result } ` ;
221+ WebUiDataRuntime . setQQLoginError ( errMsg ) ;
222+ resolve ( { result : false , message : errMsg } ) ;
223+ } else {
224+ loginContext . isLogined = true ;
225+ WebUiDataRuntime . setQQLoginStatus ( true ) ;
226+ WebUiDataRuntime . setQQLoginError ( '' ) ;
227+ resolve ( { result : true , message : '' } ) ;
228+ }
229+ } ) . catch ( ( e ) => {
230+ logger . logError ( '[NapCat] [Framework] 快速登录异常:' , e ) ;
231+ WebUiDataRuntime . setQQLoginError ( '快速登录发生错误' ) ;
232+ resolve ( { result : false , message : '快速登录发生错误' } ) ;
233+ } ) ;
234+ } ) ;
235+ } ) ;
236+
237+ // 密码登录
238+ WebUiDataRuntime . setPasswordLoginCall ( async ( uin : string , passwordMd5 : string ) => {
239+ return await new Promise ( ( resolve ) => {
240+ if ( ! uin || ! passwordMd5 ) {
241+ return resolve ( { result : false , message : '密码登录失败:参数不完整' } ) ;
242+ }
243+ logger . log ( '[NapCat] [Framework] 正在密码登录' , uin ) ;
244+ loginService . passwordLogin ( {
245+ uin,
246+ passwordMd5,
247+ step : 0 ,
248+ newDeviceLoginSig : new Uint8Array ( ) ,
249+ proofWaterSig : new Uint8Array ( ) ,
250+ proofWaterRand : new Uint8Array ( ) ,
251+ proofWaterSid : new Uint8Array ( ) ,
252+ unusualDeviceCheckSig : new Uint8Array ( ) ,
253+ } ) . then ( res => {
254+ if ( res . result === '140022008' ) {
255+ const proofWaterUrl = res . loginErrorInfo ?. proofWaterUrl || '' ;
256+ resolve ( { result : false , message : '需要验证码' , needCaptcha : true , proofWaterUrl } ) ;
257+ } else if ( res . result === '140022010' || res . result === '140022011' ) {
258+ const jumpUrl = res . loginErrorInfo ?. jumpUrl || '' ;
259+ const newDevicePullQrCodeSig = res . loginErrorInfo ?. newDevicePullQrCodeSig || '' ;
260+ resolve ( { result : false , message : '新设备需要扫码验证' , needNewDevice : true , jumpUrl, newDevicePullQrCodeSig } ) ;
261+ } else if ( res . result !== '0' ) {
262+ const errMsg = res . loginErrorInfo ?. errMsg || '密码登录失败' ;
263+ WebUiDataRuntime . setQQLoginError ( errMsg ) ;
264+ resolve ( { result : false , message : errMsg } ) ;
265+ } else {
266+ loginContext . isLogined = true ;
267+ WebUiDataRuntime . setQQLoginStatus ( true ) ;
268+ WebUiDataRuntime . setQQLoginError ( '' ) ;
269+ resolve ( { result : true , message : '' } ) ;
270+ }
271+ } ) . catch ( ( e ) => {
272+ logger . logError ( '[NapCat] [Framework] 密码登录异常:' , e ) ;
273+ WebUiDataRuntime . setQQLoginError ( '密码登录发生错误' ) ;
274+ resolve ( { result : false , message : '密码登录发生错误' } ) ;
275+ } ) ;
276+ } ) ;
277+ } ) ;
278+
279+ // 验证码登录(密码登录需要验证码时的第二步)
280+ WebUiDataRuntime . setCaptchaLoginCall ( async ( uin : string , passwordMd5 : string , ticket : string , randstr : string , sid : string ) => {
281+ return await new Promise ( ( resolve ) => {
282+ if ( ! uin || ! passwordMd5 || ! ticket ) {
283+ return resolve ( { result : false , message : '验证码登录失败:参数不完整' } ) ;
284+ }
285+ logger . log ( '[NapCat] [Framework] 正在验证码登录' , uin ) ;
286+ loginService . passwordLogin ( {
287+ uin,
288+ passwordMd5,
289+ step : 1 ,
290+ newDeviceLoginSig : new Uint8Array ( ) ,
291+ proofWaterSig : new TextEncoder ( ) . encode ( ticket ) ,
292+ proofWaterRand : new TextEncoder ( ) . encode ( randstr ) ,
293+ proofWaterSid : new TextEncoder ( ) . encode ( sid ) ,
294+ unusualDeviceCheckSig : new Uint8Array ( ) ,
295+ } ) . then ( res => {
296+ if ( res . result === '140022010' || res . result === '140022011' ) {
297+ const jumpUrl = res . loginErrorInfo ?. jumpUrl || '' ;
298+ const newDevicePullQrCodeSig = res . loginErrorInfo ?. newDevicePullQrCodeSig || '' ;
299+ resolve ( { result : false , message : '新设备需要扫码验证' , needNewDevice : true , jumpUrl, newDevicePullQrCodeSig } ) ;
300+ } else if ( res . result !== '0' ) {
301+ const errMsg = res . loginErrorInfo ?. errMsg || '验证码登录失败' ;
302+ WebUiDataRuntime . setQQLoginError ( errMsg ) ;
303+ resolve ( { result : false , message : errMsg } ) ;
304+ } else {
305+ loginContext . isLogined = true ;
306+ WebUiDataRuntime . setQQLoginStatus ( true ) ;
307+ WebUiDataRuntime . setQQLoginError ( '' ) ;
308+ resolve ( { result : true , message : '' } ) ;
309+ }
310+ } ) . catch ( ( e ) => {
311+ logger . logError ( '[NapCat] [Framework] 验证码登录异常:' , e ) ;
312+ WebUiDataRuntime . setQQLoginError ( '验证码登录发生错误' ) ;
313+ resolve ( { result : false , message : '验证码登录发生错误' } ) ;
314+ } ) ;
315+ } ) ;
316+ } ) ;
317+
318+ // 新设备验证登录(密码登录需要新设备验证时的第二步)
319+ WebUiDataRuntime . setNewDeviceLoginCall ( async ( uin : string , passwordMd5 : string , newDevicePullQrCodeSig : string ) => {
320+ return await new Promise ( ( resolve ) => {
321+ if ( ! uin || ! passwordMd5 || ! newDevicePullQrCodeSig ) {
322+ return resolve ( { result : false , message : '新设备验证登录失败:参数不完整' } ) ;
323+ }
324+ logger . log ( '[NapCat] [Framework] 正在新设备验证登录' , uin ) ;
325+ loginService . passwordLogin ( {
326+ uin,
327+ passwordMd5,
328+ step : 2 ,
329+ newDeviceLoginSig : new TextEncoder ( ) . encode ( newDevicePullQrCodeSig ) ,
330+ proofWaterSig : new Uint8Array ( ) ,
331+ proofWaterRand : new Uint8Array ( ) ,
332+ proofWaterSid : new Uint8Array ( ) ,
333+ unusualDeviceCheckSig : new Uint8Array ( ) ,
334+ } ) . then ( res => {
335+ if ( res . result === '140022011' ) {
336+ const jumpUrl = res . loginErrorInfo ?. jumpUrl || '' ;
337+ const sig = res . loginErrorInfo ?. newDevicePullQrCodeSig || '' ;
338+ resolve ( { result : false , message : '异常设备需要验证' , needNewDevice : true , jumpUrl, newDevicePullQrCodeSig : sig } ) ;
339+ } else if ( res . result !== '0' ) {
340+ const errMsg = res . loginErrorInfo ?. errMsg || '新设备验证登录失败' ;
341+ WebUiDataRuntime . setQQLoginError ( errMsg ) ;
342+ resolve ( { result : false , message : errMsg } ) ;
343+ } else {
344+ loginContext . isLogined = true ;
345+ WebUiDataRuntime . setQQLoginStatus ( true ) ;
346+ WebUiDataRuntime . setQQLoginError ( '' ) ;
347+ resolve ( { result : true , message : '' } ) ;
348+ }
349+ } ) . catch ( ( e ) => {
350+ logger . logError ( '[NapCat] [Framework] 新设备验证登录异常:' , e ) ;
351+ WebUiDataRuntime . setQQLoginError ( '新设备验证登录发生错误' ) ;
352+ resolve ( { result : false , message : '新设备验证登录发生错误' } ) ;
353+ } ) ;
354+ } ) ;
355+ } ) ;
356+ }
357+
150358export class NapCatFramework {
151359 public core : NapCatCore ;
152360 context : InstanceContext ;
0 commit comments