реструктуризация файлов, добавление вебчатов
This commit is contained in:
@@ -0,0 +1,364 @@
|
||||
unit uTTWIRC;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.Classes, System.SysUtils, IdIRC, IdSSLOpenSSL, IdContext,
|
||||
FMX.Forms, IdGlobal, IdComponent, System.StrUtils, uRecords;
|
||||
|
||||
|
||||
|
||||
type
|
||||
TNotifyEvent = procedure(s: string) of object;
|
||||
TJoinEvent = procedure(aNick: string) of object;
|
||||
TMyStatusEvent = procedure(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string) of object;
|
||||
tOnMessageRecord = procedure(aRecord: TTwitchChatMessage) of object;
|
||||
TOnLog = procedure(aModul: string; aMethod: string; aMessage: string; aLevel: integer) of object;
|
||||
|
||||
TTTW = class
|
||||
private
|
||||
ws: TIdIRC;
|
||||
ssl: TIdSSLIOHandlerSocketOpenSSL;
|
||||
FOnLog: TOnLog;
|
||||
FOnStatus: TMyStatusEvent;
|
||||
FOnDisConnect: TNotifyEvent;
|
||||
FOnJoin: TJoinEvent;
|
||||
FOnMessage: TNotifyEvent;
|
||||
FOnMessageRecord: tOnMessageRecord;
|
||||
channel_name: string;
|
||||
room_id: string;
|
||||
channel_id: string;
|
||||
procedure wsConnected(Sender: TObject);
|
||||
procedure wsStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string);
|
||||
procedure wsDisconnected(Sender: TObject);
|
||||
procedure wsDataIn(ASender: TIdContext; AIn: boolean; const AMessage: string);
|
||||
procedure Join(ASender: TIdContext; const ANickname, AHost, AChannel: string);
|
||||
procedure se(ASender: TIdContext; AErrorCode: Integer; const AErrorMessage: String);
|
||||
procedure RAW(text: string);
|
||||
procedure toLog(aLevel: integer; aMethod: string; aMessage: string);
|
||||
procedure toParse(t: string);
|
||||
public
|
||||
constructor Create(Sender: TObject);
|
||||
destructor Destroy; override;
|
||||
procedure Init(a_oauth, a_channel, a_username: string);
|
||||
procedure Connect;
|
||||
procedure Disconnect;
|
||||
procedure sendMessage(text: string);
|
||||
function ParseTwitchChatMessage(const AMessage: string): TTwitchChatMessage;
|
||||
function GetRoom_ID: string;
|
||||
function Pars(T_, text, _T: string): string;
|
||||
property OnLog: TOnLog read FOnLog write FOnLog;
|
||||
property OnStatus: TMyStatusEvent read FOnStatus write FOnStatus;
|
||||
property OnDisConnect: TNotifyEvent read FOnDisConnect write FOnDisConnect;
|
||||
property OnJoin: TJoinEvent read FOnJoin write FOnJoin;
|
||||
property OnMessage: TNotifyEvent read FOnMessage write FOnMessage;
|
||||
property OnMessageRecord: tOnMessageRecord read FOnMessageRecord write FOnMessageRecord;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses uGeneral; // ��� ������� � ��������� Log
|
||||
|
||||
const
|
||||
LOG_INFO = 0;
|
||||
LOG_WARNING = 1;
|
||||
LOG_ERROR = 2;
|
||||
LOG_DEBUG = 3;
|
||||
|
||||
procedure TTTW.toLog(aLevel: integer; aMethod: string; aMessage: string);
|
||||
begin
|
||||
if aLevel < 0 then
|
||||
aLevel := LOG_INFO
|
||||
else if aLevel > LOG_DEBUG then
|
||||
aLevel := LOG_DEBUG;
|
||||
|
||||
if Assigned(FOnLog) then
|
||||
FOnLog('uTTWIRC', aMethod, aMessage, aLevel);
|
||||
end;
|
||||
|
||||
constructor TTTW.Create(Sender: TObject);
|
||||
begin
|
||||
try
|
||||
ws := TIdIRC.Create;
|
||||
ssl := TIdSSLIOHandlerSocketOpenSSL.Create;
|
||||
ws.IOHandler := ssl;
|
||||
ws.OnConnected := wsConnected;
|
||||
ws.OnDisconnected := wsDisconnected;
|
||||
ws.OnStatus := wsStatus;
|
||||
ws.OnRaw := wsDataIn;
|
||||
ws.OnJoin := Join;
|
||||
ws.OnServerError := se;
|
||||
except
|
||||
on E: Exception do
|
||||
toLog(LOG_ERROR, 'Create', E.Message);
|
||||
end;
|
||||
end;
|
||||
|
||||
destructor TTTW.Destroy;
|
||||
begin
|
||||
try
|
||||
if Assigned(ws) then
|
||||
begin
|
||||
ws.OnConnected := nil;
|
||||
ws.OnDisconnected := nil;
|
||||
ws.OnStatus := nil;
|
||||
ws.OnRaw := nil;
|
||||
ws.OnJoin := nil;
|
||||
ws.OnServerError := nil;
|
||||
ws.IOHandler := nil;
|
||||
ws.Free;
|
||||
end;
|
||||
if Assigned(ssl) then
|
||||
ssl.Free;
|
||||
except
|
||||
on E: Exception do
|
||||
;
|
||||
end;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TTTW.ParseTwitchChatMessage(const AMessage: string): TTwitchChatMessage;
|
||||
var
|
||||
s: string;
|
||||
LSpacePos: Integer;
|
||||
LParamStr, LRestStr: string;
|
||||
LParams: TArray<string>;
|
||||
I: Integer;
|
||||
LKeyValue: TArray<string>;
|
||||
LUsernamePart: string;
|
||||
LMessagePos: Integer;
|
||||
begin
|
||||
Result := Default(TTwitchChatMessage);
|
||||
s := AMessage;
|
||||
|
||||
// �������� ����������� ��������
|
||||
LSpacePos := Pos(' ', s);
|
||||
if LSpacePos = 0 then
|
||||
Exit;
|
||||
|
||||
LParamStr := Copy(s, 1, LSpacePos - 1);
|
||||
LRestStr := Copy(s, LSpacePos + 1, Length(s) - LSpacePos);
|
||||
|
||||
// ������������ ���������
|
||||
LParams := LParamStr.Split([';']);
|
||||
for I := 0 to High(LParams) do
|
||||
begin
|
||||
LKeyValue := LParams[I].Split(['=']);
|
||||
if Length(LKeyValue) = 2 then
|
||||
begin
|
||||
case AnsiIndexStr(LKeyValue[0], [
|
||||
'@badge-info', 'badges', 'client-nonce', 'color', 'display-name', 'emotes',
|
||||
'first-msg', 'id', 'mod', 'returning-chatter', 'room-id', 'subscriber',
|
||||
'tmi-sent-ts', 'turbo', 'user-id', 'user-type', 'vip'
|
||||
]) of
|
||||
0: Result.BadgeInfo := LKeyValue[1];
|
||||
1: Result.Badges := LKeyValue[1];
|
||||
2: Result.ClientNonce := LKeyValue[1];
|
||||
3: Result.Color := LKeyValue[1];
|
||||
4: Result.DisplayName := LKeyValue[1];
|
||||
5: Result.Emotes := LKeyValue[1];
|
||||
6: Result.FirstMsg := StrToIntDef(LKeyValue[1], 0);
|
||||
7: Result.Id := LKeyValue[1];
|
||||
8: Result.Moder := StrToIntDef(LKeyValue[1], 0);
|
||||
9: Result.ReturningChatter := StrToIntDef(LKeyValue[1], 0);
|
||||
10: Result.RoomId := LKeyValue[1];
|
||||
11: Result.Subscriber := StrToIntDef(LKeyValue[1], 0);
|
||||
12: Result.TmiSentTs := StrToInt64Def(LKeyValue[1], 0);
|
||||
13: Result.Turbo := StrToIntDef(LKeyValue[1], 0);
|
||||
14: Result.UserId := LKeyValue[1];
|
||||
15: Result.UserType := LKeyValue[1];
|
||||
16: Result.Vip := StrToIntDef(LKeyValue[1], 0);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
if LRestStr.StartsWith(':') then
|
||||
begin
|
||||
LUsernamePart := Copy(LRestStr, 1, Pos('!', LRestStr) - 1);
|
||||
Result.Username := LUsernamePart.Substring(1);
|
||||
end
|
||||
else
|
||||
Result.Username := '';
|
||||
|
||||
// ��������� �����
|
||||
LMessagePos := Pos('PRIVMSG #', LRestStr);
|
||||
if LMessagePos > 0 then
|
||||
begin
|
||||
Inc(LMessagePos, Length('PRIVMSG #'));
|
||||
Result.Channel := Copy(LRestStr, LMessagePos, PosEx(' ', LRestStr, LMessagePos) - LMessagePos);
|
||||
end
|
||||
else
|
||||
Result.Channel := '';
|
||||
|
||||
// �������� �����
|
||||
LMessagePos := Pos(' :', LRestStr);
|
||||
if LMessagePos > 0 then
|
||||
Result.Message := Copy(LRestStr, LMessagePos + 2, Length(LRestStr) - LMessagePos - 1)
|
||||
else
|
||||
Result.Message := '';
|
||||
end;
|
||||
|
||||
procedure TTTW.Init(a_oauth, a_channel, a_username : string);
|
||||
begin
|
||||
try
|
||||
channel_name := a_channel;
|
||||
ws.Host := 'irc.chat.twitch.tv';
|
||||
ws.Port := 6697;
|
||||
ssl.SSLOptions.SSLVersions := [sslvSSLv23];
|
||||
ws.Password := 'oauth:' + a_oauth;
|
||||
ws.Nickname := a_username;
|
||||
channel_name := a_channel;
|
||||
// Token := a_oauth;
|
||||
except
|
||||
on E: Exception do
|
||||
toLog(LOG_ERROR, 'Init', E.Message);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTTW.Connect;
|
||||
begin
|
||||
try
|
||||
if not ws.Connected then
|
||||
begin
|
||||
ws.Connect;
|
||||
ws.Raw('CAP REQ :twitch.tv/membership twitch.tv/tags twitch.tv/commands');
|
||||
ws.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
|
||||
end;
|
||||
except
|
||||
on E: Exception do
|
||||
toLog(LOG_ERROR, 'Connect', E.Message);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTTW.Disconnect;
|
||||
begin
|
||||
try
|
||||
if ws.Connected then
|
||||
begin
|
||||
ws.Disconnect;
|
||||
end;
|
||||
except
|
||||
on E: Exception do
|
||||
toLog(LOG_ERROR, 'Disconnect', E.ClassName + ': ' + E.Message);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TTTW.GetRoom_ID: string;
|
||||
begin
|
||||
result:=room_id;
|
||||
end;
|
||||
|
||||
procedure TTTW.sendMessage(text: string);
|
||||
begin
|
||||
try
|
||||
ws.Say('#' + channel_name, text);
|
||||
except
|
||||
on E: Exception do
|
||||
toLog(LOG_ERROR, 'sendMessage', E.Message);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTTW.RAW(text: string);
|
||||
begin
|
||||
try
|
||||
ws.Raw(text);
|
||||
except
|
||||
on E: Exception do
|
||||
toLog(LOG_ERROR, 'RAW', E.Message);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTTW.wsConnected(Sender: TObject);
|
||||
begin
|
||||
if Assigned(FOnStatus) then
|
||||
FOnStatus(ws, TIdStatus.hsConnected, 'Connected to Twitch IRC');
|
||||
toLog(LOG_INFO, 'wsConnected', 'Connected to Twitch IRC');
|
||||
end;
|
||||
|
||||
procedure TTTW.wsStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string);
|
||||
begin
|
||||
if Assigned(FOnStatus) then
|
||||
FOnStatus(ASender, AStatus, AStatusText);
|
||||
end;
|
||||
|
||||
procedure TTTW.wsDisconnected(Sender: TObject);
|
||||
begin
|
||||
if Assigned(FOnDisConnect) then
|
||||
FOnDisConnect('Disconnected');
|
||||
toLog(LOG_WARNING, 'wsDisconnected', 'Disconnected from Twitch IRC');
|
||||
end;
|
||||
|
||||
procedure TTTW.wsDataIn(ASender: TIdContext; AIn: boolean; const AMessage: string);
|
||||
begin
|
||||
|
||||
toLog(LOG_DEBUG, 'wsDataIn', AMessage);
|
||||
|
||||
if Pos('CAP * ACK', AMessage) <> 0 then
|
||||
begin
|
||||
Sleep(200);
|
||||
ws.Raw('JOIN #' + channel_name);
|
||||
end;
|
||||
|
||||
toParse(AMessage);
|
||||
end;
|
||||
|
||||
procedure TTTW.toParse(t: string);
|
||||
var
|
||||
LTwitchChatMessage:tTwitchChatMessage;
|
||||
begin
|
||||
try
|
||||
if (Pos('room-id=', t) <> 0) and (Pos('ROOMSTATE', t) <> 0) then
|
||||
room_id := Pars('room-id=', t, ';');
|
||||
|
||||
if Pos('NOTICE * :Login authentication failed', t) <> 0 then
|
||||
begin
|
||||
|
||||
toLog(2, 'toParse', 'Токен бота просрочен');
|
||||
Disconnect;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if Pos('PRIVMSG', t) <> 0 then
|
||||
begin
|
||||
LTwitchChatMessage := ParseTwitchChatMessage(t);
|
||||
if Assigned(FOnMessageRecord) then
|
||||
FOnMessageRecord(LTwitchChatMessage);
|
||||
end;
|
||||
except
|
||||
on E: Exception do
|
||||
toLog(2, 'toParse', E.Message);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTTW.Join(ASender: TIdContext; const ANickname, AHost, AChannel: string);
|
||||
begin
|
||||
if Assigned(FOnJoin) then
|
||||
FOnJoin(ANickname);
|
||||
toLog(LOG_INFO, 'Join', ANickname + ' joined ' + AChannel);
|
||||
end;
|
||||
|
||||
procedure TTTW.se(ASender: TIdContext; AErrorCode: Integer; const AErrorMessage: String);
|
||||
begin
|
||||
toLog(LOG_ERROR, 'se', AErrorMessage);
|
||||
end;
|
||||
|
||||
function TTTW.Pars(T_, text, _T: string): string;
|
||||
var
|
||||
a, b: Integer;
|
||||
begin
|
||||
Result := '';
|
||||
if (T_ = '') or (text = '') or (_T = '') then
|
||||
Exit;
|
||||
a := Pos(T_, text);
|
||||
if a = 0 then
|
||||
Exit
|
||||
else
|
||||
a := a + Length(T_);
|
||||
text := Copy(text, a, Length(text) - a + 1);
|
||||
b := Pos(_T, text);
|
||||
if b > 0 then
|
||||
Result := Copy(text, 1, b - 1);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user