Discord JDA Bot sending messages infinitely - java

I'm trying to send messages using the Discord JDA API, however whenever I send one, it sends it infinitely.
JDA Version: 4.2.1_255
What I've Tried:
Research the issue
Use GuildMessageReceivedEvent instead of MessageReceivedEvent
Pseudo-Code:
Guild server = e.getGuild();
Role role = server.getRolesByName("Java", false).get(0);
System.out.println(role);
for(Member members : server.getMembers()) {
if(members.getRoles().contains(role)) {
sendPrivateMessage(members.getUser(), "Hello <#!" + members.getId() + ">"); // Sends infinitely
}
}
sendPrivateMessage():
public void sendPrivateMessage(User user, String content) {
user.openPrivateChannel()
.flatMap(channel -> channel.sendMessage(content))
.queue();
}

You can try this
public void sendPrivateMessage(User user, String content) {
if (user.isBot()) return;
user.openPrivateChannel()
.flatMap(channel -> channel.sendMessage(content))
.queue();
}

if this code gets executed in the MessageReceivedEvent you may try adding
if(event.getAuthor().isBot()){
return;
}
so that it wont reply itself if you haven't already.
(If you want it to answer to other bots, you can also use the JDA, to get the selfUser and check if this event got executed by itself)
If thats not the case maybe you could tell us more about when it gets executed.

Related

JDA Discord Bot - [ErrorResponseException] 10008: Unknown Message

