unit uTTWAPI; interface uses System.Classes, System.SysUtils, IdHTTP, IdSSLOpenSSL, System.JSON, FMX.Forms, IdMultipartFormData, DateUtils, uDataBase, System.Generics.Collections, uRecords; type TOnLog = procedure(aModul: string; aMethod: string; aMessage: string; aLevel: integer) of object; type TTTW_API = class(TObject) private Token_api: string; Token_api_streamer: string; ClientID: string; channel_name_api: string; BotName_api: string; FChatBadges: TList; FOnLog: TOnLog; function GetFollowedAtFromJson(jsonString: string): string; function getTTW(method: string; ClientID: string; isStreamer: boolean = false): string; function DelTTW(method: string; ClientID: string; isStreamer: boolean = false): string; function postTTW(method: string; ClientID: string; params: TIdMultipartFormDataStream; isStreamer: boolean = false) : string; overload; function postTTW(method: string; ClientID: string; params: TStringStream; isStreamer: boolean = false): string; overload; function patchTTW(method: string; ClientID: string; params: TStringStream; isStreamer: boolean = false): string; overload; procedure toLog(alevel: integer; amethod: string; amessage: string); public constructor Create(Sender: TObject); destructor Destroy; override; procedure Init(myClient, myToken, streamerToken, Channel, myBotName: string); procedure getTTWStat(Channel: string; var avg_viewers: integer; var max_viewers: integer; var hours_watched: integer; var followers: integer; var followers_total: integer); procedure shoutouts(id: string); procedure SendNotify(text: string); procedure banUser(id: string); procedure banUserTime(id: string; aTime: integer); procedure unBanUser(id: string); procedure warnUser(id: string); procedure raid(id: string); procedure unRaid(); procedure setModerator(id: string); procedure delModerator(id: string); procedure setVIP(id: string); procedure delVIP(id: string); procedure getCustomReward(var acr: TList); function createCustomReward(title: string; cost: string; promt: string = ''; isUserInput: boolean = false): TCustomRevards; procedure UpdateCustomReward(ahr: TCustomRevards); procedure UpdateRedemptionStatus(ahr: TCustomRewardEvent); procedure deleteCustomReward(id: string); function getRoomAndBot(): string; function getUserbyLogin(login: string): TUser; function getFollow(id: string): TDateTime; function GetRoomID: string; procedure getGlobalChatBadges(var gcb: TList); procedure getCustomChatBadges(var ccb: TList); procedure GetChannelEmotes(var ce: TList); procedure GetGlobalEmotes(var ge: TList); function ValidateTwitchToken(const TokenName, TokenValue: string; var DayOfLive: integer): Boolean; property OnLog: TOnLog read FOnLog write FOnLog; end; var room_id: string = ''; bot_id: string = ''; implementation uses uGeneral; constructor TTTW_API.Create(Sender: TObject); begin FChatBadges := TList.Create; end; function TTTW_API.createCustomReward(title: string; cost: string; promt: string = ''; isUserInput: boolean = false): TCustomRevards; var RequestData: TStringStream; s, s1, json: string; cr: TCustomRevards; JSONData: TJSONObject; JSONArray: TJSONArray; begin Result := Default(TCustomRevards); try if room_id = '' then room_id := GetRoomID; s := ''; s1 := ''; if isUserInput then s := ' "is_user_input_required":true,'; if promt <> '' then s1 := ' "prompt":"' + promt + '",'; RequestData := TStringStream.Create('{ "title":"' + title + '", ' + s + s1 + ' "cost":' + cost + ' }', TEncoding.UTF8); try json := postTTW('channel_points/custom_rewards?broadcaster_id=' + room_id, ClientID, RequestData, true); if json = '' then begin toLog(1, 'createCustomReward', 'Награда не создалась, запрос вернул пустой ответ'); Exit; end; JSONData := TJSONObject.ParseJSONValue(json) as TJSONObject; if not Assigned(JSONData) then begin toLog(2, 'createCustomReward', 'Ошибка парсинга JSON'); Exit; end; try JSONArray := JSONData.GetValue('data'); if JSONArray.Count > 0 then begin cr.id := JSONArray.Items[0].GetValue('id'); cr.title := JSONArray.Items[0].GetValue('title'); cr.promt := JSONArray.Items[0].GetValue('prompt'); cr.cost := JSONArray.Items[0].GetValue('cost'); cr.is_user_input_required := JSONArray.Items[0].GetValue ('is_user_input_required'); Result := cr; end; finally JSONData.Free; end; finally RequestData.Free; end; except on E: Exception do toLog(2, 'createCustomReward', E.Message); end; end; destructor TTTW_API.Destroy; begin FChatBadges.Free; inherited; end; procedure TTTW_API.Init(myClient, myToken, streamerToken, Channel, myBotName: string); begin ClientID := myClient; Token_api := myToken; Token_api_streamer := streamerToken; channel_name_api := Channel; BotName_api := myBotName; end; procedure TTTW_API.banUser(id: string); var RequestData: TStringStream; begin try if bot_id = '' then bot_id := getRoomAndBot; if (bot_id = '') or (room_id = '') then Exit; RequestData := TStringStream.Create('{"data": {"user_id":"' + id + '","reason":"no reason"}}', TEncoding.UTF8); try postTTW('moderation/bans?broadcaster_id=' + room_id + '&moderator_id=' + bot_id, ClientID, RequestData); finally RequestData.Free; end; except on E: Exception do toLog(2, 'banUser', E.Message); end; end; procedure TTTW_API.banUserTime(id: string; aTime: integer); var RequestData: TStringStream; begin try if bot_id = '' then bot_id := getRoomAndBot; if (bot_id = '') or (room_id = '') then Exit; RequestData := TStringStream.Create('{"data": {"user_id":"' + id + '","duration":' + IntToStr(aTime) + '}}', TEncoding.UTF8); try postTTW('moderation/bans?broadcaster_id=' + room_id + '&moderator_id=' + bot_id, ClientID, RequestData); finally RequestData.Free; end; except on E: Exception do toLog(2, 'banUserTime', E.Message); end; end; procedure TTTW_API.deleteCustomReward(id: string); begin try if Token_api_streamer = '' then Exit; if room_id = '' then Exit; DelTTW('channel_points/custom_rewards?broadcaster_id=' + room_id + '&id=' + id, ClientID, true); except on E: Exception do toLog(2, 'deleteCustomReward', E.Message); end; end; procedure TTTW_API.delModerator(id: string); begin try if Token_api_streamer = '' then Exit; if room_id = '' then Exit; DelTTW('moderation/moderators?broadcaster_id=' + room_id + '&user_id=' + id, ClientID, true); except on E: Exception do toLog(2, 'delModerator', E.Message); end; end; function TTTW_API.DelTTW(method, ClientID: string; isStreamer: boolean): string; var url: string; response1, Token: string; http: TIdHTTP; ssl: TIdSSLIOHandlerSocketOpenSSL; begin Result := ''; http := TIdHTTP.Create(nil); ssl := TIdSSLIOHandlerSocketOpenSSL.Create(nil); try try if isStreamer then Token := Token_api_streamer else Token := Token_api; http.IOHandler := ssl; ssl.SSLOptions.method := sslvSSLv23; http.Request.UserAgent := 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; http.Request.CustomHeaders.AddValue('Client-ID', ClientID); http.Request.CustomHeaders.AddValue('Authorization', 'Bearer ' + Token); http.Request.ContentType := 'application/json'; url := 'https://api.twitch.tv/helix/' + method; response1 := http.Delete(url); Result := response1; except on E: Exception do toLog(2, 'DelTTW', E.Message + ' [' + method + ']'); end; finally http.Free; ssl.Free; end; end; procedure TTTW_API.delVIP(id: string); begin try if Token_api_streamer = '' then Exit; if room_id = '' then Exit; DelTTW('channels/vips?broadcaster_id=' + room_id + '&user_id=' + id, ClientID, true); except on E: Exception do toLog(2, 'delVIP', E.Message); end; end; procedure TTTW_API.getCustomReward(var acr: TList); var JSON: string; sl: TCustomRevards; JSONData: TJSONObject; JSONArray: TJSONArray; i: integer; begin try if Token_api_streamer = '' then begin toLog(1, 'getCustomReward', 'Token_api_streamer пуст'); Exit; end; if room_id = '' then Exit; JSON := getTTW('channel_points/custom_rewards?broadcaster_id=' + room_id + '&only_manageable_rewards=true', ClientID, true); if JSON = '' then begin toLog(1, 'getCustomReward', 'Пустой ответ от API'); Exit; end; JSONData := TJSONObject.ParseJSONValue(JSON) as TJSONObject; if not Assigned(JSONData) then begin toLog(2, 'getCustomReward', 'Ошибка парсинга JSON'); Exit; end; try JSONArray := JSONData.GetValue('data'); for i := 0 to JSONArray.Count - 1 do begin sl.id := JSONArray.Items[i].GetValue('id'); sl.title := JSONArray.Items[i].GetValue('title'); sl.promt := JSONArray.Items[i].GetValue('prompt'); sl.cost := JSONArray.Items[i].GetValue('cost'); sl.is_user_input_required := JSONArray.Items[i].GetValue ('is_user_input_required'); acr.Add(sl); end; finally JSONData.Free; end; except on E: Exception do toLog(2, 'getCustomReward', E.Message); end; end; function TTTW_API.getFollow(id: string): TDateTime; var s: string; followedAt: string; begin Result := 0; s := getTTW('channels/followers?user_id=' + id + '&broadcaster_id=' + room_id, ClientID); followedAt := GetFollowedAtFromJson(s); if followedAt <> '' then Result := StrToDate(followedAt); end; function TTTW_API.GetFollowedAtFromJson(jsonString: string): string; var JSON: TJSONObject; dataArray: TJSONArray; begin Result := ''; if jsonString = '' then Exit; JSON := TJSONObject.ParseJSONValue(jsonString) as TJSONObject; if not Assigned(JSON) then Exit; try dataArray := JSON.GetValue('data') as TJSONArray; if Assigned(dataArray) and (dataArray.Count > 0) then Result := DateToStr(ISO8601ToDate(dataArray.Items[0].GetValue('followed_at'))); finally JSON.Free; end; end; function TTTW_API.getTTW(method, ClientID: string; isStreamer: boolean): string; var url: string; response1, Token: string; http: TIdHTTP; ssl: TIdSSLIOHandlerSocketOpenSSL; begin Result := ''; http := TIdHTTP.Create(nil); ssl := TIdSSLIOHandlerSocketOpenSSL.Create(nil); try try if isStreamer then Token := Token_api_streamer else Token := Token_api; http.IOHandler := ssl; ssl.SSLOptions.method := sslvSSLv23; http.Request.UserAgent := 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; http.Request.CustomHeaders.AddValue('Client-ID', ClientID); http.Request.CustomHeaders.AddValue('Authorization', 'Bearer ' + Token); url := 'https://api.twitch.tv/helix/' + method; response1 := http.Get(url); Result := response1; except on E: Exception do toLog(2, 'getTTW', E.Message + ' [' + method + ']'); end; finally http.Free; ssl.Free; end; end; procedure TTTW_API.getTTWStat(Channel: string; var avg_viewers, max_viewers, hours_watched, followers, followers_total: integer); var jsonObject: TJSONObject; url: string; response1: string; http: TIdHTTP; ssl: TIdSSLIOHandlerSocketOpenSSL; begin http := TIdHTTP.Create(nil); ssl := TIdSSLIOHandlerSocketOpenSSL.Create(nil); try try http.IOHandler := ssl; ssl.SSLOptions.method := sslvSSLv23; http.Request.UserAgent := 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; url := 'https://twitchtracker.com/api/channels/summary/' + Channel; response1 := http.Get(url); jsonObject := TJSONObject.ParseJSONValue(response1) as TJSONObject; if not Assigned(jsonObject) then begin toLog(2, 'getTTWStat', 'Ошибка парсинга JSON'); Exit; end; try avg_viewers := jsonObject.GetValue('avg_viewers'); max_viewers := jsonObject.GetValue('max_viewers'); hours_watched := jsonObject.GetValue('hours_watched'); followers := jsonObject.GetValue('followers'); followers_total := jsonObject.GetValue('followers_total'); finally jsonObject.Free; end; except on E: Exception do toLog(2, 'getTTWStat', E.Message); end; finally http.Free; ssl.Free; end; end; function ParseBadgeVersion(VersionObj: TJSONObject): TBadgeVersion; begin Result.Id := VersionObj.GetValue('id').Value; Result.ImageUrl1x := VersionObj.GetValue('image_url_1x').Value; Result.ImageUrl2x := VersionObj.GetValue('image_url_2x').Value; Result.ImageUrl4x := VersionObj.GetValue('image_url_4x').Value; Result.Title := VersionObj.GetValue('title').Value; Result.Description := VersionObj.GetValue('description').Value; Result.ClickAction := VersionObj.GetValue('click_action').Value; Result.ClickUrl := VersionObj.GetValue('click_url').Value; end; function ParseChatBadge(BadgeObj: TJSONObject): TChatBadge; var VersionsArray: TJSONArray; I: Integer; begin Result.SetId := BadgeObj.GetValue('set_id').Value; VersionsArray := BadgeObj.GetValue('versions') as TJSONArray; SetLength(Result.Versions, VersionsArray.Count); for I := 0 to VersionsArray.Count - 1 do Result.Versions[I] := ParseBadgeVersion(VersionsArray.Items[I] as TJSONObject); end; procedure ParseBadgesFromApi(const JSONResponse: string; Badges: TList); var RootObj: TJSONObject; DataArray: TJSONArray; I: Integer; begin if JSONResponse = '' then Exit; RootObj := TJSONObject.ParseJSONValue(JSONResponse) as TJSONObject; if not Assigned(RootObj) then Exit; try DataArray := RootObj.GetValue('data') as TJSONArray; for I := 0 to DataArray.Count - 1 do Badges.Add(ParseChatBadge(DataArray.Items[I] as TJSONObject)); finally RootObj.Free; end; end; procedure TTTW_API.getGlobalChatBadges(var gcb: TList); var jsonString: string; begin jsonString := getTTW('chat/badges/global', ClientID, false); ParseBadgesFromApi(jsonString, gcb); end; procedure TTTW_API.getCustomChatBadges(var ccb: TList); var jsonString: string; begin if room_id = '' then Exit; jsonString := getTTW('chat/badges?broadcaster_id=' + room_id, ClientID, false); ParseBadgesFromApi(jsonString, ccb); end; function TTTW_API.getRoomAndBot(): string; var jsonString: string; JSON: TJSONObject; dataArray: TJSONArray; begin try jsonString := getTTW('users?login=' + LowerCase(BotName_api), ClientID); JSON := TJSONObject.ParseJSONValue(jsonString) as TJSONObject; if not Assigned(JSON) then begin toLog(2, 'getRoomAndBot.GetBotID', 'Ошибка парсинга JSON'); Exit; end; try dataArray := JSON.GetValue('data') as TJSONArray; if Assigned(dataArray) and (dataArray.Count > 0) then bot_id := dataArray.Items[0].GetValue('id'); finally JSON.Free; end; except on E: Exception do toLog(2, 'getRoomAndBot.GetBotID', E.Message); end; try jsonString := getTTW('users?login=' + LowerCase(channel_name_api), ClientID); JSON := TJSONObject.ParseJSONValue(jsonString) as TJSONObject; if not Assigned(JSON) then begin toLog(2, 'getRoomAndBot.GetRoomID', 'Ошибка парсинга JSON'); Exit; end; try dataArray := JSON.GetValue('data') as TJSONArray; if Assigned(dataArray) and (dataArray.Count > 0) then room_id := dataArray.Items[0].GetValue('id'); finally JSON.Free; end; except on E: Exception do toLog(2, 'getRoomAndBot.GetRoomID', E.Message); end; Result := room_id; end; function TTTW_API.GetRoomID: string; begin Result := room_id; end; function TTTW_API.getUserbyLogin(login: string): TUser; var u: TUser; JSON: TJSONObject; dataArray: TJSONArray; jsonString: string; begin Result := Default(TUser); jsonString := getTTW('users?login=' + LowerCase(login), ClientID); if jsonString = '' then Exit; JSON := TJSONObject.ParseJSONValue(jsonString) as TJSONObject; if not Assigned(JSON) then Exit; try dataArray := JSON.GetValue('data') as TJSONArray; if Assigned(dataArray) and (dataArray.Count > 0) then begin u.id := dataArray.Items[0].GetValue('id'); u.login := dataArray.Items[0].GetValue('login'); u.DisplayName := dataArray.Items[0].GetValue('display_name'); u.created_at := ISO8601ToDate(dataArray.Items[0].GetValue('created_at')); Result := u; end; finally JSON.Free; end; end; function TTTW_API.postTTW(method, ClientID: string; params: TIdMultipartFormDataStream; isStreamer: boolean): string; var url: string; response1, Token: string; http: TIdHTTP; ssl: TIdSSLIOHandlerSocketOpenSSL; begin Result := ''; http := TIdHTTP.Create(nil); ssl := TIdSSLIOHandlerSocketOpenSSL.Create(nil); try try if isStreamer then Token := Token_api_streamer else Token := Token_api; http.IOHandler := ssl; ssl.SSLOptions.method := sslvSSLv23; http.Request.UserAgent := 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; http.Request.CustomHeaders.AddValue('Client-ID', ClientID); http.Request.CustomHeaders.AddValue('Authorization', 'Bearer ' + Token); http.Request.ContentType := 'application/json'; url := 'https://api.twitch.tv/helix/' + method; response1 := http.Post(url, params); Result := response1; except on E: Exception do toLog(2, 'postTTW', E.Message + ' [' + method + ']'); end; finally http.Free; ssl.Free; end; end; function TTTW_API.patchTTW(method, ClientID: string; params: TStringStream; isStreamer: boolean): string; var url: string; response1, Token: string; http: TIdHTTP; ssl: TIdSSLIOHandlerSocketOpenSSL; begin Result := ''; http := TIdHTTP.Create(nil); ssl := TIdSSLIOHandlerSocketOpenSSL.Create(nil); try try if isStreamer then Token := Token_api_streamer else Token := Token_api; http.IOHandler := ssl; ssl.SSLOptions.method := sslvSSLv23; http.Request.UserAgent := 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; http.Request.CustomHeaders.AddValue('Client-ID', ClientID); http.Request.CustomHeaders.AddValue('Authorization', 'Bearer ' + Token); http.Request.ContentType := 'application/json'; url := 'https://api.twitch.tv/helix/' + method; response1 := http.Patch(url, params); Result := response1; except on E: Exception do toLog(2, 'patchTTW', E.Message + ' [' + method + ']'); end; finally http.Free; ssl.Free; end; end; function TTTW_API.postTTW(method, ClientID: string; params: TStringStream; isStreamer: boolean): string; var url: string; response1, Token: string; http: TIdHTTP; ssl: TIdSSLIOHandlerSocketOpenSSL; begin Result := ''; http := TIdHTTP.Create(nil); ssl := TIdSSLIOHandlerSocketOpenSSL.Create(nil); try try if isStreamer then Token := Token_api_streamer else Token := Token_api; http.IOHandler := ssl; ssl.SSLOptions.method := sslvSSLv23; http.Request.UserAgent := 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; http.Request.CustomHeaders.AddValue('Client-ID', ClientID); http.Request.CustomHeaders.AddValue('Authorization', 'Bearer ' + Token); http.Request.ContentType := 'application/json'; url := 'https://api.twitch.tv/helix/' + method; response1 := http.Post(url, params); Result := response1; except on E: Exception do toLog(2, 'postTTW', E.Message + ' [' + method + ']'); end; finally http.Free; ssl.Free; end; end; procedure TTTW_API.raid(id: string); var p: TIdMultipartFormDataStream; begin try if room_id = '' then Exit; p := TIdMultipartFormDataStream.Create; try postTTW('raids?from_broadcaster_id=' + room_id + '&to_broadcaster_id=' + id, ClientID, p, true); finally p.Free; end; except on E: Exception do toLog(2, 'raid', E.Message); end; end; procedure TTTW_API.SendNotify(text: string); var p: TStringStream; begin try if bot_id = '' then begin toLog(1, 'SendNotify', 'bot_id пуст. Исправляю'); getRoomAndBot; if bot_id = '' then begin toLog(2, 'SendNotify', 'bot_id все равно пуст'); Exit; end; end; if room_id = '' then begin toLog(2, 'SendNotify', 'room_id пуст'); Exit; end; p := TStringStream.Create('{"message":"' + text + '","color":"primary"}', TEncoding.UTF8); try postTTW('chat/announcements?broadcaster_id=' + room_id + '&moderator_id=' + bot_id, ClientID, p); finally p.Free; end; except on E: Exception do toLog(2, 'SendNotify', E.Message); end; end; procedure TTTW_API.setModerator(id: string); var RequestData: TStringStream; begin try if Token_api_streamer = '' then Exit; if room_id = '' then Exit; RequestData := TStringStream.Create('', TEncoding.UTF8); try postTTW('moderation/moderators?broadcaster_id=' + room_id + '&user_id=' + id, ClientID, RequestData, true); finally RequestData.Free; end; except on E: Exception do toLog(2, 'setModerator', E.Message); end; end; procedure TTTW_API.setVIP(id: string); var RequestData: TStringStream; begin try if Token_api_streamer = '' then Exit; if room_id = '' then Exit; RequestData := TStringStream.Create('', TEncoding.UTF8); try postTTW('channels/vips?broadcaster_id=' + room_id + '&user_id=' + id, ClientID, RequestData, true); finally RequestData.Free; end; except on E: Exception do toLog(2, 'setVIP', E.Message); end; end; procedure TTTW_API.shoutouts(id: string); var p: TIdMultipartFormDataStream; begin try if (bot_id = '') or (room_id = '') then Exit; p := TIdMultipartFormDataStream.Create; try postTTW('chat/shoutouts?from_broadcaster_id=' + room_id + '&to_broadcaster_id=' + id + '&moderator_id=' + bot_id, ClientID, p); finally p.Free; end; except on E: Exception do toLog(2, 'shoutouts', E.Message); end; end; procedure TTTW_API.toLog(alevel: integer; amethod, amessage: string); begin if Assigned(FOnLog) then FOnLog('uTTWAPI', amethod, amessage, alevel); end; procedure TTTW_API.unBanUser(id: string); begin try if bot_id = '' then bot_id := getRoomAndBot; if (bot_id = '') or (room_id = '') then Exit; DelTTW('moderation/bans?broadcaster_id=' + room_id + '&moderator_id=' + bot_id + '&user_id=' + id, ClientID); except on E: Exception do toLog(2, 'unBanUser', E.Message); end; end; procedure TTTW_API.unRaid; begin try if room_id = '' then Exit; DelTTW('raids?broadcaster_id=' + room_id, ClientID); except on E: Exception do toLog(2, 'unRaid', E.Message); end; end; procedure TTTW_API.UpdateCustomReward(ahr: TCustomRevards); var RequestData: TStringStream; qid: string; begin try if room_id = '' then Exit; qid := ahr.id; RequestData := TStringStream.Create('{"cost": ' + IntToStr(ahr.cost) + '}', TEncoding.UTF8); try patchTTW('channel_points/custom_rewards?broadcaster_id=' + room_id + '&id=' + qid, ClientID, RequestData, true); finally RequestData.Free; end; except on E: Exception do toLog(2, 'UpdateCustomReward', E.Message); end; end; procedure TTTW_API.UpdateRedemptionStatus(ahr: TCustomRewardEvent); var qbid, qrid, qid: string; RequestData: TStringStream; begin try if (bot_id = '') or (room_id = '') then Exit; qbid := ahr.event.broadcaster_user_id; qrid := ahr.event.revard.id; qid := ahr.event.id; toLog(0, 'UpdateRedemptionStatus', 'ChannelId: ' + qbid + '; Reward.id: ' + qrid + '; Redemption.id: ' + qid); RequestData := TStringStream.Create('{"status":"CANCELED"}', TEncoding.UTF8); try patchTTW('channel_points/custom_rewards/redemptions?broadcaster_id=' + qbid + '&reward_id=' + qrid + '&id=' + qid, ClientID, RequestData, true); finally RequestData.Free; end; except on E: Exception do toLog(2, 'UpdateRedemptionStatus', E.Message); end; end; function TTTW_API.ValidateTwitchToken(const TokenName, TokenValue: string; var DayOfLive: integer): Boolean; var HTTP: TIdHTTP; SSLHandler: TIdSSLIOHandlerSocketOpenSSL; ResponseStream: TStringStream; ResponseJSON: TJSONObject; StatusCode: Integer; ResponseText: string; begin Result := False; DayOfLive := 0; if Trim(TokenValue) = '' then begin toLog(1, 'ValidateTwitchToken', 'Пустой токен'); Exit; end; HTTP := TIdHTTP.Create(nil); SSLHandler := TIdSSLIOHandlerSocketOpenSSL.Create(HTTP); ResponseStream := TStringStream.Create; try HTTP.IOHandler := SSLHandler; HTTP.Request.CustomHeaders.Values['Authorization'] := 'OAuth ' + TokenValue; HTTP.Request.UserAgent := 'YourApp/1.0'; HTTP.Request.Accept := 'application/json'; HTTP.HTTPOptions := [hoKeepOrigProtocol]; try HTTP.Get('https://id.twitch.tv/oauth2/validate', ResponseStream); StatusCode := HTTP.ResponseCode; ResponseText := ResponseStream.DataString; except on E: EIdHTTPProtocolException do begin StatusCode := E.ErrorCode; ResponseText := E.ErrorMessage; end; on E: Exception do begin toLog(2, 'ValidateTwitchToken', E.Message); Exit; end; end; if StatusCode = 200 then begin try ResponseJSON := TJSONObject.ParseJSONValue(ResponseText) as TJSONObject; if not Assigned(ResponseJSON) then begin toLog(2, 'ValidateTwitchToken', 'Ошибка парсинга JSON'); Exit; end; try if ResponseJSON.GetValue('expires_in') <> nil then begin DayOfLive := Round(ResponseJSON.GetValue('expires_in').Value.ToInteger / 86400); toLog(0, 'ValidateTwitchToken', Format('Токен действителен. Осталось: %d дней', [DayOfLive])); end; Result := True; finally ResponseJSON.Free; end; except on E: Exception do toLog(2, 'ValidateTwitchToken', E.Message); end; end else if StatusCode = 401 then begin toLog(2, 'ValidateTwitchToken', 'Invalid token'); end else begin toLog(2, 'ValidateTwitchToken', Format('HTTP %d: %s', [StatusCode, ResponseText])); end; finally ResponseStream.Free; SSLHandler.Free; HTTP.Free; end; end; procedure TTTW_API.warnUser(id: string); var RequestData: TStringStream; begin try if bot_id = '' then bot_id := getRoomAndBot; if (bot_id = '') or (room_id = '') then Exit; RequestData := TStringStream.Create('{"data": {"user_id":"' + id + '","reason":"Вам вынесено предупреждение!"}}', TEncoding.UTF8); try postTTW('moderation/warnings?broadcaster_id=' + room_id + '&moderator_id=' + bot_id, ClientID, RequestData); finally RequestData.Free; end; except on E: Exception do toLog(2, 'warnUser', E.Message); end; end; function GetStringValue(JSONObj: TJSONObject; const Name: string): string; var Val: TJSONValue; begin Val := JSONObj.GetValue(Name); if Assigned(Val) then Result := Val.Value else Result := ''; end; function GetStringArray(JSONObj: TJSONObject; const Name: string): TArray; var Arr: TJSONArray; I: Integer; begin SetLength(Result, 0); if not JSONObj.TryGetValue(Name, Arr) then Exit; SetLength(Result, Arr.Count); for I := 0 to Arr.Count - 1 do Result[I] := Arr.Items[I].Value; end; procedure ParseEmotes(const JSONString: string; EmotesList: TList); var RootObj: TJSONObject; DataArr: TJSONArray; EmoteObj: TJSONObject; ImagesObj: TJSONObject; Emote: TEmotes; I: Integer; chosenFormat, chosenTheme, chosenScale: string; foundAnimated, foundDark: Boolean; scaleVal, maxScale: Double; s: string; begin if JSONString = '' then Exit; RootObj := TJSONObject.ParseJSONValue(JSONString) as TJSONObject; if not Assigned(RootObj) then Exit; try if not RootObj.TryGetValue('data', DataArr) then Exit; for I := 0 to DataArr.Count - 1 do begin EmoteObj := DataArr.Items[I] as TJSONObject; Emote.id := GetStringValue(EmoteObj, 'id'); Emote.name := GetStringValue(EmoteObj, 'name'); Emote.tier := GetStringValue(EmoteObj, 'tier'); Emote.emote_type := GetStringValue(EmoteObj, 'emote_type'); Emote.emote_set_id := GetStringValue(EmoteObj, 'emote_set_id'); if EmoteObj.TryGetValue('images', ImagesObj) then begin Emote.images.Url1x := GetStringValue(ImagesObj, 'url_1x'); Emote.images.Url2x := GetStringValue(ImagesObj, 'url_2x'); Emote.images.Url4x := GetStringValue(ImagesObj, 'url_4x'); end; Emote.format := GetStringArray(EmoteObj, 'format'); Emote.scale := GetStringArray(EmoteObj, 'scale'); Emote.theme_mode := GetStringArray(EmoteObj, 'theme_mode'); // Выбор формата (format) foundAnimated := False; chosenFormat := 'static'; // значение по умолчанию // Проверка наличия "animated" for s in Emote.format do if s = 'animated' then begin chosenFormat := 'animated'; foundAnimated := True; Break; end; // Если не найден "animated", ищем любой не-"static" if not foundAnimated then for s in Emote.format do if s <> 'static' then begin chosenFormat := s; Break; end; // Выбор темы (theme_mode) foundDark := False; for s in Emote.theme_mode do if s = 'dark' then begin chosenTheme := 'dark'; foundDark := True; Break; end; if not foundDark then chosenTheme := 'light'; // Выбор масштаба (scale) maxScale := 0; for s in Emote.scale do begin if TryStrToFloat(s, scaleVal, TFormatSettings.Invariant) then if scaleVal > maxScale then maxScale := scaleVal; end; // Если масштабы отсутствуют, используем 1.0 if maxScale = 0 then maxScale := 1.0; chosenScale := Format('%.1f', [maxScale], TFormatSettings.Invariant); // Формирование ссылки Emote.topImage := 'https://static-cdn.jtvnw.net/emoticons/v2/' + Emote.id + '/' + chosenFormat + '/' + chosenTheme + '/' + chosenScale; EmotesList.Add(Emote); end; finally RootObj.Free; end; end; procedure TTTW_API.GetChannelEmotes(var ce: TList); var jsonres: string; begin if room_id = '' then Exit; jsonres := getTTW('chat/emotes?broadcaster_id=' + room_id, ClientID, false); ParseEmotes(jsonres, ce); end; procedure TTTW_API.GetGlobalEmotes(var ge: TList); var jsonres: string; begin jsonres := getTTW('chat/emotes/global', ClientID, false); ParseEmotes(jsonres, ge); end; end.