all repos — telegram-bot-api @ 5be25266b56e4097ab270fd83ccbec87f80d4eb8

Golang bindings for the Telegram Bot API

docs/internals/adding-endpoints.md (view raw)

  1# Adding Endpoints
  2
  3This is mostly useful if you've managed to catch a new Telegram Bot API update
  4before the library can get updated. It's also a great source of information
  5about how the types work internally.
  6
  7## Creating the Config
  8
  9The first step in adding a new endpoint is to create a new Config type for it.
 10These belong in `configs.go`.
 11
 12Let's try and add the `deleteMessage` endpoint. We can see it requires two
 13fields; `chat_id` and `message_id`. We can create a struct for these.
 14
 15```go
 16type DeleteMessageConfig struct {
 17	ChatID    ???
 18	MessageID int
 19}
 20```
 21
 22What type should `ChatID` be? Telegram allows specifying numeric chat IDs or channel usernames. Golang doesn't have union types, and interfaces are entirely
 23untyped. This library solves this by adding two fields, a `ChatID` and a
 24`ChannelUsername`. We can now write the struct as follows.
 25
 26```go
 27type DeleteMessageConfig struct {
 28	ChannelUsername string
 29	ChatID          int64
 30	MessageID       int
 31}
 32```
 33
 34Note that `ChatID` is an `int64`. Telegram chat IDs can be greater than 32 bits.
 35
 36Okay, we now have our struct. But we can't send it yet. It doesn't implement
 37`Chattable` so it won't work with `Request` or `Send`.
 38
 39### Making it `Chattable`
 40
 41We can see that `Chattable` only requires a few methods.
 42
 43```go
 44type Chattable interface {
 45	params() (Params, error)
 46	method() string
 47}
 48```
 49
 50`params` is the fields associated with the request. `method` is the endpoint
 51that this Config is associated with.
 52
 53Implementing the `method` is easy, so let's start with that.
 54
 55```go
 56func (config DeleteMessageConfig) method() string {
 57	return "deleteMessage"
 58}
 59```
 60
 61Now we have to add the `params`. The `Params` type is an alias for
 62`map[string]string`. Telegram expects only a single field for `chat_id`, so we
 63have to determine what data to send.
 64
 65We could use an if statement to determine which field to get the value from.
 66However, as this is a relatively common operation, there's helper methods for
 67`Params`. We can use the `AddFirstValid` method to go through each possible
 68value and stop when it discovers a valid one. Before writing your own Config,
 69it's worth taking a look through `params.go` to see what other helpers exist.
 70
 71Now we can take a look at what a completed `params` method looks like.
 72
 73```go
 74func (config DeleteMessageConfig) params() (Params, error) {
 75	params := make(Params)
 76
 77	params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername)
 78	params.AddNonZero("message_id", config.MessageID)
 79
 80	return params, nil
 81}
 82```
 83
 84### Uploading Files
 85
 86Let's imagine that for some reason deleting a message requires a document to be
 87uploaded and an optional thumbnail for that document. To add file upload
 88support we need to implement `Fileable`. This only requires one additional
 89method.
 90
 91```go
 92type Fileable interface {
 93	Chattable
 94	files() []RequestFile
 95}
 96```
 97
 98First, let's add some fields to store our files in. Most of the standard Configs
 99have similar fields for their files.
100
101```diff
102 type DeleteMessageConfig struct {
103     ChannelUsername string
104     ChatID          int64
105     MessageID       int
106+    Delete          interface{}
107+    Thumb           interface{}
108 }
109```
110
111Adding another method is pretty simple. We'll always add a file named `delete`
112and add the `thumb` file if we have one.
113
114```go
115func (config DeleteMessageConfig) files() []RequestFile {
116	files := []RequestFile{{
117		Name: "delete",
118		File: config.Delete,
119	}}
120
121	if config.Thumb != nil {
122		files = append(files, RequestFile{
123			Name: "thumb",
124			File: config.Thumb,
125		})
126	}
127
128	return files
129}
130```
131
132And now our files will upload! It will transparently handle uploads whether File is a string with a path to a file, `FileURL`, `FileBytes`, `FileReader`, or `FileID`.
133
134### Base Configs
135
136Certain Configs have repeated elements. For example, many of the items sent to a
137chat have `ChatID` or `ChannelUsername` fields, along with `ReplyToMessageID`,
138`ReplyMarkup`, and `DisableNotification`. Instead of implementing all of this
139code for each item, there's a `BaseChat` that handles it for your Config.
140Simply embed it in your struct to get all of those fields.
141
142There's only a few fields required for the `MessageConfig` struct after
143embedding the `BaseChat` struct.
144
145```go
146type MessageConfig struct {
147	BaseChat
148	Text                  string
149	ParseMode             string
150	DisableWebPagePreview bool
151}
152```
153
154It also inherits the `params` method from `BaseChat`. This allows you to call
155it, then you only have to add your new fields.
156
157```go
158func (config MessageConfig) params() (Params, error) {
159	params, err := config.BaseChat.params()
160	if err != nil {
161		return params, err
162	}
163
164	params.AddNonEmpty("text", config.Text)
165	// Add your other fields
166
167	return params, nil
168}
169```
170
171Similarly, there's a `BaseFile` struct for adding an associated file and
172`BaseEdit` struct for editing messages.
173
174## Making it Friendly
175
176After we've got a Config type, we'll want to make it more user-friendly. We can
177do this by adding a new helper to `helpers.go`. These are functions that take
178in the required data for the request to succeed and populate a Config.
179
180Telegram only requires two fields to call `deleteMessage`, so this will be fast.
181
182```go
183func NewDeleteMessage(chatID int64, messageID int) DeleteMessageConfig {
184	return DeleteMessageConfig{
185		ChatID:    chatID,
186		MessageID: messageID,
187	}
188}
189```
190
191Sometimes it makes sense to add more helpers if there's methods where you have
192to set exactly one field. You can also add helpers that accept a `username`
193string for channels if it's a common operation.
194
195And that's it! You've added a new method.