goirc gives a Go program an IRC client that keeps track of who is in the room
Event-based stateful IRC client framework for Go.
At a glance
- What is it?
- goirc is an event-based, stateful IRC client framework for Go in which handlers subscribe to protocol events and an optional state tracker maintains the channel and nickname view. It suits someone writing a bot or an integration against an IRC network, and it documents itself poorly, so the source is the reference.
- Who is it for?
- goirc is worth adopting if you are writing a Go program that has to talk to an IRC network and you want event handlers plus a maintained view of who is present, because the framework does the protocol bookkeeping and gives you both a raw message stream and an optional state layer.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 112 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The connection is an object you configure, then register handlers on
The API is built around a `*Conn` type and a handler registry, and the synopsis in the documentation shows the whole shape of it. The shortest form is a one-liner, `irc.SimpleClient("nick")`, and the longer form builds a config first:
// Or, create a config and fiddle with it first:
cfg := irc.NewConfig("nick")
cfg.SSL = true
cfg.SSLConfig = &tls.Config{ServerName: "irc.freenode.net"}
cfg.Server = "irc.freenode.net:7000"
cfg.NewNick = func(n string) string { return n + "^" }
c = irc.Client(cfg)The `NewNick` callback is the interesting piece, because it lets the framework pick a different nick if the one you asked for is taken, which is a small courtesy that saves every bot author from writing the same retry loop. The `SSL` flag and the `SSLConfig` are separate, so you can turn on TLS and still control the server name verification yourself. The design principle behind the whole framework is stated in the description as event-based and stateful, and both halves of that show up in the two ways you interact with the connection, registering callbacks for things that happen, and sending commands as methods.
Handlers are keyed by protocol code, not by message name
The event model is worth understanding precisely, because it is the part that differs from a callback-per-method API. You register a function against a numeric code, and the framework calls it when a message with that code arrives:
// e.g. join a channel on connect.
c.HandleFunc(irc.CONNECTED,
func(conn *irc.Conn, line *irc.Line) { conn.Join("#channel") })
// And a signal on disconnect
quit := make(chan bool)
c.HandleFunc(irc.DISCONNECTED,
func(conn *irc.Conn, line *irc.Line) { quit <- true })The framework defines named constants for the events it knows about, and `CONNECTED` and `DISCONNECTED` are two of them. The deeper point is in how the documentation describes event production. Events are produced directly from the messages from the IRC server, so you have to handle a code such as "332" for `RPL_TOPIC` to get the topic for a channel. There is no higher level vocabulary of topic-changed or nick-joined events unless the framework provides one, and the state tracking layer is what turns those raw numeric codes into something you can query. The separation is deliberate: the raw stream is always complete, and the semantic view on top of it is something you opt into.
State tracking is opt-in, and turning it on mid-connection is a mistake
The stateful half of the framework is the part that requires a decision from you. The vast majority of handlers implemented within the framework deal with state tracking of all nicks in any channels that the client is also present in, and those handlers live in `client/state_handlers.go`. State tracking is optional and disabled by default, and it is enabled and disabled by calling `EnableStateTracking()` and `DisableStateTracking()` respectively. The warning about doing this while connected is blunt: doing this while connected to an IRC server will probably result in an inconsistent state and a lot of warnings to standard error. That is a real design constraint rather than a documentation gap. The tracker learns the channel population from the raw messages it sees, so enabling it on a connection that has already joined channels means the framework has missed the join and part events that would have populated the view, and the state it holds is a partial picture. The right sequence is to enable tracking before connecting, which is why the toggle exists as a separate call rather than a config field.
Sending is a method call, and the command set is incomplete
The other direction is simpler. Commands to be sent to the server, for example PRIVMSG, are methods of the main `*Conn` struct, and the documentation points at `client/commands.go` for the list, with a parenthetical note that not all of the possible IRC commands are implemented yet. So the framework gives you a typed call for the commands it covers and leaves the rest to you, and the honest reading is that the command coverage is a work in progress rather than a complete IRC client implementation. The `Join` call in the handler example above is one of these methods, taking a channel name. The documentation also points out where the design came from, saying the author is indebted to Matt Gruen for the go-bot work which inspired the re-organisation and channel-based communication structure of `*Conn.send()` and `*Conn.recv()`. The author also notes that things could be more asynchronous, which is an honest statement about where the framework sits relative to a fully event-driven client.
Getting it is one go get, and the example connects to a real network
Acquisition is described as pretty simple, really, and it is a single command in the `client` package of the module:
go get github.com/fluffle/goirc/clientThe documentation then points at two places to read next. There is example code demonstrating usage of the library in `client.go`, and the documentation warns that this will connect to freenode and join `#go-nuts` by default, so be careful, which is a fair warning because running an example should not join a channel on a network you did not mean to join. For migration, there is a `fix/goirc.go` tool and a README there offering a quick way to move from the old `go1` API. The go.mod file shows the dependency surface, with go-sasl for SASL authentication, a logging package and glog, the mock library for tests, and `golang.org/x/net`, on Go 1.23.0. The repository root also holds a `logging/` directory, a `state/` directory, and a `vims` entry.
The documentation says it is bad, and that is the real documentation
There is a section of the project page that is worth quoting in full because it tells you how to evaluate the library. It says sorry the documentation is crap, use the source, Luke. Feedback on design decisions is welcome, and the author is indebted to Matt Gruen for the go-bot work that inspired the re-organisation and channel-based communication structure of `*Conn.send()` and `*Conn.recv()`, with the note that things could be more asynchronous. So the guidance to a new user is concrete. Read `client/handlers.go` for a more in-depth look at how handlers can be written, and use the test client, which the documentation says provides a good, if basic, example of how to use the framework. The licence is stated as the same terms as Go itself, and the code is attributed to Alex Bramley for 2009 through 2026, which places the project in the long-lived category of Go libraries rather than something recently rewritten.
Where an IRC client framework is the wrong tool
Two limits are worth naming. The first is protocol coverage. The documentation says not all of the possible IRC commands are implemented yet, and the raw event model means you are handling numeric server codes yourself, which is fine for a plain bot and a problem for anything built on IRCv3 extensions, where the modern feature set lives in message tags and capability negotiation that this generation of the framework predates. A team that needs those extensions is looking at a different library, because the gap is protocol surface rather than a missing convenience method. The second is the operational reality of IRC itself. The library can give you a clean event API, but it cannot give you a network that reliably connects, and the example pointing at freenode reflects that this ecosystem has moved on. The alternative to weigh against is a modern IRC client library that has been maintained against current networks and current authentication, where the difference in approach is between a small, readable, event-driven framework whose state layer you can inspect line by line, and a maintained package that tracks what servers actually accept. If you are writing a bot for a network you know and want to understand the protocol, goirc is the more instructive choice. If you are deploying something that has to stay connected, the maintenance question matters more than the API elegance.
Editorial conclusion
goirc is worth adopting if you are writing a Go program that has to talk to an IRC network and you want event handlers plus a maintained view of who is present, because the framework does the protocol bookkeeping and gives you both a raw message stream and an optional state layer. It is a poor fit if you need a well documented library with a modern release process, since the project says outright that the documentation is crap and tells you to use the source, and a poor fit if you depend on a specific IRC extension such as the IRCv3 message tags, because the documentation notes that not all of the possible IRC commands are implemented yet. Before you build on it, read `client/handlers.go` and `client/state_handlers.go` to see what the framework already handles, and note that the last push to the repository was on 2026-07-03.
Frequently asked questions
How do I install the goirc client framework?
Run go get github.com/fluffle/goirc/client. The project also points at example code in client.go, which connects to freenode and joins #go-nuts by default, and at the fix/goirc.go tool and its README for a quick migration from the old go1 API.
How do I enable nick and channel state tracking in goirc?
State tracking is disabled by default and is turned on with EnableStateTracking() and off again with DisableStateTracking(). The documentation warns that changing it while connected to a server will probably result in an inconsistent state and a lot of warnings to standard error, so it should be enabled before connecting.
How do I get a channel topic in goirc?
Events are produced directly from the messages from the IRC server, so you register a handler for the numeric code 332, which is RPL_TOPIC, to receive the topic for a channel. The framework provides named constants such as CONNECTED and DISCONNECTED for the events it knows about.
Which IRC commands does goirc support sending?
Commands to be sent to the server, for example PRIVMSG, are methods of the main *Conn struct, and the list lives in client/commands.go. The documentation notes that not all of the possible IRC commands are implemented yet, so the command set should be checked before you rely on a particular one.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fluffle-goirc)Was this useful?