4 using System.Text.RegularExpressions;
6 using HtmlHelp.ChmDecoding;
8 namespace TechBot.Library
10 public class ApiCommand : BaseCommand, ICommand
12 private const bool IsVerbose = false;
14 private HtmlHelpSystem chm;
15 private IServiceOutput serviceOutput;
16 private string chmPath;
17 private string mainChm;
19 public ApiCommand(IServiceOutput serviceOutput,
23 this.serviceOutput = serviceOutput;
24 this.chmPath = chmPath;
25 this.mainChm = mainChm;
29 private void WriteIfVerbose(MessageContext context,
33 serviceOutput.WriteLine(context,
39 string CHMFilename = Path.Combine(chmPath, mainChm);
40 chm = new HtmlHelpSystem();
41 chm.OpenFile(CHMFilename, null);
43 Console.WriteLine(String.Format("Loaded main CHM: {0}",
44 Path.GetFileName(CHMFilename)));
45 foreach (string filename in Directory.GetFiles(chmPath))
47 if (!Path.GetExtension(filename).ToLower().Equals(".chm"))
49 if (Path.GetFileName(filename).ToLower().Equals(mainChm))
52 Console.WriteLine(String.Format("Loading CHM: {0}",
53 Path.GetFileName(filename)));
56 chm.MergeFile(filename);
60 Console.WriteLine(String.Format("Could not load CHM: {0}. Exception {1}",
61 Path.GetFileName(filename),
65 Console.WriteLine(String.Format("Loaded {0} CHMs",
66 chm.FileList.Length));
69 public bool CanHandle(string commandName)
71 return CanHandle(commandName,
72 new string[] { "api" });
75 public void Handle(MessageContext context,
79 if (parameters.Trim().Equals(String.Empty))
80 DisplayNoKeyword(context);
88 return "!api <apiname>";
91 private bool SearchIndex(MessageContext context,
96 IndexItem item = chm.Index.SearchIndex(keyword,
97 IndexType.KeywordLinks);
98 if (item != null && item.Topics.Count > 0)
100 WriteIfVerbose(context,
101 String.Format("Keyword {0} found in index",
103 IndexTopic indexTopic = item.Topics[0] as IndexTopic;
104 return DisplayResult(context,
110 WriteIfVerbose(context,
111 String.Format("Keyword {0} not found in index",
120 private void SearchFullText(MessageContext context,
123 string sort = "Rating ASC";
124 WriteIfVerbose(context,
125 String.Format("Searching fulltext database for {0}",
128 bool partialMatches = false;
129 bool titlesOnly = true;
130 int maxResults = 100;
131 DataTable results = chm.PerformSearch(keyword,
135 WriteIfVerbose(context,
136 String.Format("results.Rows.Count = {0}",
138 results.Rows.Count.ToString() : "(none)"));
139 if (results != null && results.Rows.Count > 0)
141 results.DefaultView.Sort = sort;
142 if (!DisplayResult(context,
146 DisplayNoResult(context,
152 DisplayNoResult(context,
157 private void Search(MessageContext context,
160 if (!SearchIndex(context,
162 SearchFullText(context,
166 private bool DisplayResult(MessageContext context,
168 IndexTopic indexTopic)
170 keyword = keyword.Trim().ToLower();
171 string url = indexTopic.URL;
172 WriteIfVerbose(context,
173 String.Format("URL from index search {0}",
175 string prototype = ExtractPrototype(context,
177 if (prototype == null || prototype.Trim().Equals(String.Empty))
179 string formattedPrototype = FormatPrototype(prototype);
180 serviceOutput.WriteLine(context,
185 private bool DisplayResult(MessageContext context,
189 keyword = keyword.Trim().ToLower();
190 for (int i = 0; i < results.DefaultView.Count; i++)
192 DataRowView row = results.DefaultView[i];
193 string title = row["Title"].ToString();
194 WriteIfVerbose(context,
195 String.Format("Examining {0}", title));
196 if (title.Trim().ToLower().Equals(keyword))
198 string location = row["Location"].ToString();
199 string rating = row["Rating"].ToString();
200 string url = row["Url"].ToString();
201 string prototype = ExtractPrototype(context,
203 if (prototype == null || prototype.Trim().Equals(String.Empty))
205 string formattedPrototype = FormatPrototype(prototype);
206 serviceOutput.WriteLine(context,
214 private void DisplayNoResult(MessageContext context,
217 serviceOutput.WriteLine(context,
218 String.Format("I don't know about keyword {0}",
222 private void DisplayNoKeyword(MessageContext context)
224 serviceOutput.WriteLine(context,
225 "Please give me a keyword.");
228 private string ReplaceComments(string s)
230 return Regex.Replace(s, "//(.+)\r\n", "");
233 private string ReplaceLineEndings(string s)
235 return Regex.Replace(s, "(\r\n)+", " ");
238 private string ReplaceSpaces(string s)
240 return Regex.Replace(s, @" +", " ");
243 private string ReplaceSpacesBeforeLeftParenthesis(string s)
245 return Regex.Replace(s, @"\( ", @"(");
248 private string ReplaceSpacesBeforeRightParenthesis(string s)
250 return Regex.Replace(s, @" \)", @")");
253 private string ReplaceSemicolon(string s)
255 return Regex.Replace(s, @";", @"");
258 private string FormatPrototype(string prototype)
260 string s = ReplaceComments(prototype);
261 s = ReplaceLineEndings(s);
262 s = ReplaceSpaces(s);
263 s = ReplaceSpacesBeforeLeftParenthesis(s);
264 s = ReplaceSpacesBeforeRightParenthesis(s);
265 s = ReplaceSemicolon(s);
269 private string ExtractPrototype(MessageContext context,
272 string page = GetPage(context,
274 Match match = Regex.Match(page,
275 "<PRE class=\"?syntax\"?>(.+)</PRE>",
276 RegexOptions.Multiline |
277 RegexOptions.Singleline);
278 if (match.Groups.Count > 1)
280 string prototype = match.Groups[1].ToString();
281 return StripHtml(StripAfterSlashPre(prototype));
287 private string StripAfterSlashPre(string html)
289 int index = html.IndexOf("</PRE>");
292 return html.Substring(0, index);
298 private string StripHtml(string html)
300 return Regex.Replace(html, @"<(.|\n)*?>", String.Empty);
303 private string GetPage(MessageContext context,
306 string CHMFileName = "";
307 string topicName = "";
309 CHMStream.CHMStream baseStream;
310 if (!chm.BaseStream.GetCHMParts(url, ref CHMFileName, ref topicName, ref anchor))
312 baseStream = chm.BaseStream;
313 CHMFileName = baseStream.CHMFileName;
319 baseStream = GetBaseStreamFromCHMFileName(context,
323 if ((topicName == "") || (CHMFileName == "") || (baseStream == null))
328 return baseStream.ExtractTextFile(topicName);
331 private CHMStream.CHMStream GetBaseStreamFromCHMFileName(MessageContext context,
334 foreach (CHMFile file in chm.FileList)
336 WriteIfVerbose(context,
337 String.Format("Compare: {0} <> {1}",
340 if (file.ChmFilePath.ToLower().Equals(CHMFileName.ToLower()))
342 return file.BaseStream;
345 WriteIfVerbose(context,
346 String.Format("Could not find loaded CHM file in list: {0}",