Add code
This commit is contained in:
parent
6b17c2f576
commit
6559385261
|
@ -0,0 +1,3 @@
|
|||
pkg
|
||||
var/*.db
|
||||
bin
|
|
@ -0,0 +1,307 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"xeserv.us/r1459"
|
||||
|
||||
"gopkg.in/telegram-bot-api.v4"
|
||||
)
|
||||
|
||||
type Room struct {
|
||||
ID int
|
||||
RoomID int64 `storm:"index" storm:"unique"`
|
||||
Title string
|
||||
Shortname string `storm:"index" storm:"unique"`
|
||||
CreationDate time.Time
|
||||
|
||||
Members []string
|
||||
|
||||
client *Client
|
||||
}
|
||||
|
||||
func (c *Client) MakeRoom(shortname string, roomID int64) (*Room, error) {
|
||||
room := &Room{}
|
||||
err := c.DB.One("RoomID", roomID, room)
|
||||
if err != nil {
|
||||
chat, err := c.TGBot.GetChat(tgbotapi.ChatConfig{
|
||||
ChatID: roomID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
room = &Room{
|
||||
ID: rand.Int(),
|
||||
RoomID: roomID,
|
||||
Title: chat.Title,
|
||||
Shortname: shortname,
|
||||
CreationDate: time.Now(),
|
||||
Members: []string{},
|
||||
}
|
||||
|
||||
admins, err := c.TGBot.GetChatAdministrators(tgbotapi.ChatConfig{
|
||||
ChatID: roomID,
|
||||
})
|
||||
|
||||
var nicks []string
|
||||
|
||||
for _, admin := range admins {
|
||||
var status string
|
||||
if admin.IsCreator() {
|
||||
status = "~"
|
||||
} else {
|
||||
status = "&"
|
||||
}
|
||||
nicks = append(nicks, status+admin.User.UserName)
|
||||
}
|
||||
|
||||
nicks = append(nicks, c.Nick)
|
||||
|
||||
room.Members = append(room.Members, nicks...)
|
||||
|
||||
c.DB.Save(room)
|
||||
}
|
||||
|
||||
room.client = c
|
||||
|
||||
return room, nil
|
||||
}
|
||||
|
||||
func (r *Room) Burst() []*r1459.RawLine {
|
||||
ret := []*r1459.RawLine{}
|
||||
c := r.client
|
||||
|
||||
ret = append(ret, &r1459.RawLine{
|
||||
Verb: "JOIN",
|
||||
Source: Mask(c.Nick),
|
||||
Args: []string{
|
||||
fmt.Sprintf("#%v", r.Shortname),
|
||||
},
|
||||
})
|
||||
|
||||
ret = append(ret, &r1459.RawLine{
|
||||
Verb: "MODE",
|
||||
Source: "xeserv.us",
|
||||
Args: []string{
|
||||
fmt.Sprintf("#%v", r.Shortname),
|
||||
"+nt",
|
||||
},
|
||||
})
|
||||
|
||||
ret = append(ret, &r1459.RawLine{
|
||||
Verb: "TOPIC",
|
||||
Source: "xeserv.us",
|
||||
Args: []string{
|
||||
fmt.Sprintf("#%v", r.Shortname),
|
||||
r.Title,
|
||||
},
|
||||
})
|
||||
|
||||
ret = append(ret, &r1459.RawLine{
|
||||
Verb: "353",
|
||||
Source: "xeserv.us",
|
||||
Args: []string{
|
||||
c.Nick, "=", fmt.Sprintf("#%v", r.Shortname),
|
||||
strings.Join(r.Members, " "),
|
||||
},
|
||||
})
|
||||
|
||||
ret = append(ret, &r1459.RawLine{
|
||||
Verb: "366",
|
||||
Source: "xeserv.us",
|
||||
Args: []string{
|
||||
c.Nick, fmt.Sprintf("#%v", r.Shortname),
|
||||
"End of /NAMES list",
|
||||
},
|
||||
})
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (r Room) RecieveMessage(update tgbotapi.Update) {
|
||||
c := r.client
|
||||
|
||||
log.Printf("[#%v] %s %s", r.Shortname, update.Message.From.UserName, update.Message.Text)
|
||||
|
||||
found := false
|
||||
for _, member := range r.Members {
|
||||
if member == "" {
|
||||
continue
|
||||
}
|
||||
if update.Message.From.UserName == member[1:] || update.Message.From.UserName == member {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
r.Members = append(r.Members, update.Message.From.UserName)
|
||||
c.DB.Save(&r)
|
||||
|
||||
c.SendLine(&r1459.RawLine{
|
||||
Verb: "JOIN",
|
||||
Source: Mask(update.Message.From.UserName),
|
||||
Args: []string{
|
||||
"#" + r.Shortname,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
for _, msgLine := range strings.Split(update.Message.Text, " ") {
|
||||
c.SendLine(&r1459.RawLine{
|
||||
Verb: "PRIVMSG",
|
||||
Source: Mask(update.Message.From.UserName),
|
||||
Args: []string{
|
||||
"#" + r.Shortname,
|
||||
msgLine,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (r Room) SendMessage(l *r1459.RawLine) error {
|
||||
msg := tgbotapi.NewMessage(r.RoomID, l.Args[1])
|
||||
_, err := r.client.TGBot.Send(msg)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
// Package r1459 implements a base structure to scrape out and utilize an RFC 1459
|
||||
// frame in high level Go code.
|
||||
package r1459
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RawLine represents an IRC line.
|
||||
type RawLine struct {
|
||||
Source string `json:"source"`
|
||||
Verb string `json:"verb"`
|
||||
Args []string `json:"args"`
|
||||
Tags map[string]string `json:"tags"`
|
||||
Raw string `json:"-"` // Deprecated
|
||||
}
|
||||
|
||||
// NewRawLine creates a new line and split out an RFC 1459 frame to a RawLine. This will
|
||||
// not return an error if it fails.
|
||||
func NewRawLine(input string) (line *RawLine) {
|
||||
line = &RawLine{
|
||||
Raw: input,
|
||||
}
|
||||
|
||||
split := strings.Split(input, " ")
|
||||
|
||||
if split[0][0] == ':' {
|
||||
line.Source = split[0][1:]
|
||||
line.Verb = split[1]
|
||||
split = split[2:]
|
||||
} else {
|
||||
line.Source = ""
|
||||
line.Verb = split[0]
|
||||
split = split[1:]
|
||||
}
|
||||
|
||||
argstring := strings.Join(split, " ")
|
||||
extparam := strings.Split(argstring, " :")
|
||||
|
||||
if len(extparam) > 1 {
|
||||
ext := strings.Join(extparam[1:], " :")
|
||||
args := strings.Split(extparam[0], " ")
|
||||
|
||||
line.Args = append(args, ext)
|
||||
} else {
|
||||
line.Args = split
|
||||
}
|
||||
|
||||
if len(line.Args) == 0 {
|
||||
line.Args = []string{""}
|
||||
} else if line.Args[0][0] == ':' {
|
||||
line.Args[0] = strings.TrimPrefix(line.Args[0], ":")
|
||||
}
|
||||
|
||||
line.Verb = strings.ToUpper(line.Verb)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// String returns the serialized form of a RawLine as an RFC 1459 frame.
|
||||
func (r *RawLine) String() (res string) {
|
||||
if r.Source != "" {
|
||||
res = res + fmt.Sprintf(":%s ", r.Source)
|
||||
}
|
||||
|
||||
res = res + fmt.Sprintf("%s", r.Verb)
|
||||
|
||||
for i, arg := range r.Args {
|
||||
res = res + " "
|
||||
|
||||
if i == len(r.Args)-1 { // Make the last part of the line an extparam
|
||||
res = res + ":"
|
||||
}
|
||||
|
||||
res = res + arg
|
||||
}
|
||||
|
||||
return
|
||||
}
|
Loading…
Reference in New Issue