Reputation: 395
I'm making an IRC bot and I have a text file with a quote from each user on a new, separate line.
For example: I like peas - user1 I like tomatoes - user2 I like peas inside of tomatoes - user1
I want a way to be able to make it so that if someone types in "!quote user" it will take a random quote from that user and send it to the channel. This is the code that I have so far:
if (messageIC.startsWith("!quote")) {
String user = message.substring(7);
java.io.FileInputStream fs = null;
try {
fs = new java.io.FileInputStream("C:/Users/Quibbles/Documents/ampersand/quotes.txt");
} catch (FileNotFoundException ex) {
Logger.getLogger(Ampersand.class.getName()).log(Level.SEVERE, null, ex);
}
BufferedReader br = new BufferedReader(new InputStreamReader(fs));
ArrayList<String> array = new ArrayList<>();
String line;
try {
while((line = br.readLine()) != null)
array.add(br.readLine());
} catch (IOException ex) {
Logger.getLogger(Ampersand.class.getName()).log(Level.SEVERE, null, ex);
}
// variable so that it is not re-seeded every call.
Random rand = new Random();
// nextInt is exclusive. Should be good with output for array.
int randomIndex = rand.nextInt(array.size());
if ((array.get(randomIndex).contains(user))) {
sendMessage(channel, array.get(randomIndex));
}
}
However, this doesn't work. The bot doesn't spit out anything. What am I doing wrong?
Upvotes: 1
Views: 115
Reputation: 44328
You can replace the first half of your code with:
List<String> array = Files.readAllLines(
Paths.get(System.getProperty("user.home"),
"Documents", "ampersand", "quotes.txt"),
Charset.defaultCharset());
Your exception handling needs improvement. It makes no sense to continue with the method if you can't read the file, right? So either add throws IOException
to the containing method's signature, or do something like this:
try {
List<String> array = Files.readAllLines(
Paths.get(System.getProperty("user.home"),
"Documents", "ampersand", "quotes.txt"),
Charset.defaultCharset());
// Choose quote here
} catch (IOException e) {
// Can't continue if we can't read the quotes file.
throw new RuntimeException(e);
}
You want a random quote from a specific user, not just a random line in the file. So you should be applying your random value to only lines for the desired user:
try {
List<String> array = Files.readAllLines(
Paths.get(System.getProperty("user.home"),
"Documents", "ampersand", "quotes.txt"),
Charset.defaultCharset());
// Discard lines from other users
Iterator<String> i = array.iterator();
while (i.hasNext()) {
if (!i.next().endsWith(" - " + user)) {
i.remove();
}
}
// Important: Do not keep creating a new Random instance. Instead,
// create one instance and keep it in a field of your class.
int randomIndex = random.nextInt(array.size());
sendMessage(channel, array.get(randomIndex));
} catch (IOException e) {
// Can't continue if we can't read the quotes file.
throw new RuntimeException(e);
}
In Java 8, you can make things shorter with a Stream:
Path quotesFile = Paths.get(System.getProperty("user.home"),
"Documents", "ampersand", quotes.txt");
try (Stream<String> lines =
Files.lines(quotesFile, Charset.defaultCharset())) {
String[] array = lines.filter(line -> line.endsWith(" - " + user))
.toArray(String[]::new);
int randomIndex = random.nextInt(array.length);
sendMessage(channel, array[randomIndex]);
} catch (IOException e) {
// Can't continue if we can't read the quotes file.
throw new RuntimeException(e);
}
The Stream is in a try-with-resources statement because it is an I/O-backed Stream, meaning it supplies its values from an I/O operation (specifically, reading the file).
Upvotes: 0
Reputation:
Just some things that look like they could be causing the problem at first glance:
(I'm assuming that "C:/Users/Quibbles/Documents/ampersand/quotes.txt" contains quotes from all users.)
Firstly, don't put all quotes from all users into your ArrayList<String> array
if you're only going to be looking for one user. That way, when you pick a random value from the list, you can be sure that it will return something from the specified user (or be empty).
Secondly, as you seen to have noted in your comment, constructing an instance of Random
is a rather expensive operation, but putting it in a variable that's made each call won't stop it from being re-seeded, because it's actually being re-instantiated. You should put is outside the method, so it's not a local variable that keeps getting created and destroyed.
Thirdly, you were calling br.readLine()
twice in every iteration, which would skip lines. Since you already called it once, and assigned it to line
, just use line
.
Try this instead:
// ... somewhere in the actual class, *not* inside a method ...
private static Random rand = new Random();
// ...
// ... the rest here will be where it was, *inside* the method
if (messageIC.startsWith("!quote")) {
String user = message.substring(7);
java.io.FileInputStream fs = null;
try {
fs = new java.io.FileInputStream("C:/Users/Quibbles/Documents/ampersand/quotes.txt");
} catch (FileNotFoundException ex) {
Logger.getLogger(Ampersand.class.getName()).log(Level.SEVERE, null, ex);
}
BufferedReader br = new BufferedReader(new InputStreamReader(fs));
ArrayList<String> array = new ArrayList<String>();
String line;
try {
while ((line = br.readLine()) != null) {
if (line.contains(user)) {
array.add(br.readLine());
}
}
} catch (IOException ex) {
Logger.getLogger(Ampersand.class.getName()).log(Level.SEVERE, null, ex);
}
// `rand` will now refer to the class variable, located outside of the method
if (!array.isEmpty()) {
// nextInt is exclusive. Should be good with output for array.
sendMessage(channel, array.get(rand.nextInt(array.size())));
}
}
Also, it would be helpful to know exactly what went wrong when you said that your original code "wouldn't work". Did you try debugging it or seeing if the user's quote was found?
That way, we will be able to help you fix exactly what went wrong.
If finding the user is the problem, it might also help if we had the general format of "quotes.txt".
Anyway, I hope this helped. :)
Upvotes: 1