unit fOBS; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, uCustomEmoties, System.Variants, uWebServerChat, uWebServerEvents, fColorSettings, fFontSettings, System.Generics.Collections, uWebServerKandinsky, FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls, System.Rtti, FMX.Grid.Style, FMX.Grid, FMX.ScrollBox, FMX.Edit, FMX.Colors, FMX.ListBox, FMX.EditBox, FMX.SpinBox, FMX.Controls.Presentation, uRecords, System.Generics.Defaults, System.Character; type TChatWebServers = record port: integer; MaxMsg: integer; TimeMsg: integer; Freez: boolean; StyleBorderColor: string; StyleBlockColor: string; StyleBlockBorderSize: integer; StyleBlockPadding: integer; FontStyleDefault: string; FontColor: string; BColor: string; FontSize: integer; WebServerChat: TTTW_Chat; end; type TEventWebServers = record port: integer; typeEvent: integer; se: TStyleEvent; WebServerChat: TTTW_Events; end; type TKandinskyWebServers = record port: integer; WebServerKandinsky: TKandinsky_Web; end; type TfrOBS = class(TFrame) sgWebChats: TStringGrid; btnCreateOBSChat: TButton; btnDeleteeChat: TButton; Label1: TLabel; IntegerColumn1: TIntegerColumn; StringColumn1: TStringColumn; StringColumn2: TStringColumn; btnCreateOBSNotify: TButton; btnCreateOBSKandinsky: TButton; procedure btnDeleteeChatClick(Sender: TObject); procedure btnCreateOBSKandinskyClick(Sender: TObject); procedure btnCreateOBSChatClick(Sender: TObject); procedure btnCreateOBSNotifyClick(Sender: TObject); procedure sgWebChatsCellDblClick(const Column: TColumn; const Row: integer); private { Private declarations } function checkBttv(aMsg: string): string; function ReplaceEmotesInMessage(const MessageText, EmotesString: string): string; function FindEmoteByID(const ID: string): TEmotes; function GetBadgesHTML(Badges: string): string; public { Public declarations } listChats: TArray; listNotify: TArray; listKandinsky: TArray; BTTV: TBTTV; m7tv: t7tv; ChatBadges: Tlist; ChatEmotes: Tlist; ChatWebServers: Tlist; EventWebServers: Tlist; KandinskyWebServers: Tlist; procedure MsgToWebServer(const aRecord: TTwitchChatMessage); procedure toEventWebServer(aEvent: TFollowEvent); overload; // follow procedure toEventWebServer(aEvent: TSubEvent); overload; // sub procedure toEventWebServer(aEvent: TRaidEvent); overload; // raid procedure toEventWebServer(aEvent: TGiftEvent); overload; // gift procedure toEventWebServer(aNick, aSum, aText: string); overload; // donate procedure CreateWebEvents(eventsSettings: TOBSNotify); procedure CreateWebChat(chatSettings: TOBSChat); procedure UpdateGridFromArray; procedure AddChat(newRecord: TOBSChat); procedure EdtChat(newRecord: TOBSChat; oldPort: integer); procedure DelChat(aPort: integer); procedure AddNotify(newRecord: TOBSNotify); procedure EdtNotify(newRecord: TOBSNotify; oldPort: integer); procedure DelNotify(aPort: integer); procedure AddKandinsky(newRecord: TOBSKandinsky); procedure DelKandinsky(aPort: integer); procedure CreateWebKandinsky(aRecord: TOBSKandinsky); end; implementation {$R *.fmx} uses uGeneral, uCreateChat, uCreateNotify; { TfrOBS } function TfrOBS.checkBttv(aMsg: string): string; var Words: tstringlist; i: integer; CurrentWord, Url: string; begin Words := tstringlist.Create; try // Разбиваем строку на слова по пробелам Words.Delimiter := ' '; Words.StrictDelimiter := True; // Игнорировать повторяющиеся пробелы Words.DelimitedText := aMsg; // Обработка слов for i := 0 to Words.Count - 1 do begin CurrentWord := Words[i]; Url := BTTV.generateURL(CurrentWord); if Url = '' then Url := m7tv.generateURL(CurrentWord); if Url <> '' then Words[i] := Format('', [Url]); end; // Собираем результат result := Words.text; finally Words.Free; end; end; function TfrOBS.GetBadgesHTML(Badges: string): string; var BadgeList: TArray; CodeParts: TArray; CurrentCode, SetId, VersionId: string; Badge: TChatBadge; Version: TBadgeVersion; Found: boolean; begin // Разбиваем строку на отдельные бейдж-коды BadgeList := Badges.Split([',']); for CurrentCode in BadgeList do begin // Разделяем SetId и VersionId CodeParts := CurrentCode.Split(['/']); if Length(CodeParts) <> 2 then Continue; SetId := CodeParts[0]; VersionId := CodeParts[1]; Found := false; // Ищем соответствующий бейдж for Badge in ChatBadges do begin if Badge.SetId = SetId then begin // Ищем нужную версию for Version in Badge.Versions do begin if Version.ID = VersionId then begin // Формируем HTML-тег result := result + Format(' %s', [Version.ImageUrl1x, Version.Title, Version.Description]); Found := True; Break; end; end; if Found then Break; end; end; // Если не нашли - добавляем заглушку if not Found then result := result + ' '; end; end; procedure TfrOBS.MsgToWebServer(const aRecord: TTwitchChatMessage); var s: string; ms: TStyleChat; i: integer; begin s := checkBttv(aRecord.Message); if aRecord.Emotes <> '' then s := ReplaceEmotesInMessage(s, aRecord.Emotes); ms.Nick := GetBadgesHTML(aRecord.Badges) + '' + aRecord.DisplayName + ''; ms.Context := '' + s + ''; for i := 0 to ChatWebServers.Count - 1 do begin ms.FontColor := ChatWebServers[i].FontColor; ms.FontSize := ChatWebServers[i].FontSize; ms.FontFamily := '''' + ChatWebServers[i].FontStyleDefault + ''';'; ms.FontFamily := StringReplace(ms.FontFamily, '.ttf', '', [rfReplaceAll]); ms.BlockColor := ChatWebServers[i].StyleBlockColor; ms.BlockPadding := ChatWebServers[i].StyleBlockPadding; ms.MaxMsgCount := ChatWebServers[i].MaxMsg; ms.TimeMsg := ChatWebServers[i].TimeMsg; ms.BorderSize := ChatWebServers[i].StyleBlockBorderSize; ms.BorderColor := ChatWebServers[i].StyleBorderColor; ms.BColor := ChatWebServers[i].BColor; ChatWebServers[i].WebServerChat.AddMessage(ms); end; end; function TfrOBS.ReplaceEmotesInMessage(const MessageText, EmotesString: string): string; var ProcessedEmotes: TDictionary; Parts: tstringlist; i, ColonPos: integer; EmoteID: string; Emote: TEmotes; ResultText: string; begin result := MessageText; if EmotesString.Trim = '' then Exit; // Используем словарь для отслеживания уже обработанных эмодзи ProcessedEmotes := TDictionary.Create; Parts := tstringlist.Create; try // Разделяем общую строку смайлов по '/' Parts.StrictDelimiter := True; Parts.Delimiter := '/'; Parts.DelimitedText := EmotesString; ResultText := MessageText; // Обрабатываем каждую часть for i := 0 to Parts.Count - 1 do begin ColonPos := Pos(':', Parts[i]); if ColonPos = 0 then Continue; // Извлекаем ID смайла EmoteID := Copy(Parts[i], 1, ColonPos - 1); // Пропускаем если уже обрабатывали этот эмодзи if ProcessedEmotes.ContainsKey(EmoteID) then Continue; // Получаем данные смайла Emote := FindEmoteByID(EmoteID); if Emote.ID = '' then Continue; var imgUrl: string; if Emote.topImage <> '' then imgUrl := Emote.topImage else if Emote.images.Url4x <> '' then imgUrl := Emote.images.Url4x else Continue; // Пропускаем если нет URL // Заменяем все вхождения имени эмодзи ResultText := StringReplace(ResultText, Emote.name, Format('', [imgUrl]), [rfReplaceAll]); // Помечаем эмодзи как обработанный ProcessedEmotes.Add(EmoteID, ''); end; result := ResultText; finally ProcessedEmotes.Free; Parts.Free; end; end; procedure TfrOBS.AddChat(newRecord: TOBSChat); begin SetLength(listChats, Length(listChats) + 1); listChats[High(listChats)] := newRecord; UpdateGridFromArray; db.SaveRecordArray('listChats', listChats); CreateWebChat(newRecord); end; procedure TfrOBS.AddKandinsky(newRecord: TOBSKandinsky); begin SetLength(listKandinsky, Length(listKandinsky) + 1); listKandinsky[High(listKandinsky)] := newRecord; UpdateGridFromArray; db.SaveRecordArray('listKandinsky', listKandinsky); CreateWebKandinsky(newRecord); end; procedure TfrOBS.AddNotify(newRecord: TOBSNotify); begin SetLength(listNotify, Length(listNotify) + 1); listNotify[High(listNotify)] := newRecord; UpdateGridFromArray; db.SaveRecordArray('listNotify', listNotify); CreateWebEvents(newRecord); end; procedure TfrOBS.btnCreateOBSChatClick(Sender: TObject); var dport, i: integer; begin dport := 8080; for i := 0 to sgWebChats.RowCount - 1 do begin if strtoint(sgWebChats.Cells[0, i]) >= dport then dport := strtoint(sgWebChats.Cells[0, i]) + 1; end; fCreateChat.sbWebServerPort.Value := dport; fCreateChat.isEdit := false; fCreateChat.Show; end; procedure TfrOBS.btnCreateOBSKandinskyClick(Sender: TObject); var dport: integer; i: integer; rk: TOBSKandinsky; begin dport := 8080; for i := 0 to sgWebChats.RowCount - 1 do begin if strtoint(sgWebChats.Cells[0, i]) >= dport then dport := strtoint(sgWebChats.Cells[0, i]) + 1; end; rk.port := dport; AddKandinsky(rk); end; procedure TfrOBS.btnCreateOBSNotifyClick(Sender: TObject); var dport, i: integer; begin dport := 8080; for i := 0 to sgWebChats.RowCount - 1 do begin if strtoint(sgWebChats.Cells[0, i]) >= dport then dport := strtoint(sgWebChats.Cells[0, i]) + 1; end; fCreateNotify.sbWebServerPort.Value := dport; fCreateNotify.isEdit := false; fCreateNotify.Show; end; procedure TfrOBS.btnDeleteeChatClick(Sender: TObject); begin if sgWebChats.Cells[1, sgWebChats.Row] = 'Чат' then begin DelChat(strtoint(sgWebChats.Cells[0, sgWebChats.Row])); end; if sgWebChats.Cells[1, sgWebChats.Row] = 'Kandinsky' then begin DelKandinsky(strtoint(sgWebChats.Cells[0, sgWebChats.Row])); end; if sgWebChats.Cells[1, sgWebChats.Row] = 'Оповещение' then begin DelNotify(strtoint(sgWebChats.Cells[0, sgWebChats.Row])); end; end; procedure TfrOBS.CreateWebChat(chatSettings: TOBSChat); var ChatWebServer: TChatWebServers; fonts: tstringlist; f: TfrColorSettings; t: TfrFontSettings; procedure LoadFontList(const mySL: tstringlist); var SearchRec: TSearchRec; begin if not DirectoryExists(myConst.fontsPath) then CreateDir(myConst.fontsPath); if FindFirst(IncludeTrailingPathDelimiter(myConst.fontsPath) + '*.*', faArchive, SearchRec) = 0 then try repeat if (SearchRec.Attr and faAnyFile) = SearchRec.Attr then begin mySL.Add(SearchRec.name); end; until FindNext(SearchRec) <> 0; finally System.SysUtils.FindClose(SearchRec); end; end; begin fonts := tstringlist.Create; f := TfrColorSettings.Create(self); t := TfrFontSettings.Create(self); try LoadFontList(fonts); ChatWebServer.WebServerChat := TTTW_Chat.Create(fonts, chatSettings.port, f.ccbBColor.Items[chatSettings.ColorBackground]); ChatWebServer.port := chatSettings.port; ChatWebServer.MaxMsg := chatSettings.MaxCountMess; ChatWebServer.TimeMsg := chatSettings.TimeMess; ChatWebServer.Freez := chatSettings.Freez = 1; ChatWebServer.StyleBorderColor := f.ccbStyleBorderColor.Items [chatSettings.ColorBorder]; ChatWebServer.StyleBlockColor := chatSettings.ColorBlock; ChatWebServer.StyleBlockBorderSize := chatSettings.SolidBorder; ChatWebServer.StyleBlockPadding := chatSettings.Paddings; ChatWebServer.FontStyleDefault := t.cbFontStyleDefault.Items [chatSettings.StyleFont]; ChatWebServer.FontColor := t.ccbFontColor.Items[chatSettings.ColorFont]; ChatWebServer.BColor := f.ccbBColor.Items[chatSettings.ColorBorder];; ChatWebServer.FontSize := chatSettings.SizeFont; ChatWebServers.Add(ChatWebServer); ChatWebServers[ChatWebServers.Count - 1].WebServerChat.ActiveServer(True); ChatWebServers[ChatWebServers.Count - 1].WebServerChat.SetDeleteMode (not ChatWebServer.Freez, ChatWebServer.MaxMsg); finally fonts.Free; f.Free; t.Free; end; end; procedure TfrOBS.CreateWebEvents(eventsSettings: TOBSNotify); var EventWebServer: TEventWebServers; fonts: tstringlist; f: TfrColorSettings; t: TfrFontSettings; procedure LoadFontList(const mySL: tstringlist); var SearchRec: TSearchRec; begin if not DirectoryExists(myConst.fontsPath) then CreateDir(myConst.fontsPath); if FindFirst(IncludeTrailingPathDelimiter(myConst.fontsPath) + '*.*', faArchive, SearchRec) = 0 then try repeat if (SearchRec.Attr and faAnyFile) = SearchRec.Attr then begin mySL.Add(SearchRec.name); end; until FindNext(SearchRec) <> 0; finally System.SysUtils.FindClose(SearchRec); end; end; function GetColorFromColorPanel(aColor: TAlphaColor): string; var Color: TAlphaColor; r, G, B: Byte; A: Real; FS: TFormatSettings; begin Color := aColor; r := TAlphaColorRec(Color).r; G := TAlphaColorRec(Color).G; B := TAlphaColorRec(Color).B; A := TAlphaColorRec(Color).A / 255; // Преобразуем альфа-канал в диапазон 0..1 // Устанавливаем точку в качестве десятичного разделителя FS := TFormatSettings.Create; FS.DecimalSeparator := '.'; result := Format('rgba(%d, %d, %d, %.2f)', [r, G, B, A], FS); end; function GetColorFromCCB(aColor: TAlphaColor): string; var Color: TAlphaColor; r, G, B: Byte; begin Color := aColor; r := TAlphaColorRec(Color).r; G := TAlphaColorRec(Color).G; B := TAlphaColorRec(Color).B; result := Format('#%.2X%.2X%.2X', [r, G, B]); end; begin fonts := tstringlist.Create; f := TfrColorSettings.Create(self); t := TfrFontSettings.Create(self); try LoadFontList(fonts); EventWebServer.WebServerChat := TTTW_Events.Create(fonts, eventsSettings.port); EventWebServer.port := eventsSettings.port; EventWebServer.typeEvent := eventsSettings.typeEvent; EventWebServer.se.Title := eventsSettings.HeaderText; EventWebServer.se.Context := eventsSettings.MessText; EventWebServer.se.Url := eventsSettings.Picture; EventWebServer.se.SoundURL := eventsSettings.Sound; EventWebServer.se.Timestamp := now; EventWebServer.se.TimeMsg := eventsSettings.TimeMess; EventWebServer.se.FontTitle.Font := eventsSettings.HeaderStyleFont; EventWebServer.se.FontTitle.Size := eventsSettings.HeaderSizeFont; EventWebServer.se.FontTitle.Color := eventsSettings.HeaderColorFont; EventWebServer.se.FontContext.Font := eventsSettings.MessStyleFont; EventWebServer.se.FontContext.Size := eventsSettings.MessSizeFont; EventWebServer.se.FontContext.Color :=eventsSettings.MessColorFont; EventWebServer.se.BorderColor := eventsSettings.ColorBorder; EventWebServer.se.BorderSize := eventsSettings.SolidBorder; EventWebServer.se.BlockColor := eventsSettings.ColorBlock; EventWebServers.Add(EventWebServer); EventWebServers[EventWebServers.Count - 1].WebServerChat.ActiveServer(True); finally fonts.Free; f.Free; t.Free; end; end; procedure TfrOBS.CreateWebKandinsky(aRecord: TOBSKandinsky); var KandinskyWebServer: TKandinskyWebServers; begin KandinskyWebServer.port := aRecord.port; KandinskyWebServer.WebServerKandinsky := TKandinsky_Web.Create(TTW_Bot.frAI1.edtKandiKey.text, TTW_Bot.frAI1.edtKandiSecret.text, aRecord.port); KandinskyWebServers.Add(KandinskyWebServer); KandinskyWebServers[KandinskyWebServers.Count - 1] .WebServerKandinsky.ActiveServer(True); end; procedure TfrOBS.DelChat(aPort: integer); var i, j: integer; begin // Ищем в обратном порядке для безопасного удаления for i := High(listChats) downto 0 do begin if listChats[i].port = aPort then begin // Сдвигаем элементы массива for j := i to High(listChats) - 1 do listChats[j] := listChats[j + 1]; // Уменьшаем размер массива SetLength(listChats, Length(listChats) - 1); // Выходим после первого найденного совпадения (предполагаем уникальность портов) Break; end; end; ChatWebServers[i].WebServerChat.ActiveServer(false); ChatWebServers[i].WebServerChat.Destroy; ChatWebServers.Delete(i); db.SaveRecordArray('listChats', listChats); UpdateGridFromArray; end; procedure TfrOBS.DelKandinsky(aPort: integer); var i, j: integer; begin // Ищем в обратном порядке для безопасного удаления for i := High(listKandinsky) downto 0 do begin if listKandinsky[i].port = aPort then begin // Сдвигаем элементы массива for j := i to High(listKandinsky) - 1 do listKandinsky[j] := listKandinsky[j + 1]; // Уменьшаем размер массива SetLength(listKandinsky, Length(listKandinsky) - 1); // Выходим после первого найденного совпадения (предполагаем уникальность портов) Break; end; end; KandinskyWebServers[i].WebServerKandinsky.ActiveServer(false); KandinskyWebServers[i].WebServerKandinsky.Destroy; KandinskyWebServers.Delete(i); UpdateGridFromArray; db.SaveRecordArray('listKandinsky', listKandinsky); end; procedure TfrOBS.DelNotify(aPort: integer); var i, j: integer; begin // Ищем в обратном порядке для безопасного удаления for i := High(listNotify) downto 0 do begin if listNotify[i].port = aPort then begin // Сдвигаем элементы массива for j := i to High(listNotify) - 1 do listNotify[j] := listNotify[j + 1]; // Уменьшаем размер массива SetLength(listNotify, Length(listNotify) - 1); // Выходим после первого найденного совпадения (предполагаем уникальность портов) Break; end; end; EventWebServers[i].WebServerChat.ActiveServer(false); EventWebServers[i].WebServerChat.Destroy; EventWebServers.Delete(i); UpdateGridFromArray; db.SaveRecordArray('listNotify', listNotify); end; procedure TfrOBS.EdtChat(newRecord: TOBSChat; oldPort: integer); var i, j: integer; chatWeb: TChatWebServers; // Временная переменная для записи f: TfrColorSettings; t: TfrFontSettings; begin f := TfrColorSettings.Create(self); t := TfrFontSettings.Create(self); try // Обновляем запись в listChats for i := 0 to High(listChats) do if listChats[i].port = oldPort then begin listChats[i] := newRecord; Break; end; // Обновляем соответствующий сервер в ChatWebServers for j := 0 to ChatWebServers.Count - 1 do begin // 1. Извлекаем запись во временную переменную chatWeb := ChatWebServers[j]; if chatWeb.port = oldPort then begin // 2. Модифицируем поля записи chatWeb.MaxMsg := newRecord.MaxCountMess; chatWeb.TimeMsg := newRecord.TimeMess; chatWeb.Freez := newRecord.Freez = 1; chatWeb.StyleBorderColor := f.ccbStyleBorderColor.Items [newRecord.ColorBorder]; chatWeb.StyleBlockColor := newRecord.ColorBlock; chatWeb.StyleBlockBorderSize := newRecord.SolidBorder; chatWeb.StyleBlockPadding := newRecord.Paddings; chatWeb.FontStyleDefault := t.cbFontStyleDefault.Items [newRecord.StyleFont]; chatWeb.FontColor := t.ccbFontColor.Items[newRecord.ColorFont]; chatWeb.FontSize := newRecord.SizeFont; chatWeb.BColor := f.ccbBColor.Items[newRecord.ColorBackground]; chatWeb.WebServerChat.changeBackground (f.ccbBColor.Items[newRecord.ColorBackground]); chatWeb.WebServerChat.SetDeleteMode(not chatWeb.Freez, chatWeb.MaxMsg); // 4. Возвращаем модифицированную запись в список ChatWebServers[j] := chatWeb; Break; end; end; UpdateGridFromArray; db.SaveRecordArray('listChats', listChats); finally f.Free; t.Free; end; end; procedure TfrOBS.EdtNotify(newRecord: TOBSNotify; oldPort: integer); var i,mi: integer; ws:TEventWebServers; begin for I := 0 to EventWebServers.Count - 1 do if EventWebServers[i].port = oldPort then begin ws:=EventWebServers[i]; mi:=i; end; for i := 0 to High(listNotify) do if listNotify[i].port = oldPort then begin listNotify[i] := newRecord; ws.port:=listNotify[i].port; ws.typeEvent:=listNotify[i].TypeEvent; ws.se.title:=listNotify[i].HeaderText; ws.se.Context:=listNotify[i].MessText; ws.se.Url:=listNotify[i].Picture; ws.se.SoundURL:=listNotify[i].Sound; ws.se.TimeMsg:=listNotify[i].TimeMess; ws.se.FontTitle.Font:=listNotify[i].HeaderStyleFont; ws.se.FontTitle.Font := listNotify[i].HeaderStyleFont; ws.se.FontTitle.Size := listNotify[i].HeaderSizeFont; ws.se.FontTitle.Color := listNotify[i].HeaderColorFont; ws.se.FontContext.Font := listNotify[i].MessStyleFont; ws.se.FontContext.Size := listNotify[i].MessSizeFont; ws.se.FontContext.Color := listNotify[i].MessColorFont; ws.se.BorderColor :=listNotify[i].ColorBorder; ws.se.BorderSize := listNotify[i].SolidBorder; ws.se.BlockColor := listNotify[i].ColorBlock; EventWebServers[mi]:=ws; UpdateGridFromArray; db.SaveRecordArray('listNotify', listNotify); Break; end; end; function TfrOBS.FindEmoteByID(const ID: string): TEmotes; var i: integer; begin result.ID := ''; if not Assigned(ChatEmotes) then Exit; for i := 0 to ChatEmotes.Count - 1 do if ChatEmotes[i].ID = ID then begin result := ChatEmotes[i]; Break; end; end; procedure TfrOBS.sgWebChatsCellDblClick(const Column: TColumn; const Row: integer); var myChatRec: TOBSChat; myNotifyRec: TOBSNotify; i: integer; begin if sgWebChats.Cells[1, Row] = 'Оповещение' then begin for i := 0 to High(listNotify) do if listNotify[i].port = (strtoint(sgWebChats.Cells[0, Row])) then begin myNotifyRec := listNotify[i]; Break; end; fCreateNotify.isEdit := True; fCreateNotify.setRecord(myNotifyRec); fCreateNotify.Show; end; if sgWebChats.Cells[1, Row] = 'Чат' then begin for i := 0 to High(listChats) do if listChats[i].port = (strtoint(sgWebChats.Cells[0, Row])) then begin myChatRec := listChats[i]; Break; end; fCreateChat.isEdit := True; fCreateChat.setRecord(myChatRec); fCreateChat.Show; end; end; { 0 Отслеживание 1 Подписка 2 Подарочная подписка 3 Рейд 4 Донат 5 Свое } procedure TfrOBS.toEventWebServer(aEvent: TFollowEvent); var i: integer; ws: TEventWebServers; begin TTW_Bot.toLog('fOBS', 'toEventWebServer', 'Получено событие Follow', 0); for i := 0 to EventWebServers.Count - 1 do begin if EventWebServers[i].typeEvent = 0 then begin ws := EventWebServers[i]; ws.se.Title := StringReplace(ws.se.Title, '[NICK]', aEvent.event.user_name, [rfReplaceAll]); ws.se.Timestamp:=now; ws.se.RequireInteraction := True; TTW_Bot.toLog('fOBS', 'toEventWebServer.BlockColor', ws.se.BlockColor, 3); EventWebServers[i].WebServerChat.AddMessage(ws.se); end; end; end; procedure TfrOBS.toEventWebServer(aEvent: TSubEvent); var i: integer; ws: TEventWebServers; begin for i := 0 to EventWebServers.Count - 1 do begin if EventWebServers[i].typeEvent <> 1 then Continue; ws := EventWebServers[i]; ws.se.Title := StringReplace(ws.se.Title, '[NICK]', aEvent.event.user_name, [rfReplaceAll]); EventWebServers[i] := ws; EventWebServers[i].WebServerChat.AddMessage(EventWebServers[i].se); end; end; procedure TfrOBS.toEventWebServer(aEvent: TRaidEvent); var i: integer; ws: TEventWebServers; begin for i := 0 to EventWebServers.Count - 1 do begin if EventWebServers[i].typeEvent <> 3 then Continue; ws := EventWebServers[i]; ws.se.Title := StringReplace(ws.se.Title, '[NICK]', aEvent.event.from_broadcaster_user_name, [rfReplaceAll]); ws.se.Context := StringReplace(ws.se.Context, '[SUM]', inttostr(aEvent.event.viewers), [rfReplaceAll]); EventWebServers[i] := ws; EventWebServers[i].WebServerChat.AddMessage(EventWebServers[i].se); end; end; procedure TfrOBS.toEventWebServer(aEvent: TGiftEvent); var i: integer; ws: TEventWebServers; begin for i := 0 to EventWebServers.Count - 1 do begin if EventWebServers[i].typeEvent <> 2 then Continue; ws := EventWebServers[i]; ws.se.Title := StringReplace(ws.se.Title, '[NICK]', aEvent.event.user_name, [rfReplaceAll]); EventWebServers[i] := ws; EventWebServers[i].WebServerChat.AddMessage(EventWebServers[i].se); end; end; procedure TfrOBS.toEventWebServer(aNick, aSum, aText: string); var i: integer; ws: TEventWebServers; begin for i := 0 to EventWebServers.Count - 1 do begin if EventWebServers[i].typeEvent <> 4 then Continue; ws := EventWebServers[i]; ws.se.Title := StringReplace(ws.se.Title, '[NICK]', aNick, [rfReplaceAll]); ws.se.Context := StringReplace(ws.se.Context, '[SUM]', aSum, [rfReplaceAll]); EventWebServers[i] := ws; EventWebServers[i].WebServerChat.AddMessage(EventWebServers[i].se); end; end; procedure TfrOBS.UpdateGridFromArray; var i, rowIndex: integer; begin sgWebChats.BeginUpdate; try sgWebChats.RowCount := 0; // Сбрасываем строки rowIndex := 0; // Отдельный счетчик для строк сетки // listChats for i := 0 to High(listChats) do begin sgWebChats.RowCount := rowIndex + 1; sgWebChats.Cells[0, rowIndex] := inttostr(listChats[i].port); sgWebChats.Cells[1, rowIndex] := 'Чат'; sgWebChats.Cells[2, rowIndex] := 'http://127.0.0.1:' + inttostr(listChats[i].port); Inc(rowIndex); // Увеличиваем счетчик строк end; // listNotify for i := 0 to High(listNotify) do begin sgWebChats.RowCount := rowIndex + 1; sgWebChats.Cells[0, rowIndex] := inttostr(listNotify[i].port); sgWebChats.Cells[1, rowIndex] := 'Оповещение'; sgWebChats.Cells[2, rowIndex] := 'http://127.0.0.1:' + inttostr(listNotify[i].port); Inc(rowIndex); // Увеличиваем счетчик строк end; // listKandinsky for i := 0 to High(listKandinsky) do begin sgWebChats.RowCount := rowIndex + 1; sgWebChats.Cells[0, rowIndex] := inttostr(listKandinsky[i].port); sgWebChats.Cells[1, rowIndex] := 'Kandinsky'; sgWebChats.Cells[2, rowIndex] := 'http://127.0.0.1:' + inttostr(listKandinsky[i].port); Inc(rowIndex); // Увеличиваем счетчик строк end; finally sgWebChats.EndUpdate; end; end; end.