JDA Discord bot, read last message of textchannel - java

I want that my Discord jda bot reads the last message of an textchannel after starting.
So I call :
textChannel.getHistory().getMessageById(config.getLatestMessageId());
The MessageHistory class doesn't have an getLatestMessage method somehow.
And for some reason textChannel.getHistory() is always empty and therefore always return null.
is there an other way to read messages (written before the bot was started).
Some aditional information:
The textchannel is the correct textchannel.
it is not empty, and I also tried writing new messages while the bot is active.
But the messagehistory is always empty.
Also something I find weird:
textchannel.gethistory().isempty() is true
and textchannel.hasLastMessage is true as well.

EDIT:
Do the following:
channel.getHistory().retrievePast(1).queue(messages -> {
// messages (list) contains all received messages
// Access them in here
// Use for example messages.get(0) to get the received message
// (messages is of type List)
});
// DON'T access the received messages outside here
// If you use queue the received messages WON'T be available directly after the call
PREVIOUS:
If I do the following on my bot it works:
channel.getHistory().retrievePast(1).queue(messages -> {
if (messages.size() > 0) System.out.println(messages.get(0).getContentDisplay());
});
After the call succeeded and the lambda is executed, calling
channel.getHistory().getRetrievedHistory()
should return the received chat history
You can also execute the action directly and block the current thread until the message history was received by doing the following:
MessageHistory h = channel.getHistory();
h.retrievePast(1).complete();
List<Message> ml = h.getRetrievedHistory();
if (ml.size() > 0) System.out.println(ml.get(0).getContentDisplay());
but I don't recommand doing this, since it will block the current thread. Use the code in the first part of my answer instead, which won't block execution, and will fill the message history with data and once its ready it will call the lambda.
Notice that I'm passing in 1 as an argument for the call to 'retrievePast'. This will only receive the last message send inside the text channel.
I guess you can't receive the entire text channel, since it would be to expensive to store all the sent data in the RAM or it would just take to long.

Related

Discord4J issue: sending messages in a loop instead

Below is the code I used for a test command in Discord4J
gateway.on(MessageCreateEvent.class)
.map(MessageCreateEvent::getMessage)
.filter(message -> message.getContent().contains("test"))
.flatMap(Message::getChannel)
.flatMap(channel -> channel.createMessage("test!"))
.subscribe();
but now if you type "test" in discord, it keeps sending messages, instead of a single message like I would expect. What's the issue here? It does work with equals() and equalsignorecase(), but with all other String checking methods it goes into a loop.
This is because you receive a MessageCreateEvent for all messages you send with the bot as well. Since your message is createMessage("test!"), you will also receive a message where getContent() returns "test!". This will obviously result in "test!".contains("test") but not "test!".equals("test").
This can be prevented by checking if the message author is a bot.
filter(message -> !message.getAuthor().map(User::isBot).orElse(true))

Java/Quarkus Kafka Streams Reading/Writing to Same Topic based on a condition

Hello I have this issue that I'm trying to solve. Basically I have a Kafka Streams topology that will read JSON messages from a Kafka topic and that message gets deserialized into a POJO. Then ideally it will read check that message for a certain boolean flag. If that flag is true it will do some transformation and then write it back to the topic. However if the flag is false, I'm trying to have it not write anything but I'm not sure how I can go about it. With the MP Reactive Messaging I can just use an RxJava 2 Flowable Stream and return something like Flowable.empty() but I can't use that method here it seems.
JsonbSerde<FinancialMessage> financialMessageSerde = new JsonbSerde<>(FinancialMessage.class);
StreamsBuilder builder = new StreamsBuilder();
builder.stream(
TOPIC_NAME,
Consumed.with(Serdes.Integer(), financialMessageSerde)
)
.mapValues (
message -> checkCondition(message)
)
.to (
TOPIC_NAME,
Produced.with(Serdes.Integer(), financialMessageSerde)
);
The below is the function call logic.
public FinancialMessage checkCondition(FinancialMessage rawMessage) {
FinancialMessage receivedMessage = rawMessage;
if (receivedMessage.compliance_services) {
receivedMessage.compliance_services = false;
return receivedMessage;
}
else return null;
}
If the boolean is false it just returns a JSON body with "null".
I've tried changing the return type of the checkCondition function wrapped like
public Flowable<FinancialMessage> checkCondition (FinancialMessage rawMessage)
And then having the return from the if be like Flowable.just(receivedMessage) or Flowable.empty() but I can't seem to serialize the Flowable object. This might be a silly question but is there a better way to go about this?
Note that Kafka messages are immutable and not deleted after read, and if you read/write from the same topic with a single application, a message would be processed infinitely often (or to be more precise different copies of it) if you don't have a condition to "break" the cycle.
Also, if for example 5 services read from the same topic, all 5 services get a copy of every event. And if one service write back, all other 4 services and the writing service itself will read the message again. Thus, you get quite some data amplification.
If you have different services to react on the original input message consecutively, you could have one topic between each pair of consecutive services to really build a pipeline though.
Last, you say if the boolean flag is true you want to transform the message and emit (I assume for the next service to consumer). And for false you want to do nothing. I a further assume that for a message only a single flag will be true and a successful transformation also switches the flag (to enable processing by the next service). For this case, it's best if you can ensure that each original input message has the same initial boolean flag set to build your pipeline. Thus, only the corresponding service will read messages with its boolean flag set (you don't even need to check the boolean flag as your upstream write ensures that it's set; you could only have a sanity check).
If you don't know which boolean flag is set initially and all services read from the same input topic, just filtering out the message is correct. If all services read all messages, 4 services will filter the message while one service will process it and emit a new message with a different flag. For this architecture, a single topic might work: if a message is processed by all services and all boolean flags are false (after all services processed the message), and you write it back to the input topic, all services would drop the last copy correctly. However, using a single topic implies a lot of redundant reading/writing.
Maybe the best architecture is, to have your original input topic, and one additional input topic for each service. You also use an additional "dispatcher" service that read from the original input topics, and branches() the KStream into the service input topics according to the boolean flag. This way, each service will read only messages with the right flag set to true. Furthermore, each service will write to the input topic of the other services also using branch() after the message transformation to write it to the input topic of the correct next service. Last, you would want an output topic that each service can write into after a message is fully processed.

