Skip to content
Navigation Menu
{{ message }}
forked from TelegramBots/Telegram.Bot.Examples
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.fs
More file actions
261 lines (223 loc) · 9.9 KB
/
Copy pathProgram.fs
File metadata and controls
261 lines (223 loc) · 9.9 KB
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
// Example Telegram bot in F#
//
// Copyright (c) 2021 Arvind Devarajan
// Licensed to you under the MIT License.
// See the LICENSE file in the project root for more information.
namespace FSharp.Examples.Polling
open System
open System.IO
open System.Threading
open Telegram.Bot
open Telegram.Bot.Types
open Telegram.Bot.Exceptions
open Telegram.Bot.Types.Enums
open Telegram.Bot.Types.InlineQueryResults
open Telegram.Bot.Types.ReplyMarkups
open Telegram.Bot.Polling
open System.Threading.Tasks
module Handlers =
let handleErrorAsync (botClient:ITelegramBotClient) (err:Exception) (cts:CancellationToken) = async {
let errormsg =
match err with
| :? ApiRequestException as apiex -> $"Telegram API Error:\n[{apiex.ErrorCode}]\n{apiex.Message}"
| _ -> err.ToString()
Console.WriteLine(errormsg)
}
let botOnCallbackQueryReceived (botClient:ITelegramBotClient) (query:CallbackQuery) = async {
do!
botClient.AnswerCallbackQueryAsync(query.Id, $"Received {query.Data}")
|> Async.AwaitTask
do!
botClient.SendTextMessageAsync(ChatId(query.Message.Chat.Id), $"Received {query.Data}")
|> Async.AwaitTask
|> Async.Ignore
}
let botOnInlineQueryReceived (botClient:ITelegramBotClient) (inlinequery:InlineQuery) = async {
Console.WriteLine($"Received inline query from: {inlinequery.From.Id}");
// displayed result
let results = seq {
InlineQueryResultArticle(
id = "3",
title = "TgBots",
inputMessageContent = InputTextMessageContent("hello"))
}
do!
botClient.AnswerInlineQueryAsync(inlinequery.Id,
results |> Seq.cast,
isPersonal = true,
cacheTime = 0)
|> Async.AwaitTask
|> Async.Ignore
}
let botOnChosenInlineResultReceived (botClient:ITelegramBotClient) (chosenInlineResult:ChosenInlineResult) = async {
Console.WriteLine($"Received inline result: {chosenInlineResult.ResultId}")
}
let botOnMessageReceived (botClient:ITelegramBotClient) (message:Message) =
Console.WriteLine($"Receive message type: {message.Type}");
let sendInlineKeyboard = async {
do!
botClient.SendChatActionAsync(ChatId(message.Chat.Id), ChatAction.Typing)
|> Async.AwaitTask
|> Async.Ignore
let inlineKeyboard = seq {
// first row
seq {
InlineKeyboardButton.WithCallbackData("1.1", "11");
InlineKeyboardButton.WithCallbackData("1.2", "12");
};
// second row
seq {
InlineKeyboardButton.WithCallbackData("2.1", "21");
InlineKeyboardButton.WithCallbackData("2.2", "22");
};
}
do!
botClient.SendTextMessageAsync(
chatId = ChatId(message.Chat.Id),
text = "Choose",
replyMarkup = InlineKeyboardMarkup(inlineKeyboard))
|> Async.AwaitTask
|> Async.Ignore
}
let sendReplyKeyboard = async {
let replyKeyboardMarkup =
ReplyKeyboardMarkup( seq {
// first row
seq { KeyboardButton("1.1"); KeyboardButton("1.2") };
// second row
seq { KeyboardButton("1.1"); KeyboardButton("1.2") };
},
ResizeKeyboard = true)
do!
botClient.SendTextMessageAsync(
chatId = ChatId(message.Chat.Id),
text = "Choose",
replyMarkup = replyKeyboardMarkup)
|> Async.AwaitTask
|> Async.Ignore
}
let removeKeyboard = async {
do!
botClient.SendTextMessageAsync(
chatId = ChatId(message.Chat.Id),
text = "Removing keyboard",
replyMarkup = ReplyKeyboardRemove())
|> Async.AwaitTask
|> Async.Ignore
}
let sendFile = async {
do!
botClient.SendChatActionAsync(ChatId(message.Chat.Id), ChatAction.UploadPhoto)
|> Async.AwaitTask
|> Async.Ignore
let filePath = @"Files/tux.png"
use fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)
let fileName =
filePath.Split(Path.DirectorySeparatorChar)
|> Array.last
do!
botClient.SendPhotoAsync(
chatId = ChatId(message.Chat.Id),
photo = InputFile(fileStream, fileName),
caption = "Nice Picture")
|> Async.AwaitTask
|> Async.Ignore
}
let requestContactAndLocation = async {
let requestReplyKeyboard =
ReplyKeyboardMarkup(seq {
KeyboardButton.WithRequestLocation("Location");
KeyboardButton.WithRequestContact("Contact");
},
ResizeKeyboard = true)
do!
botClient.SendTextMessageAsync(
chatId = ChatId(message.Chat.Id),
text = "Who or Where are you?",
replyMarkup = requestReplyKeyboard)
|> Async.AwaitTask
|> Async.Ignore
}
let usage = async {
let usage =
"Usage:\n" +
"/inline - send inline keyboard\n" +
"/keyboard - send custom keyboard\n" +
"/remove - remove custom keyboard\n" +
"/photo - send a photo\n" +
"/request - request location or contact"
do!
botClient.SendTextMessageAsync(
chatId = ChatId(message.Chat.Id),
text = usage,
replyMarkup = ReplyKeyboardRemove())
|> Async.AwaitTask
|> Async.Ignore
}
async {
if message.Type <> MessageType.Text then
()
else
let fn =
// We use tryHead here just in case we get an empty
// response from the user
match message.Text.Split(' ') |> Array.tryHead with
| Some "/inline" -> sendInlineKeyboard
| Some "/keyboard" -> sendReplyKeyboard
| Some "/remove" -> removeKeyboard
| Some "/photo" -> sendFile
| Some "/request" -> requestContactAndLocation
| _ -> usage
do! fn
}
let unknownUpdateHandlerAsync (botClient:ITelegramBotClient) (update:Update) = async {
Console.WriteLine($"Unknown update type: {update.Type}");
}
let handleUpdateAsync botClient (update:Update) cts = async {
try
let fn =
match update.Type with
| UpdateType.Message -> botOnMessageReceived botClient update.Message
| UpdateType.EditedMessage -> botOnMessageReceived botClient update.EditedMessage
| UpdateType.CallbackQuery -> botOnCallbackQueryReceived botClient update.CallbackQuery
| UpdateType.InlineQuery -> botOnInlineQueryReceived botClient update.InlineQuery
| UpdateType.ChosenInlineResult -> botOnChosenInlineResultReceived botClient update.ChosenInlineResult
| _ -> unknownUpdateHandlerAsync botClient update
return fn |> Async.Start
with
| ex -> do! handleErrorAsync botClient ex cts
}
type FuncConvert =
// There is quite some bit of jugglery here, so requires some explanation:
// DefaultUpdateHandler() requires two arguments, both of type Func<_,_,_,_>, and both
// of which return a Task. Now, in order to be in the F# domain, we would like to
// have this Func<> defined as an F# function: and so we need to explicitely construct
// Func<>s by passing them to an inner lambda function.
// Now, the inner lambda function needs to return a Task, but we want to use F# async.
// We therefore use a Async.StartAsTask to start an async computation and get back a Task.
// The last ":>" is for upcasting Task<unit> (returned by the async computations) to their
// base class Task to avoid and error that says "the expression expects a Task but we have a
// Task<unit> here.".
// The good thing about doing all this is that handleUpdateAsync and handleErrorAsync are both
// in the F# domain - so now can take all advantages of F#!
static member inline ToCSharpDelegate (f) =
Func<_,_,_,_>(fun a b c -> Async.StartAsTask (f a b c) :> Task)
module TelegramBot =
[<EntryPoint>]
let main argv =
let botClient
= TelegramBotClient(TelegramBotCfg.token)
async {
let! me = botClient.GetMeAsync() |> Async.AwaitTask
printfn $"Hello, World! I am user {me.Id} and my name is {me.FirstName}."
printfn $"Start listening for {me.Username}..."
use cts = new CancellationTokenSource();
botClient.StartReceiving(
Handlers.handleUpdateAsync |> FuncConvert.ToCSharpDelegate,
Handlers.handleErrorAsync |> FuncConvert.ToCSharpDelegate,
ReceiverOptions( AllowedUpdates = [||] ),
cts.Token)
} |> Async.RunSynchronously
printfn "Press <Enter> to exit"
Console.Read() |> ignore
0
You can’t perform that action at this time.
