unit fSettings; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, uQ, IdHTTP, IdComponent, System.Zip, FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls, winapi.ShellAPI, System.IOUtils, uDataBase, FMX.Controls.Presentation, FMX.Edit, uTWAuth, uRecords, uAPIDA, uShowText, System.json, uWSDA, fLog, IdBaseComponent, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL; type TfrSettings = class(TFrame) GroupBox3: TGroupBox; Label1: TLabel; Label2: TLabel; Label3: TLabel; Label22: TLabel; edtBotName: TEdit; edtBotToken: TEdit; edtChannel: TEdit; btnGetToken: TButton; edtBotClientID: TEdit; btnGetClientID: TButton; btnOpenStream: TButton; btnGetTokenStreamer: TButton; edtBotTokenStreamer: TEdit; Label53: TLabel; GroupBox22: TGroupBox; btnDAGetCode: TButton; Label63: TLabel; edtDAClientID: TEdit; Label64: TLabel; edtDAClientSecret: TEdit; Label65: TLabel; edtDARedirectURL: TEdit; edtDACode: TEdit; Label66: TLabel; btnDAStart: TButton; btnGetDADef: TButton; cbDAAutoLogin: TCheckBox; cbTTVAutoLogin: TCheckBox; btnOpenRomaning: TButton; btnImportSettings: TButton; btnExportSettings: TButton; btnMaster: TButton; SaveDialog1: TSaveDialog; OpenDialog1: TOpenDialog; procedure btnGetTokenClick(Sender: TObject); procedure btnGetTokenStreamerClick(Sender: TObject); procedure btnOpenStreamClick(Sender: TObject); procedure btnDAGetCodeClick(Sender: TObject); procedure btnDAStartClick(Sender: TObject); procedure btnOpenRomaningClick(Sender: TObject); procedure btnImportSettingsClick(Sender: TObject); procedure btnExportSettingsClick(Sender: TObject); procedure btnMasterClick(Sender: TObject); procedure edtChannelExit(Sender: TObject); private { Private declarations } FAPIClient: TAPIClient; forbot: boolean; procedure OnTTWToken(txt: string); procedure OnTokenDA(txt: string); procedure HandleWSStatus(AStatusText: string; AStatusCode: integer); procedure HandleWSDonate(aNick, aMessage, aSum: string); public { Public declarations } FWSClient: TWSClient; destructor Destroy; override; procedure init(); end; implementation {$R *.fmx} uses uGeneral; procedure TfrSettings.btnDAGetCodeClick(Sender: TObject); var twa: TTTWAuth; URL: string; begin if (edtDAClientSecret.text = '') or (edtDAClientID.text = '') or (edtDARedirectURL.text = '') then exit; URL := 'https://www.donationalerts.com/oauth/authorize?client_id=' + edtDAClientID.text + '&redirect_uri=http://localhost/da&response_type=code&scope=oauth-user-show+oauth-donation-subscribe'; twa := TTTWAuth.Create; twa.OnToken := OnTokenDA; twa.StartServer(URL); // ttw_Auth будет освобожден автоматически после получения токена (см. uTWAuth) end; procedure TfrSettings.OnTokenDA(txt: string); begin edtDACode.text := txt; if cbDAAutoLogin.IsChecked then begin btnDAStartClick(self); end; end; procedure TfrSettings.btnDAStartClick(Sender: TObject); var UserInfo: TJSONObject; Data: TJSONObject; begin if btnDAStart.text = 'Подключиться' then begin UserInfo := nil; try try if not Assigned(FAPIClient) then init; if FAPIClient.Token = '' then begin try FAPIClient.Token := FAPIClient.GetAccessToken(edtDAClientID.text, edtDAClientSecret.text, edtDARedirectURL.text, edtDACode.text); except on E: Exception do begin TTW_Bot.toLog('fSettings', 'btnDAStartClick', 'Ошибка получения токена: ' + E.Message, 2); exit; end; end; FWSClient.APIClient := FAPIClient; try UserInfo := FAPIClient.GetUserInfo; Data := UserInfo.GetValue('data'); FWSClient.Wsstoken := Data.GetValue ('socket_connection_token'); FWSClient.WSID := Data.GetValue('id'); except on E: Exception do begin TTW_Bot.toLog('fSettings', 'btnDAStartClick', 'Ошибка получения UserInfo: ' + E.Message, 2); exit; end; end; end; try FWSClient.Connect ('wss://centrifugo.donationalerts.com/connection/websocket'); FWSClient.Send(Format('{"params":{"token":"%s"},"id":1}', [FWSClient.Wsstoken])); except on E: Exception do TTW_Bot.toLog('fSettings', 'btnDAStartClick', 'Ошибка подключения к WebSocket: ' + E.Message, 2); end; except on E: Exception do TTW_Bot.toLog('fSettings', 'btnDAStartClick', 'Неизвестная ошибка: ' + E.Message, 2); end; finally UserInfo.Free; end; end else begin try edtDACode.text := ''; if Assigned(FWSClient) then begin try FWSClient.Disconnect; except on E: Exception do TTW_Bot.toLog('fSettings', 'btnDAStartClick', 'Ошибка при отключении WS: ' + E.Message, 2); end; FreeAndNil(FWSClient); end; FreeAndNil(FAPIClient); finally btnDAStart.ImageIndex := 18; btnDAStart.text := 'Подключиться'; end; end; end; procedure TfrSettings.btnExportSettingsClick(Sender: TObject); var DestinationFile: string; begin SaveDialog1.FileName := TPath.GetFileName(myConst.DBPath); if SaveDialog1.Execute then begin DestinationFile := SaveDialog1.FileName; TFile.Copy(myConst.DBPath, DestinationFile, true); end; end; procedure TfrSettings.btnGetTokenClick(Sender: TObject); var s: string; sope: string; ttw_Auth: TTTWAuth; begin ttw_Auth := TTTWAuth.Create; ttw_Auth.OnToken := OnTTWToken; // ttw_Auth будет освобожден автоматически после получения токена (см. uTWAuth) sope := 'moderator:manage:shoutouts' + '+moderator:manage:announcements' + '+moderator:manage:banned_users' + '+moderator:manage:warnings' + '+moderator:read:followers' + '+channel:manage:raids' + '+channel:manage:moderators' + '+channel:read:redemptions' + '+chat:read' + '+chat:edit+user:read:emotes'; sope := StringReplace(sope, ':', '%3A', [rfReplaceAll]); s := 'https://id.twitch.tv/oauth2/authorize?client_id=' + edtBotClientID.text + '&redirect_uri=http://localhost&response_type=token&' + 'scope=' + sope; ttw_Auth.StartServer(''); forbot := true; fShowText.Memo1.Lines.text := s; fShowText.Show; fShowText.Memo1.WordWrap := true; end; procedure TfrSettings.btnGetTokenStreamerClick(Sender: TObject); var sope: string; ttw_Auth: TTTWAuth; begin ttw_Auth := TTTWAuth.Create; ttw_Auth.OnToken := OnTTWToken; sope := 'channel:read:redemptions' + '+channel:manage:vips' + '+moderator:read:followers' + '+channel:read:subscriptions' + '+channel:manage:moderators' + '+channel:manage:redemptions'; sope := StringReplace(sope, ':', '%3A', [rfReplaceAll]); ttw_Auth.StartServer('https://id.twitch.tv/oauth2/authorize?client_id=' + edtBotClientID.text + '&redirect_uri=http://localhost&response_type=token&' + 'scope=' + sope); forbot := false; end; procedure TfrSettings.btnImportSettingsClick(Sender: TObject); var SourceFile, DestinationDir, DestinationFile: string; begin if OpenDialog1.Execute then begin DB.Free; SourceFile := OpenDialog1.FileName; DestinationDir := myConst.DBPath; DestinationFile := myConst.DBPath; TFile.Copy(SourceFile, DestinationFile, true); DB := TSettingsDatabase.Create(myConst.DBPath); end; end; procedure TfrSettings.btnMasterClick(Sender: TObject); var qf: tfrmq; begin qf := tfrmq.Create(nil); try qf.SetLabelText('Введите ник аккаунта от которого будет писать бот:'); if qf.ShowModal = mrOk then edtBotName.text := qf.GetEditText; qf.SetLabelText('Введите ник стримера где будет работать бот:'); if qf.ShowModal = mrOk then edtChannel.text := qf.GetEditText; if btnGetClientID.Visible then begin edtBotClientID.text := appconst.TTV_ClientID; showmessage('Появится окно, там будет ссылка. ' + #13 + 'Скопируй ее и открой в браузере где авторизован бот. ' + #13 + 'Если понял - жми ОК'); btnGetTokenClick(self); showmessage ('Когда ссылка пропадет жми "Получить Token" около поля "API Token Стримера". ' + #13 + 'Если понял - жми ОК'); end else begin showmessage('Нет файла ключей! ' + #13 + 'Введите ClientID вручную и продолжите настройку без мастера!'); end; finally qf.Free; end; end; procedure TfrSettings.btnOpenRomaningClick(Sender: TObject); begin ShellExecute(0, 'open', pwidechar(ExtractFilePath(myConst.DBPath)), nil, nil, 1); end; procedure TfrSettings.btnOpenStreamClick(Sender: TObject); begin ShellExecute(0, 'open', pwidechar('https://www.twitch.tv/' + edtChannel.text), nil, nil, 1); end; destructor TfrSettings.Destroy; begin if Assigned(FWSClient) then begin try FWSClient.Disconnect; // если есть метод отключения except end; FreeAndNil(FWSClient); end; FreeAndNil(FAPIClient); inherited; end; procedure TfrSettings.edtChannelExit(Sender: TObject); begin if Sender is TEdit then DB.WriteSetting(TEdit(Sender).Name, TEdit(Sender).text); if Sender is TCheckBox then if TCheckBox(Sender).IsChecked then DB.WriteSetting(TCheckBox(Sender).Name, 'True') else DB.WriteSetting(TCheckBox(Sender).Name, 'False'); end; procedure TfrSettings.init; begin if not Assigned(FAPIClient) then FAPIClient := TAPIClient.Create; if not Assigned(FWSClient) then begin FWSClient := TWSClient.Create; FWSClient.OnStatus := HandleWSStatus; FWSClient.OnDonate := HandleWSDonate; FWSClient.OnLog := TTW_Bot.toLog; end; end; procedure TfrSettings.OnTTWToken(txt: string); begin fShowText.Close; if forbot then begin edtBotToken.text := txt; DB.WriteSetting('edtBotToken', txt); end else begin edtBotTokenStreamer.text := txt; DB.WriteSetting('edtBotTokenStreamer', txt); end; end; procedure TfrSettings.HandleWSDonate(aNick, aMessage, aSum: string); begin // fDonats.OnDADonate(aNick, aMessage, aSum); TTW_Bot.frOBS1.toEventWebServer(aNick, aSum, aMessage); TTW_Bot.frEvents1.OnDonate(aNick, aMessage, aSum); end; procedure TfrSettings.HandleWSStatus(AStatusText: string; AStatusCode: integer); begin TTW_Bot.toLog('fSettings', 'HandleWSStatus', AStatusText, 3); TTW_Bot.Label8.text := AStatusText; case AStatusCode of 0: begin btnDAStart.ImageIndex := 1; btnDAStart.text := 'Отключиться'; end; else begin btnDAStart.ImageIndex := 18; btnDAStart.text := 'Подключиться'; end; end; end; end.