/// <summary> /// 异步登出客户端 /// </summary> public void LogoutAsync() { Task.Factory.StartNew(() => { if (baseRequest != null) { try { string result = Logout(); asyncOperation.Post( new SendOrPostCallback((obj) => { LogoutComplete?.Invoke(this, new TEventArgs <User>((User)obj)); }), user); } catch (Exception e) { asyncOperation.Post( new SendOrPostCallback((obj) => { ExceptionCatched?.Invoke(this, new TEventArgs <Exception>((Exception)obj)); }), e); throw e; } } }); }
/// <summary> /// 获取头像,因为请求的时候需要带Cookie等相关参数,所以直接用新的http请求不行,务必使用客户端API来获取 /// </summary> /// <param name="url">头像地址,例如/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=filehelper&skey=@crypt_372b266_540d016177e861740ee84fec697a3b01 </param> /// <param name="action">委托Action</param> /// <returns></returns> public void GetIconAsync(string url, Action <byte[]> action) { string fullUrl = host + url; new Task(() => { try { var img = httpClient.GetImage(fullUrl); asyncOperation.Post( new SendOrPostCallback((obj) => { action((byte[])obj); }), img); } catch (Exception e) { asyncOperation.Post( new SendOrPostCallback((obj) => { ExceptionCatched?.Invoke(this, new TEventArgs <Exception>((Exception)obj)); }), e); } }).Start(); }
/// <summary> /// 获取登陆二维码 /// </summary> private void GetLoginQrCode() { try { string jsloginUrl = $"https://login.wx2.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx2.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_={Utils.GetJavaTimeStamp()}"; string result = httpClient.GetString(jsloginUrl); Utils.Debug("GetLoginQrCode " + result); string qruuidStr = "window.QRLogin.uuid = \""; int index = result.IndexOf("window.QRLogin.uuid = \""); if (index == -1) { throw new Exception("获取登陆二维码失败,请稍后再试。"); } else { uuid = result.Substring(index + qruuidStr.Length, result.Length - index - qruuidStr.Length - "\";".Length); } string qrcodeUrl = string.Format("https://login.weixin.qq.com/qrcode/{0}", uuid); var img = httpClient.GetImage(qrcodeUrl); asyncOperation.Post( new SendOrPostCallback((obj) => { GetLoginQrCodeComplete?.Invoke(this, new TEventArgs <byte[]>((byte[])obj)); }), img); } catch (Exception e) { asyncOperation.Post( new SendOrPostCallback((obj) => { ExceptionCatched?.Invoke(this, new TEventArgs <Exception>((Exception)obj)); }), e); throw e; } }
internal static void GetUIElementInToWorkAreaWithTabTipOpened(UIElement element) { try { FrameworkElement rootVisualForAnimation = GetRootVisualForAnimation(element); Rectangle workAreaWithTabTipOpened = GetWorkAreaWithTabTipOpened(element); Rectangle uiElementRectangle; Window window = rootVisualForAnimation as Window; if (window != null && workAreaWithTabTipOpened.Height >= window.Height) { uiElementRectangle = GetWindowRectangle(window); } else { uiElementRectangle = GetUIElementRect(element); } MoveRootVisualBy( rootVisual: rootVisualForAnimation, moveBy: GetYOffsetToMoveUIElementInToWorkArea( uiElementRectangle: uiElementRectangle, workAreaRectangle: workAreaWithTabTipOpened)); } catch (Exception ex) { ExceptionCatched?.Invoke(ex); } }
/// <summary> /// 主要用于提醒手机端,同步状态 /// </summary> private void StatusNotify() { try { //反馈服务器 string webwxstatusnotifyUrl = host + "/cgi-bin/mmwebwx-bin/webwxstatusnotify"; StatusNotifyRequest statusNotifyRequest = new StatusNotifyRequest(); statusNotifyRequest.BaseRequest = baseRequest; statusNotifyRequest.Code = 3; statusNotifyRequest.FromUserName = user.UserName; statusNotifyRequest.ToUserName = user.UserName; statusNotifyRequest.ClientMsgId = Utils.GetJavaTimeStamp(); //反馈结果可以不理 httpClient.PostJson <StatusNotifyResponse>(webwxstatusnotifyUrl, statusNotifyRequest); } catch (Exception e) { asyncOperation.Post( new SendOrPostCallback((obj) => { ExceptionCatched?.Invoke(this, new TEventArgs <Exception>((Exception)obj)); }), e); throw e; } }
public async Task StartAsync() { if (IsRunning) { return; } IsRunning = true; Source = new CancellationTokenSource(); try { await DiscordConnection.RunAsync(Source.Token, DiscordSocketConfig); } catch (OperationCanceledException ocex) { if (!Source.IsCancellationRequested || ocex.CancellationToken != Source.Token) { ExceptionCatched?.Invoke(this, ocex); } } catch (Exception ex) { ExceptionCatched?.Invoke(this, ex); } finally { IsRunning = false; } }
internal static void GetEverythingInToWorkAreaWithTabTipClosed() { try { foreach (KeyValuePair <FrameworkElement, Storyboard> moveRootVisualStoryboard in MoveRootVisualStoryboards) { Window window = moveRootVisualStoryboard.Key as Window; // if window exist also check if it has not been closed if (window != null && new WindowInteropHelper(window).Handle != IntPtr.Zero) { MoveRootVisualBy( rootVisual: window, moveBy: GetYOffsetToMoveUIElementInToWorkArea( uiElementRectangle: GetWindowRectangle(window), workAreaRectangle: GetWorkAreaWithTabTipClosed(window))); } else { MoveRootVisualTo(rootVisual: moveRootVisualStoryboard.Key, moveTo: 0); } } } catch (Exception ex) { ExceptionCatched?.Invoke(ex); } }
/// <summary> /// 读取用户的联系人列表,其中只包含公众号,个人号,如果返回值seq不为0,那么用户列表还没获取完(因为可能会有几千人的号,不可能一次获取完),则附带上seq的值继续获取。 /// </summary> private void GetContact() { try { //获取联系人列表 finishGetContactList = false; string getContactUrl = string.Format(host + "/cgi-bin/mmwebwx-bin/webwxgetcontact?r={0}&seq={1}&skey={2}", Utils.GetJavaTimeStamp(), 0, baseRequest.Skey); while (!finishGetContactList) { string contactResult = httpClient.GetStringOnce(getContactUrl); GetContactResponse getContactResponse = JsonConvert.DeserializeObject<GetContactResponse>(contactResult); asyncOperation.Post( new SendOrPostCallback((list) => { GetContactComplete?.Invoke(this, new TEventArgs<List<Contact>>((List<Contact>)list)); }), getContactResponse.MemberList); if (getContactResponse.Seq == 0) { finishGetContactList = true; } else { getContactUrl = string.Format(host + "/cgi-bin/mmwebwx-bin/webwxgetcontact?r={0}&seq={1}&skey={2}", Utils.GetJavaTimeStamp(), getContactResponse.Seq, baseRequest.Skey); } } //获取完联系人中的公众号,才能获得名称,这个时候再发送图文消息事件。 asyncOperation.Post( new SendOrPostCallback((obj) => { MPSubscribeMsgListComplete?.Invoke(this, new TEventArgs<List<MPSubscribeMsg>>((List<MPSubscribeMsg>)obj)); }), mpSubscribeMsgList); } catch (Exception e) { if (e is WebException) { WebException we = e as WebException; if (we.Status == WebExceptionStatus.ProtocolError && ((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.ServiceUnavailable) { //过千人账号有时候获取不到联系人列表,服务器返回503,官方测试结果也是反馈503导致获取不到,为了不影响正常使用,跳过获取联系人步骤 asyncOperation.Post( new SendOrPostCallback((obj) => { ExceptionCatched?.Invoke(this, new TEventArgs<Exception>((Exception)obj)); }), new GetContactException("无法获取好友列表")); } } else { asyncOperation.Post( new SendOrPostCallback((obj) => { ExceptionCatched?.Invoke(this, new TEventArgs<Exception>((Exception)obj)); }), e); } } }
/// <summary> /// 获取联系人信息,例如初始化、群聊里面。 /// </summary> /// <param name="statusNotifyUserName">需要获取的UserName列表,包括群,个人用户,用英文,分割</param> /// <param name="EncryChatRoomId">默认为空,如果是获取群内成员详细信息,则填写encryChatRoomId,也就是群的UserName</param> public void GetBatchGetContactAsync(string statusNotifyUserName, string encryChatRoomId = "") { Task.Factory.StartNew(() => { try { //获取历史会话列表 string webwxbatchgetcontactUrl = string.Format(host + "/cgi-bin/mmwebwx-bin/webwxbatchgetcontact?type=ex&r={0}", Utils.GetJavaTimeStamp()); string[] chatNameArr = statusNotifyUserName.Split(','); bool finishGetChatList = false; BatchGetContactRequest batchGetContactRequest = new BatchGetContactRequest(); batchGetContactRequest.BaseRequest = baseRequest; int count = chatNameArr.Length; int index = 0; //一批次最多获取50条,多出来分批获取 while (!finishGetChatList) { batchGetContactRequest.List = new List <ChatRoom>(); if (((index + 1) * 50) < count) { for (int i = index * 50; i < (index + 1) * 50; i++) { batchGetContactRequest.List.Add(new ChatRoom { UserName = chatNameArr[i], EncryChatRoomId = encryChatRoomId }); } } else { for (int i = index * 50; i < count; i++) { batchGetContactRequest.List.Add(new ChatRoom { UserName = chatNameArr[i], EncryChatRoomId = encryChatRoomId }); } finishGetChatList = true; } BatchGetContactResponse batchGetContactMsg = httpClient.PostJson <BatchGetContactResponse>(webwxbatchgetcontactUrl, batchGetContactRequest); asyncOperation.Post( new SendOrPostCallback((list) => { BatchGetContactComplete?.Invoke(this, new TEventArgs <List <Contact> >((List <Contact>)list)); }), batchGetContactMsg.ContactList); index++; } } catch (Exception e) { asyncOperation.Post( new SendOrPostCallback((obj) => { ExceptionCatched?.Invoke(this, new TEventArgs <Exception>((Exception)obj)); }), e); } }); }
static TabTipAutomation() { if (EnvironmentEx.GetOSVersion() == OSVersion.Win7) { return; } TabTip.Closed += () => TabTipClosedSubject.OnNext(true); AutomateTabTipOpen(FocusSubject.AsObservable()); AutomateTabTipClose(FocusSubject.AsObservable(), TabTipClosedSubject); AnimationHelper.ExceptionCatched += exception => ExceptionCatched?.Invoke(exception); }
internal static void KillTapTibProcess() { try { foreach (Process tabTipProcess in Process.GetProcessesByName(TabTipProcessName)) { tabTipProcess.Kill(); } } catch (Exception ex) { ExceptionCatched?.Invoke(ex); } }
private static bool TabTipClosed() { try { const int GWL_STYLE = -16; // Specifies we wish to retrieve window styles. const uint KeyboardClosedStyle = 2617245696; IntPtr KeyboardWnd = GetTabTipWindowHandle(); return(KeyboardWnd.ToInt32() == 0 || GetWindowLong(KeyboardWnd, GWL_STYLE) == KeyboardClosedStyle); } catch (Exception ex) { ExceptionCatched?.Invoke(ex); return(false); } }
/// <summary> /// Close TabTip /// </summary> public static void Close() { System.Diagnostics.Debug.WriteLine("TabTip.Close"); const int WM_SYSCOMMAND = 274; const int SC_CLOSE = 61536; try { SendMessage(GetTabTipWindowHandle().ToInt32(), WM_SYSCOMMAND, SC_CLOSE, 0); } catch (Exception ex) { ExceptionCatched?.Invoke(ex); } }
private static void EnableTabTipOpenInDesctopModeOnWin10() { try { const string TabTipAutoInvokeKey = "EnableDesktopModeAutoInvoke"; int EnableDesktopModeAutoInvoke = (int)(Registry.GetValue(TabTipRegistryKeyName, TabTipAutoInvokeKey, -1) ?? -1); if (EnableDesktopModeAutoInvoke != 1) { Registry.SetValue(TabTipRegistryKeyName, TabTipAutoInvokeKey, 1); } } catch (Exception ex) { ExceptionCatched?.Invoke(ex); } }
/// <summary> /// 异步发送文字消息 /// </summary> /// <param name="msg">消息</param> /// <param name="toUserName">发送人UserName</param> public void SendMsgAsync(string msg, string toUserName) { Task.Factory.StartNew(() => { try { SendMsg(msg, toUserName); } catch (Exception e) { asyncOperation.Post( new SendOrPostCallback((obj) => { ExceptionCatched?.Invoke(this, new TEventArgs <Exception>((Exception)obj)); }), e); } }); }
/// <summary> /// 开始初始化所有关键内容 /// </summary> private void Init() { try { string webwxinitUrl = string.Format(host + "/cgi-bin/mmwebwx-bin/webwxinit?r={0}pass_ticket={1}", Utils.Get_r(), passTicket); JObject postjson = JObject.FromObject(new { BaseRequest = baseRequest }); InitResponse initMsg = httpClient.PostJson <InitResponse>(webwxinitUrl, postjson); if (initMsg.BaseResponse.Ret != 0) { throw new Exception("程序初始化失败"); } //初始化2次,官网也是初始化2次,这样貌似比较稳定 httpClient.PostJson <InitResponse>(webwxinitUrl, postjson); user = initMsg.User; mpSubscribeMsgList = initMsg.MPSubscribeMsgList; syncKey = initMsg.SyncKey; //初始化的时候会返回一个最近联系人列表,但是主要还是以第一次sync获得的最近联系人为准。 asyncOperation.Post( new SendOrPostCallback((list) => { BatchGetContactComplete?.Invoke(this, new TEventArgs <List <Contact> >((List <Contact>)list)); }), initMsg.ContactList); asyncOperation.Post( new SendOrPostCallback((obj) => { LoginComplete?.Invoke(this, new TEventArgs <User>((User)obj)); }), user); } catch (Exception ex) { FileLog.Exception("Init", ex); asyncOperation.Post( new SendOrPostCallback((obj) => { ExceptionCatched?.Invoke(this, new TEventArgs <Exception>((Exception)obj)); }), ex); //throw ex; } }
/// <summary> /// Open TabTip /// </summary> public static void Open() { if (EnvironmentEx.GetOSVersion() == OSVersion.Win10) { EnableTabTipOpenInDesctopModeOnWin10(); } try { Process.Start(new ProcessStartInfo(TabTipExecPath) { UseShellExecute = true }); } catch (Exception ex) { ExceptionCatched?.Invoke(ex); } }
/// <summary> /// Open TabTip in undocked state /// </summary> public static void OpenUndocked() { const string TabTipDockedKey = "EdgeTargetDockedState"; try { int docked = (int)(Registry.GetValue(TabTipRegistryKeyName, TabTipDockedKey, 1) ?? 1); if (docked == 1) { Registry.SetValue(TabTipRegistryKeyName, TabTipDockedKey, 0); KillTapTibProcess(); } } catch (Exception ex) { ExceptionCatched?.Invoke(ex); } Open(); }
/// <summary> /// 检测手机是否扫码 /// </summary> private void CheckSacnLogin() { try { byte[] userAvatar = null; ScanState scanState = ScanState.UnKnown; while (syncPolling && (scanState != ScanState.Login)) { string timespan = Utils.GetTimeStamp(); string loginUrl = string.Format("https://login.wx2.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid={0}&tip=0&r={1}&_={2}", uuid, Utils.Get_r(), Utils.GetTimeStamp()); //采用长轮询的方式,25秒返内回一次检测数据。 string checkResult = httpClient.GetString(loginUrl); Utils.Debug("CheckSacnLogin " + checkResult); if (checkResult.IndexOf("window.code=408;") != -1) { scanState = ScanState.Timeout; } else if (checkResult.IndexOf("window.code=201;") != -1) { scanState = ScanState.Scan; //有些号没有头像就跳过这个步骤 if (checkResult.IndexOf("window.userAvatar") != -1) { //扫码返回的头像是base64格式,需要转化 string subStr = "window.code=201;window.userAvatar = 'data:img/jpg;base64,"; string base64UserAvatar = checkResult.Substring(subStr.Length, checkResult.Length - subStr.Length - 2); byte[] arr = Convert.FromBase64String(base64UserAvatar); userAvatar = arr; asyncOperation.Post( new SendOrPostCallback((obj) => { CheckScanComplete?.Invoke(this, new TEventArgs <byte[]>((byte[])obj)); }), userAvatar); } } else if (checkResult.IndexOf("window.code=200;") != -1) { scanState = ScanState.Login; string subStr = "window.code=200;\nwindow.redirect_uri=\""; cookieRedirectUri = checkResult.Substring(subStr.Length, checkResult.Length - subStr.Length - 2); //跳转登录页获取cookie,并且获取关键参数,根据跳转地址,获相应提交地址 string cookieRedirectResult = httpClient.LoginString(cookieRedirectUri); if (cookieRedirectUri.StartsWith("https://wx2.qq.com")) { host = "https://wx2.qq.com"; pushHost = "https://webpush.wx2.qq.com"; uploadHost = "https://file.wx2.qq.com"; } else if (cookieRedirectUri.StartsWith("https://wx8.qq.com")) { host = "https://wx8.qq.com"; pushHost = "https://webpush.wx8.qq.com"; uploadHost = "https://file.wx8.qq.com"; } else if (cookieRedirectUri.StartsWith("https://web2.wechat.com")) { host = "https://web2.wechat.com"; pushHost = "https://webpush.web2.wechat.com"; uploadHost = "https://file.web2.wechat.com"; } else if (cookieRedirectUri.StartsWith("https://web.wechat.com")) { host = "https://web.wechat.com"; pushHost = "https://webpush.web.wechat.com"; uploadHost = "https://file.web.wechat.com"; } else { host = "https://wx.qq.com"; pushHost = "https://webpush.wx.qq.com"; uploadHost = "https://file.wx.qq.com"; } httpClient.Referer = host; XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(cookieRedirectResult); //如果返回异常,则可能被暂封,无法登陆网页版 if (xmlDoc["error"]["ret"].InnerText != "0") { throw new Exception(xmlDoc["error"]["message"].InnerText); } else { baseRequest.Sid = xmlDoc["error"]["wxsid"].InnerText; baseRequest.Uin = Convert.ToInt64(xmlDoc["error"]["wxuin"].InnerText); baseRequest.Skey = xmlDoc["error"]["skey"].InnerText; passTicket = xmlDoc["error"]["pass_ticket"].InnerText; } } else if (checkResult.IndexOf("window.code=400;") != -1) { scanState = ScanState.Expires; GetLoginQrCode(); } else { scanState = ScanState.UnKnown; } Thread.Sleep(1000); } } catch (Exception ex) { FileLog.Exception("CheckSacnLogin", ex); asyncOperation.Post( new SendOrPostCallback((obj) => { ExceptionCatched?.Invoke(this, new TEventArgs <Exception>((Exception)obj)); }), ex); throw ex; } }
/// <summary> /// 开始轮询检测是否有新消息 /// </summary> private void Sync() { while (syncPolling) { try { string syncCheckUrl = string.Format(pushHost + "/cgi-bin/mmwebwx-bin/synccheck?r={0}&skey={1}&sid={2}&uin={3}&deviceid={4}&synckey={5}&_={6}", Utils.GetJavaTimeStamp(), baseRequest.Skey, baseRequest.Sid, baseRequest.Uin, baseRequest.DeviceID, syncKey.ToString(), syncKey.Step); string syncCheckResult = httpClient.GetString(syncCheckUrl); if (!syncPolling) { return; } MatchCollection matchCollection = Regex.Matches(syncCheckResult, @"\d+"); string retcode = matchCollection[0].Value; string selector = matchCollection[1].Value; Utils.Debug("retcode:" + retcode + " selector:" + selector); switch (retcode) { case "0": if (selector != "0") { //有新消息,拉取信息。 SyncRequest syncRequest = new SyncRequest(); syncRequest.BaseRequest = baseRequest; syncRequest.SyncKey = syncKey; syncRequest.rr = Utils.Get_r(); string syncUrl = string.Format(host + "/cgi-bin/mmwebwx-bin/webwxsync?sid={0}&skey={1}&pass_ticket={2}", baseRequest.Sid, baseRequest.Skey, passTicket); SyncResponse syncResponse = httpClient.PostJson <SyncResponse>(syncUrl, syncRequest); if (!syncPolling) { return; } else { syncKey = syncResponse.SyncKey; //只要不是0,就是有消息,有消息我们处理就行了,不管selector是几 if (syncResponse.AddMsgCount == 0 && syncResponse.DelContactCount == 0 && syncResponse.ModContactCount == 0 && syncResponse.ModChatRoomMemberCount == 0) { //会有这么一种情况,selector=2,但是没有任何消息体,这样会导致持续快速的空交互 //除非下次有新消息,或者主动点击手机触发消息 //为了防止这种情况,做个5秒停顿。 Thread.Sleep(5000); } else { if (syncResponse.AddMsgList.Count > 0) { asyncOperation.Post( new SendOrPostCallback((obj) => { ReceiveMsg?.Invoke(this, new TEventArgs <List <AddMsg> >((List <AddMsg>)obj)); }), syncResponse.AddMsgList); } if (syncResponse.ModContactCount > 0) { asyncOperation.Post( new SendOrPostCallback((obj) => { ModContactListComplete?.Invoke(this, new TEventArgs <List <ModContactItem> >((List <ModContactItem>)obj)); }), syncResponse.ModContactList); } if (syncResponse.DelContactCount > 0) { asyncOperation.Post( new SendOrPostCallback((obj) => { DelContactListComplete?.Invoke(this, new TEventArgs <List <DelContactItem> >((List <DelContactItem>)obj)); }), syncResponse.DelContactList); } if (syncResponse.ModChatRoomMemberCount > 0) { //待分析,这个消息基本没有 } } } } break; case "1100": //登出了微信,很可能是wx.qq.com和wx2.qq.com调用接口不一致导致的,注意登陆时候的跳转地址 Close(); asyncOperation.Post( new SendOrPostCallback((obj) => { LogoutComplete?.Invoke(this, new TEventArgs <User>((User)obj)); }), user); break; case "1101": Close(); asyncOperation.Post( new SendOrPostCallback((obj) => { LogoutComplete?.Invoke(this, new TEventArgs <User>((User)obj)); }), user); throw new Exception("1101可能其他地方登录/登出了 WEB 版微信,请检查手机端已登出WEB微信,然后稍后再试"); break; case "1102": Close(); asyncOperation.Post( new SendOrPostCallback((obj) => { LogoutComplete?.Invoke(this, new TEventArgs <User>((User)obj)); }), user); throw new Exception("1102被强制登出(很可能cookie冲突),请检查手机端已登出WEB微信,然后稍后再试"); break; default: //有其他任何异常,取消轮询 throw new Exception("轮询结果异常,停止轮询:" + syncCheckResult); break; } Thread.Sleep(1000); } catch (Exception ex) { FileLog.Exception("Init", ex); asyncOperation.Post( new SendOrPostCallback((obj) => { ExceptionCatched?.Invoke(this, new TEventArgs <Exception>((Exception)obj)); }), ex); } } }