2016-06-19 08:07:00 +03:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2016-06-20 05:09:41 +03:00
|
|
|
"bytes"
|
2016-06-19 08:07:00 +03:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
2016-06-20 05:09:41 +03:00
|
|
|
"unicode/utf8"
|
2016-06-19 08:07:00 +03:00
|
|
|
|
2016-06-19 22:02:15 +03:00
|
|
|
"gopkg.in/telegram-bot-api.v4"
|
|
|
|
|
2016-06-19 08:07:00 +03:00
|
|
|
"github.com/pyed/transmission"
|
|
|
|
)
|
|
|
|
|
2016-06-19 22:33:57 +03:00
|
|
|
const VERSION = "0.0"
|
|
|
|
|
2016-06-19 08:07:00 +03:00
|
|
|
var (
|
2016-06-19 22:02:15 +03:00
|
|
|
// flags
|
2016-06-19 22:33:57 +03:00
|
|
|
BotToken string
|
|
|
|
Master string
|
|
|
|
RpcUrl string
|
|
|
|
Username string
|
|
|
|
Password string
|
2016-06-19 08:07:00 +03:00
|
|
|
|
2016-06-19 22:02:15 +03:00
|
|
|
// transmission
|
|
|
|
Client *transmission.Client
|
|
|
|
|
|
|
|
// telegram
|
|
|
|
Bot *tgbotapi.BotAPI
|
|
|
|
Updates <-chan tgbotapi.Update
|
2016-06-19 08:07:00 +03:00
|
|
|
)
|
|
|
|
|
2016-06-19 22:02:15 +03:00
|
|
|
// init flags
|
2016-06-19 08:07:00 +03:00
|
|
|
func init() {
|
|
|
|
// define arguments and parse them.
|
2016-06-19 22:33:57 +03:00
|
|
|
flag.StringVar(&BotToken, "token", "", "Telegram bot token")
|
|
|
|
flag.StringVar(&Master, "master", "", "Your telegram handler, So the bot will only respond to you")
|
|
|
|
flag.StringVar(&RpcUrl, "url", "http://localhost:9091/transmission/rpc", "Transmission RPC URL")
|
|
|
|
flag.StringVar(&Username, "username", "", "Transmission username")
|
|
|
|
flag.StringVar(&Password, "password", "", "Transmission password")
|
2016-06-19 08:07:00 +03:00
|
|
|
|
|
|
|
// set the usage message
|
|
|
|
flag.Usage = func() {
|
|
|
|
fmt.Fprintln(os.Stderr, "Usage: transmission-bot -token=<TOKEN> -master=<@tuser> -url=[http://] -username=[user] -password=[pass]\n")
|
|
|
|
flag.PrintDefaults()
|
|
|
|
}
|
|
|
|
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
// make sure that we have the two madatory arguments: telegram token & master's handler.
|
2016-06-19 22:33:57 +03:00
|
|
|
if BotToken == "" ||
|
|
|
|
Master == "" {
|
2016-06-19 08:07:00 +03:00
|
|
|
fmt.Fprintf(os.Stderr, "Error: Mandatory argument missing!\n\n")
|
|
|
|
flag.Usage()
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2016-06-20 05:09:41 +03:00
|
|
|
|
|
|
|
// make sure that the handler doesn't contain @
|
|
|
|
if strings.Contains(Master, "@") {
|
|
|
|
Master = strings.Replace(Master, "@", "", -1)
|
|
|
|
}
|
2016-06-19 22:02:15 +03:00
|
|
|
}
|
2016-06-19 08:07:00 +03:00
|
|
|
|
2016-06-19 22:02:15 +03:00
|
|
|
// init transmission
|
|
|
|
func init() {
|
2016-06-19 08:07:00 +03:00
|
|
|
// set transmission.Config, needed to establish a connection with transmission
|
|
|
|
conf := transmission.Config{
|
2016-06-19 22:33:57 +03:00
|
|
|
Address: RpcUrl,
|
|
|
|
User: Username,
|
|
|
|
Password: Password,
|
2016-06-19 08:07:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// transmission.New() never returns an error, we will ignore it and test with client.Session.Update()
|
2016-06-19 22:02:15 +03:00
|
|
|
Client, _ = transmission.New(conf)
|
|
|
|
if err := Client.Session.Update(); err != nil {
|
2016-06-19 08:07:00 +03:00
|
|
|
|
|
|
|
// try to predict the error message, as it vague coming from pyed/transmission
|
|
|
|
if strings.HasPrefix(err.Error(), "invalid character") { // means the user or the pass is wrong.
|
2016-06-19 22:02:15 +03:00
|
|
|
fmt.Fprintf(os.Stderr, "Transmission's Username or Password is wrong.\n\n")
|
2016-06-19 08:07:00 +03:00
|
|
|
|
|
|
|
} else { // any other error is probaby because of the URL
|
2016-06-19 22:33:57 +03:00
|
|
|
fmt.Fprintf(os.Stderr, "Error: Couldn't connect to: %s\n", RpcUrl)
|
2016-06-19 22:02:15 +03:00
|
|
|
fmt.Fprintf(os.Stderr, "Make sure to pass the right full RPC URL e.g. http://localhost:9091/transmission/rpc\n\n")
|
2016-06-19 08:07:00 +03:00
|
|
|
}
|
|
|
|
// send the vague error message too
|
2016-06-19 22:02:15 +03:00
|
|
|
fmt.Fprintf(os.Stderr, "JSONError: %s\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// init telegram
|
|
|
|
func init() {
|
|
|
|
// authorize using the token
|
|
|
|
var err error
|
2016-06-19 22:33:57 +03:00
|
|
|
Bot, err = tgbotapi.NewBotAPI(BotToken)
|
2016-06-19 22:02:15 +03:00
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "Telegram Error: %s\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2016-06-20 05:09:41 +03:00
|
|
|
fmt.Fprintf(os.Stdout, "Authorized on %s", Bot.Self.UserName)
|
2016-06-19 22:02:15 +03:00
|
|
|
|
|
|
|
// get a channel and sign it to 'Updates'
|
|
|
|
u := tgbotapi.NewUpdate(0)
|
|
|
|
u.Timeout = 60
|
|
|
|
|
|
|
|
Updates, err = Bot.GetUpdatesChan(u)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "Telegram Error: %s\n", err)
|
2016-06-19 08:07:00 +03:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-19 22:33:57 +03:00
|
|
|
func main() {
|
|
|
|
for update := range Updates {
|
|
|
|
// ignore anyone other than 'master'
|
2016-06-20 05:09:41 +03:00
|
|
|
if strings.ToLower(update.Message.From.UserName) != strings.ToLower(Master) {
|
2016-06-19 22:33:57 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
// ignore edited messages
|
|
|
|
if update.Message == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// tokenize the update
|
|
|
|
tokens := strings.Split(update.Message.Text, " ")
|
|
|
|
command := strings.ToLower(tokens[0])
|
|
|
|
|
|
|
|
switch command {
|
|
|
|
case "list", "/list":
|
|
|
|
// list torrents
|
2016-06-20 05:09:41 +03:00
|
|
|
go list(&update)
|
|
|
|
|
2016-06-19 22:33:57 +03:00
|
|
|
case "downs", "/downs":
|
|
|
|
// list downloading
|
|
|
|
case "active", "/active":
|
|
|
|
// list active torrents
|
|
|
|
case "errors", "/errors":
|
|
|
|
// list torrents with errors
|
|
|
|
case "trackers", "/trackers":
|
|
|
|
// list trackers
|
|
|
|
case "add", "/add":
|
|
|
|
// takes url to a torrent to add it
|
|
|
|
case "search", "/search":
|
|
|
|
// search for a torrent
|
|
|
|
case "latest", "/latest":
|
|
|
|
// get the latest torrents
|
|
|
|
case "info", "/info":
|
|
|
|
// gets info on specific torrent
|
|
|
|
case "stop", "/stop":
|
|
|
|
// stop one torrent or more
|
|
|
|
case "stopall", "/stopall":
|
|
|
|
// stops all the torrents
|
|
|
|
case "start", "/start":
|
|
|
|
// starts one torrent or more
|
|
|
|
case "startall", "/startall":
|
|
|
|
// starts all the torrents
|
|
|
|
case "stats", "/stats":
|
|
|
|
// print transmission stats
|
|
|
|
case "speed", "/speed":
|
|
|
|
// print current download and upload speeds
|
|
|
|
case "del", "/del":
|
|
|
|
// deletes a torrent but keep its data
|
|
|
|
case "deldata", "/deldata":
|
|
|
|
// deletes a torrents and its data
|
|
|
|
case "help", "/help":
|
|
|
|
// prints a help message
|
|
|
|
case "version", "/version":
|
|
|
|
// print transmission and transmission-telegram versions
|
|
|
|
default:
|
|
|
|
// no such command, try help
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-06-20 05:09:41 +03:00
|
|
|
|
|
|
|
// list will form and send a list of all the torrents
|
|
|
|
func list(ud *tgbotapi.Update) {
|
|
|
|
torrents, err := Client.GetTorrents()
|
|
|
|
if err != nil {
|
|
|
|
send("list: "+err.Error(), ud.Message.Chat.ID)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
for i := range torrents {
|
|
|
|
buf.WriteString(fmt.Sprintf("<%d> %s\n", torrents[i].ID, torrents[i].Name))
|
|
|
|
}
|
|
|
|
|
|
|
|
if buf.Len() == 0 {
|
|
|
|
send("No torrents exist!", ud.Message.Chat.ID)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
send(buf.String(), ud.Message.Chat.ID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// send takes a chat id and a message to send.
|
|
|
|
func send(text string, chatID int64) {
|
|
|
|
// set typing action
|
|
|
|
action := tgbotapi.NewChatAction(chatID, tgbotapi.ChatTyping)
|
|
|
|
Bot.Send(action)
|
|
|
|
|
|
|
|
// check the rune count, telegram is limited to 4096 chars per message;
|
|
|
|
// so if our message is > 4096, split it in chunks the send them.
|
|
|
|
msgRuneCount := utf8.RuneCountInString(text)
|
|
|
|
LenCheck:
|
|
|
|
if msgRuneCount > 4096 {
|
|
|
|
msg := tgbotapi.NewMessage(chatID, text[:4095])
|
|
|
|
|
|
|
|
// send current chunk
|
|
|
|
if _, err := Bot.Send(msg); err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "send error: %s\n", err)
|
|
|
|
}
|
|
|
|
// move to the next chunk
|
|
|
|
text = text[4095:]
|
|
|
|
msgRuneCount = utf8.RuneCountInString(text)
|
|
|
|
goto LenCheck
|
|
|
|
}
|
|
|
|
|
|
|
|
// if msgRuneCount < 4096, send it normally
|
|
|
|
msg := tgbotapi.NewMessage(chatID, text)
|
|
|
|
if _, err := Bot.Send(msg); err != nil {
|
|
|
|
fmt.Fprint(os.Stderr, "send error: %s\n", err)
|
|
|
|
}
|
|
|
|
}
|