Golang framework for developing Telegram Bots

finch.go 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // Package finch is a framework for Telegram Bots.
  2. package finch
  3. import (
  4. "encoding/json"
  5. "io/ioutil"
  6. "log"
  7. "net/http"
  8. "os"
  9. "strings"
  10. "github.com/getsentry/raven-go"
  11. "github.com/go-telegram-bot-api/telegram-bot-api"
  12. )
  13. // Config is a type used for storing configuration information.
  14. type Config map[string]interface{}
  15. var sentryEnabled bool = false
  16. // LoadConfig loads the saved config, if it exists.
  17. //
  18. // It looks for a FINCH_CONFIG environmental variable,
  19. // before falling back to a file name config.json.
  20. func LoadConfig() (*Config, error) {
  21. fileName := os.Getenv("FINCH_CONFIG")
  22. if fileName == "" {
  23. fileName = "config.json"
  24. }
  25. f, err := ioutil.ReadFile(fileName)
  26. if err != nil {
  27. return &Config{}, nil
  28. }
  29. var cfg Config
  30. json.Unmarshal(f, &cfg)
  31. return &cfg, nil
  32. }
  33. // Save saves the current Config struct.
  34. //
  35. // It uses the same file as LoadConfig.
  36. func (c *Config) Save() error {
  37. b, err := json.Marshal(c)
  38. if err != nil {
  39. if sentryEnabled {
  40. raven.CaptureErrorAndWait(err, nil)
  41. }
  42. return err
  43. }
  44. fileName := os.Getenv("FINCH_CONFIG")
  45. if fileName == "" {
  46. fileName = "config.json"
  47. }
  48. return ioutil.WriteFile(fileName, b, 0600)
  49. }
  50. // Finch is a Telegram Bot, including API, Config, and Commands.
  51. type Finch struct {
  52. API *tgbotapi.BotAPI
  53. Config Config
  54. Commands []*CommandState
  55. Inline InlineCommand
  56. }
  57. // NewFinch returns a new Finch instance, with Telegram API setup.
  58. func NewFinch(token string) *Finch {
  59. return NewFinchWithClient(token, &http.Client{})
  60. }
  61. // NewFinchWithClient returns a new Finch instance,
  62. // using a different net/http Client.
  63. func NewFinchWithClient(token string, client *http.Client) *Finch {
  64. bot := &Finch{}
  65. api, err := tgbotapi.NewBotAPIWithClient(token, client)
  66. if err != nil {
  67. panic(err)
  68. }
  69. bot.API = api
  70. bot.Commands = commands
  71. bot.Inline = inline
  72. c, _ := LoadConfig()
  73. bot.Config = *c
  74. return bot
  75. }
  76. // Start initializes commands, and starts listening for messages.
  77. func (f *Finch) Start() {
  78. if v, ok := f.Config["sentry_dsn"]; ok {
  79. sentryEnabled = true
  80. raven.SetDSN(v.(string))
  81. }
  82. f.commandInit()
  83. u := tgbotapi.NewUpdate(0)
  84. u.Timeout = 86400
  85. updates, err := f.API.GetUpdatesChan(u)
  86. if err != nil {
  87. if sentryEnabled {
  88. raven.CaptureErrorAndWait(err, nil)
  89. }
  90. log.Fatal(err)
  91. }
  92. for update := range updates {
  93. go f.commandRouter(update)
  94. }
  95. }
  96. // StartWebhook initializes commands,
  97. // then registers a webhook for the bot to listen on
  98. func (f *Finch) StartWebhook(endpoint string) {
  99. f.commandInit()
  100. f.API.ListenForWebhook(endpoint)
  101. }
  102. // SendMessage sends a message with various changes, and does not return the Message.
  103. //
  104. // At some point, this may do more handling as needed.
  105. func (f *Finch) SendMessage(message tgbotapi.MessageConfig) error {
  106. message.Text = strings.Replace(message.Text, "@@", "@"+f.API.Self.UserName, -1)
  107. _, err := f.API.Send(message)
  108. if err != nil && sentryEnabled {
  109. raven.CaptureError(err, nil)
  110. }
  111. return err
  112. }
  113. // QuickReply quickly sends a message as a reply.
  114. func (f *Finch) QuickReply(message tgbotapi.Message, text string) error {
  115. msg := tgbotapi.NewMessage(message.Chat.ID, text)
  116. msg.ReplyToMessageID = message.MessageID
  117. return f.SendMessage(msg)
  118. }