Im making a discord bot with JDA and I want to know how to wait for a message. Something like this
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
public class Listener extends ListenerAdapter {
public void onGuildMessageReceived(GuildMessageReceivedEvent event) {
String message = event.getMessage().getContentRaw();
boolean isBot = event.getAuthor().isBot();
// Check if message is not from a bot
if (!isBot) {
if (message.equalsIgnoreCase("hi")) {
event.getChannel().sendMessage("Hello, what's your name?").queue();
// Wait for second message
String name = event.getMessage().getContentRaw(); // Saving content from second message
event.getChannel().sendMessage("Your name is: " + name).queue();
}
}
}
}
User: Hi
Bot: Hello, what's your name?
User: sends name
Bot: Your name is: name
How do i get the second message?
As already told by Minn in the comment. You could use that method given in the thread. However, I'd recommend using the JDA-Utilities EventWaiter.
If you're using Maven or Gradle, use either one of these:
<!--- Place this in your repositories block --->
<repository>
<id>central</id>
<name>bintray</name>
<url>http://jcenter.bintray.com</url>
</repository>
<!--- Place this in your dependencies block --->
<dependency>
<groupId>com.jagrosh</groupId>
<artifactId>jda-utilities</artifactId>
<version>JDA-UTILITIES-VERSION</version> <!--- This will be the latest JDA-Utilities version. Currently: 3.0.4 --->
<scope>compile</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId> <!--- This will be your JDA Version --->
<version>JDA-VERSION</version>
</dependency>
For gradle it's much easier:
# Add this to the dependencies (What's inside this block, not the block itself.
dependencies {
compile 'com.jagrosh:jda-utilities:JDA-UTILITIES-VERSION'
compile 'net.dv8tion:JDA:JDA-VERSION'
}
# Same story for the repo's
repositories {
jcenter()
}
When this has loaded in, I'd recommend checking the ExampleBot main class. for adding your own stuff to it. Since I will only explain the basics of getting the EventWaiter to work.
This will be the main class of the bot to start with, when you run this, it will start the bot with these commands. ShutdownCommand and PingCommand are a part of the Utilities. So these will be added automaticly.
public static void main(String[] args) throws IOException, LoginException, IllegalArgumentException, RateLimitedException
{
// the first is the bot token
String token = "The token of your bot"
// the second is the bot's owner's id
String ownerId = "The ID of your user account"
// define an eventwaiter, dont forget to add this to the JDABuilder!
EventWaiter waiter = new EventWaiter();
// define a command client
CommandClientBuilder client = new CommandClientBuilder();
// sets the owner of the bot
client.setOwnerId(ownerId);
// sets the bot prefix
client.setPrefix("!!");
// adds commands
client.addCommands(
// command to say hello
new HelloCommand(waiter),
// command to check bot latency
new PingCommand(),
// command to shut off the bot
new ShutdownCommand());
// start getting a bot account set up
new JDABuilder(AccountType.BOT)
// set the token
.setToken(token)
// set the game for when the bot is loading
.setStatus(OnlineStatus.DO_NOT_DISTURB)
.setActivity(Activity.playing("loading..."))
// add the listeners
.addEventListeners(waiter, client.build())
// start it up!
.build();
}
Now, for the HelloCommand. You start by making a new class called "HelloCommand". In this class you will extend the "Command" portion of the utilities.
public class HelloCommand extends Command
{
private final EventWaiter waiter; // This variable is used to define the waiter, and call it from anywhere in this class.
public HelloCommand(EventWaiter waiter)
{
this.waiter = waiter; // Define the waiter
this.name = "hello"; // The command
this.aliases = new String[]{"hi"}; // Any aliases about the command
this.help = "says hello and waits for a response"; // Description of the command
}
#Override
protected void execute(CommandEvent event)
{
// ask what the user's name is
event.reply("Hello. What is your name?"); // Respond to the command with a message.
// wait for a response
waiter.waitForEvent(MessageReceivedEvent.class,
// make sure it's by the same user, and in the same channel, and for safety, a different message
e -> e.getAuthor().equals(event.getAuthor())
&& e.getChannel().equals(event.getChannel())
&& !e.getMessage().equals(event.getMessage()),
// respond, inserting the name they listed into the response
e -> event.reply("Hello, `"+e.getMessage().getContentRaw()+"`! I'm `"+e.getJDA().getSelfUser().getName()+"`!"),
// if the user takes more than a minute, time out
1, TimeUnit.MINUTES, () -> event.reply("Sorry, you took too long."));
}
}
Now, this is where we get to the jucy stuff. When using the event waiter. There are a few area's you need to check for.
// wait for a response
waiter.waitForEvent(MessageReceivedEvent.class,
// make sure it's by the same user, and in the same channel, and for safety, a different message
e -> e.getAuthor().equals(event.getAuthor())
&& e.getChannel().equals(event.getChannel())
&& !e.getMessage().equals(event.getMessage()),
// respond, inserting the name they listed into the response
e -> event.reply("Hello, `"+e.getMessage().getContentRaw()+"`! I'm `"+e.getJDA().getSelfUser().getName()+"`!"),
// if the user takes more than a minute, time out
1, TimeUnit.MINUTES, () -> event.reply("Sorry, you took too long."));
Explained in more detail:
waiter.waitForEvent(GuildMessageReceivedEvent.class, // What event do you want to wait for?
p -> p.getAuthor().equals(message.getAuthor()) && p.getChannel().equals(message.getChannel()), // This is important to get correct, check if the user is the same as the one who executed the command. Otherwise you'll get a user who isn't the one who executed it and the waiter responds to it.
run -> {
// Run whatever stuff you want here.
}, 1, // How long should it wait for an X amount of Y?
TimeUnit.MINUTES, // Y == TimeUnit.<TIME> (Depending on the use case, a different time may be needed)
() -> message.getChannel().sendMessage("You took longer then a minute to respond. So I've reverted this message.").queue()); // If the user never responds. Do this.
This should be the BASICS to using the EventWaiter. This may depend on your use case. And it can be done by only using the eventwaiter class as well.
Related
I am using Java Spring Boot and OptaPlanner to generate a timetable with almost 20 constraints. At the initial generation, everything works fine. The score showed by the OptaPlanner logging messages matches the solution received, but when I want to resume the generation, the solution contains a lot of problems (like the constraints are not respected anymore) although the generation starts from where it has stopped and it continues initializing or finding a best solution.
My project is divided into two microservices: one that communicates with the UI and keeps the database, and the other receives data from the first when a request for starting/resuming the generation is done and generates the schedule using OptaPlanner. I use the same request for starting/resuming the generation.
This is how my project works: the UI makes the requests for starting, resuming, stopping the generation and getting the timetable. These requests are handled by the first microservice, which uses WebClient to send new requests to the second microservice. Here, the timetable will be generated after asking for some data from the database.
Here is the method for starting/resuming the generation from the second microservice:
#PostMapping("startSolver")
public ResponseEntity<?> startSolver(#PathVariable String organizationId) {
try {
SolverConfig solverConfig = SolverConfig.createFromXmlResource("solver/timeTableSolverConfig.xml");
SolverFactory<TimeTable> solverFactory = new DefaultSolverFactory<>(solverConfig);
this.solverManager = SolverManager.create(solverFactory);
this.solverManager.solveAndListen(TimeTableService.SINGLETON_TIME_TABLE_ID,
id -> timeTableService.findById(id, UUID.fromString(organizationId)),
timeTable -> timeTableService.updateModifiedLessons(timeTable, organizationId));
return new ResponseEntity<>("Solving has successfully started", HttpStatus.OK);
} catch(OptaPlannerException exception) {
System.out.println("OptaPlanner exception - " + exception.getMessage());
return utils.generateResponse(exception.getMessage(), HttpStatus.CONFLICT);
}
}
-> findById(...) method make a request to the first microservice, expecting to receive all data needed by constraints for generation (lists of planning entities, planning variables and all other useful data)
public TimeTable findById(Long id, UUID organizationId) {
SolverDataDTO solverDataDTO = webClient.get()
.uri("http://localhost:8080/smart-planner/org/{organizationId}/optaplanner-solver/getSolverData",
organizationId)
.retrieve()
.onStatus(HttpStatus::isError, error -> {
LOGGER.error(extractExceptionMessage("findById.fetchFails", "findById()"));
return Mono.error(new OptaPlannerException(
extractExceptionMessage("findById.fetchFails", "")));
})
.bodyToMono(SolverDataDTO.class)
.block();
TimeTable timeTable = new TimeTable();
/.. populating all lists from TimeTable with the one received in solverDataDTO ../
return timeTable;
}
-> updateModifiedLessons(...) method send to the first microservice the list of all generated planning entities with the corresponding planning variables assigned
public void updateModifiedLessons(TimeTable timeTable, String organizationId) {
List<ScheduleSlot> slots = new ArrayList<>(timeTable.getScheduleSlotList());
List<SolverScheduleSlotDTO> solverScheduleSlotDTOs =
scheduleSlotConverter.convertModelsToSolverDTOs(slots);
String executionMessage = webClient.post()
.uri("http://localhost:8080/smart-planner/org/{organizationId}/optaplanner-solver/saveTimeTable",
organizationId)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.body(Mono.just(solverScheduleSlotDTOs), SolverScheduleSlotDTO.class)
.retrieve()
.onStatus(HttpStatus::isError, error -> {
LOGGER.error(extractExceptionMessage("saveSlots.savingFails", "updateModifiedLessons()"));
return Mono.error(new OptaPlannerException(
extractExceptionMessage("saveSlots.savingFails", "")));
})
.bodyToMono(String.class)
.block();
}
I would probably start by making sure that the solution you save to the DB after the first run of startSolver() is the same (in terms of Java equality), including the assignments of planning variables to values, as the solution you retrieve via findById() at the beginning of the second run.
I am trying to update a document in MongoDB but cannot get to checking updated status and responding back to user. Below is my code:
#Autowired
ReactiveMongoTemplate mongoTemplate;
public Mono<String> updateUser(UserIn userIn) {
UserResponse resp = new UserResponse();
mongoTemplate.findAndModify(query, update, User.class)
//.doOnSuccess(bsItem -> {
.flatMap(user -> {
if(user.getItemId().equals(userIn.getId("_id")))
resp.setStatus("Updated");
else
resp.setStatus("Failed");
return Mono.just(resp);
}).subscribe();
return Mono.just(resp.getStatus());
}
Even though update is happening in mongodb, it throws NPE while returning. How to get the control after reactor operator is executed here?
You should almost never subscribe in your own application.
The subscriber is the client that initiated the call in this case it is probably the web application. You application is just relaying the data, so your application is a publisher which means you should not subscribe. The web app subscribes.
Try this.
#Autowired
ReactiveMongoTemplate mongoTemplate;
public Mono<String> updateUser(UserIn userIn) {
return mongoTemplate.findAndModify(query, update, User.class)
.flatMap(user -> {
final UserResponse resp = new UserResponse();
if(user.getItemId().equals(userIn.getId("_id")))
resp.setStatus("Updated");
else
resp.setStatus("Failed");
return Mono.just(resp.getStatus());
});
}
A mono is not like a stream, you fetch, map and return, all in the same mono, like a chain of events. An event chain.
I am trying to make a call out from salesforce most of the code is copied from another working package.
Can anyone tell me why the call out method below is never run?
I am saving into my custom table before and after the call out method is called but the saving into my custom table does not get called with in the call out method.
public class AutoSyncConnector {
public AutoSyncConnector()
{
}
public void Fire(string jsonToPost)
{
// 1. Authentication send the current session id so that request can be validated
String sessionId = UserInfo.getSessionId();
// 2. warp the request and post it to the configured end point
// This is how to get settings out for custom settings list
String connectorUrl = ASEndPoints__c.getValues('MainUrlEndPoint').autosync__MainSiteUrl__c;
CastlesMessageLog__c cd = new CastlesMessageLog__c();
cd.SentJson__c = 'before call out this is called';
insert cd;
AutoSyncConnector.CallOut(jsonToPost, connectorUrl);
CastlesMessageLog__c cd2 = new CastlesMessageLog__c();
cd2.SentJson__c = 'after call out this is called';
insert cd2;
}
public static void CallOut(String jsonToPost, String connectorUrl)
{
MakeCallout(jsonToPost,connectorUrl);
}
#future(callout=true)
public static void MakeCallout(String jsonToPost, String connectorUrl){
CastlesMessageLog__c cd = new CastlesMessageLog__c();
cd.SentJson__c = 'start inside before call out this is never called';
insert cd;
Http h = new Http();
HttpRequest req = new HttpRequest();
req.setTimeout(120000);
// string authorizationHeader = 'Check I can add stuff to the header';
String sfdcConnectorUrl = connectorUrl + '/api/autosyncwebhook';
req.setEndpoint(sfdcConnectorUrl);
//req.setHeader('Authorization', authorizationHeader);
req.setMethod('POST');
req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
req.setBody(jsonToPost);
h.send(req);
CastlesMessageLog__c cd2 = new CastlesMessageLog__c();
cd2.SentJson__c = 'end inside before call out this is never called';
insert cd2;
}
}
Go to Setup -> Monitoring -> Apex jobs. My gut feel is that you'll see lots of "uncommitted work pending" errors in there.
When you make any DML (insert/update/delete) you open a transaction with the database. If the next thing you'll do is a callout (which can have max timeout time 120 seconds) it'll mean you hold a lock on this record (or even whole table) for very long time. SF has no way of knowing whether the call will succeed or will have to be rolled back. So they protect the situation by banning such code right away ;)
Make callout first, then your DML.
Or make DML, call #future (that's the purpose, to switch to another thread, separate the contexts) and if the callout comes back with error - do whatever cleanup you'd consider a rollback (delete the record? update it to status = Sync failed? Send email to user / insert for him a Task to retry later?)
Is it possible to create a plugin that has access to both intent and message at the same time in botframework? I'm specifically interested in creating a plugin that can send the message that was received as well as the intent that was registered and used to outside analytics.
Yes, you can intercept and log messages via custom middleware.
The following code sample shows how to intercept messages that are exchanged between user and bot using the concept of middleware in the Bot Builder SDK for .NET.
First, create a DebugActivityLogger class and define a LogAsync method to specify what action is taken for each intercepted message. This example just prints some information about each message.
public class DebugActivityLogger : IActivityLogger
{
public async Task LogAsync(IActivity activity)
{
Debug.WriteLine($"From:{activity.From.Id} - To:{activity.Recipient.Id} - Message:{activity.AsMessageActivity()?.Text}");
}
}
Then, add the following code to Global.asax.cs. Every message that is exchanged between user and bot (in either direction) will now trigger the LogAsync method in the DebugActivityLogger class.
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
var builder = new ContainerBuilder();
builder.RegisterType<DebugActivityLogger>().AsImplementedInterfaces().InstancePerDependency();
builder.Update(Conversation.Container);
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
For a complete middleware code example, see: https://github.com/Microsoft/BotBuilder-Samples/tree/master/CSharp/core-Middleware
I'm building a Silverlight Web Resource which is intended to integrate into a form, and it needs to know all of the following information:
id of the current user
id of teams current user belongs to
id of current user's security roles
I'm working in an early-bound kind of way, added a Service Reference to the OData endpoint (http://server/org/XRMservices/2011/OrganizationData.svc) which in turn provides me with the context (let's name it cmtestcontext, which is its actual name in code).
I access data through this class (I didn't create it, I just googled it out of the net some time ago: this is a stripped-down, keep-it-short version)
public class QueryInterface
{
//NOTE: ServiceReference1 is the name of the OData service reference
//Add Service Reference -> point to CRM OData url
public ServiceReference1.cmtextcontext CrmContext;
public QueryInterface()
{
var crmServerUrl = (string)GetContext().Invoke("getServerUrl");
if (crmServerUrl.EndsWith("/")) crmServerUrl = crmServerUrl.Substring(0, crmServerUrl.Length - 1);
Uri ODataUri = new Uri(crmServerUrl + "/xrmservices/2011/organizationdata.svc/", UriKind.Absolute);
CrmContext = new cmtestContext(ODataUri) { IgnoreMissingProperties = true };
}
}
The class allows me to sort of fetch in one line, as follows (actual code snippet enveloped in a dummy method to make it copy-pastable):
void RetrieveAllInformationFromCRM()
{
QueryInterface qi = new QueryInterface();
List<Guid> allData = new List<Guid>();
//NOTE: STEP 1 - USER ID
//NOTE: Since this is a web resource, I can cheat and use Xrm.Page.context.getUserId()
//NOTE: Remove the extra '{}' from the result for it to be parsed!
allData.Add(new Guid(qi.GetContext().Invoke("getUserId").ToString().Substring(1,36)));
//NOTE: STEP 2a - TEAM MEMBERSHIP FOR USER
//NOTE: TeamMembership entity links users to teams in a N:N relationship
qi.crmContext.TeamMembershipSet.BeginExecute(new AsyncCallback((result) =>
{
var teamMemberships = qi.crmContext.TeamMembershipSet.EndExecute(result)
.Where(tm => tm.TeamId.HasValue && (tm.SystemUserId ?? Guid.Empty) == userId)
.Select(tm => tm.TeamId.Value);
//NOTE: STEP 2b - TEAMS RELATED TO TEAMMEMBERSHIPS
qi.crmContext.TeamSet.BeginExecute(new AsyncCallback((result2) =>
{
var teamDetails = qi.crmContext.TeamSet.EndExecute(result2)
.Where(t => teamMemberships.Contains(t.TeamId));
foreach (var team in teamDetails)
allData.Add(team.TeamId);
//NOTE: FINAL STEP - allData is filled and ready to be used.
}), null);
}), null);
}
In the code above, my FINAL STEP picks up allData and processes it, and the flow goes on. My concern is, if/when I'll need to modify this "reader" method I'll have to cut and paste the "final" code around to ensure it's placed after all the reads. I'd like it way better if I could just make the reads follow one another, so I could do this:
void MyReaderMethod()
{
ReadEverything();
ProcessData();
}
Basically, can you just wait for a request to finish ? Hanging UI is a non-issue, I'd just wrap the code in a BackgroundWorker along with a "Please Wait" splash.
The nicest (IMO) is to convert the Async method calls (a requirement of Silverlight) into Task based calls.
With tasks you can easily seperate the query from the result action.
Then using the Async BCL's (via nuget) you can use async/await (if you are not using VS2012, then Tasks are still nicer to work with, you will just have to use continuations)
This example is for late bound, but you can modify it for your needs
public Task<OrganizationResponse> ExecuteAsync(OrganizationRequest request)
{
return Task.Factory.FromAsync<OrganizationResponse>(
(callback, state) => Begin(() => service.BeginExecute(request, callback, state)),
service.EndExecute,
null);
}
Then you can use it like
async void MyReaderMethod()
{
//TODO:wrap in try/catch
var result = await ExecuteAsync( ... );
ProcessData(result);
}
Or for VS 2010
void MyReaderMethod()
{
ExecuteAsync( ... ).ContinueWith(task =>{
//TODO: Error handling
ProcessData(task.Result);
});
}