05.10.2010

Использование OleObject в помощь геймеру.

Всем привет.
Сегодня расскажу как можно использовать элемент WebBrowser для написания эмуляторов человеческой активности.


Рассматривать будем на примере игры Грани реальности и напишу я простенького бота для ловли крабов.

Форма нашей программы будет такой:


Главный элемент - компонент вебброузер. Сверху панелька с anchor = top и right. И на ней 3 кнопки и 1 текстфилд. Кнопки выполняют след. ф-ции: запуск бота,

идти в город/ на место ловли. Label выполняет роль своеобразного статуса, конечно можно было сделать по другому, но мне так удобней :)

Немного теории.
Вот 2 линка, которые желательны к исследованию перед тем как пойдем дальше.
http://www.cryer.co.uk/brian/delphi/twebbrowser/twebbrowser_oleobject.htm
http://www.cryer.co.uk/brian/delphi/twebbrowser/read_write_form_elements.htm

Для того чтобы не вставлять элемент адресной строки я в функцию onShow вставил код следующего содержания:

procedure TForm1.FormShow(Sender: TObject);
begin
WebBrowser1.Navigate('http://www.ereality.ru/');
end;


Думаем дальше. Как программно определить элемент с которым взаимодействует пользователь, да ещё и нажать на него(ввести что-то)?
Для броузера Firefox есть 2 интересных надстройки: Dom inspector и Firebug.
Сравнивать я их сейчас не буду, а покажу как использовать.
Ставим FireBug и дальше так же как на картинке щелкаем на линк правой кнопкой мыши и выбираем анализировать элемент.

Внизу откроется всплывающие окошко и в нем нам будет интересно вот что:

Обратим внимание, что создатели игры не дураки, и тоже кое-что понимают. Поэтому они ввели дополнительный элемент для защиты от начинающих программистов -

вторая кнопка Ловить. Но мы можем отсортировать две кнопки, по какому-либо параметру, вернее даже по innertext/innerhtml либо outertext/outerhtml.

Вглубляться в это я не буду, смысл в том что inner - то что внутри не считая тэга, а outer все вместе с тэгом контейнером.
Вот функция для совершения клика:


procedure PressLink;
var links:OleVariant;
    i:integer;
begin
    links := Form1.WebBrowser1.OleObject.Document.Frames.Item(0).Document.getelementsbytagname('a');
    for i := 0 to links.length-1 do
      begin
        if AnsiSameStr(links.item(i).innertext,'Крабы: Ловить') and (Pos('name',links.item(i).outerhtml)> 0) then
          begin
            links.item(i).click;
             break;
          end;
    end;
end;
;
Конструкция Document.Frames.Item(0).Document Указывает на то что мы берем документ первого фрейм. В конструкции данного сайта используется IFRAME который

один. В документе которого мы берем все ссылки(а), и ищем среди них ту где есть текст Крабы: Ловить и ещё эта ссылка должна содержать аттрибут name

Нажав на линк первый раз мы обломаемся - капча. Ну от капчи такого рода никуда не дется, кроме как заплатить деньги что бы взрослые дяти/тети понажимали за

вас. Здесь я должен снять шляпу перед создателями - ни графически, ни аналитически её сломать мне не удалось.


Опять же проведем процедуру с firebug, посмотрим как выглядит наша капча. Я сделал функцию - сигнализацию, которая будет кричать и вопить если появилась

капча, которая кстати содержится в форме, единственной на весь документ.


function CheckCaptcha:Boolean;
begin
if Form1.WebBrowser1.OleObject.Document.Frames.Item(0).Document.Forms.length > 0 then
  begin
  Result:= False;
  MessageBeep(MB_ICONHAND);
  ShowWindow(Application.Handle, SW_RESTORE)
  end
  else
  begin
  Result:= True;
  end;
end;


Ну вроде все. Мы начали ловить.


Но что-же делать, как определить когда нам жать кнопку достать и куда жать?
Нам придет на помощь firebug.

]

Видим что в элементе с id td_wrk(которые уникален!) имеем наши проценты и 2 кнопки: видимую и невидимую, нам надо жать видимую.

В итого пораскинув мозгами получаем вот такую вот ф-цию:

