summaryrefslogtreecommitdiff
path: root/plugins/Boltun/src
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/Boltun/src')
-rw-r--r--plugins/Boltun/src/actionQueue.cpp254
-rw-r--r--plugins/Boltun/src/actionQueue.h30
-rw-r--r--plugins/Boltun/src/boltun.cpp713
-rw-r--r--plugins/Boltun/src/boltun.h74
-rw-r--r--plugins/Boltun/src/config.cpp159
-rw-r--r--plugins/Boltun/src/config.h157
-rw-r--r--plugins/Boltun/src/resource.h46
7 files changed, 1433 insertions, 0 deletions
diff --git a/plugins/Boltun/src/actionQueue.cpp b/plugins/Boltun/src/actionQueue.cpp
new file mode 100644
index 0000000000..c464c92f39
--- /dev/null
+++ b/plugins/Boltun/src/actionQueue.cpp
@@ -0,0 +1,254 @@
+//***********************************************************
+// 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 0x0A00
+#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)
+{
+
+ 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";
+
+ return PSS_MESSAGE;
+}
+
+static void TimerAnswer(HANDLE hContact, const TalkBot::MessageInfo* info)
+{
+ DBEVENTINFO ldbei;
+ int size = (int)info->Answer.length() + 1;
+ int bufsize = size;
+ char* msg;
+
+ 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);
+
+
+ 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;
+
+ delete msg;
+
+ 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 * (int)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/src/actionQueue.h b/plugins/Boltun/src/actionQueue.h
new file mode 100644
index 0000000000..78f6ce5f9e
--- /dev/null
+++ b/plugins/Boltun/src/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/src/boltun.cpp b/plugins/Boltun/src/boltun.cpp
new file mode 100644
index 0000000000..52e140d509
--- /dev/null
+++ b/plugins/Boltun/src/boltun.cpp
@@ -0,0 +1,713 @@
+//***********************************************************
+// 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 0x0A00
+
+#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
+
+int hLangpack;
+
+TalkBot* bot = NULL;
+
+
+#define MAX_WARN_TEXT 1024
+#define MAX_MIND_FILE 1024
+
+HINSTANCE hInst;
+BOOL blInit = FALSE;
+UINT pTimer = 0;
+TCHAR *path;
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+ 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,
+ // {488C5C84-56DA-434F-96F1-B18900DEF760}
+ { 0x488c5c84, 0x56da, 0x434f, { 0x96, 0xf1, 0xb1, 0x89, 0x0, 0xde, 0xf7, 0x60 } }
+};
+
+static HANDLE hEventDbEventAdded;
+static HANDLE hEventOptInitialise;
+static HANDLE hEventPrebuild;
+static HANDLE hMenuItemAutoChat;
+static HANDLE hMenuItemNotToChat;
+static HANDLE hMenuItemStartChatting;
+
+
+#define MIND_DIALOG_FILTER _T("%s (*.mindw)\1*.mindw\1%s (*.*)\1*.*\1")
+
+
+#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_PTR 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_PTR 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;
+ Options_AddPage(wParam, &odp);
+ odp.pfnDlgProc = EngineDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_ENGINE);
+ odp.pszTab = TAB_ENGINE;
+ Options_AddPage(wParam, &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_PTR ContactClickAutoChat(WPARAM wParam, LPARAM lParam)
+{
+ return ContactClick(wParam, lParam, 0);
+}
+
+static INT_PTR ContactClickNotToChat(WPARAM wParam, LPARAM lParam)
+{
+ return ContactClick(wParam, lParam, 1);
+}
+
+static INT_PTR 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) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+extern "C" int __declspec(dllexport) Load(void)
+{
+ mir_getLP(&pluginInfo);
+
+ 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 = Menu_AddContactMenuItem(&mi);
+
+ mi.position = -50010001; //TODO: check the warning
+ mi.pszName = BOLTUN_NOT_TO_CHAT;
+ mi.pszService = SERV_CONTACT_NOT_TO_CHAT;
+ hMenuItemNotToChat = Menu_AddContactMenuItem(&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 = Menu_AddContactMenuItem(&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/src/boltun.h b/plugins/Boltun/src/boltun.h
new file mode 100644
index 0000000000..914869387c
--- /dev/null
+++ b/plugins/Boltun/src/boltun.h
@@ -0,0 +1,74 @@
+//***********************************************************
+// 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.
+
+#define BOLTUN_KEY "Boltun"
+
+
+//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
+#define DEFAULT_MIND_FILE _T("boltun.mindw")
+
+//===============================================
+// 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/src/config.cpp b/plugins/Boltun/src/config.cpp
new file mode 100644
index 0000000000..4a88bf2df3
--- /dev/null
+++ b/plugins/Boltun/src/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/src/config.h b/plugins/Boltun/src/config.h
new file mode 100644
index 0000000000..c5bb20e700
--- /dev/null
+++ b/plugins/Boltun/src/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/src/resource.h b/plugins/Boltun/src/resource.h
new file mode 100644
index 0000000000..b4fa069c0e
--- /dev/null
+++ b/plugins/Boltun/src/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