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