1135 lines
31 KiB
ObjectPascal
1135 lines
31 KiB
ObjectPascal
unit uTTWAPI;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.Classes, System.SysUtils, IdHTTP, IdSSLOpenSSL, System.JSON, FMX.Forms,
|
|
IdMultipartFormData, DateUtils, uDataBase, System.Generics.Collections,
|
|
uRecords;
|
|
|
|
type
|
|
TTTW_API = class(TObject)
|
|
private
|
|
Token_api: string;
|
|
Token_api_streamer: string;
|
|
ClientID: string;
|
|
channel_name_api: string;
|
|
BotName_api: string;
|
|
FChatBadges:tlist<TChatBadge>;
|
|
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;
|
|
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<TCustomRevards>);
|
|
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<TChatBadge>);
|
|
procedure getCustomChatBadges(var ccb: Tlist<TChatBadge>);
|
|
procedure GetChannelEmotes(var ce: Tlist<TEmotes>);
|
|
procedure GetGlobalEmotes(var ge: Tlist<TEmotes>);
|
|
function ValidateTwitchToken(const TokenName, TokenValue: string; var DayOfLive:integer): Boolean;
|
|
end;
|
|
TChatBadges = TList<TChatBadge>;
|
|
TEmotesList = TList<TEmotes>;
|
|
var
|
|
room_id: string = '';
|
|
bot_id: string = '';
|
|
|
|
implementation
|
|
|
|
uses uGeneral;
|
|
|
|
constructor TTTW_API.Create(Sender: TObject);
|
|
begin
|
|
// Èíèöèàëèçàöèÿ
|
|
end;
|
|
|
|
function TTTW_API.createCustomReward(title: string; cost: string;
|
|
promt: string = ''; isUserInput: boolean = false):TCustomRevards;
|
|
var
|
|
RequestData: TStringStream;
|
|
s, s1, json: string; i:integer;
|
|
cr:TCustomRevards; JSONData:TJSONObject; JSONArray:TJSONArray;
|
|
begin
|
|
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 + ' }', CP_UTF8);
|
|
json := postTTW('channel_points/custom_rewards?broadcaster_id=' + room_id, ClientID,
|
|
RequestData, true);
|
|
if json = '' then
|
|
// fLog.toLog(1,'TTW_API','createCustomReward','Íàãðàäà íå ñîçäàëàñü, çàïðîñ âðíóë ïóñòîé îòâåò');
|
|
JSONData := TJSONObject.ParseJSONValue(JSON) as TJSONObject;
|
|
try
|
|
if Assigned(JSONData) then
|
|
begin
|
|
JSONArray := JSONData.GetValue<TJSONArray>('data');
|
|
for i := 0 to JSONArray.Count - 1 do
|
|
begin
|
|
cr.id := JSONArray.Items[i].GetValue<string>('id');
|
|
cr.title := JSONArray.Items[i].GetValue<string>('title');
|
|
cr.promt := JSONArray.Items[i].GetValue<string>('prompt');
|
|
cr.cost := JSONArray.Items[i].GetValue<integer>('cost');
|
|
cr.is_user_input_required := JSONArray.Items[i].GetValue<boolean>
|
|
('is_user_input_required');
|
|
end;
|
|
end;
|
|
finally
|
|
JSONData.Free;
|
|
end;
|
|
result:=cr;
|
|
except
|
|
on E: Exception do
|
|
// Form1.Log(2, 'TTTW_API.createCustomReward', E.Message);
|
|
// flog.toLog(2,'TTW_API','createCustomReward',E.Message);
|
|
end;
|
|
|
|
end;
|
|
|
|
destructor TTTW_API.Destroy;
|
|
begin
|
|
// Îñâîáîæäåíèå ðåñóðñîâ
|
|
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
|
|
exit;
|
|
if room_id = '' then
|
|
exit;
|
|
|
|
RequestData := TStringStream.Create('{"data": {"user_id":"' + id +
|
|
'","reason":"no reason"}}');
|
|
try
|
|
postTTW('moderation/bans?broadcaster_id=' + room_id + '&moderator_id=' +
|
|
bot_id, ClientID, RequestData);
|
|
finally
|
|
RequestData.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
//Form1.Log(2, 'TTTW_API.banUser', E.Message);
|
|
// flog.toLog(2,'TTW_API','banUser',E.Message);
|
|
end;
|
|
end;
|
|
|
|
procedure TTTW_API.banUserTime(id: string; aTime: integer);
|
|
var
|
|
|
|
RequestData: TStringStream;
|
|
begin
|
|
try
|
|
if bot_id = '' then
|
|
exit;
|
|
if room_id = '' then
|
|
exit;
|
|
|
|
RequestData := TStringStream.Create('{"data": {"user_id":"' + id +
|
|
'","duration":' + inttostr(aTime) + '}}');
|
|
try
|
|
postTTW('moderation/bans?broadcaster_id=' + room_id + '&moderator_id=' +
|
|
bot_id, ClientID, RequestData);
|
|
finally
|
|
RequestData.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
//Form1.Log(2, 'TTTW_API.banUserTime', E.Message);
|
|
// flog.toLog(2,'TTW_API','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
|
|
///Form1.Log(2, 'TTTW_API.deleteCustomReward', E.Message);
|
|
// flog.toLog(2,'TTW_API','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
|
|
//Form1.Log(2, 'TTTW_API.delModerator', E.Message);
|
|
// flog.toLog(2,'TTW_API','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
|
|
//Form1.Log(2, 'TTTW_API.DelTTW', E.Message+' ['+method+']');
|
|
// flog.toLog(2,'TTW_API','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
|
|
//Form1.Log(2, 'TTTW_API.delVIP', E.Message);
|
|
// flog.toLog(2,'TTW_API','delVIP',E.Message);
|
|
end;
|
|
end;
|
|
|
|
procedure TTTW_API.getCustomReward(var acr: Tlist<TCustomRevards>);
|
|
var
|
|
JSON: string;
|
|
sl: TCustomRevards;
|
|
JSONData: TJSONObject;
|
|
JSONArray: TJSONArray;
|
|
i: integer;
|
|
|
|
begin
|
|
|
|
try
|
|
if Token_api_streamer = '' then
|
|
begin
|
|
exit;
|
|
end;
|
|
if room_id = '' then
|
|
exit;
|
|
|
|
JSON := getTTW('channel_points/custom_rewards?broadcaster_id=' + room_id +
|
|
'&only_manageable_rewards=true', ClientID, true);
|
|
JSONData := TJSONObject.ParseJSONValue(JSON) as TJSONObject;
|
|
try
|
|
if Assigned(JSONData) then
|
|
begin
|
|
JSONArray := JSONData.GetValue<TJSONArray>('data');
|
|
for i := 0 to JSONArray.Count - 1 do
|
|
begin
|
|
sl.id := JSONArray.Items[i].GetValue<string>('id');
|
|
sl.title := JSONArray.Items[i].GetValue<string>('title');
|
|
sl.promt := JSONArray.Items[i].GetValue<string>('prompt');
|
|
sl.cost := JSONArray.Items[i].GetValue<integer>('cost');
|
|
sl.is_user_input_required := JSONArray.Items[i].GetValue<boolean>
|
|
('is_user_input_required');
|
|
acr.add(sl);
|
|
end;
|
|
|
|
end;
|
|
finally
|
|
JSONData.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
//Form1.Log(2, 'TTTW_API.getCustomReward', E.Message);
|
|
// flog.toLog(2,'TTW_API','getCustomReward',E.Message);
|
|
end;
|
|
|
|
end;
|
|
|
|
function TTTW_API.getFollow(id: string): tdatetime;
|
|
var
|
|
s: string;
|
|
begin
|
|
s := getTTW('channels/followers?user_id=' + id + '&broadcaster_id=' + room_id,
|
|
ClientID);
|
|
Result := strToDate(GetFollowedAtFromJson(s));
|
|
end;
|
|
|
|
function TTTW_API.GetFollowedAtFromJson(jsonString: string): string;
|
|
var
|
|
JSON: TJSONObject;
|
|
dataArray: TJSONArray;
|
|
begin
|
|
Result := '';
|
|
JSON := TJSONObject.ParseJSONValue(jsonString) as TJSONObject;
|
|
try
|
|
if Assigned(JSON) then
|
|
begin
|
|
dataArray := JSON.GetValue('data') as TJSONArray;
|
|
if Assigned(dataArray) and (dataArray.Count > 0) then
|
|
Result := DateToStr
|
|
(ISO8601ToDate(dataArray.Items[0].GetValue<string>('followed_at')));
|
|
end;
|
|
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
|
|
////Form1.Log(2, 'TTTW_API.getTTW', E.Message+' ['+method+']');
|
|
// flog.toLog(2,'TTW_API','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;
|
|
try
|
|
if Assigned(jsonObject) then
|
|
begin
|
|
avg_viewers := jsonObject.GetValue<integer>('avg_viewers');
|
|
max_viewers := jsonObject.GetValue<integer>('max_viewers');
|
|
hours_watched := jsonObject.GetValue<integer>('hours_watched');
|
|
followers := jsonObject.GetValue<integer>('followers');
|
|
followers_total := jsonObject.GetValue<integer>('followers_total');
|
|
end;
|
|
finally
|
|
if Assigned(jsonObject) then
|
|
jsonObject.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
//Form1.Log(2, 'TTTW_API.getTTWStat', E.Message);
|
|
// flog.toLog(2,'TTW_API','getTTWStat',E.Message);
|
|
end;
|
|
finally
|
|
|
|
http.Free;
|
|
ssl.Free;
|
|
end;
|
|
end;
|
|
|
|
function ParseBadgeVersion(VersionObj: TJSONObject): TBadgeVersion;
|
|
begin
|
|
// Äîáàâüòå ïðîâåðêè íà nil äëÿ êàæäîãî ïîëÿ
|
|
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
|
|
// Äîáàâüòå ïðîâåðêè íà nil äëÿ êàæäîãî ïîëÿ
|
|
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: TChatBadges);
|
|
var
|
|
RootObj: TJSONObject;
|
|
DataArray: TJSONArray;
|
|
I: Integer;
|
|
Badge: TChatBadge;
|
|
begin
|
|
|
|
RootObj := TJSONObject.ParseJSONValue(JSONResponse) as TJSONObject;
|
|
try
|
|
DataArray := RootObj.GetValue('data') as TJSONArray;
|
|
|
|
for I := 0 to DataArray.Count - 1 do
|
|
begin
|
|
Badge := ParseChatBadge(DataArray.Items[I] as TJSONObject);
|
|
Badges.Add(Badge);
|
|
end;
|
|
finally
|
|
RootObj.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTTW_API.getGlobalChatBadges(var gcb: Tlist<TChatBadge>);
|
|
var
|
|
jsonString: string;
|
|
global:TChatBadge;
|
|
begin
|
|
JSONString:=getTTW('chat/badges/global',ClientID,false);
|
|
ParseBadgesFromApi(JSONString,gcb);
|
|
end;
|
|
|
|
|
|
|
|
procedure TTTW_API.getCustomChatBadges(var ccb: Tlist<TChatBadge>);
|
|
var
|
|
jsonString: string;
|
|
begin
|
|
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;
|
|
try
|
|
if Assigned(JSON) then
|
|
begin
|
|
dataArray := JSON.GetValue('data') as TJSONArray;
|
|
if Assigned(dataArray) and (dataArray.Count > 0) then
|
|
begin
|
|
bot_id := dataArray.Items[0].GetValue<string>('id');
|
|
end;
|
|
end;
|
|
finally
|
|
JSON.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
// fLog.toLog(2,'TTW_API','getRoomAndBot.GetBotID',e.Message);
|
|
end;
|
|
try
|
|
jsonString := getTTW('users?login=' + LowerCase(channel_name_api), ClientID);
|
|
JSON := TJSONObject.ParseJSONValue(jsonString) as TJSONObject;
|
|
try
|
|
if Assigned(JSON) then
|
|
begin
|
|
dataArray := JSON.GetValue('data') as TJSONArray;
|
|
if Assigned(dataArray) and (dataArray.Count > 0) then
|
|
begin
|
|
room_id := dataArray.Items[0].GetValue<string>('id');
|
|
end;
|
|
end;
|
|
finally
|
|
JSON.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
// fLog.toLog(2,'TTW_API','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
|
|
jsonString := getTTW('users?login=' + LowerCase(login), ClientID);
|
|
JSON := TJSONObject.ParseJSONValue(jsonString) as TJSONObject;
|
|
try
|
|
if Assigned(JSON) then
|
|
begin
|
|
dataArray := JSON.GetValue('data') as TJSONArray;
|
|
if Assigned(dataArray) and (dataArray.Count > 0) then
|
|
begin
|
|
u.id := dataArray.Items[0].GetValue<string>('id');
|
|
u.login := dataArray.Items[0].GetValue<string>('login');
|
|
u.DisplayName := dataArray.Items[0].GetValue<string>('display_name');
|
|
u.created_at := ISO8601ToDate
|
|
(dataArray.Items[0].GetValue<string>('created_at'));
|
|
end;
|
|
end;
|
|
finally
|
|
JSON.Free;
|
|
end;
|
|
Result := u;
|
|
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
|
|
//Form1.Log(2, 'TTTW_API.postTTW', E.Message+' ['+method+']');
|
|
// flog.toLog(2,'TTW_API','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
|
|
//Form1.Log(2, 'TTTW_API.patchTTW', E.Message+' ['+method+']');
|
|
//flog.toLog(2,'TTW_API','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
|
|
//Form1.Log(2, 'TTTW_API.postTTW', E.Message+' ['+method+']');
|
|
//flog.toLog(2,'TTW_API','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
|
|
//Form1.Log(2, 'TTTW_API.raid', E.Message);
|
|
// flog.toLog(2,'TTW_API','raid',E.Message);
|
|
end;
|
|
end;
|
|
|
|
procedure TTTW_API.SendNotify(text: string);
|
|
var
|
|
p: TStringStream;
|
|
begin
|
|
try
|
|
if bot_id = '' then
|
|
begin
|
|
//flog.toLog(1,'TTW_API','SendNotify','bot_id ïóñò. Èñïðàâëÿþ');
|
|
getRoomAndBot;
|
|
if bot_id = '' then
|
|
begin
|
|
// flog.toLog(2,'TTW_API','SendNotify','bot_id âñå ðàâíî ïóñò');
|
|
exit;
|
|
end;
|
|
end;
|
|
if room_id = '' then
|
|
begin
|
|
// flog.toLog(2,'TTW_API','SendNotify','room_id ïóñò');
|
|
exit;
|
|
end;
|
|
|
|
p := TStringStream.Create('{"message":"' + text +
|
|
'","color":"primary"}', CP_UTF8);
|
|
try
|
|
postTTW('chat/announcements?broadcaster_id=' + room_id + '&moderator_id='
|
|
+ bot_id, ClientID, p);
|
|
finally
|
|
p.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
// flog.toLog(2,'TTW_API','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('');
|
|
try
|
|
postTTW('moderation/moderators?broadcaster_id=' + room_id + '&user_id=' +
|
|
id, ClientID, RequestData, true);
|
|
finally
|
|
RequestData.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
//Form1.Log(2, 'TTTW_API.setModerator', E.Message);
|
|
// flog.toLog(2,'TTW_API','setModerator',E.Message);
|
|
end;
|
|
end;
|
|
|
|
procedure TTTW_API.setVIP(id: string);
|
|
var
|
|
userID: string;
|
|
RequestData: TStringStream;
|
|
begin
|
|
try
|
|
if Token_api_streamer = '' then
|
|
exit;
|
|
if room_id = '' then
|
|
exit;
|
|
|
|
RequestData := TStringStream.Create('');
|
|
try
|
|
postTTW('channels/vips?broadcaster_id=' + room_id + '&user_id=' + userID,
|
|
ClientID, RequestData, true);
|
|
finally
|
|
RequestData.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
//Form1.Log(2, 'TTTW_API.setVIP', E.Message);
|
|
// flog.toLog(2,'TTW_API','setVIP',E.Message);
|
|
end;
|
|
end;
|
|
|
|
procedure TTTW_API.shoutouts(id: string);
|
|
var
|
|
p: TIdMultipartFormDataStream;
|
|
begin
|
|
try
|
|
if bot_id = '' then
|
|
exit;
|
|
if 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
|
|
//Form1.Log(2, 'TTTW_API.shoutouts', E.Message);
|
|
// flog.toLog(2,'TTW_API','shoutouts',E.Message);
|
|
end;
|
|
end;
|
|
|
|
procedure TTTW_API.unBanUser(id: string);
|
|
begin
|
|
try
|
|
if bot_id = '' then
|
|
exit;
|
|
if room_id = '' then
|
|
exit;
|
|
|
|
DelTTW('moderation/bans?broadcaster_id=' + room_id + '&moderator_id=' +
|
|
bot_id + '&user_id=' + id, ClientID);
|
|
except
|
|
on E: Exception do
|
|
//Form1.Log(2, 'TTTW_API.unBanUser', E.Message);
|
|
// flog.toLog(2,'TTW_API','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
|
|
//Form1.Log(2, 'TTTW_API.unRaid', E.Message);
|
|
// flog.toLog(2,'TTW_API','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) +
|
|
'}', CP_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
|
|
//Form1.Log(2, 'TTTW_API.UpdateCustomReward', E.Message);
|
|
// flog.toLog(2,'TTW_API','UpdateCustomReward',E.Message);
|
|
end;
|
|
end;
|
|
|
|
procedure TTTW_API.UpdateRedemptionStatus(ahr: TCustomRewardEvent);
|
|
var
|
|
qbid, qrid, qid: string;
|
|
RequestData: TStringStream;
|
|
begin
|
|
try
|
|
if bot_id = '' then
|
|
exit;
|
|
if room_id = '' then
|
|
exit;
|
|
qbid := ahr.event.broadcaster_user_id;
|
|
qrid := ahr.event.revard.id;
|
|
qid := ahr.event.id;
|
|
//Form1.Log(1, 'TTTW_API.UpdateRedemptionStatus', 'ChannelId: ' + qbid +
|
|
// '; Reward.id: ' + qrid + '; Redemption.id: ' + qid);
|
|
|
|
// flog.toLog(1,'TTW_API','UpdateRedemptionStatus','ChannelId: ' + qbid +
|
|
// '; Reward.id: ' + qrid + '; Redemption.id: ' + qid);
|
|
RequestData := TStringStream.Create('{"status":"CANCELED"}', CP_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
|
|
// Form1.Log(2, 'TTTW_API.UpdateRedemptionStatus', E.Message);
|
|
// flog.toLog(2,'TTW_API','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;
|
|
if Trim(TokenValue) = '' then
|
|
begin
|
|
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
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
if StatusCode = 200 then
|
|
begin
|
|
try
|
|
ResponseJSON := TJSONObject.ParseJSONValue(ResponseText) as TJSONObject;
|
|
try
|
|
if ResponseJSON.GetValue('expires_in') <> nil then
|
|
begin
|
|
//fLog.toLog(0, 'TokenCheck', TokenName,
|
|
// Format('Òîêåí äåéñòâèòåëåí. Îñòàëîñü: %d ñåê. Êëèåíò: %s',
|
|
// [ResponseJSON.GetValue('expires_in').Value.ToInteger,
|
|
// ResponseJSON.GetValue('client_id').Value]));
|
|
DayOfLive:=round(ResponseJSON.GetValue('expires_in').Value.ToInteger/60/60/24);
|
|
end;
|
|
Result := True;
|
|
finally
|
|
ResponseJSON.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
//fLog.toLog(2, 'TokenCheck', 'JSON Parse', E.Message);
|
|
end;
|
|
end
|
|
else if StatusCode = 401 then
|
|
begin
|
|
//fLog.toLog(2, 'TokenCheck', TokenName, 'Invalid token');
|
|
DayOfLive:=0;
|
|
end
|
|
else
|
|
begin
|
|
DayOfLive:=0;
|
|
// fLog.toLog(2, 'TokenCheck', TokenName,
|
|
// 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
|
|
exit;
|
|
if room_id = '' then
|
|
exit;
|
|
|
|
RequestData := TStringStream.Create('{"data": {"user_id":"' + id +
|
|
'","reason":"Âàì âûíåñåíî ïðåäóïðåæäåíèå!"}}', CP_UTF8);
|
|
try
|
|
postTTW('moderation/warnings?broadcaster_id=' + room_id + '&moderator_id='
|
|
+ bot_id, ClientID, RequestData);
|
|
finally
|
|
RequestData.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
//Form1.Log(2, 'TTTW_API.warnUser', E.Message);
|
|
// flog.toLog(2,'TTW_API','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<string>;
|
|
var
|
|
Arr: TJSONArray;
|
|
I: Integer;
|
|
begin
|
|
SetLength(Result, 0);
|
|
if not JSONObj.TryGetValue<TJSONArray>(Name, Arr) then Exit;
|
|
|
|
SetLength(Result, Arr.Count);
|
|
for I := 0 to Arr.Count - 1 do
|
|
Result[I] := Arr.Items[I].Value;
|
|
end;
|
|
|
|
// Ôóíêöèÿ äëÿ ïðåîáðàçîâàíèÿ JSON â ñïèñîê ýìîäçè
|
|
procedure ParseEmotes(const JSONString: string; EmotesList: TEmotesList);
|
|
var
|
|
RootObj: TJSONObject;
|
|
DataArr: TJSONArray;
|
|
EmoteObj: TJSONObject;
|
|
ImagesObj: TJSONObject;
|
|
Emote: TEmotes;
|
|
I: Integer;
|
|
begin
|
|
|
|
RootObj := TJSONObject.ParseJSONValue(JSONString) as TJSONObject;
|
|
try
|
|
if not RootObj.TryGetValue<TJSONArray>('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<TJSONObject>('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');
|
|
|
|
EmotesList.Add(Emote);
|
|
end;
|
|
finally
|
|
RootObj.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTTW_API.GetChannelEmotes(var ce: Tlist<TEmotes>);
|
|
var jsonres:string;
|
|
begin
|
|
jsonres:=getTTW('chat/emotes?broadcaster_id='+room_id,ClientID,false);
|
|
ParseEmotes(jsonres, ce);
|
|
end;
|
|
|
|
procedure TTTW_API.GetGlobalEmotes(var ge: Tlist<TEmotes>);
|
|
var jsonres:string;
|
|
begin
|
|
jsonres:=getTTW('chat/emotes/global',ClientID,false);
|
|
ParseEmotes(jsonres, ge);
|
|
end;
|
|
|
|
end.
|