Browse Source

- Поддержка "умных" ответов (варианты ответов на последнее входящее сообщение, предлагаемые сервером ICQ; по умолчанию отключено)

- Изменён способ создания потоков для выполнения асинхронных действий в попытке уменьшить количество подвисаний
- Исправлено открытие ссылок на файлы, закачанные на сервер ICQ (теперь в момент открытия требуется быть в сети)
- Отключен нерабочий антиспам бот, т.к. новые контакты теперь сразу добавляются в список
- Отмена пометки контактов как удалённых после их восстановления (похоже, такое возможно)
- Ускорено обновление КЛ
- Sciter обновлён до 4.4.5.4
master 1130v89
Mikanoshi 4 months ago
parent
commit
ba4b0b8edb
33 changed files with 544 additions and 316 deletions
  1. +9
    -0
      CHANGELOG
  2. BIN
      Distro/Modules/32/sciter.dll
  3. BIN
      Distro/Modules/64/sciter.dll
  4. +2
    -0
      Distro/RnQ1124_rus.utflng
  5. +25
    -5
      Distro/Template/chat.css
  6. +37
    -1
      Distro/Template/chat.htm
  7. +7
    -7
      Distro/Template/main.htm
  8. +1
    -1
      Distro/Template/preferences.css
  9. +4
    -0
      Distro/Template/preferences.htm
  10. +0
    -2
      Distro/Template/search.css
  11. +4
    -0
      Distro/Template/viewinfo.css
  12. +4
    -2
      Distro/Template/vlist.tis
  13. BIN
      Distro/template.zip.sample
  14. +2
    -2
      README.html
  15. +3
    -34
      RnQ/CLBox.pas
  16. +13
    -11
      RnQ/ChatBox.pas
  17. +3
    -3
      RnQ/HiddenForm.pas
  18. +2
    -3
      RnQ/HistAllSearch.pas
  19. +1
    -1
      RnQ/ICQ/ICQCommon.pas
  20. +65
    -21
      RnQ/ICQ/ICQSession.pas
  21. +8
    -0
      RnQ/ICQ/Protocol_ICQ.pas
  22. +2
    -2
      RnQ/RnQBuiltTime.inc
  23. +6
    -6
      RnQ/RnQx64.dproj
  24. +9
    -7
      RnQ/SQLiteDB.pas
  25. +16
    -10
      RnQ/SciterLib.pas
  26. +8
    -8
      RnQ/SpellCheck.pas
  27. +2
    -2
      RnQ/globalLib.pas
  28. BIN
      RnQ/images.res
  29. +7
    -4
      RnQ/iniLib.pas
  30. +4
    -2
      RnQ/prefSheet.pas
  31. +150
    -157
      RnQ/utilLib.pas
  32. +60
    -2
      for.RnQ/RQUtil.pas
  33. +90
    -23
      for.RnQ/RnQNet.pas

+ 9
- 0
CHANGELOG View File

@ -1,3 +1,12 @@
Изменения в сборке 89
- Поддержка "умных" ответов (варианты ответов на последнее входящее сообщение, предлагаемые сервером ICQ; по умолчанию отключено)
- Изменён способ создания потоков для выполнения асинхронных действий в попытке уменьшить количество подвисаний
- Исправлено открытие ссылок на файлы, закачанные на сервер ICQ (теперь в момент открытия требуется быть в сети)
- Отключен нерабочий антиспам бот, т.к. новые контакты теперь сразу добавляются в список
- Отмена пометки контактов как удалённых после их восстановления (похоже, такое возможно)
- Ускорено обновление КЛ
- Sciter обновлён до 4.4.5.4
Изменения в сборке 88
- Определение ICQ New/Web клиента (иконки в теме Fresh)
- Исправлен показ некоторых капсов в данных о контакте


BIN
Distro/Modules/32/sciter.dll View File


BIN
Distro/Modules/64/sciter.dll View File


+ 2
- 0
Distro/RnQ1124_rus.utflng View File

@ -2607,6 +2607,8 @@ UIN больше, чем
Анимированная прокрутка
[Remove leading and trailing empty lines from messages]
Удалять пустые строки в начале и в конце сообщений
[Display "smart" replies]
Отображать "умные" ответы
####Themes####
[Refresh]


+ 25
- 5
Distro/Template/chat.css View File

