My bot that uses MS Bot Framework is stuck sending messages to the user in an infinite loop, both on the facebook and emulator channels.
My bot has a "root" IDialog, kind of like a menu, that calls a few other IDialogs depending on the user's selection. The child dialogs are called in this way:
...
else if (response.Text == MainOptions[2])
{
await context.Forward(new InfoCounterDialog(), ChildDialogComplete,
response, CancellationToken.None);
}
...
response is an IMessageActivity sent by user;
ChildDialogComplete is a method that builds the main menu again and ends with these lines:
.
await context.PostAsync(restartPrompt);
context.Wait(MainScreenSelectionReceived);
All dialogs work fine except this one very short dialog, which causes an infinite loop - the bot keeps sending this message again and again until I stop the web app.
namespace XXXX
{
[Serializable]
public class InfoCounterDialog : IDialog
{
public async Task StartAsync(IDialogContext context)
{
var hourNow = DateTime.Now.Hour;
var openNow = "";
if (hourNow >= 7)
{
openNow = "It is open now and will close at midnight.";
}
else
{
openNow = "It is closed now and will open at 7:00";
}
var card = HeroCardUtils.CardWithImageAndMiscButtons(
"Our information counter can help!",
"It's located in Shop 081, Level 3 in Building 2. " + openNow,
"http://www.[image URL here].jpg",
new[] { "More Details" },
new[] { ActionTypes.OpenUrl },
new[] { "[webpage URL here]" }
);
await BotUtils.SendCardToChat(context, card);
context.Done(this);
}
}
}
If you're wondering what SendCardToChat does:
public async static Task SendCardToChat(IDialogContext context, HeroCard card)
{
var activity = context.MakeMessage();
activity.Attachments = HeroCardUtils.CardToAttachments(card);
await context.PostAsync(activity);
}
To recap:
I'm launching a dialog from another dialog using context.Forward()
The dialog is supposed to show a message to the user and immediately terminate without extra input from user
Instead, it keeps sending the "Our information counter can help!" message infinitely.
My best guess is that the child dialog somehow returns the user's initial message to the conversation, which triggers the same dialog again and again. But this shouldn't happen, the child IDialog shouldn't send anything to the conversation except the HeroCard I created.
Or maybe I'm looking in a wrong direction and Bot Framework just doesn't support IDialogs that do something and immediately terminate without a context.Wait()?
I found that there isn't much documentation on context.Done(R value) but its really important in controlling the flow of the dialog.
I'm guessing that context.Done(this) calls your Dialog again? have you tried
context.Done(true);
I'm finding it frustrating that there isn't a context.Done() method (no parameter) to tell the dialog that your finished.
Further to this, i've found that in this Microsoft Example they use
// within a HotelsDialog.cs
context.Done<object>(null);
This is possible because they call the dialog from a root dialog
// within RootDialog.cs
context.Call(new HotelsDialog(), this.ResumeAfterOptionDialog);
Related
I have just started tinkering with discord.net for creating a bot. I have a very basic bot right now that already replies with my given response when I type the command text. So if I type, "Hello" it will reply with, "...world!"
However, what I want, is to have the bot reply with a canned response whenever a message contains a certain word at any point. So if a user types, "Well, hello there" it will still reply with, "...world!" even though the command word is in the middle of the message. I think I may be able to swing it with the .Contains() method, but I'm a little stuck.
private async Task OnMessageReceived(SocketMessage arg)
{
if (!(arg is SocketUserMessage message)) return;
if (message.Source != MessageSource.User) return;
string[] filters = { "hello" };
string content = message.Content;
bool contains = filters.Any(x => content.Split(" ").Any(y => y.Contains(x)));
if (contains)
{
var guild = _client.GetGuild((message.Channel as SocketGuildChannel).Guild.Id);
await message.Channel.SendMessageAsync($"{arg.Content} world!");
return;
}
}
This is the MessageReceived event that you can get from the DiscordSocketClient. Put this in your CommandHandler.
I have created a confirm dialog where the user can select yes/no
private async Task Confirm(IDialogContext context, IAwaitable<bool> result)
{
var res= await result;
await context.PostAsync(res? "Proceed" : "Ok then");
if (res) {
......
}
}
If the user selects Yes he will receive the message "Proceed"
At the same time (again if "res" is true), i want to send a
specific message to the bot without appearing in the conversation.
Is there a way to send a custom message back to the bot when user
press Yes?
You could try constructing a new activity using data stored in the context which you have access to in this method. I don't fully understand your scenario but it seems this may work for what you need.
var a = new Activity();
a.Conversation = context.Activity.Conversation;
a.Recipient = context.Activity.Recipient;
a.From = context.Activity.From;
a.Id = context.Activity.Id;
... //set whatever else you need set
a.Text = "Whatever you need the text to be";
//send or process the activity do what it is you are trying to accomplish
Edit: I think what you are actually looking for is Prompt.Confirm().
Ok, firstly, I'm not very advanced at this, xD
In public override void OnMessage (string message, EChatEntryType type) I want to execute a series of tests to figure out which response to send.
The first test is to see if the message starts with a command. I'm an admin for a CSGO server group called "Warmup Servers" and this will be a string of parts which will generate and feed back information into a separate log file.
So I'm working on a custom UserHandler.cs for my steambot. I plan on writing a simple script, so that I can do exec [botname] TGTOnline & exec [botname] TGTOffline to tell the bot if using its account. (Like playing games or something on that account).
To test if the message starts with any of the predefined responses. (More to be added later)
To test if the bot has sent a message before. I don't want someone to start a conversation with me and the bot to be sending its default response every time it recieves a message, it would be annoying for both parties.
So before the default message is called, I need to have a function which checks to see if the bot account (Both bot response or human response) has sent a message to the user recently (Last 5 Minutes) and if it has, tell the bot to do nothing...
However I have no clue on where to start with this script and would love some advice!
Here is my TGTUserHandler.cs section which relates:
using System;
using SteamKit2;
using System.Threading.Tasks;
using System.Collections.Generic;
using SteamTrade;
using SteamTrade.TradeOffer;
using SteamTrade.TradeWebAPI;
namespace SteamBot
{
public class TGTUserHandler : UserHandler
{
private bool TGTOnline = false;
private TaskCompletionSource<string> _UserReport;
public TGTUserHandler(Bot bot, SteamID sid) : base(bot, sid) { }
private bool HandleUserReport(string message)
{
if (_UserReport == null)
return false;
//to be scripted in the morning
_UserReport = null;
return true;
}
public override void OnMessage(string message, EChatEntryType type)
{
if (message.StartsWith("!"))
{
if (message.StartsWith("!help"))
{
SendChatMessage("Welcome to the TGTGaming Automated Bots, We have very limited commands and currently do not do any type of automated trade. Here is the list of current commands you can use:");
SendChatMessage("!WarmupServers [help | Report | Issue] | This command is used for all issues related to the WarmupServer's. Do `!Warmupservers help` for more information");
SendChatMessage("!getadmin | This command is used to contact a TGTGaming administrator. Please do `!getadmin help` for more information");
}
if (message.StartsWith("!WarmupServers"))
{
if (message.StartsWith("!WarmupServers help"))
{
SendChatMessage("Warmup Servers Automated Support by TGTGamer");
SendChatMessage("Commands:");
SendChatMessage("!WarmupServers Report | Used to report a user for specific reasons. This is step by step process");
SendChatMessage("!WarmupServers issue | Used to report a issue with the servers. This is a step by step process");
}
if (message.StartsWith("!WarmupServers Report"))
{
GetUserResponse("You have started the user reporting process, please confirm this is what you wanted to do. [y/n]");
if (message.StartsWith("y"))
{
SendChatMessage("A few quick questions first, please respond yes or no");
GetUserResponse("Are you still in the server?");
if (message.StartsWith("y"))
{
GetUserResponse("Is the User you wish to report still in your server?");
if (message.StartsWith("y"))
{
GetUserResponse("Is there a VIP in the server?");
if (message.StartsWith("y"))
{
SendChatMessage("Please ask the VIP to do a !startvote on this user with your reason.");
}
else
{
_UserReport = new TaskCompletionSource<string>();
return _UserReport.Task;
}
}
else
{
_UserReport = new TaskCompletionSource<string>();
return _UserReport.Task;
}
}
else
{
_UserReport = new TaskCompletionSource<string>();
return _UserReport.Task;
}
}
else
{
SendChatMessage("You have cancelled this action");
}
}
}
}
else
{
if (TGTOnline)
{
//general Responses
if (message.StartsWith("Hello"))
{
SendChatMessage("What's up?");
}
if (message.StartsWith("Heya"))
{
SendChatMessage("Hello");
}
if (message.StartsWith("Hi"))
{
SendChatMessage("Heya");
}
else
{
//defualt response
SendChatMessage(Bot.ChatResponse);
}
}
else
{
//defualt response
SendChatMessage(Bot.ChatResponse);
}
}
}
}
}
All and any help will be appreciated.
Kind regards.
TGT
External Links you may find useful:
Bot Github: https://github.com/Jessecar96/SteamBot/
UserHandler.cs: https://github.com/Jessecar96/SteamBot/blob/master/SteamBot/UserHandler.cs
Footnotes:
Steam Subscriber Agreement:
The steam Subscriber Agreement States the following:
You may not use Cheats, automation software (bots), mods, hacks, or
any other unauthorized third-party software, to modify or automate any
Subscription Marketplace process.
As I am using this bot to automate 3rd party server actions (Rcon Kick's, Server restarts & such) it is within the steam subscriber agreement acceptance for bots. If I then started to sell or buy automatically with this bot, it would breach the subscriber agreement.
I'm using Discord.Net in C#, making a bot. My bot works fantastic so far, but I want it to automatically assign users a specific role when they join a specific server. I've never actually learned any C#, only a bit of C++ so I know the basic Grammar. How would I go about this?
I'm assuming I would use UserJoined, but doing this heeds results telling me to use it before or after a += or -+ (Which I understand, but I don't understand it's usefullness in this given scenario)
You gave little information to work with but here is how to do it in all releases (so far):
This is IN the dependency map but below the "handlecommand", CommandHandleAsync or HandleCommandAsync:
client.UserJoined += AnnounceJoinedUser; //Hook into the UserJoined event of the client.
This is under the dependency map:
public async Task AnnounceJoinedUser(SocketGuildUser user) //Welcomes the new user
{
var channel = client.GetChannel(/*/TextChannelID/*/) as SocketTextChannel; // Gets the channel to send the message in
await channel.SendMessageAsync($"Welcome {user.mention} to {channel.Guild.Name}"); //Welcomes the new user
}
In case any of you wanted to send a message directly to the joining user
client.UserJoined += HandleUserJoinedAsync;
private async Task HandleUserJoinedAsync(SocketGuildUser gUser)
{
if (gUser.IsBot || gUser.IsWebhook) return;
var dmChannel = await gUser.GetOrCreateDMChannelAsync();
await dmChannel.SendMessageAsync("Witaj");
}
For all those who need an answer, in this period, I leave you this piece of code, just to send a message to a user's join, (1 line):
Client.UserJoined += join;
private async Task join(SocketGuildUser user)
{
await (user.Guild.DefaultChannel).SendMessageAsync("Text")
return;
}
Following examples here in Stack Overflow, I put together a MessageDialog to show my user error messages.
In the emulator, it works fine.
On the phone, it blows right through, flashes the MessageDialog on the screen for only a moment, and even blows through a Task.Delay I put in as a workaround.
Would somebody please explain to me what's happening, or point me in the right direction?
p.s. I also tried a ContentDialog per an article here. THAT doesn't even display the message text.
Here's a code snippet:
public static async void ShowAndGo (String MessCode, String MessText, Boolean Xit)
{
String Mess = ""; // Start out with an empty Message to tell Joe User.
String Title = ""; // And an empty title too.
if (MessCode != "") // If we're sent a Message "Code,"
Mess = App.ResLdr.GetString (MessCode) + Cx.ld + Cx.ld; // turn it into text, culturally-aware.
Mess += MessText; // Stick MessText onto the end of it.
if (Xit)
Title = App.ResLdr.GetString ("OhSnap"); // If we're goin' down, curse a little.
else
Title = App.ResLdr.GetString ("NoProb"); // If it's just informational, no problem-o.
MessageDialog messageDialog = new MessageDialog (Mess, Title);
await messageDialog.ShowAsync (); // IT FREAKING ISN'T STOPPING HERE!!!
Task.Delay (10000).Wait (); // Wait 10 seconds with error message on the screen.
// AND IT FREAKING DOESN'T STOP HERE EITHER!!!
}
The reason of your problem is simple - you are declaring async void method - avoid that, this should be used only in special cases, for example events. In the code you have, your program doesn't stop on line where you invoke the method:
ShowAndGo("Message code", "Message Text", false);
Debug.WriteLine("Something happening");
It probably shows a message, but how long it will survive it depends on your further code. The remedy for this is to change the method from void to Task and await:
public static async Task ShowAndGo (String MessCode, String MessText, Boolean Xit)
{ /* method */ }
//invoke:
await ShowAndGo("Message code", "Message Text", false);
Debug.WriteLine("Something happening"); // now it should wait till user clicks OK
Of course that requires async all the way, but probably that's how your program should look like.