despite being new to JDA I havent had any big problems till now, maybe I am just missing something crucial. To quickly explain what following code SHOULD do:
Whenever the bot gets started, the onGuildReady event creates an Object containing a loop which sends a single message to a specified channel and after 1 minute edits said message.
#Override
public void onGuildReady(GuildReadyEvent event) {
System.out.println("ON GUILD READY: " + event.getGuild());
new AutoController(event, channelid);
}
Now with my understanding, the guildReady event should enable me to send messages to specified channels in every guild my bot is connected to AND edit them or do stuff with them.
String messageId = null;
String channelId;
public AutoController(GuildReadyEvent event, String channelId){
this.event = event;
start();
}
#Override
public void run(){
try {
while (true) {
sleep((long) timer);
if(messageId == null){
event.getGuild().getTextChannelById(channelId).sendMessage("A").queue();
this.messageId = event.getGuild().getTextChannelById(channelId).getLatestMessageId();
}else{
event.getGuild().getTextChannelById(channelId).editMessageById(messageId,"B").queue();
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
Sending a message whenever the bot goes online works, but as soon as it gets in the 2nd loop where the edit should happen, it throws me an ErrorResponseException saying the message is unkknown.
[ForkJoinPool.commonPool-worker-1] ERROR RestAction - RestAction queue returned failure: [ErrorResponseException] 10008: Unknown Message net.dv8tion.jda.api.exceptions.ContextException at net.dv8tion.jda.api.exceptions.ContextException.here(ContextException.java:54) at net.dv8tion.jda.api.requests.Request.<init>(Request.java:73) at net.dv8tion.jda.internal.requests.RestActionImpl.queue(RestActionImpl.java:200) at net.dv8tion.jda.api.requests.RestAction.queue(RestAction.java:572) at net.dv8tion.jda.api.requests.RestAction.queue(RestAction.java:538) at org.gsbunker.controller.AutoController.run(AutoController.java:45)
I really dont understand why the message is unknown, ive already checked that the messageid and channelid are not null when passed - still getting the same error. the code is slightly simplified for understanding purposes, if questions occur feel free to ask. pleeeeeeeaase help me and my brain <3
Sitting back and relaxing your brain sometimes really is the best solution!
Putting the thread to sleep after queuing made sure that the message is online and ready for retrieval when executing the editMessage method
if(messageId == null){
event.getGuild().getTextChannelById(channelId).sendMessage("A").queue();
sleep(3000);
this.messageId = event.getGuild().getTextChannelById(channelId).getLatestMessageId();
}else{
event.getGuild().getTextChannelById(channelId).editMessageById(messageId,"B").queue();
}
after reviewing the documentation again, if found that the complete() method would be even better suited for a use case like this.
if(messageId == null){
event.getGuild().getTextChannelById(channelId).sendMessage("A").complete();
this.messageId = event.getGuild().getTextChannelById(channelId).getLatestMessageId();
}else{
event.getGuild().getTextChannelById(channelId).editMessageById(messageId,"B").queue();
}
yeeaahh not that big of a problem if you clear your head and stop forgetting java basics, happy coding :D

How can i change nicks with Discord Bot with JDA

Im doing a Discord Bot where i need to change the nickname of the user who send the message, for example: Person "A" send a message and i want to change his name to "B".
I have this code, but it doesnt work and i cant find why.
public void onGuildMessageReceived(GuildMessageReceivedEvent e) {
if(e.getAuthor().isBot()) return;
String[] args = e.getMessage().getContentRaw().split("\\s+");
System.out.println("lengh: " + args.length);
if(args[0].equalsIgnoreCase("!verify")) {
if(args.length == 2) {
Guild guild = e.getGuild();
//guild.getMember(e.getAuthor()).modifyNickname("asdsllslslsl").queue();
guild.modifyNickname(e.getMessage().getMember(), "pepeasd");
}
}
}
You're not queuing the RestAction you created. To queue it you'll need to add .queue() after the modifyNickname() method.
Read more about how RestAction and JDA work here: https://ci.dv8tion.net/job/JDA/javadoc/net/dv8tion/jda/api/requests/RestAction.html

How do I authenticate with my Google Cloud Function when using my Firebase App with Google Sign In?

I am a newbie (6 months going or so) and creating an app on Android (Java) that utilizes FireBase Auth with Google Sign In. (with only a few days of NodeJS exposure now) In other words my end user signs in with the Google Account. That part (I think) works pretty well so far. I use the Firestore Database heavily for a lot of things in the app.
So now I've gotten to the point where I want to use (Callable) Cloud Functions with HTTP Triggers. (never having done any of this before) I'm trying to get a proof of concept working at this time. The actual function works and I can trigger it from my app.
It appears that I cannot figure out how to make the function "private" though; as in "adding proper Members" to the Cloud function who have the right to invoke the function.
I have tried a few different things by trial error, but first let me show what I have.
This is the Cloud Function and I'm passing in an arbitrary String as a test, works nicely: (as long as "allUsers" have the role/right to invoke the function; in other words when the function is public.
exports.createTest = functions.https.onCall((data, context) => {
const text = data.text;
const uid = context.auth.uid;
const name = context.auth.token.name || null;
const email = context.auth.token.email || null;
console.log('UID: ', uid);
console.log('Name: ', name);
console.log('Email: ', email);
console.log('Message: ', text);
});
The above function gets triggered in my Android/Java code like this: (I think this code came from Google Doc/Sample/Example
private FirebaseFunctions mFunctions;
...
private void testing() {
mFunctions = FirebaseFunctions.getInstance();
Log.e(LOG_TAG, "Testing executed!");
String testMessage = "Hello Hello Testing 123 Mic Check";
createTest(testMessage)
.addOnCompleteListener(new OnCompleteListener<String>() {
#Override
public void onComplete(#NonNull Task<String> task) {
if (!task.isSuccessful()) {
Exception e = task.getException();
if (e instanceof FirebaseFunctionsException) {
FirebaseFunctionsException ffe = (FirebaseFunctionsException) e;
FirebaseFunctionsException.Code code = ffe.getCode();
Object details = ffe.getDetails();
Log.e(LOG_TAG, "FFE: " + ffe.getMessage() );
Log.e(LOG_TAG, "Code: " + code);
Log.e(LOG_TAG, "Details:" + details);
}
// ...
}
// ...
}
});
}
private Task<String> createTest(String text) {
// Create the arguments to the callable function.
Map<String, Object> data = new HashMap<>();
data.put("text", text);
data.put("push", true);
return mFunctions
.getHttpsCallable("createTest") //this is the function name
.call(data)
.continueWith(new Continuation<HttpsCallableResult, String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult> task) throws Exception {
// This continuation runs on either success or failure, but if the task
// has failed then getResult() will throw an Exception which will be
// propagated down.
String result = (String) task.getResult().getData();
if (result != null) {
Log.e(LOG_TAG, "Result: " + result);
}
return result;
}
});
}
Only when I have "allUsers" added with the role/right to "invoke Cloud Function" then I get this working. My understanding of HTTP Requests and such is pretty limited, which is not making things easier.
I tried using the "allAuthenticatedUsers" options, which I figured would do the trick, because I actually authenticate my Users in the app through Firebase/Google Sign In. This Cloud Function shall only be available to either a) authenticated users or b) users of a specific domain. (I have a domain, let's say #testorganization.com) Or if I can identify my particular app (api key?) then that would work, too.
The moment I add a member "allAuthenticatedUsers" with role to invoke the function (and remove "allUsers) nothing happens. I also tried adding the entire domain, but that wouldn't work. (duh) Also tried adding my service account (trial and error at this point) and didn't seem to work.
In my Node JS code I am actually receiving the UID of the authenticated user, so it appears that some kind of user authentication information is already being exchanged.
With that knowledge, I can (successfully tried this) get the UID and cross check that against my database and verify a user that way, but seems unnecessary and I should be able to make the permissions work. (lock the function down entirely) Plus this took a really long time just finish this cross check. Or is this pretty standard procedure to do?
Like this-->
const usersRef = admin.firestore().collection('users').doc(uid)
usersRef.get()
.then((docSnapshot) => {
if (docSnapshot.exists) {
usersRef.onSnapshot((doc) => {
console.log('User Type logged in: ', doc.data().userCategory)
console.log('User Title: ', doc.data().userTitle)
});
} else {
console.log('User does not exist')
}
});
Edit:
So while not having figured out how to shut down the function entirely, I did discover that instead of cross checking my users, I can simple check for auth like this:
if (context.auth){
//user is auth'd
} else {
//no auth
}
That's a little bit better, I guess. (but still doesn't technically prevent access to the function?!)
Thank you so much for any help. Much appreciated.
Edit2:
Here is a screensshot of the area in the cloud console (for cloud function roles/privileges) that I am referring to:
https://imgur.com/cBsjaaL
With a Callable Cloud Function, if you want to ensure that only authenticated users can trigger it's business logic, you actually don't need to configure any extra "cloud function roles/privileges" as shown at the bottom of your question.
By default, with a Callable Cloud Function you will get, when available, "Firebase Authentication and FCM tokens automatically included in requests" and it will "automatically deserializes the request body and validates auth tokens", as explained in the doc.
So you just have to follow the doc and use the context parameter. As you have mentioned in your question, you can check the user is authenticated by doing:
if (context.auth) {
//...
}
If you want to verify the user email, you would do:
exports.addMessage = functions.https.onCall((data, context) => {
const uid = context.auth.uid;
return admin.auth().getUser(uid)
.then(userRecord => {
const userEmail = userRecord.email;
//....
})
.catch(function(error) {
console.log('Error fetching user data:', error);
// Send back an error to the front end
// See https://firebase.google.com/docs/functions/callable#handle_errors
});
});
You will find more examples on how to "work with" users with the Admin SDK here in the doc.

My Telegram Bot can't read message sended by another Telegram Bot

I have two Telegram bots, written in Java.
First bot send some message every 10 min. (It works).
Second bot must read this sended message, and store it to txt file...
But problem is that the second bot don't see this message... He see messages only from USERS.
M - bot
YR - user
Chat image
Console result
Here says if
BotFather=>Bot=>Settings=>Group Privacy => Privacy mode disabled and >BotFather=>Bot=>Settings=>Allow Groups? => Groups are currently enabled
bots must see messages sent from one to another.
I did it, also bots was invited to new group with Administrator rights. Here code example (second bot):
public class TelegramBot extends TelegramLongPollingBot {
public final String BOT_TOKEN = "****************************";
public final String BOT_NAME = "*****************";
#Override
public void onUpdateReceived(Update update) {
long inChatID = 0;
int inMessID = 0;
String inText = "";
try { // from chanel
inChatID = update.getChannelPost().getChatId();
inText = update.getChannelPost().getText();
inMessID = update.getChannelPost().getMessageId();
} catch (Throwable t1) {};
try { // from chat
inChatID = update.getMessage().getChatId();
inText = update.getMessage().getText();
inMessID = update.getMessage().getMessageId();
} catch (Throwable t2) {};
System.out.println(inChatID);
System.out.println(inMessID);
System.out.println(inText);
}
#Override
public String getBotUsername() {
return BOT_NAME;
}
#Override
public String getBotToken() {
return BOT_TOKEN;
}
}
If I send any message like user, i getting in console ChatID, MessID, Text...
But if first bot send some message, i got nothing. Help me with this issue. Any suggestion where my mistake ?
I solved this issue by adding this two bots to channel with administrator rights.
It's don't works with GROUP CHAT.
Now i can see message sended from another bots.
But there is another problem: all messages sended by any bot in channel throws like channel message, with out Bot ID, Bot Name, etc...
So, I just can read message text, and parse it.

How to check if firebase task is complete in Java?

I have a Java Web application with Tomcat 8 (not android), Java 8 and Firebase Admin version 5.2.0.
The below code works fine however I need to be sure that task is complete and return a value.
#POST
#Path("/verifyToken")
#Produces(MediaType.APPLICATION_JSON)
public Response verifyToken(#Context HttpHeaders headers,#Context HttpServletRequest request) {
try {
String auth = headers.getRequestHeader("authorization").get(0);
if (!auth.isEmpty()) {
System.out.println("logged auth token: " + auth);
}
// validate token
Task<FirebaseToken> task = FirebaseAuth.getInstance().verifyIdToken(auth)
.addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
#Override
public void onSuccess(FirebaseToken decoded) {
String uid = decoded.getUid();
//do some stuff
}
});
/*
The return statement below shouldn't run unless "task" is
complete.
task has method isComplete()
*/
return Response.status(200).entity(user).build();
} catch (Exception e) {
e.printStackTrace();
System.out.println("Login Exception " + e.getMessage());
return Response.status(500).build();
}
}
Task object (task) has a method "isComplete()". However, it may need to be called more than once. If in first time the task is not complete, the code should know how to wait and call it again later and this way on until certain number of times.
I'm stuck in how to build the logic to properly wait and try again until task is complete or event quit the condition.
How to do it?
I highly recommend you check out Doug Stevenson's excellent series of blog posts on the Task API. This specific problem is covered in https://firebase.googleblog.com/2016/10/become-a-firebase-taskmaster-part-4.html
The solution seems to be Tasks.await(task)....
i'm using Tasks.await() to wait firebase task finish.
example in kotlin to get token:
var task = FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener {}
Tasks.await(task)
var token = task.result!!.token

Categories