all repos — telegram-bot-api @ c88341ccc38fd13b3ef9e5d866c13bf6237ed230

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