markdown support

for active, info, head, tail, stats
This commit is contained in:
pyed 2016-07-08 01:54:47 +03:00
parent 2fd5c8a1ce
commit 09cb682f86

View File

@ -38,6 +38,14 @@ var (
interval time.Duration = 2 interval time.Duration = 2
// duration controls how many intervals will happen // duration controls how many intervals will happen
duration = 10 duration = 10
// since telegram's markdown can't be escaped, we have to replace some chars
// affects only markdown users: info, active, head, tail
mdReplacer = strings.NewReplacer("*", "•",
"[", "(",
"]", ")",
"_", "-",
"`", "'")
) )
// init flags // init flags
@ -225,7 +233,7 @@ func main() {
default: default:
// no such command, try help // no such command, try help
go send("no such command, try /help", update.Message.Chat.ID) go send("no such command, try /help", update.Message.Chat.ID, false)
} }
} }
@ -235,7 +243,7 @@ func main() {
func list(ud tgbotapi.Update, tokens []string) { func list(ud tgbotapi.Update, tokens []string) {
torrents, err := Client.GetTorrents() torrents, err := Client.GetTorrents()
if err != nil { if err != nil {
send("list: "+err.Error(), ud.Message.Chat.ID) send("list: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -245,7 +253,7 @@ func list(ud tgbotapi.Update, tokens []string) {
// (?i) for case insensitivity // (?i) for case insensitivity
regx, err := regexp.Compile("(?i)" + tokens[0]) regx, err := regexp.Compile("(?i)" + tokens[0])
if err != nil { if err != nil {
send("list: "+err.Error(), ud.Message.Chat.ID) send("list: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -261,11 +269,11 @@ func list(ud tgbotapi.Update, tokens []string) {
} }
if buf.Len() == 0 { if buf.Len() == 0 {
send("list: No torrents", ud.Message.Chat.ID) send("list: No torrents", ud.Message.Chat.ID, false)
return return
} }
send(buf.String(), ud.Message.Chat.ID) send(buf.String(), ud.Message.Chat.ID, false)
} }
// head will list the first 5 or n torrents // head will list the first 5 or n torrents
@ -278,14 +286,14 @@ func head(ud tgbotapi.Update, tokens []string) {
if len(tokens) > 0 { if len(tokens) > 0 {
n, err = strconv.Atoi(tokens[0]) n, err = strconv.Atoi(tokens[0])
if err != nil { if err != nil {
send("head: argument must be a number", ud.Message.Chat.ID) send("head: argument must be a number", ud.Message.Chat.ID, false)
return return
} }
} }
torrents, err := Client.GetTorrents() torrents, err := Client.GetTorrents()
if err != nil { if err != nil {
send("head: "+err.Error(), ud.Message.Chat.ID) send("head: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -296,14 +304,18 @@ func head(ud tgbotapi.Update, tokens []string) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
for i := range torrents[:n] { for i := range torrents[:n] {
buf.WriteString(fmt.Sprintf("<%d> %s\n", torrents[i].ID, torrents[i].Name)) torrentName := mdReplacer.Replace(torrents[i].Name) // escape markdown
buf.WriteString(fmt.Sprintf("<*%d*> *%s*\n%s `%s` of `%s` (`%.1f%%`) ↓ `%s` ↑ `%s` R: `%s`\n\n",
torrents[i].ID, torrentName, torrents[i].TorrentStatus(), humanize.Bytes(torrents[i].Have()),
humanize.Bytes(torrents[i].SizeWhenDone), torrents[i].PercentDone*100, humanize.Bytes(torrents[i].RateDownload),
humanize.Bytes(torrents[i].RateUpload), torrents[i].Ratio()))
} }
if buf.Len() == 0 { if buf.Len() == 0 {
send("head: No torrents", ud.Message.Chat.ID) send("head: No torrents", ud.Message.Chat.ID, false)
return return
} }
send(buf.String(), ud.Message.Chat.ID) send(buf.String(), ud.Message.Chat.ID, true)
} }
@ -317,14 +329,14 @@ func tail(ud tgbotapi.Update, tokens []string) {
if len(tokens) > 0 { if len(tokens) > 0 {
n, err = strconv.Atoi(tokens[0]) n, err = strconv.Atoi(tokens[0])
if err != nil { if err != nil {
send("tail: argument must be a number", ud.Message.Chat.ID) send("tail: argument must be a number", ud.Message.Chat.ID, false)
return return
} }
} }
torrents, err := Client.GetTorrents() torrents, err := Client.GetTorrents()
if err != nil { if err != nil {
send("tail: "+err.Error(), ud.Message.Chat.ID) send("tail: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -335,21 +347,25 @@ func tail(ud tgbotapi.Update, tokens []string) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
for _, torrent := range torrents[len(torrents)-n:] { for _, torrent := range torrents[len(torrents)-n:] {
buf.WriteString(fmt.Sprintf("<%d> %s\n", torrent.ID, torrent.Name)) torrentName := mdReplacer.Replace(torrent.Name) // escape markdown
buf.WriteString(fmt.Sprintf("<*%d*> *%s*\n%s `%s` of `%s` (`%.1f%%`) ↓ `%s` ↑ `%s` R: `%s`\n\n",
torrent.ID, torrentName, torrent.TorrentStatus(), humanize.Bytes(torrent.Have()),
humanize.Bytes(torrent.SizeWhenDone), torrent.PercentDone*100, humanize.Bytes(torrent.RateDownload),
humanize.Bytes(torrent.RateUpload), torrent.Ratio()))
} }
if buf.Len() == 0 { if buf.Len() == 0 {
send("tail: No torrents", ud.Message.Chat.ID) send("tail: No torrents", ud.Message.Chat.ID, false)
return return
} }
send(buf.String(), ud.Message.Chat.ID) send(buf.String(), ud.Message.Chat.ID, true)
} }
// downs will send the names of torrents with status 'Downloading' or in queue to // downs will send the names of torrents with status 'Downloading' or in queue to
func downs(ud tgbotapi.Update) { func downs(ud tgbotapi.Update) {
torrents, err := Client.GetTorrents() torrents, err := Client.GetTorrents()
if err != nil { if err != nil {
send("downs: "+err.Error(), ud.Message.Chat.ID) send("downs: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -363,17 +379,17 @@ func downs(ud tgbotapi.Update) {
} }
if buf.Len() == 0 { if buf.Len() == 0 {
send("No downloads", ud.Message.Chat.ID) send("No downloads", ud.Message.Chat.ID, false)
return return
} }
send(buf.String(), ud.Message.Chat.ID) send(buf.String(), ud.Message.Chat.ID, false)
} }
// seeding will send the names of the torrents with the status 'Seeding' or in the queue to // seeding will send the names of the torrents with the status 'Seeding' or in the queue to
func seeding(ud tgbotapi.Update) { func seeding(ud tgbotapi.Update) {
torrents, err := Client.GetTorrents() torrents, err := Client.GetTorrents()
if err != nil { if err != nil {
send("seeding: "+err.Error(), ud.Message.Chat.ID) send("seeding: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -386,11 +402,11 @@ func seeding(ud tgbotapi.Update) {
} }
if buf.Len() == 0 { if buf.Len() == 0 {
send("No torrents seeding", ud.Message.Chat.ID) send("No torrents seeding", ud.Message.Chat.ID, false)
return return
} }
send(buf.String(), ud.Message.Chat.ID) send(buf.String(), ud.Message.Chat.ID, false)
} }
@ -398,7 +414,7 @@ func seeding(ud tgbotapi.Update) {
func paused(ud tgbotapi.Update) { func paused(ud tgbotapi.Update) {
torrents, err := Client.GetTorrents() torrents, err := Client.GetTorrents()
if err != nil { if err != nil {
send("paused: "+err.Error(), ud.Message.Chat.ID) send("paused: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -413,18 +429,18 @@ func paused(ud tgbotapi.Update) {
} }
if buf.Len() == 0 { if buf.Len() == 0 {
send("No paused torrents", ud.Message.Chat.ID) send("No paused torrents", ud.Message.Chat.ID, false)
return return
} }
send(buf.String(), ud.Message.Chat.ID) send(buf.String(), ud.Message.Chat.ID, false)
} }
// checking will send the names of torrents with the status 'verifying' or in the queue to // checking will send the names of torrents with the status 'verifying' or in the queue to
func checking(ud tgbotapi.Update) { func checking(ud tgbotapi.Update) {
torrents, err := Client.GetTorrents() torrents, err := Client.GetTorrents()
if err != nil { if err != nil {
send("checking: "+err.Error(), ud.Message.Chat.ID) send("checking: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -440,18 +456,18 @@ func checking(ud tgbotapi.Update) {
} }
if buf.Len() == 0 { if buf.Len() == 0 {
send("No torrents verifying", ud.Message.Chat.ID) send("No torrents verifying", ud.Message.Chat.ID, false)
return return
} }
send(buf.String(), ud.Message.Chat.ID) send(buf.String(), ud.Message.Chat.ID, false)
} }
// active will send torrents that are actively downloading or uploading // active will send torrents that are actively downloading or uploading
func active(ud tgbotapi.Update) { func active(ud tgbotapi.Update) {
torrents, err := Client.GetTorrents() torrents, err := Client.GetTorrents()
if err != nil { if err != nil {
send("active: "+err.Error(), ud.Message.Chat.ID) send("active: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -459,18 +475,20 @@ func active(ud tgbotapi.Update) {
for i := range torrents { for i := range torrents {
if torrents[i].RateDownload > 0 || if torrents[i].RateDownload > 0 ||
torrents[i].RateUpload > 0 { torrents[i].RateUpload > 0 {
buf.WriteString(fmt.Sprintf("<%d> %s\n%s %s of %s (%.1f%%) ↓ %s ↑ %s, R: %s\n\n", // escape markdown
torrents[i].ID, torrents[i].Name, torrents[i].TorrentStatus(), humanize.Bytes(torrents[i].Have()), torrentName := mdReplacer.Replace(torrents[i].Name)
buf.WriteString(fmt.Sprintf("<*%d*> *%s*\n%s `%s` of `%s` (`%.1f%%`) ↓ `%s` ↑ `%s` R: `%s`\n\n",
torrents[i].ID, torrentName, torrents[i].TorrentStatus(), humanize.Bytes(torrents[i].Have()),
humanize.Bytes(torrents[i].SizeWhenDone), torrents[i].PercentDone*100, humanize.Bytes(torrents[i].RateDownload), humanize.Bytes(torrents[i].SizeWhenDone), torrents[i].PercentDone*100, humanize.Bytes(torrents[i].RateDownload),
humanize.Bytes(torrents[i].RateUpload), torrents[i].Ratio())) humanize.Bytes(torrents[i].RateUpload), torrents[i].Ratio()))
} }
} }
if buf.Len() == 0 { if buf.Len() == 0 {
send("No active torrents", ud.Message.Chat.ID) send("No active torrents", ud.Message.Chat.ID, false)
return return
} }
msgID := send(buf.String(), ud.Message.Chat.ID) msgID := send(buf.String(), ud.Message.Chat.ID, true)
// keep the active list live for 'duration * interval' // keep the active list live for 'duration * interval'
time.Sleep(time.Second * interval) time.Sleep(time.Second * interval)
@ -488,8 +506,10 @@ func active(ud tgbotapi.Update) {
for i := range torrents { for i := range torrents {
if torrents[i].RateDownload > 0 || if torrents[i].RateDownload > 0 ||
torrents[i].RateUpload > 0 { torrents[i].RateUpload > 0 {
buf.WriteString(fmt.Sprintf("<%d> %s\n%s %s of %s (%.1f%%) ↓ %s ↑ %s, R: %s\n\n", // escape markdown
torrents[i].ID, torrents[i].Name, torrents[i].TorrentStatus(), humanize.Bytes(torrents[i].Have()), torrentName := mdReplacer.Replace(torrents[i].Name)
buf.WriteString(fmt.Sprintf("<*%d*> *%s*\n%s `%s` of `%s` (`%.1f%%`) ↓ `%s` ↑ `%s` R: `%s`\n\n",
torrents[i].ID, torrentName, torrents[i].TorrentStatus(), humanize.Bytes(torrents[i].Have()),
humanize.Bytes(torrents[i].SizeWhenDone), torrents[i].PercentDone*100, humanize.Bytes(torrents[i].RateDownload), humanize.Bytes(torrents[i].SizeWhenDone), torrents[i].PercentDone*100, humanize.Bytes(torrents[i].RateDownload),
humanize.Bytes(torrents[i].RateUpload), torrents[i].Ratio())) humanize.Bytes(torrents[i].RateUpload), torrents[i].Ratio()))
} }
@ -497,6 +517,7 @@ func active(ud tgbotapi.Update) {
// no need to check if it is empty, as if the buffer is empty telegram won't change the message // no need to check if it is empty, as if the buffer is empty telegram won't change the message
editConf := tgbotapi.NewEditMessageText(ud.Message.Chat.ID, msgID, buf.String()) editConf := tgbotapi.NewEditMessageText(ud.Message.Chat.ID, msgID, buf.String())
editConf.ParseMode = tgbotapi.ModeMarkdown
Bot.Send(editConf) Bot.Send(editConf)
time.Sleep(time.Second * interval) time.Sleep(time.Second * interval)
} }
@ -506,13 +527,16 @@ func active(ud tgbotapi.Update) {
for i := range torrents { for i := range torrents {
if torrents[i].RateDownload > 0 || if torrents[i].RateDownload > 0 ||
torrents[i].RateUpload > 0 { torrents[i].RateUpload > 0 {
buf.WriteString(fmt.Sprintf("<%d> %s\n%s %s of %s (%.1f%%) ↓ - B ↑ - B, R: %s\n\n", // escape markdown
torrents[i].ID, torrents[i].Name, torrents[i].TorrentStatus(), humanize.Bytes(torrents[i].Have()), torrentName := mdReplacer.Replace(torrents[i].Name)
buf.WriteString(fmt.Sprintf("<*%d*> *%s*\n%s `%s` of `%s` (`%.1f%%`) ↓ `-` ↑ `-` R: `%s`\n\n",
torrents[i].ID, torrentName, torrents[i].TorrentStatus(), humanize.Bytes(torrents[i].Have()),
humanize.Bytes(torrents[i].SizeWhenDone), torrents[i].PercentDone*100, torrents[i].Ratio())) humanize.Bytes(torrents[i].SizeWhenDone), torrents[i].PercentDone*100, torrents[i].Ratio()))
} }
} }
editConf := tgbotapi.NewEditMessageText(ud.Message.Chat.ID, msgID, buf.String()) editConf := tgbotapi.NewEditMessageText(ud.Message.Chat.ID, msgID, buf.String())
editConf.ParseMode = tgbotapi.ModeMarkdown
Bot.Send(editConf) Bot.Send(editConf)
} }
@ -521,7 +545,7 @@ func active(ud tgbotapi.Update) {
func errors(ud tgbotapi.Update) { func errors(ud tgbotapi.Update) {
torrents, err := Client.GetTorrents() torrents, err := Client.GetTorrents()
if err != nil { if err != nil {
send("errors: "+err.Error(), ud.Message.Chat.ID) send("errors: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -533,18 +557,19 @@ func errors(ud tgbotapi.Update) {
} }
} }
if buf.Len() == 0 { if buf.Len() == 0 {
send("No errors", ud.Message.Chat.ID) send("No errors", ud.Message.Chat.ID, false)
return return
} }
send(buf.String(), ud.Message.Chat.ID) send(buf.String(), ud.Message.Chat.ID, false)
} }
// sort changes torrents sorting // sort changes torrents sorting
func sort(ud tgbotapi.Update, tokens []string) { func sort(ud tgbotapi.Update, tokens []string) {
if len(tokens) == 0 { if len(tokens) == 0 {
send(`sort takes one of (id, name, age, size, progress, downspeed, upspeed, download, upload, ratio) send(`sort takes one of:
optionally start with (rev) for reversed order (*id, name, age, size, progress, downspeed, upspeed, download, upload, ratio*)
e.g. "sort rev size" to get biggest torrents first.`, ud.Message.Chat.ID) optionally start with (*rev*) for reversed order
e.g. "*sort rev size*" to get biggest torrents first.`, ud.Message.Chat.ID, true)
return return
} }
@ -616,15 +641,15 @@ func sort(ud tgbotapi.Update, tokens []string) {
} }
Client.SetSort(transmission.SortRatio) Client.SetSort(transmission.SortRatio)
default: default:
send("unkown sorting method", ud.Message.Chat.ID) send("unkown sorting method", ud.Message.Chat.ID, false)
return return
} }
if reversed { if reversed {
send("sort: reversed "+tokens[0], ud.Message.Chat.ID) send("sort: reversed "+tokens[0], ud.Message.Chat.ID, false)
return return
} }
send("sort: "+tokens[0], ud.Message.Chat.ID) send("sort: "+tokens[0], ud.Message.Chat.ID, false)
} }
var trackerRegex = regexp.MustCompile(`[https?|udp]://([^:/]*)`) var trackerRegex = regexp.MustCompile(`[https?|udp]://([^:/]*)`)
@ -633,7 +658,7 @@ var trackerRegex = regexp.MustCompile(`[https?|udp]://([^:/]*)`)
func trackers(ud tgbotapi.Update) { func trackers(ud tgbotapi.Update) {
torrents, err := Client.GetTorrents() torrents, err := Client.GetTorrents()
if err != nil { if err != nil {
send("trackers: "+err.Error(), ud.Message.Chat.ID) send("trackers: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -660,16 +685,16 @@ func trackers(ud tgbotapi.Update) {
} }
if buf.Len() == 0 { if buf.Len() == 0 {
send("No trackers!", ud.Message.Chat.ID) send("No trackers!", ud.Message.Chat.ID, false)
return return
} }
send(buf.String(), ud.Message.Chat.ID) send(buf.String(), ud.Message.Chat.ID, false)
} }
// add takes an URL to a .torrent file to add it to transmission // add takes an URL to a .torrent file to add it to transmission
func add(ud tgbotapi.Update, tokens []string) { func add(ud tgbotapi.Update, tokens []string) {
if len(tokens) == 0 { if len(tokens) == 0 {
send("add: needs atleast one URL", ud.Message.Chat.ID) send("add: needs atleast one URL", ud.Message.Chat.ID, false)
return return
} }
@ -679,16 +704,16 @@ func add(ud tgbotapi.Update, tokens []string) {
torrent, err := Client.ExecuteAddCommand(cmd) torrent, err := Client.ExecuteAddCommand(cmd)
if err != nil { if err != nil {
send("add: "+err.Error(), ud.Message.Chat.ID) send("add: "+err.Error(), ud.Message.Chat.ID, false)
continue continue
} }
// check if torrent.Name is empty, then an error happened // check if torrent.Name is empty, then an error happened
if torrent.Name == "" { if torrent.Name == "" {
send("add: error adding "+url, ud.Message.Chat.ID) send("add: error adding "+url, ud.Message.Chat.ID, false)
continue continue
} }
send(fmt.Sprintf("Added: <%d> %s", torrent.ID, torrent.Name), ud.Message.Chat.ID) send(fmt.Sprintf("Added: <%d> %s", torrent.ID, torrent.Name), ud.Message.Chat.ID, false)
} }
} }
@ -702,7 +727,7 @@ func receiveTorrent(ud tgbotapi.Update) {
fconfig := tgbotapi.FileConfig{ud.Message.Document.FileID} fconfig := tgbotapi.FileConfig{ud.Message.Document.FileID}
file, err := Bot.GetFile(fconfig) file, err := Bot.GetFile(fconfig)
if err != nil { if err != nil {
send("receiver: "+err.Error(), ud.Message.Chat.ID) send("receiver: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -714,7 +739,7 @@ func receiveTorrent(ud tgbotapi.Update) {
func search(ud tgbotapi.Update, tokens []string) { func search(ud tgbotapi.Update, tokens []string) {
// make sure that we got a query // make sure that we got a query
if len(tokens) == 0 { if len(tokens) == 0 {
send("search: needs an argument", ud.Message.Chat.ID) send("search: needs an argument", ud.Message.Chat.ID, false)
return return
} }
@ -722,13 +747,13 @@ func search(ud tgbotapi.Update, tokens []string) {
// "(?i)" for case insensitivity // "(?i)" for case insensitivity
regx, err := regexp.Compile("(?i)" + query) regx, err := regexp.Compile("(?i)" + query)
if err != nil { if err != nil {
send("search: "+err.Error(), ud.Message.Chat.ID) send("search: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
torrents, err := Client.GetTorrents() torrents, err := Client.GetTorrents()
if err != nil { if err != nil {
send("search: "+err.Error(), ud.Message.Chat.ID) send("search: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -739,10 +764,10 @@ func search(ud tgbotapi.Update, tokens []string) {
} }
} }
if buf.Len() == 0 { if buf.Len() == 0 {
send("No matches!", ud.Message.Chat.ID) send("No matches!", ud.Message.Chat.ID, false)
return return
} }
send(buf.String(), ud.Message.Chat.ID) send(buf.String(), ud.Message.Chat.ID, false)
} }
// latest takes n and returns the latest n torrents // latest takes n and returns the latest n torrents
@ -755,14 +780,14 @@ func latest(ud tgbotapi.Update, tokens []string) {
if len(tokens) > 0 { if len(tokens) > 0 {
n, err = strconv.Atoi(tokens[0]) n, err = strconv.Atoi(tokens[0])
if err != nil { if err != nil {
send("latest: argument must be a number", ud.Message.Chat.ID) send("latest: argument must be a number", ud.Message.Chat.ID, false)
return return
} }
} }
torrents, err := Client.GetTorrents() torrents, err := Client.GetTorrents()
if err != nil { if err != nil {
send("latest: "+err.Error(), ud.Message.Chat.ID) send("latest: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -779,30 +804,30 @@ func latest(ud tgbotapi.Update, tokens []string) {
buf.WriteString(fmt.Sprintf("<%d> %s\n", torrents[i].ID, torrents[i].Name)) buf.WriteString(fmt.Sprintf("<%d> %s\n", torrents[i].ID, torrents[i].Name))
} }
if buf.Len() == 0 { if buf.Len() == 0 {
send("latest: No torrents", ud.Message.Chat.ID) send("latest: No torrents", ud.Message.Chat.ID, false)
return return
} }
send(buf.String(), ud.Message.Chat.ID) send(buf.String(), ud.Message.Chat.ID, false)
} }
// info takes an id of a torrent and returns some info about it // info takes an id of a torrent and returns some info about it
func info(ud tgbotapi.Update, tokens []string) { func info(ud tgbotapi.Update, tokens []string) {
if len(tokens) == 0 { if len(tokens) == 0 {
send("info: needs a torrent ID number", ud.Message.Chat.ID) send("info: needs a torrent ID number", ud.Message.Chat.ID, false)
return return
} }
for _, id := range tokens { for _, id := range tokens {
torrentID, err := strconv.Atoi(id) torrentID, err := strconv.Atoi(id)
if err != nil { if err != nil {
send(fmt.Sprintf("info: %s is not a number", id), ud.Message.Chat.ID) send(fmt.Sprintf("info: %s is not a number", id), ud.Message.Chat.ID, false)
continue continue
} }
// get the torrent // get the torrent
torrent, err := Client.GetTorrent(torrentID) torrent, err := Client.GetTorrent(torrentID)
if err != nil { if err != nil {
send(fmt.Sprintf("info: Can't find a torrent with an ID of %d", torrentID), ud.Message.Chat.ID) send(fmt.Sprintf("info: Can't find a torrent with an ID of %d", torrentID), ud.Message.Chat.ID, false)
continue continue
} }
@ -816,14 +841,15 @@ func info(ud tgbotapi.Update, tokens []string) {
} }
// format the info // format the info
info := fmt.Sprintf("<%d> %s\n%s\t%s of %s (%.1f%%) ↓ %s ↑ %s, R: %s\nDL: %s, UP: %s\nAdded: %s, ETA: %s\nTrackers: %s", torrentName := mdReplacer.Replace(torrent.Name) // escape markdown
torrent.ID, torrent.Name, torrent.TorrentStatus(), humanize.Bytes(torrent.Have()), humanize.Bytes(torrent.SizeWhenDone), info := fmt.Sprintf("<*%d*> *%s*\n%s `%s` of `%s` (`%.1f%%`) ↓ `%s` ↑ `%s` R: `%s`\nDL: `%s` UP: `%s`\nAdded: `%s`, ETA: `%s`\nTrackers: `%s`",
torrent.ID, torrentName, torrent.TorrentStatus(), humanize.Bytes(torrent.Have()), humanize.Bytes(torrent.SizeWhenDone),
torrent.PercentDone*100, humanize.Bytes(torrent.RateDownload), humanize.Bytes(torrent.RateUpload), torrent.Ratio(), torrent.PercentDone*100, humanize.Bytes(torrent.RateDownload), humanize.Bytes(torrent.RateUpload), torrent.Ratio(),
humanize.Bytes(torrent.DownloadedEver), humanize.Bytes(torrent.UploadedEver), time.Unix(torrent.AddedDate, 0).Format(time.Stamp), humanize.Bytes(torrent.DownloadedEver), humanize.Bytes(torrent.UploadedEver), time.Unix(torrent.AddedDate, 0).Format(time.Stamp),
torrent.ETA(), trackers) torrent.ETA(), trackers)
// send it // send it
msgID := send(info, ud.Message.Chat.ID) msgID := send(info, ud.Message.Chat.ID, true)
// this go-routine will make the info live for 'duration * interval' // this go-routine will make the info live for 'duration * interval'
// takes trackers so we don't have to regex them over and over. // takes trackers so we don't have to regex them over and over.
@ -834,26 +860,30 @@ func info(ud tgbotapi.Update, tokens []string) {
continue // skip this iteration if there's an error retrieving the torrent's info continue // skip this iteration if there's an error retrieving the torrent's info
} }
info := fmt.Sprintf("<%d> %s\n%s\t%s of %s (%.1f%%) ↓ %s ↑ %s, R: %s\nDL: %s, UP: %s\nAdded: %s, ETA: %s\nTrackers: %s", torrentName := mdReplacer.Replace(torrent.Name)
torrent.ID, torrent.Name, torrent.TorrentStatus(), humanize.Bytes(torrent.Have()), humanize.Bytes(torrent.SizeWhenDone), info := fmt.Sprintf("<*%d*> *%s*\n%s `%s` of `%s` (`%.1f%%`) ↓ `%s` ↑ `%s` R: `%s`\nDL: `%s` UP: `%s`\nAdded: `%s`, ETA: `%s`\nTrackers: `%s`",
torrent.ID, torrentName, torrent.TorrentStatus(), humanize.Bytes(torrent.Have()), humanize.Bytes(torrent.SizeWhenDone),
torrent.PercentDone*100, humanize.Bytes(torrent.RateDownload), humanize.Bytes(torrent.RateUpload), torrent.Ratio(), torrent.PercentDone*100, humanize.Bytes(torrent.RateDownload), humanize.Bytes(torrent.RateUpload), torrent.Ratio(),
humanize.Bytes(torrent.DownloadedEver), humanize.Bytes(torrent.UploadedEver), time.Unix(torrent.AddedDate, 0).Format(time.Stamp), humanize.Bytes(torrent.DownloadedEver), humanize.Bytes(torrent.UploadedEver), time.Unix(torrent.AddedDate, 0).Format(time.Stamp),
torrent.ETA(), trackers) torrent.ETA(), trackers)
// update the message // update the message
editConf := tgbotapi.NewEditMessageText(ud.Message.Chat.ID, msgID, info) editConf := tgbotapi.NewEditMessageText(ud.Message.Chat.ID, msgID, info)
editConf.ParseMode = tgbotapi.ModeMarkdown
Bot.Send(editConf) Bot.Send(editConf)
time.Sleep(time.Second * interval) time.Sleep(time.Second * interval)
} }
// at the end write dashes to indicate that we are done being live. // at the end write dashes to indicate that we are done being live.
info := fmt.Sprintf("<%d> %s\n%s\t%s of %s (%.1f%%) ↓ - B ↑ - B, R: %s\nDL: %s, UP: %s\nAdded: %s, ETA: -\nTrackers: %s", torrentName := mdReplacer.Replace(torrent.Name)
torrent.ID, torrent.Name, torrent.TorrentStatus(), humanize.Bytes(torrent.Have()), humanize.Bytes(torrent.SizeWhenDone), info := fmt.Sprintf("<*%d*> *%s*\n%s `%s` of `%s` (`%.1f%%`) ↓ `- B` ↑ `- B` R: `%s`\nDL: `%s` UP: `%s`\nAdded: `%s`, ETA: `-`\nTrackers: `%s`",
torrent.ID, torrentName, torrent.TorrentStatus(), humanize.Bytes(torrent.Have()), humanize.Bytes(torrent.SizeWhenDone),
torrent.PercentDone*100, torrent.Ratio(), humanize.Bytes(torrent.DownloadedEver), humanize.Bytes(torrent.UploadedEver), torrent.PercentDone*100, torrent.Ratio(), humanize.Bytes(torrent.DownloadedEver), humanize.Bytes(torrent.UploadedEver),
time.Unix(torrent.AddedDate, 0).Format(time.Stamp), trackers) time.Unix(torrent.AddedDate, 0).Format(time.Stamp), trackers)
editConf := tgbotapi.NewEditMessageText(ud.Message.Chat.ID, msgID, info) editConf := tgbotapi.NewEditMessageText(ud.Message.Chat.ID, msgID, info)
editConf.ParseMode = tgbotapi.ModeMarkdown
Bot.Send(editConf) Bot.Send(editConf)
}(trackers, torrentID, msgID) }(trackers, torrentID, msgID)
} }
@ -863,38 +893,38 @@ func info(ud tgbotapi.Update, tokens []string) {
func stop(ud tgbotapi.Update, tokens []string) { func stop(ud tgbotapi.Update, tokens []string) {
// make sure that we got at least one argument // make sure that we got at least one argument
if len(tokens) == 0 { if len(tokens) == 0 {
send("stop: needs an argument", ud.Message.Chat.ID) send("stop: needs an argument", ud.Message.Chat.ID, false)
return return
} }
// if the first argument is 'all' then stop all torrents // if the first argument is 'all' then stop all torrents
if tokens[0] == "all" { if tokens[0] == "all" {
if err := Client.StopAll(); err != nil { if err := Client.StopAll(); err != nil {
send("stop: error occurred while stopping some torrents", ud.Message.Chat.ID) send("stop: error occurred while stopping some torrents", ud.Message.Chat.ID, false)
return return
} }
send("stopped all torrents", ud.Message.Chat.ID) send("stopped all torrents", ud.Message.Chat.ID, false)
return return
} }
for _, id := range tokens { for _, id := range tokens {
num, err := strconv.Atoi(id) num, err := strconv.Atoi(id)
if err != nil { if err != nil {
send(fmt.Sprintf("stop: %s is not a number", id), ud.Message.Chat.ID) send(fmt.Sprintf("stop: %s is not a number", id), ud.Message.Chat.ID, false)
continue continue
} }
status, err := Client.StopTorrent(num) status, err := Client.StopTorrent(num)
if err != nil { if err != nil {
send("stop: "+err.Error(), ud.Message.Chat.ID) send("stop: "+err.Error(), ud.Message.Chat.ID, false)
continue continue
} }
torrent, err := Client.GetTorrent(num) torrent, err := Client.GetTorrent(num)
if err != nil { if err != nil {
send(fmt.Sprintf("[fail] stop: No torrent with an ID of %d", num), ud.Message.Chat.ID) send(fmt.Sprintf("[fail] stop: No torrent with an ID of %d", num), ud.Message.Chat.ID, false)
return return
} }
send(fmt.Sprintf("[%s] stop: %s", status, torrent.Name), ud.Message.Chat.ID) send(fmt.Sprintf("[%s] stop: %s", status, torrent.Name), ud.Message.Chat.ID, false)
} }
} }
@ -902,17 +932,17 @@ func stop(ud tgbotapi.Update, tokens []string) {
func start(ud tgbotapi.Update, tokens []string) { func start(ud tgbotapi.Update, tokens []string) {
// make sure that we got at least one argument // make sure that we got at least one argument
if len(tokens) == 0 { if len(tokens) == 0 {
send("start: needs an argument", ud.Message.Chat.ID) send("start: needs an argument", ud.Message.Chat.ID, false)
return return
} }
// if the first argument is 'all' then start all torrents // if the first argument is 'all' then start all torrents
if tokens[0] == "all" { if tokens[0] == "all" {
if err := Client.StartAll(); err != nil { if err := Client.StartAll(); err != nil {
send("start: error occurred while starting some torrents", ud.Message.Chat.ID) send("start: error occurred while starting some torrents", ud.Message.Chat.ID, false)
return return
} }
send("started all torrents", ud.Message.Chat.ID) send("started all torrents", ud.Message.Chat.ID, false)
return return
} }
@ -920,21 +950,21 @@ func start(ud tgbotapi.Update, tokens []string) {
for _, id := range tokens { for _, id := range tokens {
num, err := strconv.Atoi(id) num, err := strconv.Atoi(id)
if err != nil { if err != nil {
send(fmt.Sprintf("start: %s is not a number", id), ud.Message.Chat.ID) send(fmt.Sprintf("start: %s is not a number", id), ud.Message.Chat.ID, false)
continue continue
} }
status, err := Client.StartTorrent(num) status, err := Client.StartTorrent(num)
if err != nil { if err != nil {
send("stop: "+err.Error(), ud.Message.Chat.ID) send("stop: "+err.Error(), ud.Message.Chat.ID, false)
continue continue
} }
torrent, err := Client.GetTorrent(num) torrent, err := Client.GetTorrent(num)
if err != nil { if err != nil {
send(fmt.Sprintf("[fail] start: No torrent with an ID of %d", num), ud.Message.Chat.ID) send(fmt.Sprintf("[fail] start: No torrent with an ID of %d", num), ud.Message.Chat.ID, false)
return return
} }
send(fmt.Sprintf("[%s] start: %s", status, torrent.Name), ud.Message.Chat.ID) send(fmt.Sprintf("[%s] start: %s", status, torrent.Name), ud.Message.Chat.ID, false)
} }
} }
@ -942,17 +972,17 @@ func start(ud tgbotapi.Update, tokens []string) {
func check(ud tgbotapi.Update, tokens []string) { func check(ud tgbotapi.Update, tokens []string) {
// make sure that we got at least one argument // make sure that we got at least one argument
if len(tokens) == 0 { if len(tokens) == 0 {
send("check: needs an argument", ud.Message.Chat.ID) send("check: needs an argument", ud.Message.Chat.ID, false)
return return
} }
// if the first argument is 'all' then start all torrents // if the first argument is 'all' then start all torrents
if tokens[0] == "all" { if tokens[0] == "all" {
if err := Client.VerifyAll(); err != nil { if err := Client.VerifyAll(); err != nil {
send("check: error occurred while verifying some torrents", ud.Message.Chat.ID) send("check: error occurred while verifying some torrents", ud.Message.Chat.ID, false)
return return
} }
send("verifying all torrents", ud.Message.Chat.ID) send("verifying all torrents", ud.Message.Chat.ID, false)
return return
} }
@ -960,21 +990,21 @@ func check(ud tgbotapi.Update, tokens []string) {
for _, id := range tokens { for _, id := range tokens {
num, err := strconv.Atoi(id) num, err := strconv.Atoi(id)
if err != nil { if err != nil {
send(fmt.Sprintf("check: %s is not a number", id), ud.Message.Chat.ID) send(fmt.Sprintf("check: %s is not a number", id), ud.Message.Chat.ID, false)
continue continue
} }
status, err := Client.VerifyTorrent(num) status, err := Client.VerifyTorrent(num)
if err != nil { if err != nil {
send("stop: "+err.Error(), ud.Message.Chat.ID) send("stop: "+err.Error(), ud.Message.Chat.ID, false)
continue continue
} }
torrent, err := Client.GetTorrent(num) torrent, err := Client.GetTorrent(num)
if err != nil { if err != nil {
send(fmt.Sprintf("[fail] check: No torrent with an ID of %d", num), ud.Message.Chat.ID) send(fmt.Sprintf("[fail] check: No torrent with an ID of %d", num), ud.Message.Chat.ID, false)
return return
} }
send(fmt.Sprintf("[%s] check: %s", status, torrent.Name), ud.Message.Chat.ID) send(fmt.Sprintf("[%s] check: %s", status, torrent.Name), ud.Message.Chat.ID, false)
} }
} }
@ -983,26 +1013,26 @@ func check(ud tgbotapi.Update, tokens []string) {
func stats(ud tgbotapi.Update) { func stats(ud tgbotapi.Update) {
stats, err := Client.GetStats() stats, err := Client.GetStats()
if err != nil { if err != nil {
send("stats: "+err.Error(), ud.Message.Chat.ID) send("stats: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
msg := fmt.Sprintf( msg := fmt.Sprintf(
` `
Total torrents: %d Total: *%d*
Active: %d Active: *%d*
Paused: %d Paused: *%d*
- Current Stats - _Current Stats_
Downloaded: %s Downloaded: *%s*
Uploaded: %s Uploaded: *%s*
Running time: %d seconds Running time: *%d seconds*
- Total Stats - _Accumulative Stats_
Sessions: %d Sessions: *%d*
Downloaded: %s Downloaded: *%s*
Uploaded: %s Uploaded: *%s*
Total Running time: %d seconds Total Running time: *%d seconds*
`, `,
stats.TorrentCount, stats.TorrentCount,
@ -1017,7 +1047,7 @@ func stats(ud tgbotapi.Update) {
stats.CumulativeStats.SecondsActive, stats.CumulativeStats.SecondsActive,
) )
send(msg, ud.Message.Chat.ID) send(msg, ud.Message.Chat.ID, true)
} }
// speed will echo back the current download and upload speeds // speed will echo back the current download and upload speeds
@ -1027,7 +1057,7 @@ func speed(ud tgbotapi.Update) {
for i := 0; i < duration; i++ { for i := 0; i < duration; i++ {
stats, err := Client.GetStats() stats, err := Client.GetStats()
if err != nil { if err != nil {
send("speed: "+err.Error(), ud.Message.Chat.ID) send("speed: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -1035,7 +1065,7 @@ func speed(ud tgbotapi.Update) {
// if we haven't send a message, send it and save the message ID to edit it the next iteration // if we haven't send a message, send it and save the message ID to edit it the next iteration
if msgID == 0 { if msgID == 0 {
msgID = send(msg, ud.Message.Chat.ID) msgID = send(msg, ud.Message.Chat.ID, false)
time.Sleep(time.Second * interval) time.Sleep(time.Second * interval)
continue continue
} }
@ -1055,7 +1085,7 @@ func speed(ud tgbotapi.Update) {
func count(ud tgbotapi.Update) { func count(ud tgbotapi.Update) {
torrents, err := Client.GetTorrents() torrents, err := Client.GetTorrents()
if err != nil { if err != nil {
send("count: "+err.Error(), ud.Message.Chat.ID) send("count: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
@ -1083,7 +1113,7 @@ func count(ud tgbotapi.Update) {
msg := fmt.Sprintf("Downloading: %d\nSeeding: %d\nPaused: %d\nVerifying: %d\n\n- Waiting to -\nDownload: %d\nSeed: %d\nVerify: %d\n\nTotal: %d", msg := fmt.Sprintf("Downloading: %d\nSeeding: %d\nPaused: %d\nVerifying: %d\n\n- Waiting to -\nDownload: %d\nSeed: %d\nVerify: %d\n\nTotal: %d",
downloading, seeding, stopped, checking, downloadingQ, seedingQ, checkingQ, len(torrents)) downloading, seeding, stopped, checking, downloadingQ, seedingQ, checkingQ, len(torrents))
send(msg, ud.Message.Chat.ID) send(msg, ud.Message.Chat.ID, false)
} }
@ -1091,7 +1121,7 @@ func count(ud tgbotapi.Update) {
func del(ud tgbotapi.Update, tokens []string) { func del(ud tgbotapi.Update, tokens []string) {
// make sure that we got an argument // make sure that we got an argument
if len(tokens) == 0 { if len(tokens) == 0 {
send("del: needs an ID", ud.Message.Chat.ID) send("del: needs an ID", ud.Message.Chat.ID, false)
return return
} }
@ -1099,17 +1129,17 @@ func del(ud tgbotapi.Update, tokens []string) {
for _, id := range tokens { for _, id := range tokens {
num, err := strconv.Atoi(id) num, err := strconv.Atoi(id)
if err != nil { if err != nil {
send(fmt.Sprintf("del: %s is not an ID", id), ud.Message.Chat.ID) send(fmt.Sprintf("del: %s is not an ID", id), ud.Message.Chat.ID, false)
return return
} }
name, err := Client.DeleteTorrent(num, false) name, err := Client.DeleteTorrent(num, false)
if err != nil { if err != nil {
send("del: "+err.Error(), ud.Message.Chat.ID) send("del: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
send("Deleted: "+name, ud.Message.Chat.ID) send("Deleted: "+name, ud.Message.Chat.ID, false)
} }
} }
@ -1117,24 +1147,24 @@ func del(ud tgbotapi.Update, tokens []string) {
func deldata(ud tgbotapi.Update, tokens []string) { func deldata(ud tgbotapi.Update, tokens []string) {
// make sure that we got an argument // make sure that we got an argument
if len(tokens) == 0 { if len(tokens) == 0 {
send("deldata: needs an ID", ud.Message.Chat.ID) send("deldata: needs an ID", ud.Message.Chat.ID, false)
return return
} }
// loop over tokens to read each potential id // loop over tokens to read each potential id
for _, id := range tokens { for _, id := range tokens {
num, err := strconv.Atoi(id) num, err := strconv.Atoi(id)
if err != nil { if err != nil {
send(fmt.Sprintf("deldata: %s is not an ID", id), ud.Message.Chat.ID) send(fmt.Sprintf("deldata: %s is not an ID", id), ud.Message.Chat.ID, false)
return return
} }
name, err := Client.DeleteTorrent(num, true) name, err := Client.DeleteTorrent(num, true)
if err != nil { if err != nil {
send("deldata: "+err.Error(), ud.Message.Chat.ID) send("deldata: "+err.Error(), ud.Message.Chat.ID, false)
return return
} }
send("Deleted with data: "+name, ud.Message.Chat.ID) send("Deleted with data: "+name, ud.Message.Chat.ID, false)
} }
} }
@ -1142,11 +1172,11 @@ func deldata(ud tgbotapi.Update, tokens []string) {
// version sends transmission version + transmission-telegram version // version sends transmission version + transmission-telegram version
func version(ud tgbotapi.Update) { func version(ud tgbotapi.Update) {
send(fmt.Sprintf("Transmission %s\nTransmission-telegram %s", Client.Version(), VERSION), ud.Message.Chat.ID) send(fmt.Sprintf("Transmission %s\nTransmission-telegram %s", Client.Version(), VERSION), ud.Message.Chat.ID, false)
} }
// send takes a chat id and a message to send, returns the message id of the send message // send takes a chat id and a message to send, returns the message id of the send message
func send(text string, chatID int64) int { func send(text string, chatID int64, markdown bool) int {
// set typing action // set typing action
action := tgbotapi.NewChatAction(chatID, tgbotapi.ChatTyping) action := tgbotapi.NewChatAction(chatID, tgbotapi.ChatTyping)
Bot.Send(action) Bot.Send(action)
@ -1158,6 +1188,9 @@ LenCheck:
if msgRuneCount > 4096 { if msgRuneCount > 4096 {
msg := tgbotapi.NewMessage(chatID, text[:4095]) msg := tgbotapi.NewMessage(chatID, text[:4095])
msg.DisableWebPagePreview = true msg.DisableWebPagePreview = true
if markdown {
msg.ParseMode = tgbotapi.ModeMarkdown
}
// send current chunk // send current chunk
if _, err := Bot.Send(msg); err != nil { if _, err := Bot.Send(msg); err != nil {
@ -1172,6 +1205,9 @@ LenCheck:
// if msgRuneCount < 4096, send it normally // if msgRuneCount < 4096, send it normally
msg := tgbotapi.NewMessage(chatID, text) msg := tgbotapi.NewMessage(chatID, text)
msg.DisableWebPagePreview = true msg.DisableWebPagePreview = true
if markdown {
msg.ParseMode = tgbotapi.ModeMarkdown
}
resp, err := Bot.Send(msg) resp, err := Bot.Send(msg)
if err != nil { if err != nil {