简体   繁体   中英

twitter api 1.1 , and oauth delphi implementation

Is there an implementation for Delphi, which connects to twitter new API, 1.1, and do operations on twitter?

they also removed all xml support, so there needs to be json operations.

the explanation from twitter site:

first we need to set an indy connection like so :

POST /oauth2/token HTTP/1.1
Host: api.twitter.com
User-Agent: My Twitter App v1.0.23
Authorization: Basic eHZ6MWV2RlM0d0VFUFRHRUZQSEJvZzpMOHFxOVBaeVJn
                     NmllS0dFS2hab2xHQzB2SldMdzhpRUo4OERSZHlPZw==
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Content-Length: 29
Accept-Encoding: gzip

grant_type=client_credentials

Then we could use the indy to get the twitter response :

HTTP/1.1 200 OK
Status: 200 OK
Content-Type: application/json; charset=utf-8
...
Content-Encoding: gzip
Content-Length: 140

{"token_type":"bearer","access_token":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAAAAAAAAAAAA%3DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}

this looks simple enough, however we need to use ssl, which force no debug with wireshark.

the code i used:

Uses EncdDecd;
Const
  Consumer_Key  = 'xvz1evFS4wEEPTGEFPHBog';
  Consumer_Secret   = 'L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg';
  Host = 'api.twitter.com/';
  Request_token_URL = 'https://api.twitter.com/oauth/request_token';
  Twitter_Content_Type = 'application/x-www-form-urlencoded;charset=UTF-8';
var
  Response:TStream;
  twittersite:TIdHttp;// assume on Form
  Trace:TMemo; //assume on Form
  IdSSLIOHandlerSocketOpenSSL1:TIdSSLIOHandlerSocketOpenSSL;//assume on Form
function EncodeBase64String(s: string): string;
var
 sIn:TSTringSTream;
begin
  sIn := TStringStream.create(s);
  result := String (EncodeBase64(Sin.Memory, sIn.Size));
  sin.Free;
end;
begin
  Response:= TMemoryStream.Create;
  try
    //Headers
    twittersite.Request.Host := Host;
    twitterSite.Request.UserAgent := 'Fucy Town 1.0';
    twitterSite.Request.CustomHeaders.Add('Authorization=Basic '+EncodeBase64String(Consumer_Key+':'+Consumer_Secret));
    twitterSite.Request.ContentType := Twitter_Content_Type;
    twitterSite.Request.CustomHeaders.Add('grant_type=client_credentials');
   //SSL
    twitterSite.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
    TwitterSite.Post(Request_token_URL,response);

    Trace.Lines.LoadFromStream(Response);
  finally
    FreeAndNil(Response);
  end;
end;

this result in 401 unauthorized.

what can be done to fix this code and get 200 ok?

DUMP

unit twitter;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdIntercept, IdLogBase, IdLogDebug, IdIOHandler, IdIOHandlerSocket,
  IdIOHandlerStack, IdSSL, IdSSLOpenSSL, IdBaseComponent, IdComponent,
  IdTCPConnection, IdTCPClient, IdHTTP, StdCtrls, IdCoder, IdCoder3to4,
  IdCoderMIME;
const
//https://dev.twitter.com/docs/auth/application-only-auth
  URL =  'https://api.twitter.com/oauth2/token';
  key = 'xvz1evFS4wEEPTGEFPHBog'; //this is example, replace with yours. 
  secret = 'L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg';//this is example, replace with yours. 
type
  TForm5 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    IdHTTP1: TIdHTTP;
    IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;
    IdLogDebug1: TIdLogDebug;
    IdEncoderMIME1: TIdEncoderMIME;
    procedure IdLogDebug1Receive(ASender: TIdConnectionIntercept;
      var ABuffer: TBytes);
    procedure IdLogDebug1Send(ASender: TIdConnectionIntercept;
      var ABuffer: TBytes);
    procedure Button1Click(Sender: TObject);
  private
    parameters:TStringList;
    ringBear:string;
    keySecretBase64:string;
    procedure initConnection;
    function getRingBear(input:TStringstream):string;
    function requestRingBear:TStringStream; //bearer token
    procedure requestTwits;
    function logoff:TStringStream;
  public
    { Public declarations }
  end;

var
  Form5: TForm5;

implementation

{$R *.dfm}

uses IdGlobal, superobject;

procedure TForm5.initConnection;
begin
    with IdSSLIOHandlerSocketOpenSSL1 do begin
      SSLOptions.Method := sslvSSLv3;
      SSLOptions.Mode :=  sslmUnassigned;
      SSLOptions.VerifyMode := [];
      SSLOptions.VerifyDepth := 2;
    end;
    with IdHTTP1 do begin
      IOHandler := IdSSLIOHandlerSocketOpenSSL1;
      ReadTimeout := 0;
      AllowCookies := True;
      ProxyParams.BasicAuthentication := False;
      ProxyParams.ProxyPort := 0;
      Request.ContentLength := -1;
      Request.ContentRangeEnd := 0;
      Request.ContentRangeStart := 0;
      Request.ContentType := 'application/x-www-form-urlencoded';
      Request.Accept := 'text/html, */*';

      Request.BasicAuthentication := False;
      Request.UserAgent := 'Mozilla/3.0 (compatible; Indy Library)';
      HTTPOptions := [hoForceEncodeParams];
    end;
    IdHTTP1.Intercept := IdLogDebug1;
    parameters.Clear;
    IdHTTP1.Request.CustomHeaders.Clear;

