package main import ( "bufio" "errors" "flag" "fmt" "io" "log" "net" "net/textproto" "strconv" "github.com/asdine/storm" "gopkg.in/telegram-bot-api.v4" "xeserv.us/r1459" ) var ( vhost = flag.String("vhost", "telegram.org", "vhost to use for things") port = flag.Int("port", 3540, "port to listen on") store = flag.String("store", "var/", "where to store things") rooms map[string]int64 ) type Client struct { net.Conn limitReader *io.LimitedReader tpReader *textproto.Reader sentNick bool sentUser bool nagged bool username string Nick string Password *string DB *storm.DB TGBot *tgbotapi.BotAPI } func (c *Client) KillClient(err error) { c.SendLine(&r1459.RawLine{ Source: "xeserv.us", Verb: "ERROR", Args: []string{ "***", "Closing link: " + err.Error(), }, }) c.Close() c = nil } func (c *Client) SendLine(line *r1459.RawLine) error { log.Printf("%s <- %s", c.Nick, line) _, err := fmt.Fprintf(c, "%s\r\n", line) return err } func (c *Client) Join(shortname string, roomID int64) { room, err := c.MakeRoom(shortname, roomID) if err != nil { c.KillClient(err) log.Fatal(err) } for _, line := range room.Burst() { c.SendLine(line) } } func Mask(nick string) string { return fmt.Sprintf("%s!telegram@%s", nick, *vhost) } func main() { flag.Parse() server, err := net.Listen("tcp", ":"+strconv.Itoa(*port)) if server == nil { panic("couldn't start listening: " + err.Error()) } for { client, err := server.Accept() if err != nil { log.Printf("couldn't accept: %s", err.Error()) continue } log.Printf("Accepted connection from %s", client.RemoteAddr()) lr := &io.LimitedReader{ R: client, N: 65536, } tpr := textproto.NewReader(bufio.NewReader(lr)) c := &Client{ Conn: client, limitReader: lr, tpReader: tpr, } go handleClient(c) } } func handleClient(c *Client) { for { line, err := c.tpReader.ReadLine() if err != nil { _ = c.Close() log.Printf("%s: %s", c.RemoteAddr(), err) return } log.Printf("%s -> %s", c.RemoteAddr(), line) ircLine := r1459.NewRawLine(line) switch ircLine.Verb { case "NICK": if len(ircLine.Args) != 1 { c.Close() } c.sentNick = true c.Nick = ircLine.Args[0] if c.DB == nil { db, err := storm.Open("var/" + c.Nick + ".db") if err != nil { c.KillClient(err) return } c.DB = db } case "PASS": if len(ircLine.Args) != 1 { c.KillClient(errors.New("Invalid format")) return } c.Password = &ircLine.Args[0] bot, err := tgbotapi.NewBotAPI(*c.Password) if err != nil { c.KillClient(err) return } me, err := bot.GetMe() if err != nil { c.KillClient(err) return } c.SendLine(&r1459.RawLine{ Source: "xeserv.us", Verb: "NOTICE", Args: []string{ "***", fmt.Sprintf("You are logged in as %s (@%s)", me.FirstName, me.UserName), }, }) c.TGBot = bot u := tgbotapi.NewUpdate(0) u.Timeout = 60 updates, err := bot.GetUpdatesChan(u) if err != nil { c.KillClient(err) } go func(c *Client) { for update := range updates { id := update.Message.Chat.ID r := &Room{} c.DB.One("RoomID", id, r) r.client = c if update.Message.Text != "" { r.RecieveMessage(update) } } }(c) case "USER": if len(ircLine.Args) != 4 { c.Close() } c.sentUser = true case "PING": ircLine.Verb = "PONG" c.SendLine(ircLine) case "PONG", "MODE", "JOIN", "WHO": continue case "ASSOCIATE": if len(ircLine.Args) != 2 { c.SendLine(&r1459.RawLine{ Verb: "NOTICE", Source: "xeserv.us", Args: []string{ c.Nick, "JOIN ", }, }) continue } n, _ := strconv.Atoi(ircLine.Args[1]) c.Join(ircLine.Args[0], int64(n)) case "PRIVMSG": room := &Room{} err := c.DB.One("Shortname", ircLine.Args[0][1:], room) if err != nil { panic(err) } room.client = c room.SendMessage(ircLine) default: c.SendLine(&r1459.RawLine{ Source: "xeserv.us", Verb: "421", Args: []string{ c.Nick, "unknown command " + ircLine.Verb, }, }) } if c.TGBot != nil && c.Password != nil && c.sentNick && c.sentUser && !c.nagged { c.nagged = true c.SendLine(&r1459.RawLine{ Source: "xeserv.us", Verb: "001", Args: []string{ c.Nick, "Welcome to the Internet Relay Chat Network: " + c.Nick, }, }) c.SendLine(&r1459.RawLine{ Source: "xeserv.us", Verb: "002", Args: []string{ c.Nick, fmt.Sprintf("Your host is xeserv.us[%s], running version tbotd HEAD DEVEL", c.LocalAddr()), }, }) c.SendLine(&r1459.RawLine{ Source: "xeserv.us", Verb: "005", Args: []string{ c.Nick, "CHANTYPES=!#", "CHANMODES=b,,,ns", "PREFIX=(ya)~&", "NETWORK=Telegram", "are supported by this server", }, }) rooms := []Room{} err := c.DB.All(&rooms) if err != nil { c.KillClient(err) return } for _, room := range rooms { c.Join(room.Shortname, room.RoomID) } } } }