├── .classpath ├── .project ├── .settings ├── org.eclipse.core.resources.prefs └── org.eclipse.jdt.core.prefs ├── LICENSE ├── README.md ├── bin └── org │ └── outstudio │ └── wxtest │ ├── Main.class │ ├── StringUtilTest.class │ ├── WechatUtilTest.class │ ├── XmlUtilTest.class │ ├── ui │ ├── MainFrame$1.class │ ├── MainFrame$10.class │ ├── MainFrame$11.class │ ├── MainFrame$2.class │ ├── MainFrame$3.class │ ├── MainFrame$4.class │ ├── MainFrame$5.class │ ├── MainFrame$6.class │ ├── MainFrame$7.class │ ├── MainFrame$8.class │ ├── MainFrame$9.class │ ├── MainFrame.class │ ├── RawPostFrame$1.class │ ├── RawPostFrame$2.class │ ├── RawPostFrame$3.class │ ├── RawPostFrame$4.class │ ├── RawPostFrame.class │ ├── TestLocationFrame$1.class │ ├── TestLocationFrame$2.class │ ├── TestLocationFrame$3.class │ ├── TestLocationFrame$4.class │ ├── TestLocationFrame$5.class │ ├── TestLocationFrame$6.class │ ├── TestLocationFrame$7.class │ ├── TestLocationFrame$8.class │ ├── TestLocationFrame$9.class │ └── TestLocationFrame.class │ └── util │ ├── SignatureUtil.class │ ├── SiteInfo.class │ ├── SiteInfoUtil.class │ ├── StringUtil.class │ ├── WechatUtil.class │ └── XmlUtil.class ├── lib ├── commons-io-2.4.jar ├── easywechat-20141113.jar ├── jsoup-out-1.7.3.jar └── rsyntaxtextarea-2.5.3.jar ├── src └── org │ └── outstudio │ └── wxtest │ ├── Main.java │ ├── ui │ ├── MainFrame.java │ ├── RawPostFrame.java │ └── TestLocationFrame.java │ └── util │ ├── SignatureUtil.java │ ├── SiteInfo.java │ ├── SiteInfoUtil.java │ ├── StringUtil.java │ ├── WechatUtil.java │ └── XmlUtil.java ├── test └── org │ └── outstudio │ └── wxtest │ ├── StringUtilTest.java │ ├── WechatUtilTest.java │ └── XmlUtilTest.java └── test_res ├── ImageReqMsg.txt ├── LinkReqMsg.txt ├── LocationReqMsg.txt ├── TextReqMsg.txt ├── VideoReqMsg.txt ├── VoiceReqMsg.txt └── invalid_xml_1.txt /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | WechatTestTool 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 3 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.7 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.source=1.7 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WechatTestTool 2 | ============== 3 | 4 | 用于测试微信公众平台应用的GUI工具,使用Java/Swing制作。 5 | 6 | 无需接入微信公众平台,即可对你的微信公众平台应用进行测试。 7 | 8 | 该工具可以向你的应用服务器模拟发送文本消息,模拟点击菜单, 9 | 还会将接收到的XML数据展现出来,并进行解析,统计响应时间等等。 10 | -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/Main.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/Main.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/StringUtilTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/StringUtilTest.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/WechatUtilTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/WechatUtilTest.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/XmlUtilTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/XmlUtilTest.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/MainFrame$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/MainFrame$1.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/MainFrame$10.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/MainFrame$10.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/MainFrame$11.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/MainFrame$11.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/MainFrame$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/MainFrame$2.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/MainFrame$3.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/MainFrame$3.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/MainFrame$4.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/MainFrame$4.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/MainFrame$5.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/MainFrame$5.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/MainFrame$6.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/MainFrame$6.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/MainFrame$7.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/MainFrame$7.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/MainFrame$8.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/MainFrame$8.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/MainFrame$9.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/MainFrame$9.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/MainFrame.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/MainFrame.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/RawPostFrame$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/RawPostFrame$1.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/RawPostFrame$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/RawPostFrame$2.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/RawPostFrame$3.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/RawPostFrame$3.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/RawPostFrame$4.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/RawPostFrame$4.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/RawPostFrame.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/RawPostFrame.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/TestLocationFrame$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/TestLocationFrame$1.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/TestLocationFrame$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/TestLocationFrame$2.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/TestLocationFrame$3.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/TestLocationFrame$3.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/TestLocationFrame$4.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/TestLocationFrame$4.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/TestLocationFrame$5.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/TestLocationFrame$5.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/TestLocationFrame$6.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/TestLocationFrame$6.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/TestLocationFrame$7.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/TestLocationFrame$7.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/TestLocationFrame$8.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/TestLocationFrame$8.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/TestLocationFrame$9.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/TestLocationFrame$9.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/ui/TestLocationFrame.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/ui/TestLocationFrame.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/util/SignatureUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/util/SignatureUtil.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/util/SiteInfo.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/util/SiteInfo.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/util/SiteInfoUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/util/SiteInfoUtil.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/util/StringUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/util/StringUtil.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/util/WechatUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/util/WechatUtil.class -------------------------------------------------------------------------------- /bin/org/outstudio/wxtest/util/XmlUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/bin/org/outstudio/wxtest/util/XmlUtil.class -------------------------------------------------------------------------------- /lib/commons-io-2.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/lib/commons-io-2.4.jar -------------------------------------------------------------------------------- /lib/easywechat-20141113.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/lib/easywechat-20141113.jar -------------------------------------------------------------------------------- /lib/jsoup-out-1.7.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/lib/jsoup-out-1.7.3.jar -------------------------------------------------------------------------------- /lib/rsyntaxtextarea-2.5.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/lib/rsyntaxtextarea-2.5.3.jar -------------------------------------------------------------------------------- /src/org/outstudio/wxtest/Main.java: -------------------------------------------------------------------------------- 1 | package org.outstudio.wxtest; 2 | 3 | import org.outstudio.wxtest.ui.MainFrame; 4 | 5 | public class Main { 6 | 7 | public static void main(String[] args) { 8 | MainFrame.launch(); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/org/outstudio/wxtest/ui/MainFrame.java: -------------------------------------------------------------------------------- 1 | package org.outstudio.wxtest.ui; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Dimension; 5 | import java.awt.EventQueue; 6 | import java.awt.Font; 7 | import java.awt.event.ActionEvent; 8 | import java.awt.event.ActionListener; 9 | import java.awt.event.ItemEvent; 10 | import java.awt.event.ItemListener; 11 | import java.io.IOException; 12 | import java.util.List; 13 | 14 | import javax.swing.JButton; 15 | import javax.swing.JComboBox; 16 | import javax.swing.JEditorPane; 17 | import javax.swing.JFrame; 18 | import javax.swing.JLabel; 19 | import javax.swing.JPanel; 20 | import javax.swing.JScrollPane; 21 | import javax.swing.JSplitPane; 22 | import javax.swing.JTextField; 23 | import javax.swing.UIManager; 24 | import javax.swing.border.EmptyBorder; 25 | import javax.swing.event.PopupMenuEvent; 26 | import javax.swing.event.PopupMenuListener; 27 | 28 | import org.easywechat.msg.BaseMsg; 29 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 30 | import org.fife.ui.rsyntaxtextarea.SyntaxConstants; 31 | import org.fife.ui.rtextarea.RTextScrollPane; 32 | import org.jsoup.nodes.Document; 33 | import org.outstudio.wxtest.util.SiteInfo; 34 | import org.outstudio.wxtest.util.SiteInfoUtil; 35 | import org.outstudio.wxtest.util.WechatUtil; 36 | 37 | import java.awt.event.MouseAdapter; 38 | import java.awt.event.MouseEvent; 39 | import java.awt.Color; 40 | 41 | public class MainFrame extends JFrame { 42 | 43 | private static final long serialVersionUID = 1L; 44 | 45 | private JPanel contentPane; 46 | private JTextField tfText; 47 | private JTextField tfToken; 48 | private JComboBox cbUrl; 49 | 50 | private JSplitPane splitPane; 51 | private JScrollPane scrollPane; 52 | private JEditorPane epView; 53 | private RTextScrollPane scrollPane_1; 54 | private RSyntaxTextArea rawResp; 55 | private JLabel lblTime; 56 | 57 | private List siteInfos; 58 | 59 | /** 60 | * Launch the application. 61 | */ 62 | public static void launch() { 63 | EventQueue.invokeLater(new Runnable() { 64 | public void run() { 65 | try { 66 | UIManager.setLookAndFeel(UIManager 67 | .getSystemLookAndFeelClassName()); 68 | MainFrame frame = new MainFrame(); 69 | frame.setVisible(true); 70 | } catch (Exception e) { 71 | e.printStackTrace(); 72 | } 73 | } 74 | }); 75 | } 76 | 77 | /** 78 | * Create the frame. 79 | */ 80 | public MainFrame() { 81 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 82 | setSize(800, 600); 83 | setLocationRelativeTo(null); 84 | setTitle("微信测试工具"); 85 | 86 | contentPane = new JPanel(); 87 | contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); 88 | contentPane.setLayout(new BorderLayout(0, 0)); 89 | setContentPane(contentPane); 90 | 91 | JPanel topPane = new JPanel(); 92 | contentPane.add(topPane, BorderLayout.NORTH); 93 | topPane.setLayout(null); 94 | topPane.setPreferredSize(new Dimension(getWidth(), 100)); 95 | 96 | cbUrl = new JComboBox(); 97 | cbUrl.setEditable(true); 98 | cbUrl.setBounds(74, 10, 270, 21); 99 | cbUrl.addPopupMenuListener(new PopupMenuListener() { 100 | @Override 101 | public void popupMenuWillBecomeVisible(PopupMenuEvent e) { 102 | loadSiteInfo(); 103 | } 104 | 105 | @Override 106 | public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { 107 | } 108 | 109 | @Override 110 | public void popupMenuCanceled(PopupMenuEvent e) { 111 | } 112 | }); 113 | cbUrl.addItemListener(new ItemListener() { 114 | 115 | @Override 116 | public void itemStateChanged(ItemEvent e) { 117 | String url = getUrlFromInput(); 118 | for (SiteInfo siteInfo : siteInfos) { 119 | if (siteInfo.getUrl().equals(url)) { 120 | displayToken(siteInfo.getToken()); 121 | } 122 | } 123 | } 124 | }); 125 | topPane.add(cbUrl); 126 | 127 | JLabel lblNewLabel = new JLabel("\u9009\u62E9\u7F51\u5740\uFF1A"); 128 | lblNewLabel.setBounds(10, 13, 64, 15); 129 | topPane.add(lblNewLabel); 130 | 131 | tfText = new JTextField(); 132 | tfText.setBounds(10, 41, 269, 21); 133 | topPane.add(tfText); 134 | tfText.setColumns(10); 135 | 136 | JButton btnSendText = new JButton("\u53D1\u9001\u6587\u5B57"); 137 | btnSendText.addActionListener(new ActionListener() { 138 | public void actionPerformed(ActionEvent e) { 139 | onBtnSendClicked(); 140 | } 141 | }); 142 | btnSendText.setBounds(289, 41, 93, 23); 143 | topPane.add(btnSendText); 144 | 145 | JLabel lblToken = new JLabel("token\uFF1A"); 146 | lblToken.setBounds(374, 13, 54, 15); 147 | topPane.add(lblToken); 148 | 149 | tfToken = new JTextField(); 150 | tfToken.setBounds(428, 10, 76, 21); 151 | topPane.add(tfToken); 152 | tfToken.setColumns(10); 153 | 154 | lblTime = new JLabel(""); 155 | lblTime.setBounds(10, 72, 119, 15); 156 | topPane.add(lblTime); 157 | 158 | JButton btnMenuClick = new JButton("\u70B9\u51FB\u83DC\u5355"); 159 | btnMenuClick.addActionListener(new ActionListener() { 160 | public void actionPerformed(ActionEvent e) { 161 | onBtnMenuClicked(); 162 | } 163 | }); 164 | btnMenuClick.setBounds(392, 41, 93, 23); 165 | topPane.add(btnMenuClick); 166 | 167 | final JLabel lblSendXml = new JLabel("\u53D1\u9001XML"); 168 | lblSendXml.setForeground(Color.BLUE); 169 | lblSendXml.addMouseListener(new MouseAdapter() { 170 | @Override 171 | public void mouseClicked(MouseEvent e) { 172 | launchPawPostFrame(); 173 | } 174 | 175 | @Override 176 | public void mouseEntered(MouseEvent e) { 177 | lblSendXml.setFont(new Font("宋体", Font.ITALIC, 14)); 178 | } 179 | 180 | @Override 181 | public void mouseExited(MouseEvent e) { 182 | lblSendXml.setFont(new Font("宋体", Font.PLAIN, 12)); 183 | } 184 | }); 185 | lblSendXml.setBounds(495, 44, 70, 15); 186 | topPane.add(lblSendXml); 187 | 188 | splitPane = new JSplitPane(); 189 | splitPane.setResizeWeight(0.45); 190 | contentPane.add(splitPane, BorderLayout.CENTER); 191 | 192 | scrollPane = new JScrollPane(); 193 | splitPane.setLeftComponent(scrollPane); 194 | 195 | epView = new JEditorPane(); 196 | scrollPane.setViewportView(epView); 197 | splitPane.setResizeWeight(0.5); 198 | 199 | rawResp = new RSyntaxTextArea(); 200 | rawResp.setEditable(false); 201 | rawResp.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML); 202 | rawResp.setCodeFoldingEnabled(true); 203 | 204 | scrollPane_1 = new RTextScrollPane(rawResp); 205 | splitPane.setRightComponent(scrollPane_1); 206 | 207 | loadSiteInfo(); 208 | } 209 | 210 | /** 211 | * 从配置文件中加载网址和token 212 | */ 213 | private void loadSiteInfo() { 214 | cbUrl.removeAllItems(); 215 | siteInfos = SiteInfoUtil.getSiteInfo(); 216 | for (SiteInfo siteInfo : siteInfos) { 217 | String url = siteInfo.getUrl(); 218 | cbUrl.addItem(url); 219 | } 220 | cbUrl.repaint(); 221 | } 222 | 223 | private void onBtnSendClicked() { 224 | displayFormattedResp("正在等待服务器响应!"); 225 | 226 | String rawUrl = getUrlFromInput(); 227 | String token = tfToken.getText(); 228 | String text = tfText.getText(); 229 | 230 | if (!rawUrl.startsWith("http")) { 231 | rawUrl = "http://" + rawUrl; 232 | } 233 | String resp; 234 | try { 235 | long beginTime = System.currentTimeMillis(); 236 | resp = WechatUtil.sendText(token, rawUrl, text); 237 | long endTime = System.currentTimeMillis(); 238 | displayDelayTime(endTime - beginTime); 239 | } catch (IllegalArgumentException ee) { 240 | displayErrorMsg("网址格式有误!"); 241 | return; 242 | } catch (IOException e) { 243 | displayErrorMsg("出现IO错误!"); 244 | return; 245 | } 246 | 247 | parseAndDisplay(resp); 248 | saveSiteInfo(new SiteInfo(rawUrl, token)); 249 | } 250 | 251 | private void onBtnMenuClicked() { 252 | displayFormattedResp("正在等待服务器响应!"); 253 | 254 | String rawUrl = getUrlFromInput(); 255 | String token = tfToken.getText(); 256 | String eventKey = tfText.getText(); 257 | 258 | if (!rawUrl.startsWith("http")) { 259 | rawUrl = "http://" + rawUrl; 260 | } 261 | String resp; 262 | try { 263 | long beginTime = System.currentTimeMillis(); 264 | resp = WechatUtil.sendClickEvent(token, rawUrl, eventKey); 265 | long endTime = System.currentTimeMillis(); 266 | displayDelayTime(endTime - beginTime); 267 | } catch (IllegalArgumentException ee) { 268 | displayErrorMsg("网址格式有误!"); 269 | return; 270 | } catch (IOException e) { 271 | displayErrorMsg("出现IO错误!"); 272 | return; 273 | } 274 | 275 | parseAndDisplay(resp); 276 | saveSiteInfo(new SiteInfo(rawUrl, token)); 277 | } 278 | 279 | /** 280 | * 向服务器发送的XML数据 281 | */ 282 | public void sendXml(String xml) { 283 | displayFormattedResp("正在等待服务器响应!"); 284 | 285 | String rawUrl = getUrlFromInput(); 286 | String token = tfToken.getText(); 287 | 288 | if (!rawUrl.startsWith("http")) { 289 | rawUrl = "http://" + rawUrl; 290 | } 291 | String resp; 292 | try { 293 | long beginTime = System.currentTimeMillis(); 294 | resp = WechatUtil.sendXml(token, rawUrl, xml); 295 | long endTime = System.currentTimeMillis(); 296 | displayDelayTime(endTime - beginTime); 297 | } catch (IllegalArgumentException ee) { 298 | displayErrorMsg("网址格式有误!"); 299 | return; 300 | } catch (IOException e) { 301 | displayErrorMsg("出现IO错误!"); 302 | return; 303 | } 304 | 305 | parseAndDisplay(resp); 306 | saveSiteInfo(new SiteInfo(rawUrl, token)); 307 | } 308 | 309 | /** 310 | * 解析返回的消息并进行展示 311 | */ 312 | private void parseAndDisplay(String resp) { 313 | if (resp.trim().isEmpty()) { 314 | displayErrorMsg("返回为空!"); 315 | return; 316 | } 317 | 318 | Document doc = WechatUtil.getDocFromXml(resp); 319 | 320 | displayRawRespText(doc.toString()); 321 | 322 | BaseMsg msg; 323 | try { 324 | msg = WechatUtil.getMsgFromDoc(doc); 325 | } catch (Exception e) { 326 | displayFormattedResp("解析出现异常!"); 327 | return; 328 | } 329 | 330 | // 将解析后的消息展现到界面中 331 | String formattedResp = WechatUtil.getPrettyString(msg); 332 | displayFormattedResp(formattedResp); 333 | } 334 | 335 | /** 336 | * 将原先不存在的服务器地址和token存进配置文件 337 | */ 338 | private void saveSiteInfo(SiteInfo info) { 339 | String url = info.getUrl(); 340 | String token = info.getToken(); 341 | 342 | boolean hasUrl = false; 343 | for (SiteInfo siteInfo : siteInfos) { 344 | if (siteInfo.getUrl().equals(url)) { 345 | hasUrl = true; 346 | break; 347 | } 348 | } 349 | if (!hasUrl) { 350 | siteInfos.add(new SiteInfo(url, token)); 351 | SiteInfoUtil.saveSiteInfo(siteInfos); 352 | } 353 | } 354 | 355 | private String getUrlFromInput() { 356 | return cbUrl.getEditor().getItem().toString(); 357 | } 358 | 359 | private void displayToken(final String token) { 360 | EventQueue.invokeLater(new Runnable() { 361 | @Override 362 | public void run() { 363 | tfToken.setText(token); 364 | } 365 | }); 366 | } 367 | 368 | /** 369 | * 展示响应时间 370 | */ 371 | private void displayDelayTime(final long timeInMillis) { 372 | EventQueue.invokeLater(new Runnable() { 373 | @Override 374 | public void run() { 375 | lblTime.setText("用时:" + timeInMillis / 1000d + " 秒"); 376 | } 377 | }); 378 | } 379 | 380 | /** 381 | * 展示错误提示信息 382 | */ 383 | private void displayErrorMsg(final String errMsg) { 384 | EventQueue.invokeLater(new Runnable() { 385 | @Override 386 | public void run() { 387 | epView.setText(errMsg); 388 | rawResp.setText(""); 389 | } 390 | }); 391 | } 392 | 393 | /** 394 | * 展示解析后的响应信息 395 | */ 396 | private void displayFormattedResp(final String text) { 397 | EventQueue.invokeLater(new Runnable() { 398 | @Override 399 | public void run() { 400 | epView.setText(text); 401 | } 402 | }); 403 | } 404 | 405 | /** 406 | * 展示原始响应信息 407 | */ 408 | private void displayRawRespText(final String text) { 409 | EventQueue.invokeLater(new Runnable() { 410 | @Override 411 | public void run() { 412 | rawResp.setText(text); 413 | } 414 | }); 415 | } 416 | 417 | /** 418 | * 开启发送XML数据的窗口 419 | */ 420 | private void launchPawPostFrame() { 421 | RawPostFrame.launch(this); 422 | } 423 | } 424 | -------------------------------------------------------------------------------- /src/org/outstudio/wxtest/ui/RawPostFrame.java: -------------------------------------------------------------------------------- 1 | package org.outstudio.wxtest.ui; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.EventQueue; 5 | import java.awt.event.ActionEvent; 6 | import java.awt.event.ActionListener; 7 | import java.io.File; 8 | import java.io.IOException; 9 | 10 | import javax.swing.JButton; 11 | import javax.swing.JFileChooser; 12 | import javax.swing.JFrame; 13 | import javax.swing.JOptionPane; 14 | import javax.swing.JPanel; 15 | import javax.swing.border.EmptyBorder; 16 | import javax.swing.filechooser.FileFilter; 17 | 18 | import org.apache.commons.io.FileUtils; 19 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 20 | import org.fife.ui.rsyntaxtextarea.SyntaxConstants; 21 | import org.fife.ui.rtextarea.RTextScrollPane; 22 | import org.outstudio.wxtest.util.XmlUtil; 23 | 24 | public class RawPostFrame extends JFrame { 25 | 26 | private static final long serialVersionUID = 1L; 27 | 28 | private MainFrame mainFrame; 29 | 30 | private JPanel contentPane; 31 | private RTextScrollPane scrollPane; 32 | private RSyntaxTextArea textArea; 33 | private JButton btnSendRaw; 34 | 35 | /** 36 | * Launch the application. 37 | */ 38 | public static void launch(final MainFrame parent) { 39 | EventQueue.invokeLater(new Runnable() { 40 | public void run() { 41 | try { 42 | RawPostFrame frame = new RawPostFrame(parent); 43 | frame.setVisible(true); 44 | } catch (Exception e) { 45 | e.printStackTrace(); 46 | } 47 | } 48 | }); 49 | } 50 | 51 | /** 52 | * Create the frame. 53 | */ 54 | public RawPostFrame(MainFrame parent) { 55 | setTitle("\u8F93\u5165XML \u6216 \u4ECEtxt\u6587\u4EF6\u4E2D\u9009\u62E9"); 56 | this.mainFrame = parent; 57 | setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 58 | setBounds(100, 100, 450, 300); 59 | contentPane = new JPanel(); 60 | contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); 61 | contentPane.setLayout(new BorderLayout(0, 0)); 62 | setContentPane(contentPane); 63 | 64 | JPanel panel = new JPanel(); 65 | contentPane.add(panel, BorderLayout.NORTH); 66 | 67 | JButton btnSelectFromFile = new JButton("\u9009\u62E9txt\u6587\u4EF6"); 68 | btnSelectFromFile.addActionListener(new ActionListener() { 69 | public void actionPerformed(ActionEvent e) { 70 | obBtnSelectFileClicked(); 71 | } 72 | }); 73 | panel.add(btnSelectFromFile); 74 | 75 | btnSendRaw = new JButton("\u53D1\u9001"); 76 | btnSendRaw.addActionListener(new ActionListener() { 77 | public void actionPerformed(ActionEvent e) { 78 | onBtnSendRawClicked(); 79 | } 80 | }); 81 | panel.add(btnSendRaw); 82 | 83 | textArea = new RSyntaxTextArea(); 84 | textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML); 85 | textArea.setCodeFoldingEnabled(true); 86 | 87 | scrollPane = new RTextScrollPane(textArea); 88 | contentPane.add(scrollPane, BorderLayout.CENTER); 89 | } 90 | 91 | private void obBtnSelectFileClicked() { 92 | JFileChooser fc = new JFileChooser(); 93 | fc.setFileFilter(new FileFilter() {// 只能选择txt文件 94 | @Override 95 | public String getDescription() { 96 | return null; 97 | } 98 | 99 | @Override 100 | public boolean accept(File f) { 101 | return f.isDirectory() || f.getName().endsWith(".txt"); 102 | } 103 | }); 104 | fc.showOpenDialog(this); 105 | File file = fc.getSelectedFile(); 106 | if (!file.getName().endsWith(".txt")) { 107 | JOptionPane.showMessageDialog(this, "请选择txt文件!"); 108 | return; 109 | } 110 | try { 111 | String content = FileUtils.readFileToString(file); 112 | textArea.setText(content); 113 | boolean isValidXml = XmlUtil.isValidXml(content); 114 | if (!isValidXml) { 115 | JOptionPane.showMessageDialog(this, "不符合XML格式!"); 116 | } 117 | } catch (IOException e) { 118 | e.printStackTrace(); 119 | } 120 | } 121 | 122 | private void onBtnSendRawClicked() { 123 | String xml = textArea.getText(); 124 | boolean isValidXml = XmlUtil.isValidXml(xml); 125 | if (xml.isEmpty() || !isValidXml) { 126 | JOptionPane.showMessageDialog(this, "不符合XML格式!"); 127 | return; 128 | } 129 | mainFrame.sendXml(xml); 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/org/outstudio/wxtest/ui/TestLocationFrame.java: -------------------------------------------------------------------------------- 1 | package org.outstudio.wxtest.ui; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Dimension; 5 | import java.awt.EventQueue; 6 | import java.awt.event.ActionEvent; 7 | import java.awt.event.ActionListener; 8 | import java.awt.event.ItemEvent; 9 | import java.awt.event.ItemListener; 10 | import java.io.IOException; 11 | import java.util.List; 12 | 13 | import javax.swing.JButton; 14 | import javax.swing.JComboBox; 15 | import javax.swing.JEditorPane; 16 | import javax.swing.JFrame; 17 | import javax.swing.JLabel; 18 | import javax.swing.JOptionPane; 19 | import javax.swing.JPanel; 20 | import javax.swing.JScrollPane; 21 | import javax.swing.JSplitPane; 22 | import javax.swing.JTextField; 23 | import javax.swing.UIManager; 24 | import javax.swing.border.EmptyBorder; 25 | import javax.swing.event.PopupMenuEvent; 26 | import javax.swing.event.PopupMenuListener; 27 | 28 | import org.easywechat.msg.BaseMsg; 29 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 30 | import org.fife.ui.rsyntaxtextarea.SyntaxConstants; 31 | import org.fife.ui.rtextarea.RTextScrollPane; 32 | import org.jsoup.nodes.Document; 33 | import org.outstudio.wxtest.util.SiteInfo; 34 | import org.outstudio.wxtest.util.SiteInfoUtil; 35 | import org.outstudio.wxtest.util.WechatUtil; 36 | 37 | public class TestLocationFrame extends JFrame { 38 | 39 | private static final long serialVersionUID = 1L; 40 | 41 | private JPanel contentPane; 42 | private JTextField tfLat; 43 | private JTextField tfToken; 44 | private JComboBox cbUrl; 45 | 46 | private JSplitPane splitPane; 47 | private JScrollPane scrollPane; 48 | private JEditorPane epView; 49 | private RTextScrollPane scrollPane_1; 50 | private RSyntaxTextArea rawResp; 51 | private JLabel lblTime; 52 | 53 | private List siteInfos; 54 | private JTextField tfLong; 55 | private JTextField tfPrecision; 56 | 57 | /** 58 | * Launch the application. 59 | */ 60 | public static void main(String[] args) { 61 | EventQueue.invokeLater(new Runnable() { 62 | public void run() { 63 | try { 64 | UIManager.setLookAndFeel(UIManager 65 | .getSystemLookAndFeelClassName()); 66 | TestLocationFrame frame = new TestLocationFrame(); 67 | frame.setVisible(true); 68 | } catch (Exception e) { 69 | e.printStackTrace(); 70 | } 71 | } 72 | }); 73 | } 74 | 75 | /** 76 | * Create the frame. 77 | */ 78 | public TestLocationFrame() { 79 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 80 | setSize(800, 600); 81 | setLocationRelativeTo(null); 82 | setTitle("微信测试工具"); 83 | 84 | contentPane = new JPanel(); 85 | contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); 86 | contentPane.setLayout(new BorderLayout(0, 0)); 87 | setContentPane(contentPane); 88 | 89 | JPanel topPane = new JPanel(); 90 | contentPane.add(topPane, BorderLayout.NORTH); 91 | topPane.setLayout(null); 92 | topPane.setPreferredSize(new Dimension(getWidth(), 100)); 93 | 94 | cbUrl = new JComboBox(); 95 | cbUrl.setEditable(true); 96 | cbUrl.setBounds(74, 10, 270, 21); 97 | cbUrl.addPopupMenuListener(new PopupMenuListener() { 98 | @Override 99 | public void popupMenuWillBecomeVisible(PopupMenuEvent e) { 100 | loadSiteInfo(); 101 | } 102 | 103 | @Override 104 | public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { 105 | } 106 | 107 | @Override 108 | public void popupMenuCanceled(PopupMenuEvent e) { 109 | } 110 | }); 111 | cbUrl.addItemListener(new ItemListener() { 112 | 113 | @Override 114 | public void itemStateChanged(ItemEvent e) { 115 | String url = getUrlFromInput(); 116 | for (SiteInfo siteInfo : siteInfos) { 117 | if (siteInfo.getUrl().equals(url)) { 118 | displayToken(siteInfo.getToken()); 119 | } 120 | } 121 | } 122 | }); 123 | topPane.add(cbUrl); 124 | 125 | JLabel lblNewLabel = new JLabel("\u9009\u62E9\u7F51\u5740\uFF1A"); 126 | lblNewLabel.setBounds(10, 13, 64, 15); 127 | topPane.add(lblNewLabel); 128 | 129 | tfLat = new JTextField(); 130 | tfLat.setText("23.137466"); 131 | tfLat.setBounds(39, 42, 104, 21); 132 | topPane.add(tfLat); 133 | tfLat.setColumns(10); 134 | 135 | JButton btnSend = new JButton( 136 | "\u53D1\u9001\u5730\u7406\u4F4D\u7F6E\u4E8B\u4EF6"); 137 | btnSend.addActionListener(new ActionListener() { 138 | public void actionPerformed(ActionEvent e) { 139 | onBtnSendClicked(); 140 | } 141 | }); 142 | btnSend.setBounds(495, 41, 136, 23); 143 | topPane.add(btnSend); 144 | 145 | JLabel lblToken = new JLabel("token\uFF1A"); 146 | lblToken.setBounds(374, 13, 54, 15); 147 | topPane.add(lblToken); 148 | 149 | tfToken = new JTextField(); 150 | tfToken.setBounds(428, 10, 76, 21); 151 | topPane.add(tfToken); 152 | tfToken.setColumns(10); 153 | 154 | lblTime = new JLabel(""); 155 | lblTime.setBounds(10, 72, 119, 15); 156 | topPane.add(lblTime); 157 | 158 | JLabel lblLat = new JLabel("Lat: "); 159 | lblLat.setBounds(10, 44, 38, 15); 160 | topPane.add(lblLat); 161 | 162 | JLabel lblLong = new JLabel("Long: "); 163 | lblLong.setBounds(163, 43, 38, 15); 164 | topPane.add(lblLong); 165 | 166 | tfLong = new JTextField(); 167 | tfLong.setText("113.352425"); 168 | tfLong.setColumns(10); 169 | tfLong.setBounds(198, 42, 104, 21); 170 | topPane.add(tfLong); 171 | 172 | JLabel lblPrecision = new JLabel("Precision: "); 173 | lblPrecision.setBounds(312, 43, 93, 15); 174 | topPane.add(lblPrecision); 175 | 176 | tfPrecision = new JTextField(); 177 | tfPrecision.setText("119.385040"); 178 | tfPrecision.setColumns(10); 179 | tfPrecision.setBounds(374, 42, 104, 21); 180 | topPane.add(tfPrecision); 181 | 182 | splitPane = new JSplitPane(); 183 | splitPane.setResizeWeight(0.45); 184 | contentPane.add(splitPane, BorderLayout.CENTER); 185 | 186 | scrollPane = new JScrollPane(); 187 | splitPane.setLeftComponent(scrollPane); 188 | 189 | epView = new JEditorPane(); 190 | scrollPane.setViewportView(epView); 191 | splitPane.setResizeWeight(0.5); 192 | 193 | rawResp = new RSyntaxTextArea(); 194 | rawResp.setEditable(false); 195 | rawResp.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML); 196 | rawResp.setCodeFoldingEnabled(true); 197 | 198 | scrollPane_1 = new RTextScrollPane(rawResp); 199 | splitPane.setRightComponent(scrollPane_1); 200 | 201 | loadSiteInfo(); 202 | } 203 | 204 | /** 205 | * 从配置文件中加载网址和token 206 | */ 207 | private void loadSiteInfo() { 208 | cbUrl.removeAllItems(); 209 | siteInfos = SiteInfoUtil.getSiteInfo(); 210 | for (SiteInfo siteInfo : siteInfos) { 211 | String url = siteInfo.getUrl(); 212 | cbUrl.addItem(url); 213 | } 214 | cbUrl.repaint(); 215 | } 216 | 217 | private void onBtnSendClicked() { 218 | displayFormattedResp("正在等待服务器响应!"); 219 | 220 | String rawUrl = getUrlFromInput(); 221 | String token = tfToken.getText(); 222 | String strLatitude = tfLat.getText(); 223 | String strLongitude = tfLong.getText(); 224 | String strPrecision = tfPrecision.getText(); 225 | 226 | double latitude, longitude, precision; 227 | try { 228 | latitude = Double.parseDouble(strLatitude); 229 | longitude = Double.parseDouble(strLongitude); 230 | precision = Double.parseDouble(strPrecision); 231 | } catch (Exception e) { 232 | JOptionPane.showMessageDialog(null, "填数字!"); 233 | return; 234 | } 235 | 236 | if (!rawUrl.startsWith("http")) { 237 | rawUrl = "http://" + rawUrl; 238 | } 239 | String resp; 240 | try { 241 | long beginTime = System.currentTimeMillis(); 242 | resp = WechatUtil.sendLocationEvent(token, rawUrl, latitude, 243 | longitude, precision); 244 | long endTime = System.currentTimeMillis(); 245 | displayDelayTime(endTime - beginTime); 246 | } catch (IllegalArgumentException ee) { 247 | displayErrorMsg("网址格式有误!"); 248 | return; 249 | } catch (IOException e) { 250 | displayErrorMsg("出现IO错误!"); 251 | return; 252 | } 253 | 254 | parseAndDisplay(resp); 255 | saveSiteInfo(new SiteInfo(rawUrl, token)); 256 | } 257 | 258 | /** 259 | * 解析返回的消息并进行展示 260 | */ 261 | private void parseAndDisplay(String resp) { 262 | if (resp.trim().isEmpty()) { 263 | displayErrorMsg("返回为空!"); 264 | return; 265 | } 266 | 267 | Document doc = WechatUtil.getDocFromXml(resp); 268 | 269 | displayRawRespText(doc.toString()); 270 | 271 | BaseMsg msg; 272 | try { 273 | msg = WechatUtil.getMsgFromDoc(doc); 274 | } catch (Exception e) { 275 | displayFormattedResp("解析出现异常!"); 276 | return; 277 | } 278 | 279 | // 将解析后的消息展现到界面中 280 | String formattedResp = WechatUtil.getPrettyString(msg); 281 | displayFormattedResp(formattedResp); 282 | } 283 | 284 | /** 285 | * 将原先不存在的服务器地址和token存进配置文件 286 | */ 287 | private void saveSiteInfo(SiteInfo info) { 288 | String url = info.getUrl(); 289 | String token = info.getToken(); 290 | 291 | boolean hasUrl = false; 292 | for (SiteInfo siteInfo : siteInfos) { 293 | if (siteInfo.getUrl().equals(url)) { 294 | hasUrl = true; 295 | break; 296 | } 297 | } 298 | if (!hasUrl) { 299 | siteInfos.add(new SiteInfo(url, token)); 300 | SiteInfoUtil.saveSiteInfo(siteInfos); 301 | } 302 | } 303 | 304 | private String getUrlFromInput() { 305 | return cbUrl.getEditor().getItem().toString(); 306 | } 307 | 308 | private void displayToken(final String token) { 309 | EventQueue.invokeLater(new Runnable() { 310 | @Override 311 | public void run() { 312 | tfToken.setText(token); 313 | } 314 | }); 315 | } 316 | 317 | private void displayDelayTime(final long timeInMillis) { 318 | EventQueue.invokeLater(new Runnable() { 319 | @Override 320 | public void run() { 321 | lblTime.setText("用时:" + timeInMillis / 1000d + " 秒"); 322 | } 323 | }); 324 | } 325 | 326 | private void displayErrorMsg(final String errMsg) { 327 | EventQueue.invokeLater(new Runnable() { 328 | @Override 329 | public void run() { 330 | epView.setText(errMsg); 331 | rawResp.setText(""); 332 | } 333 | }); 334 | } 335 | 336 | private void displayFormattedResp(final String text) { 337 | EventQueue.invokeLater(new Runnable() { 338 | @Override 339 | public void run() { 340 | epView.setText(text); 341 | } 342 | }); 343 | } 344 | 345 | private void displayRawRespText(final String text) { 346 | EventQueue.invokeLater(new Runnable() { 347 | @Override 348 | public void run() { 349 | rawResp.setText(text); 350 | } 351 | }); 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /src/org/outstudio/wxtest/util/SignatureUtil.java: -------------------------------------------------------------------------------- 1 | package org.outstudio.wxtest.util; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.security.MessageDigest; 5 | import java.security.NoSuchAlgorithmException; 6 | import java.util.Arrays; 7 | 8 | public class SignatureUtil { 9 | 10 | private static final char[] digit = { '0', '1', '2', '3', '4', '5', '6', 11 | '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 12 | 13 | /** 14 | * 得到签名 15 | */ 16 | public static String getSignature(String token, String timestamp, 17 | String nonce) { 18 | 19 | String[] arr = new String[] { token, timestamp, nonce }; 20 | // 将token、timestamp、nonce三个参数进行字典序排序 21 | Arrays.sort(arr); 22 | StringBuilder content = new StringBuilder(); 23 | for (int i = 0; i < arr.length; i++) { 24 | content.append(arr[i]); 25 | } 26 | MessageDigest md = null; 27 | String tmpStr = null; 28 | 29 | try { 30 | md = MessageDigest.getInstance("SHA-1"); 31 | // 将三个参数字符串拼接成一个字符串进行sha1加密 32 | byte[] digest = md.digest(content.toString().getBytes("UTF-8")); 33 | tmpStr = byteToStr(digest); 34 | } catch (NoSuchAlgorithmException e) { 35 | e.printStackTrace(); 36 | } catch (UnsupportedEncodingException e) { 37 | e.printStackTrace(); 38 | } 39 | // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信 40 | return tmpStr; 41 | } 42 | 43 | /** 44 | * 将字节数组转换为十六进制字符串 45 | */ 46 | private static String byteToStr(byte[] byteArray) { 47 | int len = byteArray.length; 48 | StringBuilder strDigest = new StringBuilder(len * 2); 49 | for (int i = 0; i < len; i++) { 50 | strDigest.append(byteToHexStr(byteArray[i])); 51 | } 52 | return strDigest.toString(); 53 | } 54 | 55 | /** 56 | * 将字节转换为十六进制字符串 57 | */ 58 | private static String byteToHexStr(byte mByte) { 59 | char[] tempArr = new char[2]; 60 | tempArr[0] = digit[(mByte >>> 4) & 0X0F]; 61 | tempArr[1] = digit[mByte & 0X0F]; 62 | 63 | return new String(tempArr); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/org/outstudio/wxtest/util/SiteInfo.java: -------------------------------------------------------------------------------- 1 | package org.outstudio.wxtest.util; 2 | 3 | public class SiteInfo { 4 | 5 | private String url; 6 | private String token; 7 | 8 | public SiteInfo() { 9 | } 10 | 11 | public SiteInfo(String url, String token) { 12 | this.url = url; 13 | this.token = token; 14 | } 15 | 16 | public String getUrl() { 17 | return url; 18 | } 19 | 20 | public void setUrl(String url) { 21 | this.url = url; 22 | } 23 | 24 | public String getToken() { 25 | return token; 26 | } 27 | 28 | public void setToken(String token) { 29 | this.token = token; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "SiteInfo [url=" + url + ", token=" + token + "]"; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/org/outstudio/wxtest/util/SiteInfoUtil.java: -------------------------------------------------------------------------------- 1 | package org.outstudio.wxtest.util; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import org.apache.commons.io.FileUtils; 9 | 10 | public class SiteInfoUtil { 11 | 12 | private static final String configFilePath = "config.txt"; 13 | 14 | /** 15 | * 从配置文件中读取服务器配置信息 16 | */ 17 | public static List getSiteInfo() { 18 | File file = new File(configFilePath); 19 | List siteInfos = new ArrayList<>(); 20 | try { 21 | List lines = FileUtils.readLines(file); 22 | if (lines.isEmpty()) { 23 | return siteInfos; 24 | } 25 | for (String line : lines) { 26 | String[] info = line.split(" ", 2); 27 | SiteInfo siteInfo = new SiteInfo(); 28 | siteInfo.setUrl(info[0]); 29 | siteInfo.setToken(info[1]); 30 | siteInfos.add(siteInfo); 31 | } 32 | return siteInfos; 33 | } catch (IOException e) { 34 | return siteInfos; 35 | } 36 | } 37 | 38 | /** 39 | * 将服务器配置信息存进配置文件中 40 | */ 41 | public static void saveSiteInfo(List siteInfos) { 42 | if (siteInfos == null || siteInfos.isEmpty()) { 43 | return; 44 | } 45 | List lines = new ArrayList<>(); 46 | for (SiteInfo siteInfo : siteInfos) { 47 | lines.add(siteInfo.getUrl() + " " + siteInfo.getToken()); 48 | } 49 | File file = new File(configFilePath); 50 | try { 51 | FileUtils.writeLines(file, lines); 52 | } catch (IOException e) { 53 | e.printStackTrace(); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/org/outstudio/wxtest/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package org.outstudio.wxtest.util; 2 | 3 | public class StringUtil { 4 | 5 | /** 6 | * 将字符串补充为指定长度的字符串,从右侧添加字符 7 | * 8 | * @param str 9 | * 填充前的字符串 10 | * @param ch 11 | * 填充使用的字符 12 | * @param len 13 | * 字符串的目标长度 14 | * @return 填充后的字符串 15 | */ 16 | public static String paddingRight(String str, char ch, int len) { 17 | StringBuilder sb = new StringBuilder(str); 18 | for (int i = 0; i < len - str.length(); i++) { 19 | sb.append(ch); 20 | } 21 | return sb.toString(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/org/outstudio/wxtest/util/WechatUtil.java: -------------------------------------------------------------------------------- 1 | package org.outstudio.wxtest.util; 2 | 3 | import java.io.IOException; 4 | import java.sql.Timestamp; 5 | import java.util.List; 6 | 7 | import org.easywechat.msg.Article; 8 | import org.easywechat.msg.BaseMsg; 9 | import org.easywechat.msg.ImageMsg; 10 | import org.easywechat.msg.MusicMsg; 11 | import org.easywechat.msg.NewsMsg; 12 | import org.easywechat.msg.RespType; 13 | import org.easywechat.msg.TextMsg; 14 | import org.easywechat.msg.VideoMsg; 15 | import org.easywechat.msg.VoiceMsg; 16 | import org.easywechat.msg.req.BaseReq; 17 | import org.easywechat.msg.req.EventType; 18 | import org.easywechat.msg.req.LocationEvent; 19 | import org.easywechat.msg.req.MenuEvent; 20 | import org.easywechat.msg.req.TextReqMsg; 21 | import org.easywechat.util.MessageFactory; 22 | import org.jsoup.Connection; 23 | import org.jsoup.Connection.Method; 24 | import org.jsoup.Jsoup; 25 | import org.jsoup.nodes.Document; 26 | import org.jsoup.nodes.Element; 27 | import org.jsoup.parser.Parser; 28 | import org.jsoup.select.Elements; 29 | 30 | /** 31 | * 与微信相关的工具类 32 | */ 33 | public class WechatUtil { 34 | 35 | /** 36 | * 模拟发送文本消息 37 | * 38 | * @return 响应信息,一般为XML格式 39 | * @throws IOException 40 | */ 41 | public static String sendText(String token, String url, String text) 42 | throws IOException { 43 | String realUrl = buildUrl(token, url); 44 | String xmlData = createTextReqMsg(text).toXml(); 45 | 46 | String resp = getConn(realUrl).rawData(xmlData).execute().body(); 47 | 48 | return resp; 49 | } 50 | 51 | /** 52 | * 模拟发送菜单点击事件 53 | * 54 | * @return 响应信息,一般为XML格式 55 | * @throws IOException 56 | */ 57 | public static String sendClickEvent(String token, String url, 58 | String eventKey) throws IOException { 59 | String realUrl = buildUrl(token, url); 60 | String xmlData = createMenuClickEvent(eventKey).toXml(); 61 | System.out.println(xmlData); 62 | 63 | String resp = getConn(realUrl).rawData(xmlData).execute().body(); 64 | 65 | return resp; 66 | } 67 | 68 | /** 69 | * 模拟发送地理位置事件 70 | * 71 | * @return 响应信息,一般为XML格式 72 | * @throws IOException 73 | */ 74 | public static String sendLocationEvent(String token, String url, 75 | double latitude, double longitude, double precision) 76 | throws IOException { 77 | String realUrl = buildUrl(token, url); 78 | String xmlData = createLocationEvent(latitude, longitude, precision) 79 | .toXml(); 80 | 81 | String resp = getConn(realUrl).rawData(xmlData).execute().body(); 82 | 83 | return resp; 84 | } 85 | 86 | /** 87 | * 模拟发送XML数据 88 | * 89 | * @return 响应信息,一般为XML格式 90 | * @throws IOException 91 | */ 92 | public static String sendXml(String token, String url, String xml) 93 | throws IOException { 94 | String realUrl = buildUrl(token, url); 95 | 96 | String resp = getConn(realUrl).rawData(xml).execute().body(); 97 | 98 | return resp; 99 | } 100 | 101 | public static Document getDocFromXml(String xml) { 102 | return Jsoup.parse(xml, "", Parser.xmlParser()); 103 | } 104 | 105 | /** 106 | * 解析Document得到微信响应消息对象 107 | * 108 | * @throws Exception 109 | * 如果解析失败 110 | */ 111 | public static BaseMsg getMsgFromDoc(Document doc) throws Exception { 112 | 113 | String toUserName = getTagValue("ToUserName", doc); 114 | String fromUserName = getTagValue("FromUserName", doc); 115 | String rawCreateTime = getTagValue("CreateTime", doc); 116 | long createTime = Long.parseLong(rawCreateTime); 117 | 118 | String msgType = getTagValue("MsgType", doc); 119 | 120 | BaseMsg msg; 121 | 122 | if (msgType.equals(RespType.TEXT)) { 123 | String content = getTagValue("Content", doc); 124 | msg = MessageFactory.createTextMsg(content); 125 | } else if (msgType.equals(RespType.NEWS)) { 126 | 127 | NewsMsg newsMsg = MessageFactory.createNewsMsg(); 128 | 129 | Elements items = doc.getElementsByTag("item"); 130 | for (Element item : items) { 131 | String title = getTagValue("Title", item); 132 | String description = getTagValue("Description", item); 133 | String picUrl = getTagValue("PicUrl", item); 134 | String Url = getTagValue("Url", item); 135 | 136 | newsMsg.add(title, description, picUrl, Url); 137 | } 138 | msg = newsMsg; 139 | } else if (msgType.equals(RespType.IMAGE)) { 140 | String mediaId = getTagValue("MediaId", doc); 141 | msg = MessageFactory.createImageMsg(mediaId); 142 | } else if (msgType.equals(RespType.VOICE)) { 143 | String mediaId = getTagValue("MediaId", doc); 144 | msg = MessageFactory.createVoiceMsg(mediaId); 145 | } else if (msgType.equals(RespType.VIDEO)) { 146 | String mediaId = getTagValue("MediaId", doc); 147 | String title = getTagValue("Title", doc); 148 | String description = getTagValue("Description", doc); 149 | msg = MessageFactory.createVideoMsg(mediaId, title, description); 150 | } else if (msgType.equals(RespType.MUSIC)) { 151 | String title = getTagValue("Title", doc); 152 | String description = getTagValue("Description", doc); 153 | String musicUrl = getTagValue("MusicUrl", doc); 154 | String hqMusicUrl = getTagValue("HQMusicUrl", doc); 155 | String thumbMediaId = getTagValue("ThumbMediaId", doc); 156 | msg = MessageFactory.createMusicMsg(thumbMediaId, title, 157 | description, musicUrl, hqMusicUrl); 158 | } else { 159 | return null; 160 | } 161 | 162 | msg.setFromUserName(fromUserName); 163 | msg.setToUserName(toUserName); 164 | msg.setCreateTime(createTime); 165 | 166 | return msg; 167 | } 168 | 169 | /** 170 | * 得到解析后的消息对象 171 | */ 172 | public static String getPrettyString(BaseMsg msg) { 173 | StringBuilder sb = new StringBuilder(); 174 | String space = " "; 175 | 176 | if (msg instanceof TextMsg) { 177 | TextMsg textMsg = (TextMsg) msg; 178 | sb.append("文本消息:\n"); 179 | sb.append(space).append("Content:").append(textMsg.getContent()) 180 | .append("\n"); 181 | } else if (msg instanceof ImageMsg) { 182 | ImageMsg imageMsg = (ImageMsg) msg; 183 | sb.append("图片消息:\n"); 184 | sb.append(space).append("MediaId:").append(imageMsg.getMediaId()) 185 | .append("\n"); 186 | } else if (msg instanceof VoiceMsg) { 187 | VoiceMsg voiceMsg = (VoiceMsg) msg; 188 | sb.append("语音消息:\n"); 189 | sb.append(space).append("MediaId:").append(voiceMsg.getMediaId()) 190 | .append("\n"); 191 | } else if (msg instanceof VideoMsg) { 192 | VideoMsg videoMsg = (VideoMsg) msg; 193 | sb.append("视频消息:\n"); 194 | sb.append(space).append("MediaId:").append(videoMsg.getMediaId()) 195 | .append("\n"); 196 | sb.append(space).append("Title:").append(videoMsg.getTitle()) 197 | .append("\n"); 198 | sb.append(space).append("Description:") 199 | .append(videoMsg.getDescription()).append("\n"); 200 | } else if (msg instanceof MusicMsg) { 201 | MusicMsg musicMsg = (MusicMsg) msg; 202 | sb.append("视频消息:\n"); 203 | sb.append(space).append("Title:").append(musicMsg.getTitle()) 204 | .append("\n"); 205 | sb.append(space).append("Description:") 206 | .append(musicMsg.getDescription()).append("\n"); 207 | sb.append(space).append("MusicUrl:").append(musicMsg.getMusicUrl()) 208 | .append("\n"); 209 | sb.append(space).append("HQMusicUrl:") 210 | .append(musicMsg.getHqMusicUrl()).append("\n"); 211 | sb.append(space).append("ThumbMediaId:") 212 | .append(musicMsg.getThumbMediaId()).append("\n"); 213 | } else if (msg instanceof NewsMsg) { 214 | NewsMsg newsMsg = (NewsMsg) msg; 215 | sb.append("图文消息:\n"); 216 | List
articles = newsMsg.getArticles(); 217 | for (int i = 0; i < articles.size(); i++) { 218 | sb.append(space).append("第 ").append(i + 1).append(" 条:\n"); 219 | 220 | Article article = articles.get(i); 221 | 222 | String title = article.getTitle(); 223 | String description = article.getDescription(); 224 | String picUrl = article.getPicUrl(); 225 | String url = article.getUrl(); 226 | 227 | if (title != null) { 228 | sb.append(space).append(space).append("Title:") 229 | .append(title).append("\n"); 230 | } 231 | if (description != null) { 232 | sb.append(space).append(space).append("Description:") 233 | .append(description).append("\n"); 234 | } 235 | if (picUrl != null) { 236 | sb.append(space).append(space).append("PicUrl:") 237 | .append(picUrl).append("\n"); 238 | } 239 | if (url != null) { 240 | sb.append(space).append(space).append("Url:").append(url) 241 | .append("\n"); 242 | } 243 | } 244 | } 245 | 246 | sb.append("\n"); 247 | sb.append("FromUserName:").append(msg.getFromUserName()).append("\n"); 248 | sb.append("ToUserName:").append(msg.getToUserName()).append("\n"); 249 | long createTime = msg.getCreateTime(); 250 | sb.append("CreateTime:").append(createTime).append("(") 251 | .append(getDatetimeFromCreateTime(createTime)).append(")\n"); 252 | 253 | return sb.toString(); 254 | } 255 | 256 | /** 257 | * 将微信消息的CreateTime解析成格式化的日期时间字符串 258 | */ 259 | public static String getDatetimeFromCreateTime(long createTime) { 260 | String raw = String.valueOf(createTime); 261 | String strTime = StringUtil.paddingRight(raw, '0', 13); 262 | long time = Long.parseLong(strTime); 263 | return new Timestamp(time).toString().substring(0, 19); 264 | } 265 | 266 | /** 267 | * 得到适合该项目的Connection对象 268 | */ 269 | private static Connection getConn(String url) { 270 | return Jsoup.connect(url).header("Content-Type", "application/json") 271 | .ignoreContentType(true).timeout(10000).method(Method.POST); 272 | } 273 | 274 | private static String buildUrl(String token, String rawUrl) { 275 | StringBuilder sb = new StringBuilder(rawUrl); 276 | 277 | String timestamp = String.valueOf(System.currentTimeMillis() / 1000); 278 | String nonce = "zzz"; 279 | String signature = SignatureUtil.getSignature(token, timestamp, nonce); 280 | 281 | sb.append("?signature=").append(signature).append("×tamp=") 282 | .append(timestamp).append("&nonce=").append(nonce); 283 | return sb.toString(); 284 | } 285 | 286 | private static TextReqMsg createTextReqMsg(String content) { 287 | TextReqMsg msg = new TextReqMsg(content); 288 | buildReqBody(msg); 289 | return msg; 290 | } 291 | 292 | private static MenuEvent createMenuClickEvent(String eventKey) { 293 | MenuEvent event = new MenuEvent(eventKey); 294 | event.setEvent(EventType.CLICK); 295 | buildReqBody(event); 296 | return event; 297 | } 298 | 299 | private static LocationEvent createLocationEvent(double latitude, 300 | double longitude, double precision) { 301 | LocationEvent event = new LocationEvent(latitude, longitude, precision); 302 | event.setEvent(EventType.LOCATION); 303 | buildReqBody(event); 304 | return event; 305 | } 306 | 307 | private static void buildReqBody(BaseReq req) { 308 | req.setFromUserName("FromUserName"); 309 | req.setToUserName("ToUserName"); 310 | req.setCreateTime(System.currentTimeMillis() / 1000); 311 | } 312 | 313 | private static String getTagValue(String tagName, Element ele) { 314 | Elements eles = ele.getElementsByTag(tagName); 315 | return eles.isEmpty() ? null : eles.get(0).text(); 316 | } 317 | 318 | } 319 | -------------------------------------------------------------------------------- /src/org/outstudio/wxtest/util/XmlUtil.java: -------------------------------------------------------------------------------- 1 | package org.outstudio.wxtest.util; 2 | 3 | import java.io.StringReader; 4 | 5 | import javax.xml.parsers.DocumentBuilder; 6 | import javax.xml.parsers.DocumentBuilderFactory; 7 | 8 | import org.xml.sax.InputSource; 9 | 10 | public class XmlUtil { 11 | 12 | /** 13 | * 判断给定字符串是否符合XML格式 14 | */ 15 | public static boolean isValidXml(String xml) { 16 | try { 17 | StringReader reader = new StringReader(xml); 18 | DocumentBuilder db = DocumentBuilderFactory.newInstance() 19 | .newDocumentBuilder(); 20 | db.parse(new InputSource(reader)); 21 | } catch (Exception e) { 22 | return false; 23 | } 24 | return true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/org/outstudio/wxtest/StringUtilTest.java: -------------------------------------------------------------------------------- 1 | package org.outstudio.wxtest; 2 | 3 | import org.junit.Test; 4 | import org.outstudio.wxtest.util.StringUtil; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | public class StringUtilTest { 9 | 10 | @Test 11 | public void paddingRightNormalCase() { 12 | String result = StringUtil.paddingRight("abc", '0', 5); 13 | assertEquals("abc00", result); 14 | } 15 | 16 | @Test 17 | public void paddingRightWhenInputStringIsLongerThanInputLength() { 18 | String result = StringUtil.paddingRight("123456789", '0', 5); 19 | assertEquals("123456789", result); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /test/org/outstudio/wxtest/WechatUtilTest.java: -------------------------------------------------------------------------------- 1 | package org.outstudio.wxtest; 2 | 3 | import org.junit.Test; 4 | import org.outstudio.wxtest.util.WechatUtil; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | public class WechatUtilTest { 9 | 10 | @Test 11 | public void getDatetimeFromCreateTimeLength10() { 12 | String time = WechatUtil.getDatetimeFromCreateTime(1413118184L); 13 | assertEquals("2014-10-12 20:49:44", time); 14 | } 15 | 16 | @Test 17 | public void getDatetimeFromCreateTimeLength13() { 18 | String time = WechatUtil.getDatetimeFromCreateTime(1413118184292L); 19 | assertEquals("2014-10-12 20:49:44", time); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /test/org/outstudio/wxtest/XmlUtilTest.java: -------------------------------------------------------------------------------- 1 | package org.outstudio.wxtest; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | 8 | import org.apache.commons.io.FileUtils; 9 | import org.junit.Test; 10 | import org.outstudio.wxtest.util.XmlUtil; 11 | 12 | public class XmlUtilTest { 13 | 14 | static String TEXT_REQ_MSG; 15 | static String IMAGE_REQ_MSG; 16 | static String VOICE_REQ_MSG; 17 | static String VIDEO_REQ_MSG; 18 | static String LOCATION_REQ_MSG; 19 | static String LINK_REQ_MSG; 20 | 21 | static String INVALID_XML_1; 22 | 23 | static { 24 | try { 25 | TEXT_REQ_MSG = FileUtils.readFileToString(new File( 26 | "./test_res/TextReqMsg.txt")); 27 | IMAGE_REQ_MSG = FileUtils.readFileToString(new File( 28 | "./test_res/ImageReqMsg.txt")); 29 | VOICE_REQ_MSG = FileUtils.readFileToString(new File( 30 | "./test_res/VoiceReqMsg.txt")); 31 | VIDEO_REQ_MSG = FileUtils.readFileToString(new File( 32 | "./test_res/VideoReqMsg.txt")); 33 | LOCATION_REQ_MSG = FileUtils.readFileToString(new File( 34 | "./test_res/LocationReqMsg.txt")); 35 | LINK_REQ_MSG = FileUtils.readFileToString(new File( 36 | "./test_res/LinkReqMsg.txt")); 37 | INVALID_XML_1 = FileUtils.readFileToString(new File( 38 | "./test_res/invalid_xml_1.txt")); 39 | } catch (IOException e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | 44 | @Test 45 | public void validTextReqMsgIsValidXml() { 46 | assertEquals(true, XmlUtil.isValidXml(TEXT_REQ_MSG)); 47 | } 48 | 49 | @Test 50 | public void validImageReqMsgIsValidXml() { 51 | assertEquals(true, XmlUtil.isValidXml(IMAGE_REQ_MSG)); 52 | } 53 | 54 | @Test 55 | public void validVoiceReqMsgIsValidXml() { 56 | assertEquals(true, XmlUtil.isValidXml(VOICE_REQ_MSG)); 57 | } 58 | 59 | @Test 60 | public void validVideoReqMsgIsValidXml() { 61 | assertEquals(true, XmlUtil.isValidXml(VIDEO_REQ_MSG)); 62 | } 63 | 64 | @Test 65 | public void validLocationReqMsgIsValidXml() { 66 | assertEquals(true, XmlUtil.isValidXml(LOCATION_REQ_MSG)); 67 | } 68 | 69 | @Test 70 | public void validLinkReqMsgIsValidXml() { 71 | assertEquals(true, XmlUtil.isValidXml(LINK_REQ_MSG)); 72 | } 73 | 74 | @Test 75 | public void invalidXmlCase1() { 76 | assertEquals(false, XmlUtil.isValidXml(INVALID_XML_1)); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test_res/ImageReqMsg.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1348831860 5 | 6 | 7 | 8 | 1234567890123456 9 | -------------------------------------------------------------------------------- /test_res/LinkReqMsg.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/test_res/LinkReqMsg.txt -------------------------------------------------------------------------------- /test_res/LocationReqMsg.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubiRUN/WechatTestTool/b53129bdaf923e00cb52105d3b9636914765d455/test_res/LocationReqMsg.txt -------------------------------------------------------------------------------- /test_res/TextReqMsg.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1348831860 5 | 6 | 7 | 1234567890123456 8 | -------------------------------------------------------------------------------- /test_res/VideoReqMsg.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1357290913 5 | 6 | 7 | 8 | 1234567890123456 9 | -------------------------------------------------------------------------------- /test_res/VoiceReqMsg.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1357290913 5 | 6 | 7 | 8 | 1234567890123456 9 | -------------------------------------------------------------------------------- /test_res/invalid_xml_1.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1348831860 5 | 6 | 7 | 1234567890123456 8 | --------------------------------------------------------------------------------