Skip to content

Commit 59457a9

Browse files
authored
Merge pull request #8 from deploy-f/fix_photomsg_editing
Fix for photo message updating
2 parents e25bf78 + 0175aff commit 59457a9

File tree

12 files changed

+414
-14
lines changed

12 files changed

+414
-14
lines changed

Deployf.Botf/BotController.cs

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Linq.Expressions;
2+
using Deployf.Botf.System.UpdateMessageStrategies;
23
using Telegram.Bot;
34
using Telegram.Bot.Framework.Abstractions;
45
using Telegram.Bot.Types;
@@ -19,6 +20,7 @@ public abstract class BotController
1920
protected ITelegramBotClient Client { get; set; } = null!;
2021
protected MessageBuilder Message { get; set; } = new MessageBuilder();
2122
public IKeyValueStorage? Store { get; set; }
23+
public IUpdateMessageStrategyFactory UpdateMessageStrategyFactory { get; set; }
2224
public int? MessageId { get; set; }
2325

2426
protected bool IsDirty
@@ -35,6 +37,7 @@ public virtual void Init(IUpdateContext context, CancellationToken cancellationT
3537
FromId = Context.GetSafeUserId().GetValueOrDefault();
3638
Client = Context.Bot.Client;
3739
Store = Context.Services.GetService<IKeyValueStorage>(); // todo: move outside
40+
UpdateMessageStrategyFactory = Context.Services.GetRequiredService<IUpdateMessageStrategyFactory>();
3841
Message = new MessageBuilder();
3942
}
4043

@@ -180,14 +183,46 @@ public async Task<Message> Update(InlineKeyboardMarkup? markup = null, string? t
180183
{
181184
var markupValue = markup ?? Message.Markup as InlineKeyboardMarkup;
182185
IsDirty = false;
183-
var message = await Client.EditMessageTextAsync(
184-
ChatId == 0 ? Context!.GetSafeChatId()! : ChatId,
185-
MessageId ?? Context!.GetSafeMessageId().GetValueOrDefault(),
186-
text ?? Message.Message,
187-
parseMode: mode,
188-
replyMarkup: markupValue,
189-
cancellationToken: CancelToken
190-
);
186+
187+
var chatId = Context!.GetSafeChatId()!.Value;
188+
var messageId = MessageId ?? Context!.GetSafeMessageId().GetValueOrDefault();
189+
var messageText = text ?? Message.Message;
190+
var previousMessage = Context.Update.CallbackQuery!.Message;
191+
var nextMessagePhotoUrl = Message.PhotoUrl;
192+
193+
var ctx = new UpdateMessageContext(
194+
Context,
195+
chatId,
196+
messageId,
197+
messageText,
198+
previousMessage!,
199+
nextMessagePhotoUrl,
200+
markupValue,
201+
mode,
202+
Message.ReplyToMessageId,
203+
CancelToken);
204+
205+
Message message;
206+
var strategy = UpdateMessageStrategyFactory.GetStrategy(ctx);
207+
if (strategy == null)
208+
{
209+
var logger = Context.Services.GetRequiredService<ILogger<BotController>>();
210+
logger.LogDebug("Not found a suitable strategy, using default instead");
211+
212+
message = await Client.EditMessageTextAsync(
213+
ChatId == 0 ? Context!.GetSafeChatId()! : ChatId,
214+
MessageId ?? Context!.GetSafeMessageId().GetValueOrDefault(),
215+
text ?? Message.Message,
216+
parseMode: mode,
217+
replyMarkup: markupValue,
218+
cancellationToken: CancelToken
219+
);
220+
}
221+
else
222+
{
223+
message = await strategy.UpdateMessage(ctx);
224+
}
225+
191226
await TrySaveLastMessageId(markupValue, message);
192227
ClearMessage();
193228
return message;
@@ -384,6 +419,16 @@ public void Reply(int? messageId = default)
384419
}
385420
}
386421

