Recent

Author Topic: TwilioLib - Send SMS from Freepascal  (Read 8885 times)

Trenatos

  • Hero Member
  • *****
  • Posts: 535
    • MarcusFernstrom.com
TwilioLib - Send SMS from Freepascal
« on: June 24, 2018, 10:56:00 pm »
If you need to send SMS, I just put up a simple library on GitHub - https://github.com/MFernstrom/TwilioLib

Right now it just allows for sending SMS, but I'll be implementing more Twilio functionality as I go.

Xor-el

  • Sr. Member
  • ****
  • Posts: 404
Re: TwilioLib - Send SMS from Freepascal
« Reply #1 on: June 24, 2018, 11:28:05 pm »
nice job. thanks.
some issues exist though

1). you seem to forget to free your "twilio" variable in your README Example.

2). TwilioResult is not defined in your readme. (seems it's a type error).

improvement tips

1). you can make your TTwilio class inherit from TInterfacedObject in mode Delphi to enable reference counting via interfaces.

2). it is not advisible to return an "unmanaged" type from a function like you did in "function TTwilio.send_sms( from_number, to_number, message: String ): TStringlist;"
where you returned a stringlist.
make that a procedure an allow the user pass in an Initialized stringlist via a "var" parameter so that the user is certain that he is responsible for freeing the stringlist.
in the method, check if the stringlist is initialized, and if not create it or raise an exception.
« Last Edit: June 24, 2018, 11:39:58 pm by Xor-el »

Trenatos

  • Hero Member
  • *****
  • Posts: 535
    • MarcusFernstrom.com
Re: TwilioLib - Send SMS from Freepascal
« Reply #2 on: June 25, 2018, 12:31:43 am »
Thanks for the feedback Xor-el!

I've updated the unit and example, would you mind taking a look again?

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: TwilioLib - Send SMS from Freepascal
« Reply #3 on: June 25, 2018, 11:59:18 am »
Nice code and it works,
Some remarks:
1 - You should clean up the uses clauses in the library and example. (done)
2 - You should remove the global vars from the library (done)
3 - There are many memory leaks, so test with -glh (today no time to solve them for you) (done)
4 - I would change the Tstringlist parameter to Tstrings, so you can assign to other Tstrings descendants like memo.lines, listbox.Items etc.(done)
5 - I would change the string parameters to const strings (see next) (faster and better reference management) (done)
6 - I have implemented the interface like so, because otherwise it makes little sense and added a destructor. (done)
Code: Pascal  [Select][+][-]
  1. type
  2.   ITwilio = interface
  3.   ['{7C5DF745-E72C-4652-9FB1-94992F181207}']
  4.     procedure send_sms(const from_number, to_number, message: String;var Output: TStrings);
  5.   end;
  6.      
  7.   { TTwilio }
  8.  
  9.   TTwilio = class(TInterfacedObject, ITwilio)
  10.   strict private  // there is a slight security risk otherwise. See behavior of private vs strict private
  11.     account_sid, auth_token, url : String;
  12.   public
  13.     constructor create(const sid, token : String);
  14.     destructor destroy;override;
  15.     procedure send_sms(const from_number, to_number, message: String;var Output: TStrings);
  16.   end;
  17.  
7 - Can you make the license LGPL instead of GPL v3?
8 - I would use Camel Case instead of underscores, but that is preference.
9 - I would re-arrange the exception handling like so:(done, but kept the exception in)
Code: Pascal  [Select][+][-]
  1.    if Assigned(Output) then
  2.   .....
  3.     // the code
  4.   end {$if UseExceptions}else raise Exeption.Create('No output assigned'){$endif};
The latter is because there is no point in continuing, so maybe just fail silently.
« Last Edit: June 25, 2018, 02:07:11 pm by Thaddy »
Specialize a type, not a var.

Xor-el

  • Sr. Member
  • ****
  • Posts: 404