How to stop a method until I get the response in java?

I have written a simple TCP Server program that receives data from the client. My main() method looks like this:
Acceptor acceptor = Acceptor.open(new InetSocketAddress(port));
Session session = acceptor.accept(); //accepts connection from client and returns session object
FIXSession transport = session.getTransport();
String str = transport.receive(); // receives data from client
doWork(str); // do the remaining process.
In the above code, I am trying to receive the data from client using "transport.receive()" method. If suppose client takes some time to send the data, meanwhile my main program is going to the next step "doWork(str);" and the result is null. How can I make the main() thread to wait until I receive data from the Client. In future I may run the doWork(str) method in other thread. So I need to make the doWork(str) thread to wait until I get the data from the Client. Is there any way to do?
Quoting the OP:
Even though it returns the number of bytes, if the bytes received less than 0 then it has to wait until the received bytes are greater than 0
So, the straight forward way to turn this into a blocking call:
int numBytes = transport.receive();
while (numBytes == 0) {
Thread.sleep(SOME_TIME);
numBytes = transport.receive();
}
for example. If you want code to wait; then write code that waits. But of course, that still doesn't answer how to actually receive a message. As the idea is that you need a MessageListener to actually receive messages.

apache camel - add message alert to deadletter queue

String queueA = "rabbitmq://host:5672/queue-a.exchange?queue=queue-a.exchange..etc
from(queueA)
.routeId("idForQueueA")
.onException(Exception.class)
.maximumRedeliveries(0)
// .processRef("sendEmailAlert") * not sure this belongs here*
.to(deadLetterQueueA)
.useOriginalMessage()
.end()
.processRef("dataProcessing")
.processRef("dataExporting")
.end();
Explaining the code above:
Messages are taken from queueA. Upon various processes being successful the message is consumed. If it fails its added to the dead letter queue "deadLetterQueueA". This all works ok.
My question is
When messages arrive in the deadletter queue I want to add alerts so we know to do something about it... How could I to add an email alert when a message arrives in the dead letter queue. I dont want to lose the original message if the alert fails - nor do I want the alert to consume the message.
My thoughts are.. I would need to split the message on an exception so its sent to two different queues? One for the alert which then sends out an email alert and then consumes itself. Then one for the dead letter queue that just sites there? However I'm not sure how to do this?
You can split a message to go to multiple endpoints using a multicast (details here):
.useOriginalMessage().multicast().to(deadLetterQueueA, "smtp://username#host:port?options")
This uses the camel mail component endpoints described here. Alternatively, you can continue processing the message after the to. So something like:
.useOriginalMessage()
.to(deadLetterQueueA)
.transform().simple("Hi <name>, there has been an error on the object ${body.toString}")
.to("smtp://username#host:port?options")
If you had multiple recipients, you could use a recipients list
public class EmailListBean {
#RecipientList
public String[] emails() {
return new String[] {"smtp://joe#host:port?options",
"smtp://fred#host:port?options"};
}
}
.useOriginalMessage()
.to(deadLetterQueueA)
.transform().simple("...")
.bean(EmailListBean.class)
Be careful of using JMS queues to store messages while waiting for a human to action them. I don't know what sort of message traffic you're getting. I'm assuming if you want to send an email for every failure, it's not a lot. But I would normally be wary of this sort of thing, and chose to use logging or database persistence to store the results of errors, and only use a JMS error queue to notify other processes or consumers of the error or to schedule a re-try.
There are two ways you can do this , but based on your message volume you might not want to send email on every failed message.
You can use the solution provided by AndyN , or you can use the Advisory Topics ActiveMQ.Advisory.MessageDLQd.Queue.* , whenever a message gets in to the DLQ the enqueue count of the topic will increase by 1 . By monitoring the Queue Depth you might now be able to send a mail to based on the number of the errors that ocurred.
If you want to do it at the producer end. You can use any one of the solutions provided by AndyN

Issue with readLine() method

I am building a Chat system, where I need wait to get the User input (Sender) as well as to display the reply message (from receiver) at the same time.
So I am using a while loop for receiving and sending the messages:
while((text = inFromUser.readLine()) != null) //Msg from Sender
{
while((data_from_server=inFromServer.readLine()) != null) //Msg from receiver
{
System.out.println("Displaying Output=" + data_from_server);
System.out.println(data_from_server);
}
System.out.println("Getting Input=" + text);
outToserver.writeBytes(text + "\n");
}
My Problem is the client may send inputs again and again ,whereas the receiver may/may not send the reply back. But according to my logic, it's always expecting a input from the receiver and Vice Versa. Please suggest to fix this problem.
You're going to need more than one thread. Think about it - you have to wait until the user enters some data, and when that happens, display it immediately. You also have to wait until the server gives you some data, and display that immediately.
You can't wait for both at once; if you did, nothing would be displayed until both the user and the server had entered a line. You can't wait for one, then the other; if you did, the client couldn't read what they wrote until the server sent a message, or vice versa.
You need to wait for both at the same time, but running side-by-side. You want to perform an action as soon as either of them return something. This means you need to run a second thread. One thread waits for the user, and one thread waits for the server.

Categories

Resources