mirror of
https://github.com/reactos/reactos.git
synced 2024-11-04 05:43:30 +00:00
a46601a21c
- add support for preventing all commands to be run on the channel by default. Now TechBot only answers some of the commands in PM - add back the error Command svn path=/trunk/; revision=34079
309 lines
8.4 KiB
C#
309 lines
8.4 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Data;
|
|
using System.Text.RegularExpressions;
|
|
|
|
using HtmlHelp;
|
|
using HtmlHelp.ChmDecoding;
|
|
|
|
using TechBot.Library;
|
|
|
|
namespace TechBot.Commands.MSDN
|
|
{
|
|
[Command("api", Help = "!api <apiname>")]
|
|
public class ApiCommand : Command
|
|
{
|
|
private bool IsVerbose = false;
|
|
|
|
private HtmlHelpSystem chm;
|
|
|
|
public ApiCommand()
|
|
{
|
|
LoadCHM();
|
|
}
|
|
|
|
[CommandParameter("api", "The API name")]
|
|
public string API
|
|
{
|
|
get { return Parameters; }
|
|
set { Parameters = value; }
|
|
}
|
|
|
|
private void WriteIfVerbose(string message)
|
|
{
|
|
if (IsVerbose)
|
|
Say(message);
|
|
}
|
|
|
|
private void LoadCHM()
|
|
{
|
|
string CHMFilename = Path.Combine(Settings.Default.ChmPath, Settings.Default.MainChm);
|
|
chm = new HtmlHelpSystem();
|
|
chm.OpenFile(CHMFilename, null);
|
|
|
|
Console.WriteLine(String.Format("Loaded main CHM: {0}",
|
|
Path.GetFileName(CHMFilename)));
|
|
foreach (string filename in Directory.GetFiles(Settings.Default.ChmPath))
|
|
{
|
|
if (!Path.GetExtension(filename).ToLower().Equals(".chm"))
|
|
continue;
|
|
if (Path.GetFileName(filename).ToLower().Equals(Settings.Default.MainChm))
|
|
continue;
|
|
|
|
Console.WriteLine(String.Format("Loading CHM: {0}",
|
|
Path.GetFileName(filename)));
|
|
try
|
|
{
|
|
chm.MergeFile(filename);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine(String.Format("Could not load CHM: {0}. Exception {1}",
|
|
Path.GetFileName(filename),
|
|
ex));
|
|
}
|
|
}
|
|
Console.WriteLine(String.Format("Loaded {0} CHMs",
|
|
chm.FileList.Length));
|
|
}
|
|
|
|
public override void ExecuteCommand()
|
|
{
|
|
if (Name.Trim().Equals(String.Empty))
|
|
{
|
|
Say("Please give me a keyword.");
|
|
}
|
|
else
|
|
{
|
|
Search(Name);
|
|
}
|
|
}
|
|
|
|
private bool SearchIndex(
|
|
string keyword)
|
|
{
|
|
if (chm.HasIndex)
|
|
{
|
|
IndexItem item = chm.Index.SearchIndex(keyword,
|
|
IndexType.KeywordLinks);
|
|
if (item != null && item.Topics.Count > 0)
|
|
{
|
|
WriteIfVerbose(String.Format("Keyword {0} found in index",
|
|
item.KeyWord));
|
|
IndexTopic indexTopic = item.Topics[0] as IndexTopic;
|
|
return DisplayResult( keyword,
|
|
indexTopic);
|
|
}
|
|
else
|
|
{
|
|
WriteIfVerbose(String.Format("Keyword {0} not found in index",
|
|
keyword));
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
private void SearchFullText(string keyword)
|
|
{
|
|
string sort = "Rating ASC";
|
|
WriteIfVerbose(String.Format("Searching fulltext database for {0}",
|
|
keyword));
|
|
|
|
bool partialMatches = false;
|
|
bool titlesOnly = true;
|
|
int maxResults = 100;
|
|
DataTable results = chm.PerformSearch(keyword,
|
|
maxResults,
|
|
partialMatches,
|
|
titlesOnly);
|
|
WriteIfVerbose(String.Format("results.Rows.Count = {0}",
|
|
results != null ?
|
|
results.Rows.Count.ToString() : "(none)"));
|
|
if (results != null && results.Rows.Count > 0)
|
|
{
|
|
results.DefaultView.Sort = sort;
|
|
if (!DisplayResult(keyword,
|
|
results))
|
|
{
|
|
Say("No result");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Say("No result");
|
|
}
|
|
}
|
|
|
|
private void Search(string keyword)
|
|
{
|
|
if (!SearchIndex(keyword))
|
|
SearchFullText(keyword);
|
|
}
|
|
|
|
private bool DisplayResult(string keyword,
|
|
IndexTopic indexTopic)
|
|
{
|
|
keyword = keyword.Trim().ToLower();
|
|
string url = indexTopic.URL;
|
|
WriteIfVerbose(String.Format("URL from index search {0}",
|
|
url));
|
|
string prototype = ExtractPrototype(url);
|
|
if (prototype == null || prototype.Trim().Equals(String.Empty))
|
|
return false;
|
|
string formattedPrototype = FormatPrototype(prototype);
|
|
Say(formattedPrototype);
|
|
return true;
|
|
}
|
|
|
|
private bool DisplayResult(string keyword,
|
|
DataTable results)
|
|
{
|
|
keyword = keyword.Trim().ToLower();
|
|
for (int i = 0; i < results.DefaultView.Count; i++)
|
|
{
|
|
DataRowView row = results.DefaultView[i];
|
|
string title = row["Title"].ToString();
|
|
WriteIfVerbose(String.Format("Examining {0}", title));
|
|
if (title.Trim().ToLower().Equals(keyword))
|
|
{
|
|
string location = row["Location"].ToString();
|
|
string rating = row["Rating"].ToString();
|
|
string url = row["Url"].ToString();
|
|
string prototype = ExtractPrototype(url);
|
|
if (prototype == null || prototype.Trim().Equals(String.Empty))
|
|
continue;
|
|
string formattedPrototype = FormatPrototype(prototype);
|
|
Say(formattedPrototype);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void DisplayNoResult(MessageContext context,
|
|
string keyword)
|
|
{
|
|
TechBot.ServiceOutput.WriteLine(context,
|
|
String.Format("I don't know about keyword {0}",
|
|
keyword));
|
|
}
|
|
|
|
private string ReplaceComments(string s)
|
|
{
|
|
return Regex.Replace(s, "//(.+)\r\n", "");
|
|
}
|
|
|
|
private string ReplaceLineEndings(string s)
|
|
{
|
|
return Regex.Replace(s, "(\r\n)+", " ");
|
|
}
|
|
|
|
private string ReplaceSpaces(string s)
|
|
{
|
|
return Regex.Replace(s, @" +", " ");
|
|
}
|
|
|
|
private string ReplaceSpacesBeforeLeftParenthesis(string s)
|
|
{
|
|
return Regex.Replace(s, @"\( ", @"(");
|
|
}
|
|
|
|
private string ReplaceSpacesBeforeRightParenthesis(string s)
|
|
{
|
|
return Regex.Replace(s, @" \)", @")");
|
|
}
|
|
|
|
private string ReplaceSemicolon(string s)
|
|
{
|
|
return Regex.Replace(s, @";", @"");
|
|
}
|
|
|
|
private string FormatPrototype(string prototype)
|
|
{
|
|
string s = ReplaceComments(prototype);
|
|
s = ReplaceLineEndings(s);
|
|
s = ReplaceSpaces(s);
|
|
s = ReplaceSpacesBeforeLeftParenthesis(s);
|
|
s = ReplaceSpacesBeforeRightParenthesis(s);
|
|
s = ReplaceSemicolon(s);
|
|
return s;
|
|
}
|
|
|
|
private string ExtractPrototype(string url)
|
|
{
|
|
string page = GetPage(url);
|
|
Match match = Regex.Match(page,
|
|
"<PRE class=\"?syntax\"?>(.+)</PRE>",
|
|
RegexOptions.Multiline |
|
|
RegexOptions.Singleline);
|
|
if (match.Groups.Count > 1)
|
|
{
|
|
string prototype = match.Groups[1].ToString();
|
|
return StripHtml(StripAfterSlashPre(prototype));
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
private string StripAfterSlashPre(string html)
|
|
{
|
|
int index = html.IndexOf("</PRE>");
|
|
if (index != -1)
|
|
{
|
|
return html.Substring(0, index);
|
|
}
|
|
else
|
|
return html;
|
|
}
|
|
|
|
private string StripHtml(string html)
|
|
{
|
|
return Regex.Replace(html, @"<(.|\n)*?>", String.Empty);
|
|
}
|
|
|
|
private string GetPage(string url)
|
|
{
|
|
string CHMFileName = "";
|
|
string topicName = "";
|
|
string anchor = "";
|
|
CHMStream.CHMStream baseStream;
|
|
if (!chm.BaseStream.GetCHMParts(url, ref CHMFileName, ref topicName, ref anchor))
|
|
{
|
|
baseStream = chm.BaseStream;
|
|
CHMFileName = baseStream.CHMFileName;
|
|
topicName = url;
|
|
anchor = "";
|
|
}
|
|
else
|
|
{
|
|
baseStream = GetBaseStreamFromCHMFileName(CHMFileName);
|
|
}
|
|
|
|
if ((topicName == "") || (CHMFileName == "") || (baseStream == null))
|
|
{
|
|
return "";
|
|
}
|
|
|
|
return baseStream.ExtractTextFile(topicName);
|
|
}
|
|
|
|
private CHMStream.CHMStream GetBaseStreamFromCHMFileName(string CHMFileName)
|
|
{
|
|
foreach (CHMFile file in chm.FileList)
|
|
{
|
|
WriteIfVerbose(String.Format("Compare: {0} <> {1}",
|
|
file.ChmFilePath,
|
|
CHMFileName));
|
|
if (file.ChmFilePath.ToLower().Equals(CHMFileName.ToLower()))
|
|
{
|
|
return file.BaseStream;
|
|
}
|
|
}
|
|
WriteIfVerbose(String.Format("Could not find loaded CHM file in list: {0}",
|
|
CHMFileName));
|
|
return null;
|
|
}
|
|
}
|
|
}
|