You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
5.9 KiB
227 lines
5.9 KiB
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/spf13/pflag"
|
|
"gopkg.in/yaml.v2"
|
|
)
|
|
|
|
var Version = "development"
|
|
|
|
type Config struct {
|
|
ChatID string `yaml:"CHATID"`
|
|
Token string `yaml:"TOKEN"`
|
|
Timeout int `yaml:"TIMEOUT"`
|
|
}
|
|
|
|
var (
|
|
configFile string
|
|
chatID string
|
|
token string
|
|
parse_mode string
|
|
timeout int
|
|
silent bool
|
|
versionFlag bool
|
|
helpFlag bool
|
|
)
|
|
|
|
func init() {
|
|
pflag.StringVarP(&configFile, "config", "f", "", "Configuration file path")
|
|
pflag.StringVarP(&chatID, "chatid", "c", "", "Chat ID value")
|
|
pflag.StringVarP(&token, "token", "t", "", "Bot token value")
|
|
pflag.StringVarP(&parse_mode, "parse", "p", "MarkdownV2", "Telegram message parse mode")
|
|
pflag.IntVarP(&timeout, "timeout", "", 10, "Timeout value")
|
|
pflag.BoolVarP(&silent, "silent", "s", false, "Disable notification sound")
|
|
pflag.BoolVarP(&versionFlag, "version", "v", false, "Print version information and exit")
|
|
pflag.BoolVarP(&helpFlag, "help", "h", false, "Prints this message")
|
|
}
|
|
|
|
func main() {
|
|
//Set version message
|
|
versionMsg := fmt.Sprintf("\033[0;36mtbotsend\033[0m - Version: \033[0;33m%s\033[0m", Version)
|
|
pflag.Usage = func() {
|
|
fmt.Fprintf(os.Stderr, "%s\n", versionMsg)
|
|
fmt.Fprintf(os.Stderr, "%s\n", strings.Repeat("-", len(stripANSI(versionMsg))))
|
|
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] {MESSAGE}\n\n", os.Args[0])
|
|
fmt.Fprintf(os.Stderr, "Options:\n")
|
|
pflag.PrintDefaults()
|
|
fmt.Fprintf(os.Stderr, "\nExamples:\n")
|
|
fmt.Fprintf(os.Stderr, " %s -f config.yaml -c 123456 -t botToken123 --silent Hello world!\n", os.Args[0])
|
|
fmt.Fprintf(os.Stderr, " %s --version\n", os.Args[0])
|
|
}
|
|
pflag.Parse()
|
|
|
|
if versionFlag {
|
|
//fmt.Printf("tbotsend version %s\n", Version)
|
|
fmt.Printf("%s\n", versionMsg)
|
|
os.Exit(0)
|
|
}
|
|
|
|
if helpFlag {
|
|
pflag.Usage()
|
|
os.Exit(0)
|
|
}
|
|
|
|
args := pflag.Args()
|
|
message := strings.Join(args, " ")
|
|
|
|
// Print the date and time for logging purposes
|
|
fmt.Println(" -- " + time.Now().Format("2006-01-02 15:04:05"))
|
|
|
|
var config Config
|
|
configUsed := ""
|
|
|
|
// Check configuration
|
|
if (chatID != "" || token != "") && configFile != "" {
|
|
printError("Cannot use both configuration file and configuration flags")
|
|
os.Exit(2)
|
|
} else if chatID != "" && token != "" {
|
|
// Use configuration flags
|
|
config.ChatID = chatID
|
|
config.Token = token
|
|
config.Timeout = timeout
|
|
configUsed = "MANUAL"
|
|
} else {
|
|
// Use configuration file
|
|
if configFile == "" {
|
|
// Use default config file at ${HOME}/.config/tbotsend.yaml
|
|
homeDir, err := os.UserHomeDir()
|
|
if err != nil {
|
|
printError(fmt.Sprintf("Failed to get user home directory: %v", err))
|
|
os.Exit(2)
|
|
}
|
|
configFile = filepath.Join(homeDir, ".config", "tbotsend.yaml")
|
|
}
|
|
|
|
// Read configuration file
|
|
data, err := ioutil.ReadFile(configFile)
|
|
if err != nil {
|
|
// Try secondary configuration file at ${HOME}/.tbotsend.yaml
|
|
if configFile == filepath.Join(os.Getenv("HOME"), ".config", "tbotsend.yaml") {
|
|
secondaryConfigFile := filepath.Join(os.Getenv("HOME"), ".tbotsend.yaml")
|
|
data, err = ioutil.ReadFile(secondaryConfigFile)
|
|
if err != nil {
|
|
printError(fmt.Sprintf("Failed to read configuration file '%s' or '%s': %v", configFile, secondaryConfigFile, err))
|
|
os.Exit(2)
|
|
}
|
|
configFile = secondaryConfigFile
|
|
} else {
|
|
printError(fmt.Sprintf("Failed to read configuration file '%s': %v", configFile, err))
|
|
os.Exit(2)
|
|
}
|
|
}
|
|
|
|
// Parse YAML
|
|
err = yaml.Unmarshal(data, &config)
|
|
if err != nil {
|
|
printError(fmt.Sprintf("Failed to parse configuration file '%s': %v", configFile, err))
|
|
os.Exit(2)
|
|
}
|
|
// Set default timeout if not set
|
|
if config.Timeout == 0 {
|
|
config.Timeout = 10
|
|
}
|
|
configUsed = configFile
|
|
}
|
|
|
|
// Validate configuration
|
|
if config.ChatID == "" || config.Token == "" {
|
|
printError("Configuration is missing ChatID or Token")
|
|
os.Exit(2)
|
|
}
|
|
|
|
// Ensure message is provided
|
|
if message == "" {
|
|
printError("No message provided")
|
|
os.Exit(2)
|
|
}
|
|
|
|
// Prepare to send the message
|
|
urlStr := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", config.Token)
|
|
|
|
data := url.Values{}
|
|
data.Set("chat_id", config.ChatID)
|
|
data.Set("disable_web_page_preview", "1")
|
|
if silent {
|
|
data.Set("disable_notification", "true")
|
|
}
|
|
data.Set("text", message)
|
|
data.Set("parse_mode", parse_mode)
|
|
|
|
client := &http.Client{
|
|
Timeout: time.Duration(config.Timeout) * time.Second,
|
|
}
|
|
|
|
// Send the POST request
|
|
resp, err := client.PostForm(urlStr, data)
|
|
if err != nil {
|
|
printFailure(err.Error())
|
|
os.Exit(13)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Read the response body
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
printFailure(err.Error())
|
|
os.Exit(13)
|
|
}
|
|
|
|
// Parse the response to check for success
|
|
type TelegramResponse struct {
|
|
Ok bool `json:"ok"`
|
|
Description string `json:"description"`
|
|
}
|
|
|
|
var telegramResp TelegramResponse
|
|
err = json.Unmarshal(body, &telegramResp)
|
|
if err != nil {
|
|
printFailure(err.Error())
|
|
os.Exit(13)
|
|
}
|
|
|
|
if !telegramResp.Ok {
|
|
printFailure(telegramResp.Description)
|
|
os.Exit(13)
|
|
}
|
|
|
|
// Print the required information
|
|
fmt.Printf("\033[1mConfig Used:\033[0m %s\n", configUsed)
|
|
fmt.Printf("\033[1mChat ID:\033[0m %s\n", config.ChatID)
|
|
fmt.Printf("\033[1mSilent:\033[0m %s\n", strconv.FormatBool(silent))
|
|
fmt.Printf("\033[1mMessage:\033[0m %s\n", message)
|
|
|
|
// Print SUCCESS in bold green text
|
|
printSuccess()
|
|
}
|
|
|
|
func printError(msg string) {
|
|
fmt.Fprintf(os.Stderr, "[\033[1;31mERROR\033[0m] %s\n", msg)
|
|
}
|
|
|
|
func printFailure(msg string) {
|
|
fmt.Fprintf(os.Stderr, "\033[1mResult:\033[0m [\033[1;31mFAILURE\033[0m]: %s\n\n", msg)
|
|
}
|
|
|
|
func printSuccess() {
|
|
fmt.Fprintf(os.Stdout, "\033[1mResult:\033[0m [\033[1;32mSUCCESS\033[0m]\n\n")
|
|
}
|
|
|
|
// Strip ANSI escape sequences for accurate string length assessment
|
|
func stripANSI(input string) string {
|
|
ansiPattern := `\033\[[0-9;]*[a-zA-Z]`
|
|
re := regexp.MustCompile(ansiPattern)
|
|
return re.ReplaceAllString(input, "")
|
|
}
|