Re: TwilioLib - Send SMS from Freepascal
« Reply #4 on: June 25, 2018, 01:07:24 pm »
Nice code and it works,
Some remarks:
1 - You should clean up the uses clauses in the library and example.
2 - You should remove the global vars from the library
3 - There are many memory leaks, so test with -glh (today no time to solve them for you)
4 - I would change the Tstringlist parameter to Tstrings, so you can assign to other Tstrings descendants like memo.lines, listbox.Items etc.
5 - I would change the string parameters to const strings (see next) (faster and better reference management)
6 - I have implemented the interface like so, because otherwise it makes little sense and added a destructor.
Code: Pascal  [Select][+][-]
  1. type
  2.   ITwilio = interface
  3.   ['{7C5DF745-E72C-4652-9FB1-94992F181207}']
  4.     procedure send_sms(const from_number, to_number, message: String;var Output: TStrings);
  5.   end;
  6.      
  7.   { TTwilio }
  8.  
  9.   TTwilio = class(TInterfacedObject, ITwilio)
  10.   strict private  // there is a slight security risk otherwise. See behavior of private vs strict private
  11.     account_sid, auth_token, url : String;
  12.   public
  13.     constructor create(const sid, token : String);
  14.     destructor destroy;override;
  15.     procedure send_sms(const from_number, to_number, message: String;var Output: TStrings);
  16.   end;
  17.  
7 - Can you make the license LGPL instead of GPL v3?
8 - I would use Camel Case instead of underscores, but that is preference.
9 - I would re-arrange the exception handling like so:
Code: Pascal  [Select][+][-]
  1.    if Assigned(Output) then
  2.   .....
  3.     // the code
  4.   end {$if UseExceptions}else raise Exeption.Create('No output assigned'){$endif};
The latter is because there is no point in continuing, so maybe just fail silently.

