Post a message to Teams from Delphi using WebHooks

Want to send a message to a Teams channel from your Delphi Application?
It is easy – simply post the message to your Teams channel’s WebHook URL.

Update – added Uwe Raabe’s TRESTClient version.

Well, perhaps not these hooks.
Photo by cottonbro on Pexels.com

Here is the low down basics of how to do it from Delphi. You simply do a http post with a preformatted Json string.

unit O365WebHook;

// Lars Fosdal, 2020 OCT 16
// Simple example without error handling

interface
uses
  System.Classes, System.SysUtils, System.Json, Rest.Json,
  IDGlobal, IdHTTP, IdIOHandler, IdSSL, IdSSLOpenSSL;

type
  TWebHookMessage = class
  end;

/// <summary> See https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using
/// for examples of how to structure the json for creating advanced formats</summary>
  TSimpleText = class(TWebHookMessage)
  private
    FText: String;
  public
    property Text: String read FText write FText;
    constructor Create(const aText: string);
  end;

type
  TWebHook = class
  private
    FHTTP: TIdHTTP;
    FURL: string;
  protected
    property HTTP: TIdHTTP read FHTTP;
  public
    constructor Create(const aURL: string = '');
    destructor Destroy; override;

    procedure Post(const aJson: string);
    procedure PostMessage(const aMsg: TWebhookMessage);

    property URL:String read FURL write FURL;
  end;


implementation

{ TWebHook }

constructor TWebHook.Create(const aURL: string);
begin
  FURL := aURL;
  // Remember that you need libeay32.dll and ssleay32.dll in the path for https to work
  FHTTP := TIdHTTP.Create(nil);
  HTTP.Request.ContentType := 'application/json';
  HTTP.Request.ContentEncoding := 'utf-8';
  HTTP.Request.CacheControl := 'no-cache';
  HTTP.HTTPOptions := HTTP.HTTPOptions - [hoForceEncodeParams] + [hoNoProtocolErrorException, hoWantProtocolErrorContent];
end;

destructor TWebHook.Destroy;
begin
  FHTTP.Free;
  inherited;
end;

procedure TWebHook.Post(const aJson: string);
var
  ReqString: TStringList;
  ResStream: TStringStream;
begin
  ReqString := TStringList.Create;
  try
    try
      ReqString.Text := aJson;
      ResStream := TStringStream.Create;
      try
//        DebugOut('HTTP Post ' + ReqString.Text);
        HTTP.Post(URL, ReqString, ResStream, IndyTextEncoding_UTF8);
//        DebugOut('Response: ' + ResStream.DataString);
      finally
        ResStream.Free;
      end;
    except // HTTP Exception
      on E: Exception
      do begin
//        DebugOutException(E, 'HTTP Post');
      end;
    end;
  finally
    ReqString.Free;
  end;
end;

procedure TWebHook.PostMessage(const aMsg: TWebhookMessage);
begin
  try
    Post(
      TJson.ObjectToJsonString(
        aMsg,
        [joIgnoreEmptyStrings, joIgnoreEmptyArrays, joDateIsUTC, joDateFormatISO8601]
      )
    );
  finally
    aMsg.Free;
  end;
end;

{ TSimpleText }

constructor TSimpleText.Create(const aText: string);
begin
  Text := aText;
end;

end.

How to use it. This is a very minimal example for a single text line.

procedure TestExampleToATeamsChannel;
var
  Teams: TWebhook;
begin
  Teams := TWebhook.Create('https:// ... YourLongWebHookURLHere ... ');
  try
    Teams.PostMessage(TSimpleText.Create('Hello from Delphi! :"{/=''æ}[øå] ÆØÅ'));
  finally
    Teams.Free;
  end;
end;

Make sure to read up on throttling and other rules of engagement before you start spamming your Teams channels. See the MS article on WebHooks for more detail.

Update! Uwe Raabe did a refurbished version that eliminates Indy and the need for the the OpenSLL DLLs, and makes the code work cross platform!

unit O365WebHook;

// Lars Fosdal, 2020 OCT 16
// Simple example without error handling
// Uwe Raabe replaced Indy for TRESTClient, eliminating the need for the OpenSLL DLLS

interface
uses
  System.Classes, System.SysUtils,
  REST.Json, REST.Client, REST.Types;

type
  TWebHookMessage = class
  end;

/// <summary> See https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using
/// for examples of how to structure the json for creating advanced formats</summary>
  TSimpleText = class(TWebHookMessage)
  private
    FText: String;
  public
    property Text: String read FText write FText;
    constructor Create(const aText: string);
  end;

type
  TWebHook = class
  private
    FClient: TRESTClient;
    FRequest: TCustomRESTRequest;
    FURL: string;
  protected
    property Client: TRESTClient read FClient;
    property Request: TCustomRESTRequest read FRequest;
  public
    constructor Create(const aURL: string = '');
    destructor Destroy; override;
    function PostMessage(const aMsg: TWebhookMessage; aOwnsMsg: Boolean = False): Boolean;
    property URL: string read FURL write FURL;
  end;

implementation

{ TWebHook }

constructor TWebHook.Create(const aURL: string);
begin
  inherited Create;
  FURL := aURL;

  FClient := TRESTClient.Create(nil);
  FRequest := TCustomRESTRequest.Create(nil);
  FRequest.Client := FClient;
end;

