Merge pull request #45 from MajMcCloud/development

Integrate development branch into master
This commit is contained in:
Florian Zevedei
2023-09-13 15:01:49 +02:00
committed by GitHub
37 changed files with 1038 additions and 71 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 658 KiB

@@ -6,7 +6,7 @@ namespace BotAndWebApplication.BotStuff
{
public class StartForm : FormBase
{
ButtonGrid _grid = null;
ButtonGrid? _grid = null;
int MyCounter { get; set; } = 0;
@@ -34,10 +34,10 @@ namespace BotAndWebApplication.BotStuff
AddControl(_grid);
}
private async Task _grid_ButtonClicked(object sender, TelegramBotBase.Args.ButtonClickedEventArgs e)
private Task _grid_ButtonClicked(object sender, TelegramBotBase.Args.ButtonClickedEventArgs e)
{
if (e.Button == null || e.Button.Value == null)
return;
return Task.CompletedTask;
switch (e.Button.Value)
{
@@ -60,6 +60,7 @@ namespace BotAndWebApplication.BotStuff
}
return Task.CompletedTask;
}
}
}
@@ -0,0 +1,88 @@
using InlineAndReplyCombination.Forms;
using InlineAndReplyCombination.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Attributes;
using TelegramBotBase.Controls.Hybrid;
using TelegramBotBase.Form;
namespace InlineAndReplyCombination.Baseclasses
{
public class MultipleChoiceForm : AutoCleanForm
{
[SaveState]
public UserDetails? UserDetails { get; set; }
ButtonGrid? ReplyButtonGrid;
public String ReplyButtonTitle { get; set; } = "Restart";
protected int CurrentStep = 1;
protected int MaxSteps = 3;
public MultipleChoiceForm()
{
this.Init += MultipleChoiceForm_Init;
}
private Task MultipleChoiceForm_Init(object sender, TelegramBotBase.Args.InitEventArgs e)
{
//Reply keyboard
var bf = new ButtonForm();
bf.AddButtonRow(ReplyButtonTitle, "replyButtonID");
ReplyButtonGrid = new ButtonGrid(bf);
ReplyButtonGrid.Title = $"Step {CurrentStep} / {MaxSteps}";
ReplyButtonGrid.KeyboardType = TelegramBotBase.Enums.EKeyboardType.ReplyKeyboard;
ReplyButtonGrid.ButtonClicked += ReplyButtonGrid_ButtonClicked;
AddControl(ReplyButtonGrid);
return Task.CompletedTask;
}
private async Task ReplyButtonGrid_ButtonClicked(object sender, TelegramBotBase.Args.ButtonClickedEventArgs e)
{
if (e.Button == null)
return;
switch (e.Button.Value)
{
case "replyButtonID":
await PressReplyButton();
break;
}
}
public virtual Task PressReplyButton()
{
return Task.CompletedTask;
}
public override Task NavigateTo(FormBase newForm, params object[] args)
{
//Move user details over to navigating form
if (newForm is MultipleChoiceForm mcf)
{
mcf.UserDetails = UserDetails;
}
return base.NavigateTo(newForm, args);
}
}
}
@@ -0,0 +1,68 @@
using InlineAndReplyCombination.Baseclasses;
using InlineAndReplyCombination.Forms.Steps;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Controls.Hybrid;
using TelegramBotBase.Form;
namespace InlineAndReplyCombination.Forms
{
public class StartForm : AutoCleanForm
{
ButtonGrid? buttonGrid;
public StartForm()
{
this.Init += StartForm_Init;
}
private Task StartForm_Init(object sender, TelegramBotBase.Args.InitEventArgs e)
{
var bf = new ButtonForm();
bf.AddButtonRow("Start registration", "start");
buttonGrid = new ButtonGrid(bf);
buttonGrid.Title = "Welcome to The InlineAndReplyCombination Bot!";
buttonGrid.ButtonClicked += ButtonGrid_ButtonClicked;
buttonGrid.KeyboardType = TelegramBotBase.Enums.EKeyboardType.ReplyKeyboard;
AddControl(buttonGrid);
return Task.CompletedTask;
}
private async Task ButtonGrid_ButtonClicked(object sender, TelegramBotBase.Args.ButtonClickedEventArgs e)
{
if(e.Button == null)
{
return;
}
switch(e.Button.Value)
{
case "start":
var mf = new MainForm();
mf.UserDetails = new Model.UserDetails();
await NavigateTo(mf);
break;
}
}
}
}
@@ -0,0 +1,96 @@
using InlineAndReplyCombination.Baseclasses;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Telegram.Bot.Types;
using TelegramBotBase.Base;
using TelegramBotBase.Controls.Hybrid;
using TelegramBotBase.Form;
namespace InlineAndReplyCombination.Forms.Steps
{
public class MainForm : MultipleChoiceForm
{
ButtonGrid? InlineButtonGrid;
public static List<Tuple<String, String>> AllowedInlineInputs;
static MainForm()
{
AllowedInlineInputs = new List<Tuple<string, string>>()
{
new("< 18", "<18"),
new("18 to 25", "18t25"),
new("25 to 35", "25t35"),
new("35 to 50", "35t50"),
new("over 50", "o50")
};
}
public MainForm()
{
Init += MainForm_Init;
ReplyButtonTitle = "Start over";
CurrentStep = 1;
}
private Task MainForm_Init(object sender, TelegramBotBase.Args.InitEventArgs e)
{
//Inline Keyboard
var bf_ages = new ButtonForm();
//Add all options in a single column
bf_ages.AddSplitted(AllowedInlineInputs.Select(a => new ButtonBase(a.Item1, a.Item2)), 1);
bf_ages.AddButtonRow("Some invalid input", "Invalid");
InlineButtonGrid = new ButtonGrid(bf_ages);
InlineButtonGrid.ConfirmationText = "Thank you";
InlineButtonGrid.Title = "Please choose your age:";
InlineButtonGrid.ButtonClicked += InlineButtonGrid_ButtonClicked;
InlineButtonGrid.KeyboardType = TelegramBotBase.Enums.EKeyboardType.InlineKeyBoard;
AddControl(InlineButtonGrid);
return Task.CompletedTask;
}
private async Task InlineButtonGrid_ButtonClicked(object sender, TelegramBotBase.Args.ButtonClickedEventArgs e)
{
//Not found
if (!AllowedInlineInputs.Any(a => a.Item2 == e.Button.Value))
{
await Device.Send("Invalid input!");
return;
}
if (UserDetails == null)
{
return;
}
UserDetails.AgeRange = e.Button?.Value ?? "unknown";
var sf = new SecondForm();
await NavigateTo(sf);
}
public override async Task PressReplyButton()
{
var sf = new StartForm();
await NavigateTo(sf);
}
}
}
@@ -0,0 +1,95 @@
using InlineAndReplyCombination.Baseclasses;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Telegram.Bot.Types;
using TelegramBotBase.Base;
using TelegramBotBase.Controls.Hybrid;
using TelegramBotBase.Form;
namespace InlineAndReplyCombination.Forms.Steps
{
public class SecondForm : MultipleChoiceForm
{
ButtonGrid? InlineButtonGrid;
public static List<Tuple<String, String>> AllowedInlineInputs;
static SecondForm()
{
AllowedInlineInputs = new List<Tuple<string, string>>()
{
new("Green", "green"),
new("Yellow", "yellow"),
new("Red", "red"),
new("Purple", "purple"),
new("Black", "black"),
new("Gold", "gold")
};
}
public SecondForm()
{
Init += SecondForm_Init;
ReplyButtonTitle = "Go back";
CurrentStep = 2;
}
private Task SecondForm_Init(object sender, TelegramBotBase.Args.InitEventArgs e)
{
//Inline Keyboard
var bf_ages = new ButtonForm();
//Add all options in a single column
bf_ages.AddSplitted(AllowedInlineInputs.Select(a => new ButtonBase(a.Item1, a.Item2)), 1);
InlineButtonGrid = new ButtonGrid(bf_ages);
InlineButtonGrid.ConfirmationText = "Thank you";
InlineButtonGrid.Title = "Please choose your favourite color:";
InlineButtonGrid.ButtonClicked += InlineButtonGrid_ButtonClicked;
InlineButtonGrid.KeyboardType = TelegramBotBase.Enums.EKeyboardType.InlineKeyBoard;
AddControl(InlineButtonGrid);
return Task.CompletedTask;
}
private async Task InlineButtonGrid_ButtonClicked(object sender, TelegramBotBase.Args.ButtonClickedEventArgs e)
{
//Not found
if (!AllowedInlineInputs.Any(a => a.Item2 == e.Button.Value))
{
await Device.Send("Invalid input!");
return;
}
if (UserDetails == null)
{
return;
}
UserDetails.FavouriteColor = e.Button?.Value ?? "unknown";
var tf = new ThirdForm();
await NavigateTo(tf);
}
public override async Task PressReplyButton()
{
var mf = new MainForm();
await NavigateTo(mf);
}
}
}
@@ -0,0 +1,84 @@
using InlineAndReplyCombination.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Attributes;
using TelegramBotBase.Base;
using TelegramBotBase.Controls.Hybrid;
using TelegramBotBase.Form;
namespace InlineAndReplyCombination.Forms.Steps
{
public class Summary : AutoCleanForm
{
[SaveState]
public UserDetails? UserDetails { get; set; }
ButtonGrid? ReplyButtonGrid { get; set; }
public Summary()
{
Init += Summary_Init;
}
private Task Summary_Init(object sender, TelegramBotBase.Args.InitEventArgs e)
{
var bf = new ButtonForm();
bf.AddButtonRow("Go back", "back");
bf.AddButtonRow("Return to Start", "start");
ReplyButtonGrid = new ButtonGrid(bf);
ReplyButtonGrid.Title = "Thank you for your time!";
ReplyButtonGrid.ButtonClicked += ReplyButtonGrid_ButtonClicked;
AddControl(ReplyButtonGrid);
return Task.CompletedTask;
}
public override async Task Load(MessageResult message)
{
if (UserDetails == null)
{
var sf = new StartForm();
await NavigateTo(sf);
return;
}
await Device.Send($"Your inputs are:\r\n\r\nYour age: {UserDetails.AgeRange}\r\nYour favourite color: {UserDetails.FavouriteColor}\r\nYour favourite city: {UserDetails.FavouriteCity}");
}
private async Task ReplyButtonGrid_ButtonClicked(object sender, TelegramBotBase.Args.ButtonClickedEventArgs e)
{
switch (e.Button.Value ?? "")
{
case "start":
var sf = new StartForm();
await NavigateTo(sf);
break;
case "back":
var tf = new ThirdForm();
tf.UserDetails = UserDetails;
await NavigateTo(tf);
break;
}
}
}
}
@@ -0,0 +1,95 @@
using InlineAndReplyCombination.Baseclasses;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Telegram.Bot.Types;
using TelegramBotBase.Base;
using TelegramBotBase.Controls.Hybrid;
using TelegramBotBase.Form;
namespace InlineAndReplyCombination.Forms.Steps
{
public class ThirdForm : MultipleChoiceForm
{
ButtonGrid? InlineButtonGrid;
public static List<Tuple<String, String>> AllowedInlineInputs;
static ThirdForm()
{
AllowedInlineInputs = new List<Tuple<string, string>>()
{
new("Berlin", "Berlin"),
new("Vienna", "Vienna"),
new("Rome", "Rome"),
new("London", "London"),
new("Moscow", "Moscow"),
new("Bukarest", "Bukarest")
};
}
public ThirdForm()
{
Init += SecondForm_Init;
ReplyButtonTitle = "Go back";
CurrentStep = 3;
}
private Task SecondForm_Init(object sender, TelegramBotBase.Args.InitEventArgs e)
{
//Inline Keyboard
var bf_ages = new ButtonForm();
//Add all options in a single column
bf_ages.AddSplitted(AllowedInlineInputs.Select(a => new ButtonBase(a.Item1, a.Item2)), 1);
InlineButtonGrid = new ButtonGrid(bf_ages);
InlineButtonGrid.ConfirmationText = "Thank you";
InlineButtonGrid.Title = "Please choose your favourite city:";
InlineButtonGrid.ButtonClicked += InlineButtonGrid_ButtonClicked;
InlineButtonGrid.KeyboardType = TelegramBotBase.Enums.EKeyboardType.InlineKeyBoard;
AddControl(InlineButtonGrid);
return Task.CompletedTask;
}
private async Task InlineButtonGrid_ButtonClicked(object sender, TelegramBotBase.Args.ButtonClickedEventArgs e)
{
//Not found
if (!AllowedInlineInputs.Any(a => a.Item2 == e.Button.Value))
{
await Device.Send("Invalid input!");
return;
}
if (UserDetails == null)
{
return;
}
UserDetails.FavouriteCity = e.Button?.Value ?? "unknown";
var sum = new Summary();
sum.UserDetails = this.UserDetails;
await NavigateTo(sum);
}
public override async Task PressReplyButton()
{
var sf = new SecondForm();
await NavigateTo(sf);
}
}
}
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\TelegramBotBase\TelegramBotBase.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace InlineAndReplyCombination.Model
{
[DebuggerDisplay("{AgeRange}, {FavouriteColor}, {FavouriteCity}")]
public class UserDetails
{
public String? AgeRange { get; set; }
public String? FavouriteColor { get; set; }
public String? FavouriteCity { get; set; }
}
}
@@ -0,0 +1,41 @@
using InlineAndReplyCombination.Forms;
using TelegramBotBase;
using TelegramBotBase.Builder;
namespace InlineAndReplyCombination
{
internal class Program
{
public static BotBase? BotBaseInstance { get; private set; }
static async Task Main(string[] args)
{
BotBaseInstance = BotBaseBuilder.Create()
.WithAPIKey(Environment.GetEnvironmentVariable("API_KEY") ??
throw new Exception("API_KEY is not set"))
.DefaultMessageLoop()
.WithStartForm<StartForm>()
.NoProxy()
.DefaultCommands()
.UseJSON(Path.Combine(Directory.GetCurrentDirectory(), "states.json"))
.UseEnglish()
.Build();
await BotBaseInstance.UploadBotCommands();
await BotBaseInstance.Start();
Console.WriteLine("Telegram Bot started");
Console.ReadLine();
await BotBaseInstance.Stop();
}
}
}
@@ -0,0 +1,16 @@
# InlineAndReplyCombination Example
Here you got a basic example on how to use Inline buttons and a ReplyMarkup at the same time.
I used 2 ButtonGrid controls for this purpose.
One for the ReplyKeyboard and one for the Inline buttons.
The ReplyKeyboard is within the base [*MultipleChoiceForm*](Baseclasses/MultipleChoiceForm.cs) class just for reusing a bit of code.
You can put it into all single forms as well, if you wish. Works the same.
Session serialization is enabled by default via the JSON serializer.
+34
View File
@@ -46,6 +46,7 @@ BitTorrent: `TYVZSykaVT1nKZnz9hjDgBRNB9VavU1bpW`
* [PromptDialog](#prompt-dialog)
* [ConfirmDialog](#confirm-dialog)
- [Controls](#controls)
* [Label](#label)
* [ProgressBar](#progress-bar)
* [CalendarPicker](#calendar-picker)
* [MonthPicker](#month-picker)
@@ -636,21 +637,36 @@ await this.NavigateTo(cd);
## Controls
### Label
A minimal control which allows to manage a classic "text" message and update its later without having to keep track of the message id.
<img src=".github/images/label.gif?raw=true" />
Check the example project [TelegramBotBase.Test/Tests/Controls/LabelForm.cs](TelegramBotBase.Test/Tests/Controls/LabelForm.cs)
### Progress Bar
<img src=".github/images/progressbar.PNG?raw=true" />
Check the example project [TelegramBotBase.Test/Tests/ProgressTest.cs](TelegramBotBase.Test/Tests/ProgressTest.cs)
### Calendar Picker
<img src=".github/images/calendarpicker.PNG?raw=true" />
<img src=".github/images/calendarpicker.gif?raw=true" />
Check the example project [TelegramBotBase.Test/Tests/Controls/CalendarPickerForm.cs](TelegramBotBase.Test/Tests/Controls/CalendarPickerForm.cs)
### Month Picker
<img src=".github/images/monthpicker1.PNG?raw=true" />
<img src=".github/images/monthpicker2.PNG?raw=true" />
Check the example project [TelegramBotBase.Test/Tests/Controls/MonthPickerForm.cs](TelegramBotBase.Test/Tests/Controls/MonthPickerForm.cs)
### Tree View
<img src=".github/images/treeview1.PNG?raw=true" />
@@ -658,30 +674,44 @@ await this.NavigateTo(cd);
<img src=".github/images/treeview3.PNG?raw=true" />
<img src=".github/images/treeview4.PNG?raw=true" />
Check the example project [TelegramBotBase.Test/Tests/Controls/TreeViewForms.cs](TelegramBotBase.Test/Tests/Controls/TreeViewForms.cs)
### Toggle Button
<img src=".github/images/togglebutton.gif?raw=true" />
Check the example project [TelegramBotBase.Test/Tests/Controls/ToggleButtonForm.cs](TelegramBotBase.Test/Tests/Controls/ToggleButtonForm.cs)
### Button Grid
<img src=".github/images/buttongrid.gif?raw=true" />
Check the example project [TelegramBotBase.Test/Tests/Controls/ButtonGridForm.cs](TelegramBotBase.Test/Tests/Controls/ButtonGridForm.cs)
#### Paging & Searching
<img src=".github/images/buttongrid_pagingfilter.gif?raw=true" />
Check the example project [TelegramBotBase.Test/Tests/Controls/ButtonGridPadingForm.cs](TelegramBotBase.Test/Tests/Controls/ButtonGridPagingForm.cs)
### Tagged Button Grid
<img src=".github/images/taggedbuttongrid.gif?raw=true" />
Check the example project [TelegramBotBase.Test/Tests/Controls/ButtonGridTagForm.cs](TelegramBotBase.Test/Tests/Controls/ButtonGridTagForm.cs)
### Checked Button List
<img src=".github/images/checkedbuttonlist.gif?raw=true" />
Check the example project [TelegramBotBase.Test/Tests/Controls/CheckedButtonListForm.cs](TelegramBotBase.Test/Tests/Controls/CheckedButtonListForm.cs)
### Multi Toggle Button
<img src=".github/images/multitogglebutton.gif?raw=true" />
Check the example project [TelegramBotBase.Test/Tests/Controls/MultiToggleButtonForm.cs](TelegramBotBase.Test/Tests/Controls/MultiToggleButtonForm.cs)
## Groups
For groups, there are multiple different tools which help to work with and allows bot also to manage
@@ -1047,3 +1077,7 @@ method of BotBase.
Having already a web application and want to add a TelegramBot side-by-side with it running ? Here is an example how you could do it.
- [Examples/BotAndWebApplication](Examples/BotAndWebApplication)
Want to use Inline- and ReplyMarkup at the same time ? Here is an example how you can do that:
- [Examples/InlineAndReplyCombination](Examples/InlineAndReplyCombination)
@@ -3,7 +3,6 @@ using System.Drawing.Imaging;
using System.IO;
using System.Threading.Tasks;
using Telegram.Bot.Types;
using Telegram.Bot.Types.InputFiles;
using TelegramBotBase.Form;
using TelegramBotBase.Sessions;
@@ -34,7 +33,7 @@ namespace TelegramBotBase.Extensions.Images
{
using (var fileStream = ToStream(image, ImageFormat.Png))
{
var fts = new InputOnlineFile(fileStream, name);
var fts = InputFile.FromStream(fileStream, name);
return await session.SendPhoto(fts, caption, buttons, replyTo, disableNotification);
}
@@ -55,7 +54,7 @@ namespace TelegramBotBase.Extensions.Images
{
using (var fileStream = ToStream(image, ImageFormat.Png))
{
var fts = new InputOnlineFile(fileStream, name);
var fts = InputFile.FromStream(fileStream, name);
return await session.SendPhoto(fts, caption, buttons, replyTo, disableNotification);
}
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net5;netcoreapp3.1;net6</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net6</TargetFrameworks>
<RepositoryUrl>https://github.com/MajMcCloud/TelegramBotFramework</RepositoryUrl>
<PackageProjectUrl>https://github.com/MajMcCloud/TelegramBotFramework</PackageProjectUrl>
<Copyright>MIT</Copyright>
@@ -14,11 +14,15 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Drawing.Common" Version="6.0.0"/>
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TelegramBotBase\TelegramBotBase.csproj"/>
<ProjectReference Include="..\TelegramBotBase\TelegramBotBase.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net5;netcoreapp3.1;net6</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net6</TargetFrameworks>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<RepositoryUrl>https://github.com/MajMcCloud/TelegramBotFramework</RepositoryUrl>
<PackageProjectUrl>https://github.com/MajMcCloud/TelegramBotFramework</PackageProjectUrl>
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Form;
namespace TelegramBotBase.Example.Tests
{
public class ArrayPromptDialogTest : ArrayPromptDialog
{
public ArrayPromptDialogTest() : base("Choose an option", new[] { new ButtonBase("Option 1", "option1"), new ButtonBase("Option 2", "option2") })
{
this.ButtonClicked += ArrayPromptDialogTest_ButtonClicked;
}
private void ArrayPromptDialogTest_ButtonClicked(object sender, Args.ButtonClickedEventArgs e)
{
Console.WriteLine(e.Button.Text + " has been clicked");
}
}
}
@@ -0,0 +1,84 @@
using System;
using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Controls.Hybrid;
using TelegramBotBase.Controls.Inline;
using TelegramBotBase.Enums;
using TelegramBotBase.Form;
namespace TelegramBotBase.Example.Tests.Controls;
public class LabelForm : AutoCleanForm
{
TelegramBotBase.Controls.Inline.LabelControl _label;
ButtonGrid _buttonGrid;
String[] string_options = new string[] { "My test label", "Here is a different text", "*And this looks completely different.*", "Aha! another one.", "_Gotcha!_" };
public LabelForm()
{
DeleteMode = EDeleteMode.OnLeavingForm;
Init += LabelForm_Init;
}
private Task LabelForm_Init(object sender, InitEventArgs e)
{
//The "label" control
_label = new LabelControl("My test label");
AddControl(_label);
//Optional...just for experimentation...
var bf = new ButtonForm();
bf.AddButtonRow("Toggle text", "toggle");
bf.AddButtonRow("Open menu", "menu");
_buttonGrid = new ButtonGrid(bf);
_buttonGrid.KeyboardType = EKeyboardType.ReplyKeyboard;
_buttonGrid.Title = "Choose your options:";
_buttonGrid.ButtonClicked += _buttonGrid_ButtonClicked;
AddControl(_buttonGrid);
return Task.CompletedTask;
}
private async Task _buttonGrid_ButtonClicked(object sender, ButtonClickedEventArgs e)
{
switch (e.Button.Value ?? "")
{
case "menu":
var mn = new Menu();
await NavigateTo(mn);
break;
case "toggle":
//Pick random string from array
var r = new Random((int)DateTime.UtcNow.Ticks);
String random_string;
do
{
random_string = string_options[r.Next(0, string_options.Length)];
if (random_string == null)
continue;
} while (random_string == _label.Text);
_label.Text = random_string;
break;
}
}
}
@@ -9,9 +9,9 @@ using TelegramBotBase.Form;
namespace TelegramBotBase.Example.Tests.Controls;
public class MultiToggleButtons : AutoCleanForm
public class MultiToggleButtonForm : AutoCleanForm
{
public MultiToggleButtons()
public MultiToggleButtonForm()
{
DeleteMode = EDeleteMode.OnLeavingForm;
@@ -7,9 +7,9 @@ using TelegramBotBase.Form;
namespace TelegramBotBase.Example.Tests.Controls;
public class ToggleButtons : AutoCleanForm
public class ToggleButtonForm : AutoCleanForm
{
public ToggleButtons()
public ToggleButtonForm()
{
DeleteMode = EDeleteMode.OnLeavingForm;
+6 -6
View File
@@ -1,8 +1,8 @@
using System.Linq;
using System.Threading.Tasks;
using Telegram.Bot;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InputFiles;
using Telegram.Bot.Types.ReplyMarkups;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
@@ -14,7 +14,7 @@ public class DataForm : AutoCleanForm
public override async Task SentData(DataResult data)
{
var tmp = "";
InputOnlineFile file;
InputFile file;
switch (data.Type)
{
@@ -31,7 +31,7 @@ public class DataForm : AutoCleanForm
case MessageType.Document:
file = new InputOnlineFile(data.Document.FileId);
file = InputFile.FromString(data.Document.FileId);
await Device.SendDocument(file, "Your uploaded document");
@@ -40,7 +40,7 @@ public class DataForm : AutoCleanForm
case MessageType.Video:
file = new InputOnlineFile(data.Document.FileId);
file = InputFile.FromString(data.Document.FileId);
await Device.SendDocument(file, "Your uploaded video");
@@ -48,7 +48,7 @@ public class DataForm : AutoCleanForm
case MessageType.Audio:
file = new InputOnlineFile(data.Document.FileId);
file = InputFile.FromString(data.Document.FileId);
await Device.SendDocument(file, "Your uploaded audio");
@@ -65,7 +65,7 @@ public class DataForm : AutoCleanForm
case MessageType.Photo:
var photo = new InputOnlineFile(data.Photos.Last().FileId);
var photo = InputFile.FromString(data.Photos.Last().FileId);
await Device.Send("Your image: ", replyTo: data.MessageId);
await Client.TelegramClient.SendPhotoAsync(Device.DeviceId, photo);
@@ -102,7 +102,13 @@ public class LinkReplaceTest : GroupForm
CanChangeInfo = false,
CanInviteUsers = false,
CanPinMessages = false,
CanSendMediaMessages = false,
CanManageTopics = false,
CanSendAudios = false,
CanSendVideos = false,
CanSendDocuments = false,
CanSendPhotos = false,
CanSendVideoNotes = false,
CanSendVoiceNotes = false,
CanSendMessages = false,
CanSendOtherMessages = false,
CanSendPolls = false
@@ -128,7 +134,7 @@ public class LinkReplaceTest : GroupForm
}
else
{
await e.Device.RestrictUser(from, cp, DateTime.UtcNow.AddSeconds(30));
await e.Device.RestrictUser(from, cp, null, DateTime.UtcNow.AddSeconds(30));
await e.Device.Send(e.Message.From.FirstName + " " + e.Message.From.LastName +
" has been blocked for 30 seconds");
+25 -3
View File
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Telegram.Bot.Types.Enums;
using TelegramBotBase.Base;
using TelegramBotBase.Enums;
@@ -127,7 +128,7 @@ public class Menu : AutoCleanForm
case "togglebuttons":
var tb = new ToggleButtons();
var tb = new ToggleButtonForm();
await NavigateTo(tb);
@@ -135,7 +136,7 @@ public class Menu : AutoCleanForm
case "multitogglebuttons":
var mtb = new MultiToggleButtons();
var mtb = new MultiToggleButtonForm();
await NavigateTo(mtb);
@@ -207,6 +208,22 @@ public class Menu : AutoCleanForm
break;
case "label":
var lf = new LabelForm();
await NavigateTo(lf);
break;
case "arraypromptdialog":
var apt = new ArrayPromptDialogTest();
await NavigateTo(apt);
break;
default:
message.Handled = false;
@@ -260,6 +277,11 @@ public class Menu : AutoCleanForm
btn.AddButtonRow(new ButtonBase("#19 Notifications", new CallbackData("a", "notifications").Serialize()));
btn.AddButtonRow(new ButtonBase("#20 Label", new CallbackData("a", "label").Serialize()));
btn.AddButtonRow(new ButtonBase("#21 ArrayPromptDialogTest", new CallbackData("a", "arraypromptdialog").Serialize()));
await Device.Send("Choose your test:", btn);
}
}
-1
View File
@@ -1 +0,0 @@

+9 -9
View File
@@ -5,7 +5,6 @@ using System.Threading.Tasks;
using Telegram.Bot;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InputFiles;
namespace TelegramBotBase.Base;
@@ -17,6 +16,7 @@ public class DataResult : ResultBase
public DataResult(UpdateResult update)
{
UpdateData = update;
Device = update.Device;
}
//public Telegram.Bot.Args.MessageEventArgs RawMessageData { get; set; }
@@ -51,14 +51,14 @@ public class DataResult : ResultBase
Photos.FirstOrDefault()?.FileId;
public async Task<InputOnlineFile> DownloadDocument()
public async Task<InputFileStream> DownloadDocument()
{
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(Document.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Document.FileId,
encryptedContent);
return new InputOnlineFile(encryptedContent, Document.FileName);
return InputFile.FromStream(encryptedContent, Document.FileName);
}
@@ -112,13 +112,13 @@ public class DataResult : ResultBase
return sr.ReadToEnd();
}
public async Task<InputOnlineFile> DownloadVideo()
public async Task<InputFileStream> DownloadVideo()
{
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(Video.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Video.FileId, encryptedContent);
return new InputOnlineFile(encryptedContent, "");
return InputFile.FromStream(encryptedContent, "");
}
public async Task DownloadVideo(string path)
@@ -130,13 +130,13 @@ public class DataResult : ResultBase
fs.Dispose();
}
public async Task<InputOnlineFile> DownloadAudio()
public async Task<InputFileStream> DownloadAudio()
{
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(Audio.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Audio.FileId, encryptedContent);
return new InputOnlineFile(encryptedContent, "");
return InputFile.FromStream(encryptedContent, "");
}
public async Task DownloadAudio(string path)
@@ -148,14 +148,14 @@ public class DataResult : ResultBase
fs.Dispose();
}
public async Task<InputOnlineFile> DownloadPhoto(int index)
public async Task<InputFileStream> DownloadPhoto(int index)
{
var photo = Photos[index];
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(photo.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(photo.FileId, encryptedContent);
return new InputOnlineFile(encryptedContent, "");
return InputFile.FromStream(encryptedContent, "");
}
public async Task DownloadPhoto(int index, string path)
+1 -1
View File
@@ -7,7 +7,7 @@ using System.Threading;
using System.Threading.Tasks;
using Telegram.Bot;
using Telegram.Bot.Exceptions;
using Telegram.Bot.Extensions.Polling;
using Telegram.Bot.Polling;
using Telegram.Bot.Types;
namespace TelegramBotBase.Base;
+45
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Telegram.Bot;
using Telegram.Bot.Types;
@@ -314,19 +315,63 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
return this;
}
/// <summary>
/// Uses the application runtime path to load and write a states.json file.
/// </summary>
/// <returns></returns>
public ILanguageSelectionStage UseJSON()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "states.json");
_stateMachine = new JsonStateMachine(path);
return this;
}
/// <summary>
/// Uses the given path to load and write a states.json file.
/// </summary>
/// <returns></returns>
public ILanguageSelectionStage UseJSON(string path)
{
_stateMachine = new JsonStateMachine(path);
return this;
}
/// <summary>
/// Uses the application runtime path to load and write a states.json file.
/// </summary>
/// <returns></returns>
public ILanguageSelectionStage UseSimpleJSON()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "states.json");
_stateMachine = new SimpleJsonStateMachine(path);
return this;
}
/// <summary>
/// Uses the given path to load and write a states.json file.
/// </summary>
/// <returns></returns>
public ILanguageSelectionStage UseSimpleJSON(string path)
{
_stateMachine = new SimpleJsonStateMachine(path);
return this;
}
/// <summary>
/// Uses the application runtime path to load and write a states.xml file.
/// </summary>
/// <returns></returns>
public ILanguageSelectionStage UseXML()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "states.xml");
_stateMachine = new XmlStateMachine(path);
return this;
}
/// <summary>
/// Uses the given path to load and write a states.xml file.
/// </summary>
/// <returns></returns>
public ILanguageSelectionStage UseXML(string path)
{
_stateMachine = new XmlStateMachine(path);
@@ -18,6 +18,13 @@ public interface ISessionSerializationStage
/// <returns></returns>
ILanguageSelectionStage UseSerialization(IStateMachine machine);
/// <summary>
/// Using the complex version of .Net JSON, which can serialize all objects.
/// Saves in application directory.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
ILanguageSelectionStage UseJSON();
/// <summary>
/// Using the complex version of .Net JSON, which can serialize all objects.
@@ -26,6 +33,13 @@ public interface ISessionSerializationStage
/// <returns></returns>
ILanguageSelectionStage UseJSON(string path);
/// <summary>
/// Use the easy version of .Net JSON, which can serialize basic types, but not generics and others.
/// Saves in application directory.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
ILanguageSelectionStage UseSimpleJSON();
/// <summary>
/// Use the easy version of .Net JSON, which can serialize basic types, but not generics and others.
@@ -34,6 +48,13 @@ public interface ISessionSerializationStage
/// <returns></returns>
ILanguageSelectionStage UseSimpleJSON(string path);
/// <summary>
/// Uses the XML serializer for session serialization.
/// Saves in application directory.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
ILanguageSelectionStage UseXML();
/// <summary>
/// Uses the XML serializer for session serialization.
+110
View File
@@ -0,0 +1,110 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading.Tasks;
using Telegram.Bot;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Localizations;
namespace TelegramBotBase.Controls.Inline;
[DebuggerDisplay("{Text}")]
public class Label : ControlBase
{
private bool _renderNecessary = true;
private string _text = string.Empty;
public String Text
{
get
{
return _text;
}
set
{
if (_text == value)
return;
_text = value;
_renderNecessary = true;
}
}
private ParseMode _parseMode = ParseMode.Markdown;
public ParseMode ParseMode
{
get
{
return _parseMode;
}
set
{
_parseMode = value;
_renderNecessary = true;
}
}
public Label()
{
}
public Label(string text)
{
_text = text;
}
public Label(string text, ParseMode parseMode)
{
_text = text;
_parseMode = parseMode;
}
public int? MessageId { get; set; }
public override async Task Render(MessageResult result)
{
if (!_renderNecessary)
{
return;
}
Message m;
//Update ?
if (MessageId != null)
{
m = await Device.Raw(a => a.EditMessageTextAsync(Device.DeviceId, MessageId.Value, Text, _parseMode));
_renderNecessary = false;
return;
}
//New Message
m = await Device.Raw(a => a.SendTextMessageAsync(Device.DeviceId, Text, disableNotification: true, parseMode: _parseMode));
if (m != null)
{
MessageId = m.MessageId;
}
_renderNecessary = false;
}
public override async Task Cleanup()
{
if (this.MessageId == null)
return;
await Device.DeleteMessage(MessageId.Value);
}
}
@@ -41,8 +41,6 @@ public class ArrayPromptDialog : FormBase
public ButtonBase[][] Buttons { get; set; }
[Obsolete] public Dictionary<string, FormBase> ButtonForms { get; set; } = new();
private static object EvButtonClicked { get; } = new();
public override async Task Action(MessageResult message)
@@ -75,13 +73,6 @@ public class ArrayPromptDialog : FormBase
}
OnButtonClicked(new ButtonClickedEventArgs(button) { Tag = Tag });
var fb = ButtonForms.ContainsKey(call.Value) ? ButtonForms[call.Value] : null;
if (fb != null)
{
await NavigateTo(fb);
}
}
@@ -44,6 +44,7 @@ public class FormBaseMessageLoop : IMessageLoopFactory
}
mr.Device = session;
ur.Device = session;
var activeForm = session.ActiveForm;
@@ -37,6 +37,7 @@ public class FullMessageLoop : IMessageLoopFactory
}
mr.Device = session;
ur.Device = session;
var activeForm = session.ActiveForm;
+4 -4
View File
@@ -7,9 +7,9 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTitle("TelegramBotBase Framework")]
[assembly: AssemblyDescription("This is a context based application framework for the C# TelegramBot library.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Florian Dahn")]
[assembly: AssemblyCompany("Florian Zevedei")]
[assembly: AssemblyProduct("TelegramBotBase Framework")]
[assembly: AssemblyCopyright("Copyright © Florian Dahn 2020")]
[assembly: AssemblyCopyright("Copyright © Florian Zevedei 2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.1.0.0")]
[assembly: AssemblyFileVersion("3.1.0.0")]
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.0.0")]
+14 -15
View File
@@ -9,7 +9,6 @@ using Telegram.Bot;
using Telegram.Bot.Exceptions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InputFiles;
using Telegram.Bot.Types.ReplyMarkups;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
@@ -280,7 +279,7 @@ public class DeviceSession : IDeviceSession
try
{
var t = Api(a => a.SendTextMessageAsync(deviceId, text, parseMode, replyToMessageId: replyTo,
var t = Api(a => a.SendTextMessageAsync(deviceId, text, null, parseMode, replyToMessageId: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
@@ -339,7 +338,7 @@ public class DeviceSession : IDeviceSession
try
{
var t = Api(a => a.SendTextMessageAsync(DeviceId, text, parseMode, replyToMessageId: replyTo,
var t = Api(a => a.SendTextMessageAsync(DeviceId, text, null, parseMode, replyToMessageId: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
@@ -382,7 +381,7 @@ public class DeviceSession : IDeviceSession
try
{
var t = Api(a => a.SendTextMessageAsync(DeviceId, text, parseMode, replyToMessageId: replyTo,
var t = Api(a => a.SendTextMessageAsync(DeviceId, text, null, parseMode, replyToMessageId: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
@@ -404,7 +403,7 @@ public class DeviceSession : IDeviceSession
/// <param name="replyTo"></param>
/// <param name="disableNotification"></param>
/// <returns></returns>
public async Task<Message> SendPhoto(InputOnlineFile file, string caption = null, ButtonForm buttons = null,
public async Task<Message> SendPhoto(InputFile file, string caption = null, ButtonForm buttons = null,
int replyTo = 0, bool disableNotification = false,
ParseMode parseMode = ParseMode.Markdown)
{
@@ -417,7 +416,7 @@ public class DeviceSession : IDeviceSession
try
{
var t = Api(a => a.SendPhotoAsync(DeviceId, file, caption, parseMode, replyToMessageId: replyTo,
var t = Api(a => a.SendPhotoAsync(DeviceId, file, null, caption, parseMode, replyToMessageId: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
@@ -439,7 +438,7 @@ public class DeviceSession : IDeviceSession
/// <param name="replyTo"></param>
/// <param name="disableNotification"></param>
/// <returns></returns>
public async Task<Message> SendVideo(InputOnlineFile file, string caption = null, ButtonForm buttons = null,
public async Task<Message> SendVideo(InputFile file, string caption = null, ButtonForm buttons = null,
int replyTo = 0, bool disableNotification = false,
ParseMode parseMode = ParseMode.Markdown)
{
@@ -487,7 +486,7 @@ public class DeviceSession : IDeviceSession
try
{
var t = Api(a => a.SendVideoAsync(DeviceId, new InputOnlineFile(url), parseMode: parseMode,
var t = Api(a => a.SendVideoAsync(DeviceId, InputFile.FromUri(url), parseMode: parseMode,
replyToMessageId: replyTo, replyMarkup: markup,
disableNotification: disableNotification));
@@ -525,7 +524,7 @@ public class DeviceSession : IDeviceSession
{
var ms = new MemoryStream(video);
var fts = new InputOnlineFile(ms, filename);
var fts = InputFile.FromStream(ms, filename);
var t = Api(a => a.SendVideoAsync(DeviceId, fts, parseMode: parseMode, replyToMessageId: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
@@ -567,7 +566,7 @@ public class DeviceSession : IDeviceSession
var filename = Path.GetFileName(filepath);
var fts = new InputOnlineFile(fs, filename);
var fts = InputFile.FromStream(fs, filename);
var t = Api(a => a.SendVideoAsync(DeviceId, fts, parseMode: parseMode, replyToMessageId: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
@@ -599,7 +598,7 @@ public class DeviceSession : IDeviceSession
{
var ms = new MemoryStream(document);
var fts = new InputOnlineFile(ms, filename);
var fts = InputFile.FromStream(ms, filename);
return await SendDocument(fts, caption, buttons, replyTo, disableNotification);
}
@@ -641,7 +640,7 @@ public class DeviceSession : IDeviceSession
/// <param name="replyTo"></param>
/// <param name="disableNotification"></param>
/// <returns></returns>
public async Task<Message> SendDocument(InputOnlineFile document, string caption = "",
public async Task<Message> SendDocument(InputFile document, string caption = "",
ButtonForm buttons = null, int replyTo = 0,
bool disableNotification = false)
{
@@ -653,7 +652,7 @@ public class DeviceSession : IDeviceSession
try
{
var t = Api(a => a.SendDocumentAsync(DeviceId, document, caption, replyMarkup: markup,
var t = Api(a => a.SendDocumentAsync(DeviceId, document, null, null, caption, replyMarkup: markup,
disableNotification: disableNotification, replyToMessageId: replyTo));
var o = GetOrigin(new StackTrace());
@@ -866,11 +865,11 @@ public class DeviceSession : IDeviceSession
#region "Users"
public virtual async Task RestrictUser(long userId, ChatPermissions permissions, DateTime until = default)
public virtual async Task RestrictUser(long userId, ChatPermissions permissions, bool? useIndependentGroupPermission = null, DateTime until = default)
{
try
{
await Api(a => a.RestrictChatMemberAsync(DeviceId, userId, permissions, until));
await Api(a => a.RestrictChatMemberAsync(DeviceId, userId, permissions, useIndependentGroupPermission, until));
}
catch
{
+2 -3
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net5;netcoreapp3.1;net6</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net6</TargetFrameworks>
<LangVersion>10</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
@@ -57,8 +57,7 @@
<ItemGroup>
<PackageReference Include="Telegram.Bot" Version="18.0.0" />
<PackageReference Include="Telegram.Bot.Extensions.Polling" Version="1.0.2" />
<PackageReference Include="Telegram.Bot" Version="19.0.0" />
</ItemGroup>
</Project>
+2 -2
View File
@@ -15,12 +15,12 @@
<group targetFramework=".NETFramework4.6.1">
<dependency id="Newtonsoft.Json" version="13.0.1" exclude="Build,Analyzers" />
<dependency id="System.Drawing.Common" version="4.6.0" exclude="Build,Analyzers" />
<dependency id="Telegram.Bot" version="15.0.0" exclude="Build,Analyzers" />
<dependency id="Telegram.Bot" version="19.0.0" exclude="Build,Analyzers" />
</group>
<group targetFramework=".NETStandard2.0">
<dependency id="Newtonsoft.Json" version="13.0.1" exclude="Build,Analyzers" />
<dependency id="System.Drawing.Common" version="4.6.0" exclude="Build,Analyzers" />
<dependency id="Telegram.Bot" version="15.0.0" exclude="Build,Analyzers" />
<dependency id="Telegram.Bot" version="19.0.0" exclude="Build,Analyzers" />
</group>
</dependencies>
</metadata>
+8 -1
View File
@@ -28,7 +28,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TelegramBotBase.Extensions.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCoreBot", "Examples\EFCoreBot\EFCoreBot.csproj", "{261BED47-0404-4A9A-86FC-047DE42A7D25}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotAndWebApplication", "Examples\BotAndWebApplication\BotAndWebApplication.csproj", "{52EA3201-02E8-46F5-87C4-B4752C8A815C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotAndWebApplication", "Examples\BotAndWebApplication\BotAndWebApplication.csproj", "{52EA3201-02E8-46F5-87C4-B4752C8A815C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InlineAndReplyCombination", "Examples\InlineAndReplyCombination\InlineAndReplyCombination.csproj", "{067E8EBE-F90A-4AFF-A0FF-20578216486E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -72,6 +74,10 @@ Global
{52EA3201-02E8-46F5-87C4-B4752C8A815C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52EA3201-02E8-46F5-87C4-B4752C8A815C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52EA3201-02E8-46F5-87C4-B4752C8A815C}.Release|Any CPU.Build.0 = Release|Any CPU
{067E8EBE-F90A-4AFF-A0FF-20578216486E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{067E8EBE-F90A-4AFF-A0FF-20578216486E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{067E8EBE-F90A-4AFF-A0FF-20578216486E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{067E8EBE-F90A-4AFF-A0FF-20578216486E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -84,6 +90,7 @@ Global
{889B170E-32E9-4F26-BB04-8D06EA367857} = {E3193182-6FDA-4FA3-AD26-A487291E7681}
{261BED47-0404-4A9A-86FC-047DE42A7D25} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5}
{52EA3201-02E8-46F5-87C4-B4752C8A815C} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5}
{067E8EBE-F90A-4AFF-A0FF-20578216486E} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {59CB40E1-9FA7-4867-A56F-4F418286F057}