102ee140d29e0d20fa5f350c8852b79479a6a948
[reactos.git] / irc / TechBot / TechBot.Library / Commands / ApiCommand.cs
1 using System;
2 using System.IO;
3 using System.Data;
4 using System.Text.RegularExpressions;
5 using HtmlHelp;
6 using HtmlHelp.ChmDecoding;
7
8 namespace TechBot.Library
9 {
10 [Command("api", Help = "!api <apiname>")]
11 public class ApiCommand : Command
12 {
13 private const bool IsVerbose = false;
14
15 private HtmlHelpSystem chm;
16 private string chmPath;
17 private string mainChm;
18
19 public ApiCommand()
20 {
21 Run();
22 }
23
24 private void WriteIfVerbose(MessageContext context,
25 string message)
26 {
27 if (IsVerbose)
28 TechBot.ServiceOutput.WriteLine(context,
29 message);
30 }
31
32 private void Run()
33 {
34 string CHMFilename = Path.Combine(chmPath, mainChm);
35 chm = new HtmlHelpSystem();
36 chm.OpenFile(CHMFilename, null);
37
38 Console.WriteLine(String.Format("Loaded main CHM: {0}",
39 Path.GetFileName(CHMFilename)));
40 foreach (string filename in Directory.GetFiles(chmPath))
41 {
42 if (!Path.GetExtension(filename).ToLower().Equals(".chm"))
43 continue;
44 if (Path.GetFileName(filename).ToLower().Equals(mainChm))
45 continue;
46
47 Console.WriteLine(String.Format("Loading CHM: {0}",
48 Path.GetFileName(filename)));
49 try
50 {
51 chm.MergeFile(filename);
52 }
53 catch (Exception ex)
54 {
55 Console.WriteLine(String.Format("Could not load CHM: {0}. Exception {1}",
56 Path.GetFileName(filename),
57 ex));
58 }
59 }
60 Console.WriteLine(String.Format("Loaded {0} CHMs",
61 chm.FileList.Length));
62 }
63
64 public override void Handle(MessageContext context)
65 {
66 if (parameters.Trim().Equals(String.Empty))
67 DisplayNoKeyword(context);
68 else
69 Search(context,
70 parameters);
71 }
72
73 private bool SearchIndex(MessageContext context,
74 string keyword)
75 {
76 if (chm.HasIndex)
77 {
78 IndexItem item = chm.Index.SearchIndex(keyword,
79 IndexType.KeywordLinks);
80 if (item != null && item.Topics.Count > 0)
81 {
82 WriteIfVerbose(context,
83 String.Format("Keyword {0} found in index",
84 item.KeyWord));
85 IndexTopic indexTopic = item.Topics[0] as IndexTopic;
86 return DisplayResult(context,
87 keyword,
88 indexTopic);
89 }
90 else
91 {
92 WriteIfVerbose(context,
93 String.Format("Keyword {0} not found in index",
94 keyword));
95 return false;
96 }
97 }
98 else
99 return false;
100 }
101
102 private void SearchFullText(MessageContext context,
103 string keyword)
104 {
105 string sort = "Rating ASC";
106 WriteIfVerbose(context,
107 String.Format("Searching fulltext database for {0}",
108 keyword));
109
110 bool partialMatches = false;
111 bool titlesOnly = true;
112 int maxResults = 100;
113 DataTable results = chm.PerformSearch(keyword,
114 maxResults,
115 partialMatches,
116 titlesOnly);
117 WriteIfVerbose(context,
118 String.Format("results.Rows.Count = {0}",
119 results != null ?
120 results.Rows.Count.ToString() : "(none)"));
121 if (results != null && results.Rows.Count > 0)
122 {
123 results.DefaultView.Sort = sort;
124 if (!DisplayResult(context,
125 keyword,
126 results))
127 {
128 DisplayNoResult(context,
129 keyword);
130 }
131 }
132 else
133 {
134 DisplayNoResult(context,
135 keyword);
136 }
137 }
138
139 private void Search(MessageContext context,
140 string keyword)
141 {
142 if (!SearchIndex(context,
143 keyword))
144 SearchFullText(context,
145 keyword);
146 }
147
148 private bool DisplayResult(MessageContext context,
149 string keyword,
150 IndexTopic indexTopic)
151 {
152 keyword = keyword.Trim().ToLower();
153 string url = indexTopic.URL;
154 WriteIfVerbose(context,
155 String.Format("URL from index search {0}",
156 url));
157 string prototype = ExtractPrototype(context,
158 url);
159 if (prototype == null || prototype.Trim().Equals(String.Empty))
160 return false;
161 string formattedPrototype = FormatPrototype(prototype);
162 TechBot.ServiceOutput.WriteLine(context,
163 formattedPrototype);
164 return true;
165 }
166
167 private bool DisplayResult(MessageContext context,
168 string keyword,
169 DataTable results)
170 {
171 keyword = keyword.Trim().ToLower();
172 for (int i = 0; i < results.DefaultView.Count; i++)
173 {
174 DataRowView row = results.DefaultView[i];
175 string title = row["Title"].ToString();
176 WriteIfVerbose(context,
177 String.Format("Examining {0}", title));
178 if (title.Trim().ToLower().Equals(keyword))
179 {
180 string location = row["Location"].ToString();
181 string rating = row["Rating"].ToString();
182 string url = row["Url"].ToString();
183 string prototype = ExtractPrototype(context,
184 url);
185 if (prototype == null || prototype.Trim().Equals(String.Empty))
186 continue;
187 string formattedPrototype = FormatPrototype(prototype);
188 TechBot.ServiceOutput.WriteLine(context,
189 formattedPrototype);
190 return true;
191 }
192 }
193 return false;
194 }
195
196 private void DisplayNoResult(MessageContext context,
197 string keyword)
198 {
199 TechBot.ServiceOutput.WriteLine(context,
200 String.Format("I don't know about keyword {0}",
201 keyword));
202 }
203
204 private void DisplayNoKeyword(MessageContext context)
205 {
206 TechBot.ServiceOutput.WriteLine(context,
207 "Please give me a keyword.");
208 }
209
210 private string ReplaceComments(string s)
211 {
212 return Regex.Replace(s, "//(.+)\r\n", "");
213 }
214
215 private string ReplaceLineEndings(string s)
216 {
217 return Regex.Replace(s, "(\r\n)+", " ");
218 }
219
220 private string ReplaceSpaces(string s)
221 {
222 return Regex.Replace(s, @" +", " ");
223 }
224
225 private string ReplaceSpacesBeforeLeftParenthesis(string s)
226 {
227 return Regex.Replace(s, @"\( ", @"(");
228 }
229
230 private string ReplaceSpacesBeforeRightParenthesis(string s)
231 {
232 return Regex.Replace(s, @" \)", @")");
233 }
234
235 private string ReplaceSemicolon(string s)
236 {
237 return Regex.Replace(s, @";", @"");
238 }
239
240 private string FormatPrototype(string prototype)
241 {
242 string s = ReplaceComments(prototype);
243 s = ReplaceLineEndings(s);
244 s = ReplaceSpaces(s);
245 s = ReplaceSpacesBeforeLeftParenthesis(s);
246 s = ReplaceSpacesBeforeRightParenthesis(s);
247 s = ReplaceSemicolon(s);
248 return s;
249 }
250
251 private string ExtractPrototype(MessageContext context,
252 string url)
253 {
254 string page = GetPage(context,
255 url);
256 Match match = Regex.Match(page,
257 "<PRE class=\"?syntax\"?>(.+)</PRE>",
258 RegexOptions.Multiline |
259 RegexOptions.Singleline);
260 if (match.Groups.Count > 1)
261 {
262 string prototype = match.Groups[1].ToString();
263 return StripHtml(StripAfterSlashPre(prototype));
264 }
265
266 return "";
267 }
268
269 private string StripAfterSlashPre(string html)
270 {
271 int index = html.IndexOf("</PRE>");
272 if (index != -1)
273 {
274 return html.Substring(0, index);
275 }
276 else
277 return html;
278 }
279
280 private string StripHtml(string html)
281 {
282 return Regex.Replace(html, @"<(.|\n)*?>", String.Empty);
283 }
284
285 private string GetPage(MessageContext context,
286 string url)
287 {
288 string CHMFileName = "";
289 string topicName = "";
290 string anchor = "";
291 CHMStream.CHMStream baseStream;
292 if (!chm.BaseStream.GetCHMParts(url, ref CHMFileName, ref topicName, ref anchor))
293 {
294 baseStream = chm.BaseStream;
295 CHMFileName = baseStream.CHMFileName;
296 topicName = url;
297 anchor = "";
298 }
299 else
300 {
301 baseStream = GetBaseStreamFromCHMFileName(context,
302 CHMFileName);
303 }
304
305 if ((topicName == "") || (CHMFileName == "") || (baseStream == null))
306 {
307 return "";
308 }
309
310 return baseStream.ExtractTextFile(topicName);
311 }
312
313 private CHMStream.CHMStream GetBaseStreamFromCHMFileName(MessageContext context,
314 string CHMFileName)
315 {
316 foreach (CHMFile file in chm.FileList)
317 {
318 WriteIfVerbose(context,
319 String.Format("Compare: {0} <> {1}",
320 file.ChmFilePath,
321 CHMFileName));
322 if (file.ChmFilePath.ToLower().Equals(CHMFileName.ToLower()))
323 {
324 return file.BaseStream;
325 }
326 }
327 WriteIfVerbose(context,
328 String.Format("Could not find loaded CHM file in list: {0}",
329 CHMFileName));
330 return null;
331 }
332 }
333 }