destructor TWebHook.Destroy;
begin
  FRequest.Free;
  FClient.Free;
  inherited;
end;

function TWebHook.PostMessage(const aMsg: TWebhookMessage; aOwnsMsg: Boolean = False): Boolean;
begin
  try
    Request.Client.BaseURL := URL;
    Request.Method := rmPOST;
    Request.AddBody(aMsg);
    Request.Execute;
    Result := Request.Response.Status.Success;
  finally
    if aOwnsMsg then
      aMsg.Free;
  end;
end;

{ TSimpleText }

constructor TSimpleText.Create(const aText: string);
begin
  inherited Create;
  FText := aText;
end;

end.

There is a known issue with a workaround for Delphi 10.2.

Have fun!

Migrating your Editor Colors to Delphi 10.4 Sydney

RAD Studio 10.4 Sydney introduces Custom themes for editor colors. Here is how to migrate your 10.3 settings to 10.4.

First, we make a custom color theme in 10.4 – go to Options | User Interface | Editor | Color.
With the current colors, just click on [Save As] and and give your personal scheme a name. 
I called mine Lars.

This creates a Registry branch, named HKEY_CURRENT_USER\Software\Embarcadero\BDS\21.0\Editor\Highlight\Custom themes\Lars

In RegEdit, go to HKEY_CURRENT_USER\Software\Embarcadero\BDS\20.0\Editor\Highlight (i.e. the Rio branch)

Export to a file, f.x. MyColors.reg

In MyColors.reg, you’ll see

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\20.0\Editor\Highlight]
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\20.0\Editor\Highlight\Additional search match highlight]
... and so on

Now, Open MyColors.reg in Notepad, search for “20.0\Editor\Highlight\” and replace it with “21.0\Editor\Highlight\Custom themes\Lars”.

Add the two branch paths for good measure.

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\21.0\Editor\Highlight]
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\21.0\Editor\Highlight\Custom themes]
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\21.0\Editor\Highlight\Custom themes\Lars]
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\21.0\Editor\Highlight\Custom themes\Lars\Additional search match highlight]
... and so on

Import the file into the registry, Restart the 10.4, go to Options | User Interface | Editor | Color and pick the custom theme “Lars”.

Embarcadero Launches LearnDelphi.org, a Delphi-Centric Learning Ecosystem, to Promote Delphi Education

LearnDelphi.org provides a platform for Delphi developers and educators, along with free licensing options and ways to get involved. Embarcadero (a division of Idera, Inc.), a provider of cross-platform app development productivity tools, today announced the launch of LearnDelphi.org, a learning ecosystem for Delphi developers, a teaching platform for Delphi educators, and a key destination for all things related to the Delphi Community.LearnDelphi.org is a collection of resources and free content for teaching and learning software development curated by and for Delphi developers. The site includes Delphi licensing options and resources such as ebooks, slideshows, and video tutorials, as well as ways to get involved. The content is compiled and supported by Embarcadero’s MVPs, community members, subject matter experts, and employees, and will be available in multiple languages over time. Potential contributors, educational liaisons, and educators are always welcome and encouraged to get involved.

Source: Embarcadero Launches LearnDelphi.org, a Delphi-Centric Learning Ecosystem, to Promote Delphi Education

RAD Studio 10.3 Rio – Release 3 is out

10.3.3 is the last expected release before 10.4, planned for some point in first half of 2020, and bring us

  • 64-bit Android
  • iOS 13
  • MacOS Catalina (Delphi)
  • Enterprise Connectors (Enterprise and Architect SKUs)
  • Numerous fixes to VCL, RTL and IDE
  • 180+ user reported issues

What’s new: 10.3 Rio – Release 3 – RAD Studio
ISO: http://cc.embarcadero.com/item/30896
Web Installer: http://cc.embarcadero.com/item/30893

Dalija Prasnikar have some useful observations on her blog.

Demystifying pointers in Delphi

Having a general understanding of how memory and addressing works, helps you understand pointers in Delphi (and C/C++). Learning assembler also gives you that knowledge, but there are simpler ways to think about it.

A pointer is a memory location that contains the address to the actual data you want.

Think of a street with houses. Make a list of the houses and their addresses.
This is a list of pointers. Each pointer in the list leads to the actual house it refers to.

Continue reading “Demystifying pointers in Delphi”

Generic Command Line Parser for Delphi 10.3.x

You know the feeling. You need a specific input from your command line, but you can’t find something lightweight that does the job. Hence, the FDC.CommandLine was born.

You can use – / or + as a switch, and use : or = as a flag for parameters.
/option:parameter

You can use single and double quotes to be able to parse parameters that contain spaces and quotes.
/quoteoftheday:”This is a ‘quoted’ parameter”

It also supports lists of parameters.
/inputfiles:(file1.txt , “file with space.txt”)

Take a look at the TestFDCCommandLine.dpr for more examples of how it works.

Continue reading “Generic Command Line Parser for Delphi 10.3.x”

Delphi pitfalls: Enumerated types and for loops

Not all for loops are created equal.

Consider
for x in [value1, value2, value3]

You would expect to see x vary in the order of the values in the list.

However – if x and the values are of an enumerated type, looping the “list”
does NOT loop in the apparent order of the constant, but in the order of the enumerated type declaration, such as it would for any set.

Continue reading “Delphi pitfalls: Enumerated types and for loops”