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!

First Steps with Blazor – Simple Talk

It is always fun to play with new toys! This article from Julio Sampaio introduces you to Blazor – Web pages done in C#.

Blazor stands for Browser + Razor, which gives you an idea of what’s behind the new framework. Razor is the ASP.NET programming syntax that Microsoft uses to create its C# (or VB.NET) dynamic pages. Now, you can create web applications using only C# and run them in a web browser.

Source: First Steps with Blazor – Simple Talk

Frink – Numbers with units of measure

As unusual programming languages go, this one actually is pretty amazing.
It adds units of measure to the number.

“One day Alan Eliasen read a fart joke and got so mad he invented a programming language. 20 years later Frink is one of the best special purpose languages for dealing with units.“But why do we need a language just for dealing with units?” Glad you asked! Intro to Units A unit is the physical property a number represents, like distance or time. We almost always are talking about SI units, or Système international.”

by Hillel Wayne

Source: The Frink is Good, the Unit is Evil • Hillel Wayne

Quo Vadis, MSMQ and .NET Core?

“Microsoft Message Queuing (MSMQ) is currently not available for .NET Core. While other message queuing systems are generally preferred, many enterprise applications were based on MSMQ and this creates a problem for teams looking to migrate from .NET Framework to .NET Core or the upcoming .NET 5. But a recent pull request for Reference Source may change the situation.”

By Jonathan Allen

Source: MSMQ and .NET Core