Thaddy, good job, I was about creating a pull request with more or less the changes you have suggested.

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: TwilioLib - Send SMS from Freepascal
« Reply #5 on: June 25, 2018, 01:25:59 pm »
@Trenatos
I have re-arranged the code and fixed the memory leaks.
See http://forum.lazarus.freepascal.org/index.php/topic,41667.msg289580.html#msg289580 for what I fixed and changed.
I also added an ETwilioException class.
Feel free to put it in your repository.
Code: Pascal  [Select][+][-]
  1. {======================================================
  2.   Author       Marcus Fernström
  3.   Contributor  Thaddy de Koning
  4.   License      GPL3
  5.   Version      0.3
  6.   GitHub       https://github.com/MFernstrom/TwilioLib
  7. =======================================================}
  8. unit TwilioLib;
  9.  
  10. {$mode objfpc}{$H+}{$notes off}
  11.  
  12. interface
  13.  
  14. uses
  15.   Classes, SysUtils, fphttpclient, fpjson, jsonparser;
  16.  
  17. type
  18.   ITwilio = interface
  19.   ['{7C5DF745-E72C-4652-9FB1-94992F181207}']
  20.     procedure send_sms(const from_number, to_number, message: String;var Output: TStrings);
  21.   end;
  22.      
  23.   { TTwilio }
  24.  
  25.   TTwilio = class(TInterfacedObject, ITwilio)
  26.   strict private
  27.     account_sid, auth_token, url : String;
  28.   public
  29.     constructor create(const sid, token : String);
  30.     destructor destroy;override;
  31.     procedure send_sms(const from_number, to_number, message: String;var Output: TStrings);
  32.   end;
  33.  
  34.   ETWilioException = Class(Exception);
  35.  
  36. implementation
  37.  
  38. constructor TTwilio.create(const sid, token: String );
  39. begin
  40.   Inherited create;
  41.   account_sid := sid;
  42.   auth_token := token;
  43.   url := 'https://api.twilio.com/2010-04-01/Accounts/' + sid + '/Messages.json';
  44. end;
  45.  
  46. destructor TTwilio.Destroy;
  47. begin
  48.   inherited Destroy;
  49. end;
  50.  
  51. procedure TTwilio.send_sms(const from_number, to_number, message: String;
  52.   var Output: TStrings);
  53. var
  54.   postVars: TStringlist;
  55.   responseData: TStringStream;
  56.   pos: Integer = 0;
  57.   jObject : TJSONObject = nil;
  58. begin
  59.    // Check if the output variable has been initialized
  60.    if Assigned(Output) then
  61.    begin
  62.      Output.Clear;
  63.      postVars := TStringlist.Create;
  64.      responseData := TStringStream.Create('');
  65.      try
  66.        // Prep resultVar
  67.        Output.CommaText := 'sid=,date_created=,date_updated=,date_sent=,account_sid=,to=,from=,body=,status=,num_segments=,num_media=,direction=,api_version=,price=,price_unit=,error_code=,error_message=,uri=,raw=';
  68.  
  69.        // postVars holds the API data needed, to/from/body
  70.        postVars.Add('To=' + to_number);
  71.        postVars.Add('From=' + from_number);
  72.        postVars.Add('Body=' + message);
  73.  
  74.        // Make the api call
  75.        With TFPHttpClient.Create(Nil) do
  76.        try
  77.          UserName := account_sid;
  78.          Password := auth_token;
  79.          FormPost(url, postVars, responseData);
  80.        finally
  81.          Free;
  82.        end;
  83.  
  84.        jObject := GetJSON( responseData.DataString ) as TJSONObject;
  85.  
  86.        for pos := 0 to Output.Count-1 do
  87.          if jObject.Get(Output.Names[pos]) <> Null then
  88.            Output.Values[Output.Names[pos]] := jObject.Get(Output.Names[pos]);
  89.        Output.Values['raw'] := responseData.DataString;
  90.      finally
  91.        responseData.Free;
  92.        jObject.Free;
  93.        postVars.Free;
  94.      end
  95.    end else
  96.      raise ETWilioException.Create('No output assigned') at
  97.        get_caller_addr(get_frame),
  98.        get_caller_frame(get_frame);;
  99. end;
  100. end.

And fixed the example:
Code: Pascal  [Select][+][-]
  1. program testsms.pas;
  2. {$mode objfpc}
  3. uses
  4.   Sysutils, Classes, TwilioLib;
  5.  
  6. procedure SendSMS;
  7. var
  8.   twilio: TTwilio;
  9.   twilio_result: TStrings;
  10. begin
  11.   twilio_result := TStringList.Create;
  12.   try
  13.     twilio := TTwilio.create('your sid', 'your secret');
  14.     try
  15.       try
  16.         twilio.send_sms('+OutPhone', '+ToPhone', 'message', twilio_result);
  17.       except
  18.        on E:ETWilioException do
  19.          writeln(e.message)
  20.        else raise;
  21.       end;
  22.     finally
  23.       Twilio.Free;
  24.     end;
  25.   finally
  26.     twilio_result.free;
  27.   end;
  28. end;
  29.  
  30. begin
  31.   SendSMS;
  32. end.

And added interface based example:
Code: Pascal  [Select][+][-]
  1. program testsms2.pas;
  2. {$mode objfpc}
  3. uses
  4.   Sysutils, Classes, TwilioLib;
  5.  
  6. procedure SendSMS;
  7. var
  8.   twilio: ITwilio;
  9.   twilio_result: TStrings;
  10. begin
  11.   twilio_result := TStringList.Create;
  12.   try
  13.     twilio := TTwilio.create('<SID>', '<TOKEN>') as ITwilio;
  14.     try
  15.       twilio.send_sms('<OUTPHONE>', '<TOPHONE>', '<message>', twilio_result);
  16.     except
  17.       on E:ETwilioException do
  18.         writeln(e.message)
  19.       else
  20.         raise;
  21.     end;
  22.   finally
  23.     twilio_result.free;
  24.   end;
  25. end;
  26.  
  27. begin
  28.   SendSMS;
  29. end.


