summaryrefslogtreecommitdiff
path: root/plugins/Boltun
diff options
context:
space:
mode:
authorKirill Volinsky <mataes2007@gmail.com>2012-05-17 17:37:22 +0000
committerKirill Volinsky <mataes2007@gmail.com>2012-05-17 17:37:22 +0000
commit78d71d2cad6f243c6ff31d41380b8c5b58407de5 (patch)
treed0c05983b315352c5e66d23420da4b8fd8b5aff4 /plugins/Boltun
parenta9e8daee448c229aa3f8ded0c5f5c0fe7aa42529 (diff)
added some plugins
git-svn-id: http://svn.miranda-ng.org/main/trunk@20 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/Boltun')
-rw-r--r--plugins/Boltun/Boltun.rc155
-rw-r--r--plugins/Boltun/Boltun2.gifbin0 -> 13171 bytes
-rw-r--r--plugins/Boltun/Boltun_10.sln19
-rw-r--r--plugins/Boltun/Boltun_10.vcxproj197
-rw-r--r--plugins/Boltun/Boltun_10.vcxproj.filters110
-rw-r--r--plugins/Boltun/Engine/COPYING.txt339
-rw-r--r--plugins/Boltun/Engine/CriticalSection.h56
-rw-r--r--plugins/Boltun/Engine/Mind.cpp448
-rw-r--r--plugins/Boltun/Engine/Mind.h89
-rw-r--r--plugins/Boltun/Engine/MyCodeCvt.cpp85
-rw-r--r--plugins/Boltun/Engine/MyCodeCvt.h59
-rw-r--r--plugins/Boltun/Engine/PerContactData.h173
-rw-r--r--plugins/Boltun/Engine/TalkEngine.cpp609
-rw-r--r--plugins/Boltun/Engine/TalkEngine.h112
-rw-r--r--plugins/Boltun/Engine/UnrecentChooser.cpp100
-rw-r--r--plugins/Boltun/Engine/UnrecentChooser.h47
-rw-r--r--plugins/Boltun/Engine/ValueChooser.h88
-rw-r--r--plugins/Boltun/Engine/WordsList.cpp169
-rw-r--r--plugins/Boltun/Engine/WordsList.h56
-rw-r--r--plugins/Boltun/Engine/boltun.mindwbin0 -> 342176 bytes
-rw-r--r--plugins/Boltun/Engine/tstring.h34
-rw-r--r--plugins/Boltun/TODO.txt35
-rw-r--r--plugins/Boltun/actionQueue.cpp256
-rw-r--r--plugins/Boltun/actionQueue.h30
-rw-r--r--plugins/Boltun/boltun.cpp718
-rw-r--r--plugins/Boltun/boltun.h80
-rw-r--r--plugins/Boltun/config.cpp159
-rw-r--r--plugins/Boltun/config.h157
-rw-r--r--plugins/Boltun/lang_pack.txt64
-rw-r--r--plugins/Boltun/mind.txt142
-rw-r--r--plugins/Boltun/readme.txt124
-rw-r--r--plugins/Boltun/res/boltun.icobin0 -> 766 bytes
-rw-r--r--plugins/Boltun/res/smiles.datbin0 -> 3394 bytes
-rw-r--r--plugins/Boltun/resource.h46
34 files changed, 4756 insertions, 0 deletions
diff --git a/plugins/Boltun/Boltun.rc b/plugins/Boltun/Boltun.rc
new file mode 100644
index 0000000000..4ae46eea47
--- /dev/null
+++ b/plugins/Boltun/Boltun.rc
@@ -0,0 +1,155 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (Canada) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENC)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_CAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_MAINBOLTUN ICON "res\\boltun.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// SMILES
+//
+
+IDR_SMILES SMILES "res\\smiles.dat"
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_MAIN DIALOGEX 0, 0, 307, 231
+STYLE DS_SETFONT | WS_CHILD | WS_BORDER
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Boltun",IDC_PLUGINRM,5,2,297,222
+ CONTROL "Boltun Autochat everybody",IDC_EVERYBODY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,20,230,10
+ CONTROL "Boltun Autochat everybody not in contact list",IDC_NOTINLIST,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,33,230,10
+ CONTROL "Boltun Autochat everybody while away",IDC_AUTOAWAY,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,46,230,10
+ CONTROL "Warn contacts Boltun is chatting",IDC_WARN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,59,230,10
+ CONTROL "Mark replied messages as read",IDC_MARKREAD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,72,230,10
+ EDITTEXT IDC_WAITTIME,26,161,24,12,ES_AUTOHSCROLL | ES_NUMBER
+ LTEXT "sec",IDC_LSEC,53,161,72,8
+ EDITTEXT IDC_WARNTXT,17,97,272,31,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL
+ GROUPBOX "Warning text",IDC_LWARN,12,85,283,50
+ GROUPBOX "Time before the answer",IDC_LWAITTIME,12,138,283,72
+ CONTROL "Typing time depends on message length (in chars)",IDC_PAUSEDEPENDS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,21,178,270,11
+ CONTROL "Typing and thinking time can be much longer",IDC_PAUSERANDOM,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,21,193,270,10
+ LTEXT "Typing time (for a message of 4 words):",IDC_STATIC,21,148,145,8
+ LTEXT "Thinking time:",IDC_STATIC,173,148,111,8
+ EDITTEXT IDC_THINKTIME,177,161,24,12,ES_AUTOHSCROLL | ES_NUMBER
+ LTEXT "sec",IDC_LSEC2,205,161,72,8
+END
+
+IDD_ENGINE DIALOGEX 0, 0, 307, 231
+STYLE DS_SETFONT | WS_CHILD | WS_BORDER
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Engine",IDC_PLUGINRM,5,48,297,174
+ EDITTEXT IDC_MINDFILE,12,19,210,14,ES_AUTOHSCROLL | ES_READONLY | NOT WS_TABSTOP
+ PUSHBUTTON "...",IDC_BTNPATH,226,18,16,16
+ GROUPBOX "Base of remarks",IDC_TXTREPLICPATH,5,6,297,36
+ PUSHBUTTON "Reload",IDC_BTNRELOAD,246,18,52,16
+ CONTROL "Stay silent, if have no good aswers",IDC_ENGINE_SILENT,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,62,283,10
+ CONTROL "Start answers with a lowercase letter",IDC_ENGINE_LOWERCASE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,94,282,10
+ CONTROL "Don't use ""I don't understand""-like answers",IDC_ENGINE_UNDERSTAND_ALWAYS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,27,78,248,10
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_MAIN, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 300
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 224
+ END
+
+ IDD_ENGINE, DIALOG
+ BEGIN
+ RIGHTMARGIN, 306
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (Canada) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/plugins/Boltun/Boltun2.gif b/plugins/Boltun/Boltun2.gif
new file mode 100644
index 0000000000..fc2b4a93df
--- /dev/null
+++ b/plugins/Boltun/Boltun2.gif
Binary files differ
diff --git a/plugins/Boltun/Boltun_10.sln b/plugins/Boltun/Boltun_10.sln
new file mode 100644
index 0000000000..9f1aa3541d
--- /dev/null
+++ b/plugins/Boltun/Boltun_10.sln
@@ -0,0 +1,19 @@
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Boltun_10", "Boltun_10.vcxproj", "{98DF7761-41D7-4C5C-9FFE-E5CCB3997783}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug Unicode|Win32 = Debug Unicode|Win32
+ Release Unicode|Win32 = Release Unicode|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {98DF7761-41D7-4C5C-9FFE-E5CCB3997783}.Debug Unicode|Win32.ActiveCfg = Debug Unicode|Win32
+ {98DF7761-41D7-4C5C-9FFE-E5CCB3997783}.Debug Unicode|Win32.Build.0 = Debug Unicode|Win32
+ {98DF7761-41D7-4C5C-9FFE-E5CCB3997783}.Release Unicode|Win32.ActiveCfg = Release Unicode|Win32
+ {98DF7761-41D7-4C5C-9FFE-E5CCB3997783}.Release Unicode|Win32.Build.0 = Release Unicode|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/plugins/Boltun/Boltun_10.vcxproj b/plugins/Boltun/Boltun_10.vcxproj
new file mode 100644
index 0000000000..0f05bca890
--- /dev/null
+++ b/plugins/Boltun/Boltun_10.vcxproj
@@ -0,0 +1,197 @@
+п»ї<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug Unicode|Win32">
+ <Configuration>Debug Unicode</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release Unicode|Win32">
+ <Configuration>Release Unicode</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{98DF7761-41D7-4C5C-9FFE-E5CCB3997783}</ProjectGuid>
+ <RootNamespace>Boltun</RootNamespace>
+ <ProjectName>boltun</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">$(SolutionDir)$(Configuration)/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">$(SolutionDir)$(Configuration)/Obj/$(ProjectName)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">false</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">$(SolutionDir)$(Configuration)/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">$(SolutionDir)$(Configuration)/Obj/$(ProjectName)\</IntDir>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">$(ProjectName)w</TargetName>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">$(ProjectName)w</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Release/Boltun.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <AdditionalIncludeDirectories>../../include;../ExternalAPI;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>NDEBUG;BOLTUN_EXPORTS;WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_UNICODE;UNICODE;_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ <CustomBuildStep>
+ <Command>copy Engine\boltun.mindw "$(SolutionDir)$(Configuration)\Plugins"
+</Command>
+ <Outputs>$(OutDir)boltun.mindw;%(Outputs)</Outputs>
+ </CustomBuildStep>
+ <Midl>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Debug/Boltun.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../include;../ExternalAPI;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG;WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;BOLTUN_EXPORTS;_UNICODE;UNICODE;_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>odbc32.lib;odbccp32.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="actionQueue.cpp" />
+ <ClCompile Include="boltun.cpp">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">../include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">../include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="config.cpp" />
+ <ClCompile Include="Engine\Mind.cpp" />
+ <ClCompile Include="Engine\MyCodeCvt.cpp" />
+ <ClCompile Include="Engine\TalkEngine.cpp" />
+ <ClCompile Include="Engine\UnrecentChooser.cpp" />
+ <ClCompile Include="Engine\WordsList.cpp">
+ <Optimization Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">Full</Optimization>
+ <InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">true</IntrinsicFunctions>
+ <FavorSizeOrSpeed Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">Speed</FavorSizeOrSpeed>
+ <OmitFramePointers Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">true</OmitFramePointers>
+ <EnableFiberSafeOptimizations Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">true</EnableFiberSafeOptimizations>
+ <WholeProgramOptimization Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">true</WholeProgramOptimization>
+ <BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">Default</BasicRuntimeChecks>
+ <DebugInformationFormat Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="actionQueue.h" />
+ <ClInclude Include="boltun.h" />
+ <ClInclude Include="config.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="Engine\CriticalSection.h" />
+ <ClInclude Include="Engine\Mind.h" />
+ <ClInclude Include="Engine\MyCodeCvt.h" />
+ <ClInclude Include="Engine\PerContactData.h" />
+ <ClInclude Include="Engine\TalkEngine.h" />
+ <ClInclude Include="Engine\tstring.h" />
+ <ClInclude Include="Engine\UnrecentChooser.h" />
+ <ClInclude Include="Engine\ValueChooser.h" />
+ <ClInclude Include="Engine\WordsList.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="res\boltun.ico" />
+ <None Include="res\smiles.dat" />
+ <None Include="Engine\boltun.mindw" />
+ <None Include="lang_pack.txt" />
+ <None Include="mind.txt" />
+ <None Include="readme.txt" />
+ <None Include="TODO.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="boltun.rc">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/Boltun/Boltun_10.vcxproj.filters b/plugins/Boltun/Boltun_10.vcxproj.filters
new file mode 100644
index 0000000000..e025146d37
--- /dev/null
+++ b/plugins/Boltun/Boltun_10.vcxproj.filters
@@ -0,0 +1,110 @@
+п»ї<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{053e819b-1bb3-488b-8dbd-f6b29640c30c}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Source Files\Enigne">
+ <UniqueIdentifier>{e94debaf-39e5-4d66-95b0-b02f301c140c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{db6f678c-e69c-4576-819a-360e80ffc84c}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ <Filter Include="Header Files\Engine">
+ <UniqueIdentifier>{614d736c-849c-4a07-ba4e-a6c9b895154b}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{c04447a2-5567-4425-92b8-a4a1267e7e32}</UniqueIdentifier>
+ <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="actionQueue.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="boltun.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Engine\Mind.cpp">
+ <Filter>Source Files\Enigne</Filter>
+ </ClCompile>
+ <ClCompile Include="Engine\MyCodeCvt.cpp">
+ <Filter>Source Files\Enigne</Filter>
+ </ClCompile>
+ <ClCompile Include="Engine\TalkEngine.cpp">
+ <Filter>Source Files\Enigne</Filter>
+ </ClCompile>
+ <ClCompile Include="Engine\UnrecentChooser.cpp">
+ <Filter>Source Files\Enigne</Filter>
+ </ClCompile>
+ <ClCompile Include="Engine\WordsList.cpp">
+ <Filter>Source Files\Enigne</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="actionQueue.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="boltun.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Engine\CriticalSection.h">
+ <Filter>Header Files\Engine</Filter>
+ </ClInclude>
+ <ClInclude Include="Engine\Mind.h">
+ <Filter>Header Files\Engine</Filter>
+ </ClInclude>
+ <ClInclude Include="Engine\MyCodeCvt.h">
+ <Filter>Header Files\Engine</Filter>
+ </ClInclude>
+ <ClInclude Include="Engine\PerContactData.h">
+ <Filter>Header Files\Engine</Filter>
+ </ClInclude>
+ <ClInclude Include="Engine\TalkEngine.h">
+ <Filter>Header Files\Engine</Filter>
+ </ClInclude>
+ <ClInclude Include="Engine\tstring.h">
+ <Filter>Header Files\Engine</Filter>
+ </ClInclude>
+ <ClInclude Include="Engine\UnrecentChooser.h">
+ <Filter>Header Files\Engine</Filter>
+ </ClInclude>
+ <ClInclude Include="Engine\ValueChooser.h">
+ <Filter>Header Files\Engine</Filter>
+ </ClInclude>
+ <ClInclude Include="Engine\WordsList.h">
+ <Filter>Header Files\Engine</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="res\boltun.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="res\smiles.dat">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="lang_pack.txt" />
+ <None Include="mind.txt" />
+ <None Include="readme.txt" />
+ <None Include="TODO.txt" />
+ <None Include="Engine\boltun.mindw">
+ <Filter>Source Files</Filter>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="boltun.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/Boltun/Engine/COPYING.txt b/plugins/Boltun/Engine/COPYING.txt
new file mode 100644
index 0000000000..82fa1daad4
--- /dev/null
+++ b/plugins/Boltun/Engine/COPYING.txt
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/Boltun/Engine/CriticalSection.h b/plugins/Boltun/Engine/CriticalSection.h
new file mode 100644
index 0000000000..59bc91beb9
--- /dev/null
+++ b/plugins/Boltun/Engine/CriticalSection.h
@@ -0,0 +1,56 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef CriticalSectionH
+#define CriticalSectionH
+
+#include <windows.h>
+
+class CriticalSection
+{
+ CRITICAL_SECTION csQueue;
+public:
+ inline CriticalSection()
+ {
+ InitializeCriticalSection(&csQueue);
+ }
+
+ inline ~CriticalSection()
+ {
+ DeleteCriticalSection(&csQueue);
+ }
+
+ inline void Enter()
+ {
+ EnterCriticalSection(&csQueue);
+ }
+
+ inline void Leave()
+ {
+ LeaveCriticalSection(&csQueue);
+ }
+
+ inline bool TryEnter()
+ {
+ return TryEnterCriticalSection(&csQueue) != 0;
+ }
+};
+
+#endif /* CriticalSectionH */ \ No newline at end of file
diff --git a/plugins/Boltun/Engine/Mind.cpp b/plugins/Boltun/Engine/Mind.cpp
new file mode 100644
index 0000000000..6260177095
--- /dev/null
+++ b/plugins/Boltun/Engine/Mind.cpp
@@ -0,0 +1,448 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#include "Mind.h"
+#include <fstream>
+#include <algorithm>
+#include "tstring.h"
+#include "assert.h"
+
+#include <windows.h>
+
+#ifdef UNICODE
+#include "MyCodeCvt.h"
+#endif
+
+using namespace std;
+
+typedef vector<tstring> string_vec;
+typedef multimap<tstring,tstring> string_mmap;
+
+Mind::Mind()
+{
+ data = new MindData();
+ data->referenceCount = 1;
+ data->maxSmileLen = 0;
+}
+
+Mind::~Mind()
+{
+ if (--data->referenceCount == 0)
+ delete data;
+}
+
+Mind::Mind(const Mind& mind)
+{
+ mind.data->referenceCount++;
+ data = mind.data;
+}
+
+const MindData *Mind::GetData() const
+{
+ return data;
+}
+
+Mind& Mind::operator= (const Mind& mind)
+{
+ if (--data->referenceCount == 0)
+ delete data;
+ mind.data->referenceCount++;
+ data = mind.data;
+ return *this;
+}
+
+inline void format(tstring& s)
+{
+ int pos = s.length() - 1;
+ if (s[pos] == _T('\r'))
+ s.resize(pos);
+}
+
+void toLowerStr(TCHAR* ch)
+{
+ CharLower(ch);
+}
+
+vector<tstring> Mind::Parse(tstring s)
+{
+ int len = s.length() - 1;
+ vector <tstring> res;
+ while (len != -1 && _istspace(s[len]))
+ len--;
+ if (len < 0)
+ return res;
+ s.resize(len);
+ int it = 0;
+ while (it != len)
+ {
+ while (it != len && _istspace(s[it]))
+ it++;
+ if (it == len)
+ break;
+ int start = it;
+ while (it != len && !_istspace(s[it]))
+ it++;
+ res.push_back(s.substr(start, it - start));
+ }
+ return res;
+}
+
+void Mind::Load(tstring filename)
+{
+ basic_ifstream<TCHAR, char_traits<TCHAR> > file;
+ setlocale(LC_ALL, "");
+#ifdef UNICODE
+ locale ulocale(locale(), new MyCodeCvt);
+ file.imbue(ulocale);
+#endif
+ file.open(filename.c_str(), ios_base::in | ios_base::binary);
+ tstring s1, st;
+ TCHAR *c, *co;
+ size_t count;
+ int error = 0;
+ int line = 1;
+#ifdef UNICODE
+ bool start = true;
+#endif
+ try
+ {
+ while (file.good())
+ {
+ getline(file, st);
+ if (st.empty())
+ break;
+ line++;
+#ifdef UNICODE
+ if (start)
+ {
+ if (st[0] == 65279)
+ {
+ st.erase(0, 1);
+ fileTypeMark = true;
+ }
+ else
+ fileTypeMark = false;
+ start = false;
+ }
+#endif
+ format(st);
+ count = st.length();
+ c = co = new TCHAR[count+1];
+ _tcscpy(c, st.c_str());
+ size_t pos = 0;
+ while (pos < count && _istspace(*c))
+ {
+ ++pos;
+ ++c;
+ }
+ count -= pos;
+ if (count > 2)
+ {
+ switch (*c)
+ {
+ case '(':
+ if (c[count - 1] != ')')
+ abort();
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ ++c;
+ count -= 2;
+ c[count] = '\0';
+ toLowerStr(c);
+ {
+ WordsList l(c);
+ if (!l.IsEmpty())
+ if (l.IsQuestion())
+ data->qkeywords.insert(make_pair(l, s1));
+ else
+ data->keywords.insert(make_pair(l, s1));
+ }
+ break;
+ case '{':
+ if (c[count - 1] != '}')
+ abort();
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ ++c;
+ count -= 2;
+ c[count] = '\0';
+ toLowerStr(c);
+ {
+ WordsList l(c);
+ if (!l.IsEmpty())
+ if (l.IsQuestion())
+ data->qspecialEscapes.insert(make_pair(l, s1));
+ else
+ data->specialEscapes.insert(make_pair(l, s1));
+ }
+ break;
+ case '[':
+ if (c[count - 1] != ']')
+ throw error;
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ ++c;
+ count -= 2;
+ c[count] = '\0';
+ toLowerStr(c);
+ data->widelyUsed.insert(make_pair(c, s1));
+ break;
+ case '<':
+ if (c[count - 1] != '>')
+ throw error;
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ ++c;
+ count -= 2;
+ c[count] = '\0';
+ if (_tcscmp(c,_T("QUESTION")) == 0)
+ {
+ toLowerStr(c);
+ data->question.insert(s1);
+ }
+ else
+ if (_tcscmp(c,_T("IGNORED")) == 0)
+ {
+ toLowerStr(c);
+ data->special.insert(s1);
+ }
+ else
+ if (_tcscmp(c,_T("ESCAPE")) == 0)
+ {
+ data->escape.push_back(s1);
+ }
+ else
+ if (_tcscmp(c,_T("FAILURE")) == 0)
+ {
+ data->failure.push_back(s1);
+ }
+ else
+ if (_tcscmp(c,_T("REPEAT")) == 0)
+ {
+ data->repeats.push_back(s1);
+ }
+ else
+ {
+ if (_tcscmp(c,_T("INITIAL")) != 0)
+ throw error;
+ data->initial.push_back(s1);
+ }
+ break;
+ case '@':
+ {
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ ++c;
+ count -= 1;
+ toLowerStr(c);
+ tstring sc(c);
+ int count1 = s1.length();
+ TCHAR *c = new TCHAR[count1 + 1];
+ _tcscpy(c, s1.c_str());
+ CharLower(c);
+ s1 = c;
+ delete c;
+ vector<tstring> strs = Parse(s1);
+ data->raliases.insert(make_pair(sc, strs));
+ for (vector<tstring>::const_iterator it = strs.begin(); it != strs.end(); it++)
+ data->aliases.insert(make_pair(*it, sc));
+ }
+ break;
+ default:
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ toLowerStr(c);
+ data->study.insert(make_pair(c, s1));
+ }
+ }
+ else
+ if (count)
+ {
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ data->study.insert(make_pair(c, s1));
+ }
+ }
+ if (!file.eof())
+ {
+ throw error;
+ }
+ delete co;
+ }
+ catch(...)
+ {
+ throw CorruptedMind(line);
+ delete co;
+ }
+}
+
+void Mind::Save(tstring filename) const
+{
+ basic_ofstream<TCHAR, char_traits<TCHAR> > file;
+#ifdef UNICODE
+ locale ulocale(locale(), new MyCodeCvt);
+ file.imbue(ulocale);
+#endif
+ file.open(filename.c_str(), ios_base::out | ios_base::binary);
+#ifdef UNICODE
+ if (fileTypeMark)
+ file << TCHAR(65279);
+#endif
+ for (string_mmap::iterator it = data->study.begin(); it != data->study.end(); it++)
+ {
+ file << (*it).first << _T('\r') << endl;
+ file << (*it).second << _T('\r') << endl;
+ }
+ for (multimap<WordsList, tstring>::iterator it = data->keywords.begin(); it != data->keywords.end(); it++)
+ {
+ file << _T(" (") << (tstring)(*it).first << _T(")") << _T('\r') << endl;
+ file << (*it).second << _T('\r') << endl;
+ }
+ for (multimap<WordsList, tstring>::iterator it = data->qkeywords.begin(); it != data->qkeywords.end(); it++)
+ {
+ file << _T(" (") << (tstring)(*it).first << _T(")") << _T('\r') << endl;
+ file << (*it).second << _T('\r') << endl;
+ }
+ for (multimap<WordsList, tstring>::iterator it = data->specialEscapes.begin(); it != data->specialEscapes.end(); it++)
+ {
+ file << _T(" {") << (tstring)(*it).first << _T("}") << _T('\r') << endl;
+ file << (*it).second << _T('\r') << endl;
+ }
+ for (multimap<WordsList, tstring>::iterator it = data->qspecialEscapes.begin(); it != data->qspecialEscapes.end(); it++)
+ {
+ file << _T(" {") << (tstring)(*it).first << _T("}") << _T('\r') << endl;
+ file << (*it).second << _T('\r') << endl;
+ }
+ for (string_mmap::iterator it = data->widelyUsed.begin(); it != data->widelyUsed.end(); it++)
+ {
+ file << _T(" [") << (*it).first << _T("]") << _T('\r') << endl;
+ file << (*it).second << _T('\r') << endl;
+ }
+ for (set<tstring>::iterator it = data->question.begin(); it != data->question.end(); it++)
+ {
+ file << _T(" <QUESTION>") << _T('\r') << endl;
+ file << (*it) << _T('\r') << endl;
+ }
+ for (set<tstring>::iterator it = data->special.begin(); it != data->special.end(); it++)
+ {
+ file << _T(" <IGNORED>") << _T('\r') << endl;
+ file << (*it) << _T('\r') << endl;
+ }
+ for (string_vec::iterator it = data->escape.begin(); it != data->escape.end(); it++)
+ {
+ file << _T(" <ESCAPE>") << _T('\r') << endl;
+ file << (*it) << _T('\r') << endl;
+ }
+ for (string_vec::iterator it = data->initial.begin(); it != data->initial.end(); it++)
+ {
+ file << _T(" <INITIAL>") << _T('\r') << endl;
+ file << (*it) << _T('\r') << endl;
+ }
+ for (string_vec::iterator it = data->failure.begin(); it != data->failure.end(); it++)
+ {
+ file << _T(" <FAILURE>") << _T('\r') << endl;
+ file << (*it) << _T('\r') << endl;
+ }
+ for (string_vec::iterator it = data->repeats.begin(); it != data->repeats.end(); it++)
+ {
+ file << _T(" <REPEAT>") << _T('\r') << endl;
+ file << (*it) << _T('\r') << endl;
+ }
+ for (map<tstring,vector<tstring>>::const_iterator it = data->raliases.begin(); it != data->raliases.end(); it++)
+ {
+ tstring s;
+ const vector<tstring>& v = (*it).second;
+ bool first = true;
+ for (vector<tstring>::const_iterator it1 = v.begin(); it1 != v.end(); it1++)
+ {
+ if (first)
+ {
+ first = false;
+ s = *it1;
+ }
+ else
+ {
+ s += _T(" ") + *it1;
+ }
+ }
+ file << _T('@') << (*it).first << _T('\r') << endl;
+ file << s << _T('\r') << endl;
+ }
+}
+
+void Mind::LoadSmiles(tstring filename)
+{
+ basic_ifstream<TCHAR, char_traits<TCHAR> > file;
+ file.open(filename.c_str());
+ data->smiles.clear();
+ tstring s;
+ unsigned int l = 0;
+ while (!file.eof())
+ {
+ getline(file, s);
+ if (s.length() > l)
+ l = s.length();
+ data->smiles.insert(s);
+ }
+ data->maxSmileLen = l;
+}
+
+void Mind::LoadSmiles(void *smiles, size_t size)
+{
+ data->smiles.clear();
+ TCHAR* buf = (TCHAR*)smiles;
+ unsigned l = 0;
+ TCHAR* end = buf + size;
+ while (buf != end)
+ {
+ TCHAR *lend = buf;
+ while (lend != end && *lend != _T('\r'))
+ lend++;
+ tstring s(buf, lend - buf);
+ if ((unsigned)(lend - buf) > l)
+ l = s.length();
+ data->smiles.insert(s);
+ if (lend == end)
+ break;
+ buf = lend + 2;
+ }
+ data->maxSmileLen = l;
+}
diff --git a/plugins/Boltun/Engine/Mind.h b/plugins/Boltun/Engine/Mind.h
new file mode 100644
index 0000000000..ecf3f5d199
--- /dev/null
+++ b/plugins/Boltun/Engine/Mind.h
@@ -0,0 +1,89 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef MindH
+#define MindH
+
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+#include "WordsList.h"
+#include <tchar.h>
+#include "tstring.h"
+
+
+class Mind;
+
+typedef struct
+{
+ friend class Mind;
+ std::vector<tstring> initial;
+ std::set<tstring> question;
+ std::set<tstring> special;
+ std::vector<tstring> escape;
+ std::vector<tstring> failure;
+ std::vector<tstring> repeats;
+ unsigned int maxSmileLen;
+ std::set<tstring> smiles;
+ std::multimap<WordsList, tstring> keywords;
+ std::multimap<WordsList, tstring> qkeywords;
+ std::multimap<tstring, tstring> widelyUsed;
+ std::multimap<WordsList, tstring> specialEscapes;
+ std::multimap<WordsList, tstring> qspecialEscapes;
+ std::multimap<tstring, tstring> study;
+ std::map<tstring, tstring> aliases;
+ std::map<tstring, std::vector<tstring>> raliases;
+private:
+ int referenceCount;
+} MindData;
+
+class Mind
+{
+private:
+ MindData *data;
+#ifdef UNICODE
+ bool fileTypeMark;
+#endif
+ std::vector<tstring> Parse(tstring s);
+public:
+ Mind();
+ ~Mind();
+ Mind(const Mind& mind);
+ Mind& operator= (const Mind& mind);
+
+ class CorruptedMind
+ {
+ public:
+ int line;
+ CorruptedMind(int aline)
+ : line(aline)
+ {
+ };
+ };
+
+ const MindData *GetData() const;
+ void Load(tstring filename);
+ void Save(tstring filename) const;
+ void LoadSmiles(tstring filename);
+ void LoadSmiles(void* smiles, size_t size);
+};
+
+#endif
diff --git a/plugins/Boltun/Engine/MyCodeCvt.cpp b/plugins/Boltun/Engine/MyCodeCvt.cpp
new file mode 100644
index 0000000000..71e056291a
--- /dev/null
+++ b/plugins/Boltun/Engine/MyCodeCvt.cpp
@@ -0,0 +1,85 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifdef UNICODE
+
+#include "MyCodeCvt.h"
+
+using namespace std;
+
+MyCodeCvt::MyCodeCvt(size_t _R)
+ : MyCodeCvtBase(_R)
+{
+}
+
+MyCodeCvt::result MyCodeCvt::do_in(_St& _State ,
+ const _To* _F1, const _To* _L1, const _To*& _Mid1,
+ _E* F2, _E* _L2, _E*& _Mid2) const
+{
+ return noconv;
+}
+
+#ifdef MSVC
+MyCodeCvt::result MyCodeCvt::do_out(_St& _State,
+ const _E* _F1, const _E* _L1, const _E*& _Mid1,
+ _To* F2, _E* _L2, _To*& _Mid2) const
+#else
+MyCodeCvt::result MyCodeCvt::do_out(_St& _State,
+ const _E* _F1, const _E* _L1, const _E*& _Mid1,
+ _To* F2, _To* _L2, _To*& _Mid2) const
+#endif
+{
+ return noconv;
+}
+
+MyCodeCvt::result MyCodeCvt::do_unshift( _St& _State,
+ _To* _F2, _To* _L2, _To*& _Mid2) const
+{
+ return noconv;
+}
+
+#ifdef MSVC
+int MyCodeCvt::do_length(_St& _State, const _To* _F1,
+ const _To* _L1, size_t _N2) const _THROW0()
+#else
+int MyCodeCvt::do_length(const _St& _State, const _To* _F1,
+ const _To* _L1, size_t _N2) const _THROW0()
+#endif
+
+{
+ return (_N2 < (size_t)(_L1 - _F1)) ? _N2 : _L1 - _F1;
+}
+
+bool MyCodeCvt::do_always_noconv() const _THROW0()
+{
+ return true;
+}
+
+int MyCodeCvt::do_max_length() const _THROW0()
+{
+ return 2;
+}
+
+int MyCodeCvt::do_encoding() const _THROW0()
+{
+ return 2;
+}
+
+#endif \ No newline at end of file
diff --git a/plugins/Boltun/Engine/MyCodeCvt.h b/plugins/Boltun/Engine/MyCodeCvt.h
new file mode 100644
index 0000000000..b4508c66de
--- /dev/null
+++ b/plugins/Boltun/Engine/MyCodeCvt.h
@@ -0,0 +1,59 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef MYCODECVT_H
+#define MYCODECVT_H
+
+#include <locale>
+#include <xlocale>
+#include <cwchar>
+
+typedef std::codecvt<wchar_t, char, std::mbstate_t> MyCodeCvtBase;
+
+class MyCodeCvt
+ : public MyCodeCvtBase
+{
+public:
+ typedef wchar_t _E;
+ typedef char _To;
+ typedef std::mbstate_t _St;
+ explicit MyCodeCvt( size_t _R=0 );
+protected:
+ virtual result do_in(_St& _State, const _To* _F1, const _To* _L1, const _To*& _Mid1,
+ _E* F2 , _E* _L2 , _E*& _Mid2) const;
+#ifdef MSVC
+ virtual result do_out(_St& _State, const _E* _F1, const _E* _L1, const _E*& _Mid1,
+ _To* F2, _E* _L2 , _To*& _Mid2) const;
+#else
+ virtual result do_out(_St& _State, const _E* _F1, const _E* _L1, const _E*& _Mid1,
+ _To* F2, _To* _L2 , _To*& _Mid2) const;
+#endif
+ virtual result do_unshift(_St& _State, _To* _F2, _To* _L2, _To*& _Mid2) const;
+#ifdef MSVC
+ virtual int do_length(_St& _State, const _To* _F1, const _To* _L1, size_t _N2) const _THROW0();
+#else
+ virtual int do_length(const _St& _State, const _To* _F1, const _To* _L1, size_t _N2) const _THROW0();
+#endif
+ virtual bool do_always_noconv() const _THROW0();
+ virtual int do_max_length() const _THROW0();
+ virtual int do_encoding() const _THROW0();
+} ;
+
+#endif /* MYCODECVT_H */ \ No newline at end of file
diff --git a/plugins/Boltun/Engine/PerContactData.h b/plugins/Boltun/Engine/PerContactData.h
new file mode 100644
index 0000000000..14b17784b9
--- /dev/null
+++ b/plugins/Boltun/Engine/PerContactData.h
@@ -0,0 +1,173 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef PerContactDataH
+#define PerContactDataH
+
+#include <map>
+#include <time.h>
+#include <assert.h>
+
+#include "CriticalSection.h"
+
+static std::map<unsigned, void*> perContactDataObjects;
+
+template <class Source, class Data, class ContactHandle>
+class PerContactData
+{
+ template <class Source, class Data>
+ struct InternalData
+ {
+ CriticalSection lock;
+ Data *data;
+ time_t time;
+ inline InternalData(const Source& src)
+ :time(0)
+ {
+ data = new Data(src);
+ }
+
+ inline InternalData()
+ :data(NULL)
+ {
+ assert(false);
+ }
+
+ inline ~InternalData()
+ {
+ delete data;
+ }
+ };
+ CriticalSection mapLock;
+ unsigned timerID;
+ std::map<ContactHandle, InternalData<Source, Data>* > datas;
+ typedef typename std::map<ContactHandle, InternalData<Source, Data>* >::iterator mapIt;
+ const Source& source;
+ void CleanupData();
+ template <class Source, class Data, class ContactHandle>
+ friend VOID CALLBACK RunTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
+public:
+ PerContactData(const Source& src);
+ ~PerContactData();
+ Data* GetData(ContactHandle Contact);
+ void PutData(ContactHandle Contact);
+};
+
+template <class Source, class Data, class ContactHandle>
+PerContactData<Source, Data, ContactHandle>::PerContactData(const Source& src)
+ :source(src), timerID(0)
+{
+}
+
+template <class Source, class Data, class ContactHandle>
+PerContactData<Source, Data, ContactHandle>::~PerContactData()
+{
+ mapLock.Enter();
+ if (timerID)
+ {
+ KillTimer(NULL, timerID);
+ perContactDataObjects.erase(timerID);
+ }
+ while (!datas.empty())
+ {
+ while (!(*datas.begin()).second->lock.TryEnter())
+ {
+ mapLock.Leave();
+ Sleep(10);
+ mapLock.Enter();
+ }
+ //Now we know exactly that no-one onws a contact lock
+ InternalData<Source, Data>* data = (*datas.begin()).second;
+ data->lock.Leave();
+ delete data;
+ datas.erase(datas.begin());
+ }
+ mapLock.Leave();
+}
+
+template <class Source, class Data, class ContactHandle>
+Data* PerContactData<Source, Data, ContactHandle>::GetData(ContactHandle Contact)
+{
+ mapLock.Enter();
+ mapIt it;
+ if ((it = datas.find(Contact)) == datas.end())
+ it = datas.insert(make_pair(Contact, new InternalData<Source, Data>(source))).first;
+ (*it).second->lock.Enter();
+ (*it).second->time = 0;
+ Data* data = (*it).second->data;
+ mapLock.Leave();
+ return data;
+}
+
+template <class Source, class Data, class ContactHandle>
+void PerContactData<Source, Data, ContactHandle>::PutData(ContactHandle Contact)
+{
+ mapLock.Enter();
+ datas[Contact]->lock.Leave();
+ ::time(&(datas[Contact]->time));
+ if (!timerID)
+ {
+ timerID = SetTimer(NULL, 0, 30000, RunTimerProc<Source, Data, ContactHandle>);
+ assert(timerID);
+ perContactDataObjects[timerID] = this;
+ }
+ mapLock.Leave();
+}
+
+template <class Source, class Data, class ContactHandle>
+void PerContactData<Source, Data, ContactHandle>::CleanupData()
+{
+ mapLock.Enter();
+ time_t now;
+ time(&now);
+ for (mapIt it = datas.begin(); it != datas.end(); )
+ {
+ if ((*it).second->time) //it's being in use
+ {
+ int diff = (int)difftime(now, (*it).second->time);
+ if (diff >= 30*60) //half of an hour
+ {
+ mapIt tmp = it;
+ it++;
+ delete (*tmp).second;
+ datas.erase(tmp);
+ }
+ else
+ it++;
+ }
+ else
+ it++;
+ }
+ if (timerID && datas.empty()) //timerID may become NULL before locking, so should check
+ {
+ KillTimer(NULL, timerID);
+ perContactDataObjects.erase(timerID);
+ }
+ mapLock.Leave();
+}
+
+template <class Source, class Data, class ContactHandle>
+VOID CALLBACK RunTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ PerContactData<Source, Data, ContactHandle>* val = (PerContactData<Source, Data, ContactHandle>*)perContactDataObjects[idEvent];
+ val->CleanupData();
+}
+
+#endif /* PerContactDataH */ \ No newline at end of file
diff --git a/plugins/Boltun/Engine/TalkEngine.cpp b/plugins/Boltun/Engine/TalkEngine.cpp
new file mode 100644
index 0000000000..a9625dffd2
--- /dev/null
+++ b/plugins/Boltun/Engine/TalkEngine.cpp
@@ -0,0 +1,609 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#include "TalkEngine.h"
+#include <fstream>
+#include <windows.h>
+
+#ifdef _DEBUG
+
+//#define DEBUG_PREFIXES
+//#define DEBUG_SHOW_LEVEL
+//#define DEBUG_SHOW_VARIANTS
+//#define DEBUG_SHOW_SOLUTION_REASON
+
+#endif
+
+//Enabling next define will make a bot more stupid:
+//#define EXCLUDE_SPECIAL_WORDS
+
+#ifdef DEBUG_SHOW_VARIANTS
+extern void AddBotMessage(tstring s);
+#endif
+
+using namespace std;
+
+void TalkBot::UpdateStartChar(tstring& str)
+{
+ if (!makeLowercase)
+ return;
+ size_t l = str.length();
+ if (l)
+ {
+ //Answers starting with ' ' must remain unchanged.
+ if (str[0] == _T(' '))
+ {
+ str = str.substr(1);
+ return;
+ }
+ for (size_t i = 0; i < l; i++)
+ {
+ TCHAR cl = (TCHAR)CharLower((LPTSTR)(void*)(long)str[i]);
+ TCHAR cu = (TCHAR)CharUpper((LPTSTR)(void*)(long)str[i]);
+ if (i != l - 1)
+ {
+ //Do not react to BLONDE ANSWERS
+ TCHAR ncl = (TCHAR)CharLower((LPTSTR)(void*)(long)str[i + 1]);
+ TCHAR ncu = (TCHAR)CharUpper((LPTSTR)(void*)(long)str[i + 1]);
+ if (ncl != ncu && str[i + 1] == ncu)
+ break;
+ }
+ if (cl != cu)
+ {
+ str[i] = cl;
+ break;
+ }
+ }
+ }
+}
+
+TalkBot::TalkBot(const Mind& goodMind)
+ :mind(goodMind), beSilent(false), makeLowercase(false),
+ understandAlways(false)
+{
+ contactDatas = new PerContactData<Mind, ContactData, void*>(mind);
+}
+
+TalkBot::~TalkBot()
+{
+ delete contactDatas;
+}
+
+tstring TalkBot::GetInitMessage(void* contact)
+{
+ ContactData* d = contactDatas->GetData(contact);
+ tstring s = d->initial.GetString();
+ contactDatas->PutData(contact);
+ return s;
+}
+
+tstring TalkBot::ReplaceAliases(const tstring &message)
+{
+ const TCHAR dividers[] = _T(" \t\n\r,./?\\|;:'\"~!#^&*()_-+=[{]}—\1");
+ tstring sentence = message;
+ tstring result;
+ int len = sentence.length();
+ vector<tstring> words;
+ map<int, tstring> sm;
+ //Find smiles
+ for (size_t i = 0; i < sentence.length() - 1; i++)
+ {
+ unsigned max = sentence.length() - i;
+ if (max > mind.GetData()->maxSmileLen)
+ max = mind.GetData()->maxSmileLen;
+ for (unsigned j = max; j > 0; j--)
+ {
+ tstring item = sentence.substr(i, j);
+ if (mind.GetData()->smiles.find(item)
+ != mind.GetData()->smiles.end())
+ {
+ sm[i] = item;
+ sentence.replace(i, j, _T("\1"));
+ break;
+ }
+ }
+ }
+ len = sentence.length();
+ bool hadQuestionSigns = false;
+ int it = 0;
+ while (it != len)
+ {
+ while (it != len && _tcschr(dividers, sentence[it]))
+ {
+ if (sentence[it] == _T('?'))
+ hadQuestionSigns = true;
+ map<int, tstring>::iterator smit;
+ if (sentence[it] == '\1')
+ {
+ smit = sm.find(it);
+ result.append((*smit).second);
+ }
+ else
+ result.push_back(sentence[it]);
+ it++;
+ }
+ if (it == len)
+ break;
+ int start = it;
+ while (true)
+ {
+ while (it != len && !_tcschr(dividers, sentence[it]))
+ it++;
+ if (it == len || sentence[it] != _T('-'))
+ break;
+ //If we have-a-word-with-minus, we shouldn't split it
+ if (_tcschr(dividers, sentence[it + 1]))
+ break;
+ it += 2;
+ }
+ tstring str = sentence.substr(start, it - start);
+ map<tstring, tstring>::const_iterator al = mind.GetData()->aliases.find(str);
+ if (al != mind.GetData()->aliases.end())
+ result.append((*al).second);
+ else
+ result.append(str);
+ }
+ return result;
+}
+
+tstring TalkBot::AllReplies(const tstring &incomingMessage, ContactData* contactData, Level &maxValue, std::multimap<Level, tstring> &mm)
+{
+ tstring res;
+ //Part 1
+ if (FindExact(contactData, incomingMessage, mind.GetData()->widelyUsed, res)) //widelyUsed
+ {
+ return res;
+ }
+ //Part 2
+ if (FindExact(contactData, incomingMessage, mind.GetData()->study, res)) //study
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(LOOKSLIKE, _T("(study_all) ")+res));
+#else
+ mm.insert(make_pair(LOOKSLIKE, res));
+#endif
+ maxValue = LOOKSLIKE;
+ }
+ //Part 3
+ vector<tstring> sentences;
+ SplitSectences(incomingMessage, sentences);
+ ValueChooser<> ch(sentences, true); //Using random order of sentences.
+ while ((res = ch.GetString()) != _T(""))
+ {
+ //Part 4
+ if (FindExact(contactData, res, mind.GetData()->widelyUsed, res)) //widelyUsed
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(BEST, _T("(widelyused_sent) ")+res));
+#else
+ mm.insert(make_pair(BEST, res));
+#endif
+ if (maxValue > BEST)
+ maxValue = BEST;
+ }
+ //Part 5
+ if (FindExact(contactData, res, mind.GetData()->study, res)) //study
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(LOOKSLIKE, _T("(study_sent) ")+res));
+#else
+ mm.insert(make_pair(LOOKSLIKE, res));
+#endif
+ if (maxValue > LOOKSLIKE)
+ maxValue = LOOKSLIKE;
+ }
+ //Part 6
+ vector<tstring> keywords, otherwords;
+ bool isQuestion;
+ SplitAndSortWords(res, keywords, otherwords, isQuestion);
+ //Part 7, 8
+ res = _T("");
+ FindByKeywords(contactData, keywords, res/*, ures*/, isQuestion); //keywords
+ if (res != _T(""))
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(LOOKSLIKE, _T("(keywords) ")+res));
+#else
+ mm.insert(make_pair(LOOKSLIKE, res));
+#endif
+ if (maxValue > LOOKSLIKE)
+ maxValue = LOOKSLIKE;
+ }
+/* if (ures != _T(""))
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(LOOKSLIKE2, _T("(keywords_unstrict) ")+ures));
+#else
+ mm.insert(make_pair(LOOKSLIKE2, ures));
+#endif
+ if (maxValue > LOOKSLIKE2)
+ maxValue = LOOKSLIKE2;
+ }*/
+ //Part 9
+ if (FindByOthers(contactData, otherwords, res, isQuestion)) //specialEscapes
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(BAD, _T("(otherwords) ")+res));
+#else
+ mm.insert(make_pair(BAD, res));
+#endif
+ if (maxValue > BAD)
+ maxValue = BAD;
+ }
+ }
+ if (!beSilent)
+ {
+ //Part 10
+ if (FindAny(contactData->escape, res)) //escape
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(FAIL, _T("(escape) ") + res));
+#else
+ mm.insert(make_pair(FAIL, res));
+#endif
+ if (maxValue > FAIL)
+ maxValue = FAIL;
+ }
+ //Part 11
+ if (!understandAlways && FindAny(contactData->failure, res)) //failure
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(FAIL, _T("(failure) ") + res));
+#else
+ mm.insert(make_pair(FAIL, res));
+#endif
+ if (maxValue > FAIL)
+ maxValue = FAIL;
+ }
+ }
+ return tstring();
+}
+
+TalkBot::MessageInfo* TalkBot::Reply(void* contact, tstring incomingMessage, bool saveChoice)
+{
+ TCHAR* str = new TCHAR[incomingMessage.length()+1];
+ _tcscpy(str, incomingMessage.c_str());
+ CharLower(str);
+ incomingMessage = str;
+ delete str;
+ ContactData* contactData = contactDatas->GetData(contact);
+
+ if (incomingMessage == contactData->lastMessage && GetTickCount() < contactData->lastMessageTime + 30*60*1000)
+ {
+ MessageInfo *info;
+ //only 2-3 repeats
+ if (contactData->repeatCount < 2 || contactData->repeatCount == 2 && (rand() % 2))
+ {
+ const vector<tstring>& v = mind.GetData()->repeats;
+ tstring res = v[rand() % v.size()];
+#ifdef DEBUG_PREFIXES
+ info = new MessageInfo(incomingMessage, _T("(repeat_norm) ") + res);
+#else
+ info = new MessageInfo(incomingMessage, res);
+#endif
+ }
+ else
+#ifdef DEBUG_PREFIXES
+ info = new MessageInfo(incomingMessage, _T("(repeat_silence)"));
+#else
+ info = new MessageInfo(incomingMessage, _T(""));
+#endif
+ if (saveChoice)
+ RecordAnswer(contactData, *info);
+ contactDatas->PutData(contact);
+ return info;
+ }
+
+ multimap<Level, tstring> mm;
+ Level maxValue = NOTHING;
+
+ tstring res = AllReplies(incomingMessage, contactData, maxValue, mm);
+ if (!res.empty())
+ {
+ UpdateStartChar(res);
+#ifdef DEBUG_PREFIXES
+ MessageInfo *info = new MessageInfo(incomingMessage, _T("(widelyused_all) ") + res);
+#else
+ MessageInfo *info = new MessageInfo(incomingMessage, res);
+#endif
+ if (saveChoice)
+ RecordAnswer(contactData, *info);
+ contactDatas->PutData(contact);
+ return info;
+ }
+
+ incomingMessage = ReplaceAliases(incomingMessage);
+
+ res = AllReplies(incomingMessage, contactData, maxValue, mm);
+ if (!res.empty())
+ {
+ UpdateStartChar(res);
+#ifdef DEBUG_PREFIXES
+ MessageInfo *info = new MessageInfo(incomingMessage, _T("(widelyused_all) ") + res);
+#else
+ MessageInfo *info = new MessageInfo(incomingMessage, res);
+#endif
+ if (saveChoice)
+ RecordAnswer(contactData, *info);
+ contactDatas->PutData(contact);
+ return info;
+ }
+
+ //Also does Part 12
+ tstring final = ChooseResult(contactData, maxValue, mm);
+ MessageInfo *info = new MessageInfo(incomingMessage, final);
+ UpdateStartChar(final);
+ if (saveChoice)
+ RecordAnswer(contactData, *info);
+ contactDatas->PutData(contact);
+ return info;
+}
+
+bool TalkBot::FindExact(ContactData* contactData, const tstring &incomingMessage,
+ const multimap<tstring, tstring>& map, tstring& res)
+{
+ int max = map.count(incomingMessage);
+ if (!max)
+ {
+ TCHAR c = incomingMessage[incomingMessage.length() - 1];
+ if (c != _T('?') && c != _T('.') && c != _T('!'))
+ return FindExact(contactData, incomingMessage + _T('.'), map, res);
+ return false;
+ }
+ pair<mm_cit, mm_cit> range = map.equal_range(incomingMessage);
+ for (mm_cit it = range.first; it != range.second; it++)
+ contactData->chooser.AddChoice((*it).second);
+ res = contactData->chooser.Choose();
+ return true;
+}
+
+void TalkBot::AnswerGiven(void* contact, const TalkBot::MessageInfo& info)
+{
+ ContactData* contactData = contactDatas->GetData(contact);
+ RecordAnswer(contactData, info);
+ contactDatas->PutData(contact);
+}
+
+void TalkBot::RecordAnswer(ContactData *contactData, const TalkBot::MessageInfo& info)
+{
+ contactData->chooser.SaveChoice(info.Answer);
+ if (contactData->lastMessage == info.Question)
+ contactData->repeatCount++;
+ else
+ contactData->repeatCount = 0;
+ contactData->lastMessageTime = GetTickCount();
+ contactData->lastMessage = info.Question;
+}
+
+bool TalkBot::FindAny(ValueChooser<> &ch, tstring& res)
+{
+ if (!ch.GetContainer().size())
+ return false;
+ res = ch.GetString();
+ return true;
+}
+
+void TalkBot::SplitSectences(const tstring &incomingMessage, vector<tstring>& vec)
+{
+ //FIXME: (THINK ABOUT IT:-) )these chars not always mark the end of sentence.
+ const TCHAR symbols[] = _T(".?!");
+ int it = 0, len = incomingMessage.length();
+ while (it != len)
+ {
+ while (it != len && _istspace(incomingMessage[it]))
+ it++;
+ int start = it;
+ while (it != len)
+ {
+ if (_tcschr(symbols, incomingMessage[it++]))
+ {
+ //Test for a :-! smile
+ if (it > 2 && incomingMessage[it-1] == _T('!')
+ && incomingMessage[it-2] == _T('-')
+ && incomingMessage[it-3] == _T(':'))
+ continue;
+ while (it != len && _tcschr(symbols, incomingMessage[it]))
+ it++;
+ break;
+ }
+ }
+ vec.insert(vec.end(), incomingMessage.substr(start, it - start));
+ }
+}
+
+#ifdef _DEBUG
+tstring LevelToStr(TalkBot::Level target)
+{
+ tstring lev;
+ switch (target)
+ {
+ case TalkBot::BEST: lev = _T("BEST(0)"); break;
+ case TalkBot::LOOKSLIKE: lev = _T("LOOKSLIKE(1)"); break;
+ case TalkBot::BAD: lev = _T("BAD(2)"); break;
+ case TalkBot::FAIL: lev = _T("FAIL(3)"); break;
+ case TalkBot::NOTHING: lev = _T("NOTHING(4)"); break;
+ }
+ return lev;
+}
+#endif
+
+tstring TalkBot::ChooseResult(ContactData* contactData, Level maxValue, const multimap<Level, tstring> &mm)
+{
+#ifdef DEBUG_SHOW_VARIANTS
+ AddBotMessage(_T(">>Availabe:"));
+ for (multimap<Level, tstring>::iterator it = mm.begin(); it != mm.end(); it++)
+ AddBotMessage(LevelToStr((*it).first) + _T(": ") + (*it).second);
+ AddBotMessage(_T(">>Result:"));
+#endif
+ if (maxValue == NOTHING)
+ return _T("");
+ Level target = maxValue;
+ int num = mm.count(target);
+/* if (!num)
+ {
+ target = maxValue;
+ num = mm.count(target);
+ }*/
+ typedef multimap<Level, tstring>::const_iterator lt_cit;
+ pair<lt_cit,lt_cit> range = mm.equal_range(target);
+ for (lt_cit it = range.first; it != range.second; it++)
+ contactData->chooser.AddChoice((*it).second);
+#ifdef DEBUG_SHOW_LEVEL
+ tstring lev = LevelToStr(target);
+ return lev + _T(": ") + contactData->chooser.Choose();
+#else
+ return contactData->chooser.Choose();
+#endif
+}
+
+void TalkBot::FindByKeywords(ContactData* contactData, const vector<tstring> &keywords, tstring& res/*, tstring& ures*/,
+ bool isQuestion)
+{
+ if (keywords.size() == 0)
+ return;
+ const multimap<WordsList, tstring> &keys = isQuestion ? mind.GetData()->qkeywords :
+ mind.GetData()->keywords;
+ for (multimap<WordsList, tstring>::const_iterator it = keys.begin(); it != keys.end(); it++)
+ {
+ float prio;
+ if ((*it).first.MatchesAll(keywords/*, strict*/, prio))
+#ifdef DEBUG_SHOW_SOLUTION_REASON
+ contactData->chooser.AddChoice((tstring)(*it).first + _T(": - ") + (*it).second, prio);
+#else
+ contactData->chooser.AddChoice((*it).second, prio);
+#endif
+ }
+ res = contactData->chooser.Choose();
+}
+
+bool TalkBot::FindByOthers(ContactData* contactData, const vector<tstring> &otherwords, tstring& res, bool isQuestion)
+{
+ //vector<tstring> results;
+ const multimap<WordsList, tstring> &specs = isQuestion ? mind.GetData()->qspecialEscapes :
+ mind.GetData()->specialEscapes;
+ for (multimap<WordsList, tstring>::const_iterator it = specs.begin();
+ it != specs.end(); it++)
+ if ((*it).first.MatchesAny(otherwords))
+ {
+#ifdef DEBUG_SHOW_SOLUTION_REASON
+ contactData->chooser.AddChoice((tstring)(*it).first + _T(": - ") + (*it).second);
+#else
+ contactData->chooser.AddChoice((*it).second);
+#endif
+ }
+ res = contactData->chooser.Choose();
+ if (res.empty())
+ return false;
+ return true;
+}
+
+const Mind& TalkBot::GetMind() const
+{
+ return mind;
+}
+
+void TalkBot::SplitAndSortWords(tstring sentence, vector<tstring>& keywords,
+ vector<tstring>& otherwords, bool& isQuestion)
+{
+ const TCHAR dividers[] = _T(" \t\n\r,./?\\|;:'\"~!#^&*()_-+=[{]}—");
+ int len = sentence.length();
+ vector<tstring> words;
+ map<int, tstring> sm;
+ //Find smiles
+ for (size_t i = 0; i < sentence.length() - 1; i++)
+ {
+ unsigned max = sentence.length() - i;
+ if (max > mind.GetData()->maxSmileLen)
+ max = mind.GetData()->maxSmileLen;
+ for (unsigned j = max; j > 0; j--)
+ {
+ tstring item = sentence.substr(i, j);
+ if (mind.GetData()->smiles.find(item)
+ != mind.GetData()->smiles.end())
+ {
+ sm[i] = item;
+ sentence.replace(i, j, _T(" "));
+ break;
+ }
+ }
+ }
+ len = sentence.length();
+ bool hadQuestionSigns = false;
+ int it = 0;
+ while (it != len)
+ {
+ while (it != len && _tcschr(dividers, sentence[it]))
+ {
+ if (sentence[it] == _T('?'))
+ hadQuestionSigns = true;
+ map<int, tstring>::iterator smit;
+ if (_istspace(sentence[it]) && (smit = sm.find(it)) != sm.end())
+ words.push_back((*smit).second);
+ it++;
+ }
+ if (it == len)
+ break;
+ hadQuestionSigns = false;
+ int start = it;
+ while (true)
+ {
+ while (it != len && !_tcschr(dividers, sentence[it]))
+ it++;
+ if (it == len || sentence[it] != _T('-'))
+ break;
+ //If we have-a-word-with-minus, we shouldn't split it
+ if (_tcschr(dividers, sentence[it + 1]))
+ break;
+ it += 2;
+ }
+ tstring str = sentence.substr(start, it - start);
+ words.push_back(str);
+ }
+ isQuestion = hadQuestionSigns;
+ for (vector<tstring>::iterator it = words.begin(); it != words.end(); it++)
+ {
+ if (!isQuestion)
+ {
+ if (mind.GetData()->question.find(*it) != mind.GetData()->question.end())
+ isQuestion = true;
+ }
+ if (mind.GetData()->special.find(*it) != mind.GetData()->special.end())
+ otherwords.push_back(*it);
+#ifdef EXCLUDE_SPECIAL_WORDS
+ else
+#endif
+ keywords.push_back(*it);
+ }
+}
+
+void TalkBot::SetSilent(const bool isSilent)
+{
+ beSilent = isSilent;
+}
+
+void TalkBot::SetLowercase(const bool isLowercase)
+{
+ makeLowercase = isLowercase;
+}
+
+void TalkBot::SetUnderstandAlways(const bool understandAlways)
+{
+ this->understandAlways = understandAlways;
+} \ No newline at end of file
diff --git a/plugins/Boltun/Engine/TalkEngine.h b/plugins/Boltun/Engine/TalkEngine.h
new file mode 100644
index 0000000000..364f4193d1
--- /dev/null
+++ b/plugins/Boltun/Engine/TalkEngine.h
@@ -0,0 +1,112 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef TalkEngineH
+#define TalkEngineH
+
+#include "Mind.h"
+#include "ValueChooser.h"
+#include "PerContactData.h"
+#include "UnrecentChooser.h"
+
+class TalkBot
+{
+public:
+ struct MessageInfo
+ {
+ private:
+ tstring Question;
+ MessageInfo(tstring q, tstring a)
+ :Question(q), Answer(a)
+ {
+ }
+ public:
+ tstring Answer;
+ MessageInfo(tstring q)
+ :Question(q)
+ {
+ }
+ friend class TalkBot;
+ };
+private:
+ typedef enum
+ {
+ BEST, LOOKSLIKE/*, LOOKSLIKE2*/, BAD, FAIL, NOTHING
+ } Level;
+ friend tstring LevelToStr(TalkBot::Level target);
+
+ struct ContactData
+ {
+ ValueChooser<> initial;
+ //ValueChooser<std::set<tstring> > question;
+ //ValueChooser<std::set<tstring> > special;
+ ValueChooser<> escape;
+ ValueChooser<> failure;
+ UnRecentChooser chooser;
+ tstring lastMessage;
+ long long lastMessageTime;
+ int repeatCount;
+ inline ContactData(const Mind& mind)
+ :initial(mind.GetData()->initial),
+ //question(mind.GetData()->question),
+ //special(mind.GetData()->special),
+ escape(mind.GetData()->escape),
+ failure(mind.GetData()->failure),
+ repeatCount(0)
+ {
+ }
+ };
+
+ PerContactData<Mind, ContactData, void*>* contactDatas;
+ const Mind mind;
+ bool beSilent;
+ bool makeLowercase;
+ bool understandAlways;
+ void UpdateStartChar(tstring& str);
+ typedef std::multimap<tstring, tstring>::const_iterator mm_cit;
+ bool FindExact(ContactData* contactData, const tstring &incomingMessage,
+ const std::multimap<tstring, tstring>& map, tstring& res);
+ bool FindAny(ValueChooser<> &ch, tstring& res);
+ void FindByKeywords(ContactData* contactData, const std::vector<tstring> &keywords, tstring& res/*, tstring& ures*/, bool isQuestion);
+ bool FindByOthers(ContactData* contactData, const std::vector<tstring> &otherwords, tstring& res, bool isQuestion);
+ tstring AllReplies(const tstring &incomingMessage, ContactData* contactData, Level &maxValue, std::multimap<Level, tstring> &mm);
+ tstring ReplaceAliases(const tstring &message);
+ tstring ChooseResult(ContactData* contactData, Level maxValue, const std::multimap<Level, tstring> &mm);
+ void RecordAnswer(ContactData *contactData, const TalkBot::MessageInfo& info);
+#ifdef _DEBUG
+public:
+#endif
+ void SplitSectences(const tstring &incomingMessage, std::vector<tstring>& vec);
+ void SplitAndSortWords(tstring sentence, std::vector<tstring>& keywords,
+ std::vector<tstring>& otherwords, bool& isQuestion);
+public:
+ TalkBot(const Mind& goodMind);
+ ~TalkBot();
+ const Mind& GetMind() const;
+ void SetSilent(const bool isSilent);
+ void SetLowercase(const bool isLowercase);
+ void SetUnderstandAlways(const bool understandAlways);
+ //const MindData *GetData();
+ tstring GetInitMessage(void* contact);
+ MessageInfo* Reply(void* contact, const tstring incomingMessage, bool saveChoice);
+ void AnswerGiven(void* contact, const MessageInfo& info);
+};
+
+#endif
diff --git a/plugins/Boltun/Engine/UnrecentChooser.cpp b/plugins/Boltun/Engine/UnrecentChooser.cpp
new file mode 100644
index 0000000000..069d84f377
--- /dev/null
+++ b/plugins/Boltun/Engine/UnrecentChooser.cpp
@@ -0,0 +1,100 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#include "UnrecentChooser.h"
+
+using namespace std;
+
+UnRecentChooser::UnRecentChooser()
+ :last(-1), min(-1), newItemsPrio(-1), maxOldPrio(-1)
+{
+}
+
+void UnRecentChooser::AddChoice(tstring value, float prio)
+{
+ if (items.count(value) != 0)
+ {
+ int val = items[value];
+ oldItems.insert(make_pair(val, value));
+ oldPrios.insert(make_pair(value, prio));
+ if (min > val || min == -1)
+ min = val;
+ if (maxOldPrio < prio)
+ maxOldPrio = prio;
+ }
+ else
+ {
+ if (prio > newItemsPrio)
+ {
+ newItems.push_back(value);
+ newItemsPrio = prio;
+ }
+ }
+}
+
+tstring UnRecentChooser::Choose()
+{
+ tstring res;
+ //Find answer
+ if (newItemsPrio != -1)
+ {
+ int num = rand() % newItems.size();
+ res = newItems[num];
+ }
+ else
+ if (min == -1)
+ res = _T("");
+ else
+ {
+ float minprio = maxOldPrio / 1.5F;
+ while (oldPrios[oldItems[min]] < minprio)
+ min++;
+ res = oldItems[min];
+ }
+ //Clean items
+ min = -1;
+ newItemsPrio = -1;
+ maxOldPrio = -1;
+ oldItems.clear();
+ oldPrios.clear();
+ newItems.clear();
+ return res;
+}
+
+void UnRecentChooser::SaveChoice(tstring choice)
+{
+ //Add answer
+ if (items.find(choice) != items.end())
+ {
+ for (vector<tstring>::iterator it = itemsList.begin(); it != itemsList.end(); it++)
+ if (*it == choice)
+ {
+ itemsList.erase(it);
+ break;
+ }
+ }
+ items[choice] = ++last;
+ itemsList.push_back(choice);
+ if (itemsList.size() > maxItems)
+ {
+ items.erase(*itemsList.begin());
+ itemsList.erase(itemsList.begin());
+ }
+} \ No newline at end of file
diff --git a/plugins/Boltun/Engine/UnrecentChooser.h b/plugins/Boltun/Engine/UnrecentChooser.h
new file mode 100644
index 0000000000..91a961e307
--- /dev/null
+++ b/plugins/Boltun/Engine/UnrecentChooser.h
@@ -0,0 +1,47 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef UnRecentChooserH
+#define UnRecentChooserH
+
+#include <map>
+#include <vector>
+#include <string>
+#include "tstring.h"
+
+class UnRecentChooser
+{
+ std::map<tstring, size_t> items;
+ std::vector<tstring> itemsList;
+ std::vector<tstring> newItems;
+ float newItemsPrio, maxOldPrio;
+ std::map<size_t, tstring> oldItems;
+ std::map<tstring, float> oldPrios;
+ size_t last;
+ int min;
+ static const size_t maxItems = 100;
+public:
+ UnRecentChooser();
+ void AddChoice(tstring value, float prio = 1.0);
+ tstring Choose();
+ void SaveChoice(tstring choice);
+};
+
+#endif /* UnRecentChooserH */ \ No newline at end of file
diff --git a/plugins/Boltun/Engine/ValueChooser.h b/plugins/Boltun/Engine/ValueChooser.h
new file mode 100644
index 0000000000..3fb1510609
--- /dev/null
+++ b/plugins/Boltun/Engine/ValueChooser.h
@@ -0,0 +1,88 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef StringChooserH
+#define StringChooserH
+
+#include <vector>
+#include <string>
+
+template<class container = std::vector<tstring> >
+class ValueChooser
+{
+private:
+ int freeNumbers;
+ bool *numbers;
+ const container data;
+ bool notifyOnReset;
+public:
+ ValueChooser(const container& vec, bool NotifyOnReset = false)
+ :data(vec), notifyOnReset(NotifyOnReset)
+ {
+ //randomize();
+ numbers = NULL;
+ UpdateLength();
+ }
+
+ ~ValueChooser()
+ {
+ delete numbers;
+ }
+
+ void UpdateLength()
+ {
+ delete numbers; //normal if numbers == NULL
+ numbers = new bool[data.size()];
+ Reset();
+ }
+
+ void Reset()
+ {
+ for (size_t i = 0; i < data.size(); i++)
+ numbers[i] = false;
+ freeNumbers = data.size();
+ }
+
+ typename container::value_type GetString()
+ {
+ if (!freeNumbers)
+ {
+ Reset();
+ if (notifyOnReset)
+ return _T("");
+ }
+ int result;
+ while (numbers[result = rand() % data.size()])
+ ;
+ freeNumbers--;
+ numbers[result] = true;
+ return data[result];
+ }
+
+ container GetContainer() const;
+};
+
+template<class container>
+container ValueChooser<container>::GetContainer() const
+{
+ return data;
+}
+
+#endif
diff --git a/plugins/Boltun/Engine/WordsList.cpp b/plugins/Boltun/Engine/WordsList.cpp
new file mode 100644
index 0000000000..5e5d0c37a5
--- /dev/null
+++ b/plugins/Boltun/Engine/WordsList.cpp
@@ -0,0 +1,169 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#include "WordsList.h"
+
+using namespace std;
+
+WordsList::WordsList(const tstring &data/*, bool allowUnstrict*/)
+{
+ Parse(data/*, allowUnstrict*/);
+}
+
+WordsList::operator tstring() const
+{
+ tstring res;
+ //if (unstrict)
+ // res = _T("~");
+ set<tstring>::const_iterator it = words.begin();
+ if (!words.empty())
+ while (true)
+ {
+ res += *it;
+ it++;
+ if (it != words.end())
+ res += _T(" ");
+ else
+ break;
+ }
+ res += isQuestion ? _T("?") : _T(".");
+ return res;
+}
+
+WordsList& WordsList::operator= (const tstring& s)
+{
+ Parse(s);
+ return *this;
+};
+
+void WordsList::Parse(tstring s/*, bool allowUnstrict*/)
+{
+ isQuestion = false;
+ /*if (allowUnstrict && s.length() && s[0] == _T('~'))
+ {
+ s = s.substr(1, s.npos);
+ unstrict = true;
+ }
+ else
+ unstrict = false;*/
+ int len = s.length() - 1;
+ while (len != -1 && _istspace(s[len]))
+ len--;
+ if (len < 0)
+ return;
+ if (s[len] == '?')
+ isQuestion = true;
+ else
+ if (s[len] != '.')
+ return;
+ s.resize(len);
+ int it = 0;
+ while (it != len)
+ {
+ while (it != len && _istspace(s[it]))
+ it++;
+ if (it == len)
+ break;
+ int start = it;
+ while (it != len && !_istspace(s[it]))
+ it++;
+ words.insert(s.substr(start, it - start));
+ }
+}
+
+bool WordsList::MatchesAll(const vector<tstring>& s/*, bool& WasStrict*/, float& priority) const
+{
+ std::set<tstring> temp;
+ //WasStrict = true;
+ for (vector<tstring>::const_iterator it = s.begin(); it != s.end(); it++)
+ {
+/* if (words.find(*it) == words.end())
+ if (unstrict)
+ {
+ WasStrict = false;
+ continue;
+ }
+ else
+ return false;
+ temp.insert((*it));*/
+ if (words.find(*it) != words.end())
+ temp.insert((*it));
+ }
+ if (temp.size() != words.size())
+ return false;
+ priority = words.size() * words.size() / (float)s.size();
+ return temp.size() == words.size();
+}
+
+bool WordsList::MatchesAny(const vector<tstring>& s) const
+{
+ for (vector<tstring>::const_iterator it = s.begin(); it != s.end(); it++)
+ if (words.find(*it) != words.end())
+ return true;
+ return false;
+}
+
+vector<tstring> WordsList::ConsistsOf(const set<tstring>& list) const
+{
+ vector<tstring> res;
+ for (set<tstring>::const_iterator it = words.begin(); it != words.end(); it++)
+ if (list.find(*it) == list.end())
+ res.push_back(*it);
+ return res;
+}
+
+vector<tstring> WordsList::DoesntIncludeAny(const set<tstring>& list) const
+{
+ vector<tstring> res;
+ for (set<tstring>::const_iterator it = words.begin(); it != words.end(); it++)
+ if (list.find(*it) != list.end())
+ res.push_back(*it);
+ return res;
+}
+
+bool WordsList::operator<(const WordsList& value) const
+{
+ return (tstring)*this < (tstring)value;
+}
+
+bool WordsList::operator!=(const WordsList& value) const
+{
+ return (tstring)*this != (tstring)value;
+}
+
+bool WordsList::operator==(const WordsList& value) const
+{
+ return (tstring)*this == (tstring)value;
+}
+
+size_t WordsList::Size() const
+{
+ return words.size();
+}
+
+bool WordsList::IsQuestion() const
+{
+ return isQuestion;
+}
+
+bool WordsList::IsEmpty() const
+{
+ return words.size() == 0;
+} \ No newline at end of file
diff --git a/plugins/Boltun/Engine/WordsList.h b/plugins/Boltun/Engine/WordsList.h
new file mode 100644
index 0000000000..bd74b2007b
--- /dev/null
+++ b/plugins/Boltun/Engine/WordsList.h
@@ -0,0 +1,56 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef WordsListH
+#define WordsListH
+
+#include <set>
+#include <vector>
+#include <string>
+#include "tchar.h"
+#include "tstring.h"
+
+class WordsList
+{
+ WordsList();
+ void Parse(tstring s/*, bool allowUnstrict = false*/);
+ //bool unstrict;
+#ifdef _DEBUG
+public:
+#endif
+ std::set<tstring> words;
+ bool isQuestion;
+public:
+ WordsList(const tstring &data/*, bool allowUnstrict = false*/);
+ operator tstring() const;
+ bool MatchesAll(const std::vector<tstring>& s/*, bool& WasStrict*/, float& priority) const;
+ bool MatchesAny(const std::vector<tstring>& s) const;
+ std::vector<tstring> ConsistsOf(const std::set<tstring>& list) const;
+ std::vector<tstring> DoesntIncludeAny(const std::set<tstring>& list) const;
+ WordsList& operator= (const tstring& s);
+ bool operator<(const WordsList& value) const;
+ bool operator==(const WordsList& value) const;
+ bool operator!=(const WordsList& value) const;
+ bool IsQuestion() const;
+ bool IsEmpty() const;
+ size_t Size() const;
+};
+
+#endif
diff --git a/plugins/Boltun/Engine/boltun.mindw b/plugins/Boltun/Engine/boltun.mindw
new file mode 100644
index 0000000000..a6f9f2103d
--- /dev/null
+++ b/plugins/Boltun/Engine/boltun.mindw
Binary files differ
diff --git a/plugins/Boltun/Engine/tstring.h b/plugins/Boltun/Engine/tstring.h
new file mode 100644
index 0000000000..e21984a12c
--- /dev/null
+++ b/plugins/Boltun/Engine/tstring.h
@@ -0,0 +1,34 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef TSTRING_H
+#define TSTRING_H
+
+#include <tchar.h>
+#include <string>
+
+#ifndef TCHAR
+#define TCHAR _TCHAR
+#endif
+
+typedef std::basic_string<TCHAR, std::char_traits<TCHAR>,
+ std::allocator<TCHAR> > tstring;
+
+#endif
diff --git a/plugins/Boltun/TODO.txt b/plugins/Boltun/TODO.txt
new file mode 100644
index 0000000000..6ea2151d2d
--- /dev/null
+++ b/plugins/Boltun/TODO.txt
@@ -0,0 +1,35 @@
+TODO list:
+
+--------------------------------------------------------
+Release-critical:
+
+- Add study capability to engine
+
+Done:
+- Add reaction for duplicate phrases.
+- Add aliases.
+- Check for critical section double enter
+- Multicontact talk support in engine
+- Add "Start talking" option to cantact menu.
+- Check bot warning.
+- Add "be silent, not stupid" to options
+- Add start answers with a little letter.
+- Add README about bot algorithm.
+- Add README for mind file format.
+- Add "don't use "don't understand"" option
+- Division between question and non-question sentenses
+- Empty keywords or specials should not be loaded
+- Check "каким-либо"
+- Check :-) on "ок"
+- Add answer-on status info to the readme.
+- Make AI options dependent.
+- Update mind.txt with UpdateStartChar
+- Sort keywords matches by priority
+- Use chooser everywhere
+
+--------------------------------------------------------
+Future:
+- Speed-up loading
+- Load mind from UTF8 files
+- Expert mode support
+- Add different choises of reaction for message series.
diff --git a/plugins/Boltun/actionQueue.cpp b/plugins/Boltun/actionQueue.cpp
new file mode 100644
index 0000000000..7040778c85
--- /dev/null
+++ b/plugins/Boltun/actionQueue.cpp
@@ -0,0 +1,256 @@
+//***********************************************************
+// Copyright 2003-2008 Alexander S. Kiselev, Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#include "actionQueue.h"
+#include "config.h"
+#include "boltun.h"
+#include "Engine/tstring.h"
+#include "Engine/TalkEngine.h"
+
+#include <list>
+#include <set>
+#include <time.h>
+#include "Engine/CriticalSection.h"
+#ifdef _DEBUG
+#include <assert.h>
+#endif
+
+#define MIRANDA_VER 0x0700
+#include "newpluginapi.h"
+#include "m_database.h"
+#include "m_system.h"
+#include "m_protosvc.h"
+
+using namespace std;
+
+extern TalkBot* bot;
+
+typedef void (*ActionHandler)(HANDLE hContact, const TalkBot::MessageInfo *inf);
+
+typedef struct _QueueElement {
+ HANDLE hContact;
+ const TalkBot::MessageInfo *inf;
+ ActionHandler Handler;
+ bool Sticky;
+ int TimeOffset;
+ _QueueElement(HANDLE contact, ActionHandler handler, int timeOffset, const TalkBot::MessageInfo *info = NULL, bool sticky = false)
+ :hContact(contact), Handler(handler), TimeOffset(timeOffset), inf(info), Sticky(sticky)
+ {
+ }
+} QueueElement;
+
+static list<QueueElement> actionQueue;
+static set<HANDLE> typingContacts;
+UINT_PTR timerID = 0;
+
+CriticalSection cs;
+CriticalSection typingContactsLock;
+
+void UpdateTimer();
+
+VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ cs.Enter();
+ QueueElement q = actionQueue.front();
+ actionQueue.pop_front();
+ UpdateTimer();
+ cs.Leave();
+ q.Handler(q.hContact, q.inf);
+}
+
+void UpdateTimer()
+{
+ if (timerID)
+ KillTimer(NULL, timerID);
+ if (actionQueue.size())
+ timerID = SetTimer(NULL, 0, actionQueue.front().TimeOffset, TimerProc);
+ else
+ timerID = 0;
+}
+
+static bool NotifyTyping(HANDLE hContact)
+{
+ int res = DBGetContactSettingByte(hContact, "SRMsg", "SupportTyping", 2);
+ if (res == 2)
+ res = DBGetContactSettingByte(NULL, "SRMsg", "DefaultTyping", 1);
+ return res != 0;
+}
+
+static char *MsgServiceName(HANDLE hContact)
+{
+#ifdef _UNICODE
+ char szServiceName[100];
+ char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (szProto == NULL)
+ return PSS_MESSAGE;
+
+ mir_snprintf(szServiceName, sizeof(szServiceName), "%s%sW", szProto, PSS_MESSAGE);
+ if (ServiceExists(szServiceName))
+ return PSS_MESSAGE "W";
+#endif
+ return PSS_MESSAGE;
+}
+
+static void TimerAnswer(HANDLE hContact, const TalkBot::MessageInfo* info)
+{
+ DBEVENTINFO ldbei;
+ int size = info->Answer.length() + 1;
+ int bufsize = size;
+ char* msg;
+#ifdef UNICODE
+ bufsize *= sizeof(TCHAR) + 1;
+ msg = new char[bufsize];
+ //msg[size - 1] = '\0';
+
+ if (!WideCharToMultiByte(CP_ACP, 0, info->Answer.c_str(), -1, msg, size,
+ NULL, NULL))
+ FillMemory(msg, size - 1, '-'); //In case of fault return "----" in ANSI part
+ CopyMemory(msg + size, info->Answer.c_str(), size * 2);
+#else
+ msg = respItem->szMes;
+#endif
+
+ CallContactService(hContact, MsgServiceName(hContact), PREF_TCHAR, (LPARAM)msg);
+
+ ZeroMemory(&ldbei, sizeof(ldbei));
+ ldbei.cbSize = sizeof(ldbei);
+ //FIXME: Error may happen
+ ldbei.cbBlob = bufsize;
+ ldbei.pBlob = (PBYTE)(void*)msg;
+ ldbei.eventType = EVENTTYPE_MESSAGE;
+ ldbei.flags = DBEF_SENT;
+ ldbei.szModule = BOLTUN_NAME;
+ ldbei.timestamp = (DWORD)time(NULL);
+
+ CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&ldbei);
+ bot->AnswerGiven(hContact, *info);
+ delete info;
+#ifdef UNICODE
+ delete msg;
+#endif
+ typingContactsLock.Enter();
+ typingContacts.erase(hContact);
+ typingContactsLock.Leave();
+}
+
+static void StartTyping(HANDLE hContact, const TalkBot::MessageInfo*)
+{
+ CallService(MS_PROTO_SELFISTYPING, (WPARAM)hContact,
+ (LPARAM)PROTOTYPE_SELFTYPING_ON);
+ typingContactsLock.Enter();
+ typingContacts.insert(hContact);
+ typingContactsLock.Leave();
+}
+
+void DoAnswer(HANDLE hContact, const TalkBot::MessageInfo *info, bool sticky = false)
+{
+ if (info->Answer[0] == _T('\0'))
+ return;
+ int waitTime, thinkTime = 0;
+ int defWaitTime = Config.AnswerPauseTime * 1000;
+ if (Config.PauseDepends)
+ waitTime = defWaitTime * info->Answer.length() / 25;
+ else
+ waitTime = defWaitTime;
+ if (Config.PauseRandom)
+ {
+ //Let it be up to 4 times longer.
+ waitTime = waitTime * (rand() % 300) / 100 + waitTime;
+ }
+ if (waitTime == 0)
+ waitTime = 50; //it's essential, because otherwise message will be added later
+ //then its response, that will cause incorrect ordering of
+ //messages in the opened history (reopening will
+ //help, but anyway it's no good)
+ if (NotifyTyping(hContact) && Config.AnswerThinkTime)
+ {
+ thinkTime = Config.AnswerThinkTime * 1000;
+ if (Config.PauseRandom)
+ {
+ //Let it be up to 4 times longer.
+ thinkTime = thinkTime * (rand() % 300) / 100 + thinkTime;
+ }
+ }
+ cs.Enter();
+ //Check if this contact's timer handler is now waiting for a cs.
+ bool needTimerRearrange = false;
+ if (!actionQueue.empty() && actionQueue.front().hContact == hContact)
+ {
+ needTimerRearrange = true;
+ KillTimer(NULL, timerID);
+ cs.Leave();
+ cs.Enter();
+ }
+ if (!actionQueue.empty())
+ {
+ list<QueueElement>::iterator it = actionQueue.end();
+ it--;
+ while (true)
+ {
+ if ((*it).hContact == hContact)
+ {
+ if ((*it).Sticky)
+ break;
+ list<QueueElement>::iterator tmp = it;
+ if (tmp != actionQueue.begin())
+ tmp--;
+ actionQueue.erase(it);
+ it = tmp;
+ if (actionQueue.empty())
+ break;
+ }
+ if (it == actionQueue.begin())
+ break;
+ it--;
+ }
+ }
+ typingContactsLock.Enter();
+ if (typingContacts.find(hContact) != typingContacts.end())
+ {
+ CallService(MS_PROTO_SELFISTYPING, (WPARAM)hContact, (LPARAM)PROTOTYPE_SELFTYPING_OFF);
+ typingContacts.erase(hContact);
+ }
+ typingContactsLock.Leave();
+ if (actionQueue.empty())
+ needTimerRearrange = true;
+ if (thinkTime)
+ actionQueue.push_back(QueueElement(hContact, StartTyping, thinkTime, NULL, sticky));
+ actionQueue.push_back(QueueElement(hContact, TimerAnswer, waitTime, info, sticky));
+ if (needTimerRearrange)
+ UpdateTimer();
+ cs.Leave();
+}
+
+void AnswerToContact(HANDLE hContact, const TCHAR* messageToAnswer)
+{
+ if (Config.TalkWarnContacts && DBGetContactSettingByte(hContact, BOLTUN_KEY,
+ DB_CONTACT_WARNED, FALSE) == FALSE)
+ {
+ DoAnswer(hContact, new TalkBot::MessageInfo((const TCHAR*)Config.WarnText), true);
+ DBWriteContactSettingByte(hContact, BOLTUN_KEY, DB_CONTACT_WARNED, TRUE);
+ }
+ else
+ DoAnswer(hContact, bot->Reply(hContact, messageToAnswer, false));
+}
+
+void StartChatting(HANDLE hContact)
+{
+ DoAnswer(hContact, new TalkBot::MessageInfo(bot->GetInitMessage(hContact)), true);
+}
diff --git a/plugins/Boltun/actionQueue.h b/plugins/Boltun/actionQueue.h
new file mode 100644
index 0000000000..78f6ce5f9e
--- /dev/null
+++ b/plugins/Boltun/actionQueue.h
@@ -0,0 +1,30 @@
+//***********************************************************
+// Copyright © 2003-2008 Alexander S. Kiselev, Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef ACTIONQUEUE_H
+#define ACTIONQUEUE_H
+
+#include <windows.h>
+
+void AnswerToContact(HANDLE hContact, const TCHAR* messageToAnswer);
+
+void StartChatting(HANDLE hContact);
+
+#endif /* ACTIONQUEUE_H */ \ No newline at end of file
diff --git a/plugins/Boltun/boltun.cpp b/plugins/Boltun/boltun.cpp
new file mode 100644
index 0000000000..e0f85661b0
--- /dev/null
+++ b/plugins/Boltun/boltun.cpp
@@ -0,0 +1,718 @@
+//***********************************************************
+// Copyright © 2003-2008 Alexander S. Kiselev, Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#define MIRANDA_VER 0x0700
+
+#include "Engine/TalkEngine.h"
+
+#include <windows.h>
+#include <time.h>
+#include <string>
+
+#include "newpluginapi.h"
+#include "m_clist.h"
+#include "m_skin.h"
+#include "m_database.h"
+#include "m_system.h"
+#include "m_protosvc.h"
+#include "m_options.h"
+#include "m_langpack.h"
+#include "resource.h"
+
+#include "resource.h"
+
+#include "boltun.h"
+#include "config.h"
+#include "actionQueue.h"
+
+//#define DEBUG_LOAD_TIME
+
+struct MM_INTERFACE mmi;
+
+TalkBot* bot = NULL;
+
+
+#define MAX_WARN_TEXT 1024
+#define MAX_MIND_FILE 1024
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+BOOL blInit = FALSE;
+UINT pTimer = 0;
+TCHAR *path;
+
+PLUGININFO pluginInfo={
+ sizeof(PLUGININFO),
+ BOLTUN_NAME,
+ PLUGIN_MAKE_VERSION(0,0,3,0),
+ PLUGIN_DESCRIPTION,
+ "Alexander S. Kiselev, Valentin Pavlyuchenko",
+ "Valentin.Pavlyuchenko@gmail.com",
+ "© 2003-2008 Alexander S. Kiselev A.K.A. KAS, Valentin Pavlyuchenko",
+ "http://miranda-im.org",
+ UNICODE_AWARE,
+ 0
+};
+
+static HANDLE hEventDbEventAdded;
+static HANDLE hEventOptInitialise;
+static HANDLE hEventPrebuild;
+static HANDLE hMenuItemAutoChat;
+static HANDLE hMenuItemNotToChat;
+static HANDLE hMenuItemStartChatting;
+
+#ifdef UNICODE
+#define MIND_DIALOG_FILTER _T("%s (*.mindw)\1*.mindw\1%s (*.*)\1*.*\1")
+#else
+#define MIND_DIALOG_FILTER "%s (*.mind)\1*.mind\1%s (*.*)\1*.*\1"
+#endif
+
+#ifdef DEBUG_LOAD_TIME
+#include <intrin.h>
+#endif
+
+void UpdateEngine()
+{
+ if (bot)
+ {
+ bot->SetSilent(Config.EngineStaySilent);
+ bot->SetLowercase(Config.EngineMakeLowerCase);
+ bot->SetUnderstandAlways(Config.EngineUnderstandAlways);
+ }
+}
+
+TCHAR* GetFullName(const TCHAR* filename)
+{
+ size_t flen = _tcslen(filename);
+ TCHAR* fullname = const_cast<TCHAR*>(filename);
+ if (!_tcschr(filename, _T(':')))
+ {
+ size_t plen = _tcslen(path);
+ fullname = new TCHAR[plen+flen+1];
+ fullname[0] = NULL;
+ _tcscat(fullname, path);
+ _tcscat(fullname, filename);
+ }
+ return fullname;
+}
+
+static bool LoadMind(const TCHAR* filename, int &line)
+{
+ TCHAR* fullname = GetFullName(filename);
+ HCURSOR newCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));
+ HCURSOR oldCur = SetCursor(newCur);
+#ifdef DEBUG_LOAD_TIME
+ unsigned __int64 t = __rdtsc();
+#endif
+ Mind* mind = new Mind();
+ line = -1;
+ try
+ {
+ mind->Load(fullname);
+ }
+ catch(Mind::CorruptedMind c)
+ {
+ line = c.line;
+ delete mind;
+ if (fullname != filename)
+ delete[] fullname;
+ SetCursor(oldCur);
+ return false;
+ }
+ catch(...)
+ {
+ delete mind;
+ if (fullname != filename)
+ delete[] fullname;
+ SetCursor(oldCur);
+ return false;
+ }
+ if (fullname != filename)
+ delete[] fullname;
+
+#ifdef DEBUG_LOAD_TIME
+ t = __rdtsc() - t;
+ char dest[101];
+ sprintf_s(dest, 100, "%I64d ticks\n", t / 3200000);
+ MessageBoxA(NULL, dest, NULL, 0);
+ //exit(0);
+#endif
+ SetCursor(oldCur);
+ HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(IDR_SMILES), _T("SMILES"));
+ if (!hRes)
+ {
+ delete mind;
+ return false;
+ }
+ DWORD size = SizeofResource(hInst, hRes);
+ if (!size)
+ {
+ delete mind;
+ return false;
+ }
+ HGLOBAL hGlob = LoadResource(hInst, hRes);
+ if (!hGlob)
+ {
+ delete mind;
+ return false;
+ }
+ void *data = LockResource(hGlob);
+ if (!data)
+ {
+ FreeResource(hGlob);
+ delete mind;
+ return false;
+ }
+ bool res = true;
+ try
+ {
+ mind->LoadSmiles(data, size);
+ }
+ catch(...)
+ {
+ res = false;
+ }
+ UnlockResource(data);
+ FreeResource(hGlob);
+ if (!res)
+ {
+ delete mind;
+ return false;
+ }
+ if (bot)
+ delete bot;
+ bot = new TalkBot(*mind);
+ delete mind;
+ UpdateEngine();
+ return true;
+}
+
+/*static bool SaveMind(const TCHAR* filename)
+{
+ if (!bot)
+ return false;
+ bot->GetMind().Save(filename);
+ return true;
+}*/
+
+static bool BoltunAutoChat(HANDLE hContact)
+{
+ if (DBGetContactSettingByte(hContact, BOLTUN_KEY, DB_CONTACT_BOLTUN_NOT_TO_CHAT
+ , FALSE) == TRUE)
+ return false;
+
+ if (Config.TalkWithEverybody)
+ return true;
+
+ if (Config.TalkEveryoneWhileAway)
+ {
+ int status = CallService(MS_CLIST_GETSTATUSMODE, 0, 0);
+ if (status == ID_STATUS_AWAY ||
+ status == ID_STATUS_DND ||
+ status == ID_STATUS_NA ||
+ status == ID_STATUS_OCCUPIED ||
+ status == ID_STATUS_ONTHEPHONE ||
+ status == ID_STATUS_OUTTOLUNCH)
+ return true;
+ }
+
+ if ((DBGetContactSettingByte(hContact,"CList","NotOnList",0) == 1) &&
+ Config.TalkWithNotInList)
+ return true;
+
+ if (DBGetContactSettingByte(hContact, BOLTUN_KEY, DB_CONTACT_BOLTUN_AUTO_CHAT,
+ FALSE) == TRUE)
+ return true;
+
+ return false;
+}
+
+static int MessageEventAdded(WPARAM wParam, LPARAM lParam)
+{
+ //DBEVENTINFO ldbei;
+ HANDLE hContact = (HANDLE)wParam;
+ if (!BoltunAutoChat(hContact))
+ return 0;
+
+ DBEVENTINFO dbei;
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = 0;
+
+ dbei.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, lParam, 0);
+ if (dbei.cbBlob == -1)
+ return 0;
+
+ dbei.pBlob = (PBYTE)malloc(dbei.cbBlob);
+ if (dbei.pBlob == NULL)
+ return 0;
+
+ CallService(MS_DB_EVENT_GET, lParam, (LPARAM)&dbei);
+ if (dbei.flags & DBEF_SENT || dbei.flags & DBEF_READ || dbei.eventType != EVENTTYPE_MESSAGE)
+ return 0;
+ DBEVENTGETTEXT egt;
+ egt.codepage = CP_ACP;
+ egt.datatype = DBVT_TCHAR;
+ egt.dbei = &dbei;
+ TCHAR* s = (TCHAR*)(void*)CallService(MS_DB_EVENT_GETTEXT, 0, (LPARAM)&egt);
+ free(dbei.pBlob);
+ if (Config.MarkAsRead)
+ CallService(MS_DB_EVENT_MARKREAD, wParam, lParam);
+
+ AnswerToContact(hContact, s);
+ mir_free(s);
+ return 0;
+}
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+void UpdateEverybodyCheckboxes(HWND hwndDlg)
+{
+ bool Enable = !IsDlgButtonChecked(hwndDlg, IDC_EVERYBODY) == BST_CHECKED;
+ HWND wnd;
+ wnd = GetDlgItem(hwndDlg, IDC_NOTINLIST);
+ EnableWindow(wnd, Enable);
+ wnd = GetDlgItem(hwndDlg, IDC_AUTOAWAY);
+ EnableWindow(wnd, Enable);
+}
+
+static int CALLBACK MainDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL bTranslated = FALSE;
+ static bool loading = true;
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ loading = true;
+ TranslateDialogDefault(hwndDlg);
+ CheckDlgButton(hwndDlg, IDC_EVERYBODY, Config.TalkWithEverybody ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_NOTINLIST, Config.TalkWithNotInList ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_AUTOAWAY, Config.TalkEveryoneWhileAway ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_WARN, Config.TalkWarnContacts ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_MARKREAD, Config.MarkAsRead ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_PAUSEDEPENDS, Config.PauseDepends ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_PAUSERANDOM, Config.PauseRandom ? BST_CHECKED : BST_UNCHECKED);
+ SendDlgItemMessage(hwndDlg, IDC_WAITTIME, EM_SETLIMITTEXT, 3, 0);
+ SetDlgItemInt(hwndDlg, IDC_WAITTIME, Config.AnswerPauseTime, FALSE);
+ SendDlgItemMessage(hwndDlg, IDC_THINKTIME, EM_SETLIMITTEXT, 3, 0);
+ SetDlgItemInt(hwndDlg, IDC_THINKTIME, Config.AnswerThinkTime, FALSE);
+ SendDlgItemMessage(hwndDlg, IDC_WARNTXT, EM_SETLIMITTEXT, MAX_WARN_TEXT, 0);
+ SetDlgItemText(hwndDlg, IDC_WARNTXT, Config.WarnText);
+ UpdateEverybodyCheckboxes(hwndDlg);
+ loading = false;
+ return TRUE;
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDC_EVERYBODY && HIWORD(wParam) == BN_CLICKED)
+ UpdateEverybodyCheckboxes(hwndDlg);
+ if (!loading)
+ {
+ bool notify = true;
+ switch (LOWORD(wParam))
+ {
+ case IDC_WARNTXT:
+ case IDC_WAITTIME:
+ case IDC_THINKTIME:
+ if (HIWORD(wParam) != EN_CHANGE)
+ notify = false;
+ break;
+ }
+ if (notify)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ break;
+ case WM_NOTIFY:
+ {
+ NMHDR* nmhdr = (NMHDR*)lParam;
+ switch (nmhdr->code)
+ {
+ case PSN_APPLY:
+ case PSN_KILLACTIVE:
+ {
+ Config.TalkWithEverybody = IsDlgButtonChecked(hwndDlg, IDC_EVERYBODY) == BST_CHECKED ? TRUE : FALSE;
+ Config.TalkWithNotInList = IsDlgButtonChecked(hwndDlg, IDC_NOTINLIST) == BST_CHECKED ? TRUE : FALSE;
+ Config.TalkEveryoneWhileAway = IsDlgButtonChecked(hwndDlg, IDC_AUTOAWAY) == BST_CHECKED ? TRUE : FALSE;
+ Config.TalkWarnContacts = IsDlgButtonChecked(hwndDlg, IDC_WARN) == BST_CHECKED ? TRUE : FALSE;
+ Config.MarkAsRead = IsDlgButtonChecked(hwndDlg, IDC_MARKREAD) == BST_CHECKED ? TRUE : FALSE;
+ Config.PauseDepends = IsDlgButtonChecked(hwndDlg, IDC_PAUSEDEPENDS) == BST_CHECKED ? TRUE : FALSE;
+ Config.PauseRandom = IsDlgButtonChecked(hwndDlg, IDC_PAUSERANDOM) == BST_CHECKED ? TRUE : FALSE;
+ Config.AnswerPauseTime = GetDlgItemInt(hwndDlg, IDC_WAITTIME, &bTranslated, FALSE);
+ if(!bTranslated)
+ Config.AnswerPauseTime = 2;
+ Config.AnswerThinkTime = GetDlgItemInt(hwndDlg, IDC_THINKTIME, &bTranslated, FALSE);
+ if(!bTranslated)
+ Config.AnswerThinkTime = 4;
+ TCHAR c[MAX_WARN_TEXT];
+ bTranslated = GetDlgItemText(hwndDlg, IDC_WARNTXT, c, MAX_WARN_TEXT);
+ if(bTranslated)
+ Config.WarnText = c;
+ else
+ Config.WarnText = TranslateTS(DEFAULT_WARN_TEXT);
+ }
+ return TRUE;
+ }
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+void UpdateUnderstandAlwaysCheckbox(HWND hwndDlg)
+{
+ bool Enable = !IsDlgButtonChecked(hwndDlg, IDC_ENGINE_SILENT) == BST_CHECKED;
+ HWND wnd;
+ wnd = GetDlgItem(hwndDlg, IDC_ENGINE_UNDERSTAND_ALWAYS);
+ EnableWindow(wnd, Enable);
+}
+
+static int CALLBACK EngineDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ WORD param;
+ BOOL bTranslated = FALSE;
+ static bool loading = true;
+ static int changeCount = 0;
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ loading = true;
+ TranslateDialogDefault(hwndDlg);
+ CheckDlgButton(hwndDlg, IDC_ENGINE_SILENT, Config.EngineStaySilent ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_ENGINE_LOWERCASE, Config.EngineMakeLowerCase ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_ENGINE_UNDERSTAND_ALWAYS, Config.EngineUnderstandAlways ? BST_CHECKED : BST_UNCHECKED);
+ SetDlgItemText(hwndDlg, IDC_MINDFILE, Config.MindFileName);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTNSAVE), blInit);
+ UpdateUnderstandAlwaysCheckbox(hwndDlg);
+ loading = false;
+ return TRUE;
+ case WM_COMMAND:
+ param = LOWORD(wParam);
+ if (param == IDC_ENGINE_SILENT && HIWORD(wParam) == BN_CLICKED)
+ UpdateUnderstandAlwaysCheckbox(hwndDlg);
+ OPENFILENAME ofn;
+ switch(param)
+ {
+ case IDC_BTNPATH:
+ {
+ const size_t fileNameSize = 5000;
+ TCHAR *filename = new TCHAR[fileNameSize];
+ TCHAR *fullname = GetFullName(Config.MindFileName);
+ _tcscpy(filename, fullname);
+ if (fullname != Config.MindFileName)
+ delete[] fullname;
+
+ ZeroMemory(&ofn, sizeof(ofn));
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = GetParent(hwndDlg);
+
+ TCHAR* mind = TranslateTS(MIND_FILE_DESC);
+ TCHAR* anyfile = TranslateTS(ALL_FILES_DESC);
+ size_t l = _tcslen(MIND_DIALOG_FILTER)
+ + _tcslen(mind) + _tcslen(anyfile);
+ TCHAR *filt = new TCHAR[l];
+ wsprintf(filt, MIND_DIALOG_FILTER, mind, anyfile);
+ for (size_t i = 0; i < l; i++)
+ if (filt[i] == '\1')
+ filt[i] = '\0';
+ ofn.lpstrFilter = filt;
+
+ ofn.lpstrFile = filename;
+ ofn.nMaxFile = fileNameSize;
+ ofn.Flags = OFN_FILEMUSTEXIST;
+ ofn.lpstrInitialDir = path;
+ if(!GetOpenFileName(&ofn))
+ {
+ delete filename;
+ delete[] filt;
+ break;
+ }
+ delete[] filt;
+ TCHAR* origf = filename;
+ TCHAR* f = filename;
+ TCHAR* p = path;
+ while (*p && *f)
+ {
+ TCHAR p1 = (TCHAR)CharLower((TCHAR*)(long)*p++);
+ TCHAR f1 = (TCHAR)CharLower((TCHAR*)(long)*f++);
+ if (p1 != f1)
+ break;
+ }
+ if (!*p)
+ filename = f;
+ Config.MindFileName = filename;
+ SetDlgItemText(hwndDlg, IDC_MINDFILE, filename);
+ delete origf;
+ }
+ case IDC_BTNRELOAD:
+ {
+ const TCHAR *c = Config.MindFileName;
+ int line;
+ bTranslated = blInit = LoadMind(c, line);
+ if(!bTranslated)
+ {
+ TCHAR* message = new TCHAR[5000];
+ wsprintf(message, TranslateTS(FAILED_TO_LOAD_BASE), line, c);
+ MessageBox(NULL, message, TranslateTS(BOLTUN_ERROR), MB_ICONERROR|MB_TASKMODAL|MB_OK);
+ delete[] message;
+ }
+ break;
+ }
+ default:
+ if (!loading)
+ {
+ if (param == IDC_MINDFILE/* && HIWORD(wParam) != EN_CHANGE*/)
+ break;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ break;
+ case WM_NOTIFY:
+ {
+ NMHDR* nmhdr = (NMHDR*)lParam;
+ switch (nmhdr->code)
+ {
+ case PSN_APPLY:
+ case PSN_KILLACTIVE:
+ {
+ Config.EngineStaySilent = IsDlgButtonChecked(hwndDlg, IDC_ENGINE_SILENT) == BST_CHECKED ? TRUE : FALSE;
+ Config.EngineMakeLowerCase = IsDlgButtonChecked(hwndDlg, IDC_ENGINE_LOWERCASE) == BST_CHECKED ? TRUE : FALSE;
+ Config.EngineUnderstandAlways = IsDlgButtonChecked(hwndDlg, IDC_ENGINE_UNDERSTAND_ALWAYS) == BST_CHECKED ? TRUE : FALSE;
+ UpdateEngine();
+ TCHAR c[MAX_MIND_FILE];
+ bTranslated = GetDlgItemText(hwndDlg, IDC_MINDFILE, c, MAX_MIND_FILE);
+ if (bTranslated)
+ Config.MindFileName = c;
+ else
+ Config.MindFileName = DEFAULT_MIND_FILE;
+ }
+ return TRUE;
+ }
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int MessageOptInit(WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.position = 910000000;
+ odp.hInstance = hInst;
+ odp.pszGroup = BOLTUN_GROUP;
+ odp.pszTitle = BOLTUN_NAME;
+ odp.pfnDlgProc = MainDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_MAIN);
+ odp.pszTab = TAB_GENERAL;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ odp.pfnDlgProc = EngineDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_ENGINE);
+ odp.pszTab = TAB_ENGINE;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ return 0;
+}
+
+static int ContactClick(WPARAM wParam, LPARAM lParam, BOOL clickNotToChat)
+{
+ HANDLE hContact = (HANDLE)wParam;
+
+ BOOL boltunautochat = DBGetContactSettingByte(hContact, BOLTUN_KEY, DB_CONTACT_BOLTUN_AUTO_CHAT, FALSE);
+ BOOL boltunnottochat = DBGetContactSettingByte(hContact, BOLTUN_KEY, DB_CONTACT_BOLTUN_NOT_TO_CHAT, FALSE);
+
+ if (clickNotToChat)
+ {
+ boltunnottochat = !boltunnottochat;
+ if(boltunnottochat)
+ {
+ boltunautochat = FALSE;
+ }
+ }
+ else
+ {
+ boltunautochat = !boltunautochat;
+ if(boltunautochat)
+ {
+ boltunnottochat = FALSE;
+ }
+ else
+ {
+ DBWriteContactSettingByte(hContact, BOLTUN_KEY, DB_CONTACT_WARNED, FALSE);
+ }
+ }
+
+ DBWriteContactSettingByte(hContact, BOLTUN_KEY, DB_CONTACT_BOLTUN_AUTO_CHAT, (BYTE)boltunautochat);
+ DBWriteContactSettingByte(hContact, BOLTUN_KEY, DB_CONTACT_BOLTUN_NOT_TO_CHAT, (BYTE)boltunnottochat);
+
+ return 0;
+}
+
+static int ContactClickAutoChat(WPARAM wParam, LPARAM lParam)
+{
+ return ContactClick(wParam, lParam, 0);
+}
+
+static int ContactClickNotToChat(WPARAM wParam, LPARAM lParam)
+{
+ return ContactClick(wParam, lParam, 1);
+}
+
+static int ContactClickStartChatting(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ StartChatting(hContact);
+ return 0;
+}
+
+static int MessagePrebuild(WPARAM wParam, LPARAM lParam)
+{
+ CLISTMENUITEM clmi;
+ HANDLE hContact = (HANDLE)wParam;
+
+ if (!blInit || (DBGetContactSettingByte(hContact,"CList","NotOnList",0) == 1))
+ {
+ ZeroMemory(&clmi, sizeof(clmi));
+ clmi.cbSize = sizeof(clmi);
+ clmi.flags = CMIM_FLAGS | CMIF_GRAYED;
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuItemAutoChat, (LPARAM)&clmi);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuItemNotToChat, (LPARAM)&clmi);
+ }
+ else
+ {
+ BOOL boltunautochat = DBGetContactSettingByte(hContact, BOLTUN_KEY, DB_CONTACT_BOLTUN_AUTO_CHAT, FALSE);
+ BOOL boltunnottochat = DBGetContactSettingByte(hContact, BOLTUN_KEY, DB_CONTACT_BOLTUN_NOT_TO_CHAT, FALSE);
+ ZeroMemory(&clmi, sizeof(clmi));
+ clmi.cbSize = sizeof(clmi);
+ clmi.flags = CMIM_FLAGS | CMIM_ICON | (boltunautochat ? CMIF_CHECKED : 0);
+ clmi.hIcon = LoadIcon( GetModuleHandle(NULL), MAKEINTRESOURCE((boltunautochat ? IDI_TICK : IDI_NOTICK)) );
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuItemAutoChat, (LPARAM)&clmi);
+
+ clmi.flags = CMIM_FLAGS | CMIM_ICON | (boltunnottochat ? CMIF_CHECKED : 0);
+ clmi.hIcon = LoadIcon( GetModuleHandle(NULL), MAKEINTRESOURCE((boltunnottochat ? IDI_TICK : IDI_NOTICK)) );
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuItemNotToChat, (LPARAM)&clmi);
+ }
+ return 0;
+}
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ mmi.cbSize=sizeof(struct MM_INTERFACE);
+ CallService(MS_SYSTEM_GET_MMI,0,(LPARAM)&mmi);
+
+ path = new TCHAR[MAX_PATH];
+ int len = GetModuleFileName(hInst, path, MAX_PATH);
+ if (len > MAX_PATH)
+ {
+ delete[] path;
+ TCHAR *path = new TCHAR[len];
+ int len2 = GetModuleFileName(hInst, path, len);
+ if (len2 != len)
+ {
+ delete[] path;
+ return false;
+ }
+ }
+ *(_tcsrchr(path, _T('\\'))+1) = _T('\0');
+
+ /*initialize miranda hooks and services on options dialog*/
+ hEventOptInitialise = HookEvent(ME_OPT_INITIALISE, MessageOptInit);
+ /*initialize miranda hooks and services*/
+ hEventDbEventAdded = HookEvent(ME_DB_EVENT_ADDED, MessageEventAdded);
+ hEventPrebuild = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, MessagePrebuild);
+
+ CreateServiceFunction(SERV_CONTACT_AUTO_CHAT, ContactClickAutoChat);
+ CreateServiceFunction(SERV_CONTACT_NOT_TO_CHAT, ContactClickNotToChat);
+ CreateServiceFunction(SERV_CONTACT_START_CHATTING, ContactClickStartChatting);
+ {
+ CLISTMENUITEM mi;
+
+ ZeroMemory(&mi,sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.position = -50010002; //TODO: check the warning
+ mi.flags = 0;
+ mi.hIcon = NULL;
+ mi.pszContactOwner = NULL;
+ mi.pszName = BOLTUN_AUTO_CHAT;
+ mi.pszService = SERV_CONTACT_AUTO_CHAT;
+ hMenuItemAutoChat = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&mi);
+
+ mi.position = -50010001; //TODO: check the warning
+ mi.pszName = BOLTUN_NOT_TO_CHAT;
+ mi.pszService = SERV_CONTACT_NOT_TO_CHAT;
+ hMenuItemNotToChat = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&mi);
+
+ mi.flags = CMIF_NOTOFFLINE;
+ mi.position = -50010000; //TODO: check the warning
+ mi.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_RECVMSG));
+ mi.pszName = BOLTUN_START_CHATTING;
+ mi.pszService = SERV_CONTACT_START_CHATTING;
+ hMenuItemStartChatting = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&mi);
+ }
+ int line;
+ blInit = LoadMind(Config.MindFileName, line);
+ if(!blInit)
+ {
+ TCHAR path[2000];
+ wsprintf(path, TranslateTS(FAILED_TO_LOAD_BASE), line, (const TCHAR*)Config.MindFileName);
+ MessageBox(NULL, path, TranslateTS(BOLTUN_ERROR), MB_ICONERROR|MB_TASKMODAL|MB_OK);
+ }
+ /*record for Uninstall plugin*/
+ DBWriteContactSettingString(NULL, "Uninstall", BOLTUN_NAME, BOLTUN_KEY);
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ if (pTimer)
+ KillTimer(NULL, pTimer);
+ if(blInit)
+ {
+#if 0 //No need to save, we don't have studying algorithm
+ if(Config.MindFileName && !SaveMind(Config.MindFileName))
+ {
+//This causes errors with development core when calling MessageBox.
+//It seems that it's now a Boltun problem.
+//So in case of saving error we will remain silent
+#if 0
+ TCHAR path[MAX_PATH];
+ wsprintf(path, TranslateTS(FAILED_TO_SAVE_BASE), (const TCHAR*)Config.MindFileName);
+ TCHAR* err = TranslateTS(BOLTUN_ERROR);
+ MessageBox(NULL, path, err, MB_ICONERROR|MB_TASKMODAL|MB_OK);*/
+#endif
+ }
+#endif
+ delete bot;
+ }
+ delete[] path;
+ return 0;
+}
diff --git a/plugins/Boltun/boltun.h b/plugins/Boltun/boltun.h
new file mode 100644
index 0000000000..6063dc2310
--- /dev/null
+++ b/plugins/Boltun/boltun.h
@@ -0,0 +1,80 @@
+//***********************************************************
+// Copyright © 2003-2008 Alexander S. Kiselev, Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef _BOLTUN_H
+#define _BOLTUN_H
+
+//Should use different keys in UNICODE and ANSI builds, because usage of plugin
+//with old (another) configs will cause crashes.
+#ifdef _UNICODE
+#define BOLTUN_KEY "BoltunW"
+#else
+#define BOLTUN_KEY "Boltun"
+#endif
+
+//Service names
+#define SERV_CONTACT_AUTO_CHAT "Boltun/ContactAutoChat"
+#define SERV_CONTACT_NOT_TO_CHAT "Boltun/ContactNotToChat"
+#define SERV_CONTACT_START_CHATTING "Boltun/ContactStartChatting"
+
+//Database keys
+#define DB_CONTACT_BOLTUN_NOT_TO_CHAT "BoltunNotToChat"
+#define DB_CONTACT_BOLTUN_AUTO_CHAT "BoltunAutoChat"
+#define DB_CONTACT_WARNED "Warned"
+
+//Plugin group in settings
+#define BOLTUN_GROUP "Message sessions"
+
+//Filename depends on UNICODE
+#ifdef UNICODE
+#define DEFAULT_MIND_FILE _T("boltun.mindw")
+#else
+#define DEFAULT_MIND_FILE _T("boltun.mind")
+#endif
+
+//===============================================
+// These are strings for translation:
+//===============================================
+
+//Plugin name
+#define BOLTUN_NAME "Boltun"
+
+#define PLUGIN_DESCRIPTION "Boltun, the chat bot in the russian language."
+
+#define MIND_FILE_DESC _T("Mind Files")
+#define ALL_FILES_DESC _T("All Files")
+
+//UI strings
+#define BOLTUN_AUTO_CHAT "Boltun/Auto Chat"
+#define BOLTUN_NOT_TO_CHAT "Boltun/Not to Chat"
+#define BOLTUN_START_CHATTING "Boltun/Start Chatting"
+
+#define DEFAULT_WARN_TEXT _T("Hello. I'm Boltun! I'll talk to you, while my owner is away. Please write without mistakes!")
+
+// Error messages
+#define BOLTUN_ERROR _T("Boltun Error")
+#define FAILED_TO_LOAD_BASE _T("Failed to load base of remarks. Error at line %d of %s. (Or few lines before).")
+#define FAILED_TO_SAVE_BASE _T("Failed to save base of remarks to %s")
+
+//Settings tab names
+#define TAB_GENERAL "General Settings"
+#define TAB_ENGINE "Engine Settings"
+
+#endif /*_BOLTUN_H*/
diff --git a/plugins/Boltun/config.cpp b/plugins/Boltun/config.cpp
new file mode 100644
index 0000000000..a59fc89d56
--- /dev/null
+++ b/plugins/Boltun/config.cpp
@@ -0,0 +1,159 @@
+//***********************************************************
+// Copyright © 2003-2008 Alexander S. Kiselev, Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#include "config.h"
+
+#include <windows.h>
+#include <time.h>
+#include <string>
+
+#include "newpluginapi.h"
+#include "m_database.h"
+#include "m_langpack.h"
+#include "boltun.h"
+
+//Database keys
+#define DB_EVERYBODY "Everybody"
+#define DB_NOT_IN_LIST "NotInList"
+#define DB_AUTOAWAY "AutoAway"
+#define DB_WARN "Warn"
+#define DB_MARK_READ "MarkRead"
+#define DB_WAIT_TIME "WaitTime"
+#define DB_THINK_TIME "ThinkTime"
+#define DB_PAUSE_DEPENDS "PauseDepends"
+#define DB_PAUSE_RANDOM "PauseRandom"
+#define DB_WARN_TEXT "WarnText"
+#define DB_MIND_FILE_NAME "MindFileName"
+#define DB_ENGINE_SILENT "SilentEngine"
+#define DB_ENGINE_LOWERCASE "MakeLowerCase"
+#define DB_ENGINE_UNDERSTAND_ALWAYS "UnderstandAlways"
+
+inline TCHAR* GetString(char* key, const TCHAR* def)
+{
+ DBVARIANT dbv;
+ TCHAR* val;
+ if(!DBGetContactSettingTString(NULL, BOLTUN_KEY, key, &dbv))
+ {
+ size_t len = wcslen(dbv.ptszVal) + 1;
+ val = new TCHAR[len];
+ _tcscpy_s(val, len, dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ {
+ size_t len = wcslen(def) + 1;
+ val = new TCHAR[len];
+ _tcscpy_s(val, len, def);
+ }
+ return val;
+}
+
+inline const TCHAR* SetString(char* key, const TCHAR* value)
+{
+ size_t len = _tcslen(value) + 1;
+ TCHAR* val = new TCHAR[len];
+ _tcscpy_s(val, len, value);
+ DBWriteContactSettingTString(NULL, BOLTUN_KEY, key, val);
+ return val;
+}
+
+#define BUILDETTERS(x, str, def) \
+ const bool BoltunConfig::Get##x() { \
+ return DBGetContactSettingDword(NULL, BOLTUN_KEY, str, def) != 0; } \
+ const bool BoltunConfig::Set##x(const bool value) { \
+ DBWriteContactSettingDword(NULL, BOLTUN_KEY, str, value); \
+ return value; }
+
+#define BUILDINTETTERS(x, str, def) \
+ const int BoltunConfig::Get##x() { \
+ return DBGetContactSettingDword(NULL, BOLTUN_KEY, str, def); } \
+ const int BoltunConfig::Set##x(const int value) { \
+ DBWriteContactSettingDword(NULL, BOLTUN_KEY, str, value); \
+ return value; }
+
+#define BUILDSTRETTERS(x, str, def) \
+ const TCHAR* BoltunConfig::Get##x() { \
+ return GetString(str, def); } \
+ const TCHAR* BoltunConfig::Set##x(const TCHAR* value) { \
+ return SetString(str, value); }
+
+#define BUILDINIT(x) \
+ x(&BoltunConfig::Get##x, &BoltunConfig::Set##x)
+
+BUILDETTERS(TalkWithNotInList, DB_NOT_IN_LIST, false);
+BUILDETTERS(TalkWithEverybody, DB_EVERYBODY, false);
+BUILDETTERS(TalkWarnContacts, DB_WARN, false);
+BUILDETTERS(TalkEveryoneWhileAway, DB_AUTOAWAY, false);
+BUILDETTERS(MarkAsRead, DB_MARK_READ, true);
+BUILDINTETTERS(AnswerPauseTime, DB_WAIT_TIME, 2);
+BUILDINTETTERS(AnswerThinkTime, DB_THINK_TIME, 4);
+BUILDETTERS(PauseDepends, DB_PAUSE_DEPENDS, TRUE);
+BUILDETTERS(PauseRandom, DB_PAUSE_RANDOM, TRUE);
+BUILDSTRETTERS(WarnText, DB_WARN_TEXT, TranslateTS(DEFAULT_WARN_TEXT));
+BUILDSTRETTERS(MindFileName, DB_MIND_FILE_NAME, DEFAULT_MIND_FILE);
+BUILDETTERS(EngineStaySilent, DB_ENGINE_SILENT, FALSE);
+BUILDETTERS(EngineMakeLowerCase, DB_ENGINE_LOWERCASE, FALSE);
+BUILDETTERS(EngineUnderstandAlways, DB_ENGINE_UNDERSTAND_ALWAYS, FALSE);
+
+BoltunConfig::BoltunConfig()
+ :BUILDINIT(TalkWithEverybody),
+ BUILDINIT(TalkWithNotInList),
+ BUILDINIT(TalkWarnContacts),
+ BUILDINIT(TalkEveryoneWhileAway),
+ BUILDINIT(MarkAsRead),
+ BUILDINIT(AnswerPauseTime),
+ BUILDINIT(AnswerThinkTime),
+ BUILDINIT(PauseDepends),
+ BUILDINIT(PauseRandom),
+ BUILDINIT(WarnText),
+ BUILDINIT(MindFileName),
+ BUILDINIT(EngineStaySilent),
+ BUILDINIT(EngineMakeLowerCase),
+ BUILDINIT(EngineUnderstandAlways)
+{
+ TalkWithEverybody.SetOwner(this);
+ TalkWithNotInList.SetOwner(this);
+ TalkWarnContacts.SetOwner(this);
+ TalkEveryoneWhileAway.SetOwner(this);
+ MarkAsRead.SetOwner(this);
+ AnswerPauseTime.SetOwner(this);
+ AnswerThinkTime.SetOwner(this);
+ PauseDepends.SetOwner(this);
+ PauseRandom.SetOwner(this);
+ WarnText.SetOwner(this);
+ MindFileName.SetOwner(this);
+ EngineStaySilent.SetOwner(this);
+ EngineMakeLowerCase.SetOwner(this);
+ EngineUnderstandAlways.SetOwner(this);
+}
+
+BoltunConfig::~BoltunConfig()
+{
+}
+
+class _BoltunConfigInit
+{
+public:
+ BoltunConfig cfg;
+};
+
+_BoltunConfigInit inst;
+
+BoltunConfig &Config = inst.cfg; \ No newline at end of file
diff --git a/plugins/Boltun/config.h b/plugins/Boltun/config.h
new file mode 100644
index 0000000000..c5bb20e700
--- /dev/null
+++ b/plugins/Boltun/config.h
@@ -0,0 +1,157 @@
+//***********************************************************
+// Copyright © 2003-2008 Alexander S. Kiselev, Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+#include <tchar.h>
+
+class BoltunConfig;
+
+template <class T, class BaseClass>
+class Property
+{
+public:
+ typedef const T (__thiscall BaseClass::*Getter)();
+ typedef const T (__thiscall BaseClass::*Setter)(const T);
+private:
+ const Getter getter;
+ const Setter setter;
+ BaseClass* owner;
+ bool cacheValid;
+ T cached;
+public:
+ Property(Getter g, Setter s)
+ :getter(g), setter(s), cacheValid(false)
+ {
+ }
+
+ void SetOwner(BaseClass* o)
+ {
+ owner = o;
+ }
+
+ inline operator const T()
+ {
+ if (cacheValid)
+ return cached;
+ cached = (owner->*getter)();
+ cacheValid = true;
+ return cached;
+ }
+
+ inline const T operator= (const T& value)
+ {
+ cacheValid = true;
+ return (owner->*setter)(cached = value);
+ }
+};
+
+template <class T, class BaseClass>
+class PtrProperty
+{
+public:
+ typedef const T* (__thiscall BaseClass::*Getter)();
+ typedef const T* (__thiscall BaseClass::*Setter)(const T*);
+private:
+ const Getter getter;
+ const Setter setter;
+ BaseClass* owner;
+ bool cacheValid;
+ const T* cached;
+public:
+ PtrProperty(Getter g, Setter s)
+ :getter(g), setter(s), cacheValid(false), cached(NULL)
+ {
+ }
+
+ ~PtrProperty()
+ {
+ delete cached;
+ }
+
+ void SetOwner(BaseClass* o)
+ {
+ owner = o;
+ }
+
+ inline operator const T*()
+ {
+ if (cacheValid)
+ return cached;
+ cached = (owner->*getter)();
+ cacheValid = true;
+ return cached;
+ }
+
+ inline const T* operator= (const T* value)
+ {
+ cacheValid = true;
+ delete cached;
+ cached = (owner->*setter)(value);
+ return cached;
+ }
+};
+
+class _BoltunConfigInit;
+
+#define BUILDDEFETTERS(x, typ) \
+ const typ Get##x(); \
+ const typ Set##x(const typ value);
+
+class BoltunConfig
+{
+ BUILDDEFETTERS(TalkWithEverybody, bool);
+ BUILDDEFETTERS(TalkWithNotInList, bool);
+ BUILDDEFETTERS(TalkWarnContacts, bool);
+ BUILDDEFETTERS(TalkEveryoneWhileAway, bool);
+ BUILDDEFETTERS(MarkAsRead, bool);
+ BUILDDEFETTERS(AnswerPauseTime, int);
+ BUILDDEFETTERS(AnswerThinkTime, int);
+ BUILDDEFETTERS(PauseDepends, bool);
+ BUILDDEFETTERS(PauseRandom, bool);
+ BUILDDEFETTERS(WarnText, TCHAR*);
+ BUILDDEFETTERS(MindFileName, TCHAR*);
+ BUILDDEFETTERS(EngineStaySilent, bool);
+ BUILDDEFETTERS(EngineMakeLowerCase, bool);
+ BUILDDEFETTERS(EngineUnderstandAlways, bool);
+ BoltunConfig();
+ ~BoltunConfig();
+ friend class _BoltunConfigInit;
+public:
+ Property<bool, BoltunConfig> TalkWithEverybody;
+ Property<bool, BoltunConfig> TalkWithNotInList;
+ Property<bool, BoltunConfig> TalkWarnContacts;
+ Property<bool, BoltunConfig> TalkEveryoneWhileAway;
+ Property<bool, BoltunConfig> MarkAsRead;
+ Property<int, BoltunConfig> AnswerPauseTime;
+ Property<int, BoltunConfig> AnswerThinkTime;
+ Property<bool, BoltunConfig> PauseDepends;
+ Property<bool, BoltunConfig> PauseRandom;
+ PtrProperty<TCHAR, BoltunConfig> WarnText;
+ PtrProperty<TCHAR, BoltunConfig> MindFileName;
+ Property<bool, BoltunConfig> EngineStaySilent;
+ Property<bool, BoltunConfig> EngineMakeLowerCase;
+ Property<bool, BoltunConfig> EngineUnderstandAlways;
+};
+
+extern BoltunConfig &Config;
+
+#endif /* _CONFIG_H */ \ No newline at end of file
diff --git a/plugins/Boltun/lang_pack.txt b/plugins/Boltun/lang_pack.txt
new file mode 100644
index 0000000000..79b33c35f6
--- /dev/null
+++ b/plugins/Boltun/lang_pack.txt
@@ -0,0 +1,64 @@
+[Boltun]
+Болтун
+[Boltun, the chat bot in the russian language.]
+Болтун, чат-бот для русского языка.
+[All Files]
+Все файлы
+[Mind Files]
+Файлы базы
+[Boltun/Auto Chat]
+Болтун/Авто разговор
+[Boltun/Not to Chat]
+Болтун/Не разговаривать
+[Boltun/Start Chatting]
+Болтун/Начать разговор
+[Hello. I'm Boltun! I'll talk to you, while my owner is away. Please write without mistakes!]
+Привет. Я Болтун! Я буду говорить с Вами, пока моего хозяина нет. Пожалуйста, пишите без ошибок!
+[Boltun Error]
+Ошибка Болтуна
+[Failed to load base of remarks. Error at line %d of %s. (Or few lines before).]
+Не удалось загрузить базу. Ошибка в строке %d файла %s. (Или на пару строк раньше).
+[Failed to save base of remarks to %s]
+Не удалось загрузить базу в файл %s
+[Boltun Autochat everybody]
+Болтун говорит со всеми
+[Boltun Autochat everybody not in contact list]
+Болтун говорит со всеми, кто не в списке контактов
+[Boltun Autochat everybody while away]
+Болтун говорит со всеми, когда меня нет
+[Warn contacts Boltun is chatting]
+Предупреждать о разговоре с Болтуном
+[Mark replied messages as read]
+Отмечать сообщения как прочитанные
+[sec]
+сек
+[Warning text]
+Текст предупреждения
+[Time before the answer]
+Время перед ответом
+[Typing time depends on message length (in chars)]
+Время печати зависит от длины сообщения (в буквах)
+[Typing time (for a message of 4 words):]
+Время печати (для 4-х слов):
+[Thinking time:]
+Время обдумывания ответа:
+[Typing and thinking time can be much longer]
+Время печати и обдумывания может увеличиваться
+[Engine]
+Интеллект
+[Base of remarks]
+База реплик
+[Save]
+Сохранить
+[Reload]
+Перезагрузить
+[Stay silent, if have no good aswers]
+Молчать, если нету хороших ответов
+[Start answers with a lowercase letter]
+Начинать ответы с маленькой буквы.
+[General Settings]
+Общие настройки
+[Engine Settings]
+Настройки "интеллекта"
+[Don't use "I don't understand"-like answers]
+Не использовать ответы типа "Не понял?" \ No newline at end of file
diff --git a/plugins/Boltun/mind.txt b/plugins/Boltun/mind.txt
new file mode 100644
index 0000000000..01dcd6e5ba
--- /dev/null
+++ b/plugins/Boltun/mind.txt
@@ -0,0 +1,142 @@
+В этом файле описан формат файла базы реплик и принцип работы Болтуна.
+
+==============================================================
+Формат файла.
+Файл базы реплик имеет кодировку UNICODE и состоит из пар строк.
+Такие пары строк являются допустимыми:
+
+1) Фразы обучения:
+
+"Фраза"
+"Ответ"
+
+2) Ответы по ключевым словам:
+
+ ("список ключевых слов")
+"Ответ"
+
+При этом список ключевых слов состоит из слов, ОБЯЗАТЕЛЬНО должен заканчиваться символом
+типа предложения ('?' - вопросительное, '.' - любое другое). Если список предваряется
+символом '~', то это является разрешением нечеткого совпадения.
+
+3) Ответы по специальным словам:
+
+ {"список специальных слов"}
+"Ответ"
+
+4) Часто используемые фразы:
+
+ ["Фраза"]
+"Ответ"
+
+5) Слова-признаки вопросительного предложения:
+
+ <QUESTION>
+"слово"
+
+6) Специальные слова:
+
+ <IGNORED>
+"Специальное слово"
+
+7) Общие ответы:
+
+ <ESCAPE>
+"Общий ответ"
+
+8) Начальные фразы (Для начала разговора):
+
+ <INITIAL>
+"Начальная фраза"
+
+9) Общие ответы (то же, что и 7):
+
+ <FAILURE>
+"Общий ответ"
+
+10) Алиасы
+
+@"привет"
+"превед прю прифф"
+
+11) Ответы на повторы
+
+<REPEAT>
+"Я уже сказал."
+
+==============================================================
+
+Припцип работы движка бота:
+
+Во время работы движку передается реплика клиента, а движок на основе реплики формирует ответ.
+Нахождение ответа имеет такие фазы:
+
+0) Если эта реплика совпадает с предыдущей (точное совпадение), сказанной не более 30 мин. назад
+- выдается одна из реплик-ответов на повторы (11). После 2-3 повторов Болтун перестает реагировать
+на повторы этой реплики и просто не отвечает. После 30 мин Болтун нормально реагирует на эту же
+реплику, но на повтор сразу будет молчать. Так будет до первой реплики, отличной от уже
+повторенной.
+
+1) Поиск реплики целиком среди часто используемых (4) реплик. При нахождении реплики ее ответ
+возвращается как ответ бота. Если ответов для этом реплики несколько, то выбирается
+один из них.
+
+Если реплика не найдена среди (4), то поиск ответов продолжается.
+Далее все найденные ответы запоминаются с учетом их приоритета и в конце осуществляется
+выбор. 0 - наивысший приоритет.
+
+2) Далее происходит поиск среди реплик обучения (1) аналогично пункту 1). Результаты имеют
+приоритет 1.
+
+3) Производится разбиение реплики на предложения. Разделителями предложений являются симолы
+'.' '!' '?', при этом игнорируется '!' в составе смайлика ':-!'.
+
+Следующие действия производятся для каждого предложения:
+
+ 4) Поиск предложения целиком среди часто используемых (4) фраз. Результаты имеют
+ приоритет 0.
+
+ 5) Поиск среди фраз обучения (1). Результаты имеют приоритет 1.
+
+ 6) Далее происходит разделение предложения на слова. При этом в предложении выделяются
+ как слова все смайлики, а также скобки количеством больше двух ( "))" ).
+ Производится определение типа предложения: вопросительное или нет. Вопросительное
+ предложение или имеет в конце восклицательный знак, или содержит хотя бы одно слово,
+ которое относится к вопросительным (5).
+ Отдельно записываются слова, которые относятся к специальным (6).
+
+ 7) Поиск совпадения среди ключевых слов (2). Все ключевые слова должны присутствовать в
+ предложении и тип предложения должен совпадать. Результаты имеют приоритет 1.
+ Предпочтительным является совпадение как можно большего количества ключевых слов.
+
+ 8) Поиск совпадения среди специальных слов (3). Специальные - это слова из (6).
+ Подразумевается, что они не несут особой смысловой нагрузки и по ним возможны только
+ обобщенные ответы. В предложении должно присутствовать не менее одного специального
+ слова (как правило, присутствует только одно). Также, тип предложения должен совпадать.
+ Результаты имеют приоритет 2.
+
+Результаты, полученные от разных предложений, имеют одинаковый приоритет и только один из
+них может стать ответом.
+
+Пункты 9 и 10 ипользуются, только если опция "Молчать, если нету хороших ответов"
+(aka "Иногда лучше жевать") отключена.
+
+9) Выбор одного случайного ответа среди общих ответов (7). Результаты имеют приоритет 3.
+
+10) Выбор одного случайного ответа среди общих ответов (9). Обычно (9) - это несколько
+реплик в духе "не понял". Результаты имеют приоритет 3.
+
+Таким образом, в случае непонятной фразы используется или 9 или 10 с равной вероятностью.
+
+После этого производится замена алиасов (10) в исходной реплике на соответствующие слова и
+повторение пунктов 1-10 для такой реплики.
+
+Как ответ бота выбирается случайный ответ с наименьшим значением приоритета. При нескольких
+ответах одинакового приоритета выбирается тот, который не использовался до этого
+(учитываются предыдушие ответы Болтуна этому контакту за последние 30 минут).
+Если никаких вариантов ответа нет (например, для пустой базы реплик), то ответа и не
+следует.
+
+Если ответ должен начинаться с маленькой буквы, то первая найденная буква в ответе
+заменяется на маленькую. Этого не происходит, если ответ начинается с пробела.
+При этом пробел удаляется. \ No newline at end of file
diff --git a/plugins/Boltun/readme.txt b/plugins/Boltun/readme.txt
new file mode 100644
index 0000000000..f20c3ec466
--- /dev/null
+++ b/plugins/Boltun/readme.txt
@@ -0,0 +1,124 @@
+Болтун 0.3.0 Unicode Alpha 7
+
+08.01.2009.
+
+Плагин предназначен только для тестирования.
+И нечего его распостранять без приметки "beta"
+
+================================================
+
+Список файлов:
+
+readme.txt - Этот файл
+boltunw.dll - Собственно плагин
+boltun.mindw - База реплик для плагина
+lang_pack.txt - Файл с переводом интерфейса Болтуна на русский язык.
+mind.txt - Описание формата файла базы реплик и принципа работы Болтуна.
+
+================================================
+
+Установка:
+
+Скопировать файлы boltunw.dll и boltun.mindw в папку Plugins Миранды.
+Строки из lang_pack.txt могут быть использованы для русификации интерфейса.
+
+================================================
+
+Описание настроек:
+
+Основные настройки:
+"Болтун говорит со всеми" - Болтун говорит абсолютно со всеми контактами
+"Болтун говорит со всеми, кто не в списке контактов" - Болтун говорит с теми, кто отсутствует в вашем списке контактов
+"Болтун говорит со всеми, когда меня нет" - Болтун говорит абсолютно со всеми контактами, когда статус отличается от "невидимый" или "в сети"
+"Предупреждать о разговоре с Болтуном" - Каждый контакт один раз в жизни будет предупрежден о разговоре с Болтуном.
+"Отмечать сообщения как прочитанные" - Отмечать сообщения контакта, с которым говорит Болтун, как прочитанные. Таким образом вы не увидите уведомлений о них.
+"Текст предупреждения" - Сообщение, которое Болтун передает контакту в качестве предупреждения.
+"Время печати" - время набора сообщения, в секундах.
+"Время печати зависит от длины сообщения (в буквах)" - время набора зависит от длины сообщения. "Время печати" тогда указывает время набора сообщения из 25 символов.
+"Время обдумывания ответа" - время паузы перед началом набора сообщения, в секундах.
+"Время печати и обдумывания может увеличиваться" - Время набора и обдумывания случайным образом может быть до 4-х раз больше заданного.
+
+Установки движка:
+"База реплик" - имя файла, с которого будет загружаться и в который будет сохраняться база реплик.
+Кнопка "..." - выбор файла базы реплик в диалоге.
+Кнопка "Сохранить" - сохранение базы реплик в файл, не дожидаясь автосохраниния при выходе из Миранды.
+Кнопка "Перегрузить" - загрузка базы реплик из файла.
+"Молчать, если нету хороших ответов" - Для непонятной реплики не используются общие ответы, а Болтун просто молчит.
+"Начинать ответы с маленькой буквы" - Заменыть в ответе первую букву на маленькую. Но если ответ начинается с пробела, то замены не происходит, а пробел удаляется.
+"Не испольозвать ответы типа "Не понял?"" - Пытаться ответить общими фразами вместо проявления непонимания.
+
+================================================
+
+Список изменений:
+
+08.01.2009:
+- Добавлена реакция на повторяющиеся реплики
+- Исправления ошибок в ядре движка.
+- При ошибке загрузки базы теперь приблизительно указывается строка с ошибкой в файле базы (не верьте ему!!!!).
+
+04.01.2009:
+- Удалена утечка памяти при загрузке базы
+- Поддержка алиасов
+- Небольшие оптимизации
+
+30.09.2008:
+- Исправлены некоторые недочеты, которые вызывали повторяемость бота.
+- Обновлен файл описания базы (т.к. добавлены начальные фразы в базу реплик).
+
+29.09.2008:
+- Устранено влияние истории одного контакта на разговор с другим.
+- Убрано запоминание не отосланных ответов.
+
+28.09.2008:
+- Болтун теперь в случае нескольких реплик подряд отвечает только на последнюю.
+- Предотвращены возможные проблемы при одновременном разговоре с несколькими контактами.
+- Болтун теперь отслеживает историю каждого контакта отдельно, чтобы лучше избегать повторений.
+
+19.09.2008:
+- Добавлена возможность Болтуна начинать разговор.
+
+14.09.2008:
+- Улучшен алгоритм выбора во избежание повторяющихся ответов.
+
+13.09.2008:
+- Переделано определение текущего каталога.
+- Убрано сохранение базы, чтобы никто не строил иллюзий касательно самообучения.
+- Изменены настройки выбора файла базы, исправлено некоторые ошибки в диалоге настроек.
+- Добавлена смена курсора на песочные часы на время загрузки базы.
+
+12.09.2008:
+- Исправлена ошибка, приводившая к исчезновению значков статусов в плагине MRA.
+
+10.08.2008:
+- Предупреждение о разговоре с Болтуном теперь работает не только когда включен "Болтун разговаривает со всеми"
+- Изменено расширение файла базы, чтобы не путать его с файлом профиля.
+- Исправлены ошибки с включением кнопки "Применить" в настройках при отсутствии изменений.
+- Создан файл перевода интерфейса Болтуна на русский язык.
+- Добавлена опция "Молчать, если нет хорошего ответа"
+- Добавлена опция "Отвечать с маленькой буквы"
+- Исправлен поиск предложения без финальной точки среди часто используемых.
+- Исправлен алгоритм работы движка для разных типов предложений.
+- Поиск среди ключевых слов теперь более лояльный.
+- Добавлен файл с описанием формата файла базы реплик и принципа работы бота.
+- Пустые списки ключевых и специальных слов теперь удаляются при загрузке.
+- Исправлена ошибка при ответе английскими символами.
+- Исправлена ошибка при разделении предложения на слова для слов типа "каким-нибудь".
+- Исправлена ошибка, связанная с обработкой перевода стоки в реплике.
+- Разбиение совпадений по ключевым словам на приоритеты и выбор согласно им.
+- Болтун теперь старается меньше повторяться.
+
+28.07.2008:
+- Переписан движок по образу и подобию старого (скорее всего, он несколько потупел).
+- Добавлена поддержка Unicode (для чего и переписан движок)
+- Добавлено время "обдумывания" ответа, предваряющее процесс написания.
+- Добавлено оповещение "набирает текст" при ответе бота.
+- Время "набора" зависит от длины сообщения.
+- Исправлена ошибка с появлением сообщения пользователя при установленной задержке ответа
+- Изменен порядок добавления сообжений в историю так, что теперь всегда ответы идут после вопросов (было не всегда так).
+- Файл базы реплик теперь чем-то отличается от предыдушего формата (не помню так навскидку чем...). Как минимум тем, что он в Unicode-кодировке.
+
+================================================
+
+Контакты:
+
+valentin.pavlyuchenko@gmail.com \ No newline at end of file
diff --git a/plugins/Boltun/res/boltun.ico b/plugins/Boltun/res/boltun.ico
new file mode 100644
index 0000000000..e6e619f9f2
--- /dev/null
+++ b/plugins/Boltun/res/boltun.ico
Binary files differ
diff --git a/plugins/Boltun/res/smiles.dat b/plugins/Boltun/res/smiles.dat
new file mode 100644
index 0000000000..10bd2b0d5b
--- /dev/null
+++ b/plugins/Boltun/res/smiles.dat
Binary files differ
diff --git a/plugins/Boltun/resource.h b/plugins/Boltun/resource.h
new file mode 100644
index 0000000000..b4fa069c0e
--- /dev/null
+++ b/plugins/Boltun/resource.h
@@ -0,0 +1,46 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Boltun.rc
+//
+#define IDM_HIDE 0x0010
+#define IDD_MAIN 101
+#define IDI_MAINBOLTUN 102
+#define IDD_ENGINE 103
+#define IDR_SMILES 105
+#define IDI_TICK 106
+#define IDI_NOTICK 107
+#define IDI_RECVMSG 108
+#define IDC_EVERYBODY 1000
+#define IDC_NOTINLIST 1001
+#define IDC_AUTOAWAY 1002
+#define IDC_WARN 1003
+#define IDC_MARKREAD 1004
+#define IDC_LWAITTIME 1005
+#define IDC_WAITTIME 1006
+#define IDC_LSEC 1007
+#define IDC_LWARN 1008
+#define IDC_WARNTXT 1009
+#define IDC_PLUGINRM 1010
+#define IDC_TXTREPLICPATH 1011
+#define IDC_MINDFILE 1012
+#define IDC_BTNPATH 1013
+#define IDC_BTNSAVE 1014
+#define IDC_BTNRELOAD 1015
+#define IDC_PAUSEDEPENDS 1016
+#define IDC_PAUSERANDOM 1017
+#define IDC_THINKTIME 1018
+#define IDC_LSEC2 1019
+#define IDC_ENGINE_SILENT 1020
+#define IDC_ENGINE_LOWERCASE 1021
+#define IDC_ENGINE_UNDERSTAND_ALWAYS 1022
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 106
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1023
+#define _APS_NEXT_SYMED_VALUE 102
+#endif
+#endif