tbotd/src/xeserv.us/cmd/tbotd/main.go

308 lines
5.1 KiB
Go

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 <shortname> <id>",
},
})
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)
}
}
}
}