├── .gitattributes ├── .gitignore ├── Wechat.sln ├── Wechat ├── API │ ├── RPC │ │ ├── BaseRequest.cs │ │ ├── BaseResponse.cs │ │ ├── BatchGetContact │ │ │ ├── BatchGetContactRequest.cs │ │ │ └── BatchGetContactResponse.cs │ │ ├── GetContact │ │ │ └── GetContactResponse.cs │ │ ├── SendMsg │ │ │ ├── SendMsgRequest.cs │ │ │ └── SendMsgResponse.cs │ │ ├── SendMsgImg │ │ │ ├── SendMsgImgRequest.cs │ │ │ └── SendMsgImgResponse.cs │ │ ├── Statusnotify │ │ │ ├── StatusnotifyRequest.cs │ │ │ └── StatusnotifyResponse.cs │ │ ├── Sync │ │ │ ├── SyncRequest.cs │ │ │ └── SyncResponse.cs │ │ ├── SyncCheck │ │ │ └── SyncCheckResponse.cs │ │ ├── Uploadmedia │ │ │ ├── UploadmediaRequest.cs │ │ │ └── UploadmediaResponse.cs │ │ ├── init │ │ │ ├── InitRequest.cs │ │ │ └── initResponse.cs │ │ └── oplog │ │ │ ├── OplogRequest.cs │ │ │ └── OplogResponse.cs │ ├── Result │ │ ├── LoginRedirectResult.cs │ │ └── LoginResult.cs │ ├── WechatAPIService.cs │ └── Wx │ │ ├── AddMsg.cs │ │ ├── AppInfo.cs │ │ ├── BatchUser.cs │ │ ├── ImgMsg.cs │ │ ├── Msg.cs │ │ ├── RecommendInfo.cs │ │ ├── SyncKey.cs │ │ └── User.cs ├── Properties │ └── AssemblyInfo.cs ├── Wechat.csproj ├── Wechat │ ├── Contact.cs │ ├── Events.cs │ ├── Group.cs │ ├── Message.cs │ └── WeChatClient.cs ├── packages.config └── tools │ └── UniversalTool.cs └── WechatTest ├── App.config ├── LoginForm.Designer.cs ├── LoginForm.cs ├── LoginForm.resx ├── Program.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── Simsimi.cs ├── WechatTest.csproj └── packages.config /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Microsoft Azure ApplicationInsights config file 170 | ApplicationInsights.config 171 | 172 | # Windows Store app package directory 173 | AppPackages/ 174 | BundleArtifacts/ 175 | 176 | # Visual Studio cache files 177 | # files ending in .cache can be ignored 178 | *.[Cc]ache 179 | # but keep track of directories ending in .cache 180 | !*.[Cc]ache/ 181 | 182 | # Others 183 | ClientBin/ 184 | [Ss]tyle[Cc]op.* 185 | ~$* 186 | *~ 187 | *.dbmdl 188 | *.dbproj.schemaview 189 | *.pfx 190 | *.publishsettings 191 | node_modules/ 192 | orleans.codegen.cs 193 | 194 | # RIA/Silverlight projects 195 | Generated_Code/ 196 | 197 | # Backup & report files from converting an old project file 198 | # to a newer Visual Studio version. Backup files are not needed, 199 | # because we have git ;-) 200 | _UpgradeReport_Files/ 201 | Backup*/ 202 | UpgradeLog*.XML 203 | UpgradeLog*.htm 204 | 205 | # SQL Server files 206 | *.mdf 207 | *.ldf 208 | 209 | # Business Intelligence projects 210 | *.rdl.data 211 | *.bim.layout 212 | *.bim_*.settings 213 | 214 | # Microsoft Fakes 215 | FakesAssemblies/ 216 | 217 | # GhostDoc plugin setting file 218 | *.GhostDoc.xml 219 | 220 | # Node.js Tools for Visual Studio 221 | .ntvs_analysis.dat 222 | 223 | # Visual Studio 6 build log 224 | *.plg 225 | 226 | # Visual Studio 6 workspace options file 227 | *.opt 228 | 229 | # Visual Studio LightSwitch build output 230 | **/*.HTMLClient/GeneratedArtifacts 231 | **/*.DesktopClient/GeneratedArtifacts 232 | **/*.DesktopClient/ModelManifest.xml 233 | **/*.Server/GeneratedArtifacts 234 | **/*.Server/ModelManifest.xml 235 | _Pvt_Extensions 236 | 237 | # LightSwitch generated files 238 | GeneratedArtifacts/ 239 | ModelManifest.xml 240 | 241 | # Paket dependency manager 242 | .paket/paket.exe 243 | 244 | # FAKE - F# Make 245 | .fake/ -------------------------------------------------------------------------------- /Wechat.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.9 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wechat", "Wechat\Wechat.csproj", "{A48C55F5-02AB-49EA-B77A-F6A9CC81B1E4}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WechatTest", "WechatTest\WechatTest.csproj", "{7241DDB9-6CAF-4D68-8E06-FFAFBC1ACEFE}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {A48C55F5-02AB-49EA-B77A-F6A9CC81B1E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {A48C55F5-02AB-49EA-B77A-F6A9CC81B1E4}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {A48C55F5-02AB-49EA-B77A-F6A9CC81B1E4}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {A48C55F5-02AB-49EA-B77A-F6A9CC81B1E4}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {7241DDB9-6CAF-4D68-8E06-FFAFBC1ACEFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {7241DDB9-6CAF-4D68-8E06-FFAFBC1ACEFE}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {7241DDB9-6CAF-4D68-8E06-FFAFBC1ACEFE}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {7241DDB9-6CAF-4D68-8E06-FFAFBC1ACEFE}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Wechat/API/RPC/BaseRequest.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 Wechat.API.RPC 8 | { 9 | public class BaseRequest 10 | { 11 | public string Uin; 12 | public string Sid; 13 | public string Skey; 14 | public string DeviceID; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Wechat/API/RPC/BaseResponse.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 Wechat.API.RPC 8 | { 9 | public class BaseResponse 10 | { 11 | public int ret; 12 | public string ErrMsg; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Wechat/API/RPC/BatchGetContact/BatchGetContactRequest.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 Wechat.API.RPC 8 | { 9 | 10 | public class BatchGetContactRequest 11 | { 12 | public BaseRequest BaseRequest; 13 | public int Count; 14 | public BatchUser[] List; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Wechat/API/RPC/BatchGetContact/BatchGetContactResponse.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 Wechat.API.RPC 8 | { 9 | public class BatchGetContactResponse 10 | { 11 | public BaseResponse BaseResponse; 12 | public int Count; 13 | public User[] ContactList; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Wechat/API/RPC/GetContact/GetContactResponse.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 Wechat.API.RPC 8 | { 9 | public class GetContactResponse 10 | { 11 | public BaseResponse BaseResponse; 12 | public int MemberCount; 13 | public User[] MemberList; 14 | public int Seq; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Wechat/API/RPC/SendMsg/SendMsgRequest.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 Wechat.API.RPC 8 | { 9 | public class SendMsgRequest 10 | { 11 | public BaseRequest BaseRequest; 12 | public Msg Msg; 13 | public long rr; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Wechat/API/RPC/SendMsg/SendMsgResponse.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 Wechat.API.RPC 8 | { 9 | public class SendMsgResponse 10 | { 11 | public BaseResponse BaseResponse; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Wechat/API/RPC/SendMsgImg/SendMsgImgRequest.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 Wechat.API.RPC 8 | { 9 | public class SendMsgImgRequest 10 | { 11 | public BaseRequest BaseRequest; 12 | public ImgMsg Msg; 13 | public int Scene; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Wechat/API/RPC/SendMsgImg/SendMsgImgResponse.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 Wechat.API.RPC 8 | { 9 | public class SendMsgImgResponse 10 | { 11 | public BaseResponse BaseResponse; 12 | public string MsgID; 13 | public long LocalID; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Wechat/API/RPC/Statusnotify/StatusnotifyRequest.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 Wechat.API.RPC 8 | { 9 | public class StatusnotifyRequest 10 | { 11 | public BaseRequest BaseRequest; 12 | public long ClientMsgId; 13 | public string FromUserName; 14 | public string ToUserName; 15 | public int Code; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Wechat/API/RPC/Statusnotify/StatusnotifyResponse.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 Wechat.API.RPC 8 | { 9 | public class StatusnotifyResponse 10 | { 11 | public BaseResponse BaseResponse; 12 | public long MsgID; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Wechat/API/RPC/Sync/SyncRequest.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 Wechat.API.RPC 8 | { 9 | public class SyncRequest 10 | { 11 | public BaseRequest BaseRequest; 12 | public SyncKey SyncKey; 13 | public long rr; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Wechat/API/RPC/Sync/SyncResponse.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 Wechat.API.RPC 8 | { 9 | public class SyncResponse 10 | { 11 | public BaseResponse BaseResponse; 12 | public int AddMsgCount; 13 | public AddMsg[] AddMsgList; 14 | public int ContinueFlag; 15 | public int DelContactCount; 16 | public User[] DelContactList; 17 | public int ModChatRoomMemberCount; 18 | //public ChatRoomMember[] ModChatRoomMemberList; 19 | public int ModContactCount; 20 | public User[] ModContactList; 21 | public string Skey; 22 | public SyncKey SyncKey; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Wechat/API/RPC/SyncCheck/SyncCheckResponse.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 Wechat.API.RPC 8 | { 9 | public class SyncCheckResponse 10 | { 11 | public string retcode; 12 | public string selector; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Wechat/API/RPC/Uploadmedia/UploadmediaRequest.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 Wechat.API.RPC 8 | { 9 | public class UploadmediaRequest 10 | { 11 | public int UploadType; 12 | public BaseRequest BaseRequest; 13 | public long ClientMediaId; 14 | public int TotalLen; 15 | public int StartPos; 16 | public int DataLen; 17 | public int MediaType; 18 | public string FromUserName; 19 | public string ToUserName; 20 | public string FileMd5; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Wechat/API/RPC/Uploadmedia/UploadmediaResponse.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 Wechat.API.RPC 8 | { 9 | public class UploadmediaResponse 10 | { 11 | public BaseResponse BaseResponse; 12 | public string MediaId; 13 | public int CDNThumbImgHeight; 14 | public int CDNThumbImgWidth; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Wechat/API/RPC/init/InitRequest.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 Wechat.API.RPC 8 | { 9 | public class InitRequest 10 | { 11 | public BaseRequest BaseRequest; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Wechat/API/RPC/init/initResponse.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 Wechat.API.RPC 8 | { 9 | public class InitResponse 10 | { 11 | public BaseResponse BaseResponse; 12 | public int Count; 13 | public User[] ContactList; 14 | public SyncKey SyncKey; 15 | public User User; 16 | public string ChatSet; 17 | public string SKey; 18 | public long ClientVersion; 19 | public long SystemTime; 20 | public int GrayScale; 21 | public int InviteStartCount; 22 | public int MPSubscribeMsgCount; 23 | //public MPSubscribeMsg[] MPSubscribeMsgList; 24 | public int ClickReportInterval; 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Wechat/API/RPC/oplog/OplogRequest.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 Wechat.API.RPC 8 | { 9 | public class OplogRequest 10 | { 11 | public BaseRequest BaseRequest; 12 | public int CmdId; 13 | public int OP; 14 | public string UserName; 15 | public string RemarkName;// 设置备注名,CmdId == 2时可用 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Wechat/API/RPC/oplog/OplogResponse.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 Wechat.API.RPC 8 | { 9 | public class OplogResponse 10 | { 11 | public BaseResponse BaseResponse; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Wechat/API/Result/LoginRedirectResult.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 Wechat.API 8 | { 9 | public class LoginRedirectResult 10 | { 11 | public string skey; 12 | public string wxsid; 13 | public string wxuin; 14 | public string pass_ticket; 15 | public string isgrayscale; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Wechat/API/Result/LoginResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | namespace Wechat.API 7 | { 8 | public class LoginResult 9 | { 10 | /// 11 | /// 201: 已扫描但是未确认登录 12 | /// 200: 已确认登录 13 | /// 14 | public int code; 15 | /// 16 | /// 以base64编码的用户头像图片 17 | /// code=201 时可用 18 | /// 19 | public string UserAvatar; 20 | /// 21 | /// 登录重定向Url 22 | /// code=200 时可用 23 | /// 24 | public string redirect_uri; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Wechat/API/WechatAPIService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Wechat.API.RPC; 5 | using System.Drawing; 6 | using System.IO; 7 | using Newtonsoft.Json; 8 | using Newtonsoft.Json.Linq; 9 | using System.Collections.Specialized; 10 | using System.Net.Http; 11 | using Wechat.tools; 12 | using System.Net; 13 | using System.Collections; 14 | 15 | namespace Wechat.API 16 | { 17 | public class WechatAPIService 18 | { 19 | HttpClientHandler mHandler; 20 | HttpClient mHttpClient; 21 | public WechatAPIService() { 22 | InitHttpClient(); 23 | } 24 | 25 | private void InitHttpClient() 26 | { 27 | mHandler = new HttpClientHandler(); 28 | mHandler.UseCookies = true; 29 | mHandler.AutomaticDecompression = DecompressionMethods.GZip; 30 | mHandler.AllowAutoRedirect = true; 31 | mHttpClient = new HttpClient(mHandler); 32 | mHttpClient.DefaultRequestHeaders.ExpectContinue = false; 33 | SetHttpHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"); 34 | SetHttpHeader("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4,ja;q=0.2"); 35 | SetHttpHeader("Accept-Encoding", "gzip, deflate, sdch, br"); 36 | } 37 | 38 | 39 | /// 40 | /// 获得二维码登录SessionID,使用此ID可以获得登录二维码 41 | /// 42 | /// Session 43 | public string GetNewQRLoginSessionID() 44 | { 45 | //respone like this => window.QRLogin.code = 200; window.QRLogin.uuid = "Qa_GBH_IqA=="; 46 | string url = "https://login.wx.qq.com/jslogin?appid=wx782c26e4c19acffb"; 47 | 48 | SetHttpHeader("Accept", "*/*"); 49 | mHttpClient.DefaultRequestHeaders.Referrer = new Uri("https://wx.qq.com/"); 50 | 51 | string str = GetString(url); 52 | if (str == null) return null; 53 | var pairs = str.Split(new string[] { "\"" }, StringSplitOptions.None); 54 | if (pairs.Length >= 2) { 55 | string sessionID = pairs[1]; 56 | return sessionID; 57 | } 58 | return null; 59 | } 60 | 61 | /// 62 | /// 获得登录二维码URL 63 | /// 64 | /// 65 | /// 66 | public string GetQRCodeUrl(string QRLoginSessionID) { 67 | string url = "https://login.weixin.qq.com/qrcode/" + QRLoginSessionID; 68 | return url; 69 | } 70 | 71 | /// 72 | /// 获得登录二维码图片 73 | /// 74 | /// 75 | /// 76 | public Image GetQRCodeImage(string QRLoginSessionID) 77 | { 78 | string url = GetQRCodeUrl(QRLoginSessionID); 79 | SetHttpHeader("Accept", "image/webp,image/*,*/*;q=0.8"); 80 | mHttpClient.DefaultRequestHeaders.Referrer = new Uri("https://wx.qq.com/"); 81 | try 82 | { 83 | HttpResponseMessage response = mHttpClient.GetAsync(new Uri(url)).Result; 84 | var bytes = response.Content.ReadAsByteArrayAsync().Result; 85 | if (bytes != null && bytes.Length > 0) { 86 | return Image.FromStream(new MemoryStream(bytes)); 87 | } 88 | return null; 89 | } 90 | catch { 91 | InitHttpClient(); 92 | return null; 93 | } 94 | 95 | } 96 | 97 | /// 98 | /// 登录检查 99 | /// 100 | /// 101 | /// 102 | public LoginResult Login(string QRLoginSessionID,long t) 103 | { 104 | string url = string.Format("https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid={0}&tip={1}&r={2}&_={3}", 105 | QRLoginSessionID,"0",t/1579,t); 106 | 107 | SetHttpHeader("Accept", "*/*"); 108 | mHttpClient.DefaultRequestHeaders.Referrer = new Uri("https://wx.qq.com/"); 109 | 110 | 111 | string login_result = GetString(url); 112 | if (login_result == null) return null; 113 | 114 | LoginResult result = new LoginResult(); 115 | result.code = 408; 116 | if (login_result.Contains("window.code=201")) //已扫描 未登录 117 | { 118 | string base64_image = login_result.Split(new string[] { "\'" }, StringSplitOptions.None)[1].Split(',')[1]; 119 | result.code = 201; 120 | result.UserAvatar = base64_image; 121 | } else if (login_result.Contains("window.code=200")) //已扫描 已登录 122 | { 123 | string login_redirect_url = login_result.Split(new string[] { "\"" }, StringSplitOptions.None)[1]; 124 | result.code = 200; 125 | result.redirect_uri = login_redirect_url; 126 | } 127 | 128 | return result; 129 | } 130 | 131 | public LoginRedirectResult LoginRedirect(string redirect_uri) 132 | { 133 | SetHttpHeader("Accept", "application/json, text/plain, */*"); 134 | SetHttpHeader("Connection", "keep-alive"); 135 | mHttpClient.DefaultRequestHeaders.Referrer = new Uri("https://wx.qq.com/"); 136 | 137 | string url = redirect_uri + "&fun=new&version=v2&lang=zh_CN"; 138 | string rep = GetString(url); 139 | if (rep == null) return null; 140 | 141 | LoginRedirectResult result = new LoginRedirectResult(); 142 | result.pass_ticket = rep.Split(new string[] { "pass_ticket" }, StringSplitOptions.None)[1].TrimStart('>').TrimEnd('<', '/'); 143 | result.skey = rep.Split(new string[] { "skey" }, StringSplitOptions.None)[1].TrimStart('>').TrimEnd('<', '/'); 144 | result.wxsid = rep.Split(new string[] { "wxsid" }, StringSplitOptions.None)[1].TrimStart('>').TrimEnd('<', '/'); 145 | result.wxuin = rep.Split(new string[] { "wxuin" }, StringSplitOptions.None)[1].TrimStart('>').TrimEnd('<', '/'); 146 | result.isgrayscale = rep.Split(new string[] { "isgrayscale" }, StringSplitOptions.None)[1].TrimStart('>').TrimEnd('<', '/'); 147 | return result; 148 | } 149 | 150 | /// 151 | /// 初始化 152 | /// 153 | /// 154 | /// 155 | /// 156 | /// 157 | /// 158 | /// 159 | public InitResponse Init(string pass_ticket,BaseRequest baseReq) 160 | { 161 | SetHttpHeader("Accept", "application/json, text/plain, */*"); 162 | SetHttpHeader("Connection", "keep-alive"); 163 | SetHttpHeader("Accept-Encoding", "gzip, deflate, br"); 164 | mHttpClient.DefaultRequestHeaders.Referrer = new Uri("https://wx.qq.com/"); 165 | 166 | string url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r={0}&pass_ticket={1}"; 167 | url = string.Format(url, getR(), pass_ticket); 168 | InitRequest initReq = new InitRequest(); 169 | initReq.BaseRequest = baseReq; 170 | string requestJson = JsonConvert.SerializeObject(initReq); 171 | string repJsonStr = PostString(url, requestJson); 172 | if (repJsonStr == null) return null; 173 | var rep = JsonConvert.DeserializeObject(repJsonStr); 174 | return rep; 175 | } 176 | 177 | /// 178 | /// 获得联系人列表 179 | /// 180 | /// 181 | /// 182 | /// 183 | 184 | public GetContactResponse GetContact(string pass_ticket,string skey) 185 | { 186 | SetHttpHeader("Accept", "application/json, text/plain, */*"); 187 | SetHttpHeader("Connection", "keep-alive"); 188 | SetHttpHeader("Accept-Encoding", "gzip, deflate, br"); 189 | string url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?pass_ticket={0}&r={1}&seq=0&skey={2}"; 190 | url = string.Format(url, pass_ticket, getR(), skey); 191 | string json = GetString(url); 192 | var rep = JsonConvert.DeserializeObject(json); 193 | if (rep == null) return null; 194 | return rep; 195 | } 196 | 197 | /// 198 | /// 批量获取联系人详细信息 199 | /// 200 | /// 201 | /// 202 | /// 203 | /// 204 | /// 205 | /// 206 | /// 207 | public BatchGetContactResponse BatchGetContact(string[] requestContacts,string pass_ticket,BaseRequest baseReq) 208 | { 209 | SetHttpHeader("Accept", "application/json, text/plain, */*"); 210 | SetHttpHeader("Connection", "keep-alive"); 211 | SetHttpHeader("Accept-Encoding", "gzip, deflate, br"); 212 | string url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact?type=ex&r={0}&lang=zh_CN&pass_ticket={1}"; 213 | url = string.Format(url, getR(), pass_ticket); 214 | 215 | BatchGetContactRequest req = new BatchGetContactRequest(); 216 | req.BaseRequest = baseReq; 217 | req.Count = requestContacts.Length; 218 | 219 | List requestUsers = new List(); 220 | for (int i = 0; i < req.Count; i++) { 221 | var tmp = new BatchUser(); 222 | tmp.UserName = requestContacts[i]; 223 | requestUsers.Add(tmp); 224 | } 225 | 226 | req.List = requestUsers.ToArray(); 227 | string requestJson = JsonConvert.SerializeObject(req); 228 | string repJsonStr = PostString(url, requestJson); 229 | var rep = JsonConvert.DeserializeObject(repJsonStr); 230 | if (rep == null) return null; 231 | return rep; 232 | } 233 | 234 | 235 | public SyncCheckResponse SyncCheck(SyncItem[] syncItems,BaseRequest baseReq,long syncCheckTimes) 236 | { 237 | SetHttpHeader("Accept", "*/*"); 238 | SetHttpHeader("Connection", "keep-alive"); 239 | SetHttpHeader("Accept-Encoding", "gzip, deflate, br"); 240 | 241 | string synckey = ""; 242 | for (int i = 0; i < syncItems.Length; i++) { 243 | if (i != 0) { 244 | synckey += "|"; 245 | } 246 | synckey += syncItems[i].Key + "_" + syncItems[i].Val; 247 | } 248 | string url = "https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?skey={0}&sid={1}&uin={2}&deviceid={3}&synckey={4}&_={5}&r={6}"; 249 | url = string.Format(url,UrlEncode(baseReq.Skey), UrlEncode(baseReq.Sid), baseReq.Uin, baseReq.DeviceID,synckey,syncCheckTimes,Util.GetTimeStamp()); 250 | string repStr =GetString(url); 251 | if (repStr == null) return null; 252 | SyncCheckResponse rep = new SyncCheckResponse(); 253 | if (repStr.StartsWith("window.synccheck=")) 254 | { 255 | repStr = repStr.Substring("window.synccheck=".Length); 256 | rep = JsonConvert.DeserializeObject(repStr); 257 | } 258 | 259 | return rep; 260 | } 261 | 262 | static long getR() { 263 | return Util.GetTimeStamp(); 264 | } 265 | 266 | public SyncResponse Sync(SyncKey syncKey,string pass_ticket,BaseRequest baseReq) 267 | { 268 | SetHttpHeader("Accept", "application/json, text/plain, */*"); 269 | SetHttpHeader("Connection", "keep-alive"); 270 | SetHttpHeader("Accept-Encoding", "gzip, deflate, br"); 271 | SetHttpHeader("Origin", "https://wx.qq.com"); 272 | 273 | string url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid={0}&skey={1}&lang=zh_CN&pass_ticket={2}"; 274 | url = string.Format(url,baseReq.Sid,baseReq.Skey,pass_ticket); 275 | SyncRequest req = new SyncRequest(); 276 | req.BaseRequest = baseReq; 277 | req.SyncKey = syncKey; 278 | req.rr = ~getR(); 279 | string requestJson = JsonConvert.SerializeObject(req); 280 | string repJsonStr = PostString(url, requestJson); 281 | if (repJsonStr == null) return null; 282 | var rep = JsonConvert.DeserializeObject(repJsonStr); 283 | 284 | 285 | 286 | 287 | return rep; 288 | } 289 | 290 | public StatusnotifyResponse Statusnotify(string formUser,string toUser,string pass_ticket,BaseRequest baseReq) 291 | { 292 | SetHttpHeader("Accept", "application/json, text/plain, */*"); 293 | SetHttpHeader("Connection", "keep-alive"); 294 | SetHttpHeader("Accept-Encoding", "gzip, deflate, br"); 295 | SetHttpHeader("Origin", "https://wx.qq.com"); 296 | 297 | string url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify?lang=zh_CN&pass_ticket=" + pass_ticket; 298 | StatusnotifyRequest req = new StatusnotifyRequest(); 299 | req.BaseRequest = baseReq; 300 | req.ClientMsgId = getR(); 301 | req.FromUserName = formUser; 302 | req.ToUserName = toUser; 303 | req.Code = 3; 304 | string requestJson = JsonConvert.SerializeObject(req); 305 | string repJsonStr = PostString(url, requestJson); 306 | if (repJsonStr == null) return null; 307 | var rep = JsonConvert.DeserializeObject(repJsonStr); 308 | return rep; 309 | } 310 | 311 | public SendMsgResponse SendMsg(Msg msg, string pass_ticket,BaseRequest baseReq) 312 | { 313 | string url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?sid={0}&r={1}&lang=zh_CN&pass_ticket={2}"; 314 | url = string.Format(url, baseReq.Sid, getR(), pass_ticket); 315 | SendMsgRequest req = new SendMsgRequest(); 316 | req.BaseRequest = baseReq; 317 | req.Msg = msg; 318 | req.rr = DateTime.Now.Millisecond; 319 | string requestJson = JsonConvert.SerializeObject(req); 320 | string repJsonStr = PostString(url, requestJson); 321 | if (repJsonStr == null) return null; 322 | var rep = JsonConvert.DeserializeObject(repJsonStr); 323 | return rep; 324 | } 325 | 326 | 327 | public UploadmediaResponse Uploadmedia(string fromUserName,string toUserName,string id,string mime_type, int uploadType,int mediaType,byte[] buffer,string fileName,string pass_ticket,BaseRequest baseReq) { 328 | UploadmediaRequest req = new UploadmediaRequest(); 329 | req.BaseRequest = baseReq; 330 | req.ClientMediaId = getR(); 331 | req.DataLen = buffer.Length; 332 | req.StartPos = 0; 333 | req.TotalLen = buffer.Length; 334 | req.MediaType = mediaType; 335 | req.FromUserName = fromUserName; 336 | req.ToUserName = toUserName; 337 | req.UploadType = uploadType; 338 | req.FileMd5 = Util.getMD5(buffer); 339 | 340 | string url = "https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json"; 341 | string requestJson = JsonConvert.SerializeObject(req); 342 | string mt = "doc"; 343 | if (mime_type.StartsWith("image/")) { 344 | mt = "pic"; 345 | } 346 | var dataTicketCookie = GetCookie("webwx_data_ticket"); 347 | 348 | var dataContent = new MultipartFormDataContent(); 349 | dataContent.Add(new StringContent(id), "id"); 350 | dataContent.Add(new StringContent(fileName), "name"); 351 | dataContent.Add(new StringContent(mime_type), "type"); 352 | dataContent.Add(new StringContent("Thu Mar 17 2016 14:35:28 GMT+0800 (中国标准时间)"), "lastModifiedDate"); 353 | dataContent.Add(new StringContent(buffer.Length.ToString()), "size"); 354 | dataContent.Add(new StringContent(mt), "mediatype"); 355 | dataContent.Add(new StringContent(requestJson), "uploadmediarequest"); 356 | dataContent.Add(new StringContent(dataTicketCookie.Value), "webwx_data_ticket"); 357 | dataContent.Add(new StringContent(pass_ticket), "pass_ticket"); 358 | dataContent.Add(new ByteArrayContent(buffer), "filename", fileName + "\r\n Content - Type: " + mime_type); 359 | 360 | try 361 | { 362 | var response = mHttpClient.PostAsync(url, dataContent).Result; 363 | string repJsonStr = response.Content.ReadAsStringAsync().Result; 364 | var rep = JsonConvert.DeserializeObject(repJsonStr); 365 | return rep; 366 | } 367 | catch { 368 | InitHttpClient(); 369 | return null; 370 | } 371 | 372 | } 373 | 374 | 375 | public SendMsgImgResponse SendMsgImg(ImgMsg msg, string pass_ticket,BaseRequest baseReq) 376 | { 377 | string url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsgimg?fun=async&f=json&pass_ticket={0}"; 378 | url = string.Format(url,pass_ticket); 379 | SendMsgImgRequest req = new SendMsgImgRequest(); 380 | req.BaseRequest = baseReq; 381 | req.Msg = msg; 382 | req.Scene = 0; 383 | string requestJson = JsonConvert.SerializeObject(req); 384 | string repJsonStr = PostString(url, requestJson); 385 | if (repJsonStr == null) return null; 386 | var rep = JsonConvert.DeserializeObject(repJsonStr); 387 | 388 | return rep; 389 | } 390 | 391 | 392 | 393 | public OplogResponse Oplog(string userName,int cmdID,int op,string RemarkName, string pass_ticket,BaseRequest baseReq) 394 | { 395 | string url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxoplog?pass_ticket={0}"; 396 | url = string.Format(url, pass_ticket); 397 | OplogRequest req = new OplogRequest(); 398 | req.BaseRequest = baseReq; 399 | req.UserName = userName; 400 | req.CmdId = cmdID; 401 | req.OP = op; 402 | req.RemarkName = RemarkName; 403 | string requestJson = JsonConvert.SerializeObject(req); 404 | string repJsonStr = PostString(url, requestJson); 405 | if (repJsonStr == null) return null; 406 | var rep = JsonConvert.DeserializeObject(repJsonStr); 407 | return rep; 408 | } 409 | 410 | public void Logout(string skey,string sid,string uin) 411 | { 412 | 413 | string url = string.Format("https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=0&skey={0}", System.Web.HttpUtility.UrlEncode(skey)); 414 | string requestStr = string.Format("sid={0}&uin={1}",sid,uin); 415 | SetHttpHeader("Cache-Control", "max-age=0"); 416 | SetHttpHeader("Upgrade-Insecure-Requests", "1"); 417 | SetHttpHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); 418 | SetHttpHeader("Referer", "https://wx.qq.com/"); 419 | PostString(url, requestStr); 420 | 421 | url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=1&skey=" + System.Web.HttpUtility.UrlEncode(skey); 422 | PostString(url, requestStr); 423 | 424 | mHttpClient.DefaultRequestHeaders.Remove("Cache-Control"); 425 | mHttpClient.DefaultRequestHeaders.Remove("Upgrade-Insecure-Requests"); 426 | } 427 | 428 | 429 | 430 | private string GetString(string url) 431 | { 432 | // try 433 | // { 434 | HttpResponseMessage response = mHttpClient.GetAsync(new Uri(url)).Result; 435 | string ret = response.Content.ReadAsStringAsync().Result; 436 | response.Dispose(); 437 | return ret; 438 | // } 439 | //catch { 440 | // InitHttpClient(); 441 | // return null; 442 | //} 443 | 444 | } 445 | 446 | private string PostString(string url, string content) 447 | { 448 | //try 449 | //{ 450 | HttpResponseMessage response = mHttpClient.PostAsync(new Uri(url), new StringContent(content)).Result; 451 | string ret = response.Content.ReadAsStringAsync().Result; 452 | response.Dispose(); 453 | return ret; 454 | //} 455 | //catch 456 | // { 457 | // InitHttpClient(); 458 | // return null; 459 | // } 460 | 461 | } 462 | 463 | /// 464 | /// 获取指定cookie 465 | /// 466 | /// 467 | /// 468 | public Cookie GetCookie(string name) 469 | { 470 | List cookies = GetAllCookies(mHandler.CookieContainer); 471 | foreach (Cookie c in cookies) 472 | { 473 | if (c.Name == name) 474 | { 475 | return c; 476 | } 477 | } 478 | return null; 479 | } 480 | 481 | private static List GetAllCookies(CookieContainer cc) 482 | { 483 | List lstCookies = new List(); 484 | 485 | Hashtable table = (Hashtable)cc.GetType().InvokeMember("m_domainTable", 486 | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | 487 | System.Reflection.BindingFlags.Instance, null, cc, new object[] { }); 488 | 489 | foreach (object pathList in table.Values) 490 | { 491 | SortedList lstCookieCol = (SortedList)pathList.GetType().InvokeMember("m_list", 492 | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField 493 | | System.Reflection.BindingFlags.Instance, null, pathList, new object[] { }); 494 | foreach (CookieCollection colCookies in lstCookieCol.Values) 495 | foreach (Cookie c in colCookies) lstCookies.Add(c); 496 | } 497 | return lstCookies; 498 | } 499 | 500 | 501 | private void SetHttpHeader(string name,string value) 502 | { 503 | if (mHttpClient.DefaultRequestHeaders.Contains(name)) { 504 | mHttpClient.DefaultRequestHeaders.Remove(name); 505 | } 506 | 507 | mHttpClient.DefaultRequestHeaders.Add(name, value); 508 | } 509 | string UrlEncode(string url) 510 | { 511 | return System.Web.HttpUtility.UrlEncode(url); 512 | } 513 | 514 | } 515 | 516 | 517 | } 518 | -------------------------------------------------------------------------------- /Wechat/API/Wx/AddMsg.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 Wechat.API 8 | { 9 | public class AddMsg 10 | { 11 | public AppInfo AppInfo; 12 | public int AppMsgType; 13 | public string Content; 14 | public long CreateTime; 15 | public string FileName; 16 | public string FileSize; 17 | public int ForwardFlag; 18 | public string FromUserName; 19 | public int HasProductId; 20 | public int ImgHeight; 21 | public int ImgWidth; 22 | public int ImgStatus; 23 | public string MediaId; 24 | public string MsgId; 25 | public int MsgType; 26 | public string NewMsgId; 27 | public int PlayLength; 28 | public RecommendInfo RecommendInfo; 29 | public int Status; 30 | public int StatusNotifyCode; 31 | public string StatusNotifyUserName; 32 | public int SubMsgType; 33 | public string Ticket; 34 | public string ToUserName; 35 | public string Url; 36 | public int VoiceLength; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Wechat/API/Wx/AppInfo.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 Wechat.API 8 | { 9 | public class AppInfo 10 | { 11 | public string AppID; 12 | public int Type; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Wechat/API/Wx/BatchUser.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 Wechat.API 8 | { 9 | public class BatchUser 10 | { 11 | public string UserName; 12 | public int ChatRoomId; 13 | public string EncryChatRoomId; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Wechat/API/Wx/ImgMsg.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 Wechat.API 8 | { 9 | public class ImgMsg 10 | { 11 | public long ClientMsgId; 12 | public long LocalID; 13 | public string FromUserName; 14 | public string ToUserName; 15 | public int Type;//3 16 | public string MediaId; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Wechat/API/Wx/Msg.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 Wechat.API 8 | { 9 | public class Msg 10 | { 11 | public long ClientMsgId; 12 | public long LocalID; 13 | public string Content; 14 | public string FromUserName; 15 | public string ToUserName; 16 | public int Type; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Wechat/API/Wx/RecommendInfo.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 Wechat.API 8 | { 9 | public class RecommendInfo 10 | { 11 | public string Alias; 12 | public int AttrStatus; 13 | public string City; 14 | public string Content; 15 | public string NickName; 16 | public int OpCode; 17 | public string Province; 18 | public int QQNum; 19 | public int Scene; 20 | public int Sex; 21 | public string Signature; 22 | public string Ticket; 23 | public string UserName; 24 | public int VerifyFlag; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Wechat/API/Wx/SyncKey.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 Wechat.API 8 | { 9 | public class SyncItem { 10 | public int Key; 11 | public int Val; 12 | } 13 | public class SyncKey 14 | { 15 | public int Count; 16 | public SyncItem[] List; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Wechat/API/Wx/User.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 Wechat.API 8 | { 9 | public class User 10 | { 11 | public long Uin; 12 | public string UserName; 13 | public string NickName; 14 | public string HeadImgUrl; 15 | public int ContactFlag; 16 | public int MemberCount; 17 | public User[] MemberList; 18 | public string RemarkName; 19 | public int Sex; 20 | public string Signature; 21 | public int VerifyFlag; 22 | public int OwnerUin; 23 | public string PYInitial; 24 | public string PYQuanPin; 25 | public string RemarkPYInitial; 26 | public string RemarkPYQuanPin; 27 | public int StarFriend; 28 | public int AppAccountFlag; 29 | public int Statues; 30 | public long AttrStatus; 31 | public string Province; 32 | public string City; 33 | public string Alias; 34 | public int SnsFlag; 35 | public int UniFriend; 36 | public string DisplayName; 37 | public int ChatRoomId; 38 | public string KeyWord; 39 | public string EncryChatRoomId; 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Wechat/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("Wechat")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Wechat")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 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("a48c55f5-02ab-49ea-b77a-f6a9cc81b1e4")] 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 | -------------------------------------------------------------------------------- /Wechat/Wechat.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A48C55F5-02AB-49EA-B77A-F6A9CC81B1E4} 8 | Library 9 | Properties 10 | Wechat 11 | Wechat 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 35 | True 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 100 | -------------------------------------------------------------------------------- /Wechat/Wechat/Contact.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 Wechat 8 | { 9 | public class Contact 10 | { 11 | public virtual string NickName { get; set; } 12 | public string ID { get; set; } 13 | public string RemarkName { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Wechat/Wechat/Events.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 | 8 | namespace Wechat 9 | { 10 | 11 | public class WeChatClientEvent : EventArgs { } 12 | 13 | 14 | public class GetQRCodeImageEvent : WeChatClientEvent 15 | { 16 | public Image QRImage; 17 | } 18 | 19 | public class UserScanQRCodeEvent : WeChatClientEvent 20 | { 21 | public Image UserAvatarImage; 22 | } 23 | 24 | public class LoginSucessEvent : WeChatClientEvent 25 | { 26 | 27 | } 28 | 29 | public class InitedEvent : WeChatClientEvent 30 | { 31 | 32 | } 33 | 34 | public class AddMessageEvent:WeChatClientEvent 35 | { 36 | public Message Msg; 37 | } 38 | 39 | public class StatusChangedEvent:WeChatClientEvent 40 | { 41 | public ClientStatusType FromStatus; 42 | public ClientStatusType ToStatus; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Wechat/Wechat/Group.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 Wechat 8 | { 9 | public class Group : Contact 10 | { 11 | 12 | public override string NickName 13 | { 14 | get { 15 | if (string.IsNullOrWhiteSpace(base.NickName)) { 16 | if (Members != null && Members.Length > 0) { 17 | int max = Members.Length > 4 ? 4 : Members.Length; 18 | string newName = ""; 19 | for (int i = 0; i < max; i++) { 20 | newName = newName + Members[i].NickName; 21 | if (i != max - 1) newName += ","; 22 | } 23 | base.NickName = newName; 24 | } 25 | } 26 | return base.NickName; 27 | } 28 | 29 | set 30 | { 31 | base.NickName = value; 32 | } 33 | } 34 | 35 | public Contact[] Members { get; set; } 36 | 37 | public Contact GetMember(string ID) 38 | { 39 | if (Members == null) return null; 40 | for (int i = 0; i < Members.Length; i++) 41 | { 42 | if (Members[i].ID == ID) return Members[i]; 43 | } 44 | return null; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Wechat/Wechat/Message.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 Wechat 8 | { 9 | public class Message 10 | { 11 | public string FromContactID; 12 | public string ToContactD; 13 | } 14 | 15 | 16 | public class TextMessage : Message 17 | { 18 | public string Content; 19 | } 20 | 21 | public class DefaultMessage: Message 22 | { 23 | public int MsgType; 24 | public string Content; 25 | } 26 | 27 | public class MessageFactory 28 | { 29 | public static Message CreateMessage(Wechat.API.AddMsg msg) 30 | { 31 | //MsgType 32 | //1 文本消息 33 | //3 图片消息 34 | //34 语音消息 35 | //37 VERIFYMSG 36 | //40 POSSIBLEFRIEND_MSG 37 | //42 共享名片 38 | //43 视频通话消息 39 | //47 动画表情 40 | //48 位置消息 41 | //49 分享链接 42 | //50 VOIPMSG 43 | //51 微信初始化消息 44 | //52 VOIPNOTIFY 45 | //53 VOIPINVITE 46 | //62 小视频 47 | //9999 SYSNOTICE 48 | //10000 系统消息 49 | //10002 撤回消息 50 | 51 | Message ret = null; 52 | 53 | if (msg.MsgType == 1) { 54 | ret = new TextMessage() { 55 | Content = msg.Content 56 | }; 57 | 58 | } 59 | 60 | if (ret == null) 61 | { 62 | ret = new DefaultMessage() 63 | { 64 | MsgType = msg.MsgType, 65 | Content = msg.Content 66 | }; 67 | } 68 | ret.FromContactID = msg.FromUserName; 69 | ret.ToContactD = msg.ToUserName; 70 | return ret; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Wechat/Wechat/WeChatClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Drawing.Imaging; 4 | using System.IO; 5 | using Wechat.API; 6 | using Wechat.API.RPC; 7 | using System.Collections.Generic; 8 | using Wechat.tools; 9 | 10 | namespace Wechat 11 | { 12 | 13 | public enum ClientStatusType 14 | { 15 | GetUUID, 16 | GetQRCode, 17 | Login, 18 | QRCodeScaned, 19 | WeixinInit, 20 | SyncCheck, 21 | WeixinSync, 22 | None, 23 | } 24 | 25 | 26 | public class WeChatClient 27 | { 28 | 29 | 30 | 31 | public Action OnEvent; 32 | 33 | 34 | public Contact Self { get; private set; } 35 | 36 | public bool IsLogin { get; private set; } 37 | 38 | public ClientStatusType CurrentStatus 39 | { 40 | get 41 | { 42 | return mStatus; 43 | } 44 | private set 45 | { 46 | if (mStatus != value) 47 | { 48 | var changedEvent = new StatusChangedEvent() 49 | { 50 | FromStatus = mStatus, 51 | ToStatus = value 52 | }; 53 | mStatus = value; 54 | OnEvent?.Invoke(this, changedEvent); 55 | } 56 | } 57 | } 58 | 59 | 60 | 61 | private List mContacts; 62 | 63 | public Contact[] Contacts 64 | { 65 | get{ 66 | return mContacts.ToArray(); 67 | } 68 | private set { 69 | mContacts = new List(); 70 | mContacts.AddRange(value); 71 | } 72 | } 73 | 74 | private List mGroups; 75 | public Group[] Groups 76 | { 77 | get { 78 | return mGroups.ToArray(); 79 | } 80 | private set 81 | { 82 | mGroups = new List(); 83 | mGroups.AddRange(value); 84 | } 85 | } 86 | 87 | public Group GetGroup(string ID) 88 | { 89 | if (mGroups == null) return null; 90 | return mGroups.FindLast((group) => { 91 | return group.ID == ID; 92 | }); 93 | } 94 | 95 | public Contact GetContact(string ID) 96 | { 97 | if (ID == Self.ID) return Self; 98 | if (mContacts == null) return null; 99 | return mContacts.FindLast((contact) => 100 | { 101 | return contact.ID == ID; 102 | }); 103 | } 104 | 105 | /* 106 | * Web Weixin Pipeline 107 | +--------------+ +---------------+ +---------------+ 108 | | | | | | | 109 | | Get UUID | | Get Contact | | Status Notify | 110 | | | | | | | 111 | +-------+------+ +-------^-------+ +-------^-------+ 112 | | | | 113 | | +-------+ +--------+ 114 | | | | 115 | +-------v------+ +-----+--+------+ +--------------+ 116 | | | | | | | 117 | | Get QRCode | | Weixin Init +------> Sync Check<----+ 118 | | | | | | | | 119 | +-------+------+ +-------^-------+ +-------+------+ | 120 | | | | | 121 | | | +-----------+ 122 | | | | 123 | +-------v------+ +-------+--------+ +-------v-------+ 124 | | | Confirm Login | | | | 125 | +------> Login +---------------> New Login Page | | Weixin Sync | 126 | | | | | | | | 127 | | +------+-------+ +----------------+ +---------------+ 128 | | | 129 | |QRCode Scaned| 130 | +-------------+ 131 | */ 132 | 133 | private System.Threading.Thread mMainLoopThread; 134 | private ClientStatusType mStatus = ClientStatusType.None; 135 | public void Run() 136 | { 137 | Quit(); 138 | mIsQuit = false; 139 | IsLogin = false; 140 | CurrentStatus = ClientStatusType.GetUUID; 141 | mAPIService = new WechatAPIService(); 142 | mMainLoopThread = new System.Threading.Thread(MainLoop); 143 | mMainLoopThread.Start(); 144 | } 145 | 146 | public void Quit(bool force = false) 147 | { 148 | mIsQuit = true; 149 | Logout(); 150 | if (force) { 151 | if (mMainLoopThread != null && mMainLoopThread.IsAlive) { 152 | mMainLoopThread.Abort(); 153 | } 154 | } 155 | } 156 | 157 | 158 | 159 | private bool mIsQuit = false; 160 | private WechatAPIService mAPIService = null; 161 | private string mPass_ticket; 162 | private BaseRequest mBaseReq; 163 | private string mLoginSession; 164 | private void MainLoop() 165 | { 166 | while (!mIsQuit) 167 | { 168 | HandleStatus(); 169 | } 170 | } 171 | 172 | 173 | 174 | #region StatusHandle 175 | 176 | private void HandleStatus() 177 | { 178 | switch (CurrentStatus) 179 | { 180 | case ClientStatusType.GetUUID: 181 | HandleGetLoginSession(); 182 | break; 183 | case ClientStatusType.GetQRCode: 184 | HandleGetQRCode(); 185 | break; 186 | case ClientStatusType.Login: 187 | HandleLogin(); 188 | break; 189 | case ClientStatusType.QRCodeScaned: 190 | HandleQRCodeScaned(); 191 | break; 192 | case ClientStatusType.WeixinInit: 193 | HandleInit(); 194 | break; 195 | 196 | case ClientStatusType.WeixinSync: 197 | HandleSync(); 198 | break; 199 | } 200 | } 201 | 202 | 203 | private void HandleGetLoginSession() 204 | { 205 | IsLogin = false; 206 | mLoginSession = mAPIService.GetNewQRLoginSessionID(); 207 | if (!string.IsNullOrWhiteSpace(mLoginSession)) 208 | { 209 | CurrentStatus = ClientStatusType.GetQRCode; 210 | } 211 | } 212 | 213 | private void HandleGetQRCode() 214 | { 215 | var QRCodeImg = mAPIService.GetQRCodeImage(mLoginSession); 216 | if (QRCodeImg != null) 217 | { 218 | CurrentStatus = ClientStatusType.Login; 219 | var wce = new GetQRCodeImageEvent() 220 | { 221 | QRImage = QRCodeImg 222 | }; 223 | OnEvent?.Invoke(this, wce); 224 | } 225 | else { 226 | CurrentStatus = ClientStatusType.GetUUID; 227 | } 228 | } 229 | 230 | 231 | 232 | private void HandleLogin() 233 | { 234 | var loginResult = mAPIService.Login(mLoginSession,Util.GetTimeStamp()); 235 | if (loginResult!=null && loginResult.code == 201) 236 | { 237 | // 已扫描,但是未确认登录 238 | // convert base64 to image 239 | byte[] base64_image_bytes = Convert.FromBase64String(loginResult.UserAvatar); 240 | MemoryStream memoryStream = new MemoryStream(base64_image_bytes, 0, base64_image_bytes.Length); 241 | memoryStream.Write(base64_image_bytes, 0, base64_image_bytes.Length); 242 | var image = Image.FromStream(memoryStream); 243 | OnEvent?.Invoke(this, new UserScanQRCodeEvent() 244 | { 245 | UserAvatarImage = image 246 | }); 247 | 248 | CurrentStatus = ClientStatusType.QRCodeScaned; 249 | } 250 | else 251 | { 252 | CurrentStatus = ClientStatusType.GetUUID; 253 | } 254 | } 255 | 256 | private long mSyncCheckTimes = 0; 257 | private void HandleQRCodeScaned() 258 | { 259 | mSyncCheckTimes = Util.GetTimeStamp(); 260 | var loginResult = mAPIService.Login(mLoginSession,mSyncCheckTimes); 261 | if (loginResult != null && loginResult.code == 200) 262 | { 263 | // 登录成功 264 | var redirectResult = mAPIService.LoginRedirect(loginResult.redirect_uri); 265 | mBaseReq = new BaseRequest(); 266 | mBaseReq.Skey = redirectResult.skey; 267 | mBaseReq.Sid = redirectResult.wxsid; 268 | mBaseReq.Uin = redirectResult.wxuin; 269 | mBaseReq.DeviceID = CreateNewDeviceID(); 270 | mPass_ticket = redirectResult.pass_ticket; 271 | CurrentStatus = ClientStatusType.WeixinInit; 272 | OnEvent?.Invoke(this, new LoginSucessEvent()); 273 | } 274 | else 275 | { 276 | CurrentStatus = ClientStatusType.GetUUID; 277 | } 278 | } 279 | 280 | 281 | private void HandleInit() 282 | { 283 | var initResult = mAPIService.Init(mPass_ticket, mBaseReq); 284 | if (initResult!=null && initResult.BaseResponse.ret == 0) 285 | { 286 | Self = CreateContact(initResult.User); 287 | mSyncKey = initResult.SyncKey; 288 | // 开启系统通知 289 | var statusNotifyRep = mAPIService.Statusnotify(Self.ID, Self.ID, mPass_ticket, mBaseReq); 290 | if (statusNotifyRep != null && statusNotifyRep.BaseResponse != null && statusNotifyRep.BaseResponse.ret == 0) 291 | { 292 | CurrentStatus = ClientStatusType.WeixinSync; 293 | IsLogin = true; 294 | } 295 | else { 296 | CurrentStatus = ClientStatusType.GetUUID; 297 | return; 298 | } 299 | } 300 | else 301 | { 302 | CurrentStatus = ClientStatusType.GetUUID; 303 | return; 304 | } 305 | 306 | if (!InitContactAndGroups()) { 307 | CurrentStatus = ClientStatusType.WeixinInit; 308 | IsLogin = false; 309 | return; 310 | } 311 | 312 | 313 | OnEvent?.Invoke(this, new InitedEvent()); 314 | 315 | } 316 | 317 | 318 | private bool InitContactAndGroups() 319 | { 320 | mContacts = new List(); 321 | mGroups = new List(); 322 | 323 | var contactResult = mAPIService.GetContact(mPass_ticket, mBaseReq.Skey); 324 | if (contactResult == null || contactResult.BaseResponse == null || contactResult.BaseResponse.ret != 0){ 325 | return false; 326 | } 327 | 328 | List groupIDs = new List(); 329 | foreach (var user in contactResult.MemberList) 330 | { 331 | if (user.UserName.StartsWith("@@")){ 332 | groupIDs.Add(user.UserName); 333 | } 334 | else { 335 | var contact = CreateContact(user); 336 | mContacts.Add(contact); 337 | } 338 | } 339 | 340 | if (groupIDs.Count <= 0) return true; 341 | // 批量获得群成员详细信息 342 | var batchResult = mAPIService.BatchGetContact(groupIDs.ToArray(), mPass_ticket, mBaseReq); 343 | if (batchResult == null || batchResult.BaseResponse.ret != 0) return false; 344 | 345 | foreach (var user in batchResult.ContactList) 346 | { 347 | if (!user.UserName.StartsWith("@@") || user.MemberCount <= 0) continue; 348 | Group group = new Group(); 349 | group.ID = user.UserName; 350 | group.NickName = user.NickName; 351 | group.RemarkName = user.RemarkName; 352 | List groupMembers = new List(); 353 | foreach (var member in user.MemberList) 354 | { 355 | groupMembers.Add(CreateContact(member)); 356 | } 357 | group.Members = groupMembers.ToArray(); 358 | mGroups.Add(group); 359 | } 360 | 361 | return true; 362 | } 363 | 364 | 365 | private SyncKey mSyncKey; 366 | private void HandleSync() 367 | { 368 | if (mSyncKey == null) { 369 | CurrentStatus = ClientStatusType.GetUUID; 370 | return; 371 | } 372 | if (mSyncKey.Count <= 0) return; 373 | 374 | var checkResult = mAPIService.SyncCheck(mSyncKey.List, mBaseReq,++mSyncCheckTimes); 375 | if (checkResult == null) return; 376 | 377 | 378 | if (checkResult.retcode!=null && checkResult.retcode != "0") { 379 | CurrentStatus = ClientStatusType.GetUUID; 380 | return; 381 | } 382 | if (checkResult.selector == "0") return; 383 | var syncResult = mAPIService.Sync(mSyncKey, mPass_ticket, mBaseReq); 384 | if (syncResult == null) return; 385 | mSyncKey = syncResult.SyncKey; 386 | 387 | // 处理同步 388 | ProcessSyncResult(syncResult); 389 | 390 | } 391 | 392 | private void ProcessSyncResult(SyncResponse result) 393 | { 394 | // 处理消息 395 | if (result.AddMsgCount > 0) { 396 | foreach (var msg in result.AddMsgList) 397 | { 398 | var message = MessageFactory.CreateMessage(msg); 399 | OnEvent?.Invoke(this, new AddMessageEvent() { 400 | Msg = message 401 | }); 402 | } 403 | } 404 | } 405 | 406 | #endregion 407 | 408 | 409 | private static string CreateNewDeviceID() 410 | { 411 | Random ran = new Random(); 412 | int rand1 = ran.Next(10000, 99999); 413 | int rand2 = ran.Next(10000, 99999); 414 | int rand3 = ran.Next(10000, 99999); 415 | return string.Format("e{0}{1}{2}", rand1, rand2, rand3); 416 | } 417 | 418 | 419 | public Contact CreateContact(Wechat.API.User user) 420 | { 421 | 422 | Contact contact = new Contact(); 423 | contact.ID = user.UserName; 424 | contact.NickName = user.NickName; 425 | contact.RemarkName = user.RemarkName; 426 | return contact; 427 | } 428 | 429 | public Contact[] GetGroupMembers(string groupID) 430 | { 431 | 432 | //获取群聊成员 433 | var batchResult = mAPIService.BatchGetContact(new string[] { groupID}, mPass_ticket, mBaseReq); 434 | if (batchResult == null || batchResult.BaseResponse.ret != 0) return null; 435 | 436 | List members = new List(); 437 | foreach(var contact in batchResult.ContactList) 438 | { 439 | if (contact.UserName.StartsWith("@@")) continue; 440 | members.Add(CreateContact(contact)); 441 | } 442 | 443 | return members.ToArray(); 444 | 445 | } 446 | 447 | 448 | 449 | /// 450 | /// 置顶群聊 451 | /// 452 | /// 453 | public bool StickyPost(string groupUserName) 454 | { 455 | var rep = mAPIService.Oplog(groupUserName, 3, 0, null, mPass_ticket, mBaseReq); 456 | return rep.BaseResponse.ret == 0; 457 | } 458 | 459 | public bool SetRemarkName(string id, string remarkName) 460 | { 461 | var contact = GetContact(id); 462 | if (contact == null) return false; 463 | var rep = mAPIService.Oplog(id, 2, 0, remarkName, mPass_ticket, mBaseReq); 464 | if (rep.BaseResponse.ret == 0) { 465 | contact.RemarkName = remarkName; 466 | return true; 467 | } 468 | return false; 469 | } 470 | 471 | 472 | public bool SendMsg(string toUserName, string content) 473 | { 474 | Msg msg = new Msg(); 475 | msg.FromUserName = Self.ID; 476 | msg.ToUserName = toUserName; 477 | msg.Content = content; 478 | msg.ClientMsgId = DateTime.Now.Millisecond; 479 | msg.LocalID = DateTime.Now.Millisecond; 480 | msg.Type = 1;//type 1 文本消息 481 | var response = mAPIService.SendMsg(msg, mPass_ticket, mBaseReq); 482 | if (response != null && response.BaseResponse != null && response.BaseResponse.ret == 0) 483 | { 484 | return true; 485 | } 486 | else 487 | { 488 | return false; 489 | } 490 | } 491 | 492 | int upLoadMediaCount = 0; 493 | 494 | public bool SendMsg(string toUserName, Image img, ImageFormat format = null, string imageName = null) 495 | { 496 | if (img == null) return false; 497 | string fileName = imageName != null ? imageName : "img_" + upLoadMediaCount; 498 | var imgFormat = format != null ? format : ImageFormat.Png; 499 | 500 | fileName += "." + imgFormat.ToString().ToLower(); 501 | 502 | MemoryStream ms = new MemoryStream(); 503 | img.Save(ms, imgFormat); 504 | ms.Seek(0, SeekOrigin.Begin); 505 | byte[] data = new byte[ms.Length]; 506 | int readCount = ms.Read(data, 0, data.Length); 507 | if (readCount != data.Length) return false; 508 | 509 | string mimetype = "image/" + imgFormat.ToString().ToLower(); 510 | var response = mAPIService.Uploadmedia(Self.ID, toUserName, "WU_FILE_" + upLoadMediaCount, mimetype, 2, 4, data, fileName, mPass_ticket, mBaseReq); 511 | if (response != null && response.BaseResponse != null && response.BaseResponse.ret == 0) 512 | { 513 | upLoadMediaCount++; 514 | string mediaId = response.MediaId; 515 | ImgMsg msg = new ImgMsg(); 516 | msg.FromUserName = Self.ID; 517 | msg.ToUserName = toUserName; 518 | msg.MediaId = mediaId; 519 | msg.ClientMsgId = DateTime.Now.Millisecond; 520 | msg.LocalID = DateTime.Now.Millisecond; 521 | msg.Type = 3; 522 | var sendImgRep = mAPIService.SendMsgImg(msg, mPass_ticket, mBaseReq); 523 | if (sendImgRep != null && sendImgRep.BaseResponse != null && sendImgRep.BaseResponse.ret == 0) 524 | { 525 | return true; 526 | } 527 | return false; 528 | } 529 | else 530 | { 531 | return false; 532 | } 533 | } 534 | 535 | public void Logout() 536 | { 537 | if (!IsLogin || mMainLoopThread==null || !mMainLoopThread.IsAlive) return; 538 | mAPIService.Logout(mBaseReq.Skey, mBaseReq.Sid, mBaseReq.Uin); 539 | IsLogin = false; 540 | mContacts = null; 541 | mGroups = null; 542 | CurrentStatus = ClientStatusType.GetUUID; 543 | } 544 | } 545 | } 546 | -------------------------------------------------------------------------------- /Wechat/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Wechat/tools/UniversalTool.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 Wechat.tools 9 | { 10 | public class Util 11 | { 12 | public static string getMD5(byte[] data) { 13 | 14 | MD5 md5Hash = MD5.Create(); 15 | var hash = md5Hash.ComputeHash(data); 16 | 17 | // 创建一个 Stringbuilder 来收集字节并创建字符串 18 | StringBuilder sBuilder = new StringBuilder(); 19 | 20 | // 循环遍历哈希数据的每一个字节并格式化为十六进制字符串 21 | for (int i = 0; i < hash.Length; i++) { 22 | sBuilder.Append(hash[i].ToString("x2")); 23 | } 24 | 25 | // 返回十六进制字符串 26 | return sBuilder.ToString(); 27 | 28 | } 29 | 30 | /// 31 | /// 获取时间戳 32 | /// 33 | /// 34 | public static long GetTimeStamp() 35 | { 36 | TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); 37 | return Convert.ToInt64(ts.TotalMilliseconds); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /WechatTest/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /WechatTest/LoginForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace WechatTest 2 | { 3 | partial class LoginForm 4 | { 5 | /// 6 | /// 必需的设计器变量。 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// 清理所有正在使用的资源。 12 | /// 13 | /// 如果应释放托管资源,为 true;否则为 false。 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) { 17 | components.Dispose(); 18 | } 19 | base.Dispose(disposing); 20 | } 21 | 22 | #region Windows 窗体设计器生成的代码 23 | 24 | /// 25 | /// 设计器支持所需的方法 - 不要修改 26 | /// 使用代码编辑器修改此方法的内容。 27 | /// 28 | private void InitializeComponent() 29 | { 30 | this.pictureBox_qr = new System.Windows.Forms.PictureBox(); 31 | this.comboBox_users = new System.Windows.Forms.ComboBox(); 32 | this.button_send_msg_txt = new System.Windows.Forms.Button(); 33 | this.textBox_msg_text = new System.Windows.Forms.TextBox(); 34 | this.button_send_msg_image = new System.Windows.Forms.Button(); 35 | this.label_status = new System.Windows.Forms.Label(); 36 | this.button_refreshGroupMemberInfo = new System.Windows.Forms.Button(); 37 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox_qr)).BeginInit(); 38 | this.SuspendLayout(); 39 | // 40 | // pictureBox_qr 41 | // 42 | this.pictureBox_qr.Location = new System.Drawing.Point(77, 50); 43 | this.pictureBox_qr.Name = "pictureBox_qr"; 44 | this.pictureBox_qr.Size = new System.Drawing.Size(228, 202); 45 | this.pictureBox_qr.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; 46 | this.pictureBox_qr.TabIndex = 0; 47 | this.pictureBox_qr.TabStop = false; 48 | // 49 | // comboBox_users 50 | // 51 | this.comboBox_users.FormattingEnabled = true; 52 | this.comboBox_users.Location = new System.Drawing.Point(12, 312); 53 | this.comboBox_users.Name = "comboBox_users"; 54 | this.comboBox_users.Size = new System.Drawing.Size(365, 20); 55 | this.comboBox_users.TabIndex = 1; 56 | // 57 | // button_send_msg_txt 58 | // 59 | this.button_send_msg_txt.Location = new System.Drawing.Point(153, 352); 60 | this.button_send_msg_txt.Name = "button_send_msg_txt"; 61 | this.button_send_msg_txt.Size = new System.Drawing.Size(99, 23); 62 | this.button_send_msg_txt.TabIndex = 2; 63 | this.button_send_msg_txt.Text = "发送文字消息"; 64 | this.button_send_msg_txt.UseVisualStyleBackColor = true; 65 | this.button_send_msg_txt.Click += new System.EventHandler(this.button_send_msg_txt_Click); 66 | // 67 | // textBox_msg_text 68 | // 69 | this.textBox_msg_text.Location = new System.Drawing.Point(24, 353); 70 | this.textBox_msg_text.Name = "textBox_msg_text"; 71 | this.textBox_msg_text.Size = new System.Drawing.Size(123, 21); 72 | this.textBox_msg_text.TabIndex = 3; 73 | // 74 | // button_send_msg_image 75 | // 76 | this.button_send_msg_image.Location = new System.Drawing.Point(258, 352); 77 | this.button_send_msg_image.Name = "button_send_msg_image"; 78 | this.button_send_msg_image.Size = new System.Drawing.Size(119, 23); 79 | this.button_send_msg_image.TabIndex = 4; 80 | this.button_send_msg_image.Text = "发送图片消息"; 81 | this.button_send_msg_image.UseVisualStyleBackColor = true; 82 | this.button_send_msg_image.Click += new System.EventHandler(this.button_send_msg_image_Click); 83 | // 84 | // label_status 85 | // 86 | this.label_status.Location = new System.Drawing.Point(12, 256); 87 | this.label_status.Name = "label_status"; 88 | this.label_status.Size = new System.Drawing.Size(350, 53); 89 | this.label_status.TabIndex = 5; 90 | this.label_status.Text = "扫描二维码登陆微信"; 91 | this.label_status.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 92 | // 93 | // button_refreshGroupMemberInfo 94 | // 95 | this.button_refreshGroupMemberInfo.Location = new System.Drawing.Point(24, 381); 96 | this.button_refreshGroupMemberInfo.Name = "button_refreshGroupMemberInfo"; 97 | this.button_refreshGroupMemberInfo.Size = new System.Drawing.Size(107, 23); 98 | this.button_refreshGroupMemberInfo.TabIndex = 6; 99 | this.button_refreshGroupMemberInfo.Text = "设置备注名"; 100 | this.button_refreshGroupMemberInfo.UseVisualStyleBackColor = true; 101 | this.button_refreshGroupMemberInfo.Click += new System.EventHandler(this.button_refreshGroupMemberInfo_Click); 102 | // 103 | // LoginForm 104 | // 105 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 106 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 107 | this.ClientSize = new System.Drawing.Size(411, 461); 108 | this.Controls.Add(this.button_refreshGroupMemberInfo); 109 | this.Controls.Add(this.label_status); 110 | this.Controls.Add(this.button_send_msg_image); 111 | this.Controls.Add(this.textBox_msg_text); 112 | this.Controls.Add(this.button_send_msg_txt); 113 | this.Controls.Add(this.comboBox_users); 114 | this.Controls.Add(this.pictureBox_qr); 115 | this.Name = "LoginForm"; 116 | this.Text = "WeChat"; 117 | this.Load += new System.EventHandler(this.Form1_Load); 118 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox_qr)).EndInit(); 119 | this.ResumeLayout(false); 120 | this.PerformLayout(); 121 | 122 | } 123 | 124 | #endregion 125 | 126 | private System.Windows.Forms.PictureBox pictureBox_qr; 127 | private System.Windows.Forms.ComboBox comboBox_users; 128 | private System.Windows.Forms.Button button_send_msg_txt; 129 | private System.Windows.Forms.TextBox textBox_msg_text; 130 | private System.Windows.Forms.Button button_send_msg_image; 131 | private System.Windows.Forms.Label label_status; 132 | private System.Windows.Forms.Button button_refreshGroupMemberInfo; 133 | } 134 | } 135 | 136 | -------------------------------------------------------------------------------- /WechatTest/LoginForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using Wechat.API.Http; 11 | using Wechat.API; 12 | using Wechat; 13 | using System.IO; 14 | using System.Text.RegularExpressions; 15 | using System.Diagnostics; 16 | 17 | namespace WechatTest 18 | { 19 | public partial class LoginForm : Form 20 | { 21 | 22 | WechatClient wc; 23 | 24 | public LoginForm() 25 | { 26 | InitializeComponent(); 27 | } 28 | 29 | 30 | /// 清除文本中Html的标签 31 | /// 32 | /// 33 | /// 34 | protected string ClearHtml(string Content) 35 | { 36 | Content = Zxj_ReplaceHtml("&#[^>]*;", "", Content); 37 | Content = Zxj_ReplaceHtml("]*>", "", Content); 38 | Content = Zxj_ReplaceHtml("]*>", "", Content); 39 | Content = Zxj_ReplaceHtml("]*>", "", Content); 40 | Content = Zxj_ReplaceHtml("]*>", "", Content); 41 | Content = Zxj_ReplaceHtml("]*>", "", Content); 42 | Content = Zxj_ReplaceHtml(" ", "", Content); 43 | Content = Zxj_ReplaceHtml("]*>", "", Content); 44 | Content = Zxj_ReplaceHtml("]*>", "", Content); 45 | Content = Zxj_ReplaceHtml("]*>", "", Content); 46 | Content = Zxj_ReplaceHtml("]*>", "", Content); 47 | Content = Zxj_ReplaceHtml("]*>", "", Content); 48 | Content = Zxj_ReplaceHtml("]*>", "", Content); 49 | Content = Zxj_ReplaceHtml("]*>", "", Content); 50 | Content = Zxj_ReplaceHtml("]*>", "", Content); 51 | Content = Zxj_ReplaceHtml("]*>", "", Content); 52 | Content = Zxj_ReplaceHtml("]*>", "", Content); 53 | Content = Zxj_ReplaceHtml("]*>", "", Content); 54 | Content = Zxj_ReplaceHtml("]*>", "", Content); 55 | Content = Zxj_ReplaceHtml("(javascript|jscript|vbscript|vbs):", "", Content); 56 | Content = Zxj_ReplaceHtml("on(mouse|exit|error|click|key)", "", Content); 57 | Content = Zxj_ReplaceHtml("<\\?xml[^>]*>", "", Content); 58 | Content = Zxj_ReplaceHtml("<\\/?[a-z]+:[^>]*>", "", Content); 59 | Content = Zxj_ReplaceHtml("]*>", "", Content); 60 | Content = Zxj_ReplaceHtml("]*>", "", Content); 61 | Content = Zxj_ReplaceHtml("]*>", "", Content); 62 | Content = Zxj_ReplaceHtml("]*>", "", Content); 63 | Content = Zxj_ReplaceHtml("]*>", "", Content); 64 | string clearHtml = Content; 65 | return clearHtml; 66 | } 67 | 68 | /// 69 | /// 清除文本中的Html标签 70 | /// 71 | /// 要替换的标签正则表达式 72 | /// 替换为的内容 73 | /// 要替换的内容 74 | /// 75 | private string Zxj_ReplaceHtml(string patrn, string strRep, string content) 76 | { 77 | if (string.IsNullOrEmpty(content)) { 78 | content = ""; 79 | } 80 | Regex rgEx = new Regex(patrn, RegexOptions.IgnoreCase); 81 | string strTxt = rgEx.Replace(content, strRep); 82 | return strTxt; 83 | } 84 | 85 | 86 | private void Form1_Load(object sender, EventArgs e) 87 | { 88 | comboBox_users.AutoCompleteMode = AutoCompleteMode.SuggestAppend; 89 | comboBox_users.AutoCompleteSource = AutoCompleteSource.ListItems; 90 | 91 | wc = new WechatClient(); 92 | wc.OnGetQRCodeImage = (image) => { 93 | RunInMainthread(()=> { 94 | pictureBox_qr.Image = image; 95 | }); 96 | }; 97 | 98 | wc.OnUserScanQRCode = (image) => { 99 | RunInMainthread(() => { 100 | 101 | pictureBox_qr.Image = image; 102 | label_status.Text = "扫描成功\n请在手机上确认登陆"; 103 | }); 104 | }; 105 | 106 | wc.OnLoginSucess = () => { 107 | RunInMainthread(()=> { 108 | label_status.Text = "已确认,正在登陆...."; 109 | }); 110 | }; 111 | 112 | wc.OnInitComplate = () => { 113 | RunInMainthread(() => { 114 | label_status.Text = wc.CurrentUser.NickName+"("+wc.CurrentUser.Alias+")"; 115 | }); 116 | }; 117 | 118 | wc.OnRecvMsg = (msg) => { 119 | if (msg.ToUserName == wc.CurrentUser.UserName && !msg.FromUserName.StartsWith("@@")) { 120 | System.Diagnostics.Debug.WriteLine("RecvMsg:" + msg.Content + " from " + msg.FromUserName); 121 | string rep = ""; 122 | if (msg.Content == "1") { 123 | rep = "你"; 124 | } 125 | if (msg.Content == "2") { 126 | rep = "你好"; 127 | } 128 | if (msg.Content == "3") { 129 | rep = "你好吗"; 130 | } 131 | //var rep = Simsimi.say(msg.Content); 132 | wc.SendMsg(msg.FromUserName, rep); 133 | System.Diagnostics.Debug.WriteLine("SendMsg:" + rep + " to " + msg.FromUserName); 134 | } 135 | }; 136 | 137 | wc.OnAddUser = (user) => { 138 | RunInMainthread(() => { 139 | string nickName = ClearHtml(user.NickName); 140 | comboBox_users.Items.Add(nickName + "|" + user.UserName); 141 | }); 142 | }; 143 | 144 | 145 | RunAsync(()=>{ 146 | wc.Run(); 147 | }); 148 | } 149 | 150 | 151 | 152 | void RunAsync(Action action) { 153 | ((Action)(delegate () { 154 | action?.Invoke(); 155 | })).BeginInvoke(null, null); 156 | } 157 | 158 | void RunInMainthread(Action action) { 159 | this.BeginInvoke((Action)(delegate () { 160 | action?.Invoke(); 161 | })); 162 | } 163 | 164 | private void button_send_msg_txt_Click(object sender, EventArgs e) 165 | { 166 | string str = comboBox_users.Text; 167 | var args = str.Split('|'); 168 | if (args.Length == 2) { 169 | string userName = args[1]; 170 | wc.SendMsg(userName,textBox_msg_text.Text); 171 | } 172 | } 173 | 174 | private void button_send_msg_image_Click(object sender, EventArgs e) 175 | { 176 | string str = comboBox_users.Text; 177 | var args = str.Split('|'); 178 | if (args.Length == 2) { 179 | string userName = args[1]; 180 | OpenFileDialog ofd = new OpenFileDialog(); 181 | if (ofd.ShowDialog() == DialogResult.OK) { 182 | if (File.Exists(ofd.FileName)) { 183 | var img = Image.FromFile(ofd.FileName); 184 | if (wc.SendMsg(userName, img)) { 185 | Debug.WriteLine("图片消息发送成功!"); 186 | } else { 187 | Debug.WriteLine("图片消息发送失败!"); 188 | } 189 | } 190 | } 191 | 192 | } 193 | } 194 | 195 | private void button_refreshGroupMemberInfo_Click(object sender, EventArgs e) 196 | { 197 | string str = comboBox_users.Text; 198 | var args = str.Split('|'); 199 | if (args.Length == 2) { 200 | string userName = args[1]; 201 | bool ret = wc.SetRemarkName(userName, "HelloSB"); 202 | MessageBox.Show("设置备注名:" + ret); 203 | } 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /WechatTest/LoginForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /WechatTest/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace WechatTest 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// 应用程序的主入口点。 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new LoginForm()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /WechatTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("WechatTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("WechatTest")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 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("7241ddb9-6caf-4d68-8e06-ffafbc1acefe")] 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 | -------------------------------------------------------------------------------- /WechatTest/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本: 4.0.30319.42000 5 | // 6 | // 对此文件的更改可能导致不正确的行为,如果 7 | // 重新生成代码,则所做更改将丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WechatTest.Properties 12 | { 13 | 14 | 15 | /// 16 | /// 强类型资源类,用于查找本地化字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或删除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// 返回此类使用的缓存 ResourceManager 实例。 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) { 46 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WechatTest.Properties.Resources", typeof(Resources).Assembly); 47 | resourceMan = temp; 48 | } 49 | return resourceMan; 50 | } 51 | } 52 | 53 | /// 54 | /// 覆盖当前线程的 CurrentUICulture 属性 55 | /// 使用此强类型的资源类的资源查找。 56 | /// 57 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 58 | internal static global::System.Globalization.CultureInfo Culture 59 | { 60 | get 61 | { 62 | return resourceCulture; 63 | } 64 | set 65 | { 66 | resourceCulture = value; 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /WechatTest/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /WechatTest/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 WechatTest.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /WechatTest/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WechatTest/Simsimi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Wechat.API.Http; 7 | using Newtonsoft.Json; 8 | using Newtonsoft.Json.Linq; 9 | namespace WechatTest 10 | { 11 | 12 | public class SimsimiResponse 13 | { 14 | public string response; 15 | public string id; 16 | public int result; 17 | public string msg; 18 | } 19 | 20 | public static class Simsimi 21 | { 22 | 23 | public static SimsimiResponse say(string word) 24 | { 25 | HttpClient http = new HttpClient(); 26 | string url = "http://api.simsimi.com/request.p?key=your_paid_key&lc=zh&ft=1.0&text=" + word; 27 | string ret = http.GET_UTF8String(url); 28 | var rep = JsonConvert.DeserializeObject(ret); 29 | return rep; 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /WechatTest/WechatTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7241DDB9-6CAF-4D68-8E06-FFAFBC1ACEFE} 8 | WinExe 9 | Properties 10 | WechatTest 11 | WechatTest 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 38 | True 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Form 55 | 56 | 57 | LoginForm.cs 58 | 59 | 60 | 61 | 62 | 63 | LoginForm.cs 64 | 65 | 66 | ResXFileCodeGenerator 67 | Resources.Designer.cs 68 | Designer 69 | 70 | 71 | True 72 | Resources.resx 73 | 74 | 75 | 76 | SettingsSingleFileGenerator 77 | Settings.Designer.cs 78 | 79 | 80 | True 81 | Settings.settings 82 | True 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | {a48c55f5-02ab-49ea-b77a-f6a9cc81b1e4} 91 | Wechat 92 | 93 | 94 | 95 | 102 | -------------------------------------------------------------------------------- /WechatTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --------------------------------------------------------------------------------