422+
/// <summary>
423+
/// Sets photo for message
424+
/// </summary>
425+
/// <remarks>Attention! Telegram limit is 0-1024 characters for text messages with images</remarks>
426+
/// <param name="url">
427+
/// Photo url to send. Pass a FileId as String to send a photo that exists on
428+
/// the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a photo from
429+
/// the Internet. The photo must be at most 10 MB in size.
430+
/// The photo's width and height must not exceed 10000 in total. Width and height ratio must be at most 20
431+
/// </param>
387432
public void Photo(string url)
388433
{
389434
Message.SetPhotoUrl(url);

Deployf.Botf/StartupExtensions.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Telegram.Bot;
1+
using Deployf.Botf.System.UpdateMessageStrategies;
2+
using Telegram.Bot;
23
using Telegram.Bot.Framework;
34
using Telegram.Bot.Framework.Abstractions;
45
using Telegram.Bot.Requests;
@@ -133,6 +134,12 @@ public static IServiceCollection AddBotf(this IServiceCollection services, BotfO
133134
services.AddSingleton<IArgumentBind, ArgumentBindBridge>();
134135
services.AddSingleton<ArgumentBinder>();
135136

137+
services.AddSingleton<IUpdateMessageStrategy, MediaToPlainTextStrategy>();
138+
services.AddSingleton<IUpdateMessageStrategy, PlainTextToMediaStrategy>();
139+
services.AddSingleton<IUpdateMessageStrategy, MediaToMediaFileStrategy>();
140+
services.AddSingleton<IUpdateMessageStrategy, EditTextMessageStrategy>();
141+
services.AddSingleton<IUpdateMessageStrategyFactory, UpdateMessageStrategyFactory>();
142+
136143
return services;
137144
}
138145

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
using System.Runtime.Serialization;
2+
13
namespace Deployf.Botf;
24

3-
[System.Serializable]
4-
public class BotfException : System.Exception
5+
[Serializable]
6+
public class BotfException : Exception
57
{
68
public BotfException() { }
79
public BotfException(string message) : base(message) { }
8-
public BotfException(string message, System.Exception inner) : base(message, inner) { }
10+
public BotfException(string message, Exception inner) : base(message, inner) { }
911
protected BotfException(
10-
System.Runtime.Serialization.SerializationInfo info,
11-
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
12+
SerializationInfo info,
13+
StreamingContext context) : base(info, context) { }
1214
}

Deployf.Botf/System/UpdateContextExtensions.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace Deployf.Botf;
55

66
public static class UpdateContextExtensions
77
{
8+
private const string UPDATE_MESSAGE_POLICY_KEY = "$_UpdateMessagePolicy";
89
private const string STOP_HANDLING_KEY = "$_StopHandling";
910
private const string CURRENT_HANDLER_KEY = "$_CurrentHandler";
1011
private const string FILTER_PARAMETER_KEY = "$_FilterParameter";
@@ -200,4 +201,25 @@ public static void SetFilterParameter(this IUpdateContext context, object? param
200201

201202
return null;
202203
}
204+
205+
public static void SetUpdateMsgPolicy(this IUpdateContext context, UpdateMessagePolicy policy)
206+
{
207+
context.Items[UPDATE_MESSAGE_POLICY_KEY] = policy;
208+
}
209+
210+
public static UpdateMessagePolicy? GetCurrentUpdateMsgPolicy(this IUpdateContext context)
211+
{
212+
if(context.Items.TryGetValue(UPDATE_MESSAGE_POLICY_KEY, out var policy) && policy is UpdateMessagePolicy result)
213+
{
214+
return result;
215+
}
216+
217+
return null;
218+
}
219+
}
220+
221+
public enum UpdateMessagePolicy
222+
{
223+
UpdateContent = 0, // default
224+
DeleteAndSend = 1
203225
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using Telegram.Bot;
2+
using Telegram.Bot.Types;
3+
4+
namespace Deployf.Botf.System.UpdateMessageStrategies;
5+
6+
/// <summary>
7+
/// Situation: previous message has no media file and a new message does not have.
8+
/// </summary>
9+
public class EditTextMessageStrategy : IUpdateMessageStrategy
10+
{
11+
private readonly BotfBot _bot;
12+
13+
public EditTextMessageStrategy(BotfBot bot)
14+
{
15+
_bot = bot;
16+
}
17+
18+
public bool CanHandle(IUpdateMessageContext context)
19+
{
20+
var newMessageFileIsEmpty = string.IsNullOrEmpty(context.MediaFile?.FileId) &&
21+
string.IsNullOrEmpty(context.MediaFile?.Url);
22+
23+
return context.PreviousMessage.Photo == null && newMessageFileIsEmpty;
24+
}
25+
26+
public async Task<Message> UpdateMessage(IUpdateMessageContext context)
27+
{
28+
return await _bot.Client.EditMessageTextAsync(
29+
context.ChatId,
30+
context.MessageId,
31+
context.MessageText,
32+
parseMode: context.ParseMode,
33+
replyMarkup: context.KeyboardMarkup,
34+
cancellationToken: context.CancelToken
35+
);
36+
}
37+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Telegram.Bot.Types;
2+
3+
namespace Deployf.Botf.System.UpdateMessageStrategies;
4+
5+
public interface IUpdateMessageStrategy
6+
{
7+
public bool CanHandle(IUpdateMessageContext context);
8+
public Task<Message> UpdateMessage(IUpdateMessageContext context);
9+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace Deployf.Botf.System.UpdateMessageStrategies;
2+
3+
public interface IUpdateMessageStrategyFactory
4+
{
5+
IUpdateMessageStrategy? GetStrategy(IUpdateMessageContext context);
6+
}
7+
8+
public class UpdateMessageStrategyFactory : IUpdateMessageStrategyFactory
9+
{
10+
private readonly IEnumerable<IUpdateMessageStrategy> _strategies;
11+
12+
public UpdateMessageStrategyFactory(IEnumerable<IUpdateMessageStrategy> strategies)
13+
{
14+
_strategies = strategies;
15+
}
16+
17+
public IUpdateMessageStrategy? GetStrategy(IUpdateMessageContext context)
18+
{
19+
return _strategies.FirstOrDefault(s => s.CanHandle(context));
20+
}
21+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using Telegram.Bot;
2+
using Telegram.Bot.Types;
3+
4+
namespace Deployf.Botf.System.UpdateMessageStrategies;
5+
6+
/// <summary>
7+
/// Situation: previous message has a media file and a new message has one.
8+
/// </summary>
9+
public class MediaToMediaFileStrategy : IUpdateMessageStrategy
10+
{
11+
private readonly BotfBot _bot;
12+
13+
public MediaToMediaFileStrategy(BotfBot bot)
14+
{
15+
_bot = bot;
16+
}
17+
18+
public bool CanHandle(IUpdateMessageContext context)
19+
{
20+
var newMessageHasFile = !string.IsNullOrEmpty(context.MediaFile?.FileId) ||
21+
!string.IsNullOrEmpty(context.MediaFile?.Url);
22+
23+
return context.PreviousMessage.Photo != null && newMessageHasFile;
24+
}
25+
26+
public async Task<Message> UpdateMessage(IUpdateMessageContext context)
27+
{
28+
var updateMessagePolicy = context.UpdateContext.GetCurrentUpdateMsgPolicy();
29+
if (updateMessagePolicy is UpdateMessagePolicy.DeleteAndSend)
30+
{
31+
await _bot.Client.DeleteMessageAsync(context.ChatId, context.PreviousMessage.MessageId, context.CancelToken);
32+
return await _bot.Client.SendPhotoAsync(
33+
context.ChatId,
34+
context.MediaFile!,
35+
context.MessageText,
36+
context.ParseMode,
37+
replyMarkup: context.KeyboardMarkup,
38+
cancellationToken: context.CancelToken,
39+
replyToMessageId: context.ReplyToMessageId);
40+
}
41+
else
42+
{
43+
return await _bot.Client.EditMessageMediaAsync(
44+
context.ChatId,
45+
context.MessageId,
46+
new InputMediaPhoto(context.MediaFile!)
47+
{
48+
Caption = context.MessageText
49+
},
50+
replyMarkup: context.KeyboardMarkup,
51+
cancellationToken: context.CancelToken
52+
);
53+
}
54+
}
55+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using Telegram.Bot;
2+
using Telegram.Bot.Types;
3+
4+
namespace Deployf.Botf.System.UpdateMessageStrategies;
5+
6+
/// <summary>
7+
/// Situation: previous message has media file, but a new message does not have.
8+
/// </summary>
9+
public class MediaToPlainTextStrategy : IUpdateMessageStrategy
10+
{
11+
private readonly BotfBot _bot;
12+
13+
public MediaToPlainTextStrategy(BotfBot bot)
14+
{
15+
_bot = bot;
16+
}
17+
18+
public bool CanHandle(IUpdateMessageContext context)
19+
{
20+
var newMessageFileIsEmpty = string.IsNullOrEmpty(context.MediaFile?.FileId) &&
21+
string.IsNullOrEmpty(context.MediaFile?.Url);
22+
23+
return context.PreviousMessage.Photo != null && newMessageFileIsEmpty;
24+
}
25+
26+
public async Task<Message> UpdateMessage(IUpdateMessageContext context)
27+
{
28+
await _bot.Client.DeleteMessageAsync(context.ChatId, context.PreviousMessage.MessageId, context.CancelToken);
29+
return await _bot.Client.SendTextMessageAsync(
30+
context.ChatId,
31+
context.MessageText,
32+
context.ParseMode,
33+
replyMarkup: context.KeyboardMarkup,
34+
cancellationToken: context.CancelToken,
35+
replyToMessageId: context.ReplyToMessageId);
36+
}
37+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using Telegram.Bot;
2+
using Telegram.Bot.Types;
3+
4+
namespace Deployf.Botf.System.UpdateMessageStrategies;
5+
6+
/// <summary>
7+
/// Situation: previous message has no media file, but a new message has one.
8+
/// </summary>
9+
public class PlainTextToMediaStrategy : IUpdateMessageStrategy
10+
{
11+
private readonly BotfBot _bot;
12+
13+
public PlainTextToMediaStrategy(BotfBot bot)
14+
{
15+
_bot = bot;
16+
}
17+
18+
public bool CanHandle(IUpdateMessageContext context)
19+
{
20+
var newMessageHasFile = !string.IsNullOrEmpty(context.MediaFile?.FileId) ||
21+
!string.IsNullOrEmpty(context.MediaFile?.Url);
22+
23+
return context.PreviousMessage.Photo == null && newMessageHasFile;
24+
}
25+
26+
public async Task<Message> UpdateMessage(IUpdateMessageContext context)
27+
{
28+
await _bot.Client.DeleteMessageAsync(context.ChatId, context.PreviousMessage.MessageId, context.CancelToken);
29+
return await _bot.Client.SendPhotoAsync(
30+
context.ChatId,
31+
context.MediaFile!,
32+
context.MessageText,
33+
context.ParseMode,
34+
replyMarkup: context.KeyboardMarkup,
35+
cancellationToken: context.CancelToken,
36+
replyToMessageId: context.ReplyToMessageId);
37+
}
38+
}

0 commit comments

Comments
 (0)