326 lines
5.5 KiB
Go
326 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"net/textproto"
|
|
"strconv"
|
|
|
|
"github.com/asdine/storm"
|
|
|
|
"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 {
|
|
log.Fatal(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
|
|
|
|
db, err := storm.Open("var/" + me.UserName + ".db")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
return
|
|
}
|
|
|
|
c.DB = db
|
|
|
|
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 {
|
|
if update.Message == nil {
|
|
continue
|
|
}
|
|
|
|
id := update.Message.Chat.ID
|
|
r := &Room{}
|
|
|
|
err := c.DB.One("RoomID", id, r)
|
|
if err != nil {
|
|
c.SendLine(&r1459.RawLine{
|
|
Source: "xeserv.us",
|
|
Verb: "PRIVMSG",
|
|
Args: []string{
|
|
c.Nick,
|
|
fmt.Sprintf("Unknown room ID %v message %#v", update.Message.Chat.ID, update.Message.Chat),
|
|
},
|
|
})
|
|
}
|
|
|
|
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, "ASSOCIATE <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)
|
|
}
|
|
}
|
|
}
|
|
}
|