end;
function TForm5.logoff:TStringStream;
begin 
  result := TStringStream.Create;
  idhttp1.Request.CustomHeaders.AddValue('Authorization','Basic '+keySecretBase64);
  parameters.Add('access_token='+ringBear);
  IdHTTP1.Post('https://api.twitter.com/oauth2/invalidate_token',parameters,result);
  keySecretBase64:='';
  ringBear := '';
end; //the caller needs to free the stream

function TForm5.requestRingBear:TStringStream;
begin
  result := TStringStream.create;
  keySecretBase64 := TIdEncoderMIME.EncodeString(key+ ':' + secret, IndyTextEncoding_UTF8);
  parameters.Add('grant_type=client_credentials');
  Memo1.Lines.Add('secret and key ' + keySecretBase64);
  IdHTTP1.Request.CustomHeaders.AddValue('Authorization','Basic '+keySecretBase64);
  IdHTTP1.post(URL,parameters,result);
end;//the caller needs to free the stream

procedure TForm5.requestTwits;
begin
  IdHTTP1.Request.CustomHeaders.AddValue('Authorization','Bearer ' + ringBear);
  memo1.lines.add('twits response : ' +
    IdHTTP1.Get('https://api.twitter.com/1.1/statuses/user_timeline.json?count=100&screen_name=twitterapi'));

end;

procedure TForm5.Button1Click(Sender: TObject);

var
  json:ISuperObject;
  stream:TStringStream;

begin
  stream:=TStringStream.Create;
  parameters:=TStringList.Create;
  try
    cursor := crHourGlass;
    initConnection;
// return this: {"access_token":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAAAAAAAAAAAA%3DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","token_type":"bearer"}
    ringBear := getRingBear(requestRingBear);//the caller needs to free the stream
    initConnection;
    requestTwits;
    initConnection;
    logoff;//the caller needs to free the stream
  finally
    cursor := crDefault;
    memo1.Lines.Add('stream reposne' +stream.DataString);
    stream.Free;
    parameters.Free;
  end;
end;

function TForm5.getRingBear(input: TStringstream): string;
var
  json : ISuperObject;
begin
  json := TSuperObject.ParseStream(input,true);
  result := json.S['access_token'];
  input.Free;
end;

procedure TForm5.IdLogDebug1Receive(ASender: TIdConnectionIntercept;
  var ABuffer: TBytes);
var
  i: Integer;
  s: String;
begin
  s := '';

  for i := Low(ABuffer) to High(ABuffer) do
    s := s + chr(ABuffer[i]);

  Memo1.Lines.Add('Recived: '+s);
end;
procedure TForm5.IdLogDebug1Send(ASender: TIdConnectionIntercept;
  var ABuffer: TBytes);
var
  i: Integer;
  s: String;
begin
  s := '';

  for i := Low(ABuffer) to High(ABuffer) do
    s := s + chr(ABuffer[i]);

  Memo1.Lines.Add('SEND: '+s);
end;

end.

dfm needs to contain :

object Form5: TForm5
  Left = 0
  Top = 0
  Caption = 'Form5'
  ClientHeight = 546
  ClientWidth = 605
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  DesignSize = (
    605
    546)
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 288
    Top = 513
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 0
    OnClick = Button1Click
  end
  object Memo1: TMemo
    Left = 8
    Top = 8
    Width = 589
    Height = 499
    Anchors = [akLeft, akTop, akRight, akBottom]
    Lines.Strings = (
      'Memo1')
    ScrollBars = ssVertical
    TabOrder = 1
  end
  object IdHTTP1: TIdHTTP
    Intercept = IdLogDebug1
    IOHandler = IdSSLIOHandlerSocketOpenSSL1
    AllowCookies = True
    ProxyParams.BasicAuthentication = False
    ProxyParams.ProxyPort = 0
    Request.ContentLength = -1
    Request.Accept = 'text/html, */*'
    Request.BasicAuthentication = False
    Request.UserAgent = 'Mozilla/3.0 (compatible; Indy Library)'
    HTTPOptions = [hoForceEncodeParams]
    Left = 280
    Top = 136
  end
  object IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL
    Intercept = IdLogDebug1
    MaxLineAction = maException
    Port = 0
    DefaultPort = 0
    SSLOptions.Mode = sslmUnassigned
    SSLOptions.VerifyMode = []
    SSLOptions.VerifyDepth = 0
    Left = 296
    Top = 48
  end
  object IdLogDebug1: TIdLogDebug
    OnReceive = IdLogDebug1Receive
    OnSend = IdLogDebug1Send
    Left = 136
    Top = 192
  end
  object IdEncoderMIME1: TIdEncoderMIME
    FillChar = '='
    Left = 424
    Top = 256
  end
end

remarks:

you must have application registered in twitter.

you need the key and secret from the twitter.

this is application to application api mentioned here implementing the user oauth with pin code is another order of magnitude in complex, and need user activity to input the pin code. if you need more info on that look here which contains the detail explanation here with a very good diagram. the code there is not complete, and the dfm is missing. there is a full example somewhere on dropbox oauth, but i cant find it at the moment.

in delphi xe5 there is oauth client that comes with delphi, that should make your life easier.

the log out does not work.

and ringBear is reference to barney stinson ring bear in his wedding and not ring bearer, since twitter called it Bear(i think from beacon consents, not weddings)

The idea is simple. request a ringBear, then request the twits then logoff.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM