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
commit 9548be4afc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1038 additions and 71 deletions

BIN
.github/images/label.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 KiB

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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>

View File

@ -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; }
}
}

View File

@ -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();
}
}
}

View File

@ -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.

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
@ -1046,4 +1076,8 @@ 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)
- [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)

View File

@ -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);
}

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>
<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>

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>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<RepositoryUrl>https://github.com/MajMcCloud/TelegramBotFramework</RepositoryUrl>
<PackageProjectUrl>https://github.com/MajMcCloud/TelegramBotFramework</PackageProjectUrl>

View File

@ -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");
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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;

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);

View File

@ -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");

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);
}
}

View File

@ -1 +0,0 @@


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)

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;

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);

View File

@ -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.

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);
}
}

View File

@ -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);
}
}

View File

@ -44,6 +44,7 @@ public class FormBaseMessageLoop : IMessageLoopFactory
}
mr.Device = session;
ur.Device = session;
var activeForm = session.ActiveForm;

View File

@ -37,6 +37,7 @@ public class FullMessageLoop : IMessageLoopFactory
}
mr.Device = session;
ur.Device = session;
var activeForm = session.ActiveForm;

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")]

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
{

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>

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>

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}