@ -826,7 +826,7 @@ body.msgPreview #backTabs {
}
.inputPanel {
flow: horizontal;
flow: vertical;
padding: 0 3dip 2dip 6dip;
size: *;
min-height: 40dip;
@ -843,7 +843,7 @@ body.msgPreview .inputPanel {
font-size: @mainFontSize;
text-selection: #fff @activeColor;
padding: 2dip;
background-color: #FFF;
background-color: #fff;
border: 1dip solid @frameBorderColor;
context-menu: selector(menu#inputmenu);
scroll-manner: scroll-manner(animation: true, wheel-step: 200%);
@ -856,6 +856,26 @@ body.msgPreview .inputPanel {
.input text:last-child {
padding-bottom: *;
}
.smartReplies {
flow: horizontal;
height: max-content;
padding: 0;
border-spacing: 5dip;
font-size: @baseFontSize;
font-family: @baseFontEmoji;
font-rendering-mode: snap-pixel;
}
.smartReplies > div {
size: max-content;
margin-top: 5dip;
padding: 1dip 5dip;
border: 1dip solid @btnBackColor;
}
.smartReplies > div:hover {
background-color: @btnBackColor;
border: 1dip solid @frameBorderColor;
cursor: pointer;
}
frameset[rows] > splitter {
margin: 0 5dip;
height: 3dip;
@ -1088,7 +1108,7 @@ body.msgPreview status-bar {
}
.imagegrid#stickersGrid {
width: max-content;
height: 445dip;
height: 430dip;
flow: vertical;
}
.imagecat {
@ -1122,7 +1142,7 @@ body.msgPreview status-bar {
background-repeat: no-repeat;
}
.imagecat#stickersCat {
width: 510dip;
width: 500dip;
height: max-content;
overflow-x: hidden-scroll;
overflow-y: hidden;
@ -1176,7 +1196,7 @@ body.msgPreview status-bar {
.imagelist .sesbtn > img {
display: block;
margin: *;
size: 107dip;
size: 102dip;
foreground-size: contain;
}
.imagelist .sesbtn:hover {


+ 37
- 1
Distro/Template/chat.htm View File

@ -26,6 +26,7 @@
var isActive = false,
initialLoad = true,
msgPreview = false,
lastSmartReplies = {},
rightClickedTime = "0",
rightClickedData = null,
scrollbarActive = false,
@ -39,6 +40,7 @@
showAvatar: true,
showSmileCaption: false,
showHintsInChat: true,
showSmartReplies: false,
autoCopy: true,
smoothFontRendering: true,
fontCodes: false,
@ -717,6 +719,7 @@
this.input = this.$(.input);
this.inputSplit = this.$(.inputSplit);
this.inputPanel = this.$(.inputPanel);
this.smartReplies = this.$(.smartReplies);
this.avatar = this.$(.avatar);
this.avatarimg = this.avatar.$(> img);
}
@ -894,6 +897,12 @@
};
this.updateSplitterWidth();
this.updateSmartReplies();
this.smartReplies.on("click", "div", :e {
this super.input.value = "";
this super.addToInput(e.target.text);
});
if (this.chatMessages.length == 0 && !this.historyStartReached) this.loadHistory();
}
@ -1515,7 +1524,7 @@
*/
function buildEvent(ev) {
var encrypted = ev.encrypted ? <div class="msgCryptImg" auto pic="key"></div> : "";
var prefix = ev.prefix !== "" ? <div class={ev.prefixCls} :patches={ev.patches ? ev.patches.clone() : false}>{ev.prefix}</div> : "";
var prefix = ev.prefix !== "" ? <div class={ev.prefixCls} :patches={ev.patches ? ev.patches.clone(true) : false}>{ev.prefix}</div> : "";
var statusImg = [];
if (ev.statusImg != "") statusImg.push(<div class="msgStatusImg" auto pic={ev.statusImg}></div>);
@ -1583,6 +1592,10 @@
var noFirstUnseen = this.firstUnseenEvent == null;
var evcnt = events ? events.length : 0;
var evlast = this.chat.last;
if (evcnt > 0) {
delete lastSmartReplies[this.chatid];
this.smartReplies.clear();
}
while (evlast && evcnt > 0) {
if (noFirstUnseen && !this.isInView(evlast)) this.firstUnseenEvent = evlast;
evlast = evlast.prior;
@ -1952,6 +1965,15 @@
this.avatar.@.addClass("hidden");
this.avatarimg.@#src = undefined;
}
function updateSmartReplies() {
this.smartReplies.clear();
var sreplies = lastSmartReplies[this.chatid];
if (!sreplies) return;
var replies = [];
for (var sreply in sreplies) replies.push(<div>{sreply}</div>)
this.smartReplies.append(replies);
}
}
class FileDropZone: Element {
@ -2274,6 +2296,7 @@
<splitter/>
<div class="avatar hidden"><img /></div>
</frameset>
<div class="smartReplies"></div>
</div>
</frameset>
</div>;
@ -2372,6 +2395,13 @@
}
});
self.style.documentRules({
selector: ".smartReplies",
properties: function(rule) {
return { display: (settings.showSmartReplies ? "block" : "none") }
}
});
var step = settings.wheelVelocity == 3 ? 100 : settings.wheelVelocity * 33;
pages.style#scroll-manner = "scroll-manner(animation: " + (settings.animatedScroll ? "true": "false") + ", wheel-step: " + step + "%)";
@ -2691,6 +2721,12 @@
$(#sbHint).text = hint;
}
function setSmartReplies(chat, suggests) {
lastSmartReplies[chat] = suggests;
var page = getChatPage(chat);
if (page) page.updateSmartReplies();
}
function makeStickerBtn(cat, st) {
var url;
if (st == 0) {


+ 7
- 7
Distro/Template/main.htm View File

@ -91,7 +91,7 @@
if (this.data)
this.data.extend(newData);
else
this.data = newData.clone();
this.data = newData;
this.merge(this.build(this.data));
}
@ -317,7 +317,7 @@
if (showIcon[1])
switch (showIcon[0].idx) {
case 1:
children.push(Status.build(data.status.clone())); break;
children.push(Status.build(data.status)); break;
case 2:
if (data.xstatus.status != "") children.push(<div key="2" class="xstatus">{data.xstatus.status}</div>); break;
case 3:
@ -364,7 +364,7 @@
var contactsData = cldata.contacts[data.divisor] ? cldata.contacts[data.divisor][data.group] : null;
if (contactsData)
for (var contact in Object.keys(contactsData))
contacts.push(Contact.build(contactsData[contact].clone()));
contacts.push(Contact.build(contactsData[contact]));
}
var count = cldata.getGroupCount(data.group, data.divisor).toString() + "/" + cldata.getTotalGroupCount(data.group);
var local = data.local ? <div class="local"></div> : "";
@ -433,13 +433,13 @@
var contactsData = cldata.contacts[data.divisor] ? cldata.contacts[data.divisor][0] : null;
if (contactsData)
for (var contact in Object.keys(contactsData))
contacts.push(Contact.build(contactsData[contact].clone()));
contacts.push(Contact.build(contactsData[contact]));
var groups = [];
var groupsData = cldata.groups[data.divisor];
if (groupsData)
for (var group in Object.keys(groupsData))
groups.push(Group.build(groupsData[group].clone(), true));
groups.push(Group.build(groupsData[group], true));
return <option :data={data} :expanded={true} :collapsed={false} key={data.hash} div={data.divisor} class="divisor"><caption><div class="fineline" /><div class="divtitle">{Native.DivisorToTitle(data.divisor)}</div><div class="divcount"></div><div class="fineline" /></caption>{contacts}{groups}</option>;
}
@ -702,7 +702,7 @@
if (!data) return;
var group = this.getGroup(data);
if (!group) {
var opt = Group.build(data.clone(), true);
var opt = Group.build(data, true);
var div = this.getDivisor(data);
if (data.order != 0) {
var position = div.length;
@ -724,7 +724,7 @@
if (!data) return;
var div = this.getDivisor(data);
if (!div) {
var opt = Divisor.build(data.clone());
var opt = Divisor.build(data);
if (data.order != 0) {
var position = this.length;
var exdivs = this.$$(> option.divisor);


+ 1
- 1
Distro/Template/preferences.css View File

@ -54,7 +54,7 @@ img:incomplete:not(:busy) {
}
* {
outline: none;
text-selection: #fff @activeColor;
text-selection: #fff @activeColor !important;
}
a, a:visited {


+ 4
- 0
Distro/Template/preferences.htm View File

@ -469,6 +469,7 @@
$(#antispambot).show();
else
$(#antispambot).hide();
$(#antispambot).state.disabled = true;
if ($(#spam_botusequest).value) {
$(#questansw1).show();
@ -1609,6 +1610,7 @@
<textarea uwp nowrap class="vtext" spellcheck="false" name="spam_badwords"></textarea>
</div>
</div>
<div hidden>
<h2>Anstispam-bot</h2>
<div class="row">
<div class="col">
@ -1644,6 +1646,7 @@
</div>
</div>
</div>
</div>
<h2>Ignore list</h2>
<div class="row">
<div class="col">
@ -1851,6 +1854,7 @@
<checkbox name="chat_smoothrender">Smooth font rendering</checkbox>
<checkbox name="chat_animatedscroll">Animated scrolling</checkbox>
<checkbox name="chat_msgtrim">Remove leading and trailing empty lines from messages</checkbox>
<checkbox name="chat_smartreplies">Display "smart" replies</checkbox>
</div>
</div>
<div class="rowm">


+ 0
- 2
Distro/Template/search.css View File

@ -179,13 +179,11 @@ select option {
flow: vertical;
border-spacing: 0;
overflow: hidden;
visibility: collapse;
height: 0dip;
opacity: 0;
transition: height 0.2s cubic-out, opacity 0.2s cubic-out;
}
#advanced.opened {
visibility: visible;
opacity: 1;
height: max-content;
}


+ 4
- 0
Distro/Template/viewinfo.css View File

@ -356,6 +356,10 @@ form checkbox[name=m_smsable] {
margin-bottom: 4dip !important;
}
form textarea[name=m_about] {
margin-bottom: 1px;
}
form radio[uwp] span {
line-height: 19dip;
}


+ 4
- 2
Distro/Template/vlist.tis View File

@ -146,8 +146,10 @@ class VList : Reactor.Component
top = Integer.max(0,total - middle);
} else if(bottom + top + middle < total)
bottom = top = 0;
this.style[#padding-top] = px(top);
this.style[#padding-bottom] = px(bottom);
this.style.set {
padding-top: px(top),
padding-bottom: px(bottom)
};
Element.update.call(this); // use original Element.update method.
}


BIN
Distro/template.zip.sample View File


+ 2
- 2
README.html View File

@ -1,8 +1,8 @@
<div style="line-height: 150%;">
<h1>R&Q 1130 Кастомная сборка</h1>
Номер сборки: 88<br>
Последнее обновление: 02.10.2020<br><br>
Номер сборки: 89<br>
Последнее обновление: 01.11.2020<br><br>
<div style="width: 100%; height: 1px; border-top: #D3D3D3 solid 1px;"></div>
<h2>Особенности интерфейса</h2>
- Интерфейс частично переведён на <a target="_blank" href="http://sciter.com/">движок Sciter</a>, его шаблоны написаны на HTML/CSS/TIScript и могут быть отредактированы<br>


+ 3
- 34
RnQ/CLBox.pas View File

@ -9,7 +9,7 @@ interface
uses
Winapi.Windows, Winapi.Messages, Vcl.Menus, System.SysUtils, System.Classes, System.Types, System.StrUtils, System.Variants, System.Rtti,
System.Threading, Generics.Collections, Vcl.Controls, Vcl.Graphics, Vcl.Forms,
Generics.Collections, Vcl.Controls, Vcl.Graphics, Vcl.Forms,
Sciter, SciterApi, RDGlobal, ICQCommon, ICQSession, ICQContacts, ICQConsts, Protocols_all, Nodes, globalLib, roasterLib, groupsLib,
RnQTrayLib, RnQTips, aboutDlg, BaseWindow;
@ -731,37 +731,6 @@ begin
Call('finishBuild');
end;
//TODO: Send contacts
//procedure TRnQmain.SendContactsAction(Sender: TObject);
//var
// // i:integer;
// // s : String;
// s: RawByteString;
// cnt: TICQContact;
// wnd: TselectCntsFrm;
// cl: TRnQCList;
//begin
// wnd := (Sender as Tcontrol).parent as TselectCntsFrm;
// cl := wnd.selectedList;
// if not cl.empty then
// begin
// s := (wnd.extra as Tincapsulate).str;
// if s > '' then
// begin
// // cnt := contactsDB.get(TICQContact, s);
// cnt := wnd.proto.getContact(s);
// begin
// Account.outbox.add(OE_CONTACTS, cnt, 0, cl);
// if Assigned(outboxFrm) then
// outboxFrm.updateList;
// end;
// end;
// end;
// cl.free;
// wnd.extra.free;
// wnd.close;
//end;
{ Native methods }
procedure DivisorToTitle(tag: Pointer; argc: UINT; argv: PSciterValue; retval: PSciterValue); cdecl;
@ -1627,13 +1596,13 @@ end;
procedure OnCLActivate(tag: Pointer; argc: UINT; argv: PSciterValue; retval: PSciterValue); cdecl;
begin
// Wait for active window to switch
TTask.Run(procedure
TThread.CreateAnonymousThread(procedure
begin
TThread.Queue(nil, procedure
begin
ApplyTransparency(AW_CL);
end);
end);
end).Start;
end;
procedure OnCLMouseEnter(tag: Pointer; argc: UINT; argv: PSciterValue; retval: PSciterValue); cdecl;


+ 13
- 11
RnQ/ChatBox.pas View File

@ -9,7 +9,7 @@ interface
uses
Winapi.Windows, Winapi.Messages,
System.SysUtils, System.Classes, System.Character, System.Threading, System.Types, System.StrUtils, System.DateUtils,
System.SysUtils, System.Classes, System.Character, System.Types, System.StrUtils, System.DateUtils,
System.NetEncoding, System.Variants, System.JSON, System.RegularExpressionsCore, System.RegularExpressionsConsts, System.Win.TaskbarCore,
Generics.Collections, Vcl.Controls, Vcl.Graphics, Vcl.Forms, Vcl.Imaging.PNGImage, Vcl.Taskbar,
Sciter, SciterApi, RDGlobal, RnQNet, ICQCommon, ICQContacts, history, events, utilLib, pluginLib, Stickers, GR32, SQLiteDB, BaseWindow;
@ -42,7 +42,7 @@ type
end;
TChatSettings = record
showSmiles, showRelTimes, showAvatar, showSmileCaption, showHintsInChat, autoCopy, smoothFontRendering, fontCodes,
showSmiles, showRelTimes, showAvatar, showSmileCaption, showHintsInChat, showSmartReplies, autoCopy, smoothFontRendering, fontCodes,
viewTextWrap, animatedScroll, quoteSelected, cursorBelow, alwaysOnTop: Boolean;
wheelVelocity, imageQuality, maxImgWidth, maxImgHeight, msgBuffer, sendOnEnter, spellErrorStyle, screenshotFormat: Integer;
cachePath, spellErrorColor, screenshotFilename, chatCSS: String;
@ -103,7 +103,6 @@ type
private
Preview: Boolean;
TaskBar: TTaskbarEx;
checkTask: ITask;
selectedText: String;
StartSel, EndSel: TDateTime;
IsWholeEvents: Boolean;
@ -251,6 +250,7 @@ type
procedure UpdateStatusBar;
procedure UpdateRelTimes;
procedure SetStatusBarHint(const hint: String);
procedure SetSmartReplies(Contact: TICQCOntact; const suggests: TArray<String>);
function ParseMessageBody(const body: String): String;
procedure ReplaceSmileMatch(Sender: TObject; var ReplaceWith: PCREString);
@ -976,14 +976,13 @@ const
MAXDELTA = 8;
SHAKETIMES = 150;
var
Task: ITask;
oRect, wRect: TRect;
begin
GetWindowRect(Window, wRect);
oRect := wRect;
Randomize;
Task := TTask.Create(procedure
TThread.CreateAnonymousThread(procedure
var
cnt: Integer;
begin
@ -995,8 +994,7 @@ begin
Sleep(10);
end;
MoveWindow(Window, oRect.Left, oRect.Top, oRect.Right - oRect.Left, oRect.Bottom - oRect.Top, True);
end);
Task.Start;
end).Start;
end;
function TChatBox.Pages2String: String;
@ -1274,13 +1272,13 @@ begin
Exit;
// Wait for active window to switch
TTask.Run(procedure
TThread.CreateAnonymousThread(procedure
begin
TThread.Queue(nil, procedure
begin
ApplyTransparency(AW_CHAT);
end);
end);
end).Start;
if Active and Assigned(UI.Chat.TaskBar) and Assigned(UI.Chat.TaskBar.TaskBar) then
UI.Chat.TaskBar.ActivateTab;
@ -1687,6 +1685,11 @@ begin
Call('setStatusBarHint', [hint]);
end;
procedure TChatBox.SetSmartReplies(Contact: TICQCOntact; const suggests: TArray<String>);
begin
Call('setSmartReplies', [Contact.UID, suggests]);
end;
procedure TChatBox.ScrollEvent(d: Integer);
begin
PageCall('', 'scrollEvent', [d]);
@ -3941,6 +3944,7 @@ begin
Settings.showAvatar := avatarShowInChat;
Settings.showSmileCaption := ShowSmileCaption;
Settings.showHintsInChat := ShowHintsInChat;
Settings.showSmartReplies := ShowSmartReplies;
Settings.autoCopy := autocopyHist;
Settings.wheelVelocity := wheelVelocity;
Settings.imageQuality := ChatImageQuality;
@ -4675,8 +4679,6 @@ end;
destructor TChatBox.Destroy;
begin
if Assigned(checkTask) then
checkTask.Cancel;
histories.Clear;
FreeAndNil(histories);
FreeAndNil(plugBtns);


+ 3
- 3
RnQ/HiddenForm.pas View File

@ -3,7 +3,7 @@ unit HiddenForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, System.Threading, Vcl.Forms, Vcl.Controls, Generics.Collections;
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Forms, Vcl.Controls, Generics.Collections;
type
THiddenForm = class(TForm)
@ -87,14 +87,14 @@ begin
LastMonCnt := cnt;
if CLBox.MonPositions.TryGetValue(LastMonCnt, Size) then
begin
TTask.Create(procedure
TThread.CreateAnonymousThread(procedure
begin
Sleep(1000);
TThread.Synchronize(nil, procedure
begin
UI.CL.SetPosition(Size.Left, Size.Top);
end);
end, TThreadPool.Default).Start;
end).Start;
end;
end;
end;


+ 2
- 3
RnQ/HistAllSearch.pas View File

@ -9,7 +9,7 @@ interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Types, System.Math, System.Variants, Generics.Collections,
Vcl.AppEvnts, Vcl.Menus, StdCtrls, ExtCtrls, RnQGlobal, DateUtils, Threading,
Vcl.AppEvnts, Vcl.Menus, StdCtrls, ExtCtrls, RnQGlobal, DateUtils,
Sciter, SciterAPI, GR32, ICQCommon, ICQContacts, ICQConsts, ICQSession, ChatBox, history, events;
{$I PubRTTI.inc}
@ -57,7 +57,6 @@ var
PreviewChat: TChatBox;
ContactOpened: TUID;
ContactHist: TArray<THistoryEntry>;
Task: ITask;
HistWidth: Integer;
DC: HDC;
@ -344,7 +343,7 @@ begin
SetLength(ContactHist, 0);
API.Sciter_UseElement(ElementHandle);
TTask.Create(procedure
TThread.CreateAnonymousThread(procedure
var
Evt: Thevent;
begin


+ 1
- 1
RnQ/ICQ/ICQCommon.pas View File

@ -103,7 +103,7 @@ var
implementation
uses
StrUtils, System.Threading,
StrUtils,
{$IFDEF UNICODE}
AnsiStrings,
{$ENDIF UNICODE}


+ 65
- 21
RnQ/ICQ/ICQSession.pas View File

@ -8,7 +8,7 @@ unit ICQSession;
interface
uses
Windows, SysUtils, Classes, Types, StrUtils, Math, Threading, JSON,
Windows, SysUtils, Classes, Types, StrUtils, Math, JSON,
Net.HttpClient, Net.URLClient, Generics.Defaults, Generics.Collections,
RnQGlobal, RnQNet, RDGlobal, RQUtil, RnQPrefsLib, ICQCommon, ICQContacts, ICQConsts, SynEcc, Stickers;
@ -73,7 +73,9 @@ type
IE_stickersearchupdate,
IE_MsgPatchUpdate,
IE_MsgPatchDelete
IE_MsgPatchDelete,
IE_SmartReply
);
TICQPhase = (
@ -189,6 +191,7 @@ type
eventStream: TMemoryStream;
eventWID: RawByteString;
eventEncoding: TEncoding;
eventArray: TArray<String>;
// acceptKey: String;
// ConnectSSL: Boolean;
@ -201,9 +204,8 @@ type
AutoReqXStatus: Boolean;
MyAvatarHash: String;
Pool: TThreadPool;
HttpPoll: THttpAsync;
PollingTask, ReconnectTask: ITask;
PollingTask, ReconnectTask: TAnonTask;
LastSearchPacks: TStickerPacks;
SrvHist: TSrvHist;
@ -277,6 +279,7 @@ type
procedure ProcessPermitDeny(const Data: TJSONObject);
procedure ProcessDiff(const Data: TJSONArray);
procedure ProcessXStatus(const Data: TJSONObject);
procedure ProcessSmartReply(const Data: TJSONObject);
procedure InitWebRTC;
function RequiresLogin: Boolean;
function RESTAvailable: Boolean;
@ -325,6 +328,7 @@ type
// property AuthNeeded: Boolean read P_AuthNeeded write SetAuthNeeded;
// property ShowInfo: Byte read P_ShowInfo write P_ShowInfo;
property Pwd: String read GetPwd write SetPwd;
property AimSid: String read fAimSid;
property Visibility: TVisibility read fVisibility write fVisibility;
property CurXStatus: Byte read CurXStatusVal write SetCurXStatus;
@ -587,7 +591,6 @@ begin
SavingMyInfo.Running := False;
fECCKeys.Generated := ecc_make_key(fECCKeys.PubEccKey, fECCKeys.PrivKey);
Pool := TThreadPool.Create;
HttpPoll := THttpAsync.Create(58);
HttpPoll.Callback := PollRequestDone;
end;
@ -824,11 +827,10 @@ begin
fRoster.Free;
SpamList.Free;
if Assigned(PollingTask) then
PollingTask.Cancel;
FreeAndNil(PollingTask);
if Assigned(ReconnectTask) then
ReconnectTask.Cancel;
FreeAndNil(ReconnectTask);
FreeAndNil(HttpPoll);
FreeAndNil(Pool);
FreeAndNil(ContactsDB);
end;
@ -1042,7 +1044,7 @@ begin
eventData := BaseURL + '?' + Query;
NotifyListeners(IE_serverGot);
TTask.Create(procedure
TThread.CreateAnonymousThread(procedure
var
RespStr: String;
begin
@ -1064,7 +1066,7 @@ begin
eventData := RespStr;
NotifyListeners(IE_serverSent);
end);
end, Pool).Start;
end).Start;
end;
function TICQSession.SendPresenceState(ForceOnline: Boolean = False): Boolean;
@ -3439,11 +3441,11 @@ begin
IfThen(ExtraError = '', '', #13#10 + ExtraError), False, mtError);
if Assigned(ReconnectTask) then
ReconnectTask.Cancel;
ReconnectTask := TTask.Create(procedure
FreeAndNil(ReconnectTask);
ReconnectTask := TAnonTask.Create(procedure
begin
Sleep(ICQErrorReconnectDelay);
if not (TTask.CurrentTask.Status = TTaskStatus.Canceled) then
if not TThread.Current.CheckTerminated then
TThread.Queue(nil, procedure
begin
// Try to use existing session, get new initial fetch url and start polling again. Go offline if all fails.
@ -3451,7 +3453,7 @@ begin
if not PingSession then
EndSession;
end);
end, Pool);
end);
ReconnectTask.Start;
end;
@ -3622,6 +3624,8 @@ ODS('Populating CL: ' + floattostr(TimingSeconds));
ProcessDiff(TJSONArray(edata))
else if etype.Value = 'status' then
ProcessXStatus(TJSONObject(edata))
else if etype.Value = 'suggest' then
ProcessSmartReply(TJSONObject(edata))
else
ODS('Unhandled event type: ' + etype.Value);
end;
@ -3666,17 +3670,17 @@ begin
else
begin
if Assigned(PollingTask) then
PollingTask.Cancel;
PollingTask := TTask.Create(procedure
FreeAndNil(PollingTask);
PollingTask := TAnonTask.Create(procedure
begin
Sleep(Max(100, Delay)); // Min 100ms between fetches, just in case :)
if not (TTask.CurrentTask.Status = TTaskStatus.Canceled) then
if not TThread.Current.CheckTerminated then
TThread.Queue(nil, procedure
begin
if Running then
PollURL(LastFetchBaseURL);
end);
end, Pool);
end);
PollingTask.Start;
end;
end;
@ -3813,7 +3817,9 @@ begin
Result.Bot := B;
if Buddy.GetValueSafe('deleted', I) then
Result.Deleted := I = 1;
Result.Deleted := I = 1
else
Result.Deleted := False;
Buddy.GetValueSafe('userType', Tmp);
Result.UserType := CT_UNK;
@ -4357,7 +4363,7 @@ var
MsgID: TMsgID;
evtmp: Thevent;
bTmp: Boolean;
Sticker, VoIP: TJSONValue;
Sticker, VoIP, Event: TJSONValue;
ExtSticker: TStringDynArray;
begin
Result := 0;
@ -4461,6 +4467,13 @@ begin
NotifyListeners(IE_MsgPatchDelete);
Exit;
end;
// Stranger danger!
Event := Msg.GetValue('event');
if Assigned(Event) then
if Event.GetValueSafe('type', sTmp) then
if (sTmp = 'warnAboutStranger') or (sTmp = 'noLongerStranger') then
Exit;
end;
// Process special R&Q messages
@ -4801,6 +4814,36 @@ begin
NotifyListeners(IE_xstatusChanged);
end;
procedure TICQSession.ProcessSmartReply(const Data: TJSONObject);
var
Contact: TICQContact;
Status: String;
SmartReply: TJSONValue;
SmartReplies: TJSONArray;
begin
if not Assigned(Data) or not (Data.GetValue('type').Value = 'text-smartreply') then // sticker-smartreply
Exit;
Contact := GetICQContact(Data.GetValue('sn').Value);
if not Assigned(Contact) then
Exit;
InitListenerVars(Contact, Now);
SmartReplies := TJSONArray(Data.GetValue('text'));
if SmartReplies.Count = 0 then
Exit;
for SmartReply in SmartReplies do
if Assigned(SmartReply) then
begin
SetLength(eventArray, Length(eventArray) + 1);
eventArray[High(eventArray)] := SmartReply.Value;
end;
NotifyListeners(IE_SmartReply);
end;
// Not working, for VoIP?
procedure TICQSession.InitWebRTC;
var
@ -5164,7 +5207,8 @@ begin
Params.Add('sn', UID);
Params.Add('lastRead', UIntToStr(MsgID));
// lastDelivered
// stranger
// "stranger": true
// "suspicious": true
SendRequest(True, BaseURL, MakeParams('POST', BaseURL, Params, False), 'Mark message as last read');


+ 8
- 0
RnQ/ICQ/Protocol_ICQ.pas View File

@ -795,6 +795,14 @@ case ev of
if Assigned(UI.Chat) then
UI.Chat.LoadSearchResults;
end;
IE_SmartReply:
begin
if Assigned(c) and Assigned(UI.Chat) then
begin
UI.Chat.SetSmartReplies(c, thisICQ.eventArray);
SetLength(thisICQ.eventArray, 0);
end;
end;
end;
if thisICQ.eventFlags and IF_offline > 0 then


+ 2
- 2
RnQ/RnQBuiltTime.inc View File

@ -1,2 +1,2 @@
{ 02.10.20 21:32:25 }
BuiltTime = 44106.8975116435;
{ 01.11.20 21:14:22 }
BuiltTime = 44136.8849837384;

+ 6
- 6
RnQ/RnQx64.dproj View File

@ -7,7 +7,7 @@
<MainSource>RnQx64.dpr</MainSource>
<Platform Condition="'$(Platform)'==''">Win32</Platform>
<ProjectGuid>{94E0A466-1C1B-48B7-B4DC-61F2C1EBC94C}</ProjectGuid>
<ProjectVersion>19.0</ProjectVersion>
<ProjectVersion>19.1</ProjectVersion>
<TargetedPlatforms>3</TargetedPlatforms>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
@ -215,7 +215,7 @@ $(PostBuildEvent)]]>
<DCC_MapFile>3</DCC_MapFile>
<Icon_MainIcon>Res\rnqold.ico</Icon_MainIcon>
<PostBuildEvent>
<![CDATA["C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe" sign /f "C:\SpeedProgs\Inet\Chat\RnQ\Sign\mikanoshi.pfx" /v /tr "http://time.certum.pl/" /d "RnQ" /du "https://code.highspec.ru/Mikanoshi/rnq" "$(OUTPUTPATH)"
<![CDATA["C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe" sign /f "C:\SpeedProgs\Inet\Chat\RnQ\Sign\mikanoshi.pfx" /v /tr "http://time.certum.pl/" /d "RnQ" /du "https://code.highspec.ru/Mikanoshi/rnq" "$(OUTPUTPATH)"
$(PostBuildEvent)]]>
</PostBuildEvent>
<VerInfo_Keys>FileVersion=0.11.9999.1130;OriginalFilename=R&amp;Q.exe;ProductName=R&amp;Q;ProductVersion=0.11.9999.1130;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=R&amp;Q is the best ICQ client out there!</VerInfo_Keys>
@ -276,8 +276,8 @@ $(PreBuildEvent)]]>
<DCC_MapFile>3</DCC_MapFile>
<Icon_MainIcon>Res\rnqold.ico</Icon_MainIcon>
<PostBuildEvent>
<![CDATA["D:\Progs\Other\UPX\upx.exe" -9 -q --lzma --compress-icons=2 "$(OUTPUTPATH)"
"C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe" sign /f "C:\SpeedProgs\Inet\Chat\RnQ\Sign\mikanoshi.pfx" /v /tr "http://time.certum.pl/" /d "RnQ" /du "https://code.highspec.ru/Mikanoshi/rnq" "$(OUTPUTPATH)"
<![CDATA["D:\Progs\Other\UPX\upx.exe" -9 -q --lzma --compress-icons=2 "$(OUTPUTPATH)"
"C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe" sign /f "C:\SpeedProgs\Inet\Chat\RnQ\Sign\mikanoshi.pfx" /v /tr "http://time.certum.pl/" /d "RnQ" /du "https://code.highspec.ru/Mikanoshi/rnq" "$(OUTPUTPATH)"
$(PostBuildEvent)]]>
</PostBuildEvent>
<PreBuildEvent>
@ -674,7 +674,7 @@ $(PreBuildEvent)]]>
<PreBuildEventIgnoreExitCode>False</PreBuildEventIgnoreExitCode>
<PreLinkEvent/>
<PreLinkEventIgnoreExitCode>False</PreLinkEventIgnoreExitCode>
<PostBuildEvent>&quot;C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe&quot; sign /f &quot;C:\SpeedProgs\Inet\Chat\RnQ\Sign\mikanoshi.pfx&quot; /v /tr &quot;http://time.certum.pl/&quot; /d &quot;RnQ&quot; /du &quot;https://code.highspec.ru/Mikanoshi/rnq&quot; &quot;$(OUTPUTPATH)&quot; </PostBuildEvent>
<PostBuildEvent>&quot;C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe&quot; sign /f &quot;C:\SpeedProgs\Inet\Chat\RnQ\Sign\mikanoshi.pfx&quot; /v /tr &quot;http://time.certum.pl/&quot; /d &quot;RnQ&quot; /du &quot;https://code.highspec.ru/Mikanoshi/rnq&quot; &quot;$(OUTPUTPATH)&quot;</PostBuildEvent>
<PostBuildEventIgnoreExitCode>False</PostBuildEventIgnoreExitCode>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Debug with madExcept' And '$(Platform)'=='Win32'">
@ -706,7 +706,7 @@ $(PreBuildEvent)]]>
<PreBuildEventIgnoreExitCode>False</PreBuildEventIgnoreExitCode>
<PreLinkEvent/>
<PreLinkEventIgnoreExitCode>False</PreLinkEventIgnoreExitCode>
<PostBuildEvent>&quot;D:\Progs\Other\UPX\upx.exe&quot; -9 -q --lzma --compress-icons=2 &quot;$(OUTPUTPATH)&quot; &amp;&amp;&quot;C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe&quot; sign /f &quot;C:\SpeedProgs\Inet\Chat\RnQ\Sign\mikanoshi.pfx&quot; /v /tr &quot;http://time.certum.pl/&quot; /d &quot;RnQ&quot; /du &quot;https://code.highspec.ru/Mikanoshi/rnq&quot; &quot;$(OUTPUTPATH)&quot; </PostBuildEvent>
<PostBuildEvent>&quot;D:\Progs\Other\UPX\upx.exe&quot; -9 -q --lzma --compress-icons=2 &quot;$(OUTPUTPATH)&quot;&amp;&amp;&quot;C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe&quot; sign /f &quot;C:\SpeedProgs\Inet\Chat\RnQ\Sign\mikanoshi.pfx&quot; /v /tr &quot;http://time.certum.pl/&quot; /d &quot;RnQ&quot; /du &quot;https://code.highspec.ru/Mikanoshi/rnq&quot; &quot;$(OUTPUTPATH)&quot;</PostBuildEvent>
<PostBuildEventIgnoreExitCode>False</PostBuildEventIgnoreExitCode>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Release with madExcept' And '$(Platform)'=='Win32'">


+ 9
- 7
RnQ/SQLiteDB.pas View File

@ -8,7 +8,7 @@ unit SQLiteDB;
interface
uses
Windows, Classes, Forms, Dialogs, Themes, Types, SysUtils, StrUtils, DateUtils, Generics.Collections, System.Threading, System.IniFiles,
Windows, Classes, Forms, Dialogs, Themes, Types, SysUtils, StrUtils, DateUtils, Generics.Collections, System.SyncObjs, System.IniFiles,
Variants, Data.DB, FireDAC.Comp.Client, FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Phys.SQLite, FireDAC.Phys.SQLiteDef,
FireDAC.Phys.SQLiteWrapper, FireDAC.Phys.SQLiteWrapper.Stat, FireDAC.DApt, FireDAC.Stan.Option, FireDAC.Stan.Def, FireDAC.Stan.Async,
FireDAC.Stan.Intf, FireDAC.ConsoleUI.Wait{, FireDAC.Moni.FlatFile},
@ -1269,7 +1269,7 @@ end;
procedure TSQLDatabase.ConvertHistory;
var
uins: TStringDynArray;
task: ITask;
FinishedEvent: TEvent;
begin
LogEvent('History: converting to SQLite');
@ -1279,7 +1279,8 @@ begin
if Length(uins) = 0 then
Exit;
task := TTask.Create(procedure()
FinishedEvent := TEvent.Create(nil, True, False, '');
TThread.CreateAnonymousThread(procedure()
var
hist: THistory;
qry: TFDQuery;
@ -1362,8 +1363,8 @@ begin
sql.Rollback;
end;
end;
end);
task.Start;
FinishedEvent.SetEvent;
end).Start;
if (Win32MajorVersion >= 6) and StyleServices.Enabled then
begin
@ -1390,8 +1391,9 @@ begin
FreeAndNil(taskDlg);
end;
while not task.Wait(100) do
Application.ProcessMessages;
while FinishedEvent.WaitFor(0) = wrTimeout do
if MsgWaitForMultipleObjects(0, Pointer(nil)^, False, 1000, QS_ALLINPUT) = WAIT_OBJECT_0 then
Application.ProcessMessages;
end;
// Stickers


+ 16
- 10
RnQ/SciterLib.pas View File

@ -8,7 +8,7 @@ unit SciterLib;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, System.UITypes, System.StrUtils, System.Threading, System.Variants, System.TypInfo,
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, System.UITypes, System.StrUtils, System.Variants, System.TypInfo,
Generics.Collections, Vcl.Graphics, Vcl.Forms, Vcl.Imaging.PNGImage, Vcl.Imaging.GIFImg, Vcl.Menus,
Sciter, SciterApi, RDGlobal, ICQCommon, ICQContacts, Nodes, CLBox, ChatBox, RQLog, RnQTips, PrefSheet, UtilLib;
@ -353,7 +353,7 @@ begin
end else if StartsText('sticker:', url) then
begin
RealURL := Copy(url, 9 + 1, Length(url));
TTask.Create(procedure
TThread.CreateAnonymousThread(procedure
var
fs: TMemoryStream;
begin
@ -371,7 +371,7 @@ begin
end else if StartsText('picker:', url) then
begin
RealURL := Copy(url, 8 + 1, Length(url));
TTask.Create(procedure
TThread.CreateAnonymousThread(procedure
var
fs: TMemoryStream;
begin
@ -465,13 +465,18 @@ begin
if not StartsText('http://', RealURL) and not StartsText('https://', RealURL) then
RealURL := 'http://' + RealURL;
if ContainsText(RealURL, 'files.icq.net/') then
if Account.AccProto.AimSid = '' then
begin
FileInfo := GetICQFileLinkInfo(RealURL);
//if FileInfo.mime.startsWith('image/') or FileInfo.mime.startsWith('video/') then
if FileInfo.is_previewable then
FileURL := FileInfo.dlink
RealURL := '';
OnlFeature(Account.AccProto);
end
else
begin
FileInfo := GetICQFileLinkInfo2(RealURL);
if FileInfo.dlink = '' then
FileURL := FileInfo.jsonlink
else
FileURL := FileInfo.jsonlink;
FileURL := FileInfo.dlink;
if not (FileURL = '') then
RealURL := FileURL;
end;
@ -562,7 +567,7 @@ begin
begin
Myself := True;
RAPI.RequestUse(RequestId);
TTask.Run(procedure
TThread.CreateAnonymousThread(procedure
var
ms: TMemoryStream;
fs: TFileStream;
@ -616,7 +621,7 @@ begin
FreeAndNil(fs);
FreeAndNil(ms);
end);
end).Start;
end;
Discard := False;
end else
@ -810,6 +815,7 @@ end;
function TSciterHelper.LoadTemplate(const Doc, Filename: String; Debug: Boolean = True): Boolean;
begin
SetOption(SCITER_SET_SCRIPT_RUNTIME_FEATURES, UINT_PTR(ALLOW_FILE_IO) or UINT_PTR(ALLOW_SOCKET_IO) or UINT_PTR(ALLOW_EVAL) or UINT_PTR(ALLOW_SYSINFO));
SetOption(SCITER_SET_PX_AS_DIP, UINT_PTR(False));
SetOption(SCITER_SET_DEBUG_MODE, UINT_PTR(DevMode and Debug));
if DevMode then
Result := LoadURL(FilePathToURL(myPath + Doc + '\' + Filename))


+ 8
- 8
RnQ/SpellCheck.pas View File

@ -8,7 +8,7 @@ unit SpellCheck;
interface
uses
System.SysUtils, System.Types, Winapi.Windows, Winapi.Messages, System.Classes, System.Character, System.Threading, System.StrUtils,
System.SysUtils, System.Types, Winapi.Windows, Winapi.Messages, System.Classes, System.Character, System.StrUtils,
Generics.Collections, Vcl.Graphics, Vcl.StdCtrls, Vcl.Menus, Vcl.Controls, Vcl.Forms, Winapi.ActiveX, MsSpellCheckLib_TLB, Variants;
{$I NoRTTI.inc}
@ -79,7 +79,7 @@ const
var
spellLangs: TList<DWORD> = nil;
spellGIT: IGlobalInterfaceTable = nil;
spellTask: ITask = nil;
spellTask: TAnonTask = nil;
spellText, spellTextFull: String;
procedure CreateThreading;
@ -177,9 +177,9 @@ begin
end;
if Assigned(spellTask) then
spellTask.Cancel;
FreeAndNil(spellTask);
spellTask := TTask.Create(procedure
spellTask := TAnonTask.Create(procedure
function getErrData(spellErr: ISpellingError): Variant;
var
@ -207,7 +207,7 @@ begin
// Freq, StartCount, StopCount: Int64;
// TimingSeconds: real;
begin
if TTask.CurrentTask.Status = TTaskStatus.Canceled then
if TThread.Current.CheckTerminated then
Exit;
CoInitializeEx(nil, COINIT_MULTITHREADED);
@ -218,7 +218,7 @@ begin
lngCnt := 0;
for cookie in spellLangs do
begin
if TTask.CurrentTask.Status = TTaskStatus.Canceled then
if TThread.Current.CheckTerminated then
Break;
if lngCnt = 0 then
begin
@ -263,7 +263,7 @@ begin
//QueryPerformanceCounter(StopCount);
//TimingSeconds := (StopCount - StartCount) / Freq;
//OutputDebugString(PChar(floattostr(TimingSeconds)));
if not (TTask.CurrentTask.Status = TTaskStatus.Canceled) then
if not TThread.Current.CheckTerminated then
TThread.Synchronize(nil, procedure
begin
if Assigned(UI.Chat) then
@ -278,7 +278,7 @@ end;
procedure StopSpellTask;
begin
if Assigned(spellTask) then
spellTask.Cancel;
FreeAndNil(spellTask);
end;
function GetSuggestions(word: String): Variant;


+ 2
- 2
RnQ/globalLib.pas View File

@ -19,7 +19,7 @@ uses
const
RQversion: Longword = $000A01FF; // remember: it's hex
RnQBuild = 1130;
RnQBuildCustom = 88;
RnQBuildCustom = 89;
DevMode = {$IFDEF DEBUG}True{$ELSE}False{$ENDIF};
PIC_CLIENT_LOGO = TPicName('rnq');
@ -446,7 +446,7 @@ var
MainCSS, ChatCSS: String;
BarPos, FilterPos: Integer;
// booleans
ShowHintsInCL, ShowHintsInChat, AnimateWindows,
ShowHintsInCL, ShowHintsInChat, ShowSmartReplies, AnimateWindows,
// showClientID,
ConnectOnConnection, ReopenChats, closeChatOnSend, ClosePageOnSingle, filterBarOnTop, rosterbarOnTop,
ShowMainBorder, NoBorderWithShadow, lockOnStart, closeAuthAfterReply, AutoConsumeEvents,


BIN
RnQ/images.res View File


+ 7
- 4
RnQ/iniLib.pas View File

@ -37,7 +37,7 @@ var
implementation
uses
Windows, Graphics, Classes, SysUtils, Menus, ShellAPI, Controls, Types, StrUtils, SysConst, Threading, SciterApi,
Windows, Graphics, Classes, SysUtils, Menus, ShellAPI, Controls, Types, StrUtils, SysConst, SciterApi,
RnQDialogs, RnQLangs, RnQNet, RnQTrayLib, RnQGlobal, RnQPrefsLib, RDUtils, RnQMacros, RnQStrings, RnQCrypt, RnQSysUtils,
RQUtil, RDGlobal, RQThemes, RDFileUtil, RnQGraphics32, RnQ_Avatars, RnQTips,
@ -179,6 +179,7 @@ begin
QuitConfirmation := False;
ShowHintsInCL := True;
ShowHintsInChat := True;
ShowSmartReplies := False;
closeChatOnSend := True;
ClosePageOnSingle := False;
RelativeTimeInChat := False;
@ -242,7 +243,7 @@ begin
warn := False;
addToHist := True;
ignoreauthNIL := False; // By Rapid D
useBot := False;
UseBot := False;
UseBotFromFile := False;
BotTriesCount := 3;
notNIL := False;
@ -536,6 +537,7 @@ begin
pp.addPrefInt('spam-bot-tries', spamfilter.BotTriesCount);
pp.addPrefBool('cl-hints-show', ShowHintsInCL);
pp.addPrefBool('chat-hints-show', ShowHintsInChat);
pp.addPrefBool('chat-smart-replies', ShowSmartReplies);
pp.addPrefBool('chat-close-on-send', closeChatOnSend);
pp.addPrefBool('chat-close-page-on-single', ClosePageOnSingle);
pp.addPrefBool('chat-relative-time', RelativeTimeInChat);
@ -700,6 +702,7 @@ begin
pp.getPrefBool('lock-on-start', lockOnStart);
pp.getPrefBool('cl-hints-show', ShowHintsInCL);
pp.getPrefBool('chat-hints-show', ShowHintsInChat);
pp.getPrefBool('chat-smart-replies', ShowSmartReplies);
pp.getPrefBool('chat-close-on-send', closeChatOnSend);
pp.getPrefBool('chat-close-page-on-single', ClosePageOnSingle);
pp.getPrefBool('chat-relative-time', RelativeTimeInChat);
@ -1707,7 +1710,7 @@ begin
// Bug with Sciter debug enabled
{$IFDEF DEBUG}
TTask.Run(procedure
TThread.CreateAnonymousThread(procedure
begin
Sleep(1000);
TThread.Synchronize(nil, procedure
@ -1715,7 +1718,7 @@ begin
if ReopenChats and not (Pages = '') then
UI.Chat.LoadPages(Pages);
end);
end);
end).Start;
{$ELSE}
if ReopenChats and not (Pages = '') then
UI.Chat.LoadPages(Pages);


+ 4
- 2
RnQ/prefSheet.pas View File

@ -8,7 +8,7 @@ unit prefSheet;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, System.Variants, System.IniFiles, System.Threading,
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, System.Variants, System.IniFiles,
Vcl.Forms, Vcl.Controls, Vcl.StdCtrls, Vcl.Graphics, Vcl.Dialogs, Vcl.Imaging.PNGImage,
Sciter, SciterApi, RnQPrefsLib, PluginLib, RQThemes, RQUtil, ICQCommon, BaseWindow;
@ -64,7 +64,7 @@ type
chat_autocopy, chat_singlemsg, chat_tabstatus, chat_cursorquote, chat_quoteselected, chat_textformat,
chat_chatontop, chat_pluginsabove, chat_tabhints, chat_wrapinseparate, chat_closeonempty, chat_closetabonsingle,
chat_smoothrender, chat_msgtrim, chat_smilescaptions,
chat_smoothrender, chat_msgtrim, chat_smartreplies, chat_smilescaptions,
chat_inimg, chat_outimg, chat_maximgwidth, chat_maximgheight, chat_enablestickers,
chat_videothumbs, chat_spellcheck, chat_animatedscroll,
@ -1138,6 +1138,7 @@ begin
ChatSmoothFontRendering := chat_smoothrender;
AnimatedScroll := chat_animatedscroll;
TrimMsgNewLines := chat_msgtrim;
ShowSmartReplies := chat_smartreplies;
EnableSpellCheck := chat_spellcheck;
RelativeTimeInChatDays := BoundInt(chat_reldays, 2, 31);
RelativeTimeInChatHours := BoundInt(chat_relhours, 0, 24);
@ -2501,6 +2502,7 @@ begin
chat_smoothrender := ChatSmoothFontRendering;
chat_animatedscroll := AnimatedScroll;
chat_msgtrim := TrimMsgNewLines;
chat_smartreplies := ShowSmartReplies;
chat_spellcheck := EnableSpellCheck;
chat_maximgwidthval := MaxChatImgWidthVal;
chat_maximgheightval := MaxChatImgHeightVal;


+ 150
- 157
RnQ/utilLib.pas View File

@ -9,8 +9,8 @@ interface
uses
Windows, SysUtils, Graphics, Dialogs, Classes, System.Generics.Defaults, System.Generics.Collections,
SyncObjs, Threading, Forms, Controls, StdCtrls, ExtCtrls, ComCtrls, Menus, Messages, Types, JSON, DateUtils,
StrUtils, globalLib, outboxLib, RnQNet, RnQZip, RDGlobal, events, ICQCommon, ICQConsts, ICQContacts, ICQSession, Murmur2, GR32;
SyncObjs, Forms, Controls, StdCtrls, ExtCtrls, ComCtrls, Menus, Messages, Types, JSON, DateUtils,
StrUtils, globalLib, outboxLib, RQUtil, RnQNet, RnQZip, RDGlobal, events, ICQCommon, ICQConsts, ICQContacts, ICQSession, Murmur2, GR32;
{$I PubRTTI.inc}
@ -33,9 +33,8 @@ type
TActionManager = class
private
TerminateEvents: TEvent;
ActiveActions: array [TActionKind] of ITask;
ActiveActions: array [TActionKind] of TAnonTask;
public
Pool: TThreadPool;
constructor Create;
destructor Destroy; override;
procedure Execute(Kind: TActionKind; Delay: Integer = 0);
@ -178,6 +177,7 @@ type
procedure ProcessICQLink(Data: String);
function AvatarUsePalette10: Boolean;
function IsTen: Boolean;
function IsEight: Boolean;
function IsEightOne: Boolean;
function IsElevated: Boolean;
function GetActiveMonitorCount: Integer;
@ -294,7 +294,7 @@ uses
{$IFDEF UNICODE}
AnsiStrings, Character,
{$ENDIF UNICODE}
Base64, RQUtil, RDFileUtil, RDUtils, RnQSysUtils,
Base64, RDFileUtil, RDUtils, RnQSysUtils,
RQThemes, RQLog, RnQdbDlg, RnQDialogs,
RnQLangs, RnQBinUtils, RnQGlobal, RnQCrypt, RnQPics,
RnQTrayLib, RnQTips, Hook, RnQPrefsLib, prefSheet,
@ -557,7 +557,7 @@ end;
procedure SaveAccountAsync(What: TActionKind = AK_SAVEALL);
begin
TTask.Create(procedure
TThread.CreateAnonymousThread(procedure
begin
if not Running then
Exit;
@ -1875,10 +1875,10 @@ const
SpamBotMsgFlags = IF_not_show_chat or IF_not_save_hist or IF_Simple;
var
ok: Boolean;
spmHist: Thistory;
i, j: integer;
ev0: Thevent;
s: string;
// spmHist: Thistory;
// i, j: integer;
// ev0: Thevent;
// s: string;
vProto: TICQSession;
vCnt: TICQContact;
tipsAllowed: Boolean;
@ -1888,7 +1888,7 @@ var
picsName: TPicName;
gr: TGroup;
dd: TDivisor;
events: Thevents;
// events: Thevents;
forceadd: Boolean;
Activity: Boolean;
begin
@ -1919,135 +1919,129 @@ begin
vProto := Account.AccProto;
if SpamFilter.UseBot then
if not (vProto.isInList(LT_ROSTER, vCnt) or notInList.exists(vCnt) or UI.Chat.IsChatOpen(vCnt)) then
begin
if kind in [EK_typingBeg .. EK_XstatusMsg, EK_incoming, EK_outgoing, EK_auth, EK_authDenied, EK_statuschange] then
exit
else if (IF_offline and ev.flags > 1) then
begin
end
else if kind in [EK_XstatusMsg] then
begin
end
else if ((kind = EK_MSG) and (Length(vCnt.antispam.lastQuests) > 0)
and (IsAnswer(vCnt.antispam.lastQuests, ev.getBodyText)) or IsUnverified(ev.getBodyText)) then
begin
vCnt.antispam.tries := 0;
SetLength(vCnt.antispam.lastQuests, 0);
try
spmHist := Thistory.Create(spamsFilename);
events := spmHist.getBySender(ev.who.UID);
if Length(events) > 0 then
for i := 0 to Length(events) - 1 do
begin
ev0 := events[i];
if ev0 = nil then
Continue;
// SAVE
if logpref.writehistory and (BE_save in behaviour[ev0.kind].trig) then
WriteToHistory(ev0, ev0.who);
forceadd := UI.Chat.IsChatOpen(ev0.who);
// OPENCHAT
if // not BossMode.isBossKeyOn and
(BE_openchat in behaviour[ev0.kind].trig) and not vProto.getStatusDisable.OpenChat then
begin
forceadd := not UI.Chat.OpenChat(ev0.who);
if not forceadd then
UI.Chat.SetUnreadEvent(ev0.who, ev0);
end;
// HISTORY
if BE_history in behaviour[ev0.kind].trig then
if forceadd then
UI.Chat.addEvent(ev0.who, ev0.clone);
// TRAY
// if (ev0.kind = EK_CONTACTS) and chatFrm.isVisible and (ev0.who = chatFrm.thisChat.who) then
// TselectCntsFrm.doAll(RnQmain, getTranslation('from %s', [ev0.who.displayed]),
// GetTranslation('Add selected contacts'), vProto, ev0.cl.clone, RnQmain.AddContactsAction, [sco_multi], @wnd, False, False)
// else
if BE_tray in behaviour[ev0.kind].trig then
eventQ.Add(ev0.clone);
// TIP
if tipsAllowed and not BossMode.isBossKeyOn and (BE_tip in behaviour[ev0.kind].trig) and
(ev0.flags and IF_offline = 0) and not vProto.getStatusDisable.tips then
try
UI.Tips.Add(ev0);
except end;
FreeAndNil(ev0);
end;
SetLength(events, 0);
spmHist.deleteBySender(ev.who.UID);
spmHist.Free;
except end;
if not IsUnverified(ev.getBodyText) then
OutboxAdd(OE_msg, vCnt, SpamBotMsgFlags, getTranslation(AntiSpamMsgs[2]))
end
else
begin
SetLength(ev.who.antispam.lastQuests, 0);
if logpref.writehistory and (BE_save in behaviour[ev.kind].trig) then
WriteToHistory(ev, vProto.getContact(spamsFilename));
if (BE_history in behaviour[ev.kind].trig) then
// if UI.Chat.chats.idxOfUIN(spamsFilename) >= 0 then
if UI.Chat.IsChatOpen(vProto.getContact(spamsFilename)) then
UI.Chat.addEvent(vProto.getContact(spamsFilename), ev.clone);
if ev.who.antispam.tries = SpamFilter.BotTriesCount then
begin
inc(ev.who.antispam.tries);
OutboxAdd(OE_msg, vCnt, SpamBotMsgFlags, AnsiReplaceStr(getTranslation(AntiSpamMsgs[3]), '%uin%', ev.who.uid));
exit;
end
else if vCnt.antispam.tries > SpamFilter.BotTriesCount then
exit
else
begin
Randomize;
if SpamFilter.UseBotFromFile and (Length(SpamFilter.Quests) > 0) then
begin
i := RandomRange(0, length(SpamFilter.Quests));
// if i >0 then
begin
with SpamFilter.Quests[i] do
begin
SetLength(vCnt.antispam.lastQuests, length(a));
for j := 0 to length(a) - 1 do
vCnt.antispam.lastQuests[j] := a[j];
s := q;
end;
end
{ else
begin
s := '';
ev.who.antispam.lastQuest := '';
end; }
end
else
begin
i := RandomRange(100, 999);
SetLength(vCnt.antispam.lastQuests, 1);
vCnt.antispam.lastQuests[0] := IntToStr(i);
s := TxtFromInt(i)
end;
if length(vCnt.antispam.lastQuests) > 0 then
begin
inc(vCnt.antispam.tries);
if SpamFilter.UseBotFromFile and (length(SpamFilter.Quests) > 0) then
OutboxAdd(OE_msg, vCnt, SpamBotMsgFlags, AnsiReplaceStr(getTranslation(AntiSpamMsgs[5]), '%attempt%',
IntToStr(SpamFilter.BotTriesCount + 1 - ev.who.antispam.tries)) + CRLF + getTranslation(AntiSpamMsgs[6])
+ CRLF + s)
else
OutboxAdd(OE_msg, vCnt, SpamBotMsgFlags, AnsiReplaceStr(getTranslation(AntiSpamMsgs[5]), '%attempt%',
IntToStr(SpamFilter.BotTriesCount + 1 - ev.who.antispam.tries)) + CRLF + getTranslation(AntiSpamMsgs[4]) +
CRLF + s);
exit;
end;
end;
end;
end;
// if SpamFilter.UseBot then
// if not (vProto.IsInList(LT_ROSTER, vCnt) or NotInList.exists(vCnt) or UI.Chat.IsChatOpen(vCnt)) then
// begin
// if kind in [EK_typingBeg .. EK_XstatusMsg, EK_incoming, EK_outgoing, EK_auth, EK_authDenied, EK_statuschange] then
// Exit
// else if (IF_offline and ev.flags > 1) then
// begin end else if kind in [EK_XstatusMsg] then
// begin end else if ((kind = EK_MSG) and (Length(vCnt.antispam.lastQuests) > 0)
// and (IsAnswer(vCnt.antispam.lastQuests, ev.getBodyText)) or IsUnverified(ev.getBodyText)) then
// begin
// vCnt.antispam.tries := 0;
// SetLength(vCnt.antispam.lastQuests, 0);
//
// try
// spmHist := Thistory.Create(spamsFilename);
// events := spmHist.getBySender(ev.who.UID);
// if Length(events) > 0 then
// for i := 0 to Length(events) - 1 do
// begin
// ev0 := events[i];
// if ev0 = nil then
// Continue;
//
// // SAVE
// if logpref.writehistory and (BE_save in behaviour[ev0.kind].trig) then
// WriteToHistory(ev0, ev0.who);
// forceadd := UI.Chat.IsChatOpen(ev0.who);
// // OPENCHAT
// if // not BossMode.isBossKeyOn and
// (BE_openchat in behaviour[ev0.kind].trig) and not vProto.getStatusDisable.OpenChat then
// begin
// forceadd := not UI.Chat.OpenChat(ev0.who);
// if not forceadd then
// UI.Chat.SetUnreadEvent(ev0.who, ev0);
// end;
// // HISTORY
// if BE_history in behaviour[ev0.kind].trig then