//*********************************************************** // 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 . // //*********************************************************** #include "boltun.h" 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), __PLUGIN_NAME, PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), __DESCRIPTION, __AUTHOR, __AUTHOREMAIL, __COPYRIGHT, __AUTHORWEB, UNICODE_AWARE, // {488C5C84-56DA-434F-96F1-B18900DEF760} {0x488c5c84, 0x56da, 0x434f, {0x96, 0xf1, 0xb1, 0x89, 0x0, 0xde, 0xf7, 0x60}} }; static HGENMENU hMenuItemAutoChat, hMenuItemNotToChat, hMenuItemStartChatting; #define MIND_DIALOG_FILTER _T("%s (*.mindw)\1*.mindw\1%s (*.*)\1*.*\1") #ifdef DEBUG_LOAD_TIME #include #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(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]; mir_snprintf(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; } 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(MCONTACT hContact) { if (db_get_b(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 ((db_get_b(hContact,"CList","NotOnList",0) == 1) && Config.TalkWithNotInList) return true; if (db_get_b(hContact, BOLTUN_KEY, DB_CONTACT_BOLTUN_AUTO_CHAT, FALSE) == TRUE) return true; return false; } static int MessageEventAdded(WPARAM wParam, LPARAM lParam) { MCONTACT hContact = wParam; HANDLE hDbEvent = (HANDLE)lParam; if (!BoltunAutoChat(hContact)) return 0; DBEVENTINFO dbei; ZeroMemory(&dbei, sizeof(dbei)); dbei.cbSize = sizeof(dbei); dbei.cbBlob = 0; dbei.cbBlob = db_event_getBlobSize(hDbEvent); if (dbei.cbBlob == -1) return 0; dbei.pBlob = (PBYTE)malloc(dbei.cbBlob); if (dbei.pBlob == NULL) return 0; db_event_get(hDbEvent, &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) db_event_markRead(hContact, hDbEvent); 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]; mir_sntprintf(filt, l, 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]; mir_sntprintf(message, 5000, 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 = { 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) { MCONTACT hContact = wParam; BOOL boltunautochat = db_get_b(hContact, BOLTUN_KEY, DB_CONTACT_BOLTUN_AUTO_CHAT, FALSE); BOOL boltunnottochat = db_get_b(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 { db_set_b(hContact, BOLTUN_KEY, DB_CONTACT_WARNED, FALSE); } } db_set_b(hContact, BOLTUN_KEY, DB_CONTACT_BOLTUN_AUTO_CHAT, (BYTE)boltunautochat); db_set_b(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) { MCONTACT hContact = wParam; StartChatting(hContact); return 0; } static int MessagePrebuild(WPARAM wParam, LPARAM lParam) { CLISTMENUITEM clmi = { sizeof(clmi) }; MCONTACT hContact = wParam; if (!blInit || (db_get_b(hContact,"CList","NotOnList",0) == 1)) { clmi.flags = CMIM_FLAGS | CMIF_GRAYED; Menu_ModifyItem(hMenuItemAutoChat, &clmi); Menu_ModifyItem(hMenuItemNotToChat, &clmi); } else { BOOL boltunautochat = db_get_b(hContact, BOLTUN_KEY, DB_CONTACT_BOLTUN_AUTO_CHAT, FALSE); BOOL boltunnottochat = db_get_b(hContact, BOLTUN_KEY, DB_CONTACT_BOLTUN_NOT_TO_CHAT, FALSE); clmi.flags = CMIM_FLAGS | CMIM_ICON | (boltunautochat ? CMIF_CHECKED : 0); clmi.hIcon = LoadIcon( GetModuleHandle(NULL), MAKEINTRESOURCE((boltunautochat ? IDI_TICK : IDI_NOTICK))); Menu_ModifyItem(hMenuItemAutoChat, &clmi); clmi.flags = CMIM_FLAGS | CMIM_ICON | (boltunnottochat ? CMIF_CHECKED : 0); clmi.hIcon = LoadIcon( GetModuleHandle(NULL), MAKEINTRESOURCE((boltunnottochat ? IDI_TICK : IDI_NOTICK))); Menu_ModifyItem(hMenuItemNotToChat, &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*/ HookEvent(ME_OPT_INITIALISE, MessageOptInit); /*initialize miranda hooks and services*/ HookEvent(ME_DB_EVENT_ADDED, MessageEventAdded); 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 = { sizeof(mi) }; mi.position = -50010002; //TODO: check the warning 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]; mir_sntprintf(path, SIZEOF(path), TranslateTS(FAILED_TO_LOAD_BASE), line, (const TCHAR*)Config.MindFileName); MessageBox(NULL, path, TranslateTS(BOLTUN_ERROR), MB_ICONERROR|MB_TASKMODAL|MB_OK); } 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]; mir_sntprintf(path, SIZEOF(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; }