initial commit
This commit is contained in:
commit
92e328314a
|
@ -0,0 +1,2 @@
|
||||||
|
.env
|
||||||
|
*.so
|
|
@ -0,0 +1,15 @@
|
||||||
|
# ircsuite
|
||||||
|
|
||||||
|
Like tootsuite but for IRC.
|
||||||
|
|
||||||
|
This basically connects to Mastodon and then relays public, home, and optionally hashtag searches to your IRC client, bot or script can react to toots in real time.
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- [ ] Hashtag streaming by joining `#hashtag`
|
||||||
|
- [ ] Multiple mastodon account support
|
||||||
|
`#/<accname>/<hashtag>` ?
|
||||||
|
- [ ] Multiple user support
|
||||||
|
- [ ] Full text searches by joining `?terms+in+query+syntax`
|
||||||
|
Common paginated contextual UI concepts that people have been talking about for chatbots might be amazing here, as that is what this would basically be.
|
||||||
|
- [ ]
|
|
@ -0,0 +1,96 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/Xe/ln"
|
||||||
|
"github.com/caarlos0/env"
|
||||||
|
"gopkg.in/irc.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
ServerName string `env:"SERVER_NAME" envDefault:"to.ot"`
|
||||||
|
ServerAddr string `env:"SERVER_ADDR" envDefault:"127.0.0.1:42069"`
|
||||||
|
// XXX RIP HACK TODO fix once user accounts exist
|
||||||
|
UserPassword string `env:"USER_PASSWORD" envDefault:"hunter2"`
|
||||||
|
|
||||||
|
MastodonInstance string `env:"MASTODON_INSTANCE,required"`
|
||||||
|
MastodonToken string `env:"MASTODON_TOKEN,required"`
|
||||||
|
MastodonClientID string `env:"MASTODON_CLIENT_ID,required"`
|
||||||
|
MastodonClientSecret string `env:"MASTODON_CLIENT_SECRET,required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := Config{}
|
||||||
|
err := env.Parse(&cfg)
|
||||||
|
if err != nil {
|
||||||
|
ln.Fatal(ln.F{"err": err})
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := net.Listen(cfg.ServerAddr)
|
||||||
|
if err != nil {
|
||||||
|
ln.Fatal(ln.F{"err": err, "addr": cfg.ServerAddr})
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
ctx := context.Context()
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
ln.Error(err, ln.F{"addr": cfg.ServerAddr})
|
||||||
|
}
|
||||||
|
|
||||||
|
ir := irc.NewReader(conn)
|
||||||
|
iw := irc.NewWriter(conn)
|
||||||
|
|
||||||
|
msg, err := ir.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
ln.Error(err, ln.F{"client_addr": conn.RemoteAddr().String()})
|
||||||
|
conn.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Command != "PASS" {
|
||||||
|
ln.Log(ln.F{"action": "auth_failed", "client_addr": conn.RemoteAddr().String()})
|
||||||
|
iw.Writef(":%s ERROR :authentication failed", cfg.ServerName)
|
||||||
|
conn.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Params[0] != cfg.UserPassword {
|
||||||
|
ln.Log(ln.F{"action": "wrong_password", "client_addr": conn.RemoteAddr().String()})
|
||||||
|
iw.Writef(":%s ERROR :authentication failed", cfg.ServerName)
|
||||||
|
conn.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mc, err := madon.RestoreApp("ircsuite", cfg.MastodonInstance, cfg.MastodonClientID, cfg.MastodonClientSecret, &madon.UserToken{AccessToken: cfg.MastodonToken})
|
||||||
|
if err != nil {
|
||||||
|
ln.Error(ln.F{"action": "madon.RestoreApp"})
|
||||||
|
iw.Writef(":%s ERROR :authentication failed", cfg.ServerName)
|
||||||
|
conn.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_ = mc
|
||||||
|
|
||||||
|
s := &Server{
|
||||||
|
channels: map[string]struct{}{},
|
||||||
|
mc: mc,
|
||||||
|
|
||||||
|
iw: iw,
|
||||||
|
ir: ir,
|
||||||
|
}
|
||||||
|
|
||||||
|
go s.HandleConn(ctx, conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
sync.Mutex // locked at per line execution
|
||||||
|
|
||||||
|
channels map[string]struct{} // channels the client has joined
|
||||||
|
mc *madon.Client
|
||||||
|
|
||||||
|
iw *irc.Writer
|
||||||
|
ir *irc.Reader
|
||||||
|
}
|
Loading…
Reference in New Issue