PawLab.DingTalkBot
1.0.1
dotnet add package PawLab.DingTalkBot --version 1.0.1
NuGet\Install-Package PawLab.DingTalkBot -Version 1.0.1
<PackageReference Include="PawLab.DingTalkBot" Version="1.0.1" />
<PackageVersion Include="PawLab.DingTalkBot" Version="1.0.1" />
<PackageReference Include="PawLab.DingTalkBot" />
paket add PawLab.DingTalkBot --version 1.0.1
#r "nuget: PawLab.DingTalkBot, 1.0.1"
#:package PawLab.DingTalkBot@1.0.1
#addin nuget:?package=PawLab.DingTalkBot&version=1.0.1
#tool nuget:?package=PawLab.DingTalkBot&version=1.0.1
DingTalkBot
A small .NET class library for sending text messages to a DingTalk custom robot group via the signed webhook API.
The library targets both .NET Framework and modern .NET, and is usable from VB.NET and C#. It performs the HMAC-SHA256 request signing internally, posts the message, and returns a simple result structure. It does not write any logs and does not swallow errors: network failures and non-success HTTP responses are surfaced as exceptions so the caller decides how to handle them.
Table of Contents
Features
- Signed webhook requests (timestamp + HMAC-SHA256 + Base64 + URL encoding)
- Text messages with optional
@all,@userIds, and@mobiles - No logging output; the library is silent by design
- Single shared
HttpClientinstance to avoid socket exhaustion - Multi-targeted:
net472,net6.0,net8.0 - No third-party dependencies beyond
System.Text.Jsonon .NET Framework
Requirements
| Target framework | System.Net.Http | System.Text.Json |
|---|---|---|
| .NET Framework 4.7.2 | Included in the framework | Installed via NuGet |
| .NET 6 / .NET 8 | Built into the runtime | Built into the runtime |
A DingTalk custom robot requires:
- An
access_tokenfrom the robot webhook URL - A
secretfrom the robot security settings (signature mode must be enabled)
Installation
NuGet
The library is published as PawLab.DingTalkBot.
Install via the .NET CLI:
dotnet add package PawLab.DingTalkBot
Install via the Package Manager Console in Visual Studio:
Install-Package PawLab.DingTalkBot
Build from source
git clone https://example.com/your-repo/DingTalkBot.git
cd DingTalkBot
dotnet build -c Release
The output DLLs are placed under:
bin/Release/net472/DingTalkBot.dll
bin/Release/net6.0/DingTalkBot.dll
bin/Release/net8.0/DingTalkBot.dll
Reference from another project
<ItemGroup>
<ProjectReference Include="..\DingTalkBot\DingTalkBot.vbproj" />
</ItemGroup>
Or reference the compiled DLL directly:
<ItemGroup>
<Reference Include="DingTalkBot">
<HintPath>lib\DingTalkBot.dll</HintPath>
</Reference>
</ItemGroup>
If you target .NET Framework, also add:
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>
Or
Quick Start
VB.NET
Imports DingTalkBot
Module Program
Sub Main()
Dim bot As New DingTalkBot("your_access_token", "your_secret")
' 1. Plain message, no mentions
Dim r1 As DingTalkBot.ResponseResult = bot.Send("Hello from VB.NET")
' 2. Mention everyone
Dim r2 As DingTalkBot.ResponseResult = bot.Send("Heads up, everyone", isAtAll:=True)
' 3. Mention specific users and mobile numbers
Dim atUserIds As String() = {"userid_1", "userid_2"}
Dim atMobiles As String() = {"13800000000"}
Dim r3 As DingTalkBot.ResponseResult = bot.Send("Meeting starts now", atUserIds, atMobiles)
If r1.errCode <> 0 Then
Console.WriteLine($"Send failed: {r1.errCode} - {r1.errMsg}")
End If
End Sub
End Module
C#
using DingTalkBot;
class Program
{
static void Main()
{
var bot = new DingTalkBot("your_access_token", "your_secret");
// 1. Plain message, no mentions
DingTalkBot.ResponseResult r1 = bot.Send("Hello from C#");
// 2. Mention everyone
DingTalkBot.ResponseResult r2 = bot.Send("Heads up, everyone", isAtAll: true);
// 3. Mention specific users and mobile numbers
var atUserIds = new[] { "userid_1", "userid_2" };
var atMobiles = new[] { "13800000000" };
DingTalkBot.ResponseResult r3 = bot.Send("Meeting starts now", atUserIds, atMobiles);
if (r1.errCode != 0)
{
Console.WriteLine($"Send failed: {r1.errCode} - {r1.errMsg}");
}
}
}
Note: because the class is nested,
ResponseResultis addressed asDingTalkBot.ResponseResultfrom C#. Inside the class itself you can use the short name.
API Reference
Constructor
Public Sub New(accessToken As String, secret As String)
public DingTalkBot(string accessToken, string secret)
| Parameter | Description |
|---|---|
accessToken |
The robot webhook access token. Must not be null or empty. |
secret |
The signature secret from the robot security settings. Must not be null or empty. |
Throws ArgumentNullException if either argument is null or an empty string.
The access token and secret are captured once at construction time. The signed URL is regenerated on every call, so long-lived instances are safe.
Send overloads
Public Function Send(msg As String, Optional isAtAll As Boolean = False) As ResponseResult
Public Function Send(msg As String, Optional atUserID As String() = Nothing,
Optional atUserMobile As String() = Nothing) As ResponseResult
public ResponseResult Send(string msg, bool isAtAll = false)
public ResponseResult Send(string msg, string[] atUserID = null, string[] atUserMobile = null)
| Parameter | Description |
|---|---|
msg |
The plain text message body. |
isAtAll |
When true, the message mentions every member in the group. |
atUserID |
An array of DingTalk user IDs to mention. May be null. |
atUserMobile |
An array of mobile numbers to mention. May be null. |
Both overloads produce the same payload shape. Empty arrays are sent as
[], matching the behavior of the official Python sample.
Return value: a ResponseResult populated from the DingTalk response body.
ResponseResult
Public Structure ResponseResult
Public errCode As Integer
Public errMsg As String
End Structure
| Field | Description |
|---|---|
errCode |
0 means success. Any other value is a DingTalk-side error. |
errMsg |
Human readable status, for example ok. |
DingTalk returns a body similar to:
{"errcode":0,"errmsg":"ok"}
The fields are public and immutable in practice; treat the structure as a read-only value object.
Exceptions
The library does not catch exceptions internally. Expect the following.
| Exception | Raised when |
|---|---|
ArgumentNullException |
accessToken or secret is null or empty. |
HttpRequestException |
DNS failure, connection reset, timeout, or a non-2xx HTTP status code. |
JsonException |
The response body is not valid JSON, or the expected fields are missing or of the wrong type. |
TaskCanceledException |
The request is cancelled or times out at the HttpClient level. |
Example of defensive calling code:
Try
Dim result = bot.Send("Hello")
If result.errCode <> 0 Then
Console.WriteLine($"DingTalk rejected the message: {result.errMsg}")
End If
Catch ex As HttpRequestException
Console.WriteLine($"Network problem: {ex.Message}")
Catch ex As JsonException
Console.WriteLine($"Unexpected response body: {ex.Message}")
End Try
try
{
var result = bot.Send("Hello");
if (result.errCode != 0)
{
Console.WriteLine($"DingTalk rejected the message: {result.errMsg}");
}
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Network problem: {ex.Message}");
}
catch (JsonException ex)
{
Console.WriteLine($"Unexpected response body: {ex.Message}");
}
Notes and Caveats
isAtAllis serialized as a JSON boolean. Some older samples send the string"true"instead; the boolean form is what the current DingTalk documentation specifies.The two
Sendoverloads can be ambiguous when called with a single argument. When calling from PowerShell or from a dynamic language, pass the second argument explicitly so the correct overload is selected:$bot.Send($msg, [bool]$false)From VB.NET and C# the named-argument form is clearer:
bot.Send(msg, isAtAll:=False)bot.Send(msg, isAtAll: false);atUserIdssupport depends on the robot type. For custom webhook robots theatMobilespath is the more reliable way to mention people.The signing logic lives in
DingTalkBot.Utilities.GenerateSignatureUrl. The returned URL already containsaccess_token,timestamp, andsignas query parameters and is ready to use as-is.A single static
HttpClientis shared across all instances. If you need per-call timeouts or custom handlers, useIHttpClientFactoryand inject the client instead.The library is intentionally synchronous. If you need async, wrap the call in
Task.Runor open an issue to request anSendAsyncoverload.
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net6.0-windows7.0 is compatible. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net8.0-windows7.0 is compatible. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Framework | net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 is compatible. net48 was computed. net481 was computed. |
-
.NETFramework 4.6.2
- System.Net.Http (>= 4.3.4)
- System.Text.Json (>= 10.0.12)
-
.NETFramework 4.7.2
- System.Net.Http (>= 4.3.4)
- System.Text.Json (>= 10.0.12)
-
net6.0
- No dependencies.
-
net6.0-windows7.0
- No dependencies.
-
net8.0
- No dependencies.
-
net8.0-windows7.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.