-
Notifications
You must be signed in to change notification settings - Fork 21
/
incoming.go
495 lines (430 loc) · 16.7 KB
/
incoming.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
// Copyright 2015-2016 mrd0ll4r and contributors. All rights reserved.
// Use of this source code is governed by the MIT license, which can be found in
// the LICENSE file.
package tbotapi
import "fmt"
import "sort"
// BaseResponse contains the basic fields contained in every API response.
type baseResponse struct {
Ok bool `json:"ok"`
Description string `json:"description"`
ErrorCode int `json:"error_code"`
}
// Audio represents an audio file to be treated as music.
type Audio struct {
FileBase
Duration int `json:"duration"`
MimeType string `json:"mime_type"`
}
// Chat contains information about the chat a message originated from.
type Chat struct {
ID int `json:"id"` // Unique identifier for this chat.
Type string `json:"type"` // Type of chat, can be either "private", "group" or "channel". Check Is(PrivateChat|GroupChat|Channel)() methods.
Title *string `json:"title"` // Title for channels and group chats.
Username *string `json:"username"` // Username for private chats and channels if available.
FirstName *string `json:"first_name"` // First name of the other party in a private chat.
LastName *string `json:"last_name"` // Last name of the other party in a private chat.
}
// IsPrivateChat checks if the chat is a private chat.
func (c Chat) IsPrivateChat() bool {
return c.Type == "private"
}
// IsGroupChat checks if the chat is a group chat.
func (c Chat) IsGroupChat() bool {
return c.Type == "group"
}
// IsSupergroup checks if the chat is a supergroup chat.
func (c Chat) IsSupergroup() bool {
return c.Type == "supergroup"
}
// IsChannel checks if the chat is a channel.
func (c Chat) IsChannel() bool {
return c.Type == "channel"
}
func (c Chat) String() string {
toReturn := fmt.Sprint(c.ID)
if c.IsPrivateChat() {
toReturn += " (P) "
} else if c.IsGroupChat() {
toReturn += " (G) "
} else {
toReturn += " (C) "
}
if c.Title != nil {
toReturn += "\"" + *c.Title + "\" "
}
if c.FirstName != nil {
toReturn += *c.FirstName + " "
}
if c.LastName != nil {
toReturn += *c.LastName + " "
}
if c.Username != nil {
toReturn += "(@" + *c.Username + ")"
}
return toReturn
}
// Contact represents a phone contact.
type Contact struct {
PhoneNumber string `json:"phone_number"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
ID int `json:"user_id"`
}
// Document represents a general file.
type Document struct {
FileBase
Thumbnail PhotoSize `json:"thumb"`
Name string `json:"file_name"`
MimeType string `json:"mime_type"`
}
// FileBase contains all the fields present in every file-like API response.
type FileBase struct {
ID string `json:"file_id"`
Size int `json:"file_size"`
}
// File represents a file ready to be downloaded.
type File struct {
FileBase
Path string `json:"file_path"`
}
// FileResponse represents the response sent by the API when requesting a
// file for download.
type FileResponse struct {
baseResponse
File File `json:"result"`
}
// Location represents a point on the map.
type Location struct {
Longitude float32 `json:"longitude"`
Latitude float32 `json:"latitude"`
}
// MessageResponse represents the response sent by the API on successful
// messages sent.
type MessageResponse struct {
baseResponse
Message Message `json:"result"`
}
// Message represents a message.
type Message struct {
noReplyMessage
ReplyToMessage *noReplyMessage `json:"reply_to_message"`
}
// IsForwarded checks if the message was forwarded.
func (m *Message) IsForwarded() bool {
return m.ForwardFrom != nil
}
// IsReply checks if the message is a reply.
func (m *Message) IsReply() bool {
return m.ReplyToMessage != nil
}
// Type determines the type of the message.
// Note that, for all these types, messages can still be replies or
// forwarded.
func (m *Message) Type() MessageType {
if m.Text != nil {
return TextMessage
} else if m.Audio != nil {
return AudioMessage
} else if m.Document != nil {
return DocumentMessage
} else if m.Photo != nil {
return PhotoMessage
} else if m.Sticker != nil {
return StickerMessage
} else if m.Video != nil {
return VideoMessage
} else if m.Voice != nil {
return VoiceMessage
} else if m.Contact != nil {
return ContactMessage
} else if m.Location != nil {
return LocationMessage
} else if m.NewChatMember != nil {
return NewChatMember
} else if m.LeftChatMember != nil {
return LeftChatMember
} else if m.NewChatTitle != nil {
return NewChatTitle
} else if m.NewChatPhoto != nil {
return NewChatPhoto
} else if m.DeleteChatPhoto {
return DeletedChatPhoto
} else if m.GroupChatCreated {
return GroupChatCreated
} else if m.SupergroupChatCreated {
return SupergroupChatCreated
} else if m.ChannelChatCreated {
return ChannelChatCreated
} else if m.MigrateToChatID != nil {
return MigrationToSupergroup
} else if m.MigrateFromChatID != nil {
return MigrationFromGroup
} else if m.Venue != nil {
return VenueMessage
} else if m.PinnedMessage != nil {
return PinnedMessage
}
return UnknownMessage
}
type noReplyMessage struct {
Chat Chat `json:"chat"` // Information about the chat.
ID int `json:"message_id"` // Message id.
From User `json:"from"` // Sender.
Date int `json:"date"` // Timestamp.
ForwardFrom *User `json:"forward_from"` // Forwarded from who.
ForwardDate *int `json:"forward_date"` // Forwarded from when.
Text *string `json:"text"` // The actual text content.
Entities *[]MessageEntity `json:"entities"` // For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text (optional).
Caption *string `json:"caption"` // Caption for photo or video messages.
Audio *Audio `json:"audio"` // Information about audio contents.
Document *Document `json:"document"` // Information about file contents.
Photo *[]PhotoSize `json:"photo"` // Information about photo contents.
Sticker *Sticker `json:"sticker"` // Information about sticker contents.
Video *Video `json:"video"` // Information about video contents.
Voice *Voice `json:"voice"` // Information about voice message contents.
Contact *Contact `json:"contact"` // Information about contact contents.
Location *Location `json:"location"` // Information about location contents.
Venue *Venue `json:"venue"` // Information about venue contents.
NewChatMember *User `json:"new_chat_member"` // Information about a new chat participant.
LeftChatMember *User `json:"left_chat_member"` // Information about a chat participant who left.
NewChatTitle *string `json:"new_chat_title"` // Information about changes in the group name.
NewChatPhoto *[]PhotoSize `json:"new_chat_photo"` // Information about a new chat photo.
DeleteChatPhoto bool `json:"delete_chat_photo"` // Information about a deleted chat photo.
GroupChatCreated bool `json:"group_chat_created"` // Information about a created group chat.
SupergroupChatCreated bool `json:"supergroup_chat_created"` // Information about a created supergroup chat.
ChannelChatCreated bool `json:"channel_chat_created"` // Information about a created channel.
MigrateToChatID *int `json:"migrate_to_chat_id"` // Indicates the chat ID the group chat was migrated to (is now a supergroup).
MigrateFromChatID *int `json:"migrate_from_chat_id"` // Indicates the chat ID the now supergroup chat was migrated from.
PinnedMessage *noReplyMessage `json:"pinned_message"`
}
// MessageEntityType is the type of an entity contained in a message.
type MessageEntityType string
// Entity types.
const (
EntityTypeMention = MessageEntityType("mention")
EntityTypeHashtag = MessageEntityType("hashtag")
EntityTypeBotCommand = MessageEntityType("bot_command")
EntityTypeURL = MessageEntityType("url")
EntityTypeEmail = MessageEntityType("email")
EntityTypeBold = MessageEntityType("bold")
EntityTypeItalic = MessageEntityType("italic")
EntityTypeCode = MessageEntityType("code")
EntityTypePre = MessageEntityType("pre")
EntityTypeTextLink = MessageEntityType("text_link")
)
// MessageEntity represents an entity contained in a text message.
type MessageEntity struct {
Type MessageEntityType `json:"type"`
Offset int `json:"offset"`
Length int `json:"length"`
URL *string `json:"url"`
}
// Venue represents a venue contained in a message.
type Venue struct {
Location Location `json:"location"` // Venue location.
Title string `json:"title"` // Name of the venue.
Address string `json:"address"` // Address of the venue.
FoursquareID *string `json:"foursqare_id"` // Foursqare ID of the venue (optional).
}
// MessageType is the type of a message.
type MessageType int
// Message types.
const (
TextMessage MessageType = iota // Text messages.
PinnedMessage // Pinned messages.
AudioMessage // Audio messages.
DocumentMessage // Files.
PhotoMessage // Photos.
StickerMessage // Stickers.
VideoMessage // Videos.
VoiceMessage // Voice messages.
ContactMessage // Contact information.
LocationMessage // Locations.
VenueMessage // Venues.
chatActionsBegin
NewChatMember // Joined chat participant.
LeftChatMember // Left chat participant.
NewChatTitle // Chat title change.
NewChatPhoto // New chat photo.
DeletedChatPhoto // Deleted chat photo.
GroupChatCreated // Creation of a group cha.
SupergroupChatCreated // Creation of a supergroup cha.
ChannelChatCreated // Createion of a channe.
MigrationToSupergroup // Migration to supergrou.
MigrationFromGroup // Migration from group (to supergroup).
chatActionsEnd
UnknownMessage // Unknown (probably new due to API changes).
)
var messageTypes = map[MessageType]string{
TextMessage: "Text",
PinnedMessage: "Pinned",
AudioMessage: "Audio",
DocumentMessage: "Document",
PhotoMessage: "Photo",
StickerMessage: "Sticker",
VideoMessage: "Video",
VoiceMessage: "Voice",
ContactMessage: "Contact",
LocationMessage: "Location",
VenueMessage: "Venue",
NewChatMember: "NewChatMember",
LeftChatMember: "LeftChatMember",
NewChatTitle: "NewChatTitle",
NewChatPhoto: "NewChatPhoto",
DeletedChatPhoto: "DeletedChatPhoto",
GroupChatCreated: "GroupChatCreated",
UnknownMessage: "Unknown",
}
// IsChatAction checks if the MessageType is about changes in group chats.
func (mt MessageType) IsChatAction() bool {
return mt > chatActionsBegin && mt < chatActionsEnd
}
func (mt MessageType) String() string {
val, ok := messageTypes[mt]
if !ok {
return messageTypes[UnknownMessage]
}
return val
}
// PhotoSize represents one size of a photo or a thumbnail.
type PhotoSize struct {
FileBase
Width int `json:"width"`
Height int `json:"height"`
}
// Sticker represents a sticker.
type Sticker struct {
FileBase
Width int `json:"width"`
Height int `json:"height"`
Thumbnail PhotoSize `json:"thumb"`
}
// UpdateResponse represents the response sent by the API for a GetUpdate
// request.
type updateResponse struct {
baseResponse
Update []Update `json:"result"`
}
// ByID is a wrapper to sort an []Update by ID.
type byID []Update
func (a byID) Len() int { return len(a) }
func (a byID) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byID) Less(i, j int) bool { return a[i].ID < a[j].ID }
// Sort sorts all the updates contained in an UpdateResponse by their ID.
func (resp *updateResponse) sort() {
sort.Sort(byID(resp.Update))
}
// Update represents an incoming update.
type Update struct {
ID int `json:"update_id"`
Message *Message `json:"message"`
InlineQuery *InlineQuery `json:"inline_query"`
ChosenInlineResult *ChosenInlineResult `json:"chosen_inline_result"`
CallbackQuery *CallbackQuery `json:"callback_query"`
}
// Type returns the type of the update.
func (u *Update) Type() UpdateType {
if u.Message != nil {
return MessageUpdate
} else if u.InlineQuery != nil {
return InlineQueryUpdate
} else if u.ChosenInlineResult != nil {
return ChosenInlineResultUpdate
}
return UnknownUpdate
}
// UpdateType represents an update type.
type UpdateType int
// Update types.
const (
MessageUpdate UpdateType = iota // Message update.
InlineQueryUpdate // Inline query.
ChosenInlineResultUpdate // Chosen inline result.
UnknownUpdate // Unkown, probably due to API changes.
)
var updateTypes = map[UpdateType]string{
MessageUpdate: "Message",
InlineQueryUpdate: "InlineQuery",
ChosenInlineResultUpdate: "ChosenInlineResult",
UnknownUpdate: "Unknown",
}
func (t UpdateType) String() string {
val, ok := updateTypes[t]
if !ok {
return updateTypes[UnknownUpdate]
}
return val
}
// UserResponse represents the response sent by the API on a GetMe request.
type UserResponse struct {
baseResponse
User User `json:"result"`
}
// User represents a Telegram user or bot.
type User struct {
ID int `json:"id"`
FirstName string `json:"first_name"`
LastName *string `json:"last_name"`
Username *string `json:"username"`
}
func (u User) String() string {
if u.LastName != nil && u.Username != nil {
return fmt.Sprintf("%d/%s %s (@%s)", u.ID, u.FirstName, *u.LastName, *u.Username)
} else if u.LastName != nil {
return fmt.Sprintf("%d/%s %s", u.ID, u.FirstName, *u.LastName)
} else if u.Username != nil {
return fmt.Sprintf("%d/%s (@%s)", u.ID, u.FirstName, *u.Username)
}
return fmt.Sprintf("%d/%s", u.ID, u.FirstName)
}
// UserProfilePhotosResponse represents the response sent by the API on a
// GetUserProfilePhotos request.
type UserProfilePhotosResponse struct {
baseResponse
UserProfilePhotos UserProfilePhotos `json:"result"`
}
// UserProfilePhotos represents a users profile pictures.
type UserProfilePhotos struct {
TotalCount int `json:"total_count"`
Photos []PhotoSize `json:"photos"`
}
// Video represents a video file.
type Video struct {
FileBase
Width int `json:"width"`
Height int `json:"height"`
Duration int `json:"duration"`
Thumbnail PhotoSize `json:"thumb"`
MimeType string `json:"mime_type"`
Caption string `json:"caption"`
}
// Voice represents a voice note.
type Voice struct {
FileBase
Duration int `json:"duration"`
MimeType string `json:"mime_type"`
}
// InlineQuery represents an incoming inline query.
type InlineQuery struct {
ID string `json:"id"` // Unique identifier for this query.
From User `json:"from"` // Sender.
Query string `json:"query"` // Text of the query.
Offset string `json:"offset"` // Offset of the results to be returned, can be controlled by the bot.
}
// ChosenInlineResult represents a result of an inline query that was
// chosen by the user and sent to their chat partner.
type ChosenInlineResult struct {
ID string `json:"result_id"` // Unique identifier for the result that was chosen.
From User `json:"from"` // User that chose the result.
Query string `json:"query"` // Query that was used to obtain the result.
}
// CallbackQuery represents an incoming callback query from a button of an
// inline keyboard.
type CallbackQuery struct {
ID string `json:"id"` // Unique identifier for this query.
From User `json:"from"` // Sender.
Message *Message `json:"message"` // Message with the callback button that originated the query. (optional).
InlineMessageID *string `json:"inline_message_id"` // Identifier of the message sent via the bot in inline mode, that originated the query.
Data string `json:"data"` // Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field.
}