Skip to content

Commit 14075ec

Browse files
committed
Enhance Telegram bot with new features and improvements
- Updated `HandleStartAsync` in `README.md` to include new command handlers and response types. - Renamed `adminId` to `chiefUserId` in `CafeController.cs` and modified message handling. - Added `HandleReceipt` method to generate and return a receipt as text and image. - Updated `HandleByeAsync` to remove reply keyboard in `QueryController.cs`. - Registered new `/counter` command in `Program.cs` for both English and Russian. - Upgraded target framework to `net9.0` and updated package references in `TelegramBot.ConsoleTest.csproj`. - Refactored `ExecuteResultAsync` methods in `InlineResult.cs` and `MarkdownResult.cs` for performance. - Added `RemoveReplyKeyboard` property in `TextResult.cs` for better message handling. - Set console output encoding to UTF-8 in `BotBuilder.cs`. - Enhanced `BotControllerBase.cs` with new methods for editing messages and combining action results. - Introduced `CounterController.cs` for managing a simple counter with logging. - Added new result classes: `DeleteMessageResult`, `ImageResult`, `InlineEditResult`, `MultiActionResult`, and `TextEditResult`. - Improved `ImageResult` to support stream disposal after sending.
1 parent fa38f6d commit 14075ec

File tree

17 files changed

+479
-43
lines changed

17 files changed

+479
-43
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,15 @@ public async Task<IActionResult> HandleStartAsync()
123123
- [x] Text
124124
- [x] Inline
125125
- [x] Markdown
126-
- [ ] Image
126+
- [x] Image
127127
- [ ] Video
128-
- [ ] Delete
128+
- [x] Delete
129129
- [ ] Redirect
130130
- [ ] Implement language dictionary service
131131
- [x] Implement router for inline query
132132
- [ ] Inject user model in base controller
133-
- [ ] Add user state service
133+
- [x] Add user state service
134+
- [ ] Add builder for message arguments
134135

