Different response from bot emulator and chatbot in web - c#

I have created a chatbot in web channel and direct line.
When i tested in bot emulator i get the right response and when i try to test the same intent in localhost ( webchat) i got different response.
I will show you and example :
call an agent
give me your customer number
( after custemer number was sended ) are you sure ?
if you click Yes ...the data are stored in database ( sql server )
If you do the save in localhost you get : You cancelled the form ( in fact i havent cancell any form
Here is the luisdialog here i call the form :
[LuisIntent("human")]
public async Task human(IDialogContext context, LuisResult result)
{
var form = new FormDialog<Human>(
new Human(),
Human.BuildForm,
FormOptions.PromptInStart,
result.Entities);
context.Call<Human>(form, LossFormCompleted)
}
private async Task LossFormCompleted(IDialogContext context,
IAwaitable<Human> result)
{
HumanCall form = null;
try
{
form = await result;
}
catch (OperationCanceledException)
{
}
if (form == null)
{
await context.PostAsync("You cancelled the form.");
}
else
{
//call the LossForm service to complete the form fill
var message = $"Your data are stored in database";
await context.PostAsync(message);
}
context.Wait(this.MessageReceived);
}
The form model is :
[Serializable]
public class Human
{
[Prompt("What is your contract number?")]
public string contract;
public static IForm<Human> BuildForm()
{
OnCompletionAsyncDelegate<HumanCall> wrapUpRequest = async (context, state) =>
{
using (BotModelDataContext BotDb = new BotModelDataContext())
{
tblBot bot = new tblBot();
bot = BotDb.tblBots.SingleOrDefault(q => q.Reference == state.contract);
if (bot != null)
{
using (bbbserviceSoapClient cws = new bbbserviceSoapClient())
{
viewc a= new viewc();
a.Lastname = bot.Lastname;
}
}
}
};
return new FormBuilder<Human>().Message
("can you send us some info ?")
.Field(nameof(contract))
.OnCompletion(wrapUpRequest)
.Confirm("Are you sure: Yes or No. ")
.Build();
}
}
}
Can someone help me where i'm wrong ? What can i do to retrieve the same response ? It's about timeout problem or what do you thing ?

I do a test based on the code that you provided and make slight modifications, and I find that if some exceptions occur in wrapUpRequest method, it would show "You cancelled the form" instead of the message "Your data are stored in database".
So I suspect that exceptions occurring in wrapUpRequest method (perhaps database query issue or request sent by bbbserviceSoapClient is timeout etc) when you do test via web chat, which causes the issue.
To troubleshoot the issue, you can try to implement/write custom log to detect if any exception occurs within wrapUpRequest method when you test via web chat.

Related

My Discord Bot won't accept Users as parameters for commands