function CheckPercentage:Boolean;
var container:OleVariant;
    m,k:integer;
    CheckString:String;
begin
try
container := Form1.WebBrowser1.OleObject.Document.Frames.Item(0).Document.getelementbyid('td_wrk');
CheckString:=container.innertext;

m:=Pos('%',CheckString);
k:=Pos('+',CheckString);

if(m = 0) then Result:= True;

      if m - k > 2 then
      begin
          Result:= True
      end
        else if (StrToInt(CheckString[m - 1]) > 0) then
        begin
          Result:= True
        end
        else
        begin
           Result:= False;
        end

except
Result:=False;
end;
end;

Берем позицию + и позицию % между ними текст. Вроде все просто и понятно. Если выскочит ошибка функцию отправит фалс и через некоторое время будет вызвана

опять.

Нажимаем кнопку так:

procedure CatchUp;//Nazat na knopku dostat
var buttons:OleVariant;
i:integer;
begin
buttons:=Form1.WebBrowser1.OleObject.Document.Frames.Item(0).Document.getelementsbytagname('button');
for i := 0 to buttons.length-1 do
  begin
    if AnsiSameStr(buttons.item(i).innertext,'Достать') and (Pos('WIDTH: 90px',buttons.item(i).outerhtml)> 0) then
      begin
      sLog('','Lets catchup');
        buttons.item(i).click;
        break;
      end;
  end;
end;


Таким образом мы получили нужные нам действия:
1. жмем на ссылку
2. если капча то кричим
3. ждем, жмем достать

Ниже полный листинг программы:



unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, OleCtrls, SHDocVw, StdCtrls, MSHTML, ExtCtrls,XpMan,Math,uLog;

