├── .gitattributes ├── .gitignore ├── LICENSE.TXT ├── NuGet.config ├── README.md ├── WebQQWeChat.sln ├── global.json ├── nupkg └── pack.bat ├── pic ├── BatchHangQQ.png ├── webqqcore-mac.png ├── webqqcore-ubuntu.png ├── webqqcore-win.png └── webwechat-win.png ├── src ├── BatchHangQQ │ ├── BatchHangQQ.csproj │ ├── Extensions │ │ └── ControlExtensions.cs │ ├── Program.cs │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings │ ├── Util │ │ └── RichTextBoxLogger.cs │ ├── app.config │ ├── fmQQList.Designer.cs │ ├── fmQQList.cs │ ├── fmQQList.resx │ ├── fmSingleQQ.Designer.cs │ ├── fmSingleQQ.cs │ ├── fmSingleQQ.resx │ ├── fmTalkToFriend.Designer.cs │ ├── fmTalkToFriend.cs │ ├── fmTalkToFriend.resx │ ├── icon.ico │ └── packages.config ├── ConsoleTest │ ├── ConsoleTest.csproj │ ├── Program.cs │ └── Properties │ │ └── launchSettings.json ├── WebIm │ ├── Utils │ │ └── Extensions.cs │ └── WebIm.csproj ├── WebQQ │ ├── Im │ │ ├── Actions │ │ │ ├── ChannelLoginAction.cs │ │ │ ├── CheckQRCodeAction.cs │ │ │ ├── CheckSigAction.cs │ │ │ ├── GetDiscussionInfoAction.cs │ │ │ ├── GetDiscussionListAction.cs │ │ │ ├── GetFriendInfoAction.cs │ │ │ ├── GetFriendLongNickAction.cs │ │ │ ├── GetFriendQQNumberAction.cs │ │ │ ├── GetFriendsAction.cs │ │ │ ├── GetGroupInfoAction.cs │ │ │ ├── GetGroupNameListAction.cs │ │ │ ├── GetOnlineFriendsAction.cs │ │ │ ├── GetQRCodeAction.cs │ │ │ ├── GetSelfInfoAction.cs │ │ │ ├── GetVfwebqqAction.cs │ │ │ ├── PollMsgAction.cs │ │ │ ├── SendMsgAction.cs │ │ │ ├── WebQQAction.cs │ │ │ ├── WebQQActionFuture.cs │ │ │ └── WebQQInfoAction.cs │ │ ├── Bean │ │ │ ├── AllowType.cs │ │ │ ├── Birthday.cs │ │ │ ├── Category.cs │ │ │ ├── Content │ │ │ │ ├── ContentFatory.cs │ │ │ │ ├── ContentItemType.cs │ │ │ │ ├── FaceItem.cs │ │ │ │ ├── FontItem.cs │ │ │ │ ├── IContentItem.cs │ │ │ │ └── TextItem.cs │ │ │ ├── Discussion │ │ │ │ ├── DiscussionMember.cs │ │ │ │ ├── DiscussionMemberInfo.cs │ │ │ │ ├── DiscussionMemberStatus.cs │ │ │ │ ├── DiscussionMessage.cs │ │ │ │ └── QQDiscussion.cs │ │ │ ├── Friend │ │ │ │ ├── FriendBaseInfo.cs │ │ │ │ ├── FriendInfo.cs │ │ │ │ ├── FriendMarkName.cs │ │ │ │ ├── FriendMessage.cs │ │ │ │ ├── FriendOnlineInfo.cs │ │ │ │ └── QQFriend.cs │ │ │ ├── Group │ │ │ │ ├── GroupInfo.cs │ │ │ │ ├── GroupMember.cs │ │ │ │ ├── GroupMemberCard.cs │ │ │ │ ├── GroupMemberInfo.cs │ │ │ │ ├── GroupMessage.cs │ │ │ │ └── QQGroup.cs │ │ │ ├── Message.cs │ │ │ ├── PollType.cs │ │ │ ├── QQClientTypeInfo.cs │ │ │ ├── QQStatusType.cs │ │ │ ├── QQUser.cs │ │ │ ├── SelfInfo.cs │ │ │ ├── UserStatus.cs │ │ │ └── UserVipInfo.cs │ │ ├── Core │ │ │ ├── ApiUrls.cs │ │ │ ├── IQQClient.cs │ │ │ ├── IQQContext.cs │ │ │ ├── QQErrorCode.cs │ │ │ └── QQException.cs │ │ ├── Event │ │ │ ├── CheckQRCodeArgs.cs │ │ │ ├── QQNotifyEvent.cs │ │ │ └── QQNotifyEventListener.cs │ │ ├── Module │ │ │ ├── Impl │ │ │ │ ├── ChatModule.cs │ │ │ │ ├── LoginModule.cs │ │ │ │ ├── QQModule.cs │ │ │ │ ├── SessionModule.cs │ │ │ │ └── StoreModule.cs │ │ │ └── Interface │ │ │ │ ├── IChatModule.cs │ │ │ │ ├── ILoginModule.cs │ │ │ │ └── IQQModule.cs │ │ ├── Service │ │ │ ├── Impl │ │ │ │ ├── QQActionFactory.cs │ │ │ │ └── QQConsoleLogger.cs │ │ │ └── Interface │ │ │ │ ├── IQQActionFactory.cs │ │ │ │ └── IQQService.cs │ │ └── WebQQClient.cs │ ├── Startup.cs │ ├── Util │ │ ├── Extesions.cs │ │ ├── Helpers.cs │ │ ├── ImageConverter.cs │ │ ├── QQEncryptor.cs │ │ ├── Resource.cs │ │ ├── RetryHelper.cs │ │ └── RobotType.cs │ └── WebQQ.csproj ├── WebQQCore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── Im │ │ ├── Action │ │ │ ├── AbstractHttpAction.cs │ │ │ ├── AcceptBuddyAddAction.cs │ │ │ ├── ChangeStatusAction.cs │ │ │ ├── ChannelLoginAction.cs │ │ │ ├── CheckEmailSig.cs │ │ │ ├── CheckLoginSigAction.cs │ │ │ ├── CheckQRCodeAction.cs │ │ │ ├── CheckVerifyAction.cs │ │ │ ├── DeleteEmailAction.cs │ │ │ ├── GetBuddyListAction.cs │ │ │ ├── GetCaptchaImageAction.cs │ │ │ ├── GetCustomFaceSigAction.cs │ │ │ ├── GetDiscuzInfoAction.cs │ │ │ ├── GetDiscuzListAction.cs │ │ │ ├── GetFriendAccoutAction.cs │ │ │ ├── GetFriendFaceAction.cs │ │ │ ├── GetFriendInfoAction.cs │ │ │ ├── GetFriendSignAction.cs │ │ │ ├── GetGroupAccoutAction.cs │ │ │ ├── GetGroupFaceAction.cs │ │ │ ├── GetGroupInfoAction.cs │ │ │ ├── GetGroupListAction.cs │ │ │ ├── GetGroupMemberStatusAction.cs │ │ │ ├── GetGroupPicAction.cs │ │ │ ├── GetLoginSigAction.cs │ │ │ ├── GetOffPicAction.cs │ │ │ ├── GetOnlineFriendAction.cs │ │ │ ├── GetPT4Auth.cs │ │ │ ├── GetQRCodeAction.cs │ │ │ ├── GetRecentListAction.cs │ │ │ ├── GetSelfInfoAction.cs │ │ │ ├── GetSessionMsgSigAction.cs │ │ │ ├── GetStrangerInfoAction.cs │ │ │ ├── GetTuringRobotReplyAction.cs │ │ │ ├── GetUserLevelAction.cs │ │ │ ├── GetUserPicAction.cs │ │ │ ├── GetVFWebqq.cs │ │ │ ├── GetWPKeyAction.cs │ │ │ ├── GetWebqq.cs │ │ │ ├── LoginEmailAction.cs │ │ │ ├── MarkEmailAction.cs │ │ │ ├── PollEmailAction.cs │ │ │ ├── PollMsgAction.cs │ │ │ ├── SearchGroupInfoAction.cs │ │ │ ├── SendInputNotifyAction.cs │ │ │ ├── SendMsgAction.cs │ │ │ ├── ShakeWindowAction.cs │ │ │ ├── UpdateGroupMessageFilterAction.cs │ │ │ ├── UploadCustomFaceAction.cs │ │ │ ├── UploadOfflinePictureAction.cs │ │ │ ├── WebLoginAction.cs │ │ │ └── WebLogoutAction.cs │ │ ├── Actor │ │ │ ├── ExitActor.cs │ │ │ ├── HttpActor.cs │ │ │ ├── IQQActor.cs │ │ │ ├── IQQActorDispatcher.cs │ │ │ ├── MutipleActorDispatcher.cs │ │ │ └── SimpleActorDispatcher.cs │ │ ├── Bean │ │ │ ├── Content │ │ │ │ ├── CFaceItem.cs │ │ │ │ ├── ContentItem.cs │ │ │ │ ├── FaceItem.cs │ │ │ │ ├── FontItem.cs │ │ │ │ ├── OffPicItem.cs │ │ │ │ └── TextItem.cs │ │ │ ├── QQAccount.cs │ │ │ ├── QQAllow.cs │ │ │ ├── QQBuddy.cs │ │ │ ├── QQCategory.cs │ │ │ ├── QQClientTypeInfo.cs │ │ │ ├── QQDiscuz.cs │ │ │ ├── QQDiscuzMember.cs │ │ │ ├── QQEmail.cs │ │ │ ├── QQGroup.cs │ │ │ ├── QQGroupMember.cs │ │ │ ├── QQGroupSearchInfo.cs │ │ │ ├── QQGroupSearchList.cs │ │ │ ├── QQHalfStranger.cs │ │ │ ├── QQLevel.cs │ │ │ ├── QQMsg.cs │ │ │ ├── QQStatus.cs │ │ │ ├── QQStranger.cs │ │ │ └── QQUser.cs │ │ ├── Core │ │ │ ├── IQQContext.cs │ │ │ ├── IQQLifeCycle.cs │ │ │ ├── IQQModule.cs │ │ │ ├── IQQService.cs │ │ │ ├── QQConstants.cs │ │ │ ├── QQSession.cs │ │ │ └── QQStore.cs │ │ ├── Event │ │ │ ├── Future │ │ │ │ ├── AbstractActionFuture.cs │ │ │ │ ├── HttpActionFuture.cs │ │ │ │ └── ProcActionFuture.cs │ │ │ ├── IQQActionFuture.cs │ │ │ ├── QQActionEvent.cs │ │ │ ├── QQActionEventArgs.cs │ │ │ ├── QQEvent.cs │ │ │ ├── QQNotifyEvent.cs │ │ │ ├── QQNotifyEventArgs.cs │ │ │ ├── QQNotifyHandler.cs │ │ │ └── QQNotifyHandlerProxy.cs │ │ ├── Http │ │ │ ├── IHttpAction.cs │ │ │ ├── IQQHttpListener.cs │ │ │ ├── QQHttpCookie.cs │ │ │ ├── QQHttpCookieJar.cs │ │ │ ├── QQHttpRequest.cs │ │ │ └── QQHttpResponse.cs │ │ ├── IQQClient.cs │ │ ├── Log │ │ │ ├── EmptyQQLogger.cs │ │ │ ├── IQQLogger.cs │ │ │ └── QQConsoleLogger.cs │ │ ├── Module │ │ │ ├── AbstractModule.cs │ │ │ ├── BuddyModule.cs │ │ │ ├── CategoryModule.cs │ │ │ ├── ChatModule.cs │ │ │ ├── DiscuzModule.cs │ │ │ ├── EmailModule.cs │ │ │ ├── GroupModule.cs │ │ │ ├── LoginModule.cs │ │ │ ├── ProcModule.cs │ │ │ └── UserModule.cs │ │ ├── QQActionListener.cs │ │ ├── QQErrorCode.cs │ │ ├── QQException.cs │ │ ├── QQNotifyListener.cs │ │ ├── Service │ │ │ ├── AbstractService.cs │ │ │ ├── HttpService.cs │ │ │ └── IHttpService.cs │ │ └── WebQQClient.cs │ ├── QRcodeLoginTest.cs │ ├── Resources │ │ └── hash.js │ ├── Test.cs │ ├── Util │ │ ├── DateUtils.cs │ │ ├── DefaultLogger.cs │ │ ├── EmptyDisposable.cs │ │ ├── Extensions │ │ │ ├── ByteExtensions.cs │ │ │ ├── CollectionExtensions.cs │ │ │ ├── DictionaryExtensions.cs │ │ │ ├── EnumExtensions.cs │ │ │ ├── HttpExtensions.cs │ │ │ ├── LoggerExtensions.cs │ │ │ ├── Md5Extensions.cs │ │ │ ├── ObjectExtensions.cs │ │ │ ├── StreamExtensions.cs │ │ │ ├── StringBuilderExtensions.cs │ │ │ └── StringExtensions.cs │ │ ├── HttpCilentHelper.cs │ │ ├── HttpConstants.cs │ │ ├── MimeMapping.cs │ │ ├── QQEncryptor.cs │ │ ├── Resource.cs │ │ ├── RetryHelper.cs │ │ ├── RobotType.cs │ │ ├── SimpleConsoleLogger.cs │ │ ├── StringHelper.cs │ │ └── UrlUtils.cs │ └── WebQQCore.csproj └── WebWeChat │ ├── Im │ ├── Actions │ │ ├── ActionResult │ │ │ ├── SyncCheckResult.cs │ │ │ └── WatiForLoginResult.cs │ │ ├── BatchGetContactAction.cs │ │ ├── GetContactAction.cs │ │ ├── GetQRCodeAction.cs │ │ ├── GetTuringRobotReplyAction.cs │ │ ├── GetUuidAction.cs │ │ ├── SendMsgAction.cs │ │ ├── StatusNotifyAction.cs │ │ ├── SyncCheckAction.cs │ │ ├── WatiForLoginAction.cs │ │ ├── WebLoginAction.cs │ │ ├── WebWeChatAction.cs │ │ ├── WebWeChatActionFuture.cs │ │ ├── WebwxInitAction.cs │ │ └── WebwxSyncAction.cs │ ├── Bean │ │ ├── Appinfo.cs │ │ ├── ContactMember.cs │ │ ├── GroupMember.cs │ │ ├── Message.cs │ │ ├── MessageSent.cs │ │ └── RecommendInfo.cs │ ├── Core │ │ ├── ApiUrls.cs │ │ ├── IWeChatContext.cs │ │ ├── IWebWeChatClient.cs │ │ ├── WeChatErrorCode.cs │ │ └── WeChatException.cs │ ├── Event │ │ ├── WeChatNotifyEvent.cs │ │ ├── WeChatNotifyEventListener.cs │ │ └── WeChatNotifyEventType.cs │ ├── Module │ │ ├── Impl │ │ │ ├── ChatModule.cs │ │ │ ├── ContactModule.cs │ │ │ ├── LoginModule.cs │ │ │ ├── SessionModule.cs │ │ │ ├── StoreModule.cs │ │ │ └── WeChatModule.cs │ │ └── Interface │ │ │ ├── IChatModule.cs │ │ │ ├── IContactModule.cs │ │ │ ├── ILoginModule.cs │ │ │ └── IWeChatModule.cs │ ├── RobotType.cs │ ├── Service │ │ ├── Impl │ │ │ ├── WeChatActionFactory.cs │ │ │ └── WeChatConsoleLogger.cs │ │ └── Interface │ │ │ ├── IWeChatActionFactory.cs │ │ │ └── IWeChatService.cs │ └── WebWeChatClient.cs │ ├── Startup.cs │ ├── Util │ └── Extesions.cs │ └── WebWeChat.csproj └── tools ├── gitlink └── GitLink.exe └── nuget └── nuget.exe /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2015 huoshan12345 <89009143@qq.com> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebQQWeChat [![star this repo](http://github-svg-buttons.herokuapp.com/star.svg?user=huoshan12345&repo=WebQQWeChat&style=flat&background=1081C1)](https://github.com/huoshan12345/WebQQWeChat) [![fork this repo](http://github-svg-buttons.herokuapp.com/fork.svg?user=huoshan12345&repo=WebQQWeChat&style=flat&background=1081C1)](https://github.com/huoshan12345/WebQQWeChat/fork) [![license](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)](https://github.com/huoshan12345/WebQQWeChat/blob/master/LICENSE.TXT) 2 | 3 | 这个项目是[网页QQ](http://web2.qq.com/)和[网页微信](https://web.wechat.com/)相关协议的.net实现。基于此的一些功能组件。 4 | 5 | ## 主要模块介绍 6 | ### 1. [WebQQ](https://github.com/huoshan12345/WebQQWeChat/tree/master/src/WebQQ) [![](https://img.shields.io/badge/color-2.0.3-ff69b4.svg?maxAge=2592000&label=.net%20core%20)](https://www.microsoft.com/net/download) 7 | **移植于[iqq webqq-core](https://github.com/im-qq/webqq-core.git) 在此对作者表示衷心的感谢** 8 | 网页QQ协议的C#实现。项目作为类库相当于网页QQ的sdk,内置了茉莉图灵机器人,和可用于开发聊天机器人、消息推送、群自动管理等软件。项目作为控制台程序可用于演示使用流程 9 | ![webqqcore-ubuntu](https://raw.githubusercontent.com/huoshan12345/iQQ.Net/master/pic/webqqcore-ubuntu.png) 10 | 11 | ### 2. [WebWeChat](https://github.com/huoshan12345/WebQQWeChat/tree/master/src/WebWeChat) [![](https://img.shields.io/badge/color-2.0.3-ff69b4.svg?maxAge=2592000&label=.net%20core%20)](https://www.microsoft.com/net/download) 12 | **网页微信协议主要参考了[WeixinBot](https://github.com/Urinx/WeixinBot) 在此对作者表示衷心的感谢** 13 | 仿照WebQQCore的架构,网页微信协议的C#实现。项目作为类库相当于网页微信的sdk,项目作为控制台程序可用于演示使用流程 14 | ![webwechat-win](https://raw.githubusercontent.com/huoshan12345/iQQ.Net/master/pic/webwechat-win.png) 15 | 16 | ## 开发环境 17 | 1. [Visual Studio 2017](https://www.visualstudio.com/zh-hans/downloads/) 18 | 2. [.NET Core 2.0.3](https://www.microsoft.com/net/download) 19 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | {"projects":["src","test"]} -------------------------------------------------------------------------------- /nupkg/pack.bat: -------------------------------------------------------------------------------- 1 | REM "..\tools\gitlink\GitLink.exe" ..\ -u https://github.com/huoshan12345/WebQQWeChat -c release 2 | 3 | @ECHO OFF 4 | SET /P VERSION_SUFFIX=Please enter version-suffix (can be left empty): 5 | dotnet "pack" "..\src\WebQQ" -c "Release" -o ..\..\nupkg\ --include-symbols --version-suffix "%VERSION_SUFFIX%" 6 | dotnet "pack" "..\src\WebWeChat" -c "Release" -o ..\..\nupkg\ --include-symbols --version-suffix "%VERSION_SUFFIX%" 7 | pause 8 | -------------------------------------------------------------------------------- /pic/BatchHangQQ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huoshan12345/WebQQWeChat/cecfd5deb7843ad5a9b0546ae4f073b9a7cd5413/pic/BatchHangQQ.png -------------------------------------------------------------------------------- /pic/webqqcore-mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huoshan12345/WebQQWeChat/cecfd5deb7843ad5a9b0546ae4f073b9a7cd5413/pic/webqqcore-mac.png -------------------------------------------------------------------------------- /pic/webqqcore-ubuntu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huoshan12345/WebQQWeChat/cecfd5deb7843ad5a9b0546ae4f073b9a7cd5413/pic/webqqcore-ubuntu.png -------------------------------------------------------------------------------- /pic/webqqcore-win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huoshan12345/WebQQWeChat/cecfd5deb7843ad5a9b0546ae4f073b9a7cd5413/pic/webqqcore-win.png -------------------------------------------------------------------------------- /pic/webwechat-win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huoshan12345/WebQQWeChat/cecfd5deb7843ad5a9b0546ae4f073b9a7cd5413/pic/webwechat-win.png -------------------------------------------------------------------------------- /src/BatchHangQQ/Extensions/ControlExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Forms; 8 | 9 | namespace iQQ.Net.BatchHangQQ.Extensions 10 | { 11 | public static class ControlExtensions 12 | { 13 | public static void InvokeIfRequired(this Control control, MethodInvoker action) 14 | { 15 | // See Update 2 for edits Mike de Klerk suggests to insert here. 16 | 17 | if (control.InvokeRequired) 18 | { 19 | control.Invoke(action); 20 | } 21 | else 22 | { 23 | action(); 24 | } 25 | } 26 | 27 | public static int FindFirstItemIndex(this ListView lv, string cellText, int[] subItemIndexes) 28 | { 29 | foreach (ListViewItem item in lv.Items) 30 | { 31 | if (subItemIndexes.Any(subItemIndex => item.SubItems[subItemIndex].Text == cellText)) 32 | { 33 | return item.Index; 34 | } 35 | } 36 | return -1; 37 | } 38 | 39 | public static void UpdateItem(this ListView lv, int itemIndex, ListViewItem item, int[] updateSubItemIndexes) 40 | { 41 | var oldItem = lv.Items[itemIndex]; 42 | foreach (var subIndex in updateSubItemIndexes) 43 | { 44 | oldItem.SubItems[subIndex] = item.SubItems[subIndex]; 45 | } 46 | } 47 | 48 | public static void AppendText(this RichTextBox box, string text, Color color) 49 | { 50 | box.SelectionStart = box.TextLength; 51 | box.SelectionLength = 0; 52 | 53 | box.SelectionColor = color; 54 | box.AppendText(text); 55 | box.SelectionColor = box.ForeColor; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/BatchHangQQ/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Windows.Forms; 4 | 5 | namespace iQQ.Net.BatchHangQQ 6 | { 7 | static class Program 8 | { 9 | // 保证只能运行一个窗体实例 10 | private static readonly Mutex MainFormMutex = new Mutex(true, typeof(Program).FullName); 11 | 12 | /// 13 | /// 应用程序的主入口点。 14 | /// 15 | [STAThread] 16 | static void Main() 17 | { 18 | if (MainFormMutex.WaitOne(0, false)) 19 | { 20 | Application.EnableVisualStyles(); 21 | Application.SetCompatibleTextRenderingDefault(false); 22 | Application.Run(new FmQQList()); 23 | } 24 | else 25 | { 26 | MessageBox.Show("程序已经在运行!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); 27 | Application.Exit(); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/BatchHangQQ/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的常规信息通过下列属性集 6 | // 控制。更改这些属性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("Main")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("MyWebQQ")] 13 | [assembly: AssemblyCopyright("huoshan12345")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 使此程序集中的类型 18 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, 19 | // 则将该类型上的 ComVisible 属性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("7610bf85-1d06-4a6b-b723-7422afe255e5")] 24 | 25 | // 程序集的版本信息由下面四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 内部版本号 30 | // 修订号 31 | // 32 | // 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/BatchHangQQ/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace iQQ.Net.BatchHangQQ.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/BatchHangQQ/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/BatchHangQQ/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/BatchHangQQ/fmSingleQQ.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Windows.Forms; 4 | 5 | //using System.Data; 6 | 7 | namespace iQQ.Net.BatchHangQQ 8 | { 9 | public partial class FmSingleQQ : Form 10 | { 11 | private static readonly string[] _loginProtocol = { "WebQQ", "MobileQQ" }; 12 | 13 | public FmSingleQQ() 14 | { 15 | InitializeComponent(); 16 | cboLoginProtocol.Items.AddRange(_loginProtocol.Cast().ToArray()); 17 | cboLoginProtocol.SelectedIndex = 1; 18 | } 19 | 20 | private void btnLogin_Click(object sender, EventArgs e) 21 | { 22 | 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/BatchHangQQ/fmTalkToFriend.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace iQQ.Net.BatchHangQQ 5 | { 6 | public partial class FmTalkToFriend : Form 7 | { 8 | public FmQQList _parent; 9 | public string _qqNum = ""; 10 | 11 | public FmTalkToFriend() 12 | { 13 | InitializeComponent(); 14 | } 15 | 16 | private void TalkToFriend_Load(object sender, EventArgs e) 17 | { 18 | this.lab_friend_num.Text = _qqNum; 19 | } 20 | 21 | private void btSend_Click(object sender, EventArgs e) 22 | { 23 | /* 发送消息给好友 */ 24 | 25 | } 26 | 27 | /// 28 | /// 关闭聊天窗口时移除聊天列表及窗口 29 | /// 30 | /// 31 | /// 32 | private void CloseTalk(object sender, FormClosedEventArgs e) 33 | { 34 | 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/BatchHangQQ/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huoshan12345/WebQQWeChat/cecfd5deb7843ad5a9b0546ae4f073b9a7cd5413/src/BatchHangQQ/icon.ico -------------------------------------------------------------------------------- /src/ConsoleTest/ConsoleTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/ConsoleTest/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "ConsoleTest": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/WebIm/WebIm.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | latest 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Actions/ChannelLoginAction.cs: -------------------------------------------------------------------------------- 1 | using FclEx.Extensions; 2 | using HttpAction; 3 | using HttpAction.Core; 4 | using HttpAction.Event; 5 | using Newtonsoft.Json.Linq; 6 | using WebQQ.Im.Bean; 7 | using WebQQ.Im.Core; 8 | 9 | namespace WebQQ.Im.Actions 10 | { 11 | public class ChannelLoginAction : WebQQInfoAction 12 | { 13 | public ChannelLoginAction(IQQContext context, ActionEventListener listener = null) : base(context, listener) 14 | { 15 | } 16 | 17 | protected override EnumRequestType RequestType { get; } = EnumRequestType.Form; 18 | 19 | protected override void ModifyRequest(HttpRequestItem req) 20 | { 21 | var json = new JObject 22 | { 23 | {"status", QQStatusType.Online.ToString().ToLower()}, 24 | {"ptwebqq", Session.Ptwebqq}, 25 | {"clientid", Session.ClientId}, 26 | {"psessionid", ""} 27 | }; 28 | req.AddData("r", json.ToSimpleString()); 29 | req.Referrer = ApiUrls.Referrer; 30 | } 31 | 32 | protected override void HandleResult(JToken json) 33 | { 34 | var ret = json["result"]; 35 | Session.User.Uin = ret["uin"].ToLong(); 36 | Session.User.Status = ret["status"].ToEnum(QQStatusType.Online); 37 | Session.SessionId = ret["psessionid"].ToString(); 38 | Session.Index = ret["index"].ToInt(); 39 | Session.Port = ret["port"].ToInt(); 40 | // Session.Vfwebqq = ret["vfwebqq"].ToString(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Actions/CheckSigAction.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using HttpAction.Core; 3 | using HttpAction.Event; 4 | using WebQQ.Im.Core; 5 | 6 | namespace WebQQ.Im.Actions 7 | { 8 | public class CheckSigAction : WebQQAction 9 | { 10 | public CheckSigAction(IQQContext context, ActionEventListener listener = null) : base(context, listener) 11 | { 12 | } 13 | 14 | protected override string Url => Session.CheckSigUrl; 15 | 16 | protected override EnumRequestType RequestType { get; } = EnumRequestType.Get; 17 | 18 | protected override Task HandleResponse(HttpResponseItem response) 19 | { 20 | var ptwebqq = HttpService.GetCookie("ptwebqq", Session.CheckSigUrl); 21 | ptwebqq.Expired = true; 22 | //Session.Ptwebqq = ptwebqq; 23 | return NotifyOkEventAsync(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Actions/GetDiscussionListAction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using FclEx.Extensions; 4 | using HttpAction.Core; 5 | using HttpAction.Event; 6 | using HttpAction; 7 | using Newtonsoft.Json.Linq; 8 | using WebQQ.Im.Bean.Discussion; 9 | using WebQQ.Im.Core; 10 | 11 | namespace WebQQ.Im.Actions 12 | { 13 | public class GetDiscussionListAction : WebQQInfoAction 14 | { 15 | public GetDiscussionListAction(IQQContext context, ActionEventListener listener = null) : base(context, listener) 16 | { 17 | } 18 | 19 | protected override void ModifyRequest(HttpRequestItem req) 20 | { 21 | req.AddData("clientid", Session.ClientId); 22 | req.AddData("psessionid", Session.SessionId); 23 | req.AddData("vfwebqq", Session.Vfwebqq); 24 | req.AddData("t", Timestamp); 25 | req.Referrer = ApiUrls.ReferrerS; 26 | } 27 | 28 | protected override void HandleResult(JToken json) 29 | { 30 | /* 31 | { 32 | "retcode": 0, 33 | "result": { 34 | "dnamelist": [ 35 | { 36 | "name": "月光双刀、Test、月光借口、月光双", 37 | "did": 522140442 38 | } 39 | ] 40 | } 41 | } 42 | */ 43 | var result = json["result"]; 44 | var list = result["dnamelist"].ToObject>(); 45 | list.ForEach(Store.AddDiscussion); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Actions/GetFriendLongNickAction.cs: -------------------------------------------------------------------------------- 1 | using HttpAction.Core; 2 | using HttpAction.Event; 3 | using HttpAction; 4 | using Newtonsoft.Json.Linq; 5 | using WebQQ.Im.Bean.Friend; 6 | using WebQQ.Im.Core; 7 | 8 | namespace WebQQ.Im.Actions 9 | { 10 | /// 11 | /// 获取好友个性签名 12 | /// 13 | public class GetFriendLongNickAction : WebQQInfoAction 14 | { 15 | private readonly QQFriend _friend; 16 | 17 | public GetFriendLongNickAction(IQQContext context, QQFriend friend, ActionEventListener listener = null) : base(context, listener) 18 | { 19 | _friend = friend; 20 | } 21 | 22 | 23 | protected override void ModifyRequest(HttpRequestItem req) 24 | { 25 | req.AddData("tuin", _friend.Uin); 26 | req.AddData("vfwebqq", Session.Vfwebqq); 27 | req.AddData("t", Timestamp); 28 | req.Referrer = ApiUrls.ReferrerS; 29 | } 30 | 31 | protected override void HandleResult(JToken json) 32 | { 33 | /* 34 | { 35 | "retcode": 0, 36 | "result": [ 37 | { 38 | "uin": 984536900, 39 | "lnick": "" 40 | } 41 | ] 42 | } 43 | */ 44 | _friend.LongNick = json["result"][0]["lnick"].ToString(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Actions/GetFriendQQNumberAction.cs: -------------------------------------------------------------------------------- 1 | using FclEx.Extensions; 2 | using HttpAction.Core; 3 | using HttpAction.Event; 4 | using HttpAction; 5 | using Newtonsoft.Json.Linq; 6 | using WebQQ.Im.Bean.Friend; 7 | using WebQQ.Im.Core; 8 | 9 | namespace WebQQ.Im.Actions 10 | { 11 | public class GetFriendQQNumberAction : WebQQInfoAction 12 | { 13 | private readonly QQFriend _friend; 14 | 15 | public GetFriendQQNumberAction(IQQContext context, QQFriend friend, ActionEventListener listener = null) 16 | : base(context, listener) 17 | { 18 | _friend = friend; 19 | } 20 | 21 | protected override void ModifyRequest(HttpRequestItem req) 22 | { 23 | req.AddData("tuin", _friend.Uin); 24 | req.AddData("type", 1); 25 | req.AddData("vfwebqq", Session.Vfwebqq); 26 | req.AddData("t", Timestamp); 27 | req.Referrer = ApiUrls.ReferrerS; 28 | } 29 | 30 | protected override void HandleResult(JToken json) 31 | { 32 | /* 33 | { 34 | "retcode": 0, 35 | "result": { 36 | "uiuin": "", // 总是为空,所以忽略了 37 | "account": 510942549, 38 | "uin": 3943520589 39 | } 40 | } 41 | */ 42 | _friend.QQNumber = json["result"]["account"].ToLong(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Actions/GetOnlineFriendsAction.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using AutoMapper; 3 | using FclEx.Extensions; 4 | using HttpAction.Core; 5 | using HttpAction.Event; 6 | using HttpAction; 7 | using Newtonsoft.Json.Linq; 8 | using WebQQ.Im.Bean.Friend; 9 | using WebQQ.Im.Core; 10 | 11 | namespace WebQQ.Im.Actions 12 | { 13 | public class GetOnlineFriendsAction : WebQQInfoAction 14 | { 15 | public GetOnlineFriendsAction(IQQContext context, ActionEventListener listener = null) : base(context, listener) 16 | { 17 | } 18 | 19 | protected override void ModifyRequest(HttpRequestItem req) 20 | { 21 | req.AddData("vfwebqq", Session.Vfwebqq); 22 | req.AddData("clientid", Session.ClientId); 23 | req.AddData("psessionid", Session.SessionId); 24 | req.AddData("t", Timestamp); 25 | req.Referrer = ApiUrls.Referrer; 26 | } 27 | 28 | protected override void HandleResult(JToken json) 29 | { 30 | /* 31 | { 32 | "result": [ 33 | { 34 | "client_type": 1, 35 | "status": "online", 36 | "uin": 3017767504 37 | } 38 | ], 39 | "retcode": 0 40 | } 41 | */ 42 | 43 | var result = json["result"].ToObject(); 44 | foreach (var info in result) 45 | { 46 | Store.FriendDic.GetAndDo(info.Uin, friend => Mapper.Map(info, friend)); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Actions/GetQRCodeAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using HttpAction.Core; 4 | using HttpAction.Event; 5 | using HttpAction; 6 | using WebQQ.Im.Core; 7 | 8 | namespace WebQQ.Im.Actions 9 | { 10 | public class GetQRCodeAction : WebQQAction 11 | { 12 | private const string AppId = "501004106"; 13 | 14 | public GetQRCodeAction(IQQContext context, ActionEventListener listener = null) : base(context, listener) { } 15 | 16 | protected override EnumRequestType RequestType { get; } = EnumRequestType.Get; 17 | 18 | protected override void ModifyRequest(HttpRequestItem req) 19 | { 20 | req.AddData("appid", AppId); 21 | req.AddData("e", "0"); 22 | req.AddData("l", "M"); 23 | req.AddData("s", "5"); 24 | req.AddData("d", "72"); 25 | req.AddData("v", "4"); 26 | req.AddData("t", new Random().NextDouble()); 27 | req.ResultType = HttpResultType.Byte; 28 | } 29 | 30 | protected override Task HandleResponse(HttpResponseItem response) 31 | { 32 | return NotifyOkEventAsync(ImageSharp.Image.Load(response.ResponseBytes)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Actions/GetVfwebqqAction.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using FclEx.Extensions; 3 | using HttpAction.Core; 4 | using HttpAction.Event; 5 | using HttpAction; 6 | using WebQQ.Im.Core; 7 | 8 | namespace WebQQ.Im.Actions 9 | { 10 | public class GetVfwebqqAction : WebQQAction 11 | { 12 | public GetVfwebqqAction(IQQContext context, ActionEventListener listener = null) : base(context, listener) 13 | { 14 | } 15 | 16 | protected override EnumRequestType RequestType { get; } = EnumRequestType.Get; 17 | 18 | protected override void ModifyRequest(HttpRequestItem req) 19 | { 20 | req.AddData("ptwebqq", Session.Ptwebqq); 21 | req.AddData("clientid", Session.ClientId); 22 | req.AddData("psessionid", ""); 23 | req.AddData("t", Timestamp); 24 | req.Referrer = ApiUrls.ReferrerS; 25 | } 26 | 27 | protected override Task HandleResponse(HttpResponseItem response) 28 | { 29 | var json = response.ResponseString.ToJToken(); 30 | if (json["retcode"].ToString() == "0") 31 | { 32 | var ret = json["result"]; 33 | Session.Vfwebqq = ret["vfwebqq"].ToString(); 34 | return NotifyOkEventAsync(); 35 | } 36 | else 37 | { 38 | throw new QQException(QQErrorCode.ResponseError, response.ResponseString); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Actions/WebQQActionFuture.cs: -------------------------------------------------------------------------------- 1 | using HttpAction.Actions; 2 | using HttpAction.Event; 3 | using WebQQ.Im.Core; 4 | using WebQQ.Im.Service.Interface; 5 | 6 | namespace WebQQ.Im.Actions 7 | { 8 | public class WebQQActionFuture : ActionFuture 9 | { 10 | protected IQQActionFactory ActionFactory { get; } 11 | 12 | public WebQQActionFuture(IQQContext context, ActionEventListener listener = null) 13 | : base(listener) 14 | { 15 | ActionFactory = context.GetSerivce(); 16 | } 17 | 18 | /// 19 | /// 动态创建action,要求Action的构造函数第一个参数必须为Context 20 | /// args指的是Action的构造函数的除了Context以外的参数按顺序排列 21 | /// 和PushAction(IAction action)的区别就是不需要new一个action并且给它传一个Context参数(这个参数每个QQAction都需要传) 22 | /// 23 | /// 24 | /// 25 | /// 26 | public WebQQActionFuture PushAction(params object[] args) where T : WebQQAction 27 | { 28 | var action = ActionFactory.CreateAction(args); 29 | return (WebQQActionFuture)base.PushAction(action); 30 | } 31 | 32 | /// 33 | /// 只传一个listener的重载 34 | /// 当使用lambda表达式传递listener的时候会匹配到这个重载 35 | /// 36 | /// 37 | /// 38 | /// 39 | public WebQQActionFuture PushAction(ActionEventListener listener) where T : WebQQAction 40 | { 41 | var action = ActionFactory.CreateAction(listener); 42 | return (WebQQActionFuture)base.PushAction(action); 43 | } 44 | 45 | public WebQQActionFuture PushAction(object obj, ActionEventListener listener) where T : WebQQAction 46 | { 47 | var action = ActionFactory.CreateAction(obj, listener); 48 | return (WebQQActionFuture)base.PushAction(action); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Actions/WebQQInfoAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Reflection; 4 | using System.Threading.Tasks; 5 | using FclEx.Extensions; 6 | using HttpAction.Core; 7 | using HttpAction.Event; 8 | using Newtonsoft.Json.Linq; 9 | using WebQQ.Im.Core; 10 | 11 | namespace WebQQ.Im.Actions 12 | { 13 | /// 14 | /// 获取信息类的qqaction的基类 15 | /// 16 | public abstract class WebQQInfoAction : WebQQAction 17 | { 18 | protected override EnumRequestType RequestType { get; } = EnumRequestType.Get; 19 | 20 | protected WebQQInfoAction(IQQContext context, ActionEventListener listener = null) 21 | : base(context, listener) { } 22 | 23 | protected override Task HandleResponse(HttpResponseItem response) 24 | { 25 | var json = response.ResponseString.ToJToken(); 26 | if (json["retcode"].ToString() == "0") 27 | { 28 | HandleResult(json); 29 | return NotifyOkEventAsync(); 30 | } 31 | else 32 | { 33 | throw new QQException(QQErrorCode.ResponseError, response.ResponseString); 34 | } 35 | } 36 | 37 | protected virtual void HandleResult(JToken json) { } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/AllowType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace WebQQ.Im.Bean 7 | { 8 | /// 9 | /// 对方设置的加好友策略 10 | /// 11 | public enum AllowType 12 | { 13 | /// 允许所有人添加 14 | AllowAll = 0, // 0 15 | 16 | /// 需要验证信息 17 | NeedConfirm, // 1 18 | 19 | /// 拒绝任何人加好友 20 | RefuseAll, // 2 21 | 22 | /// 需要回答问题 23 | NeedAnswer, // 3 24 | 25 | /// 需要验证和回答问题 26 | NeedAnswerAndConfirm, // 4 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Birthday.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace WebQQ.Im.Bean 7 | { 8 | public class Birthday 9 | { 10 | public int Month { get; set; } 11 | public int Year { get; set; } 12 | public int Day { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Category.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using WebQQ.Im.Bean.Friend; 5 | 6 | namespace WebQQ.Im.Bean 7 | { 8 | 9 | /// 10 | /// QQ分组 11 | /// 12 | public class Category 13 | { 14 | public int Index { get; set; } 15 | 16 | public int Sort { get; set; } 17 | 18 | public string Name { get; set; } 19 | 20 | // key是好友的uin 21 | public ConcurrentDictionary Friends { get; }= new ConcurrentDictionary(); 22 | 23 | public void AddFriend(QQFriend friend) 24 | { 25 | Friends[friend.Uin] = friend; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Content/ContentFatory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using FclEx.Extensions; 4 | using Newtonsoft.Json.Linq; 5 | using WebQQ.Util; 6 | 7 | namespace WebQQ.Im.Bean.Content 8 | { 9 | public static class ContentFatory 10 | { 11 | public static IContentItem CreateContentItem(JToken token) 12 | { 13 | var array = token as JArray; 14 | if (array != null) 15 | { 16 | var type = array[0].ToEnum(); 17 | switch (type) 18 | { 19 | case ContentItemType.Font: return array[1].ToObject(); 20 | case ContentItemType.Face: return new FaceItem(array[1].ToInt()); 21 | default: throw new ArgumentOutOfRangeException(); 22 | } 23 | } 24 | else return new TextItem(token.ToString()); 25 | } 26 | 27 | public static List ParseContents(JArray token) 28 | { 29 | var list = new List(token.Count); 30 | foreach (var t in token) 31 | { 32 | list.Add(CreateContentItem(t)); 33 | } 34 | return list; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Content/ContentItemType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using FclEx.Extensions; 6 | using Newtonsoft.Json.Linq; 7 | using WebQQ.Util; 8 | 9 | namespace WebQQ.Im.Bean.Content 10 | { 11 | public enum ContentItemType 12 | { 13 | Font, 14 | Face, 15 | Text 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Content/FaceItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FclEx.Extensions; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Linq; 5 | using WebQQ.Im.Core; 6 | 7 | namespace WebQQ.Im.Bean.Content 8 | { 9 | 10 | public class FaceItem : IContentItem 11 | { 12 | public int Id { get; set; } 13 | 14 | public FaceItem(int id) 15 | { 16 | Id = id; 17 | } 18 | 19 | public ContentItemType Type => ContentItemType.Face; 20 | 21 | public object ToJson() 22 | { 23 | return new JArray { Type.ToString().ToLower(), this }; 24 | } 25 | 26 | public string GetText() 27 | { 28 | return $"[表情 {Id}]"; 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Content/FontItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Linq; 5 | using WebQQ.Im.Core; 6 | using FclEx.Extensions; 7 | using WebQQ.Util; 8 | 9 | namespace WebQQ.Im.Bean.Content 10 | { 11 | public class FontItem : IContentItem 12 | { 13 | public string Name { get; set; } = "宋体"; 14 | public int Size { get; set; } = 12; 15 | public int[] Style { get; set; } = { 0, 0, 0 }; 16 | public string Color { get; set; } = 0.ToString("d6"); 17 | 18 | public bool IsBold() => Style?[0] != 0; // 加粗 19 | public bool IsItalic() => Style?[1] != 0; // 斜体 20 | public bool IsUnderline() => Style?[2] != 0; // 下划线 21 | 22 | [JsonIgnore] 23 | public ContentItemType Type => ContentItemType.Font; 24 | 25 | public object ToJson() 26 | { 27 | return new JArray { Type.ToString().ToLower(), JObject.FromObject(this, Helpers.JsonCamelSerializer) }; 28 | } 29 | 30 | public string GetText() 31 | { 32 | return string.Empty; 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Content/IContentItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace WebQQ.Im.Bean.Content 8 | { 9 | public interface IContentItem 10 | { 11 | ContentItemType Type { get; } 12 | object ToJson(); 13 | string GetText(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Content/TextItem.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace WebQQ.Im.Bean.Content 4 | { 5 | 6 | public class TextItem : IContentItem 7 | { 8 | public string Content { get; } 9 | 10 | public TextItem(string text) 11 | { 12 | Content = text; 13 | } 14 | 15 | public ContentItemType Type => ContentItemType.Text; 16 | 17 | public object ToJson() 18 | { 19 | return Content; 20 | } 21 | 22 | public string GetText() 23 | { 24 | return Content; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Discussion/DiscussionMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json; 6 | 7 | namespace WebQQ.Im.Bean.Discussion 8 | { 9 | public class DiscussionMember 10 | { 11 | [JsonProperty("mem_uin")] 12 | public long Uin { get; set; } 13 | 14 | [JsonProperty("ruin")] 15 | public long QQNumber { get; set; } 16 | 17 | public string Nick { get; set; } 18 | 19 | public int ClientType { get; set; } 20 | 21 | public virtual QQStatusType Status { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Discussion/DiscussionMemberInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json; 6 | 7 | namespace WebQQ.Im.Bean.Discussion 8 | { 9 | public class DiscussionMemberInfo 10 | { 11 | public long Uin { get; set; } 12 | public string Nick { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Discussion/DiscussionMemberStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json; 6 | 7 | namespace WebQQ.Im.Bean.Discussion 8 | { 9 | public class DiscussionMemberStatus : UserStatus 10 | { 11 | [JsonProperty("status")] 12 | public override QQStatusType Status { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Discussion/DiscussionMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WebQQ.Im.Bean.Discussion 4 | { 5 | public class DiscussionMessage : Message 6 | { 7 | public DiscussionMessage() 8 | { 9 | Type = MessageType.Discussion; 10 | } 11 | 12 | [JsonIgnore] 13 | public QQDiscussion Discussion { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Discussion/QQDiscussion.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using Newtonsoft.Json; 3 | using WebQQ.Im.Bean.Group; 4 | 5 | namespace WebQQ.Im.Bean.Discussion 6 | { 7 | /// 8 | /// QQ讨论组 9 | /// 10 | 11 | public class QQDiscussion 12 | { 13 | /// 14 | /// 讨论组ID,每次登陆都固定,视为没有变换 15 | /// 16 | public long Did { get; set; } 17 | /// 18 | /// 讨论组的名字 19 | /// 20 | public string Name { get; set; } 21 | 22 | [JsonIgnore] 23 | public ConcurrentDictionary Members { get; } = new ConcurrentDictionary(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Friend/FriendBaseInfo.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WebQQ.Im.Bean.Friend 4 | { 5 | public class FriendBaseInfo 6 | { 7 | public long Uin { get; set; } 8 | 9 | public int Face { get; set; } 10 | 11 | [JsonProperty("flag")] 12 | public int InfoFlag { get; set; } 13 | 14 | public string Nick { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Friend/FriendInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace WebQQ.Im.Bean.Friend 5 | { 6 | public class FriendInfo 7 | { 8 | public int Face { get; set; } 9 | public string Occupation { get; set; } 10 | public string Phone { get; set; } 11 | public AllowType Allow { get; set; } 12 | public string College { get; set; } 13 | public long Uin { get; set; } 14 | public int Constel { get; set; } 15 | public int Blood { get; set; } 16 | public string Homepage { get; set; } 17 | 18 | [JsonProperty("stat")] 19 | public QQStatusType Status { get; set; } 20 | 21 | [JsonProperty("vip_info")] 22 | public int VipInfo { get; set; } 23 | 24 | public string Country { get; set; } 25 | public string City { get; set; } 26 | public string Personal { get; set; } 27 | public string Nick { get; set; } 28 | public int Shengxiao { get; set; } 29 | public string Email { get; set; } 30 | public string Province { get; set; } 31 | public string Gender { get; set; } 32 | public string Mobile { get; set; } 33 | 34 | private Birthday _birthday; 35 | public Birthday Birthday 36 | { 37 | get { return _birthday; } 38 | set 39 | { 40 | _birthday = value; 41 | if (_birthday.Year != 0 && _birthday.Month != 0 && _birthday.Day != 0) 42 | { 43 | BirthdayDate = new DateTime(_birthday.Year, _birthday.Month, _birthday.Day); 44 | } 45 | } 46 | } 47 | public DateTime BirthdayDate { get; set; } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Friend/FriendMarkName.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WebQQ.Im.Bean.Friend 4 | { 5 | public class FriendMarkName 6 | { 7 | public long Uin { get; set; } 8 | 9 | public string MarkName { get; set; } 10 | 11 | [JsonProperty("type")] 12 | public int MarknameType { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Friend/FriendMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using WebQQ.Im.Bean.Content; 3 | 4 | namespace WebQQ.Im.Bean.Friend 5 | { 6 | public class FriendMessage : Message 7 | { 8 | public FriendMessage() 9 | { 10 | Type = MessageType.Friend; 11 | } 12 | 13 | [JsonIgnore] 14 | public QQFriend Friend { get; set; } 15 | 16 | public FriendMessage(QQFriend friend, string text) : this() 17 | { 18 | Friend = friend; 19 | Contents.Add(new TextItem(text)); 20 | Contents.Add(new FontItem()); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Friend/FriendOnlineInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json; 6 | 7 | namespace WebQQ.Im.Bean.Friend 8 | { 9 | public class FriendOnlineInfo 10 | { 11 | public long Uin { get; set; } 12 | 13 | [JsonProperty("client_type")] 14 | public int ClientType { get; set; } 15 | 16 | public QQStatusType Status { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Friend/QQFriend.cs: -------------------------------------------------------------------------------- 1 | using FclEx.Extensions; 2 | using Newtonsoft.Json; 3 | 4 | namespace WebQQ.Im.Bean.Friend 5 | { 6 | /// 7 | /// QQ好友 8 | /// 9 | public class QQFriend : FriendInfo 10 | { 11 | public int InfoFlag { get; set; } 12 | 13 | /// 14 | /// 个性签名 15 | /// 16 | public string LongNick { get; set; } 17 | 18 | /// 19 | /// 备注 20 | /// 21 | public string MarkName { get; set; } 22 | 23 | public int MarknameType { get; set; } 24 | 25 | public int CategoryIndex { get; set; } 26 | 27 | public int ClientType { get; set; } 28 | 29 | public int VipLevel { get; set; } 30 | 31 | public int IsVip { get; set; } 32 | 33 | public long QQNumber { get; set; } 34 | 35 | public string ShowName => MarkName.IsNullOrEmpty() ? (Nick.IsNullOrEmpty() ? Uin.ToString() : Nick) : MarkName; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Group/GroupInfo.cs: -------------------------------------------------------------------------------- 1 | namespace WebQQ.Im.Bean.Group 2 | { 3 | public class GroupInfo 4 | { 5 | public long Gid { get; set; } 6 | public long Code { get; set; } 7 | public string Name { get; set; } 8 | public long Flag { get; set; } 9 | public int Face { get; set; } 10 | public string Memo { get; set; } 11 | public int Class { get; set; } 12 | public string Fingermemo { get; set; } 13 | public int Createtime { get; set; } 14 | public int Level { get; set; } 15 | public long Owner { get; set; } 16 | public int Option { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Group/GroupMember.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WebQQ.Im.Bean.Group 4 | { 5 | public class GroupMember : GroupMemberInfo 6 | { 7 | [JsonProperty("muin")] 8 | public override long Uin { get; set; } 9 | 10 | [JsonProperty("mflag")] 11 | public int Flag { get; set; } 12 | 13 | public string Card { get; set; } 14 | public int ClientType { get; set; } 15 | public QQStatusType Status { get; set; } 16 | public int VipLevel { get; set; } 17 | public int IsVip { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Group/GroupMemberCard.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WebQQ.Im.Bean.Group 4 | { 5 | public class GroupMemberCard 6 | { 7 | [JsonProperty("muin")] 8 | public long Uin { get; set; } 9 | public string Card { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Group/GroupMemberInfo.cs: -------------------------------------------------------------------------------- 1 | using FclEx.Extensions; 2 | 3 | namespace WebQQ.Im.Bean.Group 4 | { 5 | public class GroupMemberInfo 6 | { 7 | public virtual long Uin { get; set; } 8 | public string Nick { get; set; } 9 | public string Province { get; set; } 10 | public string Gender { get; set; } 11 | public string Country { get; set; } 12 | public string City { get; set; } 13 | 14 | public string ShowName => Nick.IsNullOrEmpty() ? Uin.ToString() : Nick; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Group/GroupMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using WebQQ.Im.Bean.Content; 3 | 4 | namespace WebQQ.Im.Bean.Group 5 | { 6 | public class GroupMessage : Message 7 | { 8 | [JsonIgnore] 9 | public GroupMember User { get; set; } 10 | 11 | [JsonIgnore] 12 | public QQGroup Group { get; set; } 13 | 14 | public GroupMessage() 15 | { 16 | Type = MessageType.Group; 17 | } 18 | 19 | public GroupMessage(QQGroup group, string text) : this() 20 | { 21 | Group = group; 22 | Contents.Add(new TextItem(text)); 23 | Contents.Add(new FontItem()); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/Group/QQGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using Newtonsoft.Json; 3 | using FclEx.Extensions; 4 | 5 | namespace WebQQ.Im.Bean.Group 6 | { 7 | 8 | public class QQGroup : GroupInfo 9 | { 10 | /// 11 | /// 备注 12 | /// 13 | public string MarkName { get; set; } 14 | 15 | [JsonIgnore] 16 | public ConcurrentDictionary Members { get; } = new ConcurrentDictionary(); 17 | 18 | public string ShowName => MarkName.IsNullOrEmpty() ? Name : MarkName; 19 | 20 | public override string ToString() 21 | { 22 | return "QQGroup [gid=" + Gid + ", code=" + Code + ", name=" + Name + "]"; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/QQClientTypeInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WebQQ.Im.Bean 4 | { 5 | public enum QQClientType 6 | { 7 | WebQQ, 8 | MobileQQ, 9 | PcQQ, 10 | PadQQ, 11 | Unknown 12 | } 13 | 14 | /// 15 | /// QQ客户端类型 可能有多个值对应同一种客户端的情况,这些值是否能进一步区分,还需做测试 16 | /// 17 | public static class QQClientTypeInfo 18 | { 19 | public static QQClientType ValueOfRaw(string value) 20 | { 21 | QQClientType type; 22 | if (Enum.TryParse(value, true, out type)) 23 | { 24 | return type; 25 | } 26 | return QQClientType.Unknown; 27 | } 28 | 29 | public static QQClientType ValueOfRaw(int i) 30 | { 31 | switch (i) 32 | { 33 | case 1: 34 | case 2: 35 | case 3: 36 | case 4: 37 | case 5: 38 | case 6: 39 | case 10: 40 | case 0x1E4: 41 | return QQClientType.PcQQ; 42 | 43 | case 41: 44 | return QQClientType.WebQQ; 45 | 46 | case 21: 47 | case 22: 48 | case 23: 49 | case 24: 50 | return QQClientType.MobileQQ; 51 | 52 | case 42: 53 | return QQClientType.PadQQ; 54 | 55 | default: 56 | return QQClientType.Unknown; 57 | } 58 | } 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/QQStatusType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace WebQQ.Im.Bean 7 | { 8 | public enum QQStatusType 9 | { 10 | Online = 10, 11 | Offline = 20, 12 | Away = 30, 13 | Hidden = 40, 14 | Busy = 50, 15 | Callme = 60, 16 | Silent = 70 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/QQUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace WebQQ.Im.Bean 7 | { 8 | public class QQUser : SelfInfo 9 | { 10 | public QQStatusType Status { get; set; } 11 | 12 | public override Birthday Birthday 13 | { 14 | get { return _birthday; } 15 | set 16 | { 17 | _birthday = value; 18 | BirthdayDate = new DateTime(_birthday.Year, _birthday.Month, _birthday.Day); 19 | } 20 | } 21 | 22 | public DateTime BirthdayDate { get; set; } 23 | private Birthday _birthday; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/SelfInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json; 6 | using WebQQ.Im.Bean.Friend; 7 | 8 | namespace WebQQ.Im.Bean 9 | { 10 | public class SelfInfo 11 | { 12 | public virtual Birthday Birthday{ get; set; } 13 | public int Face { get; set; } 14 | public string Phone { get; set; } 15 | public string Occupation { get; set; } 16 | public AllowType Allow { get; set; } 17 | public string College { get; set; } 18 | public long Uin { get; set; } 19 | public int Blood { get; set; } 20 | public int Constel { get; set; } 21 | [JsonProperty("lnick")] 22 | public string LongNick { get; set; } 23 | public string Vfwebqq { get; set; } 24 | public string Homepage { get; set; } 25 | [JsonProperty("vip_info")] 26 | public int VipInfo { get; set; } 27 | public string City { get; set; } 28 | public string Country { get; set; } 29 | public string Personal { get; set; } 30 | public int Shengxiao { get; set; } 31 | public string Nick { get; set; } 32 | public string Email { get; set; } 33 | public string Province { get; set; } 34 | public long Account { get; set; } 35 | public string Gender { get; set; } 36 | public string Mobile { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/UserStatus.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WebQQ.Im.Bean 4 | { 5 | public class UserStatus 6 | { 7 | public long Uin { get; set; } 8 | 9 | [JsonProperty("client_type")] 10 | public int ClientType { get; set; } 11 | 12 | [JsonProperty("stat")] 13 | public virtual QQStatusType Status { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Bean/UserVipInfo.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WebQQ.Im.Bean 4 | { 5 | public class UserVipInfo 6 | { 7 | [JsonProperty("u")] 8 | public long Uin { get; set; } 9 | 10 | [JsonProperty("vip_level")] 11 | public int VipLevel { get; set; } 12 | 13 | [JsonProperty("is_vip")] 14 | public int IsVip { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Core/ApiUrls.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace WebQQ.Im.Core 7 | { 8 | public static class ApiUrls 9 | { 10 | public const string GetQRCode = "https://ssl.ptlogin2.qq.com/ptqrshow"; 11 | public const string CheckQRCode = "https://ssl.ptlogin2.qq.com/ptqrlogin"; 12 | public const string GetVfwebqq = "http://s.web2.qq.com/api/getvfwebqq"; 13 | public const string ChannelLogin = "http://d1.web2.qq.com/channel/login2"; 14 | public const string GetFriends = "http://s.web2.qq.com/api/get_user_friends2"; 15 | public const string Referrer = "http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2"; 16 | public const string ReferrerS = "http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1"; 17 | public const string GetGroupNameList = "http://s.web2.qq.com/api/get_group_name_list_mask2"; 18 | public const string GetDiscussionList = "http://s.web2.qq.com/api/get_discus_list"; 19 | public const string GetSelfInfo = "http://s.web2.qq.com/api/get_self_info2"; 20 | public const string GetOnlineFriends = "http://d1.web2.qq.com/channel/get_online_buddies2"; 21 | public const string GetFriendLongNick = "http://s.web2.qq.com/api/get_single_long_nick2"; 22 | public const string SendFriendMsg = "https://d1.web2.qq.com/channel/send_buddy_msg2"; 23 | public const string GetGroupInfo = "http://s.web2.qq.com/api/get_group_info_ext2"; 24 | public const string SendGroupMsg = "https://d1.web2.qq.com/channel/send_qun_msg2"; 25 | public const string GetDiscussionInfo = "http://d1.web2.qq.com/channel/get_discu_info"; 26 | public const string SendDiscussionMsg = "https://d1.web2.qq.com/channel/send_discu_msg2"; 27 | public const string GetFriendQQNumber = "http://s.web2.qq.com/api/get_friend_uin2"; 28 | public const string GetFriendInfo = "http://s.web2.qq.com/api/get_friend_info2"; 29 | public const string PollMsg = "http://d1.web2.qq.com/channel/poll2"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Core/IQQClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using WebQQ.Im.Module.Interface; 3 | 4 | namespace WebQQ.Im.Core 5 | { 6 | public interface IQQClient : IDisposable, IQQContext, ILoginModule, IChatModule 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Core/IQQContext.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using WebQQ.Im.Bean; 3 | using WebQQ.Im.Event; 4 | using WebQQ.Im.Module; 5 | using WebQQ.Im.Module.Interface; 6 | using WebQQ.Im.Service.Interface; 7 | 8 | namespace WebQQ.Im.Core 9 | { 10 | public interface IQQContext 11 | { 12 | T GetModule() where T : IQQModule; 13 | 14 | // 获取服务,该服务既可以是全局共享的,也可以是qq实例特有的,所以不要求T继承IQQService 15 | T GetSerivce(); 16 | 17 | Task FireNotifyAsync(QQNotifyEvent notifyEvent); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Core/QQErrorCode.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace WebQQ.Im.Core 4 | { 5 | public enum QQErrorCode 6 | { 7 | /// 8 | /// 登录凭证失效 9 | /// 10 | [Description("登录凭证失效")] 11 | InvalidLoginAuth, 12 | 13 | /// 14 | /// 响应错误 15 | /// 16 | [Description("响应错误")] 17 | ResponseError, 18 | 19 | /// 20 | /// 网络错误 21 | /// 22 | [Description("网络错误")] 23 | IoError, 24 | 25 | /// 26 | /// 参数错误 27 | /// 28 | [Description("参数错误")] 29 | ParameterError, 30 | 31 | /// 32 | /// Cookie错误 33 | /// 34 | [Description("Cookie错误")] 35 | CookieError, 36 | 37 | Timeout, // 等待超时 38 | 39 | JsonError, // JSON解析出错 40 | 41 | /// 42 | /// 未知错误 43 | /// 44 | [Description("未知错误")] 45 | UnknownError, 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Event/CheckQRCodeArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace WebQQ.Im.Event 5 | { 6 | public enum QRCodeStatus 7 | { 8 | /// 9 | /// 扫码成功 10 | /// 11 | Ok, 12 | 13 | /// 14 | /// 二维码未失效 15 | /// 16 | [Description("未失效")] 17 | Valid, 18 | 19 | /// 20 | /// 二维码失效 21 | /// 22 | [Description("已失效")] 23 | Invalid, 24 | 25 | /// 26 | /// 二维码认证中 27 | /// 28 | [Description("认证中")] 29 | Auth, 30 | } 31 | 32 | public class CheckQRCodeArgs : EventArgs 33 | { 34 | public QRCodeStatus Status { get; private set; } 35 | 36 | public string Msg { get; private set; } 37 | 38 | public CheckQRCodeArgs(QRCodeStatus status, string msg) 39 | { 40 | Status = status; 41 | Msg = msg; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Event/QQNotifyEventListener.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using WebQQ.Im.Core; 3 | 4 | namespace WebQQ.Im.Event 5 | { 6 | public delegate Task QQNotifyEventListener(IQQClient sender, QQNotifyEvent e); 7 | 8 | public interface IQQNotifyEventHandler 9 | { 10 | event QQNotifyEventListener QQNotifyEvent; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Module/Impl/ChatModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using HttpAction; 6 | using HttpAction.Event; 7 | using WebQQ.Im.Actions; 8 | using WebQQ.Im.Bean; 9 | using WebQQ.Im.Core; 10 | using WebQQ.Im.Module.Interface; 11 | using WebQQ.Util; 12 | 13 | namespace WebQQ.Im.Module.Impl 14 | { 15 | public class ChatModule : QQModule, IChatModule 16 | { 17 | public ChatModule(IQQContext context) : base(context) 18 | { 19 | } 20 | 21 | public Task SendMsg(Message msg, ActionEventListener listener = null) 22 | { 23 | return new SendMsgAction(Context, msg, listener).ExecuteAsyncAuto(); 24 | } 25 | 26 | public Task GetRobotReply(RobotType robotType, string input, ActionEventListener listener = null) 27 | { 28 | throw new NotImplementedException(); 29 | // return new GetTuringRobotReplyAction(Context, input).ExecuteAsyncAuto(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Module/Impl/LoginModule.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huoshan12345/WebQQWeChat/cecfd5deb7843ad5a9b0546ae4f073b9a7cd5413/src/WebQQ/Im/Module/Impl/LoginModule.cs -------------------------------------------------------------------------------- /src/WebQQ/Im/Module/Impl/QQModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Logging; 3 | using WebQQ.Im.Core; 4 | using WebQQ.Im.Module.Interface; 5 | 6 | namespace WebQQ.Im.Module.Impl 7 | { 8 | /// 9 | /// 基础模块 10 | /// 11 | public abstract class QQModule : IQQModule 12 | { 13 | public IQQContext Context { get; set; } 14 | protected ILogger Logger => Context.GetSerivce(); 15 | // protected IConfigurationRoot Config => Context.GetSerivce(); 16 | protected SessionModule Session => Context.GetModule(); 17 | protected StoreModule Store => Context.GetModule(); 18 | 19 | protected QQModule(IQQContext context) 20 | { 21 | Context = context ?? throw new ArgumentNullException(nameof(context)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Module/Impl/SessionModule.cs: -------------------------------------------------------------------------------- 1 | using WebQQ.Im.Bean; 2 | using WebQQ.Im.Core; 3 | 4 | namespace WebQQ.Im.Module.Impl 5 | { 6 | public enum SessionState 7 | { 8 | Offline, 9 | Online, 10 | Logining, 11 | } 12 | 13 | /// 14 | /// QQSession保存了每次登陆时候的状态信息 15 | /// 16 | public class SessionModule : QQModule 17 | { 18 | private volatile SessionState _state; // 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值. 19 | public SessionState State 20 | { 21 | get => _state; 22 | set => _state = value; 23 | } 24 | 25 | public QQUser User { get; set; } = new QQUser(); 26 | 27 | public long ClientId { get; set; } = 53999199; 28 | public string SessionId { get; set; } = ""; 29 | public string Vfwebqq { get; set; } = ""; 30 | public string Ptwebqq { get; set; } = ""; 31 | 32 | //public string LoginSig { get; set; } 33 | //public string CfaceKey { get; set; } 34 | //public string CfaceSig { get; set; } 35 | //public string EmailAuthKey { get; set; } 36 | public int Index { get; set; } 37 | public int Port { get; set; } 38 | //public int PollErrorCnt { get; set; } 39 | //public string CapCd { get; set; } 40 | //public string Psessionid { get; set; } 41 | 42 | 43 | 44 | public string CheckSigUrl { get; set; } 45 | 46 | public SessionModule(IQQContext context) : base(context) 47 | { 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Module/Interface/IChatModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using HttpAction.Event; 6 | using WebQQ.Im.Bean; 7 | using WebQQ.Util; 8 | 9 | namespace WebQQ.Im.Module.Interface 10 | { 11 | public interface IChatModule : IQQModule 12 | { 13 | Task SendMsg(Message msg, ActionEventListener listener = null); 14 | 15 | Task GetRobotReply(RobotType robotType, string input, ActionEventListener listener = null); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Module/Interface/ILoginModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using HttpAction.Event; 6 | 7 | namespace WebQQ.Im.Module.Interface 8 | { 9 | public interface ILoginModule : IQQModule 10 | { 11 | /// 12 | /// 登录,扫码方式 13 | /// 14 | /// 15 | /// 16 | Task Login(ActionEventListener listener = null); 17 | 18 | /// 19 | /// 开始保持在线并检查新消息,即挂微信 20 | /// 21 | void BeginPoll(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Module/Interface/IQQModule.cs: -------------------------------------------------------------------------------- 1 | using WebQQ.Im.Core; 2 | using WebQQ.Im.Service.Interface; 3 | 4 | namespace WebQQ.Im.Module.Interface 5 | { 6 | /// 7 | /// 模块功能接口 8 | /// 9 | public interface IQQModule 10 | { 11 | IQQContext Context { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Service/Impl/QQActionFactory.cs: -------------------------------------------------------------------------------- 1 | using WebQQ.Im.Core; 2 | using WebQQ.Im.Service.Interface; 3 | using System.Reflection; 4 | using HttpAction.Actions; 5 | using WebQQ.Im.Actions; 6 | 7 | namespace WebQQ.Im.Service.Impl 8 | { 9 | public class QQActionFactory : ActionFactory, IQQActionFactory 10 | { 11 | public QQActionFactory(IQQContext context) 12 | { 13 | Context = context; 14 | } 15 | 16 | public IQQContext Context { get;} 17 | 18 | 19 | public override IAction CreateAction(params object[] parameters) 20 | { 21 | var type = typeof(T); 22 | // 把Context作为第一个参数加进去 23 | if (typeof(WebQQAction).GetTypeInfo().IsAssignableFrom(type)) 24 | { 25 | var newArgs = new object[parameters.Length + 1]; 26 | newArgs[0] = Context; 27 | parameters.CopyTo(newArgs, 1); 28 | parameters = newArgs; 29 | } 30 | return base.CreateAction(parameters); 31 | } 32 | 33 | public void Dispose() { } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Service/Impl/QQConsoleLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FclEx.Extensions; 3 | using FclEx.Logger; 4 | using WebQQ.Im.Core; 5 | using WebQQ.Im.Module.Impl; 6 | using WebQQ.Im.Service.Interface; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace WebQQ.Im.Service.Impl 10 | { 11 | public class QQConsoleLogger : SimpleConsoleLogger, IQQService 12 | { 13 | public IQQContext Context { get; } 14 | 15 | public QQConsoleLogger(IQQContext context, LogLevel minLevel = LogLevel.Information) : base("WebQQ", minLevel) 16 | { 17 | Context = context; 18 | } 19 | 20 | protected override string GetMessage(string message, Exception exception) 21 | { 22 | var userName = Context?.GetModule().User?.Uin; 23 | var prefix = userName.IsNullOrDefault() ? string.Empty : $"[{userName}]"; 24 | return $"{DateTime.Now:HH:mm:ss}> {prefix}{message}"; 25 | } 26 | 27 | public void Dispose() 28 | { 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Service/Interface/IQQActionFactory.cs: -------------------------------------------------------------------------------- 1 | using HttpAction.Actions; 2 | 3 | namespace WebQQ.Im.Service.Interface 4 | { 5 | public interface IQQActionFactory : IActionFactory, IQQService 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/WebQQ/Im/Service/Interface/IQQService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using WebQQ.Im.Core; 3 | 4 | namespace WebQQ.Im.Service.Interface 5 | { 6 | /// 7 | /// 就是用来表示该服务是属于一个QQ实例的 8 | /// 从而在QQ实例销毁的时候服务也能一起销毁 9 | /// 如果是全局的服务(即多个实例共享的)请不要继承该接口 10 | /// 11 | public interface IQQService : IDisposable 12 | { 13 | IQQContext Context { get; } 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/WebQQ/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | 3 | namespace WebQQ 4 | { 5 | public class Startup 6 | { 7 | public static IConfigurationRoot BuildConfig() 8 | { 9 | var builder = new ConfigurationBuilder() 10 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); 11 | 12 | if (builder.GetFileProvider().GetFileInfo("project.json")?.Exists == true) 13 | { 14 | builder.AddUserSecrets(); 15 | } 16 | 17 | return builder.Build(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/WebQQ/Util/Extesions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | using AutoMapper; 6 | using FclEx.Extensions; 7 | using ImageSharp; 8 | using Newtonsoft.Json; 9 | using Newtonsoft.Json.Linq; 10 | using WebQQ.Im.Core; 11 | using WebQQ.Im.Event; 12 | using WebQQ.Im.Module.Impl; 13 | 14 | namespace WebQQ.Util 15 | { 16 | public static class Extesions 17 | { 18 | public static Task FireNotifyAsync(this IQQContext context, QQNotifyEventType type, object target = null) 19 | { 20 | return context.FireNotifyAsync(QQNotifyEvent.CreateEvent(type, target)); 21 | } 22 | 23 | public static bool IsOnline(this IQQContext qqClient) 24 | { 25 | var session = qqClient.GetModule(); 26 | return session.State == SessionState.Online; 27 | } 28 | 29 | public static bool IsOffline(this IQQContext qqClient) 30 | { 31 | var session = qqClient.GetModule(); 32 | return session.State == SessionState.Offline; 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/WebQQ/Util/Helpers.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Newtonsoft.Json.Serialization; 6 | 7 | namespace WebQQ.Util 8 | { 9 | public static class Helpers 10 | { 11 | public static JsonSerializerSettings JsonCamelSettings { get; } = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }; 12 | public static JsonSerializer JsonCamelSerializer { get; } = new JsonSerializer { ContractResolver = new CamelCasePropertyNamesContractResolver() }; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/WebQQ/Util/ImageConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ImageSharp; 3 | using Newtonsoft.Json; 4 | using WebQQ.Util; 5 | using FclEx.Extensions; 6 | 7 | namespace WebQQ.Util 8 | { 9 | public class ImageConverter : JsonConverter 10 | { 11 | public override bool CanConvert(Type objectType) 12 | { 13 | return objectType == typeof(ImageSharp.Image); 14 | } 15 | 16 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 17 | { 18 | return ((string) reader.Value).Base64StringToImage(); 19 | } 20 | 21 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 22 | { 23 | var bmp = (ImageSharp.Image)value; 24 | writer.WriteValue(bmp.ToRawBase64String()); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/WebQQ/Util/RetryHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | 5 | namespace WebQQ.Util 6 | { 7 | public static class RetryHelper 8 | { 9 | public static T Retry(Func func, int retryTimes, TimeSpan ts) 10 | { 11 | var exceptions = new List(); 12 | for (var i = 0; i < retryTimes; i++) 13 | { 14 | try 15 | { 16 | return func(); 17 | } 18 | catch (Exception ex) 19 | { 20 | Thread.Sleep(ts); 21 | exceptions.Add(ex); 22 | } 23 | } 24 | throw new AggregateException(exceptions); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/WebQQ/Util/RobotType.cs: -------------------------------------------------------------------------------- 1 | /************************************************************************************ 2 | * 创建人: huoshan12345 3 | * 电子邮箱:89009143@qq.com 4 | * 创建时间:2015/2/26 18:24:01 5 | * 描述: 6 | /************************************************************************************/ 7 | 8 | namespace WebQQ.Util 9 | { 10 | public enum RobotType 11 | { 12 | Moli, 13 | Turing, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/WebQQ/WebQQ.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | WebQQ-20161027025319 6 | 2.0.0-beta1 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/WebQQCore/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": ".NET Core Launch (console)", 6 | "type": "coreclr", 7 | "request": "launch", 8 | "preLaunchTask": "build", 9 | "program": "${workspaceRoot}/bin/Debug/netcoreapp1.0/webqqcore.dll", 10 | "args": [], 11 | "cwd": "${workspaceRoot}", 12 | "stopAtEntry": false, 13 | "externalConsole": true 14 | }, 15 | { 16 | "name": ".NET Core Launch (web)", 17 | "type": "coreclr", 18 | "request": "launch", 19 | "preLaunchTask": "build", 20 | "program": "${workspaceRoot}/bin/Debug//", 21 | "args": [], 22 | "cwd": "${workspaceRoot}", 23 | "stopAtEntry": false, 24 | "launchBrowser": { 25 | "enabled": true, 26 | "args": "${auto-detect-url}", 27 | "windows": { 28 | "command": "cmd.exe", 29 | "args": "/C start ${auto-detect-url}" 30 | }, 31 | "osx": { 32 | "command": "open" 33 | }, 34 | "linux": { 35 | "command": "xdg-open" 36 | } 37 | }, 38 | "env": { 39 | "ASPNETCORE_ENVIRONMENT": "Development" 40 | }, 41 | "sourceFileMap": { 42 | "/Views": "${workspaceRoot}/Views" 43 | } 44 | }, 45 | { 46 | "name": ".NET Core Attach", 47 | "type": "coreclr", 48 | "request": "attach", 49 | "processId": "${command.pickProcess}" 50 | } 51 | ] 52 | } -------------------------------------------------------------------------------- /src/WebQQCore/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "0.1.0", 5 | "command": "dotnet", 6 | "isShellCommand": true, 7 | "args": [], 8 | "tasks": [ 9 | { 10 | "taskName": "build", 11 | "args": [ ], 12 | "isBuildCommand": true, 13 | "showOutput": "silent", 14 | "problemMatcher": "$msCompile" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/AcceptBuddyAddAction.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Core; 2 | using iQQ.Net.WebQQCore.Im.Event; 3 | using iQQ.Net.WebQQCore.Im.Http; 4 | using Newtonsoft.Json.Linq; 5 | 6 | namespace iQQ.Net.WebQQCore.Im.Action 7 | { 8 | /// 9 | /// 接受别人的加好友请求 10 | /// 11 | public class AcceptBuddyAddAction : AbstractHttpAction 12 | { 13 | private readonly string _account; 14 | 15 | public AcceptBuddyAddAction(IQQContext context, QQActionListener listener, string account) 16 | : base(context, listener) 17 | { 18 | this._account = account; 19 | } 20 | 21 | public override QQHttpRequest OnBuildRequest() 22 | { 23 | var session = Context.Session; 24 | var req = CreateHttpRequest("POST", QQConstants.URL_ACCEPET_BUDDY_ADD); 25 | var json = new JObject 26 | { 27 | {"account", _account}, 28 | {"gid", "0"}, 29 | {"mname", ""}, 30 | {"vfwebqq", session.Vfwebqq} 31 | }; 32 | req.AddPostValue("r", json.ToString()); 33 | req.AddHeader("Referer", QQConstants.REFFER); 34 | return req; 35 | } 36 | 37 | public override void OnHttpStatusOK(QQHttpResponse response) 38 | { 39 | var str = response.GetResponseString(); 40 | var json = JObject.Parse(str); 41 | if (json["retcode"].ToString() == "0") 42 | { 43 | NotifyActionEvent(QQActionEventType.EvtOK, json); 44 | } 45 | else 46 | { 47 | throw new QQException(QQErrorCode.UnexpectedResponse, str); 48 | } 49 | } 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/ChangeStatusAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using iQQ.Net.WebQQCore.Im.Bean; 3 | using iQQ.Net.WebQQCore.Im.Core; 4 | using iQQ.Net.WebQQCore.Im.Event; 5 | using iQQ.Net.WebQQCore.Im.Http; 6 | using iQQ.Net.WebQQCore.Util; 7 | using Newtonsoft.Json.Linq; 8 | 9 | namespace iQQ.Net.WebQQCore.Im.Action 10 | { 11 | 12 | /// 13 | /// 改变在线状态 14 | /// 15 | public class ChangeStatusAction : AbstractHttpAction 16 | { 17 | private readonly QQStatus _status; 18 | 19 | public ChangeStatusAction(IQQContext context, QQActionListener listener, QQStatus status) 20 | : base(context, listener) 21 | { 22 | this._status = status; 23 | } 24 | 25 | public override QQHttpRequest OnBuildRequest() 26 | { 27 | var session = this.Context.Session; 28 | 29 | var req = CreateHttpRequest(HttpConstants.Get, 30 | QQConstants.URL_CHANGE_STATUS); 31 | req.AddGetValue("newstatus", _status.Value); 32 | req.AddGetValue("clientid", session.ClientId); 33 | req.AddGetValue("psessionid", session.SessionId); 34 | req.AddGetValue("t", DateTime.Now.CurrentTimeMillis()); 35 | 36 | req.AddHeader("Referer", QQConstants.REFFER); 37 | return req; 38 | } 39 | 40 | public override void OnHttpStatusOK(QQHttpResponse response) 41 | { 42 | var json = JObject.Parse(response.GetResponseString()); 43 | if (json["retcode"].ToString() == "0") 44 | { 45 | Context.Account.Status = _status; 46 | NotifyActionEvent(QQActionEventType.EvtOK, _status); 47 | } 48 | else 49 | { 50 | // NotifyActionEvent(QQActionEventType.EVT_ERROR, new QQException(QQErrorCode.UNEXPECTED_RESPONSE, response.GetResponseString())); 51 | throw new QQException(QQErrorCode.UnexpectedResponse, response.GetResponseString()); 52 | } 53 | } 54 | 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/CheckEmailSig.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Core; 2 | using iQQ.Net.WebQQCore.Im.Event; 3 | using iQQ.Net.WebQQCore.Im.Http; 4 | using iQQ.Net.WebQQCore.Util; 5 | 6 | namespace iQQ.Net.WebQQCore.Im.Action 7 | { 8 | /// 9 | /// 通过pt4获取到的URL进行封装 10 | /// 检测邮箱是否合法登录了 11 | /// 承∮诺 12 | /// 13 | public class CheckEmailSig : AbstractHttpAction 14 | { 15 | private string _url; 16 | public CheckEmailSig(string url, IQQContext context, QQActionListener listener) 17 | : base(context, listener) 18 | { 19 | this._url = url; 20 | } 21 | 22 | public override QQHttpRequest OnBuildRequest() 23 | { 24 | _url += "®master=undefined&aid=1"; 25 | _url += "&s_url=http%3A%2F%2Fmail.qq.com%2Fcgi-bin%2Flogin%3Ffun%3Dpassport%26from%3Dwebqq"; 26 | 27 | var req = CreateHttpRequest(HttpConstants.Get, _url); 28 | return req; 29 | } 30 | 31 | 32 | 33 | public override void OnHttpStatusOK(QQHttpResponse response) 34 | { 35 | NotifyActionEvent(QQActionEventType.EvtOK, ""); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/CheckLoginSigAction.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Core; 2 | using iQQ.Net.WebQQCore.Im.Event; 3 | using iQQ.Net.WebQQCore.Im.Http; 4 | using iQQ.Net.WebQQCore.Util; 5 | 6 | namespace iQQ.Net.WebQQCore.Im.Action 7 | { 8 | /// 9 | /// CheckLoginSigAction 10 | /// @author solosky 11 | /// 12 | public class CheckLoginSigAction : AbstractHttpAction 13 | { 14 | private readonly string _checkSigUrl; 15 | 16 | public CheckLoginSigAction(IQQContext context, QQActionListener listener, string checkSigUrl) 17 | : base(context, listener) 18 | { 19 | _checkSigUrl = checkSigUrl; 20 | } 21 | 22 | public override QQHttpRequest OnBuildRequest() 23 | { 24 | return CreateHttpRequest(HttpConstants.Get, _checkSigUrl); 25 | } 26 | 27 | public override void OnHttpStatusOK(QQHttpResponse response) 28 | { 29 | // LOG.info("checkSig result:" + response.GetResponseString()); 30 | // LOG.info("Location:" + response.getHeader("Location")); 31 | NotifyActionEvent(QQActionEventType.EvtOK, null); 32 | } 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetCaptchaImageAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.IO; 4 | using iQQ.Net.WebQQCore.Im.Core; 5 | using iQQ.Net.WebQQCore.Im.Event; 6 | using iQQ.Net.WebQQCore.Im.Http; 7 | using iQQ.Net.WebQQCore.Util; 8 | 9 | namespace iQQ.Net.WebQQCore.Im.Action 10 | { 11 | /// 12 | /// 获取验证码图片 13 | /// @author solosky 14 | /// 15 | public class GetCaptchaImageAction : AbstractHttpAction 16 | { 17 | private readonly long _uin; 18 | 19 | public GetCaptchaImageAction(IQQContext context, QQActionListener listener, long uin) 20 | : base(context, listener) 21 | { 22 | _uin = uin; 23 | } 24 | 25 | public override void OnHttpStatusOK(QQHttpResponse response) 26 | { 27 | NotifyActionEvent(QQActionEventType.EvtOK, Image.FromStream(response.ResponseStream)); 28 | } 29 | 30 | public override QQHttpRequest OnBuildRequest() 31 | { 32 | var req = CreateHttpRequest(HttpConstants.Get, QQConstants.URL_GET_CAPTCHA); 33 | req.AddGetValue("aid", QQConstants.APPID); 34 | req.AddGetValue("r", new Random().NextDouble().ToString("f16")); 35 | req.AddGetValue("uin", _uin); 36 | 37 | // 20150724增加 38 | req.AddGetValue("cap_cd", Context.Session.CapCd); 39 | req.ResultType = ResponseResultType.Stream; 40 | return req; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetCustomFaceSigAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using iQQ.Net.WebQQCore.Im.Core; 3 | using iQQ.Net.WebQQCore.Im.Event; 4 | using iQQ.Net.WebQQCore.Im.Http; 5 | using iQQ.Net.WebQQCore.Util; 6 | using Newtonsoft.Json.Linq; 7 | 8 | namespace iQQ.Net.WebQQCore.Im.Action 9 | { 10 | /// 11 | /// 获取群图片 key and sig 12 | /// @author ChenZhiHui 13 | /// @since 2013-2-23 14 | /// 15 | public class GetCustomFaceSigAction : AbstractHttpAction 16 | { 17 | public GetCustomFaceSigAction(IQQContext context, QQActionListener listener) : base(context, listener) { } 18 | 19 | public override QQHttpRequest OnBuildRequest() 20 | { 21 | var session = Context.Session; 22 | 23 | var req = CreateHttpRequest(HttpConstants.Get, 24 | QQConstants.URL_CUSTOM_FACE_SIG); 25 | req.AddGetValue("clientid", session.ClientId); 26 | req.AddGetValue("psessionid", session.SessionId); 27 | req.AddGetValue("t", DateTime.Now.CurrentTimeMillis()); 28 | 29 | req.AddHeader("Referer", QQConstants.REFFER); 30 | return req; 31 | } 32 | 33 | public override void OnHttpStatusOK(QQHttpResponse response) 34 | { 35 | var session = Context.Session; 36 | 37 | var json = JObject.Parse(response.GetResponseString()); 38 | if (json["retcode"].ToString() == "0") 39 | { 40 | var obj = json["result"].ToObject(); 41 | session.CfaceKey = obj["gface_key"].ToString(); 42 | session.CfaceSig = obj["gface_sig"].ToString(); 43 | NotifyActionEvent(QQActionEventType.EvtOK, session); 44 | } 45 | else 46 | { 47 | NotifyActionEvent(QQActionEventType.EvtError, 48 | new QQException(QQErrorCode.UnexpectedResponse, response.GetResponseString())); 49 | } 50 | } 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetFriendAccoutAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using iQQ.Net.WebQQCore.Im.Bean; 3 | using iQQ.Net.WebQQCore.Im.Core; 4 | using iQQ.Net.WebQQCore.Im.Event; 5 | using iQQ.Net.WebQQCore.Im.Http; 6 | using iQQ.Net.WebQQCore.Util; 7 | using Newtonsoft.Json.Linq; 8 | 9 | namespace iQQ.Net.WebQQCore.Im.Action 10 | { 11 | /// 12 | /// 获取QQ账号 13 | /// @author ChenZhiHui 14 | /// @since 2013-2-23 15 | /// 16 | public class GetFriendAccoutAction : AbstractHttpAction 17 | { 18 | 19 | private QQUser buddy; 20 | 21 | public GetFriendAccoutAction(IQQContext context, QQActionListener listener, QQUser buddy) 22 | : base(context, listener) 23 | { 24 | 25 | this.buddy = buddy; 26 | } 27 | 28 | public override QQHttpRequest OnBuildRequest() 29 | { 30 | var session = Context.Session; 31 | // tuin=4245757755&verifysession=&type=1&code=&vfwebqq=**&t=1361631644492 32 | var req = CreateHttpRequest(HttpConstants.Get, 33 | QQConstants.URL_GET_USER_ACCOUNT); 34 | req.AddGetValue("tuin", buddy.Uin); 35 | req.AddGetValue("vfwebqq", session.Vfwebqq); 36 | req.AddGetValue("t", DateTime.Now.CurrentTimeMillis()); 37 | req.AddGetValue("verifysession", ""); // 验证码?? 38 | req.AddGetValue("type", 1); 39 | req.AddGetValue("code", ""); 40 | 41 | req.AddHeader("Referer", QQConstants.REFFER); 42 | return req; 43 | } 44 | 45 | public override void OnHttpStatusOK(QQHttpResponse response) 46 | { 47 | var json = JObject.Parse(response.GetResponseString()); 48 | if (json["retcode"].ToString() == "0") 49 | { 50 | var obj = json["result"].ToObject(); 51 | buddy.QQ = obj["account"].ToObject(); 52 | } 53 | 54 | NotifyActionEvent(QQActionEventType.EvtOK, buddy); 55 | } 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetFriendFaceAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.IO; 4 | using iQQ.Net.WebQQCore.Im.Bean; 5 | using iQQ.Net.WebQQCore.Im.Core; 6 | using iQQ.Net.WebQQCore.Im.Event; 7 | using iQQ.Net.WebQQCore.Im.Http; 8 | using iQQ.Net.WebQQCore.Util; 9 | 10 | namespace iQQ.Net.WebQQCore.Im.Action 11 | { 12 | /// 13 | /// 获取用户头像 14 | /// @author ChenZhiHui 15 | /// @since 2013-2-23 16 | /// 17 | public class GetFriendFaceAction : AbstractHttpAction 18 | { 19 | 20 | private readonly QQUser _user; 21 | 22 | public GetFriendFaceAction(IQQContext context, QQActionListener listener,QQUser user) 23 | : base(context, listener) 24 | { 25 | this._user = user; 26 | } 27 | 28 | public override QQHttpRequest OnBuildRequest() 29 | { 30 | var session = Context.Session; 31 | var req = CreateHttpRequest(HttpConstants.Get, QQConstants.URL_GET_USER_FACE); 32 | req.AddGetValue("uin", _user.Uin); 33 | req.AddGetValue("vfwebqq", session.Vfwebqq); 34 | req.AddGetValue("t", DateTime.Now.CurrentTimeMillis()); 35 | req.AddGetValue("cache", 0); // ?? 36 | req.AddGetValue("type", 1); // ?? 37 | req.AddGetValue("fid", 0); // ?? 38 | req.AddHeader("Referer", QQConstants.REFFER); 39 | req.ResultType = ResponseResultType.Stream; 40 | return req; 41 | } 42 | 43 | public override void OnHttpStatusOK(QQHttpResponse response) 44 | { 45 | var image = Image.FromStream(response.ResponseStream); 46 | _user.Face = image; 47 | NotifyActionEvent(QQActionEventType.EvtOK, _user); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetFriendSignAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using iQQ.Net.WebQQCore.Im.Bean; 3 | using iQQ.Net.WebQQCore.Im.Core; 4 | using iQQ.Net.WebQQCore.Im.Event; 5 | using iQQ.Net.WebQQCore.Im.Http; 6 | using iQQ.Net.WebQQCore.Util; 7 | using Newtonsoft.Json.Linq; 8 | 9 | namespace iQQ.Net.WebQQCore.Im.Action 10 | { 11 | /// 12 | /// 个人签名 13 | /// @author ChenZhiHui 14 | /// @since 2013-2-23 15 | /// 16 | public class GetFriendSignAction : AbstractHttpAction 17 | { 18 | 19 | private readonly QQUser _buddy; 20 | 21 | public GetFriendSignAction(IQQContext context, QQActionListener listener,QQUser buddy) 22 | : base(context, listener) 23 | { 24 | this._buddy = buddy; 25 | } 26 | 27 | public override QQHttpRequest OnBuildRequest() 28 | { 29 | var session = Context.Session; 30 | 31 | var req = CreateHttpRequest(HttpConstants.Get, 32 | QQConstants.URL_GET_USER_SIGN); 33 | req.AddGetValue("tuin", _buddy.Uin); 34 | req.AddGetValue("vfwebqq", session.Vfwebqq); 35 | req.AddGetValue("t", DateTime.Now.CurrentTimeMillis()); 36 | 37 | req.AddHeader("Referer", QQConstants.REFFER); 38 | return req; 39 | } 40 | 41 | public override void OnHttpStatusOK(QQHttpResponse response) 42 | { 43 | var json = JObject.Parse(response.GetResponseString()); 44 | if (json["retcode"].ToString() == "0") 45 | { 46 | var result = json["result"].ToObject(); 47 | var obj = result[0].ToObject(); 48 | _buddy.Sign = obj["lnick"].ToString(); 49 | } 50 | 51 | NotifyActionEvent(QQActionEventType.EvtOK, _buddy); 52 | } 53 | 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetGroupAccoutAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using iQQ.Net.WebQQCore.Im.Bean; 3 | using iQQ.Net.WebQQCore.Im.Core; 4 | using iQQ.Net.WebQQCore.Im.Event; 5 | using iQQ.Net.WebQQCore.Im.Http; 6 | using iQQ.Net.WebQQCore.Util; 7 | using Newtonsoft.Json.Linq; 8 | 9 | namespace iQQ.Net.WebQQCore.Im.Action 10 | { 11 | /// 12 | /// 获取QQ账号 13 | /// @author ChenZhiHui 14 | /// @since 2013-2-23 15 | /// 16 | public class GetGroupAccoutAction : AbstractHttpAction 17 | { 18 | 19 | private readonly QQGroup _group; 20 | 21 | public GetGroupAccoutAction(IQQContext context, QQActionListener listener, QQGroup group) 22 | : base(context, listener) 23 | { 24 | this._group = group; 25 | } 26 | 27 | public override QQHttpRequest OnBuildRequest() 28 | { 29 | var session = Context.Session; 30 | // tuin=4245757755&verifysession=&type=1&code=&vfwebqq=**&t=1361631644492 31 | var req = CreateHttpRequest(HttpConstants.Get, 32 | QQConstants.URL_GET_USER_ACCOUNT); 33 | req.AddGetValue("tuin", _group.Code); 34 | req.AddGetValue("vfwebqq", session.Vfwebqq); 35 | req.AddGetValue("t", DateTime.Now.CurrentTimeMillis()); 36 | req.AddGetValue("verifysession", ""); // 验证码?? 37 | req.AddGetValue("type", 4); 38 | req.AddGetValue("code", ""); 39 | 40 | req.AddHeader("Referer", QQConstants.REFFER); 41 | return req; 42 | } 43 | 44 | public override void OnHttpStatusOK(QQHttpResponse response) 45 | { 46 | var json = JObject.Parse(response.GetResponseString()); 47 | if (json["retcode"].ToString() == "0") 48 | { 49 | var obj = json["result"].ToObject(); 50 | _group.Gid = obj["account"].ToObject(); 51 | } 52 | 53 | NotifyActionEvent(QQActionEventType.EvtOK, _group); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetGroupFaceAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.IO; 4 | using iQQ.Net.WebQQCore.Im.Bean; 5 | using iQQ.Net.WebQQCore.Im.Core; 6 | using iQQ.Net.WebQQCore.Im.Event; 7 | using iQQ.Net.WebQQCore.Im.Http; 8 | using iQQ.Net.WebQQCore.Util; 9 | 10 | namespace iQQ.Net.WebQQCore.Im.Action 11 | { 12 | /// 13 | /// 个人签名 14 | /// @author ChenZhiHui 15 | /// @since 2013-2-23 16 | /// 17 | public class GetGroupFaceAction : AbstractHttpAction 18 | { 19 | 20 | private readonly QQGroup _group; 21 | 22 | public GetGroupFaceAction(IQQContext context, QQActionListener listener, 23 | QQGroup group) 24 | : base(context, listener) 25 | { 26 | 27 | this._group = group; 28 | } 29 | 30 | public override QQHttpRequest OnBuildRequest() 31 | { 32 | var session = Context.Session; 33 | var req = CreateHttpRequest(HttpConstants.Get, 34 | QQConstants.URL_GET_USER_FACE); 35 | req.AddGetValue("uin", _group.Code); 36 | req.AddGetValue("vfwebqq", session.Vfwebqq); 37 | req.AddGetValue("t", DateTime.Now.CurrentTimeMillis()); 38 | req.AddGetValue("cache", "0"); 39 | req.AddGetValue("type", "4"); 40 | req.AddGetValue("fid", "0"); 41 | req.ResultType = ResponseResultType.Stream; 42 | return req; 43 | } 44 | 45 | public override void OnHttpStatusOK(QQHttpResponse response) 46 | { 47 | try 48 | { 49 | var image = Image.FromStream(response.ResponseStream); 50 | _group.Face = image; 51 | } 52 | catch (IOException e) 53 | { 54 | throw new QQException(QQErrorCode.IOError, e); 55 | } 56 | NotifyActionEvent(QQActionEventType.EvtOK, _group); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetLoginSigAction.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using iQQ.Net.WebQQCore.Im.Core; 3 | using iQQ.Net.WebQQCore.Im.Event; 4 | using iQQ.Net.WebQQCore.Im.Http; 5 | using iQQ.Net.WebQQCore.Util; 6 | 7 | namespace iQQ.Net.WebQQCore.Im.Action 8 | { 9 | /// 10 | /// 获取从登陆页面获取LoginSig 11 | /// @author solosky 12 | /// 接口更新 2013-08-03 13 | /// 14 | public class GetLoginSigAction : AbstractHttpAction 15 | { 16 | public GetLoginSigAction(IQQContext context, QQActionListener listener) : base(context, listener) { } 17 | 18 | public override QQHttpRequest OnBuildRequest() 19 | { 20 | return CreateHttpRequest(HttpConstants.Get, QQConstants.URL_LOGIN_PAGE); 21 | } 22 | 23 | public override void OnHttpStatusOK(QQHttpResponse response) 24 | { 25 | var rex = new Regex(QQConstants.REGXP_LOGIN_SIG); 26 | var mc = rex.Match(response.GetResponseString()); 27 | 28 | if (mc.Success) 29 | { 30 | var session = Context.Session; 31 | session.LoginSig = mc.Groups[1].Value; 32 | 33 | NotifyActionEvent(QQActionEventType.EvtOK, session.LoginSig); 34 | } 35 | else 36 | { 37 | NotifyActionEvent(QQActionEventType.EvtError, new QQException(QQErrorCode.InvalidResponse, "Login Sig Not Found!!")); 38 | } 39 | } 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetOffPicAction.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using iQQ.Net.WebQQCore.Im.Bean; 3 | using iQQ.Net.WebQQCore.Im.Bean.Content; 4 | using iQQ.Net.WebQQCore.Im.Core; 5 | using iQQ.Net.WebQQCore.Im.Event; 6 | using iQQ.Net.WebQQCore.Im.Http; 7 | using iQQ.Net.WebQQCore.Util; 8 | 9 | namespace iQQ.Net.WebQQCore.Im.Action 10 | { 11 | /// 12 | /// 获取聊天图片 13 | /// @author solosky 14 | /// 15 | public class GetOffPicAction : AbstractHttpAction 16 | { 17 | private readonly OffPicItem _offpic; 18 | private readonly QQMsg _msg; 19 | private readonly Stream _picOut; 20 | 21 | public GetOffPicAction(IQQContext context, QQActionListener listener, 22 | OffPicItem offpic, QQMsg msg, Stream picOut) 23 | : base(context, listener) 24 | { 25 | 26 | _offpic = offpic; 27 | _msg = msg; 28 | _picOut = picOut; 29 | } 30 | 31 | public override void OnHttpStatusOK(QQHttpResponse response) 32 | { 33 | NotifyActionEvent(QQActionEventType.EvtOK, _offpic); 34 | } 35 | 36 | public override QQHttpRequest OnBuildRequest() 37 | { 38 | var req = CreateHttpRequest(HttpConstants.Get, QQConstants.URL_GET_OFFPIC); 39 | var session = Context.Session; 40 | req.AddGetValue("clientid", session.ClientId); 41 | req.AddGetValue("f_uin", _msg.From.Uin); 42 | req.AddGetValue("file_path", _offpic.FilePath); 43 | req.AddGetValue("psessionid", session.SessionId); 44 | req.OutputStream = _picOut; 45 | return req; 46 | } 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetPT4Auth.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using iQQ.Net.WebQQCore.Im.Core; 3 | using iQQ.Net.WebQQCore.Im.Event; 4 | using iQQ.Net.WebQQCore.Im.Http; 5 | using iQQ.Net.WebQQCore.Im.Service; 6 | using iQQ.Net.WebQQCore.Util; 7 | 8 | namespace iQQ.Net.WebQQCore.Im.Action 9 | { 10 | /// 11 | /// pt4登录验证,获取到一个链接 12 | /// @author 承∮诺 13 | /// @since 2014年1月24日 14 | /// 15 | public class GetPT4Auth : AbstractHttpAction 16 | { 17 | public GetPT4Auth(IQQContext context, QQActionListener listener) : base(context, listener) { } 18 | 19 | public override QQHttpRequest OnBuildRequest() 20 | { 21 | var httpService = Context.GetSerivce(QQServiceType.HTTP); 22 | var req = CreateHttpRequest(HttpConstants.Get, QQConstants.URL_PT4_AUTH); 23 | req.AddGetValue("daid", "4"); 24 | req.AddGetValue("appid", "1"); 25 | req.AddGetValue("auth_token", QQEncryptor.Time33(httpService.GetCookie("supertoken", 26 | QQConstants.URL_CHANNEL_LOGIN).Value)); 27 | return req; 28 | } 29 | 30 | public override void OnHttpStatusOK(QQHttpResponse response) 31 | { 32 | var rex = new Regex(QQConstants.REGXP_EMAIL_AUTH); 33 | var m = rex.Match(response.GetResponseString()); 34 | 35 | if (m.Success) 36 | { 37 | var qqHex = m.Groups[2].Value; 38 | NotifyActionEvent(QQActionEventType.EvtOK, qqHex); 39 | } 40 | else 41 | { 42 | NotifyActionEvent(QQActionEventType.EvtError, 43 | new QQException(QQErrorCode.InvalidLoginAuth)); 44 | } 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetQRCodeAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using iQQ.Net.WebQQCore.Im.Core; 4 | using iQQ.Net.WebQQCore.Im.Event; 5 | using iQQ.Net.WebQQCore.Im.Http; 6 | using iQQ.Net.WebQQCore.Util; 7 | 8 | namespace iQQ.Net.WebQQCore.Im.Action 9 | { 10 | public class GetQRCodeAction : AbstractHttpAction 11 | { 12 | public GetQRCodeAction(IQQContext context, QQActionListener listener) : base(context, listener) { } 13 | 14 | 15 | public override QQHttpRequest OnBuildRequest() 16 | { 17 | var req = CreateHttpRequest(HttpConstants.Get, QQConstants.URL_GET_QRCODE); 18 | req.AddGetValue("appid", QQConstants.APPID); 19 | req.AddGetValue("e", "0"); 20 | req.AddGetValue("l", "M"); 21 | req.AddGetValue("s", "5"); 22 | req.AddGetValue("d", "72"); 23 | req.AddGetValue("v", "4"); 24 | req.AddGetValue("t", new Random().NextDouble()); 25 | //req.AddRefer(QQConstants.URL_LOGIN_PAGE); 26 | //req.AddHeader(HttpConstants.SetCookie, "qrsig=dG0lVGD8IhpDl1cMsy4qgghLk24rOwSK9YVq2YlWAjBzJ69tIE-9sFkMttULkrww; PATH=/; DOMAIN=ptlogin2.qq.com;"); 27 | req.ResultType = ResponseResultType.Stream; 28 | return req; 29 | } 30 | 31 | public override void OnHttpStatusOK(QQHttpResponse response) 32 | { 33 | NotifyActionEvent(QQActionEventType.EvtOK, Image.FromStream(response.ResponseStream)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetVFWebqq.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using iQQ.Net.WebQQCore.Im.Core; 3 | using iQQ.Net.WebQQCore.Im.Event; 4 | using iQQ.Net.WebQQCore.Im.Http; 5 | using iQQ.Net.WebQQCore.Im.Service; 6 | using iQQ.Net.WebQQCore.Util; 7 | using Newtonsoft.Json.Linq; 8 | 9 | namespace iQQ.Net.WebQQCore.Im.Action 10 | { 11 | public class GetVFWebqq : AbstractHttpAction 12 | { 13 | public GetVFWebqq(IQQContext context, QQActionListener listener) : base(context, listener) 14 | { 15 | } 16 | 17 | public override QQHttpRequest BuildRequest() 18 | { 19 | var httpService = Context.GetSerivce(QQServiceType.HTTP); 20 | var session = Context.Session; 21 | var request = CreateHttpRequest(HttpConstants.Get, QQConstants.URL_GET_VFWEBQQ); 22 | request.AddGetValue("ptwebqq", httpService.GetCookie("ptwebqq", QQConstants.URL_CHANNEL_LOGIN).Value); 23 | request.AddGetValue("clientid", session.ClientId); 24 | request.AddGetValue("psessionid", ""); 25 | request.AddGetValue("t", DateTime.Now.CurrentTimeMillis()); 26 | return request; 27 | } 28 | 29 | 30 | public override void OnHttpStatusOK(QQHttpResponse response) 31 | { 32 | var str = response.GetResponseString(); 33 | var json = JObject.Parse(str); 34 | if (json["retcode"].ToString() == "0") 35 | { 36 | var ret = json["result"].ToObject(); 37 | Context.Session.Vfwebqq = ret["vfwebqq"].ToString(); 38 | NotifyActionEvent(QQActionEventType.EvtOK, null); 39 | } 40 | else 41 | { 42 | // NotifyActionEvent(QQActionEventType.EVT_ERROR, new QQException(QQErrorCode.INVALID_RESPONSE)); //TODO .. 43 | throw new QQException(QQErrorCode.InvalidResponse, str); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetWPKeyAction.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Core; 2 | using iQQ.Net.WebQQCore.Im.Event; 3 | using iQQ.Net.WebQQCore.Im.Http; 4 | using iQQ.Net.WebQQCore.Util; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace iQQ.Net.WebQQCore.Im.Action 8 | { 9 | /// 10 | /// 通过登录得到的sid,获取到wpkey 11 | /// @author 承∮诺 12 | /// @since 2014年1月25日 13 | /// 14 | public class GetWPKeyAction : AbstractHttpAction 15 | { 16 | private readonly string _sid; 17 | 18 | public GetWPKeyAction(string sid, IQQContext context, QQActionListener listener) 19 | : base(context, listener) 20 | { 21 | 22 | _sid = sid; 23 | } 24 | 25 | public override QQHttpRequest OnBuildRequest() 26 | { 27 | var req = CreateHttpRequest(HttpConstants.Get, QQConstants.URL_GET_WP_KEY); 28 | req.AddGetValue("r", "0.7975904128979892"); 29 | req.AddGetValue("resp_charset", "UTF8"); 30 | req.AddGetValue("ef", "js"); 31 | req.AddGetValue("sid", _sid); 32 | req.AddGetValue("Referer", "http://mail.qq.com/cgi-bin/frame_html?sid=" + _sid); 33 | return req; 34 | } 35 | 36 | public override void OnHttpStatusOK(QQHttpResponse response) 37 | { 38 | var resp = response.GetResponseString(); 39 | resp = resp.Substring(1, resp.Length - 1); 40 | var json = JObject.Parse(resp); 41 | if (json["k"] != null) 42 | { 43 | NotifyActionEvent(QQActionEventType.EvtOK, json["k"].ToString()); 44 | } 45 | else 46 | { 47 | NotifyActionEvent(QQActionEventType.EvtError, QQErrorCode.UnexpectedResponse); 48 | } 49 | // System.out.println("GetWPKeyAction: " + response.GetResponseString()); 50 | } 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/GetWebqq.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using iQQ.Net.WebQQCore.Im.Core; 7 | using iQQ.Net.WebQQCore.Im.Http; 8 | using iQQ.Net.WebQQCore.Util; 9 | 10 | namespace iQQ.Net.WebQQCore.Im.Action 11 | { 12 | public class GetWebqq : AbstractHttpAction 13 | { 14 | const string url = "http://wspeed.qq.com/w.cgi"; 15 | 16 | /** 17 | *

Constructor for AbstractHttpAction.

18 | * 19 | * @param context a {@link IQQContext} object. 20 | * @param listener a {@link QQActionListener} object. 21 | */ 22 | public GetWebqq(IQQContext context, QQActionListener listener) : base(context, listener) 23 | { 24 | 25 | } 26 | 27 | public override QQHttpRequest BuildRequest() 28 | { 29 | var request = CreateHttpRequest(HttpConstants.Get, url); 30 | request.AddGetValue("appid", "1000164"); 31 | request.AddGetValue("touin", "null"); 32 | request.AddGetValue("releaseversion", "SMARTQQ"); 33 | request.AddGetValue("frequency", "1"); 34 | request.AddGetValue("commandid", "http://s.web2.qq.com/api/getvfwebqq"); 35 | request.AddGetValue("resultcode", "0"); 36 | request.AddGetValue("tmcost", "160"); 37 | return request; 38 | } 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/LoginEmailAction.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using iQQ.Net.WebQQCore.Im.Core; 3 | using iQQ.Net.WebQQCore.Im.Event; 4 | using iQQ.Net.WebQQCore.Im.Http; 5 | using iQQ.Net.WebQQCore.Util; 6 | 7 | namespace iQQ.Net.WebQQCore.Im.Action 8 | { 9 | /// 10 | /// 登录邮箱 11 | /// @author 承∮诺 12 | /// @since 2014年1月25日 13 | /// 14 | public class LoginEmailAction : AbstractHttpAction 15 | { 16 | public LoginEmailAction(IQQContext context, QQActionListener listener) : base(context, listener) { } 17 | 18 | public override QQHttpRequest OnBuildRequest() 19 | { 20 | var req = CreateHttpRequest(HttpConstants.Get, QQConstants.URL_LOGIN_EMAIL); 21 | req.AddGetValue("fun", "passport"); 22 | req.AddGetValue("from", "webqq"); 23 | req.AddGetValue("Referer", "https://mail.qq.com/cgi-bin/loginpage"); 24 | return req; 25 | } 26 | 27 | public override void OnHttpStatusOK(QQHttpResponse response) 28 | { 29 | var REGXP = "sid=(.*?)\""; 30 | var rex = new Regex(REGXP); 31 | var m = rex.Match(response.GetResponseString()); 32 | 33 | if (m.Success) 34 | { 35 | var sid = m.Groups[1].Value; 36 | NotifyActionEvent(QQActionEventType.EvtOK, sid); 37 | } 38 | else 39 | { 40 | NotifyActionEvent(QQActionEventType.EvtError, QQErrorCode.UnexpectedResponse); 41 | } 42 | } 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/MarkEmailAction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using iQQ.Net.WebQQCore.Im.Bean; 3 | using iQQ.Net.WebQQCore.Im.Core; 4 | using iQQ.Net.WebQQCore.Im.Event; 5 | using iQQ.Net.WebQQCore.Im.Http; 6 | 7 | namespace iQQ.Net.WebQQCore.Im.Action 8 | { 9 | 10 | public class MarkEmailAction : AbstractHttpAction 11 | { 12 | 13 | private bool status; 14 | private List markList; 15 | 16 | public MarkEmailAction(bool status, List markList, 17 | IQQContext context, QQActionListener listener) 18 | : base(context, listener) 19 | { 20 | this.status = status; 21 | this.markList = markList; 22 | } 23 | 24 | public override QQHttpRequest BuildRequest() 25 | { 26 | var req = CreateHttpRequest("POST", QQConstants.URL_MARK_EMAIL); 27 | req.AddPostValue("mailaction", "mail_flag"); 28 | req.AddPostValue("flag", "new"); 29 | req.AddPostValue("resp_charset", "UTF8"); 30 | req.AddPostValue("ef", "js"); 31 | req.AddPostValue("folderkey", "1"); 32 | req.AddPostValue("sid", Context.Session.EmailAuthKey); 33 | req.AddPostValue("status", status); 34 | foreach (var mail in markList) 35 | { 36 | req.AddPostValue("mailid", mail.Id); 37 | } 38 | return req; 39 | } 40 | 41 | public override void OnHttpStatusOK(QQHttpResponse response) 42 | { 43 | // ({msg : "new successful",rbkey : "1391255617",status : "false"}) 44 | var ct = response.GetResponseString(); 45 | if (ct.Contains("success")) 46 | { 47 | NotifyActionEvent(QQActionEventType.EvtOK, ct); 48 | } 49 | else 50 | { 51 | NotifyActionEvent(QQActionEventType.EvtError, ct); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/SendInputNotifyAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using iQQ.Net.WebQQCore.Im.Bean; 3 | using iQQ.Net.WebQQCore.Im.Core; 4 | using iQQ.Net.WebQQCore.Im.Event; 5 | using iQQ.Net.WebQQCore.Im.Http; 6 | using iQQ.Net.WebQQCore.Util; 7 | using Newtonsoft.Json.Linq; 8 | 9 | namespace iQQ.Net.WebQQCore.Im.Action 10 | { 11 | /// 12 | /// 发送正在输入状态通知 13 | /// @author solosky 14 | /// 15 | public class SendInputNotifyAction : AbstractHttpAction 16 | { 17 | private QQUser user; 18 | 19 | public SendInputNotifyAction(IQQContext context, QQActionListener listener, QQUser user) 20 | : base(context, listener) 21 | { 22 | 23 | this.user = user; 24 | } 25 | 26 | public override void OnHttpStatusOK(QQHttpResponse response) 27 | { 28 | var json = JObject.Parse(response.GetResponseString()); 29 | if (json["retcode"].ToString() == "0") 30 | { 31 | NotifyActionEvent(QQActionEventType.EvtOK, user); 32 | } 33 | else 34 | { 35 | NotifyActionEvent(QQActionEventType.EvtError, 36 | new QQException(QQErrorCode.UnexpectedResponse, response.GetResponseString())); 37 | } 38 | } 39 | 40 | public override QQHttpRequest OnBuildRequest() 41 | { 42 | var req = CreateHttpRequest(HttpConstants.Get, QQConstants.URL_SEND_INPUT_NOTIFY); 43 | var session = Context.Session; 44 | req.AddGetValue("clientid", session.ClientId); 45 | req.AddGetValue("to_uin", user.Uin); 46 | req.AddGetValue("t", DateTime.Now.CurrentTimeMillis()); 47 | req.AddGetValue("psessionid", session.SessionId); 48 | return req; 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/ShakeWindowAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using iQQ.Net.WebQQCore.Im.Bean; 3 | using iQQ.Net.WebQQCore.Im.Core; 4 | using iQQ.Net.WebQQCore.Im.Event; 5 | using iQQ.Net.WebQQCore.Im.Http; 6 | using iQQ.Net.WebQQCore.Util; 7 | using Newtonsoft.Json.Linq; 8 | 9 | namespace iQQ.Net.WebQQCore.Im.Action 10 | { 11 | /// 12 | /// 震动聊天窗口 13 | /// @author solosky 14 | /// 15 | public class ShakeWindowAction : AbstractHttpAction 16 | { 17 | private readonly QQUser _user; 18 | 19 | public ShakeWindowAction(IQQContext context, QQActionListener listener, QQUser user) 20 | : base(context, listener) 21 | { 22 | 23 | _user = user; 24 | } 25 | 26 | public override QQHttpRequest OnBuildRequest() 27 | { 28 | var session = Context.Session; 29 | var req = CreateHttpRequest(HttpConstants.Get, QQConstants.URL_SHAKE_WINDOW); 30 | req.AddGetValue("to_uin", _user.Uin); 31 | req.AddGetValue("psessionid", session.SessionId); 32 | req.AddGetValue("clientid", session.ClientId); 33 | req.AddGetValue("t", DateTime.Now.CurrentTimeMillis()); 34 | 35 | req.AddHeader("Referer", QQConstants.REFFER); 36 | return req; 37 | } 38 | 39 | public override void OnHttpStatusOK(QQHttpResponse response) 40 | { 41 | var json = JObject.Parse(response.GetResponseString()); 42 | if (json["retcode"].ToString() == "0") 43 | { 44 | NotifyActionEvent(QQActionEventType.EvtOK, _user); 45 | } 46 | else 47 | { 48 | NotifyActionEvent(QQActionEventType.EvtError, new QQException(QQErrorCode.UnexpectedResponse)); 49 | } 50 | 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Action/WebLogoutAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using iQQ.Net.WebQQCore.Im.Core; 3 | using iQQ.Net.WebQQCore.Im.Event; 4 | using iQQ.Net.WebQQCore.Im.Http; 5 | using iQQ.Net.WebQQCore.Util; 6 | using Newtonsoft.Json.Linq; 7 | 8 | namespace iQQ.Net.WebQQCore.Im.Action 9 | { 10 | /// 11 | /// 登录退出 12 | /// @author ChenZhiHui 13 | /// @since 2013-2-23 14 | /// 15 | public class WebLogoutAction : AbstractHttpAction 16 | { 17 | public WebLogoutAction(IQQContext context, QQActionListener listener) : base(context, listener) { } 18 | 19 | public override QQHttpRequest OnBuildRequest() 20 | { 21 | var session = Context.Session; 22 | 23 | var req = CreateHttpRequest(HttpConstants.Get, QQConstants.URL_LOGOUT); 24 | req.AddGetValue("ids", ""); // 产生过会话才出现ID,如何获取?? 25 | req.AddGetValue("clientid", session.ClientId); 26 | req.AddGetValue("psessionid", session.SessionId); 27 | req.AddGetValue("t", DateTime.Now.CurrentTimeMillis()); 28 | 29 | req.AddHeader("Referer", QQConstants.REFFER); 30 | return req; 31 | } 32 | 33 | public override void OnHttpStatusOK(QQHttpResponse response) 34 | { 35 | var json = JObject.Parse(response.GetResponseString()); 36 | var isOK = json["result"].ToString().ToLower(); 37 | if (json["retcode"].ToString() == "0") 38 | { 39 | if (isOK.Equals("ok")) 40 | { 41 | NotifyActionEvent(QQActionEventType.EvtOK, isOK); 42 | return; 43 | } 44 | } 45 | 46 | NotifyActionEvent(QQActionEventType.EvtError, isOK); 47 | } 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Actor/ExitActor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace iQQ.Net.WebQQCore.Im.Actor 8 | { 9 | /// 10 | /// 一个伪Actor只是为了让ActorLoop停下来 11 | /// 12 | public class ExitActor : IQQActor 13 | { 14 | public void Execute() 15 | { 16 | //do nothing 17 | } 18 | 19 | public QQActorType Type => QQActorType.SimpleActor; 20 | 21 | public Task ExecuteAsync() => Task.Run(() => Execute()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Actor/IQQActor.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Actor 4 | { 5 | public enum QQActorType 6 | { 7 | SimpleActor, 8 | PollMsgActor, 9 | GetRobotReply, 10 | } 11 | 12 | 13 | public interface IQQActor 14 | { 15 | void Execute(); 16 | 17 | Task ExecuteAsync(); 18 | 19 | QQActorType Type { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Actor/IQQActorDispatcher.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Core; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Actor 4 | { 5 | 6 | /// 7 | /// QQ内部操作分发器接口 8 | /// 9 | public interface IQQActorDispatcher : IQQLifeCycle 10 | { 11 | /// 12 | /// 把一个Actor放入线程安全的事件队列的末尾等待处理 13 | /// 14 | /// 15 | void PushActor(IQQActor actor); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Actor/SimpleActorDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using iQQ.Net.WebQQCore.Im.Core; 9 | 10 | namespace iQQ.Net.WebQQCore.Im.Actor 11 | { 12 | /// 13 | /// 单线程的QQ内部分发器,可以同时使用多个QQ实例里 14 | /// 15 | public class SimpleActorDispatcher : IQQActorDispatcher 16 | { 17 | private readonly bool _asyncExcute; 18 | private readonly BlockingCollection _actorQueue; 19 | 20 | public SimpleActorDispatcher(bool asyncExcute = false) 21 | { 22 | _asyncExcute = asyncExcute; 23 | _actorQueue = new BlockingCollection(); 24 | } 25 | 26 | public void PushActor(IQQActor actor) 27 | { 28 | _actorQueue.Add(actor); 29 | } 30 | 31 | /// 32 | /// 执行一个QQActor,返回是否继续下一个actor 33 | /// 34 | /// 35 | /// 36 | private bool DispatchAction(IQQActor actor) 37 | { 38 | if (actor == null) return true; 39 | if (_asyncExcute) actor.ExecuteAsync(); 40 | else actor.Execute(); 41 | return !(actor is ExitActor); 42 | } 43 | 44 | public void Run() 45 | { 46 | while (DispatchAction(_actorQueue.Take())) { }; 47 | } 48 | 49 | public void Init(IQQContext context) 50 | { 51 | Task.Run(() => Run()); 52 | } 53 | 54 | public void Destroy() 55 | { 56 | PushActor(new ExitActor()); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/Content/ContentItem.cs: -------------------------------------------------------------------------------- 1 | namespace iQQ.Net.WebQQCore.Im.Bean.Content 2 | { 3 | public interface IContentItem 4 | { 5 | ContentItemType Type { get; } 6 | object ToJson(); 7 | void FromJson(string text); 8 | string ToText(); 9 | } 10 | 11 | 12 | public enum ContentItemType 13 | { 14 | Font, 15 | Face, 16 | Offpic, 17 | Cface, 18 | Text 19 | } 20 | 21 | public sealed class ContentItemTypeInfo 22 | { 23 | public static ContentItemType ValueOfRaw(string txt) 24 | { 25 | switch (txt.ToLower()) 26 | { 27 | case "font": return ContentItemType.Font; 28 | case "face": return ContentItemType.Face; 29 | case "offpic": return ContentItemType.Offpic; 30 | case "cface": return ContentItemType.Cface; 31 | default: return ContentItemType.Text; 32 | } 33 | } 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/Content/FaceItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | 5 | namespace iQQ.Net.WebQQCore.Im.Bean.Content 6 | { 7 | 8 | public class FaceItem : IContentItem 9 | { 10 | /** * 表情的ID */ 11 | public int Id { get; set; } 12 | 13 | public FaceItem() { } 14 | 15 | public FaceItem(string text) 16 | { 17 | FromJson(text); 18 | } 19 | 20 | public FaceItem(int id) 21 | { 22 | Id = id; 23 | } 24 | 25 | public ContentItemType Type => ContentItemType.Face; 26 | 27 | public object ToJson() 28 | { 29 | var json = new JArray(); 30 | json.Add("face"); 31 | json.Add(Id); 32 | return json; 33 | } 34 | public void FromJson(string text) 35 | { 36 | try 37 | { 38 | var json = JArray.Parse(text); 39 | Id = int.Parse(json[1].ToString()); 40 | } 41 | catch (JsonException e) 42 | { 43 | throw new QQException(QQErrorCode.JsonError, e); 44 | } 45 | catch (Exception e) 46 | { 47 | throw new QQException(QQErrorCode.UnknownError, e); 48 | } 49 | } 50 | 51 | public string ToText() 52 | { 53 | return $"[表情 {Id}]"; 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/Content/OffPicItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | 5 | namespace iQQ.Net.WebQQCore.Im.Bean.Content 6 | { 7 | 8 | public class OffPicItem : IContentItem 9 | { 10 | public bool IsSuccess { get; set; } 11 | 12 | public string FilePath { get; set; } 13 | 14 | public string FileName { get; set; } 15 | 16 | public int FileSize { get; set; } 17 | 18 | public OffPicItem() { } 19 | 20 | public OffPicItem(string text) 21 | { 22 | FromJson(text); 23 | } 24 | 25 | public ContentItemType Type => ContentItemType.Offpic; 26 | 27 | public object ToJson() 28 | { 29 | // [\"offpic\",\"/27d736df-2a59-4007-8701-7218bc70898d\",\"Beaver.bmp\",14173] 30 | var json = new JArray(); 31 | json.Add("offpic"); 32 | json.Add(FilePath); 33 | json.Add(FileName); 34 | json.Add(FileSize); 35 | return json; 36 | } 37 | 38 | public void FromJson(string text) 39 | { 40 | // ["offpic",{"success":1,"file_path":"/7acccf74-0fcd-4bbd-b885-03a5cc2f7507"}] 41 | try 42 | { 43 | var json = JArray.Parse(text); 44 | var pic = (JObject)json[1]; 45 | IsSuccess = int.Parse(pic["success"].ToString()) == 1 ? true : false; 46 | FilePath = pic["file_path"].ToString(); 47 | } 48 | catch (JsonException e) 49 | { 50 | throw new QQException(QQErrorCode.JsonError, e); 51 | } 52 | catch (Exception e) 53 | { 54 | throw new QQException(QQErrorCode.UnknownError, e); 55 | } 56 | } 57 | 58 | 59 | public string ToText() 60 | { 61 | return "[图片]"; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/Content/TextItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Bean.Content 4 | { 5 | 6 | public class TextItem : IContentItem 7 | { 8 | public string Content { get; set; } 9 | 10 | public TextItem() { } 11 | 12 | public TextItem(string text) 13 | { 14 | FromJson(text); 15 | } 16 | 17 | public ContentItemType Type => ContentItemType.Text; 18 | 19 | public object ToJson() 20 | { 21 | return Content; 22 | } 23 | 24 | public void FromJson(string text) 25 | { 26 | Content = text; 27 | } 28 | 29 | public string ToText() 30 | { 31 | return Content; 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQAccount.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using iQQ.Net.WebQQCore.Util; 3 | using iQQ.Net.WebQQCore.Util.Extensions; 4 | 5 | namespace iQQ.Net.WebQQCore.Im.Bean 6 | { 7 | 8 | public class QQAccount : QQUser 9 | { 10 | public string Password { get; set; } 11 | public string Username { get; set; } 12 | 13 | public string Vfwebqq { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQAllow.cs: -------------------------------------------------------------------------------- 1 | namespace iQQ.Net.WebQQCore.Im.Bean 2 | { 3 | /// 4 | /// 对方设置的加好友策略 5 | /// 6 | public enum QQAllow 7 | { 8 | /// 允许所有人添加 9 | ALLOW_ALL, // 0 10 | 11 | /// 需要验证信息 12 | NEED_CONFIRM, // 1 13 | 14 | /// 拒绝任何人加好友 15 | REFUSE_ALL, // 2 16 | 17 | /// 需要回答问题 18 | NEED_ANSWER, // 3 19 | 20 | /// 需要验证和回答问题 21 | NEED_ANSWER_AND_CONFIRM, // 4 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQBuddy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Bean 4 | { 5 | 6 | /// 7 | /// QQ好友,出现在好友列表的用户 8 | /// 9 | 10 | public class QQBuddy : QQUser 11 | { 12 | /// 13 | /// 备注 14 | /// 15 | public string MarkName { get; set; } 16 | /// 17 | /// 分组 18 | /// 19 | public QQCategory Category { get; set; } 20 | 21 | public string ShowName => string.IsNullOrEmpty(MarkName) ? Nickname : MarkName; 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQCategory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace iQQ.Net.WebQQCore.Im.Bean 6 | { 7 | 8 | /// 9 | /// QQ分组 10 | /// 11 | 12 | public class QQCategory 13 | { 14 | public int Index { get; set; } 15 | 16 | public int Sort { get; set; } 17 | 18 | public string Name { get; set; } 19 | 20 | public List BuddyList { get; set; } = new List(); 21 | 22 | public QQBuddy GetQQBuddyByUin(int uin) 23 | { 24 | if (BuddyList.Count != 0 && uin != 0) 25 | { 26 | return BuddyList.FirstOrDefault(b => b.Uin == uin); 27 | } 28 | return null; 29 | } 30 | 31 | public int GetOnlineCount() 32 | { 33 | return BuddyList.Select(buddy => buddy.Status).Count(QQStatus.IsGeneralOnline); 34 | } 35 | 36 | public int GetBuddyCount() => BuddyList?.Count ?? 0; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQClientTypeInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Bean 4 | { 5 | public enum QQClientType 6 | { 7 | WebQQ, 8 | MobileQQ, 9 | PcQQ, 10 | PadQQ, 11 | Unknown 12 | } 13 | 14 | /// 15 | /// QQ客户端类型 可能有多个值对应同一种客户端的情况,这些值是否能进一步区分,还需做测试 16 | /// 17 | public static class QQClientTypeInfo 18 | { 19 | public static QQClientType ValueOfRaw(string value) 20 | { 21 | QQClientType type; 22 | if (Enum.TryParse(value, true, out type)) 23 | { 24 | return type; 25 | } 26 | return QQClientType.Unknown; 27 | } 28 | 29 | public static QQClientType ValueOfRaw(int i) 30 | { 31 | switch (i) 32 | { 33 | case 1: 34 | case 2: 35 | case 3: 36 | case 4: 37 | case 5: 38 | case 6: 39 | case 10: 40 | case 0x1E4: 41 | return QQClientType.PcQQ; 42 | 43 | case 41: 44 | return QQClientType.WebQQ; 45 | 46 | case 21: 47 | case 22: 48 | case 23: 49 | case 24: 50 | return QQClientType.MobileQQ; 51 | 52 | case 42: 53 | return QQClientType.PadQQ; 54 | 55 | default: 56 | return QQClientType.Unknown; 57 | } 58 | } 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQDiscuzMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Bean 4 | { 5 | 6 | public class QQDiscuzMember : QQStranger 7 | { 8 | public QQDiscuz Discuz { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQEmail.cs: -------------------------------------------------------------------------------- 1 | namespace iQQ.Net.WebQQCore.Im.Bean 2 | { 3 | public class QQEmail 4 | { 5 | public string Id { get; set; } 6 | public string Sender { get; set; } 7 | public string SenderNick { get; set; } 8 | public string SenderEmail { get; set; } 9 | public string Subject { get; set; } 10 | public string Summary { get; set; } 11 | public bool Unread { get; set; } 12 | public long Flag { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using Newtonsoft.Json; 6 | 7 | namespace iQQ.Net.WebQQCore.Im.Bean 8 | { 9 | 10 | public class QQGroup 11 | { 12 | public long Gid { get; set; } 13 | public long Gin { get; set; } 14 | public long Code { get; set; } 15 | public int Clazz { get; set; } 16 | public long Flag { get; set; } 17 | public int Level { get; set; } 18 | public int Mask { get; set; } 19 | public string Name { get; set; } 20 | public string Memo { get; set; } 21 | public string FingerMemo { get; set; } 22 | public DateTime CreateTime { get; set; } 23 | 24 | [JsonIgnore] 25 | public Image Face { get; set; } 26 | 27 | public List Members { get; set; } = new List(); 28 | 29 | public QQGroupMember GetMemberByUin(long uin) 30 | { 31 | return Members.FirstOrDefault(mem => mem.Uin == uin); 32 | } 33 | 34 | public override int GetHashCode() 35 | { 36 | return (int)this.Code; 37 | } 38 | 39 | public override bool Equals(object obj) 40 | { 41 | var other = obj as QQGroup; 42 | return Code == other?.Code; 43 | } 44 | 45 | public override string ToString() 46 | { 47 | return "QQGroup [gid=" + Gid + ", code=" + Code + ", name=" + Name + "]"; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQGroupMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Bean 4 | { 5 | /// 6 | /// 群成员 7 | /// 8 | // 9 | public class QQGroupMember : QQStranger 10 | { 11 | public QQGroup Group { get; set; } 12 | 13 | public string Card { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQGroupSearchInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Bean 4 | { 5 | /// 6 | /// 用于存储搜索群的结果 7 | /// 8 | 9 | public class QQGroupSearchInfo 10 | { 11 | /// 12 | /// 群名; 13 | /// 14 | public string GroupName { get; set; } 15 | /// 16 | /// 群ID 17 | /// 18 | public long GroupId { get; set; } 19 | /// 20 | /// 群别名ID,用于协议部分 21 | /// 22 | public long GroupAliseId { get; set; } 23 | /// 24 | /// 群等级 25 | /// 26 | public int Level { get; set; } 27 | /// 28 | /// 所有者QQ号 29 | /// 30 | public long OwerId { get; set; } 31 | /// 32 | /// 创建的日期(时间戳) 33 | /// 34 | public long CreateTimeStamp { get; set; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQGroupSearchList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Bean 4 | { 5 | public sealed class SearchType 6 | { 7 | public static readonly SearchType QQGROUPSEARCH_TEXT; // 0表示QQ号码 8 | public static readonly SearchType QQGROUPSEARCH_KEY;// 1表示关键字 9 | } 10 | 11 | public class QQGroupSearchList 12 | { 13 | public SearchType SearchType { get; set; } = SearchType.QQGROUPSEARCH_TEXT; 14 | 15 | /// 16 | /// 页数 17 | /// 18 | public int PageNum { get; set; } 19 | 20 | /// 21 | /// 当前页数 22 | /// 23 | public int CurrentPage { get; set; } = 1; 24 | 25 | /// 26 | /// 一页的信息量 27 | /// 28 | public int PageSize { get; set; } = 10; 29 | /// 30 | /// 搜索的关键字 31 | /// 32 | public string KeyStr { get; set; } 33 | 34 | /// 35 | /// 是否要验证码 36 | /// 37 | public bool NeedVfcode { get; set; } = false; 38 | /// 39 | /// 验证码数据 40 | /// 41 | public string VfCode { get; set; } 42 | /// 43 | /// 搜索出的群集合 44 | /// 45 | public List GroupsResult { get; set; } = new List(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQHalfStranger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Bean 4 | { 5 | /** 6 | * 7 | * 我是对方的好友,但对方不是我的好友,就算是半个陌生人 8 | * 只是一个标记类 9 | * 10 | * @author ZhiHui_Chen 11 | * @since date 2013-4-14 12 | */ 13 | 14 | public class QQHalfStranger : QQStranger 15 | { 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQLevel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Bean 4 | { 5 | 6 | public class QQLevel 7 | { 8 | public int Level { get; set; } 9 | 10 | public int Days { get; set; } 11 | 12 | public int Hours { get; set; } 13 | 14 | public int RemainDays { get; set; } 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Bean/QQStranger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Bean 4 | { 5 | 6 | public class QQStranger : QQUser 7 | { 8 | /// 9 | /// 临时会话信道 10 | /// 11 | public string GroupSig { get; set; } 12 | 13 | public int ServiceType { get; set; } 14 | 15 | /// 16 | /// 加好友时需要的参数 17 | /// 18 | public string Token { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Core/IQQContext.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Actor; 2 | using iQQ.Net.WebQQCore.Im.Bean; 3 | using iQQ.Net.WebQQCore.Im.Event; 4 | using iQQ.Net.WebQQCore.Im.Log; 5 | 6 | namespace iQQ.Net.WebQQCore.Im.Core 7 | { 8 | /// 9 | /// QQ环境上下文,所有的模块都是用QQContext来获取对象 10 | /// 11 | public interface IQQContext 12 | { 13 | IQQLogger Logger { get; } 14 | 15 | void PushActor(IQQActor actor); 16 | 17 | void FireNotify(QQNotifyEvent qqNotifyEvent); 18 | 19 | T GetModule(QQModuleType type) where T : IQQModule; 20 | 21 | T GetSerivce(QQServiceType type) where T:IQQService; 22 | 23 | QQAccount Account { get; set; } 24 | 25 | QQSession Session { get; set; } 26 | 27 | QQStore Store { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Core/IQQLifeCycle.cs: -------------------------------------------------------------------------------- 1 | namespace iQQ.Net.WebQQCore.Im.Core 2 | { 3 | 4 | /// 5 | /// 生命周期管理 6 | /// 实现了这个接口就可以支持对象的重复使用 7 | /// 8 | public interface IQQLifeCycle 9 | { 10 | 11 | 12 | /// 13 | /// 初始化,在使用之前调用 14 | /// 15 | /// 16 | void Init(IQQContext context); 17 | 18 | /// 19 | /// 销毁,在使用完毕之后调用 20 | /// 21 | void Destroy(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Core/IQQModule.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Event; 2 | using iQQ.Net.WebQQCore.Im.Http; 3 | 4 | namespace iQQ.Net.WebQQCore.Im.Core 5 | { 6 | public enum QQModuleType 7 | { 8 | PROC, //登陆和退出流程执行 9 | LOGIN, //核心模块,处理登录和退出的逻辑 10 | USER, //个人信息管理模块 11 | BUDDY, //好友管理模块 12 | CATEGORY, //分组管理模块 13 | GROUP, //群管理模块 14 | DISCUZ, //讨论组模块 15 | CHAT, //聊天模块 16 | EMAIL //邮件模块 17 | } 18 | 19 | public interface IQQModule : IQQLifeCycle 20 | { 21 | IQQActionFuture PushHttpAction(IHttpAction action); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Core/IQQService.cs: -------------------------------------------------------------------------------- 1 | namespace iQQ.Net.WebQQCore.Im.Core 2 | { 3 | /** 4 | * 5 | * QQ服务 6 | * 7 | * 提供和模块与协议无关的公共服务,供模块调用,如定时服务,网络连接服务,异步任务服务等 8 | * 9 | * @author solosky 10 | */ 11 | 12 | public enum QQServiceType 13 | { 14 | TIMER, //定时服务 15 | HTTP, //HTTP 16 | TASK, //异步任务,可以执行比较耗时的操作 17 | UDP, 18 | LOG, 19 | }; 20 | 21 | public interface IQQService : IQQLifeCycle 22 | { 23 | 24 | }; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Core/QQSession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Core 4 | { 5 | 6 | public enum QQSessionState 7 | { 8 | Offline, 9 | Online, 10 | Kicked, 11 | Logining, 12 | Error 13 | } 14 | 15 | /// 16 | /// QQSession保存了每次登陆时候的状态信息 17 | /// 18 | public class QQSession 19 | { 20 | private volatile QQSessionState _state; // 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值. 21 | public QQSessionState State 22 | { 23 | get { return _state; } 24 | set { _state = value; } 25 | } 26 | 27 | private long _clientId; 28 | public long ClientId 29 | { 30 | get 31 | { 32 | if (_clientId == 0) 33 | { 34 | // _clientId = new Random().Next(); //random?? 35 | _clientId = 53999199; 36 | } 37 | return _clientId; 38 | } 39 | set { _clientId = value; } 40 | } 41 | 42 | public string SessionId { get; set; } 43 | public string Vfwebqq { get; set; } 44 | public string Ptwebqq { get; set; } 45 | public string LoginSig { get; set; } 46 | public string CfaceKey { get; set; } 47 | public string CfaceSig { get; set; } 48 | public string EmailAuthKey { get; set; } 49 | public int Index { get; set; } 50 | public int Port { get; set; } 51 | public int PollErrorCnt { get; set; } 52 | public string CapCd { get; set; } 53 | public string Psessionid { get; set; } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Event/Future/HttpActionFuture.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Http; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Event.Future 4 | { 5 | /** 6 | * 7 | * 用于异步等待操作完成 8 | * 9 | * 提交任何一个操作都会返回这个对象,可以在这个对象上: 10 | * 1. 同步等待操作完成 11 | * 3. 取消请求 12 | * 13 | * 注意: 千万不要在回调函数里面等待操作完成,否则会把整个客户端挂起 14 | * 15 | * @author solosky 16 | */ 17 | public class HttpActionFuture : AbstractActionFuture 18 | { 19 | private readonly IHttpAction _httpAction; 20 | 21 | public HttpActionFuture(IHttpAction action) 22 | : base(action.Listener) 23 | { 24 | _httpAction = action; 25 | _httpAction.Listener = Listener; 26 | _httpAction.ActionFuture = this; 27 | } 28 | 29 | public override bool IsCancelable() 30 | { 31 | return _httpAction.IsCancelable(); 32 | } 33 | 34 | public override void Cancel() 35 | { 36 | _httpAction.CancelRequest(); 37 | base.Cancel(); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Event/Future/ProcActionFuture.cs: -------------------------------------------------------------------------------- 1 | namespace iQQ.Net.WebQQCore.Im.Event.Future 2 | { 3 | /** 4 | * 5 | * 多个过程操作的异步等待对象 6 | * 需手动处理 7 | * 8 | * @author solosky 9 | */ 10 | public class ProcActionFuture : AbstractActionFuture 11 | { 12 | 13 | /** 14 | *

Constructor for ProcActionFuture.

15 | * 16 | * @param proxyListener a {@link iqq.im.IQQActionListener} object. 17 | * @param Cancelable a bool. 18 | */ 19 | public ProcActionFuture(QQActionListener proxyListener, bool cancelable) 20 | : base(proxyListener) 21 | { 22 | } 23 | 24 | public override bool IsCancelable() 25 | { 26 | return true; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Event/IQQActionFuture.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace iQQ.Net.WebQQCore.Im.Event 5 | { 6 | /// 7 | /// qq动作的执行结果 8 | /// 9 | public interface IQQActionFuture 10 | { 11 | /// 12 | /// 判断这个操作是否可以取消 13 | /// 14 | /// 15 | bool IsCancelable(); 16 | 17 | /// 18 | /// 尝试取消操作 19 | /// 20 | void Cancel(); 21 | 22 | bool IsCanceled { get; } 23 | 24 | /// 25 | /// 等待最终的事件,通常是EVT_CANCELED,EVT_ERROR,EVT_OK 26 | /// 不抛出异常 27 | /// 28 | /// 29 | QQActionEvent WaitFinalEvent(); 30 | 31 | /// 32 | /// 给定一个超时时间,等待最终的事件 33 | /// 34 | /// 35 | /// 36 | QQActionEvent WaitFinalEvent(int millisecond); 37 | 38 | Task WhenFinalEvent(); 39 | 40 | Task WhenFinalEvent(int millisecond); 41 | 42 | Task WhenFinalEvent(CancellationToken token); 43 | 44 | CancellationToken Token { get; } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Event/QQActionEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Reflection; 4 | using iQQ.Net.WebQQCore.Util.Extensions; 5 | 6 | namespace iQQ.Net.WebQQCore.Im.Event 7 | { 8 | [Description("QQ动作事件类型")] 9 | public enum QQActionEventType 10 | { 11 | [Description("成功")] 12 | EvtOK, 13 | [Description("错误")] 14 | EvtError, 15 | [Description("写入")] 16 | EvtWrite, 17 | [Description("读取")] 18 | EvtRead, 19 | [Description("取消")] 20 | EvtCanceled, 21 | [Description("重试")] 22 | EvtRetry, 23 | } 24 | 25 | public class QQActionEvent : QQEvent 26 | { 27 | public QQActionEventType Type { get; } 28 | public object Target { get; } 29 | 30 | public QQActionEvent(QQActionEventType type, object target) 31 | { 32 | Type = type; 33 | Target = target; 34 | } 35 | 36 | public override string ToString() 37 | { 38 | return $"{Type.GetFullDescription()}, target={Target ?? ""}]"; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Event/QQActionEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Event 4 | { 5 | public abstract class QQActionEventArgs : EventArgs 6 | { 7 | 8 | } 9 | 10 | public class ProgressArgs : QQActionEventArgs 11 | { 12 | /** 当前进度 */ 13 | public long Current { get; set; } 14 | /** 总的进度 */ 15 | public long Total { get; set; } 16 | 17 | public override string ToString() 18 | { 19 | return "ProgressArgs [current=" + Current + ", total=" + Total + "]"; 20 | } 21 | } 22 | 23 | public class CancelArgs : QQActionEventArgs 24 | { 25 | public CancelResult Result { get; } 26 | 27 | public CancelArgs(CancelResult result) 28 | { 29 | Result = result; 30 | } 31 | } 32 | 33 | public enum CancelResult 34 | { 35 | Disabled, 36 | Error, 37 | Successful, 38 | AlreadyCanceld 39 | } 40 | 41 | public class CheckVerifyArgs : QQActionEventArgs 42 | { 43 | public int Result { get; set; } 44 | public string Code { get; set; } 45 | public long Uin { get; set; } 46 | } 47 | 48 | public enum QRCodeStatus 49 | { 50 | /// 51 | /// 扫码成功 52 | /// 53 | QRCODE_OK, 54 | 55 | /// 56 | /// 二维码未失效 57 | /// 58 | QRCODE_VALID, 59 | 60 | /// 61 | /// 二维码失效 62 | /// 63 | QRCODE_INVALID, 64 | 65 | /// 66 | /// 二维码认证中 67 | /// 68 | QRCODE_AUTH, 69 | } 70 | 71 | public class CheckQRCodeArgs : QQActionEventArgs 72 | { 73 | public QRCodeStatus Status { get; private set; } 74 | 75 | public string Msg { get; private set; } 76 | 77 | public CheckQRCodeArgs(QRCodeStatus status, string msg) 78 | { 79 | Status = status; 80 | Msg = msg; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Event/QQEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Event 4 | { 5 | /// 6 | /// QQ事件,客户端被动接受的事件,比如收到了消息,好友上线 7 | /// 8 | public abstract class QQEvent : EventArgs { } 9 | } 10 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Event/QQNotifyEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | 4 | 5 | namespace iQQ.Net.WebQQCore.Im.Event 6 | { 7 | public abstract class QQNotifyEventArgs : EventArgs 8 | { 9 | 10 | } 11 | 12 | /** 需要用户识别验证码通知 登录,加好友,获取QQ号可能都需要验证码*/ 13 | public class ImageVerify: QQNotifyEventArgs 14 | { 15 | public enum VerifyType 16 | { 17 | LOGIN, ADD_FRIEND, GET_UIN 18 | }; 19 | 20 | /** 验证的类型,登陆,添加好友,获取qq号可能会出现验证码 */ 21 | public VerifyType Type { get; set; } 22 | 23 | /** 验证码图片对象 **/ 24 | public Image Image { get; set; } 25 | 26 | /** 需要验证的原因 */ 27 | public string Reason { get; set; } 28 | 29 | /** future对象,在验证流程内部使用 */ 30 | public IQQActionFuture Future { get; set; } 31 | 32 | /** 每一个验证码对应HTTP中cookie中名字为verifysession的值 */ 33 | public string Vsession { get; set; } 34 | 35 | /** 验证码字符 */ 36 | public string Vcode { get; set; } 37 | } 38 | 39 | 40 | /// 41 | /// 登录进度通知 42 | /// 43 | public enum LoginProgress 44 | { 45 | CHECK_VERIFY, 46 | UI_LOGIN, 47 | UI_LOGIN_VERIFY, 48 | CHANNEL_LOGIN, 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Event/QQNotifyHandler.cs: -------------------------------------------------------------------------------- 1 | namespace iQQ.Net.WebQQCore.Im.Event 2 | { 3 | /** 4 | * 5 | * 一个注解可以简化NotifyEvent的监听,分发和处理 6 | * 只需在处理事件的方法上加入@QQNotifyHandler即可 7 | * 如 8 | * 9 | * @author solosky 10 | */ 11 | } 12 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Event/QQNotifyHandlerProxy.cs: -------------------------------------------------------------------------------- 1 | namespace iQQ.Net.WebQQCore.Im.Event 2 | { 3 | /** 4 | * 5 | * 使用这个类简化事件的注册,分发 6 | * 只需在被代理的类使用@IMEventHandler注解需要处理的事件类型即可 7 | * 8 | * @author solosky 9 | */ 10 | /* 11 | 12 | public class QQNotifyHandlerProxy : IQQNotifyListener{ 13 | 14 | private Object proxyObject; 15 | private Dictionary methodMap; 16 | / ** 17 | *

Constructor for QQNotifyHandlerProxy.

18 | * 19 | * @param proxyObject a {@link java.lang.Object} object. 20 | * / 21 | public QQNotifyHandlerProxy(Object proxyObject){ 22 | this.proxyObject = proxyObject; 23 | this.methodMap = new Dictionary(); 24 | foreach (Method m in proxyObject.getClass().getDeclaredMethods()) { 25 | if(m.isAnnotationPresent(QQNotifyHandler.class)){ 26 | QQNotifyHandler handler = m.getAnnotation(QQNotifyHandler.class); 27 | this.methodMap.Add(handler.value(), m); 28 | if(!m.isAccessible()){ 29 | m.setAccessible(true); 30 | } 31 | } 32 | } 33 | } 34 | 35 | / ** {@inheritDoc} * / 36 | 37 | public void OnNotifyEvent(QQNotifyEvent Event) { 38 | Method m = methodMap[Event.Type]; 39 | if(m != null){ 40 | try { 41 | m.Invoke(proxyObject, Event); 42 | } catch (Exception e) { 43 | // LOG.warn("invoke QQNotifyHandler Error!!", e); 44 | } 45 | }else{ 46 | // LOG.warn("Not found QQNotifyHandler for QQNotifyEvent = " + Event); 47 | } 48 | } 49 | 50 | } 51 | */ 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Http/IHttpAction.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using iQQ.Net.WebQQCore.Im.Event; 3 | 4 | namespace iQQ.Net.WebQQCore.Im.Http 5 | { 6 | public interface IHttpAction : IQQHttpListener 7 | { 8 | QQHttpRequest BuildRequest(); 9 | 10 | void CancelRequest(); 11 | 12 | bool IsCancelable(); 13 | 14 | void NotifyActionEvent(QQActionEventType type, object target); 15 | 16 | QQActionListener Listener { get; set; } 17 | 18 | IQQActionFuture ActionFuture { get; set; } 19 | 20 | Task ResponseFuture { set; } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Http/IQQHttpListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Http 4 | { 5 | public interface IQQHttpListener 6 | { 7 | void OnHttpFinish(QQHttpResponse response); 8 | 9 | void OnHttpError(Exception t); 10 | 11 | void OnHttpHeader(QQHttpResponse response); 12 | 13 | void OnHttpWrite(long current, long total); 14 | 15 | void OnHttpRead(long current, long total); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Log/EmptyQQLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using iQQ.Net.WebQQCore.Im.Core; 3 | using iQQ.Net.WebQQCore.Util; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace iQQ.Net.WebQQCore.Im.Log 7 | { 8 | public class EmptyQQLogger : IQQLogger 9 | { 10 | public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) 11 | { 12 | } 13 | 14 | public bool IsEnabled(LogLevel logLevel) => true; 15 | 16 | public IDisposable BeginScope(TState state) => Disposable.Empty; 17 | 18 | public IQQContext Context { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Log/IQQLogger.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Core; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace iQQ.Net.WebQQCore.Im.Log 5 | { 6 | public interface IQQLogger : ILogger 7 | { 8 | IQQContext Context { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Log/QQConsoleLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using iQQ.Net.WebQQCore.Im.Core; 3 | using iQQ.Net.WebQQCore.Util; 4 | using iQQ.Net.WebQQCore.Util.Extensions; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace iQQ.Net.WebQQCore.Im.Log 8 | { 9 | public class QQConsoleLogger : SimpleConsoleLogger, IQQLogger 10 | { 11 | public QQConsoleLogger(string name) : base(name) 12 | { 13 | } 14 | 15 | public QQConsoleLogger() : this("iQQ.Net") { } 16 | 17 | public override string GetMessage(string message, Exception exception) 18 | { 19 | if (!message.IsNullOrEmpty() && Context?.Account != null) 20 | { 21 | var qqStr = Context.Account.QQ.IsDefault() ? string.Empty : $"[{Context.Account.QQ}] "; 22 | return $"{qqStr}{message}"; 23 | } 24 | else return message; 25 | } 26 | 27 | public IQQContext Context { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Module/AbstractModule.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Actor; 2 | using iQQ.Net.WebQQCore.Im.Core; 3 | using iQQ.Net.WebQQCore.Im.Event; 4 | using iQQ.Net.WebQQCore.Im.Event.Future; 5 | using iQQ.Net.WebQQCore.Im.Http; 6 | 7 | namespace iQQ.Net.WebQQCore.Im.Module 8 | { 9 | /// 10 | /// 基础模块 11 | /// @author solosky 12 | /// 13 | public abstract class AbstractModule : IQQModule 14 | { 15 | public IQQContext Context { get; private set; } 16 | 17 | public virtual void Init(IQQContext context) 18 | { 19 | Context = context; 20 | } 21 | 22 | public virtual void Destroy() 23 | { 24 | } 25 | 26 | public IQQActionFuture PushHttpAction(IHttpAction action) 27 | { 28 | var future = new HttpActionFuture(action); //替换掉原始的QQActionListener 29 | Context.PushActor(new HttpActor(HttpActorType.BuildRequest, Context, action)); 30 | return future; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Module/BuddyModule.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Action; 2 | using iQQ.Net.WebQQCore.Im.Core; 3 | using iQQ.Net.WebQQCore.Im.Event; 4 | 5 | namespace iQQ.Net.WebQQCore.Im.Module 6 | { 7 | /// 8 | /// 好友信息处理模块 9 | /// @author solosky 10 | /// 11 | public class BuddyModule : AbstractModule 12 | { 13 | public IQQActionFuture GetOnlineBuddy(QQActionListener listener) 14 | { 15 | return PushHttpAction(new GetOnlineFriendAction(Context, listener)); 16 | } 17 | 18 | public IQQActionFuture GetRecentList(QQActionListener listener) 19 | { 20 | return PushHttpAction(new GetRecentListAction(Context, listener)); 21 | } 22 | 23 | public IQQActionFuture AddBuddy(QQActionListener listener, string account) 24 | { 25 | return PushHttpAction(new AcceptBuddyAddAction(Context, listener, account)); 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Module/CategoryModule.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Action; 2 | using iQQ.Net.WebQQCore.Im.Core; 3 | using iQQ.Net.WebQQCore.Im.Event; 4 | 5 | namespace iQQ.Net.WebQQCore.Im.Module 6 | { 7 | /// 8 | /// 好友列表模块,处理好友的添加和删除 9 | /// @author solosky 10 | /// 11 | public class CategoryModule : AbstractModule 12 | { 13 | public IQQActionFuture GetBuddyList(QQActionListener listener = null) 14 | { 15 | return PushHttpAction(new GetBuddyListAction(Context, listener)); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Module/DiscuzModule.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Action; 2 | using iQQ.Net.WebQQCore.Im.Bean; 3 | using iQQ.Net.WebQQCore.Im.Core; 4 | using iQQ.Net.WebQQCore.Im.Event; 5 | 6 | namespace iQQ.Net.WebQQCore.Im.Module 7 | { 8 | /// 9 | /// 讨论组模块 10 | /// @author solosky 11 | /// 12 | public class DiscuzModule : AbstractModule 13 | { 14 | public IQQActionFuture GetDiscuzList(QQActionListener listener) 15 | { 16 | return PushHttpAction(new GetDiscuzListAction(this.Context, listener)); 17 | } 18 | 19 | public IQQActionFuture GetDiscuzInfo(QQDiscuz discuz, QQActionListener listener) 20 | { 21 | return PushHttpAction(new GetDiscuzInfoAction(this.Context, listener, discuz)); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Module/GroupModule.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Action; 2 | using iQQ.Net.WebQQCore.Im.Bean; 3 | using iQQ.Net.WebQQCore.Im.Core; 4 | using iQQ.Net.WebQQCore.Im.Event; 5 | 6 | namespace iQQ.Net.WebQQCore.Im.Module 7 | { 8 | /// 9 | /// 群模块 10 | /// @author solosky 11 | /// 12 | public class GroupModule : AbstractModule 13 | { 14 | public IQQActionFuture GetGroupList(QQActionListener listener = null) 15 | { 16 | return PushHttpAction(new GetGroupListAction(Context, listener)); 17 | } 18 | 19 | public IQQActionFuture UpdateGroupMessageFilter(QQActionListener listener = null) 20 | { 21 | return PushHttpAction(new UpdateGroupMessageFilterAction(Context, listener)); 22 | } 23 | 24 | public IQQActionFuture GetGroupFace(QQGroup group, QQActionListener listener = null) 25 | { 26 | return PushHttpAction(new GetGroupFaceAction(Context, listener, group)); 27 | } 28 | 29 | public IQQActionFuture GetGroupInfo(QQGroup group, QQActionListener listener = null) 30 | { 31 | return PushHttpAction(new GetGroupInfoAction(Context, listener, group)); 32 | } 33 | 34 | public IQQActionFuture GetGroupGid(QQGroup group, QQActionListener listener = null) 35 | { 36 | return PushHttpAction(new GetGroupAccoutAction(Context, listener, group)); 37 | } 38 | 39 | public IQQActionFuture GetMemberStatus(QQGroup group, QQActionListener listener = null) 40 | { 41 | return PushHttpAction(new GetGroupMemberStatusAction(Context, listener, group)); 42 | } 43 | 44 | public IQQActionFuture SearchGroup(QQGroupSearchList resultList, QQActionListener listener = null) 45 | { 46 | return PushHttpAction(new SearchGroupInfoAction(Context, listener, resultList)); 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Module/UserModule.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Action; 2 | using iQQ.Net.WebQQCore.Im.Bean; 3 | using iQQ.Net.WebQQCore.Im.Core; 4 | using iQQ.Net.WebQQCore.Im.Event; 5 | 6 | namespace iQQ.Net.WebQQCore.Im.Module 7 | { 8 | /// 9 | /// 个人信息模块 10 | /// 11 | public class UserModule : AbstractModule 12 | { 13 | public IQQActionFuture GetUserInfo(QQUser user, QQActionListener listener = null) 14 | { 15 | return PushHttpAction(new GetFriendInfoAction(Context, listener, user)); 16 | } 17 | 18 | public IQQActionFuture GetUserFace(QQUser user, QQActionListener listener = null) 19 | { 20 | return PushHttpAction(new GetFriendFaceAction(Context, listener, user)); 21 | } 22 | 23 | public IQQActionFuture GetUserAccount(QQUser user, QQActionListener listener = null) 24 | { 25 | return PushHttpAction(new GetFriendAccoutAction(Context, listener, user)); 26 | } 27 | 28 | public IQQActionFuture GetUserSign(QQUser user, QQActionListener listener = null) 29 | { 30 | return PushHttpAction(new GetFriendSignAction(Context, listener, user)); 31 | } 32 | 33 | public IQQActionFuture GetUserLevel(QQUser user, QQActionListener listener = null) 34 | { 35 | return PushHttpAction(new GetUserLevelAction(Context, listener, user)); 36 | } 37 | 38 | public IQQActionFuture ChangeStatus(QQStatus status, QQActionListener listener = null) 39 | { 40 | return PushHttpAction(new ChangeStatusAction(Context, listener, status)); 41 | } 42 | 43 | public IQQActionFuture GetStrangerInfo(QQUser user, QQActionListener listener = null) 44 | { 45 | return PushHttpAction(new GetStrangerInfoAction(Context, listener, user)); 46 | } 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/QQActionListener.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Event; 2 | using System; 3 | using iQQ.Net.WebQQCore.Im.Http; 4 | 5 | namespace iQQ.Net.WebQQCore.Im 6 | { 7 | public delegate void QQActionListener(object sender, QQActionEvent e); 8 | } 9 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/QQNotifyListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using iQQ.Net.WebQQCore.Im.Event; 3 | 4 | namespace iQQ.Net.WebQQCore.Im 5 | { 6 | // 强类型的委托 7 | public delegate void QQNotifyListener(IQQClient sender, QQNotifyEvent e); 8 | 9 | ///// 10 | ///// QQ通知事件监听器 11 | ///// 12 | //public interface IQQNotifyListener 13 | //{ 14 | // // event QQNotifyHandler OnNotifyEvent; 15 | // event EventHandler OnNotifyEvent; 16 | //} 17 | } 18 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Service/AbstractService.cs: -------------------------------------------------------------------------------- 1 | using iQQ.Net.WebQQCore.Im.Core; 2 | 3 | namespace iQQ.Net.WebQQCore.Im.Service 4 | { 5 | /// 6 | /// 抽象的服务类,实现了部分接口,方便子类实现 7 | /// 8 | public abstract class AbstractService : IQQService 9 | { 10 | public IQQContext Context { get; private set; } 11 | 12 | public virtual void Init(IQQContext context) 13 | { 14 | Context = context; 15 | } 16 | 17 | public abstract void Destroy(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/WebQQCore/Im/Service/IHttpService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using iQQ.Net.WebQQCore.Im.Core; 4 | using iQQ.Net.WebQQCore.Im.Http; 5 | 6 | namespace iQQ.Net.WebQQCore.Im.Service 7 | { 8 | public enum ProxyType 9 | { 10 | HTTP, 11 | SOCK4, 12 | SOCK5 13 | } 14 | 15 | public interface IHttpService : IQQService 16 | { 17 | /// 18 | /// 设置HTTP代理 19 | /// 20 | /// 代理类型 21 | /// 代理主机 22 | /// 代理端口 23 | /// 认证用户名, 如果不需要认证,设置为null 24 | /// 认证密码,如果不需要认证,设置为null 25 | void SetHttpProxy(ProxyType proxyType, string proxyHost, 26 | int proxyPort, string proxyAuthUser, string proxyAuthPassword); 27 | 28 | string UserAgent { get; set; } 29 | 30 | /// 31 | /// 创建一个请求,这个方法会填充默认的HTTP头,比如User-Agent 32 | /// 33 | /// 34 | /// 35 | /// 36 | QQHttpRequest CreateHttpRequest(string method, string url); 37 | 38 | /// 39 | /// 执行一个HTTP请求 40 | /// 41 | /// 42 | /// 43 | /// 44 | QQHttpResponse ExecuteHttpRequest(QQHttpRequest qqRequest, IQQHttpListener listener); 45 | Task ExecuteHttpRequestAsync(QQHttpRequest request, IQQHttpListener listener, CancellationToken token); 46 | 47 | /// 48 | /// 获取一个cookie 49 | /// 50 | /// 51 | /// 52 | /// 53 | QQHttpCookie GetCookie(string name, string url); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/WebQQCore/Resources/hash.js: -------------------------------------------------------------------------------- 1 | hash = function (x, K) { 2 | x += ""; 3 | for (var N = [], T = 0; T < K.length; T++) 4 | N[T % 4] ^= K.charCodeAt(T); 5 | var U = ["EC", "OK"], V = []; 6 | V[0] = x >> 24 & 255 ^ U[0].charCodeAt(0); 7 | V[1] = x >> 16 & 255 ^ U[0].charCodeAt(1); 8 | V[2] = x >> 8 & 255 ^ U[1].charCodeAt(0); 9 | V[3] = x & 255 ^ U[1].charCodeAt(1); 10 | U = []; 11 | for (T = 0; T < 8; T++) 12 | U[T] = T % 2 == 0 ? N[T >> 1] : V[T >> 1]; 13 | N = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"]; 14 | V = ""; 15 | for (T = 0; T < U.length; T++) { 16 | V += N[U[T] >> 4 & 15]; 17 | V += N[U[T] & 15] 18 | } 19 | return V 20 | } -------------------------------------------------------------------------------- /src/WebQQCore/Util/DateUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Newtonsoft.Json.Linq; 4 | 5 | namespace iQQ.Net.WebQQCore.Util 6 | { 7 | public class DateUtils 8 | { 9 | public static DateTime Parse(JObject jsonobj) 10 | { 11 | const string format = "yyyy-M-d"; 12 | var dateString = jsonobj["year"] + "-" + jsonobj["month"] + "-" + jsonobj["day"]; 13 | var dt = DateTime.ParseExact(dateString, format, CultureInfo.CurrentCulture); 14 | return dt; 15 | } 16 | 17 | /// 18 | /// 自1970年1月1日0时起的毫秒数 19 | /// 20 | /// 21 | public static long NowTimestamp() 22 | { 23 | return DateTime.Now.CurrentTimeMillis(); 24 | } 25 | } 26 | 27 | public static class DateTimeExtensions 28 | { 29 | private static readonly DateTime _jan1St1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 30 | 31 | /// 32 | /// 自1970年1月1日0时起的毫秒数 33 | /// 34 | /// 35 | /// 36 | public static long CurrentTimeMillis(this DateTime d) 37 | { 38 | return (long)((DateTime.UtcNow - _jan1St1970).TotalMilliseconds); 39 | } 40 | 41 | public static long CurrentTimeSeconds(this DateTime d) 42 | { 43 | return CurrentTimeMillis(d) / 1000; 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/WebQQCore/Util/DefaultLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using iQQ.Net.WebQQCore.Im; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace iQQ.Net.WebQQCore.Util 10 | { 11 | internal static class DefaultLogger 12 | { 13 | public static ILogger Logger { get; } 14 | 15 | static DefaultLogger() 16 | { 17 | var loggerFactory = new LoggerFactory().AddSimpleConsole(); 18 | Logger = loggerFactory.CreateLogger("iQQ.Net"); 19 | } 20 | 21 | private static ILoggerFactory AddSimpleConsole(this ILoggerFactory factory) 22 | { 23 | factory.AddProvider(new SimpleConsoleLoggerProvider()); 24 | return factory; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/WebQQCore/Util/EmptyDisposable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace iQQ.Net.WebQQCore.Util 7 | { 8 | public class Disposable : IDisposable 9 | { 10 | public static IDisposable Empty { get; } = new Disposable(); 11 | public void Dispose() 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/WebQQCore/Util/Extensions/ByteExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace iQQ.Net.WebQQCore.Util.Extensions 8 | { 9 | public static class ByteExtensions 10 | { 11 | public static string ToHexString(this byte[] bytes) 12 | { 13 | var builder = new StringBuilder(); 14 | foreach (var @byte in bytes) 15 | { 16 | builder.Append(@byte.ToString("X2")); 17 | } 18 | return builder.ToString(); 19 | } 20 | 21 | public static byte[] ToBytes(this bool num) => BitConverter.GetBytes(num); 22 | 23 | public static byte[] ToBytes(this char num) => BitConverter.GetBytes(num); 24 | 25 | public static byte[] ToBytes(this short num) => BitConverter.GetBytes(num); 26 | 27 | public static byte[] ToBytes(this int num) => BitConverter.GetBytes(num); 28 | 29 | public static byte[] ToBytes(this long num) => BitConverter.GetBytes(num); 30 | 31 | public static byte[] ToBytes(this ushort num) => BitConverter.GetBytes(num); 32 | 33 | public static byte[] ToBytes(this uint num) => BitConverter.GetBytes(num); 34 | 35 | public static byte[] ToBytes(this ulong num) => BitConverter.GetBytes(num); 36 | 37 | public static byte[] ToBytes(this float num) => BitConverter.GetBytes(num); 38 | 39 | public static byte[] ToBytes(this double num) => BitConverter.GetBytes(num); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/WebQQCore/Util/Extensions/CollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace iQQ.Net.WebQQCore.Util.Extensions 9 | { 10 | public static class CollectionExtensions 11 | { 12 | public static bool IsNullOrEmpty(this ICollection col) 13 | { 14 | return col == null || col.Count == 0; 15 | } 16 | 17 | public static void AddWhenNotNull(this ICollection col, T item) 18 | { 19 | if(item != null) col.Add(item); 20 | } 21 | 22 | public static void AddRangeSafely(this ICollection col, IEnumerable items) 23 | { 24 | if(items == null) return; 25 | 26 | var list = col as List; 27 | if (list != null) 28 | { 29 | list.AddRange(items); 30 | } 31 | else 32 | { 33 | foreach (var item in items) 34 | { 35 | col.Add(item); 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/WebQQCore/Util/Extensions/LoggerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace iQQ.Net.WebQQCore.Util.Extensions 7 | { 8 | public static class LoggerExtensions 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/WebQQCore/Util/Extensions/Md5Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace iQQ.Net.WebQQCore.Util.Extensions 9 | { 10 | public static class Md5Extensions 11 | { 12 | public static string ToMd5String(this byte[] input) 13 | { 14 | return input.ToMd5Bytes().ToHexString(); 15 | } 16 | 17 | public static byte[] ToMd5Bytes(this byte[] input) 18 | { 19 | return MD5.Create().ComputeHash(input); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/WebQQCore/Util/Extensions/ObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace iQQ.Net.WebQQCore.Util.Extensions 8 | { 9 | public static class ObjectExtensions 10 | { 11 | public static T GetOrDefault(this T obj, T defaultValue) 12 | { 13 | return obj == null ? defaultValue : obj; 14 | } 15 | 16 | public static bool IsDefault(this T obj) 17 | { 18 | return obj.Equals(default(T)); 19 | } 20 | 21 | public static bool IsNull(this object obj) 22 | { 23 | return obj == null; 24 | } 25 | 26 | public static bool IsNullOrDefault(this T? obj) where T :struct 27 | { 28 | return obj == null || obj.Value.Equals(default(T)); 29 | } 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/WebQQCore/Util/Extensions/StreamExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace iQQ.Net.WebQQCore.Util.Extensions 9 | { 10 | public static class StreamExtensions 11 | { 12 | public static string ToString(this Stream stream, Encoding encoding = null) 13 | { 14 | using (var sr = new StreamReader(stream, encoding ?? Encoding.UTF8)) 15 | { 16 | return sr.ReadToEnd(); 17 | } 18 | } 19 | 20 | public static byte[] ToBytes(this Stream stream) 21 | { 22 | var bytes = new byte[stream.Length]; 23 | stream.Read(bytes, 0, bytes.Length); 24 | // 设置当前流的位置为流的开始 25 | stream.Seek(0, SeekOrigin.Begin); 26 | return bytes; 27 | } 28 | 29 | public static Stream ToStream(this byte[] bytes) 30 | { 31 | return new MemoryStream(bytes); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/WebQQCore/Util/Extensions/StringBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace iQQ.Net.WebQQCore.Util.Extensions 8 | { 9 | public static class StringBuilderExtensions 10 | { 11 | public static StringBuilder AppendLineIf(this StringBuilder sb, string value, bool condition) 12 | { 13 | if (condition) 14 | { 15 | sb.AppendLine(value); 16 | } 17 | return sb; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/WebQQCore/Util/HttpConstants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace iQQ.Net.WebQQCore.Util 7 | { 8 | public enum HttpMethodType 9 | { 10 | Get, 11 | Post, 12 | Put, 13 | Delete, 14 | Head, 15 | Options, 16 | Trace 17 | } 18 | 19 | /// 20 | /// 返回类型 21 | /// 22 | public enum ResponseResultType 23 | { 24 | String, 25 | Byte, 26 | Stream, 27 | } 28 | 29 | public abstract class HttpConstants 30 | { 31 | public const string UserAgent = "User-Agent"; 32 | public const string Referrer = "Referer"; 33 | public const string Post = "POST"; 34 | public const string Get = "GET"; 35 | public const string ContentType = "Content-Type"; 36 | public const string ContentLength = "Content-Length"; 37 | public const string SetCookie = "Set-Cookie"; 38 | public const string Origin = "Origin"; 39 | public const string Cookie = "Cookie"; 40 | public const string DefaultGetContentType = "application/json; charset=utf-8"; 41 | public const string DefaultPostContentType = "application/x-www-form-urlencoded"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/WebQQCore/Util/RetryHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace iQQ.Net.WebQQCore.Util 8 | { 9 | public static class RetryHelper 10 | { 11 | public static T Retry(Func func, int retryTimes, TimeSpan ts) 12 | { 13 | var exceptions = new List(); 14 | for (var i = 0; i < retryTimes; i++) 15 | { 16 | try 17 | { 18 | return func(); 19 | } 20 | catch (Exception ex) 21 | { 22 | Thread.Sleep(ts); 23 | exceptions.Add(ex); 24 | } 25 | } 26 | throw new AggregateException(exceptions); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/WebQQCore/Util/RobotType.cs: -------------------------------------------------------------------------------- 1 | /************************************************************************************ 2 | * 创建人: huoshan12345 3 | * 电子邮箱:89009143@qq.com 4 | * 创建时间:2015/2/26 18:24:01 5 | * 描述: 6 | /************************************************************************************/ 7 | 8 | namespace iQQ.Net.WebQQCore.Util 9 | { 10 | public enum RobotType 11 | { 12 | Moli, 13 | Turing, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/WebQQCore/Util/UrlUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace iQQ.Net.WebQQCore.Util 8 | { 9 | public class UrlUtils 10 | { 11 | public static string GetOrigin(string url) 12 | { 13 | return url.Substring(0, url.LastIndexOf("/")); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Actions/ActionResult/SyncCheckResult.cs: -------------------------------------------------------------------------------- 1 | namespace WebWeChat.Im.Actions.ActionResult 2 | { 3 | public enum SyncCheckResult 4 | { 5 | /// 6 | /// 什么都没有 7 | /// 8 | Nothing = 0, 9 | 10 | /// 11 | /// 新消息 12 | /// 13 | NewMsg = 2, 14 | 15 | /// 16 | /// 正在使用手机微信 17 | /// 18 | UsingPhone = 7, 19 | 20 | /// 21 | /// 红包 22 | /// 23 | RedEnvelope = 6, 24 | 25 | /// 26 | /// 已离线 27 | /// 28 | Offline = 1100, 29 | 30 | /// 31 | /// 被踢 32 | /// 33 | Kicked = 1101, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Actions/ActionResult/WatiForLoginResult.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace WebWeChat.Im.Actions.ActionResult 4 | { 5 | public enum WatiForLoginResult 6 | { 7 | /// 8 | /// 手机已允许登录 9 | /// 10 | [Description("手机已允许登录")] 11 | Success = 200, 12 | 13 | /// 14 | /// 二维码失效 15 | /// 16 | [Description("二维码失效")] 17 | QRCodeInvalid = 408, 18 | 19 | /// 20 | /// 手机已扫码 21 | /// 22 | [Description("手机已扫码")] 23 | ScanCode = 201, 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Actions/GetQRCodeAction.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Threading.Tasks; 3 | using HttpAction.Core; 4 | using HttpAction.Event; 5 | using HttpAction; 6 | using WebWeChat.Im.Core; 7 | 8 | namespace WebWeChat.Im.Actions 9 | { 10 | /// 11 | /// 获取二维码 12 | /// 13 | [Description("获取二维码")] 14 | public class GetQRCodeAction : WebWeChatAction 15 | { 16 | public GetQRCodeAction(IWeChatContext context, ActionEventListener listener = null) : base(context, listener) 17 | { 18 | } 19 | 20 | protected override HttpRequestItem BuildRequest() 21 | { 22 | var req = new HttpRequestItem(HttpMethodType.Post, string.Format(ApiUrls.GetQRCode, Session.Uuid)); 23 | req.AddData("t", "webwx"); 24 | req.AddData("_", (Session.Seq++).ToString()); 25 | req.ResultType = HttpResultType.Byte; 26 | return req; 27 | } 28 | 29 | protected override Task HandleResponse(HttpResponseItem responseItem) 30 | { 31 | // return NotifyOkEventAsync(Image.FromStream(responseItem.ResponseStream)); 32 | return NotifyOkEventAsync(ImageSharp.Image.Load(responseItem.ResponseBytes)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Actions/GetUuidAction.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Text.RegularExpressions; 3 | using System.Threading.Tasks; 4 | using HttpAction.Core; 5 | using HttpAction.Event; 6 | using HttpAction; 7 | using WebWeChat.Im.Core; 8 | 9 | namespace WebWeChat.Im.Actions 10 | { 11 | /// 12 | /// 获取UUID 13 | /// 14 | [Description("获取UUID")] 15 | public class GetUuidAction : WebWeChatAction 16 | { 17 | private readonly Regex _reg = new Regex(@"window.QRLogin.code = (\d+); window.QRLogin.uuid = ""(\S+?)"""); 18 | 19 | public GetUuidAction(IWeChatContext context, ActionEventListener listener = null) : base(context, listener) 20 | { 21 | Session.Seq = Timestamp; 22 | } 23 | 24 | protected override HttpRequestItem BuildRequest() 25 | { 26 | var req = new HttpRequestItem(HttpMethodType.Post, ApiUrls.GetUuid); 27 | req.AddQueryValue("appid", ApiUrls.Appid); 28 | req.AddQueryValue("fun", "new"); 29 | req.AddQueryValue("lang", "zh_CN"); 30 | req.AddQueryValue("_", Session.Seq++); 31 | return req; 32 | } 33 | 34 | protected override Task HandleResponse(HttpResponseItem responseItem) 35 | { 36 | var str = responseItem.ResponseString; 37 | var match = _reg.Match(str); 38 | if (match.Success && match.Groups.Count > 2 && match.Groups[1].Value == "200") 39 | { 40 | Session.Uuid = match.Groups[2].Value; 41 | return NotifyOkEventAsync(); 42 | } 43 | return NotifyErrorEventAsync(WeChatErrorCode.ResponseError); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Actions/SendMsgAction.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Threading.Tasks; 3 | using FclEx.Extensions; 4 | using HttpAction.Core; 5 | using HttpAction.Event; 6 | using WebWeChat.Im.Bean; 7 | using WebWeChat.Im.Core; 8 | 9 | namespace WebWeChat.Im.Actions 10 | { 11 | [Description("发送消息")] 12 | public class SendMsgAction : WebWeChatAction 13 | { 14 | private readonly MessageSent _msg; 15 | 16 | public SendMsgAction(IWeChatContext context, MessageSent msg, ActionEventListener listener = null) : base(context, listener) 17 | { 18 | _msg = msg; 19 | } 20 | 21 | protected override HttpRequestItem BuildRequest() 22 | { 23 | var url = string.Format(ApiUrls.SendMsg, Session.BaseUrl); 24 | var obj = new 25 | { 26 | Session.BaseRequest, 27 | Msg = _msg 28 | }; 29 | var req = new HttpRequestItem(HttpMethodType.Post, url) 30 | { 31 | StringData = obj.ToJson(), 32 | ContentType = HttpConstants.JsonContentType 33 | }; 34 | return req; 35 | } 36 | 37 | protected override Task HandleResponse(HttpResponseItem response) 38 | { 39 | var json = response.ResponseString.ToJToken(); 40 | if (json["BaseResponse"]["Ret"].ToString() == "0") 41 | { 42 | return NotifyOkEventAsync(); 43 | } 44 | else 45 | { 46 | throw new WeChatException(WeChatErrorCode.ResponseError, response.ResponseString); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Actions/WebLoginAction.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Threading.Tasks; 3 | using System.Xml.Linq; 4 | using HttpAction.Core; 5 | using HttpAction.Event; 6 | using WebWeChat.Im.Core; 7 | using WebWeChat.Im.Module.Impl; 8 | 9 | namespace WebWeChat.Im.Actions 10 | { 11 | /// 12 | /// 获取登录参数 13 | /// 14 | [Description("获取登录参数")] 15 | public class WebLoginAction : WebWeChatAction 16 | { 17 | public WebLoginAction(IWeChatContext context, ActionEventListener listener = null) 18 | : base(context, listener) 19 | { 20 | } 21 | 22 | protected override HttpRequestItem BuildRequest() 23 | { 24 | return new HttpRequestItem(HttpMethodType.Get, Session.LoginUrl); 25 | } 26 | 27 | protected override Task HandleResponse(HttpResponseItem responseItem) 28 | { 29 | /* 30 | 31 | 0 32 | 33 | @crypt_c498484a_beffad67aa727e24f7c669c51d5c895f 34 | Lr2AUrW1+FSCmtHZ 35 | 463678295 36 | SSR%2BWEx6yJf8MTN2G2XjsWtRpXWQ0J6wBHc5BeHGL3gATmsW%2FMiFX0GBqWrmm7dN 37 | 1 38 | 39 | */ 40 | var str = responseItem.ResponseString; 41 | var root = XDocument.Parse(str).Root; 42 | Session.Skey = root.Element("skey").Value; 43 | Session.Sid = root.Element("wxsid").Value; 44 | Session.Uin = root.Element("wxuin").Value; 45 | Session.PassTicket = root.Element("pass_ticket").Value; 46 | 47 | Session.State = SessionState.Online; 48 | 49 | return NotifyOkEventAsync(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Bean/Appinfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace WebWeChat.Im.Bean 7 | { 8 | public class AppInfo 9 | { 10 | public string AppID { get; set; } 11 | public int Type { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Bean/GroupMember.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using FclEx.Extensions; 3 | 4 | namespace WebWeChat.Im.Bean 5 | { 6 | public class GroupMember 7 | { 8 | public int Uin { get; set; } 9 | 10 | /// 11 | /// 用户名称,一个"@"为好友,两个"@"为群组 12 | /// 13 | public string UserName { get; set; } 14 | 15 | /// 16 | /// 昵称 17 | /// 18 | public string NickName { get; set; } 19 | 20 | public long AttrStatus { get; set; } 21 | 22 | /// 23 | /// 用户名拼音缩写 24 | /// 25 | [JsonProperty(PropertyName = "PYInitial")] 26 | public string PyInitial { get; set; } 27 | 28 | /// 29 | /// 用户名拼音全拼 30 | /// 31 | [JsonProperty(PropertyName = "PYQuanPin")] 32 | public string PyQuanPin { get; set; } 33 | 34 | /// 35 | /// 备注拼音缩写 36 | /// 37 | [JsonProperty(PropertyName = "RemarkPYInitial")] 38 | public string RemarkPyInitial { get; set; } 39 | 40 | /// 41 | /// 备注拼音全拼 42 | /// 43 | [JsonProperty(PropertyName = "RemarkPYQuanPin")] 44 | public string RemarkPyQuanPin { get; set; } 45 | 46 | public int MemberStatus { get; set; } 47 | 48 | public string DisplayName { get; set; } 49 | 50 | public string KeyWord { get; set; } 51 | 52 | public virtual string ShowName => DisplayName.IsNullOrEmpty() ? (NickName.IsNullOrEmpty() ? UserName : DisplayName) : NickName; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Bean/MessageSent.cs: -------------------------------------------------------------------------------- 1 | using FclEx.Extensions; 2 | using System; 3 | 4 | namespace WebWeChat.Im.Bean 5 | { 6 | /// 7 | /// 要发送的消息 8 | /// 9 | public class MessageSent 10 | { 11 | public MessageType Type { get; } 12 | public string Content { get; } 13 | public string FromUserName { get; } 14 | public string ToUserName { get; } 15 | public string LocalID { get; } 16 | public string ClientMsgId { get; } 17 | 18 | private MessageSent() 19 | { 20 | var time = DateTime.Now.ToTimestampMilli(); 21 | var random = new Random().Next(0, 9999).ToString("d4"); 22 | LocalID = $"{time}{random}"; 23 | ClientMsgId = LocalID; 24 | } 25 | 26 | public MessageSent(MessageType type, string content, string fromUserName, string toUserName) 27 | : this() 28 | { 29 | Type = type; 30 | Content = content; 31 | FromUserName = fromUserName; 32 | ToUserName = toUserName; 33 | } 34 | 35 | public static MessageSent CreateTextMsg(string content, string fromUserName, string toUserName) 36 | { 37 | return new MessageSent(MessageType.Text, content, fromUserName, toUserName); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Bean/RecommendInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace WebWeChat.Im.Bean 7 | { 8 | /// 9 | /// 名片信息 10 | /// 11 | public class RecommendInfo 12 | { 13 | public string UserName { get; set; } 14 | public string NickName { get; set; } 15 | public int QQNum { get; set; } 16 | public string Province { get; set; } 17 | public string City { get; set; } 18 | public string Content { get; set; } 19 | public string Signature { get; set; } 20 | public string Alias { get; set; } 21 | public int Scene { get; set; } 22 | public int VerifyFlag { get; set; } 23 | public long AttrStatus { get; set; } 24 | public int Sex { get; set; } 25 | public string Ticket { get; set; } 26 | public int OpCode { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Core/ApiUrls.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace WebWeChat.Im.Core 7 | { 8 | public static class ApiUrls 9 | { 10 | public const string Appid = "wx782c26e4c19acffb"; 11 | public const string GetUuid = "https://login.weixin.qq.com/jslogin"; 12 | public const string GetQRCode = "https://login.weixin.qq.com/qrcode/{0}"; 13 | public const string CheckQRCode = "https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login"; 14 | public const string WebwxInit = "{0}/webwxinit?pass_ticket={1}&skey={2}&r={3}"; 15 | public const string StatusNotify = "{0}/webwxstatusnotify?lang=zh_CN&pass_ticket={1}"; 16 | public const string GetContact = "{0}/webwxgetcontact?pass_ticket={1}&skey={2}&r={3}"; 17 | public const string BatchGetContact = "{0}/webwxbatchgetcontact?type=ex&pass_ticket={1}&r={2}"; 18 | // public const string SyncCheck = "{0}/synccheck"; 19 | public const string WebwxSync = "{0}/webwxsync?sid={1}&skey={2}&lang=zh_CN&pass_ticket={3}"; 20 | // public const string WebwxSync = "{0}/webwxsync"; 21 | public const string SendMsg = "{0}/webwxsendmsg"; 22 | 23 | 24 | public static readonly string[] SyncHosts = 25 | { 26 | "webpush.wx.qq.com", 27 | "webpush.wx2.qq.com", 28 | "webpush.wx8.qq.com", 29 | "webpush.web2.wechat.com", 30 | "webpush.web.wechat.com", 31 | //"webpush.wechat.com", 32 | //"webpush1.wechat.com", 33 | //"webpush2.wechat.com", 34 | //"webpush1.wechatapp.com", 35 | //"webpush.wechatapp.com", 36 | }; 37 | 38 | public const string TulingRobot = "http://www.tuling123.com/openapi/api"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Core/IWeChatContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.Extensions.Logging; 4 | using WebWeChat.Im.Event; 5 | using WebWeChat.Im.Module; 6 | using WebWeChat.Im.Module.Interface; 7 | using WebWeChat.Im.Service.Interface; 8 | 9 | namespace WebWeChat.Im.Core 10 | { 11 | public interface IWeChatContext 12 | { 13 | Task FireNotifyAsync(WeChatNotifyEvent notifyEvent); 14 | 15 | T GetSerivce(); 16 | 17 | T GetModule() where T : IWeChatModule; 18 | 19 | ILogger Logger { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Core/IWebWeChatClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using WebWeChat.Im.Module.Interface; 4 | 5 | namespace WebWeChat.Im.Core 6 | { 7 | public interface IWebWeChatClient : IDisposable, IWeChatContext, ILoginModule, IContactModule, IChatModule 8 | { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Core/WeChatErrorCode.cs: -------------------------------------------------------------------------------- 1 | namespace WebWeChat.Im.Core 2 | { 3 | public enum WeChatErrorCode 4 | { 5 | /// 6 | /// 响应错误 7 | /// 8 | ResponseError, 9 | 10 | /// 11 | /// 网络错误 12 | /// 13 | IoError, 14 | 15 | /// 16 | /// 参数错误 17 | /// 18 | ParameterError, 19 | 20 | /// 21 | /// Cookie错误 22 | /// 23 | CookieError, 24 | 25 | Timeout, // 等待超时 26 | 27 | JsonError, // JSON解析出错 28 | 29 | /// 30 | /// 未知错误 31 | /// 32 | UnknownError, 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Event/WeChatNotifyEvent.cs: -------------------------------------------------------------------------------- 1 | using FclEx.Extensions; 2 | using System; 3 | using System.Collections.Concurrent; 4 | 5 | namespace WebWeChat.Im.Event 6 | { 7 | 8 | public class WeChatNotifyEvent : EventArgs 9 | { 10 | private static readonly ConcurrentDictionary EmptyEvents 11 | = new ConcurrentDictionary(); 12 | 13 | public WeChatNotifyEventType Type { get; } 14 | public object Target { get; } 15 | 16 | private WeChatNotifyEvent(WeChatNotifyEventType type, object target = null) 17 | { 18 | Type = type; 19 | Target = target; 20 | } 21 | 22 | public override string ToString() 23 | { 24 | return $"{Type.GetFullDescription()}, target={Target ?? ""}]"; 25 | } 26 | 27 | public static WeChatNotifyEvent CreateEvent(WeChatNotifyEventType type, object target = null) 28 | { 29 | return target == null ? EmptyEvents.GetOrAdd(type, key => new WeChatNotifyEvent(key)) : new WeChatNotifyEvent(type, target); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Event/WeChatNotifyEventListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using WebWeChat.Im.Core; 6 | 7 | namespace WebWeChat.Im.Event 8 | { 9 | public delegate Task WeChatNotifyEventListener(IWebWeChatClient sender, WeChatNotifyEvent e); 10 | } 11 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Event/WeChatNotifyEventType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace WebWeChat.Im.Event 8 | { 9 | /// 10 | /// QQ通知事件类型 11 | /// 12 | [Description("微信通知事件类型")] 13 | public enum WeChatNotifyEventType 14 | { 15 | /// 16 | /// 二维码已就绪 17 | /// 18 | [Description("二维码已就绪")] 19 | QRCodeReady, 20 | 21 | /// 22 | /// 二维码失效 23 | /// 24 | [Description("二维码失效")] 25 | QRCodeInvalid, 26 | 27 | /// 28 | /// 二维码验证成功 29 | /// 30 | [Description("二维码验证成功")] 31 | QRCodeSuccess, 32 | 33 | /// 34 | /// 错误 35 | /// 36 | [Description("错误")] 37 | Error, 38 | 39 | /// 40 | /// 离线 41 | /// 42 | [Description("离线")] 43 | Offline, 44 | 45 | /// 46 | /// 登录成功 47 | /// 48 | [Description("登录成功")] 49 | LoginSuccess, 50 | 51 | /// 52 | /// 重新登录成功 53 | /// 54 | [Description("重新登录成功")] 55 | ReloginSuccess, 56 | 57 | /// 58 | /// 消息 59 | /// 60 | [Description("消息")] 61 | Message, 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Module/Impl/ChatModule.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using HttpAction.Event; 3 | using WebWeChat.Im.Bean; 4 | using WebWeChat.Im.Core; 5 | using WebWeChat.Im.Module.Interface; 6 | using HttpAction; 7 | using WebWeChat.Im.Actions; 8 | 9 | namespace WebWeChat.Im.Module.Impl 10 | { 11 | public class ChatModule : WeChatModule, IChatModule 12 | { 13 | public ChatModule(IWeChatContext context) : base(context) 14 | { 15 | } 16 | 17 | public Task SendMsg(MessageSent msg, ActionEventListener listener = null) 18 | { 19 | return new SendMsgAction(Context, msg, listener).ExecuteAsyncAuto(); 20 | } 21 | 22 | public Task GetRobotReply(RobotType robotType, string input, ActionEventListener listener = null) 23 | { 24 | return new GetTuringRobotReplyAction(Context, input).ExecuteAsyncAuto(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Module/Impl/ContactModule.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using HttpAction.Event; 3 | using WebWeChat.Im.Core; 4 | using WebWeChat.Im.Module.Interface; 5 | using HttpAction; 6 | using WebWeChat.Im.Actions; 7 | 8 | namespace WebWeChat.Im.Module.Impl 9 | { 10 | public class ContactModule : WeChatModule, IContactModule 11 | { 12 | public Task GetContact(ActionEventListener listener = null) 13 | { 14 | // 如果直接new一个Action并执行的话也可以,但是不能自动重试 15 | return new WebWeChatActionFuture(Context, listener) 16 | .PushAction() 17 | .ExecuteAsync(); 18 | } 19 | 20 | public Task GetGroupMember(ActionEventListener listener = null) 21 | { 22 | return new WebWeChatActionFuture(Context, listener) 23 | .PushAction() 24 | .ExecuteAsync(); 25 | } 26 | 27 | public ContactModule(IWeChatContext context) : base(context) 28 | { 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Module/Impl/LoginModule.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huoshan12345/WebQQWeChat/cecfd5deb7843ad5a9b0546ae4f073b9a7cd5413/src/WebWeChat/Im/Module/Impl/LoginModule.cs -------------------------------------------------------------------------------- /src/WebWeChat/Im/Module/Impl/StoreModule.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using WebWeChat.Im.Bean; 5 | using WebWeChat.Im.Core; 6 | using WebWeChat.Util; 7 | 8 | namespace WebWeChat.Im.Module.Impl 9 | { 10 | public class StoreModule : WeChatModule 11 | { 12 | /// 13 | /// 联系人总数 14 | /// 15 | public int MemberCount { get; set; } 16 | 17 | /// 18 | /// 存放联系人 19 | /// 主键是member的username 20 | /// 21 | public ConcurrentDictionary ContactMemberDic { get; set; } = new ConcurrentDictionary(); 22 | 23 | /// 24 | /// 特殊账号 25 | /// 26 | public IEnumerable SpecialUsers => ContactMemberDic.Values.Where(m => m.IsSpecialUser()); 27 | 28 | public int SpecialUserCount => SpecialUsers.Count(); 29 | 30 | /// 31 | /// 群 32 | /// 33 | public IEnumerable Groups => ContactMemberDic.Values.Where(m => m.IsGroup()); 34 | 35 | public int GroupCount => Groups.Count(); 36 | 37 | /// 38 | /// 好友 39 | /// 40 | public IEnumerable Friends => ContactMemberDic.Values.Where(m => !m.IsSpecialUser() && !m.IsGroup() && !m.IsPublicUsers()); 41 | 42 | public int FriendCount => Friends.Count(); 43 | 44 | /// 45 | /// 公众号/服务号 46 | /// 47 | public IEnumerable PublicUsers => ContactMemberDic.Values.Where(m => m.IsPublicUsers()); 48 | 49 | public int PublicUserCount => PublicUsers.Count(); 50 | 51 | public StoreModule(IWeChatContext context) : base(context) 52 | { 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Module/Impl/WeChatModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Logging; 4 | using WebWeChat.Im.Core; 5 | using WebWeChat.Im.Module.Interface; 6 | 7 | namespace WebWeChat.Im.Module.Impl 8 | { 9 | public abstract class WeChatModule : IWeChatModule 10 | { 11 | protected WeChatModule(IWeChatContext context) 12 | { 13 | Context = context ?? throw new ArgumentNullException(nameof(context)); 14 | } 15 | 16 | public IWeChatContext Context { get; } 17 | protected ILogger Logger => Context.GetSerivce(); 18 | protected IConfigurationRoot Config => Context.GetSerivce(); 19 | protected SessionModule Session => Context.GetModule(); 20 | protected StoreModule Store => Context.GetModule(); 21 | 22 | public virtual void Dispose() 23 | { 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Module/Interface/IChatModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using HttpAction.Event; 6 | using WebWeChat.Im.Bean; 7 | using WebWeChat.Im.Module.Impl; 8 | 9 | namespace WebWeChat.Im.Module.Interface 10 | { 11 | public interface IChatModule : IWeChatModule 12 | { 13 | Task SendMsg(MessageSent msg, ActionEventListener listener = null); 14 | 15 | Task GetRobotReply(RobotType robotType, string input, ActionEventListener listener = null); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Module/Interface/IContactModule.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using HttpAction.Event; 3 | 4 | namespace WebWeChat.Im.Module.Interface 5 | { 6 | public interface IContactModule: IWeChatModule 7 | { 8 | Task GetContact(ActionEventListener listener = null); 9 | 10 | Task GetGroupMember(ActionEventListener listener = null); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Module/Interface/ILoginModule.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using HttpAction.Event; 3 | 4 | namespace WebWeChat.Im.Module.Interface 5 | { 6 | public interface ILoginModule: IWeChatModule 7 | { 8 | /// 9 | /// 登录,扫码方式 10 | /// 11 | /// 12 | /// 13 | Task Login(ActionEventListener listener = null); 14 | 15 | /// 16 | /// 开始保持微信在线并检查新消息,即挂微信 17 | /// 18 | void BeginSyncCheck(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Module/Interface/IWeChatModule.cs: -------------------------------------------------------------------------------- 1 | using WebWeChat.Im.Core; 2 | using WebWeChat.Im.Service.Interface; 3 | 4 | namespace WebWeChat.Im.Module.Interface 5 | { 6 | /// 7 | /// 模块功能接口 8 | /// 9 | public interface IWeChatModule : IWeChatService 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/RobotType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace WebWeChat.Im 6 | { 7 | public enum RobotType 8 | { 9 | Tuling, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Service/Impl/WeChatActionFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using HttpAction.Actions; 3 | using WebWeChat.Im.Actions; 4 | using WebWeChat.Im.Core; 5 | using WebWeChat.Im.Service.Interface; 6 | 7 | namespace WebWeChat.Im.Service.Impl 8 | { 9 | public class WeChatActionFactory : ActionFactory, IWeChatActionFactory 10 | { 11 | public IWeChatContext Context { get;} 12 | 13 | public WeChatActionFactory(IWeChatContext context) 14 | { 15 | Context = context; 16 | } 17 | 18 | public override IAction CreateAction(params object[] parameters) 19 | { 20 | var type = typeof(T); 21 | if (typeof(WebWeChatAction).GetTypeInfo().IsAssignableFrom(type)) 22 | { 23 | var newArgs = new object[parameters.Length + 1]; 24 | newArgs[0] = Context; 25 | parameters.CopyTo(newArgs, 1); 26 | parameters = newArgs; 27 | } 28 | return base.CreateAction(parameters); 29 | } 30 | 31 | public void Dispose() 32 | { 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Service/Impl/WeChatConsoleLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Logging; 3 | using WebWeChat.Im.Core; 4 | using WebWeChat.Im.Module.Impl; 5 | using WebWeChat.Im.Service.Interface; 6 | using FclEx.Extensions; 7 | using FclEx.Logger; 8 | 9 | namespace WebWeChat.Im.Service.Impl 10 | { 11 | public class WeChatConsoleLogger : SimpleConsoleLogger, IWeChatService 12 | { 13 | public IWeChatContext Context { get; } 14 | 15 | public WeChatConsoleLogger(IWeChatContext context, LogLevel minLevel = LogLevel.Information) : base("WeChat", minLevel) 16 | { 17 | Context = context; 18 | } 19 | 20 | /// 21 | /// :warning: 22 | /// 23 | /// 24 | /// 25 | /// 26 | protected override string GetMessage(string message, Exception exception) 27 | { 28 | var userName = Context?.GetModule().User?.NickName; 29 | var prefix = userName.IsNullOrEmpty() ? string.Empty : $"[{userName}]"; 30 | return $"{DateTime.Now:HH:mm:ss}> {prefix}{message}"; 31 | } 32 | 33 | public void Dispose() 34 | { 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Service/Interface/IWeChatActionFactory.cs: -------------------------------------------------------------------------------- 1 | using HttpAction.Actions; 2 | 3 | namespace WebWeChat.Im.Service.Interface 4 | { 5 | public interface IWeChatActionFactory : IActionFactory, IWeChatService 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/WebWeChat/Im/Service/Interface/IWeChatService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using WebWeChat.Im.Core; 6 | 7 | namespace WebWeChat.Im.Service.Interface 8 | { 9 | /// 10 | /// 就是用来表示该服务是属于一个微信实例的 11 | /// 从而在微信实例销毁的时候服务也能一起销毁 12 | /// 如果是全局的服务(即多个实例共享的)请不要继承该接口 13 | /// 14 | public interface IWeChatService : IDisposable 15 | { 16 | IWeChatContext Context { get; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/WebWeChat/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | 3 | namespace WebWeChat 4 | { 5 | public class Startup 6 | { 7 | public static IConfigurationRoot BuildConfig() 8 | { 9 | var builder = new ConfigurationBuilder() 10 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); 11 | 12 | if (builder.GetFileProvider().GetFileInfo("project.json")?.Exists == true) 13 | { 14 | builder.AddUserSecrets(); 15 | } 16 | return builder.Build(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/WebWeChat/WebWeChat.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | WebWeChat-20161027025319 6 | 2.0.0-beta1 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tools/gitlink/GitLink.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huoshan12345/WebQQWeChat/cecfd5deb7843ad5a9b0546ae4f073b9a7cd5413/tools/gitlink/GitLink.exe -------------------------------------------------------------------------------- /tools/nuget/nuget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huoshan12345/WebQQWeChat/cecfd5deb7843ad5a9b0546ae4f073b9a7cd5413/tools/nuget/nuget.exe --------------------------------------------------------------------------------