unit uCustomEmoties; interface uses System.Classes, System.Generics.Collections, System.JSON, uRecords, IdHTTP, IdSSLOpenSSL, System.Net.HttpClient, System.SysUtils; type TOnLog = procedure(aModul: string; aMethod: string; aMessage: string; aLevel: integer) of object; type TBTTV = class(TObject) private list: TList; FOnLog: TOnLog; procedure AddEmotesGlobalJson(const JsonStr: string); procedure AddEmotesUserJson(const JsonStr: string); function GetHTTP(aMethod: string): string; procedure toLog(alevel: integer; amethod: string; amessage: string); public constructor Create; destructor Destroy; override; procedure GetGlobal; procedure GetCustom(uid: string); function GenerateURL(emoteName: string): string; property OnLog: TOnLog read FOnLog write FOnLog; end; type T7TV = class(TObject) private list7: TList; FOnLog: TOnLog; procedure AddEmotesGlobalJson(const JsonStr: string); procedure AddEmotesUserJson(const JsonStr: string); function GetHTTP(aMethod: string): string; public constructor Create; destructor Destroy; override; procedure GetGlobal; procedure GetCustom(uid: string); function GenerateURL(emoteName: string): string; procedure toLog(alevel: integer; amethod: string; amessage: string); property OnLog: TOnLog read FOnLog write FOnLog; end; implementation { TBTTV } constructor TBTTV.Create; begin inherited; list := TList.Create; end; destructor TBTTV.Destroy; begin FreeAndNil(list); inherited; end; procedure TBTTV.AddEmotesGlobalJson(const JsonStr: string); var JSONValue: TJSONValue; JSONArray: TJSONArray; EmoteObj: TJSONObject; NewEmote: TBTTVr; i: Integer; begin JSONValue := TJSONObject.ParseJSONValue(JsonStr); if not Assigned(JSONValue) then Exit; try if not (JSONValue is TJSONArray) then Exit; JSONArray := TJSONArray(JSONValue); for i := 0 to JSONArray.Count - 1 do begin if not (JSONArray.Items[i] is TJSONObject) then Continue; EmoteObj := TJSONObject(JSONArray.Items[i]); NewEmote := Default(TBTTVr); if Assigned(EmoteObj.GetValue('id')) then NewEmote.id := EmoteObj.GetValue('id').Value; if Assigned(EmoteObj.GetValue('code')) then NewEmote.code := EmoteObj.GetValue('code').Value; if not NewEmote.id.IsEmpty and not NewEmote.code.IsEmpty then list.Add(NewEmote); end; finally JSONValue.Free; end; end; procedure TBTTV.AddEmotesUserJson(const JsonStr: string); var JSONValue, ChannelEmotes: TJSONValue; JSONArray: TJSONArray; EmoteObj: TJSONObject; NewEmote: TBTTVr; i: Integer; begin JSONValue := TJSONObject.ParseJSONValue(JsonStr); if not Assigned(JSONValue) then Exit; try // Обработка channelEmotes ChannelEmotes := TJSONObject(JSONValue).GetValue('channelEmotes'); if (ChannelEmotes is TJSONArray) then begin JSONArray := TJSONArray(ChannelEmotes); for i := 0 to JSONArray.Count - 1 do begin if not (JSONArray.Items[i] is TJSONObject) then Continue; EmoteObj := TJSONObject(JSONArray.Items[i]); NewEmote := Default(TBTTVr); if Assigned(EmoteObj.GetValue('id')) then NewEmote.id := EmoteObj.GetValue('id').Value; if Assigned(EmoteObj.GetValue('code')) then NewEmote.code := EmoteObj.GetValue('code').Value; if not NewEmote.id.IsEmpty and not NewEmote.code.IsEmpty then list.Add(NewEmote); end; end; // Обработка sharedEmotes ChannelEmotes := TJSONObject(JSONValue).GetValue('sharedEmotes'); if (ChannelEmotes is TJSONArray) then begin JSONArray := TJSONArray(ChannelEmotes); for i := 0 to JSONArray.Count - 1 do begin if not (JSONArray.Items[i] is TJSONObject) then Continue; EmoteObj := TJSONObject(JSONArray.Items[i]); NewEmote := Default(TBTTVr); if Assigned(EmoteObj.GetValue('id')) then NewEmote.id := EmoteObj.GetValue('id').Value; if Assigned(EmoteObj.GetValue('code')) then NewEmote.code := EmoteObj.GetValue('code').Value; if not NewEmote.id.IsEmpty and not NewEmote.code.IsEmpty then list.Add(NewEmote); end; end; finally JSONValue.Free; end; end; function TBTTV.GenerateURL(emoteName: string): string; var emote: TBTTVr; begin Result := ''; for emote in list do begin if emote.code = emoteName then begin Result := 'https://cdn.betterttv.net/emote/' + emote.id + '/1x'; Exit; end; end; end; procedure TBTTV.GetCustom(uid: string); begin if not uid.IsEmpty then AddEmotesUserJson(GetHTTP('users/twitch/' + uid)); end; procedure TBTTV.GetGlobal; begin AddEmotesGlobalJson(GetHTTP('emotes/global')); end; function TBTTV.GetHTTP(aMethod: string): string; var http: TIdHTTP; ssl: TIdSSLIOHandlerSocketOpenSSL; begin Result := ''; try http := TIdHTTP.Create(nil); ssl := TIdSSLIOHandlerSocketOpenSSL.Create(nil); try http.IOHandler := ssl; ssl.SSLOptions.SSLVersions := [sslvTLSv1_2]; http.Request.UserAgent := 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; Result := http.Get('https://api.betterttv.net/3/cached/' + aMethod); finally ssl.Free; http.Free; end; except on E: Exception do begin toLog(2,'GetCustom',e.Message); Result := ''; end; end; end; procedure TBTTV.toLog(alevel: integer; amethod, amessage: string); begin if Assigned(FOnLog) then FOnLog('uCustomEmoties.TBTTV', amethod, amessage, alevel); end; { T7TV } constructor T7TV.Create; begin inherited; list7 := TList.Create; end; destructor T7TV.Destroy; begin FreeAndNil(list7); inherited; end; procedure T7TV.AddEmotesGlobalJson(const JsonStr: string); var Root: TJSONObject; EmotesArray: TJSONArray; EmoteObj, DataObj, HostObj: TJSONObject; FilesArray: TJSONArray; i: Integer; Emote: T7TVr; BaseUrl: string; begin Root := TJSONObject.ParseJSONValue(JsonStr) as TJSONObject; if not Assigned(Root) then Exit; try EmotesArray := Root.GetValue('emotes') as TJSONArray; if not Assigned(EmotesArray) then Exit; for i := 0 to EmotesArray.Count - 1 do begin if not (EmotesArray.Items[i] is TJSONObject) then Continue; EmoteObj := EmotesArray.Items[i] as TJSONObject; Emote := Default(T7TVr); // Получение базовых данных if Assigned(EmoteObj.GetValue('id')) then Emote.id := EmoteObj.GetValue('id').Value; if Assigned(EmoteObj.GetValue('name')) then Emote.code := EmoteObj.GetValue('name').Value; // Получение URL DataObj := EmoteObj.GetValue('data') as TJSONObject; if Assigned(DataObj) then begin HostObj := DataObj.GetValue('host') as TJSONObject; if Assigned(HostObj) then begin if Assigned(HostObj.GetValue('url')) then begin BaseUrl := 'https:' + HostObj.GetValue('url').Value; FilesArray := HostObj.GetValue('files') as TJSONArray; if Assigned(FilesArray) and (FilesArray.Count > 0) and (FilesArray.Items[0] is TJSONObject) then begin Emote.url := BaseUrl + '/' + (FilesArray.Items[0] as TJSONObject).GetValue('name').Value; end; end; end; end; if not Emote.id.IsEmpty and not Emote.code.IsEmpty and not Emote.url.IsEmpty then list7.Add(Emote); end; finally Root.Free; end; end; procedure T7TV.AddEmotesUserJson(const JsonStr: string); var Root, EmoteSet, EmoteObj, DataObj, HostObj: TJSONObject; EmotesArr, FilesArr: TJSONArray; i: Integer; Emote: T7TVr; BaseUrl: string; begin Root := TJSONObject.ParseJSONValue(JsonStr) as TJSONObject; if not Assigned(Root) then Exit; try if not Root.TryGetValue('emote_set', EmoteSet) then Exit; EmotesArr := EmoteSet.GetValue('emotes') as TJSONArray; if not Assigned(EmotesArr) then Exit; for i := 0 to EmotesArr.Count - 1 do begin if not (EmotesArr.Items[i] is TJSONObject) then Continue; EmoteObj := EmotesArr.Items[i] as TJSONObject; Emote := Default(T7TVr); // Получение базовых данных if Assigned(EmoteObj.GetValue('id')) then Emote.id := EmoteObj.GetValue('id').Value; if Assigned(EmoteObj.GetValue('name')) then Emote.code := EmoteObj.GetValue('name').Value; // Получение URL DataObj := EmoteObj.GetValue('data') as TJSONObject; if Assigned(DataObj) then begin HostObj := DataObj.GetValue('host') as TJSONObject; if Assigned(HostObj) then begin if Assigned(HostObj.GetValue('url')) then begin BaseUrl := 'https:' + HostObj.GetValue('url').Value; FilesArr := HostObj.GetValue('files') as TJSONArray; if Assigned(FilesArr) and (FilesArr.Count > 0) and (FilesArr.Items[0] is TJSONObject) then begin Emote.url := BaseUrl + '/' + (FilesArr.Items[0] as TJSONObject).GetValue('name').Value; end; end; end; end; if not Emote.id.IsEmpty and not Emote.code.IsEmpty and not Emote.url.IsEmpty then list7.Add(Emote); end; finally Root.Free; end; end; function T7TV.GenerateURL(emoteName: string): string; var emote: T7TVr; begin Result := ''; for emote in list7 do begin if emote.code = emoteName then begin Result := emote.url; Exit; end; end; end; procedure T7TV.GetCustom(uid: string); begin if not uid.IsEmpty then AddEmotesUserJson(GetHTTP('users/twitch/' + uid)); end; procedure T7TV.GetGlobal; begin AddEmotesGlobalJson(GetHTTP('emote-sets/global')); end; function T7TV.GetHTTP(aMethod: string): string; var HttpClient: THTTPClient; Response: IHTTPResponse; begin Result := ''; HttpClient := THTTPClient.Create; try try HttpClient.UserAgent := 'Mozilla/5.0'; Response := HttpClient.Get('https://api.7tv.app/v3/' + aMethod); Result := Response.ContentAsString; except on E: Exception do begin toLog(2,'GetHTTP',e.Message); Result := ''; end; end; finally HttpClient.Free; end; end; procedure T7TV.toLog(alevel: integer; amethod, amessage: string); begin if Assigned(FOnLog) then FOnLog('uCustomEmoties.T7TV', amethod, amessage, alevel); end; end.