135136
See the [open issues](https://github.com/BigMakCode/TelegramBot.NET/issues) for a full list of proposed features (and known issues).
136137

Sources/TelegramBot.ConsoleTest/Controllers/CafeController.cs

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
using Telegram.Bot;
1+
using System.Text;
2+
using Telegram.Bot;
3+
using SixLabors.ImageSharp;
24
using TelegramBot.Builders;
35
using TelegramBot.Attributes;
46
using TelegramBot.Controllers;
57
using TelegramBot.Abstractions;
6-
using System.Text;
8+
using SixLabors.ImageSharp.PixelFormats;
79

810
namespace TelegramBot.ConsoleTest.Controllers
911
{
1012
public class CafeController(ITelegramBotClient telegramBotClient) : BotControllerBase
1113
{
12-
const long adminId = 123;
14+
const long chiefUserId = 123; // Replace with the actual user ID of the chief in restaurant
1315

1416
[TextCommand("/burgers")]
1517
public IActionResult HandleBurgers()
@@ -21,6 +23,7 @@ public IActionResult HandleBurgers()
2123
.AddButton("🍔🍔", "/burgers/2/drink/false")
2224
.AddButton("🍔🥤", "/burgers/1/drink/true")
2325
.AddButton("🍔🍔🥤", "/burgers/2/drink/true");
26+
2427
return Inline("Choose your order:", builder.Build());
2528
}
2629

@@ -48,10 +51,12 @@ public async Task<IActionResult> HandleBurgersAsync(int burgers, bool drink)
4851
$"Total: ${totalPrice}.00\n" +
4952
"Send /burgersdone when you are ready to pick up";
5053

51-
await telegramBotClient.SendMessage(adminId, orderText);
54+
await telegramBotClient.SendMessage(chiefUserId, orderText);
5255
SetValue("customerId", User.Id);
53-
Delete();
54-
return Text(text);
56+
return MultiAction(
57+
Text(text),
58+
DeleteMessage()
59+
);
5560
}
5661

5762
[TextCommand("/burgersdone")]
@@ -73,6 +78,16 @@ public async Task<IActionResult> HandleBurgersDoneAsync()
7378

7479
[TextCommand("/receipt")]
7580
public IActionResult HandleReceipt()
81+
{
82+
var textFileStream = WriteFile();
83+
var imageStream = DrawImage();
84+
85+
return MultiAction(
86+
File(textFileStream, "receipt.txt"),
87+
Image(imageStream, "Receipt Image"));
88+
}
89+
90+
private static MemoryStream WriteFile()
7691
{
7792
StringBuilder stringBuilder = new();
7893
stringBuilder.AppendLine("Date: 2021-09-01");
@@ -82,7 +97,27 @@ public IActionResult HandleReceipt()
8297
stringBuilder.AppendLine("Payment method: Cash");
8398
stringBuilder.AppendLine("Thank you for your order!");
8499
MemoryStream fileStream = new(Encoding.UTF8.GetBytes(stringBuilder.ToString()));
85-
return File(fileStream, "receipt.txt");
100+
return fileStream;
101+
}
102+
103+
private static MemoryStream DrawImage()
104+
{
105+
const byte size = byte.MaxValue;
106+
using Image<Rgba32> image = new(size, size, Color.Gray);
107+
for (int x = 0; x < image.Width; x++)
108+
{
109+
for (int y = 0; y < image.Height; y++)
110+
{
111+
byte r = (byte)(x % size);
112+
byte g = (byte)(y % size);
113+
byte b = (byte)((x + y) / 2 % size);
114+
image[x, y] = new Rgba32(r, g, b);
115+
}
116+
}
117+
MemoryStream imageStream = new();
118+
image.SaveAsJpegAsync(imageStream);
119+
imageStream.Position = 0;
120+
return imageStream;
86121
}
87122
}
88123
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using Telegram.Bot.Types;
2+
using TelegramBot.Builders;
3+
using TelegramBot.Attributes;
4+
using TelegramBot.Controllers;
5+
using TelegramBot.Abstractions;
6+
using Microsoft.Extensions.Logging;
7+
using Telegram.Bot.Types.ReplyMarkups;
8+
9+
namespace TelegramBot.ConsoleTest.Controllers
10+
{
11+
public class CounterController(ILogger<CounterController> _logger) : BotControllerBase
12+
{
13+
private const string CounterKeyPrefix = "Counter:";
14+
private const string IncrementCommand = "/counter/increment";
15+
private const string DecrementCommand = "/counter/decrement";
16+
17+
[TextCommand("/counter")]
18+
public IActionResult GetCounter()
19+
{
20+
int value = GetCounterValue();
21+
_logger.LogInformation("Counter value requested: {Value}", value);
22+
return CreateCounterResponse(value, edit: false);
23+
}
24+
25+
[InlineCommand("/counter/increment")]
26+
public IActionResult IncrementCounter()
27+
{
28+
int value = GetCounterValue();
29+
value++;
30+
SetCounterValue(value);
31+
_logger.LogInformation("Counter incremented: {Value}", value);
32+
return CreateCounterResponse(value);
33+
}
34+
35+
[InlineCommand("/counter/decrement")]
36+
public IActionResult DecrementCounter()
37+
{
38+
int value = GetCounterValue();
39+
value--;
40+
SetCounterValue(value);
41+
_logger.LogInformation("Counter decremented: {Value}", value);
42+
return CreateCounterResponse(value);
43+
}
44+
45+
private string GetCounterKey() => $"{CounterKeyPrefix}{User.Id}";
46+
47+
private int GetCounterValue() => GetValue<int>(GetCounterKey());
48+
49+
private void SetCounterValue(int value) => SetValue(GetCounterKey(), value);
50+
51+
private static InlineKeyboardMarkup CreateCounterKeyboard()
52+
{
53+
return new KeyboardBuilder()
54+
.WithColumns(2)
55+
.AddButton("➖", DecrementCommand)
56+
.AddButton("➕", IncrementCommand)
57+
.Build();
58+
}
59+
60+
private IActionResult CreateCounterResponse(int value, bool edit = true)
61+
{
62+
string message = $"{CounterKeyPrefix} {value}";
63+
return edit ? TextEdit(message, CreateCounterKeyboard()) : Inline(message, CreateCounterKeyboard());
64+
}
65+
}
66+
}

