API-NG For Delphi Calling all Delphi developers.

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • OzPunter
    Junior Member
    • Apr 2009
    • 161

    #1

    API-NG For Delphi Calling all Delphi developers.

    As many of you know I have in the past provided sample code free for use with the old API-6. Which is (At least for me) dead easy compared to the new API-NG.

    I have received several emails from developers who have availed themselves of this free code previously, asking for assistance with the New API-NG.

    Trust me I’m just as baffled as anyone, but determined to find a solution.

    I suggest this thread as a solution to the problem for all Delphi developers.

    Would anyone who is developing Delphi code for the new API-NG respond to this thread with questions, suggestions and solutions for use by all Delphi Developers.

    BetDynamics has kindly pointed me to the location of the API-NG documentation

    Originally Posted by betdynamics
    The offline version of the documentation can be downloaded from https://api.developer.betfair.com/se...pageId=4392320

    The online version is at https://api.developer.betfair.com/se...PI-NG+Overview

    And there is example client code at https://github.com/betfair/API-NG-Delphi-Client which is an excellent working example program even though I had difficulty getting it running. See my thread here http://forum.bdp.betfair.com/showthread.php?t=2504

    Also the example code uses INDY classes extensively and I find them hard to follow and implement in my code.

    If everybody chips in here we’ll nail this big time I think, so please add your thoughts and input.

    Kind Regards
    OzPunter
  • OzPunter
    Junior Member
    • Apr 2009
    • 161

    #2
    A crude solution... And I do mean crude..

    Here’s what I’ve discovered so far. Perhaps someone can explain it better but this is the general gist of the problem.

    All Webbrowsers are not alike. Earlier versions represented the PostData as strings and the newer Webbrowsers represent postdata as a Varient in UTF-8 format.

    So if I decode the current Webbrowsers PostData by firstly converting the Varient to a string and then convert it to an Delphi Ansi string using UTF8Encode I still get embedded characters like this.

    productToken=9goGNktJms7LYhmbGo7RzKcxSC1la1m7gGwWd SbnvI8%3D

    Note the %3D on the end, that’s actually a “=” and other characters like + = / also give %HEX representations.

    No matter what I tried I couldn’t find any function that will correctly decode the PostData, so when all else fails use brut force.

    I added this type of code

    mysession := stringreplace(mysession,'%2B','+',[rfreplaceall]);
    mysession := stringreplace(mysession,'%3D','=',[rfreplaceall]);
    mysession := stringreplace(mysession,'%2F','/',[rfreplaceall]);

    Now that’s pretty crude I agree, but surprise surprise it Worked! I imagine there are other combinations of UTF-8 encoding to be taken care of, but progress is progress.

    The only function that I have found that does the decoding as required is the INDY TIdURI.URLDecode as used in the Client Example code but try as I may I can’t get it to compile in my code and I don’t know which Indy Component it relates to.

    So… The problem here I think is a basic incompatibility between the required Web Page login process, the servers, and the later versions of WebBrowsers.

    Anyone got a solution to this ?

    Comment

    • BetfairDeveloperProgram
      Administrator
      • Oct 2008
      • 680

      #3
      Hi OzPunter.

      FYI - there is also a Delphi Library for API-NG available via https://github.com/betfair/API-NG-Delphi-Client

      This covers the majority of API-NG functions, including interactive login.

      Thanks

      Neil

      Comment

      • betdynamics
        Junior Member
        • Sep 2010
        • 534

        #4
        When you say "then convert it to an Delphi Ansi string using UTF8Encode", do you actually mean "then convert it to an Delphi Ansi string using UTF8DECODE"?

        Comment

        • OzPunter
          Junior Member
          • Apr 2009
          • 161

          #5
          Originally posted by Betfair Developers Program View Post
          Hi OzPunter.

          FYI - there is also a Delphi Library for API-NG available via https://github.com/betfair/API-NG-Delphi-Client

          This covers the majority of API-NG functions, including interactive login.

          Thanks

          Neil
          Yes Neil,

          That is the code that I refer to when I'm talking about the example client. It works well if you can get it running.. The first problem I ran into was that the Twebbrowser used in the example didn't respond to the OnNavigate2 event, so when I replaced the twebbrowser it all started to work, but I have found the code very difficult to follow. It is a good example though and worth hacking apart as a starting point.

          Thank you for your responses, they are most helpful.
          Kind Regards
          OzPunter

          Comment

          • OzPunter
            Junior Member
            • Apr 2009
            • 161

            #6
            Originally posted by betdynamics View Post
            When you say "then convert it to an Delphi Ansi string using UTF8Encode", do you actually mean "then convert it to an Delphi Ansi string using UTF8DECODE"?

            Dear Bet Dynamics,

            I'm not really sure what I mean to tell the truth, a little confused (somewhat brain dead) after spending hours on this.

            It does seem that there is a fundamental mismatch between what the servers expect and what current versions of the Webbrowsers produce.

            I have tested both UTF8DEcode and UTF8ENcode and gotten virtually the same result which just adds to the confusion.

            Ultimately this remains the problem, finding a way to correctly convert the TWebbrowser’s PostData to the correct format that the BetFair server expects.

            In the example code the INDY TIdURI.URLDecode class is used, and that decodes the PostData to the correct format but I rather think that that is more a lucky accident than the intended result and shouldn’t be relied upon in the future.

            Looking at the two session strings I compared before
            (a) cHLKxUhBLs06wv8gxXwD4ht%2FyhR2skRKYL11vNrzTN0%3D
            (b) fbI+BEAV0I6m1IICwYzMcww/1IjCm6A9kqOBv/4KQh0=

            (The two strings are different because they are from different sessions)

            The string (a) is the Post Data Variant result converted to a string and in the case of (b) the string is from the TIdURI.URLDecode class.

            The values “%2F” represents a “/” in ANSI string form and the “%3D” represents a “=” sign, therefore String (a) fails as a SessionToken and string (b) with correctly encoded “/ and =” values works.

            In an end of day “mental flash” I replaced all the “illegal” character strings with their equivalents like this

            mysession := stringreplace(mysession,'%2B','+',[rfreplaceall]);
            mysession := stringreplace(mysession,'%3D','=',[rfreplaceall]);
            mysession := stringreplace(mysession,'%2F','/',[rfreplaceall]);

            and surprise.. My modified session token worked.

            Which I think points to the cause of the problem… Earlier TWebbrowsers may have worked and the TIdURI.URLDecode class also works, but current and future versions of TWebbrowsers produce PostData in a format that isn’t compatible with the servers. I don’t have any experience with TIdURI.URLDecode so I can’t comment if its format is correct, incorrect or may change in the future. I also haven’t found how to implement this class in my code so it’s a grey area.
            So now I’m searching for a way to reliably convert these Session strings to the required format as my crude string replace method cannot be a final solution.

            As always, thankyou for your assistance.

            Kind Regards
            OzPunter

            Comment

            • betdynamics
              Junior Member
              • Sep 2010
              • 534

              #7
              What version of Delphi are you using?

              Comment

              • OzPunter
                Junior Member
                • Apr 2009
                • 161

                #8
                Originally posted by betdynamics View Post
                What version of Delphi are you using?
                Delphi XE 6Pro.. Embarcadero has just released XE 7 so I imagine that that will have the same Tbrowser version

                Comment

                • betdynamics
                  Junior Member
                  • Sep 2010
                  • 534

                  #9
                  A quick web search returned the following source code for TIdURI.URLDecode:

                  Code:
                  class function TIdURI.URLDecode(ASrc: string): string;
                  var
                    i: integer;
                    ESC: string[2];
                    CharCode: integer;
                  begin
                    Result := '';    {Do not Localize}
                    ASrc := StringReplace(ASrc, '+', ' ', [rfReplaceAll]);  {do not localize}
                    i := 1;
                    while i <= Length(ASrc) do begin
                      if ASrc[i] <> '%' then begin  {do not localize}
                        Result := Result + ASrc[i]
                      end else begin
                        Inc(i); // skip the % char
                        ESC := Copy(ASrc, i, 2); // Copy the escape code
                        Inc(i, 1); // Then skip it.
                        try
                          CharCode := StrToInt('$' + ESC);  {do not localize}
                          if (CharCode > 0) and (CharCode < 256) then begin
                            Result := Result + Char(CharCode);
                          end;
                        except end;
                      end;
                      Inc(i);
                    end;
                  end;
                  Don't know if that helps you or not!

                  Comment

                  • OzPunter
                    Junior Member
                    • Apr 2009
                    • 161

                    #10
                    Success

                    Dear BetDynamics,

                    Yes it does help, thank you very much.

                    I also found code like that on the web and it is almost identical to the Indy Class supplied with Delphi (XE6 in my case) including the comments.

                    Since this class seems to be the only class capable of decoding the post data correctly to conform to the requirements of the servers I have implemented this code.

                    Firstly adding IdURI to the USERS clause, makes the TidURI.decode function available.(don’t know why I couldn’t get this to work before, probably because I was brain dead)

                    Function VariantToString(AVar: OleVariant): string;
                    {which I found here //http://delphi.cjcsoft.net/viewthread.php?tid=43936}
                    var
                    i: integer;
                    V: olevariant;
                    begin
                    Result := '';
                    if VarType(AVar) = (varVariant or varByRef) then
                    V := Variant(TVarData(AVar).VPointer^)
                    else V := AVar;
                    if VarType(V) = (varByte or varArray) then
                    try
                    for i:=VarArrayLowBound(V,1) to VarArrayHighBound(V,1) do
                    Result := Result + Chr(Byte(V[i]));
                    except;
                    end
                    else Result := V;
                    end;

                    Procedure TfrmMain.WebBrowser1BeforeNavigate2(ASender: TObject;
                    const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
                    Headers: OleVariant; var Cancel: WordBool);
                    var
                    PostDataStr:String;
                    begin
                    if (URL = 'https://www.betfair.com/') and (Length(PostData) > 0) then
                    begin
                    PostDataStr := TIdURI.URLDecode(VariantToString(PostData));
                    if pos('SUCCESS',PostDataStr) > 0 then
                    begin
                    SessionToken := copy(PostDataStr,pos('=',PostDataStr)+1,length(Pos tDataStr));
                    SessionToken := copy(SessionToken,1,pos('&',SessionToken)-1);
                    webbrowser1.Stop;
                    panel1.Visible := false;
                    tmrKeepalive.Enabled := true;
                    end;
                    end;
                    end;

                    and the resulting sessiontoken decodes correctly.

                    What I haven’t understood is why I had trouble in the first place. It seems to me that there is some underlying problem with the PostData conversions from the variant and that this may rear its ugly head sometime in the future.

                    Still this works so onward and (hopefully) upward.

                    Kind Regards
                    OzPunter

                    Comment

                    • betdynamics
                      Junior Member
                      • Sep 2010
                      • 534

                      #11
                      Glad it is working.

                      I don't think there is an underlying issue - this is just a standard decoding of an encoded string.

                      Comment

                      • OzPunter
                        Junior Member
                        • Apr 2009
                        • 161

                        #12
                        Originally posted by betdynamics View Post
                        Glad it is working.

                        I don't think there is an underlying issue - this is just a standard decoding of an encoded string.

                        Dear BetDynamics,

                        With respect, I beg to disagree.

                        If it were standard decoding then there would be dozens of examples, but in this case TIdURI.URLDecode seems to be the ONLY function capable of complying, therefore it seems to be the odd one out.

                        We’ll see if other high level languages run into a similar problem later.

                        Kind Regards
                        OzPunter

                        Comment

                        • OzPunter
                          Junior Member
                          • Apr 2009
                          • 161

                          #13
                          List Of Events

                          Having sorted something out for Login the next mystery is getting a list of events.

                          After trial and error for ages I managed to get a list of events for horse racing in Australia, more by chance than design.

                          One really annoying factor is that Harness Racing (Trots and Pacers) is lumped in with Gallops so it seems that as with the old API you have to download each of the events and filter out the unwanted events.

                          Is there any way to just retrieve Gallops instead of everything?

                          I tried putting “GALLOPS” in the textQuery variable and got no results, in fact whatever I put in the textQuery variable results in no results, so setting it to “” seems to be the only acceptable value.

                          Comment

                          • OzPunter
                            Junior Member
                            • Apr 2009
                            • 161

                            #14
                            Originally posted by OzPunter View Post
                            Having sorted something out for Login the next mystery is getting a list of events.

                            After trial and error for ages I managed to get a list of events for horse racing in Australia, more by chance than design.

                            One really annoying factor is that Harness Racing (Trots and Pacers) is lumped in with Gallops so it seems that as with the old API you have to download each of the events and filter out the unwanted events.

                            Is there any way to just retrieve Gallops instead of everything?

                            I tried putting “GALLOPS” in the textQuery variable and got no results, in fact whatever I put in the textQuery variable results in no results, so setting it to “” seems to be the only acceptable value.
                            Finally after hours I can finally get a list of events and the runners... Now I have to figure out how to get prices and then I can start on my programs... The New API is seriously convoluted and complex. More head scratching to come..

                            Comment

                            • betdynamics
                              Junior Member
                              • Sep 2010
                              • 534

                              #15
                              The short answer is no - I don't think there is an easy way to get the gallops only races.

                              The textQuery seems to be very hit and miss to me (it works on the work "Place" but not on other things!).

                              For your particular query, I'm not sure that you want to be looking at the Event information to find out if the race is gallops - instead look at the MarketCatalogue as the Pace, Trot, etc information is held in the marketName (I think).

                              However, I think you will still have to load all relevant markets and then discard those that you are not interested in.

                              [Edit]
                              Ah - our posts crossed. Glad to see that you have managed to get the list that you require. The new API is definitely funky but once you get used to it, it seems to be quite reasonable. There are still some things that I would like to see added and some things made easier, but the transition is not too bad.
                              Last edited by betdynamics; 19-09-2014, 08:37 AM.

                              Comment

                              Working...
                              X