I'm currently working on a Discord bot to learn how to code one. I thought I had it down, but when I try to use the following command, it does nothing:
[Command("ping")]
public async Task Ping(IUser user)
{
await Context.Channel.SendMessageAsync(user.ToString());
}
It's part of a public class, and if I use any other parameter type (e.g. IChannel, bool, int) it works. It's just this one parameter type. It also doesn't log any errors or exceptions. Any ideas?
[Command("ping")]
public async Task Ping(IUser user)
{
await Context.Channel.SendMessageAsync(user.ToString());
}
Your code id perfect. But think about this, the user is of the type IUser and your conversion to sting makes it vague. Instead try this:
[Command("ping")]
public async Task Ping(SocketGuildUser user)
{
await Context.Channel.SendMessageAsync(user.Username);
}
If you want to ping the user try user.Mention.
Also when I started learning I made a bot as well. Here is the source code. Its very very very basic. It definitely will help.
You could try using this workaround for your bot:
public async Task SampleCommand(string user="", [Remainder]string message="")
{
IUser subject = null;
if (user != "")
{
var guilds = (await Context.Client.GetGuildsAsync(Discord.CacheMode.AllowDownload));
var users = new List<IUser>();
foreach (var g in guilds)
users.AddRange(await g.GetUsersAsync(CacheMode.AllowDownload));
users = users.GroupBy(o => o.Id).Select(o => o.First()).ToList();
var search = users.Where(o => o.Username.ToLower().Contains(user.ToLower()) || Context.Message.MentionedUserIds.Contains(o.Id) || o.ToString().ToLower().Contains(user.ToLower())).ToArray();
if (search.Length == 0)
{
await ReplyAsync("***Error!*** *Couldn't find that user.*");
return;
}
else if (search.Length > 1)
{
await ReplyAsync("***Error!*** *Found more than one matching users.*");
return;
}
subject = search.First();
}
// ...
// execute command
Or you could wrap that in a method for easier access and reusability.
Basically, what it does is it looks for available users that match the given string (in nickname, username or mentions. You could also make it check for IDs if you so desire).
Edit: In my case I'm allowing people to mention anyone who shares the server with the bot, but in your case it might be more benefitial to just use the Context.Guild instead and cancel the command in case of DMs.
I ended up taking Reynevan's advice, and wrote a method for converting a mention into an IUser. Just call CustomUserTypereader.GetUser(mention_parameter, Context.Guild);
using System.Threading.Tasks;
using Discord;
public class CustomUserTypereader
{
public static async Task<IUser> GetUserFromString(string s, IGuild server)
{
if (s.IndexOf('#') == -1 || s.Replace("<", "").Replace(">", "").Length != s.Length - 2)
throw new System.Exception("Not a valid user mention.");
string idStr = s.Replace("<", "").Replace(">", "").Replace("#", "");
try
{
ulong id = ulong.Parse(idStr);
return await server.GetUserAsync(id);
}
catch
{
throw new System.Exception("Could not parse User ID. Are you sure the user is still on the server?");
}
}
}

Discord bot running, but will not connect to server

I've been trying to swap over my code from the 0.9.6 Discord.NET API to the new 1.0.1 API, and it's basically calling for a complete restructure to my code. But I've been having some trouble actually getting the bot up and running first of all.
I set up the code body according to the guide linked here
And while it runs without error, the bot itself is not appearing online in my server.
And before you ask, I had in fact replaced "Bot token here" with the actual bot token.
namespace DiscordBot{
public class Program
{
private CommandService commands;
private DiscordSocketClient client;
private IServiceProvider services;
static void Main(string[] args) => new Program().Start().GetAwaiter().GetResult();
public async Task Start()
{
client = new DiscordSocketClient();
commands = new CommandService();
string token = "<token>";
services = new ServiceCollection()
.BuildServiceProvider();
await InstallCommands();
await client.LoginAsync(TokenType.Bot, token);
await client.StartAsync();
await Task.Delay(-1);
}
public async Task InstallCommands()
{
// Hook the MessageReceived Event into our Command Handler
client.MessageReceived += HandleCommand;
// Discover all of the commands in this assembly and load them.
await commands.AddModulesAsync(Assembly.GetEntryAssembly());
}
public async Task HandleCommand(SocketMessage messageParam)
{
// Don't process the command if it was a System Message
var message = messageParam as SocketUserMessage;
if (message == null) return;
// Create a number to track where the prefix ends and the command begins
int argPos = 0;
// Determine if the message is a command, based on if it starts with '!' or a mention prefix
if (!(message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(client.CurrentUser, ref argPos))) return;
// Create a Command Context
var context = new CommandContext(client, message);
// Execute the command. (result does not indicate a return value,
// rather an object stating if the command executed successfully)
var result = await commands.ExecuteAsync(context, argPos, services);
if (!result.IsSuccess)
await context.Channel.SendMessageAsync(result.ErrorReason);
}
}
}
And then for the MyBot.cs class
namespace DiscordBot
{
class MyBot : ModuleBase
{
private CommandService _service;
public MyBot(CommandService service)
{
_service = service;
}
}
}
The first thing you might want to do is add some logging to your bot.
As your code might be correct, but discord could be rejecting your connection for any amount of reason.
After await client.StartAsync(); add
client.Log += (msg) => {return Console.WriteLine(${msg.ToString()}");};`
This will output the message your receive from your client to the console.
Now you also need to configure which message should be send to this event. This can be done when creating your DiscordClient(). So instead of client = new DiscordSocketClient(); You could use
client = new DiscordSocketClient(
new DiscordSocketConfig()
{
LogLevel = LogSeverity.Verbose
}
);
Verbose should give you all the information you need. However you could also use LogSeverity.Debug instead, which is the highest available logging, and therefore would give you all messages.
Now that you have some feedback in your console, go and see what concrete errors you are having.
Also I would recommend first completing the your first bot part of the linked tutorial, instead of stepping into the commands directly. Once you got this working, you can continue onwards

Validate internet connection or Azure service - Xamarin

I have a application develop in Xamarin (Cross Platform) that now run great!! But always need internet connection.
If internet connection will fail, my app suffers an unexpected shutdown. I would like controle this.
First I have "AzureDataService" class:
public class AzureDataService
{
//Conexion to backend
public MobileServiceClient MobileService { get; set; }
//Object of "Ficha" class.
IMobileServiceSyncTable<Ficha> tablaFicha;
public async Task Initialize()
{
if (isInitialized)
return;
MobileService = new MobileServiceClient("http://linkbdd");
//Save data in a local DB, later upload with internet connection
const string path = "bbddMuestra.db";
var store = new MobileServiceSQLiteStore(path);//Create DB
store.DefineTable<Ficha>();
//async initialization
await MobileService.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler());
//Inicializate table
tablaFicha = MobileService.GetSyncTable<Ficha>();
isInitialized = true;
}
//Here get data
public async Task<IEnumerable<Ficha>> GetData()
{
await Initialize();
await SyncFicha();
//Select data...
return await tablaFicha.OrderBy(a => a.Id).ToEnumerableAsync();
}
public async Task SyncFicha()
{
await tablaVehiculos.PullAsync("Ficha", tablaFicha.CreateQuery());
await MobileService.SyncContext.PushAsync();
}
End of "AzureDataService" class. Now the class when implement AzureDataService.
public partial class ListaFichas : ContentPage
{
public static ObservableCollection ficha;
public ListaFichas ()
{
InitializeComponent ();
ficha = new ObservableCollection();
}
protected async override void OnAppearing()
{
base.OnAppearing();
ficha.Clear();
//Next line get data from previous method of class "AzureDataService"
var ligas = await App.AzureService.GetData();
foreach(var item in ligas)
{
Ficha fi = item;
ficha.Add(fi);
}
//Here bind listview with data that previous get
lsvFichas.ItemsSource = ficha;
}
Please help me. I would like show a Display or DisplayActionSheet to inform user...But never unexpected shutdown.
Thanks!...
You have to check internet connection
Below link is useful for you
https://forums.xamarin.com/discussion/comment/276931#Comment_276931
AFAIK, if you call PushAsync to PUSH the sync content, then the list of creates, updates and Deletes against your offline tables would be sent one by one to the Azure App Service. Both PullAsync and PushAsync operations need your mobile client online.
Per my understanding, you could check the internet connection before call SyncFicha for sync your data. Also, you could just wrap your SyncFicha method with try-catch for both handling the internet connection and conflict when you pushing the offline data. I recommend that you could refer to the following tutorials from adrian hall's book as follows:
Detecting Connection State
Use Xam.Plugin.Connectivity for checking connectivity state as follows:
await Initialize();
if (!(await CrossConnectivity.Current.IsRemoteReachable(Client.MobileAppUri.Host, 443)))
{
Debug.WriteLine($"Cannot connect to {Client.MobileAppUri} right now - offline");
return;
}
await tablaVehiculos.PullAsync("Ficha", tablaFicha.CreateQuery());
await MobileService.SyncContext.PushAsync();
Handling Conflict Resolution

Can I get a phone number by user id via Telegram Bot API?

I am using Telegram Bot API for sending instant messages to users.
I have installed nuget package. This package is recommend by telegram developers.
I have created a telegram bot and successfully got access to it by using code. When I send messsage to bot, bot gets some info about sender.
I need the phone numbers of users to identify them in our system and send the information back to them.
My question is Can i get a user phone number by telegramUserId?
I'm doing it for user convenience. If I could to get a user phone number I should't have to ask for it from the user.
Now my command like this:
debt 9811201243
I want
debt
It's possible with bots 2.0 check out bot api docs.
https://core.telegram.org/bots/2-0-intro#locations-and-numbers
https://core.telegram.org/bots/api#keyboardbutton
No, unfortunately Telegram Bot API doesn't return phone number. You should either use Telegram API methods instead or ask it explicitly from the user. You cannot get "friends" of a user as well.
You will definitely retrieve the following information:
userid
first_name
content (whatever it is: text, photo, etc.)
date (unixtime)
chat_id
If user configured it, you will also get last_name and username.
With Telegram Bot API, you can get the phone number only when you request it from the user, but the user does not have to write the number, all he must do is to press a button in the conversation and the number will be sent to you.
When user clicks on /myNumber
The user has to confirm:
You will get his number
This. is the console output:
Take a look at this Simple console application, but you need to do some changes to handle the number:
In Handler.ch add the following lines to BotOnMessageReceived
if (message.Type == MessageType.Contact && message.Contact != null)
{
Console.WriteLine($"Phone number: {message.Contact.PhoneNumber}");
}
This is the piece of code needed in case the repository is deleted someday:
Program.cs
public static class Program
{
private static TelegramBotClient? bot;
public static async Task Main()
{
bot = new TelegramBotClient(/*TODO: BotToken hier*/);
User me = await bot.GetMeAsync();
Console.Title = me.Username ?? "My awesome bot";
using var cts = new CancellationTokenSource();
ReceiverOptions receiverOptions = new() { AllowedUpdates = { } };
bot.StartReceiving(Handlers.HandleUpdateAsync,
Handlers.HandleErrorAsync,
receiverOptions,
cts.Token);
Console.WriteLine($"Start listening for #{me.Username}");
Console.ReadLine();
cts.Cancel();
}
}
Handlers.cs
internal class Handlers
{
public static Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken)
{
var errorMessage = exception switch
{
ApiRequestException apiRequestException => $"Telegram API Error:\n[{apiRequestException.ErrorCode}]\n{apiRequestException.Message}",
_ => exception.ToString()
};
Console.WriteLine(errorMessage);
return Task.CompletedTask;
}
public static async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
{
var handler = update.Type switch
{
UpdateType.Message => BotOnMessageReceived(botClient, update.Message!),
_ => UnknownUpdateHandlerAsync(botClient, update)
};
try
{
await handler;
}
catch (Exception exception)
{
await HandleErrorAsync(botClient, exception, cancellationToken);
}
}
private static async Task BotOnMessageReceived(ITelegramBotClient botClient, Message message)
{
Console.WriteLine($"Receive message type: {message.Type}");
if (message.Type == MessageType.Contact && message.Contact != null)
{
// TODO: save the number...
Console.WriteLine($"Phone number: {message.Contact.PhoneNumber}");
}
if (message.Type != MessageType.Text)
return;
var action = message.Text!.Split(' ')[0] switch
{
"/myNumber" => RequestContactAndLocation(botClient, message),
_ => Usage(botClient, message)
};
Message sentMessage = await action;
Console.WriteLine($"The message was sent with id: {sentMessage.MessageId}");
static async Task<Message> RequestContactAndLocation(ITelegramBotClient botClient, Message message)
{
ReplyKeyboardMarkup requestReplyKeyboard = new(
new[]
{
// KeyboardButton.WithRequestLocation("Location"), // this for the location if you need it
KeyboardButton.WithRequestContact("Send my phone Number"),
});
return await botClient.SendTextMessageAsync(chatId: message.Chat.Id,
text: "Could you please send your phone number?",
replyMarkup: requestReplyKeyboard);
}
static async Task<Message> Usage(ITelegramBotClient botClient, Message message)
{
const string usage = "/myNumber - to send your phone number";
return await botClient.SendTextMessageAsync(chatId: message.Chat.Id,
text: usage,
replyMarkup: new ReplyKeyboardRemove());
}
}
private static Task UnknownUpdateHandlerAsync(ITelegramBotClient botClient, Update update)
{
Console.WriteLine($"Unknown update type: {update.Type}");
return Task.CompletedTask;
}
}

limit # of web service requests simultaneously

I have an Excel Add-In written in C#, .NET 4.5. It will send many web service requests to a web server to get data. E.g. it sends 30,000 requests to web service server. When data of a request comes back, the addin will plot the data in Excel.
Originally I did all the requests asynchronously, but sometime I will get OutOfMemoryException
So I changed, sent the requests one by one, but it is too slow, takes long time to finish all requests.
I wonder if there is a way that I can do 100 requests at a time asynchronously, once the data of all the 100 requests come back and plot in Excel, then send the next 100 requests.
Thanks
Edit
On my addin, there is a ribbon button "Refresh", when it is clicked, refresh process starts.
On main UI thread, ribbon/button is clicked, it will call web service BuildMetaData,
once it is returned back, in its callback MetaDataCompleteCallback, another web service call is sent
Once it is returned back, in its callback DataRequestJobFinished, it will call plot to plot data on Excel. see below
RefreshBtn_Click()
{
if (cells == null) return;
Range firstOccurence = null;
firstOccurence = cells.Find(functionPattern, null,
null, null,
XlSearchOrder.xlByRows,
XlSearchDirection.xlNext,
null, null, null);
DataRequest request = null;
_reportObj = null;
Range currentOccurence = null;
while (!Helper.RefreshCancelled)
{
if(firstOccurence == null ||IsRangeEqual(firstOccurence, currentOccurence)) break;
found = true;
currentOccurence = cells.FindNext(currentOccurence ?? firstOccurence);
try
{
var excelFormulaCell = new ExcelFormulaCell(currentOccurence);
if (excelFormulaCell.HasValidFormulaCell)
{
request = new DataRequest(_unityContainer, XLApp, excelFormulaCell);
request.IsRefreshClicked = true;
request.Workbook = Workbook;
request.Worksheets = Worksheets;
_reportObj = new ReportBuilder(_unityContainer, XLApp, request, index, false);
_reportObj.ParseParameters();
_reportObj.GenerateReport();
//this is necessary b/c error message is wrapped in valid object DataResponse
//if (!string.IsNullOrEmpty(_reportObj.ErrorMessage)) //Clear previous error message
{
ErrorMessage = _reportObj.ErrorMessage;
Errors.Add(ErrorMessage);
AddCommentToCell(_reportObj);
Errors.Remove(ErrorMessage);
}
}
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
Errors.Add(ErrorMessage);
_reportObj.ErrorMessage = ErrorMessage;
AddCommentToCell(_reportObj);
Errors.Remove(ErrorMessage);
Helper.LogError(ex);
}
}
}
on Class to GenerateReport
public void GenerateReport()
{
Request.ParseFunction();
Request.MetacompleteCallBack = MetaDataCompleteCallback;
Request.BuildMetaData();
}
public void MetaDataCompleteCallback(int id)
{
try
{
if (Request.IsRequestCancelled)
{
Request.FormulaCell.Dispose();
return;
}
ErrorMessage = Request.ErrorMessage;
if (string.IsNullOrEmpty(Request.ErrorMessage))
{
_queryJob = new DataQueryJob(UnityContainer, Request.BuildQueryString(), DataRequestJobFinished, Request);
}
else
{
ModifyCommentOnFormulaCellPublishRefreshEvent();
}
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
ModifyCommentOnFormulaCellPublishRefreshEvent();
}
finally
{
Request.MetacompleteCallBack = null;
}
}
public void DataRequestJobFinished(DataRequestResponse response)
{
Dispatcher.Invoke(new Action<DataRequestResponse>(DataRequestJobFinishedUI), response);
}
public void DataRequestJobFinished(DataRequestResponse response)
{
try
{
if (Request.IsRequestCancelled)
{
return;
}
if (response.status != Status.COMPLETE)
{
ErrorMessage = ManipulateStatusMsg(response);
}
else // COMPLETE
{
var tmpReq = Request as DataRequest;
if (tmpReq == null) return;
new VerticalTemplate(tmpReq, response).Plot();
}
}
catch (Exception e)
{
ErrorMessage = e.Message;
Helper.LogError(e);
}
finally
{
//if (token != null)
// this.UnityContainer.Resolve<IEventAggregator>().GetEvent<DataQueryJobComplete>().Unsubscribe(token);
ModifyCommentOnFormulaCellPublishRefreshEvent();
Request.FormulaCell.Dispose();
}
}
on plot class
public void Plot()
{
...
attributeRange.Value2 = headerArray;
DataRange.Value2 = ....
DataRange.NumberFormat = ...
}
OutOfMemoryException is not about the too many requests sent simultaneously. It is about freeing your resources right way. In my practice there are two main problems when you are getting such exception:
Wrong working with immutable structures or System.String class
Not disposing your disposable resources, especially graphic objects and WCF requests.
In case of reporting, for my opinion, you got a second one type of a problem. DataRequest and DataRequestResponse are good point to start the investigation for the such objects.
If this doesn't help, try to use the Tasks library with async/await pattern, you can find good examples here:
// Signature specifies Task<TResult>
async Task<int> TaskOfTResult_MethodAsync()
{
int hours;
// . . .
// Return statement specifies an integer result.
return hours;
}
// Calls to TaskOfTResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();
// Signature specifies Task
async Task Task_MethodAsync()
{
// . . .
// The method has no return statement.
}
// Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync();
await returnedTask;
// or, in a single statement
await Task_MethodAsync();
In your code I see a while loop, in which you can store your Task[] of size of 100, for which you can use the WaitAll method, and the problem should be solved. Sorry, but your code is huge enough, and I can't provide you a more straight example.
I'm having a lot of trouble parsing your code to figure out is being iterated for your request but the basic template for batching asynchronously is going to be something like this:
static const int batchSize = 100;
public async Task<IEnumerable<Results>> GetDataInBatches(IEnumerable<RequestParameters> parameters) {
if(!parameters.Any())
return Enumerable.Empty<Result>();
var batchResults = await Task.WhenAll(parameters.Take(batchSize).Select(doQuery));
return batchResults.Concat(await GetDataInBatches(parameters.Skip(batchSize));
}
where doQuery is something with the signature
Task<Results> async doQuery(RequestParameters parameters) {
//.. however you do the query
}
I wouldn't use this for a million requests since its recursive, but your case should would generate a callstack only 300 deep so you'll be fine.
Note that this also assumes that your data request stuff is done asynchronously and returns a Task. Most libraries have been updated to do this (look for methods with the Async suffix). If it doesn't expose that api you might want to create a separate question for how to specifically get your library to play nice with the TPL.

Categories

Resources