Skip to content

Instantly share code, notes, and snippets.

@kaecy
Last active May 5, 2022 18:54
Show Gist options
  • Save kaecy/286f8ad334aec3fcb588516feb727772 to your computer and use it in GitHub Desktop.
Save kaecy/286f8ad334aec3fcb588516feb727772 to your computer and use it in GitHub Desktop.
Simple IRC Bot (Updated)
public class SimpleIrcBot extends IRCMessageLoop {
SimpleIrcBot(String server, int port) {
super(server, port);
}
// you have full access to PRIVMSG messages that are parsed.
void raw(Message msg) {
// when someone sends "hello" we'll respond with "Hiya!"
if (msg.content.equals("hello")) {
// is this a channel the message was sent to?
if (msg.target.startsWith("#")) {
privmsg(msg.target, "Hiya!");
} else {
// no it's a private message. let's send this message back to the nickname.
privmsg(msg.nickname, "Hiya");
}
// this is weird, yep, but this is how irc is.
// the target of a message is the person or group (channel) receiving the message so
// if the bot is receiving the message the target would be
// the bot's nickname.
}
if (msg.content.equals("!news")) {
privmsg(msg.target, "In keeping with Channel 40's policy of bringing you the latest in " +
"blood and guts and in living color, you are going to see another first - an attempted suicide.");
}
}
public static void main(String[] args) {
SimpleIrcBot client = new SimpleIrcBot("irc.rizon.net", 6667);
client.nick("Christine_Chubbuck");
client.user("6a3k_49GRK", "null", "null", "real name");
client.join("#test");
client.run();
}
}
import java.io.*;
import java.net.*;
import java.util.*;
public abstract class IRCMessageLoop implements Runnable {
Socket server;
OutputStream out;
List<String> channelList;
boolean initial_setup_status;
IRCMessageLoop(String serverName, int port) {
channelList = new ArrayList<String>();
try
{
server = new Socket(serverName, port);
out = server.getOutputStream();
}
catch (IOException info)
{}
}
void send(String text) {
byte[] bytes = (text + "\r\n").getBytes();
try {
out.write(bytes);
}
catch (IOException info)
{}
}
void nick(String nickname) {
String msg = "NICK " + nickname;
send(msg);
}
void user(String username, String hostname, String servername, String realname) {
String msg = "USER " + username + " " + hostname + " " + servername + " :" + realname;
send(msg);
}
void join(String channel) {
if (!initial_setup_status) {
channelList.add(channel);
return;
}
String msg = "JOIN " + channel;
send(msg);
}
void part(String channel) {
String msg = "PART " + channel;
send(msg);
}
void privmsg(String to, String text) {
String msg = "PRIVMSG " + to + " :" + text;
send(msg);
}
void pong(String server) {
String msg = "PONG " + server;
send(msg);
}
void quit(String reason) {
String msg = "QUIT :Quit: " + reason;
send(msg);
}
abstract void raw(Message msg);
void initial_setup() {
initial_setup_status = true;
// now join the channels. you need to wait for message 001 before you join a channel.
for (String channel: channelList) {
join(channel);
}
}
void processMessage(String ircMessage) {
Message msg = MessageParser.message(ircMessage);
if (msg.command.equals("privmsg")) {
String target, content;
if (msg.content.equals("\001VERSION\001")) {
privmsg(msg.nickname, "Prototype IRC Client (Built to learn)");
return;
}
raw(msg);
System.out.println("PRIVMSG: " + msg.nickname + ": " + msg.content);
}
else if (msg.command.equals("001")) {
initial_setup();
return;
}
else if (msg.command.equals("ping")) {
pong(msg.content);
}
}
public void run() {
InputStream stream = null;
try
{
stream = server.getInputStream();
MessageBuffer messageBuffer = new MessageBuffer();
byte[] buffer = new byte[512];
int count;
while (true) {
count = stream.read(buffer);
if (count == -1)
break;
messageBuffer.append(Arrays.copyOfRange(buffer, 0, count));
while (messageBuffer.hasCompleteMessage()) {
String ircMessage = messageBuffer.getNextMessage();
System.out.println("\"" + ircMessage + "\"");
processMessage(ircMessage);
}
}
}
catch (IOException info)
{
quit("error in messageLoop");
info.printStackTrace();
}
}
}
class Message {
public String origin;
public String nickname;
public String command;
public String target;
public String content;
}
class MessageBuffer {
String buffer;
public MessageBuffer() {
buffer = "";
}
public void append(byte[] bytes) {
buffer += new String(bytes);
}
public boolean hasCompleteMessage() {
if (buffer.contains("\r\n"))
return true;
else
return false;
}
public String getNextMessage() {
int index = buffer.indexOf("\r\n");
String message = "";
if (index > -1) {
message = buffer.substring(0, index);
buffer = buffer.substring(index + 2);
}
return message;
}
public static void main(String[] args) {
MessageBuffer buf = new MessageBuffer();
buf.append("blah\r\nblah blah\r\nblah blah oh uh".getBytes());
while (buf.hasCompleteMessage()) {
System.out.println("\"" + buf.getNextMessage() + "\"");
}
buf.append(" blah\r\n".getBytes());
while (buf.hasCompleteMessage()) {
System.out.println("\"" + buf.getNextMessage() + "\"");
}
}
}
// class only parses messages it understands. if a message is not understood
// the origin and command are extracted and parsing halts.
class MessageParser {
static Message message(String ircMessage) {
Message message = new Message();
int spIndex;
if (ircMessage.startsWith(":")) {
spIndex = ircMessage.indexOf(' ');
if (spIndex > -1) {
message.origin = ircMessage.substring(1, spIndex);
ircMessage = ircMessage.substring(spIndex + 1);
int uIndex = message.origin.indexOf('!');
if (uIndex > -1) {
message.nickname = message.origin.substring(0, uIndex);
}
}
}
spIndex = ircMessage.indexOf(' ');
if (spIndex == -1) {
message.command = "null";
return message;
}
message.command = ircMessage.substring(0, spIndex).toLowerCase();
ircMessage = ircMessage.substring(spIndex + 1);
// parse privmsg params
if (message.command.equals("privmsg")) {
spIndex = ircMessage.indexOf(' ');
message.target = ircMessage.substring(0, spIndex);
ircMessage = ircMessage.substring(spIndex + 1);
if (ircMessage.startsWith(":")) {
message.content = ircMessage.substring(1);
}
else {
message.content = ircMessage;
}
}
// parse quit/join
if (message.command.equals("quit") || message.command.equals("join")) {
if (ircMessage.startsWith(":")) {
message.content = ircMessage.substring(1);
}
else {
message.content = ircMessage;
}
}
// parse ping params
if (message.command.equals("ping")) {
spIndex = ircMessage.indexOf(' ');
if (spIndex > -1) {
message.content = ircMessage.substring(0, spIndex);
}
else {
message.content = ircMessage;
}
}
return message;
}
}
@strifel
Copy link

