├── README.md
├── bots-industrial
├── net_maclife_wechat_http_Bot_HCICloudCSR.java
└── net_maclife_wechat_http_Bot_iConTek.java
├── bots
├── net_maclife_wechat_http_Bot_ActiveDirectoryAddressBook.java
├── net_maclife_wechat_http_Bot_BaiduImageSearch.java
├── net_maclife_wechat_http_Bot_BaiduOCR.java
├── net_maclife_wechat_http_Bot_BaiduTranslate.java
├── net_maclife_wechat_http_Bot_BaiduVoice.java
├── net_maclife_wechat_http_Bot_Emoji.java
├── net_maclife_wechat_http_Bot_GoogleImageSearch.java
├── net_maclife_wechat_http_Bot_MakeFriend.java
├── net_maclife_wechat_http_Bot_Manager.java
├── net_maclife_wechat_http_Bot_MissileLaunched_JustForFun.java
├── net_maclife_wechat_http_Bot_Relay.java
├── net_maclife_wechat_http_Bot_Repeater.java
├── net_maclife_wechat_http_Bot_SaveContactsToDatabase.java
├── net_maclife_wechat_http_Bot_SaveMessagePackageToDatabase.java
├── net_maclife_wechat_http_Bot_SayHi.java
├── net_maclife_wechat_http_Bot_ShellCommand.java
├── net_maclife_wechat_http_Bot_SimpleAddressBook.java
├── net_maclife_wechat_http_Bot_WebSoup.java
├── net_maclife_wechat_http_Bot_XunFeiYun.java
└── net_maclife_wechat_http_Bot_糗事百科热门.java
├── doc
├── ReadMe.Chinglish.md
├── ReadMe.中文.md
└── img
│ ├── bot-active-directory-address-book-50%.png
│ ├── bot-baidu-image-search-50%.png
│ ├── bot-baidu-translate-50%.png
│ ├── bot-baidu-translate.png
│ ├── bot-baidu-voice-50%.png
│ ├── bot-baidu-voice.png
│ ├── bot-emoji-50%.png
│ ├── bot-emoji.png
│ ├── bot-iConTek-50%.png
│ ├── bot-make-friend-addme-50%.png
│ ├── bot-make-friend-addme.png
│ ├── bot-manager-50%.png
│ ├── bot-manager.png
│ ├── bot-missile-launched-50%.png
│ ├── bot-missile-launched.png
│ ├── bot-relay.message-push-qiushibaike.png
│ ├── bot-relay.notify-transmission-download-complete-50%.png
│ ├── bot-relay.scheduled-sign-50%.png
│ ├── bot-shell-command-50%.png
│ ├── bot-shell-command.png
│ ├── bot-simple-address-book-50%.png
│ └── text-QR-code.png
├── logging.properties
├── run.sh
├── sql
├── emoji.mysql.sql
├── save-messages.mysql.sql
├── simple-address-book.mysql.sql
└── wechat-contacts.mysql.sql
└── src
├── config.dist.properties
├── net_maclife_util_ANSIEscapeTool.java
├── net_maclife_util_BaiduCloud.java
├── net_maclife_util_HTTPUtils.java
├── net_maclife_wechat_http_Bot.java
├── net_maclife_wechat_http_BotApp.java
└── net_maclife_wechat_http_BotEngine.java
/README.md:
--------------------------------------------------------------------------------
1 | doc/ReadMe.中文.md
--------------------------------------------------------------------------------
/bots-industrial/net_maclife_wechat_http_Bot_HCICloudCSR.java:
--------------------------------------------------------------------------------
1 | import java.io.*;
2 | import java.util.*;
3 |
4 | import org.apache.commons.lang3.*;
5 |
6 | import com.fasterxml.jackson.core.*;
7 | import com.fasterxml.jackson.databind.*;
8 |
9 | /**
10 | * 基于 http 协议的捷通华声 HCICloud CSR (灵云智能客服) 对话机器人。
11 | *
12 | * @author liuyan
13 | *
14 | */
15 | public class net_maclife_wechat_http_Bot_HCICloudCSR extends net_maclife_wechat_http_Bot
16 | {
17 | String HCICLOUD_SERVER_ADDRESS = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.hcicloud.csr.server.address");
18 | int HCICLOUD_SERVER_PORT = net_maclife_wechat_http_BotApp.GetConfig ().getInt ("bot.hcicloud.csr.server.port");
19 | String HCICLOUD_CSR_URL__Query = "http://" + HCICLOUD_SERVER_ADDRESS + (HCICLOUD_SERVER_PORT==80 ? "" : ":" + HCICLOUD_SERVER_PORT) + "/CSRBroker/queryAction";
20 |
21 | String HCICLOUD_APP_KEY = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.hcicloud.csr.app.key");
22 |
23 | String HCICLOUD_CSR_ROBOT_ID = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.hcicloud.csr.robot.id");
24 | String HCICLOUD_CSR_ROBOT_CHANNEL_NUMBER = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.hcicloud.csr.robot.channel.number");
25 | String HCICLOUD_CSR_TALKER_ID = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.hcicloud.csr.robot.talker.id");
26 | String HCICLOUD_CSR_RECEIVER_ID = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.hcicloud.csr.robot.receiver.id");
27 | String HCICLOUD_CHARSET_ENCODING = net_maclife_wechat_http_BotApp.utf8;
28 |
29 | @Override
30 | public int OnTextMessageReceived
31 | (
32 | JsonNode jsonMessage,
33 | JsonNode jsonFrom, String sFromAccount, String sFromName, boolean isFromMe,
34 | JsonNode jsonTo, String sToAccount, String sToName, boolean isToMe,
35 | JsonNode jsonReplyTo, String sReplyToAccount, String sReplyToName, boolean isReplyToRoom,
36 | JsonNode jsonReplyTo_RoomMember, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember,
37 | JsonNode jsonReplyTo_Person, String sReplyToAccount_Person, String sReplyToName_Person,
38 | String sContent, boolean isContentMentionedMe, boolean isContentMentionedMeFirst
39 | )
40 | {
41 | try
42 | {
43 | JsonNode jsonCSRResponse = GetCSRReponse (sFromAccount, sContent);
44 | if (jsonCSRResponse == null)
45 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
46 |
47 | String sResponse = ParseCSRResponse (jsonCSRResponse);
48 | if (StringUtils.isNotEmpty (sResponse))
49 | {
50 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sResponse);
51 | }
52 | }
53 | catch (Exception e)
54 | {
55 | e.printStackTrace ();
56 | }
57 |
58 | return
59 | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__PROCESSED
60 | | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
61 | }
62 |
63 | public String ParseCSRResponse (JsonNode jsonCSRResponse)
64 | {
65 | net_maclife_wechat_http_BotApp.logger.finer ("\n" + jsonCSRResponse);
66 | if (jsonCSRResponse == null || jsonCSRResponse.isNull())
67 | return "";
68 |
69 | int nProtocolId = net_maclife_wechat_http_BotApp.GetJSONInt (jsonCSRResponse, "protocolId");
70 | assert (nProtocolId == 6);
71 |
72 | int nResult = net_maclife_wechat_http_BotApp.GetJSONInt (jsonCSRResponse, "result");
73 |
74 | //JsonNode nodeSendTime = jsonResponse.get ("sendTime");
75 | //int nAnswerTypeID = net_maclife_wechat_http_BotApp.GetJSONInt (jsonCSRResponse, "answerTypeId");
76 | JsonNode nodeSingleNode = jsonCSRResponse.get ("singleNode");
77 | JsonNode nodeVagueNode = jsonCSRResponse.get ("vagueNode");
78 |
79 | String sResult = "";
80 | if (nResult == 0)
81 | {
82 | /*
83 | switch (nAnswerTypeID)
84 | {
85 | case 1: // 系统错误
86 | case 2 // 有敏感词
87 | case 3: // 无法回答
88 |
89 | case 4: // 需要补问
90 | case 6: // 能够回答 (正常情况)
91 | case 8: // 模式匹配
92 |
93 | case 10: // 聊天过频
94 | sResult = nodeSingleNode
95 | break;
96 | case 7: // 无法回答
97 | sResult = "无法回答";
98 | break;
99 | }
100 | */
101 | if (nodeSingleNode != null && !nodeSingleNode.isNull())
102 | {
103 | sResult = nodeSingleNode.get ("answerMsg").asText ();
104 | }
105 | if (nodeVagueNode != null && !nodeVagueNode.isNull())
106 | {
107 | JsonNode jsonItemList = nodeVagueNode.get ("itemList");
108 | if (jsonItemList!=null && !jsonItemList.isNull () && jsonItemList.size ()>0)
109 | {
110 | StringBuilder sbPromptMessages = new StringBuilder ();
111 | sbPromptMessages.append (nodeVagueNode.get ("promptVagueMsg").asText ());
112 | sbPromptMessages.append ("\n");
113 | for (JsonNode item : nodeVagueNode.get ("itemList"))
114 | {
115 | sbPromptMessages.append (item.get ("num").asInt ());
116 | sbPromptMessages.append (". ");
117 | sbPromptMessages.append (item.get ("question").asText ());
118 | sbPromptMessages.append (" (匹配分值 ");
119 | sbPromptMessages.append (item.get ("score").asInt ());
120 | sbPromptMessages.append (")\n");
121 | }
122 | sbPromptMessages.append (nodeVagueNode.get ("endVagueMsg").asText ());
123 | //sbResult.append ("\n");
124 | sResult = sResult + (StringUtils.isEmpty(sResult) ? "" : "\n\n") + sbPromptMessages.toString ();
125 | }
126 | }
127 | else
128 | {
129 | //throw new RuntimeException ("什么鬼,怎么会出这种错误");
130 | }
131 | }
132 | else
133 | {
134 | sResult = "调用机器人接口返回失败的结果";
135 | }
136 | return sResult;
137 | }
138 |
139 | public JsonNode GetCSRReponse (String sFrom_EncryptedAccount, String sInput)
140 | {
141 | try
142 | {
143 | Map mapRequestHeaders = new HashMap ();
144 | mapRequestHeaders.put ("Content-Type", "application/json");
145 |
146 | if (sFrom_EncryptedAccount.length () > 40)
147 | sFrom_EncryptedAccount = StringUtils.left (sFrom_EncryptedAccount, 40);
148 |
149 | String sRequestBody_JSONString =
150 | "{\n" +
151 | " \"protocolId\": 5,\n" +
152 | " \"robotHashCode\": \"" + HCICLOUD_CSR_ROBOT_ID + "\",\n" +
153 | " \"platformConnType\": \"" + HCICLOUD_CSR_ROBOT_CHANNEL_NUMBER + "\",\n" +
154 | " \"userId\": \"" + sFrom_EncryptedAccount + "\",\n" +
155 | " \"talkerId\": \"" + HCICLOUD_CSR_TALKER_ID + "\",\n" +
156 | " \"receiverId\": \"" + HCICLOUD_CSR_RECEIVER_ID + "\",\n" +
157 | " \"appKey\": \"" + HCICLOUD_APP_KEY + "\",\n" +
158 | " \"sendTime\": " + System.currentTimeMillis() + ",\n" +
159 | " \"type\": \"text\",\n" +
160 | " \"isNeedClearHistory\": 1,\n" +
161 | " \"isQuestionQuery\": 0,\n" +
162 | " \"query\": \"" + sInput + "\",\n" +
163 | " \"__LAST__\": 0\n" +
164 | "}";
165 | net_maclife_wechat_http_BotApp.logger.finer ("\n" + sRequestBody_JSONString);
166 | // 如果不传递 RequestHeader,则返回:“访问来源变量未定义”
167 | // {"aiResult":null,"answerTypeId":1,"protocolId":6,"result":0,"sendTime":null,"serviceLogId":null,"singleNode":{"answerMsg":"访问来源变量未定义","cmd":null,"isRichText":0,"list":null,"question":null,"score":0.0,"standardQuestion":"","standardQuestionId":0},"vagueNode":null}
168 |
169 | // 如果用微信的加密帐号当 userId 传递,则返回:“访问来源名过长”
170 | // {"aiResult":null,"answerTypeId":1,"protocolId":6,"result":0,"sendTime":null,"serviceLogId":null,"singleNode":{"answerMsg":"访问来源名过长","cmd":null,"isRichText":0,"list":null,"question":null,"score":0.0,"standardQuestion":"","standardQuestionId":0},"vagueNode":null}
171 |
172 | InputStream is = net_maclife_util_HTTPUtils.CURL_Post_Stream (HCICLOUD_CSR_URL__Query, mapRequestHeaders, sRequestBody_JSONString.getBytes (HCICLOUD_CHARSET_ENCODING));
173 |
174 | JsonNode jsonCSRResponse = net_maclife_wechat_http_BotApp.jacksonObjectMapper_Loose.readTree (is);
175 | return jsonCSRResponse;
176 | }
177 | catch (Exception e)
178 | {
179 | e.printStackTrace ();
180 | }
181 | return null;
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/bots-industrial/net_maclife_wechat_http_Bot_iConTek.java:
--------------------------------------------------------------------------------
1 | import java.io.*;
2 | import java.net.*;
3 | import java.util.*;
4 |
5 | import org.apache.commons.lang3.*;
6 |
7 | import com.fasterxml.jackson.core.*;
8 | import com.fasterxml.jackson.databind.*;
9 |
10 | import org.jsoup.*;
11 | import org.jsoup.nodes.*;
12 | import org.jsoup.select.*;
13 |
14 | /**
15 | * 基于 http 协议的 iConTek 客服机器人。
16 | *
17 | *
18 | * 与捷通华声 HCICloudCSR (灵云智能客服)机器人类似,该机器人也是利用 iConTek 的 HTTP 接口,将文字(或者语音?)发给其引擎,返回特定的结果,完成机器人客服对话的功能。
19 | *
20 | *
21 | * iConTek 有两套引擎,一个是基于语音对话的 (Sandroid)、一个是基于纯文本的 (Tandroid),由于微信网页版目前无法发语音消息,所以,只能使用 iConTek-Tandroid 引擎来处理。
22 | *
23 | *
24 | * @author liuyan
25 | *
26 | */
27 | public class net_maclife_wechat_http_Bot_iConTek extends net_maclife_wechat_http_Bot
28 | {
29 | String iConTek_Tandroid_SERVER_HOST = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.iConTek.Tandroid.server.host");
30 | int iConTek_Tandroid_SERVER_PORT = net_maclife_wechat_http_BotApp.GetConfig ().getInt ("bot.iConTek.Tandroid.server.port");
31 | static String iConTek_Tandroid_SERVER_SCHEME = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.iConTek.Tandroid.server.scheme");
32 | String iConTek_Tandroid_BaseURL__Query = (StringUtils.equalsIgnoreCase (iConTek_Tandroid_SERVER_SCHEME, "https") ? "https" : "http") + "://" + iConTek_Tandroid_SERVER_HOST + ":" + iConTek_Tandroid_SERVER_PORT + "/queryj";
33 |
34 | String iConTek_CHARSET_ENCODING = net_maclife_wechat_http_BotApp.utf8;
35 |
36 | /**
37 | * 会话列表(会话,对应于请求 Tandroid 时 URL 中的 queryid)。
38 | * 由于 iConTek Tandroid 是基于知识库的问答机器人引擎,对于顺序型(Stepping)、深挖型(细化型,DrillDown)、树状型(Branching)这些问题类型,一定是一个问题接上一个问题来问答的,这样会导致一个问题:什么时候问题结束,不知道(API 不提供这样的接口?)。
39 | * 所以,需要手工维护一个会话列表,用单独的命令开启新会话。系统还要做个定时任务,定时清理过期(比如:5 分钟内无问答?)
40 | *
41 | * Map
中的 Key 说明:
42 | *
43 | * - session-id
-
44 | *
- 会话 ID,一般是微信昵称加上一个数字(Timestamp?)
-
45 |
46 | *
- a
47 | *
48 |
49 | * - b
50 | *
51 |
52 | * - last-active-time
53 | * - 最后活动时间。
long
类型。清理会话的任务将根据该
54 |
55 | *
56 | *
57 | */
58 | Map> mapSessions = new HashMap> ();
59 |
60 | @Override
61 | public int OnTextMessageReceived
62 | (
63 | JsonNode jsonMessage,
64 | JsonNode jsonFrom, String sFromAccount, String sFromName, boolean isFromMe,
65 | JsonNode jsonTo, String sToAccount, String sToName, boolean isToMe,
66 | JsonNode jsonReplyTo, String sReplyToAccount, String sReplyToName, boolean isReplyToRoom,
67 | JsonNode jsonReplyTo_RoomMember, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember,
68 | JsonNode jsonReplyTo_Person, String sReplyToAccount_Person, String sReplyToName_Person,
69 | String sContent, boolean isContentMentionedMe, boolean isContentMentionedMeFirst
70 | )
71 | {
72 | List listCommands = net_maclife_wechat_http_BotApp.GetConfig ().getList (String.class, "bot.iConTek.commands");
73 | if (listCommands==null || listCommands.isEmpty ()) // 如果未配置命令,则不处理
74 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
75 |
76 | try
77 | {
78 | if (! net_maclife_wechat_http_BotApp.hasCommandPrefix (sContent))
79 | {
80 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
81 | }
82 | sContent = net_maclife_wechat_http_BotApp.StripOutCommandPrefix (sContent);
83 |
84 |
85 | // 解析命令行
86 | String[] arrayMessages = sContent.split ("\\s+", 2);
87 | if (arrayMessages==null || arrayMessages.length<1)
88 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
89 |
90 | String sCommandInputed = arrayMessages[0];
91 | String sCommandParametersInputed = null;
92 | if (arrayMessages.length >= 2)
93 | sCommandParametersInputed = arrayMessages[1];
94 |
95 | String[] arrayCommandAndOptions = sCommandInputed.split ("\\" + net_maclife_wechat_http_BotApp.COMMAND_OPTION_SEPARATOR + "+", 2);
96 | sCommandInputed = arrayCommandAndOptions[0];
97 | String sCommandOptionsInputed = null;
98 | //String[] arrayCommandOptions = null; // 该 Bot 只支持一个命令选项: new-session (/icontek.new-session),所以,不再尝试解析多个 options
99 | if (arrayCommandAndOptions.length >= 2)
100 | {
101 | sCommandOptionsInputed = arrayCommandAndOptions[1];
102 | //arrayCommandOptions = sCommandOptionsInputed.split ("\\" + net_maclife_wechat_http_BotApp.COMMAND_OPTION_SEPARATOR + "+");
103 | }
104 |
105 | // 检查命令有效性
106 | boolean bValidCommand = false;
107 | for (int i=0; i mapSession = GetSession (sReplyToAccount_Person); // 根据说话“人”的帐号,获取会话
125 | JsonNode jsonTandroidResponse = GetTandroidReponse (mapSession, sCommandParametersInputed);
126 | if (jsonTandroidResponse == null)
127 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
128 |
129 | String sResponse = ParseTandroidResponse (mapSession, jsonTandroidResponse);
130 | if (StringUtils.isNotEmpty (sResponse))
131 | {
132 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sResponse);
133 | }
134 | }
135 | catch (Exception e)
136 | {
137 | e.printStackTrace ();
138 | }
139 |
140 | return
141 | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__PROCESSED
142 | | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
143 | }
144 |
145 | Map GetSession (String sFromAccount)
146 | {
147 | Map mapSession = mapSessions.get (sFromAccount);
148 | if (mapSession != null)
149 | return mapSession;
150 | else
151 | return NewSession (sFromAccount);
152 | }
153 |
154 | Map NewSession (String sFromAccount)
155 | {
156 | Map mapSession = new HashMap ();
157 | mapSession.put ("last-active-time", System.currentTimeMillis ());
158 | mapSession.put ("account", sFromAccount);
159 | mapSession.put ("session-id", sFromAccount + "-" + mapSession.get ("last-active-time"));
160 | mapSessions.put (sFromAccount, mapSession);
161 | return mapSession;
162 | }
163 |
164 | public JsonNode GetTandroidReponse (Map mapSession, String sInput)
165 | {
166 | try
167 | {
168 | String sURL = iConTek_Tandroid_BaseURL__Query + "/x/" + (mapSession.get ("follow-up-question-id")==null ? "" : URLEncoder.encode ((String)mapSession.get ("follow-up-question-id"), iConTek_CHARSET_ENCODING) + "/") + mapSession.get ("session-id") + "/" + URLEncoder.encode (sInput, iConTek_CHARSET_ENCODING);
169 | net_maclife_wechat_http_BotApp.logger.finer (GetName() + " 请求的网址: " + sURL);
170 | Document doc = Jsoup.connect (sURL)
171 | .ignoreContentType (true)
172 | .validateTLSCertificates (false)
173 | //.header ("Content-Type", "application/json")
174 | //.header (name, value)
175 | .get ();
176 |
177 | String sResult = doc.text ();
178 | //net_maclife_wechat_http_BotApp.logger.finer (GetName() + " 获取到的消息:\n" + sResult);
179 | JsonNode jsonCSRResponse = net_maclife_wechat_http_BotApp.jacksonObjectMapper_Loose.readTree (sResult);
180 | return jsonCSRResponse;
181 | }
182 | catch (Exception e)
183 | {
184 | e.printStackTrace ();
185 | }
186 | return null;
187 | }
188 |
189 | public String ParseTandroidResponse (Map mapSession, JsonNode jsonTandroidResponse)
190 | {
191 | net_maclife_wechat_http_BotApp.logger.finer ("\n" + jsonTandroidResponse);
192 | if (jsonTandroidResponse == null || jsonTandroidResponse.isNull())
193 | return "";
194 |
195 | String sResponseID = net_maclife_wechat_http_BotApp.GetJSONText (jsonTandroidResponse, "responseId");
196 |
197 | // 如果是响应「标准型」、「枚举型」的「分类」, answer 会是目标知识点内的答案, 但如果找其他上下文功能的「分类」, 会有以下特别的输出:
198 | // 「顺序型问卷」,「树状型问卷」及「细化型」分类, answer 会输出 followUp 所指定的「分类」中特殊知识点内的 answer。
199 | String sAnswer = net_maclife_wechat_http_BotApp.GetJSONText (jsonTandroidResponse, "answer");
200 |
201 | String sConfidence = net_maclife_wechat_http_BotApp.GetJSONText (jsonTandroidResponse, "confidence");
202 |
203 | // y 是知识点 ID, 亦即是 FAQID 或意图 ID。
204 | String sFAQID = net_maclife_wechat_http_BotApp.GetJSONText (jsonTandroidResponse, "y");
205 |
206 | // followUp: 是系统认为开发者应该再访问的下一个「分类」位置, 只会出现在指定查找「顺序型问卷」、「树状型问卷」、「细化型」的「分类」才会出现, 查找「枚举型」或「标准型」的「分类」是不会出现 followUp。
207 | String sFollowUp = net_maclife_wechat_http_BotApp.GetJSONText (jsonTandroidResponse, "followUp");
208 | if (StringUtils.isEmpty (sFollowUp))
209 | {
210 | mapSession.remove ("follow-up-question-id");
211 | }
212 | else
213 | {
214 | mapSession.put ("follow-up-question-id", sFollowUp);
215 | }
216 |
217 | String sResult = sAnswer;
218 | return sResult;
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/bots/net_maclife_wechat_http_Bot_BaiduImageSearch.java:
--------------------------------------------------------------------------------
1 | import java.io.*;
2 | import java.net.*;
3 |
4 | import org.apache.commons.io.*;
5 | import org.apache.commons.lang3.*;
6 | import org.jsoup.nodes.*;
7 | import org.jsoup.select.*;
8 |
9 | import com.fasterxml.jackson.databind.*;
10 |
11 | public class net_maclife_wechat_http_Bot_BaiduImageSearch extends net_maclife_wechat_http_Bot
12 | {
13 | @Override
14 | public int OnImageMessageReceived
15 | (
16 | JsonNode jsonMessage,
17 | JsonNode jsonFrom, String sFromAccount, String sFromName, boolean isFromMe,
18 | JsonNode jsonTo, String sToAccount, String sToName, boolean isToMe,
19 | JsonNode jsonReplyTo, String sReplyToAccount, String sReplyToName, boolean isReplyToRoom,
20 | JsonNode jsonReplyTo_RoomMember, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember,
21 | JsonNode jsonReplyTo_Person, String sReplyToAccount_Person, String sReplyToName_Person,
22 | String sContent, File fMedia, String sImageURL
23 | )
24 | {
25 | if ((fMedia == null || ! fMedia.exists ()) && StringUtils.isEmpty (sImageURL))
26 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
27 |
28 | Document doc = null;
29 | org.jsoup.Connection jsoup_conn = null;
30 | try
31 | {
32 | String sURL = null;
33 |
34 | if (StringUtils.isNotEmpty (sImageURL))
35 | {
36 | sURL = "http://image.baidu.com/n/pc_search?queryImageUrl=" + URLEncoder.encode (sImageURL, net_maclife_wechat_http_BotApp.utf8) + "&uptype=urlsearch";
37 | net_maclife_wechat_http_BotApp.logger.info (GetName() + " 按图片网址搜索,搜索网址为:\n" + sURL);
38 |
39 | }
40 | else
41 | {
42 | sURL = "https://image.baidu.com/n/image?fr=html5&target=pcSearchImage&needJson=true&id=WU_FILE_0&name=" + URLEncoder.encode (fMedia.getName (), net_maclife_wechat_http_BotApp.utf8) + "&type=" + "&lastModifiedDate=" + "&size=" + fMedia.length ();
43 | //jsoup_conn = org.jsoup.Jsoup.connect (sURL)
44 | // .data ("", "", new FileInputStream (fMedia))
45 | // ;
46 | //doc = jsoup_conn.post ();
47 | byte[] arrayPostData = IOUtils.toByteArray (new FileInputStream (fMedia), fMedia.length ());
48 | String sJSONString = net_maclife_util_HTTPUtils.CURL_Post (sURL, arrayPostData);
49 | /*
50 | {
51 | "errno":0,
52 | "errmsg":"",
53 | "data":{
54 | "querySign":"1835526740,788942838",
55 | "imageUrl":"http:\/\/b.hiphotos.baidu.com\/image\/pic\/item\/2f738bd4b31c8701602dcef02e7f9e2f0708ff09.jpg",
56 | "pageUrl":"https:\/\/image.baidu.com\/n\/pc_search?rn=30&appid=0&tag=1&isMobile=0&queryImageUrl=http%3A%2F%2Fb.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F2f738bd4b31c8701602dcef02e7f9e2f0708ff09.jpg&querySign=1835526740%2C788942838&fromProduct=&productBackUrl="
57 | },
58 | "extra":[
59 |
60 | ]
61 | } */
62 | net_maclife_wechat_http_BotApp.logger.info (GetName() + " 上传图片后返回的 JSON\n" + sJSONString);
63 | JsonNode jsonUploadImageResult = net_maclife_wechat_http_BotApp.jacksonObjectMapper_Loose.readTree (sJSONString);
64 | int errno = net_maclife_wechat_http_BotApp.GetJSONInt (jsonUploadImageResult, "errno");
65 | if (errno == 0)
66 | {
67 | JsonNode jsonData = jsonUploadImageResult.get ("data");
68 | sURL = net_maclife_wechat_http_BotApp.GetJSONText (jsonData, "pageUrl");
69 | net_maclife_wechat_http_BotApp.logger.info (GetName() + " 上传图片后返回的 JSON 中的图片搜索网页的网址\n" + sURL);
70 | }
71 | else
72 | {
73 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
74 | }
75 | }
76 |
77 | doc = org.jsoup.Jsoup.connect (sURL).timeout (net_maclife_util_HTTPUtils.DEFAULT_READ_TIMEOUT_SECOND * 1000).get ();
78 | Elements e图片猜测 = doc.select ("#guessInfo");
79 | if (e图片猜测.isEmpty ())
80 | {
81 | net_maclife_wechat_http_BotApp.logger.info (GetName() + " 找不到 #guessInfo,也许,搜索出错了? " + doc.select (".error-text").text ());
82 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
83 | }
84 | Elements e图片猜测词语链接 = e图片猜测.select (".guess-info-text a.guess-info-word-link");
85 | if (e图片猜测词语链接.isEmpty ())
86 | {
87 | net_maclife_wechat_http_BotApp.logger.info (GetName() + " 找不到 .guess-info-text a.guess-info-word-link,也许,没有结果? " + e图片猜测.text ());
88 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
89 | }
90 |
91 | StringBuilder sbInfo = new StringBuilder ();
92 | sbInfo.append (e图片猜测词语链接.text ());
93 | sbInfo.append ("\n");
94 | sbInfo.append (e图片猜测词语链接.first ().absUrl ("href"));
95 | Elements e图片来源 = doc.select ("#sourceCard");
96 | if (! e图片来源.isEmpty ())
97 | {
98 | sbInfo.append ("\n\n" + e图片来源.select (".source-card-header-count").first ().text ()); // "发现 N 条图片来源"
99 | Elements e图片来源标题链接 = e图片来源.select (".source-card-topic-title-link");
100 | for (int i=0; i 0);
157 | for (int i=0; i注意:这个接口并不提供免费使用。调用时会报错: 百度 OCR 通用文字识别(含生僻字版) 返回失败:18 Open api qps request limit reached
208 | * @param fMedia
209 | * @return
210 | */
211 | public static JsonNode ProcessOCR_GeneralEnhanced (File fMedia) throws IOException, InterruptedException, ExecutionException
212 | {
213 | return ProcessOCR_Common (fMedia, BAIDU_OCR_URL__GeneralEnhanced, "通用文字识别(含生僻字版)");
214 | }
215 |
216 | /**
217 | * 网络图片文字识别
218 | * @param fMedia
219 | * @return
220 | */
221 | public static JsonNode ProcessOCR_WebImage (File fMedia) throws IOException, InterruptedException, ExecutionException
222 | {
223 | return ProcessOCR_Common (fMedia, BAIDU_OCR_URL__WebImage, "网络图片文字识别");
224 | }
225 |
226 | /**
227 | * 表格文字识别(异步接口)。
228 | * 注意,调用方直接读取返回值即可,不需要做异步处理,本函数自己做了异步处理 -- 调用者如同使用同步接口一样,无需关心异步返回结果问题的处理
229 | * @param fMedia
230 | * @return
231 | * @throws ExecutionException
232 | * @throws InterruptedException
233 | */
234 | public static JsonNode ProcessOCR_Form_Async (File fMedia) throws IOException, InterruptedException, ExecutionException
235 | {
236 | String sAccessToken = net_maclife_util_BaiduCloud.GetBaiduAccessToken (sBaiduCloudAppKey, sBaiduCloudAppPassword, sBaiduOAuthAccessTokenFileInJSONFormat);
237 | if (StringUtils.isEmpty (sAccessToken))
238 | return null;
239 |
240 | byte[] arrayImageData = IOUtils.toByteArray (new FileInputStream (fMedia));
241 | String sImageBase64 = Base64.encodeBase64String (arrayImageData); //Base64.encodeBase64URLSafeString (arrayImageData);
242 | //System.out.println (sImageBase64);
243 | //String sImageBase64_URLEncoded = URLEncoder.encode (sImageBase64, net_maclife_wechat_http_BotApp.utf8);
244 | //System.out.println (sImageBase64_URLEncoded);
245 | String sURL = BAIDU_OCR_URL__Form_Async__Request + "?access_token=" + sAccessToken + "";
246 | org.jsoup.Connection jsoup_conn = null;
247 | jsoup_conn = org.jsoup.Jsoup.connect (sURL)
248 | .header ("Content-Type", "application/x-www-form-urlencoded")
249 | .ignoreContentType (true)
250 | .data ("image", sImageBase64)
251 | ;
252 | Document doc = jsoup_conn.post ();
253 | JsonNode jsonResponse = net_maclife_wechat_http_BotApp.jacksonObjectMapper_Loose.readTree (doc.text ());
254 | System.out.println (jsonResponse);
255 | if (jsonResponse.get ("error_code") != null)
256 | {
257 | net_maclife_wechat_http_BotApp.logger.warning ("百度 OCR 表格文字识别(异步接口) 发出请求后返回失败:" + net_maclife_wechat_http_BotApp.GetJSONText (jsonResponse, "error_code") + " " + net_maclife_wechat_http_BotApp.GetJSONText (jsonResponse, "error_msg"));
258 | return null;
259 | }
260 | assert (jsonResponse.get ("request") != null && jsonResponse.get ("request").isArray ());
261 | JsonNode jsonResult = jsonResponse.get ("result");
262 | assert (jsonResult.size () > 0);
263 | String sRequestID = net_maclife_wechat_http_BotApp.GetJSONText (jsonResult.get (0), "request_id");
264 |
265 | Callable taskAsyncGetResult = new FormOCR_AsyncGetResultTask (sRequestID);
266 | JsonNode jsonOCRResult = net_maclife_wechat_http_BotApp.executor.submit (taskAsyncGetResult).get ();
267 | return jsonOCRResult;
268 | }
269 |
270 | static class FormOCR_AsyncGetResultTask implements Callable
271 | {
272 | String sRequestID;
273 | public FormOCR_AsyncGetResultTask (String sRequestID)
274 | {
275 | this.sRequestID = sRequestID;
276 | }
277 |
278 | @Override
279 | public JsonNode call () throws Exception
280 | {
281 | try
282 | {
283 | int nProgressPercent = 0;
284 | do
285 | {
286 | String sAccessToken = net_maclife_util_BaiduCloud.GetBaiduAccessToken (sBaiduCloudAppKey, sBaiduCloudAppPassword, sBaiduOAuthAccessTokenFileInJSONFormat);
287 | if (StringUtils.isEmpty (sAccessToken))
288 | return null;
289 | String sURL = BAIDU_OCR_URL__Form_Async__GetResult + "?access_token=" + sAccessToken + "";
290 | org.jsoup.Connection jsoup_conn = null;
291 | jsoup_conn = org.jsoup.Jsoup.connect (sURL)
292 | .header ("Content-Type", "application/x-www-form-urlencoded")
293 | .ignoreContentType (true)
294 | .data ("request_id", sRequestID)
295 | .data ("result_type", "json") // request_type 若不设置,则默认为 "excel",但这里我们需要的是 "json"
296 | ;
297 | Document doc = jsoup_conn.post ();
298 | JsonNode jsonResponse = net_maclife_wechat_http_BotApp.jacksonObjectMapper_Loose.readTree (doc.text ());
299 | System.out.println (jsonResponse);
300 | if (jsonResponse.get ("error_code") != null)
301 | {
302 | net_maclife_wechat_http_BotApp.logger.warning ("百度 OCR 表格文字识别(异步接口) 获取请求结果返回失败:" + net_maclife_wechat_http_BotApp.GetJSONText (jsonResponse, "error_code") + " " + net_maclife_wechat_http_BotApp.GetJSONText (jsonResponse, "error_msg"));
303 | return null;
304 | }
305 | assert (jsonResponse.get ("result") != null);
306 | JsonNode jsonRequest = jsonResponse.get ("result");
307 | nProgressPercent = net_maclife_wechat_http_BotApp.GetJSONInt (jsonRequest, "percent");
308 | if (nProgressPercent == 100)
309 | {
310 | return jsonRequest.get ("result_data");
311 | }
312 |
313 | TimeUnit.SECONDS.sleep (2);
314 | } while (nProgressPercent != 100);
315 | }
316 | catch (Exception e)
317 | {
318 | e.printStackTrace ();
319 | }
320 | return null;
321 | }
322 | }
323 |
324 | public static void main (String[] args) throws Exception
325 | {
326 | if (args.length < 1)
327 | {
328 | System.err.println ("需要指定一个图片文件 (.jpg 或 .png 或 .bmp 格式的图片文件)");
329 | return;
330 | }
331 | String sFileName = args[0];
332 | File fMedia = new File (sFileName);
333 |
334 | //ProcessOCR_GeneralBasic (fMedia);
335 | //ProcessOCR_AccurateBasic (fMedia);
336 | //ProcessOCR_General (fMedia);
337 | ProcessOCR_Accurate (fMedia);
338 | //ProcessOCR_GeneralEnhanced (fMedia);
339 | //ProcessOCR_WebImage (fMedia);
340 |
341 | //ProcessOCR_Form_Async (fMedia);
342 | }
343 | }
344 |
--------------------------------------------------------------------------------
/bots/net_maclife_wechat_http_Bot_BaiduTranslate.java:
--------------------------------------------------------------------------------
1 | import java.net.*;
2 | import java.util.*;
3 |
4 | import org.apache.commons.codec.digest.*;
5 | import org.apache.commons.lang3.*;
6 |
7 | import com.fasterxml.jackson.databind.*;
8 |
9 | /**
10 | * 百度翻译机器人小程序。
11 | * @author liuyan
12 | *
13 | */
14 | public class net_maclife_wechat_http_Bot_BaiduTranslate extends net_maclife_wechat_http_Bot
15 | {
16 | public static final String BAIDU_TRANSLATE_HTTP_URL = "http://api.fanyi.baidu.com/api/trans/vip/translate";
17 | public static final String BAIDU_TRANSLATE_HTTPS_URL = "https://fanyi-api.baidu.com/api/trans/vip/translate";
18 |
19 | //public static final String REGEXP_LanguageOptions = "(\\w*)(2*)(\\w*)";
20 | //public static final Pattern PATTERN_LanguageOptions = Pattern.compile (REGEXP_LanguageOptions);
21 |
22 | @Override
23 | public int OnTextMessageReceived
24 | (
25 | JsonNode jsonMessage,
26 | JsonNode jsonFrom, String sFromAccount, String sFromName, boolean isFromMe,
27 | JsonNode jsonTo, String sToAccount, String sToName, boolean isToMe,
28 | JsonNode jsonReplyTo, String sReplyToAccount, String sReplyToName, boolean isReplyToRoom,
29 | JsonNode jsonReplyTo_RoomMember, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember,
30 | JsonNode jsonReplyTo_Person, String sReplyToAccount_Person, String sReplyToName_Person,
31 | String sContent, boolean isContentMentionedMe, boolean isContentMentionedMeFirst
32 | )
33 | {
34 | List listCommands = net_maclife_wechat_http_BotApp.GetConfig ().getList (String.class, "bot.baidu-translate.commands");
35 | if (listCommands==null || listCommands.isEmpty ()) // 如果未配置命令,则不处理
36 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
37 |
38 | if (! net_maclife_wechat_http_BotApp.hasCommandPrefix (sContent))
39 | {
40 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
41 | }
42 | sContent = net_maclife_wechat_http_BotApp.StripOutCommandPrefix (sContent);
43 |
44 | String sFromLanguage = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.baidu-translate.from-language");
45 | String sToLanguage = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.baidu-translate.to-language");
46 |
47 | try
48 | {
49 | String[] arrayMessages = sContent.split ("\\s+", 2);
50 | if (arrayMessages==null || arrayMessages.length<1)
51 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
52 |
53 | String sCommandInputed = arrayMessages[0];
54 | String sCommandParametersInputed = null;
55 | if (arrayMessages.length >= 2)
56 | sCommandParametersInputed = arrayMessages[1];
57 |
58 | String[] arrayCommandOptions = sCommandInputed.split ("\\.+", 2);
59 | sCommandInputed = arrayCommandOptions[0];
60 | String sCommandOptionsInputed = null;
61 | if (arrayCommandOptions.length >= 2)
62 | sCommandOptionsInputed = arrayCommandOptions[1];
63 |
64 | for (int i=0; i\n\n可选的语言代码选项的格式:\n - .原文语言代码\n - .2译文语言代码\n - .原文语言代码2译文语言代码\n\n具体能用哪些语言代码,请参照: http://api.fanyi.baidu.com/api/trans/product/apidoc#languageList 给出的语言代码列表");
73 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
74 | }
75 |
76 | // 解析命令“翻译语言选项”:.src2dst、.src、.2dst
77 | if (StringUtils.isNotEmpty (sCommandOptionsInputed))
78 | {
79 | //Matcher matcher = PATTERN_LanguageOptions.matcher (sCommandOptionsInputed);
80 | //if (sCommandOptionsInputed.matches ()
81 | //不用规则表达式来解析了,还不如用简单的字符串格式判断
82 | if (StringUtils.contains (sCommandOptionsInputed, "2"))
83 | {
84 | if (sCommandOptionsInputed.startsWith ("2"))
85 | { // 只指定了译文语言代码
86 | sToLanguage = sCommandOptionsInputed.substring (1);
87 | }
88 | else
89 | {
90 | String[] arrayFromTo = sCommandOptionsInputed.split ("2");
91 | sFromLanguage = arrayFromTo[0];
92 | sToLanguage = arrayFromTo[1];
93 | }
94 | }
95 | else
96 | { // 只指定了原文的语言代码
97 | sFromLanguage = sCommandOptionsInputed;
98 | }
99 | }
100 | if (StringUtils.isEmpty (sFromLanguage) || StringUtils.isEmpty (sToLanguage))
101 | {
102 | String sErrorInfo = GetName() + " 的原文、译文的语言代码不能为空";
103 | //net_maclife_wechat_http_BotApp.logger.warning (sErrorInfo);
104 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sErrorInfo);
105 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
106 | }
107 | if (StringUtils.equalsIgnoreCase (sToLanguage, "auto"))
108 | {
109 | String sErrorInfo = GetName() + "机器人设置的目标语言不能为 auto";
110 | //net_maclife_wechat_http_BotApp.logger.warning (sErrorInfo);
111 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sErrorInfo);
112 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
113 | }
114 |
115 |
116 | // 命令行命令格式没问题,现在开始查询数据库
117 | String sTranslation = null;
118 | JsonNode jsonResult = GetTranslation (sCommandParametersInputed, sFromLanguage, sToLanguage);
119 | if (jsonResult == null)
120 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
121 | JsonNode jsonErrorCode = jsonResult.get ("error_code");
122 | if (jsonErrorCode != null && !jsonErrorCode.isNull ())
123 | {
124 | String sErrorInfo = GetName() + " 返回错误结果: " + net_maclife_wechat_http_BotApp.GetJSONText (jsonResult, "error_code") + ": " + net_maclife_wechat_http_BotApp.GetJSONText (jsonResult, "error_msg");
125 | //net_maclife_wechat_http_BotApp.logger.warning (sErrorInfo);
126 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sErrorInfo);
127 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
128 | }
129 | JsonNode jsonTransResults = jsonResult.get ("trans_result");
130 | if (jsonTransResults.size () == 1)
131 | {
132 | JsonNode jsonTransResult = jsonTransResults.get (0);
133 | sTranslation = net_maclife_wechat_http_BotApp.GetJSONText (jsonTransResult, "dst");
134 | }
135 | else
136 | {
137 | StringBuilder sb = new StringBuilder ();
138 | for (int j=0; j= 2 && StringUtils.isNotEmpty (args[1]))
198 | sFrom = args[1];
199 | if (args.length >= 3 && StringUtils.isNotEmpty (args[2]))
200 | sTo = args[2];
201 | //net_maclife_wechat_http_Bot bot = new net_maclife_wechat_http_Bot_BaiduTranslate ();
202 |
203 | JsonNode jsonResult = GetTranslation (sQuery, sFrom, sTo);
204 | System.err.println (jsonResult);
205 | if (jsonResult.get ("error_code") != null && ! jsonResult.get ("error_code").isNull ())
206 | {
207 | System.err.println (net_maclife_wechat_http_BotApp.GetJSONText (jsonResult, "error_code") + ": " + net_maclife_wechat_http_BotApp.GetJSONText (jsonResult, "error_msg"));
208 | return;
209 | }
210 | JsonNode jsonTransResults = jsonResult.get ("trans_result");
211 | for (int i=0; i
17 | 语音识别结果
18 |
19 | {
20 | "corpus_no":"******",
21 | "err_msg":"*****.",
22 | "err_no":0, // 0 是成功,其他为失败
23 | "result": // 最多 5 个结果,或者根本不存在(出错时)
24 | [
25 | "账号已暂停使用,"
26 | ],
27 | "sn":"*****"
28 | }
29 |
30 |
31 |
32 | * @author liuyan
33 | *
34 | */
35 | public class net_maclife_wechat_http_Bot_BaiduVoice extends net_maclife_wechat_http_Bot
36 | {
37 | public static final String BAIDU_ASR_API_URL = "http://vop.baidu.com/server_api";
38 | public static final String BAIDU_TTS_API_URL = "http://tsn.baidu.com/text2audio";
39 |
40 | static String sBaiduCloudAppID = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.baidu.voice.app.id");
41 | static String sBaiduCloudAppKey = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.baidu.voice.app.key");
42 | static String sBaiduCloudAppPassword = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.baidu.voice.app.password");
43 | static String sBaiduOAuthAccessTokenFileInJSONFormat = net_maclife_wechat_http_BotApp.cacheDirectory + "/" + net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.baidu.voice.accessTokenFile");
44 |
45 | static String sMACAddress = "BaiduVoiceBotApplet";
46 | static
47 | {
48 | try
49 | {
50 | // 确保百度 AccessToken 目录存在
51 | File fBaiduDir = new File (sBaiduOAuthAccessTokenFileInJSONFormat).getParentFile ();
52 | fBaiduDir.mkdirs ();
53 |
54 | // 获取本机 MAC 地址
55 | //
56 | InetAddress ip = InetAddress.getLocalHost();
57 | net_maclife_wechat_http_BotApp.logger.info ("本机 IP 地址: " + ip.getHostAddress());
58 | NetworkInterface network = NetworkInterface.getByInetAddress (ip);
59 | if (network != null)
60 | {
61 | byte[] arrayMAC = network.getHardwareAddress();
62 | if (arrayMAC != null)
63 | {
64 | StringBuilder sb = new StringBuilder ();
65 | for (int i=0; i mapRequestHeaders = new HashMap ();
204 | mapRequestHeaders.put ("Content-Type", "audio/amr; rate=8000");
205 |
206 | InputStream is = new FileInputStream (fMedia);
207 | byte[] arrayPostData = IOUtils.toByteArray (is, fMedia.length ());
208 | is.close ();
209 |
210 | String sResponseBodyContent = net_maclife_util_HTTPUtils.CURL_Post (sURL, mapRequestHeaders, arrayPostData);
211 | JsonNode jsonNode = net_maclife_wechat_http_BotApp.jacksonObjectMapper_Loose.readTree (sResponseBodyContent);
212 | net_maclife_wechat_http_BotApp.logger.info (GetName() + " 机器人获取百度语音识别 (ASR) 的 http 响应消息体:");
213 | net_maclife_wechat_http_BotApp.logger.info (" " + sResponseBodyContent);
214 |
215 | int err_no = net_maclife_wechat_http_BotApp.GetJSONInt (jsonNode, "err_no");
216 | String err_msg = net_maclife_wechat_http_BotApp.GetJSONText (jsonNode, "err_msg");
217 | switch (err_no)
218 | {
219 | case 0:
220 | JsonNode jsonResults = jsonNode.get ("result");
221 | if (jsonResults.size () == 1)
222 | {
223 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, (StringUtils.isEmpty (sReplyToAccount_RoomMember) ? sReplyToName : sReplyToName_RoomMember) + " 说道:\n" + jsonResults.get (0).asText ());
224 | return
225 | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__PROCESSED
226 | | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
227 | }
228 |
229 | StringBuilder sb = new StringBuilder ();
230 | for (int i=0; i
15 | * 注意:当在手机上发 emoji 表情时,比如:😞,web 端收到的是类似 <span class="emoji emoji1f612"></span>
这样的文字(难道微信担心 Web 版在不同浏览器下表现不一致?)。
16 | * 手机微信上的 emoji 字符的显示,应该是(猜测)手机操作系统自己显示的,比如 android ios 用系统内置的 emoji 字体来显示(再说一遍,是猜测)。
17 | *
18 | *
19 | * emoji 数据库是从 http://unicode.org/emoji/charts/full-emoji-list.html (这个 html 有 36M 大小!!!) 获取,然后通过本程序生成 SQL 脚本文件,然后执行该 SQL 脚本文件写入的。
20 | *
21 | * @author liuyan
22 | *
23 | */
24 | public class net_maclife_wechat_http_Bot_Emoji extends net_maclife_wechat_http_Bot
25 | {
26 | @Override
27 | public int OnTextMessageReceived
28 | (
29 | JsonNode jsonMessage,
30 | JsonNode jsonFrom, String sFromAccount, String sFromName, boolean isFromMe,
31 | JsonNode jsonTo, String sToAccount, String sToName, boolean isToMe,
32 | JsonNode jsonReplyTo, String sReplyToAccount, String sReplyToName, boolean isReplyToRoom,
33 | JsonNode jsonReplyTo_RoomMember, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember,
34 | JsonNode jsonReplyTo_Person, String sReplyToAccount_Person, String sReplyToName_Person,
35 | String sContent, boolean isContentMentionedMe, boolean isContentMentionedMeFirst
36 | )
37 | {
38 | List listCommands = net_maclife_wechat_http_BotApp.GetConfig ().getList (String.class, "bot.emoji-test.commands");
39 | if (listCommands==null || listCommands.isEmpty ()) // 如果未配置命令,则不处理
40 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
41 |
42 | if (! net_maclife_wechat_http_BotApp.hasCommandPrefix (sContent))
43 | {
44 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
45 | }
46 | sContent = net_maclife_wechat_http_BotApp.StripOutCommandPrefix (sContent);
47 |
48 | try
49 | {
50 | String[] arrayMessages = sContent.split ("\\s+", 2);
51 | if (arrayMessages==null || arrayMessages.length<1)
52 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
53 |
54 | String sCommandInputed = arrayMessages[0];
55 | String sCommandParametersInputed = null;
56 | if (arrayMessages.length >= 2)
57 | sCommandParametersInputed = arrayMessages[1];
58 |
59 | String[] arrayCommandOptions = sCommandInputed.split ("\\.+", 2);
60 | sCommandInputed = arrayCommandOptions[0];
61 | String sCommandOptionsInputed = null;
62 | if (arrayCommandOptions.length >= 2)
63 | sCommandOptionsInputed = arrayCommandOptions[1];
64 |
65 | // 命令行命令格式没问题,现在开始查询数据库
66 | for (int i=0; i...\n\n比如:\n" + sCommand + " cat face");
74 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
75 | }
76 |
77 | // 解析命令“选项”: .detail .详细
78 | boolean bShowDetail = false;
79 | if (StringUtils.isNotEmpty (sCommandOptionsInputed))
80 | {
81 | arrayCommandOptions = sCommandOptionsInputed.split ("\\.+");
82 | for (String sCommandOption : arrayCommandOptions)
83 | {
84 | if (StringUtils.equalsIgnoreCase (sCommandOption, "detail") || StringUtils.equalsIgnoreCase (sCommandOption, "详细"))
85 | {
86 | bShowDetail = true;
87 | }
88 | }
89 | }
90 |
91 | try
92 | {
93 | String sResult = Query (sCommandParametersInputed, bShowDetail);
94 | if (StringUtils.isEmpty (sResult))
95 | {
96 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "找不到关键字为 " + sCommandParametersInputed + " 的 emoji 字符");
97 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
98 | }
99 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sResult);
100 | break;
101 | }
102 | catch (Exception e)
103 | {
104 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "查询出错: " + e);
105 | }
106 | }
107 | }
108 | }
109 | catch (Exception e)
110 | {
111 | e.printStackTrace ();
112 | }
113 |
114 | return
115 | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__PROCESSED
116 | | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
117 | }
118 |
119 | String Query (String sQuery, boolean bShowDetail) throws SQLException
120 | {
121 | String sTablePrefix = StringUtils.trimToEmpty (net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.emoji-test.jdbc.database-table.prefix"));
122 | net_maclife_wechat_http_BotApp.SetupDataSource ();
123 | java.sql.Connection conn = null;
124 | PreparedStatement stmt = null;
125 | ResultSet rs = null;
126 | StringBuilder sb = null;
127 | try
128 | {
129 | conn = net_maclife_wechat_http_BotApp.botDS.getConnection ();
130 | StringBuilder sbSQL = new StringBuilder ("SELECT * FROM " + sTablePrefix + "emoji e WHERE 1=1");
131 | if (! StringUtils.containsIgnoreCase (sQuery, "*"))
132 | {
133 | String[] arrayKeywords = sQuery.split (" +");
134 | for (String sKeyword : arrayKeywords)
135 | { // 这里先用这种方式查询,以后考虑将 tag_name 挪到单独的表中,1对多的关系,查询时只按照 “=”的匹配方式进行匹配,不会造成现在这种模糊匹配的方式带来的不想要的结果:比如,查 eat 会匹配到 meat repeat 等
136 | sbSQL.append (" AND (tag_name=? OR tag_name LIKE ? OR tag_name LIKE ? OR 英文名称=? OR 英文名称 LIKE ? OR 英文名称 LIKE ?)");
137 | }
138 | }
139 | stmt = conn.prepareStatement (sbSQL.toString ());
140 | int nCol = 1;
141 | if (! StringUtils.containsIgnoreCase (sQuery, "*"))
142 | {
143 | String[] arrayKeywords = sQuery.split (" +");
144 | for (String sKeyword : arrayKeywords)
145 | {
146 | stmt.setString (nCol++, sKeyword);
147 | stmt.setString (nCol++, "%" + sKeyword + " %");
148 | stmt.setString (nCol++, "% " + sKeyword + "%");
149 | stmt.setString (nCol++, sKeyword);
150 | stmt.setString (nCol++, "%" + sKeyword + " %");
151 | stmt.setString (nCol++, "% " + sKeyword + "%");
152 | }
153 | }
154 | rs = stmt.executeQuery ();
155 | sb = new StringBuilder ();
156 | while (rs.next ())
157 | {
158 | if (bShowDetail)
159 | {
160 | sb.append ("--------------------\n");
161 | sb.append ("字符: ");
162 | }
163 | sb.append (rs.getString ("emoji_char"));
164 | sb.append (' ');
165 | if (bShowDetail)
166 | {
167 | sb.append ("\n");
168 | sb.append ("名称: ");
169 | sb.append (rs.getString ("英文名称"));
170 | sb.append ("\n");
171 | sb.append ("关键字: ");
172 | sb.append (rs.getString ("tag_name"));
173 | sb.append ("\n");
174 | //sb.append ("Unicode: ");
175 | //sb.append (rs.getString ("tag_name"));
176 | //sb.append ("\n");
177 | //sb.append ("UTF-8: ");
178 | //sb.append (rs.getString ("tag_name"));
179 | //sb.append ("\n");
180 | }
181 | //break;
182 | }
183 | }
184 | catch (SQLException e)
185 | {
186 | e.printStackTrace();
187 | throw e;
188 | }
189 | finally
190 | {
191 | try
192 | {
193 | if (rs != null)
194 | rs.close ();
195 | if (stmt != null)
196 | stmt.close ();
197 | if (conn != null)
198 | conn.close ();
199 | }
200 | catch (SQLException e)
201 | {
202 | e.printStackTrace();
203 | }
204 | }
205 | return sb==null ? null : sb.toString ();
206 | }
207 |
208 |
209 | public static void Usage ()
210 | {
211 | System.out.println ("用法:");
212 | System.out.println ("从 full-emoji-list.html 生成 .sql 入库 SQL 脚本:");
213 | System.out.println (" java net_maclife_wechat_http_Bot_Emoji gensql <.html 文件名> [输出到 .sql 文件名]");
214 | System.out.println (" 如果不指定 [输出到 .sql 文件名],则输出到当前目录下的 emoji-data.mysql.sql 文件中 (如果文件存在的话,则直接覆盖)");
215 | System.out.println ("查询:");
216 | System.out.println (" java net_maclife_wechat_http_Bot_Emoji ");
217 | }
218 |
219 | public static String ExtractImageData (String sImageSrc)
220 | {
221 | return StringUtils.substring (StringUtils.remove (sImageSrc, "—"), "data:image/png;base64,".length ());
222 | }
223 | public static void main (String[] args) throws IOException
224 | {
225 | if (args.length < 1)
226 | {
227 | Usage ();
228 | return;
229 | }
230 | String sAction = args[0];
231 | if (StringUtils.equalsIgnoreCase (sAction, "gensql"))
232 | {
233 | if (args.length < 2)
234 | {
235 | Usage ();
236 | return;
237 | }
238 |
239 | File fHTML = new File (args[1]);
240 | File fSQL = new File (args.length >=3 ? args[2] : "emoji-data.mysql.sql");
241 | FileWriter fwSQL = new FileWriter (fSQL);
242 | //fwSQL.write ("INSERT INTO emoji (sn, code, emoji_char, 浏览器显示图, Apple显示图, Google显示图, Twitter显示图, One显示图, Facebook显示图, FBM显示图, 三星显示图, Windows显示图, GMail显示图, SB显示图, DCM显示图, KDDI显示图, 英文名称, 日期, tag_name) VALUES\n");
243 |
244 | Document docHTML = Jsoup.parse (fHTML, net_maclife_wechat_http_BotApp.utf8);
245 | Elements eEmojiTable = docHTML.select ("table");
246 | Elements eRows = eEmojiTable.select ("tr");
247 | int nEmojiRow = 0;
248 | for (Element eRow : eRows)
249 | {
250 | Elements eCols = eRow.select ("td");
251 | if (eCols.isEmpty ()) // 这行是 标题行? th?
252 | continue;
253 |
254 | if ((nEmojiRow % 100) == 0) // 每 100 行数据出一个 INSERT,免得 MySQL 报错: ERROR 2006 (HY000) at line 1: MySQL server has gone away 。原因: MySQL 数据包有大小限制,虽然可以在配置文件中用 SET max_allowed_packet=nnnM 来解决,但还是成多个 INSERT 吧
255 | {
256 | if (nEmojiRow != 0)
257 | fwSQL.write (";");
258 | fwSQL.write ("\nINSERT INTO emoji (sn, code, emoji_char, 浏览器显示图, Apple显示图, Google显示图, Twitter显示图, EmojiOne显示图, Facebook显示图, FacebookMessenger显示图, Samsung显示图, Windows显示图, GMail显示图, SoftBank显示图, DoCoMo显示图, KDDI显示图, 英文名称, 日期, tag_name) VALUES\n");
259 | }
260 |
261 | if ((nEmojiRow % 100) == 0) // 每个 INSERT 的第一条数据
262 | fwSQL.write ("\t (");
263 | else
264 | fwSQL.write ("\n\t, (");
265 |
266 | nEmojiRow ++;
267 | int i=0;
268 | String s序号 = eCols.get (i++).text ();
269 | String sCode = eCols.get (i++).text ();
270 | String sChars = eCols.get (i++).text ();
271 | String s浏览器显示图 = ExtractImageData (eCols.get (i++).select ("img").attr ("src"));
272 | String sApple显示图 = ExtractImageData (eCols.get (i++).select ("img").attr ("src"));
273 | String sGoogle显示图 = ExtractImageData (eCols.get (i++).select ("img").attr ("src"));
274 | String sTwitter显示图 = ExtractImageData (eCols.get (i++).select ("img").attr ("src"));
275 | String sEmojiOne显示图 = ExtractImageData (eCols.get (i++).select ("img").attr ("src"));
276 | String sFacebook显示图 = ExtractImageData (eCols.get (i++).select ("img").attr ("src"));
277 | String sFacebookMessenger显示图 = ExtractImageData (eCols.get (i++).select ("img").attr ("src"));
278 | String sSamsung显示图 = ExtractImageData (eCols.get (i++).select ("img").attr ("src"));
279 | String sWindows显示图 = ExtractImageData (eCols.get (i++).select ("img").attr ("src"));
280 | String sGMail显示图 = ExtractImageData (eCols.get (i++).select ("img").attr ("src"));
281 | String sSoftBank显示图 = ExtractImageData (eCols.get (i++).select ("img").attr ("src"));
282 | String sDoCoMo显示图 = ExtractImageData (eCols.get (i++).select ("img").attr ("src"));
283 | String sKDDI显示图 = ExtractImageData (eCols.get (i++).select ("img").attr ("src"));
284 | String s英文名称 = eCols.get (i++).text ();
285 | String s日期时间 = eCols.get (i++).text ();
286 | String s关键字 = eCols.get (i++).text ();
287 |
288 | fwSQL.write (s序号);
289 | fwSQL.write (", '" + sCode + "'");
290 | fwSQL.write (", '" + sChars + "'");
291 | fwSQL.write (", '" + s浏览器显示图 + "'");
292 | fwSQL.write (", '" + sApple显示图 + "'");
293 | fwSQL.write (", '" + sGoogle显示图 + "'");
294 | fwSQL.write (", '" + sTwitter显示图 + "'");
295 | fwSQL.write (", '" + sEmojiOne显示图 + "'");
296 | fwSQL.write (", '" + sFacebook显示图 + "'");
297 | fwSQL.write (", '" + sFacebookMessenger显示图 + "'");
298 | fwSQL.write (", '" + sSamsung显示图 + "'");
299 | fwSQL.write (", '" + sWindows显示图 + "'");
300 | fwSQL.write (", '" + sGMail显示图 + "'");
301 | fwSQL.write (", '" + sSoftBank显示图 + "'");
302 | fwSQL.write (", '" + sDoCoMo显示图 + "'");
303 | fwSQL.write (", '" + sKDDI显示图 + "'");
304 | fwSQL.write (", '" + s英文名称 + "'");
305 | fwSQL.write (", '" + s日期时间 + "'");
306 | fwSQL.write (", '" + s关键字 + "'");
307 | fwSQL.write (')');
308 |
309 | System.out.println (sChars);
310 | }
311 | fwSQL.write (";");
312 |
313 | fwSQL.close ();
314 | }
315 | }
316 | }
317 |
--------------------------------------------------------------------------------
/bots/net_maclife_wechat_http_Bot_GoogleImageSearch.java:
--------------------------------------------------------------------------------
1 | import java.io.*;
2 | import java.net.*;
3 | import java.nio.file.*;
4 | import java.util.*;
5 |
6 | import org.apache.commons.io.*;
7 | import org.apache.commons.lang3.StringUtils;
8 | import org.jsoup.*;
9 | import org.jsoup.nodes.*;
10 | import org.jsoup.select.*;
11 |
12 | import com.fasterxml.jackson.databind.*;
13 |
14 | public class net_maclife_wechat_http_Bot_GoogleImageSearch extends net_maclife_wechat_http_Bot
15 | {
16 | public static String GOOGLE_BASE_URL = "https://www.google.com.hk";
17 | public static String GOOGLE_IMAGE_SEARCH_URL = GOOGLE_BASE_URL + "/searchbyimage";
18 | public static boolean USE_GFW_PROXY = net_maclife_wechat_http_BotApp.ParseBoolean (net_maclife_wechat_http_BotApp.GetConfig ().getString ("google.useGFWProxy"), true);
19 | public static String GFW_PROXY_TYPE = StringUtils.upperCase (net_maclife_wechat_http_BotApp.GetConfig ().getString ("app.gfw.proxy.type"));
20 | public static String GFW_PROXY_HOST = net_maclife_wechat_http_BotApp.GetConfig ().getString ("app.gfw.proxy.host");
21 | public static int GFW_PROXY_PORT = net_maclife_wechat_http_BotApp.GetConfig ().getInt ("app.gfw.proxy.port");
22 | static Proxy gfwProxy = null;
23 | //static HttpHost gfwProxy_forApacheHttpCore = null;
24 | static
25 | {
26 | if (USE_GFW_PROXY)
27 | {
28 | gfwProxy = new Proxy (Proxy.Type.valueOf (GFW_PROXY_TYPE), new InetSocketAddress(GFW_PROXY_HOST, GFW_PROXY_PORT));
29 | //gfwProxy_forApacheHttpCore = new HttpHost (GFW_PROXY_HOST, GFW_PROXY_PORT, GFW_PROXY_TYPE);
30 | }
31 | }
32 |
33 | static final String sMultipartBoundary = "JsoupDoesNotSupportFormDataWell, and, ApacheHCDoesNotSupportSOCKSProxy";
34 |
35 | @Override
36 | public int OnImageMessageReceived
37 | (
38 | JsonNode jsonMessage,
39 | JsonNode jsonFrom, String sFromAccount, String sFromName, boolean isFromMe,
40 | JsonNode jsonTo, String sToAccount, String sToName, boolean isToMe,
41 | JsonNode jsonReplyTo, String sReplyToAccount, String sReplyToName, boolean isReplyToRoom,
42 | JsonNode jsonReplyTo_RoomMember, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember,
43 | JsonNode jsonReplyTo_Person, String sReplyToAccount_Person, String sReplyToName_Person,
44 | String sContent, File fMedia, String sImageURL
45 | )
46 | {
47 | if ((fMedia == null || ! fMedia.exists ()) && StringUtils.isEmpty (sImageURL))
48 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
49 |
50 | String sURL = GOOGLE_IMAGE_SEARCH_URL;
51 | Document doc = null;
52 | org.jsoup.Connection jsoup_conn = null;
53 | InputStream fis = null;
54 | String sResponseBody = null;
55 | try
56 | {
57 | if (StringUtils.isNotEmpty (sImageURL))
58 | { // GET 方法访问
59 | sURL = sURL + "?hl=zh-CN&image_url=" + URLEncoder.encode (sImageURL, net_maclife_wechat_http_BotApp.utf8);
60 | }
61 | else
62 | { // POST 方法访问
63 | sURL = sURL + "/upload";
64 | }
65 |
66 | /*
67 | jsoup_conn = org.jsoup.Jsoup.connect (sURL);
68 | jsoup_conn.timeout (net_maclife_util_HTTPUtils.DEFAULT_READ_TIMEOUT_SECOND * 1000);
69 | if (USE_GFW_PROXY)
70 | {
71 | jsoup_conn.proxy (gfwProxy);
72 | }
73 | jsoup_conn
74 | .followRedirects (false)
75 | //.referrer ("https://www.google.com.hk/")
76 | .userAgent ("Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/1234 Firefox is versioning emperor #2, Chrome is versioning emperor #1!!!")
77 | .header ("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")
78 | //.header ("Accept-Encoding", "gzip, deflate, br")
79 | //.header ("Cookie", "NID=91=eLR2Xt0oeN-XCDP3lQkbfBLqFU0fTxLq5ocj7lYBbkKKQBqnmvTJy-y9v3Y73nPQc_PIx59ir3T7hqmyPFAH02xSg6cCp9wqTiSTVGb0HuHqWd8U75jxpKeF47FK8DKl59mUX0WsfGjDzFvzsllfF6_HfPcBW54OATKvBBgseC-yBbJPE30YmK_z3KTWFiRRdWzWYAMTgeXERmDXBqFFpHZQLNebcQHkCTLLuBuAe5MsC2PIJs1TV8iYda_kbGFVvvvboNJTBw0eKwK9sPPt5NODU5s; SID=EwM1NpOSROI0ddQDNSMzCdQV7PF1NsutdbHv1QnVNhf2qSP3LtF-dkfUuJuBCZU5bXaMmQ.; HSID=AHHYjmOQYposTbxEx; APISID=S_Ga4t7dY6Xj_2IY/ATJD3hDdWt0OYN88-; SSID=AhuyWEzQ4qpQEbiW1; SAPISID=c91O3a08aWSgUrKP/AVd_0zQa4fzUH9adu; DV=grlNNAF72VBKxtBGytCv9RBrGlCGsQpb23j9sCB7RwAAAGqv7e54uemJFAAAAJa5wcBxIXwPCQAAAA")
80 | //.header ("Connection", "keep-alive")
81 | //.header ("Upgrade-Insecure-Requests", "1")
82 | ;
83 | if (! StringUtils.isNotEmpty (sImageURL))
84 | {
85 | fis = new FileInputStream (fMedia);
86 | //jsoup_conn.data ("image_url", "");
87 | jsoup_conn.data ("encoded_image", fMedia.getName (), fis); // 在浏览器开发工具里看,Google 图片搜索在 http 请求头里并未设置 Content-Type,而是在消息体里设置的。但是 jsoup 就设置在了请求头里,然后请求消息体里少了这部分,导致返回的数据不正确
88 |
89 | //byte[] arrayImg = IOUtils.toByteArray (fis);
90 | //jsoup_conn.data ("image_content", Base64.encodeBase64String (arrayImg));
91 | //jsoup_conn.data ("filename", fMedia.getName ());
92 |
93 | jsoup_conn.data ("hl", "zh-CN");
94 | doc = jsoup_conn.post ();
95 | fis.close ();
96 | }
97 | else
98 | {
99 | doc = jsoup_conn.get ();
100 | }
101 | System.out.println (jsoup_conn.response ().header ("Location"));
102 | System.out.println (doc);
103 | //*/
104 |
105 | //
106 | // HttpClient 不支持 SOCKS 代理
107 | // http://stackoverflow.com/questions/22937983/how-to-use-socks-5-proxy-with-apache-http-client-4 <-- 绝不这么做 I'm not doing this crap
108 | //
109 | /*
110 | MultipartEntityBuilder meb = MultipartEntityBuilder.create ();
111 | FormBodyPartBuilder fbpb = FormBodyPartBuilder.create ();
112 | //fbpb.addField (name, value)
113 | //FormBodyPart bodyPart = fbpb.build ();
114 | meb.addTextBody ("image_url", "");
115 | meb.addBinaryBody ("encoded_image", fMedia);
116 | meb.addTextBody ("image_content", "");
117 | meb.addTextBody ("filename", "");
118 | meb.addTextBody ("hl", "zh-CN");
119 |
120 | //MultipartEntity entity = new MultipartEntity();
121 | //entity.addPart("user", new StringBody("user"));
122 | //entity.addPart("password", new StringBody("12345"));
123 | //entity.addPart("encoded_image", new FileBody(fMedia));
124 |
125 | RequestConfig config = RequestConfig.custom()
126 | .setConnectTimeout (net_maclife_util_HTTPUtils.DEFAULT_CONNECT_TIMEOUT_SECOND * 1000)
127 | .setConnectionRequestTimeout (net_maclife_util_HTTPUtils.DEFAULT_READ_TIMEOUT_SECOND * 1000)
128 | //.setProxy (gfwProxy_forApacheHttpCore)
129 | .build();
130 | HttpPost post = new HttpPost (sURL);
131 | post.setConfig (config);
132 | post.setEntity (meb.build ());
133 | //System.out.println (EntityUtils.toString (entity));
134 |
135 | HttpClient httpClient = HttpClients.createDefault (); //new DefaultHttpClient();
136 | HttpResponse response = httpClient.execute (post);
137 | sResponseBody = EntityUtils.toString (response.getEntity());
138 | //*/
139 |
140 | // 自己构造 multipart/form-data 消息体
141 | //*
142 | OutputStream os = null;
143 | byte[] arrayPostData = null;
144 | Map mapRequestHeaders = new HashMap ();
145 | mapRequestHeaders.put ("Content-Type", ""); // Java HttpURLConnection 你妈的能不能彻底删除 Content-Type 消息头啊
146 | mapRequestHeaders.put ("Content-Length", ""); // Java HttpURLConnection 你妈的能不能彻底删除 Content-Length 消息头啊
147 | mapRequestHeaders.put ("User-Agent", "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/1234 Firefox is versioning emperor #2, Chrome is versioning emperor #1!!!"); // 经过多次测试,User-Agent 和/或 Accept-Language 头是必须要的,否则返回不了正确响应
148 | mapRequestHeaders.put ("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
149 | if (StringUtils.isNotEmpty (sImageURL))
150 | { // GET 方法访问
151 | if (USE_GFW_PROXY)
152 | sResponseBody = net_maclife_util_HTTPUtils.CURL_ViaProxy (sURL, mapRequestHeaders, GFW_PROXY_TYPE, GFW_PROXY_HOST, GFW_PROXY_PORT);
153 | else
154 | sResponseBody = net_maclife_util_HTTPUtils.CURL (sURL, mapRequestHeaders);
155 | }
156 | else
157 | { // POST 方法访问
158 | ByteArrayOutputStream baos = new ByteArrayOutputStream ();
159 | //net_maclife_util_HTTPUtils.FillMultipartSimplely (baos, sMultipartBoundary, "image_url", "");
160 |
161 | String sImageContentType = Files.probeContentType (fMedia.toPath ());
162 | net_maclife_util_HTTPUtils.FillMultipartSimplely (baos, sMultipartBoundary, "encoded_image", fMedia, sImageContentType);
163 |
164 | //InputStream is = new FileInputStream (fMedia);
165 | //byte[] arrayImg = IOUtils.toByteArray (is);
166 | //is.close ();
167 | //net_maclife_util_HTTPUtils.FillMultipartSimplely (baos, sMultipartBoundary, "image_content", Base64.encodeBase64String (arrayImg));
168 | //net_maclife_util_HTTPUtils.FillMultipartSimplely (baos, sMultipartBoundary, "filename", fMedia.getName ());
169 |
170 | net_maclife_util_HTTPUtils.FillMultipartSimplely (baos, sMultipartBoundary, "hl", "zh-CN");
171 | net_maclife_util_HTTPUtils.FillMultipartSimplelyEnd (baos, sMultipartBoundary);
172 | baos.flush ();
173 |
174 | ByteArrayOutputStream baos_multipart = null;
175 | //baos_multipart = new ByteArrayOutputStream ();
176 | //baos_multipart.write (("Content-Type: multipart/form-data; boundary=" + sMultipartBoundary + "\r\nContent-Length: " + baos.size () + "\r\n\r\n").getBytes ());
177 | //baos.writeTo (baos_multipart);
178 | //baos_multipart.flush ();
179 | mapRequestHeaders.put ("Content-Type", "multipart/form-data; boundary=" + sMultipartBoundary);
180 | mapRequestHeaders.put ("Content-Length", String.valueOf (baos.size ()));
181 | baos_multipart = baos;
182 |
183 | arrayPostData = baos_multipart.toByteArray ();
184 | //os = new FileOutputStream ("google-image-search-post.data");
185 | //IOUtils.write (arrayPostData, os);
186 | //os.close ();
187 |
188 | URLConnection http = null;
189 |
190 | if (USE_GFW_PROXY)
191 | {
192 | //sResponseBody = net_maclife_util_HTTPUtils.CURL_Post_ViaProxy (sURL, mapRequestHeaders, arrayPostData, GFW_PROXY_TYPE, GFW_PROXY_HOST, GFW_PROXY_PORT);
193 | http = (URLConnection) net_maclife_util_HTTPUtils.CURL
194 | ("POST", sURL, mapRequestHeaders, arrayPostData, true, true, null, false /* 不跟随重定向 */, 0, 0,
195 | GFW_PROXY_TYPE, GFW_PROXY_HOST, GFW_PROXY_PORT,
196 | true, true, null, null, null, null, null, null
197 | );
198 | }
199 | else
200 | {
201 | //sResponseBody = net_maclife_util_HTTPUtils.CURL_Post (sURL, mapRequestHeaders, arrayPostData);
202 | http = (URLConnection) net_maclife_util_HTTPUtils.CURL
203 | ("POST", sURL, mapRequestHeaders, arrayPostData, true, true, null, false /* 不跟随重定向 */, 0, 0,
204 | null, null, 0,
205 | true, true, null, null, null, null, null, null
206 | );
207 | }
208 | if (http != null)
209 | {
210 | int iResponseCode = ((HttpURLConnection)http).getResponseCode();
211 | String sStatusLine = http.getHeaderField(0); // HTTP/1.1 200 OK、HTTP/1.1 404 Not Found
212 |
213 | int iMainResponseCode = iResponseCode/100;
214 | if (iMainResponseCode == 3)
215 | {
216 | // 之前测试几天都返回不了正确的结果,是因为默认设置了“跟随重定向”,
217 | // 可是,openjdk 在自动重定向到该网址时,却丢掉了前面设置的 Accept-Language User-Agent 请求头信息,导致 Google 返回不了预期的结果。
218 | // 所以,在这里,要截获重定向的网址,加上请求头后再次访问
219 | String sRedirectedURL = http.getHeaderField ("Location");
220 |
221 | String sSetCookie = http.getHeaderField ("Set-Cookie");
222 |
223 | mapRequestHeaders.remove ("Content-Type");
224 | mapRequestHeaders.remove ("Content-Length");
225 | if (StringUtils.isNotEmpty (sSetCookie))
226 | {
227 | mapRequestHeaders.put ("Cookie", sSetCookie);
228 | }
229 |
230 | if (USE_GFW_PROXY)
231 | sResponseBody = net_maclife_util_HTTPUtils.CURL_ViaProxy (sRedirectedURL, mapRequestHeaders, GFW_PROXY_TYPE, GFW_PROXY_HOST, GFW_PROXY_PORT);
232 | else
233 | sResponseBody = net_maclife_util_HTTPUtils.CURL (sRedirectedURL, mapRequestHeaders);
234 | }
235 | else if (iMainResponseCode == 2)
236 | {
237 | net_maclife_wechat_http_BotApp.logger.warning ("就目前的 Google 图片搜索来说,应该不会直接出现 2XX");
238 | }
239 | else
240 | {
241 | net_maclife_wechat_http_BotApp.logger.severe ("http 响应代码是 " + iResponseCode + ", url = " + sURL);
242 | }
243 | }
244 | }
245 |
246 | //os = new FileOutputStream("google-image-search-result.html");
247 | //IOUtils.write (sResponseBody, os, net_maclife_wechat_http_BotApp.utf8);
248 | //os.close ();
249 |
250 | doc = Jsoup.parse (sResponseBody, GOOGLE_BASE_URL);
251 | //*/
252 |
253 | Elements eTopStuff = doc.select ("#topstuff");
254 | if (eTopStuff.isEmpty ())
255 | {
256 | net_maclife_wechat_http_BotApp.logger.info (GetName() + " 找不到 #topstuff,也许,搜索出错了? " + eTopStuff.text ());
257 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
258 | }
259 | Element e图片猜测词语链接 = eTopStuff.select ("a._gUb").first ();
260 | if (e图片猜测词语链接 == null)
261 | {
262 | net_maclife_wechat_http_BotApp.logger.info (GetName() + " 找不到 ._gUb,也许,没有结果? " + eTopStuff.text ());
263 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
264 | }
265 |
266 | StringBuilder sbInfo = new StringBuilder ();
267 | sbInfo.append (e图片猜测词语链接.text ());
268 | sbInfo.append ("\n");
269 | sbInfo.append (e图片猜测词语链接.absUrl ("href"));
270 | Elements e图片来源 = doc.select ("div.normal-header");
271 | if (! e图片来源.isEmpty ())
272 | {
273 | sbInfo.append ("\n\n" + e图片来源.select (".rg-header").first ().text ()); // "包含匹配图片的页面"
274 | Elements e图片来源标题链接 = e图片来源.select ("h3.r > a");
275 | for (int i=0; i
18 | * 在微信群中,其他人用 /addme 命令让本 Bot (自己的微信) 向命令发起者发起一个“请求加好友”的请求
19 | * 如果收到别人发来的“请求加好友”的请求,则,根据“接头暗号”来决定是否自动通过该请求
20 | *
21 | * @author liuyan
22 | *
23 | */
24 | public class net_maclife_wechat_http_Bot_MakeFriend extends net_maclife_wechat_http_Bot
25 | {
26 | @Override
27 | public int OnTextMessageReceived
28 | (
29 | JsonNode jsonMessage,
30 | JsonNode jsonFrom, String sFromAccount, String sFromName, boolean isFromMe,
31 | JsonNode jsonTo, String sToAccount, String sToName, boolean isToMe,
32 | JsonNode jsonReplyTo, String sReplyToAccount, String sReplyToName, boolean isReplyToRoom,
33 | JsonNode jsonReplyTo_RoomMember, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember,
34 | JsonNode jsonReplyTo_Person, String sReplyToAccount_Person, String sReplyToName_Person,
35 | String sContent, boolean isContentMentionedMe, boolean isContentMentionedMeFirst
36 | )
37 | {
38 | List listCommands = net_maclife_wechat_http_BotApp.GetConfig ().getList (String.class, "bot.make-friend.commands");
39 | if (listCommands==null || listCommands.isEmpty ()) // 如果未配置命令,则不处理
40 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
41 |
42 | if (! net_maclife_wechat_http_BotApp.hasCommandPrefix (sContent))
43 | {
44 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
45 | }
46 | sContent = net_maclife_wechat_http_BotApp.StripOutCommandPrefix (sContent);
47 |
48 | try
49 | {
50 | String[] arrayMessages = sContent.split ("\\s+", 2);
51 | if (arrayMessages==null || arrayMessages.length<1)
52 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
53 |
54 | String sCommandInputed = arrayMessages[0];
55 | String sCommandParametersInputed = null;
56 | if (arrayMessages.length >= 2)
57 | sCommandParametersInputed = arrayMessages[1];
58 |
59 | if (StringUtils.isEmpty (sCommandParametersInputed))
60 | {
61 | sCommandParametersInputed = "你于 " + new java.sql.Timestamp (System.currentTimeMillis ()) + " 在【" + sReplyToName + "】群内请求加好友";
62 | }
63 |
64 | String[] arrayCommandOptions = sCommandInputed.split ("\\.+", 2);
65 | sCommandInputed = arrayCommandOptions[0];
66 | String sCommandOptionsInputed = null;
67 | if (arrayCommandOptions.length >= 2)
68 | sCommandOptionsInputed = arrayCommandOptions[1];
69 |
70 | for (int i=0; i\n\n可选的语言代码选项的格式:\n - .原文语言代码\n - .2译文语言代码\n - .原文语言代码2译文语言代码\n\n具体能用哪些语言代码,请参照: http://api.fanyi.baidu.com/api/trans/product/apidoc#languageList 给出的语言代码列表");
93 | // return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
94 | //}
95 |
96 | // 解析“命令选项”:
97 | if (StringUtils.isNotEmpty (sCommandOptionsInputed))
98 | {
99 | }
100 |
101 | // 命令行命令格式没问题,再检查是否已经是好友了
102 | boolean bAlreadyBeFriend = engine.SearchForSingleContact (sReplyToAccount_RoomMember) != null;
103 | if (bAlreadyBeFriend)
104 | {
105 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "已经是好友,不需再次加好友");
106 | return
107 | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__PROCESSED
108 | | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
109 | }
110 |
111 | // 现在向命令使用者发起“加好友请求”消息
112 | //int nGender = net_maclife_wechat_http_BotApp.GetJSONInt (jsonReplyTo_Person, "Sex");
113 | JsonNode jsonResult = engine.SendRequestToMakeFriend (sReplyToAccount_RoomMember, sCommandParametersInputed);
114 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "已向你【" + sReplyToName_RoomMember + "】发起“加好友请求”,携带的验证消息为:\n\n" + sCommandParametersInputed + "\n\n如果需要手工指定验证消息,请在命令后输入验证消息即可,如:\naddme " + sReplyToName_RoomMember + " 是坠棒的");
115 | break;
116 | }
117 | }
118 | catch (Exception e)
119 | {
120 | e.printStackTrace ();
121 | }
122 |
123 | return
124 | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__PROCESSED
125 | | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
126 | }
127 |
128 |
129 | @Override
130 | public int OnRequestToMakeFriendMessageReceived
131 | (
132 | JsonNode jsonMessage,
133 | JsonNode jsonFrom, String sFromAccount, String sFromName, boolean isFromMe,
134 | JsonNode jsonTo, String sToAccount, String sToName, boolean isToMe,
135 | JsonNode jsonReplyTo, String sReplyToAccount, String sReplyToName, boolean isReplyToRoom,
136 | JsonNode jsonReplyTo_RoomMember, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember,
137 | JsonNode jsonReplyTo_Person, String sReplyToAccount_Person, String sReplyToName_Person,
138 | String sContent, JsonNode jsonRecommenedInfo, Element xmlMsg
139 | )
140 | {
141 | List listAutoAccepts = net_maclife_wechat_http_BotApp.GetConfig ().getList (String.class, "bot.make-friend.auto-accepts");
142 | if (listAutoAccepts==null || listAutoAccepts.isEmpty ()) // 如果未配置暗号,则不处理
143 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
144 |
145 | String s微信ID = net_maclife_wechat_http_BotApp.GetJSONText (jsonRecommenedInfo, "UserName");
146 | //String s昵称 = net_maclife_wechat_http_BotApp.GetJSONText (jsonRecommenedInfo, "NickName");
147 | //String s微信号 = net_maclife_wechat_http_BotApp.GetJSONText (jsonRecommenedInfo, "Alias");
148 | //int n性别 = net_maclife_wechat_http_BotApp.GetJSONInt (jsonRecommenedInfo, "Sex");
149 | String s个性签名 = net_maclife_wechat_http_BotApp.GetJSONText (jsonRecommenedInfo, "Signature");
150 | //String s省 = net_maclife_wechat_http_BotApp.GetJSONText (jsonRecommenedInfo, "Province");
151 | //String s市 = net_maclife_wechat_http_BotApp.GetJSONText (jsonRecommenedInfo, "City");
152 | String s附加内容 = net_maclife_wechat_http_BotApp.GetJSONText (jsonRecommenedInfo, "Content");
153 | int nScene = net_maclife_wechat_http_BotApp.GetJSONInt (jsonRecommenedInfo, "Scene"); // 根据什么来请求加好友的?
154 | String sMakeFriendTicket = net_maclife_wechat_http_BotApp.GetJSONText (jsonRecommenedInfo, "Ticket");
155 | int nOpCode = net_maclife_wechat_http_BotApp.GetJSONInt (jsonRecommenedInfo, "OpCode"); // 固定为 2 ?
156 |
157 | try
158 | {
159 | for (int i=0; i 1)
167 | {
168 | sAutoReplyMessage = arrayAutoAccept [1];
169 | sAutoReplyMessage = StringEscapeUtils.unescapeJava (sAutoReplyMessage); // 允许用 \n 的方式发送多行文本消息
170 | }
171 |
172 | if (StringUtils.startsWithIgnoreCase (sKeyword, "*") && StringUtils.endsWithIgnoreCase (sKeyword, "*"))
173 | {
174 | sKeyword = StringUtils.substring (sKeyword, 1, sKeyword.length () - 1);
175 | bMatched = StringUtils.containsIgnoreCase (s附加内容, sKeyword);
176 | }
177 | else if (StringUtils.startsWithIgnoreCase (sKeyword, "*"))
178 | {
179 | sKeyword = StringUtils.substring (sKeyword, 1);
180 | bMatched = StringUtils.endsWithIgnoreCase (s附加内容, sKeyword);
181 | }
182 | else if (StringUtils.endsWithIgnoreCase (sKeyword, "*"))
183 | {
184 | sKeyword = StringUtils.substring (sKeyword, 0, sKeyword.length () - 1);
185 | bMatched = StringUtils.startsWithIgnoreCase (s附加内容, sKeyword);
186 | }
187 | else
188 | {
189 | bMatched = StringUtils.equalsIgnoreCase (s附加内容, sKeyword);
190 | }
191 |
192 | if (bMatched)
193 | {
194 | JsonNode jsonResult = engine.AcceptRequestToMakeFriend (sMakeFriendTicket, nScene, /*sReplyToAccount_RoomMember*/s微信ID, "暗号已对上,自动通过");
195 | if (StringUtils.isNotEmpty (sAutoReplyMessage))
196 | {
197 | SendTextMessage (sReplyToAccount_Person, sReplyToName_Person, sAutoReplyMessage);
198 | }
199 | break;
200 | }
201 | }
202 | }
203 | catch (Exception e)
204 | {
205 | e.printStackTrace();
206 | }
207 |
208 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/bots/net_maclife_wechat_http_Bot_Manager.java:
--------------------------------------------------------------------------------
1 | import java.io.*;
2 | import java.util.*;
3 | import java.util.logging.*;
4 |
5 | import org.apache.commons.lang3.*;
6 |
7 | import com.fasterxml.jackson.databind.*;
8 |
9 | /**
10 | * 远程管理 机器人。
11 | * 该机器人基本上就是:将控制台里的命令,用 Bot 再重新实现一遍,以达到:“不在电脑跟前时,用微信文字消息对 Bot 进行部分(嗯,只能是一部分)远程管理”的目的。
12 | * @author liuyan
13 | *
14 | */
15 | public class net_maclife_wechat_http_Bot_Manager extends net_maclife_wechat_http_Bot
16 | {
17 | @Override
18 | public int OnTextMessageReceived
19 | (
20 | JsonNode jsonMessage,
21 | JsonNode jsonFrom, String sFromAccount, String sFromName, boolean isFromMe,
22 | JsonNode jsonTo, String sToAccount, String sToName, boolean isToMe,
23 | JsonNode jsonReplyTo, String sReplyToAccount, String sReplyToName, boolean isReplyToRoom,
24 | JsonNode jsonReplyTo_RoomMember, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember,
25 | JsonNode jsonReplyTo_Person, String sReplyToAccount_Person, String sReplyToName_Person,
26 | String sContent, boolean isContentMentionedMe, boolean isContentMentionedMeFirst
27 | )
28 | {
29 | try
30 | {
31 | if (! net_maclife_wechat_http_BotApp.hasCommandPrefix (sContent))
32 | {
33 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
34 | }
35 | sContent = net_maclife_wechat_http_BotApp.StripOutCommandPrefix (sContent);
36 |
37 | if (StringUtils.equalsIgnoreCase (sContent, net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.manager.command.list-bots")))
38 | {
39 | StringBuilder sb = new StringBuilder ();
40 | for (int i=0; i= 2)
59 | sCommandParametersInputed = arrayMessages[1];
60 |
61 | String[] arrayCommandOptions = sCommandInputed.split ("\\" + net_maclife_wechat_http_BotApp.COMMAND_OPTION_SEPARATOR + "+", 2);
62 | sCommandInputed = arrayCommandOptions[0];
63 | String sCommandOptionsInputed = null;
64 | if (arrayCommandOptions.length >= 2)
65 | sCommandOptionsInputed = arrayCommandOptions[1];
66 |
67 | if (StringUtils.equalsIgnoreCase (sCommandInputed, net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.manager.command.load-bot")))
68 | {
69 | LoadBot (sCommandParametersInputed, sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember);
70 | }
71 | else if (StringUtils.equalsIgnoreCase (sCommandInputed, net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.manager.command.unload-bot")))
72 | {
73 | boolean bForced = StringUtils.equalsIgnoreCase (sCommandOptionsInputed, "force");
74 | if (StringUtils.equals (sCommandParametersInputed, getClass ().getCanonicalName ()) && !bForced)
75 | {
76 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "要卸载的机器人是本机器人,默认不允许这么操作,除非用 .force 选项强制执行");
77 | return 0;
78 | }
79 | UnloadBot (sCommandParametersInputed, sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember);
80 | }
81 | else if (StringUtils.equalsIgnoreCase (sCommandInputed, net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.manager.command.log-level")))
82 | {
83 | if (StringUtils.isEmpty (sCommandParametersInputed))
84 | {
85 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "当前日志级别: " + net_maclife_wechat_http_BotApp.logger.getLevel ());
86 | }
87 | else
88 | {
89 | try
90 | {
91 | String sNewLogLevel = StringUtils.upperCase (sCommandParametersInputed);
92 | net_maclife_wechat_http_BotApp.logger.setLevel (Level.parse (sNewLogLevel));
93 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "日志级别已改为: " + net_maclife_wechat_http_BotApp.logger.getLevel ());
94 | }
95 | catch (IllegalArgumentException e)
96 | {
97 | e.printStackTrace ();
98 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "非法日志级别: " + sCommandParametersInputed + ", 请换有效的日志级别名称,比如 all finest finer fine info warning severe 1000 0 1 ...");
99 | }
100 | }
101 | }
102 | else if (StringUtils.equalsAnyIgnoreCase (sCommandInputed,
103 | net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.manager.command.invite"),
104 | net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.manager.command.kick"),
105 | net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.manager.command.topic")
106 | )
107 | )
108 | {
109 | if (! isReplyToRoom)
110 | {
111 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "邀请或者踢人或改群名操作需要在群内执行");
112 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
113 | }
114 | UpdateChatRoom (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sCommandInputed, sCommandOptionsInputed, sCommandParametersInputed);
115 | }
116 | else if (StringUtils.equalsAnyIgnoreCase (sCommandInputed, net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.manager.command.new-room"))
117 | )
118 | {
119 | CreateNewRoom (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sCommandInputed, sCommandOptionsInputed, sCommandParametersInputed);
120 | }
121 | }
122 | }
123 | catch (Exception e)
124 | {
125 | e.printStackTrace ();
126 | }
127 |
128 | return
129 | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__PROCESSED
130 | | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
131 | }
132 |
133 | public void LoadBot (String sBotClassName, String sReplyToAccount, String sReplyToName, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember)
134 | {
135 | try
136 | {
137 | if (StringUtils.isEmpty (sBotClassName))
138 | {
139 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "加载机器人需要指定机器人 java 类的完整类名");
140 | return;
141 | }
142 | Class> botClass = Class.forName (sBotClassName);
143 | Object obj = botClass.newInstance ();
144 | if (obj instanceof net_maclife_wechat_http_Bot)
145 | {
146 | net_maclife_wechat_http_Bot newBot = (net_maclife_wechat_http_Bot) obj;
147 | boolean bAlreadyLoaded = false;
148 | // 检查有没有该类的实例存在,有的话,则不再重复添加
149 | for (int i=0; i <朋友帐号/微信号/备注名/昵称>");
269 | return;
270 | }
271 |
272 | List listFriends = net_maclife_wechat_http_BotApp.SplitCommandLine (sCommandParametersInputed); // 鉴于用户昵称可能包含空格或特殊字符的情况,这里必须用 SplitCommandLine 函数处理,不能简单的 .split (" ")
273 | StringBuilder sbFriendsAccounts = new StringBuilder ();
274 | for (int i=0; i listParams = net_maclife_wechat_http_BotApp.SplitCommandLine (sCommandParametersInputed); // 鉴于用户昵称可能包含空格或特殊字符的情况,这里必须用 SplitCommandLine 函数处理,不能简单的 .split (" ")
330 | //if (StringUtils.isEmpty (sCommandParametersInputed))
331 | if (listParams.size () < 3)
332 | {
333 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "必须输入新群名,以及,至少输入两个联系人帐号/微信号/备注名/昵称。如果群名、昵称有空格等特殊字符,请用引号引起来。\n\n" + sCommandInputed + "[" + net_maclife_wechat_http_BotApp.COMMAND_OPTION_SEPARATOR + "帐号|" + net_maclife_wechat_http_BotApp.COMMAND_OPTION_SEPARATOR + "微信号|" + net_maclife_wechat_http_BotApp.COMMAND_OPTION_SEPARATOR + "备注名|" + net_maclife_wechat_http_BotApp.COMMAND_OPTION_SEPARATOR + "昵称] <新群名> <朋友帐号/微信号/备注名/昵称> <朋友帐号/微信号/备注名/昵称>...");
334 | return;
335 | }
336 | String sNewRoomName = listParams.remove (0);
337 |
338 | String sSearchBy = sCommandOptionsInputed;
339 | String sNameOfSearchBy = "";
340 |
341 | if (StringUtils.equalsAnyIgnoreCase (sSearchBy, "Account", "帐号"))
342 | sNameOfSearchBy = "帐号";
343 | else if (StringUtils.equalsAnyIgnoreCase (sSearchBy, "Alias", "微信号"))
344 | sNameOfSearchBy = "微信号";
345 | else if (StringUtils.equalsAnyIgnoreCase (sSearchBy, "RemarkName", "备注名"))
346 | sNameOfSearchBy = "备注名";
347 | else if (StringUtils.equalsAnyIgnoreCase (sSearchBy, "NickName", "昵称") || StringUtils.isEmpty (sSearchBy))
348 | sNameOfSearchBy = "昵称";
349 | else
350 | {
351 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "不知道你要根据什么找人… sSearchByName = " + sNameOfSearchBy);
352 | return;
353 | }
354 |
355 | //listParams.add (0, engine.sMyEncryptedAccountInThisSession);
356 | for (int i=0; i listReplies = net_maclife_wechat_http_BotApp.GetConfig ().getList (String.class, "bot.missile-launched.replies");
30 | if (listReplies!=null && listReplies.size ()>0)
31 | {
32 | int iRandom = net_maclife_wechat_http_BotApp.random.nextInt (listReplies.size ());
33 | sReply = listReplies.get (iRandom);
34 | }
35 |
36 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "经度: " + sLongtitude + "\n纬度: " + sLatitude + "\n位置: " + sLocation + (StringUtils.isEmpty (sReply) ? "" : "\n" + sReply));
37 | return
38 | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__PROCESSED
39 | | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
40 | }
41 | catch (Exception e)
42 | {
43 | e.printStackTrace ();
44 | }
45 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/bots/net_maclife_wechat_http_Bot_Relay.java:
--------------------------------------------------------------------------------
1 | import java.io.*;
2 | import java.net.*;
3 | import java.util.*;
4 |
5 | import org.apache.commons.io.*;
6 | import org.apache.commons.lang3.*;
7 |
8 | import com.fasterxml.jackson.core.*;
9 | import com.fasterxml.jackson.databind.*;
10 |
11 | /**
12 | * 消息转发/消息中继机器人。
13 | * 该机器人将从 Socket 接收 JSON 格式的数据,根据数据中指定的接收人、消息类型、消息内容发送到微信好友。
14 | *
15 | *
16 | * JSON 消息格式
17 | *
18 | {
19 | From: "", // 来自,可以是任意内容,但通常指定应用程序名称及版本。该字段可省略(等同于值为空的 From)
20 | To: // 消息接收人,接收人可以是单个接收人、也可以多个(数组)。通过指定 Account、Alias、RemarkName、NickName 几个名称中的一个或多个的方式来定位该接收人。
21 | // 为了避免重名的情况,建议是用 Account 或 Alias 来定位接收人。 对于 Account,建议使用在后台日志中找到联系人的【明文 ID / 明文 Account】 -- 唯一、不变、相比加密帐号也短些且容易分辨些。
22 | // 如果是单个接收人,只需要采取下面数组内的格式即可,如:{Account: "weixin"}
23 | // 如果是多个接收人,只需要采取下面数组格式即可,如:[{Account: "weixin"}, {Alias: "一个微信号"}, {RemarkName: "一个备注名"}, {NickName: "一个昵称"}]
24 | [
25 | { // 这几个字段,至少需要指定其中的一个。
26 | Account: "明文帐号,或加密帐号(前体是你得知道每次登录后该会话的加密帐号,通常是要手工在后台日志中获取),或通配符帐号"。
27 | // 明文帐号: 是在手机端打开一个联系人聊天窗口,然后在网页端获取到的明文,诸如: wxid_XXXX (比较常见的个人帐号)、 gh_XXX (公众号帐号)、 NNNNNN@chatroom (群聊帐号)、还有一些用户自定义的帐号(早期注册的?)
28 | // 通配符帐号: 通配符帐号的目的是为了群发用。目前支持的通配符帐号有:
29 | // *
- 通讯录里的所有联系人;含群聊天室、公众号(含类似“微信团队”之类的);
30 | // *p
- 通讯录除去群聊天室、公众号后的联系人 (人/Person,但含类似“文件传输助手”这类的非“人”的帐号 -- 暂时没法区分);
31 | // *w
或 *g
- 所有联系人 (女人/Woman/Girl);
32 | // *m
或 *b
- 所有联系人 (男人/Man/Boy);
33 | // *r
- 通讯录里的所有聊天室联系人 (Room);
34 | // *gh
- 通讯录里的所有公众号联系人;(gh 是公众号的明文 ID/明文 Account 的帐号前缀,g公 h号?)
35 | Alias: "别名",
36 | RemarkName: "备注名",
37 | NickName: "昵称", // 昵称可能会被好友自己改动,所以,不建议用这个
38 | //DisplayName: "聊天室/群内 人在此聊天室/群的“我在本群的昵称”",
39 | }
40 | , ...
41 | ],
42 | UseAppendBotNameConfig: true | false, // true | false,是否使用“在消息中附加 Bot 名”配置项。若没有该 json 属性,则默认为 true -- 兼容以前的配置
43 | AppendBotName: true | false, // true | false,当 UseAppendBotNameConfig 为 false 时,则使用该参数明确指定是否要“在消息中附加 Bot 名”。若没有该 json 属性,则默认为 false -- 兼容以前的配置
44 | MessageType: "消息类型", // 取值 "text" | "voice" | "video"。 该字段也可以忽略,若为空或者忽略,则默认为 "text"
45 | Message: "消息" // 对于 text 文本消息,直接传文字。 对于 voice 和 video,需要用 base64 编码后传递
46 | }
47 | *
48 | * @author liuyan
49 | *
50 | */
51 | public class net_maclife_wechat_http_Bot_Relay extends net_maclife_wechat_http_Bot implements Runnable
52 | {
53 | ServerSocket ss = null;
54 |
55 | @Override
56 | public void Start ()
57 | {
58 | try
59 | {
60 | String sListenAddress = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.relay.listen.address");
61 | int nListenPort = net_maclife_wechat_http_BotApp.GetConfig ().getInt ("bot.relay.listen.port");
62 |
63 | ss = new ServerSocket ();
64 | ss.bind (new InetSocketAddress (InetAddress.getByName (sListenAddress), nListenPort));
65 | }
66 | catch (IOException e)
67 | {
68 | net_maclife_wechat_http_BotApp.logger.severe (GetName () + " 启动失败: " + e);
69 | e.printStackTrace();
70 | return;
71 | }
72 |
73 | if (botTask == null)
74 | {
75 | botTask = net_maclife_wechat_http_BotApp.executor.submit (this);
76 | }
77 | }
78 |
79 | @Override
80 | public void Stop ()
81 | {
82 | try
83 | {
84 | super.Stop ();
85 | if (ss != null && !ss.isClosed ())
86 | {
87 | ss.close ();
88 | }
89 | }
90 | catch (IOException e)
91 | {
92 | e.printStackTrace();
93 | }
94 | }
95 |
96 | @Override
97 | public void run ()
98 | {
99 | Socket s = null;
100 | while (!Thread.interrupted ())
101 | {
102 | try
103 | {
104 | s = ss.accept ();
105 | InputStream is = s.getInputStream ();
106 | PrintWriter out = new PrintWriter (s.getOutputStream (), true);
107 | String sInput = IOUtils.toString (is, net_maclife_wechat_http_BotApp.utf8);
108 | net_maclife_wechat_http_BotApp.logger.fine (GetName() + " 收到数据:\n" + net_maclife_util_ANSIEscapeTool.Green (sInput));
109 | JsonNode jsonNode = net_maclife_wechat_http_BotApp.jacksonObjectMapper_Loose.readTree (sInput);
110 | ProcessMessage (jsonNode, out);
111 | }
112 | catch (IOException e)
113 | {
114 | e.printStackTrace();
115 | }
116 | finally
117 | {
118 | if (s != null)
119 | {
120 | try
121 | {
122 | s.close ();
123 | }
124 | catch (IOException e)
125 | {
126 | e.printStackTrace();
127 | }
128 | }
129 | }
130 | }
131 | }
132 |
133 | void ProcessMessage (JsonNode jsonMessageToRelay, PrintWriter out)
134 | {
135 | try
136 | {
137 | String sFrom = net_maclife_wechat_http_BotApp.GetJSONText (jsonMessageToRelay, "From");
138 | String sMessageType = net_maclife_wechat_http_BotApp.GetJSONText (jsonMessageToRelay, "MessageType");
139 | String sMessageType_LowerCase = StringUtils.lowerCase (sMessageType);
140 | String sMessage = net_maclife_wechat_http_BotApp.GetJSONText (jsonMessageToRelay, "Message");
141 | boolean bUseAppendBotNameConfig = net_maclife_wechat_http_BotApp.GetJSONBoolean (jsonMessageToRelay, "UseAppendBotNameConfig", true);
142 | boolean bAppendBotName = net_maclife_wechat_http_BotApp.GetJSONBoolean (jsonMessageToRelay, "AppendBotName", false);
143 | if (StringUtils.isEmpty (sMessage))
144 | {
145 | out.println ("必须指定消息内容");
146 | return;
147 | }
148 | if (StringUtils.isNotEmpty (sFrom))
149 | {
150 | sMessage = sMessage + "\n-- 消息来源: " + sFrom;
151 | }
152 |
153 | JsonNode jsonTOs = jsonMessageToRelay.get ("To");
154 | List listTOs = new ArrayList ();
155 | if (jsonTOs.isArray ())
156 | {
157 | for (int i=0; i listTOs)
201 | {
202 | String sAccount = net_maclife_wechat_http_BotApp.GetJSONText (jsonTo, "Account");
203 | String sAlias = net_maclife_wechat_http_BotApp.GetJSONText (jsonTo, "Alias");
204 | String sRemarkName = net_maclife_wechat_http_BotApp.GetJSONText (jsonTo, "RemarkName");
205 | String sNickName = net_maclife_wechat_http_BotApp.GetJSONText (jsonTo, "NickName");
206 | //String sDisplayName = net_maclife_wechat_http_BotApp.GetJSONText (jsonTo, "DisplayName");
207 |
208 | if (StringUtils.isEmpty (sAccount))
209 | {
210 | JsonNode jsonContact = engine.SearchForSingleContact (null, sAlias, sRemarkName, sNickName);
211 | if (jsonContact != null)
212 | listTOs.add (net_maclife_wechat_http_BotApp.GetJSONText (jsonContact, "UserName"));
213 | }
214 | else
215 | {
216 | if (StringUtils.equalsAnyIgnoreCase (sAccount, "*")) // 所有联系人
217 | {
218 | if (engine.jsonContacts == null)
219 | return;
220 |
221 | JsonNode jsonContactList = engine.jsonContacts.get ("MemberList");
222 | for (JsonNode jsonContact : jsonContactList)
223 | listTOs.add (net_maclife_wechat_http_BotApp.GetJSONText (jsonContact, "UserName"));
224 | }
225 | else if (StringUtils.equalsAnyIgnoreCase (sAccount, "*p")) // 所有“人”(排除群聊、公众号),但其实,也可能包含类似 filehelper 之类的
226 | {
227 | if (engine.jsonContacts == null)
228 | return;
229 |
230 | JsonNode jsonContactList = engine.jsonContacts.get ("MemberList");
231 | for (JsonNode jsonContact : jsonContactList)
232 | {
233 | String sEncryptedAccount = net_maclife_wechat_http_BotApp.GetJSONText (jsonContact, "UserName");
234 | int nVerifyFlag = net_maclife_wechat_http_BotApp.GetJSONInt (jsonContact, "VerifyFlag");
235 | boolean isPublicAccount = net_maclife_wechat_http_BotApp.IsPublicAccount (nVerifyFlag);
236 | boolean isRoomAccount = net_maclife_wechat_http_BotApp.IsRoomAccount (sEncryptedAccount);
237 | if (isPublicAccount || isRoomAccount)
238 | continue;
239 |
240 | listTOs.add (sEncryptedAccount);
241 | }
242 | }
243 | else if (StringUtils.equalsAnyIgnoreCase (sAccount, "*m") || StringUtils.equalsAnyIgnoreCase (sAccount, "*b") // 所有“男人/Man/Boy”(排除群聊、公众号)
244 | || StringUtils.equalsAnyIgnoreCase (sAccount, "*w") || StringUtils.equalsAnyIgnoreCase (sAccount, "*g")) // 所有“女人/Woman/Girl”(排除群聊、公众号)
245 | {
246 | if (engine.jsonContacts == null)
247 | return;
248 |
249 | JsonNode jsonContactList = engine.jsonContacts.get ("MemberList");
250 | for (JsonNode jsonContact : jsonContactList)
251 | {
252 | String sEncryptedAccount = net_maclife_wechat_http_BotApp.GetJSONText (jsonContact, "UserName");
253 | int nVerifyFlag = net_maclife_wechat_http_BotApp.GetJSONInt (jsonContact, "VerifyFlag");
254 | boolean isPublicAccount = net_maclife_wechat_http_BotApp.IsPublicAccount (nVerifyFlag);
255 | boolean isRoomAccount = net_maclife_wechat_http_BotApp.IsRoomAccount (sEncryptedAccount);
256 | if (isPublicAccount || isRoomAccount)
257 | continue;
258 |
259 | int nGender = net_maclife_wechat_http_BotApp.GetJSONInt (jsonContact, "Sex");
260 | if (nGender == 1 && (StringUtils.equalsAnyIgnoreCase (sAccount, "*m") || StringUtils.equalsAnyIgnoreCase (sAccount, "*b"))
261 | ||
262 | nGender == 2 && (StringUtils.equalsAnyIgnoreCase (sAccount, "*w") || StringUtils.equalsAnyIgnoreCase (sAccount, "*g"))
263 | )
264 | listTOs.add (sEncryptedAccount);
265 | }
266 | }
267 | else if (StringUtils.equalsAnyIgnoreCase (sAccount, "*r")) // 所有群聊
268 | {
269 | if (engine.jsonContacts == null)
270 | return;
271 |
272 | JsonNode jsonContactList = engine.jsonContacts.get ("MemberList");
273 | for (JsonNode jsonContact : jsonContactList)
274 | {
275 | String sEncryptedAccount = net_maclife_wechat_http_BotApp.GetJSONText (jsonContact, "UserName");
276 | boolean isRoomAccount = net_maclife_wechat_http_BotApp.IsRoomAccount (sEncryptedAccount);
277 | if (isRoomAccount)
278 | listTOs.add (sEncryptedAccount);
279 | }
280 | }
281 | else if (StringUtils.equalsAnyIgnoreCase (sAccount, "*gh")) // 所有公众号,也不知道有没有人会专门发到所有公众号里…
282 | {
283 | if (engine.jsonContacts == null)
284 | return;
285 |
286 | JsonNode jsonContactList = engine.jsonContacts.get ("MemberList");
287 | for (JsonNode jsonContact : jsonContactList)
288 | {
289 | String sEncryptedAccount = net_maclife_wechat_http_BotApp.GetJSONText (jsonContact, "UserName");
290 | int nVerifyFlag = net_maclife_wechat_http_BotApp.GetJSONInt (jsonContact, "VerifyFlag");
291 | boolean isPublicAccount = net_maclife_wechat_http_BotApp.IsPublicAccount (nVerifyFlag);
292 | if (isPublicAccount)
293 | listTOs.add (sEncryptedAccount);
294 | }
295 | }
296 | else
297 | { // 非特殊形式的帐号字符串,则直接当成帐号
298 | listTOs.add (sAccount);
299 | }
300 | }
301 | }
302 | }
303 |
--------------------------------------------------------------------------------
/bots/net_maclife_wechat_http_Bot_Repeater.java:
--------------------------------------------------------------------------------
1 | import java.io.*;
2 | import java.security.*;
3 | import java.security.cert.*;
4 |
5 | import org.apache.commons.lang3.*;
6 |
7 | import com.fasterxml.jackson.core.*;
8 | import com.fasterxml.jackson.databind.*;
9 |
10 | /**
11 | * 复读机机器人
12 | * @author liuyan
13 | *
14 | */
15 | public class net_maclife_wechat_http_Bot_Repeater extends net_maclife_wechat_http_Bot
16 | {
17 | @Override
18 | public int OnTextMessageReceived
19 | (
20 | JsonNode jsonMessage,
21 | JsonNode jsonFrom, String sFromAccount, String sFromName, boolean isFromMe,
22 | JsonNode jsonTo, String sToAccount, String sToName, boolean isToMe,
23 | JsonNode jsonReplyTo, String sReplyToAccount, String sReplyToName, boolean isReplyToRoom,
24 | JsonNode jsonReplyTo_RoomMember, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember,
25 | JsonNode jsonReplyTo_Person, String sReplyToAccount_Person, String sReplyToName_Person,
26 | String sContent, boolean isContentMentionedMe, boolean isContentMentionedMeFirst
27 | )
28 | {
29 | boolean bRepeatMyOwnMessage = net_maclife_wechat_http_BotApp.ParseBoolean (net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.repeater.repeat-my-own-message", "no"), false);
30 | if (!bRepeatMyOwnMessage && isFromMe)
31 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
32 |
33 | try
34 | {
35 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sContent);
36 | }
37 | catch (Exception e)
38 | {
39 | e.printStackTrace ();
40 | }
41 |
42 | return
43 | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__PROCESSED
44 | | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/bots/net_maclife_wechat_http_Bot_SaveMessagePackageToDatabase.java:
--------------------------------------------------------------------------------
1 | import java.sql.*;
2 |
3 | import org.apache.commons.lang3.*;
4 |
5 | import com.fasterxml.jackson.databind.*;
6 |
7 | /**
8 | *
9 | * - 记录【韬乐园快餐店】润迅订餐群里的员工点餐记录(记录所点的每一份快餐的每一个具体餐品)
10 | *
11 | *
12 | *
13 | *
14 | *
15 | * @depends
16 | * @author liuyan
17 | *
18 | */
19 | public class net_maclife_wechat_http_Bot_SaveMessagePackageToDatabase extends net_maclife_wechat_http_Bot
20 | {
21 | @Override
22 | public int OnMessagePackageReceived (JsonNode jsonMessagePackage)
23 | {
24 | 保存消息包 (jsonMessagePackage);
25 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
26 | }
27 |
28 | void 保存消息包 (JsonNode jsonMessagePackage)
29 | {
30 | Connection conn = null;
31 | PreparedStatement stmt_保存消息包 = null;
32 | try
33 | {
34 | int nAddMsgCount = net_maclife_wechat_http_BotApp.GetJSONInt (jsonMessagePackage, "AddMsgCount", 0);
35 | int nModContactCount = net_maclife_wechat_http_BotApp.GetJSONInt (jsonMessagePackage, "ModContactCount", 0);
36 | int nDelContactCount = net_maclife_wechat_http_BotApp.GetJSONInt (jsonMessagePackage, "DelContactCount", 0);
37 | int nModChatRoomMemerCount = net_maclife_wechat_http_BotApp.GetJSONInt (jsonMessagePackage, "ModChatRoomMemberCount", 0);
38 |
39 | net_maclife_wechat_http_BotApp.SetupDataSource ();
40 | conn = net_maclife_wechat_http_BotApp.botDS.getConnection ();
41 | stmt_保存消息包 = conn.prepareStatement ("INSERT INTO wechat_message_packages (package_json, AddMsgCount, ModContactCount, DelContactCount, ModChatRoomMemberCount) VALUES (?,?,?,?,?)", new String[] {"package_id"});
42 | int nCol = 1;
43 | stmt_保存消息包.setString (nCol++, jsonMessagePackage.toString ());
44 | stmt_保存消息包.setInt (nCol++, nAddMsgCount);
45 | stmt_保存消息包.setInt (nCol++, nModContactCount);
46 | stmt_保存消息包.setInt (nCol++, nDelContactCount);
47 | stmt_保存消息包.setInt (nCol++, nModChatRoomMemerCount);
48 | stmt_保存消息包.executeUpdate ();
49 | stmt_保存消息包.close ();
50 |
51 | conn.close ();
52 | }
53 | catch (Exception e)
54 | {
55 | e.printStackTrace ();
56 | }
57 | finally
58 | {
59 | try
60 | {
61 | if (stmt_保存消息包 != null)
62 | stmt_保存消息包.close ();
63 | }
64 | catch (Exception e)
65 | {
66 | e.printStackTrace ();
67 | }
68 | try
69 | {
70 | if (conn != null)
71 | conn.close ();
72 | }
73 | catch (Exception e)
74 | {
75 | e.printStackTrace ();
76 | }
77 | }
78 | }
79 |
80 |
81 | public static void main (String[] args)
82 | {
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/bots/net_maclife_wechat_http_Bot_SayHi.java:
--------------------------------------------------------------------------------
1 | import java.util.*;
2 |
3 | import org.apache.commons.lang3.*;
4 |
5 | import com.fasterxml.jackson.databind.*;
6 |
7 | public class net_maclife_wechat_http_Bot_SayHi extends net_maclife_wechat_http_Bot
8 | {
9 | String sHiMessage = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.hi.message.started");
10 | String sByeMessage = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.hi.message.stopped");
11 |
12 | List listTargetAliases = null;
13 | List listTargetRemarkNames = null;
14 | List listTargetNickNames = null;
15 |
16 | public net_maclife_wechat_http_Bot_SayHi ()
17 | {
18 | listTargetAliases = net_maclife_wechat_http_BotApp.GetConfig ().getList (String.class, "bot.hi.message.target.aliases");
19 | listTargetRemarkNames = net_maclife_wechat_http_BotApp.GetConfig ().getList (String.class, "bot.hi.message.target.remark-names");
20 | listTargetNickNames = net_maclife_wechat_http_BotApp.GetConfig ().getList (String.class, "bot.hi.message.target.nick-names");
21 | }
22 |
23 | int ProcessMessage (String sMessage)
24 | {
25 | boolean bProcessed = false;
26 | JsonNode jsonContact = null;
27 | try
28 | {
29 | String sTemp;
30 | List list = null;
31 |
32 | list = listTargetAliases;
33 | for (int i=0; i mapProcessesRunning = new HashMap ();
26 |
27 | @Override
28 | public void Start ()
29 | {
30 | if (botTask == null)
31 | {
32 | botTask = net_maclife_wechat_http_BotApp.executor.submit (this);
33 | }
34 | }
35 |
36 | @Override
37 | public void Stop ()
38 | {
39 | StopAllProcesses ();
40 | super.Stop ();
41 | }
42 |
43 | @Override
44 | public int OnTextMessageReceived
45 | (
46 | JsonNode jsonMessage,
47 | JsonNode jsonFrom, String sFromAccount, String sFromName, boolean isFromMe,
48 | JsonNode jsonTo, String sToAccount, String sToName, boolean isToMe,
49 | JsonNode jsonReplyTo, String sReplyToAccount, String sReplyToName, boolean isReplyToRoom,
50 | JsonNode jsonReplyTo_RoomMember, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember,
51 | JsonNode jsonReplyTo_Person, String sReplyToAccount_Person, String sReplyToName_Person,
52 | String sContent, boolean isContentMentionedMe, boolean isContentMentionedMeFirst
53 | )
54 | {
55 | List listCommands = net_maclife_wechat_http_BotApp.GetConfig ().getList (String.class, "bot.command-line.commands");
56 | if (listCommands==null || listCommands.isEmpty ()) // 如果未配置命令,则不处理
57 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
58 |
59 | if (! net_maclife_wechat_http_BotApp.hasCommandPrefix (sContent))
60 | {
61 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
62 | }
63 | sContent = net_maclife_wechat_http_BotApp.StripOutCommandPrefix (sContent);
64 |
65 | try
66 | {
67 | String[] arrayMessages = sContent.split ("\\s+", 2);
68 | if (arrayMessages==null || arrayMessages.length<1)
69 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
70 |
71 | String sCommandInputed = arrayMessages[0];
72 | String sCommandParametersInputed = null;
73 | if (arrayMessages.length >= 2)
74 | sCommandParametersInputed = arrayMessages[1];
75 |
76 | String[] arrayCommandOptions = sCommandInputed.split ("\\" + net_maclife_wechat_http_BotApp.COMMAND_OPTION_SEPARATOR + "+", 2);
77 | sCommandInputed = arrayCommandOptions[0];
78 | String sCommandOptionsInputed = null;
79 | if (arrayCommandOptions.length >= 2)
80 | sCommandOptionsInputed = arrayCommandOptions[1];
81 |
82 | for (int i=0; i [及其参数]...\n\n.行数:用来指定最多返回该命令的输出内容的行数。如 .3 最多返回前 3 行。\n.stderr: stderr 与 stdout 合并。\n.t=超时秒数: 设置进程的超时时长\n.ll 或 .lbl、.rt、.rtr、.zss、.ss、.准实时、.实时 是打开准实时输出的选项,当命令执行时间过长时,可以利用该选项“准实时”获得输出 -- 一行一个输出,这可能会比较扰人。");
98 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
99 | }
100 |
101 | // 解析命令“选项”: .stderr .行数 .t=超时时长(秒) .rt
102 | boolean bRedirectStdErr = false;
103 | boolean bResponseLineByLine = false;
104 | int nMaxLinesReturns = DEFAULT_MAX_LINES_RETURNS;
105 | int nTimeout = net_maclife_wechat_http_BotApp.GetConfig ().getInt ("bot.command-line.process.timeout.default", DEFAULT_WATCH_DOG_TIMEOUT);
106 | if (StringUtils.isNotEmpty (sCommandOptionsInputed))
107 | {
108 | arrayCommandOptions = sCommandOptionsInputed.split ("\\.+");
109 | for (String sCommandOption : arrayCommandOptions)
110 | {
111 | if (StringUtils.equalsIgnoreCase (sCommandOption, "stderr"))
112 | {
113 | bRedirectStdErr = true;
114 | }
115 | else if (StringUtils.startsWithIgnoreCase (sCommandOption, "t="))
116 | { // 如果用 t= 指定超时时长,则用指定的数值代替默认值
117 | sCommandOption = StringUtils.substring (sCommandOption, 2);
118 | try
119 | {
120 | nTimeout = Integer.parseInt (sCommandOption);
121 | if (nTimeout < 1)
122 | {
123 | nTimeout = 1; // 至少 1 秒…
124 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, GetName() + " 机器人的命令超时时长最少为 1 秒(不允许设置不超时),已自动调整为 1 秒,命令将继续执行。");
125 | }
126 | }
127 | catch (NumberFormatException e)
128 | {
129 | e.printStackTrace ();
130 | }
131 | }
132 | else if (
133 | StringUtils.equalsIgnoreCase (sCommandOption, "ll") // Line by Line
134 | || StringUtils.equalsIgnoreCase (sCommandOption, "lbl") // Line By Line
135 | || StringUtils.equalsIgnoreCase (sCommandOption, "rt") // RealTime
136 | || StringUtils.equalsIgnoreCase (sCommandOption, "rtr") // RealTime Response
137 | || StringUtils.equalsIgnoreCase (sCommandOption, "zss") // z准s实s时
138 | || StringUtils.equalsIgnoreCase (sCommandOption, "ss") // s实s时
139 | || StringUtils.equalsIgnoreCase (sCommandOption, "准实时") //
140 | || StringUtils.equalsIgnoreCase (sCommandOption, "实时") //
141 | )
142 | {
143 | bResponseLineByLine = true;
144 | }
145 | else
146 | {
147 | try
148 | {
149 | nMaxLinesReturns = Integer.parseInt (sCommandOption);
150 | if (nMaxLinesReturns < 0)
151 | nMaxLinesReturns = 0; // 0: 不限制输出行数
152 | }
153 | catch (NumberFormatException e)
154 | {
155 | e.printStackTrace ();
156 | }
157 | }
158 | }
159 | }
160 |
161 | //
162 | String sShell = net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.command-line.shell");
163 | if (StringUtils.isEmpty (sShell))
164 | {
165 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, GetName() + " 尚未配置 Shell,无法执行。");
166 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
167 | }
168 | try
169 | {
170 | String sCommandOutput = ProcessShellCommand (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sShell, sCommandParametersInputed, bRedirectStdErr, bResponseLineByLine, nMaxLinesReturns, nTimeout);
171 | if (StringUtils.isNotEmpty (sCommandOutput))
172 | {
173 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sCommandOutput);
174 | }
175 | }
176 | catch (Throwable e)
177 | {
178 | e.printStackTrace ();
179 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, e.toString ());
180 | }
181 | }
182 | }
183 | }
184 | catch (Exception e)
185 | {
186 | e.printStackTrace ();
187 | }
188 |
189 | return
190 | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__PROCESSED
191 | | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
192 | }
193 |
194 | /**
195 | * 执行 Shell 命令。
196 | * 这里的实现方法与我的 IRC 机器人 TideBot 中的实现方式不同:这里不解析命令行,直接交给一个 Shell 处理
197 | * @param sShell
198 | * @param sCommandLineString
199 | * @param bRedirectStdErr 是否将 stderr 重定向到 stdout
200 | * @param bResponseLineByLine 是否每行都单独响应。这个选项,相当于“是否准实时输出”,对于执行类似 ping 命令这样不断定时输出行的命令,好处:可以使用这个选项让人直观的感到是否正在输出。 弊端:因为每行都产生一个微信消息,会影响到群里其他人手机 -- 不断提醒。
201 | * @param nMaxLinesReturns 最多返回多少行
202 | * @param nTimeout 进程运行超时时长。如果超过这个时长进程还未结束,则杀死该进程。
203 | */
204 | String ProcessShellCommand (String sReplyToAccount, String sReplyToName, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember, String sShell, String sCommandLineString, boolean bRedirectStdErr, boolean bResponseLineByLine, int nMaxLinesReturns, int nTimeout) throws KeyManagementException, UnrecoverableKeyException, JsonProcessingException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, InterruptedException
205 | {
206 | //List listCommandLineParameters = new ArrayList<>
207 | ProcessBuilder pb = new ProcessBuilder (sShell, "-c", sCommandLineString);
208 | // pb.inheritIO (); // 这个不能要,因为不是打印在后台,而是要取出结果,并返回
209 | if (bRedirectStdErr)
210 | pb.redirectErrorStream ();
211 |
212 | Process p = null;
213 | int nLines = 0;
214 | int nTotalLines = 0;
215 | String sLine = null;
216 | StringBuilder sb = bResponseLineByLine ? null : new StringBuilder ();
217 | try
218 | {
219 | p = pb.start ();
220 | mapProcessesRunning.put (p, System.currentTimeMillis () + nTimeout * 1000);
221 | int rc = p.waitFor ();
222 | mapProcessesRunning.remove (p);
223 | assert (rc == 0);
224 | if (rc != 0)
225 | {
226 | net_maclife_wechat_http_BotApp.logger.severe ("'" + sCommandLineString + "' 执行失败(返回代码不是 0,而是 " + rc + ")。");
227 | }
228 |
229 | InputStream in = p.getInputStream ();
230 | InputStream err = p.getErrorStream ();
231 | //IOUtils.toString (in);
232 | BufferedReader br = null;
233 | br = new BufferedReader (new InputStreamReader (in));
234 | while ((sLine = br.readLine ()) != null)
235 | {
236 | //System.out.println (sLine);
237 | nTotalLines ++;
238 | if (nMaxLinesReturns != 0 && nLines >= nMaxLinesReturns)
239 | continue;
240 | //if (nMaxLinesReturns == 0 || (nMaxLinesReturns != 0 && nLines < nMaxLinesReturns))
241 | if (bResponseLineByLine)
242 | {
243 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sLine);
244 | }
245 | else
246 | {
247 | sb.append (sLine);
248 | sb.append ('\n');
249 | }
250 | nLines ++;
251 | }
252 | //br.close ();
253 | if (! bRedirectStdErr)
254 | {
255 | br = new BufferedReader (new InputStreamReader (err));
256 | while ((sLine = br.readLine ()) != null)
257 | {
258 | System.err.println (sLine);
259 | // nTotalLines ++;
260 | // if (nMaxLinesReturns != 0 && nLines >= nMaxLinesReturns)
261 | // continue;
262 | // //if (nMaxLinesReturns == 0 || (nMaxLinesReturns != 0 && nLines < nMaxLinesReturns))
263 | // if (bResponseLineByLine)
264 | // {
265 | // SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sLine);
266 | // }
267 | // else
268 | // {
269 | // sb.append (sLine);
270 | // sb.append ('\n');
271 | // }
272 | // nLines ++;
273 | }
274 | //br.close ();
275 | }
276 |
277 | if (nMaxLinesReturns != 0 && nLines < nTotalLines)
278 | {
279 | sb.append ('\n');
280 | sb.append ("(总共 ");
281 | sb.append (nTotalLines);
282 | sb.append (" 行,只显示前 ");
283 | sb.append (nLines);
284 | sb.append (" 行)");
285 | }
286 |
287 | //err.close ();
288 | //in.close ();
289 |
290 | }
291 | catch (IOException e)
292 | { // 进程有可能超时被杀死,也应该返回被杀死前的内容
293 | e.printStackTrace();
294 | }
295 |
296 | return sb==null ? null : sb.toString ();
297 | }
298 |
299 | public void StopAllProcesses ()
300 | {
301 | for (Process p : mapProcessesRunning.keySet ())
302 | {
303 | p.destroy ();
304 | }
305 | mapProcessesRunning.clear ();
306 | }
307 |
308 | /**
309 | * 监视线程:杀死超时的进程。类似 apache commaons exec 里的 WatchDog。
310 | * 目前,监视的频率为:每隔 1 秒检查一次。
311 | */
312 | @Override
313 | public void run ()
314 | {
315 | while (! Thread.currentThread ().interrupted ())
316 | {
317 | try
318 | {
319 | TimeUnit.SECONDS.sleep (1);
320 | for (Process p : mapProcessesRunning.keySet ())
321 | {
322 | long lExpectedStopTime = mapProcessesRunning.get (p);
323 | if (System.currentTimeMillis () > lExpectedStopTime)
324 | {
325 | p.destroy ();
326 | mapProcessesRunning.remove (p);
327 | }
328 | }
329 | }
330 | catch (InterruptedException e)
331 | {
332 | e.printStackTrace ();
333 | break;
334 | }
335 | catch (Throwable e)
336 | {
337 | e.printStackTrace ();
338 | }
339 | }
340 | }
341 |
342 | public static void main (String[] args)
343 | {
344 | net_maclife_wechat_http_Bot_ShellCommand cmd = new net_maclife_wechat_http_Bot_ShellCommand ();
345 | cmd.Start ();
346 |
347 | final int 运行秒数 = 7;
348 | final int 延时退出秒数 = 7; // 得比运行秒数数值大
349 | final String sShell = "bash";
350 | String 命令行 = null;
351 | 命令行 = "ping 127.0.0.1";
352 | //命令行 = "htop";
353 | //命令行 = "nmap -T4 127.0.0.1";
354 | boolean b准实时输出 = false;
355 | try
356 | {
357 | System.err.println ("运行 " + 运行秒数 + " 秒钟的 " + 命令行);
358 | String sResult = cmd.ProcessShellCommand (null, null, null, null, sShell, 命令行, true, b准实时输出, 0, 运行秒数);
359 | System.out.println (命令行 + " 输出:\n" + sResult);
360 | //System.err.println ("等待 " + 延时退出秒数 + " 秒钟退出");
361 | // TimeUnit.SECONDS.sleep (延时退出秒数);
362 | }
363 | catch (Exception e)
364 | {
365 | e.printStackTrace();
366 | }
367 |
368 | cmd.Stop ();
369 | net_maclife_wechat_http_BotApp.executor.shutdownNow ();
370 | }
371 | }
372 |
--------------------------------------------------------------------------------
/bots/net_maclife_wechat_http_Bot_SimpleAddressBook.java:
--------------------------------------------------------------------------------
1 | import java.sql.*;
2 | import java.util.*;
3 |
4 | import org.apache.commons.lang3.*;
5 |
6 | import com.fasterxml.jackson.databind.*;
7 |
8 | /**
9 | * 简易微信群通讯录。
10 | * 简易通讯录的数据存放在数据库的单张表中。简易通讯录提供简单的通讯录的 查、修改功能。
11 | *
12 | * 查:
13 | * 通讯录 姓名 -- 查指定姓名的通讯录
14 | * 通讯录 -- 查自己的通讯录、同时提供帮助信息?
15 | * 修改:
16 | *
17 | * 增加: 暂时不提供增加的功能,考虑的原因是:
18 | * @author liuyan
19 | *
20 | */
21 | public class net_maclife_wechat_http_Bot_SimpleAddressBook extends net_maclife_wechat_http_Bot
22 | {
23 |
24 | @Override
25 | public int OnTextMessageReceived
26 | (
27 | JsonNode jsonMessage,
28 | JsonNode jsonFrom, String sFromAccount, String sFromName, boolean isFromMe,
29 | JsonNode jsonTo, String sToAccount, String sToName, boolean isToMe,
30 | JsonNode jsonReplyTo, String sReplyToAccount, String sReplyToName, boolean isReplyToRoom,
31 | JsonNode jsonReplyTo_RoomMember, String sReplyToAccount_RoomMember, String sReplyToName_RoomMember,
32 | JsonNode jsonReplyTo_Person, String sReplyToAccount_Person, String sReplyToName_Person,
33 | String sContent, boolean isContentMentionedMe, boolean isContentMentionedMeFirst
34 | )
35 | {
36 | List listCommands = net_maclife_wechat_http_BotApp.GetConfig ().getList (String.class, "bot.simple-address-book.commands");
37 | if (listCommands==null || listCommands.isEmpty ()) // 如果未配置命令,则不处理
38 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
39 |
40 | if (! net_maclife_wechat_http_BotApp.hasCommandPrefix (sContent))
41 | {
42 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
43 | }
44 | sContent = net_maclife_wechat_http_BotApp.StripOutCommandPrefix (sContent);
45 |
46 | try
47 | {
48 | String[] arrayMessages = sContent.split ("\\s+", 2);
49 | if (arrayMessages==null || arrayMessages.length<1)
50 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
51 |
52 | String sCommandInputed = arrayMessages[0];
53 | String sCommandParametersInputed = null;
54 | if (arrayMessages.length >= 2)
55 | sCommandParametersInputed = arrayMessages[1];
56 |
57 | // 命令行命令格式没问题,现在开始查询数据库
58 | for (int i=0; i");
71 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
72 | }
73 |
74 | try
75 | {
76 | String sResult = Query (sReplyToName, sCommandParametersInputed);
77 | if (StringUtils.isEmpty (sResult))
78 | {
79 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "在通讯簿【" + sReplyToName + "】(与群名相同)中没找到姓名为【" + sCommandParametersInputed + "】的联系信息");
80 | return net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
81 | }
82 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, sResult);
83 | break;
84 | }
85 | catch (Exception e)
86 | {
87 | SendTextMessage (sReplyToAccount, sReplyToName, sReplyToAccount_RoomMember, sReplyToName_RoomMember, "查询出错: " + e);
88 | }
89 | }
90 | }
91 | }
92 | catch (Exception e)
93 | {
94 | e.printStackTrace ();
95 | }
96 |
97 | return
98 | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__PROCESSED
99 | | net_maclife_wechat_http_BotEngine.BOT_CHAIN_PROCESS_MODE_MASK__CONTINUE;
100 | }
101 |
102 | String Query (String sFromName, String sQuery) throws SQLException
103 | {
104 | String sTablePrefix = StringUtils.trimToEmpty (net_maclife_wechat_http_BotApp.GetConfig ().getString ("bot.simple-address-book.jdbc.database-table.prefix"));
105 | net_maclife_wechat_http_BotApp.SetupDataSource ();
106 | Connection conn = null;
107 | PreparedStatement stmt = null;
108 | ResultSet rs = null;
109 | StringBuilder sb = null;
110 | try
111 | {
112 | conn = net_maclife_wechat_http_BotApp.botDS.getConnection ();
113 | stmt = conn.prepareStatement ("SELECT * FROM " + sTablePrefix + "wechat_simple_address_book WHERE 微信群昵称=? AND 群联系人姓名=?");
114 | int nCol = 1;
115 | stmt.setString (nCol++, sFromName);
116 | stmt.setString (nCol++, sQuery);
117 | rs = stmt.executeQuery ();
118 | sb = new StringBuilder ();
119 | while (rs.next ())
120 | {
121 | sb.append (rs.getString ("群联系人姓名"));
122 | sb.append ("\n");
123 | sb.append ("--------------------\n");
124 | sb.append ("电话: ");
125 | sb.append (rs.getString ("电话号码"));
126 | sb.append ("\n");
127 | sb.append ("单位: ");
128 | sb.append (rs.getString ("工作单位"));
129 | sb.append ("\n");
130 | sb.append ("住址: ");
131 | sb.append (rs.getString ("住址"));
132 | sb.append ("\n");
133 | sb.append ("昵称: ");
134 | sb.append (rs.getString ("群联系人昵称"));
135 | sb.append ("\n");
136 | break;
137 | }
138 | }
139 | catch (SQLException e)
140 | {
141 | e.printStackTrace();
142 | throw e;
143 | }
144 | finally
145 | {
146 | try
147 | {
148 | if (rs != null)
149 | rs.close ();
150 | if (stmt != null)
151 | stmt.close ();
152 | if (conn != null)
153 | conn.close ();
154 | }
155 | catch (SQLException e)
156 | {
157 | e.printStackTrace();
158 | }
159 | }
160 | return sb==null ? null : sb.toString ();
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/bots/net_maclife_wechat_http_Bot_糗事百科热门.java:
--------------------------------------------------------------------------------
1 | import java.io.*;
2 | import java.util.*;
3 |
4 | import org.apache.commons.lang3.*;
5 | import org.jsoup.*;
6 | import org.jsoup.nodes.*;
7 | import org.jsoup.select.*;
8 |
9 | /**
10 | * 获取糗事百科热门信息。目前,不打算用 Bot 形式来处理糗事百科,而仅仅当做 Relay Bot 的一个外部消息源。
11 | * @author liuyan
12 | * @deprecated 除了个别网站(比如需要先获取一个网页、取得 Cookie 或者某数值,再用该数值访问第二个网页),其他所有抓取网页的 Bot,都建议改为 net_maclife_wechat_http_Bot_WebSoup 的模板来处理。
13 | *
14 | */
15 | @Deprecated
16 | public class net_maclife_wechat_http_Bot_糗事百科热门 extends net_maclife_wechat_http_Bot
17 | {
18 | /**
19 | * Map 中的 key 名称:
20 | * - mode 模式,取值:
21 | * '=' 等于(不区分大小写);
22 | * 'regexp' 规则表达式
23 | * - expression : 表达式; 当 mode 为 '=' 时,这里就应该给出具体发帖人姓名; 当 mode 为 'regexp' 时,这里应该给出规则表达式
24 | * - reason : 加入黑名单的原因
25 | */
26 | static List