Sources/TelegramBot.ConsoleTest/Controllers/QueryController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public IActionResult HandleHelloAsync()
1515
[TextQuery(pattern: "bye")]
1616
public IActionResult HandleByeAsync()
1717
{
18-
return Text("Goodbye!");
18+
return Text("Goodbye!", removeReplyKeyboard: true);
1919
}
2020
}
2121
}

Sources/TelegramBot.ConsoleTest/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public static void Main(string[] args)
1818
.RegisterCommands(x =>
1919
{
2020
x.RegisterCommand("/start", "initiates the bot")
21+
.RegisterCommand("/counter", "simple counter command")
2122
.RegisterCommand("/help", "shows help message")
2223
.RegisterCommand("/burgers", "shows burgers menu")
2324
.RegisterCommand("/burgersdone", "notifies that the order is ready")
@@ -26,6 +27,7 @@ public static void Main(string[] args)
2627
.RegisterCommands(x =>
2728
{
2829
x.RegisterCommand("/start", "инициирует бота")
30+
.RegisterCommand("/counter", "простой счетчик")
2931
.RegisterCommand("/help", "показывает сообщение справки")
3032
.RegisterCommand("/burgers", "показывает меню бургеров")
3133
.RegisterCommand("/burgersdone", "уведомляет о готовности заказа")

Sources/TelegramBot.ConsoleTest/TelegramBot.ConsoleTest.csproj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net8.0</TargetFramework>
5+
<TargetFramework>net9.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
</PropertyGroup>
@@ -11,11 +11,12 @@
1111
<Content Include="appsettings.json">
1212
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
1313
</Content>
14-
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0" />
15-
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0">
14+
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.6" />
15+
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.6">
1616
<PrivateAssets>all</PrivateAssets>
1717
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1818
</PackageReference>
19+
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
1920
<ProjectReference Include="..\TelegramBot\TelegramBot.csproj" />
2021
</ItemGroup>
2122

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using Telegram.Bot;
2+
using System.Threading.Tasks;
3+
using TelegramBot.Abstractions;
4+
5+
namespace TelegramBot.ActionResults
6+
{
7+
/// <summary>
8+
/// Represents a result for deleting a message in a Telegram bot.
9+
/// </summary>
10+
public class DeleteMessageResult : IActionResult
11+
{
12+
private readonly int _messageId;
13+
14+
/// <summary>
15+
/// Initializes a new instance of the <see cref="DeleteMessageResult"/> class.
16+
/// </summary>
17+
/// <param name="messageId">The identifier of the message to delete.</param>
18+
public DeleteMessageResult(int messageId)
19+
{
20+
_messageId = messageId;
21+
}
22+
23+
/// <summary>
24+
/// Executes the result asynchronously, deleting the specified message.
25+
/// </summary>
26+
/// <param name="context">The action context containing the bot and chat information.</param>
27+
/// <returns>A task representing the asynchronous operation.</returns>
28+
public Task ExecuteResultAsync(ActionContext context)
29+
{
30+
return context.Bot.DeleteMessage(context.ChatId, _messageId);
31+
}
32+
}
33+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System;
2+
using System.IO;
3+
using Telegram.Bot;
4+
using Telegram.Bot.Types;
5+
using System.Threading.Tasks;
6+
using TelegramBot.Abstractions;
7+
8+
namespace TelegramBot.ActionResults
9+
{
10+
/// <summary>
11+
/// Image result with disposing.
12+
/// </summary>
13+
public class ImageResult : IActionResult, IDisposable
14+
{
15+
private bool _disposed;
16+
private readonly string _fileName;
17+
private readonly Stream _imageStream;
18+
private readonly bool _disposeStream = true;
19+
private readonly string _caption = string.Empty;
20+
21+
/// <summary>
22+
/// Creates a new instance of <see cref="ImageResult"/>.
23+
/// </summary>
24+
/// <param name="filePath">Full path to the image.</param>
25+
/// <param name="caption">Caption for the image.</param>
26+
public ImageResult(string filePath, string caption = "")
27+
{
28+
_caption = caption;
29+
_fileName = Path.GetFileName(filePath);
30+
_imageStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
31+
}
32+
33+
/// <summary>
34+
/// Creates a new instance of <see cref="ImageResult"/>.
35+
/// </summary>
36+
/// <param name="stream">Stream with image content.</param>
37+
/// <param name="fileName">File name.</param>
38+
/// <param name="caption">Caption for the image.</param>
39+
/// <param name="disposeStream">Dispose the stream after sending the file.</param>
40+
public ImageResult(Stream stream, string fileName, string caption = "", bool disposeStream = true)
41+
{
42+
_caption = caption;
43+
_fileName = fileName;
44+
_imageStream = stream;
45+
_disposeStream = disposeStream;
46+
}
47+
48+
/// <summary>
49+
/// Disposes the file stream if it is applicable.
50+
/// </summary>
51+
public void Dispose()
52+
{
53+
if (!_disposed && _disposeStream)
54+
{
55+
_imageStream.Dispose();
56+
_disposed = true;
57+
}
58+
}
59+
60+
/// <summary>
61+
/// Executes the result asynchronously.
62+
/// </summary>
63+
/// <param name="context">Action context.</param>
64+
/// <returns>The task representing the result of the action.</returns>
65+
public Task ExecuteResultAsync(ActionContext context)
66+
{
67+
InputFile file = InputFile.FromStream(_imageStream, _fileName);
68+
return context.Bot.SendPhoto(context.ChatId, photo: file, caption: _caption);
69+
}
70+
}
71+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using Telegram.Bot;
2+
using System.Threading.Tasks;
3+
using TelegramBot.Abstractions;
4+
using Telegram.Bot.Types.ReplyMarkups;
5+
6+
namespace TelegramBot.ActionResults
7+
{
8+
/// <summary>
9+
/// Inline query result.
10+
/// </summary>
11+
public class InlineEditResult : IActionResult
12+
{
13+
/// <summary>
14+
/// Inline message (caption).
15+
/// </summary>
16+
public string Text { get; }
17+
18+
/// <summary>
19+
/// Message identifier to edit.
20+
/// </summary>
21+
public int MessageId { get; }
22+
23+
/// <summary>
24+
/// Inline keyboard.
25+
/// </summary>
26+
public InlineKeyboardMarkup Keyboard { get; }
27+
28+
/// <summary>
29+
/// Creates a new instance of <see cref="InlineResult"/>.
30+
/// </summary>
31+
/// <param name="text">Inline message (caption).</param>
32+
/// <param name="keyboard">Inline keyboard.</param>
33+
/// <param name="messageId">Message identifier to edit.</param>
34+
public InlineEditResult(string text, InlineKeyboardMarkup keyboard, int messageId)
35+
{
36+
Text = text;
37+
Keyboard = keyboard;
38+
MessageId = messageId;
39+
}
40+
41+
/// <summary>
42+
/// Executes the result asynchronously.
43+
/// </summary>
44+
/// <param name="context">Action context.</param>
45+
/// <returns>The task representing the result of the action.</returns>
46+
public Task ExecuteResultAsync(ActionContext context)
47+
{
48+
return context.Bot.EditMessageReplyMarkup(context.ChatId, messageId: MessageId, replyMarkup: Keyboard);
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)