Works like a charm..
« Last Edit: June 25, 2018, 05:54:02 pm by Thaddy »
Specialize a type, not a var.

Trenatos

  • Hero Member
  • *****
  • Posts: 535
    • MarcusFernstrom.com
Re: TwilioLib - Send SMS from Freepascal
« Reply #6 on: June 25, 2018, 03:03:35 pm »
Whoah, thanks a bunch Thaddy, I appreciate it  :)

I'll get that code updated later today, I already changed to LGPL3.

And your comments on memory leaks made me finally sit down and get GDB working on High Sierra

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: TwilioLib - Send SMS from Freepascal
« Reply #7 on: June 25, 2018, 03:46:34 pm »
It doesn't leak anymore...
Anyway, glad to be of help. I am already using it for two-factor out-of-band authentication.
Good job!

I made a small change in the library above:
Code: Pascal  [Select][+][-]
  1. // use as instead of a hardcast, looks less ugly
  2. jObject := GetJSON( responseData.DataString ) as TJSONObject;
« Last Edit: June 25, 2018, 04:00:52 pm by Thaddy »
Specialize a type, not a var.

Trenatos

  • Hero Member
  • *****
  • Posts: 535
    • MarcusFernstrom.com
Re: TwilioLib - Send SMS from Freepascal
« Reply #8 on: June 25, 2018, 04:26:01 pm »
No more leaks, but it was a reminder to never assume :)

Starting with just sending SMS but plan to get it on par with Twilios libraries over time.

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: TwilioLib - Send SMS from Freepascal
« Reply #9 on: June 25, 2018, 04:43:30 pm »
I'd like to help now I have the account ;)
Specialize a type, not a var.

Trenatos

  • Hero Member
  • *****
  • Posts: 535
    • MarcusFernstrom.com
Re: TwilioLib - Send SMS from Freepascal
« Reply #10 on: June 25, 2018, 05:10:57 pm »
Go for it, open source is best when collaborative

Trenatos

  • Hero Member
  • *****
  • Posts: 535
    • MarcusFernstrom.com
Re: TwilioLib - Send SMS from Freepascal
« Reply #11 on: June 25, 2018, 05:30:05 pm »
I just merged your changes to the repo

sash

  • Sr. Member
  • ****
  • Posts: 366
Re: TwilioLib - Send SMS from Freepascal
« Reply #12 on: June 25, 2018, 06:02:33 pm »
A bit misleading title. A correct one should be something like "HTTP API wrapper for some 3rd party site to send SMS"
Lazarus 2.0.10 FPC 3.2.0 x86_64-linux-gtk2 @ Ubuntu 20.04 XFCE

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: TwilioLib - Send SMS from Freepascal
« Reply #13 on: June 25, 2018, 07:01:05 pm »
A bit misleading title. A correct one should be something like "HTTP API wrapper for some 3rd party site to send SMS"
No it is not misleading. Unless you mean running your own SIP etc. servers. This is a good solution on a widely used platform.
The intend is somewhat like the google API bindings that we already have.
« Last Edit: June 25, 2018, 07:04:11 pm by Thaddy »
Specialize a type, not a var.

sash

  • Sr. Member
  • ****
  • Posts: 366
Re: TwilioLib - Send SMS from Freepascal
« Reply #14 on: June 26, 2018, 12:28:13 am »
No it is not misleading. Unless you mean running your own SIP etc. servers. This is a good solution on a widely used platform.

I didn't say it is wrong solution. I mean that description doesn't match, because I thought it is about serial GSM device interface, something like gnokii.
Lazarus 2.0.10 FPC 3.2.0 x86_64-linux-gtk2 @ Ubuntu 20.04 XFCE

 

TinyPortal © 2005-2018