strifel commented Apr 17, 2018

Thank you, I had problems when connecting to IRC, with this it just works fine

@montej92
Copy link

Good Code, I am able to connect the channel,
Thanks a Lot

@kaecy
Copy link
Author

kaecy commented Apr 30, 2020

I've updated the code to make it a little easier to make IRC bots.
Thank you.

@SimPilotAdamT
Copy link

SimPilotAdamT commented Dec 9, 2021

I'm trying to use this to write an IRC client, but every time I try to use it to connect the socket to irc.libera.chat:6697, I get the following exception:

java.net.SocketException: Connection reset
        at java.net.SocketInputStream.read(SocketInputStream.java:210)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at java.net.SocketInputStream.read(SocketInputStream.java:127)
	at com.AdamT.JarChat.IRCMessageLoop.run(JarChat.java:224)
	at com.AdamT.JarChat.JarChat.run(JarChat.java:19)
	at com.AdamT.JarChat.JarChat.main(JarChat.java:89)

All source code, with one or two edits, is already in a git repo. Any ideas?

@kaecy
Copy link
Author

kaecy commented Dec 9, 2021

@SimPilotAdamT hi.

Looks like the port is not right, try 6665. Also check out their connecting guide https://libera.chat/guides/connect.

@SimPilotAdamT
Copy link

SimPilotAdamT commented Dec 10, 2021

@kaecy yeah for some reason I was using the SSL encrypted port and not the plaintext one.

@SimPilotAdamT
Copy link

I made some edits to the IRCMessageLoop constructor which might help with connections to TLS ports...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment