Files
ttw_fmx_v10/forms/uGeneral.pas
T
2025-08-17 00:39:58 +03:00

1797 lines
51 KiB
ObjectPascal
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
получение доната
procedure TfrSettings.HandleWSDonate(aNick, aMessage, aSum: string);
получение сообщения
procedure TTTW_Bot.ttwIRCOnMessageRecord(aRecord: TTwitchChatMessage);
получение событий
ttw_ES.OnFollow := frOBS1.toEventWebServer;
ttw_ES.OnSub := frOBS1.toEventWebServer;
ttw_ES.OnGift := frOBS1.toEventWebServer;
ttw_ES.OnRaid := frOBS1.toEventWebServer;
}
unit uGeneral;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, DateUtils,
System.Variants, uTTWIRC, uTTWEventSub, uTTWAPI, uSoundManager, uKeyEvent,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.TabControl,
FMX.Controls.Presentation, FMX.StdCtrls, System.ImageList, FMX.ImgList,
FMX.Styles, ShellAPI, StrUtils, IdComponent, uRegExpr, uCustomEmoties,
fSettings, fAI, fNotify, fAutoActions, FMX.ListBox, fLog, fEvents,
System.Generics.Collections, utts, uGigaChat, uChatAPI, uMyTimer, uRecords,
System.IOUtils, fCommands, uDataBase, FMX.Edit, FMX.Colors, FMX.SpinBox,
windows, System.Skia, FMX.Skia, uCreateChat, uCreateNotify, fOBS, fTTS,
fPlayerWeb, uWebServerKandinsky, FMX.Memo.Types, FMX.ScrollBox, FMX.Memo,
fRevards;
type
TTTW_Bot = class(TForm)
V: TTabControl;
TabItem1: TTabItem;
TabItem2: TTabItem;
TabItem3: TTabItem;
TabItem4: TTabItem;
frSettings1: TfrSettings;
ImageList1: TImageList;
TabItem5: TTabItem;
Panel1: TPanel;
btnConnecting: TButton;
Label2: TLabel;
Label3: TLabel;
Label5: TLabel;
Label6: TLabel;
Label7: TLabel;
Label8: TLabel;
aiConnecting: TAniIndicator;
Label9: TLabel;
Label10: TLabel;
Label11: TLabel;
Label12: TLabel;
frAI1: TfrAI;
TabItem6: TTabItem;
TabItem7: TTabItem;
TabItem8: TTabItem;
TabItem9: TTabItem;
frNotify1: TfrNotify;
Label1: TLabel;
frAutoActions1: TfrAutoActions;
frOBS1: TfrOBS;
frLog1: TfrLog;
cbTheme: TComboBox;
Label15: TLabel;
frCommands1: TfrCommands;
SpeedButton1: TSpeedButton;
SpeedButton2: TSpeedButton;
SpeedButton3: TSpeedButton;
btnCreateChat: TButton;
frTTS1: TfrTTS;
GroupBox1: TGroupBox;
frPlayerWeb1: TfrPlayerWeb;
frEvents1: TfrEvents;
GroupBox2: TGroupBox;
cbHelloTTS: TCheckBox;
GroupBox3: TGroupBox;
frRevards1: TfrRevards;
procedure cbThemeChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
procedure SpeedButton3Click(Sender: TObject);
procedure SpeedButton2Click(Sender: TObject);
procedure frSettings1btnDAStartClick(Sender: TObject);
procedure frCommands1btnRandAddClick(Sender: TObject);
procedure frOBS1btnDeleteeChatClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure frTTS1btnSendClick(Sender: TObject);
procedure btnConnectingClick(Sender: TObject);
private
{ Private declarations }
procedure toSpeech(aText: string);
procedure OnTTWStatus(ASender: TObject; const AStatus: TIdStatus;
const AStatusText: string);
procedure ttwIRCOnMessageRecord(aRecord: TTwitchChatMessage);
procedure PlayNotifySound(aMod, aVip, aSub: Boolean);
procedure CheckBannedWords(const aText, dname, aID: string);
procedure ReadDB();
function PreprocessMessageText(const aText: string): string;
procedure UpdateWordCounters(const aText: string);
procedure decodeResponseSendToTTW(aResponseText, aCommandText,
adName: string);
function Pars(T_, text, _T: string): string;
procedure gptError(Msg: string);
function ResponsParserGroups(inMess: string): string;
function ResponsParserRandoms(inMess: string): string;
function ResponsParserCounters(inMess: string): string;
function ResponsParserStatic(inMess, adName, aCommandText: string): string;
function ResponsParserAPI(inMess, adName: string): string;
function ResponsParserGPT(inMess, aCommandText: string): string;
function ResponsParserAIGen(inMess, aCommandText: string): string;
function ResponsParserAIPic(inMess, aCommandText, aNick: string): string;
function ResponsParserSounds(inMess: string): string;
function ResponsParserText(inMess: string): string;
function ValidateInput: Boolean;
procedure ConnectProcedure;
procedure DisconnectProcedure;
procedure HandleTimers(Start: Boolean);
procedure ESError(aMsg: string);
procedure ESStatus(Sender: TObject; const ConnectionEvent: String;
StatusCode: Integer; const Description: String);
procedure ESOnSubOk(s: string);
public
{ Public declarations }
procedure toLog(aModule, aMethod, aMessage: string; aCode: Integer);
function FindUserRow(const aNick: string): Integer;
procedure GlobalExceptionHandler(Sender: TObject; E: Exception);
end;
var
TTW_Bot: TTTW_Bot;
myConst: TConst;
db: TSettingsDatabase;
appconst: TBotAppCfg;
ttw_IRS: TTTW;
ttw_ES: TTTW_ES;
ttw_API: TTTW_API;
userlist: TList<TUser>;
Kandinsky: TKandinsky_Web;
kePoints: TKE;
implementation
{$R *.fmx}
procedure TTTW_Bot.HandleTimers(Start: Boolean);
var
I: Integer;
begin
for I := 0 to high(frAutoActions1.listTimer) do
begin
if Assigned(frAutoActions1.FTimerList[I]) then
begin
if Start and (frAutoActions1.listTimer[I].Enable = 1) then
frAutoActions1.FTimerList[I].StartT
else
frAutoActions1.FTimerList[I].StopT;
end;
end;
end;
procedure TTTW_Bot.toSpeech(aText: string);
var
tts: ttts;
s, s1: string;
begin
s := ExtractFilePath(ParamStr(0)) + 'piper\piper.exe';
s1 := ExtractFilePath(ParamStr(0)) + 'piper\voices';
if frTTS1.cbVoices.ItemIndex = -1 then
exit;
tts := ttts.Create(s, s1);
try
tts.SetModel(frTTS1.cbVoices.text);
tts.TextToSpeech(aText, true);
finally
tts.Free;
end;
end;
procedure TTTW_Bot.GlobalExceptionHandler(Sender: TObject; E: Exception);
begin
try
TTW_Bot.toLog('GlobalException', E.ClassName, E.Message, 2);
except
// на случай, если логгер сам кинет исключение
end;
end;
procedure TTTW_Bot.ConnectProcedure;
var
rid: string;
BotToken, StreamerToken: string;
dol: Integer;
begin
BotToken := frSettings1.edtBotToken.text;
StreamerToken := frSettings1.edtBotTokenStreamer.text;
// Проверка бот-токена
if not ttw_API.ValidateTwitchToken('Бот', BotToken, dol) then
raise Exception.Create('Недействительный бот-токен');
Label12.text := inttostr(dol);
// Проверка стример-токена (если отличается)
if (BotToken <> StreamerToken) and not ttw_API.ValidateTwitchToken('Стример',
StreamerToken, dol) then
raise Exception.Create('Недействительный стример-токен');
Label11.text := inttostr(dol);
// Инициализация и подключение основного сервиса
ttw_IRS.Init(frSettings1.edtBotToken.text, frSettings1.edtChannel.text,
frSettings1.edtBotName.text);
ttw_IRS.Connect;
// Инициализация API
try
ttw_API.Init(frSettings1.edtBotClientID.text, frSettings1.edtBotToken.text,
IfThen(frSettings1.edtBotTokenStreamer.text = '',
frSettings1.edtBotToken.text, frSettings1.edtBotTokenStreamer.text),
frSettings1.edtChannel.text, frSettings1.edtBotName.text);
except
on E: Exception do
begin
toLog('uGeneral', 'ConnectProcedure.ttw_API.Init', E.Message, 2);
end;
end;
// Обработка ролей
rid := ttw_API.getRoomAndBot;
if rid = '' then
raise Exception.Create('Не удалось получить Room ID');
try
frRevards1.LoadCustomRevards;
except
on E: Exception do
begin
toLog('uGeneral', 'ConnectProcedure.LoadCustomRevards', E.Message, 2);
end;
end;
// Загрузка эмодзи и бейджей
try
frOBS1.ChatBadges.Clear;
ttw_API.getCustomChatBadges(frOBS1.ChatBadges);
ttw_API.getGlobalChatBadges(frOBS1.ChatBadges);
frOBS1.ChatEmotes.Clear;
ttw_API.GetChannelEmotes(frOBS1.ChatEmotes);
ttw_API.GetGlobalEmotes(frOBS1.ChatEmotes);
frOBS1.BTTV.getGlobal;
frOBS1.BTTV.getCustom(rid);
frOBS1.m7tv.getGlobal;
frOBS1.m7tv.getCustom(rid);
except
on E: Exception do
begin
toLog('uGeneral', 'ConnectProcedure.Emotes', E.Message, 2);
end;
end;
// Инициализация EventSub
try
if Assigned(ttw_ES) then
FreeAndNil(ttw_ES);
ttw_ES := TTTW_ES.Create(Self, frSettings1.edtBotTokenStreamer.text,
frSettings1.edtBotClientID.text, rid);
// Назначение обработчиков событий
ttw_ES.OnLog := toLog;
ttw_ES.OnError := ESError;
ttw_ES.OnGetCustomReward := frEvents1.ESOnGetCustomReward;
ttw_ES.OnStatus := ESStatus;
// ttw_ES.OnRAW := fRewards.ESOnRAW;
ttw_ES.OnSubOk := ESOnSubOk;
ttw_ES.OnFollow := frOBS1.toEventWebServer;
ttw_ES.OnSub := frOBS1.toEventWebServer;
ttw_ES.OnGift := frOBS1.toEventWebServer;
ttw_ES.OnRaid := frOBS1.toEventWebServer;
toLog('uGeneral', 'ConnectProcedure.ttw_ES', 'Создан', 0);
ttw_ES.Connect;
except
on E: Exception do
begin
toLog('uGeneral', 'ConnectProcedure.ttw_ES', E.Message, 2);
end;
end;
HandleTimers(true);
if frSettings1.cbDAAutoLogin.IsChecked then
begin
frSettings1.btnDAGetCodeClick(Self);
end;
end;
procedure TTTW_Bot.DisconnectProcedure;
begin
try
toLog('DisconnectProcedure', 'Start', 'Начало процедуры отключения', 3);
// 1. Отключаем IRS
toLog('DisconnectProcedure', 'ttw_IRS.Disconnect',
'Попытка отключения IRS', 3);
ttw_IRS.Disconnect;
// 2. Обрабатываем EventSub
if Assigned(ttw_ES) then
begin
toLog('DisconnectProcedure', 'ttw_ES.Disconnect',
'Попытка отключения ES', 3);
ttw_ES.Disconnect;
// Даем время на корректное завершение
Sleep(150);
toLog('DisconnectProcedure', 'ttw_ES.Free', 'Освобождение ES', 3);
FreeAndNil(ttw_ES);
end;
// 3. Очищаем интерфейс
{ if Assigned(fChatFrame) then
begin
fChatFrame.ChatBadges.Clear;
fChatFrame.ChatEmotes.Clear;
{ for I := 0 to fChatFrame.ChatWebServers.Count - 1 do
begin
fChatFrame.ChatWebServers[I].WebServerChat.ActiveServer(false);
end;
end; }
// 4. Останавливаем таймеры
HandleTimers(false);
frSettings1.FWSClient.Disconnect;
Label3.text := 'Disconnected';
Label6.text := 'Disconnected';
except
on E: Exception do
toLog('DisconnectProcedure', 'General', E.ClassName + ': ' +
E.Message, 2);
end;
end;
procedure TTTW_Bot.ESError(aMsg: string);
begin
toLog('uGeneral', 'ESError', aMsg, 2);
end;
procedure TTTW_Bot.ESOnSubOk(s: string);
begin
toLog('uGeneral', 'ESOnSubOk', s, 0);
end;
procedure TTTW_Bot.ESStatus(Sender: TObject; const ConnectionEvent: String;
StatusCode: Integer; const Description: String);
begin
Label6.text := ConnectionEvent;
end;
function TTTW_Bot.ValidateInput: Boolean;
begin
Result := false;
if Trim(frSettings1.edtBotToken.text) = '' then
begin
ShowMessage('Токен бота обязателен для заполнения!');
exit;
end;
if Trim(frSettings1.edtChannel.text) = '' then
begin
ShowMessage('Название канала обязательно для заполнения!');
exit;
end;
if (Trim(frSettings1.edtBotClientID.text) = '') then
begin
ShowMessage('Client ID обязателен!');
exit;
end;
Result := true;
end;
procedure TTTW_Bot.btnConnectingClick(Sender: TObject);
begin
if not ValidateInput then
exit;
btnConnecting.Enabled := false;
aiConnecting.Enabled := true;
aiConnecting.Visible := true;
try
if btnConnecting.text = 'Подключиться' then
ConnectProcedure
else
DisconnectProcedure;
TThread.Synchronize(nil,
procedure
begin
btnConnecting.text := IfThen(btnConnecting.text = 'Подключиться',
'Отключиться', 'Подключиться');
end);
except
on E: Exception do
ShowMessage('Ошибка подключения: ' + E.Message);
end;
aiConnecting.Enabled := false;
aiConnecting.Visible := false;
btnConnecting.Enabled := true;
end;
procedure TTTW_Bot.cbThemeChange(Sender: TObject);
begin
cbTheme.ItemIndex := cbTheme.Items.IndexOf(cbTheme.text);
if cbTheme.ItemIndex <> -1 then
TStyleManager.SetStyleFromFile(myConst.stlPath + cbTheme.text);
db.WriteSetting('cbTheme', inttostr(cbTheme.ItemIndex));
end;
procedure TTTW_Bot.FormCreate(Sender: TObject);
var
Path: string;
function GetPathToTestExe: string; // вернет папку romaming
begin
Result := GetEnvironmentVariable('APPDATA');
if Result <> '' then
Result := IncludeTrailingPathDelimiter(Result);
end;
begin
myConst.GeneralPath := ExtractFilePath(ParamStr(0));
myConst.AppDataPath := GetPathToTestExe + 'TTW_Bot\';
if not DirectoryExists(myConst.AppDataPath) then
CreateDir(myConst.AppDataPath);
myConst.DBPath := myConst.AppDataPath + 'settings.db';
if not DirectoryExists(myConst.AppDataPath + 'fonts') then
CreateDir(myConst.AppDataPath + 'fonts');
myConst.fontsPath := myConst.AppDataPath + 'fonts\';
if not DirectoryExists(myConst.AppDataPath + 'imgs') then
CreateDir(myConst.AppDataPath + 'imgs');
myConst.imgsPath := myConst.AppDataPath + 'imgs\';
if not DirectoryExists(myConst.AppDataPath + 'sounds') then
CreateDir(myConst.AppDataPath + 'sounds');
myConst.soundsPath := myConst.AppDataPath + 'sounds\';
if not DirectoryExists(myConst.AppDataPath + 'stl') then
CreateDir(myConst.AppDataPath + 'stl');
myConst.stlPath := myConst.AppDataPath + 'stl\';
if not DirectoryExists(myConst.AppDataPath + 'ytSongs') then
CreateDir(myConst.AppDataPath + 'ytSongs');
myConst.ytSongsPath := myConst.AppDataPath + 'ytSongs\';
myConst.SilentPlay := myConst.GeneralPath + 'SilentPlayer.exe';
myConst.ytPlay := myConst.GeneralPath + 'Player.exe';
myConst.cfg1 := myConst.GeneralPath + 'botapp.cfg';
db := TSettingsDatabase.Create(myConst.DBPath);
frAutoActions1.FTimerList := TObjectList<TMyTimerThread>.Create(false);
ReadDB;
frCommands1.frsgSounds.ObjectRecord := frCommands1.listSounds;
frCommands1.frsgSounds.TableName := 'listSounds';
frCommands1.frsgSounds.UpdateGrid;
frCommands1.frsgFiles.ObjectRecord := frCommands1.listFiles;
frCommands1.frsgFiles.TableName := 'listFiles';
frCommands1.frsgFiles.UpdateGrid;
frCommands1.frsgNeiro.ObjectRecord := frCommands1.listNeiro;
frCommands1.frsgNeiro.TableName := 'listNeiro';
frCommands1.frsgNeiro.UpdateGrid;
for Path in TDirectory.GetFiles(myConst.stlPath) do
cbTheme.Items.Add(ExtractFileName(Path));
cbTheme.ItemIndex := strtoint(db.ReadSetting('cbTheme', '-1'));
frLog1.FLogList := TList<TRLog>.Create;
frPlayerWeb1.Init;
userlist := TList<TUser>.Create;
ttw_IRS := TTTW.Create(Self);
ttw_IRS.OnMessageRecord := ttwIRCOnMessageRecord;
ttw_IRS.OnLog := toLog;
ttw_IRS.OnStatus := OnTTWStatus;
ttw_API := TTTW_API.Create(Self);
end;
procedure TTTW_Bot.OnTTWStatus(ASender: TObject; const AStatus: TIdStatus;
const AStatusText: string);
begin
Label3.text := AStatusText;
end;
procedure TTTW_Bot.FormDestroy(Sender: TObject);
begin
FreeAndNil(frOBS1.ChatBadges);
FreeAndNil(frOBS1.ChatEmotes);
FreeAndNil(frOBS1.ChatWebServers);
FreeAndNil(frOBS1.EventWebServers);
FreeAndNil(frOBS1.KandinskyWebServers);
FreeAndNil(frRevards1.CustomRewards);
frOBS1.BTTV.Free;
frOBS1.m7tv.Free;
userlist.Free;
kePoints.Free;
DisconnectProcedure;
FreeAndNil(ttw_IRS);
FreeAndNil(ttw_ES);
FreeAndNil(ttw_API);
if Assigned(Kandinsky) then
Kandinsky.Free;
//frSettings1.Destroy;
FreeAndNil(db);
FreeAndNil(frAutoActions1.FTimerList);
FreeAndNil(frLog1.FLogList);
inherited;
end;
function TTTW_Bot.FindUserRow(const aNick: string): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to userlist.Count - 1 do
if userlist[I].login = LowerCase(aNick) then
begin
Result := I;
Break;
end;
end;
function TTTW_Bot.PreprocessMessageText(const aText: string): string;
begin
Result := LowerCase(aText);
Result := StringReplace(Result, #$D, '', [rfReplaceAll]);
Result := StringReplace(Result, '󠀀', '', [rfReplaceAll]);
Result := Trim(Result) + ' ';
end;
procedure TTTW_Bot.UpdateWordCounters(const aText: string);
var
I: Integer;
Words: TArray<string>;
mode: Boolean;
procedure IncrementCounter(ARow: Integer);
var
Count: Integer;
begin
Count := frAutoActions1.listCounters[ARow].Count + 1;
frAutoActions1.listCounters[ARow].Count := Count;
db.SaveRecordArray<TCounter>('listCounters', frAutoActions1.listCounters);
frAutoActions1.UpdateGridFromArray;
end;
function StringToArray(const input: string): TArray<string>;
var
Delimiter: char;
Words: TArray<string>;
I: Integer;
begin
Delimiter := ',';
Words := input.Split([Delimiter]);
SetLength(Result, Length(Words));
for I := 0 to High(Words) do
Result[I] := Words[I].Trim;
end;
function ContainsAnyWord(const str: string;
const Words: array of string): Boolean;
var
I: Integer;
begin
for I := Low(Words) to High(Words) do
begin
if Pos(AnsiLowerCase(Words[I]), AnsiLowerCase(str)) > 0 then
begin
Result := true;
exit;
end;
end;
Result := false;
end;
begin
for I := 0 to high(frAutoActions1.listCounters) do
begin
mode := frAutoActions1.listCounters[I].auto = 1;
if mode then
begin
Words := StringToArray(frAutoActions1.listCounters[I].trigger);
if ContainsAnyWord(aText, Words) then
IncrementCounter(I);
end;
end;
end;
procedure TTTW_Bot.CheckBannedWords(const aText, dname, aID: string);
var
I: Integer;
rx: TRegExpr;
begin
rx := TRegExpr.Create;
try
for I := 0 to high(frAutoActions1.listBanWords) do
begin
rx.Expression := frAutoActions1.listBanWords[I].regexp;
if rx.Exec(aText) then
begin
if aID = '0' then
ttw_API.banUserTime(dname, 86400)
else
ttw_API.banUserTime(aID, 86400);
Break;
end;
end;
finally
rx.Free;
end;
end;
procedure TTTW_Bot.ttwIRCOnMessageRecord(aRecord: TTwitchChatMessage);
// обработка нового сообщения
var
processedText: string;
firstWord: string;
commandText: string;
responseText, s: string;
u: TUser;
I: Integer;
function ExtractFirstWord(var aText: string): string;
var
posSpace: Integer;
begin
posSpace := Pos(' ', aText);
if posSpace > 0 then
begin
Result := Copy(aText, 1, posSpace - 1);
Result := StringReplace(Result, ',', '', [rfReplaceAll]);
Result := StringReplace(Result, '.', '', [rfReplaceAll]);
Result := StringReplace(Result, '?', '', [rfReplaceAll]);
end
else
Result := aText;
end;
function IsCommand(const aWord: string): Boolean;
begin
Result := (Length(aWord) > 0) and (aWord[1] = '!');
end;
function ExtractCommandText(const aText, aFirstWord: string): string;
begin
Result := Copy(aText, Length(aFirstWord) + 1, Length(aText));
end;
function ProcessCommand(const aCommand: string): string;
var
I: Integer;
begin
Result := '';
for I := 0 to high(frCommands1.listCommands) do
begin
if ContainsText(aCommand, frCommands1.listCommands[I].R1) then
begin
Result := frCommands1.listCommands[I].R2;
Break;
end;
end;
end;
begin
if aRecord.Username = '' then
exit;
I := FindUserRow(aRecord.Username);
if I <> -1 then
begin // user есть
u := userlist[I];
end
else
begin
u.ID := aRecord.UserId;
u.login := aRecord.Username;
u.isVIP := false;
u.isModer := false;
u.isO := false;
u.created_at := StrToDate('01.01.1990');
u.follow_at := StrToDate('01.01.1990');
u.isO_today := false;
userlist.Add(u);
end;
I := FindUserRow(aRecord.Username);
if (u.isO) and (NOT u.isO_today) then
begin
ttw_API.shoutouts(u.ID);
u.isO_today := true;
end;
userlist[I] := u;
processedText := PreprocessMessageText(aRecord.Message);
UpdateWordCounters(processedText);
CheckBannedWords(processedText, aRecord.DisplayName, aRecord.UserId);
if (frCommands1.cbTextToSpeach.IsChecked) and (processedText[1] = '!') and
(processedText[2] = '!') and (processedText[3] = '!') then
begin
s := StringReplace(processedText, '!!!', '', [rfReplaceAll]);
s := Trim(s);
toSpeech(s);
exit;
end;
if (TTW_Bot.cbHelloTTS.IsChecked) and (aRecord.FirstMsg = 1) then
toSpeech('приветствую, ' + IfThen(aRecord.DisplayName <> '',
aRecord.DisplayName, aRecord.Username));
firstWord := ExtractFirstWord(processedText);
if IsCommand(firstWord) then
begin
commandText := ExtractCommandText(processedText, firstWord);
responseText := ProcessCommand(firstWord);
decodeResponseSendToTTW(responseText, commandText, aRecord.DisplayName);
end;
TThread.Queue(nil,
procedure
begin
frOBS1.MsgToWebServer(aRecord);
end);
PlayNotifySound((aRecord.Moder = 1), (aRecord.Vip = 1),
(aRecord.Subscriber = 1));
end;
procedure TTTW_Bot.PlayNotifySound(aMod, aVip, aSub: Boolean);
var
s: string;
sm: TSongMachine;
begin
sm := TSongMachine.Create;
try
if (aMod) and (frNotify1.chEnNotifyMod.IsChecked) and
(frNotify1.edtNotifyFileNameMod.text <> '') then
begin
if frNotify1.cbNotifyFileAgain1.IsChecked then
s := frNotify1.edtNotifyFileName.text
else
s := frNotify1.edtNotifyFileNameMod.text;
sm.PlaySilent(s, inttostr(round(frNotify1.tbNotifyVolumeMod.Value)));
exit;
end;
if (aVip) and (frNotify1.chEnNotifyVip.IsChecked) and
(frNotify1.edtNotifyFileNameVip.text <> '') then
begin
if frNotify1.cbNotifyFileAgain2.IsChecked then
s := frNotify1.edtNotifyFileName.text
else
s := frNotify1.edtNotifyFileNameVip.text;
sm.PlaySilent(s, inttostr(round(frNotify1.tbNotifyVolumeVip.Value)));
exit;
end;
if (aSub) and (frNotify1.chEnNotifySub.IsChecked) and
(frNotify1.edtNotifyFileNameSub.text <> '') then
begin
if frNotify1.cbNotifyFileAgain3.IsChecked then
s := frNotify1.edtNotifyFileName.text
else
s := frNotify1.edtNotifyFileNameSub.text;
sm.PlaySilent(s, inttostr(round(frNotify1.tbNotifyVolumeSub.Value)));
exit;
end;
if (frNotify1.chEnNotify.IsChecked) and
(frNotify1.edtNotifyFileName.text <> '') then
begin
s := frNotify1.edtNotifyFileName.text;
sm.PlaySilent(s, inttostr(round(frNotify1.tbNotifyVolume.Value)));
exit;
end;
finally
sm.Free;
end;
end;
procedure TTTW_Bot.frCommands1btnRandAddClick(Sender: TObject);
begin
frCommands1.btnRandAddClick(Sender);
end;
procedure TTTW_Bot.frOBS1btnDeleteeChatClick(Sender: TObject);
begin
frOBS1.btnDeleteeChatClick(Sender);
end;
procedure TTTW_Bot.frSettings1btnDAStartClick(Sender: TObject);
begin
frSettings1.btnDAStartClick(Sender);
end;
procedure TTTW_Bot.frTTS1btnSendClick(Sender: TObject);
begin
frTTS1.btnSendClick(Sender);
end;
procedure TTTW_Bot.ReadDB;
function XorDecryptToStrings(const InputFile, Key: string): TStrings;
var
InStream: TFileStream;
MemStream: TMemoryStream;
KeyBytes: TBytes;
KeyLen, KeyIndex: Integer;
B: Byte;
begin
KeyBytes := TEncoding.ANSI.GetBytes(Key);
KeyLen := Length(KeyBytes);
if KeyLen = 0 then
raise Exception.Create('Ключ не может быть пустым');
InStream := TFileStream.Create(InputFile, fmOpenRead);
try
MemStream := TMemoryStream.Create;
try
KeyIndex := 0;
while InStream.Position < InStream.Size do
begin
InStream.ReadBuffer(B, 1);
B := B xor KeyBytes[KeyIndex];
MemStream.WriteBuffer(B, 1);
KeyIndex := (KeyIndex + 1) mod KeyLen;
end;
MemStream.Position := 0;
Result := TStringList.Create;
try
Result.LoadFromStream(MemStream, TEncoding.ANSI);
except
Result.Free; // Освобождаем при ошибке загрузки
raise;
end;
finally
MemStream.Free;
end;
finally
InStream.Free;
end;
end;
// Загрузка компонентов настроек (TEdit, TCheckBox)
procedure LoadSettingsComponents;
var
I: Integer;
c: TComponent;
begin
for I := 0 to frSettings1.ComponentCount - 1 do
begin
c := frSettings1.Components[I];
if c is TEdit then
TEdit(c).text := db.ReadSetting(TEdit(c).Name)
else if c is TCheckBox then
TCheckBox(c).IsChecked := (db.ReadSetting(TCheckBox(c).Name) = 'True');
end;
db.FChannel := frSettings1.edtChannel.text;
end;
// Загрузка данных в гриды команд
procedure LoadGridsData;
begin
db.LoadRecordArray<TRandomCounters>('RandomCounters',
frCommands1.RandomCounters);
db.LoadRecordArray<TListCommands>('listSounds', frCommands1.listSounds);
db.LoadRecordArray<TListCommands>('listFiles', frCommands1.listFiles);
db.LoadRecordArray<TListCommands>('listNeiro', frCommands1.listNeiro);
db.LoadRecordArray<TListCommands>('listCommands', frCommands1.listCommands);
frCommands1.UpdateGridFromArray;
end;
// Загрузка списка групп
procedure LoadGroupNames;
begin
db.getGroupName(frCommands1.frGroupsRequest1.lbRandomGroup.Items);
end;
// Загрузка зашифрованного конфига
procedure LoadEncryptedConfig;
var
tempList: TStrings; // Временный список для результата
I: Integer;
begin
if not FileExists(myConst.cfg1) then
exit;
tempList := nil; // Инициализация
try
tempList := XorDecryptToStrings(myConst.cfg1, 'fgvasrgEFAXFAFAS');
for I := 0 to tempList.Count - 1 do
begin
var
eqPos := Pos('=', tempList[I]);
if eqPos > 0 then
begin
var
Key := Trim(Copy(tempList[I], 1, eqPos - 1));
var
Value := Trim(Copy(tempList[I], eqPos + 1, MaxInt));
if Key = 'k1' then
appconst.TTV_ClientID := Value
else if Key = 'k2' then
appconst.AI_GigaChat_AC := Value
else if Key = 'k3' then
appconst.AI_GigaChat_ClientID := Value
else if Key = 'k4' then
appconst.AI_ChatGPT_Token := Value
else if Key = 'k5' then
appconst.AI_DeepSeec_Token := Value
else if Key = 'k6' then
appconst.DA_ClientID := Value
else if Key = 'k7' then
appconst.DA_Sicret := Value
else if Key = 'k8' then
appconst.DA_URL := Value;
end;
end;
frSettings1.btnGetClientID.Visible := (appconst.TTV_ClientID <> '');
frAI1.btnGetAIDef.Visible := ((appconst.AI_GigaChat_AC <> '') and
(appconst.AI_GigaChat_ClientID <> '')) or
(appconst.AI_ChatGPT_Token <> '') or (appconst.AI_DeepSeec_Token <> '');
frSettings1.btnGetDADef.Visible := (appconst.DA_ClientID <> '') and
(appconst.DA_Sicret <> '') and (appconst.DA_URL <> '');
finally
tempList.Free; // Важно: освобождаем временный список!
end;
end;
// Загрузка настроек уведомлений
procedure LoadNotifySettings;
var
I: Integer;
c: TComponent;
begin
for I := 0 to frNotify1.ComponentCount - 1 do
begin
c := frNotify1.Components[I];
if c is TEdit then
TEdit(c).text := db.ReadSetting(TEdit(c).Name)
else if c is TCheckBox then
TCheckBox(c).IsChecked := (db.ReadSetting(TCheckBox(c).Name) = 'True')
else if c is TSwitch then
TSwitch(c).IsChecked := (db.ReadSetting(TSwitch(c).Name) = 'True')
else if c is TTrackBar then
TTrackBar(c).Value :=
strtoint(db.ReadSetting(TTrackBar(c).Name, '100'));
end;
end;
// Загрузка настроек ИИ
procedure LoadAISettings;
var
I: Integer;
c: TComponent;
ii: Integer;
// Настройки GigaChat
procedure SetupGigaChatSettings;
begin
frAI1.rbGC.IsChecked := true;
frAI1.Label45.text := 'ClientID';
frAI1.Label47.text := 'Autorization Code';
frAI1.Label1.Visible := false;
frAI1.edtAIP2.Visible := true;
frAI1.edtAIP2.Password := true;
frAI1.edtAIP3.Visible := false;
frAI1.cbOllama.Visible := false;
end;
// Настройки DeepSeek
procedure SetupDeepSeekSettings;
begin
frAI1.rbDS.IsChecked := true;
frAI1.Label45.text := 'API Token';
frAI1.Label47.text := '';
frAI1.Label1.Visible := false;
frAI1.edtAIP2.Visible := false;
frAI1.edtAIP3.Visible := false;
frAI1.cbOllama.Visible := false;
end;
// Настройки ChatGPT
procedure SetupChatGPTSettings;
begin
frAI1.rbCG.IsChecked := true;
frAI1.Label45.text := 'API Token';
frAI1.Label47.text := '';
frAI1.Label1.Visible := false;
frAI1.edtAIP2.Visible := false;
frAI1.edtAIP3.Visible := false;
frAI1.cbOllama.Visible := false;
end;
// Настройки кастомного ИИ
procedure SetupCustomAISettings;
begin
frAI1.RBCustom.IsChecked := true;
frAI1.Label45.text := 'API Token';
frAI1.Label47.text := 'URL';
frAI1.Label1.Visible := true;
frAI1.edtAIP2.Visible := true;
frAI1.edtAIP2.Password := false;
frAI1.edtAIP3.Visible := true;
frAI1.cbOllama.Visible := true;
frAI1.cbOllama.IsChecked := db.ReadSetting(frAI1.cbOllama.Name) = '1';
end;
begin
for I := 0 to frAI1.ComponentCount - 1 do
begin
c := frAI1.Components[I];
if c is TEdit then
TEdit(c).text := db.ReadSetting(TEdit(c).Name)
else if c is TCheckBox then
TCheckBox(c).IsChecked := db.ReadSetting(TCheckBox(c).Name) = '1';
end;
ii := strtoint(db.ReadSetting('aiIndex', '0'));
case ii of
0:
SetupGigaChatSettings;
1:
SetupDeepSeekSettings;
2:
SetupChatGPTSettings;
3:
SetupCustomAISettings;
end;
frSettings1.Init;
end;
// Загрузка гридов автоматических действий
procedure LoadAutoActionsGrids;
begin
db.LoadRecordArray<TListTimer>('listTimer', frAutoActions1.listTimer);
db.LoadRecordArray<TBanWord>('listBanWords', frAutoActions1.listBanWords);
db.LoadRecordArray<TCounter>('listCounters', frAutoActions1.listCounters);
frAutoActions1.initTimers;
frAutoActions1.UpdateGridFromArray;
end;
// Загрузка интеграций с ОБС
procedure LoadOBSGrids;
var
I: Integer;
begin
db.LoadRecordArray<TOBSChat>('listChats', frOBS1.listChats);
frOBS1.BTTV := TBTTV.Create;
frOBS1.m7tv := t7tv.Create;
frOBS1.ChatBadges := TList<TChatBadge>.Create;
frOBS1.ChatEmotes := TList<TEmotes>.Create;
frOBS1.ChatWebServers := TList<TChatWebServers>.Create;
for I := 0 to High(frOBS1.listChats) do
begin
frOBS1.CreateWebChat(frOBS1.listChats[I]);
end;
db.LoadRecordArray<TOBSNotify>('listNotify', frOBS1.listNotify);
frOBS1.EventWebServers := TList<TEventWebServers>.Create;
for I := 0 to High(frOBS1.listNotify) do
begin
frOBS1.CreateWebEvents(frOBS1.listNotify[I]);
end;
db.LoadRecordArray<TOBSKandinsky>('listKandinsky', frOBS1.listKandinsky);
frOBS1.KandinskyWebServers := TList<TKandinskyWebServers>.Create;
if (frAI1.edtKandiKey.text <> '') and (frAI1.edtKandiSecret.text <> '') then
begin
for I := 0 to High(frOBS1.listKandinsky) do
begin
frOBS1.CreateWebKandinsky(frOBS1.listKandinsky[I]);
end;
end;
frOBS1.UpdateGridFromArray;
end;
// Загрузка навыков
procedure LoadSkills;
begin
db.LoadRecordArray<TEventGlobal>('ListEvents', frEvents1.ListEvents);
frEvents1.UpdateGrid;
frRevards1.CustomRewards := TList<TCustomRevards>.Create;
frEvents1.CustomRewardEvents := TList<TCustomRewardEvent>.Create;
kePoints := TKE.Create(frEvents1.edtParams, frEvents1.cbKey1,
frEvents1.cbKey2, frEvents1.cbKey3);
frTTS1.btnUpdateVoicesClick(Self);
frTTS1.cbVoices.ItemIndex := strtoint(db.ReadSetting('cbVoices', '0'));
frTTS1.cbOutput.ItemIndex := strtoint(db.ReadSetting('cbOutput', '0'));
end;
begin
LoadAISettings;
LoadSkills;
LoadSettingsComponents;
LoadGridsData;
LoadGroupNames;
LoadEncryptedConfig;
LoadNotifySettings;
LoadOBSGrids;
LoadAutoActionsGrids;
end;
procedure TTTW_Bot.SpeedButton1Click(Sender: TObject);
begin
ShellExecute(0, 'open', pwidechar('https://www.twitch.tv/incadence'),
nil, nil, 1);
end;
procedure TTTW_Bot.SpeedButton2Click(Sender: TObject);
begin
// https://www.twitch.tv/kuznecogr
ShellExecute(0, 'open', pwidechar('https://www.twitch.tv/kuznecogr'),
nil, nil, 1);
end;
procedure TTTW_Bot.SpeedButton3Click(Sender: TObject);
begin
// https://www.flaticon.com/ru/authors/karacis
ShellExecute(0, 'open',
pwidechar('https://www.flaticon.com/ru/authors/karacis'), nil, nil, 1);
end;
procedure TTTW_Bot.toLog(aModule, aMethod, aMessage: string; aCode: Integer);
begin
TThread.Synchronize(nil,
procedure
var
ml: TRLog;
begin
// Инициализация всех полей записи
ml.rTime := Now;
case aCode of
0:
ml.rType := 'INFO';
1:
ml.rType := 'WARNING';
2:
ml.rType := 'ERROR';
3:
ml.rType := 'DEBUG';
else
ml.rType := 'UNKNOWN';
end;
ml.rModule := aModule; // string
ml.rMethod := aMethod; // string
ml.rMessage := aMessage; // string
// Добавляем запись в список
frLog1.FLogList.Add(ml);
// Обновляем грид
frLog1.UpdateGridFilters;
end);
end;
procedure TTTW_Bot.decodeResponseSendToTTW(aResponseText, aCommandText,
adName: string);
var
res: string;
ID: string;
RowIndex: Integer;
begin
res := '';
res := aResponseText;
RowIndex := FindUserRow(adName);
ID := userlist[RowIndex].ID;
if ID = '' then
ID := '0';
// ----------------------------------------Группы ответов
res := ResponsParserGroups(res);
// ----------------------------------------рандомы
res := ResponsParserRandoms(res);
// ----------------------------------------счетчики
res := ResponsParserCounters(res);
// ----------------------------------------константы
res := ResponsParserStatic(res, adName, aCommandText);
// ----------------------------------------апи команды
res := ResponsParserAPI(res, adName);
// ----------------------------------------GPT
res := ResponsParserGPT(res, aCommandText);
// ----------------------------------------звуки
res := ResponsParserSounds(res);
// ----------------------------------------text
res := ResponsParserText(res);
// ----------------------------------------AIGen
res := ResponsParserAIGen(res, aCommandText);
// ----------------------------------------AIPic
res := ResponsParserAIPic(res, aCommandText, adName);
if res <> '' then
ttw_IRS.sendMessage(res);
end;
function TTTW_Bot.Pars(T_, text, _T: string): string;
var
A, B: Integer;
begin
Result := '';
A := Pos(T_, text);
if A = 0 then
exit;
A := A + Length(T_);
B := Pos(_T, text, A);
if B > 0 then
Result := Copy(text, A, B - A);
end;
function TTTW_Bot.ResponsParserGroups(inMess: string): string;
var
ss, ss2, res: string;
sl: TStringList;
begin
res := inMess;
while Pos('{{', res) <> 0 do
begin
ss := Pars('{{', res, '}}');
sl := TStringList.Create;
randomize;
db.getGroupResponse(ss, sl);
ss2 := sl[random(sl.Count)];
res := StringReplace(res, '{{' + ss + '}}', ss2, [rfReplaceAll]);
end;
Result := res;
end;
function TTTW_Bot.ResponsParserSounds(inMess: string): string;
var
ss, res: string;
I, p: Integer;
sm: TSongMachine;
begin
res := inMess;
sm := TSongMachine.Create;
try
if ContainsText(res, '||') then
begin
// Находим позицию первого '||'
p := Pos('||', res);
// Копируем все, что идет после первого '||'
ss := Copy(res, p + 2, Length(res) - p - 1);
// +2 чтобы пропустить первый '||'
// Находим позицию второго '||'
p := Pos('||', ss);
if p > 0 then
begin
// Копируем текст между '||'
ss := Copy(ss, 1, p - 1);
if ss <> '' then
begin
// Ищем значение в sgSAFiles
for I := 0 to high(frCommands1.listSounds) do
begin
if frCommands1.listSounds[I].R1 = ss then
begin
if FileExists(frCommands1.listSounds[I].R2) then
begin
{ PlaySong(sgSAFiles.Cells[1, i],
inttostr(round(tbSoundVolume.Value)), True); }
sm.PlayPublic(frCommands1.listSounds[I].R2,
inttostr(round(100)));
end
else
begin
toLog('uGeneral', 'ResponsParserSounds',
'Файл "' + frCommands1.listSounds[I].R1 + '" не найден: ' +
frCommands1.listSounds[I].R2, 2);
end;
Break;
end;
end;
// Удаляем обработанный фрагмент из исходной строки
res := StringReplace(res, '||' + ss + '||', '', [rfReplaceAll]);
end;
end;
end;
finally
sm.Free;
end;
Result := res;
end;
function TTTW_Bot.ResponsParserText(inMess: string): string;
var
ss, res, fn, rres: string;
sll: TStringList;
I, p: Integer;
begin
res := inMess;
if ContainsText(res, '|(') then
begin
p := Pos('|(', res);
ss := Copy(res, p, Length(res) - p);
p := Pos('|(', ss, 2);
ss := Copy(ss, 1, p + 1);
ss := StringReplace(ss, '|(', '', [rfReplaceAll]);
if ss <> '' then
begin
for I := 0 to high(frCommands1.listFiles) do
begin
if frCommands1.listFiles[I].R1 = ss then
begin
fn := frCommands1.listFiles[I].R2;
Break;
end;
end;
sll := TStringList.Create;
try
sll.LoadFromFile(fn, TEncoding.UTF8);
rres := sll.text;
finally
sll.Free;
end;
if rres <> '' then
begin
if Length(rres) > 450 then
begin
rres := Copy(rres, 1, 450);
rres := rres + '...';
end;
rres := StringReplace(rres, #13#10, ' ', [rfReplaceAll]);
res := StringReplace(res, '|(' + ss + '|(', rres, [rfReplaceAll]);
end;
end;
end;
Result := res;
end;
function TTTW_Bot.ResponsParserRandoms(inMess: string): string;
var
r, res: string;
I: Integer;
function RandomInRange(MinValue, MaxValue: Integer): Integer;
begin
randomize;
Result := random(MaxValue - MinValue + 1) + MinValue;
end;
begin
res := inMess;
while ContainsText(res, '[[') do
begin
r := Pars('[[', res, ']]');
for I := 0 to high(frCommands1.RandomCounters) do
begin
if frCommands1.RandomCounters[I].rndName = r then
begin
res := StringReplace(res, '[[' + r + ']]',
inttostr(RandomInRange(frCommands1.RandomCounters[I].Ot,
frCommands1.RandomCounters[I].ToValue)), [rfReplaceAll]);
Break;
end;
end;
end;
Result := res;
end;
function TTTW_Bot.ResponsParserCounters(inMess: string): string;
var
r, res: string;
I: Integer;
begin
res := inMess;
while ContainsText(res, '``') do
begin
r := Pars('``', res, '``');
for I := 0 to high(frAutoActions1.listCounters) do
begin
if frAutoActions1.listCounters[I].counterName = r then
begin
res := StringReplace(res, '``' + r + '``',
inttostr(frAutoActions1.listCounters[I].Count), [rfReplaceAll]);
Break;
end;
end;
end;
Result := res;
end;
function TTTW_Bot.ResponsParserStatic(inMess, adName,
aCommandText: string): string;
var
res, RandomUserName: string;
begin
res := inMess;
res := StringReplace(res, '[USERNAME]', '@' + adName, [rfReplaceAll]);
res := StringReplace(res, '[TO]', aCommandText, [rfReplaceAll]);
if ContainsText(res, '[RANDOMUSER]') then
begin
if Pos('@', aCommandText) <> 0 then
begin
RandomUserName := aCommandText;
end
else
begin
randomize;
RandomUserName := '@' + userlist[random(userlist.Count)].login;
end;
res := StringReplace(res, '[RANDOMUSER]', RandomUserName, [rfReplaceAll]);
end;
Result := res;
end;
procedure TTTW_Bot.gptError(Msg: string);
begin
toLog('GPT', 'gptError', Msg, 2);
end;
function TTTW_Bot.ResponsParserAIGen(inMess, aCommandText: string): string;
var
res, GPTRequest: string;
GigaChat: TGigaChat;
ChatAPI: TChatAPI;
mystr: string;
p, I: Integer;
ss: string;
begin
res := inMess;
if ContainsText(res, '<|') then
begin
// Находим позицию первого '||'
p := Pos('<|', res);
// Копируем все, что идет после первого '<|'
ss := Copy(res, p + 2, Length(res) - p - 1);
// +2 чтобы пропустить первый '<|'
// Находим позицию второго '<|'
p := Pos('<|', ss);
if p > 0 then
begin
// Копируем текст между '<|'
ss := Copy(ss, 1, p - 1);
if ss <> '' then
begin
// Ищем значение в sgSAFiles
for I := 0 to high(frCommands1.listNeiro) do
begin
if frCommands1.listNeiro[I].R1 = ss then
begin
mystr := frCommands1.listNeiro[I].R2;
Break;
end;
end;
// Удаляем обработанный фрагмент из исходной строки
// res := StringReplace(res, '<|' + ss + '<|', mystr, [rfReplaceAll]);
end;
end;
end;
mystr := StringReplace(mystr, '[UT]', aCommandText, [rfReplaceAll]);
mystr := StringReplace(mystr, '[AI]', '', [rfReplaceAll]);
if (frAI1.edtAIP1.text = '') then
begin
GPTRequest :=
'тут должен быть ответ нейросети, но стример зажал логиниться';
end
else
begin
if frAI1.rbGC.IsChecked then // GigaChat
begin
GigaChat := TGigaChat.Create(Self, frAI1.edtAIP1.text, frAI1.edtAIP2.text,
frAI1.edtGPTPrefix.text);
try
try
GPTRequest := GigaChat.GetGPTRequest
('https://gigachat.devices.sberbank.ru/api/v1/chat/completions',
'GigaChat', mystr);
except
on E: Exception do
toLog('uGeneral', 'ResponsParserAIGen.gigachat', E.Message, 2);
end;
finally
GigaChat.Destroy;
end;
end;
if frAI1.rbDS.IsChecked then // DeepSeek
begin
ChatAPI := TChatAPI.Create(Self, frAI1.edtAIP1.text,
frAI1.edtGPTPrefix.text);
try
ChatAPI.OnError := gptError;
try
GPTRequest := ChatAPI.GetGPTRequest
('https://api.deepseek.com/chat/completions',
'deepseek-chat', mystr);
except
on E: Exception do
toLog('uGeneral', 'ResponsParserAIGen.deepseek', E.Message, 2);
end;
finally
ChatAPI.Destroy;
end;
end;
if frAI1.rbCG.IsChecked then // ChatGPT
begin
ChatAPI := TChatAPI.Create(Self, frAI1.edtAIP1.text,
frAI1.edtGPTPrefix.text);
try
try
GPTRequest := ChatAPI.GetGPTRequest
('https://api.openai.com/v1/chat/completions',
'gpt-3.5-turbo', mystr);
except
on E: Exception do
toLog('uGeneral', 'ResponsParserAIGen.openai', E.Message, 2);
end;
finally
ChatAPI.Destroy;
end;
end;
if frAI1.RBCustom.IsChecked then // Custom
begin
ChatAPI := TChatAPI.Create(Self, frAI1.edtAIP1.text,
frAI1.edtGPTPrefix.text);
try
try
GPTRequest := ChatAPI.GetGPTRequest(frAI1.edtAIP2.text,
frAI1.edtAIP3.text, mystr);
except
on E: Exception do
toLog('uGeneral', 'ResponsParserAIGen.custom', E.Message, 2);
end;
finally
ChatAPI.Destroy;
end;
end;
GPTRequest := StringReplace(GPTRequest, 'nn', ' ', [rfReplaceAll]);
GPTRequest := StringReplace(GPTRequest, 'nn', ' ', [rfReplaceAll]);
if GPTRequest = '' then
GPTRequest := 'ой, кажется нейронка пока не доступна';
end;
res := StringReplace(res, '<|' + ss + '<|', GPTRequest, [rfReplaceAll]);
Result := res;
end;
function TTTW_Bot.ResponsParserAIPic(inMess, aCommandText,
aNick: string): string;
var
res: string;
I: Integer;
begin
res := inMess;
if ContainsText(res, '[Kandinsky]') then
begin
for I := 0 to frOBS1.KandinskyWebServers.Count - 1 do
frOBS1.KandinskyWebServers[I].WebServerKandinsky.generate
(aCommandText, aNick);
res := StringReplace(res, '[Kandinsky]', '', [rfReplaceAll]);
end;
Result := res;
end;
function TTTW_Bot.ResponsParserAPI(inMess, adName: string): string;
var
res, follow, age, ID: string;
RowIndex: Integer;
u: TUser;
function GetPeriodEnding(n, r: Integer): string;
var
res: array [0 .. 3, 0 .. 2] of string;
begin
res[0, 0] := 'год';
res[0, 1] := 'года';
res[0, 2] := 'лет';
res[1, 0] := 'месяц';
res[1, 1] := 'месяца';
res[1, 2] := 'месяцев';
res[2, 0] := 'день';
res[2, 1] := 'дня';
res[2, 2] := 'дней';
res[3, 0] := 'раз';
res[3, 1] := 'раза';
res[3, 2] := 'раз';
if (n mod 10 = 1) and (n mod 100 <> 11) then
Result := res[r, 0]
else if (n mod 10 >= 2) and (n mod 10 <= 4) and
((n mod 100 < 10) or (n mod 100 >= 20)) then
Result := res[r, 1]
else
Result := res[r, 2];
end;
function GetDateDifference(const inputDate: string): string;
var
currentDate, targetDate: TDateTime;
years, months, days: Integer;
begin
try
targetDate := StrToDate(inputDate);
currentDate := Now;
years := YearsBetween(currentDate, targetDate);
targetDate := IncYear(targetDate, years);
months := MonthsBetween(currentDate, targetDate);
targetDate := IncMonth(targetDate, months);
days := DaysBetween(currentDate, targetDate);
Result := inttostr(years) + ' ' + GetPeriodEnding(years, 0) + ' ' +
inttostr(months) + ' ' + GetPeriodEnding(months, 1) + ' ' +
inttostr(days) + ' ' + GetPeriodEnding(days, 2);
except
on E: Exception do
begin
toLog('uGeneral', 'ResponsParserAPI.GetDateDifference', E.Message, 2);
Result := '';
end;
end;
end;
begin
res := inMess;
RowIndex := FindUserRow(adName);
u := userlist[RowIndex];
ID := u.ID;
if ContainsText(res, '[FOLLOW]') then
begin
if YearOf(u.follow_at) < 2000 then
u.follow_at := ttw_API.getFollow(ID);
follow := GetDateDifference(DateToStr(u.follow_at));
res := StringReplace(res, '[FOLLOW]', follow, [rfReplaceAll]);
end;
if ContainsText(res, '[AGE]') then
begin
if YearOf(userlist[RowIndex].created_at) < 2000 then
u := ttw_API.getUserbyLogin(u.login);
age := GetDateDifference(DateToStr(u.created_at));
res := StringReplace(res, '[AGE]', age, [rfReplaceAll]);
end;
userlist[RowIndex] := u;
if ContainsText(res, '[STAT]') then
begin
var
avg_viewers: Integer;
var
max_viewers: Integer;
var
hours_watched: Integer;
var
followers: Integer;
var
followers_total: Integer;
ttw_API.getTTWStat(db.FChannel, avg_viewers, max_viewers, hours_watched,
followers, followers_total);
var
resultStat: string;
resultStat := 'Статистика канала за месяц: Средний онлайн: ' +
inttostr(avg_viewers) + '; Максимальный онлайн: ' + inttostr(max_viewers)
+ '; Часов просмотра: ' + inttostr(hours_watched) +
'; Подписчиков за месяц: ' + inttostr(followers) + '; Всего подписчиков: '
+ inttostr(followers_total);
res := StringReplace(res, '[STAT]', resultStat, [rfReplaceAll]);
end;
Result := res;
end;
function TTTW_Bot.ResponsParserGPT(inMess, aCommandText: string): string;
var
res, GPTRequest: string;
GigaChat: TGigaChat;
ChatAPI: TChatAPI;
begin
res := inMess;
if ContainsText(res, '[AI]') then
begin
begin
if frAI1.rbGC.IsChecked then // GigaChat
begin
GigaChat := TGigaChat.Create(Self, frAI1.edtAIP1.text,
frAI1.edtAIP2.text, frAI1.edtGPTPrefix.text);
try
try
GPTRequest := GigaChat.GetGPTRequest
('https://gigachat.devices.sberbank.ru/api/v1/chat/completions',
'GigaChat', aCommandText);
except
on E: Exception do
toLog('uGeneral', 'ResponsParserGPT.gigachat', E.Message, 2);
end;
finally
GigaChat.Destroy;
end;
end;
if frAI1.rbDS.IsChecked then // DeepSeek
begin
ChatAPI := TChatAPI.Create(Self, frAI1.edtAIP1.text,
frAI1.edtGPTPrefix.text);
try
ChatAPI.OnError := gptError;
try
GPTRequest := ChatAPI.GetGPTRequest
('https://api.deepseek.com/chat/completions', 'deepseek-chat',
aCommandText);
except
on E: Exception do
toLog('uGeneral', 'ResponsParserGPT.deepseek', E.Message, 2);
end;
finally
ChatAPI.Destroy;
end;
end;
if frAI1.rbCG.IsChecked then // ChatGPT
begin
ChatAPI := TChatAPI.Create(Self, frAI1.edtAIP1.text,
frAI1.edtGPTPrefix.text);
try
try
GPTRequest := ChatAPI.GetGPTRequest
('https://api.openai.com/v1/chat/completions', 'gpt-3.5-turbo',
aCommandText);
except
on E: Exception do
toLog('uGeneral', 'ResponsParserGPT.openai', E.Message, 2);
end;
finally
ChatAPI.Destroy;
end;
end;
if frAI1.RBCustom.IsChecked then // Custom
begin
ChatAPI := TChatAPI.Create(Self, frAI1.edtAIP1.text,
frAI1.edtGPTPrefix.text);
try
try
GPTRequest := ChatAPI.GetGPTRequest(frAI1.edtAIP2.text,
frAI1.edtAIP3.text, aCommandText, frAI1.cbOllama.IsChecked);
except
on E: Exception do
toLog('uGeneral', 'ResponsParserGPT.custom', E.Message, 2);
end;
finally
ChatAPI.Destroy;
end;
end;
GPTRequest := StringReplace(GPTRequest, 'nn', ' ', [rfReplaceAll]);
GPTRequest := StringReplace(GPTRequest, 'nn', ' ', [rfReplaceAll]);
if GPTRequest = '' then
GPTRequest := 'ой, кажется нейронка пока не доступна';
end;
res := StringReplace(res, '[AI]', GPTRequest, [rfReplaceAll]);
end;
Result := res;
end;
end.