type
  TForm1 = class(TForm)
    WebBrowser1: TWebBrowser;
    Panel1: TPanel;
    Button4: TButton;
    edt1: TEdit;
    Button1: TButton;
    Button2: TButton;
    procedure FormShow(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
implementation


{$R *.dfm}

procedure Split
   (const Delimiter: Char;
    Input: string;
    const Strings: TStrings) ;
begin
   Assert(Assigned(Strings)) ;
   Strings.Clear;
   Strings.Delimiter := Delimiter;
   Strings.DelimitedText := Input;
end;

function CheckWeight:Boolean;//Rabotaet
var container:OleVariant;
    weight:TStringList;
begin
container := Form1.WebBrowser1.OleObject.Document.getelementbyid('b_massa');
      weight:= TStringList.Create;
      Split('/',container.innertext,weight);
      if(StrToInt(weight[0]) < StrToInt(weight[1])) then
         begin
              sLog('','weight is ok');
              Result:= true;
         end
      else
         begin
              sLog('','weight is not ok');
              Result:= false;
         end;
end;

procedure delay(delay: DWORD);
 var
   lTicks: DWORD;
 begin
   lTicks := GetTickCount + delay;
   repeat
     Sleep(100);
     Application.ProcessMessages;
   until (lTicks <= GetTickCount);
 end;

procedure PressLink;
var links:OleVariant;
    i:integer;
begin
    links := Form1.WebBrowser1.OleObject.Document.Frames.Item(0).Document.getelementsbytagname('a');
    for i := 0 to links.length-1 do
      begin
        if AnsiSameStr(links.item(i).innertext,'Крабы: Ловить') and (Pos('name',links.item(i).outerhtml)> 0) then
          begin
          sLog('','Lets press link');
            links.item(i).click;
             break;
          end;
    end;
end;

function CheckCaptcha:Boolean;//Rabotaet
begin
if Form1.WebBrowser1.OleObject.Document.Frames.Item(0).Document.Forms.length > 0 then
  begin
  sLog('','Cap4a found');
  Result:= False;
  MessageBeep(MB_ICONHAND);
  ShowWindow(Application.Handle, SW_RESTORE)
  end
  else
  begin
  sLog('','cap4a not found');
  Result:= True;
  end;
end;

function CheckPercentage:Boolean;//Rabotaet
var container:OleVariant;
    m,k:integer;
    CheckString:String;
begin
try
container := Form1.WebBrowser1.OleObject.Document.Frames.Item(0).Document.getelementbyid('td_wrk');
CheckString:=container.innertext;
sLog('','Percentage:this is container outerhtml');
sLog('',container.outerhtml);
sLog('','Percentage:this is container innertext');
sLog('',container.innertext);

m:=Pos('%',CheckString);
k:=Pos('+',CheckString);

if(m = 0) then Result:= True;

sLog('','Value of m:' + IntToStr(m));
sLog('','Value of k:' + IntToStr(k));
      if m - k > 2 then
      begin
          sLog('','Percentage: number length > 1symb');
          Result:= True
      end
        else if (StrToInt(CheckString[m - 1]) > 0) then
        begin
          sLog('','Percentage: number value > 0');
          Result:= True
        end
        else
        begin
          sLog('','Percentage: number value = 0');
           Result:= False;
        end

except
sLog('','Error reading percantage');
Result:=False;
end;

end;

function CheckCage:Boolean;//Rabotaet
var container:OleVariant;
    CheckString:String;
begin
container := Form1.WebBrowser1.OleObject.Document.getelementbyid('kt');
CheckString:=container.innertext;
if Pos('Ваша клетка слома',CheckString) > 0 then
begin
sLog('','Cage is broken');
Result:=False
end
else
begin
sLog('','cage is fine');
Result:=True;
end;

end;

procedure CatchUp;//Nazat na knopku dostat
var buttons:OleVariant;
i:integer;
begin
buttons:=Form1.WebBrowser1.OleObject.Document.Frames.Item(0).Document.getelementsbytagname('button');
for i := 0 to buttons.length-1 do
  begin
    if AnsiSameStr(buttons.item(i).innertext,'Достать') and (Pos('WIDTH: 90px',buttons.item(i).outerhtml)> 0) then
      begin
      sLog('','Lets catchup');
        buttons.item(i).click;
        break;
      end;
  end;
end;

function CheckMove:Boolean;//Работает
var container:OleVariant;
begin
container:=Form1.WebBrowser1.OleObject.Document.Frames.Item(0).Document.getelementbyid('p_mv_timer');
if Pos('Перемещаемся на сектор',container.innertext) > 0 then
Result:= True
else
Result:= False;
end;

procedure Move(direction:boolean); //True - gorod//false obratno //rabotaet
var i:integer;
b:boolean;
begin
If direction then
begin
for I := 0 to 7 do
  begin
       Form1.WebBrowser1.OleObject.Document.Frames.Item(0).Document.getelementbyid('s7').click;
       repeat
       delay(3000);
       b:=CheckMove;
       until b = False;
  end;
end
else
begin
  for I := 0 to 7 do
  begin
       Form1.WebBrowser1.OleObject.Document.Frames.Item(0).Document.getelementbyid('s3').click;
       repeat
       delay(1000);
       b:=CheckMove;
       until b = False;
  end;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Move(True);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
Move(False);
end;

procedure TForm1.Button4Click(Sender: TObject);
var b:Boolean;
begin
while AnsiSameStr(edt1.text, 'go') do
begin
Randomize;

if not CheckWeight then
begin
  ShowMessage('oops');
  Exit;
end;
delay(1000);
PressLink;
delay(Round(3000*Random));
delay(3000);

repeat
  delay(1000);
  b:= CheckCaptcha;
until b = true;
delay(1000);

if not CheckCage then
begin
ShowMessage('oops');
Exit;
end;

repeat
delay(3000);
b:= CheckPercentage;
until b = True;

delay(4000);

CatchUp;

delay(3000);
end;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
WebBrowser1.Navigate('http://www.ereality.ru/');
uLog.LogFileName := ExtractFilePath(GetModuleName(HInstance))+'log.log';
uLog.LogLevel := 0;
uLog.EnableMessages:=True
end;

end.


P.S.Прошу прощения за некоторые корявости. Первый блин комом. Обещаю в будущем делать все грамотно.
P.P.S. Этот материал имеет только образовательную цель. Я в эту игру не играю, а просто показываю на её примере как можно использовать элементы webbrowser. Эмуляция действий человека является мошенничеством и наказывается баном. Используйте на свой страх и риск. Прошу прощения, если нанес кому-то ущерб.






Комментариев нет:

Отправить комментарий